Back-end/Spring

[Spring] DTO 간 복사(BeanUtils, ModelMapper커스텀 하는방법 포함)

shoney9254 2024. 2. 9. 13:18
반응형

DTO 간 복사

Spring에서 DTO 간 변수를 복사하는 경우, 주로 BeanUtils.copyProperties 메소드를 사용하거나 ModelMapper 라이브러리를 활용할 수 있습니다. 아래에 두 방법을 사용하는 예시를 제시하겠습니다.

1. BeanUtils 사용하기

BeanUtils.copyProperties 메소드는 스프링 프레임워크에 포함된 유틸리티 메소드로서, 한 객체의 프로퍼티 값을 다른 객체로 복사하는 데 사용됩니다. 이 방법은 추가적인 라이브러리를 필요로 하지 않으며, Spring Framework에 기본적으로 포함되어 있습니다.

import org.springframework.beans.BeanUtils;

// Source DTO
public class SourceDto {
    private String name;
    private int age;
    // getters and setters
}

// Target DTO
public class TargetDto {
    private String name;
    private int age;
    // getters and setters
}

SourceDto source = new SourceDto();
// source 객체 초기화

TargetDto target = new TargetDto();
BeanUtils.copyProperties(source, target);


2. ModelMapper 사용하기

 

ModelMapper는 더 복잡한 매핑이 필요할 때 유용하며, 객체 간의 데이터 매핑을 보다 세밀하게 제어할 수 있습니다. 이 라이브러리는 별도로 추가해야 합니다. ModelMapper를 사용하면 복잡한 매핑 규칙도 쉽게 정의할 수 있습니다.

<!-- pom.xml -->
<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>2.3.8</version>
</dependency>

import org.modelmapper.ModelMapper;

// Source DTO
public class SourceDto {
    private String name;
    private int age;
    // getters and setters
}

// Target DTO
public class TargetDto {
    private String name;
    private int age;
    // getters and setters
}

ModelMapper modelMapper = new ModelMapper();
SourceDto source = new SourceDto();
// source 객체 초기화

TargetDto target = modelMapper.map(source, TargetDto.class);

ModelMapper는 좀 더 복잡한 변환 작업에 유리하며, 자동 매핑 외에도 맞춤 매핑 전략을 정의할 수 있습니다. BeanUtils에 비해 세밀한 제어가 가능하지만, 성능면에서는 BeanUtils가 더 빠를 수 있습니다. 따라서 상황에 맞게 선택하여 사용하시면 됩니다.


3. ModelMapper 커스텀 하는 예시

 

ModelMapper는 복잡한 변환 작업에 유리한 여러 가지 이유가 있는데, 그 중 하나는 객체 간의 필드 이름이 다르거나, 변환 로직이 필요할 때 보다 유연하게 대응할 수 있다는 점입니다. 예를 들어, 한 객체의 필드를 다른 객체의 여러 필드로 나누거나, 반대로 여러 필드를 하나로 합치는 등의 작업을 ModelMapper를 사용하여 쉽게 처리할 수 있습니다.

아래 예시에서는 ModelMapper를 사용하여 서로 다른 필드 이름을 매핑하고, 특정 필드에 대해 커스텀 변환 로직을 적용하는 방법을 보여줍니다.

3-1. Source DTO

public class SourceDto {
    private String fullName; // TargetDto에서는 firstName과 lastName으로 나누어져 있음
    private String email;
    private String joinDate; // String 형식, TargetDto에서는 LocalDate 형식으로 변환 필요
    // getters and setters
}

3-2. Target DTO

public class TargetDto {
    private String firstName;
    private String lastName;
    private String email;
    private LocalDate joinDate; // SourceDto에서는 String 형식, 여기서는 LocalDate로 변환
    // getters and setters
}

3-3. ModelMapper 설정 및 사용

import org.modelmapper.ModelMapper;
import org.modelmapper.PropertyMap;
import org.modelmapper.Converter;
import org.modelmapper.spi.MappingContext;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

ModelMapper modelMapper = new ModelMapper();

// fullName을 firstName과 lastName으로 나누는 커스텀 매핑
modelMapper.addMappings(new PropertyMap<SourceDto, TargetDto>() {
    @Override
    protected void configure() {
        map().setEmail(source.getEmail());
        using((Converter<String, String>) context -> context.getSource().split(" ")[0]).map(source.getFullName(), destination.getFirstName());
        using((Converter<String, String>) context -> context.getSource().split(" ").length > 1 ? context.getSource().split(" ")[1] : null).map(source.getFullName(), destination.getLastName());
        // joinDate 변환
        using(new Converter<String, LocalDate>() {
            @Override
            public LocalDate convert(MappingContext<String, LocalDate> context) {
                return LocalDate.parse(context.getSource(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            }
        }).map(source.getJoinDate(), destination.getJoinDate());
    }
});

SourceDto source = new SourceDto();
// source 객체 초기화

TargetDto target = modelMapper.map(source, TargetDto.class);

이 예시에서는 ModelMapper의 addMappings 메소드를 사용하여 커스텀 매핑을 정의했습니다. fullName을 firstName과 lastName으로 나누는 로직과 joinDate를 String에서 LocalDate로 변환하는 로직을 구현했습니다. 이처럼 ModelMapper는 필드 이름이 다르거나, 특정 필드에 대한 변환 로직이 필요할 때 유연하게 대응할 수 있는 강력한 도구입니다.

반응형