# 오픈소스 그래프 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 데이터와 그래프 결합 가능**

#### ✅ 장점
```python
# 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 예시)
```bash
# PostgreSQL 설치
sudo apt-get install postgresql-14

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

# 확장 활성화
CREATE EXTENSION age;
```

#### 🐍 Python 사용
```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 사용
```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 사용
```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 사용)

```bash
# 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:
```

```bash
# 실행
docker-compose up -d
```

### 2. Python 통합

```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. 실제 추천 시스템 예시

```python
"""
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주)
```sql
-- 기존 SQLite 테이블 PostgreSQL로 이동
-- users, transactions, mileage_ledger 등
```

### Phase 2: Apache AGE 설치 (1일)
```bash
CREATE EXTENSION age;
SELECT create_graph('pharmacy_graph');
```

### Phase 3: 그래프 데이터 생성 (1주)
```python
# PubMed 논문 → 그래프 트리플
# Cypher CREATE 문으로 노드/엣지 생성
```

### Phase 4: 추천 시스템 업그레이드 (1주)
```python
# SQL + Cypher 혼합 쿼리
# GraphRAG 추론 경로 자동 생성
```

---

## 🎯 최종 권장사항

### ✅ **즉시 도입 가능: Apache AGE**

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

**시작 단계**:
```bash
# 1. Docker로 테스트
docker run -p 5432:5432 apache/age

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

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

---

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

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

---

## 📚 참고 링크

- **Apache AGE**: https://age.apache.org/
  - GitHub: https://github.com/apache/age
  - 문서: https://age.apache.org/age-manual/master/index.html

- **Memgraph**: https://memgraph.com/
  - 문서: https://memgraph.com/docs

- **ArangoDB**: https://www.arangodb.com/
  - 문서: https://www.arangodb.com/docs/

- **Dgraph**: https://dgraph.io/
  - GitHub: https://github.com/dgraph-io/dgraph

---

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