Conversation
STT Job 생성, 요약 조회/수정/발행 API 함수 및 React Query 훅 구현. AbortController 기반 STT 요청 취소 지원, setQueryData 캐시 전략 적용.
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. WalkthroughRetrospectives 기능의 API 레이어와 React Query 기반 훅들을 추가합니다. 엔드포인트 빌더, 타입 정의, API 래퍼(createSttJob, getSummary, updateSummary, publishSummary), 쿼리 키 유틸, 훅(useCreateSttJob, useSummary, useUpdateSummary, usePublishSummary) 및 재내보내기를 포함합니다. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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: 6
🧹 Nitpick comments (5)
src/features/retrospectives/hooks/useUpdateSummary.ts (1)
19-23:setQueryData에 명시적 제네릭 타입 추가 권장현재는
response.data의 타입에서 암묵적으로 추론됩니다.setQueryData<RetrospectiveSummaryResponse>를 명시하면useSummary의 캐시 타입과의 일치를 컴파일 타임에 강제할 수 있습니다.♻️ 제안 리팩토링
- queryClient.setQueryData( + queryClient.setQueryData<RetrospectiveSummaryResponse>( retrospectiveQueryKeys.summary(variables.meetingId), response.data )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/retrospectives/hooks/useUpdateSummary.ts` around lines 19 - 23, Add an explicit generic to the cache update so the cached shape is enforced at compile time: in useUpdateSummary (the onSuccess handler that calls queryClient.setQueryData with retrospectiveQueryKeys.summary(variables.meetingId)), change the call to use setQueryData<RetrospectiveSummaryResponse>(...) so the data written matches the useSummary cache type and any mismatches are caught by the compiler.src/features/retrospectives/hooks/useCreateSttJob.ts (1)
16-45: 컴포넌트 언마운트 시 진행 중인 STT 요청이 자동 취소되지 않음문제: 컴포넌트가 언마운트되어도
abortControllerRef를 정리하는 로직이 없어 HTTP 요청이STT_TIMEOUT까지 계속 실행됩니다.영향: 불필요한 서버 부하 및 네트워크 낭비. 단, TanStack Query v5가 언마운트된 컴포넌트의 상태 업데이트는 처리하지 않으므로 React 렌더링 오류는 없습니다.
대안:
useEffectcleanup에서 abort를 호출합니다.♻️ 제안 리팩토링
+import { useCallback, useEffect, useRef } from 'react' -import { useCallback, useRef } from 'react' export const useCreateSttJob = () => { const abortControllerRef = useRef<AbortController | null>(null) + useEffect(() => { + return () => { + abortControllerRef.current?.abort() + abortControllerRef.current = null + } + }, []) const mutation = useMutation<...>({🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/retrospectives/hooks/useCreateSttJob.ts` around lines 16 - 45, The hook useCreateSttJob currently never aborts in-flight requests on unmount; add a useEffect cleanup that calls abortControllerRef.current?.abort() and sets abortControllerRef.current = null (and optionally calls reset() if you want mutation state cleared) so any ongoing createSttJob request is cancelled when the component unmounts; place this useEffect in the same file alongside abortControllerRef and cancel, and ensure useEffect is imported from React so the controller is cleaned up on unmount.src/features/retrospectives/hooks/retrospectiveQueryKeys.ts (1)
4-5:summaries함수명과'summary'키 세그먼트 불일치 — 선택적 정리
summaries()함수 이름은 복수형이지만 실제 키 값은'summary'(단수)입니다. 외부에서 직접 사용되는 건summary(meetingId)뿐이므로, 아래처럼 간소화하면 혼동을 없앨 수 있습니다.♻️ 제안 리팩토링
export const retrospectiveQueryKeys = { all: ['retrospectives'] as const, - - summaries: () => [...retrospectiveQueryKeys.all, 'summary'] as const, - summary: (meetingId: number) => [...retrospectiveQueryKeys.summaries(), meetingId] as const, + summaries: () => [...retrospectiveQueryKeys.all, 'summaries'] as const, + summary: (meetingId: number) => [...retrospectiveQueryKeys.all, 'summaries', meetingId] as const, }혹은
summaries를 아예 제거하고summary만 유지해도 됩니다(현재summaries가 내부 전용이라면).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/retrospectives/hooks/retrospectiveQueryKeys.ts` around lines 4 - 5, The key segment name is inconsistent: retrospectiveQueryKeys.summaries() returns a key segment using the literal 'summary' (singular) while the function name is plural; update for clarity by either renaming the literal to 'summaries' to match retrospectiveQueryKeys.summaries(), or remove the summaries() helper entirely and have retrospectiveQueryKeys.summary(meetingId) produce the full key directly; edit the functions retrospectiveQueryKeys.summaries and retrospectiveQueryKeys.summary so their names and returned key segments match (or drop the unused summaries helper).src/features/retrospectives/retrospectives.types.ts (1)
33-36:KeyPoint와KeyPointUpdateRequest가 구조적으로 동일 — 타입 중복두 타입의 형태가 완전히 같습니다. 요청/응답 스키마가 앞으로도 동일하게 유지될 것이라면
KeyPointUpdateRequest를 별도로 정의하는 대신KeyPoint를 재사용하는 것이 좋습니다.♻️ 리팩토링 제안
-/** 주요 포인트 수정 요청 */ -export type KeyPointUpdateRequest = { - title: string - details: string[] -} +/** 주요 포인트 수정 요청 (KeyPoint와 동일 형태) */ +export type KeyPointUpdateRequest = KeyPointAlso applies to: 59-62
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/retrospectives/retrospectives.types.ts` around lines 33 - 36, The KeyPoint and KeyPointUpdateRequest types are identical; remove the duplicate by reusing KeyPoint for update requests—either delete KeyPointUpdateRequest and export a type alias like "export type KeyPointUpdateRequest = KeyPoint" or have KeyPointUpdateRequest extend KeyPoint; update any usages referencing KeyPointUpdateRequest to use the alias or KeyPoint to avoid repeating the same shape (apply the same change for the second duplicate around the symbols mentioned at lines 59-62).src/features/retrospectives/retrospectives.api.ts (1)
59-91:updateSummary/publishSummary는api.patch/post헬퍼로 통일하기
getSummary는api.get을 사용하지만, 뮤테이션 함수들은apiClient.patch/post를 직접 호출합니다. 서버 응답이 동일하게ApiResponse<T>래퍼 구조이므로, 모든 API 함수에서 일관되게api헬퍼를 사용하면 반환 타입이 모두Promise<T>로 통일됩니다.수정 제안
-export const updateSummary = async (params: UpdateSummaryParams) => { - const { meetingId, data } = params - const response = await apiClient.patch<ApiResponse<RetrospectiveSummaryResponse>>( - RETROSPECTIVES_ENDPOINTS.SUMMARY(meetingId), - data - ) - return response.data -} +export const updateSummary = async ( + params: UpdateSummaryParams +): Promise<RetrospectiveSummaryResponse> => { + const { meetingId, data } = params + return api.patch<RetrospectiveSummaryResponse>( + RETROSPECTIVES_ENDPOINTS.SUMMARY(meetingId), + data + ) +} -export const publishSummary = async (params: PublishSummaryParams) => { - const response = await apiClient.post<ApiResponse<RetrospectiveSummaryResponse>>( - RETROSPECTIVES_ENDPOINTS.PUBLISH(params.meetingId) - ) - return response.data -} +export const publishSummary = async ( + params: PublishSummaryParams +): Promise<RetrospectiveSummaryResponse> => { + return api.post<RetrospectiveSummaryResponse>( + RETROSPECTIVES_ENDPOINTS.PUBLISH(params.meetingId) + ) +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/retrospectives/retrospectives.api.ts` around lines 59 - 91, The mutation helpers updateSummary and publishSummary currently call apiClient.patch/post and return response.data; change them to use the shared api helper (api.patch and api.post) like getSummary does so the functions return Promise<RetrospectiveSummaryResponse> directly; update updateSummary (which calls RETROSPECTIVES_ENDPOINTS.SUMMARY(meetingId)) and publishSummary (RETROSPECTIVES_ENDPOINTS.PUBLISH(params.meetingId)) to use api.patch/api.post and return the typed payload (RetrospectiveSummaryResponse) for consistent return types across the module.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/features/retrospectives/hooks/useCreateSttJob.ts`:
- Line 1: Prettier formatting in
src/features/retrospectives/hooks/useCreateSttJob.ts is failing CI; run the
formatter (e.g., run prettier --write on this file or your repo) to fix
whitespace/format changes around the import statements and any remaining
formatting issues in the useCreateSttJob hook so the import { useMutation } from
'@tanstack/react-query' and surrounding code conform to project Prettier rules.
- Around line 32-34: The current onSettled handler unconditionally clears
abortControllerRef.current causing a race where a later mutate() stores a new
controller then a prior mutation's onSettled wipes it; remove the onSettled
option and instead clear abortControllerRef.current inside the STT mutation
function (mutationFn) in its .finally() callback only if the ref still points to
that mutation's AbortController (i.e., check identity equality before nulling),
so cancel() + immediate mutate() won't erase a new controller; update references
to abortControllerRef, mutate, cancel, and mutationFn accordingly.
In `@src/features/retrospectives/hooks/usePublishSummary.ts`:
- Around line 18-24: In usePublishSummary: make the setQueryData call explicit
by adding the RetrospectiveSummaryResponse generic (i.e.,
queryClient.setQueryData<RetrospectiveSummaryResponse>(retrospectiveQueryKeys.summary(variables.meetingId),
response.data)) to match useUpdateSummary, then run prettier --write to fix
formatting.
In `@src/features/retrospectives/hooks/useUpdateSummary.ts`:
- Line 1: Run Prettier on the file to fix CI formatting: format the file
containing useUpdateSummary.ts (specifically the import line "import {
useMutation, useQueryClient } from '@tanstack/react-query'") using your
project's Prettier settings (e.g. run prettier --write or use the repo's
formatting script), ensure imports and spacing conform to the project's Prettier
config, then stage and commit the formatted file so the CI warning is resolved.
In `@src/features/retrospectives/retrospectives.api.ts`:
- Line 1: Prettier formatting has failed in retrospectives.api.ts; fix by
running Prettier to reformat the file (e.g., run prettier --write on
retrospectives.api.ts or your repo's configured Prettier command), review the
changed file (retrospectives.api.ts), stage and commit the formatted changes,
and push so CI can re-run with the corrected formatting.
In `@src/features/retrospectives/retrospectives.endpoints.ts`:
- Line 1: The file retrospectives.endpoints.ts has Prettier formatting issues;
run the project's formatter (e.g., execute prettier --write on the file or run
the repo's format script) to fix spacing/linebreaks and ensure imports like
"import { API_PATHS } from '@/api'" conform to the project's Prettier config;
commit the rewritten file so CI no longer flags the Prettier error.
---
Nitpick comments:
In `@src/features/retrospectives/hooks/retrospectiveQueryKeys.ts`:
- Around line 4-5: The key segment name is inconsistent:
retrospectiveQueryKeys.summaries() returns a key segment using the literal
'summary' (singular) while the function name is plural; update for clarity by
either renaming the literal to 'summaries' to match
retrospectiveQueryKeys.summaries(), or remove the summaries() helper entirely
and have retrospectiveQueryKeys.summary(meetingId) produce the full key
directly; edit the functions retrospectiveQueryKeys.summaries and
retrospectiveQueryKeys.summary so their names and returned key segments match
(or drop the unused summaries helper).
In `@src/features/retrospectives/hooks/useCreateSttJob.ts`:
- Around line 16-45: The hook useCreateSttJob currently never aborts in-flight
requests on unmount; add a useEffect cleanup that calls
abortControllerRef.current?.abort() and sets abortControllerRef.current = null
(and optionally calls reset() if you want mutation state cleared) so any ongoing
createSttJob request is cancelled when the component unmounts; place this
useEffect in the same file alongside abortControllerRef and cancel, and ensure
useEffect is imported from React so the controller is cleaned up on unmount.
In `@src/features/retrospectives/hooks/useUpdateSummary.ts`:
- Around line 19-23: Add an explicit generic to the cache update so the cached
shape is enforced at compile time: in useUpdateSummary (the onSuccess handler
that calls queryClient.setQueryData with
retrospectiveQueryKeys.summary(variables.meetingId)), change the call to use
setQueryData<RetrospectiveSummaryResponse>(...) so the data written matches the
useSummary cache type and any mismatches are caught by the compiler.
In `@src/features/retrospectives/retrospectives.api.ts`:
- Around line 59-91: The mutation helpers updateSummary and publishSummary
currently call apiClient.patch/post and return response.data; change them to use
the shared api helper (api.patch and api.post) like getSummary does so the
functions return Promise<RetrospectiveSummaryResponse> directly; update
updateSummary (which calls RETROSPECTIVES_ENDPOINTS.SUMMARY(meetingId)) and
publishSummary (RETROSPECTIVES_ENDPOINTS.PUBLISH(params.meetingId)) to use
api.patch/api.post and return the typed payload (RetrospectiveSummaryResponse)
for consistent return types across the module.
In `@src/features/retrospectives/retrospectives.types.ts`:
- Around line 33-36: The KeyPoint and KeyPointUpdateRequest types are identical;
remove the duplicate by reusing KeyPoint for update requests—either delete
KeyPointUpdateRequest and export a type alias like "export type
KeyPointUpdateRequest = KeyPoint" or have KeyPointUpdateRequest extend KeyPoint;
update any usages referencing KeyPointUpdateRequest to use the alias or KeyPoint
to avoid repeating the same shape (apply the same change for the second
duplicate around the symbols mentioned at lines 59-62).
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/features/retrospectives/hooks/useCreateSttJob.ts`:
- Line 13: JSDoc in the useCreateSttJob hook currently uses the ambiguous phrase
"동기 API"; update that comment in useCreateSttJob to explicitly state that the
mutation issues an HTTP request and waits for the server response (i.e., the
request blocks until response), so developers understand the hook's flow —
replace "동기 API" with a clear description such as "서버 응답을 기다리는 동기적(블로킹) HTTP
요청이므로 mutation이 pending인 동안 로딩 오버레이를 표시합니다" and keep the rest of the explanation
intact.
---
Duplicate comments:
In `@src/features/retrospectives/hooks/useCreateSttJob.ts`:
- Around line 28-31: The onSettled handler unconditionally clears
abortControllerRef.current causing a race where calling cancel() then mutate()
lets the new mutation's controller be wiped by the previous mutation's
onSettled; remove the onSettled cleanup and instead perform cleanup inside the
mutation function (mutationFn) in its .finally() by creating a local
AbortController, assigning it to abortControllerRef.current when starting, and
only nulling abortControllerRef.current if abortControllerRef.current ===
localController to ensure you don't clear a newer controller; keep cancel()
logic unchanged to call abort on the current controller.
In `@src/features/retrospectives/retrospectives.endpoints.ts`:
- Line 1: Prettier formatting issues remain in this file; run prettier --write
on the changed files (including the import line referencing API_PATHS) to
auto-format the code, stage the updated file, and commit the formatted changes
so CI Prettier warnings are resolved.
- api 함수 api.* 헬퍼로 통일 - useCreateSttJob race condition 수정 - useCreateSttJob 언마운트 시 자동 취소 - KeyPointUpdateRequest 타입 alias로 변경
* [refactor] 사전의견 폴더 병합 (#92) * refactor: 사전의견 관련 폴더 병합 (#91) * fix: SubPageHeader 그림자 prop 추가 (#91) * refactor: 코드 포매팅 (#91) * fix: 빌드 에러 수정 (#91) * fix: 개인 회고 nullable 필드 처리 및 목 모드 조기 반환 추가 (#91) * chore: 개인회고 관련 파일 삭제 (#91) * feat: 약속 회고 API 레이어 구축 (#94) (#95) * feat: 약속 회고 API 레이어 구축 (#94) STT Job 생성, 요약 조회/수정/발행 API 함수 및 React Query 훅 구현. AbortController 기반 STT 요청 취소 지원, setQueryData 캐시 전략 적용. * style: prettier 포맷 수정 (#94) * refactor: 코드 리뷰 반영 (#94) - api 함수 api.* 헬퍼로 통일 - useCreateSttJob race condition 수정 - useCreateSttJob 언마운트 시 자동 취소 - KeyPointUpdateRequest 타입 alias로 변경 * feat: 에러 코드 추가 및 사전 의견 버튼 disabled 처리 (#97) (#98) 신규 에러 코드 5개(E000, M016, M017, R106, R108) 추가. MyMeetingListItem에 preOpinionTemplateConfirmed 필드 추가 후, 메인페이지 예정 약속의 사전 의견 작성 버튼 disabled 처리 반영. * [feat] 약속회고 생성 전 플로우 UI 및 기능 개발 (#100) * feat: 약속 완료 시 약속 상세 페이지 UI 변경(#87) * feat: 약속 회고 상세 페이지 추가 (#87) * feat:아코디언 컴포넌트 추가(#87) * design: 아코디언 컴포넌트css 수정(#87) * feat: 수집된 사전의견 조회 api 개발(#87) * feat: Dropzone 공통컴포넌트 생성 및 파일 업로드 기능구현(#87) * design: AI요약 버튼 디자인 수정(#87) * style: 프리티어(#87) * refactor: 파일 업로드 검증 강화 및 의존성 정리 (#87) Dropzone 파일 타입 검증 기능 추가 및 radix-ui 의존성 정리로 코드 품질 개선. - Dropzone 파일 타입 검증 로직 추가 - 파일 타입 불일치 시 onTypeRejected 콜백 호출 - alert를 toast로 변경하여 일관된 에러 처리 - radix-ui 제거 후 @radix-ui/react-accordion 추가 - Accordion, AlertIcon 컴포넌트 코드 정리 - Import/export 경로 정리 및 불필요한 mock export 제거 * refactor: 에러처리 개선 (#87) * design: 아코디언css 수정 (#87) * refactor: GetCollectedAnswersParams 배럴 추가(#87) * style: 주석제거(#87) * refactor: AlertIcon 색 커스텀 기능 추가(#87) * refactor: 회고 아이콘을 SVG 파일로 전환 (#87) - MeetingRetroIcon, PersonalRetroIcon 컴포넌트를 SVG 파일로 대체 - useCollectedAnswers 훅의 유효성 검사 강화 (정수 체크, pageSize 검증) - MeetingDetailPage 에러 처리 중복 실행 방지 * refactor: AlertIcon적용 (#87) * refactor: AlertIcon size prop추가(#87) * refactor: 주제 목록 에러 처리 개선 (#87) * fix: 버튼 프로퍼티수정(#87) * feat: 회고 상태 기반 라우트 분기 처리 추가 (#87) * feat: 목데이터에 회고상태 추가(#87) * refactor: params검증로직 페이지에서 처리(#87) * [feat] 개인회고 작성 및 조회 화면 구현 (#101) * feat: 개인 회고 작성 화면 구현 (#90) * feat: 개인 회고 전송 기능 구현 (#90) * feat: 개인회고 조회 화면 구현 (#90) * feat: 개인회고 삭제, 수정 구현 (#90) * chore 미사용 유틸리티 제거 (#90) * refactor: 코드 포매팅 (#90) * fix: 코드리뷰 반영 (#90) * fix: 코드 리뷰 반영 (#90) * refactor: 코드 포매팅 (#90) * refactor: 개인회고 폴더 정리 (#90) * fix: 코드 리뷰 반영 (#90) * feat: 약속 회고 결과 페이지 UI 및 기능 개발 (#93) (#102) * feat: 약속 회고 결과 페이지 UI 및 기능 개발 (#93) - AI 요약 결과 조회, 수정, 발행 기능 구현 - TopicSummaryCard: 구조화된 개별 input으로 주요포인트 편집 - FormPageHeader: children, onBack prop 추가 - 발행 성공 시 DetailPage로 이동 - AiLoadingOverlay SVG 직접 import 방식으로 변경 - summary 관련 목데이터 추가 * fix: 코드 리뷰 반영 (#93) - TopicSummaryCard 편집 모드 keyPoints map key를 인덱스에서 stable id로 교체 (EditableKeyPoint 타입 추가, crypto.randomUUID()로 id 생성) - mock abort 에러를 DOMException에서 Error('canceled')로 변경해 axios 취소 동작 일치 - 회고 요약 목데이터를 전역 단일 객체에서 meetingId별 Record로 리팩터링 - MeetingRetrospectiveResultPage에 isError 처리 및 다시 시도 버튼 추가 - window.history.replaceState를 navigate(location.pathname, replace)로 교체 - FormPageHeader props를 discriminated union으로 타입 강화 * style: prettier 포맷 수정 (#93) * fix: 코드 리뷰 반영 2차 (#93) - 상세 항목(details)에도 EditableDetail 타입으로 stable id 적용 (추가/삭제 시 input 포커스 혼동 방지) - handleSaveEdit에서 EditableDetail[] → string[]으로 직렬화 처리 - mock STT abort: aborted 즉시 감지, once 옵션으로 리스너 중복 방지, resolve 시 리스너 정리 - meetingId 유효성 검사 강화 (Number.isInteger && > 0) * fix: 코드 리뷰 반영 3차 (#93) - guard를 핸들러 정의 이전으로 이동해 gatheringId/meetingId non-null assertion 제거 - topic prop을 항상 SummaryTopic[]으로 유지해 union 타입 불일치 해결 - EditableDetail/EditableKeyPoint를 components/index.ts에서 함께 re-export * [fix] 책 상세 페이지, 내 책장 페이지 API 스펙 반영 (#104) * refactor: 감상 기록 액션 메뉴 분리 (#59) * refactor: Book/Keywords API 목데이터와 엔드포인트 분리 (#59) * design: 감상 기록 액션 메뉴 관련 z-index 조정 (#59) * feat: 별점 필터링에 0점 추가 (#59) * refactor: 내 책장 무한스크롤 시 useInfiniteScroll 활용 (#59) * feat: 내 책장에 등록된 책이 없을 경우 툴팁 추가 (#59) * feat: 기록 타임라인의 사전의견에 답변이 없는 주제 처리 추가 (#59) * refactor: sticky한 헤더에 대해 drop-shadow-bottom 및 useScrollCollapse 활용 (#59) * fix: 책 타임라인 api 반영 (#59) * fix: 책 리스트 조회 api 반영 (#59) * fix: 책 리스트 삭제 api 반영 및 디자인 수정 (#59) * fix: 도서 API 스펙 변경에 따른 타입 및 컴포넌트 업데이트 (#59) * fix: 약속 회고 아이템에서 미연동 액션 메뉴 제거 (#59) * feat: 도서 읽기 상태 토글에 낙관적 업데이트 적용 (#59) * refactor: 코드 포매팅 (#59) * fix: 메인페이지 ReadingBooksSection API 파라미터 및 응답 필드 타입 오류 수정 (#59) * fix: 내책장 편집 모드 진입 시 filteredBookIds 초기화 (#59) * fix: mock API 파라미터 계약 및 라우트 상수 관련 수정 (#59) * fix: PRE_OPINION 아이템 React key 중복 가능성 수정 (#59) * refactor: RootLayout에서 불필요한 overflow-x-clip 제거 (#59) * refactor: BookLogList 삭제 로직을 useBookLogDeleteActions 훅으로 분리 (#59) * [feat] 약속회고 조회 및 댓글 기능 구현 (#103) * feat: 약속 회고상세 UI개발(#96) * refactor: 회고 관련 파일 구조 meeting 폴더로 재구성 (#96) * feat: 댓글 Textarea 높이조절 기능구현(#96) * feat: 약속회고 댓글 기능 구현 (#96) - 약속회고 상세 페이지에 댓글 작성/삭제 기능 추가. - 무한 스크롤로 댓글 목록 조회하며, 약속장 및 댓글 작성자만 삭제 가능. * fix: 약속회고 댓글 에러 처리 및 안정성 개선 (#96) - 댓글 조회 실패 시 에러 토스트 및 안내 메시지 표시. - currentUserId null 체크로 삭제 권한 확인 로직 개선. - 스켈레톤 count 음수값 방어 처리. - Textarea format별 height 기본값 적용. * fix: 약속회고 댓글/상세 조회 mock 처리 개선 (#96) * refactor: 약속회고 댓글 삭제 확인 모달 개선 및 코드 정리 (#96) - window.confirm을 글로벌 모달로 변경 - Mock 함수에서 불필요한 meetingId 파라미터 제거 - Textarea 컴포넌트에서 사용하지 않는 ref 제거. --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> * [feat] 페이지 접근 권한 전역 관리 구현 (#105) * feat: 페이지 접근 권한 전역 관리 구현 (#99) 인터셉터에서 권한 에러 감지 시 커스텀 DOM 이벤트를 dispatch하고, RootLayout의 usePermissionRedirect 훅에서 토스트와 홈 리다이렉트를 처리하여 한 곳에서 통합 관리. - errors.ts: PAGE_ACCESS_ERROR_CODES Set 추가 (6개 에러 코드) - interceptors.ts: permission-denied 커스텀 이벤트 dispatch - usePermissionRedirect: 이벤트 → 토스트 + navigate(HOME) - RootLayout: usePermissionRedirect 훅 연결 - MeetingRetrospectiveCreatePage: 이중 내비게이션 방지 * fix: 권한 에러 중복 이벤트 억제 및 핸들러 방어 코드 추가 (#99) * fix: 권한 에러 시 중복 토스트/모달 방지 (#99) * fix: 약속상세 페이지의 사전의견 버튼에 링크 연동(#106) (#107) --------- Co-authored-by: choiyoungae <109134495+choiyoungae@users.noreply.github.com> Co-authored-by: haeun <110523397+haruyam15@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
🚀 풀 리퀘스트 제안
📋 작업 내용
약속 회고 AI 요약 기능을 위한 API 레이어(타입, 엔드포인트, API 함수, React Query 훅)를 구현했습니다.
UI 작업(#93)에서 사용할 기반 코드입니다.
🔧 변경 사항
retrospectives.types.ts)retrospectives.endpoints.ts)retrospectives.api.ts)📸 스크린샷 (선택 사항)
N/A
📄 기타
관련 이슈: #94
UI 작업 이슈: #93 (이 PR에 의존)
Summary by CodeRabbit
릴리스 노트
New Features
Chores