From 76e84b940610e060ff29e783830d1677aa657636 Mon Sep 17 00:00:00 2001 From: Shineast Date: Mon, 9 Feb 2026 09:27:55 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20"=EB=82=B4=20=EC=B6=9C=EB=B0=9C?= =?UTF-8?q?=EC=A7=80=20=EC=88=98=EC=A0=95=ED=95=98=EA=B8=B0"=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EC=8B=9C,=20=EC=BA=90=EC=8B=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/result/[id]/page.tsx | 5 + hooks/api/query/useCheckMeeting.ts | 2 +- mock/mockData.ts | 257 ----------------------------- 3 files changed, 6 insertions(+), 258 deletions(-) delete mode 100644 mock/mockData.ts diff --git a/app/result/[id]/page.tsx b/app/result/[id]/page.tsx index 45a99c3..9396615 100644 --- a/app/result/[id]/page.tsx +++ b/app/result/[id]/page.tsx @@ -8,8 +8,10 @@ import KakaoMapLine from '@/components/map/kakaoMapLine'; import { useMidpoint } from '@/hooks/api/query/useMidpoint'; import { useCheckMeeting } from '@/hooks/api/query/useCheckMeeting'; import { getMeetingUserId } from '@/lib/storage'; +import { useQueryClient } from '@tanstack/react-query'; export default function Page() { + const queryClient = useQueryClient(); const openModal = useOpenModal(); const router = useRouter(); const params = useParams(); @@ -103,6 +105,9 @@ export default function Page() { const [selectedResultId, setSelectedResultId] = useState(1); const handleModifyStart = () => { + queryClient.removeQueries({ queryKey: ['midpoint', id] }); + queryClient.removeQueries({ queryKey: ['recommend', id] }); + router.back(); }; diff --git a/hooks/api/query/useCheckMeeting.ts b/hooks/api/query/useCheckMeeting.ts index b7597ff..a441288 100644 --- a/hooks/api/query/useCheckMeeting.ts +++ b/hooks/api/query/useCheckMeeting.ts @@ -14,7 +14,7 @@ export const useCheckMeeting = (meetingId: string) => { return apiGet(`/api/meeting/${meetingId}/status`); }, enabled: hasUserId && !!meetingId, - refetchInterval: 10000, + refetchInterval: 5000, retry: false, throwOnError: false, }); diff --git a/mock/mockData.ts b/mock/mockData.ts deleted file mode 100644 index c69d302..0000000 --- a/mock/mockData.ts +++ /dev/null @@ -1,257 +0,0 @@ -import { getRandomHexColor } from '@/lib/color'; - -// 기존 데이터 리스트 (색상 정보 제외) -const RAW_PARTICIPANTS = [ - { - id: 1, - name: '안', - line: '2호선', - station: '홍대입구역', - latitude: 37.557527, - longitude: 126.924466, - status: 'pending', - }, - { - id: 2, - name: '손', - line: '2호선', - station: '성수역', - latitude: 37.544581, - longitude: 127.056002, - status: 'pending', - }, - { - id: 3, - name: '김', - line: '2호선', - station: '강남역', - latitude: 37.498095, - longitude: 127.02761, - status: 'done', - }, - { - id: 4, - name: '이', - line: '7호선', - station: '건대입구역', - latitude: 37.540417, - longitude: 127.069177, - status: 'done', - }, - { - id: 5, - name: '최', - line: '9호선', - station: '여의도역', - latitude: 37.521571, - longitude: 126.924292, - status: 'pending', - }, - { - id: 6, - name: '박', - line: '2호선', - station: '잠실역', - latitude: 37.513261, - longitude: 127.100159, - status: 'done', - }, - { - id: 7, - name: '정', - line: '4호선', - station: '혜화역', - latitude: 37.582236, - longitude: 127.001851, - status: 'pending', - }, - { - id: 8, - name: '조', - line: '4호선', - station: '용산역', - latitude: 37.529849, - longitude: 126.964561, - status: 'done', - }, -]; - -// 랜덤 색상을 주입하여 export -export const MOCK_PARTICIPANTS = RAW_PARTICIPANTS.map((p, idx) => ({ - ...p, - // 요청하신 S:65, B:100 고정 랜덤 색상 생성 - hexColor: getRandomHexColor(idx), -})); - -// [3] 목업 데이터: 최종 위치 결과 -export const MOCK_LOCATION_RESULTS = [ - { - id: 1, - station: '합정역', - time: '30분', - lines: ['1', '2'], // 1호선, 2호선 - isSelected: true, // 현재 선택된 상태 시뮬레이션 - }, - { - id: 2, - station: '합정역', - time: '30분', - lines: ['1', '2'], // 1호선, 2호선 - isSelected: false, // 현재 선택된 상태 시뮬레이션 - }, - { - id: 3, - station: '합정역', - time: '30분', - lines: ['1', '2'], // 1호선, 2호선 - isSelected: false, // 현재 선택된 상태 시뮬레이션 - }, -]; - -// [4]: 목업 데이터: 환승 경로 데이터 (시안 기반) -export const MOCK_TRANSFER_ROUTES = [ - { - id: 1, - name: '안가연', - startStation: '홍대입구역', - lines: ['1', '2'], // 1호선 -> 2호선 - time: '30분', - }, - { - id: 2, - name: '안가연', - startStation: '홍대입구역', - lines: ['1', '2'], - time: '30분', - }, - { - id: 3, - name: '안가연', - startStation: '홍대입구역', - lines: ['1', '2'], - time: '30분', - }, - { - id: 4, - name: '안가연', - startStation: '홍대입구역', - lines: ['1', '2'], - time: '30분', - }, -]; - -// [5]: 목업 데이터: 합정역 데이터와 실제 3개의 역(길동역, 월드컵경기장역, 사당역)에서 서울역을 가는 실제 지하철 경로 - -export const HAPJUNG_STATION = { name: '합정역', latitude: 37.548927, longitude: 126.91353 }; - -export const REAL_SUBWAY_PATHS = [ - { - id: 1, - name: '안', - originName: '길동역', - time: '48분', - color: '#8B5CF6', // 5호선 보라색 계열 유지 - // 경로: 길동(5) -> 왕십리 -> 공덕(6호선 환승) -> 합정 - stations: [ - { name: '길동역', latitude: 37.5378, longitude: 127.14 }, - { name: '강동역', latitude: 37.5358, longitude: 127.1324 }, - { name: '천호역', latitude: 37.5385, longitude: 127.1239 }, - { name: '광나루역', latitude: 37.5453, longitude: 127.1035 }, - { name: '아차산역', latitude: 37.552, longitude: 127.0895 }, - { name: '군자역', latitude: 37.5571, longitude: 127.0794 }, - { name: '장한평역', latitude: 37.5614, longitude: 127.0646 }, - { name: '답십리역', latitude: 37.5669, longitude: 127.0527 }, - { name: '마장역', latitude: 37.5661, longitude: 127.0429 }, - { name: '왕십리역', latitude: 37.5612, longitude: 127.0374 }, - { name: '행당역', latitude: 37.5574, longitude: 127.0296 }, - { name: '신금호역', latitude: 37.5545, longitude: 127.0207 }, - { name: '청구역', latitude: 37.5602, longitude: 127.0138 }, - { name: '동대문역사문화공원역', latitude: 37.5651, longitude: 127.0078 }, - { name: '을지로4가역', latitude: 37.5666, longitude: 126.998 }, - { name: '을지로3가역', latitude: 37.5663, longitude: 126.9916 }, - { name: '광화문역', latitude: 37.571, longitude: 126.9768 }, - { name: '서대문역', latitude: 37.5657, longitude: 126.9666 }, - { name: '충정로역', latitude: 37.5599, longitude: 126.9637 }, - { name: '애오개역', latitude: 37.5535, longitude: 126.9566 }, - { name: '공덕역', latitude: 37.5432, longitude: 126.9516 }, // 6호선 환승 가정 - { name: '대흥역', latitude: 37.5477, longitude: 126.9424 }, - { name: '광흥창역', latitude: 37.5475, longitude: 126.9319 }, - { name: '상수역', latitude: 37.5477, longitude: 126.9229 }, - { name: '합정역', latitude: 37.548927, longitude: 126.91353 }, - ], - }, - { - id: 2, - name: '김', - originName: '월드컵경기장역', - time: '7분', - color: '#059669', // 6호선 라인이나 구분을 위한 초록색 유지 - // 경로: 월드컵경기장(6) -> 합정 (직통) - stations: [ - { name: '월드컵경기장역', latitude: 37.5695, longitude: 126.8993 }, - { name: '마포구청역', latitude: 37.5635, longitude: 126.9033 }, - { name: '망원역', latitude: 37.556, longitude: 126.91 }, - { name: '합정역', latitude: 37.548927, longitude: 126.91353 }, - ], - }, - { - id: 3, - name: '이', - originName: '사당역', - time: '22분', - color: '#3B82F6', // 2호선이지만 구분을 위한 파란색 유지 - // 경로: 사당(2) -> 대림 -> 신도림 -> 합정 (2호선 내선순환) - stations: [ - { name: '사당역', latitude: 37.4765, longitude: 126.9816 }, - { name: '낙성대역', latitude: 37.4769, longitude: 126.9637 }, - { name: '서울대입구역', latitude: 37.4812, longitude: 126.9527 }, - { name: '봉천역', latitude: 37.4824, longitude: 126.9417 }, - { name: '신림역', latitude: 37.4842, longitude: 126.9296 }, - { name: '신대방역', latitude: 37.4875, longitude: 126.9131 }, - { name: '구로디지털단지역', latitude: 37.4852, longitude: 126.9016 }, - { name: '대림역', latitude: 37.4925, longitude: 126.895 }, - { name: '신도림역', latitude: 37.5087, longitude: 126.8913 }, - { name: '문래역', latitude: 37.5175, longitude: 126.8948 }, - { name: '영등포구청역', latitude: 37.5257, longitude: 126.8966 }, - { name: '당산역', latitude: 37.5349, longitude: 126.9025 }, - { name: '합정역', latitude: 37.548927, longitude: 126.91353 }, - ], - }, -]; - -// [6] 목업 데이터: 추천 장소 리스트 데이터 (이미지 기반) -export const MOCK_RECOMMEND_PLACES = [ - { - id: 1, - name: '조선옥', - category: '식당', - description: '연탄불 한우갈비 전문점', - phone: '0507-1327-3659', - address: '서울특별시 중구 을지로 3가 229-1', - roadAddress: '서울특별시 중구 을지로15길 6-5', - latitude: 37.5552, - longitude: 126.9715, - }, - { - id: 2, - name: '스타벅스', - category: '카페', - description: '분위기 좋은 넓은 카페', - phone: '1522-3232', - address: '서울특별시 마포구 양화로 100', - roadAddress: '서울특별시 마포구 양화로 100', - latitude: 37.5538, - longitude: 126.9725, - }, - { - id: 3, - name: '문화역서울284', - category: '놀거리', - description: '복합문화예술공간', - phone: '02-3407-3500', - address: '서울특별시 중구 통일로 1', - roadAddress: '서울특별시 중구 통일로 1', - latitude: 37.5565, - longitude: 126.971, - }, -]; From 2cd537c875d8c82874e9baa11597a58c80e182f8 Mon Sep 17 00:00:00 2001 From: Shineast Date: Tue, 10 Feb 2026 15:53:41 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20=EC=A4=91=EA=B0=84=EC=A7=80=EC=A0=90?= =?UTF-8?q?=20=EA=B2=BD=EB=A1=9C=EC=9D=98=20=EC=B6=9C=EB=B0=9C=EC=A7=80/?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=A0=95=EB=B3=B4=20UI=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/join/joinForm.tsx | 25 ++++++++---- components/map/kakaoMapLine.tsx | 67 +++++++++++++-------------------- 2 files changed, 44 insertions(+), 48 deletions(-) diff --git a/components/join/joinForm.tsx b/components/join/joinForm.tsx index 871fa0c..4c91fb1 100644 --- a/components/join/joinForm.tsx +++ b/components/join/joinForm.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useRouter, useSearchParams } from 'next/navigation'; // [추가] useSearchParams +import { useRouter, useSearchParams } from 'next/navigation'; import { useState, useEffect } from 'react'; import { useEnterParticipant } from '@/hooks/api/mutation/useEnterParticipant'; import { useToast } from '@/hooks/useToast'; @@ -14,7 +14,7 @@ interface JoinFormProps { export default function JoinForm({ meetingId }: JoinFormProps) { const router = useRouter(); - const searchParams = useSearchParams(); // [추가] 쿼리 스트링 읽기용 훅 + const searchParams = useSearchParams(); // meetingId는 부모(Page)에서 props로 전달받음 const { isLogin, isChecking } = useIsLoggedIn(meetingId); @@ -22,7 +22,7 @@ export default function JoinForm({ meetingId }: JoinFormProps) { const [name, setName] = useState(''); const [password, setPassword] = useState(''); const [isRemembered, setIsRemembered] = useState(true); - const [errorMessage, setErrorMessage] = useState(''); // [수정] string 타입으로 초기화 + const [errorMessage, setErrorMessage] = useState(''); const participantEnter = useEnterParticipant(); const { isVisible, show } = useToast(); @@ -68,7 +68,7 @@ export default function JoinForm({ meetingId }: JoinFormProps) { if (!isFormValid || !meetingId) return; try { - // @ts-ignore (혹시 모를 타입 불일치 방지, API 스펙에 따라 제거 가능) + // @ts-ignore const result = await participantEnter.mutateAsync({ meetingId, data: { @@ -77,15 +77,26 @@ export default function JoinForm({ meetingId }: JoinFormProps) { }, }); + // 200 OK (성공)일 때만 여기 실행 if (result.success) { setMeetingUserId(meetingId, name, isRemembered); router.push(`/meeting/${meetingId}`); + } + } catch (error: any) { + // ⭐ [핵심] 400 에러가 뜨면 바로 여기로 옴 + // Axios 에러 객체 안에 서버가 보낸 JSON 데이터가 들어있음 + console.log('에러 raw 데이터 확인', error); + const errorResponse = error.response?.data; + + console.log('에러 데이터 확인:', errorResponse); // 디버깅용 로그 + + // 2. 코드가 없으면 메시지 확인 + if (errorResponse?.message) { + setErrorMessage(errorResponse.message); } else { setErrorMessage('모임 참여에 실패했습니다. 다시 시도해주세요.'); - show(); } - } catch (error) { - setErrorMessage('모임 참여에 실패했습니다. 이름과 비밀번호를 확인해주세요.'); + show(); } }; diff --git a/components/map/kakaoMapLine.tsx b/components/map/kakaoMapLine.tsx index fcd34db..2ba76bb 100644 --- a/components/map/kakaoMapLine.tsx +++ b/components/map/kakaoMapLine.tsx @@ -52,7 +52,6 @@ export default function KakaoMapLine({ }: KakaoMapLineProps) { const router = useRouter(); const [map, setMap] = useState(null); - const [hoveredUserId, setHoveredUserId] = useState(null); useEffect(() => { if (!map || !endStation || userRoutes.length === 0) return; @@ -83,13 +82,11 @@ export default function KakaoMapLine({ let meetingType = ''; let category = ''; - // 🔥 1순위: localStorage에서 가져오기 if (typeof window !== 'undefined') { meetingType = localStorage.getItem(`meeting_${meetingId}_meetingType`) || ''; category = localStorage.getItem(`meeting_${meetingId}_category`) || ''; } - // 🔥 2순위: localStorage에 없으면 purposes에서 가져오기 (fallback) if (!meetingType && purposes && purposes.length > 0) { meetingType = purposes[0]; } @@ -97,9 +94,6 @@ export default function KakaoMapLine({ category = purposes[purposes.length - 1]; } - console.log('🔍 meetingType:', meetingType); - console.log('🔍 category:', category); - const params = new URLSearchParams({ meetingId, midPlace: endStation.name, @@ -107,14 +101,9 @@ export default function KakaoMapLine({ lng: endStation.longitude.toString(), }); - if (meetingType) { - params.append('meetingType', meetingType); - } - if (category) { - params.append('category', category); - } + if (meetingType) params.append('meetingType', meetingType); + if (category) params.append('category', category); - console.log('🔍 final URL:', `/recommend?${params.toString()}`); router.push(`/recommend?${params.toString()}`); }; @@ -136,10 +125,11 @@ export default function KakaoMapLine({ level={8} onCreate={setMap} > + {/* 도착지 마커 */}
{endStation.name} @@ -147,7 +137,6 @@ export default function KakaoMapLine({ {userRoutes.map((userRoute, index) => { - const isHovered = hoveredUserId === userRoute.nickname; const userColor = getRandomHexColor(userRoute.nickname); const offsetMultiplier = index - (userRoutes.length - 1) / 2; @@ -181,36 +170,32 @@ export default function KakaoMapLine({ /> )} - -
setHoveredUserId(userRoute.nickname)} - onMouseLeave={() => setHoveredUserId(null)} - > -
- - {userRoute.startStation} ({userRoute.travelTime}분) + {/* 출발지 마커 & 정보창 (항상 표시) */} + +
+ {/* 1. 상단 정보 말풍선 (검은색 박스) */} +
+ + {userRoute.startStation}역에서 + + + {userRoute.travelTime}분 -
+ + {/* 말풍선 꼬리 (아래쪽 화살표) */} +
+ {/* 2. 하단 원형 프로필 아이콘 */}
- + {userRoute.nickname.charAt(0)}
From 70c6ef941bbdfeee0a4a693db3fe0e91892d40ea Mon Sep 17 00:00:00 2001 From: Shineast Date: Tue, 10 Feb 2026 15:58:32 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=EB=AA=A8=EC=9E=84=20=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=98=A4=EB=A5=98=20=EC=8B=9C=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EC=84=9C=EB=B2=84=EB=A9=94=EC=84=B8?= =?UTF-8?q?=EC=A7=80=20=EC=95=8C=EB=A6=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/join/joinForm.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/components/join/joinForm.tsx b/components/join/joinForm.tsx index 4c91fb1..a1da9ce 100644 --- a/components/join/joinForm.tsx +++ b/components/join/joinForm.tsx @@ -77,22 +77,20 @@ export default function JoinForm({ meetingId }: JoinFormProps) { }, }); - // 200 OK (성공)일 때만 여기 실행 + // 1. HTTP 200 OK지만 논리적 실패인 경우 (success: false) if (result.success) { setMeetingUserId(meetingId, name, isRemembered); router.push(`/meeting/${meetingId}`); + } else { + setErrorMessage(result.data.message || '모임 참여에 실패했습니다. 다시 시도해주세요.'); + show(); } } catch (error: any) { - // ⭐ [핵심] 400 에러가 뜨면 바로 여기로 옴 - // Axios 에러 객체 안에 서버가 보낸 JSON 데이터가 들어있음 - console.log('에러 raw 데이터 확인', error); - const errorResponse = error.response?.data; - - console.log('에러 데이터 확인:', errorResponse); // 디버깅용 로그 + const errorData = error.data || error.response?.data; + const serverMessage = errorData?.message; - // 2. 코드가 없으면 메시지 확인 - if (errorResponse?.message) { - setErrorMessage(errorResponse.message); + if (serverMessage) { + setErrorMessage(serverMessage); } else { setErrorMessage('모임 참여에 실패했습니다. 다시 시도해주세요.'); } From b3dbe93a2e59dd975089c734227929dfa8df3d54 Mon Sep 17 00:00:00 2001 From: Shineast Date: Tue, 10 Feb 2026 16:11:51 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20=EC=A3=BC=EB=B3=80=20=EC=9E=A5?= =?UTF-8?q?=EC=86=8C=20=EC=B6=94=EC=B2=9C=20=EB=B2=84=ED=8A=BC=20=EC=A4=84?= =?UTF-8?q?=EB=B0=94=EA=BF=88=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/map/kakaoMapLine.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/map/kakaoMapLine.tsx b/components/map/kakaoMapLine.tsx index 2ba76bb..14627ef 100644 --- a/components/map/kakaoMapLine.tsx +++ b/components/map/kakaoMapLine.tsx @@ -208,7 +208,7 @@ export default function KakaoMapLine({