제약 조건 (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
NULL을 허용하지 않는다.- 주의:
NULL과 빈 문자열('')은 다르다.NOT NULL은 빈 문자열을 허용한다.
UNIQUE
- 컬럼 내 값이 유일해야 한다.
NULL은 중복 체크에서 제외된다. (NULL은 비교 불가)
PRIMARY KEY (기본 키)
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
- 컬럼에 저장되는 값의 범위나 조건을 검사한다.
- 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 로 명시적으로 제거할 수 있다.