-
Notifications
You must be signed in to change notification settings - Fork 1
✨Feat: 카카오맵 수정 및 AI 코스 추천/코스 세션 조회 api 연결 #109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 13 commits
6296b1a
339d762
ed53c8e
9c0a57a
c8f80fe
120cc93
902a002
41bb2fb
5de0d5b
ac7f16b
8acf7e9
7169487
1817266
9916376
1332fc2
24d75f3
07c8f49
4acf746
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,80 +2,95 @@ import Image from 'next/image'; | |||||||||||||||||||||||||||||||||||||||||||
| import { cn } from '@/shared/lib'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { ControlBar } from '@/shared/components'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { BottomNav } from '@/shared/components/tab/BottomNav'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||
| purposes, | ||||||||||||||||||||||||||||||||||||||||||||
| stays, | ||||||||||||||||||||||||||||||||||||||||||||
| moves, | ||||||||||||||||||||||||||||||||||||||||||||
| } from '@/shared/constants/course/courseOptions'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { useCourseSelection } from '@/shared/hooks/useCourseSelection'; | ||||||||||||||||||||||||||||||||||||||||||||
| import CourseSelectSection from '@/pages/map/components/CourseSelectSection'; | ||||||||||||||||||||||||||||||||||||||||||||
| import CourseInputSection from '@/pages/map/components/CourseInputSection'; | ||||||||||||||||||||||||||||||||||||||||||||
| import CourseSelectGroup from '@/shared/components/map/components/CourseSelectGroup'; | ||||||||||||||||||||||||||||||||||||||||||||
| import CourseInputSection from '@/shared/components/map/components/CourseInputSection'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { useRouter } from 'next/router'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { useRecommendCourse } from '@/shared/api/course/queries/useRecommendCourse'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| export default function CourseSettingPage() { | ||||||||||||||||||||||||||||||||||||||||||||
| const router = useRouter(); | ||||||||||||||||||||||||||||||||||||||||||||
| const { purpose, setPurpose, stay, setStay, move, setMove } = | ||||||||||||||||||||||||||||||||||||||||||||
| useCourseSelection(); | ||||||||||||||||||||||||||||||||||||||||||||
| const { mutate, isPending } = useRecommendCourse(); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const { purpose, setPurpose, stay, setStay, move, setMove } = useCourseSelection(); | ||||||||||||||||||||||||||||||||||||||||||||
| const [mustVisitPlace, setMustVisitPlace] = useState(''); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const canProceed = Boolean(purpose && stay && move); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const handleNext = () => { | ||||||||||||||||||||||||||||||||||||||||||||
| if (canProceed) router.push('/map/result'); | ||||||||||||||||||||||||||||||||||||||||||||
| if (!canProceed) return; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| mutate( | ||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||
| travelPurpose: purpose!, | ||||||||||||||||||||||||||||||||||||||||||||
| stayDuration: stay!, | ||||||||||||||||||||||||||||||||||||||||||||
| transportation: move!, | ||||||||||||||||||||||||||||||||||||||||||||
| userLatitude: 37.4985, | ||||||||||||||||||||||||||||||||||||||||||||
| userLongitude: 126.7822, | ||||||||||||||||||||||||||||||||||||||||||||
| mustVisitPlace: mustVisitPlace || '', | ||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||
| onSuccess: (res) => { | ||||||||||||||||||||||||||||||||||||||||||||
| if (res.success) { | ||||||||||||||||||||||||||||||||||||||||||||
| router.push(`/map/result?sessionId=${res.data.sessionId}`); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||
| onError: (err) => { | ||||||||||||||||||||||||||||||||||||||||||||
| console.error('AI 코스 추천 실패:', err); | ||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+34
to
+42
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 에러 및 실패 응답 처리 부족 현재 에러 처리에 두 가지 문제가 있습니다:
다음과 같이 수정하세요: onSuccess: (res) => {
if (res.isSuccess) {
router.push(`/map/result?sessionId=${res.result.sessionId}`);
+ } else {
+ alert('코스 추천에 실패했습니다. 다시 시도해 주세요.');
}
},
onError: (err) => {
console.error('AI 코스 추천 실패:', err);
+ alert('네트워크 오류가 발생했습니다. 다시 시도해 주세요.');
},더 나은 UX를 위해 toast 라이브러리나 모달을 사용하는 것을 권장합니다. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||
| className={cn( | ||||||||||||||||||||||||||||||||||||||||||||
| 'relative px-[2.4rem] bg-white flex flex-col h-full pt-[1.3rem] pb-[12rem]', | ||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||
| role='form' | ||||||||||||||||||||||||||||||||||||||||||||
| aria-labelledby='course-setting-title' | ||||||||||||||||||||||||||||||||||||||||||||
| aria-describedby='course-setting-desc' | ||||||||||||||||||||||||||||||||||||||||||||
| role="form" | ||||||||||||||||||||||||||||||||||||||||||||
| aria-labelledby="course-setting-title" | ||||||||||||||||||||||||||||||||||||||||||||
| aria-describedby="course-setting-desc" | ||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||
| <ControlBar className='fixed top-[1rem] left-0 right-0 z-50 px-[2rem]' /> | ||||||||||||||||||||||||||||||||||||||||||||
| <ControlBar className="fixed top-[1rem] left-0 right-0 z-50 px-[2rem]" /> | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| <main | ||||||||||||||||||||||||||||||||||||||||||||
| className='w-full pt-[3.4rem] flex flex-col overflow-auto' | ||||||||||||||||||||||||||||||||||||||||||||
| aria-live='polite' | ||||||||||||||||||||||||||||||||||||||||||||
| className="w-full pt-[3.4rem] flex flex-col overflow-auto" | ||||||||||||||||||||||||||||||||||||||||||||
| aria-live="polite" | ||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||
| <section className='mb-[3.6rem] text-center'> | ||||||||||||||||||||||||||||||||||||||||||||
| <h1 id='course-setting-title' className='sr-only'> | ||||||||||||||||||||||||||||||||||||||||||||
| <section className="mb-[3.6rem] text-center"> | ||||||||||||||||||||||||||||||||||||||||||||
| <h1 id="course-setting-title" className="sr-only"> | ||||||||||||||||||||||||||||||||||||||||||||
| 여행 코스 설정 | ||||||||||||||||||||||||||||||||||||||||||||
| </h1> | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| <Image | ||||||||||||||||||||||||||||||||||||||||||||
| src='/assets/bannerMap.svg' | ||||||||||||||||||||||||||||||||||||||||||||
| alt='여행 코스 추천 배너 이미지' | ||||||||||||||||||||||||||||||||||||||||||||
| src="/assets/bannerMap.svg" | ||||||||||||||||||||||||||||||||||||||||||||
| alt="여행 코스 추천 배너 이미지" | ||||||||||||||||||||||||||||||||||||||||||||
| width={354} | ||||||||||||||||||||||||||||||||||||||||||||
| height={79} | ||||||||||||||||||||||||||||||||||||||||||||
| className='w-full h-auto object-cover block' | ||||||||||||||||||||||||||||||||||||||||||||
| className="w-full h-auto object-cover block" | ||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||
| <p id='course-setting-desc' className='sr-only'> | ||||||||||||||||||||||||||||||||||||||||||||
| 여행 목적, 체류 시간, 이동 방식을 선택하고, 원하는 장소를 입력할 수 | ||||||||||||||||||||||||||||||||||||||||||||
| 있습니다. | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| <p id="course-setting-desc" className="sr-only"> | ||||||||||||||||||||||||||||||||||||||||||||
| 여행 목적, 체류 시간, 이동 방식을 선택하고, 원하는 장소를 입력할 수 있습니다. | ||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||
| </section> | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| <div className='flex flex-col gap-[1.9rem] mb-[8rem]'> | ||||||||||||||||||||||||||||||||||||||||||||
| <CourseSelectSection | ||||||||||||||||||||||||||||||||||||||||||||
| title='여행 목적을 선택해 주세요' | ||||||||||||||||||||||||||||||||||||||||||||
| options={purposes} | ||||||||||||||||||||||||||||||||||||||||||||
| selected={purpose} | ||||||||||||||||||||||||||||||||||||||||||||
| onSelect={setPurpose} | ||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||
| <CourseSelectSection | ||||||||||||||||||||||||||||||||||||||||||||
| title='체류 시간을 선택해 주세요' | ||||||||||||||||||||||||||||||||||||||||||||
| options={stays} | ||||||||||||||||||||||||||||||||||||||||||||
| selected={stay} | ||||||||||||||||||||||||||||||||||||||||||||
| onSelect={setStay} | ||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||
| <CourseSelectSection | ||||||||||||||||||||||||||||||||||||||||||||
| title='이동 방식을 선택해 주세요' | ||||||||||||||||||||||||||||||||||||||||||||
| options={moves} | ||||||||||||||||||||||||||||||||||||||||||||
| selected={move} | ||||||||||||||||||||||||||||||||||||||||||||
| onSelect={setMove} | ||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||
| <CourseInputSection onNext={handleNext} /> | ||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||
| <CourseSelectGroup | ||||||||||||||||||||||||||||||||||||||||||||
| purpose={purpose} | ||||||||||||||||||||||||||||||||||||||||||||
| stay={stay} | ||||||||||||||||||||||||||||||||||||||||||||
| move={move} | ||||||||||||||||||||||||||||||||||||||||||||
| setPurpose={setPurpose} | ||||||||||||||||||||||||||||||||||||||||||||
| setStay={setStay} | ||||||||||||||||||||||||||||||||||||||||||||
| setMove={setMove} | ||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| <CourseInputSection | ||||||||||||||||||||||||||||||||||||||||||||
| value={mustVisitPlace} | ||||||||||||||||||||||||||||||||||||||||||||
| onChange={setMustVisitPlace} | ||||||||||||||||||||||||||||||||||||||||||||
| onNext={handleNext} | ||||||||||||||||||||||||||||||||||||||||||||
| isLoading={isPending} | ||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||
| </main> | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| <BottomNav /> | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| 'use client'; | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| import { useRouter } from 'next/router'; | ||
| import Image from 'next/image'; | ||
| import { Header, LocationCard, AddressCopy } from '@/shared/components'; | ||
|
|
||
| export default function LocationPage() { | ||
| const router = useRouter(); | ||
| const { name, imageSrc, description, address } = router.query; | ||
|
|
||
| if (!router.isReady || !name) { | ||
| return <p className="text-center mt-10">불러오는 중...</p>; | ||
| } | ||
|
|
||
| return ( | ||
| <div className="relative w-full h-[100vh] overflow-auto px-[2.4rem]"> | ||
| <Header title={String(name)} onClick={() => router.back()} /> | ||
|
|
||
| <main className="relative pt-[14.3rem] gap-[1.2rem] flex flex-col"> | ||
| <Image | ||
| src={typeof imageSrc === 'string' ? imageSrc : '/assets/board.svg'} | ||
| alt={String(name)} | ||
| width={354} | ||
| height={436} | ||
| className="w-full h-auto object-cover block rounded-[16px]" | ||
| /> | ||
|
|
||
| <LocationCard | ||
| name={String(name)} | ||
| address={String(address)} | ||
| description={String(description)} | ||
| variant="mint" | ||
| size="large" | ||
| /> | ||
|
|
||
| <AddressCopy variant="mint" value={String(address)} /> | ||
| </main> | ||
| </div> | ||
| ); | ||
| } | ||
This file was deleted.
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
하드코딩된 좌표 사용
사용자 위치 대신 고정된 좌표
(37.4985, 126.7822)를 전송하고 있어, 모든 사용자에게 동일한 기준점에서 코스가 추천됩니다. 이는 사용자의 실제 위치 기반 추천이라는 기능 의도와 맞지 않습니다.다음 중 하나를 선택하세요:
방안 1 (권장): Geolocation API로 실제 사용자 위치 사용
방안 2: 하드코딩된 값이 의도된 것이라면 상수로 분리하고 주석 추가
🤖 Prompt for AI Agents