개발새발 로그

Babel, Terser 그리고 SWC 본문

React

Babel, Terser 그리고 SWC

이즈흐 2024. 4. 13. 01:04

우리가 프로젝트를 빌드하기 위해서 많은 빌드 툴을 사용하고 있다.

그 중 대표적인 빌드 툴은 BabelTerser일 것이다.(Webpack도 빌드 툴이다.)

 

프로젝트의 규모가 커질 수록 전체 코드들이 Babel과 Terser과 같은 빌드 툴을 거쳐서 

실제 프로덕션 코드로 변환되는 시간이 코드양에 비례해 늘어나게 된다.

빌드 시간이 오래 걸린다면 빌드를 할 때마다 그 시간을 기다려야 한다.

 

어떻게 빌드를 빠르게 할 수 있을까?

NextJS에서 Next 12버전 부터 정식으로 도입된 SWC라는 툴이 있다.

Next는 SWC로 구축된 새로운 컴파일러로 빌드하면 기존과 비교했을 때 빌드 타입이 5배까지 빨라진다고 소개했다.

 

이번 포스팅에서는 BabelTerser, SWC에 대해 알아보고, SWC가 왜 빠른지에 대해 알아보려고 합니다.

 

 

 

📖Babel

Babel은 ES6와 같은 자바스크립트 최신 버전의 코드를 이 전 버전의 코드로 변환시켜주는 도구이며, 심지어 TypeScript를 변환시켜 주기도 한다.

Babel은 자바스크립트 빌드 툴 중 트랜스파일러 라는 영역에 속한다고 한다.

트랜스파일러를 알기 위해서는 컴파일러도 알아야한다.

 

컴파일러(Compiler)

컴파일러는 고수준 언어로 작성된 소스 코드를 기계어나 바이트코드와 같은 저수준 언어로 변환하는 프로그램이다.

이 과정을 컴파일링(Compiling)이라고 하며, 변환된 코드는 컴퓨터 하드웨어나 가상 머신에서 직접 실행될 수 있다.

 

트랜스파일러란?

트랜스 파일러는 컴파일러의 하위 분류이다.

그래서 Babel의 공식문서에서 "Babel is a JavaScript compiler." 라고 표현하고 있다.

트랜스파일러는 한 고수준 언어로 작성된 소스 코드를 다른 고수준 언어로 변환하는 프로그램이다.

이 과정을 트랜스파일링(Transpiling)이라고 하며, 종종 "소스-투-소스 컴파일(source-to-source compile)"로도 불린다.

트랜스파일러는 특정 언어의 기능을 지원하지 않는 환경에서 해당 언어의 코드를 실행할 수 있게 해주거나, 새로운 언어 기능을 구형 환경에서 사용할 수 있게 해주는 역할을 한다.

 

다음은 Babel의 주요기능과 작동방식에 대해서 간단하게 찾아봤다.

 

Bable의 주요 기능

  1. 코드 변환: 최신 JavaScript 문법을 지원하지 않는 브라우저에서도 실행될 수 있도록 ES6 이상의 코드를 ES5로 변환합니다.
  2. JSX 처리: React와 같은 라이브러리에서 사용되는 JSX 문법을 JavaScript 코드로 변환합니다.
  3. 타입스크립트 지원: 타입스크립트 코드를 JavaScript 코드로 변환하는 기능을 제공합니다.
  4. 폴리필: Promise, Map, Set과 같은 최신 JavaScript API를 구형 브라우저에서도 사용할 수 있도록 하는 폴리필을 제공합니다. Babel 자체는 코드 문법을 변환하지만, 폴리필을 통해 새로운 JavaScript 런타임 기능을 구형 환경에서도 사용할 수 있게 합니다.

 

Babel의 작동 방식

  1. 파싱(Parsing): 입력된 원본 코드를 읽고, 이를 추상 구문 트리(AST, Abstract Syntax Tree)로 변환합니다. AST는 코드의 구조를 트리 형태로 표현한 것입니다.
  2. 변환(Transformation): 생성된 AST를 변경하여 새로운 AST를 만듭니다. 이 단계에서는 ES6 이상의 문법을 ES5 문법으로 변환하거나, JSX를 JavaScript로 변환하는 등의 작업이 이루어집니다.
  3. 생성(Code Generation): 변환된 AST를 다시 코드로 변환합니다. 이 코드는 최종적으로 구형 브라우저에서도 실행할 수 있는 JavaScript 코드입니다.

 

 

📖Terser

Terser라는 빌드 툴에 대해서는 처음 들어본 사람들도 많을 것이다.

Terser는 우리가 직접 다룰 일이 많지 않은 툴이다.

대부분의 프레임워크에서 이미 Terser에 대한 세팅이 기본적으로 되어있고, 

Webpack의 경우 v4 이후 버전 부터는 별도의 설정없이도 프로덕션 모드에서 자동으로 Terser 툴을 사용하도록 세팅되어있기 때문이다.

 

Terser 공식 사이트에서는 아래와 같이 소개하고 있다.

JavaScript mangler and compressor toolkit

 

여기서 mangler와 compressor가 무엇인지 간단하게 알아봤다.

 

Mangler

Magler의 뜻은 아래와 같다.

  • 1. 난도질하다, 토막토막 내다
  • 2. 엉망으로 만들다, 망가뜨리다(spoil)

훼손시킨다는 단어인데 이게 왜 빌드 툴에 필요한 것일까?

 

Terser가 magler로써 하는 역할은 변수, 함수, 속성들의 이름을 매우 단순화된 이름으로 변경해주는 것이다.우리가 프로그래밍을 할 때 "변수나 함수의 이름은 의미있는 네이밍을 사용해야 된다" 로 알고있다.

이는 코드의 가독성을 매우 향상시킨다.

 

하지만 컴퓨터입장에서는 이 네이밍이 전혀 중요하지 않다.

컴퓨터 입장에서는 어떤 변수의 이름이든 상관이 없는 것이다.

 

그러면 우리가 가독성을 위해 붙여준 이 네이밍들은 결과적으로 코드를 길게 만들고, 이로인해 소스 파일의 크기가 커진다.

브라우저는 웹페이지의 다양한 기능들을 제공하는데 필요한 소스 파일들을 서버로부터 다운로드한다.

이때 소스파일의 크기가 크다면 다운로드 시간이 오래 걸리기 때문에 이는 결국 좋지 않은 사용자 경험을 주게 된다.

 

magler소스코드에 존재하는 네이밍들을 의미없는 문자로 바꿔버린다.

이 최적화 과정을 통해서 실제 프로덕션 환경에서의 코드 사이즈를 줄일 수 있다.

 

예시를 보자

아래와 같은 파일이 있다.

const HelloWorld = ({ name, content }) => {
  let fullText = "";

  if (name) {
    fullText += `name: ${name}`;
  }
  if (content) {
    fullText += `content: ${content}`;
  }

  return fullText;
};

HelloWorld({ name: "Bill", content: "Hello World!" });

이 코드를 mangle해보도록 하자

npm install terser -g

terser --mangle --toplevel hello.js -o hello-mangled.js

먼저 terser을 설치하고, terser의 magler로 최적화를 시키면 아래와 같은 코드가 나타나게 된다.

const n = ({ name: n, content: e }) => {
  let t = "";
  if (n) {
    t += `name: ${n}`;
  }
  if (e) {
    t += `content: ${e}`;
  }
  return t;
};
n({
  name: "Bill",
  content: "Hello World!",
});

출처 https://fe-developers.kakaoent.com/2022/220217-learn-babel-terser-swc/

원본 코드와 변환된 코드가 담긴 파일 크기를 비교해 보면, 변환된 코드의 크기가 대략 30~40% 정도 줄어든 것을 확인할 수 있다.

 

Compressor

compress는 아래와 같은 뜻을 가지고 있다.

  • 1.압축[압착]하다, [컴퓨터] <파일을> 압축하다
  • 2.<사상·언어 등을> 요약하다, 집약하다 ((into))

말 그대로 압축해주는 툴로 추측할 수 있다.

 

Terser가 compressor로써 하는 역할은 우리의 자바스크립트 코드를 분석한 후

더 짧은 코드를 통해 동일한 기능을 구현할 방법이 있는지 확인하고 그 방향으로 코드를 변환해 주는 것입니다.

 

예시를 보자

아까와 같은 코드를 가져왔다.

const HelloWorld = ({ name, content }) => {
  let fullText = "";

  if (name) {
    fullText += `name: ${name}`;
  }
  if (content) {
    fullText += `content: ${content}`;
  }

  return fullText;
};

HelloWorld({ name: "Bill", content: "Hello World!" });
terser --compress --toplevel hello.js -o hello-compressed.js

위처럼 compress로 파일을 변환하면 아래와 같은 파일로 변환된다.

(({ name: name, content: content }) => {
  let fullText = "";
  name && (fullText += `name: ${name}`), content && (fullText += `content: ${content}`);
})({
  name: "Bill",
  content: "Hello World!",
});

문법 구조가 많이 달라졌다.

  • 먼저 HelloWorld라는 함수명이 사라지고, 즉시실행 함수로 변경됐다.
  • if 조건문이 && 연산자로 변경되었다.
  • fullText에 내용을 채워 넣는 코드들이 쉼표(,)를 통해 한 줄의 코드로 변경되었다.
  • 실제로 HelloWorld 함수의 리턴 값을 사용하는 곳이 없으므로 (리턴 값을 콘솔로 찍는 등으로 활용하고 있지 않음), return 문이 제거되었다.

처음 작성했던 hello.js와 문법 구조가 달라졌으나 결과적으로 같은 기능을 제공하기 때문에 코드가 압축되고, 최적화되었다. 

원본 코드와 변환된 코드의 파일 크기를 비교해보면, 이번에도 대략 30~40% 정도 줄어든 것을 확인할 수 있다.

출처&nbsp;https://fe-developers.kakaoent.com/2022/220217-learn-babel-terser-swc/

 

Terser as Minifier

위에서 설명한 코드를 mangle 하는 과정이나 compress 하는 과정과 같이, 우리가 작성한 코드를 동일한 기능을 제공하는 경량화된 코드로 변환해 주는 일련의 작업을 minify 혹은 minification(코드 경량화) 이라고 부른다.

 

그리고 코드 경량화 작업을 해주는 툴을 우리는 minifier라고 부른다.

따라서, Terser는 우리가 작성한 자바스크립트 코드를 프로덕션에서 더욱 경량화된 상태로 제공될 수 있도록 도와주는 minifier 빌드 툴이라고 할 수 있다.

 

참고로, hello.js가 mangle 과정과 compress 과정을 모두 거치고 공백까지 모두 제거된다면, 아래 결과와 같이 기존 코드의 절반 이하 크기로 경량화된다.

출처 https://fe-developers.kakaoent.com/2022/220217-learn-babel-terser-swc/

 

 

📖SWC

 

SWC는 Rust로 작성된 매우 빠른 컴파일러로, JavaScript 및 TypeScript 코드의 컴파일 및 번들링, 코드 최소화, 트리 쉐이킹(tree-shaking) 등 다양한 작업을 매우 빠른 속도로 처리할 수 있습니다. 

하지만 이름과는 달리 컴파일러의 기능만을 제공하는 것은 아닙니다.
Webpack 같은 자바스크립트 번들러의 기능을 제공할 spack도 개발 중이며 얼마든지 기능 확장이 가능하도록 설계되어 있기 때문에, 단순한 컴파일러라고 보기는 어렵고 하나의 플랫폼으로 보는 게 적절할 것 같습니다.
SWC의 개발한 한국인 개발자이신 강동윤 님께서 직접 SWC 프로젝트의 목표를 느린 웹 빌드 툴 전체를 러스트로 다시 구현하는 것이라고 말씀하신 바가 있기 때문에, SWC의 기능들은 앞으로도 점점 확장될 것으로 보입니다.

 

 

🤔그렇다면 SWC는 왜 빠른 것일까?

NextJS에서는 SWC를 시반으로 개발한 컴파일러를 통해 기존 빌드에 활용하던 Babel과 Terser을 대체한다.

이를 통해 트랜스파일링은 17배 빨라졌고, 코드 경량화 작업은 7배 빨라졌다고 한다.

 

그렇다면 빨라진 이유가 무엇일까?

바로 Rust라는 프로그래밍언어로 작성되었기 때문이다.

 

병렬 처리

Rust 이벤트 루프 기반의 싱글 스레드 언어인 JS와 달리 병렬 처리를 고려해서 설계된 언어이다.

JS는 이벤트 루프 기반의 싱글 스레드 언어이기 때문에 하나의 작업이 끝나야 다음 작업을 시작한다.

  • 컴퓨터는 멀티 태스킹이 가능해야하고, 이는 곧 Babel이나 Terser로 프로젝트를 빌드할 때 의존성이 없는 파일들은 동시에 변환되고 있어야 함을 의미한다.
  • 하지만 JS는 한 개의 스레드만을 사용하는 언어이기 때문에 Babel과 Terser은 한 번에 한 개의 파일만 변환할 수 있다.

하지만 Rust는 병렬 처리가 가능하도록 설계되어 있다.

  • 이 때문에 SWC는 의존성이 없는 파일들을 동시에 변환할 수 있다.
  • 만약 컴퓨터가 최대 5개의 작업을 동시에 진행할 수 있다면, SWC를 사용한 빌드 속도는 Babel이나 Terser로 빌드했을 때보다 최대 5배까지 빨라질 수 있는 것이다.

또한 SWC의 벤치마크 결과에 따르면 SWC는 싱글 스레드 환경에서도 Babel보다 20배나 빠르다고 한다.

  • 여기서 우리는 SWC가 단순히 여러 작업을 동시에 할 수 있다는 이유만으로 Babel보다 빠른 것은 아님을 알 수 있다.

메모리 관리 방식

그리고 Rust가 매우 성능이 뛰어난 이유 중 또 하나는 Rust만의 가비지 컬렉션을 다루는 방법인, 더 이상 사용되지 않는 데이터 객체에 의한 메모리 사용을 해소하는 메모리 관리 접근법에 있다.

 

Rust는 소유권(Ownership), 참조 및 빌림(Borrowing), 그리고 수명(Lifetimes)이라는 특별한 메모리 관리 시스템을 사용합니다.

이 시스템은 컴파일 시간에 메모리 안전성을 검사하여, 런타임 오버헤드 없이 메모리 누수와 같은 문제를 방지합니다.

 

간단하게 말하면 변수가 메모리를 가리키는게 아니라 메모리를 소유해버리는 개념이라고한다.

다른 변수에게 값을 넘겨주면 비어버리는 것이다.

 

이러한 시스템 덕분에, Rust 프로그램은 가비지 컬렉터가 없어도 메모리 안전성을 보장받을 수 있다.

컴파일러가 코드를 분석하여 메모리 관련 오류를 컴파일 시간에 발견하고, 런타임에 가비지 컬렉션으로 인한 성능 저하 없이 안전하고 효율적인 메모리 사용이 가능합니다

 

🤔가비지 컬렉터가 없다는 것?

가비지 컬렉션은 많은 메모리와 연산을 필요로하므로 결과적으로 코드의 속도를 저하시킨다.

어떤 방식의 쓰레기 수집을 사용하든 실행 시간에 작업을 하는 이상 성능 하락을 피할 수는 없다.
가비지컬렉션이 존재하더라도 메모리 누수는 발생할 수 있다.
이는 가비지컬렉션이 더 이상 접근이 불가능한 객체만 회수하기 때문이다.
설령 두 번 다시 사용하지 않는 객체라 할지라도, 프로그래머의 실수로 그 객체로 접근할 수 있는 경로가 하나라도 남게 되면 가비지컬렉션은 객체를 사용할 가능성이 있다고 판단하고 회수하지 않는다.
게다가 만약 이러한 객체가 프로그램의 실행 도중 계속해서 누적되어 간다면, 프로그램은 메모리 부족으로 결국 뻗고 말 것이다.

이 문제는 근본적으로 가비지컬렉션이 객체가 계속 사용될지 아닐지 스스로 판단할 수 없기 때문에, 객체에 접근할 수 있는 경로가 있을 경우 무조건 사용하는 객체로 간주해서 발생하는 문제다.
 실제로 가비지 컬렉션을 제공하는 언어를 사용한다 하더라도, 프로그램이 복잡해질수록 이러한 종류의 메모리 누수가 발생하는 것이 드물지 않다.
쓰레기 수집기가 많은 경우에 알아서 처리하긴 하지만 만능은 아니란 것이다.

 

 

이렇게 Babel과 Terser, SWC에 알아보았다.

SWC를 처음 알게되면서 이걸 왜 쓰는지, 빨라서 쓴다면 왜 빠른지 에 대해서 궁금해졌다.

그래서 학습하기 쉽도록 내가 찾은 정보들을 모아봤다.

 

 

출처

https://im-developer.tistory.com/230

 

[Kasra Khosravi] Why you should use SWC (and not Babel) 한글 번역

Why you should use SWC (and not Babel) - LogRocket Blog In this post, we'll cover the basics of transpilers and we'll compare Babel and SWC based on setup, execution, and speed. blog.logrocket.com 아래 글은 Kasra Khosravi가 작성한 Why you should us

im-developer.tistory.com

https://nextjs.org/blog/next-12

 

Next.js 12

Next.js 12 introduces a brand-new Rust compiler, Middleware (beta), React 18 Support, Native ESM Support, URL Imports, React Server Components (alpha), and more!

nextjs.org

https://fe-developers.kakaoent.com/2022/220217-learn-babel-terser-swc/

 

초보 웹 개발자를 위한 자바스크립트 빌드 툴과 SWC | 카카오엔터테인먼트 FE 기술블로그

이혁원(bill) 소설을 매우 좋아하는 FE 개발자입니다. 매달 카카오페이지에 캐시를 헌납하고 있습니다.

fe-developers.kakaoent.com

https://velog.io/@hamjw0122/Next.js-%EC%99%9C-Next.js%EB%8A%94-SWC%EB%A5%BC-%EC%84%A0%ED%83%9D%ED%96%88%EC%9D%84%EA%B9%8C

 

[Next.js] 왜 Next.js는 SWC를 선택했을까?

👩‍🏫 Next.js는 12버전부터 내장으로 babel 대신 SWC를 활용한 컴파일을 실행한다. SWC는 무엇이고 Babel과는 무엇이 다를까?

velog.io

 

728x90
반응형
LIST