SQL 자산을 활용한 규칙 기반 시멘틱 모델링

SQL 자산을 활용한 규칙 기반 시멘틱 모델링

SQL을 Semantic Layer로 자동 변환하는 SQL to Model의 구조를 소개합니다. SQLGlot 기반 규칙 엔진으로 검증된 SQL을 Cube Semantic Layer로 변환하고, AI Agent가 신뢰할 수 있는 분석 기반을 만드는 방법을 설명합니다.

목차

지난 글에서는 시멘틱 레이어(Semantic Layer)를 기반으로 AI 분석 에이전트를 구축했던 경험을 나누었습니다.

Text-to-SQL을 넘어서: Semantic Layer로 만드는 AI Data Analyst
Semantic Layer와 Text-to-SQL 실험 결과를 바탕으로, AI Data Analyst가 정확한 데이터 조회를 넘어 원인 분석과 다음 액션 제안까지 이어지려면 어떤 구조가 필요한지 설명합니다. HEARTCOUNT 2.0의 Agentic Loop와 비즈니스 맥락 기반 설계를 실제 성능 수치와 함께 정리했습니다.

글로벌 빅테크 기업들에서도 그 가치를 입증하고 있는데요. 엄격하게 관리된 시멘틱 레이어는 DW와 DM 전반에 파편화된 모호한 필드들을 일원화하며, 결과적으로 에이전트가 방황하며 불완전한 쿼리를 만들어낼 여지를 줄여주기 때문입니다.

시멘틱 레이어, 왜 만들기 어려울까?

문제는 시멘틱 레이어를 구축하는 일 자체가 만만치 않다는 데 있습니다. 수많은 테이블을 추상화하는 초기 구축부터, 운영 중에 비즈니스 로직이 바뀌거나 소스 스키마가 달라질 때마다 그 변경을 지속적으로 추적하고 반영해야합니다.

이 문제를 자동화하려는 연구가 없던 것은 아닙니다. LLM을 활용해 원시 테이블과 쿼리 이력을 기반으로 시멘틱 모델를 생성 하거나, dbt 모델에서 비즈니스 질문을 뽑아낸 뒤 이에 답하기 위한 모델을 생성하는 등의 시도입니다. 하지만 정제되지 않은 데이터를 LLM의 추론에 의존하여 처리하다 보니, 오히려 시멘틱 레이어로 제거하려던 모호성을 그대로 포함시킬 뿐입니다.

HEARTCOUNT의 접근 방식은 LLM의 추론에 의존하는 대신, 결정론적 규칙으로 SQL 쿼리 자산을 시멘틱 레이어로 옮기는 것입니다. 기존 연구와 대립하려는 것이 아닙니다. 오히려 초기 모델을 설계하거나 그 유효성을 검증하는 단계에서 상호 보완될 수 있습니다.

sql to model 분석 완료 화면
sql to model 분석 완료 화면

이 글에서는 SQL 자산을 구조화해 규칙 기반으로 분석하여 Cube Semantic layer로 변환하는 방식을 공유합니다.




SQL to Model이 해결하는 세 가지 문제

HEARTCOUNT의 SQL to Model은 사람이 직접 선별한 검증된 쿼리를 붙여넣으면, 이를 규칙 기반으로 분석하고 변환합니다. 이는 "시멘틱 레이어는 엄격하게 관리되는 SSOT(단일 진실 공급원) 논리 모델이어야 한다"는 업계 견해를 따릅니다.

주요 활용 사례는 다음과 같습니다.

  • 초기 구축 공수 절감: 복잡한 테이블 구조를 하나하나 수동으로 타이핑하던 번거로움을 자동 초안 생성으로 덜어줍니다.
  • 휴먼 에러 최소화: 검증된 SQL의 구조를 규칙 기반 엔진이 엄격하게 분석하고 컴파일 테스트까지 거쳐 변환하므로, 오기입이나 구문 오류 같은 휴먼 에러를 차단합니다.
  • 유지보수 및 변경 사항 추적: 소스 데이터 구조가 바뀌거나 비즈니스 로직이 수정되었을 때, 입력된 SQL과 기존 모델 간의 차이를 파악해 업데이트를 제안합니다.


SQL이 Semantic Model이 되기까지

SQL을 문자열 그대로 규칙화하기에는 한계가 있습니다. 여러 테이블을 참조하는 과정에서 Alias가 생략되거나 CTE와 서브쿼리가 복잡하게 중첩되면, 이를 문자열 규칙만으로 일관되게 처리하기 어렵기 때문입니다.

그래서 SQLGlot을 활용해 SQL을 추상 구문 트리(AST)로 파싱한 뒤, 문자열이 아닌 구조 위에서 규칙을 적용합니다.

  1. AST 파싱: 대상 데이터베이스의 Dialect를 반영하여 SQL을 AST 구조로 변환합니다.
  2. 스키마 정보 결합: Cube driver의 tablesSchemaV2()를 통해 물리적인 스키마 정보(테이블, 컬럼, 데이터 타입, PK/FK 관계)를 가져옵니다.
  3. AST 정규화 (sqlglot.qualify()): 앞선 스키마 정보를 바탕으로, 소속이 모호한 각 컬럼에 명확한 소속 테이블을 매핑하고 SELECT * 구문은 실제 컬럼들로 모두 펼쳐줍니다.

이렇게 정규화된 AST는 이후 모든 구조 분석의 기준이 됩니다.

스크린샷 2026-07-07 오후 2.26.13.png

정규화가 완료되면 이 트리 구조를 바탕으로 본격적인 분석과 변환을 시작합니다. 다만, 실무에서 사용하는 쿼리는 형태가 너무나 다양해서 세부 규칙만으로 일일이 분류하기 어렵습니다. 그래서 쿼리의 성격을 크게 두 가지 갈래로 분류합니다.

  • 팩트 테이블을 중심으로 모델 간의 조인과 지표 계산이 깔끔하게 이루어져, 시멘틱 레이어로 표현할 수 있는 쿼리.
  • 구조가 복잡하여 시멘틱 레이어의 기본 규칙만으로는 다루기 어려운 쿼리 (Self-join, Union, Recursive CTE)

이제 가장 일반적인 패턴부터, 복잡한 패턴을 어떻게 처리하는지 살펴보겠습니다.



가장 단순한 경우: 단순 집계와 조인

가장 기본적인 형태는 단순한 집계와 조인으로 구성된 쿼리입니다. 이 단계에서는 컬럼의 형태만으로도 명확하게 분류가 가능합니다. SUM, COUNT, AVG 같은 집계 함수로 감싸진 컬럼은 Measure로, GROUP BY에 포함된 키는 Dimension으로 분류하며, 테이블 간의 조인 관계는 모델 간의 관계로 변환합니다.

스크린샷 2026-07-07 오후 2.58.31.png


더 복잡한 경우: CTE, 서브쿼리와 다중 테이블

물론 실무 쿼리가 이렇게 간단하지만은 않습니다. 지표 계산식이 CTE 또는 서브쿼리에 숨어있거나, 여러 테이블의 지표를 조합해 하나의 지표로 만드는 경우가 훨씬 일반적입니다.

1) CTE와 서브쿼리 처리

CTE와 서브쿼리는 컬럼의 원본 소스를 역추적하여 해결합니다. AST를 따라 내려가 해당 컬럼이 CTE 내부에서 SUM 같은 집계 함수로 계산된 결과였는지 또는 GROUP BY에 포함된 컬럼인지를 파악하여 Measure나 Dimension으로 분류합니다.

스크린샷 2026-07-07 오후 3.28.22.png

2) 다중 테이블 처리

여러 테이블에 걸쳐 정의된 지표를 조합해야 한다면, 각 테이블을 독립된 Cube 모델로 생성한 뒤 모델 간의 Join 관계를 정의하여 해결합니다. 아래의 손해율(loss_ratio) 계산 쿼리를 예로 들 수 있습니다.

SELECT
  a.accident_type_l1,
  SUM(p.payment_amt) AS total_payment_amt,
  SUM(c.insured_amt) AS total_insured_amt,
  100.0 * SUM(p.payment_amt) / NULLIF(SUM(c.insured_amt), 0) AS loss_ratio
FROM oda.fact_accident a
JOIN oda.fact_payment  p ON a.accident_no = p.accident_no
JOIN oda.fact_contract c ON a.accident_no = c.accident_no
GROUP BY a.accident_type_l1
스크린샷 2026-07-07 오후 3.25.04.png

fact_paymentfact_contract 모델에서 각각 필요한 지표를 먼저 생성한 뒤, 중심이 되는 fact_accident 모델에서 이를 최종적으로 조합합니다. 이 변환이 가능한 이유는, 앞선 정규화 단계를 통해 각 집계 함수가 어떤 테이블 소속인지 판별해 두었기 때문입니다.

💡
이처럼 각 팩트 테이블을 독립된 모델로 추상화하는 방식은 시멘틱 레이어의 설계 철학을 따릅니다. Dimensional Modeling으로 구성된 테이블 구조를 그대로 시멘틱 레이어로 변환함으로써, 복잡한 쿼리를 모델 간의 조합으로 표현하고 재사용할 수 있습니다.


이미 집계된 값을 다시 집계해야 한다면: 다단계 집계

전년 대비 비교, 누적, 이중 집계처럼 이미 집계된 값 위에 다시 연산이 필요한 경우도 있습니다. 이런 패턴(TIME_SHIFTRANKROLLING_WINDOWPERCENT_OF_TOTAL 등)은 Cube의 Multi-stage measures로 변환합니다.

이 과정 역시 복잡하게 얽힌 CTE 체인과 서브쿼리의 구조를 분석합니다. 아래 쿼리를 보겠습니다.

WITH monthly_accident AS (
  SELECT r.claim_center_nm,
         DATE_TRUNC('month', a.accident_dt::DATE) AS month,
         COUNT(*) AS cnt
  FROM oda.fact_accident a
  JOIN oda.fact_review r ON a.accident_no = r.accident_no
  GROUP BY r.claim_center_nm, DATE_TRUNC('month', a.accident_dt::DATE)
),
monthly_with_lag AS (
  SELECT claim_center_nm, month, cnt,
         LAG(cnt) OVER (PARTITION BY claim_center_nm ORDER BY month) AS prev_cnt
  FROM monthly_accident
)
SELECT m.claim_center_nm, m.month, m.cnt, m.prev_cnt
FROM monthly_with_lag m
스크린샷 2026-07-07 오후 3.31.51.png

이 쿼리에서 monthly_with_lag CTE 안의 LAG(cnt)를 확인하고, cntmonthly_accident CTE의 COUNT(*)에서 계산된 값임을 추적합니다. 결과적으로 월별 건수와 전월 건수를 정확히 인지하여, 이를 Cube의 time_shift measure로 변환합니다.



규칙으로 표현하기 어려운 쿼리라면: 원본 SQL Embed

물론 모든 쿼리가 시멘틱 레이어의 표준적인 팩트 기반 모델로 깔끔하게 변환할 수 있는 것은 아닙니다. 앞서 살펴본 Self-join, UNION, Recursive CTE처럼 시멘틱 레이어의 기본 규칙만으로는 표현하기 까다로운 쿼리들이 존재합니다. 이 경우, 원본 SQL을 Cube의 sql 필드에 그대로 삽입(Embed)한 뒤, 최종 출력 컬럼만 노출하는 방식을 취합니다.

다만, 원본 SQL이 이미 특정 집계 단위(Grain)로 계산된 결과라면 모델 역시 그 집계 단위에 종속됩니다. 따라서 더 세부적인 수준으로 Drill-down하거나, 다른 모델과 자유롭게 조인하여 새로운 지표를 구성하는 등 시멘틱 레이어가 제공하는 조합성과 재사용성을 충분히 활용하기 어렵습니다.

스크린샷 2026-07-07 오후 5.15.24.png


규칙과 LLM의 역할 경계

이 변환 파이프라인의 높은 신뢰성과 일관성은 구조 분석 및 규칙 기반 변환, 그리고 LLM 기반의 메타데이터 생성의 역할을 철저히 분리한 데서 나옵니다.

담당 하는 일 도구
규칙 기반 (Rule-based)
  • SQL 파싱 및 정규화
  • 복잡한 패턴 감지
  • measure/dimension 분류
  • 조인 관계 추론
SQLGlot (SQL 파서 엔진)
LLM (AI-based)
  • Cube 정의, 멤버 이름, Title, Description 등 메타데이터 생성
  • measure 포맷 제안(percent, currency, number)
  • 변환 로그 기반 요약 작성
gpt-4o-mini

핵심은 Cube 모델의 구조를 전적으로 규칙이 결정한다는 점입니다. LLM은 구조를 해석하거나 변경하지 않고, 규칙이 생성한 모델에 이름과 설명 같은 메타데이터만 추가합니다. 따라서 동일한 SQL을 입력하면 언제나 동일한 Cube 모델이 생성됩니다.

스크린샷 2026-07-07 오후 4.11.38.png


한계 및 향후 과제



원본 SQL Embed 시의 분석 제약

원본 SQL을 통째로 임베드하면 해당 쿼리의 집계 단위(Grain)로 조회가 고정됩니다. 이 경우 데이터의 세부 차원으로 Drill-down 할 수 없으며, 이미 집계된 결과 위에 큐브가 재집계를 수행하면 LAG, RANK 같은 윈도우 함수 연산 결과가 왜곡될 수 있습니다. 또한, 팩트 테이블 기준의 유연성이 사라져 다른 Cube 모델과의 조인 확장성도 제한됩니다.

LLM을 활용한 변환 대상 발굴

현재는 사람이 직접 검증된 SQL을 선별해 변환 대상으로 등록하고 있습니다. 향후에는 다양한 서비스에서 공통으로 사용되는 SQL을 분석해, 활용 빈도와 중요도를 기준으로 LLM이 변환 대상을 추천하도록 확장할 계획입니다. 다만 실제 모델 생성은 기존과 동일하게 규칙 기반 파이프라인이 담당합니다.

CI/CD 기반 자동 동기화

현재는 사람이 직접 SQL을 검토해 시멘틱 모델을 생성합니다. 향후에는 데이터 스키마와 dbt 모델 변경을 감지하여, Pull Request 단계에서 시멘틱 모델을 자동 생성하고 검증하는 CI/CD 파이프라인을 구축할 계획입니다.



마치며

시멘틱 레이어는 대시보드와 AI 분석 에이전트 등 데이터 제품이 공통으로 의존하는 계층입니다. 이 계층에서 발생하는 오류가 상위 모든 분석 결과를 오염시키기 때문에, 이 영역에는 '그럴듯한' 확률적 자동화가 아니라 '결정론적으로 신뢰할 수 있는' 자동화가 필요합니다.

그래서 HEARTCOUNT는 사람이 검증한 SQL을 출발점으로 삼았습니다. 핵심 구조 설계는 SQLGlot 기반의 엄격한 규칙에 맡기고, LLM에게는 이름을 짓고 맥락을 채우는 일만 맡깁니다. 그 결과 동일한 SQL은 언제나 동일한 모델로 변환되며, 사용자는 변환의 전 과정을 투명하게 추적할 수 있습니다.

시멘틱 레이어 도입을 검토하는 실무자 입장에서 이 접근이 갖는 의미는 분명합니다. 어느 조직에나 이미 검증을 마치고 돌아가는 SQL 자산이 있습니다. 대시보드 쿼리, 정기 리포트, 현업이 신뢰하는 추출 쿼리가 모두 여기에 해당합니다. 모델링 전문가를 투입해 몇 주짜리 구축 프로젝트를 꾸리지 않아도, 지금 가진 쿼리 몇 개를 붙여넣는 것만으로 시멘틱 레이어 PoC를 시작할 수 있습니다. 지난 글에서 짚었던 '노동집약적인 구축'이라는 진입 장벽이 그만큼 낮아지는 셈입니다.

💡
데모 신청하기
이번 글에서 소개한 SQL to Model과 규칙 기반 Semantic Layer 구축 방식이 흥미로웠다면, 실제 제품에서는 어떻게 구현되는지 데모를 통해 확인해보세요. Semantic MCP와 Decision Agent의 동작 과정을 직접 보여드립니다.

함께 읽으면 좋은 글

믿어도 되나요 AI의 숫자를
AI가 답한 숫자를 믿으려면, 그 숫자가 어떤 기준으로 만들어졌는지 설명할 수 있어야 합니다. Text-to-SQL을 넘어 기업 데이터의 의미, 권한, 업무 규칙을 반영하는 Semantic MCP와 Decision Agent가 필요한 이유를 살펴봅니다.
[Webinar VOD] 질의응답 챗봇을 넘어, KPI를 움직이는 의사결정 AI Agent - ODA 소개
HEARTCOUNT가 준비하고 있는 ODA(의사결정 Agent) 웨비나를 준비했습니다. 지표의 변화가 어떤 운영 변수와 연결되는지 구조적인 이해를 기반으로, 실행 가능한 의사결정까지 도출하는 ODA의 구조를 자세히 설명하고 데모로 직접 보여드립니다.