처음 보는 테이블도
다섯 단계면 읽힙니다
섹션 3의 마지막 강의입니다. 오늘은 새로운 문법도, 새로운 실습도 없습니다. 지금까지 배운 것을 하나의 읽는 순서로 꿰는 시간이에요 — 테이블 이름 → 한 행의 의미 → 컬럼과 규칙 → 기본키 → 외래키.
이번 섹션의 여정
완료 ✓
완료 ✓
완료 ✓
지금 여기
다음 여정
오늘은 새 문법을 배우지 않습니다
섹션 3에서 우리는 테이블의 재료를 하나씩 살펴봤습니다. 행과 컬럼, 데이터 타입, NULL과 기본값, 기본키와 외래키, 그리고 구조를 만들고 바꾸는 명령까지요.
재료는 다 모였습니다. 그런데 실무에서 마주치는 건 이미 만들어져 있는 테이블입니다. 회사에 들어가서, 혹은 AI가 만들어준 스키마를 받아서 — 처음 보는 테이블 앞에 앉게 되죠.
이때 컬럼 이름부터 위에서 아래로 훑으면 금방 길을 잃습니다. 컬럼이 스무 개쯤 되면 더 그렇고요. 그래서 필요한 게 읽는 순서입니다.
테이블 이름 → 한 행의 의미 → 컬럼과 규칙 → 기본키 → 외래키. 이 순서로 보면 처음 보는 테이블도 구조가 잡힙니다.
처음 보는 테이블을 읽는 다섯 단계
다섯 단계 모두 이미 배운 내용입니다. 새로 외울 건 하나도 없고 — 순서만 새로 생기는 거예요.
| 단계 | 무엇을 확인하나 | 어디서 배웠나 |
|---|---|---|
| ① 테이블 이름 | 무엇을 저장하는 테이블인가 | 3-1 |
| ② 한 행의 의미 | 한 행의 주인공은 무엇인가 | 3-1 |
| ③ 컬럼과 규칙 | 타입 · NOT NULL · DEFAULT | 3-2 · 3-3 |
| ④ 기본키 | 한 행을 구별하는 값 | 3-4 |
| ⑤ 외래키 | 다른 테이블을 가리키는 값 | 3-5 |
① 테이블 이름 — 가장 먼저 간판을 봅니다. members, orders, order_items처럼 좋은 이름은 안에 무엇이 들어 있는지 한 단어로 알려줍니다. 반대로 tbl1, data2 같은 이름이면 이 단계에서 이미 막혀요. 3-2에서 컬럼 이름을 표지판이라고 했죠 — 테이블 이름은 그 표지판들이 모여 있는 건물의 간판입니다.
② 한 행의 의미 — 다섯 단계 중 가장 중요합니다. 이 테이블의 한 행은 회원 한 명인가, 주문 한 건인가, 주문에 담긴 상품 한 종류인가. 3-1에서 이걸 한 행의 주인공이라고 불렀습니다. 이 답이 정해져야 어떤 컬럼이 이 테이블에 있어야 하는지도 따라옵니다.
③ 컬럼과 규칙 — 이제 컬럼을 봅니다. 이름만 읽지 말고 타입과 규칙까지 함께 읽으세요. 이 값은 계산할 값인가 모양을 지킬 값인가(3-3), 비워도 되는가, 생략하면 들어갈 기본값이 있는가(3-2).
④ 기본키 — 한 행을 정확히 가리키는 값입니다. 중복될 수 없고 NULL일 수도 없죠. 컬럼 하나일 수도 있고, 여러 컬럼이 함께 하나의 기본키를 구성할 수도 있습니다.
⑤ 외래키 — 마지막으로 이 테이블이 다른 테이블을 가리키고 있는지 봅니다. 외래키가 보이면 화살표를 따라가 보세요. 자식의 외래키에서 부모의 기본키로 가는 길입니다.
이제 이 다섯 단계를 실제 shop 테이블 세 개에 그대로 적용해 보겠습니다. 같은 질문을 세 번 반복하는 게 오늘의 훈련이에요.
첫 번째 적용 — members
| member_id | member_name | gender | age | joined_at |
|---|---|---|---|---|
| 1 | 김철수 | M | 35 | 2025-01-15 |
| 2 | 이영희 | F | 28 | 2025-02-03 |
| 3 | 박민수 | M | 42 | 2025-02-10 |
| ⋮ | ||||
| 20 | 백서윤 | F | 21 | 2026-04-02 |
| 단계 | members 의 답 |
|---|---|
| ① 테이블 이름 | members — 회원을 저장하는 테이블 |
| ② 한 행의 의미 | 회원 한 명 |
| ③ 컬럼과 규칙 | 이름 · 성별 · 나이 · 가입일 — 모두 NOT NULL |
| ④ 기본키 | member_id — 컬럼 하나로 구성 |
| ⑤ 외래키 | 없음 — 다른 테이블을 가리키지 않는다 |
컬럼 하나하나도 3-2·3-3의 기준으로 읽힙니다. member_name은 길이가 달라지는 글자라 VARCHAR(50), gender는 언제나 한 글자라 CHAR(1), age는 비교하고 계산할 값이라 INT, joined_at은 기간을 계산할 값이라 DATE. 그리고 넷 다 NOT NULL이라 이름이나 가입일이 빠진 회원은 아예 저장되지 않습니다.
⑤가 없음이라는 것도 중요한 정보입니다. members는 누구도 가리키지 않는 테이블 — 즉 가리킴을 받기만 하는 부모 쪽이라는 뜻이니까요.
두 번째 적용 — orders
| order_id | member_id | ordered_at |
|---|---|---|
| 1 | 1 | 2025-02-01 |
| 2 | 2 | 2025-02-15 |
| 3 | 3 | 2025-03-01 |
| ⋮ | ||
| 6 | 1 | 2025-04-10 |
| ⋮ | ||
| 34 | 1 | 2026-02-15 |
| 단계 | orders 의 답 |
|---|---|
| ① 테이블 이름 | orders — 주문을 저장하는 테이블 |
| ② 한 행의 의미 | 주문 한 건 |
| ③ 컬럼과 규칙 | member_id INT NOT NULL · ordered_at DATE NOT NULL |
| ④ 기본키 | order_id — 컬럼 하나로 구성 |
| ⑤ 외래키 | member_id → members.member_id |
여기서 ②를 조심해야 합니다. orders의 한 행 안에 회원 번호가 들어 있다고 해서 그 행의 주인공이 회원이 되는 건 아닙니다. 주인공은 어디까지나 주문 한 건이고, 회원 번호는 그 주문에 대해 기록한 값 하나예요.
⑤의 화살표를 따라가 보면 이렇습니다. 주문 1번의 member_id는 1 → members에서 1번을 찾으면 김철수. 그래서 주문 1번은 김철수의 주문입니다. 반대로 회원 1번은 주문 1·6·14·20·27·34를 갖고 있죠 — 부모 한 행을 자식 여러 행이 가리킬 수 있습니다. 20번 백서윤 회원처럼 자식이 하나도 없는 부모도 있고요.
orders에는 order_id와 member_id가 나란히 있지만 기본키는 order_id
하나입니다. member_id는 외래키예요. 이름으로 추측하지 말고 실제 설정을
확인하세요.
세 번째 적용 — order_items
| order_id | product_id | quantity |
|---|---|---|
| 1 | 1 | 1 |
| 1 | 16 | 2 |
| 2 | 22 | 1 |
| 3 | 9 | 1 |
| 3 | 11 | 1 |
| ⋮ | ||
| 단계 | order_items 의 답 |
|---|---|
| ① 테이블 이름 | order_items — 주문에 담긴 상품을 저장하는 테이블 |
| ② 한 행의 의미 | 주문에 포함된 상품 한 종류 |
| ③ 컬럼과 규칙 | quantity INT NOT NULL — 그 상품을 몇 개 담았는가 |
| ④ 기본키 | (order_id, product_id) — 두 컬럼이 함께 구성 |
| ⑤ 외래키 | order_id → orders · product_id → products (두 개) |
order_id만 보면 값이 중복됩니다 — 한 주문에 상품이 여러 개 들어 있으니까요. product_id만 봐도 중복이고요 — 같은 상품이 여러 주문에 들어가니까요. 하지만 둘을 한 쌍으로 묶으면 각 행이 구별됩니다. (1, 1), (1, 16), (2, 22) — 겹치는 쌍이 없죠.
membersmember_id 하나로 회원이 구별된다
ordersorder_id 하나로 주문이 구별된다
order_itemsorder_id + product_id 를 묶어야 구별된다
이것도 기본키는 하나구성 컬럼이 두 개일 뿐이다
기본키가 두 개인 게 아닙니다 — 하나의 기본키를 두 컬럼이 함께 만드는 겁니다.
그래서 같은 주문에 같은 상품을 두 줄로 넣을 수 없습니다. 같은 상품을 세 개 샀다면 행을 세 번 만드는 대신 quantity에 3을 저장하는 거예요. 구조 하나가 데이터를 넣는 방식까지 정해주는 셈입니다.
세 테이블을 한 장으로
다섯 단계로 읽은 결과만 모으면 이렇게 됩니다. 컬럼 목록을 다 외우지 않아도 — 이 정도만 손에 쥐면 테이블 사이를 이동할 수 있습니다.
같은 다섯 단계를 categories와 products에도 적용해 보세요. products의 한 행은 상품 하나, 기본키는 product_id, 외래키는 category_id가 categories.category_id를 가리킵니다. 질문은 언제나 똑같습니다.
AI 나 UI 로 만들었다면 — 생성은 도구가, 검토는 사람이
AI에게 원하는 테이블을 설명하거나 DBeaver 같은 화면에서 만들면, 문법을 직접 작성하는 시간은 확실히 줄어듭니다. 3-6에서 말했듯 긴 DDL을 처음부터 손으로 적을 일은 많지 않아요.
하지만 원하는 구조가 정확히 만들어졌는지는 사람이 확인해야 합니다. 확인할 항목은 오늘의 다섯 단계 그대로입니다.
| 확인 항목 | 이렇게 물어보세요 |
|---|---|
| ① 테이블 이름 | 이름만 보고 무엇을 저장하는지 알 수 있는가 |
| ② 한 행의 의미 | 한 행이 정확히 무엇 하나를 뜻하는가 |
| ③ 컬럼 이름과 타입 | 값의 용도에 맞는 타입인가 — 계산할 값인가 모양을 지킬 값인가 |
| ④ 필수값과 기본값 | NOT NULL 과 DEFAULT 가 의도한 대로인가 |
| ⑤ 기본키와 외래키 | 올바른 컬럼에 설정됐는가 — 참조 대상은 맞는가 |
특히 ④와 ⑤는 눈에 잘 띄지 않으면서 나중에 가장 크게 문제가 되는 부분이에요. 필수값이 빠지면 반쪽짜리 행이 쌓이고, 외래키가 없으면 존재하지 않는 부모를 가리키는 데이터가 조용히 들어옵니다.
구조를 바꾸거나 지울 때도 마찬가지입니다. 3-7에서 봤듯 컬럼을 삭제하면 그 안의 값도 함께 사라지고, DROP은 테이블 자체를 없앱니다. AI가 만들어준 명령이라도 바로 실행하지 말고 — 대상과 사라지는 범위를 사람이 다시 확인하세요.
도구가 대신 만들어줄수록 검토하는 눈이 더 중요해집니다. 만들어진 결과가 맞는지 판단하려면 결국 구조를 읽을 줄 알아야 하니까요 — 그래서 오늘의 다섯 단계가 문법보다 오래 쓰입니다.
섹션 3 이 남긴 것
| 강의 | 얻은 것 |
|---|---|
| 3-1 | 테이블 · 행 · 열 · 값 — 한 행의 주인공 |
| 3-2 | 컬럼의 약속 — 이름 · 한 가지 의미 · NULL · NOT NULL · DEFAULT |
| 3-3 | 데이터 타입 — 값에 맞는 그릇 고르기 |
| 3-4 | 기본키 — 중복 불가 · NULL 불가, 복합키 |
| 3-5 | 외래키 — 부모와 자식, 참조 무결성 |
| 3-6 | CREATE TABLE — 이름 · 컬럼 · 타입 · 규칙을 하나의 구조로 |
| 3-7 | ALTER · DELETE · TRUNCATE · DROP — 실행 뒤 무엇이 남는가 |
앞으로 어떤 테이블을 보든 이름 → 한 행의 의미 → 컬럼과 규칙 → 기본키 → 외래키 순서로 확인하세요. 처음 몇 번은 의식적으로 세어가며 하고, 익숙해지면 표 하나를 보는 데 30초도 걸리지 않습니다.
한눈에 정리
| 단계 | 묻는 질문 | shop 에서의 예 |
|---|---|---|
| ① 테이블 이름 | 무엇을 저장하는 테이블인가 | orders — 주문 |
| ② 한 행의 의미 | 한 행의 주인공은 무엇인가 | 주문 한 건 |
| ③ 컬럼과 규칙 | 타입 · 필수값 · 기본값 | ordered_at DATE NOT NULL |
| ④ 기본키 | 한 행을 구별하는 값 | order_id · (order_id, product_id) |
| ⑤ 외래키 | 다른 테이블을 가리키는 값 | orders.member_id → members.member_id |
여기까지가 이미 있는 테이블을 읽는 힘입니다. 그렇다면 아직 테이블이 하나도 없을 때는요? 업무 이야기만 있고 표는 없는 상태에서 — 어떤 테이블을 몇 개 만들고 어떻게 연결할지 정하는 일, 그게 데이터베이스 모델링입니다. 다음 섹션에서 시작합니다.