docs: Sync documentation from private repository
This commit is contained in:
parent
4caac03e62
commit
8d0899df04
71
API_SPEC.md
71
API_SPEC.md
@ -1130,6 +1130,77 @@ await teamManager.removeMember(teamId, currentUser.uid);
|
||||
|
||||
---
|
||||
|
||||
### 2. POST `/user/purchase` - 플랜 구매/변경
|
||||
실제 URL: `POST /api/user/purchase`
|
||||
|
||||
**인증**: 필수
|
||||
|
||||
**Request**:
|
||||
```typescript
|
||||
{
|
||||
planType: PlanType; // FREE, PRO, CLASSROOM, ACADEMY, SCHOOL
|
||||
billingCycle: BillingCycle; // MONTHLY, YEARLY
|
||||
amount: number; // 결제 금액 (원)
|
||||
downgradeMode?: "immediate" | "scheduled"; // 다운그레이드 시 적용 시점
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```typescript
|
||||
{
|
||||
success: true,
|
||||
data: {
|
||||
creditsAdded: number; // 환불로 지급된 크레딧 (업그레이드/즉시 다운그레이드)
|
||||
isScheduled: boolean; // true면 다음 결제일에 적용
|
||||
isBillingCycleChange: boolean; // true면 결제 주기만 변경
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**동작**:
|
||||
1. **업그레이드**: 즉시 적용, 남은 기간 비례 환불 → 크레딧 지급
|
||||
2. **다운그레이드 (immediate)**: 즉시 적용, 남은 기간 비례 환불 → 크레딧 지급
|
||||
3. **다운그레이드 (scheduled)**: `scheduledPlan`에 저장, 만료 시 자동 적용
|
||||
4. **결제 주기 변경**: `scheduledPlan`에 저장, 만료 시 새 주기로 적용
|
||||
|
||||
**환불 계산**:
|
||||
```typescript
|
||||
const refundKRW = calculateProratedRefund(currentPlan, PLAN_MONTHLY_PRICES);
|
||||
const credits = convertKRWToCredits(refundKRW); // 100원 = 10 크레딧
|
||||
```
|
||||
|
||||
**캐시 무효화**: 사용자 정보
|
||||
|
||||
---
|
||||
|
||||
### 3. GET `/user/plan/estimate-refund` - 예상 환불 크레딧 조회
|
||||
실제 URL: `GET /api/user/plan/estimate-refund`
|
||||
|
||||
**인증**: 필수
|
||||
|
||||
**설명**: 다운그레이드 전 예상 환불 크레딧을 조회합니다. UI에서 "즉시 다운그레이드" 옵션에 표시됩니다.
|
||||
|
||||
**Response**:
|
||||
```typescript
|
||||
{
|
||||
success: true,
|
||||
data: {
|
||||
estimatedCredits: number; // 예상 환불 크레딧
|
||||
estimatedKRW: number; // 예상 환불 금액 (원)
|
||||
validUntil: string; // 현재 플랜 만료일 (ISO 8601)
|
||||
currentPlanType: PlanType; // 현재 플랜 타입
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**에러**:
|
||||
- `400`: 활성 플랜 없음
|
||||
- `401`: 인증 필요
|
||||
|
||||
**캐싱**: 없음 (실시간 계산)
|
||||
|
||||
---
|
||||
|
||||
## Writing API
|
||||
|
||||
### 1. POST `/writing` - 글 생성
|
||||
|
||||
@ -1,10 +1,43 @@
|
||||
# 라온누리 - 프로젝트 구조
|
||||
|
||||
> 최종 업데이트: 2025-12-08 (AI 크레딧 환불 시스템, 구매 플로우, 구독 관리)
|
||||
> 최종 업데이트: 2025-12-12 (보안 설정 통합, 팀 소유자 이전 기능)
|
||||
|
||||
초등학생을 위한 창작 글쓰기 교육 플랫폼
|
||||
|
||||
**최신 업데이트** (2025-12-08 오후):
|
||||
**최신 업데이트** (2025-12-12):
|
||||
- 🔒 **팀 보안 설정 통합**
|
||||
- **TeamSecuritySettingsDialog 확장**: 공개설정(isPublic) + 보안레벨(securityLevel) 통합
|
||||
- **공개 설정 스위치**: 보안 단계 선택 아래에 배치 (팀 공개, 글 공개 허용, 팀 설명, 커버 이미지)
|
||||
- **검색 가능/불가능 태그**: TeamInfoCard 보안 정보에 isPublic 기반 배지 추가
|
||||
- **TeamPublicSettings 제거**: 팀 관리 페이지에서 분리된 공개 설정 섹션 제거
|
||||
- **다국어 지원**: searchable/notSearchable 키 추가 (ko/en/ja)
|
||||
- 👑 **팀 소유자 이전 기능**
|
||||
- **POST /api/team/[teamId]/transfer-ownership**: 소유자 권한 이전 API
|
||||
- **TeamMemberEditor 컴포넌트**: 멤버 메뉴에 "소유자 이전" 옵션 추가
|
||||
- **소유자 표시**: 멤버 목록에서 owner 역할 배지 표시
|
||||
- **다국어 지원**: transferOwnership, transferDialogTitle, transferConfirm 등 (ko/en/ja)
|
||||
- 🛡️ **SecurityLevelSelector 개선**
|
||||
- **검색 가능 여부 표시**: 각 보안 레벨별 검색 가능 아이콘 (LuSearch/LuSearchX)
|
||||
- **searchable 속성 추가**: SecurityLevelOption 타입 확장
|
||||
|
||||
**업데이트** (2025-12-10):
|
||||
- 📉 **플랜 다운그레이드 선택 옵션**
|
||||
- **두 가지 선택지**: 즉시 다운그레이드 (크레딧 전환) / 다음 결제일 적용 (기존 scheduledPlan)
|
||||
- **DowngradeOptionDialog 컴포넌트**: 다운그레이드 방식 선택 UI
|
||||
- **GET /api/user/plan/estimate-refund**: 예상 환불 크레딧 조회 API
|
||||
- **downgradeMode 파라미터**: "immediate" | "scheduled"
|
||||
- **planUtils.ts**: 클라이언트/서버 공용 플랜 유틸리티 (isUpgrade, isDowngrade)
|
||||
- 🔄 **결제 주기 변경 기능**
|
||||
- **월간 ↔ 연간 변경**: 동일 플랜 내 결제 주기 변경 지원
|
||||
- **isBillingCycleChange 플래그**: 결제 주기만 변경 시 감지
|
||||
- **scheduledPlan.billingCycle**: 예약된 결제 주기 저장
|
||||
- **다음 결제일 자동 적용**: 만료 시 새 결제 주기로 전환
|
||||
- 👥 **팀 기반 AI 사용량 제한**
|
||||
- **teamMemberUsage**: 팀원별 AI 크레딧 사용량 추적
|
||||
- **팀 소유자 플랜 연동**: 팀원 AI 제한은 소유자 플랜 기준
|
||||
- **credits.ts**: checkAIFeatureAvailability(), deductAICredits() 함수
|
||||
|
||||
**업데이트** (2025-12-08 오후):
|
||||
- 💳 **AI 크레딧 환불 시스템**
|
||||
- **업그레이드 환불**: Prorated 계산 → AI 크레딧 지급
|
||||
- **환율**: 100원 = 10 크레딧
|
||||
@ -983,6 +1016,8 @@ firebase functions:log --only cleanupExpiredReservations
|
||||
|---------|--------|------|------|
|
||||
| **Team Code Generator** | `teamCodeGenerator.ts` | 한글/영어/일본어 팀 코드 생성 (형용사 + 색깔 + 동물, locale 파라미터) | ✅ 완료 |
|
||||
| **🆕 i18n 유틸리티** | `i18n.ts` | 서비스 레이어용 번역 함수 (detectLocale, t), nested key 지원, 파라미터 치환 | ✅ 완료 |
|
||||
| **🆕 Validation** | `validation.ts` | 폼 검증 유틸리티 (이메일/비밀번호/이름/비밀번호 확인), 타입 안전, 다국어 에러 메시지 | ✅ 완료 |
|
||||
| **🆕 SSE Stream Processor** | `sseStreamProcessor.ts` | Server-Sent Events 스트리밍 처리 (계정 병합 진행률 표시) | ✅ 완료 |
|
||||
| **Password Security** | `passwordSecurity.ts` | HIBP API 연동 (유출된 비밀번호 차단) | ✅ 완료 |
|
||||
| **Password Strength** | `passwordStrength.ts` | 비밀번호 강도 계산 | ✅ 완료 |
|
||||
| **Content Hash** | `contentHash.ts` | 🆕 **SHA-256 해시 생성** (글 목록 → 해시, id+updatedAt 조합), 🆕 **generateWritingContentHash** (개별 글 content 해시), 서버/클라이언트 지원 | ✅ 완료 |
|
||||
@ -999,6 +1034,7 @@ firebase functions:log --only cleanupExpiredReservations
|
||||
|------|--------|------|------|
|
||||
| **useRequireAuth** | `useRequireAuth.ts` | **인증 필수 페이지 훅** (비인증 시 자동 리다이렉트, additionalLoading 지원) | ✅ 완료 |
|
||||
| **useTeamData** | `useTeamData.ts` | 🆕 **팀 데이터 로딩 훅** (팀 + 멤버 로딩, 권한 체크, refreshMembers) | ✅ 완료 |
|
||||
| **🆕 useShakeAnimation** | `useShakeAnimation.ts` | **에러 Shake 애니메이션 훅** (에러 발생 시 자동 shake, getShakeAnimateProps 헬퍼, 중복 제거) | ✅ 완료 |
|
||||
| **useWritingInactivityDetection** | `useWritingInactivityDetection.ts` | **작성 멈춤 감지 훅** (N분 입력 없을 시 콜백, 타이머 리셋, 남은 시간) | ✅ 완료 |
|
||||
|
||||
---
|
||||
@ -1069,20 +1105,21 @@ firebase functions:log --only cleanupExpiredReservations
|
||||
|
||||
| 스토어 | 파일명 | 설명 | 상태 |
|
||||
|-------|--------|------|------|
|
||||
| **Auth Store** | `authStore.ts` | 인증 상태 및 로그인 다이얼로그 (단순화됨) | ✅ 완료 |
|
||||
| **Auth Store** | `authStore.ts` | 인증 상태 및 로그인 다이얼로그 (단순화됨, 🆕 **Firestore 실시간 구독**, aiCredits/plan/settings 자동 업데이트) | ✅ 완료 |
|
||||
| **User Progress Store** | `userProgressStore.ts` | 사용자 진행 상황 (레벨, XP) | ❌ 미구현 |
|
||||
| **Notification Store** | `notificationStore.ts` | 알림 상태 | ❌ 미구현 |
|
||||
|
||||
**Auth Store 아키텍처 (2025-11-07 단순화)**:
|
||||
- ✅ **user** - Firebase Auth 기반 통합 사용자 (익명 + 정식 계정)
|
||||
**Auth Store 아키텍처 (2025-11-07 단순화, 2025-12-09 리팩토링)**:
|
||||
- ✅ **user** - Firebase Auth + Firestore 결합 사용자 (익명 + 정식 계정, 🆕 **실시간 구독**)
|
||||
- ✅ **isAuthenticated** - 로그인 여부 (익명 포함)
|
||||
- ✅ **isLoading** - 🆕 **초기값 `true`로 변경 (2025-11-28)** - Auth 초기화 완료 전 리다이렉트 방지
|
||||
- ✅ **user.isAnonymous** - 익명/정식 계정 구분
|
||||
- ✅ **loginAsUser()** - 팀 코드 로그인 (PIN 제거)
|
||||
- ✅ **linkWithEmail()** - 이메일 계정 연결 (신규 계정 생성)
|
||||
- ✅ **linkWithGoogle()** - Google 계정 연결 (신규 계정 생성)
|
||||
- ✅ **mergeWithEmail()** - 기존 이메일 계정과 병합 (데이터 마이그레이션)
|
||||
- ✅ **mergeWithGoogle()** - 기존 Google 계정과 병합 (데이터 마이그레이션)
|
||||
- ✅ **mergeWithEmail()** - 기존 이메일 계정과 병합 (데이터 마이그레이션, 🆕 **performMerge 헬퍼**)
|
||||
- ✅ **mergeWithGoogle()** - 기존 Google 계정과 병합 (데이터 마이그레이션, 🆕 **performMerge 헬퍼**)
|
||||
- ✅ **🆕 initializeAuth()** - Firestore `onSnapshot` 실시간 구독 (aiCredits/plan/settings 자동 업데이트)
|
||||
- ❌ ~~currentStudent~~, ~~ownedStudents~~, ~~switchStudent()~~ 제거 (복잡도 감소)
|
||||
|
||||
---
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# 라온누리 - 개발 로드맵
|
||||
|
||||
> 최종 업데이트: 2025-12-08 (AI 크레딧 환불 시스템, 구매 플로우, 구독 관리)
|
||||
> 최종 업데이트: 2025-12-12 (팀 보안 설정 통합, 소유자 이전 기능)
|
||||
|
||||
초등학생을 위한 창작 글쓰기 교육 플랫폼 개발 계획
|
||||
|
||||
@ -145,6 +145,12 @@
|
||||
| **TeamCard AI 활성화 배지** | **team.aiEnabled === true일 때 주황색 배지 표시, LuSparkles 아이콘 사용, "AI 활성화" 텍스트, 다국어 지원 (team.list.aiEnabled, ko/en/ja)** | **2025-12-08** |
|
||||
| **UserSettingsDialog 구독 섹션** | **"내 구독" 탭 추가 (프로필/내 구독/환경설정), 현재 플랜 카드 (플랜 타입 배지, 출처 표시), 플랜 제한 정보 카드 (팀 AI 활성화, AI 분석, 글쓰기 도우미, 이미지 생성), 실시간 사용량 표시 (remaining/limit), Free 플랜 업그레이드 버튼 (새 창, locale 포함), SettingBox 컴포넌트 (투명 배경, 얇은 테두리, 블러 효과), SettingButton 컴포넌트 (브랜드 테두리, 호버 그라데이션), 로딩 상태 처리, 다국어 지원 (components.settingsDialog.subscription namespace, ko/en/ja 17개 키)** | **2025-12-08** |
|
||||
| **AI 크레딧 환불 시스템** | **업그레이드 시 Prorated 환불 → AI 크레딧 지급, FirestoreUser.aiCredits 필드 추가 (영구 사용, 월 제한 무시), 환율 100원 = 10 크레딧, AI 기능 비용 (분석 20, 이미지 100, 도우미 10), credits.ts 유틸리티 (calculateProratedRefund, isUpgrade, PLAN_MONTHLY_PRICES), planLimits.ts 상수 추가 (AI_FEATURE_COSTS, CREDIT_EXCHANGE_RATE, convertKRWToCredits), POST /api/user/purchase API (Mock 승인 + 환불 계산 + 크레딧 지급 + 플랜 업데이트, 원자적 처리), UserManager.purchasePlan() (creditsAdded 반환), PurchaseConfirmationDialog 컴포넌트 (플랜 정보, 가격 포맷팅, 월간/연간 배지, Dialog.Root 패턴), pricing 페이지 구매 플로우 (로그인 체크, 플랜별 분기, 크레딧 안내 toast), UserSettingsDialog 크레딧 표시 (주황색 배지, LuSparkles), 다국어 지원 (components.purchaseDialog, pricing.purchaseSuccessWithCredits, subscription.aiCredits/credits/creditsDescription, ko/en/ja), 향후 Toss Payments 연동 준비 (주석 포함), 타입 체크 통과** | **2025-12-08** |
|
||||
| **인증 플로우 리팩토링** | **로그인/회원가입 중복 로직 제거 (~105줄 감소), validation.ts 유틸리티 (이메일/비밀번호/이름 검증, 타입 안전), useShakeAnimation 훅 (에러 shake 애니메이션, useEffect 6회 중복 제거, getShakeAnimateProps 헬퍼), sseStreamProcessor.ts (SSE 스트리밍 처리 공통 함수), LoginForm/SignupForm 적용 (validation 유틸 + shake 훅), LoginDialog Google 핸들러 통합 (createAsyncHandler 팩토리 패턴), firebaseAuth.ts mergeAndLogin 통합 함수 (provider별 콜백), authStore.ts performMerge 헬퍼 함수 (Email/Google 병합 로직 통합), Firestore 실시간 구독 (onSnapshot, aiCredits/plan/settings 자동 업데이트, 로그아웃 시 구독 해제), /api/user route 응답 형식 버그 수정 ({user} 래핑), UserManager.createUser 방어 코드 강화, 타입 체크 통과** | **2025-12-09** |
|
||||
| **팀 기반 AI 사용량 제한** | **팀 AI 활성화 시 팀별 사용량 제한 적용, TeamAIUsage/TeamDailyImageUsage 타입 추가 (Team 문서 내 aiUsage 필드와 members[uid].dailyImage* 필드로 관리), PLAN_MEMBER_LIMITS (팀 멤버 제한), incrementTeamAIUsage/decrementTeamAIUsage 함수, 팀 멤버별 일일 이미지 생성 제한, 팀 월별 이미지 쿼터, 다국어 지원** | **2025-12-10** |
|
||||
| **플랜 다운그레이드 선택 옵션** | **DowngradeOptionDialog 컴포넌트 (즉시 적용/다음 결제일 선택), downgradeMode 파라미터 추가 (immediate/scheduled), 즉시 다운그레이드 시 크레딧 전환, GET /api/user/plan/estimate-refund API (예상 환불 크레딧 조회), isDowngrade/isUpgrade 공용 유틸로 분리 (src/lib/planUtils.ts), Pricing 페이지 통합, 다국어 지원 (downgradeDialog namespace, ko/en/ja)** | **2025-12-10** |
|
||||
| **결제 주기 변경 기능** | **동일 플랜 결제 주기 변경 (월간↔연간), scheduledPlan.billingCycle 필드 추가, 다음 결제일부터 적용, 월간 30일/연간 365일 구독 기간, billingCycleChangeSuccess 토스트 알림, 다국어 지원** | **2025-12-10** |
|
||||
| **팀 소유자 이전 기능** | **POST /api/team/[teamId]/transfer-ownership API, TeamMemberEditor 소유자 이전 메뉴, 소유자 역할 배지 표시, 다국어 지원 (transferOwnership, transferConfirm 등 ko/en/ja)** | **2025-12-12** |
|
||||
| **팀 보안 설정 통합** | **TeamSecuritySettingsDialog에 공개설정 통합 (isPublic 스위치, allowPublicWritings, description, coverImage), TeamInfoCard에 검색가능/불가능 태그 추가 (FaGlobe/FaEyeSlash), TeamPublicSettings 제거, SecurityLevelSelector searchable 속성 추가, 다국어 지원 (searchable/notSearchable ko/en/ja)** | **2025-12-12** |
|
||||
|
||||
### 🚧 진행 중
|
||||
|
||||
|
||||
@ -2439,7 +2439,7 @@ CREDIT_EXCHANGE_RATE
|
||||
|
||||
**UserSettingsDialog (구독 탭)**:
|
||||
```tsx
|
||||
<SettingBox>
|
||||
<GlassBox>
|
||||
<HStack justify="space-between">
|
||||
<Text>AI 크레딧</Text>
|
||||
<Badge colorPalette="orange">
|
||||
@ -2450,7 +2450,7 @@ CREDIT_EXCHANGE_RATE
|
||||
플랜 업그레이드 시 환불받은 크레딧입니다.
|
||||
월 제한을 초과해도 크레딧으로 AI 기능을 사용할 수 있어요.
|
||||
</Text>
|
||||
</SettingBox>
|
||||
</GlassBox>
|
||||
```
|
||||
|
||||
#### 향후 확장 (크레딧 차감)
|
||||
@ -2485,6 +2485,67 @@ if (planLimitExceeded) {
|
||||
|
||||
---
|
||||
|
||||
## 코드 품질 및 리팩토링
|
||||
|
||||
### 인증 플로우 리팩토링 (2025-12-09)
|
||||
|
||||
#### 목표
|
||||
로그인/회원가입 플로우의 중복 로직 제거, 유지보수성 향상
|
||||
|
||||
#### 제거된 중복 로직
|
||||
|
||||
**1. 이메일/비밀번호 검증** (~10줄 × 2파일 중복)
|
||||
- LoginForm.tsx, SignupForm.tsx에서 동일한 정규식 사용
|
||||
- **해결**: `validation.ts` 유틸리티 생성
|
||||
|
||||
**2. Shake 애니메이션** (~25줄 × 6회 중복)
|
||||
- 각 필드마다 useState + useEffect + motion.div 반복
|
||||
- **해결**: `useShakeAnimation` 커스텀 훅
|
||||
|
||||
**3. SSE 스트리밍 처리** (~30줄 × 2함수 중복)
|
||||
- mergeWithEmail/mergeWithGoogle에서 동일한 fetch + 파싱 로직
|
||||
- **해결**: `sseStreamProcessor.ts` 공통 함수
|
||||
|
||||
**4. 병합 로직** (~70줄 × 2함수 중복)
|
||||
- authStore의 mergeWithEmail/mergeWithGoogle 80% 동일 코드
|
||||
- **해결**: `performMerge` 헬퍼 함수 (provider별 콜백)
|
||||
|
||||
**5. firebaseAuth 병합 함수** (~30줄 × 2함수 중복)
|
||||
- mergeAndLoginWithEmail/mergeAndLoginWithGoogle 동일 패턴
|
||||
- **해결**: `mergeAndLogin` 제네릭 함수
|
||||
|
||||
#### 생성된 파일
|
||||
|
||||
```
|
||||
src/utils/validation.ts (+130줄) - 폼 검증 함수들
|
||||
src/utils/sseStreamProcessor.ts (+45줄) - SSE 처리
|
||||
src/hooks/useShakeAnimation.ts (+45줄) - Shake 애니메이션 훅
|
||||
```
|
||||
|
||||
#### 수정된 파일 및 코드 감소량
|
||||
|
||||
| 파일 | 감소량 | 개선점 |
|
||||
|------|--------|--------|
|
||||
| `LoginForm.tsx` | -35줄 | validation 유틸 + shake 훅 적용 |
|
||||
| `SignupForm.tsx` | -70줄 | validation 유틸 + shake 훅 적용 (4개 필드) |
|
||||
| `LoginDialog.tsx` | -10줄 | createAsyncHandler 팩토리 패턴 |
|
||||
| `firebaseAuth.ts` | -20줄 | mergeAndLogin 통합 함수 |
|
||||
| `authStore.ts` | -80줄 | performMerge 헬퍼 함수 |
|
||||
| **합계** | **-215줄** | **순 감소 ~105줄** |
|
||||
|
||||
#### 추가 개선사항
|
||||
|
||||
**Firestore 실시간 구독 (authStore.ts)**
|
||||
- `initializeAuth()`에서 `onSnapshot` 구독 시작
|
||||
- `aiCredits`, `plan`, `settings` 변경 시 자동 업데이트
|
||||
- 로그아웃 시 구독 자동 해제 (`unsubscribeUserDoc`)
|
||||
|
||||
**API 버그 수정**
|
||||
- `POST /api/user` 응답 형식 수정 (user → {user})
|
||||
- `UserManager.createUser()` 방어 코드 강화
|
||||
|
||||
---
|
||||
|
||||
## 참고 문서
|
||||
|
||||
- [PROJECT_STRUCTURE.md](./PROJECT_STRUCTURE.md) - 프로젝트 구조
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user