콘텐츠로 이동

Bluetape4k Graph Part 1: 그래프 데이터베이스 선택 지도

로봇 엔지니어가 Neo4j, Memgraph, AGE, TinkerGraph, FalkorDB 그래프 데이터베이스 블록을 비교하는 3D 작업대 일러스트
그래프 데이터베이스 선택은 취향 문제가 아니라 workload 문제입니다.

이 글은 bluetape4k-graph 시리즈의 1편입니다. 예전 글인 GraphDB, 언제 도입해야 할까?에서는 “관계 자체가 업무의 중심일 때만 그래프 데이터베이스를 도입하자”는 기준을 잡았습니다. 이번에는 그 다음 문제를 보겠습니다. 그래프를 쓰기로 마음먹은 뒤 실제로 더 오래 붙잡게 되는 질문, “그래서 Neo4j를 쓸까, Memgraph를 볼까, 테스트는 뭘로 돌리지?”입니다.

bluetape4k-graph는 그래프 데이터베이스를 다시 구현하는 라이브러리가 아닙니다. 그런 일을 시작하면 라이브러리 하나 만들려다 작은 데이터베이스 회사를 차리게 됩니다. Neo4j, Memgraph, Apache AGE, TinkerGraph, FalkorDB가 이미 잘하는 엔진과 query language는 그대로 두고, Kotlin/JVM 서비스에서 매번 반복되는 부분만 공통 API와 adapter로 묶었습니다.

Graph storage selection map for Neo4j, Memgraph, Apache AGE, TinkerGraph, and FalkorDB
먼저 테스트/운영/쓰기/읽기 성격을 나누고 graph db를 고르는 편이 시행착오가 적습니다.

현재 프로젝트 기준으로 바로 붙일 수 있는 graph storage는 아래와 같습니다.

Graph DBAPI 성격어울리는 상황
Neo4jNeo4j Java Driver 기반 Cypher운영 성숙도와 생태계가 중요한 기본 production 선택
MemgraphNeo4j 호환 protocol 기반 Cypherlow-latency, write-heavy 후보를 검증해야 하는 경우
Apache AGEPostgreSQL 위에서 Cypher-over-SQLPostgreSQL 중심 운영 환경에 그래프를 얹고 싶은 경우
TinkerGraphTinkerPop/Gremlin in-memory단위 테스트, 예제, 로컬 fixture
FalkorDBRedis module 기반 openCypher subsetRedis 운영 환경에 가벼운 graph service를 붙이는 경우

이 표를 볼 때 한 가지는 꼭 분리해서 봐야 합니다. “가장 좋은 graph db”를 고르는 문제가 아니라, 내 workload에 맞는 운영 후보와 테스트 후보를 나누는 문제입니다. 테스트는 TinkerGraph로 빠르게 돌리고, 운영 후보는 Neo4j나 Memgraph로 검증합니다. 조직이 PostgreSQL 표준 운영에 강하게 묶여 있으면 AGE를 고려할 수 있습니다. FalkorDB는 Redis 기반 단순 graph workload에 매력적이지만, edge-heavy write에서는 반드시 직접 측정해야 합니다. 여기서 감으로 고르면 나중에 benchmark가 친절하게 혼내 줍니다.

Neo4j, Memgraph, Apache AGE, FalkorDB를 이야기하면 자연스럽게 Cypher가 나옵니다. Cypher는 graph pattern을 문자열로 표현하는 query language입니다. SQL이 table과 row를 기준으로 묻는다면, Cypher는 vertex와 edge가 만드는 관계 모양을 그려서 묻습니다.

MATCH (alice:Person {email: $email})-[:KNOWS]->(friend:Person)
RETURN friend.name

이 query는 Person vertex에서 시작해 KNOWS edge를 따라가고, 연결된 Person을 반환합니다. 코드로 복잡한 join과 재귀 탐색을 직접 조립하기보다, “이런 관계 모양을 찾아줘”라고 database에 말하는 방식입니다.

그래서 bluetape4k-graph의 공통 API가 Cypher를 없애지는 않습니다. 없애려고 하면 결국 덜 좋은 Cypher를 하나 더 만드는 셈입니다. 서비스의 기본 CRUD, batch write, traversal, 테스트 fixture는 GraphOperations로 단순하게 시작합니다. 반대로 운영에서 query planner, index, projection까지 세밀하게 잡아야 하는 query는 Neo4j/Memgraph/AGE/FalkorDB가 제공하는 native Cypher를 그대로 쓰는 편이 맞습니다. 공통 API는 native query를 막는 벽이 아니라, 반복되는 서비스 코드를 줄이는 기준점입니다.

bluetape4k-graph module layers from examples and framework adapters to graph-core, graph I/O, and graph storage adapters
graph-core를 중심에 두고 graph storage adapter, graph I/O, Spring Boot/Ktor integration이 붙습니다.
모듈역할
graph-coreGraphOperations, schema DSL, traversal, algorithm, transaction/merge 기반 기능
graph-age, graph-neo4j, graph-memgraph, graph-tinkerpop, graph-falkordbgraph storage별 구현
graph-io/*CSV, NDJSON, GraphML bulk import/export와 OkIO 기반 I/O 조합
spring-boot/graph-spring-bootSpring Boot 4 auto-configuration
ktor/graph-ktorKtor plugin 기반 graph lifecycle
examples/*code graph, fraud detection, IAM, recommendation, supply-chain 등 실행 가능한 scenario

처음부터 모든 graph db 차이를 추상화 뒤에 숨기겠다는 접근은 위험합니다. 그래프 workload는 query planner, index, traversal 방식, transaction 의미의 차이가 큽니다. 그래서 bluetape4k-graph는 공통 CRUD/traversal은 묶지만, 선택한 graph storage가 안전하게 지원하지 않는 기능은 조용히 성공한 척하지 않습니다. “되는 줄 알았는데 운영 DB에는 아무것도 안 만들어져 있더라”는 종류의 놀라움은 가능한 한 테스트 단계에서 끝내야 합니다.

테스트나 문서 예제는 TinkerGraph가 가장 편합니다. Docker 없이 graph를 만들고, vertex/edge를 넣고, neighbor를 검증할 수 있습니다.

val ops: GraphOperations = TinkerGraphOperations()
ops.createGraph("demo")
val alice = ops.createVertex("Person", mapOf("name" to "Alice"))
val bob = ops.createVertex("Person", mapOf("name" to "Bob"))
ops.createEdge(alice.id, bob.id, "KNOWS", mapOf("since" to 2026))
val neighbors = ops.neighbors(alice.id, NeighborOptions(edgeLabel = "KNOWS"))

이 정도 예제가 단순해 보여도 의미가 있습니다. 서비스 코드는 GraphOperations로 시작하고, 실제 운영 후보는 같은 테스트를 Neo4j, Memgraph, AGE, FalkorDB로 옮겨가며 측정할 수 있습니다.

제가 기본값을 하나만 고른다면 production은 Neo4j, 테스트는 TinkerGraph입니다. 낮은 latency와 write-heavy workload를 진지하게 검증한다면 Memgraph를 후보에 넣습니다. PostgreSQL 중심으로 운영 복잡도를 줄여야 한다면 AGE가 현실적인 선택이 될 수 있습니다. Redis 기반 graph service가 이미 운영 모델과 잘 맞는다면 FalkorDB도 검토할 수 있지만, edge-heavy write는 반드시 benchmark로 확인해야 합니다.

다음 글에서는 graph-core의 API, schema DSL, transaction/merge, sync/virtual-thread/coroutine 실행 모델을 보겠습니다.

댓글

GitHub 계정으로 의견을 남기거나 reaction을 남길 수 있습니다.