쌓고 쌓다

Entity와 DTO의 분리 필요성 및 방법 본문

프로그래밍/spring

Entity와 DTO의 분리 필요성 및 방법

승민아 2024. 1. 22. 13:30

 

Entity 자체를 요청과 응답을 처리하는데 그대로 사용하니 다음과 같은 문제 또는 비효율적인 부분이 발생했다...

 

먼저 회원 엔티티를 보자.

@Entity
@Data
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;


    private String loginId;
    private String loginPwd;

    private String name;

    @Enumerated(EnumType.STRING)
    private Role role;

    @CreationTimestamp
    private LocalDateTime regDate;

    @Enumerated(EnumType.STRING)
    private MemberType type;

    private Long snsId;

}

 

이제 발생한 문제들을 간단히 보자.

 

1. 의도하지 않은 데이터 저장

회원가입시 다음과 같이 컨트롤러에서 Member 엔티티를 그대로 받아 가입을 진행했더니...

 

회원가입 요청시 유저 권한을 나타내는 role이나 소셜가입시 발급되는 snsId 컬럼에 값을 넣을 수 있다.

{
    "name" : "TESTNAME6",
    "loginId" : "test6",
    "loginPwd" : "1234",
    "snsId" : 7777,
    "type" : "ADMIN"
}

의도하지 않은 snsId, type이 그대로 디비에 넣어가버린다.

물론 값을 초기화시켜주면 해결할 수 있지만 근본적으로 문제가 있다는게... 문제다!

 

2. 순환 참조 및 불필요한 응답 데이터 처리

JPA를 사용하다보면 양방향 순환 참조가 발생할 수 있다.

예를 들어 게시글에 회원을 참조하고있고 회원은 게시글을 참조하고있다고하자.

 

이때 한 엔티티를 그대로 응답으로 보낸다면

게시글 -> 회원 -> 게시글 -> 회원 -> 무한반복

으로 순환 참조가 발생하여 문제가 된다.

 

실제로 예전에 직접 문제를 겪으며 굳이 DTO를 만들어야할까 싶어

직렬화, 역직렬화를 막는 어노테이션을 사용했지만

역시나 DTO를 만들게 되었다.

 

https://non-stop.tistory.com/560

 

양방향 연관관계 매핑 JSON 순환참조 문제

에러 발생 java.lang.IllegalStateException: Cannot call sendError() after the response has been committed org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError)] java.lang.StackO

non-stop.tistory.com

 

또한 응답으로 불필요한 데이터가 들어가 있다.

 

하나의 게시글 엔티티를 그대로 응답으로 보냈더니 참조되는 작성자 필드까지 JSON으로 변환하여

Member 클래스 또한 직렬화하여 모든 필드가 응답으로 보내지게 된다.

 

3. 엔티티 구조

엔티티에 @NotBlank와 같은 Validation을 위한 코드, @ManyToOne과 같은 연관관계 매핑,

직렬화 역직렬화 방지를 위한 @JsonIgnore 코드,,, 엔티티가 어질어질해진다.

DTO를 분리하여 검증과 모델링 코드를 분리할 수 있다.

 

 

그래서 이제 다음과 같은 디렉토리 구조로 DTO를 분리할 것이다.

 

 

MemberRequest

package com.example.spotserver.dto.request;

import com.example.spotserver.domain.Member;
import lombok.Setter;
import lombok.ToString;

@ToString
@Setter
public class MemberRequest {
    private String loginId;
    private String loginPwd;
    private String name;


    public Member toEntity(MemberRequest memberRequest) {
        Member member = new Member();
        member.setLoginId(memberRequest.loginId);
        member.setLoginPwd(memberRequest.loginPwd);
        member.setName(memberRequest.name);
        return member;
    }
}

DTO에서 엔티티로 저장하는 과정이 필요하므로 요청Dto에 엔티티로 변화하는 메서드를 작성했다.

 

@Setter는 JSON으로 바디에 들어온 요청을 스프링이 HttpMessageConverter로

객체로 변환하는데 set 메서드를 사용하기에 필요하다.

 

MemberResponse

package com.example.spotserver.dto.response;

import com.example.spotserver.domain.Member;
import com.example.spotserver.domain.Role;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;


@Getter
public class MemberResponse {

    private Long id;
    private String name;
    private Role role;

    public MemberResponse toDto(Member member) {
        MemberResponse memberResponse = new MemberResponse();
        memberResponse.id = member.getId();
        memberResponse.name = member.getName();
        memberResponse.role = member.getRole();
        return memberResponse;
    }
}

Entity를 응답DTO로 변환하는 과정을 거치므로 응답DTO에 DTO로 변환하는 메서드를 작성했다.

 

@Getter는 HttpMessageConverter가 JSON으로 응답을 보내기위해 get 메서드를 사용하기에 Getter가 필요하다.

Comments