SlideShare ist ein Scribd-Unternehmen logo
1 von 32
Downloaden Sie, um offline zu lesen
+
한장현
han41858@gmail.com
han41858.tistory.com
1
• 전 삼성SDS 선임
• TV플랫폼 JavaScript 어플리케이션 구현
• 리테일 솔루션 서버 & 프론트엔드 구현
• 프리랜서 개발자
• han41858.tistory.com 블로그 운영
• Angular 2 번역서 집필중
• GDG Korea Web Tech 운영진
한장현 (Janghyun Han)
2
3
ngular
1.6.0-rc.2 safety-insurance
4
• 마술 같은 2-way binding
• HTML 표준을 기반으로 기능 확장
• 체계적인 컴포넌트, Web Component로 가는 길
• Front-end 전체를 커버하는 프레임워크
• Google 에서 관리, 개선
• 풍부한 사용자 삽질 경험
Angular를 쓰는 이유
5
Class, inheritance
import/export
Arrow function
Promise
ECMAScript 2015
6
• 간단해지는 코드, syntax sugar
• 클래스, 상속, 모듈 구성 ⇒ 아키텍처 진화
• 브라우저들 지원 계속
• 결국엔 표준, transpiler는 거쳐갈 뿐
ES6를 쓰는 이유
7
8
ES6ES5
script src
<script src="bundle.js"></script>
"devDependencies": {
"angular": "^1.5.8",
"babel-cli": "^6.18.0",
"babel-loader": "^6.2.7",
"babel-preset-es2015": "^6.18.0",
"webpack": "^1.13.3"
}
<script src="angular.js"></script>
<script src="index.js"></script>
"devDependencies": {
"angular": "^1.5.8"
}
9
index.js
ES6ES5
(function () {
var ngApp = angular.module('angular1es5', []);
})();
import angular from 'angular';
(() => {
const ngApp = angular.module('angular1es6', []);
})();
10
Webpack
// this is ES5
var webpack = require('webpack');
module.exports = {
entry : [
'./index.js'
],
output : {
filename : 'build/bundle.js',
sourceMapFilename: '[name].map'
},
module : {
loaders : [
{
test : /.js$/,
loader : 'babel?presets[]=es2015
exclude : /node_modules/
},
{
test : /.pug$/,
loader : 'pug-loader',
exclude : /node_modules/
}
]
},
plugins : [
// new webpack.optimize.UglifyJsPlugin({minimize: true})
]
};
webpack.config.js
"devDependencies": {
"angular": "^1.5.8",
"angular-route": "^1.5.8",
"babel-cli": "^6.18.0",
"babel-loader": "^6.2.7",
"babel-preset-es2015": "^6.18.0",
"file-loader": "^0.9.0",
"pug": "^2.0.0-beta6",
"pug-loader": "^2.3.0",
"webpack": "^1.13.3",
"webpack-dev-server": "^1.16.2"
}
package.json
11
export default class HomeCtrl {
constructor () {
console.log('HomeCtrl.constructor()');
}
}
/view/homeCtrl.js
p this is home
/view/home.pug
import angular from 'angular';
import ngRoute from 'angular-route';
import HomeCtrl from './view/homeCtrl';
const main = () => {
console.log('main()');
const ngApp = angular.module('angular1es6', ['ngRoute']);
ngApp.config(($routeProvider, $locationProvider) => {
console.log('this is angular config');
$routeProvider
.when('/', {
template : require('./view/home.pug'),
controller : 'HomeCtrl',
controllerAs : 'Ctrl'
})
.otherwise({
redirectTo : '/'
});
// need to angular.js routing
$locationProvider.html5Mode({
enabled : true,
requireBase : false
});
});
ngApp.controller('HomeCtrl', HomeCtrl);
};
main();
index.js + ngRoute
12
webpack-dev-server
13
ngApp.directive('CustomDirective', () => new CustomDirective);
ngApp.filter('groupBy', GroupBy);
ngApp.service('CustomSvc', CustomSvc);
ngApp.controller('CustomCtrl', CustomCtrl);
Angular Components
ngApp.directive('CustomDirective', CustomDirective);
ngApp.filter('groupBy', GroupBy);
ngApp.service('CustomSvc', CustomSvc);
ngApp.controller('CustomCtrl', CustomCtrl);
function
Class
new Class
14
Filter
ES6ES5
ngApp.filter('uppercase', uppercase);
function uppercase () {
return function (item) {
return item.toUpperCase();
};
}
<script src="uppercase.filter.js"></script>
const uppercase = () => {
return (input) => {
return input.toUpperCase();
};
};
export default uppercase;
import uppercase from './uppercase.filter';
ngApp.filter('uppercase', uppercase);
15
.ctrlRoot
p this is home ctrl
p {{ Ctrl.title }}
button(ng-click="Ctrl.select()") Select
export default class HomeCtrl {
constructor () {
console.log('HomeCtrl.constructor()');
this.title = 'this is title';
}
select () {
console.log('HomeCtrl.select()');
}
}
$routeProvider
.when('/', {
template : require('./view/home.pug'),
controller : 'HomeCtrl',
controllerAs : 'Ctrl'
})
ngApp.controller('HomeCtrl', HomeCtrl);
function HomeCtrl ($scope) {
console.log('home controller');
$scope.title = 'this is title';
$scope.select = function () {
console.log('HomeCtrl.select()');
}
}
Controller
ES6ES5
.ctrlRoot
p this is home ctrl
p {{ title }}
button(ng-click="select()") Select
16
Service
ES6ES5
ngApp.service('myService', myService);
<script src="./view/myService.js"></script>
function myService () {
this.testFnc = function () {
console.log('myService.testFnc()');
};
return this;
}
export default class MyService {
constructor () {
console.log('MyService');
}
testFnc () {
console.log('MyService.testFnc()');
}
}
ngApp.service('MyService', MyService);
export default class HomeCtrl {
constructor (MyService) {
this.MyService = MyService;
}
select () {
this.MyService.testFnc();
}
}
static 있어야 할 것 같지만 없어야 함
17
ngApp.directive('myDirective', () => new MyDirective);
export default class MyDirective {
constructor () {
console.log('MyDirective.constructor()');
this.restrict = 'E';
this.template = '<p>message : {{ this.msg }}</p>';
this.scope = {
msg : '@'
};
}
link (scope) {
console.log(scope.msg);
}
}
ngApp.directive('myDirective', myDirective);
function myDirective () {
return {
restrict : 'E',
template : '<p>message : {{ msg }}</p>',
scope : {
msg : '@'
},
controller : function ($scope) {
console.log('myDirective.controller()');
console.log($scope.msg);
}
}
}
<script src="myDirective.js"></script>
Directive
ES6ES5
directive 등록할 때 () => new18
Directive vs. Component
19
Directive vs. Component
const customInput = {
bindings : {
model : '='
},
template : '<input ng-model="$ctrl.model"></input>',
controller : function () {
this.$onInit = () => {
}
}
};
export default customInput;
ngApp.component('customInput', customInput);ngApp.directive('customInput', () => new customInput);
export default class customInput {
constructor () {
this.restrict = 'E';
this.scope = {
model : '='
};
this.template = '<input ng-model="model"></input>';
}
controller () {
}
}
20
static delete (param) {
const self = this;
return util.objValidate(param, {
userID : Constants.TYPE.EMAIL
}, Constants.ERROR.USER_CTRL.NO_PARAMETER, log, 'delete()')
.then(param => self.isExists(param))
.then(param => {
// delete records
return recordCtrl.deleteAll({
userID : param.userID
})
.then(() => {
log('remove records ok');
// param 자체를 다시 돌려주기 위해 Promise 필요
return Promise.resolve(param);
});
})
.then(param => {
// delete cards
return cardCtrl.deleteAll({
userID : param.userID
})
.then(() => {
log('remove cards ok');
return Promise.resolve(param);
});
})
.then(param => {
// delete assets
return assetCtrl.deleteAll({
userID : param.userID
})
.then(() => {
log('remove assets ok');
return Promise.resolve(param);
});
})
.then(param => {
// delete user
return User.remove({
userID : param.userID
})
.then(() => {
return Promise.resolve(param);
}, error => {
log(error);
return Promise.reject(new ERROR(Constants.ERROR.MONGOOSE.REMOVE_FAILED, log, 'delete()'));
});
})
.then(param => {
log(`delete ok : ${param.userID}`);
return Promise.resolve(true);
});
}
Promise
21
ngApp.config(($routeProvider, $locationProvider) => {
// include styles
require('./view/home.styl');
$routeProvider
.when('/', {
template : require('./view/home.pug'),
controller : 'HomeCtrl',
controllerAs : 'Ctrl'
})
.otherwise({
redirectTo : '/'
});
});
배포 : webpack
import가 아니므로 require 22
Angular 1 + ES6 + BDD
= Hell
23
describe('homeCtrl.test', () => {
it('module import', () => {
expect(true).to.be.true;
});
});
λ karma start
(node:7564) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`,
`Buffer.allocUnsafe()` or `Buffer.alloc()` instead.
18 11 2016 02:53:45.852:INFO [framework.browserify]: bundle built
18 11 2016 02:53:45.941:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/
18 11 2016 02:53:45.941:INFO [launcher]: Launching browser Chrome with unlimited concurrency
18 11 2016 02:53:45.952:INFO [launcher]: Starting browser Chrome
18 11 2016 02:53:47.283:INFO [Chrome 54.0.2840 (Windows 10 0.0.0)]: Connected on socket /#AjAqCwTlrwmVmV_sAAAA with id
16857313
Chrome 54.0.2840 (Windows 10 0.0.0): Executed 1 of 1 SUCCESS (0.005 secs / 0.001 secs)
BDD 시작
24
λ karma start
(node:12196) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead.
18 11 2016 02:19:10.237:INFO [framework.browserify]: bundle built
18 11 2016 02:19:10.343:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/
18 11 2016 02:19:10.343:INFO [launcher]: Launching browser Chrome with unlimited concurrency
18 11 2016 02:19:10.353:INFO [launcher]: Starting browser Chrome
18 11 2016 02:19:11.676:INFO [Chrome 54.0.2840 (Windows 10 0.0.0)]: Connected on socket /#sBpP4RL0XFZAwPtxAAAA with id 52822107
Chrome 54.0.2840 (Windows 10 0.0.0) ERROR
Uncaught SyntaxError: Unexpected token import
at test/homeCtrl.test.js:1
import HomeCtrl from '../view/homeCtrl';
describe('homeCtrl.test', () => {
it('module import', () => {
console.log(HomeCtrl);
expect(true).to.be.true;
expect(HomeCtrl).to.be.ok;
});
});
λ karma start
(node:11580) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead.
18 11 2016 02:31:24.248:INFO [framework.browserify]: bundle built
18 11 2016 02:31:24.339:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/
18 11 2016 02:31:24.339:INFO [launcher]: Launching browser Chrome with unlimited concurrency
18 11 2016 02:31:24.349:INFO [launcher]: Starting browser Chrome
18 11 2016 02:31:25.657:INFO [Chrome 54.0.2840 (Windows 10 0.0.0)]: Connected on socket /#pvpyGrXqq2TZPTgmAAAA with id 30236974
LOG: class HomeCtrl { ... }
Chrome 54.0.2840 (Windows 10 0.0.0): Executed 1 of 1 SUCCESS (0.007 secs / 0.002 secs)
babel : node6
λ karma start
(node:13196) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead.
18 11 2016 02:58:38.638:INFO [framework.browserify]: bundle built
18 11 2016 02:58:38.728:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/
18 11 2016 02:58:38.729:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency
18 11 2016 02:58:38.738:INFO [launcher]: Starting browser PhantomJS
18 11 2016 02:58:40.301:INFO [PhantomJS 2.1.1 (Windows 8 0.0.0)]: Connected on socket /#JeRwavdozVZCC8HJAAAA with id 75347319
LOG: function HomeCtrl(MyService) { ... }
PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 1 of 1 SUCCESS (0.008 secs / 0.001 secs)
babel : es2015
ES6가 돌지 않는다…
+
25
var $httpBackend;
beforeEach(function () {
module('angular1es5');
inject(function (_$httpBackend_) {
$httpBackend = _$httpBackend_;
});
});
module 선언, injection 불가
ES6ES5
const $injector, $httpBackend;
beforeEach(() => {
$injector = angular.injector(['angular1es6']);
$httpBackend = $injector.get('$httpBackend');
});
Object is not a constructor (evaluating 'module('angular1es6')')
r:/temp/test/homeCtrl.test.js:15:9 <-
R:/temp/3871fde1c6cf6c302eeae7add18a3b02.browserify:22:9
26
ngMock vs. ngMockE2E
The ngMock module provides support to inject
and mock Angular services into unit tests. In
addition, ngMock also extends various core ng
services such that they can be inspected and
controlled in a synchronous manner within test
code.
The ngMockE2E is an angular module which
contains mocks suitable for end-to-end testing.
Currently there is only one mock present in this
module - the e2e $httpBackend mock.
ngMock ngMockE2E
Fake HTTP backend implementation suitable for end-to-
end testing or backend-less development of applications
that use the $http service.
Fake HTTP backend implementation suitable for unit
testing applications that use the $http service.
.when()
.expect()
.flush()
.verifyNoOutstandingExpectation()
.verifyNoOutstandingRequest()
.resetExpectations()
.when()
ngMockE2E에는 $location 없음 27
Promise + http.flush()
Chrome 54.0.2840 (Windows 10 0.0.0) homeCtrl.test http test ok FAILED
Error: No pending request to flush !
at Function.$httpBackend.flush (node_modules/angular-mocks/angular-mocks.js:1799:34)
at r:/temp/test/homeCtrl.test.js:36:17
promise 함수로 부르면 동작 안함
flush() 타이밍 달라짐
flush는 Util에서 수행
const $injector = angular.injector(['angular1es6']);
const $httpBackend = $injector.get('$httpBackend');
const $http = $injector.get('$http');
beforeEach(() => {
$httpBackend.expectGET('/test')
.respond(['this', 'is', 'GET', 'test', 'data']);
});
it('ok', () => {
return new Promise(resolve => {
$http.get('/test').then(result => {
console.log('get().then()');
console.log(result);
console.log(result.data);
resolve(true);
});
$httpBackend.flush();
});
});
동작하는 코드
static post (uri, param, config) {
// use new promise for flush()
return new Promise((resolve, reject) => {
this.http.post(uri, param, config)
.then(result => {
resolve(result);
}, error => {
console.error(error);
reject(false);
});
if (this.httpBackend && this.httpBackend.flush) {
this.httpBackend.flush();
}
});
}
it('ok', () => {
return new Promise(resolve => {
Promise.resolve()
.then(() => {
$http.get('/test').then(result => {
console.log('get().then()');
console.log(result);
console.log(result.data);
resolve(true);
});
});
$httpBackend.flush();
});
});
28
여러 test 파일 동시 실행
const $injector = angular.injector(['angular1es6']);
const $httpBackend = $injector.get('$httpBackend');
const $http = $injector.get('$http');
Chrome 54.0.2840 (Windows 10 0.0.0) SignInCtrl test logic submit() with ajax error response response error test FAILED
AssertionError: expected [Error: Unexpected request: POST /api/user/signIn
No more request expected] to be an instance of ERROR
Chrome 54.0.2840 (Windows 10 0.0.0) SignUpCtrl test logic submit() error response response error test FAILED
AssertionError: expected [Error: [$rootScope:inprog] $digest already in progress
const ngApp = angular.module(appName, ['ngMock']); // working with $location, but not $httpBackend.whenPOST()...
var $injector = angular.injector(['MoneyBook']);
const $httpBackend = $injector.get('$httpBackend');
const $http = $injector.get('$http');
ClientUtil.http = $http;
ClientUtil.httpBackend = $httpBackend;
29
생성자 안에서 Promise
export default class HomeCtrl {
constructor () {
console.log('HomeCtrl.constructor()');
Promise.resolve(() => {
// do something
});
}
}
describe('constructor()', () => {
it('ok', () => {
// expect what...?
});
});
export default class HomeCtrl {
constructor () {
console.log('HomeCtrl.constructor()');
this.somethingPromise();
}
somethingPromise(){
return Promise.resolve()
.then() => {
// do something
});
}
}
30
Promise + $scope.$apply()
export default class HomeCtrl {
constructor () {
console.log('HomeCtrl.constructor()');
this.count = 0;
}
select () {
console.log('HomeCtrl.select()');
return Promise.resolve()
.then(() => {
this.count++;
});
}
}
export default class HomeCtrl {
constructor ($scope) {
console.log('HomeCtrl.constructor()');
this.$scope = $scope;
this.count = 0;
}
select () {
console.log('HomeCtrl.select()');
return Promise.resolve()
.then(() => {
this.count++;
this.$scope.$apply();
});
}
}
31
+ =
32

Weitere ähnliche Inhalte

Was ist angesagt?

Ansible loves Python, Python Philadelphia meetup
Ansible loves Python, Python Philadelphia meetupAnsible loves Python, Python Philadelphia meetup
Ansible loves Python, Python Philadelphia meetupGreg DeKoenigsberg
 
Nginx and friends - putting a turbo button on your site
Nginx and friends - putting a turbo button on your siteNginx and friends - putting a turbo button on your site
Nginx and friends - putting a turbo button on your siteWim Godden
 
Find bottleneck and tuning in Java Application
Find bottleneck and tuning in Java ApplicationFind bottleneck and tuning in Java Application
Find bottleneck and tuning in Java Applicationguest1f2740
 
Future Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETFuture Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETGianluca Carucci
 
Node.js in action
Node.js in actionNode.js in action
Node.js in actionSimon Su
 
Europython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryEuropython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryMauro Rocco
 
Flexviews materialized views for my sql
Flexviews materialized views for my sqlFlexviews materialized views for my sql
Flexviews materialized views for my sqlJustin Swanhart
 
Taking a Test Drive
Taking a Test DriveTaking a Test Drive
Taking a Test DriveGraham Lee
 
Asynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsAsynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsPiotr Pelczar
 
"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr VronskiyFwdays
 
Guide to Node.js: Basic to Advanced
Guide to Node.js: Basic to AdvancedGuide to Node.js: Basic to Advanced
Guide to Node.js: Basic to AdvancedEspeo Software
 
Why Redux-Observable?
Why Redux-Observable?Why Redux-Observable?
Why Redux-Observable?Anna Su
 
NetApp ontap simulator
NetApp ontap simulatorNetApp ontap simulator
NetApp ontap simulatorAshwin Pawar
 
Jeroen Vloothuis Bend Kss To Your Will
Jeroen Vloothuis   Bend Kss To Your WillJeroen Vloothuis   Bend Kss To Your Will
Jeroen Vloothuis Bend Kss To Your WillVincenzo Barone
 
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuningAOE
 
"The little big project. From zero to hero in two weeks with 3 front-end engi...
"The little big project. From zero to hero in two weeks with 3 front-end engi..."The little big project. From zero to hero in two weeks with 3 front-end engi...
"The little big project. From zero to hero in two weeks with 3 front-end engi...Fwdays
 
BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....
BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....
BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....La Cuisine du Web
 

Was ist angesagt? (20)

Ansible loves Python, Python Philadelphia meetup
Ansible loves Python, Python Philadelphia meetupAnsible loves Python, Python Philadelphia meetup
Ansible loves Python, Python Philadelphia meetup
 
Nginx and friends - putting a turbo button on your site
Nginx and friends - putting a turbo button on your siteNginx and friends - putting a turbo button on your site
Nginx and friends - putting a turbo button on your site
 
Find bottleneck and tuning in Java Application
Find bottleneck and tuning in Java ApplicationFind bottleneck and tuning in Java Application
Find bottleneck and tuning in Java Application
 
Future Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETFuture Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NET
 
Node.js in action
Node.js in actionNode.js in action
Node.js in action
 
Europython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryEuropython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & Celery
 
Flexviews materialized views for my sql
Flexviews materialized views for my sqlFlexviews materialized views for my sql
Flexviews materialized views for my sql
 
Taking a Test Drive
Taking a Test DriveTaking a Test Drive
Taking a Test Drive
 
Rxjs marble-testing
Rxjs marble-testingRxjs marble-testing
Rxjs marble-testing
 
Asynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsAsynchronous programming done right - Node.js
Asynchronous programming done right - Node.js
 
Spring 4 - A&BP CC
Spring 4 - A&BP CCSpring 4 - A&BP CC
Spring 4 - A&BP CC
 
"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy
 
Guide to Node.js: Basic to Advanced
Guide to Node.js: Basic to AdvancedGuide to Node.js: Basic to Advanced
Guide to Node.js: Basic to Advanced
 
Why Redux-Observable?
Why Redux-Observable?Why Redux-Observable?
Why Redux-Observable?
 
NetApp ontap simulator
NetApp ontap simulatorNetApp ontap simulator
NetApp ontap simulator
 
Jeroen Vloothuis Bend Kss To Your Will
Jeroen Vloothuis   Bend Kss To Your WillJeroen Vloothuis   Bend Kss To Your Will
Jeroen Vloothuis Bend Kss To Your Will
 
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuning
 
Celery
CeleryCelery
Celery
 
"The little big project. From zero to hero in two weeks with 3 front-end engi...
"The little big project. From zero to hero in two weeks with 3 front-end engi..."The little big project. From zero to hero in two weeks with 3 front-end engi...
"The little big project. From zero to hero in two weeks with 3 front-end engi...
 
BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....
BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....
BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....
 

Andere mochten auch

2016 W3C Conference #1 : 웹 개발의 현재와 미래
2016 W3C Conference #1 : 웹 개발의 현재와 미래2016 W3C Conference #1 : 웹 개발의 현재와 미래
2016 W3C Conference #1 : 웹 개발의 현재와 미래양재동 코드랩
 
2016 W3C Conference #7 : Electron, 웹 기술로 담아내는 데스크톱 애플리케이션
2016 W3C Conference #7 : Electron, 웹 기술로 담아내는 데스크톱 애플리케이션2016 W3C Conference #7 : Electron, 웹 기술로 담아내는 데스크톱 애플리케이션
2016 W3C Conference #7 : Electron, 웹 기술로 담아내는 데스크톱 애플리케이션양재동 코드랩
 
2016 W3C Conference #9 : 컨테이너와 웹 어플리케이션
2016 W3C Conference #9 : 컨테이너와 웹 어플리케이션2016 W3C Conference #9 : 컨테이너와 웹 어플리케이션
2016 W3C Conference #9 : 컨테이너와 웹 어플리케이션양재동 코드랩
 
2016 W3C Conference #2 : VANILA JS로 개발하기
2016 W3C Conference #2 : VANILA JS로 개발하기2016 W3C Conference #2 : VANILA JS로 개발하기
2016 W3C Conference #2 : VANILA JS로 개발하기양재동 코드랩
 
2016 W3C Conference #5 : UNIVERSAL RENDERING (React.JS 중심)
2016 W3C Conference #5 : UNIVERSAL RENDERING (React.JS 중심)2016 W3C Conference #5 : UNIVERSAL RENDERING (React.JS 중심)
2016 W3C Conference #5 : UNIVERSAL RENDERING (React.JS 중심)양재동 코드랩
 
2016 W3C Conference #8 : IONIC으로 하이브리드 앱 개발하기, 사례와 시사점
2016 W3C Conference #8 : IONIC으로 하이브리드 앱 개발하기, 사례와 시사점2016 W3C Conference #8 : IONIC으로 하이브리드 앱 개발하기, 사례와 시사점
2016 W3C Conference #8 : IONIC으로 하이브리드 앱 개발하기, 사례와 시사점양재동 코드랩
 
2016 ABCD 소개
2016 ABCD 소개2016 ABCD 소개
2016 ABCD 소개성일 한
 
2016 W3C Conference #6 : ReactiveX + Meteor 종단간 암호화 구현 사례
2016 W3C Conference #6 : ReactiveX + Meteor 종단간 암호화 구현 사례2016 W3C Conference #6 : ReactiveX + Meteor 종단간 암호화 구현 사례
2016 W3C Conference #6 : ReactiveX + Meteor 종단간 암호화 구현 사례양재동 코드랩
 
모바일 디버깅
모바일 디버깅모바일 디버깅
모바일 디버깅yongwoo Jeon
 
EcmaScript6(2015) Overview
EcmaScript6(2015) OverviewEcmaScript6(2015) Overview
EcmaScript6(2015) Overviewyongwoo Jeon
 
Laravel 로 배우는 서버사이드 #5
Laravel 로 배우는 서버사이드 #5Laravel 로 배우는 서버사이드 #5
Laravel 로 배우는 서버사이드 #5성일 한
 
Web Components 101 polymer & brick
Web Components 101 polymer & brickWeb Components 101 polymer & brick
Web Components 101 polymer & brickyongwoo Jeon
 
CF Korea Meetup - Spring Cloud Services
CF Korea Meetup - Spring Cloud ServicesCF Korea Meetup - Spring Cloud Services
CF Korea Meetup - Spring Cloud ServicesJay Lee
 
내가 환경을 노래하는 이유 | 정욱재 노리플라이
내가 환경을 노래하는 이유 | 정욱재 노리플라이내가 환경을 노래하는 이유 | 정욱재 노리플라이
내가 환경을 노래하는 이유 | 정욱재 노리플라이cbs15min
 
Data-binding AngularJS
Data-binding AngularJSData-binding AngularJS
Data-binding AngularJSEunYoung Kim
 
XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기
XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기
XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기XpressEngine
 
안정적인 서비스 운영 2013.08
안정적인 서비스 운영   2013.08안정적인 서비스 운영   2013.08
안정적인 서비스 운영 2013.08Changyol BAEK
 
FalsyValues. Dmitry Soshnikov - ECMAScript 6
FalsyValues. Dmitry Soshnikov - ECMAScript 6FalsyValues. Dmitry Soshnikov - ECMAScript 6
FalsyValues. Dmitry Soshnikov - ECMAScript 6Dmitry Soshnikov
 

Andere mochten auch (20)

2016 W3C Conference #1 : 웹 개발의 현재와 미래
2016 W3C Conference #1 : 웹 개발의 현재와 미래2016 W3C Conference #1 : 웹 개발의 현재와 미래
2016 W3C Conference #1 : 웹 개발의 현재와 미래
 
2016 W3C Conference #7 : Electron, 웹 기술로 담아내는 데스크톱 애플리케이션
2016 W3C Conference #7 : Electron, 웹 기술로 담아내는 데스크톱 애플리케이션2016 W3C Conference #7 : Electron, 웹 기술로 담아내는 데스크톱 애플리케이션
2016 W3C Conference #7 : Electron, 웹 기술로 담아내는 데스크톱 애플리케이션
 
2016 W3C Conference #9 : 컨테이너와 웹 어플리케이션
2016 W3C Conference #9 : 컨테이너와 웹 어플리케이션2016 W3C Conference #9 : 컨테이너와 웹 어플리케이션
2016 W3C Conference #9 : 컨테이너와 웹 어플리케이션
 
2016 W3C Conference #2 : VANILA JS로 개발하기
2016 W3C Conference #2 : VANILA JS로 개발하기2016 W3C Conference #2 : VANILA JS로 개발하기
2016 W3C Conference #2 : VANILA JS로 개발하기
 
2016 W3C Conference #5 : UNIVERSAL RENDERING (React.JS 중심)
2016 W3C Conference #5 : UNIVERSAL RENDERING (React.JS 중심)2016 W3C Conference #5 : UNIVERSAL RENDERING (React.JS 중심)
2016 W3C Conference #5 : UNIVERSAL RENDERING (React.JS 중심)
 
2016 W3C Conference #8 : IONIC으로 하이브리드 앱 개발하기, 사례와 시사점
2016 W3C Conference #8 : IONIC으로 하이브리드 앱 개발하기, 사례와 시사점2016 W3C Conference #8 : IONIC으로 하이브리드 앱 개발하기, 사례와 시사점
2016 W3C Conference #8 : IONIC으로 하이브리드 앱 개발하기, 사례와 시사점
 
2016 ABCD 소개
2016 ABCD 소개2016 ABCD 소개
2016 ABCD 소개
 
2016 W3C Conference #6 : ReactiveX + Meteor 종단간 암호화 구현 사례
2016 W3C Conference #6 : ReactiveX + Meteor 종단간 암호화 구현 사례2016 W3C Conference #6 : ReactiveX + Meteor 종단간 암호화 구현 사례
2016 W3C Conference #6 : ReactiveX + Meteor 종단간 암호화 구현 사례
 
모바일 디버깅
모바일 디버깅모바일 디버깅
모바일 디버깅
 
Devfest
DevfestDevfest
Devfest
 
Web component
Web componentWeb component
Web component
 
EcmaScript6(2015) Overview
EcmaScript6(2015) OverviewEcmaScript6(2015) Overview
EcmaScript6(2015) Overview
 
Laravel 로 배우는 서버사이드 #5
Laravel 로 배우는 서버사이드 #5Laravel 로 배우는 서버사이드 #5
Laravel 로 배우는 서버사이드 #5
 
Web Components 101 polymer & brick
Web Components 101 polymer & brickWeb Components 101 polymer & brick
Web Components 101 polymer & brick
 
CF Korea Meetup - Spring Cloud Services
CF Korea Meetup - Spring Cloud ServicesCF Korea Meetup - Spring Cloud Services
CF Korea Meetup - Spring Cloud Services
 
내가 환경을 노래하는 이유 | 정욱재 노리플라이
내가 환경을 노래하는 이유 | 정욱재 노리플라이내가 환경을 노래하는 이유 | 정욱재 노리플라이
내가 환경을 노래하는 이유 | 정욱재 노리플라이
 
Data-binding AngularJS
Data-binding AngularJSData-binding AngularJS
Data-binding AngularJS
 
XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기
XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기
XECon2015 :: [2-2] 박상현 - React로 개발하는 SPA 실무 이야기
 
안정적인 서비스 운영 2013.08
안정적인 서비스 운영   2013.08안정적인 서비스 운영   2013.08
안정적인 서비스 운영 2013.08
 
FalsyValues. Dmitry Soshnikov - ECMAScript 6
FalsyValues. Dmitry Soshnikov - ECMAScript 6FalsyValues. Dmitry Soshnikov - ECMAScript 6
FalsyValues. Dmitry Soshnikov - ECMAScript 6
 

Ähnlich wie 2016 W3C Conference #4 : ANGULAR + ES6

Solving anything in VCL
Solving anything in VCLSolving anything in VCL
Solving anything in VCLFastly
 
using Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'susing Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'sAntônio Roberto Silva
 
Live deployment, ci, drupal
Live deployment, ci, drupalLive deployment, ci, drupal
Live deployment, ci, drupalAndrii Podanenko
 
Building and deploying React applications
Building and deploying React applicationsBuilding and deploying React applications
Building and deploying React applicationsAstrails
 
Ch ch-changes cake php2
Ch ch-changes cake php2Ch ch-changes cake php2
Ch ch-changes cake php2markstory
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)Igor Bronovskyy
 
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 202010 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020Matt Raible
 
Future of Web Apps: Google Gears
Future of Web Apps: Google GearsFuture of Web Apps: Google Gears
Future of Web Apps: Google Gearsdion
 
(DEV204) Building High-Performance Native Cloud Apps In C++
(DEV204) Building High-Performance Native Cloud Apps In C++(DEV204) Building High-Performance Native Cloud Apps In C++
(DEV204) Building High-Performance Native Cloud Apps In C++Amazon Web Services
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsFrancois Zaninotto
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For ManagersAgileThought
 
Angular JS deep dive
Angular JS deep diveAngular JS deep dive
Angular JS deep diveAxilis
 
Railsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slideshareRailsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slidesharetomcopeland
 
Real World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS ApplicationReal World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS ApplicationBen Hall
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best PracticesYekmer Simsek
 
Developing web-apps like it's 2013
Developing web-apps like it's 2013Developing web-apps like it's 2013
Developing web-apps like it's 2013Laurent_VB
 
MinbilDinbil Django Speed Tricks
MinbilDinbil Django Speed TricksMinbilDinbil Django Speed Tricks
MinbilDinbil Django Speed TricksLorenzo Setale
 
Slaven tomac unit testing in angular js
Slaven tomac   unit testing in angular jsSlaven tomac   unit testing in angular js
Slaven tomac unit testing in angular jsSlaven Tomac
 

Ähnlich wie 2016 W3C Conference #4 : ANGULAR + ES6 (20)

Solving anything in VCL
Solving anything in VCLSolving anything in VCL
Solving anything in VCL
 
using Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'susing Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API's
 
Live deployment, ci, drupal
Live deployment, ci, drupalLive deployment, ci, drupal
Live deployment, ci, drupal
 
Building and deploying React applications
Building and deploying React applicationsBuilding and deploying React applications
Building and deploying React applications
 
Ch ch-changes cake php2
Ch ch-changes cake php2Ch ch-changes cake php2
Ch ch-changes cake php2
 
Capistrano
CapistranoCapistrano
Capistrano
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
 
Introduction to angular js
Introduction to angular jsIntroduction to angular js
Introduction to angular js
 
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 202010 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
 
Future of Web Apps: Google Gears
Future of Web Apps: Google GearsFuture of Web Apps: Google Gears
Future of Web Apps: Google Gears
 
(DEV204) Building High-Performance Native Cloud Apps In C++
(DEV204) Building High-Performance Native Cloud Apps In C++(DEV204) Building High-Performance Native Cloud Apps In C++
(DEV204) Building High-Performance Native Cloud Apps In C++
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For Managers
 
Angular JS deep dive
Angular JS deep diveAngular JS deep dive
Angular JS deep dive
 
Railsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slideshareRailsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slideshare
 
Real World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS ApplicationReal World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS Application
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
 
Developing web-apps like it's 2013
Developing web-apps like it's 2013Developing web-apps like it's 2013
Developing web-apps like it's 2013
 
MinbilDinbil Django Speed Tricks
MinbilDinbil Django Speed TricksMinbilDinbil Django Speed Tricks
MinbilDinbil Django Speed Tricks
 
Slaven tomac unit testing in angular js
Slaven tomac   unit testing in angular jsSlaven tomac   unit testing in angular js
Slaven tomac unit testing in angular js
 

Kürzlich hochgeladen

Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piececharlottematthew16
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clashcharlottematthew16
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 

Kürzlich hochgeladen (20)

Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piece
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clash
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 

2016 W3C Conference #4 : ANGULAR + ES6

  • 2. • 전 삼성SDS 선임 • TV플랫폼 JavaScript 어플리케이션 구현 • 리테일 솔루션 서버 & 프론트엔드 구현 • 프리랜서 개발자 • han41858.tistory.com 블로그 운영 • Angular 2 번역서 집필중 • GDG Korea Web Tech 운영진 한장현 (Janghyun Han) 2
  • 3. 3
  • 5. • 마술 같은 2-way binding • HTML 표준을 기반으로 기능 확장 • 체계적인 컴포넌트, Web Component로 가는 길 • Front-end 전체를 커버하는 프레임워크 • Google 에서 관리, 개선 • 풍부한 사용자 삽질 경험 Angular를 쓰는 이유 5
  • 7. • 간단해지는 코드, syntax sugar • 클래스, 상속, 모듈 구성 ⇒ 아키텍처 진화 • 브라우저들 지원 계속 • 결국엔 표준, transpiler는 거쳐갈 뿐 ES6를 쓰는 이유 7
  • 8. 8
  • 9. ES6ES5 script src <script src="bundle.js"></script> "devDependencies": { "angular": "^1.5.8", "babel-cli": "^6.18.0", "babel-loader": "^6.2.7", "babel-preset-es2015": "^6.18.0", "webpack": "^1.13.3" } <script src="angular.js"></script> <script src="index.js"></script> "devDependencies": { "angular": "^1.5.8" } 9
  • 10. index.js ES6ES5 (function () { var ngApp = angular.module('angular1es5', []); })(); import angular from 'angular'; (() => { const ngApp = angular.module('angular1es6', []); })(); 10
  • 11. Webpack // this is ES5 var webpack = require('webpack'); module.exports = { entry : [ './index.js' ], output : { filename : 'build/bundle.js', sourceMapFilename: '[name].map' }, module : { loaders : [ { test : /.js$/, loader : 'babel?presets[]=es2015 exclude : /node_modules/ }, { test : /.pug$/, loader : 'pug-loader', exclude : /node_modules/ } ] }, plugins : [ // new webpack.optimize.UglifyJsPlugin({minimize: true}) ] }; webpack.config.js "devDependencies": { "angular": "^1.5.8", "angular-route": "^1.5.8", "babel-cli": "^6.18.0", "babel-loader": "^6.2.7", "babel-preset-es2015": "^6.18.0", "file-loader": "^0.9.0", "pug": "^2.0.0-beta6", "pug-loader": "^2.3.0", "webpack": "^1.13.3", "webpack-dev-server": "^1.16.2" } package.json 11
  • 12. export default class HomeCtrl { constructor () { console.log('HomeCtrl.constructor()'); } } /view/homeCtrl.js p this is home /view/home.pug import angular from 'angular'; import ngRoute from 'angular-route'; import HomeCtrl from './view/homeCtrl'; const main = () => { console.log('main()'); const ngApp = angular.module('angular1es6', ['ngRoute']); ngApp.config(($routeProvider, $locationProvider) => { console.log('this is angular config'); $routeProvider .when('/', { template : require('./view/home.pug'), controller : 'HomeCtrl', controllerAs : 'Ctrl' }) .otherwise({ redirectTo : '/' }); // need to angular.js routing $locationProvider.html5Mode({ enabled : true, requireBase : false }); }); ngApp.controller('HomeCtrl', HomeCtrl); }; main(); index.js + ngRoute 12
  • 14. ngApp.directive('CustomDirective', () => new CustomDirective); ngApp.filter('groupBy', GroupBy); ngApp.service('CustomSvc', CustomSvc); ngApp.controller('CustomCtrl', CustomCtrl); Angular Components ngApp.directive('CustomDirective', CustomDirective); ngApp.filter('groupBy', GroupBy); ngApp.service('CustomSvc', CustomSvc); ngApp.controller('CustomCtrl', CustomCtrl); function Class new Class 14
  • 15. Filter ES6ES5 ngApp.filter('uppercase', uppercase); function uppercase () { return function (item) { return item.toUpperCase(); }; } <script src="uppercase.filter.js"></script> const uppercase = () => { return (input) => { return input.toUpperCase(); }; }; export default uppercase; import uppercase from './uppercase.filter'; ngApp.filter('uppercase', uppercase); 15
  • 16. .ctrlRoot p this is home ctrl p {{ Ctrl.title }} button(ng-click="Ctrl.select()") Select export default class HomeCtrl { constructor () { console.log('HomeCtrl.constructor()'); this.title = 'this is title'; } select () { console.log('HomeCtrl.select()'); } } $routeProvider .when('/', { template : require('./view/home.pug'), controller : 'HomeCtrl', controllerAs : 'Ctrl' }) ngApp.controller('HomeCtrl', HomeCtrl); function HomeCtrl ($scope) { console.log('home controller'); $scope.title = 'this is title'; $scope.select = function () { console.log('HomeCtrl.select()'); } } Controller ES6ES5 .ctrlRoot p this is home ctrl p {{ title }} button(ng-click="select()") Select 16
  • 17. Service ES6ES5 ngApp.service('myService', myService); <script src="./view/myService.js"></script> function myService () { this.testFnc = function () { console.log('myService.testFnc()'); }; return this; } export default class MyService { constructor () { console.log('MyService'); } testFnc () { console.log('MyService.testFnc()'); } } ngApp.service('MyService', MyService); export default class HomeCtrl { constructor (MyService) { this.MyService = MyService; } select () { this.MyService.testFnc(); } } static 있어야 할 것 같지만 없어야 함 17
  • 18. ngApp.directive('myDirective', () => new MyDirective); export default class MyDirective { constructor () { console.log('MyDirective.constructor()'); this.restrict = 'E'; this.template = '<p>message : {{ this.msg }}</p>'; this.scope = { msg : '@' }; } link (scope) { console.log(scope.msg); } } ngApp.directive('myDirective', myDirective); function myDirective () { return { restrict : 'E', template : '<p>message : {{ msg }}</p>', scope : { msg : '@' }, controller : function ($scope) { console.log('myDirective.controller()'); console.log($scope.msg); } } } <script src="myDirective.js"></script> Directive ES6ES5 directive 등록할 때 () => new18
  • 20. Directive vs. Component const customInput = { bindings : { model : '=' }, template : '<input ng-model="$ctrl.model"></input>', controller : function () { this.$onInit = () => { } } }; export default customInput; ngApp.component('customInput', customInput);ngApp.directive('customInput', () => new customInput); export default class customInput { constructor () { this.restrict = 'E'; this.scope = { model : '=' }; this.template = '<input ng-model="model"></input>'; } controller () { } } 20
  • 21. static delete (param) { const self = this; return util.objValidate(param, { userID : Constants.TYPE.EMAIL }, Constants.ERROR.USER_CTRL.NO_PARAMETER, log, 'delete()') .then(param => self.isExists(param)) .then(param => { // delete records return recordCtrl.deleteAll({ userID : param.userID }) .then(() => { log('remove records ok'); // param 자체를 다시 돌려주기 위해 Promise 필요 return Promise.resolve(param); }); }) .then(param => { // delete cards return cardCtrl.deleteAll({ userID : param.userID }) .then(() => { log('remove cards ok'); return Promise.resolve(param); }); }) .then(param => { // delete assets return assetCtrl.deleteAll({ userID : param.userID }) .then(() => { log('remove assets ok'); return Promise.resolve(param); }); }) .then(param => { // delete user return User.remove({ userID : param.userID }) .then(() => { return Promise.resolve(param); }, error => { log(error); return Promise.reject(new ERROR(Constants.ERROR.MONGOOSE.REMOVE_FAILED, log, 'delete()')); }); }) .then(param => { log(`delete ok : ${param.userID}`); return Promise.resolve(true); }); } Promise 21
  • 22. ngApp.config(($routeProvider, $locationProvider) => { // include styles require('./view/home.styl'); $routeProvider .when('/', { template : require('./view/home.pug'), controller : 'HomeCtrl', controllerAs : 'Ctrl' }) .otherwise({ redirectTo : '/' }); }); 배포 : webpack import가 아니므로 require 22
  • 23. Angular 1 + ES6 + BDD = Hell 23
  • 24. describe('homeCtrl.test', () => { it('module import', () => { expect(true).to.be.true; }); }); λ karma start (node:7564) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead. 18 11 2016 02:53:45.852:INFO [framework.browserify]: bundle built 18 11 2016 02:53:45.941:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/ 18 11 2016 02:53:45.941:INFO [launcher]: Launching browser Chrome with unlimited concurrency 18 11 2016 02:53:45.952:INFO [launcher]: Starting browser Chrome 18 11 2016 02:53:47.283:INFO [Chrome 54.0.2840 (Windows 10 0.0.0)]: Connected on socket /#AjAqCwTlrwmVmV_sAAAA with id 16857313 Chrome 54.0.2840 (Windows 10 0.0.0): Executed 1 of 1 SUCCESS (0.005 secs / 0.001 secs) BDD 시작 24
  • 25. λ karma start (node:12196) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead. 18 11 2016 02:19:10.237:INFO [framework.browserify]: bundle built 18 11 2016 02:19:10.343:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/ 18 11 2016 02:19:10.343:INFO [launcher]: Launching browser Chrome with unlimited concurrency 18 11 2016 02:19:10.353:INFO [launcher]: Starting browser Chrome 18 11 2016 02:19:11.676:INFO [Chrome 54.0.2840 (Windows 10 0.0.0)]: Connected on socket /#sBpP4RL0XFZAwPtxAAAA with id 52822107 Chrome 54.0.2840 (Windows 10 0.0.0) ERROR Uncaught SyntaxError: Unexpected token import at test/homeCtrl.test.js:1 import HomeCtrl from '../view/homeCtrl'; describe('homeCtrl.test', () => { it('module import', () => { console.log(HomeCtrl); expect(true).to.be.true; expect(HomeCtrl).to.be.ok; }); }); λ karma start (node:11580) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead. 18 11 2016 02:31:24.248:INFO [framework.browserify]: bundle built 18 11 2016 02:31:24.339:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/ 18 11 2016 02:31:24.339:INFO [launcher]: Launching browser Chrome with unlimited concurrency 18 11 2016 02:31:24.349:INFO [launcher]: Starting browser Chrome 18 11 2016 02:31:25.657:INFO [Chrome 54.0.2840 (Windows 10 0.0.0)]: Connected on socket /#pvpyGrXqq2TZPTgmAAAA with id 30236974 LOG: class HomeCtrl { ... } Chrome 54.0.2840 (Windows 10 0.0.0): Executed 1 of 1 SUCCESS (0.007 secs / 0.002 secs) babel : node6 λ karma start (node:13196) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead. 18 11 2016 02:58:38.638:INFO [framework.browserify]: bundle built 18 11 2016 02:58:38.728:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/ 18 11 2016 02:58:38.729:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency 18 11 2016 02:58:38.738:INFO [launcher]: Starting browser PhantomJS 18 11 2016 02:58:40.301:INFO [PhantomJS 2.1.1 (Windows 8 0.0.0)]: Connected on socket /#JeRwavdozVZCC8HJAAAA with id 75347319 LOG: function HomeCtrl(MyService) { ... } PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 1 of 1 SUCCESS (0.008 secs / 0.001 secs) babel : es2015 ES6가 돌지 않는다… + 25
  • 26. var $httpBackend; beforeEach(function () { module('angular1es5'); inject(function (_$httpBackend_) { $httpBackend = _$httpBackend_; }); }); module 선언, injection 불가 ES6ES5 const $injector, $httpBackend; beforeEach(() => { $injector = angular.injector(['angular1es6']); $httpBackend = $injector.get('$httpBackend'); }); Object is not a constructor (evaluating 'module('angular1es6')') r:/temp/test/homeCtrl.test.js:15:9 <- R:/temp/3871fde1c6cf6c302eeae7add18a3b02.browserify:22:9 26
  • 27. ngMock vs. ngMockE2E The ngMock module provides support to inject and mock Angular services into unit tests. In addition, ngMock also extends various core ng services such that they can be inspected and controlled in a synchronous manner within test code. The ngMockE2E is an angular module which contains mocks suitable for end-to-end testing. Currently there is only one mock present in this module - the e2e $httpBackend mock. ngMock ngMockE2E Fake HTTP backend implementation suitable for end-to- end testing or backend-less development of applications that use the $http service. Fake HTTP backend implementation suitable for unit testing applications that use the $http service. .when() .expect() .flush() .verifyNoOutstandingExpectation() .verifyNoOutstandingRequest() .resetExpectations() .when() ngMockE2E에는 $location 없음 27
  • 28. Promise + http.flush() Chrome 54.0.2840 (Windows 10 0.0.0) homeCtrl.test http test ok FAILED Error: No pending request to flush ! at Function.$httpBackend.flush (node_modules/angular-mocks/angular-mocks.js:1799:34) at r:/temp/test/homeCtrl.test.js:36:17 promise 함수로 부르면 동작 안함 flush() 타이밍 달라짐 flush는 Util에서 수행 const $injector = angular.injector(['angular1es6']); const $httpBackend = $injector.get('$httpBackend'); const $http = $injector.get('$http'); beforeEach(() => { $httpBackend.expectGET('/test') .respond(['this', 'is', 'GET', 'test', 'data']); }); it('ok', () => { return new Promise(resolve => { $http.get('/test').then(result => { console.log('get().then()'); console.log(result); console.log(result.data); resolve(true); }); $httpBackend.flush(); }); }); 동작하는 코드 static post (uri, param, config) { // use new promise for flush() return new Promise((resolve, reject) => { this.http.post(uri, param, config) .then(result => { resolve(result); }, error => { console.error(error); reject(false); }); if (this.httpBackend && this.httpBackend.flush) { this.httpBackend.flush(); } }); } it('ok', () => { return new Promise(resolve => { Promise.resolve() .then(() => { $http.get('/test').then(result => { console.log('get().then()'); console.log(result); console.log(result.data); resolve(true); }); }); $httpBackend.flush(); }); }); 28
  • 29. 여러 test 파일 동시 실행 const $injector = angular.injector(['angular1es6']); const $httpBackend = $injector.get('$httpBackend'); const $http = $injector.get('$http'); Chrome 54.0.2840 (Windows 10 0.0.0) SignInCtrl test logic submit() with ajax error response response error test FAILED AssertionError: expected [Error: Unexpected request: POST /api/user/signIn No more request expected] to be an instance of ERROR Chrome 54.0.2840 (Windows 10 0.0.0) SignUpCtrl test logic submit() error response response error test FAILED AssertionError: expected [Error: [$rootScope:inprog] $digest already in progress const ngApp = angular.module(appName, ['ngMock']); // working with $location, but not $httpBackend.whenPOST()... var $injector = angular.injector(['MoneyBook']); const $httpBackend = $injector.get('$httpBackend'); const $http = $injector.get('$http'); ClientUtil.http = $http; ClientUtil.httpBackend = $httpBackend; 29
  • 30. 생성자 안에서 Promise export default class HomeCtrl { constructor () { console.log('HomeCtrl.constructor()'); Promise.resolve(() => { // do something }); } } describe('constructor()', () => { it('ok', () => { // expect what...? }); }); export default class HomeCtrl { constructor () { console.log('HomeCtrl.constructor()'); this.somethingPromise(); } somethingPromise(){ return Promise.resolve() .then() => { // do something }); } } 30
  • 31. Promise + $scope.$apply() export default class HomeCtrl { constructor () { console.log('HomeCtrl.constructor()'); this.count = 0; } select () { console.log('HomeCtrl.select()'); return Promise.resolve() .then(() => { this.count++; }); } } export default class HomeCtrl { constructor ($scope) { console.log('HomeCtrl.constructor()'); this.$scope = $scope; this.count = 0; } select () { console.log('HomeCtrl.select()'); return Promise.resolve() .then(() => { this.count++; this.$scope.$apply(); }); } } 31