원문 : https://yoric.github.io/post/binary-ast-newsletter-1/

CC BY-NC 4.0 https://creativecommons.org/licenses/by-nc/4.0/deed.ko

역자 서문

Binary AST는 자바스크립트를 더욱 빠르게 파싱하기 위한 표준이며 최근에 ECMA TC-39에 채택되었다. 캐시와 같은 기법은 아니고, 텍스트 형태의 자바스크립트를 바이너리 형태로 변환하여 전송 속도를 높이고 자바스크립트 파싱 자체를 빠르게 하는 것이 목표이다. 소개 글 외에도 추가적인 참고 자료를 덧붙인다.

자바스크립트 Binary AST를 향하여


이 글에서 나는 웹 페이지를 더 빠르게 로딩할 수 있게 하는 JavaScript Binary AST를 소개할 것이다. 이 프로젝트는 현재 진행 중인 상태이며 그 외에도 많은 장점이 있다.

배경 지식

자바스크립트는 느린 스크립팅 언어였지만, 지난 몇 년 동안 웹 브라우저 외에도 데스크탑, 서버, 모바일 및 임베디드 어플리케이션 등 다른 환경들에서도 충분히 빠른 속도로 구동될 수 있도록 발전해 왔다.

자바스크립트가 성장함에 따라 어플리케이션의 복잡도와 크기도 증가했다. 20년 전에 아주 소수의 웹 사이트만이 Kb 가 넘는 자바스크립트를 사용했던 반면에, 지금은 많은 웹 사이트와 비 웹 어플리케이션이 사용자가 웹 사이트와 앱을 사용하기 전에 몇 Mb 이상의 자바스크립트를 로딩해야 한다.

"몇 Mb 이상의 자바스크립트"라고 하는 말이 약간 이상하게 들릴지도 모르지만, 네이티브 어플리케이션을 한 번 생각해 보자. Stream은 3.1Mb(맥에서 측정했을 때 다른 리소스나 디버깅 심볼, 동적 라이브러리를 제외하고 순수하게 바이너리만 크기만), Telegram은 11Mb, Opera Updater는 5.8Mb이다. 웹 브라우저는 동적으로 라이브러리를 로딩하기 위해 필수적이기 때문에 웹 브라우저의 크기를 고려하지는 않겠지만, Firefox와 Chromium은 100Mb가 넘을 것이다.

물론 큰 자바스크립트 소스 코드를 처리할 때 다음과 같은 몇 가지 비용이 발생한다.

  • 무거운 네트워크 트래픽
  • 느린 구동

우리는 대규모 웹 어플리케이션의 자바스크립트 소스 코드가 바이트코드로 컴파일 되거나 인터프리트 되기 전에, 파싱하는데 걸리는 시간이 문제라는 결론에 도달했다. Facebook의 경우 빠른 컴퓨터에서도 파싱 시간이 500ms~800ms는 쉽게 넘어간다. 자바스크립트 어플리케이션이 앞으로 더 작아질 가능성은 거의 없다.

그래서 Mozillar와 Facebook의 합동팀은 자바스크립트의 실행 시작 속도를 매우 향상 시킬 수 있는 새로운 방법을 만들기로 결정했는데, 바로 Binary AST이다.

Binary AST 소개

자바스크립트 Binary AST의 개념은 간단하다. 텍스트 형태의 소스 코드를 보내는 대신 바이너리 형태의 소스 코드를 보내면 속도가 향상되지 않을까 하는 것이다.

더 구체적으로 살펴 보자. Binary AST의 소스 코드는 텍스트 형태의 자바스크립트 소스 코드와 동일하다. 새로운 프로그래밍 언어라거나 자바스크립트의 하위집합, 상위집합 혹은 바이트코드도 아니며 그냥 자바스크립트이다. 단지 소스 코드를 바이너리 형태로 표현한 것이다. 이 Binary AST 표현은 특별히 자바스크립트를 위해 고안되었고 파싱 속도를 향상시키기 위해 최적화된 소스 압축의 한 형태라고 보면 된다. 그리고 읽기 쉽고 잘 구조화된 소스코드로 디코딩될 수 있도록 만들고 있다. 이 때 주석은 유지되지 않고 있지만, 유지될 수 있도록 하는 스펙이 제안되고 있다.

Binary AST 파일을 만들어 내는 것은 일련의 빌드 과정을 필요로 하고, WebPack이나 Babel과 같은 빌드 도구들이 제 때에 Binary AST 파일을 만들어 낼 수 있을 것이다. 그래서 많은 자바스크립트 개발자들이 이미 사용하고 있는 것처럼 빌드할 때 플래그 하나 설정하는 정도로 간단하게 Binary AST 파일로 변환할 수 있게 되기를 바란다.

앞으로 Binary AST와 벤치마크 결과, 현재 상태에 대해서 자세하게 소개하게 소개할 예정이다. 지금은 초기 실험에서 아주 괜찮은 소스 압축률과 의미 있는 파싱 속도를 얻었다는 정도로만 얘기해 두자.

우리는 지난 몇 달 전부터 지금까지 Binary AST에 매진하고 있고, 이 프로젝트는 막 ECMA TC-39의 Stage 1 스펙으로 채택되었다. 이것은 굉장히 고무적인 일이지만 모든 자바스크립트 VM이나 툴체인에 구현될 때까지는 시간이 걸릴 것이다.

다음에 소개하는 기술들과 비교해 보자

압축 포맷

대부분의 웹서버는 이미 자바스크립트 데이터를 gzip이나 brotli와 같은 압축 포맷으로 보내고 있다. 이것은 데이터를 받기 위한 시간을 많이 줄여준다.

우리가 지금 하는 것은 자바스크립트를 위해 특별히 설계된 포맷이다. 여러 방법들 중에서 gzip을 내부적으로 채택하고 있는 초기의 프로토타입은 두 가지 주요 장점이 있다.

  • 더욱 빠른 파싱을 할 수 있도록 설계가 되었다.
  • 초기 실험에 따르면, gzip이나 brotli를 큰 폭으로 이겼다.

우리의 주된 목표는 파싱 속도를 더 빠르게 하는 것에 있음을 기억하라. 그러므로 미래에는 파일 크기와 파싱 속도 중에 선택을 해야 할 경우, 우리는 대부분 더 빠르게 파싱하는 것을 선택할 것이다. 또한 내부적으로 사용하는 압축 포맷도 변화가 있을 것이다.

Minifier(용량 줄이기 기법)

웹 개발자들이 자바스크립트 파일의 크기를 줄이기 위해 보통 사용하는 방법이 바로 UglifyJS나 Google Closure 컴파일러와 같은 Minifier이다.

Minifier는 일반적으로 불필요한 공백과 주석을 제거하고, 변수 이름을 더 짧게 만드는 등 많은 변환 방법을 통해 프로그램을 더 짧게 만든다.

이러한 툴들이 분명 유용하겠지만, 두 가지 면에서 충분하지는 않다.

  • 파싱 속도를 더 빠르게 설계되지는 않았다. 우리는 용량 줄이기를 했던 많은 경우에서 파싱이 더 느려지는 것을 보았다.
  • 또한 읽을 수 없게 변수와 함수 이름을 바꾼다든지, 변수 선언을 특이하게 바꾸는 등 자바스크립트 코드를 더욱 읽기 어렵게 만드는 부작용도 있다.

반대로 Binary AST 변환은

  • 파싱을 더욱 빠르게 하도록 설계되었고
  • 모든 변수 이름과 소스 코드가 쉽게 디코딩되고 읽을 수 있는 상태로 유지한다.

물론 난독화와 Binary AST 변환은 소스코드의 가독성을 유지하고 싶지 않은 어플리케이션에서도 잘 어울린다.

WebAssembly

WebAssembly(혹은 wasm)는 특정 상황에서 성능을 향상시킬 수 있는 흥미로운 웹 기술이다. wasm은 네이티브 어플리케이션이 효율적으로 전송되고 자바스크립트 VM 상에서 네이티브 속도만큼 빠르게 파싱 및 실행될 수 있는 컴파일된 포맷으로 만들어졌다.

그러나 wasm은 설계측면에서 네이티브 코드이므로 VM을 벗어나서 자바스크립트와 동작할 수는 없다.

나는 자바스크립트가 wasm으로 컴파일되는 프로젝트들을 잘 알지는 못한다. wasm이 분명히 실현 가능하겠지만 다소 어려운 일이 될 수도 있다. 왜냐하면 이것은 컴파일러 개발과 연관되어 있고 그것은 최소한 (특이하면서도 매년 스펙이 정의되는 언어인)자바스크립트의 호환성을 유지하면서 새로운 자바스크립트 VM을 만드는 것 만큼 복잡하기 때문이다. 만약 그 코드가 오늘 날의 (진짜 빠르게 만들어진)자바스크립트 VM들보다 느리게 동작한다면, 당연히 필요성은 없어질 것이다. 또한 초기 구동이 너무 느려서 쓰지 못할 정도로 크거나(우리가 여기서 해결하고자 하는 문제이기도 하다) 자바스크립트 라이브러리, (브라우저 어플리케이션의) DOM 등과 같이 쓰지 못할 경우도 마찬가지이다.

자, 이제 이 문제를 연구하는 것은 아주 흥미로운 작업이 될 것이다. 만일 우리가 틀렸다면 어떤 방법을 쓰더라도 한 번 증명해보자.

캐시 향상시키기

브라우저는 자바스크립트 코드를 다운로드하고 브라우저 캐시에 저장한다. 그래서 나중에 다시 다운로드 되지 않도록 한다. Chromium와 Firefox는 최근 자바스크립트 소스코드 뿐만 아니라 바이트코드까지 캐시할 수 있도록 향상되었다. 페이지를 두 번째 로드할 때 파싱시간 문제를 회피할 수 있다. 나는 Safari나 Edge가 어떻게 하는지에 대해서는 모르지만 그에 상응하는 수준의 기술이 적용되어 있을 것이다.

이런 멋진 기술을 만들어낸 두 팀에 모두 축하를 전한다. 이 기술로 인하여 페이지 리로딩 속도는 매우 개선되었다. 자바스크립트 코드가 업데이트 되지 않은 페이지에서는 아주 잘 동작한다.

우리가 Binary AST로 풀고자 하는 문제는 다른 것이다. 우리가 방문하고 또 다시 방문하는 페이지들도 있지만, 그보다 처음으로 방문하는 페이지들이 더 많이 있다. 게다가 재방문일지라도 마지막 방문 이후에 업데이트된 경우도 많다. 특히 자주 자주 업데이트 되는 어플리케이션 숫자는 계속 늘고 있다. 예를 들어, Facebook은 하루에도 몇 번씩 새로운 자바스크립트 코드를 배포한다. Twitter, LinkedIn, Google Docs 등도 이와 비슷한 상황이다. 당신이 자바스크립트 어플리케이션(웹 혹은 다른 것이라도)을 만드는 자바스크립트 개발자라면 당신과 사용자들이 가능한한 부드럽게 첫 화면을 볼 수 있도록 하고 싶을 것이다. 이 말은 또한 첫 로딩(혹은 업데이트된 이후 첫 로딩)도 매우 빠르게 동작할 수 있게 만들고 싶다는 것이다.

이러한 것들이 Binary AST가 해결하고자 하는 문제들이다.

만약에

만약 캐시를 향상 시킨다면?

브라우저가 자바스크립트 코드를 바이트코드로 미리 컴파일하고 실행을 위한 사전 준비를 가능하게 하는 부가적인 기술들에 대해서 논의하였다.

이런 기술에 대한 연구는 충분히 가치롭고 Binary AST를 개발하기 위한 몇몇 시나리오에도 분명히 도움 된다. 각 기술들은 다른 기술들을 또 향상 시키기 때문이다. 특별히 Binary AST의 보다 개선된 자원효율성은 이러한 기술들이 잘못 사용될 때(역자: 캐시가 안 되게 했다거나) 자원 낭비를 줄이는 동시에 캐시 기법을 전혀 사용할 수 없는 경우에도 성능을 개선할 수 있다.

만약 자바스크립트 바이트코드를 쓴다면?

전부는 아니지만 대부분의 자바스크립트 가상머신은 이미 코드의 내부 표현을 자바스크립트 바이트코드로 사용한다. 적어도 마이크로소프트의 가상 머신은 권한이 부여된 어플리케이션에서 자바스크립트 바이트코드를 적용하는 것으로 알고 있다.

자, 브라우저 벤더들이 그들의 바이트코드를 오픈하고 모든 자바스크립트 어플리케이션이 바이트코드를 적용하는 것을 상상해 보자. 하지만 이것은 몇 가지 이유에서 좋지 못한 아이디어이다.

첫 번째, VM 개발자들에게 영향을 끼친다. 자바스크립트의 내부적인 표현을 노출한 이후부터는 그것을 지속적으로 유지해야 할 필요가 있다. 자바스크립트처럼 자바스크립트 바이트코드도 주기적으로 변경될 것이고 언어의 새로운 버전과 최적화를 반영해야 할 것이다. VM이 바이트코드의 이전 버전과 호환성을 영원히 유지하도록 강제하는 것은 유지보수나 성능적인 측면에서 매우 큰 문제가 될 수 있다. 아주 제한된 환경에서는 제외하더라도, 어떤 브라우저나 VM 벤더들이 이런 것들을 만약 하기로 한다면 걱정부터 들 것이다.

두 번째, 자바스크립트 개발자에게 영향을 끼친다. 여러 개의 바이트코드를 쓴다는 것은 여러 개의 바이너리를 유지관리하고 반영해야 한다는 의미가 될 수 있다. 각 브라우저별로 나오는 버전들에서 바이트코드의 최적화를 적용하려는 경우, 관리해야 할 바이너리의 수는 수십 개도 될 수도 있다. 더 안 좋은 것은 다른 의미로 컴파일된 자바스크립트 코드가 앞에 나타날 경우 그 바이트코드의 의미는 달라질 수가 있다는 것이다. 이런 상황 속에서는-모바일 및 네이티브 개발자들이 항상 하는 것처럼- 자바스크립트 생태계는 분명히 퇴보할 것이다.

만약 표준 자바스크립트 바이트코드가 있다면?

만일 자바스크립트 VM 벤더들이 WebAssembly의 확장인 것처럼 바이트코드 포맷을 문서화하고자 한다면? 그리고 자바스크립트만을 위해서 특별히 설계가 된다면?

사람들은 그러한 포맷이 존재하지 않는 것을 아쉬워하고 있는 것은 분명하지만 누구도 이에 대해 활발한 진행을 하고 있는지는 알 수 없다.

사람들이 아직 이것을 해보지 않은 이유 중 하나는 항상 변화하고 있는 언어를 위해 바이트코드를 설계하고 유지관리하는 것이 매우 복잡한 일이기 때문이다. 벌써 자바스크립트의 복잡함보다 두 배는 될 것이다. 더 중요한 것은 자바스크립트를 인터프리팅하고 바이트코드를 만드는 두 가지를 서로 유지관리하는 것 자체가 전쟁에서 지는 것과 같을지도 모른다는 것이다. 결국에는 호환되지 않는 두 가지 자바스크립트 언어가 나타날 것이고 웹 환경에 해를 끼치게 될 것이다.

더불어 이런 바이트코드가 코드 크기나 성능에 도움이 되는지 입증되어야 한다.

그냥 파서를 더 빠르게 만든다면?

우리가 파서를 더 빠르게 만들 수 있다면 좋지 않을까? 아쉽게도 자바스크립트 파서가 상당히 개선되고 있지만 체감하기는 아직 멀었다.

생략하거나 끝까지 개선할 수 없게 되는 몇몇 과정들에 대해서 말해 보겠다.

  • 많은 인코딩 방법들, 유니코드 바이트 순서표시나 기타 사항 다루기
  • / 문자가 나눗셈 연산자인지, 주석의 시작인지, 정규식 표현인지를 알아내기
  • ( 문자가 표현식의 시작인지, 함수 호출의 인자 리스트인지, 화살표 함수의 인자인지 알아내기
  • 문자열(각각 문자열 템플릿, 배열, 함수)이 어디서 멈추는지 알아내야 하는 모호성 문제
  • let a 선언이 유효한지, 소스 코드 뒷부분에 나올 수 있는 다른 let a, var a 혹은 const a 선언과 충돌나지 않는지 알아내기
  • eval을 만났을 때 eval의 4가지 의미에 대해서 결정하기
  • 정말 지역 변수인지 결정하기
  • 등등

이상적으로는, VM 개발자들은 이런 것들을 병렬적으로 파싱하고 파싱된 코드가 실제로 사용되는지 정확히 결정될 때까지 실행을 지연시키기를 원한다. 실제로 최근 대부분의 VM들은 이러한 전략들을 구현하고 있지만 안타깝게도 자바스크립트 문법에 있는 엄청나게 많은 토큰의 모호함은 병렬처리에 뛰어난 반면, 문법적인 에러가 발생했을 때는 지연 파싱 기법을 사용할 기회가 적다는 제약이 있다.

일반적으로 용량 줄이기 기법(Minified)이 사용된 코드를 적용하는 했을 때, VM에게는 수행시간이 많이 걸릴 수 있지만 일반 파싱보다 느리게 처리될 수 있도록 하는 선-파싱 단계가 필요하다.

Binary AST 제안 스펙은 이러한 텍스트 기반 자바스크립트 소스의 문법과 의미해석 때문에 발생하는 성능 제약 사항을 극복할 수 있도록 설계되었다.

지금은 어떤 단계인가?

우리는 여러분, 웹 개발자, 도구 개발자들이 가능한 빨리 개발 단계에 진입했으면 하는 바램으로 이 글을 조금 일찍 올리게 되었다. 지금까지 두 그룹에서 수집한 피드백은 꽤 괜찮았으며 우리는 두 커뮤니티와 긴밀히 협력할 것을 기대하고 있다.

우리는 벤치 마킹을 하기 위해서 초기 프로토타입 개발(아직 쓸만한 수준은 아니다)을 완료했다. 그리고 빌드 도구들과 Firefox에 사용될 더 진화된 프로토타입을 만들고 있다. 그러나 아직 쓸만한 것이 나오기에는 시작한지 얼마 되지 않은 상태이다.

몇 주 뒤에 더 자세한 내용을 소개하는 글을 쓰도록 하겠다.

더 읽을 거리

유동식2017.09.11
Back to list