# FHIR R4를 사용하여 의료 기록 저장 방법

> 튜토리얼: 환자, 혈압 관찰 기록, 상태를 생성하고, 트랜잭션을 사용하며, 검색, 기록과 함께 업데이트, 출처 읽기를 수행합니다. 모든 요청은 무료 샌드박스에서 curl로 실행되며 실제 응답을 보여줍니다.

Source: https://developers.anpheros.com/ko/guides/store-medical-records-fhir

이 튜토리얼에서는 HL7 FHIR R4 리소스로 작은 의료 기록을 저장합니다. 즉, `Patient`, 혈압 `Observation`, 진단 `Condition`을 생성한 다음, 환자와 측정값을 하나의 트랜잭션으로 묶습니다. 그런 다음 이를 검색하고, 이전 버전을 잃지 않고 진단을 업데이트하며, 누가 무엇을 작성했는지 출처를 읽습니다. 모든 요청은 무료 Anpheros 샌드박스에 대한 일반 `curl`이며, 표시되는 모든 응답은 API가 반환하는 내용입니다.

FHIR이 처음이라면 먼저 [FHIR 플랫폼](https://developers.anpheros.com/ko/guides/fhir)에서 개념을 읽어보세요. 이 페이지는 실습 부분입니다.

## 대부분의 레코드가 시작하는 세 가지 리소스

| 리소스 | 내용 | 코딩 방식 |
|---|---|---|
| `Patient` | 레코드가 누구에 관한 것인지: 이름, 생년월일, 성별, 다른 시스템의 식별자 | 귀하의 식별자 시스템 |
| `Observation` | 측정되거나 보고된 모든 것: 활력 징후, 검사 결과, 체중, 증상 점수 | LOINC, UCUM 단위 |
| `Condition` | 진단 또는 건강 문제, 시간에 따른 상태 포함 | ICD-10 (라이선스 플래그 뒤에 SNOMED CT) |

Medications (`MedicationStatement`), allergies (`AllergyIntolerance`), immunizations 및 documents도 동일한 패턴을 따릅니다. [26가지 지원 리소스 유형](https://developers.anpheros.com/ko/guides/fhir)은 `/fhir/R4/metadata`의 capability statement에 나열되어 있습니다.

## 시작하기 전에

[platform.anpheros.com/dashboard](https://platform.anpheros.com/dashboard/)에서 **Get a sandbox key**를 클릭하여 샌드박스 키(`sk_test_…`)를 발급받으세요. 이곳에서 생성하는 모든 데이터는 실제 환자 데이터가 아니며, 다른 프로젝트에 노출되지 않습니다.

```bash
export BASE=https://platform.anpheros.com/fhir/R4
export KEY=sk_test_…
```

## 1단계: 환자 생성

```bash
curl -X POST "$BASE/Patient" -H "Authorization: Bearer $KEY" -H "Content-Type: application/fhir+json" -d '{
  "resourceType": "Patient",
  "identifier": [{"system": "https://clinic.example/mrn", "value": "MRN-10442"}],
  "name": [{"family": "Marin", "given": ["Ioana"]}],
  "gender": "female",
  "birthDate": "1979-04-12"
}'
```

서버가 할당한 id와 버전을 포함한 `201 Created`:

```json
{"resourceType": "Patient",
 "identifier": [{"system": "https://clinic.example/mrn", "value": "MRN-10442"}],
 "name": [{"family": "Marin", "given": ["Ioana"]}], "gender": "female", "birthDate": "1979-04-12",
 "id": "d09e3942-ed86-4458-b099-fa674ab21b84",
 "meta": {"versionId": "1", "lastUpdated": "2026-10-12T20:16:51.043Z"}}
```

자신이 제어하는 `system`(여기서는 클리닉의 기록 번호)과 함께 자신의 id를 `identifier`에 보관하여 `GET $BASE/Patient?identifier=https://clinic.example/mrn|MRN-10442`로 환자를 항상 다시 찾을 수 있도록 하십시오. FHIR `id`는 플랫폼의 것이며 프로젝트별로 고유합니다. 다른 프로젝트에서는 동일한 사람을 다른 id로 볼 수 있습니다.

```bash
export PID=d09e3942-ed86-4458-b099-fa674ab21b84
```

## 2단계: 측정값을 Observation으로 저장하기

혈압은 두 개의 구성 요소(수축기 및 이완기)를 가진 하나의 관찰 항목이며, 각 구성 요소는 고유한 LOINC 코드를 가집니다. 누가 측정했는지 명시하세요. 여기서는 가정용 장치입니다.

```bash
curl -X POST "$BASE/Observation" -H "Authorization: Bearer $KEY" -H "Content-Type: application/fhir+json" \
     -H "X-Anpheros-Author-Type: device" -H "Idempotency-Key: bp-2026-10-10-0830" -d '{
  "resourceType": "Observation",
  "status": "final",
  "category": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/observation-category", "code": "vital-signs"}]}],
  "code": {"coding": [{"system": "http://loinc.org", "code": "85354-9", "display": "Blood pressure panel"}]},
  "subject": {"reference": "Patient/'$PID'"},
  "effectiveDateTime": "2026-10-10T08:30:00Z",
  "component": [
    {"code": {"coding": [{"system": "http://loinc.org", "code": "8480-6"}]},
     "valueQuantity": {"value": 138, "unit": "mmHg", "system": "http://unitsofmeasure.org", "code": "mm[Hg]"}},
    {"code": {"coding": [{"system": "http://loinc.org", "code": "8462-4"}]},
     "valueQuantity": {"value": 88, "unit": "mmHg", "system": "http://unitsofmeasure.org", "code": "mm[Hg]"}}
  ]
}'
```

응답은 `id` 및 `meta.versionId: "1"`이 포함된 저장된 리소스입니다. 유용한 기록을 위해 세 가지 세부 정보가 중요합니다.

- **레이블이 아닌 코드.** `85354-9`는 모든 FHIR 시스템에서 "혈압 패널"을 의미합니다. 텍스트로 된 "BP"는 그렇지 않습니다. `GET /v1/terminology/loinc?q=blood pressure` ([FHIR 코드 시스템](https://developers.anpheros.com/ko/guides/fhir-code-systems))을 사용하여 코드를 조회하세요.
- **UCUM 단위** (`mm[Hg]`, `kg`, `mg/dL`)를 `valueQuantity.code`에 포함하여 값을 비교하고 변환할 수 있도록 합니다.
- **`Idempotency-Key`.** 타임아웃 후 재시도하면 판독값을 두 번 저장하는 대신 첫 번째 응답을 반환합니다.

검사 결과는 `category` `laboratory` 및 일반적으로 `referenceRange`이 포함된 동일한 리소스입니다. 전체 검사 보고서를 가져오려면 [FHIR 기록으로 검사 결과 가져오기](https://developers.anpheros.com/ko/guides/import-lab-results-fhir)를 참조하세요.

## 3단계: 진단을 Condition으로 기록하기

```bash
curl -X POST "$BASE/Condition" -H "Authorization: Bearer $KEY" -H "Content-Type: application/fhir+json" \
     -H "X-Anpheros-Author-Type: practitioner" -d '{
  "resourceType": "Condition",
  "subject": {"reference": "Patient/'$PID'"},
  "clinicalStatus": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/condition-clinical", "code": "active"}]},
  "verificationStatus": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", "code": "confirmed"}]},
  "code": {"coding": [{"system": "http://hl7.org/fhir/sid/icd-10", "code": "I10", "display": "Essential (primary) hypertension"}]},
  "onsetDateTime": "2026-03-02"
}'
```

`clinicalStatus` (active, resolved, in remission)와 `verificationStatus` (confirmed, provisional)은 의도적으로 분리되어 있습니다. 의심되는 진단은 확정된 진단과 다르며, 해결된 문제는 여전히 기록의 일부입니다.

## 4단계: 단일 트랜잭션으로 관련 리소스 생성

예를 들어 클리닉 시스템에서 환자와 첫 번째 측정값이 함께 도착하는 경우, `transaction` 번들로 전송합니다. 모든 것이 저장되거나 아무것도 저장되지 않습니다. 새 리소스 간의 참조는 서버가 교체하는 임시 `urn:uuid:` ID를 사용합니다.

```bash
curl -X POST "$BASE" -H "Authorization: Bearer $KEY" -H "Content-Type: application/fhir+json" -d '{
  "resourceType": "Bundle", "type": "transaction",
  "entry": [
    {"fullUrl": "urn:uuid:9f1c0d1e-0000-4000-8000-000000000001",
     "resource": {"resourceType": "Patient", "name": [{"family": "Stan", "given": ["Mihai"]}], "gender": "male", "birthDate": "1990-02-01"},
     "request": {"method": "POST", "url": "Patient"}},
    {"resource": {"resourceType": "Observation", "status": "final",
       "category": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/observation-category", "code": "vital-signs"}]}],
       "code": {"coding": [{"system": "http://loinc.org", "code": "29463-7", "display": "Body weight"}]},
       "subject": {"reference": "urn:uuid:9f1c0d1e-0000-4000-8000-000000000001"},
       "effectiveDateTime": "2026-10-10",
       "valueQuantity": {"value": 82.4, "unit": "kg", "system": "http://unitsofmeasure.org", "code": "kg"}},
     "request": {"method": "POST", "url": "Observation"}}
  ]
}'
```

`transaction-response`은 리소스당 하나의 항목을 가지며, `"status": "201 Created"`, 해당 `location` 및 저장된 리소스를 포함합니다. Observation의 `subject`는 이제 실제 `Patient/fad3665a-…`을 가리킵니다.

## 5단계: 검색

```bash
curl "$BASE/Observation?patient=$PID&code=http://loinc.org|85354-9&date=ge2026-10-01&_sort=-date" \
     -H "Authorization: Bearer $KEY"
```

검색 결과로 `searchset` Bundle을 받게 되며, 여기에는 `total`와 일치하는 리소스가 최신순으로 포함됩니다. 자주 사용하게 될 다른 검색은 다음과 같습니다: `Condition?patient=$PID&clinical-status=active`, `Observation?patient=$PID&category=laboratory`, 그리고 전체 기록을 위한 `Patient/$PID/$everything`입니다.

## 6단계: 기록 손실 없이 업데이트

고혈압이 해결되었습니다. 상태를 변경하고 종료 날짜를 추가하며, 다른 사람의 변경 사항을 덮어쓰지 않도록 읽은 버전을 `If-Match`과 함께 사용합니다.

```bash
curl -X PUT "$BASE/Condition/$CID" -H "Authorization: Bearer $KEY" -H "Content-Type: application/fhir+json" \
     -H 'If-Match: W/"1"' -d '{ …the same Condition…, "clinicalStatus": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/condition-clinical", "code": "resolved"}]}, "abatementDateTime": "2026-10-11" }'
```

`200 OK`을 `ETag: W/"2"`과 함께 사용합니다. 다른 쓰기가 먼저 발생하면 `412` 오류가 발생하며 다시 읽어야 합니다. 이전 버전은 그대로 유지됩니다.

```bash
curl "$BASE/Condition/$CID/_history" -H "Authorization: Bearer $KEY"
```

두 버전 모두 반환합니다. `versionId` 2 (`resolved`) 및 1 (`active`)입니다. `GET $BASE/Condition/$CID/_history/1`은 이전 버전을 읽습니다. 레코드의 어떤 것도 조용히 덮어쓰이지 않습니다.

## 7단계: 누가 무엇을 작성했는지 확인

모든 작성은 `Provenance` 리소스를 생성합니다. 혈압 측정값의 경우:

```bash
curl "$BASE/Provenance?target=Observation/$OBS" -H "Authorization: Bearer $KEY"
```

```json
{"resourceType": "Provenance",
 "target": [{"reference": "Observation/d83d9075-…/_history/1"}],
 "recorded": "2026-10-12T20:16:51.056000Z",
 "activity": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", "code": "CREATE"}]},
 "agent": [{"type": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", "code": "author"}]},
            "who": {"identifier": {"system": "https://anpheros.com/fhir/CodeSystem/author-type", "value": "device"}, "display": "proj_…"},
            "onBehalfOf": {"identifier": {"system": "https://anpheros.com/fhir/sid/organization", "value": "org_…"}, "display": "…"}}]}
```

전송한 작성자 유형(`device`)은 프로젝트 및 조직과 함께 기록됩니다. 작성자의 신원을 명시하려면 `X-Anpheros-Author-Ref: Practitioner/<id>`을 전송하십시오.

## 8단계: 요약으로서의 기록

```bash
curl "$BASE/Patient/$PID/\$summary" -H "Authorization: Bearer $KEY"
```

International Patient Summary(국제 환자 요약)를 반환합니다. 이는 `document` Bundle이며 `Composition`로 시작하고, 위 리소스들로부터 구성된 Problems, Medications, Allergies, Vital signs와 같은 섹션을 포함합니다. 이는 다른 시스템에 전달하거나 의사에게 인쇄하기 위한 형식입니다 ([FHIR platform](https://developers.anpheros.com/ko/guides/fhir)).

## FHIR 없이 JSON을 선호하시나요?

동일한 데이터는 더 간단한 REST 뷰, `/v1`를 통해서도 이용 가능하며, 동일한 ID를 사용합니다. `POST /v1/patients/{id}/observations`은 `{"code": "85354-9", "category": "vital-signs", "effective_at": "…", "components": […]}`을 가져와 동일한 FHIR Observation을 저장합니다. 코드에 더 적합한 것을 사용하세요. [Medical data API](https://developers.anpheros.com/ko/guides/medical-data-api) 및 [SDKs](https://developers.anpheros.com/ko/guides/sdks)를 참조하세요.

## 자주 묻는 질문

### 의료 기록을 저장하는 가장 좋은 방법은 무엇인가요?
코드로 작성되고 버전이 관리되며 각 항목을 누가 작성했는지 기록된 리소스입니다. HL7 FHIR R4는 구조(Patient, Observation, Condition 등), LOINC 및 ICD-10과 같은 표준 코드 시스템, 기록 기능을 제공합니다. FHIR 백엔드는 검색, 동의 및 출처 관리를 추가합니다.

### FHIR을 사용하기 위해 FHIR 서버가 필요한가요?
FHIR 리소스를 저장하고 검색하며 누가 읽을 수 있는지 강제할 수 있는 곳이 필요합니다. 자체적으로 오픈 소스 FHIR 서버를 실행하거나 Anpheros와 같은 관리형 서버를 사용할 수 있습니다. [Anpheros와 다른 FHIR 서버 비교](https://developers.anpheros.com/ko/guides/fhir-server-comparison)에서 옵션을 비교하고, [FHIR 대 사용자 지정 의료 데이터베이스](https://developers.anpheros.com/ko/guides/fhir-vs-custom-database)에서는 자체 스키마를 구축하는 방법을 다룹니다.

### FHIR에서 혈압을 어떻게 저장하나요?
코드 LOINC `85354-9`를 가진 하나의 `Observation`와 두 개의 `component` 항목: 수축기 `8480-6` 및 이완기 `8462-4`로 저장하며, 각 항목은 `mm[Hg]`의 `valueQuantity`를 가집니다.

### 저장된 리소스를 변경할 수 있나요?
네, `PUT` 및 `If-Match`를 사용하여 변경할 수 있습니다. 각 변경은 새 버전을 생성하며, 이전 버전은 `_history`를 통해 계속 읽을 수 있고, Provenance는 각 변경을 누가 했는지 기록합니다. 삭제는 소프트 삭제이며 기록을 유지합니다.

## 관련 자료

- [FHIR 플랫폼](https://developers.anpheros.com/ko/guides/fhir): 개념, 리소스 유형 및 작업
- [FHIR 코드 시스템](https://developers.anpheros.com/ko/guides/fhir-code-systems): LOINC, ICD-10, ATC, UCUM
- [FHIR 레코드에 실험실 결과 가져오는 방법](https://developers.anpheros.com/ko/guides/import-lab-results-fhir)
- [Medical data API](https://developers.anpheros.com/ko/guides/medical-data-api): 동일한 데이터의 더 간단한 REST 뷰
- [무료 FHIR 데이터베이스 및 샌드박스](https://developers.anpheros.com/ko/guides/free-fhir-database)
