주말 아침 - 주간 닷넷 #34


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


:pushpin: .NET 11 Preview 3 — StringBuilder.MoveChunks로 ToString 할당 회피

  • 저자: Andrew Lock
  • 태그: #dotnet11 #performance #roslyn

주요 내용

  • 새 static 메서드 MoveChunks는 한 StringBuilder의 내부 버퍼를 다른 인스턴스로 이전하고 원본을 비움
  • char[] 청크 참조만 이동시켜 실제 데이터 복사 없이 동작 — 큰 문자열에서 ToString보다 훨씬 빠름
  • 주요 용도는 Roslyn 소스 생성기에서 SourceText를 만들 때 발생하는 중간 문자열 할당 제거
  • 단순 재할당과 달리 기존 참조 보유자는 불변 스냅샷을 유지해 다운스트림 변조 차단
  • .NET 11+ 필요. 소스 제너레이터는 netstandard2.0 타겟이라 호환성 작업이 선행돼야 함

:pushpin: Unity와 부동소수점 — Mono의 float→double 캐스팅이 만든 70배 성능 격차

  • 저자: Aras Pranckevičius
  • 태그: #unity #floatingpoint #mono

주요 내용

  • Unity Mono 런타임이 32비트 float를 내부적으로 double로 변환해 계산하는 역사적 결함
  • UnityEngine.Mathf vs System.MathF vs Unity.Mathematics의 어셈블리 수준 코드 생성 차이
  • 10M 반복 sqrt 벤치: Mathf 282ms / MathF 186ms / Burst+Mathematics 35ms
  • Burst는 System.MathF를 지원하지 않아 백엔드별 최적 선택지가 달라지는 파편화
  • 향후 CoreCLR 마이그레이션이 가져올 개선 전망

:pushpin: SQL Server에서 UUID v7의 진짜 한계 — 바이트 정렬 심층 분석

https://blog.stackademic.com/uuid-v7-in-sql-server-can-you-really-use-it-the-limitation-nobody-tells-you-about-deep-dive-428b3877ebff

  • 저자: Pankaj Ikhar
  • 태그: #sqlserver #uuidv7 #indexing

주요 내용

  • SQL Server의 uniqueidentifier 정렬은 바이트 10-15부터, 그다음 8-9, 마지막에 0-5 순으로 비교
  • UUID v7은 48비트 타임스탬프를 바이트 0-5에 두므로 SQL Server 인덱스에서는 v4와 거의 동일한 랜덤 패턴
  • SQL Server에서는 PostgreSQL/MySQL과 달리 v7 이점이 사라지며 단편화 발생
  • 실제 사례에서 Guid.CreateVersion7()로 전환 후 인서트 시간이 20분에서 수 시간으로 증가
  • 권장: NEWSEQUENTIALID() + 별도 CreatedAt 컬럼, SQL Server 2025도 네이티브 v7 미지원

:pushpin: .NET의 Outbox 패턴 — PostgreSQL + RabbitMQ + MassTransit 구현

  • 저자: Hamed Salameh
  • 태그: dotnet #messaging #outboxpattern

주요 내용

  • DB와 메시지 브로커 간 원자적 트랜잭션 부재로 인한 메시지 유실 문제 정의
  • 비즈니스 상태와 outbox 레코드를 단일 트랜잭션에 기록, 별도 디스패처가 비동기 발행
  • at-least-once 전달을 보장. 순서 보장은 분리된 관심사로 명시
  • .NET 10, PostgreSQL, RabbitMQ, MassTransit 조합으로 FOR UPDATE SKIP LOCKED 폴링 디스패처 구현
  • 포이즌 메시지 처리(5회 실패 후 중단), 행 잠금 기반 수평 확장, 처리완료 메시지 정리, lag/dead 메트릭 관측 등 운영 고려사항

:pushpin: 메시지 펌프는 트랜잭션 디테일에서 실패한다

  • 저자: Daniel Marbach
  • 태그: #messaging #nservicebus #transaction

주요 내용

  • 메시지 펌프는 브로커 ack, DB write, outgoing send 조율 과정에서 안전성과 처리량의 트레이드오프 발생
  • RabbitMQ, Azure Storage Queues 등 receive-only 트랜잭션: ack가 애플리케이션 효과와 분리되어 중복 처리 또는 ghost message 위험
  • 클라우드 큐의 visibility timeout 갱신 책임을 펌프가 떠안게 되며 복구 속도와 중복 처리 간 균형 필요
  • read-deserialize-dispatch 루프가 logging, retry, DI, middleware 등으로 확장되며 플랫폼화
  • 자체 구축 vs 채택 결정 — 프로덕션급 메시지 펌프는 NServiceBus 같은 기성 솔루션이 합리적

:pushpin: 저장된 JSON을 마이그레이션하지 않고 C# 타입 진화시키기

  • 저자: Egil Hansen
  • 태그: #json #systemtextjson #migration

주요 내용

  • Egil.SystemTextJson.Migration 라이브러리: 역직렬화 시점에 자동 타입 마이그레이션 수행
  • [JsonMigratable] 어트리뷰트와 IMigrateFrom<TFrom, TTo> 인터페이스로 타입 진화 선언
  • 사용 사례: 저장 데이터, 컨트랙트 라이브러리, 블루/그린 배포, 버전 관용 API
  • 빅뱅 DB 마이그레이션, 수동 read-side 변환, 커스텀 JsonConverter, 버전 엔드포인트와의 비교
  • 브라운필드 도입 경로와 성능 트레이드오프

:pushpin: 줄바꿈은 \r\n만이 아니다 — 유니코드 줄바꿈 처리

  • 저자: Gérald Barré (meziantou)
  • 태그: dotnet #strings #unicode

주요 내용

  • 유니코드는 U+0085, U+2028, U+2029도 줄바꿈으로 인식
  • Regex.Split + (?>\r\n|[\n\v\f\r… ]) 패턴 사용
  • string.ReplaceLineEndings() 활용
  • .NET 9의 Regex.EnumerateSplits()로 할당 최소화
  • Meziantou.Framework SplitLines()ReadOnlySpan<char> 활용

:pushpin: 리라이트 없이 레거시 .NET 애플리케이션 현대화하기

https://medium.com/@adrianbailador/modernising-legacy-net-applications-without-a-rewrite-2ae93b623214

  • 저자: Adrian Bailador
  • 태그: dotnet #legacy #modernization

주요 내용

  • .NET Upgrade Assistant로 .NET Framework에서 .NET 9 LTS로 점진적 이동, Testcontainers 기반 characterization 테스트로 동작 검증
  • EF6 EDMX에서 EF Core Code First로 스키마/저장 프로시저 매핑 유지하며 마이그레이션
  • 정적 서비스 로케이터를 ASP.NET Core 생성자 주입 DI로 교체, 동기 I/O를 async 전체 스택으로 변환
  • Serilog 구조화 로깅, Polly v8 resilience pipeline(retry/circuit breaker/timeout) 적용, liveness/readiness 헬스 체크 분리
  • Strangler fig 패턴과 anti-corruption layer로 새 엔드포인트를 레거시와 병행 운영하며 feature flag로 점진 전환

:pushpin: 듀얼 모드 Windows GUI/콘솔 애플리케이션 만들기

주요 내용

  • Windows PE 헤더는 WinExe 혹은 Exe 둘 중 하나만 가능해 동시 동작이 까다로움
  • 방법 1: GUI 앱이 AttachConsole/FreeConsole P/Invoke로 콘솔에 연결 — GUI가 즉시 프롬프트로 복귀하며 출력이 어색하게 끊김
  • 방법 2: 콘솔 앱이 GUI를 띄우는 방식 — 항상 콘솔 창이 잠깐 표시되고 깜빡임 발생
  • 방법 3: GUI/CLI를 별도 실행파일로 분리하고 비즈니스 로직은 공유 라이브러리로 — 빌드는 복잡해도 가장 깔끔
  • Strahl의 결론은 실용성/유지보수성 측면에서 별도 실행파일 분리가 최선

:pushpin: EF Core in Clean Architecture, 실용적 접근

  • 저자: Anton Martyniuk
  • 태그: #efcore #cleanarchitecture #ddd

주요 내용

  • 전통적 Repository 래핑은 "추상화 위 추상화"로 메서드 폭발과 책임 모호화를 초래
  • Application 계층이 Microsoft.EntityFrameworkCore/DbContext에 직접 의존하되 Domain은 프레임워크 무관 유지
  • “DB 교체” 명분은 실제로 거의 발생하지 않으며 발생해도 Repository로 보호되지 않음
  • 모킹 대신 Testcontainers + 실제 DB(PostgreSQL/SQL Server)로 쿼리 변환 이슈를 조기 포착
  • 재사용은 IQueryable<T> 확장 메서드나 Specification 패턴으로 가볍게 처리

:pushpin: VS Code가 5만 번 평가에서 배운 것 — 모델별 토큰 사용량 70배 차이

  • 저자: VS Code Eval Team
  • 태그: #vscode llm #eval

주요 내용

  • 30개 모델에 대해 동일한 smoke test(say_hello, 5글자 파일 생성)를 6개월간 50,974회 실행
  • 동일 결과를 내면서도 모델별 출력 토큰 사용량이 55 ~ 3,676 토큰으로 약 70배 차이
  • 오버헤드 패턴 분류: 과도한 계획(52~99%), 워크스페이스 탐색(56~96%), 장황한 narration, 잘못된 도구 선택, API 대신 터미널 명령 사용
  • 모델 크기와 효율성 무관 — mini 모델이 오버헤드 1위인 반면 큰 모델이 절제된 토큰 사용을 보임
  • 권장: pass rate만이 아닌 도구 호출 시퀀스를 캡처하는 가벼운 baseline eval 운영

:bookmark_tabs: 가벼운 읽을거리

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


내가 본 가장 비싼 C# 4줄 — 빈 catch 블록이 만든 11개월 은폐 장애

  • 결제 재시도 핸들러의 빈 catch 블록이 인증서 회전 실패를 11개월간 은폐, 6시간 다운타임과 38,000건 주문 손실
  • throw ex;는 스택 트레이스를 리셋하므로 throw;를 써야 하고, 광범위 예외 처리는 모니터링이 장애를 감지하지 못하게 만듦

LINQ의 All·Any·Contains — 도메인 진실을 표현하는 방법

  • Any()는 존재성 + 단락 평가, All()은 빈 컬렉션에서 true를 반환하는 “공허한 참” 함정 (KYC 사례)
  • Contains()의 HashSet O(1) vs List O(n) 차이와 커스텀 IEqualityComparer로 도메인 동등성 정의

Microsoft.UI.Reactor로 파일 기반 WinUI 앱 만들기

  • 단일 App.cs 파일에 #: 디렉티브로 의존성·SDK·타겟 프레임워크를 선언해 프로젝트 파일 없이 동작
  • 콘솔 모드로 기본 동작하고 필요 시 네이티브 WinUI 창을 띄우는 하이브리드 구조, 유틸리티·프로토타입에 적합

sqlite-utils 4.0rc1 — 마이그레이션과 중첩 트랜잭션

  • 데코레이터 함수로 스키마 변경을 정의하고 CLI/Python API로 실행하는 마이그레이션 시스템 통합
  • db.atomic() 컨텍스트 매니저로 SQLite savepoint 기반 중첩 트랜잭션, INSERT ... ON CONFLICT SET 표준 upsert 채택

CancellationToken Async/Await 완전 가이드

  • 협력적 취소 모델, 모든 await에 토큰 전파, OperationCanceledException을 일반 예외와 분리 처리
  • CancellationTokenSource 해제와 CreateLinkedTokenSource로 타임아웃/요청중단/앱종료 통합

Durable Functions 오케스트레이션과 체이닝 패턴 입문

  • Durable Functions가 상태·Correlation ID·히스토리 테이블을 자동 관리, Orchestrator/Activity/Client 역할 분리
  • 결정론 요구사항(DateTime.UtcNow 대신 context.CurrentUtcDateTime)과 리플레이/크래시 복구 메커니즘

EF Core 단위 테스트 — InMemory vs SQLite 비교

  • UseInMemoryDatabase는 FK/UNIQUE/트랜잭션을 강제하지 않아 리포지토리 테스트에 부적합, SQLite in-memory가 실제 SQL 엔진으로 더 적합
  • SQLite는 마지막 커넥션이 닫히면 DB가 소멸되므로 커넥션 유지 필요, IDbContextFactory<T>로 작업별 컨텍스트 생성해 ChangeTracker 오염 회피

VS 2026 Theme colors — Fluent 색상 토큰 커스터마이즈

  • VS 2026 6월 업데이트에서 Fluent 색상 토큰을 IDE 내장 옵션 페이지로 직접 오버라이드 가능
  • 테마별 커스터마이즈가 개별 보존되고 토큰 단위 reset 지원, Classic Blue 재현용 JSON 설정 예시 제공

MSIX 배포 — Add vs Stage+Register 차이

  • Windows 패키지 설치는 staging(머신 단위, 디스크 배치)과 registration(사용자 단위, 프로파일 통합) 두 단계로 분리
  • Add Package는 두 작업을 한 번에 처리, 업데이트 사전 스테이징이나 원본 없이 재등록할 때 분리 호출 사용
4 Likes