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

Publication

Statistics Graph

Category

Recent Comment

2016.12.20 20:54 Angular/Concept

오늘은 Progressive Web Apps 개발에 대한 세미나 참석 내용을 정리해 본다. 



AMP

Progressive는 구글이 생각하는 서비스에 대한 점진적인 개발 진행 방향을 이야기 한다. 즉, 기술아니라 사용자 경험을 더 좋게 하기 위한 개념이다. AMP (Accelerated Mobile Pages) 는 모바일 페이지를 빠르게 만드는게 목표이다. 네트워크가 빠르면 모바일 웹이 느리다는 것을 느낄 수 없지만 느리면 반응이 느려진다. 해당 기술은 데스크톱에도 동일하게 적용될 수 있다. AMP는 정적 페이지를 위한 것이다. 


- amp html


<html amp>를 넣는다. <style amp-boilerplate>를 넣어 페이지 로딩바가 보이도록 한다. 

<!doctype html>

<html amp>

 <head>

   <meta charset="utf-8">

   <link rel="canonical" href="hello-world.html">

   <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">

   <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>

   <style amp-custom>

    

   </style>

   <script async src="https://cdn.ampproject.org/v0.js"></script>

 </head>

 <body>Hello World!</body>

</html>


- <link rel="canonical" href="amp.html">  amp를 지원하지 않는 브라우저대응을 위한 대신할 html 지정 

- 속도를 위해 amp로 된 웹페이지를 하나 더 만들어 서비스 하는것이다. amp는 기존의 html, css를 변경해서 적용하는 것이다. 



Service Worker

Web Worker는 메인페이지와 병렬 스크립트를 실행하는 백그라운드 워커를 생성하는 API로서 메세지 전송기반의 스레드 수행과 유사하다. UI Rendering과 분리되어 수행할 수 있다. 따라서 Web Work에서는 


- DOM을 직접 건드릴 수 없다. 

- 자체적인 글로벌 스코프

- 일부 속성과 API만 허가: 보면서 디버깅할 수 있는 것이 없다


Service Worker는 오프라인일 경우에도 웹 어플리케이션의 기동을 가능토록 하는 훅(Hook)을 포함하여, 어플리케이션으로 하여금 지속적인 백그라운드 포로세싱의 장점을 취하도록 하는 방법. 브라우저에서 로컬 웹서버가 있는 것과 같다. 


- 지속적인 백그라운드 처리

- 브라우저에 무언가 설치하는 것이다


정적인 파일에 대한 요청이 있을 때 네트워크가 끊어졌을 때 로컬 브라우저에서 서비스를 할 수 있게 한다. 백그라운드에서 정적 파일을 가져와서 브라우저 요청을 로컬 Service Worker 에서 처리해 준다. Application Cache의 스펙 오류를 해결하기 위해 Service Worker가 나왔다. 


- 기본 https 에서만 작동한다. 해킹방지: 중간 공격자를 피하기 위함

- 대신 127.0.0.1, localhost 테스트는 가능하다 


예, 오프라인 웹앱, 구글의 공룡게임 - 브라우저에 함께 존재한다. 


배워야 할 것은 


- 리소스 저장을 위한 Cache Storage - promise객체를 반환 

- 이벤트 생명 연장의 꿈: .waitUntile()  이벤트가 종료되지 않도록 한다  

- 패치 onfetch() 브라우저에서 리소스 접근 요청될 때 호출되는 이벤트 -> FetchEvent 요청내용을 담고 있다. .responseWith() 통해 응답한다



NAMP-CARD 

https://ampbyexample.com 좀 더 심화된 예제 사이트 이다. sw.js를 작성한다. 


var CACHE_NAME = 'pwa-workshop.github.id-namp-card-cache-v1';

// 하기 해당하는 내용을 캐쉬에 저장한다 

var urlsToCache = [

'/namp-card',

    '/index.html',

    '/manifest.json',

    '/user.png'

];


self.addEventListener('install', function(event) {

event.waitUntil(

caches.open(CACHE_NAME).then(function(cache) {

console.log(`Opened cache for namp-card ${new Date()}`);

return cache.addAll(urlsToCache);

})

);

});


self.addEventListener('fetch', function(event) {

    event.waitUntile(

        // 캐쉬안에 브라우저 요청과 매칭되는 것이 있으면 리턴을 해준다. 

        // 없으면 fetch(event, request.url)로 원격 서버에 요청 

        caches

            .match(event.request.url)

            .then(function(res) {

                return res || fetch(event, request.url);

            })

    )

});


등록을 한다. amp-install-serviceworker 스크립트를 이용하면 하기 내용이 들어간다. 개발자가 직접 넣을 수 없다. 

navigator.serviceWorker.register('/sw.js', { scope: '/' })

          .then(function(registration) {

                console.log('Service Worker Registered');

          });


https://airhorner.com/ 예의 DevTool에서 Service Worker를 볼 수 있다. 




모바일 기기에 설치형 웹앱 만들기

설치형이란? 


- 네트워크 불안하거나 오프라인인 경우에도 동작

- 홈스크린에서 바로 실행

- 앱의 이름과 아이콘을 제공


동작 가능한 웹앱


- 서비스워커의 지원 필요

- 프리캐쉬(Pre-cache)된 애플리케이션 프레임워크(App-shell) 사용

- 서비스워커를 통해 Indexed DB 또는 히스토리컬 데이터 디스플레이 


반드시 웹 메니페이스(Manifest)를 작성해야 함


- 설치 방법을 기술: 브라우저에서 Add to Home Screen 하면 모바일 바탕화면에 아이콘이 나온다. -> 굳이 앱을 설치하지 않아도 접근 가능토록 한다

- Splash 윈도우 사용가능: 아이콘 지정, 텍스트 지정 

- Prompt (부추기기)를 통해 Add to Home Screen를 하도록 유도한다. 이를 통해 앱의 설치없이 전환율을 높인다. 

- 노드 모듈의 pwa-manifest를 설치해서 사용할 수도 있다. 



실제로 수행하는 순서 참조


-  https://github.com/pwa-workshop/roadshow/blob/master/turn-into-an-installable-webapp.md

- github.com의 개인 프로필로에서 https 를 사용토록 한다.  



<참조> 

https://github.com/pwa-workshop/namp-card

https://www.ampproject.org/ko

신고
posted by peter yun 윤영식
2016.11.23 16:50 Angular/Electron & Ionic

Electron을 통한 테스트 자동화 접근법 세션


- 정적 테스트 프로그램은 테스트 프로그램이 있는 것, 동적 테스트는 기존 애플리케이션을 통해 테스트 하는 방식. 

- ATDD 수용성 테스트는 E2E 테스트를 한다. 

  + Selenium -> WebDriver

  + Nightwatch


- Electron 

  + Node Based Desktop Application

  + View렌더링을 위한 브라우져를 가지고 있으므로 전통적은 Hybrid는 아니다. 전통적인 Hybrid는 view를 쓰기위해 webview를 쓰고 이에 대한 브라우져가 os에 설치되어 있어야 view 표현이 가능했다. 그러나 eletron은 view를 내장하고 있고 standalone으로 application구현이 가능하다.

  + render process (chrome) + main process (node) 간에 RPC통신을 한다. 


- Psyclone: Automated Dynamic Testing

  + https://github.com/firejune/psyclone 

  + Node Canvas: main process에서 수행됨

  + Environment: PTY.js, openCV (게임의 이미지 체크)




Javascript playground 세션


- Swift playground와 같은 형태이다.

- Runtime Context Visualizer 구현하기 

  > 내가 찍어 놓은 console.log를 추적 

  > let stack = new Error().stack 강제 로그 stack-trace 남기기

  > 외계어 스터디 (구글검색하면 나옴): 개발을 모르는 사람들에게 무언가 가르칠 때 힘들다. 코딩하는 것을 비쥬얼라이징 해보자. 

- V8 엔진은 Debugging이 가능한 API를 가지고 있다. 

  > node의 vm module을 사용: v8내에서 다양하게 쓰이는 것을 외부로 노출해 준다.

     runInContext, runInNewContext 등등

- node-inspector

  > IDE 수준의 code base로 꽤 큰 프로젝트

  > DEBUG=*node-debug app.js 하면 node debugging 을 inspector로 가능하다 

  > node v6.3.1 에서만 작동한다. node에 inspector를 merge하는 작업 진행중. 

     v8-debug : node <-> inspector간 내용

     devtools : inspector <-> chrome devtools간 내용


- Node debug mode 를 이용해 자바스크립트 내용을 축출한다

  > node --debug --debug-port=5859 --debug-brk app.js

      Debugger listening:5859

  > v8 debugging protocol using http

  > 전문의 body에 seq, type 이 들어간다. 

     type = request + response + event 포함 


- Node debugger Command 

  > continue : 코드 진행 next, in, out, min

  > backtrace : 코드가 멈춰있을 때 모든 정보를 가져온다 

  > frame : 방금 실행한 것을 이전 상태로 rollback에서 수행 가능하다 

  > setVariableValue : runtime의 로컬 변수를 직접 수정

  > lookup : 로컬 변수로 watching 모니터링 할 수 있다. 


해당 명령을 tcp로 node debugger에 쏴주면 된다. @node/lib/_debugger.js 가 위의 명령어를 사용하고 있다. 

  > npm install v8-debugger


- Javascript Playground 

  > 왼쪽 자바스크립트 코드 오른쪽 결과 값을 보여주기 화면을 제작한다

  > onResponse 핸들러에서 결과 값을 받는다. 

  > 코드의 의미 파악: 식, 문을 파악해야 함 -> Javascript Parser 필요, esprima 파서 

     esprima.parse(코드) 결과는 각 진행 수선의 type을 전달 하는 메타 정보를 준다

  > Runtime Context Visualizer

     https://github.com/ibare/jsplay


 


참조


http://techblog.daliworks.net/Nightwatchjs/ 

신고
posted by peter yun 윤영식
2016.10.23 22:59 Angular/Concept

Angular v2가 정식 릴리즈되었다. Angular v1 은 Two-way data-binding 이라는 독특한 특징으로 인해 많은 사용자 층을 확보했지만 장점 만큼이나 성능상의 단점도 존재했었다. 또한 처음엔 쉬운듯 하면서 좀 더 깊게 들어가볼려고 하면 학습곡선이 갑작이 껑충뛰기도 했다. 가장 많이 사용했던 Directive(지시자)가 대표적이다. 많은 개발자가 만들어 놓은 지시자를 쉽게 가져다 쓸 수는 있지만 직접 만들어 애플리케이션에 접목하려 할 때 첫 문턱을 만나게 된다. 그리고 jQuery사용에 익숙한 개발자에게 Angular v1 시점상의 차이로 Angular v1 방식의 개발패턴을 요구하기도 했다. 관성은 무섭다. 기존에 사용하던 방식을 버리고 Angular v1에 맞춰서 애플리케이션을 만들어 가기란 곤혹스럽다. Angular v2 또한 그런 인식의 전환을 요구할까? 그렇다 그리고 아니다. 






웹 애플리케이션 흐름

웹 애플리케이션 개발을 위해 우리가 사용하는 jQuery같은 라이브러리나 Angular, Backbone같은 프레임워크의 가장 1차적인 목적은 무엇일까? 나는 Data Projection이라 생각한다. 데이터를 화면에 출력하기 위해 DOM을 얼마나 쉽게 조작하고 상호 작용할 수 있느냐가 선택의 기준이라 생각한다. Data Projection을 일관되고 확장가능하고 배포가능하게 하는 방식으로 기술은 발전해 왔고, 현재는 화면에 대한 제어방식이 컴포넌트 기반 방식으로 발전해 오고 있다. 


Data Projection의 역사를 보면 초장기엔 Server Side Rendering 를 사용해 웹 애플리케이션을 개발했다. 예로 JSP, PHP, ASP 같이 서버 미들웨어서 데이터를 조회하고 HTML을 조작하여 결과 HTML을 브라우져에 전송하던 시대이다. 




1세대에는 AJAX가 나오고 다양한 라이브러리나 프레임워크가 나왔다. 이때는 데이터변경에 대한 DOM반영이 서버에서 클라이언트 개발자의 몫으로 넘어오게 되었다. 즉, 직접 DOM 을 얻어와서 특정 위치에 넣어 주어야 했고, DOM에서 발생하는 이벤트를 Listening해서 처리하고 DOM에 반영하는 모든 작업이 웹 개발자가 직접 코딩하던 단계였다. Java의 프레임워크 역사로 보면 Struts 로 비유할 수 있지 않을까 싶다. 




2세대로 넘어오게 되면 Model을 DOM 에 반영하는 방식은 자동화 된다. 여기에 대표적인 프레임워크가 Ember 와 Angular v1 이다. 이때 부터 Single Page Application (SPA) 개발이라는 용어가 나오게 된다. URI 변경에 대한 대응으로 Routing  개념이 나오고, Data Projection후 원하는 일부 DOM을 변경하는 역할이 프레임워크로 넘어갔고, 웹 개발자는 좀 더 애플리케이션 비즈니스 로직에 집중토록 만들었다. Java 프레임워크로 비유하자면 Struts와 Spring Framework 초기버전의 중간 지대 정도 쯤이라 생각한다.  이때부터 Frontend (프론트앤드)라는 직군이 웹 개발자와 분리되기 시작한 지점이라 생각한다. 이에 대한 자세한 설명은 태곤님이 작성한 "[번역] 프론트엔드 개발자는 왜 구하기 어렵나요?"를 참조하자. 2011년을 기점으로 2013년 웹 애플리케이션 프레임워크가 정착을 해가는 시기였고, 현재는 대부분의 스타트업이나 중견기업에서 2세대 웹 애플리케이션 프레임워크를 선택할 경우 프론트엔드 개발자와 백앤드 개발자를 구분하여 팀을 구성하고 있는 추세이다.





3세대는 2세대의 과도기를 거쳐 2세대의 장점을 흡수 하면서 성능상의 이슈를 해결하고, 점점 복잡해 지고있는 웹 애플리케이션을 보다 직관적이고 쉽게 개발할 수 있게 노력하고 있다. 대표적인 프레임워크로는 Facebook의 React와 Google의 Angular v2 (이하 Angular)이다. Angular는 Component기반 개발 방식으로 표준인 Web Components를 지원하며 Typescript를 기본 언어로 채택했다. Typescript는 Type 시스템을 제공하기 때문에 개발단계에서 버그의 가능성을 쉽게 찾을 수 있도록 도와준다. React와 Angular에 대한 장단점은 손창욱님의 "React보다 Angular v2에 더 주목해야 하는 이유"를 참조하자. Java의 Spring Framework이 성숙하면서 Annotation 같은 기능이 추가되듯, Angular v2 프레임워크는 Java의 Spring 프레임워크 최신버전과 비유할 수 있다. 



Angular v1에 대한 개발 및 컨설팅을 3년 가까이 하면서 올해 초 Angular v2를 공부하고 기존 v1 코드를 v2 코드로 전환하면서 코드 베이스는 50%가량 줄었고, 반응속도는 30%가량 개선되었다. 8명 프론트앤드 개발자와 컨버전을 진행하면서 이구동성으로 말하는 것은 "코드가 직관적으로 변했다. 코드량이 현저히 줄었다. Typescript의 타입체킹으로 인해 실수를 최소화 할 수 있었다" 이다. 



Angular v2 왜 배워야 하는가?

Angular를 왜 배워야 하는가? 답하자면 안배워도 된다. 단순 홈페이지나 업무 화면이라면 쉽고 더 빨리 만들 수 있는 워드프레스나 서비스를 이용하거나 DOM 핸들링 라이브러리나 플러그인을 사용해 개발하는 편이 낳다. 하지만 솔루션의 복잡한 요구사항을 지속적으로 반영해야 하고 DOM제어가 복잡해 질 가능성이 높다면 jQuery, React 같은 라이브러리 보다는 Angular 같은 프레임워크를 선택하는 것이 좋다. 그리고 최근에는 ES2015 표준이 확정되었고 최신 브라우져에 대부분 기능이 구현되고 있다. 2세대와 3세대 Data Projection의 가장 큰 개발 방식의 차이는 ES2015의 이해에서부터 시작한다.  즉, ES2015 문법을 잘 알고 사용하면 좀 더 쉽고 간단하게 코드 베이스를 유지하면서 오류를 최소화할 수 있다. 예로 -> 펑션은 this에 대한 오류를 방지하고, Set/Map등 Collection은 Java의 Collection과 유사한다. 



Angular v2 시작하면 초기에 배워야 하는 것들이 갑작이 늘어난다. 이것은 2세대와 3세대의 개발 패턴이 바뀌었음을 시사한다. ES2015 문법은 그대로 TypeScript에 녹아 있고, Type System과 Annotation 기능이 녹아 들어 더욱 편리한 개발을 가능토록 한다. 따라서 ES2015의 Syntax와 개념을 이해해야 한다. 그리고 Typescript를 다시 공부해야 한다. 또한 요즘 인기를 누리고 있는 Reactive Programming을 표방한 대표적인 라이브러리인 RxJS를 Angular가 근간으로 사용하고 있다. 따라서 RxJS 에 대한 개념과 사용법을 익혀야 한다. 그런후 Web Components 란 무엇인지 알아야 하고, Angular 프레임워크의 아키텍쳐를 구성하는 개념인 Change Detection 동작원리, Dependency Management, Modulization 을 알아야 하고, 다음으로 주변의 Tooling System으로 SystemJS (Webpack), Gulp 등을 알아야 한다. 


이렇게 열거해 보니 참으로 배울 것이 많다. 다시 말하지만 안 배워도 된다. 하지만 자신의 근육을 한단계 업그레이드 시키기 위해 고통스러운 인내의 시간은 필요하다. 배워야 하는 기준은 두가지 정도로 이야기 해본다. 


첫째, 서비스 버전업을 위해 요구사항이 계속 증가하고 있는가?

둘째, 더 적고 직관적인 코드 베이스를 유지하면서 성능을 높이고 싶은가?


 

프론트엔드 개발자 직군이 새롭게 자리잡게된  5년기간 동안 많은 부분이 기존의 백앤드 개발 패턴과 유사해 지고 있다. 모듈 의존성 관리, 빌드 시스템, 프레임워크의 발전은 Java개발자들이 초장기 프레임워크 없이 개발하다 Struts를 만났을 때 기쁨에서 Spring을 만나 자유를 얻었지만 여전히 배워야 할 것들은 더욱 증가했음을 알것이다. 그러나 어쩌겠는가 우리는 더 게을러 지고싶다는 욕구가 있고 프레임워크가 그것을 만족시켜줄것이라는 희망을 품고 있는 한 배움과 진보는 계속될 뿐이다. 



참조


신고
posted by peter yun 윤영식
2016.05.01 15:34 Angular/Concept

컴포넌트 기반 개발시에 어떤 설계방식으로 해야하는지 알아본다. 새롭게 알게된 GistRun 서비스를 사용해서 테스트해 볼 것이다. 예제의 내용은 최근 Rangle.io의 Angular2 Component 온라인 강좌를 참조한다.





GistRun 사용하기 


github기능중 코드 조각을 관리할 수 있는 서비스가 있다. 한번에 최대 10개까지 파일을 만들 수 있다.  여기를 클릭해 보면 Gist에 입력한 한 주제에 대한 파일을 보여주고 Gist의 내역을 테스트 해 볼 수 있다. 



plunker 또는 jsfiddle의 내용도 불러와서 나의 Gist에 저장을 할 수 있다. 각 서비스의 장점이 있지만 일단 GistRun은 UI가 깔끔하고 속도가 빠르다. 그리고 수정사항에 대한 버전을 관리할 수 있고, Github의 종속된 서비스이므로 소스를 관리한다는 입장에 일관성을 가질 수 있을 것으로 보인다. Gist만으로 모자랐던 것을 GistRun이 생명력을 불어 넣은 격이다. 





Angular2 Component 가이드


Angular2는 컴포넌트로 시작해서 컴포넌트로 끝난다. 컴포넌트기반으로 UI를 설계할 때 자주 나오는 방식이 프리젠테이션과 컨테이너의 분리이다. 


  - Container 컴포넌트

    + 데이터와 비즈니스 로직을 다룬다

    + 컴포넌트 트리(Tree)를 렌더링 한다

    + 재사용하기 어렵다 


  - Presentational 컴포넌트

    + 상태를 포함하지 않는다. 따라서 컴포넌트 속성에 대한 수순(pure) 펑션으로 구성한다

    + 표현만을 담당한다

    + 재사용이 가능하다 


Thinking in React의 내용은 Angular2의 컴포넌트 개발 방식과 거의 일치한다. Container 컴포넌트는 하위 컴포넌트의 이벤트에 대해 XHR 요청을 수행하는 Angular2 Service를 Inject해서 서버로 부터 데이터를 요청하고, Component Tree를 통해 하위로 데이터를 내려 보낼 수 있다. 



오렌지색 부분은 전체 애플리케이션을 감싸는 Container이고 초록색은 테이블의 Row를 표현한다. 프리젠테이션 부분은 상단의 Search Input, 하단의 Row들이 된다. Angular2에서 검색을 위한 Input과 Button, Title 애플리케이션을 다음과 같이 진행한다. 



Seach The Site 레이블을 표현하는 컴포넌트를 개발한다.  @은 Typescript의 Decorator를 이용해 Angular2에 구현된 것으로 Parent -> Child 방향으로 DOM Properties를 통해 값을 받고자 할 때 사용한다. 즉, RangleLabel 컴포넌트를 사용하는 Parent 컴포넌트가 name 속성에 [] 을 이용해 값을 설정한다.  

  - 컴포넌트를 사용할 때 [name]="<expression>"  [name] 부분을 target이라 하고 <expression> 부분을 source라고 한다. 

  - []을 사용할 때 source는 항상 template expression 즉, 식이어야 한다. (statement "문"은 이벤트에서 사용함) 

  - target에 []가 있으면 source는 항상 expression으로 수행된다.

import { Component, Input } from 'angular2/core';


@Component({

  selector: 'rangle-label',

  template: '<label class="rangle-label">{{ name }}</label>',

  styles: [ `

    .rangle-label {

      color: #422D3F;

      display: block;

      font-weight: bold;

      letter-spacing: .2em;

      text-transform: uppercase;

    }

  `]

})

export class RangleLabel {

  @Input() private name: string;

}


예) 부모 컨테이너에서 사용할 때: 부모 컨테이너 컴포넌트의 name 속성을 자식 컴포넌트인 rangle-label에 내려보낸다.  

     <rangle-label [name]="name"></rangle-label>


다음으로 Input 컴포넌트를 만든다. value 속성에 대해 ngModel처럼 양방향 바인딩을 주기 위해서는 컨벤션(Convention)처럼 Input은 value이고, Output은 valueChange이어야 한다. 

  - [()] 표식은 banana in the box 표현으로 양방향 데이터 바인딩이다. 

  - [] 은 Input으로 parent -> child로 속성값을 내려 보낼 수 있다. (top down)

  - () 은 Output으로 child -> parent로 값을 올려 보낼 수 있다. (bubble up)

import { Component, Input, Output, EventEmitter } from 'angular2/core';


@Component({

  selector: 'rangle-text-field',

  template: `

    <input class="rangle-text-field"

      [placeholder]="placeholder"

      #field (keyup)="handleKeyup(field.value)">

  `,

  styles: [ `

    .rangle-text-field {

      border-radius: 3px;

      border: 1px solid #ccc;

      box-sizing: border-box;

      display: inline-block;

      font-size: inherit;

      font-weight: inherit;

      height: 2.5rem;

      padding: .5rem;

    }

  `]

})

export class RangleTextField {

  @Input() private placeholder: string;

  @Input() private value: String;

  @Output() private valueChange = new EventEmitter<String>();


  handleKeyup(fieldValue: string): void {

    this.valueChange.emit(fieldValue);

  }

}


예) 부모 컨테이너에서 사용할 때 searchTerm은 부모 컨테이너 컴포넌트의 속성이다. value를 내려보내기도 하고, 이벤트를 받기도 한다. 

      <rangle-text-field 

        placeholder="Enter Keyword"

        [(value)]="searchTerm">

      </rangle-text-field>


버튼을 만들어 보자. 템플릿의 target=source 에서 target이 ()로 감싸지면 이벤트를 나타내고 source는 statement "문"을 쓴다. expression과 statement에 대한 이해는 Angular.io의 Template Syntax 설명을 참조한다.  여기서는 클릭이 되면 바로 부모의 handleSearch가 호출된다. 

import { Component, Input, Output, EventEmitter } from 'angular2/core'


@Component({

  selector: 'rangle-button',

  template: `

    <button

      [ngClass]="dynamicStyles()"

      class="rangle-button"

      (click)="onClick.emit()">

      {{ name }}

    </button>

  `,

  styles: [ `

    :host {

      display: flex;

      align-items: stretch;

    }

    .rangle-button {

      border: none;

      border-radius: 3px;

      color: white;

      font-weight: bold;

      letter-spacing: .2em;

      margin-left: 0.5rem;

      padding: 0.5rem;

      text-transform: uppercase;

    }

    .primary {

      background: #E5373A;

    }

    .normal {

      background: #422D3F;

    }

  `]

})

export class RangleButton {

  @Input() name: string;

  @Input() isPrimary: boolean;

  @Output() onClick = new EventEmitter();


  dynamicStyles() {

    return this.isPrimary ? 'primary' : 'normal';

  }

}


예) 부모 컨테이너 컴포넌트에서 사용할 때, handleSearch는 부모 컨테이너 컴포넌트의 메소드이다. 

      <rangle-button name="Search"

           [isPrimary]="true"

           (click)="handleSearch(searchTerm)">

      </rangle-button>


다음으로 Input 과 Button 컴포넌트를 재사용할 수 있는 컨테이너를 만들어 본다. Label만을 포함하고 있고 언제는 원하는 형태로 Input과 Button을 템플릿상에 포함 시킬 수 있다. 이를 위해 ng-content 태그를 사용한다. Angular v1의 transclude와 유사하다. 하지만 큰 차이점은 역시 Angular2의 컴포넌트 아키텍쳐는 Comopnent Tree이기 때문에 Change Detection(CD)는 항시 가장 위에서 아래로 점검되고 렌더링된다. Angular v1은 parent - child 관계가 있다하더라도 Parent가 먼저 렌더링 될지 Child가 먼저될지 보장을 못했다. (물론 Directive에 priority 옵션이 있지만 적용하기에 따라 순서가 정확한지 알 수 없었다.) 즉, Angular2에서 ng-content 태그를 사용해도 렌더링에 예측이 가능하다. 





import { Component, Input } from 'angular2/core';

import { RangleLabel } from './rangle-label';


@Component({

  selector: 'rangle-bar',

  directives: [ RangleLabel ],

  template: `

    <rangle-label [name]="name">

    </rangle-label>

    <div class="row">

      <ng-content></ng-content>

    </div>

  `,

  styles: [`

    :host {

      background: #F8F8F8;

      border: solid #ccc 1px;

      display: block;

      padding: 1rem;

      margin: 1rem;

    }

    .row {

      display: flex;

      margin-top: 0.5rem;

    }

  `]

})

export class RangleBar {

  @Input() name: string;

}


예) 부모 컨테이너 컴포넌트 사용 때 

    input과 button이 있는 경우 
    <rangle-bar name="Search the site">
        <rangle-text-field placeholder="Enter Keyword"
             [(value)]="searchTerm">
        </rangle-text-field>
        <rangle-button name="Search"
            [isPrimary]="true"
            (click)="handleSearch(searchTerm)">
        </rangle-button>
    </rangle-bar>

    또는 
   
   button만 있는 경우 
   <rangle-bar name="Other Stuff">
        <rangle-button name="Button 1"
            [isPrimary]="true">
        </rangle-button>
        <rangle-button name="Button 2"></rangle-button>
        <rangle-button name="Button 3"></rangle-button>
    </rangle-bar>


당연히 <ng-content>에는 input, button 컴포넌트외에 다양한 컴포넌트가 올 수 있다. 결과는 다음과 같다. 



실데이터를 요청하는 코드는 RangleBar 에서 XHR에 대한 요청을 하고 결과를 전달하는 과정을 거치면 될 된다. Redux를 사용한다면 Action Creator를 수행한다. 애플리케이션 소스를 보자. App 컴포넌트는 가장 최상위 애플리케이션으로 Container 컴포넌트와 Presentational 컴포넌트를 사용해 화면 레이아웃을 만들고 있다. 

import { ViewEncapsulation, Component } from 'angular2/core';

import { RangleBar }  from './rangle-bar';

import { RangleButton } from './rangle-button';

import { RangleTextField } from './rangle-text-field';


@Component({

  selector: 'app',

  directives: [ RangleBar, RangleTextField, RangleButton ],

  template: `

    <rangle-bar name="Search the site">

      <rangle-text-field placeholder="Enter Keyword"

        [(value)]="searchTerm">

      </rangle-text-field>

      <rangle-button name="Search"

        [isPrimary]="true"

        (click)="handleSearch(searchTerm)">

      </rangle-button>

    </rangle-bar>


    <rangle-bar name="Other Stuff">

      <rangle-button name="Button 1"

        [isPrimary]="true">

      </rangle-button>

      <rangle-button name="Button 2"></rangle-button>

      <rangle-button name="Button 3"></rangle-button>

    </rangle-bar>

    <p> inside viewCapsulation </p>

  `,

  styles: [ 'p { background: red; border: dashed yellow 2px; }' ],

  encapsulation: ViewEncapsulation.Native

})

export class App {

  private searchTerm: string;


  handleSearch(searchTerm: string): void {

    alert(`You searched for '${searchTerm}'`);

  }

}


GistRun의 소스와 Rangle.io 동영상을 참조하자.

  - Container & Presentational Component 개발 방식에 대한 소개 

  - Component LifeCycle에 대한 소개: ng-content를 특별히 View Content라 하고, 그외 템플릿에 직접 설정된 컴포넌트를 View Child라 한다. Life Cycle에서는 둘의 시점이 나뉘어져 있다. View Content가 먼저 호출된다.  

  - ElementRef 주입받아 컴포넌트의 DOM을 사용할 수 있다.





<참조>

  

  - Angular2 Component Basic (pptsrc)- Rangle.io

  - Thinking in React

  - Presentational and Container Component - Dan Abramov

  - Rangle.io's Angular2 GitBook 

  - Change Detection Reinvented - Victor Savkin's

신고
posted by peter yun 윤영식
2016.04.18 20:46 Angular/Electron & Ionic

Angular2-seed에 Desktop 애플리케이션을 만들수 있는 Electron 기술과 네이티브 모바일 앱을 만들 수있는 NativeScript을 붙여서 확장한 Seed가 Angular2-seed-advanced 이다. Advanced 시드의 내용을 보면 Typescript의 장점을 살려서 OOP 방식으로 Build 환경과 코드를 재사용하면서 Web, Desktop, Native Mobile App을 만들 수 있는 환경을 제공한다. Advanced를 참조해서 Angular2-seed를 기반으로 Hybrid Mobile App을 만들 수 있는 Ionic2 프레임워크를 합쳐서  Web, Hybrid Mobile App용 Seed를 만들고자 한다. 기존에 나와 있는 ionic2-seed도 참조해 App 개발을 위한 SDK도 포함하는 Advanced 버전을 만든다. 





1. Action Plan


어떤 작업을 해야할지 마인드 맵으로 그려보았다. 먼저 Angular2의 Seed 소스를 분석하고 Ionic2의 새로운 개념을 알아본다. 그리고 Angular2 Code Style 과 Sass 스타일을 정할 예정이다. 분석이 끝나면 Gulp 기반으로 Web과 Mobile을 위한 빌드 환경을 만든다. 그리고 Web, Mobile 개발 환경의 폴더 구조를 만든다. Ionic2는 별도의 Ionic CLI(Command Line Interface)를 제공하기 때문에 기존 Angular2-seed와 어떻게 합칠지가 관건이다. 다음으로 Framework을 Hierarchical Layer로  SDK, Biz Context, Common 로 구성할 것이다. Angular2는 ES2015와 TypeScript 둘다 지원하고 ES2015의 Module System 문법을 사용할 수 있다. 즉, 컴포넌트를 모듈단위로 나누고 역할에 따라 계층을 나누어 Framework을 구성한다. 


환경과 프레임워크가 준비되면 샘플 프로토타입핑을 통해 Angular2-seed-ionic2가 잘 도는지 확인을 하고 일반적인 화면 예제를(MVP) 만들어 다양한 예를 통해 가이드 한다.  


필요 가이드 문서는 다음과 같다. 


  - Code Style Guide

  - Environment Guide

  - Framework Guide 

  - 10-Minutes Starting Guide (with prototyping)

  - Several Sample Guide (with MVP)






2. Project seed contents


환경과 프레임워크에 대한 자세한 내용이다. 환경은 Web과 Hybrid Mobile App을 위한 빌드와 테스트 환경을 갖추는게 목표이다. 그리고 프레임워크에는 계층형 레이어를 통해 필요한 요소를 Angular2 Components로 만들어 놓을 것이다. 일단 다음과 같은 과정을 거치면 좋을 것 같다. 


  - Step 1 : Angular2-seed-ionic2를 통해 Environment와 Framework을 최초에 만든다. 

  - Step 2 : Anguar-CLI를 통해 Angular2 Component 코드를 자동생성한다. (옵션)



대략 한달간의 기간으로 바로 Prototyping이 가능한 수준의 seed를 만들어 보고자 한다. 잘 되야 할텐데...





참조


- X-Mind (free version) 파일 

Front-end Next Generation Stack.xmind

- Angular CLI 

- Angular2 Master Starter Kit

- Ionic2 Seed (*)

- Angular2 Eduction 목록

- Angular2 Awesome 목록

- Angular2 Style Guide - Rangle.io

- Angular2 Code Style Guide - Mgechev

신고
posted by peter yun 윤영식
2016.04.16 22:27 Angular/Concept

ExtJSAngular2의 기술적 차이점에 대한 질문을 받게되어 어떻게 대답을 하면 될까? 를 고민하다 정리를 해본다. ExtJS 는 일부 기능은 무료 오픈소스이지만 전체 기능을 사용하기 위해서는 유료이다. 현재 v6 까지 나와있다. 또한 최신버전은 Sencha라는 이름으로 통합되어 나오고, 현재 ExtJS v6 버전을 다운로드 받아 사용할 수도 있다. 따라서 ExtJS나 Sencha 에서 이야기하는 장점이라 말하는 부분을 토대로 Angular v2와 비교해 보기로 한다. 참조는 Top 8 Reasons Why Enterprises Prefer Sencha ExtJS over Angular 문서를 보고 ExtJS 쪽은 영문 요약을 간단히 했고, Angular 쪽은 영어로 작성해 본다.


This document is to write differences between ExtJS v6  framework and Angular v2 framework. I reference the ExtJS site (Top 8 Reasons Why Enterprises Prefer Sencha ExtJS over Angular) and I write my opinion about Angular.

*caution: AngularJS is Angular v1, Angular v2 or Angular is Angular v2. ExtJS is ExtJS v6.





Reason #1 Components, Components, Components


 - ExtJS

   + ExtJS contains components that are optimized for both desktop and mobile devices.

   + customize the theme. mobile apps can be themed to achieve an iOS, Android,  BlackBerry the look and feel.

   + Angular does not come with a component library.


 - Angular v2

  + Angular can easily make components that are usable anytime. If we want to use components i.e grids and charts, we can use the components that have already been made by many companies and open source developers.

  + Developer can build an application with components  and can also use the current web technology such as Web Components together.

  + Angular component architecture is a component-tree for improving performance to render web page views. This architecture is implemented in the ReactJS Framework from Facebook as well.

  + Angular can easily merge with the more popular CSS Framework such as Twitter Bootstrap, Material Design, Semantic UI and Foundation which can be customized to match with any company brand. One of these can be chosen for any brand style.

  + Angular is a platform for the web and mobile. Ionic v2 is a Hybrid mobile app framework. It is based on Angular v2 and has tools that can rapidly develop an app. So, the hybrid app can be developed using Angular in a friendly code style. The hybrid app gives customers a better UX(User eXperience) than mobile browsing.

  + Ionic v2 using Angular has lots of mobile components to support the look and feel for different mobile platforms such as iOS, Android and BlackBerry. We can also customize these styles.





Reason #2 ExtJS Has Ben Battle-Tested Since 2007


 - ExtJS

   + It is launched in 2007.

   + It has long-term success.

   + migration v5 to v6 smoothly


 - Angular v2

   + Angular v1 was launched on October 20, 2010 by Google.

   + Angular v1 also has had long-term success i.e NBC, Intel, ABC News and approximately 8,400 other sites out of 1 million tested in July 2015.

   + Angular v2 already has an upgrade tool to migrate v1 to v2.






Reason #3 A Robust Framework for Building Apps vs Building One's Own Framework


 - ExtJS

   + ExtJS has MVC(Model View Controller)/MVVM (Model View ViewModel) architecture.

   + ExtJS comes with an extensive component library.


 - Angular v2

   + Angular has MVC/MVVM as well.

   + Angular is easily customized for any application's architecture. It can be applied to the one-way data flow architecture like Flux from Facebook in order to easily manage any application's state.

   + Angular is an open source framework so, many developers and companies open their components like grids or charts that have been used within their applications. If there are  components with any issues, many contributors can solve the problems in the open system and components can easily be downloaded from consistency tools such as NPM (Node Package Manager) or Bower.

   + Angular v2 is a new platform for the modern web environment including Web Components, Reactive Programming and Immutable state to solve some issues which Angular v1 experienced. Applications can be developed to be more stable with better performance using these concepts.





Reason #4 Clearly-Defined Legacy Browser Compatibility By Default


 - ExtJS

   + support Internet Explorer (IE) 8


 - Angular v2

   + Angular can support IE 9 (link).

   + If IE 8 is to be supported, poly-fill can be used to support the lower version but MicroSoft has already stopped updating IE 8 securities. So, this version is not recommended because of the danger of hacking.





Reason #5 Integrated Tools Created With a Clear Vision and Purpose


 - ExtJS

   + Senchar has several tools including Senchar Cmd, IDE Plugins, and Senchar Inspector that developers can use to speed up their application development.


 - Angular v2

   + Angular uses the Gulp or Grunt tasker that can extend plugins which are required.

   + There are IDE Plugins (WebStorm, Atom, Sublime Text, MS Code) for Angular  and developers can check out an application's performance with Angular Inspector under the  chrome extension as well.

   + Angular uses the NPM (Node Package Manager) or Bower that all front-end developers use to manage the packages and components for any application.

   + Angular is oriented to use common tooling set in current front-end environments. So, the developer can use these tools in a friendlier way.





Reason #6 No Need to Learn TypeScript or other tools and component libraries with ExtJS6


 - ExtJS

   + ExtJS 6 is a JavaScript Framework.

   + Developer doesn't need to have experience with object-oriented programming (OOP)


 - Angular v2

   + Javascript before ECMAScript 5 doesn't support the OOP but ECMAScript 6 (ES2015) can support it and the developer can simply establish more complex applications with the OOP methodology.

   + TypeScript is a superset of ES2015 and has more features simply to code any sophisticated application such as Decorator. Typescript also includes a feature that transpiles code to ES5 or ES3 code, so it can be released into the production environment without worry.

   + TypeScript can support the Static Type Checking System during the development period. TypeScript can automatically suggest some problems in code so, the developer can recover the mistakes which occured in run-time.

   + If the developer has experience with Java or OOP, they can easily learn TypeScript. It is not a weakness, but rather a strength.





Reason #7 Excellent Design Tools


 - ExtJS

   + Sencha has an excellent set of design tools.

   + Sencha Architect is a perfect tool to create clickable prototypes.

   + ExtJS Stencils can be used to create wireframes and mockups that correctly reflect the look and feel of components


 - Angular v2

   + Angular doesn't have a design tool because it is optional. If it is needed, other professional design or mockup tools can be used .

   + Ionic v2 based on Angular v2 also has  a design tool such as Ionic Creator for mobile and has a testable native app in several mobile platforms (iOS, Android) such as Ionic Viewer.

   + The Angular team has been working closely together with the Ionic team.





Reason #8 Awesome Support and Training Options From the Creators of the Framework


 - ExtJS

   + Sencha has excellent support and training teams.

   + Sencha has a professional services team that can assist with a variety of enterprise development needs.


 - Angular v2

   + There are several professional Angular training companies such as Rangle.io and Egghead.io.

   + Global conferences are always held several times annually such as NG-Conf.

   + If developers want to learn Angular, they can easily get a huge range of materials like books, videos and online courses.

   + The Angular team at Google consists of 20 full-time developers and there are over 221 contributors for Angular v2.

   + It is not a level playing field when comparing ExtJS with Angular in my opinion.



ExtJS is a good framework and has a full set of tools for web development but it can restrict customization needs, because developers can just play around with ExtJS's environment and architecture. The current web trend - software and hardware - has been changing quickly so if some applications need to be adapted into the current environment, Angular v2 would be an excellent choice. Angular v2 can provide any company lots of opportunities to improve the UX and performance for applications but more time needs to be invested learning this framework. 


기회가 된다면 다음부터는 영어 블로그도 써볼 생각이다. 그리고 영어 Angular 관련 책을 출판해 보고 싶다. O'Reilly 또는 Packt Publishing 같은 곳과 연이 된다면... 그전에 열심히 영문 블로깅하면서 영작 실력을 키워야겠지만... 비오는 토요일 밤에...




Reference

  - Top 8 Reasons Why Enterprises Prefer Sencha Ext JS over Angular

신고
posted by peter yun 윤영식
2016.04.13 13:31 Angular/Electron & Ionic

Ionic 프레임워크는 하이브리드 웹앱을 만들기 위한 프레임워크이다. 웹앱이기에 웹을 위한 프레임워크로 Angular를 사용하고 있다. Ionic v1에서는 Angular v1Ionic v2에서는 Angular v2를 사용하고 있다. 







Ionic은 하이브리드 웹앱 프레임워크외에 다양한 서비스를 제공하고 있다. Ionic 플랫폼 상에서 화면을 디자인하고 배포하고 테스트할 수 있는 서비스들을 제공한다. Ionic.io 는 Ionic 프레임워크를 이용해 만든 앱을 배포 관리하는 곳이다. Ionic Creator는 화면을 디자인하는 서비스이며, Ionic View는 앱을 앱스토어에 배포하거나 로컬에 USB로 설치하지 않고 Ionic View앱을 설치하면 Ionic 프레임워크로 만든 모든 앱들을 iOS 또는 Android 상에서 바로 볼 수 있는 서비스이다. 


위의 그림 Ionic는 Cordova를 기반으로 하고 CLI(Command Line Interface)를 제공하며 CLI는 두가지를 통합하고 있다. 첫째는 Cordova의 plugin에 대한 install/uninstall을 위한 명령이고 두번째는 Gulp를 이용해서 웹파일(Sass, Html, Scripts)을 위한 명령 Task에 대해 Gulp의 환경파일인 gulpfile.js에 정의하고 있다. Vinyl은 Gulp에서 사용되는 모듈로 다양한 OS에 상관없이 File Stream을 지원하기 위한것으로 개념은 링크를 참조한다. Ionic은 iOS와 Android Native UI에 가깝게 Angular 기반으로 컴포넌트를 제공하고 있다. 즉, Angular의 Directive(지시자, 컴포넌트)를 이용해 화면을 만드는 방식이다. 또한 Cordova의 기능을 Angular의 서비스로 사용하기 위해 ngCordova도 제공하고 있다. 


Ionic을 사용하기 위해서는 다음과 같은 사전지식이 필요하다. 

  - Node.js 그리고 NPM 사용법

  - 단순 테스트 목적이 아니라면 반드시 Angular 프레임워크 사용경험이 필요하다  

  - XCode 또는 Android Studio에서 간혹 Cordova의 Plugin을 수정할 때도 있다. 

  - 기본 XCode, Android Studio 사용법은 알아두는게 좋다. 직접 툴에서 빌드할 경우가 많다. 


현재 Ionic v2는 Angular v2를 기반으로 하고 아직 beta 버전이다 (2016.4.12).  2016년 상반기에 Angular v2 정식버전이 나오면 비슷한 시기에 정식버전이 나오리라 기대해 본다. Angular v2가 정식버전하에 하반기부터 본격적으로 쓰일 것으로 보이기때문에 Ionic도 v2를 사용하고 준비하면 좋을 것같다. Ionic v2를 사용하기 위해서는 따라서 다음과 같은 기초 지식도 필요하다. 

  - ES2015 JavaScript 스팩 상의 문법 추가 사항을 숙지해야 한다. OOP 스타일의 Syntax로 바뀌었다고 보면 된다.  

  - Angular v2 가이드문서 (반드시 TypeScript 기반으로 참조)를 최소 한번쯤은 보도록 한다. 

  - TypeScript 기반 Cordova 서비스를 통해 Native 접근이 필요할 것이다.  


알아야 할 것은 많지만 일단 설치부터 실행까지 보도록 한다. Ionic v2 CLI 명령어Cordova 공식 CLI 명령어도 한번 훑어보는게 좋다. Cordova는 현재 6.* 버전이 최신이다.





1. 설치하기 


먼저 Node.js를 설치한다. NPM(Node Package Manager)를 통해 Cordova와 ionic 프레임워크를 설치하고 Gulp 수행의 기반을 제공한다. 

  - typescript 컴파일러 설치

  - typings 는 typescript의 definition 파일을 관리하는 메니저이다. typings 폴더에서 관리된다. 

  - cordova는 iOS와 Android Native 접근을 위한 Gateway library라고 보면 된다. 

> sudo npm install -g   typings  typescript  cordova ionic@beta



다음으로 ionic에서 제공하는 샘플 파일을 자동 다운로드 받아 설치한다. ionic v2 에 typescript 버전으로 설치하는 명령어이다. 자동으로 typings폴더에 typescript definition 파일 및 package.json에 정의된 모듈도 node_modules 밑으로 자동 설치된다.

프로젝트생성) ionic start <projectName> <templateType> --v2 --ts

예) ionic start myFirst blank --v2 --ts


blank 템플릿 타입을 주면 https://github.com/driftyco/ionic2-starter-blank/archive/typescript.zip 에서 typescript 버전을 자동 다운로드해 설치한다.

templateType에는 tutorial, tabs, sidemenu, blank가 있다. 설치를 하면 다음과 같은 안내글이 나온다. 


Make sure to cd into your new app directory:

  cd ionic2-tutorial-github


To run your app in the browser (great for initial development):

  ionic serve


To run on iOS:

  ionic run ios


To run on Android:

  ionic run android


To test your app on a device easily, try Ionic View:

  http://view.ionic.io


New! Add push notifications, update your app remotely, and package iOS and Android apps with the Ionic Platform!

  https://apps.ionic.io/signup


New to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started


설명대로 ionic2-tutorial-github 폴더로 이동후 ioinic serve 명령을 수행하면 Gulp의 serve 태스커가 수행된다. 수행 결과로 ionic $ 명령콘솔이 활성화 된다.

> ionic serve 

WARN: ionic.config.js has been deprecated, you can remove it.

Running live reload server: http://localhost:35729

Watching: www/**/*, !www/lib/**/*

√ Running dev server:  http://localhost:8100

Ionic server commands, enter:

  restart or r to restart the client app from the root

  goto or g and a url to have the app navigate to the given url

  consolelogs or c to enable/disable console log output

  serverlogs or s to enable/disable server log output

  quit or q to shutdown the server and exit


ionic $





2. ionic 폴더 구조


ionic의 폴더구조 및 환경설정 파일은 다음과 같다. 


 app

 개발자가 작성하는 모든 애플리케이션 코드가 위치한다. 

 hooks (cordova)

 Cordova 빌드과정의 일부로서 동작될 수 있는 스크립트를 포함하고 있다. 앱을 패키지 할때 필요하다면 언제든 커스터마이징 할 수 있다.  

 node_modules

 npm을 통해 설치된 모듈들이 있다.

 platforms (cordova)

 ionic platform 으로 ios, android를 설치하면 하위 폴더로 생기고, XCode 또는 Android Studio에서 import할 수 있다.

 plugins (cordova)

 ionic platform 선택시 Cordova의 플러그인이 설치되는 폴더이다. 

 resources 

 앱을 위한 icon과 splash image를 해상도가 틀린 모바일 기기별로 놓는 곳이다.  

 typings

 Typescript로 쓰여지지 않는 JS 라이브러리의 타입정의를 한 type definition 파일이 있다.  

 www (cordova)

 index.html를 포함한다. 이곳은 빌드될 때 사용되는 것으로 애플리케이션 코드가 위치하는 곳이 아님을 주의하자.  "ionic build" 를 하면 "cordova build"가 수행되어 www 해당 디렉토리에 app의 코드가 위치하고 다시 platforms/ios 또는 android의 www 폴더에 copy된다. 따라서 최종 사용되는 파일은 platforms/ios (또는 android) /www/* 에 위치한다. 

 config.xml (cordova)

 앱 패키지를 만들때 사용하기 위해 Cordova의 환경설정이 존재한다. 

 ionic.config.js

 not used로 앞으로 없어질 것이다. ionic.config.json 파일은 버전 정보만 전달

 package.json

 npm 으로 설치되는 모든 모듈에 대한 설정 

tsconfig.json / typings.json

 TypeScript 환경 설정 / type definition file 환경 설정


크게 "환경파일", "Cordova", "애플리케이션" 부분으로 나뉠 수 있다. 최초 템플릿이 생성된 이후 개발자는 "애플리케이션"폴더인 "app"를 사용하면 된다. blank타입으로 만들었을 때 platforms 폴더에는 ios 플랫폼이 기본 설치된다. 







3. TypeScript 기반 개발


최근(2016.4.8) Beta버전에 CLI 를 통해 TypeScript 기반의 Angular v2 페이지와 서비스를 만들 수 있는 명령어를 공개했다. 파일이름은 kebob-case로 my-page와 같이 되고, ES6/TypeScript기반의 class는 PascalCase로 MyData로 처럼 이름을 준다. 

> ionic generate ( 또는 축약해서 g ) <page 또는 provider> <Name>


예)

> ionic g page myPage

create app/pages/my-page/my-page.html (.js, .css)


> ionic g provider MyData

create app/providers/my-data/my-data.js 


생성된 my-page.ts 소스는 다음과 같다. @Page는 TypeScript에서 제공하는 Decorator를 이용해 Class Decorator를 Ionic을 위해 만든 것이다. 

  - TypeScript의 Decorator에 대해 자세히 알고 싶다면 링크를 참조한다. 

import {Page, NavController} from 'ionic-angular';


/*

  Generated class for the MyPagePage page.


  See http://ionicframework.com/docs/v2/components/#navigation for more info on

  Ionic pages and navigation.

*/

@Page({

  templateUrl: 'build/pages/my-page/my-page.html',

})

export class MyPagePage {

  constructor(public nav: NavController) {

    this.nav = nav;

  }

}


.ts가 .js코드로 컴파일된 내역을 보고 싶다면 app 폴더로 이동해서 TypeScript 컴파일 명령어인 "tsc"를 수행한다. @Page는 __decorate({ ... }) 안에서 "ionic-angular"의 decorators/page.js 의 Class Decorator가 수행되는 것이다. ionic_angular_1.Page( <config object> ); 결국, @Page는 구현한 decorator 펑션을 호출해 주는 역할을 수행할 뿐이다. TypeScript Decorator 공식문서를 통해 개념을 이해하자. @Page는 Angular v2에서 제공하는 데코레이터가 아니라 Ionic에서 제공하는 것이다. 내부 소스를 보면 @Page 클래스는 ion-page selector를 사용하고 결국 Angular v2 core에 있는 @Component를 호출하고 있을 뿐이다. 즉, 굳이 @Page를 쓰지 않아되 되는 Decorating이다. 

// app/pages/my-page/my-page.js TypeScript 컴파일된 소스


"use strict";

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {

    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;

    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);

    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;

    return c > 3 && r && Object.defineProperty(target, key, r), r;

};

var __metadata = (this && this.__metadata) || function (k, v) {

    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);

};

var ionic_angular_1 = require('ionic-angular');

/*

  Generated class for the MyPagePage page.


  See http://ionicframework.com/docs/v2/components/#navigation for more info on

  Ionic pages and navigation.

*/

// 자동으로 postfix로 Page가 붙는다. 

var MyPagePage = (function () {

    function MyPagePage(nav) {

        this.nav = nav;

    }

    MyPagePage = __decorate([

        ionic_angular_1.Page({

            templateUrl: 'build/pages/my-page/my-page.html',

        }), 

        __metadata('design:paramtypes', [ionic_angular_1.NavController])

    ], MyPagePage);

    return MyPagePage;

}());

exports.MyPagePage = MyPagePage;



// node_modules/ionic-angular/decorators/page.js 데코레이터 구현 소스


function Page(config) {

    return function (cls) {

        // @Page 의 기본 selector

        config.selector = 'ion-page';  

        config.directives = config.directives ? config.directives.concat(directives_1.IONIC_DIRECTIVES) : directives_1.IONIC_DIRECTIVES;

        config.host = config.host || {};

        config.host['[hidden]'] = '_hidden';

        config.host['[class.tab-subpage]'] = '_tabSubPage';

        var annotations = _reflect.getMetadata('annotations', cls) || [];

        // 기본 설정후 Angular v2 core의 @Component를 호출한다. 

        annotations.push(new core_1.Component(config)); 

        _reflect.defineMetadata('annotations', annotations, cls);

        return cls;

    };

}

exports.Page = Page;


provider로 만든 것은 ES2015의 Promise, RxJS, @Injectable이 포함되어 있다. Angular v2에서는 모든 Inject되는 Service Class에는 Class Decorator로 @Injectable를 설정토록 권고한다. 

// app/providers/my-data/my-data.ts 소스 


import {Injectable} from 'angular2/core';

import {Http} from 'angular2/http';

import 'rxjs/add/operator/map';


/*

  Generated class for the MyData provider.


  See https://angular.io/docs/ts/latest/guide/dependency-injection.html

  for more info on providers and Angular 2 DI.

*/

@Injectable()

export class MyData {

  data: any = null;


  constructor(public http: Http) {}


  load() {

    if (this.data) {

      // already loaded data

      return Promise.resolve(this.data);

    }


    // don't have the data yet

    return new Promise(resolve => {

      // We're using Angular Http provider to request the data,

      // then on the response it'll map the JSON data to a parsed JS object.

      // Next we process the data and resolve the promise with the new data.

      this.http.get('path/to/data.json')

        .map(res => res.json())

        .subscribe(data => {

          // we've got back the raw data, now generate the core schedule data

          // and save the data for later reference

          this.data = data;

          resolve(this.data);

        });

    });

  }

}





5. ionic 페이지 만들기 


일단, ionic generate를 통해 생성된 page, provider (서비스)는 참조만 한다. 다음과 같이 서비스를 파일을 만든다. Angular2 http 모듈이 RXJS를 사용하고 있고 return 오브젝트가 Promise가 아니라 Observable이다. RXJS는 Reactive Programming의 자바스크립트 구현체로 링크를 참조한다.

import { Injectable } from 'angular2/core';

import { Http, Headers } from 'angular2/http';


@Injectable()

export class GitHubService {

    constructor(private http: Http) {}


    getRepos(username) {

        let repos = this.http.get(`https://api.github.com/users/${username}/repos`);

        return repos;

    }

}


다음으로 /app/pages/home/home.ts를 수정한다. @Component 대신에 @Page를 사용하고 providers로 GitHubService를 정의한다. 

import {Page} from 'ionic-angular';

import {GitHubService} from '../../providers/github.service';


@Page({

  templateUrl: 'build/pages/home/home.html',

  providers: [GitHubService]

})

export class HomePage {

  public foundRepositories;

  public username;

  constructor(private github: GitHubService) {}

  getRepos() {

    this.github.getRepos(this.username)

      .subscribe(

          data => {

              this.foundRepositories = data.json();

          },

          err => console.error(err),

          () => console.log('getRepos completed')

      );

  }

}


home.html도 다음과 같이 재정의한다. 

<ion-navbar *navbar>

<ion-title>

Home

</ion-title>

</ion-navbar>


<ion-content class="home">

<ion-list inset>

<ion-item>

<ion-label>Username</ion-label>

<ion-input [(ngModel)]="username" type="text"></ion-input>

</ion-item>

</ion-list>

<div padding>

<button block (click)="getRepos()">Search</button>

</div>

<ion-card *ngFor="#repo of foundRepositories">

<ion-card-header>

{{ repo.name }}

</ion-card-header>

<ion-card-content>

{{ repo.git_url }}

</ion-card-content>

</ion-card>

</ion-content>


ionic CLI 명령을 통해 테스트를 해보자. serve 명령을 이용하면 로컬 서버를 띄워서 브라우져상에서 테스트를 해볼 수 있다. 옵션으로 --lab을 주면 ios, android 둘 다 테스트가 가능하다. 

> ionic serve --lab 



ionic의 ion-* 시작하는 Angular component를 사용하면 mobile 플랫폼에 따라 Look and feel이 자동으로 맞춰진다. 이를 원하지 않을 때는 최소한의 ion-* 태그만을 사용하면 된다. ion-navbar 와 ion-content 태그만을 사용하고 나머지는 서비스에 맞게 HTML을 작성해도 무방하다.





6. ionic Navigation 추가하기 


네비게이션을 위해 메인 컴포넌트인 app.ts를 살펴보면, ionic에서 제공하는 @App 데코레이터를 통해서 HomePage 를 최상위 페이지로 설정하고 있다.

import 'es6-shim';

import {App, Platform} from 'ionic-angular';

import {StatusBar} from 'ionic-native';

import {HomePage} from './pages/home/home';


@App({

  template: '<ion-nav [root]="rootPage"></ion-nav>',

  config: {} // http://ionicframework.com/docs/v2/api/config/Config/

})

export class MyApp {

  rootPage: any = HomePage;


  constructor(platform: Platform) {

    platform.ready().then(() => {

      // Cordova 준비

      StatusBar.styleDefault();

    });

  }

}


GitHub 저장소 목록에서 상세페이지 이동을 위한 detail 페이지를 만들고, 일단 home.html 과 .ts 파일을 수정한다.

// home.html 에서 click 이벤트를 추가한다. 

<ion-card *ngFor="#repo of foundRepositories" (click)="goDetail(repo)">


// home.ts 에 Navigation을 위한 controller와 추가 메소드를 정의한다. 

import {Page, NavController} from 'ionic-angular';

import {GitHubService} from '../../providers/github.service';

import {DetailPage} from '../detail/detail'


@Page({

  templateUrl: 'build/pages/home/home.html',

  providers: [GitHubService]

})

export class HomePage {

  public foundRepositories;

  public username;

  constructor(private github: GitHubService, private nav: NavController) {}

  getRepos() {

    this.github.getRepos(this.username)

      .subscribe(

          data => {

              this.foundRepositories = data.json();

          },

          err => console.error(err),

          () => console.log('getRepos completed')

      );

  }

  goDetail(repo) {

      // 페이지를 Stack에 추가하는 것이다. 

      this.nav.push(DetailPage, { repo: repo });

  }

}


home.html 상단의 ion-navbar 태그는 pagenavigation하면 <Back 버튼이 자동으로 생긴다. 즉, <Back 버튼을 클릭하는 순간 이동한 페이지를 pop 하는 것이 자동으로 이루어 지기때문에 명시적으로 해줄 필요가 없다. 다음으로 저장소의 README 파일 내역을 HTML 포멧으로 가져오는 메소드를 GitHubService에 추가한다. 

// github.service.ts


getDetail(repo) {

        let headers = new Headers();

        headers.append('Accept', 'application/vnd.github.VERSION.html');

        return this.http.get(`${repo.url}/readme`, {headers: headers});

}


상세내역을 detail.ts를 통해 상세정보를 가져오고 다시 detail.html에 뿌려준다. NavParams를 통해 전달받은 repo 객체의 url로 README 값을 받아온다. detail.html에서는 아래 소스처럼 {{ readme }} 표현식을 사용하면 HTML 태그가 text로 뿌려질 뿐이다. 따라서 innerHTML 속성을 이용한다. 

// detail.ts 


import {Page, NavController, NavParams} from 'ionic-angular';

import {GitHubService} from '../../providers/github.service';


@Page({

  templateUrl: 'build/pages/detail/detail.html',

  providers: [GitHubService]

})

export class DetailPage {

  public readme = '';

  public repo;

  constructor(private nav: NavController, private github: GitHubService, private navParams: NavParams) {

      this.repo = navParams.get('repo');

      this.github.getDetail(this.repo)

        .subscribe(

            data => this.readme = data.text(),

            err => {

                if(err.status == 404) {

                    this.readme = 'This repo does not have a README file';

                } else {

                    console.error(err);

                }

            },

            () => console.log('getDetail completed')

        );

  }

}



// detail.html 

<ion-navbar *navbar>

  <ion-title>{{ repo.name }}</ion-title>

</ion-navbar>


<ion-content padding class="detail">

  <div padding>{{ readme }}</div>

</ion-content>



detail.html을 text가 아니 DOM으로 넣기 위해 innerHTML속성으로 변경하고 최종 테스트 한다. 파일을 변경하면 ionic은 파일을 watch하고 있다가 자동으로 refresh 해준다. (ionic serve 경우)

<ion-content padding class="detail">

  <div padding [innerHTML]="readme"></div>

  <!-- <div padding>{{ readme }}</div> -->

</ion-content>


ios와 Android 플랫폼에 맞는 "< Back" 버튼이 생성된다. github 사용자 이름을 입력하고 목록이 나오면 목록중의 카드하나를 클릭하면 다음과 같이 Navigation되는 것을 볼 수 있다. ionic은 Tabs 형태 또는 Menus형태의 UI 컨테이너를 통해 Navigation을 Mobile UX에 맞게 변경할 수 있다. 





7. ionic 배포와 테스트 


ionic serve 명령으로 로컬 웹서버를 띄워 로컬 웹 브라우져를 통해 기능을 테스트 했다면 emulate 또는 기기에 App을 배포해서 테스트 한다. 브라우져에서 보는 것과 실 기기에서 테스트하는 것은 하늘과 땅 차이다. 희소식은 XCode 7 부터는 Developer Account(유료)없이도 USB를 통해 iPhone에 App을 배포 테스트할 수 있다. 

// 로컬에서 xcode emulate을 띄워준다. 

> ionic emulate ios


// 연결된 기기에서 수행할 수 있다

> ionic run ios 


MacBook 상에서 ionic run을 위해서는 "npm install -g ios-deploy" 사전에 설치가 필요하다. 



또는 ionic viewer를 모바일 기기에 설치하고 프로젝트를 ionic.io 서비스에 자신의 계정에 upload한 후 작동 여부를 테스트 할 수 있다. 하지만 작동중 디버깅에 대한 부분은 보다 많은 지면을 필요로 하기에 다음에 언급한다. upload시에 로그인 안되어 있으면 등록한 id/pwd를 물어본다. 

> ionic upload 

WARN: No 'upload:before' gulp task found!

If your app requires a build step, you may want to ensure it runs before upload.


Uploading app....

Saved app_id, writing to ionic.io.bundle.min.js...

Successfully uploaded (f3ded32c)


Share your beautiful app with someone:


$ ionic share EMAIL

Saved api_key, writing to ionic.io.bundle.min.js...


최근에는 Xamarin이나 React Native, NativeScript 같은 Native Code로 전환해 주는 프레임워크가 인기를 끌고 있다. 모바일만을 고려하지 않는다면 ionic은 웹표준 기술을 통해 빠른 기능개발과 Angular2기반의 프론트앤드의 서비스와 컴포넌트(만일 Web을 Angular2로 개발한다면)를 공유할 수 있다. 즉, Native Mobile UX/UI 특성과 빌드 환경의 편의성은 Ionic 프레임워크를 사용하면서 웹개발도 도모할 수 있는 Hybrid WebApp + Web 개발이 가능하다 판단된다. 


향후 angular2-seedNativescriptElectron을 접목한 angular2-seed-advanced가 나왔듯이, angular2-seed에 Ionic2를 접목한 seed를 만들 예정이다. 





참조


  - ionic v2 공식 문서

  - Vinyl 개념 이해하기 & Stream Handbook

  - Cordova 공식 홈페이지 문서

  - TypeScript Decorator 만들기

  - 블로깅 참조 

  - 다양한 모바일 기기 테스트 방법들

  - Ionic2 Conference App 소스

  - Ionic.io 의 다양한 서비스들: Push, Deploy, Analytics, User

  - Crosswalk



자료


  - Ionic Advantures

  - Ionic Collection

  - Ionic Resources

  - AppCamp

  - play.ionic.io: playground


     


신고
posted by peter yun 윤영식
2016.04.08 18:54 Angular/ES2015 & TypeScript

Angular2 또는 최신 라이브러리를 읽기 위해서는 ES2015 (ES6)에 대한 기본적인 이해가 필요하다. 모질라의 Depth in ES6 번역글과 S65 페북모임에서 얻은 지식을 한 페이지로 정리해 본다. 






let 과 const


기본 var는 함수에 대한 scope(스코프)만을 갖는다. for, {} 블록에 대한 스코프가 없다. var의 대체제가 let 이다. 

  - let은 블럭을 기준으로 스코프를 결정한다. block-scoped

  - 글로벌 let은 없다. 즉 window.xxx 접근이 안된다. 어디간의 블럭안의 스코프에 속한다. 

  - for (let x...) 루프구문에서 x 변수를 새로 바인딩 한다. 

  - let 변수를 선언 전에 참조하는 것은 에러이다. 

  - let, var 충돌시에는 ES2015 모듈패턴을 사용해서 let으로 전환한다. 


const는 최초 값을 할당한 후 재 할당 할 수 없다. 단, 객체의 속성은 가능하다. 변수 중에 불변의 값을 갖는 것은 모두 const로 할당한다. 재 할당이 안된다는 것 외에는 let과 완전히 동일하므로 애플리케이션 상태 관리의 복잡도를 단순화 시켜줄 수 있다. 




class


이제는 OOP(Object Oriented Programming)를 위한 접근이 훨씬 쉬워졌다. Java의 OOP에 익숙한 개발자라면 import, class, super, extends, implements(TypeScript 지원)등을 통해 이전 Prototypal Inheritance의 이해 없이도 쉽게 JavaScript를 이용한 OOP가 가능해 진것이다

class Parent {

   contructor(params) { ... }

   static XXX() { ... }

   YYY() { ... }

   get ZZZ() { ... }

   set ZZZ(value) { ... }

}




Arrow Function


event 또는 XHR 콜백 펑션을 등록할 때 간혹 내부에 this를 사용하면 Global scope를 참조하게 된다. 이때 var that = this; 한 후 that을 넘겨 사용하기도 하는데 이제는 더 이상 그럴 필요가 없다. 자신 Execution Context를 만들지 않고 (function 보다 가볍다) 화살표 함수를 감싸는 외부 스코프의 this 값을 바로 쓸 수 있는 방법이 => 화살표 함수이다. 

$('.button').click(function (event) {

  ...

});


변경

$('.button').click(event => {

  ...

});


한 줄일 경우는 {}을 사용하지 않고 자동 return 구문이 된다. 만일 여러 줄일 경우는 {} 으로 감싼다. 단, 한 줄로 비어있는 객체를 return하고 싶다면 어떻게 해야할까?

arrs.map( item => ({}) ); // 성공

arrs.map( item => {} ); // bug




iterable 과 for~of 구문


배열을 순회(loop)하기 위한 방법으로 ES5의 myArray.forEach(function (value) { ....});를 사용한다. 기존 for ~ in 루프는 확장 속성들도 순회하기 때문에 성능상 이점이 없다. 배열에 만일 myArray.name = 'dowon'을 넣으면 name도 순회하고 prototype chain도 순회한다. 또는 가끔 무작위 순서로 순회하기도 한다. 쇗!! 이에 대한 해결로 for ~ of가 나왔다. 

for (var value of myArray) {

  console.log(value);  // 값이다. for ~ in 처럼 속성의 key가 아니다

}


Array, Map, Set은 순회를 위한 iterator 메소드를 제공한다. 사용자가 원하는 객체에 iterator 객체를 제공할 수 있다. 

  - 어떤 객체든 myObject[Symbol.iterator]() 메소드를 추가하면 자바스크립트에서 해당 객체를 순회할 수 있다. 

  - Duck Typing의 예로 iterator가 있으므로 해당 객체도 순회가 가능하다는 방식이다. 

  - myObject['iterator'] = function () {...} 라고 했고 만일 iterator() 가 이미 있다면 문제가 될 것이다. 이를 위해 제 7원소 Symbol을 사용해 어떤 코드의 key와도 충돌하지 않게 만든다. 

  - [Symbol.iterator]() 메소드를 제공하는 겍체를 이터러블 객체 (iterable object)라고 한다. 

Iterable is a simple representation of a series of elements that can be iterated over. current state만 있고, Iterator제공하는 메소드 하나를 가진다. 

Iterator is the object with iteration state. hasNext(), next()를 통해 다음 엘러먼트로 이동한다. 


iterator는 next를 제공해야 하므로 Symbol을 이용할 경우 다음과 같이 한다. value를 계속 0 만 주무로 무한루프이다. 

var myIterator = {

  [Symbol.iterator]: function() {

    return this;

  },

  next: function() {

    return { done: false, value: 0 } 

  }

}



형식

for ( VAR of ITERALBE ) {

  STATEMENT

}

동등한 코드 

var $iterator = ITERABLE[Symbol.iterator]();

var $result = $iterator.next();

while (!$result.done) {

  VAR = $result.value;

  STATEMENT

  $result = $iterator.next();

}




Collection 


자바스크립트의 Object는 key-value 쌍의 컬렉션이다. 즉 속성을 추가하고 값을 할당할 수 있고 이런 속성의 집합을 객체라 한다. 일반 객체로 해결할 수 없는 자료구조에서 Map, Set을 도입한다. 

  - 속성 key를 객체로 사용하고 싶을 경우 (객체는 key는 문자열만 가능, 단 ES2015에는 Symbol이라는 새로운 타입이 존재한다)

  - 일반 객체는 iterable 하지 않기에 for~of 구문이나, ... 구문을 사용할 수 없다. 


Set은 중복을 제거할 때 사용한다. Value 를 추가하고 삭제하며 동일 Value는 포함 될 수 없기 때문이다. 

  - new Set, new Set(Iterable) : 새로운 set 만들기

  - size, has, add, delete, forEach, clear

  - 다양한 이터레이터를 리턴: keys, values, entries (Map과의 호환성을 제공)

  - set[Symbol.iterator]() 구문은 set안의 값들을 순회할 수 있는 새로운 이터레이터를 리턴한다. 

var uniqueWords = new Set(words);

for (var word of uniqueWords) {

  console.log(word);

}


Map은 key-value 쌍으로 이뤄진다. 순회시 분해(destructuring)을 사용할 수 있다. 

for (var [key, value] of addressMap) {

  console.log(key, ': ', value);


Set, Map의 대용이 없어지지않고 남아있으면 Garbage Collector가 메모리를 회수 할 수 없다. 이애 해결을 위해 WeakSet, WeakMap을 사용한다. 




Symbol


심볼은 ES2015의 7번째 타입이다. 프로그램에서 이름 충돌을 피하고자 객체 속성 키(symbol-keyed property)를 사용하고 싶을 경우 사용한다. 

  - Object.property 처럼 dot(.)로 접근할 수 없고 Object[] 형식으로 접근할 수 있다. 

  - if( MY_SYMBOL in element) 처럼 속성을 참조하거나, delete element[MY_SYMBOL] 처럼 속성을 제거할 수 있다. 

  - MY_SYMBOL은 실행 스코프안에서만 모든 행위가 가능해서 충돌을 걱정할 필요없이 캡슐화를 할 수 있다. 

var MY_SYMBOL = Symbol();

var myObj = {};

myObj[MY_SYMBOL] = "dowon";


사용방법

  - Symbol() 호출: 일반적인 사용 형태, Symbol('name') 과 같이 파라미터를 주는 것은 디버깅을 위한 용도이다. 

  - Symbol.for(string): 심볼을 공유하고 싶을 경우 사용

  - Symbol.iterator: 특정 객체에 대해 Duck Typing으로 iterator를 만들고 싶을 경우 사용 

  - core.js 폴리필을 첨부해야 사용이 가능하다


기본 6가지 타입

  - Undefined

  - Null

  - Boolean

  - Number

  - String

  - Object 




Generator, 2


자바스크립트의 co-routine 이다. 함수가 끝나기전에 자신의 스코프 밖으로 나올 수 없다. 그러나 제너레이터 함수(generator-function)를 만들면 가능해 진다.

  - 제너레이터 함수는 function* 키워드로 시작한다. 

  - 제너레이터 함수안에는 yield 구문이 존재한다. 함수의 return은 한번 실행되지만 yield는 여러번 수행 가능하다. yield는 제너레이터의 실행을 멈췄다가 다음에 다시 시작할 수 있게 만든다. 

function* mygen(name) {

  yield 'hi ' + name;

  yield 'Have a great day';

}


> var iter = myget('dowon');

 [object Generator]

> iter.next();

  'hi dowon'

> iter.next();

  'Have a great day'


제너레이터 함수를 호출하면 제너레이터 객체(generator object)를 전달 받는다. yield첫번째에  실행이 멈춘 상태의 객체이다. .next()를 호출하면 다음 yield까지만 수행한다. yield 구문이 실핼될 때, 제너레이터의 스택 프레임(stack frame: 로컬 변수, 인자, 임시 값, 제너레이터 코드의 실행 위치)은 스택에서 제거된다. 


제너레이터를 통해 이터레이터를 만들 수 있다. 

> Symbol을 통해 만들기: 실행

class RangeIterator {

  constructor(start, stop) {

    this.value = start;

    this.stop = stop;

  }


  [Symbol.iterator]() { return this; }


  next() {

    var value = this.value;

    if (value < this.stop) {

      this.value++;

      return {done: false, value: value};

    } else {

      return {done: true, value: undefined};

    }

  }

}


// 'start'에서 'stop'까지 더해나가는 새로운 이터레이터를 리턴합니다.

function range(start, stop) {

  return new RangeIterator(start, stop);

}


// 수행 

for ( let value of range(0, 5)) {

 console.log(value);

}


> Generator를 통해 만들기: 실행

 - Symbol과 동일한 역할을 하는 이유는 Generator는 .next() 코드와 [Symbol.iterator]() 코드를 내장하고 있기 때문이다. 그냥 루프 처리만 작성하면 된다. 

function* range(start, stop) {

  for (let i=start; i < stop; i++) {

    yield i;

  }

}


// 수행 

for ( let value of range(0, 5)) {

 console.log(value);

}


제너레이터는 객체를 이터러블하게 만들때, 엄청나게 큰 결과를 처리, 복잡한 루프 구문을 리팩토링, 이터러블을 다루는 도구등으로 사용한다. (이부분은 경험이 없어서 이해가 힘들다. 다음기회로..) 제너레이터의 동작은 동기적으로 싱글-쓰레드 환경에서 실행된다. yield에서 멈추고 .next()에서 수행되는 된다. 만일 for ~ of 구문의 경우 iterator 스팩에 따라 수행이 된다.  




Proxy


자바스크립트 스팩 레벨에서 특정 객체에 대한 hooking을 가능케 한다. 

  - 첫번째 인자는 후킹할 객체이다. 

  - 두번째 인자는 후킹을 위한 설정이다. 보통 스팩에서 정의한 14가지 내부 메소드(internal method)를 재정의한다. 

var obj = new Proxy(<후킹할 객체>, <핸들러>);


내부 메소드는 [[ ]] 로 감싼다. 내부 메소드는 가려져 있기때문에 개발자가 호출할 수 없다. 

  - 속성 값 가져오기: obj.[[Get]](key, receiver) 는 obj.prop 또는 obj[key] 사용시 호출된다. 여기서 receiver는 속성 조회를 처음 시작했던 대상 객체이다. 

  - 속성 값 할당: obj.[[Set]](key, value, receiver) 는 obj.prop = value 또는 obj[key] = value 사용시 호출된다.  

    예) obj.prop += 2 같은 할당문의 경우 [[Get]] 호출 후 [[Set]]을 호출한다. (++, -- 도 동일)

  - obj.[[HasProperty]](key): 속성이 존재하는지 테스트 

  - obj.[[GetPrototypeOf]](): obj의 프로토타입을 반환. obj.__proto__ 또는 Object.getPrototypeOf(obj) 사용시 호출된다. 

  - functionObj.[[Call]](thisValue, arguments): functionObj() 또는 x.method() 사용시 호출된다. 

  - constructorObj.[[Construct]](arguments, newTarget): 생성자를 실행. new Date(2017,1,1) 사용시 호출된다.  


핸들러 만들기

  - set시에 무조건 Error 발생시킴

  - get시에 Reflect를 통해 기본 동작을 수행하고 추가 작업을 수행할 수 있음

var target = {};

var handler = {

  set: function (target, key, value, receiver) {

    throw new Error('not settting value');

  }

  get: function (target, key, receiver) {

    // default action 

    var result = Reflect.get(target, key, receiver);

    // custom action 

  }

}

var proxy = new Proxy(target, handler); 


> proxy.name = 'hi';

  Error: not settting value


프락시를 이용하면 어떤 객체에 대한 접근을 관찰하거나 로그를 남길 때 유용하다. 




디스트럭쳐링(Destructuring)


배열 또는 객체의 속성값을 일괄처리로 변수에 할당 받을 수 있는 방법이다. 객체나 배열을 XHR을 통해 JSON형태의 포멧으로 받을 경우 해체를 통해 값을 간편하게 변수로 받아 사용할 수 있다. 이는 코드의 가독성과 간결성을 유지해 줄 수 있다. 해체시 Default Value를 할당 할 수 있다. 배열의 경우는 인덱스 순서에 따라 하나씩 변수에 값을 할당 받지 않고 한번에 변수에 값을 할당 받는다. 

var first = myArray[0];

var second = myArray[1];


이것은 하기와 동일하다


let [first, second] = myArray;


또는 다차원 배열의 해체도 가능하다 


let [foo, [[bar], baz]] = [1, [[2], 3]]; 


또는 선택해서 받을 수 있다. 


let [ , , third] = [1, 2, 3];


또는 Rest 패턴을 통해 배열의 요소를 다른 배열로 받을 수도 있다.


let [first, ...second] = [1, 2, 3, 4, 5];


또는 함수에서 배열 리턴값을 해체한다 


function myArrayFunc() {

  return [1, 2, 3, 4];

}

let [a, b, c, d] = myArrayFunc();


객체를 해체할 수 있다. 객체 해체시에는 반드시 let, const 또는 var를 써야한다. 객체의 경우 이름이 같으면 변수 alias를 사용하지 않아도 된다. 

let myObj = { name: 'dowon' };


// myObj.name을 받아 myName 이라는 변수에 할당한다.

let { name: myName } = myObj;


좀 더 복잡한 객체의 해체도 가능하다, second = 'kangnam'이라는 default value를 할당했다. 


let complicateObj = {

  arrProps: [

    'dowon',

    { address: 'seoul'}

  ]

}

let { arrProps: [first, { second = 'kangnam' }] } = complicateObj;


이터레이션 프로토콜과 함께 사용할 수 있다


for (let [key, value] of map) {

   console.log(key, value);

}


또는 함수에서 객체 리턴값을 해체한다


function myObjFunc() {

  return {

    name: 'dowon',

    address: 'seoul'

  }

}

let { name, address } = myObjFunc();


또는 continuation passing style 사용도 가능하다. k 라는 callback에 값 1, 2를 주면 foo = 1, bar = 2 각 해체되어 할당된다. 


function returnMultipleValue(k) {

  k(1, 2);

}

returnMultipleValue(( foo, bar ) => ..... ); 


또는 ES2015의 import 구문에서 사용한다


import { Component } from 'angular2/core'; 


함수의 파라미터가 객체일때 파라미터를 해체하는 구문을 사용할 수 있다. 기존에는 파라미터 객체를 받아서 함수 내부에서 다시 변수에 할당하는 방식이 있을 경우 파로 파라미터 객체 해체를 통해 코드 간결성을 유지할 수 있다.

function myFn({ a, b, c, d }




Rest Parameter 와 Default Parameter


함수 파라미터의 해체(Destructuring)외에 함수 문법의 표현을 간결하게 해주는 두가지가 Rest/Default Parameter 이다. 함수의 파라미터를 가져올 때 기존엔 parameters를 통해 접근을 했다면 ES2015에서는 Rest Parameter로 접근한다. Rest Parameter는 함수의 마지막에 정의한다. 

function myFunc(first, ...myParams) {

  for (let param of myParams) {

    console.log(param);

  }

}


myFunc('hi', 'dowon', 'great', 'day');


... 점(Dot) 3개이후 파라미터 명칭을 설정하면 나머지 파라미터가 배열 객체로 넘어온다. 


함수의 파라미터 값이 undefined인지 검증하는 코드를 함수안에 넣는데 undefined일 경우 기본 값을 할당할 수 있다. 

function myFunc( first = 'dowon', second = 'hi') {

  console.log(first, second);

}


myFunc('Yun');


arguments 객체를 코드를 읽기 어렵게 만들고 JavaScript VM의 성능 최적화하는 것도 어렵게 만든다고 하니 앞으로는 Rest Parameter를 사용하자.




템플릿 문자열 (Template String)


백틱(Backtick) ` ` 을 이용해 문자열을 만드는 방법이 새롭게 생겼다. Angular2에서 template 정의시 많이 사용한다. 이에 더불어 문자 맵핑 채워넣기 (string interpolation) 을 백틱안에서 ${ } 를 사용해 넣을 수 있다. 

  - cross-iste scripting에 대한 공격을 피하려면 기존 처럼 신뢰할 수 없는 데이터에 대한 처리를 직접해주어야 한다. 

  - 다국어 처리도 직접한다

  - 루프문이 없다. 

let name = 'dowon';

let myStr = `Hi ${name}. Have a great day!`;


태그된 템플릿 (Tagged Template)은 백틱앞에 붙이는 사용자 정의 함수라 할 수 있다. 즉, 이 함수를 통해 템플릿 문자열을 커스터 마이징 할 수 있다. 태그 함수의 인자에는 어떤 것이 와도 되고, 리턴 값도 마찬가지이다. 

function SaferHTML(templateData) {

  var s = templateData[0];

  for (var i = 1; i < arguments.length; i++) {

    var arg = String(arguments[i]);


    // 대입문의 특수 문자들을 이스케이프시켜 표현합니다.

    s += arg.replace(/&/g, "&amp;")

            .replace(/</g, "&lt;")

            .replace(/>/g, "&gt;");


    // 템플릿의 특수 문자들은 이스케이프시키지 않습니다.

    s += templateData[i];

  }

  return s;

}


SaferHTML`<p>${name} hi</p>` 처럼 말이다. 




참조


  - Depth in ES6 번역문서

  - iterator, iterable 차이



신고
posted by peter yun 윤영식
2016.02.19 12:10 Angular/Module System

모듈단위의 파일을 만들어 모듈간에 모듈을 로딩해서 사용하는 방법을 제시하는 것이 Module Loader의 역할이고, 만들어진 모듈을 어떻게 묶어 사용할지 제시하는 것이 Module Bundler의 역할이다. 이번 글에서는 Module Bundler에 대해 알아보자. 



            





파일 번들링의 일반적인 방식 


index.html 에 <script> 태그를 이용해 첫번째는 개발자가 직접 넣는 방법, 두번째는 Grunt 또는 Gulp의 프론트앤드 툴의 도움을 받아 자동으로 넣는 방법이 있다. 하지만 응답성능에 민감한 애플리케이션에는 초기 다량의 모듈 파일 전송이라는 네트워크 성능이슈를 야기한다. 따라서 운영시에는 좀 더 컴팩하게 파일을 묶을 필요가 있다. 

  - 파일을 합치고(Concatenate)

  - 압축(Minify)하는 과정을 거친다. 


통합하고 압축하는 역할에 대한 플러그인이 Grunt/Gulp에 모두 존재한다. 하지만 CommonJS 또는 AMD나 ES2015 네이티브 로더를 사용할 때는 브라우져에 진화적인 코드로 변환해야 한다. 이때 사용하는 것이 Browserify, Webpack, JSPM 와 같은 모듈 번들러이다. 




Browserify


NodeJS에는 다양한 패키지가 존재하고 NPM(Node Package Manager)를 통해 설치한다. 모든 패키지가 CommonJS에 맞춰 모듈 패턴으로 구현을 하고 있다. 만약 이를 브라우져에서 사용하고 싶을 경우에는 Browserify 의 도움을 받으면 된다.

  - CommonJS를 번들링할 때 사용한다. 

  - AST (Abstract syntaxt tree) 에 따라 require한 모듈에서 require하고 해당 모듈이 require하고 있는 모든 하위의 모듈의 의존 파일을 자동으로 묶어준다. 

  - browserify 명령에서 entry 파일만 지정하면 된다. 의존

// app.js

var math  = require('math');

...


// console

browserify app.js -o bundle.js 




Webpack


AMD 패턴의 모듈을 번들링 할 때는 RequreJS에서 제공하는 r.js를 사용할 수 있다. 하지만 애플리케이션에서 NodeJS의 모듈도 사용하고 AMD 패턴 모듈도 사용한다면 어떻게 될까?  

  - CommonJS, AMD, ES2015 방식에 대한 번들링이 가능하다 

  - Bunlde Chunk라는 단위 묶으로 나누어서 번들링이 가능하다 


* Naver D2 Webpack 상세설명 참조




RollupJS


웹팩과 유사한 차세대 모듈 번들러로는 RollupJS가 있다. 특히 모듈안에 있는 내용중 사용하지 않는 것은 제거하는 Tree-Shaking 기술이 존재한다. 

  - maths.js에 square와 cube 펑션을 export 한다

  - cube만 사용한다. 

// maths.js 

export function square(x) {

 return x*x;


export function cube(x) {

  return x*x*x;

}


// test.js 

import { cube } from './maths.js';

console.log( cube(5) ); 


Tree Saking을 한후 square를 제거한다. 나무를 흔들면 필요없는 것은 떨어지고 필요한 것만 남는 것과 같다. (예제)

(function () {

'use strict';


// This function gets included

function cube ( x ) {

// rewrite this as `square( x ) * x`

// and see what happens!

return x * x * x;

}


console.log( cube( 5 ) ); // 125

}());




 

JSPM 


SystemJS는 모듈을 로딩하는 일관된 API를(System.config, System.import) 제공하고 패키지를 받아오고 로딩하는 역할로 JSPM을 사용할 수 있다.

  - SystemJS를 위한 패키지 메니져이다. 

  - ES6 Module Loader로 불린다

  - npm, bower, GitHub으로 부터 로딩한다. 

  - 브라우져에서 NodeJS 패키지가 browserify와 똑같이 작동한다

  - 개발시에는 개별 파일로 관리하다가 프러덕션에서는 번들링을 한다. 

  - 사용자 가이드


Browserify ==> Webpack ===> JSPM 순서로 정리 해보자. 




Angular2에서의 JSPM/SystemJS
  - JSPM을 패키지 메니져로 사용
  - SystemJS를 모듈 로더로 사용
  - TypeScript를 ES2015 자바스크립트 슈퍼셋으로 사용
  - Angular 2 를 통해 웹, 모바일, 네이티브 개발 플랫폼으로 사용


Module Loader는 CommonJS, AMD, UMD, ES2015 스팩 방식이 있고, Module Bundler는 Browserify, Webapck에서 JSPM(+SystemJS) 방식으로 수렴되고 있다. Angular2를 하게되면 Module Loader로서의 SystemJS와 Module Bundler이면서 패키지 메니져 역할을 수행하는 JSPM을 알아둘 필요가 있다.  

  - Angular 2에서 Bundler에 따른 사용 모듈 목록



<참조>

  - 모듈 번들러 설명 

  - 모듈 번들러 비교 slideshare

  - Choose ES6 Modules

  - Grunt에서 파일들을 통합/압축하는 방법

  - Browserify 와 Webpack 비교 

  - ES2015 스팩  

  - ES2015의 import/export 이야기

  


신고
posted by peter yun 윤영식
2016.02.18 18:02 Angular/Module System

프론트앤드 자바스크립트 개발이 점점 복잡해 짐에 따라 모듈 패턴으로 코드를 작성하고 단일 책임 원칙(Single Responsibility Principle)을 지키는 것이 좋다. 모듈 코드를 작성한 후 모듈을 로딩하고 배포(번들링)하는 다양한 방법들이 존재한다. 먼저 Module Loader에 대해 살펴보고 다음 글에서 Module Bundler에 대해 정리해 본다. 







모듈 패턴


모듈패턴을 사용하는 이유

  - 유지보수성(Maintainability): 단일 책임 원칙에 따라 필요한 기능을 담고 있으면서 별도 폴더와 파일로 유지하면 변경이나 확장 발생시 찾고 수정하기 쉽다. 전제 조건은 외부에 노출하는 API를 일관되게 유지하는 것이 중요하다. 

  - 이름공간(Namespacing): 자바스크립트에서 전역변수를 통한 개발을 하지 않는다. 이를 위해 즉시실행함수표현(IIFE)를 사용하여 전역변수의 오염을 방지하는데, 모듈 패턴 또한 전역변수 오명을 방지한다. 

  - 재사용성(Resuability): 모듈의 성격을 잘 나누어 놓으면 다음 프로젝트에서 그대로 사용해 쓸 수 있다. 보통 SDK나 Base Framework을 만들어 놓으면 초기 구축 비용을 최소로 할 수 있다. 


JohnPapa Angular 스타일 가이드를 보면 특별히 모듈을 지원하는 라이브러리의 도움없이 자바스크립트 모듈 패턴 방식으로 앵귤러 v1 코드를 작성토록 가이드하고 있고, 앵귤러 팀에서도 공식적으로 추천하고 있다. 

(function () {

    'use strict';


    angular

        .module('a3.common.action')

        .factory('currentAction', currentAction);


    /* @ngInject */

    function currentAction(ActionType, stateManager) {

        return {

            setDashboard: setDashboard,

            setWorkspace: setWorkspace

        };


        function setDashboard(dashboardId) {

            var action = {

                ...

            };

            stateManager.dispatch(action);

        }


        function setWorkspace(workspaceId, taskerId) {

            var action = {

                ...

            };

            stateManager.dispatch(action);

        }

    }

})();


순수 자바스크립트로 모듈 단위로 만든 후 상호 운영은 어떻게 해야할까? 일단 index.html에 설정을 하고 사용하는 순서에 index.html에 script 태그를 통해 로딩을 하는 간단한 방식을 생각해 볼 수 있다. 하지만 필요한 시점에 자바스크립트에서 로딩을 해서 사용하는 방식을 명시적으로 하려면 별도 로더의 도움이 필요하다. 




CommonJS & AMD & UMD


CommonJS는 지정한 코드를 동기적으로 로딩하는 방식으로 서버 사이드의 Node.js에서 사용한다. 

  - Object 만을 대상으로 한다.

  - module.exports 구문으로 Object를 export 한다

  - require 구문으로 Object를 import 한다. 

// 파일명: module.js 

function module() {

  this.hi = function () { return 'hi'; }

}

module.exports = module;



// 사용하는 파일: test.js

var module = require('module');

var m = new module();

m.hi();


위에서 require를 차례로 호출하면 동기적으로 하나씩 로딩을 한다. 즉, 비동기적이지 않기 때문에 로딩이 전부 되어야 수행이 된다. 브라우져에서 동기적으로 모듈 파일을 로딩하게 되면 모든 파일이 로딩된 후 화면이 실행되므로 성능 이슈를 야기할 수 있다. 따라서 CommonJS는 Node.js에서 주로 사용하고 브라우져에서는 사용하지 않는다. 


AMD(Asynchronous Module Definition)은 비동적으로 모듈을 로딩한다. 

  - Object, function, constructor, string, JSON 등 다른 타입들도 로딩이 가능한다. 

  - define 구문을 사용한다.

 define(['jquery', 'angular'], function($, angular) { 

   ...

 });


jquery, angular 파일에 대해 비동기적으로 로딩한다. AMD대표적 구현체로는 RequireJS가 있다. 


UMD(Universal Module Definition)은 AMD와 CommonJS의 기능을 둘다 지원하는 것이다. 

  - AMD, CommonJS를 고려한다 

  - 구현체로 SystemJS를 들 수 있다. SystemJS는 Universal dynamic module loader로 AMD, CommonJS뿐만 아니라 브라우져의 global scripts와 NodeJS 패키지 로딩을 하고 Traceur 또는 Babel 과 같이 작동할 수도 있다. 특히, Angular 2에서 사용한다.

  - 아래와 같이 CommonJS와 AMD를 체크하여 사용할 수도 있다. 구현 방식에 대한 다양한 예를 참조한다. 

(function (d3, jQuery) {

    'use strict';

   var Sankey2 = { ... };

   ....


    // Support AMD

    if (typeof define === 'function' && define.amd) {

        define('Sankey2', ['d3'], Sankey2);

    } 

   // Support CommonJS

   else if ('undefined' !== typeof exports && 'undefined' !== typeof module) {

        module.exports = Sankey2;

    } 

   // Support window

   else {

        window.Sankey2 = Sankey2;

    }


})(window.d3, window.$);


브라우져에서 ES6 module loader가 아닌 SystemJS (Universal module loader)를 사용할 경우 System.import 호출로 AMD, CommonJS, ES6 모듈 형식을 로딩할 수 있게 API를 제공하고  패키지 메니져로 JSPM을 사용할 수도 있다. JSPM은 무저항 브라우져용 모듈 패키지 메니져 (frictionless browser package management)로써 ES6 module loader가 작동하지 않는 곳에서 사용하는 Polyfill 이면서 AMD, CommonJS, Globals 자바스크립트 모듈 형식을 로딩할 수 있다. 



Native JS


자바스크립트 ES2015 (ES6)에서 모듈 로더를 공식지원한다. ES2015는 모듈의 importing과 exporting을 제공한다. (참조) 간결관 syntax와 비동기 로딩과 cyclic dependencies에 대해 보다 잘 지원을 한다. 

  - import, export 를 사용한다. 

// app.js 

export let count = 1;


export function hi() {

  return 'hi-' + count++;

}


// test.js

import * as app from './app';


console.log(app.hi());

console.log(app.count);



모듈로더에 대해 정리를 해보자. 자바스크립트 개발시 모듈 패턴에 입각하여 개발할 때 다양한 방식을 사용할 수 있으나

  - NodeJS 기반 서버 사이드 개발은 CommonJS 이고

  - Browser 기반 클라이언트 사이드 개발은 AMD구현체 중 하나인 RequireJS 를 사용한다. 


ppt에서 r.js는 RequireJS에서 제공하는 모듈 번들러이다. 모듈 로더에 맞는 모듈을 개발한 후에 모듈 파일을 운영 배포하기 위해 번들링 즉, 묶는 과정을 거친다. 번들링 방법은 대해 다음 글에서 살펴보자. 




<참조> 

  - 모듈 로딩 다이어그램

  - 모듈 로딩: CommonJS & AMD & UMD

  - 모듈 번들링: Browserify & Webpack 

  - CommonJS와 AMD - D2 

  - UMD 구현 예 

  - SystemJS: Universal dynamic module loader

  - Rollup.js: 차세대 Javascript module bundler

  - ES6 Module Loader Polyfill: Top Level SystemJS

  - ES6 vs CommonJS 비교

  - JavaScript Module Pattern

신고
posted by peter yun 윤영식
2016.01.15 08:16 Angular/Concept

디자인 가이드는 초기 버전의 생각이므로 현재(2015.1.15) beta 버전의 구문 형식과 틀릴 수도 있다는 것을 숙지하고 어떤 사상을 가지고 있는지 알아보는 것에 집중하자. 물고기가 아니라 물고기 잡는 그물에 집중하자는 이야기이다. 




Expressions


Angular의 expression은 Angular1의 expression 과 유사하고 몇가지 더 추가되었다. 


  - Dirty Checking과 긴밀히 통합되어 expression의 prefix 또는 constant 부분에 대한 최적화를 수행한다. 

    좀 더 자세한 사항은 dirty checking 디자인 가이드를 참조한다. 

  - One time binding을 {{::foo}} 와 같이 최초 한번만 바인딩되고 이후 바뀌지 않도록 한다. Angular1에서 사용하던 방법




Directive API


Directive는 ES6 classes를 사용해 정의하고, Annotation을 사용해 meta data를 정의한다.



Class annotation


모든 Directive는 @DecoratorDirective, @TemplateDirective, @ComponentDirective 중 하나의 annotation을 갖는 ES6 class 이다. Annotations는 일반적인 properties를 갖는다. 


  - selector: directive가 적용될 element를 찾을 css selector이다. (string)

  - events: directive가 template안에서 on-.. attribute를 사용해 발생시키는 이벤트 목록이다. (list of string)

  - visibility: element의 subtree안의 directives들이 directive 인스턴스에 접근할 수 있도록 정의한다. ['local' | 'direct-children' | 'any-children']

  - microsyntax: 특별한 properties 구문 형식과 이 properties가 다른 properties와 어떻게 맵핑하는지 정의한다 (string)


아래 예를 보면 NgShowDirective 생성을 통해 element에 ng-show attribute를 사용할 수 있게 한다. 즉, annotation은 툴이 element안에 directive를 인스턴스화하는 과정없이 그리고 코드를 실행하지 않고도 directives를 찾게한다.

import {ng} from '...';


@ng.DecoratorDirective(selector: '[ng-show]')


class NgShowDirective {}



Constructor 에 Denpendency Injection 하기


모든 directives는 Dependency Injection을 통해 contructor의 parameters 값을 갖는다. 즉, DI를 통해 다른 오브젝트 인스턴스틀 갖질 수 있는 것이다. 아래 예를 보면 @Inject annotation을 사용해 DI하고 있다. 

import {ng} from '...';


@ng.DecoratorDirective(selector: '[ng-show]')


class NgShowDirective {

  @Inject(window.HTMLElement, ng.Http, ng.SomeOtherDirective)

  constructor(element, http, someOtherDirective) { ... }

}



Directive communication


http 갖은 것은 ui 오브젝트가 아니기 때문에 element의 subtree안의 모든 directives에 접근할 수 있도록 annotation에 visibility 을 사용할 수 있다. 예로 input element에서 user.value에 대해 NgInputDirective는 input 값의 변경을 Listening하고, NgModelDirective가 validation하며, NgFormDirective은 state를 유지하는 작업을 한다. 여기서 NgModelDirective는 <form> element에 있는 NgFormDirective에 접근할 필요가 있고, NgInputDirective는 같은 element에 있는 NgModelDirective에 접근할 필요가 있다. 이를 아래와 같이 visibility를 설정한다. 

<form>

  <input type="text" ng-model="user.value" required>

</form>


import {ng} from '...';


@ng.DecoratorDirective(

  selector: 'form',

  visibility: 'subtree'

)

class NgFormDirective {

  constructor() { … }

}


@ng.DecoratorDirective(

  selector: 'input[type=text]',

  visibility: 'local'

)

class NgModelDirective {

  @Inject(NgFormDirective)

  constructor(ngForm) { … }

}


@ng.DecoratorDirective(selector: 'input[type=text]',)

class NgInputDirective {

  @Inject(NgModelDirective)

  constructor(ngModel) { … }

}



Bindable properties


Directives의 모든 properties는 데이터 바인딩을 사용할 수 있고, 이를 위해 set(write)에 대해 @ng.PropertySet annotation을 통해 환경설정을 한다. minification 후에도 property 명을 보존하고 directive의 properties에 데이터 바인딩을 쉽게 만들기 위함이다. 


  - 구문: @ng.PropertySet({trigger: ['reference' | 'collection' | 'deferred'], domOnly: [false|true] })

  - trigger

     + reference: reference가 바뀌면 reference 안에서 전달 (default)

     + collection: collection의 entry가 바뀌면 collection 안에서 전달 

     + deferred: directive가 수행할 수 있는 컴파일된 expression을 전달

  - domOnly: DOM만 변경하고 watch하고 있는 다른 property는 제외함 (default: false)

  

예 

import {ng} from '...';


@ng.DecoratorDirective {

  selector: 'dialog'

}

class Dialog {

  constructor() {

    this._content = null;

  }

  get content(content) {

    return this._content;

  }

  @ng.PropertySet({domOnly: true})

  set content(content) {...}

}


<dialog content=”{{1+2}}”>



Properties를 위한 Microsyntax


directive가 여러 값을 다룰 필요가 있을 때 microsyntax로 설정을 한다. 예로 ng-repeat을 하면 track-by expression을 해야할 때. 구문은 다음과 같다. 


  - MICROSYNTAX=(VARIABLE|FIXED|OPTIONAL)+

  - VARIABLE=$\w+

  - FIXED=[^$\[\]]+

  - OPTIONAL=\[MICROSYNTAX\]


ng-repeat을 위한 microsyntax 예를 보면 <div>에 ng-repeat-item-name, ng-repeat-collection, ng-repeat-track-by를 사용한다. 

@TemplateDirective(

  selector: '[ng-repeat]'

  microsyntax: {

   'ng-repeat': '$item-name in $collection [track by $track-by]'

  })

class NgRepeat {

  @ng.PropertySet({trigger: 'reference'})

  set ngRepeatItemName() { … }


  @ng.PropertySet({trigger: 'collection'})

  set ngRepeatCollection() { … }


  @ng.PropertySet({trigger: 'deferred'})

  set ngRepeatTrackByFn;

}



<div ng-repeat="item in items track-by item.id">

<div ng-repeat-item-name="item" ng-repeat-collection="items" ng-repeat-track-by="item.id">



Lifecycle hooks 


모든 directives는 아래 특별한 함수를 구현할 수 있다. 이유는 templates이 initialization과 finalization을 알수 있도록 허용하기 위해 lifecycle hook을 둔다. 


  - attach: templates 안에 바인딩이 된 후 한번만 호출된다. 

  - detach: directive의 element가 destroy될 때 호출된다. 


Component directive는 특별히 아래 함수도 구현할 수 있다. 


  - templateLoaded: component의 template이 로딩되어 component element에 포함될 때 호출된다. 



Events


Directives는 HTMLElement.dispatchEvent를 사용해 사용자 정의 events를 발생시킬 수 있고, HTMLElement.addEventListener를 통해 이벤트를 Listening할 수 있다. 이벤트는 tree를 타고 bubble up 된다. 설정은 아래 예와 같이 class annotation 안에 하고, event가 발생하면 dirty checking 스케쥴이 돌고 template안에 on-... attributes 를 validation한다. 

 @ng.DecoratorDirective(

  selector: 'dialog',

  events: ['close']

)

class Dialog {

  @Inject(window.HTMLElement)

  constructor(element) { ... }

  close() {

    var evt = new Event('close');

    this.element.dispatchEvent(evt);

    if (!evt.defaultPrevented()) {

      // really close the dialog...

    }

  }

}




Directive 구현 예


Decorator: ng-show

import {ng} from '...';


@ng.DecoratorDirective(

  selector: '[ng-show]'  

)

class NgShow {

  @Inject(window.HTMLElement)

  constructor(element) {

    this.element = element;

  }


  set ngShow(value) {

    this.element.style.display = value ? 'block' : 'hidden';

  }

}


Template Directive: ng-if

template 태그의 content를 사용하는 간단한 template directive 이다. 

import {ng} from '...';


@ng.TemplateDirective(

  selector: '[ng-if]'  

)

class NgIf {

  @Inject(ng.ViewHole, ng.ViewFactory, Injector)

  constructor(viewHole, viewFactory, injector) {

    this.viewFactory = viewFactory;

    this.viewHole = viewHole;

    this.injector = injector;

    this.view = null; 

  }


  set ngIf(value) {

    if (this.view) {

      this.viewHole.remove(this.view);

    }

    if (value) {

      this.view = this.viewFactory(injector);

      this.viewHole.add(this.view);

    }

  }

}


Template Direcive: ng-include

import {ng} from '...';


@ng.TemplateDirective(

  selector: '[ng-include]'

)

class NgInclude {

  @Inject(ng.ViewHole, ng.Compiler, ng.Http, ng.Directives,

          Injector)

  constructor(viewHole, compiler, http, directives, injector) {

    this.viewHole = viewHole;

    this.compiler = compiler;

    this.directives = directives;

    this.injector = injector;

    this.view = null;

    this.$http = http;

  }


  set ngInclude(value) {

    var self = this;

    this.$http(value).always(removeView).then(addView);


    function removeView() {

      if (self.view) {

        self.viewHole.remove(self.view);

        self.view = null;

      }

    }

    function addView(templateString) {

      var viewFactory = 

            this.compiler(templateString, this.directives);

      this.view = viewFactory(injector);

      this.viewHole.add(this.view);

    }

  }

}


Templat directive: ng-repeat

template directive는 새로운 execution context를 제공할 수 있고, 아래 간단한 예에서 variable을 injector안에 있는 현재 execution context에서 검증한다. 

import {ng} from '...';


@ng.TemplateDirective(

  selector: '[ng-repeat]'

  microsyntax: {

   'ng-repeat': '$item-name in $collection [track by $track-by]'

  })

class NgRepeat {

  @Inject(ng.ViewHole, ng.ViewFactory, Injector injector)

  constructor(viewHole, viewFactory, injector) {

    this.viewHole = viewHole;

    this.viewFactory = viewFactory;

    this.injector = injector;

    this.views = [];

  }

  @ng.PropertySet({trigger: 'reference'})

  set ngRepeatItemName(value) { 

    this.itemName = value; 

    this.update(); 

  }

  @ng.PropertySet({trigger: 'collection'})

  set ngRepeatCollection(arrayChangeRecord:ArrayChangeRecord) { 

    this.arrayChangeRecord = arrayChangeRecord;

    this.update(); 

  }

  @ng.PropertySet({trigger: 'deferred'})

  set ngRepeatTrackByFn(value) { 

    this.trackByFn = value; 

    this.update();

  }

  update() {

    …

    // deleting a view    

    this.viewHole.remove(deletedView);

    …

    // creating a new view

    var childContext = {

         $index: index,

         $odd: !!index%2,

         $even: !index%2 

    };

    childContext[this.itemName] = this.collection[index];

    var view = this.viewFactory(injector, childContext);

    viewHole.append(view);

   }

   ...

  }

}


Component Directive: pane

title이 있는 pane 구현 예

import {ng} from '...';


@ng.ComponentDirective(

  select: 'pane',

  template: ng.inline('<div>{{title}}</div><content/>'),

  css: ng.url('pane.css'),

)

class sample.Pane {

  constructor() {}

  set title() { … }

}




다른 Web Component 프레임워크에 component를 publishing하거나 consuming하기 



Custom Elements로 X-Tags, Polymer를 사용하는 프레임워크 


Angular는 Custom element API가 유효한지 찾고 custom element로 컴포넌트를 자동 등록하고, component directive의 properties가 custom element의 인스턴스를 접근할 수 있게 한다. 


  - custom element로 Angular component를 export 할 경우 

    + custom element API가 유효하면 Javascript 애플리케이션에서 Angular component를 DOM element 접근방식으로 event의 listen과 properties를 접근가능케 만든다.

  - Angular 애플리케이션에서 custom element 사용할 경우 

    + Angular는 native DOM element를 사용하듯이 custom elements를 사용할 수 있고, event도 listen할 수 있다.



가이드-1 다시 보기



<참조> 


  - Angular Templating 디장인 가이드 

  - Creating, Triggering Events 모질라 문서 

신고
posted by peter yun 윤영식
2016.01.14 08:23 Angular/Concept

이번에는 Angular2 Templating에 대한 Design Guide를 살펴본다. Angular1에서 Angular2로 오면서 살아남은 명칭중 하나는 Directive(지시자)이다. 지시자는 Angular 프레임워크에서 컴포넌트의 지위를 얻을 수 있는 방법을 제공했으나, Scope에 대한 격리 옵션과 모델들의 @/&/= 옵션, ControllerAs 등 문법적으로 알고 가야할 사항이 많아서 처음 접근하는 개발자에게 상당한 Learning Curve가 존재한다.




친숙함과 간결함 사이에서 (Familiarity vs Simplicity)


친숙함은 달콤하다. 지금 잘 알고 있는 것을 가지고 현재를 편하게 즐길 수 있도록 해준다. 하지만 변화에 둔하고 러닝커브가 높을 경우 신규로 알아야 할 사람들에게 많은 시간을 투자하게 만든다. 친숙함이 정말 친숙한 것인지 한번 더 돌아보고 주위 사람들이 왜(Why) 어려워 하고 어떻게(How)하면 쉽고 빠르게 익히고 널리 사용할 수 있도록 무엇(What)으로 만들 수 있을지 기술 리더는 고민해 보아야 한다. 


간결함은 하는 역할이 명확하여 쉽게 이해한다. 배움의 시간을 단축시켜주고 즐거움을 줄 수 있다. 그러나 복잡함을 간결하게 표현하기 위해서는 함축적이다. 따라서 그 내면에 있는 내용을 이해하고 쓰면 더 많은 생각들을 읽을 수 있다. 따라서 간결함을 왜 어떻게 무엇으로 만들었는지 알아 보는 것이 중요하겠다. 그 간결함을 쉽게 설명할 수 있다면... Angular2 Templating은 간결함의 시작중 하나라 본다. 


* 주의) 글에서 Angular 또는 Angular2는 Angular version 2를 지칭하고, AngularJS 또는 Angular1는 Angular version 1를 가르킨다. 


 

Templating 개념 둘러보기


Angular는 DOM element와 행위에 대한 것을 연관(연결) 한다. 여기서 행위(Behavior)를 Directive라고 부른다. 행위에 대한 부분이기 때문에 Directive는 CSS Selector와 맵핑되는 DOM element를 위해 인스터스화된 자바스크립이다. DOM element는 애플리케이션과 상호작용하기 위해 properties와 events를 사용하는데, Directive도 DOM element의 properties와 events의 동일 인터페이스를 사용한다. 


Angular 애플리케이션이 사용자 인터페이스는 "Component Directive"를 사용해 구성한다. Component Directive는 애플리케이션의 Data와 Logic을 저장하고, 또한 사용자 인터페이스를 정의하는 html template도 가진다. 컴포넌트 안에서 element와 상호작용하는 컴포넌트는 Decorator Directive이다. 보통 ng-show="false"와 같이 element의 attribute로 동작하고 엘러먼트에 여러개가 놓일 수 있다. 


Angular는 양방향 데이터 바인딩을 사용한다. 데이터 바인딩은 template안에 특별한 html attribute를 이용해 설정한다. 이때 바인딩은 expression이라고 부르는 것을 이용해 정의한다. expression은 함수(Function)를 호출하거나 데이터에 접근하는 것으로 모든 expression은 execution context안에서 실행된다. 이런 작용은 Dependency Injection Container 안에서 아래와 같이 수행된다.





Directive Type


지시자의 종류 목록


Provides execution context for expressions

Provides a hole in the DOM to insert template instances

Isolates the DOM/expressions/css for reuse

Decorator Directive

no

no

no

Template Directive

can create child execution context, don't have to.

yes

no

Component Directive

isolated execution context, always.

no

yes


Directive에 대한 일반 제약조건은 Directive는 DOM 구조를 제거(remove)/변경(change) 해서는 안된다. Template Directive와 Component Directvie는 별도의 Execution Context를 가질 수 있다. Angular에서 template directive가 execution context를 갖는 것에 유념하자.



Decorator Directive


새로운 element를 만들고 해당 element에서 뭐든 할 수 있다. 그러나 만들지 않은 것은 다른 element는 attribute 변경만 가능하다.



Template Directive


원래 있던 element/template 영역의 특정 부분에 template 인스턴스를 넣을 수 있고, template directive는 자식(child) execution context를 생성한다. 예로 ng-repeat는 row 마다 execution context를 만들지만 ng-if는 부모(parent) execution context를 재사용한다. Template Directive 종류로는 ng-if, ng-repeat, ng-view, ng-switch, ng-include 가 있다. 일반적인 문법을 보자 

<template ng-repeat>

  <div> ... </div>

</template>


template element 자체는 제거되고 template의 일부가 되지 않는다. 즉, template directive는 <template> element만을 허용한다. 중첩으로 아래와 같이 사용할 수도 있다. 

<template ng-repeat>

  <template ng-if>

    <div>...</div>

  </template>

</template>

    

template directives를 사용하면 element에 아래와 같이 template로 변경된다. 

<ul>

  <li ng-repeat>

    <span ng-if> ... </span>

  </li>

</ul>

은 아래 내용과 완전 동일하다

<ul>

  <template ng-repeat>

    <li>

      <template ng-if>

        <span> ... </span>

      </template>

    </li>

  </template>

</ul>


위이 template 태그는 Angular1의 directive의 compile/link 과정과 동일하다. 즉, Angular2에서는 compile/link과정을 template directive로 대체하는 것이다. 


    // Angular1의 이런 구문이 사라지는 것이다. 

    return {  

           ....

            templateUrl: 'plugins/taskers/wafer-traffic/wafer-traffic.html',

            replace: true,

            link: link,

            controller: ctrl,  

            ....

        };

    function link() {}

    function ctrl() {}



Component Directive 


javascript 로직, html template 과 옵션으로 css style을 컴포넌트에 가지고 있다. 이를 위해서 template안의 expressions을 격리(isolate)한다. - execution context의 격리: component directive 인스턴스가 template안의 expresssions를 위한 새로운 execution context가 되는 것이다. 즉, template안의 expressions은 component directive 인스턴스의 함수(function) 또는 properties만 접근 가능하다는 것이다. 이것은 Angular1의 directive 정의시 scope: {...} 격리하는 방법과 controllerAs를 사용해서 this로 접근하는 것등을 개발자가 정의할 필요없이 Component directive로 통일한 것으로 보인다. 프로젝트를 진행하다보면 거의 대부분 scope: { model: '=' } 과 controllerAs를 default로 놓고 사용하기 때문에 프레임워크 단에서 simple화 한 것은 환영할만 하다. 


  // Angular1의 이런 구문이 사라지는 것이다. 

  return {

            restrict: 'EA',

            scope: {

              model: '='

            },

            controller: trafficCtrl,

            controllerAs: 'traffic',

            bindToController: true

        };


- DOM 과 CSS의 격리: Component directives는 다른 directive에서 컨텐츠 변경을 못하도록 Shadow DOM을 사용한다. 따라서 Shadow DOM안에서의 element events는 외부 element로 전파되지 않는다. (buble out of component directive) 

   + Properties와 events: Template은 component의 properties들과 데이터 바인딩 되고, 이벤트는 상위 컴포넌트로 전파할 수 있다. 

   + CSS properties: 템플릿은 Shadow DOM을 기본으로 사용하는 컴포넌트에 한해 스타일을 변경할 수 있다. 

   + Child fragment: 템플릿은 Shadow DOM의 <content> 태그를 사용하는 템플릿을 넣는 자식 elements를 제공한다. 자식 elements의 expression은 밖의 템플릿 execution context와 계속 연결된다. 


expression을 위한 Execution Context는 클래스 인스턴스이고 이를 통해 expression에 대한 type checks등 다양한 일을 수행할 수 있다. 



Directives를 인스턴스화 하는 순서 


자식 elements전에 부모 element의 directives가 먼저 인스턴스화 하고, 만약 template directive가 존재하면 이것을 인스턴스화 하고, 다음으로 element의 decorator directive를 인스턴스화 한다. 순서는 directive 생성자의 dependency injection 순서에 의해 결정된다. 그리고 나서 최종 component directive가 인스턴스화 한다. 즉, 다음과 같다. 


>> Parent Element 

     template directive --> decorator directives --> component directive

         >> Child Elements

                template directive --> decorator directives --> component directive




templates에 데이터 바인딩 설정 방법


elements 또는 directives의 events 와 바인딩 하기


형식: @on-[event name]="[expression]" 

        예) <button on-click="doSomething()">

의미: click 이벤트가 발생하면 doSomething을 호출한다. 

DOM events를 기반으로 하는 thrid party 이벤트 시스템을 바인딩 할 수 있고, Directives에 새로운 이벤트를 정의해도 listener를 달 필요가 없다.



elements 의 properties 와 바인딩 하기


형식: @bind-[property name]="expresion" 

         예) <input bind-value="user.name">

의미: expression인 user.name에 대한 값 변경을 감시하다가 element property값을 갱신 한다. 


만일 element의 property가 바뀌었는데 expression은 writable하지 않거나, expression의 값이 바뀌었는데 property가 writable하지 않으면 에러가 발생한다. 양방향 데이터 바인딩은 directive와 element property 사이에 일어나기 때문이다. element의 attribute로 하지 않고 property로 바인딩 하는 이유는 다음과 같다. 


  - 거의 대부분의 attribute는 대응하는 property를 가진다. 만일 대응하는 property가 없으면 attribute가 대신한다. 

  - element properties는 항시 현재 값을 가진다. 그러나 attributes는 때때로 초기값을 지정해 주어야 한다. 예) <input>의 value attribute.

  - native attributes는 브라우저가 알아서 자동으로 대응하는 property 값을 바꿔준다. 

    예) <img>의 src property는 src attribute 바뀌면 자동을 변경된다)



elements의 properties 넣기 


형식: @[property name="a {{ [expression] }} b"

        예) <input title="some Text {{someValue}}">

의미: <input bind-title=" 'a ' + someValue + ' b' ">  


{{}} 를 사용하면 항상 string으로 변환하고, 단방향 연결이 된다. Angular1 directive의 @ 과 같다. 



text 넣기


형식: {{ [expression] }}

        예) hello {{user}}

의미: hello <span bind-text-content=" ' ' + user "> 


text node 내역과 데이터 바인딩도 항시 string으로 변환되고, 단방향이다. 



Guide-2 이어보기



<참조>


  - Templating 디자인 가이드 

  - Template Syntax 개발자 가이드 



신고
posted by peter yun 윤영식
2016.01.12 07:51 Angular/Components

이번에는 앵귤러 컴포넌트가 어떻게 웹 컴포넌트가 될 수 있는지 보자. 본 내용은 앵귤러 2의 디자인 가이드 중 Web Components에 대한 내용을 바탕으로 한다. (디자인 문서는 초기 버전이고 최신 버전의 변경분 반영이 안될 수 있다. 말 그대로 초기 생각의 흔적으로만 보고, 정의한 것들이 확정되었다곤 생각하지 말자. 따라서 변경된 내역도 나중에 찾아 보아야 한다)



Angular Component as a Web Components 


Angular Component가 Web Components가 되기위한 기본은 events, properties와 자바스크립트나 타 프레임워크에서 접근가능하고 Web Components에 적용되는 methods(API)를 갖는 DOM element를 만드는 것이다. Template 디자인 가이드에 나온 아래 그림을 보면 Template을 갖는 Component Directive로 만들면 Web Components로 Export할 수 있고, Import할 수 있다. 





Publishing events, properties, methods


앵귤러로 컴포넌트를 만들어 보자.

@ComponentDirective({

  selector: 'x-zippy'

  template: ...

})

class ZippyComponent {

  constructor(events) {

    this.events;

  }


  @Property('title')

  get title() ...;

  set title(value) ...;


  @Property('isOpen')

  get isOpen() ...;

  set isOpen(value) {

    if (value != this.isOpen) this.toggle();

  }


  @Publish('open')

  open() {

    this.isOpen = truefalse;

    this.events.fire('open');

  }


  @Publish('close')

  close() {

    this.isOpen = falsetrue;

    this.events.fire('close');

  }


  @Publish('toggle')

  toggle() {

    value ? close() : open();

  }

}


@Proptery 와 @Publish 에노테이션을 통해 앵귤러가 앵귤러 프레임워크없이도 엘러먼트 자체에 접근할 수 있는 properties/methods를 노출하고 있다. 사용예는 다음과 같다.

<x-zippy title="Hi YoungSik">

   Some thing...

</x-zippy>


native 엘러먼트처럼 사용되어 지는 컴포넌트가 되기위해 다음과 같이 동작해야 한다. 

var tab = _lookup_custom_element;

var log = null;

tab.addEventListener('open', function() { log = 'open'; })

tab.addEventListener('close', function() { log = 'close'; })


tab.close();

expect(tab.isOpen).toEqual(false);

expect(log).toEqual('close');


tab.toggle();

expect(tab.isOpen).toEqual(true);

expect(log).toEqual('open');  


 

Custom Element 등록하기(Registering)


Angular Component가 Web Components가 되기 위한 마지막은 앵귤러의 스크프밖에서 인스턴스화 할 수 있어야 한다. 이를 위한 기본 생각은 다음과 같이 Angular가 customElementFactory를 registerElement API를 통해 등록하는 방식이다. 

var injector = angular.createInjector(setOfModules);

var customElementFactory = injector.get(CustomElementFactory);

var Greeter = customElementFactory(GreeterDirective);

document.registerElement('x-greeter', { prototype: Greeter });


var div = document.createElement('div');

div.innerHTML = '<greeter>'; // trigger internal DOM parser.

var greeter = div.firstChild; // retrieve the greeter


// Notice that we are now instance of Greeter

expect(greeter instanceOf Greeter).toBe(true);


// Notice that it can be used just like any other DOM Element

greeter.name = 'world';

greeter.greet();



이후 디자인 가이드 내용이 갑작이 끝나버린 관계로 다른 문서들을 뒤적여 보기로 한다.   To be continued...



<참조>

  - Web Components 디자인 가이드 


신고
posted by peter yun 윤영식
2016.01.11 07:58 Angular/Components





앞으로의 Web App 개발은 Component기반 개발 방식으로 진행될 가능성이 높다. 아니 그렇게 될 것으로 보인다. 왜냐하면 표준 제정 기구와 기업이 원하고 그안에서 일하는 개발자들이 원한다. 여기서 기구는 W3C에서 스펙작업을 하고 있고, 기업은 구글형님이 열심히 삽질하며 진행을 하고 있다. 역시 삽질 내공은 구글이 참 잘 하는것 같다. 돈이 많아서 그런다기 보다는 미래의 먹거리를 위해 열심히 뛰는 양상이다. 


애플은 여기서 한발 물러나 있다. 그들의 주 수입은 하드웨어판매와 플랫폼에서 발생하는데, 여기서 말하는 플랫폼은 iOS 장터에서 거래되는 대부분의 수익이 Native App에서 발생한다. 따라서 굳이 Web에 열심히 힘쏫을 필요가 없다는 이야기다. 애플이 그냥 조금씩 서두리지 않고 곁눈질만 하는 놈이라면 구글은 광고로 돈을 벌고 그 광고는 웹에서 발생하니 웹의 외연 확장을 위해 Web Components는 중요한 기술로 생각한다. 그래서 열심히 3년전부터 구글 I/O를 통해 Polymer의 진행상황을 공유하고 있고, 꾸준히 삽질해 주면서 업계를 리딩할 것으로 보인다. 



"Web Components는 웹상(DOM/CSS 렌더링 엔진이 탑재된 모든 것)에서 Native Element와 같은 지위를 얻어 컴포넌트 기반 개발을 가능케 하는 핵심 스펙이다." 



라고 나는 생각한다. 기존 브라우저에서는 기본 정의된 Tag들만 해석을 할 수 있지만, 얼마든지 내가 만든 컴포넌트가 브라우저가 Native하게 해석을 해준다면... 그러니 Native 엘러먼트가 되면 Angular, Ember, React, Knockout 같은 프레임워크나 라이브러리 컴포넌트와 자연스럽게 붙여서 상호작용토록 사용할 수 있지 않겠는가? 앵귤러를 예로 들면 AngularJS v1.* 에서 Web Components와 엮어서 써볼려는 눈물겨운 노력도 있었지만 말 그대로 그냥 시도라고 보자. 근데 이상한 것은 Angular 도 구글! Web Components의 Polyfill 라이브러리인 Polymer도 구글! Future Web App Developments 방향이 Component based Development이니 Angular 프레임워크도 이 방향으로 새롭게 갱생할 필요를 느꼈을 것이다. 그래서 2년전부터 열심히 개발중인 Angular v2.* 에서 Web Components와 함께 쓸 수 있는 방향을 정의했다. 


무엇이라고 이야기하는지 보자.

allow Angular to consume any Web Components without resorting to some Angular unique APIs and or conventions. (Proposal for other frameworks require that component be aware of framework specific API )

  웹 컴포넌트가 네이티브로 인지 되면서 자연스럽게 Angular가 웹 컴포넌트를 소비할 수 있을 것이다. 이를 위해 Angular가 장치를 마련해 두려고 노력을 하고 있고, Misko가 컨퍼런스에서 이야기 하고 있다. 


- allow Angular components to be packaged as Web Components and be used in other frameworks or in vanilla JavaScript.

  앵귤러 컴포넌트가 웹 컴포넌트로도 패키지 된다. 


ng-conf 2015에서 미스코가 말한 Angular1에서 Angular2로 가면서 Syntax 표현이 바뀌게 된 이유와 Web Components를 어떻게 Angular2와 결합해 사용할 수 있는지 설명한다. 좀 박수를 쳐주고 싶다. 놀라울 따름... 잘 모르겠으면 아래의 디자인 가이드를 보자. 




Custom Element Interface


브라우저가 제공하는 엘러먼트와 Custom 엘러먼트는 구분을 하지 않고 모든 것을 DOM element로 보는 것이 웹 컴포넌트임을 전제로 한다. DOM element의 API 구성은 다음과 같다.


  - DOM events

  - DOM attributes 

  - Element properties 

  - Element methods 


DOM은 Domcument Object Model의 약어이고 이는 HTML/XML을 객체로 변화하고 객체 안에 어떤 API가 있어야 하는지 정의해 놓았다. 일단 모든 DOM의 최상위 인터페이스는 Node이고 하위로 크게 3가지인 Document, Element, Text가 존재한다. Custom element API는 events, attributes, properties, methods를 갖는다고 볼 수 있고 해당 엘러먼트에 대해서 두가지 관점으로 접근한다면 "사용하는 입장(Consumer)"과 "엘러먼트는 구현하는(implementer) 입장"이 있다. 먼저 엘러먼트 접근 사용하는 경우를 보자. 



Interface Consumer


접근해서 사용하는 입장의 4가지 경우. 여기서 native와 custom element간의 접근/사용에 대한 방법상의 차이는 전혀 존재하지 않는다.

var element = ... // element를 얻어온다


// DOM attributes

element.setAttribute('title', 'hi dowon');


// DOM event

element.addEventListener('change', ...);


// Element property 

element.value = 'youngsik';


// Element method 

element.focus();



Interface Implementer


Element를 구현하는 입장은 Custom Element를 만드는 것이고 Native element와 동일한 위치를 부여 받도록 Web Components 스펙을 제정한 것이다. 이때 구현을 위해 필요하는 영역으로 크게 4가지가 있다. 


  - Custom Element

     엘러먼트를 어떻게 만들지 API를 정의한다. 

     HTMLElement.prototype을 확장한다


  - HTML Templates

    엘러먼트의 내용을 Out of Box로 분리해서 만드는 방법을 정의한다. 

    <template id="my-element"> 태그안에 정의


  - HTML imports

    템플릿의 확장자는 .html이고 이를 import하는 방법을 정의한다. 비동기적 import. 

    <link rel="import" href="my-element.html"> 


  - Shadow DOM

    템플릿을 import한 후 사용하는 방법을 정의한다. Out of Box로 JS, CSS를 격리(Boundary)한다. 

    Document 노드 밑에 Root Node는 HTML 이듯이 Custom Element 밑의 Root 는 Shadow Root (Node)이다. 

    var shadow = node.createShadowRoot(); 처럼 Component안에서 Root Node 역할을 하는 Shadow Root Node를 생성한다. 




DOM Elements and Data-binding Frameworks


Custom Element와 앵귤러와 같은 프레임워크간에 데이터 바인딩을 위해 별도의 규칙을 강제하면 안된다. 이러한 문제를 프레임워크에서 해결을 해야한다. 데이터 바인딩에는 두가지 방향이 있다. 


  - 엘러먼트에 데이터 쓰기

     native 엘러먼트에 있는 API를 사용해 property 또는 attribute에 접근한다.  

  

  - 엘러먼트에서 데이터 읽기 

    항시 property를 읽어온다. attribute는 tag 에 표현되는 정적이 값으로 최초에 설정되고 그 이후 바뀌지 않고 tag가 DOM tree안의 Object 변경되어 property를 접근할 수 있다. 또한 attribute는 변경이 안되지만 property는 변경이 가능하다. 


앵귤러에서 이러한 property 변경을 감지하기 위해 Change Detection 이 필요하다. 일단 변경하는 방법은 엘러먼트의 속성(property)를 통해 아래와 같이 가능하다. 

// element 얻어오기 

var input = ...;


// 변경이 감지되었을 때 element의 property를 통해 변경을 수행

input.value = newValue;


그렇다면 변경의 감지는 어떻게 할까? 프레임워크도 DOM events를 이용해서 아래와 같이 감지하는 방법이 필요하다. 

var input = ...;


// 프레임워크가 input event를 등록한다. 

imput.addEventListener('input', () { 

   // 값을 읽어온다. 

   var newValue = input.value;


  // 변경 감지 시스템에 알려준다. 

  framework.notify(input.value);

});


그럼 Web Components 스펙으로 작성된 엘러먼트의 내부 상태 정보의 변경을 어떻게 Detection할까? 이에 대해 Web Components에는 스펙이 없다. 따라서 native element의 기존 이벤트를 적절히 사용해야 한다. 



Angular and Web Components


앵귤러가 Custom 엘러먼트를 어떻게 사용하는지 프롬프트 창에 텍스트를 입력하는 Web Components 예제를 통해 알아보자.


 

프롬프트 API 


  - Element properties 종류 

    + title: 다이얼로그 타이틀 텍스트

    + content: 사용자가 타이틀을 수정하면 반영될 property. 컴포넌트는 edited 이벤트 발생 

    + visible: 다이얼로그가 보이면 true, visible의 변경에 따라 open/closed 이벤트 발생


  - Element methods 종류 

    + open()

    + close()


  - DOM Attributes

    + accept-text: accept 버튼에 보여지는 텍스트

    + cancel-text: cancel 버튼에 보여지는 텍스트 


  - DOM Events 

    + accepted: accept 버튼을 클릭하면 발생하는 이벤트 

    + canceled: cancel 버튼을 클릭하면 발생하는 이벤트 

    + open: 다이얼로그 박스가 오픈하면 발생하는 이벤트

    + close: 다이얼로그 박스가 닫히면 발생하는 이벤트 

    + edited: 다이얼로그박스에 텍스트 입력할 때 발생하는 이벤트 


var Prompt = Object.create(HTMLElement.proptype, {

  // property 정의

  title: { get: function() {...}, set: function(value) {...}},

  content: { get: function() {...}, set: function(value) {...}},

  visible: { get: function() {...}, set: function(value) {...}},

  

  // attribute notification 정의 

  attributedChangedCallback(name, old, new) {...},


  // methods 정의

  open: function() {...},

  close: function() {...} 

});


document.registerElement('x-prompt', { prototype: Prompt });



Web Components 인스턴스 만들기


커스텀 엘러먼트로 사용하기 위해 registerElement를 해서 Web Component로 등록을 하면 이름을 통해 언제나 DOM Element를 인스턴화할 수 있다. 아래 보는 것처럼 앵귤러에서 사용하기 위해 앵귤러에서 추가 조치로 해야할 것이 없다. 

<div ng-repeat="item in items">

  <x-prompt></x-prompt>

</div>

 

 

이벤트 리스닝(Listening)


Custom Element가 자신의 open 이벤트를 발생시키면 Angular에서 리슨(Listen)할 수 있어야 한다. Angular 1에서는 ng-click같은 디렉티브가 설정되어 있어야 리슨을 할 수 있었다. 즉, 버전 1 로 하면 open 이벤트에 대한 디렉티브가 있어야 한다는 소리이다. 그래서 Angular 2는 디렉티브 없이 on-*을 붙이면 커스텀 이벤트도 리슨할 수 있게 제안하고 있다. (위에 ng-conf 2015에서 미스코가 후반부에 Web Components 사용예를 보면 (close)="doSomething()" 으로 처리한다. 예제)

<x-prompt on-close="doSomething()">


 

Attributes/Properties에 expression 바인딩하기 


앵귤러 데이터 바인딩은 expression이 변경되면 연결된 destination 값도 자동으로 바뀌게 한다. 앵귤러는 attribute 또는 property에 변경된 값이 쓰여지는 것을(be written) 알아야 한다. 실험적으로(heuristic) 아래와 이를 찾아낸다.

function updateValue(element, name, value) {

  // register에서 행위를 찾는다

  var useProperty = checkOverrides(element, name);

  if(useProperty == null) {

    // use heuristic

    useProperty = name in element;

  }

  if(useProperty) {

    element[name] = value;

  } else {

    element.setAttribute(name, value);

  }

}


위의 방법으로 다음을 해석해 보면

<x-prompt accept-text="I {{name}} accept" 

                   cancel-text="Cancel"

                   bind-title="contract.name">


  - cancel-text="Cancel"; 은 cancel-text Attribute 값으로 "Cancel" 문자를 updateValue(element, 'cancel-text', 'Cancel');로 호출한다. 

  - accept-text="I {{name}} accept"; 도 accept-text Attribute 값으로 expression을 문자로 변화하여 updateValue(element, 'accept-text', 'I ' + name + ' accept'); 로 호출한다. 

  - bind-title="contract.name"; 은 양방향 바인딩으로 엘러먼트의 title 프로퍼티와 contract.name이 맵핑어 expression이 바뀌면 title 프로퍼티도 업데이트 되어진다. 



Web Components 얻


최신 소스를 보면(ng-conf 2015의 youtube 예제) 다음과 같이 나온다. (앵귤러 디자인 가이드 문서를 보면 역시 글로 쓰여진 것의 업데이트하기는 거기나 여기나 비슷한가 보다. 써놓고 신경을 쓰지 않으면 업데이트 하기가 힘들어서 초기의 생각의 흔적만을 볼 수 있고, 변경된 최신 내역을 볼 수가 없다. 여기서도 마찬가지다)

<google-youtube chromeless="0"

   #player

   [videoid]="video.id"

   (googleyoutubestatechange)="setState($event.detail.data)">

</google-youtube>

<button *ng-if="!isPlaying()" class="md-button md-default-theme md-raised"

      (click)="player.play()">Play

</button>

<button *ng-if="isPlaying()" class="md-button md-default-theme md-raised"

      (click)="player.pause()">Pause

</button>


  - 일단 google-youtube Web Components에 #player 로컬 변수를 선언하고 Button 태그에서 사용을 하고 있다. 

  - (click)="player.play()" 또는 (click)="player.pause()"를 호출하고 있다. 

  - 디자인 문서에 따르면 on-click="player.play()" 그리고 ng-id="player", bind-videoid="video.id"와 같이 표현을 해야할 것이다. 어떻게 하든 많은논의를 통해 디자인에서 이야기 내용이 좀 더 발전적으로 변경된 모습을 볼 수 있다. 



엘러먼트의 property/attribute 발견하기(Detecting)


커스텀 엘러먼트는 DOM attributes와 Element property를 변경할 수 있다. DOM attributes는 DOM4의 DOM Mutation Observers를 이용해 찾을 수 있지만 Javascript나 DOM API를 이용해 Element property의 변경을 발견하는 것은 쉽지않다. 그래서 앵귤러는 실험적(heuristic)으로 컴포넌트의 이벤트를 찾아 리스닝(Listening)을 하고 변경이 발생하면 컴포넌트에 대해서만 dirty checking을 하여 변경을 반영함으로 부정적인 성능 저하를 제거한다. (즉, 해당 엘러먼트의 이벤트를 찾아 리스닝을 걸어 놓는 방법은 Native에서 많이 사용하는 방법이고 이를 그대로 활용한다. Angular 1에서는 dirty checking이 모든 $scope의 변경을 체크했다면 이제는 컴포넌트 변경안에서만 dirty checking이 일어아는 샘이다)



다음 장에서는 앵귤러 컴포넌트가 어떻게 웹 컴포넌트가 될 수 있는지 알아본다.



<참조> 

  - Angular v2.* 의 Web Components 관계 정립 문서  

  - Web Components 소개 

  - Angular + Web Components 사용 youtube 예제 소스 (ng-conf 2015)




신고
posted by peter yun 윤영식
2015.11.14 17:54 Angular/ES2015 & TypeScript

Angular2 구현 언어는 TypeScript이다. TypeScript를 배우기 위한 준비과정을 알아본다. 








Transpiler


  ES2015(EcmaScript 6) 스펙은 모던 브라우저에서 완벽히 구현되어 있지 않기 때문에 최소한 ES5기준에 맞춰서 사용을 해야 한다. TypeScript는 ES2015를 포함해 정적 타입 시스템과 ES7에서 나오는 데코레이터 @을 지원하는 슈퍼셋이다. 따라서 Babel로 ES2015를 ES5로 트랜스파일(Transpile) 하지 않고 TypeScript만의 트랜스파일러를 사용해야 한다.  


$ sudo npm install -g typescript 


TypeScript 파일의 확장자는 .ts파일로 한다. 그리고 자바스크립 파일로 트랜스파일하기 위해 tsc 명령을  사용한다. 결과물로 동일 명칭의 .js 파일이 생성된다.


$ tsc  <fileName>.ts 






Editor 


  Microsoft에서 mac에서도 사용할 수 있는 전용 Code Editor(Visual Studio Code)를 내놓았다. 설치 후 어느 위치에서든 code . 하면 현재 폴더의 파일 구조를 가지고 에디터가 열린다. 그리고 .ts 파일에 대한 스마트 위저드 헬퍼가 자동으로 뜨기 때문에 MS Visual Studio에서 코딩하는 착각을 불러 일으킨다. 필자는 최근 Sublime 3에서 Atom으로 코딩 툴을 옮겼는데 참 흥미롭게 사용중이다. 큰 파일의 경우 약간의 성능저하를 견딜 수 있다면 사용해 보길 권한다. 




Atom을 사용한다면 atom-typescript 패키지 설치를 통해 Visual Studio Code에서의 .ts 에 대한 스마트한 기능(Auto Complete, Type Info on hover...)을 그대로 사용할 수 있다. 설치후 F6를 클릭하면 tsc <fileName>.ts 명령을 수행하고 결과를 Notify UI 창으로 성공 실패여부를 알려준다. 


 





tsconfig.json


  tsconfig.json 파일은 TypeScript를 위한 프로젝트 단위 환경 파일이다. Exclude 하고 싶은 폴더나 파일, Compile 옵션, File 옵션등을 설정 할 수 있다. TypeScript의 컴파일러인 tsc 명령 수행시 참조한다. tsconfig.json의 전체 스키마를 참조한다.  또는 TypeScript Deep Dive 깃북의 설명을 참조한다.


{

    "exclude": [

        "node_modules",

        "bower_components"

    ],


    "compilerOptions": {

        "target": "es5",

        "sourceMap": true,

        "module": "commonjs",

        "declaration": false,

        "noImplicitAny": false,

        "removeComments": true,

        "noLib": false

    }

}






TypeScript 배우기 


TypeScript를 배우기 위해 ES2015의 문법을 먼저 익히는게 좋다. 

  - ES2015-features 사이트에서 새로운 기능들이 ES5 스펙과 어떤 차이가 있는지 눈여겨 보자. 

  - 동영상 강좌를 본다. 필자는 egghead.io의 동영상을 먼저 참조한다. (Pro는 유료이다)


공식홈페이지는 http://www.typescriptlang.org/ 이고, TypeScript 구현체 자체도 오픈소스로 깃헙에 공개되어 있다. 

  - 공식홈페이지에서 제공하는 TypeScript 스펙 배우기

  - TypeScript Deep Dive 깃북






TSD


TypeScript Definition Manager의 약어이다. 이미 나와있는 프레임워크(angular), 라이브러리(jquery) 같은 것을 사용하면서 애플리케이션을 TypeScript로 작성할 때 이들 구현체의 정의 내역을 미리 알고 코드에서 가이드를 주기위한 파일이다. 확장자는 angular.d.ts 또는 jquery.d.ts처럼 <name>.d.ts 가 붙는다. 예로 $('.awesome').show(); 라고 하면 타입스크립트는 $를 알지 못 한다. 먼저 declare var $:any; 라고 해주어야 사용할 수 있다.  


$ npm install -g tsd


tsd를 설치하고 tsd install <name> 으로 설치하면 수행위치에 typings 폴더 밑으로 파일을 다운로드한다. 


$ tsd init

// tsd.json 파일 

{

  "version": "v4",

  "repo": "borisyankov/DefinitelyTyped",

  "ref": "master",

  "path": "typings",

  "bundle": "typings/tsd.d.ts",

  "installed": {

    "jquery/jquery.d.ts": {

      "commit": "efd40e67ff323f7147651bdbef03c03ead7b1675"

    },

    "angularjs/angular.d.ts": {

      "commit": "efd40e67ff323f7147651bdbef03c03ead7b1675"

    }

  }

}


$ tsd install jquery

$ tsd install angular



이미 만들어져 있는 d.ts 파일을 이곳에서 http://definitelytyped.org/tsd/ 찾아 볼 수 있다. DefinitelyTyped 공식 홈페이지를 참조하자.






WorkFlow 


 Atom 에디터와 기본 툴을 설치했으니, 프로젝트를 위한 워크플로우를 만들어 보자.   


  - 프로젝트 폴더 만들기 

  - NodeJSGit 설치 

  - "npm install gulp -g" 설치 

  - npm 초기화 및 develop dependencies 설정 

$ npm init 


// package.json 첨부

"devDependencies": {

        "gulp": "^3.8.11",

        "gulp-debug": "^2.0.1",

        "gulp-inject": "^1.2.0",

        "gulp-sourcemaps": "^1.5.1",

        "gulp-tslint": "^1.4.4",

        "gulp-typescript": "^2.5.0",

        "gulp-rimraf": "^0.1.1",

        "del": *,

        "superstatic": *,

        "browser-sync": *

    }


// 의존 라이브러리 설치

$ npm install 


  - tsd 설치하고, init 초기화 명령으로 tsd.json 파일과 typings 폴더에 tsd.d.ts 파일을 자동 생성한다.

$ npm install -g tsd 


$ tsd init 



  - angular 의 TypeScript Definition 파일을 설치해 보자. 더 많은 라이브러리는 이곳에서 http://definitelytyped.org/tsd/ 에서 찾을 수 있다.

$ tsd install angular --save

 - angularjs / angular

   -> jquery > jquery


>> running install..

>> written 2 files:

    - angularjs/angular.d.ts

    - jquery/jquery.d.ts



$ tsd install jquery --save

 - jquery / jquery


>> running install..

>> written 1 file:

    - jquery/jquery.d.ts



  - gulpfile.config.js와 gulpfile.js를 https://github.com/DanWahlin/AngularIn20TypeScript 에서 복사해서 만든다. Gulp Task 설명


  - ts 파일 lint를 위해 tslint.json파일 만든다.

{

    "rules": {

        "class-name": true,

        "curly": true,

        "eofline": false,

        "forin": true,

        "indent": [true, 4],

        "label-position": true,

        "label-undefined": true,

        "max-line-length": [true, 140],

        "no-arg": true,

        "no-bitwise": true,

        "no-console": [true,

            "debug",

            "info",

            "time",

            "timeEnd",

            "trace"

        ],

        "no-construct": true,

        "no-debugger": true,

        "no-duplicate-key": true,

        "no-duplicate-variable": true,

        "no-empty": true,

        "no-eval": true,

        "no-imports": true,

        "no-string-literal": false,

        "no-trailing-comma": true,

        "no-trailing-whitespace": true,

        "no-unused-variable": false,

        "no-unreachable": true,

        "no-use-before-declare": true,

        "one-line": [true,

            "check-open-brace",

            "check-catch",

            "check-else",

            "check-whitespace"

        ],

        "quotemark": [true, "single"],

        "radix": true,

        "semicolon": true,

        "triple-equals": [true, "allow-null-check"],

        "variable-name": false,

        "whitespace": [true,

            "check-branch",

            "check-decl",

            "check-operator",

            "check-separator"

        ]

    }

}


  - Atom에서 atom-typescript를 설치해서 사용한다면 tsconfig.json도 만든다. 아래와 같이 설정값을 입력한다. 설정 값이 있어야 atom-typescript 기능을 사용할 수 있다. 보다 자세한 tsconfig.json 작성 문법을 참조한다.

{

    "compilerOptions": {

        "target": "es5",

        "module": "commonjs",

        "declaration": false,

        "noImplicitAny": false,

        "removeComments": true,

        "noLib": false

    },

    "filesGlob": [

        "./**/*.ts",

        "!./node_modules/**/*.ts"

    ],

    "files": [

        "./globals.ts",

        "./linter.ts",

        "./main/atom/atomUtils.ts",

        "./main/atom/autoCompleteProvider.ts",

        "./worker/messages.ts",

        "./worker/parent.ts"

    ]

}


  - gulp 명령을 수행하면 .js 파일과 .js.map 파일이 생성된다. 


  - TypeScript 소개 영상과 소스를 확인해 보자.






Start Kit 


  yeoman의 generator 처럼 TypeScript프로젝트 관련한 스타트 깃을 사용해 보자. 물론 angular2 기반으로 프로젝트를 가정한다. 

  - AngularClass에서 제공하는 angular2-webpack-starter 

  

   


  - AngularClass에서 제공하는 Angular2의 Server Rendering을 접목한 Universal Starter





참조 


  - TypeScript Workflow with Gulp

  - TypeScript in Awesome Angular2

 - Atom TypeScript 패키지 : atom-typescript



신고
posted by peter yun 윤영식
2015.06.22 23:20 Angular/ES2015 & TypeScript

  ECMAScript는 자바스크립트의 토대를 구성하는 스크립트 언어 명세이다. ECMA는 "Europen Computer Manufacturers Association"의 약어로 국제 표준화 기구이며, ECMAScript는 ECMA-262, ECMA-402 스펙안에서 표준화되었다. ECMAScript 3.0이 1999년 발표된 이래로 ECMAScript 5.0 이 2009년 발표되고 5.1이 2011년 그리고 마침내 ECMAScript 6.0이 2015년 6월 공표되었다. 모던 브라우저가 대부분 ECMAScript 5.0  을 지원하 있고, 현재 브라우저가 5,6,7 버전들에 대해 어느 범위까지 지원하는지 테스트 해볼 수 있다. 


  Angular 2는 TypeScript를 기반으로 개발되고 있고, TypeScript는 ES6를 포함해 별도의 추가 기능을 포함한다. ES6 기반으로 애플리케이션을 개발할 경우 BabelJS를 통해 ES5로 컴파일할 수 있다. 따라서 ES6에 대해 알아보고 필요한 문법을 사용한 후 애플리케이션 배포시에 ES5로 적용로 한다. Babel 사용을 자동화 하기위해 Browserify또는 Webpack을 사용하거나 Grunt/Gulp를 사용할 수도 있다.  




기초 문법 익히기


  - 문법을 보기전에 드미트리 소스니코드의 설명을 잠시 훑어보자

    


  - ECMAScript 6에 새롭게 추가된 기능 알아보기

  - ECMAScript 6를 5와 비교해서 실습하기 

  - Exploring JS ES6 무료 서적





ECMAScript 5 전환 자동화하기


  - Babel을 Browserify와 연동하기, Babel을 Browserify+Gulp 연동하기

  - Babel을 Webpack과 연동하기

  - Babel을 Gulp와 연동하기

  - Babel을 Grunt와 연동하기




참조 

  - ECMAScript 6.0 스펙  (PDF)

  - ECMAScript 버전별 자료

저작자 표시 비영리 변경 금지
신고
posted by peter yun 윤영식
2015.01.03 19:25 Angular/Components

구글 폴리머기반으로 자신만의 Web Components 를 만들어 보자. 아주 초간단 웹 컴포넌트이지만 기반 컴포넌트를 설치하고 어떻게 구동되는지 손으로 익혀본다. 




설치하기 


  - 폴러머를 시작하기 위해 zip파일을 받을 수 있지만 클라이언트 컴포넌트 의존관리 툴이 바우어(Bower)를 사용한다. 

     바우어는 노드(Node.js)기반위에 돌ㄹ아가기 때문에 노드를 설치하고 바우어도 설치한다. 

  - 바우어를 통해 프로젝트를 초기화 해보자. bower init 후에 그냥 끝날 때가지 엔터를 친다. bower.json 환경파일이 생성된다. 

$ mkdir tutorial && cd tutorial

$ bower init 


  - 폴리머를 설치한다. polymer.js 는 Web Components를 지원하기 위한 polyfill 파일인 webcomponents.js 파일도 포함한다. 

    + polyfill은 Web Components 스팩을 지원하지 않는 브라우져에서도 Web Components를 사용할 수 있도록 해주는 파일이다. 

       따라서 과거 브라우져는 webcomponents.js (과거는 platform.js였음) 파일을 추가해야 한다. 

     + Chrome 36+ 이상 버전은 Web Components 스팩이 구현되어 있기 때문에 polyfill 파일이 필요없다. 

$ bower install --save Polymer/polymer




처음 만들어 보는 웹 컴포넌트 


  - 웹컴포넌트를 만든 후 html안에서 사용한다. 

  - 먼저 폴리머 웹 컴포넌트를 정의한다. 

    + 폴리머 엘러먼트를 사용하기 위해 polymer.html을 추가한다. 

    + 사용자 정의 웹컴포넌트 이름은 hi-dowon이다. 구분은 "-" 을 꼭 사용한다. 

    + html에서 사용할 때 public으로 노출 시킬 attribute로 name을 지정한다. 

    + <template>와 <script>에서 상호 작용한다. 스크립트 태그에 지정한 값이 template 쪽으로 {{}} 사용해 양방향 바인딩 된다. 

tutorial> mkdir elements && cd elements

tutorial/elements> vi hi-dowon.html 


// hi-dowon.html 내용 

<link rel="import" href="../bower_components/polymer/polymer.html">


<polymer-element name="hi-dowon" attributes="name">

  <template>

    <span> This is <b>hi-<span style="color: {{color}}">{{owner}}</span></b> tag <br>

    <span> And hi {{name}}</span><br>

    <input id="message" value="{{name}}">

    <button on-click="{{setFocus}}">Set Focus in name input</button>

  </template>

  <script>

    Polymer({

      owner: 'Dowon',

      color: 'red',

      name: 'youngsik',

      setFocus: function() {

        this.$.message.focus();

      }

    })

  </script>

</polymer-element>


  - 이제 elements/hi-dowon.html 파일을 사용하는 index.html 파일을 작성한다. 

    + 웹 컴포넌트는 HTML imported를 통해 사용하고자 하는 웹컴포넌트를 조합(Composition)해 확장(Scale-out)하는 방식이다. 

    + 모던 브라우져가 아닌 구버전의 브라우져에서 웹컴포넌트 스펙지원을 위해 polyfill파일인 webcomponents.js 파일추가한다. 

       Chrome 36+ 이상 버전은 polyfill 설정이 필요없다. 

<!DOCTYPE html>

<html>

    <head>

        <script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>

        <link rel="import" href="./elements/hi-dowon.html">

    </head>

    <body>

        <hi-dowon name="ok"></hi-dowon>

    </body>

</html>


  - 결과 수행

    + 파이썬의 심플 서버를 뛰어서 호출해 본다. 

tutorial> python -m SimpleHTTPServer 8000


// 결과 화면 : 버튼을 클릭하면 포커스이동 


  - 폴리머의 레이어는 3계층으로 나뉜다 

    1) Web Components : Web Components 스팩을 지원하지 않는 브라우져를 위한 web components polyfill 레이어 

    2) Polymer 라이브러리 : 사용자 정의 엘러먼트(Custom element)를 위한 선언적 신택스를 제공

    3) Elements :Core 와 Paper element와 같이 기정의된 웹 컴포넌트 제공. 




1. Polymer에서 Custom Elements


  - Custom Element is True Element. 사용자 정의 엘러먼트를 사용하는 이유를 보자.

  • Reduce the amount of code you have to write. : 누군가 만들어 놓은 엘러먼트가 있다면 Don't invent한다. 
  • Express the function of the code. : 
  • Encapsulate internal details. : 코드 내부를 캡슐화 할 수 있다 
  • Implement APIs per element type. : 엘러먼트별 API가 있고 애트리뷰트로 접근한다. 
  • Increase productivity by letting you reuse elements. : 엘러먼트를 조합해서 생산성을 높일 수 있다. 
  • Use inheritance to create element tags based on other tags. : extends 키워들 통해 custom element를 확장할 수 잇다


  - 웹 컴포넌트의 Custom Element는 다음과 같이 작성을 해야하지만 폴리머를 쓰면 단순히 <polymer-element> 태그를 사용하면 끝!

    + Customer Element 명칭을 지을 땐 반드시 "-"이 있어야 한다. (polymer element 선언 참조)

// 코딩 

var myTag = document.registerElement('my-tag');


// 선언적 태깅 

<polymer-element name="my-tag">

  <template> 

    <div> hello dowon </div> 

  </template>

  <script>

     Polmer();

  </script>

</polymer-element>


  - extends 키워드를 통해 기존 엘러먼트를 확장할수 있고, 부모의 메소드와 속성을 자식 엘러먼트가 상속받는다. 

    또한 오버라이딩도 가능한다. 

  - 엘러먼트 종류로 UI 렌더링되는 엘러먼트와 No-UI 엘러먼트를 만들 수 있다. 예로 <core-ajax>은 XHR 요청을 마크업으로 할 수 있게 한다. 

<core-ajax url="http://gdata.youtube.com/feeds/api/videos/" auto params='{"alt":"json", "q":"chrome"}' handleAs="json"></core-ajax>

<script>

  var ajax = document.querySelector('core-ajax');

  ajax.addEventListener('core-response', function(e) {

    console.log(this.response);

  });

</script>




2. Polymer에서 Shadow DOM


  - 프로그램적으로 Shadow DOM을 하려면 createShadowRoot를 넣어야 하지만 Polymer에서는 <template> 태그를 사용한다. 

    + <span> <content> <footer>는 다른 커스텀 엘러먼트안에 있을 때 완전 캡슐화되어 숨겨진다는 것을 명심하자. 

<polymer-element name="my-custom-element" noscript>

  <template>

    <span>People say: </span>

      <content select="qq"></content> 

    <footer>sometimes</footer>

  </template>

</polymer-element>

  

  - DOM은 3가지가 있다. Light DOM, Shadow DOM, Composed DOM

     + Light DOM : 하기와 같이 my-custom-element 안에 <qq> Hi dowon </qq>로 포함된 DOM 이다. innerHTML로 접근이 가능하다.  

<my-custom-element>

  <qq> Hi dowon </qq>

</my-custom-element>


    + Shadow DOM : <polymer-element> 태그 안에 <template> ... </template> 태그안에 정의된 DOM 이다. 

       DevTools에서는 #docuement-fragment로 표기되고 사용자에게 숨겨져있고, <my-custom-element> 자식노드로 접근이 안된다. 


    + Composed (rendered) DOM : 실제 브라우져에 렌더링되는 DOM 이다. 

       light DOM이 shadow DOM에 포함되어 조합된 DOM이 보여진다. (이것은 마치 AngluarJS의 Directive에서 transclude와 유사하다)

<my-custom-element>

  <span>People say: <qq>Hi dowon</qq></span>

  <footer>sometimes</footer>

</my-custom-element>




3. Polymer에서 HTML imports 


  - <link> 태그를 사용해 HTML 도큐먼트를 재사용하면 된다. 

<link rel="import" href="my-custom-element.html">




4. Polymer에서 Web Animations


  - Web Animation 관련한 CSS Transitions, CSS Animations, SVG Animations /SMIL 스팩이 있다. 

    + 구현체

    + 아직은 복잡한 것을 표현하기에 부족함이 존재

  - 애니메이션 cost를 줄이고, animation 스팩끼리 상호작용할 수 있고, 스팩 작성자나 브라우져 벤더가 한 곳에서 사용할 수 있게하자.

  - Web Animations 모델은 웹에 애니메이션 컨텐트를 엔진에 묘사하는 것이다. 

     엔진은 CSS Transitions, CSS Animations, SVG Animations 를 지원한다. 

    + 자바스크립트 API를 통해 애니메이션을 정의 

    + Animation 오브젝트를 사용 그외 AnimationEffect, AnimationGroup, AnimationSequence 오브젝트를 사용한다. 




5. Polyfill로서 Web Components


  - web components 기술이 모든 브라우져에 구현되어 있지 않기 때문에 webcomponents.js polyfill 파일이 필요함 

  - webcomponents.js 는 다음의 라이브러리를 포함하고 있다. 

  • Web Components:

  • Dependencies required by the Web Components polyfills:


  - 설치는 바우어로 하고 웹 컴포넌트를 사용하려는 HTML에 <script> 태그로 포함시킨다. 

$ bower install --save webcomponentsjs


// html

<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>




<참조> 

  

  - Polymer 10 minutes

  - Understanding web components

  - 구글 폴리머 튜토리얼


저작자 표시 비영리 변경 금지
신고
posted by peter yun 윤영식
2014.12.27 16:58 Angular/Components

Web Components(웹 컴포넌트)는 W3C에서 나왔다. 웹 도큐먼트의 표현을 위젯이나 컴포넌트로 빌딩하는 개념으로 핵심은 HTML 앨리먼트끼리 상호작용하고 인캡슐레이션되게 만드는 기술이다. 2013년2014년 Google I/O 에서 Web Components에 대해 바이들맨이 설명을 상세히 하고 있다. 또한 Web Components org 사이트가 존재하여 관련 정보를 얻을 수있다. 







Web Components 구성 요소 


    


  - Shadow DOM

    + DOM과 Style을 인캡슐레이션 해준다. 

    + 예전에는 iframe등을 통해 위의 효과를 얻었다. 

    + shadow dom 외부의 js는 접근이 안된다. 

    + shadow boundary의 style은 외부로 영향을 미치지 않는다. 

    + polyfill 이라고 브라우져가 지원하지않으면 기능을 지원할 수 있게 도와주는 스크립트 파일이라고 보면된다. 

       webcomponents.js 파일 같은 경우가 polyfill 파일이고 이것을 <script src="webcomponents.js"/> 하면 Shadow DOM에 대해 

       모든 브라우져에서 사용할 수 있다. 하지만 브라우져 버전은 확인해야 함. 


  - HTML Templates

    + <template id="name"> ... </template> 형태로 DOM에서 작용할 테그를 정의한다. 

    + <template> 태그에 있는 것은 DOM에 들어가지 않는다. 

    + 스크립트를 통해 DOM에 포함되는 구조이다. 

    + <template> 태그는 크롬과 몇개 브라우져만 지원하고 있다. 그러나 이것 역시 polyfill을 설치하면 모든 브라우져에서 수행가능함


  - Custom Elements

     + HTML에 새로운 태그를 정의하는 것이다. 마치 Angular의 Directive(지시자)와 유사하다. 

     + <element name="x-dowon"> ...</element> 으로 하면 <x-dowon> 태그를 사용할 수 있다. 

     + 앨러먼트의 lifecycle 콜백이 존재한다. 

     + 사용자 정의 앨러먼트를 만들기 위해 Shadow DOM, HTML Template 등이 필요한 것이다.

     + document에 register(<custom elements>)를 등록한다. 


  - HTML Imports 

     + <link rel="import" href="x-dowon.html"> 설정을 <head> 태그안에 둔다. 

     + 사용하려는 웹 컴포넌트의 확장자 .html을 import 타입으로 link 태그를 사용한다. 


    * 발표 자료 링크

    


    ** Web Components 스팩에 준수하여 개발된 Custom Element는 서로 조합하여 사용가능하다. 




Polymer와 Web Components


  - 구글이 웹 컴포넌트 스팩에 따라 구현한 것이 Polymer 이다. 파이어폭스는 X-Tags

  - Web Components의 구성요소를 지원하기 위해 Polymer는 다음과 같은 스택을 가진다. 

    + Native는 브라우져

    + Web Components 스팩 및 실제 필요한 몇가지 기능이 포함된 polyfill로서 platform.js 파일이 존재 

       현재는 WebComponents org의 webcomponents.js와 같은 명칭을 사용한다. 단 Chrome 36+ 부터는 polyfill이 전혀 필요없지만

       Web Components 기능을 구현하지 않은 브라우져들은 polyfill 파일을 포함해야 polymer.js를 통한 기능을 사용할 수 있다. 

    + Polymer 만의 API가 있는 polymer.js 코어

    + 구글의 API를 웹 컴포넌트로 만들어 놓은 Elements (Core, Paper)들이 있고 이를 토대로 Application을 만들 수 있는 길을 닦아 놓았다. 

   

  - Polymer와 Web Components에 대한 요약 정리

    


  - Polymer에서는 어떻게 쓰는지 바이들맨의 설명을 들어보자. (2014년 구글 I/O)

    + <polymer-element name="hi-dowon"> .. </polymer-element> 태그를 통해 Custom Elements를 만든다. 

    + <template> 태그를 사용하고 탬플릿안에서 {{ }} 머스태쉬를 사용해 Data Binding을 한다. (AngularJS 디렉티브의 @ 과 유사)

    + 물론 트위터 부트스트랩같은 UI F/W의 사용도 가능하다.(22분)

    + http://customelements.io/ 이나 http://component.kitchen/ 같은 곳에서 컴포넌트를 찾고 Bower를 통해 설치해서 사용한다. 

    + 구글의 다양한 서비스 API를 Polymer 기반 Custom Element로 만들어 놓았다. 

     


  - Material Design을 구현한 Polymer의 Paper Element 사용방법 (2014년 구글 I/O)

    + 머터리얼 디자인으로 웹/모바일 화면을 구현하고 싶다면 참조하자. 

    + RWD(Responsive Web Design, 반응형 웹 디자인)는 구글에서 Web Starter Kit 강좌에서 잘 설명해 주고 있다. 

    + Polymer Designer를 가지고 화면을 작성할 수 있다. (23분)

    + 하이브리드 앱 (Cordova)를 통한 예도 보여줌 (29분)

    + Layout : core-* element

       Material : paper-* element

       Theming : core-style

       Transition : core-animated-page

      을 통해 일반적으로 모바일에서 원하는 효과를 바로 얻을 수 있다. 

    + 그외 매튜 맥널티의 2014년 구글 I/O 마지막 동영상도 보자 

    


  폴리머의 장점은 재사용성에 있다고 본다. 디자이너와 같은 툴을 통해 미리 정의한 컴포넌트를 비즈니스에 맞게 가지고 있다면 사용자의 레벨에 따라 원하는 모바일/웹 환경을 쉽게 만들 수 있을 것이다. 따라서 반복적인 코드가 줄어 들 것이고 일관성있는 유지보수와 확장성을 보장할 것이다. 2년전 MEAN Stack을 통해 만들고 싶었던 것은 시각화 대시보드 저작도구 였다. HTML의 태그가 Custom Element처럼 표현이 되고 저작도구(Poly Designer)나 실시간에 해석되어 데이터를 실시간 차트로 보여줄 수 있는 BtoB 솔루션을 만들 수 있을 것이다. 이미 Flex 버전의 대시보드 저작도구를 개발해 보았지만 이제는 모바일/웹이 원활히 지원되는 기술로 바뀌어야 하기 때문에 Web Components는 나에게 더욱 필요한 머스트 아이템이 되겠다.  




<참조> 


  - 2013년 구글 I/O - Web Components Tectonic Shift

  - W3C의 Web Components Spec

  - Polyfill 개념

  - 2014년 구글 I/O - Web Components & Polymer (바이들맨의 Custom Element 정리)

  - Vanilla, X-Tags, Polymer 비교

  - Vulcanize : import 웹 컴포넌트 합쳐주는 도구

저작자 표시 비영리 변경 금지
신고
posted by peter yun 윤영식
2014.12.26 11:48 Angular/Components

새 술은 새 푸데에 넣어 보자. 모던 웹브라우져기반하에 구조적인 웹 애플리케이션을 개발할 수 있도록 해주는 Dart로 구글의 Web Component스펙 구현체인 Polymer를 시작하는 것도 재미 있을 것이다. Dart기반으로 개발을 하게 되면 Dart VM이 설치된 브라우져에서 수행이 되어야 하지만 현재는 Chromium + Dart VM을 결합한 Dartium에서만 수행이 되고 Dartium 은 별도로 설치해도 되지만 DartEditor를 설치하게 되면 포함되어 있어서 통합 개발환경하에서 디버깅 및 수행을 해볼 수 있다. 하지만 dart2js 도구를 통해 .dart 파일을 .js파일을 transpile할 수 있어 기존 모던 웹 브라우져에서 수행이 가능하다. 



 



Chrome Dev Editor 설치


  - 구글 I/O에서 소개한 크롬 개발 에디터는 다트와 웹컴포넌트로 만들어진 크롬앱이다. 설치를 한다.

  - 깃헙 사이트에서 Chrome Dev Editor를 볼 수 있다. 하기 내용은 Chrome App 으로 프로젝트를 생성한 경우이다. Dart 기반으로 생성이 되었다. 

    


  - 개발도구를 통해서 웹 컴포넌트를 추가하면서 개발도구를 업그레이드하는 과정을 보자. 흥미롭다. 

    


  - 현재는 개발자 프리뷰버전이 나와있어서 열심히 개밥(dogfood)을 먹으며 실험을 하고 있는 단계이다. 하지만 1년 2년이 지나면서 성능과 안정성 대중성이 확보되리라 생각한다. 물론 극히 개인적인 추론이지만 구글의 문화를 토대로 본다면 2013년부터 구글 I/O에서 계속 Angular, Polymer의 소개가 빠지지 않고 전략적으로 밀고 있다는 생각이 든다. 





Polymer 개념 잡기 


  - Web Component는 div soup에서 벗어나 사람이나 기계가 인지할 수 있는 의미있는 단어로 HTML을작성하고 이를 컴포넌트화 하는 것이다. 

    + 의미있는 단어로 HTML을 작성하기 위해 div 태그를 재정의 할 수 있어야 한다. 

       이는 Template을 통해 Custom Elements를 만든다

    + 컴포넌트화 하기위해서는 상태에 대한 것과 동작에 대한 것에 scope를 줄 수 있어야 한다. 

       상태 scope는 CSS를 Shadow DOM이라는 기념하게 Custom Element의 Template에만 적용되는 CSS를 작성할 수 있다. 

       동작 scope는 자바스크립트로 작성을 할 수 있고 Polymer를 사용하게 되면 Dart를 통해서도 가능하다. 

  - Web Components는 Angular의 Directive 지시자와 유사하지만 약간의 차이가 존재한다. UI와 스크립트 언어간의 two-way data binding하는 reactive programming은 동일하지만 그외의 부분은 지향하는 바가 틀리고 할 수 있는 영역이 틀리다. Polymer는 좀 더 강력한 웹 애플리케이션 UI를 빨리 만들 수 있도록 지원한다. 

    


  - 내가 좋아하는 애디 오스마니의 Polymer 소개 영상을 보자. 무려 한글로 해석되어 나온다. 

    





Polymer와 Dart의 관계


  - Dart는 구조적 웹 프로그래밍 언어로 구글에서 만들었다. 동적 타입 언어에 조금이라도 익숙하고 자바를 다루어 본 개발자라면 쉽게 배울 수 있는 언어이다. DartEditor는 Eclipse기반이고 import, inheritance, main() 등 자바에서 볼 수있는 것들을 잘 차용했다. 하지만 다트는 Strong static type 시스템인 자바와 틀리게 Strong dynamic type 시스템을 지향하고 Optional static type 적용이 가능하다. Web Component 스펙의 구현체인 Polymer와 Dart의 공통점은 모던 웹 브라우져에 돌아가는 애플리케이션을 빠르고 견고하게 만들 수 있도록 하는것을 지향한다는 것이다. Dart는 dart2js 도구를 제공하기 때문에 .dart를 .js로 변환하여 Dart VM이 설치되지 않은 브라우져에서 실행이 가능한 것이다. 따라서 Polymer에 Dart를 안해 볼 이유는 없어 보인다. 

  - Dartlang.org의 Polymer 페이지의 첫 문구이다. 위에서 이야기한 내용을 축약해서 표현해 놓았다. 

Use polymer.dart—a Dart port of Polymer—to build structured, encapsulated, client-side web apps with Dart and web components.


  - 구조

    + web 폴더가 있고 그안에 html 파일이 존재

    + pubspec.yaml은 Dart의 의존관계 및 메타정보에 대한 공식 설정 파일이다. 

      이것은 Node.js의 package.json이나 Bower의 bower.json과 동일하다. 

      


  - 의존관계로 polymer.dart를 설치한다. pubspec.yaml 설정 내역이다. 

dependencies:

  polymer: ">=0.15.1 <0.16.0"

transformers:

- polymer


  - Dart 기반으로 Polymer를 팔려면 우선 다음과 같이 진행하는 것이 맞다. 

    + 우선 Dart 개념을 잡아야 한다. 

    + 다음에 자바스크립트 기반으로 Polymer를 해본다. 

    + 마지막으로 다트 기반으로 Polymer를 접근한다. 

  - Polymer.dart와 AngularDart의 관계는 Polymer와 AngularJS의 관계와 유사하다. 서로 자바스크립트이냐 다트이냐의 차이일 뿐 상호 겹치는 부분의 Data Binding에서 상호 작용(Interoperation)할 수 있다. 


  만약 모던 웹 브라우져라는 제약이 존재 한다면 언어의 구분을 두지 말고 내가 원하는 웹 애플리케이션을 빠르고 견고하고 구조적으로 만들 수 있는지 고려해 시작하자. 감기로 인해 오늘은 여기까지... 




<참조>


  - 구글 I/O Polymer 소개 영상

  - Dartium 소개

  - Angluar와 Polymer 사이의 Data Binding

  - Reactive Programming Wikipedia

저작자 표시 비영리 변경 금지
신고
posted by peter yun 윤영식
prev 1 next

티스토리 툴바