AI 시대 DB 모델링 & SQL 완벽 마스터6-5 검증 체크리스트 + 디버깅 ⭐
테크타니 LEARN — 섹션 06 · AI 결과 의심하기 ★

AI의 SQL을 의심하는 기술,
체크리스트 한 장으로 완성

섹션 6의 마지막 강의이자, 이 강의 전체 메시지의 화룡점정입니다. 오늘 가져가실 건 딱 두 가지 — AI가 만든 SQL을 받았을 때 머릿속에 돌릴 검증 체크리스트 한 장, 그리고 일부러 틀린 SQL 3개를 함께 뜯어보는 디버깅 실습 한 번입니다.

난이도종합 정리소요10분준비물6-1 ~ 6-4 수강도구DBeaver + AI 과외 선생님

이번 섹션의 여정

6-1 패턴 TOP 5

완료 ✓

6-2 JOIN 뻥튀기 심화 ⭐

완료 ✓

6-3 ~ 6-4

NULL · GROUP BY 완료 ✓

6-5 검증 체크리스트 ★

지금 여기 — 화룡점정

섹션 7 — 마무리

더 깊이 가는 법

6-1에서 6-4까지 — AI가 틀리는 패턴을 하나씩 뜯어봤습니다. 이번 강의는 그걸 언제든 꺼내 쓸 수 있는 한 장으로 압축하고, AI가 만든 SQL 3개를 실제로 디버깅하면서 손에 익힙니다.

시작 전 체크
6-1 ~ 6-4 수강 — 5패턴이 머리에 있는 상태
DBeaver + shop DB 준비 (2-3 완료)
AI 과외 선생님 세션 (2-4 완료)

검증 체크리스트 — 머릿속에 돌릴 한 장

AI가 만든 SQL을 받았을 때, 머릿속으로 돌릴 한 페이지입니다. 섹션 6 전체가 이 한 장에 들어 있어요 — 캡처해서 저장해두세요.

AI SQL 검증 체크리스트 — 캡처해서 저장하세요
① JOIN 뻥튀기
□ JOIN 개수가 "필요한 만큼"만인가?
□ COUNT(*) 검산 — 가장 자식 테이블 행 수와 같은가?
□ SUM·AVG 의 집계 단위가 의도(주문별/회원별)와 맞는가?
□ 의심되면 → COUNT(DISTINCT ...) 또는 CTE 단계 분리
 
② NULL 처리
□ LEFT JOIN 결과에 NULL 이 들어가는 컬럼? → COALESCE
□ COUNT(*) 와 COUNT(컬럼) — 의도에 맞는 쪽인가?
□ WHERE 의 != · NOT IN 에 NULL 가능성? → OR ... IS NULL
 
③ GROUP BY
□ SELECT 의 모든 비집계 컬럼이 GROUP BY 에 있는가?
□ 집계 단위(월별/일별/카테고리별)가 의도와 맞는가?
□ 비중 계산이면 — 분모가 "전체 합"인가?
 
④ 환각
□ SQL 의 모든 컬럼·테이블이 실제 스키마에 존재하는가?
□ 실행 시 컬럼 에러 → 환각 확정 → 스키마 다시 주고 재요청
 
⑤ 비즈니스 의미 — 사람만 가능 ⭐
□ "활성·인기·VIP·신규" 를 우리 회사 정의로 SQL 화했는가?
□ 결과의 숫자 단위가 의도와 맞는가? (원/천원/만원)
□ 시간 범위가 정확한가? (지난 30일? 이번 분기?)
이 체크리스트의 분업 구조

①~④는 SQL적 검증 — AI한테 “이 SQL, 체크리스트로 점검해줘” 하고 시키면 잘 도와줍니다. ⑤는 사람의 일 — ‘활성 회원’이 우리 회사에서 뭘 뜻하는지, AI는 끝까지 모릅니다.

AI 협업 검증 사이클 — 5단계

체크리스트가 무엇을 볼지였다면, 이번엔 어떤 순서로 도는지입니다. 실무에서 AI와 SQL을 주고받을 때의 표준 사이클이에요.

컨텍스트 주고 SQL 요청01 / 05

5-1에서 배운 3가지 컨텍스트에 부모-자식 관계까지 — 주고 시작합니다.

스키마 + 샘플 데이터 + 원하는 결과
DBeaver 에서 실행02 / 05

에러가 나면 그 자체가 단서 — 컬럼 에러면 패턴 ④ 환각 확정입니다.

받은 SQL 그대로 실행
결과 1차 점검03 / 05

행 수가 예상과 맞는지, 합계가 상식적인지 — 체크리스트를 돌립니다.

행 수 · 합계 · 일관성
AI 에게 검산 시키기04 / 05

의심되면 — 검산 SQL을 직접 짜지 말고 AI한테 시키세요. 같은 세션에 후속 메시지 한 줄이면 됩니다.

“이 결과 검산해줘” 라고 후속 메시지
통과 → 사용05 / 05

검증을 통과한 SQL만 씁니다. 통과 전에는 — 보고서에 올리지 않습니다.

미통과면 수정 후 4단계 반복
핵심은 4단계입니다

검산도 AI한테 시킨다 — 본인이 검산 SQL을 짤 필요가 없습니다. 6-2에서 배운 3단계 검산도 “COUNT로 검산해줘” 한 줄이면 AI가 만들어줍니다. 사람의 몫은 두 결과가 일치하는지 보는 것.

함께 디버깅 ① — 카테고리별 매출

여기부터는 함께 디버깅입니다. AI가 만든 SQL 3개 — 각각 어디가 틀렸는지, 방금 그 체크리스트를 돌려서 같이 찾아갑니다. 요구사항은 “카테고리별 매출을 구해줘” 였습니다.

-- ❌ AI가 만든 SQL — 먼저 직접 진단해보세요
SELECT
  c.category_name,
  SUM(p.price) AS 매출
FROM
  categories c
JOIN products p
  ON c.category_id = p.category_id
JOIN order_items oi
  ON p.product_id = oi.product_id
GROUP BY
  c.category_id;
해설 보기 전에 — 1분만

체크리스트 다섯 패턴 중 무엇이 해당되는지 먼저 짚어보세요. 영상이라면 일시정지할 타이밍입니다.

체크리스트 진단 — 디버깅 ①
패턴 ① JOIN 뻥튀기 ✓ JOIN 2개 — 의도한 경로 그대로
검산: 조인 행 수 = 64 — order_items 행 수와 일치
패턴 ③ GROUP BY ⚠ SELECT 의 category_name 이 GROUP BY 에 없음
(MySQL 은 허용하지만 — 명시하는 게 안전)
진짜 함정 → SUM(p.price) — quantity 가 통째로 빠졌다!

한 상품의 매출은 가격 × 수량인데, AI는 수량 없이 가격만 더했습니다. 같은 상품을 3개 사도 1개 판 걸로 계산되는 거예요 — 6-2에서 봤던 패턴의 변형입니다.

-- ✓ 수정본
SELECT
  c.category_name,
  SUM(p.price * oi.quantity) AS 매출
FROM
  categories c
JOIN products p
  ON c.category_id = p.category_id
JOIN order_items oi
  ON p.product_id = oi.product_id
GROUP BY
  c.category_id,
  c.category_name
ORDER BY
  매출 DESC;
DBeaver — 수정본 실행 결과
SELECT c.category_name, SUM(p.price * oi.quantity) AS 매출 …
category_name매출
의류1,107,000
전자기기857,000
뷰티510,000
도서479,000
식품60,000
원본 SUM(p.price) 였다면 식품 매출은 24,000원 — 실제의 절반 이하
5 rows fetched

함께 디버깅 ② — 30대 회원이 산 상품

요구사항은 “30대 회원이 산 상품 목록 뽑아줘” 였습니다.

-- ❌ AI가 만든 SQL — 어디가 틀렸을까요?
SELECT DISTINCT
  p.product_name
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
WHERE
  m.age != 20;
체크리스트 진단 — 디버깅 ②
패턴 ① JOIN 뻥튀기 ✓ DISTINCT 가 있어 중복은 정리됨
패턴 ⑤ 의미 ✗ "30대" 를 age != 20 으로 번역 — 완전 오역
"나이가 20이 아닌" — 40대·50대도 전부 포함된다
패턴 ② NULL ⚠ age 가 NULL 이면 != 비교가 UNKNOWN → 조용히 제외
(shop 은 age NOT NULL 이라 안전 — 실무에선 꼭 점검)

더 황당한 건 — 우리 shop DB에는 나이가 정확히 20인 회원이 한 명도 없습니다. 그래서 이 WHERE아무도 거르지 못하고, 결과 20종은 그냥 “팔린 적 있는 모든 상품”이에요. 쿼리는 에러 없이 돌고 결과도 그럴듯해 보입니다 — 패턴 ⑤가 무서운 이유입니다.

-- ✓ 수정본 — "30대" 를 정확히 SQL 로
SELECT DISTINCT
  p.product_name
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
WHERE
  m.age BETWEEN 30 AND 39;
DBeaver — 수정본 실행 결과
… WHERE m.age BETWEEN 30 AND 39;
product_name
라운드 티셔츠
프리미엄 그래놀라
무선 이어폰
15 rows — age != 20 이었다면 20 rows (사실상 전 상품)
15 rows fetched
예방이 최선 — 5-3 회수

이 함정은 요청 단계에서 막을 수 있었습니다. 처음부터 “30대 = age 30~39” 라고 정의를 명시해서 줬다면 AI가 오역할 여지가 없었어요. 모호한 단어는 던지기 전에 숫자로 바꿔주세요.

함께 디버깅 ③ — 회원별 평균 객단가 TOP 5

마지막은 제일 교묘합니다. 요구사항은 “회원별 평균 객단가(주문당 평균 결제액) TOP 5” 였습니다.

-- ❌ AI가 만든 SQL — 가장 어려운 문제입니다
SELECT
  m.member_name,
  AVG(p.price * oi.quantity) 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
GROUP BY
  m.member_id,
  m.member_name
ORDER BY
  평균_객단가 DESC
LIMIT 5;
체크리스트 진단 — 디버깅 ③
패턴 ③ GROUP BY ✓ member_id, member_name 모두 명시 — 문제없음
패턴 ④ 환각 ✓ 컬럼·테이블 전부 실재
패턴 ① 집계 단위 ✗ AVG(price × quantity) = 주문상세 "행별" 평균
객단가 = 주문별 결제액의 평균 — 집계 단위가 다르다

구체적으로 볼게요. 김철수는 주문 한 번에 책 세 권을 샀습니다 — SQL 입문서 24,000원 + 데이터 모델링 실전 32,000원 + AI 시대 개발자 28,000원, 합쳐서 한 번에 84,000원 결제. 이 주문의 객단가는 84,000원입니다. 그런데 AI의 AVG는 주문상세 행 3개의 평균, 28,000원으로 계산해요 — 3분의 1로 줄었습니다. 6-2의 SUM 뻥튀기와 정확히 같은 뿌리예요.

해법도 6-2에서 배운 그대로 — CTE로 단계를 분리합니다. 1단계에서 주문별 총액을 먼저 만들고, 2단계에서 그 총액들의 평균을 냅니다.

-- ✓ 수정본 — CTE 로 "주문별 총액" 먼저
WITH order_totals AS (
  SELECT
    o.order_id,
    o.member_id,
    SUM(p.price * oi.quantity) AS order_total
  FROM
    orders o
  JOIN order_items oi USING(order_id)
  JOIN products p USING(product_id)
  GROUP BY
    o.order_id,
    o.member_id
)
SELECT
  m.member_name,
  AVG(ot.order_total) AS 평균_객단가
FROM
  members m
JOIN order_totals ot
  ON m.member_id = ot.member_id
GROUP BY
  m.member_id,
  m.member_name
ORDER BY
  평균_객단가 DESC
LIMIT 5;

두 버전의 TOP 5를 나란히 놓으면 — 순위부터 다릅니다.

✕ AI 원본 — 행별 평균

1위 신우진 89,000원

조성민 75,000 · 박민수 71,200 · 김철수 49,800 · 배준호 47,333 — 순위도 금액도 전부 틀렸습니다.

✓ CTE 수정본 — 주문별 평균

1위 조성민 150,000원

박민수 118,667 · 오지은 96,000 · 신우진 89,000 · 김철수 83,000 — 조성민의 객단가가 원본의 딱 2배로 복원됩니다.

일부만 맞아서 더 위험합니다

신우진은 주문 1건에 상품 1개(무선 이어폰 89,000원)라 두 버전의 값이 똑같습니다. 집계 단위 오류는 이렇게 일부 행은 그럴듯하게 맞아 들어가서 — 표본 몇 개만 보고 “맞네” 하고 넘어가기 쉽습니다.

✓ 3개 모두 — 1분이면 진단

quantity 누락, 의미 오역, 집계 단위 오류. 체크리스트가 머리에 있으면 — 셋 다 SQL을 읽는 순간 의심 포인트가 보입니다. 그게 오늘 가져가는 능력입니다.

강의 전체 마무리 — 세 기둥

자 — 섹션 6, 그리고 강의 전체입니다.

강의 시작에서 약속드린 한 줄

“AI 시대에 진짜 배워야 할 건 — 데이터 구조를 읽는 눈과, AI 결과를 의심하는 능력.”

지금까지 세 기둥을 전부 손에 잡으셨습니다.

기둥어디서손에 잡은 것
데이터 구조를 읽는 눈섹션 4 — DB 모델링부모-자식 관계 · ERD 읽기
검증할 도구함섹션 3 — SQL 핵심 문법SELECT 부터 JOIN 까지
정확히 시키고, 의심하는 능력섹션 5 + 섹션 6컨텍스트 3가지 + 5패턴 체크리스트
그리고 — 진심으로

3~4시간 전의 여러분과 지금의 여러분은 다른 사람입니다. AI가 던져준 SQL을 그대로 믿던 사람에서, 체크리스트를 돌리고 검산을 시키는 사람으로요. 그동안 함께 따라와주셔서 감사합니다.

자주 나오는 질문

① 다섯 패턴을 매번 전부 돌려야 하나요?

몇 번 돌려보면 SQL을 읽는 순간 의심 포인트가 먼저 보입니다 — 오늘 디버깅 3개가 그 연습이었어요.

FIX ▸SQL 모양을 보고 해당 패턴만 — JOIN+집계면 , LEFT JOIN 이면 , GROUP BY 면 . 단 ⑤(비즈니스 의미)는 매번.
② AI한테 검산을 시키면, 그 검산은 어떻게 믿나요?

검산 쿼리는 COUNT·SUM 수준으로 단순해서, 본 쿼리와 같은 방식으로 둘 다 틀릴 확률이 낮습니다. 6-2의 3단계 검산이 바로 그 구조예요.

FIX ▸검산의 본질은 다른 경로로 구한 두 값이 일치하는지 보는 것 — 어긋나면 무조건 사람이 개입합니다.
③ 실무 DB에선 정답을 모르는데, 뭘 기준으로 점검하나요?

수업에서는 정답을 알고 검증했지만, 실무의 검증은 “절대 정답”이 아니라 일관성을 봅니다. 행 수·합계·단위가 서로 맞물리는지요.

FIX ▸다른 경로로 구할 수 있는 기준값(전체 행 수, 기존 보고서 수치) + 업무 상식 — 어제까지 100만이던 매출이 갑자기 10배면 SQL부터 의심.

다음 섹션 — 섹션 7, 마무리입니다. 강의 후 어떻게 더 깊이 갈지, 실무 활용 시 주의사항을 다룹니다. 그 전에 마지막 연습 문제로 체크리스트를 손에 완전히 붙여보세요.

NEXT LESSON6-5 연습 문제

틀린 SQL을 직접 진단하고 고치는 마지막 종합 실습 — 체크리스트를 손에 붙입니다.

GO ▸ 연습 문제 풀기