주말 아침 - 주간 닷넷 #29


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


:pushpin: C# 메모리 안전성 개선

  • 저자: Richard Lander
  • 태그: csharp #memorysafety #unsafe

주요 내용

  • C# 16에서 unsafe 키워드를 스코프 표시에서 호출자 의무를 명시하는 계약(contract) 시스템으로 재설계
  • /// <safety> 문서 블록 의무화, 타입 레벨 unsafe 제거, extern 선언에 새로운 safe 키워드 도입
  • Rust의 unsafe fn/unsafe {} 모델 차용, Swift의 암시적 전파 방식과는 차별화
  • 프로젝트 속성 두 개(opt-in + AllowUnsafeBlocks)로 게이트, 기본값은 unsafe 전면 차단
  • .NET 11 프리뷰 도입 → .NET 12 정식, dotnet format 마이그레이션 픽서 제공 예정

:pushpin: .NET 11 Preview 1: Web Workers로 Blazor에서 백그라운드 작업 실행하기

  • 저자: Andrew Lock
  • 태그: #dotnet11 blazor #webworkers

주요 내용

  • JavaScript 단일 스레드 이벤트 루프로 인한 UI 멈춤을 Web Workers의 멀티스레딩으로 해소
  • .NET 11 SDK가 제공하는 webworker 템플릿과 [JSExport] 데코레이션 기반 구현 절차
  • 워커마다 자체 .NET 런타임 인스턴스 초기화 비용이 크므로 워커 참조 캐싱 필요
  • dotnet-web-worker-client.js(메인)와 dotnet-web-worker.js(워커)의 메시지 패싱 아키텍처
  • 반환 타입은 원시 타입/문자열로 제한, 복합 객체는 JSON 직렬화로 전달

:pushpin: .NET 11의 Zstandard 압축

  • 저자: Steven Giesel
  • 태그: #dotnet11 #compression #zstd

주요 내용

  • .NET 11에 ZstandardStream, ZstandardEncoder/Decoder, ZstandardDictionary, ZstandardCompressionOptions 추가
  • DEFLATE 수준 압축률에 더 빠른 압축/해제 속도, 품질 레벨 -∞ ~ 22(기본 3)
  • GZipStream과 유사한 API 패턴
  • ASP.NET Core 응답 압축에 x.Providers.Add<ZstandardCompressionProvider>() 한 줄로 통합
  • Dictionary 압축이 100KB 내외 유사 페이로드(JSON 등)에서 압축률 향상

:pushpin: .NET 11에 드디어 들어온 유니온 타입

  • 저자: Andrew Lock
  • 태그: #dotnet11 csharp #uniontypes

주요 내용

  • C# 15(.NET 11 preview 2)의 union 키워드 도입: public union SupportedOS(Windows, Linux, MacOS);
  • 생성자/암시적 변환으로 인스턴스 생성, switch 식의 자동 타입 추출과 컴파일러의 망라성 검사
  • 구현 메커니즘: [Union] 어트리뷰트가 붙은 struct, IUnion 인터페이스의 object? Value
  • 값 타입 박싱 회피를 위한 TryGetValue() 오버로드 패턴
  • 사용 요건: .NET 11 SDK preview 2+, <LangVersion>preview</LangVersion>

:pushpin: .NET 10 리플렉션 성능: 벤치마크, 캐싱, 델리게이트

  • 저자: Nick Cosentino
  • 태그: dotnet10 #reflection #performance

주요 내용

  • 비싼 작업(GetProperty/Invoke)과 빠른 작업(캐시된 PropertyInfo, 컴파일된 델리게이트) 구분
  • 네 가지 최적화 전략: 정적 딕셔너리 캐싱, FrozenDictionary, Expression.Lambda().Compile(), [UnsafeAccessor]
  • 제네릭 멀티타입 캐시와 컴파일된 getter/setter, BenchmarkDotNet 측정 코드 포함
  • 호출 빈도와 컨텍스트에 따른 단계적 적용, AOT 시나리오에서는 소스 제너레이터 우위

:pushpin: ReadOnlySpan<T>로 .NET Framework의 byte 할당 제거하기

  • 저자: Andrew Lock
  • 태그: #performance #readonlyspan #allocation

주요 내용

  • C# 컴파일러가 상수 byte 배열 데이터를 어셈블리 메타데이터에 직접 임베드해 힙 할당을 제거하는 메커니즘
  • byte[], sbyte[], bool[]만 zero-allocation 최적화 대상이며 다른 타입은 .NET 7+ 런타임 지원 필요
  • 배열에 비상수 값이 섞이면 매 접근마다 전체 할당이 발생
  • Collection expressions는 static 프로퍼티에서는 컴파일 타임 보호를 제공하나 로컬 변수에서는 제공하지 않음
  • 런타임 변경 없이 컴파일러 기능만으로 동작해 .NET Framework 포함 모든 버전에서 사용 가능

:pushpin: dotnet run Hello.cs — .NET 10에서 C#이 맞이한 파이썬 같은 순간

https://medium.com/@Rajdip27/dotnet-run-hello-cs-c-finally-got-its-python-moment-in-net-10-c7486fcf4bb2

주요 내용

  • .NET 10의 파일 기반 앱 기능: 단일 .cs 파일을 dotnet run app.cs로 실행
  • CLI가 가상 프로젝트를 자동 생성/컴파일, #: 디렉티브로 NuGet 패키지·SDK·빌드 속성을 파일 내부에 선언
  • 명시적 Main 없는 암묵적 진입점, async/레코드 등 모던 C# 기능 지원
  • dotnet project convert 명령으로 스크립트를 정식 프로젝트로 승격
  • Console, Minimal API, CSV/JSON 처리 등 실행 예제 포함

:pushpin: 절반만 성공한 사용 사례 — .NET에서 부분 실패 설계하기

  • 저자: Milan Jovanović
  • 태그: dotnet #resilience #outbox

주요 내용

  • 결제/DB/이메일 다단계 작업에서의 부분 실패 문제 정의
  • 부작용 3분류: 트랜잭션 내 / 외부+가역 / 외부+비가역
  • 트랜잭션은 마지막에 커밋, 비가역 작업은 Outbox로 이벤트화, 외부 호출은 Idempotency Key 또는 보상 이벤트
  • 멱등성 키 활용 결제 호출과 PaymentFailedEvent 기반 보상 워커 예제
  • Use case와 Saga의 경계 기준 제시

:pushpin: Uber 여행 라이프사이클로 보는 .NET 상태 머신과 EF Core

https://medium.com/@adrianbailador/state-machines-in-net-modelling-ubers-trip-lifecycle-with-ef-core-100e94■■6e4c

  • 저자: Adrian Bailador
  • 태그: #statemachine #efcore #ddd

주요 내용

  • Requested → DriverAssigned → InProgress → Completed/Cancelled 상태 전이 모델링
  • 허용 전이 테이블과 Validate 메서드로 잘못된 전환 차단
  • EF Core의 RowVersion(낙관적 동시성)으로 동시 상태 변경 방지
  • Aggregate Root에서만 상태 변경 허용, 도메인 이벤트(TripCompleted, DriverAssigned) 발행
  • 안티패턴(공개 상태 필드, 분산된 검증, SaveChanges 전 이벤트 발행, ConcurrencyException 무시)과 백그라운드 타임아웃 처리

:pushpin: 실패에서 배운 복식부기 장부 시스템 설계

https://medium.com/@kamoellenkganakga/double-entry-ledger-system-every-decision-came-from-a-failure-357206849e5a

  • 저자: Kamogelo Ellen Kganakga
  • 태그: dotnet #ledger #postgresql

주요 내용

  • .NET 8 + PostgreSQL 16 기반 복식부기 장부 시스템 설계 회고
  • NUMERIC(19,4)로 부동소수점 오차 방지, SELECT FOR UPDATE + UUID 정렬 잠금으로 데드락 방지
  • Idempotency-Key 헤더로 재시도 안전성 확보, balance_after 스냅샷으로 시계열 조회 성능 확보
  • Clean Architecture 계층 분리, BCrypt + JWT(15분) + refresh token 재사용 감지
  • 100달러 잔액에 10개 병렬 출금 → 정확히 5건만 성공하는 동시성 테스트로 실증

:pushpin: FusionCache — 캐시에 필요한 것은 TTL이 아니라 복원력 전략

https://medium.com/@serhatserttas13/fusioncache-why-your-cache-needs-a-resiliency-strategy-not-just-a-ttl-b13b14605be2

  • 저자: Serhat Serttaş
  • 태그: #fusioncache #caching #resilience

주요 내용

  • L1(메모리) + L2(Redis) + Backplane 구성과 Cache Stampede 보호 메커니즘
  • Fail-Safe(데이터 소스 장애 시 만료 데이터로 폴백), Soft/Hard 타임아웃, Eager Refresh(TTL 90% 시점 백그라운드 갱신)
  • Tag 기반 무효화와 Backplane을 통한 다중 노드 L1 동기화
  • 50M req/day 시나리오에 대한 L1 1h / L2 24h / FailSafe 48h 권장 계층
  • 약한 일관성, 메모리 중복, 복잡성 등 트레이드오프와 .NET 9 HybridCache 호환

:bookmark_tabs: 가벼운 읽을거리

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


2015년처럼 appsettings.json 읽지 말자 — .NET의 Options 패턴

  • IOptions<T>(싱글톤), IOptionsSnapshot<T>(요청 스코프), IOptionsMonitor<T>(반응형)의 차이와 선택 기준
  • [Required]/[Range]/[Url] 데이터 어노테이션과 ValidateOnStart()를 통한 시작 시점 검증

HttpClient 실수가 프로덕션을 조용히 망가뜨린다

  • new HttpClient() 반복 생성 시 TCP 소켓 TIME_WAIT 잔존으로 소켓 고갈, 정적 HttpClient는 DNS 캐싱으로 구식 IP 문제
  • IHttpClientFactory의 명명/타입 기반(typed) 클라이언트 등록 패턴

List<T>가 스레드 안전하지 않다는 신호 — 오해하기 쉬운 IndexOutOfRangeException

  • List<T>.Enumerator.MoveNext()에서의 IndexOutOfRangeException은 동시 수정의 신호, 버전 체크가 인덱스 접근 이후라 원시 예외가 먼저 노출
  • 해결책: System.Collections.Concurrent 타입, 모든 접근에 락, 스냅샷 후 순회

.NET에서 Dapper 마이크로 ORM을 마스터하는 완벽 가이드

  • QueryAsync<T>, QueryFirstOrDefaultAsync<T>, ExecuteAsync, QueryMultipleAsync 등 핵심 API
  • 트랜잭션, 저장 프로시저, 벌크 작업, JOIN 다중 매핑까지 다루는 입문~중급 종합 가이드

.NET 현대화 Part 24: Prometheus와 Grafana를 활용한 현대적 모니터링

  • OpenTelemetry + Prometheus + Grafana 메트릭/모니터링 파이프라인 구성
  • AddOpenTelemetry, AddAspNetCoreInstrumentation 설정과 Prometheus scraping 엔드포인트, Grafana 연동 흐름

Windows Runtime에서 Win32 구조체를 사용하는 방법

  • WinRT가 raw 포인터를 노출하지 않아 Win32Point { Int32 X; Int32 Y; } 같은 "shadow struct"로 메모리 레이아웃을 미러링
  • PROPERTYKEY처럼 WinRT가 별도 표현을 쓰는 경우 PSPropertyKeyFromString 같은 변환 함수로 우회
4개의 좋아요