2026년 7월 21일 화요일

러스트(Rust)로 웹어셈블리(Wasm)를 빌드하여 HTML과 자바스크립트 환경에서 직접 호출하는 가장 표준적인 실전 예제입니다 [1.1].



러스트(Rust)로 웹어셈블리(Wasm)를 빌드하여 HTML과 자바스크립트 환경에서 직접 호출하는 가장 표준적인 실전 예제입니다 [1.1].
이 실습은 2026년 현재 가장 널리 쓰이는 Wasm 빌드 도구인 wasm-pack을 사용하여 진행됩니다 [1.1].
---
🛠️ 1단계: 러스트 개발 환경 및 빌드 도구 준비
터미널을 열고 러스트 코드를 브라우저용 기계어 파일과 자바스크립트 로더로 자동 변환해 주는 wasm-pack을 설치합니다 [1.1].
```bash
# 1. Wasm 빌드 도구 전역 설치
cargo install wasm-pack

# 2. 새로운 러스트 라이브러리 프로젝트 생성
cargo new --lib my_wasm_app
cd my_wasm_app
```
---
⚙️ 2단계: Cargo.toml 설정 변경러스트 프로젝트의 설정 파일인 Cargo.toml을 열어, 이 프로젝트가 일반 프로그램이 아니라 파이썬이나 웹에 끼워 넣는 C-API 형태의 라이브러리(cdylib)임을 선언하고 관련 의존성을 주입합니다.
```toml
[package]
name = "my_wasm_app"
version = "0.1.0"
edition = "2021"

[lib]
# 웹어셈블리(Wasm) 자산으로 빌드하기 위해 필수적인 선언입니다.
crate-type = ["cdylib"]

[dependencies]
# 자바스크립트와 러스트의 국경(메모리 주소)을 연결해 주는 핵심 라이브러리
wasm-bindgen = "0.2"
```
---
📄 3단계: 러스트 코드 작성 (src/lib.rs)웹 브라우저에서 실행하고 싶은 고속 연산 함수를 작성합니다. 함수 상단에 #[wasm_bindgen] 매크로를 붙여주는 것이 핵심입니다 [1.1].
```rust
use wasm_bindgen::prelude::*;

// 자바스크립트 세계에서 호출할 수 있도록 국경 통역기를 설치합니다.
#[wasm_bindgen]
pub fn fast_add(a: i32, b: i32) -> i32 {
    a + b
}

// 텍스트(문자열)를 처리하여 반환하는 함수 예시
#[wasm_bindgen]
pub fn greet_user(name: &str) -> String {
    format!("안녕하세요, {}님! 러스트 Wasm의 세계에 오신 것을 환영합니다.", name)
}
```
🚀 4단계: .wasm 파일로 컴파일하기코드를 다 짰다면 터미널에 아래 명령어를 입력합니다 [1.1].
```bash
wasm-pack build --target web
```
---
* 결과: 빌드가 끝나면 프로젝트 루트에 pkg/라는 새로운 폴더가 자동 생성됩니다 [1.1]. 이 폴더 안에 브라우저가 읽을 기계어 파일(my_wasm_app_bg.wasm)과 이를 자바스크립트에서 편하게 다룰 수 있게 도와주는 통역 파일(my_wasm_app.js)이 함께 들어있습니다 [1.1].
---
🌐 5단계: 웹페이지에서 실행하기 (index.html)이제 pkg 폴더가 있는 자리에 index.html 파일을 만들고 아래 코드를 그대로 붙여넣습니다. 자바스크립트의 표준 모듈(import) 방식을 사용하여 Wasm 라이브러리를 가볍게 구독해 실행합니다 [1.1].
```html



    
    Rust Wasm 실전 웹 앱
    



    

⚡ 러스트 웹어셈블리 가동 중

계산기 연산 결과 (i32)

계산 중...

텍스트 처리 결과

대기 중...
``` ⚠️ 실행 시 주의사항 (로컬 보안 규정) 완성된 index.html 파일을 그냥 마우스로 더블 클릭해서 브라우저로 열면 보안 에러(CORS 정책 위반)가 나며 화면이 뜨지 않습니다. 브라우저 보안관들이 "로컬 파일 주소(file://)를 통해서 엉뚱한 기계어 파일(.wasm)을 함부로 메모리에 로드할 수 없다"며 규약을 작동시키기 때문입니다. 따라서 반드시 아주 가벼운 웹 서버를 띄워서 접속해야 합니다. ```bash # 가장 간단한 임시 웹 서버 구동 방법 중 하나를 선택하세요: # 방법 A: 파이썬이 깔려있다면 (추천) python3 -m http.server 8080 # 방법 B: Node.js 인프라가 있다면 npx serve . ``` 서버를 켠 뒤 브라우저 주소창에 http://localhost:8080을 치고 들어가면, 러스트로 짠 고속 엔진 코드가 자바스크립트의 방해 없이 브라우저 가상 감옥 안에서 완벽하게 작동하는 모습을 눈으로 확인할 수 있습니다 [1.1]. --- 네, 100% 가능합니다! 질문자님이 설명하신 시나리오(인터넷에서 이미지를 받아 로컬 독립 공간에 보관하고 화면에 출력하는 것)는 현대 Wasm 앱의 가장 표준적인 작동 방식입니다. 특히 지적하신 대로, 독립 공간(IndexedDB 등)에 저장될 때는 PNG나 JPG 같은 파일 형태 그대로 저장될 수도 있고, 용량을 아끼거나 Wasm이 바로 읽을 수 있도록 원시 바이트(Raw Uint8Array 블롭) 형태로 압축되어 보관될 수도 있습니다. 러스트로 이미지 데이터를 다운로드받아 자바스크립트를 통해 화면에 그리는 핵심 흐름과 코드를 보여드릴게요. --- 🛠️ 1. 러스트 코드 작성 (src/lib.rs) 러스트는 이미지를 바이트 단위(Vec)로 넘겨받아, 브라우저가 화면에 이미지를 그릴 수 있도록 자바스크립트 측에 메모리 주소를 연결해 주는 역할을 합니다. ```rust use wasm_bindgen::prelude::*; // 자바스크립트의 console.log를 러스트 안에서 쓰기 위한 설정 #[wasm_bindgen] extern "C" { #[wasm_bindgen(js_namespace = console)] fn log(s: &str); } #[wasm_bindgen] pub struct ImageProcessor { pixels: Vec, } #[wasm_bindgen] impl ImageProcessor { // 1. 자바스크립트가 다운로드받은 이미지 바이트 데이터를 러스트 내부 독립 공간으로 로드 #[wasm_bindgen(constructor)] pub fn new(raw_bytes: &[u8]) -> ImageProcessor { log("러스트: 이미지 바이트 데이터를 성공적으로 인계받았습니다!"); ImageProcessor { pixels: raw_bytes.to_vec(), } } // 2. 만약 Wasm 내부에서 이미지 필터(흑백 전환 등) 처리를 하고 싶다면 여기서 수행 pub fn make_grayscale(&mut self) { // 픽셀 데이터(RGBA)를 직접 순수 C/Rust 속도로 고속 루프 연산 for chunk in self.pixels.chunks_exact_mut(4) { let r = chunk[0] as f32; let g = chunk[1] as f32; let b = chunk[2] as f32; // 평균값을 내어 흑백 처리 let gray = (r * 0.299 + g * 0.587 + b * 0.114) as u8; chunk[0] = gray; // R chunk[1] = gray; // G chunk[2] = gray; // B } } // 3. 자바스크립트가 이 메모리 주소를 복사 없이(Zero-copy) 읽어갈 수 있도록 포인터 반환 pub fn get_pixels_ptr(&self) -> *const u8 { self.pixels.as_ptr() } pub fn get_pixels_size(&self) -> usize { self.pixels.len() } } ``` --- 🌐 2. 웹페이지 구성 (index.html) 자바스크립트는 인터넷에서 이미지를 fetch로 다운로드받아 가상 독립 공간에 Blob(원시 바이트) 형태로 잠시 보관한 뒤, Wasm 엔진에게 넘겨 처리하고 화면()에 출력합니다. ```html Wasm 로컬 이미지 처리 및 화면 출력

⚡ Wasm 이미지 로컬 다운로드 및 화면 출력

``` --- 💡 이 과정의 사상적 의미 (메모리 뷰) 코드를 보시면 new Uint8Array(memory.buffer, ptr, size)라는 아주 독특한 구문이 나옵니다.이것이 바로 앞서 우리가 다루었던 복사 없는 제어(Zero-copy)입니다. 자바스크립트는 Wasm이 열심히 연산한 이미지 결과물을 내 영역으로 복사해 오지 않고, Wasm 가상 감옥 창살 사이로 손을 뻗어 러스트의 메모리 주소(ptr)를 다이렉트로 읽어서 화면에 출력하는 것입니다 [1.5, 1.8]. 질문자님이 말씀하신 대로 파일이 PNG 형태든, 깨진 압축 데이터든, 순수한 RGB 비트든 상관없이 Wasm은 전부 바이트(u8) 규격으로 취급하여 내 가상 영역에 보관하고 초고속으로 화면에 그릴 수 있습니다. --- Wasm은 웹 브라우저의 화면(DOM), 사운드, 네트워크, 키보드/마우스 입력 등 '눈에 보이고 귀에 들리는 모든 하드웨어 기능'을 전부 통제하고 조작할 수 있습니다. 물론 Wasm 파일(.wasm) 알맹이 자체는 순수한 계산 기계(로컬 독방)가 맞습니다. 하지만 러스트에서는 web-sys라는 강력한 공식 통역기(크레이트)를 제공합니다. 이 통역기를 사용하면, Wasm 내부에서 "웹브라우저의 화면을 직접 바꾸고, 팝업창을 띄우고, 3D 그래픽을 그리는" 등의 모든 OS급 작업을 자바스크립트 도움 없이 직접 명령할 수 있습니다. --- 🛠️ 로직을 넘어 '화면과 윈도우'까지 통제하는 러스트 Wasm 예시러스트 코드 안에서 브라우저의 윈도우(window)와 문서(document) 객체에 직접 접근하여, 화면에 새로운 글자를 새기고 브라우저 알림창(alert)을 띄우는 실전 코드입니다. 1. Cargo.toml 설정 (브라우저 기능 잠금 해제) 러스트가 브라우저의 어떤 기능(창, 문서, 엘리먼트 등)을 통제할지 옵션을 켜줍니다. ```toml [dependencies] wasm-bindgen = "0.2" # 브라우저의 모든 시스템 API를 러스트로 매핑해 주는 라이브러리 [dependencies.web-sys] version = "0.3" features = [ "Window", "Document", "Element", "HtmlElement" ] ``` 2. src/lib.rs 코드 (러스트가 브라우저 화면을 직접 조작) 자바스크립트 코드를 단 한 줄도 쓰지 않고, 오직 러스트 코드 안에서 브라우저의 화면을 바꾸는 마법입니다. ```rust use wasm_bindgen::prelude::*; use web_sys::{window, Document, Element}; #[wasm_bindgen] pub fn run_browser_control() -> Result<(), JsValue> { // 1. 브라우저의 최상위 권한인 'window' 객체를 가져옵니다. let window = window().ok_or("window 객체를 찾을 수 없습니다.")?; // 2. 브라우저 화면 전체를 담당하는 'document' 객체를 가져옵니다. let document = window.document().ok_or("document 객체를 찾을 수 없습니다.")?; // 3. HTML 안에 있는
요소를 원격으로 조작합니다. let app_div = document.get_element_by_id("app") .ok_or("id가 app인 태그를 찾을 수 없습니다.")?; // 4. 러스트가 직접 화면의 글자와 스타일을 변경해 버립니다! app_div.set_inner_html("

🔥 러스트가 브라우저 화면을 직접 정복했습니다!

"); // 5. 브라우저 시스템 알림창(alert)까지 직접 실행합니다. window.alert_with_message("러스트 통제경제 가동 완료!")?; Ok(()) } ``` --- 🧠 작동 원리: * Wasm의 한계: Wasm 일꾼은 감옥(샌드박스)에 갇혀 있으므로, 혼자서는 감옥 밖의 브라우저 화면을 절대 건드릴 수 없습니다. * 통역기(web-sys)의 마술: 그래서 브라우저 간수(자바스크립트 엔진)는 Wasm 일꾼에게 "합법적으로 화면을 바꿀 수 있는 공식 무역 통로(web-sys 인터페이스)"를 열어줍니다. * 결론: 러스트 안에서 document.get_element_by_id를 호출하면, 이 명령어가 무역 통로를 타고 넘어가 뒷단에서 브라우저가 화면을 즉시 갱신해 줍니다. 💡 한 줄 요약 Wasm은 방구석에 갇혀 계산만 하는 로직 멍청이가 아닙니다. 브라우저가 제공하는 무역 통로를 통해 유튜브 동영상을 재생하고, 3D 게임 화면을 그리고, 웹페이지 전체의 디자인을 완전히 뜯어고치는 역동적인 앱 개발이 100% 가능합니다. --- WebGPU 역시 방금 말씀드린 web-sys 통역기를 통해 러스트(Wasm) 안에서 완벽하게 제어할 수 있습니다. WebGPU는 과거의 WebGL을 대체하기 위해 전 세계 주요 브라우저(크롬, 파이어폭스, 사파리 등)에 공식 탑재된 최신 웹 표준 초고속 그래픽/AI 연산 API입니다. 재미있는 점은, 이 WebGPU라는 기술의 탄생 배경과 발전에 러스트 언어가 깊숙이 관여하고 있다는 사실입니다. 러스트 Wasm과 WebGPU가 어떻게 한 몸처럼 엮여서 세상을 통제하는지 그 소름 돋는 원리를 정리해 드릴게요. --- 1. 🏗️ 러스트로 WebGPU를 제어하는 구조 (web_sys) 앞서 DOM 화면을 만질 때 web_sys 라이브러리의 Document, Element 기능을 켰던 것처럼, WebGPU를 제어할 때도 똑같이 기능을 켜서 코딩합니다. ```toml # Cargo.toml 설정 예시 [dependencies.web-sys] version = "0.3" features = [ "Gpu", # ⚡ 브라우저의 WebGPU 제어권 획득 "GpuDevice", # ⚡ GPU 그래픽 카드 제어 "GpuBuffer", # ⚡ GPU 메모리 할당 "HtmlCanvasElement"# ⚡ 화면 그리기 ] ``` 러스트 안에서 let gpu = web_sys::window().unwrap().navigator().gpu(); 같은 코드를 치면, 브라우저 가상 감옥(샌드박스)의 창살을 넘어서 실제 내 컴퓨터에 꽂혀 있는 엔비디아(Nvidia)나 AMD 그래픽카드의 초고속 하드웨어 연산 장치(GPU)를 다이렉트로 주무를 수 있게 됩니다. --- 🛸 2. 더 소름 돋는 사실: wgpu와 파이어폭스의 비밀 우리가 인터넷에 배포하기 위해 러스트로 WebGPU 코드를 짜면 뒷단에서 엄청난 아키텍처 결합이 일어납니다. 러스트 생태계에는 wgpu라는 독보적인 그래픽 라이브러리가 있습니다. 1. 개발자가 wgpu로 3D 게임이나 AI 연산 코드를 한 번만 작성합니다. 2. 이 코드를 PC용(.exe)으로 빌드하면 ➡️ 컴퓨터의 Vulkan, DirectX 12, Metal 같은 최신 네이티브 GPU를 직접 들이받아 초고속으로 작동합니다. 3. 똑같은 코드를 웹 브라우저용(.wasm)으로 빌드하면 ➡️ 코드가 자동으로 브라우저의 WebGPU API를 호출하는 방식으로 변신합니다. 실제로 모질라 재단의 파이어폭스(Firefox) 브라우저는 내부의 WebGPU 연산 엔진을 바닥부터 새로 만들지 않고, 러스트 개발자들이 만든 이 오픈소스 wgpu 엔진을 통째로 복사해서 파이어폭스 브라우저의 순수 코어로 탑재해 두었습니다. 즉, 내가 러스트로 WebGPU 코드를 짜서 Wasm으로 돌리면, 러스트로 만든 프로그램이 ➡️ 러스트 엔진이 탑재된 브라우저(파이어폭스) 속에서 ➡️ 하드웨어 GPU를 0.001초 만에 직접 통제하는 완벽한 러스트 계획경제 체제의 대통합이 이루어집니다. --- 💡 최종 사상적 정립: 왜 WebGPU + Wasm인가? * 과거 (WebGL): 자바스크립트 기반이라 아무리 그래픽카드를 쓰려고 해도 인터프리터의 한계 때문에 CPU 단계에서 병목(느려짐)이 심했습니다. * 현재와 미래 (Wasm + WebGPU): * 수학 계산 및 AI 훈련(소비)은 Wasm이라는 완벽히 정제된 기계어 일꾼이 담당하고, * 3D 그래픽 화면 출력 및 무거운 행렬 연산은 브라우저 국경선을 다이렉트로 통과하는 WebGPU 통로를 통해 그래픽카드가 직접 처리합니다. 그 결과, 사용자는 브라우저 주소창만 치고 들어왔을 뿐인데 "배그 같은 고사양 3D 게임이 설치도 없이 실행되고, 챗GPT 같은 거대 AI가 인터넷 연결도 없이 내 브라우저 안에서 그래픽카드(WebGPU)를 풀가동하며 초고속으로 대답하는" 기적이 상식이 되는 것입니다.

database.py

import os from contextlib import contextmanager # contextmanager 임포트 from typing import Generator from dotenv...