이 형태는 디바이스 펌웨어와 Flutter 앱이 의존하는 동결 컨트랙트입니다. 변경하려면 디바이스 + 앱과 협의해 양쪽을 함께 배포해야 합니다. 전략적 결정 배경은
../WEB_REBUILD_PLAN.md §3.
- Base URL:
/api/v1 - Content-Type:
application/json - 인증: 없음 (클리닉 LAN 내부 신뢰 모델)
- 에러 응답: 모든 4xx/5xx 는
{ status, error, timestamp }형태의 sanitized envelope
- Endpoint:
GET /api/v1/patients - 설명: 시스템에 등록된 모든 환자 정보를 조회합니다.
- 요청 (Request):
- (없음)
- 응답 (Response):
200 OK- Body:
List<Patient> - 예시:
[ { "id": 1, "patientId": "p001", "name": "환자A", "age": 30, "sex": "male", "height": 175.0, "weight": 70.0 }, { "id": 2, "patientId": "p002", "name": "환자B", "age": 42, "sex": "female", "height": 165.0, "weight": 55.0 } ]
- Body:
- Endpoint:
POST /api/v1/patients - 설명: 새 환자를 등록합니다.
- 요청 (Request):
- Body:
PatientDto - 예시:
{ "patientId": "p100", "name": "신환자", "age": 25, "sex": "female", "height": 165.0, "weight": 55.0 }
- Body:
- 응답 (Response):
201 Created— 생성된 엔티티의 전체 키 (위 1.1과 동일 shape).
- Endpoint:
POST /api/v1/measurements/start - 설명: 새로운 측정 세션을 생성하고 시작합니다.
- 요청 (Request):
- Body:
Map<String, String> - 예시:
{ "patientId": "p001", "memo": "warmup set 1" }
- Body:
- 응답 (Response):
200 OK- Body:
Map<String, Long>— 키 이름은 정확히measurement_Id(대문자 I, 동결). - 예시:
{ "measurement_Id": 123 }
- Body:
- Endpoint:
POST /api/v1/measurements/{id}/data - 설명: 2.1에서 발급받은
measurement_Id에 해당하는 세션에 데이터 포인트 리스트를 일괄 저장합니다. - 요청 (Request):
- Path Variable:
id(측정 세션 ID, 예:123) - Body:
List<Map<String, Object>>— 각 항목은time_offset_ms(Number) +kg_value(Number) 필수. - 예시:
[ { "time_offset_ms": 10, "kg_value": 1.98 }, { "time_offset_ms": 20, "kg_value": 2.12 }, { "time_offset_ms": 30, "kg_value": 2.14 } ]
- Path Variable:
- 응답 (Response):
200 OK(body 없음). 비-숫자 값 / null 항목 / 누락 필드는 모두400 Bad Requestsanitized envelope.
- Endpoint:
POST /api/v1/measurements/{id}/stop - 설명: 2.1에서 시작한 측정 세션을 종료 상태로 변경합니다.
- 요청 (Request): Path Variable
id만. - 응답 (Response):
200 OK(body 없음).
- Endpoint:
GET /api/v1/patients/{patientId}/measurements - 설명: 특정 환자가 수행한 모든 측정 세션의 요약 정보 목록.
- 요청 (Request): Path Variable
patientId(외부 ID, 예:p001). - 응답 (Response):
200 OK- Body:
List<MeasurementSummaryDto>— camelCase. - 예시:
[ { "measurementId": 123, "startTime": "2026-05-01T10:30:00", "endTime": "2026-05-01T10:31:42", "memo": "warmup" }, { "measurementId": 124, "startTime": "2026-05-02T14:15:00", "endTime": null, "memo": null } ] - 알 수 없는 환자 →
404 Not Foundsanitized envelope.
- Body:
- Endpoint:
GET /api/v1/measurements/{id}/data - 설명: 특정 세션의 모든 데이터 포인트 (시간 오름차순).
- 요청 (Request): Path Variable
id(측정 세션 ID). - 응답 (Response):
200 OK- Body:
List<DataPointDto>— camelCase. - 예시:
[ { "timeOffsetMs": 0, "kgValue": 0.0 }, { "timeOffsetMs": 49, "kgValue": 0.0 }, { "timeOffsetMs": 103, "kgValue": 0.00917744591680136 }, { "timeOffsetMs": 165, "kgValue": 0.375255566375878 }, { "timeOffsetMs": 214, "kgValue": 1.2980987391209 } ]
- Body:
- 모든 와이어 형태는
src/test/java/com/project/urp/controller/*ContractTest.java의 contract 테스트로 박제되어 있습니다. measurement_Id의 대문자 I는MeasurementLifecycleContractTest가 양성 + 음성 양쪽으로 락합니다 (소문자measurement_id가 존재하면 fail).- 프론트엔드는
frontend/src/features/**/schema.ts의 Zod 스키마로 같은 컨트랙트를 검증하므로, 백엔드 변경이 있을 경우 양쪽이 동시에 빨갛게 됩니다.