RDF·SPARQL 실전 입문 — Apache Jena Fuseki로 트리플 만들고 쿼리하기 완전 가이드
RDF·SPARQL은 W3C 표준 시맨틱 웹 기술의 핵심이지만 입문자가 곧장 다루기에는 추상적이라는 인상이 있다. Apache Jena Fuseki는 SPARQL 1.1을 그대로 구현한 HTTP SPARQL 서버로, 로컬 환경에서도 명령 한 줄로 띄울 수 있어 이론과 실습을 가장 빠르게 연결해 준다. 트리플을 직접 적재하고 SELECT·CONSTRUCT·ASK·DESCRIBE 네 가지 쿼리 형식을 모두 실행해 본 뒤 DBpedia·Wikidata를 SERVICE 키워드로 연합 쿼리하는 흐름까지 한 번에 익히는 것이 이 글의 목표다.
설치부터 첫 트리플 적재, 실전 쿼리, 연합 쿼리, HTTP API 호출, 그리고 SHACL 검증까지 따라가면 RDF·SPARQL 입문 단계를 완전히 정리할 수 있다. 코드 블록은 모두 실제 명령·실행 가능한 SPARQL이며, 다이어그램은 본문 흐름에 맞춰 PNG로 함께 제공한다.

이 글의 구성
01 RDF 트리플 모델 기본
RDF는 세상의 모든 사실을 주어(Subject) - 술어(Predicate) - 목적어(Object) 세 조각으로 표현한다. "앨리스의 나이는 30살이다"라는 문장 하나가 그대로 한 줄의 트리플이 된다.
또는 BlankNode
또는 Literal
EXAMPLE · TURTLE
:Alice :hasAge 30 .
"앨리스의 나이는 30살이다" — 모든 사실은 이 세 조각의 조합으로 표현된다.
각 요소는 다음 세 종류 중 하나를 가질 수 있다.
| 노드 종류 | 설명 | 위치 |
|---|---|---|
| IRI | 국제화된 자원 식별자, 전 세계에서 유일 | 주어·술어·목적어 모두 가능 |
| BlankNode | 익명 노드 (`_:b1`), 외부 참조 불가 | 주어·목적어 |
| Literal | 값 (문자열·숫자·날짜 등 데이터타입 포함) | 목적어만 가능 |
리터럴에는 데이터타입을 명시할 수 있다. xsd:integer, xsd:dateTime, xsd:string, 언어 태그를 가진 rdf:langString 등이 자주 쓰인다. RDF·RDFS·OWL 표준 어휘로 rdf:type (인스턴스 분류), rdfs:subClassOf (클래스 계층), rdfs:label (사람이 읽는 이름), owl:sameAs (동일 자원) 등이 거의 모든 트리플에 등장한다.
02 RDF 직렬화 포맷 4종
같은 트리플 데이터라도 직렬화 방식은 여러 가지다. 실무에서 가장 자주 쓰이는 네 가지 포맷을 한 화면에 정리하면 다음과 같다.
사람이 가장 읽기 좋은 포맷. 실무 표준. @prefix 축약으로 간결.
@prefix : <http://ex.org/> .
:Alice a :Person ;
:hasAge 30 ;
:hasName "앨리스"@ko .
✓ 가독성 · 실무 표준
✗ 스트림 처리 어려움
한 줄에 트리플 하나. 스트림·diff·grep에 가장 강함. 가장 단순.
<http://ex.org/Alice>
<http://ex.org/hasAge>
"30"^^<...#integer> .
✓ 스트림 · 라인 단위 처리
✗ 장황 · 가독성 낮음
W3C 초기 표준. 장황·디버깅 어려움. 레거시 시스템에서 여전히 사용.
<rdf:RDF xmlns:rdf="...">
<rdf:Description rdf:about=":Alice">
<:hasAge>30</:hasAge>
</rdf:Description>
</rdf:RDF>
✓ XML 도구 호환
✗ 장황 · 가독성 낮음
JSON 친화 Linked Data. @context로 매핑. REST·JS·SEO에 표준.
{
"@context": {"@vocab": "..."},
"@id": ":Alice",
"hasAge": 30
}
✓ 웹 API · Schema.org SEO
✗ 깊은 그래프는 복잡
Turtle은 사람이 가장 읽기 좋아 실무 표준이 됐고, N-Triples는 한 줄에 한 트리플이라 스트림·diff·grep에 강하다. RDF/XML은 초기 표준이지만 장황해 디버깅이 어렵고 레거시에서 주로 만난다. JSON-LD는 JSON 친화적이고 @context로 매핑하는 구조라 REST API와 SEO(Schema.org) 영역에서 사실상 표준이다.
처음 실습한다면 Turtle 한 가지에 집중해 익숙해진 뒤 필요할 때 다른 포맷을 익히는 흐름이 효율적이다.
03 Apache Jena Fuseki 아키텍처와 설치
Apache Jena는 Apache Software Foundation 산하 자바 기반 RDF 프레임워크다. 단순 라이브러리가 아니라 트리플 저장·SPARQL 엔진·HTTP 서버·검증까지 한 묶음으로 제공한다.
Apache Jena 6.1 (2026-05 릴리스) · Java 21+ 필요 · ASF 오픈소스
| 계층 | 컴포넌트 | 역할 |
|---|---|---|
| HTTP | Fuseki | SPARQL HTTP 서버, 기본 포트 3030, 웹 UI 제공 |
| ENGINE | ARQ | SPARQL 1.1 쿼리·업데이트 엔진, 연합 쿼리(SERVICE) 지원 |
| API | Jena Core | Java/Scala에서 RDF 트리플 조작·직렬화·SHACL 검증 |
| STORAGE | TDB2 / In-Memory | 영구·메모리 트리플 저장소 |
설치는 매우 단순하다. 공식 사이트에서 apache-jena-fuseki 바이너리를 받아 압축을 풀고 실행하면 된다. 2026-05 현재 안정 버전은 Apache Jena 6.1(2026-05 릴리스, 직전 6.0은 2026-02)이며 Java 21 이상이 필요하다.
# 다운로드 · 압축 해제 (버전은 공식 사이트에서 최신 확인)
curl -O https://dlcdn.apache.org/jena/binaries/apache-jena-fuseki-6.1.0.zip
unzip apache-jena-fuseki-6.1.0.zip
cd apache-jena-fuseki-6.1.0
# (A) 메모리 데이터셋 — 가장 빠른 시작
./fuseki-server --mem /ds
# (B) TDB2 영구 저장소
./fuseki-server --tdb2 --loc=DB /ds
# (C) 기존 .ttl 파일을 로드하며 기동
./fuseki-server --file=data.ttl /ds
기동 후 브라우저에서 http://localhost:3030에 접속하면 웹 UI가 열린다. 쿼리·업로드·관리 화면이 모두 제공돼 별도 클라이언트 없이도 SPARQL을 바로 실험할 수 있다.
💡 메모리 vs TDB2
학습·PoC 단계에서는 `--mem` 옵션이 가장 편하다. 서버를 끄면 데이터가 사라지지만 시작이 빠르고 설정이 단순하다. 운영·실험 데이터를 유지해야 한다면 TDB2(`--tdb2 --loc=DB`)로 영구 저장소를 쓰는 편이 안전하다. TDB2는 동시 쓰기 트랜잭션과 큰 데이터셋 처리에 최적화돼 있다.
04 첫 트리플 데이터 만들기 (Turtle)
data.ttl 한 파일에 사람·회사 정보를 넣어 본다. @prefix로 네임스페이스를 선언하고, 클래스(rdfs:Class)·인스턴스(a 또는 rdf:type)·속성을 순서대로 정의한다.
# data.ttl
@prefix : <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
:Person a rdfs:Class .
:Alice a :Person ;
:hasName "앨리스"@ko ;
:hasAge 30 ;
:worksAt :Acme .
:Bob a :Person ;
:hasName "Bob" ;
:hasAge 42 ;
:worksAt :Acme .
:Acme a :Company ; rdfs:label "Acme Corp" .
이 파일을 Fuseki에 적재하는 방법은 두 가지다. 첫째, 기동 시 --file=data.ttl /ds 옵션을 주면 자동으로 로드된다. 둘째, 이미 떠 있는 서버라면 웹 UI의 "upload data"에서 파일을 올리거나 다음 HTTP 요청으로 적재할 수 있다.
# HTTP로 데이터 업로드
curl -X POST -H "Content-Type: text/turtle" \
--data-binary @data.ttl \
http://localhost:3030/ds/data
05 SPARQL 쿼리 4종 — SELECT·CONSTRUCT·ASK·DESCRIBE
SPARQL은 트리플을 다루는 W3C 표준 쿼리 언어다. 네 가지 형식이 있고 반환 형태가 각각 다르다.
변수에 바인딩된 값을 표(테이블) 형태로 반환한다. SQL의 SELECT와 유사.
SELECT ?name ?age
WHERE {
?p :hasName ?name ; :hasAge ?age .
FILTER(?age >= 30)
}
반환 · 결과 테이블(변수 = 컬럼)
기존 데이터에서 새 트리플을 만들어 RDF 그래프로 반환. 데이터 변환·통합에 유용.
CONSTRUCT {
?p :isAdult true
}
WHERE {
?p :hasAge ?a FILTER(?a >= 19)
}
반환 · 새 RDF 그래프(트리플 묶음)
패턴이 매칭되는 트리플이 있는지 boolean으로 답한다. 존재 여부 확인.
ASK {
:Alice :hasAge ?a .
FILTER(?a > 18)
}
# → true / false
반환 · true 또는 false
특정 자원과 관련된 트리플 묶음을 반환. 자원의 전체 그림을 한번에 볼 때.
DESCRIBE :Acme
# → :Acme rdf:type :Company .
# :Acme rdfs:label "Acme..." .
# ... (구현 의존)
반환 · 자원 주변 RDF 그래프
- SELECT : SQL과 가장 가깝다. 변수에 바인딩된 값을 표(테이블) 형태로 반환한다.
- CONSTRUCT : 패턴이 매칭되는 트리플로부터 새로운 트리플을 만들어 RDF 그래프로 돌려준다. 데이터 변환·통합에 강력하다.
- ASK : 해당 패턴의 트리플이 존재하는지 boolean(true/false)으로만 답한다.
- DESCRIBE : 특정 자원과 관련된 트리플 묶음을 반환한다. 구현체마다 반환 범위가 조금씩 다르지만 자원의 전체 그림을 한번에 보고 싶을 때 유용하다.
06 실전 쿼리 예제와 HTTP API
앞에서 만든 data.ttl 위에서 자주 쓰이는 SPARQL 패턴을 모아 두면 실습이 훨씬 빨라진다.
PREFIX : <http://example.org/>
# 1) 30살 이상 사람 이름·나이 (내림차순)
SELECT ?name ?age WHERE {
?p a :Person ; :hasName ?name ; :hasAge ?age .
FILTER(?age >= 30)
} ORDER BY DESC(?age)
# 2) 회사별 직원 수 집계
SELECT ?company (COUNT(?p) AS ?cnt)
WHERE { ?p :worksAt ?company . }
GROUP BY ?company
# 3) Alice의 모든 속성
SELECT ?p ?o WHERE { :Alice ?p ?o }
# 4) ASK — 성인 여부
ASK { :Alice :hasAge ?a FILTER(?a > 18) }
# 5) CONSTRUCT — 새 트리플 생성
CONSTRUCT { ?p :isAdult true }
WHERE { ?p :hasAge ?a FILTER(?a >= 19) }
# 6) DESCRIBE — 자원 묘사
DESCRIBE :Acme
HTTP API로도 같은 쿼리를 던질 수 있다. Fuseki는 SPARQL 1.1 Protocol을 따르므로 일반 REST 도구로 곧바로 다룬다.
# 쿼리 (GET) — JSON 결과 요청
curl -G 'http://localhost:3030/ds/sparql' \
--data-urlencode 'query=SELECT * WHERE {?s ?p ?o} LIMIT 5' \
-H 'Accept: application/sparql-results+json'
# 업데이트 (POST) — 트리플 추가
curl -X POST 'http://localhost:3030/ds/update' \
--data-urlencode 'update=INSERT DATA { <http://ex/x> <http://ex/p> "v" }'
INSERT DATA, DELETE DATA, DELETE WHERE, INSERT { ... } WHERE { ... } 같은 SPARQL Update 문법으로 데이터를 추가·삭제할 수 있다. 인증이 필요하다면 --passwd 옵션과 shiro.ini 파일을 함께 구성한다.
07 연합 쿼리 — DBpedia·Wikidata SERVICE
SPARQL의 진짜 매력은 여러 SPARQL 엔드포인트를 하나의 쿼리로 묶을 수 있다는 점이다. 로컬 Fuseki에 데이터를 다 적재하지 않고 외부 공개 엔드포인트에서 즉시 가져올 수 있다.
PREFIX dbo: <http://dbpedia.org/ontology/>
SELECT ?city ?name WHERE {
SERVICE <https://dbpedia.org/sparql> {
?city a dbo:City ; dbo:country <.../South_Korea> ;
rdfs:label ?name . FILTER(LANG(?name) = "ko")
}
} LIMIT 20
대표적인 공개 엔드포인트는 DBpedia(https://dbpedia.org/sparql)와 Wikidata(https://query.wikidata.org/sparql)다. 다음은 DBpedia에서 한국 도시 목록을 한국어 라벨로 가져오는 예시다.
PREFIX dbo: <http://dbpedia.org/ontology/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?city ?name WHERE {
SERVICE <https://dbpedia.org/sparql> {
?city a dbo:City ;
dbo:country <http://dbpedia.org/resource/South_Korea> ;
rdfs:label ?name .
FILTER(LANG(?name) = "ko")
}
} LIMIT 20
Wikidata도 동일한 방식이지만 일부 엔드포인트는 User-Agent 헤더를 요구한다. 같은 쿼리에서 SERVICE를 여러 번 써서 두 엔드포인트의 결과를 결합할 수도 있고, 실패해도 무시하고 진행하고 싶다면 SERVICE SILENT를 사용한다.
⚠️ 연합 쿼리 주의점
공개 엔드포인트는 트래픽 제한이 있다. 대량 호출이나 무거운 쿼리는 차단·지연될 수 있어 LIMIT를 반드시 걸고, 캐싱 가능한 결과는 로컬 트리플로 저장해 두는 편이 안전하다. 또한 같은 자원이 DBpedia·Wikidata에서 IRI가 다를 수 있으므로 `owl:sameAs` 매핑을 활용하면 두 엔드포인트의 결과를 자연스럽게 연결할 수 있다.
08 SHACL 검증과 실무 시나리오
RDF 데이터의 품질을 코드 레벨에서 검증하려면 SHACL(W3C 권고, 2017) 또는 ShEx 같은 제약 언어를 사용한다. Jena 6.0은 jena-shacl 모듈로 SHACL을 공식 지원한다.
# shapes.ttl — Person은 hasAge가 0 이상 정수여야 한다
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix : <http://example.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
:PersonShape a sh:NodeShape ;
sh:targetClass :Person ;
sh:property [ sh:path :hasAge ;
sh:datatype xsd:integer ;
sh:minInclusive 0 ;
sh:maxCount 1 ] .
# 검증 실행
shacl validate --shapes shapes.ttl --data data.ttl
실무에서 RDF·SPARQL이 가장 자주 쓰이는 영역은 메타데이터 카탈로그(DCAT), 마스터 데이터 통합, 그리고 최근에는 LLM RAG 그라운딩이다. 지식 그래프에서 컨텍스트를 SPARQL로 추출해 LLM에 주입하면 환각을 줄이고 답변의 근거를 명확히 만들 수 있다. PoC 단계에서는 Fuseki 메모리 데이터셋이 가장 가볍게 시작 가능한 환경이다.
09 자주 묻는 질문 5가지
Q1. Java 21이 꼭 필요한가? 17로는 안 되나?
Apache Jena 6.x 라인(6.0 2026-02 / 6.1 2026-05) 이후로는 Java 21이 최소 요구다. 이전 버전 Jena 4.x·5.x는 Java 17에서 동작했지만, 신규 기능과 보안 패치가 6.x에 집중되므로 새로 시작한다면 Java 21로 맞추는 것이 권장된다.
Q2. SPARQL과 SQL의 가장 큰 차이는?
SQL은 관계형 테이블 위에서 동작하지만 SPARQL은 트리플 그래프 위에서 동작한다. JOIN을 명시할 필요 없이 변수 두 개를 같은 이름으로 두면 자동으로 매칭된다. 또한 OPTIONAL·UNION·FILTER 같은 그래프 패턴이 풍부하고, CONSTRUCT로 새 트리플을 만들거나 SERVICE로 외부 엔드포인트를 끌어오는 등 SQL에는 없는 기능이 많다.
Q3. Fuseki는 운영 환경에서도 쓸 수 있나?
사용 가능하다. TDB2 영구 저장소·트랜잭션 지원·SHACL 검증·HTTP 인증을 갖추고 있어 중소 규모 서비스에서는 충분히 운영 가능하다. 다만 대규모 동시성·고가용성이 필요하면 Stardog·GraphDB·Neptune 같은 상용 그래프 DB도 함께 검토하는 편이 안전하다.
Q4. DBpedia 연합 쿼리가 자꾸 타임아웃이 나는데?
공개 엔드포인트는 정책상 쿼리 시간·결과 크기에 제한을 둔다. LIMIT를 작게 시작하고, FILTER로 결과 범위를 좁히며, 캐싱 가능한 데이터는 로컬 트리플로 저장해 사용하는 것이 안정적이다. 대량·반복 호출이 필요하면 DBpedia 덤프를 받아 로컬 TDB2에 적재해 두는 방법이 가장 확실하다.
Q5. LLM RAG에서 RDF를 쓰면 무엇이 좋아지나?
RDF·SPARQL은 사실 제약과 다중 홉 추론에 강하다. 벡터 검색이 의미적 유사 문서를 찾아 주는 데 비해, SPARQL은 정확한 사실 패턴을 추출하므로 환각 감소와 근거 추적에 유리하다. Vector DB와 Graph DB(또는 SPARQL 엔드포인트)를 함께 쓰는 하이브리드 아키텍처가 엔터프라이즈 RAG에서 점차 표준화되는 흐름이다.
마무리
RDF·SPARQL은 처음 보면 낯설지만 본질은 단순하다. 모든 사실을 주어-술어-목적어 세 조각으로 표현하고, SPARQL의 그래프 패턴으로 그 트리플을 자유롭게 조합·변환하는 도구다. Apache Jena Fuseki는 이 모든 표준을 한 묶음으로 제공하는 가장 쉬운 출발점이며, 명령 한 줄로 로컬에 띄워 즉시 실험할 수 있다.
실습 흐름은 단순하다. Fuseki를 메모리 모드로 띄우고, Turtle로 작은 데이터셋을 적재한 뒤, SELECT·CONSTRUCT·ASK·DESCRIBE 네 가지 쿼리를 차례로 실행해 결과를 비교해 본다. 익숙해지면 TDB2로 영구 저장소를 만들고, SERVICE 키워드로 DBpedia·Wikidata 같은 외부 엔드포인트를 끌어와 보고, SHACL로 데이터 제약을 코드처럼 관리하는 단계로 자연스럽게 확장된다.
표준이 정해진 기술이라 학습 자료가 풍부하고, Fuseki 자체가 가볍기 때문에 시작 비용이 거의 들지 않는다는 점도 큰 장점이다. 작은 도메인 모델 하나를 잡아 트리플로 표현하고, SPARQL로 질의하면서 직접 손으로 만져 보는 것이 가장 빠른 학습 경로다.
RDF·SPARQL 실습 체크리스트
본 글은 Apache Jena Fuseki를 활용한 RDF·SPARQL 입문 실습 가이드로 작성됐다. 버전·설치 명령·엔드포인트 정책은 변동될 수 있으므로 실습 전 Apache Jena 공식 문서, W3C SPARQL 1.1 표준, 각 공개 엔드포인트의 이용 약관을 직접 확인한다.
#RDF #SPARQL #ApacheJena #JenaFuseki #TDB2 #ARQ #Turtle #JSONLD #SPARQLQuery #SPARQLUpdate #FederatedQuery #DBpedia #Wikidata #SHACL #LLM_RAG
'IT' 카테고리의 다른 글
| OWL 2 프로파일 완전 비교 — EL·QL·RL 언제 무엇을 쓰는가 선택 가이드 (0) | 2026.05.20 |
|---|---|
| Schema.org JSON-LD SEO 실전 — 리치 스니펫 적용과 구조화 데이터 검증 가이드 (0) | 2026.05.19 |
| 온톨로지가 뭐야 — RDF·OWL·SPARQL·지식 그래프·LLM RAG 완전 정리 (0) | 2026.05.19 |
| ORA-00918 column ambiguously defined — 조인 컬럼 모호 에러 원인·해결·예방 완전 정리 (0) | 2026.05.19 |
| 데이터 거버넌스가 뭐야 — DAMA-DMBOK·데이터 품질·소유권·Lineage·데이터 메시 완전 정리 (0) | 2026.05.19 |
댓글