센로그
9. Dependency Injection (DI) 본문
◆ Dependency Injection
종속성 주입. 웹 개발시 유용한 패턴이라 많은 웹 서버 플랫폼에 도입된 기능이다.
디자인 패턴에서, 우리는 코드간의 종속성을 줄이는 것을 중요하게 생각함 (loosely coupled)
그러면 종속성이란 무엇이냐?
우선 종속성과 관련된 예시를 보자.
public class FileLogSettings
{
string _filename;
public FileLogSettings(string filename)
{
_filename = filename;
}
}
public class FileLogger
{
FileLogSettings _settings;
public FileLogger(FileLogSettings settings)
{
_settings = settings;
}
public void Log(string log)
{
Console.Write($"Log OK {log}");
}
}
public class HomeController : Controller
{
public FileLogger _logger;
public HomeController()
{
_logger = new FileLogger(new FileLogSettings("log.txt"));
}
}
- FileLogger가 FileLogSettings를 사용하고, HomeController가 FileLogger을 사용하는 경우.
- 의존성이 강하다.
- FileLogger가 바뀌는 경우 HomeController에도 영향을 줄 수 있다.
- 만약 시스템이 바뀌어서 FileLogger가 아닌 DBLogger로 로깅하도록 바뀌는 경우, 코드를 많이 수정해야 함.
- 매번 새로운 인스턴스를 생성해서 사용해야 하는게 좀 걸린다.
.NET 에서는 이런 경우 종속성을 줄이기 위해, 종속성 주입(DI)를 제공한다.
종속성 주입이란?
- (위 예시처럼) 필요할 때마다 new로 객체 인스턴스를 만들어주는 것이 아니라,
객체 인스턴스들을 미리 어딘가에서 관리하다가 필요한 경우 꽂아주는 것.- 기본적으로 생성자에서 꽂아주는 게 정석.
참고) 일반적으로 생성자에 DI를 하는 게 국룰이지만 .. Action에 대해서 DI를 할 수도 있긴 하다.
- [FromServices] 이용하면 됨
가능은 한데 뭐.. 별로 안 씀
DI 동작 과정
Request가 Routing이 완료되면, DI가 동작한다.
DI 동작 과정은 다음과 같다.
- Controller Activator 작동
- DI Container에게 Controller 생성 및 알맞는 Dependency 연결 위탁
- 만약 요청한 Dependency를 못 찾으면 Error
- DI Container에게 Controller 생성 및 알맞는 Dependency 연결 위탁
- DI Container 임무 실시
- Controller 생성됨
DI를 통해 서비스 추가
DI는 Program.cs에서 builder.Services.AddControllersWithViews(); 밑에다 추가해주면 된다.
이때 다음 세 가지를 지정하여 등록해주면 된다.
- Service Type (인터페이스 or 클래스)
- Implementation Type (클래스)
- LifeTime (Transient, Scoped, Singleton)
- AddTransient
- 항상 새로운 서비스 instance를 만든다
- AddScoped
- 한 요청 안에서는 동일한 서비스 instance를 사용한다
- 가장 일반적으로 많이 사용됨.
- DbContext, Authentication 등에 사용.
- AddSingleton
- 항상 동일한 서비스 instance를 사용한다
- 웹에서의 싱글톤은 항상 thread-safe 해야함!! 주의할 것.
- AddTransient
위의 예시 코드를 DI 패턴을 활용해 수정한 코드를 보자.
public interface IBaseLogger
{
public void Log(string log);
}
public class FileLogSettings
{
string _filename;
public FileLogSettings(string filename)
{
_filename = filename;
}
}
public class FileLogger : IBaseLogger
{
FileLogSettings _settings;
public FileLogger(FileLogSettings settings)
{
_settings = settings;
}
public void Log(string log)
{
Console.WriteLine($"Log OK {log}");
}
}
public class HomeController : Controller
{
public IBaseLogger _logger;
public HomeController(IBaseLogger logger)
{
_logger = logger;
}
}
만약 위 코드에서 HomeController의 _logger에다 FileLogger을 넣고싶다고 하자.
Program.cs에 다음 DI 코드를 추가해주기만 하면 된다,
// 각종 서비스 추가 (DI)
builder.Services.AddControllersWithViews();
//IBaseLogger을 요청한 경우 FileLogger로 연결해주도록 함
builder.Services.AddSingleton<IBaseLogger, FileLogger>();
//FileLogSettings는 일괄적으로 "log.txt"를 파라미터로 생성한 이 객체를 쓰기로 함
builder.Services.AddSingleton(new FileLogSettings("log.txt"));
- 매번 new 코드를 작성하는 것이 아니라, DI를 통해 각각 어떤 클래스와 연결할지 지정해줄 수 있다.
- FileLogger가 DBLogger로 바뀌는 경우, DI 시점에서 <IBaseLogger, DBLogger>로 교체해주기만 하면 된다.
- AddSingleton방식인 건 그냥 예제라 쓴 것임. 경우에 맞는 Lifetime을 적용하면 된다.
DI를 통해 다수의 서비스 추가
원한다면, 동일한 인터페이스에 대해 다수의 서비스를 등록할 수도 있다.
IEnumerable 형식을 사용하면 된다.
- FileLogger도 찍고 싶고, DBLogger도 찍고싶은 경우를 의미한다.
- 컨트롤러에서 IBaseLogger 형태로 로거를 받아오던 걸 IEnumerable<IBaseLogger> 이런 식으로 바꿔주면 됨.
- 물론 로깅 구현할 때도 foreach 써서 로깅해줘야 함
- DI 시에는, 다음과 같이 그냥 여러 개 연결해주면 됨
builder.Services.AddControllersWithViews(); builder.Services.AddSingleton<IBaseLogger, FileLogger>(); builder.Services.AddSingleton<IBaseLogger, DBLogger>();
- 만약 기존처럼 IEnumerable 형태가 아닌 단일 변수인 경우에는, 나중에 연결한 부분(DBLogger)이 적용됨.
Razor View Template (.cshtml)에서도 서비스가 필요하다면?
여기서는 생성자라는 개념이 따로 없으므로, 다른 걸 사용한다.
- @inject
DI 사용시 주의점 - 생명 주기
주의해야 할 점은, 당연한 얘기지만, 어떤 서비스에서 DI 부품을 사용한다면
부품들의 생명 주기는 최소한 서비스의 생명 주기보다는 길거나 같아야 한다.
다음과 같이 쓰면 안된다는 얘기다.
builder.Services.AddControllersWithViews();
builder.Services.AddSingleton<IBaseLogger, FileLogger>();
builder.Services.AddTransient(new FileLogSettings("log.txt"));
- FileLogger 내부에서 FileLogSettings를 사용하는 구조일 때,
위와 같이 FileLogSettings의 생명 주기가 FileLogger보다 짧으면 안된다.
'게임 > ASP.NET Core' 카테고리의 다른 글
11. Filter (0) | 2024.04.15 |
---|---|
10. Configuration (0) | 2024.04.15 |
8. WebAPI (+Routing Attributes) (0) | 2024.04.15 |
7. TagHelper (0) | 2024.04.15 |
6. View (0) | 2024.04.15 |