endpoints
관련 문서
- Agent Data 도메인 엔드포인트: /domains/common/core-domains/agent-data/endpoints
Learning API 엔드포인트
목차
- 관련 문서
- 1. 엔드포인트 접근 권한 매트릭스
- 2. 콘텐츠 관리 API
- 3. 학습 진행 API
- 4. 하이라이트 API
- 5. 사용자 입력 API
- 6. 설정 API
- 7. 미디어 API
- 8. 퀴즈 API
- 9. 일일 콘텐츠 API
- 10. BFF (Backend for Frontend) API
- 11. 모바일 캐싱 API
- 12. 학습 이력 API
- 13. 학습 통계 API
- 14. 오류 코드
- 15. 변경 이력
1. 엔드포인트 접근 권한 매트릭스
| 엔드포인트 | System Admin | Content Manager | Support | Regular User |
|---|---|---|---|---|
GET /v1/learning/bundles/{bundleId} | ✓ | ✓ | ✓ | ✓ |
| GET /v1/learning/bundles/latest | ✓ | ✓ | ✓ | ✓ |
| GET /v1/learning/me/lesson-states | ✓ | ✓ | ✓ | 자신만 |
GET /v1/learning/lessons/{lessonId} | ✓ | ✓ | ✓ | 자신만 |
GET /v1/learning/lessons/{lessonId}/unlock-status | ✓ | ✓ | ✓ | 자신만 |
| GET /v1/learning/progress | ✓ | ✓ | ✓ | 자신만 |
POST /v1/learning/lessons/{lessonId}/complete | ✓ | ✓ | ✓ | 자신만 |
| POST /v1/learning/highlights | ✓ | ✓ | ✓ | 자신만 |
| GET /v1/learning/highlights | ✓ | ✓ | ✓ | 자신만 |
DELETE /v1/learning/highlights/{highlightId} | ✓ | ✓ | ✓ | 자신만 |
| POST /v1/learning/inputs | ✓ | ✓ | ✓ | 자신만 |
GET /v1/learning/lessons/{lessonId}/inputs | ✓ | ✓ | ✓ | 자신만 |
PUT /v1/learning/inputs/{inputId} | ✓ | ✓ | ✓ | 자신만 |
| GET /v1/learning/settings/text | ✓ | ✓ | ✓ | 자신만 |
| PUT /v1/learning/settings/text | ✓ | ✓ | ✓ | 자신만 |
| PUT /v1/learning/settings/tts | ✓ | ✓ | ✓ | 자신만 |
GET /v1/learning/media/{mediaId}/image | ✓ | ✓ | ✓ | 자신만 |
GET /v1/learning/media/{mediaId}/video | ✓ | ✓ | ✓ | 자신만 |
GET /v1/learning/media/{mediaId}/audio | ✓ | ✓ | ✓ | 자신만 |
| GET /v1/learning/daily/content | ✓ | ✓ | ✓ | 자신만 |
POST /v1/learning/quiz/{quizId}/answers | ✓ | ✓ | ✓ | 자신만 |
| GET /v1/learning/quiz/history | ✓ | ✓ | ✓ | 자신만 |
| GET /v1/learning/bundles/active | ✓ | ✓ | ✓ | ✓ |
POST /v1/learning/bundles/{bundleId}/download-url | ✓ | ✓ | ✓ | ✓ |
| POST /v1/learning/history | ✓ | ✓ | ✓ | 자신만 |
| POST /v1/learning/history/batch | ✓ | ✓ | ✓ | 자신만 |
GET /v1/learning/history/lesson/{lessonId} | ✓ | ✓ | ✓ | 자신만 |
| GET /v1/learning/history/me | ✓ | ✓ | ✓ | 자신만 |
| GET /v1/learning/metrics/me | ✓ | ✓ | ✓ | 자신만 |
GET /v1/learning/metrics/lesson/{lessonId} | ✓ | ✓ | ✓ | 자신만 |
참고:
- ✓: 접근 가능
- ✘: 접근 불가
- (범위 내): 할당된 조직/팀 범위 내에서만 접근 가능
- (자신만): 자신의 데이터에만 접근 가능
2. 콘텐츠 관리 API
📌 기술 구현 문서: TBD
콘텐츠 관리 API는 수면탐구 레슨 콘텐츠의 번들 형태 제공과 사용자별 상태 관리를 담당합니다.
주요 특징:
- 번들 기반 콘텐츠 제공: 세션, 레슨, 미디어 정보를 하나의 번들로 제공하여 효율적인 캐싱 가능
- 콘텐츠와 사용자 상태 분리: 번들(전역 콘텐츠)과 사용자별 진행 상태를 별도 API로 관리
- 다국어 지원: 번들 조회 시 언어를 지정하여 해당 언어로 번역된 콘텐츠 제공
- 캐싱 최적화: 클라이언트는 번들 ID 기반으로 캐싱하고 ID 변경 시에만 업데이트
2.1 보안 콘텐츠 요청 (통합)
📌 관련 문서: requirements.md LRN-FR-BE-060~065
- HTTP 메서드: POST
- 경로: /v1/learning/lessons/content
- Headers:
- Content-Type: application/json (고정 - request body가 JSON)
- Authorization: Bearer
{{appToken}}(필수)
- Body:
{
"userId": "aaaa-bbbb-cccc-dddd",
"lessonId": "f3e0a0ae-51ba-422f-917d-a9b16b4dc48e",
"content": "wcs://de-de/1.0.0/lesson_01.json"
}
설명: App Token 기반 보안 검증을 통해 모든 유형의 파일(JSON, MP3, 이미지 등)을 직접 스트리밍하는 통합 endpoint입니다. 기존의 직접 GCS URL 노출 방식을 대체하여 보안을 강화하되, 모바일 앱에서의 파일 처리 방식은 기존과 동일하게 유지합니다.
Request Body 필드:
userId: 사용자 고유 식별자 (UUID 형식)content: 요청할 콘텐츠의 WCS(Welt Corp Storage) 경로- 형식:
wcs://{language}/{version}/{filename} - 예시:
wcs://de-de/1.0.0/lesson_01.json
- 형식:
성공 응답 (200 OK): 원본 파일을 Content-Type에 맞게 스트리밍:
JSON 파일 요청 시:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 12345
Content-Disposition: inline; filename="lesson_01.json"
{
"title": "Was dich in den nächsten 3 Monaten erwartet",
"sections": [...],
"media": [...]
}
MP3 파일 요청 시:
HTTP/1.1 200 OK
Content-Type: audio/mpeg
Content-Length: 2567890
Content-Disposition: inline; filename="audio_01.mp3"
[바이너리 MP3 데이터 스트리밍]
이미지 파일 요청 시:
HTTP/1.1 200 OK
Content-Type: image/jpeg
Content-Length: 567890
Content-Disposition: inline; filename="image_01.jpg"
[바이너리 이미지 데이터 스트리밍]
오류 응답:
- 403 Forbidden (레슨 미해금):
{
"success": false,
"error": "LESSON_LOCKED",
"message": "이 레슨은 아직 해금되지 않았습니다.",
"requiredConditions": ["이전 레슨 완료 필요"]
}
- 401 Unauthorized (App Token 무효):
{
"success": false,
"error": "INVALID_APP_TOKEN",
"message": "유효하지 않은 앱 토큰입니다."
}
- 404 Not Found (콘텐츠 없음):
{
"success": false,
"error": "CONTENT_NOT_FOUND",
"message": "요청한 콘텐츠를 찾을 수 없습니다."
}
서버 처리 프로세스:
- Authorization Bearer 헤더에서 App Token 검증 (Auth 도메인)
- userId와 App Token의 연결 확인
- lessonId를 통해 레슨 해금 상태 확인 (Learning 도메인)
- LOCKED (미해금): 접근 거부 - 403 Forbidden
- UNLOCKED (열람 가능): 접근 허용
- IN_PROGRESS (열람 중): 접근 허용
- COMPLETED (완료): 접근 허용
- UNAVAILABLE (열람 불가): 접근 거부 - 403 Forbidden
- 콘텐츠 접근 권한 검증
- WCS 경로에서 파일 확장자 추출 (.json, .mp3, .jpg 등)
- 파일 확장자에 따라 Response Content-Type 자동 결정:
.json→application/json.mp3→audio/mpeg.jpg→image/jpeg
- GCS에서 원본 파일 스트리밍
모바일 앱 호환성:
- 응답은 기존 GCS 직접 다운로드와 동일한 원본 파일 구조
- Content-Type 헤더로 파일 타입 자동 감지 가능
- 기존 파일 처리 로직 수정 없이 사용 가능
모바일 호환성 (iOS/Android):
iOS (Swift) 예시:
// URLRequest 생성
var request = URLRequest(url: URL(string: "/v1/learning/lessons/content")!)
request.httpMethod = "POST"
request.setValue("Bearer \(appToken)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body = [
"userId": userId,
"lessonId": "f3e0a0ae-51ba-422f-917d-a9b16b4dc48e",
"content": "wcs://de-de/1.0.0/lesson_01.json"
]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
// 파일 다운로드 (기존과 동일한 방식)
let (data, response) = try await URLSession.shared.data(for: request)
// Content-Type에 따라 처리
if let contentType = response.mimeType {
if contentType.contains("application/json") {
let lessonData = try JSONSerialization.jsonObject(with: data) // JSON 파일
} else if contentType.contains("audio/") {
// MP3 파일 - 기존 오디오 처리 로직 그대로 사용
let audioData = data
}
}
Android (Kotlin) 예시:
// OkHttp 요청 (기존과 동일한 패턴)
val requestBody = JSONObject().apply {
put("userId", userId)
put("lessonId", "f3e0a0ae-51ba-422f-917d-a9b16b4dc48e")
put("content", "wcs://de-de/1.0.0/lesson_01.json")
}.toString().toRequestBody("application/json".toMediaType())
val request = Request.Builder()
.url("/v1/learning/lessons/content")
.post(requestBody)
.addHeader("Authorization", "Bearer $appToken")
.build()
// 파일 다운로드 (기존과 동일)
val response = client.newCall(request).execute()
// Content-Type에 따라 처리
when {
response.header("Content-Type")?.contains("application/json") == true -> {
val lessonData = JSONObject(response.body?.string()) // JSON 파일
}
response.header("Content-Type")?.contains("audio/") == true -> {
val audioBytes = response.body?.bytes() // MP3 파일
}
}
2.2 학습 콘텐츠 번들 조회
📌 관련 문서: TBD
- HTTP 메서드: GET
- 경로: /v1/learning/bundles/
{bundleId} - Headers:
- Authorization:
Bearer {accessToken} - Accept-Language: de-DE, ko-KR, en-US
- Authorization:
- Path Parameters:
- bundleId: 번들 ID (필수)
- Query Parameters:
- language: string (선택, 특정 언어로 응답 강제 시 사용. Accept-Language보다 우선). 지정 시 번역 가능한 필드들(예:
title,description)이 해당 언어 값으로 직접 포함되고,translations배열은 생략됩니다. 요청한 언어의 번역이 없으면 해당 필드는null또는 생략될 수 있습니다. - includeLessons: 레슨 포함 여부 (선택, 기본값: true)
- includeQuizzes: 퀴즈 포함 여부 (선택, 기본값: true)
- includeMedia: 미디어 정보 포함 여부 (선택, 기본값: false)
- language: string (선택, 특정 언어로 응답 강제 시 사용. Accept-Language보다 우선). 지정 시 번역 가능한 필드들(예:
응답 구조 설명
번들 응답은 다음 구조를 포함합니다:
bundleId: 번들 IDversion: 번들 버전language: 응답 언어sessions: 세션 및 레슨 정보 배열quizzes: 퀴즈 정보 배열 (includeQuizzes=true인 경우)
세션 객체는 다음 필드를 포함합니다:
id: 세션 IDtitle: 세션 제목 (요청 언어로 번역됨)description: 세션 설명 (요청 언어로 번역됨)orderIndex: 세션 순서lessonCount: 세션에 속한 레슨 수lessons: 세션에 속한 레슨 목록 (includeLessons=true인 경우)
레슨 객체는 다음 필드를 포함합니다:
id: 레슨 IDtitle: 레슨 제목 (요청 언어로 번역됨)content: 레슨 내용 (요청 언어로 번역됨)orderIndex: 세션 내 레슨 순서label: 45일 프로그램에서 추천되는 일차 (1일차=1, 2일차=2, ...)media: 미디어 정보 (includeMedia=true인 경우)
퀴즈 객체는 다음 필드를 포함합니다:
id: 퀴즈 IDversion: 퀴즈 버전quizType: 퀴즈 타입 (TRUE_FALSE, SINGLE_CHOICE)question: 퀴즈 질문 (요청 언어로 번역됨)options: 선택지 목록 (SINGLE_CHOICE인 경우)correctAnswer: 정답explanation: 해설 (요청 언어로 번역됨)relatedLessonId: 관련 레슨 ID
응답 예시
{
"bundleId": "bundle_learning_2024_v1",
"language": "ko-KR",
"sessions": [
{
"id": "session_101",
"name": "레슨 01",
"title": "수면의 중요성 이해하기",
"orderIndex": 0,
"lessonCount": 5,
"lessons": [
{
"id": "lesson_101",
"title": "수면의 중요성 이해하기",
"content": "content_json_url_101",
"orderIndex": 0,
"label": 1
},
{
"id": "lesson_102",
"title": "나의 수면 패턴 알아보기",
"content": "content_json_url_102",
"orderIndex": 1,
"label": 2
},
{
"id": "lesson_103",
"title": "수면 문제 자가 진단",
"content": "수면 문제를 스스로 진단해보고 개선점을 찾아보겠습니다...",
"orderIndex": 2,
"label": 3
}
]
},
{
"id": "session_201",
"name": "레슨 02",
"title": "수면 기초",
"orderIndex": 0,
"lessonCount": 5,
"lessons": [
{
"id": "lesson_201",
"title": "건강한 수면을 위한 첫걸음",
"content": "content_json_url_01",
"orderIndex": 0,
"label": 1
},
{
"id": "lesson_202",
"title": "수면의 단계와 구조",
"content": "content_json_url_02",
"orderIndex": 1,
"label": 2
},
{
"id": "lesson_203",
"title": "수면 장애의 이해",
"content": "수면 장애는 다양한 형태로 나타날 수 있습니다...",
"orderIndex": 3,
"label": 3
}
]
}
],
"quizzes": [
{
"id": "quiz_001",
"type": "SINGLE_CHOICE",
"question": "건강한 수면 주기에서 렘수면이 차지하는 비율은 얼마인가요?",
"options": [
{
"id": "option_1",
"value": "1",
"orderIndex": 0,
"text": "약 5-10%"
},
{
"id": "option_2",
"value": "2",
"orderIndex": 1,
"text": "약 20-25%"
},
{
"id": "option_3",
"value": "3",
"orderIndex": 2,
"text": "약 40-50%"
},
{
"id": "option_4",
"value": "4",
"orderIndex": 3,
"text": "약 70-80%"
}
],
"correctAnswer": "2",
"explanation": "건강한 성인의 수면 주기에서 렘수면은 총 수면 시간의 약 20-25%를 차지합니다.",
"relatedLessonId": "lesson_124"
},
{
"id": "quiz_002",
"type": "TRUE_FALSE",
"question": "수면 부족은 면역 시스템을 약화시킬 수 있습니다.",
"options": [
{
"id": "option_true",
"value": "TRUE",
"orderIndex": 0,
"text": "참"
},
{
"id": "option_false",
"value": "FALSE",
"orderIndex": 1,
"text": "거짓"
}
],
"correctAnswer": "TRUE",
"explanation": "연구에 따르면 충분하지 않은 수면은 면역 시스템의 기능을 저하시켜 감염에 대한 저항력을 약화시킵니다.",
"relatedLessonId": "lesson_123"
}
],
"metadata": {
"contentCount": {
"sessions": 9,
"lessons": 46,
"quizzes": 25
// "media": 120
},
"lastModified": 1715600400000
}
}
- 오류 응답 (404 Not Found)
{
"code": 1010,
"message": "BUNDLE_NOT_FOUND",
"detail": "요청한 번들을 찾을 수 없습니다."
}
2.2 최신 학습 콘텐츠 번들 조회
📌 관련 문서: TBD
- HTTP 메서드: GET
- 경로: /v1/learning/bundles/latest
- Headers:
- Authorization:
Bearer {{appToken}} - Accept-Language: de-DE, ko-KR, en-US (필수)
- Authorization:
응답 구조 설명
번들 응답은 다음 구조를 포함합니다:
bundleId: 번들 IDversion: 번들 버전language: 응답 언어sessions: 세션 및 레슨 정보 배열quizzes: 퀴즈 정보 배열
세션 객체는 다음 필드를 포함합니다:
id: 세션 IDtitle: 세션 제목 (요청 언어로 번역됨)description: 세션 설명 (요청 언어로 번역됨)orderIndex: 세션 순서lessons: 세션에 속한 레슨 목록
레슨 객체는 다음 필드를 포함합니다:
id: 레슨 IDtitle: 레슨 제목 (요청 언어로 번역됨)jsonUrl: 레슨 콘텐츠 JSON URL (DB의 lesson schema의 content 필드에 해당)orderIndex: 세션 내 레슨 순서label: 프로그램에서 추천되는 일차 (1일차=1, 2일차=2, ...)
퀴즈 객체는 다음 필드를 포함합니다:
id: 퀴즈 IDtype: 퀴즈 타입 (TRUE_FALSE, SINGLE_CHOICE)question: 퀴즈 질문 (요청 언어로 번역됨)options: 선택지 목록 (각 옵션은 id, value, isCorrectValue, orderIndex, text 포함)explanation: 해설 (요청 언어로 번역됨)relatedLessonId: 관련 레슨 ID
응답 예시
{
"bundleId": "bundle_learning_2024_v1",
"language": "ko-KR",
"sessions": [
{
"id": "session_101",
"name": "레슨 01",
"title": "수면 입문",
"orderIndex": 0,
"lessons": [
{
"id": "lesson_101",
"title": "수면의 중요성 이해하기",
"jsonUrl": "content_json_url_101",
"orderIndex": 0,
"label": 1
},
{
"id": "lesson_102",
"title": "나의 수면 패턴 알아보기",
"jsonUrl": "content_json_url_102",
"orderIndex": 1,
"label": 2
},
{
"id": "lesson_103",
"title": "수면 문제 자가 진단",
"jsonUrl": "수면 문제를 스스로 진단해보고 개선점을 찾아보겠습니다...",
"orderIndex": 2,
"label": 3
}
]
},
{
"id": "session_201",
"name": "레슨 02",
"title": "수면 기초",
"orderIndex": 0,
"lessons": [
{
"id": "lesson_201",
"title": "건강한 수면을 위한 첫걸음",
"jsonUrl": "content_json_url_01",
"orderIndex": 0,
"label": 1
},
{
"id": "lesson_202",
"title": "수면의 단계와 구조",
"jsonUrl": "content_json_url_02",
"orderIndex": 1,
"label": 2
},
{
"id": "lesson_203",
"title": "수면 장애의 이해",
"jsonUrl": "수면 장애는 다양한 형태로 나타날 수 있습니다...",
"orderIndex": 3,
"label": 3
}
]
}
],
"quizzes": [
{
"id": "quiz_001",
"type": "SINGLE_CHOICE",
"question": "건강한 수면 주기에서 렘수면이 차지하는 비율은 얼마인가요?",
"options": [
{
"id": "option_1",
"value": "1",
"isCorrectValue": true,
"orderIndex": 0,
"text": "약 5-10%"
},
{
"id": "option_2",
"value": "2",
"isCorrectValue": false,
"orderIndex": 1,
"text": "약 20-25%"
},
{
"id": "option_3",
"value": "3",
"isCorrectValue": false,
"orderIndex": 2,
"text": "약 40-50%"
},
{
"id": "option_4",
"value": "4",
"isCorrectValue": false,
"orderIndex": 3,
"text": "약 70-80%"
}
],
"explanation": "건강한 성인의 수면 주기에서 렘수면은 총 수면 시간의 약 20-25%를 차지합니다.",
"relatedLessonId": "lesson_124"
},
{
"id": "quiz_002",
"type": "TRUE_FALSE",
"question": "수면 부족은 면역 시스템을 약화시킬 수 있습니다.",
"options": [
{
"id": "option_true",
"value": "TRUE",
"isCorrectValue": false,
"orderIndex": 0,
"text": "참"
},
{
"id": "option_false",
"value": "FALSE",
"isCorrectValue": true,
"orderIndex": 1,
"text": "거짓"
}
],
"explanation": "연구에 따르면 충분하지 않은 수면은 면역 시스템의 기능을 저하시켜 감염에 대한 저항력을 약화시킵니다.",
"relatedLessonId": "lesson_123"
}
]
}
- 오류 응답 (404 Not Found)
{
"code": 1010,
"message": "BUNDLE_NOT_FOUND",
"detail": "요청한 번들을 찾을 수 없습니다."
}
2.3 사용자별 레슨 상태 조회
📌 관련 문서: TBD
- HTTP 메서드: GET
- 경로: /v1/learning/me/lesson-states
- Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
- Query Parameters:
- N/A
- 설명: 현재 사용자의 레슨별 해금 상태와 완료 상태를 조회합니다. 이 API는 사용자별 진행 상황을 제공하며, 클라이언트는 이 정보를 세션/레슨 목록과 조합하여 UI를 구성합니다. 완료되지 않은 레슨이 있는 경우, 완료되지 않은 레슨 중 label 값이 가장 작은 레슨을 추천합니다.
응답 (Response)
- 성공 응답 (200 OK)
{
"userId": "user_123",
"bundleId": "latest_learning_bundle_uuid",
"states": [
{
"label": 1,
"status": "COMPLETED",
"unlockedAt": 1711929600000,
"completedAt": 1712048400000,
"lastAccessedAt": 1712048400000
},
{
"label": 2,
"status": "UNLOCKED",
"unlockedAt": 1712048400000,
"completedAt": null,
"lastAccessedAt": null
},
{
"label": 3,
"status": "LOCKED",
"unlockedAt": null,
"completedAt": null,
"lastAccessedAt": null
},
{
"label": 4,
"status": "LOCKED",
"unlockedAt": null,
"completedAt": null,
"lastAccessedAt": null,
"remainingDaysToUnlock": 78, // 46번 레슨의 경우 치료 종료일까지 차단
"blockedReason": "TREATMENT_END_LESSON"
}
]
}
2.4 레슨 메타데이터 조회
- HTTP 메서드: GET
- 경로: /learning/lessons/
{lessonId} - Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
- 설명: 레슨의 메타데이터만 반환하며, 실제 콘텐츠(이미지, 텍스트)는 앱에 내장되어 있습니다. 앱은 레슨 ID를 사용하여 내장된 콘텐츠를 식별하고 표시합니다.
응답 (Response)
- 성공 응답 (200 OK)
{
"id": "lesson_123",
"title": "건강한 수면을 위한 첫걸음",
"orderIndex": 1,
"mediaRefs": [
// 미디어 참조 정보 (실제 미디어 파일은 앱에 내장)
{
"id": "media_456",
"type": "IMAGE",
"refId": "img_sleep_cycle", // 앱 내 이미지 리소스 식별자
"metadata": {
"width": 1200,
"height": 800
}
}
],
"isCompleted": false,
"completedAt": null,
"createdAt": 1704067200000,
"updatedAt": 1704067200000
}
- 오류 응답 (403 Forbidden)
{
"code": 1001,
"message": "LESSON_NOT_UNLOCKED",
"detail": "아직 해금되지 않은 레슨입니다."
}
2.5 특정 레슨 상태 확인
- HTTP 메서드: GET
- 경로: /learning/lessons/
{lessonId}/status - Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
응답 (Response)
- 성공 응답 (200 OK)
{
"lessonId": "lesson_123",
"status": "UNLOCKED",
"unlockedAt": 1712016000000,
"lastAccessedAt": null,
"completedAt": null,
"nextUnlockAt": 1712102400000,
"blockedReason": null
}
- 열람 불가 상태 응답 (200 OK)
{
"lessonId": "lesson_125",
"status": "BLOCKED",
"unlockedAt": 1712188800000,
"lastAccessedAt": null,
"completedAt": null,
"nextUnlockAt": null,
"blockedReason": "PREVIOUS_LESSON_NOT_COMPLETED",
"prerequisiteLessonId": "lesson_124"
}
3. 학습 진행 API
📌 기술 구현 문서: TBD
학습 진행 API는 사용자의 수면탐구 학습 진행 상태를 관리합니다. 레슨 완료 처리, 전체 진행률 계산, 완료한 레슨 목록 조회 기능을 제공합니다.
3.1 진행률 조회
- HTTP 메서드: GET
- 경로: /learning/progress
- Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
응답 (Response)
- 성공 응답 (200 OK)
{
"completedLessons": 10,
"totalLessons": 42,
"progressPercentage": 23.81,
"lastCompletedAt": 1712155200000,
"startedAt": 1711324800000,
"currentDay": 10
}
3.2 레슨 완료 처리
📌 관련 문서
- HTTP 메서드: POST
- 경로: /learning/lessons/
{lessonId}/complete - Headers:
- Content-Type: application/json
- Authorization:
Bearer {accessToken}
- 설명: 해당 레슨을 완료 처리하고, 다음 레슨을 자동으로 해금합니다. 레슨 완료 시 즉시 다음 레슨이 해금되며(46번 레슨 제외), 46번 레슨(마지막 레슨)은 45번 레슨 완료 후 90일 이후에 해금됩니다.
응답 (Response)
- 성공 응답 (200 OK)
{
"success": true
}
- 오류 응답 (400 Bad Request)
{
"code": 2001,
"message": "LESSON_ALREADY_COMPLETED",
"detail": "이미 완료된 레슨입니다."
}
- 오류 응답 (403 Forbidden)
{
"code": 1001,
"message": "LESSON_NOT_UNLOCKED",
"detail": "아직 해금되지 않은 레슨입니다."
}
- 오류 응답 (403 Forbidden)
{
"code": 1003,
"message": "LESSON_ACCESS_BLOCKED",
"detail": "이전 레슨을 먼저 완료해야 합니다."
}
4. 하이라이트 API
📌 기술 구현 문서: TBD
하이라이트 API는 사용자가 중요하다고 생각하는 텍스트를 하이라이트하고 관리할 수 있는 기능을 제공합니다. 사용자는 텍스트를 선택하여 하이라이트를 생성하고, 목록을 조회하고, 더 이상 필요하지 않은 하이라이트를 삭제할 수 있습니다.
4.1 하이라이트 생성
📌 관련 문서: TBD
- HTTP 메서드: POST
- 경로: /learning/highlights
- Headers:
- Content-Type: application/json
- Authorization:
Bearer {accessToken}
- 설명: 새로운 하이라이트를 생성합니다. 하이라이트는 텍스트 선택으로 생성되며 배경색 강조 표시로 시각화됩니다.
요청 (Request)
{
"lessonId": "lesson_123",
"chapterName": "수면의 중요성",
"text": "건강한 수면은 신체 회복과 면역 시스템 강화에 필수적입니다.",
"position": {
"startOffset": 128,
"endOffset": 194
}
}
응답 (Response)
- 성공 응답 (201 Created)
{
"id": "highlight_456",
"lessonId": "lesson_123",
"chapterName": "수면의 중요성",
"text": "건강한 수면은 신체 회복과 면역 시스템 강화에 필수적입니다.",
"position": {
"startOffset": 128,
"endOffset": 194
},
"createdAt": 1711929600000
}
- 오류 응답 (400 Bad Request)
{
"code": 3001,
"message": "DUPLICATE_HIGHLIGHT",
"detail": "이미 하이라이트된 텍스트입니다."
}
4.2 하이라이트 목록 조회
- HTTP 메서드: GET
- 경로: /learning/highlights
- Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
- Query Parameters:
- lessonId: 특정 레슨의 하이라이트만 조회 (선택 사항)
- page: 페이지 번호 (선택 사항, 기본값: 1)
- pageSize: 페이지 크기 (선택 사항, 기본값: 20)
응답 (Response)
- 성공 응답 (200 OK)
{
"items": [
{
"id": "highlight_456",
"lessonId": "lesson_123",
"chapterName": "수면의 중요성",
"text": "건강한 수면은 신체 회복과 면역 시스템 강화에 필수적입니다.",
"position": {
"startOffset": 128,
"endOffset": 194
},
"createdAt": 1711929600000
},
{
"id": "highlight_457",
"lessonId": "lesson_124",
"chapterName": "수면 단계",
"text": "렘수면은 꿈을 꾸는 단계로, 학습과 기억 강화에 중요한 역할을 합니다.",
"position": {
"startOffset": 72,
"endOffset": 123
},
"createdAt": 1712016000000
}
],
"metadata": {
"totalCount": 7,
"currentPage": 1,
"pageSize": 20,
"totalPages": 1
}
}
4.3 하이라이트 삭제
- HTTP 메서드: DELETE
- 경로: /learning/highlights/
{highlightId} - Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
- 설명: 지정된 하이라이트를 영구적으로 삭제합니다.
응답 (Response)
-
성공 응답 (204 No Content)
-
오류 응답 (404 Not Found)
{
"code": 3002,
"message": "HIGHLIGHT_NOT_FOUND",
"detail": "해당 하이라이트를 찾을 수 없습니다."
}
5. 사용자 입력 API
📌 기술 구현 문서: TBD
사용자 입력 API는 학습 과정에서 사용자가 입력한 텍스트를 저장하고 관리하는 기능을 제공합니다. 입력은 포커스 아웃, 다음 버튼 클릭, 화면 이탈, 앱 백그라운드 전환 시 자동으로 저장됩니다.
5.1 입력 저장
📌 관련 문서: TBD
- HTTP 메서드: POST
- 경로: /learning/inputs
- Headers:
- Content-Type: application/json
- Authorization:
Bearer {accessToken}
요청 (Request)
{
"lessonId": "lesson_123",
"content": "오늘 배운 내용은 수면 단계에 대한 것이었다. 특히 렘수면과 비렘수면의 차이점이 인상적이었다..."
}
응답 (Response)
- 성공 응답 (201 Created)
{
"id": "input_789",
"lessonId": "lesson_123",
"content": "오늘 배운 내용은 수면 단계에 대한 것이었다. 특히 렘수면과 비렘수면의 차이점이 인상적이었다...",
"createdAt": 1712162100000,
"updatedAt": 1712162100000
}
5.2 입력 조회
- HTTP 메서드: GET
- 경로: /learning/lessons/
{lessonId}/inputs - Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
응답 (Response)
- 성공 응답 (200 OK)
{
"id": "input_789",
"lessonId": "lesson_123",
"content": "오늘 배운 내용은 수면 단계에 대한 것이었다. 특히 렘수면과 비렘수면의 차이점이 인상적이었다...",
"createdAt": 1712162100000,
"updatedAt": 1712162100000
}
- 입력 없음 응답 (204 No Content)
5.3 입력 업데이트
- HTTP 메서드: PUT
- 경로: /learning/inputs/
{inputId} - Headers:
- Content-Type: application/json
- Authorization:
Bearer {accessToken}
요청 (Request)
{
"content": "오늘 배운 내용은 수면 단계에 대한 것이었다. 특히 렘수면과 비렘수면의 차이점이 인상적이었다. 추가로 수면 주기에 대해서도 배웠다..."
}
응답 (Response)
- 성공 응답 (200 OK)
{
"id": "input_789",
"lessonId": "lesson_123",
"content": "오늘 배운 내용은 수면 단계에 대한 것이었다. 특히 렘수면과 비렘수면의 차이점이 인상적이었다. 추가로 수면 주기에 대해서도 배웠다...",
"createdAt": 1712162100000,
"updatedAt": 1712163400000
}
- 오류 응답 (404 Not Found)
{
"code": 4001,
"message": "INPUT_NOT_FOUND",
"detail": "해당 입력을 찾을 수 없습니다."
}
- 오류 응답 (400 Bad Request)
{
"code": 4002,
"message": "CONTENT_TOO_LONG",
"detail": "입력 내용이 최대 길이(1,000자)를 초과했습니다."
}
6. 설정 API
📌 기술 구현 문서: TBD
설정 API는 텍스트 크기, TTS(Text-to-Speech) 설정 등 학습 콘텐츠 표시 방식에 관한 사용자 설정을 관리하는 기능을 제공합니다. 설정은 모든 레슨에 전역적으로 적용됩니다.
6.1 텍스트 설정 조회
📌 관련 문서: TBD
- HTTP 메서드: GET
- 경로: /learning/settings/text
- Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
응답 (Response)
- 성공 응답 (200 OK)
{
"fontSize": 16,
"tts": {
"enabled": true,
"language": "de",
"speed": "NORMAL"
},
"updatedAt": 1712143800000
}
6.2 텍스트 설정 업데이트
- HTTP 메서드: PUT
- 경로: /learning/settings/text
- Headers:
- Content-Type: application/json
- Authorization:
Bearer {accessToken}
요청 (Request)
{
"fontSize": 18
}
응답 (Response)
- 성공 응답 (200 OK)
{
"fontSize": 18,
"tts": {
"enabled": true,
"language": "de",
"speed": "NORMAL"
},
"updatedAt": 1712166000000
}
- 오류 응답 (400 Bad Request)
{
"code": 5001,
"message": "INVALID_FONT_SIZE",
"detail": "폰트 크기는 12pt에서 24pt 사이여야 합니다."
}
6.3 TTS 설정 업데이트
- HTTP 메서드: PUT
- 경로: /learning/settings/tts
- Headers:
- Content-Type: application/json
- Authorization:
Bearer {accessToken}
요청 (Request)
{
"enabled": true,
"language": "de",
"speed": "FAST"
}
응답 (Response)
- 성공 응답 (200 OK)
{
"fontSize": 18,
"tts": {
"enabled": true,
"language": "de",
"speed": "FAST"
},
"updatedAt": 1712166300000
}
- 오류 응답 (400 Bad Request)
{
"code": 5002,
"message": "UNSUPPORTED_LANGUAGE",
"detail": "지원하지 않는 언어입니다. 지원 언어: de, en"
}
7. 미디어 API
📌 기술 구현 문서: TBD
미디어 API는 학습 콘텐츠에 포함된 이미지, 비디오, 오디오 클립의 메타데이터를 관리하는 기능을 제공합니다.
미디어 파일 관리 정책:
- 이미지: 모바일 앱에 내장되어 있으며, API는 메타데이터와 참조 정보만 제공
- 비디오 및 오디오 클립: 백엔드에서 파일을 관리하며, API를 통해 다운로드 URL과 메타데이터 제공
7.1 이미지 메타데이터 조회
📌 관련 문서: TBD
- HTTP 메서드: GET
- 경로: /learning/media/
{mediaId}/image - Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
응답 (Response)
- 성공 응답 (200 OK)
{
"id": "media_456",
"type": "IMAGE",
"refId": "img_sleep_cycle", // 앱 내 이미지 리소스 식별자
"lessonId": "lesson_123",
"metadata": {
"width": 1200,
"height": 800,
"aspectRatio": 1.5
},
"maxZoomLevel": 2.0
}
- 오류 응답 (404 Not Found)
{
"code": 6001,
"message": "MEDIA_NOT_FOUND",
"detail": "해당 미디어를 찾을 수 없습니다."
}
- 오류 응답 (400 Bad Request)
{
"code": 6002,
"message": "INVALID_MEDIA_TYPE",
"detail": "요청한 미디어는 이미지 형식이 아닙니다."
}
7.2 비디오 메타데이터 조회
- HTTP 메서드: GET
- 경로: /learning/media/
{mediaId}/video - Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
응답 (Response)
- 성공 응답 (200 OK)
{
"id": "media_789",
"type": "VIDEO",
"refId": "vid_sleep_animation", // 앱 내 비디오 리소스 식별자
"lessonId": "lesson_124",
"metadata": {
"width": 1920,
"height": 1080,
"duration": 180,
"thumbnailRefId": "img_sleep_anim_thumb" // 앱 내 썸네일 이미지 식별자
}
}
- 오류 응답 (404 Not Found)
{
"code": 6001,
"message": "MEDIA_NOT_FOUND",
"detail": "해당 미디어를 찾을 수 없습니다."
}
- 오류 응답 (400 Bad Request)
{
"code": 6003,
"message": "INVALID_MEDIA_TYPE",
"detail": "요청한 미디어는 비디오 형식이 아닙니다."
}
7.3 오디오 클립 메타데이터 조회
📌 관련 문서: TBD
- HTTP 메서드: GET
- 경로: /learning/media/
{mediaId}/audio - Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
응답 (Response)
- 성공 응답 (200 OK)
{
"id": "media_789",
"type": "AUDIO",
"url": "https://cdn.example.com/audio/sleep-explanation.mp3",
"lessonId": "lesson_123",
"metadata": {
"duration": 180,
"sampleRate": 44100,
"bitRate": 128,
"channels": 2,
"format": "MP3",
"fileSize": 3456000
},
"altText": "수면 주기에 대한 추가 설명"
}
- 오류 응답 (404 Not Found)
{
"code": 6001,
"message": "MEDIA_NOT_FOUND",
"detail": "해당 미디어를 찾을 수 없습니다."
}
- 오류 응답 (400 Bad Request)
{
"code": 6004,
"message": "INVALID_MEDIA_TYPE",
"detail": "요청한 미디어는 오디오 형식이 아닙니다."
}
8. 퀴즈 API
📌 기술 구현 문서: TBD
퀴즈 API는 학습한 내용을 확인하는 복습 퀴즈 기능을 제공합니다. 수면탐구 레슨 46개 중 45개를 완료한 사용자에게 제공되며, 46일차부터 89일차까지 매일 하루에 1개의 퀴즈가 제공됩니다. 퀴즈는 O/X 또는 사지선다형으로 제공됩니다.
8.1 퀴즈 응답 제출
- HTTP 메서드: POST
- 경로: /v1/learning/quiz/
{quizId}/answers - Headers:
- Content-Type: application/json
- Authorization:
Bearer {accessToken} - Accepted-Langague: 언어 코드 (de-DE, ko-KR, en-US) (필수)
퀴즈 응답 제출 API는 사용자가 특정 퀴즈에 대한 답을 선택했을 때 호출하는 API입니다. 퀴즈 응답 처리와 함께 학습 통계 업데이트 및 관련 이벤트를 발행합니다.
- 퀴즈 상태 확인: 비활성화된 퀴즈는 응답할 수 없습니다.
- 사용자 권한: 자신의 퀴즈만 응답 가능하며, 다른 사용자의 퀴즈 응답은 불가능합니다.
- 중복 응답: 동일한 퀴즈에 대한 중복 응답이 허용될 수 있으며, 최신 응답으로 업데이트됩니다.
- 옵션 검증: 존재하지 않는 옵션 ID는 오류를 발생시킵니다.
특정 퀴즈에 대한 사용자의 답변을 제출하고 관련 이벤트를 발행합니다.
Path Parameters
| Parameter | Type | Description | Required | Example |
|---|---|---|---|---|
quizId | string | 응답할 퀴즈의 고유 ID | Yes | quiz_123 |
Request Body
{
"selectedOptionId": "option_2"
}
| Field | Type | Description | Required | Example |
|---|---|---|---|---|
selectedOptionId | string | 선택한 퀴즈 옵션의 고유 ID | Yes | option_2 |
- 성공 응답 (200 OK)
{
"success": true
}
| 필드 | 타입 | 설명 | 예시 | 필수 (Yes/No) |
|---|---|---|---|---|
success | boolean | 퀴즈 응답 제출 성공 여부 | true | Yes |
400 Bad Request - 잘못된 옵션 ID
{
"code": 11033,
"message": "INVALID_QUIZ_OPTION",
"detail": "유효하지 않은 퀴즈 답변입니다"
}
이 오류는 요청한 옵션 ID가 해당 퀴즈에 존재하지 않을 때 발생합니다.
401 Unauthorized - 인증 실패
{
"code": 2051,
"message": "INVALID_TOKEN",
"detail": "토큰이 유효하지 않습니다"
}
유효하지 않은 액세스 토큰이 제공되었거나 토큰이 만료된 경우 반환됩니다.
404 Not Found - 퀴즈 없음
{
"code": 11030,
"message": "QUIZ_NOT_FOUND",
"detail": "퀴즈를 찾을 수 없습니다"
}
이 오류는 요청한 퀴즈 ID가 존재하지 않을 때 발생합니다.
404 Not Found - 사용자 상태 없음
{
"code": 7008,
"message": "USER_STATE_NOT_FOUND",
"detail": "사용자의 활성 주기를 찾을 수 없습니다"
}
이 오류는 사용자의 활성 사이클 정보가 존재하지 않을 때 발생합니다.
8.2 퀴즈 이력 조회
- HTTP 메서드: GET
- 경로: /v1/learning/quiz/history
- Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
- Query Parameters:
- page: 페이지 번호 (기본값: 1)
- pageSize: 페이지 크기 (기본값: 10)
응답 (Response)
- 성공 응답 (200 OK)
{
"items": [
{
"id": "response_456",
"quiz": {
"id": "quiz_123",
"type": "SINGLE_CHOICE",
"question": "건강한 수면 주기에서 렘수면이 차지하는 비율은 얼마인가요?",
"relatedLessonTitle": "수면의 단계와 구조"
},
"isCorrect": true,
"answeredAt": 1712167800000
},
{
"id": "response_457",
"quiz": {
"id": "quiz_124",
"type": "TRUE_FALSE",
"question": "수면의 질은 수면 시간보다 더 중요하다.",
"relatedLessonTitle": "수면의 질과 건강"
},
"isCorrect": false,
"answeredAt": 1712000100000
}
],
"metadata": {
"totalCount": 15,
"currentPage": 1,
"pageSize": 10,
"totalPages": 2
},
"statistics": {
"totalQuizzes": 15,
"correctAnswers": 12,
"accuracyRate": 80
}
}
9. 일일 콘텐츠 API
📌 기술 구현 문서: TBD
일일 콘텐츠 API는 사용자에게 오늘 해야 할 학습 콘텐츠(레슨 또는 퀴즈)를 제공하는 통합 기능을 담당합니다. 사용자의 학습 진행 상황과 일차에 따라 적절한 콘텐츠를 추천합니다.
주요 특징:
- 통합 엔드포인트: 레슨과 퀴즈를 하나의 엔드포인트에서 처리
- 번들 버전 관리: 클라이언트 번들 ID 기반 버전 일관성 보장
- 경량 응답: 콘텐츠 ID만 반환하여 클라이언트가 로컬 캐시 활용
- 동적 콘텐츠 결정: 진행 상황에 따라 레슨/퀴즈/없음/업데이트 필요 중 결정
- 캐싱 최적화: Redis 캐시를 통한 빠른 응답 (15분 TTL)
- 비즈니스 로직 준수: 모든 일반 레슨 완료 후 퀴즈 제공
9.1 일일 콘텐츠 조회 (통합)
- HTTP 메서드: GET
- 경로: /v1/learning/daily/content
- Headers:
- Authorization:
Bearer {accessToken} - Accept-Language: de-DE, ko-KR, en-US (필수)
- Authorization:
번들 버전 관리:
- 최신 번들: 클라이언트 bundleId가 최신 버전인 경우, 콘텐츠 ID만 반환 (클라이언트가 로컬 캐시에서 상세 정보 조회)
- 구버전 번들: 번들 업데이트 필요 응답 반환
- 버전 일관성: 백엔드와 클라이언트 간 콘텐츠 버전 불일치 방지
콘텐츠 결정 우선순위:
- 치료 기간 마지막 날: 최종 레슨만 제공 (사용자 진도와 관계없이)
- 모든 일반 레슨 완료 + 퀴즈 제공 시작일 도달: 일일 퀴즈 제공
- 일반 레슨 진행 중: 레슨 제공
- 모든 레슨 완료했지만 퀴즈 시작일 미도달: 콘텐츠 없음
레슨 추천 로직:
- 진도가 빠른 사용자: 현재 일차 번호에 해당하는 레슨 추천
- 진도가 느린 사용자: 다음 미완료 레슨 추천
퀴즈 제공 로직:
- 빠른/정상 진도: 학습 시작일로부터 (일반 레슨 수 + 1)일차부터
- 느린 진도: 모든 일반 레슨 완료 다음날부터
응답 (Response)
응답 구조:
currentDayIndex: 현재 일차 (학습 시작일 기준)contentType: 콘텐츠 타입 ("LESSON", "QUIZ", "NONE", "BUNDLE_UPDATE_REQUIRED")bundleId: 번들 IDlesson: 레슨 정보 (contentType이 "LESSON"인 경우, ID만 포함)quiz: 퀴즈 정보 (contentType이 "QUIZ"인 경우, ID만 포함)remainingDaysToTreatmentEnd: 치료 종료까지 남은 일수 (선택적)
1. 레슨 제공 응답 (최신 번들) (200 OK)
{
"bundleId": "bundle_learning_2024_v1",
"currentDayIndex": 7, // 필요한지 확인하기
"contentType": "LESSON",
"lessonLabel": 7
}
2. 퀴즈 제공 응답 (최신 번들) (200 OK)
{
"bundleId": "bundle_learning_2024_v1",
"currentDayIndex": 50,
"contentType": "QUIZ",
"quizId": "quiz_001"
}
4. 치료 마지막 날 응답 (200 OK)
{
"bundleId": "bundle_learning_2024_v1",
"currentDayIndex": 90,
"contentType": "LESSON",
"lessonLabel": 46
}
5. 콘텐츠 없음 응답 (200 OK)
{
"bundleId": "bundle_learning_2024_v1",
"currentDayIndex": 48
}
6. 오류 응답 (400 Bad Request)
{
"code": 8001,
"message": "INVALID_BUNDLE_ID",
"detail": "유효하지 않은 번들 ID입니다."
}
7. 오류 응답 (404 Not Found)
{
"code": 8002,
"message": "NO_DAILY_CONTENT_AVAILABLE",
"detail": "오늘 제공할 수 있는 콘텐츠가 없습니다."
}
10. BFF (Backend for Frontend) API
📌 기술 구현 문서: TBD
BFF API는 모바일 클라이언트를 위해 여러 도메인 데이터를 조합하여 단일 응답으로 제공하는 최적화된 엔드포인트입니다. 이를 통해 클라이언트의 네트워크 요청을 최소화하고 더 나은 사용자 경험을 제공합니다.
BFF 패턴 아키텍처
10.1 학습 세션 목록 조회 (BFF)
📌 관련 문서: TBD
모든 세션과 레슨 정보를 사용자별 진행 상태와 함께 조회합니다. 이 엔드포인트는 내부적으로 다음 API들을 호출하여 데이터를 조합합니다:
-
/v1/learning/sessions: 전역 콘텐츠 메타데이터 -
/v1/learning/me/lesson-states: 사용자별 레슨 상태 -
/v1/learning/progress: 전체 진행률 정보 -
HTTP 메서드: GET
-
경로: /v1/bff/learning/sessions
-
Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
-
Query Parameters:
- language: 언어 코드 (선택 사항, 기본값: de)
- status: 필터 (unlocked, locked, all) (선택 사항, 기본값: all)
- page: 페이지 번호 (선택 사항, 기본값: 1)
- pageSize: 페이지 크기 (선택 사항, 기본값: 10)
응답 (Response)
- 성공 응답 (200 OK)
{
"items": [
{
"id": "session_101",
"title": "수면 기초",
"description": "수면의 기초와 이론에 대한 학습",
"orderIndex": 1,
"isUnlocked": true,
"unlockedAt": 1711929600000,
"progress": {
"unlockedCount": 5,
"totalCount": 5,
"completedCount": 2,
"progressPercentage": 40.0
},
"lessons": [
{
"id": "lesson_123",
"title": "건강한 수면을 위한 첫걸음",
"sessionId": "session_101",
"orderIndex": 1,
"status": "COMPLETED",
"isCompleted": true,
"unlockedAt": 1711929600000,
"completedAt": 1712048400000,
"lastAccessedAt": 1712048400000,
"createdAt": 1704067200000,
"updatedAt": 1712048400000
}
],
"createdAt": 1704067200000,
"updatedAt": 1712048400000
}
],
"metadata": {
"totalCount": 5,
"currentPage": 1,
"pageSize": 10,
"totalPages": 1
}
}
10.2 학습 대시보드 조회 (BFF)
📌 관련 문서: TBD
사용자의 전체 학습 현황을 한 번의 요청으로 조회합니다.
- HTTP 메서드: GET
- 경로: /v1/bff/learning/dashboard
- Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
응답 (Response)
- 성공 응답 (200 OK)
{
"overview": {
"totalLessons": 46,
"completedLessons": 12,
"progressPercentage": 26.09,
"currentDay": 15,
"startedAt": 1711324800000,
"lastActivityAt": 1712155200000
},
"currentSession": {
"sessionId": "session_102",
"sessionTitle": "수면 최적화",
"currentLessonId": "lesson_131",
"currentLessonTitle": "수면 환경 최적화하기",
"sessionProgress": 40
},
"recentActivities": [
{
"type": "lesson_completed",
"lessonId": "lesson_130",
"lessonTitle": "수면 위생의 중요성",
"timestamp": 1712155200000
},
{
"type": "quiz_completed",
"quizId": "quiz_456",
"timestamp": 1712068800000
}
],
"recommendedLesson": {
"id": "lesson_131",
"title": "수면 환경 최적화하기",
"sessionId": "session_102",
"orderIndex": 1,
"status": "UNLOCKED",
"isCompleted": false,
"unlockedAt": 1711929600000,
"completedAt": null,
"lastAccessedAt": null,
"createdAt": 1704067200000,
"updatedAt": 1711929600000,
"recommendationReason": "CURRENT_DAY_LESSON"
},
"dailyQuiz": {
"available": false,
"eligibilityMessage": "수면탐구 레슨 46개 중 45개를 완료해야 퀴즈를 이용할 수 있습니다. (현재: 12/46)"
},
"achievements": [
{
"type": "milestone",
"title": "첫 번째 세션 완료",
"description": "수면 기초 세션의 모든 레슨을 완료했습니다.",
"achievedAt": 1711584000000
},
{
"type": "streak",
"title": "7일 연속 학습",
"description": "일주일 동안 매일 학습을 진행했습니다.",
"achievedAt": 1711497600000
}
]
}
10.3 레슨 상세 정보 조회 (BFF)
📌 관련 문서: TBD
레슨의 상세 정보와 관련 데이터를 한 번에 조회합니다.
- HTTP 메서드: GET
- 경로: /v1/bff/learning/lessons/
{lessonId} - Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
- Query Parameters:
- includeRelated: 관련 콘텐츠 포함 여부 (선택 사항, 기본값: false)
응답 (Response)
- 성공 응답 (200 OK)
{
"lesson": {
"id": "lesson_123",
"title": "건강한 수면을 위한 첫걸음",
"sessionId": "session_101",
"orderIndex": 1,
"status": "UNLOCKED",
"isCompleted": false,
"unlockedAt": 1711929600000,
"completedAt": null,
"lastAccessedAt": 1712155200000,
"createdAt": 1704067200000,
"updatedAt": 1712155200000
},
"mediaRefs": [
{
"id": "media_456",
"type": "IMAGE",
"refId": "img_sleep_cycle",
"metadata": {
"width": 1200,
"height": 800,
"aspectRatio": 1.5
}
}
],
"relatedLessons": [
{
"id": "lesson_124",
"title": "수면의 단계와 구조",
"sessionId": "session_101",
"orderIndex": 2,
"status": "LOCKED",
"isCompleted": false,
"unlockedAt": null,
"completedAt": null,
"lastAccessedAt": null,
"createdAt": 1704067200000,
"updatedAt": 1704067200000
}
]
}
11. 모바일 캐싱 API
📌 기술 구현 문서: TBD
모바일 캐싱 API는 클라이언트가 효율적으로 학습 콘텐츠를 다운로드하고 캐싱할 수 있도록 번들 형태로 콘텐츠를 제공합니다. 이를 통해 오프라인 환경에서도 원활한 학습 경험을 제공합니다.
11.1 활성 번들 정보 조회
- HTTP 메서드: GET
- 경로: /v1/learning/bundles/active
- Headers:
- Authorization: Bearer
{accessToken}
- Authorization: Bearer
- 설명: 현재 활성화된 학습 콘텐츠 번들의 정보를 조회합니다. 클라이언트는 이 정보를 통해 새로운 번들이 있는지 확인할 수 있습니다.
응답 (Response)
- 성공 응답 (200 OK)
{
"bundleId": "bundle_learning_2024_v1",
"version": "2024.1.0",
"generatedAt": 1715600400000,
"isActive": true,
"metadata": {
"contentCount": {
"sessions": 9,
"lessons": 46,
"media": 120
},
"supportedLanguages": ["de-DE", "en-US", "ko-KR"]
}
}
11.2 번들 다운로드 URL 생성
- HTTP 메서드: POST
- 경로: /v1/learning/bundles/
{bundleId}/download-url - Headers:
- Authorization: Bearer
{accessToken} - Content-Type: application/json
- Authorization: Bearer
- Path Parameters:
- bundleId: 번들 ID (필수)
요청 (Request)
{
"language": "ko-KR",
"format": "ZIP"
}
응답 (Response)
- 성공 응답 (200 OK)
{
"downloadUrl": "https://cdn.example.com/bundles/learning/bundle_learning_2024_v1_ko-KR.zip",
"expiresAt": 1715604000000,
"fileSize": 5242880,
"checksum": "sha256:abc123..."
}
12. 학습 이력 API
📌 기술 구현 문서: TBD
학습 이력 API는 사용자의 레슨 학습 과정을 상세히 추적하고 관리하는 기능을 제공합니다. 학습 시작/종료 시간, 일시정지/재개 이벤트를 기록하여 정확한 학습 소요시간을 측정하고 학습 패턴을 분석할 수 있습니다.
주요 특징:
- 상세 학습 추적: 시작, 종료, 일시정지, 재개 등 모든 학습 이벤트 기록
- 배치 처리 지원: 빈번한 일시정지/재개 이벤트를 효율적으로 처리하기 위한 배치 API 제공
- 소요시간 계산: 실제 학습에 소요된 시간을 정확히 계산 (일시정지 시간 제외)
- 학습 패턴 분석: 사용자의 학습 습관과 패턴 분석을 위한 데이터 수집
12.1 학습 이력 저장
📌 관련 문서: TBD
- HTTP 메서드: POST
- 경로: /v1/learning/history
- Headers:
- Content-Type: application/json
- Authorization:
Bearer {accessToken}
- 설명: 단일 학습 이벤트(시작, 종료, 일시정지, 재개)를 기록합니다.
요청 (Request)
{
"lessonId": "lesson_123",
"lessonLabel": 2,
"eventType": "START",
"timestamp": 1712048400000, // timestamp ms
"sessionId": "session_20240402_001",
"clientInfo": {
"platform": "iOS",
"version": "1.2.0",
"deviceId": "device_123"
}
}
응답 (Response)
- 성공 응답 (201 Created)
{
"id": "history_456",
"lessonId": "lesson_123",
"eventType": "START",
"timestamp": 1712048400000,
"sessionId": "session_20240402_001",
"createdAt": 1712048401000
}
- 오류 응답 (400 Bad Request)
{
"code": 10001,
"message": "INVALID_EVENT_TYPE",
"detail": "유효하지 않은 이벤트 타입입니다. 지원 타입: START, PAUSE, RESUME, END"
}
12.2 학습 이력 배치 저장
📌 관련 문서: TBD
- HTTP 메서드: POST
- 경로: /v1/learning/history/batch
- Headers:
- Content-Type: application/json
- Authorization:
Bearer {accessToken}
- 설명: 여러 학습 이벤트를 한 번에 배치 처리합니다. 네트워크 효율성을 위해 클라이언트에서 수집한 이벤트들을 묶어서 전송할 때 사용합니다.
요청 (Request)
{
"lessonId": "lesson_123",
"sessionId": "session_20240402_001",
"events": [
{
"eventType": "START",
"timestamp": 1712048400000 // unixtimestamp in ms
},
{
"eventType": "PAUSE",
"timestamp": 1712048460000
},
{
"eventType": "RESUME",
"timestamp": 1712048520000
},
{
"eventType": "PAUSE",
"timestamp": 1712048580000
},
{
"eventType": "RESUME",
"timestamp": 1712048640000
},
{
"eventType": "END",
"timestamp": 1712048700000
}
]
// 추후 논의 예정
// "clientInfo": {
// "platform": "iOS",
// "version": "1.2.0",
// "deviceId": "device_123"
// }
}
응답 (Response)
- 성공 응답 (201 Created)
{
"batchId": "batch_789",
"lessonId": "lesson_123",
"sessionId": "session_20240402_001",
"processedCount": 6,
"failedCount": 0,
"results": [
{
"id": "history_456",
"eventType": "START",
"timestamp": 1712048400000,
"status": "SUCCESS"
},
{
"id": "history_457",
"eventType": "PAUSE",
"timestamp": 1712048460000,
"status": "SUCCESS"
}
],
"metrics": {
"totalLearningTimeMs": 240000,
"activeLearningTimeMs": 180000,
"pausedTimeMs": 60000,
"pauseCount": 2
},
"createdAt": 1712048701000
}
- 오류 응답 (400 Bad Request)
{
"code": 10002,
"message": "BATCH_TOO_LARGE",
"detail": "배치 크기가 너무 큽니다. 최대 100개 이벤트까지 처리 가능합니다."
}
12.3 특정 레슨 학습 이력 조회
- HTTP 메서드: GET
- 경로: /v1/learning/history/lesson/
{lessonId} - Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
- Query Parameters:
- sessionId: 특정 세션의 이력만 조회 (선택 사항)
- fromDate: 조회 시작 날짜 (선택 사항, Unix timestamp)
- toDate: 조회 종료 날짜 (선택 사항, Unix timestamp)
- page: 페이지 번호 (선택 사항, 기본값: 1)
- pageSize: 페이지 크기 (선택 사항, 기본값: 50)
응답 (Response)
- 성공 응답 (200 OK)
{
"lessonId": "lesson_123",
"items": [
{
"sessionId": "session_20240402_001",
"startedAt": 1712048400000,
"endedAt": 1712048700000,
"totalLearningTimeMs": 240000,
"activeLearningTimeMs": 180000,
"pausedTimeMs": 60000,
"pauseCount": 2,
"events": [
{
"id": "history_456",
"eventType": "START",
"timestamp": 1712048400000
},
{
"id": "history_457",
"eventType": "PAUSE",
"timestamp": 1712048460000
},
{
"id": "history_458",
"eventType": "RESUME",
"timestamp": 1712048520000
},
{
"id": "history_459",
"eventType": "END",
"timestamp": 1712048700000
}
]
}
],
"metadata": {
"totalCount": 1,
"currentPage": 1,
"pageSize": 50,
"totalPages": 1
},
"summary": {
"totalSessions": 3,
"totalLearningTimeMs": 1800000,
"averageSessionTimeMs": 600000,
"completionRate": 100
}
}
12.4 내 학습 이력 조회
- HTTP 메서드: GET
- 경로: /v1/learning/history/me
- Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
- Query Parameters:
- lessonId: 특정 레슨 필터 (선택 사항)
- fromDate: 조회 시작 날짜 (선택 사항, Unix timestamp)
- toDate: 조회 종료 날짜 (선택 사항, Unix timestamp)
- page: 페이지 번호 (선택 사항, 기본값: 1)
- pageSize: 페이지 크기 (선택 사항, 기본값: 20)
응답 (Response)
- 성공 응답 (200 OK)
{
"items": [
{
"lessonId": "lesson_123",
"lessonTitle": "건강한 수면을 위한 첫걸음",
"lastSessionId": "session_20240402_001",
"lastStudiedAt": 1712048700000,
"totalSessions": 3,
"totalLearningTimeMs": 1800000,
"averageSessionTimeMs": 600000,
"completionStatus": "COMPLETED"
},
{
"lessonId": "lesson_124",
"lessonTitle": "수면의 단계와 구조",
"lastSessionId": "session_20240403_001",
"lastStudiedAt": 1712135100000,
"totalSessions": 1,
"totalLearningTimeMs": 900000,
"averageSessionTimeMs": 900000,
"completionStatus": "IN_PROGRESS"
}
],
"metadata": {
"totalCount": 15,
"currentPage": 1,
"pageSize": 20,
"totalPages": 1
},
"summary": {
"totalLessonsStudied": 15,
"totalLearningTimeMs": 27000000,
"averageLessonTimeMs": 1800000,
"totalSessions": 45,
"averageSessionsPerLesson": 3.0
}
}
13. 학습 통계 API
📌 기술 구현 문서: TBD
학습 통계 API는 사용자의 학습 데이터를 분석하여 의미 있는 통계와 인사이트를 제공합니다. 학습 시간, 패턴, 효율성 등을 측정하여 개인화된 학습 경험을 지원합니다.
주요 특징:
- 학습 시간 분석: 일별, 주별, 월별 학습 시간 통계
- 학습 효율성 측정: 레슨별 평균 완료 시간, 반복 학습 빈도
- 학습 패턴 분석: 선호하는 학습 시간대, 세션 길이 패턴
- 진도 추적: 학습 속도, 목표 대비 진행률
13.1 내 학습 통계 조회
📌 관련 문서: TBD
- HTTP 메서드: GET
- 경로: /v1/learning/metrics/me
- Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
- Query Parameters:
- period: 통계 기간 (daily, weekly, monthly, all) (선택 사항, 기본값: all)
- fromDate: 조회 시작 날짜 (선택 사항, Unix timestamp)
- toDate: 조회 종료 날짜 (선택 사항, Unix timestamp)
응답 (Response)
- 성공 응답 (200 OK)
{
"userId": "user_123",
"period": "all",
"generatedAt": 1712048701000,
"overall": {
"totalLearningTimeMs": 27000000,
"totalActiveLearningTimeMs": 24300000,
"totalPausedTimeMs": 2700000,
"totalSessions": 45,
"totalLessonsStudied": 15,
"completedLessons": 12,
"averageSessionDurationMs": 600000,
"averageLessonCompletionTimeMs": 1800000,
"learningStreak": 7,
"totalPauseCount": 120
},
"timeBasedStats": {
"dailyAverage": {
"learningTimeMs": 900000,
"sessionCount": 1.5,
"pauseCount": 4
},
"weeklyTrend": [
{
"week": "2024-W14",
"totalTimeMs": 6300000,
"sessionCount": 10,
"lessonsCompleted": 4
},
{
"week": "2024-W15",
"totalTimeMs": 7200000,
"sessionCount": 12,
"lessonsCompleted": 5
}
],
"preferredTimeSlots": [
{
"hour": 20,
"sessionCount": 15,
"totalTimeMs": 9000000
},
{
"hour": 9,
"sessionCount": 12,
"totalTimeMs": 7200000
}
]
},
"learningPatterns": {
"averageSessionsPerLesson": 3.0,
"mostProductiveDay": "Tuesday",
"averageBreaksBetweenSessions": 2.5,
"preferredSessionLength": "MEDIUM",
"learningConsistency": 0.85
},
"lessonMetrics": [
{
"lessonId": "lesson_123",
"lessonTitle": "건강한 수면을 위한 첫걸음",
"totalTimeMs": 1800000,
"sessionCount": 3,
"completionTimeMs": 1620000,
"averageSessionTimeMs": 600000,
"pauseCount": 8,
"efficiency": 0.9
}
]
}
13.2 특정 레슨 통계 조회
- HTTP 메서드: GET
- 경로: /v1/learning/metrics/lesson/
{lessonId} - Headers:
- Authorization:
Bearer {accessToken}
- Authorization:
- Query Parameters:
- includeComparison: 다른 사용자와의 비교 포함 (선택 사항, 기본값: false)
응답 (Response)
- 성공 응답 (200 OK)
{
"lessonId": "lesson_123",
"lessonTitle": "건강한 수면을 위한 첫걸음",
"userId": "user_123",
"generatedAt": 1712048701000,
"personalMetrics": {
"totalLearningTimeMs": 1800000,
"activeLearningTimeMs": 1620000,
"pausedTimeMs": 180000,
"sessionCount": 3,
"pauseCount": 8,
"completionTimeMs": 1620000,
"averageSessionTimeMs": 600000,
"longestSessionMs": 900000,
"shortestSessionMs": 300000,
"efficiency": 0.9,
"isCompleted": true,
"completedAt": 1712048700000
},
"sessionBreakdown": [
{
"sessionId": "session_20240401_001",
"startedAt": 1711929600000,
"endedAt": 1711930500000,
"durationMs": 900000,
"activeLearningTimeMs": 810000,
"pauseCount": 3,
"completed": false
},
{
"sessionId": "session_20240402_001",
"startedAt": 1712016000000,
"endedAt": 1712016600000,
"durationMs": 600000,
"activeLearningTimeMs": 540000,
"pauseCount": 2,
"completed": false
},
{
"sessionId": "session_20240402_002",
"startedAt": 1712048400000,
"endedAt": 1712048700000,
"durationMs": 300000,
"activeLearningTimeMs": 270000,
"pauseCount": 3,
"completed": true
}
],
"comparison": {
"averageLessonTimeMs": 2100000,
"medianLessonTimeMs": 1950000,
"percentileRank": 25,
"fasterThanPercent": 25,
"averageSessionCount": 3.8,
"userPerformance": "FASTER_THAN_AVERAGE"
}
}
14. 오류 코드
| 코드 | 메시지 | 설명 | HTTP 상태 코드 |
|---|---|---|---|
| 1000번대: 콘텐츠 관련 오류 | |||
| 1001 | LESSON_NOT_UNLOCKED | 아직 해금되지 않은 레슨입니다. | 403 |
| 1002 | LESSON_NOT_FOUND | 존재하지 않는 레슨입니다. | 404 |
| 1003 | LESSON_ACCESS_BLOCKED | 이전 레슨을 먼저 완료해야 합니다. | 403 |
| 1004 | LESSON_STATUS_TRANSITION_INVALID | 유효하지 않은 레슨 상태 전환입니다. | 400 |
| 1005 | LESSON_ALREADY_COMPLETED | 이미 완료된 레슨입니다. | 409 |
| 1006 | PREVIOUS_LESSON_NOT_COMPLETED | 이전 레슨을 먼저 완료해야 합니다. | 403 |
| 1007 | INVALID_START_DATE | 유효하지 않은 시작일입니다. | 400 |
| 1008 | USER_NOT_FOUND | 사용자를 찾을 수 없습니다. | 404 |
| 1009 | VERSION_REQUIRED | 콘텐츠 버전 정보가 필요합니다. | 400 |
| 1010 | BUNDLE_NOT_FOUND | 요청한 번들을 찾을 수 없습니다. | 404 |
| 2000번대: 학습 진행 관련 오류 | |||
| 2001 | LESSON_ALREADY_COMPLETED | 이미 완료된 레슨입니다. | 400 |
| 2002 | PROGRESS_NOT_FOUND | 학습 진행 정보를 찾을 수 없습니다. | 404 |
| 3000번대: 하이라이트 관련 오류 | |||
| 3001 | DUPLICATE_HIGHLIGHT | 이미 하이라이트된 텍스트입니다. | 400 |
| 3002 | HIGHLIGHT_NOT_FOUND | 해당 하이라이트를 찾을 수 없습니다. | 404 |
| 3003 | HIGHLIGHT_LIMIT_REACHED | 하이라이트 제한(레슨당 50개)에 도달했습니다. | 400 |
| 3004 | HIGHLIGHT_TOO_SHORT | 하이라이트 텍스트가 너무 짧습니다(최소 3자). | 400 |
| 3005 | HIGHLIGHT_TOO_LONG | 하이라이트 텍스트가 너무 깁니다(최대 2,000자). | 400 |
| 4000번대: 사용자 입력 관련 오류 | |||
| 4001 | INPUT_NOT_FOUND | 해당 입력을 찾을 수 없습니다. | 404 |
| 4002 | CONTENT_TOO_LONG | 입력 내용이 최대 길이(1,000자)를 초과했습니다. | 400 |
| 4003 | INVALID_IMAGE_FORMAT | 지원하지 않는 이미지 형식입니다. 지원 형식: JPEG, PNG | 400 |
| 4004 | IMAGE_TOO_LARGE | 이미지 크기가 너무 큽니다(최대 1MB). | 400 |
| 5000번대: 설정 관련 오류 | |||
| 5001 | INVALID_FONT_SIZE | 폰트 크기는 12pt에서 24pt 사이여야 합니다. | 400 |
| 5002 | UNSUPPORTED_LANGUAGE | 지원하지 않는 언어입니다. 지원 언어: de, en | 400 |
| 5003 | INVALID_TTS_SPEED | 유효하지 않은 TTS 속도입니다. 지원 속도: SLOW, NORMAL, FAST | 400 |
| 6000번대: 미디어 관련 오류 | |||
| 6001 | MEDIA_NOT_FOUND | 해당 미디어를 찾을 수 없습니다. | 404 |
| 6002 | INVALID_MEDIA_TYPE | 요청한 미디어는 이미지 형식이 아닙니다. | 400 |
| 6003 | INVALID_MEDIA_TYPE | 요청한 미디어는 비디오 형식이 아닙니다. | 400 |
| 7000번대: 퀴즈 관련 오류 | |||
| 7001 | NOT_ELIGIBLE_FOR_QUIZ | 수면탐구 레슨 46개 중 45개를 완료해야 퀴즈를 이용할 수 있습니다. | 403 |
| 7002 | NO_QUIZ_AVAILABLE | 오늘의 퀴즈가 없습니다. | 404 |
| 7003 | INVALID_OPTION | 유효하지 않은 선택지입니다. | 400 |
| 7004 | QUIZ_ALREADY_ANSWERED | 이미 응답한 퀴즈입니다. | 409 |
| 8000번대: 일일 콘텐츠 관련 오류 | |||
| 8001 | NO_DAILY_LESSON_AVAILABLE | 오늘 추천할 수 있는 레슨이 없습니다. | 404 |
| 8002 | NO_DAILY_QUIZ_AVAILABLE | 오늘의 퀴즈가 없습니다. | 404 |
| 9000번대: 일반 오류 | |||
| 9001 | UNAUTHORIZED | 인증되지 않은 요청입니다. | 401 |
| 9002 | FORBIDDEN | 접근 권한이 없습니다. | 403 |
| 9003 | INTERNAL_SERVER_ERROR | 서버 내부 오류가 발생했습니다. | 500 |
| 10000번대: 학습 이력 관련 오류 | |||
| 10001 | INVALID_EVENT_TYPE | 유효하지 않은 이벤트 타입입니다. 지원 타입: START, PAUSE, RESUME, END | 400 |
| 10002 | BATCH_TOO_LARGE | 배치 크기가 너무 큽니다. 최대 100개 이벤트까지 처리 가능합니다. | 400 |
| 10003 | INVALID_SESSION_ID | 유효하지 않은 세션 ID입니다. | 400 |
| 10004 | DUPLICATE_EVENT | 중복된 이벤트입니다. | 409 |
| 10005 | INVALID_EVENT_SEQUENCE | 유효하지 않은 이벤트 순서입니다. | 400 |
| 10006 | HISTORY_NOT_FOUND | 해당 학습 이력을 찾을 수 없습니다. | 404 |
| 10007 | INVALID_TIMESTAMP | 유효하지 않은 타임스탬프입니다. | 400 |
| 10008 | BATCH_PROCESSING_FAILED | 배치 처리 중 오류가 발생했습니다. | 500 |
| 11000번대: 학습 통계 관련 오류 | |||
| 11001 | INSUFFICIENT_DATA | 통계 생성을 위한 데이터가 부족합니다. | 400 |
| 11002 | INVALID_PERIOD | 유효하지 않은 통계 기간입니다. 지원 기간: daily, weekly, monthly, all | 400 |
| 11003 | METRICS_NOT_AVAILABLE | 해당 레슨의 통계가 아직 생성되지 않았습니다. | 404 |
| 11004 | DATE_RANGE_TOO_LARGE | 조회 날짜 범위가 너무 큽니다. 최대 1년까지 조회 가능합니다. | 400 |
15. 변경 이력
| 버전 | 날짜 | 작성자 | 변경 내용 |
|---|---|---|---|
| 0.1.0 | 2025-01-03 | bok@weltcorp.com | 최초 작성 |
| 0.2.0 | 2025-01-03 | bok@weltcorp.com | BFF API 섹션 추가 - 모바일 클라이언트 최적화를 위한 통합 엔드포인트 정의 |
| 0.3.0 | 2025-01-03 | bok@weltcorp.com | 콘텐츠 메타데이터와 사용자별 상태 분리 - GET /v1/learning/content는 전역 콘텐츠용, GET /v1/learning/me/lesson-states는 사용자별 상태용으로 분리 |
| 0.4.0 | 2025-06-09 | bok@weltcorp.com | 콘텐츠 버전 관리 강화 - /v1/learning/content API에 version 파라미터 필수화, Mobile API와 연동하여 버전 관리 |
| 0.5.0 | 2025-06-10 | bok@weltcorp.com | API 경로 명확화 - /v1/learning/sessions를 /v1/learning/content로 변경, contentId 제거하여 실제 스키마와 일치 |
| 0.6.0 | 2025-06-10 | bok@weltcorp.com | LearningContentBundle 도입 |
| 0.7.0 | 2025-06-10 | bok@weltcorp.com | 학습 이력 추적 기능 추가 - 학습 이력 API와 통계 API 추가, 레슨별 학습 소요시간 측정 및 배치 처리 지원 |
| 0.8.0 | 2025-06-10 | bok@weltcorp.com | 일일 콘텐츠 API 용어 통일 - 추천 API를 일일 콘텐츠 API로 변경, /daily 경로로 레슨과 퀴즈 엔드포인트 통일 |
| 0.9.0 | 2025-07-09 | elizabeth@weltcorp.com | 최신 번들 조회 API 추가 - GET /v1/learning/bundles/latest 엔드포인트 추가, 앱 토큰 기반 최신 학습 콘텐츠 번들 제공 |
| 0.9.1 | 2025-07-10 | elizabeth@weltcorp.com | 일일 콘텐츠 API 응답 구조 간소화 - bundleVersion, isLatestBundle 등 불필요한 필드 제거, API 응답 예시 정리 및 명확화 |
| 0.9.2 | 2025-07-10 | elizabeth@weltcorp.com | 사용자 레슨 상태 API 응답 구조 간소화 - UserLessonStatesSummaryDto 제거, summary 필드 삭제하여 개별 레슨 상태만 제공 |
| 0.9.3 | 2025-07-10 | elizabeth@weltcorp.com | 레슨 완료 API 응답 간소화 - CompleteLessonResponseDto를 success boolean 필드만 포함하도록 변경, 복잡한 객체 구조 제거 |
| 0.9.4 | 2025-07-11 | elizabeth@weltcorp.com | 퀴즈 응답 제출 API 문서 형식 표준화 - 기존 문서 형식에 맞춰 상세한 Headers, Parameters, Responses 섹션 추가 및 사용 예시, 활용 사례 보완 |