본문 바로가기

Spring Boot

DTO

DTO(Data Transfer Object)

데이터를 전달하기 위한 단순한 객체, 계층간 데이터 교환을 위해 사용하는 객체, 비즈니스 로직을 포함하지 않는다.

단순한 객체이다보니 Java Bean이다.

요구사항을 끊임없이 해결하려다보면 도메인 모델의 일부 정보만 포함하거나,

여러 도메인 모델의 정보를 합친 데이터 모델이 필요한 경우가 생길 수 있다.

메서드 파라미터가 많아지거나 그룹핑하고 싶을때도 유용하다고 한다.

 

계층이라고 하면 이게 떠오르는데..

클라이언트(Browser) - Controller(Servlet) - Service - Repository - DB

 

DTO는

1.계층간 이동(Client - Controller / Controller - Service / Service - Repository)

2. 요청(요청 Body - Controller 내 핸들러)에 사용된다고 한다.

 

예를들어 Controller, Service, Repository, DB는 서로 User 라는 도메인 객체를 통해 데이터를 전달하고

Controller에서 Client로 리턴할땐 UserDTO에 맵핑(Mapping)하여 리턴할 수 있다.

어.. 이런 식이라고 한다.

 

DTO를 왜 사용하는가? (순환참조 공부)

 1. 필요한 데이터만 응답으로 줄 수 있음

  - 클라이언트마다 넘겨줘야 하는 정보는 API마다 상이하다

  - Entity(User, Message, Channel, ...) 자체를 클라이언트에게 응답으로 넘길 때 불필요한 데이터가 포함되거나

    민감한 정보 노출이 발생할 수 있다

 2. 순환 참조 문제

  - JPA로 개발할 때 양방향 참조된 entity를 Controller에서 응답으로 return하면 순환참조가 발생하게 된다.

    양방향 맵핑이 원인이라고 볼 수 있지만 응답의 return??으로 DTO로 두는게 더 안전하다고 함..?

 3. Entity 구현을 캡슐화하여 보호 가능

  - DTO가 없으면 클라이언트 요청과 Entity Model이 결합되어 클라이언트의 요구사항 변화가 Entity에 영향을 줄 수 있음

  - Entity는 도메인 핵심 로직, 속성을 지니고 있는, 실제 DB 테이블에 대응되는 클래스이니까 쉽게 변경되어선 안된다.

 4. 도메인 객체에 바로 접근하지 않기 위해서(테이블에 직접 접근하지 않음으로 데이터 보호)

 

DTO를 사용해서 원하는 정보만 보여주기

package DTOTest2;

import java.util.Objects;
public class User {
	private String id;
	private String name;
	private String email;
	private String password;
	
	public User(String name, String email, String password) {
		this.name = Objects.requireNonNull(name);
		this.email = Objects.requireNonNull(email);
		this.password = this.encrypt(password);
	}
	
	public String getName() {
		return name;
	}
	public String getEmail() {
		return email;
	}
	
	String encrypt(String password) {
		// encryption logic??
		return password;
	}
}

*Objects.requireNonNull(T Object) : Null 체크를 위한 메서드.

파라미터로 입력된 값이 null이라면 NPE(NullPointerException)이 발생하고 그렇지 않다면 입력값을 그대로 반환함.

 

User 엔티티다.

얘는 id, name, email, password 필드를 가지고 있음

password는 절대 공개되어선 안되기 때문에 User객체 그대로 클라이언트에게 전달해선 안된다.

그래서 Client에게 노출해도 되는 필드로만 이루어진 UserDTO가 필요하다고 함.

package DTOTest2;

public class UserDTO {
	
	private String name;
	private String email;
	
	public UserDTO(String name, String email) {
		this.name = name;
		this.email = email;
	}
	// getter이랑 satter
	public String getName() {
		return name;
	}
	
	public String getEmail() {
		return email;
	}
}

 

package DTOTest2;

public class Mapper {
	public UserDTO toDto(User user) {
		String name = user.getName();
		String email = user.getEmail();
		
		return new UserDTO(name,email);
	}
}

 

package DTOTest2;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {
	private UserService userService;
	private Mapper mapper;
	
	// constructor
	
	@GetMapping
	@ResponseBody
	public List<UserDTO> getUsers() {
		return userService.getAll()
				.stream()
				.map(mapper::toDto)
				.collect(toList());
	}
}

Service, Repository에는 User로 객체를 전달하고 (Controller - Service - Repository - DB)

Controller에서 DTO 객체로 매핑(Mapping)한다음 Client에게 리턴한다.

 

* DTO를 사용하면 Client에게 User 엔티티를 주는것이 아니라서 password같은 값을 노출시키지 않고 원하는 값을 리턴할 수 있음

 

DTO는 오직 데이터를 전달하는 목적으로 사용하는 것이고

Domain은 비즈니스 로직을 담는 역할을 수행해야한다.

 

1) DTO는 한번 호출하면 해당 호출에 관련된 모든 데이터를 담은 객체를 리턴해서 사용하는 데 목적이 있다

2) 네트워크 비용 > Controller에서 매번 변환하는 비용??

    그니까 DTO 네트워크를 구성하는데 드는 비용이 Controller에서 매번 변환하는 비용보다 낫다는 판단에서 쓰는듯?

3)  같은 필드인데 어떤 경우는 null이고, 어떤 경우엔 값이 있다면 이상해지니까

     null이 존재하지 않는, 모든 필드에 값이 있는 공통 DTO를 사용한다고 한다.

4) 공통 DTO가 너무 커져서 성능에 문제가 있거나 특별한 경우에만 별도의 DTO를 사용한다고 함.

 

*** DTO를 정의할 때 class대신 Record를 활용하면 더욱 편리하다고 함.

 

'Spring Boot' 카테고리의 다른 글

HTTP 프로토콜 기초  (3) 2025.07.01
Spring AOP가 필요한 이유  (1) 2025.07.01
Web Server와 Web Application Server, 그리고 내장 Tomcat에 대해  (0) 2025.06.24
Spring 연습예제 1  (0) 2025.06.24
Spring 핵심 개념 - AOP  (3) 2025.06.24