Skip to content

Conversation

@LeeCh0129
Copy link

@LeeCh0129 LeeCh0129 commented Jun 21, 2025

📌 변경 사항 개요

대시보드 상세 페이지의 대시보드 컬럼의 생성, 수정, 삭제할 수 있는 관리 기능 구현

✨ 요약

  • 컬럼 생성 모달 (중복 체크, 최대 개수 제한)
  • 컬럼 숮어 모달 (이름 변경, 삭제 기능)
  • 컬럼 삭제 확인 모달 (카드 삭제 경고)
  • 유효성 검사 및 에러 피드백

📝 상세 내용

컬럼 관리

  • 모달 기반 UI: 생성/수정,삭제/삭제 확인 3개 모달로 구성
  • 독립적 상태 관리: useColumnModalStore (Zustand)로 모달 데이터 전달
  • 중앙 렌더러: ColumnModalRenderer로 모달 통합 관리

API 및 데이터 관리

  • TanStack Query 패턴: useMutation + 캐시 무효화로 실시간 동기화
  • 토스트 알림: 성공/실패 상태 즉시 피드백

사용자 경험

  • 실시간 유효성 검사: 중복 컬럼명, 최대 10개 제한, 빈 값 체크
  • 시각적 피드백: 에러 시 입력창 테두리 빨간색 변경
  • 중복 클릭 방지: isSubmitting 상태로 버튼 비활성화
  • 동적 UI: 에러 메시지 표시 시 모달 높이 자동 조정

🔗 관련 이슈

#97

🖼️ 스크린샷

image image image image image image image

✅ 체크리스트

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

💡 참고 사항

API 엔드포인트:

  • POST /{teamId}/columns (컬럼 생성)
  • PUT /{teamId}/columns/{columnId} (컬럼 수정)
  • DELETE /{teamId}/columns/{columnId} (컬럼 삭제)

추후 개선사항:

  • 중복 코드 분리
  • 반응형

Summary by CodeRabbit

  • 신규 기능
    • 대시보드에서 컬럼 생성, 수정, 삭제를 위한 모달 UI가 추가되었습니다.
    • 컬럼 생성 시 중복 이름 및 최대 10개 제한에 대한 검증 및 안내 메시지가 제공됩니다.
    • 컬럼 수정 및 삭제 시 각각 전용 모달을 통해 편리하게 작업할 수 있습니다.
    • 컬럼 관련 API(생성, 수정, 삭제) 함수가 새로 도입되어 기능이 확장되었습니다.
    • 컬럼 모달 상태 관리를 위한 전역 상태 관리 기능이 추가되었습니다.
  • 버그 수정
    • 멤버, 카드, 컬럼 관련 API 요청이 인증된 클라이언트로 일원화되어 보안성이 향상되었습니다.
  • 스타일
    • 컬럼 설정 아이콘에 커서 스타일이 적용되어 클릭 가능함을 명확히 표시합니다.

@LeeCh0129 LeeCh0129 self-assigned this Jun 21, 2025
@LeeCh0129 LeeCh0129 added the ✨Feat 기능 개발 label Jun 21, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jun 21, 2025

"""

Walkthrough

이 변경 사항은 대시보드의 컬럼(열) 관리 기능을 모달 기반 UI로 구현합니다. 컬럼 생성, 수정, 삭제를 위한 모달 컴포넌트, API 함수, 커스텀 훅, 상태 관리(store) 등이 추가되었으며, 기존 컬럼 관련 컴포넌트와 API 호출부는 인증된 HTTP 클라이언트로 일원화되었습니다.

Changes

파일/경로 요약 변경 요약
src/app/dashboard/[id]/page.tsx 컬럼 생성 모달 UI 플로우 도입, ColumnModalRenderer 및 useColumnModalStore 적용, 컬럼 생성 핸들러 추가
src/app/features/dashboard_Id/Column/Column.tsx Column 컴포넌트에 dashboardId prop 추가, 설정 아이콘 클릭 시 모달 오픈 로직으로 변경
src/app/features/dashboard_Id/api/fetchMembers.ts
src/app/features/dashboard_Id/api/postCard.ts
src/app/features/dashboard_Id/api/postCardImage.ts
src/app/features/dashboard_Id/api/putCard.ts
src/app/features/dashboard_Id/api/updateCardColumn.ts
인증된 axios 클라이언트로 API 호출 일원화
src/app/features/dashboard_Id/api/deleteColumn.ts 컬럼 삭제 API 함수 추가
src/app/features/dashboard_Id/api/postColumn.ts 컬럼 생성 API 함수 추가
src/app/features/dashboard_Id/api/updateColumn.ts 컬럼 수정 API 함수 추가
src/app/features/dashboard_Id/components/ColumnModalRenderer.tsx 컬럼 관련 모달 렌더러 컴포넌트 추가
src/app/features/dashboard_Id/components/CreateColumnModal.tsx 컬럼 생성 모달 컴포넌트 추가
src/app/features/dashboard_Id/components/EditColumnModal.tsx 컬럼 수정 모달 컴포넌트 추가
src/app/features/dashboard_Id/components/DeleteColumnConfirmModal.tsx 컬럼 삭제 확인 모달 컴포넌트 추가
src/app/features/dashboard_Id/hooks/useCreateColumn.ts 컬럼 생성 커스텀 훅 추가
src/app/features/dashboard_Id/hooks/useDeleteColumn.ts 컬럼 삭제 커스텀 훅 추가
src/app/features/dashboard_Id/hooks/useUpdateColumn.ts 컬럼 수정 커스텀 훅 추가
src/app/features/dashboard_Id/store/useColumnModalStore.ts 컬럼 모달 상태 관리용 Zustand store 추가
src/app/features/dashboard_Id/type/Column.type.ts 컬럼 API/훅/모달 관련 타입 및 인터페이스 다수 추가/확장

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DashboardPage
    participant ColumnModalRenderer
    participant useColumnModalStore
    participant API

    User->>DashboardPage: "새 컬럼 추가" 버튼 클릭
    DashboardPage->>useColumnModalStore: openModal('create', {dashboardId})
    DashboardPage->>ColumnModalRenderer: 렌더링 트리거
    ColumnModalRenderer->>CreateColumnModal: 모달 표시
    User->>CreateColumnModal: 컬럼명 입력 후 제출
    CreateColumnModal->>API: postColumn API 호출
    API-->>CreateColumnModal: 생성 결과 반환
    CreateColumnModal->>useColumnModalStore: closeModal()
    CreateColumnModal-->>DashboardPage: 컬럼 목록 갱신
Loading
sequenceDiagram
    participant User
    participant Column
    participant useColumnModalStore
    participant ColumnModalRenderer
    participant EditColumnModal
    participant API

    User->>Column: 설정 아이콘 클릭
    Column->>useColumnModalStore: openModal('edit', {dashboardId, columnId, columnTitle})
    Column->>ColumnModalRenderer: 렌더링 트리거
    ColumnModalRenderer->>EditColumnModal: 모달 표시
    User->>EditColumnModal: 컬럼명 수정 후 제출
    EditColumnModal->>API: updateColumn API 호출
    API-->>EditColumnModal: 수정 결과 반환
    EditColumnModal->>useColumnModalStore: closeModal()
    EditColumnModal-->>Column: 컬럼 목록 갱신
Loading
sequenceDiagram
    participant User
    participant EditColumnModal
    participant useColumnModalStore
    participant ColumnModalRenderer
    participant DeleteColumnConfirmModal
    participant API

    User->>EditColumnModal: "삭제" 버튼 클릭
    EditColumnModal->>useColumnModalStore: openModal('deleteConfirm', {dashboardId, columnId})
    ColumnModalRenderer->>DeleteColumnConfirmModal: 모달 표시
    User->>DeleteColumnConfirmModal: "삭제" 버튼 클릭
    DeleteColumnConfirmModal->>API: deleteColumn API 호출
    API-->>DeleteColumnConfirmModal: 삭제 결과 반환
    DeleteColumnConfirmModal->>useColumnModalStore: closeModal()
    DeleteColumnConfirmModal-->>EditColumnModal: 컬럼 목록 갱신
Loading

Possibly related issues

Suggested reviewers

  • dkslel1225
  • Insung-Jo

Poem

🐇
새 컬럼이 쏙, 모달 속에서 뿅!
수정도 삭제도 한 번에 뚝딱,
대시보드가 반짝, 토끼도 깡총!
상태와 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-21T15_52_15_765Z-debug-0.log


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8fff1a2 and c585882.

📒 Files selected for processing (3)
  • src/app/features/dashboard_Id/components/CreateColumnModal.tsx (1 hunks)
  • src/app/features/dashboard_Id/components/DeleteColumnConfirmModal.tsx (1 hunks)
  • src/app/features/dashboard_Id/components/EditColumnModal.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/app/features/dashboard_Id/components/CreateColumnModal.tsx
  • src/app/features/dashboard_Id/components/EditColumnModal.tsx
  • src/app/features/dashboard_Id/components/DeleteColumnConfirmModal.tsx
✨ 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: 3

♻️ Duplicate comments (1)
src/app/features/dashboard_Id/components/EditColumnModal.tsx (1)

16-17: API 호출 최적화 고려사항

CreateColumnModal과 동일한 이슈가 있습니다. modalData가 없을 때 불필요한 API 호출이 발생할 수 있습니다.

🧹 Nitpick comments (9)
src/app/features/dashboard_Id/api/postColumn.ts (1)

5-13: API 함수의 에러 처리 방식 확인

현재 API 함수에서는 에러 처리를 하지 않고 상위 레이어(hook)에서 처리하고 있습니다. 이는 일관된 패턴이라면 괜찮지만, 네트워크 오류나 HTTP 응답 검증 등 공통적인 에러 처리가 필요할 수 있습니다.

src/app/features/dashboard_Id/components/ColumnModalRenderer.tsx (1)

8-16: 깔끔한 모달 관리 패턴

조건부 렌더링을 활용한 중앙집중식 모달 관리 방식이 깔끔합니다. early return 패턴으로 가독성도 좋고, 모달 상태 관리가 Zustand 스토어로 잘 분리되어 있습니다.

성능 최적화를 위해 동적 import를 고려해볼 수 있습니다:

+import { lazy, Suspense } from 'react'
+
+const CreateColumnModal = lazy(() => import('./CreateColumnModal'))
+const EditColumnModal = lazy(() => import('./EditColumnModal')) 
+const DeleteColumnConfirmModal = lazy(() => import('./DeleteColumnConfirmModal'))

export default function ColumnModalRenderer() {
  const { modalType } = useColumnModalStore()

-  if (modalType === 'create') return <CreateColumnModal />
-  if (modalType === 'edit') return <EditColumnModal />
-  if (modalType === 'deleteConfirm') return <DeleteColumnConfirmModal />
+  if (modalType === 'create') return <Suspense fallback={<div>Loading...</div>}><CreateColumnModal /></Suspense>
+  if (modalType === 'edit') return <Suspense fallback={<div>Loading...</div>}><EditColumnModal /></Suspense>
+  if (modalType === 'deleteConfirm') return <Suspense fallback={<div>Loading...</div>}><DeleteColumnConfirmModal /></Suspense>

  return null
}
src/app/features/dashboard_Id/hooks/useUpdateColumn.ts (1)

30-33: 다국어 대응을 고려해보세요

중복 검사에서 '중복'과 'duplicate' 두 가지 키워드를 확인하고 있는데, 서버 응답의 일관성을 보장하거나 더 견고한 에러 코드 기반 검사를 고려해보세요.

- if (
-   serverMessage?.includes('중복') ||
-   serverMessage?.includes('duplicate')
- ) {
+ // 서버에서 특정 에러 코드를 반환하는 경우
+ if (error.response?.status === 409) {
   toast.error('중복된 컬럼 이름입니다')
src/app/features/dashboard_Id/store/useColumnModalStore.ts (1)

5-9: 인터페이스 중복을 확인해보세요

ColumnModalData 인터페이스가 Column.type.ts에도 동일하게 정의되어 있는 것으로 보입니다. 타입 정의의 일관성을 위해 하나의 파일에서 export하여 재사용하는 것을 고려해보세요.

다음 스크립트로 중복된 인터페이스 정의를 확인하겠습니다:

#!/bin/bash
# Description: Check for duplicate ColumnModalData interface definitions

# Search for ColumnModalData interface definitions
rg -A 5 "interface ColumnModalData"
src/app/features/dashboard_Id/Column/Column.tsx (1)

30-31: 사용하지 않는 상태 변수를 정리해보세요

openCreateColumn과 oepnConfigColumn 상태 변수들이 사용되지 않는 것 같습니다. 코드 정리를 위해 제거를 고려해보세요.

- const [openCreateColumn, setOpenCreateColumn] = useState(false) //page.tsx
- const [oepnConfigColumn, setConfigColumn] = useState(false)

또한 oepnConfigColumn에 오타가 있는 것 같습니다 (open이 oepn으로 표기됨).

src/app/features/dashboard_Id/components/CreateColumnModal.tsx (2)

16-17: API 호출 최적화 고려사항

useColumns 훅이 modalData?.dashboardId || 0로 호출되고 있는데, modalData가 없을 때 dashboardId가 0으로 설정되어 불필요한 API 호출이 발생할 수 있습니다.

조건부 쿼리 사용을 고려해보세요:

- const { data: columns } = useColumns(modalData?.dashboardId || 0)
+ const { data: columns } = useColumns(modalData?.dashboardId || 0, {
+   enabled: !!modalData?.dashboardId
+ })

41-41: 동적 높이 클래스명 개선

하드코딩된 높이 클래스명 h-286, h-266은 유지보수성이 떨어집니다.

CSS 변수나 상수로 관리하는 것을 고려해보세요:

- const modalHeight = hasError ? 'h-286' : 'h-266'
+ const MODAL_HEIGHT = {
+   default: 'h-266',
+   withError: 'h-286'
+ } as const
+ const modalHeight = hasError ? MODAL_HEIGHT.withError : MODAL_HEIGHT.default
src/app/features/dashboard_Id/components/DeleteColumnConfirmModal.tsx (1)

63-70: 버튼 접근성 개선 필요

취소 버튼에 disabled 속성은 있지만 시각적 피드백이 없습니다.

취소 버튼도 제출 중일 때 시각적 피드백을 제공하세요:

<button
  type="button"
  onClick={closeModal}
  disabled={isSubmitting}
- className="BG-white Border-btn Text-gray h-54 w-256 rounded-8 px-16 py-10 text-16 font-medium"
+ className={`BG-white Border-btn Text-gray h-54 w-256 rounded-8 px-16 py-10 text-16 font-medium ${
+   isSubmitting ? 'cursor-not-allowed opacity-50' : ''
+ }`}
>
src/app/features/dashboard_Id/components/EditColumnModal.tsx (1)

98-98: 모달 컨테이너 스타일링 일관성

다른 모달들과 달리 rounded-16을 사용하고 있습니다. 일관성을 위해 확인이 필요합니다.

다른 모달들과 동일한 rounded-8을 사용하는 것이 일관성에 도움이 될 수 있습니다:

- <div className={`BG-white ${modalHeight} w-568 rounded-16 p-24`}>
+ <div className={`BG-white ${modalHeight} w-568 rounded-8 p-24`}>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 076d3c3 and 6208f4e.

📒 Files selected for processing (19)
  • src/app/dashboard/[id]/page.tsx (5 hunks)
  • src/app/features/dashboard_Id/Column/Column.tsx (4 hunks)
  • src/app/features/dashboard_Id/api/deleteColumn.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 (2 hunks)
  • src/app/features/dashboard_Id/api/postColumn.ts (1 hunks)
  • src/app/features/dashboard_Id/api/putCard.ts (2 hunks)
  • src/app/features/dashboard_Id/api/updateCardColumn.ts (1 hunks)
  • src/app/features/dashboard_Id/api/updateColumn.ts (1 hunks)
  • src/app/features/dashboard_Id/components/ColumnModalRenderer.tsx (1 hunks)
  • src/app/features/dashboard_Id/components/CreateColumnModal.tsx (1 hunks)
  • src/app/features/dashboard_Id/components/DeleteColumnConfirmModal.tsx (1 hunks)
  • src/app/features/dashboard_Id/components/EditColumnModal.tsx (1 hunks)
  • src/app/features/dashboard_Id/hooks/useCreateColumn.ts (1 hunks)
  • src/app/features/dashboard_Id/hooks/useDeleteColumn.ts (1 hunks)
  • src/app/features/dashboard_Id/hooks/useUpdateColumn.ts (1 hunks)
  • src/app/features/dashboard_Id/store/useColumnModalStore.ts (1 hunks)
  • src/app/features/dashboard_Id/type/Column.type.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (11)
src/app/features/dashboard_Id/api/postCard.ts (1)
src/app/features/dashboard_Id/type/CardFormData.type.ts (1)
  • CardFormData (11-13)
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/api/postColumn.ts (1)
src/app/features/dashboard_Id/type/Column.type.ts (2)
  • CreateColumnRequest (15-18)
  • CreateColumnResponse (21-27)
src/app/features/dashboard_Id/api/updateColumn.ts (1)
src/app/features/dashboard_Id/type/Column.type.ts (2)
  • UpdateColumnRequest (30-32)
  • UpdateColumnResponse (35-41)
src/app/features/dashboard_Id/hooks/useDeleteColumn.ts (2)
src/app/features/dashboard_Id/type/Column.type.ts (1)
  • DeleteColumnVariables (50-53)
src/app/features/dashboard_Id/api/deleteColumn.ts (1)
  • deleteColumn (3-7)
src/app/features/dashboard_Id/hooks/useCreateColumn.ts (2)
src/app/features/dashboard_Id/type/Column.type.ts (2)
  • CreateColumnResponse (21-27)
  • CreateColumnRequest (15-18)
src/app/features/dashboard_Id/api/postColumn.ts (1)
  • postColumn (5-13)
src/app/features/dashboard_Id/hooks/useUpdateColumn.ts (2)
src/app/features/dashboard_Id/type/Column.type.ts (2)
  • UpdateColumnResponse (35-41)
  • UpdateColumnVariables (44-48)
src/app/features/dashboard_Id/api/updateColumn.ts (1)
  • updateColumn (5-14)
src/app/features/dashboard_Id/Column/Column.tsx (3)
src/app/features/dashboard_Id/type/Column.type.ts (1)
  • Column (1-8)
src/app/features/dashboard_Id/api/useCards.ts (1)
  • useCards (7-12)
src/app/features/dashboard_Id/store/useColumnModalStore.ts (1)
  • useColumnModalStore (18-23)
src/app/features/dashboard_Id/components/EditColumnModal.tsx (3)
src/app/features/dashboard_Id/store/useColumnModalStore.ts (1)
  • useColumnModalStore (18-23)
src/app/features/dashboard_Id/api/useColumns.ts (1)
  • useColumns (6-11)
src/app/features/dashboard_Id/hooks/useUpdateColumn.ts (1)
  • useUpdateColumn (11-40)
src/app/features/dashboard_Id/type/Column.type.ts (1)
src/app/features/dashboard_Id/Column/Column.tsx (1)
  • Column (15-124)
src/app/features/dashboard_Id/store/useColumnModalStore.ts (1)
src/app/features/dashboard_Id/type/Column.type.ts (1)
  • ColumnModalData (56-60)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: eslint-check
🔇 Additional comments (39)
src/app/features/dashboard_Id/api/postCard.ts (1)

1-1: 인증된 HTTP 클라이언트 사용으로 개선되었습니다.

테스트용 클라이언트에서 인증된 HTTP 클라이언트로 변경한 것은 프로덕션 환경에서의 보안과 적절한 접근 제어를 위한 올바른 접근입니다.

Also applies to: 10-10

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

1-1: 일관된 인증 클라이언트 사용으로 개선되었습니다.

카드 수정 API도 인증된 HTTP 클라이언트를 사용하도록 변경되어 다른 API 함수들과 일관성을 유지하고 있습니다.

Also applies to: 13-13

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

1-1: 멤버 데이터 조회에 적절한 인증 처리가 적용되었습니다.

멤버 정보는 민감한 데이터이므로 인증된 HTTP 클라이언트를 사용하는 것이 적절하며, TypeScript 제네릭도 올바르게 적용되어 있습니다.

Also applies to: 6-6

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

1-1: 파일 업로드에 인증 처리가 적절히 적용되었습니다.

카드 이미지 업로드는 서버 리소스를 생성하는 작업이므로 인증된 HTTP 클라이언트 사용이 필수적입니다. 기존의 multipart/form-data 헤더 설정도 올바르게 유지되고 있습니다.

Also applies to: 10-18

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

1-1: 카드 이동 기능에 인증 처리가 적절히 구현되었습니다.

카드의 컬럼 간 이동은 중요한 데이터 조작 작업이므로 인증된 HTTP 클라이언트 사용이 필수적입니다. TypeScript 타입 지정도 정확하게 유지되고 있습니다.

Also applies to: 8-13

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

8-12: 환경 변수를 통한 URL 구성 시 보안 고려사항 검토 필요

process.env.NEXT_PUBLIC_TEAM_ID를 직접 URL에 사용하고 있습니다. NEXT_PUBLIC_ 접두사가 붙은 환경 변수는 클라이언트 측에서 노출되므로 민감한 정보가 포함되지 않았는지 확인이 필요합니다.

다음 스크립트로 환경 변수 사용 패턴을 검증해보세요:

#!/bin/bash
# 다른 API 함수들에서 동일한 패턴을 사용하는지 확인
rg -A 3 "process\.env\.NEXT_PUBLIC_TEAM_ID" --type ts
src/app/features/dashboard_Id/api/deleteColumn.ts (1)

3-7: 일관된 API 패턴으로 구현됨

다른 API 함수들과 동일한 패턴으로 구현되어 일관성이 좋습니다. HTTP 클라이언트와 URL 구성 방식이 통일되어 있어 유지보수에 도움이 될 것 같습니다.

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

5-14: 타입 안전성과 일관성 확보

UpdateColumnRequestUpdateColumnResponse 인터페이스를 활용하여 타입 안전성을 확보했고, 다른 API 함수들과 일관된 패턴으로 구현되어 있습니다. REST API 컨벤션에 맞게 PUT 메서드를 사용한 점도 좋습니다.

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

11-22: React Query 패턴의 올바른 활용

useMutation의 제네릭 타입 정의가 정확하고, 성공 시 캐시 무효화를 통해 UI가 즉시 업데이트되도록 구현한 점이 훌륭합니다. 변수의 dashboardId를 사용한 쿼리 키 무효화도 적절합니다.

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

10-11: 모달 관리 시스템이 잘 구성되었습니다

ColumnModalRenderer와 useColumnModalStore의 import가 적절하며, 중앙화된 모달 상태 관리 패턴을 따르고 있습니다.


30-32: 컬럼 생성 핸들러 구현이 깔끔합니다

handleCreateColumn 함수가 적절한 컨텍스트(dashboardId)와 함께 create 모달을 여는 로직이 명확합니다.


155-157: dashboardId prop 전달이 적절합니다

Column 컴포넌트에 dashboardId를 전달하여 모달 작업을 가능하게 하는 변경사항이 잘 구현되었습니다.


159-162: 사용자 인터랙션 개선이 좋습니다

"새로운 컬럼 추가하기" 버튼에 onClick 핸들러를 추가하여 모달 기반 워크플로우를 시작하는 구현이 직관적입니다.


176-176: 모달 렌더러 배치가 적절합니다

ColumnModalRenderer를 JSX 트리 끝에 배치하여 모달이 다른 UI 요소들 위에 렌더링되도록 한 것이 좋습니다.

src/app/features/dashboard_Id/hooks/useDeleteColumn.ts (4)

11-15: 타입 정의가 정확합니다

useMutation의 제네릭 타입 정의가 적절하며, AxiosError 타입에 서버 메시지 구조를 포함한 것이 좋습니다.


16-16: mutation 함수 구현이 간결합니다

destructuring을 사용하여 columnId만 추출해 deleteColumn API를 호출하는 방식이 깔끔합니다.


17-26: 캐시 무효화 전략이 포괄적입니다

컬럼 목록과 해당 컬럼의 카드 데이터 모두 무효화하여 UI 일관성을 보장하는 전략이 훌륭합니다.


30-33: 에러 처리가 사용자 친화적입니다

서버 메시지를 우선적으로 사용하고, 없을 경우 기본 메시지를 제공하는 에러 처리 로직이 적절합니다.

src/app/features/dashboard_Id/hooks/useUpdateColumn.ts (3)

14-18: 타입 정의가 명확합니다

UpdateColumnResponse와 UpdateColumnVariables를 사용한 제네릭 타입 정의가 정확하며, 타입 안전성을 보장합니다.


19-19: mutation 함수 구조가 일관성 있습니다

columnId와 title을 destructuring하여 updateColumn API에 전달하는 방식이 명확합니다.


30-37: 중복 검사 로직이 사용자 경험을 향상시킵니다

서버 메시지에서 중복 키워드를 검사하여 특별한 에러 메시지를 제공하는 로직이 사용자 친화적입니다.

src/app/features/dashboard_Id/store/useColumnModalStore.ts (2)

3-3: 모달 타입 정의가 명확합니다

ColumnModalType의 유니온 타입 정의가 명확하며, null을 포함하여 모달이 닫힌 상태를 표현하는 것이 적절합니다.


18-23: Zustand 구현이 간결하고 효율적입니다

openModal과 closeModal 액션이 명확하며, 상태 업데이트 로직이 직관적입니다. 모달 상태 관리에 적합한 구조입니다.

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

15-21: 컴포넌트 인터페이스 확장이 적절합니다

dashboardId prop을 추가하여 모달 작업을 지원하는 인터페이스 변경이 깔끔하게 구현되었습니다.


33-39: 모달 열기 로직이 명확합니다

handleConfigColumn 함수가 필요한 모든 컨텍스트(dashboardId, columnId, columnTitle)를 전달하며 edit 모달을 여는 구현이 정확합니다.


91-92: 사용자 경험 개선이 좋습니다

설정 아이콘에 cursor-pointer 클래스를 추가하여 클릭 가능함을 시각적으로 나타내는 것이 좋은 UX 개선입니다.

src/app/features/dashboard_Id/components/CreateColumnModal.tsx (2)

105-107: 입력 필드 스타일링 일관성

에러 상태에 따른 테두리 색상 변경 로직이 올바르게 구현되어 있습니다.


114-123: 에러 메시지 표시 로직

중복 컬럼명과 최대 개수 제한에 대한 에러 메시지가 적절히 구현되어 있습니다.

src/app/features/dashboard_Id/components/DeleteColumnConfirmModal.tsx (2)

19-20: null 체크 로직

modalData?.columnId 체크가 올바르게 구현되어 있어 안전합니다.


73-83: 삭제 버튼 구현

삭제 버튼의 비활성화 상태와 시각적 피드백이 적절히 구현되어 있습니다.

src/app/features/dashboard_Id/components/EditColumnModal.tsx (4)

33-38: 중복 체크 로직 검증

현재 컬럼을 제외한 중복 체크 로직이 올바르게 구현되어 있습니다. column.id !== modalData?.columnId 조건으로 현재 편집 중인 컬럼을 제외하고 있습니다.


41-45: 버튼 활성화 조건 로직

수정 버튼의 비활성화 조건이 적절히 구현되어 있습니다. 특히 title.trim() === modalData?.columnTitle 조건으로 기존과 동일한 값일 때를 처리한 것이 좋습니다.


80-82: 모달 전환 로직

삭제 확인 모달로 전환하는 로직이 적절히 구현되어 있습니다. modalData!를 그대로 전달하는 것이 합리적입니다.


132-137: 에러 메시지 스타일링

에러 메시지에 Text-error 클래스를 사용하고 있어 다른 모달들과 일관성이 있습니다.

src/app/features/dashboard_Id/type/Column.type.ts (5)

14-27: CreateColumn 인터페이스

생성 요청과 응답 인터페이스가 적절히 정의되어 있습니다. 요청에는 필요한 필드만, 응답에는 서버에서 생성되는 필드들이 포함되어 있습니다.


29-41: UpdateColumn 인터페이스

수정 요청은 title만 필요하고, 응답은 전체 엔티티를 반환하는 구조가 합리적입니다.


43-53: Hook 변수 타입 정의

UpdateColumnVariablesDeleteColumnVariables에서 dashboardId가 캐시 무효화용으로 사용되는 것이 명확히 주석으로 표시되어 있어 좋습니다.


55-60: ColumnModalData 인터페이스

모달에서 사용할 데이터 구조가 적절히 정의되어 있습니다. columnIdcolumnTitle이 선택적 필드로 설정된 것이 create/edit 모달 모두에서 사용 가능하도록 한 좋은 설계입니다.


9-12: ColumnsResponse 인터페이스 수정

result 필드가 추가되었습니다. API 응답 구조와 일치하는지 확인이 필요합니다.

API 응답 구조를 확인하여 result 필드가 실제로 필요한지 검증해보세요:

#!/bin/bash
# Description: API 응답 구조에서 result 필드 사용 확인

# ColumnsResponse를 사용하는 곳에서 result 필드 사용 여부 확인
rg -A 5 -B 5 "ColumnsResponse|result.*string"

Comment on lines +24 to +42
onError: (error) => {
const serverMessage = error.response?.data?.message

// 서버에서 오는 에러 메시지에 따라 분기
if (
serverMessage?.includes('중복') ||
serverMessage?.includes('duplicate')
) {
toast.error('중복된 컬럼 이름입니다')
} else if (
serverMessage?.includes('최대') ||
serverMessage?.includes('maximum')
) {
toast.error('컬럼은 최대 10개까지 생성 가능합니다')
} else {
toast.error(serverMessage || '컬럼 생성 중 오류가 발생했습니다')
}
},
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 처리 로직의 견고성 개선 검토

서버 메시지 문자열 매칭을 통한 에러 분기 처리가 구현되어 있지만, 문자열 기반 판단은 서버 메시지 변경 시 취약할 수 있습니다.

더 견고한 에러 처리를 위해 다음과 같은 개선을 고려해보세요:

    onError: (error) => {
-      const serverMessage = error.response?.data?.message
+      const serverMessage = error.response?.data?.message
+      const errorCode = error.response?.data?.code

      // 에러 코드 우선, 메시지는 fallback으로 사용
-      if (
-        serverMessage?.includes('중복') ||
-        serverMessage?.includes('duplicate')
-      ) {
+      if (errorCode === 'DUPLICATE_COLUMN_NAME' || 
+          serverMessage?.includes('중복') ||
+          serverMessage?.includes('duplicate')) {
        toast.error('중복된 컬럼 이름입니다')
-      } else if (
-        serverMessage?.includes('최대') ||
-        serverMessage?.includes('maximum')
-      ) {
+      } else if (errorCode === 'MAX_COLUMNS_EXCEEDED' ||
+                 serverMessage?.includes('최대') ||
+                 serverMessage?.includes('maximum')) {
        toast.error('컬럼은 최대 10개까지 생성 가능합니다')
      } else {
        toast.error(serverMessage || '컬럼 생성 중 오류가 발생했습니다')
      }
    },
📝 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
onError: (error) => {
const serverMessage = error.response?.data?.message
// 서버에서 오는 에러 메시지에 따라 분기
if (
serverMessage?.includes('중복') ||
serverMessage?.includes('duplicate')
) {
toast.error('중복된 컬럼 이름입니다')
} else if (
serverMessage?.includes('최대') ||
serverMessage?.includes('maximum')
) {
toast.error('컬럼은 최대 10개까지 생성 가능합니다')
} else {
toast.error(serverMessage || '컬럼 생성 중 오류가 발생했습니다')
}
},
})
onError: (error) => {
const serverMessage = error.response?.data?.message
const errorCode = error.response?.data?.code
// 서버에서 오는 에러 메시지에 따라 분기
// 에러 코드 우선, 메시지는 fallback으로 사용
if (errorCode === 'DUPLICATE_COLUMN_NAME' ||
serverMessage?.includes('중복') ||
serverMessage?.includes('duplicate')) {
toast.error('중복된 컬럼 이름입니다')
} else if (errorCode === 'MAX_COLUMNS_EXCEEDED' ||
serverMessage?.includes('최대') ||
serverMessage?.includes('maximum')) {
toast.error('컬럼은 최대 10개까지 생성 가능합니다')
} else {
toast.error(serverMessage || '컬럼 생성 중 오류가 발생했습니다')
}
},
🤖 Prompt for AI Agents
In src/app/features/dashboard_Id/hooks/useCreateColumn.ts between lines 24 and
42, the error handling relies on matching substrings in server error messages,
which is fragile if server messages change. To improve robustness, refactor the
error handling to use standardized error codes or error types returned from the
server instead of string matching. Update the server API or error response
parsing to extract these codes and use them in the onError logic to determine
the appropriate toast error message.

Copy link

@Insung-Jo Insung-Jo left a comment

Choose a reason for hiding this comment

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

수고하셨습니다!

@LeeCh0129 LeeCh0129 merged commit 945f7ad into develop Jun 21, 2025
2 checks passed
@LeeCh0129 LeeCh0129 linked an issue Jun 21, 2025 that may be closed by this pull request
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨Feat 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Feat: 컬럼 관리 기능 구현 (생성/수정/삭제)

3 participants