비교 후보군

후보가 너무 많았습니다.

메타데이터 카탈로그만 해도 DataHub, Amundsen, Apache Atlas, OpenMetadata. 상용까지 포함하면 Collibra, Alation도 있습니다. NL2SQL 엔진, 문서 지식엔진, 그래프 DB까지 네 축을 채워야 하는데 조합이 기하급수적으로 불어났습니다.

엑셀 시트에 비교표를 만들었습니다. 행이 후보 도구, 열이 평가 기준. 3주쯤 지나니까 탭이 7개로 늘어나 있었습니다. 선택지가 많으면 안 고르는 게 문제입니다. 하나를 고르면 나머지와의 조합이 바뀌고, 다시 처음부터 비교해야 합니다.

네 가지 컴포넌트, 각각의 요건

이전 글 에서 DataNexus의 네 가지 컴포넌트를 정의했습니다. 메타데이터 카탈로그, NL2SQL 엔진, 문서 지식엔진, 그래프 DB.

양보할 수 없는 공통 기준이 세 가지 있었습니다. 오픈소스일 것. 멀티테넌시를 지원하거나 구현 가능할 것 -그룹사별 데이터 격리는 필수입니다. 프로덕션 레디일 것 -커뮤니티 활성도, 릴리즈 주기, 문서화 수준까지 봤습니다.

컴포넌트마다 추가 요건도 달랐습니다. 메타데이터 카탈로그는 Business Glossary에서 용어 간 관계를 정의할 수 있어야 하고, 변경 이벤트를 실시간으로 내보낼 수 있어야 했습니다. NL2SQL 엔진 쪽은 사용자별 컨텍스트 분리와 Row-level Security. 문서 지식엔진은 벡터 검색만으로 안 되고 그래프 검색까지 하이브리드로 돌려야 했습니다. 그래프 DB는 Multi-DB와 Cypher 쿼리 지원이 전제였습니다.

이 기준을 들고 후보를 걸렀습니다.

메타데이터 카탈로그

DataHub, OpenMetadata, Amundsen, Apache Atlas, 상용(Collibra/Alation). 다섯을 놓고 봤습니다.

상용은 먼저 빠졌습니다. 라이선스 비용도 문제지만 이 프로젝트에서 필요한 건 카탈로그의 Glossary를 온톨로지 저장소처럼 쓰는 것입니다. 상용 Glossary가 강력하긴 한데 내부 데이터 모델에 접근해서 커스터마이징하는 데 한계가 있습니다.

Apache Atlas는 Hadoop 생태계에 묶여 있습니다. HBase, Solr, Kafka를 전부 띄워야 합니다. 2016년 설계 그대로인데 클라우드 네이티브 환경에서 돌리기엔 무겁습니다. Amundsen은 검색 중심 카탈로그로는 괜찮은데 Glossary에서 용어 간 관계를 정의하는 기능이 빈약합니다. 온톨로지 저장소로 쓸 수 없었습니다.

끝까지 고민한 건 OpenMetadata입니다. 아키텍처가 깔끔하고 데이터 품질 측정이 내장돼 있어서 단독 카탈로그로는 훌륭했습니다. 문제는 Glossary 관계가 Parent-Child와 RelatedTerms 위주라는 점. 상속(IsA)과 포함(HasA)을 명확히 구분해야 하는 온톨로지 표현에는 모자랐습니다. 실시간 이벤트 동기화도 웹훅 방식이라 대규모 스트리밍에서 Kafka 네이티브 대비 신뢰성이 떨어졌습니다.

DataHub로 갔습니다.

Glossary 관계가 4종입니다. IsA(상속), HasA(포함), Values(값 목록), RelatedTo(일반 연관). 이 네 가지면 비즈니스 용어 간 계층을 표현할 수 있습니다. “순매출 IsA 매출”, “매출 HasA 총매출, 반품, 에누리” 같은 식입니다.

GraphQL API도 한몫했습니다. 메타데이터를 프로그래밍 방식으로 읽고 쓸 수 있어야 NL2SQL 엔진의 RAG Store에 온톨로지를 자동 동기화하는데, GraphQL이면 필요한 필드만 골라서 가져옵니다.

제일 크게 작용한 건 Kafka MCL 이벤트입니다. Metadata Change Log를 Kafka로 내보내는 구조인데, Glossary Term이 바뀌면 이벤트가 발행됩니다. 이걸 구독해서 그래프 DB 온톨로지를 실시간 동기화할 수 있습니다. 메타데이터 변경을 수동으로 반영하는 건 규모가 커질수록 반드시 누락이 생깁니다. 양보할 수 없는 요건이었습니다.

NL2SQL 엔진

처음에는 직접 만들까 생각했습니다. 대화형 BI 솔루션을 구축하면서 NL2SQL에 GPT와 Gemini를 붙이고, 프롬프트 엔지니어링을 최적화하고, 멀티에이전트 아키텍처까지 설계하는 데까지 갔었습니다.

거기서 배운 게 두 가지입니다. DDL만으로는 LLM이 비즈니스 맥락을 이해할 수 없다는 것. 그리고 처음부터 만들면 사용자 인증, 쿼리 로깅, 데이터 필터링, 응답 스트리밍, 쿼리 학습까지 부수 기능이 한없이 불어난다는 것. 견적을 내보니 1개월 넘게 잡아먹힐 판이었습니다.

그때 Vanna가 2.0으로 올라왔습니다.

1.x는 단순했습니다. Python 클래스 하나 상속받아서 train(), ask() 호출하는 방식. 프로토타이핑엔 괜찮은데 프로덕션에 넣기엔 부족했습니다. 사용자별 컨텍스트 분리도 안 되고 보안 기능도 없었습니다.

2.0은 다른 물건입니다. Agent 기반 아키텍처로 바뀌면서 독립적인 구성 요소를 조합하는 방식이 됐고, 모든 컴포넌트에 사용자 ID가 자동 전파되는 User-Aware 구조가 들어갔습니다. Row-level Security가 프레임워크 수준에서 지원됩니다. 성공한 쿼리를 자동 학습하는 Tool Memory도 내장. 테이블이나 차트 같은 Rich UI Component를 실시간 전송하는 Streaming까지 갖췄습니다.

User-Aware와 Row-level Security가 가장 중요했습니다. DataNexus는 그룹사별로 데이터를 격리해야 하는데 NL2SQL 엔진 레벨에서 이걸 지원한다는 건 직접 구현할 코드가 대폭 줄어든다는 뜻입니다.

Tool Memory도 컸습니다. NL2SQL 정확도를 올리는 가장 확실한 방법 중 하나가 성공 쿼리를 축적해서 유사 질문에 재활용하는 건데 이게 프레임워크에 내장돼 있습니다. 별도로 만들면 쿼리 저장, 유사도 매칭, 버전 관리까지 만져야 하는데 그 공수가 통째로 빠집니다.

문서 지식엔진

벡터 검색만으로는 부족합니다.

사업보고서나 내부 정책문서를 검색할 때 벡터 유사도만으로 청크를 가져오면 맥락이 끊깁니다. “A사업부의 매출 인식 기준"을 찾고 싶은데 벡터 검색은 “매출"이 포함된 청크를 유사도 순으로 나열할 뿐입니다. A사업부와 매출 인식 기준의 관계라든가, 기준이 언제 바뀌었는지 같은 그래프 구조 정보는 벡터에 담기지 않습니다.

ApeRAG는 세 가지 검색을 조합해서 이 문제를 풉니다. 임베딩 기반 의미 검색인 Vector Search, 고유명사나 코드명처럼 문자열 자체가 중요한 Full-text Search, 문서에서 추출한 엔티티 간 관계를 그래프로 탐색하는 GraphRAG. 이 셋을 동시에 돌립니다.

이 하이브리드가 DataNexus와 특히 잘 맞는 이유가 있습니다. DataHub의 Glossary Term을 ApeRAG Entity Extraction의 Taxonomy로 주입하면 문서에서 추출된 엔티티가 자동으로 비즈니스 용어와 연결됩니다. Exact Match → Synonym Match → Fuzzy Match(임계값 0.85) → Context Match, 4단계 Entity Resolution을 거칩니다.

MinerU 통합도 있습니다. 엔터프라이즈 문서에는 복잡한 테이블, 수식, 다단 레이아웃이 흔한데 일반 PDF 파서로는 테이블 행/열이 깨집니다. 특히 사업보고서처럼 병합 셀이 많은 문서는 파싱 결과가 처참합니다. MinerU는 문서 구조를 보존하면서 파싱하기 때문에 이 문제를 정면으로 해결합니다.

그래프 DB

가장 큰 변수는 Neo4j 라이선스였습니다.

Community Edition과 Enterprise Edition의 결정적 차이는 Multi-DB입니다. Community는 인스턴스 하나에 그래프 하나. Enterprise는 같은 인스턴스 안에서 여러 데이터베이스를 만들 수 있습니다.

DataNexus에서 Multi-DB는 필수입니다. 그룹사별로 온톨로지 그래프를 격리해야 합니다. groupA_ontology_db, groupB_ontology_db처럼 테넌트별 데이터베이스를 분리하고 사용자 권한으로 접근을 제어하는 구조. Community 단일 DB에 전부 넣고 라벨로 구분하는 건 보안상 말이 안 됩니다.

그렇다고 Neo4j Enterprise 라이선스를 살 수는 없습니다. 오픈소스 프로젝트의 원칙에 어긋납니다.

DozerDB가 이 딜레마를 풀었습니다. Neo4j Community Edition 위에 Enterprise 기능을 얹는 오픈소스 플러그인인데 Multi-DB를 지원합니다. CREATE DATABASE로 테넌트별 그래프를 만들 수 있고, Cypher 쿼리도 그대로 씁니다.

ArangoDB도 봤습니다. 멀티모델(문서 + 그래프 + 키밸류)이 매력적인데 Cypher를 쓸 수 없습니다. 자체 쿼리 언어 AQL이 그래프 탐색에는 괜찮지만 Neo4j 생태계의 라이브러리나 도구를 못 쓰게 됩니다. 온톨로지를 Cypher로 질의하는 패턴과 레퍼런스가 압도적으로 많기 때문에 생태계 호환성을 택했습니다.

DozerDB의 한계도 압니다. Fabric -크로스 DB 쿼리 -은 아직 미지원이라 서로 다른 데이터베이스를 한 번의 Cypher로 질의하는 건 불가능합니다. Phase 3 이후로 미뤘습니다. 당장은 단일 테넌트 내 질의만으로 충분합니다.

네 개를 연결하면

네 가지를 나란히 놓기만 하면 그냥 도구 네 개입니다.

메타데이터 동기화 아키텍처

DataHub에서 Glossary Term이 바뀌면 Kafka MCL 이벤트가 발행됩니다. 이 이벤트가 DozerDB 온톨로지 그래프에 실시간 반영되고, 동시에 Vanna의 RAG Store에도 들어갑니다. NL2SQL 프롬프트에 주입되는 맥락이 자동 갱신되는 셈입니다. ApeRAG의 Entity Extraction은 DataHub Glossary를 Taxonomy로 참조하니까 문서 검색 결과도 최신 용어 체계에 연결됩니다.

한 곳에서 용어를 고치면 네 군데가 동시에 바뀝니다. 수동으로 전파하면 규모가 커질수록 반드시 어딘가 누락됩니다.

다음 글

DataHub의 Business Glossary를 온톨로지로 쓸 때의 한계와 우회를 다룹니다.