feat: 홈 페이지 계획 추가 & 계획 완료 토글 기능 구현#30
Conversation
📝 WalkthroughWalkthrough클라이언트 측 TODO 관리와 Firestore 연동 서비스를 추가하고, Firebase 인증 상태에 따라 홈 페이지에서 TODO 로딩·추가·토글 흐름을 중앙화된 props 기반으로 리팩토링합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Page as Home Page (page.tsx)
participant Auth as Firebase Auth
participant Service as TODO Service (todoService)
participant Firestore as Firestore DB
participant UI as UI Components (BottomSection / ButtonSection / CheckList)
User->>Page: 페이지 요청/방문
Page->>Auth: onAuthStateChanged 구독 등록
Auth-->>Page: 인증 상태(유저 또는 null)
alt 사용자 인증됨
Page->>Service: fetchTodos(uid)
Service->>Firestore: todos 컬렉션 쿼리
Firestore-->>Service: 문서 목록
Service-->>Page: Todo[] 반환
Page->>UI: `todos` 및 핸들러 props 전달
UI-->>User: TODO 목록 렌더링
User->>UI: 새 TODO 추가 요청 (버튼 클릭)
UI->>Page: handleAddTodo(text)
Page->>Service: AddTodo(uid, text)
Service->>Firestore: 새 문서 추가
Firestore-->>Service: 완료
Page->>Service: fetchTodos(uid) 재요청
Service-->>Page: 업데이트된 Todo[]
Page->>UI: 갱신된 todos 전달
User->>UI: TODO 토글
UI->>Page: handleToggleTodo(id, currentStatus)
Page->>Service: toggleTodoStatus(uid, id, currentStatus)
Service->>Firestore: isChecked 업데이트
Firestore-->>Service: 완료
Page->>Service: fetchTodos(uid) 또는 상태 갱신
Page->>UI: 변경 반영
else 사용자 미인증
Page->>UI: 로그인 필요 UI 표시
UI-->>User: 로그인 안내
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
✏️ Tip: You can disable this entire section by setting Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@app/`(with-sidebar)/(home)/page.tsx:
- Around line 22-23: The component sets error via setError but never renders it;
update the page component's JSX return to conditionally render the error state
(error) from useState<string | null> so users see failures: locate the component
that declares const [error, setError] and add a visible error block (e.g., a
paragraph/div with a clear message, appropriate styling and role="alert") that
displays error when truthy, keeping existing todos rendering unchanged and
ensuring accessibility.
In `@lib/home/todoService.ts`:
- Around line 27-31: The mapping over snapshot.docs (snapshot.docs.map)
currently calls doc.data().createAt.toDate() without checks, which will throw if
createAt is missing or not a Timestamp; update the mapper to defensively check
that doc.data().createAt exists and has a toDate method (or is an expected
Timestamp type) before calling toDate, and provide a safe fallback (e.g.,
undefined or a default Date) when it’s absent or invalid so the produced Todo
objects never trigger a runtime error.
🧹 Nitpick comments (9)
components/home/ButtonSection.tsx (2)
1-3:onAddTodo의 반환 타입이 실제 사용과 불일치합니다.
page.tsx에서handleAddTodo는async함수로Promise<void>를 반환하지만, 인터페이스는void로 정의되어 있습니다. 타입 일관성을 위해Promise<void>로 변경하는 것이 좋습니다.♻️ 타입 수정 제안
interface ButtonSectionProps { - onAddTodo: (text: string) => void; + onAddTodo: (text: string) => Promise<void>; }
15-20: "새 TIL 작성" 버튼에onClick핸들러가 없습니다.현재 비활성 상태로 보이며, 추후 기능 구현이 필요합니다.
TIL 작성 기능 구현이 필요하시면 도움을 드릴 수 있습니다. 이슈를 생성해 드릴까요?
lib/home/todoService.ts (2)
13-19:createAt필드명이createdAt이 아닌createAt으로 되어 있습니다.일반적인 명명 관례는
createdAt입니다. 현재 DB에 저장된 데이터와 일치한다면 유지해도 되지만, 새 프로젝트라면createdAt으로 통일하는 것을 권장합니다.
35-41: 함수명AddTodo가 PascalCase로 되어 있습니다.JavaScript/TypeScript 관례상 함수명은 camelCase(
addTodo)를 사용합니다. PascalCase는 클래스나 컴포넌트에 사용됩니다.♻️ 함수명 수정 제안
-export const AddTodo = async (uid: string, text: string) => { +export const addTodo = async (uid: string, text: string): Promise<void> => {
page.tsx의 import도 함께 수정이 필요합니다.components/home/CheckList.tsx (1)
6-10: 주석 처리된 코드를 삭제하세요.
ChecklistItem타입이Todo로 대체되었으므로, 주석 처리된 코드는 제거하는 것이 좋습니다. 필요시 Git 히스토리에서 복구할 수 있습니다.🧹 주석 코드 삭제
import { Todo } from '@/lib/home/todoService'; import { CheckItem } from './CheckItem'; -// export type ChecklistItem = { -// id: string; -// text: string; -// isChecked: boolean; -// }; - interface CheckListProps {components/home/BottomSection.tsx (2)
3-3: 사용되지 않는 import를 제거하세요.
useState가 import되었지만 사용되지 않습니다.🧹 unused import 제거
-import { useState } from 'react'; import Card from './Card';
14-47: 주석 처리된 더미 데이터와 함수들을 삭제하세요.외부 props 기반 데이터 흐름으로 리팩토링이 완료되었으므로, 더 이상 필요하지 않은 주석 코드를 정리해 주세요. Git 히스토리에서 언제든 복구 가능합니다.
app/(with-sidebar)/(home)/page.tsx (2)
60-72: 작업 후 전체 목록을 다시 불러오는 패턴은 간단하지만 비효율적일 수 있습니다.현재
AddTodo/toggleTodoStatus후loadTodos를 호출하여 전체 데이터를 다시 fetch합니다. 데이터가 많아지면 낙관적 업데이트(optimistic update) 패턴을 고려해 보세요.💡 낙관적 업데이트 예시 (toggle)
const handleToggleTodo = async (id: string, currentStatus: boolean) => { if (!currentUser) return; // 낙관적 업데이트 setTodos((prev) => prev.map((todo) => todo.id === id ? { ...todo, isChecked: !currentStatus } : todo ) ); try { await toggleTodoStatus(currentUser.uid, id, currentStatus); } catch (err) { // 실패 시 롤백 setTodos((prev) => prev.map((todo) => todo.id === id ? { ...todo, isChecked: currentStatus } : todo ) ); setError('상태를 업데이트하는 데 실패하였습니다'); } };Also applies to: 75-87
89-95: 로딩 상태 표시가 없습니다.데이터를 불러오는 동안 사용자에게 로딩 상태를 보여주는 것이 좋습니다. 현재는 로그인되지 않은 상태와 로딩 중인 상태를 구분할 수 없습니다.
💡 로딩 상태 추가 제안
const [todos, setTodos] = useState<Todo[]>([]); const [error, setError] = useState<string | null>(null); +const [isLoading, setIsLoading] = useState(true); // 현재 사용자 정보 const [currentUser, setCurrentUser] = useState<User | null>(null); // 사용자 로그인 상태 감지 useEffect(() => { const unsubscribe = onAuthStateChanged(auth, (user) => { setCurrentUser(user); + if (!user) setIsLoading(false); }); return () => unsubscribe(); }, []); // 1. 할 일 목록 불러오기 const loadTodos = async (uid: string) => { try { setError(null); + setIsLoading(true); const fetchedTodos = await fetchTodos(uid); setTodos(fetchedTodos); } catch (err) { console.error(err); setError('데이터를 불러오는 데 실패하였습니다'); + } finally { + setIsLoading(false); } }; +if (isLoading) { + return ( + <div className="flex min-h-screen items-center justify-center"> + <p>로딩 중...</p> + </div> + ); +} if (!currentUser) {
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
app/(with-sidebar)/(home)/page.tsxcomponents/home/BottomSection.tsxcomponents/home/ButtonSection.tsxcomponents/home/CheckList.tsxlib/home/todoService.ts
🧰 Additional context used
🧬 Code graph analysis (4)
lib/home/todoService.ts (1)
lib/firebase.ts (1)
db(26-26)
components/home/ButtonSection.tsx (3)
components/auth/Button.tsx (1)
ButtonProps(13-41)components/home/CheckItem.tsx (1)
CheckItem(11-49)components/home/HeaderSection.tsx (2)
HeaderSectionProps(1-3)HeaderSectionProps(5-14)
components/home/BottomSection.tsx (2)
lib/home/todoService.ts (1)
Todo(14-19)components/home/CheckList.tsx (1)
CheckList(18-41)
components/home/CheckList.tsx (1)
lib/home/todoService.ts (1)
Todo(14-19)
🔇 Additional comments (7)
components/home/ButtonSection.tsx (1)
5-11:prompt()사용은 임시 구현임을 확인합니다.PR 설명에서 언급된 대로 임시 구현입니다. 추후 모달이나 입력 폼으로 교체 시,
handleAddClick을async로 변경하고 에러 처리를 추가하는 것을 권장합니다.lib/home/todoService.ts (1)
44-53: LGTM!
toggleTodoStatus함수는 명확하게 구현되어 있습니다.components/home/CheckList.tsx (1)
18-40: LGTM!
Todo타입으로의 마이그레이션과onToggleTodo콜백 구조가 잘 구현되었습니다. 데이터 흐름이 명확합니다.components/home/BottomSection.tsx (1)
49-66: LGTM!외부 props를 통한 데이터 주입 방식으로의 리팩토링이 잘 되었습니다. "다가오는 일정" 카드는 PR 설명에 언급된 대로 추후 구현 예정으로 이해합니다.
app/(with-sidebar)/(home)/page.tsx (3)
29-35: LGTM!
onAuthStateChanged구독과 cleanup 함수가 올바르게 구현되었습니다.
38-44:currentUser변경 시 todos를 불러오는 로직이 적절합니다.로그아웃 시 todos를 빈 배열로 초기화하는 것도 좋은 처리입니다.
97-118: LGTM!컴포넌트 구조와 데이터 흐름이 명확합니다.
BottomSection과ButtonSection에 적절한 props가 전달되었습니다.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| const [todos, setTodos] = useState<Todo[]>([]); | ||
| const [error, setError] = useState<string | null>(null); |
There was a problem hiding this comment.
error 상태가 UI에 표시되지 않습니다.
setError로 에러 메시지를 설정하지만, 사용자에게 표시되지 않습니다. 에러 발생 시 사용자가 인지할 수 있도록 UI에 렌더링해야 합니다.
🐛 에러 표시 UI 추가 제안
return 문 내부에 에러 메시지 표시 추가:
return (
<div className="bg-background flex min-h-screen flex-col gap-4 font-sans md:p-[137px]">
+ {error && (
+ <div className="rounded-lg bg-red-100 p-4 text-red-700">
+ {error}
+ </div>
+ )}
{/* 1. Header */}
<HeaderSection />🤖 Prompt for AI Agents
In `@app/`(with-sidebar)/(home)/page.tsx around lines 22 - 23, The component sets
error via setError but never renders it; update the page component's JSX return
to conditionally render the error state (error) from useState<string | null> so
users see failures: locate the component that declares const [error, setError]
and add a visible error block (e.g., a paragraph/div with a clear message,
appropriate styling and role="alert") that displays error when truthy, keeping
existing todos rendering unchanged and ensuring accessibility.
| return snapshot.docs.map((doc) => ({ | ||
| id: doc.id, | ||
| ...doc.data(), | ||
| createAt: doc.data().createAt.toDate(), | ||
| })) as Todo[]; |
There was a problem hiding this comment.
createAt이 없거나 Timestamp가 아닐 경우 런타임 에러가 발생할 수 있습니다.
doc.data().createAt이 존재하지 않거나 toDate() 메서드가 없는 경우 에러가 발생합니다. 방어적 코드 추가를 권장합니다.
🛡️ 방어적 코드 제안
return snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
- createAt: doc.data().createAt.toDate(),
+ createAt: doc.data().createAt?.toDate?.() ?? new Date(),
})) as Todo[];📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return snapshot.docs.map((doc) => ({ | |
| id: doc.id, | |
| ...doc.data(), | |
| createAt: doc.data().createAt.toDate(), | |
| })) as Todo[]; | |
| return snapshot.docs.map((doc) => ({ | |
| id: doc.id, | |
| ...doc.data(), | |
| createAt: doc.data().createAt?.toDate?.() ?? new Date(), | |
| })) as Todo[]; |
🤖 Prompt for AI Agents
In `@lib/home/todoService.ts` around lines 27 - 31, The mapping over snapshot.docs
(snapshot.docs.map) currently calls doc.data().createAt.toDate() without checks,
which will throw if createAt is missing or not a Timestamp; update the mapper to
defensively check that doc.data().createAt exists and has a toDate method (or is
an expected Timestamp type) before calling toDate, and provide a safe fallback
(e.g., undefined or a default Date) when it’s absent or invalid so the produced
Todo objects never trigger a runtime error.
1) 작업한 이슈번호
#31
2) 변경 요약 (What & Why)
홈 페이지에서 보여주는 오늘 할 일, 다가오는 일정 카드 기능을 구현.
계획을 체크하면 완료 상태로 토글됨.
무엇을 변경했는지: 홈 페이지 변경
왜 변경했는지(문제/목표):
3) 스크린샷/동영상 (UI 변경 시)
4) 상세 변경사항
5) 참고사항
Summary by CodeRabbit
새로운 기능
개선 사항
✏️ Tip: You can customize this high-level summary in your review settings.