웹팩(webpack) 이란?

in #javascript7 years ago (edited)

웹팩은 웹에서 사용되는 모든 자원(assets)을 번들링 해주는 도구입니다. 번들링의 개념은 여러개의 파일 중에 종속성이 존재하는 파일을 하나의 파일로 묶어 패키징을 시키는 과정을 의미합니다.

1번 파일에서 2번 파일에 있는 함수를 호출해야 할 경우 스크립트를 불러오는 순서를 2번 파일을 제일 상단에 올려놓고 1번 파일을 그 다음에 불러와지도록 선언을 했는데 이제는 모듈이라는 개념을 사용해 1번 파일에서 2번 파일을 불러오기만 하면됩니다.

번들링을 함으로써 파일은 하나로 합쳐지고 네트워킹 요청횟수는 줄어들게 됩니다. 그리고 중복된 소스코드도 최소화하고 모듈 개념을 사용하기 때문에 글로벌이 오염되지도 않습니다.

그리고 웹팩을 사용하면 자바스크립트 간의 종속성 뿐만 아니라 자바스크립트 내에서 필요한 css나 img와 같은 파일(.css, .jpg)도 번들링해서 하나의 파일로 합쳐줘서 네트워크 요청을 최소화 시킬 수 있습니다.

웹팩을 사용해보기

웹팩은 노드 모듈중에 하나입니다 그렇기 때문에 기본적으로 Node.js 가 설치되어있어야 합니다.
노드js가 설치 되어 있으신분은 webpack을 설치해주시면 됩니다.

npm i webpack 

그러면 webpack명령어를 사용할 수 있는데 webpack으로 어디를 번들링해서 패키징을 할지 설정을 해줘야 합니다.
프로젝트 경로 가장 상단에 webpack.config.js 라는 파일을 만들어주세요

const path = require('path');

module.exports = {
    entry: path.resolve(__dirname, 'src', 'index.js'), 
    output: { 
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    }
}

entry로 지정된 파일을 읽어 들이기 시작하며 import 혹은 require 등 다른파일을 불러오고 있는 파일들을 추적해서 하나의 파일로 만들어줍니다.

번들링 된 파일은 output에 설정한대로 파일이 생성되어 집니다.

plugins

웹팩에는 플러그인이라는 개념이 있는데 소스코드가 번들링 될 때 추가적인 작업을 더 진행하는 개념으로 이해하시면 좋을것 같습니다.
대표적인 예로 웹팩에서 제공해주는 UglifyJsPlugin이 존재합니다. 해당 플러그인은 번들링 될때 소스코드를 난독화 시켜서 번들링 하기 때문에 상용화를 할때는 해당 플러그인을 통해서 번들링 후 배포해야합니다.

const webpack = require('webpack');
  entry: ..,
  output: ..,
  plugins: [
        new webpack.optimize.UglifyJsPlugin()
  ]
}

module

webpack은 단독적으로 사용이 되면 패키징을 해주는 기능만 해주는 도구로써 보셔도 무관하지 않습니다. 여러개의 파일을 하나로 합쳐줄 뿐입니다.

하지만 module이라는 기능을 제공해서 다른 트랜스파일러와 함께 사용함으로써 더 강력한 기능을 제공해줄 수 있습니다.
가장 많이 사용되는 babel 과 함께 사용하겠습니다.
babel에 대한 설명은 여기를 참고해주세요.

webpack 에서 babel을 사용하기 위해서는 babel-loader와 babel-core가 필요합니다. babel-loader는 내부적으로 babel-core를 이용해서 소스코드를 변환하고 변환된 소스코드를 웹팩에 전달하는 역할을 수행합니다.

npm i -D babel-core babel-loader babel-preset-es2015

소스코드를 구버전의 브라우저에서 지원해주기 위해 babel-preset-es2015도 같이 설치했습니다.

//.babelrc
{  "presets": ["es2015"] }

//webpack.config.js
const path = require('path');

module.exports = {
    entry: path.resolve(__dirname, '..', 'src', 'index.js'),
    output: {
        path: path.resolve(__dirname, '..', 'dist'), 
        filename: 'bundle.js'
    },
    module: {
        rules: [{ 
            test: /\.js$/, 
            use: 'babel-loader' 
        }]
    }
}

//index.js
import P from './module'

const p = new P();

p.print();

//module.js
export default class P{
    print(){
        console.log('P');
    }
}

이렇게 설정하면 webpack으로 파일이 하나로 만들어지고, 소스코드 내부를 보면 class문법이 function으로 바껴져 있는걸 확인할 수 있습니다.

번들링된 파일 디버깅(webpack sourceMap debug)

웹팩으로 파일이 번들링 되면 실제로 브라우저에서 돌아갈때 내가 작성한 소스가 변환이 되어 있기도 하고, 작성하지 않은 소스코드가 엄청나게 많이 들어가 있습니다. 그렇기 때문에 디버깅 할때 불편할 수 밖에 없는데, 그런 문제를 해결하기 위해 webpack에서는 devtools을 제공해줍니다.
devtools에는 source map을 지정할 수 있습니다.

module.exports = {
  entry:..,
  output: ..,
  module: ..,
  devtool: 'inline-source-map'
}

소스맵을 지정하면 개발자 도구를 열었을때 webpack이라는 경로가 하나더 생겨 나고 그 안에 작성했던 원본소스가 있습니다.
원하는 곳에 브레이크 포인트를 걸면 해당 부분에서 디버깅을 할 수 있어서 sourcemap을 사용하면 번들링이 됬을때 디버깅이 어려울까봐 걱정하지 않으셔도 됩니다.

주의할 것은 UglifyJsPlugin과 같이 사용하면 source-map을 사용하지 못하니까 주의해주세요.

webpack 설정파일 관리하기(webpack-merge)

번들링 된 파일은 개발환경과 배포환경을 고려해야 합니다.
개발환경에서는 난독화 되지 않은 파일 또는 소스맵(변환된 소스가 변환되기 전 소스코드의 위치를 알려주는 파일)을 제공하고, 개발시 필요한 devserver도 제공하기도 합니다. 하지만 배포환경에서는 난독화된 파일만을 제공해야 하기 때문에 설정이 달라질 수 밖에 없습니다.

그렇기 때문에 공통된 부분은 하나의 파일로 관리하고 개발 환경과 배포환경에서 실행되어져야되는 설정을 따로 분리해야 합니다.
하지만 공통적인 부분은 같이 사용해야 하는데 webpack-merge를 사용하시면 좀 더 쉽게 설정할 수 있습니다.

npm i -D webpack-merge 

webpack-merge의 설치가 끝나면 다음과 같이 파일을 나눠줘야 합니다.

//webpack.config.base.js
const path = require('path');
const webpack = require('webpack');

module.exports = {
    entry: path.resolve(__dirname, 'src', 'index.js'),
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    }
}

//webpack.config.dev.js
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config.base');

module.exports = merge(baseConfig, {
    devtool: 'inline-source-map'
});

//webpack.config.prod.js
const merge = require('webpack-merge');
const webpack = require('webpack');
const baseConfig = require('./webpack.config.base');

module.exports = merge(baseConfig, {
    plugins: [
        new webpack.optimize.UglifyJsPlugin()
    ]
});

webpack을 실행할 때 --config옵션으로 원하는 설정파일의 경로를 넣어주기만 하면 개발환경과 배포환경을 분리할 수 있습니다.

잦은 빌드와 재실행 새로고침 해결하기 (webpack-dev-server)

webpack으로 설정되어 있는 소스코드를 수정하기 위해서는 수정후 다시 빌드 및 새로고침을 해줘야 합니다.
그렇게 되면 개발의 생산성도 떨어지고 빌드하는 것도 계속 기다려야되기 때문에 매우 시간을 많이 뺏깁니다.

webpack-dev-server를 이용하면 해당 문제를 해결할 수 있습니다.
webpack-dev-server는 개발환경에서 설정되기 때문에 위에서 설정한 개발환경에 설정을 해주면 됩니다.
일단 설치를 먼저 진행해주세요

npm i -D webpack-dev-server

그 다음에 webpack.config.dev.js 를 수정해봅시다.

const merge = require('webpack-merge');
const baseConfig = require('./webpack.config.base');
const path = require('path');
const webpack = require('webpack');

module.exports = merge(baseConfig, {
    devServer: {
        hot: true,
        filename: 'bundle.js',
        compress: true,
        publicPath: '/',
        contentBase: path.resolve(__dirname, '..', 'dist'),
        port: 3000
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ],
    devtool: 'inline-source-map'
});

보시면 plugin에 HotModuleReplacementPlugin을 사용하고 있는것을 볼 수 있는데 이것은 특정 파일이 수정이 되면 자동으로 반영을 해주기 위한 플러그인입니다.

소스코드가 수정되면 재실행하고 새로고침을 해줄 필요가 없게 되고 빌드 또한 수정된 파일만 빌드를 하기 때문에 훨씬 빨라집니다.

실행하는 방법은 npx webpack-dev-server --config ./webpack.config.dev.js와 같이 실행할 수 있습니다. (스크립트로 만들어놓고 사용하세요)

최종적으로

이런 모습이 나오고 index.js가 module.js를 참조하며 webpack-dev-server를 이용해서 실행하면 소스코드가 수정될때 자동으로 반영되서 실행이 되면 됩니다.