docs: Sync documentation from private repository
This commit is contained in:
parent
85b1c3fbe3
commit
04d199c64a
79
API_SPEC.md
79
API_SPEC.md
@ -2,7 +2,24 @@
|
|||||||
|
|
||||||
라온누리 서버 API 명세서
|
라온누리 서버 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**: 익명 계정 데이터를 정식 계정으로 마이그레이션
|
- **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
|
## Auth API
|
||||||
|
|
||||||
### POST `/auth/merge-account` - 익명 계정 데이터 병합
|
### POST `/auth/merge-account` - 익명 계정 데이터 병합
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# 라온누리 - 데이터 모델 및 스키마
|
# 라온누리 - 데이터 모델 및 스키마
|
||||||
|
|
||||||
> 최종 업데이트: 2025-11-18 (팀 코드 예약 시스템 + 다국어 생성)
|
> 최종 업데이트: 2025-11-27 (팀 커버 이미지 시스템)
|
||||||
|
|
||||||
이 문서는 Firestore 데이터베이스 및 Firebase Realtime Database 구조와 TypeScript 타입 정의를 설명합니다.
|
이 문서는 Firestore 데이터베이스 및 Firebase Realtime Database 구조와 TypeScript 타입 정의를 설명합니다.
|
||||||
|
|
||||||
@ -108,15 +108,28 @@ interface Team {
|
|||||||
id: string; // 문서 ID
|
id: string; // 문서 ID
|
||||||
code: string; // 팀 코드 (예: "춤추는 파란 사자")
|
code: string; // 팀 코드 (예: "춤추는 파란 사자")
|
||||||
name: string; // 팀 이름 (예: "2학년 1반")
|
name: string; // 팀 이름 (예: "2학년 1반")
|
||||||
ownerId: string; // 팀 소유자 UID (정식 계정)
|
ownerId: string; // 팀 소유자 UID
|
||||||
|
|
||||||
// 보안 설정
|
// 보안 레벨 (5단계 시스템)
|
||||||
securityMode: 'simple' | 'normal' | 'open';
|
securityLevel: TeamSecurityLevel; // 1~5 (OPEN, NAME_LIST, AUTH_REQUIRED, EMAIL_LIST, CLOSED)
|
||||||
requirePin: boolean; // PIN 입력 필요 여부
|
|
||||||
allowAnonymousJoin: boolean; // 명단 없는 학생 자동 가입 허용
|
|
||||||
|
|
||||||
// 멤버
|
// 명단 관리
|
||||||
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;
|
createdAt: Timestamp;
|
||||||
@ -125,13 +138,15 @@ interface Team {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 보안 모드
|
### 보안 레벨 (5단계)
|
||||||
|
|
||||||
| 모드 | requirePin | allowAnonymousJoin | 설명 |
|
| Level | Enum | 이름 | 익명 허용 | 가입 제한 | 주요 사용처 |
|
||||||
|------|-----------|-------------------|------|
|
|-------|------|------|-----------|-----------|------------|
|
||||||
| `simple` | false | true | 팀 코드 + 이름 (초등 저학년) |
|
| **1** | `OPEN` | 완전 개방 | ✅ | 닉네임 공유 로그인 | 공개 워크샵, 체험 수업 |
|
||||||
| `normal` | true | false | 팀 코드 + 이름 + PIN (보안 강화) |
|
| **2** | `NAME_LIST` | 명단 기반 | ✅ | `allowedNames` 체크 | 저학년 반 (익명이지만 통제) |
|
||||||
| `open` | false | true | 누구나 자유롭게 참여 |
|
| **3** | `AUTH_REQUIRED` | 로그인 필수 | ❌ | 정식 계정 누구나 | 고학년 반 (구글 계정) ⭐ 추천 |
|
||||||
|
| **4** | `EMAIL_LIST` | 이메일 제한 | ❌ | `allowedEmails` 체크 | 특정 학생만 (전학생 차단) |
|
||||||
|
| **5** | `CLOSED` | 닫힌 팀 | ❌ | 신규 가입 차단 | 졸업반, 종료된 프로젝트 |
|
||||||
|
|
||||||
### 예시 데이터
|
### 예시 데이터
|
||||||
|
|
||||||
@ -140,16 +155,24 @@ interface Team {
|
|||||||
"id": "team_abc123",
|
"id": "team_abc123",
|
||||||
"code": "춤추는 파란 사자",
|
"code": "춤추는 파란 사자",
|
||||||
"name": "2학년 1반",
|
"name": "2학년 1반",
|
||||||
"ownerId": "teacher_xyz",
|
"ownerId": "user_xyz",
|
||||||
|
|
||||||
"securityMode": "simple",
|
"securityLevel": 3,
|
||||||
"requirePin": false,
|
"isPublic": true,
|
||||||
"allowAnonymousJoin": 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",
|
"createdAt": "2024-11-06T00:00:00Z",
|
||||||
"updatedAt": "2024-11-07T10:00:00Z",
|
"updatedAt": "2024-11-27T10:00:00Z",
|
||||||
"isActive": true
|
"isActive": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
285
NEXT_TASKS.md
Normal file
285
NEXT_TASKS.md
Normal file
@ -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 (
|
||||||
|
<Box minH="100vh" display="flex" alignItems="center" justifyContent="center">
|
||||||
|
<VStack>
|
||||||
|
<Heading>오류가 발생했습니다</Heading>
|
||||||
|
<Text>{error.message}</Text>
|
||||||
|
<Button onClick={reset}>다시 시도</Button>
|
||||||
|
</VStack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 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 시작
|
||||||
|
**코드 품질 우선이면**: 코드 정리 → 에러 처리 → 성능 최적화
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
어떤 작업부터 시작할까요?
|
||||||
@ -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 전환**
|
- 🔄 **글 상세보기 페이지 Server Component 전환**
|
||||||
- **SEO 최적화**: 서버에서 HTML 생성 (검색엔진 크롤링 가능)
|
- **SEO 최적화**: 서버에서 HTML 생성 (검색엔진 크롤링 가능)
|
||||||
- **SNS 공유 미리보기**: 카카오톡/페이스북 링크 미리보기 지원
|
- **SNS 공유 미리보기**: 카카오톡/페이스북 링크 미리보기 지원
|
||||||
@ -253,10 +297,11 @@
|
|||||||
| **글 상세보기** | `/[locale]/writing/[writingId]` | 🆕 **Server Component 기반 상세 페이지** | 🆕 **SEO 최적화** (서버에서 HTML 생성)<br>🆕 **SNS 공유 미리보기** (카카오톡/페이스북)<br>🆕 **서버 데이터 로딩** (Firebase Admin SDK)<br>제목, 내용, 생성된 이미지 표시<br>주제 및 팀 정보 Badge<br>프롬프트 Collapsible<br>🆕 **CommentList** (Client Component)<br>🆕 **BackButton** 공통 컴포넌트 사용 | ✅ 완료 |
|
| **글 상세보기** | `/[locale]/writing/[writingId]` | 🆕 **Server Component 기반 상세 페이지** | 🆕 **SEO 최적화** (서버에서 HTML 생성)<br>🆕 **SNS 공유 미리보기** (카카오톡/페이스북)<br>🆕 **서버 데이터 로딩** (Firebase Admin SDK)<br>제목, 내용, 생성된 이미지 표시<br>주제 및 팀 정보 Badge<br>프롬프트 Collapsible<br>🆕 **CommentList** (Client Component)<br>🆕 **BackButton** 공통 컴포넌트 사용 | ✅ 완료 |
|
||||||
| **글쓰기** | `/[locale]/write` | Tiptap 기반 순수 텍스트 에디터 + 주제 선택 | 주제 선택 (자유 주제/개인 주제/팀 주제)<br>🆕 **글 수정 기능 (URL params ?id=xxx)**<br>🆕 **수정 모드 배지 표시**<br>🆕 **주제 변경 경고** (작성 중 내용 초기화 알림, 임시 저장 안내)<br>제목 입력 (Editable), 순수 텍스트 에디터 (포맷팅 없음)<br>🆕 **다중 글조각 관리** (최대 10개), "새 글쓰기" / "저장된 글조각" 버튼<br>🆕 **강화된 자동 저장** (2초 debounce, 저장 상태 표시: 저장 중/저장됨)<br>🆕 **저장 시 AI 분석** (실시간 분석 제거, 저장 버튼 클릭 시 분석 수행)<br>🆕 **분석 결과 DB 저장** (WritingAnalysis + spellingErrors + contentHash)<br>템플릿 미리채우기 (제목/내용), Firestore 저장<br>비로그인도 접근 가능 (저장 시 로그인 유도) | ✅ 완료 |
|
| **글쓰기** | `/[locale]/write` | Tiptap 기반 순수 텍스트 에디터 + 주제 선택 | 주제 선택 (자유 주제/개인 주제/팀 주제)<br>🆕 **글 수정 기능 (URL params ?id=xxx)**<br>🆕 **수정 모드 배지 표시**<br>🆕 **주제 변경 경고** (작성 중 내용 초기화 알림, 임시 저장 안내)<br>제목 입력 (Editable), 순수 텍스트 에디터 (포맷팅 없음)<br>🆕 **다중 글조각 관리** (최대 10개), "새 글쓰기" / "저장된 글조각" 버튼<br>🆕 **강화된 자동 저장** (2초 debounce, 저장 상태 표시: 저장 중/저장됨)<br>🆕 **저장 시 AI 분석** (실시간 분석 제거, 저장 버튼 클릭 시 분석 수행)<br>🆕 **분석 결과 DB 저장** (WritingAnalysis + spellingErrors + contentHash)<br>템플릿 미리채우기 (제목/내용), Firestore 저장<br>비로그인도 접근 가능 (저장 시 로그인 유도) | ✅ 완료 |
|
||||||
| **테스트** | `/[locale]/test` | 팀 코드 시스템 테스트 페이지 | 팀 코드 생성/검증 테스트<br>팀/학생 생성 테스트<br>학생 로그인 테스트<br>authStore 상태 확인 | 🔜 예정 |
|
| **테스트** | `/[locale]/test` | 팀 코드 시스템 테스트 페이지 | 팀 코드 생성/검증 테스트<br>팀/학생 생성 테스트<br>학생 로그인 테스트<br>authStore 상태 확인 | 🔜 예정 |
|
||||||
| **팀 목록** | `/[locale]/team` | 내가 만든 팀 목록 (정식 계정 전용) | 팀 카드 그리드, 팀 정보 (코드, 멤버 수, 보안 설정)<br>"새 팀 만들기" 버튼 | 🔜 예정 |
|
| **공개 팀 목록** | `/[locale]/team/all` | 🆕 **공개 팀 둘러보기** | 🆕 **공개된 팀 목록 표시** (isPublic=true)<br>🆕 **TeamCard 그리드** (글래스모피즘)<br>🆕 **페이지네이션** (커서 기반)<br>🆕 **Navbar "공개 팀" 메뉴** | ✅ 완료 |
|
||||||
| **팀 생성** | `/[locale]/team/create` | 새 팀 만들기 (정식 계정 전용) | 팀 이름 입력, 팀 코드 자동 생성<br>🆕 **5단계 보안 레벨 선택** (RadioCard, 애니메이션)<br>🆕 **명단 관리 (Level 2/4)**: TagsInput으로 Enter/쉼표 입력<br>생성 후 `/team/[teamId]`로 이동 | 🔜 예정 |
|
| **내 팀 목록** | `/[locale]/team` | 내가 만든/참여한 팀 목록 (정식 계정 전용) | 팀 카드 그리드, 팀 정보 (코드, 멤버 수, 보안 설정)<br>"새 팀 만들기" 버튼 | ✅ 완료 |
|
||||||
| **팀 멤버 페이지** | `/[locale]/team/[teamId]` | 팀 멤버용 페이지 (멤버/소유자 모두 접근) | 팀 정보, 팀 코드 복사, 멤버 목록<br>소유자는 "팀 관리" 버튼 표시<br>팀 코드 로그인 후 기본 이동 페이지 | 🔜 예정 |
|
| **팀 생성** | `/[locale]/team/create` | 새 팀 만들기 (정식 계정 전용) | 팀 이름 입력, 팀 코드 자동 생성<br>🆕 **5단계 보안 레벨 선택** (RadioCard, 애니메이션)<br>🆕 **명단 관리 (Level 2/4)**: TagsInput으로 Enter/쉼표 입력<br>생성 후 `/team/[teamId]`로 이동 | ✅ 완료 |
|
||||||
| **팀 관리** | `/[locale]/team/[teamId]/manage` | 팀 관리 페이지 (소유자 전용) | 팀 정보 및 코드, 보안 설정 표시<br>**팀 주제 관리 (생성/삭제)**<br>🆕 **주제별 학생 분석** (TopicMemberAnalysisSection)<br>멤버 목록 및 관리<br>🆕 **멤버 메뉴 - 팀 내 글 분석** (by-team)<br>"멤버 페이지 보기" 버튼 | 🔜 예정 |
|
| **팀 상세 (통합)** | `/[locale]/team/[teamId]` | 🆕 **멤버/공개 뷰 통합 페이지** | 🆕 **멤버인 경우**: 멤버 목록, 관리/나가기 버튼<br>🆕 **비멤버 + 공개 팀**: 팀 정보, 공개 글 목록, 참여 버튼<br>🆕 **비멤버 + 비공개 팀**: 접근 불가 메시지<br>팀 코드 로그인 후 기본 이동 페이지 | ✅ 완료 |
|
||||||
|
| **팀 관리** | `/[locale]/team/[teamId]/manage` | 팀 관리 페이지 (소유자 전용) | 팀 정보 및 코드, 보안 설정 표시<br>🆕 **팀 공개 설정** (isPublic, allowPublicWritings, description)<br>**팀 주제 관리 (생성/삭제)**<br>🆕 **주제별 학생 분석** (TopicMemberAnalysisSection)<br>멤버 목록 및 관리<br>🆕 **멤버 메뉴 - 팀 내 글 분석** (by-team)<br>"멤버 페이지 보기" 버튼 | ✅ 완료 |
|
||||||
|
|
||||||
### 🚧 구현 예정
|
### 🚧 구현 예정
|
||||||
|
|
||||||
@ -406,6 +451,9 @@
|
|||||||
| **GenerateImageDialog** | `GenerateImageDialog.tsx` | 🆕 **AI 이미지 생성 Dialog** (4단계 플로우: 장면 추출 → 장면 선택 → 이미지 생성 → 결과 표시) | ✅ 완료 |
|
| **GenerateImageDialog** | `GenerateImageDialog.tsx` | 🆕 **AI 이미지 생성 Dialog** (4단계 플로우: 장면 추출 → 장면 선택 → 이미지 생성 → 결과 표시) | ✅ 완료 |
|
||||||
| **SceneSelector** | `SceneSelector.tsx` | 🆕 **장면 선택 컴포넌트** (RadioCard 기반, 이모지 표시, 원문 미리보기) | ✅ 완료 |
|
| **SceneSelector** | `SceneSelector.tsx` | 🆕 **장면 선택 컴포넌트** (RadioCard 기반, 이모지 표시, 원문 미리보기) | ✅ 완료 |
|
||||||
| **WritingCard** | `WritingCard.tsx` | 🆕 **글 카드 컴포넌트** (제목, 날짜, 미리보기, 주제/점수/이미지 배지, 메뉴, 삭제) | ✅ 완료 |
|
| **WritingCard** | `WritingCard.tsx` | 🆕 **글 카드 컴포넌트** (제목, 날짜, 미리보기, 주제/점수/이미지 배지, 메뉴, 삭제) | ✅ 완료 |
|
||||||
|
| **PublicWritingCard** | `PublicWritingCard.tsx` | 🆕 **공개 글 카드** (제목, 공개 범위 배지, 미리보기, 날짜) | ✅ 완료 |
|
||||||
|
| **VisibilitySelector** | `VisibilitySelector.tsx` | 🆕 **공개 범위 선택** (PUBLIC/TEAM/PRIVATE, RadioCard 기반) | ✅ 완료 |
|
||||||
|
| **VisibilityBadge** | `VisibilityBadge.tsx` | 🆕 **공개 범위 배지** (아이콘 + 라벨, 색상별 구분) | ✅ 완료 |
|
||||||
| **InteractiveImageViewer** | `InteractiveImageViewer.tsx` | 🆕 **인터랙티브 이미지 뷰어** (왜곡 효과, 애니메이션, 오디오 반응) | ✅ 완료 |
|
| **InteractiveImageViewer** | `InteractiveImageViewer.tsx` | 🆕 **인터랙티브 이미지 뷰어** (왜곡 효과, 애니메이션, 오디오 반응) | ✅ 완료 |
|
||||||
| **GenerateImageDialog** | `GenerateImageDialog.tsx` | 🆕 **AI 이미지 생성 Dialog** (4단계 플로우: 장면 추출 → 장면 선택 → 이미지 생성 → 결과 표시) | ✅ 완료 |
|
| **GenerateImageDialog** | `GenerateImageDialog.tsx` | 🆕 **AI 이미지 생성 Dialog** (4단계 플로우: 장면 추출 → 장면 선택 → 이미지 생성 → 결과 표시) | ✅ 완료 |
|
||||||
| ~~**ScoreDisplay**~~ | ~~`ScoreDisplay.tsx`~~ | ~~실시간 피드백 점수 표시~~ | ❌ 삭제됨 (하이라이트로 대체) |
|
| ~~**ScoreDisplay**~~ | ~~`ScoreDisplay.tsx`~~ | ~~실시간 피드백 점수 표시~~ | ❌ 삭제됨 (하이라이트로 대체) |
|
||||||
@ -494,6 +542,8 @@
|
|||||||
|
|
||||||
| 컴포넌트 | 파일명 | 설명 | 상태 |
|
| 컴포넌트 | 파일명 | 설명 | 상태 |
|
||||||
|---------|--------|------|------|
|
|---------|--------|------|------|
|
||||||
|
| **TeamCard** | `TeamCard.tsx` | 🆕 **공개 팀 카드** (글래스모피즘, 보안 레벨 배지, 멤버 수, 🆕 커버 이미지 표시) | ✅ 완료 |
|
||||||
|
| **TeamCoverImageUploader** | `TeamCoverImageUploader.tsx` | 🆕 **팀 커버 이미지 업로더** (드래그앤드롭, 미리보기, 16:9 AspectRatio, 5MB 제한) | ✅ 완료 |
|
||||||
| **TeamTopicManager** | `TeamTopicManager.tsx` | 팀 주제 목록 및 생성/삭제 UI | ✅ 완료 |
|
| **TeamTopicManager** | `TeamTopicManager.tsx` | 팀 주제 목록 및 생성/삭제 UI | ✅ 완료 |
|
||||||
| **AIConfigDialog** | `AIConfigDialog.tsx` | 🆕 **AI 도우미 고급 설정 Dialog** (Slider, 커스텀 CheckboxCard) | ✅ 완료 |
|
| **AIConfigDialog** | `AIConfigDialog.tsx` | 🆕 **AI 도우미 고급 설정 Dialog** (Slider, 커스텀 CheckboxCard) | ✅ 완료 |
|
||||||
| **SecurityLevelSelector** | `SecurityLevelSelector.tsx` | 5단계 보안 레벨 선택 (RadioCard, framer-motion 애니메이션) | ✅ 완료 |
|
| **SecurityLevelSelector** | `SecurityLevelSelector.tsx` | 5단계 보안 레벨 선택 (RadioCard, framer-motion 애니메이션) | ✅ 완료 |
|
||||||
|
|||||||
@ -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** |
|
| **미사용 코드 정리** | **koreanWordList.ts 삭제 (AI 분석으로 대체), InteractiveImage.tsx 삭제 (InteractiveImageViewer로 대체), AllowListManager.tsx 삭제 (미통합), 관련 문서 업데이트** | **2025-11-26** |
|
||||||
| **팀 페이지 리팩토링** | **useTeamData 훅 생성 (팀 + 멤버 로딩, 권한 체크, refreshMembers), 팀 페이지 중복 코드 제거 (~200줄 절감), 각 하위 컴포넌트 자체 로딩 스켈레톤 활용** | **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** |
|
| **익명 계정 연결 기능** | **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** |
|
||||||
|
|
||||||
### 🚧 진행 중
|
### 🚧 진행 중
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# 라온누리 - 기술 스택 및 개발 환경
|
# 라온누리 - 기술 스택 및 개발 환경
|
||||||
|
|
||||||
> 최종 업데이트: 2025-11-21 (홈 페이지 모듈화)
|
> 최종 업데이트: 2025-11-27 (채점 시스템 개편)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -35,6 +35,7 @@
|
|||||||
| **Firebase Auth** | - | 사용자 인증 |
|
| **Firebase Auth** | - | 사용자 인증 |
|
||||||
| **Firestore** | - | NoSQL 데이터베이스 (글 저장) |
|
| **Firestore** | - | NoSQL 데이터베이스 (글 저장) |
|
||||||
| **Firebase Realtime Database** | - | 🆕 **실시간 데이터 동기화** (글쓰기 모니터링) |
|
| **Firebase Realtime Database** | - | 🆕 **실시간 데이터 동기화** (글쓰기 모니터링) |
|
||||||
|
| **Firebase Storage** | - | 🆕 **파일 저장소** (팀 커버 이미지, AI 생성 이미지) |
|
||||||
| **@google/genai** | 1.29.0 | Google Gemini API SDK (텍스트 분석, 맞춤법 검사) |
|
| **@google/genai** | 1.29.0 | Google Gemini API SDK (텍스트 분석, 맞춤법 검사) |
|
||||||
| **Redis** | - | Cache 데이터 베이스 (예정) |
|
| **Redis** | - | Cache 데이터 베이스 (예정) |
|
||||||
|
|
||||||
@ -42,6 +43,7 @@
|
|||||||
- **Gemini 2.5 Flash-Lite**: 텍스트 분석 (오감/감정/대화/의성어 평가, Delta 전송)
|
- **Gemini 2.5 Flash-Lite**: 텍스트 분석 (오감/감정/대화/의성어 평가, Delta 전송)
|
||||||
- **Gemini 2.5 Flash-Lite**: 맞춤법 검사 (초등학생 눈높이)
|
- **Gemini 2.5 Flash-Lite**: 맞춤법 검사 (초등학생 눈높이)
|
||||||
- **Gemini 2.5 Flash-Lite**: 글 작성 패턴 분석 (최근 10개 글 종합 분석, AI 평가 및 맞춤형 추천)
|
- **Gemini 2.5 Flash-Lite**: 글 작성 패턴 분석 (최근 10개 글 종합 분석, AI 평가 및 맞춤형 추천)
|
||||||
|
- **Vertex AI Imagen 4.0 Fast**: 이미지 생성 (글 장면 시각화, 일관된 애니메이션 스타일)
|
||||||
- **Vertex AI 모드**: Multi-region failover 지원 (`vertexai: true`)
|
- **Vertex AI 모드**: Multi-region failover 지원 (`vertexai: true`)
|
||||||
- **Response Schema**: JSON 응답 강제 (`Type.OBJECT`, `Type.ARRAY` 등)
|
- **Response Schema**: JSON 응답 강제 (`Type.OBJECT`, `Type.ARRAY` 등)
|
||||||
|
|
||||||
@ -197,7 +199,8 @@ NEXT_PUBLIC_API_URL=/api
|
|||||||
content: string; // HTML
|
content: string; // HTML
|
||||||
wordCount: number;
|
wordCount: number;
|
||||||
charCount: number;
|
charCount: number;
|
||||||
analysis?: { // 🆕 AI 분석 결과 (저장 시 자동 생성)
|
visibility: WritingVisibility; // 🆕 PUBLIC | TEAM | PRIVATE (기본: PRIVATE)
|
||||||
|
analysis?: { // AI 분석 결과 (저장 시 자동 생성)
|
||||||
score: number;
|
score: number;
|
||||||
breakdown: { sensory, emotion, dialogue, onomatopoeia };
|
breakdown: { sensory, emotion, dialogue, onomatopoeia };
|
||||||
foundWords: { sensory[], emotion[], onomatopoeia[] };
|
foundWords: { sensory[], emotion[], onomatopoeia[] };
|
||||||
@ -211,6 +214,7 @@ NEXT_PUBLIC_API_URL=/api
|
|||||||
createdAt: Timestamp;
|
createdAt: Timestamp;
|
||||||
updatedAt: Timestamp;
|
updatedAt: Timestamp;
|
||||||
}
|
}
|
||||||
|
// 🆕 WritingVisibility enum: PUBLIC (전체 공개), TEAM (팀 내 공개), PRIVATE (비공개)
|
||||||
```
|
```
|
||||||
- `topics/` ✅ - 글쓰기 주제 (팀 주제 + 개인 주제)
|
- `topics/` ✅ - 글쓰기 주제 (팀 주제 + 개인 주제)
|
||||||
```typescript
|
```typescript
|
||||||
@ -243,6 +247,10 @@ NEXT_PUBLIC_API_URL=/api
|
|||||||
securityMode: 'simple' | 'normal' | 'open';
|
securityMode: 'simple' | 'normal' | 'open';
|
||||||
requirePin: boolean;
|
requirePin: boolean;
|
||||||
allowAnonymousJoin: boolean;
|
allowAnonymousJoin: boolean;
|
||||||
|
// 🆕 공개 설정
|
||||||
|
isPublic: boolean; // 팀 공개 여부 (기본: false)
|
||||||
|
allowPublicWritings: boolean; // 팀원 글 공개 허용 (기본: false)
|
||||||
|
description?: string; // 팀 소개 (공개 팀용)
|
||||||
createdAt: Timestamp;
|
createdAt: Timestamp;
|
||||||
updatedAt: Timestamp;
|
updatedAt: Timestamp;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
@ -1043,9 +1051,10 @@ interface Draft {
|
|||||||
#### 참고 파일
|
#### 참고 파일
|
||||||
|
|
||||||
**서비스 레이어**:
|
**서비스 레이어**:
|
||||||
- `src/services/vertexAI.ts` - Gemini API 범용 래퍼 (`@google/genai`)
|
- `src/services/vertexAI.ts` - Gemini API 범용 래퍼 (`@google/genai`, temperature=0)
|
||||||
- `src/services/textAnalysisService.ts` - 텍스트 분석 (히스토리 기반)
|
- `src/services/textAnalysisService.ts` - 텍스트 분석 (히스토리 기반, 가중 평균 점수 계산)
|
||||||
- `src/services/spellingService.ts` - 🆕 맞춤법 검사 (독립적)
|
- `src/services/scoringConfigService.ts` - 🆕 채점 설정 병합 (기본→팀→주제 우선순위)
|
||||||
|
- `src/services/spellingService.ts` - 맞춤법 검사 (독립적)
|
||||||
- `src/services/regionHealthManager.ts` - Region 상태 관리
|
- `src/services/regionHealthManager.ts` - Region 상태 관리
|
||||||
|
|
||||||
**API & 컴포넌트**:
|
**API & 컴포넌트**:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user