NestJS

NestJS Provider Injection scopes에 대해(singleton, request, transient)

iKay 2023. 7. 31. 00:28
반응형

현대 웹 개발에서 확장 가능하고 유지보수가 용이한 애플리케이션을 구축하는 것은 성공에 아주 중요합니다. NestJS는 강력하고 진보적인 Node.js 프레임워크로서, 체계적이고 효율적이며 확장 가능한 애플리케이션을 만들 수 있는 능력으로 큰 인기를 얻고 있습니다. NestJS 애플리케이션의 유지보수성과 유연성에 기여하는 핵심 기능 중 하나가 강력한 의존성 주입 시스템입니다.

이 의존성 주입 시스템의 핵심에는 "프로바이더(Provider)"라는 개념이 있습니다. 프로바이더는 다른 부분에서 주입될 수 있는 클래스 또는 값으로, 기능을 구성하고 공유하는 데 도움을 줍니다. 그러나 프로바이더를 정의하는 것만으로는 충분하지 않습니다. 또한 이러한 프로바이더들이 어떻게 애플리케이션 내에서 관리되고 공유되어야 하는지 고려해야 합니다. 이것이 "프로바이더 스코프(Provider Scope)" 개념이 등장하는 곳입니다.

이번 스트에서는 NestJS Provider scopes 중 singleton, request 그리고 transient에 대해 정리해 보고자 합니다. 이러한 스코프를 이해하는 것은 애플리케이션 구조를 결정하고 provider의 라이프사이클을 효과적으로 관리하는 데에 꼭 필요하다고 생각합니다.

 

 


 

Singleton

default 옵션으로 동작하기 때문에 가장 일반적으로 사용되는 scope입니다. singleton scope에서 Provider는 애플리케이션 전체에서 단 하나의 인스턴스만 생성되고 공유됩니다. 즉, 처음 bootstrap 됐을 때 인스턴스가 생성되며, 그 후로는 같은 인스턴스가 계속해서 사용됩니다.

방금 설명드린대로 싱글톤은 기본적으로 NestJS 프로바이더의 default 스코프입니다. 따라서 `@Injectable()` 데코레이터를 사용하여 Provider를 정의하면 scope가 명시되지 않은 경우 자동으로 singleton scope로 취급됩니다.

singleton은 애플리케이션에서 여러 곳에서 동일한 인스턴스를 사용해야 할 때 유용합니다. 예를 들어, 데이터베이스 연결, 로깅 서비스, 설정 정보, 캐싱 메커니즘 등과 같이 여러 부분에서 동일한 상태를 공유해야 하는 경우에 singleton scope를 사용합니다.

singleton scope를 사용하는 경우, 주의해야 할 점이 있습니다. Provider가 애플리케이션 전체에서 하나의 인스턴스를 공유하기 때문에, Provider에 상태를 저장하면 다른 부분들이 이 상태를 공유하게 됩니다. 따라서 싱글톤 프로바이더의 상태는 멱등성(idempotence)을 유지하고, 사이드 이펙트가 없도록 신경 써야 합니다.

Singleton은 애플리케이션의 공통적인 상태와 로직을 공유하는 데 유용하지만, 상태 변경에 대한 주의가 필요하다는 점을 잊지 말아야 합니다.

 

 

Request

매번 HTTP 요청과 관련된 단위 작업에서 프로바이더의 라이프사이클을 유지해야 하는 경우 사용됩니다. Request scope에서 프로바이더는 각각의 HTTP 요청마다 새로운 인스턴스가 생성되고, 해당 요청을 처리하는 동안에만 유지됩니다.

Request scope는 웹 서버 애플리케이션에서 각각의 클라이언트 요청을 독립적으로 처리해야 할 때 유용합니다. HTTP 요청은 보통 서로 다른 클라이언트들이 동시에 접속하고 요청하는 상황이 발생하게 되는데, Request scope는 각각의 요청 처리를 위해 별도의 인스턴스를 제공함으로써 각 Request 마다 고유한 데이터를 가지게 됩니다.

이러한 특성으로 인해, Request scope는 주로 요청마다 state를 유지해야 하는 경우에 사용됩니다. 예를 들어, 사용자의 로그인 정보, 요청마다 다른 인증 토큰, 요청마다 생성되는 request id 등이 이에 해당합니다.

Request scope 사용하기 위해서는 `@Injectable()` 데코레이터와 함께 `Scope.REQUEST` 옵션을 설정해야 합니다:

Request scope는 HTTP 요청마다 독립적인 상태를 유지해야 하는 경우에 매우 유용하지만, 동시에 인스턴스를 많이 생성하므로 성능에 영향을 미칠 수 있습니다. 따라서 신중하게 사용해야 하며, 필요한 경우에만 적절하게 활용하는 것이 좋습니다.

 

반응형

Transient

Transient scope는 injection 될 때마다 새로운 인스턴스가 생성되어야 하는 Provider에 사용됩니다. 이 Scope는 애플리케이션의 다른 부분들과 상태를 공유하지 않으려는 경우에 유용하며, 각 Injection 지점이 새롭고 독립적인 인스턴스를 받도록 보장할 수 있습니다.

Transient scope는 생소하기도 하고 특별한 경우인 것 같아 NestJS에서 Transient scope를 사용하는 일반적인 상황에 대해 위 둘의 내용보다는 조금 더 자세히 정리하고자 합니다.

 

1. 상태를 유지하는 서비스(Stateful Services)

특정 작업 또는 요청에 대해 상태를 유지하는 서비스가 있는 경우, Transient scope를 사용하여 각 요청이나 작업에 대해 독립적인 서비스 인스턴스를 생성합니다. 이렇게 함으로써 서로 다른 요청들 간에 상태가 공유되지 않고, 데이터 오염이나 예상치 못한 동작을 방지할 수 있습니다.

 

2. 요청별 데이터 저장(Request-Specific Data)

특정 HTTP 요청과 관련된 데이터를 저장해야 할 때(예: 요청별 데이터 캐싱 등), Transient scope를 사용할 수 있습니다. 이렇게 하면 각 요청마다 데이터를 격리하여 서로 간섭하지 않도록 보장할 수 있습니다.


3. 성능 최적화(Performance Optimization)

일부 경우에는 Transient scope를 사용하여 성능 최적화를 할 수 있습니다. 예를 들어, Provider를 생성하는 데 자원이 많이 소모되는 경우, 해당 프로바이더의 수명을 최소한으로 제한하여 작업이 완료된 후 인스턴스가 폐기되도록 할 수 있습니다.

 

4. 병렬 작업(Parallel Operations)

멀티스레드 또는 병렬 처리 환경에서는 Transient scope를 사용하여 공유 리소스에 대한 경합을 피할 수 있습니다. 각 스레드 또는 프로세스는 자체적인 프로바이더 인스턴스를 가져오므로 동기화와 잠재적인 병목 현상을 줄일 수 있습니다.

 

5. 외부 API 또는 연결(External APIs or Connections)

외부 API나 연결(예: 데이터베이스 연결, 웹소켓 등)과 같이 독립된 인스턴스가 필요한 경우, Transient scope를 사용하여 각 애플리케이션 부분이 독립적인 연결 또는 세션을 받도록 할 수 있습니다.



하지만 Transient scope를 사용할 때는 신중해야 합니다. 각 주입 지점마다 새로운 인스턴스를 생성하는 것은 메모리와 리소스를 더 소비할 수 있으므로, singleton이나 request scope와 비교해 메모리 사용에 주의해야 합니다. 애플리케이션의 기능과 요구사항에 따라 적절한 스코프를 선택하는 것이 중요합니다.

모든 서비스가 tansient로 정의되어야 하는 것은 아니며, 많은 서비스는 기능과 요구사항에 따라 singleton이나 request scope로 정의하는 것이 적절합니다. 각 provider에 적합한 scope를 선택하여 NestJS 애플리케이션의 올바른 동작과 성능을 보장하는 것이 중요합니다.

반응형

'NestJS' 카테고리의 다른 글

NestJS + TypeORM 트랜잭션(Transaction) 사용에 대한 고민  (0) 2023.07.23