자연어를 활용한 AI 기반 SQL 쿼리 생성기 구축하기
요약
Python을 사용하여 프로덕션 환경에서 신뢰할 수 있는 자연어 기반 SQL 생성기(NL2SQL)를 구축하는 방법을 다룹니다. 스키마 주입 최적화, 쿼리 검증, 보안 위협 방지 등 실무적인 아키텍처 설계 가이드를 제공합니다.
핵심 포인트
- 스키마 정보를 압축하여 토큰 비용과 지연 시간을 절감하는 방법
- SQL 검증기와 읽기 전용 실행기를 통한 안전한 쿼리 실행 구조
- 프롬프트 주입 및 스키마 드리프트 등 보안 및 기술적 문제 해결
- 결과 일관성을 위한 temperature=0 설정의 중요성
SQL을 작성하는 것은 괜찮습니다. 하지만 팀에 40개 이상의 테이블이 있고, 분석가들이 컬럼 이름을 기억하지 못하며, 제품 관리자(Product Manager)들이 매일 오후마다 "그냥 간단한 쿼리 하나만"이라고 요청하기 시작하면 이야기가 달라집니다. 자연어-to-SQL (NL2SQL)은 진정한 생산성 레버(Productivity lever)이지만, 이를 프로덕션 환경에서 제대로 구현한다는 것은 언어 모델(Language Model)에 질문을 던지고 결과가 잘 나오길 바라는 수준을 넘어선다는 것을 의미합니다.
이 글에서는 Python을 사용하여 견고한 NL2SQL 시스템을 구축하는 과정을 살펴봅니다: 스키마 주입 (Schema injection), 안전한 쿼리 생성 (Safe query generation), 출력 검증 (Output validation), 그리고 데모와 프로덕션 사이에서 기다리고 있는 보안 함정들까지 다룹니다.
한눈에 보는 아키텍처 (Architecture)
핵심 흐름은 간단해 보입니다: 사용자의 질문과 데이터베이스 스키마를 언어 모델에 보내고, SQL을 돌려받는 것입니다. 하지만 실제 환경에서는 다음 세 가지 요소가 이를 확실하게 망가뜨립니다:
- 스키마 드리프트 (Schema drift): 모델이 더 이상 존재하지 않는 컬럼을 생성하는 경우
- 방언 불일치 (Dialect mismatch): MySQL 데이터베이스에 Postgres 문법을 사용하는 경우
- 프롬프트를 통한 인젝션 (Injection through prompts): 사용자가 모델로 하여금 파괴적인 쿼리를 생성하도록 강제하는 입력을 만드는 경우
신뢰할 수 있는 아키텍처는 각 계층을 격리합니다:
사용자 질문 → [스키마 주입기 (Schema injector)] → [언어 모델 (Language model)] → [SQL 검증기 (SQL validator)] → [읽기 전용 실행기 (Read-only executor)]
검증기(Validator)는 선택 사항이 아닙니다. 읽기 전용 실행기(Read-only executor) 또한 선택 사항이 아닙니다.
효율적인 스키마 컨텍스트 주입
CREATE TABLE DDL 원문을 프롬프트에 그대로 쏟아붓지 마세요. 이는 장황하고 비용이 많이 듭니다. 대신 압축된 표현을 만드세요:
from dataclasses import dataclass, field
import sqlite3
...
12개의 테이블이 있는 스키마의 경우, 이는 원본 DDL의 2,000개 이상의 토큰 대신 약 300개의 토큰을 생성합니다. 이는 지연 시간(Latency)과 비용 모두에서 중요합니다.
쿼리 생성
시스템 프롬프트(System prompt)가 대부분의 안전 로직을 담당합니다. 모델이 출력해서는 안 되는 것에 대해 명시적으로 작성하세요:
def build_system_prompt(schema_context: str, dialect: str = "sqlite") -> str:
return (
f"You are a read-only SQL query generator for {dialect}.\n...
이 작업에서 temperature=0은 타협할 수 없는 필수 사항입니다. SQL 생성은 창의적인 글쓰기가 아닙니다.
안전하게 검증하고 실행하기
모델의 출력을 절대 직접 실행하지 마세요. 구조적 유효성 검사를 먼저 수행해야 합니다:
import re
import sqlparse
...
Postgres나 MySQL를 사용하는 경우, GRANT SELECT 권한만 가진 전용 읽기 전용(read-only) 역할을 사용하고 애플리케이션 레벨의 제어에만 의존하지 마세요.
보안 관점
NL2SQL은 과소평가되기 쉬운 두 가지 취약점을 드러냅니다.
프롬프트 주입(Prompt injection): 사용자가 `
- 스키마 주입 품질 (Schema injection quality): 조밀하고(compact), 최신 상태를 유지하며, 사용자별로 접근 제어(access-controlled)가 이루어져야 함
- 출력 검증 (Output validation): 키워드 정규 표현식(regex) 뿐만 아니라 AST(Abstract Syntax Tree) 수준의 파싱을 병행해야 하며, 어느 하나만 사용해서는 안 됨
- 실행 격리 (Execution isolation): 드라이버 및 데이터베이스 수준에서의 읽기 전용(read-only) 자격 증명 사용, 엄격한 행 제한(hard row limits) 적용
"데모에서 작동하는 것"과 "프로덕션 환경에서 안전한 것" 사이의 간극은 보기보다 훨씬 넓습니다. 이 세 가지 계층이 그 간극의 대부분을 메워줍니다.
저는 사이버 보안 컨설팅 기업인 AYI NEDJIMI Consultants를 운영하고 있습니다. 저희는 PDF 및 Excel 형식의 무료 보안 강화 체크리스트를 배포합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기