Skip to content

Conversation

@dkslel1225
Copy link
Contributor

@dkslel1225 dkslel1225 commented Jun 21, 2025

📌 변경 사항 개요

  • ✨ Feat: 카드 목록, 모달 내 댓글 목록에 무한 스크롤 구현
  • 🎨 style: 스타일 일부 수정하며 반응형 작업중

✨ 요약

📝 상세 내용

🔗 관련 이슈

🖼️ 스크린샷

✅ 체크리스트

  • 브랜치 네이밍 컨벤션을 준수했습니다
  • 커밋 컨벤션을 준수했습니다
  • 코드가 프로젝트의 스타일 가이드라인을 준수합니다

💡 참고 사항

Summary by CodeRabbit

  • 신규 기능

    • 카드 및 댓글 목록에 무한 스크롤(인피니트 스크롤) 기능이 도입되어, 스크롤 시 자동으로 추가 데이터를 불러옵니다.
    • 무한 스크롤을 위한 커스텀 훅이 추가되었습니다.
  • 스타일

    • 카드 생성/수정 모달과 입력 필드, 드롭다운 화살표 등에서 모바일 환경에 맞춘 반응형 스타일이 개선되었습니다.
    • 새로운 입력 필드 전용 CSS 클래스가 추가되었습니다.
  • 버그 수정

    • 에러 발생 시 토스트 알림으로 안내가 제공되며, 일부 에러 메시지 처리 방식이 개선되었습니다.
  • 리팩터

    • 카드/댓글 목록 조회 로직이 페이지네이션에서 무한 스크롤 방식으로 변경되었습니다.
    • 불필요한 주석 및 미사용 import가 정리되었습니다.
  • 삭제

    • 기존 페이지네이션 방식의 카드/댓글 조회 관련 훅이 삭제되었습니다.

@coderabbitai
Copy link

coderabbitai bot commented Jun 21, 2025

Walkthrough

이 PR은 카드 및 댓글 목록의 무한 스크롤 기능을 도입하며, 관련 API와 훅, 스타일을 이에 맞게 리팩토링합니다. 기존의 페이지네이션 방식 훅과 API 함수는 삭제 또는 변경되었으며, 카드/댓글 모달 및 컬럼 컴포넌트의 레이아웃과 반응형 스타일도 일부 수정되었습니다.

Changes

파일/그룹 변경 요약
Card.tsx, CreateCardForm.tsx, CreateCardModal.tsx, ModifyCardForm.tsx 카드 생성/수정 폼 및 모달의 반응형 스타일 및 불필요 prop/comment 제거
CardContent.tsx, CardModal.tsx 모달 컨테이너에 스크롤 ref 추가, 불필요 import/comment 정리, 내부 div 스타일 단순화
Comments.tsx 페이지네이션 쿼리에서 무한 스크롤 쿼리로 변경, 스크롤 ref prop 추가, 로딩/에러 처리 및 렌더링 로직 수정
Column.tsx 카드 목록을 무한 스크롤로 변경, 로딩/에러/마지막 페이지 UI 추가, 데이터 구조 수정
api/fetchCards.ts, api/fetchComments.ts API 파라미터를 객체로 변경, cursorId 도입, size 기본값 조정, params 전달 방식 단순화
api/useCards.ts, api/useCommentsQuery.ts 기존 페이지네이션 훅 삭제
api/useInfiniteCards.ts, api/useInfiniteComments.ts 무한 스크롤용 커스텀 훅 추가(카드/댓글)
api/usePutCardMutation.ts 불필요 import 제거
api/useUploadCardImage.ts 에러 핸들링 로직 단순화 및 콘솔 출력 추가
hooks/useInfiniteScroll.ts 무한 스크롤 감지용 커스텀 훅 추가
globals.css 입력 컴포넌트의 반응형 width 스타일 추가 및 새로운 width 클래스 정의

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Column
    participant useInfiniteCards
    participant fetchCards
    participant API

    User->>Column: 스크롤하여 하단 접근
    Column->>useInfiniteCards: fetchNextPage() 호출
    useInfiniteCards->>fetchCards: 다음 페이지 요청(cursorId 포함)
    fetchCards->>API: GET /cards?columnId&cursorId
    API-->>fetchCards: 카드 데이터 반환
    fetchCards-->>useInfiniteCards: 카드 데이터 반환
    useInfiniteCards-->>Column: 새 카드 데이터 전달
    Column-->>User: 카드 목록 업데이트
Loading
sequenceDiagram
    participant User
    participant CardModal
    participant Comments
    participant useInfiniteComments
    participant fetchComments
    participant API

    User->>CardModal: 댓글 영역 스크롤
    CardModal->>Comments: scrollRef 전달
    Comments->>useInfiniteComments: fetchNextPage() 호출
    useInfiniteComments->>fetchComments: 다음 댓글 페이지 요청(cursorId 포함)
    fetchComments->>API: GET /comments?cardId&cursorId
    API-->>fetchComments: 댓글 데이터 반환
    fetchComments-->>useInfiniteComments: 댓글 데이터 반환
    useInfiniteComments-->>Comments: 새 댓글 데이터 전달
    Comments-->>User: 댓글 목록 업데이트
Loading

Possibly related PRs

  • CoPlay-FE/coplan#91: 카드 수정 모달 및 ModifyCardForm 초기 구현 관련. 본 PR의 레이아웃/스타일 변경과 직접적으로 연결됨.
  • CoPlay-FE/coplan#95: Card 컴포넌트 및 모달 prop 관련 변경. 본 PR의 코드와 직접적으로 연관됨.

Suggested labels

✨Feat, 💄Modify

Suggested reviewers

  • LeeCh0129

Poem

🐰
무한 스크롤로 카드가 술술,
댓글도 스르륵, 끝이 없네!
스타일도 반응형으로 쏙쏙,
코드가 깔끔해진 오늘 밤,
토끼는 기뻐 깡충깡충!

(카드와 댓글, 이제 스크롤만 해요!)

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-06-21T19_27_53_422Z-debug-0.log

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 5

🧹 Nitpick comments (6)
src/app/globals.css (1)

112-114: 새 클래스 Input-readOnly-217 도입
데스크톱(217px)과 모바일(280px) 대응을 위해 별도 클래스를 추가하셨습니다. 다만 대부분 스타일이 중복되므로, 공통 속성을 베이스 클래스로 분리해 재사용성을 높이는 것을 고려해 보세요.

src/app/features/dashboard_Id/hooks/useInfiniteScroll.ts (1)

11-16: 타입 안전성 개선이 필요합니다

fetchNextPage 매개변수가 () => void 타입으로 정의되어 있지만, 실제로는 Promise를 반환할 수 있는 함수입니다. 타입을 더 정확하게 정의하는 것이 좋겠습니다.

export const useInfiniteScroll = (
-  fetchNextPage: () => void,
+  fetchNextPage: () => void | Promise<void>,
  hasNextPage: boolean,
  isFetchingNextPage: boolean,
  targetRef?: React.RefObject<HTMLElement>,
) => {
src/app/features/dashboard_Id/api/useInfiniteComments.ts (2)

12-12: 주석이 부정확합니다

주석에서 "마지막 카드의 id를 다음 cursor로 사용"이라고 되어 있지만, 실제로는 댓글의 cursor를 사용합니다.

-      // 마지막 카드의 id를 다음 cursor로 사용
+      // 마지막 댓글의 cursor를 다음 페이지 요청에 사용

6-17: 코드 중복을 줄이기 위한 제네릭 훅 고려

useInfiniteCardsuseInfiniteComments가 거의 동일한 패턴을 가지고 있습니다. 제네릭 훅을 만들어 코드 중복을 줄이는 것을 고려해보세요.

다음과 같은 제네릭 훅을 만들 수 있습니다:

function useInfiniteData<T>(
  queryKey: string[],
  fetchFn: (pageParam: number | null) => Promise<T>,
  getNextPageParam: (lastPage: T) => number | undefined
) {
  return useInfiniteQuery<T>({
    queryKey,
    queryFn: ({ pageParam = null }) => fetchFn(pageParam),
    getNextPageParam,
    initialPageParam: null,
  })
}
src/app/features/dashboard_Id/Card/cardModal/CardContent.tsx (1)

33-36: 스크롤 컨테이너 스타일링 검토 필요

overflow-auto와 overflow-y-scroll이 함께 사용되어 중복적일 수 있습니다. overflow-y-scroll만으로도 충분할 것 같습니다.

-      className="BG-white relative max-h-764 min-h-764 w-730 overflow-auto overflow-y-scroll rounded-16 px-18 py-30 shadow-lg [mask-image:radial-gradient(white_100%,transparent_100%)] mobile:w-327 mobile:p-16 tablet:w-678 tablet:px-32 tablet:py-24"
+      className="BG-white relative max-h-764 min-h-764 w-730 overflow-y-scroll rounded-16 px-18 py-30 shadow-lg [mask-image:radial-gradient(white_100%,transparent_100%)] mobile:w-327 mobile:p-16 tablet:w-678 tablet:px-32 tablet:py-24"
src/app/features/dashboard_Id/Card/cardModal/Comments.tsx (1)

26-28: 에러 처리 개선 고려

현재 에러 발생 시에도 컴포넌트가 계속 렌더링됩니다. 에러 상태에서 early return을 고려해보세요.

  if (isError) {
    toast.error('댓글 불러오기 실패')
+   return <div className="text-center text-red-500">댓글을 불러올 수 없습니다.</div>
  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f2b585 and 5101caa.

📒 Files selected for processing (18)
  • src/app/features/dashboard_Id/Card/Card.tsx (0 hunks)
  • src/app/features/dashboard_Id/Card/cardFormModals/CreateCardForm.tsx (1 hunks)
  • src/app/features/dashboard_Id/Card/cardFormModals/CreateCardModal.tsx (1 hunks)
  • src/app/features/dashboard_Id/Card/cardFormModals/ModifyCardForm.tsx (3 hunks)
  • src/app/features/dashboard_Id/Card/cardModal/CardContent.tsx (3 hunks)
  • src/app/features/dashboard_Id/Card/cardModal/CardModal.tsx (1 hunks)
  • src/app/features/dashboard_Id/Card/cardModal/Comments.tsx (1 hunks)
  • src/app/features/dashboard_Id/Column/Column.tsx (5 hunks)
  • src/app/features/dashboard_Id/api/fetchCards.ts (1 hunks)
  • src/app/features/dashboard_Id/api/fetchComments.ts (1 hunks)
  • src/app/features/dashboard_Id/api/useCards.ts (0 hunks)
  • src/app/features/dashboard_Id/api/useCommentsQuery.ts (0 hunks)
  • src/app/features/dashboard_Id/api/useInfiniteCards.ts (1 hunks)
  • src/app/features/dashboard_Id/api/useInfiniteComments.ts (1 hunks)
  • src/app/features/dashboard_Id/api/usePutCardMutation.ts (0 hunks)
  • src/app/features/dashboard_Id/api/useUploadCardImage.ts (1 hunks)
  • src/app/features/dashboard_Id/hooks/useInfiniteScroll.ts (1 hunks)
  • src/app/globals.css (1 hunks)
💤 Files with no reviewable changes (4)
  • src/app/features/dashboard_Id/api/usePutCardMutation.ts
  • src/app/features/dashboard_Id/Card/Card.tsx
  • src/app/features/dashboard_Id/api/useCommentsQuery.ts
  • src/app/features/dashboard_Id/api/useCards.ts
🧰 Additional context used
🧬 Code Graph Analysis (6)
src/app/features/dashboard_Id/api/useInfiniteCards.ts (2)
src/app/features/dashboard_Id/type/Card.type.ts (1)
  • CardResponse (21-25)
src/app/features/dashboard_Id/api/fetchCards.ts (1)
  • fetchCards (6-26)
src/app/features/dashboard_Id/api/useInfiniteComments.ts (2)
src/app/features/dashboard_Id/type/Comment.type.ts (1)
  • CommentsResponse (14-17)
src/app/features/dashboard_Id/api/fetchComments.ts (1)
  • fetchComments (6-26)
src/app/features/dashboard_Id/Card/cardModal/CardContent.tsx (1)
src/app/features/dashboard_Id/Card/cardModal/Comments.tsx (1)
  • Comments (8-46)
src/app/features/dashboard_Id/api/fetchCards.ts (1)
src/app/features/dashboard_Id/type/Card.type.ts (1)
  • CardResponse (21-25)
src/app/features/dashboard_Id/api/fetchComments.ts (1)
src/app/features/dashboard_Id/type/Comment.type.ts (1)
  • CommentsResponse (14-17)
src/app/features/dashboard_Id/Column/Column.tsx (4)
src/app/features/dashboard_Id/type/Column.type.ts (1)
  • Column (1-8)
src/app/features/dashboard_Id/api/useInfiniteCards.ts (1)
  • useInfiniteCards (6-17)
src/app/features/dashboard_Id/hooks/useInfiniteScroll.ts (1)
  • useInfiniteScroll (11-39)
src/app/features/dashboard_Id/type/Card.type.ts (1)
  • Card (6-19)
🔇 Additional comments (33)
src/app/globals.css (2)

106-108: .Input에 모바일 너비 적용 확인
.Input 클래스에 mobile:w-280을 추가하여 모바일에서 폭이 280px로 조정됩니다. Tailwind 설정에 해당 유틸리티가 존재하는지 확인해 주세요.


109-111: .Input-readOnly에 모바일 너비 적용 확인
.Input-readOnly 클래스에도 동일하게 mobile:w-280을 추가했습니다. Tailwind 설정에 이 유틸리티가 포함되어 있는지 확인해 주세요.

src/app/features/dashboard_Id/Card/cardFormModals/CreateCardModal.tsx (1)

17-17: 모바일 레이아웃/패딩 보강
모달 컨테이너에 mobile:h-766 mobile:w-327 mobile:px-16 mobile:py-24를 추가해 모바일 대응을 강화했습니다. 반응형 디자인 기조에 부합하며, UI 테스트 시 의도대로 표시되는지 확인만 부탁드립니다.

src/app/features/dashboard_Id/Card/cardFormModals/ModifyCardForm.tsx (2)

134-134: 모바일에서 컬럼·담당자 영역 세로 정렬
.flex gap-32 컨테이너에 mobile:flex-col을 추가해 모바일에서 세로 방향으로 전환하도록 했습니다. 레이아웃 의도에 맞으며 적절합니다.


153-153: 드롭다운 아이콘 좌측 오프셋 조정
mobile:left-11을 추가해 모바일에서 드롭다운 화살표 아이콘의 좌측 마진을 조정했습니다. 디자인 의도에 부합하며, 정상 동작하는 것으로 확인됩니다.

src/app/features/dashboard_Id/Card/cardFormModals/CreateCardForm.tsx (1)

129-129: 모바일 화살표 위치 수정
드롭다운 아이콘에 mobile:right-8을 추가해 모바일에서 오른쪽 여백을 확보했습니다. 반응형 UX 개선에 기여하며 적절해 보입니다.

src/app/features/dashboard_Id/hooks/useInfiniteScroll.ts (2)

17-30: 스크롤 계산 로직이 잘 구현되었습니다

스크롤 위치 계산과 80% 임계점 설정이 적절하며, 조건 체크(hasNextPage && !isFetchingNextPage)도 올바르게 구현되어 있습니다. useCallback으로 최적화도 잘 되어 있습니다.


32-38: 이벤트 리스너 정리 시 타입 불일치 문제가 있습니다

이벤트 리스너 추가 시에는 el을 사용하지만, 정리 시에는 다시 계산하지 않고 있습니다. 이로 인해 targetRef가 변경되는 경우 문제가 발생할 수 있습니다.

useEffect(() => {
  const el = targetRef?.current ?? window
  el.addEventListener('scroll', handleScroll, { passive: true })
  return () => {
-    el.removeEventListener('scroll', handleScroll)
+    const currentEl = targetRef?.current ?? window
+    currentEl.removeEventListener('scroll', handleScroll)
  }
}, [handleScroll, targetRef])

Likely an incorrect or invalid review comment.

src/app/features/dashboard_Id/Card/cardModal/CardModal.tsx (1)

17-17: 이전 검색에서 --type tsx 옵션이 인식되지 않아 제대로 확인되지 않았습니다. CardModal.tsx 파일을 직접 찾아 클래스명, 인라인 스타일, CSS 모듈 import 여부를 점검하겠습니다.

#!/bin/bash
set -e

# CardModal.tsx 경로 찾기
FILE=$(fd --extension tsx "CardModal.tsx" | head -n 1)
echo "■ 대상 파일: $FILE"

# 파일 상단(1~200라인) 출력
echo "----- 상위 200라인 -----"
sed -n '1,200p' "$FILE"

# className 사용 여부
echo "----- className 검색 -----"
rg "className" -n "$FILE" || echo "=> className 없음"

# 인라인 style 속성 여부
echo "----- style= 검색 -----"
rg "style=" -n "$FILE" || echo "=> 인라인 style 없음"

# CSS 모듈(import .module.css/.scss) import 여부
echo "----- CSS 모듈 import 검색 -----"
rg "import.*\\.module\\." -n "$FILE" || echo "=> CSS 모듈 import 없음"
src/app/features/dashboard_Id/api/useUploadCardImage.ts (1)

16-17: 에러 처리 개선이 잘 되었습니다

서버에서 제공하는 에러 메시지를 추출하여 콘솔에 로깅하는 것은 디버깅에 도움이 됩니다. 구현도 안전하게 옵셔널 체이닝과 nullish coalescing을 사용했습니다.

src/app/features/dashboard_Id/api/useInfiniteCards.ts (1)

11-14: 페이지네이션 로직이 잘 구현되었습니다

getNextPageParam에서 카드 목록이 비어있으면 undefined를 반환하여 더 이상 페이지가 없음을 표시하고, 있으면 cursorId를 반환하는 로직이 올바릅니다.

src/app/features/dashboard_Id/Card/cardModal/CardContent.tsx (3)

2-2: useRef import 추가 승인

무한 스크롤을 위한 modalScrollRef 생성에 필요한 useRef를 적절히 import했습니다.


30-30: modalScrollRef 생성 승인

HTMLDivElement 타입으로 올바르게 ref를 생성했습니다.


167-167: Comments 컴포넌트에 scrollRef 전달 승인

modalScrollRef를 Comments 컴포넌트의 scrollRef prop으로 올바르게 전달했습니다. 무한 스크롤 구현에 필요한 변경사항입니다.

src/app/features/dashboard_Id/api/fetchCards.ts (3)

6-14: 함수 시그니처 변경 승인

cursor 기반 무한 스크롤을 지원하기 위한 함수 시그니처 변경이 적절합니다. 객체 파라미터 구조와 타입 정의가 올바릅니다.


8-8: 기본 size 값 변경 승인

size 기본값을 10에서 6으로 줄인 것은 무한 스크롤에서 더 빠른 로딩과 더 나은 UX를 위한 합리적인 결정입니다.


15-24: API 호출 방식 개선 승인

params 객체를 사용한 API 호출 방식과 cursorId의 조건부 포함 로직이 깔끔하고 올바르게 구현되었습니다.

src/app/features/dashboard_Id/api/fetchComments.ts (3)

6-14: 함수 시그니처 변경 승인

fetchCards와 일관된 패턴으로 cursor 기반 무한 스크롤을 지원하도록 변경되었습니다. 구조와 타입 정의가 올바릅니다.


8-8: 댓글 기본 size 값 적절

댓글의 기본 size를 5로 설정한 것은 모달 내에서 보여줄 댓글 수로 적절합니다.


15-24: API 호출 방식 승인

params 객체를 사용한 API 호출과 cursorId 조건부 포함 로직이 올바르게 구현되었습니다.

src/app/features/dashboard_Id/Card/cardModal/Comments.tsx (6)

3-4: 새로운 훅 import 승인

무한 스크롤 구현을 위한 useInfiniteComments와 useInfiniteScroll 훅의 import가 적절합니다.


8-14: 컴포넌트 Props 변경 승인

scrollRef prop 추가로 스크롤 컨테이너를 지정할 수 있게 된 것은 무한 스크롤 구현에 필수적인 변경사항입니다.


15-22: useInfiniteComments 훅 사용 승인

무한 스크롤에 필요한 모든 상태값들을 적절히 구조분해할당으로 받아왔습니다.


24-24: useInfiniteScroll 훅 사용 승인

무한 스크롤 트리거를 위한 훅 사용이 올바르게 구현되었습니다.


30-37: 댓글 렌더링 로직 승인

pages 배열을 순회하며 각 페이지의 댓글들을 렌더링하는 로직이 올바르게 구현되었습니다.


39-43: 로딩 상태 표시 승인

isFetchingNextPage 상태에 따른 로딩 메시지 표시가 적절합니다. 사용자 경험을 향상시킵니다.

src/app/features/dashboard_Id/Column/Column.tsx (7)

3-3: toast import 추가 승인

에러 처리를 위한 toast import가 적절합니다.


8-8: 새로운 훅 import 승인

무한 스크롤 구현을 위한 useInfiniteCards와 useInfiniteScroll 훅의 import가 적절합니다.

Also applies to: 12-12


18-25: useInfiniteCards 훅 사용 승인

무한 스크롤에 필요한 모든 상태값들을 적절히 구조분해할당으로 받아왔습니다.


27-27: useInfiniteScroll 훅 사용 승인

window 기반 무한 스크롤 트리거가 올바르게 구현되었습니다. 컬럼 컴포넌트에서는 window 스크롤을 사용하는 것이 적절합니다.


79-79: totalCount 접근 방식 승인

첫 번째 페이지에서 totalCount를 가져오는 방식이 적절하며, null 체크도 올바르게 되어 있습니다.


104-108: 카드 렌더링 로직 승인

pages 배열을 순회하며 각 페이지의 카드들을 렌더링하는 로직이 올바르게 구현되었습니다.


119-130: 무한 스크롤 상태 표시 승인

isFetchingNextPage와 hasNextPage 상태에 따른 적절한 피드백 메시지가 구현되어 사용자 경험을 향상시킵니다.

value={selectedColumn?.columnTitle ?? ''}
readOnly
className="Input-readOnly w-217"
className="Input-readOnly-217"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

입력 필드 크기 불일치
Input-readOnly-217 인풋 너비(217px)가 래퍼의 w-207(207px)보다 커 오버플로우가 발생할 수 있습니다. 래퍼 또는 인풋 클래스 중 하나를 w-217 또는 w-207으로 일치시키는 패치가 필요합니다.

- <div className="relative w-207">
+ <div className="relative w-217">

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/Card/cardFormModals/ModifyCardForm.tsx at line
147, the input field has a class setting its width to 217px, but its wrapper has
a width of 207px, causing overflow issues. To fix this, adjust either the
input's class to match the wrapper's width by changing it to 207px or update the
wrapper's width class to 217px so both widths are consistent and prevent
overflow.


export function useInfiniteCards(columnId: number) {
return useInfiniteQuery<CardResponse>({
queryKey: ['columnId', columnId],
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

쿼리 키 네이밍이 부정확합니다

쿼리 키에 'columnId'라는 문자열이 사용되었지만, 실제로는 cards 데이터를 가져오는 쿼리입니다. 의미가 더 명확한 키 이름을 사용하는 것이 좋겠습니다.

-    queryKey: ['columnId', columnId],
+    queryKey: ['cards', columnId],
📝 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
queryKey: ['columnId', columnId],
queryKey: ['cards', columnId],
🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/api/useInfiniteCards.ts at line 8, the query
key uses the string 'columnId' which is misleading since the query fetches cards
data. Rename the query key to a more descriptive name like 'cards' or
'cardsByColumn' to accurately reflect the data being queried.

Comment on lines +9 to +10
queryFn: ({ pageParam = null }) =>
fetchCards({ columnId, cursorId: pageParam as number }),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

타입 캐스팅 안전성을 개선해야 합니다

pageParam as number로 강제 타입 캐스팅을 하고 있지만, initialPageParamnull이므로 첫 번째 호출 시 타입 불일치가 발생할 수 있습니다.

    queryFn: ({ pageParam = null }) =>
-      fetchCards({ columnId, cursorId: pageParam as number }),
+      fetchCards({ columnId, cursorId: pageParam as number | null }),
📝 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
queryFn: ({ pageParam = null }) =>
fetchCards({ columnId, cursorId: pageParam as number }),
queryFn: ({ pageParam = null }) =>
fetchCards({ columnId, cursorId: pageParam as number | null }),
🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/api/useInfiniteCards.ts around lines 9 to 10,
the code forcibly casts pageParam to number using 'pageParam as number', but
since initialPageParam can be null, this causes a type mismatch on the first
call. To fix this, add a runtime check to handle the case when pageParam is null
before casting, ensuring the fetchCards function receives a valid number or
handles null appropriately without unsafe casting.

Comment on lines +9 to +10
queryFn: ({ pageParam = null }) =>
fetchComments({ cardId, cursorId: pageParam as number }),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

타입 캐스팅 안전성을 개선해야 합니다

useInfiniteCards와 동일한 문제입니다. pageParam as number로 강제 타입 캐스팅을 하고 있지만, initialPageParamnull이므로 첫 번째 호출 시 타입 불일치가 발생할 수 있습니다.

    queryFn: ({ pageParam = null }) =>
-      fetchComments({ cardId, cursorId: pageParam as number }),
+      fetchComments({ cardId, cursorId: pageParam as number | null }),
📝 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
queryFn: ({ pageParam = null }) =>
fetchComments({ cardId, cursorId: pageParam as number }),
queryFn: ({ pageParam = null }) =>
fetchComments({ cardId, cursorId: pageParam as number | null }),
🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/api/useInfiniteComments.ts around lines 9 to
10, the code forcibly casts pageParam to number despite initialPageParam being
null, causing a type mismatch on the first call. To fix this, add a runtime
check to handle the null case properly before casting, ensuring that
fetchComments receives a valid number or handles null safely without forced
casting.

Comment on lines +37 to +38
if (isLoading) return <p>loading...</p> // 스켈레톤 적용???⭐️
if (isError) return toast.error('할 일 불러오기 실패')
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

로딩과 에러 처리 개선 필요

현재 로딩 상태에서 간단한 텍스트만 표시하고, 에러 상태에서도 컴포넌트가 계속 렌더링됩니다. 개선을 고려해보세요.

- if (isLoading) return <p>loading...</p> // 스켈레톤 적용???⭐️
- if (isError) return toast.error('할 일 불러오기 실패')
+ if (isLoading) {
+   return (
+     <div className="BG-gray Border-column flex w-354 shrink-0 flex-col gap-16 p-20 tablet:w-584">
+       <div className="mb-24 flex items-center justify-between">
+         <div className="flex items-center">
+           <div className="BG-blue mb-7 mr-8 size-8 rounded-25"></div>
+           <h2 className="mb-3 mr-12 h-21 text-18 font-bold">{title}</h2>
+         </div>
+       </div>
+       <p className="text-center text-gray-500">카드를 불러오는 중...</p>
+     </div>
+   )
+ }
+ if (isError) {
+   toast.error('할 일 불러오기 실패')
+   return (
+     <div className="BG-gray Border-column flex w-354 shrink-0 flex-col gap-16 p-20 tablet:w-584">
+       <div className="mb-24 flex items-center justify-between">
+         <div className="flex items-center">
+           <div className="BG-blue mb-7 mr-8 size-8 rounded-25"></div>
+           <h2 className="mb-3 mr-12 h-21 text-18 font-bold">{title}</h2>
+         </div>
+       </div>
+       <p className="text-center text-red-500">카드를 불러올 수 없습니다.</p>
+     </div>
+   )
+ }
📝 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
if (isLoading) return <p>loading...</p> // 스켈레톤 적용???⭐️
if (isError) return toast.error('할 일 불러오기 실패')
if (isLoading) {
return (
<div className="BG-gray Border-column flex w-354 shrink-0 flex-col gap-16 p-20 tablet:w-584">
<div className="mb-24 flex items-center justify-between">
<div className="flex items-center">
<div className="BG-blue mb-7 mr-8 size-8 rounded-25"></div>
<h2 className="mb-3 mr-12 h-21 text-18 font-bold">{title}</h2>
</div>
</div>
<p className="text-center text-gray-500">카드를 불러오는 중...</p>
</div>
)
}
if (isError) {
toast.error('할 일 불러오기 실패')
return (
<div className="BG-gray Border-column flex w-354 shrink-0 flex-col gap-16 p-20 tablet:w-584">
<div className="mb-24 flex items-center justify-between">
<div className="flex items-center">
<div className="BG-blue mb-7 mr-8 size-8 rounded-25"></div>
<h2 className="mb-3 mr-12 h-21 text-18 font-bold">{title}</h2>
</div>
</div>
<p className="text-center text-red-500">카드를 불러올 수 없습니다.</p>
</div>
)
}
🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/Column/Column.tsx around lines 37 to 38, the
current loading state only shows simple text and the error state triggers a
toast but still renders the component. Improve this by replacing the loading
text with a proper skeleton loader component and prevent rendering the main
component when there is an error by returning null or an error message component
instead of continuing to render.

@dkslel1225 dkslel1225 merged commit 411d887 into feature/dashboard_id Jun 21, 2025
1 check passed
@dkslel1225 dkslel1225 self-assigned this Jun 22, 2025
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.

2 participants