공부/CS

[CS] 웹팩(webpack), 바벨(babel)

youngble 2023. 1. 12. 04:23

그전에 웹팩 과 바벨에 관하여 정리한적이 있는데 개념뿐만 아니라 어떠한 흐름으로 쓰는지에 대해서 좀더 디테일하게 정리하고자 한다.

 

순서

웹팩 적용

1. 웹팩 설치 : webpack/ webpack-cli/ webpack-dev-server

2. webpack.config.js

3. package.json scripts 빌드 명령어 

3. 로더: css-loader / style-loader

4. 플러그인(Plugin)

5. 개발서버(server)

바벨 적용

1. 바벨설치: babel-loader/ @babel/core / @babel/preset-env

2. 프러그인(Plugin)

3. Preset

4. 리액트 / @babel/preset-react

5. babel-loader


웹팩 적용

1. 웹팩 설치

원리를 알기위해 vanilla JS 로 진행 프로젝트를 셋팅하도록 하자.

mkdir vanillaProject
cd vanillaProject
npm init -y
npm i -D webpack webpack-cli

1. 먼저 vanillaProject 라는 프로젝트 디렉토리폴더를 만든다.

2. 해당 폴더에서 패키지 모듈 관리를 위해 npm init -y 로 package.json을 만들어준다.

3. 번들링을 위하여 웹팩을 설치한다. 

webpack : 웹팩 라이브러리

webpack-cli : 터미널을 통해 webpack 관련 커멘드(명령어)를 사용하기위한 패키지

4.  기본적인 번들링을 진행하기위해 html, js, css 파일을 src 폴더안에 만들어준다.

 

폴더구조

vanillaProject
├── src
│   ├── index.html
│   ├── index.js
│   └── style.css
└── package.json

src/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>연습!</title>
  </head>
  <body></body>
</html>

src/index.js

import "./style.css";
function component() {
  const element = document.createElement("div");
  element.innerHTML = "Hello world";
  element.addEventListener("click", () => {
    console.log("Hello world");
  });
  return element;
}

document.body.appendChild(component());

src/style.css

body {
  background-color: gray;
}

2. webpack.config.js

이제 웹팩 설정을 위해 webpack.config.js 파일을 root 위치에 생성한다.

const path = require("path");

module.exports = {
  mode: "production",
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "build"),
    clean: true,
  },
};

mode: 'production', 'development', 'none'  세가지 옵션

1. production : 최적화되어 빌드됨 (용량이 적음)

2. development: 빠르게 빌드됨(용량은 production보다 크지만 번들링 빌드는 빠르기때문에 테스트시 사용)

3. none : 아무기능 없음

 

3. package.json scripts 빌드 명령어

"scripts": {
   "build": "webpack --mode=production",
   "build:dev": "webpack --mode=development",
  },

build시에 웹팩을 통해 번들링을 할수 있도록 위와 같이 scripts 명령어를 넣는다.

이때 --mode=production 과 --mode=development 부분을 별도로 만들어서 써도되고 위의 webpack.config.js 에서 mode에 직접 넣고 scripts에서는 지우고 "webpack" 이라고만 넣어도 된다.

 

4. 로더 loader

 

로더란 Javascript 파일과 Json  파일외의 다른 리소스들 역시 번들링 하기 위해서 사용하는 것이다.

1. css-loader

이름그대로 css 파일에 대해서 번들링을 위해서 사용하는 로더인데 js파일에서  import/require()로 사용하여 css파일을 모듈로 불러오기 위해서 사용한다.

2. style-loader

style-loader는 css-loader와 묶음으로 생각하면 되는데 CSS파일을 모듈로 불러왔다고해서 DOM트리에 주입되는건 아니다. 따라서 css를 를 수집하여 style태그로 만들어 DOM에 넣어주는 로더이다.

 

설치

npm i -D css-loader style-loader

 

 

js파일 말고도 번들링을 하기위해서 사용할 로더를 webpack.config.js에 넣어주도록 한다.

module.exports = {
... 생략,
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      }]
    }
 }

주의할 점은 use의 배열값의 순서가 역순으로 실행된다는 것을 생각해야된다. css-loader를 먼저 실행하고 style-loader를 실행 하는 순서이다.

 

설정이 완료되었다면 npm run build 실행한다. 그러면 build폴더안에 bundle.js 라는 이름으로 번들링된 파일을 확인 할 수 있다.

 

4. 플러그인(Plugin)

플러그인은 웹팩이 단순한 번들러 이상의 역할을 해주게 한다. 곧 적용할 바벨의 경우에서도 플러그인을 통해 여러 역할을 할 수있도록 해준다. 예를들어 빌드 정보를 추가한다거나 CSS파일을 뽑아준다던지 등 해당 기능을 가진 플러그인들을 통해서 가능하게 해준다. 웹팩이 인기 있는 이유가 이러한 플러그인 때문이라고 한다.

 

여기서 우리가 필요한건 번들링된 파일들을 모두 포함한 HTML 파일 이기 때문에 번들링된 index.html 을 추출할 플러그인을 사용해보자.

html-webpack-plugin

html-webpack-plugin 을 설치하여 모든 번들을 포함한 HTML을 자동으로 생성해주는 기능을 사용해보도록 하자. 

npm i -D html-webpack-plugin

webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  mode: "production",
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "build"),
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  plugins: [new HtmlWebpackPlugin({ template: "./src/index.html" })], 
};

이렇게하고 npm run build 해주면 bundle.js 뿐만아니라 index.html 도 생성된다.

 

5. 개발서버(server)

코드 수정시에도 다시 빌드되고 핫리로드를 적용하기위해서 webpack-dev-server를 설치한다.

npm i -D webpack-dev-server

완료되었다면 package.json 의 scripts에 "start" : "wepback serve" 를 추가해주고 npm run start 를 해준다.

Default 값으로 로컬서버호스트 port는 8080 이다. 

webpack.config.json 에서 devServer 옵션을 추가하여 수정할 수 있다. 예를들어

// webpack.config.js
{
...이하생략,
devServer: {
    historyApiFallback: true,
    compress: true,
    port: 9000,
  },
  }

이렇게 넣게되면 compress 옵션으로 gzip 압축이 되고, port를 8080이 아닌 9000으로 설정한다.

그리고 중요한건 historyApiFallback 인데, 해당 경로에서 잘못된 경로를 가게된다면 ex) localhost:9000/home

잘못된 경로에 대해서 Html5 history API에서 404 에러를 처리하기위해 index.html로 리다이렉트 시켜주는 옵션이다. 따라서 만약에 경로를 /home 이라고 붙여도 index.html로 리다리엑트 시켜주기 때문에 보이는건 루트 경로인 / 화면이 보이게된다. 또한 SPA와 같은 앱을 만들때 하나의 index.html로 클라이언트사이드렌더링 처리시에도 만약 원하는 경로로 바꾸어준다면 404처리를 index.html로 하기위해서 react router 처럼 처리하여 해당 경로의 컴포넌트를 보여주게 처리한다. 따라서 AWS나 firebase 통해 배포시 404일때 어떤걸 보여줄지 설정할때 index.html 을 넣는 이유이다. SPA앱을 만들때 필수적으로 사용해야하는 옵션이라고 생각하면 된다. 

 

 

바벨 적용

위와 같이 바벨도 적용해야하는데, 바벨은 브라우저 실행환경에 구애받지 않고 최신 문법으로 코딩할 수 있도록 도와주는 자바스크립트 트랜스파일러(transpiler)이다. 같은 언어를 다른 실행 환경에서도 돌아갈수 있도록 해주는데 babel은 ES6이상의 최신 문법 코드를 ES5이하의 예전 문법으로 변경 해준다.

 

또한 이전글 처럼 babel을 통하여 react의 JSX, typescript 또한 변경해주는 것이다.

 

1. 바벨(babel) 설치

먼저 바벨의 기본적인 두 패키지를 설치하자.

1. @babel/core

2. @babel/cli

 

2. 플러그인(plugin)

 babel자체만으론 아무런 기능을 하지 않기 때문에 plugin 을 사용해보도록 해야한다.

예를들어 npx babel index.js 라고 입력하여도 아무런 일도 일어나지 않기 때문이다.

// index.js
const arrow = () => console.log("hello");

만약 위와 같은 ES6문법 중 하나인 화살표함수(Arrow Function)가 있다고한다면 이를 es5이하 문법으로 바꾸기위해서 사용하는 플러그인은 @babel/plugin-transform-arrow-functions 를 사용한다.

npm i -D @babel/plugin-transform-arrow-functions

그리고 .babelrc 혹은 babel.config.js 파일에 해당 플러그인 dependency를 쓴다고 설정해야한다.

babel.config.js 과 .babelrc 둘중 무엇을 사용하여 사용하는지 차이점은 무엇인지는 나중에 다루도록 한다.

 

1) babel.config.js 로 설정할 경우

// babel.config.js
module.exports = {
  plugins: ["@babel/plugin-transform-arrow-functions"],
};

or

2) .babelrc 로 설정할 경우

// .babelrc
{
  "plugins": ["@babel/plugin-transform-arrow-functions"]
}

 

이제 다시 npx babel index.js 를 실행하면 화살표함수가 일반함수로 변경된 것을 확인 할 수있다.

하지만 이렇게 매번 Es6이상 문법을 하나하나 바꾸기위해 npm 패키지를 설치하고 플러그인을 다시 셋팅해준다면 귀찮고 번거롭다. 따라서 위와 같은 하나하나의 플러그인을 설치하여 사용하지 말고 모든 plugin들을 미리 모아놓은 preset을 사용하도록 한다.

 

3. preset

따라서 다음과 같은 preset을 사용하면 해당 preset의 plugin들을 자동적으로 설치된다.

1. @babel/preset-env

2. @babel/preset-flow

3. @babel/preset-react

4. @babel/preset-typescript

 

이중에서 우리는 @babel/preset-env 만 사용하도록한다.

npm i -D @babel/preset-env

 

1) .babelrc의 경우

{
  "presets": ["@babel/preset-env"]
}

2) babel.config.js의 경우

module.exports = {
  presets: ["@babel/preset-env", "@babel/preset-react"],
};

 

만약 추가적으로 다른 preset을 사용하고 싶다면 배열안에 넣으면 된다 ex) "presets" : ["@babel/preset-env","@babel/preset-react"]

 

4. React / @babel/preset-react

리액트와 같은 Jsx 문법을 변경하기위해서 @babel/preset-react 를 .babelrc 혹은 babel.config.js 에 넣어 사용한다.

 

src/reactComponent.js

import ReactDOM from "react-dom";

const A = () => {
  return (
    <div>
      <h1>test!!!</h1>
    </div>
  );
};

ReactDOM.render(<A></A>, document.getElementById("root"));

npx babel ./src/reactComponent.js --out-dir dist 실행하면 아래와 같이 jsx의 코드가 변환된걸 확인할 수 있다.

"use strict";

var _reactDom = _interopRequireDefault(require("react-dom"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
var A = function A() {
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h1", null, "test!!!"));
};
_reactDom["default"].render( /*#__PURE__*/React.createElement(A, null), document.getElementById("root"));

 

5. babel-loader

이제 바벨로 변경된 js파일을 weback의 babel-loader를 통해 이어준다.

 {
 ...
 module: {
    rules: [
 ...
     ,
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        }
        ]
      },

 

그후 npm run build 하면 babel-loader가 실행된다. .babelrc 혹은 babel.config.js 에 설정된 것에 따라 트란스파일링후 이를 webpack 번들 모듈로 연결해준다.

만약 webpack.config.js 에서 모두 설정하고 사용할려면 .babelrc 파일과 babel.config.js 파일을 지우고 다음과 같이 설정한다.

 {
 ...
 module: {
    rules: [
 ...
     ,
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react"],
          },
        }
        ]
      },

options 에 presets을 넣어 사용한다.