-
Notifications
You must be signed in to change notification settings - Fork 2
3주차 과제 제출 #2
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
base: main
Are you sure you want to change the base?
3주차 과제 제출 #2
Conversation
## Walkthrough
이 변경사항은 React Query 및 Axios를 통한 데이터 패칭, 캐싱, 에러 핸들링을 프로젝트에 도입합니다. API 클라이언트와 관련 커스텀 훅들이 추가 및 구현되었으며, 컴포넌트 구조가 React Query와 에러 바운더리, 서스펜스를 활용하도록 리팩토링되었습니다. 테스트 코드와 타입 정의도 이에 맞게 수정 및 추가되었습니다.
## Changes
| 파일/그룹 | 변경 요약 |
|---------------------------------------------|-------------------------------------------------------------------------------------------|
| package.json | React Query, Axios, nock 등 신규 의존성 추가 |
| src/api.ts | 티켓/댓글 CRUD용 API 클라이언트 및 DTO, 함수 추가 |
| src/types.tsx | Ticket, Comment의 id 타입을 number에서 string으로 변경 |
| src/hooks/useTickets.ts | React Query 기반 티켓 데이터 패칭 훅 구현 |
| src/hooks/useComments.ts | 티켓별 댓글 패칭 훅 신규 추가 |
| src/hooks/useCreateTicket.ts | 티켓 생성용 뮤테이션 훅 구현 (Optimistic update 포함) |
| src/hooks/useCreateComment.ts | 댓글 생성용 뮤테이션 훅 구현 (Optimistic update 포함) |
| src/hooks/useUpdateTicketStatus.ts | 티켓 상태 업데이트용 뮤테이션 훅 신규 추가 (Optimistic update 포함) |
| src/components/App.tsx | React Query Provider 및 Devtools로 래핑 |
| src/components/Main.tsx | Suspense, ErrorBoundary 도입 및 TicketList 데이터 패칭 책임 이전 |
| src/components/TicketList.tsx | props 제거, 내부적으로 useTickets 사용하도록 변경 |
| src/components/TicketItem.tsx | 상태 토글 로직 변경, 댓글 refetch 로직 추가, useComments 도입 |
| src/components/CommentForm.tsx | ticketId 타입 변경, onCommentAdded 콜백 prop 추가 및 적용 |
| src/components/CommentList.tsx | comments prop이 배열인지 확인하는 런타임 체크 추가 |
| src/components/TicketForm.tsx | 불필요한 import 제거 |
| src/components/TicketForm.test.tsx | TicketForm 컴포넌트의 API 호출 및 입력값 테스트 신규 추가 |
| src/components/TicketItem.test.tsx | nock을 이용한 API 모킹, React Query Provider 적용, 테스트 개선 |
| src/components/TicketList.test.tsx | TicketList 렌더링 및 데이터 패칭 테스트 신규 추가 |
| src/components/CommentForm.test.tsx | CommentForm 컴포넌트의 댓글 제출 기능 테스트 신규 추가 |
| src/components/CommentList.test.tsx | CommentList 렌더링 테스트 신규 추가 |
## Sequence Diagram(s)
```mermaid
sequenceDiagram
participant User
participant App
participant QueryClientProvider
participant TicketList
participant useTickets/API
participant TicketItem
participant useComments/API
User->>App: 앱 진입
App->>QueryClientProvider: React Query 컨텍스트 제공
QueryClientProvider->>TicketList: Suspense로 TicketList 렌더링
TicketList->>useTickets/API: 티켓 목록 패칭
useTickets/API-->>TicketList: 티켓 데이터 반환
TicketList->>TicketItem: 각 티켓 렌더링
TicketItem->>useComments/API: (필요시) 댓글 패칭
useComments/API-->>TicketItem: 댓글 데이터 반환
User->>TicketItem: 상태 토글/댓글 추가 등 상호작용
TicketItem->>useUpdateTicketStatus/API: 상태 변경 요청 (Optimistic update)
TicketItem->>useCreateComment/API: 댓글 생성 요청 (Optimistic update)
API-->>TicketItem: 응답 및 캐시 갱신/롤백Estimated code review effort🎯 3 (Moderate) | ⏱️ ~40 minutes Poem
|
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.
Summary of Changes
Hello @stbpiza, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request significantly refactors the application's data layer by adopting @tanstack/react-query for all data fetching and state management. It introduces a dedicated API client, implements optimistic UI updates for a smoother user experience, and integrates React Suspense and Error Boundaries for improved loading and error handling. The changes also include new and updated tests to cover the new data interaction patterns.
Highlights
- Data Fetching & State Management: Integrated
@tanstack/react-queryfor efficient data fetching, caching, and state management across the application, replacing previous manual data handling. - API Client Implementation: Introduced a dedicated
src/api.tsmodule usingaxiosto centralize all API communication, providing clear functions for interacting with ticket and comment endpoints. - Optimistic UI Updates: Implemented optimistic updates for ticket creation, comment creation, and ticket status changes using React Query's
useMutationhooks, significantly improving perceived application responsiveness. - Error Handling & Suspense Integration: Incorporated
react-error-boundaryand React'sSuspenseto provide robust error handling and declarative loading states, enhancing user experience and application resilience. - Enhanced Testing with Mocking: Added new test files (
TicketForm.test.tsx,TicketList.test.tsx) and updatedTicketItem.test.tsxto include API mocking withnock, ensuring comprehensive testing of data interactions. - Data Model Type Alignment: Updated the
idtypes forTicketandCommentinterfaces insrc/types.tsxfromnumbertostringto align with the backend's string-based identifiers.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
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.
Code Review
This pull request integrates react-query for managing server state, enabling features for creating tickets, adding comments, and updating ticket statuses with optimistic updates. Key improvements include addressing a bug in useComments, using unique temporary IDs for optimistic updates, removing debugging logs, and enhancing type safety.
|
|
||
| console.log('🪝 useComments', data); | ||
|
|
||
| const comments = data || []; |
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.
| const [shouldRefetchComments, setShouldRefetchComments] = useState(false); | ||
|
|
||
| const { comments } = useComments(ticket.id, shouldRefetchComments); | ||
| const nowComments = shouldRefetchComments ? comments : ticket.comments; |
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.
| tickets: [ | ||
| ...old?.tickets || [], | ||
| { | ||
| id: '', |
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.
| comments: [ | ||
| ...(old?.comments || []), | ||
| { | ||
| id: '', |
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.
|
|
||
| const queryClient = new QueryClient(); | ||
|
|
||
| export default function App() { |
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.
| })); | ||
| return { previousComments }; | ||
| }, | ||
| onError: (error, { ticketId }, context: any) => { |
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 async function fetchTickets(): Promise<TicketListDto> { | ||
| const { data } = await instance.get('/tickets'); | ||
|
|
||
| console.log('🌏 fetchTickets', data); |
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.
| })); | ||
| return { previousTickets }; | ||
| }, | ||
| onError: (error, _variables, context: any) => { |
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.
| throw error; | ||
| } | ||
|
|
||
| console.log('🪝 useTickets', data); |
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.
| })); | ||
| return { previousTickets }; | ||
| }, | ||
| onError: (_error, _variables, context) => { |
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.
Actionable comments posted: 11
🔭 Outside diff range comments (1)
src/components/CommentForm.tsx (1)
14-28: 에러 핸들링 추가 제안폼 제출 시 에러 핸들링이 없습니다. 네트워크 오류나 API 에러 상황에 대한 처리를 추가하는 것을 고려해보세요.
다음과 같이 에러 핸들링을 추가할 수 있습니다:
createComment({ ticketId, content }, { onSuccess: () => { onCommentAdded(); + form.reset(); }, + onError: (error) => { + console.error('댓글 추가 실패:', error); + // 사용자에게 에러 메시지 표시 + }, }); - form.reset();
🧹 Nitpick comments (12)
src/components/App.tsx (1)
7-7: QueryClient 설정 개선 제안기본 설정으로 QueryClient를 생성하고 있습니다. 프로덕션 환경을 위해 캐시 시간, 재시도 정책 등을 설정하는 것을 고려해보세요.
다음과 같이 설정을 추가할 수 있습니다:
-const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 5 * 60 * 1000, // 5분 + retry: 1, + }, + mutations: { + retry: 1, + }, + }, +});src/components/TicketList.tsx (1)
16-16: 주석 처리된 dispatch prop을 정리해주세요.현재 사용되지 않는 dispatch prop이 주석 처리되어 있습니다. 이것이 의도적인 것인지 확인하고, 더 이상 필요하지 않다면 제거하는 것이 좋겠습니다.
- // dispatch={() => {}}src/components/Main.tsx (1)
9-9: 사용되지 않는 상태 변수를 정리해주세요.
visible상태와setVisible함수가 정의되었지만 사용되지 않고 있습니다. 필요하지 않다면 제거하는 것이 좋겠습니다.- const [visible, setVisible] = useState(true);src/hooks/useTickets.ts (1)
18-18: 프로덕션 환경에서 console.log 제거를 고려해주세요.디버깅용 console.log가 있습니다. 개발 중에는 유용하지만, 프로덕션 배포 전에는 제거하거나 개발 환경에서만 실행되도록 조건부 처리하는 것을 고려해보세요.
- console.log('🪝 useTickets', data); + if (process.env.NODE_ENV === 'development') { + console.log('🪝 useTickets', data); + }src/components/TicketList.test.tsx (1)
47-61: 테스트 어서션 개선 필요현재 테스트는 요소의 존재만 확인하고 있어 실제 데이터 바인딩을 제대로 검증하지 못합니다.
더 구체적인 어서션을 사용하세요:
it('renders tickets', async () => { renderTicketList(); await waitFor(() => { - screen.getByText(/Ticket #1/); + expect(screen.getByText('Ticket #1')).toBeInTheDocument(); }); }); it('renders comments', async () => { renderTicketList(); await waitFor(() => { - screen.getByText(/Comment Content/); + expect(screen.getByText('Comment Content')).toBeInTheDocument(); }); });src/hooks/useComments.ts (1)
20-22: 프로덕션 코드에서 디버깅 로그 제거 필요콘솔 로그가 프로덕션 환경에서도 실행되어 성능에 영향을 줄 수 있습니다.
환경변수를 활용한 조건부 로깅을 고려하세요:
- console.log(' shouldFetch', shouldFetch); - console.log('🪝 useComments', data); + if (process.env.NODE_ENV === 'development') { + console.log(' shouldFetch', shouldFetch); + console.log('🪝 useComments', data); + }src/hooks/useCreateComment.ts (1)
25-25: 프로덕션 환경에서 콘솔 로그 제거 고려에러 로깅이 프로덕션에서도 실행됩니다.
환경에 따른 조건부 로깅을 고려하세요:
- console.log('🚨 Error:', error); + if (process.env.NODE_ENV === 'development') { + console.log('🚨 Error:', error); + }src/components/TicketItem.tsx (2)
20-20: 프로덕션 코드에서 디버깅 로그 제거콘솔 로그가 남아있습니다.
- console.log(' comments', comments);
29-31: 댓글 추가 후 상태 리셋 메커니즘 부족
shouldRefetchComments가 한 번 true로 설정되면 다시 false로 돌아갈 방법이 없어, 불필요한 API 호출이 계속 발생할 수 있습니다.댓글 추가 완료 후 플래그를 재설정하는 로직을 추가하거나, 댓글 데이터를 더 효율적으로 관리하는 방법을 고려하세요:
const handleCommentAdded = () => { setShouldRefetchComments(true); + // 선택사항: 일정 시간 후 또는 특정 조건에서 false로 재설정 };src/components/TicketItem.test.tsx (1)
43-51: 테스트용 QueryClient 설정을 개선하세요.테스트에서 더 나은 성능과 일관된 동작을 위해 QueryClient를 최적화하세요.
function renderTicketItem() { - const queryClient = new QueryClient(); + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + }, + mutations: { + retry: false, + }, + }, + }); render(( <QueryClientProvider client={queryClient}> <TicketItem ticket={ticket} /> </QueryClientProvider> )); }src/api.ts (2)
16-19: Axios 인스턴스에 에러 핸들링을 추가하세요.네트워크 오류나 HTTP 오류에 대한 일관된 처리를 위해 인터셉터를 추가하는 것을 고려하세요.
const instance = axios.create({ baseURL: API_BASE_URL, timeout: 10_000, }); + +instance.interceptors.response.use( + (response) => response, + (error) => { + console.error('API Error:', error); + return Promise.reject(error); + } +);
24-24: 프로덕션에서 콘솔 로그 제거를 고려하세요.디버깅 목적의 콘솔 로그가 프로덕션 환경에서도 실행됩니다. 환경에 따른 조건부 로깅을 고려하세요.
- console.log('🌏 fetchTickets', data); + if (process.env.NODE_ENV === 'development') { + console.log('🌏 fetchTickets', data); + }또는 로깅 라이브러리 사용을 고려하세요.
Also applies to: 35-35, 51-51, 59-59
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
.DS_Storeis excluded by!**/.DS_Storepackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (18)
package.json(1 hunks)src/api.ts(1 hunks)src/components/App.tsx(1 hunks)src/components/CommentForm.tsx(2 hunks)src/components/CommentList.tsx(1 hunks)src/components/Main.tsx(1 hunks)src/components/TicketForm.test.tsx(1 hunks)src/components/TicketForm.tsx(1 hunks)src/components/TicketItem.test.tsx(2 hunks)src/components/TicketItem.tsx(2 hunks)src/components/TicketList.test.tsx(1 hunks)src/components/TicketList.tsx(1 hunks)src/hooks/useComments.ts(1 hunks)src/hooks/useCreateComment.ts(1 hunks)src/hooks/useCreateTicket.ts(1 hunks)src/hooks/useTickets.ts(1 hunks)src/hooks/useUpdateTicketStatus.ts(1 hunks)src/types.tsx(1 hunks)
🧬 Code Graph Analysis (13)
src/hooks/useTickets.ts (1)
src/api.ts (1)
fetchTickets(21-27)
src/components/TicketList.test.tsx (2)
src/api.ts (2)
TicketListDto(5-7)API_BASE_URL(13-13)src/components/TicketList.tsx (1)
TicketList(7-21)
src/components/CommentForm.tsx (1)
src/api.ts (1)
createComment(45-54)
src/hooks/useCreateTicket.ts (1)
src/api.ts (2)
createTicket(29-36)TicketListDto(5-7)
src/hooks/useCreateComment.ts (1)
src/api.ts (2)
createComment(45-54)CommentListDto(9-11)
src/components/App.tsx (2)
src/components/Header.tsx (1)
Header(1-7)src/components/Main.tsx (1)
Main(8-21)
src/components/Main.tsx (2)
src/components/TicketList.tsx (1)
TicketList(7-21)src/components/TicketForm.tsx (1)
TicketForm(9-32)
src/components/TicketForm.test.tsx (3)
src/types.tsx (1)
Ticket(6-12)src/api.ts (1)
API_BASE_URL(13-13)src/components/TicketForm.tsx (1)
TicketForm(9-32)
src/hooks/useUpdateTicketStatus.ts (1)
src/api.ts (2)
updateTicketStatus(38-43)TicketListDto(5-7)
src/components/TicketItem.test.tsx (2)
src/types.tsx (1)
Ticket(6-12)src/api.ts (1)
API_BASE_URL(13-13)
src/hooks/useComments.ts (1)
src/api.ts (1)
fetchComments(56-62)
src/components/TicketList.tsx (1)
src/hooks/useTickets.ts (1)
useTickets(5-23)
src/api.ts (1)
src/types.tsx (2)
Ticket(6-12)Comment(1-4)
🧰 Additional context used
🧬 Code Graph Analysis (13)
src/hooks/useTickets.ts (1)
src/api.ts (1)
fetchTickets(21-27)
src/components/TicketList.test.tsx (2)
src/api.ts (2)
TicketListDto(5-7)API_BASE_URL(13-13)src/components/TicketList.tsx (1)
TicketList(7-21)
src/components/CommentForm.tsx (1)
src/api.ts (1)
createComment(45-54)
src/hooks/useCreateTicket.ts (1)
src/api.ts (2)
createTicket(29-36)TicketListDto(5-7)
src/hooks/useCreateComment.ts (1)
src/api.ts (2)
createComment(45-54)CommentListDto(9-11)
src/components/App.tsx (2)
src/components/Header.tsx (1)
Header(1-7)src/components/Main.tsx (1)
Main(8-21)
src/components/Main.tsx (2)
src/components/TicketList.tsx (1)
TicketList(7-21)src/components/TicketForm.tsx (1)
TicketForm(9-32)
src/components/TicketForm.test.tsx (3)
src/types.tsx (1)
Ticket(6-12)src/api.ts (1)
API_BASE_URL(13-13)src/components/TicketForm.tsx (1)
TicketForm(9-32)
src/hooks/useUpdateTicketStatus.ts (1)
src/api.ts (2)
updateTicketStatus(38-43)TicketListDto(5-7)
src/components/TicketItem.test.tsx (2)
src/types.tsx (1)
Ticket(6-12)src/api.ts (1)
API_BASE_URL(13-13)
src/hooks/useComments.ts (1)
src/api.ts (1)
fetchComments(56-62)
src/components/TicketList.tsx (1)
src/hooks/useTickets.ts (1)
useTickets(5-23)
src/api.ts (1)
src/types.tsx (2)
Ticket(6-12)Comment(1-4)
🔇 Additional comments (25)
src/components/TicketForm.tsx (1)
1-1: 불필요한 import 제거 승인사용하지 않는
useimport를 제거한 것은 좋은 변경사항입니다. 코드가 더 깔끔해졌습니다.src/components/CommentList.tsx (1)
8-10: 방어적 프로그래밍 승인:commentsprop 타입 일관성 수동 검증 필요
Array.isArray()체크는 런타임 안전성을 높여주지만,CommentList컴포넌트의 props가Comment[]로 정의되어 있다면 불필요할 수 있습니다. 자동화된 검색으로 사용처를 찾지 못했으므로, 아래 사항을 직접 검토해 주세요:
src/components/CommentList.tsx내부에서comments: Comment[]로 정확히 선언되어 있는지 확인- 프로젝트 내 모든
<CommentList comments={…} />사용 위치를 찾아,comments에 배열이 아닌 값(예:undefined,null, 객체 등)이 전달될 가능성이 없는지 검토만약 배열 외의 값이 전달될 수 있다면,
- 호출부에서 항상 배열만 넘기도록 수정하거나
CommentListprops 타입을Comment[] | undefined등으로 명시하고 방어 코드 유지위 사항 검증 후에도 불확실한 부분이 남아있다면 알려주세요.
src/types.tsx (1)
2-2: ID 타입 변경 추가 검증 필요자동 검색 스크립트 결과,
id를 숫자로 처리하는 코드가 남아있지 않은 것으로 보입니다.
그러나 테스트, 목(mock) 데이터, 외부 API 호출 등에서 문자열 ID를 정상적으로 처리하는지 수동으로 검증해 주세요.src/components/App.tsx (2)
1-2: React Query 통합 승인React Query와 개발 도구 import가 올바르게 추가되었습니다.
11-17: React Query Provider 구조 승인QueryClientProvider로 앱을 감싸고 ReactQueryDevtools를 추가한 구조가 올바릅니다. 기존 컴포넌트 구조도 잘 보존되었습니다.
src/components/CommentForm.tsx (2)
8-11: Props 타입 변경 및 콜백 패턴 승인
ticketId를 string으로 변경한 것은 types.tsx의 변경사항과 일치합니다.onCommentAdded콜백 추가는 부모-자식 컴포넌트 간 통신을 위한 좋은 패턴입니다.
21-25: React Query 뮤테이션 통합 승인
onSuccess콜백을 통한 댓글 추가 후 처리가 올바르게 구현되었습니다. React Query의 뮤테이션 패턴을 잘 활용하고 있습니다.src/components/TicketList.tsx (2)
5-5: 올바른 커스텀 훅 import입니다.React Query를 사용한 데이터 패칭으로의 전환이 올바르게 구현되었습니다.
7-8: 깔끔한 데이터 패칭 구현입니다.컴포넌트가 props로 데이터를 받는 대신 내부에서 직접 데이터를 패칭하도록 변경된 것이 좋습니다. 이는 컴포넌트의 독립성을 높이고 React Query의 장점을 활용할 수 있게 합니다.
package.json (4)
19-19: 테스트용 HTTP 모킹 라이브러리가 적절히 추가되었습니다.
nock은 HTTP 요청을 모킹하기 위한 표준적인 라이브러리로 좋은 선택입니다.
25-27: React Query 의존성이 올바르게 추가되었습니다.최신 버전의 TanStack React Query와 개발 도구가 추가되어 데이터 패칭, 캐싱, 디버깅을 지원할 수 있습니다.
27-27: Axios HTTP 클라이언트가 적절히 추가되었습니다.API 요청을 위한 널리 사용되는 HTTP 클라이언트 라이브러리입니다.
30-30: 에러 바운더리 라이브러리가 적절히 추가되었습니다.React 컴포넌트의 에러 핸들링을 위한 표준적인 라이브러리로 좋은 선택입니다.
src/components/TicketForm.test.tsx (4)
1-14: 테스트 의존성 import가 적절합니다.필요한 테스트 도구들과 React Query 의존성이 올바르게 import되었습니다.
28-37: API 모킹 설정이 잘 구현되었습니다.nock을 사용한 HTTP 요청 모킹과 요청 본문 캡처 로직이 적절하게 구현되어 있습니다. 201 상태 코드 반환도 POST 요청에 적합합니다.
39-47: QueryClient 설정이 올바르게 구현되었습니다.React Query 컨텍스트를 제공하기 위한 QueryClientProvider 래핑이 적절하게 구현되어 있습니다.
50-66: 사용자 상호작용 테스트가 포괄적으로 구현되었습니다.폼 입력, 제출, API 호출 검증까지 전체 플로우를 테스트하는 좋은 구조입니다.
waitFor를 사용한 비동기 검증도 적절합니다.src/components/Main.tsx (2)
1-3: React와 에러 바운더리 import가 적절합니다.Suspense와 ErrorBoundary를 활용한 에러 및 로딩 상태 처리를 위한 올바른 import입니다.
13-17: Suspense와 ErrorBoundary 구조가 잘 구현되었습니다.React Query의 suspense 모드와 함께 사용하기 적합한 구조입니다. 로딩 상태와 에러 상태를 적절히 처리하고 있습니다.
src/hooks/useTickets.ts (4)
1-3: React Query와 API 함수 import가 적절합니다.useSuspenseQuery와 fetchTickets 함수가 올바르게 import되었습니다.
6-12: useSuspenseQuery 설정이 적절합니다.쿼리 키, 쿼리 함수, 재시도 설정이 올바르게 구성되어 있습니다.
retry: 0설정은 의도적인 것으로 보입니다.
14-16: 에러 핸들링이 Suspense 패턴에 적합합니다.에러 발생 시 throw하는 방식이 React Suspense와 ErrorBoundary 패턴에 적합합니다.
20-22: 데이터 추출과 fallback 처리가 안전하게 구현되었습니다.
data?.tickets || []패턴으로 안전하게 데이터를 추출하고 빈 배열로 fallback하는 것이 좋습니다.src/hooks/useUpdateTicketStatus.ts (1)
10-22: 낙관적 업데이트 로직이 잘 구현됨쿼리 취소, 이전 데이터 백업, 그리고 즉시 캐시 업데이트가 올바르게 구현되어 있습니다. 에러 발생 시 롤백을 위한 컨텍스트 반환도 적절합니다.
src/hooks/useCreateComment.ts (1)
29-31: ticketId 반환 보장됨 확인
createComment함수가 항상{ ticketId, …data }형태로 반환하므로,onSettled콜백에서ticketId를 사용해 쿼리를 무효화하는 로직은 안전합니다. 추가 검증은 필요 없습니다.
| export async function createTicket({ title, description }: { | ||
| title: string; | ||
| description: string; | ||
| }) { | ||
| const { data } = await instance.post('/tickets', { title, description }); | ||
|
|
||
| console.log('🌏 createTicket', data); | ||
| } |
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.
createTicket 함수가 생성된 데이터를 반환하도록 수정하세요.
현재 함수는 응답 데이터를 로깅하지만 반환하지 않아서, 호출하는 곳에서 생성된 티켓 정보를 사용할 수 없습니다.
export async function createTicket({ title, description }: {
title: string;
description: string;
-}) {
+}): Promise<Ticket> {
const { data } = await instance.post('/tickets', { title, description });
console.log('🌏 createTicket', data);
+
+ return data;
}📝 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.
| export async function createTicket({ title, description }: { | |
| title: string; | |
| description: string; | |
| }) { | |
| const { data } = await instance.post('/tickets', { title, description }); | |
| console.log('🌏 createTicket', data); | |
| } | |
| export async function createTicket({ title, description }: { | |
| title: string; | |
| description: string; | |
| }): Promise<Ticket> { | |
| const { data } = await instance.post('/tickets', { title, description }); | |
| console.log('🌏 createTicket', data); | |
| return data; | |
| } |
🤖 Prompt for AI Agents
In src/api.ts around lines 29 to 36, the createTicket function logs the created
ticket data but does not return it, preventing callers from accessing the new
ticket information. Modify the function to return the data object after logging
so that the created ticket details are available to the caller.
| export async function updateTicketStatus({ id, status }: { | ||
| id: string; | ||
| status: 'open' | 'closed'; | ||
| }) { | ||
| await instance.patch(`/tickets/${id}`, { status }); | ||
| } |
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.
🛠️ Refactor suggestion
updateTicketStatus 함수의 반환 타입을 명시하세요.
일관성을 위해 업데이트된 티켓 데이터를 반환하는 것을 고려하세요.
export async function updateTicketStatus({ id, status }: {
id: string;
status: 'open' | 'closed';
-}) {
- await instance.patch(`/tickets/${id}`, { status });
+}): Promise<Ticket> {
+ const { data } = await instance.patch(`/tickets/${id}`, { status });
+
+ console.log('🌏 updateTicketStatus', data);
+
+ return data;
}📝 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.
| export async function updateTicketStatus({ id, status }: { | |
| id: string; | |
| status: 'open' | 'closed'; | |
| }) { | |
| await instance.patch(`/tickets/${id}`, { status }); | |
| } | |
| export async function updateTicketStatus({ id, status }: { | |
| id: string; | |
| status: 'open' | 'closed'; | |
| }): Promise<Ticket> { | |
| const { data } = await instance.patch(`/tickets/${id}`, { status }); | |
| console.log('🌏 updateTicketStatus', data); | |
| return data; | |
| } |
🤖 Prompt for AI Agents
In src/api.ts around lines 38 to 43, the updateTicketStatus function lacks an
explicit return type and currently does not return any data. Modify the function
to specify a return type that matches the updated ticket data structure and
update the function to return the response data from the patch request, ensuring
consistency and clarity in the API's behavior.
| beforeEach(() => { | ||
| vi.resetAllMocks(); | ||
| requestTicketId = ''; | ||
|
|
||
| nock(API_BASE_URL) | ||
| .patch(`/tickets/${ticket.id}`) | ||
| .reply(200, (uri, body: any) => { | ||
| const parts = uri.split('/'); | ||
| requestTicketId = parts[parts.length - 1]; | ||
| return { | ||
| ...ticket, | ||
| status: body.status, | ||
| }; | ||
| }); | ||
| }); |
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.
🛠️ Refactor suggestion
nock 인터셉터 정리를 추가하세요.
테스트 간 간섭을 방지하기 위해 각 테스트 후 nock 인터셉터를 정리해야 합니다.
beforeEach(() => {
requestTicketId = '';
+ nock.cleanAll();
nock(API_BASE_URL)
.patch(`/tickets/${ticket.id}`)
.reply(200, (uri, body: any) => {
const parts = uri.split('/');
requestTicketId = parts[parts.length - 1];
return {
...ticket,
status: body.status,
};
});
});또한 afterEach에서도 정리하는 것을 고려하세요:
+ afterEach(() => {
+ nock.cleanAll();
+ });📝 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.
| beforeEach(() => { | |
| vi.resetAllMocks(); | |
| requestTicketId = ''; | |
| nock(API_BASE_URL) | |
| .patch(`/tickets/${ticket.id}`) | |
| .reply(200, (uri, body: any) => { | |
| const parts = uri.split('/'); | |
| requestTicketId = parts[parts.length - 1]; | |
| return { | |
| ...ticket, | |
| status: body.status, | |
| }; | |
| }); | |
| }); | |
| beforeEach(() => { | |
| requestTicketId = ''; | |
| nock.cleanAll(); | |
| nock(API_BASE_URL) | |
| .patch(`/tickets/${ticket.id}`) | |
| .reply(200, (uri, body: any) => { | |
| const parts = uri.split('/'); | |
| requestTicketId = parts[parts.length - 1]; | |
| return { | |
| ...ticket, | |
| status: body.status, | |
| }; | |
| }); | |
| }); | |
| afterEach(() => { | |
| nock.cleanAll(); | |
| }); |
🤖 Prompt for AI Agents
In src/components/TicketItem.test.tsx around lines 28 to 41, the nock
interceptor set up in beforeEach is not cleaned up after each test, which can
cause interference between tests. Add an afterEach block that calls
nock.cleanAll() to clear all interceptors after each test, ensuring isolation
and preventing side effects.
| const { comments } = useComments(ticket.id, shouldRefetchComments); | ||
| const nowComments = shouldRefetchComments ? comments : ticket.comments; | ||
|
|
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.
🛠️ Refactor suggestion
댓글 데이터 전환 로직 개선 필요
현재 로직은 shouldRefetchComments 플래그에 따라 댓글 소스를 전환하는데, 이는 혼란을 야기할 수 있습니다. 또한 한번 true로 설정되면 다시 원래 댓글로 돌아갈 방법이 없습니다.
더 명확한 댓글 관리 로직으로 개선하세요:
- const { comments } = useComments(ticket.id, shouldRefetchComments);
- const nowComments = shouldRefetchComments ? comments : ticket.comments;
+ const { comments: fetchedComments } = useComments(ticket.id, shouldRefetchComments);
+ const displayComments = shouldRefetchComments ? fetchedComments : ticket.comments;
// ... JSX에서
- <CommentList comments={nowComments} />
+ <CommentList comments={displayComments} />📝 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.
| const { comments } = useComments(ticket.id, shouldRefetchComments); | |
| const nowComments = shouldRefetchComments ? comments : ticket.comments; | |
| const { comments: fetchedComments } = useComments(ticket.id, shouldRefetchComments); | |
| const displayComments = shouldRefetchComments ? fetchedComments : ticket.comments; | |
| // ... JSX에서 | |
| <CommentList comments={displayComments} /> |
🤖 Prompt for AI Agents
In src/components/TicketItem.tsx around lines 17 to 19, the current logic
switches the comments source based on shouldRefetchComments, which causes
confusion and lacks a way to revert to the original comments. Refactor this
logic to manage comments more explicitly, such as by maintaining a separate
state for fetched comments and clearly deciding when to use fetched versus
original comments, ensuring the component can switch back and forth cleanly.
| describe('TicketList', () => { | ||
| beforeEach(() => { | ||
| const responseBody: TicketListDto = { | ||
| tickets: [ | ||
| { | ||
| id: 'ticket-1', | ||
| title: 'Ticket #1', | ||
| description: 'Ticket Description', | ||
| status: 'open', | ||
| comments: [ | ||
| { | ||
| id: 'comment-1', | ||
| content: 'Comment Content', | ||
| }, | ||
| ], | ||
| } | ||
| ], | ||
| }; | ||
|
|
||
| nock(API_BASE_URL) | ||
| .get('/tickets') | ||
| .reply(200, responseBody); | ||
| }); |
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.
테스트 격리 문제: beforeEach에서 nock 정리 누락
각 테스트 후에 nock interceptor를 정리하지 않으면 테스트 간 간섭이 발생할 수 있습니다. 또한 QueryClient를 재사용하면 캐시된 데이터로 인해 테스트가 오염될 수 있습니다.
다음 수정을 적용하세요:
describe('TicketList', () => {
+ let queryClient: QueryClient;
+
beforeEach(() => {
+ nock.cleanAll();
+ queryClient = new QueryClient({
+ defaultOptions: {
+ queries: { retry: false },
+ mutations: { retry: false },
+ },
+ });
+
const responseBody: TicketListDto = {
// ... existing mock data
};
nock(API_BASE_URL)
.get('/tickets')
.reply(200, responseBody);
});
+ afterEach(() => {
+ nock.cleanAll();
+ queryClient.clear();
+ });Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/components/TicketList.test.tsx around lines 13 to 35, the beforeEach
block sets up a nock interceptor but does not clean it up after each test,
causing test interference. Also, reusing QueryClient instances can lead to
cached data affecting tests. Fix this by adding afterEach hooks to clean all
nock interceptors and create a fresh QueryClient instance for each test to
ensure proper test isolation and avoid state leakage.
| const comments = data || []; | ||
|
|
||
| return { comments }; |
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.
타입 불일치 및 데이터 구조 처리 오류
fetchComments는 CommentListDto를 반환하지만, 여기서는 data를 직접 배열처럼 사용하고 있습니다.
올바른 데이터 구조 접근을 위해 수정하세요:
- const comments = data || [];
+ const comments = data?.comments || [];
return { comments };📝 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.
| const comments = data || []; | |
| return { comments }; | |
| const comments = data?.comments || []; | |
| return { comments }; |
🤖 Prompt for AI Agents
In src/hooks/useComments.ts around lines 24 to 26, the code treats `data` as an
array directly, but `fetchComments` returns a `CommentListDto` object. To fix
this, access the actual comments array inside the `CommentListDto` (e.g.,
`data.comments`) instead of using `data` directly. Update the assignment to
extract the comments array properly before returning it.
| onError: (error, { ticketId }, context: any) => { | ||
| console.log('🚨 Error:', error); | ||
| queryClient.setQueryData(['comments', ticketId], context.previousComments); | ||
| }, |
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.
🛠️ Refactor suggestion
에러 핸들링에서 타입 안전성 개선 필요
컨텍스트가 any 타입으로 정의되어 있어 타입 안전성이 떨어집니다.
인터페이스를 정의하여 타입 안전성을 개선하세요:
+interface MutationContext {
+ previousComments: CommentListDto | undefined;
+}
+
- onError: (error, { ticketId }, context: any) => {
+ onError: (error, { ticketId }, context: MutationContext | undefined) => {
console.log('🚨 Error:', error);
- queryClient.setQueryData(['comments', ticketId], context.previousComments);
+ queryClient.setQueryData(['comments', ticketId], context?.previousComments);
},🤖 Prompt for AI Agents
In src/hooks/useCreateComment.ts around lines 24 to 27, the onError callback
uses context typed as any, which reduces type safety. Define an interface for
the context object specifying the expected properties, such as previousComments,
and update the context parameter type to this interface to improve type safety
and prevent potential runtime errors.
| const { mutate } = useMutation({ | ||
| mutationFn: createTicket, | ||
| onMutate: async ({ title, description }) => { | ||
| await queryClient.cancelQueries({ queryKey: ['tickets'] }); | ||
| const previousTickets = queryClient.getQueryData(['tickets']); | ||
| queryClient.setQueryData(['tickets'], (old: TicketListDto) => ({ | ||
| ...old, | ||
| tickets: [ | ||
| ...old?.tickets || [], | ||
| { | ||
| id: '', | ||
| title, | ||
| description, | ||
| status: 'open', | ||
| comments: [], | ||
| }, | ||
| ], | ||
| })); | ||
| return { previousTickets }; | ||
| }, | ||
| onError: (error, _variables, context: any) => { | ||
| console.log('🚨 Error:', error); | ||
| queryClient.setQueryData(['tickets'], context.previousTickets); | ||
| }, | ||
|
|
||
| onSettled: () => { | ||
| queryClient.invalidateQueries({ queryKey: ['tickets'] }); | ||
| }, | ||
| }); | ||
|
|
||
| return mutate; | ||
| } |
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.
API 함수와 훅 구현 간의 불일치를 수정해야 합니다.
createTicket API 함수가 데이터를 반환하지 않지만, 이 훅에서는 응답 데이터를 처리할 것으로 예상하는 구조입니다. 또한 몇 가지 타입 안전성 문제가 있습니다.
다음 수정사항을 적용하세요:
- onError: (error, _variables, context: any) => {
+ onError: (error, _variables, context) => {
console.log('🚨 Error:', error);
- queryClient.setQueryData(['tickets'], context.previousTickets);
+ queryClient.setQueryData(['tickets'], context?.previousTickets);
},그리고 src/api.ts의 createTicket 함수가 생성된 티켓 데이터를 반환하도록 수정하는 것을 권장합니다.
📝 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.
| const { mutate } = useMutation({ | |
| mutationFn: createTicket, | |
| onMutate: async ({ title, description }) => { | |
| await queryClient.cancelQueries({ queryKey: ['tickets'] }); | |
| const previousTickets = queryClient.getQueryData(['tickets']); | |
| queryClient.setQueryData(['tickets'], (old: TicketListDto) => ({ | |
| ...old, | |
| tickets: [ | |
| ...old?.tickets || [], | |
| { | |
| id: '', | |
| title, | |
| description, | |
| status: 'open', | |
| comments: [], | |
| }, | |
| ], | |
| })); | |
| return { previousTickets }; | |
| }, | |
| onError: (error, _variables, context: any) => { | |
| console.log('🚨 Error:', error); | |
| queryClient.setQueryData(['tickets'], context.previousTickets); | |
| }, | |
| onSettled: () => { | |
| queryClient.invalidateQueries({ queryKey: ['tickets'] }); | |
| }, | |
| }); | |
| return mutate; | |
| } | |
| const { mutate } = useMutation({ | |
| mutationFn: createTicket, | |
| onMutate: async ({ title, description }) => { | |
| await queryClient.cancelQueries({ queryKey: ['tickets'] }); | |
| const previousTickets = queryClient.getQueryData(['tickets']); | |
| queryClient.setQueryData(['tickets'], (old: TicketListDto) => ({ | |
| ...old, | |
| tickets: [ | |
| ...old?.tickets || [], | |
| { | |
| id: '', | |
| title, | |
| description, | |
| status: 'open', | |
| comments: [], | |
| }, | |
| ], | |
| })); | |
| return { previousTickets }; | |
| }, | |
| onError: (error, _variables, context) => { | |
| console.log('🚨 Error:', error); | |
| queryClient.setQueryData(['tickets'], context?.previousTickets); | |
| }, | |
| onSettled: () => { | |
| queryClient.invalidateQueries({ queryKey: ['tickets'] }); | |
| }, | |
| }); | |
| return mutate; | |
| } |
🤖 Prompt for AI Agents
In src/hooks/useCreateTicket.ts around lines 8 to 39, the hook expects the
createTicket API function to return data, but currently it does not, causing a
mismatch and type safety issues. Update the hook to handle the returned ticket
data properly by adjusting the mutationFn and related callbacks to use the
returned ticket object. Also, modify src/api.ts's createTicket function to
return the newly created ticket data so the hook can update the cache accurately
and maintain type safety.
| { | ||
| id: '', | ||
| title, | ||
| description, | ||
| status: 'open', | ||
| comments: [], | ||
| }, | ||
| ], |
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.
🛠️ Refactor suggestion
낙관적 업데이트에서 빈 ID 사용을 개선하세요.
새 티켓을 생성할 때 빈 문자열 ID를 사용하면, 컴포넌트에서 키 prop이나 식별자로 사용할 때 문제가 발생할 수 있습니다.
임시 ID를 생성하는 방식으로 개선하세요:
{
- id: '',
+ id: `temp-${Date.now()}`,
title,
description,
status: 'open',
comments: [],
},📝 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.
| { | |
| id: '', | |
| title, | |
| description, | |
| status: 'open', | |
| comments: [], | |
| }, | |
| ], | |
| { | |
| id: `temp-${Date.now()}`, | |
| title, | |
| description, | |
| status: 'open', | |
| comments: [], | |
| }, | |
| ], |
🤖 Prompt for AI Agents
In src/hooks/useCreateTicket.ts around lines 17 to 24, the new ticket is
assigned an empty string as its ID, which can cause issues with key props or
identifiers in components. Replace the empty string ID with a temporary unique
ID generated at runtime, such as using a UUID or a timestamp-based ID, to ensure
each new ticket has a distinct identifier during optimistic updates.
| onError: (_error, _variables, context) => { | ||
| queryClient.setQueryData(['tickets'], context?.previousTickets); | ||
| }, |
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.
🛠️ Refactor suggestion
에러 핸들링에서 타입 안전성 개선 필요
컨텍스트 타입이 명시적으로 정의되지 않아 런타임 에러 가능성이 있습니다.
타입을 더 안전하게 정의하세요:
+interface MutationContext {
+ previousTickets: TicketListDto | undefined;
+}
+
- onError: (_error, _variables, context) => {
+ onError: (_error, _variables, context: MutationContext | undefined) => {
queryClient.setQueryData(['tickets'], context?.previousTickets);
},📝 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.
| onError: (_error, _variables, context) => { | |
| queryClient.setQueryData(['tickets'], context?.previousTickets); | |
| }, | |
| // add this at the top of the file (or just above your hook definition) | |
| interface MutationContext { | |
| previousTickets: TicketListDto | undefined; | |
| } | |
| // … | |
| onError: (_error, _variables, context: MutationContext | undefined) => { | |
| queryClient.setQueryData(['tickets'], context?.previousTickets); | |
| }, |
🤖 Prompt for AI Agents
In src/hooks/useUpdateTicketStatus.ts around lines 23 to 25, the onError handler
uses context without an explicit type, risking runtime errors. Define a proper
TypeScript type for the context parameter that includes the previousTickets
property, and use this type annotation in the onError function to ensure type
safety when accessing context.previousTickets.
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: 1
🧹 Nitpick comments (2)
src/components/CommentList.test.tsx (1)
32-36: 빈 목록 테스트를 더 구체적으로 검증해주세요.리스트 요소의 존재만 확인하고 있습니다. 실제로 빈 상태인지 확인하기 위해 리스트 아이템의 개수도 검증하는 것이 좋겠습니다.
it('renders empty list when no comments', () => { renderCommentList([]); - screen.getByRole('list'); + const list = screen.getByRole('list'); + expect(list).toBeInTheDocument(); + expect(list.children).toHaveLength(0); });src/components/CommentForm.test.tsx (1)
48-62: 기본적인 테스트 케이스가 잘 구현되어 있습니다.사용자 입력과 API 호출을 적절히 테스트하고 있습니다.
추가로 고려해볼 수 있는 개선사항들:
onCommentAdded콜백이 호출되는지 검증- 폼 리셋 동작 확인
- 에러 시나리오 테스트
하지만 현재 구현도 핵심 기능을 충분히 검증하고 있습니다.
await waitFor(() => { expect(requestBody).toBeTruthy(); expect(requestBody.content).toBe('New Comment'); + expect(mockOnCommentAdded).toHaveBeenCalledTimes(1); });
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/components/CommentForm.test.tsx(1 hunks)src/components/CommentList.test.tsx(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/components/CommentList.test.tsx (2)
src/types.tsx (1)
Comment(1-4)src/components/CommentList.tsx (1)
CommentList(3-18)
src/components/CommentForm.test.tsx (3)
src/types.tsx (1)
Comment(1-4)src/api.ts (1)
API_BASE_URL(13-13)src/components/CommentForm.tsx (1)
CommentForm(8-36)
🔇 Additional comments (6)
src/components/CommentList.test.tsx (3)
1-7: LGTM! 임포트 구조가 적절합니다.필요한 테스트 라이브러리와 컴포넌트, 타입들이 올바르게 임포트되어 있습니다.
9-19: 테스트 데이터 구조가 올바릅니다.Comment 인터페이스와 일치하는 테스트 데이터가 잘 정의되어 있습니다. 문자열 ID 사용도 타입 정의와 일관성이 있습니다.
21-23: 헬퍼 함수 구현이 우수합니다.기본값을 제공하면서도 테스트별 커스터마이징이 가능한 유연한 구조입니다.
src/components/CommentForm.test.tsx (3)
1-15: 임포트와 설정이 적절합니다.필요한 모든 의존성이 올바르게 임포트되어 있고, API 모킹과 React Query 테스트를 위한 구성이 잘 되어 있습니다.
17-36: API 모킹 설정이 우수합니다.nock을 사용한 HTTP 요청 모킹이 적절하게 구성되어 있고, 요청 본문을 캡처하여 검증할 수 있도록 잘 설계되어 있습니다. beforeEach에서의 정리 작업도 테스트 격리를 보장합니다.
38-46: 헬퍼 함수 구현이 올바릅니다.각 테스트마다 새로운 QueryClient를 생성하여 테스트 격리를 보장하고, QueryClientProvider로 적절히 래핑하고 있습니다.
| it('renders comments', () => { | ||
| renderCommentList(); | ||
|
|
||
| screen.getByText(/First Comment/); | ||
| screen.getByText(/Second 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.
🛠️ Refactor suggestion
테스트 단언문을 명시적으로 작성해주세요.
getByText를 호출하지만 결과를 검증하지 않고 있습니다. 테스트의 의도를 명확히 하기 위해 expect 단언문을 추가하는 것이 좋겠습니다.
it('renders comments', () => {
renderCommentList();
- screen.getByText(/First Comment/);
- screen.getByText(/Second Comment/);
+ expect(screen.getByText(/First Comment/)).toBeInTheDocument();
+ expect(screen.getByText(/Second Comment/)).toBeInTheDocument();
});📝 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.
| it('renders comments', () => { | |
| renderCommentList(); | |
| screen.getByText(/First Comment/); | |
| screen.getByText(/Second Comment/); | |
| }); | |
| it('renders comments', () => { | |
| renderCommentList(); | |
| expect(screen.getByText(/First Comment/)).toBeInTheDocument(); | |
| expect(screen.getByText(/Second Comment/)).toBeInTheDocument(); | |
| }); |
🤖 Prompt for AI Agents
In src/components/CommentList.test.tsx around lines 25 to 30, the test calls
getByText but does not explicitly assert the results. To clarify the test
intent, wrap the getByText calls with expect assertions, such as
expect(screen.getByText(/First Comment/)).toBeInTheDocument(), to explicitly
verify that the comments are rendered.
Summary by CodeRabbit
새로운 기능
버그 수정
리팩터링
테스트
문서화
작업