온톨로지와 지식그래프 기초 - DB 스키마와 무엇이 다른가

"온톨로지"라는 단어를 AI 문서에서 처음 보면 대개 두 번 놀란다. 한 번은 철학 용어가 왜 여기 나오나 싶어서, 또 한 번은 설명을 읽어도 "그래서 데이터베이스랑 뭐가 다른데?"가 해결되지 않아서다. 이 글은 그 두 번째 질문에 답하는 것을 목표로 한다. 개념을 그림으로 정리하고, 마지막엔 파이썬 20여 줄로 온톨로지의 핵심 기능을 직접 돌려본다.

결론부터 말하면 온톨로지는 "이 도메인에 어떤 종류의 것들이 존재하고, 그것들이 서로 어떻게 연결되는지"를 기계가 읽을 수 있게 못 박아 둔 규칙이다. DB 스키마와 결정적으로 다른 점은 하나다. DB 스키마는 저장 구조를 정의하고, 온톨로지는 추론 규칙을 정의한다. 그래서 온톨로지는 저장하지 않은 사실을 만들어낼 수 있다.

온톨로지는 무엇으로 이루어져 있나

온톨로지를 구성하는 요소는 사실 네 개뿐이다. 이름은 낯설지만 개념은 객체지향 프로그래밍을 해본 사람이면 이미 아는 것들이다.

  • 클래스(Class) — 개념의 범주. Service, Person. 자바의 클래스와 같은 자리다.
  • 인스턴스(Individual) — 실제 개체. OrderApi, alice.
  • 속성(Property) — 개체를 잇는 관계. owns, dependsOn. 여기서 관계는 필드가 아니라 일급 시민이다. 이것이 첫 번째 차이점이다.
  • 공리(Axiom) — 그 관계가 지켜야 할 규칙. "owns의 주체는 반드시 Person이다", "dependsOn은 이행적이다". 여기가 온톨로지의 심장이다.
온톨로지를 이루는 네 가지 요소

온톨로지의 네 요소. 위 두 층은 흔한 타입 시스템이지만, 맨 아래 '공리'가 나머지를 다르게 만든다.

주목할 점은 다이어그램 맨 아래다. 우리는 alicePerson이라고 말한 적이 없다. 단지 alice owns OrderApi라는 사실 하나와, "owns의 주체는 Person"이라는 규칙 하나를 넣었을 뿐이다. 나머지는 추론 엔진이 채운다.

핵심은 '추론' - 저장하지 않은 사실을 얻는다

온톨로지를 단순한 태그 체계나 폴더 분류와 가르는 지점이 바로 이것이다. 조금 더 실용적인 예를 보자. 마이크로서비스 의존 관계를 관리한다고 하자.

이행성 공리로 새 사실이 추론되는 과정

이행성(transitivity) 공리 하나로 그래프 전체의 간접 의존이 자동으로 채워진다.

서비스가 세 개면 별것 아닌 것처럼 보인다. 그런데 서비스가 200개고 의존 관계가 800개라면 이야기가 다르다. "이 라이브러리를 업그레이드하면 영향받는 서비스 전부"를 묻는 질의가, 애플리케이션 코드에 재귀 탐색을 짜지 않고도 데이터 계층에서 저절로 나온다.

더 중요한 건 추론 경로가 남는다는 점이다. 왜 그런 답이 나왔는지 A→B→C라는 근거 체인을 그대로 제시할 수 있다. LLM의 "그럴듯한 설명"과 달리 이건 검증 가능한 도출이다. 규제 산업이나 감사가 필요한 도메인에서 온톨로지가 살아남은 이유가 여기 있다.

헷갈리는 세 층 정리 - 온톨로지 · 지식그래프 · 저장소

온톨로지, 지식그래프, RDF, SPARQL… 문서마다 뒤섞여 나와서 혼란스럽다. 사실 층위가 다른 말들이다.

온톨로지 지식그래프 저장소의 3계층 구조

온톨로지는 타입 시스템, 지식그래프는 그 타입으로 만든 실제 인스턴스들이라고 생각하면 편하다.

실무에서 "온톨로지 도입한다"고 말할 땐 보통 이 셋을 통째로 가리킨다. 하지만 설계할 땐 반드시 위에서부터 내려와야 한다. 저장소부터 고르고 시작하면 십중팔구 스키마가 저장소 사정에 끌려간다.

20줄로 직접 확인해보기 - rdflib + owlrl

말로만 들으면 추상적이니 직접 돌려보자. 파이썬 rdflib와 추론 엔진 owlrl만 있으면 된다.

# pip install rdflib owlrl
from rdflib import Graph, Namespace, RDF, RDFS, OWL
from owlrl import DeductiveClosure, OWLRL_Semantics

EX = Namespace("http://example.com/ns#")
g = Graph()

# --- 1. 온톨로지(스키마): 규칙을 정의한다 -----------------
g.add((EX.Service, RDF.type, OWL.Class))
g.add((EX.Person,  RDF.type, OWL.Class))

# dependsOn을 "이행적 속성"으로 선언 - 이 한 줄이 핵심이다
g.add((EX.dependsOn, RDF.type,    OWL.TransitiveProperty))
g.add((EX.dependsOn, RDFS.domain, EX.Service))
g.add((EX.dependsOn, RDFS.range,  EX.Service))

# owns의 주체는 Person, 대상은 Service
g.add((EX.owns, RDFS.domain, EX.Person))
g.add((EX.owns, RDFS.range,  EX.Service))

# --- 2. 지식그래프(데이터): 사실만 넣는다 ------------------
g.add((EX.OrderApi,    EX.dependsOn, EX.AuthService))
g.add((EX.AuthService, EX.dependsOn, EX.VertexAI))
g.add((EX.VertexAI,    EX.dependsOn, EX.GcpIam))
g.add((EX.alice,       EX.owns,      EX.OrderApi))

print("추론 전 트리플:", len(g))

# --- 3. 추론 엔진 가동 ------------------------------------
DeductiveClosure(OWLRL_Semantics).expand(g)
print("추론 후 트리플:", len(g))

# --- 4. SPARQL로 질의 -------------------------------------
q = """
PREFIX ex: <http://example.com/ns#>
SELECT ?target WHERE { ex:OrderApi ex:dependsOn ?target . }
"""
for row in g.query(q):
    print(" -", str(row.target).split("#")[-1])

실행 결과다.

추론 전 트리플: 11
추론 후 트리플: 154
 - AuthService
 - VertexAI
 - GcpIam

OrderApi → VertexAIOrderApi → GcpIam넣은 적이 없는 사실이다. 이행성 공리 한 줄이 만들어냈다. 같은 그래프에 ?s a ex:Service를 물으면 OrderApi, AuthService, VertexAI, GcpIam 네 개가 전부 나오고, ?s a ex:Person을 물으면 alice가 나온다. 이 역시 타입을 지정한 적이 없고, dependsOnowns의 정의역·치역 선언에서 도출된 것이다.

주목할 것은 트리플 수다. 직접 넣은 사실은 11개인데 추론을 돌리고 나면 154개가 된다. 늘어난 143개 중 대부분은 OWL 표준 자체를 기술하는 메타 트리플이라 놀랄 필요는 없다. 다만 이 팽창이 그대로 대규모 그래프에서의 성능 문제가 된다. 실제 서비스에서는 전체 추론(materialization)을 미리 돌려둘지, 질의 시점에 필요한 만큼만 추론할지가 중요한 설계 결정이 된다.

그런데 왜 지금 다시 주목받나 - 벡터 RAG의 한계

온톨로지는 2000년대 시맨틱 웹 시절의 유물처럼 취급되다가 최근 몇 년 사이 되살아났다. 이유는 하나다. 벡터 검색 기반 RAG가 부딪힌 벽 때문이다.

벡터 RAG와 GraphRAG의 검색 방식 비교

벡터 검색은 '비슷함'을 찾고, 그래프 검색은 '연결'을 따라간다. 둘은 대체재가 아니라 보완재다.

벡터 RAG가 구조적으로 어려워하는 질문들이 있다.

  • 다단계 관계 추적 — "결제 서비스가 의존하는 모든 것들의 의존성" 같은 질문. 답이 한 문서에 모여 있지 않다.
  • 부정과 집합 연산 — "A 프로젝트에는 참여했지만 B에는 참여하지 않은 사람". 벡터 공간에 '아님'을 표현할 자리가 없다.
  • 전역적 요약 — "이 코드베이스 전체의 아키텍처 방향은?" top-k 청크만 보는 방식으로는 애초에 전체를 볼 수 없다.

근본 원인은 청킹 과정에서 관계가 잘려나간다는 것이다. 문서를 500자씩 자르는 순간, 문서 3쪽의 "이 서비스"와 12쪽의 "해당 모듈"이 같은 것을 가리킨다는 정보는 사라진다. 그래프는 그 연결을 명시적으로 붙잡아 둔다.

그래서 나온 절충안이 GraphRAG다. 문서에서 LLM이 엔티티와 관계를 추출해 그래프를 만들고, 질의할 때는 벡터로 진입점을 찾은 뒤 그래프를 따라 이웃을 확장해 컨텍스트를 구성한다. 다만 문서 수만큼 LLM 호출이 들어가므로 구축 비용이 만만치 않다는 점은 감안해야 한다.

그래서, 우리 프로젝트에도 필요한가

솔직한 판단 기준을 정리하면 이렇다.

상황판단
관계 질의가 도메인의 본질 (조직도, 의존성, 규제 준수, 의료)도입 가치 높음
답이 틀리면 안 되고 근거를 제시해야 함도입 가치 높음
데이터가 여러 소스에 흩어져 있고 동일 개체 통합이 필요도입 가치 높음
문서 Q&A 수준벡터 RAG로 충분
스키마가 매주 바뀌는 초기 단계과잉 - 유지보수가 개발 속도를 잡아먹는다
도메인 스키마를 정의해 줄 사람이 없음과잉 - 시작하지 않는 편이 낫다

현실적인 중간 지점도 있다. OWL 완전 추론까지 가지 않고, 엔티티 타입 10~20개와 관계 타입 몇 개만 정의한 뒤 그래프 DB에 넣고 벡터 인덱스를 병행하는 방식이다. LLM에게 자연어를 그래프 질의로 번역시키면, 완전한 형식 논리가 주는 효용의 상당 부분을 훨씬 적은 비용으로 얻을 수 있다. 대부분의 팀에게는 이쪽이 정답에 가깝다.

정리

  • 온톨로지 = 클래스 + 인스턴스 + 속성 + 공리. 이 중 공리가 본질이다.
  • DB 스키마와의 차이는 추론. 저장하지 않은 사실이 규칙에서 도출된다.
  • 온톨로지(규칙) → 지식그래프(사실) → 저장소(구현) 순서로 설계한다. 반대로 가면 안 된다.
  • LLM 시대에 되살아난 이유는 벡터 RAG가 관계를 다루지 못하기 때문이다.
  • 시작은 가볍게. 타입 10~20개짜리 얕은 온톨로지도 충분히 값을 한다.

이번 글은 개념 정리에 집중했다. 이어지는 글에서는 실제 문서 뭉치로 GraphRAG 파이프라인을 구축하는 과정, 그리고 이 구조를 AI 에이전트의 장기 기억에 적용해 시간에 따라 변하는 사실("예전엔 X였지만 지금은 Y")을 모순 없이 다루는 방법을 다뤄볼 생각이다.

위 예제 코드는 rdflib 7.6, owlrl 7.6 환경에서 실제로 실행해 출력까지 확인했다. 라이브러리 버전에 따라 추론 후 트리플 수는 달라질 수 있다.

함께 보면 좋은 글: 로컬 LLM 제대로 활용하는 법 — 클라우드 AI와 역할 분담, RAG와 파인튜닝까지

※ 이 글의 라이브러리 버전·추론 결과·도구 목록은 2026년 기준이며 변경될 수 있습니다.

댓글

이 블로그의 인기 게시물

메일 주소 하나 만들려다 브랜드를 세웠다 — 1인 스튜디오 브랜딩 실전기

클로드 Fable 5와 Opus 4.8 비교 - 차이점, 가격, 무료 기간 종료 후 변화까지 총정리 (7월 최신)

구글 labs-fx - 제미나이 - 클라우드 차이점 알아보기