관계의 구조 —
읽기에서 설계로
섹션 3에서는 이미 만들어진 테이블을 읽었습니다. 이번 섹션은 방향이 반대예요. 아직 테이블이 하나도 없고 업무 설명만 있다고 생각해 보세요. 그 설명을 듣고 어떤 테이블이 필요하며 서로 어떻게 연결할지 정하는 일 — 이것이 데이터베이스 모델링입니다.
이번 섹션의 여정
완료 ✓
지금 여기
FK 위치 정하기
관계 종류 · ERD · 정규화
모델링은 그림부터 그리는 일이 아닙니다
모델링이라고 하면 네모 상자와 관계선이 가득한 ERD부터 떠올리기 쉽습니다. 하지만 그림은 우리가 내린 결정을 보여주는 표현 방법일 뿐이에요. 그림을 그리는 것 자체가 모델링의 전부는 아닙니다.
그림보다 먼저, 세 가지를 결정해야 합니다.
① 회원, 상품처럼 계속 관리해야 하는 대상도 있고 주문처럼 발생한 사건도 있습니다. ② 한 행이 회원 한 명인지, 주문 한 건인지, 주문에 담긴 상품 한 종류인지 분명해야 하고요. ③ 어떤 주문을 누가 했는지, 그 주문에 어떤 상품이 담겼는지도 연결해야 합니다.
테이블만 나열해도 부족하고, 관계선만 그어도 부족합니다 — 테이블과 관계가 함께 업무의 구조를 만듭니다.
업무 문장 하나에서 출발합니다
아주 짧은 문장 하나로 시작해 볼게요.
먼저 눈에 보이는 명사를 찾으면 회원과 상품이 있습니다. 그래서 members와 products를 테이블 후보로 생각할 수 있어요.
다만 여기서 바로 조심할 게 있습니다. 명사라고 해서 모두 테이블이 되는 것은 아닙니다.
| 업무 설명에 나온 명사 | 테이블로 만들까? |
|---|---|
| 회원 | ○ — 여러 명을 구별해 계속 기록해야 한다 |
| 상품 | ○ — 여러 종류를 구별해 계속 기록해야 한다 |
| 쇼핑몰 화면 | × — 저장해서 관리할 기록이 아니다 |
| 주문 버튼 | × — 저장해서 관리할 기록이 아니다 |
업무에서 따로 관리할 정보와 규칙이 있는지도 함께 확인해야 합니다. 즉 — 명사는 좋은 출발점이지만 정답은 아닙니다.
명사뿐 아니라 ‘사건’도 기록합니다
실제 shop DB의 members에서 한 행은 회원 한 명입니다. member_id로 회원을 구별하고 이름·성별·나이·가입일을 기록하죠. products에서는 한 행이 상품 한 종류이고, product_id로 구별하며 상품명과 가격을 기록합니다.
그런데 이 두 테이블만으로는 “주문했다”는 사실을 남길 수 없습니다.
김철수 회원이 오늘 주문하고 다음 달에 다시 주문할 수도 있어요. 각 주문을 서로 구별해야 하고, 언제 주문했는지도 기록해야 합니다. 그래서 업무 문장의 동사인 “주문한다” 에서 orders라는 테이블이 나옵니다.
| 업무 문장의 조각 | 품사 | 테이블 | 한 행의 의미 |
|---|---|---|---|
| 회원이 | 명사 | members | 회원 한 명 |
| 상품을 | 명사 | products | 상품 한 종류 |
| 주문한다 | 동사(사건) | orders | 주문 한 건 |
주문, 결제, 예약, 접수처럼 발생한 사건도 — 따로 구별하고 기억해야 한다면 테이블이 될 수 있습니다.
한 주문에 여러 상품을 담으려면
이제 업무 담당자에게 질문을 하나 더 해야 합니다. “한 주문에 상품을 여러 종류 담을 수 있나요?”
우리 shop의 답은 “그렇다” 입니다. 실제로 주문 1번에는 라운드 티셔츠와 프리미엄 그래놀라가 함께 들어 있어요.
이때 orders에 컬럼을 계속 늘리는 방식은 적절하지 않습니다.
product_id_1 · 2 · 3 …상품이 몇 종류까지 들어올지에 따라 구조가 달라진다
수량은?각 상품의 수량을 기록할 자리가 마땅치 않다
4종류가 오면테이블 구조를 또 바꿔야 한다
한 행 = 상품 한 종류몇 종류가 오든 행만 늘리면 된다
quantity수량을 담을 자리가 자연스럽게 생긴다
구조는 그대로데이터만 늘어난다
order_items 의 한 행은 — 한 주문에 포함된 상품 한 종류입니다.
| order_id | product_id | quantity | 읽으면 |
|---|---|---|---|
| 1 | 1 | 1 | 주문 1번에 라운드 티셔츠가 1개 |
| 1 | 16 | 2 | 주문 1번에 프리미엄 그래놀라가 2개 |
같은 주문과 같은 상품의 조합은 한 행에 한 번만 기록하고, 여러 개를 주문했다면 quantity에 수량을 적습니다.
두 대상 사이의 관계에 수량처럼 따로 기록할 사실이 있다면 — 그 관계 자체를 별도 테이블로 기록할 수 있습니다.
네 테이블을 연결해 문장을 복원합니다
이제 연결해 보겠습니다.
| 이 값이 | 이것을 가리킨다 | 그래서 알 수 있는 것 |
|---|---|---|
| orders.member_id | members.member_id | 주문 한 건이 어느 회원의 주문인지 |
| order_items.order_id | orders.order_id | 이 상세가 어느 주문의 것인지 |
| order_items.product_id | products.product_id | 그 자리에 어떤 상품이 담겼는지 |
처음의 짧은 문장, “회원이 상품을 주문한다” 가 이제 이런 구조로 바뀌었습니다.
| 업무의 요소 | 테이블 | 한 행의 의미 |
|---|---|---|
| 회원 | members | 회원 한 명 |
| 주문 사건 | orders | 주문 한 건 |
| 주문 안의 상품과 수량 | order_items | 한 주문에 포함된 상품 한 종류 |
| 상품 | products | 상품 한 종류 |
shop 에는 상품을 분류하는 categories 도 있습니다. 다만 지금은 처음 문장의
중심 흐름을 보기 위해 회원 · 주문 · 주문상세 · 상품 네 테이블에 집중하고
있어요.
거대한 표 하나로 만들면 안 될까요
이 모든 내용을 커다란 표 하나에 넣을 수도 있지 않을까 — 충분히 들 수 있는 생각입니다. 그런데 한 행을 무엇으로 잡든 문제가 생깁니다.
| 한 행을 이렇게 잡으면 | 이런 문제가 생깁니다 |
|---|---|
| 주문상세 한 줄 | 같은 회원 이름 · 주문일 · 상품명이 여러 행에 반복된다 |
| 주문 한 건 | 상품이 늘어날 때마다 상품 컬럼을 계속 추가해야 한다 |
같은 사실을 여러 곳에서 고쳐야 하는 문제도 따라옵니다. 왜 테이블을 나누고 중복을 줄이는지는 뒤의 정규화 강의에서 다시 다룰 거예요.
지금은 이 감각만 잡으면 충분합니다 — 거대한 표 하나보다, 한 행의 의미가 분명한 테이블로 나누고 필요한 관계로 연결한다.
AI와 함께 모델링할 때
AI에게 모델링을 부탁할 때 “쇼핑몰 DB 만들어줘” 한 문장만 주면 충분하지 않습니다. 업무 규칙을 함께 알려줘야 합니다.
| 이런 것을 알려주세요 |
|---|
| 한 주문에 상품을 여러 종류 담을 수 있나요? |
| 주문이 없는 회원도 저장하나요? |
| 주문에는 회원이 반드시 있어야 하나요? |
하지만 AI는 말하지 않은 업무 규칙까지 저절로 알 수 없습니다. 제안받은 뒤에는 사람이 다시 확인해야 해요 — 기록해야 할 대상이 빠지지 않았는지, 각 테이블의 한 행이 무엇인지, 관계가 실제 업무 규칙과 맞는지.
한눈에 정리
| 질문 | 무엇을 정하는가 | shop 에서는 |
|---|---|---|
| 무엇을 기록할 것인가 | 테이블 후보를 고른다 | 회원 · 상품 · 주문 · 주문상세 |
| 한 행은 무엇인가 | 테이블의 의미를 확정한다 | order_items 한 행 = 담긴 상품 한 종류 |
| 어떻게 연결할 것인가 | 외래키로 관계를 만든다 | 자식 FK → 부모 PK 세 개 |
| 명사 → 테이블? | 후보일 뿐, 저장할 기록인지 확인 | 쇼핑몰 화면 · 주문 버튼은 테이블이 아님 |
| 사건 → 테이블? | 구별하고 기억해야 하면 테이블 | 주문한다 → orders |
데이터베이스 모델링은 그림부터 그리는 일이 아닙니다 — 무엇을 기록할지, 한 행은 무엇인지, 테이블을 어떻게 연결할지 결정하는 일입니다.
그렇다면 그 연결은 어느 테이블에 적어야 할까요? 회원 쪽일까요, 주문 쪽일까요? 다음 강의에서 외래키를 어느 테이블에 두어야 하는지 판단하는 법을 다룹니다.