diff --git a/API_SPEC.md b/API_SPEC.md
index 8e6700c..63afd9a 100644
--- a/API_SPEC.md
+++ b/API_SPEC.md
@@ -2,7 +2,24 @@
라온누리 서버 API 명세서
-## ⚠️ 최신 변경사항 (2025-11-26)
+## ⚠️ 최신 변경사항 (2025-11-27)
+
+### 🖼️ 팀 커버 이미지 API
+- **POST /api/team/[teamId]/cover-image**: 팀 커버 이미지 업로드
+ - FormData로 이미지 파일 전송 (JPEG/PNG/WebP/GIF, 최대 5MB)
+ - Firebase Storage 업로드 (`teams/{teamId}/cover-{timestamp}.{ext}`)
+ - 기존 이미지 자동 삭제
+ - 파일 공개 설정 (makePublic)
+ - 팀 문서 coverImage 필드 업데이트
+ - 권한: 팀 소유자만
+- **DELETE /api/team/[teamId]/cover-image**: 팀 커버 이미지 삭제
+ - Storage에서 파일 삭제
+ - 팀 문서 coverImage 필드 제거
+ - 권한: 팀 소유자만
+- **TeamManager 메서드**: `uploadCoverImage()`, `deleteCoverImage()`
+- **컴포넌트**: TeamCoverImageUploader (드래그앤드롭, 미리보기, AspectRatio 16:9)
+
+## ⚠️ 변경사항 (2025-11-26)
### 🔗 익명 계정 연결 기능
- **POST /api/auth/merge-account**: 익명 계정 데이터를 정식 계정으로 마이그레이션
@@ -861,6 +878,66 @@ await teamManager.removeMember(teamId, currentUser.uid);
---
+### 🆕 17. POST `/team/:teamId/cover-image` - 팀 커버 이미지 업로드
+실제 URL: `POST /api/team/:teamId/cover-image`
+
+**설명**: 팀 커버 이미지를 Firebase Storage에 업로드하고 팀 문서를 업데이트합니다.
+
+**인증**: 필수 (팀 소유자만)
+
+**Request**:
+```typescript
+// FormData 형식
+{
+ file: File; // 이미지 파일 (JPEG/PNG/WebP/GIF, 최대 5MB)
+}
+```
+
+**Response**:
+```typescript
+{
+ success: true,
+ data: {
+ coverImageUrl: string; // 업로드된 이미지의 공개 URL
+ }
+}
+```
+
+**처리 로직**:
+1. 파일 타입 검증 (JPEG/PNG/WebP/GIF만 허용)
+2. 파일 크기 검증 (5MB 이하)
+3. Firebase Storage 업로드 (`teams/{teamId}/cover-{timestamp}.{ext}`)
+4. 파일 공개 설정 (`makePublic()`)
+5. 기존 이미지가 있으면 Storage에서 삭제
+6. 팀 문서 `coverImage` 필드 업데이트
+
+**캐시 무효화**: 해당 팀, 공개 팀 목록
+
+---
+
+### 🆕 18. DELETE `/team/:teamId/cover-image` - 팀 커버 이미지 삭제
+실제 URL: `DELETE /api/team/:teamId/cover-image`
+
+**설명**: 팀 커버 이미지를 Storage에서 삭제하고 팀 문서를 업데이트합니다.
+
+**인증**: 필수 (팀 소유자만)
+
+**Response**:
+```typescript
+{
+ success: true
+}
+```
+
+**처리 로직**:
+1. 팀 문서에서 `coverImage` URL 조회
+2. Storage에서 파일 삭제
+3. 팀 문서 `coverImage` 필드 제거
+
+**캐시 무효화**: 해당 팀, 공개 팀 목록
+
+---
+
## Auth API
### POST `/auth/merge-account` - 익명 계정 데이터 병합
diff --git a/DATA_MODELS.md b/DATA_MODELS.md
index c4a4b7a..62548e7 100644
--- a/DATA_MODELS.md
+++ b/DATA_MODELS.md
@@ -1,6 +1,6 @@
# 라온누리 - 데이터 모델 및 스키마
-> 최종 업데이트: 2025-11-18 (팀 코드 예약 시스템 + 다국어 생성)
+> 최종 업데이트: 2025-11-27 (팀 커버 이미지 시스템)
이 문서는 Firestore 데이터베이스 및 Firebase Realtime Database 구조와 TypeScript 타입 정의를 설명합니다.
@@ -106,17 +106,30 @@ interface TeamCodeReservation {
```typescript
interface Team {
id: string; // 문서 ID
- code: string; // 팀 코드 (예: "춤추는파란사자")
+ code: string; // 팀 코드 (예: "춤추는 파란 사자")
name: string; // 팀 이름 (예: "2학년 1반")
- ownerId: string; // 팀 소유자 UID (정식 계정)
+ ownerId: string; // 팀 소유자 UID
- // 보안 설정
- securityMode: 'simple' | 'normal' | 'open';
- requirePin: boolean; // PIN 입력 필요 여부
- allowAnonymousJoin: boolean; // 명단 없는 학생 자동 가입 허용
+ // 보안 레벨 (5단계 시스템)
+ securityLevel: TeamSecurityLevel; // 1~5 (OPEN, NAME_LIST, AUTH_REQUIRED, EMAIL_LIST, CLOSED)
- // 멤버
- studentIds: string[]; // students 컬렉션 참조
+ // 명단 관리
+ allowedNames?: string[]; // Level 2용: 허용된 이름 목록
+ allowedEmails?: string[]; // Level 4용: 허용된 이메일 목록
+
+ // AI 글쓰기 도우미 설정
+ aiAssistanceConfig?: AIAssistanceConfig;
+
+ // 공개 설정
+ isPublic?: boolean; // 팀 공개 여부 (기본: false)
+ allowPublicWritings?: boolean; // 외부 글 공개 허용 (기본: false)
+ description?: string; // 팀 소개 (공개 팀용)
+ coverImage?: string; // 🆕 팀 커버 이미지 URL (Firebase Storage)
+
+ // 멤버 관리 (uid를 키로 사용)
+ members: {
+ [uid: string]: TeamMember; // 팀별 메타데이터
+ };
// 타임스탬프
createdAt: Timestamp;
@@ -125,31 +138,41 @@ interface Team {
}
```
-### 보안 모드
+### 보안 레벨 (5단계)
-| 모드 | requirePin | allowAnonymousJoin | 설명 |
-|------|-----------|-------------------|------|
-| `simple` | false | true | 팀 코드 + 이름 (초등 저학년) |
-| `normal` | true | false | 팀 코드 + 이름 + PIN (보안 강화) |
-| `open` | false | true | 누구나 자유롭게 참여 |
+| Level | Enum | 이름 | 익명 허용 | 가입 제한 | 주요 사용처 |
+|-------|------|------|-----------|-----------|------------|
+| **1** | `OPEN` | 완전 개방 | ✅ | 닉네임 공유 로그인 | 공개 워크샵, 체험 수업 |
+| **2** | `NAME_LIST` | 명단 기반 | ✅ | `allowedNames` 체크 | 저학년 반 (익명이지만 통제) |
+| **3** | `AUTH_REQUIRED` | 로그인 필수 | ❌ | 정식 계정 누구나 | 고학년 반 (구글 계정) ⭐ 추천 |
+| **4** | `EMAIL_LIST` | 이메일 제한 | ❌ | `allowedEmails` 체크 | 특정 학생만 (전학생 차단) |
+| **5** | `CLOSED` | 닫힌 팀 | ❌ | 신규 가입 차단 | 졸업반, 종료된 프로젝트 |
### 예시 데이터
```json
{
"id": "team_abc123",
- "code": "춤추는파란사자",
+ "code": "춤추는 파란 사자",
"name": "2학년 1반",
- "ownerId": "teacher_xyz",
+ "ownerId": "user_xyz",
- "securityMode": "simple",
- "requirePin": false,
- "allowAnonymousJoin": true,
+ "securityLevel": 3,
+ "isPublic": true,
+ "allowPublicWritings": false,
+ "description": "창의적인 글쓰기를 배우는 우리 반입니다!",
+ "coverImage": "https://storage.googleapis.com/raonnuri-84830.firebasestorage.app/teams/team_abc123/cover-1732688400000.png",
- "studentIds": ["student_001", "student_002", "student_003"],
+ "members": {
+ "user_001": {
+ "joinedAt": "2024-11-06T00:00:00Z",
+ "role": "member",
+ "nickname": "춤추는 작가"
+ }
+ },
"createdAt": "2024-11-06T00:00:00Z",
- "updatedAt": "2024-11-07T10:00:00Z",
+ "updatedAt": "2024-11-27T10:00:00Z",
"isActive": true
}
```
diff --git a/NEXT_TASKS.md b/NEXT_TASKS.md
new file mode 100644
index 0000000..c02009f
--- /dev/null
+++ b/NEXT_TASKS.md
@@ -0,0 +1,285 @@
+# 다음 작업 계획
+
+> 작성일: 2025-11-25
+> Phase 1 마무리 및 기능 고도화
+
+---
+
+## ✅ 최근 완료된 작업
+
+- **글 분석 및 상호작용 기능** (2025-11-25)
+ - `/api/writing/[id]/analyze` API 추가
+ - 글 분석 결과에 따른 상호작용 컴포넌트 추가 (`components/interaction/*`)
+ - 점수 기반 영역 해금 로직 (`utils/areaLimit.ts`)
+- **이미지 왜곡 영역 기능** (2025-11-25)
+ - `DistortionAreaData` 모델 추가
+ - `InteractiveImageViewer` 컴포넌트 추가
+ - 글 수정 시 왜곡 영역 저장 지원
+- **팀 멤버 닉네임 추가** (2025-11-25)
+ - `POST /api/team/add-member`에 닉네임 파라미터 추가
+
+---
+
+## 🔴 최우선 작업 (오늘~내일)
+
+### 1. Firebase Security Rules 작성 및 배포 ⚡ (30분)
+
+
+**현재 문제**: Firestore에 보안 규칙이 없어 누구나 모든 데이터 접근 가능 💥
+
+**작업 내용**:
+```
+1. firestore.rules 파일 생성
+2. 컬렉션별 규칙 작성:
+ - users: 본인만 수정 가능
+ - teams: 소유자만 수정, 멤버는 읽기
+ - writings: 본인만 CRUD
+ - topics: 소유자만 수정
+ - patternAnalyses: 읽기 전용 (캐시용)
+3. Firebase Console에 배포
+4. 테스트 (권한 없는 접근 차단 확인)
+```
+
+**우선순위**: 🔴 높음 (보안 필수)
+
+---
+
+### 2. Level 1 실제 작동 테스트 🧪 (30분)
+
+**목적**: 구현한 UID 기반 중복 체크 로직 검증
+
+**테스트 시나리오**:
+
+#### 시나리오 A: 첫 로그인
+```
+1. npm run dev
+2. Level 1 팀 생성 (완전 개방)
+3. 로그아웃 (또는 시크릿 모드)
+4. 팀 코드 입력 → "철수" 입력
+5. 예상: 새 익명 계정 생성 ✅
+6. 확인: /team/{teamId} 페이지에서 멤버 확인
+```
+
+#### 시나리오 B: 재로그인 (핵심)
+```
+1. 로그아웃
+2. 동일 팀 코드 → "철수" 입력
+3. 예상: Custom Token으로 기존 계정 로그인 ✅
+4. 확인: 이전 글이 그대로 있는지 확인
+```
+
+#### 시나리오 C: 다른 브라우저 (중요)
+```
+1. 다른 브라우저 또는 시크릿 모드
+2. 동일 팀 코드 → "철수" 입력
+3. 예상: 동일 계정으로 로그인 ✅
+4. 확인: 크로스 디바이스 로그인 작동
+```
+
+#### 시나리오 D: 정식 계정 이름 중복 (보안)
+```
+1. Google 로그인 → 팀 가입 → 닉네임 "영희"
+2. 로그아웃
+3. 비로그인 상태에서 "영희" 입력
+4. 예상: "이미 사용 중인 이름입니다" 에러 ✅
+5. 확인: 정식 계정 탈취 방지
+```
+
+#### 시나리오 E: 로그인 상태에서 중복
+```
+1. "철수"로 로그인된 상태
+2. 팀 참여 시도 → "철수" 입력
+3. 예상: "이미 이 팀에 가입되어 있습니다" 에러 ✅
+```
+
+**우선순위**: 🔴 높음 (복잡한 로직, 검증 필수)
+
+**예상 버그**:
+- Custom Token API 경로 오류 가능성
+- 에러 메시지 번역 누락 가능성
+- Firebase Admin SDK 권한 문제
+
+---
+
+### 3. 코드 정리 및 최적화 🧹 (30분)
+
+**체크리스트**:
+```
+□ console.log 제거 또는 조건부 처리 (process.env.NODE_ENV === 'development')
+□ 사용하지 않는 import 제거
+□ TODO 코멘트 확인 및 처리
+□ 중복 코드 리팩토링
+□ 불필요한 any 타입 제거
+□ ESLint 경고 수정
+```
+
+**대상 파일**:
+- `src/services/firebaseAuth.ts` (많은 로그)
+- `src/components/auth/StudentLoginFlow.tsx`
+- `src/managers/WritingSessionManager.ts` (디버그 로그)
+
+**우선순위**: 🟡 중간
+
+---
+
+## 🟡 이번 주 완료 목표
+
+### 4. 에러 처리 개선 (1시간)
+
+**작업 내용**:
+
+#### A. 전역 에러 바운더리
+```tsx
+// src/app/[locale]/error.tsx 생성
+'use client';
+
+export default function Error({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ return (
+
+
+ 오류가 발생했습니다
+ {error.message}
+
+
+
+ );
+}
+```
+
+#### B. 404 페이지 개선
+```tsx
+// src/app/[locale]/not-found.tsx 개선
+// 현재: 없음
+// 추가: 사용자 친화적 디자인, 홈으로 돌아가기
+```
+
+#### C. API 에러 통합 처리
+```typescript
+// src/utils/apiErrorHandler.ts 생성
+// 일관된 에러 토스트 표시
+```
+
+**우선순위**: 🟡 중간
+
+---
+
+### 5. 성능 최적화 체크 (30분)
+
+**체크 항목**:
+```
+□ React DevTools Profiler로 불필요한 리렌더링 확인
+□ 번들 크기 분석 (npm run build 후 확인)
+□ 이미지 최적화 (Next.js Image 사용)
+□ Lighthouse 점수 측정 (목표: 90+)
+□ 초기 로딩 속도 (목표: 2초 이내)
+```
+
+**예상 개선점**:
+- WritingEditor 메모이제이션
+- 큰 컴포넌트 lazy loading
+- 사용하지 않는 라이브러리 제거
+
+**우선순위**: 🟡 중간
+
+---
+
+### 6. Phase 1 문서 최종 검토 (30분)
+
+**체크리스트**:
+```
+□ CLAUDE.md - 최신 정보 반영 확인
+□ PROJECT_STRUCTURE.md - 모든 파일 반영
+□ ROADMAP.md - 완료 항목 이동
+□ API_SPEC.md - 모든 엔드포인트 문서화
+□ README.md - 설치/실행 가이드 확인
+□ DATA_MODELS.md - 스키마 최신화
+```
+
+**우선순위**: 🟢 낮음
+
+---
+
+## 🟢 다음 주 시작 작업
+
+### 옵션 A: Phase 2 - 학습 시스템 설계
+
+#### 7. 학습 페이지 설계 (2-3시간)
+```
+1. /learn 페이지 와이어프레임
+2. Lesson 데이터 모델 설계:
+ - id, title, content, category, difficulty
+ - prerequisites (선수 레슨)
+ - quizzes (연습 문제)
+ - completionCriteria
+3. UI 컴포넌트 설계:
+ - LessonCard (썸네일, 제목, 난이도)
+ - LessonContent (이론, 예시)
+ - QuizSection (문제 풀이)
+ - ProgressBar (진행률)
+```
+
+#### 8. 첫 레슨 콘텐츠 작성 (1-2시간)
+```
+예시: "문장 만들기 첫걸음"
+- 이론: 주어와 서술어
+- 예시: "나는 달린다", "고양이가 잔다"
+- 연습: 주어를 고르세요 (4지선다)
+- 평가: 문장 3개 만들기
+```
+
+---
+
+### 옵션 B: 베타 테스트 준비
+
+#### 9. 베타 테스터 모집 (1주일)
+```
+1. 초등학교 선생님 1-2명 섭외
+2. 테스트 계정 준비
+3. 사용 가이드 작성
+4. 피드백 수집 양식 준비 (Google Forms)
+```
+
+#### 10. 실제 수업 시나리오 테스트
+```
+시나리오: 초등 2학년 반 (25명)
+1. 선생님이 팀 생성 (Level 2, 명단 등록)
+2. 학생들 팀 코드로 로그인
+3. 팀 주제 "오늘의 날씨" 작성
+4. 선생님 실시간 모니터링
+5. 피드백 수집
+```
+
+---
+
+## 💡 제 최종 추천 순서
+
+### 오늘 (1.5시간):
+1. **Firebase Security Rules** (30분) ⚡ 최우선
+2. **Level 1 테스트** (30분) 🧪 검증 필수
+3. **코드 정리** (30분) 🧹 품질 확보
+
+### 내일 (선택):
+4. **에러 처리 개선** (1시간) - Phase 1 완성도
+5. **성능 최적화** (30분) - 사용자 경험
+
+### 다음 주 (선택 1개):
+- **옵션 A**: Phase 2 시작 (학습 시스템) - 기능 확장
+- **옵션 B**: 베타 테스트 준비 - 실사용 피드백
+
+---
+
+## 📋 작업 선택 가이드
+
+**빠른 출시를 원하면**: Firebase Rules → 테스트 → 베타 테스트
+**기능을 더 만들고 싶으면**: Firebase Rules → 테스트 → Phase 2 시작
+**코드 품질 우선이면**: 코드 정리 → 에러 처리 → 성능 최적화
+
+---
+
+어떤 작업부터 시작할까요?
\ No newline at end of file
diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md
index 4159595..d034d4f 100644
--- a/PROJECT_STRUCTURE.md
+++ b/PROJECT_STRUCTURE.md
@@ -1,10 +1,54 @@
# 라온누리 - 프로젝트 구조
-> 최종 업데이트: 2025-11-22 (Server Component 전환 + 공통 컴포넌트)
+> 최종 업데이트: 2025-11-27 (채점 시스템 개편)
초등학생을 위한 창작 글쓰기 교육 플랫폼
-**최신 업데이트** (2025-11-22):
+**최신 업데이트** (2025-11-27 PM-2):
+- 📊 **채점 시스템 전면 개편**
+ - **0~1 품질 기반 점수**: 기존 "표현 1개당 1점" → 5단계 품질 평가 (0, 0.25, 0.5, 0.75, 1.0)
+ - **가중 평균 0~100점**: 최종 점수 = Σ(영역 점수 × 가중치) × 100
+ - **계층적 설정**: 기본값 → 팀 설정 → 주제 설정 (우선순위 오버라이드)
+ - **타입 시스템**: `ScoringConfig`, `ScoringWeights`, `ScoringRubric`, `AreaRubric`
+ - **설정 병합 서비스**: `scoringConfigService.ts` (mergeScoringConfig 함수)
+ - **AI 일관성**: temperature=0 설정으로 점수 결정론적 출력
+ - **TeamScoringSettings 컴포넌트**: 4개 영역 가중치 슬라이더 (합계 100% 검증)
+ - **TeamRubricSettings 컴포넌트**: 5단계 채점 기준 직접 편집 (아코디언 UI)
+ - **팀 관리 페이지 통합**: AI 설정 → 채점 가중치 → 채점 기준 순서
+ - **다국어 지원**: `team.manage.scoring.*`, `team.manage.rubric.*` (ko/en/ja)
+ - **UI 컴포넌트 업데이트**: ScoreBadge (0~100), ImprovementHint (퍼센트 표시)
+
+**최신 업데이트** (2025-11-27 PM):
+- 🖼️ **팀 커버 이미지 시스템 추가**
+ - **Team 타입 확장**: `coverImage?: string` 필드 추가
+ - **Firebase Storage 업로드**: 이미지 → Storage → 공개 URL
+ - **TeamCoverImageUploader 컴포넌트**: 드래그앤드롭 지원, 미리보기
+ - **API Route**: POST/DELETE `/api/team/[teamId]/cover-image`
+ - **TeamManager 메서드**: `uploadCoverImage()`, `deleteCoverImage()`
+ - **팀 생성**: 생성 시 이미지 선택 가능 (선택적)
+ - **팀 관리**: 공개 설정에서 이미지 즉시 업로드/삭제
+ - **TeamCard 표시**: 커버 이미지가 있으면 카드 상단에 140px 높이로 표시
+ - **다국어 지원**: `team.coverImage.*` (ko, en, ja)
+ - **이미지 제한**: JPEG/PNG/WebP/GIF, 최대 5MB, 16:9 권장
+
+**최신 업데이트** (2025-11-27 AM):
+- 🌐 **글 공개 범위 시스템 추가**
+ - **WritingVisibility enum**: PUBLIC (전체 공개), TEAM (팀 내 공개), PRIVATE (비공개)
+ - **VisibilitySelector 컴포넌트**: 글 저장 시 공개 범위 선택
+ - **VisibilityBadge 컴포넌트**: 글 목록에서 공개 범위 표시
+- 🏢 **팀 공개 설정 추가**
+ - **Team 타입 확장**: `isPublic`, `allowPublicWritings`, `description` 필드
+ - **공개 설정 UI**: 팀 관리 페이지에서 공개/비공개 전환
+- 📋 **공개 팀 목록 페이지** (`/team/all`)
+ - **TeamCard 컴포넌트**: 글래스모피즘 스타일 팀 카드
+ - **페이지네이션**: 커서 기반 무한 스크롤
+ - **Navbar 메뉴 추가**: "공개 팀" 링크
+- 🔀 **팀 상세 페이지 통합** (`/team/[teamId]`)
+ - **멤버 뷰**: 멤버 목록, 관리/나가기 버튼
+ - **공개 뷰**: 팀 정보, 공개 글 목록, 참여 버튼
+ - **비공개 팀**: 접근 불가 메시지
+
+**업데이트** (2025-11-22):
- 🔄 **글 상세보기 페이지 Server Component 전환**
- **SEO 최적화**: 서버에서 HTML 생성 (검색엔진 크롤링 가능)
- **SNS 공유 미리보기**: 카카오톡/페이스북 링크 미리보기 지원
@@ -253,10 +297,11 @@
| **글 상세보기** | `/[locale]/writing/[writingId]` | 🆕 **Server Component 기반 상세 페이지** | 🆕 **SEO 최적화** (서버에서 HTML 생성) 🆕 **SNS 공유 미리보기** (카카오톡/페이스북) 🆕 **서버 데이터 로딩** (Firebase Admin SDK) 제목, 내용, 생성된 이미지 표시 주제 및 팀 정보 Badge 프롬프트 Collapsible 🆕 **CommentList** (Client Component) 🆕 **BackButton** 공통 컴포넌트 사용 | ✅ 완료 |
| **글쓰기** | `/[locale]/write` | Tiptap 기반 순수 텍스트 에디터 + 주제 선택 | 주제 선택 (자유 주제/개인 주제/팀 주제) 🆕 **글 수정 기능 (URL params ?id=xxx)** 🆕 **수정 모드 배지 표시** 🆕 **주제 변경 경고** (작성 중 내용 초기화 알림, 임시 저장 안내) 제목 입력 (Editable), 순수 텍스트 에디터 (포맷팅 없음) 🆕 **다중 글조각 관리** (최대 10개), "새 글쓰기" / "저장된 글조각" 버튼 🆕 **강화된 자동 저장** (2초 debounce, 저장 상태 표시: 저장 중/저장됨) 🆕 **저장 시 AI 분석** (실시간 분석 제거, 저장 버튼 클릭 시 분석 수행) 🆕 **분석 결과 DB 저장** (WritingAnalysis + spellingErrors + contentHash) 템플릿 미리채우기 (제목/내용), Firestore 저장 비로그인도 접근 가능 (저장 시 로그인 유도) | ✅ 완료 |
| **테스트** | `/[locale]/test` | 팀 코드 시스템 테스트 페이지 | 팀 코드 생성/검증 테스트 팀/학생 생성 테스트 학생 로그인 테스트 authStore 상태 확인 | 🔜 예정 |
-| **팀 목록** | `/[locale]/team` | 내가 만든 팀 목록 (정식 계정 전용) | 팀 카드 그리드, 팀 정보 (코드, 멤버 수, 보안 설정) "새 팀 만들기" 버튼 | 🔜 예정 |
-| **팀 생성** | `/[locale]/team/create` | 새 팀 만들기 (정식 계정 전용) | 팀 이름 입력, 팀 코드 자동 생성 🆕 **5단계 보안 레벨 선택** (RadioCard, 애니메이션) 🆕 **명단 관리 (Level 2/4)**: TagsInput으로 Enter/쉼표 입력 생성 후 `/team/[teamId]`로 이동 | 🔜 예정 |
-| **팀 멤버 페이지** | `/[locale]/team/[teamId]` | 팀 멤버용 페이지 (멤버/소유자 모두 접근) | 팀 정보, 팀 코드 복사, 멤버 목록 소유자는 "팀 관리" 버튼 표시 팀 코드 로그인 후 기본 이동 페이지 | 🔜 예정 |
-| **팀 관리** | `/[locale]/team/[teamId]/manage` | 팀 관리 페이지 (소유자 전용) | 팀 정보 및 코드, 보안 설정 표시 **팀 주제 관리 (생성/삭제)** 🆕 **주제별 학생 분석** (TopicMemberAnalysisSection) 멤버 목록 및 관리 🆕 **멤버 메뉴 - 팀 내 글 분석** (by-team) "멤버 페이지 보기" 버튼 | 🔜 예정 |
+| **공개 팀 목록** | `/[locale]/team/all` | 🆕 **공개 팀 둘러보기** | 🆕 **공개된 팀 목록 표시** (isPublic=true) 🆕 **TeamCard 그리드** (글래스모피즘) 🆕 **페이지네이션** (커서 기반) 🆕 **Navbar "공개 팀" 메뉴** | ✅ 완료 |
+| **내 팀 목록** | `/[locale]/team` | 내가 만든/참여한 팀 목록 (정식 계정 전용) | 팀 카드 그리드, 팀 정보 (코드, 멤버 수, 보안 설정) "새 팀 만들기" 버튼 | ✅ 완료 |
+| **팀 생성** | `/[locale]/team/create` | 새 팀 만들기 (정식 계정 전용) | 팀 이름 입력, 팀 코드 자동 생성 🆕 **5단계 보안 레벨 선택** (RadioCard, 애니메이션) 🆕 **명단 관리 (Level 2/4)**: TagsInput으로 Enter/쉼표 입력 생성 후 `/team/[teamId]`로 이동 | ✅ 완료 |
+| **팀 상세 (통합)** | `/[locale]/team/[teamId]` | 🆕 **멤버/공개 뷰 통합 페이지** | 🆕 **멤버인 경우**: 멤버 목록, 관리/나가기 버튼 🆕 **비멤버 + 공개 팀**: 팀 정보, 공개 글 목록, 참여 버튼 🆕 **비멤버 + 비공개 팀**: 접근 불가 메시지 팀 코드 로그인 후 기본 이동 페이지 | ✅ 완료 |
+| **팀 관리** | `/[locale]/team/[teamId]/manage` | 팀 관리 페이지 (소유자 전용) | 팀 정보 및 코드, 보안 설정 표시 🆕 **팀 공개 설정** (isPublic, allowPublicWritings, description) **팀 주제 관리 (생성/삭제)** 🆕 **주제별 학생 분석** (TopicMemberAnalysisSection) 멤버 목록 및 관리 🆕 **멤버 메뉴 - 팀 내 글 분석** (by-team) "멤버 페이지 보기" 버튼 | ✅ 완료 |
### 🚧 구현 예정
@@ -406,6 +451,9 @@
| **GenerateImageDialog** | `GenerateImageDialog.tsx` | 🆕 **AI 이미지 생성 Dialog** (4단계 플로우: 장면 추출 → 장면 선택 → 이미지 생성 → 결과 표시) | ✅ 완료 |
| **SceneSelector** | `SceneSelector.tsx` | 🆕 **장면 선택 컴포넌트** (RadioCard 기반, 이모지 표시, 원문 미리보기) | ✅ 완료 |
| **WritingCard** | `WritingCard.tsx` | 🆕 **글 카드 컴포넌트** (제목, 날짜, 미리보기, 주제/점수/이미지 배지, 메뉴, 삭제) | ✅ 완료 |
+| **PublicWritingCard** | `PublicWritingCard.tsx` | 🆕 **공개 글 카드** (제목, 공개 범위 배지, 미리보기, 날짜) | ✅ 완료 |
+| **VisibilitySelector** | `VisibilitySelector.tsx` | 🆕 **공개 범위 선택** (PUBLIC/TEAM/PRIVATE, RadioCard 기반) | ✅ 완료 |
+| **VisibilityBadge** | `VisibilityBadge.tsx` | 🆕 **공개 범위 배지** (아이콘 + 라벨, 색상별 구분) | ✅ 완료 |
| **InteractiveImageViewer** | `InteractiveImageViewer.tsx` | 🆕 **인터랙티브 이미지 뷰어** (왜곡 효과, 애니메이션, 오디오 반응) | ✅ 완료 |
| **GenerateImageDialog** | `GenerateImageDialog.tsx` | 🆕 **AI 이미지 생성 Dialog** (4단계 플로우: 장면 추출 → 장면 선택 → 이미지 생성 → 결과 표시) | ✅ 완료 |
| ~~**ScoreDisplay**~~ | ~~`ScoreDisplay.tsx`~~ | ~~실시간 피드백 점수 표시~~ | ❌ 삭제됨 (하이라이트로 대체) |
@@ -494,6 +542,8 @@
| 컴포넌트 | 파일명 | 설명 | 상태 |
|---------|--------|------|------|
+| **TeamCard** | `TeamCard.tsx` | 🆕 **공개 팀 카드** (글래스모피즘, 보안 레벨 배지, 멤버 수, 🆕 커버 이미지 표시) | ✅ 완료 |
+| **TeamCoverImageUploader** | `TeamCoverImageUploader.tsx` | 🆕 **팀 커버 이미지 업로더** (드래그앤드롭, 미리보기, 16:9 AspectRatio, 5MB 제한) | ✅ 완료 |
| **TeamTopicManager** | `TeamTopicManager.tsx` | 팀 주제 목록 및 생성/삭제 UI | ✅ 완료 |
| **AIConfigDialog** | `AIConfigDialog.tsx` | 🆕 **AI 도우미 고급 설정 Dialog** (Slider, 커스텀 CheckboxCard) | ✅ 완료 |
| **SecurityLevelSelector** | `SecurityLevelSelector.tsx` | 5단계 보안 레벨 선택 (RadioCard, framer-motion 애니메이션) | ✅ 완료 |
diff --git a/ROADMAP.md b/ROADMAP.md
index 95c681b..6b6b5d2 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,6 +1,6 @@
# 라온누리 - 개발 로드맵
-> 최종 업데이트: 2025-11-22 (Server Component 전환)
+> 최종 업데이트: 2025-11-27 (채점 시스템 개편)
초등학생을 위한 창작 글쓰기 교육 플랫폼 개발 계획
@@ -116,6 +116,13 @@
| **미사용 코드 정리** | **koreanWordList.ts 삭제 (AI 분석으로 대체), InteractiveImage.tsx 삭제 (InteractiveImageViewer로 대체), AllowListManager.tsx 삭제 (미통합), 관련 문서 업데이트** | **2025-11-26** |
| **팀 페이지 리팩토링** | **useTeamData 훅 생성 (팀 + 멤버 로딩, 권한 체크, refreshMembers), 팀 페이지 중복 코드 제거 (~200줄 절감), 각 하위 컴포넌트 자체 로딩 스켈레톤 활용** | **2025-11-26** |
| **익명 계정 연결 기능** | **POST /api/auth/merge-account API (Firestore + Realtime DB 데이터 마이그레이션), mergeAndLoginWithEmail/mergeAndLoginWithGoogle 함수 (firebaseAuth.ts), mergeWithEmail/mergeWithGoogle 액션 (authStore.ts), LoginForm/SignupForm mode prop 추가 ('auth'\|'link'), LoginDialog link 모드 지원 (3개 소셜 버튼 표시), LinkAccountFlow 삭제 (기존 폼 재사용), 용어 변경 ("병합" → "연결", "익명" → "임시"), 다국어 지원 (linkAccountDescription, naverMerge/kakaoMerge/googleMerge, mergeButton/linkButton, ko/en/ja 15개 키)** | **2025-11-26** |
+| **글 공개 범위 시스템** | **WritingVisibility enum 추가 (PUBLIC/TEAM/PRIVATE), VisibilitySelector 컴포넌트 (글 저장 시 공개 범위 선택), VisibilityBadge 컴포넌트 (글 목록에서 공개 범위 표시), Writing 타입 확장 (visibility 필드), 기본값 PRIVATE, 공개 범위별 조회 API 추가, 다국어 지원 (ko/en/ja)** | **2025-11-27** |
+| **팀 공개 설정 시스템** | **Team 타입 확장 (isPublic, allowPublicWritings, description 필드), 팀 관리 페이지에 공개 설정 UI 추가, 공개 팀 조회 API (getPublicTeams, 커서 기반 페이지네이션), 공개 글 조회 API (팀별 공개 글 목록), 다국어 지원 (team.manage.publicSettings namespace, ko/en/ja)** | **2025-11-27** |
+| **공개 팀 목록 페이지** | **/team/all 페이지 구현, TeamCard 컴포넌트 (글래스모피즘 스타일), 페이지네이션 (커서 기반 무한 스크롤), Navbar "공개 팀" 메뉴 추가, 공개 팀만 표시 (isPublic=true), 다국어 지원 (teams namespace, ko/en/ja)** | **2025-11-27** |
+| **팀 상세 페이지 통합** | **/team/[teamId] 페이지 완전 재구성, 3가지 뷰 모드 (멤버 뷰/공개 뷰/접근 불가), 멤버 뷰: 멤버 목록 + 관리/나가기 버튼, 공개 뷰: 팀 정보 + 공개 글 목록 + 참여 버튼, PublicWritingCard 컴포넌트, 다국어 지원** | **2025-11-27** |
+| **팀 커버 이미지 시스템** | **Team 타입 확장 (coverImage?: string), Firebase Storage 업로드 (adminStorage.bucket()), TeamCoverImageUploader 컴포넌트 (드래그앤드롭, 미리보기, AspectRatio 16:9, 5MB 제한 JPEG/PNG/WebP/GIF), POST/DELETE /api/team/[teamId]/cover-image (FormData 업로드, 기존 이미지 자동 삭제, makePublic, extractPathFromUrl 헬퍼), TeamManager 메서드 (uploadCoverImage, deleteCoverImage, fetch FormData, 캐시 무효화), 팀 생성 페이지 이미지 선택 (선택적), 팀 관리 페이지 즉시 업로드/삭제 (공개 설정 내), TeamCard 표시 (이미지 있으면 상단 140px, 그라데이션 오버레이), 다국어 지원 (team.coverImage namespace 16개 키, ko/en/ja)** | **2025-11-27** |
+| **팀 멤버 아바타 표시 개선** | **Avatar 컴포넌트 도입 (photoURL 표시), 익명 계정 LuUser 아이콘 + gray 색상, 정식 계정 FaUserGraduate 아이콘 + teal 색상, "정식계정/익명" 텍스트 표시 제거, 닉네임과 실명이 다른 경우만 실명 이탤릭 표시** | **2025-11-27** |
+| **채점 시스템 전면 개편** | **0~1 품질 기반 점수 (5단계: 0, 0.25, 0.5, 0.75, 1.0), 가중 평균 0~100점 변환, 계층적 설정 (기본→팀→주제 우선순위), ScoringConfig/ScoringWeights/ScoringRubric 타입, scoringConfigService.ts (설정 병합), temperature=0 일관성, TeamScoringSettings 컴포넌트 (가중치 슬라이더), TeamRubricSettings 컴포넌트 (5단계 기준 편집, 아코디언 UI), 팀 관리 페이지 통합, 다국어 지원 (ko/en/ja)** | **2025-11-27** |
### 🚧 진행 중
diff --git a/TECH_STACK.md b/TECH_STACK.md
index 73defaf..0ed17d0 100644
--- a/TECH_STACK.md
+++ b/TECH_STACK.md
@@ -1,6 +1,6 @@
# 라온누리 - 기술 스택 및 개발 환경
-> 최종 업데이트: 2025-11-21 (홈 페이지 모듈화)
+> 최종 업데이트: 2025-11-27 (채점 시스템 개편)
---
@@ -35,6 +35,7 @@
| **Firebase Auth** | - | 사용자 인증 |
| **Firestore** | - | NoSQL 데이터베이스 (글 저장) |
| **Firebase Realtime Database** | - | 🆕 **실시간 데이터 동기화** (글쓰기 모니터링) |
+| **Firebase Storage** | - | 🆕 **파일 저장소** (팀 커버 이미지, AI 생성 이미지) |
| **@google/genai** | 1.29.0 | Google Gemini API SDK (텍스트 분석, 맞춤법 검사) |
| **Redis** | - | Cache 데이터 베이스 (예정) |
@@ -42,6 +43,7 @@
- **Gemini 2.5 Flash-Lite**: 텍스트 분석 (오감/감정/대화/의성어 평가, Delta 전송)
- **Gemini 2.5 Flash-Lite**: 맞춤법 검사 (초등학생 눈높이)
- **Gemini 2.5 Flash-Lite**: 글 작성 패턴 분석 (최근 10개 글 종합 분석, AI 평가 및 맞춤형 추천)
+- **Vertex AI Imagen 4.0 Fast**: 이미지 생성 (글 장면 시각화, 일관된 애니메이션 스타일)
- **Vertex AI 모드**: Multi-region failover 지원 (`vertexai: true`)
- **Response Schema**: JSON 응답 강제 (`Type.OBJECT`, `Type.ARRAY` 등)
@@ -197,7 +199,8 @@ NEXT_PUBLIC_API_URL=/api
content: string; // HTML
wordCount: number;
charCount: number;
- analysis?: { // 🆕 AI 분석 결과 (저장 시 자동 생성)
+ visibility: WritingVisibility; // 🆕 PUBLIC | TEAM | PRIVATE (기본: PRIVATE)
+ analysis?: { // AI 분석 결과 (저장 시 자동 생성)
score: number;
breakdown: { sensory, emotion, dialogue, onomatopoeia };
foundWords: { sensory[], emotion[], onomatopoeia[] };
@@ -211,6 +214,7 @@ NEXT_PUBLIC_API_URL=/api
createdAt: Timestamp;
updatedAt: Timestamp;
}
+ // 🆕 WritingVisibility enum: PUBLIC (전체 공개), TEAM (팀 내 공개), PRIVATE (비공개)
```
- `topics/` ✅ - 글쓰기 주제 (팀 주제 + 개인 주제)
```typescript
@@ -243,6 +247,10 @@ NEXT_PUBLIC_API_URL=/api
securityMode: 'simple' | 'normal' | 'open';
requirePin: boolean;
allowAnonymousJoin: boolean;
+ // 🆕 공개 설정
+ isPublic: boolean; // 팀 공개 여부 (기본: false)
+ allowPublicWritings: boolean; // 팀원 글 공개 허용 (기본: false)
+ description?: string; // 팀 소개 (공개 팀용)
createdAt: Timestamp;
updatedAt: Timestamp;
isActive: boolean;
@@ -1043,9 +1051,10 @@ interface Draft {
#### 참고 파일
**서비스 레이어**:
-- `src/services/vertexAI.ts` - Gemini API 범용 래퍼 (`@google/genai`)
-- `src/services/textAnalysisService.ts` - 텍스트 분석 (히스토리 기반)
-- `src/services/spellingService.ts` - 🆕 맞춤법 검사 (독립적)
+- `src/services/vertexAI.ts` - Gemini API 범용 래퍼 (`@google/genai`, temperature=0)
+- `src/services/textAnalysisService.ts` - 텍스트 분석 (히스토리 기반, 가중 평균 점수 계산)
+- `src/services/scoringConfigService.ts` - 🆕 채점 설정 병합 (기본→팀→주제 우선순위)
+- `src/services/spellingService.ts` - 맞춤법 검사 (독립적)
- `src/services/regionHealthManager.ts` - Region 상태 관리
**API & 컴포넌트**: