수집 데이터 제출 API
공통 요청 헤더
모든 dha-sleep API 요청은 공통 요청 헤더를 준수해야 합니다. User-Agent, Accept-Language, 인증 헤더 요구사항을 먼저 확인하세요.
개요
수집 데이터 제출 API는 모바일 앱에서 HealthKit/Health Connect SDK를 통해 수집한 건강 데이터를 서버로 전송하는 기능을 제공합니다. GET /v1/external-health/requests로 조회한 요청에 대응하는 데이터를 수집하여 이 API로 제출합니다.
주요 특징
- 배치 제출: 여러 요청에 대한 응답을 한 번에 제출 가능
- 데이터 유형별 처리: Quantity 타입(stepCount, heartRate)과 Category 타입(sleepAnalysis)을 각각 적절한 형식으로 처리
- 시간대 보존: ISO8601 형식으로 TimeZone 정보를 함께 전송하여 정확한 시간 처리
- 백그라운드 업로드 지원: iOS Background URLSession, Android WorkManager를 통한 백그라운드 업로드 지원
데이터 수집 플로우
GET /v1/external-health/requests로 수집할 데이터 목록 조회- HealthKit/Health Connect SDK로 각 요청에 해당하는 데이터 수집
- 수집된 데이터를 이 API로 제출
수집 데이터 제출
HealthKit/Health Connect에서 수집한 건강 데이터를 서버에 제출합니다.
- HTTP Method:
POST - Path:
/v1/external-health/responses - 인증: 액세스 토큰 (
accessToken) 필요
Headers
| Header | Type | Description | Required |
|---|---|---|---|
Authorization | Bearer {accessToken} | 사용자 인증을 통해 발급받은 액세스 토큰입니다. | Yes |
Content-Type | application/json | 요청 본문 형식 | Yes |
Request Body
{
"responses": [
{
"requestId": "B437BCAC-CFFF-468C-9A30-5786F74BDB87",
"os": "iOS",
"type": "stepCount",
"startTime": "2025-12-01T10:00:00.000+09:00",
"endTime": "2025-12-02T10:00:00.000+09:00",
"interval": {
"type": "day",
"value": 1
},
"extractedAt": "2025-12-03T07:00:00.000+09:00",
"records": [
{
"startTime": "2025-12-01T10:00:00.000+09:00",
"endTime": "2025-12-02T10:00:00.000+09:00",
"sum": 8532,
"unit": "count"
}
]
},
{
"requestId": "BEC32F44-2647-44C8-8C74-1DF64097FA31",
"os": "iOS",
"type": "heartRate",
"startTime": "2025-12-01T10:00:00.000+09:00",
"endTime": "2025-12-02T10:00:00.000+09:00",
"interval": {
"type": "hour",
"value": 1
},
"extractedAt": "2025-12-03T07:00:00.000+09:00",
"records": [
{
"startTime": "2025-12-01T10:00:00.000+09:00",
"endTime": "2025-12-01T11:00:00.000+09:00",
"average": 72.5,
"unit": "count/min"
},
{
"startTime": "2025-12-01T11:00:00.000+09:00",
"endTime": "2025-12-01T12:00:00.000+09:00",
"average": 68.3,
"unit": "count/min"
}
]
},
{
"requestId": "6406D4AE-9C55-40D3-BE25-5EE0AD3859E9",
"os": "iOS",
"type": "sleepAnalysis",
"startTime": "2025-12-01T10:00:00.000+09:00",
"endTime": "2025-12-02T10:00:00.000+09:00",
"extractedAt": "2025-12-03T07:00:00.000+09:00",
"records": [
{
"startTime": "2025-12-01T23:00:00.000+09:00",
"endTime": "2025-12-01T23:05:00.000+09:00",
"category": "inBed"
},
{
"startTime": "2025-12-01T23:05:00.000+09:00",
"endTime": "2025-12-02T00:30:00.000+09:00",
"category": "asleepCore"
},
{
"startTime": "2025-12-02T00:30:00.000+09:00",
"endTime": "2025-12-02T02:00:00.000+09:00",
"category": "asleepDeep"
},
{
"startTime": "2025-12-02T02:00:00.000+09:00",
"endTime": "2025-12-02T03:30:00.000+09:00",
"category": "asleepREM"
}
]
}
]
}
요청 필드 명세
| Field | Data Type | Required | Description |
|---|---|---|---|
responses | Array<Object> | YES | 응답 배열 (최소한 빈 배열) |
response.requestId | UUID | YES | GET 요청으로 조회한 요청 ID |
response.os | String | YES | 플랫폼 (iOS 또는 Android) |
response.type | String (Enum) | YES | 데이터 유형: stepCount, heartRate, sleepAnalysis 등 |
response.startTime | String | YES | 수집 시작 시각 (ISO8601 + TimeZone) |
response.endTime | String | YES | 수집 종료 시각 (ISO8601 + TimeZone) |
response.interval | Object | NO | 집계 간격 설정 |
response.interval.type | String (Enum) | Conditional | 간격 유형: year, month, day, hour, minute, second |
response.interval.value | Int | Conditional | 집계 간격 값 |
response.extractedAt | String | YES | 데이터 추출 시각 (ISO8601 + TimeZone) |
response.records | Array<Object> | YES | 수집된 데이터 레코드 배열 (최소한 빈 배열) |
record.startTime | String | YES | 레코드 시작 시각 (ISO8601 + TimeZone) |
record.endTime | String | YES | 레코드 종료 시각 (ISO8601 + TimeZone) |
record.sum | Int | NO | 합계 값 (stepCount 등 합계 데이터용) |
record.average | Double | NO | 평균 값 (heartRate 등 평균 데이터용) |
record.category | String | NO | 카테고리 (sleepAnalysis용: inBed, asleepCore, asleepDeep, asleepREM, awake) |
record.unit | String | NO | 단위 (count, count/min 등) |
수면 카테고리 (sleepAnalysis category)
| Category | Description |
|---|---|
inBed | 침대에 누워있음 |
asleepUnspecified | 수면 (상세 단계 미지정) |
asleepCore | 코어 수면 (얕은 수면) |
asleepDeep | 깊은 수면 |
asleepREM | REM 수면 |
awake | 깨어있음 |
Responses
| HTTP Status Code | 설명 | Error Code(s) |
|---|---|---|
200 OK | 성공 | - |
400 Bad Request | 유효하지 않은 요청 ID 또는 데이터 | 21010, 21011 |
401 Unauthorized | 인증 실패 | 10001 |
409 Conflict | 중복 응답 제출 | 21012 |
200 OK - 성공
데이터 제출 성공:
{
"results": [
{
"requestId": "B437BCAC-CFFF-468C-9A30-5786F74BDB87",
"status": "COLLECTED",
"processedRecords": 1
},
{
"requestId": "BEC32F44-2647-44C8-8C74-1DF64097FA31",
"status": "COLLECTED",
"processedRecords": 2
},
{
"requestId": "6406D4AE-9C55-40D3-BE25-5EE0AD3859E9",
"status": "COLLECTED",
"processedRecords": 4
}
]
}
| 필드 | 타입 | 설명 |
|---|---|---|
results | array | 처리 결과 배열 |
results[].requestId | string | 요청 ID |
results[].status | string | 처리 상태 (COLLECTED, FAILED) |
results[].processedRecords | number | 처리된 레코드 수 |
400 Bad Request - 유효하지 않은 요청 ID
예시: 요청 ID가 존재하지 않거나 이미 처리됨 (INVALID_REQUEST_ID - 21010)
{
"code": 21010,
"message": "INVALID_REQUEST_ID",
"detail": "존재하지 않거나 이미 처리된 요청 ID입니다.",
"requestId": "req_invalid"
}
400 Bad Request - 데이터 검증 실패
예시: 수집된 데이터 유효성 검증 실패 (DATA_VALIDATION_FAILED - 21011)
{
"code": 21011,
"message": "DATA_VALIDATION_FAILED",
"detail": "수집된 데이터의 유효성 검증에 실패했습니다.",
"validationResult": {
"isValid": false,
"validRecordCount": 20,
"invalidRecordCount": 4,
"errors": [
{
"recordIndex": 5,
"field": "value",
"message": "심박수 값이 유효 범위(30-250)를 벗어났습니다.",
"code": "OUT_OF_RANGE"
}
]
}
}
409 Conflict - 중복 제출
예시: 이미 처리된 요청에 대한 중복 응답 (DUPLICATE_RESPONSE - 21012)
{
"code": 21012,
"message": "DUPLICATE_RESPONSE",
"detail": "이미 처리된 요청에 대한 중복 응답입니다.",
"requestId": "B437BCAC-CFFF-468C-9A30-5786F74BDB87"
}
설명
- 이 API는 수집된 건강 데이터를 서버에 제출하는 핵심 기능입니다.
- 배치 처리: 여러 요청에 대한 응답을 한 번의 API 호출로 제출할 수 있습니다.
- 데이터 형식:
- 모든 시간 데이터는 ISO8601 형식 + TimeZone으로 전송 (예:
2025-12-01T10:00:00.000+09:00) extractedAt: 앱에서 실제로 데이터를 추출한 시점을 정확히 기록
- 모든 시간 데이터는 ISO8601 형식 + TimeZone으로 전송 (예:
- 데이터 유형별 레코드 형식:
- stepCount (걸음 수):
sum,unit필드 사용 - heartRate (심박수):
average,unit필드 사용 - sleepAnalysis (수면 분석):
category필드 사용 (수면 단계)
- stepCount (걸음 수):
- 백그라운드 업로드:
- iOS: Background URLSession 활용
- Android: WorkManager 활용
- 업로드 실패 시 자동 재시도 구현 권장
- 오류 처리:
- 데이터 제출 실패 시에도 앱의 주요 플로우는 차단되지 않아야 합니다.
- 실패한 데이터는 로컬에 캐싱하고 이후 재시도해야 합니다.