Skip to content

[feature] 지원서의 정보를 수정하는 즉시 서버와 동기화된다.#670

Merged
lepitaaar merged 3 commits intodevelop-fefrom
feature/sync-applicant-detail
Aug 18, 2025
Merged

[feature] 지원서의 정보를 수정하는 즉시 서버와 동기화된다.#670
lepitaaar merged 3 commits intodevelop-fefrom
feature/sync-applicant-detail

Conversation

@lepitaaar
Copy link
Contributor

@lepitaaar lepitaaar commented Aug 18, 2025

#️⃣연관된 이슈

#669

📝작업 내용

react-query의 mutation을 이용해 업데이트 훅을 추가하여, 상태가변경될시 자동으로 refetch를 진행합니다. c8b0af4

debounce에 전달되는 콜백의 타입을 강제 캐스팅(as any)에 의존하지 않고 타입 가드 + 제네릭 시그니처로 보장하도록 리팩터링했습니다.
5403817

중점적으로 리뷰받고 싶은 부분(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

논의하고 싶은 부분(선택)

논의하고 싶은 부분이 있다면 작성해주세요.

🫡 참고사항

Summary by CodeRabbit

  • 신규 기능
    • 없음
  • 개선
    • 지원자 상세의 메모/상태 변경 흐름을 단순화하고 안정성 향상.
    • 상태의 기본값을 ‘제출됨’으로 명확히 설정해 혼란 감소.
  • 버그 수정
    • 변경 후 지원자 목록이 자동으로 최신화되어 오래된 정보 표시 문제 완화.
    • 잘못된 상태나 비문자 메모 입력을 차단해 업데이트 실패 감소.
  • 리팩터링
    • 업데이트 경로를 단일 서버 변경 흐름으로 통합하여 일관성 강화.

@lepitaaar lepitaaar self-assigned this Aug 18, 2025
@lepitaaar lepitaaar added 📬 API 서버 API 통신 작업 🔨 Refactor 코드 리팩토링 💻 FE Frontend 🛠Fix 기능이 의도한 대로 동작하지 않는 버그를 수정 labels Aug 18, 2025
@vercel
Copy link

vercel bot commented Aug 18, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
moadong Ready Ready Preview Comment Aug 18, 2025 1:04am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 18, 2025

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

새로운 React Query 변이 훅 useUpdateApplicant가 추가되었고, ApplicantDetailPage에서 메모/상태 업데이트가 이 훅을 사용하도록 리팩터링되었습니다. 로컬 컨텍스트 즉시 갱신 로직이 제거되고, 변이 성공 시 "clubApplicants" 쿼리 무효화가 수행되며 런타임 유효성 검사가 추가되었습니다.

Changes

Cohort / File(s) Summary
Applicants update hook
frontend/src/hooks/queries/applicants/useUpdateApplicant.ts
신규 훅 추가: useUpdateApplicant(clubId, appId). mutate({ memo, status }) 호출로 updateApplicantDetail 실행. 성공 시 "clubApplicants" 쿼리 무효화, 실패 시 콘솔 로깅.
Applicant detail integration
frontend/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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

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.

❤️ Share
🪧 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 @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in 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 ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere 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.

@PororoAndFriends PororoAndFriends requested review from PororoAndFriends and removed request for PororoAndFriends August 18, 2025 00:52
Copy link
Contributor

@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

🧹 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 유틸은 cancel API를 제공하지 않습니다. 언마운트 시 타이머 취소가 필요하다면:
    • 유틸에 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 2ee3852 and 5403817.

📒 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.ts
  • 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 (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가 강화되었습니다.

Copy link
Member

@Zepelown Zepelown left a comment

Choose a reason for hiding this comment

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

mutation은 처음 들어보네요 고생하셨습니다~

Copy link
Member

@seongwon030 seongwon030 left a comment

Choose a reason for hiding this comment

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

refresh 설정 좋습니다 !

Copy link
Contributor

@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: 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 5403817 and 40b5392.

📒 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] 형태로 범위를 좁히는 방안을 검토해 보시길 권장합니다.

@lepitaaar lepitaaar merged commit 59f8e1e into develop-fe Aug 18, 2025
5 checks passed
@lepitaaar lepitaaar deleted the feature/sync-applicant-detail branch September 20, 2025 01:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📬 API 서버 API 통신 작업 💻 FE Frontend 🛠Fix 기능이 의도한 대로 동작하지 않는 버그를 수정 🔨 Refactor 코드 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments