마케팅팀 요청 하나를 받아서,
한 사이클 전체를 라이브로
섹션 5의 마지막 강의 — 그리고 이 섹션 전체를 한 편으로 압축한 종합 시연입니다. 실무에서 실제로 받게 될 법한 요구사항 하나를 들고, 요구사항 → 프롬프트 → SQL → 검증 → 보고서까지 다섯 단계를 처음부터 끝까지 돌려봅니다. 5-1부터 5-4까지 배운 모든 무기가 여기서 한 번에 쓰입니다.
이번 섹션의 여정
완료 ✓
완료 ✓
지금 여기
AI 결과 의심하기
시나리오 — 마케팅팀에서 메시지가 왔습니다
오늘은 새 문법이 없습니다. 대신 AI 협업 SQL 의 한 사이클을 통째로 봅니다. 다섯 단계 — 이 흐름이 머릿속에 그려지면 섹션 5는 완성입니다.
자, 가상 시나리오입니다. 어느 날 사내 메신저로 이런 요청이 도착합니다.
여기서 바로 AI 채팅창을 여는 게 아닙니다. 5-3 안티패턴 ④ — 한 번에 시키지 마라. 잘 읽어보면 이 요청은 질문 두 개가 합쳐진 것이라서, 먼저 쪼갭니다.
| 부분 | 할 일 | 쓸 무기 |
|---|---|---|
| A | 30대 여성이 많이 산 카테고리 TOP 3 | JOIN + GROUP BY (3-6 ~ 3-7) |
| B | 각 카테고리의 인기 상품 1위 | 단계 분해 + CTE (5-4) |
사이클의 첫 단계 — 요구사항 분해 — 는 SQL 한 줄 없이 끝났습니다. 키보드보다 먼저 움직여야 하는 건 늘 이 분해입니다.
부분 A — 프롬프트 한 장에 다 담기
5-1 의 3가지(스키마·샘플·결과 형태) + 5-2 의 텍스트 표 포맷 + 5-3 의 5원칙을 전부 적용해서, 부분 A의 프롬프트를 만듭니다. 그대로 복사해서 따라 해보세요.
[테이블 구조]
- members: member_id PK, member_name, gender (M/F), age, joined_at (DATE)
- orders: order_id PK, member_id FK, ordered_at (DATE)
- order_items: order_id FK, product_id FK, quantity
- products: product_id PK, product_name, price, category_id FK
- categories: category_id PK, category_name
[부모-자식 관계]
- members 1:N orders
- orders 1:N order_items
- products 1:N order_items
- categories 1:N products
[샘플 데이터]
members: (1, 김철수, M, 35, '2025-01-15'), (4, 정수진, F, 31, '2025-03-05')
orders: (4, 4, '2025-03-15')
order_items: (4, 27, 1), (4, 30, 1)
products: (27, '수분 크림', 32000, 5)
categories: (5, '뷰티')
[요구사항]
30대 여성 회원이 가장 많이 산 카테고리 TOP 3.
- "많이 산" = 주문 횟수 (DISTINCT order_id)
- "30대 여성" = gender = 'F' AND age BETWEEN 30 AND 39
- 출력 컬럼: 카테고리명, 주문수
- 정렬: 주문수 많은 순
- 행 수: 3① 컨텍스트(구조·관계·샘플) ✓ ② 구체성 — “많이 산” 을 주문 횟수로 정의 ✓ ③ 결과 형태(컬럼·정렬·행 수) ✓ ④ 한 가지 요청 — 부분 A만 ✓ ⑤ 검증 — 다음 단계에서 직접 ✓
이 프롬프트를 AI에 던지면, 이런 SQL이 돌아옵니다.
SELECT
c.category_name,
COUNT(DISTINCT o.order_id) AS 주문수
FROM
members m
JOIN orders o
ON m.member_id = o.member_id
JOIN order_items oi
ON o.order_id = oi.order_id
JOIN products p
ON oi.product_id = p.product_id
JOIN categories c
ON p.category_id = c.category_id
WHERE
m.gender = 'F'
AND m.age BETWEEN 30 AND 39
GROUP BY
c.category_id,
c.category_name
ORDER BY
주문수 DESC
LIMIT 3;COUNT(*) 가 아니라 COUNT(DISTINCT o.order_id) — 3-7 의 JOIN 뻥튀기
함정을 AI가 알아서 피했습니다. 우리가 관계와
샘플 데이터까지 준 덕분이에요. 좋은 컨텍스트가 좋은 SQL을 만듭니다.
요청의 “지난 1년간” 이 프롬프트와 SQL에 빠져 있죠. 실습 DB(shop)의 주문
데이터가 전부 약 1년치라서 이번 시연에서는 기간 필터를 생략한 겁니다.
수년치 데이터가 쌓여 있는 실무 DB라면 — WHERE o.ordered_at >= ... 같은
기간 조건을 프롬프트와 SQL에 꼭 명시해야 합니다.
검증 — 실행 한 번으로 끝내지 않습니다 ⭐
쿼리를 복사해서 DBeaver에서 실행해보면 —
| category_name | 주문수 |
|---|---|
| 뷰티 | 3 |
| 의류 | 3 |
| 도서 | 2 |
결과는 나왔습니다. 그런데 — 이게 진짜 맞을까요? 5-3의 원칙 ⑤, 검증. 여기서부터가 오늘의 하이라이트입니다. 작은 쿼리 세 개로 검산해봅니다.
검산 ① — 30대 여성 회원이 몇 명이지?
SELECT
*
FROM
members
WHERE
gender = 'F'
AND age BETWEEN 30 AND 39;| member_id | member_name | gender | age |
|---|---|---|---|
| 4 | 정수진 | F | 31 |
| 6 | 강서연 | F | 38 |
| 8 | 임하늘 | F | 33 |
| 16 | 노수아 | F | 30 |
검산 ② — 그 4명이 주문을 몇 번 했지?
SELECT
m.member_name,
COUNT(*) AS 주문수
FROM
members m
JOIN orders o
ON m.member_id = o.member_id
WHERE
m.gender = 'F'
AND m.age BETWEEN 30 AND 39
GROUP BY
m.member_id,
m.member_name;| member_name | 주문수 |
|---|---|
| 정수진 | 3 |
| 강서연 | 3 |
| 임하늘 | 1 |
| 노수아 | 1 |
검산 ③ — 카테고리별 주문수 합이 8과 맞나?
부분 A 결과의 주문수 합은 3 + 3 + 2 = 8. 검산 ②의 총 주문 8건과 정확히 일치합니다. 통과 — 인데, 여기서 한 발 더 의심해봅시다. 이 합이 항상 주문 수와 일치할까요?
아닙니다. 한 주문에 여러 카테고리 상품이 섞여 있으면, 그 주문이 카테고리마다 1번씩 카운트됩니다. 예를 들어 어떤 주문에 수분 크림(뷰티) 과 SQL 입문서(도서) 가 같이 담겨 있으면 — 그 주문 하나가 뷰티에 1번, 도서에 1번, 두 번 세어지는 거예요. 지금은 8건 전부가 한 카테고리 안에서만 산 주문이라 우연히 합이 맞았을 뿐입니다. 그럼 이 SQL은 맞는 걸까요, 틀린 걸까요? — 마케팅 의도에 따라 갈립니다.
이 SQL은 틀린 답이 됩니다
여러 카테고리에 걸친 주문이 카테고리마다 중복 카운트되니까요. 이 의도라면 “주문 전체가 한 카테고리에만 속하는지”를 따지는 다른 SQL이 필요합니다.
✓ 의도가 “그 카테고리에 터치된 주문 수”라면
이 SQL이 정확한 답입니다
COUNT(DISTINCT order_id) 가 카테고리별로 그 카테고리 상품이 담긴 주문을 정확히 세 줍니다. 보통의 마케팅 분석은 이쪽인 경우가 많고요.
판정은 SQL이 못 합니다 — 마케팅팀에 되물어야 할 질문이에요. “한 주문에 여러 카테고리가 섞이면 각각 세도 되나요?” 이 한 문장을 던질 수 있느냐가 차이를 만듭니다.
결과가 나왔다 ≠ 맞다. 받자마자 작은 쿼리로 검산하고, 숫자가 맞아도 의미가 의도와 맞는지까지 의심하는 것 — 이런 의미 검증이 AI 시대의 진짜 SQL 실력입니다. AI가 절대 대신 못 봐주는 영역이거든요.
부분 B — 단계 분해 + CTE 로
부분 A 검증 끝. 이제 부분 B — 각 카테고리에서 가장 인기 있는 상품 1개씩. 이건 한 단계 SQL로 안 풀리는 모양이라, 5-4 의 단계 분해 + CTE 패턴을 꺼냅니다. 프롬프트 마지막 줄에 “WITH 절로 단계별로” 를 명시하는 게 포인트입니다.
[테이블 구조 + 관계 + 샘플] (부분 A 와 동일 — 그대로 재사용)
[요구사항]
부분 A 결과의 TOP 3 카테고리 ('뷰티', '의류', '도서') 에서 —
각 카테고리에서 30대 여성이 가장 많이 산 상품 1개씩.
- "많이 산" = 판매 수량 (SUM(quantity))
- 출력 컬럼: 카테고리명, 상품명, 총수량
- 카테고리당 1행만
복잡하니까 WITH 절로 단계별로 정리해줘.AI가 돌려주는 SQL — 길어 보이지만 겁먹지 마세요.
WITH female_30s AS (
SELECT
member_id
FROM
members
WHERE
gender = 'F'
AND age BETWEEN 30 AND 39
),
sales_per_product AS (
SELECT
c.category_name,
p.product_id,
p.product_name,
SUM(oi.quantity) AS total_qty
FROM
female_30s f
JOIN orders o
ON f.member_id = o.member_id
JOIN order_items oi
ON o.order_id = oi.order_id
JOIN products p
ON oi.product_id = p.product_id
JOIN categories c
ON p.category_id = c.category_id
WHERE
c.category_name IN ('뷰티', '의류', '도서')
GROUP BY
c.category_name,
p.product_id,
p.product_name
),
top_per_cat AS (
SELECT
category_name,
MAX(total_qty) AS max_qty
FROM
sales_per_product
GROUP BY
category_name
)
SELECT
spp.category_name,
spp.product_name,
spp.total_qty
FROM
sales_per_product spp
JOIN top_per_cat tpc
ON spp.category_name = tpc.category_name
AND spp.total_qty = tpc.max_qty;위에서부터 읽으면 5-4에서 배운 사고 그대로입니다.
이것도 그냥 믿지 않습니다 — DBeaver에서 실행하고, 카테고리당 정확히 1행씩 3행이 나오는지, 각 행이 말이 되는지 확인합니다.
| category_name | product_name | total_qty |
|---|---|---|
| 뷰티 | 수분 크림 | 4 |
| 의류 | 라운드 티셔츠 | 3 |
| 도서 | SQL 입문서 | 2 |
보고서까지 — 한 사이클 총 12분
마지막 단계. 이 두 결과를 표 캡처로 던지는 대신, AI에 마지막 한 번을 시킵니다.
방금 만든 두 SQL 의 결과를 — 마케팅팀이 한 줄로 이해할 수 있는
요약 보고서 형태로 정리해줘.여기까지 — 한 사이클 전체에 걸린 시간을 셈해보면:
| 구간 | 한 일 | 시간 |
|---|---|---|
| SQL 만들기 | 요구사항 분해 + 프롬프트 + AI 응답 (A·B) | 약 5분 |
| 검증 | 실행 + 3단 검산 + 의미 점검 | 약 5분 |
| 보고서 | AI 요약 한 번 | 약 2분 |
| 합계 | 요구사항 → 보고서 전달 | 약 12분 |
회의 다녀와서 “개발팀에 의뢰드릴게요” 하고 며칠 기다리던 일을 — 직접 12분에 끝냈습니다. 그것도 검증까지 마친 숫자로요.
섹션 5 총정리 — 다섯 강의가 한 사이클로
| 강의 | 한 줄 핵심 |
|---|---|
| 5-1 AI에게 줘야 할 3가지 | 스키마 · 샘플 데이터 · 원하는 결과 형태 |
| 5-2 ERD 전달 포맷 | 텍스트 표 포맷이 가장 잘 통한다 |
| 5-3 좋은 vs 나쁜 프롬프트 | 5 안티패턴 피하고 5 원칙 지키기 |
| 5-4 단계 분해 + CTE | 복잡한 요구사항은 쪼개서 WITH 로 |
| 5-5 라이브 시연 (지금) ⭐ | 한 사이클 전체 — 요구사항부터 보고서까지 |
모델링을 읽는 눈(섹션 4) + AI 에게 일 시키는 법(섹션 5). AI 시대 데이터 실무 역량의 3 기둥 중 2 기둥이 갖춰졌습니다.
마지막 기둥은 섹션 6 — AI 결과 의심하는 법입니다. 오늘 검산에서 살짝 맛본 “그럴듯한데 틀린 SQL” 을 패턴별로 잡아내는, 이 강의의 화룡점정이에요.
자주 막히는 지점
AI 응답은 매번 조금씩 다릅니다. 이 강의가 SQL 문장이 아니라 검증 절차를 가르치는 이유예요.
COUNT(DISTINCT order_id))가 같은지 보고, 같은 3단 검산으로 결과를 확인하세요.ORDER BY 기준 값이 같은 행들의 순서는 DB가 보장하지 않습니다. 실무 보고서라면 2차 정렬을 꼭 명시하세요.
ORDER BY 주문수 DESC, category_name 처럼 2차 정렬 기준을 추가하면 순서가 고정됩니다.실습하면서 데이터를 수정·삭제했다면 수치가 달라질 수 있습니다. 재적재하면 회원 20 · 주문 40 상태로 돌아옵니다.
init.sql 을 Alt + X 로 다시 실행하세요. 파일 첫머리의 DROP DATABASE 덕분에 몇 번이고 안전합니다.섹션 5가 끝났습니다. 이제 AI에게 일을 시킬 줄 알게 됐으니 — 연습 문제로 오늘의 사이클을 여러분 손으로 한 바퀴 직접 돌려보세요.