webpack - 개발서버,API,HMR,최적화
웹팩 개발 서버
배경
운영환경과 맞춤으로써 배포시 잠재적 문제를 미리 확인할 수 있다. 게다가 ajax 방식의 api 연동은 cors 정책 때문에 반드시 서버가 필요
프론트엔드 개발환경에서 이러한 개발용 서버를 제공해 주는 것이 webpack-dev-server다.
설치 및 사용
npm i -D webpack-dev-server
package.json
{
"scripts": {
"start": "webpack-dev-server"
}
}
웹팩 서버는 파일 변화를 감지하면 웹팩 빌드를 다시 수행하고 브라우져를 리프레시하여 변경된 결과물을 보여준다.
기본 설정
webpack.config.js
module.exports = {
devServer: {
contentBase: path.join(__dirname, "dist"),
publicPath: "/",
host: "dev.domain.com",
overlay: true,
port: 8081,
stats: "errors-only",
historyApiFallback: true,
},
}
contentBase: 정적파일을 제공할 경로. 기본값은 웹팩 아웃풋이다.
publicPath: 브라우져를 통해 접근하는 경로. 기본값은 '/' 이다.
host: 개발환경에서 도메인을 맞추어야 하는 상황에서 사용한다. 예를들어 쿠기 기반의 인증은 인증 서버와 동일한 도메인으로 개발환경을 맞추어야 한다. 운영체제의 호스트 파일에 해당 도메인과 127.0.0.1 연결한 추가한 뒤 host 속성에 도메인을 설정해서 사용한다.
overlay: 빌드시 에러나 경고를 브라우져 화면에 표시한다.
port: 개발 서버 포트 번호를 설정한다. 기본값은 8080.
stats: 메시지 수준을 정할수 있다. 'none', 'errors-only', 'minimal', 'normal', 'verbose' 로 메세지 수준을 조절한다.
historyApiFallBack: 히스토리 API를 사용하는 SPA 개발시 설정한다. 404가 발생하면 index.html로 리다이렉트한다.
API 연동
목업 API 1 : devServer.before
webpack.config.js
module.exports = {
devServer: {
before: (app, server, compiler) => {
app.get("/api/keywords", (req, res) => {
res.json([
{ keyword: "이탈리아" },
{ keyword: "세프의요리" },
{ keyword: "제철" },
{ keyword: "홈파티" },
])
})
},
},
}
before에 설정한 미들웨어는 익스프레스에 의해서 app 객체가 인자로 전달되는데 Express 인스턴스다. 이 객체에 라우트 컨트롤러를 추가할 수 있는데 app.get(url, controller) 형태로 함수를 작성한다. 컨트롤러에서는 요청 req과 응답 res 객체를 받는데 여기서는 res.json() 함수로 응답하는 코드를 만들었다.
목업 API 2 : connect-api-mocker
특정 목업 폴더를 만들어 api 응답을 담은 파일을 저장한 뒤, 이 폴더를 api로 제공해 주는 기능을 한다
mocks / api / users / GET.json
[
{ "keyword": "이탈리아" },
{ "keyword": "세프의요리" },
{ "keyword": "제철" },
{ "keyword": "홈파티 " }
]
webpack.config.js
const apiMocker = require("connect-api-mocker")
module.exports = {
devServer: {
before: (app, server, compiler) => {
app.use(apiMocker("/api", "mocks/api")) // root url , mocks 파일 위치
},
},
}
proxy setting
webpack.config.js
module.exports = {
devServer: {
proxy: {
"/api": "<http://localhost:8081>", // 프록시
},
},
}
핫 모듈 리플레이스먼트
전체화면을 갱신하지 않고 변경한 모듈만 바꿔치기하는 웹팩 개발서버의 한 기능
설정
webpack.config.js
module.exports = {
devServer = {
hot: true,
},
}
app.js
view.js파일의 변경이 감지되면 그 부분만 모듈이 변경되어 랜더링됨
// module.hot 실행
if (module.hot) {
// 변경 감지
module.hot.accept("./view", async () => {
// 리랜더링
view.render(await model.get(), controller.el) // 변경된 모듈로 교체
})
}
핫로딩을 지원하는 로더
이러한 핫 모듈 리플레이스먼트(HMR)인터페이스를 구현한 로더만이 핫 로딩을 지원함
최적화
코드가 많아지면 번들링된 결과물도 커지기 마련 → 성능에 영향
production 모드
웹팩에 내장되어 있는 최적화 방법중 mode 값을 설정하는 방식이 가장 기본이다. 세 가지 값이 올 수 있는데 지금까지 설정한 "development"는 디버깅 편의를 위해 아래 두 개 플러그인을 사용한다.
- NamedChunksPlugin
- NamedModulesPlugin
DefinePlugin을 사용한다면 process.env.NODE_ENV 값이 "development"로 설정되어 어플리케이션에 전역변수로 주입된다.
반면 mode를 "production"으로 설정하면 자바스크립트 결과물을 최소화 하기 위해 다음 일곱 개 플러그인을 사용한다.
- FlagDependencyUsagePlugin
- FlagIncludedChunksPlugin
- ModuleConcatenationPlugin
- NoEmitOnErrorsPlugin
- OccurrenceOrderPlugin
- SideEffectsFlagPlugin
- TerserPlugin
DefinePlugin을 사용한다면 process.env.NODE_ENV 값이 "production" 으로 설정되어 어플리케이션 전역변수로 들어간다.
환경변수 NODE_ENV 값에 따라 모드를 설정
webpack.config.js
const mode = process.env.NODE_ENV || "development" // 기본값을 development로 설정
module.exports = {
mode,
}
빌드 시에 production 모드로 설정하여 실행하도록 npm script 추가
package.json
{
"scripts": {
"start": "webpack-dev-server --progress",
"build": "NODE_ENV=production webpack --progress"
}
}
optimazation 속성으로 최적화
빌드 과정을 커스터마지징할 수 있는 속성 ⇒ optimazation 속성
HtmlWebpackPlugin이 html 파일을 압축한것 처럼 css 파일도 빈칸을 없애는 압축 ⇒ assets-webpack-plugin
npm i -D optimize-css-assets-webpack-plugin
webpack.config.js
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
module.exports = {
optimization: {
minimizer: mode === "production" ? [new OptimizeCSSAssetsPlugin()] : [],
},
}
optimization.minimizer 는 웹팩이 결과물을 압축할때 사용할 플러그인을 넣는 배열
TerserWebpackPlugin은 자바스크립트 코드를 난독화하고 debugger 구문을 제거한다. 기본 설정 외에도 콘솔 로그를 제거하는 옵션도 있다
npm i -D terser-webpack-plugin
webpack.config.js
const TerserPlugin = require("terser-webpack-plugin")
module.exports = {
optimization: {
minimizer:
mode === "production"
? [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // 콘솔 로그를 제거한다
},
},
}),
]
: [],
},
}
코드 스플리팅
코드를 압축하는 것 외에도 아예 결과물을 여러개로 쪼개면 좀 더 브라우져 다운로드 속도를 높일 수 있다.
엔트리를 여러개로 분리
빌드하면 엔트리별로 js 파일이 생성됨 그러나 중복코드가 존재함
SplitChunksPlugin 이 플러그인을 사용해 중복 코드 제거 ⇒ 중복되어 사용되는 코드는 vendor.js 파일에 저장
webpack.config.js
module.exports = {
entry: {
main: "./src/app.js",
controller: "./src/controller.js",
},
optimization: {
splitChunks: {
chunks: "all", // 중복 코드 제거
},
},
}
다이나믹 임포트
엔트리를 여러개로 자동으로 분리
app.js
import controller from "./controller"
document.addEventListener("DOMContentLoaded", () => {
controller.init(document.querySelector("#app"))
})
// ----- dynamic import로 변경 ----
function getController() {
return import(/* webpackChunkName: "controller" */ "./controller").then(m => {
return m.default
})
}
document.addEventListener("DOMContentLoaded", () => {
getController().then(controller => {
controller.init(document.querySelector("#app"))
})
})
import() 함수로 가져올 컨트롤러 모듈 경로를 전달하는데 주석으로 webpackHunkName: "controller"를 전달했다. 이것은 웹펙이 이 파일을 처리할때 청크로 분리하는데 그 청그 이름을 설정한 것
이렇게 하면 webpack.config.js의 entry를 하나로 유지하면서 중복코드도 제거하며 코드를 분리할 수 있다.
externals
axios 같은 써드파티 라이브러리는 패키지로 제공될때 이미 빌드 과정을 거쳤기 때문에 빌드 프로세스에서 제외하는 것이 좋다.
webpack.config.js
module.exports = {
externals: {
axios: "axios",
},
}
externals에 추가하면 웹팩은 코드에서 axios를 사용하더라도 번들에 포함하지 않고 빌드한다.
axios는 이미 node_modules에 위치해 있기 때문에 이를 웹팩 아웃풋 폴더에 옮기고 index.html에서 로딩해야한다. 파일을 복사하는 CopyWebpackPlugin을 설치한다.
npm i -D copy-webpack-plugin
webpack.config.js
const CopyPlugin = require("copy-webpack-plugin")
module.exports = {
plugins: [
new CopyPlugin([
{
from: "./node_modules/axios/dist/axios.min.js",
to: "./axios.min.js", // 목적지 파일에 들어간다
},
]),
],
}
마지막으로 index.html에서 axios를 로딩하는 코드를 추가한다.
※ 출저
'Study > webpack' 카테고리의 다른 글
webpack@4 - lint,prettier (0) | 2022.06.04 |
---|---|
webpack@4 - babel - basic (0) | 2022.06.04 |
webpack@4 - plugin (0) | 2022.06.04 |
webpack@4 - loader (0) | 2022.06.04 |
webpack@4 - 엔트리/아웃풋 (0) | 2022.06.04 |