본문으로 건너뛰기

OCR 진료비 세부산정내역서 처리 시스템 - 현재 구현 사양

개요​

이 문서는 현재 운영 중인 OCR 진료비 세부산정내역서 처리 시스템의 기술적 구현 사양을 상세히 기술합니다. 시스템 아키텍처, API 엔드포인트, 데이터 구조, 그리고 각 처리 단계별 기술적 구현 내용을 포함합니다.

시스템 구성 요소​

  • Frontend: 모바일 앱 (이미지 업로드)
  • Backend API: dta-wir-api-ts (메인 처리 서버)
  • Chat API: dta-wir-api-chat-ts (검토 시스템)
  • Storage: Google Cloud Storage (이미지 저장)
  • Database: Firestore (분석 결과 및 검토 작업 저장)
  • Message Queue: Cloud Task, Pub/Sub (비동기 처리)
  • Notification: Slack (검토 요청 알림)

시스템 흐름도​

전체 시퀀스 다이어그램​

시스템 아키텍처 다이어그램​

데이터 흐름 다이어그램​

1. OCR 업로드 단계​

1.1 환자의 이미지 업로드​

환자가 모바일 앱에서 진료비 세부산정내역서 이미지를 업로드합니다.

1.2 API 호출 및 초기 처리​

  • API 경로: APP → dta-wir-api-ts
  • 엔드포인트: accesscodes/medical-statements

1.3 서버 측 처리 (dta-wir-api-ts)​

이미지 저장​

  • 저장 위치: Cloud Storage
  • 경로: medical-statement-prod/dta-wir/{year}}/{month}

액세스 코드 생성​

데이터베이스에 다음 정보로 액세스 코드를 생성합니다:

  • type: 3
  • groupId: 1
  • siteId: NULL
  • registration_channel_id: 4
  • delivery_method: IN_APP

비동기 분석 작업 생성​

Cloud Task를 생성하여 OCR 분석을 위한 작업을 큐에 추가합니다:

  • 작업 이름: dta-wir-medical-statement-analysis
  • 포함 데이터:
    • GCS URI (업로드된 이미지 경로)
    • Device ID
    • Access Code ID
    • 분석 엔드포인트: /v1/medical-statement/analyze

2. OCR 분석 단계​

2.1 분석 작업 실행​

Cloud Task 큐에서 dta-wir-medical-statement-analysis 작업이 실행되어 다음 API를 호출합니다:

  • API 경로: Cloud Task → dta-wir-api-ts
  • 엔드포인트: medical-statement/analyze

2.2 LLM 기반 이미지 분석 및 결과 저장​

서버에서 대형 언어 모델(LLM)을 사용하여 업로드된 진료비 세부산정내역서를 분석하고, 결과를 Firestore에 저장합니다:

  • 저장 위치: Firestore
  • 컬렉션: medical-statement/analyze

2.3 분석 결과 데이터 구조​

분석 결과는 Firestore에 다음과 같은 구조로 저장됩니다:

📋 기본 메타데이터​

{
"analyzedAt": {
"timestampValue": "2025-09-24T01:30:28.053Z"
},
"status": {
"stringValue": "SUCCESS"
},
"generatedKey": {
"stringValue": "38e0990291cc721ed32e1c2f9ce8851b1a644d5b9521b0671ee8e15fd52f4126"
},
"createdAt": {
"timestampValue": "2025-09-24T01:30:28.112Z"
}
}

🏥 진료 정보 (treatmentInfo)​

{
"treatmentInfo": {
"mapValue": {
"fields": {
"treatmentDate": {
"stringValue": "2025-09-24"
},
"period": {
"stringValue": "2025-09-24~2025-09-24"
},
"isSleepQ": {
"booleanValue": true
},
"departmentName": {
"nullValue": null
},
"careInstitutionName": {
"stringValue": "31100511"
},
"careInstitutionCode": {
"stringValue": "31100511"
}
}
}
}
}

💊 의료 세부 항목 (medicalDetails) - 예시​

각 의료 항목은 숫자 키로 구분되며, 다음과 같은 정보를 포함합니다:

진찰료 예시:

{
"0": {
"mapValue": {
"fields": {
"date": { "stringValue": "2025-09-24" },
"category": { "stringValue": "진찰료" },
"amount": { "integerValue": "19100" },
"name": { "stringValue": "초진진찰료-종합병원" },
"count": { "integerValue": "1" },
"code": { "stringValue": "AA156" },
"days": { "integerValue": "1" }
}
}
}
}

처치 및 수술료 예시:

{
"7": {
"mapValue": {
"fields": {
"days": { "integerValue": "1" },
"date": { "stringValue": "2025-09-24" },
"code": { "stringValue": "TX010" },
"category": { "stringValue": "처치및수술료" },
"amount": { "integerValue": "250000" },
"name": { "stringValue": "디지털인지행동치료-불면증(슬립큐)" },
"count": { "integerValue": "1" }
}
}
}
}

참고: 실제 데이터에는 총 9개의 의료 항목(0~8번)이 포함되어 있으며, 각각 진찰료, 처치 및 수술료 등의 카테고리로 분류됩니다.

👤 환자 정보 (patientInfo)​

{
"patientInfo": {
"mapValue": {
"fields": {
"type": {
"stringValue": "건강보험"
},
"room": {
"nullValue": null
},
"name": {
"stringValue": "이유민"
},
"registrationNumber": {
"stringValue": "12380981"
}
}
}
}
}

📎 추가 메타데이터​

{
"gcsUris": {
"arrayValue": {
"values": [
{
"stringValue": "gs://medical-statement-prod/dta-wir/2025/09/1758677416465-ujzvxn.1758677415796_dee80075-6896-4274-9cb6-b3d013cf9b7b_0"
}
]
}
},
"error": {
"nullValue": null
},
"accesscodeId": {
"integerValue": "952"
},
"key": {
"stringValue": "Android-4fa95b2f-7b51-4cd4-8017-082c13f8f232"
},
"createTime": "2025-09-24T01:30:28.137847Z",
"updateTime": "2025-09-24T01:30:29.140902Z"
}
🔍 전체 JSON 데이터 보기 (클릭하여 펼치기)

전체 분석 결과 JSON 데이터는 위의 구간별 예시들을 모두 포함한 완전한 구조입니다. 실제 운영에서는 더 많은 의료 항목과 세부 정보가 포함될 수 있습니다.

{
"name": "projects/dta-wir-app-prod/databases/medical-statement/documents/analyzed/Android-4fa95b2f-7b51-4cd4-8017-082c13f8f232",
"fields": {
"analyzedAt": {
"timestampValue": "2025-09-24T01:30:28.053Z"
},
"status": {
"stringValue": "SUCCESS"
},
"generatedKey": {
"stringValue": "38e0990291cc721ed32e1c2f9ce8851b1a644d5b9521b0671ee8e15fd52f4126"
},
"createdAt": {
"timestampValue": "2025-09-24T01:30:28.112Z"
},
"result": {
"mapValue": {
"fields": {
"treatmentInfo": {
"mapValue": {
"fields": {
"treatmentDate": {
"stringValue": "2025-09-24"
},
"period": {
"stringValue": "2025-09-24~2025-09-24"
},
"isSleepQ": {
"booleanValue": true
},
"departmentName": {
"nullValue": null
},
"careInstitutionName": {
"stringValue": "31100511"
},
"careInstitutionCode": {
"stringValue": "31100511"
}
}
}
},
"medicalDetails": {
"mapValue": {
"fields": {
"4": {
"mapValue": {
"fields": {
"days": {
"integerValue": "1"
},
"count": {
"integerValue": "1"
},
"amount": {
"integerValue": "0"
},
"date": {
"stringValue": "2025-09-24"
},
"name": {
"stringValue": "성인불면증환자인지행동치료어플리케이션슬립큐"
},
"code": {
"stringValue": "GGZAA0006"
},
"category": {
"stringValue": "처치및수술료"
}
}
}
},
"2": {
"mapValue": {
"fields": {
"date": {
"stringValue": "2025-09-24"
},
"name": {
"stringValue": "외래의료질평가지원금-교육수련/1등급"
},
"code": {
"stringValue": "AU311"
},
"category": {
"stringValue": "진찰료"
},
"amount": {
"integerValue": "680"
},
"days": {
"integerValue": "1"
},
"count": {
"integerValue": "1"
}
}
}
},
"3": {
"mapValue": {
"fields": {
"days": {
"integerValue": "1"
},
"date": {
"stringValue": "2025-09-24"
},
"amount": {
"integerValue": "580"
},
"count": {
"integerValue": "1"
},
"name": {
"stringValue": "외래의료질평가지원금-연구개발/1등급"
},
"category": {
"stringValue": "진찰료"
},
"code": {
"stringValue": "AU411"
}
}
}
},
"8": {
"mapValue": {
"fields": {
"code": {
"stringValue": "TYNM007A"
},
"name": {
"stringValue": "디지털인지행동치료-불면증(슬립큐)"
},
"category": {
"stringValue": "처치및수술료"
},
"amount": {
"integerValue": "0"
},
"count": {
"integerValue": "1"
},
"date": {
"stringValue": "2025-09-24"
},
"days": {
"integerValue": "1"
}
}
}
},
"7": {
"mapValue": {
"fields": {
"days": {
"integerValue": "1"
},
"date": {
"stringValue": "2025-09-24"
},
"code": {
"stringValue": "TX010"
},
"category": {
"stringValue": "처치및수술료"
},
"amount": {
"integerValue": "250000"
},
"name": {
"stringValue": "디지털인지행동치료-불면증(슬립큐)"
},
"count": {
"integerValue": "1"
}
}
}
},
"5": {
"mapValue": {
"fields": {
"category": {
"stringValue": "처치및수술료"
},
"date": {
"stringValue": "2025-09-24"
},
"name": {
"stringValue": "디지털치료기기처방료"
},
"code": {
"stringValue": "TU001"
},
"amount": {
"integerValue": "5230"
},
"days": {
"integerValue": "1"
},
"count": {
"integerValue": "1"
}
}
}
},
"1": {
"mapValue": {
"fields": {
"category": {
"stringValue": "진찰료"
},
"amount": {
"integerValue": "3460"
},
"days": {
"integerValue": "1"
},
"count": {
"integerValue": "1"
},
"name": {
"stringValue": "외래의료질평가지원금-질.안전/2등급"
},
"code": {
"stringValue": "AU232"
},
"date": {
"stringValue": "2025-09-24"
}
}
}
},
"6": {
"mapValue": {
"fields": {
"category": {
"stringValue": "처치및수술료"
},
"amount": {
"integerValue": "16130"
},
"code": {
"stringValue": "TU002"
},
"count": {
"integerValue": "1"
},
"name": {
"stringValue": "디지털치료기기효과평가료"
},
"date": {
"stringValue": "2025-09-24"
},
"days": {
"integerValue": "1"
}
}
}
},
"0": {
"mapValue": {
"fields": {
"date": {
"stringValue": "2025-09-24"
},
"category": {
"stringValue": "진찰료"
},
"amount": {
"integerValue": "19100"
},
"name": {
"stringValue": "초진진찰료-종합병원"
},
"count": {
"integerValue": "1"
},
"code": {
"stringValue": "AA156"
},
"days": {
"integerValue": "1"
}
}
}
}
}
}
},
"patientInfo": {
"mapValue": {
"fields": {
"type": {
"stringValue": "건강보험"
},
"room": {
"nullValue": null
},
"name": {
"stringValue": "이유민"
},
"registrationNumber": {
"stringValue": "12380981"
}
}
}
}
}
}
},
"gcsUris": {
"arrayValue": {
"values": [
{
"stringValue": "gs://medical-statement-prod/dta-wir/2025/09/1758677416465-ujzvxn.1758677415796_dee80075-6896-4274-9cb6-b3d013cf9b7b_0"
}
]
}
},
"error": {
"nullValue": null
},
"accesscodeId": {
"integerValue": "952"
},
"key": {
"stringValue": "Android-4fa95b2f-7b51-4cd4-8017-082c13f8f232"
}
},
"createTime": "2025-09-24T01:30:28.137847Z",
"updateTime": "2025-09-24T01:30:29.140902Z"
}

2.4 분석 완료 알림​

분석이 완료되면 Pub/Sub을 통해 후속 처리를 위한 메시지를 발행합니다:

  • Pub/Sub 토픽: dta-wir-medical-statement-analyzed

3. OCR 검토 작업 등록 단계​

3.1 검토 작업 등록 API 호출​

Pub/Sub 메시지를 수신하여 검토 작업 등록 API를 호출합니다:

  • API 경로: Pub/Sub → dta-wir-api-ts
  • 엔드포인트: medical-statement/register-review-task

3.2 검토 작업 데이터 생성 및 저장​

서버에서 다음 작업을 수행합니다:

고유 해시 키 생성​

환자등록번호, 진료일자, 요양기관명을 조합하여 해시로 변환합니다.

Firestore 저장​

검토 작업 정보를 Firestore에 저장합니다:

  • 저장 위치: Firestore
  • 컬렉션: medical-statement/review-tasks-todo

검토 작업 데이터 구조:

{
"id": "38e0990291cc721ed32e1c2f9ce8851b1a644d5b9521b0671ee8e15fd52f4126",
"result": "PENDING",
"status": "TODO"
}

3.3 검토 작업 생성 알림​

검토 작업이 생성되면 Pub/Sub을 통해 알림 메시지를 발행합니다:

  • Pub/Sub 토픽: dta-wir-medical-statement-review-task-created

4. 검토 요청 알림 단계​

4.1 검토 요청 API 호출​

Pub/Sub 메시지를 수신하여 검토 요청 API를 호출합니다:

  • API 경로: Pub/Sub → dta-wir-api-ts
  • 엔드포인트: medical-statement/request-review-task

4.2 Slack 알림 전송​

서버에서 검토자에게 Slack을 통해 검토 요청 메시지를 전송합니다.

Slack 메시지 내용:​

Request: 진료비 세부산정내역서 검토가 필요합니다.
• 원본 이미지 1 URI

검토 요청 데이터:​

{
"taskId": "38e0990291cc721ed32e1c2f9ce8851b1a644d5b9521b0671ee8e15fd52f4126",
"createdAt": "2025-09-24T01:30:28.144Z",
"patientInfo": {
"registrationNumber": "12380981",
"name": "이유민",
"type": "건강보험",
"room": null
},
"treatmentInfo": {
"period": "2025-09-24~2025-09-24",
"treatmentDate": "2025-09-24",
"careInstitutionName": "31100511",
"careInstitutionCode": "31100511",
"departmentName": null,
"isSleepQ": true
}
}

5. OCR 검토 실행 단계​

5.1 검토 시작​

검토자가 Slack 메시지에서 "지금 검토하기" 버튼을 클릭합니다.

5.2 검토 시스템 이동​

시스템이 dta-wir-api-chat-ts로 이동하여 다음 작업을 수행합니다:

진행 중 작업 등록​

검토 작업 상태를 진행 중으로 변경하여 Firestore에 저장합니다:

  • 저장 위치: Firestore
  • 컬렉션: review-tasks-in-progress

5.3 CRM 대시보드 연동​

CRM 대시보드에서 해당 Site를 등록하여 검토 프로세스를 완료합니다.