BE/Java

MapStruct 예제 및 리스트 매핑

멍목 2024. 7. 7. 19:53
반응형

MapStruct는 객체끼리 매핑을 해주는 유용한 기능이다.

 

간단하게 아래의 예로 사용법을 알아보자.


준비사항

  • MapStruct는 Getter, Setter or builder 를 사용하기 때문에 lombok이 있으면 더욱 편리하게 사용할 수 있다.
  • dependency 설정 시, Lombok 뒤에 MapStruct를 넣어줘야 정상적으로 실행 가능하다.
  • 매핑에 진행하기 앞서, 반드시 매핑을 진행하는 두 객체는 아래와 같은 설정을 해야 한다. 
    • 반환 타입의 객체: Builder or Setter 필요 (반환 타입의 객체에 데이터를 설정해야 하기 때문)
    • 파라미터 타입의 객체: Getter 필요(파라미터 객체의 데이터를 읽어야 하기 때문)

 

 

Category.java (Entity)

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

@Getter
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "category")
public class Category {
    @Id
    private Integer id;
    private String name;
    private String upperId;
    private String isLeaf;
    private String useYn;
    private Long depth;
}

 

 

CategoryDto.java (Dto)

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

@Getter
@SuperBuilder
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class CategoryDto {
    private Integer categoryId;
    private String categoryName;
    private String upperId;
    private String isLeaf;
    private String useYn;
    private Long depth;
}

 

 

위와 같이 Entity와 Dto가 있을 때 Entity를 Dto로 변환이 가능하다. (물론 반대로도 가능하다)

위에서 언급한 MapStruct를 이용하면 된다.

 

MapStruct의 흐름은 매핑되는 인터페이스를 규칙에 맞게 작성하면, 빌드할 때 자동으로 해당 인터페이스의 구현체가 생성된다.

 

 

import org.mapstruct.*;

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.ERROR)
    public interface CategoryMapStruct {
    
    @Mapping(target = "categoryId", source = "id")
    @Mapping(target = "categoryName", source = "name")
    CategoryDto toDtoFromEntity (Category category);
    
    @Mapping(target = "id", source = "categoryId")
    @Mapping(target = "name", source = "categoryName")
    Category toEntityFromDto (CategoryDto categoryDto);
}
  • 인터페이스를 작성할 때 신경써야하는 부분은 반환 타입과 파라미터 타입이다.
  • 반환 타입과 파라미터 타입에 맞게 자동으로 매핑이 이루어진다.
  • @Mapping 
    • MapStruct는 기본적으로 두 객체간의 같은 이름의 컬럼에 대해서 자동으로 매핑을 도와준다.
    • 그렇다면, 서로 다른 이름을 가진 컬럼끼리 매핑을 할 수 없을 까?
    • @Mapping 어노테이션을 이용해서 명시적으로 서로 다른 이름의 컬럼을 매핑하는 규칙을 설정할 수 있다.
    • target은 반환 타입 객체의 컬럼 이름, source는 파라미터 타입 객체의 컬럼 이름이다.
  • toDtoFromEntity 를 보면, 반환되는 타입은 Dto 타입이고, 파라미터 타입은 Entity인 것을 볼 수 있다.
    즉, toDtoFromEntity 은 Entity를 Dto로 변경해주는 함수이다.
  • 이렇게 작성하고 정상적으로 build가 실행된다면, 해당 인터페이스의 이름 + Impl 이라는 구현체가 자동으로 생성된다.
    (위 예제에서는 CategoryMapStructImpl 이 될 것이다.)
  • @Mapper unmappedTargetPolicy : 매핑되지 않는 컬럼이 있을 때에 대한 설정
    • ReportingPolicy.IGNORE : 매핑되지 않는 컬럼이 있는 경우, 무시하는 설정
    • ReportingPolicy.WARN : 빌드 중 매핑되지 않는 컬럼이 있는 경우, 경고로 알려주는 설정
    • ReportingPolicy.ERROR : 빌드 중 매핑되지 않는 컬럼이 있는 경우, 빌드가 실패하도록 하는 설정

 

 

Collection  Mapping

import org.mapstruct.*;

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.ERROR)
    public interface CategoryMapStruct {
    
    @Named("toDtoFromEntity")
    @Mapping(target = "categoryId", source = "id")
    @Mapping(target = "categoryName", source = "name")
    CategoryDto toDtoFromEntity (Category category);
    
    @Named("toEntityFromDto")
    @Mapping(target = "id", source = "categoryId")
    @Mapping(target = "name", source = "categoryName")
    Category toEntityFromDto (CategoryDto categoryDto);
    
    
    // Collection 간에 매핑을 진행할 때 아래와 같이 @IterableMapping 어노테이션을 사용한다.
    @IterableMapping(qualifiedByName = "toDtoFromEntity")
    List<CategoryDto> toDtoListFromEntityList (List<Category categoryList);
    
    @IterableMapping(qualifiedByName = "toEntityFromDto")
    List<Category> toEntityListFromDtoList (List<CategoryDto> categoryDtoList);
    
    
}
  • Collection 간의 매핑 규칙은 @IterableMapping를 사용해야 한다.
    (만약, 두 객체간의 컬럼 이름이 모두 동일하다면 정상적으로 매핑) 
  • @Named 어노테이션으로 매핑 규칙을 설정하고, 위 소스처럼 @IterableMapping으로 규칙을 넣어줘야 Collection 간의 매핑이 가능하다.
반응형