Django 미들웨어로 OpenEMR과 Orthanc 연동
Django 기반 미들웨어를 활용한 OpenEMR-Orthanc 연동 개발 및 이슈 해결 보고서
1. 개요
본 문서는 Docker 환경에 구성된 OpenEMR(EMR)과 Orthanc(PACS) 시스템을 Django 기반 미들웨어로 연동하는 프로젝트의 개발 및 디버깅 과정을 기록한 기술 보고서이다. 프로젝트의 핵심 목표는 DICOM 파일 업로드 시 EMR의 환자 정보를 조회 및 연동하고, EMR에 해당 환자가 없을 경우 DICOM 메타데이터를 기반으로 신규 환자를 자동 생성하는 워크플로우를 REST API 기반으로 구현하는 것이다.
2. 주요 개발 및 이슈 해결 과정
2.1. 초기 서버 접속 및 네트워크 설정 문제 해결
개발 초기 단계에서 API 테스트를 진행하기 위한 서버 접속 및 네트워크 환경 설정 과정에서 다음과 같은 문제들이 발생했으며, 순차적으로 해결했다.
- 서버 바인딩 주소: Django 개발 서버의 기본 바인딩 주소는
127.0.0.1로, 외부에서의 접속을 허용하기 위해 실행 옵션을0.0.0.0:8000으로 변경했다. - 클라우드 방화벽: 외부 IP를 통한 접속을 위해 Google Cloud Platform(GCP)의 방화벽 규칙에 TCP 포트
8000에 대한 수신(인그레스)을 허용하는 정책을 추가했다. - 프로토콜 불일치: Django 개발 서버는 HTTP만 지원하나, API 클라이언트(Postman)에서
https://로 요청하여 발생한Bad request version오류는 요청 프로토콜을 명시적인http://로 변경하여 해결했다. - Trailing Slash (
/): Django의APPEND_SLASH=True기본 설정으로 인해,POST요청 시 URL 끝에 슬래시(/)가 없어 발생한RuntimeError는 요청 URL에/를 추가하여 해결했다.
2.2. 인증 체계 확립: Django JWT와 OpenEMR OAuth2
본 연동 시스템은 두 가지의 독립적인 인증 토큰 체계를 요구하며, 각 토큰의 목적과 사용처를 명확히 구분해야 했다.
- Django API 인증 (JWT):
- 발급: Django의
/token/엔드포인트에서 Django 사용자 DB의 자격 증명으로 발급. - 용도: Django 미들웨어가 제공하는 API 엔드포인트(예:
/pacs/...) 호출 시Authorization헤더에 사용.
- 발급: Django의
- OpenEMR API 인증 (OAuth2):
- 발급: OpenEMR의
/oauth2/default/token엔드포인트에서 OpenEMR API 클라이언트 자격 증명으로 발급. - 용도: Django 미들웨어 내부의
OpenEMRClient가 OpenEMR의 API(예:/api/patient)를 호출할 때 사용.
- 발급: OpenEMR의
초기 테스트 과정에서 OpenEMR API를 호출하는 Django API를 테스트하며, Authorization 헤더에 OpenEMR용 토큰을 잘못 사용하여 발생한 401 Unauthorized 오류는 각 토큰의 역할을 명확히 구분하여 해결했다.
2.3. Django-OpenEMR API 연동 및 디버깅
Django와 OpenEMR 간의 실제 API 통신 과정에서 발생한 주요 이슈와 해결 과정은 다음과 같다.
- SSL 인증서 호스트 이름 불일치: IP 주소로 OpenEMR API를 호출했을 때, OpenEMR 서버의 SSL 인증서에 명시된 호스트 이름(
cdssb1a4.duckdns.org)과 일치하지 않아SSLError가 발생했다. Django의settings.py에 정의된 OpenEMR 관련 URL을 모두 IP 주소에서 호스트 이름으로 변경하여 해결했다. - OpenEMR 인증 Grant Type:
Client Credentials Grant방식이 OpenEMR의 특정 구현(JWT Assertion 요구)으로 인해 복잡성이 높아, OpenEMR 관리자 설정에서Password Grant를 활성화하고OpenEMRClient의 인증 로직을 이에 맞춰 수정하여 안정적인 토큰 발급을 구현했다. - API 응답 구조 차이: OpenEMR의 환자 목록 조회 API (
/api/patient)와 단일 환자 조회 API (/api/patient/{uuid})의 JSON 응답 구조가 상이했다. 단일 환자 조회 시 실제 데이터가{"data": {...}}객체 내부에 중첩되어 있어,views.py의 파싱 로직을 수정하여 이를 처리했다. API 연동 시 실제 응답을 로깅하여 구조를 직접 확인하는 과정이 필수적이었다.
2.4. 핵심 기능 구현: DICOM 기반 환자 자동 생성
DICOM 업로드 시 OpenEMR에 해당 환자가 없으면 자동으로 생성하는 기능은 다음의 디버깅 과정을 통해 완성되었다.
- 초기 실패: 존재하지 않는 환자 ID의 DICOM 업로드 시,
OpenEMRClient는 환자 조회 API로부터400 Bad Request(invalid or nonexisting value)를 수신했다. - 오류 처리 로직 수정: 기존에는
404 Not Found에 대해서만 환자 생성 로직을 트리거했으나,400오류 코드도 “환자 없음”으로 간주하도록DICOMUploadView의 예외 처리 로직을 확장했다. - OpenEMR 유효성 검사 실패: 새 환자 생성 로직 진입 후, OpenEMR의 환자 생성 API는
fname(이름),DOB(생년월일) 필드가 비어있고,sex(성별) 값이 유효성 규칙(4글자 이상)에 맞지 않아 다시400 Bad Request를 반환했다. - 최종 해결:
DICOMUploadView내에서 OpenEMR에 환자 생성을 요청하기 전, 다음과 같은 데이터 처리 및 유효성 검사 로직을 추가하여 문제를 해결했다.- DICOM의
PatientName(성^이름) 태그를 파싱하여fname,lname을 추출. - DICOM의
PatientSex(F/M) 값을Female/Male로 매핑. - OpenEMR API 호출 전, 이름과 생년월일 등 필수값이 모두 유효한지 확인. 필수값이 없는 경우 환자 생성을 시도하지 않고 경고 로그만 남기도록 처리.
- DICOM의
2.5. 최종 기능 검증: DICOM 다운로드
모든 연동 완료 후 DICOM 다운로드 테스트에서 404 Not Found 오류가 발생했다. 이는 다운로드 요청 URL에 DICOM 이미지 각각의 고유 ID인 Instance UID 대신, 전체 검사를 의미하는 Study Instance UID를 잘못 사용했기 때문이었다. Orthanc 업로드 응답에서 반환된 올바른 Orthanc 내부 Instance ID를 사용하여 API를 호출함으로써 최종적으로 다운로드 기능까지 성공적으로 검증을 완료했다.
3. 결론
본 개발 과정은 이종(異種) 시스템인 EMR과 PACS를 연동할 때 발생할 수 있는 다양한 기술적 과제를 보여준다. 특히 ▲독립적인 인증 체계의 명확한 이해, ▲API의 실제 응답 구조 확인, ▲연동 대상 시스템의 데이터 유효성 규칙 준수, ▲로그 기반의 체계적인 디버깅이 안정적인 미들웨어 구축의 핵심 요소임을 확인했다. 현재 구현된 파이프라인은 향후 AI 분석 모듈 및 추가적인 CDSS 기능 확장을 위한 견고한 기반이 될 것이다.