Webpack

나의 개발 일지·2022년 11월 23일
0

기왕이면 한 번에 받는게 낫잖아요?
웹팩을 통해 모듈로 분리된 코드를 한 번에 최적화하여 배포하기

출처 : https://devindetails.com/react-development-setup/

시작하기 전에,

개발자라면 누구나 프로그래밍 언어를 처음 배우고 'Hello World!' 를 출력하는 시절을 기억할 것이다. 개발을 처음 접한 그 시절, 자그마한 기능을 가진 어플리케이션을 만들기 위해 파일을 하나 만들어서 필요한 로직들을 모두 집어 넣어 작동시켜 보고 뿌듯했던 경험은 모두가 가지고 있을 것이다. 그러나 점차 몸집이 커져가는 어플리케이션을 만들기 시작하면서, 파일 하나에 모든 로직을 집어넣어서 작성하는 것이 얼마나 가독성이 떨어지며 유지 보수하기가 힘든 일인지 깨닫게 된다. 이제, 코드의 모듈(module)화라는 새로운 세상에 눈을 뜨게 된다. 점차 코드의 로직을 분리하여 작성하는 것에 익숙해지고,모듈을 자신의 코드로 불러와 사용하는 것이 전혀 어색하게 느껴지지 않는 단계에 도착한다. 이제 어느 정도 규모가 있는 어플리케이션을 만들 수 있게 되었다. 내가 만든 어플리케이션을 배포해 세상과 공유하고 싶다는 생각이 든다. 그런데... 번들링? 내가 열심히 모듈화해 놓은 코드들을 배포하기 위해서는 다시 모두 하나로 합쳐야 한다고? 그 시절 끔찍했던 내 코드처럼 다시 작성해야 한다고?

웹팩(Webpack) 과 번들링

내 어플리케이션을 배포하고 싶은 단계에 도착한 우리, 번들링에 대해 이해해야한다. 번들링이란, 간단히 모듈화로 작성된 코드들을 하나의 파일로 다시 합쳐 배포와 네트워크 통신에 적절하게 최적화 시키는 과정이다. 그러나 겁먹지말자. 이러한 번들링 과정을 우리가 처음에 염두에 두고 코드를 작성하거나, 배포하기 직전 직접 우리 스스로 합쳐야하는 것은 아니다. 이러한 번들링 과정을 대신, 해주는 고마운 라이브러리가 존재하기 때문이다. 바로 웹팩(Webpack) 이라는 라이브러리이다.

웹팩(Webpack)

웹팩의 개념

이제 웹팩이라는 것이 존재한다는 사실을 알았으니, 웹팩을 이해하고 사용할 준비를 시작해야한다. 웹팩은 우리의 코드를 번들링을 할 때, 우리의 여러 갈래로 분할 된 코드의 입구와 출구 (입구와 출구는 사용자가 직접 작성해 주어야한다.) 를 살펴보고 그 사이에 서로 연결되어있는 모든 우리의 코드 모듈들을 하나로 합쳐 압축시켜 우리가 지정해준 입구 파일에 모두 합쳐준 (번들링된) 파일을 출구의 디렉토리에 생성해준다. 이제 웹팩을 통해 배포에 필요한 번들링 파일을 생성했으니, 이를 통해 우리는 효과적으로 우리의 애플리케이션을 배포할 수 있는 것이다.

! 웹팩의 개념은 단순히 이와 같지만, 활용하는 방법과 웹팩 라이브러리가 제공하는 기능들은 매우 다양하고 심도 있는 이해가 필요하다. 전부는 아니지만, 대략적인 기능을 웹팩의 사용 단락에서 살펴보자 !

웹팩의 사용

( 참고 ! 한국어로 작성되어 있는 웹팩 러닝 가이드 홈페이지 : https://yamoo9.gitbook.io/webpack/,
웹팩 공식 도큐멘트 (한국어) :
https://webpack.kr/concepts/ )

그래서 웹팩을 어떻게 사용하는 건데?

좋다. 웹팩은 외부 라이브러리이니 웹팩을 사용하기 위해서는 우리의 프로젝트 디렉토리에 웹팩을 설치 해주어야 한다.

$ npm i -d webpack webpack-cli

# 웹펙을 cli로 작동시키기 위해서 webpack-cli 라이브러리도 같이 인스톨한다. 
# 디펜던시에 추가하기 위해서 -d 플래그도 같이 삽입한다.

(만약 npm package를 작성하지 않은 디렉토리라면 '$ npm init-y' 커맨드를 통해서 package.json 파일을 초기 설정한다. )

웹팩의 인스톨이 완료되었다면, 이제 웹팩이 정상적으로 작동할 수 있도록 웹팩의 설정환경을 자바스크립트 파일로 새로 작성해주어야 한다. 이 자바스크립트 파일의 이름은 webpack.config.js 이다. 이 자바스크립트 파일에 우리가 필요한 웹팩의 설정 파일을 작성한다.

웹팩을 사용하기 위한 가장 기초적인 입구인 entry point와 출구인 output만 작성해준 webpack.config.js 파일의 기본적인 모습은 다음과 같다.

// in webpack.config.js

const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js',
  },
};

먼저, 웹팩의 config 객체를 모듈화에서 웹팩 라이브러리에서 사용하기 위해 다음과 같이 export 해준다. 그 후, config 객체를 작성하기 시작하면 된다. entry 프로퍼티에는 우리가 입구 파일 (번들링을 시작할 파일, 이 파일을 시작으로 연결된 모든 모듈들을 웹팩이 추적하여 번들링한다.)로 사용할 파일의 주소 값을 입력한다. 그 다음, output 프로퍼티에는 객체를 할당한다. 이 output 프로퍼티의 객체에는 path, filename 두 가지 속성을 가지는데, path 에는 번들링 파일이 생성될 절대 주소 값을 node의 path 모듈을 통해 작성해준다. 이후 filename에는 번들링 파일의 이름을 직접 설정해줄 수 있다.

이렇게, 웹팩 가장 기본적인 config을 완료해주면 웹팩을 통해 번들링을 사용할 가장 기본적인 준비가 끝난 것이다. 그런데 번들링을 시작하려면 어떻게 해야할까?

여기서 우리가 설치해준 'webpack-cli' 모듈을 사용하게 된다. 터미널에 다음과 같은 커맨드를 입력해 번들링 파일을 생성하자.

$ npx webpack

# webpack-cli 를 통해 다음과 같은 커맨드를 사용할 수 있게 되었다.
# 이후 package.json의 커스텀 script를 작성함으로써 
# 이 커맨드를 실행시키는 다른 커맨드를 작성할 수도 있다.

이제 현재 프로젝트 디렉토리의 dist 디렉토리로 가보면 웹팩이 번들링한 파일을 발견할 수 있을 것이다 (파일 내부에는 한 줄로 이루어진, 알아보기 힘든 코드가 작성되어 있는데 웹팩이 우리의 모듈화된 코드를 모두 읽어 들인뒤 압축하고 외부인이 변형하기 어렵도록 변형한 것이다.) . 이를 통해 배포에 사용하면 앞서 설명한 가장 기본적인 방식의 웹팩 사용방식을 이용할 수 있다.

웹팩의 활용

생각보다 어렵진 않은데... 이게 끝은 아니겠죠?

당연히 웹팩은 효율적으로 사용자의 코드에 맞게 활용하기 위한 다양한 옵션 들과 개발 경험을 증진시키기 위한 다양한 부가 기능들을 제공한다. 이중 몇 가지를 추려서 살펴보도록 하자.

1) 로더(Loader)

로더는 웹팩이 번들링을 진행할 때, js파일 뿐만이 아니라 import 하게 되는 다양한 파일들(css, jsx, img, png... 등)을 웹팩이 함께 번들링 할 수 있도록 진행해주는 옵션이다. 같이 번들링하게 하고자 하는 파일들에 따라 웹팩의 로더는 달라질 수 있다. 예시를 통해 살펴볼 가장 기본적인 웹팩의 로더로는 css와 js를 함께 번들링 할 수 있도록 만들어 주는 'css-loader'와 'style-loader'이다.

웹팩의 로더들은 대부분 웹팩의 기본 모듈에 포함되지 않고 외부 라이브러리로 존재하는 경우가 많기 때문에 이를 사용하기 위해서 해당 로더들을 npm 인스톨 해야 한다.

$ npm i --save-dev style-loader css-loader

# style-loader : DOM에 스타일로 모듈 내보내기를 추가 
# css-loader : 리졸브된 가져오기로 CSS 파일을 로드하고 CSS 코드를 반환

이렇게 인스톨한 로더 모듈들을 웹팩에서 사용하기 위해 이제 웹팩의 config 파일을 수정해주도록 하자.

module.exports = {
  entry : './src/index.js'
  ...
  module: {
    rules: [
      {
        test: /\.css$/i,
  		exclude : /node_modules/
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

웹팩의 config 객체의 하나의 프로퍼티로 module을 지정해준 후 module을 지정해줄 객체를 다음과 같이 할당한다. module안에 rules 배열 안에 사용하고자 하는 모듈들을 나열해 할당할 수 있다. test 프로퍼티는 정규표현식을 할당해 해당 로더를 거치게 될 파일들을 지정해줄 수 있다. exclude 프로퍼티는 로더가 파일을 추적할 때 제외시키고 싶은 디렉토리명을 지정해줄 수 있다. use 프로퍼티에는 문자열 또는 배열로 로더들을 지정해줄 수 있다. (이때, 배열을 지정할 경우 배열의 가장 마지막부터 처음으로 역순으로 로더가 실행된다.)

! 웹팩을 리엑트에 적용하기 !
리액트는 자신만의 문법(jsx)를 사용하기 때문에 웹팩은 이를 스스로 파싱해서 번들링할 수 없다. 이를 해결해주기 위해서는 새로운 로더인 'babel-loader'가 필요하다. ( babel은 다양한 형식의 문법들을 일반적인 javascript 문법으로 (폴리필을 위한) 변환해주는 라이브러리이다. ) 바벨을 통해 리액트를 웹팩으로 번들링 하기 위해서는 다음과 같은 패키지들이 필요하다.

$ npm i --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader

# 각각 바벨의 중요기능을 사용하기 위한 패키지들이다.

패키지들이 준비가 되었다면, 이제 다시 config 파일로 돌아가 babel-loader를 적용시켜주도록 하자.

module.exports = {
  entry : './src/index.js'
  ...
  module: {
    rules: [
      {
        test: /\.(js|jsx$)/i,
  		exclude : /node_modules/
        use: ["babel-loader"],
      },
    ],
  },
};

그러나 이를 통해 번들링을 하기에는 아직 부족하다. 바벨에 대한 설정을 작성해주지 않았기 때문이다. 바벨의 config 또한 다음과 같이 설정하여 끝마치도록하자.

// .babelrc 파일로 config 할 경우

{
  "presets": [
    "@babel/preset-env",
    ["@babel/preset-react",
    {"runtime": "automatic"}]
  ]
}

// babel.config.js로 config 할 경우
module.exports = {
  "presets": [
    "@babel/preset-env",
    ["@babel/preset-react",
    {"runtime": "automatic"}]
  ]
}

사용할 바벨의 프리셋들을 지정해주는 작업이다. 이때 문자열이 아니라 배열을 통해 옵션을 할당해줄 수도 있는데, runtime 옵션을 반드시 automatic 으로 지정해주어야 import가 포함된 파일을 번들링해도 제대로 작동시킬 수 있다.

2 ) 모드 (Mode)

웹팩에는 개발상황에 따라서 번들링 방식의 모드를 지원한다. 개발 중인 상태에서 빠르게 번들링 하기 위한 'development' 모드, 개발이 완료되어 배포를 위해 번들링 하기 위한 'production' 모드 두 가지를 지원한다. 웹팩의 mode는 config 객체의 안에 직접적으로 지정해줄 수도 있고, 번들링을 실행할 때 커맨드의 플래그를 통해 지정해줄 수도 있으며, 이에 따라 동적으로 웹팩의 번들링 모드를 변경할 수 있도록 설정해줄 수도 있다.

(모드를 동적으로 분리하여 배포 환경에 맞는 번들링 옵션을 지정해줄 수 있다.)

module.exports = {
  ...
  mode: 'development' || 'production', // 지정해주는 값에 따라 모드를 바꿀 수 있다.
};
$ webpack --mode=development || production
# 다음과 같이 터미널에서 모드를 지정해 줄수도 있다.

다음과 같이 전달되는 모드에 따라서 동적으로 웹팩이 작동하게 만들 수 있다.

const config = {
  entry: './app.js',
  //...
};

module.exports = (env, argv) => {
  if (argv.mode === 'development') {
    config.devtool = 'source-map';
  }

  if (argv.mode === 'production') {
    //...
  }

  return config;
};

3) 플러그인 (Plugin)

웹팩은 로더 뿐만이 아니라 플러그인을 지원해 더욱 더 다양한 방식으로 번들링을 수행할 수 있도록 도와준다. 웹팩에서 사용하고 있는 플러그인은 다음의 주소에서 찾을 수 있다. https://webpack.kr/plugins/#root

지금은 HTML 파일을 자동으로 같이 번들링 과정에 포함시켜 압축해주고 번들링한 청크파일을 자동으로 HTML 파일에 연결해주는 HtmlWebpackPlugin 에 통해 플러그인 사용법을 살펴보자.

먼저, HtmlWebpackPlugin 패키지를 인스톨한다.

$ npm install --save-dev html-webpack-plugin

인스톨된 웹팩 플러그인을 config 파일 안에서 사용할 수 있도록 지정해주자.

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

module.exports = {
  entry: 'index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js',
  },
  plugins: [new HtmlWebpackPlugin(
    {template : path.resolve(__dirname, "public", "index.html"), 
     favicon : "./public/favicon.ico"})],
};

코드에서 살펴볼 수 있듯이, 플러그인을 웹팩에 적용시키기 위해서는 loader와는 다른 방식인 인스턴스를 생성해서 지정해주어야 한다. 따라서 해당 클래스를 config 파일로 불러와 plugin 배열 내에서 new 생성자와 함께 필요한 옵션 객체를 전달해서 생성하면 된다.

(플러그인에 적용된 template 옵션은 해당 디렉토리에 존재하는 파일을 통해 번들링을 진행한다는 이야기이며, favicon 또한 디렉토리를 지정해줌으로써 해당 favicon을 번들링된 html에 추가해줄 수 있다.)

웹팩의 플러그인은 express의 미들웨어와 같이 번들링 과정에서 수행할 추가적인 작업들을 위해 다양하게 지정해줄 수 있다.

4) DevServer

마지막으로 (다른 기능들이 더 존재하지만 소개는 여기까지! ) 웹팩에서는 번들링한 파일을 생성하여 자동으로 임시 서버를 개설해 그 번들링한 정적 파일을 직접적으로 호스팅하여 줄 수 있는 서버 옵션을 제공한다.

const path = require('path');

module.exports = {
  //...
  devServer: {
    static: {
      directory: path.join(__dirname, 'public'),
    },
    compress: true,
    port: 9000,
  },
};

static 옵션을 통해서 서브할 정적파일의 주소를 지정해줄 수 있으며, server의 port 옵션 등을 따로 더 지정해줄 수 있다.


profile
반갑습니다! 오늘도 좋은 하루 입니다🙋🏻‍♂️. 프론트엔드 개발을 공부하고 있습니다!

0개의 댓글