Postgres 테이블 설계의 흔한 실수와 회피 패턴
PostgreSQL 테이블 설계에서 자주 발생하는 실수와 bad schema postgres 피해야 할 것들을 사례와 SQL 예제로 정리한 실무용 참고자료
목차
들어가며
테이블 설계는 데이터 품질과 서비스 성능에 직접 영향을 준다. 초기에 잘못 설계하면 나중에 수정 비용이 크게 증가한다. 이 글은 postgres 테이블 설계 실수 사례를 중심으로, 피해야 할 패턴과 현실적인 대안을 설명한다. 초보자도 이해하기 쉽도록 단계별로 정리했다.
자주 저지르는 설계 실수
1. 모든 것을 TEXT로 저장
가장 흔한 실수다. 숫자, 날짜, 불리언 등을 텍스트로 저장하면 비교와 집계가 느려진다. 타입이 명확하면 제약과 인덱스가 동작해 데이터 무결성이 보장된다.
2. 기본 키를 남용하거나 잘못 선택
복합 키를 남용하거나 의미 있는 값을 기본 키로 사용하는 경우가 있다. 의미 있는 값은 변경될 가능성이 있고, 복합 키는 조인과 인덱스 관리에 부담을 준다. 대신 순수한 surrogate key와 UNIQUE 제약을 조합해 관리하는 편이 안전하다.
3. 외래 키(FK) 생략
성능 때문에 FK를 아예 빼는 경우가 있다. 무결성 검증을 애플리케이션에만 의존하면 데이터 불일치가 생긴다. 트랜잭션 경계와 인덱스를 고려해 FK를 설계한다.
4. 과도한 정규화 또는 비정규화
정규화는 중복을 줄이고 무결성을 높이지만, 지나치면 조인 비용이 커진다. 반대로 무분별한 비정규화는 업데이트 복잡도와 데이터 일관성 문제를 초래한다. 업무 패턴에 따라 균형을 맞춘다.
나쁜 스키마 패턴과 이유
패턴 A: 거대한 단일 테이블
모든 칼럼을 한 테이블에 넣는 패턴이다. 조회 성능 저하, 넓은 행(wide row)으로 인한 I/O 증가, NULL이 많은 칼럼 관리 난점이 발생한다.
패턴 B: JSONB로 모든 것을 저장
유연성이 필요할 때 유용하지만, 검색과 집계가 잦다면 적절한 칼럼과 인덱스를 사용하는 편이 낫다. JSONB 내부에 중요한 데이터를 두면 인덱스 적용이 어렵다.
패턴 C: 지나친 블록체인 같은 로그 테이블 설계
변경 이력을 모두 저장하는 건 중요하지만, 무분별한 증가는 파티셔닝과 아카이빙 전략이 없으면 운영 부담이 커진다.
사례와 코드: 잘못된 설계와 개선안
아래 예시는 흔한 실수와 바로잡는 방법을 보여준다.
예시 1 — 잘못된 설계
CREATE TABLE users_bad (
id TEXT PRIMARY KEY,
name TEXT,
age TEXT,
signup_date TEXT,
is_active TEXT
);
문제점: 타입이 모두 TEXT라 집계와 조건 검색이 비효율적이며 무결성을 DB가 보장하지 못한다.
개선안
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT,
signup_date TIMESTAMP WITH TIME ZONE DEFAULT now(),
is_active BOOLEAN DEFAULT true
);
설명: 적절한 타입 사용으로 쿼리 성능과 제약 활용이 가능해진다. BIGSERIAL(또는 IDENTITY)을 사용해 surrogate key를 부여했다.
예시 2 — JSONB 남용
CREATE TABLE orders_bad (
id BIGSERIAL PRIMARY KEY,
data JSONB
);
-- 주문 상태로 자주 검색해야 하는데 JSONB 내부에 둔 경우
SELECT count(*) FROM orders_bad WHERE data->>'status' = 'paid';
개선: 자주 검색하거나 조인할 필드는 별도 칼럼으로 분리하고, JSONB는 부가 정보를 위한 용도로 제한한다.
개선안 코드
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(id),
status TEXT NOT NULL,
payload JSONB,
created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);
CREATE INDEX ON orders (status);
인덱스와 성능 관련 실수
- 무분별한 인덱스 생성: 쓰기 비용 증가와 디스크 사용 증가를 초래한다.
- 잘못된 칼럼 순서의 복합 인덱스: WHERE 절과 정렬 조건을 고려해 순서를 정한다.
- 인덱스 없이 조인 컬럼 사용: 조인 성능이 떨어진다. FK에는 인덱스를 고려한다.
운영 관점의 체크리스트
- 타입과 제약(CONSTRAINT) 정의로 무결성 확보
- 쿼리 패턴에 따른 인덱스 설계와 모니터링
- 파티셔닝과 아카이빙 전략으로 성장 대비
- 스키마 변경 계획: 마이그레이션 정책과 롤백 절차 마련
맺음말
postgres 테이블 설계 실수는 초기에 피하면 비용을 크게 줄일 수 있다. bad schema postgres 피해야 할 것을 이해하고, 실무 중심의 설계 원칙을 따르면 운영 안정성과 성능을 모두 확보할 수 있다. 마지막으로 중요한 것은 서비스 특성에 맞춘 균형 있는 설계와 지속적인 모니터링이다.