[ASP.NET Core] 의존성 주입(Dependency Injection, DI)

Arthur·2024년 3월 31일
0
post-thumbnail

의존성 주입이란


소프트웨어 엔지니어링에서 하나의 객체가 다른 객체의 의존성을 제공하는 테크닉이다.
<위키백과 - 의존성 주입>


위키백과에는 위와 같이 정의되어 있습니다.
객체라는 키워드는 C#, Java와 같은 프로그래밍 언어를 접해봤으면 들어봤을 객체(Object)를 의미합니다.

저는 의존성 주입을 처음 접했을 때
'의존성, 주입' 두 단어를 이해하는데 꽤 어려움이 있었습니다.

간단한 예시를 들면 위와 같이 클래스A가 클래스B의 메소드(함수)를 사용(호출)하는 것입니다.
클래스A는 클래스B의 메소드를 호출하기 위해 클래스B의 객체를 가지고 있어야 합니다.

위와 같은 상황이 발생하면 클래스A는 클래스B에 의존하게 됩니다.

이렇게 되면 클래스A와 클래스B의 결합도가 높아지게 됩니다.
클래스A가 호출하는 클래스B의 메소드가 변경이 되면 클래스A에도 변경에 대한 영향을 받게 됩니다.

이런 문제를 해결하기 위해 사용되는 패턴이 의존성 주입입니다.
의존성 주입은 다양한 프레임워크에서 기능을 지원해줍니다.

이 글에서는 C# ASP.NET Core를 가지고 예시를 들고 적용 방법에 대해서 작성해봤습니다.


의존성 주입의 장점

  • 응용 프로그램의 클래스 간 결합도를 낮춘다.
  • 테스트가 용이하다.
  • 필요한 서비스와 구성 요소를 동적으로 주입하여 유연하고 확장 가능한 코드 구조를 구현할 수 있다.


ASP.NET Core에서의 DI

ASP.NET Core는 클래스와 해당 종속성 간의 IoC(Inversion of Control)를 실현하는 기술인 DI 소프트웨어 디자인 패턴을 지원합니다.

.NET에서는 특정 서비스 클래스와 특정 컨트롤러 클래스 간에 의존 관계를 Program.cs(또는 Startup.cs)파일에서 설정 할수 있습니다.


private static void ConfigureServices(IServiceCollection services, string connectionString)
{
	services.AddTransient<QueryFactory>((e) =>
	{
		var connection = new MySqlConnection(connectionString);
		var compiler = new MySqlCompiler();
		return new QueryFactory(connection, compiler);
	});

	services.AddSwaggerGen();

	services.AddScoped<IAccountService, AccountService>();
	services.AddScoped<IAccountRepository, AccountRepository>();
	services.AddScoped<IOauthService, OauthService>();
	services.AddScoped<IOauthRepository, OauthRepository>();
	services.AddSingleton<PasswordEncryptor>();

	services.AddControllersWithViews();

	services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
}

위 코드는 제가 진행 중인 토이 프로젝트의 코드입니다.
Program.cs에서 Serivce 레이어와 Repository 레이어의 의존성과 결합도를 낮추기 위해
ASP .NET Core에서 제공하는 의존성 주입을 사용했습니다.

public class AccountService : IAccountService
{
	private readonly IAccountRepository _accountRepository;
	private readonly PasswordEncryptor _passwordEncryptor;
	private readonly IMapper _mapper;

	public AccountService(PasswordEncryptor passwordEncryptor, IAccountRepository accountRepository, IMapper mapper)
	{
		_passwordEncryptor = passwordEncryptor;
		_accountRepository = accountRepository;
		_mapper = mapper;
	}
	
	// ....
}

위와 같이 생성자 주입을 사용해서 사용할 수 있습니다.
생성자를 통해 DI 컨테이너에서 관리하는 객체들을 주입 받아서 사용합니다.

여기서 의존성 주입을 받을 객체는 꼭 readonly일 필요는 없습니다.
저는 의존성 주입으로 받아온 객체가 변경되지 않도록 readonly를 붙여서 사용합니다.



IoC / DI 컨테이너


.NET에 내장되어 있는 종속성 주입 관련 엔진을 DI 컨테이너라고 부릅니다. DI 컨테이너는, 미리 등록된 여러 종속성들에 요청할 때 해당 종속성에 대한 인스턴스를 생성해 줍니다.
<dotnetkorea.com - ASP.NET Core 종속성 주입(의존성 주입)>

.NET에서 의존성(종속성) 주입된 객체를 관리해주는 컨테이너라고 보면 될 것 같습니다.
개발자는 의존성 주입을 한 객체의 생명 주기를 관리하지 않아 좀 더 편하게 개발이 가능합니다.


IoC(Inversion of Control)란?

DI 컨테이너에게 객체의 제어를 맡기고 있습니다.

이렇게 프로그래머가 작성한 프로그램이 재사용 라이브러리의 흐름 제어를 받게 되는 소프트웨어 디자인 패턴을 줄여서 IoC(Inversion of Control)라고 부릅니다.



DI 컨테이너 세 가지 모드


DI 컨테이너에는 세 가지 모드가 있습니다.
구현하는 프로그램에서 각 용도와 의도에 맞게 모드를 사용해야 합니다.

Singleton 모드

Singleton 모드에서는 애플리케이션 전체에서 단일 인스턴스를 공유합니다.
이 모드를 사용하면 모든 요청이 동일한 인스턴스를 공유하므로 상태가 유지됩니다.
애플리케이션 전역에 상태를 공유해야 하는 서비스나 인스턴스에 사용됩니다.

유틸리티와 같은 역할을 하는 클래스는 싱글톤을 사용해 전역적으로 하나의 객체만 있어도 됩니다.
패스워드 암호화와 같은 유틸리티가 그 예입니다.
패스워드 암호화 유틸리티는 클라이언트로 부터 요청이 올 때마다 객체가 생성되지 않아도 됩니다.


Transient 모드

Transient 모드에서는 매번 요청할 때마다 새로운 인스턴스를 생성하여 제공합니다.
이 모드를 사용하면 매 요청마다 새로운 인스턴스를 얻을 수 있으며, 인스턴스 간의 상태가 공유되지 않습니다.
일반적으로 상태를 가지지 않는 서비스나 인스턴스가 요청마다 변경될 수 있는 경우에 사용됩니다.


Scoped 모드

Scoped 모드에서는 요청마다 새로운 인스턴스를 생성하지 않고, 같은 스코프(예: HTTP 요청) 내에서는 동일한 인스턴스를 공유합니다.
이 모드를 사용하면 스코프 내에서는 같은 인스턴스를 공유할 수 있으며, 스코프를 벗어나면 새로운 인스턴스가 생성됩니다.
주로 웹 애플리케이션에서 HTTP 요청마다 인스턴스를 공유해야 할 때 사용됩니다.

Transient, Scoped 모드는 인스턴스의 상태를 어떻게 관리할 것인가에 따라서 달라집니다.

Transient는 주로 서비스나 인스턴스가 요청마다 변경될 수 있는 경우에 사용됩니다.
예를 들면 요청 마다 랜덤한 숫자를 클라이언트에게 Response해야 한다면 Transient가 적합합니다.


Scoped 모드는 주로 웹 애플리케이션에서 HTTP 요청마다 인스턴스를 공유해야 할 때 사용됩니다.
예를 들면 유저가 로그인하여 세션이 시작될 때 해당 사용자의 정보를 가져와야 할 때가 있습니다.
이러한 경우에 Scoped 모드를 사용하여 사용자 세션 정보를 관리하는 서비스를 구현할 수 있습니다.



참고 자료


  • dotnetkorea - ASP.NET Core 종속성 주입(의존성 주입) => 링크
  • MS Docs - ASP.NET Core에서 종속성 주입 => 링크
  • ASP.NET Core Basics: Understanding Dependency Injection => 링크
  • 유튜브 IamTimCorey - Dependency Injection in .NET Core (.NET 6) => 링크
  • 위키백과 - 의존성 주입 => 링크
  • 위키백과 - 제어 반전 => 링크
profile
기술에 대한 고민과 배운 것을 회고하는 게임 서버 개발자의 블로그입니다.

0개의 댓글