심화 가이드
라우팅

라우팅 규칙 상태 읽기

Angular 라우터를 사용하면 라우팅 규칙에 연결된 정보를 읽고 활용해서 변화와 컨텍스트에 반응하는 컴포넌트를 만들 수 있습니다.

ActivatedRoute로 현재 라우팅 규칙 정보 가져오기

ActivatedRoute는 현재 라우팅 규칙에 연결된 모든 정보를 제공하는 @angular/router의 서비스입니다.

import {Component, inject} from '@angular/core';
import {ActivatedRoute} from '@angular/router';

@Component({
  selector: 'app-product',
})
export class Product {
  private activatedRoute = inject(ActivatedRoute);

  constructor() {
    console.log(this.activatedRoute);
  }
}

ActivatedRoute는 라우팅 규칙의 다양한 정보를 제공합니다. 자주 사용하는 프로퍼티는 다음과 같습니다.

프로퍼티 설명
url 라우팅 규칙 경로의 Observable입니다. 라우팅 규칙 경로의 각 부분을 UrlSegment 객체 배열로 나타냅니다.
data 라우팅 규칙에 제공한 data 객체를 포함하는 Observable입니다. resolve 가드에서 가져온 값도 포함합니다.
params 해당 라우팅 규칙의 필수 파라미터와 선택 파라미터를 포함하는 Observable입니다.
queryParams 모든 라우팅 규칙에서 사용할 수 있는 쿼리 파라미터를 포함하는 Observable입니다.
resources 라우팅 규칙에 정의된 Resource 인스턴스의 선택적 레코드입니다(withRouterResources를 활성화한 경우).

라우팅 규칙에서 접근할 수 있는 전체 정보는 ActivatedRoute API 문서를 참고하세요.

라우팅 규칙 스냅샷 이해하기

페이지 내비게이션은 시간에 따라 발생하는 이벤트입니다. 라우팅 규칙 스냅샷을 가져오면 특정 시점의 라우터 상태에 접근할 수 있습니다.

라우팅 규칙 스냅샷에는 파라미터, 데이터, 자식 라우팅 규칙 등 라우팅 규칙의 핵심 정보가 포함됩니다. 스냅샷은 정적이므로 이후에 발생하는 변화는 반영하지 않습니다.

라우팅 규칙 스냅샷에는 다음과 같이 접근할 수 있습니다.

import {Component, inject} from '@angular/core';
import {ActivatedRoute} from '@angular/router';

@Component(/* ... */)
export class UserProfile {
  readonly userId: string | null;
  private route = inject(ActivatedRoute);

  constructor() {
    // 예제 URL: https://www.angular.dev/users/123?role=admin&status=active#contact

    // 스냅샷에서 라우팅 규칙 파라미터에 접근합니다.
    this.userId = this.route.snapshot.paramMap.get('id');

    // 여러 라우팅 규칙 정보에 접근합니다.
    const snapshot = this.route.snapshot;
    console.log({
      url: snapshot.url, // Array of UrlSegments matched by this route
      // 라우팅 규칙 파라미터 객체: {id: '123'}
      params: snapshot.params,
      // 쿼리 파라미터 객체: {role: 'admin', status: 'active'}
      queryParams: snapshot.queryParams, // 쿼리 파라미터
    });
  }
}

접근할 수 있는 전체 프로퍼티는 ActivatedRoute API 문서와 ActivatedRouteSnapshot API 문서를 참고하세요.

라우팅 규칙 파라미터 읽기

라우팅 규칙에서는 라우팅 규칙 파라미터와 쿼리 파라미터를 사용할 수 있습니다.

라우팅 규칙 파라미터

라우팅 규칙 파라미터는 URL을 통해 컴포넌트에 데이터를 전달하는 방식입니다. 사용자 ID나 상품 ID처럼 URL의 식별자를 기준으로 특정 내용을 표시할 때 유용합니다.

파라미터 이름 앞에 콜론(:)을 붙이면 라우팅 규칙 파라미터를 정의할 수 있습니다.

import {Routes} from '@angular/router';
import {Product} from './product';

const routes: Routes = [{path: 'product/:id', component: Product}];

route.params를 구독하면 파라미터에 접근할 수 있습니다.

import {Component, inject, signal} from '@angular/core';
import {ActivatedRoute} from '@angular/router';

@Component({
  selector: 'app-product-detail',
  template: `<h1>Product Details: {{ productId() }}</h1>`,
})
export class ProductDetail {
  productId = signal('');
  private activatedRoute = inject(ActivatedRoute);

  constructor() {
    // 라우팅 규칙 파라미터에 접근합니다.
    this.activatedRoute.params.subscribe((params) => {
      this.productId.set(params['id']);
    });
  }
}

쿼리 파라미터

쿼리 파라미터는 라우팅 규칙 구조에 영향을 주지 않고 URL로 선택적인 데이터를 유연하게 전달하는 방식입니다. 라우팅 규칙 파라미터와 달리 내비게이션이 발생해도 유지할 수 있기 때문에 필터, 정렬, 페이지 나누기 등 상태가 있는 UI를 처리할 때 적합합니다.

// 파라미터가 하나인 구조
// /products?category=electronics
router.navigate(['/products'], {
  queryParams: {category: 'electronics'},
});

// 여러 파라미터
// /products?category=electronics&sort=price&page=1
router.navigate(['/products'], {
  queryParams: {
    category: 'electronics',
    sort: 'price',
    page: 1,
  },
});

route.queryParams로 쿼리 파라미터에 접근할 수 있습니다.

다음 ProductList 예제는 상품 목록을 표시하는 방식에 영향을 주는 쿼리 파라미터를 갱신합니다.

import {Component, inject} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';

@Component({
  selector: 'app-product-list',
  template: `
    <div>
      <select (change)="updateSort($event)">
        <option value="price">Price</option>
        <option value="name">Name</option>
      </select>
      <!-- 상품 목록 -->
    </div>
  `,
})
export class ProductList {
  private route = inject(ActivatedRoute);
  private router = inject(Router);

  constructor() {
    // 반응형으로 쿼리 파라미터에 접근합니다.
    this.route.queryParams.subscribe((params) => {
      const sort = params['sort'] || 'price';
      const page = Number(params['page']) || 1;
      this.loadProducts(sort, page);
    });
  }

  updateSort(event: Event) {
    const sort = (event.target as HTMLSelectElement).value;
    // 새 쿼리 파라미터로 URL을 갱신합니다.
    this.router.navigate([], {
      queryParams: {sort},
      queryParamsHandling: 'merge', // 다른 쿼리 파라미터를 유지합니다.
    });
  }
}

이 예제에서 사용자는 select 엘리먼트로 상품 목록을 이름이나 가격순으로 정렬할 수 있습니다. 연결된 변경 핸들러가 URL의 쿼리 파라미터를 갱신하면 변경 이벤트가 발생합니다. 이 이벤트에서 갱신된 쿼리 파라미터를 읽어 상품 목록을 갱신할 수 있습니다.

자세한 내용은 QueryParamsHandling 공식 문서를 참고하세요.

매트릭스 파라미터

매트릭스 파라미터는 전체 라우팅 규칙이 아닌 특정 URL 세그먼트에 속하는 선택적 파라미터입니다. ? 뒤에 위치하고 전체에 적용되는 쿼리 파라미터와 달리, 매트릭스 파라미터는 세미콜론(;)을 사용하며 개별 경로 세그먼트에만 적용됩니다.

라우팅 규칙 정의나 매칭 방식에 영향을 주지 않고 특정 라우팅 규칙 세그먼트에 보조 데이터를 전달할 때 유용합니다. 쿼리 파라미터처럼 라우팅 규칙 설정에 정의할 필요가 없습니다.

// URL 형식: /path;key=value
// 여러 파라미터: /path;key1=value1;key2=value2

// 매트릭스 파라미터와 함께 이동합니다.
this.router.navigate(['/awesome-products', {view: 'grid', filter: 'new'}]);
// 결과 URL: /awesome-products;view=grid;filter=new

ActivatedRoute 사용하기

import {Component, inject} from '@angular/core';
import {ActivatedRoute} from '@angular/router';

@Component(/* ... */)
export class AwesomeProducts {
  private route = inject(ActivatedRoute);

  constructor() {
    // params로 매트릭스 파라미터에 접근합니다.
    this.route.params.subscribe((params) => {
      const view = params['view']; // 예: 'grid'
      const filter = params['filter']; // 예: 'new'
    });
  }
}

참고: ActivatedRoute 대신 withComponentInputBinding을 사용하는 경우에는 매트릭스 파라미터도 컴포넌트 입력 프로퍼티에 바인딩됩니다.

RouterLinkActive로 현재 활성 라우팅 규칙 감지하기

RouterLinkActive 디렉티브를 사용하면 현재 활성 라우팅 규칙에 따라 내비게이션 엘리먼트의 스타일을 동적으로 적용할 수 있습니다. 사용자에게 현재 활성 라우팅 규칙을 알리는 내비게이션 엘리먼트에서 자주 사용합니다.

<nav>
  <a
    class="button"
    routerLink="/about"
    routerLinkActive="active-button"
    ariaCurrentWhenActive="page"
  >
    About
  </a>
  |
  <a
    class="button"
    routerLink="/settings"
    routerLinkActive="active-button"
    ariaCurrentWhenActive="page"
  >
    Settings
  </a>
</nav>

이 예제에서 URL이 routerLink와 일치하면 Angular 라우터는 해당 앵커 링크에 active-button 클래스를 적용하고 ariaCurrentWhenActive를 page로 설정합니다.

엘리먼트에 여러 클래스를 추가하려면 공백으로 구분한 문자열이나 배열을 사용할 수 있습니다.

<!-- 공백으로 구분한 문자열 문법 -->
<a routerLink="/user/bob" routerLinkActive="class1 class2">Bob</a>

<!-- 배열 문법 -->
<a routerLink="/user/bob" [routerLinkActive]="['class1', 'class2']">Bob</a>

routerLinkActive만 설정해도 aria-current가 설정되는 것은 아닙니다. 적용된 스타일 차이를 인지하기 어려운 시각 장애인 사용자도 활성 링크를 구별할 수 있도록, 앞의 예제처럼 ariaCurrentWhenActive 입력 프로퍼티를 설정하세요.

라우팅 규칙 매칭 전략

기본적으로 RouterLinkActive는 라우팅 규칙의 모든 상위 경로도 매칭되는 것으로 판단합니다.

<a [routerLink]="['/user/jane']" routerLinkActive="active-link"> User </a>
<a [routerLink]="['/user/jane/role/admin']" routerLinkActive="active-link"> Role </a>

사용자가 /user/jane/role/admin에 방문하면 두 링크 모두 active-link 클래스를 갖습니다.

라우팅 규칙이 정확히 일치할 때만 RouterLinkActive 적용하기

정확하게 일치할 때만 클래스를 적용하려면 routerLinkActiveOptions 디렉티브에 exact: true를 포함한 설정 객체를 전달하세요.

<a
  [routerLink]="['/user/jane']"
  routerLinkActive="active-link"
  [routerLinkActiveOptions]="{exact: true}"
>
  User
</a>
<a
  [routerLink]="['/user/jane/role/admin']"
  routerLinkActive="active-link"
  [routerLinkActiveOptions]="{exact: true}"
>
  Role
</a>

라우팅 규칙 매칭 방식을 더 세밀하게 지정할 수도 있습니다. exact: true는 실제로 다음 매칭 옵션 전체를 간단하게 표현한 문법입니다.

// `exact: true`는 다음 설정과 같습니다.
{
  paths: 'exact',
  fragment: 'ignored',
  matrixParams: 'ignored',
  queryParams: 'exact',
}

// `exact: false`는 다음 설정과 같습니다.
{
  paths: 'subset',
  fragment: 'ignored',
  matrixParams: 'ignored',
  queryParams: 'subset',
}

자세한 내용은 isActiveMatchOptions 공식 문서를 참고하세요.

상위 엘리먼트에 RouterLinkActive 적용하기

RouterLinkActive 디렉티브는 상위 엘리먼트에도 적용할 수 있습니다. 이 방식으로 원하는 엘리먼트에 스타일을 지정할 수 있습니다.

<div routerLinkActive="active-link" [routerLinkActiveOptions]="{exact: true}">
  <a routerLink="/user/jim">Jim</a>
  <a routerLink="/user/bob">Bob</a>
</div>

자세한 내용은 RouterLinkActive API 문서를 참고하세요.

URL 활성 여부 확인하기

isActive 함수는 특정 URL이 현재 라우터에서 활성 상태인지 추적하는 computed() 시그널을 반환합니다. 이 시그널은 라우터 상태가 바뀌면 자동으로 갱신됩니다.

import {Component, inject} from '@angular/core';
import {isActive, Router} from '@angular/router';

@Component({
  template: `
    <div [class.active]="isSettingsActive()">
      <h2>Settings</h2>
    </div>
  `,
})
export class Panel {
  private router = inject(Router);

  isSettingsActive = isActive('/settings', this.router, {
    paths: 'subset',
    queryParams: 'ignored',
    fragment: 'ignored',
    matrixParams: 'ignored',
  });
}