
동영상 압축은 서버에서 하는 것이라는 고정관념이 있었습니다. 그런데 브라우저에서 직접 동영상을 압축할 수 있다는 것을 알게 됐습니다.
FFmpeg.wasm을 알게 된 계기
이미지 압축 도구를 먼저 만들었습니다. Canvas API로 브라우저에서 이미지를 처리하는 것은 비교적 간단했습니다.
동영상은 달랐습니다. 동영상은 수십~수백 개의 이미지 프레임에 오디오까지 합쳐진 복잡한 데이터입니다. Canvas API로 처리하기에는 한계가 있었습니다.
조사하다가 FFmpeg.wasm을 발견했습니다. FFmpeg를 WebAssembly로 컴파일해서 브라우저에서 실행할 수 있게 만든 라이브러리입니다.
설치와 첫 번째 벽
설치는 간단합니다.
npm install @ffmpeg/ffmpeg @ffmpeg/util
그런데 실행하면 바로 에러가 납니다.
SharedArrayBuffer is not defined
FFmpeg.wasm은 멀티스레딩을 위해 SharedArrayBuffer가 필요합니다. 이 API는 보안상의 이유로 특정 HTTP 헤더가 있어야만 활성화됩니다.
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Next.js 설정 파일에 이 헤더를 추가했습니다.
// next.config.js
headers: [
{
source: "/(.*)",
headers: [
{ key: "Cross-Origin-Opener-Policy", value: "same-origin" },
{ key: "Cross-Origin-Embedder-Policy", value: "require-corp" },
],
},
]
로컬에서는 됩니다. 그런데 Vercel에 배포하면 또 안 됩니다. Vercel의 경우 vercel.json에도 동일한 헤더 설정이 필요했습니다.
초기 로딩 시간 문제
FFmpeg.wasm의 코어 파일은 약 30MB입니다. 처음 사용할 때 이 파일을 다운로드해야 합니다.
사용자가 동영상 압축 버튼을 처음 누르면 30초 가까이 기다려야 합니다. 이건 UX 문제입니다.
해결책은 두 가지였습니다.
로딩 상태 표시: 진행 상황을 알 수 있게 프로그레스바와 "FFmpeg 로딩 중 (28MB)..." 같은 메시지를 표시했습니다.
캐싱 활용: 한 번 다운로드하면 브라우저 캐시에 저장됩니다. 다음 방문부터는 즉시 로드됩니다.
가상 파일시스템 이해하기
FFmpeg는 파일을 읽고 씁니다. 브라우저에는 파일시스템이 없습니다. FFmpeg.wasm은 메모리 안에 가상 파일시스템을 제공합니다.
// 파일을 가상 파일시스템에 쓰기
await ffmpeg.writeFile('input.mp4', await fetchFile(file));
// FFmpeg 명령 실행
await ffmpeg.exec([
'-i', 'input.mp4',
'-crf', '28', // 압축 품질 (0=무손실, 51=최대압축)
'-preset', 'fast', // 인코딩 속도
'output.mp4'
]);
// 결과 파일 읽기
const data = await ffmpeg.readFile('output.mp4');
// 다운로드 링크 생성
const blob = new Blob([data.buffer], { type: 'video/mp4' });
const url = URL.createObjectURL(blob);
처리 시간 문제
100MB 동영상을 압축하는 데 얼마나 걸릴까요?
서버에서 FFmpeg를 돌리면 1
2분 정도입니다. 브라우저에서는? 기기마다 다르지만 5
15분이 걸릴 수 있습니다.
서버는 고성능 CPU를 사용하지만 브라우저는 사용자 기기의 CPU를 사용합니다. 구형 노트북이라면 더 오래 걸립니다.
이 한계를 솔직하게 UI에 표시했습니다. "파일 크기에 따라 수 분이 걸릴 수 있습니다. 탭을 닫지 마세요."
진행 상황 표시하기
FFmpeg.wasm은 처리 진행 상황을 알 수 있는 콜백을 제공합니다.
ffmpeg.on('progress', ({ progress }) => {
setProgress(Math.round(progress * 100));
});
0에서 100까지 프로그레스바가 채워지는 것을 보여주면 사용자가 기다리기 훨씬 쉬워집니다.
결과
동영상 압축, 포맷 변환(MP4·MOV·AVI·WebM), GIF 변환, 오디오 추출까지 구현했습니다. 모두 브라우저에서 처리되고 파일이 서버로 전송되지 않습니다.
처음 FFmpeg.wasm을 접했을 때 "이게 가능하다고?" 했는데, 실제로 돌아가는 것을 보면 아직도 신기합니다.
'Dev' 카테고리의 다른 글
| 브라우저가 생각보다 강력한 이유 — Canvas API FFmpeg PDF 모두 브라우저에서 (0) | 2026.10.02 |
|---|---|
| 무료로 웹서비스를 운영하는 방법 — Vercel Next.js 조합 (0) | 2026.09.29 |
| PDF가 편집하기 어려운 이유 — 어도비가 PDF를 만든 방식 (0) | 2026.09.24 |
| 웹서비스 처음 만들면서 놀란 것 5가지 (0) | 2026.09.21 |