데이터베이스 정규화는 한 문장으로 요약됩니다: "같은 사실은 딱 한 곳에만 저장하라." 이걸 체계적으로 하는 방법이 1NF → 2NF → 3NF입니다.

왜 중요하냐면 — 같은 정보가 여러 행에 흩어져 있으면, 하나만 고치고 나머지를 안 고치는 순간 데이터가 모순됩니다. 이걸 갱신 이상이라고 합니다. 아래에서 직접 겪어 보세요.

갱신 이상 — 직접 체험

Prof.A가 Room 502로 이사했습니다. 셀을 클릭해서 수정해 보세요. 비정규 테이블에서는 모든 행을 고쳐야 합니다 — 하나라도 빠뜨리면 데이터가 모순됩니다.

Before (비정규)
studentcourseinstructorofficegrade
S1 KimMATH101Prof.ARoom 301 ← clickA
S1 KimCS102Prof.BRoom 405B+
S2 LeeCS102Prof.BRoom 405A
S3 ParkMATH101Prof.ARoom 301 ← clickB
0/2 수정됨
After (3NF)
Instructorsinstructor_idinstructor_office
Prof.ARoom 502
Prof.BRoom 405
3NF에서는 Instructors 테이블에서 딱 1행만 수정하면 끝.

Prof.A의 연구실이 바뀌었는데, 비정규 테이블에서는 행 2개를 전부 수동으로 고쳐야 합니다. 하나라도 빠뜨리면 "Prof.A는 Room 301이면서 Room 502"라는 모순이 생깁니다. 3NF 테이블에서는 딱 1행만 고치면 끝입니다.

이것이 정규화의 전부입니다 — 나머지는 "어떤 열을 어디에 놓아야 한 곳에만 저장되는가"를 판단하는 규칙입니다.

함수 종속 — 정규화의 판단 기준

정규화는 "이 열이 어디에 의존하는가"를 따집니다. 이걸 함수 종속이라고 합니다.

예를 들어 student_id → student_name은 "학생 ID를 알면 이름이 결정된다"는 뜻입니다.

아래 다이어그램에서 화살표 색깔을 보세요:

  • 초록 = 전체 키에 의존 (정상)
  • 노랑 = 키의 일부에만 의존 (부분 종속 → 2NF 위반)
  • 빨강 점선 = 비키 열을 거쳐 의존 (이행적 종속 → 3NF 위반)
함수 종속 한눈에 보기

Enrollment(student_id, course_id, student_name, course_title, instructor_id, instructor_office, grade)

student_id기본키course_id기본키student_namecourse_titleinstructor_idinstructor_officegrade
완전 함수 종속 (전체 키에 의존)부분 함수 종속 (키의 일부에 의존)이행적 종속 (비키 열을 거쳐 의존)

노랑과 빨강 화살표를 없애는 것 = 테이블을 쪼개는 것 = 정규화입니다.

1NF → 2NF → 3NF 한 번에 보기

아래에서 단계별로 테이블이 어떻게 쪼개지는지 직접 클릭해 보세요. 빨간 셀은 "이 열이 여기 있으면 안 된다"는 표시입니다.

정규화 단계별 분해
모든 정보가 한 테이블에. 한 셀에 여러 값.
Enrollment
student_id 🔑student_namecoursesgrades
S1KimMATH101, CS102A, B+
S2LeeCS102A
S3ParkMATH101, CS102, PHY201B, A, A-
문제: courses 열에 여러 값이 한 셀에 들어있음.
해결: 학생-과목 쌍마다 행 하나로 분리.

1NF: 한 셀에 값 하나

한 셀에 MATH101, CS102 같은 여러 값을 넣으면 검색도 안 되고 조인도 안 됩니다.

규칙: 모든 셀이 원자적(하나의 값)이어야 한다.

phone1, phone2, phone3 같은 열도 같은 문제입니다 — 값을 열 이름으로 숨긴 것일 뿐.

2NF: 부분 종속 제거

복합키 (student_id, course_id)인 테이블에서 student_namestudent_id에만 의존합니다. 전체 키에 의존하는 게 아니라 일부에만 의존하는 거죠 — 이게 부분 함수 종속입니다.

규칙: 모든 비키 열은 전체 키에 의존해야 한다.

부분 종속이 있으면 해당 열을 빼서 별도 테이블로 만듭니다.

키가 단일 열이면? 부분 종속 자체가 불가능하므로 1NF면 자동으로 2NF입니다.

3NF: 이행적 종속 제거

Courses 테이블에서 instructor_officecourse_id(키)에 직접 의존하지 않습니다. course_id → instructor_id → instructor_office 경로를 거칩니다 — 비키 열이 다른 비키 열에 의존하는 거죠. 이게 이행적 종속입니다.

규칙: 비키 열은 키만을 통해 의존해야 한다. 다른 비키 열을 거치면 안 된다.

해결: instructor_office를 Instructors 테이블로 분리.

암기 대신 질문 하나

"모든 비키 속성은 키를, 전체 키를, 키만을 따라야 한다."

이 문장이 전부입니다:

  • 키를 = 1NF (사실이 키로 식별 가능해야 함)
  • 전체 키를 = 2NF (키의 일부가 아니라 전체에 의존)
  • 키만을 = 3NF (비키 열을 거치지 않고 키에 직접 의존)

시험에서 정규화 문제가 나오면, 이 질문만 던지세요: "이 열이 누구에게 의존하는가? 전체 키인가, 키의 일부인가, 아니면 비키 열인가?"

자주 하는 실수

"3NF까지 하면 끝이다" — 3NF는 좋은 출발점이지 끝이 아닙니다. 더 엄격한 BCNF가 필요한 경우도 있고, 반대로 성능을 위해 일부러 비정규화하는 경우도 있습니다.

"테이블을 많이 쪼개면 좋다" — 정규화의 목적은 테이블 수를 늘리는 게 아니라 "각 테이블이 한 종류의 사실만 책임지게" 하는 것입니다. 쪼개야 할 이유(종속 관계) 없이 쪼개면 조인만 복잡해집니다.

"NoSQL이면 정규화 필요 없다" — 정규화는 관계형 DB 전용 용어이지만, "중복 데이터의 일관성 유지" 문제는 어떤 DB에서든 존재합니다. 비정규화할 때도 "무엇을 포기하는지" 알고 해야 합니다.

직접 풀어보기
#1보통
주어진 테이블 Student(student_id, name, course_id, course_name, grade)에서 부분 함수 종속을 찾고, 2NF로 분해하세요.
#2어려움
Employee(emp_id, emp_name, dept_id, dept_name, dept_manager)에서 이행적 종속을 찾고, 3NF로 분해하시오.
Hint
dept_manager는 emp_id에 직접 의존하나요, 아니면 dept_id를 거쳐 의존하나요?

문제 풀이가 필요하신가요?

문제를 올리면 검증된 단계별 풀이를 몇 초 만에 받을 수 있습니다.

GPAI Solver 열기 →