브라우저 스토리지 전환 | 비디오 렌더링 최적화
OPFS는 Web File System Access API의 일부로, 브라우저가 특정 Origin(도메인)에 전용으로 제공하는 샌드박스형 가상 파일 시스템입니다. 2022년 이후 주요 브라우저에 표준으로 탑재되었으며, 기존 웹 스토리지 API 대비 월등한 성능을 제공합니다.
// OPFS 루트 디렉토리 획득
const root = await navigator.storage.getDirectory();
// 파일 핸들 생성 (없으면 자동 생성)
const fileHandle = await root.getFileHandle('video-chunk.bin', { create: true });
// 쓰기 스트림 열기
const writable = await fileHandle.createWritable();
await writable.write(videoChunkBuffer); // ArrayBuffer 또는 Blob
await writable.close();
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('video-chunk.bin');
const file = await fileHandle.getFile();
const buffer = await file.arrayBuffer();
// worker.js 내부
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('frame-data.bin', { create: true });
// 동기 접근 핸들 — createSyncAccessHandle은 Worker에서만 사용 가능
const syncHandle = await fileHandle.createSyncAccessHandle();
// 바이트 오프셋 지정 랜덤 쓰기
const offset = frameIndex * FRAME_SIZE;
syncHandle.write(frameBuffer, { at: offset });
// 부분 읽기
const readBuf = new ArrayBuffer(FRAME_SIZE);
syncHandle.read(readBuf, { at: offset });
syncHandle.flush();
syncHandle.close();
const root = await navigator.storage.getDirectory();
// 하위 디렉토리 생성
const projectDir = await root.getDirectoryHandle('my-project', { create: true });
// 파일 목록 순회
for await (const [name, handle] of projectDir.entries()) {
console.log(name, handle.kind); // 'file' | 'directory'
}
| 항목 | IndexedDB | OPFS |
|---|---|---|
| 저장 방식 | 키-값 기반 객체 저장소 (NoSQL) | 파일 시스템 기반 (계층적 디렉토리) |
| API 유형 | 비동기 (async) | 비동기 + 동기 (Worker 내) |
| 대용량 처리 | ❌ 느림 (직렬화/역직렬화 오버헤드) | ✅ 빠름 (원시 바이너리 스트림 직접 접근) |
| 메모리 효율 | ❌ 낮음 (전체 객체 로드) | ✅ 높음 (부분 읽기/쓰기 가능) |
| Worker 지원 | 일부 지원 | ✅ 완전 지원 (동기 API 사용 가능) |
| 랜덤 접근 | 키 기준만 가능 | ✅ 바이트 오프셋 기반 직접 접근 |
| 트랜잭션 | ✅ 지원 (ACID 보장) | ❌ 미지원 |
| 복잡한 쿼리 | ✅ 인덱스 기반 검색 가능 | ❌ 미지원 (파일 단위 접근) |
| 브라우저 지원 | ✅ 모든 주요 브라우저 | Chrome 86+, Safari 15.2+, Firefox 111+ |
| 주요 활용처 | 구조화된 메타데이터, 설정값, 소량 데이터 | 대용량 바이너리 (영상, 오디오, 이미지) |
createSyncAccessHandle()로 Worker 내 동기식 접근 가능✅ 결론: 비디오 편집 렌더링에는 OPFS + Web Worker 조합이 적합합니다.
비디오 편집 렌더링 파이프라인은 다음과 같은 특성을 갖습니다.
OPFS는 이 모든 요구사항을 충족합니다. 수 GB 파일을 직접 핸들링할 수 있고, at 옵션으로 특정 프레임 바이트 오프셋에 직접 접근하며, Web Worker 내 동기 API로 렌더링 루프를 단순화할 수 있습니다.
// 메인 스레드: 타임라인 편집 UI
const worker = new Worker('render-worker.js');
worker.postMessage({ type: 'START_RENDER', config: renderConfig });
// render-worker.js: OPFS 기반 렌더링 루프
const root = await navigator.storage.getDirectory();
const outHandle = await root.getFileHandle('output.mp4', { create: true });
const syncHandle = await outHandle.createSyncAccessHandle();
for (let frame = 0; frame < totalFrames; frame++) {
const frameData = renderFrame(frame); // CPU 렌더링
syncHandle.write(frameData, { at: frame * FRAME_BYTE_SIZE });
}
syncHandle.flush();
syncHandle.close();
비디오 편집 도구라도 역할을 나눠 두 기술을 병용하는 것이 최선입니다.
| IndexedDB 담당 | OPFS 담당 |
|---|---|
| 프로젝트 메타데이터 | 원본 영상 파일 캐시 |
| 클립 목록 및 타임라인 정보 | 렌더링 출력 파일 |
| 사용자 설정 및 환경설정 | 프레임 버퍼 / 썸네일 이미지 |
| 편집 히스토리 (Undo/Redo) | 오디오 웨이브폼 데이터 |
📋 핵심 정리
IndexedDB는 구조화된 메타데이터 관리에, OPFS는 대용량 바이너리 파일 처리에 각각 최적화되어 있습니다. 비디오 편집 렌더링처럼 대규모 I/O가 필요한 환경에서는 OPFS로의 마이그레이션이 성능과 메모리 효율 모두에서 올바른 선택이라 생각합니다.
createSyncAccessHandle 활용)