[feature] 지원서의 정보를 수정하는 즉시 서버와 동기화된다.#670
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | Summary |
|---|---|
Applicants update hookfrontend/src/hooks/queries/applicants/useUpdateApplicant.ts |
신규 훅 추가: useUpdateApplicant(clubId, appId). mutate({ memo, status }) 호출로 updateApplicantDetail 실행. 성공 시 "clubApplicants" 쿼리 무효화, 실패 시 콘솔 로깅. |
Applicant detail integrationfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx |
useUpdateApplicant 도입 및 업데이트 경로를 updateApplicant.mutate({ memo, status })로 전환. useAdminClubContext 반환값에서 setApplicantsData 제거. 기본 상태를 ApplicationStatus.SUBMITTED로 초기화. 메모/상태에 대한 런타임 타입 검사 추가. 로컬 컨텍스트 갱신 헬퍼 및 관련 호출 삭제. |
Sequence Diagram(s)
sequenceDiagram
participant UI as ApplicantDetailPage
participant Hook as useUpdateApplicant
participant API as updateApplicantDetail
participant RQ as React Query
UI->>Hook: updateApplicant.mutate({ memo, status })
Hook->>API: updateApplicantDetail(memo, status, clubId, appId)
API-->>Hook: success / error
alt success
Hook->>RQ: invalidateQueries("clubApplicants")
RQ-->>UI: refetch triggered
else error
Hook-->>UI: log error
end
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related issues
- [feature] MOA-169 지원서의 내용을 수정하면 서버와 즉시 동기화 된다 #669: 클라이언트 측에서 신청자 상세 편집을 서버와 즉시 동기화하는 훅 추가 목적과 일치.
Possibly related PRs
- [feature] 지원자의 상태와 메모를 변경한다. #651: 동일한
updateApplicantDetailAPI를 사용하며 훅 래핑 및 업데이트 흐름과 직접 연관. - [feature] 지원자 현황 디자인을 적용한다 #659: ApplicantDetailPage의 업데이트 흐름 변경 및 리팩터와 교차하는 변경점 존재.
- [feature] 동아리 관리자는 지원자를 확인할 수 있다. #612: AdminClubContext와 신청자 관리 초기 작업을 다루며 이번 PR의 컨텍스트 API 변경과 연관.
Suggested labels
✨ Feature
Suggested reviewers
- seongwon030
- oesnuj
- Zepelown
Tip
🔌 Remote MCP (Model Context Protocol) integration is now available!
Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.
✨ Finishing Touches
- 📝 Generate Docstrings
🧪 Generate unit tests
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
feature/sync-applicant-detail
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.
🪧 Tips
Chat
There are 3 ways to chat with CodeRabbit:
- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
I pushed a fix in commit <commit_id>, please review it.Open a follow-up GitHub issue for this discussion.
- Files and specific lines of code (under the "Files changed" tab): Tag
@coderabbitaiin a new review comment at the desired location with your query. - PR comments: Tag
@coderabbitaiin a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
Support
Need help? Create a ticket on our support page for assistance with any issues or questions.
CodeRabbit Commands (Invoked using PR/Issue comments)
Type @coderabbitai help to get the list of available commands.
Other keywords and placeholders
- Add
@coderabbitai ignoreanywhere in the PR description to prevent this PR from being reviewed. - Add
@coderabbitai summaryto generate the high-level summary at a specific location in the PR description. - Add
@coderabbitaianywhere in the PR title to generate the title automatically.
Status, Documentation and Community
- Visit our Status Page to check the current availability of CodeRabbit.
- Visit our Documentation for detailed information on how to use CodeRabbit.
- Join our Discord Community to get help, request features, and share feedback.
- Follow us on X/Twitter for updates and announcements.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (7)
frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (3)
14-16: 에러 로깅은 console.error로 통일에러는
console.error로 기록하는 편이 적절합니다. 추후 UI 토스트 연동도 고려하세요.다음과 같이 변경하세요:
- onError: (error) => { - console.log(`Error updating applicant detail: ${error}`); - } + onError: (error) => { + console.error('Error updating applicant detail:', error); + }
8-11: mutationKey 추가 및 payload 타입 분리로 가독성/디버깅 향상
mutationKey를 부여하면 DevTools/디버깅이 편리해집니다. 또한 payload 타입을 분리하면 재사용과 가독성이 좋아집니다.아래처럼 개선할 수 있습니다:
return useMutation({ + mutationKey: ['updateApplicant', clubId, appId], - mutationFn: ({memo, status}: { memo: string, status: ApplicationStatus }) => + mutationFn: ({ memo, status }: UpdateApplicantPayload) => updateApplicantDetail(memo, status, clubId, appId),파일 상단(혹은 본 훅 위)에 다음 타입을 추가하세요:
type UpdateApplicantPayload = { memo: string; status: ApplicationStatus; };
12-12: invalidateQueries에 clubId 추가해 쿼리 범위 좁히기
useGetApplicants.ts에서 이미['clubApplicants', clubId]를 사용 중이므로,useUpdateApplicant.ts의 무효화 키도 일관되게 수정하는 것을 권장합니다.
- 파일:
frontend/src/hooks/queries/applicants/useUpdateApplicant.ts
위치: 12번 라인- queryClient.invalidateQueries({ queryKey: ["clubApplicants"] }); + queryClient.invalidateQueries({ queryKey: ["clubApplicants", clubId] });frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (4)
47-47: 라우트 파라미터 명과 도메인 명칭 불일치(questionId ↔ applicantId)로 혼동 우려
useUpdateApplicant(clubId!, questionId!)에서questionId는 실제로 applicantId로 쓰이고 있습니다. 변수명/파라미터명을 일치시키면 가독성이 좋아집니다.다음처럼 alias를 사용해 혼동을 줄일 수 있습니다(참고 코드):
// 상단 근처 const { questionId: applicantId } = useParams<{ questionId: string }>(); // 훅 호출 const { mutate: updateApplicant } = useUpdateApplicant(clubId!, applicantId!);
72-73: 런타임 검증 간소화 가능
memo는 이벤트 핸들러에서 이미string입니다. 디바운스 유틸의 타입 제약 때문에 필요하다면 유지하되, 아니라면typeof memo !== 'string'체크는 제거해 단순화할 수 있습니다.다음처럼 정리:
- if (typeof memo !== 'string') return; if (!isApplicationStatus(status)) return;
64-66: 함수명 혼동 방지: debouncedUpdateApplicant로 명확화 권장파일 내에 API 함수명과 유사한
updateApplicantDetail(로컬, 디바운스 함수)이 있어 혼동될 수 있습니다. 디바운스 역할이 드러나게 이름을 바꾸면 가독성이 좋아집니다.아래처럼 변경하고, 하단 사용처도 함께 수정하세요:
- const updateApplicantDetail = useMemo( + const debouncedUpdateApplicant = useMemo( () => debounce((memo, status) => {사용처:
- updateApplicantDetail(newMemo, applicantStatus); + debouncedUpdateApplicant(newMemo, applicantStatus); - updateApplicantDetail(applicantMemo, newStatus); + debouncedUpdateApplicant(applicantMemo, newStatus);
66-83: 디바운스 지연시간 상수화 및 의존성 강화
- 매직 넘버(400ms)를
DEBOUNCE_MS상수로 분리하세요.updateApplicant를 의존성 배열에 추가해 클로저로 캡쳐된 오래된 함수를 방지하세요.- 현재
debounce유틸은cancelAPI를 제공하지 않습니다. 언마운트 시 타이머 취소가 필요하다면:
- 유틸에
cancel메서드를 추가하거나,lodash.debounce등 cancel을 지원하는 라이브러리로 교체 후useEffect클린업에서 호출하세요.- debounce((memo, status) => { … }, 400), - [clubId, questionId], + debounce((memo, status) => { … }, DEBOUNCE_MS), + [clubId, questionId, updateApplicant],// ApplicantDetailPage.tsx (AVAILABLE_STATUSES 아래 등) const DEBOUNCE_MS = 400;// (선택) debounce 유틸에 cancel 지원 추가 예시
function debounce<T extends (...args: unknown[]) => void>( fn: T, delay: number, ) { let timeoutId: ReturnType<typeof setTimeout>; const debounced = (...args: Parameters<T>) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn(...args), delay); }; debounced.cancel = () => clearTimeout(timeoutId); return debounced; } export default debounce;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
frontend/src/hooks/queries/applicants/useUpdateApplicant.ts(1 hunks)frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx(3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx}: Replace magic numbers with named constants for clarity.
Replace complex or nested ternary operators with if/else statements or IIFEs for readability.
Assign complex boolean conditions to named variables.
Use consistent return types for similar functions and hooks.
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle).
Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
Define constants near related logic or ensure names link them clearly.
Files:
frontend/src/hooks/queries/applicants/useUpdateApplicant.tsfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
frontend/**/*.tsx
📄 CodeRabbit Inference Engine (frontend/.cursorrules)
frontend/**/*.tsx: Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Separate significantly different conditional UI/logic into distinct components.
Colocate simple, localized logic or use inline definitions to reduce context switching.
Choose field-level or form-level cohesion based on form requirements.
Break down broad state management into smaller, focused hooks or contexts.
Use component composition instead of props drilling.
Files:
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
🧬 Code Graph Analysis (2)
frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (1)
frontend/src/apis/application/updateApplicantDetail.ts (1)
updateApplicantDetail(5-36)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (3)
frontend/src/context/AdminClubContext.tsx (1)
useAdminClubContext(30-37)frontend/src/hooks/queries/application/useGetApplication.ts (1)
useGetApplication(4-10)frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (1)
useUpdateApplicant(5-18)
🔇 Additional comments (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (1)
17-17: 새 훅 도입으로 API 호출 경로 일원화 — 좋습니다뷰에서 API 디테일을 숨기고 훅에 위임해 SRP가 강화되었습니다.
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (3)
42-44: 초기 상태값 불일치: SUBMITTED는 선택 옵션에 없어 초기 렌더 시 미선택 상태가 될 수 있습니다AVAILABLE_STATUSES에는 SUBMITTED가 포함되어 있지 않아, 초기 렌더 때 select의 value가 옵션과 매칭되지 않는 문제가 생길 수 있습니다(경고 또는 빈 선택 상태). 초기값을 실제 옵션 중 하나로 맞추는 편이 안전합니다.
다음과 같이 기본값을 첫 번째 옵션으로 맞추는 것을 제안합니다:
- const [applicantStatus, setApplicantStatus] = useState<ApplicationStatus>(ApplicationStatus.SUBMITTED); + const [applicantStatus, setApplicantStatus] = useState<ApplicationStatus>(AVAILABLE_STATUSES[0]);대안:
- placeholder 옵션을 두고 상태 타입을 ApplicationStatus | undefined로 두며, value={applicantStatus ?? ''} 형태로 처리.
67-69: isApplicationStatus는 공용 유틸로 추출하면 재사용성과 테스트 용이성이 올라갑니다해당 타입 가드는 여러 곳에서 유용합니다. 파일 내부/외부 유틸로 승격을 검토해보세요(예: src/utils/typeGuards.ts).
예시:
// src/utils/typeGuards.ts import { ApplicationStatus } from '@/types/applicants'; export const isApplicationStatus = (v: unknown): v is ApplicationStatus => typeof v === 'string' && (Object.values(ApplicationStatus) as string[]).includes(v as ApplicationStatus);그리고 본 파일에서는 import하여 사용.
63-82: 상수화 및 useMemo 의존성 배열 개선 제안
- 400ms 매직 넘버를 상수로 분리하세요.
파일 상단에 예:const DEBOUNCE_DELAY = 400 as const;useMemo의존성 배열에updateApplicant를 추가해 stale 클로저와eslint(exhaustive-deps)경고를 방지하세요.- 현재
frontend/src/utils/debounce.ts구현에는cancel메서드가 제공되지 않으므로, 언마운트 시 대기 중인 호출 취소 로직은 생략하거나, 필요하다면 debounce 유틸 자체에 cancel 기능을 추가한 뒤 다음과 같은useEffect를 도입하세요:useEffect(() => { return () => { updateApplicantDetail.cancel?.(); }; }, [updateApplicantDetail]);적용 예시 diff:
const updateApplicantDetail = useMemo( () => - debounce((memo, status) => { … }, 400), - [clubId, questionId], + debounce((memo, status) => { … }, DEBOUNCE_DELAY), + [clubId, questionId, updateApplicant], );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
frontend/src/hooks/queries/applicants/useUpdateApplicant.ts(1 hunks)frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/hooks/queries/applicants/useUpdateApplicant.ts
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx}: Replace magic numbers with named constants for clarity.
Replace complex or nested ternary operators with if/else statements or IIFEs for readability.
Assign complex boolean conditions to named variables.
Use consistent return types for similar functions and hooks.
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle).
Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
Define constants near related logic or ensure names link them clearly.
Files:
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
frontend/**/*.tsx
📄 CodeRabbit Inference Engine (frontend/.cursorrules)
frontend/**/*.tsx: Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Separate significantly different conditional UI/logic into distinct components.
Colocate simple, localized logic or use inline definitions to reduce context switching.
Choose field-level or form-level cohesion based on form requirements.
Break down broad state management into smaller, focused hooks or contexts.
Use component composition instead of props drilling.
Files:
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
🧬 Code Graph Analysis (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (3)
frontend/src/context/AdminClubContext.tsx (1)
useAdminClubContext(30-37)frontend/src/hooks/queries/application/useGetApplication.ts (1)
useGetApplication(4-10)frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (1)
useUpdateApplicant(5-18)
🔇 Additional comments (3)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (3)
16-16: useUpdateApplicant 도입과 연결이 명확합니다기존 API 호출을 훅으로 캡슐화하고 mutate만 노출한 점이 깔끔합니다. 서버 동기화 흐름이 분명해졌습니다.
71-73: 런타임 가드로 방어 코딩한 점 좋습니다입력 타입에 대한 방어 로직이 있어 예기치 않은 호출에서도 서버 변이를 막아 안전합니다.
46-46: non-null 단언 사용 검증 및 invalidateQueries 키 정확성 검토 필요
clubId!,questionId!가 항상 값이 있다는 전제가 루트(path) 설정 또는useParams에서 보장되는지 확인해 주세요.useUpdateApplicant훅의onSuccess에서 호출되는
queryClient.invalidateQueries({ queryKey: ["clubApplicants"] })
가 특정clubId에 대한 신청자 목록만 무효화하도록
queryKey: ["clubApplicants", clubId]형태로 범위를 좁히는 방안을 검토해 보시길 권장합니다.
#️⃣연관된 이슈
#669
📝작업 내용
react-query의 mutation을 이용해 업데이트 훅을 추가하여, 상태가변경될시 자동으로 refetch를 진행합니다. c8b0af4
debounce에 전달되는 콜백의 타입을 강제 캐스팅(as any)에 의존하지 않고 타입 가드 + 제네릭 시그니처로 보장하도록 리팩터링했습니다.
5403817
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit