From d09f99a0cc6c77c00cdc739cd3f9630276993b7b Mon Sep 17 00:00:00 2001 From: Documentation Bot Date: Wed, 12 Nov 2025 07:45:19 +0000 Subject: [PATCH] docs: Sync documentation from private repository --- DATA_MODELS.md | 194 ++++++++++++++++++++++++++++++++++++--- PROJECT_STRUCTURE.md | 92 ++++++++++++++++--- ROADMAP.md | 3 +- TECH_STACK.md | 211 +++++++++++++++++++++++++++++++++++++------ 4 files changed, 447 insertions(+), 53 deletions(-) diff --git a/DATA_MODELS.md b/DATA_MODELS.md index f8288c0..5bd7ddf 100644 --- a/DATA_MODELS.md +++ b/DATA_MODELS.md @@ -1,29 +1,43 @@ # 라온누리 - 데이터 모델 및 스키마 -> 최종 업데이트: 2025-11-07 +> 최종 업데이트: 2025-11-12 (실시간 글쓰기 모니터링) -이 문서는 Firestore 데이터베이스 구조와 TypeScript 타입 정의를 설명합니다. +이 문서는 Firestore 데이터베이스 및 Firebase Realtime Database 구조와 TypeScript 타입 정의를 설명합니다. **참고**: API 타입 정의는 [API_SPEC.md](./API_SPEC.md)를 참조하세요. --- -## Firestore 컬렉션 구조 +## 데이터베이스 구조 -### 컬렉션 개요 +### Firestore (영구 데이터) ``` firestore ├── teams/ # ✅ 팀 (팀 코드 시스템) -├── students/ # ✅ 학생 계정 (Anonymous Auth) -├── users/ # 🔜 사용자 프로필 및 진행 상황 +├── users/ # ✅ 사용자 프로필 및 메타데이터 ├── writings/ # ✅ 작성한 글 ├── topics/ # ✅ 글쓰기 주제 +├── patternAnalyses/ # ✅ 패턴 분석 결과 (contentHash 캐싱) ├── lessons/ # 🔜 학습 레슨 ├── stickers/ # 🔜 스티커 마스터 데이터 └── userStickers/ # 🔜 사용자별 스티커 획득 기록 ``` +### Firebase Realtime Database (실시간 데이터) + +``` +realtime-db +├── monitoring/ # 🆕 실시간 글쓰기 모니터링 +│ └── {teamId}/ +│ └── {topicId}/ +│ └── {userId}/ +├── previewRequests/ # 🆕 미리보기 요청 +│ └── {userId}/ +└── previewResponses/ # 🆕 미리보기 응답 + └── {requestId}/ +``` + **범례**: - ✅ 구현 완료 - 🔜 구현 예정 @@ -613,6 +627,160 @@ interface UserSticker { --- +## 9. WritingSession (실시간 글쓰기 모니터링) 🆕 + +**데이터베이스**: Firebase Realtime Database (휘발성 데이터) + +### Realtime DB 구조 + +#### 9.1. monitoring (글쓰기 통계) + +**경로**: `monitoring/{teamId}/{topicId}/{userId}` + +```typescript +interface WritingStats { + userId: string; // 작성자 UID + contentLength: number; // 현재 글자 수 (공백 포함) + wordCount: number; // 현재 단어 수 + topicId: string; // 현재 작성 중인 주제 ID + lastUpdated: number; // 마지막 업데이트 시간 (timestamp) - 항상 변경됨 +} + +interface SpeedDataPoint { + speed: number; // 작성 속도 (글자/분) + timestamp: number; // 기록 시간 +} + +interface MonitoringData extends WritingStats { + displayName: string; // 표시 이름 + speedHistory: SpeedDataPoint[]; // 속도 히스토리 (최근 10개) + currentSpeed: number; // 현재 속도 (글자/분) + preview?: string; // 미리보기 (선택적) +} +``` + +**예시 데이터**: +```json +{ + "monitoring": { + "team_abc123": { + "topic_xyz": { + "user_001": { + "userId": "user_001", + "contentLength": 1500, + "wordCount": 300, + "topicId": "topic_xyz", + "lastUpdated": 1731398400000 + }, + "user_002": { + "userId": "user_002", + "contentLength": 800, + "wordCount": 160, + "topicId": "topic_xyz", + "lastUpdated": 1731398395000 + } + } + } + } +} +``` + +**클라이언트 측 계산** (선생님 화면): +```typescript +// MonitoringData (UI용) +{ + userId: "user_001", + contentLength: 1500, + wordCount: 300, + topicId: "topic_xyz", + lastUpdated: 1731398400000, + displayName: "철수", + currentSpeed: 420, // 글자/분 (클라이언트 계산) + speedHistory: [ // 최근 10개 (클라이언트 계산) + { speed: 480, timestamp: 1731398370000 }, + { speed: 420, timestamp: 1731398375000 }, + { speed: 360, timestamp: 1731398380000 }, + { speed: 0, timestamp: 1731398385000 }, // 멈춤! + { speed: 0, timestamp: 1731398390000 }, + { speed: 420, timestamp: 1731398395000 }, // 재시작 + { speed: 480, timestamp: 1731398400000 } + ] +} +``` + +#### 9.2. previewRequests (미리보기 요청) + +**경로**: `previewRequests/{userId}/{requestId}` + +```typescript +interface PreviewRequest { + requestedBy: string; // 요청한 관리자 UID + timestamp: number; // 요청 시간 + requestId: string; // 고유 요청 ID +} +``` + +#### 9.3. previewResponses (미리보기 응답) + +**경로**: `previewResponses/{requestId}` + +```typescript +interface PreviewResponse { + content: string; // 현재 작성 중인 글 내용 + timestamp: number; // 응답 시간 + requestId: string; // 요청 ID (매칭용) +} +``` + +### 업데이트 주기 + +- **통계 전송**: 5초마다 (학생이 팀 주제로 작성 중일 때만) +- **자동 정리**: `onDisconnect().remove()`로 페이지 이탈 시 자동 삭제 +- **미리보기**: 요청 시에만 (10초 타임아웃) + +### Security Rules + +**파일**: `database.rules.json` + +```json +{ + "rules": { + "monitoring": { + "$teamId": { + "$topicId": { + "$userId": { + ".read": "auth != null && (auth.uid == $userId || root.child('teamOwners').child($teamId).val() == auth.uid)", + ".write": "auth != null && auth.uid == $userId" + } + } + } + }, + "previewRequests": { + "$userId": { + ".read": "auth != null && auth.uid == $userId", + ".write": "auth != null" + } + }, + "previewResponses": { + "$requestId": { + ".read": "auth != null", + ".write": "auth != null" + } + } + } +} +``` + +**권한**: +- 통계 쓰기: 본인만 +- 통계 읽기: 본인 + 팀 소유자 +- 미리보기 요청: 누구나 쓰기, 대상자만 읽기 +- 미리보기 응답: 누구나 읽기/쓰기 (requestId로 필터링) + +**TypeScript**: `src/types/writingSession.ts` + +--- + ## TypeScript 타입 정의 파일 ### 데이터 모델 타입 @@ -622,17 +790,19 @@ interface UserSticker { ``` src/types/ ├── team.ts # ✅ Team 데이터 모델 -├── student.ts # ✅ Student 데이터 모델 +├── firestoreUser.ts # ✅ User 데이터 모델 (FirestoreUser, User 분리) ├── writing.ts # ✅ Writing 데이터 모델 ├── topic.ts # ✅ Topic 데이터 모델 -├── user.ts # 🔜 User 관련 타입 (예정) +├── draft.ts # ✅ Draft 데이터 모델 (글조각) +├── writingPattern.ts # ✅ WritingPattern 분석 데이터 모델 +├── writingSession.ts # 🆕 WritingSession 실시간 모니터링 타입 ├── lesson.ts # 🔜 Lesson 관련 타입 (예정) ├── sticker.ts # 🔜 Sticker 관련 타입 (예정) └── api/ # ✅ API Request/Response 타입 - ├── team.ts # Team API 타입 (10개 엔드포인트) - ├── student.ts # Student API 타입 (13개 엔드포인트) - ├── writing.ts # Writing API 타입 (6개 엔드포인트) - └── topic.ts # Topic API 타입 (6개 엔드포인트) + ├── team.ts # Team API 타입 + ├── user.ts # User API 타입 + ├── writing.ts # Writing API 타입 + └── topic.ts # Topic API 타입 ``` ### 사용 예시 diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md index 408374d..1ba9e90 100644 --- a/PROJECT_STRUCTURE.md +++ b/PROJECT_STRUCTURE.md @@ -1,10 +1,25 @@ # 라온누리 - 프로젝트 구조 -> 최종 업데이트: 2025-11-12 (Writing API, 패턴 분석 확장, Content Hash 캐싱) +> 최종 업데이트: 2025-11-12 (실시간 글쓰기 모니터링 시스템) 초등학생을 위한 창작 글쓰기 교육 플랫폼 -**최신 업데이트** (2025-11-12): +**최신 업데이트** (2025-11-12 PM): +- 📡 **실시간 글쓰기 모니터링 시스템** + - Firebase Realtime Database 기반 실시간 통신 (Redis Pub/Sub 방식) + - 팀 주제 선택 시 해당 주제로 작성 중인 학생 실시간 표시 + - 5초 주기 자동 업데이트 (글자 수, 단어 수, lastUpdated 타임스탬프) + - **작성 속도 계산** (클라이언트 측, 글자/분 단위) + - **Sparkline 그래프** (Area Chart, 최근 10개 데이터 포인트) + - **인터랙티브 툴팁** (마우스 오버 시 속도 + 몇 초 전 데이터인지 표시) + - 미리보기 요청-응답 시스템 (관리자 클릭 → 학생 응답 → Dialog 표시) + - onDisconnect() 자동 정리 (페이지 이탈 시 세션 자동 삭제) + - LiveWritingMonitor 컴포넌트 (주제 Select, 실시간 카드 그리드) + - WritingSessionManager (Realtime DB 작업, 상세 디버그 로그) + - 완전 무료 (동시 접속 100명까지, 1GB/day) + - Security Rules로 권한 관리 (본인 쓰기, 인증된 사용자 읽기) + +**최신 업데이트** (2025-11-12 AM): - ✅ **Writing API 구현 완료** - POST /api/writing - 글 생성 (서버에서 wordCount/charCount 자동 계산) - GET /api/writing/[id] - 글 조회 (작성자만 접근) @@ -289,9 +304,25 @@ | **TeamTopicManager** | `TeamTopicManager.tsx` | 팀 주제 목록 및 생성/삭제 UI | ✅ 완료 | | **SecurityLevelSelector** | `SecurityLevelSelector.tsx` | 5단계 보안 레벨 선택 (RadioCard, framer-motion 애니메이션) | ✅ 완료 | | **AllowListManager** | `AllowListManager.tsx` | 명단 관리 (이름/이메일 추가/제거, TagsInput) | ✅ 완료 | -| **TopicMemberAnalysisSection** | `TopicMemberAnalysisSection.tsx` | 🆕 주제별 학생 글쓰기 분석 (Accordion, by-topic 분석) | 🚧 UI만 (API 추후 구현) | +| **TopicMemberAnalysisSection** | `TopicMemberAnalysisSection.tsx` | 주제별 학생 글쓰기 분석 (Accordion, by-topic 분석) | 🚧 UI만 (API 추후 구현) | +| **LiveWritingMonitor** | `LiveWritingMonitor.tsx` | 🆕 **실시간 글쓰기 모니터링** (Firebase Realtime DB, 5초 주기) | ✅ 완료 | -**주요 기능** (2025-11-10): +**주요 기능**: +- ✅ **LiveWritingMonitor** (2025-11-12): + - 주제 선택 드롭다운 (Chakra UI Select) + - StudentMonitorCard 컴포넌트 (개별 학생 카드) + - 실시간 통계: 글자 수, 단어 수, 마지막 업데이트 시간 ("N초 전") + - **작성 속도 계산** (클라이언트 측, charDiff * 12 = 글자/분) + - **Sparkline 그래프** (@chakra-ui/charts, Area Chart) + - 최근 10개 데이터 포인트 (speedHistory) + - 0도 표시 (작성 멈춤 시각화) + - Teal 색상, 투명도 30% + - **인터랙티브 툴팁** (Recharts Tooltip) + - 속도 값 ("N자/분") + - 시간 정보 ("N초 전") + - 미리보기 버튼 (Dialog로 현재 작성 중인 글 표시) + - 실시간 구독/구독 해제 (useEffect cleanup) + - 빈 상태 UI (작성 중인 학생 없을 때) - ✅ SecurityLevelSelector: RadioCard 기반, 선택 시 추가 UI 애니메이션으로 표시 - ✅ AllowListManager: Level 2/4용 명단 관리, Enter/쉼표로 구분 입력 - ✅ 그라데이션 배경 (RadioCard와 자연스럽게 연결) @@ -324,7 +355,8 @@ | **ManagerBase** | `ManagerBase.ts` | 공통 기능 (authenticatedFetch, API 호출, 캐싱) | ✅ 완료 | | **TeamManager** | `TeamManager.ts` | 팀 관련 API 호출 (생성, 조회, 수정, 삭제, 멤버 관리) | ✅ 완료 | | **UserManager** | `UserManager.ts` | 사용자 관련 API 호출 (생성, 조회, 수정, 닉네임 관리) **[NEW]** | ✅ 완료 | -| **DraftManager** | `DraftManager.ts` | 🆕 글조각 관리 (localStorage 기반, 최대 10개, CRUD) | ✅ 완료 | +| **DraftManager** | `DraftManager.ts` | 글조각 관리 (localStorage 기반, 최대 10개, CRUD) | ✅ 완료 | +| **WritingSessionManager** | `WritingSessionManager.ts` | 🆕 **실시간 글쓰기 세션 관리** (Firebase Realtime DB 작업) | ✅ 완료 | | ~~**StudentManager**~~ | ~~`StudentManager.ts`~~ | ~~학생 관련 API 호출~~ | ⚠️ Deprecated (UserManager로 대체) | | **WritingManager** | `WritingManager.ts` | 글쓰기 관련 비즈니스 로직 (CRUD, 통계) | ✅ 완료 | | **TopicManager** | `TopicManager.ts` | 주제 관련 비즈니스 로직 (CRUD, 템플릿 처리) | ✅ 완료 | @@ -341,6 +373,13 @@ - ✅ **타입 안전성**: Request/Response 타입 완전 정의 - ✅ **비즈니스 로직과 UI 레이어 분리** +**WritingSessionManager 주요 메서드** (2025-11-12): +- `startMonitoring(teamId, topicId, getStatsCallback)`: 5초 주기 통계 전송 시작 +- `stopMonitoring()`: 통계 전송 중지 (페이지 이탈 시) +- `subscribeToTopic(teamId, topicId, callback)`: 실시간 구독 (관리자용) +- `requestPreview(targetUserId)`: 미리보기 요청 (Promise 반환) +- `listenForPreviewRequests(onRequestCallback)`: 미리보기 요청 리스너 (학생용) + ### 📁 `src/services/` - 데이터 레이어 (일부 deprecated) | 서비스 | 파일명 | 설명 | 상태 | @@ -371,7 +410,8 @@ | **Team 타입** | `team.ts` | 팀 데이터 모델 (members Map), **TeamSecurityLevel Enum (1-5)** | ✅ 완료 | | **FirestoreUser 타입** | `firestoreUser.ts` | **FirestoreUser** (DB 저장용), **User** (UI용) 분리 | ✅ 완료 | | **Draft 타입** | `draft.ts` | 글조각 데이터 모델 (Draft, DraftListItem, **AnalysisHistoryItem**) | ✅ 완료 | -| **WritingPattern 타입** | `writingPattern.ts` | 🆕 **글 작성 패턴 분석** 데이터 모델 (WritingPatternAnalysis) | ✅ 완료 | +| **WritingPattern 타입** | `writingPattern.ts` | **글 작성 패턴 분석** 데이터 모델 (WritingPatternAnalysis) | ✅ 완료 | +| **WritingSession 타입** | `writingSession.ts` | 🆕 **실시간 모니터링** 데이터 모델 (WritingStats, PreviewRequest/Response, MonitoringData) | ✅ 완료 | | ~~**User 타입**~~ | ~~`user.ts`~~ | ~~사용자 데이터 모델~~ | ⚠️ Deprecated (firestoreUser.ts로 변경) | | ~~**Student 타입**~~ | ~~`student.ts`~~ | ~~학생 데이터 모델~~ | ⚠️ Deprecated (user.ts로 대체) | | **Topic 타입** | `topic.ts` | 주제 데이터 모델, TopicCategory/Difficulty/OwnerType Enum, 팀 주제 유틸 함수 | ✅ 완료 | @@ -452,22 +492,36 @@ | 파일 | 설명 | 상태 | |------|------|------| -| `firebase.ts` | Firebase 초기화 및 설정 | ✅ 완료 | +| `firebase.ts` | Firebase 초기화 및 설정 (Auth, Firestore, **Realtime DB**) | ✅ 완료 | | `site.ts` | 사이트 메타데이터 및 설정 | ✅ 완료 | +**Firebase 서비스** (2025-11-12): +- ✅ Firebase Authentication (Email/Password, Google OAuth, Anonymous) +- ✅ Cloud Firestore (데이터베이스) +- ✅ Firebase Realtime Database (실시간 모니터링) +- ✅ Firebase Analytics (분석) + +**Firebase Config**: +- Firebase 설정은 `src/config/firebase.ts`에 하드코딩되어 있습니다 +- Public API Key는 Firebase 프로젝트 설정에서 제한 가능 +- 환경변수 대신 코드에 직접 포함 (클라이언트 SDK 표준 방식) + --- ## 디렉토리 구조 요약 ``` -src/ +project_w/ +├── database.rules.json # 🆕 Firebase Realtime DB Security Rules +│ +├── src/ ├── app/ # Next.js App Router │ ├── layout.tsx # 루트 레이아웃 │ ├── page.tsx # 랜딩 페이지 (/) - 로그인 시 /home 리다이렉트 │ ├── home/ │ │ └── page.tsx # ✅ 유저 홈 페이지 (/home) │ ├── write/ -│ │ └── page.tsx # ✅ 글쓰기 페이지 (/write) +│ │ └── page.tsx # ✅ 글쓰기 페이지 (/write) - 🆕 실시간 모니터링 전송 │ ├── test/ │ │ └── page.tsx # ✅ 테스트 페이지 (/test) - 팀 코드 시스템 테스트 │ ├── globals.css @@ -498,8 +552,10 @@ src/ │ │ └── CreateTeamTopicDialog.tsx # ✅ 팀 주제 생성 │ ├── team/ # ✅ 팀 관련 │ │ ├── TeamTopicManager.tsx # ✅ 팀 주제 관리 -│ │ ├── SecurityLevelSelector.tsx # 🆕 보안 레벨 선택 (RadioCard, 애니메이션) -│ │ └── AllowListManager.tsx # 🆕 명단 관리 (TagsInput) +│ │ ├── SecurityLevelSelector.tsx # 보안 레벨 선택 (RadioCard, 애니메이션) +│ │ ├── AllowListManager.tsx # 명단 관리 (TagsInput) +│ │ ├── TopicMemberAnalysisSection.tsx # 주제별 학생 분석 (UI만) +│ │ └── LiveWritingMonitor.tsx # 🆕 실시간 글쓰기 모니터링 (Realtime DB) │ └── [미구현] │ ├── lesson/ # 학습 컴포넌트 │ ├── sticker/ # 스티커 컴포넌트 @@ -515,7 +571,11 @@ src/ ├── managers/ # ✅ 비즈니스 로직 (Manager 패턴) │ ├── ManagerBase.ts # 기본 Manager 클래스 │ ├── WritingManager.ts # 글쓰기 관리 -│ ├── TopicManager.ts # ✅ 주제 관리 +│ ├── WritingSessionManager.ts # 🆕 실시간 세션 관리 (Realtime DB) +│ ├── TopicManager.ts # 주제 관리 +│ ├── TeamManager.ts # 팀 관리 +│ ├── UserManager.ts # 사용자 관리 +│ ├── DraftManager.ts # 글조각 관리 (localStorage) │ └── [미구현] │ ├── LevelManager.ts │ └── StickerManager.ts @@ -544,11 +604,15 @@ src/ ├── types/ # TypeScript 타입 │ ├── topic.ts # ✅ 주제 관련 타입 (Enum + 유틸 함수) │ ├── team.ts # ✅ 팀 관련 타입 (Enum) -│ ├── student.ts # ✅ 학생 타입 +│ ├── firestoreUser.ts # ✅ 사용자 타입 (FirestoreUser, User) +│ ├── draft.ts # ✅ 글조각 타입 +│ ├── writingPattern.ts # ✅ 패턴 분석 타입 +│ ├── writingSession.ts # 🆕 실시간 모니터링 타입 (WritingStats, PreviewRequest/Response) │ ├── api/ # ✅ API Request/Response 타입 │ │ ├── topic.ts # ✅ 주제 API (팀/개인 주제) │ │ ├── team.ts # ✅ 팀 API -│ │ └── student.ts # ✅ 학생 API +│ │ ├── user.ts # ✅ 사용자 API +│ │ └── writing.ts # ✅ 글쓰기 API │ └── [미구현] │ └── utils/ # 유틸리티 함수 diff --git a/ROADMAP.md b/ROADMAP.md index 213e95e..5fc7923 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,6 +1,6 @@ # 라온누리 - 개발 로드맵 -> 최종 업데이트: 2025-11-12 (Writing API, 패턴 분석 확장, Content Hash 캐싱) +> 최종 업데이트: 2025-11-12 (실시간 글쓰기 모니터링 시스템) 초등학생을 위한 창작 글쓰기 교육 플랫폼 개발 계획 @@ -83,6 +83,7 @@ | **Writing API 구현** | **POST /api/writing (글 생성), GET/PUT/DELETE /api/writing/[id] (조회/수정/삭제), POST /api/writing/user (목록), POST /api/writing/recent (최근 글), src/lib/server/writing.ts (Firestore CRUD), 권한 체크 (작성자만), 텍스트 통계 자동 계산** | **2025-11-12** | | **패턴 분석 - 팀 소유자 기능** | **3가지 분석 타입 (self/by-team/by-topic), API 권한 체크, 팀 주제 필터링, WritingPatternDialog Props 확장, TopicMemberAnalysisSection (UI만), 팀 관리 페이지에 멤버 분석 메뉴** | **2025-11-12** | | **Content Hash 기반 3단계 캐싱** | **글 목록 해시 생성 (id+updatedAt), L1: localStorage (영구, LRU 10개), L2: Firestore patternAnalyses (영구), L3: Server in-memory (5분, 50개), 변경 자동 감지, AI 비용 절감 (전체 사용자 기준 1회 분석), contentHash.ts + patternCacheManager.ts + patternAnalysis.ts** | **2025-11-12** | +| **실시간 글쓰기 모니터링** | **Firebase Realtime Database 기반 실시간 통신 (Redis Pub/Sub 방식), WritingSessionManager (5초 주기 통계 전송, 미리보기 요청-응답, 상세 디버그 로그), LiveWritingMonitor 컴포넌트 (주제 Select, 실시간 카드 그리드, StudentMonitorCard), 글쓰기 페이지 모니터링 전송 (contentRef로 최적화), onDisconnect() 자동 정리, 작성 속도 계산 (클라이언트 측, 글자/분), Sparkline 그래프 (Area Chart, 최근 10개 히스토리, 0도 표시), 인터랙티브 툴팁 (속도 + 몇 초 전 데이터), 마지막 업데이트 시간 표시, database.rules.json (topicId 레벨 읽기 권한), @chakra-ui/charts + recharts 패키지, 완전 무료 (100명까지)** | **2025-11-12** | ### 🚧 진행 중 diff --git a/TECH_STACK.md b/TECH_STACK.md index 027e907..9567cb9 100644 --- a/TECH_STACK.md +++ b/TECH_STACK.md @@ -1,6 +1,6 @@ # 라온누리 - 기술 스택 및 개발 환경 -> 최종 업데이트: 2025-11-11 (글 작성 패턴 분석, 실시간 하이라이트, 인터랙티브 툴팁) +> 최종 업데이트: 2025-11-12 (실시간 글쓰기 모니터링 시스템) --- @@ -19,6 +19,8 @@ | 기술 | 버전 | 용도 | |-----|------|------| | **Chakra UI** | v3.28.0 | 컴포넌트 라이브러리 | +| **@chakra-ui/charts** | latest | 🆕 **차트 컴포넌트** (Sparkline, Area/Bar/Line 차트) | +| **Recharts** | latest | 🆕 **차트 라이브러리** (Chakra Charts 내부 사용) | | **Emotion** | 11.14.0 | CSS-in-JS | | **Framer Motion** | 12.23.24 | 애니메이션 라이브러리 | | **React Icons** | 5.5.0 | 아이콘 세트 | @@ -31,7 +33,8 @@ | **Firebase** | 12.4.0 | BaaS (Backend as a Service) | | **Firebase Auth** | - | 사용자 인증 | | **Firestore** | - | NoSQL 데이터베이스 (글 저장) | -| **@google/genai** | 1.29.0 | 🆕 **Google Gemini API SDK** (텍스트 분석, 맞춤법 검사) | +| **Firebase Realtime Database** | - | 🆕 **실시간 데이터 동기화** (글쓰기 모니터링) | +| **@google/genai** | 1.29.0 | Google Gemini API SDK (텍스트 분석, 맞춤법 검사) | | **Redis** | - | Cache 데이터 베이스 (예정) | **AI 서비스**: @@ -45,7 +48,14 @@ | 기술 | 버전 | 용도 | |-----|------|------| -| **use-debounce** | latest | 🆕 React debounce hook (5초 API 호출 제한) | +| **use-debounce** | latest | React debounce hook (5초 API 호출 제한) | + +### Charts + +| 기술 | 버전 | 용도 | +|-----|------|------| +| **@chakra-ui/charts** | latest | 🆕 **Chakra UI 차트 컴포넌트** (실시간 모니터링 그래프) | +| **recharts** | latest | 🆕 **차트 라이브러리** (Area, Line, Bar 차트) | ### State Management @@ -118,19 +128,37 @@ import { Navbar } from "@/components/navigation/Navbar"; --- -## 환경 변수 +## Firebase 설정 -### `.env.local` 파일 구조 +### Firebase Config + +Firebase 설정은 `src/config/firebase.ts`에 직접 하드코딩되어 있습니다: + +```typescript +// src/config/firebase.ts +const firebaseConfig = { + apiKey: "AIzaSyBXmSq9Sq81oNkEZsbcbc-YA9LO31URby8", + authDomain: "raonnuri-84830.firebaseapp.com", + databaseURL: "https://raonnuri-84830-default-rtdb.firebaseio.com", // 🆕 Realtime DB + projectId: "raonnuri-84830", + storageBucket: "raonnuri-84830.firebasestorage.app", + messagingSenderId: "962894843507", + appId: "1:962894843507:web:91d41427d4de819c47a406", + measurementId: "G-E4VKK56B8G" +}; + +export const fbAuth = getAuth(fbApp); +export const fbClient = getFirestore(fbApp); +export const fbRealtimeDb = getDatabase(fbApp); // 🆕 +``` + +**보안 참고**: +- Public API Key는 클라이언트 SDK 표준 방식 (Firebase 프로젝트 설정에서 도메인 제한) +- 환경변수 대신 코드에 포함 (일반적인 Firebase 클라이언트 앱 패턴) + +### 환경 변수 ```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 @@ -138,17 +166,6 @@ NEXT_PUBLIC_URL=https://raonnuri.com 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 설정 @@ -467,14 +484,156 @@ const nickname = teamManager.getMemberNickname(team, uid, user?.name); └─> migrateLegacyDraft() - 기존 단일 draft 마이그레이션 ``` -### 4. 상태 관리 원칙 +### 4. 실시간 글쓰기 모니터링 아키텍처 (Firebase Realtime Database) + +``` +관리자 (팀 관리 페이지) + ↓ 주제 선택 + ↓ +LiveWritingMonitor 컴포넌트 + ├─> 주제 드롭다운 (teamTopics) + └─> subscribeToTopic(teamId, topicId, callback) + ↓ Firebase Realtime DB 구독 + monitoring/{teamId}/{topicId}/{userId} + ↑ 5초마다 업데이트 +학생 (글쓰기 페이지) + ├─> 팀 주제 선택 감지 + └─> startMonitoring(teamId, topicId, getStats) + ├─> 5초마다 통계 전송 (contentLength, wordCount) + ├─> onDisconnect().remove() 설정 + └─> 페이지 이탈 시 자동 정리 + +미리보기 요청-응답 플로우 + 관리자: requestPreview(userId) + ↓ 요청 생성 + previewRequests/{userId}/{requestId} + ↓ 학생 리스너 감지 + 학생: listenForPreviewRequests(callback) + ↓ 현재 글 내용 전송 + previewResponses/{requestId} + ↓ 관리자 구독 + 관리자: Promise 해결 → Dialog 표시 +``` + +**주요 구성 요소**: +- **WritingSessionManager** (`src/managers/WritingSessionManager.ts`): + - `startMonitoring(teamId, topicId, getStatsCallback)`: 5초 주기 통계 전송 + - `stopMonitoring()`: 전송 중지 + DB 삭제 + - `subscribeToTopic(teamId, topicId, callback)`: 실시간 구독 (관리자) + - `requestPreview(userId)`: 미리보기 요청 (Promise 반환) + - `listenForPreviewRequests(onRequestCallback)`: 미리보기 리스너 (학생) + - 상세 디버그 로그 (전송/수신/에러 추적) + +- **LiveWritingMonitor** (`src/components/team/LiveWritingMonitor.tsx`): + - 주제 선택 Select 컴포넌트 (Chakra UI Select) + - 실시간 학생 카드 그리드 (StudentMonitorCard) + - 유저 정보와 통계 자동 결합 (userManager 활용) + - **작성 속도 실시간 계산** (클라이언트 측, 글자/분) + - **Sparkline 그래프** (Area Chart, 최근 10개 히스토리) + - **인터랙티브 툴팁** (속도 값 + 몇 초 전 데이터) + - 미리보기 Dialog + - 마지막 업데이트 시간 표시 ("N초 전") + +**Realtime DB 구조**: +```json +{ + "monitoring": { + "{teamId}": { + "{topicId}": { + "{userId}": { + "userId": "abc123", + "contentLength": 1500, + "wordCount": 300, + "topicId": "topic_123", + "lastUpdated": 1731400800000 + } + } + } + }, + "previewRequests": { + "{userId}": { + "{requestId}": { + "requestedBy": "admin_uid", + "timestamp": 1234567890, + "requestId": "req_xyz" + } + } + }, + "previewResponses": { + "{requestId}": { + "content": "현재 작성 중인 글...", + "timestamp": 1234567890, + "requestId": "req_xyz" + } + } +} +``` + +**Security Rules** (`database.rules.json`): +```json +{ + "rules": { + "monitoring": { + "$teamId": { + "$topicId": { + ".read": "auth != null", + "$userId": { + ".write": "auth != null && auth.uid == $userId" + } + } + } + }, + "previewRequests": { + "$userId": { + ".read": "auth != null && auth.uid == $userId", + ".write": "auth != null" + } + }, + "previewResponses": { + "$requestId": { + ".read": "auth != null", + ".write": "auth != null" + } + } + } +} +``` + +**권한 정책**: +- 통계 읽기: 인증된 모든 사용자 (팀 소유자만 UI 접근 가능) +- 통계 쓰기: 본인만 +- 미리보기: 요청자와 대상자만 + +**작성 속도 계산 로직** (클라이언트 측): +```typescript +// 5초마다 데이터 수신 +const charDiff = 현재글자수 - 이전글자수; +const speed = charDiff * 12; // 5초 * 12 = 60초(1분) + +// 히스토리 저장 (최근 10개) +speedHistory.push({ speed, timestamp: Date.now() }); +if (speedHistory.length > 10) speedHistory.shift(); + +// Sparkline 그래프로 시각화 +- Area Chart (면적 그래프) +- Teal 색상, 투명도 30% +- 툴팁: 마우스 오버 시 "N자/분" + "N초 전" 표시 +- 0도 표시 (작성 멈춤 시각화) +``` + +**비용 효율성**: +- Firebase Realtime DB Spark 플랫폼: 동시 접속 100명까지 **완전 무료** +- GB 다운로드 기반 과금 (쓰기/읽기 횟수 무관) +- 30명 × 1시간 수업 = ~1.5MB (무료 한도 1GB/day의 0.15%) + +### 5. 상태 관리 원칙 - **전역 상태**: Zustand 사용 (인증, 사용자 진행 상황, 알림) - **로컬 상태**: `useState` 사용 (폼 입력, UI 토글, 에디터 내용) - **로컬 저장소**: LocalStorage (임시 저장 글) - **서버 상태**: Firestore 직접 호출 (React Query는 나중에 고려) -### 5. 태그 입력 필드 패턴 (Tag Input Field) +### 6. 태그 입력 필드 패턴 (Tag Input Field) CreateTopicDialog의 제목 템플릿 입력에 사용되는 고급 UI 패턴입니다.