Angular 서비스

이번 실습에서는 Angular 서비스를 만들고 의존성 주입으로 앱에서 사용하는 방법을 알아봅니다.

학습 목표

앱에 데이터를 제공하는 서비스를 만듭니다. 이번 실습을 마치면 서비스가 로컬의 정적 데이터를 읽어 제공합니다. 이후 실습에서는 웹 서비스에서 데이터를 가져오도록 수정합니다.

서비스 개념 살펴보기

이번 실습에서는 Angular 서비스와 의존성 주입을 소개합니다.

Angular 서비스

Angular 서비스 를 사용하면 앱의 여러 컴포넌트에서 사용하는 데이터와 기능을 분리할 수 있습니다. 여러 컴포넌트에서 서비스를 사용하려면 주입 가능한 상태 로 만들어야 합니다. 주입 가능하며 컴포넌트에서 사용하는 서비스는 해당 컴포넌트의 의존성이 됩니다. 컴포넌트는 이 서비스에 의존하므로 서비스 없이 동작할 수 없습니다.

의존성 주입

의존성 주입 은 앱의 컴포넌트가 필요로 하는 의존성과 다른 컴포넌트에서 사용할 서비스를 관리하는 방식입니다.

  1. 앱에 새 서비스 만들기

    이 단계에서는 앱에 주입 가능한 서비스를 만듭니다.

    IDE의 터미널 창에서 다음 작업을 수행하세요:

    1. 프로젝트 디렉터리에서 first-app 디렉터리로 이동하세요.

    2. first-app 디렉터리에서 다음 명령어로 새 서비스를 만드세요.

      ng generate service housing --skip-tests
    3. ng serve를 실행해 앱을 빌드하고 http://localhost:4200에서 실행하세요.

    4. 앱이 오류 없이 빌드되는지 확인하세요. 오류가 있다면 수정한 후 다음 단계로 진행하세요.

  2. 새 서비스에 정적 데이터 추가하기

    이 단계에서는 새 서비스에 예제 데이터를 추가합니다. 이후 실습에서는 실제 앱처럼 웹 인터페이스에서 데이터를 가져오도록 정적 데이터를 교체합니다. 지금은 Home에 작성해 둔 로컬 데이터를 새 서비스에서 사용합니다.

    IDE의 편집 창에서 다음 작업을 수행하세요:

    1. src/app/home/home.ts의 Home에서 baseUrl 변수와 housingLocationList 변수의 배열 값을 복사하세요.

    2. src/app/housing.service.ts에서 다음 작업을 수행하세요:

      1. HousingService 클래스 안에 앞 단계에서 Home으로부터 복사한 변수를 붙여 넣으세요.

      2. HousingService 클래스 안에서 복사한 데이터 다음에 아래 함수를 붙여 넣으세요. 이 함수들은 서비스를 사용하는 쪽에서 서비스의 데이터에 접근할 수 있게 합니다.

        src/app/housing.service.ts의 서비스 함수

        import {Service} from '@angular/core';
        import {HousingLocationInfo} from './housinglocation';
        @Service()
        export class HousingService {
          readonly baseUrl = 'https://angular.dev/assets/images/tutorials/common';
        
          protected housingLocationList: HousingLocationInfo[] = [
            {
              id: 0,
              name: 'Acme Fresh Start Housing',
              city: 'Chicago',
              state: 'IL',
              photo: `${this.baseUrl}/bernard-hermant-CLKGGwIBTaY-unsplash.jpg`,
              availableUnits: 4,
              wifi: true,
              laundry: true,
            },
            {
              id: 1,
              name: 'A113 Transitional Housing',
              city: 'Santa Monica',
              state: 'CA',
              photo: `${this.baseUrl}/brandon-griggs-wR11KBaB86U-unsplash.jpg`,
              availableUnits: 0,
              wifi: false,
              laundry: true,
            },
            {
              id: 2,
              name: 'Warm Beds Housing Support',
              city: 'Juneau',
              state: 'AK',
              photo: `${this.baseUrl}/i-do-nothing-but-love-lAyXdl1-Wmc-unsplash.jpg`,
              availableUnits: 1,
              wifi: false,
              laundry: false,
            },
            {
              id: 3,
              name: 'Homesteady Housing',
              city: 'Chicago',
              state: 'IL',
              photo: `${this.baseUrl}/ian-macdonald-W8z6aiwfi1E-unsplash.jpg`,
              availableUnits: 1,
              wifi: true,
              laundry: false,
            },
            {
              id: 4,
              name: 'Happy Homes Group',
              city: 'Gary',
              state: 'IN',
              photo: `${this.baseUrl}/krzysztof-hepner-978RAXoXnH4-unsplash.jpg`,
              availableUnits: 1,
              wifi: true,
              laundry: false,
            },
            {
              id: 5,
              name: 'Hopeful Apartment Group',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/r-architecture-JvQ0Q5IkeMM-unsplash.jpg`,
              availableUnits: 2,
              wifi: true,
              laundry: true,
            },
            {
              id: 6,
              name: 'Seriously Safe Towns',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/phil-hearing-IYfp2Ixe9nM-unsplash.jpg`,
              availableUnits: 5,
              wifi: true,
              laundry: true,
            },
            {
              id: 7,
              name: 'Hopeful Housing Solutions',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/r-architecture-GGupkreKwxA-unsplash.jpg`,
              availableUnits: 2,
              wifi: true,
              laundry: true,
            },
            {
              id: 8,
              name: 'Seriously Safe Towns',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/saru-robert-9rP3mxf8qWI-unsplash.jpg`,
              availableUnits: 10,
              wifi: false,
              laundry: false,
            },
            {
              id: 9,
              name: 'Capital Safe Towns',
              city: 'Portland',
              state: 'OR',
              photo: `${this.baseUrl}/webaliser-_TPTXZd9mOo-unsplash.jpg`,
              availableUnits: 6,
              wifi: true,
              laundry: true,
            },
          ];
        
          getAllHousingLocations(): HousingLocationInfo[] {
            return this.housingLocationList;
          }
        
          getHousingLocationById(id: number): HousingLocationInfo | undefined {
            return this.housingLocationList.find((housingLocation) => housingLocation.id === id);
          }
        }

        이 함수들은 이후 실습에서 사용합니다. 지금은 id로 특정 HousingLocationInfo를 반환하거나 전체 목록을 반환한다는 점만 이해하면 됩니다.

      3. 파일 수준에 HousingLocationInfo를 불러오는 import 구문을 추가하세요.

        src/app/housing.service.ts에서 HousingLocationInfo 타입 불러오기

        import {Service} from '@angular/core';
        import {HousingLocationInfo} from './housinglocation';
        @Service()
        export class HousingService {
          readonly baseUrl = 'https://angular.dev/assets/images/tutorials/common';
        
          protected housingLocationList: HousingLocationInfo[] = [
            {
              id: 0,
              name: 'Acme Fresh Start Housing',
              city: 'Chicago',
              state: 'IL',
              photo: `${this.baseUrl}/bernard-hermant-CLKGGwIBTaY-unsplash.jpg`,
              availableUnits: 4,
              wifi: true,
              laundry: true,
            },
            {
              id: 1,
              name: 'A113 Transitional Housing',
              city: 'Santa Monica',
              state: 'CA',
              photo: `${this.baseUrl}/brandon-griggs-wR11KBaB86U-unsplash.jpg`,
              availableUnits: 0,
              wifi: false,
              laundry: true,
            },
            {
              id: 2,
              name: 'Warm Beds Housing Support',
              city: 'Juneau',
              state: 'AK',
              photo: `${this.baseUrl}/i-do-nothing-but-love-lAyXdl1-Wmc-unsplash.jpg`,
              availableUnits: 1,
              wifi: false,
              laundry: false,
            },
            {
              id: 3,
              name: 'Homesteady Housing',
              city: 'Chicago',
              state: 'IL',
              photo: `${this.baseUrl}/ian-macdonald-W8z6aiwfi1E-unsplash.jpg`,
              availableUnits: 1,
              wifi: true,
              laundry: false,
            },
            {
              id: 4,
              name: 'Happy Homes Group',
              city: 'Gary',
              state: 'IN',
              photo: `${this.baseUrl}/krzysztof-hepner-978RAXoXnH4-unsplash.jpg`,
              availableUnits: 1,
              wifi: true,
              laundry: false,
            },
            {
              id: 5,
              name: 'Hopeful Apartment Group',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/r-architecture-JvQ0Q5IkeMM-unsplash.jpg`,
              availableUnits: 2,
              wifi: true,
              laundry: true,
            },
            {
              id: 6,
              name: 'Seriously Safe Towns',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/phil-hearing-IYfp2Ixe9nM-unsplash.jpg`,
              availableUnits: 5,
              wifi: true,
              laundry: true,
            },
            {
              id: 7,
              name: 'Hopeful Housing Solutions',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/r-architecture-GGupkreKwxA-unsplash.jpg`,
              availableUnits: 2,
              wifi: true,
              laundry: true,
            },
            {
              id: 8,
              name: 'Seriously Safe Towns',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/saru-robert-9rP3mxf8qWI-unsplash.jpg`,
              availableUnits: 10,
              wifi: false,
              laundry: false,
            },
            {
              id: 9,
              name: 'Capital Safe Towns',
              city: 'Portland',
              state: 'OR',
              photo: `${this.baseUrl}/webaliser-_TPTXZd9mOo-unsplash.jpg`,
              availableUnits: 6,
              wifi: true,
              laundry: true,
            },
          ];
        
          getAllHousingLocations(): HousingLocationInfo[] {
            return this.housingLocationList;
          }
        
          getHousingLocationById(id: number): HousingLocationInfo | undefined {
            return this.housingLocationList.find((housingLocation) => housingLocation.id === id);
          }
        }
    3. 앱이 오류 없이 빌드되는지 확인하세요. 오류가 있다면 수정한 후 다음 단계로 진행하세요.

  3. Home에 새 서비스 주입하기

    이 단계에서는 Home에 새 서비스를 주입해 서비스에서 앱의 데이터를 읽을 수 있도록 합니다. 이후 실습에서는 실제 앱처럼 데이터를 가져오도록 정적 데이터를 실제 데이터 소스로 교체합니다.

    IDE의 편집 창에서 src/app/home/home.ts를 수정하세요:

    1. src/app/home/home.ts 위쪽에서 @angular/core로부터 불러오는 항목에 inject를 추가하세요. 이렇게 하면 Home에서 inject 함수를 사용할 수 있습니다.

    src/app/home/home.ts 수정하기

    import {Component, inject} from '@angular/core';
    import {HousingLocation} from '../housing-location/housing-location';
    import {HousingLocationInfo} from '../housinglocation';
    import {HousingService} from '../housing.service';
    @Component({
      selector: 'app-home',
      imports: [HousingLocation],
      template: `
        <section>
          <form>
            <input type="text" placeholder="Filter by city" />
            <button class="primary" type="button">Search</button>
          </form>
        </section>
        <section class="results">
          @for (housingLocation of housingLocationList; track $index) {
            <app-housing-location [housingLocation]="housingLocation" />
          }
        </section>
      `,
      styleUrls: ['./home.css'],
    })
    export class Home {
      housingLocationList: HousingLocationInfo[] = [];
      housingService: HousingService = inject(HousingService);
    
      constructor() {
        this.housingLocationList = this.housingService.getAllHousingLocations();
      }
    }
    1. 파일 수준에 HousingService를 불러오는 import 구문을 추가하세요:

    src/app/home/home.ts에 import 추가하기

    import {Component, inject} from '@angular/core';
    import {HousingLocation} from '../housing-location/housing-location';
    import {HousingLocationInfo} from '../housinglocation';
    import {HousingService} from '../housing.service';
    @Component({
      selector: 'app-home',
      imports: [HousingLocation],
      template: `
        <section>
          <form>
            <input type="text" placeholder="Filter by city" />
            <button class="primary" type="button">Search</button>
          </form>
        </section>
        <section class="results">
          @for (housingLocation of housingLocationList; track $index) {
            <app-housing-location [housingLocation]="housingLocation" />
          }
        </section>
      `,
      styleUrls: ['./home.css'],
    })
    export class Home {
      housingLocationList: HousingLocationInfo[] = [];
      housingService: HousingService = inject(HousingService);
    
      constructor() {
        this.housingLocationList = this.housingService.getAllHousingLocations();
      }
    }
    1. Home의 baseUrl 변수를 삭제하고 housingLocationList 배열의 항목을 지운 다음 빈 배열([])을 할당하세요. 잠시 후 HousingService에서 데이터를 가져오도록 코드를 수정합니다.

    2. Home에 다음 코드를 추가해 새 서비스를 주입하고 앱의 데이터를 초기화하세요. constructor는 컴포넌트 인스턴스를 생성할 때 실행됩니다. 이 안에서 getAllHousingLocations가 반환한 값을 housingLocationList에 할당합니다.

    src/app/home/home.ts에서 서비스 데이터로 초기화하기

    import {Component, inject} from '@angular/core';
    import {HousingLocation} from '../housing-location/housing-location';
    import {HousingLocationInfo} from '../housinglocation';
    import {HousingService} from '../housing.service';
    @Component({
      selector: 'app-home',
      imports: [HousingLocation],
      template: `
        <section>
          <form>
            <input type="text" placeholder="Filter by city" />
            <button class="primary" type="button">Search</button>
          </form>
        </section>
        <section class="results">
          @for (housingLocation of housingLocationList; track $index) {
            <app-housing-location [housingLocation]="housingLocation" />
          }
        </section>
      `,
      styleUrls: ['./home.css'],
    })
    export class Home {
      housingLocationList: HousingLocationInfo[] = [];
      housingService: HousingService = inject(HousingService);
    
      constructor() {
        this.housingLocationList = this.housingService.getAllHousingLocations();
      }
    }
    1. src/app/home/home.ts의 변경 사항을 저장하고 앱이 오류 없이 빌드되는지 확인하세요. 오류가 있다면 수정한 후 다음 단계로 진행하세요.
  4. SUMMARY: 이번 실습에서는 앱에 Angular 서비스를 추가하고 Home 클래스에 주입했습니다. 이를 통해 앱에서 데이터를 가져오는 부분을 분리했습니다. 지금은 새 서비스가 정적 데이터 배열에서 데이터를 가져옵니다. 이후 실습에서는 API 엔드포인트에서 데이터를 가져오도록 서비스를 리팩토링합니다.

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