Cypher (Neo4j) & Turtle (RDF/OWL) 완전 정복
아래 사이퍼 설명해줘. 이와 관련된 사이퍼 문법, 개발자 가이드를 마크다운 문서로 만들어 줄 수 있을까?
테스트 해볼 수 있는 환경도 알려주고.
turtle이 무엇인지도 궁금해.
// 수사관은 자신이 배정된 케이스의 데이터만 볼 수 있음
MATCH (u:User {id: $userId})-[:ASSIGNED_TO]->(case:Case)
<-[:PART_OF]-(entity)
RETURN entity
# 규칙 1: Suspect는 Person의 하위 클래스다
:Suspect rdfs:subClassOf :Person .
# 규칙 2: Investigator와 Suspect는 겹칠 수 없다
:Investigator owl:disjointWith :Suspect .
# 규칙 3: CriminalOrganization은 Organization의 하위 클래스다
:CriminalOrganization rdfs:subClassOf :Organization .
📌 코드 분석: 무엇이 섞여 있나?
질문에 제시된 코드는 두 가지 완전히 다른 기술이 혼재합니다.
| 구분 | 언어 | 목적 | 시스템 |
|---|
| 첫 번째 블록 | Cypher | 그래프 DB 데이터 조회 | Neo4j |
| 두 번째~네 번째 블록 | Turtle (.ttl) | 온톨로지/지식그래프 정의 | RDF/OWL |
1부: Cypher — Neo4j 그래프 쿼리 언어
🔍 코드 해설
1
2
3
4
| // 수사관은 자신이 배정된 케이스의 데이터만 볼 수 있음
MATCH (u:User {id: $userId})-[:ASSIGNED_TO]->(case:Case)
<-[:PART_OF]-(entity)
RETURN entity
|
이 쿼리가 하는 일을 단계별로 보면:
1
2
3
4
| [User] --ASSIGNED_TO--> [Case] <--PART_OF-- [entity]
↑ ↑ ↑
특정 userId를 배정된 케이스 케이스에 속한
가진 수사관 찾기 모든 엔티티 반환
|
핵심 포인트:
(u:User {id: $userId}) → 파라미터 바인딩으로 SQL Injection 방지<-[:PART_OF]- → 화살표 방향: entity가 case를 향해 PART_OF 관계를 가짐- 이 패턴 자체가 Row-Level Security (행 수준 보안) 역할을 함
- 조인 조건이 곧 권한 검사 — “배정된 케이스에 속한 것만” 자연스럽게 필터링
📚 Cypher 문법 완전 가이드
1. 노드 (Node) 표현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 기본 노드
()
// 레이블이 있는 노드
(:Person)
// 변수 바인딩
(p:Person)
// 속성 필터
(p:Person {name: "Alice", age: 30})
// 여러 레이블 (Neo4j 4.x+)
(p:Person:Employee)
|
2. 관계 (Relationship) 표현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // 방향 없는 관계
(a)-[:KNOWS]-(b)
// 방향 있는 관계
(a)-[:KNOWS]->(b) // a가 b를 앎
(a)<-[:KNOWS]-(b) // b가 a를 앎
// 관계에 변수 바인딩
(a)-[r:KNOWS]->(b)
// 관계 속성
(a)-[r:KNOWS {since: 2020}]->(b)
// 가변 길이 경로 (1~3홉)
(a)-[:KNOWS*1..3]->(b)
// 최단 경로
shortestPath((a)-[:KNOWS*]-(b))
|
3. MATCH — 패턴 매칭
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // 단순 매칭
MATCH (p:Person)
RETURN p
// 복합 패턴
MATCH (suspect:Suspect)-[:ASSOCIATED_WITH]->(org:CriminalOrganization)
WHERE org.name = "Cobra"
RETURN suspect.name, org.name
// 선택적 매칭 (LEFT JOIN과 동일)
MATCH (p:Person)
OPTIONAL MATCH (p)-[:HAS_ALIAS]->(alias)
RETURN p.name, alias.value
// 여러 패턴 동시 매칭
MATCH (a:Person)-[:WORKS_AT]->(c:Company),
(a)-[:LIVES_IN]->(city:City)
RETURN a.name, c.name, city.name
|
4. WHERE — 조건 필터
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // 기본 조건
WHERE p.age > 30
// AND / OR / NOT
WHERE p.age > 30 AND p.status = "active"
WHERE NOT p.deleted
// IN 리스트
WHERE p.status IN ["active", "suspended"]
// 정규식 (STARTS WITH, ENDS WITH, CONTAINS)
WHERE p.name STARTS WITH "Kim"
WHERE p.email CONTAINS "@"
// NULL 체크
WHERE p.alias IS NULL
WHERE p.alias IS NOT NULL
// 관계 존재 여부
WHERE EXISTS { (p)-[:HAS_RECORD]->() }
|
5. 데이터 변경 (Write)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // 노드 생성
CREATE (p:Person {name: "Alice", id: 1})
// 관계 생성
MATCH (a:Person {id: 1}), (b:Case {id: 100})
CREATE (a)-[:ASSIGNED_TO {date: date()}]->(b)
// MERGE — 없으면 생성, 있으면 매칭 (upsert)
MERGE (p:Person {id: $id})
ON CREATE SET p.created = timestamp()
ON MATCH SET p.lastSeen = timestamp()
// 속성 업데이트
MATCH (p:Person {id: 1})
SET p.status = "inactive"
SET p += {score: 90, updated: true} // 여러 속성 한번에
// 삭제
MATCH (p:Person {id: 999})
DELETE p // 관계 없는 노드만 삭제 가능
MATCH (p:Person {id: 999})
DETACH DELETE p // 관계 포함 강제 삭제
|
6. 집계 함수
1
2
3
4
5
6
7
8
9
10
11
| // 기본 집계
MATCH (c:Case)<-[:PART_OF]-(e)
RETURN c.id, count(e) AS entityCount, collect(e.name) AS entityNames
// 집계 함수 목록
count(*) // 전체 행 수
count(x) // null 제외 카운트
sum(x.amount) // 합계
avg(x.score) // 평균
min(x), max(x) // 최소/최대
collect(x) // 리스트로 수집
|
7. WITH — 파이프라인 (중간 결과 전달)
1
2
3
4
5
6
7
| // SQL의 서브쿼리처럼 중간 처리
MATCH (u:User)-[:ASSIGNED_TO]->(c:Case)
WITH u, count(c) AS caseCount
WHERE caseCount > 5
RETURN u.name, caseCount
ORDER BY caseCount DESC
LIMIT 10
|
8. UNWIND — 리스트 펼치기
1
2
3
4
5
6
7
8
9
| // 리스트를 개별 행으로 전개
WITH ["Alice", "Bob", "Charlie"] AS names
UNWIND names AS name
CREATE (:Person {name: name})
// JSON 배열 처리 패턴
UNWIND $suspects AS suspectData
MERGE (s:Suspect {id: suspectData.id})
SET s += suspectData
|
9. 전체 CRUD 패턴 예제 (수사 시스템)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| // ===== 케이스 생성 =====
CREATE (c:Case {
id: "CASE-2024-001",
title: "Operation Nightfall",
status: "active",
created: datetime()
})
// ===== 수사관 배정 =====
MATCH (u:Investigator {badge: $badge}), (c:Case {id: $caseId})
MERGE (u)-[:ASSIGNED_TO {assignedAt: datetime()}]->(c)
// ===== 용의자를 케이스에 연결 =====
MATCH (c:Case {id: $caseId})
MERGE (s:Suspect {name: $name})
MERGE (s)-[:PART_OF]->(c)
// ===== 조직 관계망 탐색 (가변 길이 경로) =====
MATCH path = (s:Suspect {name: "Lee"})-[:MEMBER_OF|ASSOCIATED_WITH*1..4]-(other)
RETURN path
// ===== 케이스 종결 =====
MATCH (c:Case {id: $caseId})
SET c.status = "closed", c.closedAt = datetime()
|
📊 인덱스와 성능
1
2
3
4
5
6
7
8
9
10
| // 인덱스 생성 (Neo4j 4.x+ 구문)
CREATE INDEX user_id_idx FOR (u:User) ON (u.id)
CREATE INDEX case_status_idx FOR (c:Case) ON (c.status)
// 복합 인덱스
CREATE INDEX suspect_name_dob FOR (s:Suspect) ON (s.name, s.dob)
// EXPLAIN / PROFILE로 실행 계획 확인
EXPLAIN MATCH (u:User {id: $id})-[:ASSIGNED_TO]->(c:Case) RETURN c
PROFILE MATCH (u:User {id: $id})-[:ASSIGNED_TO]->(c:Case) RETURN c
|
2부: Turtle — RDF 온톨로지 언어
🐢 Turtle이란?
Turtle (Terse RDF Triple Language) 은 지식 그래프(Knowledge Graph)의 구조와 규칙을 정의하는 언어입니다.
1
2
| RDF = Resource Description Framework
→ 모든 지식을 "주어 - 술어 - 목적어" 트리플로 표현하는 W3C 표준
|
Neo4j Cypher가 “데이터를 어떻게 조회하는가”라면, Turtle/OWL은 “데이터가 어떤 개념이고 어떤 규칙을 따르는가”를 정의합니다.
| | Cypher / Neo4j | Turtle / RDF |
|---|
| 목적 | 데이터 저장·조회 | 지식 구조·규칙 정의 |
| 유형 | 프로퍼티 그래프 DB | 시맨틱 웹 / 지식그래프 |
| 추론 | 없음 (명시적 데이터만) | 있음 (규칙으로 새 사실 추론) |
| 표준 | 사실상 표준 (Neo4j) | W3C 국제 표준 |
| 사용처 | 앱 백엔드, 그래프 분석 | 지식베이스, AI, 의미론적 검색 |
🔍 코드 해설
1
2
3
4
5
6
7
8
| # 규칙 1: Suspect는 Person의 하위 클래스다
:Suspect rdfs:subClassOf :Person .
# 규칙 2: Investigator와 Suspect는 겹칠 수 없다
:Investigator owl:disjointWith :Suspect .
# 규칙 3: CriminalOrganization은 Organization의 하위 클래스다
:CriminalOrganization rdfs:subClassOf :Organization .
|
각 줄은 트리플 (Subject - Predicate - Object) 입니다:
1
2
3
4
| :Suspect rdfs:subClassOf :Person .
↑ ↑ ↑
주어(Subject) 술어(Predicate) 목적어(Object)
"용의자 클래스" "~의 하위 클래스" "사람 클래스"
|
추론 효과:
rdfs:subClassOf → “Suspect이면 자동으로 Person이기도 함” → SPARQL에서 Person 검색 시 Suspect도 포함됨owl:disjointWith → “한 개인이 동시에 Investigator이면서 Suspect일 수 없음” → 데이터 무결성 보장
📚 Turtle 문법 완전 가이드
1. 기본 구조: 트리플
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 기본 형식: 주어 술어 목적어 .
:Alice rdf:type :Investigator .
# 약식 표현 (a = rdf:type)
:Alice a :Investigator .
# 같은 주어, 여러 술어 (세미콜론)
:Alice a :Investigator ;
:name "Alice Kim" ;
:badgeNumber "INV-001" .
# 같은 주어+술어, 여러 목적어 (쉼표)
:Alice :investigates :CASE-001, :CASE-002, :CASE-003 .
|
2. Prefix 선언
1
2
3
|
# PREFIX 없이 긴 URI 직접 사용도 가능
<http://example.org/crime#Suspect> a <http://www.w3.org/2002/07/owl#Class> .
|
3. 클래스 계층 (RDFS)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 클래스 정의
:Person a owl:Class ;
rdfs:label "사람"@ko ;
rdfs:comment "수사 시스템의 모든 사람 개체"@ko .
# 상속 계층
:Investigator rdfs:subClassOf :Person .
:Suspect rdfs:subClassOf :Person .
:Witness rdfs:subClassOf :Person .
:CriminalOrganization rdfs:subClassOf :Organization .
# 다중 상속도 가능
:UndercoverAgent rdfs:subClassOf :Investigator ;
rdfs:subClassOf :Suspect . # 위장 수사관
|
4. OWL 제약 (Constraints)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 두 클래스가 겹칠 수 없음
:Investigator owl:disjointWith :Suspect .
:Victim owl:disjointWith :Suspect .
# 속성 도메인/범위 제약
:investigates rdfs:domain :Investigator ;
rdfs:range :Case .
# 카디널리티 (함수형 속성 = 값이 하나만)
:badgeNumber a owl:FunctionalProperty ;
rdfs:domain :Investigator ;
rdfs:range xsd:string .
# 역 속성 정의
:isInvestigatedBy owl:inverseOf :investigates .
|
5. 데이터 속성 (Datatype Properties)
1
2
3
4
5
6
7
8
9
| :Suspect a owl:Class .
# 개인(Individual) 데이터
:suspect_lee a :Suspect ;
:fullName "이민준"^^xsd:string ;
:dateOfBirth "1985-03-12"^^xsd:date ;
:age 39^^xsd:integer ;
:dangerLevel 7.5^^xsd:decimal ;
:isArrested false^^xsd:boolean .
|
6. 객체 속성 (Object Properties)
1
2
3
4
5
6
7
8
| # 개인 간 관계
:suspect_lee :memberOf :cobra_org ;
:associatedWith :suspect_kim ;
:partOf :case_001 .
:cobra_org a :CriminalOrganization ;
:name "Cobra Syndicate" ;
:basedIn :city_seoul .
|
7. 완성된 범죄 수사 온톨로지 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
# ===== 클래스 정의 =====
:Person a owl:Class .
:Investigator rdfs:subClassOf :Person .
:Suspect rdfs:subClassOf :Person .
:Witness rdfs:subClassOf :Person .
:Victim rdfs:subClassOf :Person .
:Organization a owl:Class .
:CriminalOrganization rdfs:subClassOf :Organization .
:LawEnforcementAgency rdfs:subClassOf :Organization .
:Case a owl:Class .
:Evidence a owl:Class .
# ===== 제약 규칙 =====
:Investigator owl:disjointWith :Suspect .
:Suspect owl:disjointWith :Victim .
:Investigator owl:disjointWith :Victim .
# ===== 속성 정의 =====
:assignedTo rdfs:domain :Investigator ;
rdfs:range :Case .
:partOf rdfs:domain [ owl:unionOf (:Suspect :Witness :Evidence) ] ;
rdfs:range :Case .
:memberOf rdfs:domain :Person ;
rdfs:range :CriminalOrganization .
# ===== 데이터 =====
:case_001 a :Case ;
:caseNumber "2024-CASE-001" ;
:title "Operation Nightfall" ;
:status "active" .
:inv_alice a :Investigator ;
:name "Alice Kim" ;
:assignedTo :case_001 .
:sus_lee a :Suspect ;
:name "Lee Minjun" ;
:partOf :case_001 ;
:memberOf :cobra .
:cobra a :CriminalOrganization ;
:name "Cobra Syndicate" .
|
🔎 SPARQL — RDF 조회 언어 (Cypher에 대응)
Turtle로 정의한 데이터는 SPARQL로 조회합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # Cypher 예제와 동일한 의미: "수사관이 배정된 케이스의 엔티티 조회"
PREFIX : <http://crime-investigation.org/onto#>
SELECT ?entity
WHERE {
?user a :Investigator .
?user :assignedTo ?case .
?entity :partOf ?case .
}
# 추론 활용: Person 검색 시 Suspect/Victim/Investigator 모두 포함
SELECT ?person ?name
WHERE {
?person a :Person ; # 추론 엔진이 하위 클래스도 포함
:name ?name .
}
|
3부: 테스트 환경 구축
🧪 Cypher (Neo4j) 테스트 환경
옵션 1: Neo4j Desktop (로컬, 가장 추천)
1
2
3
4
5
| # 1. 다운로드
# https://neo4j.com/download/ → Neo4j Desktop
# 2. 실행 후 프로젝트 생성 → Add Database → Local DBMS
# 3. Browser에서 Cypher 직접 실행
|
옵션 2: Docker (개발 환경)
1
2
3
4
5
6
7
8
9
10
| # Neo4j 커뮤니티 에디션
docker run \
--name neo4j \
-p 7474:7474 -p 7687:7687 \
-e NEO4J_AUTH=neo4j/password \
-v $HOME/neo4j/data:/data \
neo4j:5-community
# 접속: http://localhost:7474
# 초기 로그인: neo4j / password
|
옵션 3: AuraDB (클라우드, 무료 티어)
1
2
| https://neo4j.com/cloud/platform/aura-graph-database/
→ Start Free → AuraDB Free (1GB, 무료)
|
옵션 4: Neo4j Sandbox (브라우저, 즉시)
1
2
3
| https://sandbox.neo4j.com/
→ 로그인 없이 즉시 사용 가능
→ 범죄 수사 예제 데이터셋 포함! (Crime Investigation 선택)
|
Neo4j 드라이버 (애플리케이션 연동)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // Node.js
import neo4j from 'neo4j-driver'
const driver = neo4j.driver(
'bolt://localhost:7687',
neo4j.auth.basic('neo4j', 'password')
)
const session = driver.session()
const result = await session.run(
`MATCH (u:User {id: $userId})-[:ASSIGNED_TO]->(case:Case)
<-[:PART_OF]-(entity)
RETURN entity`,
{ userId: 'user-001' }
)
result.records.forEach(record => {
console.log(record.get('entity').properties)
})
await session.close()
await driver.close()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Python
from neo4j import GraphDatabase
driver = GraphDatabase.driver("bolt://localhost:7687",
auth=("neo4j", "password"))
with driver.session() as session:
result = session.run(
"""MATCH (u:User {id: $userId})-[:ASSIGNED_TO]->(case:Case)
<-[:PART_OF]-(entity)
RETURN entity""",
userId="user-001"
)
for record in result:
print(record["entity"])
|
🧪 Turtle / RDF 테스트 환경
옵션 1: Protégé (OWL 편집기, 가장 추천)
1
2
3
4
| https://protege.stanford.edu/
→ 무료, Stanford 제공
→ Turtle 파일 열기/편집/추론 실행 모두 가능
→ 온톨로지 시각화 지원
|
옵션 2: Apache Jena Fuseki (SPARQL 서버)
1
2
3
4
5
6
7
| # Docker로 실행
docker run -p 3030:3030 \
-e ADMIN_PASSWORD=password \
stain/jena-fuseki
# 접속: http://localhost:3030
# Turtle 파일 업로드 → SPARQL 쿼리 실행
|
옵션 3: 온라인 도구 (즉시 사용)
1
2
3
4
5
6
7
8
| # RDF 유효성 검사
https://www.w3.org/RDF/Validator/
# SPARQL + Turtle 온라인 실행
https://yasgui.triply.cc/
# 시각화
https://lod-cloud.net/
|
Python으로 Turtle 처리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # pip install rdflib
from rdflib import Graph, Namespace, RDF, RDFS, OWL
g = Graph()
g.parse("crime_ontology.ttl", format="turtle")
# 모든 Suspect 조회 (rdfs:subClassOf 추론 포함)
CRIME = Namespace("http://crime-investigation.org/onto#")
for suspect in g.subjects(RDF.type, CRIME.Suspect):
name = g.value(suspect, CRIME.name)
print(f"Suspect: {name}")
# SPARQL 실행
results = g.query("""
PREFIX : <http://crime-investigation.org/onto#>
SELECT ?entity WHERE {
?entity :partOf :case_001 .
}
""")
for row in results:
print(row.entity)
|
4부: Cypher vs Turtle 비교 — 언제 무엇을 쓰나?
| 시나리오 | 추천 기술 | 이유 |
|---|
| 앱 백엔드 API | Cypher + Neo4j | 빠른 조회, 트랜잭션, 실시간 처리 |
| AI/LLM 지식 연결 | Turtle/RDF | 추론 가능, 의미론적 검색 |
| 소셜 네트워크 분석 | Cypher | 고속 그래프 탐색 |
| 의료/법률 온톨로지 | OWL/Turtle | 표준 준수, 상호운용성 |
| 금융 사기 탐지 | Cypher | 실시간 패턴 매칭 |
| 시맨틱 웹 퍼블리싱 | Turtle | W3C 표준, Linked Data |
| 하이브리드 (추천) | Neo4j + OWL 레이어 | 속도 + 의미론 동시 확보 |
5부: 수사 시스템 전체 아키텍처 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| ┌─────────────────────────────────────────────────────┐
│ 수사 시스템 │
├─────────────────────────────────────────────────────┤
│ │
│ OWL/Turtle 온톨로지 레이어 │
│ (:Suspect rdfs:subClassOf :Person) │
│ (:Investigator owl:disjointWith :Suspect) │
│ ↓ (규칙 정의 → 스키마 생성) │
│ │
│ Neo4j 그래프 DB 레이어 │
│ (u:User)-[:ASSIGNED_TO]->(c:Case) │
│ (entity)-[:PART_OF]->(c:Case) │
│ ↓ (Cypher API) │
│ │
│ 애플리케이션 레이어 (Node.js / Python) │
│ 세션별 권한 검사 → 데이터 반환 │
│ │
└─────────────────────────────────────────────────────┘
|
RBAC 패턴 (Role-Based Access Control) 완성 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // 역할 기반 접근 제어 전체 패턴
MATCH (u:User {id: $userId})
MATCH (u)-[:HAS_ROLE]->(role:Role)
// 역할에 따른 다른 접근 수준
WITH u, role
CALL {
WITH u, role
// 수사관: 자신의 케이스만
WITH u, role WHERE role.name = "Investigator"
MATCH (u)-[:ASSIGNED_TO]->(case:Case)<-[:PART_OF]-(entity)
RETURN entity, case
UNION
// 관리자: 모든 케이스
WITH u, role WHERE role.name = "Admin"
MATCH (case:Case)<-[:PART_OF]-(entity)
RETURN entity, case
}
RETURN entity, case
ORDER BY case.created DESC
|
참고 자료
Cypher (Neo4j)
- 공식 문서: https://neo4j.com/docs/cypher-manual/
- 치트시트: https://neo4j.com/docs/cypher-cheat-sheet/
- 무료 코스: https://graphacademy.neo4j.com/
Turtle / RDF / OWL
- W3C RDF Primer: https://www.w3.org/TR/rdf11-primer/
- OWL 2 Guide: https://www.w3.org/TR/owl2-primer/
- Protégé 튜토리얼: https://protege.stanford.edu/publications/ontology_development/ontology101.pdf
통합 학습
- Linked Data: https://www.w3.org/standards/semanticweb/data
- Knowledge Graph 구축: https://github.com/google-research/google-research/tree/master/knowledge_graph