@@ -20,8 +20,6 @@ import useGetApplicationsList from "@/api/applications/client/useGetApplications
2020
2121const PREFERENCE_CHOICE : ( "1순위" | "2순위" | "3순위" ) [ ] = [ "1순위" , "2순위" , "3순위" ] ;
2222
23- // API 응답 데이터의 타입을 명확하게 정의합니다.
24- // 실제 ApplicationListResponse 타입을 사용하시는 것이 더 좋습니다.
2523interface ScoreData {
2624 firstChoice : ScoreSheetType [ ] ;
2725 secondChoice : ScoreSheetType [ ] ;
@@ -38,14 +36,12 @@ const ScorePageContent = () => {
3836 const [ searchValue , setSearchValue ] = useState ( "" ) ;
3937 const [ showNeedApply , setShowNeedApply ] = useState ( false ) ;
4038
41- // ✨ 1. 초기 데이터를 API 응답 구조와 동일하게 설정합니다.
4239 const initialData : ScoreData = {
4340 firstChoice : [ ] ,
4441 secondChoice : [ ] ,
4542 thirdChoice : [ ] ,
4643 } ;
4744
48- // ✨ 2. `data`의 기본값을 위에서 정의한 initialData로 변경합니다.
4945 const {
5046 data : scoreResponseData = initialData ,
5147 isError,
@@ -55,27 +51,32 @@ const ScorePageContent = () => {
5551 } ) ;
5652
5753 const filteredAndSortedData = useMemo ( ( ) => {
58- // 데이터가 없는 경우를 대비한 방어 코드
59- const firstChoice = scoreResponseData ?. firstChoice || [ ] ;
60- const secondChoice = scoreResponseData ?. secondChoice || [ ] ;
61- const thirdChoice = scoreResponseData ?. thirdChoice || [ ] ;
54+ // ✨ 1. 대학 이름(koreanName)을 기준으로 중복을 제거하는 헬퍼 함수
55+ const uniqueByKoreanName = ( data : ScoreSheetType [ ] ) => {
56+ // Map을 사용해 koreanName을 키로 하여 중복을 효율적으로 제거합니다.
57+ const universityMap = new Map ( data . map ( ( sheet ) => [ sheet . koreanName , sheet ] ) ) ;
58+ // Map의 값들만 다시 배열로 변환하여 반환합니다.
59+ return Array . from ( universityMap . values ( ) ) ;
60+ } ;
61+
62+ // ✨ 2. API 응답 데이터를 받자마자 중복부터 제거합니다.
63+ const firstChoice = uniqueByKoreanName ( scoreResponseData ?. firstChoice || [ ] ) ;
64+ const secondChoice = uniqueByKoreanName ( scoreResponseData ?. secondChoice || [ ] ) ;
65+ const thirdChoice = uniqueByKoreanName ( scoreResponseData ?. thirdChoice || [ ] ) ;
6266
63- // 원본 데이터를 훼손하지 않기 위해 복사본을 만들어 정렬합니다.
67+ // 3. 중복이 제거된 데이터를 정렬합니다.
6468 const sortedData = {
65- // ✨ 3. `[...scoreResponseData]`가 아니라 `[...firstChoice]`로 수정합니다.
6669 firstChoice : [ ...firstChoice ] . sort ( ( a , b ) => b . applicants . length - a . applicants . length ) ,
6770 secondChoice : [ ...secondChoice ] . sort ( ( a , b ) => b . applicants . length - a . applicants . length ) ,
6871 thirdChoice : [ ...thirdChoice ] . sort ( ( a , b ) => b . applicants . length - a . applicants . length ) ,
6972 } ;
7073
71- // 필터링 로직
74+ // 4. 기존 필터링 로직을 적용합니다.
7275 const applyFilters = ( data : ScoreSheetType [ ] ) => {
7376 let result = data ;
74- // 지역 필터
7577 if ( regionFilter ) {
7678 result = result . filter ( ( sheet ) => sheet . region === regionFilter ) ;
7779 }
78- // 검색어 필터
7980 if ( searchValue ) {
8081 result = result . filter ( ( sheet ) => sheet . koreanName . toLowerCase ( ) . includes ( searchValue . toLowerCase ( ) ) ) ;
8182 }
@@ -123,15 +124,16 @@ const ScorePageContent = () => {
123124 const scoreSheets = getScoreSheet ( ) ;
124125
125126 useEffect ( ( ) => {
126- console . log ( "isLoading" , isLoading ) ;
127- console . log ( "isError" , isError ) ;
128-
129127 if ( isLoading ) return ;
130128 if ( isError ) {
131129 alert ( "지원 현황을 불러오는 중에 오류가 발생했습니다. 지원 절차를 진행해주세요." ) ;
132130 router . replace ( "/university/application/apply" ) ;
133131 }
134- } , [ isError , isLoading ] ) ;
132+ } , [ isError , isLoading , router ] ) ;
133+
134+ if ( isLoading ) {
135+ return < CloudSpinnerPage /> ;
136+ }
135137
136138 if ( searchActive ) {
137139 const hotKeyWords = [ "RMIT" , "오스트라바" , "칼스루에" , "그라츠" , "추오" , "프라하" , "보라스" , "빈" , "메모리얼" ] ;
@@ -158,7 +160,7 @@ const ScorePageContent = () => {
158160 style = { { padding : "10px 0 10px 18px" } }
159161 />
160162
161- < div className = "mx-5 mt-2.5 flex w-[calc(100%-44px)] flex-col gap-3 overflow-x-auto" >
163+ < div className = "mx-auto mt-2.5 flex w-full flex-col gap-3 overflow-x-auto" >
162164 { scoreSheets . map ( ( choice ) => (
163165 < ScoreSheet key = { choice . koreanName } scoreSheet = { choice } />
164166 ) ) }
0 commit comments