useClass와 useExisting의 비교

Adam Kim·2025년 10월 25일
0

angular

목록 보기
63/88

이번 시간에는 Angular의 의존성 주입(Dependency Injection)에서 providers를 설정할 때 useClass와 useExisting의 차이를 알아보겠습니다. 이 개념은 NgModule과 Standalone Components 모두에 동일하게 적용됩니다.

useClass

useClass는 특정 토큰(token)에 대해 주입할 클래스를 지정하는 역할을 합니다. 즉, A라는 토큰을 요청했을 때, 실제로는 B라는 클래스의 새로운 인스턴스(new instance)를 생성하여 제공합니다.
사용법은 다음과 같습니다. NgModule의 providers 배열이나 Standalone Component의 providers 배열에 설정할 수 있습니다.

// In a Standalone Component's providers array
@Component({
  ...
  standalone: true,
  providers: [
    { provide: ProductService, useClass: FakeProductService }
  ]
})

// In an NgModule's providers array
@NgModule({
  ...
  providers: [
    { provide: ProductService, useClass: FakeProductService }
  ]
})
<br/>

useExisting

useExisting는 기존에 등록된 토큰을 참조하여 별칭(alias)을 지정하는 역할을 합니다. A라는 토큰을 요청했을 때, 이미 다른 토큰(B)을 위해 생성된 기존 인스턴스(existing instance)를 그대로 반환합니다.
이 방식을 사용하려면 참조할 토큰(B)이 providers 배열에 이미 등록되어 있어야 합니다.

// In a Standalone Component's providers array
@Component({
  ...
  standalone: true,
  providers: [
     NewProductService,
     { provide: ProductService, useExisting: NewProductService },
  ]
})

// In an NgModule's providers array
@NgModule({
  ...
  providers: [
     NewProductService,
     { provide: ProductService, useExisting: NewProductService },
  ]
})

## Difference between useClass and useExisting 얼핏 보면 useClass와 useExisting은 유사해 보이지만, 인스턴스를 새로 만드는지 아니면 공유하는지에 대한 결정적인 차이가 있습니다. 이 두 가지의 차이를 예제를 통해 자세히 알아봅시다. Setting Up Services for the Example 두 가지 서비스가 있다고 가정합시다. 두 서비스는 형태는 유사하지만, add 메서드가 동작하는 방식이 달라 쉽게 구분할 수 있습니다.
// test.service.ts
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class TestService {
  index = 0;

  add(): number {
    return ++this.index;
  }
}
<br/>
code
TypeScript
// new-test.service.ts
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class NewTestService {
  index = 0;

  add(): number {
    this.index += 10;
    return this.index;
  }
}

이제 이 두 서비스를 주입받아 매초 호출하는 Standalone Component를 만들어 보겠습니다.

// app.component.ts
import { Component } from '@angular/core';
import { TestService } from './test.service';
import { NewTestService } from './new-test.service';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `<h1>useClass vs useExisting</h1>`,
  // providers 설정은 아래 시나리오에서 변경됩니다.
})
export class AppComponent {
   constructor(
      private testService: TestService,
      private newTestService: NewTestService
   ) {
     setInterval(() => {
        console.log('test     ->', this.testService.add());
        console.log('newTest  ->', this.newTestService.add());
        console.log('---');
     }, 1000);
   }
}

이제 AppComponent의 providers 설정을 바꿔가며 결과를 확인해 보겠습니다.

1. 기본 Provider 설정

가장 일반적인 방법으로, 각 서비스를 개별적으로 providers에 등록해 보겠습니다. providedIn: 'root'로 이미 전역에 등록되어 있으므로 providers 배열을 비워두어도 결과는 같습니다.

// app.component.ts
@Component({
  ...
  // providers 배열이 비어있으면 root 인젝터에서 가져옵니다.
  // 명시적으로 등록한다면 아래와 같습니다.
  providers: [TestService, NewTestService] 
})
export class AppComponent { ... }

결과:

예상대로 두 서비스는 각각의 인스턴스를 가지며 독립적으로 동작합니다.

// result
test     -> 1
newTest  -> 10
---
test     -> 2
newTest  -> 20
---
test     -> 3
newTest  -> 30
---

### 2. useClass 활용 useClass를 사용하여 NewTestService 토큰을 요청할 때 실제로는 TestService 클래스를 사용하도록 설정해 봅시다.
// app.component.ts
@Component({
  ...
  providers: [
    TestService,
    { provide: NewTestService, useClass: TestService },
  ]
})
export class AppComponent { ... }

testService를 주입받을 때: TestService의 인스턴스가 생성됩니다.
newTestService를 주입받을 때: NewTestService 토큰에 useClass: TestService가 지정되었으므로, TestService의 새로운 인스턴스가 생성됩니다.

결과:

newTestService가 TestService처럼 동작하지만, testService와는 서로 다른 인스턴스이므로 상태를 공유하지 않습니다.

// result
test     -> 1
newTest  -> 1
---
test     -> 2
newTest  -> 2
---
test     -> 3
newTest  -> 3
---

### 3. useExisting 활용 이번에는 동일한 조건에서 useClass만 useExisting으로 변경해 보겠습니다.
// app.component.ts
@Component({
  ...
  providers: [
    TestService,
    { provide: NewTestService, useExisting: TestService },
  ]
})
export class AppComponent { ... }

testService를 주입받을 때: TestService의 인스턴스가 생성됩니다.
newTestService를 주입받을 때: NewTestService 토큰에 useExisting: TestService가 지정되었으므로, TestService 토큰을 위해 이미 생성된 인스턴스를 그대로 재사용합니다.

결과:

newTestService와 testService가 완전히 동일한 하나의 인스턴스를 가리키므로, 상태(index 값)를 공유합니다.

test     -> 1
newTest  -> 2
---
test     -> 3
newTest  -> 4
---
test     -> 5
newTest  -> 6
---

결론

  1. useClass: "이 토큰을 요청하면, 저 클래스의 새 인스턴스를 만들어 줘."
    두 토큰이 같은 클래스를 사용하더라도 별개의 인스턴스가 생성됩니다.
  2. useExisting: "이 토큰을 요청하면, 저 토큰에 연결된 기존 인스턴스를 그대로 줘."

    두 토큰이 하나의 싱글턴(singleton) 인스턴스를 공유하게 됩니다.
    이 차이점을 이해하면 Angular의 의존성 주입 시스템을 더욱 유연하고 강력하게 활용할 수 있습니다.
profile
Angular2+ Developer

0개의 댓글