대충 만들어도 돌아가는 스키마, 하지만 트래픽이 올라가면 바로 비용과 장애로 돌아옵니다. 오늘 글은 PostgreSQL을 기준으로 처음 스키마를 짤 때 반드시 챙겨야 할 실전 포인트를 한 번에 정리합니다.
안녕하세요, ICT·보안·데이터 아키텍처 분야를 경험한 ICT리더 리치입니다. 신규 서비스를 설계할 때 가장 많이 듣는 질문이 “정규화는 어디까지?”, “인덱스는 뭘 박아야 빠른가요?”, “JSONB를 써도 되나요?”입니다. 저 또한 수차례의 스키마 리팩터링과 성능 튜닝을 거치며 깨달은 점들을 정리해 왔는데요. 이 글에서는 개념 설명보다 실무에서 바로 적용할 체크리스트 중심으로 안내드릴게요. 초보자에게는 길잡이로, 실무자에게는 리뷰 체크리스트로 도움이 될 것입니다.
📌 바로가기 목차

1. 요구사항→개체 도출→정규화: 모델링 기본 흐름
DB 설계의 출발점은 사용자 시나리오에서 개체·관계·속성을 도출하는 것입니다. 1차(NF1)~3차 정규화(NF3)로 중복·이상현상을 제거한 뒤, 실제 조회 패턴을 근거로 필요한 곳만 비정규화합니다.
- 요구 수집: 핵심 유즈케이스/규칙(가격, 재고, 상태 전이)을 문장으로 정리
- 개체·관계 추출: 고객–주문(1:N), 주문–주문항목(1:N), 상품–주문항목(N:M)
- 정규화: 반복/다중값 컬럼 분해 → 부분/이행 종속 제거
- 키 전략:
BIGSERIAL또는UUID서러깃 키, 외부 연동 시 자연키 검토 - 검증: 샘플 데이터 입력 → 실제 쿼리로 요구사항 충족/성능 가늠
Tip. 로그·시계열처럼 계속 커지는 데이터는 초기에 별도 테이블과 파티션을 전제로 분리해 두면 이후 운영 비용(인덱스·VACUUM·백업)을 크게 줄일 수 있습니다.
2. 제약조건(키·유니크·체크·외래키)로 품질을 보장하는 방법
애플리케이션에서 막을 수 있는 규칙도 DB 경계에서 한 번 더 검증하면 데이터 품질과 보안이 크게 향상됩니다. PostgreSQL은 CHECK/EXCLUDE 같은 강력한 제약을 제공하므로 도메인 규칙은 스키마에 새겨 넣으세요.
| 제약 | 용도 | 실전 예시 |
|---|---|---|
| PRIMARY KEY | 행 고유 식별 | id BIGSERIAL PRIMARY KEY |
| UNIQUE(부분) | 조건부 중복 방지 | CREATE UNIQUE INDEX ... WHERE deleted_at IS NULL; |
| CHECK | 도메인 규칙 고정 | amount >= 0, status IN ('pending','paid') |
| FOREIGN KEY | 참조 무결성 | ON UPDATE/DELETE 정책, 지연검증(DEFERRABLE) |
| EXCLUDE | 범위/중복 금지 | 예약(시간·자원) 겹침 금지: USING gist (room WITH =, tsrange(start,end) WITH &&) |
3. 인덱스 전략: B-Tree, GIN/BRIN, 부분·표현식 인덱스
인덱스는 읽기를 빠르게 하지만 쓰기/스토리지를 증가시킵니다. 따라서 쿼리 플랜과 실제 접근 패턴을 기준으로 선택하세요.
- B-Tree: 정확/범위 조회 기본. 복합 인덱스는 왼쪽 접두사 규칙을 고려해 정렬(예:
(tenant_id, created_at)). - 부분 인덱스: 활성 행만 인덱싱(예:
WHERE deleted_at IS NULL) → 크기↓, 캐시 효율↑. - 표현식 인덱스: 함수/계산 결과에 인덱스(예:
(lower(email)))로 대소문자 무시 검색 가속. - GIN:
jsonb포함/키검색, 배열 멤버십, 전문검색(tsvector)에 적합. - BRIN: 시간·ID 순차적 누적 테이블(로그/시계열)에서 범위 스캔 최소화로 대용량 효율↑.

4. 파티셔닝·아카이빙: 커지는 테이블의 수명 관리
수천만~수억 건으로 커지는 테이블은 분할 정복이 해법입니다. PostgreSQL의 RANGE·LIST·HASH 파티셔닝을 활용하면 파티션 프루닝으로 스캔 범위를 줄이고, 오래된 데이터는 DETACH 후 저비용 스토리지로 아카이빙할 수 있습니다.
권장 파티션 키
- 시계열/로그:
event_date(RANGE) - 멀티테넌시:
tenant_id(LIST) - 균등 분산: 고카디널리티 키 (HASH)
운영 체크리스트
- 쿼리 WHERE 절과 파티션 키 일치(프루닝 보장)
- 핫 파티션만 필수 인덱스 유지(쓰기 비용 절감)
- 주기적
DETACH→아카이빙→백업 - 대량 삭제는 DROP 파티션으로 대체
주의. 글로벌 UNIQUE/FK 제약이 필요하면 (tenant_id, local_key) 복합 키로 설계하거나, 검증을 트리거·배치로 분리해 충돌을 방지하세요.
5. JSONB와 관계형의 하이브리드 설계
스키마가 자주 변하는 영역은 JSONB로 수용하되, 핵심 비즈니스 키·금액·상태는 정규 컬럼로 엄격히 관리하는 방식이 안정적입니다. JSONB에는 GIN 인덱스를 적용해 키/경로 기반 조회 성능을 보장하고, 레포팅/집계용 컬럼은 표현식 인덱스 또는 머티리얼라이즈드 뷰로 보완하세요.
| 상황 | 정규 테이블 적합 | JSONB 적합 |
|---|---|---|
| 핵심 도메인 규칙 | 강한 제약/참조 필요 | 규칙이 잦은 변경 |
| 외부 API 페이로드 | 핵심 필드만 추출 저장 | 원문 보존/검색 용이 |
| 검색/필터 | 일관된 컬럼 기반 | 키 존재/부분 일치 조회 |
| 집계/리포트 | SQL 집계/뷰 강점 | 사전 계산 결과 캐싱 |
6. 멀티테넌시(다중고객) 스키마 선택지
비즈니스·운영 요건(격리·확장·비용)에 따라 테넌시 모델을 고르세요.
- 별도 DB: 보안·규모 격리가 최상. 운영/비용↑, 스키마 관리 복잡.
- 스키마 분리: 테넌트별 네임스페이스. 마이그레이션 자동화 필수.
- 공용 스키마(+RLS):
tenant_id컬럼과 Row-Level Security로 격리. 비용 효율↑, 정책·인덱스 설계 주의.

7. 자주 묻는 질문 (FAQ)
보통 3차 정규화(NF3)까지 진행해 이상현상을 방지합니다. 하지만 성능 문제가 확인되면 조회 패턴에 따라 부분 비정규화를 고려하세요.
아닙니다. 인덱스는 SELECT를 빠르게 하지만 INSERT/UPDATE/DELETE 성능을 저하시킵니다. 따라서 실제 쿼리 로그와 실행계획을 근거로 최소한만 설계하세요.
핵심 도메인 데이터(고객, 주문, 금액 등)는 반드시 정규 테이블로 관리해야 합니다. JSONB는 변동성이 큰 필드나 외부 API 응답 저장에 적합합니다.
수천만 건 이상 쌓이거나, 주기적 보관/삭제가 필요한 로그·시계열 테이블에서 고려하세요. 파티션 프루닝이 가능하도록 쿼리 조건과 파티션 키를 맞추는 것이 핵심입니다.
네, 정책 설계와 테스트를 철저히 하면 안전합니다. 다만 슈퍼유저, 백엔드 배치 작업 등은 RLS를 우회할 수 있으므로 반드시 보안 점검을 병행해야 합니다.
8. 마무리 요약
✅ 정규화·제약·인덱스·파티션의 균형이 성능을 만든다
PostgreSQL 설계의 본질은 일관성과 예측 가능성입니다. 정규화로 품질을 확보하고, 제약으로 규칙을 고정하며, 인덱스로 조회를 가속하고, 파티셔닝으로 성장에 대비하세요. JSONB 하이브리드와 멀티테넌시 전략은 서비스의 변화 속도를 흡수하는 완충재입니다. 오늘 소개한 체크리스트를 팀의 코드리뷰·스키마리뷰에 바로 적용해 보세요. 여러분의 데이터베이스는 더 단단하고, 더 빠르고, 더 저렴해질 것입니다.
'SW프로그래밍 개발' 카테고리의 다른 글
| 타임리프(thymeleaf)에서 폼 처리하기: th:form과 데이터 바인딩 완벽 가이드 (0) | 2025.09.22 |
|---|---|
| IntelliJ vs Eclipse, Spring Boot 개발에 최강자는 누구? (4) | 2025.08.27 |
| 초보자를 위한 DB 설계 기본 원칙 완벽 가이드(성능과 확장성 고려한 DB설계 포함) (2) | 2025.08.22 |
| 팀 프로젝트 생산성 UP! VSCode-GitHub 협업 환경 구축법 (5) | 2025.08.16 |
| JSON vs XML: 무엇을 선택해야 할까? 구조, 속도, 보안, 확장성 비교 (1) | 2025.04.21 |