공공데이터로 bar-chart-race 쇼츠 만들기: 데이터 수집부터 32초 세로 영상까지 6단계
공공데이터에서 재미있는 순위 변화를 찾아도, 표나 꺾은선 차트로 올리면 대부분 그냥 넘겨집니다. 같은 숫자를 막대가 자리를 바꾸는 32초짜리 세로 영상(bar-chart-race)으로 바꾸면 "그래서 지금 1위는 누구지?" 하고 끝까지 보게 됩니다. 데이터 한 장 인스타그램 계정에서 이 방식으로 레이스 영상 50여 편을 만들어 보면서 정리한 순서를 6단계 가이드로 옮겼습니다. 예제는 세계은행 공개 API의 나라별 외환보유액(1970~2025)이고, API 키 없이 Colab에서 그대로 실행됩니다.

- 영상을 만들기 전에 1위가 몇 번 바뀌는지부터 셉니다. 2번 미만이면 다른 지표로 바꿉니다. 이 기준 하나로 특허 출원(1위 교체 1번), 하이테크 수출(0번) 같은 주제를 미리 걸렀습니다.
- 빈 연도는 정해진 규칙으로만 채우고(예: 최대 2년 전 값까지), 채운 비율을 숫자로 남겨 영상 하단과 게시글에 밝힙니다.
- 프레임은 PNG로 저장하지 않고 픽셀을 ffmpeg에 바로 흘려보내 인코딩합니다. 무료 Colab에서 960프레임이 2분 안팎에 끝났습니다.
- "인코딩 성공"만 믿지 않고 프레임 수, 전체 디코딩, 4장면 모아보기 세 가지로 검증합니다. 글자 깨짐과 겹침은 모아보기에서만 잡혔습니다.
이 글은 이런 분께 필요합니다
공공데이터에서 찾은 순위 변화를 인스타그램 릴스나 유튜브 쇼츠로 보여 주고 싶은 분, 데이터 분석 포트폴리오에 "수집부터 결과물 검증까지" 한 흐름을 보여 줄 사례가 필요한 분을 위한 글입니다. 영상 편집 프로그램은 쓰지 않습니다. 아래 버튼으로 노트북을 열고 런타임 → 모두 실행을 누르면 이 글의 영상이 다시 만들어집니다.
준비물은 Google 계정 하나입니다. GPU, API 키, Drive 연결은 필요 없습니다. 노트북은 공개 GitHub 저장소(team-datapopcorn/datapopcorn-assets)에 있고, 2026년 10월 9일 Colab 무료 런타임(Python 3.13.16, ffmpeg 6.1.1)에서 저장소의 노트북을 처음부터 끝까지 실행해 오류 없이 1080×1920, 960프레임 MP4가 나오는 것을 확인했습니다.
실제로 이 방식으로 만들어 올린 영상은 아래와 같습니다.
- 한국 수출 품목 TOP 10, 1988~2025: 의류·신발에서 반도체로 1위가 일곱 번 바뀝니다.
- 외환보유액 TOP 10, 1970~2025: 이 글의 예제와 같은 데이터입니다.
- 나라별 한국어능력시험(TOPIK) 지원자, 2017~2025: 2025년 베트남이 중국을 처음 앞섭니다.
- 한국 대학 유학생 국적 TOP 10, 2009~2026: 2026년 7월 베트남이 64명 차이로 처음 1위에 오릅니다.

Step 1. 지표를 고르고 API에서 데이터를 받습니다
세계은행 API는 지표 코드 하나로 모든 나라의 연도별 값을 JSON으로 돌려줍니다. 외환보유액은 FI.RES.TOTL.CD(금 포함, 명목 달러)입니다.
INDICATOR = "FI.RES.TOTL.CD"
url = f"https://api.worldbank.org/v2/country/all/indicator/{INDICATOR}?format=json&per_page=20000&date=1970:2025"
여기서 한 가지를 꼭 걸러야 합니다. 응답에는 "World", "East Asia & Pacific" 같은 지역 묶음이 나라처럼 섞여 있습니다. 그대로 순위를 매기면 1위가 World가 됩니다. 나라 목록 API(/v2/country)에서 region.id가 NA인 행이 묶음이므로, 이것을 빼고 남은 217개 나라만 씁니다. 노트북 Step 1 셀이 이 처리를 하고 "나라 수: 217 | 지역 묶음 제외: 79"를 출력합니다.
1인당 값(1인당 GDP, 출산율 등)을 쓸 때는 모나코·리히텐슈타인 같은 초소형국이 1위를 차지합니다. 이때는 연도별 인구 100만 명 이상인 나라만 남기는 규칙을 정하고 화면에 적었습니다.
Step 2. 1위가 몇 번 바뀌는지 먼저 셉니다
bar-chart-race는 1위가 바뀌는 장면이 있어야 끝까지 봅니다. 그래서 영상을 만들기 전에 해마다 1위 나라를 뽑고, 바뀐 횟수를 셉니다.
leaders = raw.idxmax(axis=1).dropna()
changes = [(y, leaders[y]) for y in leaders.index[1:] if leaders[y] != leaders[y - 1]]
assert len(changes) >= 2, "1위 교체가 2번 미만입니다."
외환보유액은 6번입니다. 미국과 독일이 1970년대에 네 번 주고받고, 1995년 일본, 2006년 중국으로 넘어갑니다. 실제 제작에서 이 기준으로 걸러 낸 주제는 다음과 같습니다.
| 지표 | 1위 교체 | 판단 |
|---|---|---|
| 특허 출원(거주자) | 1번(2010년 중국) | 다른 지표로 교체 |
| 하이테크 수출 | 0번(내내 중국) | 다른 지표로 교체 |
| 인터넷 이용률 | 2025년 4개국이 100%로 동률 | 1위가 정렬 순서로만 정해져 교체 |
| 원유 생산 | 7번 | 제작 |
교체가 적어도 이야기가 분명하면(예: 한국이 7위에서 2위로 오르는 R&D 투자) 만들 수 있지만, 그때는 첫 장면과 게시글 첫 줄을 그 이야기로 잡습니다.
Step 3. 빈 연도는 정해진 규칙으로만 채웁니다
나라마다 발표가 늦거나 빠진 해가 있습니다. 빈칸을 그대로 두면 막대가 사라졌다가 다시 나타나고, 마음대로 채우면 없는 숫자를 만든 것이 됩니다. 그래서 규칙을 하나 정하고, 그 규칙이 얼마나 개입했는지 숫자로 남깁니다.
filled = raw.ffill(limit=2) # 최대 2년 전 값까지만 이어 쓰기
added_top = int(((filled.rank(axis=1, ascending=False) <= 10) & raw.isna()).sum().sum())
예제에서는 이어 쓴 칸이 전체 81개였고, 그중 화면에 보이는 TOP 10 안은 3개(0.5%)였습니다. 이 규칙은 영상 하단에 "빈 해는 최대 2년 전 값 사용"으로 적고, 게시글 출처 문단에도 씁니다. 다른 데이터에서는 관측값 사이를 선형 보간하거나, 원자료가 없는 해를 앞뒤 해 평균으로 채운 뒤 그 해를 밝혔습니다.
Step 4. 프레임마다 순위를 다시 매기고 연도 사이를 보간합니다
32초 × 30fps = 960프레임입니다. 56년치면 한 해에 약 0.5초가 돌아갑니다. 프레임마다 앞뒤 두 해의 값을 선형으로 섞고 다시 정렬합니다.
- 10위 밖 나라도 위치를 잡아 둡니다. 화면에는 10개만 그리지만 그 아래 나라도 11~12위 자리에서 기다리게 하면, 순위권으로 올라오는 막대가 갑자기 튀어나오지 않고 아래에서 밀고 올라옵니다.
- 막대 위치는 목표 순위로 조금씩 옮깁니다. 프레임마다 남은 거리의 35%씩 이동시키면 자리바꿈이 부드럽게 보입니다.
- 마지막 2.5초는 최종 순위에서 멈춥니다. 끝 장면이 바로 잘리면 최종 1위를 읽을 시간이 없습니다.
Step 5. 1080×1920 프레임을 그려 ffmpeg로 바로 인코딩합니다
Pillow로 프레임을 한 장씩 그리고, PNG로 저장하지 않고 원시 픽셀을 ffmpeg 표준 입력에 바로 씁니다. 디스크를 거의 쓰지 않습니다.
enc = subprocess.Popen(["ffmpeg", "-y", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", "1080x1920", "-r", "30", "-i", "-",
"-c:v", "libx264", "-preset", "veryfast", "-crf", "18", "-pix_fmt", "yuv420p", "-movflags", "+faststart",
"out/race_silent.mp4"], stdin=subprocess.PIPE)
for f in range(960):
enc.stdin.write(draw(f / 30).tobytes())
enc.stdin.close(); enc.wait()
화면 구성에서 지킨 규칙은 세 가지입니다.
- 안전 영역: 글자는 좌우 60px 안쪽, 위 180px 아래에만 둡니다. 막대와 숫자는 화면 아래 약 330px 위에서 끝내고, 그 아래에는 작은 출처 줄과 진행 막대만 둡니다. 릴스 화면의 계정명·캡션·버튼이 하단과 오른쪽을 가리기 때문입니다.
- 주인공 고정 줄: 강조할 나라(예제는 한국)가 10위 밖이면 맨 아래에 따로 한 줄을 두고 순위를 보여 줍니다. 헤더에는 "한국 세계 N위"를 계속 띄웁니다.
- 자리바꿈 중 숫자 숨김: 두 막대가 같은 줄을 지나갈 때는 아래 막대의 숫자를 잠깐 숨깁니다. 그대로 두면 숫자 두 개가 겹쳐 읽히지 않습니다.
무료 Colab 런타임에서 960프레임을 그리고 인코딩하는 데 2분 안팎이 걸렸습니다.
Step 6. 배경음악을 합치고 세 가지로 검증합니다
인스타그램 PC 웹에서 올리면 앱의 음악 목록을 쓸 수 없어서, 음악을 영상에 미리 합칩니다. 예제는 Kevin MacLeod의 "Club Diver"(incompetech.com, CC BY 4.0)를 쓰고, 끝 2초는 서서히 줄입니다. CC BY 곡이므로 게시글에 "음악: Kevin MacLeod - Club Diver (incompetech.com, CC BY 4.0)"를 꼭 적습니다.
ffmpeg -i out/race_silent.mp4 -i bgm.mp3 -map 0:v -map 1:a -c:v copy -c:a aac -b:a 192k \
-af "afade=t=out:st=30:d=2" -shortest -movflags +faststart out/race_final.mp4
그다음 "인코딩 완료" 메시지만 믿지 않고 세 가지를 확인합니다.
- 프레임 수와 해상도:
ffprobe -count_frames로 영상 스트림이 1080×1920, 960프레임인지 봅니다. - 전체 디코딩:
ffmpeg -v error -i out/race_final.mp4 -f null -의 오류 출력이 0바이트여야 합니다. 중간이 깨진 파일을 여기서 잡습니다. - 4장면 모아보기: 시작·중간·끝 프레임을 한 장으로 붙여 눈으로 봅니다. 글자 깨짐, 숫자 겹침, 빈 줄 같은 문제는 앞의 두 검사로는 잡히지 않습니다.

위 이미지는 2026년 10월 9일 Colab에서 저장소 노트북을 실행해 나온 모아보기입니다. 같은 실행에서 영상 960프레임, 해상도 1080×1920, 전체 디코딩 오류 0바이트를 확인했습니다.
막힐 때
실제 제작에서 모아보기로 잡은 문제를 중심으로 정리했습니다.
| 증상 | 원인 | 해결 |
|---|---|---|
| 1위가 "World"나 "High income"으로 나옴 | 세계은행 응답에 지역 묶음이 섞여 있음 | 나라 목록에서 region.id == "NA"인 행을 뺍니다 |
| 1인당 지표에서 모나코·버뮤다가 1위 | 초소형국 효과 | 연도별 인구 100만 명 이상만 남기고 화면에 규칙을 적습니다 |
| 한자·특수문자가 네모로 나옴 | 글꼴에 없는 글자(예: 브랜드명의 "新") | 이름을 한글로 바꿔 씁니다. 모아보기에서 확인합니다 |
| 헤더에 "세계 0위" | 주인공이 그해 데이터에 없음 | 순위가 없으면 "자료 없음" 같은 문구로 바꿉니다 |
| "2위와 0억 달러 차이" | 1 미만 값을 정수로 반올림 | 1 미만이면 소수 첫째 자리까지 표시합니다 |
| 어두운 막대 위 글자가 안 보임 | 막대 색과 글자 색 대비 부족 | 막대 색을 밝게 바꾸거나 글자를 막대 밖으로 뺍니다 |
| 음악을 받지 못함 | 네트워크 문제 | 노트북은 무음 영상을 최종본으로 남깁니다. 다시 실행합니다 |
다른 지표로 바꿔 보기
노트북 맨 위 설정 셀의 INDICATOR와 TITLE만 바꾸면 됩니다. 단위가 달러가 아니면 숫자 표시 함수 fmt도 바꿉니다.
| 지표 | 코드 | 메모 |
|---|---|---|
| 상품·서비스 수출액 | NE.EXP.GNFS.CD | 1위 교체 3번(미국·중국) |
| 1인당 명목 GDP | NY.GDP.PCAP.CD | 인구 100만 명 이상 규칙 필요 |
| 특허 출원(거주자) | IP.PAT.RESD | 1위 교체 1번이라 Step 2에서 멈춥니다 |
재현과 출처
- 노트북: bar_chart_race_shorts.ipynb
- 결과물 예시: sample_output/ (Colab 실행 영상과 모아보기)
- 데이터: World Bank, World Development Indicators
FI.RES.TOTL.CD(CC BY 4.0), 2026년 10월 9일 조회 - 음악: Kevin MacLeod - Club Diver (incompetech.com, CC BY 4.0)
- 글꼴: Pretendard (SIL Open Font License)
다음 글
여러 출처의 공공데이터를 받고 이어 붙이다 막히는 지점은 공공데이터 수집에서 막히는 6가지와 해결법에 정리했습니다.
