Procedure Hub 목표 임상 데이터 ERD¶
상태: 논리 모델·물리 baseline 구현 완료 · 2026-09-03 범위: Procedure Hub가 관리·공급하는 임상 콘텐츠와 판단 규칙 제외: 환자 차트, 실제 시술 이력, 전자서명 동의서, 결제·예약 구현 경계: 도메인 메서드 계약 행위 계약: 도메인 메서드 계약
1. 결론¶
현재 JSON과 ORM은 화면·파일 단위로 나뉘어 있다. 목표 모델은 피부과 의사의 판단 흐름을 기준으로 다음 다섯 영역으로 재구성한다.
환자 고민·적응증
↓
시술·제품 후보 ── 임상 지식·근거
↓
위험·금기·순서·간격 검증
↓
추천 패키지·조합·치료 계획
↓
상담 설명·동의·사후관리
핵심 결정은 다음과 같다.
Concern은 환자가 표현하는 고민이고Indication은 의학적 적응증이다. 둘을 같은 엔티티로 취급하지 않는다.ProcedureConcept는 시술군·세부 분류·실제 시술·기법을 하나의 계층으로 관리하고concept_type으로 의미를 구분한다. 따라서 규칙은 M1 같은 시술군과 IPL 같은 구체 시술을 모두 정확히 가리킬 수 있다.Product는 단일 원장으로 관리한다.taxonomy.json의 제품과products.json의 제품 목록을 별도 테이블로 유지하지 않는다.- 추천·비교·안전 화면은 각각 데이터를 다시 소유하지 않는다. 동일한 시술·제품·규칙을 조합한 read model로 만든다.
- 안전 규칙과 추천 관계를 분리한다. 충돌 시
ProcedureConstraint가 항상 우선한다. - JSONB는 원본 스냅샷과 import 이력에만 사용한다. 조회에 필요한 임상 필드는 정규화한다.
2. 현재 구조에서 확인된 문제¶
src/data에는 JSON 47개가 있고, 이 중 23개가 루트 도메인·운영 파일이다.- 20개 도메인은 이미 SQLAlchemy 모델로 미러링되어 있지만 파일 경계가 그대로 테이블 경계가 되었다.
- Next.js 데이터 route 22개는 JSON을 직접 읽고, FastAPI는 일부만 DB에서 제공한다.
- 제품은 taxonomy 내부와 독립 제품 사전에 중복되고 실제 명칭 드리프트도 존재한다.
- 추천은
problem-combos와problem-packages, 안전·순서는contraindications,treatment-sequence,synergy와 프론트 알고리즘으로 분산돼 있다. - 프론트는 자유 문장에서 순서와 간격을 다시 해석한다. 임상 규칙이 구조화된 원장으로 존재하지 않는다는 신호다.
따라서 기존 테이블을 그대로 유지한 채 저장소만 DB로 바꾸는 것은 목표가 아니다.
3. 전체 컨텍스트¶
flowchart LR
Catalog["진료 기준정보\n시술·제품·고민·부위"]
Knowledge["임상 지식·근거\n기전·효과·문헌"]
Safety["안전 규칙\n금기·순서·간격·위험"]
Recommendation["추천·계획\n패키지·조합·회차"]
Guidance["환자 안내\n동의·사후관리"]
Operations["콘텐츠 운영\n가져오기·검수·배포·복원"]
Catalog --> Knowledge
Catalog --> Safety
Catalog --> Recommendation
Knowledge --> Safety
Knowledge --> Recommendation
Safety --> Recommendation
Knowledge --> Guidance
Safety --> Guidance
Operations --> Catalog
Operations --> Knowledge
Operations --> Safety
Operations --> Recommendation
Operations --> Guidance
4. ERD A — 진료 기준정보¶
erDiagram
PROCEDURE_CONCEPT o|--o{ PROCEDURE_CONCEPT : parent_of
PROCEDURE_CONCEPT ||--o{ PROCEDURE_ALIAS : resolves
PROCEDURE_CONCEPT ||--o{ PROCEDURE_PRODUCT : uses
PRODUCT ||--o{ PROCEDURE_PRODUCT : assigned_to
MANUFACTURER o|--o{ PRODUCT : produces
PRODUCT ||--o{ PRODUCT_REGULATORY_STATUS : regulated_as
EVIDENCE_SOURCE o|--o{ PRODUCT_REGULATORY_STATUS : verifies
CONCERN ||--o{ COMPLAINT : details
CONCERN ||--o{ CONCERN_INDICATION : maps_to
INDICATION ||--o{ CONCERN_INDICATION : explains
INDICATION ||--o{ PROCEDURE_INDICATION : treated_by
PROCEDURE_CONCEPT ||--o{ PROCEDURE_INDICATION : treats
ANATOMICAL_SITE ||--o{ PROCEDURE_SITE : applicable_to
PROCEDURE_CONCEPT ||--o{ PROCEDURE_SITE : performed_at
ANATOMICAL_SITE o|--o{ ANATOMICAL_SITE : parent_of
PROCEDURE_CONCEPT {
uuid id PK
string public_code UK
string legacy_code UK
uuid parent_id FK "nullable"
string concept_type "modality|category|procedure|technique"
string name_ko
int sort_order
boolean active
}
PROCEDURE_ALIAS {
uuid id PK
uuid procedure_concept_id FK
string alias UK
string language
string source
}
PRODUCT {
uuid id PK
string public_code UK
string name_ko
uuid manufacturer_id FK "nullable"
string ingredient "nullable"
string material "nullable"
boolean active
}
MANUFACTURER {
uuid id PK
string name_ko
string country_code
}
PRODUCT_REGULATORY_STATUS {
uuid id PK
uuid product_id FK
string country_code
string authority
string status
string approval_number "nullable"
date verified_at "nullable"
uuid evidence_source_id FK "nullable"
}
PROCEDURE_PRODUCT {
uuid procedure_concept_id FK
uuid product_id FK
string role
int sort_order
}
CONCERN {
uuid id PK
string public_code UK "P1-P14"
string name_ko
int sort_order
}
COMPLAINT {
uuid id PK
uuid concern_id FK
string group_name
string name_ko
string detail
int sort_order
}
INDICATION {
uuid id PK
string public_code UK
string name_ko
}
CONCERN_INDICATION {
uuid concern_id FK
uuid indication_id FK
}
PROCEDURE_INDICATION {
uuid procedure_concept_id FK
uuid indication_id FK
string strength
}
ANATOMICAL_SITE {
uuid id PK
uuid parent_id FK "nullable"
string public_code UK
string name_ko
}
PROCEDURE_SITE {
uuid procedure_concept_id FK
uuid site_id FK
string applicability
}
설계 메모:
- 현재 M-code는
concept_type=modality인 최상위 concept의 외부 API 계약으로 유지한다. M8-IPL,M9-HIFU처럼 파일마다 달라지는 표기는 하위 concept의ProcedureAlias로 해석한다.- 신규 내부 PK는 UUID, 외부 소비자는 안정적인
public_code를 사용한다. 기존auto-*와 한글 혼합 ID는legacy_code에 보존하고 compatibility 응답에서만 기존 ID로 사용한다. - 분류 깊이는 고정하지 않는다. 시술군 아래 category 없이 실제 시술이 바로 올 수도 있다.
5. ERD B — 임상 지식과 근거¶
erDiagram
PROCEDURE_CONCEPT ||--o| PROCEDURE_PROFILE : has
PROCEDURE_CONCEPT ||--o{ KNOWLEDGE_DOCUMENT : described_by
PROCEDURE_CONCEPT o|--o{ CLINICAL_CLAIM : concerns
KNOWLEDGE_DOCUMENT ||--o{ DOCUMENT_CLAIM : contains
CLINICAL_CLAIM ||--o{ DOCUMENT_CLAIM : included_in
CLINICAL_CLAIM ||--o{ CLAIM_CITATION : supported_by
EVIDENCE_SOURCE ||--o{ CLAIM_CITATION : cites
CLINICAL_CLAIM ||--o{ CLAIM_VERIFICATION : verified_as
SOURCE_ARTIFACT ||--o{ KNOWLEDGE_DOCUMENT : imported_from
SOURCE_ARTIFACT o|--o{ EVIDENCE_SOURCE : imported_from
PROCEDURE_PROFILE {
uuid procedure_concept_id PK, FK
string mechanism_group
int duration_min
int duration_max
string onset
string duration
string anesthesia
boolean creates_wound
boolean uses_gel
int thermal_load
int bleeding_risk
int swelling_risk
}
KNOWLEDGE_DOCUMENT {
uuid id PK
uuid procedure_concept_id FK
uuid source_artifact_id FK
string document_type
string audience "clinical|counseling|patient"
string language
string title
text body
string status
}
CLINICAL_CLAIM {
uuid id PK
uuid procedure_concept_id FK "nullable"
string topic
string language
text statement
string status
}
DOCUMENT_CLAIM {
uuid document_id FK
uuid claim_id FK
int sort_order
}
EVIDENCE_SOURCE {
uuid id PK
uuid source_artifact_id FK "nullable"
string source_type
string pmid UK "nullable"
string doi UK "nullable"
string title
string journal
int published_year
}
CLAIM_CITATION {
uuid claim_id FK
uuid evidence_source_id FK
string support_level
text note
}
CLAIM_VERIFICATION {
uuid id PK
uuid claim_id FK
string status
datetime checked_at
string checker
text reason
}
SOURCE_ARTIFACT {
uuid id PK
string artifact_type "json|xlsx|paper|textbook"
string source_name
string checksum
string parser_version
datetime imported_at
}
상담 관점 / 의료 관점은 독립된 임상 사실 두 벌이 아니다. 하나의 ClinicalClaim에 연결된 audience별 KnowledgeDocument로 표현한다. 화면에서는 해당 문서가 존재할 때만 관점을 노출한다.
6. ERD C — 안전·진료 준비·회복¶
erDiagram
SAFETY_SUBJECT ||--o{ SAFETY_SUBJECT_PROCEDURE : resolves_to
PROCEDURE_CONCEPT ||--o{ SAFETY_SUBJECT_PROCEDURE : participates_in
SAFETY_SUBJECT ||--o{ PROCEDURE_CONSTRAINT : source
SAFETY_SUBJECT o|--o{ PROCEDURE_CONSTRAINT : target
EVIDENCE_SOURCE ||--o{ CONSTRAINT_EVIDENCE : supports
PROCEDURE_CONSTRAINT ||--o{ CONSTRAINT_EVIDENCE : justified_by
ANATOMICAL_SITE ||--o{ ANATOMY_HAZARD : has
PROCEDURE_CONCEPT ||--o{ PROCEDURE_COMPLICATION : may_cause
COMPLICATION ||--o{ PROCEDURE_COMPLICATION : applies_to
COMPLICATION ||--o{ COMPLICATION_RISK_FACTOR : worsened_by
COMPLICATION ||--o{ COMPLICATION_PROTOCOL : handled_by
PROCEDURE_CONCEPT ||--o{ SCREENING_APPLICABILITY : requires
SCREENING_QUESTION ||--o{ SCREENING_APPLICABILITY : asked_for
PROCEDURE_CONCEPT ||--o{ CONSENT_DISCLOSURE : discloses
PROCEDURE_CONCEPT ||--o{ RECOVERY_TIMELINE : recovers_by
RECOVERY_TIMELINE ||--o{ RECOVERY_PHASE : consists_of
RECOVERY_PHASE ||--o{ RECOVERY_REACTION : expects
PROCEDURE_CONCEPT ||--o{ PIH_RISK_RULE : assessed_by
SAFETY_SUBJECT {
uuid id PK
string public_code UK
string label
string subject_type "procedure|procedure_set|medication|material|preparation|state|scope|other"
string mapping_status "technical_verified|clinical_reviewed|pending"
json qualifiers
}
SAFETY_SUBJECT_PROCEDURE {
uuid subject_id FK
uuid procedure_concept_id FK
int sort_order
}
PROCEDURE_CONSTRAINT {
uuid id PK
uuid from_subject_id FK
uuid to_subject_id FK "nullable for state constraint"
string constraint_type "combination|minimum_interval|same_day_prohibited|state_constraint"
string safety_level "safe|caution|warning"
string enforcement "advisory|required"
int min_interval_value "nullable"
int max_interval_value "nullable"
string interval_unit "day|week|month, nullable"
text raw_interval "nullable"
boolean directional
text rationale
int priority
}
CONSTRAINT_EVIDENCE {
uuid constraint_id FK
uuid evidence_source_id FK
}
ANATOMY_HAZARD {
uuid id PK
uuid site_id FK
string hazard_type "vessel|nerve|structure"
string name
string risk_level
text guidance
}
COMPLICATION {
uuid id PK
string public_code UK
string name_ko
string severity
boolean emergency
}
PROCEDURE_COMPLICATION {
uuid procedure_concept_id FK
uuid complication_id FK
string incidence
string onset
string duration
}
COMPLICATION_RISK_FACTOR {
uuid id PK
uuid complication_id FK
string factor
string risk_level
}
COMPLICATION_PROTOCOL {
uuid id PK
uuid complication_id FK
string protocol_type "prevention|immediate|referral"
text instruction
int sort_order
}
SCREENING_QUESTION {
uuid id PK
string public_code UK
text question
string answer_type
string risk_level
}
SCREENING_APPLICABILITY {
uuid procedure_concept_id FK
uuid question_id FK
string requirement
}
CONSENT_DISCLOSURE {
uuid id PK
uuid procedure_concept_id FK
string disclosure_type
text content
boolean mandatory
}
RECOVERY_TIMELINE {
uuid id PK
uuid procedure_concept_id FK
string title
}
RECOVERY_PHASE {
uuid id PK
uuid timeline_id FK
int starts_at_day
int ends_at_day
int sort_order
}
RECOVERY_REACTION {
uuid id PK
uuid phase_id FK
string name
string level
text guidance
}
PIH_RISK_RULE {
uuid id PK
uuid procedure_concept_id FK
int fitzpatrick_min
int fitzpatrick_max
string risk_level
text prevention
}
ProcedureConstraint는 현재 contraindication·sequence·프론트 하드코딩에 흩어진 판단의 단일
원장이다. 다만 원문 endpoint에는 시술 외에도 약물·재료·상태·범위가 있으므로
SafetySubject가 그 의미를 먼저 보존한다. 양쪽 Subject가 시술로 해석될 때만 시술 pair용
ProcedureConstraint projection을 만든다. 더 구체적인 concept의 규칙이 상위 규칙보다 우선하며,
추천 엔진은 이 projection을 통과하지 못한 후보를 추천 결과로 확정할 수 없다. 세부 결정은
ADR 0003을 따른다.
7. ERD D — 추천·조합·가격¶
erDiagram
CONCERN ||--o{ RECOMMENDATION_PACKAGE : has
COMPLAINT o|--o{ RECOMMENDATION_PACKAGE : narrows
RECOMMENDATION_PACKAGE ||--o{ PACKAGE_ITEM : contains
RECOMMENDATION_PACKAGE ||--o{ PACKAGE_NARRATIVE : described_by
PROCEDURE_CONCEPT ||--o{ PACKAGE_ITEM : selected_as
PRODUCT o|--o{ PACKAGE_ITEM : optionally_uses
COMBINATION_RECIPE ||--o{ RECIPE_ITEM : contains
PROCEDURE_CONCEPT ||--o{ RECIPE_ITEM : ordered_as
PROCEDURE_CONCEPT ||--o{ PROCEDURE_SYNERGY : source
PROCEDURE_CONCEPT ||--o{ PROCEDURE_SYNERGY : target
CONCERN ||--o{ CARE_PLAN_TEMPLATE : targets
CARE_PLAN_TEMPLATE ||--o{ CARE_PLAN_STEP : schedules
PROCEDURE_CONCEPT ||--o{ CARE_PLAN_STEP : performs
PROCEDURE_CONCEPT o|--o{ PRICING_OBSERVATION : priced_as
PRODUCT o|--o{ PRICING_OBSERVATION : priced_as
RECOMMENDATION_PACKAGE {
uuid id PK
uuid concern_id FK
uuid complaint_id FK "nullable"
string package_type "tiered|complaint|optimization"
string tier "nullable"
int age_min "nullable"
int age_max "nullable"
string severity_scope "nullable"
int priority
boolean active
}
PACKAGE_NARRATIVE {
uuid id PK
uuid package_id FK
string language
string audience "clinical|counseling"
string name
text expected_result
text explanation
text rationale
}
PACKAGE_ITEM {
uuid id PK
uuid package_id FK
uuid procedure_concept_id FK
uuid product_id FK "nullable"
string role "primary|adjunct|alternative"
int sort_order
}
COMBINATION_RECIPE {
uuid id PK
string public_code UK
string name
string status
text rationale
}
RECIPE_ITEM {
uuid id PK
uuid recipe_id FK
uuid procedure_concept_id FK
int sort_order
int wait_after_days "nullable"
}
PROCEDURE_SYNERGY {
uuid id PK
uuid from_concept_id FK
uuid to_concept_id FK
string synergy_type
int score "nullable"
string timing
boolean applies_to_descendants
text rationale
}
CARE_PLAN_TEMPLATE {
uuid id PK
uuid concern_id FK
string name
int duration_months
text rationale
}
CARE_PLAN_STEP {
uuid id PK
uuid template_id FK
uuid procedure_concept_id FK
int offset_days
int sort_order
}
PRICING_OBSERVATION {
uuid id PK
uuid procedure_concept_id FK "nullable"
uuid product_id FK "nullable"
decimal min_amount
decimal max_amount
string currency
string region
date observed_at
string source
}
procedure-comparison은 별도 기준정보로 유지하지 않는다. ProcedureProfile, PricingObservation, 위험 규칙과 검증된 claim을 조합해 만드는 projection이 기본이다. 사람이 직접 작성한 비교 결론만 ClinicalClaim으로 보존한다.
PricingObservation은 procedure concept 또는 product 중 최소 하나를 반드시 참조한다. 금액은 5~8만 같은 문자열이 아니라 통화·지역·관측일과 함께 숫자 범위로 저장한다.
8. ERD E — 콘텐츠 가져오기·배포·복원¶
erDiagram
SOURCE_ARTIFACT ||--o{ IMPORT_JOB : submitted_as
IMPORT_JOB o|--o{ CONTENT_REVISION : produces
CONTENT_REVISION o|--o{ CONTENT_REVISION : follows
CONTENT_RELEASE ||--o{ RELEASE_REVISION : contains
CONTENT_REVISION ||--o{ RELEASE_REVISION : published_in
CONTENT_REVISION o|--o{ AUDIT_EVENT : audited_by
CONTENT_RELEASE o|--o{ AUDIT_EVENT : audited_by
CONTENT_RELEASE o|--o{ CONTENT_RELEASE : rollback_of
CONTENT_REVISION {
uuid id PK
uuid import_job_id FK "nullable"
uuid previous_revision_id FK "nullable"
string domain_key
int revision_no
string status "draft|in_review|approved|rejected|archived"
string content_hash
jsonb snapshot
jsonb diff_summary
string created_by
datetime created_at
}
CONTENT_RELEASE {
uuid id PK
string version UK
string status "draft|published|superseded|cancelled"
uuid rollback_of_release_id FK "nullable"
string published_by
datetime published_at
}
RELEASE_REVISION {
uuid release_id FK
uuid revision_id FK
}
IMPORT_JOB {
uuid id PK
uuid source_artifact_id FK
string domain_key
string status
jsonb validation_result
jsonb diff_summary
string requested_by
datetime created_at
}
AUDIT_EVENT {
uuid id PK
uuid revision_id FK "nullable"
uuid release_id FK "nullable"
string action
string actor
jsonb metadata
datetime occurred_at
}
운영 흐름은 이미 glossary에서 검증된 패턴을 일반화한다.
canonical mapping registry도 import 동작을 바꾸는 입력이므로 source artifact와 revision으로
함께 보존한다. 미확정 reference는 IMPORT_MAPPING_ISSUE로 격리하며 source snapshot 저장은
허용하되 release publish는 차단한다.
JSON/XLSX/관리 화면
→ parse
→ schema·FK·임상 규칙 검증
→ diff preview
→ draft revision
→ review
→ release publish transaction
→ 정규화된 live table 반영
→ cache invalidation
복원은 과거 행을 다시 활성화하지 않고 과거 snapshot을 내용으로 하는 새 revision을 발행한다. 감사 이력은 불변으로 남는다.
9. ERD F — 다국어 명칭과 통역 사전¶
erDiagram
PROCEDURE_CONCEPT ||--o{ PROCEDURE_CONCEPT_TRANSLATION : translated_as
PRODUCT ||--o{ PRODUCT_TRANSLATION : translated_as
CONCERN ||--o{ CONCERN_TRANSLATION : translated_as
COMPLAINT ||--o{ COMPLAINT_TRANSLATION : translated_as
INDICATION ||--o{ INDICATION_TRANSLATION : translated_as
ANATOMICAL_SITE ||--o{ ANATOMICAL_SITE_TRANSLATION : translated_as
COMPLICATION ||--o{ COMPLICATION_TRANSLATION : translated_as
GLOSSARY_TERM ||--o{ GLOSSARY_TRANSLATION : translated_as
PROCEDURE_CONCEPT_TRANSLATION {
uuid procedure_concept_id FK
string language
string name
string description "nullable"
}
PRODUCT_TRANSLATION {
uuid product_id FK
string language
string name
}
CONCERN_TRANSLATION {
uuid concern_id FK
string language
string name
}
COMPLAINT_TRANSLATION {
uuid complaint_id FK
string language
string name
string detail "nullable"
}
INDICATION_TRANSLATION {
uuid indication_id FK
string language
string name
}
ANATOMICAL_SITE_TRANSLATION {
uuid site_id FK
string language
string name
}
COMPLICATION_TRANSLATION {
uuid complication_id FK
string language
string name
string description "nullable"
}
GLOSSARY_TERM {
uuid id PK
string category
string ko UK
string source
string note "nullable"
}
GLOSSARY_TRANSLATION {
uuid term_id FK
string language
string translated_text
string review_status
}
KnowledgeDocument, ClinicalClaim, PackageNarrative는 본문 자체에 language와 audience가 있으므로 별도 번역 테이블을 만들지 않는다. 사용자 노출 기준정보는 명시적 translation 테이블을 사용한다. 통역 사전은 entity label과 목적이 다르므로 별도 aggregate로 유지한다.
10. Master·Enum·Const·설정의 경계¶
DB로 옮긴다는 이유만으로 모든 값을 master table로 만들지 않는다. 값의 생명주기와 변경 주체를 기준으로 다음처럼 구분한다.
| 구분 | 판단 기준 | 변경 방식 |
|---|---|---|
| Master | 다른 데이터가 동일 식별자를 FK로 참조하고, 배포 없이 추가·수정될 수 있음 | 관리 기능 + revision/release |
| Versioned domain data | 임상 설명·근거·추천·안전 규칙처럼 검수와 이력이 필요함 | draft → review → release |
| Enum | 유한하고 닫힌 상태 집합이며, 값 추가 시 코드 분기와 API 계약이 바뀜 | 코드·계약·DB CHECK를 함께 변경 |
| Const | UI 표현이나 고정된 제품 정책이며 임상 데이터와 독립적임 | 코드 변경 |
| Operational config | 환경별 임계값·활성 기능·제한값이며 임상 원장이 아님 | 환경 변수 또는 배포 설정 |
| Derived read model | master와 규칙에서 재현할 수 있는 결과임 | 저장하지 않고 조회 시 계산 |
10.1 Master로 관리할 기준정보¶
| Master aggregate | 포함 엔티티 | Master인 이유 |
|---|---|---|
| 시술 분류 | ProcedureConcept, ProcedureAlias | 모든 규칙·추천·제품 연결의 기준 식별자이며 계층·별칭이 변경됨 |
| 제품 | Product, Manufacturer, ProductRegulatoryStatus | 제조사·허가 상태·출처를 포함하고 여러 시술에서 재사용됨 |
| 고민·적응증 | Concern, Complaint, Indication | 환자 표현과 의학적 적응증을 분리해 추천과 설명이 공통 참조함 |
| 해부학적 부위 | AnatomicalSite | 시술 적용 부위와 위험 부위가 동일한 canonical site를 참조함 |
| 합병증 분류 | Complication | 시술별 발생 정보·위험 인자·대응 프로토콜이 공통 참조함 |
| 문헌 식별자 | EvidenceSource | claim·안전 규칙·제품 허가 검증이 같은 논문·가이드라인을 참조함 |
| 통역 사전 원어 | GlossaryTerm | 여러 언어 번역과 검수 상태의 기준 식별자임 |
ProcedureProfile, ClinicalClaim, KnowledgeDocument, ProcedureConstraint, ProcedureSynergy, RecommendationPackage, ScreeningQuestion, ConsentDisclosure, RecoveryTimeline은 DB에 저장하지만 공통 lookup master는 아니다. 이들은 근거와 검수 이력이 필요한 versioned domain data로 관리한다.
10.2 코드 Enum으로 고정할 값¶
| Enum | 값 또는 초기 후보 | 현재 근거 |
|---|---|---|
| LanguageCode | ko, en, ja, zh, yue, th, vi |
backend Lang, frontend Lang |
| ProcedureConceptType | modality, category, procedure, technique |
ProcedureConcept 계층 분기 |
| Audience | clinical, counseling, patient |
문서·설명 관점 분기 |
| IndicationType | primary, secondary, extended |
backend 기존 Enum |
| ContraindicationType | absolute, relative |
backend 기존 Enum |
| ConstraintType | same_day, order, minimum_interval, prohibited |
안전 엔진의 규칙 종류 |
| PairSafetyLevel | safe, caution, warning |
현재 조합 매트릭스의 임상 주의 단계 |
| ConstraintEnforcement | advisory, required |
권고와 강제 안전 gate 분리 |
| SynergyType | complementary, sequential, alternative |
현재 함께, 다음에, 대신을 canonicalize |
| RecommendationPackageType | tiered, complaint, optimization |
추천 패키지 생성 방식 |
| RecommendationTier | entry, recommended, premium, vip |
현재 추천 데이터의 닫힌 tier |
| PackageItemRole | primary, adjunct, alternative |
패키지 조립 방식 |
| PihRiskLevel | very_low, low, moderate, high, very_high |
PIH 위험도 정렬·표현 |
| ComplicationSeverity | mild, moderate, severe, critical |
합병증 대응 분기 |
| RecoveryReactionLevel | normal, abnormal, caution, emergency |
회복 반응 행동 분기 |
| ContentStatus | draft, in_review, approved, rejected, archived |
콘텐츠 검수 수명주기 |
| ReleaseStatus | draft, published, superseded, cancelled |
release 수명주기 |
| ImportMode | preview, apply |
glossary에서 검증된 import 방식 |
| RevisionEventType | import, edit, publish, rollback |
감사 이력 action |
| SourceArtifactType | json, xlsx, paper, textbook |
원본 parser 선택 |
| ClaimVerificationStatus | pending, verified, rejected, not_found |
문헌 검증 처리 분기 |
Enum은 Python StrEnum을 canonical contract로 두고 TypeScript union을 동일 계약에서 생성한다. DB에는 PostgreSQL native ENUM 대신 String + CHECK를 사용한다. 값 변경 시 baseline과 API 계약을 함께 검증하면서도 migration을 불필요하게 경직시키지 않기 위해서다.
다음 값은 이름이 비슷해도 하나의 공통 Enum으로 합치지 않는다.
PihRiskLevel,ComplicationSeverity,RecoveryReactionLevel은 각각 위험 예측, 합병증 중증도, 환자 행동 지침으로 의미와 정렬 기준이 다르다.PairSafetyLevel은 화면의 임상 주의 단계이고ConstraintEnforcement는 엔진의 강제 수준이다.warning과required를 같은 필드에 넣지 않는다.- 현재 JSON의 범용
type,level,status필드는 import 단계에서 문맥별 이름으로 바꾼다.GlobalType,GlobalLevel,GlobalStatus는 만들지 않는다. RecommendationTier는 현재 단일 서비스의 고정 동작이므로 Enum으로 시작한다. 병원별 명칭·순서·단계 커스터마이징이 실제 요구가 될 때만 master로 승격한다.
10.3 Const와 운영 설정으로 남길 값¶
| 현재 값 | 목표 위치 | 판단 |
|---|---|---|
MAX_SELECTION = 3 |
frontend product-policy const | 비교 UI의 고정 선택 제한이며 임상 원장이 아님 |
| Enum별 색상·아이콘·CSS class | frontend presentation const | 상태의 의미가 아니라 표현만 담당 |
| 메뉴 route·focus style·empty-state 문구 key | frontend presentation const | 데이터 정합성과 무관 |
| ISO 국가 코드에 대응하는 flag | frontend presentation const | 국가명 원장을 별도 구축하지 않고 표준 코드를 사용 |
SUPPORTED_LANGS |
environment config | LanguageCode 중 배포 환경에서 활성화할 부분집합 |
| glossary 삭제 비율·건수 임계값, history limit | backend operational config | import 안전장치이며 임상 콘텐츠가 아님 |
반대로 다음 하드코딩은 Const로 남기지 않는다.
| 현재 하드코딩 | 이동 대상 | 이유 |
|---|---|---|
CATEGORY_NAMES |
ProcedureConcept + translation | 화면마다 중복되고 canonical 시술명이 바뀔 수 있음 |
ORDER_RULES |
ProcedureConstraint 또는 CombinationRecipe | 시술 순서를 결정하는 임상 규칙임 |
MOA_SYNERGY |
ProcedureSynergy + ClinicalClaim + citation | 추천 점수와 근거를 바꾸는 임상 판단임 |
| 의료 단계의 순서·간격 | ProcedureConstraint, RecipeItem | 단순 표시 순서가 아니라 안전·치료 결과에 영향을 줌 |
| 제품 국가·허가 라벨 | ProductRegulatoryStatus + translation/read model | 검증일·출처·국가별 상태가 필요함 |
10.4 저장하지 않고 계산할 값¶
병행 가능,간격 필요,주의 필요라벨은ProcedureConstraint에서 계산한다.- 추천 후보의
allowed_after_confirmation같은 eligibility는 안전 gate 결과다. - 비교 테이블 행, 추천 개수, 누락 개수, 선택 개수는 조회 시 계산한다.
- 위험 badge의 색상은 Enum→presentation const 매핑으로 결정한다.
시술 A → 시술 B표시 순서는 구조화된 constraint와 recipe에서 계산한다.
새 필드를 분류할 때는 다음 순서로 결정한다.
다른 데이터가 identity를 참조하고 운영자가 배포 없이 수정하는가? → Master
임상 설명·근거·규칙이며 검수와 복원이 필요한가? → Versioned domain data
값 추가가 코드 분기와 API 계약을 바꾸는가? → Enum
환경마다 달라지는 임계값·활성 범위인가? → Operational config
화면 표현만 바꾸는가? → Const
다른 원장과 규칙으로 재현 가능한가? → Derived read model
11. 현재 파일의 목표 엔티티 매핑¶
| 현재 원본 | 목표 소유 영역 | 주요 목표 엔티티 |
|---|---|---|
taxonomy |
기준정보 + 임상 지식 | ProcedureConcept, ProcedureAlias, Concern, Indication, KnowledgeDocument |
products + taxonomy 제품 |
기준정보 | Product, ProcedureProduct |
extracted/* |
임상 지식 | SourceArtifact, KnowledgeDocument, ClinicalClaim |
procedure-profiles |
임상 지식 | ProcedureProfile |
literature-evidence + integrity |
근거 | ClinicalClaim, EvidenceSource, ClaimCitation, ClaimVerification |
synergy |
추천 관계 | ProcedureSynergy |
contraindications + treatment-sequence |
안전 규칙 | ProcedureConstraint, ConstraintEvidence |
combo-recipes |
추천 | CombinationRecipe, RecipeItem |
problem-combos + problem-packages |
추천 | Complaint, RecommendationPackage, PackageNarrative, PackageItem |
treatment-optimization |
추천·계획 | RecommendationPackage, CarePlanTemplate, CarePlanStep |
procedure-comparison |
조회 projection | ProcedureProfile + PricingObservation + ClinicalClaim에서 조립 |
anatomy-danger-zones |
안전 | AnatomicalSite, AnatomyHazard |
complications + subcategories |
안전 | Complication, ProcedureComplication, RiskFactor, Protocol |
pih-risk |
안전 | PihRiskRule |
pre-procedure-checklist |
진료 준비 | ScreeningQuestion, ScreeningApplicability |
consent-checklist |
환자 안내 | ConsentDisclosure |
patient-education |
환자 안내 | KnowledgeDocument(audience=patient) |
post-procedure-timeline |
회복 | RecoveryTimeline, RecoveryPhase, RecoveryReaction |
market-pricing |
가격 | PricingObservation |
i18n/*, glossary |
번역 | 엔티티별 translation + GlossaryTerm/GlossaryTranslation |
extraction-guardrails |
ingestion 설정 | DB 임상 원장 제외; parser 설정으로 버전 관리 |
12. 저장하지 않을 것과 계산할 것¶
| 항목 | 처리 |
|---|---|
| 비교 화면의 완성된 행 | 저장하지 않고 profile·price·risk에서 계산 |
| 병행 가능/간격 라벨 | constraint에서 계산 |
| 추천 후보의 안전 여부 | 추천 후 safety gate에서 계산 |
| 시술 순서 | constraint와 recipe를 이용해 backend에서 계산 |
| 상담/의료 관점 노출 | 해당 audience 문서가 존재할 때만 노출 |
| 현재 pre-check 선택값 | 기본적으로 저장하지 않음 |
| 동의 체크 상태 | 전자서명 요구가 생기기 전까지 저장하지 않음 |
| 화면 색상·배지·정렬 | 프론트 표현 규칙으로 유지 |
판단 결과에는 최소한 content_release_id, engine_version, rule_refs, evidence_refs를 포함한다. LLM은 검증된 결과를 설명할 수 있지만 안전 등급·간격·순서를 결정하지 않는다.
13. 구현 결과¶
Task 1. 식별자와 단일 원장 확정 — 기술 mapping 118건과 문헌 identity·taxonomy validation 완료, claim 임상 승인 대기
목표: ProcedureConcept, Product, Concern, Indication의 canonical ID와 기존 alias 매핑을 확정한다.
예상 결과:
- taxonomy/product 중복 제품의 병합표가 존재함
- 기존 M/P-code와 외부 API public code가 보존됨
- 해석되지 않는 시술·제품 텍스트 참조가 0건이거나 명시적 보류 목록에 있음
Task 2. 기준정보·revision baseline 구현 — 완료
목표: Catalog와 콘텐츠 revision/release/import 테이블을 먼저 구현한다.
예상 결과:
- JSON/XLSX를 draft revision으로 가져올 수 있음
- 검증·diff 후 하나의 release로 발행할 수 있음
- 기존 live 데이터를 덮지 않는 preview가 존재함
Task 3. 안전 규칙 구조화 — 완료, legacy pair 27/27 parity
목표: contraindication·sequence·프론트 하드코딩을 ProcedureConstraint로 통합한다.
예상 결과:
- 방향, 순서, 최소·최대 간격이 자유 문장 파싱 없이 조회됨
- 동일 pair의 충돌 규칙이 import 단계에서 차단됨
- safety 우선순위가 backend 테스트로 고정됨
Task 4. 추천·근거·안내 데이터 이관 — 완료, 미검증 근거 publication 차단
목표: 추천 패키지와 임상 claim·문헌·환자 안내를 canonical entity에 연결한다.
예상 결과:
- 추천 항목의 시술·제품이 모두 FK로 연결됨
- 추천 결과가 적용 규칙과 근거를 추적 가능함
- 상담/의료/환자 설명이 동일 임상 사실을 중복 소유하지 않음
Task 5. API 전환과 migration squash — additive API와 단일 baseline 완료, legacy cutover 보류
목표: Next.js 파일 읽기를 FastAPI 조회로 전환하고 STG 직전 하나의 baseline migration을 만든다.
예상 결과:
- 기존 CHARTY·Linky 응답 계약 parity 테스트가 통과함
- 런타임 JSON 직접 읽기가 제거됨
- STG용 단일 baseline migration과 import manifest가 존재함
14. 다음 운영 게이트¶
Mapping report, product drift report, pair conflict report를 importer와 proof script로 실측했고 본
ERD를 SQLAlchemy 모델과 단일 Alembic 0045 baseline으로 내렸다. 다음 단계는 설계 확장이
아니라 아래 운영 승인이다.
- 완료 — 시술 reference 3건의 canonical 위치를 결정적으로 확정했다.
- 완료 — 문헌 taxonomy mismatch와 PMID/identifier validation 78건을 NCBI 원본과 대조해 교정 또는 제외했다.
- 피부과 의사가 교정 source의 claim과 revision을 검수·승인한다.
- 최초 release를 발행한 뒤 natural-language retrieval 평가를 수행한다.