PRIMARY KEY·FOREIGN KEY·UNIQUE·NOT NULL·CHECK·DEFAULT 제약 조건으로 잘못된 데이터가 DB에 들어오는 것을 원천 차단합니다.
🎯 학습 목표
이 강을 마치면 테이블 설계 시 데이터 무결성을 보장하는 6가지 제약 조건(PRIMARY KEY, FOREIGN KEY, UNIQUE, NOT NULL, CHECK, DEFAULT)을 올바르게 적용할 수 있습니다. 각 제약이 어떤 잘못된 데이터를 막는지, 그리고 외래키가 테이블 간 참조 무결성을 어떻게 보호하는지 이해할 수 있게 됩니다.
💡 제약 조건이 필요한 이유
이 섹션에서는 애플리케이션 코드 대신 DB 수준에서 데이터를 검증해야 하는 이유를 살펴보겠습니다.
애플리케이션(Python 코드, 웹 서버 등)에서 유효성 검사를 해도 DB에 직접 SQL을 실행하면 우회할 수 있습니다. 또한 여러 애플리케이션이 같은 DB를 사용할 때 모든 곳에서 동일한 검증 로직을 유지하기 어렵습니다. 제약 조건(Constraint)은 DB 수준에서 데이터 규칙을 강제해, 어떤 경로로 데이터가 들어오더라도 규칙을 위반하면 거부합니다.
예를 들어 orders.member_id가 존재하지 않는 회원을 가리키는 경우(참조 무결성 위반), 이메일 열에 같은 주소가 두 번 들어가는 경우(유일성 위반), 수량에 -1이 입력되는 경우(비즈니스 규칙 위반)를 모두 DB가 자동으로 막아줍니다.
🔑 PRIMARY KEY — 기본키
이 섹션에서는 행을 고유하게 식별하는 기본키(PRIMARY KEY) 제약을 살펴보겠습니다.
PRIMARY KEY는 테이블의 각 행을 유일하게 식별하는 열(또는 열 조합)입니다. 기본키에는 자동으로 두 가지 제약이 적용됩니다. 첫째로 UNIQUE(중복 불가), 둘째로 NOT NULL(NULL 불가)입니다.
-- 단일 열 기본키
CREATE TABLE products (
product_id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
-- 복합 기본키 (두 열의 조합이 고유해야 함)
CREATE TABLE order_items (
order_id INTEGER,
item_seq INTEGER,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
PRIMARY KEY (order_id, item_seq) -- 테이블 수준 제약
);
SQLite에서 INTEGER PRIMARY KEY는 특별히 SQLite의 내부 rowid와 연결되어 AUTOINCREMENT 없이도 자동 증가하는 정수를 할당받습니다. 반면 TEXT PRIMARY KEY는 자동 증가가 없고 직접 값을 지정해야 합니다.
🔗 FOREIGN KEY — 외래키
이 섹션에서는 테이블 간 참조 무결성을 보장하는 외래키 제약을 살펴보겠습니다.
FOREIGN KEY는 다른 테이블의 기본키를 참조하는 열에 붙여, 참조하는 행이 반드시 존재하도록 강제합니다. 12강에서 개념으로 배웠던 외래키를 이제 실제 제약으로 정의합니다.
CREATE TABLE orders_with_fk (
order_id INTEGER PRIMARY KEY AUTOINCREMENT,
member_id INTEGER NOT NULL,
book_id INTEGER NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
order_date TEXT NOT NULL DEFAULT (date('now')),
status TEXT NOT NULL DEFAULT 'PENDING',
FOREIGN KEY (member_id) REFERENCES members(member_id)
ON DELETE RESTRICT -- 회원 삭제 시 주문 있으면 거부
ON UPDATE CASCADE, -- 회원 ID 변경 시 주문의 member_id도 함께 변경
FOREIGN KEY (book_id) REFERENCES books(book_id)
ON DELETE RESTRICT
);
ON DELETE와 ON UPDATE는 참조하는 행이 삭제되거나 기본키가 변경될 때 어떻게 처리할지 정의합니다.
| 옵션 | 설명 |
|---|---|
| RESTRICT (기본값) | 참조하는 행이 있으면 삭제·수정 거부 |
| CASCADE | 참조하는 행도 함께 삭제·수정 |
| SET NULL | 참조하는 행의 외래키를 NULL로 설정 |
| NO ACTION | 즉시는 허용, 트랜잭션 종료 시 체크 |
⚠️ SQLite에서 외래키 제약은 기본적으로 비활성화되어 있습니다. 활성화하려면 연결할 때마다 PRAGMA foreign_keys = ON;을 실행해야 합니다. MySQL, PostgreSQL은 기본으로 활성화되어 있습니다.
-- SQLite에서 외래키 활성화
PRAGMA foreign_keys = ON;
-- 이제 참조 무결성 위반 시 오류 발생
INSERT INTO orders_with_fk (member_id, book_id)
VALUES (999, 1); -- 오류: member_id 999는 members에 없음
🔒 UNIQUE, NOT NULL, CHECK, DEFAULT
이 섹션에서는 나머지 네 가지 제약 조건을 살펴보겠습니다.
UNIQUE는 열의 모든 값이 고유해야 합니다. 기본키와 달리 NULL을 허용하고(NULL은 서로 다른 값으로 취급), 테이블에 여러 UNIQUE 제약을 가질 수 있습니다.
CREATE TABLE users (
user_id INTEGER PRIMARY KEY,
email TEXT NOT NULL UNIQUE, -- 이메일 중복 불가
username TEXT NOT NULL UNIQUE -- 닉네임 중복 불가
);
NOT NULL은 해당 열에 NULL 값을 허용하지 않습니다. 필수 입력 필드에 사용합니다.
CHECK는 열 값이 특정 조건을 만족해야 합니다. 애플리케이션 수준의 유효성 검사를 DB 수준으로 내립니다.
CREATE TABLE products (
product_id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price INTEGER NOT NULL CHECK(price > 0), -- 가격은 양수
stock INTEGER NOT NULL DEFAULT 0 CHECK(stock >= 0), -- 재고는 0 이상
category TEXT CHECK(category IN ('A', 'B', 'C')) -- 허용 카테고리만
);
CHECK 제약을 위반하는 INSERT나 UPDATE를 시도하면 즉시 오류가 발생합니다.
DEFAULT는 INSERT 시 값을 생략하면 지정한 기본값이 자동으로 입력됩니다. 상수 값이나 함수(date('now'), datetime('now') 등)를 사용할 수 있습니다.
-- 세 가지 제약을 조합한 완성형 테이블
CREATE TABLE review (
review_id INTEGER PRIMARY KEY AUTOINCREMENT,
member_id INTEGER NOT NULL,
book_id INTEGER NOT NULL,
rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5),
content TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (member_id) REFERENCES members(member_id) ON DELETE CASCADE,
FOREIGN KEY (book_id) REFERENCES books(book_id) ON DELETE CASCADE,
UNIQUE (member_id, book_id) -- 한 회원이 같은 도서에 리뷰 하나만
);
review_id는 자동 증가 기본키, rating은 1~5 사이만 허용, created_at은 현재 시각 자동 입력, 같은 (회원, 도서) 조합의 리뷰는 한 개만 허용(UNIQUE 복합 제약), 회원이나 도서가 삭제되면 리뷰도 함께 삭제(CASCADE)하는 완성도 높은 테이블입니다.
📝 핵심 요약
이번 강에서 배운 내용을 정리하겠습니다.
| 제약 | 역할 | NULL 허용 |
|---|---|---|
| PRIMARY KEY | 행 고유 식별 (UNIQUE + NOT NULL) | 불가 |
| FOREIGN KEY | 다른 테이블 PK 참조 무결성 | 가능 (기본) |
| UNIQUE | 열 값 중복 불가 | 가능 (NULL끼리는 별개) |
| NOT NULL | NULL 값 금지 | 불가 |
| CHECK(조건) | 값이 조건을 만족해야 함 | NULL은 체크 통과 |
| DEFAULT 값 | 값 생략 시 기본값 자동 입력 | — |
제약 조건은 데이터 품질을 코드가 아닌 DB가 보장하는 핵심 메커니즘입니다. 다음 22강에서는 대용량 데이터에서 조회 속도를 극적으로 높이는 인덱스를 배웁니다. 언제 인덱스가 효과적인지, 인덱스의 부작용은 무엇인지 이해하게 됩니다.
관련 주제
- PRIMARY KEY
- FOREIGN KEY
- UNIQUE
- NOT NULL
- CHECK
- DEFAULT
- 참조 무결성
- 데이터 무결성
- 개발·프로그래밍
- 개발·프로그래밍 강의
- SQL·데이터베이스 입문 24강
- 무료강의
- 무료 온라인 강의
- NUGUNA
- 누구나
📚 시리즈 전체 공유
SQL·데이터베이스 입문 24강
이 강의가 속한 시리즈는 총 24강, 모두 무료입니다. 처음부터 배우려는 동료에게 시리즈 전체를 알려 주세요.
댓글
불러오는 중...
