Skip to content

Comments

feat: 약속 회고 API 레이어 구축 (#94)#95

Merged
mgYang53 merged 3 commits intodevelopfrom
feat/meeting-retro-api-94
Feb 23, 2026
Merged

feat: 약속 회고 API 레이어 구축 (#94)#95
mgYang53 merged 3 commits intodevelopfrom
feat/meeting-retro-api-94

Conversation

@mgYang53
Copy link
Contributor

@mgYang53 mgYang53 commented Feb 22, 2026

🚀 풀 리퀘스트 제안

📋 작업 내용

약속 회고 AI 요약 기능을 위한 API 레이어(타입, 엔드포인트, API 함수, React Query 훅)를 구현했습니다.
UI 작업(#93)에서 사용할 기반 코드입니다.

🔧 변경 사항

  • STT Job, 요약 조회/수정/발행 타입 정의 (retrospectives.types.ts)
  • 엔드포인트 상수 정의 (retrospectives.endpoints.ts)
  • API 함수 구현: createSttJob(FormData + AbortSignal), getSummary, updateSummary, publishSummary (retrospectives.api.ts)
  • React Query 훅 4종: useCreateSttJob(취소 지원), useSummary, useUpdateSummary, usePublishSummary
  • 쿼리 키 팩토리 및 배럴 export 구성

📸 스크린샷 (선택 사항)

N/A

📄 기타

관련 이슈: #94
UI 작업 이슈: #93 (이 PR에 의존)

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 회고 요약 생성(음성→텍스트 STT), 요약 조회, 수정 및 게시 기능을 앱에서 사용 가능
    • 요약 생성 요청 취소 지원 추가
  • Chores

    • 회고 관련 API, 엔드포인트 및 타입 정의 추가
    • React Query 기반 훅으로 캐시 일관성 유지 및 간편한 사용성 향상

STT Job 생성, 요약 조회/수정/발행 API 함수 및 React Query 훅 구현.
AbortController 기반 STT 요청 취소 지원, setQueryData 캐시 전략 적용.
@mgYang53 mgYang53 linked an issue Feb 22, 2026 that may be closed by this pull request
12 tasks
@mgYang53 mgYang53 self-assigned this Feb 22, 2026
@mgYang53 mgYang53 added the feat 새로운 기능 추가 label Feb 22, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 22, 2026

Warning

Rate limit exceeded

@mgYang53 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 12 minutes and 5 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

Walkthrough

Retrospectives 기능의 API 레이어와 React Query 기반 훅들을 추가합니다. 엔드포인트 빌더, 타입 정의, API 래퍼(createSttJob, getSummary, updateSummary, publishSummary), 쿼리 키 유틸, 훅(useCreateSttJob, useSummary, useUpdateSummary, usePublishSummary) 및 재내보내기를 포함합니다.

Changes

Cohort / File(s) Summary
Hooks 레이어
src/features/retrospectives/hooks/index.ts, src/features/retrospectives/hooks/retrospectiveQueryKeys.ts, src/features/retrospectives/hooks/useCreateSttJob.ts, src/features/retrospectives/hooks/usePublishSummary.ts, src/features/retrospectives/hooks/useSummary.ts, src/features/retrospectives/hooks/useUpdateSummary.ts
React Query 훅 5개 추가 및 쿼리 키 유틸 추가. useCreateSttJob에 AbortController 기반 취소(cancel) 기능 포함. 훅들은 성공 시 캐시를 retrospectiveQueryKeys로 업데이트함.
API 레이어
src/features/retrospectives/retrospectives.api.ts, src/features/retrospectives/retrospectives.endpoints.ts, src/features/retrospectives/retrospectives.types.ts
4개 API 래퍼 추가(createSttJob, getSummary, updateSummary, publishSummary)와 엔드포인트 빌더(RETROSPECTIVES_ENDPOINTS). STT는 FormData 전송 및 5분 타임아웃/신호 기반 취소 지원. 관련 타입(요청/응답) 다수 추가.
Feature 진입점
src/features/retrospectives/index.ts
hooks, API 함수, 타입들을 재내보내도록 업데이트(통합된 public surface 제공).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • haruyam15
  • choiyoungae
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Pull request title clearly and concisely describes the main change: implementing the API layer for the meeting retrospective AI summary feature.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/meeting-retro-api-94

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 렌더링 오류는 없습니다.

대안: useEffect cleanup에서 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: KeyPointKeyPointUpdateRequest가 구조적으로 동일 — 타입 중복

두 타입의 형태가 완전히 같습니다. 요청/응답 스키마가 앞으로도 동일하게 유지될 것이라면 KeyPointUpdateRequest를 별도로 정의하는 대신 KeyPoint를 재사용하는 것이 좋습니다.

♻️ 리팩토링 제안
-/** 주요 포인트 수정 요청 */
-export type KeyPointUpdateRequest = {
-  title: string
-  details: string[]
-}
+/** 주요 포인트 수정 요청 (KeyPoint와 동일 형태) */
+export type KeyPointUpdateRequest = KeyPoint

Also 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/publishSummaryapi.patch/post 헬퍼로 통일하기

getSummaryapi.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).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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로 변경
@mgYang53 mgYang53 merged commit 5ec4e8f into develop Feb 23, 2026
2 checks passed
@mgYang53 mgYang53 deleted the feat/meeting-retro-api-94 branch February 23, 2026 12:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 약속 회고 API 레이어 구축 (types, endpoints, api, hooks)

3 participants