Skip to content

데이터 모델 ​

핵심은 개념(Term)과 표기(Surface)의 분리다. 엑셀이 무너진 이유는 한 행이 개념이자 표기였기 때문이다. 이를 나누면 세 가지 검증이 모두 같은 테이블 조회로 풀린다.

terms — 하나의 개념 ​

컬럼타입설명
iduuidPK
slugtext유니크. URL 식별자
quality_profileenum기존 API·리비전 호환용. 새 판정은 용어의 상태와 내용에서 자동 계산
name_en, name_kotext목록·제목에 쓸 대표 표기. 최소 하나는 있어야 한다
full_name_en, full_name_kotext대표 풀네임 또는 확장명
domaintext[]ISP, HW, SW, Optics, PM … 동음이의어 구분축
categorytext[]business_categories.key 목록. 쓰기 API가 존재 여부를 검증
tagstext[]여러 용어에 재사용하는 자유 입력 태그. 기존 topic 값은 첫 태그로 이전
topictext기존 API 호환용 첫 태그
owner_iduuid완성을 책임질 사용자. 삭제되면 null
statusenumdraft | active | deprecated | forbidden
definition_mdtext1~2문장 정의 (API 응답·툴팁용)
body_mdtext위키 본문 (마크다운)
replaced_by_iduuiddeprecated일 때 대체 용어
created_by, updated_byuuid감사 컬럼. API 응답에 싣지 않는다
created_at, updated_attimestamptz

business_categories — 관리형 업무 분류 ​

컬럼타입설명
keytextPK. 용어와 공유 URL이 참조하는 안정적인 값
labeltext화면에 표시하는 국문 이름
label_entext영문 이름. 분류를 추가할 때 국문 이름과 함께 필수
sort_orderinteger선택 목록과 필터의 표시 순서
created_at, updated_attimestamptz

기본 설치에는 제품·고객·프로젝트·공정·설계·평가·장비·조직·시스템·기타가 들어간다. 목록은 사이드바의 분류 체계에서 확장한다. 일반 사용자도 추가하고 미사용 분류를 삭제할 수 있지만, 하나 이상의 용어가 쓰는 분류는 관리자만 삭제할 수 있다.

domains — 관리형 도메인 ​

key, 화면 이름 label, 고유한 72색 팔레트 키 color, sort_order를 가진다. 용어의 domain은 여러 도메인 key를 담는 배열이다. 업무 분류와 함께 분류 체계 화면에서 관리하며, 편집 화면에서 정의되지 않은 값을 입력하면 새 문자열을 암묵적으로 만들지 않고 해당 화면으로 안내한다.

AI 활용 설정 ​

  • workspace_settings — definition_min_chars, body_min_chars로 조직 전체의 최소 정의·본문 길이를 보관한다. 0은 내용 자체를 생략한다는 뜻이 아니라 존재 여부만 검사한다.
  • ai_config — 공급자, Base URL, 모델, 사용 여부를 담는 단일 행이다. API Key와 custom header 값은 애플리케이션이 AES-256-GCM으로 암호화한 문자열만 저장한다.
  • ai_review_suggestions — 정리 대기 용어의 현재 리비전에 대해 미리 생성한 제안과 생성기 버전을 저장한다. 용어 리비전 또는 생성기 버전이 달라지면 오래된 캐시로 취급한다.
  • ai_review_queue — 용어별 최신 AI 검토 요청을 queued, processing, ready, failed 상태로 관리한다. 자동·수동 요청 여부, 요청자와 처리 시각을 함께 기록한다.

용어의 표기·상태·내용과 전역 최소 길이를 결합해 완성도를 자동 계산한다. 자세한 판정은 AI 활용과 챗봇을 참고한다.

RAG 색인 ​

  • rag_config — RAG 사용 여부, Embedding/Reranker 공급자·Base URL·모델, 청크 크기·겹침·기본 결과 수를 담는 단일 행이다. API Key와 custom header는 AES-256-GCM 암호문만 저장한다. 벡터 차원은 1536으로 고정한다.
  • rag_documents — 현재 용어 리비전의 metadata·definition·body 청크와 SHA-256 내용 해시, vector(1536) Embedding을 저장한다. 새 리비전이 색인되면 같은 용어의 이전 청크를 교체하고, 원래 term_revisions 이력은 지우지 않는다.
  • rag_index_queue — 용어별 최신 색인 요청을 queued, processing, ready, failed 상태로 관리한다. 용어 쓰기 트랜잭션과 함께 대기열을 기록해 저장 성공 후 색인 작업이 유실되지 않게 한다.

검색은 병합되지 않은 용어의 현재 리비전만 대상으로 하며, RAG 검색 API는 이 청크와 용어 메타데이터를 함께 반환한다. Embedding 서버가 실패해도 용어 저장은 롤백되지 않고 색인 큐에 실패 상태가 남는다.

term_surfaces — 그 개념을 가리키는 모든 실제 표기 ​

컬럼타입설명
iduuidPK
term_iduuid→ terms.id (ON DELETE CASCADE)
texttext"Auto Exposure", "AE", "자동노출", "오토익스포저"
langenumen | ko | neutral
kindenumcanonical | abbreviation | full_name | alias | discouraged | forbidden
case_sensitiveboolean"AE"처럼 대소문자가 의미 있으면 true
norm_loosetext정규화 키 — 구분자 전부 제거 (autoexposure)
norm_spacetext정규화 키 — 구분자를 단일 공백으로 (auto exposure)

인덱스는 넷이다. norm_loose / norm_space B-tree(정확 매칭), norm_loose GIN + gin_trgm_ops(유사 후보), term_id. 유니크 제약은 (term_id, norm_loose, kind)다 — 같은 용어에 같은 표기를 같은 종류로 두 번 넣지 못한다.

AE 표기에는 kind=abbreviation을 지정한다.

정규화 컬럼은 손으로 채우지 않는다

norm_loose/norm_space는 @glossary/db의 surfaceKeys()가 채운다. 그 함수는 @glossary/engine의 normalizeSurface()를 그대로 호출한다. 아키텍처 § 정규화 함수의 단일 소유를 본다.

세 가지 검증이 떨어지는 방식 ​

검증판정 조건
비표준 표기 교정surface.kind가 alias/discouraged → 해당 Term의 canonical 제안
금지어·폐기어kind='forbidden' 또는 Term.status가 deprecated/forbidden → replaced_by 제안
동음이의 경고같은 정규화 키가 서로 다른 Term 2개 이상에 연결 → domain과 함께 제시

같은 표기가 여러 kind로 등록돼 있을 때 어느 것을 대표로 삼는지는 코드에 명시 고정되어 있다(apps/web/src/lib/terms/lookup.ts의 MATCH_KIND_PRIORITY).

forbidden > discouraged > canonical > abbreviation > full_name > alias

행 순서에 기대면 안 되기 때문이다. 같은 표기가 alias이자 forbidden으로 등록돼 있을 때 alias가 먼저 나오면 린터가 금지 표기를 놓친다.

term_revisions — 전체 이력 ​

변경마다 Term + Surfaces 전체를 jsonb 스냅샷으로 적재한다. diff와 롤백은 스냅샷 비교로 계산하며 diff를 따로 저장하지 않는다.

컬럼설명
term_id, revision_number유니크. 용어별로 1부터 증가
snapshotjsonb — 그 시점의 Term + Surfaces 전체
message변경 메모
author_id세션 사용자. API 키 요청이면 null
author_key_idAPI 키 요청일 때 어느 키였는지

author_key_id가 따로 있는 이유는 API 키로 만든 리비전의 author_id가 항상 null이라 누가 썼는지 구분할 수 없었기 때문이다. 나중에 채울 수 없는 값이라 처음부터 넣었다.

revision_number는 낙관적 잠금의 기준이기도 하다. 편집 화면이 읽은 번호를 expectedRevision으로 되돌려 보내고, 그 사이 남이 고쳤으면 서버가 409 revision_conflict를 돌려준다. 자세한 것은 용어 API에 있다.

term_relations — 승인된 용어 관계 ​

AI가 찾은 관계는 바로 검색 그래프에 넣지 않고 proposed로 저장해 사람이 검토할 수 있게 한다. RAG의 그래프 확장에는 approved 관계만 사용한다.

컬럼설명
source_term_id, target_term_id관계의 출발·도착 용어. 같은 용어끼리는 연결할 수 없음
relation_typerelated_to, is_a, part_of, used_in, prerequisite_of, replaces
statusproposed, approved, rejected
confidence제안 신뢰도 0~100. 검색 확장 가중치에도 사용
evidence_md관계를 제안하거나 승인한 근거
source_revision, target_revision어느 용어 리비전을 보고 제안했는지 기록
created_by, reviewed_by제안·검토 사용자

같은 (source, target, type) 관계는 한 행만 존재하며 상태를 바꿔 다시 검토한다. 용어가 삭제되면 연결도 함께 삭제된다.

/graph?view=semantic의 의미 관계 관리에서 사람이 직접 제안하고 근거·종류를 수정하거나 승인·거절할 수 있다. 등록은 proposed이며, 승인된 관계를 수정하면 다시 proposed로 돌아간다. 거절은 물리 삭제가 아니므로 수정 후 재검토할 수 있다. reviewed_by와 reviewed_at은 마지막 사람의 검토·수정자를 기록하며, 모든 변경의 별도 감사 로그는 아직 제공하지 않는다.

의미 그래프와 챗봇은 동일한 판정으로 approved이며 양쪽 용어 리비전이 현재와 일치하는 관계만 사용한다. 용어가 바뀌면 관리 목록에 재검토 필요를 표시하며, 최신 정의를 확인해 수정 후 다시 승인해야 한다. 리비전이 기록되지 않은 기존 관계는 종전처럼 사용할 수 있고 다음 승인 시 현재 리비전을 기록한다. 분류 관계 보기는 도메인·업무 분류·태그 허브다.

관계 쓰기는 로그인한 사용자만 가능하고 API 키는 조회만 지원한다. 현재 모든 로그인 사용자가 검토할 수 있으며 제안자와 승인자를 분리하는 조직별 결재 정책은 포함하지 않는다. 동시 수정은 관계 버전과 양쪽 용어 리비전으로 검사한다. 직접 관리한 관계의 근거를 AI가 덮어쓰지 않으며, 변경된 관계의 이전 AI 제안은 검토 캐시에서 제거한다.

ontology_predicates — 관계 의미 카탈로그 ​

ontology_predicates는 그래프의 실제 연결을 복제하지 않고, 관계가 어떤 의미를 갖는지 선언한다. term_relations의 기존 승인·리비전 검증과 wiki_page_terms의 공개 상태를 그대로 사용하므로 별도의 트리플스토어 없이도 검색과 챗봇이 같은 관계 규칙을 공유한다.

컬럼설명
key, label모델과 화면에서 사용할 안정적인 관계 키·국문 이름
inverse_key반대 방향으로 읽을 때 사용할 관계 키. is_a ↔ has_subtype처럼 쌍으로 관리
symmetric, transitive대칭·추이 관계 여부. 검색 확장은 무제한 추론을 하지 않고 최대 2-hop으로 제한
source_kind, target_kindterm 또는 wiki_page. 위키는 defines·applies_to로 용어를 연결
description, sort_order관계 작성·검토 화면에 표시할 설명과 목록 순서

기본 카탈로그에는 기존 6가지 용어 관계의 역관계와 위키의 defines·applies_to가 함께 들어간다. 애플리케이션은 마이그레이션 직후 잠시 카탈로그 조회가 실패해도 내장 기본값으로 검색을 계속할 수 있다.

인증 테이블 ​

  • users — email(유니크), name, password_hash, role(admin | editor | viewer), external_id(OIDC/OAuth 사용자 식별자), sso_groups.
  • sessions — id(쿠키에 실리는 값), user_id, expires_at.
  • api_keys — 해시만 저장한다. prefix(유니크)로 식별하고 scopes로 제한하며 revoked_at/expires_at으로 수명을 관리한다.
  • sso_config — 활성 mode(disabled | oidc | oauth2 | oauth2-proxy), ID/비밀번호 로그인 허용 여부, 직접 연결용 protocol, Issuer/JWKS/엔드포인트, 클라이언트 정보, claim 후보와 접근 그룹을 담는 단일 설정 행. 클라이언트 시크릿은 API 응답에 내보내지 않는다.

중복 방지 ​

두 단계로 잡는다.

  1. 등록 시점 — 정규화 키 충돌을 즉시 경고한다. 동음이의어는 허용하되 사람이 반드시 확인하게 한다. 그래서 POST /terms는 409를 던지지 않고 201 응답에 warnings를 실어 보낸다. 등록을 막으면 설계 결정과 어긋난다.
  2. 별도 리포트 — pg_trgm 유사도 기반 "잠재 중복" 목록. 병합은 merge(source → target)으로 surface를 이관하고 옛 slug는 리다이렉트로 남긴다(M3).

보조 데이터 ​

  • Attachment / AttachmentRef — WebP로 변환한 content-addressed 첨부 이미지. 현재 본문 참조는 AttachmentRef로 동기화되며, 보존 기간이 지난 미참조 실체만 워커가 정리한다. 리비전 스냅샷에서 참조되는 첨부는 보존한다.
  • UnregisteredCandidate — 문서 검증에서 발견한 표기를 정규화 키별로 누적한다. 발생 횟수, 샘플 문맥, 마지막 출처를 보존하고 open → dismissed | promoted 상태로 사람의 결정을 기록한다.

사내망 온프레미스 배포를 전제로 만든 Apache-2.0 프로젝트입니다.