From 005a0a9074ebf23932fefa72961b01573e0df434 Mon Sep 17 00:00:00 2001 From: manNomi Date: Tue, 4 Aug 2026 02:05:12 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=EC=A7=80=EC=9B=90=EC=9E=90=20?= =?UTF-8?q?=ED=98=84=ED=99=A9=EC=9D=84=20=EC=86=8C=EC=86=8D=20=EB=8C=80?= =?UTF-8?q?=ED=95=99=20=EA=B8=B0=EC=A4=80=EC=9C=BC=EB=A1=9C=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/apis/applications/getApplicants.ts | 40 ++++++++++++++++++- .../apis/applications/homeUniversityScope.ts | 40 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/apis/applications/homeUniversityScope.ts diff --git a/apps/web/src/apis/applications/getApplicants.ts b/apps/web/src/apis/applications/getApplicants.ts index f7da90243..9f5572c6d 100644 --- a/apps/web/src/apis/applications/getApplicants.ts +++ b/apps/web/src/apis/applications/getApplicants.ts @@ -1,8 +1,13 @@ import { type UseQueryOptions, type UseQueryResult, useQuery } from "@tanstack/react-query"; import type { AxiosError, AxiosResponse } from "axios"; +import { useMemo } from "react"; +import useAuthStore from "@/lib/zustand/useAuthStore"; import type { ApplicationListResponse } from "@/types/application"; +import { QueryKeys } from "../queryKeys"; +import { universitiesApi } from "../universities/api"; import { ApplicationsQueryKeys, applicationsApi } from "./api"; +import { filterApplicationsByHomeUniversityScope } from "./homeUniversityScope"; type UseGetApplicationsListOptions = Omit< UseQueryOptions, AxiosError<{ message: string }>, ApplicationListResponse>, @@ -11,17 +16,50 @@ type UseGetApplicationsListOptions = Omit< /** * @description 지원 목록 조회 훅 + * + * `GET /applications` 는 전체 지원자 현황을 내려주고 소속 대학을 구분해주지 않는다. + * 그래서 access token 에서 파싱된 소속 대학(useAuthStore.homeUniversityId)으로 + * 파견학교 목록을 따로 받아, 그 범위에 속한 항목만 남기도록 클라이언트에서 걸러낸다. */ const useGetApplicationsList = ( props?: UseGetApplicationsListOptions, ): UseQueryResult> => { - return useQuery({ + const homeUniversityId = useAuthStore((state) => state.homeUniversityId); + + const applicationsQuery = useQuery({ queryKey: [ApplicationsQueryKeys.competitorsApplicationList], queryFn: applicationsApi.getApplicationsList, staleTime: 1000 * 60 * 5, // 5분간 캐시 select: (response) => response.data, ...props, }); + + // 필터 기준이 되는 소속 대학의 파견학교 목록 + const { data: scopedUniversities } = useQuery({ + queryKey: [QueryKeys.universities.searchText, { homeUniversityId }], + queryFn: () => universitiesApi.getSearchText({ value: "", homeUniversityId: homeUniversityId ?? undefined }), + enabled: homeUniversityId !== null, + staleTime: 1000 * 60 * 5, + select: (response) => response.univApplyInfoPreviews, + }); + + const scopedData = useMemo(() => { + if (!applicationsQuery.data) { + return applicationsQuery.data; + } + + // 소속 대학을 모르면(비인증·학교 미인증) 기존과 동일하게 전체를 보여준다. + if (homeUniversityId === null) { + return applicationsQuery.data; + } + + return filterApplicationsByHomeUniversityScope(applicationsQuery.data, scopedUniversities); + }, [applicationsQuery.data, homeUniversityId, scopedUniversities]); + + return { ...applicationsQuery, data: scopedData } as UseQueryResult< + ApplicationListResponse, + AxiosError<{ message: string }> + >; }; export default useGetApplicationsList; diff --git a/apps/web/src/apis/applications/homeUniversityScope.ts b/apps/web/src/apis/applications/homeUniversityScope.ts new file mode 100644 index 000000000..c45f6acbf --- /dev/null +++ b/apps/web/src/apis/applications/homeUniversityScope.ts @@ -0,0 +1,40 @@ +import type { ApplicationListResponse, ScoreSheet } from "@/types/application"; + +/** + * 지원자 현황 응답을 소속 대학 기준으로 걸러내기 위한 키. + * + * `GET /applications` 응답(ScoreSheet)에는 홈 대학 식별자도, 지원 정보 id 도 없다. + * 그래서 파견학교 목록과 겹치는 필드(파견학교명 + 국가 + 모집인원)를 합쳐 식별한다. + * + * 파견학교명만으로는 부족하다. 서로 다른 홈 대학이 같은 이름의 파견학교를 가질 수 있고 + * (예: 경희대·중앙대 모두 보유한 국립정치대학교), 그 경우 이름만으로는 구분되지 않는다. + */ +type ScopeKeySource = { + koreanName: string; + country: string; + studentCapacity: number | null; +}; + +export const createHomeUniversityScopeKey = ({ koreanName, country, studentCapacity }: ScopeKeySource): string => + `${koreanName}|${country}|${studentCapacity ?? ""}`; + +/** + * 소속 대학의 파견학교 목록에 포함된 항목만 남긴다. + * 기준 목록이 비어 있으면 필터링하지 않고 원본을 그대로 돌려준다. + */ +export const filterApplicationsByHomeUniversityScope = ( + applications: ApplicationListResponse, + scopedUniversities: ScopeKeySource[] | undefined, +): ApplicationListResponse => { + if (!scopedUniversities || scopedUniversities.length === 0) { + return applications; + } + + const allowedKeys = new Set(scopedUniversities.map(createHomeUniversityScopeKey)); + + return { + choices: applications.choices.map((scoreSheets: ScoreSheet[]) => + scoreSheets.filter((scoreSheet) => allowedKeys.has(createHomeUniversityScopeKey(scoreSheet))), + ), + }; +};