Don’t worry about failures

babel, terser and SWC with Next.js (1) 본문

Type.Java.Script

babel, terser and SWC with Next.js (1)

허흥 2024. 3. 9. 08:30
728x90

해당 글에서는 babel, terser의 기본 개념과 이를 넘어서기 위한 새로운 도구인 SWC에 대해 살펴보고자한다.

 

1. Babel

1-1 개념 및 테스트

바벨은 구형 브라우저나 환경에서 ES6+의 기능들을 사용하기 위해서 이전 스크립트 버전으로 변환해주는 트랜스파일러이다. 

간단한 예를 봐보자

https://babeljs.io/repl

 

Babel · Babel

The compiler for next generation JavaScript

babeljs.io

간단하게 테스트할 수 있게 바벨에서 웹으로 지원해준다.

아래와 같이 코드를 작성했다고 해보자.

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

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

  return fullText;
};

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

 

이를 바벨로 트랜스파일링하면,

var HelloWorld = function HelloWorld(_ref) {
  var name = _ref.name,
    content = _ref.content;
  var fullText = "";
  if (name) {
    fullText += "name: ".concat(name);
  }
  if (content) {
    fullText += "content: ".concat(content);
  }
  return fullText;
};
HelloWorld({
  name: "Bill",
  content: "Hello World!"
});

 

이와 같이 변하게 된다.

1. const,let -> var

2. 템플릿 리터럴을 concat으로 변경

3. 구조분해 할당을 _ref를 통해 받고 하나씩 변수에 할당

4. arrow function을 일반 함수로 변경

 

이와 같이 새로운 기능들을 이전의 버전으로 되돌려준다.

 

1-2 Preset

Babel은 위의 예제에서 봤듯이 코드를 변환해주는 역할을 한다. 이를 프로젝트에 적용하려해보자.

만약 arrow function을 트랜스파일링 한다고 하면,

  1. @babel/core : 바벨의 핵심적인 기능
  2. @babel/cli : 터미널로 바벨을 사용
  3. @babel/plugin-transform-arrow-functions : 화살표 함수를 transform하는 플러그인

위의 모듈을 설치 후 아래와 같은 명령을 통해 적용해볼 수 있다.

./node_modules/.bin/babel [변환할 파일] --out-dir [변환될 위치] --plugins=@babel/plugin-transform-arrow-functions

 

이와 같이 하나하나의 플러그인을 통해 적용하기엔 번거롭기도 하면서 복잡한 과정이 될 것이다.

이러한 작업을 일괄적으로 처리할 수 있게 해주는 것이 Preset 이다.

 

preset의 경우 .babel.config.json 또는 .babelrc.json 파일을 통해 적용해볼 수 있다.

yarn add --dev @babel/preset-env

{
	"presets" : [
		[
			"@babel/preset-env",
			{
				"targets" : {
					"edge" : "17",
					"firefox" : "60",
					"chrome" : "67",
					"safari" : "11.1"	
				},
				"useBuiltIns" : "usage",
				"corejs" : "3.6.5"
			}
		]
	]
}

 

 

1-3 useBuiltInts & corejs

여기서 useBuiltIns와 corejs에 대해 살펴보자.

useBuiltIns : option값으로는 usage, entry, false이고, default는 false이다.

이 옵션은 @babel/preset-env가 polyfills를 어떻게 다룰 것이냐에 대한 옵션값이다.

 

[여기서 잠깐]

polyfills의 간단 설명

구형 브라우저에서 제공되지 않는 최신 기능들을 지원하고자 가져오는 코드 뭉치.

좀더 같단하게 이야기하면 버전이 올라면서 기능의 업그레드 개념이 아닌 완전 신규기능으로 구형으로 변환하기 어려운 코드를 새롭게 코드를 추가해준다고 생각하면 된다.

간단한 예시로는, arrow function과 같이 일반 함수로 변환이 되는 것과는 다르게

Promise, Array.prototype.includes와 같은 경우 변환이 되지 않기 때문에 polyfills를 통해 보충해줘야한다.

@babel/polyfill이 존재했지만 babel 7.4.0부터 deprecated가 되어 이를 대신하여 core-js가 대체 되었다.

 

다시 돌아와서,

useBuiltInts 옵션은 @babel/preset-env는 core-js를 임포트하여 polyfill의 역할로 없는 코드를 추가해주는 것이다. corejs의 옵션을 통해 버전을 명시할 수 있다.

 

core-js install :

yarn add core-js@3 or yarn add core-js@2 

 

useBuiltInts : false

babel이 자동으로 polifill을 삽입해주지 않는다. 직접 관리 필요.

 

useBuiltInts : entry

babel은 각 파일의 최상단에 필요한 폴리필 코드를 환경에 맞게 자동으로 삽입한다.

=> 전역에 삽인되기 때문에 전역스코프 오염 문제가 발생할 수 있다.

 

useBuiltInts : usage

실제 사용한 폴리필만 전역이 아닌 해당 파일에 삽입한다.

 

1-4 웹펙과 함께 사용하기

주로 바벨의 경우 단독으로 쓰기보다는 웹팩과 함께 사용한다.

yarn add @babel/core @babel/preset-env babel-loader --dev

or

npm i @babel/core @babel/preset-env babel-loader -D

webpack.config.js > module > rules

{
    test: /\.(?:js|mjs|cjs)$/,
    exclude: /(node_modules)/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: [
                ['@babel/preset-env', { targets: "ie 11" }]
            ]
        }
    }
}

exclude의 경우 node_modules의 모든 라이브러리를 babel-loader로 처리해버리면 느려지기 때문에 제외한다.

 

추가로 여기서 polyfill을 설정을 해보자.

{
    test: /\.(?:js|mjs|cjs)$/,
    exclude: /(node_modules)/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: [
                ['@babel/preset-env', {
                    useBuiltIns: 'usage',
                    corejs: 3
                }]
            ]
        }
    }
}

위와 같이 설정만 하면 에러가 날 것이다.

웹팩은 바벨 로더가 만든 아래와 같은 코드(예시 코드임)를 만나면 core-js를 찾을 것이기 때문이다.

아래 이미지는 core-js 없이 빌드 시 나온 에러

require("core-js/modules/es6.promise")
require("core-js/modules/es6.object.to-string")

npm i core-js@3

 

이제 다시 해보자. 하기에 앞서 간단한 테스트를 위해

const asyncTest = async () => {

}

export default asyncTest;

async 코드를 넣고 트랜스파일 시켜보자.

 

 

2. Terser

terser는 빌드 툴로써 우리가 직접 관리한 경험은 많이 없지만, 많은 툴에서 사용이되고 있다. 특히 webpack v4부터 별도 설정 없이 프로덕션 환경에서 자동으로 Terser 툴을 사용하도록 되어 있다.

 

terser의 역할은 무엇일까?

Acorn을 사용하여 Javascript 코드를 파싱하여 코드 압축, 난독화 등을 작업한다.

Acorn은 빠르고 경량화된 Javascript 파서로 ES6+의 문법을 지원하고, 코드를 추상 구문 트리(Abstract Syntax Tree, AST)로 변환한다.

javascript의 parser, mangler, compressor이다.

 

하나씩 살펴보자. 

 

2-1. Parser

위에서 언급했듯이 코드를 Acorn을 통해 AST로 변환하는 과정이다. 이를 기반으로 mangler, compressor 작업이 진행된다.

 

2-2 mangler

단어 뜻을 보면, "엉망으로 만들아", "심하게 훼손하다" 의 뜻을 가지고 있다.

단어뜻에 맞게 우리의 코드를 엉망으로 만들어 버린다. 

 

우리는 흔히 코드를 짤 때 코드의 가독성을 위해 변수, 함수의 naming에 신경을 많이 쓴다. 예를 들어, 유효한가? 에 대한 함수를 이름을 짤때, isInvalid()와 같이 하곤한다.

이 단어는 컴퓨터 입장에서 생각해보자. 과연 중요할까? 그렇지 않다. 컴퓨터가 읽을 때는 단어의 의미가 중요하지 않는다. 이 의미는 단순하게 'a', 'b'로써 표현해도 상관이 없다는 것이다.

이를 이용하여 mangler는 우리가 열심히 생각하고 짜놓은 코드를 단순화 시킨다. isInvalid() -> i() 이런식으로.

그럼 왜 이렇게 하는가?

용량을 감소시켜준다. isInvalid 9개의 알파벳이 1개의 알파벳으로 변했다. 이게 규모가 커진다고 하면, 그 효과는 더 커질 것이다.

 

테스트

npm install terser -g

 

이전에 테스트했던 js파일로 테스트해보자

 terser --mangle --toplevel asyncTest.js -o asyncTest-mangled.js
const asyncTest = async () => {
    const isInvalid = true;
    let returnText = 'what';
    
    if(isInvalid) {
        returnText = 'invalid';
        return false;
    }

    returnText = 'valid';
    return true;
}

export default asyncTest;

 

결과: 변수 이름 심플화, 용량 243 -> 115 바이트로 감소.

 

 

2-3 Compressor

compressor는 단어 그대로 압축을 한다는 의미이다. 

그렇다면 압축을 어떻게 진행될까?

위에서 테스트했던 asyncTest.js파일로 테스트해보자.

terser --compress --toplevel asyncTest.js -o asyncTest-compressed.js

 

결과: 불필요한 코드도 제거해주는 등 코드를 최소화 시켜준다. 243 -> 79로 감소

 

이와 같이 코드를 압축해주고, 변수이름을 간소화해주는 등. 즉, mangler, compressor를 통해 경량화를 시켜준다. 이처럼 경량화 해주는 툴은 minifier라고 불린다.

 

마지막 주제인 SWC with Next.js의 경우 내용이 길어져 다음 글에 이어가도록 하겠다.

 

참고 자료:

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

 

 

728x90

'Type.Java.Script' 카테고리의 다른 글

javascript prototype에 대해  (0) 2024.03.12
babel, terser and SWC with Next.js (2)  (0) 2024.03.10
requestAnimationFrame  (0) 2024.03.07
Javascript의 가비지컬렉터  (0) 2024.03.03
push와 splice  (1) 2022.12.01