대형 언어 모델(LLM)을 활용한 여행 계획 생성은 대중적인 프로젝트 주제입니다. 하지만 단순히 텍스트 생성에만 의존할 경우, 존재하지 않는 식당을 추천하거나 이동 거리를 고려하지 않는 등의 '할루시네이션(환각)' 문제가 발생합니다. 이 글에서는 텐센트 지도(Tencent Map)의 실제 POI(Point of Interest) 데이터와 DeepSeek 모델을 결합하여, 실제 실행 가능한 경로와 장소를 제안하는 지능형 여행 플래너의 구현 과정을 설명합니다.
1. 시스템 아키텍처 및 설계 개념
핵심 아이디어는 AI가 여행 계획을 짜기 전, 지도 API를 통해 목적지의 실시간 데이터를 먼저 검색하고 이 정보를 프롬프트에 주입하는 것입니다. 전체 워크플로우는 다음과 같습니다.
- 지오코딩: 사용자 목적지를 좌표(위도, 경도)로 변환.
- 키워드 추출: DeepSeek을 통해 사용자 요구사항에서 검색 키워드(명소, 맛집, 호텔 등)를 도출.
- 데이터 수집: 텐센트 지도 WebService API로 실제 POI 정보 및 장소 간 경로/소요 시간 획득.
- 프롬프트 주입: 수집된 실제 데이터를 AI 모델의 컨텍스트로 전달.
- 스트리밍 응답: SSE(Server-Sent Events)를 통해 지도 마커 데이터와 생성된 텍스트를 실시간으로 클라이언트에 전송.
2. 백엔드 구현: 데이터 엔진 및 AI 통합
FastAPI를 사용하여 비동기 통신을 지원하고, 텐센트 지도 API 호출과 AI 추론을 결합합니다.
2.1 텐센트 지도 API 연동 모듈
지도 서비스의 핵심 기능을 MapProvider 클래스로 캡슐화합니다. API 호출 시 예외 처리를 강화하여 특정 API 실패가 전체 프로세스를 중단시키지 않도록 설계합니다.
import httpx
import os
class MapProvider:
def __init__(self):
self.api_key = os.getenv("TENCENT_MAP_KEY")
self.base_url = "https://apis.map.qq.com/ws"
async def fetch_coordinates(self, address: str):
"""주소를 좌표로 변환"""
url = f"{self.base_url}/geocoder/v1/"
params = {"address": address, "key": self.api_key}
async with httpx.AsyncClient() as client:
resp = await client.get(url, params=params)
result = resp.json()
if result.get("status") == 0:
return result["result"]["location"]
return None
async def search_nearby_places(self, query: str, boundary: str):
"""주변 POI 검색"""
url = f"{self.base_url}/place/v1/search"
params = {
"keyword": query,
"boundary": boundary,
"key": self.api_key,
"page_size": 5
}
async with httpx.AsyncClient() as client:
resp = await client.get(url, params=params)
data = resp.json()
return data.get("data", []) if data.get("status") == 0 else []
2.2 DeepSeek을 활용한 검색 전략 수립
사용자의 자연어 입력을 지도 검색에 적합한 구조화된 데이터로 변환합니다. 이때 낮은 temperature(0.3) 설정을 사용하여 JSON 출력의 안정성을 높입니다.
async def generate_search_keywords(user_input: str):
prompt = f"""
사용자의 여행 요청: "{user_input}"
위 요청을 바탕으로 지도 검색을 위한 키워드를 JSON 배열로 추출하세요.
형식: [{"keyword": "명칭", "category": "attraction|food|hotel", "limit": 5}]
"""
# DeepSeek API 호출 로직 (생략)
# 결과가 JSON 형식이 아닐 경우를 대비한 파싱 및 샌드박스 처리 필수
2.3 리얼 데이터 기반 프롬프트 구성
지도 API에서 가져온 실제 정보를 AI가 읽기 쉬운 텍스트 형식으로 변환하여 최종 프롬프트에 결합합니다.
def build_context_with_poi(poi_list, routes):
context = "### 검색된 실제 장소 정보\n"
for poi in poi_list:
context += f"- {poi['title']}: {poi['address']} (카테고리: {poi['category']})\n"
context += "\n### 이동 거리 참고\n"
for route in routes:
dist = route['distance'] / 1000
time = route['duration'] / 60
context += f"- {route['origin']} -> {route['dest']}: 약 {dist:.1f}km, {time:.0f}분 소요\n"
return context
3. 프론트엔드: 지도 시각화 및 인터랙션
텐센트 지도 JS API GL 버전을 사용하여 동적인 마커 표시와 경로 렌더링을 구현합니다.
3.1 지도 레이어 초기화
Vue 3 환경에서 지도를 초기화하고, 명소, 식당, 호텔별로 다른 스타일의 마커를 설정합니다.
const initMapInstance = (center) => {
const map = new TMap.Map('map-container', {
center: new TMap.LatLng(center.lat, center.lng),
zoom: 12
});
const markerLayer = new TMap.MultiMarker({
map: map,
styles: {
'attraction': new TMap.MarkerStyle({ width: 25, height: 35, src: '/icons/red-pin.png' }),
'food': new TMap.MarkerStyle({ width: 25, height: 35, src: '/icons/orange-pin.png' }),
'hotel': new TMap.MarkerStyle({ width: 25, height: 35, src: '/icons/blue-pin.png' })
}
});
return { map, markerLayer };
};
3.2 경로(Polyline) 렌더링 주의사항
텐센트 경로 계획 API가 반환하는 좌표 형식은 [lng, lat] 순서이지만, JS SDK의 LatLng 객체는 (lat, lng) 순서를 요구합니다. 데이터 처리 시 이 순서를 뒤집어주어야 경로가 정상적으로 표시됩니다.
function drawRoutes(apiRoutes) {
const geometries = apiRoutes.map(route => ({
paths: route.polyline.map(p => new TMap.LatLng(p[1], p[0])), // 순서 변환
styleId: 'route-style'
}));
polylineLayer.setGeometries(geometries);
}
4. 실전 개발 팁 및 트러블슈팅
- API 할당량 관리: 텐센트 지도 무료 티어는 일일 호출 제한이 있습니다. POI 검색 시
asyncio.gather를 사용하여 병렬 처리를 하되, 불필요한 중복 호출을 줄이기 위해 검색 결과를 캐싱하는 것이 좋습니다. - SSE 통신 안정성: 대량의 마커 데이터를 SSE로 보낼 때 JSON 데이터가 잘리지 않도록
ensure_ascii=False설정과 적절한 버퍼 처리가 필요합니다. - 지오코딩 실패 대응: 사용자가 모호한 도시명을 입력할 경우 지오코딩이 실패할 수 있습니다. 이때는 기본 좌표를 설정하거나 사용자에게 위치 재확인을 요청하는 예외 처리를 추가해야 합니다.
- LLM JSON 파싱: DeepSeek 모델이 응답에 마크다운 코드 블록(```json ... ```)을 포함하는 경우가 많습니다. 백엔드에서 정규식이나 문자열 처리를 통해 순수 JSON만 추출하는 유틸리티 함수가 필수적입니다.
이와 같은 방식을 통해 제작된 여행 플래너는 AI의 창의성과 지도 데이터의 정확성을 동시에 확보할 수 있습니다. 사용자는 텍스트로 된 일정뿐만 아니라 지도를 통해 동선을 즉시 확인할 수 있어 훨씬 더 신뢰도 높은 경험을 얻게 됩니다.