WebAssembly 써보니 속도보다 경계 관리가 핵심이었다
5 views
# WebAssembly 실전 활용, 속도가 아니라 경계의 기술
WebAssembly를 성능 기술로만 이해하면 절반을 놓친다. 핵심은 이식 가능한 실행 단위와 격리된 신뢰 경계의 결합이다. 빠르게 하려고 도입한 팀은 종종 실망하고, 격리하고 이식하려고 도입한 팀은 대체로 만족한다. 그 차이가 어디서 오는지 들여다보자.
## 브라우저가 무거운 소프트웨어를 감당하기 시작했다
수년 전까지 브라우저는 문서 뷰어의 연장선이었다. 복잡한 벡터 연산, 대용량 이미지 처리, CAD 수준의 기하 계산은 네이티브 애플리케이션의 몫이었다. 이 경계를 무너뜨린 것이 디자인 도구와 그래픽 소프트웨어의 웹 이주다. Figma는 C++로 작성한 렌더링 코어를 WebAssembly로 컴파일해 브라우저에서 돌리고, Adobe는 웹 버전 Photoshop을 만들면서 수십 년 된 C++ 코드베이스를 같은 방식으로 옮겼다. ffmpeg나 SQLite 같은 검증된 네이티브 라이브러리를 브라우저 안에서 그대로 돌리는 시도도 이제 흔하다.

'새로 작성했다'가 아니라 '옮겼다'는 데 방점이 있다. 수백만 줄의 검증된 C/C++ 자산이 재작성 없이 웹으로 이동했다. JavaScript로 포팅하기엔 사실상 불가능했던 규모의 코드가 컴파일 타깃 하나 바꾸는 것으로 새 플랫폼을 얻었다. 겉보기엔 브라우저의 확장이지만, 실제로 일어난 일은 기존 소프트웨어 자산의 플랫폼 해방이다.
## 스택 머신, 선형 메모리, 의도된 무능력
설계를 뜯어보면 이 기술이 무엇을 위해 만들어졌는지 드러난다.
WebAssembly 모듈은 특정 하드웨어에 종속되지 않는 스택 기반 가상 머신 명령어로 표현되고, 실행 직전에 각 플랫폼의 기계어로 변환된다. C, C++, Rust가 주요 컴파일 소스이고, 가비지 컬렉션 언어를 위한 확장도 표준화가 진행 중이다. 언어와 하드웨어 사이에 중립 지대를 만든 셈이다.
메모리는 선형 메모리 모델이다. 모듈은 자신에게 할당된 연속 메모리 블록 안에서만 읽고 쓴다. 호스트의 메모리도, 다른 모듈의 메모리도, 파일 시스템도 임의로 건드릴 수 없다. 포인터 연산이 아무리 난폭해도 피해는 자기 샌드박스 안에서 끝난다.
그리고 의도된 무능력이 있다. WebAssembly 모듈은 기본적으로 아무것도 못 한다. 파일을 열 수도, 네트워크에 붙을 수도, 화면에 그릴 수도 없다. 외부 능력은 전부 호스트가 명시적으로 주입한 함수를 통해서만 얻는다. 결함이 아니라 설계의 핵심이다. 전통적인 네이티브 코드가 '기본 허용, 예외적 차단'이었다면 WebAssembly는 '기본 차단, 명시적 허용'이다. 보안 모델의 방향이 반대로 서 있다.

세 결정을 합치면 이렇게 읽힌다. 신뢰할 수 없는 코드를 안전하게, 어디서든, 거의 네이티브 속도로 실행하기 위한 형식. 브라우저는 이 형식의 첫 숙주였을 뿐이다.
## 브라우저 밖에서 드러나는 진짜 용도
실전 활용의 무게중심은 이미 브라우저 밖으로 이동하고 있다. WASI(WebAssembly System Interface)는 파일, 시계, 난수 같은 시스템 자원 접근을 표준화해 브라우저 없이도 WebAssembly를 독립 실행 단위로 만들었다. Docker 공동 창업자 솔로몬 하익스가 "WASM과 WASI가 2008년에 있었다면 Docker를 만들 필요가 없었을 것"이라고 말한 것도 이 맥락이다.
### 엣지와 서버리스
컨테이너 기반 서버리스의 약점은 콜드 스타트다. 컨테이너는 격리 단위로 훌륭하지만 기동에 시간과 메모리를 꽤 요구한다. WebAssembly 모듈은 밀리초 이하로 뜬다. Cloudflare나 Fastly 같은 엣지 사업자가 요청 단위 격리 실행에 이를 쓰는 이유다. 전 세계 수백 개 지점에서 요청이 올 때마다 새 인스턴스를 띄워도 부담 없는 실행 단위, 컨테이너가 채우지 못한 자리다.

### 플러그인 시스템
호스트 애플리케이션에서 제3자 코드를 실행하는 문제는 오래된 난제다. 동적 라이브러리는 호스트와 같은 권한으로 돌아 위험하고, 별도 프로세스는 무겁고, 스크립트 언어 임베딩은 언어를 강제한다. WebAssembly는 이 셋을 동시에 푼다. 플러그인이 샌드박스 안에서 돌고, 가볍고, 어떤 언어로든 쓸 수 있다. 프록시 서버의 필터 확장이나 데이터 처리 도구의 사용자 정의 함수 실행에서 이미 쓰이는 패턴이다. 플러그인 생태계를 설계하는 입장이라면 지금 가장 진지하게 검토할 선택지다.
### 신뢰 경계가 필요한 모든 곳
멀티테넌트 SaaS에서 고객이 올린 코드를 실행해야 할 때, 스마트 컨트랙트 런타임을 설계할 때, 서로 다른 팀의 모듈을 한 프로세스에서 돌리되 장애는 격리하고 싶을 때. 공통점은 남의 코드를 내 시스템 안에서 돌린다는 상황이다. WebAssembly는 여기서 프로세스 분리보다 가볍고 언어 임베딩보다 안전한 중간 지대를 준다.
## 언제 쓰고 언제 쓰지 말 것인가
도입이 정당한 경우는 분명하다. 재사용할 네이티브 자산이 있을 때, 계산 집약적 코어를 여러 플랫폼에 배포해야 할 때, 신뢰할 수 없는 코드의 실행 경계가 필요할 때, 콜드 스타트가 비용 구조를 지배할 때. 이 중 하나라도 강하게 성립하면 유력한 답이다.
오해도 짚어야 한다. WebAssembly가 JavaScript보다 항상 빠르지는 않다. 현대 JavaScript 엔진의 최적화는 상당한 수준이고, 호스트와 모듈 사이 경계를 넘는 호출에는 비용이 붙는다. DOM 조작이 잦거나 짧은 함수를 빈번히 왕복하는 워크로드라면 이득이 희석된다. 브라우저에서는 DOM에 직접 접근하지 못해 JavaScript 글루 코드를 거쳐야 한다는 제약도 있다. 웹 애플리케이션 전체를 WebAssembly로 쓰는 것 역시 대부분 과잉이다. 계산 코어는 WebAssembly로, 인터페이스와 오케스트레이션은 JavaScript로 나누는 편이 낫다.
도입 질문은 '이 코드가 빨라지는가'가 아니라 '이 코드에 이식성이나 격리가 필요한가'다. 질문을 바꾸는 순간 판단이 단순해진다.
## 격리의 단위가 곧 배포의 단위가 된다
소프트웨어 인프라의 역사는 격리 단위의 역사다. 물리 서버에서 가상 머신으로, 가상 머신에서 컨테이너로, 격리 단위는 가벼워지고 기동은 빨라졌다. WebAssembly는 이 계보의 다음 항으로 자리를 잡아가고 있다. 프로세스보다 가볍고 함수 호출보다 안전한 실행 단위. 브라우저에서 시작한 형식이 서버와 엣지와 플러그인 시스템으로 번지는 것은 우연이 아니라, 격리 비용을 낮추려는 산업 전체의 오랜 압력이 만든 결과다.
과제는 남아 있다. 컴포넌트 간 조합의 표준, 가비지 컬렉션 언어의 완전한 지원, 디버깅과 관측 도구는 아직 성숙 중이다. 그래도 방향은 정해졌다. WebAssembly 앞에서 던질 질문은 하나다. 지금 내 시스템에서, 코드와 코드 사이에 어떤 경계가 필요한가.
이름에는 Web이 남아 있지만, 이 기술이 재편하고 있는 것은 웹이 아니라 실행이라는 개념 그 자체다.