2025-12-23 07:55:23 +00:00

471 lines
14 KiB
Markdown

# 레슨 콘텐츠 타입별 UX 정의
이 문서는 레슨(Lesson) 시스템의 4가지 콘텐츠 타입이 **어떻게 보이고 어떻게 동작해야 하는지** 명확하게 정의합니다.
---
## 1. Theory (이론 설명)
### 목적
개념이나 지식을 가르치는 읽기 전용 콘텐츠
### 데이터 구조
```typescript
interface TheoryBlock {
markdown: string;
imageUrl?: string;
}
```
### 렌더링 방법
**컴포넌트**: `TheoryRenderer.tsx`
**UI 구성**:
- 마크다운을 HTML로 렌더링 (제목, 굵은 글씨, 목록 등 지원)
- 이미지가 있으면 하단에 표시
- 읽기 전용 (사용자 입력 없음)
**사용자 액션**:
- 읽기만 함
- 스크롤 가능
- 액션 없음 → 자동으로 "완료" 처리
**예시**:
```markdown
## 의성어란?
소리를 흉내 낸 말이에요.
**예시:**
- 강아지: 멍멍, 왈왈
- 비: 주룩주룩, 후두둑
의성어를 사용하면 글을 읽는 사람이 소리를 상상할 수 있어요!
```
**렌더링 결과**:
```
┌─────────────────────────────────┐
│ 의성어란? │
│ │
│ 소리를 흉내 낸 말이에요. │
│ │
│ 예시: │
│ • 강아지: 멍멍, 왈왈 │
│ • 비: 주룩주룩, 후두둑 │
│ │
│ 의성어를 사용하면... │
│ │
│ [이미지가 있으면 여기에 표시] │
└─────────────────────────────────┘
```
---
## 2. Quiz (퀴즈)
### 목적
이해도를 확인하는 문제 (객관식 또는 주관식)
### 데이터 구조
```typescript
interface QuizBlock {
question: string;
type: 'multiple_choice' | 'short_answer';
options?: string[]; // 객관식인 경우
answer: string | number; // 주관식: string, 객관식: 선택지 인덱스 (0부터)
explanation: string;
}
```
### 렌더링 방법
**컴포넌트**: `QuizRenderer.tsx`
**UI 구성**:
1. **문제 표시**
2. **답변 입력 영역** (타입에 따라)
- **객관식**: 라디오 버튼
- **주관식**: 텍스트 입력
3. **제출 버튼**
4. **결과 표시** (제출 후)
- 정답/오답 표시
- 해설 표시
### 사용자 액션
**객관식 플로우**:
1. 문제 읽기
2. 선택지 중 하나 선택 (Radio button)
3. "정답 확인" 버튼 클릭
4. 결과 표시:
-**정답**: "정답입니다!" + 해설
-**오답**: "아쉬워요! 정답은 X번입니다" + 해설
5. 다음으로 진행 가능
**주관식 플로우**:
1. 문제 읽기
2. 텍스트 입력
3. "정답 확인" 버튼 클릭
4. 정답과 비교 (대소문자 무시, 공백 제거 후 비교)
5. 결과 표시
6. 다음으로 진행 가능
### UI 예시
**객관식 (제출 전)**:
```
┌─────────────────────────────────┐
│ 📝 퀴즈 │
│ │
│ 다음 중 의성어가 아닌 것은? │
│ │
│ ○ 졸졸 │
│ ○ 빨갛다 │
│ ○ 윙윙 │
│ ○ 톡톡 │
│ │
│ [정답 확인] │
└─────────────────────────────────┘
```
**객관식 (정답 제출 후)**:
```
┌─────────────────────────────────┐
│ 📝 퀴즈 │
│ │
│ 다음 중 의성어가 아닌 것은? │
│ │
│ ○ 졸졸 │
│ ● 빨갛다 ❌ (선택) │
│ ○ 윙윙 │
│ ○ 톡톡 │
│ │
│ ❌ 아쉬워요! │
│ 정답은 2번 "빨갛다" 입니다. │
│ │
│ 💡 '빨갛다'는 색깔을 나타내는 │
│ 말이에요. │
└─────────────────────────────────┘
```
**주관식**:
```
┌─────────────────────────────────┐
│ 📝 퀴즈 │
│ │
│ 비유란 무엇일까요? │
│ │
│ ┌─────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────┘ │
│ │
│ [정답 확인] │
└─────────────────────────────────┘
```
---
## 3. Mission (미션)
### 목적
학생이 직접 찾거나 수행해야 할 체크리스트
### 데이터 구조
```typescript
interface MissionBlock {
description: string;
items: string[]; // 체크할 항목들
}
```
### 렌더링 방법
**컴포넌트**: `MissionRenderer.tsx`
**UI 구성**:
1. 미션 설명
2. 체크박스 리스트
3. 완료 상태 표시
### 사용자 액션
1. 미션 설명 읽기
2. 각 항목 수행 후 체크
3. 모든 항목 체크 시 자동 완료
**중요**: 실제로 "수행했는지"는 검증 불가능 → 신뢰 기반 체크
### UI 예시
**초기 상태**:
```
┌─────────────────────────────────┐
│ 🎯 미션 │
│ │
│ 소리 탐정이 되어보자! │
│ │
│ ☐ 지금 주변에서 들리는 │
│ 소리 3가지 찾기 │
│ │
│ ☐ 각 소리를 의성어로 │
│ 표현해보기 │
│ │
│ 진행률: 0 / 2 │
└─────────────────────────────────┘
```
**일부 완료**:
```
┌─────────────────────────────────┐
│ 🎯 미션 │
│ │
│ 소리 탐정이 되어보자! │
│ │
│ ☑ 지금 주변에서 들리는 │
│ 소리 3가지 찾기 │
│ │
│ ☐ 각 소리를 의성어로 │
│ 표현해보기 │
│ │
│ 진행률: 1 / 2 │
└─────────────────────────────────┘
```
**전체 완료**:
```
┌─────────────────────────────────┐
│ 🎯 미션 │
│ │
│ 소리 탐정이 되어보자! │
│ │
│ ☑ 지금 주변에서 들리는 │
│ 소리 3가지 찾기 │
│ │
│ ☑ 각 소리를 의성어로 │
│ 표현해보기 │
│ │
│ ✅ 완료했어요! │
└─────────────────────────────────┘
```
---
## 4. WritingPrompt (글쓰기 프롬프트)
### 목적
레슨 안에서 간단하게 글을 써보는 연습
**주의**: 본격적인 글쓰기는 `/write` 페이지의 Topic 사용. WritingPrompt는 배운 내용을 바로 적용해보는 **짧은 연습**용.
### 데이터 구조
```typescript
interface WritingPromptBlock {
prompt: string;
guideLines: string[];
minLength?: number; // 최소 글자 수
}
```
### 렌더링 방법
**컴포넌트**: `WritingPromptRenderer.tsx`
**UI 구성**:
1. 글쓰기 주제
2. 가이드라인 목록
3. 텍스트 에디터 (간단한 Textarea)
4. 글자 수 카운터
5. 제출 버튼
### 사용자 액션
1. 주제와 가이드라인 읽기
2. 텍스트 에디터에 글 작성
3. 최소 글자 수 도달 확인
4. "제출" 버튼 클릭
5. 완료 처리
**저장 위치**:
- LocalStorage 또는 Realtime DB에 임시 저장
- 나중에 `/write` 페이지에서 이어 쓸 수 있도록 연동 가능 (선택)
### UI 예시
**초기 상태**:
```
┌─────────────────────────────────┐
│ ✍️ 글쓰기 │
│ │
│ 소리가 가득한 아침 │
│ │
│ 가이드라인: │
│ • 오늘 아침에 들은 소리들을 │
│ 떠올려보세요 │
│ • 의성어를 3개 이상 사용해보세요 │
│ │
│ ┌─────────────────────────────┐ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ └─────────────────────────────┘ │
│ │
│ 0 / 100자 (최소 100자) │
│ │
│ [제출] (비활성화) │
└─────────────────────────────────┘
```
**작성 중**:
```
┌─────────────────────────────────┐
│ ✍️ 글쓰기 │
│ │
│ 소리가 가득한 아침 │
│ │
│ 가이드라인: │
│ • 오늘 아침에 들은 소리들을... │
│ • 의성어를 3개 이상... │
│ │
│ ┌─────────────────────────────┐ │
│ │ 아침에 일어나니 새들이 │ │
│ │ 짹짹거리는 소리가 들렸다. │ │
│ │ 부엌에서는 엄마가 후라이팬에 │ │
│ │ 계란을 지글지글 굽고 계셨다. │ │
│ │ │ │
│ └─────────────────────────────┘ │
│ │
│ 78 / 100자 (거의 다 왔어요!) │
│ │
│ [제출] (비활성화) │
└─────────────────────────────────┘
```
**제출 가능**:
```
┌─────────────────────────────────┐
│ ✍️ 글쓰기 │
│ │
│ 소리가 가득한 아침 │
│ │
│ ┌─────────────────────────────┐ │
│ │ 아침에 일어나니 새들이 │ │
│ │ 짹짹거리는 소리가 들렸다... │ │
│ │ (글 내용 계속) │ │
│ └─────────────────────────────┘ │
│ │
│ 125 / 100자 ✅ │
│ │
│ [제출] │
└─────────────────────────────────┘
```
---
## 레슨 전체 플로우
### LessonViewer 컴포넌트 구조
```typescript
<LessonViewer lessonId={lessonId}>
{/* 헤더 */}
<LessonHeader
title="1강. 소리를 글로 표현하기"
progress={currentIndex / totalContents}
/>
{/* 현재 콘텐츠 블록 */}
<ContentRenderer
content={lesson.contents[currentIndex]}
onComplete={() => handleComplete()}
/>
{/* 네비게이션 */}
<LessonNavigation
onNext={() => nextContent()}
onPrevious={() => previousContent()}
canNext={currentContentCompleted}
isLastContent={currentIndex === totalContents - 1}
/>
</LessonViewer>
```
### 진행 상태 관리
**로컬 상태**:
- `currentIndex`: 현재 보고 있는 콘텐츠 인덱스
- `completedBlocks`: 완료한 블록들의 Set
**서버 저장** (나중에 구현):
- UserLessonProgress 모델
- 레슨 완료 시 경험치 및 스티커 보상
### 완료 조건
| 타입 | 완료 조건 |
|------|-----------|
| Theory | 자동 완료 (읽기만) |
| Quiz | 정답 확인 후 |
| Mission | 모든 항목 체크 |
| WritingPrompt | 최소 글자 수 이상 + 제출 |
---
## 구현 우선순위
### Phase 1: 기본 렌더러
1. TheoryRenderer (가장 간단)
2. MissionRenderer
3. QuizRenderer (주관식만)
4. WritingPromptRenderer
### Phase 2: 고급 기능
1. QuizRenderer 객관식 지원
2. 마크다운 렌더링 (react-markdown)
3. 진행 상태 저장
### Phase 3: UX 개선
1. 애니메이션 (콘텐츠 전환)
2. 로딩 상태
3. 에러 처리
---
## 디자인 토큰
**색상**:
- Theory: `blue` (파란색)
- Quiz: `purple` (보라색)
- Mission: `orange` (주황색)
- WritingPrompt: `green` (초록색)
**아이콘**:
- Theory: `HiOutlineDocumentText`
- Quiz: `HiOutlineQuestionMarkCircle`
- Mission: `HiOutlineFlag`
- WritingPrompt: `HiOutlinePencilAlt`
---
## 참고 사항
### 접근성
- 모든 인터랙티브 요소에 키보드 접근 가능
- 스크린 리더 지원 (aria-label)
- 명확한 포커스 표시
### 반응형
- 모바일 우선 디자인
- 터치 친화적 버튼 크기 (최소 44x44px)
### 성능
- 마크다운 렌더링은 메모이제이션
- 긴 콘텐츠는 가상화 고려
---
© 2024 BlueNovaLab. All rights reserved.