Skip to content
Published on

모던 .NET 2026 — .NET 9 / .NET 10 LTS / Aspire / Blazor United / MAUI / Avalonia 11 / FastEndpoints 심층 가이드

Authors

프롤로그 — "Java가 아닌 또 하나의 엔터프라이즈"

2026년 5월. 어떤 시니어 엔지니어가 농담처럼 말했다. ".NET을 안 쓴다고 말하는 사람은 많지만, 자기 카드사 백엔드가 .NET 위에서 돌고 있다는 걸 모르는 사람도 많죠." 농담이지만 진심이 절반 정도 섞여 있다.

웹 트렌드 컨퍼런스의 무대 위는 여전히 TypeScript, Go, Rust가 차지하지만, 실제로 결제·은행·제조·공공·게임 백엔드의 한 축은 .NET이다. 그리고 .NET 본인도 2020년 .NET 5 통합 이후 매년 11월마다 새 메이저를 내며 부지런히 모양을 바꿔왔다.

2024-2026년 사이에 일어난 일을 정리하면:

  • .NET 9 (2024년 11월) — STS(Standard Term Support, 18개월). 성능·CLR 개선 중심.
  • .NET 10 (2025년 11월, LTS) — Long Term Support 3년. 새로운 기본값.
  • ASP.NET Core 10 — Minimal API의 성숙, OpenAPI 내장.
  • Aspire (2024년 11월 GA) — 클라우드 네이티브 오케스트레이션 + 텔레메트리.
  • Blazor United — Server / WASM / SSR / streaming을 하나의 모델로.
  • .NET MAUI — 안정화. iOS/Android/Mac/Windows 단일 코드.
  • Avalonia 11 — Linux 포함 진정한 크로스 플랫폼 데스크탑.
  • EF Core 9 — JSON, complex types, primitive collections.
  • C# 13 (2024) — params collections, lock object, escape sequences.
  • C# 14 (2025) — field accessor, partial members, null-conditional assignment.

이 글은 2026년 .NET 진영의 지도를 한 번에 정리한다. 어떤 버전을 골라야 하나, ASP.NET Core 10과 FastEndpoints 중 무엇을 쓰나, Aspire는 정말 필요한가, MAUI vs Avalonia 11, EF Core vs Dapper, MediatR이 유료화된 뒤의 CQRS 패턴, NativeAOT의 실제 한계 — 그리고 누가 .NET을 골라야 하는가까지.

먼저 한 가지만 미리 말해두자. .NET은 죽지 않았다. 그리고 죽기는커녕, 한국·일본의 enterprise·금융·정부 시스템에서는 오히려 점점 더 깊이 자리 잡고 있다. JVM·Go·Node와는 다른 결의 도구이고, 그 결이 잘 맞는 도메인이 분명히 존재한다.


1장 · 2026년 .NET 지도 — 한 장의 그림

영역2026년의 사실상 표준비고
런타임.NET 10 LTS (2025-11).NET 9는 2026-05 EOL 임박
언어C# 13 / C# 14F# 9, VB는 stable 유지
웹 백엔드ASP.NET Core 10 + Minimal APIs+ FastEndpoints / Carter
데이터EF Core 9 / Dapper / Marten선택 폭 큼
메시징MassTransit / NServiceBus / WolverineMediatR 유료화 영향 큼
CQRSWolverine / Brighter / 직접 구현MediatR 8.x는 paid
클라우드 오케스트레이션.NET AspireBicep + OTel 내장
웹 UIBlazor UnitedServer + WASM + SSR
모바일 / 데스크탑.NET MAUIiOS/Android/Mac/Win
크로스 플랫폼 데스크탑Avalonia 11Linux 포함
게임Unity (.NET 8 기반), Stride, MonoGameUnity는 자체 런타임
배포Container + NativeAOTself-contained 줄어듬
패키지NuGet 6 + Central Package Managementglobal.json 표준화
분석Roslyn analyzers + StyleCop + Sonarnullable enforcement

핵심만 골라 한 줄로 요약하면:

  • 런타임의 자리는 .NET 10 LTS로 굳었다. .NET 9는 짧게 쓰고 떠나는 STS다.
  • 웹은 Minimal APIs가 default가 되었지만, FastEndpoints / Carter가 "조금 더 구조적인 Minimal API"로 자리 잡았다.
  • Aspire는 .NET 진영의 docker-compose + Kubernetes + OpenTelemetry 통합 도구가 되었다.
  • UI는 셋으로 갈라졌다. 웹은 Blazor United, 모바일/데스크탑은 MAUI, Linux 포함 진정한 크로스 플랫폼은 Avalonia 11.
  • CQRS 진영은 MediatR 유료화 이후 Wolverine / Brighter / 직접 구현으로 흩어졌다.

2장 · .NET 10 LTS (2025-11) — 플래그십 릴리스

.NET 10은 2025년 11월에 GA가 됐고, Long Term Support 3년이다. 즉 2028년 11월까지 패치 지원. .NET 6 (2021-11), .NET 8 (2023-11) 다음으로 오는 정식 LTS이며, .NET 9는 그 사이의 STS였다.

핵심 변경점:

  • CLR / GC: DATAS(Dynamically Adapting To Application Sizes) GC가 default로. 메모리 footprint가 작은 서비스에서 30-40% 줄어듬.
  • JIT: PGO(Profile-Guided Optimization) on by default. devirtualization과 inlining이 한 단계 더 적극적.
  • NativeAOT 성숙: 더 많은 framework 라이브러리가 AOT-safe. ASP.NET Core minimal API + EF Core 일부가 AOT 가능.
  • JSON: System.Text.Json이 polymorphism, snake_case naming, IAsyncEnumerable streaming을 정식 지원.
  • threading: TimeProvider, PriorityQueue 개선. async LINQ가 stable.
  • C# 14 동시 발매: field accessor, partial properties/methods, null-conditional assignment.
  • NuGet 6 + Central Package Management: 솔루션 전체의 패키지 버전을 Directory.Packages.props 하나로 통일.
  • dotnet CLI 통합: dotnet run app.cs 단일 파일 실행이 정식 지원.

LTS의 의미는 단순히 패치 기간이 길다는 것이 아니라, 에코시스템 라이브러리가 가장 잘 따라온다는 것이다. EF Core 10, ASP.NET Core 10, Aspire의 안정 채널, MassTransit, MediatR fork들 — 거의 모든 메이저 라이브러리가 .NET 10을 first-class target으로 잡고 있다.

업그레이드 권장:

  • .NET 6 (LTS, 2024-11 EOL): 즉시 .NET 10으로.
  • .NET 8 (LTS, 2026-11 EOL): 천천히 .NET 10으로. EF Core, MediatR 등 라이브러리 호환성 체크 후.
  • .NET 9 (STS, 2026-05 EOL): 빨리 .NET 10으로. 어차피 곧 끝난다.

3장 · .NET 9 (2024-11) — STS와의 비교

.NET 9는 STS, 즉 18개월 지원이라 2026년 5월에 끝난다. 이 글이 쓰이는 시점이 정확히 그 무렵이다. .NET 9는 "다음 LTS인 .NET 10을 위한 실험판" 같은 역할을 했다.

.NET 9에서 들어왔다가 .NET 10에서 다듬어진 것들:

  • DATAS GC — .NET 9에서 opt-in, .NET 10에서 default.
  • System.Text.Json polymorphism — .NET 9에서 alpha, .NET 10에서 stable.
  • OpenAPI 내장 — .NET 9에서 Swashbuckle 의존을 떼고 Microsoft.AspNetCore.OpenApi로 들어왔으나, .NET 10에서 UI까지 통합.
  • Kestrel HTTP/3 — .NET 9에서 production-ready.
  • NativeAOT 확장 — .NET 9에서 더 많은 라이브러리 지원.

요점: .NET 9를 운영하는 팀은 2026년 중반까지는 .NET 10으로 이동해야 한다. 8 (LTS)로 돌아가는 건 의미가 없다 — .NET 8도 2026년 11월에 끝난다.

LTS / STS의 패턴은 이제 분명하다:

  • LTS — 짝수 메이저(8, 10, 12...), 3년 지원, 엔터프라이즈 기본값.
  • STS — 홀수 메이저(7, 9, 11...), 18개월, 빠른 실험.

엔터프라이즈는 STS를 건너뛰는 게 일반적이다. 즉 .NET 6 → 8 → 10이 정상 경로다.


4장 · C# 13 / C# 14 — params collections, field accessor

C#은 매년 11월 .NET과 같이 새 메이저를 낸다. C# 13은 2024년, C# 14는 2025년에 .NET 10과 함께 발매됐다.

C# 13의 핵심:

  • params collectionsparams Span<int>, params IEnumerable<int> 등 컬렉션 타입에 params 가능. 더 이상 params int[]만이 답이 아니다.
  • lock object 전용 타입System.Threading.Lock이 들어와 lock(myLock)에 적합한 모나딕 사용. 일반 object를 lock으로 쓰는 패턴을 분석기가 경고.
  • 새 escape sequence \e — ANSI escape (ESC 0x1B). 터미널 ANSI 색 코드 작성이 깔끔해짐.
  • partial properties — partial method에 이어 partial property도 가능. source generator 친화.
  • field keyword 미리보기 — getter/setter 안에서 backing field에 field로 직접 접근.

C# 14의 핵심:

  • field keyword 정식 — getter/setter 안에서 field로 자동 backing field 접근. 명시적 backing field 선언이 사라짐.
  • partial members 확장 — 생성자, 연산자, 인덱서까지 partial 가능.
  • null-conditional assignmentobj?.Prop = value — obj가 null이면 할당 안 함.
  • 확장된 nameof — 미해결 타입 인자를 받아도 nameof 가능.
  • 확장 멤버 진화 — 확장 메서드 외에 확장 프로퍼티 미리보기.

field 키워드를 한 줄로 보면:

// 전통 방식 — backing field 명시
private string _name = string.Empty;
public string Name
{
    get => _name;
    set => _name = value?.Trim() ?? throw new ArgumentNullException();
}

// C# 14 — field로 직접
public string Name
{
    get;
    set => field = value?.Trim() ?? throw new ArgumentNullException();
} = string.Empty;

_name을 손으로 선언하지 않아도 컴파일러가 만들어준다. 더 이상 _camelCase 백킹 필드의 네이밍 디베이트가 필요 없다.


5장 · ASP.NET Core 10 — performance + DX

ASP.NET Core 10은 .NET 10과 함께 나왔다. 2026년 5월 현재, 모던 .NET 웹의 기본형은 다음과 같다.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();
builder.Services.AddDbContext<AppDbContext>(opts =>
    opts.UseNpgsql(builder.Configuration.GetConnectionString("Default")));

var app = builder.Build();

app.MapOpenApi();              // /openapi/v1.json
app.MapScalarApiReference();   // Scalar UI 통합

app.MapGet("/users/{id:guid}", async (Guid id, AppDbContext db) =>
{
    var user = await db.Users.FindAsync(id);
    return user is null ? Results.NotFound() : Results.Ok(user);
})
.WithName("GetUser")
.WithOpenApi();

app.MapPost("/users", async (CreateUserDto dto, AppDbContext db) =>
{
    var user = new User(dto.Name, dto.Email);
    db.Users.Add(user);
    await db.SaveChangesAsync();
    return Results.Created($"/users/{user.Id}", user);
});

app.Run();

달라진 점:

  • OpenAPI 내장 — Swashbuckle을 따로 안 깐다. Microsoft.AspNetCore.OpenApi가 default.
  • Scalar UI 통합 — Swagger UI보다 가볍고 깨끗한 OpenAPI viewer.
  • JSON streaming — IAsyncEnumerable이 chunked transfer encoding으로 자연스럽게 흐름.
  • HTTP/3 default — Kestrel이 production-ready로 HTTP/3 지원.
  • Compiled routing — endpoint dispatch가 reflection 대신 compile-time 코드 생성.
  • OpenTelemetry first-classAddOpenTelemetry()가 표준 패턴.

성능 수치 (TechEmpower 류 마이크로벤치마크 기준):

  • ASP.NET Core 10 Minimal API + Kestrel: 700k+ req/s (Plaintext)
  • ASP.NET Core 10 + EF Core 9 + Postgres: 80-100k req/s (Fortunes)
  • 동급 Go (net/http): 600k req/s
  • 동급 Node (Fastify): 200k req/s

즉 ASP.NET Core는 2026년에도 가장 빠른 메인스트림 웹 프레임워크 중 하나다.


6장 · Aspire (2024-11 GA) — 클라우드 네이티브 오케스트레이션

Aspire는 2024년 11월에 GA가 된 .NET 진영의 클라우드 네이티브 오케스트레이션 + 로컬 개발 환경 + 텔레메트리 통합 도구다. 이름은 거창하지만 본질은 단순하다.

Aspire가 해결하는 문제:

  • 로컬에서 docker-compose up 대신 .NET 코드로 의존성 정의.
  • 마이크로서비스 여러 개 + Postgres + Redis + RabbitMQ를 한 솔루션으로.
  • OpenTelemetry 자동 주입 + 대시보드 내장.
  • 클라우드 배포 시 Bicep / Terraform / Kubernetes 매니페스트 자동 생성.

AppHost 프로젝트의 모양:

var builder = DistributedApplication.CreateBuilder(args);

var postgres = builder.AddPostgres("db")
    .AddDatabase("appdb");

var redis = builder.AddRedis("cache");

var rabbitmq = builder.AddRabbitMQ("messaging");

var apiService = builder.AddProject<Projects.ApiService>("api")
    .WithReference(postgres)
    .WithReference(redis)
    .WithReference(rabbitmq);

builder.AddProject<Projects.WebFrontend>("web")
    .WithReference(apiService);

builder.Build().Run();

이 코드를 dotnet run하면:

  1. Postgres / Redis / RabbitMQ 컨테이너가 자동으로 뜬다.
  2. API 서비스와 Web Frontend가 .NET 프로세스로 시작된다.
  3. 연결 문자열은 environment variable로 자동 주입.
  4. OpenTelemetry 데이터가 Aspire Dashboard로 흐른다.
  5. 분산 trace, log, metric이 한 화면에서 보인다.

Aspire Dashboard:

  • 분산 trace (waterfall view)
  • 구조화된 로그
  • 메트릭 (CPU, memory, request rate, latency)
  • 컨테이너 / 프로세스 상태
  • 환경 변수 inspector

클라우드 배포:

  • Azure Container Apps에 azd up 한 줄로 배포 가능.
  • 다른 클라우드는 Aspire 매니페스트 → Kubernetes / Bicep / Terraform 변환.

도커 컴포즈 vs Aspire:

기준docker-composeAspire
정의 언어YAMLC#
디버깅컨테이너 attach.NET 프로세스 직접 디버깅
텔레메트리별도 설치내장
클라우드 배포변환 도구 별도내장 generator
.NET 외부 서비스잘 됨잘 됨 (Postgres, Redis, OS image 등)

Aspire는 .NET 마이크로서비스를 운영하는 팀에게 사실상 default가 됐다. 단, .NET이 아닌 서비스가 많은 폴리글랏 환경이라면 docker-compose가 여전히 자연스럽다.


7장 · Blazor United — Server + WASM + SSR + streaming

Blazor는 2018년의 WebAssembly hype 이후 8년이 지나 2026년 현재, "한 컴포넌트가 여러 렌더링 모드를 골라 쓸 수 있는" 단일 모델로 정착했다. 이를 Microsoft 공식 문서는 Blazor United라고 부른다.

네 가지 렌더 모드:

  1. Static SSR — 서버에서 한 번 그리고 끝. 가장 빠름, 인터랙션 없음.
  2. Streaming SSR — 비동기 데이터 로딩 결과를 chunked로 스트리밍.
  3. Server interactive — SignalR 기반, 서버에서 이벤트 처리.
  4. WebAssembly interactive — 클라이언트 WASM, 오프라인 가능.

한 페이지가 컴포넌트 단위로 모드를 섞을 수 있다.

@page "/dashboard"
@rendermode InteractiveAuto

<h1>대시보드</h1>
<StaticSummary />
<InteractiveChart />

InteractiveAuto는 첫 방문 시 Server 모드(SignalR)로 빠르게 뜨고, WASM 다운로드가 완료되면 그 다음부터는 WASM으로 전환한다. 첫 인터랙션 지연을 줄이는 패턴.

Blazor의 강점:

  • 풀스택 C#. JavaScript 없이 SPA 가능 (또는 최소화).
  • ASP.NET Core와의 통합. 같은 인증, DI, 미들웨어 사용.
  • SignalR이 안정적. 양방향 통신이 자연스러움.
  • WASM AOT 빌드로 차츰 첫 로드 사이즈 줄어드는 중.

Blazor의 약점:

  • WASM 초기 번들 크기는 여전히 1-3MB. Compression 적용 후도 무겁다.
  • 생태계는 React/Vue에 비해 작다.
  • 모바일 브라우저 WASM 성능은 여전히 데스크탑보다 느림.
  • 디자이너 / 컴포넌트 마켓이 부족함.

누가 쓰나: 사내 도구, B2B 어드민, 풀스택 .NET 팀의 SPA가 sweet spot. 공개 마케팅 사이트나 SEO 중심 페이지는 Static SSR로 한정.


8장 · .NET MAUI — 크로스 플랫폼 네이티브

.NET MAUI(Multi-platform App UI)는 2022년 Xamarin.Forms의 후계자로 나와, 2024-2026년 사이에 안정화됐다. iOS / Android / macOS / Windows를 단일 C# 코드로.

MAUI의 구조:

  • 단일 프로젝트, 단일 코드베이스.
  • XAML 또는 C# 마크업으로 UI.
  • 플랫폼별 핸들러로 native control 그림.
  • Hot reload 지원.

MAUI Hybrid + Blazor Hybrid:

MAUI 안에서 BlazorWebView를 띄워 Razor 컴포넌트를 native 앱으로 재사용 가능. 즉 웹용으로 만든 Blazor UI를 모바일 앱에도 거의 그대로.

<BlazorWebView HostPage="wwwroot/index.html">
    <BlazorWebView.RootComponents>
        <RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
    </BlazorWebView.RootComponents>
</BlazorWebView>

이 패턴은 풀스택 .NET 팀이 모바일 앱까지 갖출 때 매력적이다. React Native보다 빌드 / 배포 파이프라인이 단순하고, Flutter보다 .NET 백엔드와의 통합이 자연스럽다.

MAUI의 약점:

  • iOS / Android 네이티브 SDK 진화 속도를 100% 따라가지는 못함.
  • Linux 데스크탑은 공식 지원 아님 (커뮤니티 GTK 백엔드 존재).
  • 일부 복잡한 애니메이션 / 성능 critical 부분은 여전히 native가 우위.

2026년 권고: 사내 모바일 / 윈도우 데스크탑 한정. 공개 앱스토어용 컨슈머 앱은 Flutter, SwiftUI/Compose, React Native와 비교 후 결정.


9장 · Avalonia 11 — 진정한 크로스 플랫폼 UI

Avalonia는 커뮤니티가 만들고 .NET Foundation에서 운영하는 오픈소스 크로스 플랫폼 UI 프레임워크다. 2023년 11월에 11.0이, 2024-2025년에 11.1 / 11.2 / 11.3이 차례로 나왔다.

MAUI와의 차이:

기준.NET MAUIAvalonia 11
거버넌스Microsoft커뮤니티 + 상용(AvaloniaUI 회사)
플랫폼iOS/Android/Mac/WiniOS/Android/Mac/Win/Linux/WebAssembly
렌더링플랫폼 native 컨트롤자체 렌더링 (Skia 기반)
UI 일관성플랫폼마다 다름모든 플랫폼 동일
디자인 패턴XAML + handlersXAML + 자체 컨트롤 트리
Hot reload지원지원
데스크탑 우선도약함강함

Avalonia가 빛나는 경우:

  • Linux 데스크탑이 진짜로 필요한 팀. (MAUI는 못 함.)
  • 모든 플랫폼에서 픽셀 단위로 같은 UI가 필요한 경우.
  • 데스크탑 중심 앱 (IDE, 그래픽 도구, 음악/영상 소프트웨어).

실제 사례:

  • JetBrains Rider는 일부 UI에 Avalonia 시도 (전면 채택은 아님).
  • WasabiWallet, Camelot, AvaloniaEdit 등 오픈소스 .NET 데스크탑 도구.
  • 여러 enterprise 사내 도구가 WPF에서 Avalonia로 이주 중. WPF는 Windows 전용이라 Mac/Linux 클라이언트를 위해.

약점:

  • 모바일은 가능하지만 native 느낌이 부족함. MAUI에 양보가 맞다.
  • WPF 대비 커뮤니티 컨트롤 마켓이 작음.

2026년 권고: WPF의 자연스러운 후계자. Linux/Mac 데스크탑 .NET 앱이라면 첫 번째 선택지.


10장 · Entity Framework Core 9

EF Core 9는 2024년 11월에 .NET 9와 같이 나왔고, EF Core 10이 2025년 11월에 나왔다. 2026년 5월의 사실상 표준은 EF Core 10이다.

최근 2-3년의 변화:

  • JSON columns 정식 지원 — Postgres jsonb, SQL Server JSON, SQLite JSON. LINQ로 JSON 내부 쿼리.
  • Complex types — owned types를 더 가볍게. Value object 모델링이 자연스러워짐.
  • Primitive collectionsList<int>을 컬럼에 그대로 매핑.
  • Bulk operationsExecuteUpdateAsync, ExecuteDeleteAsync로 N+1 없는 일괄 수정.
  • Query splitting 기본값 — JOIN 폭발 방지.

전형적인 코드:

public class AppDbContext : DbContext
{
    public DbSet<Order> Orders => Set<Order>();

    protected override void OnModelCreating(ModelBuilder mb)
    {
        mb.Entity<Order>(b =>
        {
            b.ToTable("orders");
            b.ComplexProperty(o => o.ShippingAddress);
            b.OwnsMany(o => o.Items);
            b.Property(o => o.Metadata)
                .HasColumnType("jsonb");
        });
    }
}

// Bulk delete - SQL 한 번
await db.Orders
    .Where(o => o.Status == OrderStatus.Cancelled && o.CreatedAt < DateTime.UtcNow.AddYears(-1))
    .ExecuteDeleteAsync();

EF Core vs Dapper vs Marten:

도구모델강점약점
EF CoreORM / Change tracking풍부한 LINQ, 마이그레이션, 관계복잡 쿼리는 SQL 가독성 떨어짐
DapperMicro-ORM빠름, SQL 직접 작성모든 게 수동
MartenPostgres + Event Sourcingdocument DB + event storePostgres 전용
LinqToDBSQL-shape LINQ성능 + LINQ 표현력학습 곡선

2026년의 default는 EF Core + Dapper 혼용이다. CRUD와 도메인 변경은 EF Core, 무거운 보고 / 분석 쿼리는 Dapper.


11장 · MediatR / MassTransit / NServiceBus — CQRS와 메시징

2024년 말, .NET CQRS 진영이 흔들렸다. MediatR이 8.x부터 상용 라이선스로 전환됐기 때문이다. 또한 같은 저자의 AutoMapper도 비슷한 시기에 paid edition을 도입했다. 이 결정은 .NET 커뮤니티에서 큰 논쟁을 일으켰다.

현재의 선택지:

도구모델라이선스비고
MediatR 8+in-process mediatorCommercial안정. 비용 발생.
Wolverinein-process + messagingOSS (MIT)Jeremy D. Miller. CQRS + saga 통합.
Brightercommand processorOSS (BSD)메시징 통합.
자체 구현DI + interfacen/a작은 프로젝트는 충분.

Wolverine 예시:

// Handler — interface 없이 메서드 시그니처로 인식
public class CreateOrderHandler
{
    public async Task<OrderCreated> Handle(CreateOrder command, AppDbContext db)
    {
        var order = new Order(command.CustomerId, command.Items);
        db.Orders.Add(order);
        await db.SaveChangesAsync();
        return new OrderCreated(order.Id);
    }
}

// 호출
var result = await bus.InvokeAsync<OrderCreated>(new CreateOrder(...));

interface 선언 없이 메서드 시그니처를 분석하는 게 Wolverine의 특징. MediatR의 IRequestHandler 보일러플레이트가 사라진다.

메시징 (out-of-process):

도구트랜스포트라이선스강점
MassTransitRabbitMQ, Azure Service Bus, Kafka 등OSS (Apache) → 일부 Pro가장 많이 쓰임
NServiceBus다양Commercial엔터프라이즈. saga 강함.
WolverineRabbitMQ, Kafka, Azure Service BusOSS (MIT)CQRS와 messaging 통합
RebusRabbitMQ, Azure Service Bus, etc.OSS가벼움

MassTransit의 saga 패턴:

public class OrderSaga : MassTransitStateMachine<OrderState>
{
    public State Submitted { get; private set; }
    public State Paid { get; private set; }
    public State Shipped { get; private set; }

    public Event<OrderSubmitted> OnSubmitted { get; private set; }
    public Event<PaymentReceived> OnPaid { get; private set; }

    public OrderSaga()
    {
        InstanceState(x => x.CurrentState);

        Initially(
            When(OnSubmitted)
                .Then(c => c.Saga.OrderId = c.Message.OrderId)
                .TransitionTo(Submitted));

        During(Submitted,
            When(OnPaid)
                .TransitionTo(Paid));
    }
}

2026년 권고:

  • 단순한 in-process mediator: 직접 구현 또는 Wolverine.
  • CQRS + messaging 통합 필요: Wolverine.
  • 엔터프라이즈 + 상용 지원 필요: NServiceBus.
  • 이미 MediatR + MassTransit 쓰던 팀: MediatR 8+ 라이선스 비용 계산 후 갈아탈지 결정.

12장 · FastEndpoints / Carter / Minimal APIs

ASP.NET Core 10의 라우팅 스타일은 셋으로 갈렸다.

1. Minimal APIs (default):

app.MapGet("/orders/{id:guid}", async (Guid id, AppDbContext db) =>
{
    var order = await db.Orders.FindAsync(id);
    return order is null ? Results.NotFound() : Results.Ok(order);
});

장점: 가벼움, 시작이 빠름. 단점: 큰 API에서 Program.cs가 비대해짐.

2. Controllers (전통):

[ApiController]
[Route("orders")]
public class OrdersController : ControllerBase
{
    [HttpGet("{id:guid}")]
    public async Task<IActionResult> Get(Guid id) { ... }
}

장점: 익숙함, 클래스 단위 조직화. 단점: 보일러플레이트 많음.

3. FastEndpoints / Carter (구조적 미니멀):

FastEndpoints는 "Endpoint per class" 패턴을 강제한다.

public class GetOrderEndpoint : Endpoint<GetOrderRequest, OrderResponse>
{
    public AppDbContext Db { get; set; } = null!;

    public override void Configure()
    {
        Get("/orders/{id:guid}");
        AllowAnonymous();
    }

    public override async Task HandleAsync(GetOrderRequest req, CancellationToken ct)
    {
        var order = await Db.Orders.FindAsync([req.Id], ct);
        if (order is null) { await SendNotFoundAsync(ct); return; }
        await SendAsync(new OrderResponse(order), cancellation: ct);
    }
}

Carter는 Nancy의 정신적 후계자로, 모듈 단위 라우팅.

public class OrdersModule : ICarterModule
{
    public void AddRoutes(IEndpointRouteBuilder app)
    {
        app.MapGet("/orders/{id:guid}", async (Guid id, AppDbContext db) =>
        {
            var order = await db.Orders.FindAsync(id);
            return order is null ? Results.NotFound() : Results.Ok(order);
        });
    }
}

비교:

기준Minimal APIControllersFastEndpointsCarter
보일러플레이트매우 적음많음적음매우 적음
조직화약함강함강함 (per-class)중간 (per-module)
성능가장 빠름약간 느림가장 빠름 (Minimal 위)가장 빠름 (Minimal 위)
OpenAPI자동자동자동 + 풍부자동
학습 곡선낮음낮음중간낮음
큰 API 친화도약함강함강함중간

권고: 50개 이하의 엔드포인트는 Minimal API, 그 이상은 FastEndpoints 또는 Controllers. Carter는 가볍게 모듈로 나누고 싶을 때.


13장 · NativeAOT — 작은 단일 실행 파일

NativeAOT는 .NET 7에서 GA가 됐고, .NET 9-10에서 성숙했다. 핵심 아이디어는 JIT 대신 ahead-of-time 컴파일로 단일 native 실행 파일을 생성하는 것이다.

장점:

  • 시작 시간: 보통 50-100ms (vs JIT의 200-500ms). cold start 민감한 워크로드에 좋다 (serverless, CLI 도구).
  • 메모리: 30-40% 작음. JIT 관련 메타데이터 없음.
  • 단일 실행 파일: 10-30MB의 stand-alone binary. CoreCLR / framework 동봉 안 함.
  • 트림된 바이너리: 사용 안 하는 코드 제거.

제약:

  • Reflection 제한: 전통적인 reflection-heavy 라이브러리는 동작 안 함. Serializer, ORM, IoC container가 모두 source generator를 써야 함.
  • dynamic 코드 생성 금지: Reflection.Emit이 작동 안 함.
  • JIT compilation 없음: 런타임에 새 어셈블리 로드 불가.

AOT-친화 라이브러리:

  • System.Text.Json (with source generator)
  • Minimal APIs / FastEndpoints
  • EF Core 일부 (8+부터 점진적)
  • Dapper
  • Refit (HTTP client)
  • ASP.NET Core RequestDelegateGenerator

AOT-비친화:

  • 일부 IoC 컨테이너 (Autofac, Castle Windsor 등)
  • Newtonsoft.Json (Reflection 의존)
  • MediatR 일부 패턴
  • AutoMapper

예시 — AOT minimal API:

<PropertyGroup>
    <PublishAot>true</PublishAot>
    <InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
dotnet publish -c Release -r linux-x64
# 단일 native 실행 파일 → bin/Release/net10.0/linux-x64/publish/MyApi
# 사이즈: 보통 15-25 MB

언제 쓰나:

  • AWS Lambda, Cloud Functions 등 serverless.
  • CLI 도구 (dotnet-tool 대안).
  • 컨테이너 cold start 민감한 마이크로서비스.
  • 시작 시간이 비즈니스적으로 중요한 모든 곳.

언제 안 쓰나:

  • Reflection 의존 라이브러리가 많은 레거시.
  • 빌드 시간 / 디버깅 편의가 우선인 개발 단계.
  • LegacyEF Core / 복잡한 IoC 컨테이너를 쓰는 모놀리스.

14장 · Roslyn analyzers / NuGet / dotnet CLI

Roslyn analyzers:

.NET 분석기는 컴파일 시점에 코드 품질·보안·성능 이슈를 잡는다. 2026년에는 거의 default로 다음을 깐다:

  • .NET 내장 분석기 (CA, IDE 룰)
  • StyleCop.Analyzers (코드 스타일)
  • SonarAnalyzer.CSharp (정적 분석)
  • Meziantou.Analyzer (커뮤니티 베스트 프랙티스)
  • Roslynator (refactoring + 분석)
  • SecurityCodeScan (보안 취약점)

.editorconfig로 룰을 통제:

[*.cs]
dotnet_diagnostic.CA1822.severity = warning   # mark members as static
dotnet_diagnostic.IDE0005.severity = error    # unused using
dotnet_analyzer_diagnostic.category-Style.severity = suggestion

Nullable enforcement:

<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

이 두 줄이 모던 .NET 프로젝트의 가장 큰 차이다. null 안전성을 컴파일러가 강제.

NuGet 6 + Central Package Management:

Directory.Packages.props 한 파일로 솔루션 전체의 패키지 버전 통제:

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="MediatR" Version="8.4.0" />
    <PackageVersion Include="EntityFrameworkCore.Sqlite" Version="10.0.0" />
    <PackageVersion Include="Serilog.AspNetCore" Version="8.0.3" />
  </ItemGroup>
</Project>

각 csproj에서는 버전 없이 <PackageReference Include="MediatR" />만. 버전 충돌이 사라진다.

dotnet CLI 통합:

# 2026년 표준 워크플로
dotnet new aspire-starter -n MyApp        # Aspire 솔루션 생성
dotnet add package Serilog.AspNetCore     # 패키지 추가 (CPM이면 Directory.Packages.props에 기록)
dotnet ef migrations add InitialCreate    # EF Core 마이그레이션
dotnet aspire run                         # Aspire 앱 실행
dotnet test --collect:"XPlat Code Coverage" # 테스트 + 커버리지
dotnet format                             # 자동 포매팅
dotnet workload install android ios       # MAUI 워크로드
dotnet run app.cs                         # 단일 파일 실행 (.NET 10)

global.json:

루트에 두면 솔루션이 어떤 SDK 버전을 쓸지 명시.

{
  "sdk": {
    "version": "10.0.100",
    "rollForward": "latestFeature"
  }
}

이 한 파일 덕분에 팀원마다 SDK 버전이 다를 때 생기는 미묘한 버그가 사라진다.


15장 · 한국 / 일본 — 토스, 任天堂, ソフトバンク

.NET은 한국·일본 시장에서 흔히 보이지 않는 듯하지만, 실제 enterprise / 금융 / 게임 백엔드에는 광범위하게 깔려 있다.

한국:

  • 금융권 백엔드: 신한은행, KB국민카드, 현대카드 등 대형 금융사의 일부 백엔드가 ASP.NET / .NET 8+ 위에 있다. 특히 카드사 / 채권 / 자산관리 시스템.
  • 공공 시스템: 행정망 일부, 국세청 시스템 일부, 우체국 일부에서 .NET이 사용된다. (보안 / 감사 관점에서 마이크로소프트와의 장기 라이선스 관계가 크다.)
  • 토스 (Toss): 대부분 JVM / Kotlin / Node.js 스택이지만, 일부 어드민 / 사내 도구가 .NET 위에 있다는 사례 보고. 공식 컨퍼런스 발표는 드묾.
  • NCSOFT, 넥슨, 펄어비스: 게임 클라이언트는 Unity / Unreal이지만 일부 백엔드 / 사내 도구가 .NET. Unity는 자체 .NET 런타임을 쓰지만 동일한 C# 코드베이스.
  • 삼성SDS, LG CNS: SI 프로젝트에서 .NET을 선택하는 경우가 여전히 많다.

일본:

  • 任天堂 (Nintendo): 일부 사내 도구 / 백엔드에 .NET. 게임 본체는 자체 엔진이지만 어드민·매니지먼트·온라인 서비스 일부.
  • ソフトバンク (SoftBank): 모바일 사업부의 일부 백엔드. 통신 인프라 관리 도구에 .NET이 사용된다는 발표.
  • 楽天 (Rakuten): 백엔드의 큰 축은 Java / Python이지만 광고 / 분석 시스템 일부가 .NET.
  • NTT データ / 富士通: 일본 SI 대기업이 .NET 기반 정부·금융 프로젝트를 다수 운영.
  • サイボウズ (Cybozu): kintone의 일부 서비스, 사내 도구에 .NET 사용 보고.
  • メルカリ (Mercari): 대부분 Go 백엔드지만 결제·정산 일부에 .NET 사용.

요약하면: 한국·일본 모두 enterprise / 금융 / 통신 / 정부 영역에서 .NET이 사실상 표준 옵션 중 하나다. 컨퍼런스 무대에 잘 안 보일 뿐, 매출과 시스템 규모로 보면 적지 않다.


16장 · 누가 .NET을 골라야 하나

.NET을 골라야 할 경우:

  • 엔터프라이즈 / 금융 / 정부 도메인. 마이크로소프트 ecosystem(SQL Server, Azure AD, Office)과의 자연스러운 통합. 장기 LTS와 감사 친화성.
  • 풀스택 C# 팀. Blazor + ASP.NET Core + MAUI로 웹/모바일/데스크탑을 단일 언어로.
  • 고성능 웹 백엔드. ASP.NET Core 10 + Minimal APIs는 Go에 근접한 throughput.
  • Windows 데스크탑 / .NET 통합 도구. WPF, WinForms, MAUI, Avalonia 중 골라 쓸 수 있음.
  • Unity 게임 개발. C# 단일 언어로 클라이언트·서버 통일.
  • AWS Lambda / Cloud Functions 워크로드 (NativeAOT). cold start 우수.

.NET을 피해야 할 경우:

  • PaaS / serverless heavy 환경에서 JS / Python 우선. Vercel / Cloudflare Workers 같은 환경.
  • 데이터 사이언스 / ML 우선 팀. Python 생태계가 압도적.
  • 모바일 우선 (소셜 / 컨슈머 앱). SwiftUI / Compose / React Native / Flutter가 더 자연스러움.
  • JVM 라이브러리 자산이 많은 조직. Kafka, Spark, Hadoop, Flink 등이 1순위라면 JVM이 자연스러움.
  • 극단적 시스템 프로그래밍. Rust / C++ / Go가 더 잘 맞음.

의사결정 트리 (단순화):

Q1. 도메인은?
├── 엔터프라이즈 / 금융 / 정부 / 보험 / Windows 통합
│   └── .NET 10 + ASP.NET Core 10 + EF Core 10
├── 공개 컨슈머 웹 / SEO 중심
│   └── Next.js / Remix / SvelteKit 우선 검토
├── 게임 (Unity)
│   └── C# (.NET 호환 런타임)
└── 모바일 앱 (컨슈머)
    └── Native 또는 Flutter / RN 우선

Q2. 팀 언어는?
├── C# 강함 → .NET
├── Java/Kotlin 강함 → Spring / Quarkus
├── TS/JS 강함 → Node / Deno
└── Python 강함 → FastAPI / Django

Q3. 배포 환경은?
├── Azure / On-prem Windows → .NET 자연스러움
├── AWS / GCP / K8s → .NET 가능, 단 OpenTelemetry · Aspire 활용
└── Vercel / Cloudflare / Edge → .NET 안 맞음

한 줄 권고 (2026년 5월 기준):

  • 엔터프라이즈 / 금융 / 통신 / 정부 신규 시스템: .NET 10 LTS + ASP.NET Core 10 + EF Core 10 + Aspire.
  • 사내 도구 / 어드민 풀스택: Blazor United.
  • 모바일이 .NET 팀의 추가 부담: MAUI + Blazor Hybrid.
  • 진정한 크로스 플랫폼 데스크탑: Avalonia 11.
  • 작은 API: Minimal APIs. 50개 이상 엔드포인트: FastEndpoints.
  • CQRS는 Wolverine, 메시징은 MassTransit.
  • 단일 실행 파일 / cold start 우선: NativeAOT.

가장 중요한 메타-권고: .NET을 안 쓰는 게 합리적인 도메인도 분명히 있다. 하지만 자기가 그 도메인에 있다고 단정하기 전에, 한 번쯤은 모던 .NET이 어디까지 왔는지 직접 측정해볼 가치가 있다. 2026년의 .NET은 2018년의 .NET과 거의 다른 도구다.


참고 / References