What will I learn?

초기 프로젝트 구성

  1. cargo-generate로 wasm 프로젝트의 템플릿을 다운로드 받는다.

    cargo install cargo-generate # if don't have
    cargo generate --git <https://github.com/rustwasm/wasm-pack-template>
    

    cargo-generate는 복붙하려는 새 프로젝트 이름을 물어본다. 이때 내가 원하는 이름 (예를 들어 game-of-life-wasm)을 입력하면 자동으로 보일러 플레이트를 생성해준다.

    이제 wasm-pack 프로그램으로 패키지를 빌드 및 웹 어셈블리 코드 생성을 동시에 진행한다.

    wasm-pack build
    
  2. create-wasm-app JS 프로젝트 템플릿을 다운로드 받는다.

    rust wasm 패키지를 webpack에 담가주는 템플릿. 단순히 내가 배포하려는 패키지 루트에서 다음 명령어를 실행하면 된다. 그러면 www 라는 이름의 새 디렉토리가 생성되고 그 안에 node.js 패키지를 위한 파일들이 생성된다.

    npm init wasm-app www
    

    incremental build를 위해서 사전 작업을 수행했다. www/package.json 파일에 루트 폴더의 wasm 바이너리가 들어있는 폴더를 의존하도록 만들었다.

    ..."dependencies": { "game-of-life-wasm": "file:../pkg" }
    

    그리고 index.js 파일에 placeholder로 달려있던 임포트 구문을 자연스럽게 game-of-life-wasm 으로 바꾸어 주었다.

    import * as wasm from "game-of-the-life-wasm";
    wasm.greet();
    

    npm install, npm run start 명령으로 간단하게 웹을 띄울 수 있다. 다음 캡처는 기본 템플릿에 제공된 함수의 작동을 보여준다.

    Untitled

Interfacing Rust and JS

WA는 단지 선형 메모리 공간을 JS에게 제공할 뿐이다. WA는 반대로 JS가 점유하는 힙 영역의 공간을 참조할 수 없다. 상당히 제약적인 것이다! JS는 오직 WA의 배열버퍼(u8, i32, f64, 등)만 가지고 WA의 메모리 공간을 참조할 수 있다. WA 함수는 오직 스칼라 값들만 리턴할 수 있다. WA는 JS의 전역 객체(Math, Intl, JSON, Array, BigInt 등등) 를 참조할 수 있다고 한다. [[js-sys crate]]를 이용하면 된다고..

wasm_bindgen 은 우리가 처음에 cargo generate 를 통해 불러온 템플릿 패키지가 이미 의존하던 크레이트다. wasm_bindgen은 Rust 영역과 JS 영역간에 경계를 넘나드는 공통 인터페이스를 제공해준다. Rust 구조체 인스턴스를 #[wasm_bindgen] attribute로 JS 객체로 활용할 수 있게 만들어주고, 반대로 JS 객체를 JsValue 인터페이스를 경유해 사용가능하도록 만든다.

graph LR
  Rust -- "#[wasm_bindgen]" --> JavaScript
	JavaScript -- "JsValue" --> Rust
  1. 불필요한 복사는 오버헤드를 일으킨다. => WA 선형메모리의 데이터를 복사하는 일을 줄여라!
  2. 직화와 역직렬화는 오버헤드를 일으킨다. => 큰 데이터 객체를 주고받는 일이 없도록 하라!
    1. opaque handle (불투명 핸들?)을 넘겨주면 JS에서 원격으로 WA 메모리 공간을 사용할 수 있어 오버헤드가 줄어든다.
    2. JS는 WA가 노출한 함수만을 호출하여 작고 복사가 용이한 수준의 결과를 받아내 웹에 적용한다.
    3. WA 함수는 퍼포먼스를 내야하는 코드를 실행하고 데이터를 쿼리하고 데이터를 조작한다.