Ionic CLI 와 Meteor CLI 로 프로젝트 구성하기는 모바일 프로젝트를 진행할 때 사용하면 되고, 이번에는 Angular CLI 와 Meteor CLI를 통해 프로젝트 구성을 어떻게 하는지 살펴본다.
Webpack 기반 프로젝트 초기화
Angular CLI를 통해 프로젝트를 생성한다. @angular/cli v1.5.0이 설치되고, webpack은 v3.8.1 이고, 내부적으로 @angular-devkit, @ngtools/webpack, @schematics등이 사용된다.
$ npm install -g @angular/cli
@angular/CLI 설치후 프로젝트를 생성한다. ng <command>의 상세 내용은 위키를 참조한다.
Webpack 환경파일을 수정해야 하므로, eject 명령을 수행하고, 결과로 출력된 가이드에 따라 "npm install" 명령을 수행한다. eject 명령에 대한 다양한 options은 위키를 참조한다. eject시에 옵션을 주면 옵션이 적용된 webpack.config.js가 생성된다.
$ ng eject --aot --watch
====================================================
Ejection was successful.
To run your builds, you now need to do the following commands:
- "npm run build" to build.
- "npm test" to run unit tests.
- "npm start" to serve the app using webpack-dev-server.
- "npm run e2e" to run protractor.
Running the equivalent CLI commands will result in an error.
====================================================
Some packages were added. Please run "npm install".
$ npm install
$ npm run build
eject를 수행한 경우에는 "ng serve" 명령으로 테스트 서버를 뛰울 수 없다. package.json에 적용된 스크립트인 "npm start"를 수행하고 4200 port로 브라우져에서
$ npm start
10% building modules 3/3 modules 0 activeProject is running at http://localhost:4200/
webpack output is served from /
....
Webpack 환경 내역은 크게 entry, output, module (for loader), plugins 로 구성된다. (참조)
- entry: 파일 위치
- output: 결과 위치
- module: css, .ts, .html 파일 관리 및 변환기 -> 자바스크립트 모듈로 만들기 위한 것. postfix가 "-loader" 이다. 로더는 파일단위 처리
- plugins: 압축, 핫로딩, 복사, 옮기기등. 플러그인은 번들된 결과물을 처리
Angular CLI 환경에 Meteor 설정
이전 포스트처럼 루트에 api 폴더를 만들고 이를 Meteor의 백앤드로 사용토록 설정한다.
// webpack.config.js
const webpack = require('webpack');
...
resolve: {
alias: {
'api': path.resovle(__dirname, 'api/server'),
...
}
}
externals: [ resolveExternals ],
plugins: [ ..., new webpack.ProvidePlugin({ __extends: 'typescript-extends' }) ],
node: { ..., __dirname: true }
// 맨 마지막에 넣음
function resolveExternals(context, request, callback) {
return resolveMeteor(request, callback) ||
callback();
}
function resolveMeteor(request, callback) {
var match = request.match(/^meteor\/(.+)$/);
var pack = match && match[1];
if (pack) {
callback(null, 'Package["' + pack + '"]');
return true;
}
}
루트에 있는 tsconfig.json에 Meteor 백앤드 관련 내용을 추가한다.
"compilerOptions: {
"baseUrl": ".",
"module": "commonjs",
...
"skipLibCheck": true,
"stripInternal": true,
"noImplicitAny": false,
"types": [ "@types/meteor" ]
},
"include": [ ..., "api/**/*.ts" ],
"exclude": [ ..., "api/node_modules", "api" ]
src/tsconfig.app.json과 tsconfig.spec.json안에 api에 대한 exclude도 설정해야 한다.
// src/tsconfig.app.json
"exclude": [
...,
"../api/node_modules"
]
// src/tsconfig.spec.json
"exclude": [ "../api/node_modules" ]
관련 패키지를 설치한다.
$ npm install --save-dev typescript-extends
$ npm install --save-dev @types/meteor
$ npm install --save-dev tmp
Meteor Server API 생성 및 설정
Meteor CLI를 설치하고 api 명으로 Meteor 프로젝트를 생성한다.
$ curl https://install.meteor.com/ | sh
$ meteor create api
api 폴더 밑의 필요없는 폴더를 삭제하고 루트에 있는 것으로 대체한다.
$ cd api
// 삭제
api$ rm -rf node_modules client package.json package-lock.json
// 심볼릭 링크
api$ ln -s ../package.json
api$ ln -s ../package-lock.json
api$ ln -s ../node_modules
api$ ln -s ../src/declarations.d.ts
Meteor 백앤드를 Typescript 기반으로 개발하기 위한 패키지를 설치한다.
api$ meteor add barbatus:typescript
api$ cd ..
$ npm install --save babel-runtime
$ npm install --save meteor-node-stubs
$ npm install --save meteor-rxjs
Typescript의 tsconfig.json 파일을 api 폴더안에 생성하고 다음 내역을 붙여넣는다.
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"declaration": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [
"dom",
"es2017"
],
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"target": "es6",
"skipLibCheck": true,
"stripInternal": true,
"noImplicitAny": false,
"types": [
"@types/meteor"
]
},
"exclude": [
"node_modules"
],
"compileOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}
Meteor의 server/main.js를 main.ts로 바꾼다. 위에서 설치한 meteor-rxjs는 클라이언트단의 Meteor를 RxJS Observable기반으로 사용할 수 있도록 한다.
// 예) Meteor 클라이언트단 Collection 구성
import { MongoObservable } from 'meteor-rxjs';
export const Chats = new MongoObservable.Collection('chats');
Meteor Client 준비
Meteor Server <-> Client 연동을 위해 Client를 Bundling한다.
$ sudo npm install -g meteor-client-bundler
번들링시에 Meteor Server 기본 주소는 localhost:3000 으로 설정된다. Meteor Client는 DDP를 이용하기 때문에 번들할 때 --config 옵션 또는 --url 옵션으로 Meteor Server 위치를 지정한다. -s 옵션을 주면 Client<->Server 같은 버전의 패키지를 사용하고 -s 옵션을 주지않으면 config 설정내용을 참조한다. (참조)
$ meteor-client bundle --destination meteor.bundle.js --config bundler.config.json
// config file
{
"release": "1.6",
"runtime": {
"DDP_DEFAULT_CONNECTION_URL": "http://1.0.0.127:8100"
},
"import": [
"accounts-base",
"mys:accounts-phone",
"jalik:ufs@0.7.1_1",
"jalik:ufs-gridfs@0.1.4"
]
}
package.json에 번들링 명령을 등록하고 수행한다.
"scripts": {
...,
"meteor-client:bundle": "meteor-client bundle -s api"
}
//번들링 - 최초 한번만 수행한다.
$ npm run meteor-client:bundle
Angular 클라이언트에서 Meteor Client를 사용하기 위해 src/main.ts에서 "meteor-client"를 import한다.
Collection 생성하고 Meteor 기능 사용 테스트
api/server 에 model을 하나 만든다. Angular와 Meteor가 같이 사용하는 모델 타입이다.
// api/server/models.ts
export enum MessageType {
TEXT = <any>'text'
}
export interface Chat {
_id?: string;
title?: string;
picture?: string;
lastMessage?: Message;
memberIds?: string[];
}
export interface Message {
_id?: string;
chatId?: string;
senderId?: string;
content?: string;
createdAt?: Date;
type?: MessageType;
ownership?: string;
}
api/server/collections 폴더를 생성하고 Chat 컬렉션을 생성한다. meteor-rxjs 는 RxJS로 Mongo Client를 wrapping해 놓은 것으로 Rx방식으로 Mongo Client를 사용할 수 있게 한다.
// api/server/collections/chats.ts
import { MongoObservable } from 'meteor-rxjs';
import { Chat } from '../models';
export const Chats = new MongoObservable.Collection<Chat>('chats');
// api/server/collections/messages.ts
import { MongoObservable } from 'meteor-rxjs';
import { Message } from '../models';
export const Messages = new MongoObservable.Collection<Message>('messages');
api/server/main.ts안에 샘플 데이터를 넣는다.
$ npm install --save moment
// api/server/main.ts
import { Meteor } from 'meteor/meteor';
import { Chats } from './collections/chats';
import { Messages } from './collections/messages';
import * as moment from 'moment';
import { MessageType } from './models';
Meteor.startup(() => {
// code to run on server at startup
if (Chats.find({}).cursor.count() === 0) {
let chatId;
chatId = Chats.collection.insert({
title: 'Ethan Gonzalez',
picture: 'https://randomuser.me/api/portraits/thumb/men/1.jpg'
});
Messages.collection.insert({
chatId: chatId,
content: 'You on your way?',
createdAt: moment().subtract(1, 'hours').toDate(),
type: MessageType.TEXT
});
chatId = Chats.collection.insert({
title: 'Bryan Wallace',
picture: 'https://randomuser.me/api/portraits/thumb/lego/1.jpg'
});
Messages.collection.insert({
chatId: chatId,
content: 'Hey, it\'s me',
createdAt: moment().subtract(2, 'hours').toDate(),
type: MessageType.TEXT
});
}
});
다음으로 Angular에 Chat 컬렉션을 사용한다.
// src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { Chats } from '../../api/server/collections/chats';
import { Chat } from '../../api/server/models';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'app';
chats: Chat[];
ngOnInit() {
Chats.find({}).subscribe((chats: Chat[]) => this.chats = chats );
}
}
// src/app/app.component.html 에 추가
<div> {{ chats | json }} </div>
Angular & Meteor 기동
Meteor 기동
Angular 기동
Webpack dev server는 4200이고, meteor client는 server에 websocket 3000 port로 접속을 한다. Meteor 의 mongo로 접속해서 chats collection을 확인해 본다.
$ meteor mongo
MongoDB shell version: 3.2.15
connecting to: 127.0.0.1:3001/meteor
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
http://docs.mongodb.org/
Questions? Try the support group
http://groups.google.com/group/mongodb-user
meteor:PRIMARY> show collections
chats
messages
하단에 Chat 내역이 json 형식으로 출력된다.
<참조>
- Webpack v3 환경설정 요약
- Meteor Client bundler의 작도방식 by Uri
- Meteor Client Bundler Github