---
title: "WebAssembly로 Python 확장하기 — 네이티브 툴체인 없이 10배 빠른 확장 모듈 배포"
published: 2026-01-01T22:09:16.000Z
canonical: https://jeff.news/article/292
---
# WebAssembly로 Python 확장하기 — 네이티브 툴체인 없이 10배 빠른 확장 모듈 배포

Python을 Wasm으로 확장하는 실전 가이드. wasmtime-py를 사용하면 네이티브 툴체인 없이 아키텍처 독립적인 확장 모듈을 배포할 수 있으며, 순수 Python 대비 약 10배 속도 향상이 가능하다. 포인터 부호 버그, Monocypher 암호화 라이브러리 임베딩, store 설계 결함 등 실전 함정과 우회법을 상세히 다룬다.

## 왜 Wasm으로 Python을 확장하는가

- Python은 전통적으로 C 인터페이스를 통해 네이티브 코드로 확장해왔음
- Wasm은 샌드박스 환경이라 외부 인터페이스 접근은 불가하지만, **순수 Python 대비 약 10배 속도 향상**이 가능함
- 아키텍처 독립적인 Wasm 바이너리를 Python 라이브러리에 포함시켜 배포하면, 타겟 시스템에 네이티브 툴체인 없이도 사용 가능함
- 다른 언어로 작성된 임베딩 가능한 기능(예: 암호화 라이브러리)을 Python에서 활용할 수 있게 됨

## Wasm 런타임 선택: wasm3 vs wasmtime-py

- **wasm3**: 순수 C로 작성되어 SQLite처럼 임베딩이 용이하지만, 소스 코드로만 배포됨 — 호스트에 C 툴체인이 필요하므로 본래 목적에 부합하지 않음
- **wasmtime-py** (권장): Windows/macOS/Linux의 x86-64 및 ARM64용 사전 빌드 바이너리 제공됨
  - wasm3 대비 **3배~10배 빠른** 성능을 보임
  - 단점: 설치 크기가 **약 18MiB**이며, API가 매달 깨지는 수준으로 변경됨
  - 글에서는 wasmtime-py 버전 40을 기준으로 설명함

## 치명적인 포인터 부호 버그

- Wasm은 포인터와 정수를 구분하지 않으며, 런타임은 모든 정수를 **부호 있는 값(signed)**으로 해석함
- 메모리 상위 절반의 주소를 가리키는 포인터가 **음수로 변환**되는 문제가 발생함
- wasmtime-py는 여기에 자체적인 함정을 추가함: `read`/`write` 메서드가 Python의 음수 인덱스 관례를 따르기 때문에, 음수 포인터가 경계 검사를 통과한 뒤 **잘못된 주소에 기록**됨
- 저자가 실제로 wasmtime-py를 사용하는 비(非)trivial 프로젝트를 검색한 결과 단 **하나**(샌드박스 PDF 리더)만 발견했으며, 해당 프로젝트에 이 버그가 정확히 존재함 — **Python 메모리 영역으로의 버퍼 오버플로우**가 가능한 상태였음
- 수정 방법: Wasm에서 나오는 모든 포인터를 마스크로 잘라내야 함 (`ptr & 0xFFFFFFFF`)
- 이는 Wasm 설계의 근본적 결함일 수 있으며, JavaScript에서도 동일한 문제가 발생함 (`ptr >>> 0`)

## 활용 사례 1: 더 빠른 Python

- Python의 연산 핫스팟을 C로 재구현하고 Wasm으로 컴파일하면 **약 10배 속도 향상**을 얻을 수 있음
- C는 원래 Python 대비 약 100배 빠르지만, Wasm 인터페이싱(데이터 복사 등) 오버헤드로 인해 10배 수준이 됨
- 벤치마크로 "Two Sum" 문제의 변형을 사용함
- wasmtime-py 덕분에 크로스 컴파일러 없이 배포 바이너리를 빌드할 수 있음

## 활용 사례 2: 임베디드 기능 — Monocypher 암호화 라이브러리

- Monocypher는 경량 암호화 라이브러리로, libc나 런타임 없이 곧바로 Wasm으로 컴파일 가능함
- **범프 할당자(bump allocator)** 패턴을 사용함: 키와 입력을 "스택"에 push → 암호화 루틴 실행 → 결과 복사 → 할당자 리셋(민감 데이터 자동 삭제)
- AEAD(Authenticated Encryption with Associated Data) 인터페이스를 구현하여 데이터 암호화/복호화를 시연함
- 논스와 키 생성에는 Python의 `secrets` 내장 패키지를 사용함
- Wasm으로 컴파일하면 마치 순수 Python처럼 암호화 기능을 사용할 수 있게 됨

## Store 설계 결함과 우회 방법

- wasmtime의 store가 컴파일과 인스턴스화를 하나로 묶어버리는 문제가 있음 — 한 번 컴파일하고 일회용 인스턴스를 자유롭게 생성하는 것이 불가능함
- 특히 WASI 프로그램처럼 매 실행마다 새 인스턴스가 필요한 경우 치명적인 결함이 됨
- **우회 방법**: `serialize`/`deserialize` 메서드를 사용하면 컴파일된 모듈을 store에서 분리하여 독립적인 인스턴스화가 가능함
- 역직렬화 시 검증 과정이 없어 오버헤드가 낮음

---

- 전체 소스 코드는 저자의 scratch 리포지터리에서 확인할 수 있음

## 핵심 포인트

- wasmtime-py는 Win/Mac/Linux x86-64/ARM64 사전 빌드 바이너리를 제공하며 wasm3 대비 3~10배 빠름 (약 18MiB)
- Wasm 포인터가 signed로 해석되는 치명적 버그 — 실제 프로젝트에서 Python 메모리 버퍼 오버플로우 발견됨
- C 함수를 Wasm으로 컴파일하면 순수 Python 대비 약 10배 속도 향상 가능
- Monocypher를 Wasm으로 컴파일하여 범프 할당자 패턴으로 Python에서 AEAD 암호화 구현
- wasmtime store의 컴파일/인스턴스화 결합 문제는 serialize/deserialize로 우회 가능

## 인사이트

Wasm이 Python 확장의 배포 문제를 깔끔하게 해결하지만, 포인터 부호 처리 같은 근본적 설계 결함이 도처에 숨어 있다. 실제로 wasmtime-py를 사용하는 유일한 비trivial 프로젝트에서 이 버그가 발견되었다는 사실이 생태계의 미성숙함을 보여준다.
