!theme mono skinparam classAttributeIconSize 0 skinparam classFontSize 12 skinparam classAttributeFontSize 11 title HealthSync 역설계 - Health Service 데이터 설계서 package "Health Service Database Schema" as health_db #lightblue { entity "health_checkups" as health_checkup { * id : BIGINT <> -- * user_id : VARCHAR(50) * member_serial_number : BIGINT * raw_id : BIGINT <> * reference_year : INTEGER * height_cm : DECIMAL(5,2) * weight_kg : DECIMAL(5,2) * waist_cm : DECIMAL(5,2) * bmi : DECIMAL(4,2) * visual_acuity_left : DECIMAL(3,1) * visual_acuity_right : DECIMAL(3,1) * hearing_avg : DECIMAL(4,1) * systolic_bp : INTEGER * diastolic_bp : INTEGER * fasting_glucose : INTEGER * total_cholesterol : INTEGER * triglyceride : INTEGER * hdl_cholesterol : INTEGER * ldl_cholesterol : INTEGER * hemoglobin : DECIMAL(4,1) * urine_protein : INTEGER * serum_creatinine : DECIMAL(4,1) * ast : INTEGER * alt : INTEGER * gamma_gtp : INTEGER * smoking_status : INTEGER * drinking_status : INTEGER * risk_level : VARCHAR(20) * abnormal_indicators : JSON * health_score : INTEGER * created_at : TIMESTAMP * updated_at : TIMESTAMP -- + 인덱스: idx_user_id_year + 인덱스: idx_member_serial_number + 인덱스: idx_risk_level + 인덱스: idx_health_score } entity "health_checkup_raw" as health_checkup_raw { * raw_id : BIGINT <> -- * member_serial_number : BIGINT * reference_year : INTEGER * birth_date : DATE * name : VARCHAR(50) * region_code : INTEGER * gender_code : INTEGER * age : INTEGER * height : INTEGER * weight : INTEGER * waist_circumference : INTEGER * visual_acuity_left : DECIMAL(3,1) * visual_acuity_right : DECIMAL(3,1) * hearing_left : INTEGER * hearing_right : INTEGER * systolic_bp : INTEGER * diastolic_bp : INTEGER * fasting_glucose : INTEGER * total_cholesterol : INTEGER * triglyceride : INTEGER * hdl_cholesterol : INTEGER * ldl_cholesterol : INTEGER * hemoglobin : DECIMAL(4,1) * urine_protein : INTEGER * serum_creatinine : DECIMAL(4,1) * ast : INTEGER * alt : INTEGER * gamma_gtp : INTEGER * smoking_status : INTEGER * drinking_status : INTEGER * created_at : TIMESTAMP -- + 인덱스: idx_member_serial_year + 인덱스: idx_name_birthdate + 인덱스: idx_gender_age } entity "health_normal_ranges" as health_normal_range { * item_code : VARCHAR(20) <> * gender_code : INTEGER <> -- * item_name : VARCHAR(100) * normal_min : DECIMAL(10,2) * normal_max : DECIMAL(10,2) * caution_min : DECIMAL(10,2) * caution_max : DECIMAL(10,2) * danger_min : DECIMAL(10,2) * danger_max : DECIMAL(10,2) * unit : VARCHAR(20) * description : TEXT * is_active : BOOLEAN * created_at : TIMESTAMP * updated_at : TIMESTAMP -- + 인덱스: idx_item_code + 인덱스: idx_gender_code } entity "health_files" as health_file { * file_id : VARCHAR(50) <> -- * user_id : VARCHAR(50) * file_name : VARCHAR(255) * file_type : VARCHAR(50) * file_url : VARCHAR(500) * file_size : BIGINT * upload_status : VARCHAR(20) * uploaded_at : TIMESTAMP * processed_at : TIMESTAMP -- + 인덱스: idx_user_id + 인덱스: idx_upload_status + 인덱스: idx_uploaded_at } note right of health_checkup **가공된 건강검진 데이터** • User Service의 사용자와 연결 • 정상치 기준과 비교 분석 완료 • 건강 점수 및 위험도 계산 결과 • 이상 항목을 JSON 배열로 저장 • 1명당 1개 레코드 (최신 데이터만) end note note right of health_checkup_raw **건강보험공단 원본 데이터** • 연도별 검진 데이터 보관 • 가공 전 원본 데이터 유지 • 개인정보 포함 (이름, 생년월일) • 성별/나이별 통계 분석 용도 • 여러 연도 데이터 보관 가능 end note note right of health_normal_range **건강검진 정상치 기준** • 성별별 정상치 기준 관리 • gender_code: 0(공통), 1(남성), 2(여성) • 정상/주의/위험 3단계 범위 • 의료진 검토 후 업데이트 • 마스터 데이터 성격 end note note right of health_file **업로드된 건강검진 파일** • Azure Blob Storage 연동 • PDF, 이미지 파일 지원 • 업로드 상태 추적 • OCR 처리 결과 연동 준비 end note } package "관계 정의" as relationships { health_checkup ||--|| health_checkup_raw : raw_id health_checkup }|--|| health_normal_range : 정상치_비교 health_file }|--|| health_checkup : 파일_연동 note as n1 **외래키 관계** health_checkups.raw_id → health_checkup_raw.raw_id **논리적 관계** • health_checkups ↔ health_normal_ranges (정상치 비교) • health_files ↔ health_checkups (파일-데이터 연결) • User Service users ↔ health_checkups (사용자별 데이터) **참조 무결성** • raw_id는 NOT NULL (원본 데이터 필수) • user_id는 User Service와 일관성 유지 • member_serial_number로 건보공단 데이터 연결 end note } package "데이터 타입 및 제약조건" as constraints { note as n2 **health_checkups 제약조건** • id: AUTO_INCREMENT • user_id: 최대 50자, NOT NULL • member_serial_number: User Service와 일치 • reference_year: 1990~현재년도 • bmi: 계산값, 10.0~50.0 범위 • blood pressure: systolic > diastolic • risk_level: 'normal', 'caution', 'danger' • health_score: 0~100 점수 • abnormal_indicators: JSON 배열 형태 **health_checkup_raw 제약조건** • raw_id: AUTO_INCREMENT • member_serial_number: NOT NULL • reference_year: NOT NULL • gender_code: 1(남성), 2(여성) • age: 0~120 범위 • 모든 수치 데이터: 음수 불가 **health_normal_ranges 제약조건** • 복합 기본키: (item_code, gender_code) • gender_code: 0(공통), 1(남성), 2(여성) • normal_min ≤ normal_max • caution 범위는 normal 범위 밖 • danger 범위는 caution 범위 밖 **health_files 제약조건** • file_id: UUID 형태 • file_type: 'pdf', 'jpg', 'png' 등 • file_size: 바이트 단위, 최대 10MB • upload_status: 'uploading', 'completed', 'failed' end note } package "정상치 기준 예시 데이터" as normal_ranges_data { note as n3 **주요 항목별 정상치 기준 (성인 남성 기준)** | item_code | normal_range | caution_range | danger_range | |-----------|-------------|---------------|--------------| | BMI | 18.5~24.9 | 25.0~29.9 또는 <18.5 | ≥30.0 | | SYSTOLIC_BP | 90~119 | 120~139 | ≥140 | | DIASTOLIC_BP | 60~79 | 80~89 | ≥90 | | FASTING_GLUCOSE | 70~99 | 100~125 | ≥126 | | TOTAL_CHOLESTEROL | <200 | 200~239 | ≥240 | | HDL_CHOLESTEROL | ≥40 | 35~39 | <35 | | LDL_CHOLESTEROL | <130 | 130~159 | ≥160 | | TRIGLYCERIDE | <150 | 150~199 | ≥200 | | AST | <40 | 40~80 | >80 | | ALT | <40 | 40~80 | >80 | | GAMMA_GTP | <60 | 60~100 | >100 | | HEMOGLOBIN | 13.0~17.0 | 12.0~12.9 | <12.0 | **성별 차이** • 여성은 HDL 콜레스테롤 ≥50, 혈색소 12.0~15.0 • 임신 가능 연령대 여성은 별도 기준 적용 end note } package "성능 및 운영 고려사항" as performance { note as n4 **인덱스 전략** • health_checkups: (user_id, reference_year) 복합 인덱스 • health_checkup_raw: (member_serial_number, reference_year) 복합 인덱스 • health_normal_ranges: item_code 단일 인덱스 • health_files: (user_id, uploaded_at) 복합 인덱스 **파티셔닝** • health_checkup_raw: reference_year 기준 연도별 파티셔닝 • health_files: uploaded_at 기준 월별 파티셔닝 **아카이빙** • 5년 이상 된 raw 데이터는 별도 아카이브 테이블로 이관 • 업로드 실패 파일은 30일 후 자동 삭제 **캐싱 전략** • health_normal_ranges: Redis에 전체 캐싱 (24시간) • 사용자별 최신 건강검진: Redis 캐싱 (1시간) • 건강 점수 계산 결과: Redis 캐싱 (6시간) **모니터링 지표** • 정상치 기준 업데이트 빈도 • 평균 건강 점수 추이 • 위험군 사용자 비율 • 파일 업로드 성공률 end note } package "데이터 보안 및 규정 준수" as security { note as n5 **개인정보 보호** • health_checkup_raw.name: 암호화 저장 • health_checkup_raw.birth_date: 마스킹 처리 • 의료 데이터 접근 시 감사 로그 기록 **의료정보 보안** • 의료법 및 개인정보보호법 준수 • 건강검진 데이터 3년 보존 의무 • 개인식별정보와 건강정보 분리 저장 **접근 제어** • 의료진만 정상치 기준 수정 가능 • 사용자 본인 데이터만 조회 가능 • 관리자 접근 시 의료진 승인 필요 **데이터 무결성** • 건강검진 데이터 변조 방지 • 원본 데이터 불변성 보장 • 정상치 기준 변경 시 이력 관리 **백업 및 복구** • 건강검진 데이터 일일 백업 • 정상치 기준 변경 전 백업 • 재해 복구 시 의료진 검증 필수 end note }