닷넷은 앱의 재시동만으로 앱을 설정을 변경할 수 있는 Configuration API를 제공합니다.
기본적으로 환경 변수와, “appsettings.json” 파일을 인식하게끔 설정되어 있습니다.
시스템을 아래와 같이 관심사 별로 나눈다고 할 때,
- WebApp
- Infra.Database
각 관심사를 책임지는 팀 별로 업무 분장이 이뤄질 것입니다.
예를 들면, WebApp 은 웹팀, Infra.Database 는 데이터베이스 팀 처럼 말이죠.
위와 같이 업무 분장을 하지 않더라도, 각 모듈에 관한 설정은 각 모듈에 두는 게, 아무래도 관리 포인트가 명확하게 됩니다.
- WebApp
- appsettings.json- Infra.Database
- dbsettings.json
모든 설정 파일은 엔트리 포인트인 WebApp의 빌드 결과물의 루트 폴더에 저장됩니다.
\bin\release\.net8.0\appsettings.json
\bin\release\.net8.0\dbsettings.json
이 경우, 하위 모듈에 있는 설정이 소비되는 방식은 두 가지입니다.
내부 소비
하위 모듈에 관한 설정이기 때문에, 다른 모듈에 이 설정을 노출할 필요가 없는 게 일반적이라 할 수 있습니다.
이 경우, dbsettings.json 에서 설정을 가져 오는 건 예상 외로 쉽습니다.
using Microsoft.Extensions.Configuration; // 패키지 필요.
//...
var localConfiguraiton = new Configuration()
.AddJsonFile("dbsettings.json")
.Build();
var subSectionValue = localConfiguraiton.GetRequiredSection("{TopSection}:{SubSection}")?.Value;
// subSectionValue 소비 코드.
주의할 점은, new Configuration() 는 반드시 하위 모듈에서 실행되어야 그 모듈에서 파일을 찾습니다.
외부 공개
외부 공개하는 방법은 어딘가 값을 노출하여, 이 모듈을 참조하는 다른 모듈에서 읽어 가게 만들면 됩니다.
namespace Infra.Database
public static class DbSettings
{
public static string? Value { get; internal set; }
}
// 설정
var localConfiguraiton = new Configuration()
.AddJsonFile("dbsettings.json")
.Build();
DbSettings.Value = localConfiguraiton.GetRequiredSection("{TopSection}:{SubSection}")?.Value;
그런데, 이 방법의 문제는 DbSettings.Value 에 접근하는 시점에 따라, 설정값을 받을 수도 있고 받지 못할 수도 있다(설정하는 코드보다 접근하는 코드가 먼저 실행되는 경우)는 점입니다.
그렇다고 아래와 같이 처리하면, 파일에 접근 코드가 항상 실행되기 때문에, 성능 문제가 발생합니다.
(그러나, 적시성은 좋아 집니다.)
namespace Infra.Database
public static class DbSettings
{
private static IConfigurationRoot Config = new Configuration()
.AddJsonFile("dbsettings.json")
.Build();
public static string? Value => Config
.GetSection("{TopSection}:{SubSection}").Value
}
Dependency Injection
설정을 객체로 캡슐화해서, 컨테이터에 등록하면, 앱 실행 초기에 등록되기 때문에, 성능 문제나 접근 시차 문제를 해결할 수 있습니다.
캡슐화
namespace Infra.Database.Contracts;
public class TopSection
{
public string? SubSection { get; set; }
}
서비스 등록
상위 모듈의 DI에 설정 객체를 Singleton 으로 등록합니다.
using Microsoft.Extensions.Configuration; // 패키지 필요
using Microsoft.Extensions.DependencyInjection; // 패키지 필요
using Infra.Database.Contracts;
namespace Infra.Database;
public static class IServiceCollectonExtensions
{
public static IServiceCollection AddInfraDatabaseServices(this IServiceCollection services)
{
var localConfiguraiton = new Configuration()
.AddJsonFile("dbsettings.json")
.Build();
var topSection = new TopSection
{
SubSection = localConfiguraiton.GetRequiredSection("{TopSection}:SubSection}")?.Value,
}
service.AddSingleton(sp => topSection);
// 추가 등록
return services;
}
}
상위 모듈
// program.cs
using Infra.Database;
// ...
builder.Services.AddInfraDatabaseServices();
// ...
// minimal api
app.MapGet("/my-page", (TopSection dbSetttings) => {
// ...
});
@*MyPage.razor*@
@inject TopSection DbSettings
@*
....
*@