주말 아침 - 주간 닷넷 #40

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


:pushpin: .NET 11 Performance Edition

  • 저자: Steven Giesel
  • 태그: #dotnet11 #performance #benchmarkdotnet

주요 내용

  • Enum.Equals의 박싱 제거(제네릭 메서드 활용)로 8.39ns→1.21ns, 약 86% 성능 향상
  • Vector/Span 관련 불필요한 경계 검사 제거로 약 10% 속도 개선 및 코드 크기 감소
  • TimeZone 연간 규칙 캐싱으로 DateTime.Now가 34.54ns→17.93ns로 약 50% 단축
  • Guid 파싱의 DecodeByte 로직 개선으로 모든 파싱 메서드가 약 20~25% 빨라짐
  • LINQ Min/Max에 SIMD 벡터 셔플 기법 적용, byte/short 연산 최대 75% 개선

:pushpin: Task.WhenAll 잘못 쓰지 마세요 — 5만 개 작업에서 16개 워커로

https://medium.com/@vikpoca/stop-using-task-whenall-like-this-15f710ee38a6

  • 저자: Viktor Ponamarev
  • 태그: dotnet async #taskwhenall

주요 내용

  • Task.WhenAll이 완료 집계만 담당하고 동시성 제어는 하지 않아 5만 개 요청이 그대로 동시 실행되는 문제
  • Parallel.ForEachAsync(MaxDegreeOfParallelism=16)로 균일 작업을 제한하는 방법
  • Channel.CreateBounded(용량 100) 기반 생산자-소비자 파이프라인으로 백프레셔 처리
  • _peakInFlight 카운터로 동시 진행 작업 수를 진단하는 코드
  • 캐시 조회/HTTP 요청/SQL 업데이트별로 부하가 다르므로 직접 측정을 권장

:pushpin: 탈취를 스스로 제한하는 로테이팅 리프레시 토큰과 재사용 탐지

https://medium.com/@ivanball76/one-rotating-refresh-token-and-reuse-detection-that-makes-theft-self-limiting-fab42234a04a

  • 저자: Ivan Ball-llovera
  • 태그: jwt #refreshtoken #security

주요 내용

  • 폐기 불가능한 상태비저장 JWT는 단명해야 하고 이것이 리프레시 토큰을 필요로 한다는 상충 관계
  • 사용자당 서버 저장 리프레시 토큰(64바이트 난수)을 매 로그인/갱신마다 로테이션하는 설계
  • 저장된 토큰과 요청 토큰 불일치 시 전체 세션을 무효화하는 재사용 탐지 코드
  • 단일 활성 세션, 경쟁조건으로 인한 오탐, 매 갱신마다 DB 쓰기 필요 등 4가지 트레이드오프 명시
  • 액세스 토큰 단명화부터 불일치 시 즉시 폐기까지 5단계 적용 절차

:pushpin: C#으로 데이터셋 분산팽창계수(VIF) 계산하기

https://visualstudiomagazine.com/articles/2026/08/03/computing-dataset-variance-inflation-factor-vif-using-csharp.aspx

  • 저자: James McCaffrey
  • 태그: csharp #machinelearning #statistics

주요 내용

  • 다중공선성(multicollinearity) 문제와 이를 탐지하는 VIF 지표의 수학적 정의
  • VIF = 1.0 / (1.0 - R2) 공식과 각 컬럼을 종속변수로 삼아 선형회귀로 R2를 구하는 절차
  • R2(결정계수) 계산 방식을 SSres/SStot 기반 예시 수치로 단계별 시연
  • VarInfFactor(), LinearRegressor 클래스 등 C# 콘솔 앱 전체 구현 코드
  • 정상 데이터셋(VIF 1~1.5대)과 상관관계 주입 데이터셋(VIF 수백만대) 비교 결과

:pushpin: 모던 C# URI 처리 — 절대/상대/쿼리/프래그먼트 URL

주요 내용

  • UriKind.RelativeOrAbsolute로 구성 후 IsAbsoluteUri 확인이 필요한 이유와 Query/Fragment가 상대 URI에서 예외를 던지는 문제
  • Scheme, Authority, AbsolutePath, Query, Fragment, PathAndQuery 등 URI 구성 요소 정리
  • Uri(baseUri, relativeUri) 생성자를 이용한 상대→절대 변환과 베이스 URI 후행 슬래시의 영향
  • MakeRelativeUri()를 이용한 절대→상대 변환과 스킴/호스트 불일치 시 원본이 그대로 반환되는 함정
  • Uri.TryCreate()와 nullable 반환 확장 메서드로 예외 없는 처리 제안

:pushpin: Fetch Metadata HTTP 헤더 이해하기 — Sec-Fetch-Site와 그 동료들

  • 저자: Andrew Lock
  • 태그: aspnetcore #security #httpheaders

주요 내용

  • Sec-Fetch-Dest, Sec-Fetch-Site, Sec-Fetch-Mode, Sec-Fetch-User 네 가지 헤더의 의미와 값 체계
  • Sec-Fetch-Site의 cross-site/same-origin/same-site/none 네 값의 판정 기준(스킴·도메인·포트·서브도메인)
  • 주소창 직접 입력, fetch() 호출, 이미지 로드 등 실제 요청 시나리오별 헤더 값 예시
  • .NET 11에서 이 헤더들을 CSRF 방어의 추가 계층으로 활용할 예정이라는 예고와 “Resource Isolation Policy” 구성 방향

:pushpin: 모듈을 gRPC 서비스로 추출하기, 라이브로

https://medium.com/@ivanball76/extracting-a-module-to-a-grpc-service-live-799926cf8a32

  • 저자: Ivan Ball-llovera
  • 태그: #grpc #microservices dotnet #architecture

주요 내용

  • "전송 방식은 경계에서만 결정되어야 한다"는 원칙 아래 IMessageBus 인터페이스로 모듈 간 통신 추상화
  • .Contracts 프로젝트에서 proto 파일을 자동 컴파일하고 gRPC 어댑터로 기존 인터페이스 재사용
  • JWT 전달과 Polly 복원력 파이프라인을 gRPC 통신 계층에 통합
  • 설정 변경만으로 InProcessMessageBus에서 RabbitMQ/Azure Service Bus 기반 BrokerMessageBus로 전환
  • 여러 배포 단위, 게이트웨이, 서비스 디스커버리 등 운영 복잡성 증가를 함께 지적

:pushpin: 오래된 Windows 로케일에 발목 잡히지 않는 법

  • 저자: Jeremy Davis
  • 태그: windows #locale #dateparsing

주요 내용

  • 로컬 Windows 11에서는 정상 동작하지만 프로덕션 Windows Server 2019에서 “September” 날짜 파싱이 일관되게 실패
  • 2021년 ISO/CLDR 표준 변경으로 “Sep"→"Sept” 축약형이 바뀌었으나 구버전 Windows에는 미반영돼 OS 간 로케일 데이터 불일치 발생
  • PowerShell로 두 OS의 AbbreviatedMonthNames 배열을 비교해 원인 진단
  • 런타임에 커스텀 CultureInfo를 생성·수정해 두 OS 모두에서 파싱 가능하도록 대응

:pushpin: Duende를 OIDC 서버로 사용해 ASP.NET Core Identity 애플리케이션에 SAML을 외부 Provider로 구현하기

  • 저자: damienbod
  • 태그: #saml #oidc #duendeidentityserver

주요 내용

  • 웹 클라이언트(OIDC만 인지)–Duende IdentityServer(OIDC 게이트웨이)–Entra ID(SAML)로 이어지는 3계층 아키텍처
  • Sustainsys.Saml2.AspNetCore2 패키지를 이용한 SAML 클라이언트 설정과 Entra ID Enterprise Application 메타데이터 매칭
  • LoadIdentityProviderMetadata 헬퍼로 Entra ID federation 메타데이터를 로드하는 방법
  • Challenge 요청 시작부터 /ExternalLogin/Callback 리다이렉트까지 인증 흐름
  • OIDC 사용 불가 시에만 SAML을 대체 수단으로 써야 한다는 제약 조건

:pushpin: Docker Desktop 없이 .NET 10 스택 실행하기 — WSLC와 Compose

https://medium.com/@yovannydjrod/wslc-meets-compose-running-a-full-net-10-stack-without-docker-desktop-3a4283283bf7

  • 저자: Yovanny Rodríguez
  • 태그: dotnet10 #wsl #docker

주요 내용

  • Microsoft WSL Container(WSLC)에 Compose 네이티브 지원이 없는 한계를 오픈소스 wslc-compose로 보완
  • .NET 10 ASP.NET Core API, PostgreSQL 17, Redis 7.4로 구성된 3-서비스 스택을 compose.yaml과 Multi-stage Dockerfile로 구현
  • EF Core DbContext, Redis 캐시 흐름(캐시 미스 시 DB 조회 후 10분 TTL 저장), 환경변수 분리 처리 등 구체적 코드
  • WSL 설치부터 마이그레이션 실행, curl 테스트까지 실습 단계
  • Docker Desktop 대비 WSLC의 강점과 한계(프로덕션 호환성, 고급 Compose 기능)

:pushpin: macOS에서 .NET Broken Pipe / SSL 에러 디버깅하기

https://medium.com/@preetinderblogger2026/debugging-net-broken-pipe-ssl-errors-on-macos-b3835578da47

  • 저자: Preetinder Virk
  • 태그: dotnet #macos ssl

주요 내용

  • “SSL connection could not be established”/“Broken pipe” 에러를 NuGet/TLS 문제와 애플리케이션 HTTPS 호출 문제로 구분해 진단
  • dotnet restore/build/test/run, curl, openssl s_client 등을 이용한 10단계 체계적 진단 절차
  • HTTP/2 및 IPv6 비활성화 환경변수, 최소 NuGet.Config, 프록시 설정 제거 등 구체적 조치
  • 보안 소프트웨어(방화벽/백신)의 영향과 로컬 개발 인증서 갱신, 네트워크 격리 테스트

:pushpin: YARP로 구현하는 멀티테넌트 API 게이트웨이 — 재배포 없는 동적 라우팅

https://medium.com/@shanavas-a-samad/yarp-as-a-multi-tenant-api-gateway-dynamic-routing-without-a-config-redeploy-c1246566390f

  • 저자: Shanavas Abdul Samad
  • 태그: #yarp #apigateway #multitenancy

주요 내용

  • DB를 진실의 원천으로 삼고 게이트웨이 인메모리 라우팅 테이블을 원자적으로 교체하는 아키텍처
  • AddReverseProxy().LoadFromMemory()와 IHostedService 기반 30초 주기 라우팅 갱신 서비스
  • 호스트명/헤더/JWT 클레임으로 테넌트를 식별해 X-Tenant-Id 헤더를 전파하는 미들웨어
  • 설정 검증 실패 시 이전 라우팅 테이블 유지, 인플라이트 연결 드레인 등 운영 주의점
  • 온보딩 속도, 전용 풀 테넌트, 점진적 마이그레이션 등 3가지 적용 시나리오

:pushpin: .NET 캐싱 생존 가이드

https://truestorydavestorey.medium.com/cache-me-if-you-can-a-net-caching-survival-guide-200558945ffa

  • 저자: Dave Storey
  • 태그: dotnet #caching #distributedsystems

주요 내용

  • Cache-Aside, 사전 새로고침, Read/Write-Through, L1/L2 이중 캐싱 등 캐싱 전략 분류
  • IMemoryCache 설정 코드(AbsoluteExpirationRelativeToNow, SlidingExpiration) 예시
  • 캐시 폭증(Cache Stampede), 원자성 없는 확인 로직, 분산 노드 불일치, Poison Pill 등 6가지 실무 함정
  • IMemoryCache/LazyCache/HybridCache(.NET 9+)/FusionCache를 특징별로 비교

:pushpin: 시간적 결합이란 무엇이며 어떻게 다룰 것인가

https://medium.com/@vosarat1995/temporal-coupling-what-is-it-and-how-to-deal-with-it-5de64ee2f2ec

  • 저자: Egor Tarasov
  • 태그: csharp #cleancode #refactoring

주요 내용

  • 시간적 결합(Temporal Coupling)을 "암묵적인 연산 순서에 대한 기대치"로 정의
  • 런타임 예외 명시화 → 리프 서비스 상태 제거 → 중간 서비스 stateless화 → 호출부 갱신 → 불변 레코드 도입의 5단계 리팩토링
  • 문제가 있는 원본 코드부터 리팩토링 완성본까지 전체 C# 소스를 단계별로 제공
  • 불변성과 순수 함수 기반 설계로 구조적으로 버그가 불가능한 상태를 달성하는 결론

:bookmark_tabs: 가벼운 읽을거리

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


ILSpy 11.0 Preview 1 — WPF에서 Avalonia로 전환

  • WPF UI를 걷어내고 Avalonia 12 기반으로 전환해 Windows 외에 Linux/macOS에서도 빌드·실행 가능
  • 북마크, 옴니바 검색바, NuGet 피드 탐색 등 신규 기능과 동적 표현식 await 디컴파일 등 개선

Remove Is Not Uninstall

  • MSIX 패키지 생명주기를 Stage → Register → Deregister → Destage 4단계로 정의
  • "Remove"는 Deregister를 보장하지만 페이로드 삭제(Destage)는 다른 사용자의 사용 여부에 따라 조건부

.NET의 NLog 타겟 — File, Database, Console, Custom

  • File, Console, Database, Network, Null 등 내장 타겟과 Seq, Elasticsearch 등 서드파티 통합
  • AsyncWrapper로 파일 I/O를 백그라운드 큐로 옮겨 블로킹 방지, NLog 5.x/6.x API 차이(concurrentWrites 옵션 제거 등)

OpenIddict 클라이언트 인가 요청에 커스텀 파라미터 전달하기

  • OpenIddict.Client 사용 시 PrepareAuthorizationRequestContext 이벤트에서 파라미터를 직접 설정하는 방법
  • 기본 OpenIdConnect 핸들러 사용 시 OnRedirectToIdentityProvider 이벤트로 값을 추가하는 방법

dotMorten/SideKick — E-Ink 대시보드 워커 앱

  • FT232H USB 어댑터로 Waveshare 전자종이 디스플레이를 구동하는 .NET Worker 기반 애플리케이션
  • 캘린더, Microsoft Graph, Todoist, GitHub 등 다양한 소스를 연동하는 WinUI 단일 프로젝트 MSIX 패키징 구조

모듈러 모놀리스 우선 — 왜 첫날부터 마이크로서비스를 이기는가

  • "초기에는 서비스 경계를 알 수 없다"는 문제 정의와 모놀리스 내 경계 변경 비용 대 마이크로서비스 간 변경 비용 비교
  • Contracts/Internal 네임스페이스 분리와 컴파일러 강제 경계(internal 접근 제어자), 아키텍처 테스트로 경계 위반 감지
4개의 좋아요

아래는 원글의 개선 전/후 코드입니다.

둘 중에 어떻게 더 나아 보이시나요?

개선 전

class Camera
{
    public Video? Shot;
    public void ShootMovie()
    {
        Shot = new Video("An interesting movie from start to finish", true);
    }
}

개선 후

class Camera
{
    // ...
    public static Video MakeMovie() => 
        new ("An interesting movie from start to finish", true);
}
1개의 좋아요

아티클의 주제를 극도로 축약하자면 아래 정도의 내용일 것입니다.

  • 불필요한 상태를 저장하지 마라
  • Mutable 객체를 다룰 때 주의하라

두 코드의 모양만 놓고 어느 쪽이 더 나은 코드로 보이는지를 비교하기 보다는, 아티클의 맥락에서 첫 번째 주제인 불필요한 중간 상태와 그 상태에 대한 호출 순서 의존성이 제거되었는가 를 보는 것이 맞지 않을까 합니다.

1개의 좋아요

맞습니다.

글의 맥락에 문제를 제기하기 보다는, 코드(Camera, Video)가 보여주는 그 자체로만 판단했을 때 다들 어떻게 느껴지시는 지 궁금했습니다.

1개의 좋아요