1. 개요
브라우저에서 동기식 파일 I/O를 다루려면 저장 위치, 실행 컨텍스트, I/O 방식이라는 세 문제를 함께 풀어야 합니다. OPFS는 브라우저 내부 전용 저장 공간을 제공하고, Web Worker는 블로킹 작업을 메인 스레드 밖에서 실행하게 하며, FileSystemSyncAccessHandle은 OPFS 파일을 동기식으로 읽고 쓰게 해줍니다. Pyodide의 브라우저 파일 I/O 이슈에서도 이 세 개념이 함께 등장합니다.
핵심 확인 포인트는 다음입니다.
OPFS가 왜 필요한지, 기존 방식과 어떻게 다른지Web Worker가 파일 I/O 와 어떤 관계인지FileSystemSyncAccessHandle이 어떤 문제를 해결하는지- 세 개념이 왜 항상 같이 등장하는지
2. 왜 OPFS 가 필요한가
브라우저의 파일 처리는 오랫동안 <input type="file"> 이나 FileReader 기반의 제한적 API 에 의존했습니다. File API는 사용자가 file input이나 drag and drop으로 제공한 파일을 읽고 처리하는 쪽에 가깝습니다.
이후 File System Access API 가 등장해 사용자 동의 기반으로 로컬 파일을 읽고 쓸 수 있게 됐지만, 사용자에게 보이는 파일 시스템을 다루는 것과 origin 내부 전용 저장소는 목적이 다릅니다. OPFS는 origin별 private storage endpoint로, 브라우저가 관리하는 앱 전용 저장소에 가깝습니다.
OPFS 는 이 문제를 해결하기 위해 나왔습니다. OPFS(Origin Private File System)는 각 origin(웹사이트 주소) 단위로 격리된 브라우저 내부 전용 파일 시스템입니다. 사용자에게 직접 노출되지 않는 저장소라 사용자 visible file system보다 권한·보안 흐름이 단순하고, Worker 안의 동기 접근 핸들과 결합하면 WASM 기반 앱에서 파일 I/O 모델을 맞추기 좋습니다.
- 용량 제한이 있습니다 — IndexedDB 처럼 브라우저 스토리지 쿼터를 따르며
navigator.storage.estimate()로 사용량 확인. - 사이트 데이터를 지우면 삭제됩니다.
- 사용자가 파일 경로를 찾을 수 없습니다.
- 동기식 API 를 제공합니다 — 다른 메서드는 전부 async 인데 OPFS 만
FileSystemSyncAccessHandle동기 접근 지원. 단, Dedicated Web Worker 안에서만 가능.
NOTE
OPFS 는 origin 단위로 격리된 브라우저 내부 전용 파일 시스템입니다. 보안 검사·권한 요청이 없어 async overhead 없이 접근할 수 있고, Dedicated Web Worker 환경에서 동기식 파일 접근도 가능합니다.
3. 세 가지 개념
OPFS, Web Worker, FileSystemSyncAccessHandle 은 각자 다른 관점의 개념입니다.
3.1. OPFS — 어디에 저장하는가
OPFS 는 브라우저가 관리하는 샌드박스 파일 시스템입니다. origin 마다 독립 저장 공간을 가지며 파일 탐색기에선 보이지 않습니다. 접근 방법은 둘 — 메인 스레드·Worker 모두 가능한 async API, 그리고 Worker 안에서만 가능한 FileSystemSyncAccessHandle입니다.
// async API로 OPFS 접근 (메인 스레드에서도 가능)
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('data.bin', { create: true });
// 쓰기
const writable = await fileHandle.createWritable();
await writable.write('hello');
await writable.close();
// 읽기
const file = await fileHandle.getFile();
const text = await file.text();TIP
OPFS 는 "어디에 저장하느냐"의 문제입니다. origin 단위로 격리된 브라우저 내부 파일 시스템이며, 접근은 async API(메인/Worker)와 FileSystemSyncAccessHandle(Worker 전용) 두 가지입니다.
3.2. Web Worker — 누가 실행하는가
자바스크립트는 기본 싱글 스레드입니다. 모든 코드가 메인 스레드에서 실행되고 이 스레드가 UI 렌더링·이벤트 처리도 담당하므로, 무거운 연산이나 블로킹 I/O 를 메인 스레드에서 돌리면 화면이 멈춥니다. Web Worker 는 메인 스레드와 분리된 별도 스레드를 만들고, DOM에 직접 접근하지 않는 대신 postMessage() 기반 메시지로 통신합니다.
// 메인 스레드
const worker = new Worker('worker.js');
worker.postMessage({ type: 'start' });
worker.onmessage = (e) => console.log('결과:', e.data);
// worker.js
self.onmessage = async (e) => {
// 여기서 블로킹 작업 수행
self.postMessage({ result: '완료' });
};TIP
Web Worker 는 "누가 실행하느냐"의 문제입니다. 메인 스레드 블로킹을 막기 위해 동기 I/O 는 Worker 안에서만 허용됩니다.
3.3. FileSystemSyncAccessHandle — 어떻게 읽고 쓰는가
FileSystemSyncAccessHandle 은 OPFS 파일 접근 방법 중 하나로, 이름 그대로 동기식(Sync) 으로 읽고 씁니다. read, write, getSize, flush, truncate, close 같은 메서드는 동기식으로 동작합니다. 단, 이 핸들은 OPFS 파일에만 생성할 수 있고 Dedicated Web Worker 안에서만 접근할 수 있습니다.
// Worker 안에서
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('fast.bin', { create: true });
// 핸들 생성은 async (한 번만)
const accessHandle = await fileHandle.createSyncAccessHandle();
// 이 아래는 전부 동기 — await 없음
const encoder = new TextEncoder();
const buf = encoder.encode('Some data');
accessHandle.write(buf, { at: 0 }); // 동기 쓰기
accessHandle.flush(); // 디스크에 반영
const size = accessHandle.getSize(); // 동기 크기 조회
const view = new DataView(new ArrayBuffer(size));
accessHandle.read(view, { at: 0 }); // 동기 읽기
accessHandle.close();read()/write() 는 블로킹이라 메인 스레드를 멈출 수 있어, 스펙상 Dedicated Web Worker 안에서만 사용 가능합니다. createSyncAccessHandle() 메서드 자체는 Promise를 반환하는 비동기 호출이지만, 반환된 핸들의 파일 작업 메서드가 동기식입니다.
WARNING
FileSystemSyncAccessHandle 은 스펙상 Dedicated Web Worker 안에서만 쓸 수 있습니다. 메인 스레드에서 createSyncAccessHandle() 를 호출하면 에러가 납니다.
4. 세 개념이 맞물리는 구조
각자 다른 문제를 풀지만 "브라우저에서 동기식 파일 I/O 구현"이라는 목표엔 셋이 동시에 필요합니다.
| 해결해야 할 문제 | 담당 개념 |
|---|---|
| 빠르고 권한 없이 파일을 영속 저장 | OPFS |
| 동기식 read / write 구현 | FileSystemSyncAccessHandle |
| 블로킹 호출이 UI 를 멈추지 않게 | Web Worker |
하나라도 빠지면 안 됩니다.
- OPFS 대신 File System Access API → 사용자 로컬 파일 접근은 되지만 보안 검사로 느리고 매번 권한 팝업. (목적이 다름: File System Access API=사용자 파일, OPFS=브라우저 내부 전용)
FileSystemSyncAccessHandle없이 async 만 → 동기식f.read()패턴 불가- Worker 없이 메인 스레드 → 블로킹 호출이 UI 정지
Sync I/O 실행 흐름 다이어그램
TIP
OPFS 는 저장 위치, Web Worker 는 실행 컨텍스트, FileSystemSyncAccessHandle 은 I/O 방식입니다. 서로 다른 관점이고 각자 다른 문제를 해결합니다.
5. OPFS 접근 API 비교
| API | 동기 여부 | OPFS 전용 | 읽기/쓰기 | 권장 컨텍스트 |
|---|---|---|---|---|
getFile() | async | ❌ | 읽기만 | 메인 스레드 / Worker |
createWritable() | async | ❌ | 쓰기만 | 메인 스레드 / Worker |
createSyncAccessHandle() | sync | ✅ OPFS만 | 읽기 + 쓰기 | Worker 권장 |
6. 마무리
처음 Pyodide의 브라우저 파일 I/O 맥락을 읽을 땐 세 개념이 뭉쳐 하나처럼 느껴졌지만, 관점별로 분리하면 명확해집니다.
- OPFS — 어디에 저장하느냐 (저장 위치)
- Web Worker — 누가 실행하느냐 (실행 컨텍스트)
- FileSystemSyncAccessHandle — 어떻게 읽고 쓰느냐 (I/O 방식)
TIP
세 개념은 각자 다른 문제를 해결하면서, "브라우저에서 동기식 파일 I/O"라는 하나의 목표를 위해 맞물립니다.
7. 참고문헌
- MDN — File APIdeveloper.mozilla.org
- MDN — Origin private file systemdeveloper.mozilla.org
- MDN — FileSystemSyncAccessHandledeveloper.mozilla.org
- MDN — FileSystemFileHandle: createSyncAccessHandle()developer.mozilla.org
- MDN — Web Workers APIdeveloper.mozilla.org
- MDN — Using Web Workersdeveloper.mozilla.org
- MDN — Introducing workersdeveloper.mozilla.org
- Pyodide Issue #5862 — Sync native FS via FileSystemSyncAccessHandle & OPFSgithub.com