본문 바로가기

Web + APP/Angular

Angular Tutorial with Docs - 2

반응형
SMALL

두 번째 시간입니다.

 

이제는 좀더 Angular 프레임워크 사용에 복잡해지는 부분이 많을거 같기도 하네요.

 

그래도 반복만이 살 길 !

 

계속해서 해보도록 합시다.

 

그래도 리액트가 더 좋아보이는 이유는 뭘까


컴포넌트 만들기

컴포넌트 생성

ng generate component hero-detail
  • src/app/hero-detail 폴더를 생성하고 그 안에 아래의 파일을 생성합니다.
    • 컴포넌트 스타일을 지정하는 CSS 파일
    • 컴포넌트 템플릿을 정의하는 HTML 파일
    • 컴포넌트 클래스 HeroDetailComponent가 정의된 TypeScript ㅏ일
    • HeroDetailComponent 클래스 파일을  테스트하는 파일
  • 그리고 자동으로 해당 컴포넌트는 app.module.ts 파일에 @NgModule에 등록됩니다.

@Input() 히어로 프로퍼티 추가하기

  • 외부 컴포넌트에서 바인딩되어 프로퍼티 값을 전달 받을 때, 전달 받은 곳에서 해당 프로퍼티를 @Input() 데코레이터를 사용해서 입력 프로퍼티로 선언해야합니다.
import { Component, OnInit, Input } from '@angular/core';

...
...

@Input() hero: Hero;

...
...
  • 외부 컴포넌트에서는 아래와 같이 바인딩을 시켜줘야합니다.
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
  • [hero]="selectedHero"의 경우엔 Angular가 제공하는 프로퍼티 바인딩 문법입니다.
    • selectedHero 프로퍼티를 app-hero-detail에게 hero라는 프로퍼티로 단방향 데이터 바인딩을 시킨겁니다.

서비스 추가하기

  • 컴포넌트는 데이터를 저장하는 위치가 아닙니다. 표시하는 곳이죠. 그래서 데이터 처리 로직은 서비스에게 맡기는 것이 좋습니다.
ng generate service hero
import { Injectable } from '@angular/core';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';

@Injectable({
	providedIn: 'root',
})
export class HeroService {
	
    constructor() { }
 	
    getHeroes(): Hero[] {
    	return HEROES;
    }
}
  • Angular CLI로 만든 서비스 클래스는 Injectable이 데코레이터로 사용됩니다. 이는 의존성 주입 시스템에 포함되는 클래스라고 선언하는 구문입니다.
    • @Component() 데코레이터에 메타데이터를 사용했던 것과 같은 방식입니다.
  • 위와 같은 방식으로 데이터를 service에 집중하면 컴포넌트는 데이터와 구분되어 좀 더 캡슐화된 기능을 사용할 수 있습니다.

angular.kr/tutorial/toh-pt4#heroservice-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B8%B0

 

Angular 가이드

Angular 가이드

angular.kr

  • 프로바이더의 경우엔 이해하기 힘들기에... url을 두고 반복해서 읽어보도록 합시다.
  • 결과적으로는 어떤 클래스를 데이터 베이스화 할거냐.. 이런 의미 같습니다. 

HeroService 주입하기

import { HeroService } from '../hero.service';

constructor(private heroService: HeroService) {}
  • 이렇게 작성하면 heroService 인자를 클래스 프로퍼티로 선언하면서 HeroService 타입의 객체가 들어가게 됩니다.
// 초기화 코드이기 때문에 ngOnInit() 라이프사이클 때 호출하는 것이 좋습니다.
// 생성자에서는 클래스 프로퍼티를 연결하는 정도로 가볍게 유지하는 것이 좋습니다.
ngOnInit() {
	this.getHeroes();
}

getHeroes(): void {
	this.heroes = this.heroService.getHeroes();
}
  • 위에 private으로 지정한 heroService 프로퍼티에서 Service에서 선언한 메소드를 선언할 수 있습니다.

옵저버블 데이터

  • 만약, 서버와의 통신으로 이루어진 getHeroes() 메소드였다면, 해당 메소드는 비동기 처리로 동작해야합니다.
  • 콜백 함수, Promise, Observable 방법이 있는데, Angular에서는 Observable 방법을 사용합니다.
  • Angular가 제공하는 HttpClient.get 메소드는 Observable을 반환하기 때문에 이를 사용합시다.

옵저버블 HeroService

import { Observable, of } from 'rxjs';

getHeroes(): Observable<Hero[]> {
	return of(HEROES);
}
  • RxJS 라이브러리가 제공하는 Observable은 가장 중요한 클래스입니다. Angular의 HttpClient 클래스가 제공하는 모든 메소드는 RxJS가 제공하는 Observable 타입을 반환합니다.

구독하기

getHeroes(): void {
	this.heroService.getHeroes()
    	.subscribe(heroes => this.heroes = heroes);
}
  • Observable.subscribe()를 사용한 부분이 가장 중요한데, 이 전에는 직접 heroes 프로퍼티에 할당했는데, 이 동작은 동기 방식으로 동작하기 때문에 서비스가 데이터를 즉시 반환하거나 서버의 응답이 동기 방식으로 전달될 때에만 제대로 동작합니다.
  • 하지만 서버와 통신하는 경우에는 이 로직이 제대로 동작하지 않습니다.
  • Observable의 경우엔 언제 응답이 도착하는지 관계없이 subscribe가 서버에서 받은 응답을 콜백 함수로 전달하고, 이를 heroes 프로퍼티에 할당합니다.

정리

  • 컴포넌트가 데이터를 직접 가져오는 방식을 service 클래스를 이용하여 변경했습니다.
  • 프로바이더를 사용해서 HeroService를 최상위 인젝터에 등록했습니다.
  • HeroService를 컴포넌트에 의존성으로 주입하기 위해 Angular의 의존성 주입을 사용했습니다.
  • HeroService에서 비동기 방식으로 데이터를 가져오는 메소드를 구현했습니다.
  • Observable<Hero[]>를 반환할 때 RxJS가 제공하는 of() 함수를 사용했습니다.
  • 초기화 함수를 컴포넌트 생성자가 아니라 ngOnInit 라이프사이클 후킹 함수에 구현했습니다.
  • 서비스를 컴포넌트에 의존성으로 주입할 수 있지만, 다른 서비스에도 의존성 주입을 할 수 있습니다.

네비게이션 추가하기

// flat 옵션은 새로운 폴더를 만들지 않고 src/app 폴더에 파일을 생성합니다.
// --module=app 옵션을 사용하면 AppModule의 imports 배열에 라우팅 모듈을 자동으로 추가합니다.
ng generate module app-routing --flat --module=app
  • Angular에서는 최상위 모듈과 같은 계층에 별개의 모듈을 두고 최상위 라우팅 모듈을 정의하는 방법을 권장합니다. AppModule은 이렇게 정의한 라우팅 설정을 로드해서 사용하면 됩니다.
  • 일반적으로 최상위 라우팅 모듈 이름을 AppRoutingModule이라고 정의합니다.
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroesComponent } from './heroes/heroes.component';

const routes: Routes = [
	// 아래의 기본 라우팅 규칙을 통해서 해당 주소로 이동하게 됩니다.
	{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
	{ path: 'heroes', component: HeroesComponent },
	// 아래와 같이 정의하면 라우팅 변수를 지정할 수 있습니다.
    { path: 'detail/:id', component: HeroDetailComponent },
];

@NgModule({
	imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule { }
  • RouterModule과 Routes 심볼을 로드하고, HeroesComponent를 로드해서 이동할 수 있도록 합니다.
  • 라우팅 규칙이 있습니다.
    • path: 브라우저 주소표시줄에 있는 URL과 매칭될 문자열을 지정합니다.
    • component: 라우터가 생성하고 화면에 표시할 컴포넌트를 지정합니다.
  • @Ngmodule에 메타데이터를 지정하면 모듈이 생성될 때 라우터를 초기화하면서 주소가 변화되는 것을 감지합니다. 그래서 라우터를 초기화 하기 위해서 routes라는 인자를 forRoot() 메소드에 넣습니다.
    • 그리고 App에서도 RouterModule을 사용할 수 있도록 exports 배열에 추가합니다.
<h1>{{ title }}</h1>
<nav>
	<a routerLink="heroes">Heroes</a>
</nav>
<router-outlet></router-outlet>
<app-messages></app-messages>
  • <router-outlet>은 라우팅 된 화면이 표시될 위치를 지정하는 엘리먼트입니다.
  • routerLink는 RouterModule이 제공하는 기능이며, 이 기능이 적용된 엘리먼트를 클릭하면 네비게이션을 실행합니다.
  • 아래와 같이 라우팅 규칙을 추가할 수 있습니다.
<a *ngFor="let hero of heroes" class="col-1-4"
	routerLink="/detail/{{ hero.id }}">
</a>

라우팅 변수 추출

import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';

constructor(
	private route: ActivateRoute,
    private heroService: HeroService,
    private location: Location
) {}

getHero(): void {
	// route.snapshot은 라우팅 규칙에 대한 정보를 담고 있는 객체입니다.
    // paramMap을 통해 라우팅 변수를 참조할 수 있습니다.
    // +를 붙인 이유는 문자열로 받아온 라우팅 변수를 숫자로 변환하기 위해서 입니다.
	const id = +this.route.snapshot.paramMap.get('id');
    this.heroService.getHero(id)
    	.subscribe(hero => this.hero = hero);
}

goBack(): void {
	// 이전에 주입 받은 Location 서비스를 사용하여 이전 화면으로 돌아갈 수 있습니다.
	this.location.back();
}
  • ActivatedRoute는 라우팅 규칙에 대한 정보를 담고 있어서 이를 참조하면 URL을 통해 컴포넌트로 전달되는 변수를 추출할 수 있습니다.

정리

  • 화면에 표시하는 컴포넌트를 전환하기 위해 Angular 라우터를 추가했습니다.
  • AppComponent에 <a> 링크와 <router-outlet>을 추가하면 네비게이션 동작을 실행할 수 있습니다.
  • 라우터 설정은 AppRoutingModule에 정의합니다.
  • 간단한 라우팅 규칙부터 리다이렉트 라우팅 규칙, 라우팅 변수가 있는 라우팅 규칙을 정의했습니다.
  • 앵커 엘리먼트에 routerLink 디렉티브를 적용했습니다.

이상 Angular Tutorial with Docs - 2였습니다. ^_^

반응형
LIST