yoxxin 2023. 3. 14. 01:30

우아한테크코스 5기 미션 점심 뭐 먹지 의 웹팩 설정 파일을 보며 웹팩 기초를 공부한 과정입니다.

0. 작성 이유

점심 뭐 먹지 미션을 하면서 html 파일을 분리한 구조를 사용했습니다.

당연히 js 에서 htmlimport 할 수 있을 것이라고 생각했는데요, 안되더라구요

그래서 이번 기회에 아예 몰랐던 webpack 공부를 해봤고, 그 정리본입니다.

처음 공부했기 때문에 아래 내용은 정확하지 않을 수도 있어요. 잘못된 부분은 같이 고쳐봐요. 댓글 환영합니다!

1. webpack 이란?

웹 페이지를 만들 때 생기는 수많은 파일들(js, css, html, assets 등) 을 하나로 묶어주는 도구 중 하나 (번들러 라고 합니다)

webpack 이외에도 parcel 같은 번들러가 있습니다.

2. webpack 도입 시 효과

image

먼저 여러 파일을 하나로 묶어주어서 클라이언트↔서버 간 통신 부하가 줄어듭니다!

또한 모듈끼리는 전역스코프를 공유하는 문제가 있는데요,

이를 해결하기 위해 즉시실행함수로 모듈을 함수스코프로 감싸주거나 <script type=’module’> 를 이용합니다.

하지만 모든 브라우저에서 모듈 시스템(<script type="module">)을 지원하지 않기 때문에, 호환성에 문제가 생깁니다.

모듈별로 스코프를 만들어주는 역할을 웹팩이 수행합니다!

3. webpack.config.js 란?

파일 번들 시 웹팩이 할일을 설정해주는 파일입니다.

4. 점심 뭐 먹지의 webpack.config.js

레포 링크: https://github.com/woowacourse/javascript-lunch

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
    clean: true,
  },
  resolve: {
    extensions: [".ts", ".js", ".mjs"],
  },
  module: {
    rules: [
      {
        test: /\.(js|mjs|ts)$/i,
        exclude: /node_modules/,
        use: {
          loader: "ts-loader",
        },
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        loader: "file-loader",
        options: {
          name: "[name].[ext]",
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./index.html",
    }),
  ],
  devtool: "inline-source-map",
  devServer: {
    static: "./dist",
    hot: true,
    open: true,
  },
};

각 설정(프로퍼티)별로 설정을 알아보겠습니다.

알아보기 전에! 점심 뭐 먹지의 package.json 에서는…

"scripts": {
    "watch": "webpack --watch",
    "start": "webpack serve",
    "build": "webpack --mode=production",
    ...
  },

webpack을 이용해 프로젝트 빌드를 해주고 있습니다.

이제 진짜 알아보죠.

mode

module.exports = {
  mode: "development",
    ...
}

mode를 통해 파일을 어떻게 번들링을 해줄 것인지 설정할 수 있습니다.

  • production(배포) 모드 : 로드 시간을 줄이기 위해 번들 최소화, 가벼운 소스맵 및 애셋 최적화에 초점을 맞춥니다.
    • 번들링 결과Untitled (2)
  • development(개발) 모드 : 개발 생산성을 높이기 위한 모드. 버그발생 위험이 있는 코드를 미리 경고해 주는 검증 코드도 포함되어 있습니다.
    • 번들링 결과Untitled (3)

production 모드는 띄워쓰기도 없애면서 번들링 파일을 최적화 하고있고, development 모드는 더 보기 쉽게 되어있는 걸 확인할 수 있습니다.

package.json을 다시보면,

"scripts": {
    "watch": "webpack --watch",
    "start": "webpack serve",
    "build": "webpack --mode=production",
    ...
  },

개발 시(watch, start를 이용)에는 development 모드를 이용하고 있고

배포 시에는 production 모드를 이용하는걸 볼 수 있습니다.

entry

module.exports = {
    ...
  entry: "./src/index.js",
    ...
}

entry는 번들링할 시작 파일을 명시할 수 있습니다.

entry로 설정한 파일에서 사용중인 파일들을(import하여 사용중인 파일이나 이미지) webpack이 알아서 번들링 해줍니다.

Untitled (4)

이건 제 점심 뭐 먹지 entry 인데요,

저기서 import 한 파일들을 webpack 이 따라가면서 연관된 파일들을 모두 번들링 해줍니다!

output

module.exports = {
  ...
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
    clean: true,
  },
    ...
}

output 은 번들링된 결과 파일에 대한 설정을 해줍니다.

  • filename: 번들링 결과 파일명
  • path: 번들링 결과 파일 경로
  • clean: 번들링 시 이전에 생성되어있던 번들링 파일을 지우고 번들링

이외에도 많은 설정들이 있습니다.

resolve

module.exports = {
    ...
  resolve: {
    extensions: [".ts", ".js", ".mjs"],
  },
    ...
}

resolve 은 모듈(파일)을 해석하는 방식을 설정합니다.

여러 경우에 대해서 설정할 수 있는데요,

점심 뭐 먹지에서는 확장자 extension 에 대해서만 정의해놓았습니다.

extensions: [".ts", ".js", ".mjs"] 이렇게 설정해놓으면 import 할 때 확장자를 생략할 수 있습니다.

생략된 확장자는 웹팩이 저기 명시된 순서대로(ts, js, mjs) 해석합니다.

entry 파일을 다시 볼까요?

Untitled (4)

import 문들을 보면 확장자가 생략되어 있습니다. 생략된 확장자들은 웹팩에서 붙여서 해석합니다.

12번째 줄을 보면 style도 불러오는 것을 볼 수 있습니다. 저렇게 style을 불러오려면 .css확장자도 추가해야겠네요.

그래서 저는 extenstions에 이렇게 추가해주었습니다.

extensions: [".ts", ".js", ".mjs", “.css”]

하지만 사실 웹팩은 js 파일만 해석할 수 있습니다.

따라서 저렇게만 해주면 기본 웹팩은 ts, css를 이해하지 못하기 때문에, loader를 설치해서 웹팩 기능을 확장시켜야 합니다.

loader 설정을 module 에서 해봅시다!

module

loader 은 웹팩이 기본적으로 제공해주는 기능에 추가해서 사용할 수 있는 extension 기능이라고 보시면 됩니다.

module.exports = {
    ...
  module: {
    rules: [
      {
        test: /\.(js|mjs|ts)$/i,
        exclude: /node_modules/,
        use: {
          loader: "ts-loader",
        },
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        loader: "file-loader",
        options: {
          name: "[name].[ext]",
        },
      },
    ],
  },
...
}

modulerules 에서 loader를 적용할 파일들을 정의할 수 있는데요,

즉 각 파일에 대해서 적용할 loader를 정의하는 것입니다.

  • test: loader를 적용할 파일의 확장자를 정규표현식으로 선언
  • exclude: 정규표현식에 일치하는 파일 중에서 제외할 파일
  • use: 사용할 loader들 선언

대표로 .css 에 적용하는 loaderstyle-loadercss-loader 에 대해서 알아보겠습니다.

{
    test: /\.css$/i,
    use: ["style-loader", "css-loader"],
},

(뒤쪽에 있는 loader가 먼저 실행됩니다. chaining이 있으므로 loader의 순서가 중요한 경우에 유의합시다)

  • css-loader는 css import문을 읽어서 가져와주는 역할
  • style-loader는 가져온 css를 실제로 주입해주는 역할

Untitled (5)

css-loaderstyle-loader 순서로 동작해야하므로 위 그림처럼 설정하면 style-loader 가 먼저 동작하기 때문에 에러가 납니다.

Untitled (6)

따라서 이렇게 설정해야합니다.

그러면 이제 우리는 js, ts 파일에서 css 파일들을 import하여 적용할 수 있습니다! 웹팩이 해당 코드를 이해할 수 있기 때문이죠.

💡 `webpack`에서 제공하는 공식 `loader`와 사용자들이 만든 `loader` 등 많은 `loader`들이 있으니 필요한 기능이 있다면 찾아서 사용합시다.

추가로 저는 js 에서 htmlimport 하기 위해 html-loader를 이용했습니다!

Plugins

image

loader와는 다른 또다른 extension입니다.

loader는 번들링 하는 과정(process)에서의 확장기능이라면 plugin은 최종 번들링 결과물(output)을 변경해주는 도구입니다. (웹팩 번들링 후 변환된 파일에 추가적인 기능을 더하기 위해 사용합니다.)

pluginloader보다 더 복합적이고 자유로운 일들을 할 수 있습니다.

loader는 동작 형태가 규정되어 있지만, pluginplugin 마다 사용방법이 제각각 다릅니다.

const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    ...
  plugins: [
    new HtmlWebpackPlugin({
      template: "./index.html",
    }),
  ],
    ...
}

점심 뭐 먹지에서 사용한 pluginHtmlWebpackPlugin 입니다.

npm 라이브러리인 html-webpack-plugin 을 설치해서 사용합니다.

HtmlWebpackPluginhtml 파일을 자동으로 생성해주는 역할을 하는데요,

만약 HtmlWebpackPlugin 없이 build 한다면 html 파일이 없습니다. 그래서 일일히 추가해줘야해서 번거롭죠.

image

하지만 HtmlWebpackPlugin 을 이용하면, 번들링 결과 생긴 js 파일을 import(<script type="module" src="./dist/about.bundle.js"></script>)하는 html 파일을 만들어서 output 디렉터리에 생성해줍니다.

점심 뭐 먹지의 설정에서는 template html을 이용하는 옵션을 추가했네요.

그러면 template html 에 번들링 된 결과 생성된 bundle.js 파일을 참조하는 html 파일을 만들어줍니다.

image

)

image

)

image

이 외에도 다양한 플러그인들이 있으니, 찾아서 적용하시면 됩니다.

devtool

devtool소스맵을 어떻게 생성할지 그 규칙을 정하는 프로퍼티입니다.

소스맵에 대해서는 링크로 남겨놓았습니다.

devServer

코드를 실행하려고 매번 npm run build 를 수동으로 실행하기엔 번거롭죠. 그래서 webpack은 코드가 변경될 때 마다 자동으로 컴파일하는 옵션 devServer을 제공합니다.

devServer 를 사용하려면 먼저 "webpack-dev-server" npm 라이브러리를 설치해야합니다.

점심 뭐 먹지 package.json 에는 4.11.1 버전을 명시해두었습니다.

module.exports = {
    ...
  devServer: {
    static: "./dist",
    hot: true,
    open: true,
  },
};
  • static: webpack-dev-server이 명시한 디렉터리의 파일을 localhost:8080에서 실행합니다.
  • open: 서버가 시작된 후 브라우저를 열어줍니다.
  • hot(hot module replacement): 수정된 부분만 바뀌고, 입력해놓은 정보들은 유지되는 기능입니다.(최적화)

webpack-dev-server 를 실행하려면, npx webpack serve 명령어를 이용하면 됩니다.

여기서 package.json을 다시보면,

"scripts": {
    "watch": "webpack --watch",
    "start": "webpack serve",
    "build": "webpack --mode=production",
    ...
  },

우리는 미션 프로젝트를 실행할 때 npm run start 을 이용했죠.

webpack-dev-server를 이용한 것임을 알 수 있습니다!

webpack-dev-server에 대한 더 자세한 옵션이나 설명은 docs 에서 보실 수 있습니다.

이어서 학습하면 좋은 부분

Code SplittingLazy loading: 커진 번들러 파일을 분리하고, 필요한 모듈만 불러오는 방법을 알아봅시다.

마무리

잘 모르고 그 존재만 알았던 웹팩, 정리를 하고나니 어떻게 프로젝트가 설정되었는지 이해할 수 있었어요.

웹팩은 아주 방대하니 시간을 두고 꾸준히 알아가야할 것 같습니다!

Reference

https://webpack.kr/concepts/

https://www.youtube.com/watch?v=dt2xU71pX88&list=PLuHgQVnccGMChcT9IKopFDoAIoTA-03DA&index=7