From d18130ebbc8b9574dd764a63146ab52e13d9acf8 Mon Sep 17 00:00:00 2001 From: MJY Date: Thu, 2 Apr 2026 08:17:40 +0900 Subject: [PATCH 1/6] feat(ui): polish map and auth screens --- src/app/admin/places/page.tsx | 16 +- src/app/admin/prices/page.tsx | 10 +- src/app/admin/prices/places/[id]/page.tsx | 6 +- src/app/admin/reports/page.tsx | 10 +- src/app/api/auth/signup/route.ts | 10 +- src/app/bookmarks/page.tsx | 5 +- src/app/globals.css | 50 ++++- src/app/layout.tsx | 22 -- src/app/map/page.tsx | 189 +++++------------- src/app/page.tsx | 1 - src/app/place/[id]/page.tsx | 33 ++- src/app/report/page.tsx | 32 ++- src/app/signup/page.tsx | 9 - src/app/submit/page.tsx | 35 ++-- src/components/brand-mark.tsx | 5 - src/features/auth/login-form.tsx | 2 +- src/features/auth/repository.ts | 6 +- src/features/auth/session-action-group.tsx | 70 +++++++ src/features/auth/signup-form.tsx | 13 +- .../places/admin-place-coordinate-picker.tsx | 2 +- .../places/admin-place-review-form.tsx | 5 + src/features/places/map-explorer.tsx | 14 +- 22 files changed, 261 insertions(+), 284 deletions(-) create mode 100644 src/features/auth/session-action-group.tsx diff --git a/src/app/admin/places/page.tsx b/src/app/admin/places/page.tsx index 77150aa..9a3e7fb 100644 --- a/src/app/admin/places/page.tsx +++ b/src/app/admin/places/page.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import { redirect } from "next/navigation"; import { AccessDeniedPanel } from "@/components/access-denied-panel"; +import { SessionActionGroup } from "@/features/auth/session-action-group"; import { getCategoryBySlug } from "@/features/categories/catalog"; import { AdminPlaceReviewForm } from "@/features/places/admin-place-review-form"; import { formatKrw } from "@/features/places/queries"; @@ -24,7 +25,7 @@ export default async function AdminPlacesPage() { if (user.role !== "admin") { return (

- Admin + 운영

신규 장소 승인 큐

- 등록 폼으로 들어온 장소 제보를 검토합니다. 제출된 좌표를 확인하고 - 필요하면 조정한 뒤 승인하면 바로 지도와 상세 페이지에서 확인할 수 - 있습니다. + 공개 등록 폼으로 들어온 텍스트 기반 장소 제보를 검토합니다. + 운영자가 주소와 네이버 지도 검색 결과를 참고해 좌표를 확정한 뒤 + 승인하면 바로 지도와 상세 페이지에서 확인할 수 있습니다.

@@ -65,7 +66,7 @@ export default async function AdminPlacesPage() { href="/api/admin/places" className="rounded-full border border-stone-300 bg-white px-4 py-2 text-sm text-stone-700 transition hover:bg-stone-100" > - API 보기 + 응답 보기 가격 제보 큐 +
@@ -84,7 +86,7 @@ export default async function AdminPlacesPage() { : "bg-orange-100 text-orange-700" }`} > - 데이터 소스: {result.source === "database" ? "DB" : "목업"} + 데이터 구분: {result.source === "database" ? "실데이터" : "목업"} diff --git a/src/app/admin/prices/page.tsx b/src/app/admin/prices/page.tsx index 55dfebc..87a5bd9 100644 --- a/src/app/admin/prices/page.tsx +++ b/src/app/admin/prices/page.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import { redirect } from "next/navigation"; import { AccessDeniedPanel } from "@/components/access-denied-panel"; +import { SessionActionGroup } from "@/features/auth/session-action-group"; import { AdminPriceReportReviewForm } from "@/features/places/admin-price-report-review-form"; import { formatKrw } from "@/features/places/queries"; import { @@ -26,7 +27,7 @@ export default async function AdminPricesPage() { if (user.role !== "admin") { return (

- Admin + 운영

가격 제보 검토 큐 @@ -81,8 +82,9 @@ export default async function AdminPricesPage() { href="/api/admin/prices" className="rounded-full border border-stone-300 bg-white px-4 py-2 text-sm text-stone-700 transition hover:bg-stone-100" > - API 보기 + 응답 보기 +

@@ -94,7 +96,7 @@ export default async function AdminPricesPage() { : "bg-orange-100 text-orange-700" }`} > - 데이터 소스: {result.source === "database" ? "DB" : "목업"} + 데이터 구분: {result.source === "database" ? "실데이터" : "목업"} diff --git a/src/app/admin/prices/places/[id]/page.tsx b/src/app/admin/prices/places/[id]/page.tsx index af249cb..a1884cf 100644 --- a/src/app/admin/prices/places/[id]/page.tsx +++ b/src/app/admin/prices/places/[id]/page.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import { notFound, redirect } from "next/navigation"; import { AccessDeniedPanel } from "@/components/access-denied-panel"; +import { SessionActionGroup } from "@/features/auth/session-action-group"; import { AdminPriceItemForm } from "@/features/places/admin-price-item-form"; import { formatKrw } from "@/features/places/queries"; import { getAdminPlacePriceDetail } from "@/features/places/repository"; @@ -31,7 +32,7 @@ export default async function AdminPlacePricesDetailPage({ if (user.role !== "admin") { return (

- Admin + 운영

{place.name} 가격 관리 @@ -80,6 +81,7 @@ export default async function AdminPlacePricesDetailPage({ > 장소 보기 +

diff --git a/src/app/admin/reports/page.tsx b/src/app/admin/reports/page.tsx index eeca2ed..a062ca5 100644 --- a/src/app/admin/reports/page.tsx +++ b/src/app/admin/reports/page.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import { redirect } from "next/navigation"; import { AccessDeniedPanel } from "@/components/access-denied-panel"; +import { SessionActionGroup } from "@/features/auth/session-action-group"; import { AdminReportStatusForm } from "@/features/reports/admin-report-status-form"; import { listReports } from "@/features/reports/repository"; import { @@ -33,7 +34,7 @@ export default async function AdminReportsPage() { if (user.role !== "admin") { return (

- Admin + 운영

신고 검토 큐 초안 @@ -86,8 +87,9 @@ export default async function AdminReportsPage() { href="/api/admin/reports" className="rounded-full border border-stone-300 bg-white px-4 py-2 text-sm text-stone-700 transition hover:bg-stone-100" > - API 보기 + 응답 보기 +

@@ -99,7 +101,7 @@ export default async function AdminReportsPage() { : "bg-orange-100 text-orange-700" }`} > - 데이터 소스: {result.source === "database" ? "DB" : "목업"} + 데이터 구분: {result.source === "database" ? "실데이터" : "목업"} diff --git a/src/app/api/auth/signup/route.ts b/src/app/api/auth/signup/route.ts index 775aff9..cfadab6 100644 --- a/src/app/api/auth/signup/route.ts +++ b/src/app/api/auth/signup/route.ts @@ -1,5 +1,3 @@ -import { NextResponse } from "next/server"; - import { createCredentialsUser } from "@/features/auth/repository"; import { credentialsSignupSchema } from "@/features/auth/schema"; import { consumeRateLimit } from "@/lib/rate-limit"; @@ -18,7 +16,7 @@ export async function POST(request: Request) { }); if (!rateLimit.ok) { - return NextResponse.json( + return Response.json( { ok: false, message: "회원가입 요청이 너무 빠릅니다. 잠시 후 다시 시도해주세요.", @@ -33,7 +31,7 @@ export async function POST(request: Request) { const parsed = credentialsSignupSchema.safeParse(body); if (!parsed.success) { - return NextResponse.json( + return Response.json( { ok: false, message: "회원가입 입력값 검증에 실패했습니다.", @@ -49,9 +47,9 @@ export async function POST(request: Request) { ? 201 : result.message.includes("이미 가입된 이메일") ? 409 - : result.message.includes("DB 연결") + : result.message.includes("데이터 연결") ? 503 : 500; - return NextResponse.json(result, { status }); + return Response.json(result, { status }); } diff --git a/src/app/bookmarks/page.tsx b/src/app/bookmarks/page.tsx index 836b197..ce76269 100644 --- a/src/app/bookmarks/page.tsx +++ b/src/app/bookmarks/page.tsx @@ -1,6 +1,7 @@ import Link from "next/link"; import { redirect } from "next/navigation"; +import { SessionActionGroup } from "@/features/auth/session-action-group"; import { BookmarkToggleButton } from "@/features/bookmarks/bookmark-toggle-button"; import { listBookmarks } from "@/features/bookmarks/repository"; import { getCategoryBySlug } from "@/features/categories/catalog"; @@ -49,9 +50,7 @@ export default async function BookmarksPage() { 북마크한 장소 -
- {bookmarkResult.userLabel} -
+ {bookmarkedPlaces.length > 0 ? ( diff --git a/src/app/globals.css b/src/app/globals.css index 0db10b4..32ad822 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -32,13 +32,18 @@ } .altteulmap-accent-chip { - border-color: var(--altteul-accent-border); - background-color: var(--altteul-accent-soft); - color: var(--altteul-accent-text); + border-color: #9c5934; + background: linear-gradient(180deg, #cb7c4d 0%, #b96b41 100%); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.18), + 0 0 0 1px rgba(156, 89, 52, 0.12); + color: #fff; } .altteulmap-accent-chip:hover { - background-color: var(--altteul-accent-soft-hover); + border-color: #8f522f; + background: linear-gradient(180deg, #bf7145 0%, #a95f38 100%); + color: #fff; } .altteulmap-accent-panel { @@ -105,11 +110,12 @@ .altteulmap-chip { border-radius: 0.9rem; - background-color: rgba(255, 251, 247, 0.94); + background-color: rgba(255, 255, 255, 0.96); transition: border-color 160ms ease, background-color 160ms ease, - color 160ms ease; + color 160ms ease, + box-shadow 160ms ease; } .altteulmap-chip:hover { @@ -117,6 +123,38 @@ background-color: var(--altteul-surface-fill-hover); } +.altteulmap-scope-chip { + border: 1px solid rgba(186, 173, 161, 0.75); + background-color: rgba(255, 255, 255, 0.96); + color: rgb(68, 64, 60); +} + +.altteulmap-scope-input:hover + .altteulmap-scope-chip { + border-color: var(--altteul-surface-border-strong); + background-color: var(--altteul-surface-fill-hover); +} + +.altteulmap-scope-input:checked + .altteulmap-scope-chip { + border-color: #9c5934; + background: linear-gradient(180deg, #cb7c4d 0%, #b96b41 100%); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.18), + 0 0 0 1px rgba(156, 89, 52, 0.14); + color: #fff; +} + +.altteulmap-scope-input:focus-visible + .altteulmap-scope-chip { + outline: none; + box-shadow: 0 0 0 3px var(--altteul-focus-ring); +} + +.altteulmap-scope-input:checked:focus-visible + .altteulmap-scope-chip { + box-shadow: + 0 0 0 3px var(--altteul-focus-ring), + inset 0 1px 0 rgba(255, 255, 255, 0.18), + 0 0 0 1px rgba(156, 89, 52, 0.14); +} + .altteulmap-badge { border-radius: 0.9rem; border: 1px solid rgba(189, 164, 143, 0.46); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index ece809c..aa9f6d8 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -15,28 +15,6 @@ export const metadata: Metadata = { template: `%s | ${siteName}`, }, description: siteDescription, - applicationName: siteName, - keywords: [ - "알뜰맵", - "가성비 지도", - "저렴한 식당", - "생활비 절약", - "학생 식당", - "문구 프린트", - ], - openGraph: { - type: "website", - locale: "ko_KR", - siteName, - title: siteName, - description: siteDescription, - url: "/", - }, - twitter: { - card: "summary", - title: siteName, - description: siteDescription, - }, }; export default function RootLayout({ diff --git a/src/app/map/page.tsx b/src/app/map/page.tsx index 678c00d..258583c 100644 --- a/src/app/map/page.tsx +++ b/src/app/map/page.tsx @@ -3,13 +3,14 @@ import Link from "next/link"; import { BrandMark } from "@/components/brand-mark"; import { listBookmarks } from "@/features/bookmarks/repository"; +import { SessionActionGroup } from "@/features/auth/session-action-group"; import { categoryGroups, getCategoryBySlug, } from "@/features/categories/catalog"; import { MapExplorer } from "@/features/places/map-explorer"; import { listPlaces } from "@/features/places/repository"; -import type { PlaceSearchScope, PlaceSort } from "@/features/places/types"; +import type { PlaceSearchScope } from "@/features/places/types"; import { createLoginHref, getSessionUser } from "@/lib/session"; type MapPageProps = { @@ -19,7 +20,6 @@ type MapPageProps = { export const dynamic = "force-dynamic"; export const metadata: Metadata = { - title: "지도에서 알뜰 장소 찾기", description: "주변 식당, 문구점, 프린트, 생활 서비스 가격을 지도에서 비교하고 알뜰 장소를 찾아보세요.", alternates: { @@ -34,11 +34,16 @@ const priceOptions = [ { label: "20,000원 이하", value: 20000 }, ]; -const sortOptions = [ - { label: "가격순", value: "price" }, - { label: "최근 갱신순", value: "recent" }, - { label: "좋아요순", value: "likes" }, -] as const; +const mobileFilterChipClass = + "altteulmap-chip whitespace-nowrap border px-3 py-2 text-xs transition"; +const desktopFilterChipClass = + "altteulmap-chip whitespace-nowrap border px-4 py-2 text-sm transition"; +const inactiveFilterChipClass = + "border-stone-300 bg-white text-stone-700 hover:bg-stone-100"; +const mobileScopeChipClass = + "altteulmap-chip altteulmap-scope-chip inline-flex whitespace-nowrap px-3 py-2 text-xs transition"; +const desktopScopeChipClass = + "altteulmap-chip altteulmap-scope-chip inline-flex whitespace-nowrap px-4 py-2 text-sm transition"; function getFirstValue(value: string | string[] | undefined) { return Array.isArray(value) ? value[0] : value; @@ -49,7 +54,6 @@ function createHref(params: { maxPrice?: number | null; query?: string | null; searchScope?: PlaceSearchScope; - sort?: PlaceSort | null; }) { const search = new URLSearchParams(); const trimmedQuery = params.query?.trim(); @@ -67,29 +71,11 @@ function createHref(params: { search.set("maxPrice", String(params.maxPrice)); } - if (params.sort && params.sort !== "price") { - search.set("sort", params.sort); - } - const query = search.toString(); return query ? `/?${query}` : "/"; } -function parseSort(value: string | string[] | undefined): PlaceSort { - const sort = getFirstValue(value); - - if (sort === "recent") { - return "recent"; - } - - if (sort === "likes") { - return "likes"; - } - - return "price"; -} - export default async function MapPage({ searchParams }: MapPageProps) { const params = await searchParams; const activeCategory = getFirstValue(params.category) ?? null; @@ -99,7 +85,6 @@ export default async function MapPage({ searchParams }: MapPageProps) { activeQuery && getFirstValue(params.scope) === "global" ? "global" : "viewport"; - const activeSort = parseSort(params.sort); const user = await getSessionUser(); const [result, bookmarkResult] = await Promise.all([ @@ -107,7 +92,6 @@ export default async function MapPage({ searchParams }: MapPageProps) { category: activeCategory, maxPrice: activeMaxPrice, query: activeQuery, - sort: activeSort, }), listBookmarks(user), ]); @@ -116,7 +100,6 @@ export default async function MapPage({ searchParams }: MapPageProps) { maxPrice: activeMaxPrice, query: activeQuery, searchScope: activeSearchScope, - sort: activeSort, }); const loginHref = createLoginHref(currentMapHref); const bookmarkLoginHref = createLoginHref(currentMapHref); @@ -128,13 +111,10 @@ export default async function MapPage({ searchParams }: MapPageProps) { const selectedCategory = getCategoryBySlug(activeCategory); const activePriceLabel = priceOptions.find((option) => option.value === activeMaxPrice)?.label ?? null; - const activeSortLabel = - sortOptions.find((option) => option.value === activeSort)?.label ?? "가격순"; const mobileSummaryItems = [ activeQuery ? `검색 ${activeQuery}` : null, selectedCategory?.name ?? null, activePriceLabel, - activeSort !== "price" ? activeSortLabel : null, activeSearchScope === "global" ? "전체 검색" : "현재 지도", ].filter((item): item is string => Boolean(item)); @@ -157,14 +137,11 @@ export default async function MapPage({ searchParams }: MapPageProps) { > 북마크 - {!user ? ( - - 로그인 - - ) : null} + @@ -180,7 +157,6 @@ export default async function MapPage({ searchParams }: MapPageProps) { href={createHref({ category: activeCategory, maxPrice: activeMaxPrice, - sort: activeSort, })} className="text-xs font-medium text-stone-500 transition hover:text-stone-900" > @@ -239,9 +215,9 @@ export default async function MapPage({ searchParams }: MapPageProps) { name="scope" value="viewport" defaultChecked={activeSearchScope === "viewport"} - className="peer sr-only" + className="altteulmap-scope-input sr-only" /> - + 현재 지도에서 찾기 @@ -251,46 +227,15 @@ export default async function MapPage({ searchParams }: MapPageProps) { name="scope" value="global" defaultChecked={activeSearchScope === "global"} - className="peer sr-only" + className="altteulmap-scope-input sr-only" /> - + 전체에서 찾기 -
-

- 정렬 -

-
- {sortOptions.map((option) => { - const isActive = activeSort === option.value; - - return ( - - {option.label} - - ); - })} -
-
-

가격 필터 @@ -307,12 +252,11 @@ export default async function MapPage({ searchParams }: MapPageProps) { maxPrice: option.value, query: activeQuery, searchScope: activeSearchScope, - sort: activeSort, })} - className={`altteulmap-chip whitespace-nowrap border px-3 py-2 text-xs transition ${ + className={`${mobileFilterChipClass} ${ isActive ? "altteulmap-accent-chip" - : "border border-stone-300 bg-white text-stone-700 hover:bg-stone-100" + : inactiveFilterChipClass }`} > {option.label} @@ -333,12 +277,11 @@ export default async function MapPage({ searchParams }: MapPageProps) { maxPrice: activeMaxPrice, query: activeQuery, searchScope: activeSearchScope, - sort: activeSort, })} - className={`altteulmap-chip whitespace-nowrap border px-3 py-2 text-xs transition ${ + className={`${mobileFilterChipClass} ${ !activeCategory ? "altteulmap-accent-chip" - : "border border-stone-300 bg-white text-stone-700 hover:bg-stone-100" + : inactiveFilterChipClass }`} > 전체 @@ -350,17 +293,16 @@ export default async function MapPage({ searchParams }: MapPageProps) { return ( {category.name} @@ -379,9 +321,6 @@ export default async function MapPage({ searchParams }: MapPageProps) { {activeMaxPrice ? ( ) : null} - {activeSort !== "price" ? ( - - ) : null}

@@ -395,12 +334,11 @@ export default async function MapPage({ searchParams }: MapPageProps) { maxPrice: activeMaxPrice, query: activeQuery, searchScope: activeSearchScope, - sort: activeSort, })} - className={`altteulmap-chip whitespace-nowrap border px-4 py-2 text-sm transition ${ + className={`${desktopFilterChipClass} ${ !activeCategory ? "altteulmap-accent-chip" - : "border border-stone-300 bg-white text-stone-700 hover:bg-stone-100" + : inactiveFilterChipClass }`} > 전체 @@ -417,12 +355,11 @@ export default async function MapPage({ searchParams }: MapPageProps) { maxPrice: activeMaxPrice, query: activeQuery, searchScope: activeSearchScope, - sort: activeSort, })} - className={`altteulmap-chip whitespace-nowrap border px-4 py-2 text-sm transition ${ + className={`${desktopFilterChipClass} ${ isActive ? "altteulmap-accent-chip" - : "border border-stone-300 bg-white text-stone-700 hover:bg-stone-100" + : inactiveFilterChipClass }`} > {category.name} @@ -433,7 +370,7 @@ export default async function MapPage({ searchParams }: MapPageProps) { -
+

가격 필터

@@ -448,42 +385,11 @@ export default async function MapPage({ searchParams }: MapPageProps) { maxPrice: option.value, query: activeQuery, searchScope: activeSearchScope, - sort: activeSort, - })} - className={`altteulmap-chip whitespace-nowrap border px-4 py-2 text-sm transition ${ - isActive - ? "altteulmap-accent-chip" - : "border border-stone-300 bg-white text-stone-700 hover:bg-stone-100" - }`} - > - {option.label} - - ); - })} -
-
- -
-

정렬

-
- {sortOptions.map((option) => { - const isActive = activeSort === option.value; - - return ( - {option.label} @@ -502,7 +408,6 @@ export default async function MapPage({ searchParams }: MapPageProps) { href={createHref({ category: activeCategory, maxPrice: activeMaxPrice, - sort: activeSort, })} className="altteulmap-button whitespace-nowrap border border-stone-300 bg-white px-4 py-2 text-sm text-stone-700 transition hover:bg-stone-100" > @@ -538,9 +443,9 @@ export default async function MapPage({ searchParams }: MapPageProps) { value="viewport" data-testid="search-scope-viewport" defaultChecked={activeSearchScope === "viewport"} - className="peer sr-only" + className="altteulmap-scope-input sr-only" /> - + 현재 지도에서 찾기 @@ -551,9 +456,9 @@ export default async function MapPage({ searchParams }: MapPageProps) { value="global" data-testid="search-scope-global" defaultChecked={activeSearchScope === "global"} - className="peer sr-only" + className="altteulmap-scope-input sr-only" /> - + 전체에서 찾기 @@ -565,15 +470,12 @@ export default async function MapPage({ searchParams }: MapPageProps) { {activeMaxPrice ? ( ) : null} - {activeSort !== "price" ? ( - - ) : null}
diff --git a/src/app/page.tsx b/src/app/page.tsx index 3ebe8a7..8d7c16b 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -9,7 +9,6 @@ type HomePageProps = { export const dynamic = "force-dynamic"; export const metadata: Metadata = { - title: "지도에서 알뜰 장소 찾기", description: "주변 식당, 문구점, 프린트, 생활 서비스 가격을 지도에서 비교하고 알뜰 장소를 찾아보세요.", alternates: { diff --git a/src/app/place/[id]/page.tsx b/src/app/place/[id]/page.tsx index 30caa34..ed80ff8 100644 --- a/src/app/place/[id]/page.tsx +++ b/src/app/place/[id]/page.tsx @@ -2,6 +2,7 @@ import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; +import { SessionActionGroup } from "@/features/auth/session-action-group"; import { BookmarkToggleButton } from "@/features/bookmarks/bookmark-toggle-button"; import { listBookmarks } from "@/features/bookmarks/repository"; import { getCategoryBySlug } from "@/features/categories/catalog"; @@ -53,17 +54,6 @@ export async function generateMetadata({ alternates: { canonical: `/place/${place.id}`, }, - openGraph: { - type: "article", - title, - description, - url: `/place/${place.id}`, - }, - twitter: { - card: "summary", - title, - description, - }, }; } @@ -98,19 +88,28 @@ export default async function PlacePage({ params }: PlacePageProps) { const isBookmarked = bookmarkResult.items.some( (bookmark) => bookmark.placeId === place.id, ); + const placePath = `/place/${place.id}`; const bookmarkLoginHref = createLoginHref(`/place/${place.id}`); + const loginHref = createLoginHref(placePath); const reportPath = `/report?placeId=${place.id}&placeName=${encodeURIComponent(place.name)}`; const reportHref = reportPath; return (
- - 목록으로 돌아가기 - +
+ + 목록으로 돌아가기 + + +
diff --git a/src/app/report/page.tsx b/src/app/report/page.tsx index fee5f3a..06a64c4 100644 --- a/src/app/report/page.tsx +++ b/src/app/report/page.tsx @@ -1,5 +1,9 @@ +import { SessionActionGroup } from "@/features/auth/session-action-group"; import { ReportSubmitForm } from "@/features/reports/report-submit-form"; -import { getSessionUser, getSessionUserLabel } from "@/lib/session"; +import { + createLoginHref, + getSessionUser, +} from "@/lib/session"; type ReportPageProps = { searchParams: Promise>; @@ -14,20 +18,26 @@ export default async function ReportPage({ searchParams }: ReportPageProps) { const placeId = getFirstValue(params.placeId, "unknown-place"); const placeName = getFirstValue(params.placeName, "이름 없는 장소"); const user = await getSessionUser(); + const reportPath = `/report?placeId=${encodeURIComponent(placeId)}&placeName=${encodeURIComponent(placeName)}`; + const loginHref = createLoginHref(reportPath); return (
-

- 장소 신고 -

-

- 정보 수정 요청 -

-
- {user - ? `신고 계정: ${getSessionUserLabel(user)}` - : "로그인 없이도 신고를 남길 수 있습니다."} +
+
+

+ 장소 신고 +

+

+ 정보 수정 요청 +

+
+
diff --git a/src/app/signup/page.tsx b/src/app/signup/page.tsx index e9193f3..2e4cef6 100644 --- a/src/app/signup/page.tsx +++ b/src/app/signup/page.tsx @@ -1,6 +1,4 @@ import type { Metadata } from "next"; - -import Link from "next/link"; import { redirect } from "next/navigation"; import { BrandMark } from "@/components/brand-mark"; @@ -52,13 +50,6 @@ export default async function SignupPage({ searchParams }: SignupPageProps) { socialProviders={socialProviders} credentialsSignupEnabled={credentialsSignupEnabled} /> - {!credentialsSignupEnabled ? ( -

- 로컬 DB 없이 실행 중이면 회원가입은 저장되지 않습니다. 기존 계정 로그인이 - 필요하면 로그인 - 을 사용하세요. -

- ) : null}
); diff --git a/src/app/submit/page.tsx b/src/app/submit/page.tsx index 079e61e..30150ad 100644 --- a/src/app/submit/page.tsx +++ b/src/app/submit/page.tsx @@ -1,27 +1,32 @@ +import { SessionActionGroup } from "@/features/auth/session-action-group"; import { PlaceSubmitForm } from "@/features/submission/place-submit-form"; -import { getSessionUser, getSessionUserLabel } from "@/lib/session"; +import { + createLoginHref, + getSessionUser, +} from "@/lib/session"; export default async function SubmitPage() { const user = await getSessionUser(); + const loginHref = createLoginHref("/submit"); return (
-

- 장소 등록 -

-

- 새 장소 등록 -

-
- {user - ? `등록 계정: ${getSessionUserLabel(user)}` - : "로그인 없이도 장소를 등록할 수 있습니다."} +
+
+

+ 장소 등록 +

+

+ 새 장소 등록 +

+
+
-

- 주소만 적고 끝내지 않고, 지도에 표시될 위치를 함께 확인해야 등록이 - 접수됩니다. -

diff --git a/src/components/brand-mark.tsx b/src/components/brand-mark.tsx index 0e5e76c..edb9dff 100644 --- a/src/components/brand-mark.tsx +++ b/src/components/brand-mark.tsx @@ -29,11 +29,6 @@ function BrandMarkInner({ return ( - - Local Saving Map - diff --git a/src/features/auth/login-form.tsx b/src/features/auth/login-form.tsx index 1434f57..96a94f2 100644 --- a/src/features/auth/login-form.tsx +++ b/src/features/auth/login-form.tsx @@ -122,7 +122,7 @@ export function LoginForm({ onChange={(event) => setEmail(event.target.value)} data-testid="login-email" className="altteulmap-input px-4 py-3.5 text-stone-900" - placeholder="name@example.com" + placeholder="이메일 주소" autoComplete="email" /> diff --git a/src/features/auth/repository.ts b/src/features/auth/repository.ts index d2d60b2..0d927e3 100644 --- a/src/features/auth/repository.ts +++ b/src/features/auth/repository.ts @@ -226,7 +226,7 @@ export function listSocialAuthProviders(): SocialAuthProviderAvailability[] { id: provider.id, label: provider.label, enabled: false, - unavailableReason: "로컬 DB 연결 후 사용할 수 있습니다.", + unavailableReason: "데이터 연결 후 사용할 수 있습니다.", }; } @@ -235,7 +235,7 @@ export function listSocialAuthProviders(): SocialAuthProviderAvailability[] { id: provider.id, label: provider.label, enabled: false, - unavailableReason: `AUTH_${provider.id.toUpperCase()}_CLIENT_ID와 AUTH_${provider.id.toUpperCase()}_CLIENT_SECRET이 필요합니다.`, + unavailableReason: "로그인 연동 설정이 아직 완료되지 않았습니다.", }; } @@ -257,7 +257,7 @@ export async function createCredentialsUser( if (!isDatabaseEnabled()) { return { ok: false, - message: "회원가입은 DB 연결 후 사용할 수 있습니다.", + message: "회원가입은 데이터 연결 후 사용할 수 있습니다.", item: null, }; } diff --git a/src/features/auth/session-action-group.tsx b/src/features/auth/session-action-group.tsx new file mode 100644 index 0000000..1c4e040 --- /dev/null +++ b/src/features/auth/session-action-group.tsx @@ -0,0 +1,70 @@ +import Link from "next/link"; + +import { appUserRoleLabelMap } from "@/features/auth/constants"; +import { SignOutButton } from "@/features/auth/sign-out-button"; +import { + getSessionUserLabel, + type SessionUser, +} from "@/lib/session"; + +type SessionActionGroupProps = { + user: SessionUser | null; + loginHref?: string; + signOutCallbackUrl?: string; + compact?: boolean; +}; + +function getBadgeLabel(user: SessionUser) { + const userLabel = getSessionUserLabel(user); + const roleLabel = appUserRoleLabelMap[user.role]; + + return userLabel === roleLabel ? userLabel : `${userLabel} · ${roleLabel}`; +} + +export function SessionActionGroup({ + user, + loginHref, + signOutCallbackUrl = "/", + compact = false, +}: SessionActionGroupProps) { + const badgeClassName = compact + ? "altteulmap-badge whitespace-nowrap border border-stone-200 bg-stone-50 px-3 py-1.5 text-xs text-stone-700" + : "altteulmap-badge whitespace-nowrap border border-stone-200 bg-stone-50 px-4 py-3 text-sm text-stone-700"; + const linkClassName = compact + ? "altteulmap-button whitespace-nowrap border border-stone-300 bg-white px-3 py-1.5 text-xs text-stone-700 transition hover:bg-stone-100" + : "altteulmap-button whitespace-nowrap border border-stone-300 bg-white px-4 py-2 text-sm text-stone-700 transition hover:bg-stone-100"; + + if (!user) { + if (!loginHref) { + return null; + } + + return ( + + 로그인 + + ); + } + + return ( +
+ + {getBadgeLabel(user)} + + {user.role === "admin" ? ( + + 관리 + + ) : null} + +
+ ); +} diff --git a/src/features/auth/signup-form.tsx b/src/features/auth/signup-form.tsx index 75a3a8f..faa3658 100644 --- a/src/features/auth/signup-form.tsx +++ b/src/features/auth/signup-form.tsx @@ -40,7 +40,7 @@ export function SignupForm({ event.preventDefault(); if (!credentialsSignupEnabled) { - setMessage("회원가입은 DB 연결 후 사용할 수 있습니다."); + setMessage("회원가입은 데이터 연결 후 사용할 수 있습니다."); return; } @@ -107,9 +107,6 @@ export function SignupForm({

이메일로 가입

-

- 가입이 완료되면 같은 이메일과 비밀번호로 바로 로그인됩니다. -

@@ -167,12 +164,6 @@ export function SignupForm({ /> - {!credentialsSignupEnabled ? ( -
- 새 계정 생성은 DB 연결 후 사용할 수 있습니다. 지금은 기존 시드 계정이나 - 소셜 로그인만 사용할 수 있습니다. -
- ) : null}
{message ? ( diff --git a/src/features/places/admin-place-coordinate-picker.tsx b/src/features/places/admin-place-coordinate-picker.tsx index 6db22a0..f0628cb 100644 --- a/src/features/places/admin-place-coordinate-picker.tsx +++ b/src/features/places/admin-place-coordinate-picker.tsx @@ -209,7 +209,7 @@ export function AdminPlaceCoordinatePicker({

- Coordinate picker + 위치 선택

지도에서 승인 좌표 선택 diff --git a/src/features/places/admin-place-review-form.tsx b/src/features/places/admin-place-review-form.tsx index e91a4fe..24543b2 100644 --- a/src/features/places/admin-place-review-form.tsx +++ b/src/features/places/admin-place-review-form.tsx @@ -66,6 +66,11 @@ export function AdminPlaceReviewForm({ data-testid="admin-review-form" className="space-y-4 rounded-3xl border border-stone-200 bg-white p-4" > +
+ 공개 등록은 텍스트 정보만 받습니다. 승인 전에 운영자가 네이버 지도 + 검색 결과와 주소를 대조해 위치를 확정해주세요. +
+ (null); @@ -317,7 +308,6 @@ export function MapExplorer({ maxPrice, query, searchScope, - sort, }); fetch(`/api/places/map?${search}`, { @@ -353,7 +343,7 @@ export function MapExplorer({ return () => { controller.abort(); }; - }, [activeBounds, boundsKey, category, maxPrice, query, searchScope, sort]); + }, [activeBounds, boundsKey, category, maxPrice, query, searchScope]); const handlePlaceSelect = (placeId: string) => { setSelectedPlaceId(placeId); @@ -395,7 +385,7 @@ export function MapExplorer({ activePlaceId={resolvedSelectedPlaceId} focusPlacesKey={ query && searchScope === "global" - ? `${query}:${category ?? "all"}:${maxPrice ?? "all"}:${sort}` + ? `${query}:${category ?? "all"}:${maxPrice ?? "all"}` : null } onSelectPlace={handlePlaceSelect} From cbe91f1ecbd0b3612a2fc7d9ddcc10fa51b76cd6 Mon Sep 17 00:00:00 2001 From: MJY Date: Thu, 2 Apr 2026 08:30:51 +0900 Subject: [PATCH 2/6] feat: slim cloudflare bundle and consolidate map entry --- .gitignore | 1 + PLAN.md | 35 +- PROGRESS.md | 159 +++++- README.md | 33 +- docs/cloudflare-account-to-deploy.md | 4 + docs/deploy-cloudflare.md | 5 +- eslint.config.mjs | 1 + next.config.ts | 6 +- package.json | 14 +- prd.md | 9 +- src/app/admin/page.tsx | 303 ++++++++++- src/app/api/admin/places/[id]/route.ts | 16 +- src/app/api/admin/places/route.ts | 8 +- src/app/api/admin/price-items/[id]/route.ts | 10 +- src/app/api/admin/prices/[id]/route.ts | 10 +- src/app/api/admin/prices/route.ts | 8 +- src/app/api/admin/reports/[id]/route.ts | 10 +- src/app/api/admin/reports/route.ts | 8 +- src/app/api/bookmarks/[id]/route.ts | 8 +- src/app/api/bookmarks/route.ts | 6 +- src/app/api/categories/route.ts | 4 +- .../places/[id]/comments/[commentId]/route.ts | 6 +- src/app/api/places/[id]/comments/route.ts | 8 +- src/app/api/places/[id]/prices/route.ts | 8 +- src/app/api/places/[id]/reaction/route.ts | 8 +- src/app/api/places/[id]/route.ts | 6 +- src/app/api/places/map/route.ts | 24 +- src/app/api/places/route.ts | 8 +- src/app/api/reports/route.ts | 8 +- src/app/map/page.tsx | 500 +----------------- src/app/page.tsx | 26 +- src/features/admin/repository.ts | 204 +++++++ src/features/map/map-page.tsx | 494 +++++++++++++++++ src/features/places/queries.ts | 12 - src/features/places/repository.ts | 14 +- src/features/places/types.ts | 2 +- .../submission/place-coordinate-picker.tsx | 322 ----------- src/features/submission/place-submit-form.tsx | 351 +----------- src/features/submission/schema.ts | 78 +-- src/lib/public-write-actor.ts | 4 +- src/lib/visitor-id.ts | 48 +- tests/e2e/admin-dashboard.spec.ts | 38 ++ tests/e2e/map.spec.ts | 17 - tests/e2e/submission-admin.spec.ts | 35 +- trd.md | 17 +- tsconfig.json | 18 +- wrangler.jsonc | 7 +- 47 files changed, 1417 insertions(+), 1504 deletions(-) create mode 100644 src/features/admin/repository.ts create mode 100644 src/features/map/map-page.tsx delete mode 100644 src/features/submission/place-coordinate-picker.tsx create mode 100644 tests/e2e/admin-dashboard.spec.ts diff --git a/.gitignore b/.gitignore index 30e3cad..7888013 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ # next.js /.next/ +/.next-dev/ /out/ # production diff --git a/PLAN.md b/PLAN.md index 427147f..fba9a66 100644 --- a/PLAN.md +++ b/PLAN.md @@ -22,12 +22,14 @@ - 좋아요 랭킹 ## 현재 우선순위 -1. 인증 정리와 출시 준비: 실제 로그인 경로, 로그인/회원가입 진입면과 소셜 온보딩, 반복 가능한 검증 절차, Cloudflare 배포 체크리스트 -2. 공개 UI polish: 헤더 간소화, 개발 문구 제거, 버튼/칩 줄바꿈 방지, 지도/상세 밀도 정리 -3. 운영 품질 후속 정리: rate limit 수치 조정, 관리자 UX polish, 캐시/재검증 보강 -4. 확장 기능 1차: 플레이스 좋아요/싫어요 반응 모델과 UI 우선 도입 -5. 확장 기능 2차: 공유/사진 업로드 범위와 우선순위 재결정 -6. repo-local 개발 워크플로우 유지: `.agents` skills/reviewers, `.githooks`, `verify` 스크립트 +1. 로컬 dev/runtime 안정화: `.next` 충돌 방지, dev/build/test 산출물 분리, 재현 가능한 복구 경로 정리 +2. 로그인 상태 액션과 관리자 대시보드 정리: 로그아웃 노출, 운영자 진입 동선, 관리자 overview와 유저/운영 지표 설계 +3. 인증 정리와 출시 준비: 실제 로그인 경로, 로그인/회원가입 진입면과 소셜 온보딩, 반복 가능한 검증 절차, Cloudflare 배포 체크리스트 +4. 공개 UI polish: 헤더 간소화, 개발 문구 제거, 버튼/칩 줄바꿈 방지, 지도/상세 밀도 정리 +5. 운영 품질 후속 정리: rate limit 수치 조정, 관리자 UX polish, 캐시/재검증 보강 +6. 확장 기능 1차: 플레이스 좋아요/싫어요 반응 모델과 UI 우선 도입 +7. 확장 기능 2차: 공유/사진 업로드 범위와 우선순위 재결정 +8. repo-local 개발 워크플로우 유지: `.agents` skills/reviewers, `.githooks`, `verify` 스크립트 ## 현재 제품 상태 @@ -36,7 +38,7 @@ - 네이버 지도 연동과 preview fallback - 현재 위치 버튼 - viewport 기반 장소 재조회 -- 카테고리/가격/정렬 필터 +- 카테고리/가격 필터 - 지역/전역 검색 - 장소 상세 시트와 개별 상세 페이지 - 북마크 @@ -49,7 +51,7 @@ - 관리자 가격 직접 수정/대표 지정/숨김 - 플레이스 좋아요/싫어요 반응 - 비로그인 visitor cookie 기반 좋아요/싫어요 -- 지도 목록 좋아요 노출과 좋아요순 정렬 +- 지도 목록 좋아요 노출 - 대표 가격 재계산과 동일 가격 2회 검증 처리 - 쓰기 API 최소 rate limit - 로컬 Auth.js credentials 로그인 @@ -62,11 +64,11 @@ - 검색 URL 상태 - 검색어/검색 범위는 반영되지만 지도 중심/줌 상태까지는 아직 URL에 싣지 않음 - 출시 준비 - - robots, sitemap, canonical, `smoke:local`, `deploy:check`, Playwright E2E 기본 흐름, credentials 로그인/가입/북마크/신고/관리자 승인/관리자 가격 검토 E2E, GitHub Actions CI, Cloudflare Builds 기준 배포 체크 문서는 정리 중이고, 현재 E2E는 세 그룹 실행 기준으로 반복 가능하다. 실제 OAuth와 실제 운영 도메인 기준 end-to-end 점검, 그리고 지도 반응 계열 E2E의 간헐적 서버 종료 원인 정리는 남아 있다 + - robots, sitemap, canonical, `smoke:local`, `deploy:check`, Playwright E2E 기본 흐름, credentials 로그인/가입/북마크/신고/관리자 승인/관리자 가격 검토 E2E, GitHub Actions CI, Cloudflare Builds 기준 배포 체크 문서는 정리 중이고, 현재 E2E는 세 그룹 실행 기준으로 반복 가능하다. Cloudflare 무료 플랜 기준 번들 경량화와 실제 `workers.dev` 배포까지는 통과했다. 실제 OAuth와 실제 운영 도메인 기준 end-to-end 점검은 남아 있다 - 인증 - 로그인/회원가입 진입면과 로컬 credentials, 카카오/네이버 OAuth scaffolding은 준비돼 있고, 현재는 credentials 회원가입까지 동작한다. `.env.local` 기준으로 네이버는 활성화 가능 상태이고, 카카오는 client secret 누락 시 비활성화된다. 실제 provider credential과 callback URL로 끝까지 검증한 외부 로그인 경로는 아직 없다 - 반응 기능 - - 상세/상세 시트/지도 마커/지도 목록과 좋아요순 정렬까지 반영됐다. 별도 랭킹 화면은 아직 없다 + - 상세/상세 시트/지도 마커/지도 목록 반영까지 들어갔다. 별도 랭킹 화면은 아직 없다 - 공유 기능 - 상세 페이지와 상세 시트에서 링크 공유는 가능하지만, 목록 단위 공유나 공유 유입 추적은 아직 없다 @@ -74,7 +76,7 @@ - 좋아요 랭킹 - 사진 업로드 - 테스트 체계 - - 지도/상세/비회원 좋아요/좋아요순/공유, 모바일 목록 시트/상세 시트, credentials 로그인/회원가입, 익명 댓글, 익명 장소 등록, 북마크, 익명 신고 제출/관리자 상태 변경, 익명 가격 제보/관리자 반려, 관리자 장소 승인 기준 Playwright E2E는 들어갔다. 현재는 DB 기반 `signup/bookmarks/map`, mock runtime의 `map.mobile`, DB 기반 `comments/price-review/report-admin/submission-admin` 세 그룹으로 반복 실행한다. 실제 소셜 로그인 E2E와 더 깊은 모바일 제스처 검증은 더 필요하다 + - 지도/상세/비회원 좋아요/공유, 모바일 목록 시트/상세 시트, credentials 로그인/회원가입, 익명 댓글, 익명 장소 등록, 북마크, 익명 신고 제출/관리자 상태 변경, 익명 가격 제보/관리자 반려, 관리자 장소 승인 기준 Playwright E2E는 들어갔다. 현재는 DB 기반 `signup/bookmarks/map`, mock runtime의 `map.mobile`, DB 기반 `comments/price-review/report-admin/submission-admin` 세 그룹으로 반복 실행한다. 실제 소셜 로그인 E2E와 더 깊은 모바일 제스처 검증은 더 필요하다 - 커뮤니티 `bang` - 핫딜 `deal` @@ -85,6 +87,11 @@ |---|---|---|---|---|---| | ECC에서 필요한 부분만 가져와 altteulmap 저장소 하위에 repo-local skills, reviewer guides, verify script, git hooks를 구성하고 전역 설정 없이 활성화한다 | Codex | P2 | `done` | `.agents/skills`, `.agents/reviewers`, `.githooks`, `scripts/git-hooks`가 추가되고, `npm run hooks:install`, `npm run verify`가 동작하며, `AGENTS.md`/`README.md`/`PLAN.md`/`PROGRESS.md`에 사용 규칙과 검증 결과가 반영된다 | `AGENTS.md`, `README.md`, `package.json`, `.githooks/**`, `scripts/git-hooks/**` | +### Cycle 8: 로컬 개발/검증 안정화 +| 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | +|---|---|---|---|---|---| +| 로컬 `next dev`가 build/start/e2e와 `.next`를 공유하며 깨지는 문제를 막고, 재기동 없이 페이지 전환이 계속 가능한 상태로 정리한다 | Codex | P1 | `in_progress` | dev 서버는 production build/start/e2e 산출물과 캐시를 분리해 사용하고, Turbopack `.sst/.meta` 손상이나 `Another write batch or compaction is already active` 오류가 재발하지 않도록 기본 스크립트/설정이 조정되며, 복구 명령과 검증 결과가 `README.md`와 `PROGRESS.md`에 남는다 | `package.json`, `next.config.ts`, `.gitignore`, `README.md`, `PROGRESS.md` | + ### Cycle 2: 문서 체계 정비와 지도 검색/URL 상태 반영 (완료) | 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | |---|---|---|---|---|---| @@ -103,9 +110,12 @@ ### Cycle 5: 인증 정리와 출시 준비 | 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | |---|---|---|---|---|---| +| 로그인 상태 액션과 운영자 대시보드를 운영 가능한 수준으로 확장한다 | Codex | P1 | `done` | 로그인 사용자는 주요 화면 상단에서 계정 라벨과 로그아웃 버튼을 볼 수 있고, 운영자 사용자는 관리자 진입 링크와 `/admin` overview에서 장소 등록/가격 제보/신고 큐, 사용자 수, 현재 세션 수 같은 즉시 계산 가능한 지표를 확인할 수 있으며, 방문 수/활성 사용자 수는 별도 방문 이벤트 적재 기준으로 정의된 2차 작업 계획이 같이 남는다 | `src/app/**`, `src/features/auth/**`, `src/features/**/repository.ts`, `src/db/schema.ts`, `PLAN.md`, `PROGRESS.md`, `prd.md`, `trd.md`, 테스트 | +| visit/activity 이벤트 적재를 추가해 관리자 대시보드에 방문 수와 DAU/WAU를 노출한다 | Codex | P1 | `pending` | 방문 이벤트가 visitor/session 기준으로 저장되고, `/admin` overview에서 오늘/7일 방문 수, 고유 방문자 수, DAU/WAU, 재방문율 같은 활동 지표를 확인할 수 있으며, dedupe/rate limit/보존 정책과 검증 로그가 문서에 남는다 | `src/db/schema.ts`, migration, `src/app/**` 또는 middleware, `src/features/admin/**`, `PLAN.md`, `PROGRESS.md`, `trd.md`, 테스트 | | 로컬 credentials 중심 인증을 실제 로그인/회원가입 경로로 확장하고, 핵심 사용자 흐름 테스트/SEO/배포 체크리스트를 정리한다 | Codex | P2 | `in_progress` | 로컬 credentials 로그인/회원가입과 소셜 가입 진입면, 지도 탐색/모바일 시트/익명 장소 등록/북마크/신고/관리자 승인/관리자 가격 검토 E2E가 반복 가능하게 정리되고, 이후 실제 OAuth 1개 이상이 end-to-end로 확인되며, sitemap/canonical/metadata와 Cloudflare 배포 체크리스트가 최신 상태가 된다 | `src/auth.ts`, `src/app/login/page.tsx`, `src/app/signup/page.tsx`, 테스트 코드, `README.md`, `trd.md` | | 모바일 공개 지도 UX와 인증 진입면을 모바일 우선 기준으로 다시 정리한다 | Codex | P1 | `done` | 모바일에서 검색 외 필터는 접기 가능하고, 목록/상세 시트가 더 위로 올라오며, 로그인/회원가입이 기능 집중형 단일 카드 구조로 정리되고 관련 검증 로그가 `PROGRESS.md`에 남는다 | `src/app/map/page.tsx`, `src/features/places/map-explorer.tsx`, `src/features/places/place-detail-sheet.tsx`, `src/app/login/page.tsx`, `src/app/signup/page.tsx` | -| 장소 등록 시 텍스트 주소만으로 끝나지 않도록 위치 확인을 필수로 전환한다 | Codex | P1 | `done` | 공개 등록 폼은 텍스트 주소 입력만 노출하고, 내부 위치 확인 없이는 제출되지 않으며, 지도/좌표 UI 없이 API/문서/검증 로그가 같은 규칙으로 정리된다 | `src/features/submission/**`, `src/app/api/places/route.ts`, `tests/e2e/submission-admin.spec.ts`, `prd.md`, `trd.md` | +| 공개 지도의 플레이스 정렬 기능을 제거하고 검색/필터만 남긴다 | Codex | P1 | `done` | 지도 화면에서 정렬 UI와 `sort` query/API 계약이 제거되고, 공개 플레이스 목록은 고정 순서로 노출되며, 관련 테스트/문서/검증 로그가 같이 갱신된다 | `src/app/map/page.tsx`, `src/features/places/map-explorer.tsx`, `src/app/api/places/map/route.ts`, `src/features/places/**`, `tests/e2e/map.spec.ts`, 문서 | +| 공개 장소 등록을 텍스트 입력 중심으로 단순화하고, 운영자 승인 단계에서 위치를 확정한다 | Codex | P1 | `done` | 공개 등록 폼은 상호명/주소/가격 등 텍스트 입력만 받고, 공개 제출 API는 좌표 없이 접수되며, 위치/지도/링크 처리는 운영자 승인 단계에서 수행하도록 화면/API/문서/검증 로그가 같은 규칙으로 정리된다 | `src/features/submission/**`, `src/features/places/admin-place-review-form.tsx`, `src/app/api/places/route.ts`, `src/app/api/admin/places/[id]/route.ts`, `tests/e2e/submission-admin.spec.ts`, `prd.md`, `trd.md` | | 장소 등록 진입/제출을 로그인 없이도 허용하고, 공개 CTA 문구를 `장소 등록` 기준으로 맞춘다 | Codex | P1 | `done` | 비로그인 사용자가 `/submit`에 바로 진입해 장소 등록 요청을 제출할 수 있고, 등록 API가 익명 제출을 허용하며, 공개 화면 CTA와 검증 로그가 `장소 등록` 기준으로 정리된다 | `src/app/submit/page.tsx`, `src/app/api/places/route.ts`, `src/app/map/page.tsx`, 테스트 코드 | | 공개 쓰기 기능에서 북마크만 로그인 유지하고 댓글/신고/가격 제보는 익명 허용으로 전환하며, `비슷한 장소` UI를 제거한다 | Codex | P1 | `done` | 공개 상세/상세 시트/신고 페이지에서 댓글, 가격 제보, 신고가 로그인 없이 동작하고, 북마크만 로그인 요구를 유지하며, `비슷한 장소` 섹션과 관련 응답/문구/검증 로그가 정리된다 | `src/app/place/[id]/page.tsx`, `src/features/places/place-detail-sheet.tsx`, `src/app/report/page.tsx`, `src/app/api/places/**`, `src/app/api/reports/route.ts`, `src/features/places/repository.ts`, 테스트 코드, DB schema | @@ -119,3 +129,4 @@ - 벤치마크 사이트에서 영감을 받은 기능은 확장 후보로 유지하되, core loop를 약하게 만들 정도로 범위를 넓히지 않는다. - 현재 익명 허용 범위는 `좋아요/싫어요`, `장소 등록`, `댓글`, `가격 제보`, `신고`까지다. - 공개 쓰기 중 로그인 유지 기능은 현재 `북마크`만 남긴다. 운영자 기능은 기존처럼 별도 인증/권한 검사를 유지한다. +- 운영자 대시보드의 `총 사용자 수`, `현재 세션 수`, `승인/신고/가격 검토 큐`는 현행 DB로 바로 계산할 수 있지만, `방문 수`, `DAU/WAU` 같은 활동 지표는 현재 저장되지 않는다. 이 값들은 별도 `visit/activity` 이벤트 적재를 붙인 뒤 2차로 노출한다. diff --git a/PROGRESS.md b/PROGRESS.md index a08b90d..35e2811 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -9,13 +9,166 @@ - Cycle 3: 댓글 작성/삭제, 기존 장소 가격 제보, 관리자 가격 검토 큐 완료 - Cycle 4: 관리자 가격 수정/숨김 UI, 대표 가격 재계산 규칙, 최소 rate limit, DB migration 적용, 실DB 런타임 검증 완료 - Cycle 5: sitemap/robots/canonical/기본 metadata, OAuth scaffolding, deploy check, Playwright E2E 3차, 공개 UI polish 진행 중. 공개 쓰기는 북마크를 제외하고 익명 허용으로 정리됐고, 실제 외부 로그인 E2E와 운영 도메인 기준 점검은 남아 있음 -- Cycle 5 후속: GitHub Actions CI(`Verify`, `E2E`, `Deploy Config Check`)와 Cloudflare Builds 분리 운영 경로 정리 완료 -- Cycle 6: 좋아요/싫어요 반응 도입 완료, 비로그인 visitor cookie 반응과 공개 메타 줄 분리까지 반영. 랭킹/정렬/목록 노출 확장은 남아 있음 +- Cycle 5 장소 등록 정책: 공개 폼은 텍스트 정보만 받고, 지도 위치와 네이버 지도 검색 확인은 운영자 승인 단계에서 처리하도록 다시 정리됨 +- Cycle 5 후속: GitHub Actions CI를 `push용 smoke`와 `PR용 full`로 분리하고 Cloudflare Builds와 분리 운영하는 경로 정리 완료 +- Cycle 6: 좋아요/싫어요 반응 도입 완료, 비로그인 visitor cookie 반응과 공개 메타 줄 분리까지 반영. 랭킹/목록 노출 확장은 남아 있음 - Cycle 7: repo-local AI workflow 설정 완료 (`.agents`, `.githooks`, `verify`, local commit rules) -- 다음 우선순위: 실제 외부 로그인 E2E, 운영 도메인 기준 점검, 모바일 제스처/스냅 수준 E2E 보강 +- 다음 우선순위: 관리자 visit/activity telemetry 2차, 이후 실제 외부 로그인 E2E와 운영 도메인 점검 ## 실행 로그 +### 2026-04-02 08:18 KST: `/map` 호환 경로를 redirect로 줄이고 홈 단일 진입점으로 정리 +- 완료 내용 + - `/Users/alex/project/altteulmap/src/features/map/map-page.tsx`로 실제 지도 홈 구현을 분리했다. + - `/Users/alex/project/altteulmap/src/app/page.tsx`는 위 구현을 그대로 재사용하는 최소 re-export로 정리했다. + - `/Users/alex/project/altteulmap/src/app/map/page.tsx`는 더 이상 전체 지도 화면을 중복 렌더하지 않고, query string을 유지한 채 `/`로 `permanentRedirect` 하도록 바꿨다. + - `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md`의 현재 진입 경로 안내도 `/` 기준으로 맞췄다. +- 검증 결과 + - `npm run verify:quick` 통과 + - `npm run build` 통과 예정 검증과 함께 route output 확인 +- 메모 + - 이 변경의 목적은 사용자 진입 경로를 `/`로 고정하면서, `/map`이 같은 서버 페이지를 한 번 더 싣지 않게 해 route 중복 비용을 줄이는 것이다. + +### 2026-04-02 08:10 KST: Cloudflare 무료 플랜 기준으로 번들 경량화와 배포 경로 안정화 +- 완료 내용 + - `/Users/alex/project/altteulmap/package.json`의 `build`를 `next build --webpack`으로 고정했다. Next 16 기본 Turbopack 빌드에서는 OpenNext 서버 핸들러가 너무 커져 Cloudflare Workers Free 한도 3 MiB를 넘겼다. + - `/Users/alex/project/altteulmap/src/app/api/**`, `/Users/alex/project/altteulmap/src/lib/visitor-id.ts`, `/Users/alex/project/altteulmap/src/lib/public-write-actor.ts`에서 `next/server`의 `NextRequest`/`NextResponse` 의존을 제거하고 표준 `Request`/`Response`와 수동 cookie header 설정으로 정리했다. + - `/Users/alex/project/altteulmap/src/app/layout.tsx`, `/Users/alex/project/altteulmap/src/app/place/[id]/page.tsx`에서 현재 쓰지 않는 `openGraph`/`twitter` metadata를 제거했고, `/Users/alex/project/altteulmap/wrangler.jsonc`에서는 실제로 쓰지 않는 `images` binding을 제거했다. + - `/Users/alex/project/altteulmap/package.json`에 `cf:clean`, `cf:build` 스크립트를 추가하고 `preview`, `deploy`, `upload`가 항상 `.next`, `.open-next`를 비운 뒤 다시 빌드하도록 바꿨다. 이전에는 stale build 산출물 때문에 `npm run deploy` 중 OpenNext middleware config 복사 단계가 간헐적으로 깨졌다. +- 검증 결과 + - `npm run lint` 통과 + - `npx opennextjs-cloudflare build` 통과 + - `npm run deploy` 통과 + - 배포 URL `https://altteulmap.altteul-lab.workers.dev`에서 `HTTP 200`, `/robots.txt` 응답 확인 + - OpenNext 서버 핸들러 크기 재측정 + - Turbopack 기준: `.open-next/server-functions/default/handler.mjs` 약 `2595 KiB gzip` + - Webpack 기준: `.open-next/server-functions/default/handler.mjs` 약 `1088 KiB gzip` + - 실제 Wrangler 업로드 결과 + - 최종 `Total Upload: gzip 1356.55 KiB` + - Cloudflare 무료 플랜 한도 안에서 배포 성공 +- 메모 + - 이번 경량화의 핵심은 기능 삭제보다 런타임/번들 경로를 바꾼 것이다. `Turbopack -> webpack`, `next/server -> Request/Response` 전환이 가장 큰 효과를 냈다. + - `sitemap.xml` prerender 중 로컬 production DB에 `places` 테이블이 없으면 mock fallback 로그가 남지만, 빌드와 배포 자체는 정상 통과한다. + +### 2026-04-02 08:08 KST: 로그아웃 노출과 관리자 overview 대시보드 1차 구현 +- 완료 내용 + - `/Users/alex/project/altteulmap/src/features/auth/session-action-group.tsx`를 추가해 로그인 상태 공통 액션을 묶었다. 로그인 사용자는 계정 라벨과 `로그아웃` 버튼을 보고, 운영자 계정은 같은 자리에서 `관리` 링크도 바로 볼 수 있다. + - `/Users/alex/project/altteulmap/src/app/map/page.tsx`, `/Users/alex/project/altteulmap/src/app/bookmarks/page.tsx`, `/Users/alex/project/altteulmap/src/app/submit/page.tsx`, `/Users/alex/project/altteulmap/src/app/report/page.tsx`, `/Users/alex/project/altteulmap/src/app/place/[id]/page.tsx`에 공통 액션을 붙여 공개 화면에서도 로그인 상태와 로그아웃 동선이 보이게 정리했다. + - `/Users/alex/project/altteulmap/src/features/admin/repository.ts`를 추가해 관리자 overview 집계를 분리했다. 총 사용자 수, 운영자/일반 사용자 수, 현재 세션 수, 세션 사용자 수, 활성 장소 수, 승인 대기 장소 수, 대기 가격 제보 수, 열린 신고 수와 최근 가입 사용자 목록을 한 번에 조회한다. + - `/Users/alex/project/altteulmap/src/app/admin/page.tsx`를 overview 대시보드로 다시 구성했다. KPI 카드, 장소/가격/신고 바로가기, 최근 가입 사용자 목록, 최신 장소 등록 목록, 최신 신고 목록을 넣었고 방문 지표는 아직 미계측이라는 안내를 명시했다. + - `/Users/alex/project/altteulmap/src/app/admin/places/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/reports/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/prices/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/prices/places/[id]/page.tsx`에도 같은 세션 액션을 붙여 관리자 하위 화면에서도 로그아웃이 바로 보이게 했다. + - `/Users/alex/project/altteulmap/tests/e2e/admin-dashboard.spec.ts`를 추가했고, `/Users/alex/project/altteulmap/package.json`, `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/prd.md`, `/Users/alex/project/altteulmap/trd.md`, `/Users/alex/project/altteulmap/PLAN.md`를 새 관리자/인증 동선에 맞게 갱신했다. +- 검증 결과 + - `npm run verify:quick` 통과 + - `rm -rf .next && npm run verify` 통과 + - `npm run db:push` 통과 + - `npm run db:seed` 통과 + - `DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/altteulmap USE_MOCK_DATA=false AUTH_SECRET=altteulmap-local-auth-secret-change-me NEXTAUTH_URL=http://127.0.0.1:3107 AUTH_DEMO_PASSWORD=demo1234 AUTH_ADMIN_PASSWORD=admin1234 npx playwright test tests/e2e/map.spec.ts tests/e2e/admin-dashboard.spec.ts tests/e2e/bookmarks.spec.ts tests/e2e/price-review.spec.ts tests/e2e/report-admin.spec.ts tests/e2e/submission-admin.spec.ts` 통과 + - `USE_MOCK_DATA=true AUTH_SECRET=altteulmap-local-auth-secret-change-me NEXTAUTH_URL=http://127.0.0.1:3107 AUTH_DEMO_PASSWORD=demo1234 AUTH_ADMIN_PASSWORD=admin1234 npx playwright test tests/e2e/map.mobile.spec.ts --project mobile-chromium` 통과 + - `DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/altteulmap USE_MOCK_DATA=false AUTH_SECRET=altteulmap-local-auth-secret-change-me NEXTAUTH_URL=http://127.0.0.1:3107 AUTH_DEMO_PASSWORD=demo1234 AUTH_ADMIN_PASSWORD=admin1234 npm run test:e2e:smoke:ci` 통과 +- 메모 + - 이번 1차 구현은 현재 DB로 바로 계산 가능한 지표만 노출한다. `방문 수`, `DAU/WAU`, `재방문율`은 아직 저장소가 없어 카드 대신 안내 섹션만 넣었다. + - 다음 단계는 별도 visit/activity 이벤트 적재와 dedupe 정책을 추가해 관리자 대시보드의 활동 지표를 실제 숫자로 채우는 작업이다. + +### 2026-04-02 02:10 KST: 홈/지도 탭 제목을 기본 브랜드명으로 통일 +- 완료 내용 + - `/Users/alex/project/altteulmap/src/app/page.tsx`, `/Users/alex/project/altteulmap/src/app/map/page.tsx`에서 개별 title `지도에서 알뜰 장소 찾기`를 제거했다. + - 이제 홈 `/`와 `/map`은 `/Users/alex/project/altteulmap/src/app/layout.tsx`의 기본 metadata title `알뜰맵`을 그대로 사용한다. +- 검증 결과 + - `rg -n 'title:\s*"지도에서 알뜰 장소 찾기"|title:\s*"알뜰맵"' src/app` 결과, 홈/지도 페이지의 개별 title 제거 확인 + - `npm run verify:quick` 통과 + +### 2026-04-02 01:58 KST: 지도 검색 범위 칩의 선택 상태 배경 회귀 수정 +- 완료 내용 + - `/Users/alex/project/altteulmap/src/app/map/page.tsx`에서 `현재 지도에서 찾기`/`전체에서 찾기` 칩이 쓰던 `peer-checked:bg...` 조합을 제거하고, `altteulmap-scope-input` + `altteulmap-scope-chip` 조합으로 단순화했다. 기존 구현은 선택 시 `text-white`는 적용되는데 배경은 계속 흰색으로 남아 글자가 안 보일 수 있었다. + - `/Users/alex/project/altteulmap/src/app/globals.css`에 범위 칩 전용 규칙을 추가해 `:checked + span`에서 border/background/text를 함께 제어하고, focus ring도 같이 유지되게 정리했다. +- 검증 결과 + - `npm run verify:quick` 통과 + - Playwright 브라우저 샘플에서 `.altteulmap-scope-input:checked + .altteulmap-scope-chip` 계산값이 `color: rgb(255, 255, 255)`, `backgroundImage: linear-gradient(...)`, `borderColor: rgb(156, 89, 52)`로 적용되는 것 확인 +- 메모 + - 현재 별도로 떠 있던 기존 `next dev` 프로세스(`localhost:3000`)는 수정 전 클래스 조합을 계속 서빙하고 있었다. 같은 증상이 남아 보이면 dev 서버를 한 번 재시작해 최신 스타일을 반영해야 한다. + +### 2026-04-02 01:39 KST: 로고와 인증/운영 화면의 영문 카피 제거 +- 완료 내용 + - `/Users/alex/project/altteulmap/src/components/brand-mark.tsx`에서 로고 위 보조 영문 문구 `Local Saving Map`을 제거하고, 브랜드 표기는 `알뜰맵`과 한글 설명만 남기도록 정리했다. + - `/Users/alex/project/altteulmap/src/app/admin/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/places/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/prices/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/reports/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/prices/places/[id]/page.tsx`, `/Users/alex/project/altteulmap/src/features/places/admin-place-coordinate-picker.tsx`에서 `Admin`, `Place queue`, `Price queue`, `Report queue`, `Coordinate picker`, `API 보기`, `데이터 소스: DB`처럼 섞여 있던 운영 화면 영문/영문 약어를 `운영`, `장소 검토`, `가격 검토`, `신고 검토`, `위치 선택`, `응답 보기`, `데이터 구분: 실데이터` 기준으로 통일했다. + - `/Users/alex/project/altteulmap/src/features/auth/login-form.tsx`, `/Users/alex/project/altteulmap/src/features/auth/signup-form.tsx`, `/Users/alex/project/altteulmap/src/features/auth/repository.ts`, `/Users/alex/project/altteulmap/src/app/api/auth/signup/route.ts`에서 남아 있던 `name@example.com`, `DB 연결`, 환경 변수명을 직접 노출하는 안내 문구를 걷어내고, 인증 화면과 비활성 상태 메시지를 한글 중심으로 정리했다. +- 검증 결과 + - `rg -n 'Local Saving Map|name@example.com|DB 연결|AUTH_[A-Z_]+_CLIENT|Place queue|Price queue|Report queue|Coordinate picker|>Admin<|eyebrow="Admin"|API 보기|데이터 소스:' src/app src/components src/features` 실행 결과, 남은 항목은 환경 변수 참조 같은 내부 코드뿐이고 정리 대상 사용자 노출 문구는 제거된 것 확인 + - `npm run verify:quick` 통과 + +### 2026-04-02 00:00 KST: 로그인 상태 액션/관리자 대시보드 설계 정리 +- 완료 내용 + - `/Users/alex/project/altteulmap/src/features/auth/sign-out-button.tsx`에 로그아웃 버튼 컴포넌트는 이미 있지만, `/Users/alex/project/altteulmap/src/app/map/page.tsx`를 포함한 주요 화면 상단 액션에는 아직 연결되지 않은 상태임을 확인했다. 현재는 비로그인일 때만 `로그인`이 보이고, 로그인 상태의 `로그아웃`/`관리` 동선은 공용 UI로 드러나지 않는다. + - `/Users/alex/project/altteulmap/src/app/admin/page.tsx`와 `/Users/alex/project/altteulmap/src/app/admin/places/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/reports/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/prices/page.tsx`를 기준으로 보면 관리자 검토 큐 자체는 이미 있지만, 현재 `/admin`은 요약 카드 3개만 있는 초안 상태다. 운영자 요청 기준의 `장소 등록 목록`, `신고 목록`, `유저 현황`을 한 화면에서 보는 overview/dashboard는 아직 없다. + - `/Users/alex/project/altteulmap/src/db/schema.ts` 기준으로 `users`, `auth_sessions`, `places`, `price_reports`, `content_reports` 집계는 바로 가능하지만, `방문 수`, `DAU/WAU` 같은 활동 지표를 계산할 방문 이벤트 저장소는 없다. `auth_sessions`도 `expires`만 있어 최근 활동 지표 대체재로는 부족하다. + - `/Users/alex/project/altteulmap/PLAN.md`에 새 작업 `로그인 상태 액션과 운영자 대시보드를 운영 가능한 수준으로 확장한다`를 추가했다. 구현은 `1차: 로그아웃/관리 진입 + 기존 DB 기반 overview`, `2차: visit/activity 이벤트 적재 후 방문/활성 사용자 지표`의 두 단계로 진행하는 것으로 정리했다. +- 메모 + - 1차 관리자 overview 후보 지표: 총 사용자 수, 현재 만료되지 않은 세션 수, 운영자 수/일반 사용자 수, 승인 대기 장소 수, 대기 중인 가격 제보 수, 열린 신고 수, 최근 가입 사용자 목록 + - 2차 활동 지표 후보: 오늘/7일 방문 수, 고유 방문자 수, DAU/WAU, 재방문율. 이 단계는 새 telemetry table, dedupe key, 수집 위치(layout 또는 middleware 수준) 설계가 선행돼야 한다. + +### 2026-04-02 01:26 KST: 지도 카테고리/필터 선택 상태 대비 강화 +- 완료 내용 + - `/Users/alex/project/altteulmap/src/app/globals.css`에서 `altteulmap-accent-chip`을 더 진한 오렌지 계열 배경과 흰 텍스트, 더 강한 border/shadow 조합으로 바꿔 선택 상태가 비선택 상태와 확실히 구분되게 조정했다. + - `/Users/alex/project/altteulmap/src/app/map/page.tsx`에서 모바일/데스크톱 카테고리, 가격 필터, 검색 범위 칩의 선택/비선택 클래스를 공통 상수로 정리하고, radio `peer-checked` 상태도 기존의 옅은 베이지 대신 진한 활성 색으로 통일했다. +- 검증 결과 + - `npm run verify:quick` 통과 + +### 2026-04-01 23:47 KST: 공개 지도 플레이스 정렬 기능 제거 +- 완료 내용 + - `/Users/alex/project/altteulmap/src/app/map/page.tsx`, `/Users/alex/project/altteulmap/src/features/places/map-explorer.tsx`에서 공개 지도 정렬 UI, `sort` URL 파라미터, 모바일 summary/hidden input을 제거했다. 이제 공개 지도는 검색과 카테고리/가격 필터만 유지하고, 목록은 기본 순서로 고정 노출된다. + - `/Users/alex/project/altteulmap/src/app/api/places/map/route.ts`, `/Users/alex/project/altteulmap/src/features/places/types.ts`, `/Users/alex/project/altteulmap/src/features/places/queries.ts`, `/Users/alex/project/altteulmap/src/features/places/repository.ts`에서 공개 지도용 `sort` API 계약과 `likes` 정렬 분기를 제거했다. 내부적으로는 기존 `price`/`recent` 정렬 타입만 남겨 다른 비공개/운영 경로와 충돌하지 않게 정리했다. + - `/Users/alex/project/altteulmap/tests/e2e/map.spec.ts`에서 좋아요순 회귀를 제거했고, `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/prd.md`, `/Users/alex/project/altteulmap/trd.md`, `/Users/alex/project/altteulmap/PLAN.md`를 새 공개 지도 계약에 맞게 갱신했다. +- 검증 결과 + - `npm run verify:quick` 통과 + - `rm -rf .next && npm run verify` 통과 + - `DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/altteulmap USE_MOCK_DATA=false AUTH_SECRET=altteulmap-local-auth-secret-change-me NEXTAUTH_URL=http://127.0.0.1:3107 AUTH_DEMO_PASSWORD=demo1234 AUTH_ADMIN_PASSWORD=admin1234 npx playwright test tests/e2e/map.spec.ts` 통과 + - `USE_MOCK_DATA=true AUTH_SECRET=altteulmap-local-auth-secret-change-me NEXTAUTH_URL=http://127.0.0.1:3107 AUTH_DEMO_PASSWORD=demo1234 AUTH_ADMIN_PASSWORD=admin1234 npx playwright test tests/e2e/map.mobile.spec.ts --project mobile-chromium` 통과 +- 메모 + - `npm run test:e2e:smoke`는 이번 run에서 `e2e:prepare` 이후 build 단계 중 `.next/server/pages-manifest.json`을 찾지 못해 실패했다. 정렬 제거 기능 자체는 위의 수동 build + Playwright 지도 시나리오로 회귀 확인을 마쳤고, smoke 래퍼 이슈는 별도 인프라 문제로 남긴다. + +### 2026-04-02 01:18 KST: 공개 등록/회원가입 화면의 불필요한 설명 카피 정리 +- 완료 내용 + - `/Users/alex/project/altteulmap/src/app/submit/page.tsx`에서 비로그인 안내 문구 `로그인 없이도 장소를 등록할 수 있습니다.`와 제출 전 설명 문단을 제거하고, 로그인 상태일 때만 `등록 계정` 배지만 보이게 정리했다. + - `/Users/alex/project/altteulmap/src/features/submission/place-submit-form.tsx`에서 이름/주소 입력 아래 보조 설명 문구와 하단 정책 설명 박스를 제거해 라벨과 입력 중심으로 단순화했다. + - `/Users/alex/project/altteulmap/src/app/signup/page.tsx`, `/Users/alex/project/altteulmap/src/features/auth/signup-form.tsx`에서 회원가입 화면의 로컬 DB 안내, 자동 로그인 설명, 비활성 상태 설명 박스를 제거해 기능 중심 레이아웃으로 맞췄다. +- 검증 결과 + - `rg -n "로그인 없이도|운영자가|공개 등록은|정확한 도로명 주소만|가입이 완료되면|DB 연결|기존 계정|소셜 로그인만" src/app/submit/page.tsx src/features/submission/place-submit-form.tsx src/app/login/page.tsx src/features/auth/login-form.tsx src/app/signup/page.tsx src/features/auth/signup-form.tsx`에서 정리 대상 정적 설명 문구가 제거된 것 확인 + - `npm run verify:quick` 통과 + +### 2026-04-01 KST: GitHub Actions를 빠른 main smoke와 PR full 검증으로 분리 +- 완료 내용 + - `/Users/alex/project/altteulmap/package.json`에 `test:e2e:smoke:ci`, `test:e2e:full:ci`, `test:e2e:smoke`, `test:e2e:full`을 추가하고, 기존 `test:e2e`는 full 세트를 가리키도록 정리했다. `main`용 smoke 세트는 `map`, `signup`, `submission-admin`만 먼저 돌고, 모바일/댓글/신고/가격 검토 흐름은 full 세트에 남긴다. + - `/Users/alex/project/altteulmap/.github/workflows/ci.yml`에서 E2E 단일 job을 `E2E Smoke`와 `E2E Full`로 나눴다. `push`에서는 `verify:quick + smoke + deploy:check`, `pull_request/workflow_dispatch`에서는 `verify + full`이 돌도록 바꿨다. + - 같은 workflow에서 `Verify`와 E2E 사이의 직렬 의존성을 제거해 병렬로 시작되게 했고, Playwright 브라우저 캐시(`~/.cache/ms-playwright`)를 추가해 반복 실행 시간을 줄였다. + - `push` 경로의 `Verify`는 `lint`만 돌고 실제 build는 smoke E2E에서 한 번만 수행하도록 바꿔, main 푸시 기준 중복 build를 줄였다. + - `/Users/alex/project/altteulmap/scripts/run-local-e2e.mjs`를 추가해 로컬 `test:e2e:*` 명령이 `.env.production.local` 대신 `.env`/`.env.local` 기준으로 돌게 했고, Playwright 서버용 `NEXTAUTH_URL=http://127.0.0.1:3107` 고정과 `db:push -> db:seed` 선행도 같이 처리했다. + - `/Users/alex/project/altteulmap/README.md`에 새 E2E 스크립트와 CI 분기 기준을 반영했다. +- 검증 결과 + - `npm run verify:quick` 통과 + - `npm run test:e2e:smoke` 통과 + - `npm run verify` 통과 +- 메모 + - GitHub Actions 로그의 `azure.archive.ubuntu.com`는 GitHub-hosted Ubuntu runner가 Playwright 실행용 시스템 패키지를 apt mirror에서 받는 정상 로그다. 프로젝트와 무관한 인프라 로그다. + - 로컬 `npm run verify`는 `next build` 특성상 `.env.production.local`을 먼저 읽기 때문에 production DB에 테이블이 없으면 build 로그에 mock fallback 메시지가 남을 수 있다. 빌드는 통과했고, 실제 로컬 E2E는 새 래퍼가 local env를 우선 주입해 우회한다. + +### 2026-04-02 01:10 KST: 공개 장소 등록을 텍스트-only로 단순화하고 운영자 승인 단계로 위치 확정 이관 +- 완료 내용 + - `/Users/alex/project/altteulmap/PLAN.md`에서 기존 `공개 폼 내부 위치 확인 필수` 계획을 폐기하고, `공개 등록은 텍스트 입력만 받고 위치/지도 처리는 운영자 승인 단계에서 수행`하는 새 기준으로 cycle 항목을 갱신했다. + - `/Users/alex/project/altteulmap/src/features/submission/schema.ts`에서 공개 장소 등록 schema에서 `latitude`/`longitude`를 제거했다. 이제 공개 제출 API는 이름, 주소, 카테고리, 가격, 메모 같은 텍스트 정보만 검증하며, 승인용 `placeModerationSchema`만 좌표 입력을 계속 요구한다. + - `/Users/alex/project/altteulmap/src/features/submission/place-submit-form.tsx`를 전면 단순화했다. 네이버 geocoding import, `주소로 위치 확인` 버튼, hidden 좌표 필드, 제출 전 내부 위치 확인 로직을 모두 제거했고, 운영자가 승인 단계에서 지도 위치와 네이버 지도 검색 결과를 확인한다는 안내 문구로 교체했다. + - 공개 제출 흐름에서 더 이상 쓰지 않는 `/Users/alex/project/altteulmap/src/features/submission/place-coordinate-picker.tsx`를 삭제해, 다음 세션에서 공개 등록이 좌표 picker를 쓰는 것으로 오해하지 않게 정리했다. + - `/Users/alex/project/altteulmap/src/features/places/repository.ts`는 공개 장소 등록을 항상 `latitude: null`, `longitude: null` 상태의 `pending_review`로 저장하도록 바꿨고, 제출 preview에서도 좌표를 더 이상 노출하지 않게 맞췄다. + - `/Users/alex/project/altteulmap/src/app/submit/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/places/page.tsx`, `/Users/alex/project/altteulmap/src/features/places/admin-place-review-form.tsx`, `/Users/alex/project/altteulmap/prd.md`, `/Users/alex/project/altteulmap/trd.md`를 새 정책에 맞게 갱신했다. 공개 제보는 텍스트-only, 운영자는 승인 단계에서 주소와 네이버 지도 검색 결과를 참고해 좌표를 확정하는 흐름으로 설명을 통일했다. + - `/Users/alex/project/altteulmap/tests/e2e/submission-admin.spec.ts`는 회귀 기준을 바꿨다. 공개 폼에 위치 확인 UI가 노출되지 않는지 확인하고, 텍스트-only 등록 후 운영자가 좌표를 입력해 승인하면 홈 검색에 노출되는 시나리오를 유지했다. +- 검증 결과 + - `npm run verify:quick` 통과 + - `npm run verify` 통과 + - `DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/altteulmap USE_MOCK_DATA=false AUTH_SECRET=altteulmap-local-auth-secret-change-me NEXTAUTH_URL=http://127.0.0.1:3107 AUTH_DEMO_PASSWORD=demo1234 AUTH_ADMIN_PASSWORD=admin1234 npx playwright test tests/e2e/submission-admin.spec.ts` 통과 +- 메모 + - 이번 변경은 `2026-04-02 00:05 KST`에 기록된 `공개 폼 내부 위치 확인 필수` 정책을 대체한다. 이후 공개 등록 UX는 좌표/geocoding을 전제로 두지 않는다. + - `npm run verify` 중 production build 경로에서는 로컬 DB에 `places` 테이블이 없을 때 기존과 동일하게 mock fallback 로그가 남았지만, 빌드 자체는 정상 통과했다. + ### 2026-04-02 00:20 KST: credentials 회원가입 실동작 추가 - 완료 내용 - `/Users/alex/project/altteulmap/src/db/schema.ts`, `/Users/alex/project/altteulmap/drizzle/0006_boring_titania.sql`, `/Users/alex/project/altteulmap/drizzle/meta/0006_snapshot.json`에 `auth_accounts.password_hash`를 추가해 credentials 계정 비밀번호를 별도 해시로 저장할 수 있게 했다. diff --git a/README.md b/README.md index 4315eac..a3c9521 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ npm run dev npm run lint npm run build npm run verify +npm run test:e2e:smoke npm run test:e2e npm run smoke:local npm run deploy:check @@ -38,6 +39,7 @@ npm run preview - `lint`: ESLint 검사 - `build`: Next.js 프로덕션 빌드 - `verify`: 현재 프로젝트 기준 전체 기본 검증(`lint + build`) +- `test:e2e:smoke`: `main` 푸시 기준의 빠른 Playwright smoke 세트 - `test:e2e`: Playwright E2E 실행 - `smoke:local`: 실행 중인 로컬 서버에 대해 SEO/API/credentials 로그인 기본 스모크 체크 - `deploy:check`: Cloudflare 배포 전 필수 환경 변수와 URL 설정 점검 @@ -47,10 +49,14 @@ npm run preview - `db:push`: 로컬/개발 DB에 스키마 반영 - `db:seed`: 로컬 DB에 목업 시드 데이터 입력 - `db:down`: 로컬 Postgres 컨테이너 중지 +- `cf:clean`: Cloudflare 빌드 전 `.next`, `.open-next` 정리 +- `cf:build`: Cloudflare 배포용 clean build - `preview`: OpenNext로 Cloudflare Workers 런타임 미리보기 `deploy`, `upload`는 Cloudflare 계정과 Wrangler 인증이 준비된 뒤 사용하면 됩니다. +Cloudflare 무료 플랜 기준 경량화는 `next build --webpack` + `cf:clean` 경로를 전제로 맞춰져 있습니다. 배포는 `npm run deploy`를 그대로 쓰면 됩니다. + ## DB 시작 DB 없이도 앱은 바로 실행됩니다. `.env`가 없거나 `USE_MOCK_DATA=true`면 자동으로 목업 데이터를 사용합니다. @@ -77,7 +83,7 @@ DB가 연결된 상태에서는 `/signup`에서 새 이메일 계정을 직접 기본 예시는 `.env.example`에 들어 있고, 로컬 `.env`도 같은 값으로 맞춰두었습니다. -`/map`에서 실제 네이버 지도를 보려면 `NEXT_PUBLIC_NAVER_MAP_KEY_ID`를 설정하면 됩니다. 아직 키가 없으면 같은 화면에서 자동으로 임시 프리뷰 지도로 fallback됩니다. 기존 `NEXT_PUBLIC_NAVER_MAP_CLIENT_ID` 값도 함께 지원합니다. +`/`에서 실제 네이버 지도를 보려면 `NEXT_PUBLIC_NAVER_MAP_KEY_ID`를 설정하면 됩니다. 아직 키가 없으면 같은 화면에서 자동으로 임시 프리뷰 지도로 fallback됩니다. 기존 `NEXT_PUBLIC_NAVER_MAP_CLIENT_ID` 값도 함께 지원합니다. `NEXTAUTH_URL`은 로그인 callback뿐 아니라 `robots.txt`, `sitemap.xml`, canonical metadata의 기준 URL로도 사용합니다. 배포 시에는 반드시 실제 도메인으로 바꿔야 합니다. @@ -124,13 +130,15 @@ Playwright E2E는 아래 명령으로 실행합니다. ```bash npm run playwright:install +npm run test:e2e:smoke npm run test:e2e ``` -현재 `npm run test:e2e`는 로컬 안정성을 위해 빌드 후 세 그룹으로 나눠 실행합니다. -- `signup`, `bookmarks`, `map` -- `map.mobile` (`mobile-chromium`, `USE_MOCK_DATA=true`) -- `comments`, `price-review`, `report-admin`, `submission-admin` +현재 E2E는 로컬 안정성을 위해 빌드 후 그룹별로 나뉩니다. +- `test:e2e:smoke`: `map`, `admin-dashboard`, `signup`, `submission-admin` +- `test:e2e`: smoke + `map.mobile` + `bookmarks`, `comments`, `price-review`, `report-admin` + +로컬 E2E 명령은 `.env.production.local`이 있어도 `.env`와 `.env.local` 값을 우선 주입해 local DB와 local auth 기준으로 실행합니다. 이때 `NEXTAUTH_URL`은 Playwright 서버 포트에 맞춰 `http://127.0.0.1:3107`로 고정되고, DB 기반 세트는 `db:push -> db:seed`를 먼저 실행합니다. CI는 반대로 workflow env를 명시적으로 주입합니다. 현재 기본 E2E는 아래 흐름을 검증합니다. - 지도 첫 진입 @@ -138,8 +146,8 @@ npm run test:e2e - 모바일 목록 시트 열기/닫기 - 모바일 목록 -> 상세 시트 -> 지도 복귀 - 비회원 좋아요/취소 -- 좋아요순 정렬 - 공유 버튼 fallback +- 운영자 로그인 후 관리 진입과 로그아웃 - credentials 로그인 - credentials 회원가입 - 로그인 없는 장소 등록 @@ -157,15 +165,22 @@ Cloudflare 배포 전 점검은 아래 문서를 기준으로 합니다. ## CI/CD -- GitHub Actions: `Verify`, `E2E`, `Deploy Config Check` +- GitHub Actions: `Verify`, `E2E Smoke`, `E2E Full`, `Deploy Config Check` - Cloudflare Builds: `main` push 후 자동 배포 현재 권장 운영 방식은 `GitHub Actions가 검사`, `Cloudflare Builds가 배포`를 맡는 구조입니다. -- `Verify`: `npm run verify` -- `E2E`: 로컬 Postgres service container + `npm run test:e2e` +- `push to main` + - `Verify`: `npm run verify:quick` + - `E2E Smoke`: 로컬 Postgres service container + `npm run test:e2e:smoke` + - `Deploy Config Check`: 운영 env 기준 `npm run deploy:check` +- `pull_request`, `workflow_dispatch` + - `Verify`: `npm run verify` + - `E2E Full`: 로컬 Postgres service container + `npm run test:e2e` - `Deploy Config Check`: GitHub repo `Secrets/Variables`에 저장한 운영 env로 `npm run deploy:check` +Playwright 브라우저는 GitHub Actions에서 `~/.cache/ms-playwright`를 캐시해 재실행 시간을 줄입니다. + GitHub Actions의 운영 env 이름은 `.env.production.local`과 동일하게 맞추는 것을 기준으로 합니다. 작업을 마치고 로컬 DB를 내리려면: diff --git a/docs/cloudflare-account-to-deploy.md b/docs/cloudflare-account-to-deploy.md index c343458..1b5f71d 100644 --- a/docs/cloudflare-account-to-deploy.md +++ b/docs/cloudflare-account-to-deploy.md @@ -102,6 +102,10 @@ npx wrangler login 현재 저장소는 `npm run deploy` 시 로컬에서 OpenNext build를 만든 뒤 Cloudflare로 업로드한다. +현재 배포 경로는 Cloudflare Workers Free 한도 기준으로 `next build --webpack`과 clean build를 전제로 맞춰져 있다. +- `npm run deploy`는 내부적으로 `cf:clean -> opennextjs-cloudflare build -> opennextjs-cloudflare deploy` 순서로 실행된다. +- 직접 배포할 때는 `.next`, `.open-next`를 남긴 채 재사용하지 않는 것이 안전하다. + 즉 환경 변수는 두 군데가 모두 중요하다. ### A. 로컬 빌드 환경 변수 diff --git a/docs/deploy-cloudflare.md b/docs/deploy-cloudflare.md index 4797adf..9034234 100644 --- a/docs/deploy-cloudflare.md +++ b/docs/deploy-cloudflare.md @@ -56,6 +56,9 @@ npm run smoke:local npm run deploy:check ``` +배포는 `npm run deploy`를 사용한다. 이 명령은 `.next`, `.open-next`를 먼저 비우고 다시 OpenNext build를 만든 뒤 업로드한다. +현재 저장소는 Cloudflare Workers Free 한도에 맞추기 위해 `webpack` build를 사용한다. + preview 기준으로 로컬 URL 허용 상태만 보려면: ```bash @@ -63,7 +66,7 @@ npm run deploy:check -- --preview ``` ## 7. 첫 배포 직후 확인 -1. `/map`에서 지도 렌더링 +1. `/`에서 지도 렌더링 2. `/login`에서 카카오/네이버 로그인 버튼 노출 3. `/robots.txt` 4. `/sitemap.xml` diff --git a/eslint.config.mjs b/eslint.config.mjs index 0dfda7c..b9296aa 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -9,6 +9,7 @@ const eslintConfig = defineConfig([ globalIgnores([ // Default ignores of eslint-config-next: ".next/**", + ".next-dev/**", "out/**", "build/**", "next-env.d.ts", diff --git a/next.config.ts b/next.config.ts index 437d71c..2e84e56 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,8 +1,12 @@ import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; import type { NextConfig } from "next"; +const isDevServer = + process.argv.some((argument) => argument === "dev") || + process.env.NODE_ENV === "development"; + const nextConfig: NextConfig = { - /* config options here */ + distDir: isDevServer ? ".next-dev" : ".next", }; initOpenNextCloudflareForDev(); diff --git a/package.json b/package.json index e85ae5e..17ecc47 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "version": "0.1.0", "private": true, "scripts": { - "build": "next build", - "dev": "next dev", + "build": "next build --webpack", + "dev": "rm -rf .next-dev && next dev --webpack", "start": "next start", "lint": "eslint", "e2e:prepare": "lsof -tiTCP:3107 -sTCP:LISTEN | xargs -r kill && rm -rf .next test-results playwright-report && mkdir -p .next/types .next/dev/types && printf 'export {};\\n' > .next/types/routes.d.ts && printf 'export {};\\n' > .next/dev/types/routes.d.ts", - "test:e2e:smoke:ci": "playwright test tests/e2e/map.spec.ts tests/e2e/signup.spec.ts tests/e2e/submission-admin.spec.ts", + "test:e2e:smoke:ci": "playwright test tests/e2e/map.spec.ts tests/e2e/admin-dashboard.spec.ts tests/e2e/signup.spec.ts tests/e2e/submission-admin.spec.ts", "test:e2e:full:ci": "npm run test:e2e:smoke:ci && USE_MOCK_DATA=true playwright test tests/e2e/map.mobile.spec.ts --project mobile-chromium && playwright test tests/e2e/bookmarks.spec.ts tests/e2e/comments.spec.ts tests/e2e/price-review.spec.ts tests/e2e/report-admin.spec.ts", "test:e2e:smoke": "node scripts/run-local-e2e.mjs smoke", "test:e2e:full": "node scripts/run-local-e2e.mjs full", @@ -22,15 +22,17 @@ "smoke:local": "node scripts/smoke-local.mjs", "deploy:check": "node scripts/check-cloudflare-deploy.mjs", "hooks:install": "node scripts/git-hooks/install-hooks.mjs", + "cf:clean": "rm -rf .next .next-dev .open-next", + "cf:build": "npm run cf:clean && opennextjs-cloudflare build", "db:generate": "drizzle-kit generate", "db:push": "drizzle-kit push", "db:seed": "tsx src/db/seed.ts", "db:studio": "drizzle-kit studio", "db:up": "docker compose up -d postgres", "db:down": "docker compose down", - "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview", - "deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy", - "upload": "opennextjs-cloudflare build && opennextjs-cloudflare upload", + "preview": "npm run cf:build && opennextjs-cloudflare preview", + "deploy": "npm run cf:build && opennextjs-cloudflare deploy", + "upload": "npm run cf:build && opennextjs-cloudflare upload", "cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts" }, "dependencies": { diff --git a/prd.md b/prd.md index 49e930d..3dd2bbf 100644 --- a/prd.md +++ b/prd.md @@ -194,10 +194,10 @@ MVP 핵심 기능은 다음과 같다. - 장소 등록 시 최소 입력값: - 장소 이름 - 주소 - - 텍스트 주소 기반 위치 확인 - 카테고리 - 가격 또는 가격 항목 1개 이상 - 메모(선택) +- 공개 등록은 텍스트 정보만 받고, 지도 표시 위치와 네이버 지도 검색 확인은 운영자가 승인 단계에서 처리한다. - 이후 가격 항목 추가 등록이 가능해야 한다. #### 7.2.7 검증 처리 @@ -246,6 +246,8 @@ MVP 핵심 기능은 다음과 같다. - 코멘트 작성 - 신고/수정 요청 - 기존 장소 가격 제보 +- 로그인 상태에서 상단 계정 라벨과 로그아웃 액션 확인 +- 운영자 계정이면 관리자 화면 진입 ### 8.3 로그인 방식 MVP 로그인 방식은 다음을 지원한다. @@ -376,7 +378,8 @@ MVP 이후 아래 개선을 고려한다. - 지도 - 등록 - 북마크 -- 로그인/마이페이지 +- 로그인/로그아웃 +- 운영자 계정일 때 관리자 진입 --- @@ -401,7 +404,7 @@ MVP 이후 아래 개선을 고려한다. ### 13.2 가격 모델 원칙 - 카테고리별 대표 가격 기준을 정의할 수 있다. -- 대표 가격 기준은 검색/필터/정렬에 활용할 수 있다. +- 대표 가격 기준은 검색과 가격 필터에 활용할 수 있다. - 필수 입력은 아니며, 자유 가격 항목 등록을 허용한다. - 장소 단위와 가격 항목 단위를 분리하여 저장해야 한다. diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 8845316..447e3df 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -2,6 +2,8 @@ import Link from "next/link"; import { redirect } from "next/navigation"; import { AccessDeniedPanel } from "@/components/access-denied-panel"; +import { getAdminOverview } from "@/features/admin/repository"; +import { SessionActionGroup } from "@/features/auth/session-action-group"; import { listPendingPlaces, listPendingPriceReports, @@ -25,7 +27,7 @@ export default async function AdminPage() { if (user.role !== "admin") { return ( report.status === "open" || report.status === "reviewing", - ).length; + ); return (
-

- Admin -

-

- 운영 대시보드 초안 -

-

- 로컬 개발 단계에서 제보 승인과 신고 검토를 끝까지 확인할 수 있도록 - 최소 운영 도구를 붙였습니다. -

+
+
+

+ 운영 +

+

+ 운영 대시보드 +

+

+ 승인 대기 목록, 신고 큐, 사용자 현황을 한 화면에서 보고 바로 + 운영 액션으로 넘어갈 수 있게 정리했습니다. +

+
+ +
+ +
+ + 운영 데이터: {overview.source === "database" ? "실데이터" : "목업"} + + + 방문 지표: 수집 전 + +
+ +
+
+

+ Users +

+

+ {overview.stats.totalUsers} +

+

+ 일반 {overview.stats.regularUsers}명 · 운영자 {overview.stats.adminUsers} + 명 +

+
+ +
+

+ Sessions +

+

+ {overview.stats.currentSessions} +

+

+ 현재 세션 사용자 {overview.stats.activeUsers}명 +

+
+ +
+

+ Places +

+

+ {overview.stats.activePlaces} +

+

+ 공개 중 장소 · 승인 대기 {overview.stats.pendingPlaces}건 +

+
+ +
+

+ Reports +

+

+ {overview.stats.openReports} +

+

+ 열린 신고 · 가격 제보 대기 {overview.stats.pendingPriceReports}건 +

+
+

- Place queue + 장소 검토

승인 대기 장소 {pendingPlaces.items.length}건

- 등록 폼으로 들어온 신규 장소 제보를 승인하거나 반려합니다. 제출된 - 좌표를 검수하고 필요하면 조정한 뒤 공개 목록에 반영합니다. + 공개 폼으로 들어온 텍스트 기반 장소 제보를 승인하거나 반려합니다. + 운영자가 주소와 네이버 지도 검색 결과를 확인한 뒤 좌표를 확정해 + 공개 목록에 반영합니다.

장소 검토 열기 @@ -82,7 +171,7 @@ export default async function AdminPage() {

- Price queue + 가격 검토

대기 중인 가격 제보 {pendingPriceReports.items.length}건 @@ -92,6 +181,7 @@ export default async function AdminPage() {

가격 제보 검토 열기 @@ -100,22 +190,187 @@ export default async function AdminPage() {

- Report queue + 신고 검토

- 열린 신고 {openReportCount}건 + 열린 신고 {openReports.length}건

가격 오류, 중복 장소, 정보 오류 신고를 상태별로 관리합니다.

신고 검토 열기

+ +
+
+
+

+ 최근 가입 사용자 +

+

+ 최근 생성된 계정과 현재 세션 상태를 같이 확인합니다. +

+
+ + {overview.recentUsers.length}명 + +
+ +
+ {overview.recentUsers.map((account) => ( +
+
+
+

+ {account.role === "admin" ? "운영자" : "일반 사용자"} +

+

+ {account.nickname || account.email} +

+

{account.email}

+
+ + {account.hasActiveSession ? "세션 활성" : "세션 없음"} + +
+

+ 가입 {account.joinedAt} +

+
+ ))} +
+
+ +
+
+
+
+

+ 최신 장소 등록 목록 +

+

+ 운영자 승인이 필요한 신규 장소 제보입니다. +

+
+ + 전체 보기 + +
+ + {pendingPlaces.items.length > 0 ? ( +
+ {pendingPlaces.items.slice(0, 4).map((place) => ( +
+

+ 접수 {place.createdAt} +

+

+ {place.name} +

+

{place.address}

+

{place.district}

+
+ ))} +
+ ) : ( +
+ 현재 승인 대기 장소가 없습니다. +
+ )} +
+ +
+
+
+

+ 최신 신고 목록 +

+

+ 열린 상태의 신고만 먼저 요약해 보여줍니다. +

+
+ + 전체 보기 + +
+ + {openReports.length > 0 ? ( +
+ {openReports.slice(0, 4).map((report) => ( +
+

+ {report.status === "open" ? "열림" : "검토 중"} · {report.createdAt} +

+

+ {report.placeName} +

+

{report.detail}

+
+ ))} +
+ ) : ( +
+ 현재 열린 신고가 없습니다. +
+ )} +
+
+ +
+
+
+

+ 방문/활성 사용자 지표 +

+

+ 현재 DB에는 방문 이벤트가 저장되지 않아 방문 수, DAU/WAU, + 재방문율은 아직 계산하지 않습니다. 다음 단계에서 visit/activity + 적재를 붙인 뒤 이 섹션에 운영 지표를 추가합니다. +

+
+ + 2차 작업 예정 + +
+
); diff --git a/src/app/api/admin/places/[id]/route.ts b/src/app/api/admin/places/[id]/route.ts index 0d063a2..6ea06e4 100644 --- a/src/app/api/admin/places/[id]/route.ts +++ b/src/app/api/admin/places/[id]/route.ts @@ -1,6 +1,4 @@ import { revalidatePath } from "next/cache"; -import { NextResponse } from "next/server"; - import { listPendingPlaces, moderatePlaceSubmission, @@ -18,7 +16,7 @@ export async function GET(_: Request, context: RouteContext) { const user = await getSessionUser(); if (!user) { - return NextResponse.json( + return Response.json( { ok: false, message: "로그인이 필요합니다.", @@ -28,7 +26,7 @@ export async function GET(_: Request, context: RouteContext) { } if (user.role !== "admin") { - return NextResponse.json( + return Response.json( { ok: false, message: "운영자 권한이 필요합니다.", @@ -41,7 +39,7 @@ export async function GET(_: Request, context: RouteContext) { const result = await listPendingPlaces(); const item = result.items.find((place) => place.id === id) ?? null; - return NextResponse.json( + return Response.json( { item, source: result.source, @@ -55,7 +53,7 @@ export async function PATCH(request: Request, context: RouteContext) { const user = await getSessionUser(); if (!user) { - return NextResponse.json( + return Response.json( { ok: false, message: "로그인이 필요합니다.", @@ -65,7 +63,7 @@ export async function PATCH(request: Request, context: RouteContext) { } if (user.role !== "admin") { - return NextResponse.json( + return Response.json( { ok: false, message: "운영자 권한이 필요합니다.", @@ -78,7 +76,7 @@ export async function PATCH(request: Request, context: RouteContext) { const parsed = placeModerationSchema.safeParse(body); if (!parsed.success) { - return NextResponse.json( + return Response.json( { ok: false, message: "장소 검토 입력값 검증에 실패했습니다.", @@ -101,5 +99,5 @@ export async function PATCH(request: Request, context: RouteContext) { revalidatePath(`/api/places/${id}`); } - return NextResponse.json(result, { status: result.ok ? 200 : 404 }); + return Response.json(result, { status: result.ok ? 200 : 404 }); } diff --git a/src/app/api/admin/places/route.ts b/src/app/api/admin/places/route.ts index e48b0f7..8730e34 100644 --- a/src/app/api/admin/places/route.ts +++ b/src/app/api/admin/places/route.ts @@ -1,5 +1,3 @@ -import { NextResponse } from "next/server"; - import { listPendingPlaces } from "@/features/places/repository"; import { getSessionUser } from "@/lib/session"; @@ -9,7 +7,7 @@ export async function GET() { const user = await getSessionUser(); if (!user) { - return NextResponse.json( + return Response.json( { ok: false, message: "로그인이 필요합니다.", @@ -19,7 +17,7 @@ export async function GET() { } if (user.role !== "admin") { - return NextResponse.json( + return Response.json( { ok: false, message: "운영자 권한이 필요합니다.", @@ -30,7 +28,7 @@ export async function GET() { const result = await listPendingPlaces(); - return NextResponse.json({ + return Response.json({ items: result.items, count: result.items.length, source: result.source, diff --git a/src/app/api/admin/price-items/[id]/route.ts b/src/app/api/admin/price-items/[id]/route.ts index 6bfd9d8..65fd15d 100644 --- a/src/app/api/admin/price-items/[id]/route.ts +++ b/src/app/api/admin/price-items/[id]/route.ts @@ -1,6 +1,4 @@ import { revalidatePath } from "next/cache"; -import { NextResponse } from "next/server"; - import { updatePriceItem } from "@/features/places/repository"; import { adminPriceItemUpdateSchema } from "@/features/places/write-schema"; import { getSessionUser } from "@/lib/session"; @@ -15,7 +13,7 @@ export async function PATCH(request: Request, context: RouteContext) { const user = await getSessionUser(); if (!user) { - return NextResponse.json( + return Response.json( { ok: false, message: "로그인이 필요합니다.", @@ -25,7 +23,7 @@ export async function PATCH(request: Request, context: RouteContext) { } if (user.role !== "admin") { - return NextResponse.json( + return Response.json( { ok: false, message: "운영자 권한이 필요합니다.", @@ -38,7 +36,7 @@ export async function PATCH(request: Request, context: RouteContext) { const parsed = adminPriceItemUpdateSchema.safeParse(body); if (!parsed.success) { - return NextResponse.json( + return Response.json( { ok: false, message: "가격 항목 수정 입력값 검증에 실패했습니다.", @@ -72,5 +70,5 @@ export async function PATCH(request: Request, context: RouteContext) { ? 400 : 404; - return NextResponse.json(result, { status }); + return Response.json(result, { status }); } diff --git a/src/app/api/admin/prices/[id]/route.ts b/src/app/api/admin/prices/[id]/route.ts index e011a22..2cebdd9 100644 --- a/src/app/api/admin/prices/[id]/route.ts +++ b/src/app/api/admin/prices/[id]/route.ts @@ -1,6 +1,4 @@ import { revalidatePath } from "next/cache"; -import { NextResponse } from "next/server"; - import { moderatePriceReport } from "@/features/places/repository"; import { priceReportModerationSchema } from "@/features/places/write-schema"; import { getSessionUser } from "@/lib/session"; @@ -15,7 +13,7 @@ export async function PATCH(request: Request, context: RouteContext) { const user = await getSessionUser(); if (!user) { - return NextResponse.json( + return Response.json( { ok: false, message: "로그인이 필요합니다.", @@ -25,7 +23,7 @@ export async function PATCH(request: Request, context: RouteContext) { } if (user.role !== "admin") { - return NextResponse.json( + return Response.json( { ok: false, message: "운영자 권한이 필요합니다.", @@ -38,7 +36,7 @@ export async function PATCH(request: Request, context: RouteContext) { const parsed = priceReportModerationSchema.safeParse(body); if (!parsed.success) { - return NextResponse.json( + return Response.json( { ok: false, message: "가격 제보 검토 입력값 검증에 실패했습니다.", @@ -61,5 +59,5 @@ export async function PATCH(request: Request, context: RouteContext) { revalidatePath("/api/places/map"); } - return NextResponse.json(result, { status: result.ok ? 200 : 404 }); + return Response.json(result, { status: result.ok ? 200 : 404 }); } diff --git a/src/app/api/admin/prices/route.ts b/src/app/api/admin/prices/route.ts index 6110963..114f0e5 100644 --- a/src/app/api/admin/prices/route.ts +++ b/src/app/api/admin/prices/route.ts @@ -1,5 +1,3 @@ -import { NextResponse } from "next/server"; - import { listPendingPriceReports } from "@/features/places/repository"; import { getSessionUser } from "@/lib/session"; @@ -9,7 +7,7 @@ export async function GET() { const user = await getSessionUser(); if (!user) { - return NextResponse.json( + return Response.json( { ok: false, message: "로그인이 필요합니다.", @@ -19,7 +17,7 @@ export async function GET() { } if (user.role !== "admin") { - return NextResponse.json( + return Response.json( { ok: false, message: "운영자 권한이 필요합니다.", @@ -30,7 +28,7 @@ export async function GET() { const result = await listPendingPriceReports(); - return NextResponse.json({ + return Response.json({ items: result.items, count: result.items.length, source: result.source, diff --git a/src/app/api/admin/reports/[id]/route.ts b/src/app/api/admin/reports/[id]/route.ts index a040fd8..d2732c1 100644 --- a/src/app/api/admin/reports/[id]/route.ts +++ b/src/app/api/admin/reports/[id]/route.ts @@ -1,6 +1,4 @@ import { revalidatePath } from "next/cache"; -import { NextResponse } from "next/server"; - import { updateReportStatus } from "@/features/reports/repository"; import { reportModerationSchema } from "@/features/reports/schema"; import { getSessionUser } from "@/lib/session"; @@ -15,7 +13,7 @@ export async function PATCH(request: Request, context: RouteContext) { const user = await getSessionUser(); if (!user) { - return NextResponse.json( + return Response.json( { ok: false, message: "로그인이 필요합니다.", @@ -25,7 +23,7 @@ export async function PATCH(request: Request, context: RouteContext) { } if (user.role !== "admin") { - return NextResponse.json( + return Response.json( { ok: false, message: "운영자 권한이 필요합니다.", @@ -38,7 +36,7 @@ export async function PATCH(request: Request, context: RouteContext) { const parsed = reportModerationSchema.safeParse(body); if (!parsed.success) { - return NextResponse.json( + return Response.json( { ok: false, message: "신고 상태 검증에 실패했습니다.", @@ -57,5 +55,5 @@ export async function PATCH(request: Request, context: RouteContext) { revalidatePath("/api/admin/reports"); } - return NextResponse.json(result, { status: result.ok ? 200 : 404 }); + return Response.json(result, { status: result.ok ? 200 : 404 }); } diff --git a/src/app/api/admin/reports/route.ts b/src/app/api/admin/reports/route.ts index 95a72fa..551be5c 100644 --- a/src/app/api/admin/reports/route.ts +++ b/src/app/api/admin/reports/route.ts @@ -1,5 +1,3 @@ -import { NextResponse } from "next/server"; - import { listReports } from "@/features/reports/repository"; import { getSessionUser } from "@/lib/session"; @@ -9,7 +7,7 @@ export async function GET() { const user = await getSessionUser(); if (!user) { - return NextResponse.json( + return Response.json( { ok: false, message: "로그인이 필요합니다.", @@ -19,7 +17,7 @@ export async function GET() { } if (user.role !== "admin") { - return NextResponse.json( + return Response.json( { ok: false, message: "운영자 권한이 필요합니다.", @@ -30,7 +28,7 @@ export async function GET() { const result = await listReports(); - return NextResponse.json({ + return Response.json({ items: result.items, count: result.items.length, source: result.source, diff --git a/src/app/api/bookmarks/[id]/route.ts b/src/app/api/bookmarks/[id]/route.ts index fd7204c..9ff7652 100644 --- a/src/app/api/bookmarks/[id]/route.ts +++ b/src/app/api/bookmarks/[id]/route.ts @@ -1,6 +1,4 @@ import { revalidatePath } from "next/cache"; -import { NextResponse } from "next/server"; - import { setBookmark } from "@/features/bookmarks/repository"; import { bookmarkToggleSchema } from "@/features/bookmarks/schema"; import { getSessionUser } from "@/lib/session"; @@ -15,7 +13,7 @@ export async function PUT(request: Request, context: RouteContext) { const user = await getSessionUser(); if (!user) { - return NextResponse.json( + return Response.json( { ok: false, message: "로그인이 필요합니다.", @@ -29,7 +27,7 @@ export async function PUT(request: Request, context: RouteContext) { const parsed = bookmarkToggleSchema.safeParse(body); if (!parsed.success) { - return NextResponse.json( + return Response.json( { ok: false, message: "북마크 입력값 검증에 실패했습니다.", @@ -51,5 +49,5 @@ export async function PUT(request: Request, context: RouteContext) { revalidatePath(`/api/places/${id}`); } - return NextResponse.json(result, { status: result.ok ? 200 : 404 }); + return Response.json(result, { status: result.ok ? 200 : 404 }); } diff --git a/src/app/api/bookmarks/route.ts b/src/app/api/bookmarks/route.ts index ba2b579..e359709 100644 --- a/src/app/api/bookmarks/route.ts +++ b/src/app/api/bookmarks/route.ts @@ -1,5 +1,3 @@ -import { NextResponse } from "next/server"; - import { listBookmarks } from "@/features/bookmarks/repository"; import { getSessionUser } from "@/lib/session"; @@ -9,7 +7,7 @@ export async function GET() { const user = await getSessionUser(); if (!user) { - return NextResponse.json( + return Response.json( { ok: false, message: "로그인이 필요합니다.", @@ -20,7 +18,7 @@ export async function GET() { const result = await listBookmarks(user); - return NextResponse.json({ + return Response.json({ items: result.items, count: result.items.length, source: result.source, diff --git a/src/app/api/categories/route.ts b/src/app/api/categories/route.ts index de5298c..98419ca 100644 --- a/src/app/api/categories/route.ts +++ b/src/app/api/categories/route.ts @@ -1,9 +1,7 @@ -import { NextResponse } from "next/server"; - import { categoryGroups, categoryOptions } from "@/features/categories/catalog"; export function GET() { - return NextResponse.json({ + return Response.json({ groups: categoryGroups, categories: categoryOptions, }); diff --git a/src/app/api/places/[id]/comments/[commentId]/route.ts b/src/app/api/places/[id]/comments/[commentId]/route.ts index 33b6320..8db4a3c 100644 --- a/src/app/api/places/[id]/comments/[commentId]/route.ts +++ b/src/app/api/places/[id]/comments/[commentId]/route.ts @@ -1,6 +1,4 @@ import { revalidatePath } from "next/cache"; -import { NextResponse } from "next/server"; - import { deletePlaceComment } from "@/features/places/repository"; import { getPublicWriteActor } from "@/lib/public-write-actor"; @@ -17,7 +15,7 @@ export async function DELETE(request: Request, context: RouteContext) { }); if (!actor.user && !actor.visitorId) { - return NextResponse.json( + return Response.json( { ok: false, message: "삭제 권한이 없습니다.", @@ -41,5 +39,5 @@ export async function DELETE(request: Request, context: RouteContext) { const status = result.ok ? 200 : result.message === "삭제 권한이 없습니다." ? 403 : 404; - return NextResponse.json(result, { status }); + return Response.json(result, { status }); } diff --git a/src/app/api/places/[id]/comments/route.ts b/src/app/api/places/[id]/comments/route.ts index bb959e3..f23c591 100644 --- a/src/app/api/places/[id]/comments/route.ts +++ b/src/app/api/places/[id]/comments/route.ts @@ -1,6 +1,4 @@ import { revalidatePath } from "next/cache"; -import { NextResponse } from "next/server"; - import { createPlaceComment } from "@/features/places/repository"; import { placeCommentSchema } from "@/features/places/write-schema"; import { @@ -26,7 +24,7 @@ export async function POST(request: Request, context: RouteContext) { }); if (!rateLimit.ok) { - const response = NextResponse.json( + const response = Response.json( { ok: false, message: "코멘트 등록 요청이 너무 빠릅니다. 잠시 후 다시 시도해주세요.", @@ -44,7 +42,7 @@ export async function POST(request: Request, context: RouteContext) { const parsed = placeCommentSchema.safeParse(body); if (!parsed.success) { - const response = NextResponse.json( + const response = Response.json( { ok: false, message: "코멘트 입력값 검증에 실패했습니다.", @@ -78,7 +76,7 @@ export async function POST(request: Request, context: RouteContext) { revalidatePath(`/api/places/${id}`); } - const response = NextResponse.json(result, { status: result.ok ? 200 : 404 }); + const response = Response.json(result, { status: result.ok ? 200 : 404 }); setPublicWriteActorCookie(response, actor, request); return response; } diff --git a/src/app/api/places/[id]/prices/route.ts b/src/app/api/places/[id]/prices/route.ts index 336f055..d9798ec 100644 --- a/src/app/api/places/[id]/prices/route.ts +++ b/src/app/api/places/[id]/prices/route.ts @@ -1,6 +1,4 @@ import { revalidatePath } from "next/cache"; -import { NextResponse } from "next/server"; - import { createPlacePriceReport } from "@/features/places/repository"; import { placePriceReportSchema } from "@/features/places/write-schema"; import { @@ -26,7 +24,7 @@ export async function POST(request: Request, context: RouteContext) { }); if (!rateLimit.ok) { - const response = NextResponse.json( + const response = Response.json( { ok: false, message: "가격 제보 요청이 너무 빠릅니다. 잠시 후 다시 시도해주세요.", @@ -44,7 +42,7 @@ export async function POST(request: Request, context: RouteContext) { const parsed = placePriceReportSchema.safeParse(body); if (!parsed.success) { - const response = NextResponse.json( + const response = Response.json( { ok: false, message: "가격 제보 입력값 검증에 실패했습니다.", @@ -69,7 +67,7 @@ export async function POST(request: Request, context: RouteContext) { revalidatePath(`/api/places/${id}`); } - const response = NextResponse.json(result, { status: result.ok ? 200 : 404 }); + const response = Response.json(result, { status: result.ok ? 200 : 404 }); setPublicWriteActorCookie(response, actor, request); return response; } diff --git a/src/app/api/places/[id]/reaction/route.ts b/src/app/api/places/[id]/reaction/route.ts index 0cce2c0..2c0231b 100644 --- a/src/app/api/places/[id]/reaction/route.ts +++ b/src/app/api/places/[id]/reaction/route.ts @@ -1,6 +1,4 @@ import { revalidatePath } from "next/cache"; -import { NextResponse } from "next/server"; - import { setPlaceReaction } from "@/features/places/repository"; import { placeReactionSchema } from "@/features/places/reaction-schema"; import { consumeRateLimit } from "@/lib/rate-limit"; @@ -30,7 +28,7 @@ export async function PUT(request: Request, context: RouteContext) { }); if (!rateLimit.ok) { - const response = NextResponse.json( + const response = Response.json( { ok: false, message: "반응 요청이 너무 빠릅니다. 잠시 후 다시 시도해주세요.", @@ -50,7 +48,7 @@ export async function PUT(request: Request, context: RouteContext) { const parsed = placeReactionSchema.safeParse(body); if (!parsed.success) { - const response = NextResponse.json( + const response = Response.json( { ok: false, message: "반응 입력값 검증에 실패했습니다.", @@ -88,7 +86,7 @@ export async function PUT(request: Request, context: RouteContext) { revalidatePath("/api/places/map"); } - const response = NextResponse.json(result, { status: result.ok ? 200 : 404 }); + const response = Response.json(result, { status: result.ok ? 200 : 404 }); if (!user && visitorId) { setVisitorIdCookie(response, visitorId, request.url); diff --git a/src/app/api/places/[id]/route.ts b/src/app/api/places/[id]/route.ts index e8ddb3d..32ceb4b 100644 --- a/src/app/api/places/[id]/route.ts +++ b/src/app/api/places/[id]/route.ts @@ -1,5 +1,3 @@ -import { NextResponse } from "next/server"; - import { getPlaceDetail } from "@/features/places/repository"; import { getSessionUser } from "@/lib/session"; import { getVisitorIdFromCookie } from "@/lib/visitor-id"; @@ -37,7 +35,7 @@ export async function GET(_: Request, context: RouteContext) { const place = result.item; if (!place) { - return NextResponse.json( + return Response.json( { error: { code: "NOT_FOUND", @@ -51,7 +49,7 @@ export async function GET(_: Request, context: RouteContext) { ); } - return NextResponse.json( + return Response.json( { item: place, source: result.source, diff --git a/src/app/api/places/map/route.ts b/src/app/api/places/map/route.ts index 726c5e1..e2f64e7 100644 --- a/src/app/api/places/map/route.ts +++ b/src/app/api/places/map/route.ts @@ -1,7 +1,4 @@ -import { NextRequest, NextResponse } from "next/server"; - import { listPlaces } from "@/features/places/repository"; -import type { PlaceSort } from "@/features/places/types"; export const dynamic = "force-dynamic"; @@ -29,20 +26,8 @@ function parseFiniteNumber(value: string | null) { return Number.isFinite(parsed) ? parsed : null; } -function parseSort(value: string | null): PlaceSort { - if (value === "recent") { - return "recent"; - } - - if (value === "likes") { - return "likes"; - } - - return "price"; -} - -export async function GET(request: NextRequest) { - const searchParams = request.nextUrl.searchParams; +export async function GET(request: Request) { + const searchParams = new URL(request.url).searchParams; const category = searchParams.get("category"); const query = searchParams.get("query")?.trim() || null; const searchScope = @@ -52,7 +37,6 @@ export async function GET(request: NextRequest) { const maxLat = parseFiniteNumber(searchParams.get("maxLat")); const minLng = parseFiniteNumber(searchParams.get("minLng")); const maxLng = parseFiniteNumber(searchParams.get("maxLng")); - const sort = parseSort(searchParams.get("sort")); const bounds = minLat !== null && maxLat !== null && minLng !== null && maxLng !== null ? { @@ -67,11 +51,10 @@ export async function GET(request: NextRequest) { category, maxPrice, query, - sort, bounds: searchScope === "viewport" ? bounds : null, }); - return NextResponse.json( + return Response.json( { items: result.items, count: result.items.length, @@ -81,7 +64,6 @@ export async function GET(request: NextRequest) { maxPrice, query, searchScope, - sort, bounds: searchScope === "viewport" ? bounds : null, }, source: result.source, diff --git a/src/app/api/places/route.ts b/src/app/api/places/route.ts index 7625de4..8cafdcc 100644 --- a/src/app/api/places/route.ts +++ b/src/app/api/places/route.ts @@ -1,5 +1,3 @@ -import { NextResponse } from "next/server"; - import { createPlaceSubmission } from "@/features/places/repository"; import { placeSubmissionSchema } from "@/features/submission/schema"; import { @@ -19,7 +17,7 @@ export async function POST(request: Request) { }); if (!rateLimit.ok) { - const response = NextResponse.json( + const response = Response.json( { ok: false, message: "장소 등록 요청이 너무 빠릅니다. 잠시 후 다시 시도해주세요.", @@ -37,7 +35,7 @@ export async function POST(request: Request) { const parsed = placeSubmissionSchema.safeParse(body); if (!parsed.success) { - const response = NextResponse.json( + const response = Response.json( { ok: false, message: "입력값 검증에 실패했습니다.", @@ -52,7 +50,7 @@ export async function POST(request: Request) { } const result = await createPlaceSubmission(parsed.data, actor.user?.id ?? null); - const response = NextResponse.json(result); + const response = Response.json(result); setPublicWriteActorCookie(response, actor, request); diff --git a/src/app/api/reports/route.ts b/src/app/api/reports/route.ts index b154569..1d72ee7 100644 --- a/src/app/api/reports/route.ts +++ b/src/app/api/reports/route.ts @@ -1,5 +1,3 @@ -import { NextResponse } from "next/server"; - import { createReportSubmission } from "@/features/reports/repository"; import { reportSubmissionSchema } from "@/features/reports/schema"; import { @@ -19,7 +17,7 @@ export async function POST(request: Request) { }); if (!rateLimit.ok) { - const response = NextResponse.json( + const response = Response.json( { ok: false, message: "신고 요청이 너무 빠릅니다. 잠시 후 다시 시도해주세요.", @@ -37,7 +35,7 @@ export async function POST(request: Request) { const parsed = reportSubmissionSchema.safeParse(body); if (!parsed.success) { - const response = NextResponse.json( + const response = Response.json( { ok: false, message: "신고 입력값 검증에 실패했습니다.", @@ -51,7 +49,7 @@ export async function POST(request: Request) { return response; } - const response = NextResponse.json( + const response = Response.json( await createReportSubmission(parsed.data, actor.user?.id ?? null), ); setPublicWriteActorCookie(response, actor, request); diff --git a/src/app/map/page.tsx b/src/app/map/page.tsx index 258583c..a8307ac 100644 --- a/src/app/map/page.tsx +++ b/src/app/map/page.tsx @@ -1,74 +1,25 @@ -import type { Metadata } from "next"; -import Link from "next/link"; +import { permanentRedirect } from "next/navigation"; -import { BrandMark } from "@/components/brand-mark"; -import { listBookmarks } from "@/features/bookmarks/repository"; -import { SessionActionGroup } from "@/features/auth/session-action-group"; -import { - categoryGroups, - getCategoryBySlug, -} from "@/features/categories/catalog"; -import { MapExplorer } from "@/features/places/map-explorer"; -import { listPlaces } from "@/features/places/repository"; -import type { PlaceSearchScope } from "@/features/places/types"; -import { createLoginHref, getSessionUser } from "@/lib/session"; - -type MapPageProps = { +type LegacyMapPageProps = { searchParams: Promise>; }; -export const dynamic = "force-dynamic"; - -export const metadata: Metadata = { - description: - "주변 식당, 문구점, 프린트, 생활 서비스 가격을 지도에서 비교하고 알뜰 장소를 찾아보세요.", - alternates: { - canonical: "/", - }, -}; - -const priceOptions = [ - { label: "전체 가격", value: null }, - { label: "5,000원 이하", value: 5000 }, - { label: "10,000원 이하", value: 10000 }, - { label: "20,000원 이하", value: 20000 }, -]; - -const mobileFilterChipClass = - "altteulmap-chip whitespace-nowrap border px-3 py-2 text-xs transition"; -const desktopFilterChipClass = - "altteulmap-chip whitespace-nowrap border px-4 py-2 text-sm transition"; -const inactiveFilterChipClass = - "border-stone-300 bg-white text-stone-700 hover:bg-stone-100"; -const mobileScopeChipClass = - "altteulmap-chip altteulmap-scope-chip inline-flex whitespace-nowrap px-3 py-2 text-xs transition"; -const desktopScopeChipClass = - "altteulmap-chip altteulmap-scope-chip inline-flex whitespace-nowrap px-4 py-2 text-sm transition"; - -function getFirstValue(value: string | string[] | undefined) { - return Array.isArray(value) ? value[0] : value; -} - -function createHref(params: { - category?: string | null; - maxPrice?: number | null; - query?: string | null; - searchScope?: PlaceSearchScope; -}) { +function toRootHref(params: Record) { const search = new URLSearchParams(); - const trimmedQuery = params.query?.trim(); - if (trimmedQuery) { - search.set("q", trimmedQuery); - search.set("scope", params.searchScope === "global" ? "global" : "viewport"); - } - - if (params.category) { - search.set("category", params.category); - } - - if (params.maxPrice) { - search.set("maxPrice", String(params.maxPrice)); + for (const [key, value] of Object.entries(params)) { + if (Array.isArray(value)) { + for (const entry of value) { + if (entry) { + search.append(key, entry); + } + } + continue; + } + + if (value) { + search.set(key, value); + } } const query = search.toString(); @@ -76,419 +27,8 @@ function createHref(params: { return query ? `/?${query}` : "/"; } -export default async function MapPage({ searchParams }: MapPageProps) { - const params = await searchParams; - const activeCategory = getFirstValue(params.category) ?? null; - const activeMaxPrice = Number(getFirstValue(params.maxPrice) ?? "") || null; - const activeQuery = getFirstValue(params.q)?.trim() || null; - const activeSearchScope: PlaceSearchScope = - activeQuery && getFirstValue(params.scope) === "global" - ? "global" - : "viewport"; - const user = await getSessionUser(); - - const [result, bookmarkResult] = await Promise.all([ - listPlaces({ - category: activeCategory, - maxPrice: activeMaxPrice, - query: activeQuery, - }), - listBookmarks(user), - ]); - const currentMapHref = createHref({ - category: activeCategory, - maxPrice: activeMaxPrice, - query: activeQuery, - searchScope: activeSearchScope, - }); - const loginHref = createLoginHref(currentMapHref); - const bookmarkLoginHref = createLoginHref(currentMapHref); - const submitHref = "/submit"; - const places = result.items; - const bookmarkedIds = new Set( - bookmarkResult.items.map((bookmark) => bookmark.placeId), - ); - const selectedCategory = getCategoryBySlug(activeCategory); - const activePriceLabel = - priceOptions.find((option) => option.value === activeMaxPrice)?.label ?? null; - const mobileSummaryItems = [ - activeQuery ? `검색 ${activeQuery}` : null, - selectedCategory?.name ?? null, - activePriceLabel, - activeSearchScope === "global" ? "전체 검색" : "현재 지도", - ].filter((item): item is string => Boolean(item)); - - return ( -
-
-
-
- -
- - 장소 등록하기 - - - 북마크 - - -
-
- -
-
-
-

검색

- {activeQuery ? ( - - 검색 지우기 - - ) : null} -
- -
- - -
- -
- {mobileSummaryItems.map((item) => ( - - {item} - - ))} -
- -
- - 탐색 조건 - - 열기 - - - 접기 - - - -
-
-

- 검색 범위 -

-
- - -
-
- -
-

- 가격 필터 -

-
- {priceOptions.map((option) => { - const isActive = activeMaxPrice === option.value; - - return ( - - {option.label} - - ); - })} -
-
- -
-

- 카테고리 -

-
- - 전체 - - {categoryGroups.flatMap((group) => - group.children.map((category) => { - const isActive = activeCategory === category.slug; - - return ( - - {category.name} - - ); - }), - )} -
-
-
-
- - {activeCategory ? ( - - ) : null} - {activeMaxPrice ? ( - - ) : null} -
-
- -
-
-

카테고리

-
- - 전체 - - {categoryGroups.flatMap((group) => - group.children.map((category) => { - const isActive = activeCategory === category.slug; - - return ( - - {category.name} - - ); - }), - )} -
-
- -
-
-

가격 필터

-
- {priceOptions.map((option) => { - const isActive = activeMaxPrice === option.value; - - return ( - - {option.label} - - ); - })} -
-
-
- -
-
-

검색

- {activeQuery ? ( - - 검색 지우기 - - ) : null} -
- -
-
- - -
- -
- - -
- - {activeCategory ? ( - - ) : null} - {activeMaxPrice ? ( - - ) : null} -
-
-
- - -
-
-
- ); +export default async function LegacyMapPage({ + searchParams, +}: LegacyMapPageProps) { + permanentRedirect(toRootHref(await searchParams)); } diff --git a/src/app/page.tsx b/src/app/page.tsx index 8d7c16b..45dae16 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,21 +1,5 @@ -import type { Metadata } from "next"; - -import MapPage from "@/app/map/page"; - -type HomePageProps = { - searchParams: Promise>; -}; - -export const dynamic = "force-dynamic"; - -export const metadata: Metadata = { - description: - "주변 식당, 문구점, 프린트, 생활 서비스 가격을 지도에서 비교하고 알뜰 장소를 찾아보세요.", - alternates: { - canonical: "/", - }, -}; - -export default function Home(props: HomePageProps) { - return ; -} +export { + default, + dynamic, + metadata, +} from "@/features/map/map-page"; diff --git a/src/features/admin/repository.ts b/src/features/admin/repository.ts new file mode 100644 index 0000000..1bcd8ea --- /dev/null +++ b/src/features/admin/repository.ts @@ -0,0 +1,204 @@ +import "server-only"; + +import { and, desc, eq, gt, or, sql } from "drizzle-orm"; + +import { getDb, isDatabaseEnabled } from "@/db/client"; +import { + authSessions, + contentReports, + places, + priceReports, + users, +} from "@/db/schema"; +import { + authAccountHints, + type AppUserRole, +} from "@/features/auth/constants"; +import { mockPlaces } from "@/features/places/mock-data"; +import { mockReports } from "@/features/reports/mock-data"; + +type DataSource = "mock" | "database"; + +export type AdminOverviewUserRecord = { + id: string; + email: string; + nickname: string | null; + role: AppUserRole; + joinedAt: string; + hasActiveSession: boolean; +}; + +export type AdminOverviewResult = { + source: DataSource; + visitMetricsAvailable: boolean; + stats: { + totalUsers: number; + adminUsers: number; + regularUsers: number; + currentSessions: number; + activeUsers: number; + activePlaces: number; + pendingPlaces: number; + pendingPriceReports: number; + openReports: number; + }; + recentUsers: AdminOverviewUserRecord[]; +}; + +const dateFormatter = new Intl.DateTimeFormat("sv-SE", { + timeZone: "Asia/Seoul", +}); + +function formatDate(value: Date) { + return dateFormatter.format(value); +} + +function getMockAdminOverview(): AdminOverviewResult { + return { + source: "mock", + visitMetricsAvailable: false, + stats: { + totalUsers: 2, + adminUsers: 1, + regularUsers: 1, + currentSessions: 0, + activeUsers: 0, + activePlaces: mockPlaces.length, + pendingPlaces: 0, + pendingPriceReports: 0, + openReports: mockReports.filter( + (report) => report.status === "open" || report.status === "reviewing", + ).length, + }, + recentUsers: authAccountHints.map((account) => ({ + id: account.email, + email: account.email, + nickname: account.role === "admin" ? "운영자" : "근처 주민", + role: account.role, + joinedAt: "기본 계정", + hasActiveSession: false, + })), + }; +} + +async function getDatabaseAdminOverview(): Promise { + const db = getDb(); + const now = new Date(); + + const [ + [userCountsRow], + [sessionCountsRow], + [placeCountsRow], + [pendingPriceReportCountsRow], + [openReportCountsRow], + recentUsersRows, + activeSessionUserRows, + ] = await Promise.all([ + db + .select({ + totalUsers: sql`count(*)::int`, + adminUsers: + sql`count(*) filter (where ${users.role} = 'admin')::int`, + regularUsers: + sql`count(*) filter (where ${users.role} = 'user')::int`, + }) + .from(users), + db + .select({ + currentSessions: sql`count(*)::int`, + activeUsers: sql`count(distinct ${authSessions.userId})::int`, + }) + .from(authSessions) + .where(gt(authSessions.expires, now)), + db + .select({ + activePlaces: + sql`count(*) filter (where ${places.status} = 'active')::int`, + pendingPlaces: + sql`count(*) filter (where ${places.status} = 'pending_review')::int`, + }) + .from(places), + db + .select({ + pendingPriceReports: sql`count(*)::int`, + }) + .from(priceReports) + .where(eq(priceReports.reportStatus, "pending_review")), + db + .select({ + openReports: sql`count(*)::int`, + }) + .from(contentReports) + .where( + and( + eq(contentReports.targetType, "place"), + or( + eq(contentReports.status, "open"), + eq(contentReports.status, "reviewing"), + ), + ), + ), + db + .select({ + id: users.id, + email: users.email, + nickname: users.nickname, + role: users.role, + createdAt: users.createdAt, + }) + .from(users) + .orderBy(desc(users.createdAt)) + .limit(6), + db + .select({ + userId: authSessions.userId, + }) + .from(authSessions) + .where(gt(authSessions.expires, now)) + .groupBy(authSessions.userId), + ]); + + const activeSessionUserIds = new Set( + activeSessionUserRows.map((row) => row.userId), + ); + + return { + source: "database", + visitMetricsAvailable: false, + stats: { + totalUsers: Number(userCountsRow?.totalUsers ?? 0), + adminUsers: Number(userCountsRow?.adminUsers ?? 0), + regularUsers: Number(userCountsRow?.regularUsers ?? 0), + currentSessions: Number(sessionCountsRow?.currentSessions ?? 0), + activeUsers: Number(sessionCountsRow?.activeUsers ?? 0), + activePlaces: Number(placeCountsRow?.activePlaces ?? 0), + pendingPlaces: Number(placeCountsRow?.pendingPlaces ?? 0), + pendingPriceReports: Number( + pendingPriceReportCountsRow?.pendingPriceReports ?? 0, + ), + openReports: Number(openReportCountsRow?.openReports ?? 0), + }, + recentUsers: recentUsersRows.map((user) => ({ + id: user.id, + email: user.email, + nickname: user.nickname ?? null, + role: user.role, + joinedAt: formatDate(user.createdAt), + hasActiveSession: activeSessionUserIds.has(user.id), + })), + }; +} + +export async function getAdminOverview() { + if (!isDatabaseEnabled()) { + return getMockAdminOverview(); + } + + try { + return await getDatabaseAdminOverview(); + } catch (error) { + console.error("Failed to load admin overview. Falling back to mock data.", error); + + return getMockAdminOverview(); + } +} diff --git a/src/features/map/map-page.tsx b/src/features/map/map-page.tsx new file mode 100644 index 0000000..258583c --- /dev/null +++ b/src/features/map/map-page.tsx @@ -0,0 +1,494 @@ +import type { Metadata } from "next"; +import Link from "next/link"; + +import { BrandMark } from "@/components/brand-mark"; +import { listBookmarks } from "@/features/bookmarks/repository"; +import { SessionActionGroup } from "@/features/auth/session-action-group"; +import { + categoryGroups, + getCategoryBySlug, +} from "@/features/categories/catalog"; +import { MapExplorer } from "@/features/places/map-explorer"; +import { listPlaces } from "@/features/places/repository"; +import type { PlaceSearchScope } from "@/features/places/types"; +import { createLoginHref, getSessionUser } from "@/lib/session"; + +type MapPageProps = { + searchParams: Promise>; +}; + +export const dynamic = "force-dynamic"; + +export const metadata: Metadata = { + description: + "주변 식당, 문구점, 프린트, 생활 서비스 가격을 지도에서 비교하고 알뜰 장소를 찾아보세요.", + alternates: { + canonical: "/", + }, +}; + +const priceOptions = [ + { label: "전체 가격", value: null }, + { label: "5,000원 이하", value: 5000 }, + { label: "10,000원 이하", value: 10000 }, + { label: "20,000원 이하", value: 20000 }, +]; + +const mobileFilterChipClass = + "altteulmap-chip whitespace-nowrap border px-3 py-2 text-xs transition"; +const desktopFilterChipClass = + "altteulmap-chip whitespace-nowrap border px-4 py-2 text-sm transition"; +const inactiveFilterChipClass = + "border-stone-300 bg-white text-stone-700 hover:bg-stone-100"; +const mobileScopeChipClass = + "altteulmap-chip altteulmap-scope-chip inline-flex whitespace-nowrap px-3 py-2 text-xs transition"; +const desktopScopeChipClass = + "altteulmap-chip altteulmap-scope-chip inline-flex whitespace-nowrap px-4 py-2 text-sm transition"; + +function getFirstValue(value: string | string[] | undefined) { + return Array.isArray(value) ? value[0] : value; +} + +function createHref(params: { + category?: string | null; + maxPrice?: number | null; + query?: string | null; + searchScope?: PlaceSearchScope; +}) { + const search = new URLSearchParams(); + const trimmedQuery = params.query?.trim(); + + if (trimmedQuery) { + search.set("q", trimmedQuery); + search.set("scope", params.searchScope === "global" ? "global" : "viewport"); + } + + if (params.category) { + search.set("category", params.category); + } + + if (params.maxPrice) { + search.set("maxPrice", String(params.maxPrice)); + } + + const query = search.toString(); + + return query ? `/?${query}` : "/"; +} + +export default async function MapPage({ searchParams }: MapPageProps) { + const params = await searchParams; + const activeCategory = getFirstValue(params.category) ?? null; + const activeMaxPrice = Number(getFirstValue(params.maxPrice) ?? "") || null; + const activeQuery = getFirstValue(params.q)?.trim() || null; + const activeSearchScope: PlaceSearchScope = + activeQuery && getFirstValue(params.scope) === "global" + ? "global" + : "viewport"; + const user = await getSessionUser(); + + const [result, bookmarkResult] = await Promise.all([ + listPlaces({ + category: activeCategory, + maxPrice: activeMaxPrice, + query: activeQuery, + }), + listBookmarks(user), + ]); + const currentMapHref = createHref({ + category: activeCategory, + maxPrice: activeMaxPrice, + query: activeQuery, + searchScope: activeSearchScope, + }); + const loginHref = createLoginHref(currentMapHref); + const bookmarkLoginHref = createLoginHref(currentMapHref); + const submitHref = "/submit"; + const places = result.items; + const bookmarkedIds = new Set( + bookmarkResult.items.map((bookmark) => bookmark.placeId), + ); + const selectedCategory = getCategoryBySlug(activeCategory); + const activePriceLabel = + priceOptions.find((option) => option.value === activeMaxPrice)?.label ?? null; + const mobileSummaryItems = [ + activeQuery ? `검색 ${activeQuery}` : null, + selectedCategory?.name ?? null, + activePriceLabel, + activeSearchScope === "global" ? "전체 검색" : "현재 지도", + ].filter((item): item is string => Boolean(item)); + + return ( +
+
+
+
+ +
+ + 장소 등록하기 + + + 북마크 + + +
+
+ +
+
+
+

검색

+ {activeQuery ? ( + + 검색 지우기 + + ) : null} +
+ +
+ + +
+ +
+ {mobileSummaryItems.map((item) => ( + + {item} + + ))} +
+ +
+ + 탐색 조건 + + 열기 + + + 접기 + + + +
+
+

+ 검색 범위 +

+
+ + +
+
+ +
+

+ 가격 필터 +

+
+ {priceOptions.map((option) => { + const isActive = activeMaxPrice === option.value; + + return ( + + {option.label} + + ); + })} +
+
+ +
+

+ 카테고리 +

+
+ + 전체 + + {categoryGroups.flatMap((group) => + group.children.map((category) => { + const isActive = activeCategory === category.slug; + + return ( + + {category.name} + + ); + }), + )} +
+
+
+
+ + {activeCategory ? ( + + ) : null} + {activeMaxPrice ? ( + + ) : null} +
+
+ +
+
+

카테고리

+
+ + 전체 + + {categoryGroups.flatMap((group) => + group.children.map((category) => { + const isActive = activeCategory === category.slug; + + return ( + + {category.name} + + ); + }), + )} +
+
+ +
+
+

가격 필터

+
+ {priceOptions.map((option) => { + const isActive = activeMaxPrice === option.value; + + return ( + + {option.label} + + ); + })} +
+
+
+ +
+
+

검색

+ {activeQuery ? ( + + 검색 지우기 + + ) : null} +
+ +
+
+ + +
+ +
+ + +
+ + {activeCategory ? ( + + ) : null} + {activeMaxPrice ? ( + + ) : null} +
+
+
+ + +
+
+
+ ); +} diff --git a/src/features/places/queries.ts b/src/features/places/queries.ts index 236317e..679f23a 100644 --- a/src/features/places/queries.ts +++ b/src/features/places/queries.ts @@ -27,18 +27,6 @@ export function sortPlaceRecords(items: PlaceRecord[], sort: PlaceSort) { ); } - if (sort === "likes") { - if (right.likeCount !== left.likeCount) { - return right.likeCount - left.likeCount; - } - - if (left.dislikeCount !== right.dislikeCount) { - return left.dislikeCount - right.dislikeCount; - } - - return left.representativePriceAmount - right.representativePriceAmount; - } - return left.representativePriceAmount - right.representativePriceAmount; }); } diff --git a/src/features/places/repository.ts b/src/features/places/repository.ts index ce60c24..fcc63be 100644 --- a/src/features/places/repository.ts +++ b/src/features/places/repository.ts @@ -108,8 +108,6 @@ export type PlaceSubmissionResult = { categorySlug: string; roadAddress: string; district: string; - latitude?: number; - longitude?: number; priceItems: Array<{ label: string; amount: number; @@ -511,8 +509,6 @@ function toFallbackPlacePreview( categorySlug: input.categorySlug, roadAddress: input.roadAddress, district: input.district, - latitude: input.latitude, - longitude: input.longitude, priceItems: input.priceItems.map((item) => ({ label: item.label, amount: item.amount, @@ -716,9 +712,7 @@ async function listDatabasePlaces({ .orderBy( ...(sort === "recent" ? [desc(places.lastPriceUpdatedAt), desc(places.updatedAt)] - : sort === "likes" - ? [desc(places.updatedAt)] - : [asc(places.representativePriceAmount), desc(places.updatedAt)]), + : [asc(places.representativePriceAmount), desc(places.updatedAt)]), ); const categoryMap = await loadCategoryMap(rows.map((row) => row.internalId)); @@ -1163,8 +1157,8 @@ async function createDatabasePlaceSubmission( note: input.note || null, roadAddress: input.roadAddress, district: input.district, - latitude: input.latitude ?? null, - longitude: input.longitude ?? null, + latitude: null, + longitude: null, status: "pending_review", representativePriceAmount: input.priceItems[representativeIndex].amount, representativePriceLabel: input.priceItems[representativeIndex].label, @@ -1238,8 +1232,6 @@ async function createDatabasePlaceSubmission( categorySlug: input.categorySlug, roadAddress: input.roadAddress, district: input.district, - latitude: input.latitude, - longitude: input.longitude, priceItems: input.priceItems.map((item) => ({ label: item.label, amount: item.amount, diff --git a/src/features/places/types.ts b/src/features/places/types.ts index b2ae4dd..bab36e7 100644 --- a/src/features/places/types.ts +++ b/src/features/places/types.ts @@ -35,7 +35,7 @@ export type PlaceBounds = { export type PlaceQueryBounds = PlaceBounds; export type PlaceSearchScope = "viewport" | "global"; -export type PlaceSort = "price" | "recent" | "likes"; +export type PlaceSort = "price" | "recent"; export type PlaceRecord = { id: string; diff --git a/src/features/submission/place-coordinate-picker.tsx b/src/features/submission/place-coordinate-picker.tsx deleted file mode 100644 index 57fcb5a..0000000 --- a/src/features/submission/place-coordinate-picker.tsx +++ /dev/null @@ -1,322 +0,0 @@ -"use client"; - -import { useEffect, useEffectEvent, useMemo, useRef, useState, useTransition } from "react"; - -import { - DEFAULT_MAP_CENTER, - geocodeAddress, - getLoadedNaverMapSdk, - getNaverMapKeyId, - loadNaverMapSdk, - type MapPoint, - type MapStatus, - type NaverMapInstance, - type NaverMarkerInstance, -} from "@/features/map/naver-map-sdk"; - -type PlaceCoordinatePickerProps = { - placeName: string; - address: string; - district: string; - latitude: string; - longitude: string; - onChange: (next: { latitude: string; longitude: string }) => void; - disabled?: boolean; - showLookupButton?: boolean; -}; - -function formatCoordinate(value: number | null) { - if (value === null || Number.isNaN(value)) { - return ""; - } - - return value.toFixed(6); -} - -function parseCoordinatePair( - latitude: string, - longitude: string, -): MapPoint | null { - const parsedLatitude = Number(latitude); - const parsedLongitude = Number(longitude); - - if (!Number.isFinite(parsedLatitude) || !Number.isFinite(parsedLongitude)) { - return null; - } - - return { - lat: parsedLatitude, - lng: parsedLongitude, - }; -} - -export function PlaceCoordinatePicker({ - placeName, - address, - district, - latitude, - longitude, - onChange, - disabled = false, - showLookupButton = true, -}: PlaceCoordinatePickerProps) { - const mapContainerRef = useRef(null); - const mapInstanceRef = useRef(null); - const markerRef = useRef(null); - const naverMapKeyId = getNaverMapKeyId(); - const [status, setStatus] = useState( - naverMapKeyId ? "loading" : "missing-key", - ); - const [lookupMessage, setLookupMessage] = useState(null); - const [isLookingUp, startLookupTransition] = useTransition(); - const selectedPoint = useMemo( - () => parseCoordinatePair(latitude, longitude), - [latitude, longitude], - ); - const emitChange = useEffectEvent(onChange); - - useEffect(() => { - if (!naverMapKeyId || !mapContainerRef.current) { - return; - } - - let cancelled = false; - - loadNaverMapSdk(naverMapKeyId) - .then((naver) => { - if (cancelled || !mapContainerRef.current) { - return; - } - - const center = selectedPoint ?? DEFAULT_MAP_CENTER; - const centerLatLng = new naver.maps.LatLng(center.lat, center.lng); - - if (!mapInstanceRef.current) { - mapInstanceRef.current = new naver.maps.Map(mapContainerRef.current, { - center: centerLatLng, - zoom: selectedPoint ? 16 : 13, - mapDataControl: false, - scaleControl: false, - logoControl: false, - }); - - naver.maps.Event.addListener( - mapInstanceRef.current, - "click", - (event?: { coord?: { y?: number; x?: number } }) => { - if (disabled) { - return; - } - - const nextLatitude = event?.coord?.y; - const nextLongitude = event?.coord?.x; - - if ( - typeof nextLatitude !== "number" || - typeof nextLongitude !== "number" - ) { - return; - } - - emitChange({ - latitude: formatCoordinate(nextLatitude), - longitude: formatCoordinate(nextLongitude), - }); - }, - ); - } else { - mapInstanceRef.current.setCenter?.(centerLatLng); - mapInstanceRef.current.setZoom?.(selectedPoint ? 16 : 13); - } - - setStatus("ready"); - }) - .catch((error) => { - console.error("Failed to initialize NAVER Maps for submission picker.", error); - - if (!cancelled) { - setStatus("error"); - } - }); - - return () => { - cancelled = true; - }; - }, [disabled, naverMapKeyId, selectedPoint]); - - useEffect(() => { - if (status !== "ready" || !mapInstanceRef.current) { - return; - } - - const naver = getLoadedNaverMapSdk(); - const LatLng = naver?.maps.LatLng; - - if (!naver || !LatLng) { - return; - } - - if (!selectedPoint) { - markerRef.current?.setMap?.(null); - markerRef.current = null; - mapInstanceRef.current.setCenter?.( - new LatLng(DEFAULT_MAP_CENTER.lat, DEFAULT_MAP_CENTER.lng), - ); - mapInstanceRef.current.setZoom?.(13); - return; - } - - markerRef.current?.setMap?.(null); - markerRef.current = new naver.maps.Marker({ - map: mapInstanceRef.current, - position: new LatLng(selectedPoint.lat, selectedPoint.lng), - title: placeName || "등록 위치", - }); - - mapInstanceRef.current.panTo?.(new LatLng(selectedPoint.lat, selectedPoint.lng)); - mapInstanceRef.current.setZoom?.(16); - }, [placeName, selectedPoint, status]); - - useEffect(() => { - if (status !== "ready" || !mapInstanceRef.current) { - return; - } - - const handleResize = () => { - mapInstanceRef.current?.autoResize?.(); - }; - - window.addEventListener("resize", handleResize); - - return () => { - window.removeEventListener("resize", handleResize); - }; - }, [status]); - - useEffect(() => { - return () => { - markerRef.current?.setMap?.(null); - markerRef.current = null; - mapInstanceRef.current?.destroy?.(); - mapInstanceRef.current = null; - }; - }, []); - - const statusMessage = - status === "missing-key" - ? "지도 연결이 아직 준비되지 않았습니다. 잠시 후 다시 시도해주세요." - : status === "error" - ? "지도를 불러오지 못했습니다. 잠시 후 다시 시도해주세요." - : status === "loading" - ? "지도를 불러오는 중입니다." - : null; - - return ( -
-
-

- Location -

-

- 지도 위치 확인 -

- {placeName || district || address ? ( -

- {[placeName || null, district || null, address || null] - .filter(Boolean) - .join(" · ")} -

- ) : null} -
- -
-
-

- {status === "ready" ? "네이버 지도" : "프리뷰 안내"} -

-
- {showLookupButton ? ( - - ) : null} - {selectedPoint ? ( - - ) : null} -
-
-
-
-
-

- 지도에 노출하려면 위치 확인이 필요합니다. - {statusMessage ? ` ${statusMessage}` : ""} -

- {lookupMessage ? ( -

{lookupMessage}

- ) : null} -
-
- ); -} diff --git a/src/features/submission/place-submit-form.tsx b/src/features/submission/place-submit-form.tsx index a6c468d..71b12f8 100644 --- a/src/features/submission/place-submit-form.tsx +++ b/src/features/submission/place-submit-form.tsx @@ -1,22 +1,14 @@ "use client"; import { zodResolver } from "@hookform/resolvers/zod"; -import { useFieldArray, useForm, useWatch } from "react-hook-form"; -import { useEffect, useRef, useState, useTransition } from "react"; +import { useFieldArray, useForm } from "react-hook-form"; +import { useState, useTransition } from "react"; import { categoryGroups } from "@/features/categories/catalog"; -import { - geocodeAddress, - getNaverMapKeyId, - loadNaverMapSdk, -} from "@/features/map/naver-map-sdk"; import { type PlaceSubmissionFormInput, type PlaceSubmissionFormValues, - type PlaceSubmissionInput, - placeSubmissionCoordinateRequirementMessage, placeSubmissionFormSchema, - placeSubmissionSchema, } from "@/features/submission/schema"; type SubmitResult = { @@ -29,8 +21,6 @@ type SubmitResult = { categorySlug: string; roadAddress: string; district: string; - latitude?: number; - longitude?: number; priceItems: Array<{ label: string; amount: number; @@ -45,8 +35,6 @@ const defaultValues: PlaceSubmissionFormInput = { categorySlug: "", roadAddress: "", district: "", - latitude: "", - longitude: "", note: "", priceItems: [ { @@ -60,12 +48,6 @@ const defaultValues: PlaceSubmissionFormInput = { export function PlaceSubmitForm() { const [submitResult, setSubmitResult] = useState(null); const [isPending, startTransition] = useTransition(); - const [isResolvingLocation, startLocationResolveTransition] = useTransition(); - const [locationMessage, setLocationMessage] = useState(null); - const previousLocationInputRef = useRef<{ - address: string; - district: string; - } | null>(null); const form = useForm< PlaceSubmissionFormInput, @@ -80,11 +62,6 @@ export function PlaceSubmitForm() { register, control, handleSubmit, - clearErrors, - getValues, - setError, - setValue, - trigger, formState: { errors }, } = form; @@ -92,258 +69,17 @@ export function PlaceSubmitForm() { control, name: "priceItems", }); - const watchedAddress = useWatch({ - control, - name: "roadAddress", - defaultValue: "", - }); - const watchedDistrict = useWatch({ - control, - name: "district", - defaultValue: "", - }); - const watchedLatitude = useWatch({ - control, - name: "latitude", - defaultValue: "", - }); - const watchedLongitude = useWatch({ - control, - name: "longitude", - defaultValue: "", - }); - const latitudeValue = - typeof watchedLatitude === "string" - ? watchedLatitude - : watchedLatitude == null - ? "" - : String(watchedLatitude); - const longitudeValue = - typeof watchedLongitude === "string" - ? watchedLongitude - : watchedLongitude == null - ? "" - : String(watchedLongitude); - const isLocationConfirmed = Boolean(latitudeValue && longitudeValue); - - const setCoordinateFields = (latitude: string, longitude: string) => { - setValue("latitude", latitude, { - shouldDirty: true, - shouldValidate: true, - }); - setValue("longitude", longitude, { - shouldDirty: true, - shouldValidate: true, - }); - }; - - useEffect(() => { - const previous = previousLocationInputRef.current; - previousLocationInputRef.current = { - address: watchedAddress, - district: watchedDistrict, - }; - - if (!previous) { - return; - } - - const addressChanged = - previous.address !== watchedAddress || previous.district !== watchedDistrict; - - if (!addressChanged || !latitudeValue || !longitudeValue) { - return; - } - - setValue("latitude", "", { - shouldDirty: true, - shouldValidate: true, - }); - setValue("longitude", "", { - shouldDirty: true, - shouldValidate: true, - }); - setError("latitude", { - type: "manual", - message: "주소가 바뀌어 위치를 다시 확인해주세요.", - }); - setError("longitude", { - type: "manual", - message: "주소가 바뀌어 위치를 다시 확인해주세요.", - }); - }, [ - latitudeValue, - longitudeValue, - setError, - setValue, - watchedAddress, - watchedDistrict, - ]); - - const resolveCoordinatesFromAddress = async ( - values: PlaceSubmissionFormValues, - ): Promise => { - if ( - typeof values.latitude === "number" && - typeof values.longitude === "number" - ) { - clearErrors(["latitude", "longitude"]); - return placeSubmissionSchema.parse(getValues()); - } - - if ( - typeof values.latitude === "number" || - typeof values.longitude === "number" - ) { - setError("latitude", { - type: "manual", - message: "위도와 경도는 함께 입력해주세요.", - }); - setError("longitude", { - type: "manual", - message: "위도와 경도는 함께 입력해주세요.", - }); - return null; - } - - if (!values.roadAddress.trim()) { - setError("latitude", { - type: "manual", - message: placeSubmissionCoordinateRequirementMessage, - }); - setError("longitude", { - type: "manual", - message: placeSubmissionCoordinateRequirementMessage, - }); - return null; - } - - const naverMapKeyId = getNaverMapKeyId(); - - if (!naverMapKeyId) { - setError("latitude", { - type: "manual", - message: - "주소 위치를 자동으로 확인할 수 없습니다. 잠시 후 다시 시도해주세요.", - }); - setError("longitude", { - type: "manual", - message: - "주소 위치를 자동으로 확인할 수 없습니다. 잠시 후 다시 시도해주세요.", - }); - return null; - } - - try { - const query = [values.district.trim(), values.roadAddress.trim()] - .filter(Boolean) - .join(" "); - const result = await loadNaverMapSdk(naverMapKeyId).then(() => - geocodeAddress(query), - ); - - if (!result) { - setLocationMessage(null); - setError("latitude", { - type: "manual", - message: - "입력한 주소로 위치를 찾지 못했습니다. 주소를 다시 확인해주세요.", - }); - setError("longitude", { - type: "manual", - message: - "입력한 주소로 위치를 찾지 못했습니다. 주소를 다시 확인해주세요.", - }); - return null; - } - - const nextLatitude = result.point.lat.toFixed(6); - const nextLongitude = result.point.lng.toFixed(6); - - setCoordinateFields(nextLatitude, nextLongitude); - clearErrors(["latitude", "longitude"]); - setLocationMessage("주소 확인이 완료되었습니다."); - - const parsed = placeSubmissionSchema.safeParse({ - ...getValues(), - latitude: nextLatitude, - longitude: nextLongitude, - }); - - if (!parsed.success) { - setLocationMessage(null); - setError("latitude", { - type: "manual", - message: placeSubmissionCoordinateRequirementMessage, - }); - setError("longitude", { - type: "manual", - message: placeSubmissionCoordinateRequirementMessage, - }); - return null; - } - - return parsed.data; - } catch { - setLocationMessage(null); - setError("latitude", { - type: "manual", - message: - "주소 기준 위치 확인에 실패했습니다. 잠시 후 다시 시도해주세요.", - }); - setError("longitude", { - type: "manual", - message: - "주소 기준 위치 확인에 실패했습니다. 잠시 후 다시 시도해주세요.", - }); - return null; - } - }; - - const handleAddressLookup = () => { - startLocationResolveTransition(async () => { - const values = getValues(); - const query = [values.district?.trim(), values.roadAddress?.trim()] - .filter(Boolean) - .join(" "); - - if (!query) { - setLocationMessage(null); - setError("roadAddress", { - type: "manual", - message: "업장 주소를 먼저 입력해주세요.", - }); - setLocationMessage(null); - return; - } - - clearErrors(["roadAddress", "latitude", "longitude"]); - await resolveCoordinatesFromAddress(values as PlaceSubmissionFormValues); - }); - }; const onSubmit = handleSubmit((values) => { startTransition(async () => { setSubmitResult(null); - const payload = await resolveCoordinatesFromAddress(values); - - if (!payload) { - return; - } - - const isValid = await trigger(); - - if (!isValid) { - return; - } - const response = await fetch("/api/places", { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify(payload), + body: JSON.stringify(values), }); const result = (await response.json()) as SubmitResult; @@ -376,9 +112,6 @@ export function PlaceSubmitForm() { className="rounded-2xl border border-stone-300 bg-white px-4 py-3 outline-none transition focus:border-stone-900" placeholder="예: 학교앞김밥, 성북청년밥집" /> -

- 간판명이나 사용자가 바로 알아볼 수 있는 이름 하나만 입력하면 됩니다. -

{errors.name ? ( {errors.name.message} @@ -427,52 +160,20 @@ export function PlaceSubmitForm() {

-
-
-

업장 주소

-

- 도로명 주소를 입력하고 위치 확인까지 끝내야 지도에 표시됩니다. -

-
- - {isLocationConfirmed ? "위치 확인됨" : "위치 미확인"} - -
-
+
+ {errors.roadAddress ? (

{errors.roadAddress.message}

) : null} - {isLocationConfirmed && locationMessage ? ( -

- {locationMessage} -

- ) : null}
@@ -578,39 +279,13 @@ export function PlaceSubmitForm() {
-
-
- 주소 텍스트를 기준으로 내부 위치 확인을 진행합니다. 정확한 도로명 주소를 입력해주세요. -
- - - {errors.latitude || errors.longitude ? ( -
- {errors.latitude?.message ?? errors.longitude?.message} -
- ) : null} -
- {isLocationConfirmed - ? "업장 위치 확인이 완료되었습니다. 제출 시 이 주소 기준으로 저장됩니다." - : "아직 업장 위치가 확인되지 않았습니다. 주소로 위치 확인이 필요합니다."} -
-
-
@@ -644,16 +319,6 @@ export function PlaceSubmitForm() { {submitResult.preview.roadAddress} ·{" "} {submitResult.preview.district}

- {typeof submitResult.preview.latitude === "number" && - typeof submitResult.preview.longitude === "number" ? ( - <> -

제출 좌표

-

- {submitResult.preview.latitude.toFixed(6)},{" "} - {submitResult.preview.longitude.toFixed(6)} -

- - ) : null}
{submitResult.preview.priceItems.map((item) => (
- issue.input === undefined - ? placeSubmissionCoordinateRequirementMessage - : "위도 범위가 올바르지 않습니다.", - }) - .min(-90, "위도 범위가 올바르지 않습니다.") - .max(90, "위도 범위가 올바르지 않습니다."), -); - -const requiredLongitudeSchema = z.preprocess( - normalizeCoordinateInput, - z - .number({ - error: (issue) => - issue.input === undefined - ? placeSubmissionCoordinateRequirementMessage - : "경도 범위가 올바르지 않습니다.", - }) - .min(-180, "경도 범위가 올바르지 않습니다.") - .max(180, "경도 범위가 올바르지 않습니다."), -); - const optionalLatitudeSchema = z.preprocess( normalizeCoordinateInput, z @@ -107,8 +78,6 @@ const placeSubmissionBaseSchema = z.object({ .trim() .min(1, "지역 구분을 입력해주세요.") .max(80, "지역 구분은 80자 이하로 입력해주세요."), - latitude: optionalLatitudeSchema, - longitude: optionalLongitudeSchema, note: z .string() .trim() @@ -142,56 +111,17 @@ function refinePlaceSubmissionLabels( }); } -function refineOptionalCoordinatePair( - value: z.infer, - context: z.RefinementCtx, -) { - const hasLatitude = typeof value.latitude === "number"; - const hasLongitude = typeof value.longitude === "number"; - - if (hasLatitude !== hasLongitude) { - context.addIssue({ - code: z.ZodIssueCode.custom, - message: "위도와 경도는 함께 입력해주세요.", - path: [hasLatitude ? "longitude" : "latitude"], - }); - } -} - export const placeSubmissionFormSchema = placeSubmissionBaseSchema.superRefine( - (value, context) => { - refinePlaceSubmissionLabels(value, context); - refineOptionalCoordinatePair(value, context); - }, + refinePlaceSubmissionLabels, ); -export const placeSubmissionSchema = placeSubmissionBaseSchema - .extend({ - latitude: requiredLatitudeSchema, - longitude: requiredLongitudeSchema, - }) - .superRefine((value, context) => { - refinePlaceSubmissionLabels(value, context); - - if ( - typeof value.latitude !== "number" || - typeof value.longitude !== "number" - ) { - context.addIssue({ - code: z.ZodIssueCode.custom, - message: "주소 위치를 확인해 좌표를 지정해주세요.", - path: [ - typeof value.latitude !== "number" ? "latitude" : "longitude", - ], - }); - } - }); +export const placeSubmissionSchema = placeSubmissionFormSchema; export const placeModerationSchema = z .object({ decision: z.enum(["approve", "reject"]), - latitude: z.coerce.number().min(-90).max(90).optional(), - longitude: z.coerce.number().min(-180).max(180).optional(), + latitude: optionalLatitudeSchema, + longitude: optionalLongitudeSchema, }) .superRefine((value, context) => { if (value.decision === "approve") { diff --git a/src/lib/public-write-actor.ts b/src/lib/public-write-actor.ts index 8d34dc9..c05b38b 100644 --- a/src/lib/public-write-actor.ts +++ b/src/lib/public-write-actor.ts @@ -1,7 +1,5 @@ import "server-only"; -import type { NextResponse } from "next/server"; - import { getSessionUser, type SessionUser } from "@/lib/session"; import { createVisitorId, @@ -41,7 +39,7 @@ export async function getPublicWriteActor( } export function setPublicWriteActorCookie( - response: NextResponse, + response: Response, actor: PublicWriteActor, request: Request, ) { diff --git a/src/lib/visitor-id.ts b/src/lib/visitor-id.ts index b59266b..b16684b 100644 --- a/src/lib/visitor-id.ts +++ b/src/lib/visitor-id.ts @@ -1,7 +1,6 @@ import "server-only"; import { cookies } from "next/headers"; -import { NextResponse } from "next/server"; const VISITOR_ID_COOKIE_NAME = "altteulmap_visitor_id"; const VISITOR_ID_COOKIE_MAX_AGE = 60 * 60 * 24 * 365; @@ -20,6 +19,42 @@ export async function getVisitorIdFromCookie() { return normalizeVisitorId(cookieStore.get(VISITOR_ID_COOKIE_NAME)?.value); } +function serializeCookie( + name: string, + value: string, + options: { + httpOnly?: boolean; + sameSite?: "lax" | "strict" | "none"; + secure?: boolean; + path?: string; + maxAge?: number; + }, +) { + const parts = [`${name}=${encodeURIComponent(value)}`]; + + if (options.maxAge) { + parts.push(`Max-Age=${options.maxAge}`); + } + + if (options.path) { + parts.push(`Path=${options.path}`); + } + + if (options.httpOnly) { + parts.push("HttpOnly"); + } + + if (options.sameSite) { + parts.push(`SameSite=${options.sameSite[0].toUpperCase()}${options.sameSite.slice(1)}`); + } + + if (options.secure) { + parts.push("Secure"); + } + + return parts.join("; "); +} + function shouldUseSecureVisitorCookie(requestUrl?: string) { if (!requestUrl) { return process.env.NODE_ENV === "production"; @@ -35,19 +70,18 @@ function shouldUseSecureVisitorCookie(requestUrl?: string) { } export function setVisitorIdCookie( - response: NextResponse, + response: Response, visitorId: string, requestUrl?: string, ) { - response.cookies.set( - VISITOR_ID_COOKIE_NAME, - visitorId, - { + response.headers.append( + "Set-Cookie", + serializeCookie(VISITOR_ID_COOKIE_NAME, visitorId, { httpOnly: true, sameSite: "lax", secure: shouldUseSecureVisitorCookie(requestUrl), path: "/", maxAge: VISITOR_ID_COOKIE_MAX_AGE, - }, + }), ); } diff --git a/tests/e2e/admin-dashboard.spec.ts b/tests/e2e/admin-dashboard.spec.ts new file mode 100644 index 0000000..a33c045 --- /dev/null +++ b/tests/e2e/admin-dashboard.spec.ts @@ -0,0 +1,38 @@ +import "dotenv/config"; + +import { expect, test } from "@playwright/test"; + +import { + ADMIN_EMAIL, + ADMIN_PASSWORD, + loginWithCredentials, +} from "./helpers/auth"; + +test("운영자 로그인 시 지도와 관리자 대시보드에서 관리/로그아웃 액션을 사용할 수 있다", async ({ + page, +}) => { + await loginWithCredentials(page, { + callbackUrl: "/", + email: ADMIN_EMAIL, + password: ADMIN_PASSWORD, + }); + + await expect(page).toHaveURL(/\/$/); + await expect(page.getByTestId("session-user-badge")).toBeVisible(); + await expect(page.getByTestId("session-admin-link")).toBeVisible(); + await expect(page.getByTestId("sign-out-button")).toBeVisible(); + + await page.getByTestId("session-admin-link").click(); + + await expect(page).toHaveURL(/\/admin$/); + await expect(page.getByTestId("admin-metric-total-users")).toBeVisible(); + await expect(page.getByTestId("admin-metric-current-sessions")).toBeVisible(); + await expect(page.getByTestId("admin-recent-user-list")).toBeVisible(); + await expect(page.getByTestId("admin-overview-places-link")).toBeVisible(); + await expect(page.getByTestId("admin-overview-reports-link")).toBeVisible(); + + await page.getByTestId("sign-out-button").click(); + + await expect(page).toHaveURL(/\/$/); + await expect(page.getByTestId("session-login-link")).toBeVisible(); +}); diff --git a/tests/e2e/map.spec.ts b/tests/e2e/map.spec.ts index 5088eec..2abbc72 100644 --- a/tests/e2e/map.spec.ts +++ b/tests/e2e/map.spec.ts @@ -56,20 +56,3 @@ test("지도 검색, 상세 시트, 비회원 좋아요, 공유, 닫기 흐름", await expect(detailSheet).toBeHidden(); await expect(page.getByTestId("map-panel-shell")).toBeVisible(); }); - -test("좋아요순 정렬에서 목록 좋아요 수가 내림차순이다", async ({ page }) => { - await page.goto("/"); - - await page.getByTestId("sort-option-likes").click(); - await expect(page).toHaveURL(/sort=likes/); - await expect(page.getByTestId("place-list")).toBeVisible(); - - const counts = await page - .locator("[data-testid^='place-list-like-count-']") - .allTextContents(); - const numericCounts = counts.map((value) => parseCount(value)); - const sortedCounts = [...numericCounts].sort((left, right) => right - left); - - expect(numericCounts.length).toBeGreaterThan(1); - expect(numericCounts).toEqual(sortedCounts); -}); diff --git a/tests/e2e/submission-admin.spec.ts b/tests/e2e/submission-admin.spec.ts index 5e045e2..4a62f34 100644 --- a/tests/e2e/submission-admin.spec.ts +++ b/tests/e2e/submission-admin.spec.ts @@ -1,6 +1,6 @@ import "dotenv/config"; -import { expect, test, type Page } from "@playwright/test"; +import { expect, test } from "@playwright/test"; import { ADMIN_EMAIL, @@ -10,38 +10,15 @@ import { loginWithCredentials, } from "./helpers/auth"; -async function setHiddenCoordinate( - page: Page, - testId: "submit-latitude" | "submit-longitude", - value: string, -) { - await page.getByTestId(testId).evaluate((element, nextValue) => { - const input = element as HTMLInputElement; - input.value = String(nextValue); - input.dispatchEvent(new Event("input", { bubbles: true })); - input.dispatchEvent(new Event("change", { bubbles: true })); - }, value); -} - -test("장소 등록 폼에서 주소가 바뀌면 좌표를 다시 확인해야 한다", async ({ +test("공개 장소 등록 폼은 텍스트 입력만 노출한다", async ({ page, }) => { await page.goto("/submit"); await expect(page.getByTestId("place-submit-form")).toBeVisible(); - await page.getByTestId("submit-district").fill("서울 성북구"); - await page - .getByTestId("submit-road-address") - .fill("서울 성북구 동소문로22길 31"); - await setHiddenCoordinate(page, "submit-latitude", "37.590100"); - await setHiddenCoordinate(page, "submit-longitude", "127.015800"); - - await page - .getByTestId("submit-road-address") - .fill("서울 성북구 보문로 12"); - - await expect(page.getByTestId("submit-latitude")).toHaveValue(""); - await expect(page.getByTestId("submit-longitude")).toHaveValue(""); + await expect(page.getByTestId("submit-address-lookup-button")).toHaveCount(0); + await expect(page.getByTestId("submit-latitude")).toHaveCount(0); + await expect(page.getByTestId("submit-longitude")).toHaveCount(0); }); test("장소를 등록하고 운영자가 승인하면 홈 검색에 노출된다", async ({ @@ -91,8 +68,6 @@ test("장소를 등록하고 운영자가 승인하면 홈 검색에 노출된 await userPage .getByTestId("submit-note") .fill("E2E 승인 검증용 제출 데이터입니다."); - await setHiddenCoordinate(userPage, "submit-latitude", latitude); - await setHiddenCoordinate(userPage, "submit-longitude", longitude); await userPage.getByTestId("submit-place-button").click(); await expect(userPage.getByTestId("submit-result")).toBeVisible(); diff --git a/trd.md b/trd.md index d1656d5..94b6183 100644 --- a/trd.md +++ b/trd.md @@ -155,7 +155,8 @@ - 비회원과 로그인 사용자 모두 가능 - 장소 신규 등록 가능 - 공개 등록 폼은 `name` 단일 필드만 노출하고, `business_name`은 운영 보강 또는 기존 데이터 호환용 nullable metadata로 유지한다 -- 공개 등록 폼은 지도/좌표 UI를 노출하지 않고, 텍스트 주소 입력 뒤 내부 geocoding으로 위치 확인이 끝나야 저장한다 +- 공개 등록 폼은 지도/좌표 UI와 링크 입력 UI를 노출하지 않고, 이름/주소/가격 같은 텍스트 정보만 접수한다 +- 지도 표시 좌표 확인은 운영자 승인 단계에서 처리하고, 승인 시 확정한 좌표를 저장한다 - 가격 항목은 최소 1개 입력 - 인증 사용자가 아니면 `created_by_user_id`는 `null`로 저장하고, rate limit은 session user id가 없을 때 visitor cookie 또는 forwarded IP fallback 기준으로 적용한다 @@ -192,11 +193,15 @@ ### 6.9 운영자 관리 - 운영자 전용 라우트 제공 - 최소 기능: + - overview 대시보드 + - 사용자 수, 현재 세션 수, 승인/검토 큐 수 집계 + - 최근 가입 사용자 목록 - 신고 목록 조회 - 장소 승인/수정 - 중복 장소 병합 - 가격 항목 수정 - 숨김 처리 +- 방문 수, DAU/WAU 같은 활동 지표는 별도 visit/activity 이벤트 적재 후 2차로 제공한다. --- @@ -445,7 +450,6 @@ - `minPrice` - `maxPrice` - `includeNoPrice` -- `sort` 응답 필드: - `id` @@ -531,10 +535,11 @@ ## 13. 운영자 도구 설계 ### 13.1 운영자 대시보드 우선순위 -1. 신고 처리 -2. 중복 장소 병합 -3. 장소 숨김/복구 -4. 가격 항목 수정 +1. overview 대시보드와 운영 진입 동선 +2. 신고 처리 +3. 중복 장소 병합 +4. 장소 숨김/복구 +5. 가격 항목 수정 ### 13.2 중복 병합 규칙 - 병합 시 대표 장소 1건을 남긴다. diff --git a/tsconfig.json b/tsconfig.json index cf9c65d..472a3ad 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -19,7 +23,9 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": [ + "./src/*" + ] } }, "include": [ @@ -28,7 +34,11 @@ "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts", - "**/*.mts" + "**/*.mts", + ".next-dev/types/**/*.ts", + ".next-dev/dev/types/**/*.ts" ], - "exclude": ["node_modules"] + "exclude": [ + "node_modules" + ] } diff --git a/wrangler.jsonc b/wrangler.jsonc index 6ffb086..6c4eaff 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -20,10 +20,5 @@ "service": "altteulmap", "environment": "production" } - ], - "images": { - // Enable image optimization - // see https://opennext.js.org/cloudflare/howtos/image - "binding": "IMAGES" - } + ] } From b0f5c800fd5e9792c0a54bfedf4d05ad66119534 Mon Sep 17 00:00:00 2001 From: MJY Date: Thu, 2 Apr 2026 08:34:53 +0900 Subject: [PATCH 3/6] chore: simplify legacy map redirect --- PLAN.md | 19 +++++++++---------- PROGRESS.md | 18 ++++++++++++++++++ README.md | 17 +++++++++++++---- next.config.ts | 9 +++++++++ package.json | 2 +- src/app/map/page.tsx | 34 ---------------------------------- 6 files changed, 50 insertions(+), 49 deletions(-) delete mode 100644 src/app/map/page.tsx diff --git a/PLAN.md b/PLAN.md index fba9a66..ec7375a 100644 --- a/PLAN.md +++ b/PLAN.md @@ -22,14 +22,13 @@ - 좋아요 랭킹 ## 현재 우선순위 -1. 로컬 dev/runtime 안정화: `.next` 충돌 방지, dev/build/test 산출물 분리, 재현 가능한 복구 경로 정리 -2. 로그인 상태 액션과 관리자 대시보드 정리: 로그아웃 노출, 운영자 진입 동선, 관리자 overview와 유저/운영 지표 설계 -3. 인증 정리와 출시 준비: 실제 로그인 경로, 로그인/회원가입 진입면과 소셜 온보딩, 반복 가능한 검증 절차, Cloudflare 배포 체크리스트 -4. 공개 UI polish: 헤더 간소화, 개발 문구 제거, 버튼/칩 줄바꿈 방지, 지도/상세 밀도 정리 -5. 운영 품질 후속 정리: rate limit 수치 조정, 관리자 UX polish, 캐시/재검증 보강 -6. 확장 기능 1차: 플레이스 좋아요/싫어요 반응 모델과 UI 우선 도입 -7. 확장 기능 2차: 공유/사진 업로드 범위와 우선순위 재결정 -8. repo-local 개발 워크플로우 유지: `.agents` skills/reviewers, `.githooks`, `verify` 스크립트 +1. 관리자 visit/activity telemetry 2차: 방문 수, 고유 방문자, DAU/WAU, 재방문율을 적재하고 관리자 대시보드에 연결 +2. 인증 정리와 출시 준비: 실제 로그인 경로, 로그인/회원가입 진입면과 소셜 온보딩, 반복 가능한 검증 절차, Cloudflare 배포 체크리스트 +3. 공개 UI polish: 헤더 간소화, 개발 문구 제거, 버튼/칩 줄바꿈 방지, 지도/상세 밀도 정리 +4. 운영 품질 후속 정리: rate limit 수치 조정, 관리자 UX polish, 캐시/재검증 보강 +5. 확장 기능 1차: 플레이스 좋아요/싫어요 반응 모델과 UI 우선 도입 +6. 확장 기능 2차: 공유/사진 업로드 범위와 우선순위 재결정 +7. repo-local 개발 워크플로우 유지: `.agents` skills/reviewers, `.githooks`, `verify` 스크립트 ## 현재 제품 상태 @@ -87,10 +86,10 @@ |---|---|---|---|---|---| | ECC에서 필요한 부분만 가져와 altteulmap 저장소 하위에 repo-local skills, reviewer guides, verify script, git hooks를 구성하고 전역 설정 없이 활성화한다 | Codex | P2 | `done` | `.agents/skills`, `.agents/reviewers`, `.githooks`, `scripts/git-hooks`가 추가되고, `npm run hooks:install`, `npm run verify`가 동작하며, `AGENTS.md`/`README.md`/`PLAN.md`/`PROGRESS.md`에 사용 규칙과 검증 결과가 반영된다 | `AGENTS.md`, `README.md`, `package.json`, `.githooks/**`, `scripts/git-hooks/**` | -### Cycle 8: 로컬 개발/검증 안정화 +### Cycle 8: 로컬 개발/검증 안정화 (완료) | 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | |---|---|---|---|---|---| -| 로컬 `next dev`가 build/start/e2e와 `.next`를 공유하며 깨지는 문제를 막고, 재기동 없이 페이지 전환이 계속 가능한 상태로 정리한다 | Codex | P1 | `in_progress` | dev 서버는 production build/start/e2e 산출물과 캐시를 분리해 사용하고, Turbopack `.sst/.meta` 손상이나 `Another write batch or compaction is already active` 오류가 재발하지 않도록 기본 스크립트/설정이 조정되며, 복구 명령과 검증 결과가 `README.md`와 `PROGRESS.md`에 남는다 | `package.json`, `next.config.ts`, `.gitignore`, `README.md`, `PROGRESS.md` | +| 로컬 `next dev`가 build/start/e2e와 `.next`를 공유하며 깨지는 문제를 막고, 재기동 없이 페이지 전환이 계속 가능한 상태로 정리한다 | Codex | P1 | `done` | dev 서버는 production build/start/e2e 산출물과 캐시를 분리해 사용하고, Turbopack `.sst/.meta` 손상이나 `Another write batch or compaction is already active` 오류가 재발하지 않도록 기본 스크립트/설정이 조정되며, 복구 명령과 검증 결과가 `README.md`와 `PROGRESS.md`에 남는다 | `package.json`, `next.config.ts`, `.gitignore`, `README.md`, `PROGRESS.md` | ### Cycle 2: 문서 체계 정비와 지도 검색/URL 상태 반영 (완료) | 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | diff --git a/PROGRESS.md b/PROGRESS.md index 35e2811..7208ffc 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -13,10 +13,28 @@ - Cycle 5 후속: GitHub Actions CI를 `push용 smoke`와 `PR용 full`로 분리하고 Cloudflare Builds와 분리 운영하는 경로 정리 완료 - Cycle 6: 좋아요/싫어요 반응 도입 완료, 비로그인 visitor cookie 반응과 공개 메타 줄 분리까지 반영. 랭킹/목록 노출 확장은 남아 있음 - Cycle 7: repo-local AI workflow 설정 완료 (`.agents`, `.githooks`, `verify`, local commit rules) +- Cycle 8: 로컬 dev/runtime 안정화 완료 (`.next-dev` 분리, `webpack` dev 고정, build/e2e와 출력 경로 분리) - 다음 우선순위: 관리자 visit/activity telemetry 2차, 이후 실제 외부 로그인 E2E와 운영 도메인 점검 ## 실행 로그 +### 2026-04-02 08:31 KST: 로컬 dev/build/e2e 산출물 분리로 페이지 전환 멈춤과 cache 손상 방지 +- 완료 내용 + - `/Users/alex/project/altteulmap/package.json`의 `dev`를 `rm -rf .next-dev && next dev --webpack`으로 바꿨다. 이제 로컬 dev는 Turbopack cache가 아니라 별도 `.next-dev` 산출물과 webpack dev 서버를 사용한다. + - `/Users/alex/project/altteulmap/next.config.ts`에 dev/build 런타임별 `distDir` 분기를 추가해, dev는 `.next-dev`, build/start/e2e는 계속 `.next`를 쓰도록 고정했다. 이로써 `next dev`와 `next build`/`next start`/Playwright가 같은 `.next`를 공유하며 `00000084.sst` 같은 SST 파일을 잃어버리던 경로를 끊었다. + - `/Users/alex/project/altteulmap/.gitignore`, `/Users/alex/project/altteulmap/package.json`의 `cf:clean`, `/Users/alex/project/altteulmap/README.md`를 새 산출물 경로 기준으로 갱신했다. Cloudflare clean도 이제 `.next-dev`까지 함께 정리한다. + - `next dev` 첫 기동 시 `/Users/alex/project/altteulmap/tsconfig.json`의 `include`에 `.next-dev/types/**/*.ts`, `.next-dev/dev/types/**/*.ts`가 자동 추가됐다. dev 타입 생성 위치가 바뀐 데 따른 정상 반영이라 유지한다. +- 검증 결과 + - `npm run verify:quick` 통과 + - `rm -rf .next && npm run verify` 통과 + - `npm run dev` 기동 후 `.next-dev` 생성 확인, `curl -s -o /tmp/altteulmap-admin.html -w '%{http_code}' http://127.0.0.1:3000/admin` 결과 `307` + - 같은 dev 서버를 유지한 채 `rm -rf .next && npm run build` 통과 + - 같은 dev 서버를 유지한 채 `curl -s -o /tmp/altteulmap-root-during-build.html -w '%{http_code}' http://127.0.0.1:3000/` 결과 `200` + - 같은 dev 서버를 유지한 채 `DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/altteulmap USE_MOCK_DATA=false AUTH_SECRET=altteulmap-local-auth-secret-change-me NEXTAUTH_URL=http://127.0.0.1:3107 AUTH_DEMO_PASSWORD=demo1234 AUTH_ADMIN_PASSWORD=admin1234 npm run test:e2e:smoke:ci` 통과 +- 메모 + - 이번 이슈의 직접 원인은 `next dev`가 `.next/dev/cache/turbopack` 아래 SST/metadata를 쓰는 동안, build/start/e2e 경로가 같은 `.next`를 다시 만지면서 cache 참조가 깨진 것이다. 사용자에게 보인 `Unable to open static sorted file`, `Failed to lookup task ids`, `Another write batch or compaction is already active`는 이 충돌의 결과다. + - 이후 로컬 dev가 다시 이상하면 production 산출물인 `.next`는 건드리지 말고 `rm -rf .next-dev && npm run dev`로 복구하는 것을 기본 경로로 삼는다. + ### 2026-04-02 08:18 KST: `/map` 호환 경로를 redirect로 줄이고 홈 단일 진입점으로 정리 - 완료 내용 - `/Users/alex/project/altteulmap/src/features/map/map-page.tsx`로 실제 지도 홈 구현을 분리했다. diff --git a/README.md b/README.md index a3c9521..37db41d 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,15 @@ npm run dev 브라우저에서 `http://localhost:3000`을 열면 됩니다. +로컬 개발 서버는 `webpack` 기반으로 실행되고 산출물을 `.next-dev`에 저장합니다. 반대로 `build`, `start`, Playwright E2E, Cloudflare 빌드는 계속 `.next` 또는 `.open-next`를 사용하므로, dev 서버를 띄운 채 빌드/검증을 돌려도 예전처럼 같은 `.next`를 공유하며 깨지지 않습니다. + +페이지 전환이 멈추거나 dev cache가 이상하면 아래처럼 dev 산출물만 비우고 다시 올리면 됩니다. + +```bash +rm -rf .next-dev +npm run dev +``` + ## 주요 스크립트 ```bash @@ -35,9 +44,9 @@ npm run db:down npm run preview ``` -- `dev`: Next.js 로컬 개발 서버 +- `dev`: Next.js 로컬 개발 서버 (`webpack`, `.next-dev`) - `lint`: ESLint 검사 -- `build`: Next.js 프로덕션 빌드 +- `build`: Next.js 프로덕션 빌드 (`.next`) - `verify`: 현재 프로젝트 기준 전체 기본 검증(`lint + build`) - `test:e2e:smoke`: `main` 푸시 기준의 빠른 Playwright smoke 세트 - `test:e2e`: Playwright E2E 실행 @@ -49,13 +58,13 @@ npm run preview - `db:push`: 로컬/개발 DB에 스키마 반영 - `db:seed`: 로컬 DB에 목업 시드 데이터 입력 - `db:down`: 로컬 Postgres 컨테이너 중지 -- `cf:clean`: Cloudflare 빌드 전 `.next`, `.open-next` 정리 +- `cf:clean`: Cloudflare 빌드 전 `.next`, `.next-dev`, `.open-next` 정리 - `cf:build`: Cloudflare 배포용 clean build - `preview`: OpenNext로 Cloudflare Workers 런타임 미리보기 `deploy`, `upload`는 Cloudflare 계정과 Wrangler 인증이 준비된 뒤 사용하면 됩니다. -Cloudflare 무료 플랜 기준 경량화는 `next build --webpack` + `cf:clean` 경로를 전제로 맞춰져 있습니다. 배포는 `npm run deploy`를 그대로 쓰면 됩니다. +현재 산출물 경로는 `dev -> .next-dev`, `build/start/e2e -> .next`, `Cloudflare preview/deploy -> .open-next`로 분리돼 있습니다. Cloudflare 무료 플랜 기준 경량화는 `next build --webpack` + `cf:clean` 경로를 전제로 맞춰져 있습니다. 배포는 `npm run deploy`를 그대로 쓰면 됩니다. ## DB 시작 diff --git a/next.config.ts b/next.config.ts index 2e84e56..3f434e4 100644 --- a/next.config.ts +++ b/next.config.ts @@ -7,6 +7,15 @@ const isDevServer = const nextConfig: NextConfig = { distDir: isDevServer ? ".next-dev" : ".next", + async redirects() { + return [ + { + source: "/map", + destination: "/", + permanent: true, + }, + ]; + }, }; initOpenNextCloudflareForDev(); diff --git a/package.json b/package.json index 17ecc47..9ea114a 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "build": "next build --webpack", + "build": "rm -rf .next .next-dev && next build --webpack", "dev": "rm -rf .next-dev && next dev --webpack", "start": "next start", "lint": "eslint", diff --git a/src/app/map/page.tsx b/src/app/map/page.tsx deleted file mode 100644 index a8307ac..0000000 --- a/src/app/map/page.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { permanentRedirect } from "next/navigation"; - -type LegacyMapPageProps = { - searchParams: Promise>; -}; - -function toRootHref(params: Record) { - const search = new URLSearchParams(); - - for (const [key, value] of Object.entries(params)) { - if (Array.isArray(value)) { - for (const entry of value) { - if (entry) { - search.append(key, entry); - } - } - continue; - } - - if (value) { - search.set(key, value); - } - } - - const query = search.toString(); - - return query ? `/?${query}` : "/"; -} - -export default async function LegacyMapPage({ - searchParams, -}: LegacyMapPageProps) { - permanentRedirect(toRootHref(await searchParams)); -} From 89510060abae46e245092ccde13009361bdb6ce7 Mon Sep 17 00:00:00 2001 From: MJY Date: Thu, 2 Apr 2026 08:46:41 +0900 Subject: [PATCH 4/6] fix(ci): defer auth env access during build --- src/app/api/auth/[...nextauth]/route.ts | 6 +- src/auth.ts | 168 ++++++++++++------------ src/lib/session.ts | 9 +- 3 files changed, 95 insertions(+), 88 deletions(-) diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts index d0b5ff8..fd22bbd 100644 --- a/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -1,9 +1,11 @@ import NextAuth from "next-auth"; -import { authOptions } from "@/auth"; +import { getAuthOptions } from "@/auth"; export const runtime = "nodejs"; -const handler = NextAuth(authOptions); +async function handler(request: Request) { + return NextAuth(getAuthOptions())(request); +} export { handler as GET, handler as POST }; diff --git a/src/auth.ts b/src/auth.ts index 76ca7ff..2f10ac1 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -1,4 +1,4 @@ -import NextAuth, { type NextAuthOptions } from "next-auth"; +import { type NextAuthOptions } from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import KakaoProvider from "next-auth/providers/kakao"; import NaverProvider from "next-auth/providers/naver"; @@ -72,89 +72,89 @@ function createAuthProviders() { return providers; } -export const authOptions: NextAuthOptions = { - secret: getRequiredServerEnv("AUTH_SECRET"), - session: { - strategy: "jwt", - }, - pages: { - signIn: "/login", - }, - providers: createAuthProviders(), - callbacks: { - async signIn({ user, account }) { - if (!account || account.provider === "credentials") { - return true; - } - - if (account.provider !== "kakao" && account.provider !== "naver") { - return true; - } - - if (!user.email) { - return "/login?error=OAuthEmailRequired"; - } - - const syncedUser = await syncOAuthUser({ - provider: account.provider, - providerAccountId: account.providerAccountId, - type: account.type, - email: user.email, - name: user.name, - accessToken: - typeof account.access_token === "string" ? account.access_token : null, - refreshToken: - typeof account.refresh_token === "string" - ? account.refresh_token - : null, - expiresAt: - typeof account.expires_at === "number" ? account.expires_at : null, - tokenType: - typeof account.token_type === "string" ? account.token_type : null, - scope: typeof account.scope === "string" ? account.scope : null, - idToken: - typeof account.id_token === "string" ? account.id_token : null, - sessionState: - typeof account.session_state === "string" - ? account.session_state - : null, - }); - - if (!syncedUser) { - return "/login?error=OAuthAccountSyncFailed"; - } - - user.id = syncedUser.id; - user.email = syncedUser.email; - user.name = syncedUser.nickname ?? syncedUser.email.split("@")[0]; - user.role = syncedUser.role; - - return true; +export function getAuthOptions(): NextAuthOptions { + return { + secret: getRequiredServerEnv("AUTH_SECRET"), + session: { + strategy: "jwt", }, - jwt({ token, user }) { - if (user) { - token.sub = user.id; - token.email = user.email; - token.name = user.name; - token.role = (user as { role: AppUserRole }).role; - } - - return token; + pages: { + signIn: "/login", }, - session({ session, token }) { - if (session.user) { - session.user.id = token.sub ?? ""; - session.user.email = token.email ?? session.user.email ?? ""; - session.user.name = token.name ?? session.user.name; - session.user.role = - token.role === "admin" || token.role === "user" - ? token.role - : "user"; - } - - return session; - }, - }, -}; + providers: createAuthProviders(), + callbacks: { + async signIn({ user, account }) { + if (!account || account.provider === "credentials") { + return true; + } + + if (account.provider !== "kakao" && account.provider !== "naver") { + return true; + } + + if (!user.email) { + return "/login?error=OAuthEmailRequired"; + } + + const syncedUser = await syncOAuthUser({ + provider: account.provider, + providerAccountId: account.providerAccountId, + type: account.type, + email: user.email, + name: user.name, + accessToken: + typeof account.access_token === "string" ? account.access_token : null, + refreshToken: + typeof account.refresh_token === "string" + ? account.refresh_token + : null, + expiresAt: + typeof account.expires_at === "number" ? account.expires_at : null, + tokenType: + typeof account.token_type === "string" ? account.token_type : null, + scope: typeof account.scope === "string" ? account.scope : null, + idToken: + typeof account.id_token === "string" ? account.id_token : null, + sessionState: + typeof account.session_state === "string" + ? account.session_state + : null, + }); + + if (!syncedUser) { + return "/login?error=OAuthAccountSyncFailed"; + } -export default NextAuth(authOptions); + user.id = syncedUser.id; + user.email = syncedUser.email; + user.name = syncedUser.nickname ?? syncedUser.email.split("@")[0]; + user.role = syncedUser.role; + + return true; + }, + jwt({ token, user }) { + if (user) { + token.sub = user.id; + token.email = user.email; + token.name = user.name; + token.role = (user as { role: AppUserRole }).role; + } + + return token; + }, + session({ session, token }) { + if (session.user) { + session.user.id = token.sub ?? ""; + session.user.email = token.email ?? session.user.email ?? ""; + session.user.name = token.name ?? session.user.name; + session.user.role = + token.role === "admin" || token.role === "user" + ? token.role + : "user"; + } + + return session; + }, + }, + }; +} diff --git a/src/lib/session.ts b/src/lib/session.ts index 287b5d3..0779bdc 100644 --- a/src/lib/session.ts +++ b/src/lib/session.ts @@ -2,8 +2,9 @@ import "server-only"; import { getServerSession } from "next-auth"; -import { authOptions } from "@/auth"; +import { getAuthOptions } from "@/auth"; import type { AppUserRole } from "@/features/auth/constants"; +import { serverEnv } from "@/lib/env"; export type SessionUser = { id: string; @@ -58,7 +59,11 @@ export function createSignupHref(callbackUrl: string) { } export async function getSessionUser(): Promise { - const session = await getServerSession(authOptions); + if (!serverEnv.AUTH_SECRET) { + return null; + } + + const session = await getServerSession(getAuthOptions()); const user = session?.user; if (!user?.id || !user.email) { From 5fd72716293aebf6e14e62b5346ad414b5721a8e Mon Sep 17 00:00:00 2001 From: MJY Date: Thu, 2 Apr 2026 08:51:43 +0900 Subject: [PATCH 5/6] fix(ci): run cf build in wrangler deploys --- wrangler.jsonc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wrangler.jsonc b/wrangler.jsonc index 6c4eaff..2b43a72 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -8,6 +8,12 @@ "nodejs_compat", "global_fetch_strictly_public" ], + "build": { + // Workers Builds on Cloudflare need to generate the OpenNext worker bundle first. + // Local deploy scripts already run `cf:build`, so this only fills the gap when + // Wrangler is invoked directly from a fresh checkout. + "command": "test -f .open-next/worker.js || npm run cf:build" + }, "assets": { "directory": ".open-next/assets", "binding": "ASSETS" From 092427d13bb034c606d9fc4a363a89395c5803c9 Mon Sep 17 00:00:00 2001 From: MJY Date: Thu, 2 Apr 2026 09:04:56 +0900 Subject: [PATCH 6/6] feat: split admin app and prepare public cutover --- .gitignore | 4 + PLAN.md | 28 +- PROGRESS.md | 86 + README.md | 38 +- apps/admin/.npmrc | 1 + apps/admin/next.config.ts | 14 + apps/admin/open-next.config.ts | 3 + apps/admin/package.json | 9 + apps/admin/postcss.config.mjs | 7 + apps/admin/src/app/admin/page.tsx | 1 + apps/admin/src/app/admin/places/page.tsx | 1 + apps/admin/src/app/admin/prices/page.tsx | 1 + .../src/app/admin/prices/places/[id]/page.tsx | 1 + apps/admin/src/app/admin/reports/page.tsx | 1 + .../src/app/api/admin/places/[id]/route.ts | 1 + apps/admin/src/app/api/admin/places/route.ts | 1 + .../app/api/admin/price-items/[id]/route.ts | 1 + .../src/app/api/admin/prices/[id]/route.ts | 1 + apps/admin/src/app/api/admin/prices/route.ts | 1 + .../src/app/api/admin/reports/[id]/route.ts | 1 + apps/admin/src/app/api/admin/reports/route.ts | 1 + .../src/app/api/auth/[...nextauth]/route.ts | 3 + apps/admin/src/app/globals.css | 219 + apps/admin/src/app/layout.tsx | 23 + apps/admin/src/app/login/page.tsx | 1 + apps/admin/src/app/page.tsx | 5 + apps/admin/src/app/signup/page.tsx | 1 + apps/admin/tsconfig.json | 34 + apps/admin/wrangler.jsonc | 22 + data/goodprice/import-meta.json | 14132 ++++ docs/cloudflare-account-to-deploy.md | 14 + docs/deploy-cloudflare.md | 21 +- eslint.config.mjs | 4 + package.json | 17 +- scripts/build-admin-worker.mjs | 28 + scripts/build-public-worker.mjs | 81 + scripts/import-goodprice.ts | 667 + scripts/sync-admin-entrypoints.mjs | 51 + src/app/admin/page.tsx | 381 +- src/app/admin/places/page.tsx | 200 +- src/app/admin/prices/page.tsx | 281 +- src/app/admin/prices/places/[id]/page.tsx | 166 +- src/app/admin/reports/page.tsx | 171 +- src/app/api/admin/places/[id]/route.ts | 107 +- src/app/api/admin/places/route.ts | 41 +- src/app/api/admin/price-items/[id]/route.ts | 75 +- src/app/api/admin/prices/[id]/route.ts | 64 +- src/app/api/admin/prices/route.ts | 41 +- src/app/api/admin/reports/[id]/route.ts | 60 +- src/app/api/admin/reports/route.ts | 41 +- src/db/seed.ts | 2 +- src/features/admin/api/place-detail.ts | 103 + src/features/admin/api/places-list.ts | 37 + src/features/admin/api/price-detail.ts | 63 + src/features/admin/api/price-item-detail.ts | 74 + src/features/admin/api/prices-list.ts | 37 + src/features/admin/api/report-detail.ts | 59 + src/features/admin/api/reports-list.ts | 37 + .../admin/entrypoints/api/place-detail.ts | 103 + .../admin/entrypoints/api/places-list.ts | 37 + .../admin/entrypoints/api/price-detail.ts | 63 + .../entrypoints/api/price-item-detail.ts | 74 + .../admin/entrypoints/api/prices-list.ts | 37 + .../admin/entrypoints/api/report-detail.ts | 59 + .../admin/entrypoints/api/reports-list.ts | 37 + .../entrypoints/pages/dashboard-page.tsx | 377 + .../entrypoints/pages/place-prices-page.tsx | 162 + .../admin/entrypoints/pages/places-page.tsx | 196 + .../admin/entrypoints/pages/prices-page.tsx | 277 + .../admin/entrypoints/pages/reports-page.tsx | 167 + src/features/admin/pages/dashboard-page.tsx | 377 + .../admin/pages/place-prices-page.tsx | 162 + src/features/admin/pages/places-page.tsx | 196 + src/features/admin/pages/prices-page.tsx | 277 + src/features/admin/pages/reports-page.tsx | 167 + src/features/admin/repository.ts | 2 +- src/features/admin/stubs/api/place-detail.ts | 19 + src/features/admin/stubs/api/places-list.ts | 7 + src/features/admin/stubs/api/price-detail.ts | 13 + .../admin/stubs/api/price-item-detail.ts | 13 + src/features/admin/stubs/api/prices-list.ts | 7 + src/features/admin/stubs/api/report-detail.ts | 13 + src/features/admin/stubs/api/reports-list.ts | 7 + .../admin/stubs/pages/dashboard-page.tsx | 25 + .../admin/stubs/pages/place-prices-page.tsx | 35 + .../admin/stubs/pages/places-page.tsx | 25 + .../admin/stubs/pages/prices-page.tsx | 25 + .../admin/stubs/pages/reports-page.tsx | 25 + src/features/auth/session-action-group.tsx | 3 +- src/features/places/catalog-data.ts | 10 + src/features/places/imported-goodprice.json | 60626 ++++++++++++++++ src/features/places/queries.ts | 2 +- src/features/places/repository.ts | 2 +- src/lib/admin-app.ts | 44 + src/lib/env.ts | 9 + wrangler.admin.jsonc | 25 + 96 files changed, 79662 insertions(+), 1608 deletions(-) create mode 100644 apps/admin/.npmrc create mode 100644 apps/admin/next.config.ts create mode 100644 apps/admin/open-next.config.ts create mode 100644 apps/admin/package.json create mode 100644 apps/admin/postcss.config.mjs create mode 100644 apps/admin/src/app/admin/page.tsx create mode 100644 apps/admin/src/app/admin/places/page.tsx create mode 100644 apps/admin/src/app/admin/prices/page.tsx create mode 100644 apps/admin/src/app/admin/prices/places/[id]/page.tsx create mode 100644 apps/admin/src/app/admin/reports/page.tsx create mode 100644 apps/admin/src/app/api/admin/places/[id]/route.ts create mode 100644 apps/admin/src/app/api/admin/places/route.ts create mode 100644 apps/admin/src/app/api/admin/price-items/[id]/route.ts create mode 100644 apps/admin/src/app/api/admin/prices/[id]/route.ts create mode 100644 apps/admin/src/app/api/admin/prices/route.ts create mode 100644 apps/admin/src/app/api/admin/reports/[id]/route.ts create mode 100644 apps/admin/src/app/api/admin/reports/route.ts create mode 100644 apps/admin/src/app/api/auth/[...nextauth]/route.ts create mode 100644 apps/admin/src/app/globals.css create mode 100644 apps/admin/src/app/layout.tsx create mode 100644 apps/admin/src/app/login/page.tsx create mode 100644 apps/admin/src/app/page.tsx create mode 100644 apps/admin/src/app/signup/page.tsx create mode 100644 apps/admin/tsconfig.json create mode 100644 apps/admin/wrangler.jsonc create mode 100644 data/goodprice/import-meta.json create mode 100644 scripts/build-admin-worker.mjs create mode 100644 scripts/build-public-worker.mjs create mode 100644 scripts/import-goodprice.ts create mode 100644 scripts/sync-admin-entrypoints.mjs create mode 100644 src/features/admin/api/place-detail.ts create mode 100644 src/features/admin/api/places-list.ts create mode 100644 src/features/admin/api/price-detail.ts create mode 100644 src/features/admin/api/price-item-detail.ts create mode 100644 src/features/admin/api/prices-list.ts create mode 100644 src/features/admin/api/report-detail.ts create mode 100644 src/features/admin/api/reports-list.ts create mode 100644 src/features/admin/entrypoints/api/place-detail.ts create mode 100644 src/features/admin/entrypoints/api/places-list.ts create mode 100644 src/features/admin/entrypoints/api/price-detail.ts create mode 100644 src/features/admin/entrypoints/api/price-item-detail.ts create mode 100644 src/features/admin/entrypoints/api/prices-list.ts create mode 100644 src/features/admin/entrypoints/api/report-detail.ts create mode 100644 src/features/admin/entrypoints/api/reports-list.ts create mode 100644 src/features/admin/entrypoints/pages/dashboard-page.tsx create mode 100644 src/features/admin/entrypoints/pages/place-prices-page.tsx create mode 100644 src/features/admin/entrypoints/pages/places-page.tsx create mode 100644 src/features/admin/entrypoints/pages/prices-page.tsx create mode 100644 src/features/admin/entrypoints/pages/reports-page.tsx create mode 100644 src/features/admin/pages/dashboard-page.tsx create mode 100644 src/features/admin/pages/place-prices-page.tsx create mode 100644 src/features/admin/pages/places-page.tsx create mode 100644 src/features/admin/pages/prices-page.tsx create mode 100644 src/features/admin/pages/reports-page.tsx create mode 100644 src/features/admin/stubs/api/place-detail.ts create mode 100644 src/features/admin/stubs/api/places-list.ts create mode 100644 src/features/admin/stubs/api/price-detail.ts create mode 100644 src/features/admin/stubs/api/price-item-detail.ts create mode 100644 src/features/admin/stubs/api/prices-list.ts create mode 100644 src/features/admin/stubs/api/report-detail.ts create mode 100644 src/features/admin/stubs/api/reports-list.ts create mode 100644 src/features/admin/stubs/pages/dashboard-page.tsx create mode 100644 src/features/admin/stubs/pages/place-prices-page.tsx create mode 100644 src/features/admin/stubs/pages/places-page.tsx create mode 100644 src/features/admin/stubs/pages/prices-page.tsx create mode 100644 src/features/admin/stubs/pages/reports-page.tsx create mode 100644 src/features/places/catalog-data.ts create mode 100644 src/features/places/imported-goodprice.json create mode 100644 src/lib/admin-app.ts create mode 100644 wrangler.admin.jsonc diff --git a/.gitignore b/.gitignore index 7888013..8900b54 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,9 @@ # next.js /.next/ /.next-dev/ +/apps/admin/.next/ +/apps/admin/.next-dev/ +/apps/admin/.open-next/ /out/ # production @@ -46,6 +49,7 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts +/apps/admin/next-env.d.ts # OpenNext .open-next diff --git a/PLAN.md b/PLAN.md index ec7375a..42f4d2e 100644 --- a/PLAN.md +++ b/PLAN.md @@ -22,13 +22,14 @@ - 좋아요 랭킹 ## 현재 우선순위 -1. 관리자 visit/activity telemetry 2차: 방문 수, 고유 방문자, DAU/WAU, 재방문율을 적재하고 관리자 대시보드에 연결 -2. 인증 정리와 출시 준비: 실제 로그인 경로, 로그인/회원가입 진입면과 소셜 온보딩, 반복 가능한 검증 절차, Cloudflare 배포 체크리스트 -3. 공개 UI polish: 헤더 간소화, 개발 문구 제거, 버튼/칩 줄바꿈 방지, 지도/상세 밀도 정리 -4. 운영 품질 후속 정리: rate limit 수치 조정, 관리자 UX polish, 캐시/재검증 보강 -5. 확장 기능 1차: 플레이스 좋아요/싫어요 반응 모델과 UI 우선 도입 -6. 확장 기능 2차: 공유/사진 업로드 범위와 우선순위 재결정 -7. repo-local 개발 워크플로우 유지: `.agents` skills/reviewers, `.githooks`, `verify` 스크립트 +1. 관리자 앱 분리 1차: public/admin build 경로를 분리하고, 별도 `apps/admin` 빌드와 외부 관리자 앱 전환 경로를 정리 +2. 관리자 visit/activity telemetry 2차: 방문 수, 고유 방문자, DAU/WAU, 재방문율을 적재하고 관리자 대시보드에 연결 +3. 인증 정리와 출시 준비: 실제 로그인 경로, 로그인/회원가입 진입면과 소셜 온보딩, 반복 가능한 검증 절차, Cloudflare 배포 체크리스트 +4. 공개 UI polish: 헤더 간소화, 개발 문구 제거, 버튼/칩 줄바꿈 방지, 지도/상세 밀도 정리 +5. 운영 품질 후속 정리: rate limit 수치 조정, 관리자 UX polish, 캐시/재검증 보강 +6. 확장 기능 1차: 플레이스 좋아요/싫어요 반응 모델과 UI 우선 도입 +7. 확장 기능 2차: 공유/사진 업로드 범위와 우선순위 재결정 +8. repo-local 개발 워크플로우 유지: `.agents` skills/reviewers, `.githooks`, `verify` 스크립트 ## 현재 제품 상태 @@ -64,6 +65,8 @@ - 검색어/검색 범위는 반영되지만 지도 중심/줌 상태까지는 아직 URL에 싣지 않음 - 출시 준비 - robots, sitemap, canonical, `smoke:local`, `deploy:check`, Playwright E2E 기본 흐름, credentials 로그인/가입/북마크/신고/관리자 승인/관리자 가격 검토 E2E, GitHub Actions CI, Cloudflare Builds 기준 배포 체크 문서는 정리 중이고, 현재 E2E는 세 그룹 실행 기준으로 반복 가능하다. Cloudflare 무료 플랜 기준 번들 경량화와 실제 `workers.dev` 배포까지는 통과했다. 실제 OAuth와 실제 운영 도메인 기준 end-to-end 점검은 남아 있다 +- 관리자 분리 + - 관리자 실제 구현은 `src/features/admin/pages`, `src/features/admin/api`에 있고, public 앱의 `/admin`, `/api/admin`은 `entrypoints`를 통해 embedded/external 모드를 바꿀 수 있다. 별도 `apps/admin` Next 앱 빌드는 가능하지만, 실제 `altteulmap-admin` 배포와 `ADMIN_APP_URL` 기준 cutover는 아직 남아 있다 - 인증 - 로그인/회원가입 진입면과 로컬 credentials, 카카오/네이버 OAuth scaffolding은 준비돼 있고, 현재는 credentials 회원가입까지 동작한다. `.env.local` 기준으로 네이버는 활성화 가능 상태이고, 카카오는 client secret 누락 시 비활성화된다. 실제 provider credential과 callback URL로 끝까지 검증한 외부 로그인 경로는 아직 없다 - 반응 기능 @@ -81,6 +84,17 @@ ## Active Plan +### Cycle 9: 외부 착한가격업소 데이터 적재 (완료) +| 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | +|---|---|---|---|---|---| +| 행정안전부 `착한가격업소` 사이트의 목록/엑셀/상세 구조를 분석해 1차 수집 경로와 필터 기준을 확정한다 | Codex | P1 | `done` | `/bssh/bsshList.do`, `/bssh/bsshPageExcel.do`, `/bssh/bsshInfo.json`의 역할, 좌표/상세/가격 데이터 차이, robots/rate limit 고려사항, `1만원 이하 1천건` 선별 기준과 적재 전략이 `PROGRESS.md`에 정리된다 | `PLAN.md`, `PROGRESS.md`, 외부 사이트 구조 | +| `착한가격업소` 실제 데이터를 더미 데이터 대신 넣을 수 있도록 수집 스크립트, 정규화 매핑, 좌표 확보 전략, import seed 경로를 구현한다 | Codex | P1 | `done` | 공식 다운로드 또는 페이지 수집으로 원천 데이터가 로컬 파일/테이블에 저장되고, `1만원 이하` 기준과 업종/지역 매핑이 정규화되며, 좌표는 목록 페이지 좌표 또는 별도 geocoding으로 채워지고, 최소 1천건이 앱에서 조회 가능하며, 검증 로그가 `PROGRESS.md`에 남는다 | 수집 설계 확정, `src/db/**`, import script, geocoding 경로, 검증 | + +### Cycle 10: 관리자 앱 분리 1차 +| 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | +|---|---|---|---|---|---| +| 관리자 페이지/API의 실제 구현을 공유 모듈로 고정하고, public 앱과 별도 `apps/admin` 앱이 같은 구현을 재사용하도록 정리한다 | Codex | P1 | `in_progress` | `src/features/admin/pages`, `src/features/admin/api`가 관리자 실제 구현의 기준이 되고, public 앱의 `/admin`, `/api/admin`은 `entrypoints`를 통해 embedded/external 모드를 스위치할 수 있으며, `apps/admin`이 별도 Next 앱으로 빌드되고, `ADMIN_APP_URL` 기준 외부 관리자 앱 전환 경로와 검증 결과가 문서에 남는다 | `src/app/admin/**`, `src/app/api/admin/**`, `src/features/admin/**`, `src/lib/admin-app.ts`, `apps/admin/**`, `package.json`, Cloudflare 배포 문서 | + ### Cycle 7: repo-local AI workflow setup (완료) | 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | |---|---|---|---|---|---| diff --git a/PROGRESS.md b/PROGRESS.md index 7208ffc..d3c2eb4 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -3,6 +3,7 @@ 기준일: 2026-04-01 ## 진행 현황 요약 +- Cycle 10: 관리자 실제 구현을 `src/features/admin/**`로 모으고, public 앱 `entrypoints`와 별도 `apps/admin` 빌드를 추가해 관리자 분리 1차 스캐폴딩 완료. `deploy:admin`과 `deploy:public` 경로도 분리했고, 실제 admin deploy와 `ADMIN_APP_URL` cutover는 마지막 운영 적용만 남아 있음 - Cycle 0: 프로젝트 로컬 기반, DB 경로, 지도 탐색, 장소 상세, 등록, 신고, 북마크, 관리자 검토, 로컬 인증, 네이버 지도 연동 완료 - Cycle 1: 현재 위치 버튼, viewport 재조회, 모바일 목록 바텀시트, 모바일 상세 시트 기초 정리 완료 - Cycle 2: `PLAN.md`/`PROGRESS.md` 운영 문서 형식 정비, 지역/전역 검색, 검색 URL 상태 반영 완료 @@ -14,10 +15,95 @@ - Cycle 6: 좋아요/싫어요 반응 도입 완료, 비로그인 visitor cookie 반응과 공개 메타 줄 분리까지 반영. 랭킹/목록 노출 확장은 남아 있음 - Cycle 7: repo-local AI workflow 설정 완료 (`.agents`, `.githooks`, `verify`, local commit rules) - Cycle 8: 로컬 dev/runtime 안정화 완료 (`.next-dev` 분리, `webpack` dev 고정, build/e2e와 출력 경로 분리) +- Cycle 9: 행정안전부 `착한가격업소` 실제 데이터 1000건 import 완료. 지역 라운드로빈 수집, 상세 메뉴 보강, DB seed/API 검증까지 반영됨 - 다음 우선순위: 관리자 visit/activity telemetry 2차, 이후 실제 외부 로그인 E2E와 운영 도메인 점검 ## 실행 로그 +### 2026-04-02 09:08 KST: `착한가격업소` 실제 데이터 importer 구현과 1000건 seed 반영 +- 완료 내용 + - `/Users/alex/project/altteulmap/scripts/import-goodprice.ts`를 추가했다. `bsshList.do`를 지역별 round-robin으로 순차 POST 요청해 `대표 가격 <= 10000원`인 업소만 모으고, 선택된 업소에 한해서 `bsshInfo.json`으로 상세 메뉴를 보강한 뒤 앱용 `PlaceRecord[]`로 변환한다. + - 수집 결과는 `/Users/alex/project/altteulmap/src/features/places/imported-goodprice.json`과 `/Users/alex/project/altteulmap/data/goodprice/import-meta.json`으로 저장된다. 전자는 앱/seed 입력, 후자는 `bsshSn`, 지역 분포, 업종 분포, 선택 옵션 같은 수집 메타를 남긴다. + - `/Users/alex/project/altteulmap/src/features/places/catalog-data.ts`를 추가하고 `/Users/alex/project/altteulmap/src/features/places/repository.ts`, `/Users/alex/project/altteulmap/src/features/places/queries.ts`, `/Users/alex/project/altteulmap/src/features/admin/repository.ts`, `/Users/alex/project/altteulmap/src/db/seed.ts`를 실제 import 데이터 우선 사용 구조로 바꿨다. `imported-goodprice.json`이 비어 있지 않으면 mock fallback과 DB seed가 모두 이 데이터를 사용한다. + - `/Users/alex/project/altteulmap/package.json`, `/Users/alex/project/altteulmap/README.md`에 `npm run data:goodprice` 실행 경로를 추가했다. + - 실제 생성 결과는 2026-04-02 기준 `1000` places, `2616` price items이며, 대표 가격 최대값은 `10000원`, `priceItems > 10000`은 `0건`이었다. +- 검증 결과 + - `npm run data:goodprice -- --limit=20 --delay-ms=50 --include-detail=false --output=/tmp/goodprice-test.json --manifest=/tmp/goodprice-test-manifest.json` 통과 + - `npm run data:goodprice -- --limit=50 --delay-ms=50 --timeout-ms=10000 --include-detail=true --output=/tmp/goodprice-50.json --manifest=/tmp/goodprice-50-manifest.json` 통과 + - `npm run data:goodprice -- --delay-ms=50 --timeout-ms=10000` 통과 + - `python3`로 `/Users/alex/project/altteulmap/src/features/places/imported-goodprice.json` 검사 결과 `1000`건, 평균 `2.62` price items, 대표 가격 최대 `10000`, `priceItems > 10000` `0건` 확인 + - `npm run verify:quick` 통과. 단, 저장소 기존 `apps/admin/.open-next/**` generated 파일 때문에 ESLint warning은 계속 남음 + - `rm -rf .next && npm run verify`는 동일한 `apps/admin/.open-next/**` generated lint 오류 때문에 실패. 이번 importer 변경과 직접 관련된 오류는 아니고, `npm run build` 단독 경로는 통과 + - `npm run build` 통과 + - `npm run db:up` 통과 + - `npm run db:push` 통과 + - `npm run db:seed` 통과 + - `PORT=3111 USE_MOCK_DATA=false NEXTAUTH_URL=http://127.0.0.1:3111 npm run start` 후 `curl http://127.0.0.1:3111/api/places/map` 응답에서 `source: "database"`와 실제 `goodprice-*` payload 확인 +- 메모 + - 현재 importer는 공공 사이트 부하를 줄이기 위해 병렬 대량 요청 대신 지역별 순차 수집과 timeout/fallback을 사용한다. + - 좌표는 목록 페이지의 `articleObject2`에서 가져오고, 상세 메뉴는 `bsshInfo.json`에서 보강한다. 공식 Excel export는 구조 파악과 분포 확인에는 유용했지만, 좌표가 없어 1차 앱 적재 원천으로는 직접 쓰지 않았다. + - `apps/admin/.open-next/**` 경로는 저장소 기존 generated 결과물이라 `verify` 전체 통과를 막고 있다. importer 작업 자체의 build/seed/API 경로는 정상 확인했다. + +### 2026-04-02 10:12 KST: 관리자 Worker 배포 경로 보정과 public/admin cutover 조건 고정 +- 완료 내용 + - `/Users/alex/project/altteulmap/scripts/build-admin-worker.mjs`, `/Users/alex/project/altteulmap/apps/admin/open-next.config.ts`를 추가해 `apps/admin`이 자기 cwd에서 OpenNext build를 만들도록 정리했다. + - `/Users/alex/project/altteulmap/package.json`의 `cf:build:admin`, `preview:admin`, `deploy:admin`을 별도 관리자 앱 기준으로 바꿨고, `/Users/alex/project/altteulmap/wrangler.admin.jsonc`도 `apps/admin/.open-next/**`를 바라보도록 수정했다. + - `/Users/alex/project/altteulmap/scripts/build-public-worker.mjs`는 `ADMIN_APP_URL`이 없으면 실패하게 바꿨다. public-only 배포가 `/admin`, `/api/admin`을 제거하므로 외부 관리자 앱 주소 없이 배포되면 링크가 깨지기 때문이다. + - `/Users/alex/project/altteulmap/.env.example`, `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md`, `/Users/alex/project/altteulmap/docs/cloudflare-account-to-deploy.md`를 새 배포 순서에 맞게 갱신했다. +- 검증 결과 + - `npm run verify:quick` 통과 + - `npm run cf:build:admin` 통과 + - `ADMIN_APP_URL=https://altteulmap-admin.altteul-lab.workers.dev npm run cf:build:public` 통과 +- 메모 + - 이전 `deploy:admin`은 root app OpenNext 산출물을 다시 배포하는 구조라 실제 별도 관리자 앱 배포로 이어지지 않았다. 지금 수정은 그 경로를 바로잡는 목적이다. + - `cf:build:admin` 결과 route 목록에 `/admin`, `/api/admin/*`, `/login`, `/signup`만 남는 것을 확인했다. + - `cf:build:public` 결과 route 목록에서 `/admin`, `/api/admin/*`가 빠지는 것을 확인했다. + +### 2026-04-02 09:24 KST: 관리자 분리 1차 스캐폴딩과 public/admin 빌드 경로 정리 +- 완료 내용 + - `/Users/alex/project/altteulmap/src/features/admin/pages`, `/Users/alex/project/altteulmap/src/features/admin/api`를 관리자 실제 구현 기준으로 두고, `/Users/alex/project/altteulmap/src/app/admin/**`, `/Users/alex/project/altteulmap/src/app/api/admin/**`는 `/Users/alex/project/altteulmap/src/features/admin/entrypoints/**`를 바라보도록 정리했다. + - `/Users/alex/project/altteulmap/scripts/sync-admin-entrypoints.mjs`를 추가해 `ALTTEULMAP_ADMIN_MODE=embedded|external`에 따라 entrypoint가 실제 구현 또는 stub(`/Users/alex/project/altteulmap/src/features/admin/stubs/**`)을 바라보게 했다. + - `/Users/alex/project/altteulmap/src/lib/admin-app.ts`와 stub 페이지/API를 보강해서 `ADMIN_APP_URL`이 없을 때는 redirect loop 대신 안내 패널 또는 `503` 응답을 반환하게 했다. + - `/Users/alex/project/altteulmap/apps/admin/**`에 별도 Next 앱을 스캐폴딩했다. 이 앱은 `/admin`, `/api/admin`, `/login`, `/signup`, `/api/auth/[...nextauth]`만 노출하고, 루트의 shared admin/auth 구현을 재사용한다. + - `/Users/alex/project/altteulmap/package.json`에는 `admin:sync`, `admin:build`, `admin:dev`, `admin:start`를 추가했고, 루트 `build/dev/cf:build`는 entrypoint를 먼저 동기화하도록 맞췄다. +- 검증 결과 + - `npm run admin:sync` 통과 + - `npm run verify:quick` 통과 + - `npm run build` 통과 + - `npm run admin:build` 통과 + - `ALTTEULMAP_ADMIN_MODE=external npm run admin:sync` 후 `src/features/admin/entrypoints/pages/dashboard-page.tsx`, `src/features/admin/entrypoints/api/places-list.ts`가 stub 구현으로 바뀌는 것 확인 후 `npm run admin:sync`로 embedded 복구 +- 메모 + - 현재는 build 경로와 별도 admin 앱 빌드만 정리된 상태다. 실제 운영에서는 `apps/admin`을 `altteulmap-admin`으로 배포하고, public 앱은 `ADMIN_APP_URL`과 `ALTTEULMAP_ADMIN_MODE=external` 기준으로 cutover하는 단계가 한 번 더 필요하다. + +### 2026-04-02 09:05 KST: Cloudflare preview build에서 AUTH_SECRET 누락으로 죽던 auth eager load 수정 +- 완료 내용 + - `/Users/alex/project/altteulmap/src/auth.ts`에서 `authOptions` 상수와 default `NextAuth(...)` eager 초기화를 제거하고, `getAuthOptions()` factory로 바꿨다. 이제 module import만으로 `AUTH_SECRET`을 바로 읽지 않는다. + - `/Users/alex/project/altteulmap/src/app/api/auth/[...nextauth]/route.ts`는 request 시점에 `NextAuth(getAuthOptions())`를 생성하도록 바꿨다. + - `/Users/alex/project/altteulmap/src/lib/session.ts`는 `AUTH_SECRET`이 없는 환경에서는 `getSessionUser()`를 `null`로 반환해, Cloudflare preview build의 prerender 단계에서 `/submit` 같은 공개 페이지가 로그인 세션 조회 때문에 실패하지 않게 정리했다. +- 검증 결과 + - `npm run verify:quick` 통과 + - `tmpdir=$(mktemp -d) && rsync -a --delete --exclude '.git' --exclude '.env' --exclude '.env.*' --exclude '.dev.vars' --exclude '.dev.vars.*' --exclude 'node_modules' ./ "$tmpdir/" && cd "$tmpdir" && npm ci --ignore-scripts && env -i HOME="$HOME" PATH="$PATH" NODE_ENV=production npm run cf:build` 통과 +- 메모 + - 수정 전에는 같은 무환경 build에서 `AUTH_SECRET is not set` 예외가 `/api/bookmarks/[id]`, `/api/admin/places/[id]`, `/submit` prerender 단계까지 전파돼 Cloudflare external build가 실패하는 것을 재현했다. + - 이 수정은 build 시점 강제만 제거한 것이다. 실제 runtime auth를 쓰려면 Cloudflare 쪽 `AUTH_SECRET`은 계속 필요하다. + +### 2026-04-02 08:39 KST: 행정안전부 `착한가격업소` 수집 구조 조사와 1차 적재 계획 정리 +- 완료 내용 + - `https://goodprice.go.kr/bssh/bsshList.do`가 서버 렌더 POST 폼 목록 페이지임을 확인했다. `pageIndex`, `srchCtpvCd`, `srchSggCd`, `srchIndutyCdArr`, `srchBsshNm`, `srchKeyword`, 편의시설 플래그를 hidden field로 보내며, 페이지당 목록은 4건씩 렌더된다. + - 같은 페이지의 JS에서 목록 마커 좌표가 `articleObject2` 문자열로 현재 페이지 4건에 한해 포함되는 것을 확인했다. 즉 공식 좌표는 목록 HTML에만 있고, 상세 JSON에는 없다. + - `https://goodprice.go.kr/bssh/bsshInfo.json`는 `bsshSn` 기준 상세 API로 열려 있고, 업소명/주소/업종/전화/편의시설/부서 연락처/이미지 메타와 `menuList`를 JSON으로 준다. 다만 여기에는 `lat`, `lot`이 비어 있다. + - `https://goodprice.go.kr/bssh/bsshPageExcel.do`는 현재 검색 조건 전체 결과를 `.xls`로 내려준다. `pageIndex=1&menuId=MN-0103`만 보내도 2026-04-02 기준 전체 `12,109`건이 한 번에 내려왔고, 열은 `업종명/업소명/주요품목/가격/전화/주소/편의시설/지역화폐/이미지명`까지 포함한다. + - 같은 전체 Excel에서 `가격 <= 10000원` 조건을 적용해 보니 `8,661`건이 남았다. 업종 분포는 `한식 5,258`, `미용업 976`, `기타요식업 690`, `중식 632` 순이고, 지역 상위는 `서울특별시 1,389`, `경기도 1,264`, `부산광역시 810`이었다. + - `robots.txt`는 일부 커뮤니티 경로만 막고 있지만, 기본적으로 공공 사이트이므로 대량 병렬 크롤링 대신 `공식 Excel 우선 + 필요한 상세/좌표만 저속 보강` 전략으로 잡는다. +- 검증 결과 + - `curl -L 'https://goodprice.go.kr/bssh/bsshList.do'`로 목록 HTML/폼 구조 확인 + - `curl -s 'https://goodprice.go.kr/bssh/bsshInfo.json' -X POST -F 'bsshSn=11005' ... | jq`로 상세 JSON 필드 확인 + - `curl -s -D - -o /tmp/goodprice-page.xls 'https://goodprice.go.kr/bssh/bsshPageExcel.do' -X POST -d 'pageIndex=1&menuId=MN-0103'`로 전체 Excel export 확인 + - `PYTHONPATH=/tmp/xlrd-check python3 ...`로 Excel row 수와 `가격 <= 10000` 분포 계산 +- 메모 + - `bsshInfo.json`는 `bsshSn`가 필요하지만 Excel에는 `bsshSn`가 없다. 따라서 `상세 메뉴/이미지/공식 좌표`까지 쓰려면 목록 HTML에서 `goInfo('...')`와 `articleObject2`를 추가로 모아 join해야 한다. + - 1차 import는 `Excel 일괄 다운로드 -> 가격 1만원 이하 필터 -> 지역/업종 균형 샘플 1천건 선별 -> 주소 geocoding 또는 목록 좌표 보강 -> 앱용 seed 생성` 순서가 가장 안전하다. 상세 메뉴/이미지는 2차 enrichment로 분리하는 편이 요청 수와 복잡도를 줄인다. + ### 2026-04-02 08:31 KST: 로컬 dev/build/e2e 산출물 분리로 페이지 전환 멈춤과 cache 손상 방지 - 완료 내용 - `/Users/alex/project/altteulmap/package.json`의 `dev`를 `rm -rf .next-dev && next dev --webpack`으로 바꿨다. 이제 로컬 dev는 Turbopack cache가 아니라 별도 `.next-dev` 산출물과 webpack dev 서버를 사용한다. diff --git a/README.md b/README.md index 37db41d..6072f16 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,14 @@ npm run test:e2e:smoke npm run test:e2e npm run smoke:local npm run deploy:check +npm run admin:build +npm run cf:build:admin npm run hooks:install npm run db:up npm run db:generate npm run db:push npm run db:seed +npm run data:goodprice npm run db:down npm run preview ``` @@ -52,19 +55,36 @@ npm run preview - `test:e2e`: Playwright E2E 실행 - `smoke:local`: 실행 중인 로컬 서버에 대해 SEO/API/credentials 로그인 기본 스모크 체크 - `deploy:check`: Cloudflare 배포 전 필수 환경 변수와 URL 설정 점검 +- `admin:sync`: public 앱의 관리자 route entrypoint를 `embedded` 또는 `external` 구현으로 동기화 +- `admin:build`: 별도 `apps/admin` 관리자 앱 빌드 +- `cf:build:admin`: 별도 `apps/admin` 관리자 Worker용 OpenNext build - `hooks:install`: 이 저장소 전용 git hook 활성화 - `db:up`: 로컬 Postgres 컨테이너 시작 - `db:generate`: Drizzle 마이그레이션 SQL 생성 - `db:push`: 로컬/개발 DB에 스키마 반영 -- `db:seed`: 로컬 DB에 목업 시드 데이터 입력 +- `db:seed`: 로컬 DB에 시드 데이터 입력 (`imported-goodprice.json`이 있으면 실제 착한가격업소 1000건 우선 사용) +- `data:goodprice`: 행정안전부 `착한가격업소` 사이트에서 `1만원 이하` 실제 업소를 수집해 `src/features/places/imported-goodprice.json`과 `data/goodprice/import-meta.json` 생성 - `db:down`: 로컬 Postgres 컨테이너 중지 - `cf:clean`: Cloudflare 빌드 전 `.next`, `.next-dev`, `.open-next` 정리 - `cf:build`: Cloudflare 배포용 clean build - `preview`: OpenNext로 Cloudflare Workers 런타임 미리보기 +- `preview:public`: 관리자 route를 제외한 public 앱 preview +- `preview:admin`: 별도 관리자 앱 preview +- `deploy:public`: 관리자 route를 제외한 public 앱 배포 +- `deploy:admin`: 별도 관리자 앱 배포 -`deploy`, `upload`는 Cloudflare 계정과 Wrangler 인증이 준비된 뒤 사용하면 됩니다. +`deploy`, `deploy:public`, `deploy:admin`, `upload`는 Cloudflare 계정과 Wrangler 인증이 준비된 뒤 사용하면 됩니다. -현재 산출물 경로는 `dev -> .next-dev`, `build/start/e2e -> .next`, `Cloudflare preview/deploy -> .open-next`로 분리돼 있습니다. Cloudflare 무료 플랜 기준 경량화는 `next build --webpack` + `cf:clean` 경로를 전제로 맞춰져 있습니다. 배포는 `npm run deploy`를 그대로 쓰면 됩니다. +현재 산출물 경로는 `dev -> .next-dev`, `build/start/e2e -> .next`, `Cloudflare preview/deploy -> .open-next`로 분리돼 있습니다. Cloudflare 무료 플랜 기준 경량화는 `next build --webpack` + `cf:clean` 경로를 전제로 맞춰져 있습니다. + +관리자 분리 1차가 들어가 있어서, 현재 배포 경로는 두 가지입니다. +- 기본 `deploy`: 관리자 구현을 포함한 현재 앱 전체 배포 +- `deploy:public`: public 앱만 배포하고 `/admin`, `/api/admin`은 번들에서 제외하는 경로 +- `deploy:admin`: `apps/admin`을 `altteulmap-admin` 같은 별도 Worker로 배포하는 경로 + +`deploy:public`은 `ADMIN_APP_URL`이 반드시 있어야 합니다. public 앱에서 관리자 링크를 별도 관리자 앱으로 보낼 때 쓰는 값이기 때문입니다. + +별도 관리자 앱은 `apps/admin`에서 관리하며, 로컬 검증은 `npm run admin:build`, Worker 번들 검증은 `npm run cf:build:admin`으로 먼저 확인합니다. ## DB 시작 @@ -90,12 +110,23 @@ npm run db:seed DB가 연결된 상태에서는 `/signup`에서 새 이메일 계정을 직접 만들 수 있습니다. 가입이 끝나면 같은 이메일/비밀번호로 바로 로그인됩니다. +실제 데이터를 다시 받으려면 아래 순서로 실행하면 됩니다. + +```bash +npm run data:goodprice -- --delay-ms=50 --timeout-ms=10000 +npm run db:seed +``` + +생성된 `src/features/places/imported-goodprice.json`은 mock fallback과 DB seed 양쪽에서 공통으로 우선 사용합니다. 수집 메타와 원본 업소 id/지역 분포는 `data/goodprice/import-meta.json`에 남습니다. + 기본 예시는 `.env.example`에 들어 있고, 로컬 `.env`도 같은 값으로 맞춰두었습니다. `/`에서 실제 네이버 지도를 보려면 `NEXT_PUBLIC_NAVER_MAP_KEY_ID`를 설정하면 됩니다. 아직 키가 없으면 같은 화면에서 자동으로 임시 프리뷰 지도로 fallback됩니다. 기존 `NEXT_PUBLIC_NAVER_MAP_CLIENT_ID` 값도 함께 지원합니다. `NEXTAUTH_URL`은 로그인 callback뿐 아니라 `robots.txt`, `sitemap.xml`, canonical metadata의 기준 URL로도 사용합니다. 배포 시에는 반드시 실제 도메인으로 바꿔야 합니다. +관리자 앱을 분리할 때는 `ADMIN_APP_URL`도 같이 설정합니다. 예를 들면 `https://altteulmap-admin.altteul-lab.workers.dev`처럼 별도 관리자 Worker 주소를 넣고, 그 뒤 public 앱을 `deploy:public`으로 배포합니다. + 소셜 로그인을 붙일 때는 지도 키와 분리해서 아래 환경 변수를 사용합니다. - `AUTH_KAKAO_CLIENT_ID` @@ -201,6 +232,7 @@ npm run db:down ## Cloudflare 관련 파일 - `wrangler.jsonc`: Workers 설정 파일 +- `wrangler.admin.jsonc`: 별도 관리자 Worker 설정 파일 - `open-next.config.ts`: OpenNext 설정 파일 - `.dev.vars`: 로컬 Cloudflare 개발용 변수 - `public/_headers`: 정적 자산 캐시 헤더 diff --git a/apps/admin/.npmrc b/apps/admin/.npmrc new file mode 100644 index 0000000..7068b01 --- /dev/null +++ b/apps/admin/.npmrc @@ -0,0 +1 @@ +workspaces=false diff --git a/apps/admin/next.config.ts b/apps/admin/next.config.ts new file mode 100644 index 0000000..f60cc14 --- /dev/null +++ b/apps/admin/next.config.ts @@ -0,0 +1,14 @@ +import type { NextConfig } from "next"; + +const isDevServer = + process.argv.some((argument) => argument === "dev") || + process.env.NODE_ENV === "development"; + +const nextConfig: NextConfig = { + distDir: isDevServer ? ".next-dev" : ".next", + experimental: { + externalDir: true, + }, +}; + +export default nextConfig; diff --git a/apps/admin/open-next.config.ts b/apps/admin/open-next.config.ts new file mode 100644 index 0000000..1a57e31 --- /dev/null +++ b/apps/admin/open-next.config.ts @@ -0,0 +1,3 @@ +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; + +export default defineCloudflareConfig({}); diff --git a/apps/admin/package.json b/apps/admin/package.json new file mode 100644 index 0000000..c40cc33 --- /dev/null +++ b/apps/admin/package.json @@ -0,0 +1,9 @@ +{ + "name": "altteulmap-admin", + "private": true, + "scripts": { + "build": "rm -rf .next .next-dev && node ../../node_modules/next/dist/bin/next build --webpack", + "dev": "rm -rf .next-dev && node ../../node_modules/next/dist/bin/next dev --webpack -p 3001", + "start": "node ../../node_modules/next/dist/bin/next start -p 3001" + } +} diff --git a/apps/admin/postcss.config.mjs b/apps/admin/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/apps/admin/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/apps/admin/src/app/admin/page.tsx b/apps/admin/src/app/admin/page.tsx new file mode 100644 index 0000000..7bc5c94 --- /dev/null +++ b/apps/admin/src/app/admin/page.tsx @@ -0,0 +1 @@ +export { default, dynamic } from "@/features/admin/pages/dashboard-page"; diff --git a/apps/admin/src/app/admin/places/page.tsx b/apps/admin/src/app/admin/places/page.tsx new file mode 100644 index 0000000..e8b12cd --- /dev/null +++ b/apps/admin/src/app/admin/places/page.tsx @@ -0,0 +1 @@ +export { default, dynamic } from "@/features/admin/pages/places-page"; diff --git a/apps/admin/src/app/admin/prices/page.tsx b/apps/admin/src/app/admin/prices/page.tsx new file mode 100644 index 0000000..b04fe07 --- /dev/null +++ b/apps/admin/src/app/admin/prices/page.tsx @@ -0,0 +1 @@ +export { default, dynamic } from "@/features/admin/pages/prices-page"; diff --git a/apps/admin/src/app/admin/prices/places/[id]/page.tsx b/apps/admin/src/app/admin/prices/places/[id]/page.tsx new file mode 100644 index 0000000..25479be --- /dev/null +++ b/apps/admin/src/app/admin/prices/places/[id]/page.tsx @@ -0,0 +1 @@ +export { default, dynamic } from "@/features/admin/pages/place-prices-page"; diff --git a/apps/admin/src/app/admin/reports/page.tsx b/apps/admin/src/app/admin/reports/page.tsx new file mode 100644 index 0000000..127fddb --- /dev/null +++ b/apps/admin/src/app/admin/reports/page.tsx @@ -0,0 +1 @@ +export { default, dynamic } from "@/features/admin/pages/reports-page"; diff --git a/apps/admin/src/app/api/admin/places/[id]/route.ts b/apps/admin/src/app/api/admin/places/[id]/route.ts new file mode 100644 index 0000000..1d25b23 --- /dev/null +++ b/apps/admin/src/app/api/admin/places/[id]/route.ts @@ -0,0 +1 @@ +export { GET, PATCH } from "@/features/admin/api/place-detail"; diff --git a/apps/admin/src/app/api/admin/places/route.ts b/apps/admin/src/app/api/admin/places/route.ts new file mode 100644 index 0000000..a556247 --- /dev/null +++ b/apps/admin/src/app/api/admin/places/route.ts @@ -0,0 +1 @@ +export { dynamic, GET } from "@/features/admin/api/places-list"; diff --git a/apps/admin/src/app/api/admin/price-items/[id]/route.ts b/apps/admin/src/app/api/admin/price-items/[id]/route.ts new file mode 100644 index 0000000..19b69a4 --- /dev/null +++ b/apps/admin/src/app/api/admin/price-items/[id]/route.ts @@ -0,0 +1 @@ +export { PATCH } from "@/features/admin/api/price-item-detail"; diff --git a/apps/admin/src/app/api/admin/prices/[id]/route.ts b/apps/admin/src/app/api/admin/prices/[id]/route.ts new file mode 100644 index 0000000..a9bf7bc --- /dev/null +++ b/apps/admin/src/app/api/admin/prices/[id]/route.ts @@ -0,0 +1 @@ +export { PATCH } from "@/features/admin/api/price-detail"; diff --git a/apps/admin/src/app/api/admin/prices/route.ts b/apps/admin/src/app/api/admin/prices/route.ts new file mode 100644 index 0000000..ed1b7a5 --- /dev/null +++ b/apps/admin/src/app/api/admin/prices/route.ts @@ -0,0 +1 @@ +export { dynamic, GET } from "@/features/admin/api/prices-list"; diff --git a/apps/admin/src/app/api/admin/reports/[id]/route.ts b/apps/admin/src/app/api/admin/reports/[id]/route.ts new file mode 100644 index 0000000..45bff70 --- /dev/null +++ b/apps/admin/src/app/api/admin/reports/[id]/route.ts @@ -0,0 +1 @@ +export { PATCH } from "@/features/admin/api/report-detail"; diff --git a/apps/admin/src/app/api/admin/reports/route.ts b/apps/admin/src/app/api/admin/reports/route.ts new file mode 100644 index 0000000..93ab527 --- /dev/null +++ b/apps/admin/src/app/api/admin/reports/route.ts @@ -0,0 +1 @@ +export { dynamic, GET } from "@/features/admin/api/reports-list"; diff --git a/apps/admin/src/app/api/auth/[...nextauth]/route.ts b/apps/admin/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..2bc3cd1 --- /dev/null +++ b/apps/admin/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,3 @@ +export const runtime = "nodejs"; + +export { GET, POST } from "@/app/api/auth/[...nextauth]/route"; diff --git a/apps/admin/src/app/globals.css b/apps/admin/src/app/globals.css new file mode 100644 index 0000000..71e52d6 --- /dev/null +++ b/apps/admin/src/app/globals.css @@ -0,0 +1,219 @@ +@import "tailwindcss"; +@source "../../../../src"; +@source "../.."; + +:root { + --altteul-accent: #c97949; + --altteul-accent-hover: #b4683c; + --altteul-accent-soft: #f4e1d2; + --altteul-accent-soft-hover: #edd6c3; + --altteul-accent-border: #e4c2a8; + --altteul-accent-text: #8f522f; + --altteul-accent-ink: #6d4128; + --altteul-accent-panel-a: #fff7ef; + --altteul-accent-panel-b: #f2dfcf; + --altteul-surface-fill: #fffdfb; + --altteul-surface-fill-hover: #fcf8f4; + --altteul-surface-fill-active: #f7f0e8; + --altteul-surface-border: rgba(171, 141, 116, 0.24); + --altteul-surface-border-strong: rgba(150, 118, 91, 0.36); + --altteul-focus-ring: rgba(201, 121, 73, 0.14); +} + +.altteulmap-naver-map img { + max-width: none !important; +} + +.altteulmap-accent-solid { + background-color: var(--altteul-accent); + color: #fff; +} + +.altteulmap-accent-solid:hover { + background-color: var(--altteul-accent-hover); +} + +.altteulmap-accent-chip { + border-color: #9c5934; + background: linear-gradient(180deg, #cb7c4d 0%, #b96b41 100%); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.18), + 0 0 0 1px rgba(156, 89, 52, 0.12); + color: #fff; +} + +.altteulmap-accent-chip:hover { + border-color: #8f522f; + background: linear-gradient(180deg, #bf7145 0%, #a95f38 100%); + color: #fff; +} + +.altteulmap-accent-panel { + border: 1px solid var(--altteul-accent-border); + background: linear-gradient( + 135deg, + var(--altteul-accent-panel-a) 0%, + var(--altteul-accent-panel-b) 100% + ); + color: var(--altteul-accent-ink); +} + +.altteulmap-accent-ghost { + border-color: var(--altteul-accent-border); + background-color: rgba(255, 250, 246, 0.88); + color: var(--altteul-accent-ink); +} + +.altteulmap-accent-ghost:hover { + background-color: #fff; +} + +.altteulmap-button { + border: 1px solid var(--altteul-surface-border); + border-radius: 0.85rem; + background-color: var(--altteul-surface-fill); + transition: + border-color 160ms ease, + background-color 160ms ease, + color 160ms ease; + -webkit-tap-highlight-color: transparent; +} + +.altteulmap-button:hover { + border-color: var(--altteul-surface-border-strong); + background-color: var(--altteul-surface-fill-hover); +} + +.altteulmap-button:active { + background-color: var(--altteul-surface-fill-active); +} + +.altteulmap-button:focus-visible { + outline: none; + box-shadow: 0 0 0 3px var(--altteul-focus-ring); +} + +.altteulmap-button:disabled { + background-color: rgba(247, 242, 237, 0.9); +} + +.altteulmap-button.altteulmap-accent-solid { + border-color: rgba(173, 104, 62, 0.38); + background-color: var(--altteul-accent); +} + +.altteulmap-button.altteulmap-accent-solid:hover { + background-color: var(--altteul-accent-hover); +} + +.altteulmap-button.altteulmap-accent-ghost { + background-color: #fff8f2; +} + +.altteulmap-chip { + border-radius: 0.9rem; + background-color: rgba(255, 255, 255, 0.96); + transition: + border-color 160ms ease, + background-color 160ms ease, + color 160ms ease, + box-shadow 160ms ease; +} + +.altteulmap-chip:hover { + border-color: var(--altteul-surface-border-strong); + background-color: var(--altteul-surface-fill-hover); +} + +.altteulmap-scope-chip { + border: 1px solid rgba(186, 173, 161, 0.75); + background-color: rgba(255, 255, 255, 0.96); + color: rgb(68, 64, 60); +} + +.altteulmap-scope-input:hover + .altteulmap-scope-chip { + border-color: var(--altteul-surface-border-strong); + background-color: var(--altteul-surface-fill-hover); +} + +.altteulmap-scope-input:checked + .altteulmap-scope-chip { + border-color: #9c5934; + background: linear-gradient(180deg, #cb7c4d 0%, #b96b41 100%); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.18), + 0 0 0 1px rgba(156, 89, 52, 0.14); + color: #fff; +} + +.altteulmap-scope-input:focus-visible + .altteulmap-scope-chip { + outline: none; + box-shadow: 0 0 0 3px var(--altteul-focus-ring); +} + +.altteulmap-scope-input:checked:focus-visible + .altteulmap-scope-chip { + box-shadow: + 0 0 0 3px var(--altteul-focus-ring), + inset 0 1px 0 rgba(255, 255, 255, 0.18), + 0 0 0 1px rgba(156, 89, 52, 0.14); +} + +.altteulmap-badge { + border-radius: 0.9rem; + border: 1px solid rgba(189, 164, 143, 0.46); + background-color: rgba(247, 241, 234, 0.92); +} + +.altteulmap-input { + border: 1px solid rgba(186, 173, 161, 0.75); + border-radius: 1.15rem; + background-color: rgba(255, 255, 255, 0.92); + transition: + border-color 160ms ease, + background-color 160ms ease, + box-shadow 160ms ease; +} + +.altteulmap-input:hover { + border-color: rgba(158, 138, 121, 0.8); +} + +.altteulmap-input:focus { + outline: none; + border-color: rgba(120, 96, 78, 0.88); + background-color: #fff; + box-shadow: 0 0 0 3px rgba(120, 96, 78, 0.12); +} + +.altteulmap-brand-button { + border-radius: 0.95rem; +} + +.altteulmap-button.altteulmap-brand-kakao { + border-color: rgba(82, 66, 0, 0.14); + background-color: #fee500; + color: #291d06; +} + +.altteulmap-button.altteulmap-brand-kakao:hover { + border-color: rgba(82, 66, 0, 0.18); + background-color: #f2da00; +} + +.altteulmap-button.altteulmap-brand-kakao:active { + background-color: #ebd300; +} + +.altteulmap-button.altteulmap-brand-naver { + border-color: rgba(2, 150, 78, 0.28); + background-color: #03c75a; + color: #fff; +} + +.altteulmap-button.altteulmap-brand-naver:hover { + border-color: rgba(2, 150, 78, 0.34); + background-color: #02b351; +} + +.altteulmap-button.altteulmap-brand-naver:active { + background-color: #02a54b; +} diff --git a/apps/admin/src/app/layout.tsx b/apps/admin/src/app/layout.tsx new file mode 100644 index 0000000..91bfd8f --- /dev/null +++ b/apps/admin/src/app/layout.tsx @@ -0,0 +1,23 @@ +import type { Metadata } from "next"; + +import "./globals.css"; + +export const metadata: Metadata = { + title: { + default: "알뜰맵 운영", + template: "%s | 알뜰맵 운영", + }, + description: "알뜰맵 운영 콘솔", +}; + +export default function AdminRootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/apps/admin/src/app/login/page.tsx b/apps/admin/src/app/login/page.tsx new file mode 100644 index 0000000..95184b9 --- /dev/null +++ b/apps/admin/src/app/login/page.tsx @@ -0,0 +1 @@ +export { metadata, default } from "@/app/login/page"; diff --git a/apps/admin/src/app/page.tsx b/apps/admin/src/app/page.tsx new file mode 100644 index 0000000..a13b34f --- /dev/null +++ b/apps/admin/src/app/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default function AdminRootPage() { + redirect("/admin"); +} diff --git a/apps/admin/src/app/signup/page.tsx b/apps/admin/src/app/signup/page.tsx new file mode 100644 index 0000000..be15f0b --- /dev/null +++ b/apps/admin/src/app/signup/page.tsx @@ -0,0 +1 @@ +export { metadata, default } from "@/app/signup/page"; diff --git a/apps/admin/tsconfig.json b/apps/admin/tsconfig.json new file mode 100644 index 0000000..35747ca --- /dev/null +++ b/apps/admin/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [{ "name": "next" }], + "baseUrl": ".", + "paths": { + "@/*": ["../../src/*"], + "@admin/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "../../src/types/**/*.d.ts", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + ".next-dev/types/**/*.ts", + ".next-dev/dev/types/**/*.ts" + ], + "exclude": ["node_modules"] +} diff --git a/apps/admin/wrangler.jsonc b/apps/admin/wrangler.jsonc new file mode 100644 index 0000000..013a7b4 --- /dev/null +++ b/apps/admin/wrangler.jsonc @@ -0,0 +1,22 @@ +{ + "$schema": "../../node_modules/wrangler/config-schema.json", + "main": ".open-next/worker.js", + "name": "altteulmap-admin", + "keep_vars": true, + "compatibility_date": "2026-03-29", + "compatibility_flags": [ + "nodejs_compat", + "global_fetch_strictly_public" + ], + "assets": { + "directory": ".open-next/assets", + "binding": "ASSETS" + }, + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + "service": "altteulmap-admin", + "environment": "production" + } + ] +} diff --git a/data/goodprice/import-meta.json b/data/goodprice/import-meta.json new file mode 100644 index 0000000..f7b33d5 --- /dev/null +++ b/data/goodprice/import-meta.json @@ -0,0 +1,14132 @@ +{ + "source": "https://goodprice.go.kr/bssh/bsshList.do", + "importedAt": "2026-04-01T23:59:20.790Z", + "options": { + "limit": 1000, + "maxPrice": 10000, + "delayMs": 50, + "timeoutMs": 10000, + "includeDetail": true, + "outputPath": "src/features/places/imported-goodprice.json", + "manifestPath": "data/goodprice/import-meta.json" + }, + "selectedCount": 1000, + "regions": [ + { + "name": "서울특별시", + "count": 61 + }, + { + "name": "부산광역시", + "count": 66 + }, + { + "name": "대구광역시", + "count": 66 + }, + { + "name": "인천광역시", + "count": 61 + }, + { + "name": "광주광역시", + "count": 53 + }, + { + "name": "대전광역시", + "count": 65 + }, + { + "name": "울산광역시", + "count": 64 + }, + { + "name": "세종특별자치시", + "count": 32 + }, + { + "name": "경기도", + "count": 63 + }, + { + "name": "강원특별자치도", + "count": 62 + }, + { + "name": "충청북도", + "count": 60 + }, + { + "name": "충청남도", + "count": 58 + }, + { + "name": "전북특별자치도", + "count": 59 + }, + { + "name": "전라남도", + "count": 68 + }, + { + "name": "경상북도", + "count": 50 + }, + { + "name": "경상남도", + "count": 65 + }, + { + "name": "제주특별자치도", + "count": 47 + } + ], + "categories": [ + { + "name": "기타비요식업", + "count": 14 + }, + { + "name": "한식", + "count": 610 + }, + { + "name": "중식", + "count": 74 + }, + { + "name": "베이커리", + "count": 25 + }, + { + "name": "목욕업", + "count": 16 + }, + { + "name": "미용업", + "count": 123 + }, + { + "name": "기타요식업", + "count": 63 + }, + { + "name": "양식", + "count": 14 + }, + { + "name": "이용업", + "count": 26 + }, + { + "name": "일식", + "count": 13 + }, + { + "name": "세탁업", + "count": 22 + } + ], + "items": [ + { + "bsshSn": "11005", + "name": "㈜추억을 파는 극장 (허리우드클래식)", + "categoryName": "기타비요식업", + "address": "서울특별시 종로구 삼일대로 428 (낙원동, 4층)", + "phone": "02-3672-4235", + "representativeMenu": "영화입장권(경로)", + "price": 2000, + "latitude": 37.5727635233966, + "longitude": 126.987902476529, + "pageIndex": 1, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "16045", + "name": "국수전문점", + "categoryName": "한식", + "address": "서울특별시 종로구 종로 246 (종로5가) 1층", + "phone": "02-2275-7443", + "representativeMenu": "잔치국수", + "price": 5000, + "latitude": 37.57083727146669, + "longitude": 127.00511941421306, + "pageIndex": 1, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1643", + "name": "고등어구이정식", + "categoryName": "한식", + "address": "부산광역시 중구 자갈치로 64-2 1층 (남포동)", + "phone": "051-241-6526", + "representativeMenu": "고등어구이", + "price": 9000, + "latitude": 35.0973974519139, + "longitude": 129.03117988708, + "pageIndex": 1, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1640", + "name": "구포밀면", + "categoryName": "한식", + "address": "부산광역시 중구 대청로141 (중앙동)", + "phone": "051-466-6112", + "representativeMenu": "손칼국수", + "price": 7000, + "latitude": 35.1030494036287, + "longitude": 129.035366385151, + "pageIndex": 1, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1644", + "name": "급행", + "categoryName": "한식", + "address": "부산광역시 중구 중앙대로41번길 11-2 (중앙동1가)", + "phone": "051-246-6970", + "representativeMenu": "밀면", + "price": 7000, + "latitude": 35.101031137809, + "longitude": 129.035081756288, + "pageIndex": 1, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1645", + "name": "기와집", + "categoryName": "한식", + "address": "부산광역시 중구 대청로 137번길 7-2 (중앙동3가)", + "phone": "051-961-8851", + "representativeMenu": "자연산송이밥", + "price": 10000, + "latitude": 35.1035458476205, + "longitude": 129.034884173445, + "pageIndex": 1, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "17985", + "name": "갑석이네", + "categoryName": "한식", + "address": "대구광역시 중구 큰장로26안길 66 (대신동) 대신동", + "phone": "053-252-3307", + "representativeMenu": "소갈비살(120g)", + "price": 7500, + "latitude": 35.86637337234426, + "longitude": 128.57702787483387, + "pageIndex": 1, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17983", + "name": "교동생선구이비빔밥", + "categoryName": "한식", + "address": "대구광역시 중구 교동길 48-1 (교동) 교동", + "phone": "053-257-0778", + "representativeMenu": "교동생선구이정식", + "price": 9000, + "latitude": 35.87327790566105, + "longitude": 128.5972021600524, + "pageIndex": 1, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2011", + "name": "낙동강반점", + "categoryName": "중식", + "address": "대구광역시 중구 서성로14길 115 (향촌동)", + "phone": "053-252-7866", + "representativeMenu": "짜장면", + "price": 4000, + "latitude": 35.8732438953014, + "longitude": 128.593902587658, + "pageIndex": 1, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2230", + "name": "골목집", + "categoryName": "한식", + "address": "인천광역시 중구 신포로 32-22 (신포동)", + "phone": "032-765-3149", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 37.4714816686167, + "longitude": 126.625847852578, + "pageIndex": 1, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "13183", + "name": "계림동나주곰탕", + "categoryName": "한식", + "address": "광주광역시 동구 구성로 262-9 (계림동) 1층", + "phone": "062-225-0970", + "representativeMenu": "곰탕", + "price": 10000, + "latitude": 35.15717956808311, + "longitude": 126.91948612900808, + "pageIndex": 1, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "10150", + "name": "24시서울식당", + "categoryName": "한식", + "address": "대전광역시 동구 동서대로 1695번길 53 (용전동)", + "phone": "042-626-1969", + "representativeMenu": "김치찌개 백반", + "price": 7000, + "latitude": 36.3518040236434, + "longitude": 127.436526849885, + "pageIndex": 1, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "10151", + "name": "2900냥식당", + "categoryName": "한식", + "address": "대전광역시 동구 대전로 815번길 5 (정동)", + "phone": "042-256-8686", + "representativeMenu": "선지해장국", + "price": 6000, + "latitude": 36.3322630706726, + "longitude": 127.431757660701, + "pageIndex": 1, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2561", + "name": "강청골순대국밥", + "categoryName": "한식", + "address": "대전광역시 동구 동대전로 229 (가양동)", + "phone": "042-635-8181", + "representativeMenu": "순대국밥", + "price": 8000, + "latitude": 36.3419614517988, + "longitude": 127.447983980001, + "pageIndex": 1, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "14317", + "name": "개천식당", + "categoryName": "한식", + "address": "대전광역시 동구 대전로779번길 41 (원동) 중앙시장 내 위치", + "phone": "042-256-1003", + "representativeMenu": "만둣국", + "price": 9000, + "latitude": 36.32850763956439, + "longitude": 127.43158384633288, + "pageIndex": 1, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2986", + "name": "노르쇠핑", + "categoryName": "베이커리", + "address": "울산광역시 중구 태화로 136 (태화동)", + "phone": "052-267-5873", + "representativeMenu": "단팥빵", + "price": 1500, + "latitude": 35.5531564921762, + "longitude": 129.290936456527, + "pageIndex": 1, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2987", + "name": "뉴강남탕", + "categoryName": "목욕업", + "address": "울산광역시 중구 당산4길 15 (우정동)", + "phone": "052-211-4873", + "representativeMenu": "대인", + "price": 7500, + "latitude": 35.5540646829463, + "longitude": 129.310128409134, + "pageIndex": 1, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "10200", + "name": "6000냥돌솥추어탕", + "categoryName": "한식", + "address": "세종특별자치시 조치원읍 새내10길 61", + "phone": "044-864-2133", + "representativeMenu": "돌솥추어탕", + "price": 7000, + "latitude": 36.6008005980251, + "longitude": 127.301683744225, + "pageIndex": 1, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "19179", + "name": "광진손짜장", + "categoryName": "중식", + "address": "세종특별자치시 조치원읍 새내10길 27 1층", + "phone": "044-865-3657", + "representativeMenu": "짜장면", + "price": 3000, + "latitude": 36.60059049054393, + "longitude": 127.29972619189017, + "pageIndex": 1, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10192", + "name": "꼬막짬뽕", + "categoryName": "중식", + "address": "세종특별자치시 도움3로 105-6 2층", + "phone": "044-865-0655", + "representativeMenu": "유니자장", + "price": 5500, + "latitude": 36.5045900450719, + "longitude": 127.249051704513, + "pageIndex": 1, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "16371", + "name": "(권선)컷트클럽", + "categoryName": "미용업", + "address": "경기도 수원시 권선구 금곡로 46 (금곡동, 호매실역 서희 스타힐스) 나동 103호", + "phone": "031-297-2979", + "representativeMenu": "남성커트", + "price": 9000, + "latitude": 37.27181301997054, + "longitude": 126.938271063389, + "pageIndex": 1, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "3406", + "name": "(장안)컷트클럽", + "categoryName": "미용업", + "address": "경기도 수원시 장안구 조원로 100 .", + "phone": "031-246-6306", + "representativeMenu": "남성커트", + "price": 8000, + "latitude": 37.301600598246, + "longitude": 127.015159002188, + "pageIndex": 1, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "14932", + "name": "24시 전주국밥(콩마루)", + "categoryName": "한식", + "address": "경기도 수원시 영통구 청명남로 46 (영통동) 1층", + "phone": "031-202-3324", + "representativeMenu": "콩나물국밥", + "price": 6000, + "latitude": 37.25344290305785, + "longitude": 127.0765394153501, + "pageIndex": 1, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "16372", + "name": "Bestie(카페베스티)", + "categoryName": "기타요식업", + "address": "경기도 수원시 권선구 권광로 87 (권선동) 2층", + "phone": "031-221-2034", + "representativeMenu": "아메리카노", + "price": 2500, + "latitude": 37.255736139357296, + "longitude": 127.02931494445001, + "pageIndex": 1, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "4336", + "name": "가고파미용실", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 안마산로 216 (퇴계동)", + "phone": "033-262-9488", + "representativeMenu": "커트", + "price": 5000, + "latitude": 37.8495302656251, + "longitude": 127.737647046448, + "pageIndex": 1, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "15775", + "name": "2교시간식시간", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 서원남로 62 (모충동) 1층", + "phone": "043-288-7473", + "representativeMenu": "돈가스", + "price": 9000, + "latitude": 36.622448560220946, + "longitude": 127.48123556595677, + "pageIndex": 1, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4736", + "name": "357짜장 짬뽕", + "categoryName": "중식", + "address": "충청북도 청주시 청원구 내덕로 19 1층(내덕동)", + "phone": "043-225-3357", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 36.6565710804677, + "longitude": 127.482796815455, + "pageIndex": 1, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "17000", + "name": "86카츠", + "categoryName": "양식", + "address": "충청북도 청주시 청원구 상당로302번길 117-1 (내덕동) 1층(내덕동)", + "phone": "-", + "representativeMenu": "돈카츠", + "price": 9500, + "latitude": 36.65322329929266, + "longitude": 127.49392991767124, + "pageIndex": 1, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "16062", + "name": "9.9추어탕", + "categoryName": "한식", + "address": "충청북도 청주시 상당구 남사로140번길 33 (남문로1가) 1층", + "phone": "043-256-9900", + "representativeMenu": "추어탕", + "price": 10000, + "latitude": 36.63060364132639, + "longitude": 127.49034759503, + "pageIndex": 1, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4909", + "name": "9000냥프로헤어", + "categoryName": "미용업", + "address": "충청남도 천안시 동남구 충무로 412-27 2층(영성동)", + "phone": "-", + "representativeMenu": "커트", + "price": 9000, + "latitude": 36.7971238482422, + "longitude": 127.151205811449, + "pageIndex": 1, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4935", + "name": "가마솥 선지국밥", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 사직로 2-2 1층(사직동)", + "phone": "041-574-5665", + "representativeMenu": "선지국밥", + "price": 5000, + "latitude": 36.8003866595724, + "longitude": 127.149801708648, + "pageIndex": 1, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "11752", + "name": "가화손만두 옛날통닭", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 큰재빼기길 28 (오룡동)", + "phone": "041-558-0863", + "representativeMenu": "옛날통닭", + "price": 8000, + "latitude": 36.8039869569013, + "longitude": 127.149159570599, + "pageIndex": 1, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "16879", + "name": "강짬뽕", + "categoryName": "중식", + "address": "충청남도 천안시 동남구 신부12길 12 (신부동) 1층", + "phone": "0507-1357-6142", + "representativeMenu": "자장면", + "price": 6000, + "latitude": 36.81807192926583, + "longitude": 127.15946515907602, + "pageIndex": 1, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "5413", + "name": "나눔공정카페", + "categoryName": "기타요식업", + "address": "전북특별자치도 전주시 완산구 전룡4길 8 1층", + "phone": "063-278-9799", + "representativeMenu": "아메리카노", + "price": 1800, + "latitude": 35.8258068254203, + "longitude": 127.119654841458, + "pageIndex": 1, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "11329", + "name": "경남식당", + "categoryName": "중식", + "address": "전라남도 목포시 산대로2번길 3-2 (산정동)", + "phone": "061-272-8111", + "representativeMenu": "자장면", + "price": 5000, + "latitude": 34.8024474268616, + "longitude": 126.391838705576, + "pageIndex": 1, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "15142", + "name": "경성회관", + "categoryName": "한식", + "address": "전라남도 목포시 용당로 54 (산정동) 1층", + "phone": "061-242-5993", + "representativeMenu": "청국장 백반", + "price": 10000, + "latitude": 34.792331194754645, + "longitude": 126.4023009728321, + "pageIndex": 1, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "15158", + "name": "고향삼겹살", + "categoryName": "한식", + "address": "전라남도 목포시 영산로 691 (석현동) 고향삼겹살", + "phone": "061-281-3931", + "representativeMenu": "청국장", + "price": 9000, + "latitude": 34.82641754948634, + "longitude": 126.42137100869118, + "pageIndex": 1, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6367", + "name": "45번길국수", + "categoryName": "한식", + "address": "경상북도 포항시 남구 대이로45번길 12-4 45번길국수", + "phone": "-", + "representativeMenu": "잔치국수", + "price": 6000, + "latitude": 36.019222777464, + "longitude": 129.340576077049, + "pageIndex": 1, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6384", + "name": "감자바위", + "categoryName": "한식", + "address": "경상북도 포항시 남구 연일읍 유강길9번길 32 감자바위", + "phone": "054-272-8877", + "representativeMenu": "비빔밥", + "price": 8000, + "latitude": 36.0033909011122, + "longitude": 129.316527350566, + "pageIndex": 1, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "13952", + "name": "119찹쌀꽈배기", + "categoryName": "기타요식업", + "address": "경상남도 창원시 마산합포구 해안대로 11 (해운동) 1층 119호(해운동, 이화월드프라자)", + "phone": "055-245-7939", + "representativeMenu": "꽈배기", + "price": 500, + "latitude": 35.180646134171674, + "longitude": 128.560237476249, + "pageIndex": 1, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "19285", + "name": "강남추어탕", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 중원로85번길 18 (화천동) 강남추어탕", + "phone": "055-541-5033", + "representativeMenu": "추어탕", + "price": 9000, + "latitude": 35.15213845076089, + "longitude": 128.65821162206046, + "pageIndex": 1, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "17141", + "name": "고운프리티", + "categoryName": "기타요식업", + "address": "경상남도 창원시 진해구 돌리로9번길 10-1 (석동) 1층", + "phone": "0507-1428-2177", + "representativeMenu": "아메리카노 HOT(ICE +500원)", + "price": 1500, + "latitude": 35.159411115795905, + "longitude": 128.69968744654085, + "pageIndex": 1, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "10627", + "name": "24시국수회관", + "categoryName": "한식", + "address": "제주특별자치도 제주시 남성로 122-1", + "phone": "064-702-1162", + "representativeMenu": "순대국밥", + "price": 8000, + "latitude": 33.5087686026345, + "longitude": 126.518427788905, + "pageIndex": 1, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7012", + "name": "JJ노블휘트니스클럽", + "categoryName": "기타비요식업", + "address": "제주특별자치도 제주시 성신로 1길 34 3층(연동, 신동부삼무3차아파트)", + "phone": "064-743-1515", + "representativeMenu": "1일", + "price": 10000, + "latitude": 33.4922271525915, + "longitude": 126.48897408179, + "pageIndex": 1, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7203", + "name": "가람", + "categoryName": "한식", + "address": "제주특별자치도 제주시 복지로북길 4 1층", + "phone": "064-756-0012", + "representativeMenu": "추어탕", + "price": 10000, + "latitude": 33.4888491368419, + "longitude": 126.520646841034, + "pageIndex": 1, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "16639", + "name": "남도식당", + "categoryName": "중식", + "address": "서울특별시 종로구 종로 374-1 (숭인동) 1층", + "phone": "02-2234-5544", + "representativeMenu": "짜장면", + "price": 3500, + "latitude": 37.5737068376897, + "longitude": 127.01922365532883, + "pageIndex": 2, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "11579", + "name": "눈나무집", + "categoryName": "한식", + "address": "서울특별시 종로구 삼청로 136-1 1층", + "phone": "02-739-6742", + "representativeMenu": "김치말이국수", + "price": 6500, + "latitude": 37.5874798577018, + "longitude": 126.981887109774, + "pageIndex": 2, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "946", + "name": "돈까스보라", + "categoryName": "양식", + "address": "서울특별시 종로구 대학로5길 5 (연건동)", + "phone": "02-741-3455", + "representativeMenu": "수제 돈까스", + "price": 7000, + "latitude": 37.5781982996716, + "longitude": 127.001695852612, + "pageIndex": 2, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "16864", + "name": "동대문 한국통닭", + "categoryName": "한식", + "address": "서울특별시 종로구 종로 304 (창신동) 1층", + "phone": "02-747-2443", + "representativeMenu": "통닭 1마리", + "price": 4500, + "latitude": 37.571667373066134, + "longitude": 127.01153873736409, + "pageIndex": 2, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "10018", + "name": "깡통골목할매 유부전골 본점", + "categoryName": "한식", + "address": "부산광역시 중구 부평3길 29 (부평동1가)", + "phone": "051-245-1878", + "representativeMenu": "유부전골", + "price": 6800, + "latitude": 35.1020375328885, + "longitude": 129.027061206734, + "pageIndex": 2, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "15032", + "name": "꽃가람 남포직영점", + "categoryName": "한식", + "address": "부산광역시 중구 비프광장로 3 (부평동2가) 꽃가람 남포직영점", + "phone": "0507-1317-4600", + "representativeMenu": "고추장돼지불고기정식", + "price": 9500, + "latitude": 35.09835574338491, + "longitude": 129.02574087593763, + "pageIndex": 2, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1646", + "name": "남선식당", + "categoryName": "한식", + "address": "부산광역시 중구 충장대로4번길6 (중앙동4가)", + "phone": "051-469-3828", + "representativeMenu": "순두부찌개", + "price": 7000, + "latitude": 35.1046241396056, + "longitude": 129.03680238801, + "pageIndex": 2, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1647", + "name": "남원원조추어탕", + "categoryName": "한식", + "address": "부산광역시 중구 해관로 37-1 (중앙동)", + "phone": "051-246-5636", + "representativeMenu": "추어탕", + "price": 9000, + "latitude": 35.1022868055746, + "longitude": 129.035469797726, + "pageIndex": 2, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "10075", + "name": "누렁소식당", + "categoryName": "한식", + "address": "대구광역시 중구 국채보상로131길 24 (동인동2가) 누렁소식당", + "phone": "053-422-8327", + "representativeMenu": "한식뷔페", + "price": 7000, + "latitude": 35.87085460440117, + "longitude": 128.60218193488083, + "pageIndex": 2, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "15357", + "name": "덕종국수", + "categoryName": "한식", + "address": "대구광역시 중구 약령길 53 (수동) 1층", + "phone": "053-353-7971", + "representativeMenu": "비빔밥", + "price": 8000, + "latitude": 35.86868347395729, + "longitude": 128.5892390743561, + "pageIndex": 2, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17975", + "name": "도리집", + "categoryName": "한식", + "address": "대구광역시 중구 달구벌대로 2109-34 (동성로3가) 1층", + "phone": "053-253-3062", + "representativeMenu": "까만도리밥(덮밥)", + "price": 4900, + "latitude": 35.86686640891391, + "longitude": 128.59398585931365, + "pageIndex": 2, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "13117", + "name": "김윤식의 착한국수, 솥뚜껑삼겹살", + "categoryName": "한식", + "address": "인천광역시 중구 운중로 8 (운남동) 2층", + "phone": "032-255-9510", + "representativeMenu": "국수+떡갈비", + "price": 8000, + "latitude": 37.49336872061384, + "longitude": 126.53441990901412, + "pageIndex": 2, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "14133", + "name": "다정가마솥순두부", + "categoryName": "한식", + "address": "인천광역시 중구 운중로 140-4 (중산동) 1층", + "phone": "0507-1320-8254", + "representativeMenu": "초당(백)순두부", + "price": 9000, + "latitude": 37.49373607272692, + "longitude": 126.54783653076029, + "pageIndex": 2, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2223", + "name": "도래순", + "categoryName": "중식", + "address": "인천광역시 중구 신포로15번길 68-1 1층(해안동2가)", + "phone": "032-766-3907", + "representativeMenu": "짜장면", + "price": 7000, + "latitude": 37.4724317342844, + "longitude": 126.621168040258, + "pageIndex": 2, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "14638", + "name": "그냥집밥", + "categoryName": "한식", + "address": "광주광역시 동구 서석로85번길 8-3 (대의동) 지하", + "phone": "062-223-2624", + "representativeMenu": "한식뷔페", + "price": 8000, + "latitude": 35.15002860036522, + "longitude": 126.9201698199041, + "pageIndex": 2, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "13743", + "name": "담양국수", + "categoryName": "한식", + "address": "광주광역시 동구 증심사길30번길 28-3 (운림동) 1층", + "phone": "062-226-1174", + "representativeMenu": "멸치국수", + "price": 6000, + "latitude": 35.132817322727675, + "longitude": 126.95774143856892, + "pageIndex": 2, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2258", + "name": "대왕김밥(대인동)", + "categoryName": "한식", + "address": "광주광역시 동구 구성로204번길 15-6 1층(대인동)", + "phone": "062-529-3363", + "representativeMenu": "김밥", + "price": 3500, + "latitude": 35.1525929911284, + "longitude": 126.91613437943, + "pageIndex": 2, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2562", + "name": "경성화로", + "categoryName": "한식", + "address": "대전광역시 동구 동서대로 1748번길 152 (가양동)", + "phone": "042-632-1087", + "representativeMenu": "삼겹살(150g)", + "price": 7000, + "latitude": 36.3488134723206, + "longitude": 127.450323327516, + "pageIndex": 2, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2527", + "name": "곽은아미용실", + "categoryName": "미용업", + "address": "대전광역시 동구 새울로 100 (용운동)", + "phone": "042-272-4478", + "representativeMenu": "커트(여성 일반)", + "price": 10000, + "latitude": 36.3271167002537, + "longitude": 127.461826711325, + "pageIndex": 2, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "18955", + "name": "국민추어탕", + "categoryName": "한식", + "address": "대전광역시 동구 옥천로 52-2 (신흥동) 1층", + "phone": "042-282-1585", + "representativeMenu": "추어탕+돌솥밥", + "price": 9000, + "latitude": 36.31971954491453, + "longitude": 127.44667251762813, + "pageIndex": 2, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2544", + "name": "김화칼국수", + "categoryName": "한식", + "address": "대전광역시 동구 중앙로 203번길 28 (중동)", + "phone": "042-221-7594", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 36.332041975521, + "longitude": 127.431018789434, + "pageIndex": 2, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "11675", + "name": "다원식당", + "categoryName": "한식", + "address": "울산광역시 중구 옥교6길 19 (옥교동)", + "phone": "052-211-4068", + "representativeMenu": "비빔밥", + "price": 8000, + "latitude": 35.5541946842973, + "longitude": 129.327531124694, + "pageIndex": 2, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "16555", + "name": "대가국밥", + "categoryName": "한식", + "address": "울산광역시 중구 화진길 11-4 (태화동) 1층", + "phone": "052-212-2988", + "representativeMenu": "돼지국밥", + "price": 9000, + "latitude": 35.55523350485414, + "longitude": 129.3085469323432, + "pageIndex": 2, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "19374", + "name": "돈대박 복산점", + "categoryName": "한식", + "address": "울산광역시 중구 계변로 92-1 (복산동) 돈대박복산점", + "phone": "052-282-3313", + "representativeMenu": "대패삼겹살(100g)", + "price": 3500, + "latitude": 35.563484990060665, + "longitude": 129.3294908791611, + "pageIndex": 2, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "10182", + "name": "농부의한우", + "categoryName": "한식", + "address": "세종특별자치시 금남면 세종로 470 1층", + "phone": "044-866-1181", + "representativeMenu": "국밥", + "price": 7000, + "latitude": 36.4608221175864, + "longitude": 127.27888584, + "pageIndex": 2, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10167", + "name": "대신식당", + "categoryName": "한식", + "address": "세종특별자치시 연서면 당산로 349", + "phone": "044-867-7127", + "representativeMenu": "김치찌개", + "price": 8000, + "latitude": 36.562836822603, + "longitude": 127.282636054749, + "pageIndex": 2, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10185", + "name": "도담얼큰이 칼국수", + "categoryName": "한식", + "address": "세종특별자치시 보듬4로 9 1동 2층 29호(도담동 카림애비뉴)", + "phone": "044-867-0309", + "representativeMenu": "칼국수", + "price": 5900, + "latitude": 36.5131131389938, + "longitude": 127.260704077872, + "pageIndex": 2, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "17109", + "name": "가치있는밥상", + "categoryName": "한식", + "address": "경기도 수원시 영통구 월드컵로179번길 14-2 (원천동) 1층", + "phone": "031-221-5581", + "representativeMenu": "김치찌개", + "price": 8000, + "latitude": 37.27819928647353, + "longitude": 127.0453792190971, + "pageIndex": 2, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "13884", + "name": "강릉장칼국수", + "categoryName": "한식", + "address": "경기도 수원시 팔달구 수원천로 314 (남수동) 1층", + "phone": "031-241-7959", + "representativeMenu": "장칼국수", + "price": 8000, + "latitude": 37.28145528956532, + "longitude": 127.01857508520432, + "pageIndex": 2, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "4338", + "name": "개성시대미용실", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 춘천로68번길 10 (효자동)", + "phone": "033-256-9304", + "representativeMenu": "커트", + "price": 8000, + "latitude": 37.8700600426461, + "longitude": 127.725474230628, + "pageIndex": 2, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4375", + "name": "골목손두부", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 공지로 439번길 16-4 (근화동)", + "phone": "033-256-6259", + "representativeMenu": "순두부", + "price": 8000, + "latitude": 37.8726486894719, + "longitude": 127.719572641824, + "pageIndex": 2, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4368", + "name": "곱돌생삼겹살", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 백령로138번길 55 (효자동)", + "phone": "033-255-4241", + "representativeMenu": "생삼겹살(캐나다)", + "price": 10000, + "latitude": 37.8731172068198, + "longitude": 127.745471505129, + "pageIndex": 2, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4679", + "name": "가든미용실", + "categoryName": "미용업", + "address": "충청북도 청주시 상당구 청남로2197번길 12 (석교동)", + "phone": "043-223-8097", + "representativeMenu": "커트", + "price": 6000, + "latitude": 36.6278271128724, + "longitude": 127.490164264559, + "pageIndex": 2, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15768", + "name": "가이오청년밥상", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 사직대로164번길 27 (사창동) 1층", + "phone": "043-271-1009", + "representativeMenu": "김치찌개", + "price": 3000, + "latitude": 36.6339341047818, + "longitude": 127.46555600261848, + "pageIndex": 2, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4742", + "name": "감촌삼겹살", + "categoryName": "한식", + "address": "충청북도 청주시 청원구 안덕벌로39번길 43-1 (내덕동)", + "phone": "043-211-2472", + "representativeMenu": "생삼겹살(180g)", + "price": 10000, + "latitude": 36.6591837012203, + "longitude": 127.491676959315, + "pageIndex": 2, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4708", + "name": "개미돈까스", + "categoryName": "한식", + "address": "충청북도 청주시 상당구 사직대로361번길 54-11 (북문로2가)", + "phone": "043-223-7456", + "representativeMenu": "돈가스", + "price": 8000, + "latitude": 36.6394461100471, + "longitude": 127.487984622146, + "pageIndex": 2, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "11028", + "name": "겨자씨까페", + "categoryName": "기타요식업", + "address": "충청남도 천안시 동남구 사직로 10-6 (사직동)", + "phone": "041-555-0738", + "representativeMenu": "아메리카노", + "price": 2000, + "latitude": 36.8006603819216, + "longitude": 127.150044275946, + "pageIndex": 2, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "11409", + "name": "경북당", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 병천면 아우내장터2길 46", + "phone": "041-561-4133", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 36.7626220651971, + "longitude": 127.297917324637, + "pageIndex": 2, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "16877", + "name": "경자네칼국수", + "categoryName": "한식", + "address": "충청남도 천안시 서북구 월봉5길 7 (쌍용동) 102호", + "phone": "041-578-7676", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 36.790593294551826, + "longitude": 127.11713150353609, + "pageIndex": 2, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "13651", + "name": "고운육회방앗간", + "categoryName": "한식", + "address": "충청남도 천안시 서북구 백석3로 25 (백석동) 1층", + "phone": "041-551-8890", + "representativeMenu": "한우육회비빔밥", + "price": 10000, + "latitude": 36.821819579628574, + "longitude": 127.11424205567585, + "pageIndex": 2, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "13856", + "name": "돈초야", + "categoryName": "양식", + "address": "전북특별자치도 전주시 완산구 송정로 43 (효자동1가)", + "phone": "063-222-1983", + "representativeMenu": "돈가스 2장", + "price": 5900, + "latitude": 35.802405949453835, + "longitude": 127.12153109657699, + "pageIndex": 2, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5435", + "name": "동래분식", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 풍남문2길 39", + "phone": "063-288-4607", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 35.8125817693067, + "longitude": 127.147152512312, + "pageIndex": 2, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "16971", + "name": "곰순이식당", + "categoryName": "한식", + "address": "전라남도 목포시 용당로216번길 21 (용당동) 곰순이식당", + "phone": "061-279-4811", + "representativeMenu": "백반", + "price": 8000, + "latitude": 34.80645255778166, + "longitude": 126.40056637892347, + "pageIndex": 2, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5616", + "name": "광양숯불갈비", + "categoryName": "한식", + "address": "전라남도 목포시 노적봉길 21 (죽동)", + "phone": "061-242-5282", + "representativeMenu": "산채비빔밥", + "price": 7000, + "latitude": 34.7896123625075, + "longitude": 126.383187062276, + "pageIndex": 2, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "16974", + "name": "김밥나라(중앙병원점)", + "categoryName": "한식", + "address": "전라남도 목포시 텃골로13번길 32 (석현동) 상가동 104호", + "phone": "061-282-5888", + "representativeMenu": "김치찌개", + "price": 6000, + "latitude": 34.82007683022403, + "longitude": 126.41976655741243, + "pageIndex": 2, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "18284", + "name": "김밥마트(용당점)", + "categoryName": "한식", + "address": "전라남도 목포시 영산로 260 (용당동) 김밥마트", + "phone": "061-272-4288", + "representativeMenu": "김밥", + "price": 3000, + "latitude": 34.803356100920574, + "longitude": 126.3947727037926, + "pageIndex": 2, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6368", + "name": "강가박가92", + "categoryName": "한식", + "address": "경상북도 포항시 남구 중앙로134번길 3 강가박가92", + "phone": "054-281-7838", + "representativeMenu": "잔치국수", + "price": 5000, + "latitude": 36.025206265541, + "longitude": 129.368486584177, + "pageIndex": 2, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "14874", + "name": "강변식당", + "categoryName": "한식", + "address": "경상북도 포항시 북구 죽도로40번길 44-2 (죽도동) 강변식당", + "phone": "054-272-8328", + "representativeMenu": "정식", + "price": 8000, + "latitude": 36.02997134258029, + "longitude": 129.36267202809643, + "pageIndex": 2, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "18324", + "name": "개나리분식", + "categoryName": "한식", + "address": "경상북도 포항시 남구 정몽주로879번길 8 (청림동) 개나리분식", + "phone": "054-292-9136", + "representativeMenu": "정식", + "price": 7000, + "latitude": 35.99320750163037, + "longitude": 129.40410586349998, + "pageIndex": 2, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6380", + "name": "거성갈비살", + "categoryName": "한식", + "address": "경상북도 포항시 남구 대해로 134 거성갈비살", + "phone": "-", + "representativeMenu": "정식", + "price": 9000, + "latitude": 36.0193468713474, + "longitude": 129.365579165755, + "pageIndex": 2, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6792", + "name": "고인돌", + "categoryName": "한식", + "address": "경상남도 창원시 마산회원구 합성옛길 309 (합성동)", + "phone": "055-297-9070", + "representativeMenu": "흑돼지삼겹살", + "price": 10000, + "latitude": 35.2443031085876, + "longitude": 128.588528120564, + "pageIndex": 2, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "6929", + "name": "곰내커피", + "categoryName": "기타요식업", + "address": "경상남도 창원시 진해구 웅천중로56번길 12-1 1,2층(성내동)", + "phone": "055-544-3040", + "representativeMenu": "아메리카노(hot)", + "price": 2500, + "latitude": 35.1113597022546, + "longitude": 128.750465382937, + "pageIndex": 2, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "10635", + "name": "감나무집", + "categoryName": "한식", + "address": "제주특별자치도 제주시 오남로 12 1층", + "phone": "064-722-8292", + "representativeMenu": "흑돼지두루치기정식", + "price": 10000, + "latitude": 33.4941885992074, + "longitude": 126.519518313018, + "pageIndex": 2, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7114", + "name": "강김밥집", + "categoryName": "한식", + "address": "제주특별자치도 제주시 애월읍 하귀9길 2 1층 114호", + "phone": "064-711-5096", + "representativeMenu": "강김밥", + "price": 2900, + "latitude": 33.4864182898895, + "longitude": 126.414565502113, + "pageIndex": 2, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7202", + "name": "고기서맛나(하몽이네)", + "categoryName": "한식", + "address": "제주특별자치도 제주시 중앙로21길 4 1층(이도일동)", + "phone": "0507-1322-1011", + "representativeMenu": "제주산 생뒷고기", + "price": 5000, + "latitude": 33.5078471486649, + "longitude": 126.524747667261, + "pageIndex": 2, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "18196", + "name": "동문식당", + "categoryName": "한식", + "address": "서울특별시 종로구 종로 300-1 (창신동) 1층", + "phone": "-", + "representativeMenu": "콩나물비빔밥", + "price": 5000, + "latitude": 37.57159249625645, + "longitude": 127.01124193717713, + "pageIndex": 3, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "19249", + "name": "둘리분식", + "categoryName": "한식", + "address": "서울특별시 종로구 창경궁로34길 15 (혜화동) 1층", + "phone": "02-744-8626", + "representativeMenu": "된장찌개", + "price": 4000, + "latitude": 37.584324897003846, + "longitude": 127.0011787060946, + "pageIndex": 3, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "19006", + "name": "남포설렁탕", + "categoryName": "한식", + "address": "부산광역시 중구 구덕로34번길 5 (남포동2가) 1층", + "phone": "051-255-2263", + "representativeMenu": "설렁탕", + "price": 10000, + "latitude": 35.098466691867394, + "longitude": 129.03204192140768, + "pageIndex": 3, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1648", + "name": "남포수제비", + "categoryName": "한식", + "address": "부산광역시 중구 광복로 49번길 7-1 (창선동)", + "phone": "051-245-6821", + "representativeMenu": "충무김밥", + "price": 6500, + "latitude": 35.0998830313842, + "longitude": 129.030510175225, + "pageIndex": 3, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "10020", + "name": "남포이발컷트", + "categoryName": "이용업", + "address": "부산광역시 중구 중구로 2 2층(남포동5가)", + "phone": "-", + "representativeMenu": "커트", + "price": 6000, + "latitude": 35.0979757736079, + "longitude": 129.028282683866, + "pageIndex": 3, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1649", + "name": "단골식당", + "categoryName": "한식", + "address": "부산광역시 중구 동광길 193 (영주1동)", + "phone": "051-441-8540", + "representativeMenu": "묵은지김치전골", + "price": 8000, + "latitude": 35.1109965770514, + "longitude": 129.037200736899, + "pageIndex": 3, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "14626", + "name": "동인1번지", + "categoryName": "한식", + "address": "대구광역시 중구 동덕로36길 78 (동인동4가) 동인1번지", + "phone": "053-257-6244", + "representativeMenu": "김치돼지찌개", + "price": 8000, + "latitude": 35.869513126099164, + "longitude": 128.6084875504821, + "pageIndex": 3, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17986", + "name": "동인식당", + "categoryName": "한식", + "address": "대구광역시 중구 동덕로36길 130 (동인동4가) 동인동", + "phone": "053-423-9961", + "representativeMenu": "정식", + "price": 7000, + "latitude": 35.869224595714726, + "longitude": 128.6114387547134, + "pageIndex": 3, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "14949", + "name": "만리장성", + "categoryName": "중식", + "address": "대구광역시 중구 공평로 95 (동문동) .", + "phone": "053-425-2412", + "representativeMenu": "짜장면", + "price": 2000, + "latitude": 35.87252613112093, + "longitude": 128.60076336835849, + "pageIndex": 3, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2012", + "name": "만리향반점", + "categoryName": "중식", + "address": "대구광역시 중구 명덕로35길 112 (남산)", + "phone": "053-255-9380", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 35.8614534554198, + "longitude": 128.590364961693, + "pageIndex": 3, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "16953", + "name": "마카롱데이즈", + "categoryName": "베이커리", + "address": "인천광역시 중구 우현로35번길 13-2 (신생동) 1층", + "phone": "0507-1485-5016", + "representativeMenu": "다쿠마롱(10개 이상 주문시)", + "price": 1700, + "latitude": 37.47044194467634, + "longitude": 126.62613140213853, + "pageIndex": 3, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "17145", + "name": "명동식당", + "categoryName": "한식", + "address": "인천광역시 중구 답동로30번길 25 (경동) 1층", + "phone": "032-765-5668", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 37.47170701109189, + "longitude": 126.63290688540795, + "pageIndex": 3, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2259", + "name": "대왕김밥(학동)", + "categoryName": "한식", + "address": "광주광역시 동구 남문로 671 (학동)", + "phone": "062-223-0868", + "representativeMenu": "김밥", + "price": 3000, + "latitude": 35.1305985718859, + "longitude": 126.929101660948, + "pageIndex": 3, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "15363", + "name": "대접", + "categoryName": "한식", + "address": "광주광역시 동구 서석로85번길 8-7 (대의동) 1층", + "phone": "062-651-9998", + "representativeMenu": "잔치국수/비빔국수", + "price": 4500, + "latitude": 35.150050907279855, + "longitude": 126.9203188050476, + "pageIndex": 3, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "14224", + "name": "덕산오징어보쌈", + "categoryName": "한식", + "address": "광주광역시 동구 남문로622번길 20 (소태동) 1층", + "phone": "062-223-5868", + "representativeMenu": "점심 백반", + "price": 8000, + "latitude": 35.127077487064696, + "longitude": 126.93344224312833, + "pageIndex": 3, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "14430", + "name": "넘치는국수한그릇", + "categoryName": "한식", + "address": "대전광역시 동구 신기로 123-15 (가오동) 가오초등학교 앞", + "phone": "042-284-7888", + "representativeMenu": "잔치국수", + "price": 5000, + "latitude": 36.31171842237039, + "longitude": 127.458444535411, + "pageIndex": 3, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2528", + "name": "뉴모델이용원", + "categoryName": "이용업", + "address": "대전광역시 동구 계족로 103 (신흥동)", + "phone": "042-282-1933", + "representativeMenu": "이용료", + "price": 7000, + "latitude": 36.3235977678715, + "longitude": 127.445302754, + "pageIndex": 3, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2529", + "name": "대광이용원", + "categoryName": "이용업", + "address": "대전광역시 동구 계족로 73-2 (신흥동)", + "phone": "042-282-7720", + "representativeMenu": "이용료", + "price": 7000, + "latitude": 36.3212528089861, + "longitude": 127.443947726738, + "pageIndex": 3, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "14196", + "name": "떡볶이가좋다", + "categoryName": "한식", + "address": "울산광역시 중구 중앙시장길 2-7 (옥교동) 1층", + "phone": "052-246-4702", + "representativeMenu": "즉석떡볶이", + "price": 3000, + "latitude": 35.55501926169491, + "longitude": 129.32205281017949, + "pageIndex": 3, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "16674", + "name": "마마칼국수", + "categoryName": "한식", + "address": "울산광역시 중구 남외1길 56 (남외동) 1층", + "phone": "052-291-1159", + "representativeMenu": "돌솥비빔밥", + "price": 7000, + "latitude": 35.56704089419386, + "longitude": 129.35042603603065, + "pageIndex": 3, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2999", + "name": "만수원숯불갈비", + "categoryName": "한식", + "address": "울산광역시 중구 해오름16길 2 (남외동)", + "phone": "052-297-4327", + "representativeMenu": "쌈정식", + "price": 8000, + "latitude": 35.5607915680552, + "longitude": 129.342737348461, + "pageIndex": 3, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "19370", + "name": "모닝베이커리", + "categoryName": "베이커리", + "address": "울산광역시 중구 구교6길 63 (반구동) 모닝베이커리", + "phone": "052-294-6636", + "representativeMenu": "팥빵", + "price": 1000, + "latitude": 35.565210010138046, + "longitude": 129.34129671811115, + "pageIndex": 3, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "15902", + "name": "만리장성", + "categoryName": "중식", + "address": "세종특별자치시 조치원읍 충현로 91 101호 만리장성", + "phone": "044-868-5400", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 36.59668528394338, + "longitude": 127.29539473219728, + "pageIndex": 3, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10190", + "name": "맛있는 수다", + "categoryName": "베이커리", + "address": "세종특별자치시 조치원읍 새내10길 41 맛있는 수다", + "phone": "044-867-4967", + "representativeMenu": "고기만두(8개)", + "price": 5000, + "latitude": 36.6006451799859, + "longitude": 127.300531090282, + "pageIndex": 3, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "17111", + "name": "경희당구클럽", + "categoryName": "기타비요식업", + "address": "경기도 수원시 영통구 덕영대로 1707 (영통동) 4층", + "phone": "010-5631-2323", + "representativeMenu": "10분", + "price": 1000, + "latitude": 37.24799357407884, + "longitude": 127.07820639116257, + "pageIndex": 3, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "15567", + "name": "고운돈 79냉삼집", + "categoryName": "한식", + "address": "경기도 수원시 영통구 반달로 50 (영통동) 1층", + "phone": "031-205-5678", + "representativeMenu": "냉삼겹(150g)", + "price": 7900, + "latitude": 37.249825831037455, + "longitude": 127.07722668063614, + "pageIndex": 3, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "14640", + "name": "광명수산 수원역점", + "categoryName": "일식", + "address": "경기도 수원시 팔달구 갓매산로 38 (매산로2가) 1층", + "phone": "031-244-8899", + "representativeMenu": "활어회덮밥", + "price": 8000, + "latitude": 37.267869938538816, + "longitude": 127.00558487313573, + "pageIndex": 3, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "16650", + "name": "광명세탁", + "categoryName": "세탁업", + "address": "강원특별자치도 춘천시 삭주로 27 (교동)", + "phone": "033-252-6271", + "representativeMenu": "양복상의", + "price": 6000, + "latitude": 37.88154669495617, + "longitude": 127.73468581005257, + "pageIndex": 3, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4376", + "name": "광치해장국", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 소양로 108 (소양동)", + "phone": "033-253-1100", + "representativeMenu": "선지해장국", + "price": 6000, + "latitude": 37.8932252439588, + "longitude": 127.726036660624, + "pageIndex": 3, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4358", + "name": "국수닭", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 남춘로 36번길 48 (퇴계동)", + "phone": "033-262-6399", + "representativeMenu": "닭국수", + "price": 6500, + "latitude": 37.8615806593812, + "longitude": 127.735535866785, + "pageIndex": 3, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4359", + "name": "그랑블루바다향기칼국수", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 동내면 고은길 168 2층", + "phone": "033-263-2801", + "representativeMenu": "해물칼국수", + "price": 9000, + "latitude": 37.8383207877726, + "longitude": 127.783588716154, + "pageIndex": 3, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "14756", + "name": "고가네왕돈까스", + "categoryName": "한식", + "address": "충청북도 청주시 청원구 중앙로 135-2 (우암동)", + "phone": "043-255-1855", + "representativeMenu": "왕돈까스", + "price": 8000, + "latitude": 36.64938771590744, + "longitude": 127.48776069611203, + "pageIndex": 3, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15786", + "name": "고가네행복밥상", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 수곡로113번길 11 (수곡동) 1층", + "phone": "043-285-6878", + "representativeMenu": "동태찌개", + "price": 8000, + "latitude": 36.6203686371318, + "longitude": 127.47013521623991, + "pageIndex": 3, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15852", + "name": "고드미손두부", + "categoryName": "한식", + "address": "충청북도 청주시 흥덕구 서현로 12-31 (가경동)", + "phone": "043-284-0944", + "representativeMenu": "국산콩순두부", + "price": 9000, + "latitude": 36.6159289050475, + "longitude": 127.42453456395413, + "pageIndex": 3, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4724", + "name": "고려미용실", + "categoryName": "미용업", + "address": "충청북도 청주시 서원구 모충로 98-1 (모충동)", + "phone": "043-274-6207", + "representativeMenu": "커트", + "price": 8000, + "latitude": 36.6277882283516, + "longitude": 127.474142701453, + "pageIndex": 3, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "5410", + "name": "또또분식", + "categoryName": "한식", + "address": "전북특별자치도 전주시 덕진구 삼송3길 14", + "phone": "-", + "representativeMenu": "떡볶이", + "price": 4500, + "latitude": 35.8424270036272, + "longitude": 127.135459918463, + "pageIndex": 3, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "13854", + "name": "또또커피", + "categoryName": "기타요식업", + "address": "전북특별자치도 전주시 덕진구 삼송3길 11-1 (금암동) 1층", + "phone": "-", + "representativeMenu": "아메리카노", + "price": 2000, + "latitude": 35.842528730789724, + "longitude": 127.13507910263799, + "pageIndex": 3, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5436", + "name": "또와분식", + "categoryName": "베이커리", + "address": "전북특별자치도 전주시 완산구 태평5길 41-5", + "phone": "063-273-2923", + "representativeMenu": "찹쌀도너츠(3개)", + "price": 2000, + "latitude": 35.8241917473874, + "longitude": 127.143168338244, + "pageIndex": 3, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "15156", + "name": "까망헤어필", + "categoryName": "미용업", + "address": "전라남도 목포시 산정로79번길 15-1 (산정동) 까망헤어필", + "phone": "061-277-3173", + "representativeMenu": "커트", + "price": 10000, + "latitude": 34.795406737074515, + "longitude": 126.39113623218255, + "pageIndex": 3, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "10497", + "name": "남도먹거리방", + "categoryName": "한식", + "address": "전라남도 목포시 삼일로13번길 2-1 (남교동)", + "phone": "061-242-9895", + "representativeMenu": "순대", + "price": 8000, + "latitude": 34.7936753216335, + "longitude": 126.383348462671, + "pageIndex": 3, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "16973", + "name": "낭만맛집", + "categoryName": "한식", + "address": "전남 목포시 만호로 11-1 (금동2가)", + "phone": "061-244-6668", + "representativeMenu": "톳비빔밥", + "price": 8000, + "latitude": 34.784382080202, + "longitude": 126.381886313664, + "pageIndex": 3, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "15149", + "name": "대영한우식육식당", + "categoryName": "한식", + "address": "전라남도 목포시 영산로257번길 4-1 (용당동) 대영한우식육식당", + "phone": "061-273-7789", + "representativeMenu": "갈비탕", + "price": 10000, + "latitude": 34.80385281569496, + "longitude": 126.39420318915167, + "pageIndex": 3, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6320", + "name": "걸앤맨", + "categoryName": "미용업", + "address": "경상북도 포항시 남구 축항로72번길 14", + "phone": "054-272-1533", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.0285256046441, + "longitude": 129.376479161194, + "pageIndex": 3, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6321", + "name": "경민헤어샵", + "categoryName": "미용업", + "address": "경상북도 포항시 남구 대해로79번길 27", + "phone": "-", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.0254310711759, + "longitude": 129.36329181671, + "pageIndex": 3, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6322", + "name": "경아미용실", + "categoryName": "미용업", + "address": "경상북도 포항시 남구 송림로 61", + "phone": "054-247-4573", + "representativeMenu": "커트", + "price": 7000, + "latitude": 36.0352517105272, + "longitude": 129.374922321202, + "pageIndex": 3, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6427", + "name": "경희미용실", + "categoryName": "미용업", + "address": "경상북도 포항시 북구 서동로47번길 25-4 1층(덕수동)", + "phone": "-", + "representativeMenu": "커트", + "price": 5000, + "latitude": 36.0433237553854, + "longitude": 129.364506666177, + "pageIndex": 3, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "16322", + "name": "구서방네손칼국수", + "categoryName": "한식", + "address": "경상남도 창원시 마산합포구 해안대로 1 (월남동5가) 1층 구서방네손칼국수", + "phone": "055-222-9549", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 35.18079038371285, + "longitude": 128.55905853200002, + "pageIndex": 3, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "17102", + "name": "국밥명가 온마루", + "categoryName": "한식", + "address": "경상남도 창원시 의창구 북면 천주로 472 103호", + "phone": "0507-1346-4134", + "representativeMenu": "돼지국밥", + "price": 7000, + "latitude": 35.29896206632041, + "longitude": 128.6039200218082, + "pageIndex": 3, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "17142", + "name": "국수랑돈가스랑", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 진해대로597번길 8 (경화동) 1층", + "phone": "0507-1440-7000", + "representativeMenu": "왕돈가스", + "price": 8000, + "latitude": 35.15809278309137, + "longitude": 128.6810665767454, + "pageIndex": 3, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "14292", + "name": "김영이헤어모드", + "categoryName": "미용업", + "address": "경상남도 창원시 진해구 돌리로10번길 25 (석동, 유일수정빌라) 1층", + "phone": "055-543-4053", + "representativeMenu": "커트", + "price": 8000, + "latitude": 35.15791820070982, + "longitude": 128.7029593955796, + "pageIndex": 3, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "15812", + "name": "고산동산", + "categoryName": "한식", + "address": "제주특별자치도 제주시 고산동산5길 18 (이도이동) 1층", + "phone": "010-3693-1604", + "representativeMenu": "정식", + "price": 8000, + "latitude": 33.49614111635461, + "longitude": 126.53452311902686, + "pageIndex": 3, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "18065", + "name": "고슬", + "categoryName": "한식", + "address": "제주특별자치도 제주시 문송길 22 (연동) 고슬", + "phone": "070-8900-8495", + "representativeMenu": "떡볶이", + "price": 4000, + "latitude": 33.487975312677904, + "longitude": 126.49712441188434, + "pageIndex": 3, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "14253", + "name": "고찌가게", + "categoryName": "양식", + "address": "제주특별자치도 제주시 가령로4길 27 (이도이동) 1층 101호", + "phone": "010-4424-4619", + "representativeMenu": "함박스테이크", + "price": 7000, + "latitude": 33.49814114694817, + "longitude": 126.53535069630652, + "pageIndex": 3, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7116", + "name": "공주네국수", + "categoryName": "한식", + "address": "제주특별자치도 제주시 삼봉로 353 1층(봉개동)", + "phone": "064-722-4533", + "representativeMenu": "물만두", + "price": 5000, + "latitude": 33.4913445688525, + "longitude": 126.59371518559, + "pageIndex": 3, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "19250", + "name": "맛밥김밥전문점", + "categoryName": "한식", + "address": "서울특별시 종로구 창경궁로 269 (혜화동) 1층", + "phone": "02-747-5425", + "representativeMenu": "돈가스", + "price": 7500, + "latitude": 37.585295075666664, + "longitude": 127.00073357977945, + "pageIndex": 4, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "11063", + "name": "믿음미용실", + "categoryName": "미용업", + "address": "서울특별시 종로구 계동길 91 1층", + "phone": "-", + "representativeMenu": "커트", + "price": 8000, + "latitude": 37.5814377145875, + "longitude": 126.986679281786, + "pageIndex": 4, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1650", + "name": "대교식당", + "categoryName": "한식", + "address": "부산광역시 중구 충장대로 4번길 8 (중앙동4가)", + "phone": "051-469-0146", + "representativeMenu": "된장찌개", + "price": 6000, + "latitude": 35.1044916301827, + "longitude": 129.036809946537, + "pageIndex": 4, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1641", + "name": "대성밀냉면전문", + "categoryName": "한식", + "address": "부산광역시 중구 보수대로 44번길 6 (부평동)", + "phone": "051-244-9658", + "representativeMenu": "밀면(소)", + "price": 8000, + "latitude": 35.1008554024383, + "longitude": 129.02385096253, + "pageIndex": 4, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "13811", + "name": "돌고래", + "categoryName": "한식", + "address": "부산광역시 중구 중구로40번길 15 (신창동2가) 2층", + "phone": "051-246-1825", + "representativeMenu": "순두부백반", + "price": 8000, + "latitude": 35.1015493615088, + "longitude": 129.02959068112168, + "pageIndex": 4, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1651", + "name": "동경식당", + "categoryName": "한식", + "address": "부산광역시 중구 대청로99번길 16 (대청동2가)", + "phone": "051-469-7162", + "representativeMenu": "추어탕", + "price": 10000, + "latitude": 35.104213999781, + "longitude": 129.030751512242, + "pageIndex": 4, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "10076", + "name": "명가", + "categoryName": "한식", + "address": "대구광역시 중구 달구벌대로 지하 2100 (106)", + "phone": "053-252-9743", + "representativeMenu": "된장비빔밥", + "price": 6000, + "latitude": 35.8644213158621, + "longitude": 128.593340834492, + "pageIndex": 4, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "15468", + "name": "명성김밥", + "categoryName": "한식", + "address": "대구광역시 중구 국채보상로131길 55 (동인동1가, 시티타운) 시티상가 10호", + "phone": "053-425-0276", + "representativeMenu": "김밥", + "price": 3000, + "latitude": 35.872799919942665, + "longitude": 128.60198095419133, + "pageIndex": 4, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "15355", + "name": "모정", + "categoryName": "한식", + "address": "대구광역시 중구 달구벌대로 2037-16 (동산동) 모정(식당)", + "phone": "-", + "representativeMenu": "된장찌개", + "price": 8000, + "latitude": 35.86701996348072, + "longitude": 128.58658111123873, + "pageIndex": 4, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2231", + "name": "백원이네", + "categoryName": "한식", + "address": "인천광역시 중구 예단포1로 2-10 (운북동)", + "phone": "032-751-5059", + "representativeMenu": "해물라면", + "price": 5000, + "latitude": 37.5314534478937, + "longitude": 126.502439167743, + "pageIndex": 4, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "18031", + "name": "복락만두", + "categoryName": "중식", + "address": "인천광역시 중구 신포로27번길 49 (중앙동3가) 1층", + "phone": "032-772-8868", + "representativeMenu": "찐만두", + "price": 6000, + "latitude": 37.47256655347608, + "longitude": 126.6227457445627, + "pageIndex": 4, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "14637", + "name": "마시타", + "categoryName": "일식", + "address": "광주광역시 동구 서석로85번길 8 (대의동) 1층", + "phone": "070-4027-2166", + "representativeMenu": "마시타라멘", + "price": 7500, + "latitude": 35.14995826194754, + "longitude": 126.92008294904745, + "pageIndex": 4, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "11651", + "name": "마쏘라까망베르", + "categoryName": "한식", + "address": "광주광역시 동구 필문대로205번길 19 (지산동)", + "phone": "062-224-5004", + "representativeMenu": "돈가스(L)", + "price": 9000, + "latitude": 35.1520673077606, + "longitude": 126.933228831241, + "pageIndex": 4, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2247", + "name": "명문목욕탕", + "categoryName": "목욕업", + "address": "광주광역시 동구 지원로 5 (소태동)", + "phone": "062-227-2459", + "representativeMenu": "목욕", + "price": 6000, + "latitude": 35.1238630581617, + "longitude": 126.932939954425, + "pageIndex": 4, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2526", + "name": "돈하우스", + "categoryName": "양식", + "address": "대전광역시 동구 동대전로 131번길 8-16 1층(자양동)", + "phone": "042-624-5050", + "representativeMenu": "치킨까스", + "price": 6900, + "latitude": 36.3347457791416, + "longitude": 127.44561323845, + "pageIndex": 4, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "16531", + "name": "돌구이 돼지마을 해장국", + "categoryName": "한식", + "address": "대전광역시 동구 대전로815번길 54-1 (정동) 1층", + "phone": "042-226-1330", + "representativeMenu": "백반", + "price": 7000, + "latitude": 36.334231713428856, + "longitude": 127.4305695784201, + "pageIndex": 4, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2519", + "name": "동양전자", + "categoryName": "기타비요식업", + "address": "대전광역시 동구 대전로 797번길 11 (중동)", + "phone": "042-254-3090", + "representativeMenu": "전자장비 수리비", + "price": 10000, + "latitude": 36.3304315762194, + "longitude": 127.432265782557, + "pageIndex": 4, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "13641", + "name": "배가한식", + "categoryName": "한식", + "address": "울산광역시 중구 중앙길 255 (학산동) .", + "phone": "-", + "representativeMenu": "김치찌개", + "price": 5000, + "latitude": 35.55841683163878, + "longitude": 129.32668187695438, + "pageIndex": 4, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "11235", + "name": "빠삐용 베이커리", + "categoryName": "베이커리", + "address": "울산광역시 중구 중앙길 91 (성남동)", + "phone": "052-248-0250", + "representativeMenu": "팥빵", + "price": 1900, + "latitude": 35.5555689712778, + "longitude": 129.318463151667, + "pageIndex": 4, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "14775", + "name": "서울본갈비본감자탕성안점", + "categoryName": "한식", + "address": "울산광역시 중구 백양로 54 (성안동) 1층", + "phone": "0507-1314-2168", + "representativeMenu": "돼지갈비(180g)", + "price": 8500, + "latitude": 35.573990867359086, + "longitude": 129.31211816914396, + "pageIndex": 4, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2989", + "name": "선경이용원", + "categoryName": "이용업", + "address": "울산광역시 중구 유곡로 10 (우정동)", + "phone": "052-246-1703", + "representativeMenu": "컷트", + "price": 5000, + "latitude": 35.5582366838079, + "longitude": 129.30845466344, + "pageIndex": 4, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "10172", + "name": "명품한우타운", + "categoryName": "한식", + "address": "세종특별자치시 금남면 금남구즉로 110-3", + "phone": "044-866-6660", + "representativeMenu": "한우탕", + "price": 6000, + "latitude": 36.4730253891059, + "longitude": 127.290692559985, + "pageIndex": 4, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10198", + "name": "모란반점", + "categoryName": "중식", + "address": "세종특별자치시 보듬3로 104-11 202호(아름동, 행복프라자)", + "phone": "044-867-2477", + "representativeMenu": "자장면", + "price": 5500, + "latitude": 36.5123101279615, + "longitude": 127.248844235046, + "pageIndex": 4, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10195", + "name": "무진장 황태칼국수", + "categoryName": "한식", + "address": "세종특별자치시 금남면 용포로 74 금남면 용포로74", + "phone": "044-866-7151", + "representativeMenu": "황태얼큰이칼국수+보리밥", + "price": 7000, + "latitude": 36.4651078017756, + "longitude": 127.281290524669, + "pageIndex": 4, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "19000", + "name": "구름빵집", + "categoryName": "베이커리", + "address": "경기도 수원시 장안구 이목로 24 (정자동, 수원 SK SKY VIEW) 603동 1층 114호", + "phone": "031-223-7727", + "representativeMenu": "단팥빵", + "price": 2000, + "latitude": 37.308841568254806, + "longitude": 126.98470292444611, + "pageIndex": 4, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "16201", + "name": "국수명가", + "categoryName": "한식", + "address": "경기도 수원시 권선구 평동로79번길 45 (평동) 1층 154호", + "phone": "031-242-8473", + "representativeMenu": "비빔밥", + "price": 7000, + "latitude": 37.259876627948074, + "longitude": 126.99277737503824, + "pageIndex": 4, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "3384", + "name": "국수촌", + "categoryName": "한식", + "address": "경기도 수원시 영통구 매여울로53번길 63-2 (매탄동)", + "phone": "031-213-5033", + "representativeMenu": "잔치국수", + "price": 3000, + "latitude": 37.2726657638748, + "longitude": 127.041141095451, + "pageIndex": 4, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "15487", + "name": "금란미용실", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 둥지길 7 (효자동)", + "phone": "033-257-4174", + "representativeMenu": "커트", + "price": 10000, + "latitude": 37.874828386296336, + "longitude": 127.73569431782938, + "pageIndex": 4, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "10303", + "name": "금옥", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 동면 금옥길 289-27 1층", + "phone": "033-241-1791", + "representativeMenu": "막국수", + "price": 8000, + "latitude": 37.9229090212663, + "longitude": 127.80680948061, + "pageIndex": 4, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4378", + "name": "낭만국수집", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 서부대성로57번길2 (옥천동)", + "phone": "033-251-5688", + "representativeMenu": "돌솥비빔밥", + "price": 8000, + "latitude": 37.8829699924835, + "longitude": 127.72960354019, + "pageIndex": 4, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "15779", + "name": "괴산식당", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 매봉로76번길 30 (수곡동) 1층", + "phone": "043-285-6045", + "representativeMenu": "청국장", + "price": 7000, + "latitude": 36.61685424168219, + "longitude": 127.48003972548739, + "pageIndex": 4, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "13537", + "name": "국가대표", + "categoryName": "한식", + "address": "충청북도 청주시 청원구 수암로88번길 5-1 (우암동)", + "phone": "-", + "representativeMenu": "순두부찌개", + "price": 6000, + "latitude": 36.65014622761729, + "longitude": 127.49373602311476, + "pageIndex": 4, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15534", + "name": "국수좋은날", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 1순환로1063번길 34 (분평동) 1층", + "phone": "043-291-5962", + "representativeMenu": "잔치국수", + "price": 5000, + "latitude": 36.60797810746097, + "longitude": 127.48522816108827, + "pageIndex": 4, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "13709", + "name": "굼드림", + "categoryName": "기타요식업", + "address": "충청북도 청주시 상당구 상당로69번길 15 (북문로1가) 대신증권 1층", + "phone": "-", + "representativeMenu": "아메리카노", + "price": 2000, + "latitude": 36.63425701957848, + "longitude": 127.48966620404532, + "pageIndex": 4, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4910", + "name": "국빈4000냥미용실", + "categoryName": "미용업", + "address": "충청남도 천안시 동남구 대흥로 132-2 (사직동)", + "phone": "-", + "representativeMenu": "커트", + "price": 7000, + "latitude": 36.8008837521399, + "longitude": 127.148549025545, + "pageIndex": 4, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "13065", + "name": "김밥군쫄면양", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 충절로 311 (구성동) 109호", + "phone": "-", + "representativeMenu": "김밥", + "price": 3000, + "latitude": 36.791883821934995, + "longitude": 127.16249631665832, + "pageIndex": 4, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "11750", + "name": "김밥하나", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 대흥로 340 (신부동)", + "phone": "041-556-6247", + "representativeMenu": "김밥", + "price": 3000, + "latitude": 36.8177959668313, + "longitude": 127.15176650569, + "pageIndex": 4, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "18214", + "name": "란미용실", + "categoryName": "미용업", + "address": "전북특별자치도 전주시 완산구 공수내1길 7-2 (서서학동) 1층", + "phone": "070-4007-3960", + "representativeMenu": "커트", + "price": 10000, + "latitude": 35.805287710386786, + "longitude": 127.14763371176575, + "pageIndex": 4, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5437", + "name": "만나별미", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 거마평로 122", + "phone": "063-224-9529", + "representativeMenu": "돈가스", + "price": 7000, + "latitude": 35.8032647559385, + "longitude": 127.116997533405, + "pageIndex": 4, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5438", + "name": "만남의집", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 서학로 28-1", + "phone": "063-287-5589", + "representativeMenu": "김치찌개", + "price": 8000, + "latitude": 35.8095241776664, + "longitude": 127.152553371019, + "pageIndex": 4, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "15140", + "name": "대흥정", + "categoryName": "한식", + "address": "전라남도 목포시 용당로216번길 25-1 (용당동) 대흥정", + "phone": "061-273-0414", + "representativeMenu": "백반", + "price": 8000, + "latitude": 34.80647798540608, + "longitude": 126.40112936933254, + "pageIndex": 4, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "15153", + "name": "드라이하우스 북항점", + "categoryName": "세탁업", + "address": "전라남도 목포시 청호로219번길 30 (산정동) 1층 106호", + "phone": "061-278-2788", + "representativeMenu": "정장한벌", + "price": 6100, + "latitude": 34.80742822388893, + "longitude": 126.36751972578438, + "pageIndex": 4, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "10506", + "name": "맹순이네 국밥", + "categoryName": "한식", + "address": "전라남도 목포시 북항로 73 1층(죽교동)", + "phone": "-", + "representativeMenu": "돼지머리국밥", + "price": 6000, + "latitude": 34.7998878782976, + "longitude": 126.376318072647, + "pageIndex": 4, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6386", + "name": "고을마당", + "categoryName": "한식", + "address": "경상북도 포항시 남구 대이로9번길 22 고을마당", + "phone": "-", + "representativeMenu": "해물칼국수", + "price": 7000, + "latitude": 36.015803950231, + "longitude": 129.341696748169, + "pageIndex": 4, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6387", + "name": "고향식당", + "categoryName": "한식", + "address": "경상북도 포항시 남구 대송면 제내길75번길 29-5 고향식당", + "phone": "054-293-1230", + "representativeMenu": "비빔밥", + "price": 8000, + "latitude": 35.9795204842443, + "longitude": 129.364706422308, + "pageIndex": 4, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6809", + "name": "낙동추어탕", + "categoryName": "한식", + "address": "경상남도 창원시 성산구 대정로35번길 4-1 (가음동, 정진상가)", + "phone": "055-286-5565", + "representativeMenu": "추어탕", + "price": 9000, + "latitude": 35.2078507245462, + "longitude": 128.698126634249, + "pageIndex": 4, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "7117", + "name": "광양왕소금깡통구이", + "categoryName": "한식", + "address": "제주특별자치도 제주시 광양13길 13", + "phone": "064-755-9966", + "representativeMenu": "돌솥밥", + "price": 6000, + "latitude": 33.4992265614392, + "longitude": 126.531680684457, + "pageIndex": 4, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "15758", + "name": "국수한상", + "categoryName": "한식", + "address": "제주특별자치도 제주시 남광로 7 (이도이동) 국수한상", + "phone": "064-751-5808", + "representativeMenu": "멸치국수", + "price": 7000, + "latitude": 33.492506273154056, + "longitude": 126.53696512732265, + "pageIndex": 4, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7018", + "name": "굴무기낭", + "categoryName": "기타요식업", + "address": "제주특별자치도 제주시 애월읍 납읍남로2길 6 1동 1층", + "phone": "0507-1484-1370", + "representativeMenu": "감귤쉰다리에이드", + "price": 5000, + "latitude": 33.433110680166, + "longitude": 126.327918925624, + "pageIndex": 4, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "16044", + "name": "복코돼지", + "categoryName": "한식", + "address": "서울특별시 종로구 종로 232 (종로5가) 1층", + "phone": "02-2271-5989", + "representativeMenu": "김치찌개(점심)", + "price": 7000, + "latitude": 37.570761508765074, + "longitude": 127.00353272937728, + "pageIndex": 5, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "17542", + "name": "불백당", + "categoryName": "한식", + "address": "서울특별시 종로구 우정국로2길 34 (관철동) 1층", + "phone": "02-722-0086", + "representativeMenu": "고추장불백", + "price": 5500, + "latitude": 37.56946019643439, + "longitude": 126.98494601675547, + "pageIndex": 5, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "958", + "name": "삼삼 뚝배기", + "categoryName": "한식", + "address": "서울특별시 종로구 동숭길 51 (동숭동)", + "phone": "02-765-4683", + "representativeMenu": "된장뚝배기", + "price": 7000, + "latitude": 37.5805564090189, + "longitude": 127.004190744723, + "pageIndex": 5, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "16866", + "name": "샐러드하우스", + "categoryName": "기타요식업", + "address": "서울특별시 종로구 새문안로5가길 28 (적선동) 지하1층 119호", + "phone": "02-722-5891", + "representativeMenu": "에그듬뿍샌드위치", + "price": 4900, + "latitude": 37.57446023473285, + "longitude": 126.97359705813241, + "pageIndex": 5, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "16480", + "name": "동광세탁소", + "categoryName": "세탁업", + "address": "부산광역시 중구 법수길 19 (보수동1가) 1층", + "phone": "051-256-9047", + "representativeMenu": "드라이크리닝(하의)", + "price": 4000, + "latitude": 35.10490454876418, + "longitude": 129.02599093842454, + "pageIndex": 5, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "17048", + "name": "동명칼국수", + "categoryName": "한식", + "address": "부산광역시 중구 중구로34번길 23 (신창동1가) 2층", + "phone": "051-241-0061", + "representativeMenu": "칼국수", + "price": 6500, + "latitude": 35.10111918682593, + "longitude": 129.03031073104827, + "pageIndex": 5, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "19007", + "name": "된장한상", + "categoryName": "한식", + "address": "부산광역시 중구 대청로141번길 17 (중앙동4가) 1층", + "phone": "051-442-3006", + "representativeMenu": "된장한상(비빔밥+된장)", + "price": 8000, + "latitude": 35.104348496848104, + "longitude": 129.03506122917605, + "pageIndex": 5, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1653", + "name": "뚱보집", + "categoryName": "한식", + "address": "부산광역시 중구 중앙대로 29번길 2-11 (중앙동)", + "phone": "051-246-7466", + "representativeMenu": "콩나물밥", + "price": 4000, + "latitude": 35.1006950852985, + "longitude": 129.036117527715, + "pageIndex": 5, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "14627", + "name": "밥맛나는집", + "categoryName": "한식", + "address": "대구광역시 중구 경상감영길 37 (서문로1가) 밥맛나는집", + "phone": "053-428-0121", + "representativeMenu": "된장찌개", + "price": 7000, + "latitude": 35.87145492938626, + "longitude": 128.58946800122544, + "pageIndex": 5, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17982", + "name": "배사공멸치국수", + "categoryName": "한식", + "address": "대구광역시 중구 관덕정길 10 (남산동) 남산동", + "phone": "053-422-8866", + "representativeMenu": "잔치국수", + "price": 6000, + "latitude": 35.865329712937886, + "longitude": 128.59079734801406, + "pageIndex": 5, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "11440", + "name": "쎈밤 종로점", + "categoryName": "한식", + "address": "대구광역시 중구 종로 30-1 1층", + "phone": "070-7685-7137", + "representativeMenu": "국내산 삼겹살(150g)", + "price": 8900, + "latitude": 35.8686744440218, + "longitude": 128.592029244117, + "pageIndex": 5, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2007", + "name": "영생이용소", + "categoryName": "이용업", + "address": "대구광역시 중구 이천로 184-8 (대봉동)", + "phone": "053-425-9732", + "representativeMenu": "커트", + "price": 10000, + "latitude": 35.8575452270514, + "longitude": 128.598969402186, + "pageIndex": 5, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "14222", + "name": "삼미", + "categoryName": "기타요식업", + "address": "인천광역시 중구 제물량로 210-4 (해안동2가) 1층", + "phone": "032-812-4055", + "representativeMenu": "치즈카야토스트", + "price": 5000, + "latitude": 37.47195223689287, + "longitude": 126.62120161220716, + "pageIndex": 5, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "16743", + "name": "삼시국수", + "categoryName": "한식", + "address": "인천광역시 중구 운중로 97 (운남동) 1층", + "phone": "0507-1479-2358", + "representativeMenu": "멸치국수", + "price": 7000, + "latitude": 37.49421029445236, + "longitude": 126.5443392421388, + "pageIndex": 5, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "14009", + "name": "소미국수", + "categoryName": "한식", + "address": "인천광역시 중구 서해대로 486-2 (유동) 1층", + "phone": "032-773-1003", + "representativeMenu": "감자칼국수", + "price": 9000, + "latitude": 37.46916114263947, + "longitude": 126.63654581942478, + "pageIndex": 5, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "19356", + "name": "무등산국밥", + "categoryName": "한식", + "address": "광주광역시 동구 제봉로17번길 2 (학동) 1층", + "phone": "062-223-2051", + "representativeMenu": "암뽕순대국밥", + "price": 7000, + "latitude": 35.14024503188192, + "longitude": 126.92183479745447, + "pageIndex": 5, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2264", + "name": "박순자녹두집", + "categoryName": "한식", + "address": "광주광역시 동구 구성로204번길 26 (대인동)", + "phone": "062-223-8694", + "representativeMenu": "수제비", + "price": 6000, + "latitude": 35.1518665091161, + "longitude": 126.916416294973, + "pageIndex": 5, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2265", + "name": "반디식당", + "categoryName": "한식", + "address": "광주광역시 동구 구성로 258 (계림동)", + "phone": "062-222-0809", + "representativeMenu": "백반", + "price": 6000, + "latitude": 35.1570933401735, + "longitude": 126.918837815272, + "pageIndex": 5, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2564", + "name": "뒤집어진뚝배기", + "categoryName": "한식", + "address": "대전광역시 동구 동대전로131번길 8-11 (자양동)", + "phone": "042-622-3692", + "representativeMenu": "제육", + "price": 7500, + "latitude": 36.3346159683747, + "longitude": 127.445358439471, + "pageIndex": 5, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "16819", + "name": "또와짬뽕", + "categoryName": "중식", + "address": "대전광역시 동구 동대전로 153-5 (자양동) 1층", + "phone": "042-623-6237", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 36.335615601209376, + "longitude": 127.446345541849, + "pageIndex": 5, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2575", + "name": "마실", + "categoryName": "한식", + "address": "대전광역시 동구 동산초교로 22번길 45 (홍도동)", + "phone": "042-635-7086", + "representativeMenu": "수제비", + "price": 7000, + "latitude": 36.3475476850448, + "longitude": 127.429039114596, + "pageIndex": 5, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "14774", + "name": "송림식당", + "categoryName": "한식", + "address": "울산광역시 중구 성안6길 1 (성안동) 1층", + "phone": "052-246-8751", + "representativeMenu": "정식", + "price": 8000, + "latitude": 35.575033452695315, + "longitude": 129.31651249571607, + "pageIndex": 5, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2994", + "name": "수다라", + "categoryName": "한식", + "address": "울산광역시 중구 서원1길 3 (반구동)", + "phone": "052-293-8818", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 35.5632555809223, + "longitude": 129.335301504887, + "pageIndex": 5, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "14577", + "name": "순정분식", + "categoryName": "한식", + "address": "울산광역시 중구 반구정15길 18 (반구동) 1층", + "phone": "-", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 35.55845233751778, + "longitude": 129.34466380262637, + "pageIndex": 5, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "10174", + "name": "복조갈비", + "categoryName": "한식", + "address": "세종특별자치시 조치원읍 새내12길 51 1층", + "phone": "044-862-3890", + "representativeMenu": "육개장", + "price": 8000, + "latitude": 36.6026560183833, + "longitude": 127.300695361905, + "pageIndex": 5, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "19176", + "name": "부산면가", + "categoryName": "한식", + "address": "세종특별자치시 호려울로 29 (보람동) 110호", + "phone": "044-862-0626", + "representativeMenu": "옛날칼국수", + "price": 6500, + "latitude": 36.477909935227785, + "longitude": 127.29023798268373, + "pageIndex": 5, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "14528", + "name": "까페시모 우만점", + "categoryName": "기타요식업", + "address": "경기도 수원시 팔달구 경수대로656번길 37-22 (우만동) 1층", + "phone": "031-304-0307", + "representativeMenu": "아메리카노", + "price": 2900, + "latitude": 37.28563203757849, + "longitude": 127.02975069485917, + "pageIndex": 5, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "14526", + "name": "꼼빠도르", + "categoryName": "베이커리", + "address": "경기도 수원시 팔달구 정조로 797-2 (팔달로2가) 101, 102호", + "phone": "031-243-0588", + "representativeMenu": "팥빵", + "price": 2000, + "latitude": 37.279316026053095, + "longitude": 127.01637087798076, + "pageIndex": 5, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "3367", + "name": "꽃씨미용실", + "categoryName": "미용업", + "address": "경기도 수원시 권선구 금호로15번길12 (금곡동)", + "phone": "031-297-6106", + "representativeMenu": "커트", + "price": 10000, + "latitude": 37.2692497847657, + "longitude": 126.953624838044, + "pageIndex": 5, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "15303", + "name": "남문건강제분소", + "categoryName": "한식", + "address": "경기도 수원시 팔달구 창룡대로26번길 42-7 (남수동) 1층", + "phone": "031-241-8002", + "representativeMenu": "콩나물국밥", + "price": 6000, + "latitude": 37.28002458529435, + "longitude": 127.0188847877665, + "pageIndex": 5, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "10304", + "name": "낭만국시", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 명동길29번길 3 (죽림동)", + "phone": "033-252-6255", + "representativeMenu": "칼국시", + "price": 7000, + "latitude": 37.8781171202343, + "longitude": 127.725962552609, + "pageIndex": 5, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4354", + "name": "누이네", + "categoryName": "일식", + "address": "강원특별자치도 춘천시 영서로2279번길 25 (온의동)", + "phone": "033-251-2261", + "representativeMenu": "회덮밥", + "price": 9000, + "latitude": 37.8628068929099, + "longitude": 127.720326390422, + "pageIndex": 5, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4327", + "name": "달콤한게으름", + "categoryName": "기타요식업", + "address": "강원특별자치도 춘천시 신북읍 신샘밭로 502 달콤한게으름", + "phone": "0507-1359-7877", + "representativeMenu": "아메리카노", + "price": 4000, + "latitude": 37.9295512271344, + "longitude": 127.765130880111, + "pageIndex": 5, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "18315", + "name": "닭갈비짱", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 스무숲1길 38-9 -", + "phone": "033-261-3688", + "representativeMenu": "특선 도시락", + "price": 10000, + "latitude": 37.8492683443013, + "longitude": 127.750878159251, + "pageIndex": 5, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "16059", + "name": "그레이스 칼국수", + "categoryName": "한식", + "address": "충청북도 청주시 상당구 남일면 효촌송암길 18-11 1층 103호", + "phone": "043-288-1932", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 36.58914562523452, + "longitude": 127.50987272855514, + "pageIndex": 5, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4746", + "name": "금와가든", + "categoryName": "한식", + "address": "충청북도 청주시 청원구 팔결로 66 (주중동)", + "phone": "043-211-8660", + "representativeMenu": "한식뷔페", + "price": 6000, + "latitude": 36.6890862826954, + "longitude": 127.482711215662, + "pageIndex": 5, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "16069", + "name": "기쁨가득 엄마밥상", + "categoryName": "한식", + "address": "충청북도 청주시 흥덕구 사운로190번길 1 (운천동)", + "phone": "-", + "representativeMenu": "된장찌개", + "price": 6000, + "latitude": 36.64618197563589, + "longitude": 127.47854183832706, + "pageIndex": 5, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15849", + "name": "김진헤어", + "categoryName": "미용업", + "address": "충청북도 청주시 흥덕구 증안로 44 (복대동) 2층", + "phone": "043-239-4129", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.63652312339393, + "longitude": 127.42812791746137, + "pageIndex": 5, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "19313", + "name": "깨돌이김밥", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 각원사길 52 (안서동) 깨돌이김밥", + "phone": "0507-1420-7277", + "representativeMenu": "김밥", + "price": 3000, + "latitude": 36.830711862714494, + "longitude": 127.1776761133309, + "pageIndex": 5, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "18220", + "name": "만족", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 안터6길 41 (서신동) 1층", + "phone": "0507-1358-5650", + "representativeMenu": "비빔밥/순두부찌개/비빔막국수/바지락칼국수(점심)", + "price": 8000, + "latitude": 35.8300315070019, + "longitude": 127.11645550914488, + "pageIndex": 5, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5425", + "name": "맛자랑 팥고향집", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 서학로 32-4", + "phone": "063-231-0993", + "representativeMenu": "손칼국수", + "price": 7000, + "latitude": 35.8093835840183, + "longitude": 127.152905612703, + "pageIndex": 5, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5419", + "name": "명보헤어", + "categoryName": "미용업", + "address": "전북특별자치도 전주시 완산구 팔달로 101-26", + "phone": "063-282-1643", + "representativeMenu": "커트", + "price": 10000, + "latitude": 35.8123674581985, + "longitude": 127.148537758879, + "pageIndex": 5, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "18209", + "name": "바다미용샵", + "categoryName": "미용업", + "address": "전북특별자치도 전주시 완산구 영경1길 21 (중화산동2가) 우성근영타운상가 1-101", + "phone": "063-223-8840", + "representativeMenu": "커트(학생)", + "price": 10000, + "latitude": 35.81809189327877, + "longitude": 127.12518062331628, + "pageIndex": 5, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "15151", + "name": "몽헤어갤러리", + "categoryName": "미용업", + "address": "전라남도 목포시 산정로 211 (용당동) 몽헤어갤러리", + "phone": "061-276-0479", + "representativeMenu": "학생커트", + "price": 7000, + "latitude": 34.80675654469635, + "longitude": 126.38939338794059, + "pageIndex": 5, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "15141", + "name": "미달이네집밥", + "categoryName": "한식", + "address": "전라남도 목포시 용당로 52 (산정동) 미달이네집밥", + "phone": "061-244-0222", + "representativeMenu": "한식뷔페", + "price": 9000, + "latitude": 34.79223219163826, + "longitude": 126.40229481607567, + "pageIndex": 5, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "10507", + "name": "백련가정식백반", + "categoryName": "한식", + "address": "전라남도 목포시 백년대로 83 (용당동)", + "phone": "061-281-3931", + "representativeMenu": "고등어구이백반", + "price": 9000, + "latitude": 34.7993153187521, + "longitude": 126.398708002647, + "pageIndex": 5, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6388", + "name": "고향식당", + "categoryName": "한식", + "address": "경상북도 포항시 남구 오천읍 세계길 34-2 고향식당", + "phone": "054-291-5806", + "representativeMenu": "정식", + "price": 7000, + "latitude": 35.9651038865803, + "longitude": 129.415954040359, + "pageIndex": 5, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "16132", + "name": "공원분식", + "categoryName": "한식", + "address": "경상북도 포항시 북구 환호공원길 19-1 (환호동) 공원분식", + "phone": "054-242-9337", + "representativeMenu": "잔치국수", + "price": 6000, + "latitude": 36.06805492716173, + "longitude": 129.3918551381414, + "pageIndex": 5, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6428", + "name": "곽헤어뷰티", + "categoryName": "미용업", + "address": "경상북도 포항시 북구 장량중앙로65번길 18-1 (양덕동) 1층", + "phone": "0507-1449-1250", + "representativeMenu": "남자커트", + "price": 6000, + "latitude": 36.09024160303488, + "longitude": 129.3898774029156, + "pageIndex": 5, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "15582", + "name": "낙지엔닭갈비", + "categoryName": "한식", + "address": "경상남도 창원시 마산회원구 내서읍 중리상곡로 77 101호", + "phone": "055-231-3217", + "representativeMenu": "낙지비빔밥", + "price": 10000, + "latitude": 35.24768918750772, + "longitude": 128.50844422463186, + "pageIndex": 5, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "10579", + "name": "남양스튜디오", + "categoryName": "기타비요식업", + "address": "경상남도 창원시 마산합포구 장장군로 31-23 (장군동4가)", + "phone": "055-245-0548", + "representativeMenu": "가족사진", + "price": 10000, + "latitude": 35.1971042919322, + "longitude": 128.565386781544, + "pageIndex": 5, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "15129", + "name": "남원식당", + "categoryName": "한식", + "address": "경상남도 창원시 의창구 도계로83번길 6-24 (도계동) 1층", + "phone": "055-238-0517", + "representativeMenu": "가정식 백반", + "price": 8000, + "latitude": 35.25676280757884, + "longitude": 128.6403988733871, + "pageIndex": 5, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "17648", + "name": "뉴헤어라인 이용원", + "categoryName": "이용업", + "address": "경상남도 창원시 진해구 제황로 83-1 (충무동) 1층", + "phone": "0507-1405-1427", + "representativeMenu": "남성커트", + "price": 9000, + "latitude": 35.15172782098772, + "longitude": 128.66680204776503, + "pageIndex": 5, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "7068", + "name": "귀빈반점", + "categoryName": "중식", + "address": "제주특별자치도 제주시 정원로 53-1 (노형동)", + "phone": "064-749-3080", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 33.4771339388759, + "longitude": 126.47755224554, + "pageIndex": 5, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "11423", + "name": "금악 똣똣라면", + "categoryName": "한식", + "address": "제주특별자치도 제주시 한림읍 금악로 18", + "phone": "-", + "representativeMenu": "똣똣라면(오리지널)", + "price": 6500, + "latitude": 33.3586559057476, + "longitude": 126.296620943982, + "pageIndex": 5, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "17999", + "name": "금악리자양식당", + "categoryName": "중식", + "address": "제주특별자치도 제주시 한림읍 금악로 18 자양식당", + "phone": "010-7163-3544", + "representativeMenu": "닭짬뽕", + "price": 9000, + "latitude": 33.35865254119738, + "longitude": 126.29662954404655, + "pageIndex": 5, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "14004", + "name": "서울식당", + "categoryName": "한식", + "address": "서울특별시 종로구 창신길 50 (창신동) 1층", + "phone": "02-764-7587", + "representativeMenu": "백반", + "price": 8000, + "latitude": 37.57383386363352, + "longitude": 127.01070027218017, + "pageIndex": 6, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "13892", + "name": "세종커피", + "categoryName": "기타요식업", + "address": "서울특별시 종로구 세종대로23길 54 (당주동) 지하1층 26호", + "phone": "010-5827-9542", + "representativeMenu": "아메리카노", + "price": 2500, + "latitude": 37.57183953065674, + "longitude": 126.97328450326039, + "pageIndex": 6, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1637", + "name": "미도리우동", + "categoryName": "일식", + "address": "부산광역시 중구 흑교로 45번길 10-1 (보수동)", + "phone": "051-244-8246", + "representativeMenu": "유부초밥", + "price": 4000, + "latitude": 35.1036985758005, + "longitude": 129.024128409662, + "pageIndex": 6, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "17051", + "name": "미향장어탕", + "categoryName": "한식", + "address": "부산광역시 중구 남포길 45-1 (남포동2가) 1층", + "phone": "0507-1430-2452", + "representativeMenu": "장어탕", + "price": 10000, + "latitude": 35.09811695918981, + "longitude": 129.03373733333655, + "pageIndex": 6, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "14624", + "name": "예원생선구이", + "categoryName": "한식", + "address": "대구광역시 중구 동덕로 114-12 (삼덕동2가) 1층", + "phone": "053-421-4440", + "representativeMenu": "고등어구이 정식", + "price": 9000, + "latitude": 35.8646330747017, + "longitude": 128.60381216563215, + "pageIndex": 6, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "15358", + "name": "옛고을", + "categoryName": "한식", + "address": "대구광역시 중구 달구벌대로 2197-39 (삼덕동2가) 옛고을식당", + "phone": "053-427-8503", + "representativeMenu": "제육덮밥", + "price": 8000, + "latitude": 35.86464415410117, + "longitude": 128.60349499293326, + "pageIndex": 6, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "10074", + "name": "옛날국수", + "categoryName": "한식", + "address": "대구광역시 중구 중앙대로 439 향촌동", + "phone": "053-256-1221", + "representativeMenu": "잔치국수", + "price": 2000, + "latitude": 35.8724146556383, + "longitude": 128.594100102946, + "pageIndex": 6, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17977", + "name": "와래이수제꼬치전문점", + "categoryName": "한식", + "address": "대구광역시 중구 달구벌대로450길 16-6 (대봉동) 달구벌대로450길 16-6", + "phone": "053-254-1553", + "representativeMenu": "닭꼬치", + "price": 4000, + "latitude": 35.86127644033642, + "longitude": 128.60713054755325, + "pageIndex": 6, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2234", + "name": "신신분식", + "categoryName": "한식", + "address": "인천광역시 중구 자유공원로27번길 1 (내동)", + "phone": "032-762-0992", + "representativeMenu": "우동", + "price": 5500, + "latitude": 37.475111739177, + "longitude": 126.627848403961, + "pageIndex": 6, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2225", + "name": "신포대패", + "categoryName": "한식", + "address": "인천광역시 중구 개항로 23 (관동3가)", + "phone": "032-765-5237", + "representativeMenu": "신포대패(120g)", + "price": 6000, + "latitude": 37.4719246800065, + "longitude": 126.625275097774, + "pageIndex": 6, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2235", + "name": "신포화로구이", + "categoryName": "한식", + "address": "인천광역시 중구 신포로 27번길 16 (관동3가)", + "phone": "032-777-0099", + "representativeMenu": "김치찌개", + "price": 7500, + "latitude": 37.4719823087487, + "longitude": 126.624610999499, + "pageIndex": 6, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2226", + "name": "신흥숯불갈비", + "categoryName": "한식", + "address": "인천광역시 중구 서해대로454번길 14-2 (선화동)", + "phone": "032-883-7717", + "representativeMenu": "냉면", + "price": 10000, + "latitude": 37.4656929791796, + "longitude": 126.636120369468, + "pageIndex": 6, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2266", + "name": "밥s", + "categoryName": "한식", + "address": "광주광역시 동구 지산로 21 (지산동)", + "phone": "062-222-2838", + "representativeMenu": "돈가스", + "price": 4500, + "latitude": 35.1460563034625, + "longitude": 126.931121713111, + "pageIndex": 6, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "10138", + "name": "삼미관", + "categoryName": "한식", + "address": "광주광역시 동구 백서로189번길 14-32 (서석동)", + "phone": "062-227-0391", + "representativeMenu": "간짜장", + "price": 8000, + "latitude": 35.1458043478303, + "longitude": 126.924281394451, + "pageIndex": 6, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2530", + "name": "머리카락&피부샵", + "categoryName": "미용업", + "address": "대전광역시 동구 동서대로 1692번길 153 (성남동)", + "phone": "-", + "representativeMenu": "미용료(여자커트)", + "price": 8000, + "latitude": 36.3424896102607, + "longitude": 127.438366074824, + "pageIndex": 6, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "16081", + "name": "메밀고개 시골막국수", + "categoryName": "한식", + "address": "대전광역시 동구 판교3길 3 (판암동) 메밀고개 시골막국수", + "phone": "042-274-7787", + "representativeMenu": "메밀만두", + "price": 6000, + "latitude": 36.31841600671825, + "longitude": 127.45230275069078, + "pageIndex": 6, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "16083", + "name": "명동만두", + "categoryName": "한식", + "address": "대전광역시 동구 중앙로203번길 15 (중동) 약수식당", + "phone": "042-256-0444", + "representativeMenu": "비빔밥", + "price": 7000, + "latitude": 36.331539175383156, + "longitude": 127.43111599831819, + "pageIndex": 6, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2545", + "name": "미가칼국수", + "categoryName": "한식", + "address": "대전광역시 동구 백룡로 3 (자양동)", + "phone": "042-621-7533", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 36.3365141375393, + "longitude": 127.447715649929, + "pageIndex": 6, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2995", + "name": "엄마네뷔페", + "categoryName": "한식", + "address": "울산광역시 중구 당산4길 22 (우정동)", + "phone": "-", + "representativeMenu": "정식", + "price": 7000, + "latitude": 35.5537031836629, + "longitude": 129.310593372233, + "pageIndex": 6, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "16557", + "name": "에뜨왈베이커리", + "categoryName": "베이커리", + "address": "울산광역시 중구 화합로 492-1 (복산동) 1층", + "phone": "052-227-8388", + "representativeMenu": "단팥빵", + "price": 1200, + "latitude": 35.5679065905029, + "longitude": 129.33295718527037, + "pageIndex": 6, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "11677", + "name": "옥교국밥", + "categoryName": "한식", + "address": "울산광역시 중구 중앙시장길 2-5 (옥교동)", + "phone": "052-245-1163", + "representativeMenu": "돼지국밥", + "price": 9000, + "latitude": 35.5550846130913, + "longitude": 129.322138841423, + "pageIndex": 6, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2996", + "name": "유가냉면", + "categoryName": "한식", + "address": "울산광역시 중구 먹자거리 12 (성남동)", + "phone": "052-258-6696", + "representativeMenu": "물냉면", + "price": 9000, + "latitude": 35.5545078517398, + "longitude": 129.320075389132, + "pageIndex": 6, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "10183", + "name": "세종세탁 명품", + "categoryName": "세탁업", + "address": "세종특별자치시 만남로 190 상가 203호", + "phone": "044-864-2226", + "representativeMenu": "이불", + "price": 10000, + "latitude": 36.5170635630589, + "longitude": 127.241017466402, + "pageIndex": 6, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10168", + "name": "송덕이용원", + "categoryName": "이용업", + "address": "세종특별자치시 전동면 금이로 266", + "phone": "044-867-1488", + "representativeMenu": "조발", + "price": 10000, + "latitude": 36.6302394683097, + "longitude": 127.260476864844, + "pageIndex": 6, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10196", + "name": "스페이스", + "categoryName": "양식", + "address": "세종특별자치시 한누리대로 311 110호~111호", + "phone": "044-863-1122", + "representativeMenu": "갈릭누들파스타", + "price": 9900, + "latitude": 36.492070617908, + "longitude": 127.255836690696, + "pageIndex": 6, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10186", + "name": "시장감자탕", + "categoryName": "한식", + "address": "세종특별자치시 조치원읍 조치원로 30-8 101호", + "phone": "044-862-6513", + "representativeMenu": "뼈해장국", + "price": 7000, + "latitude": 36.6012589737358, + "longitude": 127.300019359062, + "pageIndex": 6, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "15882", + "name": "냉삼집", + "categoryName": "한식", + "address": "경기도 수원시 영통구 영통로214번길 9 (영통동) 106호", + "phone": "031-206-3389", + "representativeMenu": "급랭삼겹살(200g)", + "price": 9900, + "latitude": 37.246578887390704, + "longitude": 127.05763865693096, + "pageIndex": 6, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "3419", + "name": "논골손칼국수", + "categoryName": "한식", + "address": "경기도 수원시 장안구 장안로75번길 52-2 102호(정자동)", + "phone": "031-252-9696", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 37.2903281507546, + "longitude": 127.000141983781, + "pageIndex": 6, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "17117", + "name": "늘하얀세탁소", + "categoryName": "세탁업", + "address": "경기도 수원시 팔달구 화양로68번길 61 (화서동) 1층", + "phone": "031-242-5554", + "representativeMenu": "드라이클리닝(신사복)", + "price": 8000, + "latitude": 37.28181847804219, + "longitude": 126.99725940969884, + "pageIndex": 6, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "3401", + "name": "닛시헤어필", + "categoryName": "미용업", + "address": "경기도 수원시 장안구 파장로21번길 27 (송죽동)", + "phone": "031-257-1411", + "representativeMenu": "커트", + "price": 8000, + "latitude": 37.3033791048968, + "longitude": 126.997399634911, + "pageIndex": 6, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "10313", + "name": "더밥", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 춘주로91번길 10 (온의동)", + "phone": "033-242-2178", + "representativeMenu": "제육덮밥", + "price": 8900, + "latitude": 37.862555029709, + "longitude": 127.720541103243, + "pageIndex": 6, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4340", + "name": "동네미용실", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 동내면 영서로 1747 1층", + "phone": "033-262-3257", + "representativeMenu": "커트", + "price": 10000, + "latitude": 37.8327701426154, + "longitude": 127.761293705291, + "pageIndex": 6, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "10310", + "name": "동이만두", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 효제길 36 (효자동)", + "phone": "033-255-3633", + "representativeMenu": "김치찌개", + "price": 6000, + "latitude": 37.8709546627662, + "longitude": 127.737049930361, + "pageIndex": 6, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "15783", + "name": "꽃돼지댁", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 서원남로20번길 4 (모충동) 1층", + "phone": "043-295-3423", + "representativeMenu": "1인보쌈정식", + "price": 10000, + "latitude": 36.6220030068416, + "longitude": 127.48577351993595, + "pageIndex": 6, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4687", + "name": "나누리장터", + "categoryName": "한식", + "address": "충청북도 청주시 상당구 쇠내로 69-1 (금천동)", + "phone": "-", + "representativeMenu": "장터칼국수", + "price": 4000, + "latitude": 36.6249932875208, + "longitude": 127.501521214447, + "pageIndex": 6, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4747", + "name": "나성불고기쌈밥", + "categoryName": "한식", + "address": "충청북도 청주시 청원구 향군로 51 (우암동)", + "phone": "043-253-3511", + "representativeMenu": "된장찌개", + "price": 6000, + "latitude": 36.6501623292297, + "longitude": 127.485399652549, + "pageIndex": 6, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4959", + "name": "남성커트오천", + "categoryName": "이용업", + "address": "충청남도 천안시 서북구 월봉로 66 (쌍용동)", + "phone": "-", + "representativeMenu": "커트", + "price": 7000, + "latitude": 36.7964851683758, + "longitude": 127.118063685574, + "pageIndex": 6, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4911", + "name": "남성컷트오천냥클럽", + "categoryName": "이용업", + "address": "충청남도 천안시 동남구 서부대로 257-3 105호(신방동)", + "phone": "041-572-5283", + "representativeMenu": "커트", + "price": 7000, + "latitude": 36.7910983221181, + "longitude": 127.128572630944, + "pageIndex": 6, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "10392", + "name": "넘버원 남성컷트", + "categoryName": "미용업", + "address": "충청남도 천안시 서북구 두정로 263 (두정동)", + "phone": "041-904-1020", + "representativeMenu": "커트", + "price": 7700, + "latitude": 36.8334141610709, + "longitude": 127.146148378172, + "pageIndex": 6, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "13848", + "name": "백운면", + "categoryName": "한식", + "address": "전북특별자치도 전주시 덕진구 떡전4길 20-1 (금암동)", + "phone": "063-277-1007", + "representativeMenu": "잔치국수", + "price": 5000, + "latitude": 35.83744887478051, + "longitude": 127.12964026212448, + "pageIndex": 6, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "13852", + "name": "백운면아중점", + "categoryName": "한식", + "address": "전북특별자치도 전주시 덕진구 가재미로 38 (인후동1가) 1층", + "phone": "063-714-3877", + "representativeMenu": "잔치국수", + "price": 5900, + "latitude": 35.82849083250596, + "longitude": 127.16271510485184, + "pageIndex": 6, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5414", + "name": "빵굽는나라", + "categoryName": "베이커리", + "address": "전북특별자치도 전주시 완산구 팔달로 263", + "phone": "063-275-2974", + "representativeMenu": "도너츠(2개)", + "price": 1000, + "latitude": 35.8254765184598, + "longitude": 127.143901262223, + "pageIndex": 6, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5439", + "name": "서신정", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 감나무4길 26", + "phone": "063-277-3455", + "representativeMenu": "고등어구이", + "price": 10000, + "latitude": 35.8317212468129, + "longitude": 127.119926240397, + "pageIndex": 6, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "15145", + "name": "복스짜장짬뽕(용해점)", + "categoryName": "중식", + "address": "전라남도 목포시 대양로 111 (용해동) 복스짜장짬뽕(용해점)", + "phone": "061-277-2737", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 34.82166789677919, + "longitude": 126.39167301998671, + "pageIndex": 6, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5609", + "name": "삼국지", + "categoryName": "중식", + "address": "전라남도 목포시 하당남부로 108 (옥암동)", + "phone": "061-284-8775", + "representativeMenu": "자장면", + "price": 6000, + "latitude": 34.8022264162699, + "longitude": 126.435217612049, + "pageIndex": 6, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "16968", + "name": "서울대중", + "categoryName": "중식", + "address": "전남 목포시 소영길 7 (용당동)", + "phone": "061-276-0242", + "representativeMenu": "짜장", + "price": 6500, + "latitude": 34.8046888223113, + "longitude": 126.395998892103, + "pageIndex": 6, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5617", + "name": "손가네분식", + "categoryName": "한식", + "address": "전라남도 목포시 남해로 8-1 (산정동)", + "phone": "061-242-3375", + "representativeMenu": "김밥", + "price": 2500, + "latitude": 34.7900774150742, + "longitude": 126.396716006428, + "pageIndex": 6, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6374", + "name": "국수마을", + "categoryName": "한식", + "address": "경상북도 포항시 남구 효자동5번길 10 나동 국수마을", + "phone": "-", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 36.0078387636712, + "longitude": 129.329195759208, + "pageIndex": 6, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "14723", + "name": "국시묵자", + "categoryName": "한식", + "address": "경상북도 포항시 남구 장기면 동해안로 3271-1 -", + "phone": "-", + "representativeMenu": "잔치국수", + "price": 6000, + "latitude": 35.87906957004322, + "longitude": 129.5171647482038, + "pageIndex": 6, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "10551", + "name": "굿맨", + "categoryName": "이용업", + "address": "경상북도 포항시 북구 아치로 57 상가동 101호 (우현동,화성아파트)", + "phone": "-", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.0511457346007, + "longitude": 129.355698800567, + "pageIndex": 6, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "16131", + "name": "권민선남성컷", + "categoryName": "미용업", + "address": "경상북도 포항시 남구 상공로221번길 28 (해도동) 권민선남성컷", + "phone": "054-277-4596", + "representativeMenu": "컷트", + "price": 10000, + "latitude": 36.02519921582511, + "longitude": 129.3695250773602, + "pageIndex": 6, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "14472", + "name": "다래성", + "categoryName": "중식", + "address": "경상남도 창원시 마산합포구 교방북2길 1 (교방동) 육일약국 인근", + "phone": "055-243-6166", + "representativeMenu": "짜장면", + "price": 7000, + "latitude": 35.2148898765735, + "longitude": 128.56421595996898, + "pageIndex": 6, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "13714", + "name": "다래원", + "categoryName": "한식", + "address": "경상남도 창원시 성산구 신촌로 21 (신촌동) 지하 21호(신촌동, 유신종합상가)", + "phone": "055-261-1930", + "representativeMenu": "한식부페", + "price": 7000, + "latitude": 35.20756422962718, + "longitude": 128.62678091758497, + "pageIndex": 6, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "13719", + "name": "다복식당", + "categoryName": "한식", + "address": "경상남도 창원시 마산합포구 진동면 진북산업로 82 1층", + "phone": "055-271-7783", + "representativeMenu": "한식(뷔페)", + "price": 6000, + "latitude": 35.123393709489115, + "longitude": 128.4878116919056, + "pageIndex": 6, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "6810", + "name": "다복한식부페", + "categoryName": "한식", + "address": "경상남도 창원시 성산구 동산로65번길 33 (상남동)", + "phone": "055-282-1212", + "representativeMenu": "뷔페 1인", + "price": 7000, + "latitude": 35.2170705144953, + "longitude": 128.684009304443, + "pageIndex": 6, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "7201", + "name": "금천미용실", + "categoryName": "미용업", + "address": "제주특별자치도 제주시 중앙로23길 6 .", + "phone": "064-758-9254", + "representativeMenu": "커트", + "price": 10000, + "latitude": 33.5069619652156, + "longitude": 126.525476490278, + "pageIndex": 6, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7019", + "name": "김녕빵집", + "categoryName": "베이커리", + "address": "제주특별자치도 제주시 구좌읍 김녕로1길 25 1층", + "phone": "-", + "representativeMenu": "소금버터빵", + "price": 2100, + "latitude": 33.5559045926848, + "longitude": 126.747620056758, + "pageIndex": 6, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "15002", + "name": "스윗샐러드", + "categoryName": "한식", + "address": "서울특별시 종로구 종로1길 50 (중학동) 지하1층", + "phone": "02-733-3225", + "representativeMenu": "버섯샐러드", + "price": 7900, + "latitude": 37.57491239937121, + "longitude": 126.97896535083947, + "pageIndex": 7, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "943", + "name": "약속커피숍", + "categoryName": "기타요식업", + "address": "서울특별시 종로구 종로 302 (창신동)", + "phone": "02-764-1100", + "representativeMenu": "맥심커피", + "price": 2000, + "latitude": 37.5716332944196, + "longitude": 127.01140072111, + "pageIndex": 7, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "947", + "name": "영미용실", + "categoryName": "미용업", + "address": "서울특별시 종로구 지봉로 62 (숭인동)", + "phone": "02-743-6052", + "representativeMenu": "커트", + "price": 8000, + "latitude": 37.5755618391963, + "longitude": 127.015850681862, + "pageIndex": 7, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1655", + "name": "민아식당", + "categoryName": "한식", + "address": "부산광역시 중구 충장대로9번길 21-1 (중앙동4가)", + "phone": "051-462-1774", + "representativeMenu": "따로국밥", + "price": 9000, + "latitude": 35.107289725493, + "longitude": 129.037456085025, + "pageIndex": 7, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1638", + "name": "석기시대", + "categoryName": "중식", + "address": "부산광역시 중구 동광길 75-1 1층(동광동5가)", + "phone": "-", + "representativeMenu": "만둣국", + "price": 7000, + "latitude": 35.1061565412503, + "longitude": 129.034868484553, + "pageIndex": 7, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "17050", + "name": "양가손만두 부평직영점", + "categoryName": "한식", + "address": "부산광역시 중구 부평1길 48 (부평동2가) 1층", + "phone": "051-243-8436", + "representativeMenu": "고기찐만두", + "price": 7000, + "latitude": 35.101570977897914, + "longitude": 129.02602680604465, + "pageIndex": 7, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "14625", + "name": "용정반점", + "categoryName": "중식", + "address": "대구광역시 중구 중앙대로 376-16 (덕산동) .", + "phone": "053-423-8118", + "representativeMenu": "쇠고기짜장면", + "price": 5500, + "latitude": 35.8664000572041, + "longitude": 128.5943258541125, + "pageIndex": 7, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2018", + "name": "우리할매칼국수", + "categoryName": "한식", + "address": "대구광역시 중구 남산로7길 44 (남산)", + "phone": "053-255-8802", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 35.8587866686385, + "longitude": 128.580391726297, + "pageIndex": 7, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "14950", + "name": "윤소인고단백장어죽", + "categoryName": "한식", + "address": "대구광역시 중구 동성로 19-11 (사일동) 1층", + "phone": "053-255-7097", + "representativeMenu": "김치찌개", + "price": 6000, + "latitude": 35.86899504826444, + "longitude": 128.59462940407954, + "pageIndex": 7, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "14012", + "name": "옹진찰밥", + "categoryName": "한식", + "address": "인천광역시 중구 제물량로306번길 45 (북성동2가) 1층", + "phone": "032-765-2773", + "representativeMenu": "소라된장찌개", + "price": 8000, + "latitude": 37.476757097944784, + "longitude": 126.61796394907029, + "pageIndex": 7, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "13120", + "name": "우리두리머물다", + "categoryName": "기타요식업", + "address": "인천광역시 중구 제물량로166번길 5 (신생동) 1층", + "phone": "0507-1446-1503", + "representativeMenu": "미식혜(500ml)", + "price": 3000, + "latitude": 37.47050706598982, + "longitude": 126.62550477060184, + "pageIndex": 7, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "13187", + "name": "서림미용실", + "categoryName": "미용업", + "address": "광주광역시 동구 운림길 23 (운림동, 무등파크맨션) 상가동 202호", + "phone": "062-228-2909", + "representativeMenu": "커트(성인)", + "price": 10000, + "latitude": 35.13428906928481, + "longitude": 126.94139361034274, + "pageIndex": 7, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2284", + "name": "서석식당", + "categoryName": "한식", + "address": "광주광역시 동구 백서로 171-3 1층(서석동)", + "phone": "062-223-9009", + "representativeMenu": "낚지비빔밥", + "price": 9000, + "latitude": 35.1438778671915, + "longitude": 126.92342010806, + "pageIndex": 7, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2269", + "name": "수라상", + "categoryName": "한식", + "address": "광주광역시 동구 제봉로82번길 13-9 (동명동)", + "phone": "062-234-2002", + "representativeMenu": "찌개(된장순두부청국장김치)", + "price": 7000, + "latitude": 35.1469059969152, + "longitude": 126.922577872694, + "pageIndex": 7, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "13185", + "name": "옥땡김밥", + "categoryName": "한식", + "address": "광주광역시 동구 무등로 503-1 (산수동) 1층", + "phone": "062-263-0527", + "representativeMenu": "김밥", + "price": 2500, + "latitude": 35.15563375812957, + "longitude": 126.93466382911126, + "pageIndex": 7, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "18131", + "name": "별이네 분식", + "categoryName": "한식", + "address": "대전광역시 동구 비래서로42번길 70 (가양동) (가양동)", + "phone": "042-673-7737", + "representativeMenu": "잔치국수", + "price": 5000, + "latitude": 36.3512336467034, + "longitude": 127.44731554696641, + "pageIndex": 7, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2531", + "name": "복지이용원", + "categoryName": "이용업", + "address": "대전광역시 동구 대전로 791번길 50 (중동)", + "phone": "042-251-8428", + "representativeMenu": "이용료", + "price": 7000, + "latitude": 36.3295205271986, + "longitude": 127.430506838139, + "pageIndex": 7, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "16673", + "name": "자금성", + "categoryName": "중식", + "address": "울산광역시 중구 구교9길 64 (반구동) 1층", + "phone": "052-292-2346", + "representativeMenu": "자장면", + "price": 5500, + "latitude": 35.56321242205221, + "longitude": 129.3378786473305, + "pageIndex": 7, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2997", + "name": "장현돼지국밥", + "categoryName": "한식", + "address": "울산광역시 중구 종가로 745 (장현동)", + "phone": "052-293-0392", + "representativeMenu": "돼지국밥", + "price": 8000, + "latitude": 35.5881230965409, + "longitude": 129.343478559476, + "pageIndex": 7, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "10188", + "name": "엉클생고기(보람동)", + "categoryName": "한식", + "address": "세종특별자치시 호려울로 29 207호", + "phone": "044-868-9242", + "representativeMenu": "파불고기+공기밥된장찌개(점심특선)", + "price": 10000, + "latitude": 36.4779099639115, + "longitude": 127.290238012216, + "pageIndex": 7, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10193", + "name": "엉클생고기(아름동)", + "categoryName": "한식", + "address": "세종특별자치시 보듬3로 100 209, 210호", + "phone": "044-862-0267", + "representativeMenu": "파불고기+공기밥된장찌개 점십특선", + "price": 10000, + "latitude": 36.5119529197334, + "longitude": 127.248623728597, + "pageIndex": 7, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "3363", + "name": "당수여성사우나", + "categoryName": "목욕업", + "address": "경기도 수원시 권선구 당진로15번길 56 (당수동)", + "phone": "031-419-9042", + "representativeMenu": "목욕료", + "price": 8000, + "latitude": 37.2920971102154, + "longitude": 126.938984343475, + "pageIndex": 7, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "3428", + "name": "대복식당", + "categoryName": "한식", + "address": "경기도 수원시 팔달구 매산로84-1 (매산로3가)", + "phone": "031-238-2080", + "representativeMenu": "한식뷔페", + "price": 6000, + "latitude": 37.269844360739, + "longitude": 127.010370350622, + "pageIndex": 7, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "11723", + "name": "뚝배기마을", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 중앙로37-3 (중앙로1가)", + "phone": "033-255-5455", + "representativeMenu": "된장찌개", + "price": 7000, + "latitude": 37.8814765592669, + "longitude": 127.728258889089, + "pageIndex": 7, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4381", + "name": "롯데자율식당", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 공지로 387 지하층(약사동)", + "phone": "0507-1395-5955", + "representativeMenu": "한식뷔페", + "price": 8000, + "latitude": 37.8716410438521, + "longitude": 127.723885862381, + "pageIndex": 7, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4356", + "name": "만리향", + "categoryName": "중식", + "address": "강원특별자치도 춘천시 명동길29번길 4-5 (죽림동)", + "phone": "033-251-2227", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 37.8779879948066, + "longitude": 127.725893087544, + "pageIndex": 7, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "16642", + "name": "머릿결사랑", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 칠전서길 15-2 (칠전동, 칠전대우2차아파트)", + "phone": "033-253-8940", + "representativeMenu": "커트", + "price": 10000, + "latitude": 37.8403768426874, + "longitude": 127.71243175796782, + "pageIndex": 7, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "16981", + "name": "남문갤러리카페", + "categoryName": "기타요식업", + "address": "충청북도 청주시 상당구 무심동로336번길 105 (남문로2가) (남문로2가)", + "phone": "0507-1437-8606", + "representativeMenu": "아메리카노", + "price": 3000, + "latitude": 36.63162469216909, + "longitude": 127.4888655463895, + "pageIndex": 7, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "16070", + "name": "남원추어탕", + "categoryName": "한식", + "address": "충청북도 청주시 흥덕구 풍산로25번길 38-1 (가경동)", + "phone": "043-236-8959", + "representativeMenu": "찰솥밥추어탕", + "price": 10000, + "latitude": 36.62809148912409, + "longitude": 127.43233008358878, + "pageIndex": 7, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4938", + "name": "늑대골", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 풍세로696 (구룡동)", + "phone": "041-573-5122", + "representativeMenu": "백반", + "price": 8000, + "latitude": 36.7722721143035, + "longitude": 127.13808266914, + "pageIndex": 7, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "19291", + "name": "단대로", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 단대로 110 (신부동) 가동 1층", + "phone": "041-562-7767", + "representativeMenu": "삼겹살 200g", + "price": 7000, + "latitude": 36.83081336695418, + "longitude": 127.16257265306993, + "pageIndex": 7, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "18728", + "name": "달식당", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 대흥로 215 (대흥동) 달식당", + "phone": "041-557-2266", + "representativeMenu": "냉모밀", + "price": 8000, + "latitude": 36.80733781767005, + "longitude": 127.14715925093927, + "pageIndex": 7, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "5440", + "name": "섬진강백반", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 꽃밭정로 13", + "phone": "063-226-6809", + "representativeMenu": "김치찌개", + "price": 7000, + "latitude": 35.7967253851275, + "longitude": 127.134316971962, + "pageIndex": 7, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5426", + "name": "세은이네집", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 풍남문2길 42-3 1층", + "phone": "063-283-3376", + "representativeMenu": "국수", + "price": 7000, + "latitude": 35.8135584755908, + "longitude": 127.146849696525, + "pageIndex": 7, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5415", + "name": "수제왕돈까스(경원점)", + "categoryName": "양식", + "address": "전북특별자치도 전주시 완산구 충경로 84", + "phone": "063-282-8855", + "representativeMenu": "기본돈가스", + "price": 7500, + "latitude": 35.8187713842813, + "longitude": 127.148157077333, + "pageIndex": 7, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5442", + "name": "신뱅이", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 경기전길 153-9", + "phone": "063-282-3030", + "representativeMenu": "야채비빔밥", + "price": 8000, + "latitude": 35.8126362604144, + "longitude": 127.151819419275, + "pageIndex": 7, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "11789", + "name": "시골집 순두부(연산)", + "categoryName": "한식", + "address": "전라남도 목포시 연산로 140 (연산동)", + "phone": "061-274-9999", + "representativeMenu": "순두부", + "price": 9000, + "latitude": 34.8096541758217, + "longitude": 126.3815570128, + "pageIndex": 7, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "15155", + "name": "어락", + "categoryName": "한식", + "address": "전라남도 목포시 평화로 45 (상동) 어락", + "phone": "061-287-8789", + "representativeMenu": "장어탕", + "price": 10000, + "latitude": 34.795442625350994, + "longitude": 126.43019587300199, + "pageIndex": 7, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "11788", + "name": "연산순대국밥", + "categoryName": "한식", + "address": "전라남도 목포시 후광대로 105번안길 21 (옥암동)", + "phone": "061-285-4012", + "representativeMenu": "순대국밥", + "price": 9000, + "latitude": 34.8114935235184, + "longitude": 126.44243592283, + "pageIndex": 7, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "10499", + "name": "영빈미용실", + "categoryName": "미용업", + "address": "전라남도 목포시 삼학로343번길 6 (용해동)", + "phone": "-", + "representativeMenu": "커트(기본)", + "price": 7000, + "latitude": 34.8018144738611, + "longitude": 126.408611997702, + "pageIndex": 7, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6430", + "name": "권헤어", + "categoryName": "미용업", + "address": "경상북도 포항시 북구 죽도시장5길 4-6 권헤어", + "phone": "-", + "representativeMenu": "커트(여성)", + "price": 10000, + "latitude": 36.0353351270253, + "longitude": 129.366985164135, + "pageIndex": 7, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6323", + "name": "그린빌미용실", + "categoryName": "미용업", + "address": "경상북도 포항시 남구 대이로 138 그린빌미용실", + "phone": "054-274-0450", + "representativeMenu": "여자커트", + "price": 10000, + "latitude": 36.0267483836043, + "longitude": 129.341432453617, + "pageIndex": 7, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6369", + "name": "그린손칼국수", + "categoryName": "한식", + "address": "경상북도 포항시 남구 오천읍 장기로1690번길 7-1 1층", + "phone": "-", + "representativeMenu": "손칼국수", + "price": 3000, + "latitude": 35.9662564080845, + "longitude": 129.4171669752, + "pageIndex": 7, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "15982", + "name": "다정다맛", + "categoryName": "한식", + "address": "경상남도 창원시 의창구 퇴촌로25번길 6-31 (사림동) 다정다맛", + "phone": "055-284-6114", + "representativeMenu": "김치찌개", + "price": 7000, + "latitude": 35.24267515982134, + "longitude": 128.68959344279395, + "pageIndex": 7, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "6834", + "name": "대도식당", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 중원로 67-14 (평안동)", + "phone": "055-546-3458", + "representativeMenu": "추어탕", + "price": 9000, + "latitude": 35.1504050990581, + "longitude": 128.659012145261, + "pageIndex": 7, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "17127", + "name": "대림식당", + "categoryName": "한식", + "address": "경상남도 창원시 마산회원구 회원동24길 36 (회원동) 대림식당", + "phone": "055-223-2407", + "representativeMenu": "삼겹살", + "price": 8000, + "latitude": 35.21968201084791, + "longitude": 128.57667591118778, + "pageIndex": 7, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "14394", + "name": "대복갈비", + "categoryName": "한식", + "address": "경남 창원시 마산회원구 3.15성역로 131 대복갈비(구암동)", + "phone": "0507-1351-3061", + "representativeMenu": "물냉면", + "price": 7000, + "latitude": 35.2520164772151, + "longitude": 128.593762598517, + "pageIndex": 7, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "17558", + "name": "완판생국수", + "categoryName": "한식", + "address": "서울특별시 종로구 종로 258 (종로6가) 1층", + "phone": "02-2272-0066", + "representativeMenu": "멸치국수", + "price": 5500, + "latitude": 37.5706572089172, + "longitude": 127.00647971676864, + "pageIndex": 8, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "944", + "name": "왕관커피숍", + "categoryName": "기타요식업", + "address": "서울특별시 종로구 종로 222 (종로5가)", + "phone": "02-2266-8365", + "representativeMenu": "커피", + "price": 2000, + "latitude": 37.570760396587, + "longitude": 127.002357657235, + "pageIndex": 8, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "19246", + "name": "유로시안커피전문점", + "categoryName": "기타요식업", + "address": "서울특별시 종로구 종로40길 4 (종로6가) 1층", + "phone": "02-2268-1007", + "representativeMenu": "아메리카노(16oz) 포장", + "price": 1500, + "latitude": 37.57075442433323, + "longitude": 127.00654859833715, + "pageIndex": 8, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "17052", + "name": "오야꼬", + "categoryName": "일식", + "address": "부산광역시 중구 중구로 68 (대청동4가) 1층", + "phone": "051-462-8989", + "representativeMenu": "모밀셋트", + "price": 9000, + "latitude": 35.10349395265852, + "longitude": 129.02867590895872, + "pageIndex": 8, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1635", + "name": "용두산이용원", + "categoryName": "이용업", + "address": "부산광역시 중구 남포길 43 (남포동2가)", + "phone": "051-242-7007", + "representativeMenu": "커트", + "price": 5000, + "latitude": 35.0981609773096, + "longitude": 129.033460927363, + "pageIndex": 8, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1658", + "name": "일미기사식당", + "categoryName": "한식", + "address": "부산광역시 중구 흑교로 16-1 (부평동)", + "phone": "051-253-4440", + "representativeMenu": "시락국밥", + "price": 4500, + "latitude": 35.1008721419571, + "longitude": 129.02530342269, + "pageIndex": 8, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "14157", + "name": "재성밀면", + "categoryName": "한식", + "address": "부산광역시 중구 중앙대로41번길 14-1 (동광동3가) 1층", + "phone": "051-242-3836", + "representativeMenu": "물밀면", + "price": 7500, + "latitude": 35.1011509548915, + "longitude": 129.03476576507273, + "pageIndex": 8, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "2013", + "name": "장성루", + "categoryName": "중식", + "address": "대구광역시 중구 국채보상로139길 10 (동인동4가) 장성루", + "phone": "053-425-4303", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 35.86951806204098, + "longitude": 128.606526645754, + "pageIndex": 8, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2025", + "name": "장셰프한식전통중화요리", + "categoryName": "중식", + "address": "대구광역시 중구 경상감영길 117-7 (향촌동)", + "phone": "053-214-0706", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 35.872626221362, + "longitude": 128.593626482761, + "pageIndex": 8, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "10078", + "name": "제일분식", + "categoryName": "한식", + "address": "대구광역시 중구 남산로2길 26 (남산동)", + "phone": "053-256-6630", + "representativeMenu": "떡볶이", + "price": 1500, + "latitude": 35.8569075638758, + "longitude": 128.584273183178, + "pageIndex": 8, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2014", + "name": "중해반점", + "categoryName": "중식", + "address": "대구광역시 중구 명륜로 63 (남산동)", + "phone": "053-255-8555", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 35.8617367040049, + "longitude": 128.591676584073, + "pageIndex": 8, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2224", + "name": "월미식당", + "categoryName": "한식", + "address": "인천광역시 중구 월미로234번길 6 (북성동1가)", + "phone": "032-761-6800", + "representativeMenu": "짜장면", + "price": 7000, + "latitude": 37.4754721671142, + "longitude": 126.598889031169, + "pageIndex": 8, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "13118", + "name": "일등짬뽕", + "categoryName": "중식", + "address": "인천광역시 중구 운중로 28 (운남동) 1층", + "phone": "032-747-0660", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 37.493135983340906, + "longitude": 126.53660042079217, + "pageIndex": 8, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2270", + "name": "우리뷔페", + "categoryName": "한식", + "address": "광주광역시 동구 금남로 181 1층 102호(금남로5가)", + "phone": "062-364-3816", + "representativeMenu": "백반", + "price": 6000, + "latitude": 35.1519634025693, + "longitude": 126.913207209362, + "pageIndex": 8, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "14226", + "name": "이가진우동", + "categoryName": "한식", + "address": "광주광역시 동구 학소로 141 (학동) 1층", + "phone": "062-233-3488", + "representativeMenu": "우동", + "price": 5500, + "latitude": 35.13224809736366, + "longitude": 126.93196652792969, + "pageIndex": 8, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2565", + "name": "부림식당", + "categoryName": "한식", + "address": "대전광역시 동구 중앙로 200번길 27 (중동)", + "phone": "042-226-8346", + "representativeMenu": "된장찌개 백반", + "price": 7000, + "latitude": 36.329614784984, + "longitude": 127.43223119972, + "pageIndex": 8, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2532", + "name": "뿌리미용실", + "categoryName": "미용업", + "address": "대전광역시 동구 충무로 219 (인동)", + "phone": "042-286-4444", + "representativeMenu": "미용료(커트)", + "price": 8000, + "latitude": 36.3225191758149, + "longitude": 127.440235253078, + "pageIndex": 8, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "17349", + "name": "좋은고기팜", + "categoryName": "한식", + "address": "울산광역시 중구 유곡로 58 (유곡동) 1층", + "phone": "0507-1342-5431", + "representativeMenu": "생 삼겹살(100g)", + "price": 6000, + "latitude": 35.557166607144175, + "longitude": 129.3034434213232, + "pageIndex": 8, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "11315", + "name": "탕짜", + "categoryName": "중식", + "address": "울산광역시 중구 시계탑거리 6 (성남동)", + "phone": "052-246-5566", + "representativeMenu": "짜장면", + "price": 6500, + "latitude": 35.5550431249539, + "longitude": 129.321593713924, + "pageIndex": 8, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "3001", + "name": "태화식육식당", + "categoryName": "한식", + "address": "울산광역시 중구 중앙시장길 21-3 (성남동)", + "phone": "052-244-7968", + "representativeMenu": "삼겹살(100g)", + "price": 4700, + "latitude": 35.5552643330267, + "longitude": 129.323407871925, + "pageIndex": 8, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "10178", + "name": "옛날통닭", + "categoryName": "한식", + "address": "세종특별자치시 조치원읍 조치원6길 16 조치원읍", + "phone": "-", + "representativeMenu": "옛날통닭", + "price": 7500, + "latitude": 36.6009257081913, + "longitude": 127.299491553093, + "pageIndex": 8, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "19180", + "name": "오가네빵굼터", + "categoryName": "기타요식업", + "address": "세종특별자치시 조치원읍 조치원6길 27 1층", + "phone": "044-864-0261", + "representativeMenu": "단팥빵", + "price": 800, + "latitude": 36.600210807134104, + "longitude": 127.2997884074273, + "pageIndex": 8, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10191", + "name": "유정칼국수", + "categoryName": "한식", + "address": "세종특별자치시 장군면 장척로 417-1", + "phone": "044-857-5999", + "representativeMenu": "돌솥비빔밥", + "price": 8000, + "latitude": 36.4980690848686, + "longitude": 127.206482661374, + "pageIndex": 8, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "16805", + "name": "대왕칼국수", + "categoryName": "한식", + "address": "경기도 수원시 팔달구 창룡대로7번길 11 (북수동) 1층", + "phone": "031-252-2820", + "representativeMenu": "칼국수 보통", + "price": 6000, + "latitude": 37.28246168863594, + "longitude": 127.01757013330725, + "pageIndex": 8, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "19003", + "name": "데일리샌듀(DailySandew)", + "categoryName": "베이커리", + "address": "경기도 수원시 영통구 광교중앙로248번길 95-1 (하동) 102호", + "phone": "-", + "representativeMenu": "단호박샌드위치", + "price": 7000, + "latitude": 37.291532557494804, + "longitude": 127.06836414210785, + "pageIndex": 8, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "14336", + "name": "도담칡냉면", + "categoryName": "한식", + "address": "경기도 수원시 장안구 수성로382번길 32-3 (영화동) .", + "phone": "031-247-0925", + "representativeMenu": "냉면", + "price": 7000, + "latitude": 37.29270733638951, + "longitude": 127.01452356573881, + "pageIndex": 8, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "14020", + "name": "동남사우나", + "categoryName": "목욕업", + "address": "경기도 수원시 장안구 영화로25번길 22 (영화동, 태영아파트) .", + "phone": "031-243-6422", + "representativeMenu": "목욕비", + "price": 10000, + "latitude": 37.288921701882735, + "longitude": 127.00821781483458, + "pageIndex": 8, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "4328", + "name": "메이카드", + "categoryName": "기타요식업", + "address": "강원특별자치도 춘천시 복골길6번길 21 1층(후평동)", + "phone": "0507-1391-1402", + "representativeMenu": "아메리카노", + "price": 3500, + "latitude": 37.8743681928941, + "longitude": 127.755540944248, + "pageIndex": 8, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4332", + "name": "바위목욕탕", + "categoryName": "목욕업", + "address": "강원특별자치도 춘천시 춘천로213번길 9 (효자동)", + "phone": "033-253-3610", + "representativeMenu": "대인", + "price": 8000, + "latitude": 37.8781770353052, + "longitude": 127.737068247626, + "pageIndex": 8, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4382", + "name": "바탕골분식", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 서부대성로44번길 19 (요선동)", + "phone": "033-254-3564", + "representativeMenu": "김치찌개", + "price": 8000, + "latitude": 37.88168933514, + "longitude": 127.727315490081, + "pageIndex": 8, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4759", + "name": "내고향맛집", + "categoryName": "한식", + "address": "충청북도 청주시 흥덕구 운천로3번길 2 (운천동)", + "phone": "043-276-4414", + "representativeMenu": "청국장", + "price": 7000, + "latitude": 36.6506697774039, + "longitude": 127.474773448765, + "pageIndex": 8, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "14757", + "name": "내수시민미용실", + "categoryName": "미용업", + "address": "충청북도 청주시 청원구 내수읍 내수로 729-2", + "phone": "043-214-5124", + "representativeMenu": "커트", + "price": 6000, + "latitude": 36.72673097882712, + "longitude": 127.53610288381759, + "pageIndex": 8, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "13536", + "name": "네모샘밥집", + "categoryName": "한식", + "address": "충청북도 청주시 청원구 상당로232번길 34 (우암동)", + "phone": "-", + "representativeMenu": "보리밥", + "price": 6000, + "latitude": 36.64906312692569, + "longitude": 127.49115091042215, + "pageIndex": 8, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15580", + "name": "더드림빵", + "categoryName": "베이커리", + "address": "충청남도 천안시 동남구 차돌고개5길 15 (다가동) 1동 102호", + "phone": "041-577-4473", + "representativeMenu": "단팥빵/소보로빵/슈크림빵/생크림빵", + "price": 1500, + "latitude": 36.79908109750335, + "longitude": 127.13558280814556, + "pageIndex": 8, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4912", + "name": "동은헤어", + "categoryName": "미용업", + "address": "충청남도 천안시 동남구 새말3길 38-4 지하1층(신방동)", + "phone": "070-4036-7721", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.7945688750786, + "longitude": 127.131044743835, + "pageIndex": 8, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "15798", + "name": "동화당만두찐빵", + "categoryName": "베이커리", + "address": "충청남도 천안시 서북구 미라8길 15 (쌍용동) 101호", + "phone": "041-577-7238", + "representativeMenu": "고기/김치만두(10개)", + "price": 5500, + "latitude": 36.80331426026365, + "longitude": 127.12911330329518, + "pageIndex": 8, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "5411", + "name": "아리랑하우스", + "categoryName": "한식", + "address": "전북특별자치도 전주시 덕진구 한배미1길 29", + "phone": "063-241-9300", + "representativeMenu": "한우탕", + "price": 9000, + "latitude": 35.8236184860742, + "longitude": 127.165468633315, + "pageIndex": 8, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5403", + "name": "야미전북대점", + "categoryName": "양식", + "address": "전북특별자치도 전주시 덕진구 명륜4길 21-5 2층", + "phone": "063-277-6678", + "representativeMenu": "수제 생돈가스", + "price": 6900, + "latitude": 35.843953906049, + "longitude": 127.126451107898, + "pageIndex": 8, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "18216", + "name": "에바다미용실", + "categoryName": "미용업", + "address": "전북특별자치도 전주시 완산구 관선1길 74-4 (남노송동) 1층", + "phone": "-", + "representativeMenu": "커트", + "price": 10000, + "latitude": 35.81910634092517, + "longitude": 127.15785208426966, + "pageIndex": 8, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "15996", + "name": "영아네밥상", + "categoryName": "한식", + "address": "전북특별자치도 전주시 덕진구 송천로 35-4 (송천동1가)", + "phone": "063-274-6598", + "representativeMenu": "한식뷔페(점심)", + "price": 8000, + "latitude": 35.856686667251026, + "longitude": 127.1180991261228, + "pageIndex": 8, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5622", + "name": "예향밥상", + "categoryName": "한식", + "address": "전라남도 목포시 수문로20번길 7-3 1층(남교동)", + "phone": "061-247-8989", + "representativeMenu": "한식뷔페", + "price": 7000, + "latitude": 34.7925532071234, + "longitude": 126.383301637162, + "pageIndex": 8, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "10496", + "name": "옛날짜장", + "categoryName": "한식", + "address": "전라남도 목포시 산정로 120-1 (산정동)", + "phone": "061-272-1872", + "representativeMenu": "옛날짜장", + "price": 6000, + "latitude": 34.7992827458212, + "longitude": 126.393162428108, + "pageIndex": 8, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "10502", + "name": "용일식육식당", + "categoryName": "한식", + "address": "전라남도 목포시 양을로196번길 7 (용당동, 용일아파트)", + "phone": "061-272-0184", + "representativeMenu": "백반", + "price": 9000, + "latitude": 34.8104772153102, + "longitude": 126.391872175823, + "pageIndex": 8, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "10500", + "name": "우아미미용실", + "categoryName": "미용업", + "address": "전라남도 목포시 영산로250번길 13 (용당동)", + "phone": "061-277-1869", + "representativeMenu": "커트(기본)", + "price": 5000, + "latitude": 34.8020067240489, + "longitude": 126.39447577251, + "pageIndex": 8, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "11261", + "name": "기계반점", + "categoryName": "중식", + "address": "경상북도 포항시 북구 기계면 자조길 15 1층", + "phone": "054-246-4244", + "representativeMenu": "짬뽕", + "price": 8000, + "latitude": 36.070902333521, + "longitude": 129.211627068839, + "pageIndex": 8, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6831", + "name": "대봉막창", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 벚꽃로60번길 25 1층 1025호(화천동, 중앙시장)", + "phone": "055-545-7292", + "representativeMenu": "돼지막창", + "price": 9000, + "latitude": 35.1510678992756, + "longitude": 128.665642625987, + "pageIndex": 8, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "14220", + "name": "대패3000냥삼겹살", + "categoryName": "한식", + "address": "경상남도 창원시 마산합포구 산호남로 11 (산호동) 1층", + "phone": "055-223-3538", + "representativeMenu": "대패삼겹살(1인분_기본5인)", + "price": 3000, + "latitude": 35.21758679106357, + "longitude": 128.58131775121527, + "pageIndex": 8, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "15973", + "name": "돌솥밥 수", + "categoryName": "한식", + "address": "경상남도 창원시 의창구 용지로293번길 21 (사림동) 돌솥밥 수", + "phone": "055-261-2905", + "representativeMenu": "돌솥밥 정식", + "price": 10000, + "latitude": 35.240281774923226, + "longitude": 128.68795184702972, + "pageIndex": 8, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "7121", + "name": "김희선제주몸국", + "categoryName": "한식", + "address": "제주특별자치도 제주시 어영길 45-6 1층(용담삼동)", + "phone": "064-745-0047", + "representativeMenu": "몸국", + "price": 9000, + "latitude": 33.5185567991117, + "longitude": 126.49807778062, + "pageIndex": 8, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "14247", + "name": "꽃가마식당", + "categoryName": "한식", + "address": "제주특별자치도 제주시 신산로 97 (일도이동) 1층", + "phone": "064-755-6454", + "representativeMenu": "갈비탕", + "price": 10000, + "latitude": 33.50533987098967, + "longitude": 126.53587722621165, + "pageIndex": 8, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "18039", + "name": "꽃이피어나", + "categoryName": "미용업", + "address": "제주특별자치도 제주시 도남로 102-12 (도남동) 꽃이피어나", + "phone": "064-722-1202", + "representativeMenu": "커트", + "price": 10000, + "latitude": 33.49138991942305, + "longitude": 126.52711922981103, + "pageIndex": 8, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "948", + "name": "은미용실", + "categoryName": "미용업", + "address": "서울특별시 종로구 사직로 108-6 (내자동)", + "phone": "02-738-1882", + "representativeMenu": "커트", + "price": 5000, + "latitude": 37.5757821511679, + "longitude": 126.970611077009, + "pageIndex": 9, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "19244", + "name": "을밀대 (소문난국밥전문)", + "categoryName": "한식", + "address": "서울특별시 종로구 수표로 131 (낙원동) 1층", + "phone": "02-742-1633", + "representativeMenu": "우거지해장국", + "price": 3000, + "latitude": 37.572493575407705, + "longitude": 126.98826091182536, + "pageIndex": 9, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "11182", + "name": "이레이발관", + "categoryName": "이용업", + "address": "서울특별시 종로구 수표로 115 (낙원동)", + "phone": "010-7192-6413", + "representativeMenu": "커트", + "price": 7000, + "latitude": 37.5715425171338, + "longitude": 126.989053894726, + "pageIndex": 9, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1659", + "name": "전주식당", + "categoryName": "한식", + "address": "부산광역시 중구 대청로 137번길9-1 (중앙동)", + "phone": "051-469-0771", + "representativeMenu": "돌솥밥", + "price": 9000, + "latitude": 35.1036816954121, + "longitude": 129.034881739077, + "pageIndex": 9, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "19098", + "name": "정가는집밥", + "categoryName": "한식", + "address": "부산광역시 중구 동광길 28 (중앙동4가) 정가는집밥", + "phone": "051-240-1138", + "representativeMenu": "정식", + "price": 8000, + "latitude": 35.10397220382151, + "longitude": 129.03454710183587, + "pageIndex": 9, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "11185", + "name": "정원이용원", + "categoryName": "이용업", + "address": "부산광역시 중구 대청로134번길16 (동광동3가)", + "phone": "051-246-0108", + "representativeMenu": "커트", + "price": 10000, + "latitude": 35.1014290808544, + "longitude": 129.034814332461, + "pageIndex": 9, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "17980", + "name": "지정식당", + "categoryName": "한식", + "address": "대구광역시 중구 중앙대로58길 10 (남산동) 남산동", + "phone": "053-427-8322", + "representativeMenu": "순두부", + "price": 6000, + "latitude": 35.85740112997052, + "longitude": 128.59163907520352, + "pageIndex": 9, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2015", + "name": "천선루", + "categoryName": "중식", + "address": "대구광역시 중구 대봉로 261-1 (봉산동)", + "phone": "053-422-4222", + "representativeMenu": "짜장면", + "price": 5500, + "latitude": 35.8618460883915, + "longitude": 128.600047477155, + "pageIndex": 9, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "10070", + "name": "춘사김밥", + "categoryName": "한식", + "address": "대구광역시 중구 중앙대로 282 (남산동)", + "phone": "053-423-7827", + "representativeMenu": "참치김밥", + "price": 3000, + "latitude": 35.8583288163274, + "longitude": 128.591617516822, + "pageIndex": 9, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17981", + "name": "콩나물과시래기밥맛집", + "categoryName": "한식", + "address": "대구광역시 중구 약령길 58-7 (장관동) 1층", + "phone": "0507-1342-9756", + "representativeMenu": "콩나물국밥", + "price": 7000, + "latitude": 35.86881460101817, + "longitude": 128.5898667124599, + "pageIndex": 9, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2227", + "name": "장터삼겹살(신흥)", + "categoryName": "한식", + "address": "인천광역시 중구 서해대로454번길 11-3 (선화동)", + "phone": "032-887-5355", + "representativeMenu": "삼겹살(200g)", + "price": 10000, + "latitude": 37.466061476741, + "longitude": 126.635998763313, + "pageIndex": 9, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2228", + "name": "장터삼겹살(연안)", + "categoryName": "한식", + "address": "인천광역시 중구 연안부두로53번길 31 (항동7가)", + "phone": "032-882-6880", + "representativeMenu": "삼겹살 (180g)", + "price": 9000, + "latitude": 37.4534364723169, + "longitude": 126.604052571574, + "pageIndex": 9, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "15362", + "name": "인촌", + "categoryName": "한식", + "address": "광주광역시 동구 구성로194번길 20-1 (금남로4가) 1-2층", + "phone": "062-234-3389", + "representativeMenu": "고등어구이", + "price": 10000, + "latitude": 35.151739719004304, + "longitude": 126.91559598810717, + "pageIndex": 9, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "13190", + "name": "전지적돼지시점", + "categoryName": "한식", + "address": "광주광역시 동구 지산로 40 (지산동) 1층", + "phone": "0507-1354-1656", + "representativeMenu": "김치찌개", + "price": 7500, + "latitude": 35.147333475396294, + "longitude": 126.93215202959168, + "pageIndex": 9, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "11652", + "name": "종합분식", + "categoryName": "한식", + "address": "광주광역시 동구 장동로 23-3 (장동)", + "phone": "062-225-4928", + "representativeMenu": "김밥", + "price": 3000, + "latitude": 35.1482844984997, + "longitude": 126.924369524217, + "pageIndex": 9, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2546", + "name": "삼대째전통칼국수", + "categoryName": "한식", + "address": "대전광역시 동구 대전로 825번길 13 (정동)", + "phone": "042-257-5432", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 36.3325533506759, + "longitude": 127.430855541848, + "pageIndex": 9, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "13385", + "name": "생일집", + "categoryName": "한식", + "address": "대전광역시 동구 대전로791번길 45 (중동) (중동)", + "phone": "042-226-5254", + "representativeMenu": "순대국밥", + "price": 8000, + "latitude": 36.32931900162008, + "longitude": 127.43084985672475, + "pageIndex": 9, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2540", + "name": "성심관", + "categoryName": "중식", + "address": "대전광역시 동구 동대전로 208 (자양동)", + "phone": "042-637-2822", + "representativeMenu": "짜장면", + "price": 3000, + "latitude": 36.3401647426563, + "longitude": 127.449098715918, + "pageIndex": 9, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "11390", + "name": "태화장터칼국수", + "categoryName": "한식", + "address": "울산광역시 중구 화진길 11-4 (태화동)", + "phone": "052-244-6569", + "representativeMenu": "들깨칼국수", + "price": 8000, + "latitude": 35.5552335681624, + "longitude": 129.308547033364, + "pageIndex": 9, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "13648", + "name": "텃밭의반찬&분식", + "categoryName": "한식", + "address": "울산광역시 중구 계변고개 11 (복산동) .", + "phone": "-", + "representativeMenu": "된장찌개", + "price": 5000, + "latitude": 35.560346871362604, + "longitude": 129.328562284427, + "pageIndex": 9, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "18450", + "name": "파송송분식", + "categoryName": "한식", + "address": "울산광역시 중구 종가4길 5 (유곡동) 101호", + "phone": "0507-1342-4911", + "representativeMenu": "떡볶이", + "price": 3500, + "latitude": 35.56051072842763, + "longitude": 129.29590608898556, + "pageIndex": 9, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "10199", + "name": "자연셀프 세차장", + "categoryName": "기타비요식업", + "address": "세종특별자치시 금남면 용포로 32", + "phone": "-", + "representativeMenu": "고압세척(2분30초)", + "price": 1000, + "latitude": 36.4613848948516, + "longitude": 127.280691708831, + "pageIndex": 9, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "19178", + "name": "전주미담콩나물국바바", + "categoryName": "한식", + "address": "세종특별자치시 아름서1길 27 (아름동) 101호", + "phone": "0507-1440-1549", + "representativeMenu": "콩나물국밥", + "price": 5000, + "latitude": 36.51128159316062, + "longitude": 127.24363527534015, + "pageIndex": 9, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "15901", + "name": "정브레드", + "categoryName": "기타요식업", + "address": "세종특별자치시 새롬중앙1로 13 (새롬동, 새뜸마을6단지) 상가", + "phone": "044-864-3311", + "representativeMenu": "찹쌀 도너츠", + "price": 1000, + "latitude": 36.48442826252241, + "longitude": 127.25083904576256, + "pageIndex": 9, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "15900", + "name": "집밥한식뷔페", + "categoryName": "한식", + "address": "세종특별자치시 부강면 부강외천로 139 집밥한식뷔페", + "phone": "0507-1411-3828", + "representativeMenu": "한식뷔페", + "price": 6000, + "latitude": 36.52811606536304, + "longitude": 127.3824825445357, + "pageIndex": 9, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "3400", + "name": "동남세탁소", + "categoryName": "세탁업", + "address": "경기도 수원시 장안구 만석로101번길 46 (정자동)", + "phone": "031-253-1414", + "representativeMenu": "드라이클리닝", + "price": 7000, + "latitude": 37.3007439187584, + "longitude": 126.991377835815, + "pageIndex": 9, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "3393", + "name": "동아사우나", + "categoryName": "목욕업", + "address": "경기도 수원시 영통구 매여울로40번길 45 (매탄동)", + "phone": "031-214-3537", + "representativeMenu": "일반 입욕비", + "price": 10000, + "latitude": 37.2727443891937, + "longitude": 127.047494082233, + "pageIndex": 9, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "11391", + "name": "똘똘이네M카페", + "categoryName": "한식", + "address": "경기도 수원시 권선구 고색로 54번길66 (고색동)", + "phone": "031-278-5081", + "representativeMenu": "생삼겹(100g)", + "price": 3000, + "latitude": 37.2474159314554, + "longitude": 126.983768254338, + "pageIndex": 9, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "13795", + "name": "박사로냉면", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 서면 박사로 839 박사로냉면", + "phone": "0507-1399-2411", + "representativeMenu": "냉면", + "price": 7000, + "latitude": 37.8919570462785, + "longitude": 127.68961561665465, + "pageIndex": 9, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "11109", + "name": "박설매미용실", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 서부대성로 126-1 1층(운교동)", + "phone": "-", + "representativeMenu": "커트", + "price": 10000, + "latitude": 37.8783657017514, + "longitude": 127.73400940214, + "pageIndex": 9, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4342", + "name": "박혜숙헤어", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 춘천로309번길 7 (후평동)", + "phone": "033-257-1360", + "representativeMenu": "커트", + "price": 10000, + "latitude": 37.8823136801827, + "longitude": 127.746727606313, + "pageIndex": 9, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4383", + "name": "밥보네집", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 서부대성로 239번길 4 (효자동)", + "phone": "033-255-2123", + "representativeMenu": "제육볶음정식", + "price": 6000, + "latitude": 37.8730237470997, + "longitude": 127.744950136894, + "pageIndex": 9, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "15845", + "name": "다정순대국", + "categoryName": "한식", + "address": "충청북도 청주시 청원구 직지대로 844-2 (우암동)", + "phone": "043-274-1954", + "representativeMenu": "순대국밥", + "price": 8000, + "latitude": 36.64864564378414, + "longitude": 127.48481810685502, + "pageIndex": 9, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "16073", + "name": "달시쓰", + "categoryName": "기타요식업", + "address": "충청북도 청주시 서원구 창직로 104 (사직동) 1층", + "phone": "-", + "representativeMenu": "아메리카노", + "price": 2000, + "latitude": 36.63017696582773, + "longitude": 127.47323022163665, + "pageIndex": 9, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15579", + "name": "듀팡과자점", + "categoryName": "베이커리", + "address": "충청남도 천안시 서북구 늘푸른5길 22 (두정동) 102호", + "phone": "041-556-0456", + "representativeMenu": "슈크림빵", + "price": 1600, + "latitude": 36.827325601311905, + "longitude": 127.12967689750721, + "pageIndex": 9, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "14667", + "name": "디저트카페 아마이", + "categoryName": "기타요식업", + "address": "충청남도 천안시 동남구 신촌4로 34 (신방동) .", + "phone": "0507-1324-8466", + "representativeMenu": "아메리카노", + "price": 3500, + "latitude": 36.789176140906456, + "longitude": 127.1251417877315, + "pageIndex": 9, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "15305", + "name": "로봇카페 쉼21", + "categoryName": "기타요식업", + "address": "충청남도 천안시 서북구 불당21로 71 (불당동) 1층 108호", + "phone": "0507-1365-2171", + "representativeMenu": "아메리카노", + "price": 1500, + "latitude": 36.81296311764873, + "longitude": 127.1081762349608, + "pageIndex": 9, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "5445", + "name": "자유식당", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 풍남문3길 25 (전동)", + "phone": "063-286-1986", + "representativeMenu": "청국장", + "price": 6000, + "latitude": 35.8148512494836, + "longitude": 127.146919020928, + "pageIndex": 9, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5432", + "name": "전원갈비", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 서신천변로 57", + "phone": "063-242-8560", + "representativeMenu": "우렁쌈밥", + "price": 8000, + "latitude": 35.8306785212809, + "longitude": 127.114061757425, + "pageIndex": 9, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "18285", + "name": "울도숯불갈비", + "categoryName": "한식", + "address": "전라남도 목포시 대양로 107-1 (연산동) 1층", + "phone": "061-276-7800", + "representativeMenu": "돌판비빔밥", + "price": 9000, + "latitude": 34.821537871081894, + "longitude": 126.39121589517268, + "pageIndex": 9, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5624", + "name": "유달산추어탕", + "categoryName": "한식", + "address": "전라남도 목포시 노적봉길 22-1 (죽동)", + "phone": "061-270-7030", + "representativeMenu": "추어탕", + "price": 9000, + "latitude": 34.7900302636707, + "longitude": 126.383065291475, + "pageIndex": 9, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "11413", + "name": "유달손칼국수", + "categoryName": "한식", + "address": "전라남도 목포시 상동로 65 (상동) 유달손칼국수", + "phone": "061-278-1093", + "representativeMenu": "멸치칼국수", + "price": 7500, + "latitude": 34.81503671921055, + "longitude": 126.41272602035068, + "pageIndex": 9, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "10498", + "name": "일로순대", + "categoryName": "한식", + "address": "전라남도 목포시 삼일로13번길 4 (남교동)", + "phone": "061-243-9424", + "representativeMenu": "순대", + "price": 8000, + "latitude": 34.7936987314894, + "longitude": 126.383236507782, + "pageIndex": 9, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "18335", + "name": "기와집식당", + "categoryName": "한식", + "address": "경상북도 포항시 남구 연일읍 동문로 45-2 기와집식당", + "phone": "054-283-9888", + "representativeMenu": "소고기국밥", + "price": 8000, + "latitude": 35.994115629687634, + "longitude": 129.35170500121095, + "pageIndex": 9, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "14714", + "name": "길커피", + "categoryName": "기타요식업", + "address": "경상북도 포항시 남구 송도해안길73번길 2 (송도동) 1층", + "phone": "-", + "representativeMenu": "원두커피", + "price": 2000, + "latitude": 36.03715240915286, + "longitude": 129.37803218689874, + "pageIndex": 9, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "18614", + "name": "동마산시장 815족발", + "categoryName": "한식", + "address": "경상남도 창원시 마산회원구 합성남9길 64 (합성동) 1층", + "phone": "055-255-0815", + "representativeMenu": "족발한팩", + "price": 10000, + "latitude": 35.23951128222638, + "longitude": 128.58769435088436, + "pageIndex": 9, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "16320", + "name": "동북식당", + "categoryName": "한식", + "address": "경상남도 창원시 마산회원구 회원동15길 32 (회원동, 대양빌라) 동북식당", + "phone": "055-224-4004", + "representativeMenu": "한정식", + "price": 7000, + "latitude": 35.2204142401416, + "longitude": 128.57174853653618, + "pageIndex": 9, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "11421", + "name": "동촌손칼국수", + "categoryName": "한식", + "address": "경상남도 창원시 마산회원구 합성남3길 39 (합성동)", + "phone": "055-255-3378", + "representativeMenu": "손칼국수", + "price": 5500, + "latitude": 35.2388486973012, + "longitude": 128.585592677036, + "pageIndex": 9, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "7044", + "name": "남문이용원", + "categoryName": "이용업", + "address": "제주특별자치도 제주시 남성로 125 (삼도일동)", + "phone": "064-757-5935", + "representativeMenu": "일반컷트", + "price": 10000, + "latitude": 33.5090975162483, + "longitude": 126.518756056763, + "pageIndex": 9, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7222", + "name": "남촌식당", + "categoryName": "한식", + "address": "제주특별자치도 제주시 서광로 174", + "phone": "064-753-1502", + "representativeMenu": "멸치국수", + "price": 6000, + "latitude": 33.4997144726038, + "longitude": 126.514871117614, + "pageIndex": 9, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "10624", + "name": "남현순대", + "categoryName": "한식", + "address": "제주특별자치도 제주시 천수로 56 108동 102호", + "phone": "064-751-3374", + "representativeMenu": "멸치국수", + "price": 7000, + "latitude": 33.5039520338025, + "longitude": 126.542601590797, + "pageIndex": 9, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "19061", + "name": "이모네감자탕", + "categoryName": "한식", + "address": "서울특별시 종로구 새문안로9길 29-3 (당주동) 1층", + "phone": "02-733-7314", + "representativeMenu": "뼈해장국", + "price": 9000, + "latitude": 37.571272473253565, + "longitude": 126.97522954478573, + "pageIndex": 10, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "950", + "name": "이소빈 머리나라", + "categoryName": "미용업", + "address": "서울특별시 종로구 명륜길 9 (명륜3가)", + "phone": "02-766-0545", + "representativeMenu": "커트", + "price": 10000, + "latitude": 37.5879678107949, + "longitude": 126.996150319087, + "pageIndex": 10, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "955", + "name": "인사동칼국수", + "categoryName": "한식", + "address": "서울특별시 종로구 인사동5길 25 1층", + "phone": "02-737-1151", + "representativeMenu": "칼국수", + "price": 7500, + "latitude": 37.5720973140597, + "longitude": 126.985392406484, + "pageIndex": 10, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "17053", + "name": "쭈니네부엌", + "categoryName": "한식", + "address": "부산광역시 중구 중구로 86 (대청동4가) 1층", + "phone": "051-467-9359", + "representativeMenu": "추억의쫄우동", + "price": 6000, + "latitude": 35.10453814088189, + "longitude": 129.03021358937883, + "pageIndex": 10, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1639", + "name": "청춘탕수육", + "categoryName": "중식", + "address": "부산광역시 중구 대청로135번길18 (중앙동4가)", + "phone": "051-468-3786", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 35.1043355825209, + "longitude": 129.034908852842, + "pageIndex": 10, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1633", + "name": "카페두콩", + "categoryName": "기타요식업", + "address": "부산광역시 중구 흑교로46번길8 (보수동1가)", + "phone": "-", + "representativeMenu": "아메리카노", + "price": 2500, + "latitude": 35.1034858345236, + "longitude": 129.025890398337, + "pageIndex": 10, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "18938", + "name": "커피컵피", + "categoryName": "기타요식업", + "address": "부산광역시 중구 대청로135번길 13-1 (중앙동4가) 1,2층", + "phone": "0507-2085-0982", + "representativeMenu": "커피", + "price": 2000, + "latitude": 35.10404106218001, + "longitude": 129.03464555501037, + "pageIndex": 10, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "10077", + "name": "특별한돼지국밥", + "categoryName": "한식", + "address": "대구광역시 중구 국채보상로140길 35 (동인동4가) .", + "phone": "053-425-5243", + "representativeMenu": "돼지국밥", + "price": 8000, + "latitude": 35.867239763231744, + "longitude": 128.60637360935215, + "pageIndex": 10, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "14629", + "name": "포항회식당", + "categoryName": "한식", + "address": "대구광역시 중구 대봉로 240 (대봉동, 대봉서한포레스트) 상가 106호. 포항회식당", + "phone": "053-422-4415", + "representativeMenu": "정식", + "price": 7000, + "latitude": 35.860129676053866, + "longitude": 128.6023289015774, + "pageIndex": 10, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17974", + "name": "한강반점", + "categoryName": "중식", + "address": "대구광역시 중구 달구벌대로387길 42 (대신동) 1층", + "phone": "053-253-7518", + "representativeMenu": "짜장면", + "price": 3000, + "latitude": 35.86500733659813, + "longitude": 128.57524030565048, + "pageIndex": 10, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2026", + "name": "한양뚝배기감자탕", + "categoryName": "한식", + "address": "대구광역시 중구 국채보상로140길 28 (동인동2가) .", + "phone": "053-424-7161", + "representativeMenu": "뚝배기", + "price": 7000, + "latitude": 35.86756096197846, + "longitude": 128.60615118148775, + "pageIndex": 10, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "14011", + "name": "진국", + "categoryName": "한식", + "address": "인천광역시 중구 제물량로 245-2 (항동1가) 1층", + "phone": "032-772-0321", + "representativeMenu": "콩나물국밥", + "price": 6000, + "latitude": 37.473760992186925, + "longitude": 126.61789147782304, + "pageIndex": 10, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "16952", + "name": "진순두부", + "categoryName": "한식", + "address": "인천광역시 중구 우현로35번길 24-3 (신포동) 1층", + "phone": "032-764-9898", + "representativeMenu": "바지락순두부+솥밥", + "price": 10000, + "latitude": 37.47101351299041, + "longitude": 126.62511183339346, + "pageIndex": 10, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "14225", + "name": "진흥각", + "categoryName": "중식", + "address": "인천광역시 중구 신포로23번길 20 (중앙동4가) 1층", + "phone": "032-772-3058", + "representativeMenu": "유니짜장", + "price": 6500, + "latitude": 37.471703087227134, + "longitude": 126.62392009417408, + "pageIndex": 10, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2249", + "name": "주영미용실", + "categoryName": "미용업", + "address": "광주광역시 동구 무등로375번길 27-1 1층(계림동)", + "phone": "062-529-3184", + "representativeMenu": "성인남녀컷트", + "price": 10000, + "latitude": 35.1630933471234, + "longitude": 126.924296217507, + "pageIndex": 10, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "11310", + "name": "진 숯불갈비", + "categoryName": "중식", + "address": "광주광역시 동구 무등로 529 (산수동)", + "phone": "0507-1482-7550", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 35.1564823477708, + "longitude": 126.937445538702, + "pageIndex": 10, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2277", + "name": "참스민", + "categoryName": "한식", + "address": "광주광역시 동구 남문로 752-3 1층(학동)", + "phone": "062-232-3883", + "representativeMenu": "김밥", + "price": 3000, + "latitude": 35.1370928662675, + "longitude": 126.925449709403, + "pageIndex": 10, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "14318", + "name": "손복순수라상뷔페", + "categoryName": "한식", + "address": "대전광역시 동구 석천로 7-15 (낭월동) 낭월동", + "phone": "042-272-3233", + "representativeMenu": "뷔페(~7세)", + "price": 3000, + "latitude": 36.27595980984733, + "longitude": 127.46565739495651, + "pageIndex": 10, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2533", + "name": "송미용실", + "categoryName": "미용업", + "address": "대전광역시 동구 산내로 1302 (낭월동) 103호", + "phone": "042-271-9443", + "representativeMenu": "미용료(커트)", + "price": 8000, + "latitude": 36.27973981942758, + "longitude": 127.46857116346227, + "pageIndex": 10, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2524", + "name": "수정목욕탕", + "categoryName": "목욕업", + "address": "대전광역시 동구 비래서로62번길 103 (가양동)", + "phone": "042-631-3660", + "representativeMenu": "목욕료", + "price": 6000, + "latitude": 36.3498831171768, + "longitude": 127.449018725201, + "pageIndex": 10, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "18132", + "name": "슈퍼몽테마파크", + "categoryName": "기타비요식업", + "address": "대전광역시 동구 석천로 8 (낭월동) 2층", + "phone": "042-271-1644", + "representativeMenu": "이용료(1시간)", + "price": 5000, + "latitude": 36.27670405471021, + "longitude": 127.46581697285043, + "pageIndex": 10, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "14576", + "name": "할매칼국수", + "categoryName": "한식", + "address": "울산광역시 중구 중앙시장길 21-4 (옥교동) 1층", + "phone": "052-245-1333", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 35.55535577904836, + "longitude": 129.3236810137489, + "pageIndex": 10, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2998", + "name": "할머니손칼국시", + "categoryName": "한식", + "address": "울산광역시 중구 화진4길 20 (태화동)", + "phone": "052-245-8542", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 35.5559132736789, + "longitude": 129.307740756218, + "pageIndex": 10, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "13647", + "name": "홍사부", + "categoryName": "중식", + "address": "울산광역시 중구 내황14길 50 (반구동, 나린헤리티지 2차) .", + "phone": "052-298-0777", + "representativeMenu": "자장면", + "price": 6000, + "latitude": 35.55381618863106, + "longitude": 129.34837209424774, + "pageIndex": 10, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "14612", + "name": "24시해장국", + "categoryName": "한식", + "address": "울산광역시 남구 번영로156번길 23 (달동) 1층", + "phone": "052-274-3344", + "representativeMenu": "순두부찌개", + "price": 9000, + "latitude": 35.53975218505595, + "longitude": 129.33041512613374, + "pageIndex": 10, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "10180", + "name": "침산똥돼지", + "categoryName": "한식", + "address": "세종특별자치시 조치원읍 행복6길 40", + "phone": "044-867-3391", + "representativeMenu": "오삼불고기(점심특선)", + "price": 9000, + "latitude": 36.606084930284, + "longitude": 127.293726259292, + "pageIndex": 10, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10187", + "name": "큰나무식당", + "categoryName": "한식", + "address": "세종특별자치시 금남면 대평시장2길 6 1층", + "phone": "044-866-4924", + "representativeMenu": "보리밥", + "price": 8000, + "latitude": 36.4658590703269, + "longitude": 127.282333172075, + "pageIndex": 10, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10175", + "name": "한아름식당", + "categoryName": "한식", + "address": "세종특별자치시 연동면 태산로 60 청정상가 102호", + "phone": "044-868-5005", + "representativeMenu": "된장찌개", + "price": 8000, + "latitude": 36.5290966808229, + "longitude": 127.351449156014, + "pageIndex": 10, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "10197", + "name": "행복한 약초칼국수", + "categoryName": "한식", + "address": "세종특별자치시 보듬3로 8-15 1층 102호(도담동, 해피라움5)", + "phone": "044-862-1128", + "representativeMenu": "칼국수", + "price": 5000, + "latitude": 36.5138053791972, + "longitude": 127.258063600072, + "pageIndex": 10, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "3387", + "name": "뚱가네", + "categoryName": "한식", + "address": "경기도 수원시 영통구 영통로 217번길5 (영통동)", + "phone": "031-234-5208", + "representativeMenu": "차돌된장찌개", + "price": 7000, + "latitude": 37.2466692531637, + "longitude": 127.056605302185, + "pageIndex": 10, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "19001", + "name": "뚱보네 꽈배기", + "categoryName": "베이커리", + "address": "경기도 수원시 장안구 경수대로1081번길 39 (파장동) 1층", + "phone": "0507-1489-1202", + "representativeMenu": "꽈배기(3개)", + "price": 2000, + "latitude": 37.30773025414107, + "longitude": 126.99299873461685, + "pageIndex": 10, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "18122", + "name": "러너커피로스터스", + "categoryName": "기타요식업", + "address": "경기도 수원시 장안구 만석로209번길 12 (송죽동) 1층", + "phone": "010-3245-3759", + "representativeMenu": "아메리카노", + "price": 2500, + "latitude": 37.305402508312305, + "longitude": 127.00062950713131, + "pageIndex": 10, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "4384", + "name": "밥짓는마을", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 중앙로 63-1 2층(중앙로2가)", + "phone": "033-242-2624", + "representativeMenu": "한식뷔페", + "price": 7000, + "latitude": 37.8793858304165, + "longitude": 127.726806123679, + "pageIndex": 10, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "11725", + "name": "백령밥집", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 백령로 180 (후평동) 가운데 1층", + "phone": "033-241-2287", + "representativeMenu": "가정식백반", + "price": 7000, + "latitude": 37.87627632697244, + "longitude": 127.74631332467781, + "pageIndex": 10, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "16648", + "name": "백양세탁소", + "categoryName": "세탁업", + "address": "강원특별자치도 춘천시 춘천로 263-1 (후평동)", + "phone": "033-252-6311", + "representativeMenu": "양복상의", + "price": 5000, + "latitude": 37.8799279349072, + "longitude": 127.74265772378813, + "pageIndex": 10, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "16651", + "name": "백조컴퓨터세탁", + "categoryName": "세탁업", + "address": "강원특별자치도 춘천시 퇴계로146번길 12-1 (퇴계동)", + "phone": "033-261-8348", + "representativeMenu": "양복상의", + "price": 5000, + "latitude": 37.85855890084086, + "longitude": 127.73469966103542, + "pageIndex": 10, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "15770", + "name": "대길식당", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 창신로 34-1 (사창동) 1층", + "phone": "049-271-0284", + "representativeMenu": "한우내장탕(보통)", + "price": 7000, + "latitude": 36.633961660488815, + "longitude": 127.46479698824699, + "pageIndex": 10, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4737", + "name": "대동관", + "categoryName": "중식", + "address": "충청북도 청주시 청원구 내수읍 내수로 731-2", + "phone": "043-214-0020", + "representativeMenu": "자장면", + "price": 4000, + "latitude": 36.7268063827084, + "longitude": 127.536156682424, + "pageIndex": 10, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15578", + "name": "로스팅포인트 오렌지", + "categoryName": "기타요식업", + "address": "충청남도 천안시 서북구 불당25로 154 (불당동) 118호", + "phone": "-", + "representativeMenu": "핸드드립", + "price": 3000, + "latitude": 36.814917231485325, + "longitude": 127.10795228730834, + "pageIndex": 10, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4961", + "name": "루미너스헤어", + "categoryName": "미용업", + "address": "충청남도 천안시 서북구 서부12길12 (성정동)", + "phone": "041-575-0727", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.8207843702587, + "longitude": 127.137340027875, + "pageIndex": 10, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "11751", + "name": "맛짱김밥", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 원거리11길 42 (원성동)", + "phone": "041-568-7775", + "representativeMenu": "김치찌개", + "price": 6000, + "latitude": 36.8010379639367, + "longitude": 127.157625650438, + "pageIndex": 10, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4962", + "name": "머리못하는집", + "categoryName": "미용업", + "address": "충청남도 천안시 서북구 불당17길 14 (불당동)", + "phone": "041-551-9111", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.8091043219242, + "longitude": 127.11029060282, + "pageIndex": 10, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "5401", + "name": "제일크리너스샵", + "categoryName": "세탁업", + "address": "전북특별자치도 전주시 덕진구 쪽구름로 37", + "phone": "063-211-2177", + "representativeMenu": "양복(1벌)", + "price": 9000, + "latitude": 35.8686263475242, + "longitude": 127.077142048735, + "pageIndex": 10, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "18212", + "name": "조선옥미용실", + "categoryName": "미용업", + "address": "전북특별자치도 전주시 완산구 완산길 105 (서완산동1가) 1층", + "phone": "063-288-1229", + "representativeMenu": "커트(학생)", + "price": 8000, + "latitude": 35.81196716378034, + "longitude": 127.13938563394339, + "pageIndex": 10, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "11327", + "name": "중본이쟁반짜장", + "categoryName": "중식", + "address": "전북특별자치도 전주시 완산구 공북로71", + "phone": "063-271-2223", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 35.826119320872, + "longitude": 127.139523334928, + "pageIndex": 10, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "18286", + "name": "작은곰집", + "categoryName": "한식", + "address": "전라남도 목포시 삼일로13번길 3 (남교동) 작은곰집", + "phone": "061-244-2180", + "representativeMenu": "순대", + "price": 8000, + "latitude": 34.79357861479481, + "longitude": 126.38329621043106, + "pageIndex": 10, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "18287", + "name": "조선국수", + "categoryName": "한식", + "address": "전라남도 목포시 교육로66번길 23-1 (상동) 1층", + "phone": "0507-1303-3481", + "representativeMenu": "비빔국수", + "price": 6000, + "latitude": 34.80412602893619, + "longitude": 126.42234641443257, + "pageIndex": 10, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5625", + "name": "종가집", + "categoryName": "한식", + "address": "전라남도 목포시 노적봉길 22 (죽동)", + "phone": "061-242-7766", + "representativeMenu": "청국장", + "price": 8000, + "latitude": 34.7900216219656, + "longitude": 126.383192100449, + "pageIndex": 10, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "18283", + "name": "진국밥", + "categoryName": "한식", + "address": "전라남도 목포시 영산로250번길 35 (용당동) 주2동 1층", + "phone": "061-278-0009", + "representativeMenu": "돼지머리국밥", + "price": 8000, + "latitude": 34.80020715783033, + "longitude": 126.3957145704502, + "pageIndex": 10, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6324", + "name": "김육환 미용실", + "categoryName": "미용업", + "address": "경상북도 포항시 남구 대이로 14 김육환 미용실", + "phone": "054-281-7918", + "representativeMenu": "여자커트", + "price": 10000, + "latitude": 36.0170774969108, + "longitude": 129.343624477205, + "pageIndex": 10, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "16134", + "name": "까꼬뽀꼬", + "categoryName": "미용업", + "address": "경상북도 포항시 북구 흥해읍 동해대로1574번길 18 까꼬뽀꼬", + "phone": "-", + "representativeMenu": "학생컷", + "price": 10000, + "latitude": 36.11248157816856, + "longitude": 129.34344079201517, + "pageIndex": 10, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "15292", + "name": "둘리왕만두", + "categoryName": "한식", + "address": "경상남도 창원시 의창구 도계로60번길 13-16 (도계동) 둘리왕만두", + "phone": "055-273-8700", + "representativeMenu": "만두", + "price": 5000, + "latitude": 35.257774977009696, + "longitude": 128.63697480240288, + "pageIndex": 10, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "18773", + "name": "따뜻한밥상", + "categoryName": "한식", + "address": "경상남도 창원시 의창구 퇴촌로25번길 6-15 (사림동) 지하 1층", + "phone": "010-2587-5925", + "representativeMenu": "김치찌개", + "price": 3000, + "latitude": 35.24245999906216, + "longitude": 128.6887510304655, + "pageIndex": 10, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "19169", + "name": "만날재삼계탕", + "categoryName": "한식", + "address": "경상남도 창원시 마산합포구 월영동6길 7 (해운동) 2층", + "phone": "055-222-3908", + "representativeMenu": "삼계탕", + "price": 10000, + "latitude": 35.17985383679972, + "longitude": 128.56194235833954, + "pageIndex": 10, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "18741", + "name": "넉둥베기", + "categoryName": "한식", + "address": "제주특별자치도 제주시 서문로 9 (용담일동) 1층", + "phone": "064-743-2585", + "representativeMenu": "돼지머리국밥", + "price": 6000, + "latitude": 33.50976651062224, + "longitude": 126.51245788210441, + "pageIndex": 10, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "16863", + "name": "장원식당", + "categoryName": "한식", + "address": "서울특별시 종로구 창신5나길 3 (창신동) 1층", + "phone": "02-6777-1437", + "representativeMenu": "우거지뼈해장국", + "price": 8000, + "latitude": 37.573776039713145, + "longitude": 127.01117339129199, + "pageIndex": 11, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "959", + "name": "전주콩나물국밥", + "categoryName": "한식", + "address": "서울특별시 종로구 자하문로 3 (내자동)", + "phone": "02-738-8223", + "representativeMenu": "콩나물국밥", + "price": 7000, + "latitude": 37.5764830744797, + "longitude": 126.972264842484, + "pageIndex": 11, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1634", + "name": "퍼짱", + "categoryName": "기타요식업", + "address": "부산광역시 중구 충장대로9번길 21 1층(중앙동4가)", + "phone": "-", + "representativeMenu": "양지곰탕", + "price": 8500, + "latitude": 35.1072175500747, + "longitude": 129.037444857052, + "pageIndex": 11, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "10021", + "name": "학마을", + "categoryName": "중식", + "address": "부산광역시 중구 중구로 29번길 7 (부평동1가)", + "phone": "051-257-0221", + "representativeMenu": "짜장면", + "price": 4000, + "latitude": 35.1003450042619, + "longitude": 129.02748323782, + "pageIndex": 11, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1661", + "name": "한양왕순대", + "categoryName": "한식", + "address": "부산광역시 중구 해관로20-1 (중앙동)", + "phone": "051-245-9138", + "representativeMenu": "순대국밥", + "price": 9000, + "latitude": 35.1006289027265, + "longitude": 129.035905126423, + "pageIndex": 11, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "10072", + "name": "한우장", + "categoryName": "한식", + "address": "대구광역시 중구 국채보상로 555 (종로1가) (전)", + "phone": "053-257-1125", + "representativeMenu": "설렁탕", + "price": 10000, + "latitude": 35.8709131264325, + "longitude": 128.59143517676986, + "pageIndex": 11, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17987", + "name": "한울식당", + "categoryName": "한식", + "address": "대구광역시 중구 경상감영길 280 (동인동2가) 동인동", + "phone": "053-425-2462", + "representativeMenu": "정식", + "price": 8000, + "latitude": 35.87080928368533, + "longitude": 128.60257722168762, + "pageIndex": 11, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "14064", + "name": "MHK정미용실", + "categoryName": "미용업", + "address": "대구광역시 동구 반야월로 134-1 (신기동) 동부프라자 나동", + "phone": "010-2829-6158", + "representativeMenu": "커트", + "price": 8000, + "latitude": 35.87268975976554, + "longitude": 128.70304821144194, + "pageIndex": 11, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2237", + "name": "철이네손수제비랑칼국수(신포)", + "categoryName": "한식", + "address": "인천광역시 중구 개항로 9-1 1층(중앙동4가)", + "phone": "032-777-1159", + "representativeMenu": "얼큰수제비", + "price": 7000, + "latitude": 37.4714042648041, + "longitude": 126.623802333892, + "pageIndex": 11, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2238", + "name": "철이네손수제비랑칼국수(신흥)", + "categoryName": "한식", + "address": "인천광역시 중구 인중로144번길 71-1 (답동)", + "phone": "032-204-8990", + "representativeMenu": "수제비", + "price": 7000, + "latitude": 37.4684000940284, + "longitude": 126.630126454085, + "pageIndex": 11, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2239", + "name": "청수식당", + "categoryName": "한식", + "address": "인천광역시 중구 제물량로206번길 3 (해안동2가)", + "phone": "032-765-8586", + "representativeMenu": "오삼불고기", + "price": 9000, + "latitude": 37.4717703194453, + "longitude": 126.621537648833, + "pageIndex": 11, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2241", + "name": "할매왕족발순대", + "categoryName": "한식", + "address": "인천광역시 중구 우현로45번길 13 (신포동) (신포동)", + "phone": "032-772-6919", + "representativeMenu": "순대국밥", + "price": 9000, + "latitude": 37.47117471982517, + "longitude": 126.62741642573887, + "pageIndex": 11, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2278", + "name": "칠미우동만두", + "categoryName": "한식", + "address": "광주광역시 동구 지산로 69 1층(지산동)", + "phone": "062-228-7228", + "representativeMenu": "김밥", + "price": 3000, + "latitude": 35.1498802119369, + "longitude": 126.933003887435, + "pageIndex": 11, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2280", + "name": "학동김밥", + "categoryName": "한식", + "address": "광주광역시 동구 남문로 683 1층(학동)", + "phone": "062-225-3248", + "representativeMenu": "김밥", + "price": 3000, + "latitude": 35.1315918766729, + "longitude": 126.928644258668, + "pageIndex": 11, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2547", + "name": "신도칼국수", + "categoryName": "한식", + "address": "대전광역시 동구 대전로 825번길 11 (정동)", + "phone": "-", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 36.3325852871967, + "longitude": 127.430995831215, + "pageIndex": 11, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "16821", + "name": "신미식당", + "categoryName": "한식", + "address": "대전광역시 동구 우암로85번길 35 (삼성동) 1층", + "phone": "042-672-5728", + "representativeMenu": "선지국밥", + "price": 6000, + "latitude": 36.33884769737071, + "longitude": 127.42700019228904, + "pageIndex": 11, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "14611", + "name": "30년전통감자탕", + "categoryName": "한식", + "address": "울산광역시 남구 봉월로8번길 15 (신정동) 1층", + "phone": "0507-1317-8555", + "representativeMenu": "뼈다귀해장국", + "price": 8000, + "latitude": 35.53379995639712, + "longitude": 129.30877505918386, + "pageIndex": 11, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "15122", + "name": "가을정류장", + "categoryName": "일식", + "address": "울산광역시 남구 문수로 295 (옥동) 1층", + "phone": "052-266-1005", + "representativeMenu": "오리지날함박스테이크", + "price": 10000, + "latitude": 35.535986796355175, + "longitude": 129.28599318182114, + "pageIndex": 11, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2897", + "name": "경북식당", + "categoryName": "한식", + "address": "울산광역시 남구 돋질로 145번길 38-25 -", + "phone": "052-267-7855", + "representativeMenu": "정식", + "price": 7000, + "latitude": 35.544040122471, + "longitude": 129.321265246852, + "pageIndex": 11, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "15109", + "name": "경북칼국수", + "categoryName": "한식", + "address": "울산광역시 남구 월평로37번길 5 (신정동) 1층", + "phone": "052-272-6903", + "representativeMenu": "손칼국수", + "price": 6000, + "latitude": 35.541808785746284, + "longitude": 129.30930615922028, + "pageIndex": 11, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "13003", + "name": "행복한곰탕", + "categoryName": "한식", + "address": "세종특별자치시 절재로 194 (어진동) 중앙타운 317호", + "phone": "-", + "representativeMenu": "곰탕", + "price": 9000, + "latitude": 36.50830733147383, + "longitude": 127.26244504454054, + "pageIndex": 11, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "18695", + "name": "헤이맨남성컷", + "categoryName": "이용업", + "address": "세종특별자치시 새롬북로 13 (새롬동, 새뜸마을4단지) 상가동 1층", + "phone": "044-864-7004", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.48655065561284, + "longitude": 127.24647322838923, + "pageIndex": 11, + "regionCode": "36", + "regionName": "세종특별자치시" + }, + { + "bsshSn": "14930", + "name": "레알떡볶이", + "categoryName": "한식", + "address": "경기도 수원시 영통구 봉영로 1623 (영통동) 1층 133-2호", + "phone": "0507-1311-8698", + "representativeMenu": "떡볶이", + "price": 3000, + "latitude": 37.25592537670266, + "longitude": 127.07472815939546, + "pageIndex": 11, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "14332", + "name": "렛츠헤어", + "categoryName": "미용업", + "address": "경기도 수원시 권선구 권광로27번길 51 (권선동) 1층", + "phone": "-", + "representativeMenu": "컷트", + "price": 10000, + "latitude": 37.251111070274, + "longitude": 127.02439282380257, + "pageIndex": 11, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "13883", + "name": "만두가게", + "categoryName": "한식", + "address": "경기도 수원시 팔달구 수원천로 315 (남수동) 1층", + "phone": "031-251-3900", + "representativeMenu": "만두", + "price": 6000, + "latitude": 37.28148605591346, + "longitude": 127.01796582445644, + "pageIndex": 11, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "11724", + "name": "봉실스넥", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 서부대성로239번길 8 (효자동)", + "phone": "033-251-6890", + "representativeMenu": "순두부찌개", + "price": 5000, + "latitude": 37.8732547890045, + "longitude": 127.745099874572, + "pageIndex": 11, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "16632", + "name": "분식집에 하숙하는 붕어빵", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 삭주로 25 (교동)", + "phone": "0507-1402-8552", + "representativeMenu": "떡볶이", + "price": 3000, + "latitude": 37.88144386414532, + "longitude": 127.73456759786936, + "pageIndex": 11, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "15847", + "name": "대복분식", + "categoryName": "한식", + "address": "충청북도 청주시 흥덕구 강내면 태성탑연로 454-1 주2동 1층", + "phone": "043-236-3142", + "representativeMenu": "된장찌개", + "price": 8000, + "latitude": 36.622893602285906, + "longitude": 127.3588240445716, + "pageIndex": 11, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "16071", + "name": "대선집", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 구룡산로51번다길 4 (성화동) 1층", + "phone": "043-233-2212", + "representativeMenu": "돌솥제육정식", + "price": 10000, + "latitude": 36.615134258549226, + "longitude": 127.45225164740799, + "pageIndex": 11, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15520", + "name": "대성식당", + "categoryName": "한식", + "address": "충청북도 청주시 상당구 상당로186번길 17 (수동) (대성식당)", + "phone": "043-258-1345", + "representativeMenu": "가정식백반", + "price": 6000, + "latitude": 36.64494841100788, + "longitude": 127.49068538143457, + "pageIndex": 11, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4760", + "name": "대운분식", + "categoryName": "한식", + "address": "충청북도 청주시 흥덕구 옥산면 청주역로 654", + "phone": "043-260-0667", + "representativeMenu": "콩나물밥", + "price": 6000, + "latitude": 36.6654960294927, + "longitude": 127.374242450261, + "pageIndex": 11, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "16878", + "name": "명윤", + "categoryName": "중식", + "address": "충청남도 천안시 서북구 직산읍 4산단로 241 1동", + "phone": "0507-1426-9775", + "representativeMenu": "자장면", + "price": 6000, + "latitude": 36.85787766445741, + "longitude": 127.10949582855619, + "pageIndex": 11, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "16880", + "name": "모모아지트", + "categoryName": "기타요식업", + "address": "충청남도 천안시 서북구 봉정로 140 (성정동) 3층", + "phone": "0507-1337-4919", + "representativeMenu": "아메리카노", + "price": 3000, + "latitude": 36.814482384883114, + "longitude": 127.14202209848756, + "pageIndex": 11, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4994", + "name": "문헤어갤러리", + "categoryName": "미용업", + "address": "충청남도 천안시 서북구 미라14길 20 (쌍용동)", + "phone": "041-577-4466", + "representativeMenu": "커트", + "price": 8000, + "latitude": 36.8056741666016, + "longitude": 127.13015496868, + "pageIndex": 11, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "10501", + "name": "착한가격헤어", + "categoryName": "미용업", + "address": "전라남도 목포시 상리로9번길 22 1층(상동)", + "phone": "061-278-1571", + "representativeMenu": "커트(기본)", + "price": 6000, + "latitude": 34.8137466442366, + "longitude": 126.41499805728, + "pageIndex": 11, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "15147", + "name": "태동반점", + "categoryName": "중식", + "address": "전라남도 목포시 마인계터로40번길 10-1 (죽동) 태동반점", + "phone": "061-243-3351", + "representativeMenu": "짜장면", + "price": 7000, + "latitude": 34.7899992363049, + "longitude": 126.38341878576848, + "pageIndex": 11, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "11126", + "name": "하나둘미용실", + "categoryName": "미용업", + "address": "전라남도 목포시 수강로 6 (수강동1가)", + "phone": "061-243-9098", + "representativeMenu": "여성(일반)컷트", + "price": 10000, + "latitude": 34.7856055450319, + "longitude": 126.386018127697, + "pageIndex": 11, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6309", + "name": "끄티디저트카페", + "categoryName": "기타요식업", + "address": "경상북도 포항시 남구 중앙로134번길 2 끄티디저트카페", + "phone": "054-281-2496", + "representativeMenu": "아메리카노", + "price": 2000, + "latitude": 36.0250169966387, + "longitude": 129.368453187457, + "pageIndex": 11, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6326", + "name": "나미화헤어클럽", + "categoryName": "미용업", + "address": "경상북도 포항시 남구 송도로 10-1", + "phone": "-", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.0337446127344, + "longitude": 129.372175309483, + "pageIndex": 11, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6495", + "name": "나주식당", + "categoryName": "한식", + "address": "경상북도 포항시 북구 양학로32번길 9-1 나주식당", + "phone": "-", + "representativeMenu": "정식", + "price": 7500, + "latitude": 36.0264062592383, + "longitude": 129.351296310605, + "pageIndex": 11, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "17129", + "name": "만원족발", + "categoryName": "한식", + "address": "경상남도 창원시 마산회원구 양덕북2길 13 (양덕동) 1층", + "phone": "055-299-4822", + "representativeMenu": "왕족발", + "price": 10000, + "latitude": 35.232299773538415, + "longitude": 128.58552226970943, + "pageIndex": 11, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "17130", + "name": "맛집", + "categoryName": "한식", + "address": "경상남도 창원시 마산회원구 구암북12길 17 (구암동) 맛집", + "phone": "055-255-9597", + "representativeMenu": "맛집김밥", + "price": 3000, + "latitude": 35.25555706724836, + "longitude": 128.59779095142022, + "pageIndex": 11, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "17139", + "name": "먹거리아", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 벚꽃로70번길 17-2 (화천동) 먹거리아", + "phone": "055-542-9476", + "representativeMenu": "고구마튀김 8개", + "price": 5000, + "latitude": 35.151431963349566, + "longitude": 128.66566078665707, + "pageIndex": 11, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "6804", + "name": "메밀파티", + "categoryName": "한식", + "address": "경상남도 창원시 성산구 토월로 88 (상남동) 지하", + "phone": "055-284-0618", + "representativeMenu": "막국수", + "price": 8000, + "latitude": 35.21882052587623, + "longitude": 128.68409106230135, + "pageIndex": 11, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "7013", + "name": "다마내기", + "categoryName": "기타비요식업", + "address": "제주특별자치도 제주시 원노형로 26 202호 (서림빌딩)", + "phone": "064-747-0618", + "representativeMenu": "중대 10분", + "price": 1700, + "latitude": 33.4864471773391, + "longitude": 126.484759413813, + "pageIndex": 11, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "18086", + "name": "다올국수", + "categoryName": "한식", + "address": "제주특별자치도 제주시 서광로 209-1 (삼도일동) 다올국수", + "phone": "064-755-7076", + "representativeMenu": "멸치국수", + "price": 7000, + "latitude": 33.50016357321733, + "longitude": 126.51892020124394, + "pageIndex": 11, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "18078", + "name": "다인헤어", + "categoryName": "미용업", + "address": "제주특별자치도 제주시 원노형로 9 (노형동) 다인헤어", + "phone": "064-744-9242", + "representativeMenu": "컷트", + "price": 10000, + "latitude": 33.487776208529105, + "longitude": 126.48422199658157, + "pageIndex": 11, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "952", + "name": "종묘이용원", + "categoryName": "이용업", + "address": "서울특별시 종로구 종로 146 (종로3가) (종로4가)", + "phone": "-", + "representativeMenu": "커트", + "price": 7000, + "latitude": 37.57022814518435, + "longitude": 126.99369870159373, + "pageIndex": 12, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "16865", + "name": "중국", + "categoryName": "중식", + "address": "서울특별시 종로구 자하문로33길 2 (청운동) 1층", + "phone": "02-737-8055", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 37.58619403165867, + "longitude": 126.96972945340502, + "pageIndex": 12, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "10019", + "name": "황소식당", + "categoryName": "한식", + "address": "부산광역시 중구 충장대로 4번길 8 (중앙동4가)", + "phone": "051-465-6648", + "representativeMenu": "부대찌개", + "price": 7000, + "latitude": 35.1044916301827, + "longitude": 129.036809946537, + "pageIndex": 12, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1420", + "name": "3678통장어", + "categoryName": "한식", + "address": "부산광역시 서구 구덕로 280번길 17 (동대신동1가)", + "phone": "051-242-3678", + "representativeMenu": "장어탕", + "price": 10000, + "latitude": 35.1095112240639, + "longitude": 129.01944149398, + "pageIndex": 12, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1393", + "name": "777헤어샵", + "categoryName": "미용업", + "address": "부산광역시 서구 자갈치로 8-1 (충무동1가)", + "phone": "051-244-2430", + "representativeMenu": "커트(남)", + "price": 10000, + "latitude": 35.0961210951299, + "longitude": 129.025327657564, + "pageIndex": 12, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "10083", + "name": "강변복어", + "categoryName": "한식", + "address": "대구광역시 동구 호반길67 (방촌동)", + "phone": "053-982-7252", + "representativeMenu": "복어탕", + "price": 8000, + "latitude": 35.8788829142879, + "longitude": 128.660877483508, + "pageIndex": 12, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "1870", + "name": "고향손칼국수", + "categoryName": "한식", + "address": "대구광역시 동구 장등로 9 (신천동)", + "phone": "053-752-8894", + "representativeMenu": "잔치국수", + "price": 4500, + "latitude": 35.8682732754141, + "longitude": 128.622905927079, + "pageIndex": 12, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2222", + "name": "현대세탁소", + "categoryName": "세탁업", + "address": "인천광역시 중구 인항로 34 1층 102호 (신흥동3가)", + "phone": "032-891-5252", + "representativeMenu": "바지", + "price": 5000, + "latitude": 37.4571633075413, + "longitude": 126.634101603919, + "pageIndex": 12, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "16954", + "name": "혼밥밥상", + "categoryName": "한식", + "address": "인천광역시 중구 하늘중앙로225번길 11 (중산동) 2층 206,207호", + "phone": "-", + "representativeMenu": "백반", + "price": 10000, + "latitude": 37.487838041971756, + "longitude": 126.55922287899413, + "pageIndex": 12, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2281", + "name": "한우물", + "categoryName": "한식", + "address": "광주광역시 동구 필문대로191번길 15-2 (산수동)", + "phone": "062-675-9944", + "representativeMenu": "생선구이(모듬)", + "price": 9000, + "latitude": 35.1533803800092, + "longitude": 126.933174859781, + "pageIndex": 12, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2282", + "name": "해뜨는식당", + "categoryName": "한식", + "address": "광주광역시 동구 제봉로190번길 7-1 1층(대인동)", + "phone": "062-227-6063", + "representativeMenu": "백반", + "price": 1000, + "latitude": 35.1540271398023, + "longitude": 126.917438484668, + "pageIndex": 12, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "17166", + "name": "헤어필(학동)", + "categoryName": "미용업", + "address": "광주광역시 동구 천변우로 603 (학동, 백화아파트) 상가102동 104호", + "phone": "-", + "representativeMenu": "성인커트", + "price": 10000, + "latitude": 35.13097376055375, + "longitude": 126.92644589626747, + "pageIndex": 12, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2568", + "name": "옛고을식당", + "categoryName": "한식", + "address": "대전광역시 동구 역전시장길 8 (정동)", + "phone": "042-274-1983", + "representativeMenu": "비빔밥", + "price": 7000, + "latitude": 36.3304560058946, + "longitude": 127.433731277703, + "pageIndex": 12, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "14911", + "name": "왕관식당", + "categoryName": "한식", + "address": "대전광역시 동구 선화로196번길 6 (중동) 중동", + "phone": "042-221-1663", + "representativeMenu": "콩나물밥", + "price": 6000, + "latitude": 36.33360283723725, + "longitude": 127.42754191112995, + "pageIndex": 12, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2887", + "name": "경주이용원", + "categoryName": "이용업", + "address": "울산광역시 남구 월평로159번길 4 1층", + "phone": "052-268-5170", + "representativeMenu": "초등부컷트", + "price": 10000, + "latitude": 35.545149268254, + "longitude": 129.322629378239, + "pageIndex": 12, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "15106", + "name": "경주칼국수", + "categoryName": "한식", + "address": "울산광역시 남구 월평로37번길 5 (신정동) 1층", + "phone": "052-271-3278", + "representativeMenu": "손칼국수", + "price": 6000, + "latitude": 35.541808785746284, + "longitude": 129.30930615922028, + "pageIndex": 12, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "18735", + "name": "궁전중화요리", + "categoryName": "중식", + "address": "울산광역시 남구 두왕로22번길 5-16 (선암동) 1층", + "phone": "052-256-3003", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 35.505829524208984, + "longitude": 129.30959340736075, + "pageIndex": 12, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "14935", + "name": "만두촌", + "categoryName": "한식", + "address": "경기도 수원시 권선구 하탑로 39 (탑동) 102호", + "phone": "031-295-8798", + "representativeMenu": "고기찐만두(10개)", + "price": 5000, + "latitude": 37.26911230608807, + "longitude": 126.97589883567454, + "pageIndex": 12, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "10202", + "name": "멋진남자", + "categoryName": "이용업", + "address": "경기도 수원시 영통구 매봉로35번길 50 1층(매탄동)", + "phone": "-", + "representativeMenu": "남성커트", + "price": 10000, + "latitude": 37.2719859731344, + "longitude": 127.050140470108, + "pageIndex": 12, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "14015", + "name": "메종드커피", + "categoryName": "기타요식업", + "address": "경기도 수원시 팔달구 매산로 131 (교동) 1층", + "phone": "031-258-3545", + "representativeMenu": "아메리카노", + "price": 3400, + "latitude": 37.27211175333725, + "longitude": 127.01481129960187, + "pageIndex": 12, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "16199", + "name": "명랑꽈배기", + "categoryName": "기타요식업", + "address": "경기도 수원시 권선구 하탑로 43 (탑동) 1층", + "phone": "010-9532-0088", + "representativeMenu": "찹쌀꽈배기(3개)", + "price": 2000, + "latitude": 37.26917399031983, + "longitude": 126.97642424590657, + "pageIndex": 12, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "13799", + "name": "블루밍", + "categoryName": "기타요식업", + "address": "강원특별자치도 춘천시 옥천길54번길 26-1 (옥천동)", + "phone": "-", + "representativeMenu": "아메리카노", + "price": 2000, + "latitude": 37.88199230616044, + "longitude": 127.73200418707303, + "pageIndex": 12, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4390", + "name": "삼남매", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 춘천로170번길 8 1층(운교동)", + "phone": "033-637-5770", + "representativeMenu": "가정식백반", + "price": 8000, + "latitude": 37.8756946677783, + "longitude": 127.73332299084, + "pageIndex": 12, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "10312", + "name": "석사반점", + "categoryName": "중식", + "address": "강원특별자치도 춘천시 효석로67번길 25 (석사동)", + "phone": "033-262-2354", + "representativeMenu": "자장면", + "price": 4000, + "latitude": 37.862559878518, + "longitude": 127.739362733706, + "pageIndex": 12, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "15843", + "name": "더 맨", + "categoryName": "미용업", + "address": "충청북도 청주시 청원구 율봉로 258 (율량동)", + "phone": "043-214-2500", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.666703430250955, + "longitude": 127.49720884874743, + "pageIndex": 12, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4748", + "name": "덤블식당", + "categoryName": "한식", + "address": "충청북도 청주시 청원구 향군로31번길 32 (우암동)", + "phone": "043-257-2237", + "representativeMenu": "된장찌개", + "price": 7000, + "latitude": 36.6477777347953, + "longitude": 127.485430090266, + "pageIndex": 12, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15776", + "name": "도니수제돈까스", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 호국로 99 (사직동) 1층", + "phone": "043-277-8230", + "representativeMenu": "등심돈까스정식", + "price": 6000, + "latitude": 36.636876933056726, + "longitude": 127.47622733012022, + "pageIndex": 12, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4964", + "name": "미헤어샵", + "categoryName": "미용업", + "address": "충청남도 천안시 서북구 쌍용16길 24 (쌍용동)", + "phone": "041-575-1196", + "representativeMenu": "커트", + "price": 7000, + "latitude": 36.798721258652, + "longitude": 127.120954088064, + "pageIndex": 12, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4913", + "name": "박정미5000냥헤어", + "categoryName": "미용업", + "address": "충청남도 천안시 동남구 대흥로 127 (사직동)", + "phone": "041-561-3344", + "representativeMenu": "커트", + "price": 7000, + "latitude": 36.800548575871, + "longitude": 127.148519132471, + "pageIndex": 12, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "11119", + "name": "박정미5000냥헤어2호점", + "categoryName": "미용업", + "address": "충청남도 천안시 동남구 영성로 36 (중앙동)", + "phone": "-", + "representativeMenu": "커트", + "price": 7000, + "latitude": 36.8014348547938, + "longitude": 127.150782653869, + "pageIndex": 12, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "5412", + "name": "청년식탁 사잇길", + "categoryName": "한식", + "address": "전북특별자치도 전주시 덕진구 백제대로 563 2층", + "phone": "063-272-0214", + "representativeMenu": "김치찌개", + "price": 3000, + "latitude": 35.8409178469739, + "longitude": 127.131238280077, + "pageIndex": 12, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5446", + "name": "청라회관", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 노송여울2길 10", + "phone": "063-286-3044", + "representativeMenu": "돌솥비빔밥", + "price": 8000, + "latitude": 35.8242200343495, + "longitude": 127.146544090353, + "pageIndex": 12, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5421", + "name": "초희미용실", + "categoryName": "미용업", + "address": "전북특별자치도 전주시 완산구 모악로 4683 우미아파트 상가", + "phone": "063-226-3701", + "representativeMenu": "커트(성인)", + "price": 8000, + "latitude": 35.7876745457672, + "longitude": 127.130038142656, + "pageIndex": 12, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "15994", + "name": "최가돈", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 후곡길 23-24 (효자동2가) 1층", + "phone": "063-227-5931", + "representativeMenu": "김치찌개", + "price": 7000, + "latitude": 35.81087083441043, + "longitude": 127.09465897893688, + "pageIndex": 12, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "10495", + "name": "홍가네만두분식", + "categoryName": "한식", + "address": "전라남도 목포시 삼일로 12 (창평동)", + "phone": "061-242-6705", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 34.7933588484617, + "longitude": 126.38358115361, + "pageIndex": 12, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "18288", + "name": "홍두깨멸치칼국수", + "categoryName": "한식", + "address": "전라남도 목포시 교육로 45-1 (상동) 1층", + "phone": "061-282-7669", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 34.8014150837424, + "longitude": 126.42079872738657, + "pageIndex": 12, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "15146", + "name": "황가네보리밥", + "categoryName": "한식", + "address": "전라남도 목포시 노적봉길 19-4 (무안동) 황가네보리밥", + "phone": "061-278-3986", + "representativeMenu": "보리밥", + "price": 9000, + "latitude": 34.78982445409798, + "longitude": 126.38334964496875, + "pageIndex": 12, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "10504", + "name": "흑산도전복", + "categoryName": "한식", + "address": "전라남도 목포시 양을로208번길 9 (용당동)", + "phone": "061-276-5095", + "representativeMenu": "백반", + "price": 9000, + "latitude": 34.8103104782939, + "longitude": 126.393158316771, + "pageIndex": 12, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6327", + "name": "내가본미용실", + "categoryName": "미용업", + "address": "경상북도 포항시 남구 대이로45번길 15 내가본미용실", + "phone": "054-274-7754", + "representativeMenu": "여자커트", + "price": 10000, + "latitude": 36.0187057254121, + "longitude": 129.340316118787, + "pageIndex": 12, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "11257", + "name": "넌지시", + "categoryName": "기타요식업", + "address": "경상북도 포항시 남구 오천읍 남원로 85-99 나동 101호", + "phone": "070-8870-3318", + "representativeMenu": "아메리카노", + "price": 2500, + "latitude": 35.9659854034839, + "longitude": 129.400825078075, + "pageIndex": 12, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6805", + "name": "메밀한판", + "categoryName": "한식", + "address": "경상남도 창원시 성산구 마디미로 28 B동 202호(상남동, 상남재래시장)", + "phone": "055-262-2002", + "representativeMenu": "냄비우동", + "price": 5500, + "latitude": 35.2222148675498, + "longitude": 128.683379990696, + "pageIndex": 12, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "6811", + "name": "명동식당", + "categoryName": "한식", + "address": "경상남도 창원시 성산구 원이대로589번길 12 (용호동)", + "phone": "055-262-8374", + "representativeMenu": "정식", + "price": 8000, + "latitude": 35.2286912789379, + "longitude": 128.679710134939, + "pageIndex": 12, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "17137", + "name": "모도면집", + "categoryName": "한식", + "address": "경상남도 창원시 성산구 창이대로881번길 17 (대방동) 대방대동황토방아파트상가 12동 2층 206호", + "phone": "055-713-1130", + "representativeMenu": "비빔면", + "price": 8500, + "latitude": 35.20742717951569, + "longitude": 128.7086999798537, + "pageIndex": 12, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "10625", + "name": "도남오거리도남점", + "categoryName": "한식", + "address": "제주특별자치도 제주시 도남로6길 16", + "phone": "064-722-4844", + "representativeMenu": "김치찌개", + "price": 5000, + "latitude": 33.4965453079539, + "longitude": 126.526082680031, + "pageIndex": 12, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "14005", + "name": "진미김밥", + "categoryName": "한식", + "address": "서울특별시 종로구 종로46길 28 (창신동) 1층", + "phone": "02-2268-0069", + "representativeMenu": "진미김밥", + "price": 4000, + "latitude": 37.57045965794985, + "longitude": 127.01018731204245, + "pageIndex": 13, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "19059", + "name": "착한커피공장", + "categoryName": "기타요식업", + "address": "서울특별시 종로구 자하문로7길 66 (누하동) 1층", + "phone": "02-738-7774", + "representativeMenu": "아메리카노", + "price": 2000, + "latitude": 37.58019735852819, + "longitude": 126.9691947292228, + "pageIndex": 13, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "18198", + "name": "창신숯불돼지갈비", + "categoryName": "한식", + "address": "서울특별시 종로구 종로 322-11 (창신동) 1층", + "phone": "02-766-5861", + "representativeMenu": "돼지갈비정식", + "price": 10000, + "latitude": 37.5716697926823, + "longitude": 127.01361493727262, + "pageIndex": 13, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "11365", + "name": "광화냉면", + "categoryName": "한식", + "address": "부산광역시 서구 대영로 73번길 108 (동대신동3가)", + "phone": "051-253-4823", + "representativeMenu": "냉면", + "price": 9000, + "latitude": 35.1150878509163, + "longitude": 129.016829835228, + "pageIndex": 13, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "17709", + "name": "국수집국모", + "categoryName": "한식", + "address": "대구광역시 동구 팔공로27길 24 (불로동) 1층", + "phone": "053-981-3339", + "representativeMenu": "잔치국수", + "price": 5000, + "latitude": 35.9115976360345, + "longitude": 128.63861561811987, + "pageIndex": 13, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "13033", + "name": "갓구워낸빵", + "categoryName": "베이커리", + "address": "인천광역시 동구 샛골로 173 (송림동) 1층", + "phone": "032-567-9019", + "representativeMenu": "단팥빵", + "price": 1200, + "latitude": 37.47786072727075, + "longitude": 126.64475585486453, + "pageIndex": 13, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "13022", + "name": "남성여성커트전문점", + "categoryName": "미용업", + "address": "인천광역시 동구 샛골로161번길 26 (송림동) 1층", + "phone": "010-8670-9155", + "representativeMenu": "남성커트", + "price": 5000, + "latitude": 37.47703198808541, + "longitude": 126.64334345184642, + "pageIndex": 13, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "13798", + "name": "냠냠분식", + "categoryName": "한식", + "address": "인천광역시 동구 수문통로 39 (송현동) 1층", + "phone": "032-777-3355", + "representativeMenu": "떡볶이", + "price": 3500, + "latitude": 37.47984549987457, + "longitude": 126.63397144892058, + "pageIndex": 13, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2093", + "name": "네오클리닝센타", + "categoryName": "세탁업", + "address": "인천광역시 동구 송현로 19번길 14 (송현동)", + "phone": "032-766-0030", + "representativeMenu": "정장1벌", + "price": 10000, + "latitude": 37.4785676119164, + "longitude": 126.634101843256, + "pageIndex": 13, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "13184", + "name": "호수목욕탕", + "categoryName": "목욕업", + "address": "광주광역시 동구 구성로194번길 7-1 (대인동) 1층", + "phone": "062-226-1142", + "representativeMenu": "성인", + "price": 4000, + "latitude": 35.15276002074088, + "longitude": 126.9147242991847, + "pageIndex": 13, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "17171", + "name": "홀인원김밥", + "categoryName": "한식", + "address": "광주광역시 동구 독립로 262-1 (금남로5가) 1", + "phone": "1555-5878", + "representativeMenu": "그냥김밥", + "price": 3500, + "latitude": 35.15454223324605, + "longitude": 126.91127651626087, + "pageIndex": 13, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2569", + "name": "왕산", + "categoryName": "한식", + "address": "대전광역시 동구 계족로 202 (대동)", + "phone": "042-626-7006", + "representativeMenu": "갈비탕", + "price": 7000, + "latitude": 36.3312406960166, + "longitude": 127.441181784066, + "pageIndex": 13, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "18678", + "name": "우리뷰티", + "categoryName": "미용업", + "address": "대전광역시 동구 용운로1번길 5 (대동) 대동", + "phone": "042-282-7718", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.32944033216982, + "longitude": 127.44360995154223, + "pageIndex": 13, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "14912", + "name": "우리헤어클럽", + "categoryName": "미용업", + "address": "대전광역시 동구 백룡로57번길 118 (자양동) 자양동", + "phone": "0507-1367-4071", + "representativeMenu": "미용료(커트)", + "price": 10000, + "latitude": 36.3383596687159, + "longitude": 127.45088284522323, + "pageIndex": 13, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2525", + "name": "운동화목욕한날", + "categoryName": "세탁업", + "address": "대전광역시 동구 용운로 170가동 102호(용운동)", + "phone": "-", + "representativeMenu": "운동화 빨래", + "price": 5000, + "latitude": 36.3276734373929, + "longitude": 127.460965313411, + "pageIndex": 13, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "17508", + "name": "꽃보다남자남성커트", + "categoryName": "미용업", + "address": "울산광역시 남구 두왕로190번길 31 (선암동) 1층", + "phone": "0507-1315-4144", + "representativeMenu": "커트", + "price": 10000, + "latitude": 35.518683361146806, + "longitude": 129.31515922674268, + "pageIndex": 13, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "15094", + "name": "나일57", + "categoryName": "베이커리", + "address": "울산광역시 남구 삼산로83번길 35 (달동) 1층 101호", + "phone": "0507-1346-4157", + "representativeMenu": "아메리카노", + "price": 3000, + "latitude": 35.53775574203502, + "longitude": 129.3154140122934, + "pageIndex": 13, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "15102", + "name": "내고향추어탕", + "categoryName": "한식", + "address": "울산광역시 남구 산업로339번길 24-13 (선암동) 105호", + "phone": "052-256-7972", + "representativeMenu": "추어탕", + "price": 9000, + "latitude": 35.516428145861525, + "longitude": 129.33786343766934, + "pageIndex": 13, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "3398", + "name": "명품세탁 빨래터", + "categoryName": "세탁업", + "address": "경기도 수원시 영통구 동탄원천로 915번길 36 104호", + "phone": "031-216-8939", + "representativeMenu": "세탁료", + "price": 7500, + "latitude": 37.2569793706321, + "longitude": 127.043828501468, + "pageIndex": 13, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "11096", + "name": "모아헤어", + "categoryName": "미용업", + "address": "경기도 수원시 장안구 화산로 285번길 12 화남아파트상가 102호(율전동)", + "phone": "031-295-4005", + "representativeMenu": "커트", + "price": 9000, + "latitude": 37.3002864705875, + "longitude": 126.964407790648, + "pageIndex": 13, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "11689", + "name": "모이세 분식", + "categoryName": "한식", + "address": "경기도 수원시 팔달구 팔달문로3번길 5 (팔달로2가)", + "phone": "031-245-1189", + "representativeMenu": "돈까스", + "price": 8000, + "latitude": 37.2779056593256, + "longitude": 127.017591481138, + "pageIndex": 13, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "4343", + "name": "송혜진미용실", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 신흥길5번길 8-6 1층(우두동)", + "phone": "-", + "representativeMenu": "커트", + "price": 9000, + "latitude": 37.9005696948236, + "longitude": 127.727393589753, + "pageIndex": 13, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "13802", + "name": "수제돈가스", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 명동길 43 (죽림동)", + "phone": "033-254-5074", + "representativeMenu": "돈가스정식", + "price": 7000, + "latitude": 37.87732611130328, + "longitude": 127.72485363921628, + "pageIndex": 13, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "11113", + "name": "숙미용실", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 공지로 432-1 (근화동)", + "phone": "033-253-9217", + "representativeMenu": "커트", + "price": 5000, + "latitude": 37.8737216981244, + "longitude": 127.719469536522, + "pageIndex": 13, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4761", + "name": "도토리명가", + "categoryName": "한식", + "address": "충청북도 청주시 흥덕구 흥덕로162번길 13 (운천동)", + "phone": "-", + "representativeMenu": "도토리묵밥", + "price": 7000, + "latitude": 36.6476629164762, + "longitude": 127.477294311131, + "pageIndex": 13, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15760", + "name": "동의명가 뼈해장국,순대국,감자탕", + "categoryName": "한식", + "address": "충청북도 청주시 상당구 무심동로392번길 4 (서문동) 1층", + "phone": "-", + "representativeMenu": "순대국밥", + "price": 6900, + "latitude": 36.6354992792621, + "longitude": 127.48497460255712, + "pageIndex": 13, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4743", + "name": "돼풍이국내산생고기", + "categoryName": "한식", + "address": "충청북도 청주시 청원구 율봉로94번길 27 (율량동)", + "phone": "043-211-5959", + "representativeMenu": "삼겹살(150g)", + "price": 9000, + "latitude": 36.6654896584325, + "longitude": 127.480643814831, + "pageIndex": 13, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4915", + "name": "백미용실", + "categoryName": "미용업", + "address": "충청남도 천안시 동남구 우영1길 10 106호(봉명동)", + "phone": "041-572-5953", + "representativeMenu": "커트", + "price": 7000, + "latitude": 36.805213750612, + "longitude": 127.140789087028, + "pageIndex": 13, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4989", + "name": "별난생태나라", + "categoryName": "한식", + "address": "충청남도 천안시 서북구 봉서5길 10 104호(쌍용동)", + "phone": "041-575-3675", + "representativeMenu": "동태탕", + "price": 9000, + "latitude": 36.808266626223, + "longitude": 127.132230635153, + "pageIndex": 13, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "15998", + "name": "춘향골밥상", + "categoryName": "한식", + "address": "전북특별자치도 전주시 덕진구 벚꽃2길 5 (진북동)", + "phone": "063-255-8500", + "representativeMenu": "우렁된장찌개", + "price": 8000, + "latitude": 35.827911752494195, + "longitude": 127.1324721401412, + "pageIndex": 13, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "15995", + "name": "하루국수", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 메너머1길 30 (중화산동2가)", + "phone": "0507-1347-0031", + "representativeMenu": "물국수", + "price": 5000, + "latitude": 35.82106396957341, + "longitude": 127.11962562598723, + "pageIndex": 13, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5402", + "name": "하얀세탁나라", + "categoryName": "세탁업", + "address": "전북특별자치도 전주시 덕진구 시천로 29-30 (송천동1가, 한양아파트 상가 1층)", + "phone": "063-902-4555", + "representativeMenu": "양복", + "price": 6000, + "latitude": 35.8605364471238, + "longitude": 127.113373136028, + "pageIndex": 13, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "17489", + "name": "e쁘제헤어프리", + "categoryName": "미용업", + "address": "전라남도 여수시 허문정1길 52 (문수동) 1층", + "phone": "061-651-0612", + "representativeMenu": "성인여자커트", + "price": 10000, + "latitude": 34.74984276015307, + "longitude": 127.69462555986541, + "pageIndex": 13, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5747", + "name": "가든회관", + "categoryName": "한식", + "address": "전라남도 여수시 시청동1길 7 (학동) 가든회관", + "phone": "061-681-5940", + "representativeMenu": "백반", + "price": 9000, + "latitude": 34.76010930737318, + "longitude": 127.66429037149783, + "pageIndex": 13, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6433", + "name": "뉴현대미용실", + "categoryName": "미용업", + "address": "경상북도 포항시 북구 새마을로 2 뉴현대미용실", + "phone": "054-241-5678", + "representativeMenu": "커트", + "price": 5000, + "latitude": 36.0384567529592, + "longitude": 129.358106840482, + "pageIndex": 13, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6363", + "name": "다미손만두돈까스", + "categoryName": "한식", + "address": "경상북도 포항시 남구 구룡포읍 구룡포길 85-1 다미손만두돈까스", + "phone": "054-284-2291", + "representativeMenu": "냉면", + "price": 7000, + "latitude": 35.9912234274676, + "longitude": 129.555134254705, + "pageIndex": 13, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6828", + "name": "미가칼국수", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 벚꽃로60번길 19-6 832(화천동)", + "phone": "055-543-5200", + "representativeMenu": "보리밥", + "price": 4000, + "latitude": 35.1512659472656, + "longitude": 128.665285213057, + "pageIndex": 13, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "6825", + "name": "미진과자점", + "categoryName": "기타요식업", + "address": "경상남도 창원시 진해구 충장로130번길 4 (충무동)", + "phone": "055-545-3133", + "representativeMenu": "벚꽃크림치즈타르트", + "price": 3500, + "latitude": 35.151815090165, + "longitude": 128.66605694706, + "pageIndex": 13, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "16321", + "name": "바름카페공방", + "categoryName": "기타요식업", + "address": "경상남도 창원시 마산회원구 구암동정길 63 (구암동) 101호 바름카페공방", + "phone": "010-6878-7075", + "representativeMenu": "커피(아메리카노)", + "price": 2000, + "latitude": 35.253547999427646, + "longitude": 128.6004429633202, + "pageIndex": 13, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "14254", + "name": "도넛킹", + "categoryName": "기타요식업", + "address": "제주특별자치도 제주시 구남로6길 25 (이도이동) 1층", + "phone": "010-7443-1282", + "representativeMenu": "오리지널 글레이즈", + "price": 1500, + "latitude": 33.48841844009604, + "longitude": 126.53559021440631, + "pageIndex": 13, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "11363", + "name": "청정바지락칼국수", + "categoryName": "한식", + "address": "서울특별시 종로구 창신길 11 1층", + "phone": "02-743-6557", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 37.5722071295158, + "longitude": 127.010524084538, + "pageIndex": 14, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "19060", + "name": "체부동잔치집", + "categoryName": "한식", + "address": "서울특별시 종로구 자하문로1길 16 (체부동) 1층", + "phone": "02-730-5420", + "representativeMenu": "잔치국수", + "price": 5000, + "latitude": 37.57655121434925, + "longitude": 126.97150085198783, + "pageIndex": 14, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "16867", + "name": "카츠연", + "categoryName": "일식", + "address": "서울특별시 종로구 삼봉로 81 (수송동) 지하1층 136호", + "phone": "010-4015-1318", + "representativeMenu": "등심돈카츠정식", + "price": 9500, + "latitude": 37.572557012406925, + "longitude": 126.98181439015927, + "pageIndex": 14, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "13949", + "name": "토리베이커리", + "categoryName": "베이커리", + "address": "서울특별시 종로구 옥인길 30-3 (옥인동) 1층", + "phone": "010-5330-0771", + "representativeMenu": "막걸리식빵", + "price": 4000, + "latitude": 37.58107428102593, + "longitude": 126.96708919813716, + "pageIndex": 14, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1410", + "name": "남강숯불갈비", + "categoryName": "한식", + "address": "부산광역시 서구 충무대로 255번길 9 (남부민동)", + "phone": "051-254-8108", + "representativeMenu": "된장찌개", + "price": 8000, + "latitude": 35.0930541909753, + "longitude": 129.023262189766, + "pageIndex": 14, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "19202", + "name": "대신 벚꽃길 빙수", + "categoryName": "기타요식업", + "address": "부산광역시 서구 대영로73번길 60 (동대신동3가) 1층", + "phone": "051-991-3610", + "representativeMenu": "팥빙수", + "price": 8500, + "latitude": 35.11309551348351, + "longitude": 129.0178920843254, + "pageIndex": 14, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1388", + "name": "대영탕", + "categoryName": "목욕업", + "address": "부산광역시 서구 구덕로 132번길 9 (토성동5가)", + "phone": "051-254-9515", + "representativeMenu": "대인", + "price": 8000, + "latitude": 35.0972775635676, + "longitude": 129.022430372656, + "pageIndex": 14, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "17930", + "name": "길목식당", + "categoryName": "한식", + "address": "대구광역시 동구 평화로 58 (신암동)", + "phone": "053-957-9711", + "representativeMenu": "청국장 찌개", + "price": 7500, + "latitude": 35.88437674353279, + "longitude": 128.62015239131767, + "pageIndex": 14, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "14954", + "name": "날마다좋은집1호점", + "categoryName": "한식", + "address": "대구광역시 동구 아양로11길 10 (신암동)", + "phone": "053-426-3531", + "representativeMenu": "고딧국(2~3인용)", + "price": 6000, + "latitude": 35.88346970782011, + "longitude": 128.62034336657473, + "pageIndex": 14, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "1882", + "name": "다모아식당", + "categoryName": "한식", + "address": "대구광역시 동구 파계로 54 (지묘동)", + "phone": "053-986-2929", + "representativeMenu": "해물칼국수", + "price": 5000, + "latitude": 35.9386980766118, + "longitude": 128.638782921677, + "pageIndex": 14, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17599", + "name": "만석골", + "categoryName": "한식", + "address": "인천 동구 화도진로192번길 2 1층 101호(만석동, 계성빌라)", + "phone": "032-766-8080", + "representativeMenu": "육개장", + "price": 10000, + "latitude": 37.4859697544878, + "longitude": 126.622682978871, + "pageIndex": 14, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "19034", + "name": "명가순대", + "categoryName": "한식", + "address": "인천광역시 동구 화수로 18 (송현동) 명가순대", + "phone": "032-765-8833", + "representativeMenu": "설렁탕", + "price": 9000, + "latitude": 37.48123968414481, + "longitude": 126.6356097358181, + "pageIndex": 14, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "17593", + "name": "모모식당", + "categoryName": "양식", + "address": "인천 동구 화수로 7 1층 102호 (송현동, 센타빌딩)", + "phone": "032-213-2434", + "representativeMenu": "모모등심돈까스", + "price": 10000, + "latitude": 37.4803199313529, + "longitude": 126.636012820597, + "pageIndex": 14, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2094", + "name": "문성각", + "categoryName": "중식", + "address": "인천광역시 동구 송미로 35 (송림동) 1층", + "phone": "032-777-0400", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 37.482903570468814, + "longitude": 126.6497277170181, + "pageIndex": 14, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2304", + "name": "금강식당", + "categoryName": "한식", + "address": "광주광역시 서구 천변우하로 373 (동천동)", + "phone": "062-514-9111", + "representativeMenu": "생고기비빔밥", + "price": 8000, + "latitude": 35.1689145192112, + "longitude": 126.861118425373, + "pageIndex": 14, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "14921", + "name": "금룡", + "categoryName": "중식", + "address": "광주광역시 서구 염화로 115 (화정동) 1층", + "phone": "062-681-2500", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 35.1430601138362, + "longitude": 126.87725557866646, + "pageIndex": 14, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2535", + "name": "은성미용실", + "categoryName": "미용업", + "address": "대전광역시 동구 새터2길 26 (신흥동)", + "phone": "042-283-9633", + "representativeMenu": "미용료(커트)", + "price": 8000, + "latitude": 36.3227027931205, + "longitude": 127.443901827716, + "pageIndex": 14, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "14916", + "name": "인동왕만두", + "categoryName": "베이커리", + "address": "대전광역시 동구 대전로 697 (인동) 인동", + "phone": "042-285-5060", + "representativeMenu": "왕만두(고기만두)", + "price": 6000, + "latitude": 36.32247820088637, + "longitude": 127.43724722008199, + "pageIndex": 14, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "16532", + "name": "장가네대구왕뽈데기", + "categoryName": "한식", + "address": "대전광역시 동구 계족로 309-1 (성남동) 1층", + "phone": "042-623-0750", + "representativeMenu": "대구탕(점심)", + "price": 10000, + "latitude": 36.3400092088714, + "longitude": 127.43610509783201, + "pageIndex": 14, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2541", + "name": "장성", + "categoryName": "중식", + "address": "대전광역시 동구 우암로 85번길 14 (삼성동)", + "phone": "042-622-8494", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 36.3380538647397, + "longitude": 127.427767366719, + "pageIndex": 14, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "15118", + "name": "대박분식", + "categoryName": "한식", + "address": "울산광역시 남구 월평로171번길 26 (신정동, 신정 지웰) 119호", + "phone": "052-269-8845", + "representativeMenu": "김밥", + "price": 3500, + "latitude": 35.54739693327833, + "longitude": 129.32322710623595, + "pageIndex": 14, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "18786", + "name": "더짬뽕", + "categoryName": "중식", + "address": "울산광역시 남구 달삼로23번길 12 (달동) 1층", + "phone": "0507-1356-8941", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 35.53660268642255, + "longitude": 129.33148195611733, + "pageIndex": 14, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2899", + "name": "덤", + "categoryName": "한식", + "address": "울산광역시 남구 대학로84번길 5-5", + "phone": "052-247-1217", + "representativeMenu": "냉칼국수", + "price": 8000, + "latitude": 35.5426013606551, + "longitude": 129.260284008748, + "pageIndex": 14, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "3377", + "name": "못골해장국", + "categoryName": "한식", + "address": "경기도 수원시 권선구 금호로246번길 15-4 1층", + "phone": "031-278-3493", + "representativeMenu": "선지해장국", + "price": 6000, + "latitude": 37.2749502849126, + "longitude": 126.977331077535, + "pageIndex": 14, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "16346", + "name": "몽뻬르베이커리카페", + "categoryName": "베이커리", + "address": "경기도 수원시 장안구 파장로 70 (파장동)", + "phone": "031-246-5474", + "representativeMenu": "아메리카노", + "price": 2900, + "latitude": 37.30605816813522, + "longitude": 126.9931791203893, + "pageIndex": 14, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "3403", + "name": "문화미용실", + "categoryName": "미용업", + "address": "경기도 수원시 장안구 정조로 934번길 29-18 (영화동)", + "phone": "031-256-1463", + "representativeMenu": "커트", + "price": 8000, + "latitude": 37.2923701800345, + "longitude": 127.013797271773, + "pageIndex": 14, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "16643", + "name": "실비아미용실", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 춘천로296번길 18 (후평동)", + "phone": "033-253-5746", + "representativeMenu": "커트", + "price": 10000, + "latitude": 37.879961550470746, + "longitude": 127.74662200640537, + "pageIndex": 14, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "10306", + "name": "쌍다리이용소", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 춘천순환로 42 상가동 101호 (석사동)", + "phone": "033-262-5087", + "representativeMenu": "커트", + "price": 5000, + "latitude": 37.8546857141914, + "longitude": 127.748703975697, + "pageIndex": 14, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "15784", + "name": "또또와식당", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 내수동로108번길 43 (사창동) 1층", + "phone": "-", + "representativeMenu": "순두부찌개", + "price": 6000, + "latitude": 36.63225988198158, + "longitude": 127.45773160968325, + "pageIndex": 14, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15782", + "name": "럭키마라탕 양꼬치", + "categoryName": "중식", + "address": "충청북도 청주시 서원구 서원남로 27-1 (모충동) 1층", + "phone": "-", + "representativeMenu": "마라탕(100g)", + "price": 1800, + "latitude": 36.622339731999546, + "longitude": 127.4846288209598, + "pageIndex": 14, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "19234", + "name": "부흥만두", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 사직로 30 (사직동) 부흥만두", + "phone": "0507-1412-5388", + "representativeMenu": "고기만두 10개", + "price": 6000, + "latitude": 36.802733785154345, + "longitude": 127.14941788531904, + "pageIndex": 14, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4926", + "name": "북경중화요리", + "categoryName": "중식", + "address": "충청남도 천안시 동남구 신부1길 6 (신부동)", + "phone": "041-554-1585", + "representativeMenu": "짜장면", + "price": 6500, + "latitude": 36.8141759551811, + "longitude": 127.158996108759, + "pageIndex": 14, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4965", + "name": "비비헤어샵", + "categoryName": "미용업", + "address": "충청남도 천안시 서북구 미라2길 26-1 (쌍용동)", + "phone": "041-571-1666", + "representativeMenu": "커트", + "price": 5000, + "latitude": 36.8018297147943, + "longitude": 127.129696210405, + "pageIndex": 14, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "16875", + "name": "빵미당", + "categoryName": "기타요식업", + "address": "충청남도 천안시 동남구 충절로 407 (삼룡동) 1층", + "phone": "0507-1387-9046", + "representativeMenu": "단팥빵", + "price": 1700, + "latitude": 36.78497963691765, + "longitude": 127.16873525335542, + "pageIndex": 14, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "5424", + "name": "한들각중화요리", + "categoryName": "중식", + "address": "전북특별자치도 전주시 완산구 안행로 112 1층", + "phone": "063-232-0317", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 35.8062940498025, + "longitude": 127.134355429754, + "pageIndex": 14, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5447", + "name": "한일옥", + "categoryName": "한식", + "address": "전북특별자치도 전주시 완산구 중산중앙로 29", + "phone": "063-222-3118", + "representativeMenu": "한우육회비빔밥", + "price": 9000, + "latitude": 35.8166803897759, + "longitude": 127.121458191969, + "pageIndex": 14, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "15997", + "name": "향촌", + "categoryName": "한식", + "address": "전북특별자치도 전주시 덕진구 숲정이4길 3 (진북동, 다세대주택)", + "phone": "063-252-5733", + "representativeMenu": "김치찌개", + "price": 8000, + "latitude": 35.82908945757786, + "longitude": 127.13270073754536, + "pageIndex": 14, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "11766", + "name": "훈희국수", + "categoryName": "한식", + "address": "전북특별자치도 전주시 덕진구 우아8길 8", + "phone": "063-247-1275", + "representativeMenu": "백반(청국장 육개장 뼈다귀탕)", + "price": 8000, + "latitude": 35.8506326095344, + "longitude": 127.156849553268, + "pageIndex": 14, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5711", + "name": "개성미용실", + "categoryName": "미용업", + "address": "전라남도 여수시 율촌면 당머리길3-1", + "phone": "061-684-7114", + "representativeMenu": "커트", + "price": 10000, + "latitude": 34.882586423451, + "longitude": 127.579141600752, + "pageIndex": 14, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "11806", + "name": "광장서대회", + "categoryName": "한식", + "address": "전라남도 여수시 이순신광장로 196 (종화동) A동 1층", + "phone": "061-666-2013", + "representativeMenu": "서대회(1인)", + "price": 10000, + "latitude": 34.73862390148165, + "longitude": 127.74443243353089, + "pageIndex": 14, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "11807", + "name": "광장서대회(이순신광장점)", + "categoryName": "한식", + "address": "전라남도 여수시 이순신광장로 129 (중앙동) 1층(교동)", + "phone": "0507-1384-2782", + "representativeMenu": "서대회(1인)", + "price": 10000, + "latitude": 34.738245805249676, + "longitude": 127.73846543180578, + "pageIndex": 14, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "16128", + "name": "다정한정식", + "categoryName": "한식", + "address": "경상북도 포항시 남구 장기면 동해안로 3273 다정한정식", + "phone": "054-284-9005", + "representativeMenu": "한정식", + "price": 7000, + "latitude": 35.87919614059732, + "longitude": 129.5172000677791, + "pageIndex": 14, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6423", + "name": "닥터크리닝", + "categoryName": "세탁업", + "address": "경상북도 포항시 북구 삼호로253번길 14-1", + "phone": "054-249-4519", + "representativeMenu": "세탁료 (상의)", + "price": 4000, + "latitude": 36.0612792841722, + "longitude": 129.378003776355, + "pageIndex": 14, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "18356", + "name": "달전밀면", + "categoryName": "한식", + "address": "경상북도 포항시 북구 흥해읍 도음로917번길 12 달전밀면", + "phone": "054-261-6468", + "representativeMenu": "밀면", + "price": 7000, + "latitude": 36.07939354295344, + "longitude": 129.33344894912452, + "pageIndex": 14, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6424", + "name": "대명세탁소", + "categoryName": "세탁업", + "address": "경상북도 포항시 북구 중흥로213번길 18-1", + "phone": "054-275-9579", + "representativeMenu": "와이셔츠", + "price": 2500, + "latitude": 36.0236667659089, + "longitude": 129.356179134247, + "pageIndex": 14, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "18617", + "name": "바보엄마칼국수 진해석동점", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 석동로59번길 6-9 (석동) 1층", + "phone": "055-547-5255", + "representativeMenu": "사골칼국수", + "price": 5500, + "latitude": 35.15643049996103, + "longitude": 128.7056441225601, + "pageIndex": 14, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "18612", + "name": "바보엄마칼국수 창원점", + "categoryName": "한식", + "address": "경상남도 창원시 의창구 의창대로247번길 33 (소답동) 1층", + "phone": "055-299-0094", + "representativeMenu": "사골칼국수", + "price": 5500, + "latitude": 35.26473140031365, + "longitude": 128.625169764524, + "pageIndex": 14, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "15989", + "name": "바삭왕돈까스", + "categoryName": "한식", + "address": "경상남도 창원시 성산구 중앙대로 111 (중앙동) 205호", + "phone": "055-268-5008", + "representativeMenu": "바삭돈까스", + "price": 8500, + "latitude": 35.22477010317151, + "longitude": 128.67987955499908, + "pageIndex": 14, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "15406", + "name": "박하헤어", + "categoryName": "미용업", + "address": "경상남도 창원시 진해구 충장로 397-1 (이동) 1층", + "phone": "055-542-5894", + "representativeMenu": "컷트", + "price": 10000, + "latitude": 35.15259896527641, + "longitude": 128.69534269679198, + "pageIndex": 14, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "18050", + "name": "돌하루방방", + "categoryName": "기타비요식업", + "address": "제주특별자치도 제주시 화삼북로 43 (화북일동) 돌하루방방", + "phone": "010-9487-4132", + "representativeMenu": "트램펄린(30분)", + "price": 3000, + "latitude": 33.517489993448145, + "longitude": 126.57772441179883, + "pageIndex": 14, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "953", + "name": "파고다이용원", + "categoryName": "이용업", + "address": "서울특별시 종로구 종로17길 12 (종로2가)", + "phone": "-", + "representativeMenu": "커트", + "price": 6000, + "latitude": 37.5709410977686, + "longitude": 126.988999535085, + "pageIndex": 15, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "19241", + "name": "현궁", + "categoryName": "한식", + "address": "서울특별시 종로구 종로1길 42 (수송동) 지하1층", + "phone": "02-736-3369", + "representativeMenu": "철판제육쌈밥", + "price": 10000, + "latitude": 37.57390412787066, + "longitude": 126.97898519648197, + "pageIndex": 15, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1423", + "name": "뚜레박", + "categoryName": "한식", + "address": "부산광역시 서구 엄광산로 32 (서대신동3가)", + "phone": "051-246-5233", + "representativeMenu": "청국장", + "price": 8000, + "latitude": 35.1283242912417, + "longitude": 129.008759677418, + "pageIndex": 15, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1390", + "name": "류박사종합세탁", + "categoryName": "세탁업", + "address": "부산광역시 서구 대영로 40 (서대신동1가)", + "phone": "051-253-0972", + "representativeMenu": "양복", + "price": 7000, + "latitude": 35.1092342130678, + "longitude": 129.015966141997, + "pageIndex": 15, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1404", + "name": "마산손팥칼국수", + "categoryName": "한식", + "address": "부산광역시 서구 자갈치로 10 (충무동1가)", + "phone": "051-243-7226", + "representativeMenu": "팥칼국수", + "price": 6000, + "latitude": 35.0960483857352, + "longitude": 129.025418401842, + "pageIndex": 15, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "17711", + "name": "다반트", + "categoryName": "기타요식업", + "address": "대구광역시 동구 용진길 4 (중대동)", + "phone": "010-9774-7888", + "representativeMenu": "아메리카노", + "price": 4500, + "latitude": 35.97887295843366, + "longitude": 128.6327864396004, + "pageIndex": 15, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17929", + "name": "다온가야밀면", + "categoryName": "한식", + "address": "대구광역시 동구 반야월로 311-11 (신서동)", + "phone": "053-269-7771", + "representativeMenu": "물밀면", + "price": 7000, + "latitude": 35.872498143444936, + "longitude": 128.7226197105274, + "pageIndex": 15, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17932", + "name": "다옴한식뷔페", + "categoryName": "한식", + "address": "대구광역시 동구 동대구로83길 18 (신천동, 동림아파트) 상가동 101호(신천동, 동림아파트)", + "phone": "053-755-4446", + "representativeMenu": "한식뷔페(일반)", + "price": 8000, + "latitude": 35.869577777820616, + "longitude": 128.62410506814535, + "pageIndex": 15, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "13035", + "name": "밥심", + "categoryName": "한식", + "address": "인천광역시 동구 샛골로 129 (송림동) 1층", + "phone": "032-766-5648", + "representativeMenu": "한식부페", + "price": 8000, + "latitude": 37.473936489089034, + "longitude": 126.6440676374549, + "pageIndex": 15, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2099", + "name": "벽란식당", + "categoryName": "한식", + "address": "인천광역시 동구 화도진로 102-4 (화수동) 1층", + "phone": "032-766-7022", + "representativeMenu": "벤뎅이덮밥", + "price": 9000, + "latitude": 37.48049836555108, + "longitude": 126.6292399056602, + "pageIndex": 15, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "17584", + "name": "브레드 오뉴", + "categoryName": "기타요식업", + "address": "인천 동구 송미로 50 1층, 2층 일부호 (송림동)", + "phone": "032-777-4997", + "representativeMenu": "단팥빵", + "price": 2000, + "latitude": 37.4826357598258, + "longitude": 126.650995645435, + "pageIndex": 15, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2302", + "name": "김강심 칼국수", + "categoryName": "한식", + "address": "광주광역시 서구 회재로 841 (매월동)", + "phone": "062-682-8801", + "representativeMenu": "바지락칼국수", + "price": 9000, + "latitude": 35.1193464844953, + "longitude": 126.857831988453, + "pageIndex": 15, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "11081", + "name": "꺳잎머리", + "categoryName": "미용업", + "address": "광주광역시 서구 경열로76번길 6 (농성동)", + "phone": "062-361-1781", + "representativeMenu": "성인커트", + "price": 10000, + "latitude": 35.151028051239, + "longitude": 126.894249830137, + "pageIndex": 15, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "14467", + "name": "나주곰탕 노안집", + "categoryName": "한식", + "address": "광주광역시 서구 서광주로 171 (마륵동) 101호", + "phone": "062-382-0525", + "representativeMenu": "곰탕", + "price": 9000, + "latitude": 35.1424008219587, + "longitude": 126.84867047979041, + "pageIndex": 15, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2542", + "name": "정통짜장", + "categoryName": "중식", + "address": "대전광역시 동구 동대전로 102 (대동)", + "phone": "042-631-3535", + "representativeMenu": "짜장면", + "price": 7500, + "latitude": 36.3313112531177, + "longitude": 127.444394046485, + "pageIndex": 15, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "18133", + "name": "제일국수", + "categoryName": "한식", + "address": "대전광역시 동구 계족로 398 (성남동) (성남동)", + "phone": "042-626-7467", + "representativeMenu": "잔치국수", + "price": 5000, + "latitude": 36.34716116444066, + "longitude": 127.43251503519676, + "pageIndex": 15, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "18134", + "name": "제일짜장", + "categoryName": "중식", + "address": "대전광역시 동구 옥천로176번길 15-17 (판암동) (판암동)", + "phone": "042-322-9999", + "representativeMenu": "짜장면", + "price": 3900, + "latitude": 36.316651760925616, + "longitude": 127.46034281277733, + "pageIndex": 15, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "16820", + "name": "동호손칼국수", + "categoryName": "한식", + "address": "울산광역시 남구 월평로37번길 8 (신정동) 1층", + "phone": "0507-1348-4813", + "representativeMenu": "손칼국수", + "price": 6000, + "latitude": 35.542081300176, + "longitude": 129.3095258019906, + "pageIndex": 15, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "15119", + "name": "만리장성", + "categoryName": "중식", + "address": "울산광역시 남구 번영로233번길 2 (신정동) 1층", + "phone": "052-256-1325", + "representativeMenu": "유니짜장", + "price": 6000, + "latitude": 35.5456689671537, + "longitude": 129.32345313112896, + "pageIndex": 15, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2900", + "name": "만추", + "categoryName": "한식", + "address": "울산광역시 남구 돋질로239번길 4-4", + "phone": "052-260-0123", + "representativeMenu": "낙지볶음", + "price": 9000, + "latitude": 35.5434956538115, + "longitude": 129.331253640737, + "pageIndex": 15, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "14019", + "name": "미랑", + "categoryName": "한식", + "address": "경기도 수원시 장안구 만석로 181 (송죽동) 1층", + "phone": "031-255-7200", + "representativeMenu": "소고기국밥", + "price": 5900, + "latitude": 37.30303778554212, + "longitude": 126.99929045467675, + "pageIndex": 15, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "3444", + "name": "미소레", + "categoryName": "기타요식업", + "address": "경기도 수원시 팔달구 덕영대로 지하911 (매산로1가) 64호", + "phone": "031-247-9449", + "representativeMenu": "아메리카노", + "price": 3400, + "latitude": 37.2674076594333, + "longitude": 127.00066634836122, + "pageIndex": 15, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "11033", + "name": "미영목욕탕", + "categoryName": "목욕업", + "address": "경기도 수원시 권선구 정조로 432 (세류동)", + "phone": "031-236-5038", + "representativeMenu": "목욕료", + "price": 9000, + "latitude": 37.2474693136438, + "longitude": 127.014533989176, + "pageIndex": 15, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "16810", + "name": "민속왕순대", + "categoryName": "한식", + "address": "경기도 수원시 영통구 매탄로 61 (매탄동) 1층", + "phone": "031-238-5862", + "representativeMenu": "순대국밥", + "price": 9000, + "latitude": 37.25670557506686, + "longitude": 127.04114285158384, + "pageIndex": 15, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "4392", + "name": "안가네밥집", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 남춘로25번길 4-1 1층(퇴계동)", + "phone": "033-255-0528", + "representativeMenu": "두부전골(2인이상)", + "price": 7000, + "latitude": 37.863683071761, + "longitude": 127.730587479698, + "pageIndex": 15, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4393", + "name": "양지뜨락", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 동면 만천양지길 114 양지뜨락", + "phone": "033-244-6690", + "representativeMenu": "손두부전골", + "price": 8000, + "latitude": 37.88764975508379, + "longitude": 127.7665243763595, + "pageIndex": 15, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "16986", + "name": "로얄생고기", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 봉명로 238-1 (사창동) (사창동)", + "phone": "043-262-9233", + "representativeMenu": "점심특선(짜글이)", + "price": 9000, + "latitude": 36.63972616939694, + "longitude": 127.46810353853678, + "pageIndex": 15, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15780", + "name": "마신네제육볶음짜장", + "categoryName": "중식", + "address": "충청북도 청주시 흥덕구 1순환로594번길 48 (봉명동) 1층", + "phone": "043-262-0789", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 36.640964119481026, + "longitude": 127.45784060326959, + "pageIndex": 15, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4966", + "name": "사라헤어라인", + "categoryName": "미용업", + "address": "충청남도 천안시 서북구 양지21길 25 (성정동)", + "phone": "041-575-5093", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.8127160425691, + "longitude": 127.138482517182, + "pageIndex": 15, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4967", + "name": "사천냥클럽", + "categoryName": "미용업", + "address": "충청남도 천안시 서북구 두정로 271 (두정동)", + "phone": "041-564-5283", + "representativeMenu": "커트", + "price": 5000, + "latitude": 36.8332567599608, + "longitude": 127.147007440446, + "pageIndex": 15, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4943", + "name": "삼양순대", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 공설시장1길 9 (대흥동)", + "phone": "041-562-3980", + "representativeMenu": "순대국밥", + "price": 8000, + "latitude": 36.8099075703016, + "longitude": 127.148908259385, + "pageIndex": 15, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4944", + "name": "서산순대집", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 큰시장길 17 (사직동)", + "phone": "041-555-3723", + "representativeMenu": "순대국밥", + "price": 8000, + "latitude": 36.8018424794039, + "longitude": 127.148521406806, + "pageIndex": 15, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "5183", + "name": "가야장", + "categoryName": "중식", + "address": "전북특별자치도 군산시 경암4길 4 경암동", + "phone": "063-442-2760", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 35.9758848350524, + "longitude": 126.731277098377, + "pageIndex": 15, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "14783", + "name": "건행사", + "categoryName": "한식", + "address": "전북특별자치도 군산시 문화로 194 (미장동) 미장동", + "phone": "063-452-3880", + "representativeMenu": "양념 제육(600g)", + "price": 3500, + "latitude": 35.96975827182978, + "longitude": 126.72713182040677, + "pageIndex": 15, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "13060", + "name": "국일식당", + "categoryName": "한식", + "address": "전북특별자치도 군산시 평화길 99-4 (죽성동) 국일식당", + "phone": "063-445-2716", + "representativeMenu": "생선탕", + "price": 10000, + "latitude": 35.98618129717657, + "longitude": 126.71664597988809, + "pageIndex": 15, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5737", + "name": "구봉국수", + "categoryName": "한식", + "address": "전라남도 여수시 좌수영로 18-4 (서교동)", + "phone": "061-641-0389", + "representativeMenu": "잔치국수", + "price": 6000, + "latitude": 34.7420171629949, + "longitude": 127.728259892105, + "pageIndex": 15, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5750", + "name": "구이구이식당", + "categoryName": "한식", + "address": "전라남도 여수시 공화남 2길 19 (공화동)", + "phone": "061-666-6567", + "representativeMenu": "생선구이백반", + "price": 10000, + "latitude": 34.7446444430853, + "longitude": 127.742681652413, + "pageIndex": 15, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5738", + "name": "국수가 맛있는 집", + "categoryName": "한식", + "address": "전라남도 여수시 거북선공원2길 11 (학동)", + "phone": "061-686-6640", + "representativeMenu": "잔치국수", + "price": 6000, + "latitude": 34.7606101451048, + "longitude": 127.66632099815, + "pageIndex": 15, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6391", + "name": "대왕한정식", + "categoryName": "한식", + "address": "경상북도 포항시 남구 상도로26번길 10 대왕한정식", + "phone": "054-277-8570", + "representativeMenu": "된장찌개", + "price": 9000, + "latitude": 36.0112042608931, + "longitude": 129.353700089153, + "pageIndex": 15, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "14877", + "name": "더(the) 공감", + "categoryName": "미용업", + "address": "경상북도 포항시 북구 환호공원길 17-4 (환호동) 1층", + "phone": "054-615-9097", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.06810739171294, + "longitude": 129.39160852450493, + "pageIndex": 15, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6785", + "name": "발리불소사우나", + "categoryName": "목욕업", + "address": "경상남도 창원시 마산회원구 석전동16길 28 (석전동)", + "phone": "055-292-3588", + "representativeMenu": "목욕(대인)", + "price": 7000, + "latitude": 35.2368615089355, + "longitude": 128.579505619476, + "pageIndex": 15, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "7130", + "name": "동문시장고기국수금복식당", + "categoryName": "한식", + "address": "제주특별자치도 제주시 동문로 16", + "phone": "064-757-6055", + "representativeMenu": "고기국수", + "price": 8000, + "latitude": 33.5127966491588, + "longitude": 126.5283196783, + "pageIndex": 15, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7198", + "name": "동진", + "categoryName": "한식", + "address": "제주특별자치도 제주시 동문로4길 9 (일도일동)", + "phone": "-", + "representativeMenu": "멸치국수", + "price": 6000, + "latitude": 33.5120874337174, + "longitude": 126.528242265925, + "pageIndex": 15, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "15821", + "name": "디벨롭(주식회사 마켓로스팅)", + "categoryName": "기타요식업", + "address": "제주특별자치도 제주시 고산동산5길 4 (이도이동) 1층", + "phone": "1800-6816", + "representativeMenu": "플렛 화이트", + "price": 3000, + "latitude": 33.49553210244031, + "longitude": 126.53372036725969, + "pageIndex": 15, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "11580", + "name": "홍순두부", + "categoryName": "한식", + "address": "서울특별시 종로구 명륜길 50 1층", + "phone": "-", + "representativeMenu": "홍순두부", + "price": 5500, + "latitude": 37.5892935619767, + "longitude": 126.994629871047, + "pageIndex": 16, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "13929", + "name": "효자왕족발", + "categoryName": "한식", + "address": "서울특별시 종로구 자하문로 31-2 (통인동) 1층", + "phone": "02-735-7601", + "representativeMenu": "보쌈정식(점심)", + "price": 9000, + "latitude": 37.578801957623014, + "longitude": 126.9712764779437, + "pageIndex": 16, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "19242", + "name": "흥진옥", + "categoryName": "한식", + "address": "서울특별시 종로구 종로5길 19-12 (청진동) 1층", + "phone": "02-732-2214", + "representativeMenu": "뼈해장국", + "price": 10000, + "latitude": 37.571396433403386, + "longitude": 126.98027437148129, + "pageIndex": 16, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1424", + "name": "마을식당", + "categoryName": "한식", + "address": "부산광역시 서구 구덕로148번길 37 (토성동1가)", + "phone": "051-231-7877", + "representativeMenu": "순두부찌개", + "price": 7000, + "latitude": 35.098196151826, + "longitude": 129.022780265053, + "pageIndex": 16, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "16518", + "name": "만두우", + "categoryName": "한식", + "address": "부산광역시 서구 충무대로255번길 5-39 (남부민동) 1층", + "phone": "051-244-1719", + "representativeMenu": "고기만두", + "price": 4500, + "latitude": 35.09250331554456, + "longitude": 129.02367538034923, + "pageIndex": 16, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1425", + "name": "명문김밥", + "categoryName": "한식", + "address": "부산광역시 서구 구덕로 124번길 16-1 (토성동4가)", + "phone": "051-254-9295", + "representativeMenu": "원조김밥", + "price": 2500, + "latitude": 35.097384071594, + "longitude": 129.022711843754, + "pageIndex": 16, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1866", + "name": "도심속바닷가식당", + "categoryName": "일식", + "address": "대구광역시 동구 화랑로 508 (용계동)", + "phone": "053-962-5444", + "representativeMenu": "회덮밥", + "price": 7000, + "latitude": 35.8746900465407, + "longitude": 128.67940877344, + "pageIndex": 16, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17588", + "name": "브레드나눔터", + "categoryName": "베이커리", + "address": "인천 동구 화도진로 36 1층(송현동)", + "phone": "010-6435-2786", + "representativeMenu": "꽈배기 12개", + "price": 5000, + "latitude": 37.4759668751201, + "longitude": 126.634280823838, + "pageIndex": 16, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "13023", + "name": "석이네분식", + "categoryName": "한식", + "address": "인천광역시 동구 샛골로162번길 17 (송림동) 1층 (현대시장 내)", + "phone": "010-2307-9215", + "representativeMenu": "백반", + "price": 7000, + "latitude": 37.47696074463108, + "longitude": 126.64588420994905, + "pageIndex": 16, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "17597", + "name": "선이네생선구이", + "categoryName": "한식", + "address": "인천 동구 샛골로 128-1 1층(송림동)", + "phone": "032-762-4358", + "representativeMenu": "생선구이", + "price": 10000, + "latitude": 37.4737972412671, + "longitude": 126.64440141639, + "pageIndex": 16, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "17610", + "name": "송학남원추어탕", + "categoryName": "한식", + "address": "인천 동구 수문통로 15 1층(송현동)", + "phone": "032-765-5256", + "representativeMenu": "추어탕", + "price": 10000, + "latitude": 37.4779829436, + "longitude": 126.632329265245, + "pageIndex": 16, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2288", + "name": "남진컴퓨터", + "categoryName": "세탁업", + "address": "광주광역시 서구 구성로 109-3 (양동)", + "phone": "062-368-3931", + "representativeMenu": "정장1벌드라이", + "price": 6000, + "latitude": 35.1488007460721, + "longitude": 126.907060175109, + "pageIndex": 16, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "14036", + "name": "담양통추어탕", + "categoryName": "한식", + "address": "광주광역시 서구 월드컵4강로229번길 47 (쌍촌동) 1층", + "phone": "062-384-9980", + "representativeMenu": "추어탕", + "price": 10000, + "latitude": 35.154919778981, + "longitude": 126.86578570780239, + "pageIndex": 16, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "14736", + "name": "대성떡방앗간", + "categoryName": "기타비요식업", + "address": "광주광역시 서구 상무대로1005번길 46 (내방동) 1층", + "phone": "062-365-0059", + "representativeMenu": "참기름 350ml", + "price": 10000, + "latitude": 35.155975871786964, + "longitude": 126.87179971307111, + "pageIndex": 16, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2548", + "name": "주연칼국수", + "categoryName": "한식", + "address": "대전광역시 동구 흥룡로 29-1 (가양동)", + "phone": "042-626-3838", + "representativeMenu": "바지락 칼국수", + "price": 5500, + "latitude": 36.3488890741268, + "longitude": 127.447085109324, + "pageIndex": 16, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "16082", + "name": "중국집", + "categoryName": "중식", + "address": "대전광역시 동구 대전로791번길 44 (중동) 1층 중국집", + "phone": "042-226-0648", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 36.32950860974082, + "longitude": 127.43082628893573, + "pageIndex": 16, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2536", + "name": "지선미용타운", + "categoryName": "미용업", + "address": "대전광역시 동구 솔랑시울길 87 (소제동)", + "phone": "-", + "representativeMenu": "미용료(커트)", + "price": 5000, + "latitude": 36.3353609397276, + "longitude": 127.434496484742, + "pageIndex": 16, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "15108", + "name": "명문칼국수", + "categoryName": "한식", + "address": "울산광역시 남구 월평로37번길 5 (신정동) 1층", + "phone": "052-274-2719", + "representativeMenu": "손칼국수", + "price": 6000, + "latitude": 35.541808785746284, + "longitude": 129.30930615922028, + "pageIndex": 16, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "11681", + "name": "모두랑국수먹는날", + "categoryName": "한식", + "address": "울산광역시 남구 수암로 54번길 17 1층", + "phone": "052-260-2411", + "representativeMenu": "국수", + "price": 5000, + "latitude": 35.5292661959496, + "longitude": 129.313833538665, + "pageIndex": 16, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "17495", + "name": "무교동갈비", + "categoryName": "한식", + "address": "울산광역시 남구 북부순환도로13번길 6-3 (무거동) 1층", + "phone": "052-221-0472", + "representativeMenu": "뷔페정식", + "price": 8000, + "latitude": 35.55177936582806, + "longitude": 129.26483586878214, + "pageIndex": 16, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "3440", + "name": "백천홍두깨칼국수", + "categoryName": "한식", + "address": "경기도 수원시 팔달구 수원천로 264번길 21-11", + "phone": "070-7559-1261", + "representativeMenu": "칼국수(보통)", + "price": 5000, + "latitude": 37.2774167510803, + "longitude": 127.019892600036, + "pageIndex": 16, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "13686", + "name": "벽산세탁소", + "categoryName": "세탁업", + "address": "경기도 수원시 장안구 파장로 53 (정자동, 정자 벽산블루밍) 108호", + "phone": "031-245-1269", + "representativeMenu": "드라이클리닝", + "price": 6000, + "latitude": 37.3054993333515, + "longitude": 126.99475365985273, + "pageIndex": 16, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "4394", + "name": "언덕집", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 금강로83번길 6 (운교동)", + "phone": "033-256-2220", + "representativeMenu": "보리밥뷔페", + "price": 7000, + "latitude": 37.8783065745038, + "longitude": 127.730392278353, + "pageIndex": 16, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4357", + "name": "연화각", + "categoryName": "중식", + "address": "강원특별자치도 춘천시 충혼길52번길 20-23 (온의동)", + "phone": "033-256-5863", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 37.8617817124155, + "longitude": 127.71898650725, + "pageIndex": 16, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "16640", + "name": "예대미용실", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 후만로 69-1 (후평동) 1층", + "phone": "033-241-2937", + "representativeMenu": "커트", + "price": 8000, + "latitude": 37.87612733597792, + "longitude": 127.75221288287008, + "pageIndex": 16, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4395", + "name": "옛촌", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 향교앞길 4 (교동)", + "phone": "033-254-1511", + "representativeMenu": "백반", + "price": 8000, + "latitude": 37.8810059576535, + "longitude": 127.734270783857, + "pageIndex": 16, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "14740", + "name": "만나김치식당", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 남이면 양촌2길 12 (남이면)", + "phone": "043-295-2366", + "representativeMenu": "백반", + "price": 6000, + "latitude": 36.59132784293651, + "longitude": 127.46831535088468, + "pageIndex": 16, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "14764", + "name": "만리장성", + "categoryName": "중식", + "address": "충청북도 청주시 흥덕구 덕암로 2 (봉명동) 봉명동 2803", + "phone": "0507-1440-7787", + "representativeMenu": "짬뽕", + "price": 8000, + "latitude": 36.6525501290308, + "longitude": 127.45432666746878, + "pageIndex": 16, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "14755", + "name": "맘이가", + "categoryName": "한식", + "address": "충청북도 청주시 청원구 팔결로 170 (외남동)", + "phone": "043-225-0033", + "representativeMenu": "백반", + "price": 6000, + "latitude": 36.69825806579632, + "longitude": 127.48090489420618, + "pageIndex": 16, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "16991", + "name": "메르토스트", + "categoryName": "기타요식업", + "address": "충청북도 청주시 서원구 창신로26번길 4 (사창동) (사창동)", + "phone": "0507-1411-1253", + "representativeMenu": "계란햄치즈토스트", + "price": 3000, + "latitude": 36.633121740098396, + "longitude": 127.46456147717325, + "pageIndex": 16, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4945", + "name": "서산집", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 사직로 18 (사직동)", + "phone": "041-552-6650", + "representativeMenu": "순대국밥", + "price": 8000, + "latitude": 36.8017540145744, + "longitude": 127.149508884078, + "pageIndex": 16, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "11164", + "name": "선경세탁소", + "categoryName": "세탁업", + "address": "충청남도 천안시 동남구 양지4길 15 (봉명동)", + "phone": "041-574-7001", + "representativeMenu": "신사복드라이", + "price": 9000, + "latitude": 36.8084580969342, + "longitude": 127.137098587523, + "pageIndex": 16, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "14780", + "name": "군산꽁보리", + "categoryName": "한식", + "address": "전북특별자치도 군산시 신금길 18 (신영동) 2층, 414호(공설시장)", + "phone": "063-732-6065", + "representativeMenu": "보리비빔밥", + "price": 5500, + "latitude": 35.98317108726744, + "longitude": 126.72032453610362, + "pageIndex": 16, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5187", + "name": "꼬꼬꽥꽥", + "categoryName": "한식", + "address": "전북특별자치도 군산시 백릉로 35 경암동", + "phone": "063-464-4943", + "representativeMenu": "닭곰탕", + "price": 8000, + "latitude": 35.9750863927652, + "longitude": 126.729895097495, + "pageIndex": 16, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5188", + "name": "나라식당", + "categoryName": "한식", + "address": "전북특별자치도 군산시 서흥안2길 30 문화동", + "phone": "063-462-0673", + "representativeMenu": "백반", + "price": 8000, + "latitude": 35.9722769459747, + "longitude": 126.708026322198, + "pageIndex": 16, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "16455", + "name": "김연선 헤어뷰", + "categoryName": "미용업", + "address": "전라남도 여수시 돌산읍 강남동로 46-19 1층 104호(청솔상가)", + "phone": "061-643-0669", + "representativeMenu": "소아 커트", + "price": 8000, + "latitude": 34.71784643561729, + "longitude": 127.75728889294511, + "pageIndex": 16, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5712", + "name": "남성커트클럽 블루가이", + "categoryName": "이용업", + "address": "전라남도 여수시 여문2로 97 (문수동)", + "phone": "061-653-0317", + "representativeMenu": "커트", + "price": 10000, + "latitude": 34.7547925562527, + "longitude": 127.695461279732, + "pageIndex": 16, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5751", + "name": "남촌식당", + "categoryName": "한식", + "address": "전라남도 여수시 신기남길 33 (신기동)", + "phone": "061-682-1619", + "representativeMenu": "삼겹살", + "price": 10000, + "latitude": 34.7607366293679, + "longitude": 127.672645842488, + "pageIndex": 16, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "13340", + "name": "내고향식당", + "categoryName": "한식", + "address": "전라남도 여수시 어항로 13-1 (봉산동) 내고향식당", + "phone": "061-642-9032", + "representativeMenu": "비빔밥(점심)", + "price": 7000, + "latitude": 34.732507178346495, + "longitude": 127.72016163447275, + "pageIndex": 16, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6435", + "name": "더헤어", + "categoryName": "미용업", + "address": "경상북도 포항시 북구 흥해읍 중성로32번길 14 (1층)", + "phone": "054-261-6785", + "representativeMenu": "여성커트", + "price": 8000, + "latitude": 36.1074175340788, + "longitude": 129.346453745514, + "pageIndex": 16, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6484", + "name": "돈부야", + "categoryName": "일식", + "address": "경상북도 포항시 북구 삼호로8번길 1 1층", + "phone": "054-246-0771", + "representativeMenu": "우동", + "price": 7500, + "latitude": 36.0418016819054, + "longitude": 129.36741065867, + "pageIndex": 16, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6436", + "name": "동동이네시니어펌전문점", + "categoryName": "미용업", + "address": "경상북도 포항시 북구 월막길 2", + "phone": "054-247-6989", + "representativeMenu": "커트", + "price": 7000, + "latitude": 36.049135177781, + "longitude": 129.367187661289, + "pageIndex": 16, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6812", + "name": "보리밥집", + "categoryName": "한식", + "address": "경상남도 창원시 성산구 마디미로28 D동 203호 (상남동, 상남재래시장)", + "phone": "055-263-7298", + "representativeMenu": "보리밥", + "price": 7000, + "latitude": 35.2222148675498, + "longitude": 128.683379990696, + "pageIndex": 16, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "6772", + "name": "복가득뚱보갈비", + "categoryName": "한식", + "address": "경상남도 창원시 마산합포구 노산서18길 114 (교방동)", + "phone": "055-243-0172", + "representativeMenu": "물냉면", + "price": 6000, + "latitude": 35.2148634483788, + "longitude": 128.564078372768, + "pageIndex": 16, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "6835", + "name": "복개천감자탕", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 중원로79번길 13-1 (송학동)", + "phone": "055-546-5580", + "representativeMenu": "뼈해장국", + "price": 8000, + "latitude": 35.1513509347336, + "longitude": 128.65847962579, + "pageIndex": 16, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "14091", + "name": "복자네뼈해장국", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 이동로38번길 8 (이동) 1층", + "phone": "0507-1404-5766", + "representativeMenu": "뼈해장국(1인분)", + "price": 8500, + "latitude": 35.15045351250758, + "longitude": 128.6957928158142, + "pageIndex": 16, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "11864", + "name": "또옵서식당", + "categoryName": "한식", + "address": "제주특별자치도 제주시 번영로 459", + "phone": "064-794-0051", + "representativeMenu": "된장찌개", + "price": 6000, + "latitude": 33.4922578901329, + "longitude": 126.591494992712, + "pageIndex": 16, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7216", + "name": "또와헤어샾", + "categoryName": "미용업", + "address": "제주특별자치도 제주시 지석6길 9 또와헤어샾", + "phone": "064-756-6528", + "representativeMenu": "커트", + "price": 10000, + "latitude": 33.5203409285847, + "longitude": 126.581100037461, + "pageIndex": 16, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "18037", + "name": "뛰니뛰니", + "categoryName": "기타비요식업", + "address": "제주특별자치도 제주시 연신로 76 (이도이동) 뛰니뛰니", + "phone": "064-727-0369", + "representativeMenu": "어린이입장료", + "price": 6000, + "latitude": 33.4946894705016, + "longitude": 126.54802422292092, + "pageIndex": 16, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "968", + "name": "345국수", + "categoryName": "한식", + "address": "서울특별시 중구 청구로 83-9 1층", + "phone": "02-223-5257", + "representativeMenu": "손수제비", + "price": 8000, + "latitude": 37.560172073734, + "longitude": 127.013228537389, + "pageIndex": 17, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "979", + "name": "거목식당", + "categoryName": "한식", + "address": "서울특별시 중구 을지로36길 18 (을지로5가)", + "phone": "02-226-0010", + "representativeMenu": "된장찌개", + "price": 7000, + "latitude": 37.5659767545803, + "longitude": 127.001477572948, + "pageIndex": 17, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "16085", + "name": "공화춘", + "categoryName": "중식", + "address": "서울특별시 중구 청파로 457-1 (중림동) 2층", + "phone": "02-723-4038", + "representativeMenu": "짜장면", + "price": 6000, + "latitude": 37.56020605082971, + "longitude": 126.96781059246393, + "pageIndex": 17, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1426", + "name": "미소정 멸치국수", + "categoryName": "한식", + "address": "부산광역시 서구 대영로 85번길 24-1 (동대신동2가)", + "phone": "051-242-8531", + "representativeMenu": "멸치국수", + "price": 4000, + "latitude": 35.1121156286607, + "longitude": 129.020062894885, + "pageIndex": 17, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1427", + "name": "바보면가", + "categoryName": "한식", + "address": "부산광역시 서구 임시수도기념로 21-10 (부민동3가)", + "phone": "051-255-8336", + "representativeMenu": "손칼국수+돈까스", + "price": 7000, + "latitude": 35.1030261158569, + "longitude": 129.018574291429, + "pageIndex": 17, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "17740", + "name": "동림식당", + "categoryName": "한식", + "address": "대구광역시 동구 파계로116길 49 (중대동)", + "phone": "053-982-2860", + "representativeMenu": "칼국수", + "price": 4000, + "latitude": 35.98617130128156, + "longitude": 128.6374302331672, + "pageIndex": 17, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "1871", + "name": "만보칼국수", + "categoryName": "한식", + "address": "대구광역시 동구 팔공로209길 15 (백안동)", + "phone": "053-982-7524", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 35.9539345412516, + "longitude": 128.693850133799, + "pageIndex": 17, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "14063", + "name": "머리쟁이", + "categoryName": "미용업", + "address": "대구광역시 동구 아양로 117 (신암동)", + "phone": "053-957-0050", + "representativeMenu": "커트", + "price": 8000, + "latitude": 35.88476589784557, + "longitude": 128.62608730053938, + "pageIndex": 17, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "13034", + "name": "시온이네집밥", + "categoryName": "한식", + "address": "인천광역시 동구 샛골로 168-3 (송림동) 1층 (현대시장 내)", + "phone": "032-710-9274", + "representativeMenu": "청국장", + "price": 6000, + "latitude": 37.47735018760701, + "longitude": 126.6452386553363, + "pageIndex": 17, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2101", + "name": "신대청마루", + "categoryName": "한식", + "address": "인천광역시 동구 방축로 105 (송림동, 산업용품유통센타 편익 C동 지하 21호)", + "phone": "032-589-2355", + "representativeMenu": "백반", + "price": 7000, + "latitude": 37.4841229128464, + "longitude": 126.656111822612, + "pageIndex": 17, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2311", + "name": "대정회관", + "categoryName": "한식", + "address": "광주광역시 서구 대남대로462번길 3 (농성동)", + "phone": "062-364-7999", + "representativeMenu": "청국장", + "price": 8000, + "latitude": 35.1515316175609, + "longitude": 126.88645436957, + "pageIndex": 17, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "14042", + "name": "대흥민속목욕탕", + "categoryName": "목욕업", + "address": "광주광역시 서구 운천로 34 (금호동) 금호1동", + "phone": "062-374-7079", + "representativeMenu": "성인", + "price": 7000, + "latitude": 35.13576588266793, + "longitude": 126.85944266692678, + "pageIndex": 17, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "13667", + "name": "동해계절맛집", + "categoryName": "한식", + "address": "광주광역시 서구 계수로 56 (쌍촌동) 1층", + "phone": "062-385-6363", + "representativeMenu": "김치찌개", + "price": 9000, + "latitude": 35.16026802456817, + "longitude": 126.8567005862501, + "pageIndex": 17, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2312", + "name": "동해회바다", + "categoryName": "한식", + "address": "광주광역시 서구 경열로26번길 3 (농성동)", + "phone": "062-367-1023", + "representativeMenu": "생선초밥", + "price": 10000, + "latitude": 35.1514604680169, + "longitude": 126.88894822121, + "pageIndex": 17, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "16084", + "name": "진심만두", + "categoryName": "한식", + "address": "대전광역시 동구 대전로785번길 41-3 (원동) 119호", + "phone": "0507-1378-0210", + "representativeMenu": "고기교자만두", + "price": 5000, + "latitude": 36.32895512036512, + "longitude": 127.4312023578623, + "pageIndex": 17, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2549", + "name": "진짜손칼국수", + "categoryName": "한식", + "address": "대전광역시 동구 우암로 341-2 (가양동)", + "phone": "042-622-0109", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 36.3496914492847, + "longitude": 127.451550689252, + "pageIndex": 17, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2537", + "name": "착한미용실", + "categoryName": "미용업", + "address": "대전광역시 동구 중앙로204번길 6 (중동)", + "phone": "042-222-0594", + "representativeMenu": "미용료(커트)", + "price": 9000, + "latitude": 36.3305464456764, + "longitude": 127.431800535141, + "pageIndex": 17, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "18135", + "name": "커피언니", + "categoryName": "기타요식업", + "address": "대전광역시 동구 중앙로203번길 36 (중동) (중동)", + "phone": "010-2254-7813", + "representativeMenu": "아메리카노", + "price": 3000, + "latitude": 36.33236086267619, + "longitude": 127.43077262275567, + "pageIndex": 17, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "15095", + "name": "미송", + "categoryName": "한식", + "address": "울산광역시 남구 수암로 171 (야음동) 1층", + "phone": "052-276-5445", + "representativeMenu": "김밥", + "price": 4000, + "latitude": 35.52698514192849, + "longitude": 129.3255895227967, + "pageIndex": 17, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2890", + "name": "산동만두", + "categoryName": "중식", + "address": "울산광역시 남구 월평로37번길 8", + "phone": "052-226-7459", + "representativeMenu": "자장면", + "price": 5000, + "latitude": 35.5421330808182, + "longitude": 129.309578534087, + "pageIndex": 17, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "13862", + "name": "삼산국수", + "categoryName": "한식", + "address": "울산광역시 남구 삼산중로 41 (달동) 1층", + "phone": "052-256-3998", + "representativeMenu": "손칼국수", + "price": 6000, + "latitude": 35.53723577488518, + "longitude": 129.3356157418434, + "pageIndex": 17, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "11692", + "name": "분식나라 김밥마을", + "categoryName": "한식", + "address": "경기도 수원시 영통구 덕영대로 1699 (영통동)", + "phone": "031-206-0599", + "representativeMenu": "비빔밥", + "price": 7500, + "latitude": 37.2479713166815, + "longitude": 127.077097970377, + "pageIndex": 17, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "3395", + "name": "붕붕샐러드", + "categoryName": "기타요식업", + "address": "경기도 수원시 영통구 신원로 146 .", + "phone": "031-206-9891", + "representativeMenu": "아메리카노", + "price": 3000, + "latitude": 37.2494809372827, + "longitude": 127.057033352061, + "pageIndex": 17, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "14021", + "name": "블랙남성컷", + "categoryName": "미용업", + "address": "경기도 수원시 권선구 세권로 76 (세류동, 경일아파트) 105호", + "phone": "031-234-7585", + "representativeMenu": "커트", + "price": 9000, + "latitude": 37.25844888657563, + "longitude": 127.01243796592051, + "pageIndex": 17, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "13803", + "name": "오믈하우스", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 서부대성로227번길 24 (효자동) 2층", + "phone": "0507-1363-1331", + "representativeMenu": "오므라이스", + "price": 5000, + "latitude": 37.873367758817224, + "longitude": 127.74517942757676, + "pageIndex": 17, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4364", + "name": "와송칼국수", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 명동길 46-1 (중앙로3가)", + "phone": "033-255-7600", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 37.8772319253533, + "longitude": 127.724402610449, + "pageIndex": 17, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4396", + "name": "왕가", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 석사길 15 (석사동)", + "phone": "033-261-0559", + "representativeMenu": "한식뷔페", + "price": 6000, + "latitude": 37.8579328322032, + "longitude": 127.744971856148, + "pageIndex": 17, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4762", + "name": "명동식당", + "categoryName": "한식", + "address": "충청북도 청주시 흥덕구 천석로 56 (봉명동)", + "phone": "043-268-8320", + "representativeMenu": "청국장", + "price": 7000, + "latitude": 36.6403799754467, + "longitude": 127.465551511862, + "pageIndex": 17, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15524", + "name": "몽키만두", + "categoryName": "한식", + "address": "충청북도 청주시 흥덕구 사직대로 62 (복대동) 101호(몽키만두)", + "phone": "043-260-9132", + "representativeMenu": "고기만두", + "price": 5000, + "latitude": 36.634284271568625, + "longitude": 127.45416055015329, + "pageIndex": 17, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "18723", + "name": "성환반점", + "categoryName": "중식", + "address": "충청남도 천안시 서북구 성환읍 성진로 27 성환반점", + "phone": "041-581-3353", + "representativeMenu": "짬뽕", + "price": 9000, + "latitude": 36.91213733151647, + "longitude": 127.13855017469305, + "pageIndex": 17, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4968", + "name": "세븐데이남성커트", + "categoryName": "이용업", + "address": "충청남도 천안시 서북구 두정로 212 107호 (두정동)", + "phone": "041-568-6808", + "representativeMenu": "커트", + "price": 9000, + "latitude": 36.8333285541934, + "longitude": 127.140544198528, + "pageIndex": 17, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "14668", + "name": "셀프밥집", + "categoryName": "한식", + "address": "충청남도 천안시 서북구 나사렛대길 22-4 (쌍용동) .", + "phone": "041-575-1213", + "representativeMenu": "고추장불고기", + "price": 8000, + "latitude": 36.79863752371899, + "longitude": 127.11896536809991, + "pageIndex": 17, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "17033", + "name": "담다한식뷔페", + "categoryName": "한식", + "address": "전북특별자치도 군산시 문화로 151 (수송동) 1층", + "phone": "0507-1495-1228", + "representativeMenu": "한식(초등학생)", + "price": 7000, + "latitude": 35.9701627176277, + "longitude": 126.72247350070346, + "pageIndex": 17, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "17032", + "name": "돔베초밥", + "categoryName": "일식", + "address": "전북특별자치도 군산시 수송남로 2 (수송동) 1층", + "phone": "063-471-8884", + "representativeMenu": "모듬초밥(10p)", + "price": 10000, + "latitude": 35.95970510152752, + "longitude": 126.71457201468164, + "pageIndex": 17, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "13056", + "name": "동신미용실", + "categoryName": "미용업", + "address": "전북특별자치도 군산시 창성3길 13 (개복동) 동신미용실", + "phone": "-", + "representativeMenu": "커트(여)", + "price": 8000, + "latitude": 35.984326824789925, + "longitude": 126.71266844987171, + "pageIndex": 17, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5713", + "name": "뉴미인", + "categoryName": "미용업", + "address": "전라남도 여수시 구봉산길 11-2 (국동)", + "phone": "061-642-7885", + "representativeMenu": "여성 커트", + "price": 10000, + "latitude": 34.7344828301154, + "longitude": 127.718486920101, + "pageIndex": 17, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5714", + "name": "늘고운 미용실", + "categoryName": "미용업", + "address": "전라남도 여수시 서교 6길 5 (서교동)", + "phone": "-", + "representativeMenu": "커트", + "price": 7000, + "latitude": 34.7423213814819, + "longitude": 127.728456280602, + "pageIndex": 17, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5753", + "name": "대명한우소머리곰탕", + "categoryName": "한식", + "address": "전라남도 여수시 시청서5길 8 (학동)", + "phone": "061-666-9988", + "representativeMenu": "한우설렁탕", + "price": 9000, + "latitude": 34.7567070697685, + "longitude": 127.657703549652, + "pageIndex": 17, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5754", + "name": "대성국밥", + "categoryName": "한식", + "address": "전라남도 여수시 좌수영로 42 (서교동)", + "phone": "-", + "representativeMenu": "소머리국밥", + "price": 6000, + "latitude": 34.7440040528689, + "longitude": 127.728555791744, + "pageIndex": 17, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6328", + "name": "동진이용소", + "categoryName": "이용업", + "address": "경상북도 포항시 남구 대해로 114", + "phone": "-", + "representativeMenu": "성인조발", + "price": 10000, + "latitude": 36.0208157469306, + "longitude": 129.364260018145, + "pageIndex": 17, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "18349", + "name": "두꺼비컷트", + "categoryName": "미용업", + "address": "경상북도 포항시 북구 문화로13번길 4 (덕수동) 두꺼비컷트", + "phone": "010-4538-8845", + "representativeMenu": "성인커트", + "price": 10000, + "latitude": 36.04313828411014, + "longitude": 129.36688103561735, + "pageIndex": 17, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "14090", + "name": "본가냉면", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 이동로40번길 5 (이동) 1층, 이동 화이트광장 뒷골목", + "phone": "010-6664-3402", + "representativeMenu": "물냉면", + "price": 8000, + "latitude": 35.150346456545456, + "longitude": 128.69686429984836, + "pageIndex": 17, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "15394", + "name": "블랙맨", + "categoryName": "미용업", + "address": "경상남도 창원시 성산구 안민안길 17 (안민동) 103호, 블랙맨", + "phone": "070-4065-4802", + "representativeMenu": "컷트", + "price": 9000, + "latitude": 35.186564515371245, + "longitude": 128.69139110245138, + "pageIndex": 17, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "19281", + "name": "블루남성컷트", + "categoryName": "이용업", + "address": "경상남도 창원시 성산구 창이대로901번길 4 (대방동) 상가 112동 109호", + "phone": "010-4551-0848", + "representativeMenu": "컷트", + "price": 8000, + "latitude": 35.2047363655845, + "longitude": 128.70762132136215, + "pageIndex": 17, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "7046", + "name": "라영헤어숍", + "categoryName": "미용업", + "address": "제주특별자치도 제주시 다랑곶4길 18", + "phone": "064-744-3340", + "representativeMenu": "커트", + "price": 10000, + "latitude": 33.4883543798046, + "longitude": 126.480241062992, + "pageIndex": 17, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7135", + "name": "마라톤식당", + "categoryName": "한식", + "address": "제주특별자치도 제주시 서광로2길 15", + "phone": "064-757-1945", + "representativeMenu": "김치찌개", + "price": 7000, + "latitude": 33.498788985127, + "longitude": 126.512962697004, + "pageIndex": 17, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "980", + "name": "광희식당", + "categoryName": "한식", + "address": "서울특별시 중구 장충단로 212-2 (광희동2가)", + "phone": "02-226-1555", + "representativeMenu": "된장찌개", + "price": 7000, + "latitude": 37.5636366550507, + "longitude": 127.007233814214, + "pageIndex": 18, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "16087", + "name": "뉴욕스넥", + "categoryName": "한식", + "address": "서울특별시 중구 을지로 지하131 (을지로3가) 56호", + "phone": "02-2274-1110", + "representativeMenu": "야채김밥", + "price": 3500, + "latitude": 37.566442970950895, + "longitude": 126.99419916729464, + "pageIndex": 18, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "15373", + "name": "대박물갈비", + "categoryName": "한식", + "address": "서울특별시 중구 퇴계로4길 7 (남창동) 1층", + "phone": "02-755-6974", + "representativeMenu": "제육볶음", + "price": 9000, + "latitude": 37.557537858746215, + "longitude": 126.97773067463572, + "pageIndex": 18, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "981", + "name": "돈우가식당", + "categoryName": "한식", + "address": "서울특별시 중구 남대문로 25-17 1층(북창동)", + "phone": "02-378-1313", + "representativeMenu": "김치찌개", + "price": 7000, + "latitude": 37.5620306338518, + "longitude": 126.97816807023, + "pageIndex": 18, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "16515", + "name": "베이커리 루마카", + "categoryName": "기타요식업", + "address": "부산광역시 서구 구덕로265번길 34 (서대신동1가) 1층", + "phone": "051-243-4874", + "representativeMenu": "소금빵", + "price": 2200, + "latitude": 35.10824564582161, + "longitude": 129.0177444252452, + "pageIndex": 18, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1428", + "name": "별미집", + "categoryName": "한식", + "address": "부산광역시 서구 구덕로333번길 14 (서대신동3가)", + "phone": "051-254-9891", + "representativeMenu": "추어탕", + "price": 8000, + "latitude": 35.1133251923336, + "longitude": 129.015381162663, + "pageIndex": 18, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "15507", + "name": "보리정", + "categoryName": "한식", + "address": "부산광역시 서구 대영로74번길 27-7 (동대신동1가) 1층", + "phone": "051-248-8703", + "representativeMenu": "집밥정식", + "price": 7000, + "latitude": 35.109533954678874, + "longitude": 129.02014367187397, + "pageIndex": 18, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "17744", + "name": "면만드는사람들 팔공산점", + "categoryName": "한식", + "address": "대구광역시 동구 팔공로197길 39 (백안동)", + "phone": "053-982-3511", + "representativeMenu": "온메밀국수", + "price": 8000, + "latitude": 35.955968687471895, + "longitude": 128.69084189551475, + "pageIndex": 18, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "1872", + "name": "면사랑칼국수", + "categoryName": "한식", + "address": "대구광역시 동구 효동로 126 2층(효목동)", + "phone": "053-954-2001", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 35.8872864225781, + "longitude": 128.64111680973, + "pageIndex": 18, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "18130", + "name": "미도반점", + "categoryName": "중식", + "address": "대구광역시 동구 송라로12길 29 (신천동)", + "phone": "053-755-7524", + "representativeMenu": "짜장면", + "price": 4000, + "latitude": 35.874262579029114, + "longitude": 128.62137941519185, + "pageIndex": 18, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "1864", + "name": "미미미용실", + "categoryName": "미용업", + "address": "대구광역시 동구 동촌로346 백자맨션상가9동 103호(용계동)", + "phone": "010-5689-8867", + "representativeMenu": "컷(어린이)", + "price": 7000, + "latitude": 35.8767267035582, + "longitude": 128.677260687309, + "pageIndex": 18, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2102", + "name": "아구군갈비양", + "categoryName": "한식", + "address": "인천광역시 동구 샛골로 117-1 (송림동)", + "phone": "032-766-5002", + "representativeMenu": "백반", + "price": 6000, + "latitude": 37.472961619829, + "longitude": 126.643853360434, + "pageIndex": 18, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "13021", + "name": "어도(魚島)", + "categoryName": "한식", + "address": "인천광역시 동구 금곡로 56-1 (금곡동) 1층", + "phone": "032-761-8887", + "representativeMenu": "순두부", + "price": 8000, + "latitude": 37.47325445087663, + "longitude": 126.6418035282525, + "pageIndex": 18, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "13026", + "name": "에불바리 떡볶이", + "categoryName": "기타요식업", + "address": "인천광역시 동구 중앙로 15 (금곡동) 1층", + "phone": "070-8818-1919", + "representativeMenu": "아메리카노", + "price": 1500, + "latitude": 37.4734332394039, + "longitude": 126.63504718718686, + "pageIndex": 18, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "14300", + "name": "와글와글 맛있는 국수집", + "categoryName": "한식", + "address": "인천광역시 동구 솔빛로 87-1 (송림동) 1층", + "phone": "032-764-3959", + "representativeMenu": "잔치국수", + "price": 5000, + "latitude": 37.47626291093436, + "longitude": 126.64329789059067, + "pageIndex": 18, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2289", + "name": "럭키세탁소", + "categoryName": "세탁업", + "address": "광주광역시 서구 염화로40번길 20 (화정동)", + "phone": "062-372-6901", + "representativeMenu": "양복1벌드라이", + "price": 7000, + "latitude": 35.1366373980231, + "longitude": 126.87488624762, + "pageIndex": 18, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2315", + "name": "모리화", + "categoryName": "중식", + "address": "광주광역시 서구 풍금로 31-1 (풍암동)", + "phone": "062-652-8552", + "representativeMenu": "짜장면", + "price": 5500, + "latitude": 35.122275201315, + "longitude": 126.863800531773, + "pageIndex": 18, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2316", + "name": "무등회관", + "categoryName": "한식", + "address": "광주광역시 서구 죽봉대로111번길 12 (광천동)", + "phone": "062-364-2746", + "representativeMenu": "돌솥비빔밥", + "price": 9000, + "latitude": 35.1635482674685, + "longitude": 126.882202724351, + "pageIndex": 18, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2573", + "name": "평양숨두부", + "categoryName": "한식", + "address": "대전광역시 동구 대전로 387 (가오동) (대성동)", + "phone": "042-284-4141", + "representativeMenu": "숨두부백반", + "price": 7000, + "latitude": 36.30147395225034, + "longitude": 127.45595934984647, + "pageIndex": 18, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "14917", + "name": "하늘이용원", + "categoryName": "이용업", + "address": "대전광역시 동구 비래서로42번길 89 (가양동) 가양동", + "phone": "-", + "representativeMenu": "컷트", + "price": 6000, + "latitude": 36.3502938694925, + "longitude": 127.44709305501448, + "pageIndex": 18, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2578", + "name": "한성짜장", + "categoryName": "중식", + "address": "대전광역시 동구 흥룡로74 (가양동)", + "phone": "042-635-2160", + "representativeMenu": "갈비탕", + "price": 8500, + "latitude": 36.3505650431666, + "longitude": 127.45145602789, + "pageIndex": 18, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "16814", + "name": "삼산국수전문점", + "categoryName": "한식", + "address": "울산광역시 남구 돋질로251번길 14-1 (삼산동) 1층", + "phone": "052-258-3998", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 35.544723940575366, + "longitude": 129.3321969935939, + "pageIndex": 18, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "11682", + "name": "삼시세끼만포장", + "categoryName": "한식", + "address": "울산광역시 남구 대학로 145번길 25", + "phone": "052-223-7847", + "representativeMenu": "정식", + "price": 9000, + "latitude": 35.5491127108744, + "longitude": 129.25926425639, + "pageIndex": 18, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "15101", + "name": "삼호밀면", + "categoryName": "한식", + "address": "울산광역시 남구 삼호로37번길 13 (무거동) 1층", + "phone": "052-277-6933", + "representativeMenu": "밀면", + "price": 7000, + "latitude": 35.551037197714315, + "longitude": 129.26826664991606, + "pageIndex": 18, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "18124", + "name": "블럭앤키즈 화서점", + "categoryName": "기타비요식업", + "address": "경기도 수원시 팔달구 일월로22번길 22-5 (화서동) 301호", + "phone": "031-292-9265", + "representativeMenu": "블럭방(1시간)", + "price": 7000, + "latitude": 37.28583154659394, + "longitude": 126.98139721057959, + "pageIndex": 18, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "14527", + "name": "빵마루", + "categoryName": "베이커리", + "address": "경기도 수원시 팔달구 세지로174번길 33 (인계동) 1층", + "phone": "031-225-4507", + "representativeMenu": "팥빵", + "price": 1700, + "latitude": 37.26651280121945, + "longitude": 127.02379082579587, + "pageIndex": 18, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "15307", + "name": "사랑이네 베트남쌀국수", + "categoryName": "기타요식업", + "address": "경기도 수원시 장안구 조원로89번길 44-2 (조원동) 1층", + "phone": "010-3122-7457", + "representativeMenu": "쌀국수", + "price": 5000, + "latitude": 37.29972174575287, + "longitude": 127.0138643281873, + "pageIndex": 18, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "4347", + "name": "왕언니네미장원", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 중앙로 77-2 (중앙로2가)", + "phone": "-", + "representativeMenu": "커트", + "price": 8000, + "latitude": 37.8786098574848, + "longitude": 127.72562344249, + "pageIndex": 18, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "13804", + "name": "왕짱구", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 춘천로 195 (효자동) 1층", + "phone": "033-254-4862", + "representativeMenu": "꼬마김밥(8줄)", + "price": 3500, + "latitude": 37.87691958683272, + "longitude": 127.7358144650472, + "pageIndex": 18, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "10311", + "name": "우두동밥집", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 벌말길 54-1 (석사동)", + "phone": "033-255-5999", + "representativeMenu": "한식뷔페", + "price": 8000, + "latitude": 37.859382765949, + "longitude": 127.742351389083, + "pageIndex": 18, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4397", + "name": "우리순대국", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 후석로45번길 15 (석사동)", + "phone": "033-252-9545", + "representativeMenu": "순대국", + "price": 8000, + "latitude": 37.8593067249022, + "longitude": 127.740549073063, + "pageIndex": 18, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4710", + "name": "미소국밥", + "categoryName": "한식", + "address": "충청북도 청주시 상당구 상당로 135 (북문로2가)", + "phone": "043-223-8848", + "representativeMenu": "바지락순두부", + "price": 7000, + "latitude": 36.6403948654709, + "longitude": 127.48970770266, + "pageIndex": 18, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15848", + "name": "미소수제돈까스", + "categoryName": "양식", + "address": "충청북도 청주시 흥덕구 강내면 태성탑연로 441", + "phone": "043-234-5789", + "representativeMenu": "등심돈까스", + "price": 9500, + "latitude": 36.62173390753512, + "longitude": 127.35834796838594, + "pageIndex": 18, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4917", + "name": "소망미용실", + "categoryName": "미용업", + "address": "충청남도 천안시 동남구 구성5길 31 (구성동)", + "phone": "041-556-8816", + "representativeMenu": "커트", + "price": 6000, + "latitude": 36.7972975969191, + "longitude": 127.162734320004, + "pageIndex": 18, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "13652", + "name": "송원떡집", + "categoryName": "한식", + "address": "충청남도 천안시 서북구 봉서5길 10 (쌍용동)", + "phone": "041-578-6753", + "representativeMenu": "인절미 1팩", + "price": 2500, + "latitude": 36.80825811514656, + "longitude": 127.13224341449542, + "pageIndex": 18, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4918", + "name": "스타미용실", + "categoryName": "미용업", + "address": "충청남도 천안시 동남구 대흥로 271 (대흥동)", + "phone": "041-562-0155", + "representativeMenu": "커트(어르신)", + "price": 5000, + "latitude": 36.8123236063302, + "longitude": 127.148852869095, + "pageIndex": 18, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "5176", + "name": "두꺼비탕", + "categoryName": "목욕업", + "address": "전북특별자치도 군산시 동팔마길 31 장재동", + "phone": "063-442-8985", + "representativeMenu": "성인", + "price": 8000, + "latitude": 35.9769871090007, + "longitude": 126.721038103437, + "pageIndex": 18, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "14778", + "name": "두리식당", + "categoryName": "한식", + "address": "전북특별자치도 군산시 미원안길 8 (미원동) 미원동", + "phone": "063-445-3417", + "representativeMenu": "청국장", + "price": 8000, + "latitude": 35.98047012095301, + "longitude": 126.71553829446381, + "pageIndex": 18, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "15533", + "name": "디어왁싱", + "categoryName": "미용업", + "address": "전북특별자치도 군산시 궁포1로 24-3 (조촌동) 1층, 109호", + "phone": "0507-1437-1061", + "representativeMenu": "왁싱(인중)", + "price": 6000, + "latitude": 35.9768524162065, + "longitude": 126.74059066427611, + "pageIndex": 18, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5756", + "name": "대수식당", + "categoryName": "한식", + "address": "전라남도 여수시 성산6길 32 (화장동)", + "phone": "061-681-2292", + "representativeMenu": "백반", + "price": 6000, + "latitude": 34.7724434221449, + "longitude": 127.641942601437, + "pageIndex": 18, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "11801", + "name": "덕충식당", + "categoryName": "한식", + "address": "전라남도 여수시 공화남3길 9 (공화동)", + "phone": "061-664-7838", + "representativeMenu": "백반", + "price": 7000, + "latitude": 34.7468001047787, + "longitude": 127.744235818224, + "pageIndex": 18, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5759", + "name": "도담식당", + "categoryName": "한식", + "address": "전라남도 여수시 충무로 54-9 (충무동)", + "phone": "-", + "representativeMenu": "백반", + "price": 9000, + "latitude": 34.7414865862163, + "longitude": 127.731736351422, + "pageIndex": 18, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "14725", + "name": "또또분식", + "categoryName": "한식", + "address": "경상북도 포항시 남구 장기면 양포항길 21-1 -", + "phone": "054-276-1052", + "representativeMenu": "손칼국수", + "price": 6000, + "latitude": 35.87917402242877, + "longitude": 129.51773492819117, + "pageIndex": 18, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6795", + "name": "빅보이분식", + "categoryName": "한식", + "address": "경상남도 창원시 마산회원구 봉암북4길 58 (봉암동)", + "phone": "055-297-9292", + "representativeMenu": "김치찌개", + "price": 6000, + "latitude": 35.2232485628643, + "longitude": 128.597549978846, + "pageIndex": 18, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "14351", + "name": "사철밀면", + "categoryName": "한식", + "address": "경상남도 창원시 의창구 사림로130번길 5-5 (사림동) 주택 1층", + "phone": "055-287-8307", + "representativeMenu": "물밀면", + "price": 7000, + "latitude": 35.24312887541039, + "longitude": 128.68505183829419, + "pageIndex": 18, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "13735", + "name": "산장국수", + "categoryName": "한식", + "address": "경상남도 창원시 마산합포구 고운로 22 (평화동, 평화아파트) 12호", + "phone": "010-5550-2460", + "representativeMenu": "잔치국수", + "price": 4000, + "latitude": 35.186161057327695, + "longitude": 128.55869543926426, + "pageIndex": 18, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "6816", + "name": "삼구반점", + "categoryName": "중식", + "address": "경상남도 창원시 의창구 대산면 유등로 170-15", + "phone": "055-291-4373", + "representativeMenu": "짬뽕", + "price": 7000, + "latitude": 35.3363515087417, + "longitude": 128.726725367192, + "pageIndex": 18, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "7136", + "name": "마모루", + "categoryName": "일식", + "address": "제주특별자치도 제주시 광양13길 10-1 1층", + "phone": "0507-1404-4303", + "representativeMenu": "돈코츠라멘", + "price": 8500, + "latitude": 33.4988561334146, + "longitude": 126.531499520634, + "pageIndex": 18, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "18751", + "name": "마시쭈", + "categoryName": "한식", + "address": "제주특별자치도 제주시 서사로 97 (삼도일동, 레미안빌) 마시쭈", + "phone": "064-702-3544", + "representativeMenu": "쭈꾸미 덮밥", + "price": 10000, + "latitude": 33.503675132111844, + "longitude": 126.52012010252068, + "pageIndex": 18, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "10623", + "name": "만강촌옛날칼국수", + "categoryName": "한식", + "address": "제주특별자치도 제주시 월랑로 42 (노형동)", + "phone": "064-744-4780", + "representativeMenu": "닭칼국수", + "price": 7900, + "latitude": 33.4893502595565, + "longitude": 126.476040481369, + "pageIndex": 18, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "18573", + "name": "동경우동(2호점)", + "categoryName": "한식", + "address": "서울특별시 중구 퇴계로27길 16 (필동1가) 1층 105호", + "phone": "02-2277-3223", + "representativeMenu": "우동", + "price": 5000, + "latitude": 37.56195219030635, + "longitude": 126.99184490399296, + "pageIndex": 19, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "18572", + "name": "동경우동집", + "categoryName": "한식", + "address": "서울특별시 중구 충무로 48 (초동) 1층 1호", + "phone": "02-2274-3440", + "representativeMenu": "우동", + "price": 5000, + "latitude": 37.56537537416687, + "longitude": 126.99296134367208, + "pageIndex": 19, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "10673", + "name": "명동얼큰수제비", + "categoryName": "한식", + "address": "서울특별시 중구 남대문로 64 1층", + "phone": "02-752-7649", + "representativeMenu": "김치찌개", + "price": 8000, + "latitude": 37.5627658956023, + "longitude": 126.982216802182, + "pageIndex": 19, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1405", + "name": "부산밀면", + "categoryName": "한식", + "address": "부산광역시 서구 대영로 18번길 13 (서대신동2가)", + "phone": "010-4017-4082", + "representativeMenu": "물밀면", + "price": 7000, + "latitude": 35.1094771001795, + "longitude": 129.013487853455, + "pageIndex": 19, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "18715", + "name": "부산식당", + "categoryName": "한식", + "address": "부산광역시 서구 대청로6번길 43 (토성동3가) 부산식당", + "phone": "051-241-5080", + "representativeMenu": "명태탕", + "price": 8000, + "latitude": 35.101316967405765, + "longitude": 129.02048922780577, + "pageIndex": 19, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1861", + "name": "미소띤하루", + "categoryName": "베이커리", + "address": "대구광역시 동구 이노밸리로 168 103호(각산동, 안심빌딩)", + "phone": "053-965-9794", + "representativeMenu": "소보로빵", + "price": 1500, + "latitude": 35.8784451966479, + "longitude": 128.715684288524, + "pageIndex": 19, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "1873", + "name": "미진손칼국수", + "categoryName": "한식", + "address": "대구광역시 동구 아양로50길 119-1 (효목동)", + "phone": "053-941-4664", + "representativeMenu": "손칼국수", + "price": 6000, + "latitude": 35.8824649802755, + "longitude": 128.642824663592, + "pageIndex": 19, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "14952", + "name": "불로회 수산", + "categoryName": "일식", + "address": "대구광역시 동구 팔공로30길 9 (불로동)", + "phone": "053-983-8070", + "representativeMenu": "회덮밥", + "price": 8000, + "latitude": 35.91179442743553, + "longitude": 128.64211388427978, + "pageIndex": 19, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "13032", + "name": "유풍반점", + "categoryName": "중식", + "address": "인천광역시 동구 샛골로194번길 16 (송림동) 1층", + "phone": "032-772-5119", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 37.47926076663097, + "longitude": 126.64636358766377, + "pageIndex": 19, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "17608", + "name": "잘된정", + "categoryName": "중식", + "address": "인천광역시 동구 샛골로171번길 5 (송림동) 1층(송림동)", + "phone": "032-761-0038", + "representativeMenu": "짜장면", + "price": 5000, + "latitude": 37.477691922823226, + "longitude": 126.64454299959506, + "pageIndex": 19, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2105", + "name": "청해물텀벙", + "categoryName": "한식", + "address": "인천광역시 동구 수문통로 5-1 (화평동) 1층", + "phone": "032-761-4140", + "representativeMenu": "점심아구뚝배기", + "price": 10000, + "latitude": 37.477346435473336, + "longitude": 126.63159719785347, + "pageIndex": 19, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "17602", + "name": "친친", + "categoryName": "중식", + "address": "인천 동구 샛골로210번길 9 1층 107호(송림동)", + "phone": "032-766-5533", + "representativeMenu": "중화제육덮밥", + "price": 10000, + "latitude": 37.4809530287519, + "longitude": 126.646384617566, + "pageIndex": 19, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "13665", + "name": "별천지", + "categoryName": "한식", + "address": "광주광역시 서구 상무대로695번길 8 (마륵동) 1층", + "phone": "062-375-2795", + "representativeMenu": "생선구이백반", + "price": 9500, + "latitude": 35.143450019964995, + "longitude": 126.84034418110387, + "pageIndex": 19, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2574", + "name": "한우농장30년", + "categoryName": "한식", + "address": "대전광역시 동구 동부로 13 (판암동)", + "phone": "042-274-9393", + "representativeMenu": "냉면", + "price": 7000, + "latitude": 36.3182694180623, + "longitude": 127.456048288873, + "pageIndex": 19, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2550", + "name": "행복칼국수", + "categoryName": "한식", + "address": "대전광역시 동구 대전로 815번길 15 (정동)", + "phone": "042-222-9632", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 36.3326785842423, + "longitude": 127.43143840488, + "pageIndex": 19, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2538", + "name": "헤어코리아미용실", + "categoryName": "미용업", + "address": "대전광역시 동구 한남로 7번길 87 (홍도동)", + "phone": "0507-1376-0694", + "representativeMenu": "미용료(커트)", + "price": 9000, + "latitude": 36.3477117403251, + "longitude": 127.429378780645, + "pageIndex": 19, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "13383", + "name": "헤어코리아착한나눔", + "categoryName": "미용업", + "address": "대전광역시 동구 대전로797번길 38 (중동) 중동 27-33", + "phone": "042-274-2221", + "representativeMenu": "미용료(커트)", + "price": 6000, + "latitude": 36.33006491325828, + "longitude": 127.43082686518471, + "pageIndex": 19, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "17496", + "name": "샤브리브", + "categoryName": "일식", + "address": "울산광역시 남구 대공원입구로21번길 27 (옥동) 1층", + "phone": "0507-1377-2688", + "representativeMenu": "얼큰육수샤브", + "price": 9000, + "latitude": 35.5341764411374, + "longitude": 129.29127740367105, + "pageIndex": 19, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2905", + "name": "서울식당", + "categoryName": "한식", + "address": "울산광역시 남구 중앙로258번길 7", + "phone": "052-271-5103", + "representativeMenu": "김치,된장,순두부찌개", + "price": 7000, + "latitude": 35.5440882982423, + "longitude": 129.311623070929, + "pageIndex": 19, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "18787", + "name": "선경기사식당", + "categoryName": "한식", + "address": "울산광역시 남구 삼산로402번길 31 (삼산동) 1층", + "phone": "052-267-8487", + "representativeMenu": "순두부", + "price": 9000, + "latitude": 35.53656890072942, + "longitude": 129.35102283113696, + "pageIndex": 19, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "14532", + "name": "샐러디치오도씨 수원성대점", + "categoryName": "양식", + "address": "경기도 수원시 장안구 서부로2106번길 21 (율전동) 1층 4호", + "phone": "031-205-5636", + "representativeMenu": "햄치즈샌드위치", + "price": 6200, + "latitude": 37.29725954060872, + "longitude": 126.97136033632337, + "pageIndex": 19, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "17115", + "name": "서서김밥", + "categoryName": "한식", + "address": "경기도 수원시 영통구 매영로 33 (매탄동) 1층", + "phone": "031-214-6330", + "representativeMenu": "야채김밥", + "price": 3000, + "latitude": 37.26960250984764, + "longitude": 127.04648154679711, + "pageIndex": 19, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "16798", + "name": "서울한우 홈플러스 북수원점", + "categoryName": "한식", + "address": "경기도 수원시 장안구 경수대로 930 (조원동) 1층 X3YC호", + "phone": "031-241-9680", + "representativeMenu": "얼큰한우국밥", + "price": 9900, + "latitude": 37.30276736285418, + "longitude": 127.00880909013584, + "pageIndex": 19, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "3368", + "name": "서현대미용실", + "categoryName": "미용업", + "address": "경기도 수원시 권선구 정조로576번길 3 (세류동)", + "phone": "031-232-6686", + "representativeMenu": "커트", + "price": 10000, + "latitude": 37.2604055446764, + "longitude": 127.013461026266, + "pageIndex": 19, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "13805", + "name": "원식이네손칼국수", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 후석로 591 (소양로1가) 원식이네손칼국수", + "phone": "033-257-5652", + "representativeMenu": "해물칼국수", + "price": 8000, + "latitude": 37.89357805500181, + "longitude": 127.7279325507315, + "pageIndex": 19, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "16647", + "name": "이레미용실", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 효석로67번길 18-1 (석사동)", + "phone": "-", + "representativeMenu": "커트", + "price": 8000, + "latitude": 37.86191645758754, + "longitude": 127.73930830005943, + "pageIndex": 19, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "15518", + "name": "민들레식당", + "categoryName": "한식", + "address": "충청북도 청주시 상당구 대성로 57 (서운동) (민들레식당)", + "phone": "043-255-3028", + "representativeMenu": "된장찌개(백반)", + "price": 7000, + "latitude": 36.63047211931871, + "longitude": 127.49290793289809, + "pageIndex": 19, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "4733", + "name": "박현순헤어", + "categoryName": "미용업", + "address": "충청북도 청주시 청원구 율봉로159번길 48-20 1층(율량동)", + "phone": "043-211-7623", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.6705031580318, + "longitude": 127.486655068416, + "pageIndex": 19, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "14743", + "name": "백합미용실", + "categoryName": "미용업", + "address": "충청북도 청주시 상당구 당산로 10-1 (대성동)", + "phone": "043-224-8890", + "representativeMenu": "커트(남성)", + "price": 7000, + "latitude": 36.6330216282411, + "longitude": 127.4969961822045, + "pageIndex": 19, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15577", + "name": "스탠바이커피", + "categoryName": "기타요식업", + "address": "충청남도 천안시 서북구 불당21로 67-18 (불당동) 114호", + "phone": "0507-1384-6480", + "representativeMenu": "아메리카노", + "price": 2000, + "latitude": 36.81404962232316, + "longitude": 127.10850447130197, + "pageIndex": 19, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "18725", + "name": "시골손칼국수", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 사직로 28-1 (사직동) 시골손칼국수", + "phone": "041-556-6181", + "representativeMenu": "칼국수", + "price": 5000, + "latitude": 36.802628752680896, + "longitude": 127.14939807826286, + "pageIndex": 19, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "4954", + "name": "쌍용동사진관", + "categoryName": "기타비요식업", + "address": "충청남도 천안시 서북구 미라3길 27 (쌍용동)", + "phone": "041-571-9182", + "representativeMenu": "반명함", + "price": 10000, + "latitude": 36.8021171826636, + "longitude": 127.130754046358, + "pageIndex": 19, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "5177", + "name": "머리박사", + "categoryName": "미용업", + "address": "전북특별자치도 군산시 문화로36 203호", + "phone": "063-462-9680", + "representativeMenu": "남성커트", + "price": 9000, + "latitude": 35.9694641851937, + "longitude": 126.709955551767, + "pageIndex": 19, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5191", + "name": "명산칼국수", + "categoryName": "한식", + "address": "전북특별자치도 군산시 오룡로 57-2 명산동", + "phone": "063-468-1470", + "representativeMenu": "바지락칼국수", + "price": 6000, + "latitude": 35.9825246812404, + "longitude": 126.709954003925, + "pageIndex": 19, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5707", + "name": "돈까스천국", + "categoryName": "양식", + "address": "전라남도 여수시 통제영3길 14 (교동)", + "phone": "061-662-0239", + "representativeMenu": "돈까스", + "price": 10000, + "latitude": 34.7413896058147, + "longitude": 127.734325663512, + "pageIndex": 19, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5708", + "name": "돈뗄", + "categoryName": "양식", + "address": "전라남도 여수시 양지1길 30 1층(미평동)", + "phone": "061-652-2760", + "representativeMenu": "수제돈가스", + "price": 9000, + "latitude": 34.7727185677749, + "longitude": 127.702281894777, + "pageIndex": 19, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5760", + "name": "돈사랑", + "categoryName": "한식", + "address": "전라남도 여수시 중앙로 93-2 (중앙동)", + "phone": "061-663-0866", + "representativeMenu": "백반", + "price": 10000, + "latitude": 34.7393455980908, + "longitude": 127.737831078851, + "pageIndex": 19, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6439", + "name": "롯데이용소", + "categoryName": "이용업", + "address": "경상북도 포항시 북구 두호로 19-11", + "phone": "054-247-8775", + "representativeMenu": "남자커트", + "price": 8000, + "latitude": 36.0594247293204, + "longitude": 129.376181188119, + "pageIndex": 19, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6311", + "name": "루머팡", + "categoryName": "기타요식업", + "address": "경상북도 포항시 남구 오천읍 장기로1690번길 5 다동 5호", + "phone": "-", + "representativeMenu": "아메리카노", + "price": 3000, + "latitude": 35.9660455313984, + "longitude": 129.417540251313, + "pageIndex": 19, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6803", + "name": "삼포복집", + "categoryName": "한식", + "address": "경상남도 창원시 성산구 용지로 78 2층 5호(중앙동,남선상가)", + "phone": "055-281-3535", + "representativeMenu": "지리", + "price": 9000, + "latitude": 35.2227526441192, + "longitude": 128.676906326096, + "pageIndex": 19, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "10582", + "name": "새마을찌개나라", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 중원로85번길 13 103호(화천동, 평화빌라)", + "phone": "055-545-1134", + "representativeMenu": "불고기정식", + "price": 10000, + "latitude": 35.1518273238499, + "longitude": 128.658677263667, + "pageIndex": 19, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "18613", + "name": "새맛정 한식뷔페", + "categoryName": "한식", + "address": "경상남도 창원시 마산회원구 봉암공단로 26 (봉암동) 1층", + "phone": "0507-1459-0857", + "representativeMenu": "한식뷔페", + "price": 6500, + "latitude": 35.21671451783592, + "longitude": 128.60004187399605, + "pageIndex": 19, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "16569", + "name": "서민갈비", + "categoryName": "한식", + "address": "경상남도 창원시 마산합포구 산호북18길 21 (산호동) 서민갈비", + "phone": "055-243-7171", + "representativeMenu": "돼지갈비(200g)", + "price": 10000, + "latitude": 35.220823467615624, + "longitude": 128.58465602110726, + "pageIndex": 19, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "14242", + "name": "만나빵집", + "categoryName": "베이커리", + "address": "제주특별자치도 제주시 구좌읍 종달로5길 5-1 1층", + "phone": "010-6701-1885", + "representativeMenu": "반미바게트", + "price": 2500, + "latitude": 33.49324369426974, + "longitude": 126.89703211799348, + "pageIndex": 19, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7139", + "name": "맛소", + "categoryName": "한식", + "address": "제주특별자치도 제주시 원노형5길 5 1층", + "phone": "064-742-0455", + "representativeMenu": "콩국수", + "price": 9000, + "latitude": 33.4882010292329, + "longitude": 126.484560727674, + "pageIndex": 19, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "11266", + "name": "맠(makk)", + "categoryName": "기타요식업", + "address": "제주특별자치도 제주시 진남로 6길 3 (주소변경)", + "phone": "-", + "representativeMenu": "마카롱", + "price": 2000, + "latitude": 33.5192267759866, + "longitude": 126.565536590436, + "pageIndex": 19, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "7140", + "name": "매콤명태촌", + "categoryName": "한식", + "address": "제주특별자치도 제주시 절물3길 7 1층", + "phone": "064-743-6868", + "representativeMenu": "황태해장국", + "price": 8000, + "latitude": 33.4918593353103, + "longitude": 126.433805337832, + "pageIndex": 19, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "983", + "name": "민정이네", + "categoryName": "한식", + "address": "서울특별시 중구 만리재로 37길 21 (만리동1가)", + "phone": "02-312-1420", + "representativeMenu": "점심백반", + "price": 7000, + "latitude": 37.5565915616197, + "longitude": 126.967168586088, + "pageIndex": 20, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "16086", + "name": "바재커피", + "categoryName": "기타요식업", + "address": "서울특별시 중구 남대문시장8길 25 (남창동) 1층", + "phone": "02-776-5577", + "representativeMenu": "아메리카노(핫)", + "price": 2000, + "latitude": 37.55934272418682, + "longitude": 126.97870933285441, + "pageIndex": 20, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "15377", + "name": "산과들그리고바다", + "categoryName": "한식", + "address": "서울특별시 중구 을지로14길 22 (을지로3가) 1층", + "phone": "02-2277-7887", + "representativeMenu": "모듬돌솥비빔밥", + "price": 10000, + "latitude": 37.56523732282778, + "longitude": 126.99202506646904, + "pageIndex": 20, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1402", + "name": "부원", + "categoryName": "중식", + "address": "부산광역시 서구 구덕로157번길 6 (토성동3가)", + "phone": "051-256-0061", + "representativeMenu": "짜장면", + "price": 4000, + "latitude": 35.0987972600376, + "longitude": 129.01970425062, + "pageIndex": 20, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "18716", + "name": "북경반점", + "categoryName": "중식", + "address": "부산광역시 서구 충무대로 64 (암남동, 송도삼정비치) 북경반점", + "phone": "051-244-8454", + "representativeMenu": "간짜장", + "price": 7000, + "latitude": 35.07836072507977, + "longitude": 129.01969618711593, + "pageIndex": 20, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1429", + "name": "뷔페청", + "categoryName": "한식", + "address": "부산광역시 서구 흑교로109번길 43 (부용동1가)", + "phone": "051-242-4311", + "representativeMenu": "성인", + "price": 8000, + "latitude": 35.1056136879459, + "longitude": 129.02082234077, + "pageIndex": 20, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1394", + "name": "사랑방미용실", + "categoryName": "미용업", + "address": "부산광역시 서구 대티로 131 (서대신동3가)", + "phone": "051-242-8977", + "representativeMenu": "커트", + "price": 5000, + "latitude": 35.1109554946482, + "longitude": 129.009654362478, + "pageIndex": 20, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "17766", + "name": "삼보식당", + "categoryName": "한식", + "address": "대구광역시 동구 큰고개로 33 (신암동)", + "phone": "010-3530-0133", + "representativeMenu": "한식뷔페", + "price": 7000, + "latitude": 35.88761259935702, + "longitude": 128.6247276701282, + "pageIndex": 20, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "1862", + "name": "삼일라사세탁소", + "categoryName": "세탁업", + "address": "대구광역시 동구 효목로5길 7 (효목동)", + "phone": "053-755-1276", + "representativeMenu": "티셔츠드라이", + "price": 4000, + "latitude": 35.8750480007903, + "longitude": 128.63888107243, + "pageIndex": 20, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17703", + "name": "생선엔밥", + "categoryName": "한식", + "address": "대구광역시 동구 경대로 48 (신암동)", + "phone": "053-262-4197", + "representativeMenu": "생선구이정식", + "price": 8000, + "latitude": 35.88509005641053, + "longitude": 128.6149836033377, + "pageIndex": 20, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17604", + "name": "카페립", + "categoryName": "기타요식업", + "address": "인천 동구 박문로 1 교구청, 청소년센터3층(송림동)", + "phone": "070-4443-1726", + "representativeMenu": "휘낭시에 플레인", + "price": 2000, + "latitude": 37.4706891169897, + "longitude": 126.651839572554, + "pageIndex": 20, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "14301", + "name": "하루도넛", + "categoryName": "기타요식업", + "address": "인천광역시 동구 송림로 106-1 (송림동) 1,2층", + "phone": "032-777-1648", + "representativeMenu": "꽈배기", + "price": 800, + "latitude": 37.47620674347315, + "longitude": 126.64651702408199, + "pageIndex": 20, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2106", + "name": "한울타리", + "categoryName": "한식", + "address": "인천광역시 동구 석수로 47-1 (만석동)", + "phone": "032-221-8887", + "representativeMenu": "청국장", + "price": 8000, + "latitude": 37.4812201279796, + "longitude": 126.626544029841, + "pageIndex": 20, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "2107", + "name": "해주식당", + "categoryName": "한식", + "address": "인천광역시 동구 화도진로44번길 3 (송현동) 1층 송현시장 내", + "phone": "032-888-4009", + "representativeMenu": "백반", + "price": 8000, + "latitude": 37.47650336140443, + "longitude": 126.6339042004762, + "pageIndex": 20, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "13670", + "name": "상무본가장어", + "categoryName": "한식", + "address": "광주광역시 서구 시청서편로4번길 16-1 (치평동) 1층", + "phone": "062-372-9293", + "representativeMenu": "장어탕", + "price": 10000, + "latitude": 35.15909682794521, + "longitude": 126.84738486591091, + "pageIndex": 20, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "18043", + "name": "서울족발보쌈", + "categoryName": "한식", + "address": "광주광역시 서구 상무화원로32번길 13-18 (치평동) 1층", + "phone": "062-373-7211", + "representativeMenu": "족발정식", + "price": 10000, + "latitude": 35.14598060035197, + "longitude": 126.84439065995004, + "pageIndex": 20, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2285", + "name": "세븐노래연습장", + "categoryName": "기타비요식업", + "address": "광주광역시 서구 시청로56번길 8 지하1층(치평동)", + "phone": "062-372-8331", + "representativeMenu": "30분", + "price": 4000, + "latitude": 35.1532792040005, + "longitude": 126.852731376864, + "pageIndex": 20, + "regionCode": "29", + "regionName": "광주광역시" + }, + { + "bsshSn": "2551", + "name": "홍가네칼국수", + "categoryName": "한식", + "address": "대전광역시 동구 대전로 807번길 40 (중동)", + "phone": "042-254-5945", + "representativeMenu": "칼국수", + "price": 7000, + "latitude": 36.3309478641562, + "longitude": 127.430241597664, + "pageIndex": 20, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2543", + "name": "홍가짜장", + "categoryName": "중식", + "address": "대전광역시 동구 동대전로 278 (가양동)", + "phone": "042-365-3323", + "representativeMenu": "홍가짜장", + "price": 4000, + "latitude": 36.3459230781175, + "longitude": 127.445797225616, + "pageIndex": 20, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2558", + "name": "희원숯불갈비식당", + "categoryName": "한식", + "address": "대전광역시 동구 가양로 56번길 19 (가양동)", + "phone": "042-627-5329", + "representativeMenu": "돼지갈비(250g)", + "price": 10000, + "latitude": 36.3418094483622, + "longitude": 127.443094601499, + "pageIndex": 20, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2857", + "name": "0시50분", + "categoryName": "한식", + "address": "대전광역시 중구 대종로 504 1층(은행동)", + "phone": "042-524-5816", + "representativeMenu": "우거지해장국", + "price": 7000, + "latitude": 36.3293358176833, + "longitude": 127.425489899251, + "pageIndex": 20, + "regionCode": "30", + "regionName": "대전광역시" + }, + { + "bsshSn": "2906", + "name": "소문난 고추장불고기 국밥", + "categoryName": "한식", + "address": "울산광역시 남구 월평로37번길 3", + "phone": "052-273-6661", + "representativeMenu": "돼지,섞어,순대,선지국밥", + "price": 7000, + "latitude": 35.5416195196528, + "longitude": 129.309458628481, + "pageIndex": 20, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "2908", + "name": "솟대", + "categoryName": "한식", + "address": "울산광역시 남구 대학로47번길 10 1층", + "phone": "052-223-4346", + "representativeMenu": "순두부찌개", + "price": 9000, + "latitude": 35.5407837447282, + "longitude": 129.255862186774, + "pageIndex": 20, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "17504", + "name": "수지미용실", + "categoryName": "미용업", + "address": "울산광역시 남구 번영로107번길 17 (달동) 205호", + "phone": "052-269-3868", + "representativeMenu": "커트", + "price": 10000, + "latitude": 35.53427924703676, + "longitude": 129.32685098572432, + "pageIndex": 20, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "18785", + "name": "순복식당", + "categoryName": "한식", + "address": "울산광역시 남구 월평로85번길 20 (신정동) 1층", + "phone": "052-266-9347", + "representativeMenu": "김치찌개", + "price": 8000, + "latitude": 35.54457488092627, + "longitude": 129.3142706641577, + "pageIndex": 20, + "regionCode": "31", + "regionName": "울산광역시" + }, + { + "bsshSn": "3399", + "name": "성일사우나", + "categoryName": "목욕업", + "address": "경기도 수원시 장안구 경수대로973번길 13-20 (송죽동)", + "phone": "031-246-6489", + "representativeMenu": "대인", + "price": 9000, + "latitude": 37.3031115091404, + "longitude": 127.005025091289, + "pageIndex": 20, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "16205", + "name": "세븐힐여성사우나", + "categoryName": "목욕업", + "address": "경기도 수원시 팔달구 중부대로 47 (지동) 지하1층", + "phone": "031-244-0071", + "representativeMenu": "일반", + "price": 9000, + "latitude": 37.27569315113086, + "longitude": 127.0220900432593, + "pageIndex": 20, + "regionCode": "41", + "regionName": "경기도" + }, + { + "bsshSn": "13806", + "name": "이미경헤어클릭", + "categoryName": "미용업", + "address": "강원특별자치도 춘천시 충열로 10-5 (우두동) 이미경헤어클릭", + "phone": "033-253-0028", + "representativeMenu": "커트", + "price": 10000, + "latitude": 37.900210305145734, + "longitude": 127.72949847551419, + "pageIndex": 20, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "13807", + "name": "이서냉면", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 우석로 14-1 (석사동) 1층", + "phone": "0507-1487-4040", + "representativeMenu": "냉면", + "price": 7000, + "latitude": 37.86193728705492, + "longitude": 127.74766638895937, + "pageIndex": 20, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "15475", + "name": "일리터", + "categoryName": "기타요식업", + "address": "강원특별자치도 춘천시 중앙로68번길 3 (중앙로2가) 1, 2층(중앙로2가)", + "phone": "033-241-7778", + "representativeMenu": "아메리카노", + "price": 1000, + "latitude": 37.87936544046061, + "longitude": 127.72610245830614, + "pageIndex": 20, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "10309", + "name": "임가네", + "categoryName": "한식", + "address": "강원특별자치도 춘천시 새롬공원길 25 (석사동)", + "phone": "033-262-5245", + "representativeMenu": "소고기뭇국", + "price": 8000, + "latitude": 37.8544088335552, + "longitude": 127.747076544582, + "pageIndex": 20, + "regionCode": "51", + "regionName": "강원특별자치도" + }, + { + "bsshSn": "4712", + "name": "보글보글식당", + "categoryName": "한식", + "address": "충청북도 청주시 상당구 영운로11번길 23 (영운동)", + "phone": "043-250-0012", + "representativeMenu": "가정식백반", + "price": 6000, + "latitude": 36.6166130893195, + "longitude": 127.497059927318, + "pageIndex": 20, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15838", + "name": "보라매 미용타운", + "categoryName": "미용업", + "address": "충청북도 청주시 청원구 1순환로342번길 16-1 (내덕동)", + "phone": "043-223-3044", + "representativeMenu": "커트", + "price": 10000, + "latitude": 36.65914920998362, + "longitude": 127.47618218928218, + "pageIndex": 20, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "15772", + "name": "복실이 식당", + "categoryName": "한식", + "address": "충청북도 청주시 서원구 매봉로2번길 40 (분평동) 1층", + "phone": "043-294-3667", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 36.61043494588689, + "longitude": 127.47990007377227, + "pageIndex": 20, + "regionCode": "43", + "regionName": "충청북도" + }, + { + "bsshSn": "16876", + "name": "안골식당", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 먹거리2길 6-1 (신부동) 2층", + "phone": "0507-1348-2664", + "representativeMenu": "만두전골", + "price": 10000, + "latitude": 36.81768377259163, + "longitude": 127.15402963313205, + "pageIndex": 20, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "17020", + "name": "엄마밥상 한식뷔페", + "categoryName": "한식", + "address": "충청남도 천안시 서북구 오성9길 5 (두정동) 1층", + "phone": "041-554-8335", + "representativeMenu": "한식뷔페", + "price": 9000, + "latitude": 36.833566400988474, + "longitude": 127.12808573112298, + "pageIndex": 20, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "11410", + "name": "옛김포식당", + "categoryName": "한식", + "address": "충청남도 천안시 동남구 영성로 25-19 (사직동)", + "phone": "041-554-3029", + "representativeMenu": "칼국수", + "price": 6000, + "latitude": 36.8004524284923, + "longitude": 127.150097319368, + "pageIndex": 20, + "regionCode": "44", + "regionName": "충청남도" + }, + { + "bsshSn": "17041", + "name": "모정식당", + "categoryName": "한식", + "address": "전북특별자치도 군산시 조촌4길 34-4 (조촌동) 1층", + "phone": "063-461-0102", + "representativeMenu": "김치찌개", + "price": 9000, + "latitude": 35.96943678402732, + "longitude": 126.73620476668584, + "pageIndex": 20, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "15532", + "name": "묵은지삼겹살", + "categoryName": "한식", + "address": "전북특별자치도 군산시 경암로 56 (경암동) 묵은지삼겹살", + "phone": "063-442-3055", + "representativeMenu": "된장찌개", + "price": 7000, + "latitude": 35.97555034978496, + "longitude": 126.73025962030721, + "pageIndex": 20, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5192", + "name": "미주옥", + "categoryName": "한식", + "address": "전북특별자치도 군산시 구영7길 133-4 영화동", + "phone": "063-442-5505", + "representativeMenu": "아욱국", + "price": 8000, + "latitude": 35.9893754831364, + "longitude": 126.712272232213, + "pageIndex": 20, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "15530", + "name": "베테랑돈까스", + "categoryName": "양식", + "address": "전북특별자치도 군산시 양안로 103-1 (조촌동) 조촌동", + "phone": "063-452-5582", + "representativeMenu": "베테랑 생돈까스", + "price": 6000, + "latitude": 35.971740241807225, + "longitude": 126.74345325965865, + "pageIndex": 20, + "regionCode": "52", + "regionName": "전북특별자치도" + }, + { + "bsshSn": "5705", + "name": "동광탕", + "categoryName": "목욕업", + "address": "전라남도 여수시 동문로 101 2층(공화동)", + "phone": "061-662-4272", + "representativeMenu": "목욕비", + "price": 8000, + "latitude": 34.7456605198534, + "longitude": 127.742352326201, + "pageIndex": 20, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "5762", + "name": "두레", + "categoryName": "한식", + "address": "전라남도 여수시 학동1길 12-7 (학동) 1층", + "phone": "061-684-1808", + "representativeMenu": "백반", + "price": 7000, + "latitude": 34.76449978425705, + "longitude": 127.66474470490957, + "pageIndex": 20, + "regionCode": "46", + "regionName": "전라남도" + }, + { + "bsshSn": "6412", + "name": "리클라스", + "categoryName": "기타요식업", + "address": "경상북도 포항시 북구 대신로7번길 7 리클라스", + "phone": "054-248-3742", + "representativeMenu": "카페라떼", + "price": 4800, + "latitude": 36.0448115361729, + "longitude": 129.367277819827, + "pageIndex": 20, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "6312", + "name": "마실카페", + "categoryName": "기타요식업", + "address": "경상북도 포항시 남구 동해면 일월로81번길 9 마실카페", + "phone": "-", + "representativeMenu": "아메리카노", + "price": 3000, + "latitude": 35.989400103331, + "longitude": 129.44204186305, + "pageIndex": 20, + "regionCode": "47", + "regionName": "경상북도" + }, + { + "bsshSn": "14603", + "name": "선백면옥", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 자은로28번길 8 (자은동) 선백면옥", + "phone": "055-542-9969", + "representativeMenu": "물밀면", + "price": 7000, + "latitude": 35.14906570208649, + "longitude": 128.7052270170308, + "pageIndex": 20, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "11516", + "name": "성안식당", + "categoryName": "한식", + "address": "경상남도 창원시 마산회원구 합성서5길 18 (합성동)", + "phone": "055-256-6696", + "representativeMenu": "갈비탕", + "price": 8000, + "latitude": 35.2412291831889, + "longitude": 128.583488769265, + "pageIndex": 20, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "15415", + "name": "소금깎이 창원진해본점", + "categoryName": "한식", + "address": "경상남도 창원시 진해구 동진로61번길 6 (석동) 소금깎이 창원진해본점", + "phone": "055-547-0062", + "representativeMenu": "토마호크(100g)", + "price": 9000, + "latitude": 35.15181122430042, + "longitude": 128.70271441863437, + "pageIndex": 20, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "18615", + "name": "소문난김밥", + "categoryName": "한식", + "address": "경상남도 창원시 마산회원구 내서읍 중리상곡로 129 소문난김밥", + "phone": "055-231-4893", + "representativeMenu": "소문난김밥", + "price": 3000, + "latitude": 35.25181139457977, + "longitude": 128.51144426255615, + "pageIndex": 20, + "regionCode": "48", + "regionName": "경상남도" + }, + { + "bsshSn": "11154", + "name": "머리하기좋은날", + "categoryName": "미용업", + "address": "제주특별자치도 제주시 신대로6길 11 ()", + "phone": "064-713-6667", + "representativeMenu": "커트(샴푸 미포함)", + "price": 10000, + "latitude": 33.4922364704893, + "longitude": 126.495273237818, + "pageIndex": 20, + "regionCode": "50", + "regionName": "제주특별자치도" + }, + { + "bsshSn": "18570", + "name": "신라미용실", + "categoryName": "미용업", + "address": "서울특별시 중구 퇴계로 217 (충무로4가, 진양상가, 진양아파트) 1층 36호, 37호", + "phone": "010-9264-3072", + "representativeMenu": "남성컷", + "price": 10000, + "latitude": 37.562423982135904, + "longitude": 126.99593553115459, + "pageIndex": 21, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "967", + "name": "신빙고", + "categoryName": "기타요식업", + "address": "서울특별시 중구 동호로12길 93 1층(신당동)", + "phone": "02-223-8819", + "representativeMenu": "아메리카노", + "price": 2300, + "latitude": 37.559329134852, + "longitude": 127.012071243923, + "pageIndex": 21, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "984", + "name": "영자씨우동김밥", + "categoryName": "한식", + "address": "서울특별시 중구 마른내로 15-1 (저동2가)", + "phone": "02-227-9791", + "representativeMenu": "김치찌개", + "price": 7000, + "latitude": 37.5649541262687, + "longitude": 126.989606762221, + "pageIndex": 21, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "969", + "name": "옛날홍두깨손칼국수", + "categoryName": "한식", + "address": "서울특별시 중구 동호로7길 32 1층", + "phone": "02-223-1458", + "representativeMenu": "손수제비", + "price": 6000, + "latitude": 37.5525136777809, + "longitude": 127.010624637953, + "pageIndex": 21, + "regionCode": "11", + "regionName": "서울특별시" + }, + { + "bsshSn": "1430", + "name": "산바다", + "categoryName": "한식", + "address": "부산광역시 서구 엄광산로 6 (서대신동3가)", + "phone": "051-241-2976", + "representativeMenu": "메기매운탕", + "price": 8000, + "latitude": 35.1277404417829, + "longitude": 129.007513285213, + "pageIndex": 21, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1431", + "name": "삼원두루치기", + "categoryName": "한식", + "address": "부산광역시 서구 구덕로193번길 12-17 (부민동2가)", + "phone": "051-231-5452", + "representativeMenu": "두루치기", + "price": 8000, + "latitude": 35.1024534581686, + "longitude": 129.019112839039, + "pageIndex": 21, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1406", + "name": "성일칼국수", + "categoryName": "한식", + "address": "부산광역시 서구 해안새벽시장길 8 (충무동1가)", + "phone": "051-244-1547", + "representativeMenu": "칼국수", + "price": 5000, + "latitude": 35.0958892269302, + "longitude": 129.025392096138, + "pageIndex": 21, + "regionCode": "26", + "regionName": "부산광역시" + }, + { + "bsshSn": "1874", + "name": "시장냉면", + "categoryName": "한식", + "address": "대구광역시 동구 팔공로30길 10-3 (불로동)", + "phone": "053-981-4044", + "representativeMenu": "얼큰냉면", + "price": 6000, + "latitude": 35.9113533839705, + "longitude": 128.642338260752, + "pageIndex": 21, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "1885", + "name": "신산홍", + "categoryName": "한식", + "address": "대구광역시 동구 아양로7길 12 가상가동 지하1층 108호(신암동, 신암뜨란채)", + "phone": "053-944-5650", + "representativeMenu": "양념닭목살(130g)", + "price": 9000, + "latitude": 35.8823321773353, + "longitude": 128.61768521709, + "pageIndex": 21, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "17768", + "name": "엄마솥밥상", + "categoryName": "한식", + "address": "대구광역시 동구 동대구로 423 (신천동)", + "phone": "053-753-2030", + "representativeMenu": "된장정식", + "price": 8000, + "latitude": 35.86819851346647, + "longitude": 128.6259527502153, + "pageIndex": 21, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "1868", + "name": "영남루반점", + "categoryName": "중식", + "address": "대구광역시 동구 해동로 18 (지저동)", + "phone": "053-981-9881", + "representativeMenu": "자장면", + "price": 5000, + "latitude": 35.896627045467, + "longitude": 128.637690520629, + "pageIndex": 21, + "regionCode": "27", + "regionName": "대구광역시" + }, + { + "bsshSn": "2108", + "name": "화도진순대국", + "categoryName": "한식", + "address": "인천 동구 화도진로 101 1층(화평동)", + "phone": "032-777-1289", + "representativeMenu": "순대국", + "price": 9000, + "latitude": 37.4801807743095, + "longitude": 126.628989131469, + "pageIndex": 21, + "regionCode": "28", + "regionName": "인천광역시" + }, + { + "bsshSn": "17895", + "name": "가족이발관", + "categoryName": "미용업", + "address": "인천광역시 미추홀구 석정로 372 (주안동) 1층", + "phone": "-", + "representativeMenu": "염색", + "price": 7000, + "latitude": 37.467045636101226, + "longitude": 126.67678399359703, + "pageIndex": 21, + "regionCode": "28", + "regionName": "인천광역시" + } + ] +} diff --git a/docs/cloudflare-account-to-deploy.md b/docs/cloudflare-account-to-deploy.md index 1b5f71d..3da5aa9 100644 --- a/docs/cloudflare-account-to-deploy.md +++ b/docs/cloudflare-account-to-deploy.md @@ -15,6 +15,7 @@ - 배포 명령은 이미 준비되어 있다. - `npm run preview` - `npm run deploy` + - 필요 시 `npm run deploy:public`, `npm run deploy:admin` - 현재 앱은 Cloudflare D1이 아니라 외부 PostgreSQL 연결 문자열 `DATABASE_URL`을 사용한다. - 현재 앱은 Hyperdrive binding을 쓰지 않는다. - 첫 배포는 `DATABASE_URL` 직접 연결로 진행하고, Hyperdrive는 나중에 별도 작업으로 붙이는 편이 안전하다. @@ -136,6 +137,7 @@ npx wrangler login DATABASE_URL= AUTH_SECRET= NEXTAUTH_URL= +ADMIN_APP_URL= NEXT_PUBLIC_NAVER_MAP_KEY_ID= AUTH_KAKAO_CLIENT_ID= AUTH_KAKAO_CLIENT_SECRET= @@ -165,6 +167,7 @@ RESEND_API_KEY= - `AUTH_KAKAO_CLIENT_ID` - `AUTH_NAVER_CLIENT_ID` - `NEXTAUTH_URL` + - `ADMIN_APP_URL` ## 7. `NEXTAUTH_URL`을 어떻게 잡을지 @@ -235,6 +238,17 @@ npm run deploy 를 순서대로 수행한다. +관리자 앱을 public 앱과 분리해서 운영할 계획이면 아래 순서를 쓴다. + +```bash +npm run deploy:admin +# altteulmap-admin workers.dev 주소 확인 +# ADMIN_APP_URL에 위 주소 반영 +npm run deploy:public +``` + +이때 `deploy:public`은 `ADMIN_APP_URL`이 비어 있으면 실패한다. public 번들에서 `/admin`, `/api/admin`을 제거하는 대신 관리자 링크를 외부 관리자 앱으로 보내기 때문이다. + 공식 문서: - [Cloudflare Next.js guide](https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/) - [OpenNext get started](https://opennext.js.org/cloudflare/get-started) diff --git a/docs/deploy-cloudflare.md b/docs/deploy-cloudflare.md index 9034234..e9b52fc 100644 --- a/docs/deploy-cloudflare.md +++ b/docs/deploy-cloudflare.md @@ -20,6 +20,7 @@ - `DATABASE_URL` - `AUTH_SECRET` - `NEXTAUTH_URL` +- `ADMIN_APP_URL` (`deploy:public` 또는 외부 관리자 앱 분리 시) - `NEXT_PUBLIC_NAVER_MAP_KEY_ID` - `AUTH_KAKAO_CLIENT_ID` - `AUTH_KAKAO_CLIENT_SECRET` @@ -56,7 +57,24 @@ npm run smoke:local npm run deploy:check ``` -배포는 `npm run deploy`를 사용한다. 이 명령은 `.next`, `.open-next`를 먼저 비우고 다시 OpenNext build를 만든 뒤 업로드한다. +배포는 목적에 따라 아래를 사용한다. + +```bash +npm run deploy +npm run deploy:public +npm run deploy:admin +``` + +- `deploy`: 현재 앱 전체 배포 +- `deploy:public`: 관리자 route를 제외한 public 앱 배포 +- `deploy:admin`: 별도 `apps/admin` 관리자 앱 배포 + +관리자 분리 기준 배포 순서는 아래를 권장한다. +1. `npm run deploy:admin` +2. `ADMIN_APP_URL=https://altteulmap-admin..workers.dev` 설정 +3. `npm run deploy:public` + +이 명령들은 `.next`, `.open-next`를 먼저 비우고 다시 OpenNext build를 만든 뒤 업로드한다. 현재 저장소는 Cloudflare Workers Free 한도에 맞추기 위해 `webpack` build를 사용한다. preview 기준으로 로컬 URL 허용 상태만 보려면: @@ -75,6 +93,7 @@ npm run deploy:check -- --preview ## 8. 주의사항 - production에서는 `NEXTAUTH_URL`에 `localhost`를 쓰면 안 된다. +- `deploy:public`은 `ADMIN_APP_URL` 없이 실행하면 실패하게 해두었다. public 번들에서 `/admin`, `/api/admin`을 제거하므로 외부 관리자 앱 주소가 필요하다. - 네이버 지도 키와 네이버 로그인 키는 서로 다른 값이다. - 지도용 네이버 키는 `NEXT_PUBLIC_NAVER_MAP_KEY_ID`, 로그인용은 `AUTH_NAVER_CLIENT_ID` / `AUTH_NAVER_CLIENT_SECRET`이다. - 네이버 지도는 환경 변수만 맞춰도 끝나지 않는다. NAVER Cloud Platform 지도 애플리케이션 설정의 웹 서비스 URL 또는 허용 도메인에 실제 배포 주소(`https://..workers.dev` 또는 custom domain)를 같이 등록해야 한다. diff --git a/eslint.config.mjs b/eslint.config.mjs index b9296aa..183a20f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -10,9 +10,13 @@ const eslintConfig = defineConfig([ // Default ignores of eslint-config-next: ".next/**", ".next-dev/**", + "apps/admin/.next/**", + "apps/admin/.next-dev/**", + "apps/admin/.open-next/**", "out/**", "build/**", "next-env.d.ts", + "apps/admin/next-env.d.ts", ".open-next/**", ".wrangler/**", "drizzle/**", diff --git a/package.json b/package.json index 9ea114a..f8b1d52 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,12 @@ "version": "0.1.0", "private": true, "scripts": { - "build": "rm -rf .next .next-dev && next build --webpack", - "dev": "rm -rf .next-dev && next dev --webpack", + "build": "npm run admin:sync && rm -rf .next .next-dev && next build --webpack", + "dev": "ALTTEULMAP_ADMIN_MODE=embedded npm run admin:sync && rm -rf .next-dev && next dev --webpack", + "admin:sync": "node scripts/sync-admin-entrypoints.mjs", + "admin:build": "npm_config_workspaces=false npm --prefix apps/admin run build", + "admin:dev": "npm_config_workspaces=false npm --prefix apps/admin run dev", + "admin:start": "npm_config_workspaces=false npm --prefix apps/admin run start", "start": "next start", "lint": "eslint", "e2e:prepare": "lsof -tiTCP:3107 -sTCP:LISTEN | xargs -r kill && rm -rf .next test-results playwright-report && mkdir -p .next/types .next/dev/types && printf 'export {};\\n' > .next/types/routes.d.ts && printf 'export {};\\n' > .next/dev/types/routes.d.ts", @@ -23,15 +27,22 @@ "deploy:check": "node scripts/check-cloudflare-deploy.mjs", "hooks:install": "node scripts/git-hooks/install-hooks.mjs", "cf:clean": "rm -rf .next .next-dev .open-next", - "cf:build": "npm run cf:clean && opennextjs-cloudflare build", + "cf:build": "npm run admin:sync && npm run cf:clean && opennextjs-cloudflare build", + "cf:build:public": "npm run cf:clean && node scripts/build-public-worker.mjs", + "cf:build:admin": "node scripts/build-admin-worker.mjs", "db:generate": "drizzle-kit generate", "db:push": "drizzle-kit push", "db:seed": "tsx src/db/seed.ts", + "data:goodprice": "tsx scripts/import-goodprice.ts", "db:studio": "drizzle-kit studio", "db:up": "docker compose up -d postgres", "db:down": "docker compose down", "preview": "npm run cf:build && opennextjs-cloudflare preview", + "preview:public": "npm run cf:build:public && opennextjs-cloudflare preview -c wrangler.jsonc", + "preview:admin": "npm run cf:build:admin && opennextjs-cloudflare preview -c wrangler.admin.jsonc", "deploy": "npm run cf:build && opennextjs-cloudflare deploy", + "deploy:public": "npm run cf:build:public && opennextjs-cloudflare deploy -c wrangler.jsonc", + "deploy:admin": "npm run cf:build:admin && opennextjs-cloudflare deploy -c wrangler.admin.jsonc", "upload": "npm run cf:build && opennextjs-cloudflare upload", "cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts" }, diff --git a/scripts/build-admin-worker.mjs b/scripts/build-admin-worker.mjs new file mode 100644 index 0000000..4676a00 --- /dev/null +++ b/scripts/build-admin-worker.mjs @@ -0,0 +1,28 @@ +import { execFileSync } from "node:child_process"; +import path from "node:path"; +import { rmSync } from "node:fs"; + +const projectRoot = process.cwd(); +const adminAppRoot = path.join(projectRoot, "apps", "admin"); +const openNextBin = path.join( + projectRoot, + "node_modules", + ".bin", + "opennextjs-cloudflare", +); + +for (const relativePath of [".next", ".next-dev", ".open-next"]) { + rmSync(path.join(adminAppRoot, relativePath), { + recursive: true, + force: true, + }); +} + +execFileSync(openNextBin, ["build"], { + cwd: adminAppRoot, + env: { + ...process.env, + npm_config_workspaces: "false", + }, + stdio: "inherit", +}); diff --git a/scripts/build-public-worker.mjs b/scripts/build-public-worker.mjs new file mode 100644 index 0000000..5227589 --- /dev/null +++ b/scripts/build-public-worker.mjs @@ -0,0 +1,81 @@ +import { execFileSync } from "node:child_process"; +import { cpSync, existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import dotenv from "dotenv"; + +const projectRoot = process.cwd(); +const backupRoot = mkdtempSync(path.join(tmpdir(), "altteulmap-public-build-")); +const adminTargets = [ + "src/app/admin", + "src/app/api/admin", +]; + +for (const filename of [ + ".env", + ".env.production", + ".env.local", + ".env.production.local", +]) { + dotenv.config({ + path: path.join(projectRoot, filename), + override: true, + }); +} + +function backupAndRemove(relativePath) { + const sourcePath = path.join(projectRoot, relativePath); + + if (!existsSync(sourcePath)) { + return; + } + + const backupPath = path.join(backupRoot, relativePath); + mkdirSync(path.dirname(backupPath), { recursive: true }); + cpSync(sourcePath, backupPath, { recursive: true }); + rmSync(sourcePath, { recursive: true, force: true }); +} + +function restore(relativePath) { + const sourcePath = path.join(projectRoot, relativePath); + const backupPath = path.join(backupRoot, relativePath); + + rmSync(sourcePath, { recursive: true, force: true }); + + if (!existsSync(backupPath)) { + return; + } + + mkdirSync(path.dirname(sourcePath), { recursive: true }); + cpSync(backupPath, sourcePath, { recursive: true }); +} + +if (!process.env.ADMIN_APP_URL) { + throw new Error( + "[build-public-worker] ADMIN_APP_URL is required when building the public-only worker.", + ); +} + +try { + for (const relativePath of adminTargets) { + backupAndRemove(relativePath); + } + + execFileSync( + "npx", + ["opennextjs-cloudflare", "build"], + { + cwd: projectRoot, + env: { + ...process.env, + }, + stdio: "inherit", + }, + ); +} finally { + for (const relativePath of adminTargets) { + restore(relativePath); + } + + rmSync(backupRoot, { recursive: true, force: true }); +} diff --git a/scripts/import-goodprice.ts b/scripts/import-goodprice.ts new file mode 100644 index 0000000..1c08bea --- /dev/null +++ b/scripts/import-goodprice.ts @@ -0,0 +1,667 @@ +import { mkdir, writeFile } from "node:fs/promises"; +import { createHash } from "node:crypto"; +import path from "node:path"; + +type CliOptions = { + limit: number; + maxPrice: number; + delayMs: number; + timeoutMs: number; + includeDetail: boolean; + outputPath: string; + manifestPath: string; +}; + +type RegionOption = { + code: string; + name: string; +}; + +type GoodpriceListItem = { + bsshSn: string; + name: string; + categoryName: string; + address: string; + phone: string; + representativeMenu: string; + price: number; + latitude: number; + longitude: number; + pageIndex: number; + regionCode: string; + regionName: string; +}; + +type GoodpriceDetailMenu = { + label: string; + amount: number; + isDesignated: boolean; +}; + +type GoodpriceDetail = { + description: string; + businessHours: string; + phone: string; + menus: GoodpriceDetailMenu[]; +}; + +type PlacePriceItem = { + id: string; + label: string; + amount: number; + verificationStatus: "verified" | "unverified"; + reportedAt: string; +}; + +type PlaceRecord = { + id: string; + name: string; + businessName?: string; + categorySlug: string; + address: string; + district: string; + latitude: number; + longitude: number; + representativePriceAmount: number; + representativePriceLabel: string; + verificationStatus: "verified" | "unverified"; + lastPriceUpdatedAt: string; + description: string; + note: string; + likeCount: number; + dislikeCount: number; + viewerReaction: "like" | "dislike" | null; + priceItems: PlacePriceItem[]; + history: Array<{ + id: string; + label: string; + amount: number; + verificationStatus: "verified" | "unverified"; + recordedAt: string; + }>; + comments: Array<{ + id: string; + authorLabel: string; + body: string; + createdAt: string; + }>; +}; + +const GOODPRICE_BASE_URL = "https://goodprice.go.kr"; +const LIST_PATH = "/bssh/bsshList.do"; +const DETAIL_PATH = "/bssh/bsshInfo.json"; + +function getKstDateStamp() { + return new Intl.DateTimeFormat("sv-SE", { + timeZone: "Asia/Seoul", + }).format(new Date()); +} + +const IMPORTED_AT = getKstDateStamp(); + +function parseArgs(argv: string[]): CliOptions { + const options: CliOptions = { + limit: 1000, + maxPrice: 10000, + delayMs: 120, + timeoutMs: 15000, + includeDetail: true, + outputPath: "src/features/places/imported-goodprice.json", + manifestPath: "data/goodprice/import-meta.json", + }; + + for (const arg of argv) { + if (!arg.startsWith("--")) { + continue; + } + + const [flag, rawValue] = arg.slice(2).split("=", 2); + const value = rawValue ?? ""; + + switch (flag) { + case "limit": + options.limit = Number(value) || options.limit; + break; + case "max-price": + options.maxPrice = Number(value) || options.maxPrice; + break; + case "delay-ms": + options.delayMs = Number(value) || options.delayMs; + break; + case "timeout-ms": + options.timeoutMs = Number(value) || options.timeoutMs; + break; + case "include-detail": + options.includeDetail = value !== "false"; + break; + case "output": + options.outputPath = value || options.outputPath; + break; + case "manifest": + options.manifestPath = value || options.manifestPath; + break; + default: + break; + } + } + + return options; +} + +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +function stripTags(value: string) { + return value.replace(/<[^>]*>/g, " "); +} + +function decodeEntities(value: string) { + return value + .replace(/ /g, " ") + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/'/g, "'") + .replace(/"/g, '"'); +} + +function cleanText(value: string | undefined) { + if (!value) { + return ""; + } + + return decodeEntities(stripTags(value)) + .replace(/\s+/g, " ") + .trim(); +} + +function parsePrice(value: string) { + const digits = value.replace(/[^\d]/g, ""); + return digits.length > 0 ? Number(digits) : Number.NaN; +} + +function createSlug(bsshSn: string) { + return `goodprice-${bsshSn}`; +} + +function createStableId(...parts: string[]) { + return createHash("sha1").update(parts.join("|")).digest("hex").slice(0, 20); +} + +function getDistrict(address: string) { + const tokens = address.split(/\s+/).filter(Boolean); + return tokens.slice(0, 2).join(" "); +} + +function mapCategorySlug(categoryName: string) { + switch (categoryName) { + case "한식": + return "korean"; + case "일식": + return "japanese"; + case "양식": + return "western"; + case "중식": + return "chinese"; + case "베이커리": + return "bakery"; + case "기타요식업": + return "other-food"; + case "세탁업": + return "laundry"; + case "목욕업": + return "bath"; + case "숙박업": + return "lodging"; + case "이용업": + return "barber"; + case "미용업": + return "beauty"; + case "기타비요식업": + return "other-service"; + default: + return "other-service"; + } +} + +async function fetchText( + pathname: string, + form: Record, + timeoutMs: number, + expectJson = false, +) { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), timeoutMs); + + try { + const response = await fetch(`${GOODPRICE_BASE_URL}${pathname}`, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", + }, + body: new URLSearchParams(form), + signal: controller.signal, + }); + + if (!response.ok) { + throw new Error(`${pathname} ${response.status} ${response.statusText}`); + } + + return expectJson ? response.json() : response.text(); + } finally { + clearTimeout(timeout); + } +} + +function extractRegionOptions(html: string) { + const selectMatch = html.match( + /