이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

게시글

강좌1 - Webpack - 일 년 전 등록 / 2달 전 수정

웹팩4(Webpack) 설정하기

조회수:
0
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

안녕하세요. 이번 시간에는 웹팩에 대해서 알아보겠습니다! 특히 최근에 나온 웹팩4에 대해서 다룰 겁니다. 웹팩2에서 웹팩3으로 업그레이드하는 방법을 적어둔 것이 엊그제같은데 벌써 웹팩4가 나왔네요.

웹팩3에서는 ModuleConcatenationPlugin과 import()에 청크 네임을 넣을 수 있는 부분이 추가되었습니다. 웹팩4는 생각보다 많이 바뀌었습니다. 경쟁자인 parcel을 의식하는 느낌도 드네요. 웹팩은 앞으로도 빠르게 메이저 버전을 내놓으면서 주요 기능들을 추가한다고 하네요.

요즘 웹팩이 매우 핫하지만 사람들은 웹팩에 대해 매우 어려워합니다. 저도 어려웠습니다. 왜냐면 공식 문서가 친절하지 않거든요(지금 이것도 매우 친절해진 겁니다). 그리고 모든 파일을 하나로 합친다는 개념도 잘 와닿지 않습니다. 왜 파일을 하나로 합쳐야 할까요? 바로 http 요청이 비효율적이기 때문입니다.

웹페이지는 수 많은 구성요소로 이루어져 있습니다. 항상 제 블로그를 예로 들지만, 기본적인 html, js, css 파일 외에도, 웹폰트, 이미지, json 데이터 등등 수 많은 파일들을 받아와야 합니다. http/2에서는 하나의 커넥션에 동시에 여러 파일들을 요청할 수 있습니다. 하지만 아직 보편화되어있지 않기 때문에 현재 주로 사용하는 http/1.1에서는 커넥션 하나를 열어 하나씩 요청을 보내야합니다. 하나의 요청이 끝나야 다음 요청을 보낼 수 있기 때문에 요청이 많을수록 비효율적이죠. (물론 브라우저에서 파이프라이닝이라는 꼼수로 요청을 여러 개씩 보내기는 하는데 근본적인 해결 방법은 아닙니다)

http/2는 아직은 무리고(IE에서 지원하지 않습니다) http/1은 너무 느리다면 어떻게 해야 할까요? 네... 방법이 없습니다. 개발자인 저희가 희생해야죠. 바로 요청 수를 줄이는 겁니다! 그래서 이미지는 스프라이트로 만들어 한 번에 받고, 걸프, 그런트같은 번들러로 js파일이나 css파일을 하나로 합치곤 했죠. 그러다가 이제 번들러 끝판왕 웹팩이 나왔습니다. 하나로 합쳐주면서 크로스 브라우징 대응도 해주고 압축도 해주는 등 여러모로 편한 점이 많습니다. 아래의 그림처럼 여러 파일들을 하나로 합쳐줍니다.

undefined

또 하나의 문제가, JS가 점점 중요해지면서 JS 자체만으로도 엄청난 의존 관계가 생겼습니다. ES2015 모듈, RequireJS, CommonJS, UMD같은 JS 모듈 시스템들이 나오면서 JS 파일도 다른 프로그래밍 언어처럼 모듈 개념이 생겼습니다. import나 require로 js끼리 서로 의존합니다. 특히 노드로 만들다 보면 모듈이 기본 수 백개에서 많게는 수 만개까지 갑니다. 이런 것을 하나의 JS로 합쳐주는 거죠.

하나의 파일로 합치기엔 너무 크다면 여러 개의 파일로 나눌 수도 있습니다. 보통 라이브러리들은 자주 수정되지 않기 때문에 라이브러리만 모아둔 JS 파일 하나를 만들고, 코드 수정이 자주되는 핵심 페이지는 따로 하나 만들어서 두 개의 JS가 생성됩니다. 다 여러분이 설정하기에 따라 달려있습니다.

주의할 점은 importrequire을 쓰지 않고 그냥 옛날처럼 스크립트 주르륵 불러오는 방식으로 코딩하신 분은 웹팩의 장점을 누릴 수 없다는 겁니다. 그런 분들은 모듈 시스템 부터 공부하셔야 합니다.

gulp, grunt같은 것을 사용해보셨다면 태스크러너나 번들러의 개념을 아시겠지만, 일단 아예 처음 사용하는 분이라고 생각하고 알려드리겠습니다. 일단 기본적인 Node.js랑 npm을 사용할 줄 알아야합니다. 모르신다면... NodeJS 강좌 1강을 참고하세요. 

npm i -g webpack webpack-cli && npm i -D webpack webpack-cli

명령프롬프트에서 npm으로 위와 같이 글로벌 설치하면 됩니다. 그리고 개발 환경으로도 설치해줍니다. 웹팩3까지는 webpack만 설치해도 되었는데 웹팩4부터는 webpack-cli를 같이 설치해야 커맨드라인에 webpack이란 명령어를 사용할 수 있습니다. 웹팩은 하나의 설정 파일로 모든걸 해결합니다. 살짝 grunt랑 비슷합니다. 그러면 이제 설정 파일을 만들어야겠죠? package.json이 있는 위치에 다음 파일을 만들어줍니다.

webpack.config.js

const webpack = require('webpack');
module.exports = {
  mode: 'development',
  entry: {
    app: '',
  },
  output: {
    path: '',
    filename: '',
    publicPath: '',
  },
  module: {

  },
  plugins: [],
  optimization: {},
  resolve: {
    modules: ['node_modules'],
    extensions: ['.js', '.json', '.jsx', '.css'],
  },
};

파일명이 webpack.config.js여야 웹팩이 바로 인식합니다. 이름을 다르게 하고 싶다면 (예를 들면 webpack.config.prod.js)라면 명령 프롬프트에서 실행할 때 webpack --config webpack.config.prod.js라고 --config 플래그를 사용해 경로를 알려주면 됩니다. 코드를 따라 치기보다는 원리만 알고 가시면 됩니다. 

아직까지는 빈 껍데기만 있습니다. 핵심적인 부분은 entry, output, module, plugins 이렇게 네 개입니다. 다른 부분까지 모두 알려드리기는 힘들 것 같고요. 이 위주로 설명드리겠습니다.

마지막 resolve만 먼저 설명드리자면 웹팩이 알아서 경로나 확장자를 처리할 수 있게 도와주는 옵션입니다. modules에 node_modules를 넣으셔야 디렉토리의 node_modules를 인식할 수 있습니다. 그리고 extensions에 넣은 확장자들은 웹팩에서 알아서 처리해주기 때문에 파일에 저 확장자들을 입력할 필요가 없어집니다.

mode

웹팩4에서 추가되었습니다. mode가 development면 개발용, production이면 배포용입니다. 배포용일 경우에는 알아서 최적화가 적용됩니다. 따라서 기존 최적화플러그인들이 대량으로 호환되지 않습니다.

웹팩3이라면 config 파일에서 mode랑 optimization을 빼주세요.

entry

entry 부분이 웹팩이 파일을 읽어들이기 시작하는 부분입니다. app이 객체의 키로 설정되어 있는데 이 부분 이름은 자유롭게 바꾸시면 됩니다. 저 키가 app이면 결과물이 app.js로 나오고, zero면 zero.js로 나옵니다.

{
  entry: {
    app: '파일 경로',
    zero: '파일 경로',
  }
}

위와 같이 하면 app.js, zero.js 두 개가 생성됩니다. 결과물로 여러 JS를 만들고 싶을 때 저렇게 구분해주면 됩니다. 보통 멀티페이지 웹사이트에서 entry를 여러 개 넣어줍니다. 하나의 entry에 여러 파일들을 넣고 싶을 때는 아래처럼 배열을 사용하면 됩니다.

{
  entry: {
    app: ['a.js', 'b.js'],
  },
}

위의 경우는 a.jsb.js가 한 파일로 엮여 app.js라는 결과물로 나옵니다. 이렇게 웹팩은 entry의 js 파일부터 시작해서 import, require 관계로 묶여진 다른 js까지 알아서 파악한 뒤 모두 entry에 기재된 키 개수만큼으로 묶어줍니다.

js 파일 대신 npm 모듈들을 넣어도 됩니다. 보통 @babel/polyfill이나 eventsource-polyfill같은 것들을 적용할 때 다음과 같이 합니다. 

아래는 리액트에서 주로 사용하는 예시입니다. app.jsvendor.js가 결과물로 나옵니다(웹팩4에서는 vendor를 자동으로 만들어줍니다. 다다음 강좌 를 참고하세요).

{
  entry: {
    vendor: ['@babel/polyfill', 'eventsource-polyfill', 'react', 'react-dom'],
    app: ['@babel/polyfill', 'eventsource-polyfill', './client.js'],
  },
}

이렇게 하면 각각의 엔트리가 polyfill들이 적용된 상태로 output으로 나옵니다. IE 환경에서 최신 자바스크립트를 사용해 개발하고 싶다면 저 두 폴리필을 npm에서 다운 받은 후 저렇게 모든 엔트리에 넣어주셔야 합니다.

output

이제 결과물이 어떻게 나올지 설정을 해야 합니다.

{
  output: {
    path: '/dist',
    filename: '[name].js',
    publicPath: '/',
  },
}

path랑 publicPath가 헷갈릴 수 있겠네요. path는 output으로 나올 파일이 저장될 경로입니다. publicPath는 파일들이 위치할 서버 상의 경로입니다. Express에 비유하면 express.static 경로와 비슷한 겁니다. filename을 보시면 좀 이상하게 생긴 게 있습니다. [name].js라고 되어 있는데요. 이렇게 써줘야 [name]에 아까 entry의 app이나 vendor가 들어가 app.js, vendor.js로 결과물이 나옵니다.

다른 옵션으로는 [hash]나 [chunkhash]가 있습니다. [hash]는 매번 웹팩 컴파일 시 랜덤한 문자열을 붙여줍니다. 따라서 캐시 삭제 시 유용합니다. [hash]가 컴파일할 때마다 랜덤 문자열을 붙여준다면 [chunkhash]는 파일이 달라질 때에만 랜덤 값이 바뀝니다. 이것을 사용하면 변경되지 않은 파일들은 계속 캐싱하고 변경된 파일만 새로 불러올 수 있습니다.

loader

이제부터 막강한 웹팩의 기능들이 나옵니다. 바로 로더(loader)입니다. 보통 웹팩을 사용하면 babel을 주로 같이 사용합니다. ES2015 이상의 문법들은 IE같은 구형 브라우저랑 호환시키기 위함인데요. 또는 jsx같은 react 문법을 컴파일하려고 하는 목적도 있습니다. babel을 웹팩2와 연결시켜 볼까요? 일단 설치부터 해봅니다.

npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-stage-0

일단 babel-loader와 babel-core는 필수이고요. 나머지 preset들은 선택입니다. react는 react 하시는 분만 설치하면 되고요. env는 브라우저에 필요한 ecmascript 버전을 자동으로 파악해서 알아서 polyfill을 넣어줍니다. 정말 놀라운 기술입니다. stage-0은 env보다도 더 실험적인 최신 기술을 위한 겁니다. 바벨에 대해서는 바벨 강좌 를 참고하시면 좋습니다.

{
  module: {
    rules: [{
      test: /\.jsx?$/,
      loader: 'babel-loader',
      options: {
        presets: [
          [
            '@babel/preset-env', {
              targets: { node: 'current' }, // 노드일 경우만
              modules: 'false'
            }
          ],
          '@babel/preset-react',
          '@babel/preset-stage-0'
        ],
      },
      exclude: ['/node_modules'],
    }],
  },
}

env만 좀 독특하죠? 'env' 다음에 target과 modules: false라는 옵션이 들어 있습니다. option의 option인 셈이죠. modules를 false로 해야 트리 쉐이킹이 됩니다. es2015 모듈 시스템 import되지 않은 export들을 정리해주는 기능이죠. 꼭 트리 쉐이킹을 사용하세요! 단, commonJS나 AMD, UMD같은 모듈 시스템을 사용해야하는 클라이언트에서는 쓰면 제대로 처리되지 않습니다. 위의 예시는 서버의 예시이기 때문에 클라이언트 상황을 알아보려면 다음 강좌 를 참고하세요!

혹시 다른 사이트에서 rules나 use 대신 loaders를 쓰고, options 대신 query를 쓰는 곳이 있다면, 웹팩1에 대한 강좌입니다. 웹팩2에서 바뀌었습니다. 이제는 그렇게 쓰면 에러가 발생합니다.

위와 같이하면 test 정규식조건(js나 jsx 파일)에 부합하는 파일들을 loader에 지정한 로더가 컴파일해줍니다. options는 로더에 대한 옵션으로 아까 설치한 presets들을 적용하고 있는 게 보입니다. exclude는 제외할 폴더나 파일로, 바벨로 컴파일하지 않을 것들을 지정해줍니다. 바벨로는 컴파일하지 않지만 웹팩으로는 컴파일합니다. 반대로 include로 꼭 이 로더를 사용해서 컴파일할 것들을 지정해줄 수도 있습니다.

웹팩2에서 변경된 점은 loader 옵션에 babel 대신 babel-loader 전체 이름을 적어주어야 한다는 점(resolve 옵션에서 예전처럼 돌아갈 수 있습니다)과 json-loader가 내장되어 따로 적어줄 필요가 없다는 점입니다.

plugin

플러그인은 약간 부가적인 기능입니다. 다양한 플러그인들이 나와있는데 이를 사용하면 효과적으로 번들링을 할 수 있습니다. 예를 들면 압축을 한다거나, 핫리로딩을 한다거나, 파일을 복사하는 등의 부수적인 작업을 할 수 있습니다. 다양한 플러그인들이 패키지로 존재하기 때문에 쇼핑하듯 골라보세요!

{
  plugins: [
    new webpack.LoaderOptionsPlugin({
      minimize: true,
    }),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production'), // 아래 EnvironmentPlugin처럼 할 수도 있습니다.
    }),
    new webpack.EnvironmentPlugin(['NODE_ENV']), // 요즘은 위의 DefinePlugin보다 이렇게 하는 추세입니다.
  ],
}

대표적인 웹팩 기본 제공 플러그인들입니다. LoaderOptionsPlugin은 로더들에게 옵션을 넣어주는 플러그인이고요. ModuleConcatenationPlugin은 웹팩3에서 새로 나왔는데 웹팩이 변환하는 자바스크립트 코드를 조금이나마 더 줄여줍니다. UglifyJsPlugin이 압축, console 제거, 소스맵 보존 등을 하는 플러그인이고, DefinePlugin은 JS 변수를 치환해주는 플러그인입니다. 참고로 UglifyJsPlugin은 es6 이상의 코드를 컴파일하지 못하는 버그가 있기 때문에 uglifyjs-webpack-plugin을 직접 npm에서 설치하여 대신 사용하면 됩니다. 이외에도 BannersPlugin, IgnorePlugin, EnvironmentPlugin, ContextReplacementPlugin 등 기본 제공 플러그인도 어마어마합니다.

웹팩3에서 플러그인들의 변경점이 있습니다. DedupePlugin은 사라졌고, OccurrenceOrderPlugin은 기본으로 켜져 있으니 더 이상 추가하지 마세요.

웹팩4에서는 ModuleConcatenationPlugin과 UglifyJsPlugin, NoEmitOnErrorsPlugin, NamedModules 플러그인이 모두 사라지고 wepback.config.js에 optimization 속성으로 대치되었습니다.

optimization

웹팩4에서 최적화 관련 플러그인들이 모두 이쪽 속성으로 통합되었습니다. 나중에 나오는 CommonsChunkPlugin도 사라지고 여기에 병합되었습니다.

{
  optimization: {
    minimize: true/false,
    splitChunks: {},
    concatenateModules: true,
  }
}

예를 들자면 minimize가 UglifyJsPlugin을 계승하고, splitChunks가 CommonsChunkPlugin을 계승합니다. 또한 mode가 production일 때는 자동으로 이 두 속성이 켜집니다. concatenateModules 옵션은 ModuleConcatenationPlugin을 계승합니다.

용량 관계로 다음 강좌에서 계속 이어집니다! 이번 시간에 주로 js를 번들링하는 방법을 살펴봤다면, 다음 시간에는 css랑 기타 파일들 번들링 방법을 알아보겠습니다.

투표로 게시글에 관해 피드백을 해주시면 많은 도움이 됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright © 2016- 무단 전재 및 재배포 금지

댓글

11개의 댓글이 있습니다.
한 달 전
웹팩4에 jquery는 붙일 수 없나요? 다들 안된다고 해서요 퍼블리셔분이 jquery만 할 줄 안다고 하셔서요 웹팩4는 쓰고 싶고요 vue webpack4 jquery 이렇게 구성하고 싶은데요
한 달 전
검색 결과 제이쿼리 되는 것 같습니다. npm에서 jquery받으신 후에 require하시면 되고, 웹팩 설정에서 expose-loader인가 이걸 쓰네요.
2달 전
좋은 설명 감사합니다.
그런데 웹팩 설치시 글로벌모드(전역)로 설치하는 경우는 어떤 상황에서 그런지요?
예제에서는 전역과 지역으로 모두 설치했는데 어떤 이유인가요?
2달 전
보통 전역으로 설치하는 건 터미널에 명령어로 사용하기 위함입니다. 지역으로 설치하는 건 프로젝트 내에서 사용하기 위함이고요.
2달 전
중간중간 강좌 링크들이 잘 안열리는데 게시글이 정리된건가요? 그리고 댓글남기려고 회원가입 하려는데 SNS 가입도 안되고있네요
2달 전
수정했습니다. PWA 적용 중에 에러가 발생했습니다. ㅠㅠ
4달 전
장문의 친절한 글 너무 감사드립니다. 좋은 공부가 되었습니다.
4달 전
감사합니다!
4달 전
좋은 글 올려주셔서 감사합니다. 항목 하나하나 찾아서 보기가 힘들었는데 너무 좋았어요.
궁금한 점이 있는데요, webpack bundle analyzer라는 걸 써서 제가 만든 앱 번들을 확인했더니 8MB(...)가까이 되더군요...
개발모드에서 확인한 거긴 한데 여기서 궁금한 점이
1. 개발모드에서는 프로덕션 모드보다 용량을 조금 덜 신경써도 되나요?(당연히 용량을 줄여야겠지만)
2. 최적화 하는 법을 찾다가 개발자 도구로 볼 수 있는 waterfall chart에 관심이 생겼는데요, 가장 이상적인 waterfall의 모습은 어떤 건지 여쭤봐도 될까요? 제가 만든 앱은 중간 중간에 빈 공간이 많은데 이걸 줄일 수 있는 어떤 기능 같은 게 있을까요?
4달 전
답장이 너무 늦었네요. 개발모드는 용량을 신경 안 쓰셔도 됩니다. 프로덕션 모드만 용량을 신경 쓰시면 되고요. waterfall은 상황에 따라 달라서 제가 답하기 힘드네요. http2를 쓰느냐 아니냐도 관련이 있고요. 빈 공간이 왜 생기는지는 performance 탭에서 찾아보셔야할 것 같습니다.
4달 전
지금 시간에..ㅎㄷㄷ 매번 이렇게 답 달아주셔서 감사드립니다! 얼른 주무세요! 항상 감사드립니다
5달 전
웹팩 3 사용시 플러그인쪽이 오묘해서 어떻게 써야할 지 모르겠네요 ㅠㅠ
5달 전
플러그인은 일단 추가하고 보시면 됩니다. 에러가 나면 빼고요. 웹팩 3에서는 배포 환경일 때 ModuleConcatenationPlugin과 UglifyJsPlugin 다시 넣어주셔야 합니다.
5달 전
안녕하세요 ㅋㅋㅋ 게시글이 웹팩 3였다가 4로 바뀌어서 댓글답니다! 트렌디하게 잘 블로그를 관리해주시네요. 사실 작성자님 글들 보면서 웹팩을 만져봤다가 하필 그날에 웹팩이 새로운 버전으로 바뀌어서 삽질을 하구 (작성자님 잘못이 아닙니다ㅠㅠ) 웹팩 버전을 다운시켜야겠거니 했습니다.
작성자님 예전버전 설치 방법을 알려주시고(저는 알고 있습니다) 그냥 숫자 3로 남겨놓으시는게 어떨련지요? 아무튼 전 3버전으로 받고 이 글 보면서 잘 따라해보도록 하겠습니다 ㅎㅎ
8달 전
강의 잘읽었습니다 api server만 따로 만들려구 합니다 웹팩을 통해 서버쪽 핫리로드도 가능한가요?? 아니면 테스크 러너를 써야하는지요...?
8달 전
서버쪽 핫 리로드는 따로 하셔야합니다. 노드면 nodemon같은 걸로요
9달 전
항상 어려웠던 웹팩에 대해서 이제 조금 알것같습니다 감사합니다
일 년 전
항상 유용한 글 감사드립니다. 제가 아직 grunt나 gulp같은 빌드 툴을 본격적으로 사용하지 않아서, 궁금한 게 하나 있는데요. 이렇게 자바스크립 프로젝트를 번들링하기 시작하면 프로젝트 구조가 제대로 갖추어지 않았다면 프로젝트 사이즈가 커지면 빌드타임도 상당히 길어질 것 같은데요.J2EE에서 작은 프로젝트에서 만들어진 jar를 하나의 큰 war로 만드는 것과 비슷하게 가고 있다는 느낌이 드는데요. 이부분이 제가 j2ee에서 엄청나게 싫증을 느끼는 부분인데요. 혹시 웹팩같은 번들툴을 쓰면서 제가 우려하는 것처럼 문제가 될 만한 부분이 있을까요?
일 년 전
역시 빌드타임이 문제입니다. 보통 배포용이면 최소 3~4분은 걸립니다. 제 블로그도 그렇고요. 하지만 개발 시에는 핫 리로딩같은 기능이 있어서 실시간으로 변경되기 때문에 그렇게 큰 문제는 아니라고 생각합니다. 어차피 배포는 한 번만 하면 되기 때문이죠.
일 년 전
웹팩강좌보고 자바스크립트 window,document강좌 부터 쭉 봤습니다 이해가 잘되어 도움이 많이 됫습니다 감사합니다
조그만 부탁을 드릴것이 잇다면 웹팩강좌는 자그마한 프로젝트를 이용하여 실제 사용하는 실습을 통해 배우고 싶습니다. 강좌를 보고 따라해보았지만 약간 막막하네요 ㅠㅠ 부족한탓에 이리 댓글답니다. 항상 좋은 강좌만들어주셔서 감사합니다
일 년 전
제 강좌가 따라하면서 배우는 걸 의도한 게 아니라서요 ㅠㅠ 실제 프로젝트를 보시고 싶으시면 제 github의 react-vote나 react-filepicker 코드에서 webpack2를 사용하는 것을 참고하시면 될 거 같습니다.