WebAssembly 실전 가이드 | C++/Rust를 웹에서 실행하는 방법

WebAssembly 실전 가이드 | C++/Rust를 웹에서 실행하는 방법

이 글의 핵심

WebAssembly로 C++/Rust 코드를 브라우저에서 네이티브 수준 성능으로 실행하는 방법. Emscripten, wasm-pack 사용법, JavaScript 연동, 최적화 기법을 실전 예제와 함께 설명합니다.

들어가며

WebAssembly(WASM)는 C++, Rust 같은 네이티브 언어를 브라우저에서 거의 네이티브 수준 성능으로 실행할 수 있게 해주는 바이너리 포맷입니다. 게임 엔진, 이미지/비디오 처리, 암호화, 과학 계산 등 CPU 집약적 작업을 웹에서 구현할 때 JavaScript의 한계를 넘어설 수 있습니다.

이 글은 C++과 Rust 코드를 WASM으로 컴파일하는 방법, JavaScript와 연동하는 방법, 성능 최적화 기법, 실무 사례를 단계별로 설명합니다.


목차

  1. WebAssembly란?
  2. C++로 WASM 만들기 (Emscripten)
  3. Rust로 WASM 만들기 (wasm-pack)
  4. JavaScript 연동
  5. 성능 최적화
  6. 실무 사례
  7. 트러블슈팅
  8. 마무리

WebAssembly란?

핵심 특징

1. 바이너리 포맷

  • 텍스트 형식 .wat (WebAssembly Text)
  • 바이너리 형식 .wasm (실제 배포 형식)
  • 파싱·컴파일 속도가 JavaScript보다 빠름

2. 샌드박스 실행

  • 브라우저 보안 모델 내에서 안전하게 실행
  • 메모리는 선형 배열(Linear Memory)로 격리
  • 직접 DOM 접근 불가 (JavaScript 통해 간접 접근)

3. 언어 독립적

  • C, C++, Rust, Go, AssemblyScript 등 다양한 언어 지원
  • LLVM 기반 컴파일러 체인 활용

아키텍처

┌─────────────┐
│ C++/Rust    │
│ Source Code │
└──────┬──────┘
       │ Compile

┌─────────────┐
│ .wasm       │
│ Binary      │
└──────┬──────┘
       │ Load

┌─────────────┐     ┌──────────────┐
│ JavaScript  │◄───►│ WASM Module  │
│ Glue Code   │     │ (Sandbox)    │
└─────────────┘     └──────────────┘


┌─────────────┐
│ Browser     │
│ (DOM/API)   │
└─────────────┘

C++로 WASM 만들기 (Emscripten)

Emscripten 설치

# Emscripten SDK 설치
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh  # Windows: emsdk_env.bat

기본 예제: 수학 함수

hello.cpp

#include <emscripten/emscripten.h>
#include <cmath>

// EMSCRIPTEN_KEEPALIVE: JavaScript에서 호출 가능하도록 export
extern "C" {
    EMSCRIPTEN_KEEPALIVE
    int add(int a, int b) {
        return a + b;
    }

    EMSCRIPTEN_KEEPALIVE
    double calculateDistance(double x1, double y1, double x2, double y2) {
        double dx = x2 - x1;
        double dy = y2 - y1;
        return std::sqrt(dx * dx + dy * dy);
    }

    EMSCRIPTEN_KEEPALIVE
    void processArray(int* arr, int size) {
        for (int i = 0; i < size; i++) {
            arr[i] = arr[i] * 2;
        }
    }
}

컴파일

# 기본 컴파일
emcc hello.cpp -o hello.js \
    -s EXPORTED_FUNCTIONS='["_add","_calculateDistance","_processArray"]' \
    -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'

# 최적화 빌드
emcc hello.cpp -o hello.js \
    -O3 \
    -s EXPORTED_FUNCTIONS='["_add","_calculateDistance","_processArray"]' \
    -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' \
    -s MODULARIZE=1 \
    -s EXPORT_NAME='createModule' \
    --closure 1

컴파일 옵션 설명:

  • -O3: 최대 최적화 (파일 크기와 성능 균형)
  • -s MODULARIZE=1: ES6 모듈로 export
  • --closure 1: Google Closure Compiler로 추가 압축
  • -s WASM=1: WASM 출력 (기본값)

JavaScript에서 호출

// hello.js와 hello.wasm 로드
const Module = await createModule();

// 함수 호출
const result = Module.ccall('add', 'number', ['number', 'number'], [10, 20]);
console.log(result);  // 30

// 또는 cwrap으로 래핑
const add = Module.cwrap('add', 'number', ['number', 'number']);
console.log(add(15, 25));  // 40

// 배열 처리
const size = 5;
const ptr = Module._malloc(size * 4);  // int = 4 bytes
const arr = new Int32Array(Module.HEAP32.buffer, ptr, size);
arr.set([1, 2, 3, 4, 5]);

Module.ccall('processArray', null, ['number', 'number'], [ptr, size]);
console.log(Array.from(arr));  // [2, 4, 6, 8, 10]

Module._free(ptr);  // 메모리 해제 필수!

이미지 처리 예제

image_processor.cpp

#include <emscripten/emscripten.h>
#include <cstdint>

extern "C" {
    EMSCRIPTEN_KEEPALIVE
    void grayscale(uint8_t* pixels, int width, int height) {
        for (int i = 0; i < width * height; i++) {
            int offset = i * 4;  // RGBA
            uint8_t r = pixels[offset];
            uint8_t g = pixels[offset + 1];
            uint8_t b = pixels[offset + 2];
            
            // 그레이스케일 변환
            uint8_t gray = static_cast<uint8_t>(0.299 * r + 0.587 * g + 0.114 * b);
            
            pixels[offset] = gray;
            pixels[offset + 1] = gray;
            pixels[offset + 2] = gray;
            // Alpha 채널은 유지
        }
    }

    EMSCRIPTEN_KEEPALIVE
    void blur(uint8_t* pixels, int width, int height, int radius) {
        // 간단한 박스 블러 구현
        // 실제로는 가우시안 블러나 최적화된 알고리즘 사용
        // ... (구현 생략)
    }
}
emcc image_processor.cpp -o image_processor.js \
    -O3 \
    -s EXPORTED_FUNCTIONS='["_grayscale","_blur"]' \
    -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' \
    -s MODULARIZE=1 \
    -s EXPORT_NAME='ImageProcessor' \
    -s ALLOW_MEMORY_GROWTH=1

JavaScript 연동:

const processor = await ImageProcessor();

// Canvas에서 이미지 데이터 가져오기
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

// WASM 메모리로 복사
const ptr = processor._malloc(imageData.data.length);
processor.HEAPU8.set(imageData.data, ptr);

// 그레이스케일 처리
processor.ccall('grayscale', null, 
    ['number', 'number', 'number'], 
    [ptr, canvas.width, canvas.height]
);

// 결과를 다시 Canvas로
const processed = new Uint8ClampedArray(
    processor.HEAPU8.buffer, ptr, imageData.data.length
);
imageData.data.set(processed);
ctx.putImageData(imageData, 0, 0);

processor._free(ptr);

Rust로 WASM 만들기 (wasm-pack)

환경 설정

# Rust 설치 (rustup.rs)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# wasm-pack 설치
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

# 프로젝트 생성
cargo new --lib my-wasm-project
cd my-wasm-project

Cargo.toml 설정:

[package]
name = "my-wasm-project"
version = "0.1.0"
edition = "2021"

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

[dependencies]
wasm-bindgen = "0.2"

[profile.release]
opt-level = 3
lto = true
codegen-units = 1

기본 예제

src/lib.rs

use wasm_bindgen::prelude::*;

// JavaScript에서 호출 가능한 함수
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u64 {
    match n {
        0 => 0,
        1 => 1,
        _ => {
            let mut a = 0u64;
            let mut b = 1u64;
            for _ in 2..=n {
                let temp = a + b;
                a = b;
                b = temp;
            }
            b
        }
    }
}

// 구조체 export
#[wasm_bindgen]
pub struct Calculator {
    value: f64,
}

#[wasm_bindgen]
impl Calculator {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Calculator {
        Calculator { value: 0.0 }
    }

    pub fn add(&mut self, x: f64) {
        self.value += x;
    }

    pub fn multiply(&mut self, x: f64) {
        self.value *= x;
    }

    pub fn get_value(&self) -> f64 {
        self.value
    }

    pub fn reset(&mut self) {
        self.value = 0.0;
    }
}

// console.log 사용
#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    log(&format!("Hello, {}!", name));
}

빌드

# 개발 빌드
wasm-pack build --target web

# 프로덕션 빌드 (최적화)
wasm-pack build --target web --release

# npm 패키지로 빌드
wasm-pack build --target bundler --release

JavaScript에서 사용

// ES6 모듈로 import
import init, { add, fibonacci, Calculator, greet } from './pkg/my_wasm_project.js';

async function run() {
    // WASM 초기화
    await init();

    // 함수 호출
    console.log(add(10, 20));  // 30
    console.log(fibonacci(10));  // 55

    // 구조체 사용
    const calc = new Calculator();
    calc.add(10);
    calc.multiply(2);
    console.log(calc.get_value());  // 20
    calc.free();  // 메모리 해제

    // console.log 호출
    greet('WASM');  // "Hello, WASM!"
}

run();

고급 예제: 이미지 필터

src/lib.rs

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct ImageProcessor {
    width: u32,
    height: u32,
    data: Vec<u8>,
}

#[wasm_bindgen]
impl ImageProcessor {
    #[wasm_bindgen(constructor)]
    pub fn new(width: u32, height: u32) -> ImageProcessor {
        let size = (width * height * 4) as usize;
        ImageProcessor {
            width,
            height,
            data: vec![0; size],
        }
    }

    pub fn get_data_ptr(&self) -> *const u8 {
        self.data.as_ptr()
    }

    pub fn set_pixel_data(&mut self, data: &[u8]) {
        self.data.copy_from_slice(data);
    }

    pub fn grayscale(&mut self) {
        for i in 0..(self.width * self.height) as usize {
            let offset = i * 4;
            let r = self.data[offset] as f32;
            let g = self.data[offset + 1] as f32;
            let b = self.data[offset + 2] as f32;

            let gray = (0.299 * r + 0.587 * g + 0.114 * b) as u8;

            self.data[offset] = gray;
            self.data[offset + 1] = gray;
            self.data[offset + 2] = gray;
        }
    }

    pub fn invert(&mut self) {
        for i in 0..(self.width * self.height) as usize {
            let offset = i * 4;
            self.data[offset] = 255 - self.data[offset];
            self.data[offset + 1] = 255 - self.data[offset + 1];
            self.data[offset + 2] = 255 - self.data[offset + 2];
        }
    }

    pub fn brightness(&mut self, factor: f32) {
        for i in 0..(self.width * self.height) as usize {
            let offset = i * 4;
            self.data[offset] = ((self.data[offset] as f32 * factor).min(255.0)) as u8;
            self.data[offset + 1] = ((self.data[offset + 1] as f32 * factor).min(255.0)) as u8;
            self.data[offset + 2] = ((self.data[offset + 2] as f32 * factor).min(255.0)) as u8;
        }
    }
}

JavaScript 사용:

import init, { ImageProcessor } from './pkg/my_wasm_project.js';

async function processImage() {
    await init();

    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

    // ImageProcessor 생성
    const processor = new ImageProcessor(canvas.width, canvas.height);
    
    // 이미지 데이터 복사
    processor.set_pixel_data(imageData.data);

    // 필터 적용
    processor.grayscale();
    // processor.invert();
    // processor.brightness(1.5);

    // 결과를 Canvas로
    const ptr = processor.get_data_ptr();
    const processed = new Uint8ClampedArray(
        wasm.memory.buffer, ptr, imageData.data.length
    );
    imageData.data.set(processed);
    ctx.putImageData(imageData, 0, 0);

    processor.free();
}

JavaScript 연동

데이터 전달 방식

1. 숫자 (Number)

#[wasm_bindgen]
pub fn process_number(x: f64) -> f64 {
    x * 2.0
}
const result = process_number(3.14);  // 6.28

2. 문자열 (String)

#[wasm_bindgen]
pub fn reverse_string(s: &str) -> String {
    s.chars().rev().collect()
}
const reversed = reverse_string("hello");  // "olleh"

3. 배열 (Array/Vec)

#[wasm_bindgen]
pub fn sum_array(arr: &[i32]) -> i32 {
    arr.iter().sum()
}

#[wasm_bindgen]
pub fn create_array(size: usize) -> Vec<i32> {
    (0..size as i32).collect()
}
const sum = sum_array(new Int32Array([1, 2, 3, 4, 5]));  // 15
const arr = create_array(10);  // [0, 1, 2, ..., 9]

4. 객체 (Struct)

#[wasm_bindgen]
pub struct Point {
    pub x: f64,
    pub y: f64,
}

#[wasm_bindgen]
impl Point {
    #[wasm_bindgen(constructor)]
    pub fn new(x: f64, y: f64) -> Point {
        Point { x, y }
    }

    pub fn distance(&self, other: &Point) -> f64 {
        let dx = self.x - other.x;
        let dy = self.y - other.y;
        (dx * dx + dy * dy).sqrt()
    }
}
const p1 = new Point(0, 0);
const p2 = new Point(3, 4);
console.log(p1.distance(p2));  // 5.0
p1.free();
p2.free();

메모리 관리

수동 메모리 관리 (C++ Emscripten):

// 메모리 할당
const ptr = Module._malloc(1024);

// 사용
const view = new Uint8Array(Module.HEAPU8.buffer, ptr, 1024);

// 해제 (필수!)
Module._free(ptr);

자동 메모리 관리 (Rust wasm-bindgen):

// Rust 객체는 .free() 호출 필요
const obj = new MyRustStruct();
obj.do_something();
obj.free();  // 명시적 해제

// 또는 try-finally
try {
    const obj = new MyRustStruct();
    obj.do_something();
} finally {
    obj.free();
}

성능 최적화

1. 컴파일 최적화

Emscripten (C++):

emcc source.cpp -o output.js \
    -O3 \                          # 최대 최적화
    -s WASM=1 \
    -s ALLOW_MEMORY_GROWTH=1 \     # 동적 메모리 증가
    -s INITIAL_MEMORY=16MB \       # 초기 메모리
    -s MAXIMUM_MEMORY=256MB \      # 최대 메모리
    -s STACK_SIZE=1MB \            # 스택 크기
    --closure 1 \                  # Closure Compiler
    -flto \                        # Link Time Optimization
    -s MODULARIZE=1

wasm-opt (추가 최적화):

# Binaryen wasm-opt 사용
wasm-opt input.wasm -O3 -o output.wasm

# 더 공격적인 최적화
wasm-opt input.wasm -O4 --enable-simd -o output.wasm

Rust:

[profile.release]
opt-level = 3           # 또는 'z' (크기 최적화), 's' (크기 우선)
lto = true              # Link Time Optimization
codegen-units = 1       # 단일 코드 생성 유닛 (느리지만 최적)
strip = true            # 디버그 심볼 제거
panic = 'abort'         # 패닉 시 언와인딩 비활성화

2. SIMD 활용

Rust SIMD 예제:

use std::arch::wasm32::*;

#[wasm_bindgen]
pub fn add_arrays_simd(a: &[f32], b: &[f32]) -> Vec<f32> {
    let mut result = Vec::with_capacity(a.len());
    
    unsafe {
        let chunks = a.len() / 4;
        for i in 0..chunks {
            let offset = i * 4;
            let va = v128_load(a.as_ptr().add(offset) as *const v128);
            let vb = v128_load(b.as_ptr().add(offset) as *const v128);
            let vr = f32x4_add(va, vb);
            
            let temp: [f32; 4] = std::mem::transmute(vr);
            result.extend_from_slice(&temp);
        }
        
        // 나머지 처리
        for i in (chunks * 4)..a.len() {
            result.push(a[i] + b[i]);
        }
    }
    
    result
}

3. 멀티스레딩

SharedArrayBuffer 사용:

# 멀티스레딩 지원 빌드
emcc source.cpp -o output.js \
    -pthread \
    -s USE_PTHREADS=1 \
    -s PTHREAD_POOL_SIZE=4

주의사항:

  • Cross-Origin-Opener-Policy: same-origin 헤더 필요
  • Cross-Origin-Embedder-Policy: require-corp 헤더 필요
  • 모든 브라우저에서 지원되지 않음 (보안 정책)

4. 파일 크기 최적화

전략:

  1. 불필요한 표준 라이브러리 제거

    # C++: -fno-exceptions, -fno-rtti
    emcc source.cpp -o output.js -O3 -fno-exceptions -fno-rtti
  2. Rust: 작은 할당자 사용

    [dependencies]
    wee_alloc = "0.4"
    #[global_allocator]
    static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
  3. 압축

    # Brotli 압축 (gzip보다 15-20% 더 작음)
    brotli output.wasm -o output.wasm.br
  4. 코드 분할

    • 필요한 기능만 별도 WASM 모듈로 분리
    • 동적 import로 필요할 때만 로드

실무 사례

1. Figma - 디자인 툴

사용 기술: C++ → WASM (Emscripten)

적용 분야:

  • 벡터 그래픽 렌더링 엔진
  • 복잡한 레이어 연산
  • 실시간 필터 효과

성과: JavaScript 대비 3-10배 빠른 렌더링 성능

2. Google Earth - 3D 지도

사용 기술: C++ → WASM

적용 분야:

  • 3D 지형 렌더링
  • 대용량 지도 데이터 처리
  • 실시간 카메라 제어

3. AutoCAD Web - CAD 소프트웨어

사용 기술: C++ (30년 코드베이스) → WASM

적용 분야:

  • 기존 데스크톱 코드 재사용
  • 복잡한 CAD 연산
  • 대용량 도면 파일 처리

4. Photoshop Web - 이미지 편집

사용 기술: C++ → WASM

적용 분야:

  • 이미지 필터 (블러, 샤프닝 등)
  • 레이어 합성
  • 색상 보정 알고리즘

5. 암호화 라이브러리

사용 기술: Rust → WASM

적용 분야:

  • AES, RSA 암호화
  • 해시 함수 (SHA-256, SHA-512)
  • 서명 검증

장점: 네이티브 수준 성능 + 메모리 안전성


트러블슈팅

문제 1: WASM 파일이 로드되지 않음

증상:

Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "application/octet-stream"

해결:

# nginx 설정
location ~ \.wasm$ {
    types {
        application/wasm wasm;
    }
    add_header Content-Type application/wasm;
}

문제 2: 메모리 부족 에러

증상:

RuntimeError: memory access out of bounds

해결:

# Emscripten: 메모리 증가 허용
emcc source.cpp -o output.js \
    -s ALLOW_MEMORY_GROWTH=1 \
    -s INITIAL_MEMORY=32MB \
    -s MAXIMUM_MEMORY=512MB

문제 3: 함수를 찾을 수 없음

증상:

TypeError: Module.myFunction is not a function

해결:

# 함수 export 확인
emcc source.cpp -o output.js \
    -s EXPORTED_FUNCTIONS='["_myFunction"]' \
    -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'

# C++ 코드에서 extern "C" 확인
extern "C" {
    EMSCRIPTEN_KEEPALIVE
    void myFunction() { }
}

문제 4: 성능이 기대보다 낮음

원인 분석:

  1. JavaScript ↔ WASM 경계 호출이 너무 잦음

    • 해결: 배치 처리, 큰 단위로 데이터 전달
  2. 메모리 복사 오버헤드

    • 해결: 공유 메모리 사용, 포인터 전달
  3. 디버그 빌드 사용

    • 해결: Release 빌드 (-O3, —release)
  4. SIMD 미사용

    • 해결: SIMD 명령어 활용

성능 측정:

console.time('WASM');
wasmFunction(data);
console.timeEnd('WASM');

console.time('JavaScript');
jsFunction(data);
console.timeEnd('JavaScript');

문제 5: 크로스 오리진 에러 (멀티스레딩)

증상:

SharedArrayBuffer is not defined

해결:

// 서버 응답 헤더 설정 필요
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
# Python Flask 예제
@app.after_request
def add_headers(response):
    response.headers['Cross-Origin-Opener-Policy'] = 'same-origin'
    response.headers['Cross-Origin-Embedder-Policy'] = 'require-corp'
    return response

성능 벤치마크

이미지 처리 (1920x1080 그레이스케일)

구현시간상대 성능
JavaScript (순수)45ms1.0x
JavaScript (TypedArray 최적화)28ms1.6x
WASM (C++)8ms5.6x
WASM (Rust)7ms6.4x
WASM (Rust + SIMD)3ms15.0x

수학 연산 (피보나치 40)

구현시간상대 성능
JavaScript850ms1.0x
WASM (C++)95ms8.9x
WASM (Rust)92ms9.2x

파일 크기 비교

구현원본최적화Brotli 압축
C++ (Emscripten)450KB180KB65KB
Rust (wasm-pack)320KB120KB42KB

실전 팁

1. 개발 워크플로우

# 개발 중: 빠른 빌드
wasm-pack build --dev

# 프로덕션: 최적화 빌드
wasm-pack build --release

# 파일 크기 확인
ls -lh pkg/*.wasm

# wasm-opt 추가 최적화
wasm-opt pkg/my_project_bg.wasm -O3 -o pkg/my_project_bg.wasm

2. 디버깅

Chrome DevTools:

  • Sources 패널에서 WASM 디버깅 지원
  • 중단점 설정 가능
  • 변수 검사 가능

console.log 추가:

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
}

pub fn debug_function() {
    log(&format!("Debug: value = {}", value));
}

3. 에러 처리

Rust:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn divide(a: f64, b: f64) -> Result<f64, JsValue> {
    if b == 0.0 {
        Err(JsValue::from_str("Division by zero"))
    } else {
        Ok(a / b)
    }
}

JavaScript:

try {
    const result = divide(10, 0);
} catch (error) {
    console.error('Error:', error);  // "Division by zero"
}

4. 점진적 마이그레이션

단계별 접근:

  1. 프로파일링: 병목 구간 식별
  2. 핫 패스 마이그레이션: CPU 집약적 부분만 WASM으로
  3. 벤치마크: 실제 성능 향상 측정
  4. 전체 마이그레이션: 필요 시 더 많은 부분 이식

예시:

// 1단계: JavaScript로 전체 구현
function processImage(imageData) {
    // 전체 로직
}

// 2단계: 핫 패스만 WASM으로
import { grayscale } from './wasm/image.js';

function processImage(imageData) {
    // 전처리 (JavaScript)
    
    // CPU 집약적 부분 (WASM)
    grayscale(imageData.data);
    
    // 후처리 (JavaScript)
}

마무리

WebAssembly는 웹에서 네이티브 수준 성능을 실현하는 강력한 도구입니다. 특히:

핵심 포인트:

  • C++: 기존 코드베이스 재사용, Emscripten 사용
  • Rust: 메모리 안전성, 현대적인 도구 체인 (wasm-pack)
  • 성능: JavaScript 대비 3-15배 빠름 (워크로드에 따라)
  • 적용 분야: 이미지/비디오 처리, 게임, 암호화, 과학 계산

시작 가이드:

  1. 간단한 수학 함수부터 시작
  2. 메모리 관리 패턴 익히기
  3. 실제 프로젝트에 점진적 적용
  4. 성능 측정 후 확장

다음 단계:

참고 자료: