상세 컨텐츠

본문 제목

Apache AGE ( Graph DB ) 그래프 DB에 대해서

Internet Tech/AI

by 누한 2026. 2. 26. 00:48

본문

🕸️ Apache AGE 완전 가이드 — 그래프 DB, 이제 PostgreSQL에서 활용하세요!

"관계형 DB는 알겠는데, 그래프 DB는 또 뭐야?" 라고 생각하고 계신다면, 이 글이 딱 맞습니다.
Apache AGE를 처음 접하는 학생, 비전공자, 그리고 DB를 조금 다뤄보신 분들을 위해 쉽고 친절하게 풀어보겠습니다.


목차

  1. Apache AGE 란?
  2. 그래프 DB, 왜 필요할까?
  3. 활용 유즈케이스
  4. Apache AGE의 미래
  5. 설치 및 구성 가이드
  6. 기본 사용법 — Cypher 쿼리 입문
  7. 마무리

1. Apache AGE 란?

한 줄 소개

Apache AGEPostgreSQL 위에서 그래프 데이터베이스 기능을 사용할 수 있게 해 주는 오픈소스 확장 플러그인입니다.

  • AGE = A Graph Extension
  • PostgreSQL(전통 관계형 DB) + 그래프 DB 기능 = Apache AGE 🎉
  • Apache Software Foundation이 공식 Top-Level 프로젝트로 관리 중입니다.

 

PostgreSQL이란?

PostgreSQL(줄여서 Postgres)은 세계에서 가장 많이 사용되는 오픈소스 관계형 데이터베이스 중 하나입니다. 우리가 흔히 아는 표(테이블) 형태로 데이터를 저장하고 SQL로 조회하는 바로 그 DB입니다.

 

그럼 그래프 DB는?

그래프 DB는 노드(Node, 점)엣지(Edge, 연결선)로 데이터를 표현하는 방식입니다.

예를 들어 SNS에서 이렇게 표현할 수 있습니다:

  • 노드: 사람 (철수, 영희, 민수)
  • 엣지: 친구 관계 (철수 ↔ 영희, 영희 ↔ 민수)

"철수와 2단계 이내로 연결된 모든 사람을 찾아줘!" 같은 질문은 기존 SQL로는 꽤 복잡하지만, 그래프 DB에서는 아주 자연스럽게 표현할 수 있습니다.

Apache AGE의 핵심 강점

특징 설명
멀티모델 지원 기존 테이블(관계형)과 그래프를 한 DB에서 동시에 사용 가능
Cypher 쿼리 지원 그래프 쿼리 표준 언어인 Cypher 사용 가능
PostgreSQL 생태계 기존 Postgres 도구, 드라이버, 인프라를 그대로 활용 가능
오픈소스 Apache License 2.0, 무료 사용 가능
ACID 보장 데이터 일관성, 무결성 보장 (PostgreSQL의 트랜잭션 보장을 그대로 상속)

⚠️ 참고: Apache AGE는 PostgreSQL의 확장(Extension) 입니다. 독립적인 그래프 DB(Neo4j 등)가 아니라 PostgreSQL 위에 그래프 기능을 더하는 플러그인 형태라는 점을 이해하고 사용하시면 좋습니다.


 

2. 그래프 DB, 왜 필요할까?

🤔 기존 SQL의 한계

친구 추천 기능을 만든다고 생각해 보겠습니다. "나의 친구의 친구를 추천해줘!"를 SQL로 표현하면 이렇게 됩니다:

-- 기존 SQL 방식 — 2단계 연결도 이렇게 복잡합니다
SELECT DISTINCT f2.friend_id
FROM friends f1
JOIN friends f2 ON f1.friend_id = f2.user_id
WHERE f1.user_id = 1
  AND f2.friend_id != 1
  AND f2.friend_id NOT IN (
    SELECT friend_id FROM friends WHERE user_id = 1
  );

이것을 4단계, 5단계로 늘리면 JOIN이 기하급수적으로 증가하고, 성능도 크게 저하됩니다.

✅ 그래프 쿼리(Cypher)로는 이렇게 됩니다

-- Apache AGE의 Cypher 방식 — 직관적이고 깔끔합니다!
MATCH (me:Person {id: 1})-[:FRIEND*2]->(recommended:Person)
WHERE NOT (me)-[:FRIEND]->(recommended)
RETURN recommended;

*2만 숫자로 바꾸면 몇 단계든 표현이 가능합니다. 코드가 훨씬 읽기 쉽고 깊이 탐색 시 성능도 유리합니다.

그래프로 표현하면 더 자연스러운 데이터들

  • 소셜 네트워크의 팔로우/친구 관계
  • 물류의 배송 경로 (A도시 → B도시 → C도시)
  • 사기 탐지 (이 계좌와 저 계좌, 어디서 연결되어 있는지?)
  • 지식 그래프 (AI가 "지식"을 저장하는 방식)
  • 조직도 (CEO → 부장 → 팀장 → 직원)

 

3. 활용 유즈케이스

🔍 유즈케이스 1: 소셜 네트워크 분석

인스타그램, 링크드인 같은 서비스에서 "당신이 알 수도 있는 사람"을 추천하는 기능입니다.

-- 공통 친구가 많은 사람을 추천합니다
SELECT * FROM cypher('social_graph', $$
  MATCH (me:User {name: '김철수'})-[:FRIEND]->(mutual:User)<-[:FRIEND]-(recommend:User)
  WHERE NOT (me)-[:FRIEND]->(recommend) AND me <> recommend
  RETURN recommend.name AS 추천인, COUNT(mutual) AS 공통친구수
  ORDER BY 공통친구수 DESC
  LIMIT 5
$$) AS (추천인 agtype, 공통친구수 agtype);

위 쿼리를 해석하면 이렇습니다: "나(김철수)의 친구이면서, 아직 나랑 친구가 아닌 사람 중에서 공통 친구가 많은 순서대로 5명을 추천해 주세요." 꽤 직관적이지 않나요?

🛡️ 유즈케이스 2: 금융 사기 탐지 (Fraud Detection)

은행에서 이상한 거래 패턴을 찾는 용도로 그래프 DB가 매우 많이 활용됩니다.

-- 동일한 IP에서 3개 이상의 계좌가 거래한 경우를 탐지합니다
SELECT * FROM cypher('finance_graph', $$
  MATCH (account:Account)-[:USED_IP]->(ip:IP)<-[:USED_IP]-(other:Account)
  WITH ip, COLLECT(DISTINCT account) AS accounts
  WHERE SIZE(accounts) >= 3
  RETURN ip.address AS 의심IP,
         SIZE(accounts) AS 연결계좌수,
         [a IN accounts | a.account_number] AS 계좌목록
$$) AS (의심IP agtype, 연결계좌수 agtype, 계좌목록 agtype);

여러 계좌가 같은 IP를 공유하면 의심스러운 패턴입니다. 그래프 구조로 보면 이러한 연결 관계가 한눈에 들어옵니다.

🗺️ 유즈케이스 3: 지식 그래프 (Knowledge Graph)

AI 챗봇이나 검색 엔진이 "지식"을 저장하고 탐색하는 방식에 활용됩니다.

예를 들어 "서울이 수도인 나라의 대통령은?"이라는 질문을 처리하려면:

  • 서울 → [수도of] → 대한민국
  • 대한민국 → [대통령] → (현재 대통령 정보)
-- 지식 그래프 탐색 예시
SELECT * FROM cypher('knowledge_graph', $$
  MATCH (city:City {name: '서울'})-[:CAPITAL_OF]->(country:Country)
        -[:HAS_PRESIDENT]->(president:Person)
  RETURN city.name AS 도시, country.name AS 나라, president.name AS 대통령
$$) AS (도시 agtype, 나라 agtype, 대통령 agtype);

🚚 유즈케이스 4: 물류 및 경로 탐색

택배가 서울에서 부산까지 어떤 경로가 가장 효율적인지 탐색할 수 있습니다.

-- 최단 경로 탐색 예시
-- 참고: Apache AGE의 shortestPath는 방향 없는 관계(-[*]-)를 사용해야 합니다
SELECT * FROM cypher('logistics_graph', $$
  MATCH path = shortestPath(
    (start:Hub {name: '서울'})-[*]-(end:Hub {name: '부산'})
  )
  RETURN [node IN nodes(path) | node.name] AS 경로,
         LENGTH(path) AS 경유지수
$$) AS (경로 agtype, 경유지수 agtype);

⚠️ 주의: Apache AGE의 shortestPath 함수는 현재 방향이 없는 관계(-[*]-)만 지원합니다. Neo4j에서처럼 단방향(-[*]->)으로 작성하면 오류가 발생할 수 있으므로 주의가 필요합니다.

🏥 유즈케이스 5: 의료 데이터 (질병-약물 관계)

약의 부작용, 질병 간 연관성을 분석하는 데도 매우 유용합니다.

-- 특정 약물과 관련된 부작용을 탐색합니다
SELECT * FROM cypher('medical_graph', $$
  MATCH (drug:Drug {name: '아스피린'})-[:CAUSES]->(effect:SideEffect)
        -[:CONTRAINDICATED_WITH]->(condition:Condition)
  RETURN drug.name AS 약품, effect.name AS 부작용, condition.name AS 주의질환
$$) AS (약품 agtype, 부작용 agtype, 주의질환 agtype);

💡 유즈케이스 6: 추천 시스템 (e-커머스)

"이 상품을 구매한 고객은 이런 상품도 구매했어요!" 기능 구현에 활용됩니다.

-- 협업 필터링 기반 상품 추천
SELECT * FROM cypher('ecommerce_graph', $$
  MATCH (user:User {id: 'U001'})-[:PURCHASED]->(item:Product)
        <-[:PURCHASED]-(similar_user:User)-[:PURCHASED]->(recommend:Product)
  WHERE NOT (user)-[:PURCHASED]->(recommend)
  RETURN recommend.name AS 추천상품,
         COUNT(similar_user) AS 추천점수
  ORDER BY 추천점수 DESC
  LIMIT 10
$$) AS (추천상품 agtype, 추천점수 agtype);

 

4. Apache AGE의 미래

📈 현재 위치와 성장 가능성

Apache AGE는 AGENS Graph(바이트소프트 개발)를 기반으로 2020년 Apache Incubator에 입문하여, 2022년에 정식 Apache Top-Level Project로 승격되었습니다. 2024년 기준 최신 버전은 v1.6.0이며, PostgreSQL 11 ~ 17 버전까지 지원합니다.

전문 그래프 DB(Neo4j 등)에 비해 아직 성숙 단계이지만, 성장 속도가 빠르고 커뮤니티도 활발하게 발전하고 있습니다.

🔮 주목할 트렌드들

① AI + 그래프 DB의 만남

요즘 가장 주목받는 키워드인 RAG(Retrieval-Augmented Generation)GraphRAG가 있습니다. GPT 같은 AI 모델이 단순히 학습 데이터만 활용하는 것이 아니라, 실시간으로 그래프 형태의 지식 베이스를 검색하면서 더 정확한 답변을 생성하는 기술입니다. Apache AGE는 이 구조에 매우 잘 맞는 플랫폼입니다.

실제로 Apache AGE 공식 사이트에서도 LangChain GraphStore 통합 사례를 소개하고 있을 만큼, AI 파이프라인과의 연계가 적극적으로 추진되고 있습니다.

사용자 질문 → AI 모델 → Apache AGE 그래프 검색 → 관련 지식 추출 → 정확한 답변

② 멀티모델 DB의 부상

"그래프도 쓰고 싶고, 관계형도 쓰고 싶은데 DB를 두 개 운영하기는 싫다"는 요구가 점점 커지고 있습니다. Apache AGE는 PostgreSQL 위에 있으므로 이 두 가지 요구를 한 번에 충족할 수 있습니다.

③ openCypher 표준화

Cypher 쿼리 언어가 업계 표준으로 자리잡고 있습니다. Neo4j, Amazon Neptune, Apache AGE 등 여러 플랫폼이 Cypher를 지원하면서, 한번 배워두면 여러 시스템에서 활용할 수 있는 이식성이 높아지고 있습니다.

④ GQL (ISO 표준 그래프 쿼리 언어)

2024년 ISO에서 공식적으로 GQL(Graph Query Language) 표준이 확정되었습니다. SQL처럼 그래프 DB에도 국제 표준이 생긴 것입니다. Apache AGE도 이 표준 방향에 맞춰 발전 중입니다.

⑤ pgvector 연동 (벡터 검색)

Apache AGE 커뮤니티에서는 pgvector 확장과의 연동 제안이 활발하게 논의되고 있습니다. 그래프 탐색과 벡터 유사도 검색을 결합하면 AI 검색 파이프라인에서 강력한 시너지를 낼 수 있습니다.

🗓️ 로드맵 방향 (커뮤니티 기반)

  • PostgreSQL 최신 버전 지원 지속 확대 (현재 PG17까지 지원)
  • 성능 최적화 (대용량 그래프 처리)
  • pgvector 등 외부 확장(Extension)과의 연동 지원 강화
  • 더 많은 그래프 알고리즘 내장 지원 (PageRank, 커뮤니티 탐지 등)
  • GQL 표준 호환성 향상

⚠️ 현실적인 이야기

물론 아직 주의할 점도 있습니다:

  • 전문 그래프 DB(Neo4j, Amazon Neptune)에 비해 대규모 그래프 처리 성능은 아직 차이가 있을 수 있습니다.
  • 커뮤니티가 성장 중이라 일부 기능의 문서화가 아직 부족한 부분이 있습니다.
  • 내장 그래프 알고리즘 라이브러리가 전문 그래프 DB에 비해 제한적입니다.
  • shortestPath 등 일부 Cypher 기능이 Neo4j와 완전히 동일하게 구현되지 않은 부분이 있으므로, 사용 전 공식 문서 확인을 권장합니다.

그러나 "이미 PostgreSQL을 사용 중이고 그래프 기능을 추가하고 싶다"는 상황이라면 Apache AGE는 매우 합리적인 선택입니다. 별도의 그래프 DB 서버를 운영할 필요가 없기 때문입니다.


 

5. 설치 및 구성 가이드

🐳 방법 1: Docker로 초간단 설치 (강력 추천!)

Docker가 설치되어 있다면 이 방법이 가장 쉽습니다.

# Apache AGE 공식 Docker 이미지를 실행합니다
docker run \
    --name myPostgresDb \
    -p 5455:5432 \
    -e POSTGRES_USER=postgresUser \
    -e POSTGRES_PASSWORD=postgresPW \
    -e POSTGRES_DB=postgresDB \
    -d \
    apache/age

각 옵션 설명
-name: 컨테이너에 이름을 부여합니다.
-p 5455:5432: 로컬 포트 5455를 컨테이너 내부 포트 5432에 연결합니다.
-e POSTGRES_USER: PostgreSQL 접속 사용자명을 설정합니다.
-e POSTGRES_PASSWORD: 접속 비밀번호를 설정합니다.
-e POSTGRES_DB: 기본 데이터베이스 이름을 설정합니다.
-d: 백그라운드 모드로 실행합니다.



실행 확인:

docker ps
# myPostgresDb 컨테이너가 Up 상태이면 성공입니다!

컨테이너 접속:

docker exec -it myPostgresDb psql -U postgresUser -d postgresDB

⚠️ 중요: AGE 확장 활성화 방법

Docker 이미지에는 AGE 확장이 미리 설치되어 있지만, 사용하려는 데이터베이스에 처음 한 번은 CREATE EXTENSION을 실행해야 하며, 이후 접속할 때마다 LOADSET search_path를 실행해야 합니다.

-- [최초 1회] AGE 확장을 데이터베이스에 등록합니다
CREATE EXTENSION IF NOT EXISTS age;

-- [매 세션마다] AGE 라이브러리를 현재 세션에 로드합니다
LOAD 'age';

-- [매 세션마다] AGE 스키마를 검색 경로에 추가합니다
SET search_path = ag_catalog, "$user", public;

-- 확인: 그래프 목록이 나오면 정상입니다 (처음엔 비어있어도 OK)
SELECT * FROM ag_catalog.ag_graph;

💡 : 매번 세션 초기화 명령을 입력하기 번거롭다면, .psqlrc 파일에 위 LOAD/SET 명령들을 저장해 두거나, 애플리케이션 연결 시 초기화 쿼리로 자동 실행하도록 설정할 수 있습니다.


🛠️ 방법 2: 소스에서 직접 빌드 (Linux / Mac)

사전 요구사항 확인

Apache AGE는 PostgreSQL 11 ~ 17 버전을 지원합니다(v1.6.0 기준). 먼저 호환 버전의 PostgreSQL이 설치되어 있어야 합니다.

# PostgreSQL 버전 확인
psql --version

# 빌드 도구 설치 (Ubuntu/Debian 기준)
sudo apt-get install build-essential libreadline-dev zlib1g-dev flex bison

# CentOS/RHEL 기준
# yum install gcc glibc glib-common readline readline-devel zlib zlib-devel flex bison

# PostgreSQL 서버 개발 라이브러리 설치 (Ubuntu 예시, 버전에 맞게 조정)
sudo apt install postgresql-server-dev-all

Apache AGE 소스 다운로드 및 빌드

# 1. GitHub에서 소스를 클론합니다
git clone https://github.com/apache/age.git
cd age

# 2. PostgreSQL의 pg_config 경로를 확인합니다
pg_config --version

# 3. 빌드 및 설치합니다
make
sudo make install

# pg_config 경로가 PATH에 없다면 명시적으로 지정합니다
# make PG_CONFIG=/usr/lib/postgresql/15/bin/pg_config install

PostgreSQL 설정 파일 수정

# postgresql.conf 파일 위치를 확인합니다
psql -U postgres -c "SHOW config_file;"

# 파일을 편집하여 다음 내용을 추가하거나 수정합니다
# shared_preload_libraries = 'age'
sudo nano /etc/postgresql/15/main/postgresql.conf

PostgreSQL 재시작:

sudo systemctl restart postgresql

데이터베이스에서 확장 활성화:

-- postgres(슈퍼유저)로 접속 후 실행합니다
CREATE EXTENSION age;
LOAD 'age';
SET search_path = ag_catalog, "$user", public;

🪟 방법 3: Windows (WSL2 또는 Docker 권장)

Windows 환경에서는 두 가지 방법을 권장합니다.

옵션 A: Docker Desktop for Windows (가장 간편)

Docker Desktop을 설치한 후 방법 1의 Docker 명령어를 그대로 사용합니다.

옵션 B: WSL2 (Windows Subsystem for Linux 2)

# PowerShell(관리자 권한)에서 WSL2를 설치합니다
wsl --install

# WSL2 Ubuntu 실행 후 방법 2(소스 빌드)를 동일하게 진행합니다

🔗 방법 4: Python에서 Apache AGE 연결하기

실제 애플리케이션 개발 시 Python에서 Apache AGE를 연결하는 방법입니다.

패키지 설치

Apache AGE의 공식 Python 드라이버는 age 패키지입니다. psycopg2와 antlr4(결과 파싱용)를 함께 사용합니다.

# psycopg2 설치 (PostgreSQL Python 드라이버)
pip install psycopg2-binary

# antlr4 설치 (AGE 결과 파싱에 필요, 반드시 4.11.1 버전 사용)
pip install antlr4-python3-runtime==4.11.1

# Apache AGE Python 드라이버 설치
pip install apache-age-python

⚠️ 중요: antlr4-python3-runtime의 버전은 반드시 4.11.1을 사용해야 합니다. 다른 버전을 사용하면 파싱 오류가 발생할 수 있습니다.

기본 연결 예시

import age

# Apache AGE Python 드라이버로 연결합니다
connection_string = (
    "host=localhost "
    "port=5455 "
    "dbname=postgresDB "
    "user=postgresUser "
    "password=postgresPW"
)

# 연결 및 그래프 초기화
ag = age.connect(connection_string, graph="my_graph")

try:
    # 노드 생성
    ag.execCypher(
        "CREATE (p:Person {name: %s, age: %s})",
        params=('김철수', 30)
    )
    ag.commit()

    # 데이터 조회
    cursor = ag.execCypher(
        "MATCH (p:Person) RETURN p.name, p.age"
    )

    for row in cursor:
        # agtype은 문자열로 반환되므로 필요 시 타입 변환을 합니다
        print(f"이름: {row[0]}, 나이: {row[1]}")

except Exception as e:
    print(f"오류 발생: {e}")
    ag.rollback()
finally:
    ag.close()

💡 참고: agtype 값은 내부적으로 문자열 형태로 반환됩니다. 숫자나 복잡한 객체를 사용할 때는 타입 변환에 주의가 필요합니다.


 

6. 기본 사용법 — Cypher 쿼리 입문

🌱 그래프 생성 및 기본 CRUD

그래프 생성

-- 새 그래프를 생성합니다
SELECT create_graph('my_social_network');

노드(Node) 만들기 — CREATE

-- 사람 노드를 하나 추가합니다
SELECT * FROM cypher('my_social_network', $$
  CREATE (p:Person {name: '김철수', age: 28, city: '서울'})
  RETURN p
$$) AS (p agtype);

-- 여러 노드를 한 번에 추가합니다
-- (반환값이 없을 때도 AS 절은 반드시 작성해야 합니다)
SELECT * FROM cypher('my_social_network', $$
  CREATE (:Person {name: '이영희', age: 25, city: '부산'}),
         (:Person {name: '박민수', age: 32, city: '인천'}),
         (:Person {name: '최지연', age: 27, city: '서울'})
$$) AS (v agtype);

관계(Edge) 만들기 — MATCH + CREATE

-- 철수와 영희가 친구 관계임을 나타냅니다
SELECT * FROM cypher('my_social_network', $$
  MATCH (a:Person {name: '김철수'}), (b:Person {name: '이영희'})
  CREATE (a)-[:FRIEND {since: '2023-01-15'}]->(b)
  RETURN a.name AS 사람A, b.name AS 사람B
$$) AS (사람A agtype, 사람B agtype);

-- 철수가 민수를 팔로우합니다
SELECT * FROM cypher('my_social_network', $$
  MATCH (a:Person {name: '김철수'}), (b:Person {name: '박민수'})
  CREATE (a)-[:FOLLOWS]->(b)
  RETURN a.name AS 팔로워, b.name AS 팔로잉
$$) AS (팔로워 agtype, 팔로잉 agtype);

데이터 조회 — MATCH

-- 모든 사람을 조회합니다
SELECT * FROM cypher('my_social_network', $$
  MATCH (p:Person)
  RETURN p.name AS 이름, p.age AS 나이, p.city AS 도시
$$) AS (이름 agtype, 나이 agtype, 도시 agtype);

-- 서울에 사는 사람만 조회합니다
SELECT * FROM cypher('my_social_network', $$
  MATCH (p:Person {city: '서울'})
  RETURN p.name AS 이름, p.age AS 나이
$$) AS (이름 agtype, 나이 agtype);

-- 철수의 친구들을 찾습니다 (양방향 관계 탐색)
SELECT * FROM cypher('my_social_network', $$
  MATCH (me:Person {name: '김철수'})-[:FRIEND]-(friend:Person)
  RETURN friend.name AS 친구이름
$$) AS (친구이름 agtype);

데이터 수정 — SET

-- 나이와 직업 정보를 업데이트합니다
SELECT * FROM cypher('my_social_network', $$
  MATCH (p:Person {name: '김철수'})
  SET p.age = 29, p.job = '개발자'
  RETURN p
$$) AS (p agtype);

데이터 삭제 — DELETE / DETACH DELETE

-- 노드만 삭제합니다 (연결된 관계가 없어야 삭제 가능합니다)
SELECT * FROM cypher('my_social_network', $$
  MATCH (p:Person {name: '삭제할사람'})
  DELETE p
$$) AS (v agtype);

-- 노드와 연결된 모든 관계를 함께 삭제합니다 (권장 방법)
SELECT * FROM cypher('my_social_network', $$
  MATCH (p:Person {name: '삭제할사람'})
  DETACH DELETE p
$$) AS (v agtype);

🔗 실전 예제: 소셜 네트워크 구축

-- 1. 그래프를 생성합니다
SELECT create_graph('sns_demo');

-- 2. 사용자 노드를 생성합니다
SELECT * FROM cypher('sns_demo', $$
  CREATE (:User {id: 1, name: '김개발', interest: ['Python', 'AI']}),
         (:User {id: 2, name: '이디자인', interest: ['Figma', 'UX']}),
         (:User {id: 3, name: '박데이터', interest: ['SQL', 'AI']}),
         (:User {id: 4, name: '최마케팅', interest: ['SEO', 'SNS']}),
         (:User {id: 5, name: '정PM', interest: ['Jira', 'Notion']})
$$) AS (v agtype);

-- 3. 팔로우 관계를 각각 설정합니다
SELECT * FROM cypher('sns_demo', $$
  MATCH (a:User {id: 1}), (b:User {id: 2}) CREATE (a)-[:FOLLOWS]->(b)
$$) AS (v agtype);

SELECT * FROM cypher('sns_demo', $$
  MATCH (a:User {id: 1}), (b:User {id: 3}) CREATE (a)-[:FOLLOWS]->(b)
$$) AS (v agtype);

SELECT * FROM cypher('sns_demo', $$
  MATCH (a:User {id: 2}), (b:User {id: 4}) CREATE (a)-[:FOLLOWS]->(b)
$$) AS (v agtype);

SELECT * FROM cypher('sns_demo', $$
  MATCH (a:User {id: 3}), (b:User {id: 4}) CREATE (a)-[:FOLLOWS]->(b)
$$) AS (v agtype);

SELECT * FROM cypher('sns_demo', $$
  MATCH (a:User {id: 4}), (b:User {id: 5}) CREATE (a)-[:FOLLOWS]->(b)
$$) AS (v agtype);

-- 4. 1~2단계 이내로 도달 가능한 사용자를 조회합니다
SELECT * FROM cypher('sns_demo', $$
  MATCH (me:User {id: 1})-[:FOLLOWS*1..2]->(reach:User)
  WHERE me <> reach
  RETURN DISTINCT reach.name AS 도달가능한사용자
$$) AS (도달가능한사용자 agtype);

-- 결과: 김개발이 직간접적으로 팔로우할 수 있는 모든 사용자 목록이 출력됩니다

🔀 PostgreSQL 테이블과 그래프를 함께 사용하기 (킬러 피처!)

Apache AGE의 진짜 매력은 기존 관계형 테이블 데이터와 그래프 데이터를 함께 조인(JOIN)할 수 있다는 점입니다.

-- 1. 기존 관계형 테이블을 생성합니다
CREATE TABLE user_orders (
    user_id      INTEGER,
    product_name VARCHAR(100),
    amount       INTEGER,
    order_date   DATE
);

INSERT INTO user_orders VALUES
  (1, 'Python 책', 25000, '2024-01-15'),
  (1, '키보드',   89000, '2024-02-20'),
  (3, 'Python 책', 25000, '2024-01-20');

-- 2. 그래프 데이터와 관계형 데이터를 함께 조회합니다
--    (김개발이 팔로우하는 사람들의 구매 내역 조회)
SELECT
    graph_result.팔로잉이름,
    uo.product_name AS 구매상품,
    uo.amount       AS 금액
FROM
    (
        SELECT *
        FROM cypher('sns_demo', $$
            MATCH (me:User {id: 1})-[:FOLLOWS]->(following:User)
            RETURN following.id AS user_id, following.name AS 팔로잉이름
        $$) AS (user_id agtype, 팔로잉이름 agtype)
    ) AS graph_result
    JOIN user_orders uo
      ON uo.user_id = (graph_result.user_id::text)::integer;

💡 핵심 포인트: graph_result.user_idagtype 타입이므로, 일반 정수형 컬럼과 JOIN하려면 ::text::integer와 같이 타입 변환이 필요합니다. 이 부분은 처음 사용 시 자주 만나는 포인트이므로 잘 기억해 두세요.


7. 마무리

Apache AGE는 "이미 PostgreSQL을 사용 중인데 그래프 기능이 필요하다"는 상황에서 매우 합리적인 선택입니다. 별도의 그래프 DB 서버를 운영하지 않아도 되고, 기존 SQL과 그래프 쿼리를 자유롭게 함께 사용할 수 있습니다.

이런 분들께 Apache AGE를 추천합니다:

  • PostgreSQL을 이미 사용 중인 팀
  • SNS, 추천 시스템, 사기 탐지 기능이 필요한 개발자
  • AI/ML 파이프라인에 지식 그래프를 통합하고 싶은 분
  • 그래프 DB를 처음 배워보고 싶은 학생 및 개발 입문자

이런 경우에는 다른 솔루션도 고려해 보세요:

  • 그래프 기능이 서비스의 핵심이고 초대형 규모 처리가 필요하다면 → Neo4j, Amazon Neptune
  • 완전 관리형(Fully Managed) 클라우드 서비스가 필요하다면 → Amazon Neptune, Azure Cosmos DB (Gremlin API)

 


🔗 참고 자료 및 유용한 링크

리소스 링크
Apache AGE 공식 사이트 https://age.apache.org/
GitHub 저장소 https://github.com/apache/age
공식 문서 (Setup) https://age.apache.org/age-manual/master/intro/setup.html
공식 문서 (Cypher 쿼리) https://age.apache.org/age-manual/master/intro/cypher.html
Docker Hub https://hub.docker.com/r/apache/age
openCypher 언어 레퍼런스 https://opencypher.org/
AGE Viewer (그래프 시각화 도구) https://github.com/apache/age-viewer
LangChain GraphStore 연동 PR https://github.com/langchain-ai/langchain/pull/20582

이 문서는 Apache AGE 공식 문서(v1.6.0 기준) 를 기준으로 작성됐으나 오입력 및 잘못된 정보를 포함할 수도 있습니다. 원본 문서를 확인하세요.

관련글 더보기