개발천재

[Spring Boot] DTO와 Entity 간의 변환 본문

개발 준비/Spring Boot

[Spring Boot] DTO와 Entity 간의 변환

세리블리 2025. 2. 21. 21:57
반응형

Entity와 DTO의 역할 차이

Entity
DB와 직접 연결되는 핵심 모델

DTO
View나 API를 위한 가공된 데이터 객체

 

Entity는 데이터베이스 테이블과 1:1 매핑되는 객체이다.
Entity는 @Entity로 선언된 클래스를 말하며 데이터베이스의 구조(컬럼, 관계 등)를 그대로 반영한다. 비즈니스 로직을 직접 담지 않고, 순수한 데이터 구조만 가진다. 

 

DTO(Data Transfer Object)는 데이터를 전달하기 위한 객체이다.
Controller ↔ Service ↔ View(프론트엔드) 간의 데이터 전송에 사용하며, Entity와 달리, 비즈니스 요구사항에 맞게 필요한 데이터만 담거나 가공할 수 있다.
주로 getter, setter 또는 spring에서는 @Data 등을 사용해 간단하게 정의한다.

 

 


 

Entity를 바로 쓰면 안되는 이유

Entity는 DB 구조와 직결되어 있다. 
만약 Entity를 바로 View(프론트)로 전달하면, DB 설계 변경 시 프론트 코드까지 영향을 받는다. 이는 유지보수가 어렵고, 시스템이 취약해진다.

불필요하거나 민감한 데이터 노출 방지를 막기 위해서다.
Entity에는 모든 컬럼이 들어있지만, 실제로 View에서 필요한 데이터는 일부일 수 있다. 예를 들어 비밀번호나, 계정 생성일 같은 정보는 굳이 프론트에 보낼 필요가 없다.

데이터 가공이 필요할 때
Entity에는 firstName, lastName이 있는데, 화면에는 fullName이 필요한 경우가 있을 수 있다. Entity를 직접 쓰면 모든 화면마다 firstName + lastName을 처리해야 하지만, DTO에서 fullName을 만들어주면 깔끔하게 해결할 수 있다.

계층 간 의존성 최소화
Controller, Service, Repository 등 각 계층은 Entity에 의존하면 안된다. DTO를 통해 계층 간 데이터를 주고받으면, 나중에 구조 변경이 생겨도 한쪽만 수정하면 된다.

 

 

Entity와 DTO 간의 변환이 필요한 이유는 서로 다른 목적을 가지고 있기 때문다. Entity는 데이터베이스와의 직접적인 상호작용을 위해 설계된 객체로, 주로 데이터베이스에 저장된 정보를 표현한다. 반면 DTO(Data Transfer Object)는 데이터를 전송하기 위해 사용되며, 불필요한 정보나 복잡한 연관 관계를 제외하고 필요한 데이터만 담아서 클라이언트나 다른 시스템에 전달된다. 따라서 Entity와 DTO는 서로 다른 용도로 사용되므로, 데이터를 변환하여 각각의 목적에 맞게 사용해야 한다.

 


 

 

Entity와 DTO를 분리하는 방법

1. Entity 클래스 작성 (DB 테이블과 매핑)

Entity는 DB 테이블과 직접 연결되기 때문에, 컬럼과 1:1로 매핑된다.
민감한 정보(예: 비밀번호), 필요 없는 데이터(예: 생성 날짜 등)는 Entity에서 모두 관리하지만, View로 보낼 때는 꼭 필요한 데이터만 담는 DTO를 사용한다.

@Entity
public class Member {

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

    private String name;
    private String email;
    private String password;

    // 기본 생성자, getter, setter
}



2. DTO 클래스 작성 (데이터 전달용)

DTO에는 View에서 필요한 데이터만 담는다.
예를 들어, password는 화면에 보여줄 필요가 없으니 DTO에는 넣지 않는다. 아래 예제에서는 Entity에서 name과 email만 담은 memberDto를 생성했다.

public class MemberDto {

    private String name;
    private String email;

    // 기본 생성자
    public MemberDto() {}

    // 생성자
    public MemberDto(String name, String email) {
        this.name = name;
        this.email = email;
    }

    // getter, setter
}

 


3. Entity ↔ DTO 변환 메서드 작성

public class MemberDto {

    private String name;
    private String email;

    // Entity → DTO 변환
    public static MemberDto fromEntity(Member member) {
        return new MemberDto(member.getName(), member.getEmail());
    }

    // DTO → Entity 변환
    public Member toEntity() {
        return new Member(this.name, this.email, null); // 비밀번호는 null로 처리
    }
}



4. Service 계층에서 변환

@Service
public class MemberService {

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    public void saveMember(MemberDto memberDto) {
        Member member = memberDto.toEntity();
        memberRepository.save(member);
    }

    public MemberDto findMember(Long id) {
        Member member = memberRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("회원 없음"));
        return MemberDto.fromEntity(member);
    }
}

 


5. Controller에서 DTO 사용

@Controller
@RequestMapping("/member")
public class MemberController {

    private final MemberService memberService;

    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }

    @PostMapping("/register")
    public String register(@ModelAttribute MemberDto memberDto) {
        memberService.saveMember(memberDto);
        return "redirect:/member/list";
    }

    @GetMapping("/{id}")
    public String getMember(@PathVariable Long id, Model model) {
        MemberDto memberDto = memberService.findMember(id);
        model.addAttribute("member", memberDto);
        return "member/view";
    }
}


Entity와 DTO를 분리하면 Entity는 DB 작업에만 집중하고, DTO는 View(화면)와의 데이터 전달에만 집중할 수 있다. 나중에 DB 구조가 바뀌더라도, DTO만 수정하면 되기 때문에 유지보수도 쉬워진다.

 

 


 

 

생성자를 이용한 방법

DTO 클래스에서 생성자를 사용해 Entity를 DTO로 변환하는 방식이다. 간단하고 명확하다는 장점이 있지만 필드가 많아지면 생성자가 복잡해질 수 있다.

// Entity (데이터베이스 테이블과 매핑되는 클래스)
@Entity
@Getter
@NoArgsConstructor
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String email;
    private String password; // 비밀번호는 DTO로 전달 X

    public User(String username, String email, String password) {
        this.username = username;
        this.email = email;
        this.password = password;
    }
}

// DTO (클라이언트에게 필요한 데이터만 전달)
@Getter
public class UserDTO {
    private String username;
    private String email;

    // Entity를 DTO로 변환하는 생성자
    public UserDTO(User user) {
        this.username = user.getUsername();
        this.email = user.getEmail();
    }
}

// Entity → DTO 변환 예시
User user = new User("testUser", "test@example.com", "password123");
UserDTO userDTO = new UserDTO(user);

System.out.println(userDTO.getUsername()); // testUser
System.out.println(userDTO.getEmail());    // test@example.com

 

 

 

정적 메서드를 이용한 변환

DTO 클래스 내부에 정적 메서드를 만들어 변환하는 방식이다. 가독성이 좋고, 유지보수가 쉽다는 장점이 있지만, 코드를 더 작성해야 한다.

@Getter
public class UserDTO {
    private String username;
    private String email;

    // 정적 메서드로 Entity → DTO 변환
    public static UserDTO fromEntity(User user) {
        UserDTO dto = new UserDTO();
        dto.username = user.getUsername();
        dto.email = user.getEmail();
        return dto;
    }
}

// 변환 예제
User user = new User("testUser", "test@example.com", "password123");
UserDTO userDTO = UserDTO.fromEntity(user);

 

 

 

ModelMapper 라이브러리 사용하여 변환

Spring에서 제공하는 ModelMapper를 사용하면 자동으로 Entity를 DTO로 변환할 수 있다. ModelMapper를 사용하기 위해서는 dependency에 ModelMapper를 추가해줘야 한다.


간단하고 자동 변환이 가능하다는 장점이 있지만 ModelMapper가 모든 필드를 매핑하므로, 필요한 데이터만 선택적으로 변환하려면 별도로 설정이 필요하다.

1. 의존성 추가

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


2. 변환 코드

import org.modelmapper.ModelMapper;

ModelMapper modelMapper = new ModelMapper();
UserDTO userDTO = modelMapper.map(user, UserDTO.class);

 

 

Stream API를 이용한 List 변환

데이터베이스에서 가져온 Entity 리스트를 DTO 리스트로 변환할 때 Stream API를 사용하면 편리하다. Stream API는 코드가 간결하고 가독성이 좋다는 장점이 있고 Java 8 이상에서 사용이 가능하다.

List<User> users = userRepository.findAll(); // DB에서 모든 사용자 조회

// Entity 리스트를 DTO 리스트로 변환
List<UserDTO> userDTOList = users.stream()
                                 .map(UserDTO::fromEntity)
                                 .collect(Collectors.toList());
반응형