본문 바로가기
C.W.K.
Stream
Lesson 09 of 10 · published

UNIQUE, CHECK, EXCLUDE 제약

~14 min · schema, constraints

Level 0스키마 새싹
0 XP0/86 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

데이터베이스를 마지막 방어선으로

앱 검사는 친절한 오류를 만드는 데 좋지만 모든 쓰기 경로를 통제하지는 못해. 마이그레이션, 관리 스크립트, 다른 서비스가 우회할 수 있어. 절대로 깨지면 안 되는 규칙은 제약으로 데이터베이스에 새겨야 해.

UNIQUE는 중복을 막아

이메일이나 상품 코드처럼 같은 값이 둘 존재하면 안 되는 열에 써. 여러 열을 묶어 “사용자 1명은 한 상품에 리뷰를 하나만 남길 수 있다” 같은 범위의 고유성도 표현할 수 있어. NULL 처리 방식은 요구에 맞게 확인해야 해.

CHECK는 행 안의 논리를 검사해

가격이 0 이상인지, 할인율이 50 이하인지처럼 한 행의 불리언 조건을 강제해. 다른 행이나 시간에 따라 달라지는 규칙을 억지로 넣기보다 트리거나 별도 모델을 검토해.

EXCLUDE는 서로 겹치지 못하게 해

UNIQUE가 같은 값만 막는다면 EXCLUDE는 범위의 겹침처럼 연산 결과가 충돌하는 조합을 막아. 회의실 예약이나 유효 기간에 특히 강해. GiST 색인과 필요한 연산자 클래스를 이용해 “같은 자원에서 시간이 조금이라도 겹치면 안 된다”를 한 제약으로 표현할 수 있어.

Code

한 열과 여러 열에 적용하는 UNIQUE·sql
CREATE TABLE users (
    id       INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    email    TEXT UNIQUE NOT NULL,
    username TEXT UNIQUE NOT NULL
);

CREATE TABLE reviews (
    id         INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    user_id    INTEGER NOT NULL REFERENCES users(id),
    product_id INTEGER NOT NULL REFERENCES products(id),
    rating     INTEGER NOT NULL CHECK (rating BETWEEN 1 AND 5),
    body       TEXT,
    UNIQUE (user_id, product_id)  -- 유저당 product 당 1 review
);
업무 규칙을 스키마에 새기는 CHECK·sql
CREATE TABLE orders (
    id       INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    quantity INTEGER NOT NULL CHECK (quantity > 0),
    price    NUMERIC(10,2) NOT NULL CHECK (price >= 0),
    status   TEXT NOT NULL CHECK (status IN ('pending','paid','shipped','refunded')),
    discount NUMERIC(4,3) NOT NULL DEFAULT 0
                CHECK (discount BETWEEN 0 AND 1)
);
EXCLUDE로 방 예약 겹침을 막아·sql
CREATE EXTENSION IF NOT EXISTS btree_gist;

CREATE TABLE room_bookings (
    id     INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    room   TEXT NOT NULL,
    during TSRANGE NOT NULL,
    EXCLUDE USING gist (room WITH =, during WITH &&)
);

INSERT INTO room_bookings (room, during) VALUES
  ('A1', '[2026-05-10 10:00, 2026-05-10 11:00)');
INSERT INTO room_bookings (room, during) VALUES
  ('A1', '[2026-05-10 10:30, 2026-05-10 12:00)');
-- ERROR: conflicting key value violates exclusion constraint

External links

Exercise

가지고 있는 스키마에서 현재 앱 코드에만 들어 있는 업무 규칙을 찾아 CHECK 제약으로 하나 이상 표현해. 동료에게 검토를 부탁하고 그 규칙을 스키마가 맡아도 되는지 토론해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.