diff --git a/NEXT_TASKS.md b/NEXT_TASKS.md new file mode 100644 index 0000000..e9df313 --- /dev/null +++ b/NEXT_TASKS.md @@ -0,0 +1,281 @@ +# 다음 작업 계획 + +> 작성일: 2025-11-18 +> Phase 1 마무리 및 다음 단계 준비 + +--- + +## ✅ 현재 상태 + +- **Phase 1 진행률**: 99% 완료 +- **타입 체크**: ✅ 통과 (에러 0개) +- **최근 완료**: + - Level 1 중복 체크 로직 (UID 기반, Custom Token) + - 팀 나가기 기능 + - 서비스 레이어 i18n 유틸리티 + - firebaseAuth.ts 다국어 지원 + +--- + +## 🔴 최우선 작업 (오늘~내일) + +### 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 7ab3490..109668d 100644 --- a/PROJECT_STRUCTURE.md +++ b/PROJECT_STRUCTURE.md @@ -325,7 +325,7 @@ | ~~**SpellingErrorDisplay**~~ | ~~`SpellingErrorDisplay.tsx`~~ | ~~맞춤법 오류 표시~~ | ❌ 삭제됨 (하이라이트로 대체) | | **CreateTopicDialog** | `CreateTopicDialog.tsx` | 개인 주제 생성 다이얼로그 (태그 입력 UI) | ✅ 완료 | | **CreateTeamTopicDialog** | `CreateTeamTopicDialog.tsx` | 팀 주제 생성 다이얼로그 (템플릿 지원) | ✅ 완료 | -| **SavedDraftsDialog** | `SavedDraftsDialog.tsx` | 저장된 글조각 목록 다이얼로그 (불러오기/삭제) | ✅ 완료 | +| **SavedDraftsDialog** | `SavedDraftsDialog.tsx` | 저장된 글조각 목록 다이얼로그 (불러오기/삭제, **동기화 상태 배지** 🟢synced/🟡syncing/⚪local) | ✅ 완료 | **주요 기능**: - ✅ 순수 텍스트 입력 (포맷팅 없음) @@ -469,7 +469,7 @@ | **ManagerBase** | `ManagerBase.ts` | 공통 기능 (authenticatedFetch, API 호출, 캐싱) | ✅ 완료 | | **TeamManager** | `TeamManager.ts` | 팀 관련 API 호출 (생성, 조회, 수정, 삭제, 멤버 관리) | ✅ 완료 | | **UserManager** | `UserManager.ts` | 사용자 관련 API 호출 (생성, 조회, 수정, 닉네임 관리) **[NEW]** | ✅ 완료 | -| **DraftManager** | `DraftManager.ts` | 글조각 관리 (localStorage 기반, 최대 10개, CRUD) | ✅ 완료 | +| **DraftManager** | `DraftManager.ts` | 글조각 관리 (**localStorage + Realtime DB 하이브리드**, 기기 간 동기화, 최대 10개, CRUD, syncStatus) | ✅ 완료 | | **WritingSessionManager** | `WritingSessionManager.ts` | 🆕 **실시간 글쓰기 세션 관리** (Firebase Realtime DB 작업) | ✅ 완료 | | ~~**StudentManager**~~ | ~~`StudentManager.ts`~~ | ~~학생 관련 API 호출~~ | ⚠️ Deprecated (UserManager로 대체) | | **WritingManager** | `WritingManager.ts` | 글쓰기 관련 비즈니스 로직 (CRUD, 통계) | ✅ 완료 | @@ -494,6 +494,41 @@ - `requestPreview(targetUserId)`: 미리보기 요청 (Promise 반환) - `listenForPreviewRequests(onRequestCallback)`: 미리보기 요청 리스너 (학생용) +--- + +### 🆕 `functions/` - Firebase Cloud Functions (서버리스 백엔드) + +Firebase Cloud Functions로 구현된 백그라운드 작업 및 자동화 로직입니다. + +| Function | 파일 | 타입 | 스케줄/Trigger | 설명 | 상태 | +|----------|------|------|----------------|------|------| +| **cleanupExpiredReservations** | `cleanup.ts` | Scheduled | 매 시간 0분 | 만료된 팀 코드 예약 정리 (expiresAt < now) | ✅ 배포됨 | +| **cleanupExpiredDrafts** | `triggers/drafts.ts` | Scheduled | 매일 새벽 3시 | 180일 이상 오래된 drafts 정리 (Realtime DB) | ✅ 배포됨 | +| **onTeamDeleted** | `triggers/team.ts` | Firestore Trigger | teams/{teamId} 삭제 | 팀 주제, 모니터링 데이터 cascade 삭제 | ✅ 배포됨 | +| **onWritingCreated** | `triggers/writing.ts` | Firestore Trigger | writings/{writingId} 생성 | 글 생성 감지 (추후 백그라운드 분석) | ✅ 배포됨 | + +**주요 특징**: +- ✅ **서버리스**: 별도 서버 관리 불필요, 자동 스케일링 +- ✅ **한글 로그**: 모든 logger 메시지 한글 작성 +- ✅ **리전**: asia-northeast1 (도쿄, 한국과 가장 가까움) +- ✅ **파일 분리**: index.ts에서는 export만, 로직은 별도 파일 +- ✅ **에러 처리**: 일부 실패해도 전체 작업 계속 진행 + +**배포 명령어**: +```bash +cd functions +npm run build +npm run deploy +``` + +**로그 확인**: +```bash +firebase functions:log +firebase functions:log --only cleanupExpiredReservations +``` + +--- + ### 📁 `src/services/` - 데이터 레이어 (일부 deprecated) | 서비스 | 파일명 | 설명 | 상태 | @@ -528,7 +563,7 @@ | **FirestoreUser 타입** | `firestoreUser.ts` | **FirestoreUser** (DB 저장용), **User** (UI용) 분리 | ✅ 완료 | | **Writing 타입** | `writing.ts` | 글 데이터 모델, 🆕 **WritingAnalysis** (AI 분석 결과, contentHash 기반 재사용), **SpellingError** (맞춤법 오류), 🆕 **AIAssistanceRecord** (AI 도움 이력), 🆕 **GeneratedImage** (AI 생성 이미지) | ✅ 완료 | | **Scene 타입** | `scene.ts` | 🆕 **장면 데이터 모델** (Scene, SceneExtractionResponse) | ✅ 완료 | -| **Draft 타입** | `draft.ts` | 글조각 데이터 모델 (Draft, DraftListItem, **AnalysisHistoryItem**) | ✅ 완료 | +| **Draft 타입** | `draft.ts` | 글조각 데이터 모델 (Draft, DraftListItem, **AnalysisHistoryItem**, **syncStatus**: 'local'\|'synced'\|'syncing') | ✅ 완료 | | **WritingPattern 타입** | `writingPattern.ts` | **글 작성 패턴 분석** 데이터 모델 (WritingPatternAnalysis) | ✅ 완료 | | **WritingSession 타입** | `writingSession.ts` | 🆕 **실시간 모니터링** 데이터 모델 (WritingStats, PreviewRequest/Response, MonitoringData) | ✅ 완료 | | ~~**User 타입**~~ | ~~`user.ts`~~ | ~~사용자 데이터 모델~~ | ⚠️ Deprecated (firestoreUser.ts로 변경) | @@ -663,6 +698,18 @@ project_w/ ├── database.rules.json # 🆕 Firebase Realtime DB Security Rules │ +├── functions/ # 🆕 Firebase Cloud Functions (서버리스 백엔드) +│ ├── src/ +│ │ ├── index.ts # Entry point (export만) +│ │ ├── cleanup.ts # cleanupExpiredReservations (매 시간) +│ │ └── triggers/ +│ │ ├── drafts.ts # cleanupExpiredDrafts (매일 새벽 3시) +│ │ ├── team.ts # onTeamDeleted (Firestore Trigger) +│ │ └── writing.ts # onWritingCreated (Firestore Trigger) +│ ├── lib/ # 빌드 결과 (tsc) +│ ├── package.json +│ └── tsconfig.json +│ ├── messages/ # 🆕 다국어 번역 파일 │ ├── ko.json # 한국어 번역 │ └── en.json # 영어 번역 @@ -734,7 +781,7 @@ project_w/ │ ├── TopicManager.ts # 주제 관리 │ ├── TeamManager.ts # 팀 관리 │ ├── UserManager.ts # 사용자 관리 -│ ├── DraftManager.ts # 글조각 관리 (localStorage) +│ ├── DraftManager.ts # 글조각 관리 (localStorage + Realtime DB 하이브리드) │ └── [미구현] │ ├── LevelManager.ts │ └── StickerManager.ts diff --git a/ROADMAP.md b/ROADMAP.md index 83e674d..4a107f5 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,6 +1,6 @@ # 라온누리 - 개발 로드맵 -> 최종 업데이트: 2025-11-18 (팀 코드 다국어 생성 + Realtime DB 예약) +> 최종 업데이트: 2025-11-19 (AI Delta 개선 + Draft 동기화 + Firebase Functions Phase 1) 초등학생을 위한 창작 글쓰기 교육 플랫폼 개발 계획 @@ -63,12 +63,12 @@ | **API Routes 구현** | **보안 레벨 변경, 명단 관리 API (RESTful 원칙), POST/DELETE 메서드 구분** | **2025-11-10** | | **lib/server/team 확장** | **updateTeamSecurityLevel, add/removeAllowedName/Email, 자동 명단 생성** | **2025-11-10** | | **react-icons 전환** | **이모티콘 → react-icons/lu로 전환 (LuGlobe, LuClipboardList 등)** | **2025-11-10** | -| **다중 글조각 관리** | **DraftManager (localStorage, 최대 10개), SavedDraftsDialog, 2초 debounce 자동 저장, 저장 상태 표시** | **2025-11-10** | +| **다중 글조각 관리** | **DraftManager (localStorage + Realtime DB 하이브리드, 기기 간 동기화, 최대 10개), SavedDraftsDialog (syncStatus 배지), 2초 debounce 자동 저장** | **2025-11-10 / 2025-11-19** | | **테마 슬롯 레시피** | **Dialog, Select slot recipe 추가 (자동 배경색, border, shadow)** | **2025-11-10** | | **TopicSelector 그룹핑** | **ItemGroup으로 자유/팀/개인 주제 구분, 팀 주제에 팀 이름 표시, Separator 추가** | **2025-11-10** | | **TopicOption 확장** | **teamName 필드 추가, TopicManager에서 팀 정보 조회 (동적 import)** | **2025-11-10** | | **주제 변경 경고 Dialog** | **작성 중 내용이 있을 때 주제 변경 시 경고 Dialog 표시, 임시 저장 안내** | **2025-11-10** | -| **실시간 피드백 시스템** | **Vertex AI 기반 텍스트 분석, ScoreDisplay 컴포넌트, Delta 전송 (토큰 40% 절감), 서버 캐싱 (LRU)** | **2025-11-11** | +| **실시간 피드백 시스템** | **Vertex AI 기반 텍스트 분석, ScoreDisplay 컴포넌트, Delta 전송 (diff-match-patch 기반, 5자 미만 누적, 80% 기준), 서버 캐싱 (LRU)** | **2025-11-11 / 2025-11-19** | | **Multi-Region Failover** | **3개 region 자동 전환 (도쿄/싱가포르/미국), Region health tracking, Exponential backoff, RPM 3배 증가 (15→45)** | **2025-11-11** | | **서비스 레이어 분리** | **vertexAI.ts (범용 래퍼), textAnalysisService.ts (분석 로직), regionHealthManager.ts (상태 관리)** | **2025-11-11** | | **문서화 완료** | **TECHNICAL_IMPLEMENTATION.md, SERVICE_DIRECTION.md 전면 개편 (Vertex AI 기반)** | **2025-11-11** | @@ -96,6 +96,10 @@ | **팀 나가기 기능** | **팀 상세 페이지에 "팀 나가기" 버튼 추가 (멤버만 표시), Dialog 확인창, teamManager.removeMember() 호출, POST /api/team/remove-member 권한 체크 수정 (본인 제거 허용, 소유자 제거 금지), 성공 시 팀 목록으로 리다이렉트, 번역 추가 (team.detail namespace, leaveTeam/leaveTeamConfirm 등 7개 키, ko/en/ja)** | **2025-11-18** | | **Level 1 중복 체크 로직 (UID 기반)** | **loginAsUser() 함수 수정 (currentUid 파라미터 추가), Level 1 닉네임 중복 체크 (team.members 검색), 4가지 케이스 처리 (비로그인/로그인 × 중복 유무), Custom Token API 생성 (POST /api/team/get-custom-token, 익명 계정만 발급), 비로그인 상태 중복 시 기존 계정으로 자동 로그인, 로그인 상태 중복 시 에러 (UID 일치 체크), 정식 계정 탈취 방지 (providerData 체크), authStore.loginAsUser에서 currentUid 전달, StudentLoginFlow 에러 메시지 처리, 번역 추가 (errors.team namespace, alreadyJoinedTeam/nicknameInUse 등 6개 키, ko/en/ja)** | **2025-11-18** | | **서비스 레이어 i18n 유틸리티** | **src/utils/i18n.ts 생성 (React 훅 없이 번역 사용), detectLocale() 함수 (URL path 우선 → navigator.language fallback), t() 함수 (nested key 지원, 파라미터 치환), messages/*.json import, firebaseAuth.ts 전체 에러 메시지 다국어 처리 (getErrorMessage, loginAsUser, linkEmailPassword, linkGoogleAccount), convertFirebaseUser 기본 이름 다국어, 번역 추가 (errors.auth namespace 11개 + errors.team namespace 6개, ko/en/ja)** | **2025-11-18** | +| **AI Delta 전송 로직 개선** | **diff-match-patch 라이브러리 도입, 정확한 diff 계산 (앞/중간/뒤 수정 모두 감지), 5자 미만 변경 누적 (previousText 유지), 완화된 기준 (80% 미만, 200자 미만), 9개 테스트 케이스 통과 (뒷부분 추가, 중간 수정, 앞부분 추가, 삭제, 대량 변경, 누적 변경), skipped 응답 처리 (클라이언트에서 prev 유지), analyze-text API 수정** | **2025-11-19** | +| **Draft 클라우드 동기화** | **localStorage + Realtime DB 하이브리드 저장, syncStatus 필드 추가 ('local'\|'synced'\|'syncing'), DraftManager 확장 (syncToCloud, loadDraftsFromCloud, mergeDrafts, deleteDraftFromCloud), SavedDraftsDialog 배지 표시 (🟢동기화됨/🟡동기화 중/⚪기기만), write/page.tsx mergeDrafts 호출 (로그인 시), database.rules.json drafts 규칙 추가 (본인만 읽기/쓰기), 기기 간 동기화 (학교 ↔ 집), updatedAt 비교로 최신 버전 선택** | **2025-11-19** | +| **팀 코드 예약 반환 로직** | **generate-code API 수정 (previousCode 파라미터 추가), 새 코드 받기 시 이전 예약 자동 해제, team/create page 중복 호출 방지 (isGeneratingCode 체크), 팀 생성 실패 시에도 예약 해제 (catch 블록), GenerateTeamCodeRequest 타입 확장, TeamManager.generateUniqueTeamCode previousCode 파라미터** | **2025-11-19** | +| **Firebase Cloud Functions Phase 1** | **functions/ 프로젝트 초기화 (Node.js 22, v2 API), 파일 분리 구조 (index.ts export만, 로직 별도 파일), cleanupExpiredReservations (매 시간, 팀 코드 예약 정리), cleanupExpiredDrafts (매일 새벽 3시, 180일 이상 drafts 정리), onTeamDeleted (Firestore Trigger, cascade 삭제), onWritingCreated (Firestore Trigger, 추후 자동 분석), 한글 로그, asia-northeast1 리전, firebase.json predeploy 수정 (lint 제거), 4개 함수 배포 완료** | **2025-11-19** | ### 🚧 진행 중 diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..d3afdc8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,260 @@ +# Security Policy + +라온누리 프로젝트의 보안 정책 및 구현 내역을 문서화합니다. + +--- + +## 🔒 보안 조치 요약 + +| 보안 영역 | 조치 | 상태 | 적용일 | +|----------|------|------|--------| +| **XSS 방지** | HTML Sanitization | ✅ 적용 완료 | 2025-11-19 | +| **인증** | Firebase Auth | ✅ 적용 완료 | 2025-10-xx | +| **API 인증** | JWT Token (ID Token) | ✅ 적용 완료 | 2025-10-xx | +| **SQL Injection** | Firestore (NoSQL) | ✅ 원천 방지 | - | +| **CSRF** | SameSite Cookies | 🚧 검토 필요 | - | +| **Rate Limiting** | API 요청 제한 | ⏳ 예정 | - | + +--- + +## 1. XSS (Cross-Site Scripting) 방지 + +### 문제점 +- 사용자가 작성한 글 (`writings` 컬렉션)의 `content` 필드는 **HTML 문자열**로 저장됩니다. +- Tiptap 에디터는 안전한 HTML을 생성하지만, 악의적인 사용자가 **API를 직접 호출**하여 악성 스크립트를 주입할 수 있습니다. + +**공격 시나리오**: +```typescript +// 악의적인 API 호출 +POST /api/writing +{ + "title": "정상 제목", + "content": "

정상 내용

" +} +``` + +### 해결책: HTML Sanitization + +**백엔드 자동 세탁** (`src/lib/server/writing.ts`): +```typescript +import { sanitizeHtml } from "@/utils/sanitizeHtml"; + +// 글 생성 시 자동 세탁 (Line 46-47) +const sanitizedTitle = sanitizeHtml(data.title); +const sanitizedContent = sanitizeHtml(data.content); + +// 글 수정 시 자동 세탁 (Line 199-203) +if (data.title) { + updateData.title = sanitizeHtml(data.title); +} +if (data.content) { + updateData.content = sanitizeHtml(data.content); +} +``` + +**세탁 규칙** (`src/utils/sanitizeHtml.ts`): +- ✅ **허용된 태그**: `

`, ``, ``, ``, `