← 프로젝트 프로젝트 케이스 스터디

Drive Seoul

공개 자료의 서로 다른 서울을 주행 가능한 한 세계로 합치고, 느린 회선에서도 여러 사람이 함께 달리게 만든 브라우저 게임입니다

광화문 일대 상공에서 본 Drive Seoul. 건물의 높낮이와 넓은 교차로의 차선·횡단보도, 가로수와 멀리 보이는 지형이 함께 나타납니다.
역할
개인 프로젝트 — 데이터 파이프라인·게임 시스템·클라이언트와 서버 개발
기간
2026년 개인 프로젝트 (진행 중)
스택
TypeScript · Three.js · Node.js/WebSocket · GDAL/PROJ · Cloudflare Pages/R2

서울을 사실적으로 보이게 하는 데 필요한 것은 건물 외벽의 사진보다 길이 어디서 이어지고, 몇 차로가 어느 방향으로 흐르며, 다리가 지면보다 얼마나 높은지였습니다. 그래서 색과 간판을 덜어낸 3D 서울을 만들되 도로·건물·지형의 형태와 배치를 공개 자료에 맞추기로 했습니다. 처음부터 플레이어들이 같은 세계에 접속해 택시를 운전하고 경주와 구간 기록을 남기는 게임으로 설계했습니다.

서로 다른 자료에서 한 도시로

원천마다 잘하는 일이 달랐습니다. OpenStreetMap(OSM)은 골목까지 이어지는 도로망의 뼈대가 됩니다. 국가 표준노드링크에서는 방향별 차로 수와 제한속도, 교량·고가·터널 구분을 가져와 OSM 구간의 위치와 진행 방향을 대조해 붙였습니다. 서울 열린데이터광장의 교통안전시설물에서는 차선의 실선·점선, 정지선, 횡단보도 면과 신호등 부착대의 위치를 받았습니다. 도로면은 중심선에 폭을 주어 만들되 공개 차선의 단면으로 폭을 보정하고, 차가 올라갈 노면은 교차점과 연결로가 끊기지 않도록 종단 높이를 따로 계산했습니다. 도로망을 왜 이렇게 합쳤는지는 연결성 측정과 함께 기록했습니다.

건물은 OSM 외곽선만으로 서울을 채울 수 없었습니다. 서울시 건축물대장 표제부의 층수·높이를 주소와 건물 위치로 연결하고, OSM에 빠진 외곽선은 Overture Maps 건물로 보강했습니다. 높이를 확인할 수 없는 건물에만 주변의 건물 높이 격자와 용도·면적을 이용한 추정을 남깁니다. 이 과정에서 좁은 골목의 주택까지 인근 고층 건물의 평균 높이를 받아 벽처럼 솟는 문제가 드러났고, 저층 주거지의 건물 높이를 다시 맞췄습니다. 공개 자료 보강 작업에서는 건물 외곽선이 약 19만 동에서 41만 동으로 늘고, 떼어 둔 높이 표본의 중앙 오차가 5.6%가 된 판을 확인했습니다. 로그인이 필요한 GIS건물통합정보를 받았다고 가정하지 않고, 실제로 받을 수 있는 자료로 세계를 지었습니다.

지형도 같은 원칙을 따릅니다. 서울시가 공개한 등고선과 표고점은 원래 좌표계에서 게임의 EPSG:5186 좌표로 바꾸고 4m 격자의 맨땅 높이로 보간했습니다. 도로는 지형을 그대로 따라 출렁이게 하지 않고 완만한 주행 높이를 정한 뒤 주변 땅을 도로에 맞춰 깎거나 채웁니다. 이렇게 만든 도시는 512m 청크로 나눠 운전 방향과 거리에 따라 필요한 것부터 받습니다. 원천·판·해시를 청크 생성 과정에 남기고, OSM 파생 자료는 다른 공개 자료와 별도 청크로 두어 출처와 이용 조건을 구분했습니다.

느린 회선에서도 함께 달리기

첫 공개 경로에서는 한국에서 접속한 WebSocket이 로스앤젤레스의 Cloudflare 엣지를 거쳐 다시 서울 서버로 돌아왔습니다. 밖에서 본 왕복 지연은 500ms대까지 올라갔습니다. 정적 파일과 세계 청크를 받는 경로와 실시간 연결을 나눠 살피고, 공개 입구를 서울 엣지에 붙는 Pages 주소로 옮긴 뒤 측정한 게임 왕복 시간은 32~37ms였습니다. 경로 조사와 측정은 서버 코드를 바꾸기 전에 먼저 네트워크가 어디로 도는지 확인해야 했던 사례입니다.

경로를 줄여도 회선의 흔들림과 대역폭 한계는 남습니다. 서버는 차를 모두에게 매번 보내지 않습니다. 플레이어 주변의 512m 셀과 진행 방향 앞쪽을 관심 영역으로 삼고, 가까운 차와 먼 차의 송신 주기를 달리합니다. 클라이언트가 받은 스냅숏을 확인해 주면 서버는 확인 지연과 소켓에 쌓인 바이트로 혼잡을 판단합니다. 줄이 밀릴수록 스냅숏 빈도와 먼 교통의 갱신을 낮춰 오래된 상태가 계속 쌓이지 않게 합니다. 전송 상태는 작게 양자화하고 WebSocket 메시지는 압축합니다. 브라우저도 왕복 지연이 높으면 입력 전송 간격을 조절하고 최근 입력을 겹쳐 보내며, 다른 차의 보간 지연을 실제 도착 간격에 맞춰 100~800ms 사이에서 조정합니다. 느린 회선 개선 이슈에 회선 흉내의 조건과 전후 결과를 남겼습니다.

교통량이 늘자 멀리 있는 NPC의 좌표를 계속 실어 보내는 비용이 더 커졌습니다. 지금은 차로와 차로 위 거리·속도·가속도를 키프레임으로 보내고, 예측과 실제 위치가 벌어지거나 차로가 바뀔 때 갱신합니다. 클라이언트는 같은 차로 그래프로 그 사이의 움직임을 만듭니다. 더 먼 교통은 서버와 클라이언트가 같은 규칙으로 생성하는 흐름 교통으로 그려 네트워크 전송을 아낍니다. 가까워져 충돌할 수 있는 범위에 들어오면 실제 NPC로 이어받습니다. 키프레임 송신과 흐름 교통은 각각 측정과 화면 검증을 거쳐 운영에 들어갔습니다.

내 차가 먼 서버의 확정을 기다릴 때는 서버만 아는 충돌이 늦게 도착해 차가 크게 튀었습니다. 지금은 내 차를 브라우저에서 바로 움직이고 입력과 함께 상태를 보냅니다. 서버는 속도·이동 거리·충돌·상태 전환의 타당성을 확인하고, 벗어난 상태에는 되돌림을 보냅니다. 판정과 기록은 서버의 재검증을 거칩니다. 이 선택은 느린 회선에서 운전 조작이 즉시 반응하게 하지만 클라이언트를 어느 범위까지 믿을지 명시해야 합니다. 권위 변경 이슈에 허용 범위와 시험을 적었습니다.

이 수치들은 한 장의 회선 흉내와 당시 공개 경로에서 잰 결과입니다. RTT 500ms·지터 120ms·손실 1% 조건의 시험에서는 남의 차 끊김이 초당 240회에서 10.5회로, 한 사람의 수신량이 10.4KB/s에서 4.8KB/s로 줄었습니다(원인 분리와 전후 측정). 그 뒤에도 건물의 빈틈과 입체교차, 실제 기기의 긴 프레임과 회선 차이를 공개 이슈로 계속 고치고 있습니다. 서울의 재현과 멀티플레이 모두, 보이는 화면 뒤의 원천과 측정값을 되짚을 수 있게 하는 것이 이 프로젝트의 작업 방식입니다.

← 프로젝트로 돌아가기