Skip to content

Comments

[release] FE v1.1.26#1228

Merged
seongwon030 merged 17 commits intomainfrom
develop-fe
Feb 22, 2026
Merged

[release] FE v1.1.26#1228
seongwon030 merged 17 commits intomainfrom
develop-fe

Conversation

@seongwon030
Copy link
Member

@seongwon030 seongwon030 commented Feb 22, 2026

#️⃣연관된 이슈

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

📝작업 내용

  • 관리자 기능 버그 수정
  • swiper 라이브러리 취약점으로 버전 업그레이드

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

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

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

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

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

🫡 참고사항

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • 지원자 폼 유효성 검사 추가 (제목·설명·질문·외부 URL) 및 오류 집계
  • 버그 수정
    • 지원자 데이터 로딩/오류 처리 분리 및 명확한 오류 메시지
  • 개선사항
    • 메모·상태 편집: 입력은 로컬 상태로, 포커스 해제 시 즉시 저장(디바운스 제거)
    • 지원자 목록 사전로딩으로 내비게이션 개선
  • 작업
    • 주요 UI 라이브러리(swiper) 버전 업그레이드

seongwon030 and others added 12 commits February 21, 2026 15:54
- validateApplicationForm 함수로 제목/설명 필수 및 최대 길이, 질문 제목, 외부 URL 검증
- 검증 실패 시 항목별 에러 메시지를 alert으로 표시
- FormTitle에 maxLength={50} 추가
- 기존 인라인 URL 검증 로직을 validateApplicationForm으로 통합
…validation-MOA-666

[feature] 지원서 저장 시 검증 추가
- 기존 truthy 체크를 제거하고 memo는 string 타입, status는 ApplicationStatus enum 타입가드로 검증하도록 변경
…unce-MOA-669

[fix] 지원자 수정 디바운스 제거
- applicationData를 tanstack으로 가져옴
…r-MOA-671

[Fix] 지원자 페이지 새로고침 에러 해결
…ade-MOA-673

[chore] Swiper 라이브러리 버전 업그레이드
@vercel
Copy link

vercel bot commented Feb 22, 2026

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

Project Deployment Actions Updated (UTC)
moadong Ready Ready Preview, Comment Feb 22, 2026 8:52am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 22, 2026

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

관리자 애플리케이션 폼에 클라이언트 검증을 추가하고, 신청자 상세 페이지의 데이터 페칭 및 업데이트 흐름을 변경(사전페치 useGetApplicants 추가, debounce 제거, onBlur 기반 즉시 업데이트), 불필요한 임포트 정리 및 Swiper 의존성 업그레이드가 포함된 변경입니다.

Changes

Cohort / File(s) Summary
Swiper 의존성 업그레이드
frontend/package.json
swiper 버전 ^11.2.10 → ^12.1.2로 업데이트 (파일 끝 개행 제거 포함).
신청자 상세 및 업데이트 흐름
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
useGetApplicants 도입으로 신청자 리스트 사전 페치 추가, debounce 제거, 메모는 local state에서 onChange로 관리 후 onBlur에서 서버 업데이트 호출, isApplicationStatus 타입 가드 추가, 로딩/에러 처리 분리 및 개선.
임포트 정리 (Applicants 리스트)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx
사용하지 않는 useQueryClientupdateApplicationStatus 등 불필요한 import 제거.
클라이언트 측 폼 검증 추가
frontend/src/pages/AdminPage/validation/validateApplicationForm.ts, frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx
새로운 validateApplicationForm·hasErrors 추가(제목/설명/질문/외부URL 검증), ApplicationEditTab에서 제출 전 유효성 검사 도입 및 에러 집계/알림, FormTitlemaxLength={50} 적용, 기존 하드코딩된 URL 화이트리스트 제거 및 검증 모듈로 이동.

Sequence Diagram(s)

sequenceDiagram
    participant AdminUI as Admin UI (ApplicantDetailPage)
    participant Hook as useGetApplicants / useUpdateApplicant
    participant API as Applicants API
    participant Store as Applicants list (cached)

    AdminUI->>Hook: 요청 (컴포넌트 마운트) -> useGetApplicants 쿼리 실행
    Hook->>API: GET /applicants
    API-->>Hook: applicants 데이터 반환
    Hook-->>Store: 캐시 저장
    Hook-->>AdminUI: applicantsData 제공

    Note over AdminUI,Hook: 사용자 편집 (메모/상태)
    AdminUI->>AdminUI: 로컬 상태 업데이트 (onChange)
    AdminUI->>Hook: onBlur -> updateApplicantDetail 호출
    Hook->>API: PATCH /applicants/:id (변경사항)
    API-->>Hook: 업데이트 결과
    Hook-->>Store: 캐시 갱신
    Hook-->>AdminUI: 업데이트 완료/에러 반환
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

💻 FE, 📈 release, ✨ Feature

Suggested reviewers

  • lepitaaar
  • oesnuj
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 버전 업그레이드 릴리스를 나타내며, 커밋 메시지와 PR 설명에 따르면 관리자 기능 버그 수정과 swiper 라이브러리 보안 업그레이드를 포함하는 변경사항과 부분적으로 관련이 있습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop-fe

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
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

🧹 Nitpick comments (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (1)

104-110: 에러 가드 순서 — isError!applicantsData 앞으로 이동 권장

현재 순서에서는 isApplicantsErrorfalse이고 applicantsDataundefined이면서 동시에 isError(폼 데이터 오류)가 true인 경우, 사용자가 실제 폼 오류 메시지 대신 "지원자 데이터를 불러올 수 없습니다."를 보게 됩니다. isError!formData 검사를 !applicantsData 앞으로 올리면 더 정확한 오류 메시지를 표시할 수 있습니다.

♻️ 수정 제안
 if (isLoading || isApplicantsLoading) return <Spinner />;
 if (isApplicantsError)
   return <div>지원자 데이터를 불러오는 중 오류가 발생했습니다.</div>;
-if (!applicantsData) return <div>지원자 데이터를 불러올 수 없습니다.</div>;
 if (isError) return <div>지원서 정보를 불러오는 중 오류가 발생했습니다.</div>;
 if (!formData) return <div>지원서 정보가 없습니다.</div>;
+if (!applicantsData) return <div>지원자 데이터를 불러올 수 없습니다.</div>;
 if (!applicant) return <div>해당 지원자를 찾을 수 없습니다.</div>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx`
around lines 104 - 110, The guard ordering in ApplicantDetailPage causes
form-level errors to be shadowed by the applicantsData empty check; move the
isError and !formData checks (the conditions using isError and formData) to run
before the applicantsData existence check so form-related error messages render
instead of "지원자 데이터를 불러올 수 없습니다."; keep isApplicantsError check where it is (the
isApplicantsError conditional) and ensure the final order in the render
early-return sequence is: isLoading/isApplicantsLoading, isApplicantsError,
isError, !formData, !applicantsData, !applicant.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx`:
- Around line 81-99: updateApplicantDetail may send applicantId: undefined
because questionId from useParams can be undefined; add a guard/narrow before
calling updateApplicant (or inside updateApplicantDetail) to ensure questionId
is defined (e.g., if (!questionId) return or surface an error) and then pass the
validated questionId to updateApplicant; reference the questionId variable and
the updateApplicant call in ApplicantDetailPage (and keep existing
isApplicationStatus check).

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx`:
- Line 1: Removing the default React import broke the type reference used in
ApplicantsListTab (the parameter typed as React.MouseEvent at the click handler
around line 92); fix by either re-adding the default import (import React from
'react') at the top of ApplicantsListTab or, preferably, change the type
annotation to the named import form (import { MouseEvent } from 'react') and
update the handler signature to use MouseEvent instead; update the import
statement near the existing useEffect/useState imports and adjust the handler
type (e.g., the function referenced in ApplicantsListTab that accepts e:
React.MouseEvent) accordingly.

In `@frontend/src/pages/AdminPage/validation/validateApplicationForm.ts`:
- Around line 3-9: The ALLOWED_EXTERNAL_URLS array entry for Google Forms is
missing a trailing slash causing startsWith checks to over-accept; update the
'https://docs.google.com/forms' entry in ALLOWED_EXTERNAL_URLS to include the
trailing '/' (i.e. 'https://docs.google.com/forms/') so prefix matching in the
validation logic correctly restricts URLs; verify any code that uses
ALLOWED_EXTERNAL_URLS.startsWith logic still works with the updated constant.

---

Nitpick comments:
In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx`:
- Around line 104-110: The guard ordering in ApplicantDetailPage causes
form-level errors to be shadowed by the applicantsData empty check; move the
isError and !formData checks (the conditions using isError and formData) to run
before the applicantsData existence check so form-related error messages render
instead of "지원자 데이터를 불러올 수 없습니다."; keep isApplicantsError check where it is (the
isApplicantsError conditional) and ensure the final order in the render
early-return sequence is: isLoading/isApplicantsLoading, isApplicantsError,
isError, !formData, !applicantsData, !applicant.

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.

UX개선 릴리즈 바로갑시다~

@seongwon030 seongwon030 merged commit cb37953 into main Feb 22, 2026
3 of 4 checks passed
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.

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)

74-79: ⚠️ Potential issue | 🟠 Major

상태 변경 중 메모 입력 내용이 유실될 수 있음

handleStatusChange가 호출되면 즉시 updateApplicantDetail이 실행되고, React Query가 뮤테이션 완료 후 useGetApplicants 쿼리를 재조회합니다. 재조회 결과로 새 applicant 객체가 반환되면 useEffectsetAppMemo(applicant.memo)가 트리거되어 textarea 내용이 뮤테이션 시점의 memo 값으로 초기화됩니다. 사용자가 상태를 변경한 후 blur 이벤트 전까지 메모를 추가로 입력했다면, 그 내용이 조용히 사라집니다.

일반적인 해결 방법은 textarea에 포커스가 있는 동안은 외부 데이터로 memo 상태를 덮어쓰지 않도록 보호하는 것입니다.

🛡️ 수정 제안 (isFocused 플래그 활용)
+  const [isMemoFocused, setIsMemoFocused] = useState(false);

   useEffect(() => {
     if (applicant) {
-      setAppMemo(applicant.memo);
+      if (!isMemoFocused) {
+        setAppMemo(applicant.memo);
+      }
       setApplicantStatus(mapStatusToGroup(applicant.status).status);
     }
-  }, [applicant, applicant?.status, applicant?.memo]);
+  }, [applicant, applicant?.status, applicant?.memo, isMemoFocused]);
   const handleMemoBlur = () => {
+    setIsMemoFocused(false);
     updateApplicantDetail(applicantMemo, applicantStatus);
   };
   <Styled.MemoTextarea
     onChange={handleMemoChange}
+    onFocus={() => setIsMemoFocused(true)}
     onBlur={handleMemoBlur}
     placeholder='메모를 입력해주세요'
     value={applicantMemo}
   />

Also applies to: 122-130

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx`
around lines 74 - 79, The effect that syncs applicant.memo into local state
(useEffect in ApplicantDetailPage calling setAppMemo) is overwriting in-progress
edits when the textarea is focused; add an isFocused flag for the memo textarea
(managed via onFocus/onBlur) and guard the effect so it only calls
setAppMemo(applicant.memo) when isFocused is false, leaving local edits intact
during focus; update the textarea handlers to toggle isFocused and adjust the
effect dependencies (include isFocused) so the memo is only overwritten by
incoming applicant updates when the field is not focused; ensure
handleStatusChange and the React Query mutation can still update the server
state but won't clobber the user's in-flight local edits.
🧹 Nitpick comments (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (1)

54-59: applicationFormId ?? undefined 중복 표현

useParams의 반환 타입이 이미 string | undefined이므로 ?? undefined는 항상 applicationFormId 자체를 반환합니다. Line 71의 useUpdateApplicant 호출도 동일합니다.

♻️ 수정 제안
-  } = useGetApplicants(applicationFormId ?? undefined);
+  } = useGetApplicants(applicationFormId);
-  const { mutate: updateApplicant } = useUpdateApplicant(
-    applicationFormId ?? undefined,
-  );
+  const { mutate: updateApplicant } = useUpdateApplicant(applicationFormId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx`
around lines 54 - 59, The code is passing applicationFormId through a redundant
nullish coalescing (applicationFormId ?? undefined) into hooks; since useParams
already types applicationFormId as string | undefined, remove the "?? undefined"
and pass applicationFormId directly to useGetApplicants and useUpdateApplicant
(and any other hooks in this file using the same pattern) to simplify the call
sites; look for references to applicationFormId, useGetApplicants, and
useUpdateApplicant in ApplicantDetailPage and update them accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx`:
- Around line 74-79: The effect that syncs applicant.memo into local state
(useEffect in ApplicantDetailPage calling setAppMemo) is overwriting in-progress
edits when the textarea is focused; add an isFocused flag for the memo textarea
(managed via onFocus/onBlur) and guard the effect so it only calls
setAppMemo(applicant.memo) when isFocused is false, leaving local edits intact
during focus; update the textarea handlers to toggle isFocused and adjust the
effect dependencies (include isFocused) so the memo is only overwritten by
incoming applicant updates when the field is not focused; ensure
handleStatusChange and the React Query mutation can still update the server
state but won't clobber the user's in-flight local edits.

---

Nitpick comments:
In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx`:
- Around line 54-59: The code is passing applicationFormId through a redundant
nullish coalescing (applicationFormId ?? undefined) into hooks; since useParams
already types applicationFormId as string | undefined, remove the "?? undefined"
and pass applicationFormId directly to useGetApplicants and useUpdateApplicant
(and any other hooks in this file using the same pattern) to simplify the call
sites; look for references to applicationFormId, useGetApplicants, and
useUpdateApplicant in ApplicantDetailPage and update them accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants