MSA 기반 이커머스 애플리케이션 토이 프로젝트 ‐ MapStruct 도입 - dnwls16071/Backend_Summary GitHub Wiki

❗문제

  • MSA 기반 이커머스 애플리케이션 토이 프로젝트를 진행하면서 DTO를 사용하게 되었다.
  • 각 레이어 간 DTO의 결합도를 낮추기 위해 DTO간의 변환 로직을 작성해주었는데 데이터 전달 목적이라는 취지로 만들어진 DTO에서 변환 로직을 작성해주는게 SRP 원칙을 위반하는 것이 아닐까라는 생각이 문득 들었다.
  • ModelMapper와 같은 일반적인 매퍼를 사용하면 매핑에 필요한 로직이 여러 서비스에 모두 분산되어 매핑이 필요한 곳에서는 많아지게 되면서 코드의 관리가 어렵다는 생각이 들었다.
  • 코드 복잡성과 관리 어려움에서 벗어나기 위해 MapStruct를 적용해보았다.

🧠과정

MapStruct

  • MapStruct는 Java Bean 유형 간 매핑 구현을 단순화하는 코드 생성기이다.
  • 컴파일 시점에 코드를 생성해 런타임에서 안전성을 보장한다.
  • 다른 매핑 라이브러리보다 속도가 빠르다.
  • 반복되는 객체 매핑에서 발생할 수 있는 오류를 줄일 수 있으며 구현 코드를 자동으로 만들어준다는 점이 좋아 사용이 용이하다.
  • Annotation Processor을 이용하여 객체 간 매핑을 자동으로 제공한다.
  • 다만 Lombok 라이브러리에 먼저 의존성 추가가 되어있어야 한다. MapStruct는 Lombok의 Getter/Setter/Builder를 이용하여 생성되기 때문에 Lombok보다 먼저 의존성이 선언된 경우 실행할 수 없다.
// MapStruct for object mapping
implementation group: 'org.mapstruct', name: 'mapstruct', version: '1.6.3'
annotationProcessor group: 'org.mapstruct', name: 'mapstruct-processor', version: '1.6.3'
  • 위와 같이 디펜던시를 추가하고 매핑을 위한 인터페이스를 만들어준다.
@Mapper(componentModel = "spring")
public interface UserMapper {

    // (User) : Request -> Command
    CreateUserCommand toCommand(CreateUserRequest request);

    // (User) : Command -> Entity
    User toEntity(String email, String name, String password);
}
  • MapStruct는 Annotation Processor를 이용하는 매핑인 만큼, Annotation을 통합 옵션이나 매핑에 대한 정책을 @Mapper에 설정할 수 있다.
  • 매퍼를 빈으로 만들어야 하는 경우, 위와 같이 설정하면 빈으로 등록할 수 있다.
  • 생성된 매퍼는 싱글톤 범위의 빈으로 동작한다.

🚀결론

  • DTO 간의 변환 로직을 DTO에서 처리하면서 발생했던 SRP 원칙의 위배를 해결하기 위해 여러 방법을 고민해보았다.
  • MSA 환경이라는 점을 감안해 각 도메인별로 독립적인 매퍼 관리가 가능하기 떄문에 MapStruct를 우선적으로 고려했고 타입 안정성과 성능 확보는 물론 보일러플레이트 코드의 감소라는 이점도 누릴 수 있었다.
  • 은탄환은 없다라는 말이 있듯이 MapStruct가 장점만 가지고 있는 것은 아니다.
    • 복잡한 비즈니스 로직이 포함된 매핑에는 부적합하다.
    • 컴파일 시점 의존성으로 인한 빌드 시간이 증가한다.
    • IDE에서 생성된 코드 디버깅의 어려움이 발생한다.