45개의 비디오테이프를 디지털화하기 위한 8년간의 여정 (2부)
1부에서는 오래된 가족 영상을 디지털 형식으로 캡처하고 각각의 장면으로 나누기까지 겪은 고된 여정을 설명했습니다. 모든 클립을 처리한 뒤에는 영상을 찾아보는 일이 YouTube에서 클립을 검색하는 것처럼 간단하기를 바랐습니다. 하지만 이 영상들은 우리 가족만의 사적인 추억이므로, 실제 YouTube는 너무 공개적입니다. 사용하기 쉽고 안전한 방식으로 영상을 공유할 방법이 필요했습니다.
3단계: 공유
ClipBucket, 사실상 설치할 수 없는 오픈 소스 YouTube 복제판
처음 시도한 해결책은 ClipBucket이었습니다. ClipBucket은 스스로 호스팅할 수 있는 오픈 소스 YouTube 복제판을 표방합니다.

ClipBucket은 사용자가 직접 호스팅할 수 있는(이론적으로는) 오픈 소스 YouTube 복제판입니다.
이상하게도 ClipBucket에는 설치 안내가 전혀 없었습니다. 저는 외부 가이드를 참고해 서버 구성 관리 도구인 Ansible로 설치 과정을 자동화했습니다.
어려움의 일부는 ClipBucket의 설치 스크립트가 완전히 망가져 있었다는 점이었습니다. 당시 Google 직원이었던 저는 YouTube 복제판에 패치를 기여할 수 없었지만, 수정 방법이 명백했을 버그 보고서를 제출했습니다. 몇 달이 지나도록 그들은 문제를 인정하지 않았습니다. 오히려 릴리스할 때마다 작동을 망가뜨리는 오류를 더 많이 추가했습니다.
ClipBucket의 사업은 컨설팅 모델을 기반으로 했습니다. 코드는 무료로 공개하고, 배포에 도움이 필요한 고객에게 비용을 청구하는 방식이었습니다. 유료 설치 지원으로 수익을 내는 회사가 사용자가 직접 배포하는 방식에 그다지 관심이 없을 수도 있다는 사실이 서서히 깨달아졌습니다.
더 현대적인 대안, MediaGoblin
ClipBucket 때문에 몇 달 동안 좌절한 뒤, 이용 가능한 대안을 다시 살펴보다가 MediaGoblin을 발견했습니다.

MediaGoblin은 직접 호스팅하는 미디어 공유 플랫폼입니다.
MediaGoblin에는 마음에 드는 점이 많았습니다. 보기 흉한 PHP로 작성된 ClipBucket과 달리, MediaGoblin은 제가 매우 익숙한 언어인 Python으로 작성되어 있었습니다. 영상 업로드를 쉽게 자동화할 수 있는 명령줄 인터페이스도 포함되어 있었습니다. 무엇보다 MediaGoblin은 Docker 이미지를 제공했기 때문에 설치 과정에서 추측할 일이 사라질 터였습니다.
뜻밖에 어려웠던 MediaGoblin 재도커화
MediaGoblin의 Docker 이미지가 배포를 간단하게 만들어 줄 것이라고 생각했습니다. 그런데 꼭 그렇지는 않았습니다.
미리 빌드된 이미지에는 제가 필요로 하는 기능 두 가지가 없었습니다.
- 인증
- MediaGoblin은 기본적으로 공개되어 있으므로, 낯선 사람들이 사이트에 접근하지 못하게 할 방법이 필요했습니다.
- 트랜스코딩
- 영상을 업로드할 때마다 MediaGoblin은 최적의 스트리밍을 위해 영상을 다시 인코딩하려고 합니다. 이미 스트리밍에 적합한 영상이라면 이 과정은 화질을 떨어뜨리고 처리 자원을 낭비합니다.
- MediaGoblin에는 트랜스코딩을 건너뛰는 설정 옵션이 있지만, 기존 Docker 이미지는 설정을 변경할 수 없었습니다.
문제없었습니다. Docker 이미지는 오픈 소스였으므로 직접 다시 빌드할 수 있었습니다.
안타깝게도 Docker 이미지는 최신 MediaGoblin 저장소를 기반으로 더 이상 빌드되지 않았습니다. 마지막으로 성공한 빌드와 일치하는 버전에 맞춰 동기화해 보았지만, 그것도 실패했습니다. 똑같은 코드를 사용해 빌드하고 있었는데도 MediaGoblin의 외부 의존성이 어느새 바뀌어 빌드를 망가뜨린 것입니다. 수십 시간을 들여 MediaGoblin의 10분 넘게 걸리는 빌드 과정을 몇 번이고 지켜본 끝에, 마침내 작동시킬 수 있었습니다.
몇 달 뒤 같은 일이 다시 발생했습니다. 지난 몇 년 동안 MediaGoblin의 의존성 변동 때문에 제 빌드가 여러 차례 망가졌고, 이 글을 쓰는 중에도 한 번 더 문제가 발생했습니다. 결국 저는 MediaGoblin을 직접 포크하고, 모든 의존성을 명시적인 버전으로 하드코딩했습니다. 즉, MediaGoblin이 celery 3.0 이상 어떤 버전에서든 작동한다고 불확실하게 선언하는 대신, 제가 MediaGoblin으로 테스트한 celery의 4.2.1 릴리스에 의존하도록 설정했습니다. MediaGoblin에는 재현 가능한 빌드 메커니즘이 필요한 것 같지만, 아직 그 일까지 맡지는 않았습니다.
아무튼 여러 시간 동안 고생한 끝에 MediaGoblin을 Docker 안에서 빌드하고 조정할 수 있는 상태가 되었습니다. 그다음에는 불필요한 영상 트랜스코딩을 건너뛰고 인증을 위해 Nginx를 추가하는 일은 간단했습니다.
4단계: 호스팅
MediaGoblin을 로컬 컴퓨터에서 Docker로 실행하게 되었으니, 다음 단계는 가족이 영상에 접근할 수 있도록 클라우드 서버에 설정을 배포하는 일이었습니다.
MediaGoblin과 영상 저장 문제
애플리케이션의 Docker 이미지를 받아 공개 URL에서 호스팅해 주는 플랫폼은 많습니다. 문제는 MediaGoblin 애플리케이션 자체뿐 아니라 공유해야 할 영상 파일도 33GB나 있었다는 점입니다. 파일을 Docker 이미지에 하드코딩할 수도 있었지만, 번거롭고 보기에도 좋지 않았습니다. 설정 파일을 한 줄만 바꿔도 33GB의 데이터를 다시 배포해야 했을 테니까요.
ClipBucket을 사용할 때는 gcsfuse로 이 문제를 해결했습니다. gcsfuse는 운영체제가 Google Cloud Storage의 디렉터리를 일반 파일 시스템 경로로 불러올 수 있게 해주는 유틸리티입니다. 영상 파일을 Google Cloud Storage에 저장하고 gcsfuse를 사용해 ClipBucket에는 로컬 파일처럼 보이게 했습니다.
차이점은 ClipBucket이 완전한 가상 머신에서 실행된 반면, MediaGoblin은 Docker 컨테이너에서 실행된다는 것이었습니다. Docker 아래에 클라우드 저장소 파일을 마운트하는 일은 훨씬 더 복잡한 것으로 드러났습니다. 온갖 함정을 해결하는 데 수십 시간을 썼고, 그 과정에 관한 블로그 글도 하나 통째로 작성했습니다.

Google Cloud Storage와 MediaGoblin을 통합하기 위한 초기 아키텍처. 2018년에 쓴 블로그 글에 기록했습니다.
몇 주 동안 모든 구성 요소가 서로 잘 작동하도록 설득한 끝에, 마침내 작동했습니다. MediaGoblin의 코드를 전혀 변경하지 않고도 미디어 파일을 Google Cloud Storage에서 읽고 쓰도록 속일 수 있었습니다.
유일한 문제는 MediaGoblin이 사용할 수 없을 정도로 느려졌다는 것이었습니다. 홈페이지의 영상 미리보기 이미지를 불러오는 데 꼬박 20초가 걸렸습니다. 영상을 보다가 앞으로 건너뛰면 MediaGoblin은 10초라는 영겁의 시간 동안 멈춘 뒤 재생을 재개했습니다.
근본적인 문제는 영상과 이미지 파일이 사용자에게 도달하기까지 길고 구불구불한 경로를 거친다는 것이었습니다. Google Cloud Storage에서 gcsfuse를 거쳐 MediaGoblin, Nginx를 통과한 다음 마지막으로 사용자의 브라우저에 도착해야 했습니다. gcsfuse는 속도에 최적화되어 있지 않기 때문에 주요 병목이었습니다. 프로젝트 홈페이지에서 지연 시간이 좋지 않다고 직접 경고하고 있습니다.

느린 성능에 관한 gcsfuse 문서의 경고
이상적으로는 브라우저가 모든 중간 계층을 건너뛰고 Google Cloud Storage에서 파일을 직접 가져와야 합니다. MediaGoblin의 코드베이스를 파고들어 Google Cloud Storage용 복잡한 통합 로직을 추가하지 않고 그렇게 하려면 어떻게 해야 할까요?
Nginx sub_filter 트릭
다행히 조금 추하기는 한 간단한 해결책을 찾았습니다. Nginx의 default.conf 파일에 이 필터를 추가했습니다.
sub_filter "/mgoblin_media/media_entries/" "https://storage.googleapis.com/MY-GCS-BUCKET/media_entries/sub_filter_once off;제 설정에서 Nginx는 최종 사용자와 MediaGoblin 사이의 프록시 역할을 했습니다. 위 지시어는 Nginx가 MediaGoblin의 모든 HTML 응답을 최종 사용자에게 전달하기 전에 검색 및 치환하도록 합니다. Nginx는 MediaGoblin의 미디어 파일에 대한 상대 경로를 모두 Google Cloud Storage URL로 바꿉니다.
예를 들어 MediaGoblin은 다음과 같은 HTML을 생성합니다.
<video width="720" height="480" controls autoplay> <source src="/mgoblin_media/media_entries/16/Michael-riding-a-bike.mp4" type="video/mp4" /></video>Nginx는 응답을 다음과 같이 수정합니다.
<video width="720" height="480" controls autoplay> <source src="https://storage.googleapis.com/MY-GCS-BUCKET/media_entries/16/Michael-riding-a-bike.mp4" type="video/mp4" /></video>전체 구조는 다음과 같습니다.

Nginx는 MediaGoblin의 응답을 다시 작성해 클라이언트가 Google Cloud Storage에서 미디어 파일을 직접 가져올 수 있게 합니다.
이 해결책의 멋진 점은 MediaGoblin의 코드를 수정할 필요가 전혀 없었다는 것입니다. Nginx 지시어 두 줄만으로 서로의 존재를 전혀 알지 못하는 MediaGoblin과 Google Cloud Storage를 매끄럽게 통합할 수 있었습니다.
참고: 이 해결책을 사용하려면 Google Cloud Storage의 파일을 누구나 읽을 수 있어야 합니다. 무단 접근 위험을 줄이기 위해 저는 길고 무작위적인 버킷 이름(예: mediagoblin-39dpduhfz1wstbprmyk5ak29)을 사용하고, 버킷의 접근 제어 정책에서 권한 없는 사용자가 디렉터리 내용을 나열하지 못하도록 설정했습니다.
최종 결과물
이제 완전하게 작동하는 해결책이 갖춰졌습니다. MediaGoblin은 Google Cloud Platform의 자체 컨테이너에서 문제없이 실행되었고, 덕분에 패치나 업그레이드를 자주 할 필요가 없었습니다. 전체 과정이 자동화되고 재현 가능했기 때문에 변경 사항을 쉽게 배포하거나 이전 버전으로 되돌릴 수 있었습니다.
가족은 영상을 쉽게 찾아볼 수 있다는 점을 무척 좋아했습니다. Nginx 성능 개선 덕분에 YouTube를 탐색하는 것처럼 반응도 빨랐습니다.
탐색 화면은 다음과 같았습니다.

우리 가족의 홈 비디오 공유 서버 탐색 화면
미리보기 이미지를 클릭하면 다음과 같은 화면으로 이동했습니다.

미디어 서버에서 개별 클립 보기
수년간 작업한 끝에, 처음 구상했던 YouTube 같은 방식으로 우리 가족의 영상을 탐색하는 경험을 가족에게 선사할 수 있어서 정말 뿌듯했습니다.
보너스: 월 1달러 이하로 비용 낮추기
홈 비디오는 몇 달에 한 번씩만 보게 되는 영상입니다. 가족 전체가 사이트에 접속한 시간은 1년에 약 20시간이었지만, 서버는 24시간 내내 실행되고 있었습니다. 99.7%의 시간 동안 놀고 있는 서버에 매달 15달러를 내고 있었던 셈입니다.
2018년 말 Google은 Cloud Run을 출시했습니다. 이 서비스의 핵심 기능은 HTTP 요청에 응답할 수 있을 만큼 빠르게 Docker 컨테이너를 실행하는 것이었습니다. 덕분에 서버를 대기 모드로 두었다가 누군가 URL을 방문할 때만 실행할 수 있었습니다. 저처럼 자주 접속하지 않는 애플리케이션의 경우 비용이 월 15달러에서 연간 몇 센트로 줄어들었습니다.
이제는 이유가 기억나지 않지만 Cloud Run은 제 MediaGoblin 이미지와 작동하지 않았습니다. 하지만 Cloud Run이 존재한다는 사실을 보며 Heroku에도 비슷한 서비스가 무료로 제공된다는 것이 떠올랐고, Heroku의 도구는 Google의 도구보다 훨씬 사용하기 쉬웠습니다.
무료 앱 서버를 사용하니 유일한 비용은 데이터 저장 비용입니다. Google의 표준 리전 스토리지는 GB당 2.3센트이고, 영상 컬렉션은 33GB를 차지하므로 매달 0.77달러만 지불합니다.

이 전체 해결책에 드는 비용은 한 달에 0.77달러뿐입니다.
이 작업을 시작하려는 사람을 위한 팁
분명 이 과정에는 오랜 시간이 걸렸지만, 이 글이 다른 사람들이 홈 비디오를 디지털화하고 공유하는 데 드는 노력의 80~90%를 줄여 주기를 바랍니다. 다음 절에는 제 해결책의 세부 사항을 담은 자세한 단계별 안내가 있지만, 홈 비디오를 디지털화하고 공유할 때 도움이 될 일반적인 팁을 먼저 소개하겠습니다.
- 원본을 캡처하고 편집하는 단계에서 가능한 한 많은 메타데이터를 기록합니다.
- 테이프에 붙은 라벨에는 유용한 정보가 적혀 있는 경우가 많습니다.
- 각 클립이 어느 테이프에서 어떤 순서로 나온 것인지 기록해 둡니다.
- 녹화 날짜를 추정할 수 있는 클립 속 단서를 적어 둡니다.
- 원본 캡처는 전문가에게 맡기는 것도 고려합니다.
- 영상 디지털화 업체와 같은 품질을 직접 구현하기는 정말 어렵고 비용도 많이 듭니다.
- 다만 EverPresent라는 업체는 피하십시오(자세한 내용이 궁금하면 이메일을 보내 주세요).
- 직접 캡처한다면 디스크 공간을 넉넉히 마련합니다.
- 압축하지 않은 영상 캡처 파일은 표준 화질 영상 기준으로 분당 약 100~200MB입니다.
- 저는 모든 파일을 10TB 용량의 Synology DS412+에 저장했습니다.
- 메타데이터는 특정 애플리케이션에 종속되지 않는 형식으로 기록합니다.
- 클립 설명, 타임 코드, 날짜 등입니다.
- 애플리케이션별 형식으로 보관하면(더 나쁜 경우는 아예 버리면) 다른 해결책을 선택했을 때 작업을 재현할 수 없습니다.
- 편집하면서 영상을 보면 유용한 메타데이터를 많이 확인할 수 있습니다. 기록하지 않으면 잃게 됩니다.
- 영상에서 무슨 일이 일어나고 있는가?
- 누가 나오는가?
- 언제 녹화했는가?
- 좋아하는 영상에 표시를 해 둡니다.
- 솔직히 말해 대부분의 홈 비디오 영상은 꽤 지루합니다.
- 저는 좋아하는 클립에 “best of” 태그를 붙이고, 재미있는 영상을 보고 싶을 때 그 영상들만 찾아봅니다.
- 가능한 한 빨리 처음부터 끝까지 이어지는 전체 해결책을 구축합니다.
- 저는 먼저 모든 테이프를 캡처한 다음, 모든 테이프를 편집하는 식으로 진행하려 했습니다.
- 돌이켜 보면 처음부터 테이프 하나를 골라 공유하는 데 필요한 작업을 모두 해 봤어야 했습니다. 그러면 초기에 내린 결정이 최종 결과에 어떤 영향을 미치는지 알 수 있었을 것입니다.
- 트랜스코딩을 최소화합니다.
- 클립을 편집하거나 다시 인코딩할 때마다 화질이 저하됩니다.
- 원본 영상은 가능한 한 높은 품질로 캡처한 다음, 각 클립을 브라우저에서 별도 플러그인 없이 재생할 수 있는 형식으로 정확히 한 번만 트랜스코딩합니다.
- 영상 클립을 공유할 때는 가능한 한 간단한 해결책을 사용합니다.
- 모음 영상을 만듭니다.
- 영상 모음은 여러 홈 비디오에서 최고의 순간을 한데 모으는 재미있는 방법입니다.
- 모음 영상에서는 음악이 전부입니다. The National의 “Slow Show”는 모음 영상에 정말 잘 어울리는데, 아직 아무도 그 사실을 알아채지 못한 것 같습니다.
내 작업 과정을 처음부터 끝까지 따라 하는 안내
제가 이 작업을 어떻게 했는지 세세한 내용이 궁금하다면, 처음부터 끝까지 전체 작업 흐름을 보여 주는 튜토리얼을 작성해 두었습니다. 제 작업 과정을 재현하는 데 필요한 모든 소스 코드와 명령어가 포함되어 있습니다.
일러스트: Loraine Yow.
이 영상과 사진 일부를 공유할 수 있게 허락해 주고, 애초에 모든 것을 녹화해 주었으며, 이 과정 내내 큰 응원을 보내 준 가족에게 특별히 감사드립니다.
글을 무작위로 읽기