Skip to content

Conversation

@seongje973
Copy link
Collaborator

@seongje973 seongje973 commented Oct 11, 2025

#️⃣연관된 이슈

ex) #이슈번호, #이슈번호
#781

📝작업 내용

이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지/동영상 첨부 가능)

  • API에 applicationFromId 부분 추가

  • 관리자용 api 에서 clubId 삭제

  • 클럽의 모든 지원서를 불러오는 api 추가

  • 수정된 API 에 맞게 hooks 파일들도 수정

  • ApplicationListTab에서 실제 지원서를 표시하도록 변경

image
  • ListTab에서 지원서 클릭시 해당 지원서의 ApplicationEditTab으로 연결

  • 더보기 버튼의 삭제하기 버튼 기능 구현 ( API가 구현이 안되서 일단은 해당부분 주석처리 )

  • 삭제하기 버튼 클릭시 confirm으로 한번 더 재확인

  • 지원자 현황 탭 클릭시 지원서 관리 탭과 동일한 페이지 뜨게 변경

  • 각 지원서 클릭시 ApplicantsTab으로 연결

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

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

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

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

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

🫡 참고사항

Summary by CodeRabbit

  • 새로운 기능

    • 신청 폼 목록을 API로 조회하는 관리자용 신청 폼 목록 UI 추가(학기/연도별 그룹화, 목록/상세 네비게이션) 및 관련 스타일/훅/API 추가
  • 개선 사항

    • 전반적인 신청·신청자 흐름을 applicationFormId 기반으로 통일(조회·뮤테이션·캐시 무효화 등)
    • 초기 폼 데이터와 타입에 semesterYear/semesterTerm/active 필드 추가
    • 목록·상세 화면에 로딩·오류 처리 및 빈 ID 가드 추가
  • 제한/보류

    • 일부 삭제 기능은 비활성화(주석 처리)되어 즉시 사용 불가; 라우팅 구조 일부 변경

@vercel
Copy link

vercel bot commented Oct 11, 2025

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

Project Deployment Preview Comments Updated (UTC)
moadong Ready Ready Preview Comment Nov 17, 2025 8:31am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 11, 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.
  • You can also validate your configuration using the online YAML validator.
  • 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

클럽 중심에서 applicationFormId 중심으로 API 엔드포인트·함수 시그니처·훅·타입 및 라우팅을 전환하고, 지원서 목록/편집 UI(리스트 컴포넌트·스타일)와 관련 API·훅(전체/활성 조회 등)을 추가·재구성했습니다.

Changes

Cohort / File(s) Summary
Applicant APIs & hooks
frontend/src/apis/applicants/deleteApplicants.ts, frontend/src/apis/applicants/getClubApplicants.ts, frontend/src/hooks/queries/applicants/useDeleteApplicants.ts, frontend/src/hooks/queries/applicants/useGetApplicants.ts, frontend/src/hooks/queries/applicants/useUpdateApplicant.ts
함수 시그니처 clubId → applicationFormId 변경, API 경로 및 요청 바디 업데이트, 쿼리 키/invalidateQueries 및 enabled 제어 반영
Application APIs
frontend/src/apis/application/createApplication.ts, frontend/src/apis/application/updateApplication.ts, frontend/src/apis/application/applyToClub.ts, frontend/src/apis/application/getAllApplications.ts, frontend/src/apis/application/getActiveApplications.ts, frontend/src/apis/application/deleteApplication.ts
create/update/apply 시그니처 및 엔드포인트 조정(클럽ID 제거 또는 applicationFormId 추가), 전체/활성 조회 API 추가, 삭제 API 스켈레톤 추가(주석)
Application hooks
frontend/src/hooks/queries/application/useGetApplication.ts, frontend/src/hooks/queries/application/useGetApplicationlist.ts, frontend/src/hooks/queries/application/useDeleteApplication.ts
useGetApplication 확장(clubId, applicationFormId), 전체 리스트 훅 추가, 삭제 훅 스켈레톤(주석) 추가
관리자 UI — ApplicationList / 스타일 / 메뉴
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/..., frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.styles.ts, frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx, frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicationMenu.tsx
ApplicationListTab 컴포넌트 및 styled-components 추가; API 기반 학기별 그룹화, More 메뉴(외부 클릭 닫기) 구현; 삭제 기능 비활성화/스켈레톤
관리자 UI — ApplicationEditTab 변경
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
create/update를 react-query mutation으로 전환; createApplication 시그니처 변경 반영; 폼 초기화 및 질문 ID 재정렬 로직 추가
Applicant pages & routing 변경
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx, frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx, frontend/src/App.tsx, frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
applicationFormId 기반 데이터 페칭·전역 동기화; ApplicantDetailPage에 가드 추가; 라우팅을 /admin/.../applicants-list/:applicationFormId/... 구조로 재구성 및 사이드바 경로 업데이트
타입·컨텍스트·초기값·모의데이터 확장
frontend/src/types/application.ts, frontend/src/types/applicants.ts, frontend/src/context/AdminClubContext.tsx, frontend/src/constants/INITIAL_FORM_DATA.ts, frontend/src/mocks/data/mockData.ts
ApplicationFormData에 semesterYear/semesterTerm/active 필드 추가; Applicant에 applicationFormId 필드 추가; INITIAL_FORM_DATA·mockData 확장; 컨텍스트 포맷 정리(포맷팅)
PrivateRoute 단순화
frontend/src/pages/AdminPage/auth/PrivateRoute/PrivateRoute.tsx
useGetApplicants 호출 및 applicants 데이터 세팅 주석 처리(설정자만 유지)

Sequence Diagram(s)

sequenceDiagram
    participant UI as Admin UI
    participant Q as react-query
    participant API as Backend API
    Note over UI,API: 지원서 목록 조회 (ApplicationListTab)
    UI->>Q: useGetApplicationlist()
    Q->>API: GET /api/club/application
    API-->>Q: 200 + application list
    Q-->>UI: 캐시된 데이터 제공 → 렌더(학기별 그룹화)
Loading
sequenceDiagram
    participant Edit as ApplicationEditTab
    participant Q as react-query
    participant API as Backend API
    Note over Edit,API: 생성/수정 흐름
    Edit->>API: POST /api/club/application (createApplication)
    API-->>Edit: 200 + data
    Edit->>Q: invalidateQueries(['applicationForm'])
    Edit->>API: PUT /api/club/application/{applicationFormId} (updateApplication)
    API-->>Edit: 200 + data
    Edit->>Q: invalidateQueries(['applicationForm', applicationFormId])
Loading
sequenceDiagram
    participant Applicants as ApplicantsTab / Detail
    participant Q as react-query
    participant API as Backend API
    Note over Applicants,API: 지원자 목록 및 상세 업데이트
    Applicants->>API: GET /api/club/apply/info/{applicationFormId}
    API-->>Applicants: applicants[]
    Applicants->>API: PUT /api/club/applicant/{applicationFormId} (updateApplicantDetail)
    API-->>Applicants: 200 OK
    Applicants->>Q: invalidateQueries(['clubApplicants', applicationFormId])
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • 집중 검토 항목:
    • API 경로·파라미터 변경 일관성(applicationFormId 사용 여부)
    • 배치 delete/update 페이로드와 백엔드 계약(DELETE 바디, PUT 리스트 형식)
    • react-query 쿼리 키·invalidateQueries·enabled 제어 일관성
    • ApplicationListTab의 그룹화 로직 및 외부 클릭 이벤트 메모리/정리
    • ApplicationEditTab의 생성 vs 업데이트 분기 및 질문 ID 재정렬
    • AdminClubContext를 통한 applicationFormId 전파 시점

Possibly related issues

  • MOA-285 — 다중 지원서 API와 연결한다: applicationFormId 중심 전환과 지원서 목록·조회 추가가 해당 목표와 직접 연관됩니다.
  • Moadong/moadong#496: 프론트에서 application form 관련 API·훅·페이지 변경을 요구하는 이슈로, 본 PR의 API/훅 추가·수정과 관련 있습니다.

Possibly related PRs

Suggested reviewers

  • Zepelown
  • seongwon030
  • oesnuj
  • suhyun113

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 '다중 지원서 API 연결'로 전체 변경 사항의 주요 목표를 명확하게 요약하고 있습니다. applicationFormId 추가, API 연결, hooks 수정 등 핵심 변경이 제목에 잘 반영되어 있습니다.
Linked Issues check ✅ Passed PR의 코드 변경 사항들이 MOA-285 이슈의 목표인 '다중 지원서 API 연결'을 충분히 구현하고 있습니다. applicationFormId 필드 추가, clubId 삭제, getAllApplications API 추가, hooks 수정, UI 업데이트가 모두 포함되어 있습니다.
Out of Scope Changes check ✅ Passed 대부분의 변경 사항이 다중 지원서 API 연결 범위 내에 있습니다. 다만 ApplicantDetailPage의 'applicants-lsit' 오타(오타로 보임), PrivateRoute의 주석 처리, 삭제 기능의 일부 미구현 부분이 있지만 전반적으로 범위 내 변경입니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#781-connectingmultipleapplicationsapi-MOA-285

📜 Recent 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 0d39d24 and e8d2c57.

📒 Files selected for processing (3)
  • frontend/src/constants/INITIAL_FORM_DATA.ts (2 hunks)
  • frontend/src/mocks/data/mockData.ts (1 hunks)
  • frontend/src/types/application.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/types/application.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
🔇 Additional comments (3)
frontend/src/mocks/data/mockData.ts (1)

124-126: LGTM!

목 데이터에 학기 정보와 활성 상태 필드를 추가한 것이 적절합니다. 하드코딩된 값들이 타입 정의와 일치하며 테스트 목적으로 사용하기에 적합합니다.

frontend/src/constants/INITIAL_FORM_DATA.ts (2)

39-41: 런타임 기반 초기화가 적절합니다.

현재 날짜를 기반으로 학기 정보를 동적으로 계산하는 방식이 이전 리뷰 피드백을 반영하여 개선되었습니다. active 필드는 타입 정의에 맞게 소문자로 설정되어 있습니다.


3-7: 학기 구분 로직이 비즈니스 요구사항과 일치하는지 확인해주세요.

현재 코드의 학기 판단 로직(currentMonth <= 6으로 1월~7월을 1학기로 판단)이 의도된 학기 기준과 일치하는지 검증이 필요합니다.

코드베이스에서 확인한 내용:

  • 현재 로직: 1월7월 = FIRST(1학기), 8월12월 = SECOND(2학기)
  • UI 표시: FIRST='1학기', SECOND='2학기'
  • 문서/주석: 학기 경계 기준에 대한 설명 없음

일반적인 한국 대학의 학기(봄학기 3월8월, 가을학기 9월2월)와 다르므로, 부경대학교나 모아동의 특별한 정책에 따른 것인지 확인 바랍니다. 의도가 일치한다면 코드에 주석으로 학기 기준을 명시하는 것이 좋습니다.


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.

@seongje973 seongje973 added ✨ Feature 기능 개발 📬 API 서버 API 통신 작업 💻 FE Frontend labels Oct 11, 2025
@seongje973 seongje973 requested a review from Zepelown October 11, 2025 12:01
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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (1)

56-69: mutationFn 인자 전달 불일치 - 래퍼 함수 필수

updateApplication은 3개의 개별 매개변수를 기대하지만, useMutation에서 mutationFn: updateApplication으로 직접 할당하면 객체 전체가 첫 번째 매개변수로만 전달되어 clubIdapplicationFormIdundefined가 됩니다. createMutation(라인 45-46)처럼 래퍼 함수를 사용하세요:

mutationFn: (payload) => updateApplication(payload.data, payload.clubId, payload.applicationFormId),
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 6827782 and b4b81a3.

📒 Files selected for processing (5)
  • frontend/src/hooks/queries/applicants/useDeleteApplicants.ts (1 hunks)
  • frontend/src/hooks/queries/applicants/useGetApplicants.ts (1 hunks)
  • frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (1 hunks)
  • frontend/src/hooks/queries/application/useGetApplication.ts (1 hunks)
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.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/application/useGetApplication.ts
  • frontend/src/hooks/queries/applicants/useGetApplicants.ts
  • frontend/src/hooks/queries/applicants/useDeleteApplicants.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
  • frontend/src/hooks/queries/applicants/useUpdateApplicant.ts
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/ApplicationEditTab/ApplicationEditTab.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-08-25T14:43:52.320Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 703
File: backend/src/main/java/moadong/club/controller/ClubApplyController.java:84-84
Timestamp: 2025-08-25T14:43:52.320Z
Learning: In the Moadong codebase, questionId and clubId are equivalent identifiers that represent the same entity. The ClubApplicationRepository.findAllByIdInAndQuestionId method correctly uses clubId as the questionId parameter for filtering club applications.

Applied to files:

  • frontend/src/hooks/queries/application/useGetApplication.ts
  • frontend/src/hooks/queries/applicants/useGetApplicants.ts
  • frontend/src/hooks/queries/applicants/useDeleteApplicants.ts
  • frontend/src/hooks/queries/applicants/useUpdateApplicant.ts
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Break down broad state management into smaller, focused hooks or contexts.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
🧬 Code graph analysis (2)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (4)
frontend/src/context/AdminClubContext.tsx (1)
  • useAdminClubContext (30-37)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
  • useGetApplication (4-11)
frontend/src/types/application.ts (1)
  • ApplicationFormData (49-53)
frontend/src/apis/application/createApplication.ts (1)
  • createApplication (5-31)
frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (2)
frontend/src/types/applicants.ts (1)
  • UpdateApplicantParams (26-30)
frontend/src/apis/application/updateApplicantDetail.ts (1)
  • updateApplicantDetail (5-32)
🪛 Biome (2.1.2)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx

[error] 26-26: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 29-29: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 31-31: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 33-33: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 45-45: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 57-57: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (1)

48-80: 잘못된 ID로 지원자 수정 요청 전송됨
Line 49에서 useUpdateApplicantclubId를 넘기고 있어 /api/club/applicant/${clubId}로 요청이 날아갑니다. 백엔드가 기대하는 값은 applicationFormId이므로 현재 구현은 지속적으로 404/실패를 낳습니다. applicationFormId를 인자로 사용하고, 값이 준비되기 전에는 뮤테이션을 호출하지 않도록 방어 로직을 추가해 주세요.

-  const { mutate: updateApplicant } = useUpdateApplicant(clubId!);
+  const { mutate: updateApplicant } = useUpdateApplicant(applicationFormId ?? '');
...
-        updateApplicant([
+        if (!applicationFormId) return;
+        updateApplicant([
...
-    [clubId, questionId],
+    [applicationFormId, questionId, updateApplicant],
♻️ Duplicate comments (1)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (1)

24-33: 훅 호출 순서 위반 계속 존재
Line 24의 if (!clubId) return null; 때문에 최초 렌더에서 clubId가 준비되지 않은 경우 이후 렌더보다 적은 훅이 호출되어 React 훅 규칙이 깨집니다. 이전 리뷰에서도 동일 지적이 있었으니, 훅을 항상 호출한 뒤 enabled 옵션이나 후속 분기에서 렌더링을 제어하는 방식으로 구조를 바꿔 주세요.

-  const { formId } = useParams<{ formId: string }>();
-  if (!clubId) return null;
-
-  const { data:existingFormData, isLoading, isError, error} = useGetApplication(clubId, formId ?? '');
+  const { formId } = useParams<{ formId: string }>();
+  const { data: existingFormData, isLoading, isError, error } = useGetApplication(clubId, formId);
+  if (!clubId) {
+    return <div>클럽 정보를 불러올 수 없습니다.</div>;
+  }
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between b4b81a3 and 492667b.

📒 Files selected for processing (13)
  • frontend/src/apis/applicants/deleteApplicants.ts (1 hunks)
  • frontend/src/apis/applicants/getClubApplicants.ts (1 hunks)
  • frontend/src/apis/application/applyToClub.ts (1 hunks)
  • frontend/src/apis/application/createApplication.ts (1 hunks)
  • frontend/src/apis/application/updateApplicantDetail.ts (1 hunks)
  • frontend/src/apis/application/updateApplication.ts (1 hunks)
  • frontend/src/hooks/queries/applicants/useDeleteApplicants.ts (1 hunks)
  • frontend/src/hooks/queries/applicants/useGetApplicants.ts (1 hunks)
  • frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (1 hunks)
  • frontend/src/hooks/queries/application/useGetApplication.ts (1 hunks)
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (1 hunks)
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (2 hunks)
  • frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • frontend/src/apis/application/updateApplication.ts
  • frontend/src/apis/application/applyToClub.ts
  • frontend/src/hooks/queries/applicants/useUpdateApplicant.ts
  • frontend/src/apis/applicants/deleteApplicants.ts
  • frontend/src/hooks/queries/application/useGetApplication.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/src/apis/applicants/getClubApplicants.ts
  • frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
  • frontend/src/apis/application/createApplication.ts
  • frontend/src/hooks/queries/applicants/useGetApplicants.ts
  • frontend/src/hooks/queries/applicants/useDeleteApplicants.ts
  • frontend/src/apis/application/updateApplicantDetail.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.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
  • frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
🧠 Learnings (9)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
  • frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
📚 Learning: 2025-08-25T14:43:52.320Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 703
File: backend/src/main/java/moadong/club/controller/ClubApplyController.java:84-84
Timestamp: 2025-08-25T14:43:52.320Z
Learning: In the Moadong codebase, questionId and clubId are equivalent identifiers that represent the same entity. The ClubApplicationRepository.findAllByIdInAndQuestionId method correctly uses clubId as the questionId parameter for filtering club applications.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
  • frontend/src/apis/applicants/getClubApplicants.ts
  • frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
  • frontend/src/hooks/queries/applicants/useGetApplicants.ts
  • frontend/src/hooks/queries/applicants/useDeleteApplicants.ts
📚 Learning: 2025-05-19T05:45:52.957Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 406
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:34-38
Timestamp: 2025-05-19T05:45:52.957Z
Learning: The code duplication between createClubApplication and editClubApplication methods in ClubApplyService.java is acknowledged but will be addressed in a future refactoring, as per the developer's plan.

Applied to files:

  • frontend/src/apis/application/createApplication.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Break down broad state management into smaller, focused hooks or contexts.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-09-30T05:26:41.788Z
Learnt from: alsdddk
Repo: Moadong/moadong PR: 765
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:431-435
Timestamp: 2025-09-30T05:26:41.788Z
Learning: In the Moadong codebase's club application feature (backend/src/main/java/moadong/club/), multiple ClubApplicationForm entities can have ACTIVE status for the same clubId, semesterYear, and semesterTerm simultaneously. There is no uniqueness constraint requiring only one ACTIVE form per semester.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-08-24T17:33:23.092Z
Learnt from: suhyun113
Repo: Moadong/moadong PR: 698
File: frontend/src/hooks/queries/applicants/useDeleteApplicants.ts:10-16
Timestamp: 2025-08-24T17:33:23.092Z
Learning: The codebase has inconsistent console logging patterns across files - mixing console.log/console.error for errors, using both Korean and English messages, and various formatting styles (template literals vs separate arguments, with/without emojis). A unified logging format like `console.error('[ModuleName] Operation failed:', error)` should be established for better debugging consistency.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
🧬 Code graph analysis (5)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (2)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
  • useGetApplication (4-11)
frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (1)
  • useUpdateApplicant (5-18)
frontend/src/apis/applicants/getClubApplicants.ts (1)
frontend/src/apis/auth/secureFetch.ts (1)
  • secureFetch (3-41)
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (2)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
  • useGetApplication (4-11)
frontend/src/apis/application/applyToClub.ts (1)
  • applyToClub (4-35)
frontend/src/apis/application/updateApplicantDetail.ts (1)
frontend/src/apis/auth/secureFetch.ts (1)
  • secureFetch (3-41)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (5)
frontend/src/context/AdminClubContext.tsx (1)
  • useAdminClubContext (30-37)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
  • useGetApplication (4-11)
frontend/src/types/application.ts (1)
  • ApplicationFormData (49-53)
frontend/src/apis/application/createApplication.ts (1)
  • createApplication (5-30)
frontend/src/apis/application/updateApplication.ts (1)
  • updateApplication (5-31)
🪛 Biome (2.1.2)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx

[error] 26-26: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 29-29: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 31-31: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 32-32: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 44-44: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 55-55: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

Comment on lines 6 to 10
queryKey: ['applicationForm', applicationFormId],
queryFn: () => getClubApplicants(applicationFormId!),
retry: false,
enabled: !!applicationFormId,
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

쿼리 키 불일치로 캐시 무효화 실패
Line 6에서 queryKey['applicationForm', applicationFormId]로 바꿨지만, useDeleteApplicants·useUpdateApplicant는 여전히 ['clubApplicants', applicationFormId]를 무효화 키로 사용합니다. 그 결과 삭제/수정 이후에도 목록이 갱신되지 않아 사용자가 오래된 데이터를 보게 됩니다. 기존 키와 맞추어 캐시가 정상적으로 비워지도록 수정해 주세요.

-    queryKey: ['applicationForm', applicationFormId],
+    queryKey: ['clubApplicants', applicationFormId],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
queryKey: ['applicationForm', applicationFormId],
queryFn: () => getClubApplicants(applicationFormId!),
retry: false,
enabled: !!applicationFormId,
})
queryKey: ['clubApplicants', applicationFormId],
queryFn: () => getClubApplicants(applicationFormId!),
retry: false,
enabled: !!applicationFormId,
})
🤖 Prompt for AI Agents
In frontend/src/hooks/queries/applicants/useGetApplicants.ts around lines 6 to
10, the queryKey was changed to ['applicationForm', applicationFormId] but other
hooks (useDeleteApplicants/useUpdateApplicant) still invalidate
['clubApplicants', applicationFormId], causing cache not to refresh; restore the
queryKey to ['clubApplicants', applicationFormId] (or alternatively update the
invalidate keys in the other hooks to the new key) so the cache invalidation
after delete/update targets the same key and the list refreshes correctly.

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: 4

🧹 Nitpick comments (4)
frontend/src/apis/application/getActiveApplications.ts (1)

9-12: 응답 오류 처리 개선 권장

response.ok가 false일 때 await response.json()을 호출하면, 응답 본문이 유효한 JSON이 아닐 경우 추가 예외가 발생할 수 있습니다.

다음과 같이 개선할 수 있습니다:

   if (!response.ok) {
     console.error(`Failed to fetch: ${response.statusText}`);
-    throw new Error((await response.json()).message);
+    const errorData = await response.json().catch(() => ({ message: response.statusText }));
+    throw new Error(errorData.message);
   }
frontend/src/apis/applicants/deleteApplicants.ts (1)

17-20: 응답 오류 처리 개선 권장

getActiveApplications.ts와 동일한 오류 처리 패턴 개선을 권장합니다. response.ok가 false일 때 JSON 파싱이 실패할 수 있습니다.

   if (!response.ok) {
     console.error(`Failed to fetch: ${response.statusText}`);
-    throw new Error((await response.json()).message);
+    const errorData = await response.json().catch(() => ({ message: response.statusText }));
+    throw new Error(errorData.message);
   }
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx (1)

37-44: 활성 지원서가 없는 경우 에러 처리 개선 권장

활성화된 지원서가 없을 때 예외를 발생시키지만, 이 에러 메시지가 사용자에게 직접 표시되지 않고 catch 블록에서 외부 링크로 폴백됩니다. 사용자 경험을 위해 더 명확한 메시지를 표시하는 것이 좋습니다.

     } else {
-      throw new Error('활성화된 지원서가 없습니다.');
+      alert('현재 활성화된 지원서가 없습니다. 외부 지원 링크를 확인해주세요.');
+      throw new Error('활성화된 지원서가 없습니다.');
     }
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (1)

86-95: 기존 에러 처리와 일관성 확인

Lines 86-95의 에러 처리 로직은 applicantsDataformData를 별도로 검사하지만, 새로 추가된 applicationFormId 체크는 위에서 제안한 대로 먼저 수행되어야 합니다. 이렇게 하면 에러 메시지가 더 구체적이고 일관성 있게 표시됩니다.

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 492667b and 0a8034f.

📒 Files selected for processing (16)
  • frontend/src/apis/applicants/deleteApplicants.ts (1 hunks)
  • frontend/src/apis/applicants/getClubApplicants.ts (1 hunks)
  • frontend/src/apis/application/applyToClub.ts (3 hunks)
  • frontend/src/apis/application/createApplication.ts (1 hunks)
  • frontend/src/apis/application/getActiveApplications.ts (1 hunks)
  • frontend/src/apis/application/getAllApplications.ts (1 hunks)
  • frontend/src/apis/application/getApplication.ts (1 hunks)
  • frontend/src/hooks/queries/applicants/useDeleteApplicants.ts (1 hunks)
  • frontend/src/hooks/queries/applicants/useGetApplicants.ts (1 hunks)
  • frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (1 hunks)
  • frontend/src/hooks/queries/application/useGetApplication.ts (1 hunks)
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx (3 hunks)
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (2 hunks)
  • frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (3 hunks)
  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx (2 hunks)
  • frontend/src/types/applicants.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • frontend/src/apis/application/applyToClub.ts
  • frontend/src/hooks/queries/applicants/useUpdateApplicant.ts
  • frontend/src/hooks/queries/applicants/useGetApplicants.ts
  • frontend/src/apis/application/getAllApplications.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/apis/application/getActiveApplications.ts
  • frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
  • frontend/src/apis/application/getApplication.ts
  • frontend/src/apis/applicants/getClubApplicants.ts
  • frontend/src/hooks/queries/applicants/useDeleteApplicants.ts
  • frontend/src/types/applicants.ts
  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
  • frontend/src/apis/applicants/deleteApplicants.ts
  • frontend/src/hooks/queries/application/useGetApplication.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
  • frontend/src/apis/application/createApplication.ts
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/ApplicationFormPage/ApplicationFormPage.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
🧠 Learnings (6)
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.

Applied to files:

  • frontend/src/apis/application/getActiveApplications.ts
  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
📚 Learning: 2025-09-30T05:26:41.788Z
Learnt from: alsdddk
Repo: Moadong/moadong PR: 765
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:431-435
Timestamp: 2025-09-30T05:26:41.788Z
Learning: In the Moadong codebase's club application feature (backend/src/main/java/moadong/club/), multiple ClubApplicationForm entities can have ACTIVE status for the same clubId, semesterYear, and semesterTerm simultaneously. There is no uniqueness constraint requiring only one ACTIVE form per semester.

Applied to files:

  • frontend/src/apis/application/getActiveApplications.ts
  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.

Applied to files:

  • frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
📚 Learning: 2025-08-25T14:43:52.320Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 703
File: backend/src/main/java/moadong/club/controller/ClubApplyController.java:84-84
Timestamp: 2025-08-25T14:43:52.320Z
Learning: In the Moadong codebase, questionId and clubId are equivalent identifiers that represent the same entity. The ClubApplicationRepository.findAllByIdInAndQuestionId method correctly uses clubId as the questionId parameter for filtering club applications.

Applied to files:

  • frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
  • frontend/src/apis/applicants/getClubApplicants.ts
  • frontend/src/hooks/queries/applicants/useDeleteApplicants.ts
  • frontend/src/hooks/queries/application/useGetApplication.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
  • frontend/src/apis/application/createApplication.ts
📚 Learning: 2025-05-19T05:45:52.957Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 406
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:34-38
Timestamp: 2025-05-19T05:45:52.957Z
Learning: The code duplication between createClubApplication and editClubApplication methods in ClubApplyService.java is acknowledged but will be addressed in a future refactoring, as per the developer's plan.

Applied to files:

  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
  • frontend/src/apis/application/createApplication.ts
📚 Learning: 2025-09-21T02:23:27.796Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 744
File: frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx:47-48
Timestamp: 2025-09-21T02:23:27.796Z
Learning: ClubApplyButton 컴포넌트에서 ShareButton은 항상 렌더링되어야 하므로 정적 import를 사용하는 것이 적절함. 동적 import는 불필요함.

Applied to files:

  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
🧬 Code graph analysis (5)
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (2)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
  • useGetApplication (4-14)
frontend/src/apis/application/applyToClub.ts (1)
  • applyToClub (4-34)
frontend/src/apis/applicants/getClubApplicants.ts (1)
frontend/src/apis/auth/secureFetch.ts (1)
  • secureFetch (3-41)
frontend/src/apis/applicants/deleteApplicants.ts (1)
frontend/src/apis/auth/secureFetch.ts (1)
  • secureFetch (3-41)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (2)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
  • useGetApplication (4-14)
frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (1)
  • useUpdateApplicant (5-20)
frontend/src/apis/application/createApplication.ts (2)
frontend/src/types/application.ts (1)
  • ApplicationFormData (49-53)
frontend/src/apis/auth/secureFetch.ts (1)
  • secureFetch (3-41)
🔇 Additional comments (7)
frontend/src/apis/application/getApplication.ts (1)

3-7: 변경 사항 확인 완료

API 엔드포인트가 applicationFormId를 포함하도록 올바르게 업데이트되었습니다.

frontend/src/hooks/queries/application/useGetApplication.ts (1)

4-13: 변경 사항 확인 완료

훅이 applicationFormId 파라미터를 포함하도록 올바르게 업데이트되었으며, enabled 조건이 두 파라미터를 모두 검사하여 타입 안전성을 보장합니다.

frontend/src/apis/applicants/deleteApplicants.ts (1)

4-16: API 구조 변경 확인 완료

clubId 기반에서 applicationFormId 기반으로 API 엔드포인트가 변경되었습니다. 이는 다중 지원서 API 연결이라는 PR 목표와 일치합니다.

frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (2)

21-21: 다중 지원서 파라미터 처리 확인 완료

clubIdformId를 모두 추출하고 사용하도록 올바르게 업데이트되었습니다. Guard clause로 두 파라미터의 존재를 확인하여 타입 안전성을 보장합니다.

Also applies to: 33-33, 40-40


42-44: 로컬 스토리지 키 변경 확인 완료

저장 키가 clubId 기반에서 formId 기반으로 변경되어 각 지원서별로 답변을 별도로 관리할 수 있게 되었습니다. 이는 다중 지원서 지원에 적합한 변경입니다.

frontend/src/hooks/queries/applicants/useDeleteApplicants.ts (1)

4-14: 훅 시그니처 업데이트 확인 완료

applicationFormId 기반으로 변경되었으며, 쿼리 무효화 키도 올바르게 업데이트되었습니다.

frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx (1)

36-44: API 응답 필드명 및 다중 활성 지원서 처리 방식 확인 필요

코드에서 명시적으로 표시된 대로 API 응답 구조에 대한 불명확함이 있습니다(라인 38-39). activeForms[0].id 사용 시 실제 응답 필드명이 id인지 확인이 필요합니다.

더불어, 학습된 정보에 따르면 동일한 clubId에 대해 여러 ACTIVE 상태의 지원서가 동시에 존재할 수 있습니다. 현재 코드는 항상 첫 번째 지원서([0])만 사용하는데, 이것이 의도된 동작인지 또는 사용자가 선택할 수 있어야 하는지 확인이 필요합니다.

다음을 확인해주세요:

  • 백엔드 /api/club/{clubId}/apply 엔드포인트의 응답 필드명 확인
  • 다중 활성 지원서 존재 시 예상되는 동작(첫 번째 선택 vs 사용자 선택 vs 기타)

Comment on lines +5 to +13
export const createApplication = async (data: ApplicationFormData) => {
try {
const response = await secureFetch(
`${API_BASE_URL}/api/club/${clubId}/application`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
const response = await secureFetch(`${API_BASE_URL}/api/club/application`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
);
body: JSON.stringify(data),
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

clubId 제거에 대한 검증이 필요합니다.

함수 시그니처에서 clubId 파라미터가 제거되었는데, 백엔드가 어떻게 어느 클럽에 대한 지원서인지 식별할 수 있는지 불명확합니다.

확인이 필요한 사항:

  1. ApplicationFormData 타입에 clubId 또는 applicationFormId 필드가 포함되어 있는지
  2. 백엔드가 인증 컨텍스트나 다른 방식으로 클럽 정보를 추출하는지
  3. 엔드포인트 경로 /api/club/application가 정확한지

다음 스크립트를 실행하여 ApplicationFormData 타입 정의와 이 함수의 호출 위치를 확인해주세요:


🏁 Script executed:

#!/bin/bash
# ApplicationFormData 타입 정의 확인
echo "=== ApplicationFormData 타입 정의 ==="
rg -A 10 "export interface ApplicationFormData" --type=ts

# createApplication 함수 호출 위치 확인
echo -e "\n=== createApplication 호출 위치 ==="
rg -n "createApplication\(" --type=ts -C 3

# 백엔드 엔드포인트 확인
echo -e "\n=== 백엔드 API 엔드포인트 확인 (있다면) ==="
fd -e java -e kt | xargs rg -l "/api/club/application" 2>/dev/null || echo "백엔드 파일을 찾을 수 없습니다"

Length of output: 1487



---

🏁 Script executed:

```shell
#!/bin/bash
# ApplicationEditTab.tsx 전체 구조 확인 - clubId 처리 방식 확인
echo "=== ApplicationEditTab.tsx 상단부 (props, import, context) ==="
head -100 frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx

# 라우트 정보 확인 - URL 파라미터가 있는지 확인
echo -e "\n=== useParams, useSearchParams 사용 ==="
rg -n "useParams|useSearchParams|useLocation" frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx -C 2

# createApplication 전체 함수 확인
echo -e "\n=== createApplication.ts 전체 구현 ==="
cat frontend/src/apis/application/createApplication.ts

# ApplicationEditTab 라우트 설정 확인
echo -e "\n=== 라우팅 설정에서 ApplicationEditTab 사용 ==="
rg -n "ApplicationEditTab" --type=ts --type=tsx -C 3

Length of output: 5030



---

🏁 Script executed:

```shell
#!/bin/bash
# secureFetch 구현 확인 - clubId가 어떻게 전달되는지 확인
echo "=== secureFetch 함수 구현 ==="
cat frontend/src/apis/auth/secureFetch.ts

# AdminClubContext 구현 확인 - clubId가 어디서 오는지
echo -e "\n=== AdminClubContext 구현 ==="
rg -A 30 "export.*useAdminClubContext" --type=ts --type=tsx

# updateApplication 함수 비교 - 어떻게 다르게 처리하는지 확인
echo -e "\n=== updateApplication 구현 (clubId 처리 비교) ==="
cat frontend/src/apis/application/updateApplication.ts

Length of output: 1968


createApplication 함수에서 clubId 정보가 완전히 손실되고 있습니다 - 심각한 버그입니다.

검증 결과:

  • ApplicationEditTab.tsx에서 clubId는 컨텍스트에서 추출됩니다 (22번 줄)
  • 하지만 createApplication 호출 시 clubId가 전혀 전달되지 않습니다 (45번 줄)
  • ApplicationFormData 타입에는 clubIdapplicationFormId 필드가 없습니다
  • 백엔드 엔드포인트는 /api/club/application (정적 경로, clubId 미포함)
  • secureFetch는 인증 토큰만 추가하고 clubId는 추가하지 않습니다

대조적으로, updateApplication.ts는:

  • applicationFormId를 함수 파라미터로 받습니다
  • 엔드포인트에 포함시킵니다: /api/club/application/${applicationFormId}

필수 수정 사항 (다음 중 하나 선택):

  1. ApplicationFormDataclubId 또는 applicationFormId 필드 추가
  2. createApplication 함수 시그니처에 clubId 파라미터 복원
  3. 엔드포인트를 /api/club/application/${clubId}로 변경

현재 상태에서는 백엔드가 어느 클럽에 속한 지원서인지 식별할 수 없습니다.

🤖 Prompt for AI Agents
In frontend/src/apis/application/createApplication.ts around lines 5-13, the
createApplication function drops the clubId so the backend can't associate the
submission with a club; restore clubId by updating the function signature to
accept clubId (e.g., createApplication(clubId, data)), ensure
ApplicationFormData remains unchanged, and include the clubId in the request
(either as part of the URL endpoint /api/club/application/${clubId} or as a
field in the sent payload consistent with backend expectations); update any
callers (e.g., ApplicationEditTab.tsx) to pass the clubId when invoking
createApplication.

answers: AnswerItem[];
memo: string;
createdAt: string;
applicationFormId: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

마이그레이션 중 필수 필드로 인한 잠재적 런타임 오류 위험

PR 설명에서 "API 사용하는 부분 고치는중"이라고 명시되어 있는데, applicationFormId를 필수 필드로 정의하면 다음과 같은 문제가 발생할 수 있습니다:

  1. 백엔드 API가 아직 모든 응답에서 이 필드를 반환하지 않는 경우
  2. 기존 코드에서 Applicant 객체를 생성할 때 이 필드를 포함하지 않은 경우
  3. 테스트/목 데이터에 이 필드가 누락된 경우

마이그레이션 기간 동안은 선택적 필드로 정의하는 것이 안전합니다:

-  applicationFormId: string;
+  applicationFormId?: string;

모든 API와 사용처가 업데이트된 후에 필수 필드로 변경할 수 있습니다.

다음 스크립트로 현재 Applicant 인터페이스 사용처와 API 응답을 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: Applicant 인터페이스 사용처 및 applicationFormId 필드 사용 확인

# Applicant 인터페이스를 사용하는 모든 파일 찾기
echo "=== Applicant 인터페이스 사용처 ==="
rg -n --type ts --type tsx "Applicant" -C 2

# applicationFormId 필드 참조 확인
echo "=== applicationFormId 필드 사용처 ==="
rg -n --type ts --type tsx "applicationFormId" -C 2

# API 응답 타입 정의 확인
echo "=== API 응답에서 Applicant 사용 ==="
rg -n --type ts --type tsx "getClubApplicants|deleteApplicants" -A 5

Length of output: 174


🏁 Script executed:

#!/bin/bash
# Description: 수정된 Applicant 인터페이스 사용처 및 applicationFormId 필드 사용 확인

# TypeScript 파일에서 Applicant 인터페이스 사용처 찾기
echo "=== Applicant 인터페이스 사용처 ==="
rg -n "Applicant" --type ts -C 2 | head -100

# applicationFormId 필드 참조 확인
echo ""
echo "=== applicationFormId 필드 사용처 ==="
rg -n "applicationFormId" --type ts -C 2

# API 응답 타입 정의 확인
echo ""
echo "=== API 응답 타입 및 Applicant 객체 생성 ==="
rg -n "getClubApplicants|deleteApplicants|new Applicant|Applicant\(" --type ts -A 3

# Mock 데이터에서 Applicant 사용 확인
echo ""
echo "=== Mock 데이터에서 Applicant 사용 ==="
fd -e ts -e tsx -path "*mock*" -o -path "*test*" | xargs rg -n "Applicant|applicationFormId" 2>/dev/null || echo "No mock files found"

# frontend/src/types 디렉토리의 파일 구조 확인
echo ""
echo "=== types 디렉토리 구조 ==="
fd . frontend/src/types --type f | head -20

Length of output: 47496


마이그레이션 기간 동안 필수 필드로 인한 타입 안전성 문제 확인됨

코드 분석 결과 원래의 우려가 타당한 것으로 확인되었습니다.

증거:

  1. ApplicantDetailPage.tsx 라인 46에서 이미 방어적 패턴 사용:

    • applicant?.applicationFormId (선택적 연쇄 연산자 사용)
    • 라인 52: !!applicationFormId (존재 확인)
    • 라인 53: applicationFormId! (널이 아님 단언)
  2. 이 방어적 코딩 패턴은 개발자들이 이미 이 필드가 항상 존재하지 않을 수 있음을 인식하고 있음을 나타냅니다.

  3. 타입 정의(필수)와 실제 사용(방어적)의 불일치는 타입 안전성 문제를 야기합니다.

권장사항:

마이그레이션 기간 동안 필드를 선택적으로 변경하세요:

-  applicationFormId: string;
+  applicationFormId?: string;

이렇게 하면:

  • 타입 정의가 실제 코드 동작과 일치
  • 불필요한 논-널 단언 제거 가능
  • 모든 API와 사용처 업데이트 후 필수 필드로 변경
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
applicationFormId: string;
applicationFormId?: string;
🤖 Prompt for AI Agents
In frontend/src/types/applicants.ts around line 24, the applicationFormId is
declared required but callers already treat it as optional; change the type to
optional (make applicationFormId?: string) so the type matches current defensive
usage during migration, then run type checks and update/remove any inappropriate
non-null assertions where callers now correctly guard for undefined; after all
API/users are updated, revert to required if appropriate.

@seongje973 seongje973 changed the title feat: 다중 지원서 API 연결 [feature] 다중 지원서 API 연결 Nov 4, 2025
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: 2

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 0a8034f and a363482.

📒 Files selected for processing (1)
  • frontend/src/pages/AdminPage/auth/PrivateRoute/PrivateRoute.tsx (1 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/pages/AdminPage/auth/PrivateRoute/PrivateRoute.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/auth/PrivateRoute/PrivateRoute.tsx
🧠 Learnings (1)
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.

Applied to files:

  • frontend/src/pages/AdminPage/auth/PrivateRoute/PrivateRoute.tsx
🧬 Code graph analysis (1)
frontend/src/pages/AdminPage/auth/PrivateRoute/PrivateRoute.tsx (1)
frontend/src/context/AdminClubContext.tsx (1)
  • useAdminClubContext (30-37)

import { useAdminClubContext } from '@/context/AdminClubContext';
import Spinner from '@/components/common/Spinner/Spinner';
import { useGetApplicants } from '@/hooks/queries/applicants/useGetApplicants';
// import { useGetApplicants } from '@/hooks/queries/applicants/useGetApplicants';
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

주석 처리된 코드를 제거하거나 마이그레이션을 완료하세요.

주석 처리된 import와 hook 호출이 남아있습니다. PR 설명에 "API 사용하는 부분 고치는중"이라고 명시되어 있어 작업이 진행 중인 것으로 보입니다. 다음 중 하나를 선택해주세요:

  1. 더 이상 필요하지 않다면 주석 처리된 코드를 완전히 제거
  2. 마이그레이션이 필요하다면 applicationFormId 기반의 새로운 API로 교체

코딩 가이드라인에 따르면 주석 처리된 코드는 제거되어야 합니다.

AdminClubContext에서 setApplicantsData를 제거해도 다른 컴포넌트에 영향이 없는지 확인하기 위해 다음 스크립트를 실행해주세요:

Also applies to: 10-12


🏁 Script executed:

#!/bin/bash
# Description: AdminClubContext의 setApplicantsData와 applicantsData 사용처 확인

# AdminClubContext에서 applicantsData 관련 정의 확인
echo "=== AdminClubContext 정의 확인 ==="
rg -n -A 5 -B 5 'applicantsData|setApplicantsData' frontend/src/context/AdminClubContext.tsx

# 전체 프론트엔드에서 applicantsData 사용처 확인
echo -e "\n=== applicantsData 사용처 확인 ==="
rg -n --type tsx --type ts -g '!node_modules' 'applicantsData' frontend/src

Length of output: 877


🏁 Script executed:

#!/bin/bash
# Description: Search for applicantsData and useAdminClubContext usage across frontend

# Fix: Use correct ripgrep syntax - use -t for type shorthand
echo "=== applicantsData 사용처 확인 ==="
rg -n 'applicantsData' frontend/src -t ts -t tsx

echo -e "\n=== useAdminClubContext 사용처 확인 ==="
rg -n 'useAdminClubContext' frontend/src -t ts -t tsx

# Check PrivateRoute file to see current state
echo -e "\n=== PrivateRoute.tsx 전체 내용 ==="
cat frontend/src/pages/AdminPage/auth/PrivateRoute/PrivateRoute.tsx

Length of output: 249


🏁 Script executed:

#!/bin/bash
# Description: Find files and verify repository structure

echo "=== PrivateRoute 파일 검색 ==="
fd -i 'privateroute' frontend/src

echo -e "\n=== AdminClubContext 파일 검색 ==="
fd -i 'adminclubcontext' frontend/src

echo -e "\n=== AdminPage 디렉토리 구조 ==="
fd -t d 'AdminPage' frontend/src | head -20

echo -e "\n=== applicantsData 사용처 (타입 지정 없음) ==="
rg 'applicantsData' frontend/src 2>&1 | head -20

echo -e "\n=== useAdminClubContext 사용처 ==="
rg 'useAdminClubContext' frontend/src 2>&1 | head -20

echo -e "\n=== 전체 프론트엔드 구조 ==="
fd --max-depth 3 -t d frontend/src | head -30

Length of output: 4976


주석 처리된 코드를 제거하거나 마이그레이션을 완료하세요. 현재 변경사항은 하위 컴포넌트를 손상시킵니다.

PrivateRoute에서 applicantsData 설정 로직을 제거했지만, ApplicantsTab.tsxApplicantDetailPage.tsx에서 여전히 context에서 applicantsData를 사용하고 있습니다:

  • ApplicantsTab.tsx: const { clubId, applicantsData } = useAdminClubContext(); (applicantsData 필수 사용)
  • ApplicantDetailPage.tsx: const { applicantsData, clubId } = useAdminClubContext(); (applicantsData 필수 사용)

현재 상태는 breaking change입니다:

  • PrivateRoute에서 더 이상 applicantsData를 설정하지 않으므로 null 상태 유지
  • ApplicantsTab과 ApplicantDetailPage는 null 데이터로 인해 오동작할 것

필수 사항:

  1. 대체 데이터 소스 제공: ApplicantsTab과 ApplicantDetailPage에 대한 applicationFormId 기반 API 호출 구현
  2. 또는 PrivateRoute에서 데이터 설정 로직을 applicationFormId 기반으로 수정 완료
  3. AdminClubContext에서 더 이상 필요없다면 applicantsData, setApplicantsData 제거
🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/auth/PrivateRoute/PrivateRoute.tsx around line
6, the commented-out import and removal of applicantsData setup causes a
breaking change because ApplicantsTab.tsx and ApplicantDetailPage.tsx still read
applicantsData from AdminClubContext; restore a working data flow by either (A)
reintroducing applicantsData population in PrivateRoute using applicationFormId
to fetch and set applicantsData into AdminClubContext before rendering children,
or (B) implement applicationFormId-based API calls inside ApplicantsTab and
ApplicantDetailPage and stop relying on context (then remove applicantsData and
setApplicantsData from AdminClubContext). Ensure the chosen approach fetches by
applicationFormId, handles loading/error states, and keeps the other components
updated accordingly.

…feature/#781-connectingmultipleapplicationsapi-MOA-285
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: 2

🧹 Nitpick comments (2)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (2)

25-25: 코드 스타일 개선 필요

구조 분해 할당에서 공백이 누락되었습니다.

다음과 같이 수정해주세요:

-  const { data:existingFormData, isLoading, isError, error} = useGetApplication(clubId ?? undefined, formId ?? '');
+  const { data: existingFormData, isLoading, isError, error } = useGetApplication(clubId ?? undefined, formId ?? '');

65-75: 로딩 UI 일관성 개선 제안

Early return이 모든 훅 호출 이후에 위치하여 이전 리뷰의 문제가 해결되었습니다. 다만 클럽 로딩 시에는 Spinner를 사용하고 지원서 로딩 시에는 텍스트 메시지를 사용하는 것이 일관성이 없습니다.

더 나은 사용자 경험을 위해 지원서 로딩 시에도 Spinner를 사용하는 것을 고려해보세요:

  if (isLoading) {
-    return <div>지원서 정보를 불러오는 중...</div>;
+    return <Spinner />;
  }
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between c51c82a and e47f5f3.

📒 Files selected for processing (1)
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (2 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-05-19T05:45:52.957Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 406
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:34-38
Timestamp: 2025-05-19T05:45:52.957Z
Learning: The code duplication between createClubApplication and editClubApplication methods in ClubApplyService.java is acknowledged but will be addressed in a future refactoring, as per the developer's plan.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-09-30T05:26:41.788Z
Learnt from: alsdddk
Repo: Moadong/moadong PR: 765
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:431-435
Timestamp: 2025-09-30T05:26:41.788Z
Learning: In the Moadong codebase's club application feature (backend/src/main/java/moadong/club/), multiple ClubApplicationForm entities can have ACTIVE status for the same clubId, semesterYear, and semesterTerm simultaneously. There is no uniqueness constraint requiring only one ACTIVE form per semester.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-08-24T17:33:23.092Z
Learnt from: suhyun113
Repo: Moadong/moadong PR: 698
File: frontend/src/hooks/queries/applicants/useDeleteApplicants.ts:10-16
Timestamp: 2025-08-24T17:33:23.092Z
Learning: The codebase has inconsistent console logging patterns across files - mixing console.log/console.error for errors, using both Korean and English messages, and various formatting styles (template literals vs separate arguments, with/without emojis). A unified logging format like `console.error('[ModuleName] Operation failed:', error)` should be established for better debugging consistency.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
🧬 Code graph analysis (1)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (5)
frontend/src/context/AdminClubContext.tsx (1)
  • useAdminClubContext (45-52)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
  • useGetApplication (4-14)
frontend/src/types/application.ts (2)
  • ApplicationFormData (49-56)
  • Question (5-14)
frontend/src/apis/application/createApplication.ts (1)
  • createApplication (5-25)
frontend/src/apis/application/updateApplication.ts (1)
  • updateApplication (5-31)
🔇 Additional comments (4)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (4)

12-18: LGTM!

새로운 mutation 기반 데이터 흐름을 위한 import들이 올바르게 추가되었습니다.


27-41: LGTM!

기존 지원서 데이터를 불러와 폼 상태를 초기화하는 로직이 올바르게 구현되었습니다. 질문 ID의 최댓값을 계산하여 nextId를 설정하는 부분도 적절합니다.


43-51: LGTM!

지원서 생성 mutation이 올바르게 구현되었습니다. 성공 시 쿼리 무효화, 사용자 피드백, 페이지 이동이 적절하게 처리되고 있습니다.


176-183: LGTM!

formId 존재 여부에 따라 생성/수정 mutation을 호출하는 로직이 명확하게 구현되었습니다.

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

♻️ Duplicate comments (1)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (1)

174-174: 타입 불일치 해결 필요 (재확인)

active 필드는 ApplicationFormData 타입 정의에 따라 string 타입이어야 하지만, 현재 false (boolean)를 fallback으로 사용하고 있습니다. 이는 런타임 타입 오류를 발생시킬 수 있습니다.

백엔드 API가 기대하는 정확한 문자열 값(예: 'ACTIVE', 'INACTIVE')으로 수정해주세요.

-      active: formData.active ?? false,
+      active: formData.active ?? 'INACTIVE',

또는 백엔드가 기대하는 정확한 기본값을 사용하세요.

🧹 Nitpick comments (2)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (2)

25-25: 코드 일관성 개선 권장

다음 마이너 이슈들을 개선하면 코드 가독성이 향상됩니다:

  • 구조 분해 할당 시 spacing 불일치 (data:existingFormDatadata: existingFormData)
  • Fallback 값 불일치 (clubId ?? undefined vs formId ?? ''): 두 매개변수 모두 동일한 타입(string | undefined)을 받으므로 통일된 fallback 사용 권장
-  const { data:existingFormData, isLoading, isError, error} = useGetApplication(clubId ?? undefined, formId ?? '');
+  const { data: existingFormData, isLoading, isError, error } = useGetApplication(clubId, formId);

70-72: 로딩 UI 일관성 개선 권장

클럽 데이터 로딩 시에는 <Spinner /> 컴포넌트를 사용하지만, 지원서 데이터 로딩 시에는 일반 텍스트만 표시합니다. 사용자 경험 향상을 위해 일관된 로딩 인디케이터 사용을 권장합니다.

  if (isLoading) {
-    return <div>지원서 정보를 불러오는 중...</div>;
+    return <Spinner />;
  }
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between e47f5f3 and 5efda18.

📒 Files selected for processing (1)
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (2 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-05-19T05:45:52.957Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 406
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:34-38
Timestamp: 2025-05-19T05:45:52.957Z
Learning: The code duplication between createClubApplication and editClubApplication methods in ClubApplyService.java is acknowledged but will be addressed in a future refactoring, as per the developer's plan.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-09-30T05:26:41.788Z
Learnt from: alsdddk
Repo: Moadong/moadong PR: 765
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:431-435
Timestamp: 2025-09-30T05:26:41.788Z
Learning: In the Moadong codebase's club application feature (backend/src/main/java/moadong/club/), multiple ClubApplicationForm entities can have ACTIVE status for the same clubId, semesterYear, and semesterTerm simultaneously. There is no uniqueness constraint requiring only one ACTIVE form per semester.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-08-24T17:33:23.092Z
Learnt from: suhyun113
Repo: Moadong/moadong PR: 698
File: frontend/src/hooks/queries/applicants/useDeleteApplicants.ts:10-16
Timestamp: 2025-08-24T17:33:23.092Z
Learning: The codebase has inconsistent console logging patterns across files - mixing console.log/console.error for errors, using both Korean and English messages, and various formatting styles (template literals vs separate arguments, with/without emojis). A unified logging format like `console.error('[ModuleName] Operation failed:', error)` should be established for better debugging consistency.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
🧬 Code graph analysis (1)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (5)
frontend/src/context/AdminClubContext.tsx (1)
  • useAdminClubContext (45-52)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
  • useGetApplication (4-14)
frontend/src/types/application.ts (2)
  • ApplicationFormData (49-56)
  • Question (5-14)
frontend/src/apis/application/createApplication.ts (1)
  • createApplication (5-25)
frontend/src/apis/application/updateApplication.ts (1)
  • updateApplication (5-31)
🔇 Additional comments (1)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (1)

43-63: Mutation 로직 잘 구현되었습니다!

생성 및 수정 mutation이 적절하게 구현되었습니다:

  • 쿼리 무효화 키가 정확하게 설정됨
  • 에러 핸들링 및 성공 알림 구현
  • 네비게이션 경로 일관성 유지

과거 리뷰에서 지적된 템플릿 리터럴 및 쿼리 키 이슈들이 모두 해결되었습니다.

Copy link
Contributor

@lepitaaar lepitaaar left a comment

Choose a reason for hiding this comment

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

오랜 작업완료 수고하셨습니다.. 궁금한게있어서 답변 부탁드리겠습니다~

Comment on lines 33 to 35
semesterYear: 2025,
semesterTerm: 'SECOND',
active: 'Unpublished',
Copy link
Contributor

Choose a reason for hiding this comment

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

active필드 uppercase와 semesterYear,semesterTerm 둘다 클라이언트 날짜를 기준으로 잡는게좋을꺼같습니다


useEffect(() => {
if (clubId) {
setIsClubLoading(isLoading);
Copy link
Contributor

Choose a reason for hiding this comment

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

isLoading을 추가하신 이유가무엇일까요?

setClubId(clubId);
}
}, [clubId, setClubId]);
}, [isLoading, clubId, setClubId, setIsClubLoading]);
Copy link
Contributor

Choose a reason for hiding this comment

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

의존성 배열에 함수를 넣은 이유가 궁금합니다

</Styled.DateHeader>
</Styled.ListHeader>
{applications.map((application) => (
{(group.forms.map((application: any) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

마찬가지로 any타입 정의부탁드립니다

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: 2

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 504d5da and 8688241.

📒 Files selected for processing (3)
  • frontend/src/apis/application/updateApplicantDetail.ts (1 hunks)
  • 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/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-05-19T05:45:52.957Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 406
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:34-38
Timestamp: 2025-05-19T05:45:52.957Z
Learning: The code duplication between createClubApplication and editClubApplication methods in ClubApplyService.java is acknowledged but will be addressed in a future refactoring, as per the developer's plan.

Applied to files:

  • frontend/src/apis/application/updateApplicantDetail.ts
🧬 Code graph analysis (2)
frontend/src/apis/application/updateApplicantDetail.ts (1)
frontend/src/apis/auth/secureFetch.ts (1)
  • secureFetch (3-41)
frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (2)
frontend/src/types/applicants.ts (1)
  • UpdateApplicantParams (27-31)
frontend/src/apis/application/updateApplicantDetail.ts (1)
  • updateApplicantDetail (5-31)

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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (1)

138-138: 일관성 없는 ID 체크를 수정하세요.

컴포넌트가 이제 applicationFormId 기반으로 동작하는데 여전히 clubId를 체크하고 있습니다. 이는 논리적 불일치를 야기합니다.

다음과 같이 수정하세요:

-  if (!clubId) return null;
+  if (!applicationFormId) return null;
♻️ Duplicate comments (5)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx (1)

107-150: 타입 안전성이 개선되었습니다.

SemesterGroupApplicationFormItem 타입을 명시적으로 사용하여 이전 리뷰에서 지적된 any 타입 문제가 해결되었습니다.

frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx (4)

107-150: 타입 안전성이 개선되었습니다.

SemesterGroupApplicationFormItem 타입을 명시적으로 사용하여 이전 리뷰에서 지적된 any 타입 문제가 해결되었습니다.


72-73: 에러 객체의 안전한 접근을 보장하세요.

ApplicantsListTab.tsx와 동일한 문제입니다. errorundefined일 수 있는데 error.message에 직접 접근하고 있습니다.

  if (isError) {
-    return <div>오류가 발생했습니다: {error.message}</div>;
+    return <div>지원서 목록을 불러오는데 실패했습니다. 다시 시도해주세요.</div>;
  }

28-39: 삭제 기능 구현을 완료하거나 코드를 제거하세요.

ApplicantsListTab.tsx와 동일한 문제입니다. 삭제 핸들러가 주석 처리되어 있어 ApplicationMenu의 삭제 옵션이 작동하지 않습니다.

ApplicantsListTab.tsx에 대한 리뷰 코멘트(lines 28-39)를 참고하여 동일하게 적용하세요.

Based on learnings


78-97: 중복된 formatDateTime 함수를 제거하세요.

이 함수는 ApplicantsListTab.tsx에도 동일하게 구현되어 있습니다. 코드 중복은 유지보수성을 해치고 버그 발생 위험을 높입니다.

ApplicantsListTab.tsx의 리뷰 코멘트(lines 78-97)를 참고하여 공통 유틸 함수로 분리하세요.

  1. frontend/src/utils/dateFormatter.ts에 함수 정의
  2. 두 파일 모두에서 import하여 사용:
+import { formatDateTime } from '@/utils/dateFormatter';
+
 const ApplicationListTab = () => {
   // ...
-  const formatDateTime = (dateTimeString: string) => {
-    // ... 구현 코드 제거
-  };

Based on learnings

🧹 Nitpick comments (3)
frontend/src/apis/application/updateApplicantDetail.ts (1)

9-11: 가드 절이 추가되어 이전 리뷰 우려사항이 해결되었습니다.

applicationFormIdundefined일 때 잘못된 엔드포인트 호출을 방지하는 검증 로직이 잘 구현되었습니다. 다만, if 키워드 뒤에 공백을 추가하면 코드 스타일이 더 일관적입니다.

선택적 개선안:

-  if(!applicationFormId) {
+  if (!applicationFormId) {
     throw new Error('applicationFormId가 존재하지 않아 지원자 정보를 수정할 수 없습니다.');
   }
frontend/src/types/application.ts (1)

63-66: 중복된 인터페이스를 통합하세요.

ApplicationForm 인터페이스(lines 63-66)와 ApplicationFormItem 인터페이스(lines 68-73)가 유사한 목적을 가지고 있습니다. ApplicationFormItem이 더 완전한 형태이므로, ApplicationForm을 제거하거나 용도를 명확히 구분하세요.

용도가 동일하다면 하나로 통합:

-export interface ApplicationForm {
-  id: string;
-  title: string;
-}
-
 export interface ApplicationFormItem {
   id: string;
   title: string;
   editedAt: string;
   status: 'active' | 'published' | 'unpublished';
 }
+
+// 간단한 참조가 필요한 경우 Pick 유틸리티 사용
+export type ApplicationFormRef = Pick<ApplicationFormItem, 'id' | 'title'>;

용도가 다르다면 명확한 네이밍으로 구분:

export interface ApplicationFormSummary {  // 또는 ApplicationFormBasic
  id: string;
  title: string;
}

export interface ApplicationFormDetail {  // 또는 ApplicationFormListItem
  id: string;
  title: string;
  editedAt: string;
  status: 'active' | 'published' | 'unpublished';
}
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (1)

225-227: 중복 제출 방지를 위한 버튼 비활성화 권장

현재 저장 버튼이 mutation 진행 중에도 활성화 상태를 유지하여 사용자가 여러 번 클릭할 수 있습니다. isCreating 또는 isUpdating 상태를 활용해 제출 중에는 버튼을 비활성화하면 사용자 경험이 개선됩니다.

다음과 같이 수정을 고려해보세요:

        <Styled.ButtonWrapper>
-          <Styled.submitButton onClick={handleSubmit}>
+          <Styled.submitButton 
+            onClick={handleSubmit}
+            disabled={isCreating || isUpdating}
+          >
-            저장하기
+            {(isCreating || isUpdating) ? '저장 중...' : '저장하기'}
          </Styled.submitButton>
        </Styled.ButtonWrapper>
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 8688241 and 0d39d24.

📒 Files selected for processing (9)
  • frontend/src/apis/application/updateApplicantDetail.ts (1 hunks)
  • frontend/src/constants/INITIAL_FORM_DATA.ts (2 hunks)
  • frontend/src/hooks/queries/applicants/useGetApplicants.ts (1 hunks)
  • frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (1 hunks)
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx (1 hunks)
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (5 hunks)
  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (2 hunks)
  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx (4 hunks)
  • frontend/src/types/application.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/src/hooks/queries/applicants/useGetApplicants.ts
  • frontend/src/constants/INITIAL_FORM_DATA.ts
  • frontend/src/hooks/queries/applicants/useUpdateApplicant.ts
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx
  • frontend/src/apis/application/updateApplicantDetail.ts
📚 Learning: 2025-05-19T05:45:52.957Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 406
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:34-38
Timestamp: 2025-05-19T05:45:52.957Z
Learning: The code duplication between createClubApplication and editClubApplication methods in ClubApplyService.java is acknowledged but will be addressed in a future refactoring, as per the developer's plan.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-09-30T05:26:41.788Z
Learnt from: alsdddk
Repo: Moadong/moadong PR: 765
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:431-435
Timestamp: 2025-09-30T05:26:41.788Z
Learning: In the Moadong codebase's club application feature (backend/src/main/java/moadong/club/), multiple ClubApplicationForm entities can have ACTIVE status for the same clubId, semesterYear, and semesterTerm simultaneously. There is no uniqueness constraint requiring only one ACTIVE form per semester.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
  • frontend/src/types/application.ts
📚 Learning: 2025-08-24T17:33:23.092Z
Learnt from: suhyun113
Repo: Moadong/moadong PR: 698
File: frontend/src/hooks/queries/applicants/useDeleteApplicants.ts:10-16
Timestamp: 2025-08-24T17:33:23.092Z
Learning: The codebase has inconsistent console logging patterns across files - mixing console.log/console.error for errors, using both Korean and English messages, and various formatting styles (template literals vs separate arguments, with/without emojis). A unified logging format like `console.error('[ModuleName] Operation failed:', error)` should be established for better debugging consistency.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
📚 Learning: 2025-05-16T06:25:11.193Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 418
File: frontend/src/mocks/api/clubHandlers.test.ts:0-0
Timestamp: 2025-05-16T06:25:11.193Z
Learning: 이 프로젝트에서는 API URL 생성과 같은 유틸리티 함수는 별도 파일로 분리하여 관리하는 것이 좋습니다. 특히 테스트 파일 내에 정의된 유틸리티 함수는 재사용성과 일관성을 위해 분리가 권장됩니다.

Applied to files:

  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx
🧬 Code graph analysis (5)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx (3)
frontend/src/hooks/queries/application/useGetApplicationlist.ts (1)
  • useGetApplicationlist (4-10)
frontend/src/context/AdminClubContext.tsx (1)
  • useAdminClubContext (40-47)
frontend/src/types/application.ts (2)
  • SemesterGroup (75-79)
  • ApplicationFormItem (68-73)
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (5)
frontend/src/context/AdminClubContext.tsx (1)
  • useAdminClubContext (40-47)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
  • useGetApplication (4-14)
frontend/src/types/application.ts (2)
  • ApplicationFormData (49-56)
  • Question (5-14)
frontend/src/apis/application/createApplication.ts (1)
  • createApplication (5-25)
frontend/src/apis/application/updateApplication.ts (1)
  • updateApplication (5-31)
frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx (3)
frontend/src/hooks/queries/application/useGetApplicationlist.ts (1)
  • useGetApplicationlist (4-10)
frontend/src/context/AdminClubContext.tsx (1)
  • useAdminClubContext (40-47)
frontend/src/types/application.ts (2)
  • SemesterGroup (75-79)
  • ApplicationFormItem (68-73)
frontend/src/apis/application/updateApplicantDetail.ts (1)
frontend/src/apis/auth/secureFetch.ts (1)
  • secureFetch (3-41)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (3)
frontend/src/context/AdminClubContext.tsx (1)
  • useAdminClubContext (40-47)
frontend/src/hooks/queries/applicants/useGetApplicants.ts (1)
  • useGetApplicants (4-11)
frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (1)
  • useUpdateApplicant (5-24)
🔇 Additional comments (6)
frontend/src/apis/application/updateApplicantDetail.ts (2)

7-7: 이전 리뷰 피드백이 반영되었습니다.

clubId에서 applicationFormId 기반으로의 마이그레이션이 올바르게 적용되었으며, 아래 가드 절이 undefined 케이스를 처리하고 있습니다.


14-14: 엔드포인트가 올바르게 업데이트되었습니다.

가드 절 덕분에 applicationFormId가 유효한 문자열임이 보장되며, API 경로가 새로운 다중 지원서 구조에 맞게 정확히 변경되었습니다.

frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (2)

482-482: 네비게이션 경로가 올바르게 업데이트되었습니다.

applicationFormId를 포함한 새로운 경로 구조로 정확하게 업데이트되었습니다.


39-40: 빈 문자열 폴백이 잘못된 API 호출을 유발할 수 있습니다.

applicationFormId ?? ''를 사용하면 applicationFormIdundefined일 때 빈 문자열이 전달되어 여전히 truthy 값으로 평가됩니다. useGetApplicants 훅의 enabled: !!applicationFormId 조건이 true가 되어 잘못된 ID로 API 호출이 발생합니다.

다음과 같이 수정하세요:

-  const { data: fetchData, isLoading, isError } = useGetApplicants(applicationFormId ?? '');
+  const { data: fetchData, isLoading, isError } = useGetApplicants(applicationFormId);

훅 내부의 enabled 조건이 이미 undefined 체크를 처리하므로 폴백 값이 불필요합니다.

Likely an incorrect or invalid review comment.

frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx (1)

23-26: 네비게이션 경로를 확인하세요.

handleGoToDetailForm은 지원자 목록 페이지(/admin/applicants-list/${applicationFormId})로 이동하는데, 함수명이 "DetailForm"으로 되어 있어 혼란을 줄 수 있습니다.

다음을 확인하세요:

  1. Line 23의 함수명과 실제 동작이 일치하는지 검증
  2. /admin/applicants-list/${applicationFormId} 경로가 의도한 페이지인지 확인

함수명이 목적과 맞지 않다면 다음과 같이 변경:

-  const handleGoToDetailForm = (applicationFormId: string) => {
+  const handleGoToApplicantsList = (applicationFormId: string) => {
     setApplicationFormId(applicationFormId);
     navigate(`/admin/applicants-list/${applicationFormId}`);
   }

그리고 line 123에서도 함수명 업데이트:

-  <Styled.ApplicationTitle $active={isActive} onClick={() => handleGoToDetailForm(application.id)}>
+  <Styled.ApplicationTitle $active={isActive} onClick={() => handleGoToApplicantsList(application.id)}>
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (1)

167-171: active 필드의 기본값 검증 - 부분 수정

코드베이스 검증 결과, 원본 리뷰의 예측과 다릅니다:

수정 사항:

  • INITIAL_FORM_DATA에서 active 필드의 기본값은 'Unpublished'입니다 (빈 문자열이 아님)
  • 백엔드가 'ACTIVE', 'INACTIVE' 같은 특정 값을 기대한다는 가정은 확인되지 않았습니다

그러나 여전한 우려:

  • Line 170의 active: formData.active ?? ''formData.activeundefined 또는 null이 될 수 있음을 암시합니다
  • 편집 모드에서 기존 지원서를 로드할 때 백엔드 API 응답이 active 필드를 누락하거나 null로 반환할 가능성이 있습니다
  • 빈 문자열이 백엔드 검증을 통과하는지 여부는 백엔드 코드 없이 확인 불가능합니다

검증 권장사항:
백엔드 개발자에게 다음을 확인하세요:

  1. GET 지원서 API 응답에서 active 필드가 항상 포함되는가?
  2. active 필드가 null/undefined일 수 있는가?
  3. 빈 문자열('')이 유효한 active 값인가?

Comment on lines +28 to +39
// const handleDeleteApplication = (applicationFormId: string) => {
// // 사용자에게 재확인
// if (window.confirm('지원서 양식을 정말 삭제하시겠습니까?\n삭제된 양식은 복구할 수 없습니다.')) {
// deleteApplication(applicationFormId, {
// onSuccess: () => {
// setOpenMenuId(null);
// // 성공 알림
// alert('삭제되었습니다.');
// },
// });
// }
// };
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

삭제 기능 구현을 완료하거나 코드를 제거하세요.

삭제 핸들러가 주석 처리되어 있어 ApplicationMenu의 삭제 옵션이 작동하지 않습니다.

다음 중 하나를 선택하세요:

옵션 1: 삭제 기능 구현 완료 (권장)

import { useDeleteApplication } from '@/hooks/queries/application/useDeleteApplication';

const { mutate: deleteApplication } = useDeleteApplication();

const handleDeleteApplication = (applicationFormId: string) => {
  if (window.confirm('지원서 양식을 정말 삭제하시겠습니까?\n삭제된 양식은 복구할 수 없습니다.')) {
    deleteApplication(applicationFormId, {
      onSuccess: () => {
        setOpenMenuId(null);
        alert('삭제되었습니다.');
      },
      onError: () => {
        alert('삭제에 실패했습니다. 다시 시도해주세요.');
      },
    });
  }
};

// ApplicationMenu에 전달
<ApplicationMenu
  isActive={isActive}
  onDelete={() => handleDeleteApplication(application.id)}
/>

옵션 2: 기능이 필요 없다면 관련 코드 모두 제거

  • 주석 처리된 import와 handler 제거
  • ApplicationMenu에서 onDelete prop 제거
  • ApplicationMenu 컴포넌트에서 삭제 옵션 UI 제거

Based on learnings

🤖 Prompt for AI Agents
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx
around lines 28 to 39: the delete handler is commented out so the
ApplicationMenu delete action is nonfunctional; fix by re-enabling and
implementing the handler: import and use the useDeleteApplication hook to get
the mutate function, implement handleDeleteApplication(applicationFormId) to
confirm with window.confirm and call the mutate with onSuccess that clears
setOpenMenuId and shows a success alert and onError that shows a failure alert,
then pass a bound onDelete={() => handleDeleteApplication(application.id)} into
ApplicationMenu; alternatively if deletion is not needed, remove the commented
import/handler, remove the onDelete prop usage from ApplicationMenu calls, and
remove the delete option from the ApplicationMenu component.

Comment on lines +72 to +73
if (isError) {
return <div>오류가 발생했습니다: {error.message}</div>;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

에러 객체의 안전한 접근을 보장하세요.

errorundefined일 수 있는데 error.message에 직접 접근하고 있습니다. React Query의 에러 타입은 unknown이므로 타입 가드가 필요합니다.

다음과 같이 수정하세요:

  if (isError) {
-    return <div>오류가 발생했습니다: {error.message}</div>;
+    return (
+      <div>
+        오류가 발생했습니다: {error instanceof Error ? error.message : '알 수 없는 오류'}
+      </div>
+    );
  }

또는 사용자 친화적인 메시지로:

  if (isError) {
    return <div>지원서 목록을 불러오는데 실패했습니다. 다시 시도해주세요.</div>;
  }
🤖 Prompt for AI Agents
In
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx
around lines 72-73, the code directly accesses error.message which can be
undefined or of unknown type; update the error branch to safely handle error by
checking for its presence and type before accessing message (e.g., use a type
guard like typeof error === 'object' && error !== null && 'message' in error to
read message) or replace with a stable user-friendly fallback string such as
"지원서 목록을 불러오는데 실패했습니다. 다시 시도해주세요." to avoid runtime errors.

Comment on lines +78 to +97
const formatDateTime = (dateTimeString: string) => {
const now = new Date();
const date = new Date(dateTimeString);
const isToday = now.getFullYear() === date.getFullYear() && now.getMonth() === date.getMonth() && now.getDate() === date.getDate();
if (isToday) {
// [오늘 날짜인 경우] 시간만 표시
return date.toLocaleString('ko-KR', {
hour: 'numeric',
minute: '2-digit',
hour12: true,
});
} else {
// [오늘 날짜가 아닌 경우] 날짜만 표시
return date.toLocaleString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
});
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

날짜 포맷팅 함수를 유틸로 분리하세요.

formatDateTime 함수가 여러 파일에서 중복 사용되고 있습니다(ApplicationListTab.tsx에도 동일한 구현이 있음). 재사용성과 유지보수성을 위해 별도 유틸 파일로 분리해야 합니다.

frontend/src/utils/dateFormatter.ts 파일을 생성하세요:

/**
 * ISO 날짜 문자열을 한국 시간대 기준으로 포맷팅합니다.
 * 오늘 날짜인 경우 시간만, 그 외에는 날짜만 표시합니다.
 * 
 * @param dateTimeString - ISO 형식의 날짜/시간 문자열
 * @returns 포맷팅된 날짜/시간 문자열
 */
export const formatDateTime = (dateTimeString: string): string => {
  const now = new Date();
  const date = new Date(dateTimeString);
  const isToday = 
    now.getFullYear() === date.getFullYear() && 
    now.getMonth() === date.getMonth() && 
    now.getDate() === date.getDate();
  
  if (isToday) {
    return date.toLocaleString('ko-KR', {
      hour: 'numeric',
      minute: '2-digit',
      hour12: true,
    });
  }
  
  return date.toLocaleString('ko-KR', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  });
};

그리고 이 파일에서 import하여 사용:

import { formatDateTime } from '@/utils/dateFormatter';

Based on learnings

🤖 Prompt for AI Agents
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab.tsx lines
78-97: the local formatDateTime implementation is duplicated across files;
extract it into a new utility file frontend/src/utils/dateFormatter.ts that
exports formatDateTime(dateTimeString: string): string using the same logic,
remove the local function from this file (and the duplicate in
ApplicationListTab.tsx), and update both components to import { formatDateTime }
from '@/utils/dateFormatter'; ensure the exported function has the same
signature and behavior and update any affected imports/tsconfig paths so the
project compiles.

};
const { mutate: deleteApplicants } = useDeleteApplicants(clubId!);
const { mutate: updateDetailApplicants } = useUpdateApplicant(clubId!);
const { mutate: updateDetailApplicants } = useUpdateApplicant(applicationFormId ?? '');
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

빈 문자열 폴백을 제거하세요.

Line 40과 동일한 이유로 applicationFormId ?? '' 대신 applicationFormId를 직접 전달하세요. 훅 내부에서 유효성을 검증합니다.

-  const { mutate: updateDetailApplicants } = useUpdateApplicant(applicationFormId ?? '');
+  const { mutate: updateDetailApplicants } = useUpdateApplicant(applicationFormId);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { mutate: updateDetailApplicants } = useUpdateApplicant(applicationFormId ?? '');
const { mutate: updateDetailApplicants } = useUpdateApplicant(applicationFormId);
🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx around line
70, remove the empty-string fallback and pass applicationFormId directly to the
hook: replace useUpdateApplicant(applicationFormId ?? '') with
useUpdateApplicant(applicationFormId). The hook should perform validation, so do
not coerce undefined to '' here; ensure TypeScript types still align (adjust
call site types if necessary).

@seongje973 seongje973 merged commit 1e5be34 into develop-fe Nov 17, 2025
3 of 5 checks passed
@seongje973 seongje973 deleted the feature/#781-connectingmultipleapplicationsapi-MOA-285 branch November 17, 2025 08:36
@seongje973 seongje973 restored the feature/#781-connectingmultipleapplicationsapi-MOA-285 branch November 17, 2025 12:55
@seongje973 seongje973 deleted the feature/#781-connectingmultipleapplicationsapi-MOA-285 branch November 17, 2025 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📬 API 서버 API 통신 작업 💻 FE Frontend ✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants