Angular에 Webpack 적용 후기 & Tip 기술 이야기 2017.12.10 21:55

Angular를 사용한 웹사이트를 만들었는데, 막상 Release 할 때 첫화면 로딩 성능이 잘 안나와서 급하게 Webpack을 적용해서 성능을 개선했던 기억을 간단하게나마 정리해본다.


기술적인 내용은 참고했던 사이트의 소개 위주로 간단하게 적고, 경험적인 내용을 추가로 정리해서 덧 붙인다.

우선 기술적인 내용!

Webpack으로 AOT Compile and Bundling!

결론적으로! Webpack 적용해서 index.html 에는 bundled js 파일만 남겼다.

우선 AOT가 무엇인지 알아야 하는데, Angular는 2가지 compile 방법을 제공한다. 성능 개선이 목적이었기에 당연히 빠르다던 AOT를 적용했다.

1.Just-in-Time (JIT), which compiles your app in the browser at runtime
2.Ahead-of-Time (AOT), which compiles your app at build time.

AOT 적용에는 2가지 문제가 있었는데, 하나는 설정이 어렵다는 것이고, 다른 하나는 어렵게 설정을 성공해도 막상 눈에띄는 성능 개선을 느낄 수 없다는 것이다. 설정 방법에 대해서 언급하기가 어려운게, 방법이 계속 변경되어서ㅠ 이미 내가 사용했던 방법은 공식 사이트에서 자취를 감췄다.ㅠ

The Ahead-of-Time (AOT) Compiler

지금 나와있는 방법도 약간 덧없는 이유가 어차피 Webpack을 적용하면 webpack plugin을 통해서 컴파일한다. 다시말해 성능 개선을 위한 작업으로 AOT를 적용할 거면, 그냥 첨부터 Webpack을 적용하는게 좋다.

Webpack을 적용해서 빌드하면 Webpack Plugin을 통해서 AOT 컴파일된 js 파일로 번들링이 되고, index.html에 기존에 JIT를 위해 존재했던 systemjs 관련 설정들 없이 번들링된 js파일만 로딩하면 된다.

다양한 방법에 대해서 정리를 잘 해둔 글을 소개한다.
Multiple solutions for Angular Ahead of Time (AOT) Compilation

Lazy Loading

Lazy loading을 적용해서 메뉴를 누를때 해당 Component의 js가 로딩도록 했다.

사실, 성능 개선을 위해서 ngc로 AOT를 하고, rollup 으로 번들링을 먼저 했었다. Angular.io의 가이드는 그것으로 충분할 것 같이 설명이 되어 있었기에, 차근 차근 가이드대로 설정했다. 하지만, 그것만으로는 안된다.ㅠ 첫화면 로딩 속도 개선을 위해서 Lazy Loading이 꼭 필요하다.

잘 설명된 글이 있어서 소개하고 넘어가겠다. 예제도 github에 올려놔서, 정말 도움이 많이 되었다.ㅠ 공식 사이트보다 훨 낫다.

Angular2 AOT with Webpack and Lazy Loading

Lazy loading: code splitting NgModules with Webpack

angular-lazy-load-demo(github)

Common module을 여러개의 Chunk로 나누는 Tip!

CommonsChunkPlugin 을 사용해서 Bundling 된 Output js 파일을 Chunk로 쪼개야 로딩 속도를 좀 더 높일 수 있다. 근데, 가이드 대로만 하면 여전히 Common module에 대한 Bundled js file이 상당히 큰 사이즈를 갖는다. 그래서 더 나눌 수 없는지...이것 저것 해봤는데, 허허...된다! CommonsChunkPlugin을 여러개 사용해봤다.

여러개의 CommonsChunkPlugin을 다른 방식으로 사용해보자!

CommonsChunkPlugin공식사이트에도 여러개 사용하는 방법에 대해서 나와있지만, 그것은 Entry도 여러개인 경우다. 나는 Entry가 하나인 상황에서 여러 설정을 적용해 봤다.

CommonsChunkPlugin 하나만 사용 했을때

아래와 같이 설정하였고,

//webpack.config.js
new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    minChunks: function(module){
        return module.context && /node_modules/.test(module.context );
    }
}),
new ngToolsWebpack.AotPlugin({
    tsConfigPath: './tsconfig.webpack.json'
}),

아래와 같은 결과가 나왔다.

Version: webpack 3.10.0
Time: 13070ms
Asset           Size      Chunks                  Chunk Names
app.0-chunk.js   169 kB       0  [emitted]
app.1-chunk.js   112 kB       1  [emitted]
app.2-chunk.js  82.8 kB       2  [emitted]
app.3-chunk.js  8.07 kB       3  [emitted]
app.4-chunk.js  3.51 kB       4  [emitted]
app.main.js       54 kB       5  [emitted]         main
app.vendor.js    529 kB       6  [emitted]  [big]  vendor

app.vendor.js529 kB 사이즈로 매우 크다.

CommonsChunkPlugin 여러개 사용 했을때

common chunk를 나누기 위해서 아래와 같이 설정하였고,

//webpack.config.js
new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    minChunks: function(module){
        return module.context && /node_modules/.test(module.context );
    }
}),
new webpack.optimize.CommonsChunkPlugin({
    name: 'rxjs',
    minChunks: function(module){
        return (module.context && (/node_modules\/rxjs/.test(module.context) || /@angular/.test(module.context)));
    }
}),
new webpack.optimize.CommonsChunkPlugin({
    name: 'angular',
    minChunks: function(module){
        return module.context && /@angular/.test(module.context);
    }
}),
new webpack.optimize.CommonsChunkPlugin({
    name: 'angular.core',
    minChunks: function(module){
        return module.context && (/@angular\/core/.test(module.context) || /@angular\/common/.test(module.context));
    }
}),
new ngToolsWebpack.AotPlugin({
    tsConfigPath: './tsconfig.webpack.json'
}),

아래와 같은 결과가 나왔다.

Hash: 1774b1f29d33e5e906d8
Version: webpack 3.10.0
Time: 13422ms
    Asset                 Size       Chunks           Chunk Names
    app.0-chunk.js        169 kB        0  [emitted]
    app.1-chunk.js        112 kB        1  [emitted]
    app.2-chunk.js        82.8 kB       2  [emitted]
    app.3-chunk.js        8.07 kB       3  [emitted]
    app.4-chunk.js        3.51 kB       4  [emitted]
    app.rxjs.js           195 kB        5  [emitted]  rxjs
    app.main.js           54 kB         6  [emitted]  main
    app.vendor.js         35.8 kB       7  [emitted]  vendor
    app.angular.js        134 kB        8  [emitted]  angular
    app.angular.core.js   164 kB        9  [emitted]  angular.core

app.vendor.jsapp.rxjs.js, app.angular.js, app.angular.core.js 로 더 나눠서 로딩속도를 개선했다.

기술적인것은 간단하게만 적으려고 했는데, 후...길어졌네. 지금은 또 상황이 달라졌을 수도 있는데, 다양한 plugin이나 라이브러리, 프레임웤등을 적절히 사용해서 최고의 결과를 도출하는 것이 중요하다! 지금 적용된 것도 여러가지 Webpack Plugin을 스파이킹하보고 그 중 하나를 선택한 것이다.

그것을 적용하기까지 이야기

다양한 솔루션은 축복이자 고통이다.

처음 이 문제를 접했을때 큰 걱정이 없었다. Angular 2가 이미 충분히 많이 사용하고 있는 프레임워크니까 당연히 이정도 솔루션은 있을 줄 알았다. 근데, 문제는 솔루션이 너무 많다는 것이다.ㅠ 심지어 공식 사이트 조차도 이렇게 할 수도 있고, 이렇게 해도 된다는 식으로 가이드가 되어 있었다. (번들링을 위해 rollup이나 webpack을 사용할 수 있다고...결국 둘다 해보고 결정ㅠ)

여러가지가 있는 것은 좋은데, 어떤 것이 좋은지, 지금 우리 상황에 어떤것을 적용할 수 있을지 명확하지가 않아서 힘들었다. 아픈만큼 성숙한다고 했던가. 엄청 고생했고, 그 과정에서 이래 저래 많은 것을 알게되었다.

Agile도 처음에 끝까지 고민해보자.

난 Agile을 무척 좋아하는데, 어쩌다보니 Agile이 눈앞에 것만, 단기 목표에만 집중하는 것처럼 진행 된 부분이 있다. 반복적으로, 점진적인 개선을 이뤄나가는 방식에는 동의하지만 그렇다고해서 나중 일은 나중에 생각하자는 것은 정말 경계해야 할 일이다.

개발 환경을 꾸릴때 최종 Deployment까지 준비해야 하며, 각 단계의 리스크 관리가 필요하다.

Agile에도 Architect이 필요하고, 설계과정이 충분히 필요하다.

새삼 다시 또 고맙다.

정리를 잘 해주신 블로거들에게 감사하고 또 감사드린다. angular.io를 엄청 신뢰하고 있었는데, 큰 도움이 못 되었다. 오히려, 블로거들의 글이 없었더라면 목표를 달성 할 수 없었을 것이다. 여러 가지 솔루션의 비교와 고민, 그리고 주의할 점과 예제까지! 매번 고마움을 느끼고, 그 마음으로 나도 이렇게 글을 남긴다.

누군가에게 도움이 되길 바라며...

티스토리 툴바