commit 09db74ff4c8e750e72b29689b31b26d7a991cc8b Author: baekryang Date: Mon Nov 10 10:04:29 2025 +0900 init diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md new file mode 100644 index 0000000..c58d7df --- /dev/null +++ b/PROJECT_STRUCTURE.md @@ -0,0 +1,407 @@ +# 라온누리 - 프로젝트 구조 + +> 최종 업데이트: 2025-11-07 (아키텍처 단순화 - Users 컬렉션 전환) + +초등학생을 위한 창작 글쓰기 교육 플랫폼 + +--- + +## 페이지 구조 + +### ✅ 구현 완료 + +| 페이지 | 경로 | 설명 | 주요 기능 | +|-------|------|------|---------| +| **랜딩 페이지** | `/` | 서비스 소개 및 홍보 (비로그인 전용) | Hero, Features, How It Works, CTA, Footer
로그인 시 `/home`으로 자동 리다이렉트 | +| **유저 홈** | `/home` | 인증된 사용자 대시보드 | 환영 메시지, 빠른 시작 대시보드, 최근 활동
비로그인 시 `/`로 자동 리다이렉트
정식 계정은 "내 팀" 카드 추가 표시 | +| **글쓰기** | `/write` | Tiptap 기반 순수 텍스트 에디터 + 주제 선택 | 주제 선택 (자유 주제/개인 주제)
제목 입력 (Editable), 순수 텍스트 에디터 (포맷팅 없음)
실시간 글자수/단어수, 자동 저장 (LocalStorage 3초마다)
템플릿 미리채우기 (제목/내용), Firestore 저장
비로그인도 접근 가능 (저장 시 로그인 유도) | +| **테스트** | `/test` | 팀 코드 시스템 테스트 페이지 | 팀 코드 생성/검증 테스트
팀/학생 생성 테스트
학생 로그인 테스트
authStore 상태 확인 | +| **팀 목록** | `/team` | 내가 만든 팀 목록 (정식 계정 전용) | 팀 카드 그리드, 팀 정보 (코드, 멤버 수, 보안 설정)
"새 팀 만들기" 버튼 | +| **팀 생성** | `/team/create` | 새 팀 만들기 (정식 계정 전용) | 팀 이름 입력, 팀 코드 자동 생성
보안 모드 선택 (simple/normal/open)
상세 설정 (PIN, 자유 가입)
생성 후 `/team/[teamId]/manage`로 이동 | +| **팀 멤버 페이지** | `/team/[teamId]` | 팀 멤버용 페이지 (멤버/소유자 모두 접근) | 팀 정보, 팀 코드 복사, 멤버 목록
소유자는 "팀 관리" 버튼 표시
팀 코드 로그인 후 기본 이동 페이지 | +| **팀 관리** | `/team/[teamId]/manage` | 팀 관리 페이지 (소유자 전용) | 팀 정보 및 코드, 보안 설정 표시
**팀 주제 관리 (생성/삭제)**
멤버 목록 및 관리
"멤버 페이지 보기" 버튼 | + +### 🚧 구현 예정 + +| 페이지 | 경로 | 설명 | 상태 | +|-------|------|------|------| +| **학습하기** | `/learn` | 레슨/코스 학습 | ❌ 미구현 (Navbar 링크만 존재) | +| **스티커** | `/stickers` | 스티커 컬렉션 | ❌ 미구현 (Navbar 링크만 존재) | +| **마이페이지** | Dialog (별도 페이지 없음) | 사용자 대시보드 다이얼로그 | 🔜 계획 중 (UserProfileButton에서 열림) | + +--- + +## 컴포넌트 구조 + +### 📁 `src/components/auth/` - 인증 관련 + +| 컴포넌트 | 파일명 | 설명 | 상태 | +|---------|--------|------|------| +| **AuthInitializer** | `AuthInitializer.tsx` | Firebase 인증 상태 초기화 | ✅ 완료 | +| **LoginDialog** | `LoginDialog.tsx` | 로그인/회원가입 다이얼로그 (일반/팀 코드 모드 전환) | ✅ 완료 | +| **StudentLoginFlow** | `StudentLoginFlow.tsx` | 팀 코드 학생 로그인 플로우 (3단계) | ✅ 완료 | +| **LoginForm** | `LoginForm.tsx` | 로그인 폼 컴포넌트 | ✅ 완료 | +| **SignupForm** | `SignupForm.tsx` | 회원가입 폼 컴포넌트 | ✅ 완료 | +| **SocialLoginButton** | `SocialLoginButton.tsx` | 소셜 로그인 버튼 | ✅ 완료 | +| **UserProfileButton** | `UserProfileButton.tsx` | 사용자 프로필/메뉴 버튼 | ✅ 완료 | + +**주요 기능**: +- ✅ 이메일/비밀번호 로그인 +- ✅ 이메일/비밀번호 회원가입 + - ✅ 이름, 이메일, 비밀번호, 비밀번호 확인 입력 + - ✅ 실시간 비밀번호 강도 게이지 + - ✅ HIBP API 연동 (유출된 비밀번호 차단) + - ✅ 폼 검증 및 에러 애니메이션 +- ✅ Google OAuth 로그인 +- ✅ 팀 코드 기반 사용자 로그인 (Anonymous Auth - 단순화됨) + - ✅ 한글 팀 코드 ("춤추는 파란 사자" 형식) + - ✅ 사용자 이름 입력 (2단계) + - ❌ PIN 인증 제거 (복잡도 감소) + - ✅ UID 기반 통합 인증 (currentStudent 제거) + - ✅ 팀별 닉네임 시스템 +- ✅ 로그인/회원가입 페이드 전환 애니메이션 +- ✅ Anonymous ↔ 정식 계정 연결 (UID 유지) +- 🔜 네이버 로그인 (준비 중) +- 🔜 카카오 로그인 (준비 중) + +--- + +### 📁 `src/components/landing/` - 랜딩 페이지 + +| 컴포넌트 | 파일명 | 설명 | 상태 | +|---------|--------|------|------| +| **FeatureCard** | `FeatureCard.tsx` | 특징 카드 | ✅ 완료 | +| **StepCard** | `StepCard.tsx` | 단계 카드 | ✅ 완료 | +| **SectionHeader** | `SectionHeader.tsx` | 섹션 헤더 | ✅ 완료 | + +--- + +### 📁 `src/components/navigation/` - 네비게이션 + +| 컴포넌트 | 파일명 | 설명 | 상태 | +|---------|--------|------|------| +| **Navbar** | `Navbar.tsx` | 상단 네비게이션 바 | ✅ 완료 | + +**네비게이션 링크**: +- 홈 (`/` 또는 `/home`) - 인증 상태에 따라 동적 변경 + - 비로그인: `/` (랜딩 페이지) + - 로그인: `/home` (유저 대시보드) +- 글쓰기 (`/write`) - 미구현 +- 학습하기 (`/learn`) - 미구현 +- 스티커 (`/stickers`) - 미구현 + +--- + +### 📁 `src/components/ui/` - UI 기본 컴포넌트 + +| 컴포넌트 | 파일명 | 설명 | 상태 | +|---------|--------|------|------| +| **Provider** | `provider.tsx` | Chakra UI Provider | ✅ 완료 | +| **ColorMode** | `color-mode.tsx` | 다크모드 토글 | ✅ 완료 | +| **Toaster** | `toaster.tsx` | 토스트 알림 | ✅ 완료 | +| **Tooltip** | `tooltip.tsx` | 툴팁 | ✅ 완료 | + +--- + +### 📁 `src/components/seo/` - SEO + +| 컴포넌트 | 파일명 | 설명 | 상태 | +|---------|--------|------|------| +| **StructuredData** | `StructuredData.tsx` | JSON-LD 구조화 데이터 | ✅ 완료 | + +--- + +### 📁 `src/components/writing/` - 글쓰기 에디터 + +| 컴포넌트 | 파일명 | 설명 | 상태 | +|---------|--------|------|------| +| **WritingEditor** | `WritingEditor.tsx` | Tiptap 기반 순수 텍스트 에디터 | ✅ 완료 | +| **TopicSelector** | `TopicSelector.tsx` | 주제 선택 드롭다운, 팀/개인 주제 배지 표시 | ✅ 완료 | +| **CreateTopicDialog** | `CreateTopicDialog.tsx` | 개인 주제 생성 다이얼로그 (태그 입력 UI) | ✅ 완료 | +| **CreateTeamTopicDialog** | `CreateTeamTopicDialog.tsx` | 팀 주제 생성 다이얼로그 (템플릿 지원) | ✅ 완료 | + +**주요 기능**: +- ✅ 순수 텍스트 입력 (포맷팅 없음) +- ✅ 초등학생 친화적 단순 인터페이스 +- ✅ 실시간 글자 수/단어 수 카운터 +- ✅ 자동 저장 (LocalStorage) +- ✅ 주제 선택 (자유 주제/팀 주제/개인 주제) +- ✅ 주제 정보 표시 (설명, 키워드, 예시 질문) +- ✅ 팀/개인 주제 배지 표시 (teal/pink) +- ✅ 템플릿 미리채우기 (제목/내용) +- ✅ 개인 주제 생성 (카테고리, 난이도, 키워드, 템플릿 설정) +- ✅ 팀 주제 생성 (팀 소유자 전용, 팀 관리 페이지) +- ✅ 태그 입력 필드 UI (제목 템플릿: 방향키 네비게이션, Backspace/Delete 삭제) + +--- + +### 📁 `src/components/team/` - 팀 관리 + +| 컴포넌트 | 파일명 | 설명 | 상태 | +|---------|--------|------|------| +| **TeamTopicManager** | `TeamTopicManager.tsx` | 팀 주제 목록 및 생성/삭제 UI | ✅ 완료 | + +--- + +### 📁 예정된 컴포넌트 디렉토리 + +| 디렉토리 | 설명 | 컴포넌트 | +|---------|------|--------------| +| `lesson/` | 학습 관련 | LessonCard, LessonContent, QuizSection, ProgressBar, LevelBadge | +| `sticker/` | 스티커 관련 | StickerGrid, StickerCard, StickerDetail, StickerStats, LockedSticker | +| `level/` | 레벨 시스템 | LevelBadge, LevelUpModal, ExperienceBar | +| `profile/` | **프로필 Dialog** | **ProfileDialog (탭: 내정보, 내글, 통계, 설정)**, ProfileCard, StatsWidget, WritingHistory | +| `notification/` | 알림 시스템 | NotificationBell, NotificationList, NotificationItem | +| `admin/` | 관리자 | TopicManager, UserManager, ContentEditor | + +**Notes**: +- `profile/` 디렉토리는 Dialog 기반으로 구현됩니다. UserProfileButton 클릭 시 ProfileDialog가 열립니다. + +--- + +## 비즈니스 로직 레이어 + +### 📁 `src/managers/` - Manager 패턴 (API 호출 + 클라이언트 캐싱) + +| Manager | 파일명 | 설명 | 상태 | +|---------|--------|------|------| +| **ManagerBase** | `ManagerBase.ts` | 공통 기능 (authenticatedFetch, API 호출, 캐싱) | ✅ 완료 | +| **TeamManager** | `TeamManager.ts` | 팀 관련 API 호출 (생성, 조회, 수정, 삭제, 멤버 관리) | ✅ 완료 | +| **UserManager** | `UserManager.ts` | 사용자 관련 API 호출 (생성, 조회, 수정, 닉네임 관리) **[NEW]** | ✅ 완료 | +| ~~**StudentManager**~~ | ~~`StudentManager.ts`~~ | ~~학생 관련 API 호출~~ | ⚠️ Deprecated (UserManager로 대체) | +| **WritingManager** | `WritingManager.ts` | 글쓰기 관련 비즈니스 로직 (CRUD, 통계) | ✅ 완료 | +| **TopicManager** | `TopicManager.ts` | 주제 관련 비즈니스 로직 (CRUD, 템플릿 처리) | ✅ 완료 | +| **index.ts** | `index.ts` | 모든 매니저 export | ✅ 완료 | +| **LevelManager** | `LevelManager.ts` | 레벨/경험치 관리 | ❌ 미구현 | +| **StickerManager** | `StickerManager.ts` | 스티커 획득/관리 | ❌ 미구현 | + +**주요 기능**: +- ✅ **싱글톤 패턴**으로 전역 인스턴스 관리 +- ✅ **HTTP API 호출**: Next.js API Routes로 서버 로직 분리 +- ✅ **클라이언트 사이드 캐싱**: in-memory 캐싱 (TTL 기반) +- ✅ **자동 캐시 무효화**: 변경 작업 시 관련 캐시 삭제 +- ✅ **인증 자동 처리**: ID Token 자동 헤더 추가 +- ✅ **타입 안전성**: Request/Response 타입 완전 정의 +- ✅ **비즈니스 로직과 UI 레이어 분리** + +### 📁 `src/services/` - 데이터 레이어 (일부 deprecated) + +| 서비스 | 파일명 | 설명 | 상태 | +|-------|--------|------|------| +| **Firebase Auth** | `firebaseAuth.ts` | 인증 서비스 (로그인, 회원가입, 소셜, Anonymous, 계정 연결) | ✅ 완료 (단순화됨) | +| **Firestore** | `firestore.ts` | Firestore CRUD 헬퍼 함수 (WritingManager에서 사용) | ✅ 완료 | +| ~~**Team Service**~~ | ~~`teamService.ts`~~ | ~~팀 CRUD, 팀 코드 생성~~ | ⚠️ Deprecated (TeamManager로 이동) | +| ~~**Student Service**~~ | ~~`studentService.ts`~~ | ~~학생 CRUD, PIN 해시/검증~~ | ⚠️ Deprecated (UserManager로 대체) | +| **Level System** | `levelSystem.ts` | 레벨/경험치 계산 로직 | ❌ 미구현 | +| **Sticker System** | `stickerSystem.ts` | 스티커 획득 조건 엔진 | ❌ 미구현 | + +**마이그레이션 완료**: +- ✅ **teamService.ts** → **TeamManager** (API 호출 방식) +- ✅ **studentService.ts** → **UserManager** (UID 기반 단순화) +- ✅ **students 컬렉션** → **users 컬렉션** (UID 기반) +- ✅ Firestore 직접 접근 → HTTP API 호출로 전환 +- ✅ PIN 기능 제거, 팀별 닉네임 시스템 추가 + +### 📁 `src/types/` - 타입 정의 + +| 타입 | 파일명 | 설명 | 상태 | +|------|--------|------|------| +| **Team 타입** | `team.ts` | 팀 데이터 모델 (memberUids, members Map), TeamSecurityMode Enum (simple/open) | ✅ 완료 | +| **User 타입** | `user.ts` | 사용자 데이터 모델 (nicknames Map) **[NEW]** | ✅ 완료 | +| ~~**Student 타입**~~ | ~~`student.ts`~~ | ~~학생 데이터 모델~~ | ⚠️ Deprecated (user.ts로 대체) | +| **Topic 타입** | `topic.ts` | 주제 데이터 모델, TopicCategory/Difficulty/OwnerType Enum, 팀 주제 유틸 함수 | ✅ 완료 | +| **API 공통 타입** | `api.ts` | ApiResponse, ApiError, HttpMethod Enum | ✅ 완료 | +| **Team API 타입** | `api/team.ts` | 팀 API Request/Response (add/removeMember 추가, requirePin 제거) | ✅ 완료 | +| **User API 타입** | `api/user.ts` | 사용자 API Request/Response (닉네임 관리 포함) **[NEW]** | ✅ 완료 | +| ~~**Student API 타입**~~ | ~~`api/student.ts`~~ | ~~학생 API Request/Response~~ | ⚠️ Deprecated (api/user.ts로 대체) | +| **Topic API 타입** | `api/topic.ts` | 주제 API Request/Response (9개 엔드포인트, 팀 주제 포함) | ✅ 완료 | + +--- + +## 상태 관리 (Zustand) + +### 📁 `src/store/` + +| 스토어 | 파일명 | 설명 | 상태 | +|-------|--------|------|------| +| **Auth Store** | `authStore.ts` | 인증 상태 및 로그인 다이얼로그 (단순화됨) | ✅ 완료 | +| **User Progress Store** | `userProgressStore.ts` | 사용자 진행 상황 (레벨, XP) | ❌ 미구현 | +| **Notification Store** | `notificationStore.ts` | 알림 상태 | ❌ 미구현 | + +**Auth Store 아키텍처 (2025-11-07 단순화)**: +- ✅ **user** - Firebase Auth 기반 통합 사용자 (익명 + 정식 계정) +- ✅ **isAuthenticated** - 로그인 여부 (익명 포함) +- ✅ **user.isAnonymous** - 익명/정식 계정 구분 +- ✅ **loginAsUser()** - 팀 코드 로그인 (PIN 제거) +- ✅ **linkWithEmail()** - 이메일 계정 연결 +- ✅ **linkWithGoogle()** - Google 계정 연결 +- ❌ ~~currentStudent~~, ~~ownedStudents~~, ~~switchStudent()~~ 제거 (복잡도 감소) + +--- + +## 설정 파일 + +### 📁 `src/config/` + +| 파일 | 설명 | 상태 | +|------|------|------| +| `firebase.ts` | Firebase 초기화 및 설정 | ✅ 완료 | +| `site.ts` | 사이트 메타데이터 및 설정 | ✅ 완료 | + +--- + +## 디렉토리 구조 요약 + +``` +src/ +├── app/ # Next.js App Router +│ ├── layout.tsx # 루트 레이아웃 +│ ├── page.tsx # 랜딩 페이지 (/) - 로그인 시 /home 리다이렉트 +│ ├── home/ +│ │ └── page.tsx # ✅ 유저 홈 페이지 (/home) +│ ├── write/ +│ │ └── page.tsx # ✅ 글쓰기 페이지 (/write) +│ ├── test/ +│ │ └── page.tsx # ✅ 테스트 페이지 (/test) - 팀 코드 시스템 테스트 +│ ├── globals.css +│ ├── favicon.ico +│ ├── team/ # ✅ 팀 관리 페이지들 +│ │ ├── page.tsx # ✅ 팀 목록 (/team) +│ │ ├── create/ +│ │ │ └── page.tsx # ✅ 팀 생성 (/team/create) +│ │ └── [teamId]/ +│ │ ├── page.tsx # ✅ 팀 멤버 페이지 (/team/[teamId]) +│ │ └── manage/ +│ │ └── page.tsx # ✅ 팀 관리 페이지 (/team/[teamId]/manage) +│ └── [미구현 페이지들] +│ ├── learn/ # 학습 페이지 +│ ├── stickers/ # 스티커 페이지 +│ └── admin/ # 관리자 +│ +├── components/ # React 컴포넌트 +│ ├── auth/ # ✅ 인증 (로그인, 프로필, 팀 코드) +│ ├── landing/ # ✅ 랜딩 페이지 카드들 +│ ├── navigation/ # ✅ 네비게이션 바 +│ ├── seo/ # ✅ SEO 컴포넌트 +│ ├── ui/ # ✅ Chakra UI 기본 +│ ├── writing/ # ✅ 글쓰기 에디터 +│ │ ├── WritingEditor.tsx # 순수 텍스트 에디터 (포맷팅 없음) +│ │ ├── TopicSelector.tsx # ✅ 주제 선택 컴포넌트 (팀/개인 배지) +│ │ ├── CreateTopicDialog.tsx # ✅ 개인 주제 생성 +│ │ └── CreateTeamTopicDialog.tsx # ✅ 팀 주제 생성 +│ ├── team/ # ✅ 팀 관련 +│ │ └── TeamTopicManager.tsx # ✅ 팀 주제 관리 +│ └── [미구현] +│ ├── lesson/ # 학습 컴포넌트 +│ ├── sticker/ # 스티커 컴포넌트 +│ ├── level/ # 레벨 시스템 +│ ├── profile/ # 프로필 컴포넌트 +│ ├── notification/ # 알림 컴포넌트 +│ └── admin/ # 관리자 컴포넌트 +│ +├── config/ # ✅ 설정 +│ ├── firebase.ts +│ └── site.ts +│ +├── managers/ # ✅ 비즈니스 로직 (Manager 패턴) +│ ├── ManagerBase.ts # 기본 Manager 클래스 +│ ├── WritingManager.ts # 글쓰기 관리 +│ ├── TopicManager.ts # ✅ 주제 관리 +│ └── [미구현] +│ ├── LevelManager.ts +│ └── StickerManager.ts +│ +├── data/ # ✅ 정적 데이터 +│ └── classCodeWords.ts # ✅ 한글 팀 코드 단어 (형용사, 색상, 동물) +│ +├── services/ # 데이터 레이어 (Firestore/API 호출) +│ ├── firebaseAuth.ts # ✅ 인증 (이메일, Google, Anonymous) +│ ├── classroomService.ts # ✅ 클래스룸 CRUD +│ ├── studentService.ts # ✅ 학생 CRUD, PIN 관리 +│ ├── firestore.ts # ✅ Firestore CRUD +│ └── [미구현] +│ ├── levelSystem.ts +│ └── stickerSystem.ts +│ +├── store/ # Zustand 상태 관리 +│ ├── authStore.ts # ✅ 인증 상태 (currentStudent 중심) +│ └── [미구현] +│ ├── userProgressStore.ts +│ └── notificationStore.ts +│ +├── theme/ # ✅ Chakra UI 테마 +│ └── system.ts +│ +├── types/ # TypeScript 타입 +│ ├── topic.ts # ✅ 주제 관련 타입 (Enum + 유틸 함수) +│ ├── team.ts # ✅ 팀 관련 타입 (Enum) +│ ├── student.ts # ✅ 학생 타입 +│ ├── api/ # ✅ API Request/Response 타입 +│ │ ├── topic.ts # ✅ 주제 API (팀/개인 주제) +│ │ ├── team.ts # ✅ 팀 API +│ │ └── student.ts # ✅ 학생 API +│ └── [미구현] +│ +└── utils/ # 유틸리티 함수 + ├── passwordStrength.ts # ✅ 비밀번호 강도 계산 + ├── passwordSecurity.ts # ✅ HIBP API 연동 + └── classCodeGenerator.ts # ✅ 팀 코드 생성/검증/정규화 +``` + +--- + +## 다음 구현 단계 + +### Phase 1: 핵심 기능 (현재) +1. ✅ 프로젝트 설정 및 인프라 +2. ✅ 인증 시스템 (로그인) +3. ✅ 랜딩 페이지 +4. ✅ 회원가입 기능 (페이드 전환 + HIBP 보안 검증) +5. ✅ 인증 기반 라우팅 (`/`와 `/home` 분리) +6. ✅ 글쓰기 페이지 (Tiptap 에디터, LocalStorage 자동 저장, Firestore 저장) +7. ✅ 주제 선택 및 관리 (자유/개인 주제, 템플릿) +8. ✅ **팀 코드 시스템** (완료) + - ✅ 한글 팀 코드 생성 ("춤추는 파란 사자") + - ✅ Anonymous Auth 학생 로그인 + - ✅ 팀/학생 관리 서비스 (ownerId 기반) + - ✅ PIN 인증 (SHA-256) + - ✅ 정식 계정 연결 (linkWithCredential) + - ✅ currentStudent 중심 authStore 재설계 + - ✅ 학생 로그인 UI (3단계 플로우, 이름 입력, 완료 화면) + - ✅ 팀 관리 UI (목록/생성/멤버 페이지/관리 페이지) + - ✅ 용어 변경: "클래스" → "팀", "교사" → "소유자" +9. 🚧 **다음**: 내가 쓴 글 목록 + +### Phase 2: 팀 코드 UI 완성 +- 학생 로그인 UI (팀 코드 입력 → 이름 선택 → PIN) +- 팀 관리 (팀 생성, 멤버 관리) +- LoginDialog에 학생 탭 추가 + +### Phase 3: 학습 시스템 +- `/learn` 학습 페이지 +- 레슨 콘텐츠 및 진행 상황 + +### Phase 4: 게임화 +- 레벨업 시스템 +- `/stickers` 스티커 페이지 + +### Phase 5: 사용자 경험 +- ProfileDialog 마이페이지 (Dialog 형식) +- 검색 및 알림 + +### Phase 6: 고급 기능 +- 부모님 모드 (계정 연결) +- 관리자 패널 + +--- + +## 관련 문서 + +- [TECH_STACK.md](./TECH_STACK.md) - 기술 스택 및 개발 환경 설정 +- [DATA_MODELS.md](./DATA_MODELS.md) - 데이터베이스 스키마 및 타입 정의 +- [ROADMAP.md](./ROADMAP.md) - 개발 우선순위 및 로드맵 +- [CLAUDE.md](./CLAUDE.md) - Claude Code 작업 가이드 + +--- + +© 2024 BlueNovaLab. All rights reserved. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d22a62 --- /dev/null +++ b/README.md @@ -0,0 +1,234 @@ +# 라온누리 (Raonnuri) + +초등학생을 위한 창의적 글쓰기 교육 플랫폼 + +> 최종 업데이트: 2025-11-07 + +## 프로젝트 소개 + +라온누리는 초등학생들이 재미있게 글쓰기를 배울 수 있는 한국어 교육 플랫폼입니다. + +### 주요 특징 +- **팀 코드 시스템**: 초등 저학년도 쉽게 로그인 ("춤추는 파란 사자") +- **개인 맞춤 주제**: 자유 주제, 그룹 주제, 개인 주제 지원 +- **글쓰기 에디터**: 초등학생 친화적인 순수 텍스트 에디터 +- 🔜 **레벨업 시스템**: 경험치와 스티커 보상 (예정) +- 🔜 **학습 커리큘럼**: 체계적인 레슨 시스템 (예정) + +## 기술 스택 + +- **Framework**: Next.js 16 (App Router) with React 19 +- **언어**: TypeScript +- **UI Library**: Chakra UI v3 +- **인증**: Firebase Authentication + - Email/Password, Google OAuth (정식 계정) + - Anonymous Auth (학생 팀 코드 로그인) +- **데이터베이스**: Firestore +- **상태 관리**: Zustand +- **비즈니스 로직**: Manager 패턴 (API 호출 + 클라이언트 캐싱) +- **스타일링**: Chakra UI + Emotion +- **애니메이션**: Framer Motion +- 🔜 **캐싱**: Redis (예정) + +## 시작하기 + +### 필수 요구사항 + +- Node.js 18.17 이상 +- npm, yarn, pnpm, 또는 bun + +### 설치 + +```bash +npm install +``` + +### 개발 서버 실행 + +```bash +npm run dev +``` + +개발 서버는 [http://localhost:3001](http://localhost:3001)에서 실행됩니다. + +> **참고**: 이 프로젝트는 React Compiler를 사용하므로 webpack 모드로 실행됩니다 (Turbopack 아님). + +### 기타 명령어 + +```bash +npm run build # 프로덕션 빌드 +npm start # 프로덕션 서버 실행 (포트 3001) +npm run lint # ESLint 실행 +``` + +## 프로젝트 구조 + +``` +src/ +├── app/ # Next.js App Router +│ ├── layout.tsx # 루트 레이아웃 +│ ├── page.tsx # 랜딩 페이지 +│ ├── home/ # 유저 대시보드 +│ ├── write/ # 글쓰기 페이지 +│ └── team/ # 팀 관련 페이지 (목록, 생성, 상세, 관리) +├── components/ +│ ├── auth/ # 인증 (로그인, 회원가입, 학생 로그인) +│ ├── writing/ # 글쓰기 에디터, 주제 선택 +│ ├── navigation/ # 네비게이션 바 +│ └── ui/ # Chakra UI 기본 컴포넌트 +├── managers/ # ✅ 비즈니스 로직 (API + 캐싱) +│ ├── ManagerBase.ts # 공통 기능 (authenticatedFetch, caching) +│ ├── TeamManager.ts # 팀 관련 API 호출 +│ ├── StudentManager.ts # 학생 관련 API 호출 +│ ├── WritingManager.ts # 글쓰기 API 호출 +│ └── TopicManager.ts # 주제 API 호출 +├── types/ # TypeScript 타입 +│ ├── team.ts, student.ts, writing.ts, topic.ts +│ └── api/ # API Request/Response 타입 +├── config/ # Firebase 설정 +├── services/ # Firebase Auth, Firestore +├── store/ # Zustand (인증 상태) +└── theme/ # Chakra UI 커스텀 테마 +``` + +## 주요 기능 + +### ✅ 인증 시스템 + +**정식 계정** (학부모/고학년): +- 이메일/비밀번호 로그인 및 회원가입 +- Google OAuth 소셜 로그인 +- 비밀번호 강도 체크 + HIBP API 유출 확인 + +**학생 로그인** (초등 저학년): +- 팀 코드 기반 로그인 ("춤추는 파란 사자") +- Anonymous Auth 사용 +- 이름만 입력 또는 이름 + PIN +- 정식 계정 연결 가능 (선택적) + +### ✅ 팀 관리 시스템 + +- 팀 생성 (누구나 가능) +- 한글 팀 코드 자동 생성 (10만 가지 조합) +- 보안 모드 3종: simple, normal, open +- 팀원 관리 (이름 수정, 강퇴) +- 멤버 페이지 (팀 정보 및 멤버 목록) + +### ✅ 글쓰기 기능 + +- Tiptap 순수 텍스트 에디터 (포맷팅 없음) +- 주제 선택 (자유/그룹/개인) +- 개인 주제 생성 (템플릿 지원) +- 실시간 글자수/단어수 카운터 +- LocalStorage 자동 저장 + +### ✅ UI/UX + +- 다크 모드 지원 +- 반응형 디자인 (모바일 우선) +- Chakra UI v3 커스텀 테마 +- Framer Motion 애니메이션 +- 초등학생 친화적 디자인 + +### ✅ 아키텍처 + +- Manager 패턴 (비즈니스 로직 레이어) +- API 추상화 (HTTP 호출) +- 클라이언트 사이드 캐싱 (TTL 기반) +- 타입 안전성 (완전한 타입 정의) +- 35개 API 엔드포인트 준비 완료 + +## 개발자 가이드 + +상세한 개발 가이드는 다음 문서를 참고하세요: +- [CLAUDE.md](./CLAUDE.md) - Claude Code 개발 가이드 +- [API_SPEC.md](./API_SPEC.md) - API 명세서 +- [TECH_STACK.md](./TECH_STACK.md) - 기술 스택 상세 +- [PROJECT_STRUCTURE.md](./PROJECT_STRUCTURE.md) - 프로젝트 구조 상세 +- [DATA_MODELS.md](./DATA_MODELS.md) - 데이터 모델 + +### Manager 패턴 사용 + +**❌ 서비스 직접 사용 금지**: +```typescript +// Bad - deprecated +import { getTeamsByOwner } from "@/services/teamService"; +``` + +**✅ Manager 사용**: +```typescript +// Good +import { teamManager, studentManager } from "@/managers"; + +// 팀 목록 (1분 캐싱) +const teams = await teamManager.getMyTeams(); + +// 학생 목록 (30초 캐싱) +const students = await studentManager.getStudentsByTeam(teamId); + +// 팀 생성 (캐시 자동 무효화) +const teamId = await teamManager.createTeam({ + name: "우리반", + code: "춤추는파란사자", + securityMode: "simple", + requirePin: false, + allowAnonymousJoin: true +}); +``` + +### 보호된 페이지 만들기 + +```tsx +"use client"; +import { useAuthStore } from "@/store/authStore"; +import { useRouter } from "next/navigation"; +import { useEffect } from "react"; + +export default function ProtectedPage() { + const { isAuthenticated, isLoading } = useAuthStore(); + const router = useRouter(); + + useEffect(() => { + if (!isLoading && !isAuthenticated) { + router.push("/"); + } + }, [isAuthenticated, isLoading, router]); + + if (isLoading) return
로딩 중...
; + if (!isAuthenticated) return null; + + return
보호된 콘텐츠
; +} +``` + +## 환경 변수 + +Firebase 설정을 위해 `.env.local` 파일을 생성하세요: + +```bash +# Firebase 설정 (필수) +NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key +NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_auth_domain +NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_id +NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_storage_bucket +NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id +NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id + +# 사이트 URL (프로덕션) +NEXT_PUBLIC_URL=https://raonnuri.com + +# API Base URL (선택적, 기본값: /api) +NEXT_PUBLIC_API_URL=/api +``` + +## 라이선스 + +MIT License + +## 기여하기 + +기여는 언제나 환영합니다! Pull Request를 보내주세요. + +--- + +**라온누리** - 즐거운 글쓰기, 밝은 배움터 \ No newline at end of file diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..f5cb363 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,355 @@ +# 라온누리 - 개발 로드맵 + +> 최종 업데이트: 2025-11-07 (그룹 기능 제거, 팀 시스템 개선) + +초등학생을 위한 창작 글쓰기 교육 플랫폼 개발 계획 + +--- + +## 현재 개발 단계 + +**Phase 1: 핵심 기능** (진행 중 - 95% 완료) + +--- + +## Phase 1: 핵심 기능 (현재) + +**목표**: 사용자가 가입하고 글을 작성할 수 있는 기본 기능 완성 + +### ✅ 완료된 작업 + +| 항목 | 설명 | 완료일 | +|-----|------|-------| +| 프로젝트 설정 | Next.js 16, React 19, TypeScript 설정 | 2025-10-25 | +| Firebase 연동 | Firebase Auth 설정 및 초기화 | 2025-10-26 | +| 로그인 기능 | 이메일/비밀번호 로그인 | 2025-10-27 | +| Google OAuth | Google 소셜 로그인 | 2025-10-27 | +| 상태 관리 | Zustand 인증 스토어 구현 | 2025-10-27 | +| 랜딩 페이지 | Hero, Features, How It Works, CTA 섹션 | 2025-10-28 | +| 네비게이션 | Navbar 및 다크모드 지원 | 2025-10-28 | +| SEO 최적화 | 메타데이터, OpenGraph, StructuredData | 2025-10-28 | +| 커스텀 테마 | Chakra UI v3 테마 시스템 구축 | 2025-10-28 | +| **회원가입 기능** | **LoginDialog 페이드 전환, 비밀번호 강도 게이지, HIBP API 연동** | **2025-10-28** | +| **인증 기반 라우팅** | **`/`와 `/home` 분리, 자동 리다이렉트 구현** | **2025-10-29** | +| **글쓰기 페이지** | **Tiptap 순수 텍스트 에디터 (포맷팅 없음), Editable 제목, LocalStorage 자동 저장, 하단 고정 버튼** | **2025-10-30** | +| **Firestore 연동** | **글 저장/불러오기/삭제 서비스 함수 구현** | **2025-10-30** | +| **Manager 패턴 도입** | **WritingManager 구현 (싱글톤), Firestore 호출 레이어 분리, Firebase Functions 전환 대비** | **2025-10-30** | +| **주제 선택 기능** | **TopicManager, TopicSelector 컴포넌트, 개인 주제 시스템, 템플릿 미리채우기 (제목/내용)** | **2025-10-30** | +| **개인 주제 생성 UI** | **CreateTopicDialog 구현, 태그 입력 필드 (키보드 네비게이션, 방향키 선택, Backspace/Delete 삭제)** | **2025-10-31** | +| **팀 코드 시스템 (백엔드)** | **한글 팀 코드 생성 ("춤추는 파란 사자"), Anonymous Auth, teamService/studentService, PIN SHA-256, currentStudent 중심 authStore 재설계, 정식 계정 연결 (linkWithCredential)** | **2025-11-06** | +| **팀 관리 UI** | **팀 목록(/team), 팀 생성(/team/create), 멤버 페이지(/team/[teamId]), 관리 페이지(/team/[teamId]/manage), 학생 로그인 플로우 개선 (이름 입력, 완료 화면)** | **2025-11-06** | +| **용어 변경** | **"클래스" → "팀", "교사" → "소유자", ownerId 기반 시스템, 누구나 팀 생성 가능** | **2025-11-06** | +| **Semantic 토큰 적용** | **브랜드 컬러 우선 사용, fg/bg/border 토큰, 다크 모드 대응** | **2025-11-06** | +| **학생 관리 기능** | **팀 관리 페이지에 학생별 관리 메뉴 (이름 수정, 강퇴), Menu + Dialog UI** | **2025-11-07** | +| **매니저 패턴 API 전환** | **TeamManager, StudentManager를 API 호출 방식으로 전환, Firestore 직접 호출 제거** | **2025-11-07** | +| **API 타입 시스템 구축** | **ApiResponse, HttpMethod Enum, Team/Student API 타입 정의 (23개 엔드포인트)** | **2025-11-07** | +| **클라이언트 캐싱 구현** | **BaseManager에 in-memory 캐싱 (TTL, 자동 무효화), callApiWithCache 메서드** | **2025-11-07** | +| **API 명세서 작성** | **API_SPEC.md 작성 (전체 엔드포인트, 권한, 캐싱 전략)** | **2025-11-07** | +| **타입 Enum화** | **TeamSecurityMode, TopicCategory/Difficulty/OwnerType을 Enum으로 전환** | **2025-11-07** | +| **팀 주제 시스템** | **팀 소유자가 팀 주제 생성 가능, ownerId = teamId 방식, TopicSelector에 배지 표시** | **2025-11-07** | +| **아키텍처 단순화** | **students → users 컬렉션 전환, PIN 제거, UID 기반 통합, 팀별 닉네임 시스템, authStore 단순화** | **2025-11-07** | +| **그룹 기능 완전 제거** | **Group 관련 타입/API/UI 제거, 팀(Team) 기능만 사용** | **2025-11-07** | +| **팀 목록 조회 개선** | **getMyTeams() 소유+참여 팀 모두 반환, getAllUserTeams() 서버 함수 추가, 중복 제거** | **2025-11-07** | +| **UserManager 개선** | **getUser() 404 시 null 반환, 팀 코드 로그인 시 신규 사용자 생성 플로우 개선** | **2025-11-07** | +| **TopicSelector UI 개선** | **드롭다운 메뉴에 팀/개인 주제 배지 표시, 선택 전에도 주제 타입 확인 가능** | **2025-11-07** | +| **T_ prefix 제거** | **팀 주제 ownerId에서 T_ prefix 제거, ownerId = teamId 직접 사용** | **2025-11-07** | + +### 🚧 진행 중 + +| 항목 | 설명 | 진행률 | +|-----|------|-------| + +### 📋 예정 작업 + +| 항목 | 설명 | 우선순위 | 예상 일정 | +|-----|------|---------|---------| +| **Next.js API Routes 구현** | **Team/User API 엔드포인트 구현, ID Token 검증, 권한 체크** | 🔴 **긴급** | **2025-11-08** | +| **서버 사이드 Redis 캐싱** | **API Routes에 Redis 캐싱, Rate Limiting 추가** | 🟡 중간 | 2025-11-09 | +| 내가 쓴 글 목록 | `/home`에 최근 글 표시, 글 목록 페이지 | 🔴 높음 | 2025-11-10 | +| 글 수정 기능 | 기존 글 불러와서 수정 | 🔴 높음 | 2025-11-11 | +| ~~firestore.ts userId→studentId 변경~~ | ~~글쓰기 기능과 팀 코드 시스템 통합~~ | ~~🟡 중간~~ | ✅ **완료 (2025-11-07)** | +| 이미지 업로드 | Firebase Storage 연동 | 🟡 중간 | 2025-11-13 | + +**Phase 1 완료 목표**: 2025년 11월 15일 + +--- + +## Phase 2: 학습 시스템 + +**목표**: 체계적인 학습 콘텐츠 제공 + +**예상 기간**: 2025년 11월 16일 ~ 12월 15일 + +### 주요 기능 + +| 항목 | 설명 | 우선순위 | +|-----|------|---------| +| 학습 페이지 UI | `/learn` 페이지 레이아웃 | 🔴 높음 | +| 레슨 목록 | 카테고리별 레슨 표시 | 🔴 높음 | +| 레슨 상세 페이지 | 이론, 예시, 연습문제 표시 | 🔴 높음 | +| 진행 상황 트래킹 | 완료한 레슨 체크 및 진행률 | 🔴 높음 | +| 퀴즈/연습 문제 | 객관식, 주관식 문제 시스템 | 🟡 중간 | +| 레슨 검색/필터 | 난이도, 카테고리별 필터링 | 🟢 낮음 | + +### 데이터 준비 + +- [ ] 초급 레슨 콘텐츠 작성 (10개) +- [ ] 중급 레슨 콘텐츠 작성 (10개) +- [ ] 연습 문제 제작 (각 레슨당 3-5개) + +**Phase 2 완료 목표**: 2025년 12월 15일 + +--- + +## Phase 3: 게임화 요소 + +**목표**: 학습 동기 부여를 위한 게임화 시스템 + +**예상 기간**: 2025년 12월 16일 ~ 2026년 1월 31일 + +### 주요 기능 + +| 항목 | 설명 | 우선순위 | +|-----|------|---------| +| 레벨 시스템 | 경험치 계산 및 레벨업 로직 | 🔴 높음 | +| 레벨업 애니메이션 | 축하 모달 및 이펙트 | 🟡 중간 | +| 스티커 페이지 UI | `/stickers` 갤러리 레이아웃 | 🔴 높음 | +| 스티커 획득 시스템 | 조건 확인 및 자동 부여 | 🔴 높음 | +| 스티커 알림 | 획득 시 축하 애니메이션 | 🟡 중간 | +| 경험치 표시 | 네비게이션 또는 프로필에 XP바 | 🟢 낮음 | +| 연속 출석 시스템 | Streak 계산 및 보상 | 🟡 중간 | + +### 데이터 준비 + +- [ ] 스티커 디자인 제작 (50개) +- [ ] 스티커 획득 조건 정의 +- [ ] 레벨별 경험치 테이블 설계 + +**Phase 3 완료 목표**: 2026년 1월 31일 + +--- + +## Phase 4: 사용자 경험 강화 + +**목표**: 사용자 편의성 및 관리 기능 개선 + +**예상 기간**: 2026년 2월 1일 ~ 3월 15일 + +### 주요 기능 + +| 항목 | 설명 | 우선순위 | +|-----|------|---------| +| **ProfileDialog (마이페이지)** | **Dialog 형식 대시보드 (탭 기반)** | 🔴 높음 | +| ├─ 내정보 탭 | 프로필 정보, 레벨, XP 표시 | 🔴 높음 | +| ├─ 내글 탭 | 작성한 글 목록 (페이지네이션) | 🔴 높음 | +| ├─ 통계 탭 | 글 수, 레벨, 스티커 통계 차트 | 🟡 중간 | +| └─ 설정 탭 | 프로필 편집, 비밀번호 변경, 계정 설정 | 🟡 중간 | +| 글 검색 | 제목/내용 기반 검색 | 🟡 중간 | +| 알림 시스템 | 인앱 알림 (스티커, 레벨업) | 🟢 낮음 | +| 알림 센터 | 알림 목록 및 설정 | 🟢 낮음 | + +**Dialog 구조**: +``` +ProfileDialog (UserProfileButton 클릭 시 열림) +├─ Tabs: 내정보 | 내글 | 통계 | 설정 +├─ 내정보: ProfileCard, LevelBadge, ExperienceBar +├─ 내글: WritingHistory (목록 + 페이지네이션) +├─ 통계: StatsWidget, 차트 +└─ 설정: ProfileEditForm, PasswordChangeForm +``` + +**Phase 4 완료 목표**: 2026년 3월 15일 + +--- + +## Phase 5: 고급 기능 + +**목표**: 부모님 참여 및 관리자 기능 + +**예상 기간**: 2026년 3월 16일 ~ 5월 31일 + +### 주요 기능 + +| 항목 | 설명 | 우선순위 | 상태 | +|-----|------|---------|------| +| ~~팀 관리 기능~~ | ~~팀 생성, 멤버 관리~~ | ~~🔴 높음~~ | ✅ **백엔드 완료 (Phase 1)** | +| ~~팀 코드 시스템~~ | ~~한글 코드, Anonymous Auth~~ | ~~🔴 높음~~ | ✅ **백엔드 완료 (Phase 1)** | +| 부모님 모드 | 자녀 진행 상황 대시보드 (정식 계정 연결 활용) | 🔴 높음 | ⏳ 예정 | +| 피드백 시스템 | 글에 댓글/평점 작성 | 🔴 높음 | ⏳ 예정 | +| 학습 리포트 | 주간/월간 활동 리포트 | 🟡 중간 | ⏳ 예정 | +| 목표 설정 | 일일/주간 목표 설정 | 🟢 낮음 | ⏳ 예정 | +| 관리자 패널 | 주제/레슨 관리 UI | 🔴 높음 | ⏳ 예정 | +| 사용자 관리 | 사용자 목록, 통계 | 🟡 중간 | ⏳ 예정 | +| 콘텐츠 에디터 | 주제/레슨 CRUD | 🔴 높음 | ⏳ 예정 | +| 스티커 에디터 | 스티커 업로드 및 관리 | 🟡 중간 | ⏳ 예정 | + +### 역할 시스템 + +```typescript +enum UserRole { + STUDENT = 'student', // 학생 + PARENT = 'parent', // 부모님 + TEAM_OWNER = 'team_owner', // 팀 소유자 + ADMIN = 'admin', // 관리자 +} +``` + +**Phase 5 완료 목표**: 2026년 5월 31일 + +--- + +## Phase 6: 최적화 및 확장 (향후 계획) + +**예상 기간**: 2026년 6월 이후 + +### 성능 최적화 + +- [ ] 이미지 최적화 (Next.js Image) +- [ ] 번들 크기 최적화 +- [ ] 코드 스플리팅 개선 +- [ ] Lighthouse 점수 개선 (목표: 95+) + +### 추가 기능 (검토 중) + +- [ ] 네이버/카카오 소셜 로그인 +- [ ] 글 공유 기능 (SNS, 링크) +- [ ] 친구 시스템 (팔로우, 친구 추가) +- [ ] 글쓰기 대회/챌린지 +- [ ] AI 글쓰기 도우미 (맞춤법, 문법 제안) +- [ ] 음성 녹음 기능 +- [ ] 그림 그리기 도구 통합 +- [ ] 모바일 앱 (React Native) + +--- + +## 우선순위 정의 + +| 기호 | 우선순위 | 설명 | +|-----|---------|------| +| 🔴 | 높음 | 핵심 기능, 빠른 개발 필요 | +| 🟡 | 중간 | 중요하지만 유연한 일정 | +| 🟢 | 낮음 | 추가 개선 사항, 여유 있을 때 | + +--- + +## 마일스톤 + +### M1: MVP 출시 (2025년 11월 15일) +- 회원가입/로그인 +- 글쓰기 기본 기능 +- 주제 선택 및 저장 + +### M2: 학습 시스템 추가 (2025년 12월 15일) +- 레슨 콘텐츠 제공 +- 진행 상황 트래킹 + +### M3: 게임화 완성 (2026년 1월 31일) +- 레벨 시스템 +- 스티커 컬렉션 + +### M4: 베타 출시 (2026년 3월 15일) +- ProfileDialog (마이페이지) 및 통계 +- 알림 시스템 + +### M5: 정식 출시 (2026년 5월 31일) +- 부모님 모드 +- 관리자 패널 + +--- + +## 개발 원칙 + +### 1. 사용자 중심 설계 +- 초등학생이 쉽게 사용할 수 있는 직관적 UI +- 읽기 쉬운 큰 글씨와 명확한 아이콘 +- 복잡한 기능은 단계적으로 안내 + +### 2. 성능 최적화 +- 빠른 페이지 로딩 (목표: < 2초) +- 부드러운 애니메이션 (60fps) +- 효율적인 데이터 로딩 (페이지네이션, 캐싱) + +### 3. 접근성 (Accessibility) +- 키보드 네비게이션 지원 +- 스크린 리더 호환 +- WCAG 2.1 AA 준수 + +### 4. 보안 및 개인정보 +- 학생 개인정보 보호 +- Firestore 보안 규칙 철저히 적용 +- HTTPS 통신 필수 + +### 5. 확장 가능성 +- 모듈화된 컴포넌트 구조 +- 타입 안전성 (TypeScript) +- 명확한 데이터 모델 + +--- + +## 기술 부채 관리 + +### 현재 기술 부채 + +| 항목 | 설명 | 해결 계획 | +|-----|------|---------| +| ~~Firestore 미연동~~ | ~~DB 미구축~~ | ✅ 해결됨 (2025-10-30) | +| 테스트 부재 | 단위/통합 테스트 없음 | Phase 4에서 추가 예정 | +| 에러 바운더리 | 전역 에러 처리 미흡 | Phase 4에서 개선 예정 | +| minH 스크롤 이슈 | write 페이지 불필요한 스크롤 | 검토 필요 | + +--- + +## 팀 구성 (현재) + +| 역할 | 담당자 | 주요 업무 | +|-----|--------|---------| +| Full Stack Developer | - | 전체 개발 | +| UI/UX Designer | - | 디자인 및 사용자 경험 | +| Content Creator | - | 레슨/주제 콘텐츠 작성 | + +--- + +## 성공 지표 (KPI) + +### MVP 단계 (M1) + +- [ ] 회원가입 사용자 50명 +- [ ] 작성된 글 100개 이상 +- [ ] 평균 세션 시간 5분 이상 + +### 베타 단계 (M4) + +- [ ] 활성 사용자 200명 +- [ ] 일일 활성 사용자(DAU) 50명 +- [ ] 레슨 완료율 70% 이상 + +### 정식 출시 (M5) + +- [ ] 활성 사용자 1,000명 +- [ ] 일일 활성 사용자(DAU) 200명 +- [ ] 사용자 만족도 4.5/5.0 이상 +- [ ] 평균 재방문율 60% 이상 + +--- + +## 리스크 관리 + +| 리스크 | 발생 가능성 | 영향도 | 대응 방안 | +|-------|-----------|-------|---------| +| 개발 일정 지연 | 중간 | 높음 | 기능 우선순위 재조정, MVP 먼저 출시 | +| 사용자 이탈 | 낮음 | 높음 | 게임화 요소 강화, 정기적 콘텐츠 업데이트 | +| Firebase 비용 초과 | 낮음 | 중간 | 사용량 모니터링, 쿼리 최적화 | +| 콘텐츠 부족 | 중간 | 중간 | 외부 작가 섭외, 사용자 제작 콘텐츠 | + +--- + +## 관련 문서 + +- [PROJECT_STRUCTURE.md](./PROJECT_STRUCTURE.md) - 프로젝트 구조 +- [TECH_STACK.md](./TECH_STACK.md) - 기술 스택 +- [DATA_MODELS.md](./DATA_MODELS.md) - 데이터 모델 +- [CLAUDE.md](./CLAUDE.md) - Claude Code 가이드 + +--- + +© 2024 BlueNovaLab. All rights reserved. \ No newline at end of file diff --git a/TECH_STACK.md b/TECH_STACK.md new file mode 100644 index 0000000..aca247d --- /dev/null +++ b/TECH_STACK.md @@ -0,0 +1,619 @@ +# 라온누리 - 기술 스택 및 개발 환경 + +> 최종 업데이트: 2025-11-07 (타입 Enum화, 팀 주제 시스템 추가) + +--- + +## 기술 스택 + +### Core Framework + +| 기술 | 버전 | 용도 | +|-----|------|------| +| **Next.js** | 16.0.0 | React 프레임워크 (App Router) | +| **React** | 19.2.0 | UI 라이브러리 | +| **TypeScript** | 5.x | 타입 안전성 | + +### UI & Styling + +| 기술 | 버전 | 용도 | +|-----|------|------| +| **Chakra UI** | v3.28.0 | 컴포넌트 라이브러리 | +| **Emotion** | 11.14.0 | CSS-in-JS | +| **Framer Motion** | 12.23.24 | 애니메이션 라이브러리 | +| **React Icons** | 5.5.0 | 아이콘 세트 | +| **Tiptap** | latest | 리치 텍스트 에디터 | + +### Backend & Database + +| 기술 | 버전 | 용도 | +|-----|------|------| +| **Firebase** | 12.4.0 | BaaS (Backend as a Service) | +| **Firebase Auth** | - | 사용자 인증 | +| **Firestore** | - | NoSQL 데이터베이스 (글 저장) | +| **Redis** | - | Cache 데이터 베이스 (예정) | + +### State Management + +| 기술 | 버전 | 용도 | +|-----|------|------| +| **Zustand** | 5.0.8 | 전역 상태 관리 | + +### Development Tools + +| 기술 | 버전 | 용도 | +|-----|------|------| +| **ESLint** | 9.x | 코드 린팅 | +| **babel-plugin-react-compiler** | 1.0.0 | React 컴파일러 최적화 | + +--- + +## 개발 명령어 + +### 주요 명령어 + +```bash +# 개발 서버 시작 (포트 3001) +npm run dev + +# 프로덕션 빌드 +npm run build + +# 프로덕션 서버 시작 (포트 3001) +npm start + +# ESLint 실행 +npm run lint +``` + +### 중요 사항 + +- **포트**: 개발/프로덕션 서버 모두 포트 `3001` 사용 (기본 3000이 아님) +- **Webpack 모드**: `--webpack` 플래그 사용 (React Compiler 요구사항) +- **Turbopack 미사용**: React Compiler와 호환성을 위해 webpack 모드 사용 + +--- + +## 프로젝트 설정 + +### Next.js 설정 (`next.config.ts`) + +```typescript +// React Compiler 활성화 +const nextConfig = { + reactCompiler: true, + // ... 기타 설정 +}; +``` + +### TypeScript 설정 + +- **Path Alias**: `@/*` → `./src/*` +- 모든 import는 `@/` 경로 사용 + +```typescript +// 예시 +import { useAuthStore } from "@/store/authStore"; +import { Navbar } from "@/components/navigation/Navbar"; +``` + +### ESLint 설정 + +- Next.js 공식 ESLint 설정 사용 +- React 19 및 Next.js 16 규칙 적용 + +--- + +## 환경 변수 + +### `.env.local` 파일 구조 + +```bash +# Firebase 설정 (필수) +NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key +NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com +NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_id +NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project.appspot.com +NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id +NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id + +# 사이트 URL (프로덕션) +NEXT_PUBLIC_URL=https://raonnuri.com + +# API Base URL (선택적, 기본값: /api) +NEXT_PUBLIC_API_URL=/api +``` + +### 환경 변수 사용 예시 + +```typescript +// src/config/firebase.ts +const firebaseConfig = { + apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, + authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, + // ... +}; +``` + +--- + +## Firebase 설정 + +### 인증 제공자 + +| 제공자 | 상태 | 설정 위치 | 용도 | +|-------|------|----------|------| +| **이메일/비밀번호** | ✅ 활성화 | Firebase Console > Authentication | 정식 계정 (학부모/고학년) | +| **Google OAuth** | ✅ 활성화 | Firebase Console > Authentication | 정식 계정 (소셜 로그인) | +| **Anonymous** | ✅ 활성화 | Firebase Console > Authentication | **학생 팀 코드 로그인** | +| **네이버** | 🔜 준비 중 | - | 정식 계정 (소셜 로그인) | +| **카카오** | 🔜 준비 중 | - | 정식 계정 (소셜 로그인) | + +### Firestore 데이터베이스 + +``` +프로젝트 루트 +└── firestore.rules # Firestore 보안 규칙 (예정) +``` + +**컬렉션 구조**: +- `writings/` ✅ - 작성한 글 + ```typescript + { + userId: string; + title: string; + content: string; // HTML + wordCount: number; + charCount: number; + status: 'draft' | 'published'; + topicId?: string | null; // 주제 ID (null은 자유 주제) + createdAt: Timestamp; + updatedAt: Timestamp; + } + ``` +- `topics/` ✅ - 글쓰기 주제 (팀 주제 + 개인 주제) + ```typescript + { + title: string; + description: string; + category: TopicCategory; // Enum: daily | imagination | emotion | experience + difficulty: TopicDifficulty; // Enum: easy | medium | hard + ownerType: TopicOwnerType; // Enum: system | team | personal + ownerId?: string; // 팀 주제: teamId, 개인 주제: userId + keywords: string[]; + examplePrompts: string[]; + titleTemplate?: string; // 제목 템플릿 + contentTemplate?: string; // 내용 템플릿 + usageCount: number; + createdAt: Timestamp; + updatedAt: Timestamp; + createdBy: string; + isActive: boolean; + } + // 팀 주제: ownerId = teamId 직접 사용 (예: abc123) + // 유틸 함수: getTeamOwnerId(teamId), extractTeamId(ownerId) - 단순 반환 + ``` +- `classrooms/` ✅ - **팀 (팀 코드 시스템)** + ```typescript + { + code: string; // "춤추는 파란 사자" (한글 팀 코드) + name: string; // "2학년 1반" + ownerId: string; // 팀 소유자 UID + securityMode: 'simple' | 'normal' | 'open'; + requirePin: boolean; + allowAnonymousJoin: boolean; + studentIds: string[]; // students 컬렉션 참조 + createdAt: Timestamp; + updatedAt: Timestamp; + isActive: boolean; + } + ``` +- `students/` ✅ - **학생 계정 (독립적, Anonymous Auth 기반)** + ```typescript + { + firebaseUid: string; // Anonymous Auth UID + linkedUserId?: string; // 연결된 정식 계정 (선택적, 1:1) + name: string; + pinHash?: string; // SHA-256 해시 + classroomIds: string[]; // 다중 팀 지원 + isAnonymous: true; + createdAt: Timestamp; + lastLoginAt: Timestamp; + } + ``` +- `users/` 🔜 - 사용자 프로필 및 진행 상황 (정식 계정) + ```typescript + { + uid: string; + email: string; + ownedStudentIds: string[]; // students 컬렉션 ID 배열 + role: 'student' | 'parent' | 'teacher'; + // ... + } + ``` +- `lessons/` 🔜 - 학습 레슨 +- `stickers/` 🔜 - 스티커 마스터 데이터 +- `userStickers/` 🔜 - 사용자별 스티커 획득 기록 + +--- + +## Chakra UI v3 커스텀 테마 + +- **파일**: `src/theme/system.ts` +- **브랜드 컬러**: 핑크(#FF6B9D), 오렌지(#FFA07A), 청록(#4ECDC4) +- **다크모드**: 시맨틱 토큰으로 자동 전환 +- **반응형 타이포그래피**: hero, heading, body 등 텍스트 스타일 정의 + +--- + +## 아키텍처 패턴 + +### 1. Manager 패턴 + API 아키텍처 (3계층 구조) + +``` +UI Layer (Components/Pages) + ↓ 매니저 호출 +Manager Layer (비즈니스 로직 + 클라이언트 캐싱) + ├─> TeamManager (싱글톤) + │ ├─> createTeam() → POST /team + │ ├─> getTeam() → GET /team/:id (5분 캐싱) + │ ├─> getMyTeams() → GET /team/list (소유+참여 팀, 1분 캐싱) + │ ├─> updateTeam() → PUT /team/:id + │ ├─> deleteTeam() → DELETE /team/:id + │ └─> generateUniqueTeamCode() → POST /team/generate-code + │ + ├─> StudentManager (싱글톤) + │ ├─> createStudent() → POST /student + │ ├─> getStudent() → GET /student/:id (5분 캐싱) + │ ├─> getStudentsByTeam() → POST /student/by-team (30초 캐싱) + │ ├─> kickStudentFromTeam() → POST /student/kick + │ ├─> validateStudentPin() → POST /student/validate-pin + │ └─> linkStudentToUser() → POST /student/link + │ + ├─> WritingManager (싱글톤) + │ ├─> createWriting() + │ ├─> getWriting() + │ └─> getUserWritings() + │ + └─> TopicManager (싱글톤) + ├─> getAvailableTopics() + └─> createPersonalTopic() + ↓ HTTP API 호출 +API Layer (Next.js API Routes / Server Actions) - 구현 대기 + ├─> /api/team/* (팀 관련 엔드포인트) + ├─> /api/student/* (학생 관련 엔드포인트) + └─> ID Token 검증, 권한 체크, Firestore 접근 + ↓ +Database Layer + ├─> Firestore (영구 데이터) + └─> Redis (캐싱, Rate Limiting) - 예정 +``` + +**Manager 패턴의 장점**: +- ✅ **UI와 비즈니스 로직 완전 분리** +- ✅ **싱글톤 패턴**으로 전역 인스턴스 관리 +- ✅ **클라이언트 사이드 캐싱**: GET 요청 자동 캐싱 (TTL 기반) +- ✅ **캐시 무효화**: 변경 작업 시 관련 캐시 자동 삭제 +- ✅ **API 추상화**: HTTP 호출 로직을 BaseManager에서 처리 +- ✅ **타입 안전성**: Request/Response 타입 완전 정의 +- ✅ **테스트 용이성**: API 모킹으로 단위 테스트 가능 +- ✅ **유연성**: Firestore 직접 접근 → API 호출로 전환 완료 + +**BaseManager 기능**: +```typescript +// src/managers/ManagerBase.ts + +abstract class BaseManager { + // 인증 + protected async getIdToken(): Promise + protected getCurrentUserId(): string | null + protected isAuthenticated(): boolean + + // API 호출 + protected async authenticatedFetch(endpoint, options) + protected async ApiCall(method, endpoint, data) +} + +abstract class SingletonManager extends BaseManager { + // 캐싱 + protected getCached(key, ttl?): T | null + protected setCached(key, data): void + protected invalidateCache(key): void + protected invalidateCachePattern(pattern): void + protected clearCache(): void + + // API + 캐싱 통합 + protected async callApiWithCache(cacheKey, method, endpoint, data, ttl) +} +``` + +**사용 예시**: +```typescript +// team/page.tsx +import { teamManager } from "@/managers"; + +// 팀 목록 조회 - 소유한 팀 + 참여한 팀 (1분간 캐싱됨) +const teams = await teamManager.getMyTeams(); + +// 팀 생성 (캐시 자동 무효화) +const teamId = await teamManager.createTeam({ + name: "우리반", + code: "춤추는파란사자", + securityMode: "simple", + requirePin: false, + allowAnonymousJoin: true +}); +``` + +**참고 문서**: +- [API_SPEC.md](./API_SPEC.md) - 전체 API 명세서 + +### 2. 인증 플로우 및 라우팅 (currentStudent 중심 아키텍처) + +``` +1. AuthInitializer (클라이언트) + └─> initializeAuth() 호출 (마운트 시) + └─> onAuthStateChanged 리스너 등록 + ├─> firebaseUser.isAnonymous ? + │ └─> getStudentByFirebaseUid() → authStore.currentStudent 설정 + └─> else ? + └─> getStudentsByUserId() → authStore.ownedStudents 설정 + +2. authStore (Zustand) - 재설계됨 + ├─> **currentStudent** (Student | null) - 현재 활동 중인 학생 (필수) + ├─> user (User | null) - 정식 계정 (선택적) + ├─> ownedStudents (Student[]) - 정식 계정이 소유한 학생들 + ├─> isAnonymous (boolean) - Anonymous Auth 여부 + ├─> isAuthenticated - 정식 계정 로그인 여부 + ├─> isLoading + └─> 액션: + ├─> login/signup/loginWithGoogle (기존) + ├─> **loginAsStudent(classCode, name, pin?)** - 팀 코드 로그인 + ├─> **switchStudent(student)** - 학생 전환 + ├─> **linkCurrentStudentWithEmail()** - 계정 연결 + └─> **linkCurrentStudentWithGoogle()** - Google 계정 연결 + +3. 인증 기반 라우팅 + ├─> 랜딩 페이지 (/) + │ └─> 로그인 상태 확인 + │ └─> isAuthenticated || currentStudent ? redirect(/home) : 랜딩 표시 + │ + └─> 유저 홈 (/home) + └─> 인증 상태 확인 + └─> !currentStudent ? redirect(/) : 대시보드 표시 + +4. 보호된 페이지 패턴 + └─> useAuthStore()로 currentStudent 확인 + └─> 미인증 시 redirect(/) 또는 openLoginDialog() +``` + +### 3. 글쓰기 및 저장 로직 (Manager 패턴 적용) + +``` +1. 사용자가 /write 페이지 접근 + ├─> LocalStorage에서 임시 저장된 글 불러오기 + └─> 에디터에 복원 + +2. 글 작성 중 + ├─> 제목: Editable 컴포넌트 (인라인 편집) + ├─> 본문: Tiptap 순수 텍스트 에디터 (포맷팅 비활성화) + │ └─> 초등학생을 위한 단순한 텍스트 입력에 집중 + ├─> 3초마다 LocalStorage에 자동 저장 + └─> 하단 고정 버튼 (취소, 저장) + +3. 저장 버튼 클릭 + ├─> 미인증 시: 로그인 다이얼로그 표시 + └─> 인증 시: + └─> writingManager.createWriting() 호출 + ├─> 유효성 검사 (제목, 내용) + ├─> 텍스트 통계 계산 (글자 수, 단어 수) + ├─> Firestore에 저장 + └─> LocalStorage 임시 저장 삭제 후 /home 이동 + +4. WritingManager API + ├─> createWriting() - 새 글 작성 + ├─> getWriting() - 글 조회 + ├─> getUserWritings() - 사용자 글 목록 + ├─> getRecentWritings() - 최근 글 목록 + ├─> updateWriting() - 글 수정 + └─> deleteWriting() - 글 삭제 +``` + +### 4. 상태 관리 원칙 + +- **전역 상태**: Zustand 사용 (인증, 사용자 진행 상황, 알림) +- **로컬 상태**: `useState` 사용 (폼 입력, UI 토글, 에디터 내용) +- **로컬 저장소**: LocalStorage (임시 저장 글) +- **서버 상태**: Firestore 직접 호출 (React Query는 나중에 고려) + +### 5. 태그 입력 필드 패턴 (Tag Input Field) + +CreateTopicDialog의 제목 템플릿 입력에 사용되는 고급 UI 패턴입니다. + +**구현 방식**: +```typescript +// 상태 구조 +type TemplatePart = { + id: string; + type: "text" | "placeholder"; + value: string; // "{date}" 또는 일반 텍스트 + label?: string; // "날짜" (placeholder인 경우) +}; + +const [templateParts, setTemplateParts] = useState([]); +const [currentInput, setCurrentInput] = useState(""); +const [selectedPartIndex, setSelectedPartIndex] = useState(null); +``` + +**주요 기능**: +- ✅ 자동 플레이스홀더 감지: `{date}`, `{time}` 등 입력 시 자동으로 태그 변환 +- ✅ 키보드 네비게이션: + - `←` (왼쪽 화살표): 이전 part 선택 + - `→` (오른쪽 화살표): 다음 part 선택 또는 입력 필드로 복귀 + - `Backspace`: 선택된 part 삭제 (입력이 비어있으면 마지막 part 선택) + - `Delete`: 선택된 part 삭제 (다음 part로 이동) + - `Enter`: 현재 입력 확정 +- ✅ 마우스 인터랙션: + - part 클릭: 해당 part 선택 + - × 버튼: 즉시 삭제 +- ✅ 시각적 피드백: + - 선택된 텍스트: 파란 배경 + 파란 테두리 + - 선택된 태그: 진한 파란 테두리 + 투명도 감소 + +**사용 사례**: +- 템플릿 입력 (제목/내용 템플릿) +- 이메일 받는 사람 입력 (Gmail 스타일) +- 태그 입력 (해시태그, 키워드) +- 멘션 입력 (Slack, Discord 스타일) + +**참고 파일**: `src/components/writing/CreateTopicDialog.tsx:61-321` + +--- + +### 6. 팀 코드 시스템 아키텍처 (초등 저학년 로그인 간소화) + +#### 핵심 개념 + +**문제**: 초등 저학년은 이메일/비밀번호 로그인이 어려움 +**해결**: 팀 소유자가 팀 코드를 발급하고, 학생은 간단히 로그인 +**아키텍처**: currentStudent 중심, 정식 계정은 선택사항 + +#### 계정 구조 + +``` +정식 계정 (User) - 선택적 +├─ Firebase Auth: user456 (Email/Google) +├─ Firestore: users/user456 +└─ ownedStudentIds: ["studentDoc1", "studentDoc2"] + │ + ├─> 학생 계정 1 (Student) - 독립적, 필수 + │ ├─ Firebase Auth: anon123 (Anonymous) + │ ├─ Firestore: students/studentDoc1 + │ ├─ linkedUserId: user456 (1:1 관계) + │ ├─ teamIds: ["team1", "team2"] + │ └─ 모든 활동 데이터(writings, topics)는 studentId로 기록 + │ + └─> 학생 계정 2 (Student) + └─ ... (동일 구조) +``` + +#### 팀 코드 생성 + +**한글 3단어 조합**: `[형용사/동사] + [색상] + [동물]` + +``` +예시: "춤추는 파란 사자" + +조합 수: +- 형용사/동사: 100개 +- 색상: 20개 +- 동물: 50개 +→ 총 100,000가지 조합 (10만 개) + +특징: +✅ 기억하기 쉬움 (이미지 연상) +✅ 구두 전달 가능 (말로 쉽게 전달) +✅ 타이핑 오타 최소화 (자동완성 가능) +✅ 초등 저학년도 이해 가능 +✅ 재미있고 친근함 (팀 정체성 형성) +``` + +**파일**: `src/data/classCodeWords.ts`, `src/utils/classCodeGenerator.ts` + +#### 학생 로그인 플로우 (개선됨 - 2025-11-06) + +``` +1. 학생 로그인 (팀 코드 3단계) + Step 1: 팀 코드 입력 + ├─> "춤추는 파란 사자" 입력 + ├─> Firestore teams 조회 + ├─> 소유자 체크 (본인 팀이면 /manage로 리다이렉트) + └─> Step 2로 진행 + + Step 2: 이름 입력 (선택 → 입력으로 개선) + ├─> 이름 직접 입력 (예: "김민지") + ├─> PIN 필요하면 Step 3 (PIN) + └─> PIN 불필요하면 Step 3 (완료 화면) + + Step 3: PIN 입력 또는 완료 화면 + ├─> [PIN 필요] 숫자 패드로 PIN 입력 → 검증 → 완료 화면 + └─> [완료 화면] 참여/로그인 구분 + ├─> 신규: 🎉 "환영합니다! {이름}님, {팀명}에 참여했어요" + └─> 재로그인: 👋 "반가워요! {이름}님, 다시 돌아왔군요!" + +2. 로그인 완료 + ├─> Anonymous Auth 유지/생성 + ├─> authStore.currentStudent 설정 + └─> /team/[teamId] 멤버 페이지로 이동 + +2. 정식 계정 연결 (선택적, 학부모/고학년) + ├─> 설정 → "내 계정 만들기" + ├─> 이메일 회원가입 또는 Google 로그인 + ├─> linkWithCredential() 호출 + │ └─ Anonymous(anon123) → Email(user456) 전환 + ├─> Firestore 연결: + │ ├─ students/studentDoc.linkedUserId = user456 + │ └─ users/user456.ownedStudentIds = [studentDoc] + └─> 이후 user456으로 로그인 가능 (currentStudent 자동 설정) + +3. 정식 계정 로그인 (학생 자동 선택) + ├─> user456으로 로그인 + ├─> Firestore users/user456 조회 + ├─> ownedStudentIds로 students 조회 + ├─> 학생이 1명: 자동 선택 + ├─> 학생이 2명+: StudentPicker 표시 (누구로 활동할까요?) + └─> authStore.currentStudent 설정 +``` + +#### 보안 모드 + +| 모드 | 인증 단계 | 사용 사례 | +|------|----------|----------| +| **simple** | 팀 코드 + 이름 | 교실 전용, 저학년 (1-2학년) | +| **normal** | 팀 코드 + 이름 + PIN | 가정 학습 포함, 고학년 (3-4학년) | +| **open** | 팀 코드 + 자유 가입 | 전학생, 체험 학생 허용 | + +#### Rate Limiting (학생 친화적) + +``` +5회 실패: 💡 "어려우면 팀을 만든 사람에게 물어보세요!" +10회 실패: ⚠️ "입력을 확인해주세요. 띄어쓰기는 안 해도 괜찮아요!" +15회 실패: 🔒 "2분 후에 다시 시도해주세요. 팀 관리자에게 도움을 요청하세요." +``` + +#### 데이터 흐름 + +``` +모든 활동 데이터는 studentId로 기록: + +writings/{writingId} +├─ studentId: "studentDoc1" ← 핵심! (userId 아님) +├─ title: "나의 하루" +└─ content: "..." + +조회 시: +- 팀 코드 계정: getUserWritings(currentStudent.id) +- 정식 계정: ownedStudents.map(s => getUserWritings(s.id)) +``` + +**참고 파일**: +- `src/managers/TeamManager.ts` - 팀 관련 API 호출 + 캐싱 +- `src/managers/StudentManager.ts` - 학생 관련 API 호출 + 캐싱 +- `src/managers/ManagerBase.ts` - API 호출 및 캐싱 공통 로직 +- `src/services/firebaseAuth.ts:125-316` - 학생 로그인 로직 +- `src/store/authStore.ts` - currentStudent 중심 상태 관리 +- `src/types/api/team.ts` - 팀 API 타입 정의 +- `src/types/api/student.ts` - 학생 API 타입 정의 +- `API_SPEC.md` - 전체 API 명세서 (23개 엔드포인트) + +--- + +## 참고 문서 + +- [PROJECT_STRUCTURE.md](./PROJECT_STRUCTURE.md) - 프로젝트 구조 +- [ROADMAP.md](./ROADMAP.md) - 개발 로드맵 +- [CLAUDE.md](./CLAUDE.md) - Claude Code 가이드 + +--- + +© 2024 BlueNovaLab. All rights reserved. \ No newline at end of file