한 주 동안 .NET 생태계에서 있었던 주요 이슈와 아티클, 기술 트렌드를 정리해 소개합니다.
XPath로 Roslyn 구문 트리 쿼리하기
- 저자: Gérald Barré
- 태그: #roslyn csharp #staticanalysis
주요 내용
- Roslyn 구문 트리를 XPathNavigator로 노출해 System.Xml.XPath 엔진으로 쿼리하는 MA0240 규칙의 구현 방식
- 구문 노드는 SyntaxKind 이름의 XML 엘리먼트로, 토큰은 속성으로 매핑되는 변환 규칙
- BannedSyntaxes.txt에 XPath 식과 메시지를 작성해 goto·lock·중첩 삼항 연산자·매개변수 5개 초과 등 커스텀 규칙을 ■■라이저 없이 정의
- semantic 네임스페이스와 operation 네임스페이스로 3단계 쿼리 수준을 제공,
syntax()함수로 operation 트리에서 다시 syntax 트리로 이동하는 혼합 조회
Postgres가 잘못된 인덱스를 선택할 때
- 저자: Milan Jovanović
- 태그: #postgresql #database #performance
주요 내용
created_at DESC인덱스를 추가한 후 쿼리가 16.5ms에서 241ms로 오히려 느려진 사례- 백만 행 규모 comments 테이블에서 특정 user_id의 1만 건이 12~14개월 전 구간에 몰려 있는 상황을 재현
- 쿼리 플래너가
ORDER BY+LIMIT에 맞춰 조기 종료를 기대했으나 실제로는 495,944개 행을 버린 뒤에야 결과를 찾는 실행계획을 선택 (user_id)단일 인덱스,(created_at DESC)단일 인덱스,(user_id, created_at DESC)복합 인덱스의 실행 시간 비교(16.537ms → 241.354ms → 0.067ms)- 복합 인덱스 적용 시 기존 단일 인덱스 중복 여부 검토, 쓰기 성능·저장 공간 오버헤드, 대표성 있는 데이터로 테스트할 것을 권고
Visitor 패턴을 제네릭 공변으로 다시 구현하기 — Visitor.NET
- 저자: StepOne
- 태그: csharp #designpatterns #sourcegenerators
주요 내용
- 컴파일러·정적 분석기에서 계층 구조를 반복 순회할 때 발생하는 문제를 침습적 방식 → 클래식 비지터 → 비순환 비지터 순으로 전개
- 비순환 비지터의 런타임 다운캐스트 문제를 C# 제네릭 분산(공변/반공변)으로 해결한
IVisitable<out TVisitable>,IVisitor<in TVisitable, out TReturn>인터페이스 설계 - 원소 계층·기본 비지터 인터페이스·구체 비지터 구현 3단계에 걸친 “triple dispatch” 구조
- 형변환 방식별(암시적 변환, 명시적 캐스트, as, dynamic) 성능 벤치마크에서 dynamic이 현저히 느리다는 결과
[AutoVisitable<Element>]소스 제너레이터로 보일러플레이트 Accept 오버로드를 자동 생성하는 Visitor.NET 라이브러리 공개
C#로 파이널 스테이트 트랜스듀서를 만든 이유
- 저자: Jordan Rowles
- 태그: csharp #datastructures #searchengine
주요 내용
Dictionary<string,long>은 정확 일치 조회는 되지만 접두사 열거, 와일드카드 순회, Levenshtein 자동자 교집합 같은 검색엔진 기능은 지원하지 못하는 한계- Trie로 전환해 공통 접두사를 그래프 간선으로 공유하되 접미사 중복 문제가 남는 지점
- 동일 출력·동일 도착 상태·동일 수락 여부를 가진 상태를 병합하는 최소 비순환 FST로 발전
- 빌더는 정렬된 바이트 시퀀스를 받아 미확정 경로(“frontier”)를 유지하다 확정 시 컴파일하고 레지스트리로 동일 상태를 병합
{"car":42, "cat":50}예시로 상태 공유와 출력 분산을 시연, 디스크 포맷도 제시
하나의 변환, 두 개의 세계 — C#과 IQueryable 매핑
- 저자: Yevhen Cherkes
- 태그: #efcore #iqueryable #expressiontrees
주요 내용
- 사람이 읽기 좋은 이름 있는 변환 코드와 IQueryable 프로바이더가 요구하는 투명한 표현식 트리 사이의 충돌
- 명명된 메서드를 인라인 처리하면 이름이 사라지고,
Expression<Func<T,TResult>>로 저장하면 표현식 간 합성이 어려워지는 문제 - Expression-First(Raffinert.Expressions)와 C#-First(AlephMapper류) 두 소유권 모델을 대조
- 런타임에 구조가 결정되는 쿼리는 PredicateBuilder/LINQKit 방식의 동적 합성이 필요하다는 지적
- AutoMapper ProjectTo, Mapperly, EntityFrameworkCore.Projectables, LINQKit을 기존 도구 생태계로 정리하고 “Eliminate, Generate, Derive, Compose” 의사결정 프레임워크 제시
.NET 11이 Duende IdentityServer 개발자에게 바꾸는 것들
- 저자: AL Rodriguez
- 태그: #dotnet11 #duende #identityserver
주요 내용
- .NET 11 앱은 .NET 10 대상으로 빌드된 Duende IdentityServer 패키지를 그대로 참조 가능
- ASP.NET Core Identity 기본 UI가 SignInOptions 관련 설정을 회원가입 이후 흐름에서 실제로 강제하도록 변경
- Blazor Interactive Server 컴포넌트가 회로 재연결 없이 ClaimsPrincipal을 갱신하고 AuthenticationStateChanged 이벤트를 발생
- SignalR 허브가 EnableAuthenticationRefresh 옵션으로 토큰 만료 시 재연결 없이 인증을 갱신
- Kestrel이 HTTPS Negotiate 인증에 엔드포인트 채널 바인딩 토큰을 제공하며, 배포 간 Data Protection 키 링 보존이 필요함을 명시
ASP.NET wwwroot 파일 접근 보호하기
- 저자: Rick Strahl
- 태그: aspnetcore #security #staticfiles
주요 내용
- wwwroot 폴더의 모든 파일이 기본적으로 인증 없이 공개 정적 자산으로 서빙되는 문제
UseStaticFiles()이전에 커스텀 미들웨어를 추가해StartsWithSegments()로 경로를 검사하고 미인증 요청은 401로 반환하는 방법UseAuthentication()이 이 미들웨어보다 먼저 실행돼야 하며,MapGet()라우팅 핸들러는 정적 파일 미들웨어가 먼저 가로채 기존 정적 파일 요청을 안정적으로 가로채지 못하는 한계- IIS나 프록시가 특정 확장자를 직접 서빙하도록 설정된 경우 ASP.NET Core 파이프라인 자체를 우회하는 사례와 web.config
<location>설정을 통한 해결책
C#에서 튜플 순회하기
- 저자: Jason Bock
- 태그: csharp #tuples #performance
주요 내용
ValueTuple<T1..Tn>이 모두ITuple인터페이스를 구현하며 Length 프로퍼티와 인덱서를 제공- 튜플에 그대로 foreach를 쓰면 CS1579 컴파일 에러가 발생
ITuple확장 메서드로 GetEnumerator를 정의해 인덱스를 yield return하는 방식으로 해결, 8개 초과 요소의 재귀적 TRest 구■■지 이 방법 하나로 처리- 4개 요소 튜플 순회는
List<object>대비 약 10배, 64개 요소는 약 171배 느리고 메모리 할당도 크게 증가
EF Core 리포팅 뷰 — 쿼리 전에 행부터 설계하라
- 저자: Mitchel Sellers
- 태그: #efcore #sql #dataaccess
주요 내용
- 조인부터 하지 말고 “한 행이 무엇을 의미하는지”(grain)를 먼저 정의할 것을 제안
- 1:N 관계마다 윈도우 함수(
ROW_NUMBER() OVER PARTITION BY)로 최신 레코드 선택, 집계, EXISTS 기반 존재 플래그, 스칼라 결합, 별도 상세 쿼리 중 하나를 선택해 사전에 축소 - 1:N을 그대로 조인하면 3×2×4=24행처럼 카티전 곱이 발생하며 DISTINCT()는 근본 해결책이 아니라는 지적
- 리포팅 뷰를
entity.HasNoKey()로 키 없는 엔터티로 매핑하되 식별 컬럼은 디버깅·조인을 위해 남겨두는 방법 ToQueryString()으로 생성된 SQL을 직접 확인하고 모든 컬렉션에 자식이 여러 개인 케이스로 grain 안정성을 테스트할 것을 조언
TPL Dataflow로 파이프라인 구축하기
- 저자: Nick Cosentino
- 태그: #tpldataflow #concurrency dotnet10
주요 내용
TransformBlock2개와ActionBlock3단계로 구성된 주문 처리 파이프라인 예제BoundedCapacity,MaxDegreeOfParallelism,EnsureOrdered=true등 블록별 설정과 근거를 표로 정리SendAsync는 백프레셔에 참여하고 수락 여부를 bool로 반환하는 반면Post는 즉시 반환만 하는 차이,PropagateCompletion=true없이는 하위 블록이 완료되지 않는 점TaskCompletionSource<ExceptionDispatchInfo>로 첫 장애를 포착하고 공유 CancellationToken을 취소하는 장애 처리 패턴- 파이프라인이 인메모리 구조라 프로세스 종료 시 버퍼가 유실되는 비영속성 한계
가벼운 읽을거리
후보 항목 중 이슈로 선정되지 않은 가벼운 읽을거리들
Wolverine으로 논리적 메시지 중복 제거하기
- 오퍼레이터의 이중 클릭, 콘솔 재발행, 스케줄러 재시도 등 서로 다른 경로로 들어온 동일한 "논리적 의도"가 각기 다른 envelope ID를 가져 중복 처리되는 문제
DeduplicationId필드와wolverine_deduplication테이블로 설정 가능한 윈도우(기본 24시간) 내 동일 ID 메시지를 조용히 폐기·로깅- 핸들러 레벨
[Deduplicated]특성, 발행 측DeliveryOptions.DeduplicationId등 4가지 설정 방식과 옵트인 방식, 메이저 버전에서만 스키마 변경이 이뤄지는 제약
스택 초기화를 막는 작은 C# 특성 — SkipLocalsInit
[module: SkipLocalsInit]은 컴파일러가 IL에 initlocals 플래그를 넣지 않게 해 로컬 변수 스택 제로화를 생략- C#의 확정 할당 규칙 덕분에 일반 지역 변수는 안전하며, 실제 영향받는 대상은 stackalloc처럼 명시적으로 할당된 미초기화 메모리
- 버퍼를 완전히 채우지 않고 읽으면 비결정적 버그, 부분 초기화된 버퍼가 노출되면 정보 유출로 이어질 수 있어 코덱·파서·직렬화기 등 핫루프 상황에 한정해 권장
WireMock.Net과 Protobuf로 gRPC 목킹하기
- 기존 WireMock.Net의 gRPC 목킹은 런타임에 .proto 파일을 로드해야 하고 JSON 기반 바디 매칭으로 여러 스텁이 서로 다른 메시지 타입 역직렬화를 시도해 오류가 발생하는 문제
- 업스트림에 기여한 EarlyMismatch 기능으로 URL 경로 등 메타데이터로 불일치 스텁을 조기에 걸러 불필요한 역직렬화 방지
- WireMock.Grpc.Protobuf 패키지가 생성된 protobuf 클래스를 직접 사용해 정확 바이트 매칭과 타입 프레디케이트 매칭을 지원, gRPC 표준 5바이트 프레이밍을 직접 처리
C# DIY 컴파일러 만들기 — HydraScript
- 대학 졸업논문에서 시작한 TypeScript/Go 영감 언어 인터프리터 HydraScript를 FrontEnd·IR·BackEnd 세 서브도메인으로 분리
- AST에 흩어진 가상 메서드 대신 Visitor 패턴으로 전환, 단항 표현식 처리 메서드를 약 20줄에서 6줄로 축소
- 파라미터 기반 주소 지정 대신 O(1) 주소 재계산 자료구조를 직접 설계하고, 공변·반공변 기반 비순환 Visitor 라이브러리(Visitor.NET)로 서브도메인 간 순환 의존성 제거
Result 대안으로서의 유니온 타입 비교
- OIDC 로그아웃 엔드포인트 사례로 전통적인
Result<T>래퍼 클래스 방식과 새로 도입된 C# 유니온 타입 문법을 비교 Result<T>방식에서는 처리되지 않은 케이스가 조용히 누락됐으나, 정확히 4개 케이스로 구성된 판별 유니온은 스위치 표현식에서 전수 처리를 강제해 누락 시 빌드 에러 발생- catch-all 패턴을 쓰면 유니온에서도 다시 조용한 실패가 재도입될 수 있다는 지적
SSH는 데스크톱이 아니다
- 이동 중 노트북에 Docker/kubectl/.NET SDK 등이 없어 SSH로 데스크톱에 접속해 Claude Code를 실행하게 된 배경
- Windows에서 SSH 세션 종료 시 job object teardown으로 모든 프로세스가 종료되는 문제, 비대화형 세션에서 Git Credential Manager 오류로 패스프레이즈 없는 전용 SSH 키를 사용하는 우회
- Windows Update 재부팅 후 로그인 세션이 필요한 서비스가 SSH로 재기동되지 않는 문제와 브라우저 인증·UAC 등 GUI 종속 작업을 원격 데스크톱 도구로 주기적으로 처리해야 하는 한계
EF Core 10에서 Action Method Selector로 GET 다중 구현하기
- .NET Core 10으로 컴파일하면 특성 없이는 모든 요청에 파라미터 없는 Get()이 호출되는 브레이킹 체인지
ActionMethodSelectorAttribute를 상속한QueryStringConstraint클래스로 쿼리스트링 키를 메서드 파라미터명과 비교해 매칭 여부 판정- 파라미터 없는 Get()을 포함해 구분이 필요한 모든 GET 메서드에
[QueryStringConstraint]를 붙여야 하는 제약
프로덕션 .NET에서 메모리 누수 추적하기
- dotnet-counters로 힙 크기와 Gen 2 GC 동작을 모니터링해 “Gen 2 수행 후에도 힙이 줄지 않는” 패턴을 확인
- dotnet-gcdump 스냅샷 2회 비교와 gcroot 분석으로 static PriceTicker → EventHandler 델리게이트 → CartViewModel 참조 체인을 추적, 약 14,000개 인스턴스 누적을 확인
- 해결책으로 IDisposable 구현 후 구독 해제, 또는 PriceCache 싱글턴으로 설계를 바꾸는 방법 제시
System.Text.Json 다형성 처리와 Swagger 통합하기
- Newtonsoft.Json은 런타임 타입 정보로 동적 계약을 구성하지만 System.Text.Json은 명시적 타입 정보 없이는 파생 타입을 빈 객체로 직렬화하는 차이
- .NET 7
[JsonDerivedType]특성으로 $type 판별자를 붙여 다형성 직렬화를 구현하고, Swashbuckle이 기본적으로 다형성을 인식하지 못하는 문제를UseOneOfForPolymorphism·SelectDiscriminatorNameUsing등으로 해결 - FluentValidation의
SetInheritanceValidator로 파생 타입별 검증을 연결하고, Refit 클라이언트에서 실제 파생 타입의 BaseType으로 직렬화해야 하는 함정과 해결 코드 제시
마이크로서비스 동기 vs 비동기 통신
- 동기(HTTP/REST, gRPC+protobuf)와 비동기(RabbitMQ, Azure Service Bus point-to-point / Kafka, Event Hubs pub-sub) 통신 방식을 기술별로 구분
- HttpClient·gRPC .proto·Microsoft.Extensions.Http.Resilience 기반 재시도·서킷 브레이커 설정과 Azure Service Bus를 이용한 비동기 발행·구독 코드 제공
- 결합도·지연시간·가용성·확장성·일관성·복잡도·장애 가시성 기준의 비교표와 "경계에서는 동기, 그 뒤에서는 비동기"라는 하이브리드 원칙 정리
.NET에서 System.Text.Json으로 JsonPath 쓰기
- Meziantou.Framework.JsonPath라는 NuGet 패키지로 System.Text.Json의 JsonNode에 RFC 9535 기반 JSONPath 쿼리를 적용
JsonPath.Parse()로 표현식을 한 번 컴파일해 재사용하고Evaluate()/EvaluateValue()로 정규화된 경로와 값을 반환, 속성명·와일드카드·인덱스·슬라이스·필터 선택자 지원- 잘못된 연산 시 null을 반환하는 Lax 모드와 예외를 던지는 Strict 모드 두 가지 평가 모드 제공
