본문 바로가기
IT & 비즈니스

WebAssembly(Wasm): 웹 브라우저를 넘어 차세대 범용 컴퓨팅 플랫폼으로의 진화

by notes9107 2026. 8. 24.

1. 브라우저에서 처음 만난 Wasm의 현실

저는 2024년 3월, 브라우저에서 이미지 배경 제거 기능을 빠르게 만들고 싶어 WebAssembly를 처음 붙였습니다. 당시에는 C++로 작성된 기존 이미지 처리 코드를 그대로 웹에 옮기면 자바스크립트보다 훨씬 빨라질 거라 기대했습니다. 하지만 첫 결과는 기대와 달랐습니다. 4MB짜리 Wasm 파일을 내려받고 초기화하는 데 제 노트북 크롬에서 약 1.8초가 걸렸고, 작은 이미지 한 장만 처리할 때는 오히려 순수 자바스크립트 코드가 더 빨랐습니다. 연산 시간만 보고 기술을 선택하면 안 된다는 사실을 그때 체감했습니다.
제가 처음 놓친 부분은 자바스크립트와 Wasm 사이의 데이터 복사였습니다. 이미지 픽셀 배열을 자바스크립트에서 Wasm 선형 메모리로 옮기고, 처리가 끝난 뒤 다시 꺼내는 과정이 매번 발생했습니다. 2,400×1,600 픽셀 이미지를 처리할 때는 픽셀 복사에만 평균 74ms가 추가됐습니다. 처리 함수 자체는 31ms였으니, 정작 연산보다 연결 코드가 더 오래 걸린 셈입니다. 이후에는 메모리를 매번 새로 할당하지 않고 버퍼를 재사용했으며, 여러 장을 한 번에 넘기는 방식으로 바꾼 뒤 전체 시간이 126ms에서 68ms로 줄었습니다.

2. 속도보다 먼저 부딪힌 메모리 문제

Wasm을 쓰면서 제가 가장 크게 당황한 부분은 선형 메모리였습니다. C++ 코드에서는 익숙하게 포인터를 다뤘지만, 브라우저에서는 그 포인터가 자바스크립트 객체를 가리키지 않았습니다. 2024년 5월 테스트 중에는 해제된 버퍼의 주소를 다시 사용해 특정 이미지에서만 결과가 깨지는 오류가 발생했습니다. 작은 샘플에서는 멀쩡했고 20MB가 넘는 사진에서만 재현돼 원인을 찾는 데 이틀이 걸렸습니다. 브라우저 개발자 도구에 일반적인 자바스크립트 스택 오류가 남지 않아 더 오래 헤맸습니다.
제가 택한 해결책은 Rust로 경계면을 다시 작성하는 일이었습니다. Rust의 소유권 규칙이 처음에는 번거롭게 느껴졌지만, 버퍼의 생명주기를 명시하면서 실수가 눈에 띄게 줄었습니다. 다만 Rust를 Wasm으로 컴파일한다고 모든 코드가 작아지는 것은 아니었습니다. 정규식과 파일 처리 라이브러리까지 한꺼번에 포함하자 빌드 결과가 11.6MB로 늘어났습니다. 기능에 쓰지 않는 의존성을 걷어내고 LTO와 압축을 적용하니 배포 파일은 2.9MB가 됐습니다. 저는 이 경험 뒤에 속도 측정표와 함께 초기 파일 크기, 메모리 사용량, 모바일 실행 시간도 기록하기 시작했습니다.

3. 브라우저 밖으로 나갈 때 달라진 규칙

2025년 1월에는 같은 이미지 처리 모듈을 서버에서 실행해 달라는 요청을 받았습니다. 처음에는 브라우저에서 돌아가던 Wasm 파일을 Node.js에서 그대로 불러오면 된다고 생각했지만, 파일을 읽고 임시 결과를 저장하는 부분에서 막혔습니다. 브라우저의 Wasm 모듈은 파일 시스템 권한을 자동으로 갖지 않기 때문입니다. Node.js 호스트 함수로 파일 접근을 연결하고, 입력 경로와 출력 경로를 명시적으로 넘긴 뒤에야 작업이 이어졌습니다. 실행 환경이 바뀌면 Wasm 코드보다 호스트와 연결되는 부분을 먼저 점검해야 한다는 교훈을 얻었습니다.
그 뒤에는 WASI를 이용해 서버 실행 구조를 바꿔 봤습니다. 제 테스트 환경은 Ubuntu 22.04, 4코어 가상 머신이었고, 10,000장의 1,920×1,080 이미지 변환 작업을 비교했습니다. 기존 Node.js 구현은 14분 12초, Wasm 모듈은 9분 47초가 걸렸지만, 첫 요청의 준비 시간은 Wasm 쪽이 420ms 더 길었습니다. 장시간 반복 작업에서는 이득이 있었고 짧은 요청만 처리하는 API에서는 차이가 거의 없었습니다. 그래서 모든 서버 코드를 Wasm으로 옮기지 않고, CPU 사용량이 높은 변환 함수만 분리했습니다. 범용 플랫폼이라는 말만 믿고 적용 범위를 넓히면 운영 복잡도만 커질 수 있다는 판단이었습니다.

4. 샌드박스가 주는 안도감과 착각

Wasm 모듈을 엣지 실행 환경에 올리면서 보안 구조도 직접 확인했습니다. 제가 작성한 모듈은 호스트가 넘긴 메모리와 함수만 볼 뿐, 서버의 파일 목록이나 환경 변수에 바로 접근하지 못했습니다. 실수로 디버그 코드에 파일 경로를 넣었을 때도 모듈 내부에서 해당 경로를 읽어 오지는 못했습니다. 이런 격리는 외부 코드를 받아 실행하는 서비스에서 마음을 조금 놓게 했습니다. 다만 샌드박스가 코드의 모든 위험을 없애지는 않았습니다.
실제로 2025년 4월, 이미지 변환 요청에 제한을 두지 않아 512MB 입력을 반복 처리하는 테스트가 들어왔고, 런타임 메모리가 빠르게 소진됐습니다. Wasm 메모리 접근은 격리돼 있었지만, 호스트가 허용한 실행 시간과 메모리 한도가 넉넉했던 탓에 서비스 전체가 영향을 받았습니다. 이후 요청 크기를 16MB로 제한하고, 호출당 300ms 타임아웃과 메모리 상한을 적용했습니다. 로그에는 모듈 이름, 입력 크기, 실행 시간, 트랩 발생 여부를 남겼습니다. 저는 Wasm의 샌드박스를 보안 장치 하나로 여기지 않고, 권한·자원 제한·입력 검증을 함께 묶어 운영하는 방식으로 바꿨습니다.

5. 제가 지금 Wasm을 선택하는 기준

제가 여러 프로젝트를 거치며 세운 기준은 단순합니다. 이미 C, C++, Rust로 검증된 계산 코드를 웹과 서버 양쪽에서 재사용해야 하거나, 실행 환경을 자주 바꾸면서도 같은 결과를 유지해야 할 때 Wasm을 먼저 검토합니다. 반대로 화면 상태 관리, 간단한 문자열 가공, 네트워크 요청처럼 호스트 API와 자주 대화하는 작업은 자바스크립트가 훨씬 편했습니다. 2026년 2월 진행한 내부 실험에서도 5ms 이하로 끝나는 함수는 Wasm으로 옮겨 얻은 이익이 거의 없었습니다. 호출 경계와 변환 비용이 계산 시간을 상쇄했기 때문입니다.
처음 시작하는 분이라면 저는 큰 서비스를 통째로 바꾸기보다 작은 함수 하나를 떼어 양쪽에서 측정해 보라고 권하고 싶습니다. 입력과 출력 크기, 초기화 시간, 반복 실행 시간, 메모리 증가량을 같은 조건에서 기록하면 홍보 문구보다 현실적인 판단이 나옵니다. 제 경우에는 이미지 처리 함수 하나를 분리하는 데 3일이 걸렸지만, 이후 브라우저와 서버 코드의 중복이 420줄 줄었고 장애 원인도 좁히기 쉬워졌습니다. Wasm은 모든 문제를 대신 해결하는 만능 도구가 아니라, 경계와 비용을 직접 관리할 때 가치가 드러나는 실행 방식입니다. 작은 실험과 실패 기록부터 남기는 접근이 독자 여러분의 시간과 운영 부담을 줄여 줄 겁니다.