사용량 보고 (활동, 스크린 타임, 트랜잭션)
이 가이드는 제품이 사용 데이터를 k-ID에 보내 신뢰할 수 있는 성인이 Family Connect에서 확인할 수 있도록 하는 방법을 다룹니다. 같은 수집 모델로 들어가는 입구가 세 개 있습니다.
/activity/push는 제품이 선언한 활동 유형에 대한 레코드를 받습니다./screentime/push는 완료된 스크린 타임 사용 구간을 받습니다./transaction/push는 완료된 구매를 받습니다.
셋 다 보고 전용이며 부모 설정이 필요 없습니다. 제품이 일어난 일을 보고하면 k-ID가 이를 집계하고, 신뢰할 수 있는 성인용 화면이 그 집계를 읽습니다. 어느 것도 실시간 제어를 실행하지 않습니다. 완전한 스크린 타임 기능, 즉 부모가 설정하는 한도, 조용한 시간, 휴식 알림, 세션 상태 머신과 그것이 발생시키는 이벤트는 스크린 타임 가이드를 참조하십시오. 트랜잭션 가시성, 구매 승인, 결제 기반 확인은 트랜잭션 가이드를 참조하십시오.
이것들이 무엇이고 그 경계가 어디인지는 활동과 트랜잭션을 참조하십시오.
시작하기 전에
세 엔드포인트 모두 보낸 내용을 k-ID 세션에 귀속시키므로, 연령 게이트에서 받은 sessionId가 필요합니다. 세션을 참조하십시오. 호출은 서버 간 통신이며 인증에 설명된 대로 API 키로 인증합니다.
세션 외에 각 경로에는 전제 조건이 하나씩 있습니다.
- 활동에는 선언된 활동 유형이 필요합니다. 모든 레코드는 제품이 Compliance Studio에서 선언한 유형을 지정하며, 유형은 API 키가 사용하는 환경으로 푸시하기 전까지 로컬 상태입니다. 활동 유형과 테스트 및 퍼블리싱을 참조하십시오.
- 스크린 타임에는 제품에 스크린 타임 기능이 활성화되어 있어야 합니다. 활성화되어 있지 않으면
/screentime/push는FEATURE_DISABLED를 반환합니다. - 트랜잭션에는 제품에 트랜잭션 기능이 활성화되어 있어야 합니다. 활성화되어 있지 않으면
/transaction/push는FEATURE_DISABLED를 반환합니다.
세 가지 수집 엔드포인트
세 엔드포인트는 하나의 모델을 공유하며, 한 항목이 담는 내용만 다릅니다.
/activity/push | /screentime/push | /transaction/push | |
|---|---|---|---|
| 받는 것 | 선언된 활동 유형에 대한 레코드 | 완료된 스크린 타임 사용 구간 | 완료된 구매 |
| 배치 필드, 최대 | records, 최대 1000 | events, 최대 100 | events, 최대 100 |
| 각 항목이 담는 것 | id, type, value, timestamp, 선택적 attributes | id, durationSeconds, timestamp | id, title, amount, currency, timestamp, status, 선택적 description과 url |
| 전제 조건 | 선언·푸시된 활동 유형 | 스크린 타임 기능 활성화 | 트랜잭션 기능 활성화 |
| 실시간 이벤트 | 없음 | 없음(라이브 경로는 스크린 타임 가이드에 있습니다) | 없음(라이브 경로는 트랜잭션 가이드에 있습니다) |
공통점은 수집 모델 전체입니다. 각 항목은 멱등성을 위한 클라이언트 생성 id와 최근 7일 이내의 timestamp를 담고, 한 배치는 정확히 한 세션만 다루며, 항목은 하나씩 검증되어 항목별로 수락 또는 거부되고, 수락된 내용은 무언가를 실행하는 것이 아니라 신뢰할 수 있는 성인이 보는 집계에 반영됩니다.
활동 레코드 푸시
한 세션에 대한 레코드 배치를 /activity/push로 보냅니다.
type은 활동 유형의 키로, 유형을 만들 때 k-ID가 생성하는 식별자(UUID)입니다. 직접 만들지 말고 Compliance Studio의 활동 유형 구성에서 복사하십시오.
timestamp는 활동이 실제로 발생한 시각으로 바꾸십시오. 위의 값은 설명용입니다. 7일보다 오래된 타임스탬프는 거부되므로 예시를 그대로 복사하면 오래된 값이 됩니다.
요청 예시
POST /api/v1/activity/push
Content-Type: application/json
Authorization: Bearer your-api-key
{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"records": [
{
"id": "7c3e0c6e-9c2a-4f1e-bd2b-1f9a3c6d8e10",
"type": "2f9a1c84-6b3e-4d52-9f08-1a7c3e5d9b20",
"value": { "seconds": 1800 },
"timestamp": "2026-08-19T14:32:05Z",
"attributes": { "mode": "co_op" }
}
]
}
응답 예시
{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"accepted": 1,
"rejected": 1,
"records": [
{ "id": "7c3e0c6e-9c2a-4f1e-bd2b-1f9a3c6d8e10", "status": "accepted" },
{ "id": "a4f2b8d1-3e7c-4a90-8b6f-2c5d9e0a1b34", "status": "rejected", "reason": "timestamp_too_old" }
]
}
200 응답이 모든 레코드가 저장되었다는 뜻은 아닙니다. HTTP 상태만 보지 말고 rejected와 레코드별 status를 확인하십시오.
값 형식
각 활동 유형은 메트릭 유형을 선언하며, 레코드의 value 객체는 해당 메트릭 유형의 필드만 담아야 합니다. seconds와 count처럼 필드가 섞인 레코드는 각각이 유효하더라도 거부됩니다.
| 메트릭 유형 | value | 비고 |
|---|---|---|
duration | { "seconds": 1800 } | 정수 초, 0 이상 |
currency | { "amount": 999, "currency": "USD" } | amount는 최소 단위이며 0 이상입니다. currency는 ISO 4217 코드입니다 |
count | { "count": 7 } | 0 이상 |
gauge | { "number": 0.85 } | 임의의 배정밀도 실수 |
boolean | { "bool": true } |
통화 코드는 ISO 4217과 대조되므로 GEMS 같은 게임 내 통화 이름은 거부됩니다. 가상 통화 구매는 카운트로 보고하거나, 부모에게 보여주려는 것이 실제 금액이라면 실제 통화 금액으로 보고하십시오.
속성
attributes는 선택적 키/값 메타데이터로, 제품의 어떤 모드에서 시간을 보냈는지처럼 나누어 보여주고 싶은 차원에 사용합니다. 각 유형은 허용하는 속성 키를 선언하며 규칙은 엄격합니다.
- 한 레코드는 최대 20개의 속성을 담을 수 있습니다.
- 키는 최대 64자, 값은 최대 256자입니다.
- 유형이 선언하지 않은 키는 거부됩니다. 속성 키를 하나도 선언하지 않은 유형은 어떤 속성도 받지 않습니다.
- 이메일 주소, 전화번호, 결제 카드 번호로 보이는 값은 거부됩니다.
개인정보 대신 자체 시스템에서 해석할 수 있는 불투명한 식별자를 보내십시오.
활동 거부 사유
| 사유 | 내용 |
|---|---|
invalid_id | id가 UUID가 아닙니다 |
unknown_type | API 키의 모드가 제공하는 구성에 해당 type이 이 제품용으로 선언되어 있지 않습니다 |
invalid_value | value가 유형의 메트릭 유형과 맞지 않거나, 음수이거나, 둘 이상의 메트릭 유형 필드를 담고 있습니다 |
timestamp_in_future | timestamp가 현재보다 미래입니다 |
timestamp_too_old | timestamp가 7일보다 오래되었습니다 |
attributes_too_many | 속성이 20개를 넘습니다 |
attribute_key_too_long | 속성 키가 64자를 넘습니다 |
attribute_value_too_long | 속성 값이 256자를 넘습니다 |
attribute_key_not_allowed | 속성 키가 이 유형에 선언되어 있지 않습니다 |
attribute_value_forbidden_content | 속성 값이 이메일 주소, 전화번호 또는 결제 카드 번호로 보입니다 |
type_metric_type_unsupported | 유형의 메트릭 유형을 이 API 버전이 처리하지 않습니다 |
transient_error | 레코드는 유효했지만 저장하지 못했습니다. 다시 시도하십시오 |
transient_error를 제외한 모든 사유는 제품이 고쳐야 할 레코드를 가리킵니다. 바꾸지 않은 레코드를 다시 보내면 같은 사유로 거부되므로, 재시도 큐가 아니라 로그로 보내십시오.
스크린 타임 사용량 푸시
/screentime/push는 완료된 사용 구간을 사후에 보고합니다. 구간을 버퍼링하는 서버나 종료 시 합계를 보고하는 게임 엔진을 위한 경로입니다. 수락된 각 구간은 durationSeconds를 아이의 하루 스크린 타임 합계에 더하고 부모의 Family Connect 차트에 표시됩니다. 활동 레코드와 같은 형태의 수집입니다.
이는 보고 전용입니다. 휴식 알림, 한도 경고, 한도 도달 이벤트는 발생하지 않습니다. 이는 세션 시작 시점에 예약되는 것이며 사후에 재구성할 수 없기 때문입니다. 제품이 이러한 실시간 신호나 그 뒤의 부모 설정 한도, 조용한 시간, 오버라이드가 필요하다면, 그것은 이 엔드포인트가 아니라 스크린 타임 가이드에 있는 라이브 경로입니다.
요청 예시
POST /api/v1/screentime/push
Content-Type: application/json
Authorization: Bearer your-api-key
{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"events": [
{
"id": "9d4e2f81-7a3b-4c56-8e90-1f2a3b4c5d6e",
"durationSeconds": 1800,
"timestamp": "2026-06-24T09:00:00Z"
}
]
}
응답 예시
{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"accepted": 1,
"rejected": 1,
"events": [
{ "id": "9d4e2f81-7a3b-4c56-8e90-1f2a3b4c5d6e", "status": "accepted" },
{ "id": "c1b2a3d4-5e6f-4708-9a1b-2c3d4e5f6071", "status": "rejected", "reason": "invalid_input" }
]
}
활동과 마찬가지로 HTTP 상태만 보지 말고 rejected와 이벤트별 status를 확인하십시오.
구간 규칙
각 이벤트는 완료된 구간 하나로, 멱등성을 위한 고유 id, 사용 구간이 시작된 timestamp(UTC), 그리고 durationSeconds를 담습니다.
durationSeconds는 1초에서 24시간까지입니다. 더 긴 구간은 여러 구간으로 나누십시오.- 구간은 이미 완료되어 있어야 합니다.
timestamp + durationSeconds가 미래일 수 없습니다. timestamp는 최근 7일 이내여야 합니다.- 부모가 설정한 시간대에서 자정을 넘는 구간은 두 날짜로 나뉘어, 각 날의 합계에 그날 실제로 발생한 사용이 반영됩니다.
- 한 번의 호출에 최대 100개 구간입니다.
스크린 타임 거부 사유
| 사유 | 내용 |
|---|---|
invalid_input | 구간이 검증 규칙을 위반했습니다: 잘못된 id, 1초에서 24시간 범위를 벗어난 durationSeconds, 범위를 벗어난 timestamp 등. 바꾸지 않고 다시 시도하면 또 실패합니다 |
internal_error | 일시적인 저장 실패입니다. 다시 시도해도 안전합니다 |
트랜잭션 푸시
한 세션에 대한 완료된 구매 배치를 /transaction/push로 보냅니다. 다른 두 푸시와 마찬가지로 보고 전용입니다. 보고한 각 구매는 기록되어 Family Connect에서 연결된 부모에게 표시되며, 구매를 보고한다고 해서 결제가 차단되는 일은 없습니다.
제품에 트랜잭션 기능이 활성화되어 있어야 하며, 활성화되어 있지 않으면 /transaction/push는 FEATURE_DISABLED를 반환합니다. 수락된 각 구매는 통화 메트릭 이벤트로서 활동에도 반영되므로, 보고된 구매는 신뢰할 수 있는 성인이 읽는 같은 집계로 흘러갑니다.
각 항목은 구매 내용을 담습니다. title, 통화의 최소 단위로 표시한 amount, currency, 구매가 완료된 timestamp, 그리고 successful 또는 failed인 status입니다. 전체 필드 집합, 요청 및 응답 형식, 이벤트별 상태는 트랜잭션 가이드의 구매 보고를 참조하십시오.
배치 단위 오류
일부 조건에서는 호출 전체가 실패하며, 응답에는 항목별 결과 대신 error 코드가 담깁니다. 오류 처리를 참조하십시오.
| 오류 | 원인 |
|---|---|
NOT_FOUND | 이 제품에 해당 sessionId의 세션이 없습니다 |
INVALID_INPUT | 세션이 취소되었거나, 배치가 비어 있거나 크기 한도를 넘거나, 본문을 해석할 수 없습니다 |
FEATURE_DISABLED | 스크린 타임 기능이 활성화되지 않은 상태에서 /screentime/push가 호출되었거나, 트랜잭션 기능 없이 /transaction/push가 호출되었습니다 |
재시도와 멱등성
세 엔드포인트 모두 각 항목에 클라이언트 생성 id를 받습니다. /activity/push에서는 레코드의 id, /screentime/push에서는 구간의 id, /transaction/push에서는 구매의 id이며, 이 id가 재시도를 안전하게 만듭니다. 이미 수락된 id의 항목을 다시 푸시하면 수락으로 다시 집계될 뿐 새로 저장되지 않으므로, 중간에 타임아웃된 배치는 그대로 다시 보낼 수 있습니다.
이 성질은 id가 안정적일 때만 성립합니다. ID는 사용이 발생한 시점에 한 번만 생성하고, 보낼 항목과 함께 저장하십시오. 전송 시점에 생성한 id는 재시도마다 중복을 만듭니다.
배치 전략 선택
7일의 타임스탬프 범위는 상한이지 목표가 아닙니다. 더 작고 잦은 배치가 좋은 이유가 두 가지 있습니다.
- 신뢰할 수 있는 성인은 도착한 사용량만 볼 수 있으므로, 하루에 한 번 푸시하는 제품은 어제의 모습만 보여줍니다.
- 7일보다 오래된 항목은 거부됩니다. 활동의 경우 그 기간을 넘는 백필은 비동기 파일 업로드 경로를 사용해야 하며, 스크린 타임 구간에는 그런 경로가 없으므로 기간 내에 보고하십시오.
각 엔드포인트의 크기 한도(1000개 레코드, 100개 구간 또는 100개 구매) 안에서 세션 단위로 몇 분 분량의 플레이를 배치로 묶으면 두 문제를 모두 피할 수 있습니다.
통합 검증
/activity/send-test-digest는 연결된 성인이 받는 요약 이메일을 해당 세션의 최근 7일 실제 데이터로 생성해 보냅니다. 요약에는 세션이 쌓은 스크린 타임, 활동, 구매가 반영되므로, 푸시한 내용이 부모가 읽을 수 있는 형태가 되는지 확인하는 가장 빠른 방법입니다.
요청 예시
POST /api/v1/activity/send-test-digest
Content-Type: application/json
Authorization: Bearer your-api-key
{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672"
}
응답 예시
{
"sent": true
}
이 엔드포인트에 대해 알아야 할 네 가지가 있습니다.
- 테스트 모드 전용입니다. 라이브 API 키로 호출하면
NOT_FOUND오류가 반환됩니다. 실제로 연결된 성인에게 이메일이 발송되는 일은 없습니다. - 세션에 연결된 성인이 있어야 합니다.
hasApproverEmail이 false인 세션은INVALID_INPUT을 반환합니다. sent: false는 성공입니다. 최근 한 주에 요약할 내용이 없어 이메일이 나가지 않았다는 뜻입니다.- 최근 7일이 대상입니다. 기간은 오늘까지이므로, 이 세션에 푸시한 데이터도 포함됩니다.
선택적으로 locale(예: ja)을 전달하면 해당 언어로 번역된 이메일을 미리 볼 수 있습니다.
통합 체크리스트
- Compliance Studio에서 활동 유형을 선언하고 API 키가 사용하는 환경으로 구성을 푸시했으며, 스크린 타임을 푸시한다면 스크린 타임 기능을, 트랜잭션을 푸시한다면 트랜잭션 기능을 활성화했습니다.
- 항목 ID를 사용이 발생하는 곳에서 생성하고, 재시도 시 재사용할 수 있도록 항목과 함께 저장합니다.
rejected와 항목별reason을 읽고, 거부율 변화에 알림을 보내며, 일시적 사유(활동은transient_error, 스크린 타임과 트랜잭션은internal_error)만 재시도하는 응답 처리를 구현했습니다.- 타임스탬프는 UTC이며, 푸시 주기는 7일 범위보다 충분히 짧습니다.
- 속성은 선언된 키로 제한되며 개인정보를 담지 않습니다.
- 테스트 요약을 보내고 엔드 투 엔드로 확인했습니다.