- Published on
WebAssembly 완전 가이드 2025: 브라우저를 넘어서 — WASI, Component Model, 서버사이드 Wasm
- Authors

- Name
- Youngju Kim
- @fjvbn20031
목차
1. WebAssembly란 무엇인가
WebAssembly(Wasm)는 스택 기반 가상 머신을 위한 바이너리 명령어 포맷이다. 원래 웹 브라우저에서 네이티브에 가까운 성능을 제공하기 위해 설계되었지만, 이제는 브라우저를 넘어 서버, 엣지, IoT까지 확장되고 있다.
1.1 Wasm의 핵심 특성
| 특성 | 설명 | 의미 |
|---|---|---|
| 바이너리 포맷 | 컴팩트한 바이너리 인코딩 | 빠른 전송, 빠른 디코딩 |
| 스택 머신 | 스택 기반 명령어 실행 | 단순하고 효율적인 실행 모델 |
| 선형 메모리 | 연속된 바이트 배열 | 안전한 메모리 접근, 샌드박스 |
| 타입 안전 | 강타입 시스템 | 컴파일 시 검증, 런타임 안전 |
| 이식성 | 아키텍처 독립적 | x86, ARM, RISC-V 어디서나 실행 |
| 샌드박스 | 격리된 실행 환경 | 호스트 시스템 보호 |
1.2 Wasm의 역사
2015: WebAssembly 프로젝트 발표 (W3C)
2017: 4대 브라우저에서 Wasm 1.0 지원
2019: WASI 초기 제안 (비브라우저 Wasm)
2020: Wasm 1.0이 W3C 공식 권고안
2021: Component Model 제안, Fermyon 창립
2022: Docker+Wasm 기술 프리뷰, WASI Preview 1
2023: WASI 0.2 안정화, Component Model 성숙
2024: GC proposal 구현, 주요 런타임 최적화
2025: WASI 0.2 Production-ready, Wasm 서버 본격 채택
1.3 Wasm 바이너리 구조
Wasm 모듈 구조:
+------------------+
| Magic Number | 0x00 0x61 0x73 0x6D ("\0asm")
| Version | 0x01 0x00 0x00 0x00 (v1)
+------------------+
| Type Section | 함수 시그니처 정의
| Import Section | 외부 함수/메모리 임포트
| Function Section | 함수 인덱스
| Table Section | 간접 호출 테이블
| Memory Section | 선형 메모리 정의
| Global Section | 전역 변수
| Export Section | 외부 공개 함수/메모리
| Start Section | 자동 실행 함수
| Element Section | 테이블 초기화
| Code Section | 함수 본문 (바이트코드)
| Data Section | 메모리 초기 데이터
+------------------+
1.4 WAT (WebAssembly Text Format)
Wasm의 텍스트 표현 형식인 WAT를 이해하면 내부 동작을 파악할 수 있다.
;; 두 수를 더하는 간단한 Wasm 모듈
(module
;; 함수 타입 정의
(type $add_type (func (param i32 i32) (result i32)))
;; 함수 구현
(func $add (type $add_type) (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add
)
;; 선형 메모리 (1 페이지 = 64KB)
(memory (export "memory") 1)
;; 함수 내보내기
(export "add" (func $add))
)
2. 브라우저 Wasm
2.1 다양한 언어에서 Wasm으로 컴파일
Rust - Wasm 1등 시민
// lib.rs - Rust에서 Wasm으로
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u64 {
if n <= 1 {
return n as u64;
}
let mut a: u64 = 0;
let mut b: u64 = 1;
for _ in 2..=n {
let temp = a + b;
a = b;
b = temp;
}
b
}
#[wasm_bindgen]
pub struct ImageProcessor {
width: u32,
height: u32,
pixels: Vec<u8>,
}
#[wasm_bindgen]
impl ImageProcessor {
#[wasm_bindgen(constructor)]
pub fn new(width: u32, height: u32) -> ImageProcessor {
ImageProcessor {
width,
height,
pixels: vec![0; (width * height * 4) as usize],
}
}
pub fn grayscale(&mut self) {
for chunk in self.pixels.chunks_exact_mut(4) {
let gray = (0.299 * chunk[0] as f64
+ 0.587 * chunk[1] as f64
+ 0.114 * chunk[2] as f64) as u8;
chunk[0] = gray;
chunk[1] = gray;
chunk[2] = gray;
}
}
pub fn pixels_ptr(&self) -> *const u8 {
self.pixels.as_ptr()
}
}
# Rust -> Wasm 빌드
wasm-pack build --target web
C/C++ - Emscripten
// image_filter.c - C에서 Wasm으로
#include <emscripten.h>
#include <stdint.h>
EMSCRIPTEN_KEEPALIVE
void apply_blur(uint8_t* pixels, int width, int height) {
uint8_t* temp = (uint8_t*)malloc(width * height * 4);
for (int y = 1; y < height - 1; y++) {
for (int x = 1; x < width - 1; x++) {
for (int c = 0; c < 3; c++) {
int sum = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
sum += pixels[((y+dy)*width + (x+dx))*4 + c];
}
}
temp[(y*width + x)*4 + c] = sum / 9;
}
temp[(y*width + x)*4 + 3] = pixels[(y*width + x)*4 + 3];
}
}
memcpy(pixels, temp, width * height * 4);
free(temp);
}
# C -> Wasm 빌드
emcc image_filter.c -O3 -s WASM=1 \
-s EXPORTED_FUNCTIONS='["_apply_blur"]' \
-s EXPORTED_RUNTIME_METHODS='["cwrap"]' \
-o image_filter.js
2.2 JavaScript와의 상호 운용
// Wasm 모듈 로드 및 사용
async function initWasm() {
// 방법 1: fetch + instantiate
const response = await fetch('module.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, {
env: {
// 호스트 함수를 Wasm에 제공
log_value: (value) => console.log('Wasm says:', value),
get_time: () => Date.now(),
}
});
// Wasm 함수 호출
const result = instance.exports.add(10, 20);
console.log('Result:', result); // 30
// 선형 메모리 접근
const memory = new Uint8Array(instance.exports.memory.buffer);
// 메모리에서 데이터 읽기/쓰기
return instance;
}
// 방법 2: wasm-bindgen (Rust)
import init, { fibonacci, ImageProcessor } from './pkg/my_wasm.js';
async function main() {
await init();
console.log('fib(40):', fibonacci(40));
const processor = new ImageProcessor(800, 600);
processor.grayscale();
}
2.3 브라우저 Wasm 주요 사용 사례
| 사용 사례 | 예시 | 성능 향상 |
|---|---|---|
| 이미지/비디오 처리 | Photoshop Web, FFmpeg.wasm | 10-50x JS 대비 |
| 게임 엔진 | Unity WebGL, Unreal Engine | 네이티브의 70-90% |
| 코덱/압축 | AV1 디코딩, Brotli 압축 | 5-20x JS 대비 |
| 암호화 | SHA-256, AES 연산 | 3-10x JS 대비 |
| CAD/3D 모델링 | AutoCAD Web, SketchUp | 네이티브 수준 |
| 과학 계산 | 시뮬레이션, 데이터 분석 | 10-100x JS 대비 |
| PDF 처리 | pdf.js 가속, 렌더링 | 3-5x 향상 |
3. WASI 0.2: 시스템 인터페이스
WASI(WebAssembly System Interface)는 Wasm이 브라우저 밖에서 파일시스템, 네트워크 등 시스템 리소스에 안전하게 접근하기 위한 표준이다.
3.1 WASI 0.2 인터페이스
WASI 0.2 구조:
+----------------------------------+
| wasi:cli (CLI 앱 지원) |
| wasi:http (HTTP 클라이언트/서버) |
| wasi:filesystem (파일시스템 접근) |
| wasi:sockets (네트워크 소켓) |
| wasi:clocks (시간 관련) |
| wasi:random (난수 생성) |
| wasi:io (스트림 I/O) |
+----------------------------------+
| Component Model (기반 레이어) |
+----------------------------------+
| Wasm Core (실행 엔진) |
+----------------------------------+
3.2 WASI HTTP 서버 (Rust)
// Rust + WASI HTTP 서버
use spin_sdk::http::{IntoResponse, Request, Response};
use spin_sdk::http_component;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct TodoItem {
id: u32,
title: String,
completed: bool,
}
#[http_component]
fn handle_request(req: Request) -> anyhow::Result<impl IntoResponse> {
let method = req.method().as_str();
let path = req.path();
match (method, path) {
("GET", "/api/todos") => {
let todos = vec![
TodoItem { id: 1, title: "Learn WASI".into(), completed: false },
TodoItem { id: 2, title: "Build Wasm app".into(), completed: false },
];
Ok(Response::builder()
.status(200)
.header("content-type", "application/json")
.body(serde_json::to_string(&todos)?)
.build())
},
("POST", "/api/todos") => {
let body: TodoItem = serde_json::from_slice(req.body())?;
Ok(Response::builder()
.status(201)
.header("content-type", "application/json")
.body(serde_json::to_string(&body)?)
.build())
},
_ => Ok(Response::builder()
.status(404)
.body("Not Found")
.build())
}
}
3.3 Capability-Based 보안 모델
WASI는 Capability-Based Security를 핵심으로 한다. 프로그램은 명시적으로 부여받은 권한만 사용할 수 있다.
# WASI 실행 시 권한 부여 예시
# 특정 디렉터리만 읽기 허용
wasmtime run --dir /data::/data:readonly my-app.wasm
# 네트워크 접근 허용
wasmtime run --tcplisten 0.0.0.0:8080 my-server.wasm
# 환경 변수 전달
wasmtime run --env KEY=VALUE my-app.wasm
기존 보안 모델 (Ambient Authority):
프로세스 -> [전체 파일시스템 접근 가능]
-> [모든 네트워크 접근 가능]
-> [환경 변수 모두 읽기 가능]
WASI 보안 모델 (Capability-Based):
Wasm 모듈 -> [/data 디렉터리만 읽기 가능]
-> [localhost:8080만 리슨 가능]
-> [지정된 환경 변수만 접근 가능]
4. Component Model: 모듈 조합
Component Model은 Wasm 모듈을 조합하여 복잡한 애플리케이션을 구성하는 표준이다.
4.1 WIT (Wasm Interface Type)
// todo.wit - 인터페이스 정의
package example:todo@0.1.0;
interface types {
record todo-item {
id: u32,
title: string,
completed: bool,
}
variant todo-error {
not-found(string),
validation-error(string),
storage-error(string),
}
}
interface todo-store {
use types.{todo-item, todo-error};
list-todos: func() -> result<list<todo-item>, todo-error>;
get-todo: func(id: u32) -> result<todo-item, todo-error>;
create-todo: func(title: string) -> result<todo-item, todo-error>;
update-todo: func(id: u32, title: string, completed: bool) -> result<todo-item, todo-error>;
delete-todo: func(id: u32) -> result<_, todo-error>;
}
world todo-app {
import wasi:http/outgoing-handler@0.2.0;
import wasi:logging/logging;
export todo-store;
}
4.2 Component 조합
# 여러 컴포넌트를 조합하여 하나의 앱 구성
# 1. 각 컴포넌트 빌드
cargo component build --release # Rust 컴포넌트
componentize-py -d todo.wit -w todo-app app.py # Python 컴포넌트
# 2. 컴포넌트 조합
wasm-tools compose \
--definitions auth-component.wasm \
--definitions db-component.wasm \
app-component.wasm \
-o composed-app.wasm
# 3. 조합된 컴포넌트 실행
wasmtime serve composed-app.wasm
4.3 Component Model의 장점
| 장점 | 설명 |
|---|---|
| 언어 독립적 조합 | Rust 모듈 + Python 모듈 + Go 모듈을 하나로 |
| 타입 안전한 인터페이스 | WIT로 정의된 계약 보장 |
| 샌드박스 격리 | 컴포넌트 간 메모리 격리 |
| 경량 | 컨테이너 대비 수십 배 가벼움 |
| 빠른 시작 | 마이크로초 단위 초기화 |
| 이식성 | 어디서나 실행 가능 |
5. 서버사이드 Wasm 프레임워크
5.1 Fermyon Spin
Spin은 Wasm 기반 마이크로서비스를 빌드하기 위한 프레임워크다.
# spin.toml - Spin 앱 설정
spin_manifest_version = 2
[application]
name = "my-api"
version = "1.0.0"
description = "Wasm 기반 API 서버"
[[trigger.http]]
route = "/api/hello"
component = "hello"
[[trigger.http]]
route = "/api/users/..."
component = "users"
[component.hello]
source = "target/wasm32-wasi/release/hello.wasm"
allowed_outbound_hosts = []
[component.users]
source = "target/wasm32-wasi/release/users.wasm"
allowed_outbound_hosts = ["https://api.example.com"]
key_value_stores = ["default"]
sqlite_databases = ["default"]
// Spin HTTP 핸들러 (Rust)
use spin_sdk::http::{IntoResponse, Request, Response};
use spin_sdk::http_component;
use spin_sdk::key_value::Store;
#[http_component]
fn handle_request(req: Request) -> anyhow::Result<impl IntoResponse> {
// Key-Value 스토어 사용
let store = Store::open_default()?;
match req.method().as_str() {
"GET" => {
let key = req.path().trim_start_matches("/api/users/");
match store.get(key)? {
Some(value) => Ok(Response::builder()
.status(200)
.header("content-type", "application/json")
.body(value)
.build()),
None => Ok(Response::builder()
.status(404)
.body("Not found")
.build()),
}
},
"POST" => {
let body = req.body().to_vec();
let key = format!("user:{}", uuid::Uuid::new_v4());
store.set(&key, &body)?;
Ok(Response::builder()
.status(201)
.header("content-type", "application/json")
.body(body)
.build())
},
_ => Ok(Response::builder()
.status(405)
.body("Method not allowed")
.build()),
}
}
# Spin 앱 빌드 및 실행
spin build
spin up # 로컬 실행 (포트 3000)
# Fermyon Cloud에 배포
spin deploy
5.2 wasmCloud
wasmCloud는 분산 Wasm 애플리케이션 플랫폼이다.
# wadm.yaml - wasmCloud 앱 매니페스트
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: my-api
annotations:
description: "wasmCloud HTTP API"
spec:
components:
- name: api-handler
type: component
properties:
image: ghcr.io/myorg/api-handler:0.1.0
traits:
- type: spreadscaler
properties:
instances: 5
- type: link
properties:
target: httpserver
namespace: wasi
package: http
interfaces: [incoming-handler]
- name: httpserver
type: capability
properties:
image: ghcr.io/wasmcloud/http-server:0.22.0
traits:
- type: link
properties:
target: api-handler
namespace: wasi
package: http
interfaces: [incoming-handler]
source_config:
- name: default-http
properties:
address: 0.0.0.0:8080
5.3 서버사이드 Wasm 프레임워크 비교
| 특성 | Fermyon Spin | wasmCloud | Lunatic |
|---|---|---|---|
| 초점 | HTTP 마이크로서비스 | 분산 애플리케이션 | 액터 모델 |
| 배포 모델 | 단일 노드 / 클라우드 | 분산 격자 (lattice) | 클러스터 |
| 스토리지 | KV, SQLite, Redis | Capability Provider | 내장 |
| 언어 지원 | Rust, Go, JS, Python | Rust, Go, AssemblyScript | Rust, AssemblyScript |
| 콜드 스타트 | 1ms 미만 | 1ms 미만 | 수 ms |
| 성숙도 | GA | GA | 초기 |
| 클라우드 서비스 | Fermyon Cloud | Cosmonic | 없음 |
6. Docker + Wasm
Docker Desktop은 Wasm 컨테이너를 네이티브로 지원한다.
6.1 Docker Wasm 아키텍처
기존 Linux 컨테이너:
Docker Engine -> containerd -> runc -> Linux Container
(full OS, 수십~수백 MB)
Wasm 컨테이너:
Docker Engine -> containerd -> runwasi -> Wasm Runtime (wasmtime/wasmedge)
(Wasm 모듈, 수 MB)
6.2 Wasm Docker 이미지 빌드
# Dockerfile.wasm - Wasm 컨테이너 이미지
FROM scratch
COPY target/wasm32-wasi/release/my-app.wasm /my-app.wasm
ENTRYPOINT ["/my-app.wasm"]
# Wasm 이미지 빌드 및 실행
docker buildx build --platform wasi/wasm -t my-wasm-app .
docker run --runtime=io.containerd.wasmtime.v1 \
--platform wasi/wasm \
-p 8080:8080 \
my-wasm-app
6.3 Docker Compose with Wasm
# docker-compose.yml - Wasm + Linux 컨테이너 혼합
version: "3"
services:
# Wasm 서비스 (초경량)
api:
image: my-wasm-api:latest
runtime: io.containerd.wasmtime.v1
platform: wasi/wasm
ports:
- "8080:8080"
environment:
- DB_HOST=postgres
# 전통적인 Linux 컨테이너
postgres:
image: postgres:16
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- pgdata:/var/lib/postgresql/data
# Wasm 워커 서비스
worker:
image: my-wasm-worker:latest
runtime: io.containerd.wasmtime.v1
platform: wasi/wasm
environment:
- QUEUE_URL=redis://redis:6379
redis:
image: redis:7-alpine
volumes:
pgdata:
6.4 Wasm vs Linux 컨테이너 비교
| 특성 | Linux 컨테이너 | Wasm 컨테이너 |
|---|---|---|
| 이미지 크기 | 50MB-1GB | 1-10MB |
| 시작 시간 | 100ms-수초 | 1ms 미만 |
| 메모리 사용 | 50MB-수GB | 1-50MB |
| 보안 모델 | 네임스페이스/cgroup | 샌드박스/Capability |
| 이식성 | Linux 커널 필요 | 아키텍처 독립 |
| 프로세스 격리 | OS 수준 | 언어 수준 |
| 네트워킹 | 완전 지원 | WASI 소켓 (제한적) |
| 파일시스템 | 완전 지원 | WASI FS (제한적) |
7. Edge Wasm
7.1 Cloudflare Workers
// Cloudflare Worker (Wasm 기반)
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname === '/api/compute') {
// Wasm 모듈로 무거운 계산 수행
const wasmModule = await import('./compute.wasm');
const result = wasmModule.heavy_computation(42);
return new Response(JSON.stringify({ result }), {
headers: { 'Content-Type': 'application/json' }
});
}
// KV 스토어 사용
if (url.pathname.startsWith('/api/cache/')) {
const key = url.pathname.split('/').pop();
if (request.method === 'GET') {
const value = await env.MY_KV.get(key);
return new Response(value || 'Not found', {
status: value ? 200 : 404
});
}
if (request.method === 'PUT') {
const body = await request.text();
await env.MY_KV.put(key, body, { expirationTtl: 3600 });
return new Response('OK');
}
}
return new Response('Not found', { status: 404 });
}
};
7.2 Fastly Compute
// Fastly Compute (Rust + Wasm)
use fastly::{Error, Request, Response};
use fastly::http::StatusCode;
#[fastly::main]
fn main(req: Request) -> Result<Response, Error> {
let path = req.get_path().to_string();
match path.as_str() {
"/api/hello" => {
Ok(Response::from_status(StatusCode::OK)
.with_body_text_plain("Hello from Fastly Compute!"))
},
path if path.starts_with("/api/proxy/") => {
// 백엔드로 프록시
let backend_path = path.trim_start_matches("/api/proxy");
let mut backend_req = req;
backend_req.set_path(backend_path);
backend_req.send("origin_backend")
},
_ => {
Ok(Response::from_status(StatusCode::NOT_FOUND)
.with_body_text_plain("Not found"))
}
}
}
7.3 Edge Wasm 플랫폼 비교
| 플랫폼 | 런타임 | 언어 | 콜드 스타트 | 무료 티어 |
|---|---|---|---|---|
| Cloudflare Workers | V8 + Wasm | JS, Rust, C++ | 0ms (상시 로드) | 10만 요청/일 |
| Fastly Compute | Wasmtime | Rust, Go, JS | 수 ms | 없음 |
| Vercel Edge Functions | V8 + Wasm | JS, TS | 0ms (상시 로드) | 100만 실행/월 |
| Deno Deploy | V8 + Wasm | JS, TS, Wasm | 0ms | 10만 요청/일 |
| Fermyon Cloud | Spin | Rust, Go, JS, Python | 1ms 미만 | 무료 베타 |
8. 언어별 Wasm 지원
8.1 Rust - 1등 시민
Rust는 Wasm의 가장 강력한 언어 지원을 제공한다.
// Cargo.toml
[package]
name = "my-wasm-app"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wit-bindgen = "0.25"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# 빌드 타겟
rustup target add wasm32-wasi # WASI 타겟
rustup target add wasm32-unknown-unknown # 브라우저 타겟
# 빌드
cargo build --target wasm32-wasi --release
# Component로 변환
wasm-tools component new target/wasm32-wasi/release/my_app.wasm \
-o my_app.component.wasm
8.2 Go - TinyGo
// main.go - TinyGo Wasm
package main
import (
"encoding/json"
"net/http"
spinhttp "github.com/fermyon/spin/sdk/go/v2/http"
)
type Response struct {
Message string `json:"message"`
Count int `json:"count"`
}
func init() {
spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {
resp := Response{
Message: "Hello from Go Wasm!",
Count: 42,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
})
}
func main() {}
# TinyGo로 Wasm 빌드
tinygo build -target=wasi -o main.wasm main.go
8.3 Python - componentize-py
# app.py - Python Wasm (Spin)
from spin_sdk.http import IncomingHandler, Request, Response
import json
class IncomingHandler(IncomingHandler):
def handle_request(self, request: Request) -> Response:
if request.method == "GET" and request.uri == "/api/hello":
body = json.dumps({
"message": "Hello from Python Wasm!",
"path": request.uri,
})
return Response(
200,
{"content-type": "application/json"},
bytes(body, "utf-8")
)
return Response(404, {}, b"Not found")
# Python -> Wasm 컴포넌트
componentize-py -d spin-http.wit -w spin-http-trigger app -o app.wasm
8.4 JavaScript - jco
// app.js - JavaScript Wasm Component
import { handle } from 'wasi:http/incoming-handler@0.2.0';
export const incomingHandler = {
handle(request, responseOut) {
const headers = new Headers();
headers.set('Content-Type', 'application/json');
const body = JSON.stringify({
message: 'Hello from JS Wasm!',
method: request.method(),
path: request.pathWithQuery(),
});
const response = new OutgoingResponse(headers);
response.setStatusCode(200);
const bodyStream = response.body();
bodyStream.write(new TextEncoder().encode(body));
bodyStream.close();
responseOut.set(response);
}
};
# JavaScript -> Wasm Component
jco componentize app.js -w wasi-http.wit -o app.wasm
8.5 언어 지원 비교
| 언어 | Wasm 성숙도 | WASI 지원 | Component Model | 바이너리 크기 | 비고 |
|---|---|---|---|---|---|
| Rust | 매우 높음 | 완전 | 완전 | 1-5MB | 1등 시민 |
| C/C++ | 높음 | 완전 | 부분적 | 0.5-3MB | Emscripten |
| Go (TinyGo) | 중간 | 완전 | 완전 | 2-10MB | 표준 Go 일부 미지원 |
| Python | 중간 | 완전 | 완전 | 10-30MB | componentize-py |
| JavaScript | 중간 | 완전 | 완전 | 5-15MB | jco |
| C# (.NET) | 중간 | 부분적 | 초기 | 10-50MB | NativeAOT-LLVM |
| Swift | 초기 | 부분적 | 초기 | 5-20MB | SwiftWasm |
| Kotlin | 초기 | 부분적 | 초기 | 10-30MB | Kotlin/Wasm |
9. 성능 비교
9.1 콜드 스타트 비교
| 플랫폼 | 콜드 스타트 | 메모리 사용 | 비고 |
|---|---|---|---|
| Wasm (Spin) | 0.5-1ms | 2-10MB | 마이크로초 수준 |
| Wasm (wasmtime) | 1-5ms | 5-20MB | 일반적 |
| Lambda (Python) | 150-300ms | 50-128MB | VPC 추가 시 더 느림 |
| Lambda (Java) | 800-3000ms | 128-512MB | SnapStart로 개선 가능 |
| Docker Container | 500-5000ms | 50MB-1GB | 이미지 크기 의존 |
| Cloud Run | 300-2000ms | 128MB-8GB | 인스턴스 타입 의존 |
9.2 처리량 비교
HTTP "Hello World" 벤치마크 (단일 인스턴스):
Spin (Rust/Wasm): ~150,000 req/s
Native Rust (Actix): ~200,000 req/s
Go (net/http): ~120,000 req/s
Node.js (Express): ~30,000 req/s
Python (FastAPI): ~10,000 req/s
Wasm은 네이티브의 70-90% 성능을 달성
9.3 메모리 효율성
단일 HTTP 서비스 메모리 사용:
Wasm (Spin): 2-5 MB
Wasm (wasmtime): 5-15 MB
Node.js: 30-80 MB
Python: 30-60 MB
Java (JVM): 100-300 MB
.NET: 50-150 MB
Go: 10-30 MB
1GB 메모리에서 동시 서비스 수:
Wasm: ~200개 서비스
Node.js: ~15개 서비스
Java: ~5개 서비스
10. 보안 모델
10.1 Wasm 샌드박스
전통적인 프로세스:
+------------------------------------------+
| 프로세스 |
| - 전체 파일시스템 접근 |
| - 모든 네트워크 접근 |
| - 시스템 콜 무제한 |
| - 다른 프로세스 메모리 접근 (exploit 시) |
+------------------------------------------+
Wasm 샌드박스:
+------------------------------------------+
| Wasm 모듈 |
| +------------------------------------+ |
| | 선형 메모리 (모듈 전용) | |
| | - 경계 검사 (bounds checking) | |
| | - 호스트 메모리 접근 불가 | |
| +------------------------------------+ |
| | 허가된 시스템 인터페이스만 사용 | |
| | - wasi:filesystem (지정 경로만) | |
| | - wasi:sockets (지정 주소만) | |
| +------------------------------------+ |
+------------------------------------------+
10.2 Capability-Based Security 원칙
| 원칙 | 설명 | 예시 |
|---|---|---|
| No Ambient Authority | 기본 권한 없음 | 파일시스템, 네트워크 접근 불가 |
| Principle of Least Privilege | 최소 권한만 부여 | 특정 디렉터리만 접근 허용 |
| Explicit Capability Passing | 명시적 권한 전달 | 호스트가 핸들을 전달 |
| Revocable | 권한 회수 가능 | 런타임에 권한 제거 |
10.3 보안 비교
| 보안 측면 | 컨테이너 | Wasm |
|---|---|---|
| 격리 수준 | OS 수준 (cgroup/namespace) | 언어 수준 (바이트코드 검증) |
| 메모리 안전 | 커널에 의존 | 경계 검사 내장 |
| 시스템 콜 | seccomp으로 필터링 | WASI로 제한 |
| 탈출 가능성 | 커널 취약점 시 가능 | 매우 어려움 |
| 권한 모델 | 유저/그룹 기반 | Capability 기반 |
| 코드 검증 | 없음 | 로드 시 검증 |
11. 실전 사용 사례
11.1 플러그인 시스템
// 호스트 애플리케이션 - 플러그인 로더
use wasmtime::*;
use wasmtime_wasi::WasiCtxBuilder;
fn load_plugin(plugin_path: &str) -> Result<(), Box<dyn std::error::Error>> {
let engine = Engine::default();
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |s| s)?;
// 플러그인에 최소 권한만 부여
let wasi = WasiCtxBuilder::new()
.inherit_stdout() // stdout만 허용
// 파일시스템 접근 없음
// 네트워크 접근 없음
.build();
let mut store = Store::new(&engine, wasi);
let module = Module::from_file(&engine, plugin_path)?;
let instance = linker.instantiate(&mut store, &module)?;
// 플러그인 함수 호출
let process = instance
.get_typed_func::<(i32,), i32>(&mut store, "process")?;
let result = process.call(&mut store, (42,))?;
println!("Plugin result: {}", result);
Ok(())
}
11.2 Edge Computing
Edge Wasm 아키텍처:
사용자 요청 -> [CDN Edge (300+ PoP)]
|
v
[Wasm Worker]
|
+-> 캐시 히트: 즉시 응답 (0ms)
|
+-> 캐시 미스: 원본 서버에 요청
| |
| v
| [Origin Server]
| |
v v
[응답 + 캐시 저장]
장점:
- 사용자에게 가장 가까운 위치에서 실행
- 콜드 스타트 거의 없음
- 글로벌 분산 자동 배포
11.3 Serverless Functions
Lambda vs Wasm Serverless:
AWS Lambda:
요청 -> API Gateway -> Lambda Runtime -> 함수 실행
콜드 스타트: 100ms-8s
메모리: 128MB-10GB
과금: 1ms 단위
Wasm Serverless (Spin/Fermyon):
요청 -> Spin Runtime -> Wasm 모듈 실행
콜드 스타트: 0.5ms 이하
메모리: 2-50MB
과금: 요청 단위
11.4 블록체인 스마트 컨트랙트
여러 블록체인이 Wasm을 스마트 컨트랙트 실행 엔진으로 채택했다.
| 블록체인 | Wasm 런타임 | 언어 지원 |
|---|---|---|
| Polkadot | Substrate | Rust (ink!) |
| Near | Wasmer | Rust, AssemblyScript |
| Cosmos (CosmWasm) | Wasmer | Rust |
| Dfinity (ICP) | 커스텀 | Rust, Motoko |
12. Wasm vs Container 비교
12.1 종합 비교
| 특성 | Wasm | Container |
|---|---|---|
| 시작 시간 | 마이크로초-밀리초 | 밀리초-초 |
| 이미지 크기 | 1-50MB | 50MB-수GB |
| 메모리 사용 | 1-50MB | 50MB-수GB |
| CPU 오버헤드 | 0-30% | 0-5% |
| 보안 | 강한 샌드박스 | OS 수준 격리 |
| 네트워킹 | WASI (제한적) | 완전 지원 |
| 파일시스템 | WASI (제한적) | 완전 지원 |
| GPU 지원 | 실험적 | 완전 지원 |
| 에코시스템 | 성장 중 | 매우 성숙 |
| 디버깅 | 발전 중 | 성숙 |
| 상태 관리 | Stateless 권장 | Stateful 가능 |
12.2 언제 Wasm을 선택하나
Wasm 선택 기준:
- 밀리초 이하 콜드 스타트 필요
- 극한의 경량 서비스
- 플러그인/확장 시스템
- Edge 컴퓨팅
- 다중 언어 모듈 조합
- 강한 샌드박스 격리 필요
- 제한된 리소스 환경 (IoT)
Container 선택 기준:
- 복잡한 네트워킹
- GPU 사용
- 레거시 애플리케이션
- 풍부한 에코시스템 필요
- 상태 보존 필요
- 대용량 메모리/스토리지
13. 미래 전망
13.1 진행 중인 Wasm 제안
| 제안 | 상태 | 설명 |
|---|---|---|
| GC (Garbage Collection) | Phase 4 | Java, Kotlin, Dart 등 GC 언어 지원 |
| Threads | Phase 3 | 공유 메모리 멀티스레딩 |
| SIMD | Phase 4 (완료) | 128비트 SIMD 연산 |
| Exception Handling | Phase 4 | try/catch 네이티브 지원 |
| Stack Switching | Phase 2 | 코루틴, async/await 지원 |
| Memory64 | Phase 3 | 64비트 메모리 주소 |
| Branch Hinting | Phase 3 | 분기 예측 힌트 |
| Tail Call | Phase 4 (완료) | 꼬리 호출 최적화 |
13.2 Wasm 미래 로드맵
2025-2026 전망:
- WASI 0.2 완전 안정화 및 주요 클라우드 채택
- Component Model 생태계 성숙
- Docker Wasm 프로덕션 레디
- GC proposal 주요 런타임 구현
- Threads proposal 안정화
2027+ 전망:
- Wasm이 컨테이너와 동등한 위상
- Edge-first 아키텍처의 기본 런타임
- IoT/임베디드 표준 실행 환경
- 다중 언어 컴포넌트 생태계 확립
14. 퀴즈
Q1. WASI의 핵심 보안 모델은 무엇인가?
정답: Capability-Based Security (권한 기반 보안)
WASI는 "No Ambient Authority" 원칙을 따른다. Wasm 모듈은 기본적으로 아무 권한도 없으며, 호스트가 명시적으로 부여한 capability(파일 핸들, 네트워크 소켓 등)만 사용할 수 있다. 이는 전통적인 Unix의 ambient authority 모델과 대비된다.
Q2. Wasm Component Model의 장점은?
정답:
- 언어 독립적 조합: Rust, Python, Go 등 서로 다른 언어로 작성된 모듈을 하나로 조합 가능
- WIT 인터페이스: 타입 안전한 계약으로 모듈 간 통신
- 샌드박스 격리: 각 컴포넌트는 자신만의 메모리 공간을 가짐
- 경량: 컨테이너 대비 수십 배 가벼움
Q3. Wasm의 콜드 스타트가 Lambda보다 빠른 이유는?
정답:
- Wasm 바이너리는 1-10MB로 매우 작아 로딩이 빠름
- 런타임 초기화가 마이크로초 수준 (JVM이나 Python 인터프리터 같은 무거운 초기화 없음)
- AOT 컴파일된 바이너리 직접 실행
- 선형 메모리 모델로 간단한 메모리 설정
- 프로세스/컨테이너 생성 오버헤드 없음
Q4. Docker + Wasm은 어떻게 동작하는가?
정답:
Docker Desktop은 containerd의 runwasi shim을 사용하여 Wasm 컨테이너를 실행한다. 기존의 runc 대신 wasmtime이나 wasmedge 같은 Wasm 런타임이 컨테이너를 실행한다. FROM scratch 기반 이미지에 .wasm 파일만 포함하여 초경량 이미지를 만들 수 있고, docker-compose에서 Linux 컨테이너와 Wasm 컨테이너를 혼합 운용할 수 있다.
Q5. Wasm이 아직 컨테이너를 대체할 수 없는 이유는?
정답:
- WASI의 시스템 인터페이스가 아직 제한적 (GPU, 복잡한 네트워킹 등)
- 에코시스템이 컨테이너에 비해 미성숙
- 레거시 애플리케이션 마이그레이션이 어려움
- 상태 관리가 기본적으로 stateless
- 디버깅 도구가 아직 발전 중
- 일부 언어의 Wasm 지원이 초기 단계
단, Wasm은 컨테이너를 "대체"하기보다 "보완"하는 기술로, Edge와 경량 서비스 영역에서 컨테이너와 공존할 전망이다.
15. 참고 자료
- WebAssembly 공식 사이트 - https://webassembly.org/
- WASI 공식 문서 - https://wasi.dev/
- Component Model 명세 - https://component-model.bytecodealliance.org/
- Fermyon Spin 문서 - https://developer.fermyon.com/spin/
- wasmCloud 문서 - https://wasmcloud.com/docs/
- Bytecode Alliance - https://bytecodealliance.org/
- wasmtime 런타임 - https://wasmtime.dev/
- Docker Wasm 가이드 - https://docs.docker.com/desktop/wasm/
- Cloudflare Workers - https://developers.cloudflare.com/workers/
- Fastly Compute - https://developer.fastly.com/learning/compute/
- wasm-tools - https://github.com/bytecodealliance/wasm-tools
- WIT 명세 - https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md
- componentize-py - https://github.com/bytecodealliance/componentize-py