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