Primary key, Unique, Foreign key

mskimdev·2026년 4월 17일

MySQL

목록 보기
10/20

PRIMARY KEY, UNIQUE, FOREIGN KEY

테이블을 처음 설계할 때 가장 자주 마주치는 세 가지 제약 조건이 있다. PRIMARY KEY, UNIQUE, FOREIGN KEY. 셋 다 "중복을 막는다"는 인상을 주는데, 역할이 미묘하게 다르다. 각각이 왜 존재하는지, 어떤 상황에서 쓰이는지를 구분해두면 테이블 설계가 훨씬 명확해진다.


키 제약 조건

PRIMARY KEY

기본 키(Primary Key)는 테이블의 각 행을 유일하게 식별하는 컬럼이다. 사람으로 치면 주민등록번호 같은 것이다. 이름은 같은 사람이 여러 명일 수 있지만, 주민등록번호는 절대 겹치지 않는다.

PRIMARY KEY는 내부적으로 NOT NULL + UNIQUE가 동시에 적용된다. 즉, 값이 반드시 있어야 하고, 중복되면 안 된다.

CREATE TABLE members (
    member_id   INT          PRIMARY KEY AUTO_INCREMENT,
    member_name VARCHAR(50)  NOT NULL,
    email       VARCHAR(100) NOT NULL
);

member_id가 PRIMARY KEY다. AUTO_INCREMENT를 함께 쓰면 INSERT할 때 값을 직접 넣지 않아도 DB가 자동으로 1, 2, 3... 순서로 채워준다.

INSERT INTO members (member_name, email) VALUES ('김민수', 'kim@email.com');
INSERT INTO members (member_name, email) VALUES ('이지현', 'lee@email.com');
member_id | member_name | email
----------+-------------+---------------
        1 | 김민수       | kim@email.com
        2 | 이지현       | lee@email.com

PRIMARY KEY는 테이블당 딱 하나만 지정할 수 있다. 여러 컬럼을 묶어서 하나의 기본 키로 만드는 복합 기본 키(Composite Primary Key)도 가능하지만, 그 경우에도 기본 키 자체는 하나다.

-- 복합 기본 키: 두 컬럼의 조합이 유일해야 한다
CREATE TABLE order_items (
    order_id   INT NOT NULL,
    product_id INT NOT NULL,
    quantity   INT NOT NULL,
    PRIMARY KEY (order_id, product_id)
);

UNIQUE

UNIQUE 제약 조건도 중복을 막는다는 점에서 PRIMARY KEY와 비슷하다. 차이는 두 가지다.

  • PRIMARY KEY는 테이블에 하나뿐이지만, UNIQUE는 여러 컬럼에 동시에 붙일 수 있다.
  • PRIMARY KEY는 NULL을 허용하지 않지만, UNIQUE는 NULL을 허용한다. (NULL은 "값 없음"이라서 중복으로 보지 않는다)

이메일 컬럼을 예시로 보면, 같은 이메일로 두 계정을 만들 수 없게 막고 싶다. 이메일은 행의 식별자로 쓰기엔 적합하지 않고(바뀔 수 있으니까), 그래도 중복은 안 된다 — 이럴 때 UNIQUE를 쓴다.

CREATE TABLE members (
    member_id   INT          PRIMARY KEY AUTO_INCREMENT,
    member_name VARCHAR(50)  NOT NULL,
    email       VARCHAR(100) NOT NULL UNIQUE,
    phone       VARCHAR(20)  UNIQUE
);

emailphone 모두 UNIQUE가 붙어 있다. 각각 중복 없이 유일해야 한다.

INSERT INTO members (member_name, email) VALUES ('김민수', 'kim@email.com');

-- 오류: email 'kim@email.com'이 이미 존재한다
INSERT INTO members (member_name, email) VALUES ('김민수2', 'kim@email.com');

phone처럼 값이 없을 수도 있는 컬럼은 NULL을 허용하면서 UNIQUE만 걸어두면 된다. NULL끼리는 중복으로 보지 않기 때문에 여러 행에 phone이 없어도 오류가 나지 않는다.

FOREIGN KEY

외래 키(Foreign Key)는 앞의 두 제약 조건과 성격이 조금 다르다. PRIMARY KEY와 UNIQUE가 하나의 컬럼 안에서 값을 검사한다면, FOREIGN KEY는 다른 테이블의 값을 참조한다.

예를 들어 주문 테이블(orders)이 있고, 어떤 회원이 주문했는지 기록해야 한다면, orders 테이블에 member_id를 넣는다. 이때 존재하지 않는 회원 ID가 들어오면 안 된다. FOREIGN KEY가 이걸 막아준다.

CREATE TABLE members (
    member_id   INT         PRIMARY KEY AUTO_INCREMENT,
    member_name VARCHAR(50) NOT NULL
);

CREATE TABLE orders (
    order_id   INT  PRIMARY KEY AUTO_INCREMENT,
    member_id  INT  NOT NULL,
    order_date DATE NOT NULL,
    FOREIGN KEY (member_id) REFERENCES members(member_id)
);

orders.member_idmembers.member_id를 참조한다. 이 관계를 참조 무결성(Referential Integrity)이라고 부른다.

INSERT INTO members (member_name) VALUES ('김민수'); -- member_id = 1

-- 정상: member_id 1은 members에 존재한다
INSERT INTO orders (member_id, order_date) VALUES (1, '2026-04-17');

-- 오류: member_id 99는 members에 없다
INSERT INTO orders (member_id, order_date) VALUES (99, '2026-04-17');

FOREIGN KEY가 걸려 있으면 참조 중인 부모 행을 함부로 지울 수 없다. 회원에게 주문 내역이 있는 상태에서 삭제하려 하면 오류가 난다. 이 동작을 ON DELETE, ON UPDATE 옵션으로 제어할 수 있다.

FOREIGN KEY (member_id) REFERENCES members(member_id)
    ON DELETE CASCADE
    ON UPDATE CASCADE
옵션동작
RESTRICT (기본값)참조 중인 행이 있으면 삭제/수정 불가
CASCADE부모가 삭제/수정되면 자식도 함께 삭제/수정
SET NULL부모가 삭제되면 자식의 FK 컬럼을 NULL로 변경
NO ACTIONRESTRICT와 동일 (MySQL에서는 같게 처리)

CASCADE는 편리하지만 주의가 필요하다. 회원 하나를 지웠는데 연결된 주문 수십 건이 줄줄이 삭제될 수 있다.


세 가지 비교 정리

PRIMARY KEYUNIQUEFOREIGN KEY
목적행의 고유 식별컬럼 값의 중복 방지다른 테이블 참조
NULL 허용불가가능컬럼 설정에 따름
테이블당 개수1개여러 개 가능여러 개 가능
자동 인덱스생성됨생성됨생성 권장

세 제약 조건은 각자의 역할이 있다. PRIMARY KEY는 "이 행이 누구인지", UNIQUE는 "이 값이 겹치면 안 된다", FOREIGN KEY는 "이 값이 저쪽 테이블에 반드시 있어야 한다"는 것을 보장한다. 하나의 테이블에 셋이 함께 쓰이는 것도 자연스럽다.

profile
<- 개발 공부하는 나

0개의 댓글