포스트

상태 코드 컬럼 설계: 매직넘버를 없애라

상태값을 1·2·3 매직넘버로 두면 가독성과 무결성이 무너진다. enum·코드테이블·CHECK 제약으로 상태 컬럼을 안전하게 설계하는 법.

상태 코드 컬럼 설계: 매직넘버를 없애라

상태 코드 관리를 다루다 보면 테이블 곳곳에 status = 1, status = 3 같은 숫자가 박힌다. 짤 때는 자명해 보이지만, 6개월 뒤 3이 무엇이었는지 아무도 모른다. 상태 컬럼은 단순해 보여도 가독성·무결성·확장성이 한꺼번에 걸리는 설계 지점이다.

매직넘버의 세 가지 비용

order.status = 2라는 코드는 세 가지를 잃는다. 첫째, 가독성: 2가 결제완료인지 배송중인지 코드만 봐선 모른다. 둘째, 무결성: 컬럼이 그냥 INT라면 99 같은 존재하지 않는 상태도 저장된다. 셋째, 추적성: 어떤 상태값이 실제로 쓰이는지, 어디서 분기되는지 grep으로 추적이 안 된다.

해법은 두 층으로 나뉜다. 애플리케이션 층의 enum과 DB 층의 제약이다. 둘은 대체재가 아니라 보완재다.

1) 애플리케이션 — enum으로 의미 부여

Java라면 상태를 enum으로 정의하고, DB에는 안정적인 코드 문자열로 저장한다.

1
2
3
4
5
6
7
8
9
10
public enum OrderStatus {
    PENDING("PENDING"),
    PAID("PAID"),
    SHIPPED("SHIPPED"),
    CANCELED("CANCELED");

    private final String code;
    OrderStatus(String code) { this.code = code; }
    public String code() { return code; }
}

저장값으로 ordinal()(0,1,2…)을 쓰면 안 된다. enum 상수 순서를 바꾸거나 중간에 하나 끼우는 순간 기존 데이터의 의미가 통째로 어긋난다. 반드시 명시적 코드값(문자열 또는 고정 정수)을 매핑한다.

2) DB — CHECK 제약 또는 코드테이블

DB는 애플리케이션을 믿지 않는다. 배치·수기 보정·다른 서비스가 같은 테이블을 건드릴 수 있으므로, 허용 집합을 DB가 직접 강제해야 한다.

값이 적고 거의 안 변하면 CHECK 제약이 가볍고 명확하다.

1
2
3
4
5
CREATE TABLE orders (
  id      BIGINT PRIMARY KEY,
  status  VARCHAR(20) NOT NULL
          CHECK (status IN ('PENDING','PAID','SHIPPED','CANCELED'))
);

값에 표시명·정렬순서·활성여부 같은 메타데이터가 붙거나 운영 중 추가가 잦다면 코드테이블 + FK가 낫다.

1
2
3
4
5
6
7
8
9
10
CREATE TABLE order_status_code (
  code      VARCHAR(20) PRIMARY KEY,
  label     VARCHAR(50) NOT NULL,
  sort_no   INT NOT NULL,
  is_active BOOLEAN NOT NULL DEFAULT TRUE
);

ALTER TABLE orders
  ADD CONSTRAINT fk_order_status
  FOREIGN KEY (status) REFERENCES order_status_code(code);

FK는 존재하지 않는 상태값의 저장을 원천 차단한다. 새 상태는 코드만 추가하면 배포 없이 늘어난다.

CHECK vs 코드테이블, 어떻게 고르나

기준CHECK 제약코드테이블 + FK
값 변경 빈도거의 없음잦음
부가 메타데이터없음표시명·순서 등 필요
조회 시 조인불필요label 위해 조인
무결성 강제DDL 수준참조 무결성

운영 함정

  • 상태 전이를 컬럼이 막아주지 않는다: 제약은 “허용된 값인가”만 본다. SHIPPED → PENDING처럼 말이 안 되는 역행은 막지 못한다. 전이 규칙은 서비스 계층에서 상태 머신으로 검증해야 한다.
  • 이미 들어간 더러운 데이터: CHECK·FK를 나중에 거는 순간, 기존에 들어간 비정상 상태값 때문에 DDL이 실패한다. 제약 추가 전 SELECT DISTINCT status로 실값 분포를 먼저 확인하고 보정한다.

핵심 요약

  • 매직넘버는 가독성·무결성·추적성을 모두 잃는다. enum으로 의미를, DB 제약으로 무결성을 강제하라.
  • enum 저장값은 ordinal() 금지, 고정 코드값 매핑.
  • 정적이면 CHECK, 메타데이터·잦은 변경이면 코드테이블+FK.
  • 허용 집합은 DB가 강제하되, 상태 전이 규칙은 서비스 계층이 책임진다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.