-
Notifications
You must be signed in to change notification settings - Fork 2
✨ Feat: 대시보드 상세 페이지 #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Walkthrough이번 변경 사항에서는 대시보드의 컬럼 및 카드 데이터를 React Query와 Axios를 활용해 API에서 불러오고, 이를 표시하는 컴포넌트들이 새로 추가되었습니다. 이미지 최적화 설정과 JWT 인증이 적용된 Axios 클라이언트, 테마 토글 컴포넌트 임포트, React Query Provider 통합도 포함됩니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant DashboardPage
participant useColumns
participant axiosClient
participant useCards
participant Column
participant Card
User->>DashboardPage: 페이지 접근
DashboardPage->>useColumns: 컬럼 데이터 요청(dashboardId)
useColumns->>axiosClient: GET /columns?dashboardId=...
axiosClient-->>useColumns: 컬럼 데이터 반환
useColumns-->>DashboardPage: 컬럼 데이터 반환
DashboardPage->>Column: 각 컬럼 컴포넌트 렌더링
Column->>useCards: 카드 데이터 요청(columnId)
useCards->>axiosClient: GET /cards?columnId=...
axiosClient-->>useCards: 카드 데이터 반환
useCards-->>Column: 카드 데이터 반환
Column->>Card: 각 카드 컴포넌트 렌더링
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
npm error Exit handler never called! 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (10)
src/app/dashboard/[id]/page.tsx (2)
11-12: 기본적인 로딩/에러 상태 처리현재 로딩과 에러 상태가 매우 기본적입니다. 사용자 경험을 위해 더 나은 UI 컴포넌트 사용을 고려해보세요.
- if (isLoading) return <p>loading...</p> - if (error) return <p>error...{error.message}</p> + if (isLoading) return <div className="flex items-center justify-center h-screen">로딩 중...</div> + if (error) return <div className="flex items-center justify-center h-screen text-red-500">오류가 발생했습니다: {error.message}</div>
16-16: 고정 레이아웃 고려사항사이드바에 고정 너비(w-300)와 높이(h-1080)를 사용하고 있습니다. 다양한 화면 크기에서의 반응형 디자인을 고려해보세요.
src/app/dashboard/[id]/Card/Tags.tsx (2)
2-3: TODO 항목 추적태그 색상 랜덤 배정과 중복 태그 방지 기능이 계획되어 있습니다. 이를 이슈로 등록하여 추적하는 것을 고려해보세요.
이러한 기능들을 구현하는 코드를 생성해드릴까요?
10-10: 하드코딩된 색상을 테마 시스템으로 이전 고려인라인 스타일로 하드코딩된 색상 대신 CSS 변수나 테마 시스템 사용을 고려해보세요.
- style={{ backgroundColor: '#F7DBF0', color: '#D549B6' }} + className="bg-tag-primary text-tag-primary-foreground"src/app/dashboard/[id]/Column/Column.tsx (2)
7-8: 불필요한 타입 어노테이션 제거
column에서 구조분해할 때 이미Column타입이 추론되므로 추가 타입 어노테이션이 불필요합니다.- const { id, title }: { id: number; title: string } = column + const { id, title } = column🧰 Tools
🪛 Biome (1.9.4)
[error] 7-7: Shouldn't redeclare 'Column'. Consider to delete it or rename it.
'Column' is defined here:
(lint/suspicious/noRedeclare)
11-12: 일관된 로딩/에러 처리다른 컴포넌트들과 마찬가지로 기본적인 로딩/에러 상태입니다. 전체 앱에서 일관된 로딩 컴포넌트 사용을 고려해보세요.
src/app/dashboard/[id]/Card/Card.tsx (3)
11-11: 플레이스홀더 텍스트 제거"Todo Card" 텍스트가 개발 중 플레이스홀더로 보입니다. 제거하거나 적절한 레이블로 변경해주세요.
- Todo Card
8-8: 사용되지 않는 속성 확인
assignee와description속성을 구조분해하지만assignee는 플레이스홀더 텍스트로만 표시되고description은 사용되지 않습니다.- const { imageUrl, title, tags, dueDate, assignee } = card + const { imageUrl, title, tags, dueDate, assignee, description } = card그리고 실제 assignee 정보와 description을 렌더링하는 것을 고려해보세요.
16-17: 이미지 크기 비율 고려고정된 400x600 크기 대신 카드 컨테이너에 맞는 적절한 비율을 사용하는 것을 고려해보세요.
- width={400} - height={600} + width={314} + height={200}src/app/api/useCards.ts (1)
1-3: 임시 하드코딩된 값들에 대한 개선 계획이 필요합니다.주석에서 언급한 것처럼 하드코딩된 컬럼 ID와 향후 개선 사항들이 명시되어 있어 좋습니다. 다만 이러한 임시 구현들을 추적할 수 있도록 TODO 주석이나 이슈 번호를 추가하는 것을 고려해보세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
public/images/calendar.svgis excluded by!**/*.svgpublic/images/config.svgis excluded by!**/*.svgpublic/images/plus.svgis excluded by!**/*.svg
📒 Files selected for processing (11)
next.config.mjs(1 hunks)src/app/api/axiosClient.ts(1 hunks)src/app/api/useCards.ts(1 hunks)src/app/api/useColumns.ts(1 hunks)src/app/dashboard/[id]/Card/Card.tsx(1 hunks)src/app/dashboard/[id]/Card/Tags.tsx(1 hunks)src/app/dashboard/[id]/Column/Column.tsx(1 hunks)src/app/dashboard/[id]/page.tsx(1 hunks)src/app/globals.css(1 hunks)src/app/layout.tsx(2 hunks)src/app/providers.tsx(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/app/dashboard/[id]/Card/Card.tsx (2)
src/app/api/useCards.ts (1)
Card(13-26)src/app/dashboard/[id]/Card/Tags.tsx (1)
Tags(1-17)
src/app/layout.tsx (2)
src/app/providers.tsx (1)
Providers(7-21)src/app/shared/components/ThemeToggle.tsx (1)
ThemeToggle(6-27)
src/app/dashboard/[id]/Column/Column.tsx (3)
src/app/api/useColumns.ts (1)
Column(6-13)src/app/api/useCards.ts (2)
useCards(40-45)Card(13-26)src/app/dashboard/[id]/Card/Card.tsx (1)
Card(7-28)
🪛 Biome (1.9.4)
src/app/dashboard/[id]/Card/Card.tsx
[error] 7-7: Shouldn't redeclare 'Card'. Consider to delete it or rename it.
'Card' is defined here:
(lint/suspicious/noRedeclare)
src/app/dashboard/[id]/Column/Column.tsx
[error] 7-7: Shouldn't redeclare 'Column'. Consider to delete it or rename it.
'Column' is defined here:
(lint/suspicious/noRedeclare)
🔇 Additional comments (9)
next.config.mjs (1)
5-14: 이미지 최적화 설정이 올바르게 구성되었습니다AWS S3 버킷에서 제공되는 원격 이미지에 대한 Next.js 이미지 최적화 설정이 적절하게 구성되었습니다. 특정 호스트네임으로 제한하여 보안을 유지하고 있습니다.
src/app/globals.css (1)
37-39: CSS 유틸리티 클래스가 일관성 있게 추가되었습니다
.Border-column클래스가 기존 패턴을 따라 올바르게 구현되었으며, 라이트/다크 모드 지원도 적절히 구성되었습니다.src/app/layout.tsx (1)
25-27: 임시 코드 주석을 제거하고 의도를 명확히 하세요"푸쉬하기 전에 이거 삭제해야함" 주석이 있는데, ThemeToggle 컴포넌트를 실제로 제거해야 하는지 아니면 주석만 제거해야 하는지 명확하지 않습니다.
다음 중 어떤 것이 의도인지 확인해 주세요:
- ThemeToggle 컴포넌트를 완전히 제거
- 주석만 제거하고 ThemeToggle 유지
- ThemeToggle을 다른 위치로 이동
현재 상태로는 프로덕션에 임시 코드가 포함될 위험이 있습니다.
src/app/providers.tsx (1)
3-21: React Query 프로바이더 통합이 올바르게 구현되었습니다React Query의 모범 사례를 따라 QueryClient 인스턴스를 useState로 관리하고, 기존 ThemeProvider와 적절히 통합되었습니다. 프로바이더 계층 구조도 올바르게 설정되었습니다.
src/app/dashboard/[id]/Column/Column.tsx (1)
7-7: 정적 분석 경고 확인Biome에서 'Column' 재선언 경고를 보고했지만, 이는 타입을 import하고 컴포넌트 이름으로 사용하는 일반적인 패턴이므로 거짓 양성으로 보입니다.
🧰 Tools
🪛 Biome (1.9.4)
[error] 7-7: Shouldn't redeclare 'Column'. Consider to delete it or rename it.
'Column' is defined here:
(lint/suspicious/noRedeclare)
src/app/api/useColumns.ts (3)
21-21: 하드코딩된 팀 ID 확인PR 목표에 명시된 대로 임시 설정된 "7-6" 팀 ID입니다. 향후 동적으로 처리하도록 변경이 필요합니다.
팀 ID를 파라미터로 받도록 API 설계를 변경하는 것을 고려해보세요:
-export async function fetchColumns(dashboardId: number): Promise<Column[]> { +export async function fetchColumns(teamId: string, dashboardId: number): Promise<Column[]> { const res = await axiosClient.get<ColumnsResponse>( - `/7-6/columns?dashboardId=${dashboardId}`, + `/${teamId}/columns?dashboardId=${dashboardId}`, )
6-13: 타입 정의 승인Column 인터페이스가 잘 정의되어 있고 API 응답 구조와 일치합니다.
27-31: React Query 훅 구현 승인적절한 쿼리 키 사용과 타입 안전성이 보장된 훅 구현입니다.
src/app/api/useCards.ts (1)
13-26:Details
❓ Verification inconclusive
타입 일관성을 확인해주세요.
Card 인터페이스에서
teamId는 string 타입이고dashboardId,columnId는 number 타입입니다. 이러한 타입 불일치가 의도된 것인지 확인이 필요합니다.다음 스크립트로 API 응답 구조를 확인해보세요:
🏁 Script executed:
#!/bin/bash # Description: API 응답에서 ID 필드들의 타입 일관성 확인 # API 관련 타입 정의들을 검색하여 일관성 확인 rg -A 3 -B 3 "teamId|dashboardId|columnId" --type tsLength of output: 2419
Card·Column 인터페이스 ID 타입 일관성 확인
src/app/api/useCards.ts
- teamId: string
- dashboardId: number
- columnId: number
src/app/api/useColumns.ts
- teamId: string
- dashboardId: number
코드 상으로는 teamId가 string, dashboardId·columnId가 number로 정의되어 있어 인터페이스 간 일관성은 유지되고 있습니다.
다만, API 스펙 혹은 실제 응답에서 teamId가 string 타입인 것이 의도된 것인지 백엔드 문서나 실제 예시를 한 번 더 검증해주세요.
| axiosClient.interceptors.request.use((config) => { | ||
| if (TEMP_TOKEN) { | ||
| config.headers['Authorization'] = `Bearer ${TEMP_TOKEN}` | ||
| } | ||
|
|
||
| return config | ||
| }) |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
|
||
| import Column from './Column/Column' | ||
| export default function DashboardID() { | ||
| const dashboard = 15120 |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
src/app/api/useCards.ts
Outdated
| export async function fetchCards(columnId: number): Promise<CardResponse> { | ||
| const res = await axiosClient.get<CardResponse>( | ||
| `/7-6/cards?size=10&columnId=${columnId}`, | ||
| ) | ||
| return res.data | ||
| } |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
Insung-Jo
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
작업 수고 많으셨습니다 👍 👍 해당 코드에서 궁금한 부분은 리뷰로 남겼습니다!
| images: { | ||
| remotePatterns: [ | ||
| { | ||
| protocol: 'https', | ||
| hostname: 'sprint-fe-project.s3.ap-northeast-2.amazonaws.com', | ||
| port: '', | ||
| pathname: '/**', | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 코드를 추가 하신 이유가 무엇인가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
외부 이미지를 사용하기 위해 추가한 설정입니다.
Next.js의 Image 컴포넌트는 보안상의 이유로 외부 도메인의 이미지를 기본적으로 차단합니다.
받아온 이미지 URL을 보면
"imageUrl": "https://sprint-fe-project.s3.ap-northeast-2.amazonaws.com/taskify/task_image/7-6_50923_1749446154135.jpeg",
이렇고, 외부 API에서 데이터를 받아올때. 이미지 URL은 AWS S3에 저장해서 제공하고 있습니다.(amazonaws)
Next.js에서 이 이미지를 표시하려면 S3 도메인을 허용해야 했습니다.
images: {
remotePatterns: [
{
protocol: 'https', // HTTPS만 허용
hostname: 'sprint-fe-project.s3.ap-northeast-2.amazonaws.com', // AWS S3 버킷
port: '', // 기본 포트 (443)
pathname: '/**', // 모든 경로 허용
},
],
},There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
답변 감사합니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
외부 도메인의 이미지를 차단하는 지는 처음 알았네용 😮
| export default function useColumns(dashboardId: number) { | ||
| return useQuery<Column[]>({ | ||
| queryKey: ['columns', dashboardId], | ||
| queryFn: () => fetchColumns(dashboardId), | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아직 useQuery에 대해 조사하지 못해서 질문드립니다!
useQuery는 보통 어떤 상황에서 사용하나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useQuery 를 사용하면 기존 방식대로 useEffect, useState를 사용해서 데이터를 호출/처리할 필요가 없어지는데요
복잡한 서버 상태 관리를 간결하게 해주는 훅이라서,
실제 API 호출 함수(fetch)와 <->데이터를 받고 상태 관리하려고 하는 컴포넌트 사이의 중간 처리 장치쯤의.. 느낌입니다.
useQuery에 API 호출 함수를 연결하고 몇가지 설정하면, 호출 시 복잡한 상태 관리는 useQuery가 자동으로 처리해줍니다.
컴포넌트에서는 작성해둔 커스텀 훅을 호출해서 useQuery가 제공하는 다양한 상태를 활용하면 됩니다.
비포 애프터 예시)
// 기존 방식 (복잡함)
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
fetchData()
.then(setData)
.catch(setError) //-->error
.finally(() => setLoading(false)) //-->isLoading
}, [])
// useQuery (간단함)
const { data, isLoading, error } = useQuery({
queryKey: ['data'], // useEffect, setData가 따로 필요 없음
queryFn: fetchData
})
if (isLoading) return <p>loading...</p>
if (error) return <p>error...{error.message}</p>
//제 코드에서는 {
// queryKey: ['data'],
// queryFn: fetchData
// }) 이부분을 커스텀 훅에 따로 넣고, 컴포넌트에서는 해당 훅(useColumns)를 호출한 구조입니다.로딩/에러상태 관리, 캐싱, 리페칭, 의존성 변화 감지, 실패시 자동 재시도 등 복잡한 로직을 알아서 처리해주는 부분이 많습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 useQuery 사용이 궁금했는데 코드로 보니 이해가 잘 되네요!!
LeeCh0129
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
타입 정의부터, 컴포넌트 분리까지 컴포넌트 구조도 깔끔하게해서 대시보드 상세 페이지를 잘 구현해주셨네요. 고생많으셨습니다👍
yuj2n
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
지윤님 대시보드 상세 페이지 작업 수고 많으셨고 tanStack Query 사용방법 배워갑니당~!
| images: { | ||
| remotePatterns: [ | ||
| { | ||
| protocol: 'https', | ||
| hostname: 'sprint-fe-project.s3.ap-northeast-2.amazonaws.com', | ||
| port: '', | ||
| pathname: '/**', | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
외부 도메인의 이미지를 차단하는 지는 처음 알았네용 😮
| @@ -0,0 +1,46 @@ | |||
| //size일단 10으로 하고, 나중에 커서아이디 받아서 무한 스크롤 구현해야 함. | |||
| import { useQuery } from '@tanstack/react-query' | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tanStack Query 사용하셨군용!!
| <ThemeProvider attribute="class" defaultTheme="system" enableSystem={true}> | ||
| {children} | ||
| </ThemeProvider> | ||
| <QueryClientProvider client={queryClient}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tanStack Query의 경우도 provider로 감싸줘야 하는군용
| export default function useColumns(dashboardId: number) { | ||
| return useQuery<Column[]>({ | ||
| queryKey: ['columns', dashboardId], | ||
| queryFn: () => fetchColumns(dashboardId), | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 useQuery 사용이 궁금했는데 코드로 보니 이해가 잘 되네요!!
📌 변경 사항 개요
📝 상세 내용
1. 외부 이미지 URL 사용하기 위해 추가한 설정입니다.
2. useColumns, useCards - useQuery 사용함
useCiolumns에서 dashboardId를 받아서 컬럼 데이터를 받아옵니다.
컬럼 컴포넌트 내부에서 useCards에 columnId를 전달하여, 컬럼에 속하는 카드 데이터를 받아옵니다.
카드 내부의 태그는 별도 컴포넌트로 작업중입니다.
🔗 관련 이슈
#21
🖼️ 스크린샷
✅ 체크리스트
💡 참고 사항
API 호출 관련해서 임시로 작성해둔 부분은 일단 무시해주세요!(팀아이디를 7-6으로, 토큰도 임시 작성함)
Summary by CodeRabbit
신규 기능
스타일
기타