“지난달 매출을 알려줘.”
사람에게는 단순해 보이는 질문이지만 데이터베이스에는 정답이 하나로 존재하지 않을 수 있습니다. 주문금액을 합칠지 결제금액을 합칠지, 취소와 환불을 어느 날짜에 차감할지, 부가세와 배송비를 포함할지, 해외 법인의 환율은 언제 기준으로 적용할지에 따라 숫자가 달라집니다. 같은 회사에서도 영업, 재무, 운영 조직이 서로 다른 매출을 보고 있을 수 있습니다.
대규모 언어 모델이 자연어를 SQL로 바꾸는 능력은 빠르게 좋아졌습니다. 하지만 문법적으로 실행되는 SQL과 회사의 질문에 정확히 답하는 SQL은 다른 문제입니다. 테이블과 컬럼을 잘 찾아도 업무 정의를 모르면 그럴듯하게 틀린 숫자를 만들 수 있습니다. 더 위험한 점은 오류 메시지가 없고 결과가 표와 차트로 깔끔하게 보인다는 것입니다.
기업용 Text-to-SQL의 핵심은 모델에게 스키마 전체를 보여주고 SQL을 자유롭게 쓰게 하는 것이 아닙니다. 회사의 데이터 의미를 시맨틱 레이어에 정의하고, 사용자의 권한 안에서 검증된 쿼리만 제한적으로 실행하며, 결과를 다시 검증하는 구조를 만드는 것입니다.
Text-to-SQL은 번역 문제가 아니라 의미·권한·실행 문제다
자연어 분석 시스템은 최소 세 가지 불확실성을 동시에 해결해야 합니다.
- 언어의 불확실성: “최근 고객”, “활성 사용자”, “매출”, “성장률” 같은 표현의 뜻을 결정해야 합니다.
- 데이터의 불확실성: 어떤 테이블과 컬럼을 사용하고 어떤 키와 조인 방향을 선택할지 알아야 합니다.
- 운영의 불확실성: 누가 어떤 행을 볼 수 있는지, 쿼리가 얼마의 자원을 쓸지, 결과를 외부로 내보내도 되는지 통제해야 합니다.
모델의 프롬프트에 DDL과 예시 몇 개를 넣으면 작은 데모는 만들 수 있습니다. 그러나 데이터웨어하우스에는 비슷한 이름의 테이블, 폐기 예정 컬럼, 여러 단계의 중간 모델, 일대다 조인, 늦게 도착하는 사실 데이터가 존재합니다. 사용자가 질문을 조금만 바꾸면 모델은 다른 경로를 선택하고 숫자가 흔들립니다.
따라서 목표를 “자연어를 SQL 문자열로 생성한다”가 아니라 사용자의 질문을 회사가 합의한 분석 개념으로 변환한다로 바꿔야 합니다. SQL은 그 과정의 마지막 실행 형식일 뿐입니다.
시맨틱 레이어는 회사의 데이터 언어를 코드로 만든다
시맨틱 레이어(Semantic Layer)는 물리적인 테이블과 비즈니스 질문 사이에 놓이는 의미 계층입니다. 여러 BI 도구와 AI 애플리케이션이 같은 지표 정의와 조인 규칙을 재사용하도록 합니다. dbt는 시맨틱 레이어가 기존 모델 위에 지표를 정의하고 조인을 처리해 여러 소비 도구에서 일관된 지표를 사용하게 한다고 설명합니다. LookML 역시 차원, 집계, 계산과 데이터 관계를 모델로 정의하고 이를 이용해 SQL을 생성합니다.
기업용 시맨틱 모델에는 보통 다음 요소가 필요합니다.
| 요소 | 역할 | 예시 |
|---|---|---|
| 엔터티 | 업무 객체와 식별자 정의 | 고객, 주문, 품목, 설비 |
| 차원 | 분석 기준과 계층 정의 | 지역, 제품군, 채널, 고객등급 |
| 팩트 | 집계 전 원자 측정값 정의 | 결제금액, 수량, 가동시간 |
| 지표 | 합의된 계산식과 필터 정의 | 순매출, 활성고객, 납기준수율 |
| 시간 차원 | 날짜 의미와 집계 단위 정의 | 주문일, 결제일, 출고일, 회계월 |
| 관계 | 허용된 조인 경로와 카디널리티 정의 | 고객 1:N 주문, 주문 1:N 품목 |
| 용어·동의어 | 현업 표현을 논리 필드에 연결 | 매출·매상·sales → 순매출 |
| 정책 | 행·열·민감도와 접근 범위 적용 | 영업조직별 고객, 개인정보 마스킹 |
중요한 점은 설명을 많이 적는 것보다 정의를 실행 가능하게 만드는 것입니다. “활성 고객은 최근 거래가 있는 고객”이라고만 쓰면 최근이 며칠인지 모호합니다. “기준일 이전 90일 이내 결제 완료 주문이 한 건 이상이며 테스트 계정이 아닌 고객”처럼 시간, 상태, 제외 조건을 명시해야 합니다.
같은 매출 질문이 달라지는 다섯 가지 지점
1. 기준 이벤트
주문 생성, 결제 승인, 출고, 세금계산서 발행 중 어느 시점을 매출 발생으로 볼지 정해야 합니다. 월 경계에서는 이 선택만으로 결과가 크게 달라집니다.
2. 상태와 취소 처리
결제 대기, 부분 취소, 전액 환불, 반품 완료를 어떤 방식으로 포함할지 결정해야 합니다. 현재 상태만 보면 지난달 말의 상태를 재현하지 못할 수도 있으므로 이력 데이터가 필요합니다.
3. 조인에 의한 중복
주문 한 건을 품목과 프로모션 테이블에 동시에 조인하면 행이 곱해져 금액이 중복 합산될 수 있습니다. 관계의 카디널리티와 집계 수준을 시맨틱 모델이 알고 있어야 합니다.
4. 시간대와 회계 달력
UTC 자정과 한국시간 자정은 다르고, 회사의 회계월이 달력 월과 다를 수 있습니다. 사용자의 시간대와 법인별 회계 달력을 적용하지 않으면 경계 날짜가 잘못 포함됩니다.
5. 통화와 세금
원거래 통화, 결제일 환율, 월평균 환율 중 무엇을 쓰는지, 세금과 배송비를 포함하는지 정의해야 합니다. 이 규칙은 프롬프트의 자연어 설명보다 버전 관리되는 지표 코드에 두는 편이 안전합니다.
시맨틱 레이어의 가치는 AI에게 친절한 설명을 제공하는 데 그치지 않습니다. 사람이 BI 대시보드에서 보던 숫자와 AI가 답하는 숫자가 같은 정의를 공유하게 만드는 데 있습니다. 지표 정의가 바뀌면 모든 소비 채널에 동일하게 반영돼야 합니다.
질문에서 SQL로 바로 가지 말고 구조화된 의도를 거쳐라
운영 시스템에서는 모델이 SQL 전체를 한 번에 생성하게 하기보다 중간 표현을 만드는 것이 유리합니다. 예를 들어 질문을 다음과 같이 구조화할 수 있습니다.
{
"metric": "net_revenue",
"dimensions": ["sales_region"],
"time_range": {"type": "previous_calendar_month"},
"filters": [{"field": "order_status", "operator": "in", "value": ["paid", "shipped"]}],
"order_by": [{"field": "net_revenue", "direction": "desc"}],
"limit": 20
}
이 표현은 허용된 지표와 차원인지 검증하기 쉽고, 사용자에게 해석 내용을 보여주기도 좋습니다. 시맨틱 컴파일러가 이 의도를 검증된 조인과 계산식으로 SQL로 변환하면 모델이 물리 테이블 구조를 임의로 조합하는 범위를 줄일 수 있습니다.
질문이 모호하면 추측하지 말고 되물어야 합니다. “매출은 결제 기준 순매출을 의미하나요, 주문 기준 총액을 의미하나요?”라는 한 번의 확인이 정교한 프롬프트보다 안전합니다. 답을 만들 수 없는 질문을 거절하는 능력도 정확도의 일부입니다.
검증된 질문과 SQL은 운영 지식의 테스트 케이스다
현업이 자주 묻는 질문과 분석가가 승인한 SQL을 쌍으로 관리하면 두 가지 효과가 있습니다. 생성 시 유사한 사례를 근거로 사용할 수 있고, 모델·프롬프트·시맨틱 모델이 바뀔 때 회귀 테스트를 돌릴 수 있습니다. Snowflake의 Verified Query Repository도 자연어 질문과 검증된 SQL을 시맨틱 모델에 저장해 정확성과 신뢰도를 높이는 구조를 제공합니다.
좋은 검증 세트는 단순 질문만 모으지 않습니다.
- 기간 경계를 포함한 질문: 이번 달, 직전 분기, 회계연도
- 여러 엔터티를 조인하는 질문: 고객별 제품군 매출
- 분모가 중요한 지표: 전환율, 불량률, 납기준수율
- 동의어와 사내 약어가 포함된 질문
- 권한에 따라 결과가 달라져야 하는 질문
- 답할 수 없어야 정상인 질문과 공격성 입력
시간 상대 표현만 저장하면 테스트 결과가 날짜에 따라 바뀝니다. 회귀 평가에서는 “지난 분기” 대신 고정된 시작일과 종료일을 사용하고, 운영용 질문과 평가용 기준 질문을 분리하는 것이 좋습니다.
생성된 SQL은 코드다: 실행 전에 여러 문을 통과시켜라
모델이 만든 SQL은 신뢰할 수 없는 코드로 취급해야 합니다. SELECT로 시작하는지 정규식 한 번 확인하는 수준으로는 부족합니다. 주석, 공통 테이블 표현식, 서브쿼리, 저장 함수 등을 이용하면 단순 필터를 우회할 수 있기 때문입니다. SQL 파서로 추상 구문 트리(AST)를 만들고 허용된 문법과 객체만 통과시키는 방식이 필요합니다.
사용자 신원과 권한을 끝까지 전달한다
AI 서비스의 공용 관리자 계정으로 데이터베이스에 접속하면 사용자의 권한 경계가 사라집니다. 가능하면 사용자 또는 조직의 신원 컨텍스트를 데이터 플랫폼까지 전달하고, 행 수준·열 수준 정책을 데이터베이스에서 강제해야 합니다. 모델에게 “다른 조직의 데이터는 보지 마라”고 지시하는 것은 보안 통제가 아닙니다.
읽기 전용 계정과 허용 목록을 사용한다
분석 에이전트에는 INSERT, UPDATE, DELETE, DDL 권한이 필요하지 않습니다. 승인된 뷰와 시맨틱 모델만 조회할 수 있는 별도 역할을 만들고, 원본 개인정보 테이블과 시스템 카탈로그는 기본적으로 차단합니다. OWASP도 LLM 기반 시스템의 과도한 기능·권한·자율성을 위험 요인으로 보고 최소 기능과 최소 권한을 권고합니다.
자원 사용량에 상한을 둔다
안전한 SELECT도 전체 테이블 스캔과 거대한 카티션 조인으로 장애나 비용 폭증을 만들 수 있습니다. 실행 시간, 스캔 바이트, 반환 행 수, 동시 실행 수에 제한을 걸고, 가능하면 실행 계획이나 dry run으로 예상 비용을 먼저 검사합니다. 대화형 질문은 작은 결과를 먼저 반환하고 상세 데이터는 별도 승인 흐름으로 분리합니다.
민감 데이터는 결과 단계에서도 검사한다
허용된 쿼리라도 작은 집단의 집계 결과로 개인을 추론하거나, 자유 텍스트 컬럼에 개인정보가 포함될 수 있습니다. 최소 집단 크기, 마스킹, 다운로드 제한, 응답 후처리와 감사 로그를 함께 적용해야 합니다.
정답 SQL과 정답 결과는 같은 말이 아니다
SQL 문자열이 기준 답안과 다르다고 틀린 것은 아닙니다. 같은 결과를 내는 쿼리는 여러 방식으로 작성할 수 있습니다. 반대로 SQL이 실행돼도 업무 질문에 답하지 못할 수 있습니다. 평가는 여러 층으로 나눠야 합니다.
- 구문 유효성: 대상 데이터베이스에서 파싱되고 실행 가능한가
- 실행 결과 정확성: 고정된 평가 데이터에서 기대 결과와 같은가
- 의미 정확성: 올바른 지표, 시간, 필터와 집계 수준을 선택했는가
- 정책 준수: 권한 밖 테이블·행·열을 요청하거나 노출하지 않았는가
- 효율성: 과도한 스캔, 조인, 중복 계산 없이 제한 안에서 끝나는가
- 설명 가능성: 적용한 정의와 필터, 데이터 기준 시각을 사용자에게 보여주는가
- 거절 품질: 모호하거나 범위 밖인 질문을 안전하게 되묻거나 거절하는가
운영 지표도 별도로 필요합니다. 질문 성공률만 보면 사용자가 잘못된 답을 믿고 떠난 경우를 놓칩니다. 수정 질문률, 분석가에게 전환된 비율, 사용자가 결과를 채택한 비율, 검증된 쿼리 사용률, 쿼리 비용과 지연시간, 정책 차단 건수를 함께 추적해야 합니다.
멀티턴 대화는 편리하지만 의미가 조용히 바뀔 수 있다
“지역별로 보여줘”, “그중 작년보다 줄어든 곳만”, “제품군도 나눠줘” 같은 후속 질문은 분석 경험을 크게 개선합니다. 동시에 현재 지표, 기간, 필터와 정렬 조건을 정확히 이어가야 하는 상태 관리 문제가 생깁니다.
대화 원문만 모델에 계속 넣기보다 현재 분석 상태를 구조화해 저장해야 합니다. 각 차례마다 사용자가 무엇을 추가·삭제·변경했는지 보여주고, “그중”이나 “같은 기준”처럼 모호한 참조는 명시적인 필터로 변환합니다. 중요한 의사결정에 쓰는 결과라면 최종 질문을 독립 실행 가능한 형태로 다시 표시하는 것이 좋습니다.
실무 도입은 읽기 전용 좁은 도메인에서 시작한다
1단계: 질문과 지표를 먼저 수집한다
테이블 목록이 아니라 실제 회의와 보고서에서 반복되는 질문 30~50개를 모읍니다. 질문마다 담당 분석가, 현재 사용하는 대시보드·SQL, 기대 결과와 업무 결정을 연결합니다.
2단계: 도메인 하나의 시맨틱 모델을 만든다
매출, 고객, 생산, 설비 중 하나를 고르고 핵심 지표와 차원, 시간 규칙, 조인 관계를 정의합니다. 모든 데이터를 한 번에 모델링하려 하지 않습니다. 정의 충돌은 기술팀만 결정하지 말고 지표 오너의 승인을 받습니다.
3단계: 검증 질문으로 오프라인 평가한다
정상·경계·권한·거절 사례를 포함한 평가 세트를 만들고 모델이나 프롬프트가 바뀔 때 자동 실행합니다. 결과값, 사용한 지표, 실행 비용과 정책 위반을 비교합니다.
4단계: 분석가 대상 그림자 모드로 운영한다
AI가 만든 해석과 SQL을 분석가가 검토한 뒤 실행하게 합니다. 수정 내역을 수집해 동의어, 지표 설명, 조인 규칙과 검증 쿼리를 보강합니다.
5단계: 일반 사용자에게 제한적으로 개방한다
검증된 도메인, 작은 결과, 읽기 전용 질문부터 제공합니다. 답변에는 지표 정의, 필터, 데이터 기준 시각, 사용한 출처와 SQL 보기 기능을 포함합니다. 민감하거나 비용이 큰 질문은 승인 큐로 보냅니다.
6단계: 피드백을 시맨틱 모델의 변경 절차로 연결한다
자주 실패하는 질문을 단순히 프롬프트에 추가하지 않습니다. 새로운 용어인지, 지표 정의의 빈틈인지, 데이터 품질 문제인지 분류하고 코드 리뷰와 회귀 평가를 거쳐 반영합니다.
피해야 할 설계 패턴
- 전체 스키마 덤프: 수백 개 테이블 DDL을 한 프롬프트에 넣으면 중요한 관계가 묻히고 폐기 컬럼이 선택됩니다.
- SQL 생성과 실행의 동일 권한: 모델 출력이 곧바로 관리자 연결에서 실행되면 한 번의 오류가 사고가 됩니다.
- 정규식 하나로 SELECT 검사: SQL 문법의 다양한 표현과 우회 경로를 안전하게 처리할 수 없습니다.
- 대시보드와 AI의 지표 정의 분리: 사용자가 같은 질문에 두 숫자를 보게 되면 시스템 전체의 신뢰가 무너집니다.
- 정확도만 보고 비용을 무시: 맞는 답을 만들더라도 매 질문마다 대규모 스캔을 하면 운영할 수 없습니다.
- 항상 답하게 만들기: 데이터가 없거나 질문이 모호할 때 자신 있게 추측하는 것보다 확인 질문과 거절이 낫습니다.
기업용 Text-to-SQL의 완성도는 모델 밖에서 결정된다
자연어로 데이터를 묻는 인터페이스는 분석의 진입 장벽을 크게 낮출 수 있습니다. 현업 담당자가 SQL을 기다리지 않고 원인을 탐색하고, 회의 중에 조건을 바꾸며 후속 질문을 할 수 있습니다. 그러나 편리함이 신뢰를 대신하지는 못합니다.
시맨틱 레이어는 회사가 합의한 지표와 관계를 제공하고, 검증된 질문은 정확도의 기준선을 만들며, 실행 가드레일은 데이터와 인프라를 보호합니다. 사용자 피드백과 운영 로그는 다시 모델과 평가 세트를 개선합니다. 이 네 요소가 순환할 때 Text-to-SQL은 데모용 챗봇에서 의사결정 시스템으로 발전합니다.
결국 중요한 질문은 “어떤 LLM이 SQL을 가장 잘 쓰는가?”가 아닙니다. 회사의 언어를 데이터 정의로 연결했는가, 틀린 질문과 위험한 쿼리를 멈출 수 있는가, 그리고 숫자의 근거를 사람이 확인할 수 있는가입니다. 자연어 분석의 신뢰도는 모델의 유창함보다 의미와 실행의 경계를 얼마나 잘 설계했는지에 달려 있습니다.