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

Publication

Category

Recent Post

2013. 11. 25. 09:36 AngularJS/Start MEAN Stack

MEAN Stack을 가지고 서비스를 만드는데 사용을 하려면 어떤 순서로 익혀야 할까? 각각의 기술을 따로 배우는 것은 어차피 전문서적들이 있기 때문에 여기서 다시 상세히 설명하는 것은 의미가 없을 것 같고, 서비스를 기획, 개발, 런칭까지의 과정을 알아보자. 모든 과정의 내용은 웹 서비스쪽에 공개되어 공유될 것이다 




Sprint-1) Full Stack 개발자 되기

  - MEAN 개념과 개요

    + AngularJS

    + NodeJS + ExpressJS

    + MongoDB + Redis(Advanced)

  - MEAN 이외에 알아야 할 것들

    + JavaScript 

    + Bootstrap (or Zurb Foundation

    + NodeJS Modules

    + Mongoose



Sprint-1) 개발 준비 단계

  - 로컬 개발 환경 만들기 

    + Vagrant + Linux 환경 구성하기

    + 개발환경에 Node.js, MongoDB 설치하기 

    + Trello 에서 Scrum 개발하기 

  - MEAN Stack 개발도구 개발환경에 설치하고 익히기 

    + Yeoman : 클라이언트 라이브러리관리 및 배포 Grunt, Bower, Yo 설치하기 

    + NPM : 서버 라이브러리 관리

  - Git & GitHub 저장소 만들기 

    + Git Branch 전략 

    + GitHub 사용방법

  - 클라우드 환경에 배포하여 테스트 하기

    + Cloud Development(Nitrous.io) 를 통한 협업개발 및 테스트하기

    + Heroku같은 PaaS에 배포하여 테스트하기 

 


Sprint-1) MEANK Stack 개발 환경 구성하기

  - SRS 작성하기 

    + Software Request Requirement란 무엇인가?

    + Trello 에 소프트웨어 요구사항 정의서 작성하기 

    + Balsamiq 도구 사용하여 Wireframe & Mockup 그리기 화면 그리기

  - AngularJS Scaffolding 코드 사용하기 

    + angular-generator 알아보기 

    + angular 기본골격 만들기

    + NodeJS로 테스트 하기 

  - MEAN Stack 테스트 전략 

    + AngularJS에서의 테스트 방법 : Karma Framework

    + NodeJS에서의 테스트 방법 : Mocha 그외 



Sprint-2) 서비스 프로토타입 개발하기

  - 가장 중요한 화면 설계하기

    + 클라이언트 화면 Mockup 구체화 하기 

    + NodeJs에서 처리 할 RESTful API 설계하기 

    + MongoDB 에 저장 할 Document Schema 설계하기

  - MongoDB Store 개발 및 테스트

    + Dynamic Schema, Embedded vs Reference 에 대한 이해 

    + Mongoose 기반으로 Schema 만들기

    + Test Framework 이용한 Model 단위 테스트하기 

  - Node.js 기반 RESTful API 개발 및 테스트

    + RESTful API 코드 뼈대 만들기 in NodeJS

    + Test Framework 이용한 서비스 단위 테스트하기

    + RESTful Client 테스트 도구 또는 curl 로 호출 테스트하기

  - Angular.js 기반 화면 개발 및 테스트

    + AngularJS와 Bootstrap 이용하여 화면 구성하기

    + AngularJS 애플리케이션 구조 개발 및 RESTful API 서비스 개발 

    + Test Framework 이용한 화면 단위 테스트하기 

  - Heroku 같은 PaaS에 올려서 통합 테스트 하기 



Sprint-3) 서비스 개발 반복하기

  - 반복하기 

    + sprint-01에서 반복하였던 과정을 지속하기 

  - 메인 및 로그인 화면 만들기 

    + Bootstrap의 다양한 도구 활용

    + OAuth 기능 로그인 하기  

  - 채팅기능 만들기 

    + 채팅 Mockup 만들기

    + NodeJS Socket.io API 구현

    + MongoDB 채팅 Schema 설계

    + AngularJS Socket.io 멀티 케스팅 구현 

    + Bootstrap 통한 화면 구현 및 AngularJS 연동

 - Heroku 같은 PaaS에 올려서 프로토타입 테스트 하기 



Sprint-3) 서비스 런칭하기

  - 서비스 일일 빌드시스템 구성하기 

    + Travis (또는 Jenkins)기반 자동 배포 기능 만들기 

    + Travis 에서 빌드된 것을 클라우드시스템(PaaS) 자동 배포하기 

  - 런칭 페이지 만들기 

    + GitHub Pages를 이용한 런칭 만드는 방법

    + Bootstrap을 이용하여 런칭 페이지만들어 GitHub에 반영하기 

  - 모니터링 하기

    + 클라이언트 AngularJS Log 모니터링 하기
    + NodeJS Log 모니터링 하기 

    + MEAN Stack 운영 및 장애 모니터링 전략 



Next Project) MEAN Stack Advanced

  - 채팅기능 고도화하기 

    + SNS 서비스 아키텍쳐들 알아보기  

    + 대용량 데이터를 처리를 위한 아키텍쳐 설계하기

  - NodeJS + Redis + MongoDB 연동하기 

    + Redis 알아보기 

    + NodeJS-Redis 연동하기 

  - 정제된 데이터 RDBMS와 연동하기

    + NodeJS - MySQL 연동하기 

    + MongoDB의 MapReduce를 이용하여 데이터 마이닝하기 

    + NodeJS에서 MongoDB 마이닝 데이터 MySQL에 넣기 

  - SNS 서비스 운영 고도화

    + NodeJS와 Nginx 연동하기 

    + NodeJS를 Proxy 서버로 만들어 Clustering 하기

    + MongoDB Sharding 구조 만들기 

    + Redis Master/Slave구조 만들기 

  - 유지보수 전략

    + 테스트 환경과 프로덕션 환경의 재구성

    + NodeJS Scale-out 확장 전략

    + MongoDB Sharding 및 Data Backup 전략 

    + Redis Scale-out 전략 

  - 빅데이터의 활용

    + 개인화를 위한 데이터 활용 방안

    + MongoDB Integration Framework을 이용한 MapReducing


전체 서비스를 만들고 런칭하기까지 참 많은 것들을 익히고 개발해야 하지만, 누군가에게 의미있는 가치를 줄 수 있는 SNS(Social Network Service)를 할 수 있다는 것에 만족을 느낀다. 


posted by 윤영식
2013. 11. 20. 10:58 AngularJS/Start MEAN Stack

갑작이 떠오른 아이디어를 모바일 서비스로 빠르게 만들고 싶다. 클라이언트/서버/스토어 이런 것을 해줄 사람은 없다. 나 홀로 만들어 보고 프로토타입핑해서 스타팅해보고 싶을 때 MEAN Stack을 사용하자 




1. 스프링같은 "WebApp Framework" 에 대한 고민

  클라이언트단의 Android 또는 iOS 네이티브 코드는 익히는데 시간은 없고, 다양한 스마트 기기에 대응을 했으면 한다. 그리고 JavaScript를 어느 정도 할 수 있다. 이때 생각할 수 있는 대안이 "웹앱 사이트" 또는 "모바일 웹앱"을 만드는 것이다. 그러나 예전 jQuery 코드를 생각해 보자. 조금만 복잡해 지면 쉽게 스파게티가 되고, 테스트 코드는 엄두도 못내며 결국 유지보수의 골칫덩어리로 남게 된다. 

그렇다면 자바에서 Spring Framework과 같이 DI가 되면서 MVC처럼 모듈화 개발을 가능하게 해주는 클라이언트단 프레임워크는 없을까? 처음엔 Backbone.js를 살펴 보았고, 예로 Trello와 같은 서비스에서 사용을 하고 있다. 하지만 코드의 길이가 길어지고 불필요한 중복코드의 남발이 야기되어 좀 더 간결하면서 예전 Flex Framework과 같은 것은 없을지 고민해 보았다. HTML을 확장해서 사용해도 자동 해석을 하여 브라우져가 이해할 수 있는 HTML로 바꾸어 주면 되는 그런 프레임워크 말이다. 역시 구글 형님들도 똑같은 고민을 하고 프로젝트에 적용하고 놀라운 효과를 본 새로운 프레임워크를 공식 런칭하게 되니 이름하여 AngularJS





2. 새로운 "I/O Machine" 에 대한 고민

  서비스에서 요하는 기능중 가장 중요한 부분은 Push 기능이 아닐까 싶다. 실시간으로 변화하는 정보를 수많은 유저에게 바로 Push 서비스해줄 수 있는 서버가 필요하다. 하지만 Push를 위하여 별도의 Port를 사용하긴 싫고 웹앱이므로 요청하였던 HTTP Port를 사용하고 싶다면 어떤 것을 선택해야 할까?

서버단이 Java라면 vert.x도 고려해 볼 만하다. 하지만 아직 레퍼런스와 문서가 부족하다. 하지만 여기에 우리에게 구세주와 같은 세로운 I/O 머신이 나왔으니 V8엔진을 기반으로 돌아가는 Node.js이다. Node.js를 사용하는 가장 큰 목적중 하나가 Socket.io와 같은 이벤트 기반의 Push 모듈들이 아닐까 생각이든다. Node.js는 모듈기반으로 필요한 것들을 추가하여 사용할 수 있다. 이를 위하여 NPM(Node Package Manager)이 존재하고 방대한 모듈과 레퍼런스들이 존재한다. 또한 Chrome에 적용된 V8 자바스크립트 해석기를 달고 있으므로 모든 코딩은 JavaScript 만으로 이루어진다





3. 스키마에 자유로운 "Store"에 대한 고민 

  서비스를 빠르게 만들고 싶은데 개발을 진행하다 보면 자주 테이블이 변경되고 이에 맞추어 서버코드들도 수정을 해주거나 새롭게 테이블을 생성해 주는 번거로운 작업들을 거친다. 따라서 스키마가 자유로운 데이터베이스를 원한다. 그리고 서비스이므로 사용자의 데이터를 많이 쌓아 두고 싶다. 이를 통해 향후 마케팅 용으로 사용하려 한다. 이왕이면 클라이언트/서버가 모두 자바스크립트이므로 데이터베이스 핸들링도 자바스크립트 이면 좋겠다. 이를 위해 새롭게 나온 NoSQL 이 있으니 이름하여 MongoDB. JSON형태로 데이터를 저장하고 정규화 없이 도큐먼트 형태로 저장을 할 수 있어서 스키마의 제약이 없다. 또한 테라급의 데이터 정도는 MongoDB를 Sharding구조로 만들어 MapReduce를 이용하여 처리할 수 있다. 그리고 Node.js 처럼 V8 해석기를 탑재하여 데이터 핸들링을 JavaScript로 할 수 있다. 마치 Oracle의 SqlPlus에서 Sql을 수행하듯 MongoDB가 정의한 형식의 쿼리를 수행할 수 있다. 





4. Node.js를 기업용 WAS(Web Application Server)로 만들기  

  초창기 Java를 떠올려 보자. 처음엔 Applet에 열광하여 업무를 애플릿으로 만든적이 있다. 그때 서버단은 Apache + PHP정도 였을 것이다. 그리고 SUN에서 J2EE 스팩을 발표하면서 Tomcat, JBoss, WebSphere, WebLogic 같은 WAS가 나왔으며, 이 또한 C계열의 TP-Monitor처럼 Java기반 I/O Machine 이 나오게 된 것이다. Java진영의 WAS는 나름의 공통된 스팩을 기반으로 웹을 이끌어 갈 수 있는 새로운 I/O 머신으로 떠올랐고, 기업에서 요구하는 Transaction 처리에 대한 스팩도 제안하여 많이 사용하게 되었다. 최근에는 Tomcat같은 Servlet 엔진만 갖춘 WAS에 Spring Framework를 사용하면 충분히 기업용 Transaction을 처리하는 업무를 개발할 수 있는 환경까지 오게 되었다. 그러나 Java의 WAS가 web1.0의 중심 머신이었다면 Node.js기반의 WAS가 web2.0의 중심 머신으로 되기 위한 조건은 무엇일까?

  결국 Node.js위에 올라가는 Java의 J2EE 스팩과 같은 견고한 스팩이 필요하다. 현재까지는 아직 그러한 스팩이 나오진 않으나 Java의 Servlet 스팩에 비견할 만한 프레임워크가 나왔으니 그것이 Express.js 이다. 이제 web2.0의 I/O 머신은 Node.js가 될 것이고, 여기에 웹에 대한 표준 스팩구현체는-사실 Node.js를 위한 공통된 스팩은 없다- 아니지만 가장 많이 사용하는 Express.js를 통하여 Node.js가 Tomcat과 같은 기능을 보유하게 되었다.




Angular.js -> Node.js + Express.js -> MongoDB 를 사용하게 되면 

  - JavaScript로 모두 개발을 한다. 클라이언트/서버 분리하여 개발할 필요가 없다. 그냥 FullStack JavaScript개발을 하면 된다

  - 클라이언트 개발이 MV* 개발이 되어 서버 개발에 익숙한 개발자가 쉽게 접근할 수 있다 

  - 서버에서 Push 기능을 쉽게 적용할 수 있으므로 다양한 서비스의 응용이 가능하다 

  - 자바의 서블릿 엔진 구현체인 Tomcat과 같이 충실한 RESTful I/O 머신을 쉽게 구현할 수 있다

  - 서버의 멀티 쓰레드 문제에서 해방될 수 있다. Asynch의 세상에서 놀자 

  - 데이터 스키마를 미리 정의하지 않고 에자일하게 개발을 진행할 수 있다

  - 큰 규모의 데이터를 저장하고 처리 할 수 있다  



그러나 우리에게 남은 과제는 이것들을 어떻게 배우고 사용하는지 잘 모른다는 것이다. 각자를 심도 있게 배우는 것은 별도의 가이드북을 따로 보길 바란다. 대신 여기서는 실전 서비스를 어떻게 하면 빠르게 만들 수 있을지 노하우를 적어보려한다. 낮에는 회사에서 업무를 보면서 향후 스타트업을 준비하는 개발자 또는 사람들에게 좀 더 이로운 가치를 전달해 주고 싶은 개발자가 3개월안에 개발할 수 있는 서비스를 이 책을 통해 런칭할 수 있는 것이 가능해 질 수 있길 바란다. 그래서 책을 쓰면서 동시에 작은 서비스를 만들어 보려 한다. 책은 가이드이고 그물일 뿐이다. 실제 돌아가는 서비스가 물고기이다. 즉, 실제 서비스를 낚지 못하면 이 책의 가치도 없다고 생각한다. 실제로 이것을 증명해 보이고 싶다. 



Getting Start MEAN

          


posted by 윤영식
2013. 11. 9. 07:08 My Services/PlayHub

Angular 기반 SPA를 위한 Push 환경은 Node 기반의 Socket.io를 사용한다. Angular.js의 장점중 하나가 DI(Dependency Injection)이고 이를 Node에서 구현한 프레임워크인 express-train (축약, train)을 사용한다. train은 특정 폴더에 .js 파일을 놓으면 자동으로 DI되어 사용을 할 수 있게 해준다. 일정한 규약만을 지키면 개발 코드를 모듈화 하여 관리할 수 있고 테스트 할 수 있다. train은 express에 DI 기능을 접목해 놓은 것으로 보면 된다





1. Express-Train 설치하기 

  - 설치 : npm install -g express-train 

  - 새로운 Node 환경 프로젝트 생성

// train이 사용하는 기본 템플릿을 기반으로 자동으로 palette.fronend.node 디렉토리에 기본 폴더와 파일을 생성해 준다 

$ train new palette.frontend.node 

// 수행 http://localhost:4000 호출 

$ train run 

  - 디렉토리 구조 

    + app : auto-injected 로 표현된 것 .js 파일을 해당 디렉토리에 놓으면 다른 곳에서 파일 명칭으로 불러와서 (function parameter DI 방식) 사용을 할 수 있다 

    + bin : 이것은 node_modules/express-train 밑에 존재한다 

    + test : Mocha를 이용한다

    + public : Angular.js 기반으로 작성한 모든 static 파일들이 이곳에 놓일 것이다. 엄밀히 따지면 Distributed된 Frontend파일이 놓인다

app

  /controllers      -- application controllers (**autoinjected**)

  /lib                 -- application specific modules (**autoinjected**)

  /middleware    -- application middleware (**autoinjected**)

  /models          -- application models (**autoinjected**)

  /public            -- static content (html, js, css, etc) : SPA모든 파일을 해당 폴더 밑에 복사할 것이다  

  /views            -- view templates (loaded into express' view engine) : 서버단의 파일 jade, ejs, hbs 같은 서버 template 파일

  index.js           -- index file exports the express train application : train run 시에 수행되는 파일 based on Node.js


bin                   -- executable scripts : 

doc                  -- documentation

config              -- environmental configuration files : train 환경 설정 파일 

test                 -- tests : 서버 테스트 파일

package.json    -- npm package.json (needs to have express-train as a dependency)

  - train의 환경파일 : development와 production 환경파일을 구분함 

{

  "cookie_secret": "boggle at the situation",

  "session":{

    "key": "boggle at the situation",

    "secret": "boggle at the situation"

  },

  "client_port": 3000,

  "request_timeout": 1000,

  // RESTful 호출 접두사 Restanuglar 참조 

  "restful_api_version": "/api/v1",

  "server_name": "first",

  // REDIS 연결 Client Library 환경 설정

  "data_broker": {

    "port" : 6379,

    "host" : "localhost",

    "auth" : ""

  },

  // MongoDB 환경 설정

  "mongodb":{

    "uri": "mongodb://localhost/playhub_palette"

  },

  "playhub_channel_file": "channels.json",

  // Winston 에러 로그가 쌓일 MongoDB 정보 

  "winston_mongo": {

    "level": "error",

    "db": "playhub_palette",

    "host": "localhost",

    "port": 27017,

    "collection": "sd_logs"

  },

  // Winston 로그 파일의 명칭 설정

  "winston_file": {

    "filename": "sd.log",

    "exception": "sd_exception.log",

    "maxSize": 20480,

    "maxFiles": 12,

    "colorize": true

  },

  "character_encoding":"utf-8"

}



2. Express-Train 파일 사용하기 

  - auto-injected 되는 폴더에 모듈로 운용하고 싶은 .js 파일을 놓는다. 주로 MVC 파일과 Utility 또는 Service 파일들이다 

  - auto-inject 환경내역은 node_modules/express-train/lib/app.js 파일안에 이미 설정되어 있다.

// app.js 내역중 일부 

// autoinject라는 옵션을 주면 해당 지정된 폴더의 .js파일을 메모리에 로딩하고 DI를 한다 

// 이때 주의할 것은 상호 참조가 되지 않도록 하기 위한 폴더 전략이 중요하다. 위에서 부터 아래순서로 로딩되어 DI 된다 

var LOCATIONS = createApplication.locations = {

    pkg:{

        path: '../package.json'

    },

    config:{

        path: '../config'

    },

    logs:{

        path: '../logs'

    },

    models: {

        path: 'models',

        autoinject: true,

        aggregateOn: 'models'

    },

    views: {

        path: 'views'

    },

    lib: {

        path: 'lib',

        autoinject: true

    },

    controllers: {

        path: 'controllers',

        autoinject: true

    },

    pub: {

        path: 'public'

    },

    middleware: {

        path: 'middleware',

        autoinject: true

    }, 

   // 만약 app밑에 services폴더를 만든다면! 

    services: {

       path: 'services',

       autoinject: true

    },

}; 


// app.js 에서 express를 생성하고 views와 public 폴더를 강제 설정하고 있다

// _.each에서 autoinject 폴더의 모든 파일을 읽고 DI를 수행해 준다. 이때 DI되는 .js 파일은 Singleton 패턴이다 

// tree.constant로 저장된 객체를 DI 받을 수 있다

function createApplication(dir, locs) {

    catchErrors();


    var app = express(),

        locations = _.defaults((locs || {}), LOCATIONS);

        tree = new nject.Tree();


    app.set('views', path.join(dir, locations.views.path));

    app.set('public', path.join(dir, locations.pub.path));


    var config = loadConfig(path.join(dir, locations.config.path));

    tree.constant('config', config);


    _.each(_.where(locations, {autoinject: true}), function(location) {

        traverseAndRegister(path.join(dir, location.path), tree, location.aggregateOn)

    });


    //allow override of app

    if(!tree.isRegistered('app')) {

        tree.constant('app', app);  

    }


    //log path 하나 추가함 

    var logPath = path.join(dir, locations.logs.path);

    tree.constant('logPath', logPath);


    return tree.resolve();

}


  - 폴더 전략 : app.js에 설정된 순서에-위에서 아래로- 따라 로딩됨, 참조가 많은 것은 주로 service가 되지 않을까 함

    + lib/1level : 가장 기본적인 파일 참조하는 것이 없는 것들 - util, logger, db connection, constant

    + lib/2level : express-train의 파일들  - route, views, middleware 설정

    + lib/3level : 1, 2level의 .js를 사용하는 파일들 

////////

// 형식

module.exports = function(app, config, logger, ...) {

  // app, config, logger 등 파일이름을 대소문자 구분하여 넣으면 자동 DI를 해준다. 객체는 train에서 Singleton으로 관리한다 

}


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

// 예) lib/1level/logger.js

// MongoDB에 winston logger를 이용하여 에러발생 내역을 저장하거나 파일로 떨굼 


var winston = require('winston'),

  path = require('path');


module.exports = function(app, config, logPath) {


  winston.info('[1level: log config] init');

  require('winston-mongodb').MongoDB;


  var dbOpts = {

    level: config.winston_mongo.level,

    db: config.winston_mongo.db,

    collection: config.winston_mongo.collection,

    host: config.winston_mongo.host,

    port: config.winston_mongo.port

  };

  winston.info('[1level: log config] DB option is ' + JSON.stringify(dbOpts) );


  var fileName = path.join(logPath, config.winston_file.filename);

  winston.info('[1level: log config] general log : ' + fileName);

  var exceptionFileName = path.join(logPath, config.winston_file.exception);

  winston.info('[1level: log config] exception log : ' + exceptionFileName);


  var fileOpts = {

    filename: fileName,

    maxsize: config.winston_file.maxSize,

    maxFiles: config.winston_file.maxFiles,

    colorize: config.winston_file.colorize

  }


  var exceptionOpts = {

    filename: exceptionFileName,

    maxsize: config.winston_file.maxSize,

    maxFiles: config.winston_file.maxFiles,

    colorize: config.winston_file.colorize

  }


  var logger = new winston.Logger({

    transports: [

      new winston.transports.Console(),

      new winston.transports.File(fileOpts),

      new winston.transports.MongoDB(dbOpts)

    ],

    exceptionHandlers: [

      new winston.transports.Console(),

      new winston.transports.File(exceptionOpts),

      new winston.transports.MongoDB(dbOpts)

    ]

  });


//  testing

// logger.log(config.winston_mongo.level, '[log config] Test logging module completely');

//  logger.info('==============>> hi info');

//  logger.warn('==============>> hi warn');

//  logger.error('==============>> hi error');  <- MongoDB에도 쌓인다 


  return logger;

}


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

// 예) lib/2level/ 또는 lib/2level에서 logger사용하기 

// lib/2level/views.js 파일에서 

// logger.js 파일에서 파일명칭을 - 대소문자 구분 - Function Parameter DI 해준다 

module.exports = function (applogger) {

  logger.info('[2level: view engine - jade] init');

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

};




방법-1. Angular 프로젝트 배포후 Express-Train 프로젝트로 통합 

  - 엄밀히 따지면 합치는 시점은 개발이 다 된후 수행한다. 즉, SPA와 Node 파트의 Git 저장소를 틀리게 가져감을 전제로 한다 

    + SPA 저장소 : palette.frontend

    + Node저장소 : palette.frontend.node 

  - 개발이 된후 SPA와 Node 통합은 다음 순서로 하자 

    + Yeoman을 통하여 생성된 Angular 스케폴딩 파일은 별도로 개발한다 -> grunt build 수행 -> dist 폴더에 배포파일이 생성된다 

    + palette.frontend.node/app/public/ 와 palette.frontend.node/app/views 폴더 밑의 모든 파일을 삭제한다

    + palette.frontend.node/app/public/ 밑으로 dist/* 모든 파일을 복사한다 

// 폴더구조 

playhub

  |_ palette.frontend         (별도 git repository)

  |_ palette.frontend.node (별도 git repository)


// Angular 기반 frontend 파일 

playhub$ cd palette.frontend

playhub$ grunt build 


// Express-train에서 사용하지 않을 파일 삭제  

playhub$ cd ../palette.frontend.node/app/public

playhub$ rm -rf *

playhub$ cd ../views

playhub$ rm -rf *


// Angular 기반 SPA frontend파일을 Express-train의 app/public/ 폴더 밑으로 복사 

playhub$ cp -rf ../../../palette.frontend/dist/* ../public/

  - 테스트 하기 

$ cd palette.frontend.node

train run 

// http://localhost:4000/ 으로 호출한다 



방법-2. Express-Train 프로젝트를 Angular 프로젝트로 통합

  - Angular 프로젝트(palette.frontend)의 grunt, bower, yo, karma 기능을 그대로 사용한다 

  - Express-Train 프로젝트(palette.frontend.node)는 express-train기반으로 이미 스케폴딩된 서버단 애플리케이션이다. 

    즉 palette.frontend.node의 디렉토리 구조를 변경시켜보자  

// express-train 프로젝트로 이동하기 

$ cd palette.frontend.node


// express-train의 bin 디렉토리를 palette.frontend.node로 이동하기  

$ cd node_modules/

$ cd express-train/

$ ls -arlt

total 80

drwxr-xr-x   3 nulpulum  staff    102  9 17 17:20 test

-rw-r--r--   1 nulpulum  staff     90  9 17 17:20 index.js

-rw-r--r--   1 nulpulum  staff     82  9 17 17:20 .npmignore

-rw-r--r--   1 nulpulum  staff  13393  9 26 19:13 ReadMe.md

drwxr-xr-x   4 nulpulum  staff    136 11  8 14:14 bin

drwxr-xr-x   5 nulpulum  staff    170 11  8 14:14 boilerplates

-rw-r--r--   1 nulpulum  staff  14976 11  8 14:14 package.json

drwxr-xr-x   4 nulpulum  staff    136 11  8 14:14 lib

drwxr-xr-x  12 nulpulum  staff    408 11  8 14:14 node_modules

drwxr-xr-x  11 nulpulum  staff    374 11  8 14:14 .

drwxr-xr-x  13 nulpulum  staff    442 11  8 14:14 ..

// bin을 palette.frontend.node 루트로 이동시킨다 

$ mv bin ../../


// bin으로 들어가서 train run 명령을 수행 - 내부적으로 commander 모듈을 사용한다 

$ train run

module.js:340

    throw err;

          ^

Error: Cannot find module 'commander'

    at Function.Module._resolveFilename (module.js:338:15)

    at Function.Module._load (module.js:280:25)

    at Module.require (module.js:364:17)

    at require (module.js:380:17)

    at Object.<anonymous> (/Users/nulpulum/development/playground/playhub/palette.frontend.node/bin/train:3:15)

    at Module._compile (module.js:456:26)

    at Object.Module._extensions..js (module.js:474:10)

    at Module.load (module.js:356:32)

    at Function.Module._load (module.js:312:12)

    at Function.Module.runMain (module.js:497:10)


// 에러가 발생하므로 commander, underscore, handlebars, boilerplate 모듈을 설치한다 

$ cd .. && npm install commander --save 

$ npm install underscore --save

$ npm install handlebars --save

$ npm install boilerplate --save-dev

$ cd bin && train run 

module.js:340

    throw err;

          ^

Error: Cannot find module '../../lib/app'

    at Function.Module._resolveFilename (module.js:338:15)

    at Function.Module._load (module.js:280:25)

    at Module.require (module.js:364:17)

    at require (module.js:380:17)

    at Object.<anonymous>(/Users/nulpulum/development/playground/playhub/palette.frontend.node/bin/commands/console.js:4:13)

    at Module._compile (module.js:456:26)

    at Object.Module._extensions..js (module.js:474:10)

    at Module.load (module.js:356:32)

    at Function.Module._load (module.js:312:12)

    at Module.require (module.js:364:17)


// 에러 다시 수정 하기 

$ cd ../node_modules/express-train/

$ ls -alrt

total 80

drwxr-xr-x   3 nulpulum  staff    102  9 17 17:20 test

-rw-r--r--   1 nulpulum  staff     90  9 17 17:20 index.js

-rw-r--r--   1 nulpulum  staff     82  9 17 17:20 .npmignore

-rw-r--r--   1 nulpulum  staff  13393  9 26 19:13 ReadMe.md

drwxr-xr-x   5 nulpulum  staff    170 11  8 14:14 boilerplates

-rw-r--r--   1 nulpulum  staff  14976 11  8 14:14 package.json

drwxr-xr-x   4 nulpulum  staff    136 11  8 14:14 lib

drwxr-xr-x  12 nulpulum  staff    408 11  8 14:14 node_modules

drwxr-xr-x  10 nulpulum  staff    340 11  9 15:19 .

drwxr-xr-x  16 nulpulum  staff    544 11  9 15:29 ..


// express-train의 lib밑의 모든파일을 bin 밑으로 이동시킨다 

$ mv lib/* ../../bin/

// index.js 파일을 이동한 bin 디렉토리에 같이 놓는다 

$ mv index.js ../../bin/


// express-train 은 삭제한다 

$ cd .. && rm -rf express-train

$ cd ../../bin/

// 최종 lib이 bin 밑으로 들어갔다 

$ ls -alrt

total 32

-rw-r--r--   1 nulpulum  staff    64  9 17 17:20 runner.js

-rw-r--r--   1 nulpulum  staff  3594  9 27 14:06 app.js

drwxr-xr-x  11 nulpulum  staff   374 11  9 15:19 ..

-rw-r--r--   1 nulpulum  staff    89 11  9 15:56 index.js

-rwxr-xr-x   1 nulpulum  staff   785 11  9 16:02 train

drwxr-xr-x   7 nulpulum  staff   238 11  9 16:02 .

drwxr-xr-x   3 nulpulum  staff   102 11  9 16:08 commands


// commands 폴더에서 runt.js를 제외한 사용하지 않는 나머지 파일은 삭제하거나 나중을 위해서 다른 위치로 백업한다 

$ cd commands && rm console.js cycle.js new.js boilerplate.js


// commands 폴더의 run.js 안에 내역을 수정 한다 

// 기존 내역 : var appPath = path.join(process.cwd(), '../app/index.js');

// 수정 내역 

var appPath = path.join(process.cwd(), './index.js');


// lib밑에 있는 index.js가 가르키는 파일 수정 

// 기존 내역 : app: require('./lib/app')

// 수정 내역

// module.exports = {

//   app: require('./app.js')

// };


var paletteApp = require('./app.js');

module.exports = paletteApp(__dirname);


// palette.frontend.node 루트의 app 디렉토리명칭을 api 라고 변경한다 

$ cd ../../ && mv app api 

  

// 다음으로 직접 app.js안의 인식하는 디렉토리 경로를 상대경로로 변경한다 

$ cd bin && vi app.js

//위치 변경하기 

var LOCATIONS = createApplication.locations = {

    pkg:{

        path: '../package.json'

    },

    config:{

        path: '../config'

    },

    logs:{

        path: '../logs'

    },

    models: {

        path: '../api/models',

        autoinject: true,

        aggregateOn: 'models'

    },

    views: {

        path: '../api/views'

    },

    lib: {

        path: '../api/lib',

        autoinject: true

    },

    controllers: {

        path: '../api/controllers',

        autoinject: true

    },

    // 향후 palette.frontend의 grunt build시에 dist 폴더 밑으로 되는 것을 public 폴더로 바꿀 것이다 

    pub: {

        path: '../public'

    },

    middleware: {

        path: '../api/middleware',

        autoinject: true

    }

};


// api 디렉토리 밑에 있는 public 디렉토리를 루트로 옮겨서 테스트 해본다 

$ mv app/public .


// bin 디렉토리 밑에 있는 train 쉘 실행하기 

$ train run 

[express train application listening on 4000]


  - Express-Train기반의 서버구조를 만들었고, 이제 이것을 palette.frontend 쪽으로 옮겨보자 

// palette.frontend.node 프로젝트의 package.json 명칭을 변경 - 나중에 palette.frontend쪽의 package.json과 합치기 위함

$ mv package.json packate.json.node


// public 폴더는 삭제한다 

$ rm -rf public 


// 옮기기 

$ mv * ../palette.frontend/

mv: rename node_modules to ../palette.frontend/node_modules: Directory not empty

mv: rename test to ../palette.frontend/test: Directory not empty

$ cd node_modules

$ mv * ../../palette.frontend/node_modules/


// palette.frontend 에서 dist를 public으로 명칭 변경하여 테스트 

$ cd ../../palette.frontend

$ mv dist public 

$ cd bin

$ train run

[express train application listening on 4000]


// 이제 grunt build를 수행할 때 dist 디렉토리를 만드는 것이 아니라 public 으로 만들어 보자 

$ cd .. && rm -rf public 


// grunt 환경파일의 내역 수정 

$ vi Gruntfile.js

// 변경내역 

 grunt.initConfig({

    yeoman: {

      // configurable paths

      app: require('./bower.json').appPath || 'app',

      dist: 'public'  // 'dist'를 'public'으로 변경한다 

    },


// 다시 grunt로 빌드를 하면 public 폴더가 생성된다 

$ grunt build 

$ cd bin && train run 

[express train application listening on 4000]


// 최종 palette.frontend의 합친 구조

  - api : Node 서버 

  - app : SPA Angular

  - public : app 배포 디렉토리이면서 bin/train run 수행시 인식되는 서비스 폴더


// palette.frontend git 저장소에 push하기전에 package.json.node의 내용을 package.json에 통합한다 

{

  "name": "palette",

  "version": "0.0.1",

  "dependencies": {

    "express": "3.2.x",

    "express-train": "1.0.x",

    "connect-timeout": "latest",

    "connect-mongodb": "latest",

    "mongoose": "latest",

    "async": "latest",

    "express-hbs": "latest",

    "passport": "latest",

    "winston": "latest",

    "commander": "~2.0.0",

    "underscore": "~1.5.2",

    "handlebars": "~1.1.2",

    "nject": "~0.1.6"

  },

  "devDependencies": {

    "grunt": "~0.4.1",

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

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

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

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

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

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

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

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

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

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

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

    "grunt-contrib-watch": "~0.5.2",

    "grunt-autoprefixer": "~0.2.0",

    "grunt-usemin": "~0.1.11",

    "grunt-svgmin": "~0.2.0",

    "grunt-rev": "~0.1.0",

    "grunt-concurrent": "~0.3.0",

    "load-grunt-tasks": "~0.1.0",

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

    "grunt-ngmin": "~0.0.2",

    "time-grunt": "~0.1.0",

    "karma-ng-scenario": "~0.1.0",

    "grunt-karma": "~0.6.2",

    "karma-script-launcher": "~0.1.0",

    "karma-chrome-launcher": "~0.1.0",

    "karma-firefox-launcher": "~0.1.0",

    "karma-html2js-preprocessor": "~0.1.0",

    "karma-jasmine": "~0.1.3",

    "karma-requirejs": "~0.1.0",

    "karma-coffee-preprocessor": "~0.1.0",

    "karma-phantomjs-launcher": "~0.1.0",

    "karma": "~0.10.4",

    "karma-ng-html2js-preprocessor": "~0.1.0",

    "mocha": "latest"

  },

  "engines": {

    "node": ">=0.8.0"

  },

  "main": "./app/index.js",

  "scripts": {

    "test": "grunt test",

    "start": "cd bin && train run",

    "server-test": "mocha test"

  }

}


// npm 명령으로 scripts 설정 내역 호출하기 

$ npm start

[express train application listening on 4000]


  - 최종 완성본 저장소 : MyPlayHub GitHub Repository

// git clone 하여 train run 수행시 다음과 같이 오류가 발생할 경우 

$ train run

env: node\r: No such file or directory


// dos2unix 패키지를 설치하여 수정하여 준다 

$ brew install dos2unix

==> Installing dos2unix dependency: gettext

==> Downloading http://ftpmirror.gnu.org/gettext/gettext-0.18.3.tar.gz

.. 중략 ..


$ dos2unix train

dos2unix: converting file train to Unix format ...

$ train run 

[express train application listening on 4000]



방법-3. Yeoman Generator FullStack을 통한 통합

  - Express + Angular의 통합

  - Angular는 클라이언트의 파일 전송과 암호화를 위하여 Lint와 압축을 위해 Grunt를 사용함 

  - Express는 서버단이므로 압축이 필요없음 

// FullStack generator 설치 

$ sudo npm install -g generator-angular-fullstack

// FullStackTest라는 애플리케이션 명칭으로 스케폴딩 생성

$ yo angular-fullstack FullStackTest

// 빌드하여 클라이언트 파일 Lint 및 압축 

// public 폴더밑으로 배포본이 만들어짐 

$ grunt build 

... 중략 ...

Elapsed time

useminPrepare:html                   25ms

concurrent:dist                          5s

autoprefixer:dist                        113ms

concat:public/scripts/modules.js  23ms

copy:dist                                  640ms

cdnify:dist                                 21ms

ngmin:dist                                263ms

cssmin:public/styles/main.css     33ms

uglify:dist                                  28ms

uglify:public/scripts/plugins.js      373ms

uglify:public/scripts/modules.js   134ms

usemin:html                             482ms

usemin:css                              152ms

Total                                        8s



<참조> 

  - Angular-Express-Train-Seed

  - Yeoman : generator-fullstack with angular and express

  - dos2unix 설치하여 사용하기

posted by 윤영식
2013. 9. 25. 09:16 My Services/Smart Visualization

SPA 방식의 개발을 위해서는 Client에 MV* Framework을 선택해야 한다. 초창기는 Backbone.js(이하, 백본)를 많이 사용하였고, 구현체로 Trello가 있으니 해당 서비스의 아키텍쳐를 참조해 보자. 그리고 SKT에서 코너스톤이라는 이름의 Framework도 백본을 사용하여 "웹앱"을 만들 수 있도록 안드로이드/iOS용 런타임 환경도 제공한다. 백본은 가벼우면서 광범위하게 많이 사용을 하고 있지만 개발 복잡성 비교에서 중간정도에 위치한다. 


<Trello 아키텍쳐>



브라우져에서 동작하는 애플리케이션의 템플릿이 .html 이면서 <chart ../> 와 같이 사용자 정의 html tag를 사용하여 View단을 좀 더 단순화 및 컴포넌트화 할 수 있는 AngularJS를 사용하기로 한다. AngularJS에는 여러 장점이 있지만 "웹 애플리케이션을 견고하게 만드는 방법" 블로깅을 통해 백본대신 왜 AngularJS를 선택했는지 갈음한다. AngularJs 프레임워크 기반의 SPA 개발을 위하여 쉽게 접근하는 방법은 도구를 통하여 하는 것인데, 오스마니님이 개발한 Yeoman을 통하여 AngularJS기반 프로젝트를 쉽게 구성할 수 있다. 



1. Yeoman 사용하기

  - 사전에 NodeJS 최신버전이 설치되어 있어야 한다. 

    설치후 npm (Node Package Manager)를 통하여 Yeoman(이하, yo)을 설치함

  - Yo 설치하기  : 가이드를 따라서 설치하면 된다 

    + yo : angular, express 등 관련 framework에 대한 scaffolding 프로젝트를 자동으로 만들어 주는 기능을 한다 

              generator-angular, generator-express와 같이 generator-*가 앞에 붙는다. npm 통하여 설치함 

              generator 만드는 방법은 "generator-angular와 express 연동하기" 블로그 참조한다 

    + bower : client단의 필요한 컴포넌트를 자동설치할 수 있다. 의존성관계 bower.json 파일에 설정한다. npm 과 유사함

    + grunt : 개발된 소스를 테스트, 빌드하는 javascript의 ant과 같은 역할을 한다. Gruntfile.js 에 설정한다 

    


  - yo 설치후 "yo -h" 수행하면 설치된 generator 목록을 볼 수 있다. 또는 yo라고 수행해도 된다 (보여지는 형식만 틀리다)

// generator와 관련된 MUST HAVE generator

$ sudo npm install -g generator-generator


// angularjs 관련 generator 설치 

$ sudo npm install -g generator-angular


// AngularJS 관련 scaffolding 명령 목록 

$ yo -h 

Angular

  angular:app  (default : angular) 

  angular:common

  angular:constant

  angular:controller

  angular:decorator

  angular:directive

  angular:factory

  angular:filter

  angular:main

  angular:provider

  angular:route

  angular:service

  angular:value

  angular:view


// express generator도 설치하자 

sudo npm install -g generator-express


* 여기서 잠깐!

만일 사용하는 OS가 Windows라면 지금 당장 Linux를 설치해서 사용한다. 우리가 배포하고 서비스하는 서버는 Cloud환경일 가능성이 다분히 높기 때문이다. Windows Server같은 곳에서 Node.js를 운영하는 것은 상상조차 하고 싶지 않다. 

  - Vagrant를 Windows에 설치해서 쉽게 Linux환경에 접근토록 하자



2. AngularJS 스케폴딩 만들기

  - yo를 통하여 AngularJS 기반의 SPA 프로젝트를 만든다. 여기에는 Node.js로 동작하는 서버 코드는 생성되지 않는다 

  - GitHub에 SmartVisualization 라고 생성을 한다. 각자의 계정에 만든다 

// GitHub의 저장소 clone 하고 angular 스케폴딩 명령 수행

$ git clone https://github.com/<자신의 아이디>/SmartVisualization.git  SmartVisualization

$ cd SmartVisualization

$ yo angular:app SmartVisualization

[?] Would you like to include Twitter Bootstrap? Yes

[?] Would you like to use the SCSS version of Twitter Bootstrap with the Compass CSS Authoring Framework? No

[?] Which modules would you like to include? (Press <space> to select)

❯⬢ angular-resource.js

 ⬢ angular-cookies.js

 ⬢ angular-sanitize.js


... 모듈들 자동 설치 중략 ...


// npm : package.json

// bower : .bowerrc, bower.json

// grunt : Gruntfile.js

// test 도구인 karma: karma*

// git 관련 : .git*

$ ls -alrt

-rw-r--r--   1 nulpulum  staff    11  2 21  2013 .gitattributes

-rw-r--r--   1 nulpulum  staff   415  4 16 00:02 .editorconfig

-rw-r--r--   1 nulpulum  staff   394  6 10 21:31 .jshintrc

-rw-r--r--   1 nulpulum  staff    56  6 10 21:31 .gitignore

-rw-r--r--   1 nulpulum  staff    44  6 10 21:31 .bowerrc

-rw-r--r--   1 nulpulum  staff   120  7 15 06:32 .travis.yml

-rw-r--r--   1 nulpulum  staff  1304  8 12 05:33 karma.conf.js

-rw-r--r--   1 nulpulum  staff  1348  8 12 05:33 karma-e2e.conf.js

drwxr-xr-x   5 nulpulum  staff   170  9 25 08:51 ..

drwxr-xr-x   5 nulpulum  staff   170  9 25 09:04 test

-rw-r--r--   1 nulpulum  staff   404  9 25 09:04 bower.json

-rw-r--r--   1 nulpulum  staff  8217  9 25 09:04 Gruntfile.js

drwxr-xr-x  12 nulpulum  staff   408  9 25 09:04 app

drwxr-xr-x  16 nulpulum  staff   544  9 25 09:04 .

drwxr-xr-x  39 nulpulum  staff  1326  9 25 09:04 node_modules

-rw-r--r--   1 nulpulum  staff  1466  9 25 09:05 package.json


  - Yeoman을 통하여 AngularJS 프로젝트 스케폴딩을 만들었다면 Grunt명령을 통하여 테스팅을 위한 서버를 기동할 수 있다 

// 명령을 수행하면 Node.js를 기반으로 서버가 기동하고 9000 port로 Chrome브라우져가 자동 실행되어 호출된다

// Grunt는 Java의 Ant와 같은 도구이다 

$ grunt server

Running "server" task


Running "clean:server" (clean) task

Cleaning .tmp...OK


Running "concurrent:server" (concurrent) task


    Running "copy:styles" (copy) task

    Copied 2 files

    

    Done, without errors.


    Elapsed time

    copy:styles  15ms

    Total        16ms


    Running "coffee:dist" (coffee) task


    Done, without errors.


    Elapsed time

    coffee:dist  10ms

    Total        10ms


Running "autoprefixer:dist" (autoprefixer) task

File ".tmp/styles/bootstrap.css" created.

File ".tmp/styles/main.css" created.


Running "connect:livereload" (connect) task

Started connect web server on localhost:9000.


Running "open:server" (open) task


Running "watch" task

Waiting...


// 브라우져 자동 실행 및 9000 포트 자동 호출된 결과물 


다음은 AngularJS 에 대하여 알아보자



<참조>

  - Client MV* Framework에대한 ToDoMVC 개발 복잡성 비교

  - Yeoman의 generator-angular와 express 연동하기

  - Vagrant를 통하여 개발환경 꾸미기

posted by 윤영식
2013. 9. 4. 14:05 AngularJS/Start MEAN Stack

Mobile 서비스 개발을 위하여 JavaScript 기반의 기술 스택을 선택하여 사용하다 보니 1년사이에 MongoDB, Express.js, Angular.js, Node.js를 공부하고 솔루션에 적용하게 되었다. 그간 해당 기술들을 추상화한 프레임워크들을 사용하여 개발을 진행해 왔다. 그러나 가끔 풀리지 않는 문제에 대한 근원적인 해결책을 찾는데는 다시 처음부터 추상화 내역에 대한 이해가 바탕이 되지 않으면 풀리지 않았다. 


즉, 기본 스택을 추상화한 스택사용시에는 추상화 영역에 대한 사용경험과 전반적인 이해가 없이는 오히려 시간공수만 더 늘어나는 격이 된다. 예로 Express와 MongoDB를 좀 더 수월하게 사용키위하여 Trains, Sails Framework들을 사용해 보았고 나름의 장점은 있으나 왠지 남의 옷을 입은듯한 느낌이랄까? 문제 발생시 빠른 대응이 어려웠다 (개인적으론 Trains Framework의 Node.js단의 IoC 방식을 좋아한다)


따라서 소규모 팀으로 개발하면서 하나의 가치를 빠른 시간안에 전달하고자 한다면 기본 Stack기반으로 진행하면서 필요한 시점에 스스로 추상화 모듈을 만들어 사용하는 방법이 좋지 않을까 생각한다. (Don't Invent Wheel 이니 초기엔 쓸만한 것을 GitHub에서 찾아 응용하고 없으면 개발하자) 그런 의미에서 mean.io 에서 기본 Stack에 충실하면서 현재 사용되는 최신 개발 기술들 - Jade, Bootstrap, Mongoose, Bower, Grunt 같은 - 도 함께 접목되어 적당히 어려운 상태이므로 개발시 집중(Flow)을 가능케 하는 구조를 가지고 있다. (너무 어려우면 흥미를 읽고, 너무 쉬우면 긴장감이 떨어진다. 적당히 어려우면 흥미와 긴장감을 주어 개발시 집중을 가능케 한다) 



1. 설치

  - 홈페이지에서 zip 파일 다운로드 

  - 프로젝트로 이동해서 

$ cd mean


// 사전에 node.js 설치 

// Node, npm 설치 shell

$ npm install .

 

// 사전에 mongodb 설치하고 start

$ grunt



2. 접속 

  - http://localhost:3000/

    + Sign Up 할 수 있다

    + 테스트 글을 CRUD 할 수 있다 

    


  - MongoDB 

> show collections

articles

sessions

system.indexes

users


해당 스택을 통해 SPA(Single Page Application) 개발에 대한 Lean(신속한) 스타트업을 시도하자.

 


<참조>

  - mean.io

posted by 윤영식
2013. 5. 7. 09:37 NodeJS/Concept

서비스 또는 솔루션을 만들기 위해 Node.js와 Express.js를 선택하였다면  그 다음 고민은 애플리케이션 개발을 위하여 필요한 스택을 선정하는 일이다. 크게는 로깅, 환경설정, 통신등 기본적인 부분들을 직접 개발하지 말고 이미 만들어진 바퀴를 공짜로 구해서 달아보자 



1. 준비하기 

  - Programming Javascript Application Book 책의 내용을 발췌한 것이다 

  - Node 버전관리 NVM 설치 

    + https://github.com/creationix/nvm

    + 설치 : nvm install [버전]

    + 수행 : nvm run [버전]

    + 확인 : nvm ls  (설치된 버전 목록을 보여줌)

    + 가능 : nvm ls-remote (설치 가능 버전 목록을 보여줌)

  - nvm 설치 명령 : git 사전 설치요구 : 수행시 nvm.sh 에 syntax오류가 나오면 nvm.sh을 copy하고 기존것 삭제후 다시 paste하여 만듦

$ curl https://raw.github.com/creationix/nvm/master/install.sh | sh


    + 기타 명령들 

$ nvm help


Node Version Manager


Usage:

    nvm help                          Show this message

    nvm install [-s] <version>  Download and install a <version>

    nvm uninstall <version>     Uninstall a version

    nvm use <version>           Modify PATH to use <version>

    nvm run <version> [<args>]  Run <version> with <args> as arguments

    nvm ls                            List installed versions

    nvm ls <version>             List versions matching a given description

    nvm ls-remote                 List remote versions available for install

    nvm deactivate                Undo effects of NVM on current shell

    nvm alias [<pattern>]      Show all aliases beginning with <pattern>

    nvm alias <name> <version>    Set an alias named <name> pointing to <version>

    nvm unalias <name>                Deletes the alias named <name>

    nvm copy-packages <version> Install global NPM packages contained in <version> to current version


Example:

    nvm install v0.4.12            Install a specific version number

    nvm use 0.2                    Use the latest available 0.2.x release

    nvm run 0.4.12 myApp.js   Run myApp.js using node v0.4.12

    nvm alias default 0.4        Auto use the latest installed v0.4.x version


  - 프로젝트 만들고 초기화 하기 

    + 디렉토리 만들고 해당 디렉토리로 이동

    + npm init 수행하여 package.json 파일 만들기 : 이름, 버전, 설명, 키워드, 저장소위치, 라이센스 및 기타 정보등을 입력한다 

$ mkdir basic && cd basic

$ npm init

This utility will walk you through creating a package.json file.

It only covers the most common items, and tries to guess sane defaults.


See `npm help json` for definitive documentation on these fields

and exactly what they do.


Use `npm install <pkg> --save` afterwards to install a package and

save it as a dependency in the package.json file.


Press ^C at any time to quit.

name: (basic) JavaScript-Programming

version: (0.0.0) 0.0.1

description: This is a javascript programming

entry point: (index.js)

test command: grunt test

git repository: https:/github.com/ysyun/javascriptacular

keywords: angularjs

author: yun dowon

license: (BSD) MIT

About to write to /Users/prototyping/express/basic/package.json:


{

  "name": "JavaScript-Programming",

  "version": "0.0.1",

  "description": "This is a javascript programming ",

  "main": "index.js",

  "scripts": {

    "test": "grunt test"

  },

  "repository": {

    "type": "git",

    "url": "https:/github.com/ysyun/javascriptacular"

  },

  "keywords": [

    "angularjs"

  ],

  "author": "yun dowon",

  "license": "MIT"

}



Is this ok? (yes)

npm WARN package.json JavaScript-Programming@0.0.1 No README.md file found!

$ ls -lart

-rw-r--r--  1 nulpulum  staff  360  5  7 09:22 package.json

drwxr-xr-x  3 nulpulum  staff  102  5  7 09:22 .


  - 모듈 설치시에 package.json 파일에 자동 기록을 위하여 npm install --save [module] 처럼 --save 옵션을 준다 



2. 준비할 스택들

  • Mout Like Underscore / LoDash. Stuff that should probably be included in JavaScript.

  • Express Web application framework.

  • Nconf Application config.

  • Hogan Mustache for express. (Jade 또는 EJS 를 써도 됨)

  • Superagent Communicate with APIs.

  • Socket.io Realtime communications (websockets).

  • Q Promises.

  • Async Asynchronous functional utilities.

  • Bunyan Logging. (Winston도 많이 사용)

  • Tape Testing. (Mocha, Karma 도 사용)

  • Cuid Better than guid/uuid for web applications.

  • Derby.js Add realtime, collaborative MVC to express. (Sails.js 또는 Bone.js 도 사용)

  • Node-http-proxy Proxy your service APIs.



3. Express 사용하기 

  - Routing 을 이용한다

  - Middleware를 통하여 중간에 Hooking 하여 처리하고 다음으로 next() 호출 넘기는 use 를 사용한다 

// Add some data to the request object that your other
// midleware and routes can use.
app.use(function (req, res, next) {
  req.foo = 'bar';
  next();

});

    + 전체 예제 

'use strict';
var express = require('express'),
 
  // Create app instance.
  app = express(),
 
  // Use the `PORT` environment variable, or port 44444
  port = process.env.PORT || 44444;
 
// The new middleware adds the property `foo` to the request
// object and sets it to 'bar'.
app.use(function (req, res, next) {
  req.foo = 'bar';
  next();
});
 
app.get('/', function (req, res) {
  res.setHeader('Content-Type', 'text/plain');
 
  // Send the value passed from the middleware, above.
  res.end(req.foo);
});
 
app.listen(port, function () {
  console.log('Listening on port ' + port);

});


// 결과 : use를 설정한 순서대로 처리된다 

$ curl http://localhost:44444/
bar



4. SailsJS

  - 난 sails를 Full Stack Server Side Data-Oriented API Framework 이라고 말하고 싶다.

  - Front-end 단이 SPA로 개발될 경우

  - Back-end를 API 서버로 확장함 

  - Node.js + Express.js + MongoDB 와 연결 및 JSON Memory DB 사용가능

    + sails-angularjs-yeoman 의 로그인 데모

    + sails-mongodb 연결

    + sails-redis 연결



<참조>

   - 원문 : Getting start with Node and Express 

   - middleware의 next() 사용법 : Node.js의 connect 이해하기

   - AngularJS와 MongoDB 연결 Bridge


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. 1. 30. 16:03 NodeJS/Prototyping

한개의 Socket.io 인스턴스를 공유하여 3개의 독립적인 채팅 서버를 운영하는 방법과 Express.js 사용시 virtual host를 설정하여 sub domain을 만드는 방법을 알아본다.



1. 가정

  - mydomain.com / sub1.mydomain.com / sub2.mydomain.com 의 서로 독립적인 채팅서버 운영

  - socket.io 인스턴스는 하나만 사용하여 공유함

  - 테스트 사이트 구경하기



2. GitHub에서 소스 다운로드

  - git 기본 설치 되었음을 가정한다 (참조)

  - 각 분리된 namespace에서 Chat 애플리케이션 3개의 인스턴스를 수행할 것이다

  - 소스 설치하기 

[mongodb@localhost ~]$ git clone git://github.com/braitsch/sub.socket.io.git socket-io-example

Initialized empty Git repository in /home/mongodb/socket-io-example/.git/

... 중략 ..

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

[mongodb@localhost ~]$ cd socket-io-example/

[mongodb@localhost socket-io-example]$ pwd

/home/mongodb/socket-io-example


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

// 각 분리된 namespace 존재 

[mongodb@localhost socket-io-example]$ ls 

README.md  

mydomain.com 

sub1.mydomain.com  

sub2.mydomain.com



3. mydomain.com의 app.js 와 mydomain.com/subdomain/sub1(2).js 소스이해

  - Express 사용하기 

// app.js

var exp = require('express');

var app = exp.createServer();


  - socket.io Listen 설정 : 전역(gobal)으로 Socket.IO 인스턴스에 접근하도록 선언한다 

// app.js

// create the single instance of socket.io that will be shared across all applications //

// 한개의 socket.io인스턴스를 생성하여 모든 애플리케이션간 공유한다

global.socket = require('socket.io').listen(app);

global.socket.set('log level', 1);

global.socket.set('transports', [ 'websocket', 'flashsocket', 'htmlfile', 'xhr-polling', 'jsonp-polling']);


  - subdomain 만들기

    + virtual host로써 동작하는 subdomain 애플리케이션을 설정한다

// create subdomain applications //

app.use(exp.vhost('sub1.' + global.host, require('./subdomains/sub1')));

app.use(exp.vhost('sub2.' + global.host, require('./subdomains/sub2')));


  - 모듈 로딩하면서 수행

// finally create this application, our root server //

// chat-socket.js 파일 모듈 로딩

require('./app/config')(app, exp);

require('./app/server/router')(app);

require('./app/server/modules/chat-socket');


  - sub1(2).js 소스 : 채팅 커넥션과 메세지를 다루기위해 chat-socket을 포함한 Express 애플리케이션이다

var exp = require('express');

var app = exp.createServer();


app.root = global.root + '/sub1.mydomain.com';

require(app.root + '/app/config')(app, exp);

require(app.root + '/app/server/router')(app);

require(app.root + '/app/server/modules/chat-socket');

module.exports = app;



4. chat-socket.js 소스이해

  - socket.io에 네임스페이스를 '/chat'으로 정의하기 

// pwd : ./socket-io-example/mydomain.com/app/server/modules

module.exports = function()

{

// 랜덤하게 사용자 색깔 지정 

var colors = ['#AE331F', '#D68434', '#116A9F', '#360B95', '#5F209E'];

        // 사용자 커넥션 추적을 유지하기 위한 객체 생성 

var connections = { };

// if you use socket.of(), you can set namespace. 

        // chat 네임스페이스 정의하고 connect  되면 수행할 펑션들 정의 

global.socket.of('/chat').on('connection', function(socket) {

       // give each connected user a random color so it's easier to tell them apart in the chat log //

               // 채팅사용자가 준비상태 

socket.on('user-ready', function(data) {

socket.name = data.name;

socket.color = data.color = colors[Math.floor(Math.random() * colors.length)];

brodcastMessage('user-ready', data);

});


               // 메세지 전송하기 

socket.on('user-message', function(data) {

data.color = socket.color;

brodcastMessage('user-message', data);

});


                // 접속자 상태 전송 

function dispatchStatus()

{

brodcastMessage('status', connections);

}

                // 메세지 전송하기 

function brodcastMessage(message, data)

{

               // remove socket.emit if you don't want the sender to receive their own message //

socket.emit(message, data);

socket.broadcast.emit(message, data);

}

                // soket.io 커넥션 끊길 때 호출 

        // handle connections & disconnections //

connections[socket.id] = {}; dispatchStatus();

socket.on('disconnect', function() {

delete connections[socket.id]; dispatchStatus();

brodcastMessage('user-disconnected', { name : socket.name, color : socket.color });

});

});

}();


  - sub1.mydomain.com 에 대하여 네임스페이스를 틀리게 기술한다 

global.socket.of('/chat-sub1').on('connection', function(socket) ...


  - sub2.mydomain.com 에 대하여 네임스페이스를 틀리게 기술한다 

global.socket.of('/chat-sub2').on('connection', function(socket) ...



5. index.js와 index.jade 소스이해

  - index.jade 하단에 index.js 와 socket.io.js 포함 

div.navbar.navbar-fixed-top

div.navbar-inner

div.container-fluid

ul.nav.pull-left

.brand Node.js & Socket.IO Chat Server

ul.nav.pull-right

li

a(href='#')#connected


#container

#send-message.well.span4

h2 Say Something

hr

label Your Name 

input(type="text", name="name", id='name').span4

label Your Message

textarea(rows='6', name="msg", id='msg').span4

button#btn-send.btn.btn-primary

i.icon-pencil.icon-white

| Send it !

#messages.well.span4

h2 Conversation

hr

#incoming


script(src='/vendor/jquery.min.js')

script(src='/socket.io/socket.io.js')

script(src='/js/index.js')


  - index.js 파일

$(document).ready(function() {


$('#msg').focus();

        // give user a generic name to start //

        // 사용자가 접속하면 랜덤 명칭을 설정한다 

$('#name').val(Math.random().toFixed(8).toString().substr(2));

$('#btn-send').click(function(){ sendMessage(); })

$('#msg').keypress(function(e){ if (e.keyCode === 13) { sendMessage(); return false; } })


       // initialize the socket connection to listen on the 'chat' namespace //

socket = io.connect('/chat');

socket.on('status', function (connections) {

var i=0; for (p in connections) i++;

var s = i > 1 ? ' are '+i+' People ' : ' is '+i+' Person ';

$('#connected').html('There '+s+' Currently Connected');

});

socket.on('user-ready', function (data) {

$('#incoming').append('<span class="shadow" style="color:'+data.color+'">'+data.name +' :: connected</span><br>');

autoScroll();

});

socket.on('user-message', function (data) {

$('#incoming').append('<span class="shadow" style="color:'+data.color+'">'+data.name +' :: '+ data.message+'</span><br>');

autoScroll();

});

socket.on('user-disconnected', function (data) {

$('#incoming').append('<span class="shadow" style="color:'+data.color+'">'+data.name +' :: disconnected</span><br>');

autoScroll();

});


        // register the user's name with the socket connection on the server // 

socket.emit('user-ready', {name : $('#name').val() });

var autoScroll = function() { 

document.getElementById('incoming').scrollTop = document.getElementById('incoming').scrollHeight; 

}


        // 메세지 보내기 

var sendMessage = function() {

socket.emit('user-message', {name : $('#name').val() , message : $('#msg').val() });

$('#msg').val('')

}

});



7. 채팅 서비스 설정하기 

  - 채팅 서비스에 대한 수행전에 필요한 모듈에 대해서 install 한다 

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

// mydomain.com 에 stylus, jade, express, socket.io 설치

// package.json 참조하여 설치함

$ cd mydomain.com 

$ npm install -d 

... 중략 ...

stylus@0.32.0 node_modules/stylus

jade@0.28.1 node_modules/jade

express@2.5.8 node_modules/express

socket.io@0.9.13 node_modules/socket.io

npm info ok 


// mydomain.com 안에 모듈 설치 디렉토리 생성

[mongodb@localhost mydomain.com]$ ll
total 20
drwxrwxr-x. 4 mongodb mongodb 4096 Feb  1 23:17 app
-rw-rw-r--. 1 mongodb mongodb 1109 Feb  1 23:17 app.js
drwxrwxr-x. 7 mongodb mongodb 4096 Feb  2 19:49 node_modules
-rw-rw-r--. 1 mongodb mongodb  590 Feb  1 23:17 package.json
drwxrwxr-x. 2 mongodb mongodb 4096 Feb  1 23:17 subdomains

//////////////////////////////////////////////////////
// sub1/sub2 도메인에 jade, stylus, express 설치 
// (socket.io는 mydomain에만 설치됨)
$ cd ../sub1.mydomain.com
$ npm install -d 
... 중략 ...
npm info prepublish sub.socket.io@1.0.0
jade@0.28.1 node_modules/jade
stylus@0.32.0 node_modules/stylus
express@2.5.8 node_modules/express


  -  /etc/hosts 파일에 다음을 설정한다 : 브라우져에서 호출하기 위한 sub1, sub2 도메인을 설정한다 

127.0.0.1 sub1.localhost

127.0.0.1 sub2.localhost



8. Chat Node Server 실행하기

  - mydomain.com 디렉토리로 들어가서 Node를 실행한다

/home/mongodb/socket-io-example/mydomain.com

[mongodb@localhost mydomain.com]$ node app

   info  - socket.io started

Express server listening on port 8080 in development mode 


  - 브라우져 2개를 띄워서 http://localhost:8080 을 동일하게 실행한다 

    + dowon 채팅창과 다른 브라우져창에서 try 명칭의 사용자가 들어왔다

    + socket.io를 통하여 채팅을 주고 받고 있다


  - 브라우져 2개를 띄워서 http://sub1.localhost:8080 을 동일하게 실행한다 (http://sub2.localhost:8080 동일 수행함)

    + youngsik 채팅창과 다른 브라우져창에서 girl 명칭의 사용자가 들어왔다

    + mydomain.com의 동일한 socket.io를 통하여 채팅을 주고 받고 있다 (namespace만 틀릴 뿐이다)



<참조>

  - 원문 : Building a Node.js Chat Application and Sharing Socket.IO Across Multiple Subdomains

  - 소스 : https://github.com/braitsch/sub.socket.io

  - Express.js Virtual Host 설정

  - 원문과 동일한 socket.io 인스턴스 한개와 Virtual Host 를 통한 SubDomain 공유에 대한 동일 예제


posted by 윤영식
prev 1 next