닷넷의 Host 객체는 DI, 설정, 그리고, 앱의 생애 주기 관련 도구를 제공합니다.
일반적으로 "호스트"라고 하면 네트워크가 연상되지만, 닷넷에서는 네트워크를 결부하지 않은 개념으로 사용합니다. 네트워크 기능을 결부하여,
WebHost라는 파생 객체도 제공하고, 이를 기반한 웹앱으로WebApplication(Asp.Net Core 앱) 도 제공합니다.
닷넷에서 "Configuration"의 의미는 (앱의 재빌드/재시작 없이) 앱의 파라미터를 설정/변경하는 것을 가리킵니다. 참고로 닷넷 문서, VS 에서는 "구성"이라고 번역하는데, 이 글에서는 "설정"이라고 표현합니다.
닷넷 Host의 설정 시스템은, 설정 공급자( 대표적으로 JsonFileProvider - appsettings.json 의 컨텐츠를 설정값에 추가)가 제공한 값을 코드에 공급합니다.
공급하는 방식은 아래와 같습니다.
- raw 값을 그대로 (이후 코드에서 객체에 수동 바인딩할 수 있음)
ConfigurationBinder를 통해 바인딩된 객체
이 글은 두 번째 방식과 연관된, 제하의 인터페이스 패밀리에 관한 것입니다.
예제를 위해,
설정 소스
appsettins.json
{
"Upstreams" : {
"Backends" : [ "https://app1.mysite.com", "https://app2.mysite.com" ]
}
}
바인딩 모델
public sealed class Upstreams // 기본 생성자 있어야 함.
{
public Uri[] Backends { get; set; } = []; // get, set 모두 있어야 함.
}
설정 바인딩을 위한 모델은 아래의 요구 사항을 만족해야 합니다.
- 기본생성자 존재
- public read/write 속성
자동 바인딩 설정
// program.cs
builder.Configure<Upstreams>(builder.Configuration.GetSection("Upstreams"));
참고
Upstreams.Backends는Uri[]타입이고,appsettings.Upstreams.Backends의 형식은string[]타입니다. 설정 바이딩 시에,string이Uri에 자동 변환되는 편리 기능이 있습니다.
자동 바인딩 설정으로 인해, 우리 코드는 Upstreams 객체를 아래 세 가지 타입 중 하나로 선택해서 받을 수 있습니다.
- IOptions<Upstreams>
- IOptionsSnapshot<Upstreams>
- IOptionsMonitor<Upstreams>
IOptions<T>
Value 속성을 처음으로 읽을 때 값이 확정되고, 이 값은 앱의 종료까지 변하지 않습니다.
class ScopedService(IOptions<Upstreams> upstreamsOptions)
{
public void Propagate()
{
var uptreams = upstreamsOptions.Value; // 읽기
// ...
주석에 “읽기” 라고 표시된 코드가 처음으로 실행될 때(Lazy), IOptions가 생성되고, 이후 appsettigns.json 을 통해 값을 변경하더라도 재생성되지 않습니다.
이는 마치 싱글톤 서비스와 유사하고, 실제로도 주입 받는데 제한이 없습니다.
IOptionsSnapshot<T>
동일하게 Value 속성을 제공하지만, 객체의 수명주기는 Scope 종속적입니다.
즉, Scope 마다 생성되고, 생성될 때 값을 설정 값을 반영합니다.
스코프 마다 반드시 확인해야 하는 값이라면, 이 인터페이스를 사용해야 합니다.
Scoped 서비스와 마찬가지로, 스코프 컨텍스트 없는 서비스 결정(Service resolution) 코드는 런타임 에러라는 점에 주의해야 합니다.
// 런타임 에러
var upstreamsOptions = sp.GetRequiredService<IOptionsSnapshot<UpStreams>>();
반드시 스코프 내부에서 생성해야 합니다.
using var scope = serviceProvider.CreateScope();
var upstreamsOptions = scope.ServiceProvider
.GetRequiredService<IOptionsSnapshot<UpStreams>>();
var upstreams = upstreamsOptions.Value;
IOptionsMonitor<T>
이 인터페이스는 다른 형제들과 다르게 CurrnetValue 속성을 제공하는데, 이 속성의 값이 설정 값(appsettings.json)과 리얼타임으로 일치함을 보장합니다.
설정 파일의 변경 사항을 실시간으로 알고 싶을 때 이 객체를 사용하면 되는데, 그 예중에 하나가 예제로 쓰인 Upstreams 객체일 것입니다.
life-time 은 싱글턴으로 생성/주입 시 특별한 주의가 필요치 않습니다.
또한 OnChange 라는 이벤트를 제공하는데, 설정 값에 변경이 있을 때 발행되어 데이터 바인딩 등, 발행자-구독자 패턴의 코드에 유용합니다.
인터페이스 별 성능
Value 나 CurrentValue 를 읽을 때는 특별한 성능 오버헤드는 없다고 합니다.
다만, IOptionsMonitor<T>.OnChange 에 많은 구독자가 등록된 경우에는 클라이언트 코드 성능에 영향을 줄 수 있어, 무거운 이벤트 핸들러는 가급적 비동기 패턴으로 구현하는 것이 권고됩니다.
options.OnChanged += async (value, name) => { await Task.Run(() => { }, value); };