프론트엔드/실전 리액트 프로그래밍

[스터디 with 실전 리액트 프로그래밍] 16편 - 바벨

Junheehee 2022. 8. 21. 17:58

실전 리액트 프로그래밍

 

바벨과 웹팩은 웹 어플리케이션을 제작할 때 필수적인 기술이다.

create-react-app, next.js 등 프로젝트를 구축해주는 라이브러리, 프레임워크는 바벨과 웹팩을 기본적으로 포함한다.

바벨과 웹팩을 자세히 모르더라도 내장된 기본 설정으로 간단한 프로젝트를 제작할 수는 있지만,

프로젝트의 규모가 점점 커지면 바벨과 웹팩을 알아야 할 순간이 찾아온다.

 

 

 

 

바벨(Babel)

 

바벨은 입력과 출력이 모두 자바스크립트 코드인 컴파일러다.

하이 레벨의 언어를 로우 레벨 언어로 변환하는 보통의 컴파일러와는 비교된다.

바벨은 JSX, ES6 등 최신 문법을 브라우저에서 이해할 수 있는 코드로 변환시켜준다.

바벨을 이용하면 오래된 브라우저도 최신 자바스크립트 코드를 이해할 수 있게 된다.

 

 

먼저 바벨을 실행할 프로젝트를 만들자.

 

$ npm init -y
$ npm install @babel/core @babel/cli
@babel/plugin-transform-arrow-functions
@babel/plugin-transform-template-literals
@babel/preset-react

필요한 패키지들을 설치한다.

 

// package.json

{
  "name": "7-1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/cli": "^7.18.10",
    "@babel/core": "^7.18.10",
    "@babel/plugin-transform-arrow-functions": "^7.18.6",
    "@babel/plugin-transform-template-literals": "^7.18.9",
    "@babel/preset-react": "^7.18.6"
  }
}

package.json을 보면 패키지가 잘 설치된 것을 볼 수 있다.

 

 

이제 바벨로 컴파일할 코드를 만들자.

 

// code.js

const element = <div>babel test</div>;
const text = `element type is ${element.type}`;
const add = (a, b) => a + b;

이 코드를  바로 실행하면 오류가 발생한다.

node.js는 이 코드를 아직 이해할 수 없기 때문이다.

 

코드를 실행하기 위해선 바벨로 컴파일해야 한다.

첫번째줄 JSX 문법은 @babel/preset-react로 변환할 것이다.

두번째줄 템플릿 리터럴 코드는 @babel/plugin-transform-template-literals,

세번째줄 화살표 함수는 @babel/plugin-transform-arrow-functions로 변환 할 것이다.

 

 

바벨을 실행하는 방법은 여러가지가 있다.

먼저 @babel/cli로 실행해보자.

 

 

 

 

@babel.cli

 

다음 명령어를 실행하면

$ npx babel src/code.js
 --presets=@babel/preset-react
 --plugins=@babel/plugin-transform-template-literals
 ,@babel/plugin-transform-arrow-functions

 

콘솔창에 실행 결과가 출력된다.

// 콘솔창

const element = /*#__PURE__*/React.createElement("div", null, "babel test");
const text = "element type is ".concat(element.type);

const add = function (a, b) {
  return a + b;
};

 

JSX문법은 createElement 함수로 변환되었다.

템플릿 리터은 concat 메서드로, 화살표 함수는 일반 함수로 변환되었다.

 

 

@babel/cli를 이용하면 위에 명령어의 --presets처럼 대부분의 설정값을 표현할 수 있지만,

편의를 위해 설정 파일을 따로 만들자.

 

파일명은 babel.config.js로 하자.

// babel.config.js

const presets = ["@babel/preset-react"];
const plugins = [
  "@babel/plugin-transform-template-literals",
  "@babel/plugin-transform-arrow-functions",
];

module.exports = { presets, plugins };

 

이제 cli에서 --presets들을 일일이 쓰지 않아도 된다.

$ npx babel src/code.js

바벨이 잘 실행된 것을 확인할 수 있다.

 

 

컴파일 결과를 파일로 따로 저장하고 싶다면, 아래 명령어를 실행하면 된다.

$ npx babel src/code.js --out-file dist.js
$ npx babel src --out-dir dist

첫번째는 파일만 변환하고, 두번째는 폴더 전체를 변환해준다.

 

 

 

 

웹팩의 babel-loader

 

이번엔 웹팩의 babel-loader로 바벨을 실행해보자.

 

필요한 웹팩과 패키지를 설치하자.

$ npm install webpack webpack-cli babel-loader

 

그리고 config 파일을 만들어 웹팩 설정을 하자.

// webpack.config.js

const path = require("path");

module.exports = {
  entry: "./src/code.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "code.bundle.js",
  },
  module: {
    rules: [{ test: /\.js$/, use: "babel-loader" }],
  },
  optimization: { minimizer: [] },
};

babel-loader는 바벨의 설정 파일 babel.config.js를 이용한다.

babel-loader로 변환된 code.bundle.js 파일을 확인할 수 있다.

 

 

 

 

@babel/core

 

앞서 본 @babel/cli와 babel-loader 모두 @babel/core을 이용해서 바벨을 실행한다.

이번엔 직접 @babel/core로 바벨을 실행해보자.

 

// runBabelCore.js

const babel = require("@babel/core");
const fs = require("fs");

const filename = "./src/code.js";
const source = fs.readFileSync(filename, "utf8");
const presets = ["@babel/preset-react"];
const plugins = [
  "@babel/plugin-transform-template-literals",
  "@babel/plugin-transform-arrow-functions",
];
const { code } = babel.transformSync(source, {
  filename,
  presets,
  plugins,
  configFile: false,
});
console.log(code);

@babel/core 모듈을 가져온 뒤 플러그인과 프리셋을 설정한 후 변환하였다.

"node runBabelCore"로 파일을 실행하면 바벨로 변환된 코드가 콘솔창에 출력된다.

 

 

바벨의 컴파일은 세 단계를 거친다.

  1. 파싱 단계: 입력된 코드로부터 AST(abstract syntax tree)를 생성한다.

  2. 변환 단계: AST를 원하는 형태로 변환한다.

  3. 생성 단계: AST를 코드로 출력한다.

 

@babel/core을 이용하면 변환 단계에서 하나의 AST로부터 여러 설정으로 변환할 수 있기 때문에 자유도가 높다.

 

똑같은 코드를  다른 플러그인을 적용해서 각각 변환해보자.

const babel = require("@babel/core");
const fs = require("fs");

const filename = "./src/code.js";
const source = fs.readFileSync(filename, "utf8");
const presets = ["@babel/preset-react"];
const { ast } = babel.transformSync(source, {
  filename,
  ast: true,
  code: false,
  presets,
  configFile: false,
});
const { code: code1 } = babel.transformFromAstSync(ast, source, {
  filename,
  plugins: ["@babel/plugin-transform-template-literals"],
  configFile: false,
});
const { code: code2 } = babel.transformFromAstSync(ast, source, {
  filename,
  plugins: ["@babel/plugin-transform-arrow-functions"],
  configFile: false,
});
console.log(code1);
console.log("\n");
console.log(code2);

같은 프리셋으로 먼저 AST를 생성하고,

code1은 plugin-transform-template-literals을, code2는 plugin-transform-arrow-functions을 적용했다.

바벨을 실행하니 적용한 플러그인에 따라 다르게 변환된 것을 확인할 수 있다.

// code1
const element = /*#__PURE__*/React.createElement("div", null, "babel test");
const text = "element type is ".concat(element.type);

const add = (a, b) => a + b;


// code 2
const element = /*#__PURE__*/React.createElement("div", null, "babel test");
const text = `element type is ${element.type}`;

const add = function (a, b) {
  return a + b;
};

 

 

 

 

env

 

바벨 설정 파일에는 사용할 수 있는 다양한 속성이 있다.

extends 속성으로 다른 설정 파일을 가져오거나, overrides 속성으로 파일별로 설정할 수도 있다.

 

그 중 env 속성은 환경별로 설정할 수 있게 해준다.

 

프로덕션 환경 설정을 추가해보자.

// babel.config.js

const presets = ["@babel/preset-react"];
const plugins = [
  "@babel/plugin-transform-template-literals",
  "@babel/plugin-transform-arrow-functions",
];
const env = {
  production: {
    presets: ["minify"],
  },
};

module.exports = { presets, plugins, env };

env 속성을 이용해 프로덕션 환경에 minify 프리셋을 추가했다.

 

이제 프로덕션 환경으로 바벨을 실행해보자.

// 명령어
$ NODE_ENV=production npx babel ./src/code.js


// 실행 결과
const element=/*#__PURE__*/React.createElement("div",null,"babel test"),
text="element type is ".concat(element.type),add=function(c,a){return c+a};

minify가 적용되어 코드가 압축되었다.

NODE_ENV 환경 변수를 설정하지 않으면 기본값은 development이다.

 

 

 

 

폴리필(polyfill)

 

바벨을 사용하더라도 최신 기능을 모두 사용할 수 있는 것은 아니다.

만약 바벨로 최신 코드를 변환했더라도 브라우저가 오래되었다면 코드가 오류날 수 있기 때문이다.

이 때는 폴리필로 런타임에 기능을 주입해야한다.

즉 바벨 뿐만 아니라 폴리필에 대한 별도 설정도 해야 한다.

 

 

core-js는 바벨에서 폴리필을 위해 공식적으로 지원하는 패키지다.

core-js는 사용법이 간단하지만 필요하지 않은 플로필까지 추가함으로 번들 파일의 크기가 커진다.

 

필요한 플로필만 추가하기 위해서 core-js로부터 특정 플로필만 추가하는 방법도 있고,

@babel/preset-env로 실행 환경에 맞는 플로필을 자동으로 추가해줄 수도 있다.

당연히 최신 실행 환경으로 갈수록 추가되는 플로필이 적어진다.

 

 

 

 

코드

 

 

GitHub - junhee-won/react-study: with 실전 리액트 프로그래밍

with 실전 리액트 프로그래밍. Contribute to junhee-won/react-study development by creating an account on GitHub.

github.com