LabHub

블로그

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

한국어English日本語

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

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

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

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

이 글은 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

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


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였다.

핵심 변경점:

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

업그레이드 권장:


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

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

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

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

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

엔터프라이즈는 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의 핵심:

C# 14의 핵심:

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();

달라진 점:

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

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


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

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

Aspire가 해결하는 문제:

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:

클라우드 배포:

도커 컴포즈 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의 강점:

Blazor의 약점:

누가 쓰나: 사내 도구, 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의 구조:

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의 약점:

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가 빛나는 경우:

실제 사례:

약점:

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년의 변화:

전형적인 코드:

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년 권고:


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 실행 파일을 생성하는 것이다.

장점:

제약:

AOT-친화 라이브러리:

AOT-비친화:

예시 — 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

언제 쓰나:

언제 안 쓰나:


14장 · Roslyn analyzers / NuGet / dotnet CLI

Roslyn analyzers:

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

.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 / 금융 / 게임 백엔드에는 광범위하게 깔려 있다.

한국:

일본:

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


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

.NET을 골라야 할 경우:

.NET을 피해야 할 경우:

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

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


참고 / References

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다