AngularJS Concept

AngularJS 사이트에서 AngularJS에 대한 개념 설명을 정리하고자 한다.

용어 :


ConceptDescription
Template추가적인 마크업을 포함한 HTML
Directives커스텀 속성(attributes)와 엘리먼트(elements)를 말하며 HTML을 확장한다. 
Modelview에서 사용자에게 보여주고자 하는 데이터이며, 사용자와 인터렉션을 하는 대상 객체이다. 
Scope컨트롤러, 디렉티브, 표현식 등이 접근할 수 있는 컨텍스트를 말하며, 이는 모델이 저장되는 곳이다.
Expressionsscope로 부터 변수 혹은 함수(functions)에 접근한다. 
Compiler템플릿을 파싱하고, 초기화 하는 명령어와 표현식을 초기화 한다. 
Filter사용자에게 노출되기 위해서 표현식의 값을 포매팅한다. 
View사용자가 보게될 내용 
Data Binding모델과 뷰사이에 데이터를 싱크한다. 
Controller뷰 뒷단에 있는 비즈니스 로직이다. 
Dependency Injection객체와 함수(functions)를 생성하고 엮어준다. 
Injector의존성을 인젝션하는 컨테이너이다. 
Module컨트롤러, 서비스, 필더, 디렉티브를 포함하는 app의 서로다른 부분에 해당하는 컨테이너이다. 이것은 Injector에 의해서 설정된다. 
Service재사용가능한 비즈니스 로직으로 views에 독립적인 객체이다. 

데이터 바인딩 : 

다음 예제는 수량과 가격을 계산하는 예제로 각 통화에 따라서 값을 노출하는 예이다.

--------------------------------------------------------------------------------------------
<div ng-app ng-init="qty=1;cost=2">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="qty">
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="cost">
  </div>
  <div>
    <b>Total:</b> {{qty * cost | currency}}
  </div>
</div>
--------------------------------------------------------------------------------------------

Invoice:
Quantity: 
Costs: 
Total: $6.00

샘플 예제 : https://docs.angularjs.org/guide/concepts#template



Template : 상단 코드의 HTML영역을 Template라고 부른다. 
Compiler : Angular가 시작되면, 상기 마크업을 파싱하고 처리하는 역할을 한다. 
View : 로드되고 변환되고 DOM으로 렌더링 된 것을 view라고 부른다. 
Directives : 상기 코드에서 ng-app등과 같은 것을 directive라고 부른다. 이는 특정 행위를 수행하며 이는 attributes나 elements에 적용이 된다. 

상기 예제는 ng-app 속성에 해당하는 directive를 이용하고 있으며 어플리케이션의 초기화를 자동으로 수행하는 역할을 한다. 

input 엘리먼트에 있는 ng-model 디렉티브는 입력된 값을 저장하고 업데이트 하는 역할을 한다. 

Custom directives to access the DOM:
DOM에 접근하는 Custom directives : Angular에서는 오직 directive를 통해서만 DOM에 접근하도록 하고 있다. 이것은 매우 중요한 포인트로 DOM에 직접 접근하는 것은 매우 테스트하기가 어렵게 만든다. 만약 DOM에 직접 접근하고자 한다면 이것을 위한 Custom directive를 작성하는 것을 권고한다. 
{{ expression | filter }} 로 두겹의 {혹은 } 를 사용하여 데이터를 표시하고 있다. 이것은 마크업의 값을 평가할때(evaluate) 해당 값을 교체하게 된다.
Expression 은 템플릿에서 JavaScript와 같은 코드를 작성할 수 있는 것으로, Angular가 값을 읽고 쓸 수 있도록 해준다. 

이 변수값은 global값이 아니다. JavaScript에서와 같이 scope내에서 유효한 값이된다. 
Scope는 Angular에서 값의 범위를 제공하며, 이는 expression을 위한 접근가능한 범위이다. 

값은 scope에 저장되며, 이것은 model에서 접근된다. 
상단 예제는 입력 필드에서 들어온 값을 서로 곱한 값을 저장하고 보여준다. 

Filter는 사용자에게 보여줄 표현식의 데이터를 포매팅하는 것이다. 상기 예제에서는  currency 포맷을 이용하여 금액을 표시하고 있다. 

상기 예제에서 중요한 것은 Angular가 live binding을 제공한다는 것이다. 입력값이 변경되면 표현식의 값이 자동적으로 재 계산되며, 이 값은 계산된 값으로 DOM에 반영된다. 

이 개념은 two-way data binding이라고 한다. 
One-way data binding : 
  기존의 템플릿을 이용한 방식 JSTL, FreeMarker 등과 같이 서버에서 모델과 템플릿을 조합하여 최종 결과를 클라이언트에 전송하여 브라우저가 렌더링 하는 방식
Two-way data binding : 
  템플릿이 라이브 View로 컴파일되고, 뷰의 변경사항이 모델에 반영되고, 모델의 변경사항이 View에 즉각 변경되는 형태를 말한다. 

Controller : 

상기 예제에서 추가적인 로직을 넣어보자. 이는 서로다른 통화에서 가격을 계산하고, 주문을 결제하는 내용을 담고 있다.

------------------------------------------------------------------
angular.module('invoice1', [])
.controller('InvoiceController', function() {
  this.qty = 1;
  this.cost = 2;
  this.inCurr = 'EUR';
  this.currencies = ['USD', 'EUR', 'CNY'];
  this.usdToForeignRates = {
    USD: 1,
    EUR: 0.74,
    CNY: 6.09
  };

  this.total = function total(outCurr) {
    return this.convertCurrency(this.qty * this.cost, this.inCurr, outCurr);
  };
  this.convertCurrency = function convertCurrency(amount, inCurr, outCurr) {
    return amount * this.usdToForeignRates[outCurr] / this.usdToForeignRates[inCurr];
  };
  this.pay = function pay() {
    window.alert("Thanks!");
  };

});

------------------------------------------------------------------


------------------------------------------------------------------
<div ng-app="invoice1" ng-controller="InvoiceController as invoice">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="invoice.cost" required >
    <select ng-model="invoice.inCurr">
      <option ng-repeat="c in invoice.currencies">{{c}}</option>
    </select>
  </div>
  <div>
    <b>Total:</b>
    <span ng-repeat="c in invoice.currencies">
      {{invoice.total(c) | currency:c}}
    </span>
    <button class="btn" ng-click="invoice.pay()">Pay</button>
  </div>

</div>

------------------------------------------------------------------


Invoice:
Quantity: 
Costs:  
Total: USD2.70 EUR2.00 CNY16.46 



Controller : 상기 코드를 보면 controller이라는 새로운 자바 스크립트 코드가 들어 있다. 
이는 실제 controller인스턴스를 생성하는 코드이며, 컨트롤러의 목적은 값을 노출하고, expression과 directive에 기능을 추가하는 역할을 한다. 

컨트롤러를 생성하고나면 이를 사용하기 위해서는 HTML에 ng-controller 디렉티브를 추가하여 사용할 수 있다. 이 디렉티브는 Angular에 InvoiceController가 디렉티브의 엘리먼트와 그 하위의 엘리먼트 전체에대한 처리를 책임진다는 의미이다. 

문법은 InvoiceController as invoice 로 Angular에게 컨트롤러의 인스턴스와 이 인스턴를 저장하고 있는 invoice 변수를 현재 scope에 지정하였다는 것을 알려준다. 

이후에는 invoice. 이라는 프리픽스를 붙여서 컨트롤러 인스턴스에 접근할 수 있다. 

ng-repeat라는 디렉티브를 이용하여 Currency 리스트를 view에 노출할 수 있다. 

상기에 저정된 total 함수는 {{ invoice.total(...) }} 의 형태로 처리결과를 화면에 노출할 수 있다. 

버튼의 경우 ngClick를 이용하여 해당 이벤트를 기동할 수 있도록 한다. 이것은 버튼이 클릭된경우 해당하는 표현식을 검증하는 방식으로 수행된다. 

새로운 자바 스크립트 파일은 module를 생성하고 있다. 이것은 컨트롤러를 등록하는 역할을 수행하고 있다. 

Service :

InvoiceController 는 모든 예제를 포함하고 있다. 그러나 어플리케이션이 확장되고 커지면 view에 독립적인 모델로 Controller에서 부터 로직 부분을 Service로 떼어내는 것이 좋다. 
이를 통해서 다른 파트에서 동일한 처리를 하는 Service를 재사용 가능하게 된다. 
이후 우리가 가격계산하는 로직이 변경되는 경우 Controller의 변경없이 Service의 변경만으로 원하는 작업을 수행할 수 있다.


------------------------------------------------------------------
angular.module('finance2', [])
.factory('currencyConverter', function() {
  var currencies = ['USD', 'EUR', 'CNY'];
  var usdToForeignRates = {
    USD: 1,
    EUR: 0.74,
    CNY: 6.09
  };
  var convert = function (amount, inCurr, outCurr) {
    return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
  };

  return {
    currencies: currencies,
    convert: convert
  };
});
------------------------------------------------------------------


------------------------------------------------------------------
angular.module('invoice2', ['finance2'])
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
  this.qty = 1;
  this.cost = 2;
  this.inCurr = 'EUR';
  this.currencies = currencyConverter.currencies;

  this.total = function total(outCurr) {
    return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr);
  };
  this.pay = function pay() {
    window.alert("Thanks!");
  };
}]);
------------------------------------------------------------------


------------------------------------------------------------------
<div ng-app="invoice2" ng-controller="InvoiceController as invoice">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="invoice.cost" required >
    <select ng-model="invoice.inCurr">
      <option ng-repeat="c in invoice.currencies">{{c}}</option>
    </select>
  </div>
  <div>
    <b>Total:</b>
    <span ng-repeat="c in invoice.currencies">
      {{invoice.total(c) | currency:c}}
    </span>
    <button class="btn" ng-click="invoice.pay()">Pay</button>
  </div>
</div>

------------------------------------------------------------------

우리는 이전예제에서 convertCurrency 함수를 새로운 파일에 이동시켰다. 
그렇다면 어떻게 controller이 새로 때어낸 함수를 이용할 수 있을까? 


Dependency Injection : 여기서 사용하는 것이 Depencency Injection이다. 이는 소프트웨어 디자인 패턴으로 어떻게 객체나 함수가 생성되는지 그리고 어떻게 이러한 의존성을 획득하고 사용할 수 있는지를 알려준다. 
Angular에서 directives, filters, controllers, services 등등은 생성되고, depencency를 추가할 수 있도록 되어 있다. 

의존성을 이용하기 위해서는 의존성 추가 파트에 해당 의존성을 추가해 주어야 한다. Angular에서 이러한 일을 해주는 것이 module이다. Angular가 시작되면 ng-app 디렉티브의 설정을 이용하게 되며, 모든 의존성 있는 모듈들을 추가한다.

상기 예제에서는 ng-app="invoice2" 디렉티브를 템플릿에 추가하였으며 이것은 Angular에게 invoice2를 어플리케이션의 메인 모듈이며 이를 이용하겠다고 알리는 것이다. 
이후 코드 부분에서 angular.module('invoice2', ['finance2']) 를 이용하여 invoice2가 finance2 모듈에 의존성을 갖는다는 것을 알려준다. Angualr는 InvoiceController를 이용하며 게다가 currencyConverter 서비스도 함께 이용하게 된다. 

이제 Angular는 어플리케이션의 모든 파트를 알고 있게 되었다. 이제는 그것들을 생성하는 일이 남아 있다. 이전 섹션에서는 controllers는 factory 함수를 이용하여 컨트롤러가 생성된 것을 확인했다. 서비스도 다양한 방법으로 그들의 factory를 정의할 수 있다. 상단의 예제에서 우리는 currencyConverter함수를 서비스 팩토리를 이용하여 반환하였다. 

어떻게 InvoiceController가 currencyConverter 함수의 레퍼런스를 획득할 수 있을까? 
Angular에서는 단순하게 정의한 생성자 아규먼트로 이 행위를 수행할 수 있다. injector는 객체를 올바른 순서로 생성하고, 이전 생성된 객체를 의존성이 있는 객체의 팩토리에 전달할 수 있는 방법을 제공한다. 
InvoiceController는 currencyConverter로 이름된 아규먼트를 가진다. 이를 통해서 Angular는 컨트롤러와 서비스의 의존성에 대해 알게 되며, 컨트롤러에서 서비스의 인스턴스를 아규먼트를 통해서 접근할 수 있게 된다. 

이전 섹션과 이 섹션의 차이는 이제는 module.controller함수에서 배열을 전달하고 있다는 것이다. 
배열의 첫번째는 의존성이 있는 서비스의 이름을 의미하며, 이것은 컨트롤러에서 필요한 값이다. 배열의 마지막 엔트리는 생성자 함수이다. Angualr에서는 array를 이용하여 DI를 수행하여 코드를 최소화 하고 있다. 

Accessing the backend

------------------------------------------------------------------
angular.module('invoice3', ['finance3'])
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
  this.qty = 1;
  this.cost = 2;
  this.inCurr = 'EUR';
  this.currencies = currencyConverter.currencies;

  this.total = function total(outCurr) {
    return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr);
  };
  this.pay = function pay() {
    window.alert("Thanks!");
  };
}]);
------------------------------------------------------------------


------------------------------------------------------------------
angular.module('finance3', [])
.factory('currencyConverter', ['$http', function($http) {
  var YAHOO_FINANCE_URL_PATTERN =
        '//query.yahooapis.com/v1/public/yql?q=select * from '+
        'yahoo.finance.xchange where pair in ("PAIRS")&format=json&'+
        'env=store://datatables.org/alltableswithkeys&callback=JSON_CALLBACK';
  var currencies = ['USD', 'EUR', 'CNY'];
  var usdToForeignRates = {};

  var convert = function (amount, inCurr, outCurr) {
    return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
  };

  var refresh = function() {
    var url = YAHOO_FINANCE_URL_PATTERN.
               replace('PAIRS', 'USD' + currencies.join('","USD'));
    return $http.jsonp(url).then(function(response) {
      var newUsdToForeignRates = {};
      angular.forEach(response.data.query.results.rate, function(rate) {
        var currency = rate.id.substring(3,6);
        newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);
      });
      usdToForeignRates = newUsdToForeignRates;
    });
  };

  refresh();

  return {
    currencies: currencies,
    convert: convert
  };
}]);

------------------------------------------------------------------


------------------------------------------------------------------
<div ng-app="invoice3" ng-controller="InvoiceController as invoice">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="invoice.cost" required >
    <select ng-model="invoice.inCurr">
      <option ng-repeat="c in invoice.currencies">{{c}}</option>
    </select>
  </div>
  <div>
    <b>Total:</b>
    <span ng-repeat="c in invoice.currencies">
      {{invoice.total(c) | currency:c}}
    </span>
    <button class="btn" ng-click="invoice.pay()">Pay</button>
  </div>
</div>
------------------------------------------------------------------

이 finance모듈에서 우리는 $http를 이용하고 있다. 이것은 내장 서비스로 Angular가 서버의 backend로 접근할 수 있도록 제공해준다. $http는 XMLHttpRequest와 JSONP 전송을 wrapping하고 있다. 

from : https://docs.angularjs.org/guide/concepts





Share this

Related Posts

Previous
Next Post »