Riok.Mapperly: AutoMapper의 AOT 식 솔루션

.NET으로 애플리케이션을 개발하는 과정을 진행하다보면, 아키텍처 분리를 시도하면서 필연적으로 DTO, 뷰 모델, 데이터베이스 모델 간에 중복되는 코드를 계속 만들고 유지해야 하는 고통이 따릅니다.

이 문제를 자동화하고 쉽게 풀 목적으로 각종 Mapper들이 그동안 많이 나왔습니다. 하지만 최근 .NET에 지향하는 AOT는 안타깝게도 이런 라이브러리들이 의존하는 리플렉션 때문에 제대로 쓰기 어렵습니다.

그러던 중에, AOT 시나리오에서도 mapper를 쓸 수 있게 함은 물론, 컴파일 타임에서 사전에 모델 매핑 상에서 발생할 수 있는 문제까지 잡아주는 소스 제네레이터 기반의 Model Mapper가 있어 소개드려봅니다. 바로 Riok.Mapperly 패키지입니다.

예를 들어 아래와 같은 코드가 있다고 가정해보겠습니다. Car 라는 논리 모델을 CarDto로 매핑하는 예시입니다.

#:package Riok.Mapperly@4.3.0

using Riok.Mapperly.Abstractions;

// Enums of source and target have different numeric values -> use ByName strategy to map them
[Mapper(EnumMappingStrategy = EnumMappingStrategy.ByName)]
public static partial class CarMapper
{
	[MapProperty(nameof(Car.Manufacturer), nameof(CarDto.Producer))] // Map property with a different name in the target type
	public static partial CarDto MapCarToDto(Car car);
}

public class Car
{
	public string Name { get; set; } = string.Empty;

	public int NumberOfSeats { get; set; }

	public CarColor Color { get; set; }

	public Manufacturer? Manufacturer { get; set; }

	public List<Tire> Tires { get; } = [];
}

public enum CarColor
{
	Black = 1,
	Blue = 2,
	White = 3,
}

public class Manufacturer
{
	public Manufacturer(int id, string name)
	{
		Id = id;
		Name = name;
	}

	public int Id { get; }

	public string Name { get; }
}

public class Tire
{
	public string Description { get; set; } = string.Empty;
}

//////////////////////////////////////////////////////////////////

public class CarDto
{
	public string Name { get; set; } = string.Empty;

	public int NumberOfSeats { get; set; }

	public CarColorDto Color { get; set; }

	public ProducerDto? Producer { get; set; }

	public List<TireDto>? Tires { get; set; }
}

// Intentionally use different numeric values for demonstration purposes
public enum CarColorDto
{
	Yellow = 1,
	Green = 2,
	Black = 3,
	Blue = 4,
}

// The manufacturer, but named differently for demonstration purposes
public class ProducerDto
{
	public ProducerDto(int id, string name)
	{
		Id = id;
		Name = name;
	}

	public int Id { get; }

	public string Name { get; }
}

public class TireDto
{
	public string Description { get; set; } = string.Empty;
}

거의 모든 멤버들이 1:1로 대응되지만, 문제가 있습니다. 아래 그림처럼 CarColor와 CarColorDto enum 사이에 불일치하는 멤버들이 존재한다는 사실입니다. 이런 부분을 지금 보시는 것처럼 컴파일러 수준에서 미리 체크가 됩니다.

또한 소스 제네레이터 타입의 패키지는 파일 기반 앱 환경에서도 매우 잘 작동하므로, CommunityToolkit.Mvvm 같은 패키지와 섞어서 써도 아주 좋습니다.

이 주제로 고민 중이신 분들을 위하여 정보를 공유드립니다. :smiley:

8 Likes

Mapperly는 Visual Studio 2022에서는 사용할 수 없나요?

using Riok.Mapperly.Abstractions;

namespace MapperlyTest;

[Mapper]
public partial class MinimalMapper
{
	public partial int Map(int value);
}

새 클래스 라이브러리 프로젝트를 생성 후 이렇게 간단한 소스를 작성해서 빌드하니 빌드는 정상적으로 되는데 구현 소스(g.cs) 파일이 생성이 안됩니다.

Visual Studio 2026에서 동일하게 하니까 잘 됩니다.

Visual Studio 2022 버그일까요? 아니면 Visual Studio 2022는 지원을 안하는것일까요?

Visual Studio 2022에서 오류 없이 사용할 수 있는 방법이 있을까요?

Source Generator nuget 패키지는 .NET 8부터 지원되는 기능이라 Visual Studio 2022에서도 잘 쓰이던 기능입니다. 혹시 프로젝트 타입이 .NET SDK 프로젝트인데 그런 상황이신걸까요? .NET Framework 프로젝트에서 사용하시려는 경우에는 레거시 MSBUILD 프로젝트 타입으로는 처리가 안되고, 반드시 .NET SDK 프로젝트로 마이그레이션해야 기능을 사용하실 수 있습니다.

.NET SDK 프로젝트이고 .NET 9로 만들었습니다. (Visual Studio 프로젝트 템블릿에서 클래스 라이브러리 선택)

지금까지 다른 소스 제너레이터 패키지들(Microsoft.Windows.CsWin32 등)은 문제 없이 잘 되었는데. 이번에 Mapperly를 사용해보려고 하니까 안되네요.

1 Like