도메인 온톨로지와 구조 온톨로지 — 이름을 고정하는 일과 경계를 고정하는 일
온톨로지를 한 덩어리로 다루면 논의가 엉킨다. 실제로는 서로 다른 것을 고정하는 두 층이고, 무너지는 방식도 다르다.
- 도메인 온톨로지는 무엇을 무엇이라 부를 것인가를 고정한다 → 식별자 · 분류체계 · 통제어휘
- 구조 온톨로지는 그 어휘로 만든 문장이 참인지 어떻게 판정하는가를 고정한다 → 의미 · 제약 · 경계
여섯 관점을 표준 사양과 실제로 터진 사고로 하나씩 붙여본다. 사고는 전부 내가 운영하는 K3s 홈랩과 정산 시스템에서 나온 것이다.
I. 도메인 온톨로지 — 이름을 고정한다
1) 식별자 — 이름은 바꾸는 게 아니라 별칭을 선언하는 것이다
RDF에서 자원을 가리키는 것은 IRI다. 여기서 중요한 건 IRI가 이름이지 주소가 아니라는 점이다. 그리고 OWL은 고유이름 가정(Unique Name Assumption)을 두지 않는다 — 서로 다른 두 IRI가 같은 대상을 가리킬 수 있고, 그걸 말하려면 owl:sameAs로 선언해야 한다.12
이게 추상적으로 들리면 실물을 보자. 내 정산 시스템의 운영 호스트는 jen.lemuel.co.kr 인데, 나중에 settlement.lemuel.co.kr 이 붙었다. 통짜 rename이 깔끔해 보였지만 일부러 안 했다. 이미 발행한 블로그 글 9편이 jen 을 가리키고 있었기 때문이다. 이름을 바꾸면 그 9개 참조가 전부 끊긴다.
그래서 택한 게 별칭이다. 두 이름이 같은 것을 가리킨다고 선언하고, 옛 이름을 살려뒀다. owl:sameAs 가 하는 일과 구조적으로 같다.
그런데 별칭이 안 통하는 자리가 있었다. APP_BASE_URL — 발송 메일 본문에 박히는 링크다. 여기는 “둘 중 하나를 골라 문자열로 굳히는” 자리라서 별칭이라는 개념이 성립하지 않는다. 식별자 관점의 실무 규칙이 여기서 나온다.
별칭으로 해결되는 자리와, 어느 하나를 정본으로 골라야만 하는 자리를 먼저 갈라라. 후자를 못 찾으면 rename은 반드시 새는 곳이 생긴다.
2) 분류체계 — 상위/하위 관계는 생각보다 미끄럽다
분류를 만들 때 대부분 rdfs:subClassOf 같은 걸 떠올리지만, 사전·주제어 체계에는 별도 표준이 있다. W3C SKOS다.3
SKOS에서 제일 자주 놓치는 설계 결정이 이거다.
“to support this usage convention, the properties
skos:broaderandskos:narrowerare not declared as transitive properties.”
skos:broader 는 일부러 이행적(transitive)이지 않다. 직접 상위 한 칸만 뜻한다. 조상을 따라 올라가고 싶으면 skos:broaderTransitive 를 따로 쓰는데, 그것도 “직접 서술하지 말고 추론으로만 얻어라”는 관례가 붙어 있다.
왜 이렇게 만들었나. 주제어 체계에서 “상위”는 여러 종류의 관계가 섞여 있기 때문이다. 종-속 관계, 부분-전체 관계, 사례 관계가 전부 “broader”라는 한 단어에 들어간다. 그걸 무비판적으로 이행 처리하면 3~4단계 올라간 시점에 명백히 거짓인 문장이 나온다. 그래서 표준이 기본값을 “이행 안 함”으로 잡고, 이행이 필요한 쪽이 명시적으로 요청하게 만들었다.
반대 방향의 실패도 겪었다. 내 개인 지식베이스 ~/wiki 는 문서를 위키링크로 잇는데, 실측해보니 엣지 108개가 전부 references 한 타입이었다. 관계 타입이 하나면 분류체계가 아니라 링크 목록이다. “A가 B를 반증한다”, “A는 B의 특수한 경우다” 같은 질의가 원리적으로 불가능해진다.4
정리하면 분류체계의 실패는 양쪽에서 온다 — 관계 타입이 하나로 뭉개지거나, 성격이 다른 관계를 하나로 뭉쳐놓고 이행성을 함부로 주거나.
3) 통제어휘 — 국내에 이미 법정 표준이 있다
통제어휘(controlled vocabulary)는 “같은 것은 같은 말로 부른다”는 합의다. SKOS는 이걸 라벨로 다룬다 — 개념 하나에 언어별 skos:prefLabel 은 하나, 나머지 표현은 skos:altLabel, 검색에만 걸리고 노출은 안 할 것은 skos:hiddenLabel.3
그런데 이 구조는 국내 공공데이터 표준에 이미 법적 근거를 갖고 들어와 있다. 「공공데이터법」 제23조(공공데이터의 표준화)와 「공공기관의 데이터베이스 표준화 지침」 제11~13조에 근거해 행정안전부가 공통표준용어를 고시한다.56
구조가 꽤 정교하다.
| 규격 요소 | 내용 | SKOS 대응 |
|---|---|---|
| 공통표준단어 | 용어명을 조합하는 최소 어휘 | 어휘 원자 |
| 공통표준도메인 | 도메인분류명+타입+길이 (예: 연월일C8, 금액N15, 여부C1) |
— (제약 쪽) |
| 허용값 | 도메인이 가질 수 있는 값 범위 (예: 월 01~12, 여부 Y/N) |
— (제약 쪽) |
| 용어 이음동의어 목록 | 같은 용어의 다른 한글명 관리 | skos:altLabel |
| 개정구분명(폐기/변경)·개정사유 | 어휘의 폐기 이력 | 어휘 생애주기 |
| 저장 형식 / 표현 형식 | YYYYMM(202002) vs YYYY-MM(2020-02) |
— |
주목할 게 두 개다.
첫째, 이음동의어 필드가 규격에 처음부터 있다. 통제어휘를 “하나만 남기고 나머지를 금지하는 것”으로 오해하기 쉬운데, 실제 규격은 대표어를 정하되 동의어를 버리지 않고 관리한다. SKOS의 prefLabel/altLabel과 같은 판단이다. 현장의 말을 지우면 통제어휘를 아무도 안 쓴다.
둘째, 저장 형식과 표현 형식을 분리해뒀다. 같은 값을 기계가 담는 방식과 사람에게 보이는 방식이 다르다는 걸 어휘 규격 수준에서 못박은 것이다.
통제어휘가 없으면 생기는 일
이름 공간에 통제가 없으면 조용히 충돌한다. 겪은 사고 하나.
쿠버네티스는 Pod을 띄울 때 기존 Service 정보를 환경변수로 자동 주입한다. 그런데 Service 이름이 앱 설정 프리픽스와 겹치면 <NAME>_PORT 같은 변수가 앱이 읽는 설정을 덮어써서 포트를 0 으로 만든다. 더 나쁜 건 발현 조건이다 — Pod이 Service보다 먼저 떠 있으면 주입이 안 되니까 멀쩡하다. 그 Pod을 재시작하는 날에만 죽는다. 배포가 아니라 노드 재부팅 같은 무관해 보이는 사건이 방아쇠가 된다.
이건 코드 버그가 아니라 두 어휘 공간(플랫폼 예약 이름 vs 앱 설정 이름)이 서로의 존재를 모르는 문제다. 통제어휘의 부재가 만드는 전형적인 사고다.
II. 구조 온톨로지 — 판정 규칙을 고정한다
4) 의미 — “무엇이 따라 나오는가”
OWL 2는 클래스·프로퍼티·개체에 형식 의미론을 부여해서 적혀 있지 않은 사실을 이끌어낸다(entailment). 사양은 Direct Semantics(기술논리 SROIQ 기반)와 RDF-Based Semantics 두 가지를 정의하고, 추론 비용을 감당 가능하게 자르려고 EL·QL·RL 프로파일을 따로 둔다.2
의미론이 하는 일을 실무 언어로 옮기면 이거다. 어떤 단어를 쓰기로 했으면, 거기서 따라 나오는 것도 같이 받아들이기로 한 것이다.
여기서 터진 사고. 배포 파이프라인에서 이미지 태그 갱신 정책을 newest-build 로 두고 허용 태그 필터를 안 걸었다. “가장 최신 빌드”라는 말의 의미가 정의되지 않은 채 남은 것이다. 결과는 운영 네임스페이스가 개발 브랜치 이미지로 하루에 여러 번 왕복했고, 자동 롤백이 참조하는 “정상 태그 목록”까지 오염됐다. 롤백 대상 자체가 잘못된 이미지가 된다.
더 아픈 사례도 있다. 우리는 main 브랜치를 “배포 가능”이라는 뜻으로 써 왔는데, 실측해보니 main 빌드는 운영 프로파일에서 아예 기동하지 못했다. 결제 어댑터 배선이 빠져 CrashLoop였다. 즉 main = 배포 가능 이라는 의미 규칙이 거짓인 채로 오래 유지되고 있었다.
의미론의 실무 버전은 “이 이름이 참이라고 주장하는 게 뭔지 적고, 그게 실제로 참인지 재는 것” 이다. 안 재면 이름은 자기 뜻을 잃는다.
5) 제약 — 추론과 검증은 다른 일이다
OWL만으로는 “이 데이터가 규격을 지켰는지”를 못 본다. 열린 세계를 전제로 추론하는 언어이지 검증하는 언어가 아니기 때문이다. 적혀 있지 않은 것은 거짓이 아니라 그냥 모르는 것이다.
그래서 W3C는 SHACL을 따로 냈다. RDF 그래프를 조건 집합에 대해 검증하고, 조건을 담은 shapes graph 와 검사 대상 data graph 를 분리한다. Core는 필수 구현, SPARQL 확장은 선택이다.7
제약이 없거나 어긋났을 때의 실물 두 개.
하나. 정산 원장 테이블 account_entries 에 값 범위를 강제하는 CHECK 제약이 걸려 있었는데, 새 항목 유형을 추가하면서 그 CHECK를 같이 넓히는 걸 잊었다. 결과는 부분 실패가 아니라 해당 유형 insert 전멸이다. 제약은 지켜지든 전부 막든 둘 중 하나라, 어긋나면 조용히 나빠지지 않고 한꺼번에 터진다. 이게 제약의 장점이자 함정이다.
둘. JPA @Query 의 파라미터와 @Param 이름이 어긋나는 문제. 컴파일은 통과하고 런타임에 42P18(파라미터 타입 결정 실패)로 터지면서 로그 파이프라인까지 폭주시켰다. 해결은 개별 수정이 아니라 빌드 게이트였다 — -parameters 를 전역으로 켜고, @Query 의 바인딩 이름과 @Param 을 대조하는 검사를 빌드에 넣었다.
여기서 SHACL의 설계 판단이 그대로 반복된다. 제약은 문서가 아니라 별도 실행 단계여야 한다. shapes graph를 data graph에서 분리한 이유와, 검증을 리뷰가 아니라 빌드에 건 이유가 같다.
6) 경계 — “어디까지가 이 온톨로지의 관할인가”
가장 자주 빠뜨리는 관점이다. RDF 1.1은 이걸 데이터 모델 수준에서 다룬다. RDF 데이터셋은 이름 없는 default graph 하나와 0개 이상의 named graph 로 구성되고, 그래프 이름은 데이터셋 안에서 유일하다.1
사양에 있는 이 경고가 특히 중요하다.
“Despite the use of the word ‘name’ in ‘named graph’, the graph name is not required to denote the graph.”
이름이 붙어 있다고 그 이름이 그 그래프를 가리킨다는 보장이 없다는 것. 경계는 선언한다고 생기는 게 아니라 별도로 확인해야 하는 것이라는 얘기다.
실물 사고 세 개가 전부 이 관점이다.
- dependabot 설정은 기본 브랜치에서만 읽힌다.
develop에 넣어두고 몇 달을 “돌고 있다”고 믿었다.main이 1,370 커밋 뒤처져 있었으니 설정은 존재했지만 관할 밖이었다. 실패도 아니고 경고도 아니고, 그냥 아무 일도 안 일어난다. - 한 리포가 다른 리포의 부분집합 사본이었다. 프론트엔드를 통째로 복사해온 탓에, 그 리포에 존재하지 않는 서비스를 계속 호출했다. 코드는 정상이고 호출도 정상인데 경계 밖을 부르고 있었다.
- SPA 라우트가 nginx 프록시 목록과 겹쳤다. 화면 URL이 백엔드 프록시 경로 목록에 들어 있으면 링크 이동은 되는데 새로고침에서만 깨진다. 게다가 nginx 설정이 세 벌로 흩어져 있고 운영이 쓰는 건 그중 하나였다. 경계가 한 군데 안 적혀 있으면 경계가 아니다.
세 사고의 공통점: 에러가 안 난다. 의미 위반은 이상한 답을 내고 제약 위반은 터지지만, 경계 위반은 조용하다. 그래서 제일 늦게 발견된다.
두 층을 겹쳐 보면
| 도메인 온톨로지 | 구조 온톨로지 | |
|---|---|---|
| 고정하는 것 | 무엇을 무엇이라 부를 것인가 | 그 말이 참인지 어떻게 판정할 것인가 |
| 관점 | 식별자 · 분류체계 · 통제어휘 | 의미 · 제약 · 경계 |
| 대표 표준 | SKOS, 공통표준용어 고시 | RDFS/OWL 2, SHACL, RDF Dataset |
| 무너질 때 증상 | 같은 것을 다르게 부른다 / 이름이 충돌한다 | 틀린 게 따라 나온다 / 조용히 관할 밖이다 |
| 발견 난이도 | 중간 (부딪히면 보인다) | 경계가 최악 (아무 일도 안 일어난다) |
실무에서 온톨로지 얘기를 하면 대개 도메인 쪽에서 멈춘다. 용어집을 만들고 코드값을 정리하고 끝낸다. 그런데 위에 적은 사고 여섯 건 중 넷이 구조 쪽, 그중 셋이 경계였다.
그래서 순서를 이렇게 잡는 게 맞다고 본다.
- 식별자부터. 이름은 나중에 못 바꾼다. 별칭으로 흡수되는 자리와 정본을 골라야 하는 자리를 먼저 분리한다.
- 통제어휘는 동의어를 버리지 않는다. 대표어를 정하되 현장의 말을
altLabel로 남긴다. 공공 표준도 그렇게 한다. - 분류는 관계 타입부터 늘린다. 타입 하나짜리 그래프는 그래프가 아니다. 그리고 성격이 다른 관계에 이행성을 함부로 주지 않는다.
- 의미는 재서 확인한다.
main = 배포 가능같은 규칙은 주장이 아니라 측정 대상이다. - 제약은 빌드에 건다. 문서에 적힌 제약은 제약이 아니다.
- 경계는 명시적으로 쓰고, 관할 밖을 검사한다. 조용한 실패가 기본값이므로, “적용되고 있는가”를 따로 확인하는 절차가 없으면 없는 것과 같다.
같은 주제를 다른 축으로 쪼갠 글: AI 온톨로지 구축 — 그래프와 벡터DB, 두 관점이 각각 무엇을 포기하는가
References
-
W3C, RDF 1.1 Concepts and Abstract Syntax, W3C Recommendation, 2014-02-25 (IRI, RDF dataset, named graph). https://www.w3.org/TR/rdf11-concepts/ ↩ ↩2
-
W3C, OWL 2 Web Ontology Language Document Overview (Second Edition), W3C Recommendation, 2012-12-11. https://www.w3.org/TR/owl2-overview/ ↩ ↩2
-
W3C, SKOS Simple Knowledge Organization System Reference, W3C Recommendation, 2009-08-18 (§5 Lexical Labels, §8 Semantic Relations). https://www.w3.org/TR/skos-reference/ ↩ ↩2
-
~/wiki엣지 타입 통계 등 본문의 운영 수치는 필자 개인 환경 실측값이며 일반화된 벤치마크가 아니다. 상세는 LLM-Wiki 지식베이스를 측정해봤다. ↩ -
행정안전부, 공공데이터 공통표준용어 (공공데이터포털 개방 데이터, 8차 제·개정 2025.11 기준) — 공통표준단어·공통표준도메인·허용값·용어 이음동의어 목록·개정구분명 등 항목 정의. https://www.data.go.kr/data/15156379/fileData.do ↩
-
행정안전부, 공공기관의 데이터베이스 표준화 지침 (행정규칙, 국가법령정보센터). 근거 법률은 「공공데이터의 제공 및 이용 활성화에 관한 법률」 제23조(공공데이터의 표준화). https://www.law.go.kr/LSW/admRulInfoP.do?admRulSeq=2100000255382&chrClsCd=010201 ↩
-
W3C, Shapes Constraint Language (SHACL), W3C Recommendation, 2017-07-20. https://www.w3.org/TR/shacl/ ↩