GitHub

서울 현지인이 외국인 관광객을 위해 만든 맛집 영어 블로그 개발기 (Next.js, Supabase, LLM)

hojun lee · 07/08/2025
커버이미지

Tasty 4 seoul

외국인을 위한 한국 맛집 영어 블로그

Naver blog 크롤링 → DeepL API 고품질 번역 → 맛집 주소 추출 → Perplexity API 주소 기반 여행지 추천 → GEMINI API 포스팅 SEO 및 품질 향상 → Supabase postresql DB 업로드

Subabasegoogle Map API 맵을 활용한 UX 경험 강화 → SEO 최적화된 UIUX 설계 및 배포

이 많은 외국인은 뭘 보고 여행할까?

결국 컨텐츠가 살아남는 시대이다. 나는 한국인이 주로 이용하는 네이버 블로그에 음식(맛집)블로그를 작성해왔다. 내가 살고 있는 마포구를 돌아다니다보면 수 많은 외국인, 뻥을 보태면 홍대, 상수, 망원 길거리에 70%가 넘는 사람이 외국인이다. 궁금증이 생겼다. ‘이 사람들은 무엇을 보며 여행을 할까?’ 이 기획의 시작점은 이 것이다. 1차 탐색은 [인스타그램, 틱톡, 유튜브, 지도 앱] 으로 트렌드 인기를 빠르게 파악한 뒤 결국 여행의 세부적인 의사결정 단계는 2차 탐색 [ 블로그 ] 로 상세 후기, 가격, 꿀팁, 실제 경험기반 정보를 얻는다고 한다.

대부분의 여행객들은 두 단계로 정보를 얻는다는 것을 알게 되었다.

  1. 1차 탐색 (트렌드 파악): 인스타그램, 틱톡, 유튜브 숏폼, 구글맵의 인기 장소로 요즘 '뜨는' 곳을 파악한다.
  2. 2차 탐색 (의사결정): 하지만 실제 방문을 결정하는 단계에서는, 신뢰할 수 있는 블로그를 찾는다. 생생한 후기, 정확한 가격, 현지인만 아는 꿀팁 등 실제 경험에 기반한 상세한 정보를 원하기 때문이다.

나는 마포구 현지인으로서 직접 가보고 얻은 생생한 스토리텔링을 기반으로 외국인에게 맛과 멋이 있는 마포구 블로그를 제공하고자 한다. 다만 내 번역 실력만으로는 맛의 미묘한 뉘앙스와 감성을 전달하기 어려웠다. 그래서 이 프로젝트는 LLM API의 도움을 받아, 기술로 언어의 장벽을 넘어 본다.

기술스택 선정

  • 프레임워크: Next.js (App Router) - 선택 이유: 여행자들은 이동 중에 모바일로 정보를 빠르게 확인해야 합니다. Next.js의 서버 컴포넌트(RSC)는 서버에서 페이지를 미리 렌더링하여 사용자에게 즉각적인 로딩 경험을 제공합니다. 이는 사용자 이탈을 막는 데 결정적입니다.
    • 지속 가능한 성장을 위한 설계: Feature-Sliced Design (FSD)
  • 백엔드 & DB: Supabase
    • 선택 이유: 1인 개발 프로젝트에서 복잡한 백엔드 인프라를 구축하는 것은 비효율적입니다. Supabase는 데이터베이스, API, 인증 등을 통합 제공하여 제가 온전히 콘텐츠와 프론트엔드 경험에만 집중할 수 있게 해주었습니다.
  • 스타일링: Tailwind CSS & shadcn/ui
    • 선택 이유: Tailwind CSS의 유틸리티 우선 접근법은 빠른 프로토타이핑을 가능하게 했습니다. 여기에 shadcn/ui를 더해, 단순한 UI 라이브러리를 넘어 프로젝트에 완전히 통합되고 커스터마이징 가능한 디자인 시스템을 구축할 수 있었습니다.
  • 지도: React Google Maps API (@react-google-maps/api)
    • 선택 이유: 맛집 블로그에서 '위치'는 핵심 정보입니다. 주소만으로 지도에 핀을 찍고, 시각적으로 위치를 안내하는 기능은 외국인 관광객에게 필수적인 편의를 제공합니다.

폴더구조 : FSD

개인 프로젝트일수록 체계적인 구조 없이 시작하면 금방 스파게티 코드가 되기 십상이다. 이 프로젝트가 앞으로 계속 성장할 것을 염두에 두고, Feature-Sliced Design (FSD) 아키텍처를 도입했다.

FSD는 프로젝트를 기능(Feature) 단위로 명확하게 분리하고, 계층 간의 의존성을 엄격하게 관리한다.

   1 src/
   2 ├── app/         # 최상위 계층 (전역 설정, 레이아웃)
   3 ├── widgets/     # 위젯 계층 (여러 기능/엔티티의 조합, 예: 관련 글 목록)
   4 ├── features/    # 기능 계층 (사용자 액션, 예: 지도 상호작용)
   5 ├── entities/    # 엔티티 계층 (핵심 데이터 모델, 예: 게시물)
   6 └── shared/      # 공유 계층 (공용 UI, API 클라이언트, 유틸 함수)

이 구조 덕분에 새로운 기능을 추가할 때 어디에 코드를 작성해야 할지 명확해졌고, 코드의 재사용성과 유지보수성이 극대화되었다.

핵심 기능: 주소만으로 지도에 핀 찍고 길 안내하기

가장 공들인 기능은 주소 텍스트만으로 지도 위에 정확한 위치를 표시하고, 사용자와 상호작용하는 부분이었다.

지도 기반 여행지 추천

  1. 지오코딩(Geocoding): useJsApiLoader 훅으로 Google Maps API 스크립트가 완전히 로드되었는지 확인한 후, geocoder.geocode()를 호출하여 주소 문자열을 위도/경도 좌표로 변환합니다.
  2. 동적 경계 설정: 변환된 모든 좌표(locations)를 포함하는 LatLngBounds 객체를 생성하고, map.fitBounds()를 호출하여 모든 핀이 가장 잘 보이는 확대 레벨과 중앙 위치를 자동으로 계산하도록 했습니다.
  3. 사용자 경험 향상: 페이지 이동: 지도 위의 핀이나 테이블의 행을 클릭하면, 해당 가게의 상세 리뷰 페이지(blog/[id])로 바로 이동할 수 있습니다.

주소 기반 여행지 추천 : 프롬프트

prompt = f"""
다음 주소와 주변 장소를 기반으로 3가지 여행 코스를 제안해주세요. 각 코스는 2~3시간 코스로 2~3개 장소를 포함해야 합니다. 현실적이고 즐거운 일정을 만들어주세요.

주소: {address}

# 페르소나 (Persona)
당신은 특정 동네를 자기 손바닥 보듯 꿰뚫고 있는 토박이 주민입니다. 당신은 정이 많고, 자신의 동네를 방문하는 사람들에게 숨겨진 보석 같은 장소들을 알려주는 것을 즐깁니다. 상업적인 광고나 블로그에서 흔히 볼 수 있는 뻔한 정보가 아닌, 당신의 실제 경험과 노하우가 담긴 진솔한 이야기를 들려주세요.

# 목표 (Objective)
아래에 제시된 `{address}` 주소를 기반으로, 그 동네의 매력을 흠뻑 느낄 수 있는 여행 코스 3가지를 추천해 주세요. 각 코스는 단순한 장소 나열이 아닌, 테마와 스토리가 있는 하나의 완성된 경험이어야 합니다. 여행객들이 "아, 여기는 정말 와보길 잘했다!"라고 느낄 만한 곳들로 엄선해 주세요.
일목요연하게 요약된 형태로 작성하고 글을 최대 300자를 넘지 않도록 하세요.
정확히 있는 식당과 공원 명칭을 사용하세요. address는 음식점일 확률이 높으니, 디저트나 카페, 공원 등 반드시 실제 명칭을 기반으로 추천해주세요.

# 결과물 형식 (Format)
1.  **세 가지 테마 코스**: '코스 1: [창의적인 코스 제목]', '코스 2: [창의적인 코스 제목]', '코스 3: [창의적인 코스 제목]' 형식으로 세 가지 여행 코스를 제안해 주세요. 제목은 여행객의 호기심을 자극할 수 있도록 지어주세요. (예: '시간이 멈춘 골목길 산책', '인스타그래머를 위한 힙스터 성지 순례', '혼자서 즐기는 사색의 시간')
2.  **구체적인 장소 추천**: 각 코스마다 3~4개의 장소(맛집, 카페, 명소, 상점 등)를 포함해 주세요.
3.  **생생한 추천 이유**: 각 장소를 추천하는 이유를 동네 주민의 시선에서 구체적이고 생생하게 설명해 주세요. (예: '여기는 사장님이 직접 내려주는 드립 커피가 일품인데요, 특히 창가 자리에 앉으면 지나가는 동네 고양이들을 볼 수 있어서 제가 가장 아끼는 자리랍니다.')
4.  **자연스러운 동선**: 각 코스 내 장소들은 자연스럽게 이어지는 동선으로 구성하고, 이동 팁이나 예상 소요 시간을 슬쩍 언급해주세요.

# 톤 앤 매너 (Tone & Style)
*   **친근한 구어체**: 마치 친한 친구나 동생에게 우리 동네를 소개해 주는 것처럼 다정하고 친근한 말투를 사용해 주세요. (예: '~거든요', '~랍니다', '~한번 가보세요')
*   **진솔함**: 과장되지 않고 진솔한 표현을 사용하여 추천에 진정성을 더해주세요.

# 제약 조건 (Constraints)
*   **지리적 근접성**: 추천하는 모든 장소는 주어진 주소에서 도보나 짧은 대중교통으로 쉽게 이동할 수 있어야 합니다.
*   **정확한 정보**: 반드시 실제로 존재하며 현재 운영 중인 장소만 추천해야 합니다.
*   **숨겨진 명소 위주**: 너무 유명해서 관광객으로 붐비는 곳보다는, 동네 주민들이 실제로 아끼고 사랑하는 숨겨진 장소 위주로 추천해 주세요. 만약 유명한 곳을 포함한다면, 그곳을 남들과는 다르게 즐길 수 있는 특별한 팁을 함께 제공해야 합니다.
"""

해당 프롬프트와 llm (pplx)를 이용해 주소 기반 3가지 여행 코스를 제안받고 추천한다. 생각보다 뛰어난 추천코스가 나와서 놀랐다.

라우트별 기능 및 SEO 최적화 전략

성공적인 웹 애플리케이션은 잘 설계된 구조뿐만 아니라, 사용자와 검색 엔진이 쉽게 발견할 수 있도록 만드는 SEO 전략이 필수적이다 마케팅 비용을 최대한 아끼려면 organic searching이 최고이기 때문이다. 각 라우트의 역할과 그에 맞는 SEO 최적화 방법을 소개한다.

  1. / (메인 페이지) - 블로그 핵심 요약
  • 기능 : 히어로 섹션을 통해 블로그의 정체성을 보여주고, 최신 게시물 3개와 해당 위치가 표시된 지도를 노출하여 흥미를 유발한다.
  • SEO 최적화:
    • 메타 데이터: layout.tsx에 설정된 메타 태그(title, description)는 사이트 전체의 정체성을 나타냅니다. "Seoul Food Guide", "Local's Guide to Seoul Restaurants" 등 핵심 키워드를 포함하여 검색 엔진에 우리 사이트가 무엇을 하는 곳인지 명확히 알린다.
    • 콘텐츠의 신선도: 최신 게시물을 동적으로 보여줌으로써, 검색 엔진에 "이 사이트는 꾸준히 관리되고 있다"는 긍정적인 신호를 보낸다.
    • 성능 (LCP/CLS): Next.js 서버 컴포넌트를 사용하여 서버에서 데이터를 미리 가져와 렌더링하므로, 사용자가 느끼는 로딩 속도(LCP)가 매우 빠르다. 이는 이탈률을 낮추고 SEO에 긍정적인 영향을 준다.
  1. '/' (블로그 페이지) - 블로그 모음
  • 기능 : 블로그 글 목록을 보여준다. 카드형태로 간략히 보여주면서 사용자의 관심을 이끈다.
    • 인피니티 스크롤 : 블로그 포스팅이 많아질수록 해당 페이지는 부하가 커질 수 밖에 없다. 처음 화면 로딩 시 많은 데이터를 가져오고 그려야하기 때문이다. 그래서 블로그 데이터는 6개씩 가져오고 그 아래로 스크롤을 내릴 때 6개씩 더 가져오는 무한스크롤 방식을 채택했다. 사용자는 끊김 없는 포스팅을 볼 수 있고, 서버도 부하를 줄일 수 있다.

  1. /blog/[id] (상세 페이지) - SEO의 핵심
  • 기능: 개별 맛집에 대한 상세한 정보(리뷰, 사진, 가격, 위치 등)를 제공하는 페이지이다. 결국 사용자가 검색을 통해 가장 많이 유입될 것으로 예상되는 핵심 페이지이다.
  • SEO 최적화:
    • 동적 메타 데이터: Next.js의 generateMetadata 함수를 사용하여 각 포스트의 제목과 설명을 기반으로 동적인 <title>과 <meta name="description">을 생성한다. 결국 봇들이 내 홈페이지를 잘 읽을 수 있게 아부하는 수밖에 없다.

예를 들어, "망원동 최고의 돼지국밥, OOO식당 후기 | Taste for Seoul"과 같이 구체적인 제목을 만들어 특정 키워드 검색에 효과적으로 대응한다.

  • 구조화된 데이터 (Structured Data): JSON-LD 형식으로 Article 또는 Restaurant 스키마를 적용하여 검색 엔진이 이 페이지의 콘텐츠가 '기사' 혹은 '식당 정보'임을 명확히 이해하도록 돕습니다. 이는 검색 결과에서 별점, 리뷰 수 등이 표시되는 '리치 스니펫(Rich Snippet)' 노출 확률을 높여 클릭률(CTR)을 상승시킨다.
  • 콘텐츠: 의미 있는 <h1>, <h2> 태그 사용, 이미지에 alt 속성으로 설명을 추가하는 등 시맨틱 HTML을 철저히 준수하는 것이 기본이자 가장 중요하다.
  • 추가적인 클릭을 유도하기 위해 관련 컨텐츠 3개를 밑에 깔아 놓았다. 이는 분명 홈페이지가 더 궁금한 사람들에게 큰 상호 작용을 할 것이다.
    1. /map & /about - 신뢰도와 탐색의 즐거움

  • 기능: /map은 등록된 모든 맛집을 지도와 테이블로 보여주며 시각적인 탐색 경험을 제공한다. /about은 운영자의 스토리를 통해 블로그의 신뢰도를 구축한다.
  • SEO 최적화:
    • E-E-A-T (경험, 전문성, 권위, 신뢰성): 구글이 중요하게 생각하는 E-E-A-T 요소를 충족시키는 데 /about 페이지는 결정적인 역할을 한다. "서울 마포구에 거주하는 현지인"이라는 경험과 정체성을 명확히 밝혀 콘텐츠의 신뢰도를 높인다.
    • 고유한 콘텐츠 제공: /map 페이지는 단순한 목록을 넘어, 지도라는 시각적 도구를 통해 사용자에게 독특한 탐색 경험을 제공한다. 이는 사용자의 체류 시간을 늘리고 사이트에 긍정적인 인상을 남긴다.
  1. /sitemap.ts & /rss.xml - 검색 엔진과의 소통 창구
  • 기능: 사용자가 직접 방문하는 페이지는 아니지만, 검색 엔진 최적화에 있어 가장 중요한 기술적 요소이다. sitemap.ts는 사이트의 모든 페이지 목록을 검색 엔진에 제공하고, rss.xml은 새로운 콘텐츠가 발행되었음을 알리는 역할을 합니다.
  • SEO 최적화:
    • 크롤링 효율성: 동적으로 생성되는 사이트맵은 새로운 글이 발행될 때마다 자동으로 업데이트되어, 구글봇과 같은 크롤러가 우리의 모든 콘텐츠를 빠르고 누락 없이 수집해가도록 돕는다.
    • 색인 촉진: RSS 피드를 구독하는 검색 엔진 및 여러 서비스에 신규 콘텐츠를 적극적으로 알림으로써, 검색 결과에 더 빨리 노출될 기회를 만든다.

결론

내가 가진 컨텐츠를 어떻게 내가 가진 기술과 결합할 것인가 에 대한 고민이 항상있었다. 개발을 하면서 항상 느꼈던 생각은 결국 컨텐츠가 없으면 깡통이라는 것이다. 난 어떤 스토리를 가지고 있고, 내 주변에 사람들은 어떤 컨텐츠를 가지고 있는지 관찰하는 버릇이 생겼다. 그 컨텐츠를 IT 기술로 만드는 것은 결국 기획의 몫이다. 이 것이 이 되진 않을 수 있다. 하지만 돈이 되지 않아도 가치있는 일은 있다. 나만의 가치를 tasty4seoul과 나의 다양한 프로젝트를 통해 녹여낼 것이다.

앞으로도 지켜봐주길 바란다.