웹팩과 모듈(Webpack & Module)

sooyuni·2022년 9월 5일
1
post-thumbnail

🔅Webpack이란?

웹팩이란 최신 프런트엔드 프레임워크에서 가장 많이 사용되는 모듈 번들러(Module Bundler)이다.

모듈 번들러(Module Bundler)란 웹 애플리케이션을 구성하는 자원
(HTML, CSS, Javscript, Images 등)을 모두 각각의 모듈로 보고 이를 조합해서 병합된 하나의 결과물을 만드는 도구를 의미한다.

🔅모듈이란?

  • 모듈(Module) : 모듈이란 프로그래밍 관점에서 특정 기능을 갖는 작은 코드 단위를 의미한다. 자바스크립트로 치면 아래와 같은 코드가 모듈이다.
  1. 두 숫자의 합을 구하는 sum() 함수
  2. 두 숫자의 차를 구하는 substract() 함수
  3. 원주율 값을 갖는 pi 상수
    이처럼 성격이 비슷한 기능들을 하나의 의미 있는 파일로 관리하면 모듈이 된다.

🔅웹팩에서의 모듈이란?

웹팩에서 지칭하는 모듈이라는 개념은 위와 같이 자바스크립트 모듈에만 국한되지 않고 웹 애플리케이션을 구성하는 모든 자원을 의미한다.
웹 애플리케이션을 제작하려면 HTML, CSS, Javascript, Images, Font 등 많은 파일들이 필요한데. 이 파일 하나하나가 모두 모듈이다.

🔅모듈 번들링이란?

웹 애플리케이션을 구성하는 몇십, 몇백개의 자원들을 하나의 파일로 병합 및 압축 해주는 동작을 모듈 번들링이라고 한다.

🔅웹팩을 사용하는 이유

웹팩을 사용해야 하는 이유를 이해하기 위해선 번들러가 존재하기 전,
웹에서 JavaScript를 어떻게 사용했는지를 알아야 한다.

브라우저에서 자바스크립트가 동작하는 두 가지 방법이 있다.
1. 각 기능이 있는 스크립트를 추가한다.
이 방법은 너무 많은 스크립트로 인해 네트워크의 병목을 유발하는 원인이 될 수 있어서 확장이 어렵다는 단점이 있다.
2. 모든 프로젝트에 하나의 거대한 .js 파일을 만들어 사용한다.
그러나 이것은 유효 범위와 크기, 가독성, 유지보수에 문제를 발생시킬 수 있다는 단점이 있다.

1. IIFEs - 즉시 호출되는 함수 표현식 (Immediately invoked function expressions)

IIFE는 대규모 프로젝트에서 유효범위 문제를 해결한다. 스크립트 파일을 IIFE로 감싸면 유효범위에 대한 걱정 없이 파일을 안전하게 연결하거나 결합 할 수 있다.

IIFE의 사용은 Make, Gulp, Grunt, Broccoli, Brunch와 같은 툴과 연관이 있다. 이 툴들은 태스크 러너라고 하며, 모든 프로젝트 파일들을 함께 연결한다.

그러나 하나의 파일을 변경해도 전체를 다시 빌드해야 한다는 단점이 있다. 연결하면 파일 간의 스크립트를 쉽게 재사용 할 수 있지만, 빌드 최적화를 더욱더 어렵게 했다. 코드가 실제로 사용되고 있는지 어떻게 알 수 있을까?

만약 lodash에서 하나의 함수만 사용해도, 전체 라이브러리를 추가하고 모든 것을 뭉쳐야 한다. 코드의 의존성을 어떻게 treeshaking(가지치기) 할까? 지연 로딩 청크 코드는 확장이 어렵고 개발자의 많은 수동 작업을 필요로 한다.

2. Node.js로 인한 JavaScript 모듈의 탄생

Webpack은 브라우저 외부 환경의 서버나 컴퓨터에서 사용할 수 있는 JavaScript 런타임인 Node.js에서 동작한다.

현재 JavaScript는 브라우저에서 동작하지 않는데 어떻게 Node 애플리케이션은 새로운 코드 청크를 불러오는 걸까?

바로 require를 도입한 CommonJS가 출시되었는데 이는 현재 파일에서 모듈을 불러오고 사용할 수 있다. 이것은 필요한 곳에 모듈을 가져와 별도의 구성 없이 바로 사용할 수 있도록 하여 유효범위 문제를 해결했다.

3. npm + Node.js + Module = 대량 배포

JavaScript는 언어로서, 플랫폼으로서, 빠른 개발 방식, 빠른 애플리케이션을 만드는 방법으로 세계적으로 사용되고 있는데, 그러나 CommonJS에 대한 브라우저의 지원은 없었다. 라이브 바인딩 또한 없었다. 순환 참조의 문제가 있었고, 동기적인 모듈 해석과 로딩이 느렸다. CommonJS가 Node.js 프로젝트에서는 뛰어난 솔루션이었지만 브라우저는 모듈을 지원하지 않았다. 때문에 브라우저에서 CommonJS의 실행을 가능하게 하는 Browserify와 RequireJS, SystemJS 같은 번들러와 툴들이 만들어졌다.

4. ESM - ECMAScript Modules

모듈이 ECMAScript 표준에서 공식 기능이 되고 있다.
그러나 브라우저 지원은 불완전하고, 번들링이 여전히 더 빠르기 때문에 현재 초기 구현 모듈보다 권장되고 있다.

5. Automatic Dependency Collection(자동 의존성 선언)

구식의 태스크 러너와 Google Closure Compiler 조차 모든 의존성을 미리 수동으로 선언해야 한다. Webpack 같은 번들러는 자동으로 빌드하고, 가져오거나 내보낸 항목을 기반으로 디펜던시 그래프를 추론한다. 이는 다른 플러그인과 로더와 함께 훌륭한 개발자 경험을 제공한다.

이와 같이, 모듈을 작성할 수도 있고, 어떠한 포맷의 모듈 (적어도 ESM에 도달하기 전까지)도 지원하며, 리소스와 애셋을 동시에 처리 가능한 것이 있으면 좋지 않을까?
웹팩 공식문서에서 설명하는 webpack이 존재하는 이유는 아래와 같다. "Webpack은 성능과 로딩 시간을 중요하게 생각하며, JavaScript 애플리케이션을 번들로 묶을 수 있는(ESM과 CommonJS 모두 지원) 도구이며, 이미지나 폰트, 스타일 시트 같은 다양한 애셋을 지원하도록 확장할 수 있도록 한다."

🔅Webpack의 네가지 주요 속성

웹팩의 빌드(파일 변환) 과정을 이해하기 위해서는 아래 4가지 주요 속성에 대해서 알고 있어야 한다.

  • entry
  • output
  • loader
  • plugin
    각 주요 속성에 대해 자세히 살펴보자.

🌟 Entry

엔트리 포인트는 webpack이 내부의 Dependency graph를 생성하기 위해 사용해야 하는 모듈이다. Webpack은 엔트리 포인트가 (직간접적으로) 의존하는 다른 모듈과 라이브러리를 찾아낸다.

기본값은 ./src/index.js이지만, webpack 설정에서 entry 속성을 설정하여 다른 (또는 여러 엔트리 포인트)를 지정할 수 있습니다. 예를 들어보겠습니다.

webpack.config.js

module.exports = {
  entry: './path/to/my/entry/file.js',
};

🌟 Dependency Graph
아래와 같이 모듈 간의 의존 관계가 생기는 구조를 의존성 그래프(Dependency Graph)라고 한다.
webpack은 애플리케이션에 필요한 모든 모듈을 포함하는 의존성 그래프 를 재귀적으로 빌드한 다음 이러한 모든 모듈을 브라우저에서 로드할 소수의 번들 (종종 단 하나)로 번들링한다.

🌟 Entry 유형
앞에서 살펴본 것처럼 엔트리 포인트는 1개가 될 수도 있지만 아래와 같이 여러 개가 될 수도 있다.

entry: {
  login: './src/LoginView.js',
  main: './src/MainView.js'
}

위와 같이 엔트리 포인트를 분리하는 경우는 싱글 페이지 애플리케이션이 아닌 특정 페이지로 진입했을 때 서버에서 해당 정보를 내려주는 형태의 멀티 페이지 애플리케이션에 적합하다.

🌟 Output

output 속성은 생성된 번들을 내보낼 위치와 이 파일의 이름을 지정하는 방법을 webpack에 알려주는 역할을 한다. 기본 출력 파일의 경우에는 ./dist/main.js로 , 생성된 기타 파일의 경우에는 ./dist 폴더로 설정된다. 다음과 같이 설정에서 output 필드를 지정할 수 있다.

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',
  },
};

위의 예제에서, output.filenameoutput.path 속성을 사용하여 webpack에 번들의 이름과 내보낼 위치를 알려주었다. 상단에서 가져오는 path 모듈이 무엇인지 궁금할 수 있는데, 이것은 파일 경로를 지정하기 위해 사용되는 core Node.js 모듈이다.

🌟 Loaders

webpack은 기본적으로 JavaScript와 JSON 파일만 이해한다. 로더를 사용하면 webpack이 다른 유형의 파일을 처리하거나, 그들을 유효한 모듈로 변환하여 애플리케이션에서 사용하거나, 의존성 그래프에 추가한다.

상위 수준에서 로더는 webpack 설정에 두 가지 속성을 가진다.
변환이 필요한 파일(들)을 식별하는 test 속성
변환을 수행하는데 사용되는 로더를 가리키는 use 속성

webpack.config.js

const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js',
  },
  module: {
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
};

위 설정에서는 test와 use라는 두 가지 필수 속성을 가진 하나의 모듈을 위해 rules 속성을 정의했다. 이는 webpack의 컴파일러에 다음과 같이 말한다.

"이봐 webpack 컴파일러, require ()/import 문 내에서 '.txt' 파일로 확인되는 경로를 발견하면 번들에 추가하기 전에 raw-loader를 사용하여 변환해."

🌟 Plugins

로더는 특정 유형의 모듈을 변환하는 데 사용되지만, 플러그인을 활용하여 번들을 최적화하거나, 애셋을 관리하고, 또 환경 변수 주입등과 같은 광범위한 작업을 수행 할 수 있다.

플러그인을 사용하려면 require ()를 통해 플러그인을 요청하고 plugins 배열에 추가해야 한다. 대부분의 플러그인은 옵션을 통해 사용자가 지정할 수 있다. 다른 목적으로 플러그인을 여러 번 사용하도록 설정할 수 있으므로 new 연산자로 호출하여 플러그인의 인스턴스를 만들어야 한다.

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 내장 plugin에 접근하는 데 사용

module.exports = {
  module: {
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
  plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};

위의 예제에서 html-webpack-plugin은 생성된 모든 번들을 자동으로 삽입하여 애플리케이션용 HTML 파일을 생성한다.

🔅 주요 속성 리뷰

여태까지 살펴본 웹팩 4가지 주요 속성을 도식으로 나타내보면 다음과 같다.

  • Entry 속성은 웹팩을 실행할 대상 파일. 진입점
  • Output 속성은 웹팩의 결과물에 대한 정보를 입력하는 속성. 일반적으로 filename과 path를 정의
  • Loader 속성은 CSS, 이미지와 같은 비 자바스크립트 파일을 웹팩이 인식할 수 있게 추가하는 속성. 로더는 오른쪽에서 왼쪽 순으로 적용
  • Plugin 속성은 웹팩으로 변환한 파일에 추가적인 기능을 더하고 싶을 때 사용하는 속성. 웹팩 변환 과정 전반에 대한 제어권을 갖고 있음

💡참고 자료

https://webpack.js.org/ 웹팩 공식 문서
https://joshua1988.github.io/webpack-guide/ 웹팩 핸드북

profile
기록하고, 기록하자. 남는건 기록 뿐📝

0개의 댓글