서비스는 데이터 가져오기, 비즈니스 규칙, 공유 상태, 로깅 등 UI가 아닌 로직을 위한 클래스로, 컴포넌트들이 재사용할 수 있습니다. **Dependency Injection(DI)**은 컴포넌트가 직접 서비스를 생성하는 대신, Angular가 필요한 컴포넌트에 그 서비스를 제공하는 방식입니다.
서비스 정의하기
ts
{ } ;
({ : })
{
users = [];
() { .; }
() { ..(u); }
}
서비스는 데이터 가져오기, 비즈니스 규칙, 공유 상태, 로깅 등 UI가 아닌 로직을 위한 클래스로, 컴포넌트들이 재사용할 수 있습니다. **Dependency Injection(DI)**은 컴포넌트가 직접 서비스를 생성하는 대신, Angular가 필요한 컴포넌트에 그 서비스를 제공하는 방식입니다.
{ } ;
({ : })
{
users = [];
() { .; }
() { ..(u); }
}
@Injectable({ providedIn: "root" }) decorator는 그 서비스를 앱 전체를 위한 단일 공유 인스턴스(싱글턴)로 Angular의 DI 시스템에 등록합니다.
@Component({ ... })
export class UserListComponent {
// Angular는 이 생성자 매개변수를 보고 싱글턴을 자동으로 주입한다
constructor(private userService: UserService) {}
users = this.userService.getUsers(); // 그냥 사용하면 된다
}
서비스를 생성자 매개변수로 선언하기만 하면, Angular의 DI가 인스턴스를 생성하고 공급합니다. new UserService()를 절대 작성하지 않습니다. (최신 Angular는 생성자 주입 대신 inject() 함수도 사용할 수 있습니다.)
import { inject } from "@angular/core";
export class UserListComponent {
private userService = inject(UserService); // 대체 주입 방식
}
✓ 단일 진실 공급원 — 컴포넌트 전반에 걸쳐 하나의 공유 UserService 인스턴스
✓ 느슨한 결합 — 컴포넌트는 생성이 아닌 타입에 의존
✓ 테스트 용이성 — 테스트에서 mock 서비스로 쉽게 교체
✓ 생명주기 관리 — Angular가 생성과 공유를 처리
컴포넌트가 자신의 의존성을 생성하지 않기 때문에, 테스트에서 가짜 서비스를 주입하거나, 구현을 한 곳에서 앱 전체적으로 변경할 수 있습니다.
서비스 + DI는 Angular 아키텍처의 핵심입니다. 컴포넌트는 UI에 집중하고 서비스는 로직과 상태를 보유하며, DI가 최소한의 결합으로 이들을 연결합니다.
이 분리(그리고 그로 인한 테스트 용이성)는 백엔드 프레임워크에서 차용한 Angular의 대표적인 강점 중 하나이며, @Injectable({ providedIn: 'root' })는 공유되는 주입 가능한 싱글턴을 위한 일상적인 패턴입니다.