WebAssembly 연동 — JS에서 Wasm 모듈 호출하기
WebAssembly(Wasm)는 브라우저에서 네이티브에 가까운 속도로 실행되는 바이너리 포맷입니다. C, C++, Rust 같은 언어로 작성한 코드를 컴파일하여 JavaScript와 함께 사용할 수 있습니다.
WebAssembly란?
고수준 언어 (C/C++/Rust)
↓ 컴파일러 (Emscripten, wasm-pack)
WebAssembly (.wasm 바이너리)
↓ 브라우저 로드
JavaScript에서 호출
Wasm이 JavaScript보다 빠른 이유:
- 바이너리 형식 → 파싱 비용 최소
- 정적 타입 → 타입 체크 오버헤드 없음
- 메모리를 직접 관리 → GC 없음
- SIMD 같은 저수준 최적화 가능
Wasm 모듈 로드
// 방법 1: 스트리밍 컴파일 (권장)
const response = await fetch("module.wasm");
const { instance } = await WebAssembly.instantiateStreaming(response, {
env: {
// JS에서 Wasm으로 전달하는 함수
log: (value) => console.log("Wasm:", value),
},
});
// 익스포트된 함수 호출
const result = instance.exports.add(40, 2);
console.log(result); // 42
// 방법 2: ArrayBuffer에서 컴파일
const buffer = await fetch("module.wasm").then((r) => r.arrayBuffer());
const module = await WebAssembly.compile(buffer);
const instance2 = await WebAssembly.instantiate(module);
WAT로 이해하는 Wasm
Wasm의 텍스트 표현(WAT)으로 간단한 모듈을 살펴봅니다.
;; add.wat — 두 수를 더하는 Wasm 모듈
(module
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)
(export "add" (func $add)))
메모리 공유
// Wasm 메모리 생성
const memory = new WebAssembly.Memory({
initial: 1, // 1페이지 = 64KB
maximum: 10, // 최대 10페이지
});
// JavaScript에서 Wasm 메모리 접근
const view = new Uint8Array(memory.buffer);
view[0] = 42;
// 문자열 전달 (메모리에 직접 쓰기)
function writeString(memory, offset, str) {
const encoder = new TextEncoder();
const bytes = encoder.encode(str);
const view = new Uint8Array(memory.buffer);
view.set(bytes, offset);
return bytes.length;
}
function readString(memory, offset, length) {
const decoder = new TextDecoder();
const view = new Uint8Array(memory.buffer, offset, length);
return decoder.decode(view);
}
Rust에서 Wasm으로
// Rust 코드 (wasm-bindgen 사용)
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u32 {
if n <= 1 { return n; }
fibonacci(n - 1) + fibonacci(n - 2)
}
// JavaScript에서 호출
import init, { fibonacci } from "./pkg/my_wasm.js";
await init();
console.log(fibonacci(40)); // Rust 컴파일된 코드로 빠르게 실행
성능 비교
// JavaScript 피보나치
function jsFibonacci(n) {
if (n <= 1) return n;
return jsFibonacci(n - 1) + jsFibonacci(n - 2);
}
// 벤치마크
console.time("JS");
jsFibonacci(40);
console.timeEnd("JS"); // ~1500ms
console.time("Wasm");
wasmFibonacci(40);
console.timeEnd("Wasm"); // ~300ms (약 5배 빠름)
Wasm이 적합한 경우
| 적합 | 부적합 |
|---|---|
| 무거운 수학 연산 | DOM 조작 |
| 이미지/비디오 처리 | 일반 웹 UI |
| 게임 엔진 | 간단한 CRUD |
| 암호화/압축 | API 호출 |
| 과학 시뮬레이션 | 일반 비즈니스 로직 |
WASI — 서버 사이드 Wasm
// WASI (WebAssembly System Interface) — 브라우저 밖에서 Wasm 실행
// 파일 시스템, 네트워크 등 시스템 인터페이스 제공
// Node.js에서도 실험적으로 지원
import { WASI } from "wasi";
const wasi = new WASI({ args: [], env: {} });
const module = await WebAssembly.compile(wasmBuffer);
const instance = await WebAssembly.instantiate(module, {
wasi_snapshot_preview1: wasi.wasiImport,
});
wasi.start(instance);
**기억하기 **: WebAssembly는 JavaScript를 대체하는 것이 아니라 ** 보완 **하는 기술입니다. 무거운 연산(이미지 처리, 게임, 암호화)은 Wasm으로, UI와 DOM 조작은 JavaScript로 처리하는 것이 최적의 조합입니다. Rust + wasm-bindgen 조합이 가장 인기 있는 Wasm 개발 스택입니다.
댓글 로딩 중...