소스 코드를 기계어로 만드는 일


주말에 2021FE Conf를 보면서 swc 소개에 대한 발표를 인상깊게 들었다. 평소에 Babel을 사용해 변환된 코드나 빌드 시간을 대수롭지 않게 여겼는데, 이 빌드 시간이 느리다고 느껴져 컴파일 프로그램을 만들어버리다니! swc의 제작자가 대단하다고 느껴지는 반면 나는 이에 대해 궁금한적이 있었나? 라는 생각을 하게 되었다. 이 기회에 컴파일러나 babel, 그리고 swc에 대해 정리해보면 좋겠다는 마음에 이 글을 작성하게 되었다. 그러기 위해서는 먼저 고수준 언어와 저수준 언어에 대해 알아보아야 한다.

고수준 언어와 저수준 언어

고수준 언어 (high-level programming language)는 기계어보다 인간의 언어에 가깝다. 읽기 쉬우며 유지보수에 용이하기 때문에 현재 프로그래밍에 사용되는 대부분의 소스코드는 고수준 언어로 작성되고 있다. 하지만 이를 실행하기 위해서는 저수준 언어로의 변환이 필수적이다. 저수준 언어 (low-level language)인 기계어 또는 어셈블리어로 변환하기 위해서 컴파일러 또는 인터프리터가 사용된다.

컴파일러

컴파일러는 소스 코드 전체를 훑고 기계어로 변환하는 작업을 실행하는 프로그램이다. 컴파일러의 종류에는 원시 코드를 바로 기계어로 번환하는 정적 컴파일 (Static Compilation), 원시 코드를 바이트 코드로 변환하는 바이트코드 컴파일 (Bytecode Compilation), 바이트 코드 등의 중간 코드를 기계어로 변환하는 AOT 컴파일 (Ahead-Of-Time Compilation), 실시간으로 컴파일을 실행하는 JIT 컴파일 (Just-In-Time Compilation)이 있다.

대표적인 컴파일 언어: C, C++, Rust, Go

인터프리터

인터프리터는 소스 코드를 한 줄씩 읽어 내려가며 실행하는 프로그램이다. 인터프리터는 자체적으로 실행할 수 없기 때문에 실행 파일(.exe 파일)은 컴파일러 언어로 실행한다. 한줄씩 실행하며 기계어로 변환하는 방식이기 때문에 속도는 컴파일 언어보다 느리다. 또한 컴파일러는 전체 소스 코드를 변환 후에 에러를 보고하지만 인터프리터는 각 행마다 실행시 에러가 발생하면 이후 소스 코드는 실행하지 않기 때문에 보안적 측면에 용이하다.

대표적인 인터프리트 언어: Javascript, SQL, Python, Ruby

V8 엔진: 자바스크립트 엔진

V8 엔진은 자바스크립트 코드를 실행하기 위한 오픈소스 자바스크립트 엔진이다. C++로 작성되었으며 구글 크롬 브라우저와 Deno와 NodeJS 런타임 환경 등에 사용된다. V8 엔진은 방식인 JIT(Just-In-Time) 컴파일 방식을 사용하고 있다. 이를 사용해 자바스크립트 코드가 기계어로 변환되어 웹에서 실행되는 것이다.

image

babel

바벨은 자바스크립 컴파일러다. ECMAScript 2015 이상 버전의 코드를 오래된 브라우저 환경에서도 실행할 수 있도록 도와준다. React preset을 사용해 JSX 문법을 변환할 수도 있다. 모든 브라우저 환경에서 안전하게 Javascript를 사용하기 위해서는 Babel과 같은 컴파일러를 사용해서 ES5의 문법으로 변환 후 배포해야 하는것이다. CRA를 사용해 리액트 프로젝트를 생성하면 빌드 툴로 babel을 사용하는 것을 확인할 수 있다. 빌드를 돌리면 .dist 폴더가 생성되는데 이가 바로 babel의 실행 결과이다. 또한 .babelrc 파일에 minify 패키지를 프리셋으로 추가하거나 원하는 JS 파일의 버전을 명시하는 등 원하는 설정을 할 수 있다.

문법이 아닌 함수의 경우에는 바벨 폴리필을 사용해서 브러우저 엔진이 구현하지 않더라도 실행할 수 있다. 폴리필은 소스 코드에 작성된 최신 함수를 스크립트에 추가하고 런타임에 이를 실행한다. 컴파일시 실행되는 바벨과 다른 점이라고 할 수 있다.

swc

자 그럼 swc는 뭘까? NextJS와 DENO에서 사용하고 있는 이 빌드 툴은 babel과 비교하여 훨씬 빠른 빌드 속도를 자랑한다. 현재 작업중인 Next 프로젝트의 버전을 12로 올렸더니 babelrc 파일을 제거하고 swcrc 파일을 설정하라는 문구를 보고 이를 실감했다. 아래에 소개된 swc와 관련된 내용은 2021 FE Conf swc와 웹 개발의 미래 발표를 참고했다.

자바스크립트 기반의 바벨은 하나의 CPU 코어만 사용하고, CPU 위주의 작업을 하기 때문에 이벤트 루프는 CPU 위주의 작업에 맞는 모델이 아니라고 한다. 느려진 빌드의 해결책으로 언어를 Rust로 바꾸면서 swc는 시작되었다. 이름도 Speedy Web Compiler 의 약자이니 swc를 제작한 이유를 예상할 수 있을것이다. Rust를 선택한 이유는 병렬 처리 기능으로 성능을 높이고, FFI (Foreign Function Interface) 및 제너릭을 사용해 편리하게 개발할 수 있었기 때문이라고 한다.

기존 웹 빌드 툴들은 실행이 끝나면 종료되기 때문에 컴파일러의 도움을 받지 못하고 JS를 최적화 해야 한다. SWC의 제작자는 JIT 컴파일러가 도움이 되지 않는 구조인데 Rust를 사용하면 이를 해결할 수 있고, 이 때문에 많은 빌드 툴들이 Rust로 옮겨갈것이라고 생각한다고 한다.