돌아가기
#wasm
#rust
#nextjs

러스트로 웹어셈블리 써보기

웹어셈블리는 최신 웹 브라우저에서 실행할 수 있는 코드로 이진 명령어 형식의 저수준 언어이다.

C, C++, Rust와 같은 고성능 프로그레밍 언어로 작성된 코드를 웹에서 네이티브에 가까운 속도로 실행할 수 있게 해준다.

다음은 웹어셈블리를 사용하여 자바스크립트 alert 함수를 호출하는 예제이다.

러스트를 사용하여 웹어셈블리를 작성하는 방법

1. 러스트 프로젝트 생성

bash
cargo new wasm
cd wasm
wasm/
src/
main.rs
Cargo.toml
Cargo.lock

2. 필수 라이브러리 설치

wasm-bindgenwasm-pack을 설치한다.

  • wasm-bindgen: 러스트와 자바스크립트사이의 상호작용을 원활하게 해주는 라이브러리
  • wasm-pack: 러스트 코드로 작성된 웹 어셈블리 프로젝트를 빌드하고 패키징하는 워크플로우 도구
bash
cargo add wasm-bindgen
cargo install wasm-pack

3. Cargo.toml 파일 수정

toml
...
[lib]
crate-type = ["cdylib"]
 
[dependencies]
wasm-bindgen = "0.2.100"

cdylib은 러스트 프로젝트를 다른 프로젝트 언어에서 호출 할 수 있는 동적 라이브러리로 컴파일할 때 사용되는 크레이트 타입이다.

4. 러스트 코드 작성

main.rs 파일을 lib.rs로 변경 한 후, 러스트 코드를 작성한다.

rust
use wasm_bindgen::prelude::*;
 
#[wasm_bindgen]
extern "C" {
    fn alert(s: &str); // alert 함수를 외부에서 사용할 수 있도록 선언
}
 
#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name)); // alert 함수를 호출
}

5. 러스트 코드 빌드

bash
wasm-pack build .

빌드 결과로 pkg 디렉토리가 생성된다.

로딩 중...wasm-pack build

6. 웹 어셈블리 파일 사용

컴포넌트 파일에서 웹 어셈블리 파일을 사용하기 위해서는 변환된 js 파일을 임포트 해야 한다.

tsx
"use client";
 
import dynamic from "next/dynamic";
import { useEffect, useState } from "react";
 
function WasmAlertComponent() {
  const [greet, setGreet] = useState<Function | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
 
  useEffect(() => {
    const loadWasm = async () => {
      try {
        setIsLoading(true);
        setError(null);
        
        // WASM 모듈을 동적으로 로드
        const greetModule = await import("@/examples/wasm/pkg/wasm");
        
        // greet 함수가 존재하는지 확인
        if (typeof greetModule.greet === 'function') {
          await greetModule.default();
          setGreet(() => greetModule.greet);
        } else {
          throw new Error("WASM greet 함수를 찾을 수 없습니다.");
        }
      } catch (err) {
        console.error("WASM 로딩 오류:", err);
        setError(err instanceof Error ? err.message : "WASM 모듈 로딩에 실패했습니다.");
      } finally {
        setIsLoading(false);
      }
    };
    
    loadWasm();
  }, []);
 
  const handleClick = () => {
    if (greet && typeof greet === 'function') {
      try {
        greet("world");
      } catch (err) {
        console.error("WASM 함수 실행 오류:", err);
        alert("WASM 함수 실행 중 오류가 발생했습니다.");
      }
    }
  };
 
  if (isLoading) {
    return (
      <div className="text-gray-500 bg-gray-100 rounded-lg p-2 flex items-center justify-center w-fit border-[1px] border-gray-300">
        WASM 로딩 중...
      </div>
    );
  }
 
  if (error) {
    return (
      <div className="text-red-500 bg-red-100 rounded-lg p-2 flex items-center justify-center w-fit border-[1px] border-red-300">
        오류: {error}
      </div>
    );
  }
 
  return (
    <div
      onClick={handleClick}
      className="cursor-pointer text-blue-500 hover:text-blue-600 bg-blue-500/10 rounded-lg p-2
  flex items-center justify-center w-fit border-[1px] border-blue-500
  "
    >
      클릭하여 러스트 웹어셈블리 호출
    </div>
  );
}
 
const WasmAlert = dynamic(() => Promise.resolve(WasmAlertComponent), {
  ssr: false,
});
 
export default WasmAlert;

wasm vs js 성능 비교

WASM 모듈 로딩 중...

특이하게도 문자열 처리 성능은 자바스크립트가 더 빠르다. 그 이유는 러스트에서 매 루프 반복마다 format! 매크로로 새로운 문자열을 생성하는데, 이는 내부적으로 자바스크립트와의 상호작용을 위해 문자열을 생성하는 과정에서 발생하는 오버헤드가 있기 때문이다.

또한 최신 자바스크립트 엔진은 문자열 처리와 같은 특정한 작업에서 매우 빠른 실행 속도를 보여준다.

Web Assembly는 3D 렌더링, 비디오 및 오디오 처리, 암호화 및 복잡한 수학적 계산등 cpu-intensive한 작업에서 자바스크립트보다 더 빠른 성능을 보여준다.

웹 어셈블리로 작성한 콘웨이의 생명 게임

웹 어셈블리를 사용하여 콘웨이의 생명 게임을 작성한 예제이다.

코드 보기

웹 미리보기
로딩 중...