Text2SQL 실전 테스트 — LLM이 SQL을 직접 작성하면
"카페 아메리카노 가격이 얼마예요?"라는 질문에 LLM은 4,500원이라고 환각을 만들어냅니다. RAG는 문서에서 근거를 찾지만, 테이블이 13개이고 행이 386개인 복합 도메인에서는 DB 직접 검색이 더 정확합니다. 그래서 자연어를 SQL로 바꾸는 Text2SQL을 도입했습니다. LLM 라우터가 질문 의도를 분류하고, Text2SQL이 PostgreSQL에서 데이터를 가져오고, LLM이 자연어로 응답을 생성합니다. 40문항 실전 테스트 결과, 정확도가 10.8%에서 70.1%로 올라갔습니다.
10.8%→70.1%
팩트 정확도 향상
100%
SQL 문법 유효율
90%
LLM 라우팅 정확도
1,926ms
평균 응답 시간
동의어 사전의 한계
이전 하이브리드 검색에서는 동의어 사전으로 사용자 질문을 DB 스키마에 매핑했습니다. "가격" → price, "메뉴" → menu_items 같은 키워드 매핑입니다. 테이블이 3~4개일 때는 충분했지만, 13개 테이블로 확장하자 문제가 터졌습니다.
동의어 사전이 무너지는 이유
실제로 40개 질문을 동의어 사전 방식으로 테스트하면 정확도 41.2%였습니다. 단순 조회는 처리하지만, JOIN이나 집계가 들어가면 급격히 무너집니다. 테이블이 늘어날수록 동의어 관리가 비현실적이기 때문에, LLM이 직접 스키마를 이해하고 SQL을 작성하는 Text2SQL 방식으로 전환했습니다.
LLM 라우터 + Text2SQL 아키텍처
전체 흐름은 4단계입니다. LLM을 2번 호출하지만, SQL 실행과 RAG 검색은 수 ms 수준이므로 총 응답 시간은 2초 이내에 수렴합니다.
처리 흐름
자연어 질문 → Intent 분류 (DB조회 / 일반대화 / RAG)
테이블 스키마 + 질문 → SQL 쿼리 생성 (LLM)
PostgreSQL에서 쿼리 실행 → 결과 행 반환
SQL 결과 + 원본 질문 → 자연어 응답 (LLM)
핵심은 LLM 라우터입니다. 질문이 DB 조회인지, 일반 대화인지, RAG가 필요한지를 먼저 판단합니다. DB 조회로 분류되면 동시에 SQL을 생성합니다. 라우팅 정확도가 낮으면 아무리 SQL을 잘 만들어도 의미가 없기 때문에, 라우터 성능이 전체 파이프라인의 상한선을 결정합니다.
스키마 프롬프트 설계: 13개 테이블의 DDL(CREATE TABLE 문)을 LLM 프롬프트에 직접 주입합니다. 테이블명, 컬럼명, 데이터 타입, FK 관계까지 포함하면 약 2,000토큰입니다. LLM은 이 스키마를 보고 어떤 테이블을 JOIN할지, 어떤 컬럼을 WHERE에 넣을지 판단합니다. 동의어 사전은 "사람이 매핑을 정의"하지만, Text2SQL은 "LLM이 스키마를 읽고 추론"합니다.
40문항 실전 테스트
테스트 도메인은 종합병원입니다. 환자, 의사, 진료과, 진료기록, 처방, 수납, 병실, 예약 등13개 테이블, 386행의 데이터로 구성했습니다. 질문은 4단계 난이도(Tier)로 설계했습니다.
| Tier | 난이도 | 예시 질문 | 문항 수 | 정확도 |
|---|---|---|---|---|
| Tier 1 | 단순 조회 | 내과 소속 의사 목록은? | 12 | 75% |
| Tier 2 | JOIN | 김환자의 처방 이력을 알려줘 | 10 | 88% |
| Tier 3 | 집계 | 진료과별 평균 진료비는? | 10 | 64% |
| Tier 4 | 복합 | 3월에 가장 많이 처방된 약은? | 8 | 52% |
| 전체 평균 | 40 | 70.1% | ||
흥미로운 점은 Tier 2(JOIN)가 Tier 1(단순 조회)보다 높은 88%를 기록한 것입니다. JOIN 질문은 "김환자의 처방"처럼 테이블 간 관계가 명확해서 LLM이 FK를 정확히 잡습니다. 반면 단순 조회라도 "현재 입원 중인 환자"처럼 상태 필터링이 필요하면,status = 'admitted' 같은 값을 추론해야 해서 오히려 어렵습니다.
SQL 생성 품질 지표
100%
SQL 생성률
40문항 모두 SQL 쿼리 생성 성공
100%
문법 유효율
생성된 SQL 전체가 문법적으로 유효
95%
실행 성공률
38/40 쿼리가 에러 없이 실행 완료
70.1%
정답 정확도
실행 결과가 실제 정답과 일치
SQL 생성률과 문법 유효율이 모두 100%인 것은, 스키마 DDL을 프롬프트에 직접 주입한 효과입니다. LLM이 실제 테이블 구조를 보기 때문에 존재하지 않는 컬럼을 참조하는 오류가 거의 없습니다. 정확도 격차(100% → 70.1%)는 "올바른 SQL이지만 의미적으로 다른 결과를 반환"하는 경우입니다.
3방식 정확도 비교
동일한 40문항을 3가지 방식으로 테스트했습니다. LLM만 사용한 경우와의 차이가 극명합니다.
DB 데이터를 전혀 참조하지 않음. 숫자·이름을 전부 환각으로 생성
키워드 매핑으로 단순 조회는 성공. JOIN·집계에서 급격히 하락
LLM이 스키마를 읽고 SQL을 직접 생성. 복합 쿼리까지 처리
LLM만 사용하면 환자 이름, 진료비, 처방약 등 모든 수치 데이터를 환각으로 생성합니다. 정확도 10.8%는 "예/아니오" 류 질문에서 우연히 맞춘 수준입니다. 동의어 사전은 단순 조회에서 효과적이지만, 테이블이 늘어나면 매핑 관리가 한계에 도달합니다.
왜 70.1%가 의미 있는가: 하이브리드 검색에서 RAG의 팩트 정확도가 92.5%였지만, RAG는 "문서에 있는 정보"만 검색합니다. Text2SQL은 "테이블에 있는 모든 데이터"에 SQL로 접근하므로 커버리지가 다릅니다. 특히 집계(SUM, AVG, MAX)나 조건부 필터링은 RAG로 불가능하고 SQL만 가능한 영역입니다. 실전에서는 RAG + Text2SQL을 함께 사용하는 것이 최선입니다.
레이턴시 분석
전체 응답 시간은 평균 1,926ms입니다. 목표였던 2,500ms를 넉넉히 달성했습니다. 병목은 명확합니다 — LLM 호출입니다.
| 단계 | 소요 시간 | 전체 비중 | 비고 |
|---|---|---|---|
| LLM 라우팅 + SQL 생성 | ~900ms | 46.7% | 1차 LLM 호출 |
| SQL 실행 (PostgreSQL) | ~7ms | 0.4% | 인덱스 활용, 386행 |
| RAG 검색 (병렬) | ~18ms | 0.9% | DB 조회와 동시 실행 |
| 답변 생성 (LLM) | ~1,000ms | 51.9% | 2차 LLM 호출 |
| 합계 | ~1,926ms | 100% | 목표 2,500ms 이내 달성 |
SQL 실행(7ms)과 RAG 검색(18ms)은 합쳐도 25ms입니다. 전체 응답 시간의 98.7%가 LLM 호출입니다. 성능 최적화를 한다면, LLM 추론 속도를 높이는 것이 유일하게 의미 있는 방향입니다. GPU 업그레이드나 AWQ 양자화로 tok/s를 높이면 응답 시간이 비례해서 줄어듭니다.
LLM 라우터 성능
90%
라우팅 정확도
36/40 올바르게 분류
4건
오분류
DB→일반으로 잘못 분류
0건
역방향 오류
일반→DB 오분류 없음
라우팅 오류 4건은 모두 "DB 조회가 필요한 질문을 일반 대화로 분류"한 경우입니다. 역방향 오류(일반 질문을 DB로 보내는 것)는 0건이므로, 불필요한 SQL 실행은 발생하지 않습니다. 라우터가 "확실하지 않으면 일반 대화로 보내는" 보수적인 전략을 학습한 것으로 보입니다.
한계와 개선 방향
70.1%는 동의어 사전(41.2%)보다 크게 개선됐지만, 아직 실서비스에 바로 넣기에는 부족합니다. 오답 30%를 분석하면 패턴이 보입니다.
증상: "최근 3개월간"을 SQL의 DATE_SUB로 변환 실패
개선안: 현재 날짜를 프롬프트에 주입 + 날짜 함수 Few-shot 예시 추가
증상: SQL은 정확하지만 결과가 0행일 때 "데이터가 없다"고 답해야 하는데 환각 생성
개선안: 결과가 0행이면 LLM에 명시적으로 "해당 데이터 없음" 컨텍스트 주입
증상: 15000을 "만오천원"으로 변환 시 단위 오류
개선안: SQL 결과를 숫자 그대로 전달하고, 포맷팅은 후처리 레이어에서 처리
증상: "현재 입원 중"이라는 상태 값이 스키마에 없으면 추론 실패
개선안: 스키마 프롬프트에 주요 enum 값과 비즈니스 규칙을 주석으로 추가
이 개선안들은 모두 프롬프트 엔지니어링 수준에서 해결 가능합니다. 모델을 바꾸거나 아키텍처를 변경할 필요 없이, 스키마 프롬프트에 날짜 함수 예시, enum 값 목록, 0행 처리 규칙을 추가하면 됩니다. 개선 적용 후 80% 이상을 목표로 합니다.
환각 방어 사례
Text2SQL의 가장 큰 장점은 "데이터가 없으면 SQL이 빈 결과를 반환한다"는 것입니다. LLM만 사용하면 존재하지 않는 환자 이름, 가짜 진료비를 만들어내지만, Text2SQL은 DB에 없으면 0행을 반환합니다. 문제는 이 0행을 LLM이 어떻게 해석하느냐입니다.
SQL 결과가 0행일 때 LLM이 환각을 생성하는 케이스는 프롬프트에 명시적 가드레일을 추가해 방어합니다.
결론
동의어 사전 → Text2SQL 전환의 결론은 명확합니다. 테이블이 5개를 넘어가면 동의어 사전은 유지보수가 불가능해지고, LLM이 스키마를 직접 읽는 Text2SQL이 정확도와 확장성 모두에서 우위입니다.
최종 정리
| 항목 | LLM만 | 동의어사전 | Text2SQL |
|---|---|---|---|
| 팩트 정확도 | 10.8% | 41.2% | 70.1% |
| 확장성 (테이블 추가) | 변화 없음 | 동의어 수동 추가 | DDL만 프롬프트에 추가 |
| 집계 쿼리 | 불가 | 매우 제한적 | SUM/AVG/MAX 가능 |
| 유지보수 비용 | 없음 | 높음 | 낮음 |
| 응답 시간 | ~1,000ms | ~1,200ms | ~1,926ms |
응답 시간이 1,926ms로 가장 길지만, LLM 2회 호출이 원인이므로 GPU 성능에 비례해 단축됩니다. 정확도 향상(10.8% → 70.1%)과 유지보수 비용 절감을 고려하면 합리적인 트레이드오프입니다.
AI 챗봇의 데이터 검색 파이프라인은 이제 3계층으로 완성됩니다: RAG는 비정형 문서 검색, 하이브리드 검색은 DB+RAG 병렬 결합, 그리고 Text2SQL은 구조화된 데이터의 직접 쿼리를 담당합니다. LLM 라우터가 질문 의도에 따라 적절한 경로를 선택하면, 각각의 강점을 최대로 활용할 수 있습니다.
다음 단계: RAG + Text2SQL + 하이브리드 검색을 하나의 라우터 아래에 통합하고, 질문 유형에 따라 자동으로 최적 경로를 선택하는 통합 파이프라인을 구축할 예정입니다. 라우팅 정확도 90% → 95% 이상을 목표로, Few-shot 예시와 피드백 루프를 추가합니다.
댓글
(4개)로그인하면 댓글을 작성할 수 있습니다.
13테이블 386행 도메인에서 SQL 생성률 100%, 문법 유효율 100%면 놀랍습니다. 동의어 사전 방식에서 Text2SQL로 넘어가는 명확한 근거가 되겠네요.
LLM 라우터의 라우팅 정확도 90%가 핵심인 것 같습니다. Intent 분류가 틀리면 아무리 SQL을 잘 생성해도 소용없으니까요. 라우터 성능 개선이 다음 과제겠네요.
4-Tier 난이도 분석이 실용적입니다. 단순조회 75%, JOIN 88%, 집계 64%, 복합 52% — JOIN이 단순조회보다 높은 이유가 의외였습니다. JOIN은 테이블 관계가 명확해서 LLM이 잘 잡나 봅니다.
관련 글
© 2026 TreeRU. All rights reserved.
본 콘텐츠의 저작권은 TreeRU에 있으며, 출처를 밝히지 않은 무단 전재 및 재배포를 금합니다. 인용 시 출처(treeru.com)를 반드시 명시해 주세요.