앱에 상세 페이지 연결하기

이번 실습에서는 앱에 상세 페이지를 연결하는 방법을 알아봅니다.

IMPORTANT: 라우팅 실습은 로컬 환경에서 진행하는 것을 권장합니다.

학습 목표

이번 실습을 마치면 애플리케이션에서 상세 페이지로 이동할 수 있습니다.

라우팅 파라미터 개념 살펴보기

각 주택에는 사용자가 상세 페이지로 이동했을 때 표시할 고유한 정보가 있습니다. 이 정보를 표시하려면 라우팅 파라미터를 사용해야 합니다.

라우팅 파라미터를 사용하면 URL 경로에 동적 정보를 포함할 수 있습니다. 사용자가 선택한 주택을 식별하기 위해 HousingLocationInfo 타입의 id 프로퍼티를 사용합니다.

  1. routerLink로 동적 경로 이동하기

    실습 10에서 src/app/routes.ts에 두 번째 라우팅 규칙을 추가했습니다. 이 규칙에는 라우팅 파라미터 id를 나타내는 특별한 경로 구간이 있습니다:

    'details/:id'

    여기서 :id는 동적 값이며 코드에서 어떤 경로를 요청하는지에 따라 달라집니다.

    1. src/app/housing-location/housing-location.ts의 section 엘리먼트에 링크 태그를 추가하고 routerLink 디렉티브를 지정하세요:

      housing-location.ts에 routerLink 디렉티브가 있는 링크 추가하기

      import {Component, input} from '@angular/core';
      import {HousingLocationInfo} from '../housinglocation';
      import {RouterLink} from '@angular/router';
      
      @Component({
        selector: 'app-housing-location',
        imports: [RouterLink],
        template: `
          <section class="listing">
            <img
              class="listing-photo"
              [src]="housingLocation().photo"
              alt="Exterior photo of {{ housingLocation().name }}"
              crossorigin
            />
            <h2 class="listing-heading">{{ housingLocation().name }}</h2>
            <p class="listing-location">{{ housingLocation().city }}, {{ housingLocation().state }}</p>
            <a [routerLink]="['/details', housingLocation().id]">Learn More</a>
          </section>
        `,
        styleUrls: ['./housing-location.css'],
      })
      export class HousingLocation {
        housingLocation = input.required<HousingLocationInfo>();
      }

      routerLink 디렉티브를 사용하면 Angular 라우터가 애플리케이션에 동적 링크를 만들 수 있습니다. routerLink에 전달하는 배열은 경로의 고정된 부분과 동적 데이터, 두 항목으로 구성됩니다.

      템플릿에서 routerLink를 사용하려면 파일 수준에서 @angular/router의 RouterLink를 불러오고 컴포넌트의 imports 배열에도 추가하세요.

    2. 이제 앱의 라우팅이 동작하는지 확인할 수 있습니다. 브라우저에서 홈 페이지를 새로고침하고 주택의 "Learn More" 버튼을 클릭하세요.

    'details works!' 문구가 표시된 상세 페이지
  2. 라우팅 파라미터 가져오기

    이 단계에서는 Details에서 라우팅 파라미터를 읽습니다. 현재 앱에는 details works!가 표시됩니다. 다음으로 라우팅 파라미터로 전달받은 id 값을 표시하도록 코드를 수정합니다.

    1. src/app/details/details.ts에서 Details에 필요한 함수, 클래스, 서비스를 불러오도록 import 구문을 수정하세요:

    파일 수준 import 수정하기

    import {Component, inject} from '@angular/core';
    import {ActivatedRoute} from '@angular/router';
    import {HousingService} from '../housing.service';
    import {HousingLocationInfo} from '../housinglocation';
    
    @Component({
      selector: 'app-details',
      template: `
        <article>
          <img
            class="listing-photo"
            [src]="housingLocation?.photo"
            alt="Exterior photo of {{ housingLocation?.name }}"
            crossorigin
          />
          <section class="listing-description">
            <h2 class="listing-heading">{{ housingLocation?.name }}</h2>
            <p class="listing-location">{{ housingLocation?.city }}, {{ housingLocation?.state }}</p>
          </section>
          <section class="listing-features">
            <h2 class="section-heading">About this housing location</h2>
            <ul>
              <li>Units available: {{ housingLocation?.availableUnits }}</li>
              <li>Does this location have wifi: {{ housingLocation?.wifi }}</li>
              <li>Does this location have laundry: {{ housingLocation?.laundry }}</li>
            </ul>
          </section>
        </article>
      `,
      styleUrls: ['./details.css'],
    })
    export class Details {
      route: ActivatedRoute = inject(ActivatedRoute);
      housingService = inject(HousingService);
      housingLocation: HousingLocationInfo | undefined;
    
      constructor() {
        const housingLocationId = Number(this.route.snapshot.params['id']);
        this.housingLocation = this.housingService.getHousingLocationById(housingLocationId);
      }
    }
    1. @Component 데코레이터의 template 프로퍼티를 수정해 housingLocationId 값을 표시하세요:

      template: `<p>details works! {{ housingLocationId }}</p>`,
    2. Details 클래스의 본문을 다음 코드로 수정하세요:

      export class Details {
         route: ActivatedRoute = inject(ActivatedRoute);
         housingLocationId = -1;
         constructor() {
         this.housingLocationId = Number(this.route.snapshot.params['id']);
         }
      }

      이 코드는 Details에서 현재 라우팅 정보를 제공하는 ActivatedRoute를 사용하게 합니다. constructor에서는 라우팅 파라미터로 가져온 id를 문자열에서 숫자로 변환합니다.

    3. 모든 변경 사항을 저장하세요.

    4. 브라우저에서 주택의 "Learn More" 링크를 클릭하고 페이지의 숫자가 해당 주택 데이터의 id 프로퍼티와 일치하는지 확인하세요.

  3. Details 수정하기

    이제 라우팅이 정상적으로 동작하므로 Details 템플릿을 수정해 라우팅 파라미터에 해당하는 주택의 구체적인 정보를 표시합니다.

    데이터를 가져오기 위해 HousingService를 호출합니다.

    1. 템플릿을 다음 코드와 같이 수정하세요:

      src/app/details/details.ts의 Details 템플릿 수정하기

      import {Component, inject} from '@angular/core';
      import {ActivatedRoute} from '@angular/router';
      import {HousingService} from '../housing.service';
      import {HousingLocationInfo} from '../housinglocation';
      
      @Component({
        selector: 'app-details',
        template: `
          <article>
            <img
              class="listing-photo"
              [src]="housingLocation?.photo"
              alt="Exterior photo of {{ housingLocation?.name }}"
              crossorigin
            />
            <section class="listing-description">
              <h2 class="listing-heading">{{ housingLocation?.name }}</h2>
              <p class="listing-location">{{ housingLocation?.city }}, {{ housingLocation?.state }}</p>
            </section>
            <section class="listing-features">
              <h2 class="section-heading">About this housing location</h2>
              <ul>
                <li>Units available: {{ housingLocation?.availableUnits }}</li>
                <li>Does this location have wifi: {{ housingLocation?.wifi }}</li>
                <li>Does this location have laundry: {{ housingLocation?.laundry }}</li>
              </ul>
            </section>
          </article>
        `,
        styleUrls: ['./details.css'],
      })
      export class Details {
        route: ActivatedRoute = inject(ActivatedRoute);
        housingService = inject(HousingService);
        housingLocation: HousingLocationInfo | undefined;
      
        constructor() {
          const housingLocationId = Number(this.route.snapshot.params['id']);
          this.housingLocation = this.housingService.getHousingLocationById(housingLocationId);
        }
      }

      housingLocation의 프로퍼티에 옵셔널 체이닝 연산자 ?.로 접근한다는 점을 확인하세요. 이렇게 하면 housingLocation이 null이나 undefined여도 애플리케이션에 오류가 발생하지 않습니다.

    2. Details 클래스의 본문을 다음 코드와 같이 수정하세요:

      src/app/details/details.ts의 Details 클래스 수정하기

      import {Component, inject} from '@angular/core';
      import {ActivatedRoute} from '@angular/router';
      import {HousingService} from '../housing.service';
      import {HousingLocationInfo} from '../housinglocation';
      
      @Component({
        selector: 'app-details',
        template: `
          <article>
            <img
              class="listing-photo"
              [src]="housingLocation?.photo"
              alt="Exterior photo of {{ housingLocation?.name }}"
              crossorigin
            />
            <section class="listing-description">
              <h2 class="listing-heading">{{ housingLocation?.name }}</h2>
              <p class="listing-location">{{ housingLocation?.city }}, {{ housingLocation?.state }}</p>
            </section>
            <section class="listing-features">
              <h2 class="section-heading">About this housing location</h2>
              <ul>
                <li>Units available: {{ housingLocation?.availableUnits }}</li>
                <li>Does this location have wifi: {{ housingLocation?.wifi }}</li>
                <li>Does this location have laundry: {{ housingLocation?.laundry }}</li>
              </ul>
            </section>
          </article>
        `,
        styleUrls: ['./details.css'],
      })
      export class Details {
        route: ActivatedRoute = inject(ActivatedRoute);
        housingService = inject(HousingService);
        housingLocation: HousingLocationInfo | undefined;
      
        constructor() {
          const housingLocationId = Number(this.route.snapshot.params['id']);
          this.housingLocation = this.housingService.getHousingLocationById(housingLocationId);
        }
      }

      이제 선택한 주택에 맞는 정보를 표시하는 코드가 준비되었습니다. constructor에서 HousingService의 getHousingLocationById 함수를 호출하며 라우팅 파라미터를 인자로 전달합니다.

    3. 다음 스타일을 src/app/details/details.css 파일에 복사하세요:

      Details 스타일 추가하기

      .listing-photo {
        height: 600px;
        width: 50%;
        object-fit: cover;
        border-radius: 30px;
        float: right;
      }
      
      .listing-heading {
        font-size: 48pt;
        font-weight: bold;
        margin-bottom: 15px;
      }
      
      .listing-location::before {
        content: url('/public/location-pin.svg') / '';
      }
      
      .listing-location {
        font-size: 24pt;
        margin-bottom: 15px;
      }
      
      .listing-features > .section-heading {
        color: var(--secondary-color);
        font-size: 24pt;
        margin-bottom: 15px;
      }
      
      .listing-features {
        margin-bottom: 20px;
      }
      
      .listing-features li {
        font-size: 14pt;
      }
      
      li {
        list-style-type: none;
      }
      
      .listing-apply .section-heading {
        font-size: 18pt;
        margin-bottom: 15px;
      }
      
      label,
      input {
        display: block;
      }
      label {
        color: var(--secondary-color);
        font-weight: bold;
        text-transform: uppercase;
        font-size: 12pt;
      }
      input {
        font-size: 16pt;
        margin-bottom: 15px;
        padding: 10px;
        width: 400px;
        border-top: none;
        border-right: none;
        border-left: none;
        border-bottom: solid 0.3px;
      }
      @media (max-width: 1024px) {
        .listing-photo {
          width: 100%;
          height: 400px;
        }
      }

      그리고 변경 사항을 저장하세요.

    4. Details에서 방금 만든 details.css 파일을 스타일 파일로 지정하세요:

      details.ts에서 생성한 CSS 파일 사용하기

      import {Component, inject} from '@angular/core';
      import {ActivatedRoute} from '@angular/router';
      import {HousingService} from '../housing.service';
      import {HousingLocationInfo} from '../housinglocation';
      
      @Component({
        selector: 'app-details',
        template: `
          <article>
            <img
              class="listing-photo"
              [src]="housingLocation?.photo"
              alt="Exterior photo of {{ housingLocation?.name }}"
              crossorigin
            />
            <section class="listing-description">
              <h2 class="listing-heading">{{ housingLocation?.name }}</h2>
              <p class="listing-location">{{ housingLocation?.city }}, {{ housingLocation?.state }}</p>
            </section>
            <section class="listing-features">
              <h2 class="section-heading">About this housing location</h2>
              <ul>
                <li>Units available: {{ housingLocation?.availableUnits }}</li>
                <li>Does this location have wifi: {{ housingLocation?.wifi }}</li>
                <li>Does this location have laundry: {{ housingLocation?.laundry }}</li>
              </ul>
            </section>
          </article>
        `,
        styleUrls: ['./details.css'],
      })
      export class Details {
        route: ActivatedRoute = inject(ActivatedRoute);
        housingService = inject(HousingService);
        housingLocation: HousingLocationInfo | undefined;
      
        constructor() {
          const housingLocationId = Number(this.route.snapshot.params['id']);
          this.housingLocation = this.housingService.getHousingLocationById(housingLocationId);
        }
      }
    5. 브라우저를 새로고침하고 주택의 "Learn More" 링크를 클릭했을 때 해당 항목의 데이터에 맞는 상세 정보가 표시되는지 확인하세요.

    주택 정보가 표시된 상세 페이지
  4. Home으로 돌아가는 기능 확인하기

    앞선 실습에서 App 템플릿에 routerLink를 추가했습니다. 이 코드로 로고를 클릭할 때마다 Home으로 돌아갈 수 있습니다.

    1. 코드가 다음과 같은지 확인하세요:

      app.ts의 routerLink 확인하기

      import {Component} from '@angular/core';
      import {Home} from './home/home';
      import {RouterLink, RouterOutlet} from '@angular/router';
      
      @Component({
        selector: 'app-root',
        imports: [Home, RouterLink, RouterOutlet],
        template: `
          <main>
            <a [routerLink]="['/']">
              <header class="brand-name">
                <img class="brand-logo" src="/public/logo.svg" alt="logo" aria-hidden="true" />
              </header>
            </a>
            <section class="content">
              <router-outlet />
            </section>
          </main>
        `,
        styleUrls: ['./app.css'],
      })
      export class App {
        title = 'homes';
      }

      이미 작성한 코드이지만 올바른지 다시 확인하세요.

  5. SUMMARY: 이번 실습에서는 상세 페이지를 표시하는 라우팅을 추가했습니다.

    이제 다음 작업을 할 수 있습니다:

    • 라우팅 파라미터로 이동할 화면에 데이터 전달하기
    • routerLink 디렉티브로 동적 데이터를 사용한 경로 만들기
    • 라우팅 파라미터로 HousingService에서 데이터를 가져와 특정 주택 정보 표시하기

    여기까지 잘 따라오셨습니다.

    이 실습에서 다룬 내용을 더 알아보려면 다음 문서를 참고하세요: