pharmacy-pos-qr-system/docs/opensource-graph-db-comparison.md
시골약사 032795c0fa docs: GraphRAG 및 그래프 DB 전환 기획 문서 추가
약국 POS 시스템의 GraphRAG 기반 추천 시스템 구축 관련 문서:

## 핵심 설계 문서

1. 질병코드기반 제품추천.md
   - ICD-10 질병 코드 활용 추천 시스템 설계
   - 계층 구조 (질병 → 질병군 → 제품군 → 개별 제품)
   - 처방전 기반 추천 알고리즘

2. complex-product-graph-modeling.md
   - 복합제(비맥스제트 등) 그래프 모델링
   - 성분 간 시너지 효과 표현
   - 복합 증상 매칭 쿼리 예시

3. pubmed-graphrag-workflow.md
   - PubMed → GraphRAG 전체 워크플로우 (5단계)
   - 논문 검색, 근거 추출, 지식 그래프 구축
   - MCP Server 개발 가이드

## 그래프 DB 비교 및 평가

4. sqlite-graph-evaluation.md
   - SQLite vs SQLite-Graph vs Neo4j 비교
   - 현 시점(2026-01) 평가: 기존 SQL 유지 권장
   - 6개월 후 재평가 계획

5. opensource-graph-db-comparison.md
   - 오픈소스 그래프 DB 비교 (Neo4j, ArangoDB 등)

6. 온톨로지로전환.md
   - 관계형 DB → 온톨로지 구조 전환 가이드
   - PubMed RAG 활용 방안
   - 추론 규칙 설계

## PubMed GraphRAG 활용

7. pycnogenol-multi-indication-graphrag.md
   - 피크노제놀 다중 적응증 GraphRAG 구축 사례
   - 7가지 적응증별 근거 수준

8. grpahrag_아쉬아간다.md
   - Ashwagandha GraphRAG 구축 사례

9. pubdmed이용ai.md
   - PubMed + AI 통합 활용 가이드

## 추가 워크플로우

10. pubmed-graphrag-workflow_next.md
    - 다음 단계 워크플로우

11. PostgresGRAPH전환.md
    - PostgreSQL + Apache AGE 전환 가이드

모든 문서는 한국어로 작성되었으며, 코드 예시는 영어로 포함.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-24 21:04:56 +09:00

20 KiB

오픈소스 그래프 DB 비교 및 추천

Neo4j 대신 사용할 수 있는 오픈소스 그래프 데이터베이스 옵션

작성일: 2026-01-24 대상 프로젝트: 약국 POS QR 시스템 (PubMed GraphRAG)


🎯 평가 기준

우리 프로젝트에 필요한 조건:

✅ 완전한 오픈소스 (상업적 제약 없음)
✅ Cypher 또는 유사 쿼리 언어 지원
✅ Python 통합 용이
✅ 배포 간편 (별도 서버 최소화)
✅ SQLite/PostgreSQL 같은 익숙한 DB와 통합
✅ 중소 규모 그래프 최적화 (1,000~10,000 노드)

📊 오픈소스 그래프 DB 비교

1. Apache AGE 최고 추천!

┌─────────────────────────────────────────────────────────┐
│  PostgreSQL + 그래프 = Apache AGE                       │
│  "기존 PostgreSQL에 그래프 기능 추가"                   │
└─────────────────────────────────────────────────────────┘

🌟 핵심 특징

  • PostgreSQL 확장 (익숙한 DB + 그래프 기능)
  • Cypher 쿼리 완벽 지원 (Neo4j와 동일)
  • SQL + Cypher 혼합 사용 가능
  • Apache 2.0 라이선스 (완전 오픈소스)
  • 기존 PostgreSQL 데이터와 그래프 결합 가능

장점

# SQL과 Cypher를 함께 사용!
# 기존 테이블 (users, products)
SELECT * FROM users WHERE age > 30;

# 그래프 쿼리 (관계 탐색)
SELECT * FROM cypher('graph_name', $$
    MATCH (u:User)-[:PURCHASED]->(p:Product)
    WHERE u.age > 30
    RETURN u.name, p.name
$$) AS (user_name text, product_name text);

⚠️ 단점

  • PostgreSQL 필요 (SQLite보다 무거움)
  • 비교적 신생 프로젝트 (2020년 시작)

📦 설치 (Ubuntu/Debian 예시)

# PostgreSQL 설치
sudo apt-get install postgresql-14

# Apache AGE 설치
sudo apt-get install postgresql-14-age

# 확장 활성화
CREATE EXTENSION age;

🐍 Python 사용

import psycopg2
from age import Age

conn = psycopg2.connect(
    host="localhost",
    database="pharmacy_db",
    user="postgres"
)

cursor = conn.cursor()

# 그래프 생성
cursor.execute("""
    SELECT create_graph('pharmacy_graph');
""")

# Cypher 쿼리
cursor.execute("""
    SELECT * FROM cypher('pharmacy_graph', $$
        CREATE (n:Drug {name: 'Naproxen', type: 'NSAID'})
        RETURN n
    $$) AS (drug agtype);
""")

# 경로 탐색
cursor.execute("""
    SELECT * FROM cypher('pharmacy_graph', $$
        MATCH path = (d:Drug {name: 'Statin'})-[*1..3]->(s:Symptom)
        RETURN path
    $$) AS (path agtype);
""")

🎯 우리 프로젝트 적용

현재: SQLite (mileage.db)
    ↓
마이그레이션: PostgreSQL + Apache AGE
    ↓
장점:
- 기존 users, transactions 테이블 유지 (SQL)
- 약물-증상 관계는 그래프 (Cypher)
- 한 DB에서 모두 처리 ✨

2. Memgraph

┌─────────────────────────────────────────────────────────┐
│  빠른 인메모리 그래프 DB                                │
│  "Neo4j와 호환되는 Cypher 지원"                         │
└─────────────────────────────────────────────────────────┘

🌟 핵심 특징

  • 완전한 Cypher 지원 (Neo4j 호환)
  • 인메모리 처리 (매우 빠름)
  • 스트림 처리 지원 (Kafka 통합)
  • BSL 라이선스 (Community Edition 무료)

장점

  • Neo4j보다 빠름 (인메모리)
  • 완전한 Cypher 지원 (학습 곡선 낮음)
  • Python 라이브러리 우수

⚠️ 단점

  • 별도 서버 필요
  • 인메모리 → 메모리 많이 필요
  • 데이터 영속성 설정 필요

🐍 Python 사용

from gqlalchemy import Memgraph

memgraph = Memgraph(host='127.0.0.1', port=7687)

# Cypher 쿼리 (Neo4j와 동일)
results = memgraph.execute_and_fetch("""
    MATCH (d:Drug {name: 'Naproxen'})-[:SAFER_THAN]->(other:Drug)
    RETURN d.name, other.name
""")

for result in results:
    print(result['d.name'], result['other.name'])

3. ArangoDB

┌─────────────────────────────────────────────────────────┐
│  멀티모델 DB (문서 + 그래프 + 키-밸류)                  │
│  "하나의 DB로 모든 데이터 모델 지원"                    │
└─────────────────────────────────────────────────────────┘

🌟 핵심 특징

  • 멀티모델: 문서(JSON) + 그래프 + 키-밸류
  • AQL 쿼리 언어 (Cypher와 유사, 더 강력)
  • 완전 오픈소스 (Apache 2.0)
  • Python, JavaScript 등 다양한 드라이버

장점

  • 유연함 (그래프 + JSON 문서 모두 저장)
  • 성능 우수
  • 웹 UI 기본 제공

⚠️ 단점

  • Cypher 아님 (AQL 학습 필요)
  • 별도 서버 필요
  • 설정 복잡할 수 있음

🐍 Python 사용

from arango import ArangoClient

client = ArangoClient(hosts='http://localhost:8529')
db = client.db('pharmacy_db', username='root', password='password')

# 그래프 생성
graph = db.create_graph('pharmacy_graph')

# 문서 + 그래프 혼합 (강력!)
result = db.aql.execute("""
    FOR drug IN drugs
        FILTER drug.type == 'NSAID'
        FOR vertex, edge, path IN 1..3 OUTBOUND drug GRAPH 'pharmacy_graph'
            FILTER vertex._id == 'symptoms/pain'
            RETURN path
""")

4. Dgraph

┌─────────────────────────────────────────────────────────┐
│  GraphQL 네이티브 그래프 DB                             │
│  "GraphQL로 그래프 쿼리"                                │
└─────────────────────────────────────────────────────────┘

🌟 핵심 특징

  • GraphQL 쿼리 언어 (Cypher 대신)
  • Go로 작성 (빠르고 가벼움)
  • 분산 처리 지원
  • Apache 2.0 라이선스

장점

  • GraphQL 사용 시 최적
  • 성능 우수
  • 분산 확장 용이

⚠️ 단점

  • Cypher 미지원 (GraphQL 학습 필요)
  • 별도 서버 필요
  • 커뮤니티 Neo4j보다 작음

5. JanusGraph

┌─────────────────────────────────────────────────────────┐
│  Apache 재단 대규모 그래프 DB                           │
│  "수억 개 노드 지원"                                    │
└─────────────────────────────────────────────────────────┘

🌟 핵심 특징

  • Apache 재단 공식 프로젝트
  • 대규모 그래프 최적화 (수십억 엣지)
  • Gremlin 쿼리 언어 (Cypher 아님)
  • 다양한 백엔드 지원 (Cassandra, HBase 등)

장점

  • 대규모 그래프에 최적
  • Apache 재단 신뢰성
  • 엔터프라이즈급 기능

⚠️ 단점

  • 우리 프로젝트에 과함 (중소 규모용 아님)
  • 설정 매우 복잡
  • Gremlin 학습 곡선 높음

6. Nebula Graph

┌─────────────────────────────────────────────────────────┐
│  분산 그래프 DB                                         │
│  "중국발 오픈소스, 빠른 성능"                           │
└─────────────────────────────────────────────────────────┘

🌟 핵심 특징

  • nGQL (Cypher와 유사)
  • 분산 아키텍처
  • Apache 2.0 라이선스

⚠️ 단점

  • 별도 서버 (Meta, Graph, Storage)
  • 설정 복잡
  • 커뮤니티 주로 중국어

🏆 종합 비교표

┌─────────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│    항목     │ AGE      │ Memgraph │ ArangoDB │ Dgraph   │ JanusGrph│
├─────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ 쿼리 언어   │ Cypher   │ Cypher   │ AQL      │ GraphQL  │ Gremlin  │
├─────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ 배포 난이도 │ ⭐⭐     │ ⭐⭐⭐   │ ⭐⭐⭐   │ ⭐⭐⭐   │ ⭐⭐⭐⭐⭐│
├─────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ 학습 곡선   │ ⭐       │ ⭐       │ ⭐⭐     │ ⭐⭐     │ ⭐⭐⭐⭐  │
├─────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ 성능        │ ⭐⭐⭐⭐ │ ⭐⭐⭐⭐⭐│ ⭐⭐⭐⭐ │ ⭐⭐⭐⭐ │ ⭐⭐⭐⭐⭐│
├─────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ SQL 통합    │ ✅       │ ❌       │ ❌       │ ❌       │ ❌       │
├─────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ 중소규모 적합│ ✅       │ ✅       │ ✅       │ ⭐⭐     │ ❌       │
├─────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ 라이선스    │ Apache   │ BSL      │ Apache   │ Apache   │ Apache   │
├─────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ 우리적합도  │⭐⭐⭐⭐⭐│ ⭐⭐⭐⭐ │ ⭐⭐⭐   │ ⭐⭐     │ ⭐       │
└─────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘

⭐ 적음/쉬움/낮음 = 좋음
⭐⭐⭐⭐⭐ 많음/어려움/높음 = 나쁨 (배포, 학습 곡선)
⭐⭐⭐⭐⭐ 많음 = 좋음 (성능, 적합도)

🎯 우리 프로젝트 최적 선택

🥇 1순위: Apache AGE

선택 이유

✅ PostgreSQL 확장 (익숙한 환경)
✅ SQL + Cypher 혼합 사용 (마이그레이션 쉬움)
✅ 기존 데이터 + 그래프 한 DB에서 관리
✅ 완전 오픈소스 (Apache 2.0)
✅ 배포 간단 (PostgreSQL만 있으면 됨)

마이그레이션 경로

현재: SQLite
    ↓
1단계: PostgreSQL + 기본 테이블 마이그레이션
    ↓
2단계: Apache AGE 확장 설치
    ↓
3단계: 그래프 노드/엣지 생성
    ↓
결과: SQL (users, transactions) + Cypher (약물 관계)

🥈 2순위: Memgraph

선택 이유

✅ 완전한 Cypher 지원 (Neo4j 호환)
✅ 매우 빠름 (인메모리)
✅ Python 라이브러리 우수

적합한 경우

- 실시간 추천이 매우 중요
- 메모리 충분히 있음
- 별도 서버 운영 가능

🥉 3순위: ArangoDB

선택 이유

✅ 멀티모델 (유연함)
✅ 성능 우수
✅ 웹 UI 좋음

적합한 경우

- JSON 문서 + 그래프 모두 필요
- AQL 학습 가능
- 다양한 데이터 모델 실험

🛠️ Apache AGE 실전 적용 가이드

1. 설치 (Docker 사용)

# Docker Compose 설정
# docker-compose.yml
version: '3.8'
services:
  postgres-age:
    image: apache/age:latest
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: pharmacy_db
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
# 실행
docker-compose up -d

2. Python 통합

"""
Apache AGE + Python 예시
"""
import psycopg2
from age import Age

# 연결
conn = psycopg2.connect(
    host="localhost",
    database="pharmacy_db",
    user="postgres",
    password="password"
)

age = Age(conn)
cursor = conn.cursor()

# 1. 그래프 생성
age.setGraph('pharmacy_graph')
cursor.execute("SELECT create_graph('pharmacy_graph');")

# 2. 노드 생성 (Cypher)
cursor.execute("""
    SELECT * FROM cypher('pharmacy_graph', $$
        CREATE (statin:Drug {name: 'Statin', type: 'HMG-CoA inhibitor'}),
               (coq10:Drug {name: 'CoQ10', type: 'Supplement'}),
               (myopathy:Condition {name: 'Myopathy'}),
               (evidence:Evidence {pmid: '30371340', reliability: 0.95})
        RETURN statin, coq10, myopathy, evidence
    $$) AS (statin agtype, coq10 agtype, myopathy agtype, evidence agtype);
""")

# 3. 관계 생성
cursor.execute("""
    SELECT * FROM cypher('pharmacy_graph', $$
        MATCH (statin:Drug {name: 'Statin'}),
              (coq10:Drug {name: 'CoQ10'}),
              (myopathy:Condition {name: 'Myopathy'}),
              (evidence:Evidence {pmid: '30371340'})
        CREATE (statin)-[:INHIBITS {mechanism: 'HMG-CoA pathway'}]->(coq10),
               (coq10)-[:REDUCES {effect_size: -1.60, p_value: 0.001}]->(myopathy),
               (evidence)-[:SUPPORTS]->(coq10)-[:REDUCES]->(myopathy)
        RETURN statin, coq10, myopathy
    $$) AS (statin agtype, coq10 agtype, myopathy agtype);
""")

# 4. 경로 탐색 (GraphRAG!)
cursor.execute("""
    SELECT * FROM cypher('pharmacy_graph', $$
        MATCH path = (statin:Drug {name: 'Statin'})-[*1..3]->(myopathy:Condition {name: 'Myopathy'})
        RETURN path
    $$) AS (path agtype);
""")

results = cursor.fetchall()
for row in results:
    print(row[0])

# 5. SQL + Cypher 혼합!
cursor.execute("""
    -- SQL: 사용자 조회
    WITH high_risk_users AS (
        SELECT id, name, age
        FROM users
        WHERE age > 60 AND has_hypertension = true
    )
    -- Cypher: 안전한 약물 추천
    SELECT u.name, drug_name
    FROM high_risk_users u,
    LATERAL (
        SELECT * FROM cypher('pharmacy_graph', $$
            MATCH (drug:Drug)-[:SAFE_FOR]->(profile:PatientProfile {name: 'Elderly_HTN'})
            RETURN drug.name
        $$) AS (drug_name agtype)
    );
""")

conn.commit()
conn.close()

3. 실제 추천 시스템 예시

"""
Apache AGE 기반 약물 추천 시스템
"""

class DrugRecommender:
    def __init__(self, conn):
        self.conn = conn
        self.cursor = conn.cursor()

    def recommend(self, patient_id, symptom):
        """
        환자 프로필 + 증상 → 약물 추천 (근거 포함)
        """
        # 1. SQL: 환자 정보 조회
        self.cursor.execute("""
            SELECT age, hypertension, diabetes
            FROM users
            WHERE id = %s
        """, (patient_id,))

        patient = self.cursor.fetchone()
        age, has_htn, has_dm = patient

        # 2. 환자 프로필 결정
        if has_htn and has_dm:
            profile = 'Patient_HTN_DM'
        elif has_htn:
            profile = 'Patient_HTN'
        elif age > 65:
            profile = 'Elderly'
        else:
            profile = 'General'

        # 3. Cypher: 그래프 기반 추천
        self.cursor.execute(f"""
            SELECT * FROM cypher('pharmacy_graph', $$
                MATCH (drug:Drug)-[treats:TREATS]->(condition:Condition {{name: '{symptom}'}})
                WHERE NOT (drug)-[:CONTRAINDICATED_IN]->(:PatientProfile {{name: '{profile}'}})
                MATCH (evidence:Evidence)-[:SUPPORTS]->(treats)
                RETURN
                    drug.name AS drug,
                    treats.effect_size AS effect,
                    evidence.pmid AS pmid,
                    evidence.reliability AS reliability
                ORDER BY evidence.reliability DESC, treats.effect_size DESC
                LIMIT 1
            $$) AS (drug agtype, effect agtype, pmid agtype, reliability agtype);
        """)

        result = self.cursor.fetchone()

        if result:
            return {
                'drug': result[0],
                'effect_size': result[1],
                'evidence_pmid': result[2],
                'reliability': result[3],
                'patient_profile': profile
            }
        else:
            return None


# 사용 예시
recommender = DrugRecommender(conn)

# 환자 ID 123, 증상 "Pain"
recommendation = recommender.recommend(patient_id=123, symptom='Pain')

print(recommendation)
# {
#   'drug': 'Naproxen',
#   'effect_size': -1.8,
#   'evidence_pmid': '27959716',
#   'reliability': 0.99,
#   'patient_profile': 'Patient_HTN_DM'
# }

📊 마이그레이션 로드맵

Phase 1: SQLite → PostgreSQL (1주)

-- 기존 SQLite 테이블 PostgreSQL로 이동
-- users, transactions, mileage_ledger 등

Phase 2: Apache AGE 설치 (1일)

CREATE EXTENSION age;
SELECT create_graph('pharmacy_graph');

Phase 3: 그래프 데이터 생성 (1주)

# PubMed 논문 → 그래프 트리플
# Cypher CREATE 문으로 노드/엣지 생성

Phase 4: 추천 시스템 업그레이드 (1주)

# SQL + Cypher 혼합 쿼리
# GraphRAG 추론 경로 자동 생성

🎯 최종 권장사항

즉시 도입 가능: Apache AGE

선택 이유:

  1. PostgreSQL 확장 (익숙함)
  2. SQL + Cypher 모두 사용 가능
  3. 기존 데이터 + 그래프 한 DB
  4. 완전 오픈소스
  5. 마이그레이션 쉬움

시작 단계:

# 1. Docker로 테스트
docker run -p 5432:5432 apache/age

# 2. Python 라이브러리 설치
pip install psycopg2-binary age

# 3. 간단한 그래프 생성 테스트
python test_age.py

🔮 대안: Memgraph (성능 최우선 시)

조건:

  • 별도 서버 운영 가능
  • 메모리 충분 (8GB+)
  • 실시간 추천 필수

📚 참고 링크


작성: 2026-01-24 추천: Apache AGE