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