C# 의 종속성 주입은 특히 더 복잡한 ASP.NET Core 애플리케이션에서 종속성을 구성할 때 생명의 은인입니다. IServiceCollection에 이미 익숙 하거나 이미 제공된 DI 제품에 최대한 근접하고 싶다면 C#의 Scrutor가 크게 향상되었습니다.
이 기사에서는 C#의 종속성 주입 및 Scrutor에 대한 높은 수준의 개요를 제공합니다. 그러나 거기서부터 Scrutor와 함께 사용할 수 있고 애플리케이션에 매우 도움이 될 수 있는 3가지 팁을 바로 살펴보겠습니다!
Scrutor는 종속성 주입을 향상시키는 C#의 강력한 NuGet 패키지입니다. 종속성 등록 및 해결을 단순화하여 코드를 더 쉽게 관리하고 구성할 수 있습니다.
종속성 주입은 느슨한 결합을 촉진하고 테스트 가능성과 유지 관리 가능성을 향상시키는 애플리케이션 구성에 널리 사용되는 접근 방식입니다. 클래스 내에서 종속성을 직접 생성하는 대신 클래스에 종속성을 주입하는 작업이 포함됩니다. 이는 종속성을 생성하는 책임이 이를 요구하는 클래스의 소유가 아니라 호출 스택 상위의 누군가가 소유한다는 것을 의미합니다. 결국 이로 인해 거의 모든 종속성 생성이 애플리케이션의 진입점으로 푸시되어 다루기가 어려워집니다. 그러나 종속성 주입 프레임워크는 이 모든 논리를 정리하고 구성하는 데 도움이 됩니다.
Scrutor는 종속성을 등록하고 해결하는 간단하고 직관적인 방법을 제공하여 이 개념을 한 단계 더 발전시킵니다. Scrutor를 사용하면 더 이상 각 종속성을 하나씩 수동으로 등록할 필요가 없습니다. 대신 규칙과 속성을 사용하여 프로세스를 자동화할 수 있습니다.
등록 및 해결을 위해 C#에서 Scrutor를 사용하는 방법을 설명하기 위해 간단한 시나리오를 고려해 보겠습니다. UserRepository 및 ProductRepostiory와 같은 다양한 데이터 저장소를 사용해야 하는 애플리케이션이 있다고 가정해 보겠습니다.
먼저 프로젝트에 Scrutor NuGet 패키지를 설치해야 합니다. 패키지 관리자 콘솔을 열고 다음 명령을 실행합니다.
Install-Package Scrutor
다음으로 리포지토리와 해당 인터페이스를 정의해야 합니다. 이 예는 본질적으로 비어 있지만 잠시 후 이에 대해 언급하겠습니다.
public interface IUserRepository { // Interface methods } public class UserRepository : IUserRepository { // Implementation } public interface IProductRepository { // Interface methods } public class ProductRepository : IProductRepository { // Implementation }
이제 Scrutor를 사용하여 종속성을 연결해 보겠습니다. 시작 코드(예: ASP.NET Core의 ConfigureServices 메서드)에 다음 코드를 추가합니다.
services.Scan(scan => scan .FromAssemblyOf<Startup>() .AddClasses(classes => classes.AssignableToAny( typeof(IUserRepository), typeof(IProductRepository))) .AsImplementedInterfaces() .WithScopedLifetime());
이 코드는 Scrutor의 Scan 메서드를 사용하여 Startup 클래스가 포함된 어셈블리를 검색합니다. 그런 다음 IUserRepository 또는 IProductRepository 인터페이스에 할당할 수 있는 클래스를 필터링합니다. 마지막으로 이러한 인터페이스의 구현을 매핑하고 해당 인터페이스에 등록합니다.
이제 이러한 종속성을 클래스에 주입할 수 있습니다. 예를 들어 IUserRepository가 필요한 UserService 클래스가 있다고 가정해 보겠습니다.
public class UserService { private readonly IUserRepository _userRepository; public UserService(IUserRepository userRepository) { _userRepository = userRepository; } // Rest of the class implementation }
생성자에서 IUserRepository를 종속성으로 선언함으로써 Scrutor는 IServiceCollection과 함께 UserRepository 인스턴스를 자동으로 해결하고 주입합니다.
서비스 인터페이스 IService
와 구현 MyService
있다고 가정해 보세요. 로깅 문제로 비즈니스 로직을 오염시키지 않고 MyService
에서 각 메서드 호출의 시작과 종료를 기록하려고 합니다.
먼저 IService
인터페이스와 그 구현을 정의합니다.
public interface IService { void DoWork(); } public class MyService : IService { public void DoWork() { // Business logic here } }
다음으로 IService
구현하는 데코레이터 클래스 LoggingDecorator
만듭니다. 이 클래스는 실제 서비스를 래핑하고 위임된 메서드 호출에 대한 로깅을 추가합니다.
public class LoggingDecorator : IService { private readonly IService _decorated; private readonly ILogger<LoggingDecorator> _logger; public LoggingDecorator(IService decorated, ILogger<LoggingDecorator> logger) { _decorated = decorated; _logger = logger; } public void DoWork() { _logger.LogInformation("Starting work."); _decorated.DoWork(); _logger.LogInformation("Finished work."); } }
이제 Scrutor를 사용하여 Startup.cs
또는 서비스를 구성하는 모든 위치에 서비스와 해당 데코레이터를 등록하세요.
public void ConfigureServices(IServiceCollection services) { // Register the base service services.AddScoped<IService, MyService>(); // Use Scrutor to apply the decorator services.Decorate<IService, LoggingDecorator>(); // Make sure to register the ILogger or ILogger<T> dependencies if not already done }
이 설정에서는 Scrutor를 사용하여 IService
등록을 LoggingDecorator
로 래핑합니다. IService
해석할 때 DI 컨테이너는 LoggingDecorator
인스턴스를 제공하고 이는 차례로 MyService
인스턴스를 래핑합니다. 이 접근 방식은 로깅 논리를 비즈니스 논리 외부에 유지하여 문제를 분리합니다.
복잡한 시스템을 구축할 때 기능 플래그와 같은 것을 도입하고 싶은 경우가 종종 있습니다. 이를 통해 구성에 따라 다양한 코드 경로를 사용할 수 있으며 전체 애플리케이션을 다시 컴파일하고 배포할 필요가 없습니다. 어떤 상황에서는 단지 다른 코드 경로를 선택하는 것이 아니라 전체 구현을 바꾸는 것이 중요합니다!
예제를 함께 시도해 봅시다! NewFeatureService
(새 실험적 기능) 및 StandardFeatureService
(현재 안정적인 기능) 등 여러 구현이 포함된 IFeatureService
인터페이스가 있다고 가정합니다. 구성 플래그를 기반으로 런타임에 이러한 구현 간에 전환하려고 합니다.
먼저 IFeatureService
인터페이스와 해당 구현을 정의합니다.
public interface IFeatureService { void ExecuteFeature(); } public class NewFeatureService : IFeatureService { public void ExecuteFeature() { // New feature logic } } public class StandardFeatureService : IFeatureService { public void ExecuteFeature() { // Standard feature logic } }
다음으로, 기능 토글 설정에 따라 사용할 구현을 결정하는 방법이 필요합니다. 이는 appsettings.json
, 데이터베이스 설정 또는 기타 구성 소스의 값일 수 있습니다.
이제 Scrutor를 사용하여 기능 토글을 기반으로 적절한 서비스 구현을 동적으로 등록하십시오.
public void ConfigureServices(IServiceCollection services) { // Assume GetFeatureToggleValue() retrieves the current feature toggle setting var useNewFeature = GetFeatureToggleValue("UseNewFeature"); // Dynamically register the appropriate implementation based on the feature toggle if (useNewFeature) { services.AddScoped<IFeatureService, NewFeatureService>(); } else { services.AddScoped<IFeatureService, StandardFeatureService>(); } // Optional: You could combine this with the previous example // to use the decorator pattern too! // services.Decorate<IFeatureService, FeatureServiceProxy>(); } private bool GetFeatureToggleValue(string featureName) { // This method would typically check your // configuration source to determine if the feature is enabled // For simplicity, this is just a placeholder return false; // or true based on actual configuration }
이와 같은 접근 방식을 취하면 다음과 같은 측면에서 도움이 될 수 있습니다.
이 기사에서는 C#에서 Scrutor를 사용한 종속성 주입에 대한 3가지 간단한 팁을 제공했습니다. 종속성 주입이 무엇인지, Scrutor가 어떻게 적용되는지에 대한 간략한 개요를 살펴본 후 바로 예제로 넘어갔습니다. 우리는 어셈블리 스캐닝, 종속성을 장식하는 방법, 전체 구현에 기능 플래그 지정을 고려하는 방법까지 살펴보았습니다.
종속성 주입은 애플리케이션이 복잡해짐에 따라 애플리케이션을 크게 단순화할 수 있는 기능이며 Microsoft의 기본 제공 IServiceCollection 제공을 고수하려는 경우 Scrutor는 큰 도움이 될 수 있습니다. 이 내용이 유용하고 더 많은 학습 기회를 찾고 있다면 무료 주간 소프트웨어 엔지니어링 뉴스레터를 구독 하고 YouTube에서 내 무료 동영상을 확인해 보세요! Discord에서 저와 다른 커뮤니티 회원들 과 함께하세요!
여기에도 게시되었습니다 .