제목 날짜 내용 발행일 23.03.21
해당 포스트는
WEBPACK
에 대해 학습한 내용을 정리하며 기록한 내용입니다.
프론트엔드 개발에서 번들링은 사용자가 더 쉽고 빠르게 프론트엔드 애플리케이션에 접근할 수 있도록 용량을 줄이거나 파일을 최소화하여 유저에게 전달하는 과정
리액트는 당시 앵귤러의 단점을 보완할 수 있는 대체재로서 뷰와 함께 거론되기 시작
리액트가 주목받던 이유 중 하나가 “프레임워크가 아니고 라이브러리"라는 점 때문
프론트엔드 개발에 꼭 필요한 점 말고는 코드를 추가하지 말고, 더 필요한 것이 있으면 개발자가 설치하라는 의견을 제시
Learn Once, Write Anywhere
We don’t make assumptions about the rest of your technology stack, so you can develop new features in React without rewriting existing code.
리액트 개발진은 개발자가 어떤 기술 스택을 사용할지 미리 가정하지 않는다.
그래서 개발자가 새로운 코드를 다시 작성할 필요 없이 기능을 추가할 수 있다.
당시에 앵귤러의 갑작스러운 버전 변경과 논란과 겹쳐서 리액트의 이런 자유도는 많은 프론트엔드 개발자들의 관심을 받게 되었다.
반대로 이런 특성 때문에 생기는 문제도 많다.
“리액트"만 알아서는 개발하기가 어렵다는 점이다.
react
, react-dom
, react-scripts
, create-react-app
, react-router-dom
, storybook
, styled-component
등 많은 부가 라이브러리를 설치하고 활용하는 법을 배웠다.
이렇게 알아야 하는 점이 많아지다 보니, 리액트 개발진은 이런 문제를 한 번에 해결할 수 있는 create-react-app
이라는 툴 체인을 개발하여 초급 리액트 개발자가 쉽게 리액트에 접근할 수 있도록 했다.
리액트를 “간단하게" 시작하기 위해 create-react-app
에서 사용되는 툴 목록은 어마어마하다.
create-react-app
의 큰 부분인 react-scripts
에 사용되는 라이브러리 목록만 봐도 알 수 있다. (여기서도 웹팩이 사용)
반대로, 사용자에게 최적의 번들을 제공하기 위한 전문 프론트엔드 개발자들은 이런 create-react-app
의 거대한 라이브러리 목록을 줄이고자 직접 웹팩을 설치하여 하나씩 리액트와 그에 필요한 라이브러리 설정을 하기 시작했다.
리액트는 프론트엔드 라이브러리로서 최소한의 기능을 제공하고자 가볍게 만들어졌지만, 시간이 지나면서 아이러니하게도 개발자의 다양한 니즈를 충족시키기 위해 더 많은 라이브러리를 필수적으로 사용해야만 했고, 개발자가 필요한 이런저런 라이브러리를 골라서 번들링 할 수 있는 웹팩이 필요하게 되었다.
너무나 당연한 이야기지만, 리액트 컴포넌트와 Hooks, 라이프 사이클에 대한 정보가 모두 들어있는 리액트와 이 리액트 코드를 브라우저에 보여줄 수 있는 react-dom
은 꼭 필요
React를 학습하기 전, JSX
부터 배워야 했었다. 그런데, 브라우저에서 JavaScript는 읽을 수 있지만 JSX
는 읽을 수 없다.
그렇다면 지금까지 React를 JSX
로 작성해왔는데 어떻게 브라우저에서 내가 만든 React 애플리케이션을 볼 수 있었을까?
create-react-app
에 포함되어 있는 babel
이 jsx
를 js
로 변경해주어 번들링을 해줬기 때문이다.
참고로 babel
은 JSX
를 JavaScript로 변경하여 entry
에서 불러올 수 있게 만들어줬기 때문에 로더의 일종으로 볼 수 있겠다.
create-react-app
으로 만들어진 애플리케이션을 보면 import 'aaa.css'
와 같이 입력해도 CSS가 적용되던 것을 알 수 있다. 우리가 배웠던 css-loader
가 필요하다는 것을 쉽게 알 수 있다.
react-hot-loader
는 webpack-dev-server
처럼 저장할 때 마다 변경사항을 개발 환경에 적용해주는 라이브러리
추가적인 특징이 있다면 react-hot-loader
는 리액트 상태를 유지
시켜준다.
eslint
는 JavaScript로 개발 시 자주 접하는 에러를 방지하기 위한 린터.
eslint
역시 많은 config
와 plugin
이 있는데, 이를 잘 조합하면 리액트에서 자주 접하는 에러를 미리 발견하는데 도움이 된다.
prettier
는 JavaScript로 개발 시 통일성 있게 코드 형식을 맞출 수 있게 도와주는 툴.
eslint
와 조합해서 통일된 코드 형식까지 강요할 수도 있다.
이번 과제에서는 create-react-app
으로 만든 리액트 웹 애플리케이션의 src 디렉터리 코드만을 가지고 webpack
으로 번들링하는 것이다.
나만의 아고라 스테이츠 서버 레퍼런스 코드를 이용해서 시도해봤다.
리액트 웹 애플리케이션을 제작합니다.
babel-loader, style-loader, css-loader, html-webpack-plugin 등 필요한 로더나 플러그인을 스스로 학습하고 리액트 앱을 번들링합니다.
npm i -y
npm i react react-dom
react
: 리액트 컴포넌트와 Hooks, 라이프 사이클에 대한 정보가 들어있는 코어 라이브러리
react-dom
: React
의 데이터를 실제 DOM
으로 변경하기 위해 사용
npm i -D webpack webpack-cli
webpack: 웹팩 코어
webpack-cli: 터미널에서 웹팩 사용
자바스크립트로 작성된 프로젝트는 번들링할때는 webpack.config.js
에서 entry
와 output
만 설정하고 번들링할 수 있었다.
하지만 JSX
로 작성된 리액트 코드는 바벨을 통해서 브라우저가 이해할 수 있는 자바스크립트로 변환해서 번들링해줘야 한다.
JSX
를 자바스크립트로 변환 처리 해주는 것이 바벨 로더
npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react
preset이란 자주 사용하는 babel 플러그인들을 합쳐서 제공하는 것을 의미
@babel/core : 바벨 코어
@babel/preset-env: 구문 변환 및 폴리필 같은 다양한 처리를 해주는 preset
@babel/preset-react: jsx 구문을 js로 바꿔주는 등의 처리를 해주는 preset
babel-loader : 바벨과 웹팩을 연결
//webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js', // 빌드 시작 위치
output: {
// 빌드결과 저장 위치
filename: 'app.bundle.js',
path: path.resolve(__dirname, 'docs'),
clean: true,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env'],
['@babel/preset-react', { runtime: 'automatic' }],
],
},
},
},
],
},
};
loader
이기 때문에 module.rules
하단에 작성style-loader
와 css-loader
로 번들링하면 header
태그 안에서 internal
방식으로 번들링 됨
external
방식으로 CSS 번들링하는 플러그인인 css-minimizer-webpack-plugin
를 설치했다.
npm i -D css-minimizer-webpack-plugin
npm i -D mini-css-extract-plugin
mini-css-extract-plugin
은 css 파일을 한줄로 압축한다.
//webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
entry: './src/index.js', // 빌드 시작 위치
output: {
// 빌드결과 저장 위치
filename: 'app.bundle.js',
path: path.resolve(__dirname, 'docs'),
clean: true,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env'],
['@babel/preset-react', { runtime: 'automatic' }],
],
},
},
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
exclude: /node_modules/,
},
],
},
optimization: {
minimizer: [
new CssMinimizerPlugin(), // css를 줄여주는 플러그인
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'style.css',
}),
],
};
//index.js
import style from './index.css'
index.js
파일 안에 css파일이 import
되어 있어야함
npm install -D html-webpack-plugin
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
entry: './src/index.js', // 빌드 시작 위치
output: {
// 빌드결과 저장 위치
filename: 'app.bundle.js',
path: path.resolve(__dirname, 'docs'),
clean: true,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env'],
['@babel/preset-react', { runtime: 'automatic' }],
],
},
},
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
exclude: /node_modules/,
},
],
},
optimization: {
minimizer: [
new CssMinimizerPlugin(), // css를 줄여주는 플러그인
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
new MiniCssExtractPlugin({
filename: 'style.css',
}),
],
};
여기까지 진행하면 docs
폴더에 최소한의 웹팩 설정으로 생성된 번들 결과가 JS, CSS, HTML 파일이 생성된다.
Mode |webpack 러닝 가이드
Mode | 공식문서
웹팩 설정 파일에 mode
속성을 추가해서 사용할 수 있다.
기본값은 production(배포 모드)
이고, 웹팩 모듈 번들링 과정에서 코드를 최적화해서 용량을 줄인다.
반면에 development(개발 모드)
는 안전한 로컬 환경에서 개발한다고 가정하고, 편리한 개발을 위해서 어느 파일에서 에러가 발생했는지도 알려주는 최적화되지 않은 번들 파일을 제공한다.
웹팩 설정파일을 두가지 모드로 설정하기 위해서 webpack-merge
를 설치한다.
npm install -D webpack-merge
기본 설정 파일을 머지한 각각의 개발/배포 환경에 필요한 설정파일을 따로 만들어준다.
webpack-dev-server
도 설치해서 변경된 코드를 개발 서버에 반영해서 보여줄 수 있도록 설정을 추가했다.
npm i -D webpack-dev-server
// webpack.config.dev.js
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.config.js');
const path = require('path');
module.exports = merge(baseConfig, {
mode: 'development',
devServer: {
static: {
directory: path.resolve(__dirname, 'docs'),
},
port: 3001,
hot: true,
},
});
// webpack.config.prod.js
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.config.js');
module.exports = merge(baseConfig, {
mode: 'production',
});
//package.json scripts에 추가
"scripts": {
"build": "webpack --config webpack.config.prod.js",
"dev": "webpack server --open --config webpack.config.dev.js",
},
코드에 문제가 없는지 검사하기 위해서 설치
npm install -D eslint eslint-plugin-react @babel/eslint-parser
//pacakge.json scripts에 추가
"lint": "eslink ./src",
웹 접근성에 대해서 지켜야하는 부분을 알려주는 eslint rule
설치해봄
npm install -D eslint-plugin-jsx-a11y
따로 설정파일을 작성해야 한다.
// .eslintrc.js
module.exports = {
parser: "@babel/eslint-parser",
env: {
browser: true,
commonjs: true,
es6: true,
node: true,
},
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:jsx-a11y/recommended",
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2020,
sourceType: "module",
},
settings: {
react: {
version: "18.2.0",
},
},
plugins: ["react"],
rules: {
"react/react-in-jsx-scope": 0,
"react/jsx-uses-react": 0,
"react/prop-types": 0,
},
};
코드 형식 통일(VSCode extension으로도 할 수 있지만 직접 작성할줄도 알아야한다.)
npm install -D prettier
//pacakge.json scripts에 추가
"pretty": "prettier --write ./",
따로 설정파일을 만들어줘야 한다.
// .prettierrc.js
module.exports = {
singleQuote: true,
jsxSingleQuote: true,
};