기존 프로젝트에서 Dto Entity를 매핑할 때 model mapper 라이브러리를 사용했었다.
편하게 사용할 수 있었지만
내부적으로 리플렉션을 이용하기 때문에, 성능 상 문제가 있다.
이번에는 많이들 추천하는 Map Struct를 사용해봤다.
적용하면서 자잘한 문제들이 있었는데, 내가 겪은 문제점들에 대한 해결 방법들을 정리해보겠다.
https://mapstruct.org/documentation/dev/reference/html/
=> 앞서 말하면 Map Struct 공식문서를 보면 대부분은 알 수가 있다.
의존성 추가
implementation "org.mapstruct:mapstruct:1.5.2.Final"
compileOnly 'org.projectlombok:lombok'
annotationProcessor "org.mapstruct:mapstruct-processor:1.5.2.Final"
annotationProcessor 'org.projectlombok:lombok', "org.projectlombok:lombok-mapstruct-binding:0.2.0"
롬복과 같이 사용하면 충돌이 난다고는 하지만, 최신 버전에서는 그런게 없다고 한다.
충돌을 피하려면 롬복 전에 추가하면 된다.
Generic Mapper 생성
public interface GenericDtoMapper<D, E> {
D toDTO(E e);
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
void updateFromVO(D dto, @MappingTarget E entity);
}
public interface GenericEntityMapper<D, E> {
E toEntity(D d);
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
void updateFromVO(D dto, @MappingTarget E entity);
}
=> 먼저 Entity 매퍼와 Dto 매퍼 인터페이스를 정의했다. 모든 타입을 받기 위해 Generic을 적용시켰다.
@Mapper(componentModel = "spring")
public interface UserJoinMapper extends GenericEntityMapper<JoinRequestDto, User> {
}
=> @Mapper 애노테이션을 통해 MapStruct가 구체화할 매퍼를 정의한다.
=> 이런 식으로 확장해 구체화하면 된다.
=> componentModel 이라는 애노테이션 속성을 통해 Spring Bean으로 등록해서 사용 가능하다.
문제점
=> 일반적인 필드들의 이름이 같거나 틀려도 @Mapping 애노테이션이나 @ValueMapping으로 가능한데,
참조 타입의 필드는 매핑에 좀 더 신경을 써줘야 한다.
@Mapping(target = "userId", source = "id")
@Override
DetailResponseDto toDTO(User user);
각 객체의 필드 이름이 틀릴 때 매핑 애노테이션 활용
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
public class DetailResponseDto {
private Long id;
private String name;
private String categoryName;
private Integer price;
private Integer quantity;
private String content;
@Setter
private String image;
}
매핑할 DTO
@Entity
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Product extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@JsonBackReference
@ManyToOne
@JoinColumn(name = "category_id")
private Category category;
private Integer price;
private Integer quantity;
private String content;
private String image;
@Enumerated(EnumType.STRING)
@Column(name = "product_status", nullable = false)
private ChickenStatus status;
매핑할 Entity
@Mapper(componentModel = "spring")
public interface ItemDetailMapper extends GenericDtoMapper<DetailResponseDto, Product> {
@Mapping(target = "categoryName", source = "product.category.categoryName.chickenName")
@Override
DetailResponseDto toDTO(Product product);
}
매핑 인터페이스 정의
카테고리는 Enum 타입이기 때문에, DTO 매핑 시 String으로 변경해야 한다.
소스 객체인 Product 엔티티의 하위 필드들을. 연산자를 활용해 직접적으로 매핑한다.
대략적으로 매핑에 대한 문제들은 이렇게 해결할 수 있다.
반응형
'📕 Spring Framework > Spring Project' 카테고리의 다른 글
결제 API 리팩토링 - [1] (feat. 전략 패턴) (2) | 2022.09.20 |
---|---|
동시성 조회 문제 해결 및 성능에 관한 고민 [Lock, Queue, Redis] (0) | 2022.09.14 |
DB 수정 & jpa 세팅 (0) | 2022.08.08 |
코드 리팩토링 [1] (0) | 2022.08.03 |
리팩토링 계획 (0) | 2022.07.28 |