-
[Navigation] #3 OSRM 라우팅 서버 구축 실습 (Docker, 지도 데이터 전처리, Route API)Navigation 2026. 3. 9. 22:47
이번 글에서는 직접 OSRM 서버를 실행해 보면서
지도 데이터 전처리 과정과 라우팅 서버 구동 과정을 실습해 보겠다.

1. OSRM Docker 이미지 받기
이번 실습에서는 OSRM 공식 Docker 이미지를 이용해 라우팅 환경을 구성해보겠다.
공식 이미지 Pull
docker pull ghcr.io/project-osrm/osrm-backend이미지 확인
docker images | findstr osrm이제 OSRM 실행에 필요한 Docker 이미지를 준비했으므로, 다음 단계에서는 라우팅에 사용할 지도 데이터를 준비해보자.
2. 지도 데이터 준비
OSRM은 경로 탐색을 위해 .osm.pbf 형식 OpenStreetMap(OSM) 기반의 지도 데이터를 사용한다.
이번 실습에서는 Geofabrik에서 제공하는 한국 지도 데이터를 사용한다.
다운로드 사이트 : Geofabrik (https://download.geofabrik.de/asia/south-korea.html)
예시 데이터
south-korea-latest.osm.pbf예시 경로
C:\Users\jihye\nav\osrm\south-korea-260123.osm.pbf지도 데이터를 준비했으므로, 이제 OSRM이 라우팅을 수행할 수 있도록 전처리 과정을 진행해 보자.
3. 전처리 (Preprocessing)
OSRM은 .osm.pbf 파일을 그대로 사용해 경로 탐색을 수행하지 않는다.
라우팅을 위해서는 여러 단계의 전처리 과정을 거쳐 전용 데이터 파일을 생성해야 한다.이번 실습에서는 OSRM이 지원하는 2가지 알고리즘(MLD, CH) 중 MLD(Multi-Level Dijkstra) 을 사용한다.
전처리 단계는 크게 3단계다:
- extract : OSM 데이터를 라우팅 그래프로 변환
- partition : 그래프를 여러 영역(cell)으로 분할
- customize : 경로 계산을 위한 shortcut weight 계산
3-1. extract (그래프 생성 단계)
OSM 원본 데이터를 OSRM이 사용할 수 있는 도로 네트워크 그래프로 변환하는 단계다.
핵심 작업은 다음과 같다.
- Lua 프로파일(car.lua, bike.lua 등)을 기반으로 도로 필터링
- 주행 가능 여부 판별
- 도로 속도, 가중치 계산
- 내부 그래프 구조 생성
즉, OSM 원본 지도 → 차량이 실제로 달릴 수 있는 도로 그래프 생성
car 프로파일로 extract 실행
docker run -t -v C:\Users\jihye\nav\osrm:/data ghcr.io/project-osrm/osrm-backend osrm-extract -p /opt/car.lua /data/south-korea-260123.osm.pbf실행이 완료되면 .osrm 관련 파일들이 생성된다.
3-2. partition (MLD 전용 단계)
이 단계는 MLD 알고리즘을 위한 그래프 분할 단계다.
그래프를 여러 영역(Cell) 단위로 나누어 쿼리 속도를 높이기 위한 준비 작업을 한다.
docker run -t -v C:\Users\jihye\nav\osrm:/data ghcr.io/project-osrm/osrm-backend osrm-partition /data/south-korea-260123.osrm3-3. customize (가중치 계산 단계)
각 영역 간의 최적 경로 정보를 가중치(weight) 기반으로 계산하는 단계다.
docker run -t -v C:\Users\jihye\nav\osrm:/data ghcr.io/project-osrm/osrm-backend osrm-customize /data/south-korea-260123.osrm전처리가 완료되면 OSRM이 사용할 라우팅 데이터가 준비된다. 이제 OSRM 서버를 실행하여 실제 API 요청을 처리할 수 있는 상태로 만들어 보자.
4. OSRM 서버 실행
전처리된 데이터를 기반으로 OSRM 서버를 실행하면 HTTP API를 통해 경로 탐색 요청을 처리할 수 있다.
Docker 컨테이너를 실행하여 OSRM 서버를 시작해 보자.docker run -d -p 5000:5000 -v C:\Users\jihye\nav\osrm:/data ghcr.io/project-osrm/osrm-backend osrm-routed --algorithm mld /data/south-korea-260123.osrm서버 확인
http://localhost:50005. OSRM API 요청
OSRM 서버가 정상적으로 실행되면 HTTP API를 통해 경로 탐색 요청을 보낼 수 있다.
OSRM API는 REST 형태로 제공되며, 기본 요청 구조는 다음과 같다./{service}/v1/{profile}/{lon,lat;lon,lat}?options이 요청 구조는 어떤 서비스(service)를 사용할지, 어떤 교통 수단(profile)을 기준으로 어떤 좌표 간의 경로를 계산할지를 정의한다.
각 구성 요소가 의미하는 바를 살펴보자.
- {service}
요청할 서비스 종류(service)를 의미한다.
OSRM은 여러 API 서비스를 지원한다.
[서비스 예시]- route (두 지점 간 경로 계산)
- table (거리/시간 행렬 계산)
- nearest (가장 가까운 도로 탐색)
- match (GPS -> 도로 매칭)
- trip (여러 지점 방문 최적 순서)
- /v1
API 버전(version)을 의미한다.
현재 OSRM Route API의 기본 버전은 v1이다.
버전을 URL에 포함하는 이유는 향후 API 구조가 변경되더라도 이전 버전과의 호환성을 유지하기 위해서이다. - {profile}
경로 계산에 사용할 라우팅 프로파일을 의미한다.
프로파일은 어떤 교통 수단을 기준으로 경로를 계산할지를 결정한다.
[프로파일 예시]프로파일은 전처리 단계에서 사용한 Lua 프로파일(car.lua, foot.lua, bike.lua)과 연결된다.
- driving (자동차 경로)
- walking (보행자 경로)
- cycling (자전거 경로)
- {lon,lat;lon,lat}
경로를 계산랑 좌표 목록을 의미한다
좌표는 경도(lon), 위도(lat) 형식으로 입력하며, 좌표가 여러 개일 경우 ;로 구분한다.
좌표가 3개 이상이면 중간 경유지(waypoint)가 된다. 만약 A;B;C 로 입력하면 A-> B-> C 경로라는 의미이다. - ?options
경로 계산 결과를 조정하는 쿼리 파라미터(query parameters)다.
[옵션 예시]- overview (geometry 반환 수준)
- steps (단계별 경로 정보)
- overview (세부 구간 정보)
- alternatives (대안 경로)
- geometries (geometry 형식)
[경로 요청 예시]
다음은 OSRM Route API를 이용해 두 지점 사이의 경로를 요청하는 예시이다.
http://localhost:5000/route/v1/driving/126.9780,37.5665;127.0276,37.4979?overview=full&steps=true이 요청의 각 구성 요소는 다음과 같은 의미를 가진다.
- route : 겅로 계산 서비스
- v1 : API 버전
- driving : 자동차 기준 경로
- 126.9780,37.5665 : 출발지 좌표
- 127.0276,37.4979 : 도착지 좌표
- overview=full : 전체 경로 geometry 반환
- steps = true : 단계별 경로 안내 정보 모음
5-1. OSRM Route API 예제 실습
예제 실습으로 서울 시청 근처에서 강남역 근처까지의 경로를 조회해 보겠다.
다음과 같은 API 요청을 통해 경로를 확인할 수 있다.
http://localhost:5000/route/v1/driving/126.9780,37.5665;127.0276,37.4979좌표의 의미는 다음과 같다.
출발지 : 126.9780, 37.5665 (서울시청 인근)
도착지 : 127.0276, 37.4979 (강남역 인근)
이 요청을 실행하면 OSRM은 두 지점을 연결하는 최적의 자동차 경로와 예상 이동 시간, 거리 정보를 JSON 형태로 반환한다.
5-2. Route API 응답(JSON) 구조 이해
Route API 요청을 실행하면 OSRM은 경로 정보를 JSON 형태로 반환한다.
예시 응답은 다음과 같다.
{ "code": "Ok", "routes": [ { "legs": [ { "steps": [], "weight": 598.5, "summary": "", "duration": 598.5, "distance": 9950.1 } ], "weight_name": "routability", "geometry": "yhhdFmk_fW@tCcLk@|Ea~@zR@bZuIvHiOtTmE`oAwq@lKiOzWeHhg@m]~t@ap@tv@kOh{A{k@", "weight": 598.5, "duration": 598.5, "distance": 9950.1 } ], "waypoints": [ { "hint": "WMQ6gP___38KAAAAIQAAAAAAAAAVAAAAR7dxQcqECkIAAAAAEG7-QQoAAAAhAAAAAAAAABUAAAAsBQAAyYeRBx06PQLQh5EHJDg9AgAA3xAAAAAA", "location": [126.977993, 37.567005], "name": "세종대로20길", "distance": 56.0525781 }, { "hint": "ALcAgP___38EAAAABQAAAAAAAAAFAAAAFgPhQIMWsz8AAAAAIGokQQQAAAAFAAAAAAAAAAUAAAAsBQAAlkmSBx8sPAKQSZIHLCw8AgAAbwQAAAAA", "location": [127.027606, 37.497887], "name": "서초대로", "distance": 1.537341475 } ] }이 응답은 크게 다음 4가지 주요 구조로 이루어져 있다.
- code
- routes
- waypoints
- geometry
1. code
"code": "Ok"요청이 정상적으로 처리되었는지 나타내는 상태 코드이다.
대표 값 값 의미 Ok 요청 성공 InvalidQuery 요청 파라미터 오류 NoRoute 경로를 찾을 수 없음 NoSegment 좌표가 도로에 매칭되지 않음 2. routes
"routes": [ { "legs": [ { "steps": [], "weight": 598.5, "summary": "", "duration": 598.5, "distance": 9950.1 } ], "weight_name": "routability", "geometry": "yhhdFmk_fW@tCcLk@|Ea~@zR@bZuIvHiOtTmE`oAwq@lKiOzWeHhg@m]~t@ap@tv@kOh{A{k@", "weight": 598.5, "duration": 598.5, "distance": 9950.1 } ]실제로 계산된 경로 결과 목록이다.
보통 하나의 경로만 반환되지만, alternatives=true 옵션을 사용하면 여러 개의 대안 경로가 반환될 수 있다.
routes 내부에는 다음과 같은 주요 정보가 포함된다.
정보 필드 의미 distance 총 이동 거리 duration 예상 이동 시간 geometry 경로 좌표 legs 구간 정보 3. distance / duration
"distance": 9950.1 "duration": 598.5경로의 총 거리와 예상 이동 시간이다.
단위sms distance - meters, duration - seconds로
예시의 distance는 9950m (약 9.9km), duration는 598s (약 9분 58초)이다.
4. legs
"legs": [ { "steps": [], "weight": 598.5, "summary": "", "duration": 598.5, "distance": 9950.1 } ]경로를 여러 구간으로 나눈 정보이다.
예를 들어 A → B → C 와 같은 경로라면 다음과 같이 나뉜다.
Leg 1 : A → B
Leg 2 : B → C현재 예제는 출발지와 도착지만 존재하므로 leg가 하나만 존재한다.
5. steps
"steps": []
경로의 턴 단위 네비게이션 정보이다.예를 들어 좌회전, 직진, 우회전 같은 안내가 포함된다.
기본 요청에서는 steps=false이기 때문에 빈 배열이 반환된다.
steps=true 옵션을 사용하면 상세 안내가 포함된다.
6. geometry
"geometry": "yhhdFmk_fW@tCcLk@|Ea~@zR@bZuIvHiOtTmE`oAwq@lKiOzWeHhg@m]~t@ap@tv@kOh{A{k@"경로의 좌표 데이터를 인코딩한 polyline 문자열이다.
이 값은 지도 라이브러리에서 경로를 그릴 때 사용된다.
대표적으로 Leaflet, Mapbox,OpenLayers 과 같은 라이브러리에서 사용된다.
geometries=geojson 옵션을 사용하면 더 읽기 쉬운 형태로 받을 수 있다.
7. waypoints
"waypoints": [ { "hint": "WMQ6gP___38KAAAAIQAAAAAAAAAVAAAAR7dxQcqECkIAAAAAEG7-QQoAAAAhAAAAAAAAABUAAAAsBQAAyYeRBx06PQLQh5EHJDg9AgAA3xAAAAAA", "location": [126.977993, 37.567005], "name": "세종대로20길", "distance": 56.0525781 }, { "hint": "ALcAgP___38EAAAABQAAAAAAAAAFAAAAFgPhQIMWsz8AAAAAIGokQQQAAAAFAAAAAAAAAAUAAAAsBQAAlkmSBx8sPAKQSZIHLCw8AgAAbwQAAAAA", "location": [127.027606, 37.497887], "name": "서초대로", "distance": 1.537341475 } ]입력한 좌표가 실제 도로 네트워크에 매칭된 위치를 의미한다.
의미 필드 설명 location 매칭된 도로 좌표 name 도로 이름 distance 입력 좌표와 도로 사이 거리 즉, 사용자가 입력한 좌표 → 실제 도로 위치로 스냅(snap)된 결과라고 보면 된다.
[예시 응답 + 설명]
서울 시청 → 강남역 경로를 계산한 결과이다.
거리는 9.95km, 시간은 약 10분이며 경로는 polyline geometry로 반환된다.
{ "code": "Ok", -> 요청이 정상 처리되었음을 의미 "routes": [ -> 가능한 경로 후보 목록, 기본적으로 가장 좋은 경로 1개가 반환됨 { "legs": [ -> 경로를 구간별로 나눈 것, 예: A → B → C 이면 leg1 : A → B, leg2 : B → C, 지금은 출발->도착만 있어서 leg 1개 { "steps": [], -> turn-by-turn navigation 정보, 지금은 overview=false, steps=false (기본)이라 빈 배열, 만약 ?steps=true을 추가하면 좌회전, 우회전, 직진과 같은 정보가 나옴 "weight": 598.5, -> OSRM의 routing cost, 보통 driving profile에서 weight ≈ duration "summary": "", "duration": 598.5, -> 예상 이동 시간, OSRM은 car.lua의 속도값 기반으로 계산함, 598.5초 (약 9분 58초) "distance": 9950.1 -> 총 이동 거리, 9,950.1 m (약 9.95 km) } ], "weight_name": "routability", -> 경로 계산 기준, OSRM 기본값 routability (운행 가능성 + 시간 기반 cost) "geometry": "yhhdFmk_fW@tCcLk@|Ea~@zR@bZuIvHiOtTmE`oAwq@lKiOzWeHhg@m]~t@ap@tv@kOh{A{k@", -> 경로의 polyline encoded 좌표, OSRM은 Encoded Polyline 형태로 압축함, 이 값을 decode하면 (위도,경도) 경로 좌표 리스트가 됨 이는 지도를 그릴 때 사용함 "weight": 598.5, "duration": 598.5, "distance": 9950.1 } ], "waypoints": [ -> 요청 좌표가 실제 도로에 snap된 위치 { "hint": "WMQ6gP___38KAAAAIQAAAAAAAAAVAAAAR7dxQcqECkIAAAAAEG7-QQoAAAAhAAAAAAAAABUAAAAsBQAAyYeRBx06PQLQh5EHJDg9AgAA3xAAAAAA", "location": [126.977993, 37.567005], -> 스냅된 도로 좌표 "name": "세종대로20길", -> 도로 이름 "distance": 56.0525781 -> 입력 좌표 → 도로까지 거리, 즉 입력 좌표 → 실제 도로 56m 이동 }, { "hint": "ALcAgP___38EAAAABQAAAAAAAAAFAAAAFgPhQIMWsz8AAAAAIGokQQQAAAAFAAAAAAAAAAUAAAAsBQAAlkmSBx8sPAKQSZIHLCw8AgAAbwQAAAAA", "location": [127.027606, 37.497887], "name": "서초대로", "distance": 1.537341475 } ] }이번 글에서는 Docker를 이용해 OSRM을 실행하고
Route API를 통해 경로를 조회하는 방법까지 살펴보았다.
다음 글에서는 OSRM의 다양한 API와 기능들을 실습을 통해 더 자세히 알아보겠다.
'Navigation' 카테고리의 다른 글
[Navigation] #4 route API 분석 (옵션별 결과 비교) (0) 2026.03.22 [Navigation] #2 OSRM을 알아보자 (0) 2026.02.23 [Navigation] #1 경로탐색은 어떻게 하는 것일까? (1) 2026.02.19