카카오 모빌리티 API 사용하기
카카오 지도 API를 활용해서 구현한 코드나 초기 설정 방법? 같은 건 많이 찾아볼 수 있는데,
카카오 모빌리티 API 에 관한 포스팅은 별로 없어서 조금 어려웠다.
사용자가 현재 위치에서 중간에 경유지를 한번 거친 후 목적지에 간다고 가정할 때의 예상 소요 시간과 최단거리 등을 나타내는 기능을 구현했다.
PoC 형태로 실현여부를 판단하기 위한 작업이라 구체적인 기능이나 스토리보드는 딱히 없었다...😣
(해야하니까 하는 것... 난 로봇이다 🤖)
아무튼 카카오 모빌리티 API에서 어떻게 데이터를 사용하고 컴포넌트, 함수 등을 간단하게 정리해보면서 복습해보기로 했다.
엄.. 이렇게 구현하는게 맞나 싶지만..! 잘못한 건 따로 공부하면서 수정해보기로! 😗
1. kakaoRoute 컴포넌트
fetchRoute 함수는 카카오모빌리티 API의 v1/directions 엔드포인트를 사용하여 경로 탐색을 수행하는 기능을 담당한다.
DISTANCE)와 최단 시간(TIME) 중 사용자가 선택 가능하도록 구현함
const KAKAO_REST_API_KEY = import.meta.env.VITE_KAKAO_REST_API_KEY;
const WAYPOINTS_URL = "https://apis-navi.kakaomobility.com/v1/directions";
카카오 모빌리티 API 사이트를 참고해서 “다중 경유지 길찾기” 엔드포인드를 활용했다.
.env 환경변수에서 카카오 REST API 키를 가져오고WAYPOINTS_URL은 카카오모빌리티 API의 경로 탐색 엔드포인트를 변수로 담았다.const waypointsParam = Array.isArray(waypoints)
? waypoints.map((wp) => `${wp.lng},${wp.lat}`).join("|")
: "";
waypoints가 배열이면 "경유지1_LNG,경유지1_LAT|경유지2_LNG,경유지2_LAT" 형식의 문자열로 변환한다.LNG,LAT 순서이므로 변환이 필요하다.const response = await axios.get<RouteResponse>(WAYPOINTS_URL, {
headers: {
Authorization: `KakaoAK ${KAKAO_REST_API_KEY}`
},
params: {
origin: `${start.lng}, ${start.lat}`,
destination: `${destination.lng}, ${destination.lat}`,
waypoints: waypointsParam,
priority
}
});
API 요청하기 위해 axios 를 사용했다.
Authorization: KakaoAK {REST_API_KEY} → 카카오 API 사용을 위해 필수로 적어야한다.origin: 출발지 (LNG, LAT)destination: 목적지 (LNG, LAT)waypoints: 경유지 (LNG, LAT | LNG, LAT ...)priority: "TIME" 또는 "DISTANCE" (사용자가 select option을 선택할 때 동적으로 바뀌게 설정함)
성공적으로 불러온 데이터를 보면 이렇게 구성되어있다. 이제 이를 활용해서 기능 구현을 해보려고 한다.
if (!response.data.routes.length) {
console.error("❌ 경로 탐색 실패: 경로가 없습니다.");
return null;
}
응답 데이터의 routes 값이 비어있는 경우, 경로가 없다는 뜻이므로 null 값을 반환하도록 한다.
✅ sections 배열 값 확인

✅ summary 배열 값 확인

const { summary, sections } = response.data.routes[0];
const { duration, distance } = summary;
summary 객체
duration: 예상 소요 시간 (초 단위)distance: 총 이동 거리 (미터 단위)sections 객체
세부 경로 데이터를 포함하는 배열
const path = sections.flatMap((section) =>
section.roads.flatMap((road) => {
const coords: kakao.maps.LatLng[] = [];
for (let i = 0; i < road.vertexes.length; i += 2) {
coords.push(new window.kakao.maps.LatLng(road.vertexes[i + 1], road.vertexes[i]));
}
return coords;
})
);
vertexes 배열은 [lng1, lat1, lng2, lat2, lng3, lat3, ...] 형태로 제공되기 때문에 카카오 지도 객체와 함께 사용하려면 이 순서를 바꿔주어야한다. (성가심 레전드 🤬)
i를 2씩 증가시키면서 lat, lng 좌표를 생성new window.kakao.maps.LatLng)로 변환path)로 저장
→ 이 경로 값들을 이어서 polyline 을 그려주는 형태이다!
✅ 변환 결과 예시
[
new kakao.maps.LatLng(37.5665, 126.9780),
new kakao.maps.LatLng(37.5670, 126.9775),
new kakao.maps.LatLng(37.5705, 126.9770)
]
✅ 최종 반환값
return { summary, path, duration, distance };
summary: 출발지, 목적지, 경유지 정보 포함
path: 최적 경로 좌표 배열
duration: 예상 소요 시간 (초)
distance: 총 이동 거리 (미터)
2. RoutePath 컴포넌트
RoutePath 컴포넌트는 부모 컴포넌트에서 지도 객체와 좌표 정보를 받아 사용하고 있으며, 카카오 모빌리티 API를 활용해 지도에 경로를 그리는 역할을 담당한다.
경로를 Polyline(선)으로 표시하고, 예상 소요 시간과 거리를 화면에 출력하는 기능을 포함하고 있다.
RoutePath의 역할fetchRoute를 호출하여 출발지-경유지-목적지의 경로 데이터를 가져옴✅ Props 정의
interface RoutePathProps {
map: kakao.maps.Map | null; // 지도 객체
start: { lat: number; lng: number }; // 출발지 좌표
waypoints: { lat: number; lng: number }[]; // 경유지 리스트 배열 형태 저장
destination: { lat: number; lng: number }; // 목적지 좌표
priority: "TIME" | "DISTANCE"; // 경로 탐색 기준 ("TIME": 최단 시간, "DISTANCE": 최단 거리)
}
✅ State / Ref 정의
const [, setRoutePath] = useState<kakao.maps.LatLng[]>([]); // routePath 미사용으로 제거
const [routeInfo, setRouteInfo] = useState<{ duration: number; distance: number } | null>(null);
const polylineRef = useRef<kakao.maps.Polyline | null>(null);
routeInfo: 예상 소요 시간 & 이동 거리 저장polylineRef: 현재 지도에 표시된 경로선(Polyline) 객체 저장setRoutePath: 경로 좌표 저장용, (현재 routePath 변수는 사용되지 않으므로 제거함)→ PolyLine(경로선)의 참조값을 useRef로 저장해, 기존 경로를 제거하는데 활용한다.
useEffect(() => {
if (!map || !start || !destination || waypoints.length === 0) return;
fetchRoute(start, waypoints, destination, priority).then((response) => {
if (!response) return;
setRouteInfo({ duration: response.duration, distance: response.distance });
fetchRoute 함수 호출 : 최적 경로, 예상 거리 및 소요 시간 데이터를 가져옴routeInfo에 저장하여 UI에 반영// 기존 Polyline이 있으면 지도에서 제거
if (polylineRef.current) {
polylineRef.current.setMap(null);
}
// 새로운 Polyline 생성 및 추가
const polyline = new window.kakao.maps.Polyline({
path: response.path, // 가져온 경로 좌표
strokeWeight: 5, // 선 두께
strokeColor: "#6A31F6", // 선 색상 (보라색)
strokeOpacity: 0.8, // 투명도
strokeStyle: "solid" // 선 스타일
});
polyline.setMap(map); // 지도에 Polyline 추가
polylineRef.current = polyline; // Polyline 객체 저장
Polyline을 생성하여 경로를 지도에 표시 (카카오 지도 선 표시하기 API 사용)path 값으로 fetchRoute에서 가져온 최적 경로를 사용polylineRef.current = polyline; : Polyline 객체를 저장하여 이후 삭제 가능✅ 결과화면

return () => {
if (polylineRef.current) {
polylineRef.current.setMap(null);
polylineRef.current = null;
}
};
{routeInfo && (
<div className="absolute left-52 top-4 z-50 rounded bg-white p-2 text-sm font-semibold shadow dark:text-black">
⏳ 예상 소요 시간: {Math.round(routeInfo.duration / 60)}분
<br />
📏 예상 이동 거리: {(routeInfo.distance / 1000).toFixed(1)}km
</div>
)}
Math.round(duration / 60)) 단위로 변환distance / 1000) 단위로 변환하여 소수점 1자리 표시absolute 클래스로 지도의 특정 위치(왼쪽 상단)에 정보 표시
3. Map 컴포넌트
⭐️ 경로 탐색과 관련된 로직만 정리
Map 컴포넌트는 카카오 지도 API를 활용하여 지도를 렌더링하고, 경로 탐색 기능을 제공하는 핵심 컴포넌트이다.
RoutePath : 경로 데이터를 가져와 지도에 Polyline을 그리는 역할Map 컴포넌트에서는 RoutePath를 언제, 어떤 데이터로 호출할지 결정한다.fetchRoute는 RoutePath 내부에서 API를 호출하여 경로 데이터를 가져오며, Map 컴포넌트는 이를 제어하는 역할을 한다.// 경로 탐색 버튼 클릭 시 동작 (경로 표시/숨김)
const handleRouteToggle = () => {
if (isRouteVisible) {
setIsRouteVisible(false);
setRouteData(null);
} else {
setRouteData({
start: { lat: location.latitude, lng: location.longitude }, // 출발지 = 현재 위치
waypoints: [
{ lat: 37.5154133, lng: 126.9071288 },
{ lat: 37.52626250000001, lng: 126.8959528 }
],
destination: { lat: 37.521638, lng: 126.9049865 } // 목적지
});
setIsRouteVisible(true);
}
};
handleRouteToggle() 에서 setIsRouteVisible(true)를 설정하면 RoutePath가 표시된다.
isRouteVisible이 false면 경로를 숨김 (setRouteData(null) → RoutePath 제거)출발지는 사용자의 현재 위치를 불러오는 함수를 사용
경유지, 목적지는 위도, 경도 값을 하드코딩으로 적용했다. (PoC 기능 구현 중)
priority 값 ("TIME" 또는 "DISTANCE")를 설정 가능
{map && isRouteVisible && routeData && (
<RoutePath
map={map}
start={routeData.start}
waypoints={routeData.waypoints}
destination={routeData.destination}
priority={priority}
/>
)}
isRouteVisible이 true이고 routeData가 존재하면 RoutePath 컴포넌트를 렌더링한다.fetchRoute를 호출하여 경로 데이터를 가져오고, Polyline을 지도에 그린다.RoutePath 컴포넌트에 priority 값을 전달하여 API 요청 시 적용된다.
{/* 최단 시간 vs 최단 거리 선택 */}
<div className="absolute left-4 top-4 z-50 rounded bg-white p-2 shadow dark:text-black">
<label className="text-sm font-semibold">경로 기준:</label>
<select
className="ml-2 rounded border p-1"
value={priority}
onChange={(e) => setPriority(e.target.value as "TIME" | "DISTANCE")}
>
<option value="TIME">최단 시간</option>
<option value="DISTANCE">최단 거리</option>
</select>
</div>
priority)이 RoutePath에 props로 전달fetchRoute 호출 시 "TIME" or "DISTANCE"로 요청하여 동적으로 바뀌도록 함