Neo4j의 아키텍처, Cypher 쿼리 언어, 벡터 인덱스, GDS 라이브러리, Python 드라이버까지 지식 그래프 구축에 필요한 Neo4j의 핵심 기능을 다룹니다.
Neo4j는 세계에서 가장 널리 사용되는 프로퍼티 그래프 데이터베이스입니다. 2007년 처음 출시된 이후, 그래프 데이터베이스 시장을 이끌어 왔습니다.
Neo4j의 가장 큰 특징은 **Native Graph Storage(네이티브 그래프 저장)**입니다. 관계형 데이터베이스가 JOIN 연산으로 관계를 처리하는 것과 달리, Neo4j는 노드와 관계를 물리적으로 인접하게 저장합니다.
이 구조 덕분에 관계 순회의 시간 복잡도가 데이터 크기와 무관하게 **O(1)**에 가깝습니다. 관계형 데이터베이스에서 JOIN의 비용이 데이터 크기에 따라 증가하는 것과 대비됩니다.
| 옵션 | 설명 | 적합한 경우 |
|---|---|---|
| Neo4j Community | 오픈소스, 단일 인스턴스 | 개발/학습, 소규모 프로젝트 |
| Neo4j Enterprise | 클러스터링, 역할 기반 접근 제어 | 프로덕션 환경 |
| AuraDB Free | 클라우드 매니지드, 무료 티어 | 프로토타이핑 |
| AuraDB Professional | 클라우드 매니지드, SLA 보장 | 프로덕션 클라우드 |
학습 목적이라면 AuraDB Free 티어를 추천합니다. 설치 없이 브라우저에서 바로 Cypher 쿼리를 실행할 수 있으며, 200K 노드와 400K 관계까지 무료로 사용할 수 있습니다.
Cypher는 Neo4j의 선언적 그래프 쿼리 언어입니다. SQL에 비견될 만큼 직관적이며, 그래프 패턴을 ASCII 아트처럼 표현합니다.
// 노드 생성
CREATE (p:Person {name: "김개발", role: "Engineer"})
// 관계 생성
MATCH (p:Person {name: "김개발"})
MATCH (t:Technology {name: "Neo4j"})
CREATE (p)-[:USES {since: 2023}]->(t)
// 패턴 매칭 - Cypher의 핵심
MATCH (p:Person)-[:USES]->(t:Technology)
WHERE p.name = "김개발"
RETURN p.name, t.name
// 가변 길이 경로
MATCH path = (a:Technology)-[:DEPENDS_ON*1..3]->(b:Technology)
RETURN pathCypher의 패턴 표현은 다음과 같은 구문을 따릅니다.
() — 노드--> — 방향 있는 관계-[:TYPE]-> — 타입이 지정된 관계*1..3 — 1~3 홉의 가변 길이 경로// CREATE: 노드와 관계 생성
CREATE (d:Document {title: "GraphRAG 가이드", createdAt: datetime()})
// READ: 조건부 조회
MATCH (d:Document)
WHERE d.createdAt > datetime("2026-01-01")
RETURN d.title, d.createdAt
ORDER BY d.createdAt DESC
LIMIT 10
// UPDATE: 속성 수정
MATCH (d:Document {title: "GraphRAG 가이드"})
SET d.updatedAt = datetime(), d.status = "published"
// DELETE: 노드와 관련 관계 삭제
MATCH (d:Document {title: "임시 문서"})
DETACH DELETE d// 기술별 문서 수 집계
MATCH (d:Document)-[:COVERS]->(t:Technology)
RETURN t.name, count(d) AS docCount
ORDER BY docCount DESC
// 저자별 전문 분야 목록
MATCH (a:Author)-[:EXPERT_IN]->(t:Technology)
RETURN a.name, collect(t.name) AS expertises
// CASE 표현식
MATCH (t:Technology)
RETURN t.name,
CASE
WHEN t.type = "Database" THEN "데이터베이스"
WHEN t.type = "Framework" THEN "프레임워크"
ELSE "기타"
END AS typeKorean쿼리 성능을 위해 적절한 인덱스를 생성하는 것이 중요합니다.
// B-tree 인덱스 (기본)
CREATE INDEX idx_person_name FOR (p:Person) ON (p.name)
// 복합 인덱스
CREATE INDEX idx_doc_category_date
FOR (d:Document) ON (d.category, d.publishedAt)
// 유일성 제약 (자동으로 인덱스도 생성)
CREATE CONSTRAINT uniq_tech_name
FOR (t:Technology) REQUIRE t.name IS UNIQUE텍스트 기반 검색을 위한 **Full-Text Index(전문 검색 인덱스)**도 지원합니다.
// 전문 검색 인덱스 생성
CREATE FULLTEXT INDEX ft_document_content
FOR (d:Document) ON EACH [d.title, d.content, d.summary]
// 전문 검색 쿼리
CALL db.index.fulltext.queryNodes("ft_document_content", "GraphRAG knowledge graph")
YIELD node, score
RETURN node.title, score
ORDER BY score DESC
LIMIT 5Neo4j 5.x부터 **Vector Index(벡터 인덱스)**를 네이티브로 지원합니다. 이는 GraphRAG 구현에 핵심적인 기능입니다.
// 벡터 인덱스 생성 (1536차원, OpenAI 임베딩 기준)
CREATE VECTOR INDEX vec_document_embedding
FOR (d:Document) ON (d.embedding)
OPTIONS {
indexConfig: {
`vector.dimensions`: 1536,
`vector.similarity_function`: 'cosine'
}
}
// 벡터 유사도 검색
WITH [0.1, 0.2, ...] AS queryEmbedding // 실제로는 1536차원 벡터
CALL db.index.vector.queryNodes(
"vec_document_embedding",
10,
queryEmbedding
) YIELD node, score
RETURN node.title, score벡터 인덱스와 그래프 순회를 하나의 쿼리에서 결합할 수 있다는 것이 Neo4j의 강점입니다. 벡터 유사도로 후보를 찾고, 그래프 관계를 따라 추가 컨텍스트를 수집하는 패턴이 GraphRAG의 핵심입니다.
GDS는 Neo4j 위에서 실행되는 그래프 분석 및 머신러닝 라이브러리입니다. 60개 이상의 알고리즘을 제공합니다.
| 카테고리 | 알고리즘 | 용도 |
|---|---|---|
| 중심성 | PageRank, Betweenness | 중요 노드 식별 |
| 커뮤니티 감지 | Louvain, Label Propagation | 그룹/클러스터 발견 |
| 유사도 | Node Similarity, KNN | 유사 노드 탐색 |
| 경로 탐색 | Dijkstra, A* | 최단 경로 계산 |
| 노드 임베딩 | Node2Vec, FastRP, GraphSAGE | 노드를 벡터로 변환 |
GDS는 먼저 **Graph Projection(그래프 프로젝션)**을 통해 분석 대상 그래프를 메모리에 로드한 후, 알고리즘을 실행합니다.
// 1. 그래프 프로젝션 생성
CALL gds.graph.project(
'techGraph',
['Technology', 'Document'],
{
COVERS: {orientation: 'UNDIRECTED'},
DEPENDS_ON: {orientation: 'NATURAL'}
}
)
// 2. PageRank 실행
CALL gds.pageRank.stream('techGraph')
YIELD nodeId, score
WITH gds.util.asNode(nodeId) AS node, score
RETURN node.name, score
ORDER BY score DESC
LIMIT 10
// 3. 커뮤니티 감지 (Louvain)
CALL gds.louvain.stream('techGraph')
YIELD nodeId, communityId
WITH gds.util.asNode(nodeId) AS node, communityId
RETURN communityId, collect(node.name) AS members
ORDER BY size(collect(node.name)) DESC
// 4. 노드 임베딩 (FastRP)
CALL gds.fastRP.stream('techGraph', {embeddingDimension: 128})
YIELD nodeId, embedding
WITH gds.util.asNode(nodeId) AS node, embedding
SET node.graphEmbedding = embedding
// 5. 프로젝션 정리
CALL gds.graph.drop('techGraph')실제 애플리케이션에서는 Python 드라이버를 통해 Neo4j에 연결합니다.
from neo4j import GraphDatabase
# 드라이버 생성
driver = GraphDatabase.driver(
"bolt://localhost:7687",
auth=("neo4j", "password")
)
# 세션을 통한 쿼리 실행
def get_technologies(driver, category: str) -> list[dict]:
"""특정 카테고리의 기술 목록을 조회합니다."""
query = """
MATCH (t:Technology)-[:BELONGS_TO]->(c:Category {name: $category})
RETURN t.name AS name, t.version AS version
ORDER BY t.name
"""
records, _, _ = driver.execute_query(
query,
category=category,
database_="neo4j"
)
return [record.data() for record in records]
# 사용
techs = get_technologies(driver, "Database")
for tech in techs:
print(f"{tech['name']} v{tech['version']}")
driver.close()from neo4j import GraphDatabase, ManagedTransaction
driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))
def create_document(tx: ManagedTransaction, title: str, techs: list[str]) -> str:
"""문서를 생성하고 기술과 연결합니다."""
result = tx.run("""
CREATE (d:Document {
title: $title,
createdAt: datetime()
})
WITH d
UNWIND $techs AS techName
MATCH (t:Technology {name: techName})
CREATE (d)-[:COVERS]->(t)
RETURN elementId(d) AS docId
""", title=title, techs=techs)
record = result.single()
return record["docId"]
# 쓰기 트랜잭션
with driver.session(database="neo4j") as session:
doc_id = session.execute_write(
create_document,
title="Neo4j 벡터 인덱스 활용법",
techs=["Neo4j", "Python"]
)
print(f"생성된 문서 ID: {doc_id}")
driver.close()from neo4j import GraphDatabase
from openai import OpenAI
openai_client = OpenAI()
neo4j_driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))
def hybrid_search(question: str, top_k: int = 5) -> list[dict]:
"""벡터 + 그래프 하이브리드 검색을 수행합니다."""
# 1. 질문을 벡터로 변환
response = openai_client.embeddings.create(
model="text-embedding-3-small",
input=question
)
query_embedding = response.data[0].embedding
# 2. 벡터 유사도로 후보 문서를 찾고, 그래프를 순회하여 관련 기술 수집
query = """
CALL db.index.vector.queryNodes("vec_document_embedding", $topK, $embedding)
YIELD node AS doc, score
OPTIONAL MATCH (doc)-[:COVERS]->(tech:Technology)
OPTIONAL MATCH (tech)-[:DEPENDS_ON]->(dep:Technology)
RETURN doc.title AS title,
score,
collect(DISTINCT tech.name) AS technologies,
collect(DISTINCT dep.name) AS dependencies
ORDER BY score DESC
"""
records, _, _ = neo4j_driver.execute_query(
query,
topK=top_k,
embedding=query_embedding,
database_="neo4j"
)
return [record.data() for record in records]
# 사용
results = hybrid_search("GraphRAG에서 커뮤니티 감지는 어떻게 작동하나요?")
for r in results:
print(f"[{r['score']:.3f}] {r['title']}")
print(f" 기술: {', '.join(r['technologies'])}")AuraDB는 Neo4j의 완전 관리형 클라우드 서비스입니다. 인프라 관리 없이 그래프 데이터베이스를 사용할 수 있습니다.
from neo4j import GraphDatabase
# AuraDB는 neo4j+s:// 프로토콜 사용 (TLS 포함)
driver = GraphDatabase.driver(
"neo4j+s://xxxxxxxx.databases.neo4j.io",
auth=("neo4j", "your-auradb-password")
)
# 연결 확인
driver.verify_connectivity()
print("AuraDB 연결 성공")AuraDB Free 티어는 30일간 비활성 상태가 지속되면 자동으로 일시 중지됩니다. 학습 중에는 주기적으로 쿼리를 실행하여 인스턴스를 활성 상태로 유지하는 것이 좋습니다.
이번 장에서는 Neo4j의 핵심 기능을 전반적으로 살펴보았습니다.
다음 장 미리보기: 4장에서는 Amazon Neptune을 중심으로 다른 그래프 데이터베이스를 살펴봅니다. Neptune Analytics의 임베딩 저장, Bedrock 통합, 그리고 TigerGraph, JanusGraph, Memgraph 등 대안도 비교합니다.
이 글이 도움이 되셨나요?
관련 주제 더 보기
Amazon Neptune의 아키텍처와 Bedrock 통합, 그리고 TigerGraph, JanusGraph, Memgraph 등 주요 그래프 데이터베이스를 비교하며 프로젝트에 맞는 선택 가이드를 제공합니다.
프로퍼티 그래프와 RDF의 차이, 노드/엣지/속성 설계 원칙, 온톨로지 설계부터 실전 도메인 모델링까지 지식 그래프의 데이터 모델링 기초를 다룹니다.
비정형 텍스트에서 LLM을 활용하여 엔티티와 관계를 추출하고, JSON 파싱, 엔티티 해소, Neo4j 적재까지의 전체 파이프라인을 구축하는 방법을 다룹니다.