.NET의 Generic Host란 무엇인가 - DI, 설정, 로그, BackgroundService를 먼저 정리
· 小村 豪 · C#, .NET, Generic Host, Worker, 설계
.NET에서 콘솔 앱이나 worker를 쓰기 시작하면, 처음에는 Main에 약간의 처리를 쓰는 것만으로 해결됩니다.
다만, 약간 자라면 대체로 이 부근이 늘어납니다.
appsettings.json을 읽고 싶다- 환경 변수로 덮어쓰고 싶다
ILogger로 로그를 내고 싶다- 서비스의 생성을
new투성이로 하고 싶지 않다 - 백그라운드에서 루프를 돌리고 싶다
Ctrl+C나 서비스 정지에서 깔끔하게 끝내고 싶다
여기서 나오는 것이 Generic Host입니다.
다만, 이 이름도 약간 섞이기 쉽습니다.
Host.CreateApplicationBuilder와Host.CreateDefaultBuilder는 무엇이 다른가IHost는 DI 컨테이너와 같은 것인가BackgroundService와 무엇이 연결되어 있는가- ASP.NET Core의
WebApplicationBuilder와 별개인가 - 콘솔 앱에서도 사용할 가치가 있는가
이 부근이 섞이면 Generic Host가 「Web 앱 전용 같은 것」으로 보이거나, 반대로 「뭐든 host로 해야 할 것」으로 보이기도 합니다. 어느 쪽도 약간 거칩니다.
이 글에서는 주로 .NET 6 이후의 현재의 실무감을 전제로, 다음 4가지를 먼저 정리합니다.
- Generic Host의 정체
- 무엇을 한꺼번에 돌봐주는가
Host.CreateApplicationBuilder/Host.CreateDefaultBuilder/WebApplication.CreateBuilder의 관계- 어디서부터 들어가면 온화한가
1. 먼저 결론(한마디로)
- Generic Host는 .NET 앱의 기동과 유효 기간을 한꺼번에 다루는 토대입니다.
- 그 속에 DI, 설정, 로그,
IHostedService/BackgroundService, 앱의 정지 처리가 들어갑니다. - 새로운 비 Web 앱에서는 먼저
Host.CreateApplicationBuilder(args)부터 들어가는 것이 자연스럽습니다. - ASP.NET Core의
WebApplicationBuilder도 별세계가 아니라, 같은 host의 사고방식을 Web용으로 넓힌 창구입니다. - 즉 Generic Host는 DI 컨테이너 단체의 이야기가 아니라, 앱의 조립 지점과 수명 관리를 한꺼번에 하는 구조입니다.
요컨대, 앱이 「인수를 읽어 1회 표시하고 끝」을 약간 넘긴 부근부터 Generic Host는 꽤 효과적입니다. 반대로, 거기까지 자라지 않은 작은 도구에까지 매번 반드시 가지고 들어올 것은 아닙니다.
2. 먼저 보는 정리표
2.1. Generic Host가 안고 있는 것
처음에 이 상자의 내용물을 나누어 두면 꽤 편합니다.
| 요소 | Generic Host가 돌봐주는 것 | 무엇이 기쁜가 |
|---|---|---|
| DI | IServiceCollection에서 서비스를 조립한다 |
new의 연쇄를 줄이기 쉽다 |
| Configuration | appsettings.json, 환경 변수, 커맨드라인 인수 등을 모은다 |
환경별의 차분을 다루기 쉽다 |
| Logging | ILogger<T>를 사용하기 위한 기반을 만든다 |
로그 출력처를 나중에 교체하기 쉽다 |
| Hosted service | IHostedService / BackgroundService의 기동과 정지를 다룬다 |
상주 처리를 앱 본체와 나누기 쉽다 |
| Lifetime | IHostApplicationLifetime, IHostEnvironment 등을 통해 개시・정지를 다룬다 |
Ctrl+C, SIGTERM, 서비스 정지로 끝나는 방식을 맞추기 쉽다 |
여기서 중요한 것은, Generic Host가 「편리한 DI 래퍼 1개」가 아니라는 것입니다. 실제로는 앱의 입구 주변을 한꺼번에 배선하는 상자, 정도의 보는 법이 가장 빗나가기 어렵습니다.
2.2. builder의 차이
여기도 처음에 1장으로 보는 편이 빠릅니다.
| 입구 | 주된 용도 | 쓰는 맛 | 먼저의 선택 |
|---|---|---|---|
Host.CreateApplicationBuilder(args) |
콘솔 / worker 등의 신규 비 Web 앱 | builder.Services / builder.Configuration / builder.Logging에 직접 쓴다 |
신규라면 이것 |
Host.CreateDefaultBuilder(args) |
기존 코드나 오래된 확장 메서드 주체의 구성 | ConfigureServices 등을 체인한다 |
기존 자산이 있다면 이것 |
WebApplication.CreateBuilder(args) |
ASP.NET Core Web 앱 / API | Generic Host에 Web용의 사정을 더한 입구 | Web이라면 이것 |
CreateApplicationBuilder와 CreateDefaultBuilder는,
한쪽이 신기능이고 한쪽이 별물, 이라는 이야기가 아닙니다.
어느 쪽도 같은 코어 기능과 기본 동작을 가지고 있습니다. 다른 것은 주로 쓰는 방식의 유파입니다.
새로운 비 Web 앱이라면, 지금은 Host.CreateApplicationBuilder(args)부터 들어가는 것이 자연스럽습니다.
WebApplication.CreateBuilder(args)는 그 흐름을 Web용으로 넓힌 입구라고 생각해 두면 정리하기 쉽습니다.
3. Generic Host의 전체상(그림)
전체상을 대략 그림으로 하면 이렇습니다.
flowchart LR
Args["args / 환경 변수 / appsettings.json"] --> Builder["Host.CreateApplicationBuilder(args)"]
Builder --> Config["builder.Configuration"]
Builder --> Services["builder.Services"]
Builder --> Logging["builder.Logging"]
Services --> Hosted["IHostedService / BackgroundService"]
Builder --> Build["builder.Build()"]
Build --> Host["IHost"]
Host --> Run["Run / RunAsync"]
Run --> Lifetime["개시・정지・Ctrl+C・SIGTERM"]
Lifetime --> Hosted
보통은 Program.cs에서 builder를 만들고,
builder.Services에 서비스를 추가하고,
builder.Configuration이나 builder.Logging을 필요에 따라 조정하고,
마지막에 Build()해서 IHost를 얻어 Run() / RunAsync()로 달리게 합니다.
수수하게 큰 것은 Host.CreateApplicationBuilder(args) 시점에서 꽤 많은 것이 이미 실려 있다는 것입니다.
기본값으로는 예를 들어 다음과 같은 것이 들어갑니다.
- 콘텐츠 루트는 현재의 디렉터리
- 호스트 구성은
DOTNET_접두사 붙은 환경 변수와 커맨드라인 인수 - 앱 구성은
appsettings.json,appsettings.{Environment}.json, Development의 user secrets, 환경 변수, 커맨드라인 인수 - 로그는 Console / Debug / EventSource / EventLog(Windows만)
Development환경에서는 scope 검증과 의존 관계 검증
즉, 아무 생각 없이 0부터 배선하고 있는 것이 아니라, 처음부터 「평범하게 사용하는 범위에서는 꽤 충분한 토대」가 놓여 있습니다.
4. Generic Host에서 무엇이 기쁜가
4.1. 기동 처리를 1곳으로 모을 수 있다
Generic Host의 가장 수수하고 큰 효과는 앱의 입구가 흩어지기 어려워지는 것입니다.
앱이 약간 자라면 Main 주변에서 다음과 같은 것이 늘어납니다.
- 설정 파일의 읽기
- 환경별의 교체
- 로거의 초기화
HttpClient나 repository나 service의 조립- 백그라운드 처리의 기동
- 종료 시그널 시의 뒷정리
이것을 host 없이 전부 손으로 이어가면, 처음에는 가볍더라도 나중에 점점 입구가 끈적거려집니다.
Generic Host를 사용하면, Program.cs가 「의존 관계를 한꺼번에 조립하는 장소」로서 분명해집니다.
이 정리만으로도 코드 리뷰의 쉬움이 꽤 바뀝니다.
4.2. DI / 설정 / 로그가 처음부터 연결된다
Generic Host를 사용하면 DI, 설정, 로그가 처음부터 같은 토대에 실립니다.
예를 들어 클래스 측에서는 이런 것을 평범하게 받을 수 있습니다.
ILogger<T>IConfigurationIHostEnvironmentIOptions<T>
여기서 효과적인 것은 설정의 읽는 법과 서비스의 만드는 법이 따로따로의 유파가 되기 어려운 것입니다.
설정이 1개나 2개라면, IConfiguration["Section:Key"]를 직접 읽는 것만으로도 동작합니다.
다만, 실무에서 설정이 늘어나면 IOptions<T>로 section별로 클래스에 묶는 편이 온화합니다.
마찬가지로 로그도 ILoggerFactory를 여기저기서 손으로 만들기보다,
필요한 클래스에 ILogger<T>를 주입하는 편이 전망이 좋아집니다.
Generic Host가 편리한 것은, 이것들을 따로따로의 이야기로 하지 않고, 앱 전체의 토대로서 함께 다룰 수 있는 곳입니다.
4.3. 정상 종료와 상주 운전을 다루기 쉽다
Generic Host는 「어떻게 기동할까」뿐만 아니라 「어떻게 멈출까」도 돌봐줍니다.
host가 기동하면 등록된 각 IHostedService의 StartAsync가 불립니다.
worker 서비스에서는 BackgroundService를 포함하는 hosted service의 ExecuteAsync가 달립니다.
여기서 말하는 「정상 종료」는 갑자기 처리를 끊는 것이 아니라,
- 정지의 신호를 흘린다
- 루프나 대기를 빠져나온다
- 접속이나 리소스를 정리한다
는 순서를 밟아 끝나는 것입니다.
장시간 동작하는 앱에서는 여기가 꽤 중요합니다.
Ctrl+C, SIGTERM, 서비스 정지와 같은 이벤트로 앱 전체의 멈추는 방식을 맞추기 쉬워집니다.
또, 종료를 앱 측에서 요구하고 싶을 때는 IHostApplicationLifetime.StopApplication()을 사용할 수 있습니다.
「이제 일은 끝났으니 깔끔하게 떨어졌으면 좋겠다」라는 신호를 host의 문맥으로 낼 수 있습니다.
5. 최소 구성
5.1. 콘솔 앱에서 사용하는 최소 예
먼저 중요한 것은, Generic Host를 사용한다고 해서 반드시 BackgroundService를 만들 필요는 없다는 것입니다.
1회만 달리는 콘솔 도구에서도 DI, 설정, 로그를 원한다면 Generic Host는 충분히 사용할 수 있습니다.
보통의 console 프로젝트에 나중에 실을 거라면, 우선 Microsoft.Extensions.Hosting을 참조합니다.
dotnet add package Microsoft.Extensions.Hosting
Program.cs의 최소 예는 예를 들어 이렇습니다.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<JobRunner>();
using IHost host = builder.Build();
try
{
JobRunner runner = host.Services.GetRequiredService<JobRunner>();
await runner.RunAsync();
return 0;
}
catch (Exception ex)
{
ILogger logger = host.Services
.GetRequiredService<ILoggerFactory>()
.CreateLogger("Program");
logger.LogError(ex, "Unhandled exception occurred during job execution.");
return 1;
}
internal sealed class JobRunner(
ILogger<JobRunner> logger,
IConfiguration configuration,
IHostEnvironment hostEnvironment)
{
public Task RunAsync()
{
string message = configuration["Sample:Message"] ?? "(no message)";
logger.LogInformation("Environment: {EnvironmentName}", hostEnvironment.EnvironmentName);
logger.LogInformation("Message: {Message}", message);
return Task.CompletedTask;
}
}
장시간 상주하지 않는다면, RunAsync()까지 가지 않아도 좋습니다.
Build()해서 필요한 서비스를 해결하고, 일을 마치면 그대로 종료한다.
그래도 Generic Host의 맛은 충분히 사용할 수 있습니다.
여기는 의외로 중요합니다. 단명 작업에까지 매번 Worker 템플릿을 가지고 들어올 필요는 없습니다.
5.2. appsettings.json
위 예라면 설정 파일은 이런 최소 형태로 충분합니다.
{
"Sample": {
"Message": "hello from Generic Host"
}
}
이 예에서는 생으로 configuration["Sample:Message"]를 읽고 있습니다.
값을 1개나 2개 볼 뿐이라면 이것으로 충분합니다.
다만, 실무에서 설정이 늘어나면,
- section별로 클래스에 나눈다
IOptions<T>로 주입한다- 기동 시에 검증한다
는 형태로 기울이는 편이 키 문자열의 흩어짐을 피하기 쉽습니다.
또, Generic Host의 기본값으로는 appsettings.json뿐만 아니라,
appsettings.{Environment}.json, 환경 변수, 커맨드라인 인수도 연결되므로,
「개발 시에만 교체한다」 「본번에서는 환경 변수로 덮어쓴다」가 꽤 자연스럽게 됩니다.
5.3. BackgroundService를 싣는다
장시간 동작하는 처리라면, BackgroundService를 사용하면 꽤 자연스럽습니다.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddScoped<PollingJob>();
builder.Services.AddHostedService<PollingWorker>();
using IHost host = builder.Build();
await host.RunAsync();
internal sealed class PollingWorker(
IServiceScopeFactory scopeFactory,
ILogger<PollingWorker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using PeriodicTimer timer = new(TimeSpan.FromSeconds(30));
while (await timer.WaitForNextTickAsync(stoppingToken))
{
using IServiceScope scope = scopeFactory.CreateScope();
PollingJob job = scope.ServiceProvider.GetRequiredService<PollingJob>();
await job.RunAsync(stoppingToken);
logger.LogInformation("Polling completed.");
}
}
}
internal sealed class PollingJob(ILogger<PollingJob> logger)
{
public Task RunAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Do work here.");
return Task.CompletedTask;
}
}
이 예에서 봐두고 싶은 것은 2점입니다.
BackgroundService의 본체는ExecuteAsync- scoped한 의존 관계를 원한다면,
IServiceScopeFactory로 scope를 만든다
BackgroundService 자체에는 기본의 scope가 없습니다.
예를 들어 DbContext와 같은 scoped 서비스를 사용하고 싶다면,
위와 같이 job 측을 scope 속에서 해결하는 형태가 안전합니다.
덧붙여, 정기 실행의 도구 그 자체의 고르는 법은 다른 테마이지만,
async 베이스로 쓴다면 PeriodicTimer는 꽤 온화합니다.
이 부근은 관련 기사의 타이머 기사와도 연결됩니다.
6. 전형 패턴
6.1. 단명한 콘솔 도구
배치, 변환 도구, 메인터넌스 커맨드와 같이, 1회만 일을 하고 끝나는 앱에서도 Generic Host는 평범하게 사용할 수 있습니다.
어울리는 것은 예를 들어 다음과 같은 장면입니다.
- 설정 파일을 읽고 싶다
- 로그를 내고 싶다
HttpClient나 repository를 주입하고 싶다- 종료 코드를 반환하고 싶다
이 종류의 앱에서 갑자기 BackgroundService와 RunAsync()를 가지고 들어오면,
약간 무거운 데다가 호스트의 수명 관리를 과잉으로 사용하게 됩니다.
단명 작업이라면, 앞의 최소 예처럼 JobRunner를 해결해서 실행하는 것만으로 충분합니다.
6.2. worker / 백그라운드 서비스
상주 worker, 폴링, 큐 소비, 감시, 정기 실행과 같은 처리에서는,
Generic Host와 BackgroundService의 조합이 꽤 자연스럽습니다.
특히 기쁜 것은 다음과 같은 점입니다.
- 기동과 정지의 흐름이 host 측에서 맞춰진다
- 로그, 설정, DI가 처음부터 사용할 수 있다
Ctrl+C나 정지 시그널로 취소를 흘리기 쉽다- 상주 처리의 본체를
Program.cs에서 분리하기 쉽다
게다가 Windows Service나 컨테이너의 문맥과도 연결하기 쉽습니다. 상주 앱으로서 기르려면 Generic Host는 꽤 자연스러운 토대입니다.
Windows Service화하는 장면에서는 현재의 디렉터리 전제로 파일을 찾기보다
IHostEnvironment.ContentRootPath를 기점으로 생각하는 편이 사고가 적습니다.
host의 문맥으로 「앱의 기준 패스」가 정해지기 때문입니다.
6.3. ASP.NET Core의 아래에도 있다
Web 앱 / API에서는 WebApplication.CreateBuilder(args)를 사용하므로,
얼핏 보면 Generic Host와 별세계로 보일지도 모릅니다.
하지만 감각으로는 꽤 이어져 있습니다.
builder.Servicesbuilder.Configurationbuilder.Logging
의 쓰는 맛이 닮아 있는 것은 그 때문입니다.
ASP.NET Core에서는 HTTP 서버의 기동도 host의 lifetime 속에 들어가 있습니다.
즉, Web 측의 Program.cs를 읽었을 때 「왜 여기서 DI나 설정이나 로그를 만지고 있는가」가 알기 쉬워진다, 는 의미에서도 Generic Host의 이해는 효과적입니다.
7. 어울리는 케이스
Generic Host가 기분 좋게 맞춰지기 쉬운 것은 예를 들어 다음입니다.
- 설정, 로그, DI를 사용하는 콘솔 앱
- queue consumer, poller, watchdog, scheduler적인 worker
Ctrl+C나 SIGTERM으로 뒷정리하고 싶은 장시간 실행 앱- 장래적으로 Windows Service / 컨테이너 상주로 자랄 가능성이 있는 앱
- ASP.NET Core와 같은 확장 군의 유파에 맞추고 싶은 앱
공통되는 것은, 「앱의 입구와 수명 관리를 거칠게 하고 싶지 않다」는 것입니다.
8. 어울리지 않는 / 과잉인 케이스
반대로, 처음부터 Generic Host를 주역으로 하지 않아도 좋은 장면도 있습니다.
- 인수를 1회 읽고 1회 출력하고 끝나는 것뿐인 작은 도구
- 수십 분만 사용하는 거친 검증 코드
- 라이브러리 프로젝트
- 설정을 1개 읽는 것뿐이고, DI나 로그나 수명 관리까지는 필요 없는 케이스
여기서는 host를 세우기보다 단순한 구현 쪽이 조용합니다.
중요한 것은, Generic Host가 강하다고 해서, 모든 executable에 필수는 아니다 라는 것입니다.
9. 빠지기 쉬운 곳
마지막으로, Generic Host 첫수에 밟기 쉬운 점을 정리합니다.
- Generic Host를 DI 컨테이너라고만 본다
- 실제로는 기동・정지・설정・로그・hosted service를 포함하는 토대입니다.
- 신규 앱인데 타성으로
Host.CreateDefaultBuilder부터 시작한다- 기존 코드에 맞추는 사정이 없다면, 우선은
Host.CreateApplicationBuilder쪽이 자연스럽습니다.
- 기존 코드에 맞추는 사정이 없다면, 우선은
BackgroundService에 scoped 서비스를 직접 넣는다- hosted service에는 기본의 scope가 없습니다.
IServiceScopeFactory로 scope를 만드는 편이 안전합니다.
- hosted service에는 기본의 scope가 없습니다.
- 1회만으로 끝나는 worker인데 정지를 host에 전하지 않는다
- Worker 템플릿으로 「run once」를 하려면, 일이 끝난 시점에서
IHostApplicationLifetime.StopApplication()을 부르지 않으면 host는 그대로 계속 달립니다.
- Worker 템플릿으로 「run once」를 하려면, 일이 끝난 시점에서
- 정상 종료하고 싶은데
Environment.Exit로 끊는다- host를 사용하고 있다면, 깔끔하게 멈추고 싶은 장면에서는
StopApplication()쪽이 도리가 좋습니다.
- host를 사용하고 있다면, 깔끔하게 멈추고 싶은 장면에서는
- Windows Service에서 current directory를 전제로 한다
- 파일 탐색은
IHostEnvironment.ContentRootPath기점으로 생각하는 편이 안정됩니다.
- 파일 탐색은
- 단명한 CLI인데 처음부터
BackgroundService로 감싼다- 1회뿐인 일이라면, 보통의 서비스 클래스를 해결해서 실행하는 것만으로 충분합니다.
BackgroundService의 정기 실행에 callback 타이머를 거칠게 넣는다async한 흐름으로 쓴다면PeriodicTimer쪽이 읽기 쉽고 거칠어지기 어려운 경우가 많습니다.
Generic Host에서는, 「단명 작업인가, 상주 작업인가」를 처음에 나누는 것만으로도 꽤 망설이기 어려워집니다.
10. 정리
Generic Host를 한마디로 말하면, .NET 앱의 입구와 수명 관리를 한꺼번에 하는 토대입니다.
봐두고 싶은 포인트를 다시 한 번 정리하면 다음입니다.
- Generic Host는 DI뿐만 아니라 설정, 로그, 정지 처리, hosted service를 포함한다
- 새로운 비 Web 앱이라면 우선
Host.CreateApplicationBuilder(args)가 자연스럽다 - 단명한 작업이라면
BackgroundService를 사용하지 않고 build해서 실행하는 것만으로도 좋다 - 상주 처리라면
BackgroundService와 host의 lifetime 관리가 꽤 효과적이다 BackgroundService에는 기본의 scope가 없으므로 scoped 서비스는 명시적으로 scope를 만든다- ASP.NET Core의
WebApplicationBuilder도 사고방식으로서는 같은 흐름 위에 있다
Generic Host는 무거운 의식을 위한 도구가 아닙니다. 설정, 로그, 의존 관계, 기동, 종료가 조금이라도 늘어나면 그것들을 벽 속으로 도망치지 말고 입구에 모아두기 위한 도구입니다.
반대로, 아직 거기까지 필요 없는 작은 도구라면 가지고 들어오지 않아도 좋습니다. 이 구분이 되면 Generic Host는 「어쩐지 넣는 것」이 아니라 사용처가 분명한 실무의 토대가 됩니다.
11. 참고 자료
- .NET Generic Host - .NET
- Worker Services in .NET
- Use scoped services within a BackgroundService - .NET
- Configuration in .NET
- Options pattern in .NET
- .NET Generic Host in ASP.NET Core
- Create Windows Service using BackgroundService - .NET
- 관련 기사: PeriodicTimer / System.Threading.Timer / DispatcherTimer의 사용 구분 - .NET의 정기 실행을 먼저 정리
- 관련 기사: C# async/await의 베스트 프랙티스 - Task.Run과 ConfigureAwait의 판단표
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
Generic Host / BackgroundService를 데스크톱 앱에 가지고 들어오는 이유 - 기동・수명・graceful shutdown의 정리가 꽤 편해진다
WPF나 WinForms 같은 데스크톱 앱에서 Generic Host와 BackgroundService를 도입해 기동, 상주 처리, graceful shutdown, DI, 로그, 설정의 입구를 한 곳으로 모으는 설계 정리법과 안티패턴을 실무 시...
.NET의 Native AOT란 무엇인가 - JIT, ReadyToRun, trimming과의 차이를 먼저 정리
Native AOT가 publish 시점에 .NET 앱을 정적으로 굳히는 배포 모델임을 JIT, ReadyToRun, trimming, source generator와 비교해 정리하고, 어느 앱에 잘 맞고 어디서 막히는지 실무 관점에서 판별 기준...
PeriodicTimer / System.Threading.Timer / DispatcherTimer의 사용 구분 - .NET의 정기 실행을 먼저 정리
PeriodicTimer는 async 루프, System.Threading.Timer는 ThreadPool callback, DispatcherTimer는 WPF UI 스레드라는 책무 차이를 정리하고, .NET 6 이후의 worker・WPF에서 ...
FileSystemWatcher 사용법과 주의점 - 누락, 중복 알림, 완료 판정의 함정
Windows .NET 파일 감시에서 FileSystemWatcher의 이벤트를 완료 알림으로 오인하기 쉬운 함정과, 재스캔 요청·원자적 claim·idempotency를 축으로 누락과 중복을 견디는 안전한 설계 패턴을 정리합니다.
C# async/await의 베스트 프랙티스 - Task.Run과 ConfigureAwait의 판단표
C# async/await의 베스트 프랙티스를 I/O 대기와 CPU 계산의 구분, Task.Run·ConfigureAwait(false)·fire-and-forget·WhenAll·SemaphoreSlim·Channel의 형 선택 판단표로 정리합니다.
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
Generic Host & 앱 아키텍처
Generic Host, BackgroundService, DI, 구성, 로깅, 앱 수명 설계를 정리한 토픽 페이지입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
상주 처리, 장비 연동, 운영 로그, 유지 보수 가능한 구조가 필요한 Windows 데스크톱 애플리케이션을 지원합니다.
기술 상담 & 설계 리뷰
설계 방향, 아키텍처 경계, 수명 관리, 기존 Windows 자산 처리 방법을 정리하는 데 도움을 드립니다.
저자 프로필
기사 저자의 프로필 페이지입니다.
Go Komura
합동회사 코무라소프트 대표
Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.
공개 링크