정형 DB·빅데이터와 함께 가기 — 그래프는 대체가 아니라 보강이다 (확장편)

@JavaPark · May 26, 2026 · 15 min read

안녕하세요, 자바파커입니다.

"회사 PG에 ERD가 잘 잡혀 있고 Snowflake랑 ES도 굴리는데, 그래프 DB까지 깔라고요?"

6편 회고로 시리즈를 마무리한 뒤 받은 피드백 중 가장 묵직한 질문이었습니다. 사실 1인 블로그 100개 글이라는 작은 예제로 풀었던 시리즈여서, 이미 정형 DB와 빅데이터 스택이 깔린 조직 관점이 약했어요. 그래서 한 편 더 붙입니다.

결론부터 말씀드리면 — 그래프 DB는 RDB·DW·검색엔진을 대체하는 게 아니라 위에 얹는 한 층입니다. 각자 잘하는 영역이 명확해서, 한 곳에 다 욱여넣으려는 순간 운영이 무너집니다.

이번 편에서는 다섯 가지를 다룹니다. (1) 4-tier 데이터 아키텍처, (2) ERD ↔ 온톨로지 매핑, (3) 동기화 패턴, (4) 빅데이터 스케일 전략, (5) 안티패턴.


4-tier 데이터 아키텍처 — 각자 잘하는 일

먼저 큰 그림. 운영 중인 데이터 시스템을 네 층으로 나눠봅니다.

대표 도구 잘하는 일 못하는 일
운영 정형 DB (OLTP) PostgreSQL, MySQL 트랜잭션, 강한 일관성, 정형 스키마 검증 다단계 관계 탐색, 자유 텍스트 검색
데이터 웨어하우스 (OLAP) Snowflake, BigQuery 대용량 집계, 분석 쿼리, 컬럼 압축 실시간 트랜잭션, 관계 깊이 탐색
검색엔진 Elasticsearch, OpenSearch 자유 텍스트, 패싯, 역색인 관계 추적, 정확한 결과 보장
지식 그래프 Neo4j, Memgraph 관계 탐색, 의미 매칭(+벡터), 사실 추론 대용량 집계, 트랜잭션 처리

이 표를 외워두면 다음 질문에 즉답이 됩니다. "이 데이터를 어디에 둬야 하나?"

  • 사용자/주문/결제 → OLTP
  • 일별 매출 집계 → DW
  • 상품 검색·자동완성 → 검색엔진
  • 카테고리·인용·추천 관계 → 지식 그래프

그래프 DB는 보통 "지식 레이어" 역할입니다. 원천 데이터는 OLTP·DW에 그대로 두고, 그래프는 관계와 의미를 압축한 작은 사본을 가집니다.


ERD ↔ 온톨로지 매핑

이미 정형 DB에 ERD가 있다면, 그 ERD가 곧 온톨로지의 1차 초안입니다. 매핑 규칙은 단순합니다.

ERD 온톨로지 / 그래프
테이블 클래스(Label)
PK Unique constraint
FK Relationship type
일반 컬럼 Property
조인 테이블 (N:N) 관계로 직접 변환 (별도 노드 없이)
상속 테이블 다중 라벨 또는 subClassOf (보통 다중 라벨이 가벼움)

예시 — 단순 e-commerce ERD를 그래프로:

-- ERD (Postgres)
CREATE TABLE products (id UUID PK, name TEXT, category_id UUID FK);
CREATE TABLE categories (id UUID PK, name TEXT, parent_id UUID FK);
CREATE TABLE product_tags (product_id UUID, tag_id UUID);  -- 조인 테이블
CREATE TABLE tags (id UUID PK, name TEXT);

→ 그래프로:

// 라벨과 관계만 남기고, 조인 테이블은 관계로 흡수
(:Product {id, name})-[:IN_CATEGORY]->(:Category {id, name})
(:Category)-[:CHILD_OF]->(:Category)
(:Product)-[:HAS_TAG]->(:Tag {id, name})

여기서 한 가지 결정이 필요합니다. 그래프에 어떤 컬럼까지 옮길 것인가?

지침: 검색·관계 탐색에 쓰는 컬럼만. 가격·재고처럼 트랜잭션이 자주 일어나고 시점 정합성이 중요한 컬럼은 그래프로 옮기지 마세요. OLTP가 항상 정답입니다.


동기화 패턴 — 그래프는 어떻게 최신 상태가 되는가

그래프가 사본이라면, 원천(OLTP/DW)이 바뀔 때 그래프도 따라가야 합니다. 세 가지 패턴이 있습니다.

1) 배치 동기화 (가장 단순)

야간 1회 또는 시간당 1회, ETL 잡으로 그래프를 다시 적재. 3편의 적재 스크립트가 정확히 이 패턴입니다.

  • 언제: 데이터 변경이 느리고, 분 단위 지연이 허용될 때
  • 장점: 운영이 가장 쉬움. 실패해도 재실행
  • 단점: 신선도(freshness) 제한

2) CDC (Change Data Capture)

PG의 logical replication, MySQL의 binlog, Debezium 같은 도구로 변경 이벤트를 흘려보내 그래프에 반영.

  • 언제: 초·분 단위 신선도가 필요할 때
  • 장점: 거의 실시간
  • 단점: 운영 복잡도 ↑. 이벤트 순서·중복·실패 처리

3) 하이브리드 (현실에서 가장 흔함)

  • 핵심 엔티티(상품·카테고리): CDC로 거의 실시간
  • 파생/집계(인기 태그 톱100): 배치
  • LLM 추출 관계(related/cites): 글 발행 시점 워크플로

90%의 조직이 이 하이브리드입니다. 무리하게 모든 데이터를 같은 신선도로 맞추려 들지 마세요. 신선도는 가장 비싼 자원입니다.


빅데이터 스케일 전략 — 다 그래프에 넣지 마라

가장 흔한 실수: "이왕 그래프 DB 깔았으니 다 넣자."

수천만 노드·수억 관계로 가는 순간 Neo4j Community는 한계가 옵니다. Enterprise·AuraDB로 가도 비용이 급증해요. 스케일 전략은 무엇을 그래프에 두지 않을 것인가의 문제입니다.

그래프에 두는 것

  • 검색에 쓰이는 엔티티·관계 (수만~수백만 노드 수준)
  • 사실 기반 의미 정보 (카테고리, 인용, 선수 관계)
  • 검색용 임베딩 (Neo4j 5+ 벡터 인덱스)

그래프에 두지 않는 것

  • 원천 트랜잭션 데이터 — OLTP에 그대로 (FK로 참조)
  • 이벤트 로그, 클릭스트림 — DW나 데이터 레이크에
  • 이미지·파일 바이너리 — 객체 스토리지(S3)에 URL만
  • 시계열 메트릭 — 시계열 DB(Influx, Timescale)에

참조로 가볍게 잇기

그래프에는 외부 ID만 두고, 상세는 원천에서 가져옵니다.

// 그래프 노드는 가볍게
(:Product {sku: "SKU-12345"})
// 질의 시 SKU로 PG에서 상세 조회
const product = await pg.query("SELECT * FROM products WHERE sku=$1", [sku])

이 패턴은 그래프 사이즈를 수십 분의 1로 줄이면서 응답성도 챙깁니다. GraphRAG의 합성 단계에서 LLM에 넘기는 컨텍스트도 가벼워지고요.


검색엔진과 분담 — 자유 텍스트는 거기 두자

Elasticsearch가 이미 있다면, 자유 텍스트 검색은 그쪽이 답입니다. 그래프 + 검색엔진의 자연스러운 분담:

질의 유형 어디로 가나
"iPhone 케이스" (자유 텍스트) ES
"가죽 케이스 중 5만 원 이하" (필터) ES
"이 상품과 함께 산 상품" (관계) 그래프
"이 브랜드의 다른 카테고리 상품" (관계) 그래프
"이 상품 리뷰에 자주 등장한 키워드" (텍스트+관계) ES + 그래프

5편의 GraphRAG 라우터에 채널을 하나 더 추가하면 됩니다.

// 5편 router에 channel = "search" 추가
const routerTool = {
  /* ... */
  properties: {
    mode: { type: "string", enum: ["graph", "vector", "search", "hybrid"] },
  },
}

그러면 자유 텍스트는 ES, 관계는 그래프, 의미 매칭은 벡터 — 세 채널이 자연스럽게 협업합니다.


실전 분담 예시 — e-commerce 추천 검색

조각조각 본 걸 한 시나리오로 묶어봅니다.

사용자: "지난주에 산 운동화랑 어울리는 가을 외투 추천해줘"

단계 시스템 하는 일
1 OLTP (PG) 사용자 최근 주문에서 "지난주 운동화" SKU 조회
2 그래프 (Neo4j) 운동화 SKU → 함께 산 상품(:BOUGHT_TOGETHER) → 카테고리 매핑
3 그래프 "가을" 시즌 태그·"외투" 카테고리 노드 필터
4 검색엔진 (ES) 필터링된 후보군에서 자유 텍스트(스타일·색상) 매칭
5 벡터 (Neo4j 인덱스 or 별도) 사용자 취향 임베딩과 후보 임베딩 매칭
6 DW (Snowflake) 후보별 최근 7일 판매·환불률 가중치
7 LLM 위 정보를 받아 자연어 추천 답변 합성

7단계 중 그래프 DB가 담당하는 건 2·3·5번뿐입니다. 그래프가 중심이 아니라 한 축이라는 게 잘 보여요.


안티패턴 5가지

직접 부딪히거나 다른 케이스에서 들은 흔한 사고들.

1) "그래프 = 새 OLTP"로 쓰기

트랜잭션·결제·재고를 그래프에 직접 쓰는 패턴. 강한 일관성·고가용성이 필요하면 반드시 RDB에. 그래프는 보조.

2) 모든 RDB 컬럼을 그래프에 미러링

ERD 그대로 옮겨 100개 속성을 가진 노드가 등장합니다. 검색·관계에 쓰는 것만 옮기는 게 원칙입니다.

3) 그래프를 DW 대체로 사용

월별 매출 집계를 그래프에서 돌리려고 시도. OLAP 워크로드는 DW가 답입니다. Neo4j는 집계 성능이 RDB·DW의 비교 대상이 아닙니다.

4) CDC 없이 매번 풀 재적재

데이터가 수백만 건인데 일 1회 전체 재적재 → 잡 시간이 4시간을 넘김. 변경된 것만 incremental로 가야 합니다.

5) 검색엔진을 그래프로 대체 시도

ES가 잘하는 자유 텍스트·패싯·랭킹을 그래프 + 벡터로 다 재현하려고 하면, 운영 비용은 5배 들고 결과는 더 나쁩니다. 잘하는 일은 잘하는 도구에 맡기세요.


6편 회고에 보태는 한 마디

6편 도메인 적합도 표에 한 줄 추가합니다.

이미 RDB·DW·ES가 깔린 조직에서 그래프 DB는 "관계와 의미" 한 층으로 추가하는 게 정답입니다. 대체가 아니라 보강. 이 한 줄을 받아들이면 도입 의사결정이 훨씬 쉬워집니다.

처음부터 빅데이터를 가정하고 시작할 필요는 없습니다. 작은 규모(이번 시리즈처럼 100개 글)로 가설을 검증한 뒤, 핵심 엔티티만 그래프에 두고 나머지는 기존 스택에 맡기는 점진적 확장이 가장 안전합니다.


자주 묻는 질문 (FAQ)

Q1. ERD가 100개 테이블인 큰 조직인데, 다 매핑해야 하나요? 아니요. 검색 시나리오에 등장하는 테이블만 매핑하세요. 보통 20% 미만입니다. 매핑하지 않은 테이블은 그래프에서 ID 참조만 두고 OLTP에서 가져옵니다.

Q2. RDF/SPARQL이 정형 데이터 통합엔 더 정통적이지 않나요? 맞습니다. 정형 데이터·온톨로지 추론이 핵심이라면 RDF(GraphDB, Stardog) 진영이 더 성숙합니다. 다만 LLM·GraphRAG 생태계는 LPG(Neo4j) 쪽이 압도적이라, RAG가 목적이면 여전히 Neo4j를 권합니다. 둘 다 쓰는 조직도 있어요.

Q3. 그래프 DB의 백업·HA는 어떻게 하나요? Community Edition은 단일 노드 기준 dump/restore가 표준이고, HA가 필요하면 AuraDB 또는 Enterprise 클러스터로 가야 합니다. 운영 단계에 가까워질수록 매니지드 서비스의 비용 대비 이득이 큽니다. 자체 운영은 학습·프로토타입 구간까지만 권합니다.


여러분 회사의 데이터 스택은 어떻게 구성돼 있나요? 댓글로 알려주시면, 어디에 그래프 한 층을 끼우면 좋을지 같이 짚어보겠습니다.

이걸로 시리즈를 진짜 마무리합니다. 긴 여정 함께해주셔서 감사합니다.

@JavaPark
AI 시대의 개발자 도구, 실전 경험을 공유합니다