feat: web-27 신청 부원 조회 페이지#24
Conversation
There was a problem hiding this comment.
Code Review
This pull request replaces mock data with real API integration on the recruit management page (RecruitManagePage.jsx). It introduces applicant list fetching with custom pagination logic (mapping 15-item server pages to 5-item display pages), applicant detail fetching, and error handling with an alert modal. Feedback on the changes highlights two main areas of improvement: handling potential race conditions and memory leaks in the asynchronous page-fetching effect by tracking the component's mount status, and adding defensive checks in the date formatting helper (formatAppliedDate) to prevent rendering invalid dates (e.g., NaN.NaN.NaN) when given null or invalid inputs.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const fetchApplicants = useCallback(async () => { | ||
| try { | ||
| const paging = await getApplicantList({ | ||
| page: serverPageIndex, | ||
| size: APPLICANT_PAGE_SIZE, | ||
| }); | ||
| setServerContent(paging?.content ?? []); | ||
| setTotalElements(paging?.totalElements ?? 0); | ||
| } catch (error) { | ||
| console.error('[RecruitManagePage] 신청 부원 목록 조회 실패', error); | ||
| setServerContent([]); | ||
| setTotalElements(0); | ||
| setAlertMessage('신청 부원 목록을 불러오지 못했습니다.\n잠시 후 다시 시도해주세요.'); | ||
| } | ||
| }, [serverPageIndex]); | ||
|
|
||
| useEffect(() => { | ||
| fetchApplicants(); | ||
| }, [fetchApplicants]); |
There was a problem hiding this comment.
페이지를 빠르게 전환할 때 비동기 요청의 응답 순서가 뒤바뀌어 이전 페이지의 데이터가 현재 페이지에 덮어씌워지는 경쟁 상태(Race Condition)가 발생할 수 있습니다. 또한 컴포넌트가 언마운트된 후 상태를 업데이트하려는 메모리 누수 위험도 존재합니다.\n\n이를 방지하기 위해 useEffect 내부에 fetch 로직을 정의하고, 마운트 여부를 추적하는 플래그(isMounted)를 사용하여 안전하게 상태를 업데이트하도록 개선하는 것이 좋습니다. 이 방식을 사용하면 useCallback을 제거하여 코드를 더 단순하게 유지할 수 있습니다.
useEffect(() => {
let isMounted = true;
const fetchApplicants = async () => {
try {
const paging = await getApplicantList({
page: serverPageIndex,
size: APPLICANT_PAGE_SIZE,
});
if (isMounted) {
setServerContent(paging?.content ?? []);
setTotalElements(paging?.totalElements ?? 0);
}
} catch (error) {
console.error('[RecruitManagePage] 신청 부원 목록 조회 실패', error);
if (isMounted) {
setServerContent([]);
setTotalElements(0);
setAlertMessage('신청 부원 목록을 불러오지 못했습니다.\n잠시 후 다시 시도해주세요.');
}
}
};
fetchApplicants();
return () => {
isMounted = false;
};
}, [serverPageIndex]);| function formatAppliedDate(isoString) { | ||
| const date = new Date(isoString); | ||
| const yyyy = date.getFullYear(); | ||
| const mm = String(date.getMonth() + 1).padStart(2, '0'); | ||
| const dd = String(date.getDate()).padStart(2, '0'); | ||
| return `${yyyy}.${mm}.${dd}`; | ||
| } |
There was a problem hiding this comment.
isoString이 유효하지 않거나 빈 값(null, undefined 등)일 경우, new Date(isoString)은 유효하지 않은 날짜 객체를 반환하며 getFullYear() 등의 메서드 호출 시 NaN을 반환하여 화면에 NaN.NaN.NaN과 같이 렌더링될 수 있습니다. 이를 방지하기 위해 방어적 코드를 추가하는 것이 좋습니다.
function formatAppliedDate(isoString) {
if (!isoString) return '';
const date = new Date(isoString);
if (isNaN(date.getTime())) return '';
const yyyy = date.getFullYear();
const mm = String(date.getMonth() + 1).padStart(2, '0');
const dd = String(date.getDate()).padStart(2, '0');
return yyyy + '.' + mm + '.' + dd;
}
📌 작업 내용
신청부원 조회 페이지 api 연동, 기타오류 수정
🔗 관련 이슈
📸 스크린샷 / 화면 녹화
🧪 테스트 방법
1.admin/Memberpage
2.
3.
✅ 체크리스트
npm run lint통과를 확인했습니다npm run format을 적용했습니다console.log를 제거했습니다develop인지 확인했습니다💬 리뷰어에게 한 마디
// 신청 부원 목록 페이지 크기. 모달에서 스크롤로 보여주므로 넉넉히 한 번에 받아온다.
export const APPLICANT_PAGE_SIZE = 100;
이 부분을
// 신청 부원 목록 페이지 크기. 백엔드에서 15로 고정(min/max 모두 15)되어 있어 그대로 맞춘다.
export const APPLICANT_PAGE_SIZE = 15;
로 수정했습니다