Angular 공식 홈페이지에서 제공하는 튜토리얼을 번역하고 정리한 글이다. 원문은 https://angular.io/docs
에서 확인할 수 있다.
CodeLab 자료
보이지 않는다면 “SHOW EMBED”를 클릭!
순서
Angular Codelab 첫번째
- 1. Introduction
- 2. The Application Shell
- 3. The Hero Editor
- 4. Displaying a List
- 5. Master/Detail Components
Angular Codelab 두번째
- 6. Services
- 7. Routing
- 8. HTTP
6. Service
왜 서비스를 사용해야 하는가
컴포넌트가 뷰를 전시하는 일 외에 데이터를 직접 가져오거나 저장하는 등의 일은 하지 않는 것이 좋다. 관심사를 분리하고 재사용성을 늘리려면 컴포넌트는 데이터의 표현에 집중하고, 데이터에 접근하는 것은 서비스에 위임하는 것이 좋다.
HeroService 생성
Angular CLI
를 사용하여 hero
서비스를 생성한다.
ng generate service hero
다음과 같은 스켈레톤 코드를 확인할 수 있다.
@Injectable()
위 코드를 보면 새로운 데코레이터가 있는 것을 알 수 있다. 이것은 해당 클래스를 Angular의 의존성 주입 시스템에 참여하도록 하는 데코레이터이다. 또한 providedIn
속성으로 root
값을 주게 되면 어플리케이션 전역에서 이 서비스를 의존성 주입할 수 있게 된다.
서비스에 히어로 목록을 반환하는 기능을 구현해보자. 다음과 같이 Hero
와 HEROES
를 import
하고 getHeroes
메소드를 구현한다.
HeroesComponent 수정
HeroService
를 import
하고 주입한다. constructor
에 HeroService
타입의 heroService
인자를 private
접근제한자로 추가한다. 이를 통해 private
heroService
멤버 변수를 정의하고 HeroService
를 주입하게 된다.
constructor(private heroService: HeroService) { }
Angular 는 HeroesComponent
를 만들면 의존성 주입 시스템은 heroService
인자를 HeroService
의 싱글턴 인스턴스로 설정한다.
그 다음으로는 히어로 목록을 가져오는 메소드를 생성하고 이것을 ngOnInit
에서 호출하도록 한다.
물론 생성자에서 getHeroes()
메소드를 호출할 수도 있지만 좋은 방법은 아니다. 생성자는 타입스크립트 상에서 클래스 인스턴스를 생성 시에 사용하고 데이터를 초기화하거난 설정값을 가져오는 등의 행위는 ngOnInit
생명주기에서 하는 것이 좋다.
Observable data
지금까지는 HeroService
의 getHeroes()
메소드는 히어로 목록을 동기적으로 가져온고 있다. 하지만 대부분의 웹 서비스에서 데이터의 요청과 응답은 비동기적으로 수행된다. 따라서 이번 튜토리얼에서 이것을 mocking하기 위해 getHeroes()
메소드는 Promise
든 콜백함수든 Observable
이든 어떤 종류든간에 비동기 방식으로 처리되어야 한다.
최종적으로는 Angular의 HttpClient
의 get()
메소드를 이용하여 히어로 목록을 가져올 것인데 get()
메소드는 Observable
을 반환하기 때문에 getHeroes()
메소드의 반환값을 Observable
이 되도록 할 것이다.
Observable
은 RxJS
라이브러리의 핵심 클래스들 중의 하나이다. 다다음 챕터인 HTTP
에서 Angular의 HttpClient
메소드가 Observable
을 리턴한다는 것을 알게 될 것이고 지금은 시뮬레이션을 위해 RxJS
의 of()
메소드를 이용할 것이다.
of(HEORES)
는 히어로 목록을 Observable
로 만들어주고 이를 반환하게 된다.
HeroesComponent 구독하기(Subscribe)
HeroService
에서 getHeroes()
메소드는 Observable
을 반환하게 되었으므로 HeroesComponent
에서 getHeroes()
메소드를 수정해야 한다.
수정 전에는 단순히 동기적으로 히어로 목록을 가져왔다면 이번에는 Observable
이 히어로 목록을 방출(emit)할 때까지 기다린 후 방출이 되면 subscribe
에서 콜백으로 그 값을 가져오게 된다.
메시지 보여주기
이번 섹션에는 다음과 같은 것을 해볼 것이다.
- 화면 맨 아래 메시지를 표시하는
MessageComponent
를 추가한다. - 앱 전체에 주입 가능한
MessageService
를 생성한다. HeroService
에MessageService
를 주입한다.HeroService
가 히어로 목록을 성공적으로 가져올 때 메시지를 표시한다.
먼저 Angular CLI
를 통해 MessageComponent
를 생성한다.
ng generate component messages
이 컴포넌트를 전시하기 위해 app.component.html에 추가한다.
다음으로 MessageService
를 생성한다.
ng generate service message
메시지를 담을 messages
배열을 정의하고 메시지 추가와 삭제 메소드를 구현한다.
이제 HeroService
에 생성한 MessageService
를 주입한다.
constructor(private messageService: MessageService) { }
그리고 히어로 목록을 가져올 때 메시지도 추가한다.
이제 메시지를 볼 수 있도록 MessageComponent
를 수정한다.
템플릿 코드는 다음과 같이 수정한다.
요약
HeroService
를 루트에provider
로 등록하여 앱 어디에서나 이 서비스를 주입할 수 있도록 하였다.- Angular 의존성 주입을 이용하여
HeroeService
를 주입하였다. HeroService
에서 비동기적으로 반환하는 메소드를 구현하였다.- 히어로 목록을
Observable<Hero[]>
타입으로 반환할 수 있도록 하였다. - 클래스간의 느슨한 연결을 위해 메시지를 관리하는
MessageService
를 만들었다.
7. Routing
SPA(Single Page Application)을 위해 한 페이지 내 에서 컴포넌트를 변경해가며 전시를 해야하므로 프론트엔드에서도 라우팅이 필요하다. 이를 클라이언트 사이드 내비게이션 구현 방식이라고 하며 Angular에서는 요청 URL 경로와 컴포넌트를 쌍으로 구성하여 처리한다.
appRoutingModule 생성
ng generate module app-routing --flat --module=app
RouterModule
을 import
하고 경로를 추가해준다.
그리고 이 라우터를 통해 전시되는 컴포넌트의 위치는 다음과 같이 router-outlet
으로 구현한다.
다음은 사용자의 클릭을 라우터 탐색으로 바꾸는 디렉티브인 routerLink
를 통해 라우터 경로대로 이동할 수 있도록 버튼을 생성한다.
대시보드 컴포넌트 생성
HeroesComponent
와 마찬가지로 대시보드도 컴포넌트를 생성하고 라우터 경로를 추가한 후 routerLink
를 통해 /dashboard
경로로 이동 시 라우터 검색을 할 수 있도록 한다.
먼저 DashboardComponent
를 생성한다.
ng generate component dashboard
상위 3개를 가져오기 위해 slice
를 수행한다.
ngFor
디렉티브를 통해 히어로 목록을 전시한다.
HeroDetailComponent 수정
HeroDetailComponent
에서는 url에 명시된 히어로 아이디를 가져와 서비스에게 요청하는 방식으로 수정한다. 이 때 ActivatedRoute
의 snapshot
에서 그 아이디를 가져온 후 히어로 서비스에 조회요청을 한다.
HeroService
에 조회 기능을 추가한다.
요약
AppRoutingModule
에서 라우터를 구성했다.- 간단한 경로, 리다이렉션 경로로 및 매개 변수화 된 경로(
/:id
)를 정의했다. - anchor 요소에서
routerLink
디렉티브를 사용하여 라우터 탐색을 하도록 했다. - 밀접하게 결합된
HeroDetailComponent
를 분리했다. - 라우터 링크 매개 변수를 사용하여 사용자가 선택한 히어로의 상세 페이지로 이동했다.
- 여러 구성 요소간에
HeroService
를 공유했다.
8. HTTP
웹서비스는 서버에 데이터를 요청하고 그 응답 결과를 웹 페이지에 전시하는 형태가 될 것이다. 현재까지는 메모리 상의 데이터를 전시한 것이고 Angular의 HttpClientModule
을 통해 HTTP 통신을 하는 방법을 알아볼 것이다.
웹 서버를 Mocking하기 위해 다음 모듈을 설치한다.
npm install angular-in-memory-web-api --save
Mock 데이터는 다음과 같이 정의한다.
HeroService
에서 히어로를 조회하는 방식을 다음과 같이 바꾼다. HttpClient
를 의존성 주입하고 get()
메소드를 이용해 서버로 요청한다. 이 때 tap()
연산자를 통해 로그를 찍고 catchError()
연산자를 통해 에러 발생 시 handleError()
핸들러 메소드를 호출하도록 한다. 그리고 이 두 연산자를 pipe()
를 통해 체이닝한다.
tap()
: 로깅하는 것과 같이 어떠한 사이드 이팩트가 없고 투명하게 수행한다.catchError()
: 옵저버블 시퀀스에서 에러 발생 시 핸들러를 실행한다.- 그 외 다양한 연산자가 존재
히어로 업데이트
HeroService
에서 업데이트 기능을 추가한다. 이 때 HttpClient
의 put()
메소드를 이용한다.
HeroDetailComponent
에서 업데이트를 할 수 있도록 한다. Observable
객체는 subscribe()
를 해야만 반드시 수행한다.
히어로 등록
HeroService
에 히어로를 등록하는 기능을 추가한다. 이 때 HttpClient
의 post()
메소드를 이용한다.
HeroesComponent
에서 등록을 할 수 있도록 서비스를 호출한다.
히어로 삭제
HeroService
에서 히어로를 삭제하는 기능을 추가한다. 이 때 HttpClient
의 delete()
메소드를 이용한다.
히어로 목록에 x 버튼을 생성한 후 delete()
를 호출한다.
히어로 검색
먼저HeroService
에 검색 기능을 추가한다.
HeroSearchComponent
를 생성 후 다음과 같이 템플릿을 수정한다. 컴포넌트에서는 rxjs
연산자를 적절히 파이핑하여 다음과 같이 구현한다.
debounceTime(300)
은 새로운 문자열 이벤트의 흐름이 300ms 동안 일시 중지 될 때까지 기다린 후 최신 문자열을 전달한다. 300ms보다 자주 요청을하지 않는다.distinctUntilChanged()
는 필터 텍스트가 변경된 경우에만 요청을 보낸다.switchMap()
은debounce
및distinctUntilChanged
를 통해 검색하는 각 검색어에 대해 검색 서비스를 호출한다. 이전 검색 관측 값을 취소하고 파기하며 관찰 가능한 최신 검색 서비스 만 반환한다.
요약
- 필수 모듈인 HTTP 모듈을 설치하여 HTTP 통신을 수행하였다.
- web API를 통해 히어로 목록을 가져올 수 있도록
HeroService
을 수정했다. HeroService
에 get, post, put, delete HTTP 메소드를 통해 기능을 구현했다.- 히어로를 추가, 편집 및 삭제할 수 있도록 컴포넌트를 수정했다.
- 옵저버블 객체를 구독하고 콜백 함수로 방출된 값을 처리하였다.
결론
이로써 Angular 공식 사이트에 있는 튜토리얼을 모두 진행해보았다. 컴포넌트를 생성하고 뷰를 전시했으며 서비스를 통해 컴포넌트의 관심사를 분리하여 유연한 구조로 리팩토링하였다. SPA를 위해 프론트엔드에서 라우팅을 수행하였으며 HTTP 통신을 하고 이것을 옵저버 패턴으로 처리하였다.
이번 코드랩을 통해 Angular를 통해 개발 함에 있어 어떤 구성요소들로 구성되어 있고 어떤 키워드들이 있는지 알 수 있을 것이다.