블로그 이미지
윤영식
Frontend Application Architecter, Full Stacker, KnowHow Dispenser and Bike Rider

Publication

Statistics Graph

Recent Comment

2017.09.04 15:51 JHipster

Angular 최신 버전이 v5 beta-6를 향해 가고 있다. 9월에는 v5 release가 될 것으로 보인다. Angular에서 툴 기능으로 @angular/cli를 제공하고 있는데 오늘은 이것의 내부구조에 대해 연구해 본다. 



@angular/cli 설치 및 프로젝트 생성

설치는 npm 또는 yarn을 이용한다.

$> npm i -g @angular/cli


or 


$> yarn add global @angular/cli


Angular 기반 프로젝트를 생성해 보자.

$> ng new jamong


실행해 보자

$> ng serve




폴더 구조

angular/cli는 Automation Tool 과 Module loader로 webpack을 사용하고 있다. 웹팩의 환경파일을 루트 폴더에 생성한다. 
$> ng eject
=====================================================
Ejection was successful.

To run your builds, you now need to do the following commands:
   - "npm run build" to build.
   - "npm test" to run unit tests.
   - "npm start" to serve the app using webpack-dev-server.
   - "npm run e2e" to run protractor.

Running the equivalent CLI commands will result in an error.

=====================================================
Some packages were added. Please run "npm install".


설명에 따라 추가 명령을 수행한다. 

$> npm run build && npm start


// package.json의 build 와 start 스크립트 설정내역

  "scripts": {

    "ng": "ng",

    "start": "webpack-dev-server --port=4200",

    "build": "webpack",

    "test": "karma start ./karma.conf.js",

    "lint": "ng lint",

    "e2e": "protractor ./protractor.conf.js",

    "pree2e": "webdriver-manager update --standalone false --gecko false --quiet"

  },



webpack 오류가 난다면 webpack이  global설치가 않되어 있기때문이다. webpack을 설치한다. 

$> yarn add webpack --dev

또는

$> yarn add global webpack 

또는 

$> npm install -g webpack





Webpack 환경파일

webpack의 기본은 초기 참조할 entry파일과 결과 번들파일을 지정하는 것이다. 

$> webpack <entry file path> <output bundle file path>


예) webpack ./entry.js bunlde.js


간단한 번들링은 CLI를 사용해도 무방하지만 복잡한 옵션이 적용되어야 한다면 환경파일을 사용한다. 예) webpack.config.js 아래 예에서 ouput의 filename에 [name]은 entry의 'app' 키값이다. 

module.exports = {

  context: __dirname + '/app',

  entry: {

    app: './app.js'

  },

  output: {

    path: __dirname + '/dist',

    filename: '[name].bundle.js'

  }

}


환경설정 자세한 옵션은 공식문서를 참조한다. 여기서 주의할 것은 entry는 String, Object, Array로 설정가능하다. 

- String: 하나만 설정

- Array: output 번들링 파일에 순서적으로 합쳐진다.

- Object: SPA처럼 index.html에 적용되는 것이 아니라, index1.html 또는 index2.html 등 output이 각각의 html에 포함될 때 사용한다. 





Webpack Loader

Webpack에는 자바스크립트가 아닌 확장형식의 파일을 자바스크립트에서 동작할 수 있도록 해주는 로더(Loader)가 있다. 즉, 모든(CSS, Images, HTML...)을 모듈로 취급하게 해주는 핵심역할을 로더가 수행한다. 말 그대로 다양한 파일 확장자의 Module Loader의 줄임말이라 보면된다. (전체 목록 참조) 만일 설정중에 module, rules를 사용하지않고 loaders를 쓰거나, options대신 query를 쓰면 webpack 1에 대한 설명이다.


- 스타일: style, css

- 변환: typescript, coffeescript, ES2015


환경파일안에 module 밑으로 설정한다. 


module.exports = {

  entry: ...

  output: ...

  module: {

    rules: [

      {

        test: /\.css$/,

        use: [ 'style-loader', 'css-loader']

      }

    ]

    ...

  }


}


특히 Angular 개발시에는 Typescript를 사용하므로 sourcemap을 남기려면 'source-map-loader'를 module 프로퍼티에 설정한다. (sourcemap에 대해 webpack3에서는 plugin으로 설정도 가능하다.) enforce 설정값을 "pre"로 하면 자바스크립트 변환전에 sourcemap 을 만든다.

{

        "enforce": "pre",

        "test": /\.js$/,

        "loader": "source-map-loader",

        "exclude": [

          /(\\|\/)node_modules(\\|\/)/

        ]

}


개발시에는 Node.js기반의 webpack-dev-server를 통해 개발 페이지를 테스트할 수 있다. 옵션은 --inline (전체 페이지 리로딩) --hot (변경 컴포넌트만 리로딩)한다. 

// 전체 및 부분 리로딩 옵션 설정

$> webpack-dev-server --inline --hot




Webpack Plugins

플러그인은 Output 번들의 Chunk 또는 Compilation 레벨 파일에 대한 추가 작업을 수행한다. 예로 ulgifyJSPlugin은 번들 파일 사이즈를 줄이고, 코드를 못 알아보게 만들어 준다. 또는 extract-text-webpack-plugin의 경우는 css-loader, style-loader의 결과를 하나의 별도 외부 파일로 만들어 준다. (플러그인 목록 참조)


@angular/cli에서  ng eject로 나온 webpack.config.js안의 plugins설정 내역

"plugins": [

    new NoEmitOnErrorsPlugin(),

    new GlobCopyWebpackPlugin({

      "patterns": [

        "assets",

        "favicon.ico"

      ],

      "globOptions": {

        "cwd": path.join(process.cwd(), "src"),

        "dot": true,

        "ignore": "**/.gitkeep"

      }

    }),

    new ProgressPlugin(),

    new CircularDependencyPlugin({

      "exclude": /(\\|\/)node_modules(\\|\/)/,

      "failOnError": false

    }),

    new NamedLazyChunksWebpackPlugin(),

    new HtmlWebpackPlugin({

      "template": "./src/index.html",

      "filename": "./index.html",

      "hash": false,

      "inject": true,

      "compile": true,

      "favicon": false,

      "minify": false,

      "cache": true,

      "showErrors": true,

      "chunks": "all",

      "excludeChunks": [],

      "title": "Webpack App",

      "xhtml": true,

      "chunksSortMode": function sort(left, right) {

        let leftIndex = entryPoints.indexOf(left.names[0]);

        let rightindex = entryPoints.indexOf(right.names[0]);

        if (leftIndex > rightindex) {

          return 1;

        } else if (leftIndex < rightindex) {

          return -1;

        } else {

          return 0;

        }

      }

    }),

    new BaseHrefWebpackPlugin({}),

    new CommonsChunkPlugin({

      "name": [

        "inline"

      ],

      "minChunks": null

    }),

    new CommonsChunkPlugin({

      "name": [

        "vendor"

      ],

      "minChunks": (module) => {

        return module.resource &&

          (module.resource.startsWith(nodeModules) ||

            module.resource.startsWith(genDirNodeModules) ||

            module.resource.startsWith(realNodeModules));

      },

      "chunks": [

        "main"

      ]

    }),

    new SourceMapDevToolPlugin({

      "filename": "[file].map[query]",

      "moduleFilenameTemplate": "[resource-path]",

      "fallbackModuleFilenameTemplate": "[resource-path]?[hash]",

      "sourceRoot": "webpack:///"

    }),

    new CommonsChunkPlugin({

      "name": [

        "main"

      ],

      "minChunks": 2,

      "async": "common"

    }),

    new NamedModulesPlugin({}),

    new AotPlugin({

      "mainPath": "main.ts",

      "replaceExport": false,

      "hostReplacementPaths": {

        "environments/environment.ts": "environments/environment.ts"

      },

      "exclude": [],

      "tsConfigPath": "src/tsconfig.app.json",

      "skipCodeGeneration": true

    })

]


JHipster로 자동생된 webpack.config.dev.js의 plugins 설정 내역

plugins: [

        new BrowserSyncPlugin({

            host: 'localhost',

            port: 9000,

            proxy: {

                target: 'http://localhost:9060',

                ws: true

            }

        }, {

            reload: false

        }),

        new webpack.NoEmitOnErrorsPlugin(),

        new webpack.NamedModulesPlugin(),

        new writeFilePlugin(),

        new webpack.WatchIgnorePlugin([

            utils.root('src/test'),

        ]),

        new WebpackNotifierPlugin({

            title: 'JHipster',

            contentImage: path.join(__dirname, 'logo-jhipster.png')

        })

 ]




Production vs Development

개발과 운영 시점에 webpack 환경을 달리 적용하기 위해 보통  webpack.config.dev.js 와 webpack.config.prod.js를  따로 만들고 package.json의 script에 적용해 사용한다. package.json에 적용하고 싶지 않다며 gulp를 사용해도 된다. 


@angular/cli의 script 내역

 "scripts": {

    "ng": "ng",

    "start": "webpack-dev-server --port=4200",

    "build": "webpack",

    "test": "karma start ./karma.conf.js",

    "lint": "ng lint",

    "e2e": "protractor ./protractor.conf.js",

    "pree2e": "webdriver-manager update --standalone false --gecko false --quiet"

}


JHipster script 내역

"scripts": {

    "lint": "tslint --type-check --project './tsconfig.json' -e 'node_modules/**'",

    "lint:fix": "yarn run lint -- --fix",

    "ngc": "ngc -p tsconfig-aot.json",

    "cleanup": "rimraf target/{aot,www}",

    "clean-www": "rimraf target//www/app/{src,target/}",

    "start": "yarn run webpack:dev",

    "serve": "yarn run start",

    "build": "yarn run webpack:prod",

    "test": "karma start src/test/javascript/karma.conf.js",

    "test:watch": "yarn test -- --watch",

    "webpack:dev": "yarn run webpack-dev-server -- --config webpack/webpack.dev.js --progress --inline --hot --profile --port=9060",

    "webpack:build:main": "yarn run webpack -- --config webpack/webpack.dev.js --progress --profile",

    "webpack:build": "yarn run cleanup && yarn run webpack:build:main",

    "webpack:prod:main": "yarn run webpack -- --config webpack/webpack.prod.js --progress --profile",

    "webpack:prod": "yarn run cleanup && yarn run webpack:prod:main && yarn run clean-www",

    "webpack:test": "yarn run test",

    "webpack-dev-server": "node --max_old_space_size=4096 node_modules/webpack-dev-server/bin/webpack-dev-server.js",

    "webpack": "node --max_old_space_size=4096 node_modules/webpack/bin/webpack.js",

    "e2e": "protractor src/test/javascript/protractor.conf.js",

    "postinstall": "webdriver-manager update && node node_modules/phantomjs-prebuilt/install.js"

}


to be continued...



<참고>

- Webpack core concept

- 네이버 webpack 소개

- Webpack의 혼란스러운 사항들

- Webpack3에서 주의할 점


신고

'JHipster' 카테고리의 다른 글

[CLI] @angular/cli속의 Webpack 이해하기  (0) 2017.09.04
posted by peter yun 윤영식