268킬로바이트.
2024년 Video.js의 용량이었다. 원래 만든 사람인 스티브 헤퍼넌이 16년 만에 프로젝트를 되찾아오기 직전의 수치다. 그리고 그는 분명 Brightcove에서는 아무도 묻지 않았던 질문을 던졌다: 이 코드 중에 아직 존재해야 할 게 있긴 한 건가?
답은, 알고 보니, 대부분 아니오였다. 프로젝트 공식 블로그에서 발표된 Video.js 9는 압축(minified)·gzip 기준 약 32KB로 출시된다. 88% 감소. 기발한 트리 셰이킹 기법이나 빌드 도구 최적화 덕분이 아니다. 코드를 삭제해서다. 브라우저가 몇 년 전에 이미 해결한 문제들을 풀고 있던 수천, 수만 줄의 코드를.
이틀째 diff를 들여다보고 있는데, 이게 우리 나머지 의존성 트리에 대해 무엇을 말해주는지 생각을 멈출 수가 없다.
아무도 들여다보고 싶지 않았던 역사
Video.js는 2010년에 시작됐다. 그 시절 웹 개발을 했다면 당시 상황을 기억할 것이다: Flash가 여전히 웹 비디오의 기본이었고, HTML5 <video>는 문서상으로만 존재하는 스펙이었으며, 브라우저마다 코덱에 대한 각자의 소신이 있었다. Video.js는 실제로 존재하는, 고통스러운 문제를 해결했다. Flash 폴백과 함께 브라우저 간 일관된 플레이어를 제공했다.
스티브 헤퍼넌이 만들었고, 이후 Brightcove—생소하다면 말해두자면 비디오 호스팅 회사다—가 사실상 프로젝트를 입양했다. 헤퍼넌은 떠났다. Brightcove가 유지보수했다. 뭐, 일종의.
여기서 이야기가 흥미로워진다. Video.js 블로그 포스트와 Hacker News에서 거의 500포인트를 찍은 토론에 따르면, 프로젝트가 쇠퇴한 건 Brightcove가 악의적이어서가 아니다. Brightcove가 무관심했기 때문이다. 오픈소스 인프라를 유지보수하는 데 대한 기업의 인센티브 구조는, 관대하게 표현하자면, 어긋나 있다. Brightcove는 Video.js가 Brightcove를 위해 작동하기만 하면 됐다. 나머지는 다른 누군가의 문제였다.
그래서 코드베이스는 비대해졌다. IE8용 폴리필이 IE8이 사라진 지 한참 후에도 남아있었다. Flash 재생을 지원하기 위해 만든 추상화 레이어가 Flash가 죽은 후에도 그대로였다. 풀스크린 토글 같은 기능의 자체 구현이 Fullscreen API가 보편적으로 지원된 이후에도 유지됐다. 프로젝트는 해결된 문제들의 박물관이 되었고, 각 전시물은 수백만 웹사이트의 프로덕션 번들 안에 정성스럽게 보존되어 있었다.
88% 감소의 해부학
구체적으로 말해보자. "88% 작아졌다"는 당연히 의심을 부르는 종류의 숫자다. 마땅히 그래야 한다.
GitHub 리포지토리에 문서화된 Video.js 9 리라이트는 다음을 제거하거나 대체했다:
- 자체 UI 컴포넌트 시스템 전체. Video.js는 자체 컴포넌트 계층 구조와 자체 라이프사이클 메서드를 가지고 있었다. 2010년에는 합리적이었다. 2026년에는 브라우저 네이티브
<video>컨트롤이 대부분의 경우에 충분하고, 커스텀 UI가 필요하다면 이미 컴포넌트 라이프사이클을 다루는 프레임워크를 쓰고 있을 것이다.
- Flash 지원. 당연히. 하지만 여기서 "당연히"가 상당히 많은 일을 하고 있다. 이 코드는 모든 주요 브라우저가 Flash를 버린 지 수년 후에도 살아남아 있었으니까.
- 자체 이벤트 시스템. Video.js는 자체 이벤트 위임과 버블링을 구현했다. DOM의
addEventListener가 모든 브라우저에서 올바르게 작동한 지 10년이 넘었다.
- 폴리필. 정말 많은 폴리필.
requestAnimationFrame용,classList용, 폴리필이 필요한 줄도 몰랐던 것들용.
- 미들웨어 시스템. 내가 파악한 바로는 Brightcove 내부 도구 외에는 사실상 아무도 쓰지 않았던.
남은 것은 네이티브 <video> 엘리먼트를 감싸는 얇은 래퍼로, 진짜 어려운 부분들만 처리한다: 적응형 비트레이트 스트리밍, 접근성, 그리고 실제로 동작을 확장해야 할 때를 위한 플러그인 API.
여기서 나는 멈칫했다. v9 브랜치를 받아서 간단히 비교해봤다:
bash
Video.js 8.x
du -sh node_modules/video.js/dist/
1.2M
Video.js 9
du -sh node_modules/video.js/dist/
142K
오타가 아니다. dist 폴더가 1.2메가바이트에서 142킬로바이트로 줄었다. gzip 전송 크기 감소는 더 극적이다.
아무도 하고 싶지 않은 산수
Video.js는 약 70만 개 이상의 웹사이트에 설치되어 있는 것으로 추정된다. CDN 링크를 스크립트 태그에 넣고 다시는 생각하지 않는 그런 종류의 패키지다. 그 사이트들 하나하나가 대부분 브라우저가 이미 네이티브로 하는 일을 재구현한 4분의 1메가바이트짜리 자바스크립트를 전송하고 있었다.
여기에 페이지 뷰를 곱해라. 느린 연결의 사용자를 곱해라. 그 모든 자바스크립트를 파싱하고 실행하는 제한된 CPU의 모바일 기기를 곱해라.
이건 Video.js만의 문제가 아니다. 모든 것의 문제다.
나는 소규모 Rust 크레이트를 유지보수하는데, 거기서도 아무도 안 쓰는 컴파일러 버전을 위한 호환성 심을 유지하고 있는 나를 발견한 적이 있다. 자바스크립트 세계에서는 죽은 코드의 비용이 사용자가 체감하는 지연 시간으로 측정되는데, 위험은 더 높고 절제력은 어째서인지 더 낮다.
Hacker News 쓰레드에서 내가 반복적으로 봐온 패턴이 드러났다: 누군가 "이 코드는 왜 있죠?"라고 물으면 답은 "지우면 뭔가 깨질 수 있는데, 아무도 뭔가를 깨뜨린 사람이 되고 싶지 않으니까"다. 기업의 관리 방식은 안 깨뜨리는 것에 최적화되어 있다. 개선하는 것에는 최적화되어 있지 않다. 이 둘은 매우 다른 것이다.
기업의 관리가 조용히 실패하는 이유
의견을 의견으로서 밝히겠다: 우리가 오픈소스 프로젝트를 기업에 넘기는 방식은 근본적으로 결함이 있다.
암묵적 거래는 "커뮤니티의 무료 노동을 받는 대신, 불이 꺼지지 않게 유지한다"는 것이다. 그런데 "불이 꺼지지 않게"는 사람마다 다른 의미를 가진다. 기업에게는 CI가 초록불이고 누구도 치명적 CVE를 제출하지 않는다는 뜻이다. 사용자에게는 프로젝트가 타겟으로 하는 플랫폼과 함께 진화한다는 뜻이어야 한다.
Brightcove 하의 Video.js는 전통적인 의미로 썩지 않았다. 테스트는 통과했다. 릴리스는 나갔다. GitHub 리포도 활발했다. 하지만 프로젝트는 수년간의 죽은 짐을 지고 있었다. 아무도 삭제라는 힘들고 화려하지 않은 작업을 할 인센티브가 없었기 때문이다.
스티브 헤퍼넌이 돌아와서 "이거 다 지워라"라고 말할 권한과 의지를 가진 것은 예외적 사례이지, 일반적인 경우가 아니다. 대부분의 원래 만든 이는 돌아오지 않는다. 대부분의 기업 관리자는 물러나지 않는다. 프로젝트는 그냥 천천히 굳어간다.
npm audit를 실행하고 어떤 라이브러리가 아직도 Object.assign을 폴리필하고 있어서 존재하는 서브 의존성의 서브 의존성에서 취약점을 볼 때마다 이 생각이 난다.
더 나은 관리를 위한 템플릿
Video.js 9 리라이트는 우리에게 청사진을 준다. 그리고 예상보다 단순하다:
1. 분기별로 플랫폼 가정을 감사하라. Video.js는 더 이상 존재하지 않는 웹을 위해 만들어졌다. 오래 유지되는 모든 프로젝트는 "이거 이제 브라우저가 네이티브로 하나?"를 정기적으로 점검해야 한다. 연간이 아니라. 분기별로.
2. 중요한 것을 측정하라. 코드 줄 수는 성공 지표가 아니다. 기능 수도 아니다. 번들 크기, 런타임 성능, API 표면적이 당신이 문제를 해결하고 있는지 아니면 만들어내고 있는지를 말해준다.
3. 유지보수자에게 삭제 권한을 줘라. 이건 문화의 문제다. 프로젝트의 기여 가이드라인이 코드를 추가하는 것보다 제거하는 것을 더 어렵게 만든다면, 프로젝트는 커지기만 할 것이다. 엔트로피가 이긴다.
4. 핵심과 생태계를 분리하라. Video.js 9는 상당한 기능을 플러그인으로 밀어냈다. 핵심은 더 적은 일을 한다. 이것이 올바르다. 비디오 플레이어 라이브러리는 비디오를 재생해야 한다. 커스텀 스킨, 애널리틱스 통합, 그런 건 플러그인이다. 모든 사람의 번들에 들어갈 필요가 없다.
참고로, Video.js 8과 Video.js 9를 각각 넣은 테스트 페이지에서 Lighthouse를 돌려봤다. 성능 점수가 12점 올랐다. 비디오 하나만 재생하는 페이지에서. 내 밴쿠버 집의 적당히 빠른 연결에서. 더 느린 연결에서는 그 격차가 상당히 벌어질 것이다.
웹의 하위 호환성 중독
이건 단지 Video.js에 관한 이야기가 아니다. 웹이 자신의 역사와 맺는 관계에 관한 이야기다.
자바스크립트에는 하위 호환성을 신성하게 여기는 문화적 문제가 있다. 라이브러리들은 사용자의 사용자가 몇 년 전에 버린 브라우저를 계속 지원한다. 아무도 호출하지 않는 API를 계속 유지한다. 아무것도 하지 않는 코드를 계속 배포한다. 삭제하는 것이 유지하는 것보다 더 위험하게 느껴지니까.
브라우저는 앞으로 나아갔다. 우리의 도구는 그러지 못했다.
Moment.js도 유지보수자들이 사실상 "다른 걸 쓰세요"라고 말하며 더 가벼운 대안을 가리켰을 때 비슷한 전환점을 맞았다. Lodash는 이제 네이티브 자바스크립트인 유틸리티 함수를 여전히 배포하고 있다. jQuery는, 뭐. 알 것이다.
Video.js가 주목할 만한 이유는 항복하고 사람들에게 다른 걸 쓰라고 하는 대신, 원래 만든 이가 돌아와서 고쳤기 때문이다. 16년 후에. 이것은 영감을 주는 일이거나, 우리가 소프트웨어를 유지보수하는 방식에 대한 통렬한 고발이다. 아마 둘 다일 것이다.
직접 해보라
현재 Video.js 7이나 8을 쓰고 있다면, v9로의 마이그레이션 경로가 프로젝트의 GitHub 리포지토리에 문서화되어 있다. 플러그인 API가 변경되었으니, 커스텀 플러그인이 있다면 작업이 좀 필요할 것이다. 하지만 핵심 <video> 래퍼 API는 대체로 호환된다.
Video.js를 쓰고 있지 않다면, 부탁 하나만 들어달라. 프로젝트에서 의존성 하나를 골라라. 번들 크기를 봐라. 그다음 그게 실제로 뭘 하는지 봐라. 그리고 자문하라: 이거 이제 브라우저가 하지 않나?
node_modules의 얼마나 많은 부분이 2026년에 2012년의 문제를 풀고 있는지 놀라게 될 수도 있다.
Cargo(패키지 매니저가 아니라 내 개, 둘 다 여기서 해당되긴 하지만)가 방금 내 커피를 쏟았는데, 마무리하라는 신호인 것 같다. 요약하자면: Video.js는 무게의 88%를 덜어내고 더 나은 프로젝트가 되었다. 불편한 따름정리는, 당신의 프로젝트도 아마 그럴 수 있다는 것이다. 문제는 삭제 버튼을 누를 권한과 인센티브와 배짱을 가진 사람이 있느냐는 것이다.