AI 시대 DB 모델링 & SQL 완벽 마스터5-5 라이브 시연 ⭐
테크타니 LEARN — 섹션 05 · AI 협업 SQL 작성법

마케팅팀 요청 하나를 받아서,
한 사이클 전체를 라이브로

섹션 5의 마지막 강의 — 그리고 이 섹션 전체를 한 편으로 압축한 종합 시연입니다. 실무에서 실제로 받게 될 법한 요구사항 하나를 들고, 요구사항 → 프롬프트 → SQL → 검증 → 보고서까지 다섯 단계를 처음부터 끝까지 돌려봅니다. 5-1부터 5-4까지 배운 모든 무기가 여기서 한 번에 쓰입니다.

난이도보통소요10분준비물5-1 ~ 5-4 + shop DB핵심한 사이클 종합 시연 ⭐

이번 섹션의 여정

5-1 ~ 5-3 컨텍스트·포맷·프롬프트

완료 ✓

5-4 단계 분해 + CTE

완료 ✓

5-5 라이브 시연 ⭐

지금 여기

섹션 6

AI 결과 의심하기

시나리오 — 마케팅팀에서 메시지가 왔습니다

오늘은 새 문법이 없습니다. 대신 AI 협업 SQL 의 한 사이클을 통째로 봅니다. 다섯 단계 — 이 흐름이 머릿속에 그려지면 섹션 5는 완성입니다.

AI 협업 SQL — 한 사이클
[1] 요구사항 분해
└▶ [2] 프롬프트 구성 (5-1 의 3가지 + 5-2 포맷 + 5-3 원칙)
└▶ [3] AI 에게 SQL 받기
└▶ [4] DBeaver 검증 ← 오늘 가장 천천히 볼 단계 ⭐
└▶ [5] 보고서 전달
섹션 5 에서 배운 전부가 이 다섯 칸 안에 들어 있습니다
시작 전 체크
5-1 ~ 5-4 완료 — 섹션 5 의 무기 장착
shop DB 적재 완료 (2-3 의 init.sql)
AI 도구 아무거나 (ChatGPT·Gemini·Claude)
DBeaver 실행 중

자, 가상 시나리오입니다. 어느 날 사내 메신저로 이런 요청이 도착합니다.

사내 메신저 — 마케팅팀
마케팅팀: 이번 달에 출시할 신상품 마케팅을
어디에 집중할지 결정하려고요. 지난 1년간 —
 
30대 여성 회원이 가장 많이 구매한 카테고리 TOP 3 와,
각 카테고리에서 가장 인기 있는 상품 1개씩 알려주세요.

여기서 바로 AI 채팅창을 여는 게 아닙니다. 5-3 안티패턴 ④한 번에 시키지 마라. 잘 읽어보면 이 요청은 질문 두 개가 합쳐진 것이라서, 먼저 쪼갭니다.

부분할 일쓸 무기
A30대 여성이 많이 산 카테고리 TOP 3JOIN + GROUP BY (3-6 ~ 3-7)
B각 카테고리의 인기 상품 1위단계 분해 + CTE (5-4)
Step 1 이 끝났습니다

사이클의 첫 단계 — 요구사항 분해 — 는 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
5원칙 셀프 체크 — 전부 통과

① 컨텍스트(구조·관계·샘플) ✓ ② 구체성 — “많이 산” 을 주문 횟수로 정의 ✓ ③ 결과 형태(컬럼·정렬·행 수) ✓ ④ 한 가지 요청 — 부분 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;
받자마자 1차 점검 — 눈에 띄는 것 하나

COUNT(*) 가 아니라 COUNT(DISTINCT o.order_id)3-7 의 JOIN 뻥튀기 함정을 AI가 알아서 피했습니다. 우리가 관계와 샘플 데이터까지 준 덕분이에요. 좋은 컨텍스트가 좋은 SQL을 만듭니다.

그런데 — 기간 조건은 어디 갔나요?

요청의 “지난 1년간” 이 프롬프트와 SQL에 빠져 있죠. 실습 DB(shop)의 주문 데이터가 전부 약 1년치라서 이번 시연에서는 기간 필터를 생략한 겁니다. 수년치 데이터가 쌓여 있는 실무 DB라면 — WHERE o.ordered_at >= ... 같은 기간 조건을 프롬프트와 SQL에 꼭 명시해야 합니다.

검증 — 실행 한 번으로 끝내지 않습니다 ⭐

쿼리를 복사해서 DBeaver에서 실행해보면 —

DBeaver — 부분 A 실행 결과
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;
category_name주문수
뷰티3
의류3
도서2
3 rows fetched

결과는 나왔습니다. 그런데 — 이게 진짜 맞을까요? 5-3의 원칙 ⑤, 검증. 여기서부터가 오늘의 하이라이트입니다. 작은 쿼리 세 개로 검산해봅니다.

검산 ① — 30대 여성 회원이 몇 명이지?

SELECT
  *
FROM
  members
WHERE
  gender = 'F'
  AND age BETWEEN 30 AND 39;
DBeaver — 검산 ①
member_idmember_namegenderage
4정수진F31
6강서연F38
8임하늘F33
16노수아F30
4 rows — 대상 회원은 4명

검산 ② — 그 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;
DBeaver — 검산 ②
member_name주문수
정수진3
강서연3
임하늘1
노수아1
합계 8건 — 4명이 총 8번 주문

검산 ③ — 카테고리별 주문수 합이 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에서 배운 사고 그대로입니다.

CTE 사고 구조 — 단계가 그대로 보입니다
female_30s
└▶ ① 30대 여성만 추려두고
sales_per_product
└▶ ② 그들이 산 상품별 총수량을 집계하고
top_per_cat
└▶ ③ 카테고리별 최대 수량을 구한 뒤
최종 SELECT
└▶ ④ 최대 수량과 일치하는 상품만 남긴다

이것도 그냥 믿지 않습니다 — DBeaver에서 실행하고, 카테고리당 정확히 1행씩 3행이 나오는지, 각 행이 말이 되는지 확인합니다.

DBeaver — 부분 B 실행 결과
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;
category_nameproduct_nametotal_qty
뷰티수분 크림4
의류라운드 티셔츠3
도서SQL 입문서2
카테고리당 정확히 1행 — 요구사항 형태 충족
검증 통과 → 마케팅팀 전달 준비 완료

보고서까지 — 한 사이클 총 12분

마지막 단계. 이 두 결과를 표 캡처로 던지는 대신, AI에 마지막 한 번을 시킵니다.

방금 만든 두 SQL 의 결과를 — 마케팅팀이 한 줄로 이해할 수 있는
요약 보고서 형태로 정리해줘.
AI — 요약 보고서 (예시)
■ 30대 여성 구매 분석 (지난 1년)
· 구매 집중 카테고리: 뷰티 · 의류 · 도서 (주문 기준 TOP 3)
· 카테고리별 1위 상품: 수분 크림 / 라운드 티셔츠 / 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 라이브 시연 (지금) ⭐한 사이클 전체 — 요구사항부터 보고서까지
✓ 섹션 5 완주

모델링을 읽는 눈(섹션 4) + AI 에게 일 시키는 법(섹션 5). AI 시대 데이터 실무 역량의 3 기둥 중 2 기둥이 갖춰졌습니다.

예고 — 남은 한 기둥

마지막 기둥은 섹션 6 — AI 결과 의심하는 법입니다. 오늘 검산에서 살짝 맛본 “그럴듯한데 틀린 SQL” 을 패턴별로 잡아내는, 이 강의의 화룡점정이에요.

자주 막히는 지점

① AI가 강의 예시와 다른 SQL을 줬어요

AI 응답은 매번 조금씩 다릅니다. 이 강의가 SQL 문장이 아니라 검증 절차를 가르치는 이유예요.

FIX ▸정상입니다. 모양이 아니라 구조(5테이블 JOIN + COUNT(DISTINCT order_id))가 같은지 보고, 같은 3단 검산으로 결과를 확인하세요.
② 뷰티와 의류 순서가 바뀌어 나와요

ORDER BY 기준 값이 같은 행들의 순서는 DB가 보장하지 않습니다. 실무 보고서라면 2차 정렬을 꼭 명시하세요.

FIX ▸둘 다 3건 동률이라 그렇습니다. ORDER BY 주문수 DESC, category_name 처럼 2차 정렬 기준을 추가하면 순서가 고정됩니다.
③ 검산 수치가 강의와 달라요

실습하면서 데이터를 수정·삭제했다면 수치가 달라질 수 있습니다. 재적재하면 회원 20 · 주문 40 상태로 돌아옵니다.

FIX ▸2-3 의 init.sqlAlt + X 로 다시 실행하세요. 파일 첫머리의 DROP DATABASE 덕분에 몇 번이고 안전합니다.

섹션 5가 끝났습니다. 이제 AI에게 일을 시킬 줄 알게 됐으니 — 연습 문제로 오늘의 사이클을 여러분 손으로 한 바퀴 직접 돌려보세요.

NEXT LESSON5-5 연습 문제

새 요구사항 하나를 받아 분해 → 프롬프트 → 검증까지, 한 사이클을 직접 돌려봅니다.

GO ▸ 연습 문제 풀기