TIL··6 min read·

지식 그래프와 RDF — 관계에 이름표를 붙여 저장하는 법

온톨로지를 실제 데이터로 적으면 어떻게 생겼을까. 모든 사실을 세 조각으로 쪼개는 RDF, 빈칸 채우기 같은 질의어 SPARQL, 그리고 컴퓨터가 스스로 결론을 내는 추론까지 사전 지식 없이 정리했다.

지식 그래프와 RDF

난이도 — 중급 (1편을 먼저 읽으면 훨씬 쉽다)

앞 글에서 "관계에 이름표를 붙이면 온톨로지"라고 했다. 개념은 알겠는데 한 가지가 남는다.

그래서 그 이름표를 실제로 어디에 어떻게 저장하는가?

그 답이 이 글이다. 저장된 결과물을 지식 그래프(knowledge graph)라 부르고, 그것을 적는 표준 방식을 RDF(Resource Description Framework)라고 한다.

미리 말해 두면, 이건 연구실 기술이 아니다. 구글이 검색 결과 옆에 인물 정보를 띄우는 방식이 이것이고, 프론트엔드 개발자라면 이미 비슷한 걸 쓰고 있다. 그 이야기는 글 끝에서 한다.


표(테이블)로는 왜 불편한가

우리가 아는 가장 익숙한 데이터 저장법은 표다. 엑셀이나 데이터베이스 테이블처럼, 세로줄(칸)을 미리 정하고 가로줄(행)을 채운다.

이름소속팀등번호
손흥민토트넘7

깔끔하다. 그런데 지식을 쌓는 용도로 쓰면 불편한 점이 두 가지 생긴다.

첫째, 칸을 미리 정해야 한다. "별명도 기록하자"가 되는 순간, 표 구조 자체를 뜯어고쳐야 한다. 데이터가 몇 만 줄이면 큰일이다.

둘째, 정보가 들쭉날쭉하면 빈칸이 넘친다. 어떤 선수는 정보가 스무 개고 어떤 선수는 이름뿐이다. 표는 모두에게 같은 칸을 강요하니 빈칸투성이가 된다.

지식은 원래 고르지 않다. 그래서 다른 방식이 필요하다.


RDF — 모든 사실을 세 조각으로

RDF의 아이디어는 놀랄 만큼 단순하다.

세상의 모든 사실을 `무엇이 - 어떻다 - 무엇이다` 세 조각으로 적는다.

거의 문장이랑 같다. "손흥민은 토트넘 소속이다"를 세 조각으로 자르면 이렇게 된다.

사실 하나 = 세 조각짜리 한 줄

주어술어목적어
손흥민소속팀토트넘
토트넘연고지런던
런던국가영국

한 줄이 사실 하나다. 새 사실은 칸이 아니라 줄을 추가해서 적는다.

이 세 조각짜리 한 줄을 트리플(triple, 세 쌍)이라고 부른다. 왼쪽부터 주어(subject) · 술어(predicate) · 목적어(object)다. 문법 용어 그대로다.

이런 줄을 잔뜩 모으면 자연스럽게 그물이 된다. 위 그림에서 이어 붙이기를 눌러 보면, 앞 줄의 목적어가 다음 줄의 주어가 되면서 저절로 이어지는 게 보인다.

바로 이 그물이 지식 그래프다. 앞 글의 가족관계도가 데이터가 된 모습이다.

표와 무엇이 다른가

핵심 차이는 새 정보를 추가하는 방법이다.

  • 표에서는 → 칸(세로줄)을 추가해야 한다. 구조를 바꾸는 큰일이다.
  • RDF에서는 → 줄 하나를 더 쓴다. 끝이다.
code
손흥민   별명    쏘니        ← 그냥 한 줄 추가

구조를 미리 정할 필요가 없다. 아는 것만 적으면 되고, 모르는 건 안 적으면 그만이라 빈칸도 없다. 정보가 들쭉날쭉해도 아무 문제가 없다.

그런데 더 근본적인 차이가 하나 더 있다. 관계의 이름 자체가 '데이터'라는 점이다.

표에서 소속팀은 칸 이름, 곧 설계에 해당한다. 데이터가 아니다. 그래서 표에 무언가 물어보려면 어떤 칸이 있는지 먼저 알아야 한다.

RDF에서 소속팀은 그냥 가운데 자리에 놓인 값이다. 이 차이가 왜 강력한지는 바로 다음 절에서 드러난다.

이름표는 주소로 적는다

한 가지만 더. 실제 RDF에서는 단어 대신 주소(URL처럼 생긴 것)를 쓴다.

그냥 "사과"라고 쓰면 과일인지 회사인지 알 수 없다. 하지만 주소는 세계에서 유일하다.

code
https://example.org/fruit/apple      ← 과일 사과
https://example.org/company/apple    ← 회사 애플

덕분에 뜻밖의 이득이 생긴다. 서로 다른 곳에서 만든 데이터가 저절로 이어진다. 내가 쓴 주소와 남이 쓴 주소가 같으면, 둘은 같은 것을 가리킨다. 약속을 따로 하지 않아도 데이터가 연결된다.


SPARQL — 빈칸 채우기로 질문하기

데이터를 모았으면 꺼내 써야 한다. 그래프에 질문하는 언어가 SPARQL(스파클이라고 읽는다)이다.

방식이 재미있는데, 빈칸 채우기 문제를 내듯이 묻는다.

sparql
SELECT ?선수 WHERE {
  ?선수   소속팀   ?팀   .
  ?팀     연고지   런던  .
}

무섭게 생겼지만 읽는 법은 간단하다. ?가 붙은 것이 빈칸이다. 우리말로 옮기면 이렇다.

"누군가(?선수)가 어떤 팀(?팀)에 소속돼 있고, 그 팀의 연고지가 런던이다. 이 빈칸들을 채워라."

컴퓨터는 그래프를 뒤져서 이 모양과 맞아떨어지는 부분을 찾아 빈칸을 채운다.

찾고 싶은 모양을 그대로 그려서 묻는다는 점이 핵심이다. 표에서 쓰는 SQL이 "이 표와 저 표를 어떻게 이어 붙일지" 지시해야 하는 것과 대비된다.

구조를 몰라도 물어볼 수 있다

빈칸은 아무 자리에나 놓을 수 있다. 가운데(관계) 자리에도 놓을 수 있다. 이렇게 물어보는 셈이다.

“손흥민에 대해 아는 것을 전부 보여줘”

주어술어 (관계)목적어 (값)
손흥민무엇이든무엇이든

점선 칸은 아직 모르는 자리다. 컴퓨터가 그래프를 뒤져서 채워 준다.

앞에서 관계 이름이 데이터라고 한 것이 여기서 값어치를 한다. 안에 무엇이 들어 있는지 몰라도 물어볼 수 있다.

실제 SPARQL 문법에서는 빈칸에 ?로 시작하는 이름을 붙여 준다. 위 그림의 점선 칸이 아래의 ?관계, ?값이다.

sparql
SELECT ?관계 ?값 WHERE {
  손흥민   ?관계   ?값  .
}

표에서는 이런 질문 자체가 성립하지 않는다. SELECT 뒤에 쓸 칸 이름을 모르면 질문을 만들 수가 없기 때문이다.


규칙을 적어 두면 컴퓨터가 스스로 채운다

여기까지는 사실을 적고 찾기만 했다. 온톨로지의 진짜 값어치는 규칙을 함께 적을 때 나온다.

예를 들어 이런 규칙을 미리 적어 둔다.

  • 프리미어리그팀은 축구팀의 한 종류다

그리고 사실 하나만 적는다.

  • 토트넘은 프리미어리그팀이다

그러면 아무도 적지 않은 사실이 저절로 나온다.

  • 따라서 토트넘은 축구팀이다

1편에서 "개는 숨을 쉰다"로 설명했던 추론이 바로 이것이다. 사람이 모든 사실을 적지 않아도 된다는 게 핵심이다.

이 규칙을 적는 언어에 이름이 붙어 있는데, 이름까지 외울 필요는 없고 층이 나뉘어 있다는 것만 알면 충분하다.

이름하는 일
RDF(Resource Description Framework)사실을 세 조각으로 적는다
RDFS(RDF Schema)"무엇은 무엇의 한 종류다" 같은 기본 규칙을 정한다
OWL(Web Ontology Language)더 정교한 규칙(같음, 반대 관계 등)을 정한다
한 줄 요약: RDF는 사실을 적고, RDFS와 OWL은 적지 않은 사실까지 끌어낸다.

이미 우리 곁에 있다 — schema.org와 JSON-LD

여기까지 오면 "그래서 이걸 누가 쓰나" 싶을 텐데, 매일 보고 있다.

검색 결과에 별점, 조리 시간, 작성자가 함께 뜨는 걸 본 적이 있을 것이다. 그게 가능한 이유는 그 페이지가 자기 정보를 기계가 읽을 수 있는 형태로 적어 뒀기 때문이다. 그 형식이 JSON-LD이고, 이건 RDF를 JSON 문법으로 쓴 것이다.

블로그 글이라면 이렇게 생겼다.

json
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "지식 그래프와 RDF",
  "datePublished": "2026-08-19",
  "author": { "@type": "Person", "name": "Trond" }
}

앞에서 본 개념과 하나씩 맞아떨어진다.

  • @context — 어떤 단어 사전을 쓸지 선언한다. 여기서는 schema.org라는 공용 사전.
  • @type — 이게 무엇인지. BlogPosting은 "블로그 글"이라는 뜻이다.
  • 나머지 줄 — 전부 트리플이다. "headline": "..."은 곧 <이 글> 제목 "지식 그래프와 RDF".

즉 웹페이지에 구조화 데이터(structured data)를 넣는 일은, 내 페이지의 사실을 트리플로 번역해 검색 엔진의 지식 그래프에 보태는 행위다.

그리고 여기서 반가운 사실이 하나 있다. 단어 사전을 직접 만들 필요가 없다. schema.org에 이미 수천 개가 정의돼 있고, 가져다 쓰면 된다.


그럼 표보다 좋은 건가 — 아니다

여기까지 읽으면 "표는 낡았고 그래프가 좋다"처럼 들릴 수 있는데, 그렇지 않다. 사실 실제 RDF 저장소 내부를 열어 보면 대개 세 칸짜리 표다.

주어술어목적어
손흥민소속팀토트넘
손흥민별명쏘니

즉 저장 방식이 다른 게 아니다. 칸을 이름·소속팀·등번호로 나눌 것인가, 주어·술어·목적어로 나눌 것인가. 모델을 어떻게 잡을 것인가의 차이다. 그래서 얻는 것과 잃는 것이 분명하다.

  • 얻는 것 — 구조 변경이 그냥 줄 추가가 되고, 관계 자체를 물어볼 수 있고, 다른 데이터와 합치기 쉬워진다.
  • 잃는 것 — 질의가 조인 범벅이 되어 느려진다. 그리고 설계가 비싸다. 어떤 개념을 어떤 관계로 이을지 정하는 일은 코드보다 사람들 사이의 합의 문제에 가까워서, 한번 잘못 잡으면 되돌리기 어렵다.

주문 내역이나 재고처럼 모양이 일정한 데이터에 RDF를 쓰면 손해다. 반대로 대상마다 아는 것이 제각각인 지식에는 표가 불편하다. 도구의 우열이 아니라 데이터의 성격 문제다.

그래서 현실적인 지점은 대체로 이렇다. 직접 설계는 최소로, 이미 있는 공용 사전을 최대한 재사용하고, 추론은 꼭 필요한 곳에만. 정교한 체계를 만들어 놓고 데이터가 따라오지 않으면, 남는 건 아무도 안 보는 문서뿐이다.


정리

  • RDF(Resource Description Framework) — 모든 사실을 주어 - 술어 - 목적어 세 조각으로 적는다. 구조를 미리 정할 필요가 없다.
  • SPARQL — 빈칸이 있는 모양을 그려서 질문하면, 컴퓨터가 채워 준다.
  • 규칙(RDFS·OWL) — 적어 두면 적지 않은 사실까지 컴퓨터가 끌어낸다. 이게 온톨로지의 값어치다.
  • JSON-LD — 이 모든 것의 가장 실용적인 얼굴. 웹페이지가 검색 엔진에 자기를 설명하는 방법이다.

그런데 사실, 프론트엔드 개발자는 이런 걸 이미 쓰고 있다. 매일 넣는 그 코드가 사실은 RDF였다는 이야기를 다음 글에서 실제 코드로 확인한다.

Related Posts

같이 읽으면 좋은 글