Skip to content

Create Week8 Mission1, 2, 3#68

Open
chulee-53 wants to merge 4 commits into
mainfrom
chulee-53/week08
Open

Create Week8 Mission1, 2, 3#68
chulee-53 wants to merge 4 commits into
mainfrom
chulee-53/week08

Conversation

@chulee-53
Copy link
Copy Markdown
Member

@chulee-53 chulee-53 commented May 19, 2026

📝 미션 번호

8주차 Misson 1, 2, 3

📋 구현 사항

  • 검생창 디바운스 적용 -> 불필요한 네트워크 감소
  • 무한스크롤 스로틀링 적용 -> 네트워크 요청 안정화
  • 사이드바 리팩토링

📎 스크린샷

  • mission01
mission01.mp4
  • mission02
mission02.mp4
  • mission03
mission03.mp4

✅ 체크리스트

  • Merge 하려는 브랜치가 올바르게 설정되어 있나요?
  • 로컬에서 실행했을 때 에러가 발생하지 않나요?
  • 불필요한 주석이 제거되었나요?
  • 코드 스타일이 일관적인가요?

🤔 질문 사항

Summary by CodeRabbit

릴리스 노트

  • New Features
    • 사용자 계정 관리: 회원가입, 로그인, 프로필 수정, 계정 탈퇴 기능 추가
    • LP(게시글) 생성 및 관리: 새로운 게시글 작성, 삭제 기능 지원
    • 상호작용 기능: 좋아요 및 댓글 작성/수정/삭제 지원
    • 무한 스크롤을 통한 게시글 및 댓글 목록 조회
    • 검색 기능: 게시글 검색 및 정렬 옵션
    • 구글 소셜 로그인 지원

Review Change Stack

@chulee-53 chulee-53 requested a review from wantkdd May 19, 2026 10:47
@chulee-53 chulee-53 self-assigned this May 19, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Warning

Rate limit exceeded

@chulee-53 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 29 minutes and 25 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 043eb612-9848-429b-9682-48d5ef1d514e

📥 Commits

Reviewing files that changed from the base of the PR and between 3162b13 and 66e0720.

📒 Files selected for processing (9)
  • Week08/chulee-53/mission01-02-03/index.html
  • Week08/chulee-53/mission01-02-03/package.json
  • Week08/chulee-53/mission01-02-03/src/components/LpCard.tsx
  • Week08/chulee-53/mission01-02-03/src/hooks/mutations/usePostLogin.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/queries/useGetInfiniteLpList.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/useThrottle.ts
  • Week08/chulee-53/mission01-02-03/src/layouts/ProtectedLayout.tsx
  • Week08/chulee-53/mission01-02-03/src/pages/MyPage.tsx
  • Week08/chulee-53/mission01-02-03/src/utils/validate.ts
📝 Walkthrough

Walkthrough

주요 기능을 갖춘 완전한 React 클라이언트 애플리케이션 초기화입니다. Vite + TypeScript + React Router + React Query 기반으로 인증 토큰 자동 갱신, LP(게시글) CRUD, 댓글 시스템, 무한 스크롤, 프로필 관리 기능이 통합되어 있습니다. 모든 API 호출은 axios 인터셉터로 인증 헤더를 관리하고, React Query로 상태와 캐시를 동기화하며, Tailwind와 Lucide 아이콘으로 UI를 구성합니다.

Changes

전체 플랫폼 구현

Layer / File(s) Summary
프로젝트 설정 및 빌드 구성
package.json, .gitignore, README.md, eslint.config.js, index.html, tsconfig.*, vite.config.ts, src/index.css
Vite + TypeScript + React 프로젝트 설정, ESLint 규칙, 환경 변수 타입 선언, Tailwind CSS 로드
인증 API 및 토큰 자동 갱신
src/api/axios.ts, src/api/auth.ts
Axios 인스턴스의 요청 헤더에 accessToken 주입, 401 응답 시 토큰 리프레시 자동화 (refreshPromise로 동시 요청 병합), 인증 API 래퍼 (login, signup, logout, getMyInfo, patchMyInfo, withdrawAccount)
인증 상태 관리 및 타입
src/context/AuthContext.tsx, src/types/auth.ts
React Context로 accessToken 상태/세터 관리, useAuth 훅, 로그인/회원가입/내정보 API 요청/응답 타입
API 래퍼 함수
src/api/lp.ts, src/api/comments.ts
LP 목록/상세 조회, 좋아요 토글, LP 생성/삭제, 댓글 CRUD (axios 기반)
데이터 타입 및 스키마
src/types/common.ts, src/types/lp.ts, src/types/comments.ts, src/utils/validate.ts
공통 응답, LP 엔티티, 댓글, 페이징 DTO, 회원가입/로그인 폼 검증 (Zod)
React Query 훅
src/hooks/queries/*, src/hooks/mutations/*
조회: useGetLpDetail, useGetInfiniteLpList, useGetInfiniteLikedLpList, useGetInfiniteLpComment, useGetMyInfo / 변경: usePostLp, usePostComment, usePatchComment, useDeleteComment, usePostLike, useDeleteLike, useDeleteLp, usePostLogin, usePostLogout, usePatchMyInfo, useDeleteAccount (캐시 무효화/낙관적 업데이트 포함)
유틸리티 훅 및 상수
src/hooks/useForm.ts, src/hooks/useDebounce.ts, src/hooks/useThrottle.ts, src/hooks/useLocalStorage.ts, src/hooks/useSidebar.tsx, src/constants/delay.ts, src/constants/key.ts
폼 상태 관리, 디바운스/스로틀, 로컬스토리지 추상화, 사이드바 열림 상태, Query/localStorage 키 상수화
App 루팅 및 제공자
src/App.tsx, src/main.tsx
publicRoutes (홈, 로그인, 회원가입, OAuth) / privateRoutes (마이페이지, LP 상세) 정의, QueryClientProvider-AuthProvider-RouterProvider 중첩, React Query Devtools
레이아웃 및 네비게이션
src/layouts/Layout.tsx, src/layouts/ProtectedLayout.tsx, src/components/Navbar.tsx, src/components/Sidebar.tsx
기본 레이아웃 (Navbar + Outlet + Sidebar), 보호된 레이아웃 (토큰 없으면 /login 리다이렉트), 상단 검색/인증 링크 및 사이드바 (탈퇴 확인 모달)
인증 페이지
src/pages/LoginPage.tsx, src/pages/SignupPage.tsx, src/pages/GoogleLoginRedirectPage.tsx
이메일/비밀번호 로그인, 구글 OAuth 버튼, 단계형 회원가입 (이메일 → 비밀번호 → 프로필), 해시 토큰 처리 및 리다이렉트
UI 컴포넌트
src/components/Input.tsx, src/components/LpCard.tsx, src/components/LpCardSkeleton.tsx, src/components/CommentsSkeleton.tsx, src/components/HamburgerButton.tsx
텍스트 입력 (에러 표시), LP 카드 (썸네일/제목/생성일/좋아요), 스켈레톤 (펄싱 로딩), 햄버거 메뉴 아이콘
홈 페이지
src/pages/HomePage.tsx
LP 검색 쿼리, 정렬 토글 (asc/desc), useInView + useThrottle로 무한 스크롤 트리거, AddLpModal 토글 버튼 (우측 하단)
LP 상세 페이지 및 댓글
src/pages/LpDetailPage.tsx, src/components/LpComments.tsx
LP 상세 조회, 작성자 정보, 생성일차, 좋아요 토글/수, 태그, 무한 스크롤 댓글 (최신/오래된 정렬), 댓글 작성/수정/삭제 UI (본인만 메뉴 표시), 아바타 이미지 로드 실패 시 기본값 처리
마이페이지
src/pages/MyPage.tsx
프로필 표시 (이름/소개글/이메일/아바타), 프로필 수정 모달, 좋아요한 LP 무한 목록 (12개씩 페이징), 이미지 로드 실패 시 기본 아바타
LP 생성 모달
src/components/AddLpModal.tsx
제목, 내용, 썸네일 파일 선택 및 FileReader 미리보기, 태그 추가 (Enter/Add 버튼, 중복 방지, 제거), 제출 검증 후 usePostLp 뮤테이션, pending 상태 UI

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • wantkdd

Poem

🐰 새로운 플랫폼이 피어나고,
토큰은 자동으로 갱신되고,
댓글은 무한히 흐르고,
좋아요는 순식간에 반영되네.
Vite의 속도로 날아가는 LP 여행! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 변경사항의 핵심(8주차 미션 1, 2, 3 구현)을 명확하게 요약하고 있으며, 저장소의 일관된 네이밍 컨벤션을 따르고 있습니다.
Description check ✅ Passed PR 설명은 템플릿의 주요 섹션(미션 번호, 구현 사항, 스크린샷, 체크리스트)을 모두 포함하고 있으며, 각 미션별로 영상이 첨부되어 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chulee-53/week08

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 18

🧹 Nitpick comments (5)
Week08/chulee-53/mission01-02-03/README.md (1)

1-74: ⚡ Quick win

README를 실제 구현 내용 기준으로 갱신해 주세요.

현재 문서는 템플릿 안내 중심이라, 이번 PR의 핵심 기능(검색 디바운스, 무한스크롤 스로틀, 사이드바 리팩터링)을 바로 파악하기 어렵습니다. 실행 방법/핵심 기능/주요 페이지를 프로젝트 기준으로 정리해 두는 게 좋습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Week08/chulee-53/mission01-02-03/README.md` around lines 1 - 74, Update
README.md to replace the generic Vite React template text with project-specific
documentation: add a Setup/Run section with exact install and start commands
(how to run dev server and build), a "Key Features" list describing "search
debounce" (component SearchBar or SearchInput), "infinite-scroll throttle"
(component InfiniteScrollList or useInfiniteScroll), and "sidebar refactor"
(Sidebar or SidebarContainer) including short usage notes and any important
props; add a "Pages / Components" section that lists main pages and components
(e.g., Home, Search results, Sidebar) and a short sentence on how to exercise
each feature, plus a "Development Notes" section with lint/test commands and any
noteworthy implementation details or caveats (debounce/throttle timings,
accessibility notes). Ensure the README clearly surfaces the three PR highlights
so reviewers can immediately find and run those features.
Week08/chulee-53/mission01-02-03/src/components/AddLpModal.tsx (1)

83-88: ⚡ Quick win

아이콘 전용 버튼들에 접근성 이름을 추가해 주세요.

닫기/태그 제거 버튼이 아이콘만 있어 보조기기에서 의미가 불명확합니다.

♻️ 제안 코드
         <button
+          type="button"
+          aria-label="모달 닫기"
           onClick={onClose}
           className="absolute top-4 right-4 text-gray-400 hover:text-white transition-colors cursor-pointer"
         >
@@
                   <button
                     type="button"
+                    aria-label={`태그 ${tag} 제거`}
                     onClick={() => removeTag(tag)}
                     className="hover:text-white"
                   >

Also applies to: 163-169

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Week08/chulee-53/mission01-02-03/src/components/AddLpModal.tsx` around lines
83 - 88, The icon-only buttons in AddLpModal (e.g., the close button using the X
component and other tag-remove buttons referenced around the same file) lack
accessible names; add an accessible label by setting an appropriate aria-label
(e.g., aria-label="Close" or aria-label="Remove tag") or include a
visually-hidden text node (sr-only) inside the button so assistive technologies
can convey the button's purpose; update the button elements that call onClose
and the tag removal handler(s) to include these labels while preserving existing
classes and handlers.
Week08/chulee-53/mission01-02-03/src/pages/HomePage.tsx (1)

35-46: ⚡ Quick win

정렬 토글의 선택 상태를 접근성 속성으로 노출해 주세요.

현재 토글 상태가 시각적으로만 표현되어 보조기기 사용자가 현재 정렬 상태를 알기 어렵습니다.

♻️ 제안 코드
                 <button
+                    aria-pressed={order === "asc"}
                     onClick={() => setOrder("asc")}
                     className={`border px-3 py-1 rounded-l-lg transition-colors cursor-pointer ${order === "asc" ? "bg-white text-black" : "bg-black text-white"}`}
                 >
                     오래된순
                 </button>
                 <button
+                    aria-pressed={order === "desc"}
                     onClick={() => setOrder("desc")}
                     className={`border px-3 py-1 rounded-r-lg transition-colors cursor-pointer ${order === "desc" ? "bg-white text-black" : "bg-black text-white"}`}
                 >
                     최신순
                 </button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Week08/chulee-53/mission01-02-03/src/pages/HomePage.tsx` around lines 35 -
46, The two sort buttons ("오래된순" and "최신순") only show selected state visually;
add accessibility states so assistive tech can detect selection by setting
aria-pressed on each button based on the order state (e.g., aria-pressed={order
=== "asc"} for the 오래된순 button and aria-pressed={order === "desc"} for the 최신순
button) and keep the existing onClick handlers (setOrder) and class toggles;
optionally add a concise aria-label that includes the selection (e.g., "정렬: 오래된순
선택됨") to those same button elements to further clarify the current state to
screen readers.
Week08/chulee-53/mission01-02-03/src/components/Input.tsx (1)

11-22: ⚡ Quick win

에러 메시지를 입력 필드와 접근성으로 연결해 주세요.

현재 오류 텍스트가 시각적으로만 표시되어 보조기기에서 오류 맥락을 놓칠 수 있습니다. aria-invalid/aria-describedby를 같이 설정하는 편이 좋습니다.

♻️ 제안 코드
 const Input = ({ errorMessage, className, ...props }: InputProps) => {
+    const errorId = React.useId();
     return (
         <div className="flex flex-col gap-2">
             <input
+                aria-invalid={Boolean(errorMessage)}
+                aria-describedby={errorMessage ? errorId : undefined}
                 className={cn("bg-transparent text-white text-sm px-4 py-3 rounded-md outline-none border transition-colors",
                     errorMessage ? 'border-red-500 focus:border-blue-500' : 'border-gray-500 hover:border-gray-400 focus:border-blue-500',
                     className || ''
                 )}
                 {...props}
             />
-            {errorMessage && <p className="text-red-500 text-xs">{errorMessage}</p>}
+            {errorMessage && <p id={errorId} className="text-red-500 text-xs">{errorMessage}</p>}
         </div>
     );
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Week08/chulee-53/mission01-02-03/src/components/Input.tsx` around lines 11 -
22, The Input component currently renders an error paragraph visually but
doesn't expose it to assistive tech; update the Input function to set
aria-invalid={Boolean(errorMessage)} on the <input> and add aria-describedby
pointing to the error element when errorMessage exists. Use an id for the error
node derived from the input id (e.g., if props.id use `${props.id}-error`) or
generate one with React's useId inside Input, and render the <p> error element
with that id so screen readers can associate the message; ensure you still
spread {...props} so existing id/presentational props are preserved.
Week08/chulee-53/mission01-02-03/src/components/LpComments.tsx (1)

36-50: ⚡ Quick win

현재 사용자 조회를 쿼리 훅으로 통합해주세요.

직접 getMyInfo() 호출은 캐시를 우회해 중복 요청/상태 분산을 만듭니다. useGetMyInfo로 통합하는 편이 안정적입니다.

♻️ Proposed refactor
-import { useState, useEffect } from "react";
+import { useState, useEffect } from "react";
 ...
-import { useAuth } from "../context/AuthContext";
-import { getMyInfo } from "../api/auth";
+import useGetMyInfo from "../hooks/queries/useGetMyInfo";
 ...
-  const { accessToken } = useAuth();
-  const [currentUserId, setCurrentUserId] = useState<number | null>(null);
+  const { data: me } = useGetMyInfo();
+  const currentUserId = me?.data?.id ?? null;
 ...
-  useEffect(() => {
-    const fetchUserId = async () => {
-      if (accessToken) {
-        try {
-          const response = await getMyInfo();
-          if (response?.data?.id) {
-            setCurrentUserId(response.data.id);
-          }
-        } catch (error) {
-          console.error("유저 정보를 불러오는데 실패했습니다.", error);
-        }
-      }
-    };
-    fetchUserId();
-  }, [accessToken]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Week08/chulee-53/mission01-02-03/src/components/LpComments.tsx` around lines
36 - 50, The current useEffect/fetchUserId calls getMyInfo() directly which
bypasses the app's query caching and causes duplicate requests; replace that
logic with the centralized query hook useGetMyInfo: import and call
useGetMyInfo() (or the existing hook name) instead of getMyInfo(), read the
returned data/isSuccess/isLoading flags from the hook, and when the hook's data
contains an id call setCurrentUserId(data.id) (remove fetchUserId and the direct
getMyInfo call); also remove accessToken from the dependency array and rely on
the hook to handle auth state so the component stays in sync with the global
query cache.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Week08/chulee-53/mission01-02-03/index.html`:
- Line 7: Update the HTML document's <title> element so the visible tab name
matches the current mission scope: change the existing
<title>mission01-02</title> to the correct mission label (e.g.,
<title>mission01-02-03</title>) in the index.html file; locate the <title> tag
and replace its text to reflect the actual mission range.

In `@Week08/chulee-53/mission01-02-03/package.json`:
- Line 2: package.json의 "name" 필드("name": "mission01-02")가 PR 범위(01-02-03)와
불일치합니다; package.json의 name 값을 프로젝트 범위와 일치하도록 "mission01-02-03"으로 업데이트하세요 (수정 대상:
package.json의 "name" 키).

In `@Week08/chulee-53/mission01-02-03/src/api/axios.ts`:
- Around line 35-41: The interceptor accesses error.config and headers without
guards which can throw for network/cancelled errors; update the axios error
interceptor to defensively check that error and error.config exist before
reading originalRequest (the variable originalRequest) and verify
originalRequest.headers (and error.response) are defined before indexing into
headers or checking status; if any of these are missing, short-circuit by
returning Promise.reject(error) (or skip retry logic) so the interceptor never
performs property access on undefined — update the sections around
originalRequest usage and the headers access to perform null/undefined checks
and create safe defaults only when present.

In `@Week08/chulee-53/mission01-02-03/src/components/AddLpModal.tsx`:
- Around line 91-118: Replace the clickable div thumbnail with a semantic,
keyboard-focusable control by using a <label> tied to the hidden <input> (give
the input an id and use htmlFor on the label) or wrap the input inside the label
so Enter/Space and screen readers will open the file picker; keep using
fileInputRef and handleFileChange for the input's onChange, preserve previewUrl
rendering inside the label, add an accessible label text/aria-label (e.g.,
"Select LP image") and visible focus styles, and remove the div's onClick in
favor of the label/htmlFor behavior so keyboard users can open the file dialog.

In `@Week08/chulee-53/mission01-02-03/src/components/HamburgerButton.tsx`:
- Around line 7-8: The icon-only button in HamburgerButton is missing an
explicit type and accessible name; update the <button> element in the
HamburgerButton component to include type="button" to avoid accidental form
submission and add an accessible label (e.g., aria-label="Open menu" or
aria-expanded tied to state) so assistive technologies can convey the button
purpose; locate the button that uses the onClick prop in the HamburgerButton
component and add the type and appropriate aria attributes there.

In `@Week08/chulee-53/mission01-02-03/src/components/LpCard.tsx`:
- Around line 11-14: Replace the clickable <div> in LpCard.tsx with an
accessible interactive element: either a <button type="button"> that calls
navigate(`/lp/${lp.id}`) in its onClick, or a <Link to={`/lp/${lp.id}`}> (from
react-router-dom) wrapping the card content; keep the same className and
visuals, remove the div's onClick, and add an explicit accessible name (e.g.,
aria-label using lp.title or lp.id) so keyboard users can focus and activate the
card. Ensure to import Link if chosen, or set type="button" on the button to
avoid form submit behavior.

In `@Week08/chulee-53/mission01-02-03/src/components/LpComments.tsx`:
- Around line 122-124: The Enter key handler on the input currently calls
handlePostComment on any Enter press without checking IME composition; update
the component to ignore Enter while composition is active by either (A) checking
e.nativeEvent.isComposing inside the onKeyDown handler and returning early if
true before calling handlePostComment, or (B) add an isComposing state plus
onCompositionStart and onCompositionEnd handlers to set/clear that flag and have
onKeyDown return early when isComposing is true; reference the onKeyDown prop,
handlePostComment, and add onCompositionStart/onCompositionEnd or isComposing
state as needed.

In `@Week08/chulee-53/mission01-02-03/src/components/Navbar.tsx`:
- Around line 64-68: The search toggle icons (X and Search) inside the Navbar
component currently have onClick handlers directly on the SVG components
(referenced as X, Search and using isSearchOpen and toggleSearch); wrap each
icon in a <button type="button" aria-label="검색 열기/닫기"> and move the
onClick={toggleSearch} to that button so it becomes keyboard-focusable and
activated by Enter/Space, keep the existing className styling (apply to the
button or icon as appropriate) and preserve size props on the X/Search
components.

In `@Week08/chulee-53/mission01-02-03/src/hooks/mutations/usePostLogin.ts`:
- Around line 13-21: The onSuccess handler currently returns silently when
response?.data?.accessToken is missing; update the onSuccess in usePostLogin.ts
to detect the missing token and treat it as a failure by showing an error alert
(e.g., alert("로그인에 실패했습니다: 토큰이 없습니다.")), clearing any partial state (call
setAccessToken(null) and remove token from storage via
setAccessTokenInStorage(null) or the existing removal helper), and avoid
navigating to "/" so the user stays on the login page; keep the existing success
branch (setAccessTokenInStorage(token); setAccessToken(token); alert success;
navigate("/")) unchanged and only add the explicit failure branch for when
response?.data?.accessToken is falsy.

In `@Week08/chulee-53/mission01-02-03/src/hooks/queries/useGetInfiniteLpList.ts`:
- Around line 11-14: The queryKey used in useGetInfiniteLpList incorrectly omits
the limit parameter, causing cache pollution because queryFn calls getLpList({
cursor: pageParam, limit, ... }) but queryKey only includes QUERY_KEY.lps,
search, order; update the queryKey in useGetInfiniteLpList to include limit
(e.g., [QUERY_KEY.lps, search, order, limit]) so different page sizes generate
separate caches and prevent stale/incorrect results from getLpList called by
queryFn.

In `@Week08/chulee-53/mission01-02-03/src/hooks/useForm.ts`:
- Around line 3-6: The generic contract UseFormProps<T> and the useForm update
logic diverge because the updater always writes string values; either constrain
the hook to string-only forms by changing UseFormProps<T> to UseFormProps<T
extends Record<string, string>> and reflect that in useForm, or make the field
updater accept the actual field type by typing the setter/update function to use
Partial<T> (or value: T[K]) and avoid coercing to string; update the validate
signature and any usages of initialValues, validate, and the useForm
setFieldValue/setValues handlers to match the chosen approach (i.e., remove
forced string casts in useForm or restrict T to string fields).

In `@Week08/chulee-53/mission01-02-03/src/hooks/useThrottle.ts`:
- Around line 14-24: The hook useThrottle currently resets the timeout on every
value change (timerId) and uses an initial lastExecuted that causes the first
update to be delayed, making it behave like debounce; change the logic to
compute remaining = interval - (Date.now() - lastExecuted.current), if remaining
<= 0 then immediately setThrottledValue(value) and set lastExecuted.current =
Date.now(), otherwise schedule a timeout for remaining (do not restart it on
each value change in a way that violates the throttle window) and clear that
timeout in the returned cleanup; also initialize lastExecuted.current to 0 (or
Date.now() - interval) so the first call can fire immediately. Ensure you
reference useThrottle, lastExecuted, setThrottledValue, interval, and timerId
when locating and updating the code.

In `@Week08/chulee-53/mission01-02-03/src/layouts/ProtectedLayout.tsx`:
- Around line 11-15: In ProtectedLayout, the call to the hook useSidebar()
happens after an early return based on accessToken, violating Rules of Hooks;
move the useSidebar() call (const { isOpen, toggle, close } = useSidebar()) to
the top of the component so all hooks are called unconditionally (before the if
(!accessToken) return and any other conditional returns) to ensure consistent
hook order during renders.

In `@Week08/chulee-53/mission01-02-03/src/pages/LpDetailPage.tsx`:
- Around line 93-95: The edit button (the button wrapping the Pencil icon in
LpDetailPage.tsx) is currently clickable but has no handler; either hide it or
render it disabled when no edit action exists: check for the presence of the
edit handler/permission (e.g., an onEdit prop, isEditable flag, or currentUser
check) and if absent, replace the interactive button with a non-interactive
element or a disabled button (add aria-disabled="true", a visually-disabled
style such as cursor-not-allowed and reduced opacity, and remove any onClick) so
it no longer appears clickable or triggers confusion.

In `@Week08/chulee-53/mission01-02-03/src/pages/MyPage.tsx`:
- Around line 81-86: The icon-only <button> that calls handleOpenEditModal and
contains the <Settings /> icon (and the other icon-only button referenced later)
lack accessible names; add an aria-label attribute to each icon-only button
(e.g., aria-label="Open settings" for the button wrapping <Settings /> and an
appropriate descriptive label for the other icon button) so screen readers can
convey their purpose; update the JSX for the button elements that render only
icons to include these aria-labels.
- Around line 117-172: The modal rendered when isEditModalOpen is true lacks
dialog semantics; update the outer modal container (the div that is shown when
isEditModalOpen) to include role="dialog" and aria-modal="true", add an id on
the <h3> title (e.g., editProfileTitle) and set aria-labelledby on the dialog to
that id, and add accessible labels for interactive controls (e.g., give the
close button an aria-label="Close profile editor"); ensure these changes are
applied around the elements referenced (isEditModalOpen modal container, the h3
title, and the close button) so screen readers can identify the dialog and its
title.
- Around line 23-26: The guard for the infinite-scroll sentinel currently uses
the general isLikedFetching (from useGetInfiniteLikedLpList), causing triggers
during background refetch; update the hook destructure to also pull
isFetchingNextPage (e.g., isFetchingNextPage: isLikedFetchingNextPage) from
useGetInfiniteLikedLpList and change the sentinel guard (the check that decides
whether to call fetchNextPage) to use that isLikedFetchingNextPage flag instead
of isLikedFetching so fetchNextPage is only suppressed while the next page is
actively loading.

In `@Week08/chulee-53/mission01-02-03/src/utils/validate.ts`:
- Around line 5-6: The empty-value checks in the validators currently return an
empty string (e.g., the `if (!value) return "";` in validate.ts), allowing
required fields to pass; change those checks in the email and password
validation functions (e.g., the functions used by `validateSignin`, such as
validateEmail and validatePassword) to return a clear required-field error (for
example "필수 입력 항목입니다." or "이메일을 입력해주세요."/"비밀번호를 입력해주세요.") instead of "" so empty
email/password fail validation, and keep the existing regex/strength checks
after that early return.

---

Nitpick comments:
In `@Week08/chulee-53/mission01-02-03/README.md`:
- Around line 1-74: Update README.md to replace the generic Vite React template
text with project-specific documentation: add a Setup/Run section with exact
install and start commands (how to run dev server and build), a "Key Features"
list describing "search debounce" (component SearchBar or SearchInput),
"infinite-scroll throttle" (component InfiniteScrollList or useInfiniteScroll),
and "sidebar refactor" (Sidebar or SidebarContainer) including short usage notes
and any important props; add a "Pages / Components" section that lists main
pages and components (e.g., Home, Search results, Sidebar) and a short sentence
on how to exercise each feature, plus a "Development Notes" section with
lint/test commands and any noteworthy implementation details or caveats
(debounce/throttle timings, accessibility notes). Ensure the README clearly
surfaces the three PR highlights so reviewers can immediately find and run those
features.

In `@Week08/chulee-53/mission01-02-03/src/components/AddLpModal.tsx`:
- Around line 83-88: The icon-only buttons in AddLpModal (e.g., the close button
using the X component and other tag-remove buttons referenced around the same
file) lack accessible names; add an accessible label by setting an appropriate
aria-label (e.g., aria-label="Close" or aria-label="Remove tag") or include a
visually-hidden text node (sr-only) inside the button so assistive technologies
can convey the button's purpose; update the button elements that call onClose
and the tag removal handler(s) to include these labels while preserving existing
classes and handlers.

In `@Week08/chulee-53/mission01-02-03/src/components/Input.tsx`:
- Around line 11-22: The Input component currently renders an error paragraph
visually but doesn't expose it to assistive tech; update the Input function to
set aria-invalid={Boolean(errorMessage)} on the <input> and add aria-describedby
pointing to the error element when errorMessage exists. Use an id for the error
node derived from the input id (e.g., if props.id use `${props.id}-error`) or
generate one with React's useId inside Input, and render the <p> error element
with that id so screen readers can associate the message; ensure you still
spread {...props} so existing id/presentational props are preserved.

In `@Week08/chulee-53/mission01-02-03/src/components/LpComments.tsx`:
- Around line 36-50: The current useEffect/fetchUserId calls getMyInfo()
directly which bypasses the app's query caching and causes duplicate requests;
replace that logic with the centralized query hook useGetMyInfo: import and call
useGetMyInfo() (or the existing hook name) instead of getMyInfo(), read the
returned data/isSuccess/isLoading flags from the hook, and when the hook's data
contains an id call setCurrentUserId(data.id) (remove fetchUserId and the direct
getMyInfo call); also remove accessToken from the dependency array and rely on
the hook to handle auth state so the component stays in sync with the global
query cache.

In `@Week08/chulee-53/mission01-02-03/src/pages/HomePage.tsx`:
- Around line 35-46: The two sort buttons ("오래된순" and "최신순") only show selected
state visually; add accessibility states so assistive tech can detect selection
by setting aria-pressed on each button based on the order state (e.g.,
aria-pressed={order === "asc"} for the 오래된순 button and aria-pressed={order ===
"desc"} for the 최신순 button) and keep the existing onClick handlers (setOrder)
and class toggles; optionally add a concise aria-label that includes the
selection (e.g., "정렬: 오래된순 선택됨") to those same button elements to further
clarify the current state to screen readers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e9d37bb6-6bf3-490f-8596-aae26d58047e

📥 Commits

Reviewing files that changed from the base of the PR and between b47368a and 3162b13.

⛔ Files ignored due to path filters (3)
  • Week08/chulee-53/mission01-02-03/package-lock.json is excluded by !**/package-lock.json
  • Week08/chulee-53/mission01-02-03/src/images/defaultavatar.png is excluded by !**/*.png
  • Week08/chulee-53/mission01-02-03/src/images/google_logo.png is excluded by !**/*.png
📒 Files selected for processing (66)
  • Week08/chulee-53/mission01-02-03/.gitignore
  • Week08/chulee-53/mission01-02-03/README.md
  • Week08/chulee-53/mission01-02-03/eslint.config.js
  • Week08/chulee-53/mission01-02-03/index.html
  • Week08/chulee-53/mission01-02-03/package.json
  • Week08/chulee-53/mission01-02-03/src/App.css
  • Week08/chulee-53/mission01-02-03/src/App.tsx
  • Week08/chulee-53/mission01-02-03/src/api/auth.ts
  • Week08/chulee-53/mission01-02-03/src/api/axios.ts
  • Week08/chulee-53/mission01-02-03/src/api/comments.ts
  • Week08/chulee-53/mission01-02-03/src/api/lp.ts
  • Week08/chulee-53/mission01-02-03/src/components/AddLpModal.tsx
  • Week08/chulee-53/mission01-02-03/src/components/CommentsSkeleton.tsx
  • Week08/chulee-53/mission01-02-03/src/components/HamburgerButton.tsx
  • Week08/chulee-53/mission01-02-03/src/components/Input.tsx
  • Week08/chulee-53/mission01-02-03/src/components/LpCard.tsx
  • Week08/chulee-53/mission01-02-03/src/components/LpCardSkeleton.tsx
  • Week08/chulee-53/mission01-02-03/src/components/LpComments.tsx
  • Week08/chulee-53/mission01-02-03/src/components/Navbar.tsx
  • Week08/chulee-53/mission01-02-03/src/components/Sidebar.tsx
  • Week08/chulee-53/mission01-02-03/src/constants/delay.ts
  • Week08/chulee-53/mission01-02-03/src/constants/key.ts
  • Week08/chulee-53/mission01-02-03/src/context/AuthContext.tsx
  • Week08/chulee-53/mission01-02-03/src/hooks/mutations/useDeleteAccount.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/mutations/useDeleteComment.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/mutations/useDeleteLike.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/mutations/useDeleteLp.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/mutations/usePatchComment.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/mutations/usePatchMyInfo.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/mutations/usePostComment.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/mutations/usePostLike.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/mutations/usePostLogin.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/mutations/usePostLogout.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/mutations/usePostLp.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/queries/useGetInfiniteLikedLpList.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/queries/useGetInfiniteLpComment.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/queries/useGetInfiniteLpList.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/queries/useGetLpComment.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/queries/useGetLpDetail.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/queries/useGetLpList.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/queries/useGetMyInfo.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/useDebounce.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/useForm.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/useLocalStorage.ts
  • Week08/chulee-53/mission01-02-03/src/hooks/useSidebar.tsx
  • Week08/chulee-53/mission01-02-03/src/hooks/useThrottle.ts
  • Week08/chulee-53/mission01-02-03/src/index.css
  • Week08/chulee-53/mission01-02-03/src/layouts/Layout.tsx
  • Week08/chulee-53/mission01-02-03/src/layouts/ProtectedLayout.tsx
  • Week08/chulee-53/mission01-02-03/src/main.tsx
  • Week08/chulee-53/mission01-02-03/src/pages/GoogleLoginRedirectPage.tsx
  • Week08/chulee-53/mission01-02-03/src/pages/HomePage.tsx
  • Week08/chulee-53/mission01-02-03/src/pages/LoginPage.tsx
  • Week08/chulee-53/mission01-02-03/src/pages/LpDetailPage.tsx
  • Week08/chulee-53/mission01-02-03/src/pages/MyPage.tsx
  • Week08/chulee-53/mission01-02-03/src/pages/SignupPage.tsx
  • Week08/chulee-53/mission01-02-03/src/types/auth.ts
  • Week08/chulee-53/mission01-02-03/src/types/comments.ts
  • Week08/chulee-53/mission01-02-03/src/types/common.ts
  • Week08/chulee-53/mission01-02-03/src/types/lp.ts
  • Week08/chulee-53/mission01-02-03/src/utils/validate.ts
  • Week08/chulee-53/mission01-02-03/src/vite-env.d.ts
  • Week08/chulee-53/mission01-02-03/tsconfig.app.json
  • Week08/chulee-53/mission01-02-03/tsconfig.json
  • Week08/chulee-53/mission01-02-03/tsconfig.node.json
  • Week08/chulee-53/mission01-02-03/vite.config.ts

Comment thread Week08/chulee-53/mission01-02-03/index.html Outdated
Comment thread Week08/chulee-53/mission01-02-03/package.json Outdated
Comment thread Week08/chulee-53/mission01-02-03/src/api/axios.ts
Comment thread Week08/chulee-53/mission01-02-03/src/components/AddLpModal.tsx
Comment thread Week08/chulee-53/mission01-02-03/src/components/HamburgerButton.tsx
Comment thread Week08/chulee-53/mission01-02-03/src/pages/LpDetailPage.tsx
Comment thread Week08/chulee-53/mission01-02-03/src/pages/MyPage.tsx
Comment thread Week08/chulee-53/mission01-02-03/src/pages/MyPage.tsx Outdated
Comment thread Week08/chulee-53/mission01-02-03/src/pages/MyPage.tsx
Comment thread Week08/chulee-53/mission01-02-03/src/utils/validate.ts Outdated
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