3. 사용해보기: 네비게이션
이 문서는 사용해보기 튜토리얼의 첫 번째 단계에서 만든 앱을 기준으로 한다.
지금까지 작업한 앱에는 상품 목록 화면과 상품 상세정보 화면이 존재한다.
이 애플리케이션에 이런 기능을 추가해 보자:
- 브라우저 주소 표시줄에 URL을 입력하면 해당 제품 화면으로 이동한다.
- 싱글 페이지 애플리케이션에서 링크를 클릭하면 화면을 전환한다.
- 브라우저의 "뒤로 가기", "앞으로 가기" 버튼을 누르면 브라우저 히스토리를 기반으로 이동한다.
1. URL 경로와 컴포넌트 연결하기
이 애플리케이션에는 이미 Angular Router
를 사용해서 ProductListComponent
로 화면을 전환하는 기능이 추가되어 있다. 이번 섹션에서는 상품의 상세정보 화면으로 이동하는 라우팅 규칙을 추가해 보자.
상품의 상세정보를 표시하는 컴포넌트를 생성한다. 파일 목록에서 app
폴더에 마우스 오른쪽 버튼을 클릭하고 Angular Generator
를 선택한 후에 Component
를 선택하면 된다. 이 때 컴포넌트의 이름은 product-details
라고 지정한다.
app.module.ts
파일을 열고 상품 상세정보에 해당하는 라우팅 규칙을 추가한다. path
는 products/:productId
를 지정하고 component
는 ProductDetailsComponent
를 지정하면 된다.
@NgModule({
imports: [
BrowserModule,
ReactiveFormsModule,
RouterModule.forRoot([
{ path: '', component: ProductListComponent },
{ path: 'products/:productId', component: ProductDetailsComponent },
])
],
declarations: [
AppComponent,
TopBarComponent,
ProductListComponent,
ProductAlertsComponent,
ProductDetailsComponent,
],
product-list.component.html
파일을 연다.
상품 이름을 표시하는 <a>
링크에 routerLink
를 추가하고 인자로 product.id
를 지정한다.
<div *ngFor="let product of products">
<h3>
<a
[title]="product.name + ' details'"
[routerLink]="['/products', product.id]"
>
{{ product.name }}
</a>
</h3>
<!-- . . . -->
</div>
RouterLink
디렉티브를 활용하면 <a>
엘리먼트를 커스터마이징할 수 있다. 이 경우에 라우팅 경로는 /products
라는 URL 세그먼트로 시작된다. 그리고 마지막 세그먼트는 해당 상품의 id
프로퍼티 값이 할당된다. 그래서 상품의 id
값이 1인 경우라면 최종 URL은 https://getting-started-myfork.stackblitz.io/products/1
과 같은 형태가 된다.
상품의 이름을 클릭했을 때 라우터가 제대로 동작하는지 확인해 보자. 그러면 ProductDetailsComponent
로 전환되면서 "product-details works!"라는 문구가 화면에 표시된다. 미리보기 화면의 URL도 변경된 것을 확인해 보자. products/#
라는 주소의 마지막 세그먼트 #
는 이전에 클릭한 상품의 id
값이 된다.
2. 상품 상세정보 표시하기
ProductDetailsComponent
는 개별 상품의 정보를 표시한다. 그리고 Angular Router
는 사전에 정의된 라우팅 규칙 중에서 현재 브라우저의 URL에 매칭되는 컴포넌트를 화면에 표시한다.
이번 섹션에서는 products
데이터와 라우팅 규칙을 결합해서 특정 상품의 상세정보를 화면에 표시해 보자.
product-details.component.ts
파일에 @angular/router
패키지로 제공되는 ActivatedRoute
와 ..products
파일로 준비된 products
배열을 불러온다.
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { Product, products } from "../products";
product
프로퍼티를 정의한다.
export class ProductDetailsComponent implements OnInit {
product: Product | undefined;
/* ... */
}
생성자 소괄호 안에 private route: ActivatedRoute
를 추가해서 ActivatedRoute
를 constructor()
안에 의존성으로 주입한다.
export class ProductDetailsComponent implements OnInit {
product: Product | undefined;
constructor(private route: ActivatedRoute) {}
}
ActivatedRoute
는 Angular Router
가 로드한 개별 컴포넌트에 대한 정보를 담고 있다. 이 객체를 참조하면 해당 컴포넌트가 표시될 때 사용된 라우팅 규칙이나 라우팅 인자를 확인할 수 있다. ActivatedRoute
를 의존성으로 주입하면 이제 컴포넌트에서 이 서비스를 사용할 준비는 끝났다.
Note
- 서비스를 사용해서 데이터를 다루는 방법에 대해 자세하게 알아보려면 해당 문서를 참고한다.
ngOnInit()
메서드 안에서 라우팅 인자로 전달된 productId
를 참조하고, 이 값에 해당되는 상품을 products
배열 안에서 찾는다.
ngOnInit() {
// First get the product id from the current route.
const routeParams = this.route.snapshot.paramMap;
const productIdFromRoute = Number(routeParams.get('productId'));
// Find the product that correspond with the id provided in route.
this.product = products.find(product => product.id === productIdFromRoute);
}
경로 매개변수는 경로에 정의한 경로 변수에 해당한다. 경로 매개변수에 액세스하기 위해 특정 시점의 활성 경로에 대한 정보가 포함된 ActivatedRouteSnapshot
인 route.snapshot
을 사용한다. 경로와 일치하는 URL은 productId
를 제공하기 때문에 Angular는 productId
를 사용하여 각 고유 상품에 대한 상세정보를 표시한다.
상품의 상세정보를 표시할 수 있도록 ProductDetailsComponent
템플릿을 수정하는데, 이 때 *ngIf
를 사용한다. 이렇게 작성하면 해당 상품이 존재할 때만 <div>
엘리먼트가 렌더링되면서 상품의 이름, 가격, 설명이 화면에 표시된다.
<h2>Product Details</h2>
<div *ngIf="product">
<h3>{{ product.name }}</h3>
<h4>{{ product.price | currency }}</h4>
<p>{{ product.description }}</p>
</div>
<h4>{{ product.price | currency }}</h4>
코드를 보면 product.price
는 숫자 타입이지만 금액 형식으로 표시하기 위해 currency
파이프를 사용했다. 파이프는 HTML 템플릿에 표시되는 데이터의 형식을 조작할 때 사용한다.
Note
- Angular 파이프에 대해 자세하게 알아보려면 파이프 문서를 참고한다.
이제 사용자가 상품 목록에서 상품의 이름을 클릭하면 라우터가 해당 상품에 해당하는 URL로 이동하면서 ProductDetailsComponent
가 화면에 표시되고, 상품의 상세정보가 화면에 표시된다.
Note
- Angular
Router
에 대해 더 자세하게 알아보려면 라우팅 & 네비게이션 문서를 참고한다.