Spring/Spring Boot

[Spring] JPA FindAll<Entity> to convert DTO (ModelMapper)

민돌v 2021. 12. 3. 00:30
728x90

Spring에서는 DTO를 이용해서 사용자에게 Request를 받고, Response를 보냄으로써, 데이터를 보호한다.

Request를 DTO로 받는방법은 정말 많고, 자료도 많지만 Response를 DTO로 변환하는 방법은 잘 나오지 않아 정리해 보고자 한다.

 

1) 가장 쉬운 방법

가장 쉬운 방법은 역시 노가다다.

Response로 보내기 위한 엔티티 정보를 불러오와서, Setter로 하나씩 DTO 클래스에 입력해주면된다.

하지만 이건 쿨하지 못하다.

 

2) ModelMapper

이 글의 주된 목표이다.

ModelMapper를 사용하면, 비교적 쿨하게, 한방에, 센스있는 converting이 가능하다.

 

 

ModelMapper란

ModelMapper의 목표는 특정 사용 사례를 처리하기 위한 간단하고 리팩토링이 안전한 API를 제공하면서 사람이 하는 것과 같은 방식으로 규칙에 따라 한 개체 모델이 다른 개체 모델에 매핑되는 방식을 자동으로 결정하여 개체 매핑을 쉽게 만드는 것입니다.

라고 공식 문서에 나와있다.

 

간단하게 정리하면,

서로 다른 object 간의 필드 값을 자동으로 mapping 해주는 library이며

사람이 하는 것과 유사하게, 유연한 객체 변화를 해준다는 것이다.

 

이게 무슨 말이냐면, 딱히 특별한 설정을 해주지 않아도, Type이나 변수명으로 알아서 매핑할 필드를 찾아 Convert 해준다.

 

사용해보면 매우 놀랍다.

 

Modelmapper 사용 법

1) import  최신버전을 추가해 주면 된다.

Maven

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>2.3.8</version>
</dependency>

gradle

implementation group: 'org.modelmapper', name: 'modelmapper', version: '2.3.8'

 

2) 의존성 주입

Modelmapper를 DI(의존성) 주입 처리를 해서 사용할 떄마다 생성하지 않도록, @Bean으로 등록한다.

BeanConfiguration.class

@Configuration
public class BeanConfiguration {

    @Bean
    public ModelMapper modelMapper(){
        return new ModelMapper();
    }

}

 

3) 사용 방법 코드

Entity To DTO

이 한줄로 간단하게 사용할 수 있다.

modelMapper.map(post,PostResponseDto.class);

//조회
    @GetMapping("/posts/detail")
    public PostResponseDto getPostDetail(@RequestParam Long id){
        Post post = postService.getPostDetail(id);

        PostResponseDto postResponseDto =  modelMapper.map(post,PostResponseDto.class);
        return postResponseDto;
    }

특별한 설정이 없어도, Modelmapper가 알아서 센스있게 변수명과 타입을 보고 매핑을 해준다.

 

DTO

@Getter
@Setter
public class PostResponseDto {
    private Long idx;    //idx
    private String title;
    private String content;
    private String createdAt;  //생성시간
    private String img;
    private String username;
    private int view;
    private List<PostComment> comments;
}

Entity(post)

@Entity
@Getter
@Setter
@NoArgsConstructor
@DynamicInsert
@Table(name = "post")
public class Post extends Timestamped {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long idx;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String content;

    @Column
    private String img;

    @Column(columnDefinition = "int default 0")
    private int view;

    @Column(nullable = false)
    private Long userId;

    @Column(nullable = false)
    private String username;

    @JsonIgnore
    @OneToMany(mappedBy = "post")
    private List<PostComment> comments = new ArrayList<PostComment>();

 

주의할 점

modelmapper는 Setter를 이용해서 데이터를 세팅하기 때문에 Getter와 Setter가 필수다.

Setter를 생성하지 않으면 모두  Null값으로 들어간다.

 

또한, 데이터의 연산이라고 할 수 도있지만, Controller 부분에서 처리를 해주는 것이, 데이터를 유연하게 바꿔주어 개발할 때 중복 코드를 줄일 수 있는 방법이라고 생각한다.

Service나 Domain부분에서 처리를 해준다면, 다른 타입과 모양으로 컨버팅을 하기 위해서는 매개변수만 다른 매서드를 똑같이 만들어주어야 하기 때문이다. 

 

 

List<Entity> To List<DTO>

그렇다면 엔티티 리스트를 DTO리스트로 어떻게 바꿔야할 까?

정답은 Stream() 문법이다.

 

Stream() 문법은 자바 문법으로 문자 출력형식 말고, 람다와 반복문을 섞은 JAVA8부터 적용이된 메서드이다.

 

public List<PostResponseDto> getPostList(){
        List<Post> postList = postrepository.findAll();
        
        //1번
        List<PostResponseDto> resultList = postList.stream().map(post -> modelMapper.map(post, PostResponseDto.class)).collect(Collectors.toList());
        
        //2번
        List<PostResponseDto> resultList = Arrays.asList(modelMapper.map(postList,PostResponseDto[].class));

        return resultList;
    }

1번처람 Stream함수를 이용해도 되고, 

2번처럼, 배열처리를 한 후 리스트로 바꿔도 된다.

 

둘 다 잘 된다.

 

 


참고

반응형