GenAI Mentoring — NL2SQL 프로젝트 보고서

2026년 2월 ~ 5월

1. 프로젝트 배경

본 프로젝트는 멘토링 프로그램의 일환으로 진행되는 기술 검증(PoC)이다. 실무 과제 해결이 목적이 아니라, NL2SQL 기술을 직접 구현하고 그 과정에서 마주치는 한계를 체계적으로 정의·해결하며 관련 지식을 학습하는 것이 핵심 목표이다.

주제 선정 배경: 데이터 분석 수요가 증가하고 있으나 비즈니스 부서는 SQL 이해도 부족으로 직접 쿼리 작성이 어려운 상황이다. 이를 해결하는 자연어 기반 분석 에이전트를 구축해보면서, LLM 기반 Text-to-SQL의 실제 한계(Schema Linking, 환각, 결과 검증 등)를 정량적으로 측정하고, AWS 네이티브 서비스(Bedrock, Redshift, KB)와 Agentic 아키텍처 패턴을 실험적으로 검증한다.

학습 목표:

항목내용
프로그램AWS GenAI Mentoring — 주제: NL2SQL (Text-to-SQL Agent)
기간2026년 2월 ~ 5월
기술 스택AWS Bedrock (Claude Opus 4.7) + Redshift Serverless + EC2 (Node.js) + ALB + Route 53
초기 도메인은행 수신상품 (적금/예금) — products, customers, accounts 3개 테이블
전환 도메인이커머스 (주문/상품/고객/배송/결제/마케팅/집계/운영) — 42개 테이블 / 170K+ rows
핵심 원칙응답시간보다 정확도 우선 · 실험 결과는 정량 수치로 기록

2. Phase 1 — 초기 PoC (은행 도메인)

2.1 구현한 2가지 모드

Direct SQL 모드Bedrock KB 모드
스키마 조회우리가 information_schema.columns로 전체 스키마를 Redshift에서 가져와 프롬프트에 넣음KB가 Glue/Redshift 메타데이터를 내부적으로 보유
SQL 생성Claude가 프롬프트 보고 직접 생성KB의 GenerateQuery API가 생성
정확도 튜닝프롬프트에 컬럼값 힌트 하드코딩Curated Queries + 테이블 Description
에러 처리SQL 실행 실패 시 Claude가 수정 (최대 2회)동일
장점직접 제어 가능, 디버깅 용이프롬프트 관리 불필요, AWS 네이티브
단점스키마/값 매핑을 수동 관리KB 설정에 의존, 복잡 쿼리 한계

2.2 KB 모드를 메인으로 선택한 이유

PoC 계획서의 원칙 "AWS Native 서비스를 최대한 활용하여 커스텀 개발을 최소화"에 따라, 스키마 분석/SQL 생성/메타데이터 관리를 KB에 위임하는 구조를 채택. Direct SQL 모드는 비교 테스트 및 fallback 용도로 병행.

2.3 Phase 1에서 확인한 성과

3. 봉착한 문제

핵심 문제 정의: 테이블이 늘어나면 두 모드 모두 "어떤 테이블을 써야 하는지" 정확히 찾지 못하고, 실행은 됐지만 결과가 틀린 경우도 감지하지 못한다.

3.1 스키마 조회 이슈 (Schema Linking 실패)

모드문제
Direct SQL전체 스키마가 프롬프트에 다 들어가서 LLM이 유사한 테이블 중 엉뚱한 것을 선택. 테이블 20개 이상이면 컨텍스트 윈도우 압박.
Bedrock KBCurated Queries에 등록되지 않은 새로운 유형의 질문에서 KB도 잘못된 테이블을 선택하거나 SQL 생성 자체를 실패.

3.2 결과 검증 부재

현재 에러 수정 루프는 SQL 실행이 실패한 경우만 잡는다. "실행은 됐지만 결과가 틀린 경우" (예: cif_status = 'ACTIVE'로 잘못 매핑하여 0건 반환)는 감지하지 못한다.

3.3 컬럼값 매핑 오류

LLM은 스키마 구조는 인식하지만 실제 저장된 데이터 값을 알지 못하는 구조적 한계가 존재. 동일 질문에도 매번 다른 WHERE 조건이 생성될 수 있음. (NL2SQL 오류 유형 중 컬럼 예측 오류가 32.3%로 가장 높은 비중)

4. 해결 방향

4.1 Schema Linking — RAG 기반 테이블 필터링

현재: 질문 → [전체 스키마 40~50개 테이블] → LLM → SQL (혼란) 개선: 질문 → [RAG 검색으로 관련 3~5개만 필터링] → LLM → SQL (정확)

각 테이블의 메타데이터(이름, 설명, 컬럼 설명, 관련 키워드)를 벡터 임베딩으로 저장해두고, 사용자 질문과 유사도 검색으로 관련 테이블만 뽑아내는 구조.

4.2 Reflection Agent — 결과 검증 루프

현재: SQL 실행 → 성공하면 그대로 반환 (결과가 틀려도 모름) 개선: SQL 실행 → 결과 검증 (0건? 이상값?) → 의심스러우면 SQL 재생성

SQL 실행 결과를 LLM이 한 번 더 검증하는 단계 추가. "이 결과가 질문의 의도에 맞는지", "결과 범위가 합리적인지" 체크.

5. 실험 설계 결정 사항

Phase 1에서 발견한 문제를 해결하기 위해 Phase 2 실험에 앞서 확정한 4가지 결정 사항.

5.1 이커머스 도메인으로 전환

5.2 Bedrock Knowledge Base 모드 제거

Phase 1에서는 Direct SQL 모드와 Bedrock KB 모드 2가지를 병행 운영했으나, Phase 2 실험을 위해 KB 모드를 완전히 제거하기로 결정.

KB 방식 vs Direct SQL + Schema Linking 방식 비교:

항목Bedrock KBDirect SQL + Schema Linking (채택)
SQL 생성 주체KB 내부 (AWS 블랙박스)우리가 직접 Claude에 프롬프트 작성
Schema LinkingKB 내부에서 자동 처리 (과정 불투명)RAG 임베딩 검색으로 직접 구현
튜닝 방법Table Descriptions + Curated Queries (콘솔 수동)프롬프트 엔지니어링 + 임베딩 메타데이터 (코드 관리)
디버깅어떤 단계에서 실패했는지 추적 불가각 단계 로그 가능
기여도 분석"KB가 알아서 해줌" (어디서 개선됐는지 불명)단계별 분리 측정 가능
재현성KB 콘솔 설정에 의존코드와 데이터로 완전 재현 가능
KB 모드 제거 이유
  1. 실험 통제가 어려움 — KB는 내부 동작이 공개되지 않은 블랙박스라 Schema Linking/Reflection Agent의 효과를 분리 측정할 수 없음.
  2. 프로젝트 목적과 불일치 — 본 프로젝트의 핵심은 "Schema Linking과 Reflection Agent가 LLM 기반 NL2SQL 정확도를 얼마나 개선하는가"의 정량 검증. KB는 이 구조를 통제 불가능하게 만듦.
  3. 보고서 설득력 확보 — "이 단계를 이렇게 개선해서 몇 %p 상승"이라는 근거가 필요. KB 방식으로는 이 논리 구조가 성립하지 않음.

Phase 2는 Direct SQL 모드 단일 경로로 단순화. Phase 1의 KB 관련 코드, UI 토글, Curated Queries 설정은 모두 제거.

5.3 LLM 모델 선택 — Claude Opus 4.7

Phase 1에서는 Claude Sonnet 4를 사용했으나, Phase 2 실험의 정밀도 향상과 최신 모델의 개선 효과 확인을 위해 Claude Opus 4.7로 교체.

항목이전 (Sonnet 4)이후 (Opus 4.7)
Model IDapac.anthropic.claude-sonnet-4-20250514-v1:0global.anthropic.claude-opus-4-7
추론 능력일반 작업 적합복잡 추론/코드 생성에 강점
속도/비용빠르고 저렴느리지만 정확도 높음

Schema Linking/Reflection Agent 효과를 측정할 때 모델 변화가 변수를 오염시키면 안 되므로, Baseline도 Opus 4.7로 재측정하여 동일 모델 기준으로 전후 비교.

5.4 검증 방법론

Spider 2.0 벤치마크의 ground-truth 구조를 참고하되, 데이터는 이커머스 도메인에 맞게 직접 설계. 정답 SQL은 사전에 작성하고 Redshift에서 실행 검증.

지표정의
Table Selection AccuracyLLM이 올바른 테이블을 선택한 비율 (기대 테이블 집합이 생성 SQL에 포함되는지)
SQL Validity Rate생성된 SQL이 문법 오류 없이 실행 가능한 비율
Execution Accuracy (EX)생성된 SQL의 실행 결과가 정답 SQL 실행 결과와 일치하는 비율 (핵심 지표)
Retry Recovery Rate1차 SQL 실패 후 자동 수정 루프로 성공한 비율

6. Phase 2 실험 진행 (이커머스 도메인)

6.1 진행 스텝 전체 플로우

Step작업상태상세
1데이터셋 설계완료이커머스 DW 구조 42개 테이블 설계 (8개 도메인). 합성 데이터 170,237건 생성. S3 업로드 + Redshift COPY 적재 완료.
2테스트 세트 작성완료질문 30개 + 정답 SQL + 정답 결과 세트 작성 (EASY 10, MEDIUM 10, HARD 10). 30개 정답 SQL 모두 Redshift에서 정상 실행 확인.
3앱 코드 전환완료server.js에서 banking 스키마 하드코딩 제거, Opus 4.7 채택, KB 로직 삭제, UI 탭 추가(Agent/보고서/데이터셋), ALB + HTTPS 도메인 연결.
4Baseline 측정완료30개 질문을 Claude Opus 4.7 Direct SQL 모드(전체 스키마 311개 컬럼 프롬프트 주입)로 실행. 전체 Execution Accuracy 33.3% (EASY 60%, MEDIUM 20%, HARD 20%). Table Selection 100%, SQL Validity 100%.
5Reflection Agent 구현 + 측정진행중SQL 실행 결과를 LLM이 검증하는 단계 추가. 의심스러우면 SQL 재생성 후 재실행. 같은 30개 질문으로 정확도 비교.
6Schema Linking 구현 + 측정예정테이블 메타데이터를 Bedrock Titan Embedding으로 벡터화 → 유사도 검색으로 관련 테이블 3~5개만 필터링 → LLM에 전달. 같은 30개 질문으로 정확도 비교.
7보고서예정Step 4~6 전후 비교 수치 정리. 각 기법의 기여도 분석. 한계점 및 향후 개선 방향.

6.2 Step 1 상세 — 데이터셋 설계

스키마 구성 (42개 테이블 / 8개 도메인):

도메인테이블 수주요 테이블
고객6customers, customer_addresses, customer_segments, customer_membership, customer_preferences, customer_activity_log
상품8product_categories, product_brands, products, product_variants, product_inventory, product_reviews, product_images, product_tags
주문6cart_items, orders, order_items, order_status_history, order_returns, order_cancellations
결제4payment_methods, payments, refunds, installments
배송4shipping_carriers, shipping_zones, shipments, delivery_tracking
마케팅5campaigns, coupons, coupon_usage, promotions, recommendations
집계 (DW Mart)5daily_sales_summary, monthly_sales_summary, category_sales_summary, customer_ltv_summary, product_performance_summary
운영4warehouses, sellers, seller_payouts, customer_service_tickets

Schema Linking을 의도적으로 어렵게 만드는 장치:

데이터 규모 (총 170,237건):

테이블 그룹건수
customers / customer_*~6,700
products / product_*~10,800
orders / order_items / order_*~38,500
payments / refunds / installments~8,900
shipments / delivery_tracking~26,600
customer_activity_log50,000
recommendations20,000
집계 테이블 5개~6,300
기타 (마케팅/운영)~2,400

인프라 변경 사항:

6.3 Step 2~3 상세 — 테스트 세트 작성 및 앱 전환

테스트 세트 (test_set.json, 30문항):

난이도개수특징
EASY101-2 테이블 단순 조회 / 집계. 예: "전체 고객 수", "DIAMOND 등급 회원 수", "월별 매출 추이"
MEDIUM102-3 테이블 JOIN + 집계. 예: "브랜드별 상품 개수와 평균가", "결제수단별 총 결제액", "VIP 세그먼트 총 구매 금액"
HARD10CTE, 윈도우 함수, 4+ 테이블 JOIN, 서브쿼리. 예: "매출 증감률 TOP 3 카테고리", "카테고리별 재구매율", "창고 지역별 배송 완료율"

각 질문마다 question, expected_tables (정답 테이블 목록), ground_truth_sql (정답 SQL)을 명시. 30개 정답 SQL 모두 Redshift에서 정상 실행 확인.

앱 코드 전환 (server.js + index.html):

6.4 Step 4 상세 — Baseline 측정 결과 (Opus 4.7 기준)

전체 Execution Accuracy: 33.3% (30문항 중 10개 정답 일치)
현재 방식: Claude Opus 4.7 + 42개 테이블 / 311개 컬럼 정보를 프롬프트에 통째로 주입

난이도별 지표:

난이도Table SelectionSQL ValidityExecution Accuracy
EASY (n=10)100.0%100.0%60.0%
MEDIUM (n=10)100.0%100.0%20.0%
HARD (n=10)100.0%100.0%20.0%
ALL (n=30)100.0%100.0%33.3%

Sonnet 4 (Phase 1) vs Opus 4.7 (Phase 2) 비교:

지표Sonnet 4Opus 4.7차이
Table Selection86.7%100.0%+13.3%p
SQL Validity93.3%100.0%+6.7%p
Execution Accuracy26.7%33.3%+6.7%p

실패 유형 분석 (Opus 4.7 기준, 20건):

유형건수의미
테이블 오선택0건Opus 4.7은 42개 테이블 중에서 올바른 테이블을 전부 찾아냄
SQL 실행 실패0건문법/실행 오류 전혀 없음. 모든 SQL이 Redshift에서 정상 실행
결과 불일치 (SQL은 정상)20건모든 실패가 이 유형. 컬럼 이름 차이, ORDER BY 절 누락, 과도한 JOIN, ROUND 처리 차이 등
핵심 발견 — Opus 4.7의 내재된 Schema Linking 능력
Opus 4.7은 42개 테이블 중 올바른 테이블을 100% 정확히 선택함. 이는 모델 자체가 이미 우수한 내장 Schema Linking 능력을 갖추고 있음을 의미함. 그러나 Execution Accuracy는 33.3%에 그침. 이유는 LLM이 생성한 SQL이 "의미적으로는 맞지만 정답 SQL과 컬럼 이름/순서/포맷이 미묘하게 다른" 케이스 때문.

시사점:

Opus 4.7이 자주 보이는 실패 패턴 예시:


GenAI Mentoring — NL2SQL 프로젝트 · AWS Bedrock + Redshift Serverless · 2026