XGEN 15일 무료 체험 — 설치 없이 브라우저에서 바로 시작하세요체험 신청
PlateerAI Labs
·Reading Time | 2 min
작성 | 김진수

CSV 행은 줄이고 정상 클래스는 지키는 방법(5편)

사실 테이블 행을 생략하는 모델링과 고아 클래스 탐지·삭제를 분리해, 크기 최적화가 의미 손실로 이어지지 않게 합니다.

CSV 행은 줄이고 정상 클래스는 지키는 방법(5편) — 커버 일러스트Series · 온톨로지 개발기시리즈 5번째 글 · 전체 10편 보기

지식그래프 설계 · 5/10

커머스 CSV를 그대로 RDF로 바꾸자 그래프 대부분을 주문과 판매 이력 행이 차지했습니다. 테이블을 클래스로, 컬럼을 속성으로, 외래키를 관계로 옮긴 결과는 원본에 충실했지만, 상품과 분류 같은 기준 개체를 탐색하기 위해 거대한 사실 기록을 함께 읽어야 했습니다.

반대 방향의 문제도 생겼습니다. 그래프를 정리하려고 인스턴스와 관계가 부족한 클래스를 자동 삭제했더니 약 1,500개의 정상 클래스까지 사라졌습니다. 추출 단계에서 아직 관계를 충분히 만들지 못한 개념을 후처리가 불필요한 개념으로 판단한 것입니다.

두 문제 모두 그래프를 줄이는 작업처럼 보이지만 해결 기준은 달랐습니다. 정형 행을 남길지는 테이블의 역할로 결정해야 하고, 고아 클래스를 지울지는 탐지의 신뢰도와 삭제 권한으로 결정해야 했습니다. 그래서 기준 정보와 사실 기록의 모델링, 고아 탐지와 실제 삭제를 각각 분리했습니다.

기준 정보와 사실 기록을 다르게 모델링했습니다

상품, 색상, 분류처럼 여러 테이블에서 반복 참조되는 데이터는 기준 정보입니다. 각 행을 개체로 남기면 상품에서 색상으로, 분류에서 상품으로 관계를 따라갈 수 있습니다.

주문, 판매 이력, 로그처럼 시간에 따라 누적되는 데이터는 사실 기록입니다. 집계와 원문 조회에는 중요하지만 모든 행이 장기 그래프의 중심 개체일 필요는 없습니다. 이런 테이블은 클래스와 컬럼, 외래키 구조를 남기고 행은 별도 원본 저장소가 맡도록 했습니다.

판정은 테이블 이름보다 참조 구조를 우선합니다. 다른 테이블이 참조하는 대상이면 기준 정보 후보이고, 여러 기준 테이블을 연결하면서 주로 참조를 내보내는 쪽이면 사실·연결 테이블 후보입니다. 다만 실제 구현은 외래키 방향만 쓰지 않고 행 수가 작은 테이블을 보수적으로 유지하는 guard도 둡니다. 이 행 수 기준은 도메인 불변식이 아니라 오판 비용을 줄이기 위한 운영 휴리스틱입니다. 단일 파일만 들어와 전체 참조 방향을 알 수 없을 때도 행을 유지합니다.

이 규칙을 같은 데이터에 적용했을 때 개체는 13,798개에서 286개로 줄었고, 대량 사실 행 관계 대신 기준 개체와 스키마 관계가 남았습니다. 중요한 검증은 감소율이 아니었습니다. 기준 개체가 남아 있는지, 외래키 관계가 올바른 방향으로 연결됐는지, 사실 테이블의 스키마가 보존됐는지를 함께 확인했습니다.

고아 탐지와 삭제를 분리했습니다

인스턴스가 없고, 속성의 도메인이나 레인지로 쓰이지 않으며, 상하위 관계도 없는 클래스는 고아 후보로 볼 수 있습니다. 그러나 관계 추출이 아직 불완전하다면 정상 개념도 같은 모양을 가집니다.

자동 후처리가 고아 후보를 바로 지우면 앞 단계의 누락을 데이터 정리로 오해하게 됩니다. 실제 대형 그래프에서도 자동 삭제를 멈추고 탐지만 남겼을 때 1,568개의 후보가 감지됐고, 이전 결과보다 1,547개의 클래스가 보존됐습니다.

기본 동작을 삭제에서 후보 수 보고로 바꿨습니다. 현재 통계에는 orphans_detected 같은 개수만 남고, 후보 URI와 판정 근거는 영속화되지 않습니다. 구현으로 확보한 안전선은 자동 삭제를 끈 데까지입니다.

안전한 삭제에는 참조 무결성 계약이 더 필요합니다

노드 수가 줄었다고 정리가 끝난 것은 아닙니다. 삭제 대상 URI를 주어나 목적으로 가진 트리플, 속성의 도메인과 레인지, 스키마 저장소의 레코드를 함께 확인해야 합니다. 일부 참조만 남으면 화면에서는 노드가 사라져도 스키마 안에는 끊어진 관계가 생깁니다.

현재 명시 삭제 경로는 대상 URI가 주어인 outgoing 트리플을 제거하지만, 그 URI를 목적으로 가리키는 incoming 관계까지 원자적으로 정리하지 않습니다. 따라서 후보 이력, incoming 참조 검사, 스키마 레코드 정리를 하나의 삭제 계획으로 만들기 전에는 운영 자동 삭제를 켜지 않는 편이 안전합니다. 다음 빌드에서 후보가 사라졌는지 비교할 URI와 판정 근거를 남기는 기능도 후속 과제입니다.

무거운 후처리는 제어 API와 실행 자원을 나누었습니다

중복 정리와 품질 검사가 늘면서 3편에 남아 있던 실행 문제도 커졌습니다. 문서 청크 스캔, 스키마 전체 조회, CSV 변환을 백그라운드 코루틴에서 동기로 실행하면 하나의 작업이 이벤트루프를 점유합니다. 작업 상태를 별도 저장소에 두었어도 진행률 API가 응답하지 않으면 사용자는 취소와 완료를 확인할 수 없습니다.

입력 크기에 따라 시간이 늘어나는 Qdrant 스캔, 데이터베이스 조회, CSV 변환, 스키마 저장은 워커 스레드로 넘겼습니다. 빌드당 몇 번만 실행되는 짧은 상태 갱신은 이벤트루프에 남겼습니다. 모든 함수를 기계적으로 분리하지 않고 점유 시간이 데이터 양에 비례하는 구간을 골랐습니다.

26,934개 청크를 스캔한 로컬 조건에서는 25.6초 동안 진행 상태를 50회 조회했고 평균 12ms, 최대 57ms로 응답했습니다. 이 값은 해당 장비의 결과이지 일반적인 성능 보장은 아닙니다. 검증 목적은 무거운 빌드 단계가 실행되는 동안 제어 API도 함께 멈추지 않는지 확인하는 것이었습니다.

정형 그래프를 작게 만드는 것과 자동 삭제를 안전하게 만드는 일은 같은 원칙을 공유합니다. 데이터가 많거나 연결이 적다는 모양만 보고 줄이지 않고, 데이터의 역할과 구조가 완성된 범위를 기준으로 변경합니다.

이 단계에서는 정형 그래프의 생성 규칙을 고쳤지만, 빌드 끝의 의미 기반 중복 정리가 정형 클래스와 문서 개념을 구분하지 못하는 한계가 남아 있었습니다. 이 문제는 실제 반복 빌드에서 서로 다른 테이블 클래스가 합쳐진 뒤 확인됐고, 8편에서 생성 출처를 영속화하는 방식으로 해결합니다.

이제 그래프 구조를 검색에 사용할 준비가 됐습니다. 다음 편에서는 2편에서 만든 멀티턴 ReAct 검색을 실제 질문 세트로 측정하고, 어떤 질문에는 그래프가 필요하고 어떤 질문에는 루프가 불필요한지 확인한 뒤 검색 구조를 바꿉니다.


이전 편 → 트리플 수로는 지식그래프 품질을 알 수 없었습니다 다음 편 → 빈 그래프를 측정한 A/B에서 시작한 검색 재설계

#온톨로지#정형 데이터#데이터 무결성
블로그 목록으로