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

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를 로딩하는 코드를 추가한다.

 

※ 출저

https://www.inflearn.com/course/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD/dashboard

 

프론트엔드 개발환경의 이해와 실습 (webpack, babel, eslint..) - 인프런 | 강의

이미 만들어 놓은 개발 환경을 이해할 수 있어요. 처음부터 직접 개발 환경을 만들 수 있어요., - 강의 소개 | 인프런...

www.inflearn.com

 

728x90

'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

+ Recent posts