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

Publication

Category

Recent Post

2017. 4. 4. 10:59 Dev Environment/Build System

Gulp 빌드 환경에 파일이 변경되었을 때 자동으로 브라우져 화면을 업데이트해주는 Live reload환경을 접목해 본다.





Gulp 환경 만들기


gulp CLI를 설치한다.

$ npm i -g gulp-cli


npm 대신 속도가 빠르다는 yarn을 사용해 본다. yarn을 설치한다.

$ npm i -g yarn



테스트 폴를 생성하고,yarn을 이용해 package.json을 만든다. -y 플래그를 주면 모두 Yes로 응답해서 package.json을 생성해준다.

$ mkdir livereload && cd livereload

$ yarn init -y

yarn init v0.21.3

warning The yes flag has been set. This will automatically answer yes to all questions which may have security implications.

success Saved package.json

✨  Done in 0.06s.



index.html파일을 생성하고, gulp 패키지를 로컬에 설치한다. index.html에 간단한 body 태그를 넣는다. 

$ touch index.html

$ yarn add --dev gulp


TypeScript기반으로 파일을 작성하기 위해 다음 설정을 한다. [Typescript] NodeJS에서 Typescript 사용하기

$ yarn add --dev @types/node



gulp환경파일을 생성하고, default 태스크를 작성한다. gulp 명령을 실행하면 default 태스크가 실행된다.

$ touch Gulpfile.ts


// Gulpfile.js

const gulp = require('gulp');


gulp.task('default', function () {

  console.log('Gulp and running!')

});


// test

$ gulp

[09:27:23] Using gulpfile ~/prototyping/livereload/gulpfile.ts

[09:27:23] Starting 'default'...

Gulp and running!

[09:27:23] Finished 'default' after 71 μs





테스트 서버 환경 만들기


Express를 설치하고 정적 파일을 처리하는 서버를 생성한다.

$ yarn add --dev express


// Gulpfile.ts

const gulp = require('gulp');


function startExpress() {

  const express = require('express');

  const app = express();

  app.use(express.static(__dirname));

  app.listen(4000);

}


gulp.task('default', () => startExpress());


index.html 을 작성하고, gulp 명령을 수행한 다음 http://localhost:4000 호출해서 hi peter가 출력하면 정상작동이다. 

<!DOCTYPE html>

<html>

  <head>

    <meta charset="utf-8">

    <title></title>

  </head>

  <body>

    hi peter

  </body>

</html>


다음으로 livereload server 인 tiny-lr을 설치한다.

$ yarn add --dev tiny-lr


tiny-lr서버도 gulp환경에 넣는다. gulp명령을 실행하고 http://localhost:35729/ 접속해서 {"tinylr":"Welcome","version":"1.0.3"} 출력되면 정상작동이다. tiny-lr의 기본 포트는 35729 이다.

const gulp = require('gulp');


function startExpress() {

  const express = require('express');

  const app = express();

  app.use(express.static(__dirname));

  app.listen(4000);

}


function startLivereload() {

  const lr = require('tiny-lr')();

  lr.listen(35729);

}


gulp.task('default', () => {

  startExpress();

  startLivereload();

});





Livereload 환경 만들기


전체 동작방식은 다음과 같다.

  - 브라우져가 열리면 livereload.js는 tiny-lr과 웹소켓 연결을 맺는다.

  - 1) gulp.watch가 파일 변경을 감지한다. 

  - 1) gulp.watch는 변경파일에 대해 tiny-lr에게 reload 하라고 호출한다. 

  - 2) tiny-lr 서버는 livereload.js 모듈과 웹소켓으로 통신한다.

  - 3) livereload.js는 파일을 다시 요청한다. 

  - 화면이 refresh되면서 livereload.js와 tiny-lr 간 웹소켓 연결을 다시 맺는다. 





Express로 서비스하는 페이지가 livereload 처리하는 자바스크립트 로직을 넣기위해 connect-livereload NodeJS 미들웨어도 설치한다.

$ yarn add --dev connect-livereload


connect-livereload를 Express 미들웨어로 설정한다.

// Gulpfile.ts

function startExpress() {

  const express = require('express');

  const app = express();

  app.use(require('connect-livereload')());

  app.use(express.static(__dirname));

  app.listen(4000);

}



gulp.watch를 통해 변경된 파일의 목록을 tiny-lr에 전달하는 코드를 작성한다. 

const gulp = require('gulp');


function startExpress() {

  const express = require('express');

  const app = express();

  app.use(require('connect-livereload')());

  app.use(express.static(__dirname));

  app.listen(4000);

}


let lr;

function startLivereload() {

  lr = require('tiny-lr')();

  lr.listen(35729);

}


function notifyLivereload(event) {

  // `gulp.watch()` events 는 절대경로를 주기 때문에 상대 경로(relative)를 만든다.

  var fileName = require('path').relative(__dirname, event.path);


  lr.changed({

    body: {

      files: [fileName]

    }

  });

}


gulp.task('default', () => {

  startExpress();

  startLivereload();

  gulp.watch('*.html', notifyLivereload);

});


다시 gulp를 수행하고 브라우져에서 http://localhost:4000/index.html 을 호출한 후에 index.html의 내용을 변경하면 브라우져가 자동으로 refresh되는 것을 볼 수 있다. 브라우져에서 element검사를 하면 아래와 같이 livereload.js파일이 </body> 전에 자동 추가된다. (소스 참조)



위의 코드를 단순히 하기위해 gulp-livereload를 사용할 수도 있다. 샘플 파일: https://github.com/BISTel-MIPlatform/livereload-tiny-lr





참조

  - https://github.com/mklabs/tiny-lr

 - https://github.com/livereload/livereload-js

  - https://github.com/intesso/connect-livereload

  - https://github.com/vohof/gulp-livereload

posted by 윤영식
2013. 4. 25. 11:45 Dev Environment/Build System

구글 플러스 보다가 여맨 개발자인 오스마니님의 Generator 문서 정리했다는 글을 보고 얼른 들어와 봤다. AngularJS를 하면서 Yeoman을 필수로 상요하고 있어서 필요한 모듈이나 라이브러리를 스케폴딩하도록 수정하고 있기 때문에 어째 잘 정리되었는지 살펴본다 



1. 소개

  - 제너레이터의 종류는 두가지이다 

    + Boilerplate : 프로젝트 껍데기를 만들어준다. 프로젝트 구조화에 도움 예) HTML5 boilerplate, Twitter Bootstrap boilerplate 등 

    + Scaffolder : boilerplate외에 빌드시스템, sub-generator, 의존성관리, 자동화 업무수행(workflow)을 한다 

  - 공식적인 제너레이터들을 사용하거나 fork해서 수정 사용해도 된다 예) AngularJS+Express 연동



2. Boilerplate  Generator 만들기 

  - awesome 이란 걸 만들어 보자 

    + 원문은 사실 왜 이렇게 명령을 하는지 친절하게 설명은 하지 않고 있다. 기본은 알고 있다는 전제로 간단히 설명했다 

    + html5 boilerplate generator를 만들어 본다 

///////////////////////////////////////////////////////

// yeoman의  boilerplate generator를 가장 먼저 클론한다

$ git clone git://github.com/addyosmani/generator-boilerplate.git

Cloning into 'generator-boilerplate'...

.. 중략..

Resolving deltas: 100% (20/20), done.


///////////////////////////////////////////////////////

// git submodule add 명령(참조) 을 통하여 html5 bp 클론

// 여기서 중요한 것은 app/templates 으로 반드시 지정

// app/templates은 yeoman의 복제 템플릿이 놓이는 위치

// 다른 방법 : yo boilerplate https://github.com/h5bp/html5-boilerplate/archive/master.tar.gz

$ cd generator-boilerplate/

$ generator-boilerplate> git submodule add git://github.com/h5bp/html5-boilerplate.git app/templates

Cloning into 'app/templates'...

remote: Counting objects: 4955, done.

..중략...

warning: LF will be replaced by CRLF in .gitmodules.

The file will have its original line endings in your working directory.


///////////////////////////////////////////////////////

// templates 밑으로 html5-boilerplate 코드가 들어감

$ tree

.

├── README.md

├── app

│   ├── index.js

│   └── templates

│       ├── 404.html

│       ├── CHANGELOG.md

│       ├── CONTRIBUTING.md

│       ├── LICENSE.md

│       ├── README.md

│       ├── apple-touch-icon-114x114-precomposed.png

│       ├── apple-touch-icon-144x144-precomposed.png

│       ├── apple-touch-icon-57x57-precomposed.png

│       ├── apple-touch-icon-72x72-precomposed.png

│       ├── apple-touch-icon-precomposed.png

│       ├── apple-touch-icon.png

│       ├── crossdomain.xml

│       ├── css

│       │   ├── main.css

│       │   └── normalize.css

│       ├── doc

│       │   ├── TOC.md

│       │   ├── crossdomain.md

│       │   ├── css.md

│       │   ├── extend.md

│       │   ├── faq.md

│       │   ├── html.md

│       │   ├── js.md

│       │   ├── misc.md

│       │   └── usage.md

│       ├── favicon.ico

│       ├── humans.txt

│       ├── img

│       ├── index.html

│       ├── js

│       │   ├── main.js

│       │   ├── plugins.js

│       │   └── vendor

│       │       ├── jquery-1.9.1.min.js

│       │       └── modernizr-2.6.2.min.js

│       └── robots.txt

└── package.json


7 directories, 34 files


///////////////////////////////////////////////////////

// git 상태정보 .gitmodule 모듈 환경파일 자동 생성됨 

$ git status

warning: LF will be replaced by CRLF in .gitmodules.

The file will have its original line endings in your working directory.

# On branch master

# Changes to be committed:

#   (use "git reset HEAD <file>..." to unstage)

#

# modified:   .gitmodules

# new file:   app/templates

#


$ cat .gitmodules

[submodule "app/templates"]

path = app/templates

url = git://github.com/h5bp/html5-boilerplate.git


$ cat .git/config

[core]

repositoryformatversion = 0

filemode = true

bare = false

logallrefupdates = true

ignorecase = true

precomposeunicode = false

[remote "origin"]

url = git://github.com/addyosmani/generator-boilerplate.git

fetch = +refs/heads/*:refs/remotes/origin/*

[branch "master"]

remote = origin

merge = refs/heads/master

[submodule "app/templates"]

url = git://github.com/h5bp/html5-boilerplate.git


///////////////////////////////////////////////////////

// Node.js는 기본 설치되어 있어야 한다

// node package manager를 통하여 yeoman관련 모듈설치

$ npm install


  - 원문에서 다음으로 자신이 원하는 프로젝트를 만들라고 하는데, 중간과정이 생략되었다. 이제 만든 generator-boilerplate  사용법을 보자 

///////////////////////////////////////////////////////

// awesome이라는 명칭으로 변경한다

// yeoman 명명규칙으로 generator- prefix는 꼭 붙인다

// package.json 과 README.md 파일 내역을 수정한다 

$ mv generator-boilerplate generator-awesome

$ cd generator-awesome

$ vi package-json  // 이름과 버전 및 설명을 수정

$ vi README.md   // 내용 수정


///////////////////////////////////////////////////////

// yo 명령은 node를 기반으로 한다. 

// 따라서 global하게 찾을 수 있게 node_modules 밑에

// symbolic link을 걸어주었다

$ cd /usr/local/lib/node_modules

$ sudo ln -s <Your Directory Full Path>/generator-awesome  generator-awesome

Password:

/usr/local/lib/node_modules> ls -alrt

 ..중략..

drwxr-xr-x  13 nobody  staff   442  4 12 13:45 generator-karma

drwxr-xr-x  18 nobody  staff   612  4 12 14:30 npm

drwxr-xr-x  15 nobody  staff   510  4 12 14:31 bower

drwxr-xr-x  13 nobody  staff   442  4 12 15:03 grunt-cli

drwxr-xr-x   7 nobody  staff   238  4 18 09:57 yo

drwxr-xr-x  26 nobody  staff   884  4 18 10:42 generator-angular

drwxr-xr-x  16 nobody  staff   544  4 18 16:59 grunt-init

drwxr-xr-x  10 nobody  staff   340  4 19 23:46 grunt-express

drwxr-xr-x  16 nobody  staff   544  4 19 23:46 grunt

drwxr-xr-x  16 nobody  staff   544  4 22 11:04 generator-express

drwxr-xr-x  14 nobody  staff   476  4 22 11:31 generator-generator

drwxr-xr-x  13 nobody  staff   442  4 22 11:44 generator-bootstrap

lrwxr-xr-x   1 root    wheel    48  4 22 17:08 generator-angular-sd -> /Users/development/generator-angular-sd

drwxr-xr-x  16 nobody  staff   544  4 24 15:15 jshint

lrwxr-xr-x   1 root    wheel    52  4 25 10:40 generator-awesome -> /Users/prototyping/yeoman/generator-awesome


$ yo -h

Please choose a generator below.

Awesome

  awesome:app


///////////////////////////////////////////////////////

// 자신의 프로젝트 디렉토리를 만든다 

// 프로젝트 디렉토리로 이동하여 yo awesome 수행! 

$ mkdir  dowonProject 

$ cd dowonProject

 yo awesome  (또는 yo awesome:app)

Generating from Generator Boilerplate v0.1.4...

   create .gitattributes

 .. 중략 ..

   create js/vendor/jquery-1.9.1.min.js

   create js/vendor/modernizr-2.6.2.min.js

   create robots.txt



3. Scaffolding Generator 만들기 

  - generator-generator 를 사용하여 만든다 

///////////////////////////////////////////////////////

// 사실 yo, bower, grunt-cli를 설치해야 한다 (참조)

// 스케폴딩 제너레이터를 만들어 주는 제너레이터 설치

$ sudo npm install -g yo

$ sudo npm install -g generator-generator


///////////////////////////////////////////////////////

// 사용할 수 있는 yeoman generator list를 보자 

$ yo -h 

Generator

  generator

  generator:app

  generator:subgenerator


///////////////////////////////////////////////////////

// 스케폴딩 제너레이터 디렉토리를 만들고 명령수행

// generator- 는 반드시 붙인다. 뒤에 오는 이름에는 

// 절대로 - 를 사용하지 않는다. underbar 사용함!

$ mkdir generator-angularjs_express

$ cd generator-angularjs-express

$ yo generator:app

     _-----_

    |       |

    |--(o)--|   .--------------------------.

   `---------´  |    Welcome to Yeoman,    |

    ( _´U`_ )   |   ladies and gentlemen!  |

    /___A___\   '__________________________'

     |  ~  |

   __'.___.'__

 ´   `  |° ´ Y `


Would you mind telling me your username on Github? (someuser) ysyun

What's the base name of your generator? (generator-angularjs_express)

   create package.json

   create .editorconfig

   ..중략..

   create app/templates/_component.json

npm http GET https://registry.npmjs.org/yeoman-generator


/////////////////////////////////////////////////////////////////

// app 밑으로 index.js 파일과 templates 폴더 생성

// yo 는 generator 밑의 폴더(여기선 app하나) 밑에 기본

// index.js 를 찾고 해당 파일을 수행한다 

// 즉, app가 있으므로 향후 명령은 yo angularjs-express:app 가 된다 

//

// mocha와 yeoman-generator가 설치됨

tree

.

├── LICENSE

├── README.md

├── app

│   ├── index.js

│   └── templates

│       ├── _component.json

│       ├── _package.json

│       ├── editorconfig

│       ├── jshintrc

│       └── travis.yml

├── node_modules

│   ├── mocha

│   └── yeoman-generator

├── package.json

└── test

    ├── test-creation.js

    └── test-load.js


/////////////////////////////////////////////////////////////////

// app이외의 다른 generator를 만들고 싶다면 subgenerator 이용

$ yo generator:subgenerator dowon

   create dowon/index.js

   create dowon/templates/somefile.js


$ cat index.js

'use strict';

var util = require('util');

var yeoman = require('yeoman-generator');


var DowonGenerator = module.exports = function DowonGenerator(args, options, config) {

  // By calling `NamedBase` here, we get the argument to the subgenerator call

  // as `this.name`.

  yeoman.generators.NamedBase.apply(this, arguments);


  console.log('You called the dowon subgenerator with the argument ' + this.name + '.');

};


util.inherits(DowonGenerator, yeoman.generators.NamedBase);


DowonGenerator.prototype.files = function files() {

  this.copy('somefile.js', 'somefile.js');

};


/////////////////////////////////////////////////////////////////

// angularjs-express라고 만든 Scaffoliding generator 사용하기 

/usr/local/lib/node_modules> sudo ln -s <your full path>/generator-angularjs_express generator-angularjs_express

/usr/local/lib/node_modules> yo -h


Angularjs_express

  angularjs_express:app

  angularjs_express:dowon


////////////////////////////////

// 이제 명령어를 사용할 수 있다

$ yo angularjs_express:app 또는 dowon


다음에는 index.js 확장하는 방법을 알아보자 



<참조> 

  - 원문 : http://yeoman.io/generators.html

posted by 윤영식
2013. 4. 22. 23:42 Dev Environment/Build System

Yeoman은 generator-* 를 통하여 스케폴딩 코드 껍데기를 만들어준다. 이중 angular를 위해서 generator-angular와 테스트를 위한 generator-karma등 다양한 generator가 있고, 사용자가 원하는 generator를 만들 수가 있다. generator-angular안에는 클라이언트단만을 고려한 스케폴딩만 존하여 generator-angular를 GitHub에서 forking하여 ExpressJS를 붙여 프로젝트에 실제 필요한 스케폴딩을 만들어 본다



1) generator-angular forking

  - https://github.com/yeoman/generator-angular 에서 fork를 했다


  - 로컬로 복제한다 

    + git clone https://github.com/ysyun/generator-angular 

  - yeoman 1.0 beta 부터는 명령이 yo 로 바뀌었다. 포스팅 보고서 설치를 한다

  - yo 로 수행할 수 있는 generator 목록을 보자 

    + 만일 generator-angular 를 설치했다면 Angular 라고 나온다

    + Naming convention 규칙에 따라서 generator-[Name] 으로 generator가 만들어지고 [Name]만을 취한다

    + generator들은 npm install -g 옵션으로 설치하여 맥(Mac) 기준으로 /usr/local/lib/node_modules/* 밑에 설치된다 

$ yo --help

 Usage: yo GENERATOR [args] [options]


General options:

  -h, --help     # Print generator's options and usage

  -f, --force    # Overwrite files that already exist


Please choose a generator below.


Angular  <<=== generator-angular 

  angular:app

  angular:common

  angular:controller

  angular:directive

  angular:filter

  angular:main

  angular:route

  angular:service

  angular:view


Bootstrap

  bootstrap:app


Express

  express:app

  express:basic

  express:common


Generator

  generator

  generator:app

  generator:subgenerator


Karma

  karma:app


Mocha

  mocha:app

  mocha:generator


Webapp

  webapp:app


$ cd /usr/local/lib/node_modules && ls -alrt

drwxr-xr-x  13 nobody  staff   442  4 12 13:45 generator-karma

drwxr-xr-x  18 nobody  staff   612  4 12 14:30 npm

drwxr-xr-x  15 nobody  staff   510  4 12 14:31 bower

drwxr-xr-x  13 nobody  staff   442  4 12 15:03 grunt-cli

drwxr-xr-x   7 nobody  staff   238  4 18 09:57 yo

drwxr-xr-x  26 nobody  staff   884  4 18 10:42 generator-angular

drwxr-xr-x  16 nobody  staff   544  4 18 16:59 grunt-init

drwxr-xr-x  10 nobody  staff   340  4 19 23:46 grunt-express

drwxr-xr-x  16 nobody  staff   544  4 19 23:46 grunt

drwxr-xr-x  12 nobody  staff   408  4 22 10:20 coffee-script

drwxr-xr-x  16 nobody  staff   544  4 22 11:04 generator-express

drwxr-xr-x  14 nobody  staff   476  4 22 11:31 generator-generator

drwxr-xr-x  13 nobody  staff   442  4 22 11:44 generator-bootstrap

drwxr-xr-x  29 root    wheel   986  4 22 15:53 generator-angular_dowon

lrwxr-xr-x   1 root    wheel    48  4 22 17:08 generator-angular-sd -> /Users/nulpulum/development/generator-angular-sd


  - fork한 generator-angular를 로컬머신에서 generator-angular-sd 라는 별도 디렉토리에 받고, /usr/local/lib/node_modules/밑에 symbolic link 를 걸어주었다. 

  - 다시 yo --help를 해보면 하기와 같이 angular-sd가 나온다.

    + 앞으로 명령을 yo angular-sd:app 또는 yo angular-sd:express 명령을 치면 스케폴딩 소스가 생기는 것이다. 

Angular-sd

  angular-sd:app

  angular-sd:common

  angular-sd:controller

  angular-sd:directive

  angular-sd:express  <<=== 만들 대상 

  angular-sd:filter

  angular-sd:main

  angular-sd:route

  angular-sd:service

  angular-sd:view



2) Angular Express Generator 만들기 

  - https://github.com/hmalphettes/yeoman-angular-express-example  샘플을 참조해서 작성을 시도해 보았다 

  - 일단 Yeoman 스케폴딩 방식에 대해서 알아보자 (Yeoman Wiki 설명문)

    + Yeoman은 디렉토리별로 index.js 파일을 기본으로 참조한다 

    + 디폴트 디렉토리는 app 이다

    + 그렇다면 다음과 같은 구조가 될 것이다. 즉, yo --help 했을 때 최상위 generator-angular-sd 밑의 디렉토리명이 옵션이 된다

    + 초기 generator를 만드는 도구는 npm install -g generator-generator로 설치한다 (직접 만들고 싶다면 설치,우선 참조만)

angular-sd/

    app/index.js

    common/index.js

    controller/index.js

    ... 중략 ...

    view/index.js


  - yo angular-sd:express 를 수행하려면  generator-angular-sd/express/  디렉토리를 만들고, index.js를 만든다

    + 코드는 yeoman-generator 를 사용한다 

    + 파란색 부분이 중요하다. 모든 스케폴딩 코드 템플릿을 generator-angular-sd/templates/ 밑에 관리하는 것이 일반적이다

    + templates 디렉토리 밑에 express 디렉토리를 만든다  

// index.js

'use strict';

var path = require('path');

var util = require('util');

var yeoman = require('yeoman-generator');


module.exports = ExpressGenerator;


function ExpressGenerator() {

  yeoman.generators.Base.apply(this, arguments);

}


util.inherits(ExpressGenerator, yeoman.generators.Base);


ExpressGenerator.prototype.setupEnv = function setupEnv() {

  this.sourceRoot(path.join(__dirname, '../templates/express'));

  this.directory('.', '.', true);

};


  - templates/express/ 디렉토리 밑에 express 관련 views, routes, public 등을 놓아도 되지만 여기서는 app.js 만들 놓는다. 

    + yo angular-sd를 먼저 수행하면 app 디렉토리가 express의 public 정적 디렉토리 파일 역할을 해준다. 

    + Angular를 사용한다는 것은 SPA이기 때문에 index.html 하나만 두고 전부 partial html로 사용할 것이다.

    + 따라서 Express의 jade는 사용하지 않을 것이다. app.js 파일 하나만 만듦

    + 정적 파일 디렉토리를 app로 지정하고, '/' 요청은 '/app/index.html'로 보냄. index.html 은 angular 파일이다 

var express = require('express')

  , routes = require('./server/routes')

  , http = require('http')

  , path = require('path')

  , fs = require('fs');


var app = express();


app.configure(function(){

  app.set('port', process.env.PORT || 3000);

  // angular /app/views

  // app.set('views', __dirname + '/server/views');

  // app.set('view engine', 'jade');

  app.use(express.favicon());

  app.use(express.logger('dev'));

  app.use(express.bodyParser());

  app.use(express.methodOverride());

  app.use(app.router);

  // angular app == public dir

  app.use(express.static(path.join(__dirname, 'app')));

});


app.configure('development', function(){

  app.use(express.errorHandler());

});


app.get('/', function(req, res) {

  res.sendfile(__dirname + '/app/index.html');

});

// app.get('/', routes.index);

// app.get('/users', user.list);


http.createServer(app).listen(app.get('port'), function(){

  console.log("Express server listening on port " + app.get('port'));


  - package.json 내역에 express 포함시킴 

    + 스케폴딩 위치는 generator-angular-sd/templates/common 밑에 package.json 파일이 존재한다 (수정할 파일)

    + package.json의 내역을 참조하여 node_modules에 필요 모듈을 다운로드 한다 

    + 맨 마지막에 fork 하였던 https://github.com/ysyun/generator-angular 을 travis ci에 연동하기 위해 scripts{ test:...} 넣음

    + grunt express-server를 위하여 grunt-express를 넣음 

{

  "name": "anulgar-express",

  "description": "Grunt task for running an Express Server for Smart Dashboard",

  "homepage": "http://mobincon.tistory.com",

  "author": {

    "name": "Yun Young Sik",

    "email": "ysyun@yuwin.co.kr"

  },

  "repository": {

    "type": "git",

    "url": "https://github.com/ysyun/generator-angular"

  },

  "version": "0.0.1",

  "scripts": {

    "test": "grunt travis --verbose"

  },

  "dependencies": {

    "express": "~3.2.0",

    "jade": "~0.29.0"

  },

  "devDependencies": {

    "grunt": "~0.4.1",

    "grunt-contrib-copy": "~0.4.0",

    "grunt-contrib-concat": "~0.1.3",

    "grunt-contrib-coffee": "~0.6.4",

    "grunt-contrib-uglify": "~0.2.0",

    "grunt-contrib-compass": "~0.1.3",

    "grunt-contrib-jshint": "~0.3.0",

    "grunt-contrib-cssmin": "~0.5.0",

    "grunt-contrib-connect": "~0.2.0",

    "grunt-contrib-clean": "~0.4.0",

    "grunt-contrib-htmlmin": "~0.1.1",

    "grunt-contrib-imagemin": "~0.1.2",

    "grunt-contrib-livereload": "~0.1.2",

    "grunt-bower-requirejs": "~0.4.1",

    "grunt-usemin": "~0.1.10",

    "grunt-regarde": "~0.1.1",

    "grunt-rev": "~0.1.0",

    "grunt-karma": "~0.3.0",

    "grunt-open": "~0.2.0",

    "matchdep": "~0.1.1",

    "grunt-google-cdn": "~0.1.1",

    "grunt-ngmin": "~0.0.2",

    "grunt-express": "git://github.com/hmalphettes/grunt-express.git",

    "socket.io": "*"

  },

  "engines": {

    "node": ">=0.8.0"

  }

}


  - 명령을 수행해 본다

    + yo angular-sd  : angular-sd:app 와 동일하고 generator-angular-sd/app/index.js 설정한 내역을 수행한다 

    + yo angular-sd:express : express 관련 파일을 스케폴딩한다. 초기에 jade도 넣었다가 그냥 app.js 만 놔두었다 

$ mkdir angular-express

$ cd angular-express

$ yo angular-sd

  ... 쭉 엔터치고 한참 의존관계있는 파일을 받는다 ...

$ ls -alrt

drwxr-xr-x   4 nulpulum  staff   136  4 22 23:15 ..

drwxr-xr-x   4 nulpulum  staff   136  4 22 23:16 test

-rw-r--r--   1 nulpulum  staff  1491  4 22 23:16 package.json

-rw-r--r--   1 nulpulum  staff  1273  4 22 23:16 karma.conf.js

-rw-r--r--   1 nulpulum  staff  1134  4 22 23:16 karma-e2e.conf.js

-rw-r--r--   1 nulpulum  staff   360  4 22 23:16 component.json

-rw-r--r--   1 nulpulum  staff  8189  4 22 23:16 Gruntfile.js

-rw-r--r--   1 nulpulum  staff   409  4 22 23:16 .jshintrc

-rw-r--r--   1 nulpulum  staff    50  4 22 23:16 .gitignore

-rw-r--r--   1 nulpulum  staff    11  4 22 23:16 .gitattributes

-rw-r--r--   1 nulpulum  staff   415  4 22 23:16 .editorconfig

-rw-r--r--   1 nulpulum  staff    38  4 22 23:16 .bowerrc

drwxr-xr-x  12 nulpulum  staff   408  4 22 23:16 app

drwxr-xr-x  15 nulpulum  staff   510  4 22 23:16 .

drwxr-xr-x  30 nulpulum  staff  1020  4 22 23:17 node_modules


$ yo angular-sd:express

   create app.js



3) Grunt file 만들기

  - Express에 대해서 grunt를 통하여 test, build, preview를 수행하기 위하여 Gruntfile.js를 수정한다

     + generator-angular-sd/templates/common/Gruntfile.js 가 수정파일이다 

     + package.json 파일 에서  "grunt-express": "git://github.com/hmalphettes/grunt-express.git" 꼭 들어가야 함

  - Gruntfile.js : 기존 generator-angular의 Gruntfile.js에서 express 관련 내역을 첨부한다 (파일참조)

'use strict';

var lrSnippet = require('grunt-contrib-livereload/lib/utils').livereloadSnippet;

var mountFolder = function (connect, dir) {

  return connect.static(require('path').resolve(dir));

};

var path = require('path');


module.exports = function (grunt) {

  // load all grunt tasks

  require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);


  // configurable paths

  var yeomanConfig = {

    app: 'app',

    dist: 'dist',

    server: 'server'

  };


  try {

    yeomanConfig.app = require('./component.json').appPath || yeomanConfig.app;

  } catch (e) {}


  grunt.initConfig({

    yeoman: yeomanConfig,

    watch: {

      coffee: {

        files: ['<%%= yeoman.app %>/scripts/{,*/}*.coffee'],

        tasks: ['coffee:dist']

      },

      coffeeTest: {

        files: ['test/spec/{,*/}*.coffee'],

        tasks: ['coffee:test']

      },

      compass: {

        files: ['<%%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],

        tasks: ['compass']

      },

      livereload: {

        files: [

          '<%%= yeoman.app %>/{,*/}*.html',

          '{.tmp,<%%= yeoman.app %>}/styles/{,*/}*.css',

          '{.tmp,<%%= yeoman.app %>}/scripts/{,*/}*.js',

          '<%%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',

          '<%%= yeoman.server %>'

        ],

        tasks: ['livereload']

"Gruntfile.js" [dos] 349L, 8192C

        ],

        tasks: ['livereload']

      }

    },

    connect: {

      options: {

        port: 9000,

        // Change this to '0.0.0.0' to access the server from outside.

        hostname: 'localhost'

      },

      livereload: {

        options: {

          middleware: function (connect) {

            return [

              lrSnippet,

              mountFolder(connect, '.tmp'),

              mountFolder(connect, yeomanConfig.app)

            ];

          }

        }

      },

      test: {

        options: {

          middleware: function (connect) {

            return [

              mountFolder(connect, '.tmp'),

              mountFolder(connect, 'test')

            ];

          }

        }

      }

    },

    express: {

      livereload: {

        options: {

          port: 9000,

          bases: [ path.resolve('.tmp'), path.resolve(yeomanConfig.app) ],

          monitor: {},

          debug: true //,

          // server: path.resolve('/app')

        }

      },

      test: {

        options: {

          port: 9000,

          bases: [ path.resolve('.tmp'), path.resolve(yeomanConfig.app) ],

          port: 9000,

          bases: [ path.resolve('.tmp'), path.resolve(yeomanConfig.app) ],

          monitor: {},

          debug: true //,

          // server: path.resolve('/app')

        }

      }

    },


  ... 중략 ...

  grunt.registerTask('default', ['build']);



  // for express

  grunt.registerTask('express-server', [

    'clean:server',

    'coffee:dist',

    'compass:server',

    'livereload-start',

    'express:livereload',

    'open',

    'watch'

  ]);


  grunt.registerTask('express-test', [

    'clean:server',

    'coffee',

    'compass',

    'connect:test',

    'karma'

  ]);


  grunt.registerTask('travis', ['jshint', 'test']);


  - 명령 수행하기 

    + grunt express-test : 테스트 

    + grunt : 빌드

    + grunt express-server : 미리보기 수행


    + 명령을 수행하면 자동으로 브라우져가 뜨면 성공



모든 것이 성공했다면 Remote Repository로 push한다. 그리고 Travis-ci에 forking한 프로젝트를 연결한다. 해당 부분은 다음에 보도록 하자.


결과물 : https://github.com/ysyun/generator-angular



<참조>

  - Travis CI 연동하기

  - 2013.11 Monthly Digest for Yeoman

  - Yeoman : generator-fullstack with angular and express

  - Yeoman : generator-mean with angular and express, mongodb, node

posted by 윤영식
2013. 4. 15. 11:44 Dev Environment/Build System

AngularJS 개발시 반복적인 작업을 제거하고 생산성을 높일 수 있는 통합 도구인 Yeoman의 사용법에 대해 알아보고, 처음 개발을 시작할 때 코드의 틀을 잡아 주는 몇가지 Seed 프로젝트를 통하여 어떤 구조로 애플리케이션을 만들어야 하는지 알아두자



1. 필요 요소 기술

  - UI : Bootstrap from Twitter  간혹 Foundation을 이용

  - Build/Preview : Grunt

  - Test : 기본은 Karma이고, Mocha를 사용하고 싶다면 Karma-Mocha Karma구동을 위한 Mocha adapter 존재

  - Package Management : Bower from Twitter

이들의 통합적으로 사용하기 위한 툴로 yeoman 을 설치하면 된다. 간단한 명령으로 AngularJS 관련 코드를 자동 생성하여 준다



2. Yeoman startup

  

yo - the scaffolding tool from Yeoman

bower - the package management tool  

  + component.json : 의존관계 설정

  + .bowerrc : 컴포넌트 설치 위치정보

grunt - the build tool 

  + Gruntfile.js : ant와 같은 수행 Task 정의

  + package.json : Node.js위에 구동되므로 package.json에 Grunt contributor 라이브러리 정의

karma - grunt test 시에 수행 

  + karma.conf.js : 로컬 테스트 환경

  + karma-e2e.conf.js : Real Browser 테스트 환경 즉, end-to-end (Client<->Server) 테스트 수행. 사용 port 8080


  - Yeoman command for AngularJS

// 설치 : grunt-cli, bower가 같이 설치된다 

npm install -g yo


// Yeoman을 위한 generator 설치 for AngularJS & Karam

// 하기 에러 내역 참조하여 generator-angular의 script-base.js 파일 수정

// Generator는 Yeoman에서 생성하는 boilerplate 나 scaffolding 코드를 만들어주는

// Yeoman의 플러그인이라 생각하면 된다. 이미 만들어지 Generator 목록이 존재한다

// > https://github.com/yeoman (generator-* 목록들)

// > Generator 만들기 참조

npm install -g generator-webapp generator-angular generator-karma


// 명령어 (프로젝트 디렉토리에서 수행)

yo angular 또는 yo angular:app 애플리케이션 생성

yo angular:controller myControllerName 컨트롤러 생성

yo angular:directive myDirectiveName 디렉티브 생성

yo angular:filter myFilterName 필터 생성

yo angular:service myServiceName 서비스 생성


  - yo angular:* 사용시 에러 발생 해결방법

예) yo angular:controller Test 수행시 하기와 같은 에러 발생

~/angularjs/sd_01> yo angular:controller Test


path.js:360

        throw new TypeError('Arguments to path.join must be strings');

              ^

TypeError: Arguments to path.join must be strings

    at path.js:360:15

    at Array.filter (native)

    at Object.exports.join (path.js:358:36)

    at Generator (/usr/local/lib/node_modules/generator-angular/script-base.js:38:29)

    at new Generator (/usr/local/lib/node_modules/generator-angular/controller/index.js:10:14)


==> 해결하기 

1) /usr/local/lib/node_modules/generator-angular/script-base.js 의 38 번째 줄 내용을 하기와 같이 수정함

 37     if (!this.options.coffee &&

 38       //this.expandFiles(path.join(this.appPath, '/scripts/**/*.coffee'), {}).length > 0) {  // 주석처리 

 39       this.expandFiles(path.join(process.cwd(), '/scripts/**/*.coffee'), {}).length > 0) {  // process.cwd() 로 바꿈

 40       this.options.coffee = true;

 41     }


2) 재수행 : 친절하게도 TestCase까지 자동 생성해 준다 

   ~/angularjs/sd_01> yo angular:controller Test

   create app/scripts/controllers/Test.js

   create test/spec/controllers/Test.js


  - Yeoman command for Bower : 의존관계 라이브러리에 대한 관리 담당 

// 명령어

bower search <dep> - search for a dependency in the Bower registry

bower install <dep>..<depN> - install one or more dependencies

bower list - list out the dependencies you have installed for a project

bower update <dep> - update a dependency to the latest version available


// 설치하기 

// .bowerrc 에 설정된 디렉토리로 다운로드된 라이브러리가 설치된다

$ bower install angular-ui

bower cloning git://github.com/angular-ui/angular-ui.git

bower cached git://github.com/angular-ui/angular-ui.git

bower fetching angular-ui

bower checking out angular-ui#v0.4.0

bower copying /Users/nulpulum/.bower/cache/angular-ui/bd4cf7fc7bfe2a2118a7705a22201834

bower installing angular-ui#0.4.0


$ cd app/components/

$ ls

angular angular-scenario es5-shim json3

angular-mocks angular-ui jquery


$ cd ../..

$ cat .bowerrc

{

    "directory": "app/components"

}


  - Yeoman command for Grunt : 프러덕션 빌드, 미리보기, 테스팅 

    + grunt는 task에 대한 여러 target들이 존재하는 구조이다. 

    + 명령을 grunt <task> 로 주면 task밑의 모든 target이 순서대로 수행된다

    + 명령을 grunt <task>:<target> 을 지정하여 특정 target만 수행할 수도 있다

    + task와 관련한 여러가지 Plugins 이 이미 만들어져 있고 직접 만들 수도 있다 (기존 Plugins 목록)

// 명령어

grunt test  - 테스트하기. run the unit tests for an app  

grunt server - 브라우져에서 보기. 변경사항 실시간 반영됨. preview an app you have generated (with Livereload)

grunt - 배포본 빌드하기.  build an optimized, production-ready version of your app  (warning 무시 옵션 --force 사용가능)


// 브라우져 미리보기 in app root folder

grunt server

// warning -jslint 같은- 무시하고 계속 build하기

grunt  --force 



3. 기본 코드 생성

  - Grunt-Express : Grunt와 Express 연동하기

  - Yeoman-Angular-Express : Yeoman을 이용하여 Angular project 생성한 후 Express와 통합하는 예제

  - Angular-Socket.io-Seed : Angular와 Socket.io를 접목 시키고자 할때 사용 (포스팅)



4. Addy Osmani 님이 이야기 하는 Automation Front-end workflow



결론적으로 Yeoman을 사용하여 기반 코드를 생성하고 빌드/테스팅 자동화를 한다. 그리고 필요한 코드들은 Seed 프로젝트를 참조하여 자기것으로 만들면 되겠다



<참조>

  - 원문 : Google, Twitter and AngularJS

  - 보다 많은 AngularJS와 BackboneJS에 대한 기사 in DailyJS

  - Grunt를 위한 express 플러그인 테스트

posted by 윤영식
2013. 1. 31. 10:08 Dev Environment/Build System

Grunt의 API를 사용하여 Task를 만들어 보자. Grunt의 Task가 핵심으로 한개를 수행하거나 여러개를 동시에 수행할 수도  있다. 


1) Task 별칭 등록하기 

  - grunt.registerTask 로 Task의 별칭을 지정한다

    + api : grunt.registerTask(taskName, taskList)

    + 예 : task.registerTask('default', 'lint qunit concat min'); 

    + grunt 수행시 특정 task를 지정하지 않으면 자동으로 default 별칭의 task가 수행된다 


2) Function Task 만들기

  - grunt.regiterTask 로 Task를 신규로 만들 수 있다

    + api : grunt.registerTask(taskName, description, taskFunction)

    + description, taskFunction을 파라미터로 넘기면 새로운 task 등록됨 

    + fail이 나면 return false 를 해야 한다 

//////////////////////////////////////////////
// grunt.js 환경파일에 등록한다 
  grunt.registerTask('dowon', 'A sample task that logs stuff.', function(arg1, arg2) {
    if (arguments.length === 0) {
      grunt.log.writeln(this.name + ", no args");
    } else {
      grunt.log.writeln(this.name + ", " + arg1 + " " + arg2);
    }
  });

//////////////////////////////////////////////
// 결과
// 아규먼트 없을 때
D:\Development\testing_grunt>grunt.cmd dowon
Running "dowon" task
dowon, no args
Done, without errors.

// 아규먼트 전달 : 이용 
D:\Development\testing_grunt>grunt.cmd dowon:hello:youngsik
Running "dowon:hello:youngsik" (dowon) task
dowon, hello youngsik
Done, without errors.


3) Multi Task 만들기 

  - grunt.registerMultiTask 를 이용하여 만든다

    + api : grunt.registerMultiTask(taskName, description, taskFunction)

    + lint, concat, min task등이 multi task이다 

    + sub-properties (별칭 targets) 이 내부적으로 task가 된다

    + multi task에서는 function에서 특별히 this 키워드를 사용하여 접근한다 

  - grunt.initConfig 안에 target을 만든다 객체명칭 = multi task의 대표 명칭이다 

    + 하기 예제에서 logstuff 가 multi task의 대표명칭이다 

    + grunt.registerMultiTask의 첫번째 아규먼트인 multi task 명칭을 logstuff 로 일치시킨다 

    + 등록한 function에서 this 함에 주의*

//////////////////////////////////////////////
/*global config:true, task:true*/
grunt.initConfig({
  logstuff: {
    foo: [1, 2, 3],
    bar: 'hello world',
    baz: false
  }
});

grunt.registerMultiTask('logstuff', 'This task logs stuff.', function() {
  // this.target === the name of the target
  // this.data === the target's value in the config object
  // this.name === the task name
  // this.args === an array of args specified after the target on the command-line
  // this.flags === a map of flags specified after the target on the command-line
  // this.file === file-specific .src and .dest properties

  // Log some stuff.
  grunt.log.writeln(this.target + ': ' + this.data);

  // If data was falsy, abort!!
  if (!this.data) { return false; }
  grunt.log.writeln('Logging stuff succeeded.');
});

//////////////////////////////////////////////
// 결과
// target을 지정하지 않았을 때는 전체 target이 수행
D:\Development\testing_grunt>grunt.cmd logstuff
Running "logstuff:foo" (logstuff) task
foo: 1,2,3
Logging stuff succeeded.

Running "logstuff:bar" (logstuff) task
bar: hello world
Logging stuff succeeded.

Running "logstuff:baz" (logstuff) task
baz: false
 Task "logstuff:baz" failed. Use --force to continue. 

Aborted due to warnings.

// foo target을 지정
D:\Development\testing_grunt>grunt.cmd logstuff:foo
Running "logstuff:foo" (logstuff) task
foo: 1,2,3
Logging stuff succeeded.

Done, without errors.

// bar target을 지정 
D:\Development\testing_grunt>grunt.cmd logstuff:bar
Running "logstuff:bar" (logstuff) task
bar: hello world
Logging stuff succeeded.

Done, without errors.

// baz target에서 false return하면서 warning 발생함 
D:\Development\testing_grunt>grunt.cmd logstuff:baz
Running "logstuff:baz" (logstuff) task
baz: false
 Task "logstuff:baz" failed. Use --force to continue. 

Aborted due to warnings.


4) Init Task 만들기 

  - api : grunt.registerInitTask(taskName, description, taskFunction)

    +최초에 수행되는 init task를 수행하고 별도의 환경 설정 데이터도 필요없다 


5) Task명칭 바꾸기 

  - api : grunt.renameTask(oldname, newname)



6) Inside Tasks 알아보기 

  - grunt의 this 에는 여러 유용한 properties 와 method를 가지고 있다. grunt.task.current 로 expose 한다 

  - Task에서 구현하는 function에서 사용한다 

  - this.async / grunt.task.current.async

    + task를 비동직적으로 수행시켜 준다 

//////////////////////////////////////////////
// Tell grunt this task is asynchronous.
var done = this.async();
// Your async code.
setTimeout(function() {
  // Let's simulate an error, sometimes.
  var success = Math.random() > 0.5;
  // All done!
  done(success);
}, 1000);


  - this.requires / grunt.task.current.requires : 의존관계있는 task를 열거

  - this.requiresConfig / grunt.task.current.requiresConfig : config properties 열거 

  - this.name / grunt.task.current.name : 등록된 task 명칭 

  - this.nameArgs / grunt.task.current.nameArgs : grunt뒤의 아규먼트들 예) grunt.cmd task1:foo  -> task1:foo 

  - this.args / grunt.task.current.args : task명칭을 뺀 실제 아규먼트값을 배열로 리턴

  - this.flags / grunt.task.current.flags : 아규먼트를 만들어준다 

  - this.errorCount / grunt.task.current.errorCount : task에서 발생한 에러건수 


7) Indside Multi Tasks 알아보기 

  - this.target / grunt.task.current.target : multi task에서만 사용. config data로 등록된 key (name) 값 

  - this.data / grunt.task.current.data : multi task에서만 사용. config data로 등록된 value 값

  - this.file / grunt.task.current.file : compact format 사용시 this.file.src , this.file.dest 가 만들어진다 (template api 참조)



8) 외부에서 정의된 Task 로딩하기 

  - 큰 프로젝트에서 sub 프로젝트끼리 공유하기 위하여 task와 helper 들을 만든다 

  - 여러 디렉토리에서 task를 로딩하거나 또는 Npm-installed grunt plugin 을 로딩한다 

  - task 관련 파일들 로딩하기 

    + api : grunt.loadTasks(tasksPath)

  - task와 helper 로딩하기 

    + api : grunt.loadNpmTasks(pluginName)

  - npm module 호출 

    + api : grunt.npmTasks(npmModuleName)



9) Helper 정의하고 실행하기 

  - 이미 만들어지 helper들 존재 (참조)

  - helper 만들기 

    + api : grunt.registerHelper(helperName, helperFunction)

//////////////////////////////////////////////
grunt.registerHelper('add_two_nums', function(a, b) {
  return a + b;
});

  - helper 이름 바꾸기 

    + api : grunt.renameHelper(oldname, newname)


  - helper 호출하기 

    + api : grunt .helper(helperName, [, agruments...])



10) Warning 과 Fatal Error

  - api 호출하여 화면에 출력 한다 

  - grunt.warn(error [, errorCode])

  - grunt.fatal(error [, errorCode])



<참조>

  - Grunt API

posted by 윤영식
2013. 1. 28. 17:48 Dev Environment/Build System

이전에 Ant를 이용하여 Javascript project를 build하는 것을 알아 보았다. 요즘은 Grunt.js라는 빌드도구가 나와서 많이 사용되고 있고, Javascript 에 최적화 되어 있기때문에 앞으로는 Grunt.js를 사용한다. Grunt.js에 대해서 알아보자 


1) 준비하기

  - node.js 와 npm 설치되어 있어야 한다 

  - 기본 설치 : npm uninstall -g grunt  ==> npm install -g grunt-cli

  - 부가 설치 : npm install grunt-contrib

  - grunt.js (gruntfile)이 핵심 구성내역

    + 프로젝트 환경

    + grunt plugins 또는 tasks 폴더 로딩

    + Tasks 와 Helpers



2) Task 만들기 

  - 환경파일 만들기 (node.js 모듈로 생성) :  grunt init:gruntfile  명령 수행하면 Gruntfile.js 파일 생성됨(grunt 환경파일)

    + 윈도우 : grunt.cmd init:gruntfile 수행

  - grunt.js 파일안에 task 열거 lint, qunit, concat, min, watch, jshint, testuglify

    + lint : 자바스크립트 문법오류 가능성 있는 것 체크

    + qunit : Unit test를 위하여 QUnit을 사용 (PhantomJS이용)

    + concat : 지정된 디렉토리/파일을 1개의 파일로 합침

    + min : 파일사이즈 minify 최소화

    + mincss : css 최소화 

    + watch : node.js::fs 모듈의 watch 메소드 사용하여 파일 변경사항 발생시 task를 수행한다 

    + 사용가능 task 목록 보기 : grunt -h 


// grunt.js 파일 내역 
/*global module:false*/
module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    pkg: '',
    meta: {
      banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
        '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
        '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
        '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
        ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */'
    },
    lint: {
      files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js']
    },
    qunit: {
      files: ['test/**/*.html']
    },
    concat: {
      dist: {
        src: ['', '.js>'],
        dest: 'dist/<%= pkg.name %>.js'
      }
    },
    min: {
      dist: {
        src: ['', ''],
        dest: 'dist/<%= pkg.name %>.min.js'
      }
    },
    watch: {
      files: '',
      tasks: 'lint qunit'
    },
    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        boss: true,
        eqnull: true,
        browser: true
      },
      globals: {}
    },
    uglify: {}
  });

  // Default task.
  grunt.registerTask('default', 'lint qunit concat min');
};

  - module.exports = function(grunt){...}; 를 딱 한번만 grunt.js 에 표기한다 (module.exports 참조)

    + 가급적 module.exports를 사용한다 (exports 사용안함)

  - grunt.initConfig({...}); 에 환경설정 한다 

  - 환경 설정 참조 파일 : javascript-hooker/grunt.js

  - 일반 설정 사항

// Project configuration.
grunt.initConfig({
  // Project metadata, used by some directives, helpers and tasks.
  meta: {},
  // Lists of files to be concatenated, used by the "concat" task.
  concat: {},
  // Lists of files to be linted with JSHint, used by the "lint" task.
  lint: {},
  // Lists of files to be minified with UglifyJS, used by the "min" task.
  min: {},
  // Lists of files or URLs to be unit tested with QUnit, used by the "qunit" task.
  qunit: {},
  // Configuration options for the "server" task.
  server: {},
  // Lists of files to be unit tested with Nodeunit, used by the "test" task.
  test: {},
  // Configuration options for the "watch" task.
  watch: {},
  // Global configuration options for JSHint.
  jshint: {},
  // Global configuration options for UglifyJS.
  uglify: {}
});

  - plugins 또는 task를 로딩한다

// TASK
// Load tasks and helpers from the "tasks" directory, relative to grunt.js.
grunt.loadTasks('tasks');

// PLUGIN
// Load tasks and helpers from the "grunt-sample" Npm-installed grunt plugin.
grunt.loadNpmTasks('grunt-sample');


3) 사용하기 : grunt <task>

  - lint 수행 : grunt lint 

  - min 수행 : grunt min 

  - grunt 명령으로 default 수행 만들기 : grunt.registerTask('default', 'lint test concat min mincss'); 



4) Grunt.js & Yeoman 이해하기 


<참조>

  - Grunt.js Getting Started

  - Grunt.js 소개

  - 사용하기 : Outsider님 블로깅

posted by 윤영식
2013. 1. 28. 09:05 Dev Environment/Build System

참조의 원문에 대해서 테스트 해하고 글을 정리해 본다. JavaScript에 대한 빌드 자동화를 ant로 하는 방법을 알아보자.


Javascript코드에 대해 Ant를 이용하여 Java의 Maven처럼 compile -> test -> build를 자동으로 수행할 수 있는 방법을 소개하고 있다. 하지만 현재는 gruntjs를 통하여 본 글에서 수행하였던 것을 보다 더 쉽게 할 수 있다. gruntjs에서 하나 단점은 shell command 호출인데 grunt-jasmine-task를 통해  극복하였다. 


1) JavaScript 대응 테스트 & 빌드자동화 도구들

  - 구문오류 체크 : JSLint / JSHint 

  - 유닛 또는 행휘 테스트 : Jasmine 또는 QUnit

  - API 문서 생성 : JSDoc

  - JS 파일압축 : YUI compressor 또는 Uglify.js 

  - 위의 내역들 빌드 통합 수행 : Ant


각 내역들에 대해서 순차적으로 수행하는 것을 Ant로 만들어 보도록 한다 


2) Ant로 구현한 Build Automation 구조

  - GitHub 소스 : git clone https://github.com/creynders/Build-JS-example.git  수행하여 로컬에 내려 받는다 

  - Ant로 하다 보니 1)에서 필요로 하는 모든 프레임워크 라이브러리를 다운로드 받아 놓아야 한다   

   

  

  - 디렉토리 구조 

    + bin : ant의 destination 디렉토리, 배포되는 최종 .js 파일이 위치함 

    + docs : API 문서 생성 디렉토리 

    + lib : 3-rd party 도구들 

    + specs : unit test 파일 존재 (test 파일이 specification 즉, 명세라는 표현이 맘에 든다)

    + src : 원본 소스 


3) 도구 설명

  - Apache Ant : 의존관계를 만들어서 타겟소스에 대하여 빌드를 수행케 하는 command-line 도구 (다운로드)

  - JSHint : JSLint 기반의 자바스크립트를 위한 code 품질 측정 도구. 에러나 문제가능성 있는 것을 찾아줌 (다운로드)

  - JSDoc Toolkit : HTML (XML, JSON, TEXT) 포멧으로 자바스크립트 애플리케이션에 대한 자동 문서 생성 도구 (다운로드)

  - YUI compressor : 압축을 통하여 네트워크 전송 성능을 높여준다. (다운로드)

  - Jasmine : 자바스크립트 코드 테스트를 위한 행위지향 개발 프레임워크(behavior-driven) (다운로드)

  - PhantomJS : 자바스크립트에 대해서 브라우져 없이(without GUI client) 없이 브라우징이 가능토록 해준다 (다운로드)

    + 즉, HTML 파일의 parse & rendering을 해준 다음 수행하고 결과값 읽고 ant에게 전달한다 (Headless WebKit)

    + OS 버전에 맞는 것을 다운로드 받은후 path를 잡아준다

    + 좀 더 쉽게 사용할 수 있도록 casperjs 도 있다. (요걸 사용하는게 좋을듯)


4) 소스 빌드 프로세스 (순서)

  - 원본 소스는 3개 존재 : buildexample.js, Baz.js, Foo.js

    + buildexample.js에서 namespace를 정의한다 

  - 모든 .js 파일에 대해 JSHint를 통해서 QA tool을 수행한다 

  - unit test를 수행한다

  - 3개의 개별 .js 파일을 1개의 .js 파일로 통합한다

  - 한개의 파일을 축소된(minified) .js 파일로 나눈다 

  - API 문서를 생성한다 

  - 통합하고 축소화한 파일명칭과 API 문서에 version number를 사용한다 


5) build.xml 작성하기 

  * properties : 상수 속성값을 설정. build.properties

  * tasks : ant 수행전 전처리 환경 내역들 

  * targets : 각 단계에 대하여 ant에 task로 구분하여 설정. build.xml


  - 버젼 (Version Number)

    + version.txt 파일을 읽는다 

    + 테스트 : ant version

<target name="version">
    <loadfile property="version.old" srcFile="version.txt" />
    <input message="Current version number is ${version.old}. Please enter the new version number:"
        defaultValue="${version.old}" addproperty="version"/>
    <echo file="version.txt" message="${version}" />
</target>


  - 속성 선언(Properties Declaration)

    + build.properties 파일을 사용한다 

    + build.uer.properties에 속성 재정의 한다 

<target name="properties">
    <tstamp>
        <format property="timestamp" pattern="yyyyMMddHHmmss"/>
    </tstamp>
    <!-- allow user-specific overrides -->
    <property file="build.user.properties"/>
    <property file="build.properties"/>
</target>


  - 빌드 디렉토리 생성 

   + build 디렉토리 생성한다 : 생성 temporary 파일이 위치한다. 빌드과정이 성공하면 bin에 파일이 위치한다.

<target name="create_build" depends="properties">
    <echo>Creating build...</echo>
    <delete dir="${dir.build}" />
    <mkdir dir="${dir.build.current}" />
    <echo>Finished.</echo>
</target>


  - 파일 하나로 합치기 (Consolidation)

   + src 디렉토리의 buildexample.js를 복사해서 build 디렉토리에 buildexample-.js 로 넣는다

   + 다른 파일들을 buildexample-.js 에 포함시키고 version과 timestamp를 넣는다 

<target name="consolidate" depends="properties, create_build">
    <echo>Consolidating...</echo>
    <copy file="${dir.src}/${name.base.js}" tofile="${file.consolidated.js}"/>
    <replace file="${file.consolidated.js}" token="%VERSION%" value="${version}"/>
    <replace file="${file.consolidated.js}" token="%TIMESTAMP%" value="${timestamp}"/>
    <concat id="srcfiles" destfile="${file.consolidated.js}" append="true>
        <fileset dir="${dir.src}" includes="**/*.js" excludes="${name.base.js}"/>
    </conca>
    <pathconvert pathsep=";" property="files" refid="srcfiles"/>
    <echo>${files}</echo>
    <echo>Finished.</echo>
</target>


  - JSHint 구문 검사 (코드 품질 측정)

    + PhantomJS를 통하여 수행한다 (PhantomJS API)

    + phantom-jshint-runner.js 파일로 jshint.js를 수행토록 한다 (lib 디렉토리에 존재)

<target name="jshint" depends="properties, consolidate">
    <echo>Checking syntax...</echo>
      <exec executable="phantomjs" dir="${basedir}" failonerror="true" resultproperty="specs.results">
          <arg line="'${file.jshint-runner.js}'" />
          <arg line="'${file.jshint.js}'" />
          <arg line="'${file.consolidated.js}'" />
          <arg line="${timeout.phantom}" />
      </exec>
    <echo>Finished</echo>
</target>


  - Jasmine 테스트에서 .html 파일 수행 (specs/index.html 존재)

    + PhantomJS를 통해 html을 읽어와 파싱하고 결과값을 ant로 반환한다

    + phantom-jasmine-runner.js 파일을 작성한다 (web 애플리케이션 headless test 방법)

<target name="specs" depends="properties">
    <echo>Running specs...</echo>
    <exec executable="phantomjs" dir="${basedir}" failonerror="true" resultproperty="specs.results">
        <arg line="'${file.jasmine-runner.js}'" />
        <arg line="'${file.specs-runner.html}'" />
        <arg line="${timeout.phantom}" />
    </exec>
    <echo>Finished.</echo>
</target>


  - 압축 (Minifying)

    + YUI compressor를 사용한다 (사용법)

    + command line 사용 :  java -jar yuicompressor-x.y.z.jar [options] [input files]

<target name="minify" depends="properties,create_build, consolidate">
    <echo>Minifying...</echo>
    <exec executable="java" dir="${basedir}" failonerror="true">
        <arg line="-jar '${file.yui_compressor.jar}'" />
        <arg line="--type js" />
        <arg line="-o '${file.minified.js}'" />
        <arg line="'${file.consolidated.js}'" />
    </exec>
    <echo>Finished</echo>
</target>


  - JSDoc 파일 생성 

    + command line 사용 : java -jar jsrun.jar app/run.js myscript.js -t=templates/jsdoc

<target name="jsdocs" depends="properties, consolidate">
    <echo>recreating docs folder...</echo>
    <delete dir="${dir.docs}"/>
    <mkdir dir="${dir.docs}" />
    <echo>Generating...</echo>
    <exec executable="java" dir="${basedir}">
        <arg line="-jar '${file.jsdoc_toolkit.jar}' '${dir.jsdoc_toolkit}/app/run.js'" />
        <!-- -d tells JSDoc toolkit where to output the documentation -->
        <arg line="-d='${dir.docs}'" />
        <!-- use the default template -->
        <arg line="-t='${dir.jsdoc_toolkit}/templates/jsdoc'" />
        <!-- Create an arg element for each file you want to include in the documentation -->
        <arg line="'${file.consolidated.js}'" />
    </exec>
    <echo>Finished</echo>
</target>


  - Clean up

    + build 디렉토리 파일이 빌드가 성공하면 bin 디렉토리로 옮긴다 

<target name="finish" depends="properties">
    <echo>Finishing...</echo>
    <delete dir="${dir.bin}" />
    <mkdir dir="${dir.bin}" />
    <move file="${dir.build.current}" tofile="${dir.bin}"/>
    <echo>Finished.</echo>
</target>


  - 각 target을 한꺼번에 수행하기 

<target name="build" depends="version, properties, create_build, consolidate, jshint, specs, minify, jsdocs, finish">
</target>


  - 수행 : ant build

$ ant build

Buildfile: d:\git-repositories\build-automation-javascript\build.xml


version:

    [input] Current version number is 1.0.0. Please enter the new version number: [1.0.0]

1.0.1   <== 직접 입력


properties:


create_build:

     [echo] Creating build...

   [delete] Deleting directory d:\git-repositories\build-automation-javascript\build

    [mkdir] Created dir: d:\git-repositories\build-automation-javascript\build\20130128154436

     [echo] Finished.


consolidate:

     [echo] Consolidating...

     [copy] Copying 1 file to d:\git-repositories\build-automation-javascript\build\20130128154436

     [echo] concat (d:\git-repositories\build-automation-javascript\src\Baz.js;d:\git-repositories\build-automation-javascript\src\Foo.js)

     [echo] Finished.


jshint:

     [echo] Checking syntax...

     [exec] read: d:\git-repositories\build-automation-javascript/build/20130128154436/buildexample-1.0.1.js

     [echo] Finished


specs:

     [echo] Running specs...

     [exec] open: file:///D:/git-repositories/build-automation-javascript/specs/index.html

     [exec] success

     [exec] finished in 218ms.

     [exec] 2 specs, 0 failures in 0.196s

     [echo] Finished.


minify:

     [echo] Minifying...

     [echo] Finished


jsdocs:

     [echo] recreating docs folder...

   [delete] Deleting directory d:\git-repositories\build-automation-javascript\docs

    [mkdir] Created dir: d:\git-repositories\build-automation-javascript\docs

     [echo] Generating...

     [echo] Finished


finish:

     [echo] Finishing...

   [delete] Deleting directory d:\git-repositories\build-automation-javascript\bin

    [mkdir] Created dir: d:\git-repositories\build-automation-javascript\bin

     [move] Moving 2 files to d:\git-repositories\build-automation-javascript

     [echo] Finished.


build:


BUILD SUCCESSFUL

Total time: 16 seconds


* 현재는 Grunt를 많이 사용하고 있다. (참조)


<참조>

원문 : Automating JavaScript builds

posted by 윤영식
prev 1 next