Week 9. 서비스 아키텍처 및 성능 최적화
그래프 시각화, FastAPI 서버, Incremental Update, 성능 최적화
학습 목표
이 튜토리얼을 완료하면 다음을 할 수 있습니다:
- 그래프 시각화 구현 (D3.js, Neovis.js, pyvis)
- FastAPI 기반 API 서버 구축
- Incremental Update 및 정합성 관리
- 프로덕션 성능 최적화
핵심 개념
핵심 개념
"레스토랑 서비스" 비유
지금까지 우리는 주방(Kitchen) 시스템을 만들었습니다.
- 데이터: 식재료
- 온톨로지: 레시피
- Graph DB: 주방 설비
- LLM: 셰프 (요리 및 추론)
서비스 아키텍처는 손님에게 음식을 대접하는 것입니다.
- API (FastAPI): 웨이터. 주문(Query)을 받고 요리(Result)를 서빙합니다.
- 시각화: 플레이팅. 데이터를 먹음직스럽게(이해하기 쉽게) 보여줍니다.
- 캐싱(Caching): 미리 손질된 재료. 피크 타임에도 빠르게 서빙할 수 있게 돕습니다.
1. 서비스 아키텍처
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Frontend │────▶│ FastAPI │────▶│ Neo4j │
│ (React) │ │ Server │ │ Graph DB │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
│ ┌──────┴──────┐ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
└─────▶│ D3.js │ │ LLM │◀──────┘
│ Viz │ │ Service │
└──────────┘ └──────────┘2. 그래프 시각화
Neovis.js (Neo4j 직접 연결)
import NeoVis from 'neovis.js';
const config = {
containerId: "viz",
neo4j: {
serverUrl: "bolt://localhost:7687",
serverUser: "neo4j",
serverPassword: "password",
},
labels: {
Movie: { label: "title", size: "popularity" },
Person: { label: "name" }
},
relationships: {
DIRECTED: { thickness: "weight" },
ACTED_IN: { thickness: "weight" }
},
initialCypher: `
MATCH (d:Director)-[:DIRECTED]->(m:Movie)<-[:ACTED_IN]-(a:Actor)
RETURN d, m, a LIMIT 100
`
};
const viz = new NeoVis(config);
viz.render();pyvis (Python 기반)
from pyvis.network import Network
def visualize_graph(neo4j_driver, query):
net = Network(height="600px", width="100%", notebook=True)
with neo4j_driver.session() as session:
result = session.run(query)
for record in result:
for node in record["nodes"]:
net.add_node(node.id, label=node["name"])
for rel in record["rels"]:
net.add_edge(rel.start_node.id, rel.end_node.id, title=rel.type)
return net.show("graph.html")3. FastAPI 서버
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from neo4j import GraphDatabase
app = FastAPI(title="Knowledge Graph API")
class QueryRequest(BaseModel):
query: str
params: dict = {}
class EntityRequest(BaseModel):
name: str
entity_type: str
properties: dict = {}
@app.post("/query")
async def execute_query(request: QueryRequest):
with driver.session() as session:
result = session.run(request.query, request.params)
return [dict(record) for record in result]
@app.get("/entity/{entity_type}/{name}")
async def get_entity(entity_type: str, name: str):
query = f"""
MATCH (e:{entity_type} {{name: $name}})
OPTIONAL MATCH (e)-[r]-(connected)
RETURN e, collect({{rel: type(r), node: connected}}) as connections
"""
with driver.session() as session:
result = session.run(query, name=name)
record = result.single()
if not record:
raise HTTPException(404, "Entity not found")
return dict(record)
@app.post("/entity")
async def create_entity(request: EntityRequest):
query = f"""
CREATE (e:{request.entity_type} {{name: $name}})
SET e += $properties
RETURN e
"""
with driver.session() as session:
result = session.run(query, name=request.name, properties=request.properties)
return dict(result.single()["e"])4. Incremental Update
class GraphUpdater:
def __init__(self, driver):
self.driver = driver
def upsert_entity(self, entity_type, entity_id, properties):
"""개체 업서트 (있으면 업데이트, 없으면 생성)"""
query = f"""
MERGE (e:{entity_type} {{id: $id}})
SET e += $properties
SET e.updated_at = datetime()
RETURN e
"""
with self.driver.session() as session:
return session.run(query, id=entity_id, properties=properties).single()
def add_relationship(self, source_id, target_id, rel_type, properties=None):
"""관계 추가 (중복 방지)"""
query = """
MATCH (s {id: $source_id})
MATCH (t {id: $target_id})
MERGE (s)-[r:$rel_type]->(t)
SET r += $properties
RETURN r
"""
with self.driver.session() as session:
return session.run(query,
source_id=source_id,
target_id=target_id,
rel_type=rel_type,
properties=properties or {}
).single()
def sync_from_source(self, source_data, last_sync_time):
"""외부 소스와 동기화"""
new_records = [r for r in source_data if r["updated_at"] > last_sync_time]
for record in new_records:
self.upsert_entity(
record["type"],
record["id"],
record["properties"]
)5. 성능 최적화
인덱싱 전략
// 조회 성능 향상
CREATE INDEX entity_id FOR (n:Entity) ON (n.id)
CREATE INDEX entity_name FOR (n:Entity) ON (n.name)
// 복합 인덱스
CREATE INDEX movie_year_title FOR (m:Movie) ON (m.year, m.title)
// 전문 검색
CREATE FULLTEXT INDEX entity_search FOR (n:Entity) ON EACH [n.name, n.description]쿼리 최적화
// Bad: 전체 스캔
MATCH (m:Movie) WHERE m.title CONTAINS "Inception" RETURN m
// Good: 인덱스 활용
MATCH (m:Movie) WHERE m.title = "Inception" RETURN m
// Better: 파라미터 사용 (캐싱)
MATCH (m:Movie {title: $title}) RETURN m면접 질문 맛보기
Q: 지식 그래프 서비스의 확장성을 어떻게 확보하나요?
- 수평 확장: Neo4j Cluster (Causal Clustering)
- 캐싱: Redis로 빈번한 쿼리 결과 캐싱
- 읽기/쓰기 분리: Read Replicas 활용
- 샤딩: 도메인별 그래프 분리
더 많은 면접 질문은 Premium Interviews (opens in a new tab)에서 확인하세요.
🎬 프로젝트: 영화 추천 지식그래프
🎉 프로젝트 완료!
| Week | 주제 | 프로젝트 마일스톤 |
|---|---|---|
| 1 | 온톨로지 입문 | ✅ 영화 도메인 설계 완료 |
| 2 | RDF & RDFS | ✅ 영화 10편 RDF 변환 완료 |
| 3 | OWL & 추론 | ✅ 추론 규칙 적용 완료 |
| 4 | 지식 추출 | ✅ 영화 100편 자동 수집 완료 |
| 5 | Neo4j | ✅ 그래프 DB 구축 완료 |
| 6 | GraphRAG | ✅ 자연어 질의 시스템 완료 |
| 7 | 온톨로지 에이전트 | ✅ 자동 업데이트 에이전트 완료 |
| 8 | 도메인 확장 | ✅ 의료 도메인 확장 완료 |
| 9 | 서비스 배포 | 프로덕션 배포 |
Week 9 마일스톤: 영화 추천 서비스 배포
9주간 만든 영화 지식그래프를 실제 서비스로 배포합니다.
서비스 아키텍처:
┌─────────────────────────────────────────────┐
│ Frontend │
│ (React Dashboard + Chat Interface) │
└─────────────────┬───────────────────────────┘
│ REST API
┌─────────────────▼───────────────────────────┐
│ FastAPI Server │
│ /recommend /search /chat /update │
└──────┬──────────────┬───────────────────────┘
│ │
┌──────▼──────┐ ┌─────▼─────┐
│ Neo4j │ │ LLM │
│ (Graph DB) │ │ (OpenAI) │
└─────────────┘ └───────────┘API 엔드포인트:
@app.get("/recommend/{movie_title}")
# 유사 영화 추천
@app.post("/chat")
# 자연어 질의 (GraphRAG)
@app.post("/update")
# 새 영화 추가 (에이전트 트리거)
@app.get("/graph/stats")
# 지식그래프 통계배포 체크리스트:
- Neo4j Aura 클라우드 설정
- FastAPI 서버 Docker화
- Vercel/Railway 배포
- API 키 환경변수 설정
- 모니터링 (Sentry)
축하합니다! 9주간의 프로젝트가 완성됩니다.
프로젝트 노트북에서는 다음을 직접 구현합니다:
- FastAPI로 /recommend, /chat, /update API 구현
- pyvis로 지식그래프 시각화 대시보드
- Neo4j Aura 클라우드 연결
- Docker Compose로 원클릭 배포
완성된 시스템: "놀란 감독 스타일의 SF 영화 추천해줘"라고 물으면, 지식그래프에서 감독-장르-평점 관계를 추론하고 자연어로 답변하는 AI 에이전트
실습 노트북
이론을 더 깊이 탐구하고 싶다면:
실습 노트북에서는 추가로 다음 내용을 다룹니다:
- Kubernetes 배포 설정
- 로드 테스트 (Locust) 및 성능 튜닝
- Prometheus + Grafana 모니터링
- CI/CD 파이프라인 구성
과정 완료!
축하합니다! 9주 과정을 모두 마쳤습니다.
배운 내용 요약
- 온톨로지 공학: RDF, RDFS, OWL, 추론
- 지식 추출: NER, RE, Entity Resolution
- 그래프 DB: Neo4j, Cypher
- LLM 통합: GraphRAG, 온톨로지 에이전트
- 프로덕션: 시각화, API, 최적화
다음 단계
- 실제 도메인 프로젝트 진행
- Premium 콘텐츠 (opens in a new tab)로 심화 학습
- 커뮤니티 참여 및 기여