Skip to content

feat: 홈 페이지 계획 추가 & 계획 완료 토글 기능 구현#30

Merged
GamjaIsMine02 merged 4 commits into
devfrom
feature/home-page
Jan 15, 2026
Merged

feat: 홈 페이지 계획 추가 & 계획 완료 토글 기능 구현#30
GamjaIsMine02 merged 4 commits into
devfrom
feature/home-page

Conversation

@GamjaIsMine02
Copy link
Copy Markdown
Contributor

@GamjaIsMine02 GamjaIsMine02 commented Jan 15, 2026

1) 작업한 이슈번호

#31

2) 변경 요약 (What & Why)

  • 홈 페이지에서 보여주는 오늘 할 일, 다가오는 일정 카드 기능을 구현.

  • 계획을 체크하면 완료 상태로 토글됨.

  • 무엇을 변경했는지: 홈 페이지 변경

  • 변경했는지(문제/목표):

3) 스크린샷/동영상 (UI 변경 시)

전/후 비교, 반응형(모바일/데스크톱) 캡쳐

  • Before:
  • After:
image image

4) 상세 변경사항

  • 라우팅/페이지: app/(with-sidebar)/(home)/page.tsx
  • 컴포넌트: BottomSection.tsx, ButtonSection.tsx, CheckList.tsx
  • 상태관리:
  • API 호출:
  • 스타일:
  • 기타: todoService.ts

5) 참고사항

  • 유저 정보 fetch 기능 필요
  • 다가오는 일정 타입 정의 및 기능 필요
  • 현재 '계획 추가하기' 버튼에 임시로 프롬프트로 할 일을 추가하도록 구현해놨으며, 다가오는 일정 카드는 주석처리 한 상태

Summary by CodeRabbit

  • 새로운 기능

    • 사용자별 TODO 목록 관리, 항목 추가 및 완료 상태 토글 기능 추가
    • 인증 상태에 따른 자동 로드 및 로그인 필요 UI 제공
  • 개선 사항

    • 인증 변화에 실시간으로 반영되는 UI
    • 추가/토글 동작 시 오류 처리 및 사용자 피드백 강화

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

클라이언트 측 TODO 관리와 Firestore 연동 서비스를 추가하고, Firebase 인증 상태에 따라 홈 페이지에서 TODO 로딩·추가·토글 흐름을 중앙화된 props 기반으로 리팩토링합니다.

Changes

Cohort / File(s) 변경 사항
Firestore TODO 서비스
lib/home/todoService.ts
새 모듈 추가: Todo 인터페이스 및 fetchTodos(uid), AddTodo(uid, text), toggleTodoStatus(uid, todoId, currentStatus) 구현 (Firestore 쿼리/업데이트 포함)
홈 페이지 로직 통합
app/(with-sidebar)/(home)/page.tsx
Firebase 인증 구독(onAuthStateChanged) 추가, currentUser, todos, error 로컬 상태 도입, loadTodos(uid), handleAddTodo(text), handleToggleTodo(id, status) 핸들러 구현 및 BottomSection/ButtonSection에 props 전달
하단 섹션 및 체크리스트
components/home/BottomSection.tsx, components/home/CheckList.tsx
내부 더미 상태 제거 → 외부 todos: Todo[]onToggleTodo(id, currentStatus) props 기반 렌더링으로 전환; ChecklistItem 타입 제거/대체, 토글 핸들러 서명 변경
버튼 섹션 상호작용
components/home/ButtonSection.tsx
ButtonSectionProps 추가: onAddTodo(text) prop 수신; 버튼 클릭 시 프롬프트로 입력 받아 onAddTodo 호출하도록 핸들러 연결

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 작은 발로 와르르, 할일을 모아둔 바구니,
Firestore에 심은 당근 한 뿌기, 쑥쑥 자라 할일이 돋아나네.
클릭 한 번이면 토글, 당근이 탁 바뀌고,
인증된 친구만 볼 수 있는 비밀밭,
토끼가 기뻐 뛰네, 힙합 대신 깡총깡총!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 변경사항의 핵심인 홈 페이지 계획 추가 및 계획 완료 토글 기능을 명확하게 설명하며, 제목 규칙도 준수합니다.
Description check ✅ Passed PR 설명은 대부분의 필수 항목(이슈번호, 변경 요약, 상세 변경사항, 참고사항)을 포함하지만 '왜 변경했는지'는 불완전하며 스크린샷도 After만 제공됩니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a1f5e2f and b956099.

📒 Files selected for processing (1)
  • lib/home/todoService.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/home/todoService.ts

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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에서 handleAddTodoasync 함수로 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/toggleTodoStatusloadTodos를 호출하여 전체 데이터를 다시 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

📥 Commits

Reviewing files that changed from the base of the PR and between b3bf487 and a1f5e2f.

📒 Files selected for processing (5)
  • app/(with-sidebar)/(home)/page.tsx
  • components/home/BottomSection.tsx
  • components/home/ButtonSection.tsx
  • components/home/CheckList.tsx
  • lib/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 설명에서 언급된 대로 임시 구현입니다. 추후 모달이나 입력 폼으로 교체 시, handleAddClickasync로 변경하고 에러 처리를 추가하는 것을 권장합니다.

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!

컴포넌트 구조와 데이터 흐름이 명확합니다. BottomSectionButtonSection에 적절한 props가 전달되었습니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +22 to +23
const [todos, setTodos] = useState<Todo[]>([]);
const [error, setError] = useState<string | null>(null);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment thread lib/home/todoService.ts
Comment on lines +27 to +31
return snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
createAt: doc.data().createAt.toDate(),
})) as Todo[];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

@GamjaIsMine02 GamjaIsMine02 merged commit 6ff687a into dev Jan 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant