본문으로 건너뛰기

슬립큐 앱 진료비 세부산정내역서 업로드/등록 기능 개선 Tech Spec

요약 (Summary)​

배경 (Background)​

현재 슬립큐 앱의 진료비 세부산정내역서 업로드 기능은 현재 구현 사양에서 확인할 수 있습니다.

목표 (Goals)​

목표가 아닌 것 (Non-Goals)​

계획 (Plan) - 개발을 위한 세부적인 모든 내용​

리팩토링 목표​

  • OCR 처리 로직을 범용 API 서버(dta-wir-api-universal)로 분리
  • 기존 모바일 앱의 엔드포인트 변경 최소화
  • 이벤트 기반 아키텍처로 전환하여 확장성 향상
  • 서비스 간 명확한 책임 분리

시스템 구성 요소 변경사항​

기존 (AS-IS)​

  • Frontend: 모바일 앱 → dta-wir-api-ts
  • Backend API: dta-wir-api-ts (모든 OCR 처리 담당)
  • Chat API: dta-wir-api-chat-ts (검토 시스템)

변경 후 (TO-BE)​

  • Frontend: 모바일 앱 → Load Balancer (URL rewrite) → dta-wir-api-universal (직접 연결)
  • OCR Backend API: dta-wir-api-universal (OCR 처리 전담)
  • Chat API: dta-wir-api-chat-ts (검토 시스템, 유지)

📍 라우팅 방식: Load Balancer에서 URL rewrite를 통해 앱의 기존 엔드포인트(/accesscodes/medical-statements)를 dta-wir-api-universal로 직접 라우팅합니다. 이를 통해 불필요한 프록시 레이어를 제거하고 응답 속도를 개선했습니다.


전체 프로세스 플로우 다이어그램​


1. OCR 업로드 단계​

1.1 환자의 이미지 업로드​

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

변경사항: 없음 (기존과 동일)


1.2 API 호출 및 라우팅​

1.2.1 앱에서 기존 엔드포인트 호출 (변경 없음)​

모바일 앱은 기존 엔드포인트를 그대로 사용합니다.

  • 앱의 API 호출: POST /accesscodes/medical-statements
  • 엔드포인트 URL: 변경 없음 (앱 배포 불필요)

설계 의도: 앱에서 API 엔드포인트를 변경하는 것은 배포 부담이 크므로, 인프라 레벨(Load Balancer)에서 라우팅을 변경하여 하위 호환성을 유지합니다.

1.2.2 Load Balancer의 URL Rewrite (인프라 변경)​

Load Balancer에서 URL rewrite 규칙을 통해 요청을 dta-wir-api-universal로 직접 라우팅합니다.

  • 라우팅 규칙: POST /ts/v1/auth/accesscodes/medical-statements → dta-wir-api-universal로 rewrite하여 /v1/auth/accesscodes/medical-statements로 처리
  • 장점:
    • 불필요한 프록시 레이어 제거
    • 네트워크 홉(hop) 감소로 응답 속도 개선
    • dta-wir-api-ts의 부하 감소

URL Rewrite 설정 (Terraform):

{
priority = 8
service = "https://www.googleapis.com/compute/v1/projects/${local.project}/global/backendServices/dta-wir-api-universal"
match_rules = {
prefix_match = "/ts/v1/auth/accesscodes/medical-statements"
}
route_action = {
url_rewrite = {
path_prefix_rewrite = "/v1/auth/accesscodes/medical-statements"
}
}
}

동작 방식:

  1. 모바일 앱이 POST /ts/v1/auth/accesscodes/medical-statements로 요청
  2. Load Balancer가 prefix match 규칙에 따라 요청을 캐치
  3. URL rewrite를 통해 /v1/auth/accesscodes/medical-statements로 경로 변경
  4. dta-wir-api-universal 백엔드 서비스로 라우팅

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

dta-wir-api-universal에서 OCR 업로드의 핵심 로직을 처리합니다.

1.3.1 이미지 저장​

기존과 동일하게 Cloud Storage에 이미지를 저장합니다.

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

1.3.2 액세스 코드 생성​

데이터베이스에 액세스 코드를 생성합니다.

액세스 코드 생성 파라미터:

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

1.3.3 이벤트 발행​

Cloud Pub/Sub의 events 토픽에 OCR 등록 완료 이벤트를 발행합니다.

  • Pub/Sub 토픽: events
  • 이벤트 타입: ocr.medical-statement.created

이벤트 메시지 구조:

{
"eventType": "ocr.medical-statement.created",
"data": {
"accessCodeId": 952,
"deviceId": "Android-4fa95b2f-7b51-4cd4-8017-082c13f8f232",
"gcsUris": ["gs://medical-statement-prod/dta-wir/2025/10/1758677416465-ujzvxn.1758677415796_dee80075-6896-4274-9cb6-b3d013cf9b7b_0"],
"imageCount": 1,
"uploadedAt": "2025-10-10T12:34:56.789Z"
}
}

1.3.4 응답 반환​

클라이언트(앱)에게 업로드 완료 응답을 반환합니다.

Response Body:

{
"success": true,
"accessCodeId": 952,
"uploadedAt": "2025-10-10T12:34:56.789Z",
"imageCount": 1,
"status": "UPLOADED",
"message": "이미지가 성공적으로 업로드되었습니다. 분석이 시작됩니다."
}

2. OCR 분석 및 검토 등록 단계​

✨ 프로세스 통합: 기존 시스템에서는 "분석 → 검토 작업 등록 → 검토 요청 알림"이 3개의 독립적인 단계로 분리되어 있었으나, 이벤트 기반 아키텍처로 전환하면서 하나의 통합된 프로세스로 재설계했습니다. 이를 통해 불필요한 Pub/Sub 토픽 홉(hop)을 제거하고, 처리 시간을 단축하며, 데이터 일관성을 향상시킬 수 있습니다.

2.1 이벤트 기반 분석 프로세스​

2.1.1 이벤트 수신 및 처리 시작​

Cloud Pub/Sub의 events 토픽을 구독하여 ocr.medical-statement.created 이벤트를 수신하고 처리를 시작합니다.

  • 이벤트 소스: events 토픽 (Pub/Sub)
  • 처리 시스템: dta-wir-api-universal
  • 이벤트 타입: ocr.medical-statement.created

처리 흐름:

events 토픽 (Pub/Sub)
↓ (이벤트 구독)
dta-wir-api-universal
↓ (이벤트 핸들러 실행)
ocr.medical-statement.created 로직

2.1.2 dta-ocr-api 호출 - 이미지 분석​

dta-ocr-api를 호출하여 업로드된 진료비 세부산정내역서 이미지를 LLM 기반으로 분석합니다.

  • API 경로: dta-wir-api-universal → dta-ocr-api
  • 엔드포인트: POST /analyze

2.1.3 Site ID 등록 및 환자 정보 연동​

🆕 새로운 기능: 분석된 진료 정보를 바탕으로 요양기관(Site) 정보를 자동으로 등록하고, AccessCode 및 UserCycle에 환자 정보를 연동합니다. 이는 기존 시스템에서는 수동으로 처리되던 작업을 자동화하여 검토 프로세스를 개선합니다.

처리 흐름:

  1. Site 조회/생성

    • OCR 분석 결과에서 요양기관 정보 추출
    • 기존 Site 조회 (요양기관코드 기준)
    • 없으면 새로운 Site 생성
  2. AccessCode 업데이트

    • siteId: 조회/생성된 Site ID
    • medicalRegistrationNumber: OCR에서 추출한 환자등록번호(병록번호)
  3. UserCycle 업데이트 (조건부)

    • AccessCode와 연결된 UserCycle 조회
    • UserCycle이 존재하는 경우:
      • siteId: 조회/생성된 Site ID
      • medicalRegistrationNumber: OCR에서 추출한 환자등록번호(병록번호)

📌 설계 참고: UserCycle이 아직 존재하지 않는 경우(사용자가 아직 등록을 완료하지 않은 경우)에도 AccessCode에 siteId와 medicalRegistrationNumber를 저장하는 것으로 충분합니다. 실제 사용자 등록(registration) 시 dta-wir-api에서 AccessCode에 저장된 이 값들을 참조하여 UserCycle을 생성하기 때문에, 데이터 정합성이 자동으로 보장됩니다.

데이터베이스 업데이트 예시:

-- AccessCode 업데이트
UPDATE access_codes
SET
site_id = 123,
medical_registration_number = 'P202501234567'
WHERE id = 952;

-- UserCycle 업데이트 (존재하는 경우)
UPDATE user_cycles
SET
site_id = 123,
medical_registration_number = 'P202501234567'
WHERE access_code_id = 952;

2.1.4 분석 결과 저장​

🔄 저장소 변경: 기존에는 Firestore의 review-tasks-todo 컬렉션에 검토 작업을 저장했으나, 관계형 데이터베이스의 user_medical_statement 테이블로 변경합니다.

OCR 분석 결과를 user_medical_statement 테이블에 저장합니다.

저장 데이터:

  • OCR 분석 결과 (진료 정보, 금액 등)
  • status: TODO - 검토 대기 상태
  • result: PENDING - 검토 결과 대기 중

2.1.5 Slack 검토 요청 알림 전송​

분석 및 등록이 완료되면 즉시 검토자에게 Slack 알림을 전송합니다.

  • 알림 채널: Slack (검토자 전용 채널)
  • 메시지 타입: Interactive Message (버튼 포함)

Slack 메시지 전송 시점:

  • OCR 분석 완료 후 즉시 전송 (비동기)
  • 전송 실패 시 재시도 로직 포함 (최대 3회)

3. OCR 검토 프로세스​

💡 참고: 현재 버전에서는 OCR 분석 및 데이터 저장이 완료되면 Slack으로 검토 알림이 전송됩니다. 검토자는 Slack 메시지의 "지금 검토하기" 버튼을 통해 검토 프로세스를 시작할 수 있습니다.


주요 변경사항 요약​

구분기존 (AS-IS)변경 (TO-BE)변경 이유
라우팅 방식앱 → `dta-wir-api-ts₩앱 → LB (URL rewrite) → dta-wir-api-universal프록시 레이어 제거, 응답 속도 개선, 부하 분산
프로세스 구조분석 → 등록 → 알림 (3단계 분리)분석 + 등록 + 알림 (1단계 통합)불필요한 Pub/Sub 홉 제거, 처리 시간 단축
Site 등록수동 등록자동 등록 (OCR 분석 기반)검토 프로세스 자동화, 운영 효율 향상
환자 정보 연동AccessCode만 업데이트AccessCode + UserCycle 자동 업데이트
(siteId, medicalRegistrationNumber)
데이터 정합성 향상, 환자-병원 연동 자동화
데이터 저장소Firestore (review-tasks-todo)PostgreSQL (user_medical_statement)데이터 일관성, 쿼리 성능, 참조 무결성 향상
Chat API 연동dta-wir-api-ts Tool 사용dta-wir-api-universal Tool 사용불필요한 중간 레이어 제거, 직접 데이터 접근
이벤트 아키텍처Cloud Task 기반Pub/Sub 이벤트 기반확장성 및 유연성 향상, 느슨한 결합