
내 라이딩을 투르 드 프랑스 중계처럼 — GPX 기록을 넣으면 감독이 샷을 고르는 영상 생성기
라이딩 앱이 만들어 주는 3D 플라이오버 영상은 카메라 하나가 같은 각도로 코스를 따라갈 뿐이라 금방 지루해진다. 그래서 GPX·FIT 기록을 넣으면 구간마다 헬기·오토바이·측면 카메라를 골라 컷으로 잇고, 오르막 앞에서는 시간을 멈춰 경사 카드를 띄우고, 결승에서는 사진 판독과 순위표까지 내는 「중계 감독」 프로그램 ridecast를 만들었다. 2시간 19분짜리 네 명의 라이딩이 197초 영상이 됐다. 영상을 만드는 동안 AI는 한 번도 부르지 않는다. 처음 판은 너무 빨랐고, 카메라는 산에 파묻혔고, 꼴찌는 결승선을 넘지 못했다. 글 안에서 직접 돌려 볼 수 있다.

라이딩을 마치고 앱을 열면 3D 영상이 하나 만들어져 있다. 위성 사진 위로 지형이 솟고, 내가 달린 선이 그려지며 카메라가 따라간다. 처음 몇 번은 신기하다. 그런데 115 km를 달린 날의 영상을 끝까지 보기가 어려웠다. 카메라가 하나뿐이고 각도가 처음부터 끝까지 같다. 평지 30 km와 땀을 쏟은 오르막 3 km가 똑같은 속도로 지나간다.
투르 드 프랑스 중계는 그렇지 않다. 헬기가 산 전체를 보여 주다가 오토바이 카메라가 선수 얼굴 앞으로 붙는다. 오르막에 들어서기 전에 경사 그래프가 깔리고, 공격이 나오면 화면이 두 선수를 한 번에 잡는다. 정상까지 1 km가 남으면 빨간 삼각 깃발(플람 루주)이 뜬다. 같은 경주라도 누가 어떤 카메라를 언제 고르느냐에 따라 지루한 세 시간이 되기도 하고 손에 땀을 쥐는 세 시간이 되기도 한다.
내 기록에는 그 재료가 다 들어 있다. 1초마다 위치와 고도가 찍혀 있고, 같이 달린 사람들의 기록도 있다. 빠진 건 감독 하나다. 이번 회에는 그 감독을 만들었다.
무엇을 하려 했나
- 라이딩 기록(GPX·FIT·TCX)을 넣으면 그랑투르 중계처럼 연출된 영상이 나오게 한다.
- 카메라는 하나가 아니라 여럿. 구간 성격(긴 직선, 굽은 길, 오르막, 최고 속도, 추월, 결승)에 맞춰 샷을 고르고 컷으로 잇는다.
- 같이 달린 사람이 있으면 경주로 만든다. 순위, 시간차, 선두 그룹과 추격 그룹, 결승 사진 판독, 최종 순위표까지.
- 영상을 만드는 단계에서는 AI를 부르지 않는다. AI는 프로그램을 짜는 데만 쓰고, 다 만든 프로그램은 몇 번을 돌려도 돈이 들지 않아야 한다.
경주를 화면에 담는 방법 78년

투르 드 프랑스가 처음 생중계된 건 1948년이다. 파리 파르크 데 프랭스 경기장에 선수들이 들어오는 장면이었다. 길 위를 따라가는 중계는 그 뒤의 일이다. 1965년 프랑스 국영방송 자료 화면에는 안테나를 단 오토바이가 나온다. 오토바이 카메라가 찍은 화면을 헬기가 받아 넘기는 방식을 프랑스 TV가 처음 시도했다는 설명이 붙어 있다. 지금도 뼈대는 같다. 오토바이와 헬기의 카메라가 찍고, 더 높이 뜬 중계기가 받아 넘긴다.
2015년에는 다른 갈래가 생겼다. 출전 선수 198명 전원의 자전거에 GPS 송신기가 달려 1초마다 위치와 속도를 보냈다. 카메라가 비추지 않는 선수도 이제 숫자로는 어디 있는지 안다. 중계 화면의 「선두와 몇 초 차」가 그때부터 추정이 아니라 측정이 됐다.
일반 라이더 쪽에서는 2016년 네덜란드에서 Relive가 창업해 내 기록을 3D 지도 영상으로 바꿔 주기 시작했다. Strava는 2023년 말 Flyover를 내놨다. 둘 다 좋은 물건이다. 다만 둘 다 기록을 따라 날아가는 영상이지 기록으로 중계하는 영상은 아니다. 프로 중계에서 영상과 데이터는 따로 온다. 내 라이딩에는 카메라가 없고 데이터만 있다. 그렇다면 데이터를 보고 카메라를 어디에 둘지 정하는 쪽, 그러니까 감독을 흉내 내야 한다.
그래서 만든 것: ridecast

이름은 ride와 broadcast를 붙여 ridecast로 지었다. 하는 일은 다섯 단계다.
분석. 기록 파일을 읽어 1초 간격으로 다시 맞춘다. 기기마다 기록 간격이 달라서다. 여기서 속도, 경사, 진행 방향, 멈춘 구간을 계산한다. 오르막은 따로 찾는다. 평균 경사와 길이로 Strava와 같은 방식의 등급(4등급부터 1등급, 그 위의 HC)을 매기고, 정상 좌표로 지명을 찾아 「산성리 오르막」 같은 이름을 붙인다. 동행자의 기록은 내 경로 위에 겹친다. 그래야 「누가 몇 미터 앞에 있나」를 셀 수 있다.
감독. 이 프로그램의 본체다. 코스를 훑으면서 구간마다 어울리는 샷을 고른다.
- 1.4 km 넘게 곧게 뻗은 길이면 풀백: 선수에게 붙어 있다가 직선 전체가 보이도록 크게 빠진다.
- 굽은 길이면 헬기 부감: 거의 수직으로 내려다봐야 굽이가 보인다.
- 최고 속도가 나온 구간이면 크래시 줌: 멀리서 급히 다가가 슬로모션으로 「최고 속도」를 띄운다.
- 오르막 앞에서는 시간을 멈춘다. 경사를 색 블록으로 그린 프로필 카드를 띄우고, 코스를 한 번 미리 날아 본 뒤 오르기 시작한다.
- 오르막에서는 오토바이 추적·정면·측면을 섞고, 오른쪽에 정상까지 남은 거리와 경사 그래프, 그 위를 기어오르는 작은 선수 그림을 띄운다.
그리고 몇 장면은 「여기는 반드시 보여 준다」고 못 박아 둔다. 순위가 바뀐 순간(추월 배틀 샷), 가장 가파른 150 m(측면 슬로), 헤어핀(헬기 정지 부감)이다. 결승은 정해진 순서대로 간다. 마지막 1 km, 스프린트 정면, 결승선 옆 고정 카메라, 사진 판독, 최종 순위표, 완주 기록.
촬영. 감독이 프레임마다 「카메라는 어디서 어느 방향을 몇 도로 내려다보는가」를 적어 두면, 화면 없이 도는 크롬이 3D 지도를 그 자리에 놓고 한 장씩 찍는다. 지도는 MapLibre에 Esri 위성 사진과 AWS 고도 타일을 얹었다. 중계 그래픽과 페달을 밟는 선수 그림도 같은 장에 그린다. 마지막에 ffmpeg가 이어 붙인다.
여기까지 어디에도 AI가 없다. 분석과 감독은 규칙과 계산이고, 촬영은 브라우저다. AI는 이 규칙들을 같이 정하고 코드를 짜는 데만 들어갔다. 다 만든 뒤로는 기록 파일만 바꿔 넣으면 된다. 데스크톱에서 197초짜리 1080p 영상 하나가 5분쯤 걸린다.
예제: 남한산성 한 바퀴

시험용 기록은 직접 만들었다. 실제 도로를 따라 길을 짜는 BRouter로 잠실에서 미사리, 광지원을 거쳐 남한산성에 올랐다가 잠실로 돌아오는 51.7 km를 그리고, 그 길 위에 네 명의 가상 선수를 달렸다. 평지는 다 같이 대열로 달리게 하고, 오르막에서는 선수마다 출력을 다르게 줘서 경사에 따라 속도가 정해지는 간단한 물리 모델로 각자 오르게 했다. 출력은 수백 미터마다 조금씩 출렁이고, 오르막마다 각자 다른 지점에서 한 번씩 치고 나갔다가 힘이 빠지도록 했다. 누가 치고 나가고 누가 다시 따라붙는 장면이 나오게 하려는 장치다. 이름은 가람, 나래, 다온, 라온이다.
이 글의 선수 넷과 그 기록은 실제 사람이 아니라 합성 데이터다. 도로와 지형, 위성 사진만 실제다.
이 기록 네 개를 넣으면 이런 일이 생긴다.
- 2시간 19분(움직인 시간 2시간 9분), 상승 고도 662 m의 라이딩이 197초 영상이 된다.
- 오르막 두 곳을 스스로 찾았다. 4등급 「가지울 오르막」과 3등급 「산성리 오르막」(8.74 km, 평균 3.0%, 최대 10.2%). 이름은 정상 좌표 근처의 지명이다.
- 샷은 모두 45개. 순위가 바뀐 추월은 아홉 번 나왔고, 감독은 그중 두 번을 골라 두 선수를 한 화면에 잡는 배틀 샷을 넣었다. 오르막 하나에 쓸 수 있는 영상 시간이 정해져 있어 고정 장면은 몇 개만 들어가고, 서로 겹치지 않는 것 중에서 선두 교체, 추월, 급경사, 헤어핀 순으로 고른다. 뽑히지 못한 추월은 화면 아래 「추월 라온 ▶ 나래 제침」 같은 자막으로만 지나간다.
- 결승에서는 넷이 거의 같이 들어왔다. 사진 판독으로 가린 차이는 0.40초, 0.94초, 1.49초. 종합 순위는 여기에 결승 보너스(10·6·4초)를 빼서 매긴다.

감독의 시간 배분
이 영상에서 가장 중요한 숫자는 배속이다. 2시간 19분을 197초에 담으면 평균 42배속이다. 그런데 평균대로 고르게 감으면 앱의 플라이오버와 다를 게 없다.

그래프를 보면 감독이 무엇을 했는지 보인다. 평지는 100배, 200배로 지나간다. 오르막에 들어가면 헬기 샷은 여전히 빠르지만 선수에게 붙는 샷은 2.5~5배로 떨어진다. 결승선 카메라는 1.4배, 거의 실제 속도다. 회색 칸은 아예 시간이 멈춘 장면이다.
샷 안에서도 속도가 일정하지 않다. 경사가 급해지거나, 헤어핀을 돌거나, 두 선수가 붙어 있거나, 결승이 가까워질수록 「볼거리 점수」가 올라가고, 점수가 높은 곳에 시간을 더 쓴다. 한 샷에 쓸 실제 시간은 정해 두고, 그 안에서 점수에 비례해 영상 시간을 나눠 주는 방식이다.
직접 돌려 보기
글 안의 플레이어는 영상 파일이 아니다. 영상을 만들 때 쓰는 화면과 같은 코드가 여러분의 브라우저에서 지도를 그리고 있다. 띠를 끌어 오르막 한가운데로 가면 상황판의 순위와 시간차도 그 순간 값으로 바뀐다. 실제 영상은 이 화면을 프레임마다 기다렸다 찍은 것이라 타일이 덜 뜬 장면이 없다.
어디서 틀렸나
처음 판은 너무 빨랐다. 1차본은 87초였다. 근접 샷도 25~90배속으로 지나가서, 오토바이 카메라가 선수 옆에 붙어 있는데 선수가 순간이동을 했다. 프로 중계를 떠올려 보면 당연하다. 카메라가 선수에게 붙는 건 그 선수를 보여 주려는 거고, 보여 주려면 실제 속도에 가까워야 한다. 근접 샷은 6배속을 넘지 않게 묶고, 볼거리 점수에 따라 느려지는 장치를 이때 넣었다. 영상은 197초로 늘었다.
선수 표시가 화면 구석으로 튀었다. 카메라가 크게 건너뛰는 컷마다 선수 핀이 화면 엉뚱한 곳에 한 프레임씩 나타났다. 지도는 지구를 옆으로 이어 붙여서 같은 지점이 경도 360°마다 복제돼 있다. 지도 라이브러리가 표시를 붙일 자리로 「직전 위치에서 가장 가까운 복제본」을 고르는데, 카메라가 순간이동하니 동경 127°가 아니라 서경 232°(-232°) 쪽 복제본을 골랐다. 표시를 옮기기 전에 직전 위치 기억을 지우는 한 줄로 고쳤다.
선수가 땅속에 있었다. 3D 지형 타일이 늦게 도착하면 선수 표시가 고도 0 m 기준에 남는다. 산 중턱을 달리는 선수가 산 밑에 그려진다. 촬영할 때는 지도가 다 그려질 때까지 기다렸다가 한 번 더 놓는 것으로 해결했다. 이 글에 넣은 플레이어도 같은 문제를 겪어서 같은 방법을 썼다.
카메라가 산에 파묻혔다. 오토바이 카메라는 선수 뒤 낮은 곳에 있다. 오르막에서 길이 굽으면 카메라와 선수 사이에 산비탈이 끼고, 화면은 흙색으로 가득 찬다. 실제 오토바이는 길 위에 있으니 이런 일이 없지만, 계산으로 놓은 카메라는 비탈 속에도 들어간다. 카메라와 선수 사이 시선 위 다섯 점의 지형 높이를 재고, 가리면 내려다보는 각도를 5°씩 더 세운다.
꼴찌가 결승선을 넘지 못했다. 사진 판독을 넣었더니 1등만 들어오고 나머지는 판정이 안 됐다. 영상의 시간은 주인공(첫 번째 기록)을 기준으로 흐르는데, 주인공 기록이 결승선에서 딱 끝나 버리니 그 뒤에 들어온 사람은 시간축 밖에 있었다. 결승선 좌표를 따로 받고, 결승을 지난 기록이 조금이라도 있어야 한다는 조건을 붙였다. 실제로 쓸 때도 결승을 지나 몇 초 더 기록하고 꺼야 한다.
오르막을 못 찾았다. 「평균 3% 넘는 구간」으로 오르막을 찾았는데, 길고 완만한 진입로 뒤에 짧은 급경사가 붙은 오르막은 전체 평균이 3%를 못 넘어서 통째로 빠졌다. 남한산성 같은 길이 바로 그렇다. 평균 3%가 넘는 가장 이른 시작점을 찾아 완만한 앞부분을 잘라내도록 바꿨다.
시험 경로가 등산로였다. 처음 그린 시험 경로에서 경사가 25%가 나왔다. 자전거로는 오를 수 없는 숫자다. 길을 짜는 프로그램에 「걷기·트레킹」 방식으로 경로를 물었더니 남한산성 등산로를 타고 올라간 것이었다. 도로 자전거 방식으로 바꾸고 광지원을 거쳐 차도로 오르게 다시 그렸다. 합성 데이터도 처음부터 의심해야 한다.
글에 넣었더니 지도가 안 떴다. 오늘 이 글을 준비하면서 생긴 일이다. 오시오는 글 안에 끼우는 화면을 따로 격리하고, 그 안에서 할 수 있는 일을 좁게 열어 둔다. 지도 라이브러리는 무거운 계산을 뒤에서 돌리는 일꾼(워커)을 만드는데, 그 일꾼을 만드는 길이 막혀 있었다. 기존 끼움 화면 중에 이런 3D 지도를 쓴 건 하나도 없어서 그동안 드러나지 않았다. 그 길 하나만 열었다.
왜 딸깍이 되는가
중계에는 문법이 있다. 오르막 앞에서는 프로필을 깔고, 공격이 나오면 두 선수를 잡고, 정상 1 km 전에는 깃발을 띄운다. 수십 년 동안 다듬어진 문법이라 규칙으로 옮겨 적을 수 있다. 「어디가 볼 만한가」도 기록 안에 숫자로 들어 있다. 경사, 속도, 순위가 바뀐 시각.
그래서 AI가 할 일은 영상을 만드는 게 아니라, 그 문법을 프로그램으로 옮기는 일이었다. 한 번 옮겨 두면 그다음부터는 기록 파일만 넣으면 된다. 매번 AI에게 「이 영상을 멋지게 만들어 줘」라고 부탁하는 방식이었다면 돈도 들고 결과도 매번 달랐을 것이다. ridecast는 같은 기록을 넣으면 언제나 같은 영상이 나온다.
따라 하려면
- 참고할 화면을 정확히 대라. 「멋진 영상」이 아니라 「투르 드 프랑스 중계 화면」이라고 하면, 헬기·오토바이 샷이나 상황판, 플람 루주 같은 구체적인 부품이 따라 나온다. 이름이 붙은 문법이 있는 분야일수록 좋다.
- 지금 가진 것과 비교해 무엇이 싫은지 말하라. 이번 출발점은 「카메라가 하나뿐이라 지루하다」였다. 그 한 문장이 감독 모듈이라는 설계를 정했다.
- 첫 판을 보고 고칠 점을 구체적으로 적어라. 2차본에 들어간 것들(느리게, 페달 밟는 선수, 추월·급경사·헤어핀 강조, 사진 판독, 오르막 등급, 순위표, 레이스 상황판)은 전부 1차본을 보고 나온 주문이다. 처음부터 다 말할 필요는 없다.
- 시험 데이터를 따로 만들어라. 내 기록 하나로는 추월도 사진 판독도 시험할 수 없다. 엎치락뒤치락하는 가짜 경주를 만들어 두면 경우의 수를 한 번에 본다.
못 하는 것
- 실제 기록으로는 아직 돌려 보지 않았다. 이 글의 예제는 전부 합성 데이터다. 실제 GPS 기록에는 터널에서 끊기거나 위치가 튀는 잡음이 있고, 동행자끼리 기기 시계가 몇 초씩 어긋나기도 한다. 그때 순위 계산이 얼마나 버티는지는 모른다.
- Strava 연동도 검증하지 못했다. 활동과 구간(세그먼트)을 받아오는 코드는 있지만 실제 계정으로 받아 본 적이 없다.
- 세로 영상이 없다. 쇼츠·릴스용 9:16 화면 배치는 아직 만들지 않았다.
- 파일이 크다. 197초 1080p가 546 MB다. 화질을 높게 잡아 둔 탓인데, 줄이는 설정은 아직 없다.
- 위성 사진은 무료지만 조건이 있다. 출처 표기가 필수고, 상업적으로 대량 배포하려면 약관을 따로 확인해야 한다. 위성 사진은 찍힌 날의 모습이라 공사장이 그대로 나오기도 한다.
- 동행자 기록은 같은 시간대여야 한다. 다른 날 같은 코스를 달린 기록끼리 겨루는 「유령 경주」는 아직 안 된다.
- 사람 감독만큼 눈치가 있지는 않다. 경치가 좋은 곳, 함께 달린 사람이 손을 흔든 곳은 숫자로 안 보인다. 감독이 아는 건 경사와 속도와 순위뿐이다.
앱의 플라이오버가 「내가 어디를 달렸나」를 보여 준다면, 중계는 「그날 무슨 일이 있었나」를 보여 준다. 그 차이는 카메라를 몇 대 두느냐보다 언제 어느 카메라로 넘어가느냐에서 났다. 다음에 친구들과 남한산성을 오르면, 결승선을 지나 몇 초 더 기록을 켜 두려 한다.
재미있게 읽었다면 꼭 의견을 달아달라.
출처
- 1948년 첫 생중계: 1948 Tour de France(2차), Van Reeth, TV Broadcasting of the Tour de France: From Local Experiment to Global Media Product, 1948-2021, Essays in Economic & Business History (2022)
- 1965년 오토바이–헬기 연결: 프랑스 국립시청각연구소 INA Technical resources for the Tour de France(1965-09-18 방송, 해설 내용 기준)
- 2015년 전 선수 GPS: BikeRadar, Big data comes to the Tour de France, ARN, Dimension Data equips Tour de France
- 라이딩 영상 앱: Relive TechCrunch 2017-05-02, Strava Flyover DC Rainmaker 2023-11
- 오르막 등급은 Strava의 등급 체계(4~HC)를 따랐고, 산악·포인트 배점과 결승 보너스(10·6·4초)는 투르 드 프랑스 규정의 배점 방식을 흉내 냈다
- 지도: MapLibre GL JS, 위성 사진 Esri World Imagery(Imagery © Esri, Maxar, Earthstar Geographics), 고도 AWS Terrain Tiles, 지명 OpenStreetMap Nominatim(© OpenStreetMap 기여자), 경로 BRouter
- 프로그램·합성 기록·영상·그래프는 모두 이 글의 실험으로 만들었다