주말 아침 - 주간 닷넷 #45

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


:pushpin: .NET 11 RC1 공개 — go-live 라이선스와 C# 15 확정

  • 저자: .NET 팀
  • 태그: #dotnet11 #csharp15 #releasenotes

주요 내용

  • go-live 지원 라이선스가 부여되어 프로덕션 사용이 가능하며 Visual Studio 2026 Insiders와 VS Code C# Dev Kit에서 지원
  • C# 15가 .NET 11 기본 언어 버전으로 확정되고 union 타입, closed class hierarchy, non-virtual static interface member, labeled break/continue, extension indexer가 정식화
  • 런타임은 Unix 인프로세스 크래시 리포터 확장과 s390x 지원을 추가했고, JIT가 x64 AVX10.1/F16C·Arm64 FP16 명령어로 System.Half 연산을 가속
  • 라이브러리는 Process 종료 시그널·상태 조회 API, 실험적 caller-driven TLS 세션, Linux DNS 레코드 조회, JSON의 새 숫자 타입과 union 직렬화를 추가
  • ASP.NET Core는 SignalR 인증 리프레시 API와 Blazor Server 회로 갱신을 추가했고, decimal/BigInteger↔부동소수점 변환 반올림 방식 변경에는 호환 스위치가 없음

:pushpin: .NET·.NET Framework 9월 서비싱 업데이트 — CVE 8건

  • 저자: Rahul Bhandari, Tara Overfield
  • 태그: dotnet #servicing #cve

주요 내용

  • .NET 10.0.12, 9.0.20, 8.0.31과 .NET Framework 4.6.2·4.7·4.7.1·4.7.2·4.8·4.8.1을 함께 다루는 통합 서비싱 릴리스
  • CVE-2026-69805, CVE-2026-69439, CVE-2026-71328, CVE-2026-69304, CVE-2026-58649, CVE-2026-66822, CVE-2026-69806이 .NET 10.0/9.0/8.0에 영향
  • CVE-2026-69522는 .NET과 .NET Framework 양쪽 모두에 영향을 미치는 공통 취약점
  • 개별 버그 수정 목록은 본문에 나열하지 않고 GitHub 릴리스 노트와 MSRC 링크로 안내

:pushpin: ASP.NET Core에서 C# 유니온과 닫힌 계층 사용하기

  • 저자: Dmitrii Korolev
  • 태그: #csharp15 aspnetcore #unions

주요 내용

  • discriminator 필드 없이 여러 타입 중 하나를 표현해야 하는 상황을 Kubernetes maxUnavailable(정수 또는 퍼센트 문자열) 사례로 제시
  • public union IntOrString(int, string); 문법으로 union을 선언하면 캐스팅 없는 대입이 가능하고 switch 패턴 매칭에서 컴파일러가 완전성을 검사
  • closed 한정자가 외부 어셈블리의 상속을 차단해 컴파일러가 파생 타입 집합을 고정된 것으로 처리하며, [JsonPolymorphic(InferClosedTypePolymorphism = true)]로 타입명 기반 $type discriminator를 자동 추론
  • Minimal API, MVC, SignalR, Blazor(JS interop·상태 유지), OpenAPI(anyOf 스키마)까지 통합
  • union은 쿼리스트링·라우트 값·헤더·폼 필드 바인딩이 불가능하고, discriminator 없는 구조적 분류는 페이로드 크기에 비례한 스캔 오버헤드가 발생

:pushpin: 배포하는 것을 테스트하라 — MSTest와 Native AOT

  • 저자: Amaury Levé
  • 태그: #mstest nativeaot #sourcegenerator

주요 내용

  • MSTest 4.4부터 테스트 프로젝트를 Native AOT 실행 파일로 게시·실행 가능
  • System.Text.Json 리플렉션 직렬화가 매니지드 모드에서는 통과하지만 Native AOT에서는 “reflection-based serialization has been disabled” 오류로 실패하는 사례로 문제를 제시
  • 소스 생성기가 빌드 시점에 테스트 클래스 레지스트리, 속성 메타데이터, 생성자·호출 델리게이트, 트리밍 호환 참조를 생성해 트리밍 이전에 테스트 존재 여부를 기록
  • MSTest.Sdk/4.4.0<PublishAot>true</PublishAot> 최소 설정, dotnet publish -r linux-x64 명령 제시
  • 상속만으로 [TestClass]인 클래스, 비공개·파일 로컬·정적·제네릭 테스트 클래스, ref/out/in 매개변수 제네릭 테스트, [AssemblyFixtureProvider]는 지원 대상에서 제외

:pushpin: Rider와 ReSharper는 왜 느리게 시작했나 — 범인은 Defender

https://blog.jetbrains.com/dotnet/2026/09/09/why-rider-and-resharper-were-slow-to-start-and-how-microsoft-helped-fix-the-problem/

  • 저자: Alexander Ulitin, Michael Hawker
  • 태그: rider #resharper #windowsdefender

주요 내용

  • ReSharper가 out-of-process 아키텍처로 전환된 후 Windows 최초 실행에 10~40초가 걸렸고 Microsoft 자체 도구는 1초 미만이었음
  • Microsoft Defender가 새로 실행된 프로세스를 스캔하는 방식이 원인이며, 파일이 쓰기 보호 디렉터리에 있으면 최적화 스캔(약 2초), 사용자 쓰기 가능 디렉터리에 있으면 전체 스캔(약 50초)이 적용됨
  • Defender의 ETW StreamScanRequestTask 이벤트와 PowerShell 모듈로 스캔 소요 시간을 측정해 원인을 규명
  • Microsoft가 Defender 1.449.454.0에서 해당 시나리오를 최적화해 쓰기 보호 디렉터리 설치 시 스캔 시간을 5초 미만으로 단축
  • JetBrains Toolbox 설치 시 Defender 제외 설정을 적용하면 최초 실행 시간이 약 절반으로 감소하며, 유사 문제 분석용 Defender Performance Tool을 GitHub에 공개

:pushpin: EF Core 스펙으로 Include·프로젝션·추적 제어하기

  • 저자: Nick Cosentino
  • 태그: #efcore #specificationpattern linq

주요 내용

  • 스펙이 불리언 조건을 넘어 쿼리 결과의 형태 자체를 규정한다는 관점을 제시하며 include는 조건자가 아니고, 추적은 성능 토글이 아니며, 프로젝션은 결과 계약을 바꾼다고 정리
  • Customer/Order 엔티티로 DTO 프로젝션과 엔티티 그래프 로딩을 대비
  • 필터링된 include에서 정렬·Skip·Take를 조합하는 방법과 AsTracking()/AsNoTracking()/AsNoTrackingWithIdentityResolution() 선택 기준
  • 단일 쿼리와 분할 쿼리의 조인 폭발 대 왕복 횟수 트레이드오프
  • ReaderCommandCounter 인터셉터로 SQLite에서 실제 실행되는 SQL 커맨드 수를 세는 xUnit 테스트로 검증

:pushpin: .NET 라이브러리의 공개 API를 생성하고 리뷰하기

  • 저자: Gérald Barré
  • 태그: dotnet #apidesign #tooling

주요 내용

  • Meziantou.Framework.PublicApiGenerator로 어셈블리의 공개 API를 C# 문법의 정규화된 스냅샷 파일로 생성
  • 기존 Microsoft.CodeAnalysis.PublicApiAnalyzers가 C# 문법이 아니고 멀티 타겟팅에 취약한 점을 지적
  • CLI 도구, MSBuild 통합(PublicApiGeneratorOutputPath), 라이브러리 직접 사용 세 가지 방식 제공
  • 멀티 타겟팅 시 #if NET10_0/#elif NET8_0 전처리기 지시문으로 프레임워크별 차이를 병합 출력
  • .NET 11/C# 15의 closed hierarchy와 union type 문법도 반영하며 --verify-no-change 플래그로 CI에서 의도치 않은 API 변경을 차단

:pushpin: .NET 9·10의 CoreCLR GC 핸들 변경

  • 저자: Austin Wise
  • 태그: dotnet10 #gc #interop

주요 내용

  • 기존 GCHandle 구조체를 대체하는 제네릭 타입 GCHandle<T>, WeakGCHandle<T>, PinnedGCHandle<T>GCHandleExtensions 도입
  • 타입 검사를 컴파일 타임으로 옮겨 AddrOfPinnedObject()를 비고정 핸들에 호출하는 오류를 컴파일 오류로 전환
  • 새 제네릭 핸들은 널 검사·타입 검증 없이 단순 역참조만 수행해 기존 대비 약 26% 빠른 벤치마크 결과
  • .NET 9에 추가된 Weak Interior Pointer Handle은 GC 이동 시 비관리 메모리의 보조 포인터를 갱신하며 collectable assembly의 정적 변수 관리에 사용됨
  • .NET 10에 추가된 Cross-Reference Handle은 MAUI Android에서 .NET 힙과 Java 힙 간 컬렉션을 강한 연결 요소 분석으로 조율

:pushpin: 누가 묻는가 — 아무것도 증명하지 않는 헬스체크

https://medium.com/@psatishkrishna/whos-asking-45f86d86faf5

주요 내용

  • Liveness probe에는 재시작으로 해결 가능한 항목만 포함해야 한다며 WorkerHeartbeat로 백그라운드 워커 생존을 확인하는 예시 제시
  • Readiness probe는 실제 사용하는 의존성을 검사해야 하며, 테이블명을 명시하지 않는 쿼리는 테이블이 삭제돼도 통과함
  • IHostedLifecycleService.StoppingAsync()로 Kestrel이 연결을 끊기 전에 readiness gate를 먼저 닫는 정상 종료 구현
  • 범용 CanConnectAsync() 체크와 도메인 특화 쿼리 체크를 대조
  • ReadinessGate, WorkerAliveCheck, QuotesApiCheck 구현 코드와 종료 시점별 연결 처리 타이밍 측정 결과 제시

:pushpin: 도메인 이벤트는 당신의 퍼블릭 API가 아니다

  • 저자: Derek Comartin
  • 태그: #eventdrivenarchitecture #domainevents #ddd

주요 내용

  • 도메인 이벤트를 외부에 그대로 노출하면 DB 스키마를 API로 직접 노출하는 것과 같은 결합이 생긴다고 지적
  • 데이터베이스 모델·도메인 모델·통합 이벤트를 서로 다른 세 모델로 구분
  • 물류 배차 예시에서 내부 이벤트(TruckReserved, CarrierAssigned, DispatchInstructionsSent, ETACalculated, ShipperNotified)를 퍼블릭 통합 이벤트 OrderDispatched 하나로 요약해 발행
  • TruckOrderNotUsedTruckOrderCanceledBeforeShipment처럼 비즈니스적으로 다른 상황을 구분해 인보이싱·차량관리·정산에 미치는 영향 차이를 설명
  • ID만 담아 동기 콜백을 유발하는 방식과 전체 엔티티 스냅샷 방식을 모두 피하고, 이벤트가 잦은 버전 변경을 겪으면 미발견 비즈니스 개념의 신호로 볼 것을 제안

:pushpin: C#으로 만든 오픈소스 텔레그램 서버, 4년 후

  • 저자: Aykut Alparslan Koc
  • 태그: csharp #mtproto #systemdesign

주요 내용

  • 4년 전 SocketAsyncEventArgs/IValueTaskSource/System.IO.Pipelines 기반 TCP 에코 서버에서 출발한 프로젝트가 현재 Ferrite로 발전
  • .NET 10 대상이며 텔레그램 API 레이어 214의 732개 메서드 중 494개를 디스패치하고, 패치된 공식 안드로이드·iOS 클라이언트로 로그인·메시지·그룹/채널·파일 전송·시크릿 챗·통화까지 검증
  • MTProto 메시지를 인증키 처리 → 컨테이너 언팩 → gzip 해제 → 서비스 메시지 소비 → 실행 엔진 전달의 처리 체인으로 구성해 연결 객체 코드 비대화를 방지
  • 자체 소스 생성기 Ferrite.TLParser로 TL 스키마에서 C# 리더·라이터를 생성하고, 일반·스트리밍·파일 요청을 별도 계약으로 분리해 공통 경로가 파일 스트림을 짊어지지 않게 설계
  • 분산 프로파일에서 Cassandra·Redis·MinIO·Elasticsearch·Kafka를 조합하되 MTProto 연결은 노드에 로컬로 유지한 채 envelope로 상태만 동기화

:bookmark_tabs: 가벼운 읽을거리

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


MSIX PackageVolume — 패키지 저장소가 동작하는 방식

  • MSIX 패키지가 스테이징되는 설치 저장소를 관리하는 Windows 추상화로, 시스템 드라이브 외에 이동식 미디어·네트워크 공유까지 지원
  • 패키지를 드라이브 문자 대신 저장 볼륨의 미디어 ID로 식별해 마운트 지점이 바뀌어도 일관되게 인식되며, 비-시스템 볼륨은 오프라인 접근을 통한 보호 우회를 막기 위해 암호화됨
  • AddPackageVolumeAsync(), FindPackageVolume(), SetDefaultPackageVolume() 등 API와 PowerShell 대응 명령 제공

더 나쁜 것이 더 낫다 — C# 대 F#

  • 같은 .NET 위에서 돌아가 빌드 도구·호스팅 변수를 배제하고 언어 자체만 비교할 수 있는 쌍이라는 전제에서 출발
  • F#이 기본적으로 순환 의존성을 허용하지 않아 아키텍처 건전성을 컴파일 타임에 강제한다는 점을 핵심 장점으로 제시
  • C# 쪽 장점으로 Razor 호환성, IDE 지원, 순환 복잡도·커버리지·뮤테이션 테스트 툴링을 인정

C#으로 PDF에서 표 추출을 시도했다 — 어디서 깨지는가

  • 텍스트 조각의 수직 정렬로 행을 묶고 헤더 위치로 열을 배정하는 2단계 기하학적 재구성 기법을 PdfPig으로 구현
  • 병합·스팬 셀, 여러 줄 래핑, 테두리 없는 불규칙 표, 페이지당 다중 표에서 재구성이 깨지는 실패 사례를 좌표 수준으로 기술
  • 스캔 문서는 OCR이 필요하나 C#에 대응 라이브러리가 없어 PP-OCRv5·ONNX Runtime 등 Python 기반 비전 모델로 넘어가야 한다고 결론

최종적 일관성 이해하기

  • 강한 일관성은 노드 간 조정 비용이 크며 쓰기 지연이 가장 느린 노드와 네트워크에 의해 제한됨을 설명
  • CAP 정리상 파티션 내성이 필수이므로 실제로는 일관성과 가용성 중 선택만 남는다고 정리하고 MongoDB·HBase(CP), Cassandra·DynamoDB(AP)를 예로 제시
  • Cassandra QUORUM/ONE, DynamoDB 강한 일관성 읽기(비용 2배), MongoDB writeConcern 등 연산별 조정 옵션과, 금융 거래·재고 예약·토큰 무효화처럼 즉시 일관성이 필요한 예외를 구분

유니온 타입? 아니, 판별 유니온

  • C# 유니온 타입을 JSON으로 역직렬화할 때 “JSON value type ‘Object’ is ambiguous for union type” 오류가 나는 원인은 케이스 타입을 식별할 디스크리미네이터가 없기 때문
  • [DiscriminatedUnion] 속성과 IIncrementalGenerator 기반 소스 생성기로 생성자·디스크리미네이터 enum·커스텀 JsonConverter<T>를 자동 생성
  • 생성된 컨버터가 $discriminator 필드로 케이스를 구분해 역직렬화 모호성을 해소

Ardalis.Specification과 EF Core — 버전 핀과 캐시의 함정

  • 패키지 9.3.1이 net10.0을 직접 타겟하지 않고 net8.0/net9.0만 지원한다는 버전 호환성 경고
  • DbSet.ToListAsync(spec)WithSpecification().ToListAsync()의 후처리 적용 여부가 다르고, EnableCache/WithCacheKey는 메타데이터일 뿐 실제 캐싱을 수행하지 않음
  • WithSpecification 직접 사용, 커스텀 SpecificationEvaluator, RepositoryBase<T> 상속 세 실행 경로를 비교하고 SQLite xUnit 스모크 테스트로 검증

Meziantou.Framework.TemporaryContainers — 테스트용 임시 컨테이너

  • Docker Engine API, Docker/Podman CLI, macOS container, Windows wslc 등 5가지 컨테이너 런타임을 추상화하고 ContainerRuntime.Auto가 OS별 우선순위로 자동 탐지
  • Apple container의 사전 포트 할당, wslc의 재시작 미지원·볼륨 명령 부재 같은 런타임별 차이를 라이브러리가 흡수
  • ReuseId로 테스트 실행 간 컨테이너를 재사용하고 Redis·PostgreSQL·MongoDB·SQL Server용 사전 구성 헬퍼와 GetConnectionString() 제공
4개의 좋아요