파이썬 (Python)/네이버 지도 & Streamlit

5 - Streamlit에서 네이버 지도 통합 – 인터랙티브 장소 검색

gsroot 2025. 6. 18. 11:26
728x90
반응형
SMALL

이번 마지막 포스팅에서는 Streamlit을 활용해 사용자 입력 기반 장소 검색, 데이터 가공, 그리고 네이버 지도 시각화까지 한 번에 해결하는 대화형 앱을 완성하는 실전적 방법을 더욱 풍부하게 안내합니다. 이 시리즈에서 다룬 모든 기술과 노하우를 통합해, 실제 업무, 개인 프로젝트, 내부 도구 개발 등 다양한 현장에 바로 적용 가능한 통합형 솔루션으로 발전시킬 수 있습니다.


Streamlit의 장점과 다양한 활용 사례

Streamlit은 파이썬만으로 손쉽게 웹 앱을 개발할 수 있는 프레임워크로, 다음과 같은 다양한 장점과 현장 적용 사례를 갖고 있습니다.

  • 초간편 웹앱 개발: 별도의 프론트엔드·백엔드 기술 없이 파이썬 코드만으로 실시간 대화형 웹 UI와 데이터 시각화 앱 제작 가능
  • 원스톱 데이터 파이프라인: 장소 검색, 데이터 가공, 지도 시각화, 결과 다운로드·공유까지 한 화면에서 처리 가능
  • 비전공자·초보자도 실무 투입: 복잡한 웹 개발 경험이 없어도 직관적으로 빠른 프로토타입 및 실전 앱 제작이 가능
  • 강력한 API 연동성: 네이버 지도 API, 공공데이터, 구글 서비스 등 외부 API와의 통합이 자유로워, 국내 환경에 최적화된 사용자 경험 제공
  • 배포와 공유의 용이성: Streamlit Cloud, 사내 서버, 클라우드 등 다양한 환경에 손쉽게 배포·공유 가능

실제로 데이터 분석 자동화, 리포트 도구, 매장 관리, 부동산 지도, 여행지 추천, 상권 분석, 사내 검색 서비스, 업무 대시보드 등 수많은 실무 현장에서 널리 활용되고 있습니다.


Streamlit 기반 네이버 지도 앱 구조와 흐름

Streamlit으로 구현하는 네이버 지도 앱의 구조와 전체 흐름을 단계별로 정리하면 아래와 같습니다.

  1. 검색어 입력 UI: st.text_input, st.button 등으로 사용자가 키워드·지역·업종 입력
  2. 네이버 장소 검색: 입력값 기반으로 검색 API 호출, DataFrame화 및 데이터 정제
  3. 검색 결과 표시: st.dataframe, st.table로 장소 리스트를 표로 보여주고, 선택·필터·다운로드도 지원
  4. 지도 시각화: 선택된(혹은 전체) 장소를 Static Map에 마커로 표시, st.image로 실시간 지도 렌더링
  5. UI 옵션: 마커 색상, 라벨, 지도 확대 수준, 카테고리별 필터 등 다양한 옵션으로 사용자 맞춤화
  6. 결과 저장·공유: 지도 이미지·장소 데이터 CSV·엑셀로 다운로드, 슬랙·이메일 공유 등 다양한 외부 연동도 가능

이러한 앱 구조는 사용자 친화적이며, 반복 검색·데이터 자동화·시각화·보고서 제작까지 논스톱으로 처리할 수 있습니다.


실전 Streamlit 구현 예제 – 통합 코드와 상세 설명

아래는 네이버 지도 검색 및 Static Map 연동을 Streamlit에서 완성하는 실전 예제 코드입니다. 실무 활용 및 확장성까지 염두에 둔 구조로, 다양한 검색 옵션, 대량 데이터, 사용자 정의 UI, 자동화까지 모두 대응할 수 있습니다.

import streamlit as st
import requests
from urllib.parse import quote
from PIL import Image
import io
import pandas as pd
from pyproj import Transformer
import re

NAVER_CLIENT_ID = "<네이버_검색_Client_ID>"
NAVER_CLIENT_SECRET = "<네이버_검색_Client_Secret>"
NCP_CLIENT_ID = "<네이버_클라우드_Client_ID>"
NCP_CLIENT_SECRET = "<네이버_클라우드_Client_Secret>"

def tm128_to_wgs84(mapx, mapy):
    transformer = Transformer.from_crs("EPSG:5179", "EPSG:4326", always_xy=True)
    lon, lat = transformer.transform(mapx, mapy)
    return lat, lon

st.title("네이버 지도 장소 검색 & 시각화 데모")
query = st.text_input("장소/키워드 입력", "강남역 카페")
max_results = st.slider("결과 개수", min_value=1, max_value=20, value=5)
btn = st.button("검색하기")

if btn and query:
    search_url = "https://openapi.naver.com/v1/search/local.json"
    params = {"query": query, "display": max_results, "start": 1, "sort": "random"}
    headers = {"X-Naver-Client-Id": NAVER_CLIENT_ID, "X-Naver-Client-Secret": NAVER_CLIENT_SECRET}
    res = requests.get(search_url, params=params, headers=headers)
    items = res.json().get("items", [])

    df_list = []
    for idx, item in enumerate(items, 1):
        title = re.sub('<.*?>', '', item['title'])
        mapx, mapy = float(item['mapx']), float(item['mapy'])
        lat, lon = tm128_to_wgs84(mapx, mapy)
        df_list.append({"번호": idx, "이름": title, "도로명주소": item['roadAddress'], "카테고리": item['category'], "lat": lat, "lon": lon})
    df = pd.DataFrame(df_list)
    st.dataframe(df)

    selected = st.multiselect("지도에 표시할 장소를 선택하세요 (미선택 시 전체 표시)", df["이름"]) 
    df_show = df if not selected else df[df["이름"].isin(selected)]

    # 지도 확대/마커 옵션 추가
    col1, col2 = st.columns(2)
    with col1:
        level = st.slider("지도 확대 (level)", min_value=7, max_value=16, value=12)
    with col2:
        marker_color = st.selectbox("마커 색상", ["blue", "red", "green", "yellow", "black"]) 

    base_url = "https://naveropenapi.apigw.ntruss.com/map-static/v2/raster"
    width, height = 600, 400
    markers = []
    for idx, row in df_show.iterrows():
        marker = f"type:n|size:mid|color:{marker_color}|pos:{row['lon']} {row['lat']}|label:{row['번호']}"
        markers.append(marker)
    markers_query = '&'.join([f"markers={quote(m)}" for m in markers])
    req_url = f"{base_url}?w={width}&h={height}&level={level}&maptype=basic&lang=ko&{markers_query}"
    map_headers = {"X-NCP-APIGW-API-KEY-ID": NCP_CLIENT_ID, "X-NCP-APIGW-API-KEY": NCP_CLIENT_SECRET}
    res2 = requests.get(req_url, headers=map_headers)
    image = Image.open(io.BytesIO(res2.content))
    st.image(image, caption="검색된 장소 위치 지도")
    # 이미지 다운로드 기능 추가
    img_bytes = io.BytesIO()
    image.save(img_bytes, format='PNG')
    st.download_button(label="지도 이미지 다운로드", data=img_bytes.getvalue(), file_name="map.png", mime="image/png")

    # 데이터 다운로드 기능
    st.download_button(label="장소 데이터 다운로드 (CSV)", data=df_show.to_csv(index=False).encode('utf-8-sig'), file_name="places.csv", mime="text/csv")

실전 활용 팁과 마무리 체크리스트

  • UI/UX 고도화: 검색 결과 선택, 지도 옵션, 에러 안내, 데이터 다운로드, 컬러 옵션 등 다양한 사용자 경험을 풍부하게 개선할 수 있습니다.
  • 보안: API Key는 반드시 환경변수/시크릿 매니저 등 안전하게 관리하고, 코드 내 하드코딩을 피하세요.
  • 확장성: 위치 기반 추천, 일정 관리, 대량 검색, 보고서 자동화, 메신저 연동 등 다양한 실무·개인 서비스로 손쉽게 확장할 수 있습니다.
  • 클라우드 배포 팁: Streamlit Cloud, AWS, GCP 등에서 서비스 배포 시 앱 도메인/서버IP를 NCP 콘솔에 등록해 CORS 오류를 예방하세요.
  • API 쿼터/성능 주의: 네이버 API 사용 쿼터, 응답 속도, 동시 접속 수 등 한계를 고려해 앱 구조를 설계하세요.
  • 테스트 및 유지보수: 다양한 검색어, 위치, 결과 수 등으로 앱을 충분히 테스트하고, 향후 네이버 API 정책 변경 시에도 즉시 대응할 수 있도록 문서화 및 관리 체계를 갖추는 것이 좋습니다.

참고 문서 및 공식 가이드


시리즈를 마치며

네이버 지도 API의 기초부터, 파이썬을 활용한 장소 검색 자동화, 지도 이미지 시각화, 그리고 최종적으로 Streamlit 대시보드 앱 완성까지 모든 과정을 실전 위주로 단계별 안내했습니다. 실제 예제 코드를 직접 실행해보며, 데이터 기반 자동화와 시각화 역량을 실질적으로 체득할 수 있도록 구성했습니다.

지도 데이터 자동화와 웹앱 개발 능력은 앞으로도 데이터 서비스, 실생활 문제 해결, 현업·개인 프로젝트 등에서 폭넓게 적용될 것입니다. 이 시리즈가 여러분의 데이터 기반 서비스 설계, 업무 자동화, 창의적 아이디어 실현에 확실한 도움과 영감을 주기를 바랍니다.


728x90
반응형
LIST