본문으로 건너뛰기
버전: 0.68.0

수집 데이터 제출 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를 통한 백그라운드 업로드 지원

데이터 수집 플로우

  1. GET /v1/external-health/requests로 수집할 데이터 목록 조회
  2. HealthKit/Health Connect SDK로 각 요청에 해당하는 데이터 수집
  3. 수집된 데이터를 이 API로 제출

수집 데이터 제출

HealthKit/Health Connect에서 수집한 건강 데이터를 서버에 제출합니다.

  • HTTP Method: POST
  • Path: /v1/external-health/responses
  • 인증: 액세스 토큰 (accessToken) 필요

Headers

HeaderTypeDescriptionRequired
AuthorizationBearer {accessToken}사용자 인증을 통해 발급받은 액세스 토큰입니다.Yes
Content-Typeapplication/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"
}
]
}
]
}

요청 필드 명세

FieldData TypeRequiredDescription
responsesArray<Object>YES응답 배열 (최소한 빈 배열)
response.requestIdUUIDYESGET 요청으로 조회한 요청 ID
response.osStringYES플랫폼 (iOS 또는 Android)
response.typeString (Enum)YES데이터 유형: stepCount, heartRate, sleepAnalysis
response.startTimeStringYES수집 시작 시각 (ISO8601 + TimeZone)
response.endTimeStringYES수집 종료 시각 (ISO8601 + TimeZone)
response.intervalObjectNO집계 간격 설정
response.interval.typeString (Enum)Conditional간격 유형: year, month, day, hour, minute, second
response.interval.valueIntConditional집계 간격 값
response.extractedAtStringYES데이터 추출 시각 (ISO8601 + TimeZone)
response.recordsArray<Object>YES수집된 데이터 레코드 배열 (최소한 빈 배열)
record.startTimeStringYES레코드 시작 시각 (ISO8601 + TimeZone)
record.endTimeStringYES레코드 종료 시각 (ISO8601 + TimeZone)
record.sumIntNO합계 값 (stepCount 등 합계 데이터용)
record.averageDoubleNO평균 값 (heartRate 등 평균 데이터용)
record.categoryStringNO카테고리 (sleepAnalysis용: inBed, asleepCore, asleepDeep, asleepREM, awake)
record.unitStringNO단위 (count, count/min 등)

수면 카테고리 (sleepAnalysis category)

CategoryDescription
inBed침대에 누워있음
asleepUnspecified수면 (상세 단계 미지정)
asleepCore코어 수면 (얕은 수면)
asleepDeep깊은 수면
asleepREMREM 수면
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
}
]
}
필드타입설명
resultsarray처리 결과 배열
results[].requestIdstring요청 ID
results[].statusstring처리 상태 (COLLECTED, FAILED)
results[].processedRecordsnumber처리된 레코드 수
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: 앱에서 실제로 데이터를 추출한 시점을 정확히 기록
  • 데이터 유형별 레코드 형식:
    • stepCount (걸음 수): sum, unit 필드 사용
    • heartRate (심박수): average, unit 필드 사용
    • sleepAnalysis (수면 분석): category 필드 사용 (수면 단계)
  • 백그라운드 업로드:
    • iOS: Background URLSession 활용
    • Android: WorkManager 활용
    • 업로드 실패 시 자동 재시도 구현 권장
  • 오류 처리:
    • 데이터 제출 실패 시에도 앱의 주요 플로우는 차단되지 않아야 합니다.
    • 실패한 데이터는 로컬에 캐싱하고 이후 재시도해야 합니다.