안녕하세요. 자바파커입니다.
AI에게 데이터베이스 스키마를 넘기고 이렇게 물어봅니다.
지난달 순매출은 얼마야?
orders 테이블에 amt, status, created_at이 있으니 쉬워 보입니다. 하지만 AI는 다음을 모릅니다.
amt가 주문금액인지 실제 승인금액인지- 부가세와 배송비가 포함됐는지
status = 'C'가 완료인지 취소인지- 환불은 주문 금액에서 빼는지 별도 테이블을 보는지
- 주문과 결제를 1:1로 조인해도 되는지
- "지난달"을 주문일과 결제일 중 어느 날짜로 계산하는지
컬럼 타입은 있어도 업무 의미가 없습니다. 이 상태에서 LLM이 만든 SQL은 문법적으로 맞아도 비즈니스적으로 틀릴 수 있습니다.
결론부터 말하면 정형 데이터를 AI에 연결할 때 필요한 것은 행 전체를 문장으로 바꿔 임베딩하는 작업이 아닙니다.
물리 스키마 위에 grain·metric·relationship·business term·governance를 올린 Semantic Layer를 만들고, 질문마다 필요한 조각만 TAG 파이프라인에 공급해야 합니다.
SAG는 그다음입니다. 테이블 계산만으로 답할 수 없는 정책 문서와 사건의 관계를 Event + Entity로 연결할 때 사용합니다.
먼저 구분하기 — Schema, Semantic Layer, TAG, SAG
네 용어를 한 덩어리로 섞으면 구현 방향이 흐려집니다.
| 개념 | 저장하는 것 | 담당하는 일 |
|---|---|---|
| Physical schema | 테이블·컬럼·타입·PK·FK | 데이터가 어떻게 저장됐는가 |
| Semantic Layer | 업무 용어·grain·metric·join·동의어·정책 | 데이터가 무엇을 의미하는가 |
| TAG | 질문 → 쿼리 합성 → DB 실행 → 답변 생성 | 정형 데이터를 정확히 계산하고 설명 |
| SAG | Chunk → Event + Entity → SQL 기반 관계 확장 | 여러 문서의 근거를 동적으로 연결 |
Snowflake의 Semantic View도 logical table, dimension, fact, metric, relationship을 정의해 업무 용어와 물리 테이블 사이를 연결합니다. 설명·동의어·데이터 타입·검증된 SQL 예제가 자연어 질의의 정확도를 높이는 핵심 메타데이터입니다.
TAG(Table-Augmented Generation)는 UC Berkeley 연구진이 제안한 정형 데이터 질의 모델입니다.
Query synthesis: 질문 → 실행 가능한 쿼리
Query execution: 쿼리 → 결과 테이블
Answer generation: 질문 + 결과 테이블 → 최종 답변반면 SAG(SQL-Retrieval Augmented Generation)는 원래 정형 테이블 분석을 가리키는 말이 아닙니다. 문서 청크마다 의미가 완결된 Event와 여러 Entity를 만들고, 질의 시 공유 Entity를 SQL 조인 키로 사용해 로컬 관계를 구성하는 검색 구조입니다.
따라서 이번 글에서는 Semantic Layer + TAG를 본체, SAG를 문서 근거 확장 계층으로 사용합니다.
스키마만으로 틀리는 실제 예
다음 세 테이블이 있다고 해보겠습니다.
orders(order_id, customer_id, amt, status, created_at)
payments(payment_id, order_id, approved_amt, paid_at, status)
refunds(refund_id, payment_id, refund_amt, refunded_at, status)사용자가 "지난달 순매출"을 물었을 때 LLM이 가장 쉽게 만드는 SQL은 이렇습니다.
SELECT SUM(amt)
FROM orders
WHERE created_at >= DATE_TRUNC('month', CURRENT_DATE) - INTERVAL '1 month'
AND created_at < DATE_TRUNC('month', CURRENT_DATE);문법은 맞지만 다음 문제가 있습니다.
- 주문 생성일이 아니라 결제 승인일을 기준으로 해야 합니다.
- 실패·취소된 주문이 포함될 수 있습니다.
- 부분 환불과 전체 환불을 빼지 않았습니다.
- 한 주문에 여러 결제 시도가 있으면 중복 계산할 수 있습니다.
orders.amt가 할인 전 금액일 수 있습니다.
이 문제는 모델을 더 크게 바꿔도 해결되지 않습니다. 조직 안에만 있는 정의가 입력에 없기 때문입니다.
실무에서 필요한 메타데이터 8종
1. Description — 이 값은 무엇인가
approved_amt:
description: 결제사가 최종 승인한 세금 포함 금액
unit: KRW금액, 일자, 코드 같은 설명은 없는 것과 같습니다. 포함·제외 범위까지 적어야 합니다.
2. Grain — 한 행은 무엇을 뜻하는가
grain: 결제 시도 한 건당 한 행grain이 없으면 조인 후 행이 늘어났을 때 중복 집계를 발견하기 어렵습니다.
3. Keys와 Relationships — 어떻게 연결하는가
relationships:
- from: payments.order_id
to: orders.order_id
cardinality: many_to_one
- from: refunds.payment_id
to: payments.payment_id
cardinality: many_to_oneFK가 있다고 항상 분석용 조인이 되는 것은 아닙니다. 유효 기간 조건, 최신 레코드 조건, soft delete 조건이 필요한 조인도 별도로 적어야 합니다.
4. Value semantics — 코드값은 무엇을 뜻하는가
status:
values:
APPROVED: 승인 완료
FAILED: 승인 실패
CANCELED: 승인 후 취소샘플값만 보여주면 LLM이 C를 Complete나 Canceled 중 하나로 추측합니다.
5. Business terms와 Synonyms — 사용자는 어떤 말로 묻는가
business_terms:
- name: 순매출
synonyms: [실매출, net revenue, 확정 매출]테이블은 approved_amt라고 쓰고 현업은 "확정 매출"이라고 부를 수 있습니다. 이 간극을 메타데이터가 메웁니다.
6. Metrics — 계산 공식을 한 곳에 고정하기
metrics:
net_revenue:
description: 승인 결제액에서 완료 환불액을 차감한 금액
expression: SUM(payments.approved_amt) - SUM(refunds.refund_amt)
time_dimension: payments.paid_at
filters:
- payments.status = 'APPROVED'
- refunds.status = 'COMPLETED'
unit: KRW자연어 설명만 저장하면 질의마다 공식이 달라집니다. metric은 실행 가능한 수식과 시간축, 필터를 함께 가져야 합니다.
7. Governance — 누가 보고 얼마나 최신인가
owner: finance-data@company.example
freshness: 15m
classification: internal
pii_columns: [customer_id]
allowed_roles: [finance_analyst]메타데이터 검색이 권한을 우회하면 안 됩니다. 사용자가 접근 가능한 Semantic View만 검색해야 합니다.
8. Verified queries — 맞다고 확인한 질문과 SQL
verified_queries:
- question: 지난달 순매출은 얼마인가?
sql: |
SELECT month, net_revenue
FROM analytics.monthly_sales
WHERE month = DATE_TRUNC('month', CURRENT_DATE) - INTERVAL '1 month'
verified_by: finance-data검증된 질문과 SQL은 단순 few-shot 예제가 아닙니다. 조직이 승인한 계산 방식을 고정하는 회귀 테스트 자산입니다. Snowflake도 Verified Query Repository를 별도로 제공하며, 잘못된 SQL을 넣으면 오히려 정확도를 떨어뜨린다고 명시합니다.
벤더 중립 Semantic Model 예시
처음부터 거대한 온톨로지를 만들 필요는 없습니다. 질문이 자주 들어오는 도메인 하나부터 다음 정도로 시작할 수 있습니다.
version: 1
domain: sales
owner: finance-data
entities:
order:
source: mart.orders
grain: 주문 한 건당 한 행
primary_key: order_id
synonyms: [주문, 구매]
payment:
source: mart.payments
grain: 결제 시도 한 건당 한 행
primary_key: payment_id
dimensions:
status:
values:
APPROVED: 승인 완료
FAILED: 승인 실패
facts:
approved_amount:
expr: approved_amt
unit: KRW
relationships:
- name: payment_to_order
from: payment.order_id
to: order.order_id
cardinality: many_to_one
metrics:
net_revenue:
label: 순매출
synonyms: [실매출, net revenue]
expr: approved_payment_amount - completed_refund_amount
time_dimension: payment.paid_at
verified_queries:
- question: 지난달 순매출
file: queries/net_revenue_last_month.sql중요한 점은 이 YAML 자체가 아닙니다. 같은 정의를 dbt Semantic Layer, Snowflake Semantic View, Cube, LookML, 사내 JSON 모델 등으로 옮겨도 됩니다.
업무 정의가 코드로 버전 관리되고, 질문 실행 시 그 정의를 검색할 수 있는가가 핵심입니다.
메타데이터를 만들어내는 실전 파이프라인
1단계 — 물리 메타데이터 자동 수집
PostgreSQL이라면 information_schema에서 시작할 수 있습니다.
SELECT
table_schema,
table_name,
column_name,
ordinal_position,
data_type,
is_nullable
FROM information_schema.columns
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY table_schema, table_name, ordinal_position;여기에 PK·FK·view definition·column comment·owner를 추가로 수집합니다. 이 단계는 LLM이 아니라 DB 카탈로그가 원본입니다.
2단계 — 안전한 데이터 프로파일링
각 컬럼에 대해 다음 통계를 계산합니다.
- NULL 비율
- distinct 개수와 대략적인 cardinality
- 숫자의 min/max/분위수
- 날짜 범위와 최신 적재 시각
- 상위 빈도값
- 패턴: 이메일·전화번호·UUID 가능성
여기서 원본 고객값을 무조건 LLM에 보내면 안 됩니다. PII는 마스킹하고, sample value 대신 통계와 허용 코드 목록만 전달하는 것이 기본입니다.
3단계 — LLM으로 설명 초안 생성
LLM은 다음 작업에 유용합니다.
- 약어가 많은 테이블·컬럼 설명 초안
- 비슷한 업무 용어와 동의어 후보
- 데이터 프로파일 기반 타입·코드 의미 후보
- 기존 위키·DDL comment·대시보드 설명의 병합
- 누락된 owner·metric·join rule 질문 생성
하지만 metric 공식과 권한을 자동 확정하게 해서는 안 됩니다. 생성은 AI, 승인은 데이터 오너가 맡습니다.
AWS의 structured semantic layer 예제도 테이블마다 schema와 sample data, 참고 문서를 읽어 메타데이터를 생성한 뒤 Glue와 S3에 쓰고, 수정 이력을 남기는 human-in-the-loop 흐름을 사용합니다.
4단계 — Steward 검토와 버전 관리
검토 UI에는 설명만 보여주지 말고 근거도 같이 보여줘야 합니다.
제안: status = C는 CANCELED다
근거: 값 분포 C 3.2%, 취소 대시보드 필터 status='C'
검토: 결제 담당자 승인
버전: semantic-model/sales/v17누가 무엇을 언제 승인했는지 남겨야 잘못된 정의가 배포됐을 때 되돌릴 수 있습니다.
5단계 — 검색 가능한 단위로 발행
전체 카탈로그를 한 문서로 임베딩하지 않습니다.
domain 문서 sales의 범위·owner·주요 질문
entity 문서 order/payment/refund의 grain과 설명
metric 문서 net_revenue 공식·시간축·필터
relationship 허용된 join path와 cardinality
verified query 질문·승인 SQL·적용 조건질문이 들어오면 먼저 domain을 고르고, 관련 entity·metric·relationship만 가져옵니다. 이것이 progressive disclosure입니다.
런타임 — TAG를 안전하게 실행하는 7단계
1. 질문 분류
"지난달 순매출이 전월보다 감소한 이유는?"
→ domain: sales
→ intent: metric comparison + explanation2. Semantic metadata 검색
행 데이터를 찾는 것이 아니라 다음을 먼저 찾습니다.
net_revenuemetric- payment·refund entity
- 승인된 join path
- time dimension
- 비슷한 verified query
3. Query synthesis
LLM은 검색된 메타데이터 안에서 SQL을 만듭니다. 전체 DB 스키마를 프롬프트에 넣지 않습니다.
4. 정적 검증
실행 전 최소한 다음을 확인합니다.
SELECT 전용인가?
허용된 schema와 table만 참조하는가?
승인되지 않은 cross join이 있는가?
metric의 필수 filter가 유지됐는가?
PII 컬럼을 직접 반환하는가?
LIMIT 또는 집계가 있는가?문자열 정규식보다 SQL AST 파서를 사용하는 것이 안전합니다.
from sqlglot import parse_one, exp
tree = parse_one(generated_sql, read="postgres")
if tree.find(exp.Insert) or tree.find(exp.Update) or tree.find(exp.Delete):
raise ValueError("read-only query만 허용합니다")
allowed = {"payments", "refunds", "orders"}
used = {table.name for table in tree.find_all(exp.Table)}
if not used <= allowed:
raise ValueError(f"허용되지 않은 테이블: {used - allowed}")5. 비용 검증과 제한된 실행
EXPLAIN으로 예상 스캔량을 확인하고 timeout·row limit·read-only role을 적용합니다. LLM에 관리자 계정을 주는 방식은 실험 환경에서도 피해야 합니다.
6. Answer generation
DB가 계산한 결과 테이블을 LLM이 설명합니다.
순매출은 91억 원으로 전월보다 8.4% 감소했습니다.
주요 원인은 환불액 14% 증가입니다.
기준: 결제 승인일
공식: 승인 결제액 - 완료 환불액
데이터 최신 시각: 2026-08-31 08:45 KST숫자는 LLM이 계산하지 않고 DB가 계산합니다. LLM은 결과를 비교·요약하고 질문에 맞게 설명합니다.
7. 관찰과 Verified Query 승격
질문, 검색된 메타데이터, 생성 SQL, 검증 결과, 실행 시간, 사용자 수정 내용을 기록합니다. 반복해서 성공한 질문은 담당자가 검토한 뒤 verified query로 승격합니다.
TAG는 Text-to-SQL보다 무엇이 다른가
Text-to-SQL의 목표는 질문과 같은 의미의 SQL을 만드는 것입니다. 하지만 실무 질문은 SQL만으로 끝나지 않습니다.
"매출이 줄어든 지역을 찾고, 고객 후기에서 공통 불만을 요약해줘."
이 질문에는 세 능력이 모두 필요합니다.
- DB가 지역별 매출을 정확히 집계
- 조건에 맞는 후기 행을 대규모로 필터링
- LLM이 후기의 의미를 분류·요약
TAG는 이를 query synthesis → query execution → answer generation으로 명시적으로 분리합니다. 데이터베이스는 정확한 계산과 대규모 처리를, LLM은 의미 판단과 설명을 담당합니다.
그래서 모든 행을 임베딩해서 RAG로 숫자를 세는 방식보다 안전합니다. 벡터 검색의 top-k는 집계 대상 전체를 보장하지 않기 때문입니다.
SAG는 어디에 붙는가
다음 질문은 테이블만으로 답하기 어렵습니다.
순매출이 감소한 고객군과 관련된 최근 정책 변경 및 장애 사건을 함께 찾아줘.
TAG는 감소한 고객군과 수치를 계산할 수 있습니다. 하지만 정책 문서, 장애 보고서, 상담 기록의 연결은 별도의 지식 검색이 필요합니다.
SAG는 문서 청크를 다음처럼 저장합니다.
Chunk 101
Event: 8월 12일 무료배송 기준이 3만원에서 5만원으로 변경됨
Entities: [무료배송, 주문정책, 5만원]
Chunk 204
Event: 8월 중순 소액 주문 전환율이 11% 감소함
Entities: [소액주문, 전환율, 무료배송]질문 시 무료배송 같은 공유 Entity를 SQL 조인 키로 사용해 두 Event를 연결합니다. 전역 지식 그래프를 미리 모두 만들지 않고, 현재 질문에 필요한 로컬 관계만 구성하는 것이 핵심입니다.
TAG: 정확한 숫자와 대상 집합을 계산
SAG: 그 숫자와 연결된 문서 사건·정책 근거를 확장이 둘을 합치면 답변에 계산 근거와 문서 근거를 함께 붙일 수 있습니다.
흔히 실패하는 설계 7가지
1. 컬럼 description만 채운다
설명은 시작일 뿐입니다. grain, metric, join cardinality, time dimension이 없으면 집계 SQL은 계속 틀립니다.
2. 모든 행을 문장으로 바꿔 임베딩한다
검색과 샘플 탐색에는 쓸 수 있지만 정확한 합계·개수·순위 계산의 원본이 되어서는 안 됩니다.
3. 전체 카탈로그를 프롬프트에 넣는다
테이블이 늘수록 이름 충돌과 잘못된 join 후보가 증가합니다. 질문 관련 domain부터 단계적으로 검색해야 합니다.
4. FK만 보고 조인한다
이력 테이블, SCD, soft delete, 다중 결제처럼 분석 조건이 추가되는 조인은 semantic relationship으로 별도 관리합니다.
5. AI가 만든 metric을 자동 배포한다
매출·활성 사용자·이탈률은 조직의 의사결정을 바꿉니다. 담당자 승인과 테스트 없이 자동 확정하면 안 됩니다.
6. SQL 생성 성공률만 본다
실행된 SQL이 업무 정의에 맞는지, join이 정확한지, 답의 숫자가 검증됐는지를 따로 측정해야 합니다.
7. 권한을 검색 이후에만 검사한다
접근할 수 없는 테이블의 이름과 설명도 민감할 수 있습니다. 메타데이터 검색 단계부터 사용자 권한을 적용합니다.
무엇을 측정해야 하나
| 지표 | 확인하는 것 |
|---|---|
| Domain retrieval accuracy | 올바른 업무 도메인을 찾았는가 |
| Schema-link recall | 필요한 테이블·컬럼·metric을 빠뜨리지 않았는가 |
| Join-path accuracy | 올바른 관계와 cardinality를 사용했는가 |
| Metric correctness | 조직의 승인된 공식과 필터를 유지했는가 |
| Execution accuracy | SQL 실행 결과가 정답과 일치하는가 |
| Answer groundedness | 답의 숫자와 설명이 결과 테이블에 근거하는가 |
| Freshness compliance | 요구된 최신성 범위를 만족하는가 |
| Correction rate | 사용자가 SQL이나 metric을 얼마나 수정하는가 |
평가 세트는 문법 문제보다 업무에서 실제로 헷갈린 질문으로 만듭니다.
"매출"과 "순매출"을 구분하는 질문
주문일과 결제일이 다른 질문
부분 환불이 포함된 질문
한 주문에 결제 시도가 여러 개인 질문
권한 없는 PII를 요청하는 질문도입 순서 — 2주짜리 최소 구현
1주차
- 질문이 많은 도메인 하나 선택
- 핵심 entity 5개 이하 선정
- physical schema·comments·owner 자동 수집
- grain·relationship·metric 10개 수동 정의
- verified query 20개 확보
2주차
- metadata retrieval 구현
- SQL AST 검증과 read-only 실행 계정 적용
- 실행 결과 기반 답변 생성
- 질문·SQL·수정 이력 로깅
- 20개 질문으로 execution accuracy 측정
처음부터 전사 데이터 카탈로그를 완성하려 하지 마세요. 답해야 할 질문에서 거꾸로 필요한 의미를 채우는 방식이 훨씬 빠릅니다.
체크리스트
- 테이블마다 한 행의 grain이 적혀 있는가
- metric에 공식·시간축·필터·단위가 있는가
- join path와 cardinality가 명시돼 있는가
- 코드값과 동의어가 업무 언어로 설명돼 있는가
- PII·owner·freshness·권한이 기록돼 있는가
- AI 생성 메타데이터를 담당자가 승인하는가
- 질문마다 관련 메타데이터만 검색하는가
- 생성 SQL을 AST·권한·비용 기준으로 검증하는가
- 숫자는 DB가 계산하고 LLM은 설명만 하는가
- 반복 성공한 질문을 verified query로 관리하는가
- 문서 근거가 필요할 때만 SAG를 확장 계층으로 사용하는가
자주 묻는 질문
기존 Data Catalog와 Semantic Layer는 다른가요?
Data Catalog는 기술 메타데이터·검색·lineage·governance를 폭넓게 관리합니다. Semantic Layer는 그중에서도 분석 질문에 필요한 business entity, dimension, fact, metric, relationship을 실행 가능한 형태로 고정하는 데 초점이 있습니다. 둘을 따로 만들기보다 카탈로그의 기술 메타데이터 위에 semantic model을 연결하는 편이 좋습니다.
정형 데이터도 벡터 DB에 넣어야 하나요?
모든 행을 넣을 필요는 없습니다. 테이블·컬럼·metric·verified query 같은 메타데이터 검색에는 임베딩이 유용합니다. 정확한 합계와 필터링은 원본 DB가 수행해야 합니다. 텍스트 컬럼의 의미 검색이 필요할 때만 해당 컬럼에 벡터 인덱스를 추가합니다.
LLM이 FK를 읽으면 조인 관계를 알 수 있지 않나요?
물리 관계는 알 수 있지만 분석 관계는 다를 수 있습니다. 최신 이력 조건, soft delete, 유효 기간, 결제 성공 상태처럼 FK에 없는 조건이 필요합니다. cardinality와 grain도 별도로 제공해야 중복 집계를 막을 수 있습니다.
SAG를 쓰면 Knowledge Graph가 필요 없나요?
SAG는 전역 그래프 대신 질의 시점에 공유 Entity로 로컬 관계를 만드는 접근입니다. 엄격한 온톨로지 추론, 전사 master data, 장기적인 관계 탐색이 필요하다면 기존 Knowledge Graph가 여전히 적합할 수 있습니다. 두 방식은 대체재라기보다 운영 비용과 추론 요구에 따른 선택입니다.
하이브리드 Markdown 편과는 어떻게 연결되나요?
하이브리드 Markdown 글은 PDF·Office 같은 비정형 문서에서 구조와 출처를 보존하는 방법입니다. 이번 글은 DB 테이블의 업무 의미와 계산 규칙을 보존합니다. 전자는 문서 근거, 후자는 정확한 수치를 담당하며 SAG와 TAG를 통해 한 답변에서 만날 수 있습니다.
마무리
정형 데이터는 이미 구조가 있으니 AI에 연결하기 쉽다고 생각하기 쉽습니다. 하지만 DB의 구조와 비즈니스의 의미는 다릅니다.
AI가 필요한 것은 더 많은 행이 아니라 다음 질문에 답하는 메타데이터입니다.
이 행은 무엇인가?
이 숫자는 어떻게 계산하는가?
어떤 테이블을 어떤 조건으로 연결하는가?
사용자는 이 개념을 어떤 말로 부르는가?
누가 볼 수 있고 얼마나 최신인가?
어떤 질문과 SQL이 검증됐는가?Semantic Layer가 이 의미를 고정하고, TAG가 DB의 계산과 LLM의 설명을 분리합니다. 문서 속 사건과 정책까지 연결해야 할 때 SAG를 붙입니다.
Schema는 데이터의 모양을 말하고, Semantic Layer는 데이터의 뜻을 말합니다.
참고 자료
- Snowflake — Cortex Analyst와 Semantic Views
- Snowflake — Semantic View YAML specification
- Snowflake — Verified Query Repository
- AWS Samples — Semantic Layer for Structured Data
- Databricks — Agent metadata in metric views
- UC Berkeley — Text2SQL is Not Enough: Unifying AI and Databases with TAG
- SAG — SQL-Retrieval Augmented Generation paper
- SAG 공식 문서 — Core data model