콘텐츠로 이동

제약 조건 (Constraint)

제약 조건: 테이블에 저장되는 데이터의 무결성(Integrity) 을 보장하기 위해 컬럼이나 테이블에 걸어두는 규칙. 규칙을 어기는 데이터는 DBMS가 거부한다.

왜 쓰는지

애플리케이션 코드로 유효성 검사를 하더라도 여러 서비스가 하나의 DB를 공유하거나 직접 SQL로 데이터를 수정하는 경우에는 코드가 우회될 수 있다. 제약 조건을 DB 레벨에서 걸면 어떤 경로로 데이터가 들어오든 무결성이 보장된다.

어떻게 쓰는지

테이블 생성 시 제약 조건

CREATE TABLE member (
    id       BIGINT PRIMARY KEY AUTO_INCREMENT,
    email    VARCHAR(100) NOT NULL UNIQUE,
    age      INT CHECK (age >= 0 AND age <= 150),
    is_active BOOLEAN DEFAULT TRUE,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

기존 테이블에 제약 조건 추가

-- NOT NULL은 ADD로 불가능, MODIFY 사용
ALTER TABLE member MODIFY COLUMN name VARCHAR(50) NOT NULL;

-- UNIQUE 추가
ALTER TABLE member ADD CONSTRAINT uq_member_phone UNIQUE (phone);

-- FOREIGN KEY 추가
ALTER TABLE orders ADD CONSTRAINT fk_orders_member 
    FOREIGN KEY (member_id) REFERENCES member(id) ON DELETE CASCADE;

-- CHECK 제약 추가
ALTER TABLE member ADD CONSTRAINT ck_age CHECK (age >= 0 AND age <= 150);

제약 조건 삭제

-- UNIQUE, FOREIGN KEY, CHECK 삭제
ALTER TABLE member DROP CONSTRAINT uq_member_email;

-- PRIMARY KEY 삭제 (MySQL)
ALTER TABLE member DROP PRIMARY KEY;

언제 쓰는지

상황 제약 조건 이유
NULL 불가 필드 ✅ NOT NULL 필수 정보 보장
값의 유일성 필요 ✅ UNIQUE 이메일, 사용자명 등 중복 방지
행 식별 ✅ PRIMARY KEY 테이블 내 행 유일성 필수
테이블 간 관계 ✅ FOREIGN KEY 참조 무결성 유지
값의 범위/조건 ✅ CHECK 나이, 상태값 범위 제한
자동 기본값 ✅ DEFAULT timestamp, boolean 기본값
애플리케이션 검증 ❌ DB 제약 단순 유효성 검사는 코드로

장점

장점 설명
데이터 무결성 DB 진입점 상관없이 규칙 강제
비즈니스 규칙 중앙화 DB 레벨에서 한 번에 관리
성능 애플리케이션 검증 우회로 안전
유지보수 규칙 변경이 한 곳(DB)에서만 필요
오류 방지 잘못된 데이터 입력 자체를 차단

단점

단점 설명
성능 오버헤드 FOREIGN KEY 검증, CHECK 계산 비용
유연성 부족 규칙 변경 시 ALTER TABLE로 스키마 변경 필요
개발 속도 제약 조건 설계에 시간 소요
복잡한 규칙 복합 조건은 DB에서 표현하기 어려움 (애플리케이션으로)
마이그레이션 어려움 FOREIGN KEY로 인한 데이터 마이그레이션 복잡성

종류

NOT NULL

CREATE TABLE member (
    name VARCHAR(50) NOT NULL  -- NULL 저장 불가
);
  • NULL을 허용하지 않는다.
  • 주의: NULL과 빈 문자열('')은 다르다. NOT NULL은 빈 문자열을 허용한다.

UNIQUE

CREATE TABLE member (
    email VARCHAR(100) UNIQUE  -- 중복 불가, NULL은 허용
);
  • 컬럼 내 값이 유일해야 한다.
  • NULL은 중복 체크에서 제외된다. (NULL은 비교 불가)

PRIMARY KEY (기본 키)

CREATE TABLE member (
    id BIGINT PRIMARY KEY AUTO_INCREMENT
);
  • NOT NULL + UNIQUE를 합친 것. 행을 유일하게 식별한다.
  • 테이블당 하나만 존재한다.
  • 복합 기본 키: PRIMARY KEY (order_id, product_id)

FOREIGN KEY (외래 키)

CREATE TABLE orders (
    id        BIGINT PRIMARY KEY,
    member_id BIGINT,
    FOREIGN KEY (member_id) REFERENCES member(id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);
  • 다른 테이블의 기본 키를 참조한다. 참조 무결성 보장.
  • ON DELETE / ON UPDATE 옵션:
옵션 동작
CASCADE 부모 행 삭제/수정 시 자식 행도 함께 처리
SET NULL 부모 행 삭제 시 자식 컬럼을 NULL로
RESTRICT 자식 행이 있으면 부모 행 삭제 거부 (기본값)
NO ACTION RESTRICT와 유사

주의: 외래 키 제약은 강력하지만 대규모 서비스에서는 성능·운영 부담으로 인해 DB 외래 키를 사용하지 않고 애플리케이션 레벨에서 참조 무결성을 관리하는 경우도 많다. (트레이드오프 판단 필요)

DEFAULT

CREATE TABLE member (
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    is_active  BOOLEAN  DEFAULT TRUE
);
  • 값을 지정하지 않으면 기본값이 자동으로 들어간다.

CHECK

CREATE TABLE member (
    age INT CHECK (age >= 0 AND age <= 150)
);
  • 컬럼에 저장되는 값의 범위나 조건을 검사한다.
  • MySQL 8.0.16 이상에서 실제 강제 적용 (이전 버전은 문법은 허용되나 무시됨).

제약 조건 이름 지정

-- 이름 없는 제약 조건 (DBMS가 자동 생성)
email VARCHAR(100) UNIQUE

-- 이름 있는 제약 조건 (에러 메시지 명확, 나중에 삭제 용이)
CONSTRAINT uq_member_email UNIQUE (email),
CONSTRAINT fk_orders_member FOREIGN KEY (member_id) REFERENCES member(id)

: 제약 조건에 이름을 붙이면 위반 시 에러 메시지에 이름이 표시되어 디버깅이 쉽고, ALTER TABLE ... DROP CONSTRAINT 로 명시적으로 제거할 수 있다.