← 가이드 목록으로 ← Back to guides

Rust와 WebAssembly, 그리고 안전한 동시성 Rust, WebAssembly, and Safe Concurrency

C/C++ 수준의 성능을 제공하는 Rust는 WebAssembly(Wasm)의 주요 언어로 자리 잡고 있습니다. 더불어, 데이터 레이스(Data Race)를 컴파일 타임에 차단하는 Rust의 혁신적인 동시성(Concurrency) 모델을 깊게 이해해 봅니다.

Rust, delivering C/C++ level performance, is establishing itself as a premier language for WebAssembly (Wasm). Let's also dive deep into Rust's innovative Concurrency model, which prevents data races at compile time.

Rust에서 WebAssembly(Wasm)로 컴파일 Compiling Rust to WebAssembly (Wasm)

`wasm-pack` 도구와 `wasm-bindgen` 크레이트를 사용하면 Rust 코드를 자바스크립트에서 쉽게 호출할 수 있는 Wasm 모듈로 빌드할 수 있습니다.

By using the `wasm-pack` tool and `wasm-bindgen` crate, you can build Rust code into Wasm modules that can be easily invoked from JavaScript.

// Cargo.toml 설정
[package]
name = "wasm-example"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"
// Cargo.toml configuration
[package]
name = "wasm-example"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"
// src/lib.rs (Rust 코드)
use wasm_bindgen::prelude::*;

// JS의 alert 함수를 가져와서 사용
#[wasm_bindgen]
extern "C" {
  pub fn alert(s: &str);
}

// JS로 노출할 함수
#[wasm_bindgen]
pub fn greet(name: &str) {
  alert(&format!("Hello, {}! This is from Rust Wasm.", name));
}

#[wasm_bindgen]
pub fn calculate_heavy_logic(n: u32) -> u32 {
  // 브라우저의 JS 스레드를 멈추게 하는 무거운 연산을 Wasm으로 이양
  if n <= 1 { return n; }
  let mut a = 0;
  let mut b = 1;
  for _ in 2..=n {
    let temp = a + b;
    a = b;
    b = temp;
  }
  b
}
// src/lib.rs (Rust code)
use wasm_bindgen::prelude::*;

// Import the JS alert function
#[wasm_bindgen]
extern "C" {
  pub fn alert(s: &str);
}

// Export a function to JS
#[wasm_bindgen]
pub fn greet(name: &str) {
  alert(&format!("Hello, {}! This is from Rust Wasm.", name));
}

#[wasm_bindgen]
pub fn calculate_heavy_logic(n: u32) -> u32 {
  // Offload heavy computation that blocks the JS thread to Wasm
  if n <= 1 { return n; }
  let mut a = 0;
  let mut b = 1;
  for _ in 2..=n {
    let temp = a + b;
    a = b;
    b = temp;
  }
  b
}

JavaScript에서 호출하기 Calling from JavaScript

// npm run start 환경이나 웹팩 등에서
import { greet, calculate_heavy_logic } from './pkg/wasm_example';

greet("Frontend Developer");
const result = calculate_heavy_logic(45);
console.log(`Fib(45) from Wasm: ${result}`);

안전한 동시성 (Fearless Concurrency) Fearless Concurrency

`Arc`(Atomic Reference Counted)와 `Mutex`(Mutual Exclusion)의 조합은 Rust 멀티스레드 프로그래밍의 정석입니다. 소유권과 생명주기 검사가 컴파일 단계에서 완벽하게 동작하기 때문에, 컴파일이 성공하면 데이터 레이스(스레드 경합)가 발생하지 않음을 확신할 수 있습니다.

The combination of `Arc` (Atomic Reference Counted) and `Mutex` (Mutual Exclusion) is the standard for Rust multithreaded programming. Since ownership and lifetime checks work flawlessly at compile time, a successful compilation guarantees the absence of data races.

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
  // 공유 상태 생성 (Arc로 감싸서 참조 카운팅, Mutex로 내부 가변성 확보)
  let counter = Arc::new(Mutex::new(0));
  let mut handles = vec![];

  for _ in 0..10 {
    // 스레드로 이동시키기 위해 Arc 클론
    let counter = Arc::clone(&counter);
    let handle = thread::spawn(move || {
      // lock()을 얻은 동안 안전하게 데이터 변경
      let mut num = counter.lock().unwrap();
      *num += 1;
    });
    handles.push(handle);
  }

  // 모든 스레드 종료 대기
  for handle in handles {
    handle.join().unwrap();
  }

  println!("Result: {}", *counter.lock().unwrap());
}
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
  // Create shared state (wrapped in Arc for reference counting, and Mutex for interior mutability)
  let counter = Arc::new(Mutex::new(0));
  let mut handles = vec![];

  for _ in 0..10 {
    // Clone the Arc to move into the thread
    let counter = Arc::clone(&counter);
    let handle = thread::spawn(move || {
      // Safely mutate data while holding the lock()
      let mut num = counter.lock().unwrap();
      *num += 1;
    });
    handles.push(handle);
  }

  // Wait for all threads to complete
  for handle in handles {
    handle.join().unwrap();
  }

  println!("Result: {}", *counter.lock().unwrap());
}

✨ WebAssembly 도입의 장점 ✨ Advantages of adopting WebAssembly

  • 성능: 브라우저 환경에서 이미지 처리, 비디오 인코딩, 복잡한 물리가 들어간 게임 연산 등을 네이티브에 가까운 속도로 처리합니다.
  • 이식성: 한 번 작성된 Wasm 모듈은 브라우저뿐만 아니라 Node.js, Deno, Edge 워커 등 다양한 환경에서 실행됩니다.
  • 보안: Wasm은 브라우저의 강력한 샌드박스 내부에서 안전하게 실행됩니다.
  • Performance: Handles image processing, video encoding, and complex game physics in the browser at near-native speeds.
  • Portability: Wasm modules run not only in browsers but also in Node.js, Deno, and Edge workers.
  • Security: Wasm runs securely within the browser's strict sandbox.

💡 컴파일 타임에 잡히는 데이터 경합 💡 Data races caught at compile time

위의 코드에서 만약 `Arc`를 쓰지 않고 단순히 값을 스레드 안으로 빌려주려(borrow) 하거나, `Mutex`를 빼먹고 값을 변형하려 들면 Rust 컴파일러가 강력한 에러를 뱉으며 조기 차단합니다. 다른 언어에서는 런타임에 디버깅하기 매우 까다로운 버그가 Rust에선 발생조차 불가능합니다.

In the code above, if you tried to borrow a value into a thread without `Arc`, or modify the value without a `Mutex`, the Rust compiler would immediately throw a hard error. Bugs that are extremely hard to debug at runtime in other languages are literally impossible to compile in Rust.