Skip to content

Conversation

@dkslel1225
Copy link
Contributor

@dkslel1225 dkslel1225 commented Jun 19, 2025

📌 변경 사항 개요

✨ 요약

📝 상세 내용

🔗 관련 이슈

🖼️ 스크린샷

✅ 체크리스트

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

💡 참고 사항

Summary by CodeRabbit

  • 신규 기능

    • 카드 생성, 이미지 업로드, 담당자 지정, 태그, 마감일 등 다양한 필드를 지원하는 카드 생성 폼 및 모달이 추가되었습니다.
    • 컬럼, 카드, 멤버 데이터를 조회하는 기능과 관련된 커스텀 훅과 API 함수가 도입되었습니다.
    • 대시보드 내 모달 출력을 위한 전용 DOM 컨테이너가 추가되었습니다.
  • 버그 수정

    • 태그, 아바타 등에서 색상 계산 로직이 개선되어 일관된 색상 표시가 가능합니다.
    • 카드 및 컬럼 렌더링 시 불필요한 요소 및 중복 렌더링이 제거되었습니다.
  • 스타일

    • 입력 필드, 읽기 전용 입력, 텍스트, 호버 효과 등 다양한 입력 관련 CSS 유틸리티 클래스가 추가되었습니다.
  • 리팩터링

    • 카드, 컬럼, 멤버 등 데이터 타입 및 API 관련 코드가 기능별 폴더로 분리되어 구조가 개선되었습니다.
    • 내부 API 클라이언트 명칭 및 베이스 URL이 통일되었습니다.
  • 문서화

    • 타입스크립트 인터페이스가 추가되어 데이터 구조가 명확하게 정의되었습니다.
  • 기타

    • 불필요한 파일 및 코드가 제거되고, 일부 내부 로직이 최적화되었습니다.

jyn and others added 8 commits June 18, 2025 18:11
- UI, 기능 적용. 완성도 80%
- 폴더 구조 변경
-폼데이터의 디폴트값을 빈 문자열로 설정
- onSubmit함수에서는 해당 란을 제외한 데이터를 보냄.

(빈 문자열로 디폴트값 설정 안하면, 기본 파일리스트 객체가 전달되기 때문에 이미지 URL이 있는지 참,거짓 측정이 불가함)
…Modal

✨ Feat: create card modal 카드 생성 모달 구현
@coderabbitai
Copy link

coderabbitai bot commented Jun 19, 2025

Walkthrough

카드와 컬럼 관련 API 훅 및 타입이 기존의 공통 폴더에서 features/dashboard_Id 폴더로 리팩토링 및 이전되었습니다. 카드 생성, 이미지 업로드, 담당자 선택 등 신규 컴포넌트가 도입되었고, 관련 타입 및 유틸 함수도 추가되었습니다. 모달 전용 root DOM, 입력 컴포넌트, CSS 유틸리티 클래스도 새로 추가되었습니다.

Changes

파일/경로 요약 변경 요약
package.json date-fns, react-datepicker 신규 의존성 추가
src/app/api/useCards.ts, src/app/api/useColumns.ts 카드/컬럼 관련 기존 API 훅 및 타입 삭제
src/app/dashboard/[id]/api/updateCardColumn.ts 카드 컬럼 이동 API 함수 삭제
src/app/dashboard/[id]/layout.tsx AboutLayout 레이아웃 컴포넌트 신규 추가
src/app/dashboard/[id]/page.tsx 컬럼/카드 훅 및 타입 import 경로 변경, 동적 dashboardId 적용, sidebar 제거 등 리팩토링
src/app/features/dashboardId/Card/Tags.tsx getColor 함수 인자 변경(배열 → 길이)
src/app/features/dashboardId/Card/cardFormModals/AssigneeList.tsx 담당자 리스트 드롭다운 컴포넌트 신규 추가(간단 버전)
src/app/features/dashboard_Id/Card/Card.tsx 타입 import 경로 변경, dueDate/assignee 조건부 렌더링 등 내부 구현 개선
src/app/features/dashboard_Id/Card/Tags.tsx 태그 리스트 컴포넌트 신규 추가
src/app/features/dashboard_Id/Card/cardFormModals/AssigneeList.tsx 담당자 리스트 드롭다운 컴포넌트(멤버, setAssignee, form control 연동) 신규 추가
src/app/features/dashboard_Id/Card/cardFormModals/CreateCardForm.tsx 카드 생성 폼 컴포넌트 신규 추가
src/app/features/dashboard_Id/Card/cardFormModals/CreateCardModal.tsx 카드 생성 모달 컴포넌트 신규 추가
src/app/features/dashboard_Id/Card/cardFormModals/input/DateInput.tsx 날짜 입력 컴포넌트 신규 추가
src/app/features/dashboard_Id/Card/cardFormModals/input/Input.tsx 라벨+입력 래퍼 컴포넌트 신규 추가
src/app/features/dashboard_Id/Column/Column.tsx 카드 생성 모달/폼 연동, 드래그 이벤트 개선 등 내부 리팩토링
src/app/features/dashboard_Id/api/fetchCards.ts 카드 목록 조회 API 함수 신규 추가
src/app/features/dashboard_Id/api/fetchColumns.ts 컬럼 목록 조회 API 함수 신규 추가
src/app/features/dashboard_Id/api/fetchMembers.ts 대시보드 멤버 조회 API 함수 신규 추가
src/app/features/dashboard_Id/api/postCard.ts 카드 생성 API 함수 신규 추가
src/app/features/dashboard_Id/api/postCardImage.ts 카드 이미지 업로드 API 함수 신규 추가
src/app/features/dashboard_Id/api/updateCardColumn.ts 카드 컬럼 이동 API 함수 신규 추가
src/app/features/dashboard_Id/api/useCardMutation.ts 카드/컬럼 타입 import 경로 변경, 불필요 공백 정리
src/app/features/dashboard_Id/api/useCards.ts 카드 목록 조회 React Query 훅 신규 추가
src/app/features/dashboard_Id/api/useColumns.ts 컬럼 목록 조회 React Query 훅 신규 추가
src/app/features/dashboard_Id/api/useMembers.ts 대시보드 멤버 조회 React Query 훅 신규 추가
src/app/features/dashboard_Id/api/usePostCard.ts 카드 생성 Mutation 훅 신규 추가
src/app/features/dashboard_Id/api/useUploadCardImage.ts 카드 이미지 업로드 Mutation 훅 신규 추가
src/app/features/dashboard_Id/lib/getDashboardMembers.ts 멤버 → 담당자 변환 유틸 함수 및 타입 신규 추가
src/app/features/dashboard_Id/type/Card.type.ts 카드 응답 타입(CardResponse) 신규 추가
src/app/features/dashboard_Id/type/CardFormData.type.ts 카드 폼 데이터 타입 신규 추가
src/app/features/dashboard_Id/type/Column.type.ts 컬럼, 컬럼 응답 타입 신규 추가
src/app/features/dashboard_Id/type/Member.type.ts 멤버, 멤버 응답 타입 신규 추가
src/app/globals.css 입력 관련 유틸리티 CSS 클래스 신규 추가
src/app/layout.tsx <div id="modal-root" /> 추가(모달 포털용)
src/app/shared/components/common/Avatar.tsx getColor 인자 변경(배열 → 길이)
src/app/shared/lib/getColor.ts getColor 함수 시그니처 변경(배열 → 길이)
src/app/shared/lib/testAxios.ts axios 인스턴스명 변경 및 baseURL 수정

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DashboardPage
    participant CreateCardModal
    participant CreateCardForm
    participant usePostCard
    participant API

    User->>DashboardPage: "카드 생성" 버튼 클릭
    DashboardPage->>CreateCardModal: 모달 열기
    CreateCardModal->>CreateCardForm: 폼 렌더링
    User->>CreateCardForm: 카드 정보 입력 및 제출
    CreateCardForm->>usePostCard: 카드 생성 요청
    usePostCard->>API: POST /cards
    API-->>usePostCard: 카드 생성 결과 반환
    usePostCard-->>CreateCardForm: 성공/실패 콜백
    CreateCardForm-->>CreateCardModal: onClose 호출(성공시)
    CreateCardModal-->>DashboardPage: 모달 닫기, 목록 갱신
Loading

Possibly related PRs

  • CoPlay-FE/coplan#48: 카드 드래그앤드롭 및 컬럼 이동 API 호출 기능의 최초 구현과 직접적으로 관련됨
  • CoPlay-FE/coplan#37: 이번 PR에서 삭제/이전된 카드/컬럼 API 훅 및 타입의 최초 구현 PR로, 코드 레벨에서 긴밀히 연결됨
  • CoPlay-FE/coplan#82: 동일한 신규 의존성, 모달/폼/훅/타입 추가 등 주요 변경점이 상당 부분 중복됨

Suggested labels

✨Feat, 📁Chore

Suggested reviewers

  • Insung-Jo
  • LeeCh0129

Poem

🐰
새 폴더에 옮긴 카드와 컬럼,
모달 속에 피어난 새 입력폼.
담당자 드롭다운, 태그도 반짝,
CSS도 새 옷 갈아입고,
API 훅은 더 똑똑해졌네!
토끼는 깡총, 코드도 깔끔—
대시보드가 한층 더 빛나요!

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-19T11_08_59_970Z-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: 8

🔭 Outside diff range comments (1)
src/app/features/dashboardId/Card/cardFormModals/AssigneeList.tsx (1)

1-30: 파일 경로 일관성 확인 필요

현재 파일 경로가 dashboardId인데, 다른 파일들은 dashboard_Id를 사용하고 있습니다. 이는 일관성 문제를 야기할 수 있습니다.

폴더 구조의 일관성을 위해 다음 중 하나를 선택해야 합니다:

  1. 이 파일을 src/app/features/dashboard_Id/Card/cardFormModals/AssigneeList.tsx로 이동
  2. 또는 다른 파일들을 dashboardId 구조로 통일

파일 경로 불일치로 인한 임포트 오류가 발생할 수 있습니다.

♻️ Duplicate comments (1)
src/app/features/dashboard_Id/api/fetchCards.ts (1)

1-3: testAxios 사용에 대한 일관성 확인

fetchColumns.ts와 동일하게 테스트용 axios 인스턴스를 사용하고 있습니다. 프로덕션 환경에서의 사용 의도를 확인해주세요.

🧹 Nitpick comments (16)
src/app/features/dashboard_Id/api/useMembers.ts (1)

9-9: 쿼리 키를 더 구체적으로 변경하는 것을 권장합니다.

현재 ['dashboardId', dashboardId] 키는 너무 일반적이어서 다른 대시보드 관련 쿼리와 충돌할 가능성이 있습니다.

다음과 같이 변경하는 것을 권장합니다:

-    queryKey: ['dashboardId', dashboardId],
+    queryKey: ['members', dashboardId],
src/app/features/dashboard_Id/api/usePostCard.ts (1)

15-22: 사용자 피드백 일관성 개선 필요

다른 훅(useUploadCardImage)에서는 토스트 알림으로 에러를 표시하지만, 여기서는 콘솔 로그만 출력합니다. 사용자 경험 일관성을 위해 토스트 알림 추가를 고려해보세요.

toast 추가 예시:

import { useMutation, useQueryClient } from '@tanstack/react-query'
import axios from 'axios'
+import { toast } from 'sonner'

import { postCard } from './postCard'

export function usePostCard() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: postCard,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['columnId'] })
+     toast.success('카드가 성공적으로 생성되었습니다.')
    },
    onError: (error) => {
      if (axios.isAxiosError(error)) {
        const message = error.response?.data?.message
-       console.error('카드 생성 실패:', message ?? '알 수 없는 에러')
+       toast.error(message ?? '카드 생성에 실패했습니다.')
      } else {
-       console.error('카드 생성 실패:', error)
+       toast.error('카드 생성에 실패했습니다.')
      }
    },
  })
}
src/app/features/dashboard_Id/api/fetchCards.ts (1)

6-14: 기본 구현은 적절하지만 입력 검증 추가 고려

fetchColumns와 달리 columnId 유효성 검사가 없습니다. 일관성을 위해 추가를 고려해보세요.

입력 검증 추가 예시:

export async function fetchCards(
  columnId: number,
  size: number = 10,
): Promise<CardResponse> {
+ if (!columnId) {
+   throw new Error('columnId가 유효하지 않습니다.')
+ }
+ 
  const res = await api.get<CardResponse>(
    `/${process.env.NEXT_PUBLIC_TEAM_ID}/cards?size=${size}&columnId=${columnId}`,
  )
  return res.data
}
src/app/features/dashboard_Id/api/postCard.ts (1)

6-8: ApiResponse 인터페이스를 공통 타입으로 분리하세요.

ApiResponse 인터페이스가 다른 API 함수들에서도 재사용될 가능성이 높습니다.

공통 타입 파일로 분리하여 재사용성을 높이세요:

// src/app/features/dashboard_Id/type/ApiResponse.type.ts
export interface ApiResponse {
  message: string
}
src/app/features/dashboard_Id/api/postCardImage.ts (1)

15-15: 주석에 오타가 있습니다.

주석 끝부분의 's'를 제거하세요.

-        'Content-Type': 'multipart/form-data', // multipart/form-data 방식으로 파일 전송: 모든 문자를 인코딩하지 않음s
+        'Content-Type': 'multipart/form-data', // multipart/form-data 방식으로 파일 전송: 모든 문자를 인코딩하지 않음
src/app/features/dashboard_Id/Card/Tags.tsx (2)

6-7: 색상 배열을 상수로 분리하세요.

하드코딩된 색상 배열을 상수로 분리하여 재사용성과 유지보수성을 높이세요.

+const TAG_COLORS = {
+  backgrounds: ['#F9EEE3', '#E7F7DB', '#F7DBF0', '#DBE6F7'],
+  texts: ['#D58D49', '#86D549', '#D549B6', '#4981D5'],
+} as const

export default function Tags({ tags }: { tags: string[] }) {
  //태그 컬러 - 랜덤 배정
  //카드 생성 시 - 동일 태그 입력 불가하도록
-  const bgColors = ['#F9EEE3', '#E7F7DB', '#F7DBF0', '#DBE6F7']
-  const textColors = ['#D58D49', '#86D549', '#D549B6', '#4981D5']
+  const bgColors = TAG_COLORS.backgrounds
+  const textColors = TAG_COLORS.texts

14-25: 접근성을 개선하세요.

태그 요소에 의미론적 역할을 추가하여 스크린 리더 사용자에게 더 나은 경험을 제공하세요.

          <span
            key={tag}
            className="inline-block whitespace-nowrap rounded-4 px-9 pb-3 pt-5"
+            role="button"
+            tabIndex={0}
+            aria-label={`태그: ${tag}`}
            style={{
              backgroundColor: bgColors[colorIndex],
              color: textColors[colorIndex],
            }}
          >
            {tag}
          </span>
src/app/features/dashboard_Id/Card/cardFormModals/input/DateInput.tsx (1)

9-18: 접근성 속성을 추가하세요.

날짜 입력 필드에 접근성 속성을 추가하여 사용자 경험을 개선하세요.

    <input
      ref={ref}
      {...restProps}
      inputMode="none"
      readOnly
      className="Input-readOnly"
      id={id}
      placeholder="날짜와 시간을 선택하세요"
+      aria-label="마감일 선택"
+      aria-describedby="dueDate-description"
    />
src/app/features/dashboard_Id/Card/cardFormModals/input/Input.tsx (1)

2-7: 인터페이스 네이밍 개선이 필요합니다.

인터페이스 이름이 컴포넌트 이름과 동일하여 충돌할 수 있습니다. 더 명확한 이름으로 변경하는 것이 좋겠습니다.

-interface Input {
+interface InputProps {
   children: React.ReactNode
   labelName: string
   labelFor: string
   accent?: boolean
 }
src/app/features/dashboard_Id/api/useColumns.ts (1)

6-11: 에러 처리 개선을 고려해보세요.

현재 fetchColumns 함수에서 에러 처리를 하고 있지만, 이 훅에서도 추가적인 에러 처리나 로딩 상태 관리가 필요한지 검토해보시기 바랍니다.

필요하다면 다음과 같이 추가 옵션을 고려할 수 있습니다:

 export default function useColumns(dashboardId: number) {
   return useQuery<Column[]>({
     queryKey: ['columns', dashboardId],
     queryFn: () => fetchColumns(dashboardId),
+    enabled: Boolean(dashboardId),
+    staleTime: 1000 * 60 * 5, // 5분
   })
 }
src/app/features/dashboard_Id/api/fetchMembers.ts (1)

6-11: API 호출에 에러 핸들링 추가 권장

현재 함수에는 에러 핸들링이 없어 네트워크 오류나 API 응답 오류 시 처리가 어려울 수 있습니다.

다음과 같이 에러 핸들링을 추가하는 것을 고려해보세요:

 export async function fetchMembers(dashboardId: number): Promise<Member[]> {
-  const res = await api.get<MembersResponse>(
-    `/${process.env.NEXT_PUBLIC_TEAM_ID}/members?&dashboardId=${dashboardId}`,
-  )
-  return res.data.members
+  try {
+    const res = await api.get<MembersResponse>(
+      `/${process.env.NEXT_PUBLIC_TEAM_ID}/members?&dashboardId=${dashboardId}`,
+    )
+    return res.data.members
+  } catch (error) {
+    console.error('Failed to fetch members:', error)
+    throw error
+  }
 }
src/app/dashboard/[id]/layout.tsx (2)

3-3: 사용하지 않는 Sidebar 임포트 제거

Sidebar를 임포트했지만 주석 처리되어 실제로 사용하지 않고 있습니다.

사용하지 않는 임포트를 제거하는 것을 권장합니다:

-import Sidebar from '@/app/shared/components/common/sidebar/Sidebar'

Also applies to: 12-12


5-5: 컴포넌트 이름이 용도와 일치하지 않음

컴포넌트 이름이 AboutLayout이지만 실제로는 대시보드 레이아웃에 사용되고 있습니다.

컴포넌트 이름을 용도에 맞게 변경하는 것을 고려해보세요:

-export default function AboutLayout({
+export default function DashboardLayout({
src/app/features/dashboardId/Card/cardFormModals/AssigneeList.tsx (1)

21-21: 개발용 console.log 제거 필요

프로덕션 코드에서 디버깅용 console.log를 제거해야 합니다.

다음과 같이 console.log를 제거하세요:

 onClick={() => {
   setSelectedAssignee(Assignee)
-  console.log(Assignee)
 }}
src/app/dashboard/[id]/page.tsx (1)

17-17: 주석 처리된 코드를 제거해주세요.

사용하지 않는 주석 처리된 코드는 코드 가독성을 해치므로 제거하는 것이 좋습니다.

-  // const { data: columns, isLoading, error } = useColumns(id)
src/app/features/dashboard_Id/Card/cardFormModals/CreateCardForm.tsx (1)

92-94: 디버그 코드를 제거해주세요.

프로덕션 코드에서 console.log는 제거하는 것이 좋습니다.

-    console.log('submitted', payload)
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3a4833c and b9c5436.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • public/images/plus.svg is excluded by !**/*.svg
📒 Files selected for processing (38)
  • package.json (1 hunks)
  • src/app/api/useCards.ts (0 hunks)
  • src/app/api/useColumns.ts (0 hunks)
  • src/app/dashboard/[id]/api/updateCardColumn.ts (0 hunks)
  • src/app/dashboard/[id]/layout.tsx (1 hunks)
  • src/app/dashboard/[id]/page.tsx (2 hunks)
  • src/app/features/dashboardId/Card/Tags.tsx (1 hunks)
  • src/app/features/dashboardId/Card/cardFormModals/AssigneeList.tsx (1 hunks)
  • src/app/features/dashboard_Id/Card/Card.tsx (3 hunks)
  • src/app/features/dashboard_Id/Card/Tags.tsx (1 hunks)
  • src/app/features/dashboard_Id/Card/cardFormModals/AssigneeList.tsx (1 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/input/DateInput.tsx (1 hunks)
  • src/app/features/dashboard_Id/Card/cardFormModals/input/Input.tsx (1 hunks)
  • src/app/features/dashboard_Id/Column/Column.tsx (6 hunks)
  • src/app/features/dashboard_Id/api/fetchCards.ts (1 hunks)
  • src/app/features/dashboard_Id/api/fetchColumns.ts (1 hunks)
  • src/app/features/dashboard_Id/api/fetchMembers.ts (1 hunks)
  • src/app/features/dashboard_Id/api/postCard.ts (1 hunks)
  • src/app/features/dashboard_Id/api/postCardImage.ts (1 hunks)
  • src/app/features/dashboard_Id/api/updateCardColumn.ts (1 hunks)
  • src/app/features/dashboard_Id/api/useCardMutation.ts (2 hunks)
  • src/app/features/dashboard_Id/api/useCards.ts (1 hunks)
  • src/app/features/dashboard_Id/api/useColumns.ts (1 hunks)
  • src/app/features/dashboard_Id/api/useMembers.ts (1 hunks)
  • src/app/features/dashboard_Id/api/usePostCard.ts (1 hunks)
  • src/app/features/dashboard_Id/api/useUploadCardImage.ts (1 hunks)
  • src/app/features/dashboard_Id/lib/getDashboardMembers.ts (1 hunks)
  • src/app/features/dashboard_Id/type/Card.type.ts (1 hunks)
  • src/app/features/dashboard_Id/type/CardFormData.type.ts (1 hunks)
  • src/app/features/dashboard_Id/type/Column.type.ts (1 hunks)
  • src/app/features/dashboard_Id/type/Member.type.ts (1 hunks)
  • src/app/globals.css (2 hunks)
  • src/app/layout.tsx (1 hunks)
  • src/app/shared/components/common/Avatar.tsx (1 hunks)
  • src/app/shared/lib/getColor.ts (1 hunks)
  • src/app/shared/lib/testAxios.ts (1 hunks)
💤 Files with no reviewable changes (3)
  • src/app/dashboard/[id]/api/updateCardColumn.ts
  • src/app/api/useColumns.ts
  • src/app/api/useCards.ts
🧰 Additional context used
🧬 Code Graph Analysis (19)
src/app/features/dashboardId/Card/Tags.tsx (1)
src/app/shared/lib/getColor.ts (1)
  • getColor (1-5)
src/app/shared/components/common/Avatar.tsx (1)
src/app/shared/lib/getColor.ts (1)
  • getColor (1-5)
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/fetchColumns.ts (1)
src/app/features/dashboard_Id/type/Column.type.ts (2)
  • Column (1-8)
  • ColumnsResponse (9-11)
src/app/features/dashboard_Id/api/postCard.ts (1)
src/app/features/dashboard_Id/type/CardFormData.type.ts (1)
  • CardFormData (1-10)
src/app/features/dashboard_Id/Card/Tags.tsx (1)
src/app/shared/lib/getColor.ts (1)
  • getColor (1-5)
src/app/features/dashboard_Id/api/usePostCard.ts (1)
src/app/features/dashboard_Id/api/postCard.ts (1)
  • postCard (10-16)
src/app/features/dashboard_Id/api/useCards.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-14)
src/app/features/dashboard_Id/api/useUploadCardImage.ts (1)
src/app/features/dashboard_Id/api/postCardImage.ts (1)
  • postCardImages (4-21)
src/app/dashboard/[id]/layout.tsx (1)
src/app/shared/components/common/header/Header.tsx (1)
  • Header (12-66)
src/app/features/dashboard_Id/api/useMembers.ts (2)
src/app/features/dashboard_Id/type/Member.type.ts (1)
  • Member (1-10)
src/app/features/dashboard_Id/api/fetchMembers.ts (1)
  • fetchMembers (6-11)
src/app/features/dashboard_Id/api/useColumns.ts (2)
src/app/features/dashboard_Id/type/Column.type.ts (1)
  • Column (1-8)
src/app/features/dashboard_Id/api/fetchColumns.ts (1)
  • fetchColumns (6-16)
src/app/features/dashboard_Id/api/fetchMembers.ts (1)
src/app/features/dashboard_Id/type/Member.type.ts (2)
  • Member (1-10)
  • MembersResponse (11-14)
src/app/features/dashboard_Id/type/Column.type.ts (1)
src/app/features/dashboard_Id/Column/Column.tsx (1)
  • Column (13-105)
src/app/features/dashboardId/Card/cardFormModals/AssigneeList.tsx (3)
src/app/features/dashboard_Id/Card/cardFormModals/AssigneeList.tsx (2)
  • AssigneeList (24-50)
  • Assignee (9-12)
src/app/features/dashboard_Id/type/Card.type.ts (1)
  • Assignee (1-5)
src/app/shared/lib/cn.ts (1)
  • cn (4-6)
src/app/features/dashboard_Id/Card/Card.tsx (2)
src/app/features/dashboard_Id/Card/Tags.tsx (1)
  • Tags (3-29)
src/app/shared/components/common/Avatar.tsx (1)
  • Avatar (32-61)
src/app/dashboard/[id]/page.tsx (1)
src/app/features/dashboard_Id/api/useColumns.ts (1)
  • useColumns (6-11)
src/app/features/dashboard_Id/lib/getDashboardMembers.ts (2)
src/app/features/dashboard_Id/Card/cardFormModals/AssigneeList.tsx (1)
  • Assignee (9-12)
src/app/features/dashboard_Id/type/Member.type.ts (1)
  • Member (1-10)
src/app/features/dashboard_Id/Card/cardFormModals/CreateCardForm.tsx (7)
src/app/features/dashboard_Id/api/useUploadCardImage.ts (1)
  • useUploadCardImage (8-19)
src/app/features/dashboard_Id/api/useMembers.ts (1)
  • useMembers (7-12)
src/app/features/dashboard_Id/Card/cardFormModals/AssigneeList.tsx (2)
  • Assignee (9-12)
  • AssigneeList (24-50)
src/app/features/dashboard_Id/type/CardFormData.type.ts (1)
  • CardFormData (1-10)
src/app/features/dashboard_Id/api/usePostCard.ts (1)
  • usePostCard (7-24)
src/app/features/dashboard_Id/Card/cardFormModals/input/Input.tsx (1)
  • Input (9-28)
src/app/features/dashboard_Id/Card/Tags.tsx (1)
  • Tags (3-29)
🪛 Biome (1.9.4)
src/app/features/dashboard_Id/Card/cardFormModals/CreateCardForm.tsx

[error] 92-92: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 94-97: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🔇 Additional comments (39)
src/app/layout.tsx (1)

28-28: 모달 Portal 컨테이너 추가가 적절합니다.

React Portal을 위한 modal-root div가 올바른 위치에 추가되었습니다. Providers 외부에 배치하여 모달이 전역 스타일과 독립적으로 렌더링될 수 있도록 했습니다.

package.json (1)

16-16: lockfile이 없어 npm audit가 실패했습니다. 아래 스크립트로 package-lock.json을 생성한 뒤 보안 검사를 다시 실행해주세요.

#!/bin/bash
# package-lock.json 생성
npm install --package-lock-only

# 보안 취약점 검사
npm audit --audit-level=moderate
src/app/shared/lib/testAxios.ts (2)

10-10: 인터셉터와 export 업데이트가 일관성 있습니다.

변경된 인스턴스 이름에 맞춰 인터셉터 설정과 export문이 올바르게 업데이트되었습니다.

Also applies to: 18-18


3-5: ```shell
#!/bin/bash

.ts 및 .tsx 파일에서 api 인스턴스(import api from testAxios) 사용처 검색

rg -n "import\s.*api\s.from\s.testAxios" -g '.ts' -g '.tsx'

이전 axiosClient 사용처가 남아있는지 확인

rg -n "axiosClient" -g '.ts' -g '.tsx'


</details>
<details>
<summary>src/app/shared/lib/getColor.ts (1)</summary>

`1-5`: 아래 명령어로 `getColor` 호출처를 다시 검색해주세요. 이번에는 glob 옵션을 사용해 `.ts`·`.tsx` 파일을 모두 탐색합니다.


```shell
#!/bin/bash
# Description: ts/tsx 파일에서 getColor 함수 사용처를 찾아 올바른 인자가 전달되는지 확인합니다.

rg -n -C 2 "getColor\(" --glob "*.ts" --glob "*.tsx"
src/app/globals.css (3)

45-47: 텍스트 입력 스타일이 일관성 있게 추가되었습니다.

.Text-Input 클래스가 기존 텍스트 스타일 패턴과 일관성 있게 다크 모드 지원과 함께 추가되었습니다.


84-86: 입력 요소 호버 효과가 적절히 구현되었습니다.

.BG-Input-hovered 클래스가 라이트/다크 모드 모두에서 적절한 호버 효과를 제공합니다.


91-96: 입력 요소 스타일이 포괄적으로 정의되었습니다.

.Input.Input-readOnly 클래스가 폼 요소의 다양한 상태(포커스, 읽기 전용)를 잘 다루고 있으며, 다크 모드 지원도 완벽합니다.

src/app/features/dashboardId/Card/Tags.tsx (1)

12-12: getColor 함수 시그니처 변경에 맞는 올바른 수정입니다.

새로운 getColor 함수가 배열 길이를 매개변수로 받도록 변경된 것에 맞춰 적절하게 수정되었습니다.

src/app/shared/components/common/Avatar.tsx (1)

34-34: getColor 함수 사용법이 올바르게 업데이트되었습니다.

Tags 컴포넌트와 일관된 방식으로 getColor 함수의 새로운 시그니처에 맞게 수정되었습니다.

src/app/features/dashboard_Id/type/Card.type.ts (1)

21-25: 표준적인 페이지네이션 응답 인터페이스가 잘 정의되었습니다.

카드 목록 조회 API의 응답 구조를 명확하게 타입으로 정의하여 타입 안전성을 보장합니다.

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

4-4: 리팩토링에 맞는 올바른 임포트 경로 변경입니다.

기존 API 모듈에서 새로운 feature-specific 타입 정의로 임포트 경로가 적절하게 변경되었습니다.

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

7-19: 이미지 업로드 훅 구현이 적절합니다.

에러 핸들링과 사용자 피드백이 잘 구현되어 있습니다. 서버 에러 메시지를 우선적으로 사용하고, 대체 메시지로 폴백하는 로직이 적절합니다.

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

7-12: 카드 조회 훅이 올바르게 구현되었습니다.

쿼리 키 구조 ['columnId', columnId]가 적절하며, 향후 무한 스크롤 구현 계획에 대한 주석도 유용합니다.

src/app/features/dashboard_Id/api/fetchColumns.ts (2)

6-9: 입력 검증 로직이 적절합니다.

dashboardId 유효성 검사와 명확한 에러 메시지가 잘 구현되어 있습니다.


1-3: 테스트 axios 인스턴스 사용 확인 필요

현재 testAxios를 사용하고 있고 프로덕션용 axios가 주석 처리되어 있습니다. 의도적인 것인지 확인이 필요합니다.

다음 스크립트로 프로젝트 전체에서 어떤 axios 인스턴스가 사용되고 있는지 확인해보세요:

#!/bin/bash
# 프로젝트에서 axios 인스턴스 사용 현황 확인
echo "=== testAxios 사용 현황 ==="
rg "from.*testAxios" --type ts --type tsx -A 2

echo -e "\n=== 일반 axios 사용 현황 ==="  
rg "from.*lib/axios" --type ts --type tsx -A 2

echo -e "\n=== axios 설정 파일들 ==="
fd "axios" --type f
src/app/features/dashboard_Id/api/updateCardColumn.ts (1)

1-2: 프로덕션 환경에서 testAxios 사용 여부를 검증하세요.

현재 testAxios를 사용하고 있고 프로덕션용 axios는 주석 처리되어 있습니다. 프로덕션 배포 시 올바른 API 클라이언트가 사용되는지 확인이 필요합니다.

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

1-1: testAxios 사용에 대한 검증이 필요합니다.

다른 API 함수들과 동일하게 testAxios를 사용하고 있습니다. 프로덕션 환경에서의 사용 여부를 확인하세요.

src/app/features/dashboard_Id/Card/Tags.tsx (1)

3-29: 전반적으로 잘 구현된 컴포넌트입니다.

getColor 함수를 적절히 사용하여 일관된 색상 할당을 구현했고, 적절한 스타일링과 키 속성을 사용했습니다.

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

4-22: forwardRef 구현이 잘 되어 있습니다.

forwardRef를 적절히 사용하여 ref 전달을 지원하고, displayName을 설정하여 디버깅에 도움이 되도록 구현했습니다.

src/app/features/dashboard_Id/type/CardFormData.type.ts (1)

1-10: 인터페이스 정의가 깔끔하고 적절합니다.

카드 폼 데이터를 위한 타입 정의가 명확하고 필요한 필드들이 적절히 구성되어 있습니다. 필수 필드와 선택적 필드(dueDate, imageUrl)의 구분도 적절합니다.

src/app/features/dashboard_Id/lib/getDashboardMembers.ts (1)

10-18: 함수 구현이 깔끔하고 안전합니다.

undefined 처리와 타입 변환 로직이 적절하게 구현되어 있습니다. 한국어 주석도 코드의 의도를 명확히 설명하고 있습니다.

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

9-28: 컴포넌트 구현이 적절하고 접근성을 고려했습니다.

label과 input 요소의 연결(htmlFor)이 적절히 구현되어 있고, 필수 필드 표시(accent)도 좋은 UX 패턴입니다. 스타일링도 일관성 있게 적용되어 있습니다.

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

6-6: import 경로 업데이트가 적절합니다.

타입 정의를 feature-specific 폴더로 이동한 리팩터링에 맞춰 import 경로가 올바르게 업데이트되었습니다.


20-20: props 구조분해를 통한 코드 개선이 좋습니다.

card.id 대신 구조분해된 id를 사용하여 코드가 더 간결해졌습니다.


50-73: 조건부 렌더링 개선이 훌륭합니다.

마감일과 담당자 섹션을 각각 조건부로 렌더링하도록 개선하여 불필요한 빈 요소 렌더링을 방지했습니다. 코드 가독성도 향상되었습니다.

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

6-11: React Query 훅 구현이 표준 패턴을 잘 따르고 있습니다.

dashboardId를 쿼리 키에 포함하여 대시보드별 캐시 분리가 적절히 구현되었고, 타입 정의도 명확합니다. 간결하고 재사용 가능한 훅 구조입니다.

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

1-1: 프로덕션용 Axios 설정 확인

다음 스크립트를 실행해 src/app/shared/lib/axios.ts(또는 유사 파일)의 설정을 확인하고, testAxios와 비교해주세요.

#!/bin/bash
# shared/lib 디렉터리 파일 목록 확인
echo "=== src/app/shared/lib 파일 목록 ==="
ls src/app/shared/lib

echo ""
# 프로덕션용 axios 설정 확인
if [ -f src/app/shared/lib/axios.ts ]; then
  echo "=== src/app/shared/lib/axios.ts 내용 ==="
  cat src/app/shared/lib/axios.ts
elif [ -f src/app/shared/lib/axios.js ]; then
  echo "=== src/app/shared/lib/axios.js 내용 ==="
  cat src/app/shared/lib/axios.js
else
  echo "프로덕션 axios 파일(axios.ts/.js)을 찾을 수 없습니다."
fi
src/app/features/dashboard_Id/type/Column.type.ts (1)

1-11: 타입 정의가 올바르게 구성되었습니다

Column 인터페이스와 ColumnsResponse 인터페이스가 적절하게 정의되어 있습니다. 필드 타입들이 일관성 있게 설정되어 있고 API 응답 구조도 표준적입니다.

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

8-9: modal-root 요소가 없을 때의 처리가 적절합니다

document.getElementById('modal-root')가 null일 경우 적절히 처리하고 있습니다.

src/app/features/dashboardId/Card/cardFormModals/AssigneeList.tsx (1)

2-2: 사용 중인 AssigneeList 컴포넌트가 어느 경로의 파일인지 확인하기 위해 아래 스크립트를 실행해주세요:

#!/bin/bash
echo "=== AssigneeList 사용처 검색 ==="
rg "AssigneeList" -n .

echo -e "\n=== dashboardId 폴더 참조 검색 ==="
rg "src/app/features/dashboardId" -n .

echo -e "\n=== dashboard_Id 폴더 참조 검색 ==="
rg "src/app/features/dashboard_Id" -n .
src/app/features/dashboard_Id/type/Member.type.ts (1)

1-14: 타입 정의가 깔끔하고 잘 구조화되어 있습니다.

Member와 MembersResponse 인터페이스가 명확하게 정의되어 있고, 필드 타입들이 적절히 설정되어 있습니다. profileImageUrl의 nullable 처리도 올바르게 되어 있습니다.

src/app/dashboard/[id]/page.tsx (1)

14-16: 동적 대시보드 ID 구현이 올바르게 되어 있습니다.

useParams를 사용하여 URL에서 동적으로 대시보드 ID를 가져오고 있으며, 새로운 useColumns 훅에 올바르게 전달하고 있습니다.

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

26-48: 폼 상태 관리가 잘 구현되어 있습니다.

react-hook-form을 사용한 폼 상태 관리가 적절히 구현되어 있고, 이미지 업로드, 태그 관리, 담당자 선택 등의 기능이 잘 구조화되어 있습니다.


193-196: 태그 중복 처리 로직이 올바르게 구현되어 있습니다.

태그 중복을 체크하고 trim()을 사용하여 공백을 제거하는 로직이 적절히 구현되어 있습니다.

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

9-11: 모달 컴포넌트 통합이 잘 되어 있습니다.

CreateCardFormCreateCardModal을 적절히 import하고 있으며, 새로운 피처 구조에 맞게 경로가 업데이트되었습니다.


95-102: 카드 생성 모달 구현이 적절합니다.

조건부 렌더링으로 모달을 표시하고, 필요한 props들(onClose, columnId)을 올바르게 전달하고 있습니다.

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

9-17: 타입 정의가 명확하고 잘 구조화되어 있습니다.

Assignee 인터페이스와 AssigneeListProps가 명확하게 정의되어 있으며, react-hook-form과의 통합을 위한 ControllerRenderProps 타입 사용이 적절합니다.


40-43: 담당자 선택 로직이 올바르게 구현되어 있습니다.

UI 상태(setAssignee)와 폼 상태(controlField.onChange) 업데이트를 분리하여 처리하는 것이 적절합니다. 주석으로 설명된 로직도 명확합니다.

Comment on lines +12 to +14
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['columnId'] }) //'columnId' 쿼리 invalidate - 카드 리스트가 stale 상태임을 알리고 다시 fetch 하도록 유도함
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

쿼리 키 불일치로 인한 캐시 무효화 문제

useCards 훅에서는 ['columnId', columnId] 키를 사용하지만, 여기서는 ['columnId']만 무효화하고 있습니다. 이로 인해 카드 생성 후 목록이 자동으로 업데이트되지 않을 수 있습니다.

다음과 같이 수정하세요:

-      queryClient.invalidateQueries({ queryKey: ['columnId'] })
+      queryClient.invalidateQueries({ queryKey: ['columnId'] })

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

🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/api/usePostCard.ts around lines 12 to 14, the
query key used in queryClient.invalidateQueries is inconsistent with the key
used in the useCards hook. The hook uses ['columnId', columnId] but the
invalidateQueries call only uses ['columnId'], causing cache invalidation to
fail. Fix this by including the columnId variable in the invalidateQueries call
to match the full query key, ensuring the card list updates correctly after
creation.

Comment on lines +5 to +16
export async function updateCardColumn(
cardId: number,
columnId: number,
): Promise<{ success: boolean }> {
const res = await api.put<{ success: boolean }>(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/cards/${cardId}`,
{
columnId: columnId,
},
)
return res.data
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

환경변수 검증과 에러 처리를 추가하세요.

현재 구현에서 다음 사항들을 개선할 수 있습니다:

  • process.env.NEXT_PUBLIC_TEAM_ID가 undefined일 경우 처리
  • API 요청 실패 시 에러 처리
  • 카드 ID와 컬럼 ID 유효성 검증

다음과 같은 개선사항을 적용하세요:

export async function updateCardColumn(
  cardId: number,
  columnId: number,
): Promise<{ success: boolean }> {
+  if (!process.env.NEXT_PUBLIC_TEAM_ID) {
+    throw new Error('NEXT_PUBLIC_TEAM_ID is not defined')
+  }
+  
+  if (cardId <= 0 || columnId <= 0) {
+    throw new Error('Invalid cardId or columnId')
+  }
+
+  try {
    const res = await api.put<{ success: boolean }>(
      `/${process.env.NEXT_PUBLIC_TEAM_ID}/cards/${cardId}`,
      {
        columnId: columnId,
      },
    )
    return res.data
+  } catch (error) {
+    console.error('Failed to update card column:', error)
+    throw error
+  }
}
📝 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
export async function updateCardColumn(
cardId: number,
columnId: number,
): Promise<{ success: boolean }> {
const res = await api.put<{ success: boolean }>(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/cards/${cardId}`,
{
columnId: columnId,
},
)
return res.data
}
export async function updateCardColumn(
cardId: number,
columnId: number,
): Promise<{ success: boolean }> {
if (!process.env.NEXT_PUBLIC_TEAM_ID) {
throw new Error('NEXT_PUBLIC_TEAM_ID is not defined')
}
if (cardId <= 0 || columnId <= 0) {
throw new Error('Invalid cardId or columnId')
}
try {
const res = await api.put<{ success: boolean }>(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/cards/${cardId}`,
{
columnId: columnId,
},
)
return res.data
} catch (error) {
console.error('Failed to update card column:', error)
throw error
}
}
🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/api/updateCardColumn.ts lines 5 to 16, add
validation to check if process.env.NEXT_PUBLIC_TEAM_ID is defined before using
it, validate that cardId and columnId are valid numbers, and wrap the API call
in a try-catch block to handle and propagate errors properly. Return or throw
meaningful errors if validations fail or the API request encounters an error.

Comment on lines +10 to +16
export async function postCard(payload: CardFormData): Promise<ApiResponse> {
const res = await api.post(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/cards`,
payload,
)
return res.data
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

환경변수 검증과 에러 처리를 추가하세요.

updateCardColumn 함수와 동일한 문제점들이 있습니다. 환경변수 검증과 에러 처리를 추가하세요.

export async function postCard(payload: CardFormData): Promise<ApiResponse> {
+  if (!process.env.NEXT_PUBLIC_TEAM_ID) {
+    throw new Error('NEXT_PUBLIC_TEAM_ID is not defined')
+  }
+
+  try {
    const res = await api.post(
      `/${process.env.NEXT_PUBLIC_TEAM_ID}/cards`,
      payload,
    )
    return res.data
+  } catch (error) {
+    console.error('Failed to create card:', error)
+    throw error
+  }
}
📝 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
export async function postCard(payload: CardFormData): Promise<ApiResponse> {
const res = await api.post(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/cards`,
payload,
)
return res.data
}
export async function postCard(payload: CardFormData): Promise<ApiResponse> {
if (!process.env.NEXT_PUBLIC_TEAM_ID) {
throw new Error('NEXT_PUBLIC_TEAM_ID is not defined')
}
try {
const res = await api.post(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/cards`,
payload,
)
return res.data
} catch (error) {
console.error('Failed to create card:', error)
throw error
}
}
🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/api/postCard.ts between lines 10 and 16, the
function postCard lacks environment variable validation and error handling
similar to updateCardColumn. Add a check to ensure
process.env.NEXT_PUBLIC_TEAM_ID is defined before making the API call, and
implement try-catch to handle potential errors, returning or logging appropriate
error information.

Comment on lines +4 to +21
export async function postCardImages(
columnId: number,
file: File,
): Promise<{ imageUrl: string }> {
const formData = new FormData()
formData.append('image', file)
const res = await api.post(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/columns/${columnId}/card-image`,
formData,
{
headers: {
'Content-Type': 'multipart/form-data', // multipart/form-data 방식으로 파일 전송: 모든 문자를 인코딩하지 않음s
},
},
)

return res.data
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

파일 유효성 검증과 에러 처리를 추가하세요.

파일 업로드 함수에 다음 개선사항들을 적용하세요:

  • 파일 크기 제한 검증
  • 파일 타입 검증
  • 환경변수 검증
  • 에러 처리 개선
export async function postCardImages(
  columnId: number,
  file: File,
): Promise<{ imageUrl: string }> {
+  if (!process.env.NEXT_PUBLIC_TEAM_ID) {
+    throw new Error('NEXT_PUBLIC_TEAM_ID is not defined')
+  }
+
+  // 파일 크기 제한 (예: 10MB)
+  const maxSize = 10 * 1024 * 1024
+  if (file.size > maxSize) {
+    throw new Error('파일 크기가 10MB를 초과합니다')
+  }
+
+  // 이미지 파일 타입 검증
+  const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
+  if (!allowedTypes.includes(file.type)) {
+    throw new Error('지원하지 않는 이미지 파일 형식입니다')
+  }
+
+  try {
    const formData = new FormData()
    formData.append('image', file)
    const res = await api.post(
      `/${process.env.NEXT_PUBLIC_TEAM_ID}/columns/${columnId}/card-image`,
      formData,
      {
        headers: {
-          'Content-Type': 'multipart/form-data', // multipart/form-data 방식으로 파일 전송: 모든 문자를 인코딩하지 않음s
+          'Content-Type': 'multipart/form-data', // multipart/form-data 방식으로 파일 전송: 모든 문자를 인코딩하지 않음
        },
      },
    )

    return res.data
+  } catch (error) {
+    console.error('Failed to upload card image:', error)
+    throw error
+  }
}
📝 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
export async function postCardImages(
columnId: number,
file: File,
): Promise<{ imageUrl: string }> {
const formData = new FormData()
formData.append('image', file)
const res = await api.post(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/columns/${columnId}/card-image`,
formData,
{
headers: {
'Content-Type': 'multipart/form-data', // multipart/form-data 방식으로 파일 전송: 모든 문자를 인코딩하지 않음s
},
},
)
return res.data
}
export async function postCardImages(
columnId: number,
file: File,
): Promise<{ imageUrl: string }> {
if (!process.env.NEXT_PUBLIC_TEAM_ID) {
throw new Error('NEXT_PUBLIC_TEAM_ID is not defined')
}
// 파일 크기 제한 (예: 10MB)
const maxSize = 10 * 1024 * 1024
if (file.size > maxSize) {
throw new Error('파일 크기가 10MB를 초과합니다')
}
// 이미지 파일 타입 검증
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
if (!allowedTypes.includes(file.type)) {
throw new Error('지원하지 않는 이미지 파일 형식입니다')
}
try {
const formData = new FormData()
formData.append('image', file)
const res = await api.post(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/columns/${columnId}/card-image`,
formData,
{
headers: {
'Content-Type': 'multipart/form-data', // multipart/form-data 방식으로 파일 전송: 모든 문자를 인코딩하지 않음
},
},
)
return res.data
} catch (error) {
console.error('Failed to upload card image:', error)
throw error
}
}
🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/api/postCardImage.ts between lines 4 and 21,
add validation to check the file size and type before uploading, verify that the
environment variable NEXT_PUBLIC_TEAM_ID is defined and valid, and wrap the API
call in a try-catch block to handle errors gracefully by returning or logging
meaningful error messages.

inputMode="none"
readOnly
className="Input-readOnly"
id="dueDate"
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

하드코딩된 id 속성을 개선하세요.

고정된 id="dueDate"는 동일한 컴포넌트의 여러 인스턴스가 존재할 때 문제가 될 수 있습니다.

props로 id를 전달받거나 고유한 id를 생성하도록 개선하세요:

const DateInput = forwardRef<
  HTMLInputElement,
-  React.InputHTMLAttributes<HTMLInputElement>
+  React.InputHTMLAttributes<HTMLInputElement> & { id?: string }
>((props, ref) => {
+  const { id = 'dueDate', ...restProps } = props
  return (
    <input
      ref={ref}
-      {...props}
+      {...restProps}
      inputMode="none"
      readOnly
      className="Input-readOnly"
-      id="dueDate"
+      id={id}
      placeholder="날짜와 시간을 선택하세요"
    />
  )
})
📝 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
id="dueDate"
const DateInput = forwardRef<
HTMLInputElement,
- React.InputHTMLAttributes<HTMLInputElement>
+ React.InputHTMLAttributes<HTMLInputElement> & { id?: string }
>((props, ref) => {
+ const { id = 'dueDate', ...restProps } = props
return (
<input
ref={ref}
- {...props}
+ {...restProps}
inputMode="none"
readOnly
className="Input-readOnly"
- id="dueDate"
+ id={id}
placeholder="날짜와 시간을 선택하세요"
/>
)
})
🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/Card/cardFormModals/input/DateInput.tsx at line
15, the id attribute is hardcoded as "dueDate", which can cause conflicts if
multiple instances of this component are rendered. Modify the component to
accept an id as a prop or generate a unique id internally, then replace the
hardcoded id with this dynamic value to ensure uniqueness across instances.

Comment on lines +3 to +6
export interface Assignee {
userId: number
nickname: string
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

타입 정의 중복을 피해주세요.

Assignee 인터페이스가 AssigneeList.tsx에서도 동일하게 정의되어 있습니다. 타입 정의를 별도 파일로 분리하거나 한 곳에서만 정의하고 다른 곳에서 import하도록 리팩터링하는 것이 좋겠습니다.

-export interface Assignee {
-  userId: number
-  nickname: string
-}

별도의 타입 파일 (예: src/app/features/dashboard_Id/type/Assignee.type.ts)을 생성하고 해당 타입을 export하여 사용하세요.

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

🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/lib/getDashboardMembers.ts around lines 3 to 6,
the Assignee interface is duplicated in AssigneeList.tsx. To fix this, create a
separate type definition file such as
src/app/features/dashboard_Id/type/Assignee.type.ts, move the Assignee interface
there, export it, and then import and use this shared type in both
getDashboardMembers.ts and AssigneeList.tsx to avoid duplication.


interface ModalProps {
children: React.ReactNode
onClose: () => void
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

onClose 콜백 props가 사용되지 않음

onClose props가 정의되어 있지만 모달 내에서 실제로 사용되지 않고 있습니다. 사용자가 모달 외부를 클릭하거나 ESC 키를 눌렀을 때 모달을 닫을 수 있도록 기능을 추가하는 것을 고려해보세요.

다음과 같이 onClose 기능을 추가할 수 있습니다:

 return createPortal(
-  <div className="fixed inset-0 flex items-center justify-center bg-black/40">
+  <div 
+    className="fixed inset-0 flex items-center justify-center bg-black/40"
+    onClick={onClose}
+  >
     <div className="BG-white h-970 w-584 overflow-y-scroll rounded-16 p-32 shadow-lg [mask-image:radial-gradient(white_100%,transparent_100%)]">
       {children}
     </div>
   </div>,

추가로 ESC 키 처리를 위한 useEffect 훅도 고려해볼 수 있습니다.

Also applies to: 7-7

🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/Card/cardFormModals/CreateCardModal.tsx around
lines 5 to 7, the onClose callback prop is defined but not used. To fix this,
integrate the onClose function to close the modal when the user clicks outside
the modal area or presses the ESC key. Implement an event listener using a
useEffect hook to detect the ESC key press and call onClose accordingly, and
ensure clicking outside the modal triggers onClose as well.

Comment on lines +89 to +91
if (!data.dueDate) delete payload.dueDate
if (!data.imageUrl || !preview) delete payload.imageUrl // delete로 아예 필드의 해당 key를 지워야, 서버가 "없음"으로 인식함..
console.log('🌀', data.imageUrl)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

성능 이슈: delete 연산자 사용을 피하고 디버그 코드를 제거해주세요.

정적 분석 도구에서 지적한 대로 delete 연산자는 성능에 영향을 줄 수 있습니다. 또한 프로덕션 코드에서 console.log는 제거해야 합니다.

-    if (!data.dueDate) delete payload.dueDate
-    if (!data.imageUrl || !preview) delete payload.imageUrl // delete로 아예 필드의 해당 key를 지워야, 서버가 "없음"으로 인식함..
-    console.log('🌀', data.imageUrl)
+    if (!data.dueDate) payload.dueDate = undefined
+    if (!data.imageUrl || !preview) payload.imageUrl = undefined
📝 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 (!data.dueDate) delete payload.dueDate
if (!data.imageUrl || !preview) delete payload.imageUrl // delete로 아예 필드의 해당 key를 지워야, 서버가 "없음"으로 인식함..
console.log('🌀', data.imageUrl)
if (!data.dueDate) payload.dueDate = undefined
if (!data.imageUrl || !preview) payload.imageUrl = undefined
🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/Card/cardFormModals/CreateCardForm.tsx around
lines 89 to 91, avoid using the delete operator on payload keys due to
performance concerns and remove the console.log debug statement. Instead of
deleting keys, set payload.dueDate and payload.imageUrl to undefined or null
when their corresponding data values are falsy, and remove the console.log line
entirely to clean up production code.

@dkslel1225 dkslel1225 merged commit a644776 into develop Jun 19, 2025
2 checks 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.

2 participants