plan.md

Tue, 21 Oct 2025 06:52:36 GMT

# Firebase Todo App - 코드 설계도 및 프롬프트 엔지니어링 가이드 **작성일**: 2025년 10월 18일 **프로젝트**: todo-app **목적**: 처음부터 끝까지 오류 없이 Todo 앱을 구축하기 위한 완전한 가이드 --- ## 📋 목차 1. [프로젝트 아키텍처 개요](https://#1-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B0%9C%EC%9A%94) 2. [컴포넌트 계층 구조](https://#2-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EA%B3%84%EC%B8%B5-%EA%B5%AC%EC%A1%B0) 3. [데이터 플로우](https://#3-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%94%8C%EB%A1%9C%EC%9A%B0) 4. [핵심 기능별 설계](https://#4-%ED%95%B5%EC%8B%AC-%EA%B8%B0%EB%8A%A5%EB%B3%84-%EC%84%A4%EA%B3%84) 5. [프롬프트 엔지니어링 가이드](https://#5-%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81-%EA%B0%80%EC%9D%B4%EB%93%9C) 6. [주의사항 및 문제 해결](https://#6-%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD-%EB%B0%8F-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0) 7. [배포 가이드](https://#7-%EB%B0%B0%ED%8F%AC-%EA%B0%80%EC%9D%B4%EB%93%9C) --- ## 1. 프로젝트 아키텍처 개요 ### 1.1 기술 스택 ``` Frontend Framework: React 19.1.1 + TypeScript 5.9.3 Build Tool: Vite 7.1.7 Routing: React Router DOM 7.9.4 Backend: Firebase (Authentication + Firestore) Internationalization: i18next + react-i18next Styling: Bootstrap 5 (via CDN) + CSS State Management: React Context API (Theme) ``` ### 1.2 프로젝트 구조 ``` todo-app/ ├── public/ # 정적 파일 │ └── vite.svg ├── src/ │ ├── components/ # React 컴포넌트 │ │ ├── AddTodoForm.tsx # Todo 추가 폼 │ │ ├── TodoItem.tsx # Todo 단일 아이템 │ │ └── TodoList.tsx # Todo 목록 컨테이너 │ ├── context/ # React Context │ │ └── ThemeContext.tsx # 다크모드 테마 관리 │ ├── firebase/ # Firebase 설정 및 서비스 │ │ ├── config.ts # Firebase 초기화 │ │ └── services.ts # Firestore CRUD 함수 │ ├── hooks/ # Custom React Hooks │ │ ├── useAuth.ts # 인증 상태 관리 │ │ └── useTodos.ts # Todo 목록 관리 │ ├── locales/ # 다국어 번역 파일 │ │ ├── en/translation.json # 영어 │ │ ├── ko/translation.json # 한국어 │ │ └── ja/translation.json # 일본어 │ ├── pages/ # 페이지 컴포넌트 │ │ ├── HomePage.tsx # 메인 페이지 (Todo 관리) │ │ └── LoginPage.tsx # 로그인/회원가입 페이지 │ ├── App.tsx # 앱 라우팅 │ ├── main.tsx # 앱 진입점 │ ├── i18n.ts # i18next 설정 │ ├── App.css # 앱 스타일 │ └── index.css # 전역 스타일 ├── firestore.rules # Firestore 보안 규칙 ├── firestore.indexes.json # Firestore 인덱스 ├── firebase.json # Firebase 설정 ├── .firebaserc # Firebase 프로젝트 ID ├── package.json # 의존성 관리 ├── tsconfig.json # TypeScript 설정 └── vite.config.ts # Vite 설정 ``` ### 1.3 주요 의존성 ``` { "dependencies": { "firebase": "^12.4.0", "i18next": "^25.6.0", "i18next-browser-languagedetector": "^8.2.0", "react": "^19.1.1", "react-dom": "^19.1.1", "react-i18next": "^16.1.0", "react-router-dom": "^7.9.4" }, "devDependencies": { "@types/node": "^24.6.0", "@types/react": "^19.1.16", "@types/react-dom": "^19.1.9", "@vitejs/plugin-react": "^5.0.4", "firebase-tools": "^14.20.0", "typescript": "~5.9.3", "vite": "^7.1.7" } } ``` --- ## 2. 컴포넌트 계층 구조 ``` App (라우팅) ├── LoginPage (비인증) │ └── 로그인/회원가입 폼 └── HomePage (인증 후) ├── Header │ ├── 제목 │ ├── 언어 선택기 │ ├── 테마 토글 버튼 │ └── 로그아웃 버튼 ├── AddTodoForm │ ├── 텍스트 입력 │ └── 추가 버튼 └── TodoList └── TodoItem[] (반복) ├── 체크박스 ├── 텍스트 └── 삭제 버튼 ``` --- ## 3. 데이터 플로우 ``` ┌─────────────────────────────────────────────────────────────┐ │ Firebase Cloud │ │ ┌────────────────┐ ┌─────────────────┐ │ │ │ Authentication │ │ Firestore DB │ │ │ │ (Auth SDK) │ │ (todos 컬렉션) │ │ │ └────────────────┘ └─────────────────┘ │ └──────────┬──────────────────────────────┬──────────────────┘ │ │ │ onAuthStateChanged │ onSnapshot (실시간) │ │ ┌──────────▼──────────────────────────────▼──────────────────┐ │ Frontend (React) │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ main.tsx (전역 AbortError 차단 시스템) │ │ │ └───────────────────────────────────────────────────────┘ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ App.tsx (React Router) │ │ │ │ ├─ useAuth() → user, loading │ │ │ │ └─ Route 분기 (인증/비인증) │ │ │ └───────────────────────────────────────────────────────┘ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ HomePage │ │ │ │ ├─ useTodos() → todos[], loading │ │ │ │ ├─ ThemeContext → theme, toggleTheme │ │ │ │ ├─ i18next → t(), i18n │ │ │ │ └─ Components: AddTodoForm, TodoList │ │ │ └───────────────────────────────────────────────────────┘ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Firebase Services (CRUD) │ │ │ │ ├─ addTodo(userId, text) │ │ │ │ ├─ getTodos(userId, callback) → unsubscribe │ │ │ │ ├─ toggleTodoCompleted(id, completed) │ │ │ │ └─ deleteTodo(id) │ │ │ └───────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## 4. 핵심 기능별 설계 ### 4.1 인증 시스템 (Authentication) ``` // useAuth Hook 사용자 인증 상태를 실시간으로 추적 - onAuthStateChanged 리스너 등록 - user, loading 상태 반환 - cleanup 함수로 메모리 누수 방지 // LoginPage - 이메일/비밀번호 입력 - 회원가입: createUserWithEmailAndPassword - 로그인: signInWithEmailAndPassword - 에러 핸들링: AbortError 필터링 ``` ### 4.2 Todo CRUD 시스템 ``` // Firestore 데이터 구조 interface Todo { id: string; text: string; completed: boolean; createdAt: Timestamp; userId: string; } // 보안 규칙 - allow list: 인증된 사용자만 쿼리 가능 - allow get: 자신의 문서만 읽기 가능 - allow create: 자신의 userId로만 생성 - allow update/delete: 자신의 문서만 수정/삭제 ``` ### 4.3 실시간 동기화 (Real-time Sync) ``` // useTodos Hook - getTodos() → onSnapshot 리스너 등록 - userId로 필터링된 쿼리 - createdAt 기준 내림차순 정렬 - 실시간 업데이트 자동 반영 - cleanup 시 unsubscribe() 호출 ``` ### 4.4 다국어 지원 (i18n) ``` // 지원 언어: en (영어), ko (한국어), ja (일본어) // 브라우저 언어 자동 감지 // 사용자가 수동으로 언어 변경 가능 // 모든 UI 텍스트 번역 파일로 관리 ``` ### 4.5 다크모드 (Theme) ``` // ThemeContext - localStorage에 테마 저장 - Bootstrap 5의 data-bs-theme 속성 활용 - light/dark 토글 - 앱 전체에 Context로 제공 ``` ### 4.6 AbortError 차단 시스템 ``` // 3단계 방어 시스템 1. fetch 오버라이드: AbortError를 소스에서 차단 2. unhandledrejection: 놓친 에러 캐치 3. console.error 필터링: inspector.js 로그 차단 ``` --- ## 5. 프롬프트 엔지니어링 가이드 각 컴포넌트를 **오류 없이 한 번에 작성**하기 위한 프롬프트입니다. ### Step 1: 프로젝트 초기화 ``` React + TypeScript + Vite로 Todo 앱을 만들고 싶어요. 다음 요구사항을 충족하는 프로젝트를 생성해주세요: 1. React 19 + TypeScript 5 + Vite 7 사용 2. Firebase Authentication + Firestore 사용 3. React Router DOM으로 라우팅 4. i18next로 다국어 지원 (영어, 한국어, 일본어) 5. Bootstrap 5 스타일링 (CDN 사용) 다음 명령어로 프로젝트를 생성하고 필요한 패키지를 설치해주세요: npm create vite@latest todo-app -- --template react-ts cd todo-app npm install 그리고 다음 패키지들을 추가 설치해주세요: npm install firebase react-router-dom i18next react-i18next i18next-browser-languagedetector 개발 도구도 설치해주세요: npm install -D firebase-tools @types/node package.json을 확인하고 모든 의존성이 올바르게 설치되었는지 확인해주세요. ``` --- ### Step 2: Firebase 설정 파일 생성 ``` Firebase 프로젝트를 설정하고 싶어요. 다음 파일들을 생성해주세요: 1. src/firebase/config.ts - Firebase 초기화 파일 - Vite 환경 변수 (import.meta.env.VITE_*) 사용 - getAuth, getFirestore import - 환경 변수 검증 로직 포함 2. src/firebase/services.ts - Firestore CRUD 서비스 - Todo 인터페이스 정의 (id, text, completed, createdAt, userId) - addTodo(userId, text) - 새 Todo 추가 - getTodos(userId, callback) - 실시간 리스너 (where userId, orderBy createdAt desc) - toggleTodoCompleted(docId, completed) - 완료 상태 토글 - deleteTodo(docId) - Todo 삭제 - 모든 함수에 AbortError 처리 포함 (error.name === 'AbortError'면 조용히 반환) - permission-denied 에러는 명확한 메시지로 로깅 3. firestore.rules - Firestore 보안 규칙 - todos 컬렉션에 대해: - allow list: 인증된 사용자만 - allow get: 자신의 문서만 - allow create: 자신의 userId로만 - allow update, delete: 자신의 문서만 4. firebase.json - Firebase 프로젝트 설정 - firestore rules 및 indexes 경로 지정 5. .env.local 템플릿 생성 - VITE_API_KEY, VITE_AUTH_DOMAIN, VITE_PROJECT_ID 등 환경 변수 예시 각 파일의 전체 코드를 TypeScript와 Firebase v10+ 최신 문법으로 작성해주세요. React 17+ 에서는 JSX 변환 시 `import React`가 불필요하므로 포함하지 마세요. type import는 `import type { ... }`나 `type ReactNode` 형식으로 작성해주세요. ``` --- ### Step 3: Custom Hooks 생성 ``` React Custom Hooks를 만들고 싶어요. 다음 두 개의 hook을 생성해주세요: 1. src/hooks/useAuth.ts - Firebase Authentication 상태를 관리하는 hook - onAuthStateChanged 리스너 등록 - user (User | null)와 loading (boolean) 상태 반환 - useEffect cleanup에서 unsubscribe 호출 - TypeScript 타입 안전성 보장 2. src/hooks/useTodos.ts - useAuth hook 사용하여 현재 사용자 가져오기 - getTodos 서비스 사용하여 실시간 리스너 등록 - todos (Todo[])와 loading (boolean) 상태 반환 - user가 없으면 빈 배열 반환 - useEffect cleanup에서 리스너 해제 주의사항: - React 17+이므로 `import React` 불필요 - type import는 `import type { User }` 형식 사용 - dependency array 정확히 설정 - 메모리 누수 방지를 위한 cleanup 함수 필수 ``` --- ### Step 4: Context 생성 (Theme) ``` 다크모드를 지원하는 ThemeContext를 만들고 싶어요. src/context/ThemeContext.tsx 파일을 생성해주세요: 요구사항: 1. Theme 타입: 'light' | 'dark' 2. ThemeContextType 인터페이스: { theme: Theme, toggleTheme: () => void } 3. ThemeProvider 컴포넌트: - localStorage에서 초기 테마 로드 (기본값: 'light') - theme이 변경되면 document.documentElement.setAttribute('data-bs-theme', theme) 실행 - localStorage에 theme 저장 4. useTheme 커스텀 hook: - ThemeContext 사용 - context가 undefined면 에러 발생 주의사항: - type ReactNode는 `import { type ReactNode }` 형식으로 import - React import 불필요 - children props 타입 명시 ``` --- ### Step 5: i18n 설정 ``` 다국어 지원 설정을 해주세요. 1. src/i18n.ts 파일 생성: - i18next, react-i18next, i18next-browser-languagedetector import - 영어(en), 한국어(ko), 일본어(ja) 번역 파일 import - LanguageDetector 사용하여 브라우저 언어 자동 감지 - fallbackLng: 'en' - interpolation.escapeValue: false 2. 번역 파일 생성: - src/locales/en/translation.json - src/locales/ko/translation.json - src/locales/ja/translation.json 번역 키: - loginTitle: "Login or Sign Up" / "로그인 또는 회원가입" / "ログインまたはサインアップ" - emailPlaceholder: "Email" / "이메일" / "Eメール" - passwordPlaceholder: "Password" / "비밀번호" / "パスワード" - loginButton, signupButton, logoutButton - todoTitle: "Todo List" / "할 일 목록" / "Todoリスト" - addTaskButton, taskPlaceholder, deleteButton - noTasks: "No tasks yet. Add one above!" - loading: "Loading..." / "로딩 중..." / "読み込み中..." - adding, deleting (로딩 상태 텍스트) 모든 파일을 JSON 형식으로 작성해주세요. ``` --- ### Step 6: 컴포넌트 생성 - AddTodoForm ``` Todo 추가 폼 컴포넌트를 만들고 싶어요. src/components/AddTodoForm.tsx 파일을 생성해주세요: 요구사항: 1. Props: { userId: string } 2. 로컬 상태: - text (string) - 입력 텍스트 - isAdding (boolean) - 추가 중 상태 3. handleSubmit 함수: - e.preventDefault() 호출 - text.trim() 검증 - isAdding이면 early return - setIsAdding(true) 설정 - try-catch-finally로 addTodo 호출 - 성공 시 setText('') 로 입력 필드 초기화 - permission-denied 에러는 명확히 로깅 - AbortError는 조용히 처리 - finally에서 setIsAdding(false) 4. UI: - form onSubmit={handleSubmit} - Bootstrap input-group 사용 - input: type="text", placeholder={t('taskPlaceholder')} - button: type="submit", {isAdding ? t('adding') : t('addTaskButton')} - isAdding 중에는 input과 button disabled 주의사항: - useTranslation() hook 사용 - React import 불필요 - async/await 사용 ``` --- ### Step 7: 컴포넌트 생성 - TodoItem ``` 개별 Todo 아이템 컴포넌트를 만들고 싶어요. src/components/TodoItem.tsx 파일을 생성해주세요: 요구사항: 1. Props: { todo: Todo } 2. 로컬 상태: - isToggling (boolean) - 토글 중 - isDeleting (boolean) - 삭제 중 3. handleToggle 함수: - isToggling이면 early return - setIsToggling(true) - try-catch-finally로 toggleTodoCompleted 호출 - permission-denied와 AbortError 처리 - finally에서 setIsToggling(false) 4. handleDelete 함수: - isDeleting이면 early return - setIsDeleting(true) - try-catch-finally로 deleteTodo 호출 - permission-denied와 AbortError 처리 - finally에서 setIsDeleting(false) 5. UI: - li.list-group-item - checkbox: checked={todo.completed}, onChange={handleToggle} - label: todo.completed이면 text-decoration-line-through, text-muted 클래스 - button: onClick={handleDelete}, {isDeleting ? t('deleting') : t('deleteButton')} - isToggling이나 isDeleting 중에는 모든 컨트롤 disabled 주의사항: - useTranslation() hook 사용 - async/await 사용 - 중복 클릭 방지 필수 ``` --- ### Step 8: 컴포넌트 생성 - TodoList ``` Todo 목록을 렌더링하는 간단한 컨테이너 컴포넌트를 만들어주세요. src/components/TodoList.tsx 파일을 생성해주세요: 요구사항: 1. Props: { todos: Todo[] } 2. UI: - ul.list-group - todos.map()으로 TodoItem 렌더링 - key={todo.id}로 설정 주의사항: - React import 불필요 - React.FC 타입 명시 - TodoItem import ``` --- ### Step 9: 페이지 생성 - LoginPage ``` 로그인/회원가입 페이지를 만들어주세요. src/pages/LoginPage.tsx 파일을 생성해주세요: 요구사항: 1. 로컬 상태: - email (string) - password (string) - error (string | null) 2. handleSignUp 함수: - React.MouseEvent 타입 - e.preventDefault() 호출 - email, password 검증 - try-catch로 createUserWithEmailAndPassword 호출 - AbortError는 조용히 처리 - 다른 에러는 setError(err.message) 3. handleLogIn 함수: - handleSignUp과 유사하지만 signInWithEmailAndPassword 사용 4. UI: - Bootstrap card 레이아웃 - h2: {t('loginTitle')} - input email: placeholder={t('emailPlaceholder')} - input password: placeholder={t('passwordPlaceholder')} - button 로그인: onClick={handleLogIn}, type="submit" - button 회원가입: onClick={handleSignUp}, type="button" - error가 있으면 alert 표시 주의사항: - useTranslation() hook 사용 - React 19의 event handler 타입 사용 - async/await 사용 ``` --- ### Step 10: 페이지 생성 - HomePage ``` 메인 페이지 (Todo 관리)를 만들어주세요. src/pages/HomePage.tsx 파일을 생성해주세요: 요구사항: 1. Hooks: - useAuth() → user - useTodos() → todos, loading - useTheme() → theme, toggleTheme - useTranslation() → t, i18n 2. handleLogout 함수: - signOut(auth).catch() - AbortError 조용히 처리 3. handleLanguageChange 함수: - i18n.changeLanguage(lng) 호출 4. UI 구조: - card.card-header: - h1: {t('todoTitle')} - select: 언어 선택 (EN, KO, JA) - button: 테마 토글 ({theme === 'dark' ? '🌙' : '☀️'}) - button: 로그아웃 {t('logoutButton')} - card.card-body: - AddTodoForm (user가 있을 때만) - loading이면 spinner 표시 - todos.length === 0이고 loading이 아니면 {t('noTasks')} - todos.length > 0이면 TodoList 주의사항: - 모든 컴포넌트와 hook import - Bootstrap 클래스 사용 - 조건부 렌더링 정확히 구현 ``` --- ### Step 11: App 라우팅 설정 ``` 앱의 라우팅을 설정해주세요. src/App.tsx 파일을 생성해주세요: 요구사항: 1. useAuth() hook으로 user, loading 가져오기 2. loading이면 "Loading..." 표시 3. BrowserRouter 사용: - Route "/" - user가 있으면 HomePage, 없으면 /login으로 Navigate - Route "/login" - user가 없으면 LoginPage, 있으면 /로 Navigate 4. 전체를 div.container.mt-5로 감싸기 주의사항: - React Router DOM의 Navigate 컴포넌트 사용 - 인증 상태에 따른 리다이렉트 정확히 구현 - App.css import (빈 파일이어도 됨) ``` --- ### Step 12: 진입점 설정 (main.tsx) ``` 앱의 진입점 파일을 만들어주세요. 특히 AbortError 차단 시스템을 포함해야 합니다. src/main.tsx 파일을 생성해주세요: 요구사항: 1. 전역 AbortError 차단 시스템 (3단계): 1단계 - fetch 오버라이드: - const originalFetch = window.fetch 저장 - window.fetch를 새 함수로 오버라이드 - .catch()에서 AbortError 또는 code === 'cancelled' 체크 - AbortError면 new Promise(() => {}) 반환 (완전히 삼킴) - 다른 에러는 throw 2단계 - unhandledrejection 핸들러: - window.addEventListener('unhandledrejection', ...) - AbortError면 event.preventDefault() - 다른 에러는 console.error로 로깅 3단계 - console.error 필터링: - const originalConsoleError = console.error 저장 - console.error를 새 함수로 오버라이드 - 메시지에 'AbortError' 또는 'user aborted' 포함 체크 - AbortError 관련이면 return (로그 출력 안 함) - 다른 에러는 originalConsoleError로 로깅 2. React 앱 렌더링: - createRoot(document.getElementById('root')!) - StrictMode로 감싸기 - ThemeProvider로 감싸기 - App 컴포넌트 렌더링 3. 필요한 import: - React StrictMode, createRoot - './index.css', './i18n' (부수효과 import) - App, ThemeProvider 주의사항: - 3단계 AbortError 차단 시스템이 가장 중요 - 주석으로 각 단계 명확히 표시 - originalFetch, originalConsoleError 변수 사용 ``` --- ### Step 13: CSS 파일 설정 ``` 스타일링 파일들을 설정해주세요. 1. src/index.css: - body의 background-color를 #f0f2f5로 설정 - !important 플래그 사용하여 Bootstrap 오버라이드 2. src/App.css: - 빈 파일 또는 필요한 앱별 스타일 추가 3. index.html: - Bootstrap 5 CSS CDN 추가 ( 태그) - Bootstrap 5 JS CDN 추가 (