외래 키

Foreign Key

개념

한 표의 칸에 다른 표의 행 번호를 적어 두 표를 이어 붙이고, 없는 행을 가리키는 일이 생기지 않게 막아 주는 장치입니다.

등장 배경

데이터를 표 하나에 다 적던 시절이 있었습니다. 주문 내역을 남긴다면 주문 줄마다 주문한 사람의 이름과 이메일과 회사명을 통째로 같이 적는 식입니다.

불편은 같은 사실이 여러 줄에 흩어진 뒤에 옵니다. 고객사 하나가 사명을 바꾸면 그 회사가 낸 주문 4,000줄을 전부 고쳐야 합니다. 한 줄이라도 빠뜨리면 같은 회사가 두 이름으로 집계되고, 매출을 회사별로 묶은 표에서 숫자가 두 조각으로 갈립니다.

그래서 표를 나눠 쓰는 방식이 자리를 잡았습니다. 회사는 회사 테이블에 한 번만 적고, 주문 줄에는 회사 번호만 남깁니다. 테이블은 행과 열로 데이터를 담는 저장 단위이고, 한 행이 회사 하나입니다. 이제 사명은 한 줄만 고치면 끝납니다.

문제는 번호만 적어 두면 그 번호가 진짜 있는 회사를 가리키는지 아무도 보장하지 않는다는 점입니다. 페이지덱에서 태오가 테스트로 만든 계정을 지운 적이 있습니다. 계정은 지워졌는데 그 계정이 남긴 문서 줄에는 없어진 회원 번호가 그대로 남아 있었고, 문서 목록 화면이 작성자 이름을 찾다가 빈칸을 만나 통째로 깨졌습니다. 외래 키는 이렇게 가리킬 데가 없는 번호가 애초에 저장되지 못하게 데이터베이스 쪽에 거는 잠금입니다.

작동 방식

주문 표의 회원 번호 칸을 두고 "이 칸의 값은 반드시 회원 표에 실제로 있는 번호여야 한다" 고 데이터베이스에 등록하면 그 칸이 외래 키가 됩니다. 등록하는 순간부터 데이터베이스가 매 쓰기마다 그 조건을 검사합니다.

거는 데 필요한 것은 셋입니다.

  1. 가리켜지는 쪽의 기본 키 — 행 하나를 고유하게 집어내는 칸입니다. 회원 표의 회원 번호가 여기에 해당합니다.
  2. 가리키는 쪽의 칸 — 주문 표의 회원 번호 칸입니다. 값의 종류가 상대 쪽 기본 키와 같아야 합니다.
  3. 둘을 잇는 제약 선언 — 표를 만들 때나 나중에 마이그레이션으로 추가합니다. 마이그레이션은 이미 데이터가 쌓인 표의 구조를 바꾸는 작업입니다.

걸고 나면 두 방향에서 막힙니다. 없는 회원 번호로 주문을 저장하려 하면 저장이 거부되고, 주문이 달려 있는 회원을 지우려 하면 삭제가 거부됩니다. 삭제 쪽은 어떻게 할지 골라 둘 수 있습니다. 같이 지우거나, 막거나, 빈 값으로 바꾸는 세 가지입니다. 회원이 지워질 때 문서도 같이 지우는 게 맞는 제품이 있고, 문서는 남기고 작성자 칸만 비우는 게 맞는 제품이 있습니다. 외래 키는 그 결정을 코드가 아니라 표 정의에 새겨 둡니다.

검사를 프로그램 쪽에서 해도 되는데 굳이 데이터베이스에 거는 이유는 입구가 여럿이기 때문입니다. 웹 서버가 쓰고, 배치 작업이 쓰고, 운영자가 콘솔에서 직접 고치기도 합니다. 프로그램마다 같은 검사를 넣으면 언젠가 한 곳이 빠지고, 그 경로로 들어온 잘못된 값은 몇 달 뒤에야 발견됩니다.

쓰이는 곳

외래 키는 데이터가 표 여러 개로 갈린 곳이면 어디에나 붙습니다. 표를 나누는 설계를 정규화라고 하는데, 같은 사실이 두 군데 적히지 않도록 표를 쪼개는 일이고, 쪼갠 표를 다시 잇는 게 외래 키입니다.

페이지덱의 스키마만 봐도 여러 곳에 걸려 있습니다. 스키마는 어떤 표가 있고 표마다 어떤 칸이 있는지를 적어 둔 설계도입니다.

  1. 문서 표의 작성자 칸이 회원 표를 가리킵니다. 문서 목록에서 작성자 이름을 보여 줄 때 이 연결을 타고 갑니다.
  2. 댓글 표의 문서 칸이 문서 표를 가리킵니다. 문서가 지워지면 댓글도 같이 지우도록 걸어 뒀습니다.
  3. 팀 멤버십 표가 회원 표와 팀 표를 동시에 가리킵니다. 한 표에 외래 키가 둘 이상 붙는 흔한 모양입니다.

읽을 때는 이 연결을 따라 두 표를 맞춰 한 결과로 꺼냅니다. 문서 목록에 작성자 이름을 붙여 달라는 요청이 그것입니다.

반대로 일부러 걸지 않는 자리도 있습니다. 데이터가 너무 커져 여러 서버에 쪼개 담으면 가리키는 쪽과 가리켜지는 쪽이 다른 서버에 놓여 검사 자체가 불가능해집니다. 쓰기가 몰리는 표에서는 검사 비용이 아까워 빼기도 합니다. 뺀 만큼의 책임은 프로그램이 지게 되고, 비정규화처럼 속도를 위해 정확함을 조금 내주는 선택과 같은 성격입니다.

실무 용례

"그 칸에 외래 키 걸려 있어서 회원 먼저 지우면 에러 나요."

삭제 순서를 바꾸거나 연결 삭제 설정을 봐야 한다는 뜻입니다.

"외래 키 없이 번호만 들고 있는 표라 지금 데이터에 깨진 줄이 좀 있을 거예요."

검사가 없었으니 가리킬 데 없는 값이 쌓였을 가능성이 높다는 말입니다.

"이 표는 쓰기가 많아서 외래 키는 빼고 애플리케이션에서 확인하기로 했어요."

성능을 위해 검사를 밖으로 옮겼다는 설계 결정을 공유하는 말입니다.

페이지덱에서 태오가 새 기능의 표를 그릴 때 가장 먼저 정하는 것이 어느 칸이 어느 표를 가리키는지입니다. 댓글 기능을 붙일 때도 표를 만들기 전에 문서와 회원 중 무엇을 가리킬지, 문서가 지워지면 댓글을 어떻게 할지를 먼저 정했습니다.

이 결정이 늦어지면 나중에 고치기가 어렵습니다. 이미 쌓인 데이터에 가리킬 데 없는 값이 섞여 있으면 등록 자체가 거부되고, 그 줄들을 먼저 손으로 정리해야 합니다. 페이지덱도 초기에 만든 표 하나에 반년 뒤 외래 키를 걸다가 깨진 줄 300여 개를 정리하는 데 이틀을 썼습니다.

흔한 오해

외래 키를 속도를 올려 주는 장치로 아는 경우가 있습니다. 표를 잇는 것과 빨리 찾는 것이 머릿속에서 붙어 있어서 생기는 오해입니다. 실제로는 반대에 가깝습니다. 외래 키는 쓸 때마다 상대 표를 확인하는 검사라서 쓰기를 아주 조금 느리게 만듭니다. 빠르기를 맡는 것은 찾아보기 역할을 하는 인덱스입니다. 외래 키를 걸면 인덱스가 자동으로 함께 만들어지는 데이터베이스가 많아 둘이 같아 보일 뿐입니다.

두 표를 실제로 붙여 둔 것이라고 읽는 것도 흔합니다. 이름에 키라는 말이 들어가 무언가를 열어 주는 통로처럼 들리기 때문입니다. 저장돼 있는 건 번호 하나뿐이고, 상대 표의 내용은 읽을 때마다 새로 맞춰 꺼냅니다. 붙어 있다고 여기면 주문 시점의 회사명을 남겨야 하는 화면에서 엉뚱한 결과를 보게 됩니다. 지난달 주문서에 지금 바뀐 사명이 찍혀 나오는 식입니다.

자주 헷갈리는 용어

용어무엇을 가리키는가외래 키와의 차이
기본 키자기 표의 행 하나기본 키는 자기 표 안에서 행을 고유하게 집어내는 칸이고, 외래 키는 남의 표의 기본 키를 적어 두는 칸입니다. 한 칸이 양쪽 역할을 겸하기도 합니다

같이 보면 좋은 페이지

  • 데이터베이스

    외래 키가 어느 자리에 놓인 개념인지, 함께 쓰이는 다른 구조는 무엇인지 봅니다

  • 스키마

    외래 키를 포함한 표 설계 전체가 어떻게 적히는지 이어집니다

마지막 업데이트 2026-09-16