# PostgreSQL 사용 시 도움 되는 패턴들

> Clean Markdown view of GeekNews topic #20067. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=20067](https://news.hada.io/topic?id=20067)
- GeekNews Markdown: [https://news.hada.io/topic/20067.md](https://news.hada.io/topic/20067.md)
- Type: GN+
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2025-04-01T09:20:32+09:00
- Updated: 2025-04-01T09:20:32+09:00
- Original source: [mccue.dev](https://mccue.dev/pages/3-11-25-life-altering-postgresql-patterns)
- Points: 63
- Comments: 4

## Summary

PostgreSQL을 더 효과적으로 사용하기 위한 패턴들을 소개합니다. UUID 기본 키를 활용하면 DB 연결 없이도 안전하게 ID를 생성할 수 있고, created_at과 updated_at 필드를 추가해 디버깅을 용이하게 할 수 있습니다. 외래 키에 on update/delete restrict를 설정해 데이터 무결성을 보호하고, 스키마와 enum 테이블을 사용해 가독성과 유연성을 높이며, 테이블 이름은 단수형으로, 조인 테이블은 기계적으로 명명하는 패턴이 유용합니다. 또한, soft delete와 상태 로그 테이블로 데이터 추적성을 강화하고, JSON 쿼리를 적극 활용해 중첩 데이터를 효율적으로 조회할 수 있으며, 뷰는 최소화해 유지보수 부담을 줄이는 것이 좋습니다.

## Topic Body

- Postgres를 더 생산적이고 안전하게 사용하는 데 도움이 되는 실용적인 패턴들을 정리한 글  
- 각 패턴은 작지만 누적되면 큰 차이를 만들어냄  
  
### UUID 기본 키 사용  
  
- UUID는 무작위이기 때문에 정렬이나 인덱스 성능 면에서 단점이 있음  
- 숫자 ID보다 공간을 더 많이 차지함  
- 하지만 다음과 같은 장점이 있음  
  - DB에 연결하지 않고도 UUID를 생성할 수 있음  
  - 외부에 안전하게 노출 가능함  
- `gen_random_uuid()`를 사용해 기본 키로 UUID를 자동 생성할 수 있음  
  
### created_at과 updated_at 필드 항상 추가  
  
- 디버깅 시 레코드 생성 및 변경 시점을 아는 것이 매우 유용함  
- `updated_at`은 트리거를 통해 자동으로 갱신되도록 설정 가능함  
- 함수는 한 번만 만들고, 트리거는 각 테이블에 적용해야 함  
  
### 외래 키에는 on update/delete restrict 설정  
  
- 외래 키 제약 조건을 설정할 때 `on update restrict on delete restrict`를 반드시 사용해야 함  
- 데이터 삭제 시 실수로 연쇄 삭제가 발생하지 않도록 방지함  
- 저장 공간은 저렴하지만 데이터 복구는 매우 어렵기 때문에 보수적으로 처리하는 것이 좋음  
  
### 스키마 사용 권장  
  
- 기본 스키마는 `public`이지만, 애플리케이션이 커지면 별도의 스키마로 분리하는 것이 좋음  
- 스키마는 네임스페이스처럼 작동하며, 서로 다른 스키마 간에도 조인이 가능함  
- 테이블 수가 많아질수록 스키마를 활용하는 것이 가독성과 유지보수에 유리함  
  
### Enum 테이블 패턴 사용  
  
- PostgreSQL의 enum 타입이나 check constraint 대신 enum 테이블을 사용하는 방식이 더 유연함  
- enum 값을 별도 테이블로 관리하면, 메타데이터를 추가하거나 enum 값을 쉽게 확장 가능함  
- 외래 키로 enum 테이블의 값을 참조하여 제약 조건을 유지함  
  
### 테이블 이름은 단수형으로 지정  
  
- 테이블 이름은 복수가 아닌 단수형으로 지정하는 것이 바람직함  
- 쿼리 작성 시 단수형이 더 명확하며, 복수형은 소유격이나 의미적 혼란을 야기할 수 있음  
  
### 조인 테이블은 기계적으로 이름 지정  
  
- 다대다 관계를 위한 조인 테이블은 두 테이블명을 이어붙여 명명하는 것이 안전하고 명확함  
- 예: `person_pet`  
- 조합에 대한 고유 인덱스를 추가하여 중복 방지  
  
### 삭제 대신 soft delete 사용  
  
- 데이터를 실제로 삭제하기보다, 삭제 시점을 나타내는 `revoked_at` 같은 timestamp 필드를 사용하는 것이 좋음  
- 삭제 여부뿐 아니라, 언제 삭제되었는지를 추적할 수 있음  
- Boolean 값보다 timestamp가 더 많은 정보를 제공함  
  
### 상태(Status)는 로그 테이블로 표현  
  
- 단일 컬럼으로 상태를 표현하는 대신, 상태 변경 이력을 별도 테이블로 저장  
- 상태 발생 시점은 `valid_at` 컬럼으로 명시  
- 최신 상태를 빠르게 조회할 수 있도록 `latest` 플래그 및 유니크 인덱스 + 트리거를 설정함  
- 이는 비동기 이벤트 처리나 순서가 뒤섞일 수 있는 상황에서 유리함  
  
### 특별한 행에는 system_id 추가  
  
- enum 테이블 외에도, 특정 "시스템 행"이 필요한 경우가 있음  
- `system_id` 텍스트 필드를 nullable로 추가하고 유니크 인덱스를 설정  
- `system_id`를 통해 특정 행을 명확하게 조회 가능  
  
### 뷰(View)는 최소한으로 사용  
  
- 뷰는 복잡한 쿼리를 추상화하는 데 유용하지만 유지보수가 어려움  
  - 컬럼 제거 시 뷰 재생성이 필요  
  - 뷰 위에 뷰를 만들면 성능 및 가독성 문제가 생김  
- 필요한 만큼만 신중하게 사용할 것  
  
### JSON 쿼리 적극 활용  
  
- Postgres는 JSON 저장뿐 아니라 JSON 반환 쿼리도 매우 강력함  
- 중첩된 관계를 한 번의 쿼리로 JSON 형태로 반환 가능함  
- N+1 문제 없이 필요한 모든 데이터를 한 번에 가져올 수 있음  
- 단점: 타입 정보 손실, 전체 데이터를 한 번에 메모리에 불러와야 함  
- 성능이나 구조 상 장점이 더 큼

## Comments



### Comment 36625

- Author: jhj0517
- Created: 2025-04-01T17:56:35+09:00
- Points: 1

> 조인 테이블은 기계적으로 이름 지정  
  
이름 지을 때 이런 룰이 있다는 거 자체가 좋은 것 같아요~

### Comment 36618

- Author: halfenif
- Created: 2025-04-01T13:55:06+09:00
- Points: 1

UUID7을 고려하면 시간순 정렬은 되는것 아닐까요?

### Comment 36590

- Author: winterjung
- Created: 2025-04-01T10:47:33+09:00
- Points: 1

[PostgreSQL와 UUID를 기본 키로 사용하는 것에 대해](https://news.hada.io/topic?id=15713) 글도 참고해볼만 하겠네요.

### Comment 36586

- Author: t7vonn
- Created: 2025-04-01T10:36:27+09:00
- Points: 1

soft delete시 timestamp 넣는 방법 좋군요   
기본키로 UUID 넣으면 시간순 정렬이 안되니, snowflake id 또는 ulid를 쓰는 것도 좋을 것 같습니다. 이 경우는 각 서버가 sequence number를 들고 있어야 하긴 하지만요
