요즈음 닷넷 코어 기반으로 본격적인 애플리케이션/서비스 개발에 많은 시간을 들이다보니 "최선의 보일러 플레이트"를 찾는데 많은 시간을 투여하고 있습니다.
역사와 전통의 (?) Main 메서드는 여전히 제 기능을 수행하지만, 최근 닷넷 기반 애플리케이션에서 사실상 몰라서는 안될 표준으로 자리잡은 것이 바로 호스팅 API입니다. 그런데 호스팅 API를 그냥 쓰려니 Main 메서드를 쓰던 사상과는 격차가 너무 크게 느껴질때가 많습니다.
그래서 나름대로 찾은 새로운 호스팅 API 친화적인 진입점 만들기 전략으로 아래와 같은 샘플 코드를 사용해보기 시작했습니다.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Threading.Tasks;
using Microsoft.Data.Sqlite;
// 인메모리 데이터베이스를 계속 유지시키려면 따로 SQLite 연결을 열어둔 상태로 유지해야 합니다.
using var keepAlive = new SqliteConnection("Data Source=:memory:;");
await keepAlive.OpenAsync();
var builder = new HostApplicationBuilder();
builder.Services.AddDbContext<VmManagerDbContext>(options =>
{
options.UseSqlite(keepAlive);
});
builder.Services.AddHostedService<Application>();
var app = builder.Build();
await app.RunAsync();
public sealed class Application(
MyDbContext DbContext,
IHostApplicationLifetime HostAppLifetime
) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
await DbContext.Database.EnsureDeletedAsync(stoppingToken).ConfigureAwait(false);
await DbContext.Database.EnsureCreatedAsync(stoppingToken).ConfigureAwait(false);
}
finally
{
HostAppLifetime.StopApplication();
}
}
}
위의 코드를 만들기 위해서 사용한 NuGet 패키지는 2개 종류인데, Microsoft.EntityFrameworkCore.Sqlite
와 Microsoft.Extensions.Hosting
패키지입니다. 이외에도 무척 많은 패키지가 필요하지만, 전이적 패키지 (Transitive Package)라는 특성 덕에 연관된 패키지를 일일이 지정하지 않고 대표 패키지만 써줘도 됩니다. (간혹 보안 취약점 때문에 특정 패키지만 버전을 올려줘야하지만 이는 그런 상황이 있을 때에만 선택적으로 챙기면 됩니다.)
위의 코드에서의 포인트는, Main 메서드 대신 BackgroundService를 사용했다는 점이고, BackgroundService에 정의된 ExecuteAsync를 Main 메서드의 대용품으로 볼 수 있다는 점입니다. 물론, BackgroundService는 원하는만큼 더 추가할 수 있고, 이렇게 만들면 여러 엔트리포인트를 병렬로 실행하게 만드는 것도 가능할 것입니다. 그리고 생성자 주입 기능을 사용하도록, 여기서는 Primary Constructor를 사용했지만 원하는 스타일로 생성자 주입 코드를 만들어주시면 좋을 것입니다.
위와 같은 스타일로 애플리케이션 진입점을 설계할 경우 호스팅 API에 좀 더 밀착해서 사용할 수 있어서 관리성이 좋은 코드를 만드는데 도움이 되지 않을까 생각해봤습니다. ㅎㅎ
아울러, 본래 인메모리 DB는 EF Core가 제공하는 InMemory 버전의 프로바이더 패키지가 있지만, 애초에 테스트용이라고 목적을 명시하고 있을뿐 아니라 트랜잭션같은 기능들은 따로 DB 생성 시점에서 옵션까지 지정해줘야 하는 번거로움이 있는데 반해, SQLite에도 인메모리 DB 기능을 자체적으로 제공하고 있어, 위와 같이 설정하고 사용하면 EF Core에서 모델링한 데이터베이스 모델이 실제로 피지컬하게 어떻게 렌더링되는지 "로그"로 확인해볼 수 있어 유용한 접근법이 될 것 같아 소개해봅니다.
SQL 스키마 로그 생성 예시:
포럼 회원 여러분께서는 어떻게 보일러 플레이트를 만들어 사용하시는지 궁금합니다.!