Angluar Routing & Navigation

라우팅과 내비게이션!

SPA (Single Page Application)

  • 단일 페이지 애플리케이션은 모던 웹의 패러다임
  • 장점
    • 기존의 서버 사이드 렌더링과 비교할때 배포가 간단하며 네이티브 앱과 유사한 사용자 경험을 제공
  • 단점
    • link tag를 사용하는 전통적인 웹 방식은 새로운 페이지 요청 시마다 정적 리소스가 다운로드되고 전체 페이지를 다시 렌더링하는 방식으로 동작 새로고침이 발생되므로, 변경이 필요없는 부분을 포함하여 전체 페이지를 갱신하는 것으로 비효율적
  • SPA
    • 웹 애플리케이션에 필요한 모든 정적 리소스를 애플리케이션 최초 기동 시에 한번만 다운로
    • 이후 새로운 페이지 요청시 페이지 갱신에 필요한 데이터만을 전달받아 페이지를 갱신하므로 전체적인 트래픽을 감소
    • 전체 페이지를 다시 렌더링하지 않고, 변경되는 부분만을 갱신하므로 새로고침이 발생하지 않아 네이티브 앱과 유사한 사용자 경험 제공
  • 모바일 사용이 증가하고 있는 현 시점에 트래픽 감소와 속도 사용성 반응성의 향상은 매우 중요한 이슈

Routing

  • 라우팅이란 출발지에서 목적지까지의 경로를 결정하는 기능
  • 애플리케이션의 라우팅은 사용자가 태스크를 수행하기 위해 어떤 화면에서 다른 화면으로 화면을 전환하는 내비게이션 관리하기 위한 기능

  • 브라우저가 화면을 전환하는 경우

    • 브라우저의 주소창에 URL 입력
    • 웹페이지의 링크를 클릭
    • sub버튼을 클릭하면 action에 지정한 URL로 입력 데이터가 전송
    • 브라우저의 뒤로가기 또는 앞으로가기 버튼
  • AJAX 요청에 의해 서버로부터 데이터를 응답받아 화면을 생성

    • 브라우저 주소창의 URL은 변경되지 않는다.
    • 사용자의 방문 history를 관리할 수 없음을 의미
    • SEO (검색엔진 최적화) 이슈의 발생 원인
    • 히스토리를 관리하기 위해서는 각 페이지는 브라우저의 주소창에서 구별할 수 있는 URL을 소유

Angular Router 개요와 위치 정책

  • 개요
    • Angular 라우터를 제공
    • 라우터를 구성하고 라우트에 해당하는 컴포넌트를 맵핑
  • Location Strategy
    • a 태그의 href 어트리뷰트를 사용하지 않는 AJAX는 URL을 변경시키지 않는다.
    • history.back(), history.go(n) 등도 동작하지 않음
    • 새로고침을 클릭하면 주소창의 주소가 변경되지 않기 때문에 언제나 첫페이지가 다시 로딩
    • 하나의 주소로 동작하는 ajax 방식은 SEO 이슈에서도 자유로울 수 없다
  • 위 문제를 해결하기 위해서 Angular에서는 2가지의 위치 정책을 제공
    • 애플리케이션 전역에서 유일한 URL을 소유
    • PathLocationStrategy (HTML5 History API 기반 내비게이션 정책)
    • HashLocationStrategy (Hash 기반 내비게이션 정책)

Angular에서 selector를 찾을 수 없다고 말할때 해결하는 방법

진짜 한시간동안 삽질을 한것 같다. Angular에서 모듈을 이해하는데 어려움이 있는것 같다.

If my-component is an Angular component, then verify that it is part of this module. If my-component is a Web Component then add CUSTOM_ELEMENTS_SCHEMA to the @NgModule.schemas of this component to suppress this message.

위 에러를 계속 보고 있는데, 너무 답답한게 Moudle에 이미 하위 Module을 추가했는데,
찾을 수 없다는 에러가 계속 나왔다. 이 문제는 몇단계의 Module을 import하게 되면 생기는 문제 같다.
[AppMoudle]에 새롭게 내가 생성한 [CustomMoudle]을 생성하고, CutomMoule에 새로운 Component를 생성해서
declarations를 했는데, 계속 undefined라고 나와서 정말 스트레스를 받았음,
결론부터 말하면, NgModule에서 사용하고자 하는 Module을 exports를 해야지 selector를 통해서 import가 가능하다.

ts @NgModule({ imports: [] exports: [HeaderComponent], declarations: [HeaderComponent] })

  • 참고
    • https://stackoverflow.com/questions/43937387/if-selector-is-an-angular-component-then-verify-that-it-is-part-of-this-mod?rq=1

템플릿과 템플릿 문법 (Template)

템플릿 (Template) 란?

  • 템플릿은 HTML과 Angular 고유의 템플릿 문법을 사용하여 UI의 초사 단위인 컴포넌트의 뷰를 정의
  • 동적으로 변하는 데이터는 컴포넌트 클래스가 관리하며 템플릿 문법의 데이터 바인딩에 의해 정적 HTML에 포함
  • 뷰 생성과정
    • Component class에서 데이터 바인딩을 통해 데이터와 템플릿이 합쳐져 compile을 통해 최종적인 view를 생성
  • Angular는 템플릿과 컴포넌트 클래스로 뷰와 모델(데이터와 비즈니스 로직)을 분리한다.
  • Angular는 컴포넌트 기반 개발 (CBD, Component Based Development) 프레임워크

    • MVC와 MVVM패턴과 일치하지 않지만 템플릿은 뷰(View)를 나타내고 컴포넌트 클래스는 Controller와 ViewModel의 일부를 담당
  • 역할 종류

    • 모델 (Model)
    • 애플리케이션에서 사용되는 데이터 형식을 말한다. 비즈니스 로직, 유효성 검사 및 그외 다양한 기능 포함
    • 뷰 (View)
    • 사용자에게 모델(데이터)을 표시하는 것, 뷰는 같은 모델을 다양한 방식으로 표현할 수 있다.
    • 컨트롤러 (Controller)
    • 모델과 뷰의 상호 작용을 감시하고 업데이트
    • 뷰모델 (ViewModel)
    • 뷰가 데이터 바인딩을 통해 컨트롤러의 역할
  • DOM의 상태 변경 전달 순서

    • DOM은 상태(input요소에 값을 입력한 상태, checkbox 요소를 체크한 상태)를 갖고 있으며, 뷰와 모델은 분리되어 있지만 상태는 공유되어야 한다.
    • DOM의 상태가 변경되면 템플릿은 변화를 감지하고 변화된 상태를 컴포넌트 클래스에 전달
    • 컴포넌트 클래스는 비즈니스 로직을 실행하고 템플릿에 실행 결과를 공유
    • 템플릿은 이를 전달받아 DOM 업데이트
  • 템플릿은 선언형 프로그래밍 (Declarative programming) 방식으로 뷰와 모델의 관계를 관리.

  • Angular는 변화 감지(Change detection)메커니즘 위에서 동작하는 데이터 바인딩(Data binding)을 통해 템플릿과 컴포넌트 클래스를 긴밀하게 연결하고 동기화
  • 템플릿과 컴포넌트 클래스는 데이터 바인딩 (인터폴레이션, 프로퍼티 바인딩, 이벤트 바인딩, 양방향 바인딩으로 연결) (예: {{expression}})
  • 변화감지 (change detection)은 zone.js에서 업데이트 변화 감시를 한다.

템플릿 문법 (Template Syntax)

  • 템플릿과 컴포넌트 클래스 간 데이터 공유를 위한 데이터 바인딩과 동적으로 DOM 구조, 스타일 등을 변경할 수 있는 빌트인 디렉티브 등 지원
  • 정적인 뷰는 HTML만으로 정의할 수 있지만 컴포넌트와 연계하여 동적으로 변화하는 뷰를 정의하기 위해서 템플릿 문법을 사용
  • 데이터 바인딩
    • 인터폴레이션
    • 프로퍼티 바인딩
    • 어트리뷰트 바인딩
    • 클래스 바인딩
    • 스타일 바인딩
    • 이벤트 바인딩
    • 양방향 데이터 바인딩
  • 빌트인 디렉티브
    • 빌트인 어트리뷰트 디렉티브
    • ngClass
    • ngStyle
    • 빌트인 구조 디렉티브
    • ngIf
    • ngFor
    • ngSwitch
  • 템플릿 참조 변수
  • 템플릿 표현식 연산자

  • [참고]

    • http://poiemaweb.com/angular-component-template

템플릿 참조 변수와 세이프 내비게이션 연산자 (Template reference variable & Safe navigation operator)

템플릿 참조 변수 (Template reference variable)

  • DOM요소에 대한 참조를 담고 있는 변수
  • 템플릿 요소 내에서 해시 기호(#)를 변수명 앞에 추가하여 템플릿 참조변수 선언
  • 템플릿 내에서만 유효하며 컴포넌트 클래스에 어떠한 side effect도 주지 않는다.
  • 이벤트 바인딩을 통해 컴포넌트 클래스로 전달할 수는 있다.
  • 사용예로는 이메일 주소 validation check

html <element #myelement> .... </element>

세이프 내비게이션 연산 (Safe navigation operator)

  • 세이프 내비게이션 연산 ?는 컴포넌트 클래스의 프로퍼티의 값이 null 또는 undefined 일때 발생하는 에러를 회피하기 위해 사용
  • 사용예

    • {{ obj }} <-- 결과 없으며 화면에 아무것도 출력하지 않는다.
    • {{ obj.id }} <-- Cannot read property 'id' of undefined
    • {{ obj?id }} <-- 세이프 내비게이션 연산자는 에러를 발생시키지 않는다.
  • [참고]

    • http://poiemaweb.com/angular-component-template-reference-variable

Angular Basic

Angular 소개

  • Angular는 SPA(Single Page Application) 개발을 위한 구글의 오픈소스 자바스킓트 프레임워크
  • 모바일 웹, 네이티브 모바일과 데스크탑 애플리케이션까지 프론트엔드 개발에 필요한 대부분 기능을 갖추고 있다.
  • TypeScript를 주력 언어로 채택하여 대규모 애플리케이션 개발에 보다 적합한 환경

Angular VS AngularJS

  • Angular는 정적 타이핑 ECMAScript6 스펙을 충족시키기 위해 TypeScript로 재작성
  • Controller와 $scope 기반 개발 --> 컴포넌트 기반 개발(CBD, Component Based Development)로 전환
  • angular.module과 jQlite보다 향상된 모듈 시스템과 DOM 제어 기능을 제공하며 API 또한 단순화
  • 선택적 바인딩을 지원하고 디렉티브와 서비스 의존성주입은 간소화
  • 주력 개발 언어로써 TypeScript를 도입, 대규모 개발에 적합한 정적타입과 인터페이스 제네릭 등 타입체크 지원 기능
  • ECMAScript6에서 새롭게 도입된 모듈, 클래스 등과 ECMAScript7의 데코레이터를 지원
  • 강력한 개발환경 지원 도구인 Angular CLI를 제공
  • 그냥 AngularJS와 Angular는 후속버전이긴 하지만, 호환성이 없는 새로운 프레 워크로 이해

Angular 장점

  • 개선된 개발 생산성
    • 컴포넌트 기반 개발
    • Angular에서는 컴포넌트가 개발의 중심 (CBD: Component Based Development)
    • 개발 생산성을 향상시키며 대규모 애플리케이션에 적합한 구조
    • TypeScript의 도입
    • 2012년 MS에서 발표한 오픈소스로 강력한 정적 타이핑
    • ECMAScript6(ECMAScript 2015)의 클래스, 모듈 등
    • ECMAScript7의 데코레이터를 지원
    • 다양한 도구를 지원
      • IntelliSense
      • 코드 어시스
      • 타입 체크
      • 리팩토링
    • 명시적 적적 타입 지정은 코드의 가독성을 향상
    • 모듈, 클래스, 인터페이스 등 강력한 OOP지원은 크고 복잡한 프로젝트의 코드 기반을 쉽게 구성할 수 있도록 돕는다.
    • JavaScript, Dart 도 작성할 수 있다. 하지만 커뮤니티에서는 TypeScript를 사용
    • 개발 도구의 통합 및 개발 환경 구축 자동화
    • Angular CLI를 통한 간편한 개발 환경 구축을 지원
      • 폴더 구조, 기본파일 생성, 빌드를 위한 설정, 디펜던시, 트랜스 파일러, 번들러 등
  • 성능의 향상

    • Digest Loop로 인한 성능저하 문제의 해결
    • AngularJs의 단점 중 대표적인 것이 Digest Loop(Model의 변화를 View에 반영시키는 과정)로 인한 성능저하
    • 양방향 바인딩을 위해서는 watcher가 추가되어야 하고 watcher에 대해 Digest Loop가 실행되기 때문에 watcher가 늘어날수록 성능은 저하
    • AoT 컴파일 (ahead of time compilation)
    • AoT컴파일은 사전 컴파일 방식을 의미
      • ngIf, ngFor, ngSwitch와 같은 구조 디렉티브(Structual directive)를 브라우저가 실행 가능한 코드로 변환하여야 하는데, 이러한 과정을 런타임에 실시하지 않고 사전에 컴파일하여 실행 속도를 향상시키는 기법
    • AoT컴파일의 또 다른 이점은 JIT(just-in-time)컴파일러를 필요로 하지 않기 때문에 프레임워크 크기를 기존 Angular보다 50%정도 줄일 수 있다는 것
    • Lazy Loading
    • 애플리케이션에서 모든 모듈을 한꺼번에 로딩하지 않고, 필요한 시점에 필요한 모듈만을 로딩하는 방식 (로딩 속도 향상)
      • 지연로딩은 SPA의 태생적 단점을 극복하기 위한 하나의 대안
    • 코드 최적화
  • 참고

    • http://poiemaweb.com/angular-basics

빌트인 드렉티브

빌트인 디렉티브(Built-in directive)란?

  • DOM의 모든것을 관리하기 위한 지
  • HTML요소 또는 어트리뷰트의 형태로 사용하여 디렉티브가 사용된 요소에게 무언가를 하라는 지시를 전달
  • 컴포넌트의 복잡도를 낮추고 가독성을 향상

  • 디렉티브 3가지 유형

    • 컴포넌트 디렉티브 (Component Directives)
    • 컴포넌트의 템플리 표시하기 위한 디렉티브 @Component 데코레이터의 메타데이터 객체의 selector 프로퍼티
    • 어트리뷰터 디렉티브 (Attribute Directives)
    • HTML 요소의 어트리뷰트와 같이 사용하여 해당 요소의 모양이나 동작을 제어, ngClass, ngSylte
    • 구조 디렉티브 (Structual Directives)
    • DOM 요소를 반복 생성(ngFor), 조건에 의한 추가 또는 제거(ngIf, ngSwitch)를 통해 DOM 레이아웃 변경

빌트인 어트리뷰트 디렉티브(Built-in attribute directive)

  • HTML요소의 어트리뷰트와 같이 사용하여 해당 요소의 모양이나 동작을 제어
  • ngClass
    • CSS 클래스를 추가 또는 제거
    • 한개의 클래스를 추가 또는 제거할 때는 클래스 바인딩을 사용하는 것이 좋다.
  • ngStyle
    • HTML 인라인 스타일을 추가 또는 제거
    • 한개의 인라인 스타일을 추가 또는 제거할 때는 스타일 바인딩을 사용하는 것이 좋다

빌트인 구조 디렉티브 (Built-in structural directivec)

  • 구조 디렉티브
    • 구조 디렉티브는 DOM요소를 반복 생성(ngFor), 조건에 의한 추가 또는 제거를 수행 (ngIf, ngSwitch)을 통해 뷰의 구조를 변경한다.
    • 구조 디렉티브에는 *접두사를 추가하여 []을 사용하지 않는다.
    • 하나의 호스트 요소(디렉티브가 선언된 요소)에는 하나의 구조 디렉티브만을 사용
  • ngIf
    • *ngIf="expression"
    • ngIf디렉티브를 사용하지 않고, 스타일 바인딩 또는 클래스 바인딩을 사용하여 요소를 표시/비표시 구현 가능
    • 위 방법은 브라우저에 의해 렌더링 되지 않지만 DOM에 남아있다. ngIf 디렉티브에 의해 제거된 요소는 DOM에 남아있지 않고, 완전히 제거되어 불필요한 자원 낭비를 방지
    • else, then을 사용 가능
  • ngFor
    • 컴포넌트 클래스의 컬렉션을 반복하여 호스트 요소 (ngFor 디렉티브가 선언된 요소) 및 하위 요소를 DOM에 추가
    • *ngFor="let item of items; let i=index; let odd=odd; trackBy: trackById"
    • for of에서 사용할 수 있는 iterable 이라면 사용 가능, 문자열 MAP, 배열이 아닌 일반 객체는 사용할 수 없다.
    • 인덱스를 취득할 필요가 있는 경우, index를 사용하여 변수에 인덱스 할당 가능, first, last, even, odd와 같은 로컬 변수가 제공
    • ngFor 디렉티브는 컬렉션 데이터가 변경되면 컬렉 과 연결된 모든 DOM요소를 제거하고 다시 생성한다.
    • 컬렉션의 변경 사항을 추적할 수 없기 때문에,크기가 매우 큰 컬렉션을 다루는 경우 퍼포먼스 상의 문제를 발생시킬 수 있다.
    • ngFor 디렉티브는 퍼포먼스 향상시키기 위한 기능으로 trackBy를 제공한다.
    • trackBy에서 트랙킹 할 수 있는 프로퍼티는 유니크하여야 한다.
    • 일반적인 경우 ngFor는 충분히 빠르기 때문에 trackBy에 의한 퍼포먼스 최적화는 기본적으로 필요하지 않다.
  • ngSwitch

    • switch 조건에 따라 여러 요소 중에 하나의 요소를 선택하여 DOM에 추가한다.
  • 참고

    • http://poiemaweb.com/angular-component-built-in-directive

서비스(Service)란?

  • 화면을 구성하는 뷰를 생성하고 관리하는 역할
  • 컴포너는트 내의 부가기능(로깅기능, 서버 통신 기능에 사용)
  • 컴포넌트 외의 부가 기능이 변경되면 컴포넌트를 변경해야한다. (독립성x)
  • 코드중복, 재사용성이 낮고, 복잡도는 높아진다.
  • 서비스가 필요한 이유?
    • 컴포넌트 관심사와 애플리케이션 전역 관심사를 분리하기 위해서 서비스 사용
    • 서비스느 재사용이 가능하게 되어 일관된 앱 코드 작성이 가능
  • 뷰를 구성하기 위해 데이터를 서버로 부터 취득 하는 행위를 컴포넌트에게 필요한 기능이지만 그 기능 자체가 컴포넌트의 주된 괌심사는 아니다.
  • 위 데이터를 서버로 부터 취득하는 공통 관심사를 서비스로 분리한다면 구성 요소마다 자신의 관심사에 집중할 수 있음
  • 결과적으로 재사용성은 높아지고 복잡도는 낮출 수 있다.

서비스 생성

  • 서비스는 Dependency Injection이 가능한 클래스로 작성한다. (의존성은 뒤에서 설명)
  • $ ng generate service hero
  • 의존성
    • 서비스를 만들고, 사용할 컴포넌트에서 객체를 생성해서 함수를 호출할 수 있다. 하지만 이 방법을 사용하면 호출할 서비스가 변경되면 컴포넌트를 변경해야 하기 때문에 의존성이 긴밀한 결합(Tight Coupling) 되어 있다고 할 수 있다.
    • Tight Coupling에서 느슨한 결합(Loose Coupling)으로 전환하기 위해서 의존성주입(Dependency Injection)을 지원한다
  • 의존성 주입은 애플리케이션이 직접 인스턴스를 생성하는 것이 아니라 Angular 프레임워크가 생성한 인스턴스를 사용하는 방식이다
  • 의존성 주입을 사용하여 컴포넌트가 직접 인스터스를 생성하지 않고, Angular가 인스턴스 생성의 주체가 되도록 하는게 좋다.

의존성 주입 (Dependency Injection)

  • 의존성 주입(DI)은 구성요소 간의 의존 관계를 코드 내부가 아닌, 외부 설정파일 등을 통해 정의하는 디자인 패턴 중의 하나로 구성 요소 간 결합도를 낮추고 재사용성을 높인다.
  • 결과적으로 인스턴스를 컴포넌트가 직접 생성하지 않는다.
  • Angluar가 인스턴스를 생성하고 컴포넌트에 전달하는 방식
  • 제어권의 역전(Inversion of Control, IOC)
    • 컴포넌트는 필요한 의존관계 객체를 요구하고, 프레임워크는 제어권(Control)을 갖는 주체로 동작 요구된 의존 관계 객체의 인스턴스를 ㅈ기접 생성하여 전달
  • 필요한 의존 관계 객체의 인스턴스를 어떻게 생성하는지 Angular가 모르기 때문에 이 정보를 Angular에게 알려주어야 한다.
    • @Component Annotation의 providers 프로퍼티는 의존성으로 주입될 객체의 인스턴스를 어떻게 생성하는지 Angular에게 설명
  • 컴포넌트의 메소드에서 this에 의해 참조 가능

인젝터 (Injector)

  • 컴포넌트가 생성될 때, Angular는 컴포넌트에 필요한 인스턴스를 인젝터에게 요청
  • 인젝터는 이전에 이미 생성한 인스턴스를 담고 있는 컨테이너를 관리
    • 인스턴스가 컨테이너에 존재하지 않으면 인스턴스 생성하고 컨테이너에 추가

인젝터 트리 (Injector tree)

  • 컴포넌트는 트리 구조로 구성
  • 인젝터 수행 순서
    • 컴포넌트는 각각 하나의 인젝터를 가지고 있기 때문에 컴포넌트의 트리 구조와 동일한 인젝터가 생성
    • 컴포넌트의 주입 요청이 있을 때 인젝터는 현재 컴포넌트에서 등록한 프로바이더에서 주입 대상을 검색
    • 이때 해당 컴포넌트의 프로바이더에서 주입 대상을 찾을 수 없으면 상위 컴포넌트의 프로바이더에서 주입 대상을 검색
    • 상위 컴포넌트의 프로바이더를 검색하여 주입 대상을 찾으면 해당 인젝터는 인스턴스를 생성하며 상위 인젝 가 생성한 인스턴스는 하위 컴포넌트에서 사용가능
  • 결과적으로 이젝터 트리의 최상위 인젝터 즉 루트 인젝터에서 생성한 인스턴스는 전역에서 사용 가능한 전역 서비스로 사용 가능

프로바이더 (Provider)

  • 인젝터가 인스턴스를 생성할 때 인스턴스를 어떻게 생성하는지 인스턴스 생생 정보를 Angular에 알려주어야 한다.
  • 위 인스턴스 생성 정보는 providers 프로퍼티에 등록한다.
  • providers 프로퍼티 모듈의 @NgModule 또는 컴포넌트의 @Component 어노테이션에 등록한다.
  • @NgModule 어노테이션에 드옥한 서비스는 모듈 전체(루트 모듈의 경우, 애플리케이션 전역)에 반영
  • @Component 어노테이션에 등록한 서비스는 해당 컴포넌트와 자식 컴포넌트에 반영
  • 모듈 레벨에 등록한 서비스는 하나의 인스턴스를 공유하지만 컴포넌트 레벨로 등록한 서비스는 컴포넌트가 생성될 때마다 새로운 인스턴스를 취득

  • 프로바이더 3가지 종류

    • 클래스 프로바이더 (Class Provider)
    • 클래스 인스턴스를 의존성 주입하기 위한 설정을 등록 (위에서 Tight Coupling에서 Loose Coupling을 하기 위해 객체 생성 방식을 변경할때 우리는 class provider를 사용)
    • 간단하게 provide는 그대로 두고, useClass에 AnotherGreetingService로 변경하면 가능
      • 확인하기 위해서는 console.log(greetingService instanceof AnotherGreetingService); // true
    • @Component 레벨에서 프로바이더를 등록하면, @NgModule에 동일하기 AnotherGreetingService를 등록하면 중복하여 객체가 생성
    • @NgMoudle에 그대로 두고, @Component 레벨에서 키를 useClass를 useExistingd로 변경하면, 인스턴스를 싱글터으로 생성하도록 프로바이더를 수정할 수 있다.
      • 파라미터에 생성자를 두개 두고 생성하면 확인 가능
      • 확인하기 위해서 console.log(greetingService === anotherGreetingservice);
    • 값 프로바이더 (Value Provider)
    • 고정 값을 의존성 주입하기 위한 설정을 등록
    • 예) URL, PORT
    • @Component에 providers에 dict의 형태로 삽입하면 사용 가능
      • 예) providers; [{provide: AppConfig, useValue: MYAPPCONFIG}]
    • AppConfig는 인스턴스 값 프로바이더 useValue property의 MYAPPCONFIG의 값으로 초기화 되었다.
    • 주입될 의존성은 클래스가 아닌 문자열, 함수, 객체일 수 도 있음
    • @Inject 데코레이터에는 주입할 대상의 토큰을 설정
      • 클래스 이외의 토큰의 경우, 명시적으로 @Inject 데코레이터를 선언해야 한다.
      • @Inject('myConfig') public myConfig: string
    • 팩토리 프로바이더 (Factory Provider)
    • 의존성을 생성할 때 어떠한 로직을 거쳐야 한다면 팩토리 함수를 사용
      • 조건을 인자로 받아 의존성을 생성
      • 여러 의존성 중에 어떤 것을 생성할 지 결정해야 하는 경우
      • dev모드일때 mock데이터를 실제 서비스일때는 실제 데이터를 전달하기 위해 사용
    • 인젝션 토큰 (Injection Token)
    • 공통 상수로 사용하는 경우를 제외하고 토큰으로 클래스를 사용
    • 인젝션 토큰은 클래스가 아닌 의존성(non-class dependency)
      • 객체, 문자열, 함수 등을 위한 토큰을 주입받기 위해 사용
    • 인젝션 토큰 (Injection Token) 사용 사례
      • 타입스크립트는 자바스크립트로 트랜스파일링될 때, 자바스크립트는 인터페이스를 지원하지 않으므로, 인터페이스가 사라지게 된다.
      • Angular가 런타임에 찾을 수 있는 인터페이스 타입 정보가 없기 때문에 인터페이스를 토큰으로 등록하면 에러가 발생
      • 위에서 AppConfig을 class가 아닌 interface로 정의를 내리는 경우
      • MYAPPCONFIG를 제거 할 수 있음
    • 선택적 의존성 주입 (Optional Dependency)
    • 프로바이더 등록을 하지 않으면 의존성 주입은 실패하고 애플리케이션은 중단된다.
    • @Optional decorator를 사용하면 의존성 주입이 필수가 아닌 선택 사항임을 Angular에게 전달
    • 의존성이 없어도 에러로 인해 애플리케이션이 중단되지 않는다
    • 서비스 중재자 패턴 (Service Mediator Pattern)
    • 컴포넌트는 독리적인 존재지만, 다른 컴포넌트와 결합도를 낮게 유지하면서 상태 정보를 교환
    • @Input, @Output 데코레이터를 이용하면 컴포넌트 간에 상태를 공유할 수 있다
      • 원거리 컴포넌트 간의 상태 공유를 위해서 상태 공유가 필요없는 컴포넌트를 경
      • 일관된 자료 구조가 존재하지 않기 때문에 개별적인 프로퍼티만 교환 할 수 밖에 없는 한계
    • 위 문제로 인해서 컴포넌트 간 데이터 중재자로 서비스를 사용하면 일정한 형식의 자료 구조를 사용하면서 컴포넌트 간 상태 공유 가능
  • [참고]

    • http://poiemaweb.com/angular-service
  1. 요시 2018.04.16 21:46

    한글로 되어서 참 반갑네요 ㅎㅎ
    근데 한번 읽어서는 감이 전혀 안옵니다 ㅜㅜ
    오늘도 초보는 웁니다.

No Module factory available for dependency type: ContextElementDependency

노드에서 모듈의 버전이 맞지 않으면 발생하는 문제인데,
lazy loading을 통해서 문제를 해결할 수 있지만
그렇게 하면 더 복잡할 것 같아서 나는 간단 무식하게 해결

해결 방법

아래 처럼 문제를 해결하면 무식하지만 문제가 해결된다.

npm uninstall --save-dev webpack sudo rm -R node_modules npm install

[참고] https://github.com/angular/angular-cli/issues/6417

데이터 바인딩이란?

  • 필요한 이유?
    • 구조화된 웹앱은 뷰, 모델의 분리가 필요, 하지만 뷰와 모델은 유기적으로 동작이 필요
    • 위 문제를 해결하기 위해 데이터 바인딩이 필요
  • 데이터 바인딩은 뷰와 모델을 하나로 연결
  • 템플릿 = View, 데이터 = Model
  • 뷰와 컴포넌트 클래스의 데이터를 하나로 묶어 유기적 동작
  • 기존 jQuery를 사용
    • 뷰와 모델 간의 관계를 느슨하게 결합하기 어려운 구
    • 구조 상 문제로 뷰가 변경되면 로직도 변경될 가능성이 매우 높음
  • Angular는 DOM에 직접 적느하지 않고, 템플릿과 컴포넌트 클래스의 상호 관계를 선언 (Declarative programming)으로 뷰와 모델 관계를 관리
  • 컴포넌트 클래스와 템플릿 문법으로 기술
    • {{title}}
    • 결과적으로 템플릿이 변경되어도 컴포넌트 클래스를 변경할 필요가 없음

변화 감지 (Change detection)

  • 모델의 동기화를 유지하기 위해 변화를 감지, 이를 반영
  • 데이터 바인딩은 변화감지 매커니즘의 토대 위에서 수행
  • AngularJS는 양방향 바인딩(Two-way binding), ng-click과 같은 이벤트만을 사용하는 등 제약 있었음
  • Angluar는 양방향 바인딩과 단방향 바인딩(one-way binding)을 모두 지원(zone.js 라이브러리 사용: 네이티브 DOM 이벤트를 사용하여 변화 감지가 수행되도록 개선)
  • 뷰 변화 감지는 DOM 이벤트를 캐치, 하지만 모델은 HTML의 요소가 아니므로 이벤트 발생하지 않음
    • 위 이유로 별도의 감지를 위해 조치가 필요 (비동기식 처리 수행될 때 컴포넌트 클래스의 데이터 변경)
    • DOM 이벤트 - click, key press, mouse move 등
      • 예) 클릭 이벤트 핸들러에 의해 컴포넌트 클래스의 name property의 값을 변경
    • Timer(setTimeout, setInterval)의 tick 이벤트
    • Ajax 통신 / Promise
    • 모델이 변경된다는 것은 컴포넌트 클래스의 프로퍼티 값이 변경되는 것을 의미

데이터 바인딩

  • Angular는 단방향, 양방향 데이터 바인딩을 지원, 기존 웹 프로그래밍에서 사용하는 DOM 조작 방식보다 간편하게 데이터를 가져와서 뷰 표현
  • 바인뎅 제공 7가지

    • 단방향
    • 인터폴레이션
      • {{expression}}
    • 프로퍼티 바인딩
      • [property]="expression"
    • 어트리뷰트 바인딩
      • [attr.attribute-name]="expression"
    • 클래스 바인딩
      • [class.class-name]="expression"
      • HTML 클래스 어트리뷰트에 클래스를 추가,삭제 가능
    • 스타일 바인딩
      • [style.style-name]="expression"
    • 이벤트 바인딩
      • (event)="statement"
      • 뷰의 상태 변화(버튼 클릭, 체크박스 체크, input에 텍스트 입력 등)에 의해 이벤트가 발생하면 이벤트 핸들러를 호출
    • 양방향 데이터 바인딩
    • [(ngModel)]="variable"
  • [참고]

    • http://poiemaweb.com/angular-component-data-binding

Angular Tutorial

아래 튜토리얼은 4~8

  • Displaying a List
  • Master/Detail Components
  • Services
  • Master/Detail Components
  • Services
  • Routing
  • HTTP

Displaying a List

  • Heroes의 리스트를 보여주고, hero 선택 및 hero의 details까지!
  • Create mock Heroes
    • 실제로는 remote data server로 부터 데이터를 가져와야 하지만, mock heroes를 만들어서 서버에서 동작하듯
    • mock-heroes.ts를 src/app/ 폴더에 생성
  • Displaying heroes
    • HeroesComponents에 보여주기 위해서
    • 위에서 생성한 mock-heroes를 import
    • 그런다음 heroes의 property에 heroes를 가리키도록 바인딩
    • List heroes with ngFor
    • HeroesComponent template file 열고 변경 li, ul태그 추가
    • ngFor는 angular's repeater
    • Style the heroes
    • heroes list는 attractive해야 하고, 유저가 마우스를 리스트 위에 올리거나, 선택할때 visually 응답이 있어야 한다.
    • 전체 스타일을 적용하기 위해서는 styles.css
    • heroes.component.ts에 heroes.component.css스타일을 적용해야 한다.
  • Master/Detail
    • master list에 있는 hero를 유저가 클릭했을때, page아래 hero's details이 표시 되어야 한다.
    • Add a click event binding
    • li에 click event를 binding 하기 위해서 (click)="onSelect(hero)"를 추가
    • Add the click event handler
    • application이 최초에 추가 되었을때는 아무것도 선택된 내용이 없을 것이다.
    • onSelect() 함수에 clicked hero를 selecdtedHero에 assign해준다.
    • Update the details template
    • 이전 hero의 property는 더이상 존재하지 않기 때문에 hero를 selectedHero로 rename
    • Hide empty details with ngif
    • 새로고침을 하게 되면, seletecedHero가 undefined되도록 디자인 되었기 때문에 에러가 발생한다.
    • ngIf를 통해서 selectedHero에 값이 undefined되어 있으면 hero detail이 보이지 않도록 조건을 추가
    • Style the selected hero
    • li중에 선택된 항목에 대해서 특정색을 보여줘야 한다.
    • .selected CSS class를 사용하면 된다.

Master/Detail Components

  • large components를 smaller sub-components로 나누는 방법에 대해서 설명
  • HeroDetailsComponent를 reusable
  • Make the HeroDetailComponent
    • hero-detail component를 Angular CLI를 통해 생성하자.
    • Wrtie the template
    • HeroesComponent의 아래에 있는 hero detail을 HeroDetailComponent에 옮기자
    • Add the @Input() hero property
    • HeroDetailComponent template에서 hero property를 binding하기 위해서 hero를 import
    • hero property는 input property여야하고, @Input() decroator 이여야지만 외부 HeroesComponent에서 bind를 할 수 있다.
    • hero-detail.components.ts에 Input을 import하고, @Input() hero: Hero;
  • Show the HeroDetailComponent
    • HeroesComponent는 아직 master/detail view이다.
    • HerosComponent는 parent로 child HeroDetailComponent를 관리하게 된다. (리스트에서 어떤 히어로가 선택이 되었을때 보여질 새로운 hero를 전달)
    • Update the HeroesComponent template
    • HeroDetailComponent selector는 app-hero-detail
    • element를 HeroesComponents template 아래에 추가
    • HeroesComponent.selectedHero를 hero property로 binding
    • 위 처럼 binding을 하면 HeroComponent에서 선택된 selectedHero는 HeroDetailComponent에 업데이트되고 새로운 hero의 detail을 보여줄 것이다.
  • What Changed?
    • 기존에 HerosComponent에서 모든것을 처리했다면, 지금은 HeroDetailComponent에서 대신 hero의 detail을 보여주고 있다.
    • 기존 HeroesComponent를 Refactoring
    • HeroesComponent의 역할을 줄였다.
    • HeroesComponent의 수정 없이 HeroDetailComponent의 수정을 통해 rich hero editor로 수정이 가능
    • HeroesComponent를 hero detail view 수정 없이 수정 가능
    • 이후 컴포넌트에서 HeroDetailComponent를 re-use 가능하다

Services

  • HeroesComponent는 현재 fake data를 통해서 getting and displaying을 하고 있다.
  • HeroesComponent에서 서비스 단위 테스트를 하는것에 대해서 설명한다.
  • Why Services
    • Components는 fetch, save data를 직접 처리하면 안된다. component는 data를 제공하고, 서비스에 data를 delegate하는데 초점을 맞춰야 한다.
    • HeroService를 생성해서 모든 application classes에서 heroes를 가져올 수 있도록 한다.
    • 새로운 서비스를 angular dependency injection을 이용해 생성하고, HeroesComponent constructor에가 inject한다.
    • services는 서로 알지 못하는 classes 사이에 정보를 공유하는데 좋은 방법이다.
    • MessageService를 생성하고, 두개의 장소에 inject
    • HeroService: send message하는데 사용
    • MessageComponent: message를 display
  • Create the HeroService
    • Angular CLI를 사용해 hero service를 생성
    • $ ng generate service hero
    • skeleton HeroService class를 생성한다. (src/app/hero.service.ts)
    • @Injectable() services
    • 새로운 서비스 injectable
    • get hero data
    • HeroService는 어디서든 hero 데이터를 가져올 수 있다. (web, local storage, mock data source 등등)
    • data access를 components에서 제거한다고 해도, 어떤 다른 component의 수정없이 서비스만 동작을 변경시키면 된다.
  • Provide the HeroService
    • Dependency injection system에서 HeroService를 제공해야 Angular가 HeroesComponentdㅔ inject를 할 수 있다.
    • HeroesComponent, AppComponent, AppModuel에서 HeroService를 제공하기 위한 방법이 여러가지 있다. 각각의 장단점이 있는데,
    • 이 tutorial에서는 AppMoudle에서 제공
    • CLI를 통해
    • $ ng generate service hero --module=app
    • AppModule class를 열어서 HeroService를 @NgModule.providers array에 import
    • providers array는 HeroSerivce의 single shared instance를 생성하고, class에 inject하도록 angular에게 지시하는 역할을 한다.
    • HeroService는 이제 HeroesCoponent에 plug into 될 준비가 되었다.
  • Update HeroesComponent
    • HeroesComponent에서 HEROES는 이제 더이상 사용하지 않기 때문에 제거하고, HeroService를 대신 추가하자.
    • Inject the HeroService
    • heroService를 추가
    • Angular에서 HeroesComponent를 생성할때 Dependency Injection system에서는 heroService를 HeroService의 singleton intance로서 세팅
    • Add getHeroes()
    • service로 부터 heroes를 retrieve하는 function을 생성.
    • Call it in ngOnInit
    • 생성자에서 getHeroes()를 호출하는 방법은 좋은 방법이 아니다.
    • constructor는 단순한 초기화만 진행되어야 하고, HTTP request를 생성해 remote server에서 실제 데이터를 가져오는 기능은 하면 안된다.
    • 대신 getHeroes()는 ngOnInit lifecycle hook에서 호출이 되어야 한다. Angular에서는 ngOnInit을 HeroesComponent instance가 생성된 이후에 적절한 시점에서 호출을 한다.
  • Observable data
    • HeroService.getHeroes() method는 synchronous signature를 갖고 있기 때문에 HeroService는 heroes를 synchronously fetch를 할 수 있다.
    • heroService는 서버로 부터 응답을 기다려야만 하기 때문에, getHeroes()를 통해 즉시 hero data를 받아올 수 없다. 결과적으로는 service가 완료될때까지 브라우저는 동작하지 않는다.
    • Heroservice.getHeroes()는 asynchronous signature여야만 한다.
    • callback을 사용해서 해결
    • Observable HeroService
    • Observable은 RxJS library의 key class중 하나이다.
    • 이후에 HTTP tutorial에서는 Angular's HTTPClient methods return RxJS Observable에 대해서 알아볼 것이다.
    • 여기에서는 simulate getting data from the server with the RxJS of() function
    • HeroService file을 열고 RxJS로부터 Observable과 of symbols를 import
    • getHeroes method를 변경한다.
  • Subscribe in HeroesComponent
    • HeroService.getHeroes method는 Hero[]를 리턴하는데, 위 처럼 변경하면 Observable
    • Observable.subscrbie()는 critical하게 다르다.
    • 이전 버전에서 component's heroes property에 heroes의 array를 assign했다. 그 assignment는 synchronously를 발생하게 된다.
    • 위 문제로 server의 응답으로부터 기다려야 하기 때문에 UI는 freeze된다.
    • 새로우 버전은 Observable을 기다린다. 기다린 이후에 subscribe는 array를 callback을 통해 pass를 한다. 이과정을 통해서 heroes property에 set이 된다.
    • 위 asynchronous approach는 HeroService에서 서버로부터 heroes를 requests할때 사용하면 된다.
  • Show message
    • 이번 섹션에서는
    • screen의 아래 부분에 위치하는 app messages를 보여주는 MessagesComponent를 추가
    • displayed되는 message를 보내기 위해 injectable, app-wide MessageService를 생성
    • HeroService에 MessageService를 inject
    • HeroService가 heroes를 성공적으로 fetches할때 message를 화면에 표시
    • Create MessagesComponent
    • CLI를 통해 MessagesComponent를 생성
      • $ ng generate component message
    • AppComponent template에서 MessagesComponent가 보이도록 수정
    • Create The MessageService
    • CLI를 통해 messageService를 생성
      • $ ng generate service message --module=app
    • MessageService의 내용을 수정
    • Inject it into the HeroService
    • HeroService에 MessageService를 import
    • Send a message from HeroService
    • getHeroes method에 heroes가 fetch 되었을때 send message 로직 추가
    • Display the message from HeroService
    • MessagesComponent는 모든 messages를 출력을 해야한다.
    • MessageComponent에 MessageService를 import
    • messageService property는 반드시 public으로 해야 다른 templatedㅔ서도 binding해서 사용 가능
    • Bind to the Message Service
    • mesageComponent template를 수정

Routing

  • Tour of Heroes app의 requirements
    • Add Dashboard view
    • Heroes, Dashboard views사이에 navigat
    • view에서 hero를 클릭하면, selected hero의 detail view가 보이도록
    • email에 있는 deep link를 클릭할때, 특정 영웅에 대한 세부정보보기를 열도록
  • Add the AppRoutingModule
    • Angular에서 best practice는 toload and configure the router in a separate, top-level module that is dedicated to routing and imported by the root AppModule.
    • module class이름을 AppRoutingModule, app-routing.module.ts는 src/app folder에 위치
    • CLI를 통해 생성
    • $ ng generate module app-routing --flat --module=app
    • flat은 its own folder대신에 src/app에 위치하도록
    • --module=app은 AppModule의 array에 register
    • Add Routes
    • Routes는 user가 클릭한 link 또는 browser address bar에 URL을 붙여넣었을때 보여지는 화면을 의미한다.
    • Angular Route는 two properties를 가지고 있다.
      • path: address bar에 있는 URL과 match되는 string
      • component: router로 navigating할때 생성되는 component
    • HeroesComponent를 navigate한다고 의도할때, URL은 localhost:4200/heroes
    • HeroesComponent를 import하고, Routes에 path로 heroes, component로 HeroesComponent를 추가하면 된다.
    • RouterModule.forRoot()
    • router는 반드시 처음 초기화를 하고, browser location changes를 listening해야한다.
    • RouterModule을 @NgModule.imports array에 추가하고, routes를 RouterModule.forRoot()내에 호출한다.
    • Add RouterOutlet
    • AppComponent template에서 의 elements를 element로 변경
  • Add a navigation link(routerLink)
    • User는 항상 URL을 복사해서 붙여넣지 않고, navigate하기 위해서는 클릭을 한다.
    • nav TAG를 이용해서 clicked을 할때 HeroComponent로 이동할 수 있게 한다.
    • routerLink attribute에 "/heroes"를 세팅
  • Add a dashoard view
    • Routing은 multiple views가 있을때 더 적합하다.
    • CLI를 통해 DashboardComponent를 추가하자
    • $ ng generate component dashboard
    • DashboardComponent는 AppModule속에 선언이 된다.
    • Add the dashboard route
    • dashboard를 navigate하기 위해서는 router는 적절한 route가 필요하다.
    • AppRoutingModule에 DashboardComponent를 import
    • AppRoutingModule.routes를 추가하고, path에 dashboard, component에 DashboardComponent를 추가
    • Add a default route
    • app이 시작할때 browser의 주소는 web site's root를 가리키기 된다. 어떤 route에 매치되지 않으면 어디도 이동하지 않는다.
    • app이 시작할때 자동으로 dashoard로 navigate하게 하기 위해서 AppRoutingModules.Routes array에 다음과 같이 추가
    • path에는 '' redirectTo에는 "/dashboard", pathMatch에는 'full'
    • Add dashboard link to the shell
    • DashboardComponent와 HeroesComponent의 사이에서 앞,뒤로 이동을 할수도 있기 때문에 appComponent에 dashboard navigation 추가
  • Navigating to hero details
    • Delete hero details from Heroes Component
    • Add a hero detail route
    • : colon은 indicate :id
    • DashboardComponent hero links
    • HeroesComponent hero links
    • Remove dead code
  • Routable HeroDetailComponent

HTTP

  • Angular's HTtpClient로 부터 data persistence features에 대해서 알아본다.
    • HeroService에서 HTTPReqeusts를 통해 데이터를 가져온다.
    • HTTP를 통해서 heroes를 추가, 삭제, 제거가 가능.
    • Users는 heroes를 이름으로 검색이 가능
  • Enable HTTP Services
    • HttpClient는 HTTP를 통해서 remote server와 communicating을 위한 Angular's mechanism
    • HttpClient는 app어디에서도 사용이 가능하다.
    • root AppModule을 열고
    • HttpClientModule을 import한다. (@angular/common/http)
    • @NgModule.imports array에 추가
  • Simulate a data server
    • $ npm install angular-in-memory-web-api --save
    • InMemoryWebApiModule과 InMemoryDataService를 import
  • Heroes and HTTP
    • HTTPClient, HttpHeaders를 @angular/common/http로 부터 import
    • HttpClient를 prviate property http에 inject
    • Get heroes with HttpClient
    • 기존 of의 method를 this.http.get
    • Http methods return one value
    • HttpClient methods는 RxJS Observable을 return
    • HttpClient.get returns response data
    • 기본적으로 untyped JSON으로 응답이 온다.
    • JSON 데이터의 모양은 서버의 데이터 API에 의해 결정, 이 예제에서는 hero data의 array로 반환
    • Error handling
    • catchError를 import (rxjs/operators)
    • pipe method를 통해 에러를 핸들링 한다.
    • Tap into the Observable
    • tap을 통해 메시지의 로그를 출력할 수 있다.
    • Get hero by id
    • api/hero/:id를 통해 API를 제공할 수 있다.
    • Update heroes
    • save()를 통해 업데이트
    • Add HeroService.updateHero()
    • updateHero() method는 getHeroes()와 유사. 다른점은 http.put()을 사용한다는
    • put은 URL, data, options
    • Add a new hero
    • input element를 사용해서
    • Add HeroService.addHero9)
    • put대신 post를 호.
    • Delete a hero
    • Add HeroService.deleteHero()
    • HttpClient.delete 사용
  • Search by name
    • HeroService.searchHeroes
    • Add search to the Dashboard
    • Create HeroSearchComponent
    • AsyncPipe
    • Fix HeroSearchComponent class
    • The serachTerms RxJS subject
    • Chaining RxJS operators

+ Recent posts