diff --git a/solution/frontend/src/features/onboarding/Step2PlaceSearch.tsx b/solution/frontend/src/features/onboarding/Step2PlaceSearch.tsx index e75cb7b..00f42d4 100644 --- a/solution/frontend/src/features/onboarding/Step2PlaceSearch.tsx +++ b/solution/frontend/src/features/onboarding/Step2PlaceSearch.tsx @@ -1,7 +1,7 @@ import {useState} from 'react'; import {useSearchParams} from 'react-router'; import {ArrowRight, Check, MapPin, Phone, Search, TriangleAlert} from 'lucide-react'; -import type {PlaceCandidate} from '@/api'; +import {getAccessToken, type PlaceCandidate} from '@/api'; import {Badge} from '@/components/ui/badge'; import {Button} from '@/components/ui/button'; import {Input} from '@/components/ui/input'; @@ -83,6 +83,14 @@ export function Step2PlaceSearch() { const runSearch = () => { clearIdentity(); // 상호를 고쳐 다시 찾는 것이므로 앞서 확정한 신원은 물린다 setPickedIndex(null); + // ★ 로그인 전에는 서버를 부르지 않는다. 로그인은 에디터 진입에서 한 번 받는 것이 이 앱의 흐름인데, + // 장소 API 는 전부 토큰을 요구해서(place.py) 여기서 부르면 2단계가 로그인 벽이 된다. + // 입력한 값으로 신원을 세우고 넘어간다 — 검증은 로그인 뒤에 다시 할 수 있다. + if (!getAccessToken()) { + confirmIdentity(search.confirmManual(storeName, location)); + goToStep(3); + return; + } void search.search(storeName, location); }; @@ -113,6 +121,12 @@ export function Step2PlaceSearch() { const pickByUrl = async () => { const url = placeUrl.trim(); if (!url) return; + // URL 확인도 서버가 토큰을 요구한다 — 로그인 전에는 입력값으로 넘어간다. + if (!getAccessToken()) { + confirmIdentity(search.confirmManual(storeName, location)); + goToStep(3); + return; + } const identity = await search.confirmByUrl(url); if (!identity) return; confirmIdentity(identity); diff --git a/solution/frontend/src/features/onboarding/usePlaceSearch.ts b/solution/frontend/src/features/onboarding/usePlaceSearch.ts index 1d0a5f3..609cd07 100644 --- a/solution/frontend/src/features/onboarding/usePlaceSearch.ts +++ b/solution/frontend/src/features/onboarding/usePlaceSearch.ts @@ -132,15 +132,10 @@ export function usePlaceSearch(industry: IndustryType, existingPlaceId: string | // 누른 검색이 토큰 없이 나가 '로그인 만료'로 떨어진다 — 만료가 아니라 경합이다. await ensureAutoSession(); - // ★ '만료' 가 아니라 **아직 로그인한 적이 없는 것**이다(자동 로그인 계정이 없으면 - // ensureAutoSession 이 즉시 끝난다). 지도 검색·URL 확인 모두 서버가 토큰을 요구하므로 - // (place.py 전 엔드포인트가 IsValidAccessToken) 이 단계는 로그인 없이는 지나갈 수 없다. + // ★ 토큰이 없으면 화면(Step2)이 검색을 부르지 않고 입력값으로 넘어간다 — 2단계는 로그인 벽이 아니다. + // 그래도 여기 도달했다면 세션이 도중에 끊긴 것이므로, 로그인을 요구하지 말고 조용히 물러난다. if (!getAccessToken()) { - setState({ - ...INITIAL, - phase: 'unavailable', - unavailableReason: '내 가게 확인은 로그인이 필요합니다. 로그인한 뒤 다시 검색해 주세요.', - }); + setState({...INITIAL}); return; }