이번 실습에서는 Angular 서비스를 만들고 의존성 주입으로 앱에서 사용하는 방법을 알아봅니다.
학습 목표
앱에 데이터를 제공하는 서비스를 만듭니다. 이번 실습을 마치면 서비스가 로컬의 정적 데이터를 읽어 제공합니다. 이후 실습에서는 웹 서비스에서 데이터를 가져오도록 수정합니다.
서비스 개념 살펴보기
이번 실습에서는 Angular 서비스와 의존성 주입을 소개합니다.
Angular 서비스
Angular 서비스 를 사용하면 앱의 여러 컴포넌트에서 사용하는 데이터와 기능을 분리할 수 있습니다. 여러 컴포넌트에서 서비스를 사용하려면 주입 가능한 상태 로 만들어야 합니다. 주입 가능하며 컴포넌트에서 사용하는 서비스는 해당 컴포넌트의 의존성이 됩니다. 컴포넌트는 이 서비스에 의존하므로 서비스 없이 동작할 수 없습니다.
의존성 주입
의존성 주입 은 앱의 컴포넌트가 필요로 하는 의존성과 다른 컴포넌트에서 사용할 서비스를 관리하는 방식입니다.
-
앱에 새 서비스 만들기
이 단계에서는 앱에 주입 가능한 서비스를 만듭니다.
IDE의 터미널 창에서 다음 작업을 수행하세요:
프로젝트 디렉터리에서
first-app디렉터리로 이동하세요.first-app디렉터리에서 다음 명령어로 새 서비스를 만드세요.ng generate service housing --skip-testsng serve를 실행해 앱을 빌드하고http://localhost:4200에서 실행하세요.앱이 오류 없이 빌드되는지 확인하세요. 오류가 있다면 수정한 후 다음 단계로 진행하세요.
-
새 서비스에 정적 데이터 추가하기
이 단계에서는 새 서비스에 예제 데이터를 추가합니다. 이후 실습에서는 실제 앱처럼 웹 인터페이스에서 데이터를 가져오도록 정적 데이터를 교체합니다. 지금은
Home에 작성해 둔 로컬 데이터를 새 서비스에서 사용합니다.IDE의 편집 창에서 다음 작업을 수행하세요:
src/app/home/home.ts의Home에서baseUrl변수와housingLocationList변수의 배열 값을 복사하세요.src/app/housing.service.ts에서 다음 작업을 수행하세요:HousingService클래스 안에 앞 단계에서Home으로부터 복사한 변수를 붙여 넣으세요.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를 반환하거나 전체 목록을 반환한다는 점만 이해하면 됩니다.파일 수준에
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); } }
앱이 오류 없이 빌드되는지 확인하세요. 오류가 있다면 수정한 후 다음 단계로 진행하세요.
-
Home에 새 서비스 주입하기이 단계에서는
Home에 새 서비스를 주입해 서비스에서 앱의 데이터를 읽을 수 있도록 합니다. 이후 실습에서는 실제 앱처럼 데이터를 가져오도록 정적 데이터를 실제 데이터 소스로 교체합니다.IDE의 편집 창에서
src/app/home/home.ts를 수정하세요: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(); } }- 파일 수준에
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(); } }Home의baseUrl변수를 삭제하고housingLocationList배열의 항목을 지운 다음 빈 배열([])을 할당하세요. 잠시 후HousingService에서 데이터를 가져오도록 코드를 수정합니다.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(); } }src/app/home/home.ts의 변경 사항을 저장하고 앱이 오류 없이 빌드되는지 확인하세요. 오류가 있다면 수정한 후 다음 단계로 진행하세요.
SUMMARY: 이번 실습에서는 앱에 Angular 서비스를 추가하고 Home 클래스에 주입했습니다.
이를 통해 앱에서 데이터를 가져오는 부분을 분리했습니다.
지금은 새 서비스가 정적 데이터 배열에서 데이터를 가져옵니다.
이후 실습에서는 API 엔드포인트에서 데이터를 가져오도록 서비스를 리팩토링합니다.
이 실습에서 다룬 내용을 더 알아보려면 다음 문서를 참고하세요: