Rust + WebAssembly로 보는 프론트엔드 기술의 미래.
Rust + WebAssembly로 보는 프론트엔드 기술의 미래.
흔히들 프론트엔드 기술은 쉽다고 합니다. 디자이너가 전달해 준 UI만 개발하면 끝이고 React나 HTML, CSS 태그 좀만 알면 시장에서 통용되는 기술은 마스터할 수 있으니까요. 그러나 프론트엔드 기술은 생각보다 다양하고 마스터하기 어려운 분야입니다.
특히 제한된 성능 내에서 최대한의 디바이스 호환성을 가지기 위해서는 성능 최적화가 필수입니다. 그래서 DOM을 자주 업데이트하지 않고 메모리를 최대한 가볍게 유지하는 기술이 필요하죠. 특히 프론트엔드에서 버벅거림은 UX를 해치는 최악의 경우 중 하나입니다. 차라리 에러 페이지로 넘어가는 방법이 좋은 설계 방식입니다.
그래서 복잡한 핸들링이나 계산은 WebAssembly로 개발하게 됩니다. 피그마에 웹 어셈블리 기술이 쓰였다는 사실은 이미 잘 알려져 있습니다. 픽셀 단위로 연산하거나 무거운 시뮬레이션을 웹에서 시각화하고자 할때 WebAssembly는 빛을 발합니다. 또한 웹 상에서 구현하기 까다로운 기능에 웹 어셈블리를 사용하면 성능상의 이점을 얻을 수 있습니다.
- 성능
최근에 FFT 구현체를 Rust + WebAssembly로 구현 해보았습니다.¹ 원래는 JavaScript 런타임에서 FFT를 처리하려고 했는데 심하게 버벅리는 문제가 있었습니다.
JavaScript 환경에서 FFT를 구현하면 메모리 이슈가 발생합니다. 데이터의 크기가 많아질 수록 UI는 버벅대고 컴퓨터는 힘들어합니다. 따라서 WebAssembly 환경으로 분리해주어야 합니다.
결과는 좋았습니다. 메모리 사용량 급증으로 인한 쓰로틀링에 걸리지 않았고 UX는 훨씬 부드러웠습니다.
사실 처리 시간 성능 면에서 큰 차이는 없었습니다. 2^14 data size에서도 모두 20ms 내에서 처리 가능 했습니다. 실시간 처리 성능도 우수했고요. 그럼에도 웹 어셈블리를 통해 실행 컨텍스트를 분리해 주니 JavaScript 연산보다 훨씬 효율적으로 자원 분배가 가능했습니다.
- Cargo와 wasm_bindgen & 빌드
Rust에는 Cargo라는 강력한 패키지 매니저가 있습니다. 새 프로젝트를 세팅해 주거나 한 번에 웹 어셈블리 개발 환경을 만들어줍니다. 제 개인적인 사용 경험으로는 NPM보다 월등한 개발자 경험을 제공해주는 듯 합니다.
더군다나 WebAssembly와 호환성이 좋습니다. 다른 언어와 다르게 함수 바인딩이나 데이터 처리등 사용하기 편한 도구들을 제공하고 컴파일의 경우에도 TypeScript 컴파일하는 것과 크게 다르지 않습니다. wasm-pack build 명령어 하나만으로 WebAssembly와 JS를 편하게 연결해 주는 파일이 생깁니다.
여기에 TypeScript를 적용하면 웹 생태계 전체를 강타입으로 개발 가능합니다. 애초에 Rust가 강타입을 지원하기에 Rust에 TypeScript 조합은 견고한 소프트웨어를 만들기에 최적화되어 있습니다.
- WebAssembly가 만능일까?
답부터 말씀드리자면, 만능은 아닙니다. 대부분의 연산에서 JS 환경과 큰 차이는 없습니다. 게다가 아직 생태계도 완전해지지 않았고 Rust와 같은 언어를 따로 컴파일해주어야 하기에 이식성이 좋지 않습니다.
그럼에도 성능 최적화에서 매력적인 선택지임은 분명합니다. 웹이라는 제한된 성능에서 최대한의 프레임을 끌어내야 할 경우, 블록체인 웹을 개발해야 할 경우, 웹에서 인증 혹은 보안과 관련된 기능이 필요한 경우 WebAssembly를 사용하시는 편이 좋습니다.
특히 보안의 경우 WebAssembly 런타임 내에서 실행되기 때문에 내부 로직을 블랙박스 형태로 유지할 수 있습니다. 이는 XSS와 같은 DOM 보안 취약점의 대안이 됩니다.