Conceptly
← 전체 목록
⚙️

WebAssembly

클라이언트브라우저에서 네이티브 수준 성능을 내는 바이너리 실행 형식

WebAssembly(WASM)는 C, C++, Rust 같은 언어로 작성된 코드를 브라우저에서 실행할 수 있는 바이너리 형식의 가상 명령어 집합입니다. JavaScript로는 한계가 있는 고성능 연산 영역을 브라우저 안에서 처리할 수 있게 합니다.

아키텍처 다이어그램

🔄 프로세스 다이어그램

점선 애니메이션은 데이터 또는 요청의 흐름 방향을 나타냅니다

왜 필요한가요?

브라우저에서 무거운 연산을 처리하는 일은 오랫동안 JavaScript 하나가 모든 것을 감당해야 하는 구조였습니다. 영상을 실시간으로 인코딩하거나, 물리 시뮬레이션을 돌리거나, CAD 도구를 웹으로 옮기려 할 때마다 JS의 단일 스레드 실행과 동적 타입 시스템에서 오는 성능 한계에 부딪혔습니다. C++이나 Rust로 작성된 검증된 라이브러리를 웹에서 쓰려면 JS로 다시 만들거나 포기해야 했고, 그 과정에서 개발 비용과 성능 손실이 함께 발생했습니다.

왜 이런 방식이 등장했나요?

초기 웹은 문서를 표현하는 공간이었지만 웹 앱이 점점 네이티브 앱의 역할을 맡게 되면서 성능 요구가 급격히 높아졌습니다. asm.js가 2013년에 등장해 JS의 부분집합을 최적화 힌트로 쓰는 방식으로 속도를 높이려 했지만, 파싱 비용이 크고 최적화 보장이 불충분했습니다. Mozilla, Google, Microsoft, Apple이 공동으로 설계한 WebAssembly는 2017년에 주요 브라우저에 동시 탑재됐습니다. JS처럼 텍스트를 파싱하는 대신, 이미 최적화된 바이트코드를 바로 JIT 컴파일 할 수 있는 구조를 만들어 브라우저 안에서도 예측 가능한 성능을 낼 수 있게 됐습니다.

내부적으로 어떻게 동작하나요?

WebAssembly의 실행 경로는 소스 코드에서 시작해 컴파일러가 .wasm 바이너리를 만드는 것으로 오프라인에서 완성됩니다. C/C++ 코드는 Emscripten, Rust 코드는 wasm-pack 같은 도구체인이 WASM 바이트코드로 변환합니다. 브라우저는 이 .wasm 파일을 받아 JIT 컴파일로 네이티브 코드에 가까운 형태로 변환하고 실행합니다. JS와의 연결은 WebAssembly JavaScript API를 통해 이뤄집니다. WebAssembly.instantiate()로 모듈을 로드하면 JS에서 WASM 함수를 직접 호출할 수 있습니다. 데이터를 주고받을 때는 WASM 선형 메모리(ArrayBuffer)에 값을 쓰고 JS에서 읽는 방식으로 공유합니다. WASM 자체는 DOM에 접근하거나 네트워크를 직접 호출할 수 없기 때문에, UI 조작이나 외부 통신은 항상 JS가 대신 수행합니다.

경계와 구분

WebAssembly와 JavaScript는 서로 대체 관계가 아닙니다. WASM은 JS가 느린 영역인 수치 연산, 암호화, 미디어 처리 같은 CPU 집약적 작업을 담당하고, JS는 DOM 조작, 이벤트 처리, 네트워크 호출처럼 브라우저 생태계와 맞닿아 있는 부분을 담당합니다. 실제로 WASM만으로 동작하는 웹 앱은 없고, 항상 JS가 접착제 역할을 합니다. Web Worker와의 관계도 중요합니다. WASM 연산을 메인 스레드에서 실행하면 UI 렌더링을 막기 때문에, 무거운 WASM 작업은 Web Worker 안에서 실행하는 패턴이 일반적입니다. Worker가 연산 격리와 스레딩을 제공하고, WASM이 그 안에서 성능을 담당하는 조합입니다. SharedArrayBuffer를 쓰면 Worker와 메인 스레드가 WASM 메모리를 직접 공유할 수도 있지만, 이때는 Spectre 같은 사이드채널 공격 방어를 위해 Cross-Origin-Isolation 헤더 설정이 필요합니다. JS와의 경계에서 실제 비용이 생기는 지점은 타입 변환입니다. WASM은 i32, i64, f32, f64 같은 정수·부동소수 타입만 직접 다루기 때문에, 문자열이나 구조체를 넘기려면 선형 메모리에 바이트로 직렬화해야 합니다. 호출 빈도가 높을수록 이 직렬화 비용이 누적되므로, WASM 함수 호출 횟수를 줄이고 한 번에 많은 작업을 묶어서 처리하는 방향이 유리합니다.

언제 쓰나요?

WASM이 실제로 의미 있는 영역은 JS 성능의 한계가 사용자 경험에 직접 영향을 주는 곳입니다. 브라우저 기반 영상 편집 도구에서 인코딩 시간이 수십 초에서 수 초로 줄어드는 경우, 3D 모델링 앱에서 렌더링 프레임이 안정적으로 유지되는 경우, 복잡한 스프레드시트에서 수백만 행 계산이 버벅이지 않는 경우가 대표적입니다. 기존 C/Rust 라이브러리를 재사용할 수 있다는 점도 실무에서 중요합니다. SQLite를 WASM으로 컴파일해 브라우저 내 오프라인 DB로 활용하거나, 오디오 코덱, PDF 렌더러, 암호화 라이브러리를 JS로 재작성하지 않고 그대로 포팅하는 사례들이 있습니다. 단, 모든 코드를 WASM으로 옮기는 건 권장하지 않습니다. DOM 조작이 많거나 JS 호출과 WASM 호출이 짧게 교차하는 패턴에서는 직렬화 오버헤드 때문에 오히려 느려질 수 있습니다. WASM은 JS가 병목이 되는 연산에만 집중하고, 나머지는 JS에 두는 구조가 현실적으로 잘 작동합니다.

게임 엔진 포팅영상·이미지 처리CAD·과학 시뮬레이션기존 C/Rust 라이브러리 재사용