Wasm은 WebAssembly의 약자가 아니다

I
Inkyu Oh

Front-End2025.11.14

Thunderseethe's Devlog 블로그 포스트 번역
2025-07-15


“Wasm does not stand for WebAssembly”라는 주장은 터무니없게 들릴 수 있습니다. webassembly.org를 보면 이는 단순한 소리가 아님을 확인할 수 있습니다.

다행히도, 저는 좋은 논점을 위해 이성을 방해하지 않는 사람입니다. Wasm과 WebAssembly라는 이름의 관계에 대해 이야기해 봅시다.
Wasm(길게는 WebAssembly)의 지지자로서, 저는 사람들에게 그 장점을 전파하는 데 많은 시간을 보냅니다. 제 연단에 서서 Wasm에 대한 두 가지 일반적인 오해를 접하게 됩니다:
  • Wasm은 웹 기술이다
  • Wasm은 어셈블리 언어이다
이름 때문에 이해할 수 있는 실수이지만, 이는 Wasm에 익숙하지 않은 사람들이 Wasm을 무시하고 더 깊이 파고들지 않게 만듭니다. 이는 Wasm이 제공할 수 있는 많은 것들 중 웹 기술이나 어셈블리 언어가 없기 때문에 아쉬운 일입니다. 이러한 오해를 해소하고 Wasm의 컴파일 타겟으로서의 강력함을 설득하고 싶습니다.
Wasm의 얼굴에서 WebAssembly 가면을 벗기면, 실제로 바이트코드임을 알 수 있습니다. x86-64나 Arm과 같은 어셈블리 언어가 아니라, Wasm은 JVM이나 .NET 바이트코드와 더 많은 공통점을 가지고 있습니다. Wasm은 바이트코드로서 실제 CPU가 아닌 가상 머신(VM)에서 실행됩니다. 이 가상 머신은 브라우저 특정 기능이 없습니다. 이 기사를 작성할 당시 Wasm은 DOM과 상호작용할 방법이 없습니다. 서버에서 실행되는 가상 머신을 작성하거나 임베디드 장치에서 실행할 수 있습니다. 유명한 폰트 셰이핑 엔진인 Harfbuzz폰트에 Wasm을 포함하여 사용자 정의 셰이핑을 수행할 수 있게 해줍니다. 이는 저를 매혹시키고 동시에 경계하게 만듭니다.
어떻게 그들이 은밀한 폰트가 내 모든 데이터를 훔치거나 심지어 뉴스레터에 가입시키는 악성 코드를 실행하지 않을 것이라고 확신할 수 있을까요? 비밀은 Wasm의 실행 모델에 있습니다. Wasm 런타임이라고 부르고 싶은 모든 VM은 Wasm 사양을 준수해야 합니다. 이 사양의 큰 부분은 Wasm의 실행이 호스트로부터 샌드박스화되도록 보장하는 데 할애되어 있습니다. 호스트가 파일 시스템과 상호작용하거나 네트워크와 대화할 수 있는 기능을 명시적으로 노출하지 않으면 Wasm은 기본적으로 이를 수행할 수 없습니다. Harfbuzz는 폰트에 포함된 Wasm에 아무것도 노출하지 않으므로 Wasm이 악의적인 행동을 하지 않을 것이라고 신뢰할 수 있습니다.
이 샌드박싱이 제공하는 보안은 Wasm의 주요 판매 포인트 중 하나입니다. 이는 사람들이 임의의 실행을 허용하고 싶지만 해킹당하고 싶지 않은 곳 어디에서나 가치를 제공할 수 있습니다. 이는 웹 안팎에서 많은 곳에서 유용합니다.

이름만의 웹 어셈블리, 실제로는 둘 다 아님

그렇다면 여기서 무슨 일이 일어나고 있는 걸까요? Wasm이 어셈블리가 아니고, 특별히 웹도 아닌데 왜 이런 이름이 붙었을까요? Wasm의 기원 이야기에서 그 답을 찾을 수 있습니다. 2015년경, 프로그래머들은 자바스크립트를 작성하고 싶지 않지만 동시에 브라우저에서 앱을 실행하고 싶어했습니다.
시도는 새로운 "더 나은" 언어를 브라우저에 추가하려는 시도가 있었지만, 이는 실패했습니다. 새로운 표면 언어를 추가하는 것은 문제를 일시적으로만 해결합니다. 결국 사람들은 자바스크립트와 함께 새로운 언어를 피하고 싶어할 것입니다. 새로운 언어는 기존 코드베이스를 웹으로 가져오고 싶어하는 사람들에게 아무런 도움이 되지 않습니다. 그들은 여전히 자바스크립트가 아닌 새로운 언어로 앱을 다시 작성해야 하는 똑같이 불쾌한 작업에 직면하게 됩니다.
다른 해결책이 필요했습니다. 브라우저가 자바스크립트로만 말한다면, 우리는 단순히 컴파일러가 자바스크립트를 말하도록 가르쳐야 합니다. asm.js는 이 필요를 충족시키기 위해 등장했습니다. 이는 컴파일러가 타겟으로 삼도록 의도된 자바스크립트의 하위 집합입니다. asm.js는 기술적으로 놀라운 성과를 이루어 "일반" 자바스크립트보다 네이티브에 훨씬 가까운 성능을 제공합니다.
자바스크립트에서 과 비트 연산을 하면, 예를 들어 처럼, 출력은 반드시 정수여야 한다는 것을 알고 계셨나요? 저도 몰랐지만, 자바스크립트 VM은 알고 있으며 를 기본적으로 느린 Number 클래스가 아닌 정수로 최적화합니다. asm.js는 이러한 트릭을 사용하여 VM이 우리의 컴파일된 언어를 효율적으로 실행하도록 유도합니다. 이는 할 수 있다는 점에서 멋지지만, 루브 골드버그 기계처럼 멋진 것이지 아르키메데스의 지렛대 법칙처럼 멋진 것은 아닙니다.
우리의 컴파일된 언어는 이미 우리의 값이 정수임을 알고 있었고, 우리의 VM은 이미 정수를 처리하는 방법을 알고 있습니다. 우리는 자바스크립트가 우리의 실행을 중재하기 때문에 이러한 전술을 사용해야만 합니다. 자바스크립트와 대화하는 대신 모든 브라우저에 포함된 VM과 직접 대화할 수 있다면 좋지 않을까요? 그러나 브라우저는 단일 VM을 제공하지 않으며, 각 브라우저는 자체 VM(V8이 90%의 브라우저에 있지만 여전히 기술적으로 대안이 있음)을 제공하며, 이를 타겟으로 하는 방법을 변경하는 API 차이가 있습니다. 자바스크립트는 통합된 인터페이스를 제공하지만, 우리의 목적에는 이상적이지 않습니다.
Wasm은 자바스크립트를 타겟으로 하는 것에서 우리를 구하기 위해 등장했습니다. 이는 컴파일된 언어에 훨씬 더 적합한 브라우저 실행에 대한 새로운 통합 인터페이스를 제공합니다. 따라서 WebAssembly라는 이름이 어떻게 생겨났는지 알 수 있습니다. asm.js의 작업을 기반으로 Wasm은 브라우저에 대한 새로운 저수준 인터페이스를 제공합니다.

그들은 처음부터 알고 있었다

이 기원은 이름을 설명하지만, 구현을 포함하지는 않습니다. Wasm의 창시자들은 단순히 새로운 웹 인터페이스를 만드는 것이 아니라 일반적인 계산을 위한 새로운 사양을 만들고자 했습니다. Wasm의 창시자 중 한 명인 Andreas Rossberg는 WebAssembly라는 이름을 프로젝트에 자금을 지원하도록 설득하기 위한 이름으로 설명합니다(한 번이 아니라 두 번!). Wasm의 팀은 단순히 웹 기술을 만들려고 한 것이 아니었습니다.
Wasm을 위해 설계된 휴대용 바이트코드는 널리 적용 가능하며 자바스크립트의 모든 웹 특정 기술이 없습니다. 또한 상당히 고수준이어서 어셈블리보다 훨씬 더 쉬운 컴파일러 타겟입니다. 예외, 구조체, 고차 함수와 같은 기능을 기본적으로 포함하여 이를 더 낮은 수준의 구조로 매핑할 필요가 없습니다. 언어에 비동기 및 기타 제한된 제어 흐름 패턴을 지원하기 위해 타입이 지정된 연속성을 추가하는 논의도 있습니다.
LLVM에서 Wasm을 생성할 수 있어 C, Swift, Rust 등과 같은 언어가 Wasm을 타겟으로 할 수 있지만, 이는 테이블에 가치를 남기는 것입니다. 오해하지 마세요, 이것이 Wasm의 킬러 앱입니다. 기존의 Rust 앱을 Wasm으로 컴파일하면 갑자기 웹에서 실행됩니다. LLVM에 의존하는 방대한 코드가 Wasm으로 번역될 수 있는 능력은 추가 투자의 길을 열어줍니다.
하지만 LLVM을 Wasm으로 번역하는 것은 다시 자바스크립트를 통해 대화하는 것처럼 느껴집니다. LLVM의 IR은 비구조적 제어 흐름(즉, 기본 블록)을 사용하는 반면, Wasm은 구조적 제어 흐름(즉, while, if, 함수 등)만을 사용합니다. 비구조적 제어 흐름을 구조적으로 번역하려면 Relooper와 같은 알고리즘이 필요합니다. C, Swift, Rust는 모두 구조적 제어 흐름으로 시작했으며, LLVM은 이를 기본 블록으로 변환했으며, 우리는 이를 다시 Wasm을 위한 구조적 제어 흐름으로 변환해야 합니다. 이는 기존 언어에 대한 필수적인 악입니다.
그러나 새로운 언어를 구상하는 컴파일러 작성자로서, 우회 작업 없이 직접 Wasm을 생성할 수 있습니다. 이는 특히 가비지 컬렉션 제안이 표준화됨에 따라 Wasm의 과소평가된 장점입니다. Wasm을 사용하면 백엔드를 건너뛰고 매우 고수준의 IR을 Wasm으로 변환하여 즉시 실행할 수 있습니다. 게임을 사랑해서 컴파일러의 백엔드를 작성하는 데 투자하고 있다면, 좋습니다, 마음을 따르세요. 더 이상 돈을 위해 컴파일러를 작성하는 사람은 없습니다.

Wasm의 맛보기

백엔드에 신경 쓰지 않고 빨리 끝내고 싶어서 LLVM IR을 타겟으로 하려는 경우, 타입체커에서 다시 놀 수 있도록 더 빠르게 돌아갈 수 있는 고수준의 타겟으로 Wasm을 고려해 보세요. 우리는 Wasm이 아닌 것에 대해 많이 이야기했습니다. 이제 Wasm이 무엇인지 간단히 살펴보겠습니다. 자세한 내용은 mdn에 Wasm에 대한 훌륭한 소개가 있습니다.
Wasm은 완전히 형식적으로 명시되어 있습니다. 사양은 코드가 오작동하는 이유를 알아내려고 할 때 수없이 귀중한 도움을 주었습니다. 모든 동작이 명시되어 있기 때문에 비결정론이 발생하는 정확한 지점을 나열할 수 있습니다. 목록에 있는 기능을 사용하지 않는다면, 축하합니다. 당신의 Wasm은 결정적입니다.
이는 미묘하게 느껴질 수 있지만, 컴파일러 작성자로서 큰 이점입니다. 표면 구문에서 기계 코드로 번역할 때 변동이 몰래 들어오는 것은 매우 쉽습니다. 해시 맵 대신 정렬된 맵을 사용하여 해시 알고리즘의 시드에 따라 컴파일러의 출력이 변경될 수 있습니다. 변수에 ID를 할당하는 것을 안정화하지 않아 두 변수가 ID 2와 3 사이를 계속 교환할 수 있습니다. 이러한 일은 발생합니다. Wasm의 결정성은 버그가 있는 곳이 아니라는 확신을 줄 수 있으며, 컴파일러가 고장난 이유를 찾으려고 할 때 확인해야 할 버그의 큰 부분을 제거합니다.
큰 부분의 버그를 제거하는 것에 대해 말하자면, Wasm의 형식적 사양의 또 다른 결과는 정적 타이핑입니다. Wasm 명령어, Wasm의 함수, 모듈, 내보내기 등은 모두 타입이 지정되어 있습니다. 이러한 타입은 실행 전에 검증되어, 백엔드에서 신중하게 생성한 Wasm이 의미가 있음을 보장합니다. 타입은 우리가 타입을 추론하거나 오버로드를 확인하는 데 많은 시간을 낭비하고 있다는 우려를 불러일으킬 수 있습니다. 걱정하지 마세요. 모든 Wasm 타입을 미리 정의해야 하며, Wasm은 단지 당신의 작업을 확인할 뿐입니다. 나쁜 날에도 빠른 작업입니다.
Wasm은 시작하기 위해 WebAssembly라는 이름이 필요했지만, 그 이름은 Wasm에 몇 가지 오해를 안겨주었습니다. 이러한 오해를 제거하여 Wasm의 더 명확한 그림을 드러내기를 바랍니다. Wasm에 대한 관심이 생겼다면 시작할 수 있는 방법이 많이 있습니다.
Rust는 주요 비브라우저 Wasm 런타임인 wasmtime의 호스트입니다. wasmtime의 개발자인 bytecode alliance는 Wasm 도구를 제공합니다. 이러한 도구 중 wasm-encoder 크레이트는 컴파일러 제작자에게 특히 유용합니다. 이는 바이너리 Wasm 모듈을 인코딩하기 위한 편리한 타입 안전 API를 제공합니다.
Rust의 영역 밖에서는 binaryen이 Wasm으로의 컴파일을 쉽고 빠르고 효과적으로 만드는 것을 목표로 하는 라이브러리입니다. 이는 C와 자바스크립트에서 API를 제공합니다. 당신의 선택 언어가 무엇이든, binaryen은 거의 확실히 FFI로부터 한 걸음 떨어져 있습니다. binaryen 위에 구축된 wasm-opt는 Wasm을 입력받아 최적화된 Wasm을 반환하는 CLI를 제공합니다.
The WebAssembly Binary Toolkit은 Wasm 작업을 더 쉽게 만드는 또 다른 CLI 도구 모음을 제공합니다. 이 도구 모음에서 주목할 만한 항목은 wasm2wat과 wat2wasm으로, 텍스트와 바이너리 형식 간의 변환을 제공하여 디버깅에 매우 유용합니다.
Wasm을 시작할 수 있는 방법은 많이 있습니다. 개척 정신이 있다면 Wasm의 텍스트 형식 모양으로 문자열을 조합하여 로컬 브라우저에서 자바스크립트 API를 사용하여 컴파일할 수도 있습니다.
0
52

댓글

?

아직 댓글이 없습니다.

첫 번째 댓글을 작성해보세요!