이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ
강좌1 - Webpack - 9달 전 등록 / 5달 전 수정

웹팩3(Webpack) 설정하기

조회수:
0

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

안녕하세요. 이번 시간에는 웹팩에 대해서 알아보겠습니다! 특히 최근에 나온 웹팩3에 대해서 다룰 겁니다. 웹팩2가 나온지 반 년이 안 되었는데 벌써 웹팩3가 나왔네요. 웹팩3는 웹팩2와 크게 달라진 부분이 없기 때문에 그냥 편하게 업그레이드 하시면 됩니다. ModuleConcatenationPlugin과 import()에 청크 네임을 넣을 수 있는 부분이 추가되었습니다. 앞으로도 빠르게 메이저 버전을 내놓으면서 주요 기능들을 추가한다고 하네요.

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

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

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

또 하나의 문제가, 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 && npm i -D webpack

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

webpack.config.js

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

  },
  plugins: [],
  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에 넣은 확장자들은 웹팩3에서 알아서 처리해주기 때문에 파일에 저 확장자들을 입력할 필요가 없어집니다.

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가 결과물로 나옵니다.

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

이렇게 하면 각각의 엔트리가 polyfill들이 적용된 상태로 나옵니다. 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: [
          'env', {
            targets: { node: 'current' }, // 노드일 경우만
            modules: 'false'
          },
          'react',
          '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.optimize.ModuleConcatenationPlugin(),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true,
      compress: {
        warnings: 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 변수를 치환해주는 플러그인입니다. 이외에도 BannersPlugin, IgnorePlugin, EnvironmentPlugin, ContextReplacementPlugin 등 기본 제공 플러그인도 어마어마합니다.

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

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


오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요~! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright © 2016- 무단 전재 및 재배포 금지
3개의 댓글이 있습니다.
18시간 전
항상 어려웠던 웹팩에 대해서 이제 조금 알것같습니다 감사합니다
5달 전
항상 유용한 글 감사드립니다. 제가 아직 grunt나 gulp같은 빌드 툴을 본격적으로 사용하지 않아서, 궁금한 게 하나 있는데요. 이렇게 자바스크립 프로젝트를 번들링하기 시작하면 프로젝트 구조가 제대로 갖추어지 않았다면 프로젝트 사이즈가 커지면 빌드타임도 상당히 길어질 것 같은데요.J2EE에서 작은 프로젝트에서 만들어진 jar를 하나의 큰 war로 만드는 것과 비슷하게 가고 있다는 느낌이 드는데요. 이부분이 제가 j2ee에서 엄청나게 싫증을 느끼는 부분인데요. 혹시 웹팩같은 번들툴을 쓰면서 제가 우려하는 것처럼 문제가 될 만한 부분이 있을까요?
5달 전
역시 빌드타임이 문제입니다. 보통 배포용이면 최소 3~4분은 걸립니다. 제 블로그도 그렇고요. 하지만 개발 시에는 핫 리로딩같은 기능이 있어서 실시간으로 변경되기 때문에 그렇게 큰 문제는 아니라고 생각합니다. 어차피 배포는 한 번만 하면 되기 때문이죠.
9달 전
웹팩강좌보고 자바스크립트 window,document강좌 부터 쭉 봤습니다 이해가 잘되어 도움이 많이 됫습니다 감사합니다
조그만 부탁을 드릴것이 잇다면 웹팩강좌는 자그마한 프로젝트를 이용하여 실제 사용하는 실습을 통해 배우고 싶습니다. 강좌를 보고 따라해보았지만 약간 막막하네요 ㅠㅠ 부족한탓에 이리 댓글답니다. 항상 좋은 강좌만들어주셔서 감사합니다
9달 전
제 강좌가 따라하면서 배우는 걸 의도한 게 아니라서요 ㅠㅠ 실제 프로젝트를 보시고 싶으시면 제 github의 react-vote나 react-filepicker 코드에서 webpack2를 사용하는 것을 참고하시면 될 거 같습니다.