제어 유지: Webpack 및 React 가이드, Pt. 1
게시 됨: 2022-03-11새로운 React 프로젝트를 시작할 때 선택할 수 있는 템플릿이 많이 있습니다: Create React App, react-boilerplate
, React Starter Kit 등.
수천 명의 개발자가 채택한 이러한 템플릿은 대규모 애플리케이션 개발을 지원할 수 있습니다. 그러나 그들은 개발자 경험을 남겨두고 다양한 기본값으로 묶인 번들 출력을 제공하므로 이상적이지 않을 수 있습니다.
빌드 프로세스에 대한 더 큰 제어를 유지하려면 사용자 지정 Webpack 구성에 투자하도록 선택할 수 있습니다. 이 Webpack 튜토리얼에서 배우게 될 것처럼 이 작업은 그다지 복잡하지 않으며 지식은 다른 사람의 구성 문제를 해결할 때 유용할 수도 있습니다.
웹팩: 시작하기
오늘날 우리가 자바스크립트를 작성하는 방식은 브라우저가 실행할 수 있는 코드와 다릅니다. 우리는 최신 브라우저에서 아직 지원되지 않는 다른 유형의 리소스, 변환된 언어 및 실험 기능에 자주 의존합니다. Webpack은 이 격차를 해소하고 개발자 경험과 관련하여 비용 부담 없이 크로스 브라우저 호환 코드를 생성할 수 있는 JavaScript용 모듈 번들러입니다.
시작하기 전에 이 Webpack 튜토리얼에 제공된 모든 코드는 GitHub에서 완전한 Webpack/React 예제 구성 파일의 형태로도 사용할 수 있다는 점을 염두에 두어야 합니다. 거기에서 자유롭게 참조하고 질문이 있는 경우 이 문서로 다시 오십시오.
기본 구성
Legato(버전 4)부터 Webpack을 실행하기 위해 구성이 필요하지 않습니다. 빌드 모드를 선택하면 대상 환경에 더 적합한 일련의 기본값이 적용됩니다. 이 기사의 정신에서 우리는 이러한 기본값을 무시하고 각 대상 환경에 대해 합리적인 구성을 직접 구현할 것입니다.
먼저 webpack
과 webpack-cli
를 설치해야 합니다.
npm install -D webpack webpack-cli
그런 다음 webpack.config.js
를 다음 옵션이 포함된 구성으로 채워야 합니다.
-
devtool
: 개발 모드에서 소스 맵 생성을 활성화합니다. -
entry
: React 애플리케이션의 기본 파일입니다. -
output.path
: 출력 파일을 저장할 루트 디렉터리입니다. -
output.filename
: 생성된 파일에 사용할 파일 이름 패턴입니다. -
output.publicPath
: 웹 서버에서 파일이 배포될 루트 디렉터리의 경로입니다.
const path = require("path"); module.exports = function(_env, argv) { const isProduction = argv.mode === "production"; const isDevelopment = !isProduction; return { devtool: isDevelopment && "cheap-module-source-map", entry: "./src/index.js", output: { path: path.resolve(__dirname, "dist"), filename: "assets/js/[name].[contenthash:8].js", publicPath: "/" } }; };
위의 구성은 일반 JavaScript 파일에 대해 잘 작동합니다. 그러나 Webpack 및 React를 사용할 때 사용자에게 코드를 제공하기 전에 추가 변환을 수행해야 합니다. 다음 섹션에서는 Babel을 사용하여 Webpack이 JavaScript 파일을 로드하는 방식을 변경할 것입니다.
JS 로더
Babel은 코드 변환을 위한 많은 플러그인이 있는 JavaScript 컴파일러입니다. 이 섹션에서는 Webpack 구성에 로더로 도입하고 최신 JavaScript 코드를 일반 브라우저에서 이해할 수 있는 코드로 변환하도록 구성합니다.
먼저 babel-loader
와 @babel/core
를 설치해야 합니다.
npm install -D @babel/core babel-loader
그런 다음 Webpack 구성에 module
섹션을 추가하여 babel-loader
가 JavaScript 파일을 로드하도록 합니다.
@@ -11,6 +11,25 @@ module.exports = function(_env, argv) { path: path.resolve(__dirname, "dist"), filename: "assets/js/[name].[contenthash:8].js", publicPath: "/" + }, + module: { + rules: [ + { + test: /\.jsx?$/, + exclude: /node_modules/, + use: { + loader: "babel-loader", + options: { + cacheDirectory: true, + cacheCompression: false, + envName: isProduction ? "production" : "development" + } + } + } + ] + }, + resolve: { + extensions: [".js", ".jsx"] } }; };
우리는 babel.config.js
라는 별도의 설정 파일을 사용하여 Babel을 설정할 것입니다. 다음 기능을 사용합니다.
-
@babel/preset-env
: 최신 JavaScript 기능을 이전 버전과 호환되는 코드로 변환합니다. -
@babel/preset-react
: JSX 구문을 일반 바닐라 JavaScript 함수 호출로 변환합니다. -
@babel/plugin-transform-runtime
: Babel 도우미를 공유 모듈로 추출하여 코드 중복을 줄입니다. -
@babel/plugin-syntax-dynamic-import
: 기본Promise
지원이 없는 브라우저에서 동적import()
구문을 활성화합니다. -
@babel/plugin-proposal-class-properties
: 클래스 기반 React 구성 요소를 작성하기 위해 공개 인스턴스 필드 구문 제안에 대한 지원을 활성화합니다.
또한 몇 가지 React 관련 프로덕션 최적화를 활성화할 것입니다.
-
babel-plugin-transform-react-remove-prop-types
는 프로덕션 코드에서 불필요한 소품 유형을 제거합니다. -
@babel/plugin-transform-react-inline-elements
는 컴파일하는 동안React.createElement
를 평가하고 결과를 인라인합니다. -
@babel/plugin-transform-react-constant-elements
는 정적 React 요소를 상수로 추출합니다.
아래 명령은 필요한 모든 종속성을 설치합니다.
npm install -D @babel/preset-env @babel/preset-react @babel/runtime @babel/plugin-transform-runtime @babel/plugin-syntax-dynamic-import @babel/plugin-proposal-class-properties babel-plugin-transform-react-remove-prop-types @babel/plugin-transform-react-inline-elements @babel/plugin-transform-react-constant-elements
그런 다음 babel.config.js
를 다음 설정으로 채웁니다.
module.exports = { presets: [ [ "@babel/preset-env", { modules: false } ], "@babel/preset-react" ], plugins: [ "@babel/plugin-transform-runtime", "@babel/plugin-syntax-dynamic-import", "@babel/plugin-proposal-class-properties" ], env: { production: { only: ["src"], plugins: [ [ "transform-react-remove-prop-types", { removeImport: true } ], "@babel/plugin-transform-react-inline-elements", "@babel/plugin-transform-react-constant-elements" ] } } };
이 구성을 통해 모든 관련 브라우저와 호환되는 방식으로 최신 JavaScript를 작성할 수 있습니다. React 애플리케이션에 필요할 수 있는 다른 유형의 리소스가 있으며 이에 대해서는 다음 섹션에서 다룰 것입니다.
CSS 로더
React 애플리케이션을 스타일링할 때 최소한 일반 CSS 파일을 포함할 수 있어야 합니다. 다음 로더를 사용하여 Webpack에서 이 작업을 수행할 것입니다.
-
css-loader
: CSS 파일을 구문 분석하여 이미지, 글꼴 및 추가 스타일 가져오기와 같은 외부 리소스를 확인합니다. -
style-loader
: 개발 중에 로드된 스타일을 런타임에 문서에 삽입합니다. -
mini-css-extract-plugin
: 브라우저 캐싱을 활용하기 위해 프로덕션 사용을 위해 로드된 스타일을 별도의 파일로 추출합니다.
위의 CSS 로더를 설치해 보겠습니다.
npm install -D css-loader style-loader mini-css-extract-plugin
그런 다음 Webpack 구성의 module.rules
섹션에 새 규칙을 추가합니다.
@@ -1,4 +1,5 @@ const path = require("path"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = function(_env, argv) { const isProduction = argv.mode === "production"; @@ -25,6 +26,13 @@ module.exports = function(_env, argv) { envName: isProduction ? "production" : "development" } } + }, + { + test: /\.css$/, + use: [ + isProduction ? MiniCssExtractPlugin.loader : "style-loader", + "css-loader" + ] } ] },
또한 plugins
섹션에 MiniCssExtractPlugin
을 추가합니다. 이 섹션은 프로덕션 모드에서만 활성화합니다.
@@ -38,6 +38,13 @@ module.exports = function(_env, argv) { }, resolve: { extensions: [".js", ".jsx"] - } + }, + plugins: [ + isProduction && + new MiniCssExtractPlugin({ + filename: "assets/css/[name].[contenthash:8].css", + chunkFilename: "assets/css/[name].[contenthash:8].chunk.css" + }) + ].filter(Boolean) }; };
이 구성은 일반 CSS 파일에서 작동하며 다음 기사에서 논의할 Sass 및 PostCSS와 같은 다양한 CSS 프로세서와 함께 작동하도록 확장할 수 있습니다.
이미지 로더
Webpack은 또한 이미지, 비디오 및 기타 바이너리 파일과 같은 정적 리소스를 로드하는 데 사용할 수 있습니다. 이러한 유형의 파일을 처리하는 가장 일반적인 방법은 file-loader
또는 url-loader
를 사용하는 것입니다. 이는 소비자에게 필요한 리소스에 대한 URL 참조를 제공합니다.
이 섹션에서는 일반적인 이미지 형식을 처리하기 위해 url-loader
를 추가합니다. url-loader
가 file-loader
와 다른 점은 원본 파일의 크기가 주어진 임계값보다 작은 경우 URL에 전체 파일을 base64로 인코딩된 내용으로 포함하므로 추가 요청이 필요 없다는 것입니다.
먼저 url-loader
를 설치합니다.
npm install -D url-loader
그런 다음 Webpack 구성의 module.rules
섹션에 새 규칙을 추가합니다.
@@ -34,6 +34,16 @@ module.exports = function(_env, argv) { isProduction ? MiniCssExtractPlugin.loader : "style-loader", "css-loader" ] + }, + { + test: /\.(png|jpg|gif)$/i, + use: { + loader: "url-loader", + options: { + limit: 8192, + name: "static/media/[name].[hash:8].[ext]" + } + } } ] },
SVG
SVG 이미지의 경우 가져온 파일을 React 구성 요소로 변환하는 @svgr/webpack
로더를 사용할 것입니다.
@svgr/webpack
을 설치합니다.
npm install -D @svgr/webpack
그런 다음 Webpack 구성의 module.rules
섹션에 새 규칙을 추가합니다.
@@ -44,6 +44,10 @@ module.exports = function(_env, argv) { name: "static/media/[name].[hash:8].[ext]" } } + }, + { + test: /\.svg$/, + use: ["@svgr/webpack"] } ] },
SVG 이미지는 React 구성 요소로 편리할 수 있으며 @svgr/webpack
은 SVGO를 사용하여 최적화를 수행합니다.

참고: 특정 애니메이션이나 마우스 오버 효과의 경우 JavaScript를 사용하여 SVG를 조작해야 할 수도 있습니다. 다행히 @svgr/webpack
은 기본적으로 SVG 콘텐츠를 JavaScript 번들에 포함하므로 이에 필요한 보안 제한을 우회할 수 있습니다.
파일 로더
다른 종류의 파일을 참조해야 하는 경우 일반 file-loader
가 작업을 수행합니다. url-loader
와 유사하게 작동하며 이를 필요로 하는 코드에 자산 URL을 제공하지만 최적화하려는 시도는 하지 않습니다.
항상 그렇듯이 먼저 Node.js 모듈을 설치합니다. 이 경우 file-loader
:
npm install -D file-loader
그런 다음 Webpack 구성의 module.rules
섹션에 새 규칙을 추가합니다. 예를 들어:
@@ -48,6 +48,13 @@ module.exports = function(_env, argv) { { test: /\.svg$/, use: ["@svgr/webpack"] + }, + { + test: /\.(eot|otf|ttf|woff|woff2)$/, + loader: require.resolve("file-loader"), + options: { + name: "static/media/[name].[hash:8].[ext]" + } } ] },
여기에 CSS 파일에서 참조할 수 있는 글꼴 로드를 위한 file-loader
가 추가되었습니다. 이 예제를 확장하여 필요한 다른 종류의 파일을 로드할 수 있습니다.
환경 플러그인
Webpack의 DefinePlugin()
을 사용하여 빌드 환경의 환경 변수를 애플리케이션 코드로 노출할 수 있습니다. 예를 들어:
@@ -1,5 +1,6 @@ const path = require("path"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const webpack = require("webpack"); module.exports = function(_env, argv) { const isProduction = argv.mode === "production"; @@ -65,7 +66,12 @@ module.exports = function(_env, argv) { new MiniCssExtractPlugin({ filename: "assets/css/[name].[contenthash:8].css", chunkFilename: "assets/css/[name].[contenthash:8].chunk.css" - }) + }), + new webpack.DefinePlugin({ + "process.env.NODE_ENV": JSON.stringify( + isProduction ? "production" : "development" + ) + }) ].filter(Boolean) }; };
여기에서 process.env.NODE_ENV
를 빌드 모드를 나타내는 문자열로 대체했습니다: "development"
또는 "production"
.
HTML 플러그인
index.html
파일이 없으면 JavaScript 번들은 아무 소용이 없으며 아무도 찾을 수 없습니다. 이 섹션에서는 HTML 파일을 생성하기 위해 html-webpack-plugin
을 소개합니다.
html-webpack-plugin
을 설치합니다.
npm install -D html-webpack-plugin
그런 다음 Webpack 구성의 plugins
섹션에 html-webpack-plugin
을 추가합니다.
@@ -1,6 +1,7 @@ const path = require("path"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const webpack = require("webpack"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = function(_env, argv) { const isProduction = argv.mode === "production"; @@ -71,6 +72,10 @@ module.exports = function(_env, argv) { "process.env.NODE_ENV": JSON.stringify( isProduction ? "production" : "development" ) + }), + new HtmlWebpackPlugin({ + template: path.resolve(__dirname, "public/index.html"), + inject: true }) ].filter(Boolean) };
생성된 public/index.html
파일은 번들을 로드하고 애플리케이션을 부트스트랩합니다.
최적화
빌드 프로세스에서 사용할 수 있는 몇 가지 최적화 기술이 있습니다. 기능 면에서 비용 없이 번들의 크기를 줄일 수 있는 프로세스인 코드 축소부터 시작하겠습니다. 코드를 최소화하기 위해 JavaScript 코드용 terser-webpack-plugin
과 optimize-css-assets-webpack-plugin
두 가지 플러그인을 사용합니다.
설치해 보겠습니다.
npm install -D terser-webpack-plugin optimize-css-assets-webpack-plugin
그런 다음 구성에 optimization
섹션을 추가합니다.
@@ -2,6 +2,8 @@ const path = require("path"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); +const TerserWebpackPlugin = require("terser-webpack-plugin"); +const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin"); module.exports = function(_env, argv) { const isProduction = argv.mode === "production"; @@ -75,6 +77,27 @@ module.exports = function(_env, argv) { isProduction ? "production" : "development" ) }) - ].filter(Boolean) + ].filter(Boolean), + optimization: { + minimize: isProduction, + minimizer: [ + new TerserWebpackPlugin({ + terserOptions: { + compress: { + comparisons: false + }, + mangle: { + safari10: true + }, + output: { + comments: false, + ascii_only: true + }, + warnings: false + } + }), + new OptimizeCssAssetsPlugin() + ] + } }; };
위의 설정은 모든 최신 브라우저와의 코드 호환성을 보장합니다.
코드 분할
코드 분할은 애플리케이션의 성능을 개선하는 데 사용할 수 있는 또 다른 기술입니다. 코드 분할은 두 가지 다른 접근 방식을 참조할 수 있습니다.
- 동적
import()
문을 사용하여 번들 크기의 상당 부분을 구성하는 애플리케이션 부분을 추출하고 요청 시 로드할 수 있습니다. - 브라우저 캐싱을 활용하고 반복 방문자의 성능을 개선하기 위해 덜 자주 변경되는 코드를 추출할 수 있습니다.
타사 종속성과 공통 청크를 별도의 파일로 추출하기 위한 설정으로 Webpack 구성의 optimization.splitChunks
.splitChunks 섹션을 채웁니다.
@@ -99,7 +99,29 @@ module.exports = function(_env, argv) { sourceMap: true }), new OptimizeCssAssetsPlugin() - ] + ], + splitChunks: { + chunks: "all", + minSize: 0, + maxInitialRequests: 20, + maxAsyncRequests: 20, + cacheGroups: { + vendors: { + test: /[\\/]node_modules[\\/]/, + name(module, chunks, cacheGroupKey) { + const packageName = module.context.match( + /[\\/]node_modules[\\/](.*?)([\\/]|$)/ + )[1]; + return `${cacheGroupKey}.${packageName.replace("@", "")}`; + } + }, + common: { + minChunks: 2, + priority: -10 + } + } + }, + runtimeChunk: "single" } }; };
여기에서 사용한 옵션을 자세히 살펴보겠습니다.
-
chunks: "all"
: 기본적으로 공통 청크 추출은 동적import()
로 로드된 모듈에만 영향을 줍니다. 이 설정은 진입점 로딩에 대한 최적화도 가능하게 합니다. -
minSize: 0
: 기본적으로 특정 크기 임계값 이상의 청크만 추출 대상이 됩니다. 이 설정을 사용하면 크기에 관계없이 모든 공통 코드에 대해 최적화할 수 있습니다. -
maxInitialRequests: 20
및maxAsyncChunks: 20
: 이 설정은 진입점 가져오기 및 분할점 가져오기 각각에 대해 병렬로 로드할 수 있는 소스 파일의 최대 수를 늘립니다.
또한 다음과 같은 cacheGroups
구성을 지정합니다.
-
vendors
: 타사 모듈에 대한 추출을 구성합니다.-
test: /[\\/]node_modules[\\/]/
: 타사 종속성을 일치시키기 위한 파일 이름 패턴입니다. -
name(module, chunks, cacheGroupKey)
: 동일한 모듈의 개별 청크에 공통 이름을 부여하여 함께 그룹화합니다.
-
-
common
: 애플리케이션 코드에서 공통 청크 추출을 구성합니다.-
minChunks: 2
: 적어도 두 개의 모듈에서 참조되는 경우 청크는 공통된 것으로 간주됩니다. -
priority: -10
:vendors
캐시 그룹에 대한 청크가 먼저 고려되도록common
캐시 그룹에 음수 우선 순위를 할당합니다.
-
또한 runtimeChunk: "single"
을 지정하여 여러 진입점 간에 공유할 수 있는 단일 청크로 Webpack 런타임 코드를 추출합니다.
개발 서버
지금까지 우리는 애플리케이션의 프로덕션 빌드를 만들고 최적화하는 데 중점을 두었지만 Webpack에는 라이브 다시 로드 및 오류 보고 기능이 있는 자체 웹 서버도 있어 개발 프로세스에 도움이 됩니다. webpack-dev-server
라고 하며 별도로 설치해야 합니다.
npm install -D webpack-dev-server
이 스니펫에서는 Webpack 구성에 devServer
섹션을 소개합니다.
@@ -120,6 +120,12 @@ module.exports = function(_env, argv) { } }, runtimeChunk: "single" + }, + devServer: { + compress: true, + historyApiFallback: true, + open: true, + overlay: true } }; };
여기에서는 다음 옵션을 사용했습니다.
-
compress: true
: 더 빠른 재로드를 위해 자산 압축을 활성화합니다. -
historyApiFallback: true
: 히스토리 기반 라우팅을 위해index.html
로의 폴백을 활성화합니다. -
open: true
: 개발 서버를 시작한 후 브라우저를 엽니다. -
overlay: true
: 브라우저 창에 Webpack 오류를 표시합니다.
API 요청을 백엔드 서버로 전달하도록 프록시 설정을 구성해야 할 수도 있습니다.
Webpack 및 React: 성능 최적화 및 준비 완료!
Webpack으로 다양한 리소스 유형을 로드하는 방법, 개발 환경에서 Webpack을 사용하는 방법 및 프로덕션 빌드를 최적화하기 위한 여러 기술을 방금 배웠습니다. 필요한 경우 언제든지 전체 구성 파일을 검토하여 자신의 React/Webpack 설정에 대한 영감을 얻을 수 있습니다. 이러한 기술을 연마하는 것은 React 개발 서비스를 제공하는 모든 사람에게 표준 요금입니다.
이 시리즈의 다음 부분에서는 TypeScript 사용, CSS 전처리기, 서버 측 렌더링 및 ServiceWorkers와 관련된 고급 최적화 기술을 포함하여 보다 구체적인 사용 사례에 대한 지침으로 이 구성을 확장할 것입니다. React 애플리케이션을 프로덕션으로 가져오기 위해 Webpack에 대해 알아야 할 모든 것을 배우려면 계속 지켜봐 주십시오.