개발 노트/ASP.NET Core

MediatR 유료화를 보며 CQRS를 다시 생각했습니다

Roslyn 2026. 7. 5. 20:36
반응형

CQRS 패턴을 닷넷에서 이야기할 때 MediatR은 거의 자연스럽게 함께 언급되곤 했습니다. Command와 Query를 나누고, Controller나 Minimal API endpoint에서는 요청만 보내고, 실제 처리는 Handler에 맡기는 구조가 익숙했기 때문입니다.

저도 이 흐름이 꽤 마음에 들었습니다. 기능이 늘어날수록 Controller가 비대해지는 것을 막을 수 있고, 요청 단위로 코드를 나누면 나중에 다시 열어봤을 때 흐름을 따라가기 쉬웠습니다. 특히 혼자 서비스를 만들고 운영할 때는 “이 기능이 어디에 있지?”를 빨리 찾는 것이 생각보다 중요합니다.

CQRS는 거창한 구조라기보다 역할을 나누는 방식입니다
CQRS는 Command Query Responsibility Segregation의 줄임말입니다. 말은 조금 무겁지만, 기본 생각은 단순합니다. 데이터를 바꾸는 요청과 데이터를 조회하는 요청을 구분하자는 것입니다.

예를 들어 게시글을 등록하는 작업은 Command에 가깝습니다. 반대로 게시글 목록을 읽어오는 작업은 Query에 가깝습니다. 이렇게 나누면 코드의 목적이 분명해집니다. 이 코드는 상태를 바꾸는 코드인지, 단순히 읽어오는 코드인지가 파일 이름과 구조에서 바로 드러납니다.

여기에 중재자 패턴을 더하면 호출하는 쪽과 처리하는 쪽의 연결이 느슨해집니다. API는 “이 요청을 처리해 주세요”라고만 말하고, 실제 Handler를 찾아 실행하는 일은 중재자가 맡습니다. MediatR은 이 역할을 닷넷에서 아주 편하게 제공해준 대표적인 라이브러리였습니다.

MediatR의 라이선스 변화는 한 번쯤 생각해볼 계기가 됐습니다
최근 MediatR은 13.0.0 버전부터 라이선스 정책이 바뀌었습니다. 공식 안내에 따르면 MediatR 13.0.0 이후 버전은 Lucky Penny Software의 상업 라이선스 대상이며, 그 이전 버전은 기존 오픈소스 라이선스 조건이 유지됩니다. 현재 NuGet 기준 최신 MediatR은 14.x 계열까지 올라와 있습니다.

이 변화 자체를 좋다거나 나쁘다고 단정하고 싶지는 않습니다. 오픈소스 유지에도 비용과 시간이 들고, 오랫동안 널리 쓰인 라이브러리가 지속 가능한 방식을 찾는 것은 자연스러운 일입니다. 다만 사용하는 입장에서는 이제 “그냥 늘 쓰던 패키지니까 넣는다”에서 한 번 더 멈춰 생각하게 됩니다.

특히 작은 서비스나 개인 프로젝트에서는 의존성을 하나 추가하는 일도 운영의 일부입니다. 라이선스, 업데이트, 보안, 장기 유지보수까지 함께 따라옵니다. 그래서 MediatR의 변화는 단순히 비용 문제가 아니라, 이 정도 구조를 꼭 외부 라이브러리에 맡겨야 하는가를 다시 묻게 만들었습니다.

요즘 닷넷에서는 직접 구현도 크게 어렵지 않습니다
예전에는 이런 구조를 직접 만들려면 DI 컨테이너나 리플렉션 처리 때문에 신경 쓸 일이 많았습니다. 하지만 지금의 닷넷은 기본 의존성 주입 기능이 꽤 강해졌습니다. ASP.NET Core는 기본적으로 IServiceProvider와 IServiceCollection 기반의 DI 컨테이너를 제공하고, 서비스 수명 관리나 생성자 주입도 자연스럽게 사용할 수 있습니다.

.NET 8 이후에는 Keyed Services도 공식적으로 제공됩니다. 같은 인터페이스의 여러 구현을 키로 구분해 등록하고 가져오는 방식입니다. 물론 CQRS용 중재자를 만들 때 반드시 Keyed Services가 필요한 것은 아니지만, 닷넷의 기본 DI가 예전보다 훨씬 표현력이 좋아졌다는 점은 분명합니다.

간단한 CQRS 구조라면 직접 만드는 것도 어렵지 않습니다. IRequest, IRequestHandler 같은 인터페이스를 만들고, Dispatcher가 IServiceProvider를 통해 알맞은 Handler를 찾아 실행하도록 구성하면 됩니다. 여기에 필요한 공통 로깅이나 트랜잭션 처리는 프로젝트 상황에 맞게 조금씩 붙이면 됩니다.

직접 만든다는 것은 모든 기능을 다시 만들겠다는 뜻은 아닙니다
물론 MediatR이 해주던 모든 기능을 직접 다시 만들겠다는 뜻은 아닙니다. MediatR은 요청/응답, 알림, 파이프라인 동작, 예외 처리 등 여러 기능을 안정적으로 제공해왔습니다. 이미 그 기능들을 깊게 사용하고 있다면 계속 사용하는 편이 더 현실적일 수 있습니다.

다만 많은 프로젝트에서는 MediatR의 모든 기능을 쓰지 않습니다. Command 하나 보내고 Handler 하나 실행하는 정도로만 사용하는 경우도 많습니다. 그런 상황이라면 작은 Dispatcher를 직접 두는 편이 오히려 더 단순할 수 있습니다. 외부 패키지의 변화에 덜 흔들리고, 우리 프로젝트에 필요한 만큼만 유지하면 되기 때문입니다.

저는 이 지점이 중요하다고 느꼈습니다. 직접 구현은 라이브러리를 부정하는 선택이 아닙니다. 지금 내 서비스 규모에서 필요한 추상화가 어디까지인지를 다시 정리하는 일에 가깝습니다.

마무리
MediatR의 유료화는 닷넷 개발자들에게 작은 불편함이자, 동시에 좋은 점검 기회가 된 것 같습니다. 그동안 너무 자연스럽게 추가하던 의존성이 정말 필요한지, 아니면 지금의 닷넷 기본 기능만으로도 충분한지 돌아보게 만들었기 때문입니다.

제 기준에서는 단순한 CQRS와 중재자 패턴 정도라면 이제 직접 구현해서 사용하는 것도 충분히 현실적인 선택입니다. 닷넷의 기본 DI가 좋아졌고, 작은 서비스에서는 필요한 만큼만 단순하게 가져가는 편이 오래 운영하기에 더 편할 때가 있습니다.

결국 선택은 프로젝트의 크기보다 운영 방식에 달려 있습니다. 이미 MediatR의 풍부한 기능을 잘 쓰고 있다면 유지하는 것이 맞고, 단순한 요청 분리만 필요하다면 직접 구현도 좋은 선택이 될 수 있습니다. 이번 기록은 어느 한쪽을 정답으로 정하기보다, 의존성을 추가하기 전에 한 번 더 생각해보자는 기준에 가깝습니다.

반응형