본문으로 건너뛰기

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를 등록하여 검토 프로세스를 완료합니다.