1NF·2NF·3NF 외우지 마세요 —
직관 두 개면 충분합니다
정규화라는 단어, 듣기만 해도 머리가 아파오죠. 약속드린 대로 — 이 강의에서 정규화 이론은 외우지 않습니다. 같은 정보 두 번 적지 마라, 한 테이블에 한 종류만. 이 두 직관만 잡으면 정규화의 본질은 물론, shop이 왜 5테이블로 쪼개져 있는지까지 한 번에 풀립니다.
이번 섹션의 여정
완료 ✓
완료 ✓
지금 여기
실무 ERD · 종합 실습 ⭐
AI 협업 SQL 작성법
정규화 — 이론부터 외우지 않습니다
“정규화” 라는 단어를 들으면 보통 이런 게 떠오릅니다 — 1차 정규형, 2차 정규형, 3차 정규형… 외워야 하나? 시험 봐야 하나? 안 외웁니다. 약속드린 대로요.
정규화의 본질은 한 문장입니다 — “테이블을 왜, 어떻게 쪼개는가.” 4-5에서 shop ERD를 읽으며 5테이블을 봤을 때 “애초에 왜 이렇게 흩어놨을까?” 라는 질문이 남았죠. 그 답이 오늘의 두 직관입니다.
중복이 없도록 데이터를 여러 테이블로 나누어 설계하는 원칙입니다. 교과서는 단계(정규형)별로 가르치지만, 실무에서 쓰는 판단 감각은 위의 두 직관으로 만들어져요.
직관 1 — 같은 정보 두 번 적지 마라
사고 실험으로 시작합니다. 만약 회원 정보를 주문 테이블에 다 박아두면 어떻게 될까요? 김철수 님은 우리 shop의 단골입니다 — orders 에 member_id 가 1인 주문이 실제로 6건이에요. 주문할 때마다 이름과 나이를 같이 적는 설계라면 —
| order_id | member_id | member_name | member_age | ordered_at |
|---|---|---|---|---|
| 1 | 1 | 김철수 | 35 | 2025-02-01 |
| 6 | 1 | 김철수 | 35 | 2025-04-10 |
| 14 | 1 | 김철수 | 35 | 2025-08-01 |
| 20 | 1 | 김철수 | 35 | 2025-10-12 |
| 27 | 1 | 김철수 | 35 | 2025-12-10 |
| 34 | 1 | 김철수 | 35 | 2026-02-15 |
이 설계에서 사고가 어떻게 시작되는지, 시나리오 그대로 따라가 볼게요. 어느 날 김철수 님이 개명을 합니다.
회원이 이름 변경을 요청합니다. 흔한 일이죠. 그런데 중복 설계에서는 이 평범한 요청이 수색 작전이 됩니다.
주문 테이블에 박힌 6군데를 전부 찾아 고쳐야 합니다. 주문이 600건, 6,000건이라면? 누락 없이 다 찾는다는 보장이 없어요.
다섯 군데만 고치고 한 군데를 놓치는 순간 — 같은 회원ID에 김철민과 김철수가 공존합니다. 나중에 분석할 때 어느 게 맞는 이름인지 아무도 모릅니다.
직관 1을 지킨 설계는 — 우리가 이미 쓰고 있는 shop의 구조 그대로입니다. 회원 정보는 members 에 한 번만 적고, 주문은 회원ID만 가리킵니다.
같은 정보 두 번 적지 마라. 같은 정보를 두 번 적는 순간 — 데이터 일관성 사고가 시작됩니다.
직관 2 — 한 테이블에 한 종류만
두 번째 사고 실험. 이번엔 반대로 — members 테이블에 주문 정보까지 같이 들어 있다고 해봅시다.
| member_id | member_name | last_order_id | last_order_date | total_orders |
|---|---|---|---|---|
| 1 | 김철수 | 34 | 2026-02-15 | 6 |
언뜻 편해 보입니다 — 회원만 조회해도 주문 요약이 따라 나오니까요. 그런데 members 는 회원 정보 테이블이고, 주문은 다른 종류의 데이터입니다. 섞는 순간 문제가 줄줄이 따라옵니다.
두 종류가 섞이면 다 같이 망가집니다
새 주문이 들어올 때마다 회원 테이블도 같이 업데이트해야 하고, 회원 정보만 보고 싶은데 주문이 끼어 산만합니다. 더 큰 문제 — 한 행만 저장되니 마지막 주문 이전의 기록은 아예 사라집니다.
각 테이블이 한 종류만 담당
주문이 들어오면 orders 에 1행 추가 — 그게 전부입니다. 회원 테이블은 건드릴 일이 없고, 주문 기록은 전부 남아요. 총 주문 수·마지막 주문이 필요하면 아래처럼 SQL 한 줄이면 됩니다.
직관 2를 지킨 설계에서 “김철수의 총 주문 수”, “마지막 주문” 이 궁금하면 — 저장해둘 필요 없이 그때그때 꺼내면 됩니다.
USE shop;
-- 김철수(member_id = 1)의 총 주문 수 — SQL 한 줄
SELECT
COUNT(*) AS total_orders
FROM
orders
WHERE
member_id = 1;
-- 마지막 주문 — 역시 한 줄
SELECT
order_id,
ordered_at
FROM
orders
WHERE
member_id = 1
ORDER BY
ordered_at DESC
LIMIT 1;| total_orders |
|---|
| 6 |
| order_id | ordered_at |
|---|---|
| 34 | 2026-02-15 |
한 테이블에 한 종류의 정보만. 회원 테이블은 회원만, 주문 테이블은 주문만 — 조합이 필요하면 SQL이 합니다.
shop의 설계 — 두 직관이 이미 적용돼 있습니다
이제 우리 shop의 init.sql 을 다시 보세요. 4-5에서 ERD로 읽었던 그 5테이블에 두 직관이 그대로 녹아 있습니다.
| 테이블 | 담당하는 한 종류 | 같은 정보 중복 |
|---|---|---|
| members | 회원 (이름 · 나이 · 성별 · 가입일) | 없음 |
| categories | 카테고리 (카테고리명) | 없음 |
| products | 상품 (상품명 · 가격 · 카테고리ID) | 없음 |
| orders | 주문 (회원ID · 주문일) | 없음 |
| order_items | 주문상세 (어떤 상품을 몇 개) | 없음 |
5개 테이블로 쪼개져 있어서 — 오히려 “같은 것을 한 번만 적는다”가 가능합니다.
테이블을 나누는 건 복잡하게 만드는 게 아니라, 중복을 없애기 위한 선택이에요.
비정규화 — 5초만 알아두세요
실무에서 가끔 “비정규화(de-normalization)한다” 는 말을 듣게 됩니다. 일부러 중복을 만든다는 뜻이에요. 언제 할까요? 성능 때문입니다. 조회가 너무 자주 일어나고 JOIN 비용이 큰 경우 — “매번 JOIN 하느니 미리 복제해두자” 라는 선택이죠.
비정규화는 데이터가 수억 건 규모이거나 조회가 초당 수천 건일 때 고려하는 매우 특수한 경우의 처방입니다. 학습 단계는 물론 일반 실무에서도 — 정규화 기본, 비정규화는 예외. 이 순서만 기억하세요.
한눈에 정리
| 직관 | 의미 | 안 지키면 |
|---|---|---|
| 1 같은 정보 두 번 적지 X | 한 정보는 한 곳에만 | 데이터 일관성 사고 |
| 2 한 테이블에 한 종류만 | 회원·주문·상품은 각자의 집 | 산만함 + 기록 유실 |
이 두 줄만 머리에 있으면 — “이 설계가 맞나? 쪼개야 하나?” 판단 직관이 생깁니다.
1NF·2NF·3NF, 외울 필요 없어요.
자주 막히는 지점
오늘의 두 직관이 사실 그 정규형들이 말하려는 핵심입니다 — 중복을 없애라(2NF·3NF의 본질), 한 테이블에 한 종류만(1NF의 정신).
쪼갠 만큼 조회할 때 JOIN이 필요해지는 건 사실입니다. 그래도 순서는 바뀌지 않아요 — 정규화 기본, 비정규화는 예외.
잘못된 설계일 수도 있지만, 성능을 위해 의도적으로 둔 비정규화(캐시 컬럼)일 수도 있습니다.
orders 쪽 — 그 컬럼은 복제본이라는 걸 알고 다루면 됩니다. 둘이 어긋나 있으면 원본이 정답입니다.오늘 챙길 건 단 두 줄입니다 — 같은 정보 두 번 적지 마라, 한 테이블에 한 종류만. 직관이 손에 잡혔는지 연습 문제로 확인해보세요. 그다음 강의는 4-7 실무 ERD 분석 — 쇼핑몰·SNS·예약 시스템의 ERD를 나란히 놓고, 부모-자식 패턴이 어디서나 똑같이 반복된다는 걸 확인합니다.