한 주 동안 .NET 생태계에서 있었던 주요 이슈와 아티클, 기술 트렌드를 정리해 소개합니다.
Device Bound Session Credentials 이해하기
- 저자: Andrew Lock
- 태그: #security #dbsc aspnetcore #cookies
주요 내용
- 인증 쿠키가 bearer 토큰처럼 동작해 탈취되면 세션 하이재킹이 가능해지는 구조
- 로그인 응답의
Secure-Session-Registration헤더로 알고리즘(ES256, RS256), 등록 경로, 챌린지 전달 - 브라우저가 TPM/보안 영역에 키 쌍을 생성하고 챌린지를 서명한 JWT를
Secure-Session-Response로 전송 - 서버가 약 5분 수명의 단기 쿠키를 발급하고, 갱신 시
Secure-Session-Challenge와Sec-Secure-Session-Id로 키 소유를 재증명 - 구현 요소는 로그인 응답 헤더, 등록 엔드포인트, 갱신 엔드포인트 3가지이며 Chromium 전용, ASP.NET Core 프로토타입 PR 존재
아무것도 사지 않고 세계 최악의 네트워크에서 모바일 앱을 쓸 만하게 만든 과정
- 저자: Mikhail Petrusheuski
- 태그: maui #performance #caching #networking
주요 내용
- .NET MAUI 앱 PlayMaksi(인도·이집트·인도네시아 어린이 사용자)를 6주간 개선해 홈 로드 p90 26.2초에서 6.6초, 실패율 70.9%에서 32.4%로 감소
error_stage이벤트 파라미터와 BigQueryevents_*테이블 분석으로 일부 국가 실패의 87%가 DNS 단계임을 확인- 2MB 이상 표지 이미지를 360/720/1080px WebP로 생성하고 키별 세마포어로 중복 생성 방지
- 캐시 헤더를
max-age=3600에서 불변 키 기반public, max-age=31536000, immutable+ ETag로 변경, 동적 페이지는 브라우저 60초·엣지 300초·stale-while-revalidate 600초 - 클라이언트 취소를 500이 아닌 499로 구분, DoH 폴백은 IP 고정으로 구현했으나 개선이 측정되지 않음
Kahuna가 단일 노드에서 초당 29만 요청에 도달한 방법
- 저자: Andres Gutierrez
- 태그: #kahuna #grpc #performance #actormodel
주요 내용
- 8코어 Apple M3, 폐쇄 루프 워커 64개 기준 GET 290K req/s(Valkey 9.1은 247K req/s)
- 병목은 저장소가 아닌 전송 계층으로, 작은 요청마다 gRPC 오버헤드가 발생. Request Frames로 독립 요청을 단일 스트림 메시지에 합쳐 처리량 4배
- Fused Finalization으로 prepare·검증·commit을 한 액터 턴에 수행해 액터 메시지 6개를 1개로 축소
- 속도 제한기 84K에서 약 200K 결정/초, RETURN 1 스크립트 105K에서 416~438K req/s, p50 700μs에서 284μs, p99 4ms에서 약 0.7ms
- Fast path는 단일 임시 키, 스캔·명시적 트랜잭션 없음으로 제한되며, 남은 약점으로 lock API 개별 메시지와 2키 트랜잭션 19K/s 공개
ASP.NET Core의 Device Bound Session Credentials(DBSC) 실험적 지원: .NET 11 프리뷰 탐험 - 8부
- 저자: Andrew Lock
- 태그: #dotnet11 aspnetcore #security
주요 내용
- 수명이 긴 인증 쿠키를 단기 쿠키로 교체하고, TPM에 보관된 개인 키로 서명한 챌린지 응답으로 갱신하는 DBSC 원리
- 등록은
Secure-Session-Registration헤더, 갱신은Sec-Secure-Session-Id,Secure-Session-Challenge,Secure-Session-Response(JWT) 헤더로 진행 Microsoft.AspNetCore.Authentication.DeviceBoundSessions패키지의AddDeviceBoundSession("Application"), Identity에서는sourceScheme: IdentityConstants.ApplicationScheme지정- 내부적으로
{scheme}.Dbsc.Session,{scheme}.Dbsc.Refresh(/.well-known/dbsc경로 한정) 쿠키와 PolicySchemeHandler 등록 - HTTPS 필수, 브라우저 지원 한정, 광고 차단기 간섭, 개발자 도구에 요청이 보이지 않아 서버 로그로 디버깅
C# fixed buffer의 숨은 함정
- 저자: Kevin Gosse
- 태그: csharp #fixedbuffer #unsafe
주요 내용
- 생성자가 없는 struct는 컴파일러가
initobj로 fixed buffer까지 0으로 초기화하지만, 사용자 정의 생성자가 있으면 fixed buffer가 초기화되지 않을 수 있음 - C# 사양(§23.8.4)에서 fixed-size buffer는 definite assignment 검사 대상에서 제외
fixed long buffer[1]예제에서 생성자 유무에 따라 출력이0 0 0 0 0과0 1 2 3 4로 갈림- C# 11의 자동 기본값 초기화와 결합했을 때의 영향
- 대응 방법은 생성자에서
Span<T>.Clear()로 직접 지우거나InlineArray로 이전
.NET 커스텀 객체 모델에 JsonPath 지원 추가하기
- 저자: Gérald Barré
- 태그: dotnet #jsonpath #meziantou
주요 내용
JsonNode로 변환하지 않고 강타입 C# 도메인 클래스에 직접 JSONPath 식(RFC 9535 기반)을 평가Meziantou.Framework.JsonPath의JsonPathNavigator<TValue>를 상속해 구현- 구현 대상 메서드는
GetKind(),TryGetPropertyValue(),GetProperties(),GetArrayLength(),TryGetElement(),TryGetString()/TryGetNumber()/TryGetBoolean() ProductCatalog/Product/ProductMetadata모델용ProductCatalogNavigator구현 예제(활성 상품 중 50유로 미만 이름 조회)Evaluate()는 전체 매치,EvaluateValue()는 첫 매치를 반환하며JsonPathEvaluationMode로 strict/lax 선택
.NET 11 Runtime Async: 계층 호출은 빨라지고 단일 호출은 느려진다
- 저자: Tolga Yildiz
- 태그: #dotnet11 #runtimeasync #benchmark #performance
주요 내용
- .NET 8, 10, 11 RC1(Runtime Async 비활성)을 비교하는 실험군 A와 .NET 11 RC1에서 변환 효과만 분리하는 실험군 B로 구성
- Ryzen 9 9950X3D, Windows 11, BenchmarkDotNet 환경에서 TaskCompletionSource 게이트로 동기 완료를 막고 중단 상황을 통제
- 4단계 계층 async에서 동기 완료 46% 빠름, 할당 352B에서 88B(75% 감소), 중단 시 26% 빠름
- 단일 호출은 동기 Task 24% 느림, ValueTask 35% 느림, 통제된 중단 46% 느림(할당 200B에서 272B)
- 활성화는
<Features>runtime-async=on</Features>, 검증은 메타데이터 비트0x2000및AsyncStateMachineAttribute부재 확인
C#에서 메모리 덤프 생성하기
- 저자: Aaron Powell
- 태그: #memorydump #diagnostics threadpool
주요 내용
ThreadPoolWatcher가 주기적으로 작업을 제출해 완료 지연이 임계값을 넘으면 덤프를 자동 생성- Windows는
dbghelp.dll의MiniDumpWriteDump를 P/Invoke로 호출하며 덤프 크기 약 125MB - Linux는 런타임에 포함된
createdump를 하위 프로세스로 실행하며 덤프 크기 약 800MB - Yama LSM 환경에서는
PR_SET_PTRACER, 컨테이너에서는--cap-add=SYS_PTRACE필요 - 댓글에서 지적된 사항:
DllImport대신LibraryImport, 자기 자신 덤프 시 락 보유 스레드 정지로 인한 교착 가능성, 덤프 내 민감 정보
GC가 .NET 앱의 DI 누수를 막아주지 못하는 이유
- 저자: Satyam Pushkar
- 태그: dotnet dependencyinjection #gc #memoryleak
주요 내용
- GC 루트와 도달 가능성 기준으로 DI 수명과 GC 수명이 독립적으로 결정되는 구조
- 루트 컨테이너에서 resolve한 IDisposable Transient를 컨테이너가 추적 목록에 보관해 해제되지 않는 사례
- Singleton이 Scoped(IOrderRepository→DbContext)를 캡처하는 Captive Dependency
- 제거 정책 없는 Singleton 컬렉션(ConcurrentDictionary)과 이벤트 구독 누수
- ValidateScopes, ValidateOnBuild 옵션과 dotnet-counters, dotnet-dump, dotnet-gcdump, dotnet-monitor 진단 도구 사용
C#에서 UTF-16 문자열을 얼마나 빠르게 고칠 수 있는가
- 저자: Daniel Lemire
- 태그: #simd #utf16 #performance
주요 내용
- 짝이 없거나 잘못된 surrogate는 ill-formed 문자열이므로 디스크·네트워크로 보내기 전에 정리해야 함
- SimdUnicode 라이브러리의
IsWellFormed()(검사)와ToWellFormed()(lone surrogate를 U+FFFD로 대체) - Intel Xeon Gold 6548N(AVX-512)에서 라틴 텍스트 검증 69 GB/s,
IndexOfAnyInRange는 33 GB/s - 정상 이모지 입력에서 기존 방식은 0.4 GB/s로 떨어지고 SIMD는 53 GB/s 유지
- 이미 정상인 문자열은 원본을 그대로 반환해 할당이 없으며 Apple M4 Max에서도 유사한 경향
C# 15 컬렉션 식 인수: C# 12, 13과 무엇이 다른가
- 저자: Laurent Kempé
- 태그: #csharp15 #collectionexpressions #language
주요 내용
- 컬렉션 식 첫 요소로
with(...)를 두어 생성자·팩토리 인수를 전달([with(capacity: 100), 1, 2, 3]) - C# 12(컬렉션 식, spread), C# 13(params 컬렉션 확장), C# 14(변경 없음), C# 15(with 인수) 버전별 비교 표
- 용량 사전 지정,
StringComparer.OrdinalIgnoreCase전달,CollectionBuilder특성의 커스텀 컬렉션 예제 - 배열·span 미지원,
dynamic인수 불가, 타입 추론에는 영향 없음
가벼운 읽을거리
후보 항목 중 이슈로 선정되지 않은 가벼운 읽을거리들
멀티 테넌트 모범 사례가 역효과를 낼 때
- 암묵적 테넌트 컨텍스트가 HTTP 요청에서 백그라운드 큐 처리로 실행 컨텍스트가 바뀔 때 숨은 의존성 버그를 만드는 사례
- 명시적 테넌트 ID와 암묵적 컨텍스트, 전역 쿼리 필터와 테넌트 간 작업용 우회로, 공유 DB와 테넌트별 DB, 기능 플래그와 모듈 분리의 트레이드오프 비교
- 테넌트 식별을 어디서 명시할지, 컨텍스트 전환 시 무엇이 일어나는지, 어디서 경계를 넘어도 안전한지에 대한 설계 질문
PostgreSQL에서 SQL Server로: 날짜·시간 필드 노트
- PostgreSQL
timestamp(마이크로초 6자리, 시간대 없음)·timestamptz와 SQL Serverdatetime2(7)(100ns 정밀도, 6~8바이트)·datetimeoffset(오프셋 포함, 8~10바이트) 대응 timestamptz는 오프셋을 저장하지 않고 UTC로 정규화하지만datetimeoffset은 입력 오프셋을 보존AT TIME ZONE,SWITCHOFFSET()T-SQL 예제와 10억 행 기준 저장 공간 약 2GB 차이
다운스트림 API 하나가 서비스 전체를 무너뜨린 날
- Payment API가 약 200ms에서 5~10초 이상으로 느려지고 Order API의 재시도가 트래픽을 늘려 장애가 커지는 시나리오
- Circuit Breaker의 Closed·Open·Half-Open 상태,
AddStandardResilienceHandler()기본 설정과CircuitBreakerStrategyOptions(실패율 50%, 최소 10건, 샘플링 30초, 차단 15초) - 멱등성 키 없는 재시도의 중복 처리 위험, 400대와 500대 오류 구분, 타임아웃·재시도·서킷 브레이커 역할 분리
구조체는 스택에 있지 않다: C#의 실제 메모리 관리 방식
- 값 타입은 자신을 담는 컨테이너가 있는 곳에 위치한다는 점과 지역 변수, 클래스 필드, 구조체 배열, async/람다 캡처 시 힙 승격 사례
- 정수 100만 개 박싱 시 약 24MB 힙 압박, 클래스 오브젝트 헤더(SyncBlockIndex + MethodTable 포인터 16바이트)
Span<T>제로 할당 슬라이싱,ref struct제약,readonly struct와 구조체 크기 16~24바이트 이하 권장
WinAppCLI v0.7.0 - Windows Sandbox 자동화, Native AOT 지원, 단일 파일 애플리케이션 등
--on sandbox플래그로 Windows Sandbox에서 앱을 실행·디버깅하고winapp target명령으로 게스트 관리, 파일 전송, 데스크톱 캡처winapp run . --aot로 Native AOT 앱을 별도 publish 없이 빌드·실행(x64, ARM64),#:property지시문으로.cs단일 파일을 패키지 ID와 함께 실행winapp pack이 .NET 프로젝트의 빌드·패키징·서명을 수행하고winapp new --template reactor로 MVU 방식 순수 C# WinUI 앱 생성(실험적, .NET 10 필요)
