Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAI 리뷰 생성 및 사용량 조회 API 클라이언트와 타입을 추가하고, MemoryAddButton과 AIWrite 컴포넌트의 AI 쓰기 흐름을 정적에서 API 기반 동적 흐름으로 전환합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant MemoryAddButton
participant API as API Client
participant Server
participant AIWrite
User->>MemoryAddButton: AI 쓰기 버튼 클릭
MemoryAddButton->>API: getAiUsage(roomId) [GET /rooms/{roomId}/users/ai-usage]
API->>Server: 요청
Server-->>API: AiUsageData
API-->>MemoryAddButton: 사용량 응답
alt recordCount < 2
MemoryAddButton->>User: 에러 스낵바 (최소 기록 필요)
else recordReviewCount >= 5
MemoryAddButton->>User: 에러 스낵바 (사용량 초과)
else 사용 가능
MemoryAddButton->>User: 확인 모달 (남은 사용 횟수 표시)
User->>MemoryAddButton: 확인
MemoryAddButton->>AIWrite: 네비게이션 (/aiwrite/{roomId})
AIWrite->>API: createAiReview(roomId) [POST /rooms/{roomId}/record/ai-review]
API->>Server: 요청
Server-->>API: CreateAiReviewData
API-->>AIWrite: aiContent
AIWrite->>User: AI 생성 콘텐츠 표시
end
alt API 오류
MemoryAddButton->>User: 에러 스낵바 (메시지)
AIWrite->>User: 에러 스낵바 및 리다이렉트
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro Disabled knowledge base sources:
📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/pages/aiwrite/AIWrite.tsx (1)
41-52: 에러 처리 타입 개선 제안현재 수동 타입 가드를 사용한 에러 처리가 작동하지만,
axios의AxiosError타입을 사용하면 더 간결하고 타입 안전하게 작성할 수 있습니다.다음과 같이 리팩토링할 수 있습니다:
+import { AxiosError } from 'axios';} catch (error) { console.error('AI 독서감상문 생성 실패:', error); let errorMessage = 'AI 독서감상문 생성에 실패했습니다'; - if (error && typeof error === 'object' && 'response' in error) { - const axiosError = error as { - response?: { - data?: { - message?: string; - }; - }; - }; - if (axiosError.response?.data?.message) { - errorMessage = axiosError.response.data.message; - } + if (error instanceof AxiosError && error.response?.data?.message) { + errorMessage = error.response.data.message; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
src/api/record/createAiReview.ts(1 hunks)src/api/record/getAiUsage.ts(1 hunks)src/api/record/index.ts(1 hunks)src/components/memory/MemoryAddButton/MemoryAddButton.tsx(3 hunks)src/pages/aiwrite/AIWrite.tsx(3 hunks)src/types/record.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/api/record/createAiReview.ts (2)
src/types/record.ts (2)
ApiResponse(88-93)CreateAiReviewData(76-79)src/api/index.ts (1)
apiClient(7-13)
src/api/record/getAiUsage.ts (2)
src/types/record.ts (2)
ApiResponse(88-93)AiUsageData(82-85)src/api/index.ts (1)
apiClient(7-13)
src/pages/aiwrite/AIWrite.tsx (2)
src/hooks/usePopupActions.ts (1)
usePopupActions(9-35)src/api/record/createAiReview.ts (1)
createAiReview(8-13)
src/components/memory/MemoryAddButton/MemoryAddButton.tsx (2)
src/hooks/usePopupActions.ts (1)
usePopupActions(9-35)src/api/record/getAiUsage.ts (1)
getAiUsage(8-13)
🔇 Additional comments (6)
src/types/record.ts (1)
75-85: 타입 정의가 명확합니다.AI 독서감상문 및 이용 횟수 조회를 위한 타입이 잘 정의되었습니다. 주석도 명확하여 이해하기 쉽습니다.
src/components/memory/MemoryAddButton/MemoryAddButton.tsx (1)
59-89: 검증 로직이 올바르게 구현되었습니다.API 호출 및 에러 처리, 사용자 검증 로직이 적절하게 구현되어 있습니다.
src/api/record/createAiReview.ts (1)
1-31: API 함수가 잘 구현되었습니다.표준 API 클라이언트 패턴을 따르고 있으며, 사용 예시 주석이 포함되어 있어 다른 개발자가 이해하기 쉽습니다.
src/api/record/getAiUsage.ts (1)
1-31: API 함수가 잘 구현되었습니다.표준 API 클라이언트 패턴을 따르고 있으며, 타입 안정성이 확보되어 있습니다.
src/pages/aiwrite/AIWrite.tsx (2)
19-68: useEffect 구현이 적절합니다.API 호출 및 에러 처리가 잘 구현되어 있습니다. eslint-disable 주석은 PR 목표에 명시된 무한 루프 버그 수정을 위해 의도적으로 추가된 것으로 보입니다.
navigate와openSnackbar는 훅에서 제공되는 안정적인 함수이므로 의존성 배열에서 제외해도 문제없습니다.
17-17: 동적 콘텐츠 로딩이 잘 구현되었습니다.API로부터 받은 AI 생성 콘텐츠를 상태로 관리하고 UI에 표시하는 로직이 적절하게 구현되어 있습니다.
Also applies to: 27-27, 85-85, 124-124
| // 모달 표시 (잔여 횟수 = 5 - 사용한 횟수) | ||
| openConfirm({ | ||
| title: 'AI 독서감상문 생성 (Beta)', | ||
| disc: `기록장에서 작성한 기록을 기반으로<br/>독서감상문을 생성하시겠어요?<br/>(서비스 내 잔여 이용횟수 : ${recordReviewCount}/5)`, | ||
| confirmText: '확인', | ||
| cancelText: '취소', | ||
| onConfirm: () => { | ||
| closePopup(); | ||
| navigate(`/aiwrite/${currentRoomId}`); | ||
| console.log('AI 독서 감상문 생성 시작 - roomId:', currentRoomId); | ||
| }, | ||
| }); |
There was a problem hiding this comment.
잔여 이용횟수 표시 로직 오류
PR 목표와 검증 로직(라인 81)을 보면 recordReviewCount는 "사용 횟수"(0~5)를 의미합니다. 그러나 라인 94에서 이 값을 "잔여 이용횟수"로 직접 표시하고 있어 잘못된 정보가 사용자에게 표시됩니다.
예를 들어, 사용자가 AI 독서감상문을 3번 사용했다면 recordReviewCount는 3이고, 잔여 횟수는 2회여야 하지만, 현재 코드는 "3/5"로 표시됩니다.
다음과 같이 수정하세요:
// 모달 표시 (잔여 횟수 = 5 - 사용한 횟수)
openConfirm({
title: 'AI 독서감상문 생성 (Beta)',
- disc: `기록장에서 작성한 기록을 기반으로<br/>독서감상문을 생성하시겠어요?<br/>(서비스 내 잔여 이용횟수 : ${recordReviewCount}/5)`,
+ disc: `기록장에서 작성한 기록을 기반으로<br/>독서감상문을 생성하시겠어요?<br/>(서비스 내 잔여 이용횟수 : ${5 - recordReviewCount}/5)`,
confirmText: '확인',
cancelText: '취소',
onConfirm: () => {추가로, src/types/record.ts의 라인 83 주석도 명확성을 위해 수정하는 것을 권장합니다:
- recordReviewCount: number; // AI 독서감상문 작성 가능 횟수
+ recordReviewCount: number; // AI 독서감상문 사용 횟수 (0~5)🤖 Prompt for AI Agents
In src/components/memory/MemoryAddButton/MemoryAddButton.tsx around lines 91 to
102, the UI displays recordReviewCount as the "remaining uses" but
recordReviewCount is actually the number of times used (0–5); change the
displayed value to remaining = Math.max(0, 5 - recordReviewCount) and inject
that remaining value into the disc string instead of recordReviewCount; ensure
the calculation caps at 0 so negative remaining never shows. Also update the
comment in src/types/record.ts at line 83 to clearly state that
recordReviewCount represents the number of times used, not remaining uses.
There was a problem hiding this comment.
@ljh130334 수정 확인했습니다! 사용 횟수로 통일하신 것이 명확하고 일관성 있는 접근이네요. 감사합니다! 👍
If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/pages/aiwrite/AIWrite.tsx (2)
67-68: 의존성 배열 누락에 대한 더 안전한 대안을 고려하세요.무한 루프를 수정하기 위해
navigate와openSnackbar를 의존성에서 제거한 것은 이해하지만,eslint-disable을 사용하는 것은 잠재적 위험을 내포합니다. 이들 함수가 안정적인 참조라고 가정하고 있지만, 향후 변경 시 버그가 발생할 수 있습니다.더 안전한 패턴으로 리팩토링을 고려하세요:
useEffect(() => { const fetchAiReview = async () => { if (!roomId) return; try { const result = await createAiReview(Number(roomId)); if (result.isSuccess) { setAiContent(result.data.content); } else { openSnackbar({ message: result.message, variant: 'top', isError: true, - onClose: () => {}, }); navigate(`/rooms/${roomId}/memory`); } } catch (error) { console.error('AI 독서감상문 생성 실패:', error); let errorMessage = 'AI 독서감상문 생성에 실패했습니다'; if (error && typeof error === 'object' && 'response' in error) { const axiosError = error as { response?: { data?: { message?: string; }; }; }; if (axiosError.response?.data?.message) { errorMessage = axiosError.response.data.message; } } openSnackbar({ message: errorMessage, variant: 'top', isError: true, - onClose: () => {}, }); navigate(`/rooms/${roomId}/memory`); } finally { setIsLoading(false); } }; fetchAiReview(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [roomId]); + }, [roomId, navigate, openSnackbar]);
navigate와openSnackbar는 실제로 안정적인 참조이므로 의존성에 포함해도 무한 루프가 발생하지 않아야 합니다. 만약 여전히 문제가 발생한다면,usePopupStore의 구현을 확인하여 메모이제이션이 적절히 되어 있는지 검토해야 합니다.
41-52: 에러 메시지 추출 로직을 유틸리티 함수로 추출하는 것을 권장합니다.수동으로 axios 에러를 파싱하는 현재 코드는 장황하고 타입 안정성이 낮습니다. 재사용 가능한 유틸리티 함수로 추출하면 코드베이스 전반에서 일관된 에러 처리가 가능합니다.
유틸리티 함수를 생성하여 사용하세요 (예:
src/utils/errorHandler.ts):// src/utils/errorHandler.ts import { AxiosError } from 'axios'; export const extractErrorMessage = (error: unknown, fallback: string): string => { if (error instanceof AxiosError) { return error.response?.data?.message ?? fallback; } return fallback; };그런 다음 이 함수를 사용하도록 리팩토링:
} catch (error) { console.error('AI 독서감상문 생성 실패:', error); - let errorMessage = 'AI 독서감상문 생성에 실패했습니다'; - - if (error && typeof error === 'object' && 'response' in error) { - const axiosError = error as { - response?: { - data?: { - message?: string; - }; - }; - }; - if (axiosError.response?.data?.message) { - errorMessage = axiosError.response.data.message; - } - } + const errorMessage = extractErrorMessage( + error, + 'AI 독서감상문 생성에 실패했습니다' + ); openSnackbar({ message: errorMessage,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/pages/aiwrite/AIWrite.tsx(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/aiwrite/AIWrite.tsx (2)
src/hooks/usePopupActions.ts (1)
usePopupActions(9-35)src/api/record/createAiReview.ts (1)
createAiReview(8-13)
🔇 Additional comments (6)
src/pages/aiwrite/AIWrite.tsx (6)
10-10: API 연동 및 상태 관리 초기화가 적절합니다.새로운 API 함수를 임포트하고 동적 콘텐츠를 위한 상태를 추가한 것이 올바릅니다.
Also applies to: 17-17
28-36: navigate 호출 후 잠재적인 상태 업데이트 경고를 확인하세요.에러 처리와 실패 케이스에서
navigate()를 호출하지만, 컴포넌트가 언마운트되기 전에 다른 비동기 작업이 여전히 진행 중일 수 있습니다. 현재 코드는 finally 블록에서setIsLoading(false)를 호출하는데, 이는 navigate 후에도 실행됩니다.개발 환경에서 다음을 확인해 주세요:
- API 에러 시나리오를 테스트하여 콘솔에 "Can't perform a React state update on an unmounted component" 경고가 나타나는지 확인
- 경고가 발생한다면, cleanup 로직이나 isMounted 플래그 추가를 고려
예시 개선안:
useEffect(() => { let isMounted = true; const fetchAiReview = async () => { if (!roomId) return; try { const result = await createAiReview(Number(roomId)); if (!isMounted) return; if (result.isSuccess) { setAiContent(result.data.content); } else { openSnackbar({ message: result.message, variant: 'top', isError: true, }); navigate(`/rooms/${roomId}/memory`); } } catch (error) { if (!isMounted) return; // ... error handling } finally { if (isMounted) { setIsLoading(false); } } }; fetchAiReview(); return () => { isMounted = false; }; }, [roomId]);Also applies to: 54-60
83-100: 클립보드 복사 로직이 올바르게 업데이트되었습니다.동적 AI 콘텐츠를 복사하도록 변경된 것이 적절하며, 에러 처리도 잘 구현되어 있습니다.
124-124: 콘텐츠 렌더링이 올바르게 구현되었습니다.동적으로 로드된 AI 콘텐츠를 표시하도록 정확하게 변경되었습니다.
135-138: 반응형 레이아웃 개선이 적절합니다.컨테이너에 최소/최대 너비 제약과 중앙 정렬을 추가하여 다양한 화면 크기에서 더 나은 사용자 경험을 제공합니다.
215-218: 복사 버튼 중앙 정렬이 올바르게 구현되었습니다.고정 위치 버튼을 화면 중앙에 배치하고 컨테이너의 최대 너비와 일치시켜 일관된 레이아웃을 유지합니다.
#️⃣ 연관된 이슈
#287
📝 작업 내용
AI 독서감상문 API 연동 완료
이번 PR에서는 AI 독서감상문 생성 기능과 관련된 2개의 API를 연동하였습니다.
1. API 연동
POST /rooms/{roomId}/record/ai-review)GET /rooms/{roomId}/users/ai-usage)recordCount: 사용자가 작성한 기록 개수recordReviewCount: AI 독서감상문 사용 횟수 (0~5)2. 사용자 검증 로직 구현
Case 1: 기록 개수 부족 (recordCount < 2)
Case 2: 이용 횟수 초과 (recordReviewCount >= 5)
Case 3: 정상 (recordCount >= 2 && recordReviewCount < 5)
(서비스 내 잔여 이용횟수 : {recordReviewCount}/5)3. 버그 수정
navigate,openSnackbar제거4. 파일 변경 사항
src/api/record/createAiReview.ts- AI 독서감상문 생성 APIsrc/api/record/getAiUsage.ts- AI 이용 횟수 조회 APIsrc/types/record.ts-CreateAiReviewData,AiUsageData타입 추가src/api/record/index.ts- 신규 API export 추가src/pages/aiwrite/AIWrite.tsx- API 연동 및 로딩/에러 처리src/components/memory/MemoryAddButton/MemoryAddButton.tsx- 검증 로직 구현Summary by CodeRabbit
릴리스 노트
새로운 기능
UI/동작 개선
버그 수정 / 오류 처리