[Frontend] Webpack

2021. 5. 2. 20:30Frontend

※ 이 글은 캡틴판교님의 인프런 강좌 ’프론트엔드 개발자를 위한 웹팩’을 수강한 후 공부한 내용을 정리한 글입니다. 웹팩 버전은 4.42.0 버전 기준입니다.

Webpack

웹팩은 현재 가장 많이 사용되는 모듈 번들러입니다. 모듈 번들러는 웹 애플리케이션을 구성하는 HTML, CSS, JavaScript, Images, Fonts 등 유기적인 각각의 모듈들을 파싱하여 하나의 파일로 만들어주는 도구를 말합니다.

필요성

1. 파일 단위의 자바스크립트 모듈 관리

자바스크립트에서 var로 선언한 변수는 기본적으로 전역에서 접근할 수 있습니다. 그래서 어디서든 접근할 수 있다는 점이 특징이죠. 하지만 이는 많은 문제점을 야기합니다. 특히 여러 모듈로 분리된 상황에서는요. 만약 다음과 같은 두 모듈이 있다고 가정해봅시다.

// num.js
var num = 10;
function getNum() {
  return num;
}
// sum.js
var num = 20;
function getSum(a) {
  return a + num;
}

두 모듈을 script 태그로 가져온 뒤 계산을 해볼까요?

<html>
  <body>
    <script src="num.js"></script>
    <script src="sum.js"></script>
    <script>      
      console.log(getNum());
    </script>
  </body>
</html>

결과는 어떨까요? num.js의 getNum을 사용했으니 10을 출력하기를 기대하지만 결과는 20이 출력됩니다. num이 전역 변수이기 때문에 나중에 선언된 20을 가진 num이 출력됩니다. 지금은 파일이 두 개밖에 없지만 만약 큰 규모의 프로젝트에서 여러 사람들이 여러 모듈을 분리하여 개발한다면 어떨까요? 그 중에 겹치는 변수명이 없을거라고 장담하지 못합니다. 이러한 문제를 웹팩은 각 모듈별로 분리하여 처리하여 해결합니다.

2. 웹 개발 자동화 도구

웹 개발 자동화 도구 이전에는 파일을 수정하고, 브라우저로 가서 F5를 눌러 새로고침해야 했습니다. 또한 웹 서비스를 개발하고 배포할 땐 각종 압축과 같은 최적화 과정을 거쳐야 했죠. 웹팩은 이 과정들을 모두 포함하여 처리합니다.

3. 웹 애플리케이션의 빠른 로딩 속도와 성능

보통 사람들은 웹 페이지를 클릭하고 3초 이내에 화면이 나오지 않으면 나간다고 합니다. 이는 회사의 입장에서 고객을 잃는 치명적인 문제입니다. 최근 웹에서 제공하는 서비스의 양이 많아짐에 따라 로딩 속도를 줄이려는 노력이 있었습니다. 대표적인 것이 파일의 개수를 줄여 HTTP Request의 수를 줄이는 것이죠. 또한 첫 페이지에 모든 자원들을 로드하는 게 아니라 요청에 따라 동적으로 로딩하는 Lazy Loading이 등장했습니다. 웹팩은 이러한 기능마저 지원합니다.

해결하려는 문제

1. 자바스크립트 변수의 유효 범위 문제

2. 브라우저별 HTTP 요청 개수의 제약

클라이언트와 서버가 통신하려면 HTTP 이전에 TCP 연결이 우선 연결되어야 합니다. 그리고 TCP 스펙에 따라 HTTP 요청 개수가 정해지죠. 그런데 이때 브라우저별로 HTTP 요청에 대한 제약이 있습니다. 이에 HTTP 요청의 개수를 줄여 최대한 빠르게 페이지를 로딩하기 위한 해결책으로 웹팩이 개발되었습니다. 번들링을 통해 파일의 개수를 줄이면 HTTP request 개수를 줄일 수 있습니다.

3. 사용하지 않는 코드의 관리

4. Dynamic Loading & Lazy Loading 미지원

특별한 라이브러리 없이는 동적으로 원하는 순간에 모듈을 불러오는 것이 불가능합니다. 웹팩의 Code Splitting 기능을 활용하면 해결할 수 있습니다.

웹팩의 주요 기능

entry

웹팩에 웹 애플리케이션에 대한 진입점을 지정하는 부분입니다.

var path = require('path');
module.exports = {
  // ...
  entry: './index.js',
  // index.js를 통해 웹팩이 코드를 읽어나갑니다.
  // ...
};

output

빌드된 파일을 어떻게 내보낼지 지정하는 부분입니다.

var path = require('path');
module.exports = {
  // ...
  output: {
    filename: 'bundle.js',
    // 빌드된(번들링된) 파일의 결과는 bundle.js 입니다.
    path: path.resolve(__dirname, 'dist'),
    // bundle.js의 저장 위치는 dist 폴더입니다.
  },
  // ...
};

module

웹팩은 오직 JavaScript 파일과 JSON 파일만을 읽을 수 있습니다. 모듈에서는 loaders를 통해 웹팩이 다른 확장자를 가진 파일들을 처리할 수 있도록 해줍니다.

var path = require('path');
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },    
    ],
  },
  // ...
};

css 파일은 ’css-loader’를 통해 웹팩이 읽을 수 있게 해주고, ’style-loader’를 통해 파싱된 결과를 html 안의 인라인 스타일로 넣어줍니다. use에서 loader를 읽는 순서는 오른쪽에서 왼쪽이므로, style-loader와 css-loader의 순서가 바뀌면 웹팩은 오류를 냅니다. 따라서 loader를 적용할 땐 순서에 유의하여 입력해야 합니다.

plugins

플러그인은 웹팩의 기본적인 동작에 추가적인 기능을 제공하는 프로퍼티입니다. 로더는 파일을 파싱하는 과정에 관여하는 반면에 플러그인은 해당 결과물의 형태를 바꾸는 역할을 한다고 보면 됩니다.

var path = require('path');
var MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  mode: 'none',
  entry: './index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [{ loader: MiniCssExtractPlugin.loader }, 'css-loader'],
      },
    ],
  },
  // 파싱된 css 파일을 output 폴더에 추가하는 plugin입니다.
  plugins: [new MiniCssExtractPlugin()],
};

plugin은 배열 내부에서 익명 인스턴스 생성 방식으로 작성해야 합니다.

Conclusion

간략하게 웹팩에 대해 알아보았습니다. 웹팩은 현재 프론트엔드 개발 환경에서 빠질 수 없는 위치에 올라왔습니다. 채용 조건에도 웹팩에 대한 이해를 요구하는 회사도 종종 보입니다. CRA와 같이 자동으로 웹팩 코드를 만들어주는 해주는 고마운 기능들도 있지만, 결국 개발자라면 내부에서 어떻게 돌아가는지 이해하는 것을 생각해야 합니다. 웹팩에 대해 좋은 강의를 제공해주신 캡틴판교님께 감사하다는 말씀 드립니다.