AI 시대 DB 모델링 & SQL 완벽 마스터4-6 정규화 직관
테크타니 LEARN — 섹션 04 · DB 모델링

1NF·2NF·3NF 외우지 마세요 —
직관 두 개면 충분합니다

정규화라는 단어, 듣기만 해도 머리가 아파오죠. 약속드린 대로 — 이 강의에서 정규화 이론은 외우지 않습니다. 같은 정보 두 번 적지 마라, 한 테이블에 한 종류만. 이 두 직관만 잡으면 정규화의 본질은 물론, shop이 왜 5테이블로 쪼개져 있는지까지 한 번에 풀립니다.

난이도쉬움소요8분준비물4-2 ~ 4-5 (부모-자식 · ERD)암기량0 — 직관 2개면 끝

이번 섹션의 여정

4-1 ~ 4-4

완료 ✓

4-5 ERD 읽는 법

완료 ✓

4-6 정규화 직관

지금 여기

4-7 ~ 4-8

실무 ERD · 종합 실습 ⭐

섹션 5

AI 협업 SQL 작성법

정규화 — 이론부터 외우지 않습니다

“정규화” 라는 단어를 들으면 보통 이런 게 떠오릅니다 — 1차 정규형, 2차 정규형, 3차 정규형… 외워야 하나? 시험 봐야 하나? 안 외웁니다. 약속드린 대로요.

정규화의 본질은 한 문장입니다 — “테이블을 왜, 어떻게 쪼개는가.” 4-5에서 shop ERD를 읽으며 5테이블을 봤을 때 “애초에 왜 이렇게 흩어놨을까?” 라는 질문이 남았죠. 그 답이 오늘의 두 직관입니다.

오늘 잡을 직관 — 단 두 줄
직관 1 │ 같은 정보 두 번 적지 마라
직관 2 │ 한 테이블에 한 종류만
 
이 두 줄이 1NF · 2NF · 3NF 가 말하려는 핵심의 전부입니다
정규화(Normalization)가 뭐냐면

중복이 없도록 데이터를 여러 테이블로 나누어 설계하는 원칙입니다. 교과서는 단계(정규형)별로 가르치지만, 실무에서 쓰는 판단 감각은 위의 두 직관으로 만들어져요.

시작 전 체크
4-2 — 부모/자식 테이블 개념
4-4 — FK가 테이블을 이어준다는 것
4-5 — shop ERD를 한 번 읽어본 경험

직관 1 — 같은 정보 두 번 적지 마라

사고 실험으로 시작합니다. 만약 회원 정보를 주문 테이블에 다 박아두면 어떻게 될까요? 김철수 님은 우리 shop의 단골입니다 — ordersmember_id 가 1인 주문이 실제로 6건이에요. 주문할 때마다 이름과 나이를 같이 적는 설계라면 —

사고 실험 — orders (잘못된 설계)
order_idmember_idmember_namemember_ageordered_at
11김철수352025-02-01
61김철수352025-04-10
141김철수352025-08-01
201김철수352025-10-12
271김철수352025-12-10
341김철수352026-02-15
같은 이름과 나이가 6번 — 주문할 때마다 복사됩니다

이 설계에서 사고가 어떻게 시작되는지, 시나리오 그대로 따라가 볼게요. 어느 날 김철수 님이 개명을 합니다.

개명 요청 접수01 / 03

회원이 이름 변경을 요청합니다. 흔한 일이죠. 그런데 중복 설계에서는 이 평범한 요청이 수색 작전이 됩니다.

김철수 → 김철민으로 바꿔주세요
운영자의 수색 작전02 / 03

주문 테이블에 박힌 6군데를 전부 찾아 고쳐야 합니다. 주문이 600건, 6,000건이라면? 누락 없이 다 찾는다는 보장이 없어요.

orders 에서 김철수가 박힌 행을 전부 찾아 UPDATE
한 군데 빼먹음 — 사고03 / 03

다섯 군데만 고치고 한 군데를 놓치는 순간 — 같은 회원ID에 김철민과 김철수가 공존합니다. 나중에 분석할 때 어느 게 맞는 이름인지 아무도 모릅니다.

같은 member_id 인데 이름이 두 개

직관 1을 지킨 설계는 — 우리가 이미 쓰고 있는 shop의 구조 그대로입니다. 회원 정보는 members 에 한 번만 적고, 주문은 회원ID만 가리킵니다.

좋은 설계 — 회원 정보는 한 곳에만
members (회원 정보는 여기 한 곳)
member_id │ member_name │ age
──────────┼─────────────┼────
1 │ 김철수 │ 35 ← 단 한 번만 적힘
 
orders (주문은 회원ID 만 가리킴)
order_id │ member_id │ ordered_at
─────────┼───────────┼───────────
1 │ 1 │ 2025-02-01
6 │ 1 │ 2025-04-10
⋮ │ ⋮ │ ⋮
이름이 바뀌면 members 한 군데만 — 모든 주문에 자동 반영
📐 직관 1

같은 정보 두 번 적지 마라. 같은 정보를 두 번 적는 순간 — 데이터 일관성 사고가 시작됩니다.

직관 2 — 한 테이블에 한 종류만

두 번째 사고 실험. 이번엔 반대로 — members 테이블에 주문 정보까지 같이 들어 있다고 해봅시다.

사고 실험 — members (잘못된 설계)
member_idmember_namelast_order_idlast_order_datetotal_orders
1김철수342026-02-156
회원 테이블 안에 주문이라는 다른 종류의 데이터가 끼어 있습니다

언뜻 편해 보입니다 — 회원만 조회해도 주문 요약이 따라 나오니까요. 그런데 members회원 정보 테이블이고, 주문은 다른 종류의 데이터입니다. 섞는 순간 문제가 줄줄이 따라옵니다.

✕ members 에 주문 정보까지

두 종류가 섞이면 다 같이 망가집니다

새 주문이 들어올 때마다 회원 테이블도 같이 업데이트해야 하고, 회원 정보만 보고 싶은데 주문이 끼어 산만합니다. 더 큰 문제 — 한 행만 저장되니 마지막 주문 이전의 기록은 아예 사라집니다.

✓ 회원은 members, 주문은 orders

각 테이블이 한 종류만 담당

주문이 들어오면 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;
DBeaver — SQL Editor (Ctrl + Enter)
SELECT COUNT(*) AS total_orders FROM orders WHERE member_id = 1;
total_orders
6
DBeaver — SQL Editor (Ctrl + Enter)
SELECT order_id, ordered_at FROM orders WHERE member_id = 1 ORDER BY ordered_at DESC LIMIT 1;
order_idordered_at
342026-02-15
집계 컬럼을 미리 저장해두지 않아도 — 필요할 때 SQL 로 꺼냅니다
항상 최신 · 항상 정확 — 원본이 orders 한 곳이니까요
📐 직관 2

한 테이블에 한 종류의 정보만. 회원 테이블은 회원만, 주문 테이블은 주문만 — 조합이 필요하면 SQL이 합니다.

shop의 설계 — 두 직관이 이미 적용돼 있습니다

이제 우리 shop의 init.sql 을 다시 보세요. 4-5에서 ERD로 읽었던 그 5테이블에 두 직관이 그대로 녹아 있습니다.

shop — 두 직관 적용 현황
테이블담당하는 한 종류같은 정보 중복
members회원 (이름 · 나이 · 성별 · 가입일)없음
categories카테고리 (카테고리명)없음
products상품 (상품명 · 가격 · 카테고리ID)없음
orders주문 (회원ID · 주문일)없음
order_items주문상세 (어떤 상품을 몇 개)없음
각 테이블이 한 종류만 담당 — 같은 정보가 두 번 박힌 곳이 없습니다
이름 변경은 members 한 군데, 가격 변경은 products 한 군데. 끝
💡 — 왜 이렇게 흩어놨어? 의 답

5개 테이블로 쪼개져 있어서 — 오히려 “같은 것을 한 번만 적는다”가 가능합니다.

테이블을 나누는 건 복잡하게 만드는 게 아니라, 중복을 없애기 위한 선택이에요.

비정규화 — 5초만 알아두세요

실무에서 가끔 “비정규화(de-normalization)한다” 는 말을 듣게 됩니다. 일부러 중복을 만든다는 뜻이에요. 언제 할까요? 성능 때문입니다. 조회가 너무 자주 일어나고 JOIN 비용이 큰 경우 — “매번 JOIN 하느니 미리 복제해두자” 라는 선택이죠.

정규화가 기본, 비정규화는 예외

비정규화는 데이터가 수억 건 규모이거나 조회가 초당 수천 건일 때 고려하는 매우 특수한 경우의 처방입니다. 학습 단계는 물론 일반 실무에서도 — 정규화 기본, 비정규화는 예외. 이 순서만 기억하세요.

한눈에 정리

오늘의 두 직관
직관의미안 지키면
1 같은 정보 두 번 적지 X한 정보는 한 곳에만데이터 일관성 사고
2 한 테이블에 한 종류만회원·주문·상품은 각자의 집산만함 + 기록 유실
⭐ 판단 직관

이 두 줄만 머리에 있으면 — “이 설계가 맞나? 쪼개야 하나?” 판단 직관이 생깁니다.

1NF·2NF·3NF, 외울 필요 없어요.

자주 막히는 지점

① 그럼 1NF·2NF·3NF 는 평생 몰라도 되나요?

오늘의 두 직관이 사실 그 정규형들이 말하려는 핵심입니다 — 중복을 없애라(2NF·3NF의 본질), 한 테이블에 한 종류만(1NF의 정신).

FIX ▸면접·시험처럼 용어가 필요한 순간이 오면 그때 찾아보세요. 직관이 있으면 이론은 10분이면 읽힙니다.
② 테이블을 쪼개면 JOIN 이 늘어나는데, 손해 아닌가요?

쪼갠 만큼 조회할 때 JOIN이 필요해지는 건 사실입니다. 그래도 순서는 바뀌지 않아요 — 정규화 기본, 비정규화는 예외.

FIX ▸JOIN 은 DB가 가장 잘하는 일입니다. 중복이 일으키는 일관성 사고의 비용이 훨씬 커요 — JOIN 비용이 진짜 문제가 되는 특수한 경우의 처방이 바로 위의 비정규화입니다.
③ 실무 테이블에서 total_orders 같은 집계 컬럼을 본 적 있어요

잘못된 설계일 수도 있지만, 성능을 위해 의도적으로 둔 비정규화(캐시 컬럼)일 수도 있습니다.

FIX ▸원본은 항상 orders 쪽 — 그 컬럼은 복제본이라는 걸 알고 다루면 됩니다. 둘이 어긋나 있으면 원본이 정답입니다.

오늘 챙길 건 단 두 줄입니다 — 같은 정보 두 번 적지 마라, 한 테이블에 한 종류만. 직관이 손에 잡혔는지 연습 문제로 확인해보세요. 그다음 강의는 4-7 실무 ERD 분석 — 쇼핑몰·SNS·예약 시스템의 ERD를 나란히 놓고, 부모-자식 패턴이 어디서나 똑같이 반복된다는 걸 확인합니다.

NEXT LESSON4-6 연습 문제

잘못된 설계를 두 직관으로 직접 찾아내고 고쳐봅니다.

GO ▸ 연습 문제 풀기