주말 아침 - 주간 닷넷 #30

주말 아침 - 주간 닷넷 #30

한 주 동안 .NET 생태계에서 있었던 주요 이슈와 아티클, 기술 트렌드를 정리해 소개합니다.


:pushpin: C# Nullable 타입 완벽 정리 (2026)

  • 저자: NDepend Blog
  • 태그: csharp #nullable #language

주요 내용

  • 같은 이름을 공유하는 두 가지 nullable(값 타입 vs 참조 타입)을 별개 기능으로 구분
  • .csproj<Nullable>enable</Nullable>, <WarningsAsErrors>nullable</WarningsAsErrors> 설정과 #nullable pragma 사용
  • Nullable<T>HasValue/Value/GetValueOrDefault, is-pattern 권장, lifted operator의 비교 의외성(두 null은 ==는 true지만 >=는 false)
  • ??, ?., ??=, null-forgiving !, [NotNullWhen] 등 nullable 어트리뷰트로 컴파일러에 API 의미 전달
  • C# 11 required 멤버, C# 13 field 키워드로 자동 속성 nullable 처리

:pushpin: NuGet Package Pruning — .NET 10의 의존성 위생 개선

  • 저자: Nikolche Kolev
  • 태그: #nuget dotnet10 #vulnerability

주요 내용

  • .NET 10부터 패키지 프루닝이 기본 활성화되어 플랫폼이 이미 제공하는 패키지를 restore 그래프에서 자동 제거
  • 기본값 사용 시 전이 취약점 보고가 약 70% 감소
  • restore 시간이 최대 50%까지 단축되며 의존성 충돌 해소로 성공률 개선
  • NuGetAuditMode=all과 결합해 진짜 전이 의존성만 감사, 거짓 양성 제거
  • 직접 참조도 비공개화(privatize)되며, 완전히 제거 가능한 경우 NU1510로 신호

:pushpin: 신뢰성 있는 메시지 소비를 위한 Inbox 패턴

  • 저자: Milan Jovanović
  • 태그: #messaging #inboxpattern #distributedsystems

주요 내용

  • 메시지 브로커의 at-least-once 전달로 인한 중복 처리 문제 정의
  • 메시지를 처리 전 inbox 테이블에 기록하고 ON CONFLICT DO NOTHING으로 중복 제거
  • 수신과 처리를 분리해 재시도 정책과 수평 확장을 가능하게 함
  • FOR UPDATE SKIP LOCKED로 다중 프로세서 인스턴스 동시성 안전 보장
  • Inbox 패턴과 Idempotent Consumer의 트레이드오프 비교, MassTransit 기반 InboxConsumer<T> 구현

:pushpin: IAsyncEnumerable<T> 스트리밍 — 메모리 95GB에서 150MB로

  • 저자: Adrian Bailador
  • 태그: csharp async #streaming

주요 내용

  • 50만 건 DB 레코드를 메모리에 적재해 서버가 다운된 실제 사고 사례로 도입
  • 100만 건 기준 메모리 사용량을 ~95GB에서 ~150MB로 감소
  • 네 가지 프로덕션 패턴: EF Core 스트리밍, HttpClient 스트리밍, 파일 처리, ASP.NET 컨트롤러 응답
  • 누락된 cancellation token, 조기 리소스 해제, DbContext 수명, 트랜잭션 타임아웃 등 흔한 오류와 수정 코드
  • BenchmarkDotNet 결과, NDJSON/SSE 활용, 5가지 안티패턴과 6가지 모범 사례

:pushpin: 회전하지 않던 정적 HttpClient — 풀링된 연결의 이야기

  • 저자: Infinite Loop Development Ltd
  • 태그: #httpclient #connectionpool #proxy

주요 내용

  • HttpClient는 목적지 호스트 기준으로 연결 풀을 유지하고 keep-alive로 재사용 — 요청별 신규 연결이 아님
  • 프록시 로테이션은 TCP 연결 수준에서 일어나 풀링된 연결 재사용 시 회전이 무력화됨
  • 조용한 안티-어뷰즈 응답(요청은 받지만 응답하지 않음) + 기본 100초 타임아웃이 결합되어 디버깅 어려운 행 현상 발생
  • 해결: ConnectionClose = true 헤더로 요청마다 새 TCP 연결을 강제하고 타임아웃을 15초로 단축
  • 고처리량 시나리오에서는 회전과 성능을 모두 잡기 위해 세션 토큰 기반 접근

:pushpin: XAML.io v0.7로 네이티브 데스크톱 앱 퍼블리시

  • 저자: XAML.io team (Userware)
  • 태그: xaml #webassembly #crossplatform

주요 내용

  • 브라우저 내 C#/XAML IDE에서 네이티브 데스크톱 앱(.exe/.app/ELF) 빌드 파이프라인을 전부 WebAssembly로 클라이언트 사이드 구현
  • 결과물은 런타임에는 WASM이 아닌 실제 .NET 10 네이티브 프로세스(JIT 포함)로 동작
  • UI는 OpenSilver의 DOM 기반 렌더링을 OS WebView(Edge WebView2, WKWebView, WebKitGTK)로 표시
  • macOS ad-hoc 코드서명을 Filip Navara의 CodeSign 라이브러리 WASM 포팅으로 클라이언트 측에서 처리
  • .NET single-file 압축 활용해 플랫폼별 ~30-40MB 결과물 생성, Melanzana/MEMFS/LC_CODE_SIGNATURE 등 구현 세부 공개

:pushpin: 분당 100만 요청을 처리하는 .NET 고처리량 API 설계

  • 저자: Ali Hamza Ansari
  • 태그: aspnetcore #performance #benchmarkdotnet

주요 내용

  • ASP.NET Core + PostgreSQL + EF Core/Dapper 기반 Orders API로 naive → 최적화 단계별 비교
  • DB: 조건부 부분 인덱스, AsNoTracking(), 최대 100건 페이지네이션, projection으로 필드 축소
  • 핫 엔드포인트에서 EF Core vs Dapper 비교, filtered include로 N+1 회피
  • Output Caching(600초 TTL)과 in-memory 캐싱을 용도별로 구분, BenchmarkDotNet으로 10배 이상 개선 측정
  • HAProxy/NGINX 리버스 프록시, 지리적 배치, SSD, 백그라운드 오프로드, 레이트 리미트

:pushpin: .NET 10의 IHostedService와 BackgroundService 이해하기

  • 저자: Mukesh Murugan
  • 태그: dotnet10 #backgroundservice #hosting

주요 내용

  • IHostedService raw 인터페이스(30줄) vs BackgroundService(6줄) 동등 구현 비교
  • 다섯 가지 프로덕션 함정: .NET 6+의 unhandled 예외로 인한 호스트 크래시, captive dependency, ExecuteAsync 조기 반환, stoppingToken 무시, ThreadPool 고갈
  • IServiceScopeFactory로 매 반복 새 스코프를 생성하는 의존성 주입 패턴
  • BackgroundServiceExceptionBehavior 설정 및 graceful shutdown 타임아웃
  • 영속성/크론/대시보드/다중 인스턴스 조율이 필요한 경우 Hangfire, Quartz.NET, Wolverine 사용 권장

:pushpin: Task vs ValueTask — 무엇이고 언제 쓰며 왜 중요한가

https://medium.com/@mahdi.com.haidar/task-vs-valuetask-in-net-what-they-are-when-to-use-each-and-why-it-matters-e8e663c3cf74

  • 저자: Mohammad Haidar
  • 태그: async #valuetask #performance

주요 내용

  • Task는 결과가 이미 준비된 경우에도 힙 객체 할당, ValueTask는 구조체로 동기 완료 시 할당 회피
  • ValueTask는 한 번만 await 가능하며 저장/공유 금지 — 다회 await 필요 시 .AsTask() 변환
  • 일반 I/O(HTTP/DB)·공개 API는 Task, 핫패스/인프라 코드에서만 ValueTask 사용
  • PipeReader.ReadAsync, IAsyncEnumerator.MoveNextAsync 등 BCL 고속 데이터 경로에 ValueTask 활용
  • .NET 5부터 Task.FromResult가 true/false/0/-1 등 흔한 값을 내부 캐시하나 한정적

:pushpin: 이 서비스는 Singleton, Scoped, Transient 중 무엇이어야 하는가

주요 내용

  • ASP.NET Core의 DI 수명(Singleton/Scoped/Transient) 선택은 성능 최적화가 아닌 설계 결정의 영역
  • 가이드라인: 요청 단위 서비스는 Scoped, 안전하게 공유 가능한 경우만 Singleton, 작은 stateless 헬퍼는 Transient
  • Singleton이 Scoped 의존성을 가지는 lifetime mismatch 안티패턴
  • Singleton 캐시의 스레드 안전성 검증 필요성과 IMemoryCache 활용
  • BackgroundService에서 IServiceScopeFactory로 새 스코프를 생성하는 모범 사례

:bookmark_tabs: 가벼운 읽을거리

후보 항목 중 이슈로 선정되지 않은 가벼운 읽을거리들


거의 모든 .NET 코드베이스에서 보이는 DI 안티패턴 5가지

  • Service Locator, Captive Dependency, 모든 것을 Singleton, Constructor Over-Injection, new로 직접 생성
  • 각 안티패턴마다 호출자 은폐·수명 불일치·경합 조건 등 부작용과 대안 패턴 제시

전략 패턴 + ASP.NET Core keyed services

  • 하드코딩된 조건문 대신 인터페이스와 DI로 "무엇을 하는가"와 "어떻게 하는가"를 분리
  • ASP.NET Core의 keyed services와 GetRequiredKeyedService로 런타임 전략 선택 구현

.NET 개발자를 위한 실용적 모나드

  • Pyramid of Doom과 예외 기반 흐름 제어 문제를 Railroad-oriented programming으로 해소
  • Checked 모나드의 Unit/Map/Bind/Match 구현과 ASP.NET Core 미니멀 API에서 Match로 Ok/BadRequest 분기

WPF용 DevFlow — .NET 8+ 데스크톱 앱을 위한 현대적 도구

  • XAML Hot Reload/Live Visual Tree 같은 런타임 UI 도구를 IDE 외부로 개방하는 흐름을 WPF에 확장
  • LeXtudio WPF Labs는 GET /api/v1/ui/tree, /ui/screenshot, POST /ui/tap HTTP 엔드포인트로 러닝 앱 노출

Foundry 모델 라우터를 위한 평가(Evals) 실행 방법

  • 품질/비용/지연 3축으로 모델 라우터를 평가하는 오픈소스 프레임워크, LLM-as-judge + 편향 통제
  • run_eval.py --dataset ... --sample-size --resume 스크립트와 8개 차트 대시보드 제공

.NET LTS와 STS 버전의 지원 기간 정리

  • LTS와 STS의 품질은 동일하며 차이는 지원 기간뿐
  • .NET 9부터 STS 지원이 1.5년 → 2년으로 연장되어 인접 LTS/STS의 EoS 시점이 정렬

운영 환경에서 앱이 크래시했다 — 150만 행 임포트 회고

  • 2년간 무탈히 동작하던 금융 데이터 임포트 API가 150만 행 업로드로 메모리 고갈 크래시
  • 임포트 로직을 마이크로서비스로 분리·KEDA 오토스케일링·Azure Blob 스트리밍·SqlBulkCopy로 처리 시간 3~4일 → 3~4시간 단축
5 Likes

좋은 글이 많네요. :heart_eyes:

3 Likes

오우.. DevFlow 글에 MewUI도 언급되는 군요

2 Likes