재우니 개발자 블로그

Mapperly로 배우는 실전 .NET 객체 매핑

🚀 서론: 왜 Mapperly인가?

.NET 개발자라면 DTO 변환이나 API 모델 매핑 때문에 반복적인 코드를 수도 없이 작성해 보셨을 겁니다.
예를 들어, User 엔티티를 UserDto로 바꿔야 한다면 속성 하나하나를 직접 할당해야 하죠. 작은 프로젝트에서는 괜찮지만, 규모가 커지면 유지보수 지옥이 찾아옵니다.

 

 

여기서 Mapperly가 빛을 발합니다.
Mapperly는 Roslyn 소스 생성기를 활용해 빌드 시점에 매핑 코드를 자동 생성합니다. 즉, 런타임에 리플렉션을 쓰지 않고 순수 C# 코드처럼 빠르게 동작하죠.

이 글에서는 실무에서 자주 마주치는 매핑 패턴을 중심으로 Mapperly 활용법을 알려드립니다.

 


 

 

 

🔧 본론: 실무에서 자주 쓰는 Mapperly 패턴

1. 가장 기본적인 DTO 변환

예를 들어 User → UserDto 매핑이 필요하다고 합시다.

public class User
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

public class UserDto
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

 

 

Mapperly 매퍼 선언은 이렇게 간단합니다 👇

[Mapper]
public static partial class UserMapper
{
    public static partial UserDto ToDto(User user);
}

 

 

빌드 후 생성된 코드는 다음과 같아요:

public static partial UserDto ToDto(User user)
{
    var dto = new UserDto();
    dto.Id = user.Id;
    dto.Name = user.Name;
    return dto;
}

 

 

👉 핸드코딩과 똑같이 깔끔하게 생성됩니다.


2. 속성명이 다른 경우

실무에서는 엔티티와 DTO 속성명이 다른 경우가 흔합니다.

public class Product
{
    public int Id { get; set; }
    public string Title { get; set; } = string.Empty;
}

public class ProductDto
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty; // Title → Name
}

 

 

이럴 때는 MapProperty로 매핑을 지정합니다.

[Mapper]
public static partial class ProductMapper
{
    [MapProperty(nameof(Product.Title), nameof(ProductDto.Name))]
    public static partial ProductDto ToDto(Product product);
}

 

➡️ 속성명이 달라도 문제없이 매핑됩니다.


3. 컬렉션 매핑

Order 엔티티 안에 OrderItem 리스트가 있고, 이를 OrderItemDto 리스트로 매핑해야 한다고 해봅시다.

public class Order
{
    public List<OrderItem> Items { get; set; } = [];
}

public class OrderDto
{
    public List<OrderItemDto> Items { get; set; } = [];
}

 

 

Mapperly는 컬렉션도 자동으로 처리합니다.

[Mapper]
public static partial class OrderMapper
{
    public static partial OrderDto ToDto(Order order);
}

 

 

➡️ 빌드하면 내부적으로 foreach 루프를 생성해 줍니다.


4. 열거형 매핑 (Enum)

열거형 값이 다를 때도 흔히 문제가 되는데요.

public enum Status { Pending = 1, Completed = 2 }
public enum StatusDto { Waiting = 1, Done = 2 }

 

 

숫자 값이 다르면 Mapperly가 오류를 냅니다.
이때 EnumMappingStrategy.ByName을 쓰면 이름 기준으로 매핑됩니다.

[Mapper(EnumMappingStrategy = EnumMappingStrategy.ByName)]
public static partial class StatusMapper
{
    public static partial StatusDto ToDto(Status status);
}

 

 


5. EF Core 성능 최적화 (IQueryable Projection)

실무에서 가장 많이 쓰이는 패턴 중 하나는 DB에서 엔티티 → DTO 변환입니다.

[Mapper]
public static partial class UserMapper
{
    public static partial UserDto ToDto(User user);

    // IQueryable projection 지원
    public static partial IQueryable<UserDto> ProjectToDto(IQueryable<User> query);
}

 

 

이제 EF Core에서 이렇게 사용할 수 있습니다.

var dtos = dbContext.Users.ProjectToDto().ToList();

 

➡️ Mapperly가 LINQ Expression으로 변환해주기 때문에 DB에서 필요한 컬럼만 SELECT 하게 됩니다. (성능 ⬆️)


 

 

🎯 결론: 실무에서 Mapperly가 주는 가치

제 경험상, AutoMapper 같은 리플렉션 기반 매퍼보다 Mapperly는 다음과 같은 장점이 있습니다.

  • 속도: 런타임 리플렉션 없음 → DTO 변환 속도가 눈에 띄게 빨라집니다.
  • 안정성: 빌드 시 매핑 검증 → 런타임 버그 감소
  • 유지보수성: 생성된 코드가 사람이 작성한 것과 똑같아서 디버깅이 쉬움
  • 현대적 개발 환경 대응: AOT, 트리밍, 서버리스 환경에서도 완벽 호환

즉, Mapperly는 실무에서 "안전하고 빠른 매핑"을 하고 싶을 때 선택해야 할 도구입니다.


❓ Q&A

Q1. AutoMapper에서 Mapperly로 옮길 수 있나요?
A. 네, 기존 매핑 로직을 점진적으로 Mapperly로 교체할 수 있습니다.

Q2. 매핑이 잘못되면 어떻게 알 수 있나요?
A. 빌드 시점에 오류나 경고 메시지를 통해 바로 확인할 수 있습니다.

Q3. 성능 차이가 정말 크나요?
A. 특히 EF Core와 같이 대량 데이터를 다루는 경우 성능 차이가 확실하게 납니다.


🏷️ 관련 태그

  • #dotnet
  • #mapperly
  • #객체매핑
  • #자동매핑
  • #efcore
  • #성능최적화

🎨 이미지 프롬프트 제안

  1. 서론: “개발자가 반복적인 매핑 코드를 지우고 자동화된 코드가 생성되는 장면”
  2. 속성명이 다른 경우: “두 개의 다른 이름 속성이 연결되는 다이어그램”
  3. 컬렉션 매핑: “리스트 아이콘이 DTO 아이콘으로 변환되는 과정”
  4. Enum 매핑: “이름은 같지만 숫자가 다른 두 Enum이 일치하는 모습”
  5. EF Core Projection: “SQL SELECT가 DTO로 직접 변환되는 인포그래픽”
  6. 결론: “개발자가 성능과 생산성을 동시에 잡고 웃는 모습”