웹어셈블리 입문 (WebAssembly Introduction)

웹어셈블리 입문 (WebAssembly Introduction)

웹어셈블리(WebAssembly, 이하 Wasm)는 웹에서 고성능 애플리케이션을 실행할 수 있도록 설계된 저수준 바이너리 포맷이다. 자바스크립트와 함께 사용되어 웹 개발의 새로운 가능성을 열어준다. 이번에는 웹어셈블리의 기본 개념부터 시작해, 자바스크립트와의 연동, 성능 최적화, 그리고 실제 사용 사례까지 포스팅해보려 한다.


웹어셈블리는 단순히 자바스크립트를 대체하는 것이 아니라, 자바스크립트와 협력하여 웹 애플리케이션의 성능을 극대화하는 도구다.


1. 웹어셈블리란 무엇인가?

웹어셈블리는 웹 브라우저에서 실행되는 저수준의 바이너리 코드 형식이다. C, C++, Rust와 같은 언어로 작성된 코드를 컴파일하여 웹에서 실행할 수 있게 해준다. 이는 자바스크립트의 성능 한계를 극복하고, 웹에서 더 복잡하고 성능 집약적인 애플리케이션을 구동할 수 있도록 한다.


웹어셈블리의 주요 특징은 다음과 같다:

  • 고성능: 네이티브 코드에 가까운 속도로 실행된다.
  • 언어 독립성: 다양한 언어로 작성된 코드를 웹에서 실행할 수 있다.
  • 보안: 웹 브라우저의 보안 모델 내에서 안전하게 실행된다.
  • 상호 운용성: 자바스크립트와 원활하게 연동된다.

웹어셈블리는 자바스크립트와 함께 사용되며, 자바스크립트가 처리하기 어려운 작업을 대신 처리할 수 있다. 예를 들어, 이미지 처리, 3D 그래픽, 암호화 연산 등에서 큰 성능 향상을 기대할 수 있다.


2. 웹어셈블리 시작하기

웹어셈블리를 사용하기 위해서는 먼저 C, C++, Rust 등의 언어로 작성된 코드를 웹어셈블리 바이너리 파일(.wasm)로 컴파일해야 한다. 이를 위해 Emscripten과 같은 도구를 사용할 수 있다. 여기서는 간단한 C 함수를 웹어셈블리로 컴파일하고, 자바스크립트에서 호출하는 방법을 알아보자.


2.1 환경 설정

먼저, Emscripten을 설치해야 한다. Emscripten은 C/C++ 코드를 웹어셈블리로 컴파일하는 도구다. 설치 방법은 다음과 같다:

# Emscripten 설치 (macOS/Linux)
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh

설치가 완료되면, 간단한 C 함수를 작성해보자. 아래는 두 수를 더하는 함수다:

// add.c
#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int add(int a, int b) {
    return a + b;
}

이제 이 C 파일을 웹어셈블리로 컴파일한다:

emcc add.c -s WASM=1 -o add.js

이 명령은 add.wasmadd.js 파일을 생성한다. add.js는 웹어셈블리 모듈을 로드하고 자바스크립트에서 사용할 수 있도록 해주는 glue 코드다.


2.2 웹어셈블리 모듈 로드 및 사용

이제 HTML 파일에서 이 웹어셈블리 모듈을 로드하고, 자바스크립트에서 호출해보자:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>웹어셈블리 예제</title>
</head>
<body>
    <script src="add.js"></script>
    <script>
        Module.onRuntimeInitialized = () => {
            const add = Module.cwrap('add', 'number', ['number', 'number']);
            console.log(add(3, 5));  // 8
        };
    </script>
</body>
</html>

위 코드에서 Module.cwrap은 C 함수를 자바스크립트에서 호출할 수 있는 함수로 감싸준다. 이렇게 하면 자바스크립트에서 웹어셈블리 함수를 직접 호출할 수 있다.


3. 자바스크립트와의 상호 작용

웹어셈블리는 자바스크립트와 데이터를 주고받을 수 있다. 기본적으로 숫자, 불리언 등의 단순한 데이터 타입은 쉽게 전달할 수 있지만, 문자열이나 배열과 같은 복잡한 데이터는 메모리 관리를 통해 처리해야 한다.


3.1 데이터 전달: 숫자와 불리언

앞의 예제에서 보았듯이, 숫자는 직접 전달할 수 있다. 불리언도 마찬가지로 0과 1로 처리된다.

// C 코드
EMSCRIPTEN_KEEPALIVE
int is_positive(int num) {
    return num > 0;
}

// 자바스크립트
const isPositive = Module.cwrap('is_positive', 'number', ['number']);
console.log(isPositive(5));  // 1 (true)
console.log(isPositive(-3)); // 0 (false)

3.2 데이터 전달: 문자열

문자열을 전달하려면 웹어셈블리의 메모리에 문자열을 복사하고, 그 포인터를 전달해야 한다. Emscripten은 이를 위한 도구를 제공한다.

// C 코드
#include <string.h>

EMSCRIPTEN_KEEPALIVE
char* greet(const char* name) {
    static char buffer[100];
    sprintf(buffer, "Hello, %s!", name);
    return buffer;
}

// 자바스크립트
const greet = Module.cwrap('greet', 'string', ['string']);
console.log(greet('World'));  // "Hello, World!"

여기서 Module.cwrap의 두 번째 인자로 'string'을 지정하면, Emscripten이 자동으로 문자열을 처리해준다.


3.3 데이터 전달: 배열

배열을 전달하려면 웹어셈블리의 메모리에 직접 접근해야 한다. 아래는 C에서 배열을 처리하는 예제다:

// C 코드
EMSCRIPTEN_KEEPALIVE
int sum_array(int* arr, int len) {
    int sum = 0;
    for (int i = 0; i < len; i++) {
        sum += arr[i];
    }
    return sum;
}

// 자바스크립트
const sumArray = Module.cwrap('sum_array', 'number', ['number', 'number']);
const arr = new Int32Array([1, 2, 3, 4, 5]);
const ptr = Module._malloc(arr.length * arr.BYTES_PER_ELEMENT);
Module.HEAP32.set(arr, ptr / arr.BYTES_PER_ELEMENT);
const result = sumArray(ptr, arr.length);
Module._free(ptr);
console.log(result);  // 15

여기서 Module._malloc으로 메모리를 할당하고, Module.HEAP32.set으로 자바스크립트 배열을 웹어셈블리 메모리에 복사한다. 함수 호출 후 Module._free로 메모리를 해제한다.


4. 성능 고려 사항

웹어셈블리는 자바스크립트보다 빠른 실행 속도를 제공하지만, 모든 상황에서 그렇지는 않다. 웹어셈블리가 유리한 경우와 그렇지 않은 경우를 이해하는 것이 중요하다.


4.1 웹어셈블리가 유리한 경우

  • 수치 연산: 행렬 연산, 암호화, 물리 시뮬레이션 등
  • 복잡한 알고리즘: 이미지 처리, 오디오 처리 등
  • 대규모 데이터 처리: 빅데이터 분석, 머신러닝 추론 등

4.2 웹어셈블리와 자바스크립트 성능 비교

간단한 피보나치 수열 계산을 통해 성능을 비교해보자. 먼저 자바스크립트로 작성한 코드다:

function fib(n) {
    if (n <= 1) return n;
    return fib(n - 1) + fib(n - 2);
}

console.time('js');
fib(35);
console.timeEnd('js');

이제 C로 작성한 피보나치 함수를 웹어셈블리로 컴파일한다:

// fib.c
#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int fib(int n) {
    if (n <= 1) return n;
    return fib(n - 1) + fib(n - 2);
}

컴파일 후 자바스크립트에서 호출한다:

Module.onRuntimeInitialized = () => {
    const fibWasm = Module.cwrap('fib', 'number', ['number']);
    console.time('wasm');
    fibWasm(35);
    console.timeEnd('wasm');
};

실행해보면, 웹어셈블리 버전이 자바스크립트보다 빠르다는 것을 확인할 수 있다. 다만, 함수 호출 오버헤드 등이 있으므로 작은 작업에서는 자바스크립트가 더 나을 수 있다.


5. 고급 주제

웹어셈블리를 더 깊이 이해하기 위해 메모리 관리, 웹 워커와의 통합, 디버깅 방법 등을 알아보자.


5.1 메모리 관리

웹어셈블리는 선형 메모리 모델을 사용한다. 이 메모리는 자바스크립트와 공유되며, WebAssembly.Memory 객체를 통해 접근할 수 있다.

// 자바스크립트에서 메모리 생성
const memory = new WebAssembly.Memory({ initial: 1 });
const heap = new Uint8Array(memory.buffer);

// C 코드에서 메모리 사용
EMSCRIPTEN_KEEPALIVE
void write_to_memory() {
    int* ptr = (int*)malloc(sizeof(int));
    *ptr = 42;
}

메모리 크기는 동적으로 늘릴 수 있다:

memory.grow(1);  // 1 페이지 (64KB) 추가

5.2 웹 워커와의 통합

웹 워커를 사용하면 웹어셈블리 코드를 백그라운드 스레드에서 실행할 수 있어, UI 스레드를 차단하지 않고 복잡한 연산을 수행할 수 있다.

// worker.js
importScripts('add.js');

Module.onRuntimeInitialized = () => {
    const add = Module.cwrap('add', 'number', ['number', 'number']);
    self.postMessage(add(3, 5));
};

// main.js
const worker = new Worker('worker.js');
worker.onmessage = (e) => {
    console.log('Result from worker:', e.data);  // 8
};

5.3 디버깅

웹어셈블리 코드를 디버깅하려면, Emscripten의 -g 옵션을 사용하여 소스 맵을 생성할 수 있다.

emcc -g add.c -s WASM=1 -o add.js

이렇게 하면 브라우저의 개발자 도구에서 C 코드를 디버깅할 수 있다.


6. 실제 사용 사례

웹어셈블리는 이미 다양한 분야에서 사용되고 있다. 몇 가지 예를 살펴보자:

  • Figma: 웹 기반 디자인 도구로, 웹어셈블리를 사용해 복잡한 그래픽 연산을 처리한다.
  • AutoCAD: 웹 버전에서 웹어셈블리를 사용해 CAD 도면을 렌더링하고 편집한다.
  • Google Earth: 웹어셈블리를 사용해 3D 지구본을 렌더링한다.

이 외에도 게임, 비디오 편집, 암호화폐 채굴 등 다양한 분야에서 웹어셈블리가 활용되고 있다.


7. 일반적인 함정과 모범 사례

웹어셈블리를 사용할 때 주의해야 할 점과 모범 사례를 알아보자.


7.1 보안 고려 사항

웹어셈블리 모듈은 신뢰할 수 있는 출처에서 로드해야 한다. 또한, 메모리 버퍼 오버플로우와 같은 취약점을 방지하기 위해 메모리 관리를 신중하게 해야 한다.


7.2 최적화

웹어셈블리 코드를 최적화하려면, 불필요한 메모리 할당을 피하고, 함수 호출 오버헤드를 줄이는 것이 중요하다. 또한, 웹어셈블리와 자바스크립트 간의 데이터 전달을 최소화해야 한다.


7.3 업데이트 추적

웹어셈블리는 계속 발전하고 있으므로, 최신 기능과 개선 사항을 주기적으로 확인하는 것이 좋다. 공식 웹사이트에서 최신 정보를 얻을 수 있다.


8. 결론

웹어셈블리는 웹 개발의 새로운 지평을 열어주는 도구다. 자바스크립트와 협력하여 웹 애플리케이션의 성능을 극대화할 수 있으며, 다양한 언어로 작성된 코드를 웹에서 실행할 수 있게 해준다.


더 많은 정보를 원한다면, MDN 웹어셈블리 문서를 참고하자.


+ Recent posts