한 주 동안 .NET 생태계에서 있었던 주요 이슈와 아티클, 기술 트렌드를 정리해 소개합니다.
17ms에서 0.04ms로 — 올바른 SQL 인덱스 설계하기
- 저자: Milan Jovanović
- 태그: #sql #postgresql #performance
주요 내용
- 인덱스는 테이블 스키마가 아니라 실제 실행되는 쿼리에서 설계해야 한다는 원칙과 복합 인덱스 컬럼 순서(동등조건 → 정렬/범위), 최좌접두사 규칙
- 단순 동등조건 검색에서 순차 스캔 17ms가 인덱스 스캔 0.6ms로 단축
- 복합 조건+정렬 쿼리는 16.6ms에서 0.04ms로, EXPLAIN ANALYZE 실행 계획에
Index Scan using ix_comments_issue_user_date가 찍히는 과정 제시 - 대시보드 LATERAL 서브쿼리에서 인덱스 컬럼 순서가 잘못되면 정렬이 6,537회 수행되며 436ms, 순서 최적화 후 0.5ms
- 복합 인덱스는 100만 행당 30MB, 단일 컬럼 인덱스는 7MB로 저장 비용과 쓰기 성능 저하가 따르며 중복 인덱스를 제거해야 한다는 지침
내 벤치마크는 라이브러리가 2배 빠르다고 했다. 아니었다.
https://baryodev.medium.com/my-benchmark-said-my-library-was-2x-faster-it-was-not-d6a6110bf354
- 저자: Baryo Dev
- 태그: #benchmarkdotnet csharp #performance
주요 내용
- Mapsicle이 AutoMapper 대비 2.1배 빠르다고 주장했으나 재측정 결과 1.48배였고, 컬렉션 매핑은 오히려 1.33배 느림(2,428ns vs 1,823ns)
- 손으로 작성한 Stopwatch 루프(10만 회 반복)가 290ns를 보고한 반면 BenchmarkDotNet은 33ns로, 9배 오차의 원인은 매핑이 아니라 할당·GC 압력을 측정하고 있었기 때문
- CI 게이트를 BenchmarkDotNet 요약 데이터를 직접 읽도록 재구성하고, 보호 대상을 되돌려 게이트가 실제로 실패하는지 검증하는 절차 도입
Type.IsAssignableFrom이 암시적 변환을 인식하지 못해 int→long 매핑이 예외 없이 0을 반환하는 등 데이터 손상 버그 7개 발견- 근본 원인은 변환 로직이 세 곳에 중복 구현되어 있던 것
초록 CI 배지는 .NET 패키지 무결성을 증명하지 않는다
- 저자: Christopher D. Cavell
- 태그: #supplychainsecurity #nuget dotnet
주요 내용
- 초록 CI 배지가 증명하는 것(특정 환경에서의 빌드·테스트 성공)과 증명하지 못하는 것(게시된 패키지와 검증된 소스의 동일성)의 구분
- 소스 커밋 → 의존성 → CI 워크플로 → 빌드 환경 → 패키지 생성 → 레지스트리 → 소비자로 이어지는 아티팩트 추적 경로와 각 경계에서 던져야 할 질문
ContinuousIntegrationBuild=true와 Source Link 기반 결정론적 빌드, NuGet 저자 서명과 리포지토리 서명의 구분(dotnet nuget verify), 체크섬이 같은 채널 교체 시 무력해지는 한계gh attestation verify를 통한 빌드 provenance 검증과packages.lock.json·액션 SHA 고정을 통한 의존성 고정- tj-actions/changed-files 공급망 공격(CVE-2025-30066)을 예로 SHA 고정의 효과와 한계, 저장소 규모에 비례한 검증 수준을 제시하는 8항목 체크리스트
.NET에서 머신의 전체 프로세서 개수 구하기
- 저자: Andrew Lock
- 태그: dotnet #performance #interop
주요 내용
Environment.ProcessorCount의 버전별 동작 차이 — .NET Framework는 호스트 전체 논리 프로세서를 반환하고, .NET 6+는 친화성·컨테이너 제한 하에서 사용 가능한 프로세서만 반환- 컨테이너·친화성 제한과 무관하게 머신의 물리적 전체 프로세서 수가 필요한 시나리오
- Windows에서 P/Invoke로
GetActiveProcessorCount()호출 - macOS에서
sysctlbyname("hw.logicalcpu")호출 - Linux에서는
sysconf대신/sys/devices/system/cpu/online파일을 직접 파싱해 엣지 케이스를 회피하는 이유
용량은 숫자가 아니다 — Redis Lua로 구현하는 원자적 승인과 스케일아웃
- 저자: Marco Marano
- 태그: #redis #distributedsystems dotnet
주요 내용
- 두 제어 평면이 동일한 용량 스냅샷을 읽고 같은 결정론적 로직을 실행하면 같은 slot을 이중 점유하는 경쟁 조건
AvailableRunSlots/ReservedRunSlots/EffectiveAvailableRunSlots로 나눈 다차원 용량 모델과 웜 런타임 재사용 → 새 Pod 생성 → 백프레셔 우선순위- 개별 Redis 명령은 원자적이지만 여러 명령에 걸친 결정은 원자적이지 않으므로, ZREMRANGEBYSCORE·ZRANGE·ZCARD·ZADD·PEXPIRE를 하나의 Lua 스크립트로 묶어 경계 검사와 slot 점유를 단일 원자 연산으로 처리
- Selection은 선택하고 Reservation은 소유하며 Creation은 변형한다는 세 연산의 역할 분리
- 예약 실패 시 재시도 대신 인벤토리를 다시 구성해 재선택하고, 정렬 집합 점수를 만료 타임스탬프로 사용해 크래시된 제어 평면의 용량 누수를 방지
.NET 서비스 디스커버리 4가지 구현 방식
- 저자: Arash Zand
- 태그: #servicediscovery #kubernetes #dapr
주요 내용
- Kubernetes DNS 방식에서 TCP 연결 풀링으로 DNS 재해석이 일어나지 않는 문제와
SocketsHttpHandler설정 해법 Microsoft.Extensions.ServiceDiscovery의 프로바이더 체인과AddServiceDiscovery/AddStandardResilienceHandler등록 순서 문제- Consul 등록(
AgentServiceRegistration)과 헬스체크 기반 해석 핸들러 구현 - Dapr 사이드카를 통한 서비스 호출에서 mTLS·재시도·분산 추적이 자동화되는 대신 발생하는 오버헤드
- 네 방식의 복잡도·메타데이터·mTLS·개발환경 지원 비교표와 멱등성 키를 포함한 복원력 설정
Aspire로 BFF 보안 아키텍처 구현하고 배포하기
- 저자: damienbod
- 태그: #dotnetaspire #bff #security
주요 내용
- Angular 프론트엔드 + ASP.NET Core BFF + Auth0 인증 + 다운스트림 보안 API 구성을 .NET Aspire AppHost로 배포
- AppHost 클래스의 코드 기반 구성으로 각 애플리케이션을 매핑하는 방식
- 개발 환경에서는 Angular 개발 서버를 통합하고 프로덕션에서는 빌드 산출물을 서빙하는 조건부 구성
- Auth0 자격증명과 DPoP 키 파라미터 등록, 프로젝트 참조·환경변수 설정, YARP 역프록시 구성
- 전체 코드는 damienbod/Auth0BffDpopApi 저장소에 공개
비즈니스 규칙은 위키에 머물 필요가 없다 — Roslyn 분석기로 강제하기
- 저자: Pabasara Mahindapala
- 태그: #roslyn csharp #staticanalysis
주요 내용
- 다중 테넌트 ASP.NET Core 앱에서 사용자 권한과 비즈니스 접근 권한 두 계층 중 하나가 누락되면 발생하는 크로스테넌트 IDOR 취약점
[BusinessAuthorize]속성과CheckAgainstBusinessInRequest플래그를 검사하는DiagnosticAnalyzer구현- 속성 검색 → 체크 비활성화 판정 → BusinessId 매개변수 탐지 → 진단 보고의 4단계 분석 로직
.editorconfig로 Warning에서 Error로 점진 전환하고SuppressMessage로 예외 사유를 기록하는 도입 전략.csproj의OutputItemType="Analyzer"설정 등 프로젝트 구성 방법
이벤트 기반 아키텍처와 커플링 — 생각만큼 디커플링되지 않았다
- 저자: Derek Comartin
- 태그: #eventdriven #architecture #messaging
주요 내용
- 메시지 브로커 도입이 느슨한 결합을 자동으로 보장하지 않으며 "CRUD over Pub/Sub"가 안티패턴이라는 지적
ShipmentStatusChanged같은 CRUD성 이벤트가 내부 데이터 모델을 공개 API처럼 노출시켜 소비자가 상태값을 역공학하게 만드는 문제ShipmentDispatched,ShipmentDelayed등 명시적 비즈니스 이벤트로의 재설계- 도메인 이벤트(서비스 내부, 자유 변경)와 통합 이벤트(서비스 간, 계약·버전관리 필요)의 구분
- CDC 사용 시 DB 변화를 그대로 노출하지 말고 공개 통합 이벤트로 번역하라는 원칙
Undo는 기능이다 — Saga 보상과 재조정 백스톱
- 저자: Ivan Ball-llovera
- 태그: #saga #compensation #idempotency
주요 내용
- 체크아웃은 원자적이지만 결제 승인 웹훅이 비동기로 도착해 이전 단계 롤백이 필요한 문제
- 보상 로직을 도메인 이벤트 핸들러로 분리하고 별도 DI 스코프에서 독립 커밋
Order.InventoryRestored같은 DB 불변 마커로 멱등성을 보장하고 재고 복구와 동일한 SaveChanges에서 커밋- RowVersion 낙관적 동시성 토큰으로 동시 재배달 처리
- 프레임워크 없이 적용할 수 있는 6단계 원칙 별도 정리
프로젝트에서 NuGet 패키지가 할 수 있는 일 제한하기
- 저자: Gérald Barré
- 태그: dotnet #nuget #security
주요 내용
- NuGet 패키지가 라이브러리 외에 MSBuild props/targets와 Roslyn 분석기를 포함해 복원·빌드 시점에 코드를 실행할 수 있다는 공급망 위험
IncludeAssets="compile;runtime"으로 분석기와 빌드 타겟을 제외하는 기본 완화 방법ExcludeAssets="analyzers;build;buildMultitargeting;buildTransitive"로 특정 자산만 선택적으로 제외- 런타임 전용·빌드 도구 전용 등 시나리오별 설정과
PrivateAssets="all"조합 - 일부 패키지는 분석기·빌드 타겟이 정상 동작에 필수라 설정 변경 후 테스트가 필요하다는 주의사항
가벼운 읽을거리
후보 항목 중 이슈로 선정되지 않은 가벼운 읽을거리들
Testcontainers — 일회용 Docker 컨테이너 속 실제 의존성
- docker-compose 대비 수명주기 자동 정리·준비 상태 감지·동적 포트의 이점과 xUnit
IAsyncLifetime기반DatabaseFixture구현 - HTTP 헬스체크·로그 메시지 기반 대기 전략, Ryuk 정리 컨테이너, 고정 지연·포트 하드코딩 등 안티패턴 정리
.NET OSS 재라이선싱 논란은 인센티브 문제다
- IdentityServer 상용화 성공 이후 유지보수자들의 재라이선싱 흐름과, .NET Foundation이 소속 프로젝트의 재라이선싱을 막을 권한이 없다는 구조적 한계
- OSMF(소스 유지, 바이너리 배포에만 수수료)와 완전한 재라이선싱(MIT→BSL)의 구분, 기부가 아닌 계약 기반 고객 관계로 전환하자는 제안
Blazor를 위한 보안 헤더와 CSP — 미들웨어 하나로 모든 호스트에
- Blazor는
'wasm-unsafe-eval'이 필요해 CSP를 잘못 조이면 앱이 하얗게 로드되며 부팅에 실패하는 문제 ICspPolicyProvider로 CSP를 런타임 해석하고, API origin 파싱 실패 시 강제 차단 대신 Report-Only로 강등해 설정 오류가 장애로 번지지 않게 하는 구성
DocumentDB Orleans 스택
- Orleans의 클러스터 멤버십·그레인 스토리지·리마인더·그레인 디렉토리·스트림 5개 지속성 계층을 단일
IDocumentStore추상화로 통합 - ETag 기반 CAS로 동시성을 제어하고, 트랜잭션 내 행 수준 잠금으로 스트림 시퀀스 번호를 할당해 삽입 순서와 커밋 순서를 일치시키는 구현
패키지된 애플리케이션의 유형 (MSIX)
- 패키지된 프로세스를 RuntimeBehavior 속성 기준으로 Universal(UWP), Centennial(Desktop Bridge), Win32alacarte 세 유형으로 분류
- Centennial은 보안·정리를 목표로 Windows 10 RS1에, Win32alacarte는 호환성을 최우선으로 20H1에 도입된 설계 배경
쓰레기 코드, 깨끗한 메모리 — .NET 가비지 컬렉터 이해하기
- Gen 0/1/2 세대별 수집과 SOH·LOH(85KB 이상)·POH·FOH 힙 영역, 표시-계획-재배치-압축 단계
ArrayPool<T>,string.Create,ref readonly등 할당 감소 패턴과 dotnet-gcdump·PerfView 진단 도구, .NET 10의 DATAS와 JIT 이스케이프 분석
Aspire 13.5
- Kubernetes 영구 볼륨을 C#/TypeScript로 모델링(스토리지 클래스·용량·접근 모드 설정, PVC 자동 생성)하고
WithTerminal()로 대시보드 내 인터랙티브 터미널 세션 지원 AsExistingInResourceGroup등으로 기존 Azure 리소스를 스코프별로 참조, 실험 API 표기와aspire update --migrate마이그레이션 경로 명시
Windows Package Manager v1.29.290
- 실험 기능 Source Priority로 소스별 우선순위를 지정해 모호성 발생 시 프롬프트 대신 최우선 소스를 자동 선택
- export 시 설치에 쓴
--override/--custom인자를 캡처해 import에서 재적용, 모든 명령에--no-progress플래그 추가
SDK 없이 .NET 도구 실행하기
- .NET 10
dnx가 캐시된 도구 실행에도 SDK 오버헤드를 요구하는 문제를 Native AOT로 빌드한ndx로 해결, 명령 문법은 그대로 유지 - 콜드 캐시 실행 기준
dnx580ms 대ndx10ms, NuGet 캐시를 공유하며.netconfig로 플로팅 버전 자동 업데이트
