WebAssembly 완벽 가이드 | WASM·Rust·성능·브라우저·실전 활용

WebAssembly 완벽 가이드 | WASM·Rust·성능·브라우저·실전 활용

이 글의 핵심

WebAssembly로 고성능 웹 앱을 구축하는 완벽 가이드입니다. Rust로 WASM 빌드, JavaScript 통합, 성능 최적화까지 실전 예제로 정리했습니다.

실무 경험 공유: JavaScript 이미지 처리를 WASM으로 전환하면서, 처리 속도가 10배 빨라지고 사용자 경험이 크게 향상된 경험을 공유합니다.

들어가며: “JavaScript가 느려요”

실무 문제 시나리오

시나리오 1: 계산이 너무 느려요
JavaScript는 느립니다. WASM은 네이티브 수준입니다.

시나리오 2: 이미지 처리가 필요해요
JavaScript는 제한적입니다. WASM은 빠른 처리를 제공합니다.

시나리오 3: 게임 개발이 필요해요
성능이 중요합니다. WASM은 고성능을 제공합니다.


1. WebAssembly란?

핵심 특징

WebAssembly는 브라우저에서 실행되는 바이너리 포맷입니다.

주요 장점:

  • 빠른 속도: 네이티브 수준
  • 작은 크기: 압축된 바이너리
  • 보안: 샌드박스 환경
  • 다국어: Rust, C, C++, Go
  • 브라우저 지원: 모든 모던 브라우저

성능 비교:

  • JavaScript: 100ms
  • WASM: 10ms

2. Rust로 WASM 빌드

설치

# Rust 설치
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# wasm-pack 설치
cargo install wasm-pack

프로젝트 생성

cargo new --lib my-wasm-project
cd my-wasm-project

Cargo.toml

[package]
name = "my-wasm-project"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = [cdylib]

[dependencies]
wasm-bindgen = "0.2"

3. Rust 코드

기본 함수

// src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

복잡한 예제

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct Calculator {
    value: f64,
}

#[wasm_bindgen]
impl Calculator {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Calculator {
        Calculator { value: 0.0 }
    }

    pub fn add(&mut self, num: f64) {
        self.value += num;
    }

    pub fn subtract(&mut self, num: f64) {
        self.value -= num;
    }

    pub fn get_value(&self) -> f64 {
        self.value
    }
}

4. 빌드

wasm-pack build --target web

출력:

pkg/
├── my_wasm_project.js
├── my_wasm_project_bg.wasm
└── package.json

5. JavaScript 통합

기본 사용

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>WASM Example</title>
  </head>
  <body>
    <h1>WebAssembly Example</h1>
    <div id="result"></div>

    <script type="module">
      import init, { add, greet } from './pkg/my_wasm_project.js';

      async function run() {
        await init();

        const result = add(5, 3);
        console.log('5 + 3 =', result);

        const greeting = greet('John');
        console.log(greeting);

        document.getElementById('result').textContent = greeting;
      }

      run();
    </script>
  </body>
</html>

React 통합

// App.tsx
import { useEffect, useState } from 'react';

export default function App() {
  const [wasm, setWasm] = useState<any>(null);
  const [result, setResult] = useState<number>(0);

  useEffect(() => {
    async function loadWasm() {
      const module = await import('./pkg/my_wasm_project.js');
      await module.default();
      setWasm(module);
    }

    loadWasm();
  }, []);

  const handleCalculate = () => {
    if (wasm) {
      const result = wasm.add(5, 3);
      setResult(result);
    }
  };

  return (
    <div>
      <button onClick={handleCalculate}>Calculate</button>
      <p>Result: {result}</p>
    </div>
  );
}

6. 실전 예제: 이미지 처리

Rust

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn grayscale(data: &mut [u8]) {
    for i in (0..data.len()).step_by(4) {
        let r = data[i] as f32;
        let g = data[i + 1] as f32;
        let b = data[i + 2] as f32;

        let gray = (0.299 * r + 0.587 * g + 0.114 * b) as u8;

        data[i] = gray;
        data[i + 1] = gray;
        data[i + 2] = gray;
    }
}

JavaScript

import init, { grayscale } from './pkg/my_wasm_project.js';

async function processImage(imageFile: File) {
  await init();

  const img = new Image();
  img.src = URL.createObjectURL(imageFile);

  await new Promise((resolve) => {
    img.onload = resolve;
  });

  const canvas = document.createElement('canvas');
  canvas.width = img.width;
  canvas.height = img.height;

  const ctx = canvas.getContext('2d')!;
  ctx.drawImage(img, 0, 0);

  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

  grayscale(imageData.data);

  ctx.putImageData(imageData, 0, 0);

  return canvas.toDataURL();
}

7. 성능 최적화

메모리 관리

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct LargeData {
    data: Vec<u8>,
}

#[wasm_bindgen]
impl LargeData {
    #[wasm_bindgen(constructor)]
    pub fn new(size: usize) -> LargeData {
        LargeData {
            data: vec![0; size],
        }
    }

    pub fn process(&mut self) {
        // 처리 로직
    }
}

// JavaScript에서 사용 후 메모리 해제
// const data = new LargeData(1000000);
// data.process();
// data.free();

정리 및 체크리스트

핵심 요약

  • WebAssembly: 브라우저 바이너리 포맷
  • 빠른 속도: 네이티브 수준
  • Rust: 주요 개발 언어
  • JavaScript 통합: 간편한 사용
  • 보안: 샌드박스 환경
  • 브라우저 지원: 모든 모던 브라우저

구현 체크리스트

  • Rust 설치
  • wasm-pack 설치
  • Rust 코드 작성
  • WASM 빌드
  • JavaScript 통합
  • 성능 최적화
  • 배포

같이 보면 좋은 글

  • Rust 웹 개발 가이드
  • Bun 완벽 가이드
  • 성능 최적화 가이드

이 글에서 다루는 키워드

WebAssembly, WASM, Rust, Performance, Browser, Frontend, JavaScript

자주 묻는 질문 (FAQ)

Q. JavaScript를 완전히 대체할 수 있나요?

A. 아니요, 성능이 중요한 부분만 WASM으로 작성하고 나머지는 JavaScript를 사용합니다.

Q. 모든 브라우저에서 작동하나요?

A. 네, 모든 모던 브라우저(Chrome, Firefox, Safari, Edge)에서 지원합니다.

Q. Rust를 배워야 하나요?

A. WASM 개발에는 Rust가 가장 적합하지만, C/C++, Go도 가능합니다.

Q. 프로덕션에서 사용해도 되나요?

A. 네, Figma, Google Earth 등 많은 서비스에서 사용합니다.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3