심층 분석

WebAssembly 설명: 해결하는 문제와 한계

koboshiCo-founder
·6분 읽기
WebAssembly 설명: 해결하는 문제와 한계
요약

WebAssembly는 JavaScript를 대체하려는 것이 아니다. C, C++, Rust 등으로 컴파일한 코드를 브라우저에서 네이티브에 가까운 속도로 실행할 수 있게 해주는 컴팩트한 바이너리 명령어 형식이다. 이 글에서는 순수 웹 앱의 한계, WebAssembly가 해결하는 문제, 실제 트레이드오프, 흔한 사용 사례, 그리고 클라이언트 사이드 HEIC 변환이 WebAssembly 없이는 왜 불가능한지 설명한다.

HEIC 사진을 브라우저 변환 도구에 끌어 넣어보자. 이 파일은 HEVC로 인코딩된 이미지 데이터를 담은 컨테이너다 — 4K 영상에서도 쓰이는 그 H.265 압축이다. 브라우저에는 내장 HEVC 디코더가 없고, JavaScript로 디코더를 처음부터 만든다면 느리고, 깨지기 쉽고, 용량도 엄청날 것이다. 정작 필요한 디코더는 strukturag가 유지보수하는 C++ 라이브러리인 libheif다. WebAssembly는 이 C++ 디코더가 브라우저 탭 안에서 돌아갈 수 있게 해주는 기술이다.

이 한 예제가 핵심을 짚어준다. WebAssembly는 새로운 프로그래밍 언어도, JavaScript를 대체하려는 기술도 아니다. 이식 가능한 바이너리 명령어 형식이자, JavaScript와 나란히 브라우저 안에서 실행되는 샌드박스 환경이다.

WebAssembly란 무엇인가

WebAssembly(Wasm)은 W3C 표준이다. 컴팩트한 바이너리 명령어 형식과 스택 기반 가상 머신을 정의한다. 보통 사람이 직접 Wasm을 작성하지는 않는다. C, C++, Rust, Go, Zig, C# 등 점점 늘어나는 언어에서 이를 대상으로 컴파일한다. 그 결과는 .wasm 모듈이 되고, 브라우저는 이를 다운로드해 검증한 뒤 메모리 안전한 샌드박스 안에서 실행한다.

모든 주요 브라우저는 2017년부터 WebAssembly를 지원한다. 모듈 형식은 다음과 같이 설계되었다.

  • 컴팩트: 바이너리 형식이라 동등한 JavaScript 소스보다 파싱이 빠르다.
  • 고속: 브라우저가 사전에 기계어로 컴파일한다.
  • 안전: 각 모듈은 격리된 선형 메모리와 경계 검사를 갖는다.
  • 언어 중립: Wasm 명령어 집합을 대상으로 할 수 있는 언어는 웹에서 실행할 수 있다.

기존 웹의 한계

JavaScript는 사용자 인터페이스, 네트워크 요청, glue 코드를 짜기에 좋은 언어다. 동적 타입에 가비지 컬렉션과 JIT 컴파일을 사용한다. 이런 특성 덕분에 유연하지만, 무거운 연산에서는 예측하기 어렵다.

가비지 컬렉션 일시 정지는 애니메이션 도중 UI 스레드를 멈출 수 있다. JIT 워밍업은 같은 코드가 다른 시점에 다른 속도로 실행되게 한다. 수치나 비트 단위 작업 — 이미지 디코딩, 동영상 인코딩, 암호화, 물리 시뮬레이션, 대규모 행렬 연산 — 에서 JavaScript는 같은 로직을 C나 Rust에서 컴파일한 것보다 종종 한 차례 이상 느리다.

또 다른 한계는 생태계에 갇혀 있다는 점이다. 수십 년에 걸쳐 쌓인 이미지, 오디오, 동영상, 과학 라이브러리들이 C와 C++로 작성되어 있다. 이를 JavaScript로 다시 작성하는 것은 웹에 그 기능을 가져오는 현실적인 방법이 아니다.

WebAssembly가 존재하는 이유

Wasm은 두 가지 문제를 동시에 해결하기 위해 만들어졌다.

  1. 기존 네이티브 코드를 브라우저에서 실행할 수 있다. JavaScript로 다시 작성하지 않아도 된다.
  2. 웹 앱에 예측 가능한 고성능 실행 계층을 제공한다. JavaScript가 원래 맡도록 설계되지 않은 영역에서 말이다.

이는 명백히 JavaScript의 동반자이지 후계자가 아니다. DOM, 네트워크 스택, 대부분의 API는 여전히 JavaScript가 담당한다. Wasm은 격리된 연산을 처리하고, JavaScript는 조율을 담당한다.

WebAssembly가 해결하는 것

  • CPU 집약적 워크로드: 이미지 디코딩, 동영상 트랜스코딩, 물리 연산, 데이터 암호화.
  • 예측 가능한 성능: 가비지 컬렉터도 없고 JIT 워밍업 곡선도 없다.
  • 코드 재사용: libheif, FFmpeg, SQLite, OpenCV 같은 검증된 라이브러리를 브라우저로 가져온다.
  • 개인정보 보호: 민감한 데이터를 서버에 업로드하지 않고 로컬에서 처리할 수 있다.
  • 오프라인 실행: .wasm 모듈을 한 번 다운로드하면 네트워크 왕복 없이 실행된다.

장점과 단점

영역장점한계
성능수치 및 메모리 중심 작업에서 네이티브에 가까운 속도네이티브보다 빠르지 않으며, 브라우저와 기기의 제약을 여전히 받는다
이식성하나의 모듈이 모든 브라우저와 OS에서 실행된다DOM과 대부분의 브라우저 API에는 JavaScript 호스트가 필요하다
보안경계 검사가 있는 샌드박스 선형 메모리다른 브라우저 코드처럼 Spectre 유형의 사이드 채널에 취약하다
생태계기존 C/C++/Rust 코드베이스를 재사용한다JavaScript와의 상호운용성은 복잡성과 오버헤드를 더한다
시작바이너리 파싱이 빠르다첫 로드 시 모듈 다운로드와 인스턴스화 비용이 클 수 있다

솔직한 결론부터 말하면, Wasm은 브라우저가 예전에는 할 수 없던 일을 가능하게 하지만, 사용자 기기 위에서 돌아간다는 제약은 사라지지 않는다.

일반적인 사용 사례

웹 앱이 로컬에서 무거운 작업을 해야 할 때 WebAssembly는 기본 선택이 되었다.

  • 이미지 및 동영상 편집: Figma, Photopea 등의 도구가 렌더링과 효과에 Wasm을 사용한다.
  • 동영상 트랜스코딩: FFmpeg.wasm은 브라우저 안에서 FFmpeg을 실행해 포맷 변환을 한다.
  • 광학 문자 인식: Tesseract.js는 Tesseract OCR 엔진을 Wasm으로 포팅했다.
  • 게임: Unity, Godot, Unreal Engine을 Wasm으로 내보낼 수 있어 브라우저 기반 게임을 만들 수 있다.
  • 데이터 분석: Pyodide는 Python과 NumPy, Pandas, SciPy 같은 과학 패키지를 가져오고, sql.js는 브라우저에서 SQLite를 실행한다.
  • 머신러닝 추론: ONNX Runtime Web은 Wasm 백엔드를 사용해 CPU 추론을 수행한다.
  • CAD 및 3D 뷰어: 과거 데스크톱 전용이던 복잡한 기하학 커널.
  • 암호화: 클라이언트 사이드에서 실행되는 해싱, 암호화, 영지식 증명.

일반적인 WebAssembly 기반 라이브러리

라이브러리출처기능
heic-toEmscripten을 통한 libheif브라우저에서 HEIC/HEIF 이미지를 디코딩해 JPEG, PNG, 비트맵으로 변환한다.
FFmpeg.wasmEmscripten을 통한 FFmpeg완전히 클라이언트 사이드에서 오디오/동영상 트랜스코딩, 리먹싱, 필터링을 수행한다.
Tesseract.jsEmscripten을 통한 Tesseract OCR브라우저에서 이미지 속 텍스트를 인식한다.
sql.jsEmscripten을 통한 SQLite메모리 내 파일을 사용해 브라우저에서 전체 SQLite 데이터베이스 엔진을 실행한다.
PyodideEmscripten을 통한 CPython백엔드 없이 Python과 NumPy, Pandas, SciPy 같은 패키지를 실행한다.
ONNX Runtime WebEmscripten을 통한 ONNX RuntimeONNX 머신러닝 모델을 CPU(Wasm) 또는 GPU(WebGL/WebGPU)에서 실행한다.

표의 모든 항목은 같은 패턴을 따른다. 검증된 네이티브 프로젝트를 Wasm으로 컴파일하고, JavaScript API로 감싸는 것이다. 실무에서 WebAssembly의 가치는 바로 여기에 있다.

HEIC를 JPG/PNG/WebP로 온라인 변환하려면 WebAssembly가 필요한 이유

HEIC는 코덱이 아니라 컨테이너다. 실제 이미지 데이터는 HEVC로 인코딩되어 있다. 이 코덱은 계산 비용이 크고 특허 제약도 따른다. 웹 페이지가 사용할 수 있는 내장 HEVC 디코더를 탑재한 브라우저는 없다. 서버 사이드 변환이 명백한 대안이지만, 사용자의 사진을 업로드해야 한다.

대안은 libheif, 즉 데스크톱 도구에서 쓰는 것과 같은 디코더를 WebAssembly로 컴파일해 배포하는 것이다. 우리의 HEIC to JPG 변환기를 사용하면 디코딩은 브라우저 안에서 일어난다:

  1. ftyp 박스 서명을 읽어 파일을 검증한다.
  2. libheif Wasm 모듈이 HEVC 비트스트림을 로컬에서 디코딩한다.
  3. 디코딩된 비트맵을 캔버스에 그린 뒤 JPEG로 내보낸다.

같은 모듈은 투명도를 지원하는 무손실 출력을 위한 HEIC to PNG와 웹에 최적화된 크기를 위한 HEIC to WebP에도 쓰인다. 업로드도, 서버 처리도, 제3자가 이미지를 보는 일도 없다.

이 워크플로우는 WebAssembly가 실제 이미지 디코더를 실행할 수 있기 때문에 가능하다. JavaScript만으로는 HEVC를 합리적인 시간 안에 파싱할 수 없고, 서버에 업로드하면 개인정보 보호라는 약속이 깨진다. 가능하게 해주는 것은 바로 Wasm이다.

WebAssembly의 미래

Wasm은 브라우저를 넘어 확장되고 있다. WebAssembly System Interface(WASI)는 이식 가능한 시스템 인터페이스를 정의해 Wasm 모듈이 서버, 엣지 워커, IoT 기기에서 실행되게 한다. Wasmtime, Wasmer, WasmEdge 같은 런타임은 이미 브라우저 밖에서 프로덕션에 배포되어 있다.

Component Model 제안은 Wasm 모듈이 타입이 명확한 인터페이스를 노출하고 라이브러리처럼 조합될 수 있게 한다. 그러면 Rust 모듈을 Python 호스트나 Go 서비스에 쉽게 연결할 수 있게 될 것이다. WebAssembly GC는 Java와 Kotlin 같은 관리형 언어 지원을 강화한다. 이 모든 것은 브라우저에서 JavaScript를 대체하려는 것이 아니라, Wasm을 보편적이고 가벼운 런타임 대상으로 만드는 것이다.

브라우저 안에서도 같은 흐름이 보인다. 네이티브 기능이 클라이언트 쪽으로 더 많이 옮겨오고, 개인정보를 보호하는 도구가 늘며, 한때 데스크톱 전용이던 라이브러리들이 탭 안에서 돌아간다. WebAssembly는 사용자가 눈에 띄는 기능은 아니지만, 점점 더 많은 무거운 작업이 웹에서 실행될 수 있게 해주는 기반이 되고 있다.

더 읽을 블로그 글