LeanX

Cypher 기초: 그래프 DB의 CRUD

Cypher는 그래프 DB의 SQL로, 노드는 소괄호·관계는 대괄호로 표현하며 CREATE/MATCH/WHERE와 *1..5 다단계 탐색으로 노드·관계를 생성·조회·필터링할 수 있습니다.

지난 레슨에서 GraphCypherQAChain이 자연어 질문을 Cypher 쿼리로 '번역'해준다고 했습니다. AI가 대신 써주니 편하지만, 그 결과가 맞는지 읽고 필요하면 직접 수정·삭제하려면 최소한 CRUD(생성·조회·수정·삭제) 수준의 Cypher는 손에 익혀두는 게 좋습니다. 이번 레슨에서 그 기초 문법을 앞 레슨의 김민수·보안 팀 예제로 익혀봅니다.

SQL vs Cypher — 무엇을 다루나

SQL (관계형 DB)

  • 테이블·행·열에 데이터를 저장
  • SELECT ... FROM ... WHERE 로 조회
  • 관계는 그때그때 JOIN으로 계산
  • 정형 데이터·집계 질문에 강함

행과 열의 세계

Cypher (그래프 DB)

  • 노드·관계·속성에 데이터를 저장
  • MATCH 패턴으로 노드와 관계를 조회
  • 관계가 이미 저장돼 있어 탐색이 빠름
  • 연결·경로·다중 홉 질문에 강함

점과 선의 세계

Cypher는 '그래프 DB의 SQL'입니다. SQL을 안다면 절반은 아는 셈이고, 결정적인 차이는 '관계를 문법으로 직접 그린다'는 점뿐입니다.

노드는 소괄호 (), 관계는 대괄호 []

Cypher 문법의 뼈대는 딱 두 가지입니다. 개체(노드)는 소괄호로 감싸고, 그 사이를 잇는 관계는 대괄호로 감쌉니다. 속성은 중괄호에 담고, 화살표로 관계의 방향을 나타냅니다.

문법 요소표기설명
노드(변수:라벨 {속성})예: (kim:Person {name: '김민수'}) — 소괄호로 감싼다
관계-[변수:관계]->예: -[r:RESPONSIBLE_FOR]-> — 대괄호로 감싼다
속성{키: 값}예: {name: '김민수', age: 34} — Cypher는 키에 따옴표를 쓰지 않는다
방향-> 또는 <-관계의 방향을 표시하며, 생략하면 방향 무관 탐색

CREATE — 생성

CREATE로 노드를 만듭니다. 라벨(Person)과 속성({...})을 함께 지정하고, RETURN으로 만든 결과를 돌려받아 눈으로 확인할 수 있습니다.

단일 노드 만들기

CREATE (kim:Person {name: '김민수', age: 34})
RETURN kim;

여러 노드 한 번에 (콤마로 구분, 마지막엔 콤마 없음)

CREATE (kim:Person {name: '김민수', age: 34}),
       (park:Person {name: '박지현', age: 29}),
       (lee:Person {name: '이상', age: 41})
RETURN kim, park, lee;

관계 만들기 — 먼저 MATCH로 기존 노드를 찾아 잇는다

MATCH (kim:Person {name: '김민수'})
CREATE (kim)-[r:RESPONSIBLE_FOR]->(refactor:Project {name: '결제 시스템 리팩터링', status: '진행 중'})
RETURN kim, r, refactor;

관계 이름은 대문자·스네이크 케이스(RESPONSIBLE_FOR, NEXT_CHUNK)로 쓰는 것이 Neo4j 관례입니다. 헷갈릴 땐 '노드는 소괄호, 관계는 대괄호'만 기억하세요 — 이 한 줄이 Cypher 문법의 절반입니다.

MATCH — 조회(Read)

MATCH는 '이런 패턴에 맞는 것을 찾아줘'라고 그래프에 요청하는 구문입니다. SQL의 SELECT에 해당하며, 찾을 노드·관계의 모양을 그대로 그려 넣습니다.

전체 조회 — 관계 있는 것만 vs 모든 노드

// 관계로 이어진 모든 노드·관계
MATCH (n)-[r]->(m)
RETURN n, r, m;

// 관계가 없는 외톨이 노드까지 포함해 전체 노드
MATCH (n)
RETURN n;

라벨 필터 + 속성만 골라 출력

// Person 라벨 노드만
MATCH (p:Person)
RETURN p;

// 노드 전체가 아니라 특정 속성만
MATCH (p:Person)
RETURN p.name, p.age;

필터 두 가지 방법 — 인라인 vs WHERE

// 방법 1) 인라인 속성 매칭 (정확히 일치)
MATCH (p:Person {name: '이상'})
RETURN p;

// 방법 2) WHERE 절 — 부분 일치·비교가 가능해 더 유연
MATCH (p:Person)
WHERE p.name CONTAINS '이상'
RETURN p;

// 비교·논리 연산 조합 (AND / OR / NOT)
MATCH (p:Person)
WHERE p.age > 30 AND NOT p.name = '박지현'
RETURN p.name, p.age;

다단계 탐색(multi-hop) — GraphRAG의 핵심

"김민수와 보안 팀은 어떤 관계야?"는 한 번의 조회로 답이 나오지 않습니다. 김민수에서 출발해 여러 홉(hop)을 건너야 하죠. 김민수 →담당→ 결제 시스템 리팩터링 →포함→ 장애율 개선 프로젝트 →공동진행→ 보안 팀. 이 여정을 Cypher는 한 줄로 표현합니다.

김민수에서 보안 팀까지 — 여러 홉을 건너는 경로

  • 김민수 사람 (출발 노드)
  • 결제 시스템 리팩터링 담당 →
  • 장애율 개선 프로젝트 포함 →
  • 보안 팀 공동진행 →

이 경로를 사람이 일일이 JOIN으로 잇는 대신, Cypher의 *1..5 한 줄로 '알아서 따라가게' 하는 것이 그래프 탐색의 힘입니다.

가변 길이 경로 — *1..5

MATCH (start:Person {name: '김민수'})
MATCH (end:Team {name: '보안 팀'})
// *1..5 = 두 노드 사이 1~5홉 이내의 모든 경로
MATCH path = (start)-[*1..5]->(end)
RETURN path;
// 범위를 [*1..2]로 줄이면 4홉 경로가 안 잡혀 No records 가 뜬다

최단 경로 + 방향 무관 탐색

// 가능한 경로 중 가장 짧은 것 하나만
MATCH (start:Person {name: '김민수'}), (end:Team {name: '보안 팀'})
MATCH path = shortestPath((start)-[*1..5]->(end))
RETURN path;

// 화살표를 빼면 방향 무관 — 김민수와 3홉 내로 연결된 아무 노드나
MATCH (start:Person {name: '김민수'})-[*1..3]-(x)
RETURN DISTINCT x;

UPDATE·DELETE 맛보기

조회를 익혔으니 나머지 CRUD도 짧게 봅니다. 속성 수정은 SET, 삭제는 DELETE, 그리고 '있으면 재사용·없으면 생성'하는 MERGE가 파이프라인에서 특히 요긴합니다.

SET · MERGE · DETACH DELETE

// 속성 수정 (SET)
MATCH (p:Person {name: '김민수'})
SET p.age = 35
RETURN p;

// 있으면 재사용, 없으면 생성 — 중복 노드 방지
MERGE (p:Person {name: '김민수'})
RETURN p;

// DB의 모든 노드·관계 삭제 (학습용 초기화)
MATCH (n)
DETACH DELETE n;

MERGE는 중복 노드를 막아줍니다. 파이프라인에서 같은 개체('김민수')가 여러 청크에 등장해 여러 번 들어와도, MERGE를 쓰면 그래프에는 노드 하나로 유지됩니다. 다음 레슨에서 문서를 적재할 때 이 성질이 핵심 역할을 합니다.

MATCH (n) DETACH DELETE n; 은 DB의 모든 노드와 관계를 한 번에 지웁니다. 학습용 인스턴스를 깨끗이 초기화할 땐 편하지만, 운영 DB에서 실행하면 전체 데이터가 통째로 날아갑니다. 삭제 계열 쿼리는 실행 전에 반드시 대상 범위를 MATCH ... RETURN 으로 먼저 확인하세요.

이제 Cypher의 CRUD를 손에 넣었습니다. 다음 레슨에서는 지금까지의 개념·문법을 모두 동원해, 실제 PDF 문서 한 편을 로드하는 것부터 질문에 답하는 것까지 GraphRAG 파이프라인을 처음부터 끝까지 만들어봅니다.