실전 리액트 프로그래밍 개정판 - 이재승 지음 (참고자료)
지난번에는 바벨을 좀 깊이 공부했지만 이제 웹팩을 조금 깊이 알아볼까 한다.
웹팩은 모듈(module) 번들러(bundler)다.
여기서 모듈은 각 리소스 파일을 말하고, 번들은 웹팩 실행 후에 나오는 결과 파일이다. 하나의 번들 파일은 여러 모듈로 만들어진다.
웹팩을 사용하면 제작한 여러 가지 리소스를 사용자에게 전달하기 좋은 형태로 만들 수 있다.
지난 블로깅에도 언급했지만 한번 더 작성하고자 한다.
2000년대 초반의 웹 페이지는 페이지가 바뀔 때마다 새로운 HTML을 요청해서 화면을 매번 그리는 방식이었다. 자바스크립트는 돔을 조작하는 간단한 역할만 했기 때문에 HTML의 script 태그에 넣는 것으로 충분했다. Ajax가 유행했을 때는 자바스크립트의 비중이 조금 더 커졌지만 그래 봐야 페이지당 자바스크립트 파일 몇 개면 충분했다. 그러나 단일 페이지 애플리케이션은 하나의 HTML에 수십, 수백 개의 자바스크립트 파일을 포함하기 때문에 더는 기존 방식이 통할리 없다.
<html>
<head>
<script src="javascript_1.js"></script>
<script src="javascript_2.js"></script>
//...
<script src="javascript_999.js"></script>
</head>
<body>
// ...
</body>
</html>
이런 식으로는 계속 늘어나는 자바스크립트 파일을 관리하기 힘들다. 또한 실행되는 순서도 신경 써야 하고, 기존에 생성된 전역 변수를 덮어쓰는 위험도 존재한다.
웹팩을 이용하면 이러한 문제들을 해결할 수 있다.
프로젝트 하나를 만들어 보자.
mkdir webpack-init
cd webpack-init
npm init -y
npm install webpack webpack-cli
webpack-cli
를 이용하면 CLI(command line interface)
에서 웹팩을 실행할 수 있다.
프로젝트 루트에 src 폴더를 만들고 그 밑에 util.js 파일을 만들고 다음처럼 작성하자.
그 다음에는 src 폴더 밑에 index.js 파일을 만들고 util.js 모듈의 sayHello 함수를 사용하는 코드를 작성한다.
이제 웹팩을 실행해 보자.
npx webpack
웹팩을 실행하면 dist 폴더가 만들어지고 그 밑에 main.js 번들 파일이 생성된다. index.js 모듈과 util.js 모듈이 main.js 번들 파일로 합쳐졌다. 별다른 설정 없이 웹팩을 실행하면 ./src/index.js
모듈을 입력으로 받아서 ./dist/main.js
번들 파일을 만든다.
프로젝트 루트에 webpack.config.js 파일을 만들고, 다음 코드를 입력한다.
번들 파일의 내용을 쉽게 확인하기 위해 압축하지 않도록 설정했다.
이제 웹팩을 실행해 보자.
npx webpack
책에서와는 다른 결과가 나왔는데 아마 웹팩 버전이 5라서 다른걸로 예상한다.
로더(loader)는 모듈을 입력으로 받아서 원하는 형태로 변환한 후 새로운 모듈을 출력해 주는 함수다. 자바스크립트 파일뿐만 아니라 이미지 파일, CSS 파일, CSV 파일 등 모든 파일은 모듈이 될 수 있다.
실습을 위한 프로젝트를 생성해보자.
mkdir webpack-loader
cd webpack-loader
npm init -y
npm install webpack webpack-cli
가장 먼저 자바스크립트 파일을 처리하는 babel-loader를 알아보자. 다음과 같이 필요한 패키지를 설치한다.
npm install babel-loader @babel/core @babel/preset-react react react-dom
자바스크립트 코드에서 JSX 문법으로 작성된 리액트 코드를 처리하기 위해 필요한 패키지들이다.
프로젝트 루트에 src 폴더를 만들고 그 밑에 index.js 파일을 만들고 다음과 같이 JSX 문법을 사용하는 코드를 입력한다.
프로젝트 루트에 babel.config.js 파일을 만들고, 다음과 같이 @babel/preset-react 프리셋을 사용하도록 설정한다.
프로젝트 루트에 webpack.config.js 파일을 만들고 다음과 같이 babel-loader를 설정해 보자.
module: {...}
: js 확장자를 갖는 모듈은 babel-loader가 처리하도록 설정한다.웹팩을 실행해 보면 dist 폴더 밑에 main.js 파일이 생성된다. 잘 동작하는지 확인하기 위해 dist 폴더 밑에 index.html 파일을 만들고, 다음 코드를 입력한다.
index.html 파일을 브라우저에서 실행해 보면 의도한 대로 잘 동작하는 것을 확인할 수 있다.
(만약 babel-loader를 설정하지 않고 웹팩을 실행하면 웹팩이 JSX문법을 이해하지 못하기 때문에 에러가 발생한다.)
이제 CSS 파일을 처리하는 로더를 알아보자.
npm install css-loader
src 폴더 밑에 App.css 파일을 만들고 다음 내용을 입력해 보자.
index.js 파일에 App.css 파일을 사용하는 코드를 추가한다.
자바스크립트 모듈에서 CSS 모듈을 import 해서 불러온다. 현재는 CSS 모듈을 처리하는 로더가 없기 때문에 웹팩을 실행하면 에러가 발생한다.
webpack.config.js 파일에 다음 코드를 추가해 보자.
css 확장자를 갖는 모듈은 css-loader가 처리하도록 설정했다.
웹팩 실행 후 index.html을 브라우저에서 확인해 보자. 콘솔에 출력된 내용을 확인해 보면 CSS 모듈의 내용이 보인다.
하지만 화면에 보이는 돔 요소의 스타일은 변경되지 않았다.
=> 스타일을 실제로 적용하기 위해서는 style-loader가 필요하다.
npm install style-loader
style-loader를 사용하도록 webpack.config.js 파일의 내용을 다음과 같이 수정하자.
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
}
css-module은 css-loader가 제공해 주는 기능이다. css-loader는 이 외에도 CSS 코드에서 사용된 @import, url() 등의 처리를 도와준다.
웹팩을 실행해서 브라우저로 결과를 확인해 보면 스타일이 제대로 적용된 것을 확인할 수 있다.
다른 종류의 파일도 처리해 보자.
먼저 임의의 PNG 파일을 src 폴더 밑에 icon.png 파일로 저장한다. src 폴더 밑에 data.txt 파일을 만들고 아무 내용이나 입력해 보자. 마찬가지로 src 폴더 밑에 data.json 파일을 만든 다음 다음 내용을 입력한다.
index.js 파일에서 지금까지 추가한 파일을 사용하도록 다음과 같이 수정해보자.
JSON 모듈은 웹팩에서 기본적으로 처리해 주기 때문에 별도의 로더를 설치하지 않아도 된다. TXT 모듈과 PNG 모듈을 처리하기 위해 다음 패키지를 설치하자.
npm install file-loader raw-loader
webpack.config.js 파일에 file-loader와 raw-loader를 설정하는 코드를 추가하자.
웹팩 실행 후 dist 폴더에 생성된 이미지 파일의 이름에는 해시값이 포함되어 있다. 이 해시값은 이미지 파일을 수정하는 경우에만 변경되기 때문에 사용자에게 전달된 이미지 파일은 브라우저의 캐싱 효과를 최대한 활용할 수 있다.
의도대로 잘 나오는 것을 확인할 수 있다.
이미지 파일을 번들 파일에 포함시키면 브라우저의 파일 요청 횟수를 줄일 수 있다. 이때 번들 파일 크기가 너무 커지면 자바스크립트가 늦게 실행되므로 작은 이미지 파일만 포함시키는 게 좋다.
url-loader를 사용해서 크기가 작은 이미지 파일만 번들 파일에 포함시켜 보자.
npm install url-loader
webpack.config.js 파일에서 이전에 작성했던 file-loader 설정을 지우고 다음과 같이 url-loader 설정으로 변경하자.
limit: 8192
: url-loader는 파일 크기가 이 값보다 작은 경우에는 번들 파일에 파일의 내용을 포함시킨다. 만약 파일 크기가 이 값보다 큰 경우에는 다른 로더가 처리할 수 있도록 fallback 옵션을 제공한다.limit 옵션에 icon.png 파일보다 큰 값을 입력하고 웹팩을 실행해 보자.
img 태그의 src 속성값을 확인해 보니 파일의 경로가 아니라 데이터가 입력된 것을 확인할 수 있다.
플러그인은 로더보다 강력한 기능을 갖는다. 로더는 특정 모듈에 대한 처리만 담당하지만 플러그인은 웹팩이 실행되는 전체 과정에 개입할 수 있다.
프로젝트 하나를 만들어 보자.
mkdir webpack-plugin
cd webpack-plugin
npm init -y
npm install webpack webpack-cli
프로젝트 루트에 src 폴더를 만들고 그 밑에 index.js 파일을 만든다. index.js 파일에 간단한 코들르 작성해 보자.
이 코드를 컴파일하기 위한 패키지를 설치해 보자.
npm install @babel/core @babel/preset-react babel-loader react react-dom
프로젝트 루트에 webpack.config.js 파일을 만들고 다음과 같이 babel-loader를 사용하도록 한다.
chunkhash
를 사용하면 파일의 내용이 수정될 때마다 파일 이름이 변경되도록 할 수 있다.웹팩을 실행해서 나오는 결과물을 확인하기 위해서는 이전처럼 HTML 파일을 수동으로 작성해야 한다. 여기서는 번들 파일 이름에 chunkhash 옵션을 설정했기 때문에 파일의 내용이 변경될 때마다 HTML 파일의 내용도 수정해야 한다.
=> 이 작업을 자동으로 하는 플러그인이 html-webpack-plugin이다.
다음과 같이 플러그인을 설치하자.
npm install clean-webpack-plugin html-webpack-plugin
webpack.config.js 파일에 다음 설정을 추가해 보자.
프로젝트 루트에 template 폴더를 만들고 그 밑에 index.html 파일을 만들어보자. html-webpack-plugin이 생성하는 HTML에 포함시킬 내용을 index.html 파일에 추가한다.
기타 필요한 태그를 이 파일에 추가하면 html-webpack-plugin이 생성하는 새로운 HTML 파일에 같이 포함된다.
웹팩을 실행하면 dist 폴더 밑에 index.html 파일이 생성된다.
번들 파일이 script 태그로 등록된다. 이 파일을 브라우저에서 실행하면 제대로 동작하는 것을 확인할 수 있다.
DefinePlugin
은 모듈 내부에 있는 문자열을 대체해 준다.
이 플러그인은 웹팩에 내장된 플러그인이기 때문에 별도로 설치할 필요는 없다.
다음과 같이 DefinePlugin으로 대체할 문자열을 index.js 파일에 추가한다.
webpack.config.js 파일에 다음 코드를 추가하자.
웹팩 실행 후 번들 파일의 내용을 확인해 보자.
=> 코드가 압축된 상태라서 확인이 쉽지는 않지만 대략 다음과 같은 코드를 확인할 수 있다.
o.a.createElement(
'div',
null,
o.a.createElement('h3', null, '안녕하세요, 웹팩 플러그인 예제입니다.'),
o.a.createElement('p', null, 'html-webpack-plugin 플러그인을 사용합니다.'),
o.a.createElement('p', null, '앱 버전은 1.2.3입니다.'),
o.a.createElement('p', null, '10 * 10 = 100'),
);
APP_VERSION 과 TEN 모두 프로덕션 모드로 웹팩을 실행했기 때문에 미리 계산된 결과가 번들 파일에 포함됐다.
자주 사용되는 모듈은 import 키워드를 사용해서 가져오는 게 귀찮을 수 있다.
// 자주 사용되는 모듈의 예
import React from "react;
import $ from 'jquery';
JSX문법을 사용하면 리액트 모듈을 사용하지 않는 것처럼 느껴질 수 있다. 사실은 바벨이 JSX 문법을 React.createElement 코드로 변환해 주기 때문에 리액트 모듈이 필요하다.
=> JSX 문법을 사용하는 파일을 작성한다면 import React from "react"
를 상단에 적어야 한다.
ProviderPlugin
를 사용하면 미리 설정한 모듈을 자동으로 등록해 준다. 이 플러그인은 웹패에 기본으로 포함되어 있기 때문에 별도로 설치할 필요는 없다.
ProviedPlugin
의 기능을 확인하기 위해 index.js 파일을 수정해 보자.
리액트 모듈을 주석처리하고 jquery를 사용해서 돔 요소를 가져오게 했다.
jquery 모듈을 가져오는 import 코드는 작성하지 않아도 된다.
webpack.config.js 파일에 ProvidePlugin
을 설정하는 코드를 추가해 보자.
다음과 같이 jquery 패키지를 설치하자.
npm install jquery
웹팩을 실행해 보면 에러 없이 번들 파일이 생성되는 것을 확인할 수 있다.