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

Publication

Category

Recent Post

2016. 5. 1. 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 윤영식
2015. 5. 15. 11:38 React

ReactJS(리액트)는 자바스크립트로 UI 컴포넌트를 만드는 프레임워크이다. 만들어진 컴포넌트를 사용할 수도 있고, 수정할 수 있고, 조합할 수 있으며 자신만의 UI 컴포넌트를 직접 만들 수 있다. 즉 프론트앤드 UI의 화면을 컴포넌트 조합으로 만들고, 화면의 데이터를 앵귤러처럼 자동 업데이트 해주지만 앵귤러와 틀리게 단방향 데이터 바인딩이다. 리액트는 앵귤러(AngularJS)의 지시자(Directive)와 비교되지만 훨씬 간단하고 성능 또한 월등하다. 이제 배우고 익혀 사용할 때가 되었다. 







개념잡기 


  - 쿼라(Quora)에서 이야기하는 장/단점을 살펴보자. 

     장점

       + Virtual DOM 을 JS로 구현해서 UI 컴포넌트의 속도가 엄청 빠르다. 

       + Component의 재사용과 복잡한 UI 컴포넌트 조합이 가능하다.

       + Uni-direction(단방향) 방식하에 데이터가 변경되면 관련된 모든 UI 컴포넌트가 변경된다.

       + commonJS / AMD 패턴과 잘 조합되고, 크롬 익스텐션을 통해 디버깅할 수 있다. 

     단점

       + 초보자들에겐 역시 러닝 커브가 존재한다.

       + 다른 프레임워크가 동작하기 위해선 부가적인 작업들이 필요하다. 

       + 앵귤러처럼 router, model등 Frontend Full Framework을 제공하지 않아서 꼭 다른 프레임워크와의 조합이 필요하다. 


  - 처음에는 무조건 공식 홈페이지의 가이드를 보자. 

      + Quick Start

      + Advanced Guides

      + Reference


  - 프리젠테이션으로 정리해 보자. 재미있게 진행되니 WTF는 날리지 말자.

     

  - egghead.io 에서 리액트에 관련하여 ReactJS, React Native, Flux Architecture를 동강으로 제공한다. 물론 무료가 있다. 
    * 유료강좌를 듣길 추천한다. 앵귤러와 리액트에 대한 좋은 강좌가 많다. 한달 대략 15달러
    + Render MethodProperties, State 사용하기

  - 제이쿼리를 리액트로 바꾸는 과정을 잘 설명해 주고 있다. 글을 읽고 다시 egghead.io 를 보면 이해가 될 것이다. 

    + jQuery to React 가이드

       내용중에 앵귤러를 사용하고 있는 개발자라면 앵귤러의 지사자에서 scope: { createXXX: '&' } 방식으로 자식의 부모의 함수를 호출 할 수 있다

       그리고 자식, 부모간에 scope 객체의 상속을 통해 scope 속성을 접근할 수 있다는 것도 알 수 있다. 

    + Flux 소개 아래 영상을 보면 react가 Virtual DOM을 통해 더 낳은 성능과 단순성을 통한 버그 가능성 제거를 이룰 수 있는지 가늠할 수 있다

       * Flux 사이트

      


  - 이제 다시 설치부터 기초 개념을 문서를 통해 쭉 살펴보자. 

  - ng-conf 말고 react-conf도 있다. 동영상을 통해 향후 방향을 점쳐보자. 
    

  - 리액트 컴포넌트를 찾고 싶을 경우 


  - 리액트 자료 

    + Awesome 자료 목록들

    + 앵귤러와 결합해서 사용하기 : NGREACT 


  - 리액트 컴포넌트 프레임워크 : 트위터 부트스트랩같은 조합

   + reapp.io


  - 새 술은 새 푸대에 넣자. 리액트가 UI 컴포넌트라면 Webpack 또는 Browserify 를 이용해 모듈화 하자
    + 리액트를 위한 웹팩의 starter kit

  - 디버깅은 Chrome Extention 설치하고 시작
  - 필요에 맞는 툴과 환경을 선택한다.

 


Immutable Data에 대한 이해 

불변 데이터에 대해 알아 두어야 한다. 예로 리스트를 생성하고 거기에 새로운 값을 넣으면 리스트를 가르키는 레퍼런스는 같을까 틀릴까? 기존 방식이라면 동일한 레퍼런스이겠지만 Immutable의 세계에 오면 값이 변경 되면 새로운 객체가 된다. 리액트에서는 성능향상을 위해 Immutable.js를 사용토록 권장하고 있다. 이것에 대해 발표한 영상을 보고 영상을 요약한 블로그 글을 보도록 한다. 
  - 영상 설명 블로그 [1], [2], [3]
  - 발표 영상

  - 최근 보고 있는 Clojure는 기본적으로 Immutable Data이다. 클로져 관련 블로그를 읽어보자.



자바스크립트 개념 장착

리액트를 하다 보면 this와 bind(this)를 자주 사용할 때가 있고, ES6로 전환하다보면 좀 더 빈번히 보게 된다. 제대로 이해하고 React 코드를 보자 
  - bind를 해주는 이유 Callback 때문 그렇다면 Callback을 이해하자
  - Callback은 자바스크립트가 Funtional Programming이 가능하게 해주고 Closure이다. 클로저를 이해하자
  - React에서 Object만드는 것이 많이 나오니 자바스크립트의 Object에 대해 알아보자
  - http://javascriptissexy.com/ 에서 다른 글도 참조하시라


'React' 카테고리의 다른 글

[React] 다시 시작하기  (0) 2018.09.14
[React] Semantic-UI를 React 컴포넌트로 만들기 - 1  (0) 2015.08.23
[React] CSS Framework 선정하기  (0) 2015.08.15
[Flux] Flux 배우는 방법  (0) 2015.07.04
[React] AngularJS와 ReactJS 비교  (0) 2015.07.01
posted by 윤영식
2013. 4. 9. 09:56 AngularJS/Concept

재사용성을 위하여 사용자 정의 컴포넌트는 어떻게 만들 수 있는지 알아보자. Markdown 컴포넌트를 만들어 본다



1) 코딩하기 

  - <markdown> 태그안에 들어가는 마크다운 포멧을 해석하여 주는 showdown library는 GitHub에서 다운로드 받는다 

  - 해당 내용을 showdown 라이브러리에서 해석할 수 있도록 컴포넌트를 만든다 


  - 사전준비 

     + 소스복제 : git clone https://github.com/ysyun/prototyping

     + 모듈소스 : cd prototyping/angularjs/component


  - component.html 소스코드 

    + ng-app 태그를 통하여 어떤 모듈을 사용할지 지정

    + showdown 로컬에 다운로드

    + component.js 안에 myApp안에 사용할 markdown directive 를 정의한다

<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>AngularJS Hello World using Directives</title>
<script src="http://code.angularjs.org/1.1.4/angular.min.js"></script>
<script src="../../underscore/underscore-min.js"></script>
     <script src="../../showdown/compressed/showdown.js"></script>
<script src="component.js"></script>
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<markdown>
[[MobiCon]](http://mobicon.tistory.com/)

This is Test
============
    'function() { console.log('hi youngsik'); }
</markdown>


</body>
</html>


  - component.js 소스코드 

    + angular.module 정의

    + ng-app=myApp 에 대한 모듈이며, 의존관계 없음

    + new Showdown.converter()를 통하여 마크다운 포멧 해석기 생성

    + 'E' : DOM Element 지정, A-attribute, C-class, M-comment (참조)

angular.module('myApp', [])
    .directive('markdown', function() {
        var converter = new Showdown.converter();
        return {
            restrict : 'E',
            link : function (scope, element, attrs) {
                var htmlDiv = converter.makeHtml(element.text());
                console.log(htmlDiv);
                element.html(htmlDiv);
            },
// template: '<textarea rows="10" cols="10"></textarea>'
        }
    })


** 소스 : https://github.com/ysyun/prototyping/tree/master/angularjs/component



2) 동영상

  - component-1
 
  - component-2



<참조>

  - 원문 : http://blog.angularjs.org/2012/05/custom-components-part-1.html

              http://blog.angularjs.org/2012/05/custom-components-part-2.html

posted by 윤영식
prev 1 next