1. 개요

이 예제에서는  DTO 패턴 , 정의, 사용 방법 및 시기에 대해 설명합니다. 결국 우리는 그것을 올바르게 사용하는 방법을 알게 될 것입니다.

2. 패턴

DTO 또는 데이터 전송 개체는 메서드 호출 수를 줄이기 위해 프로세스 간에 데이터를 전달하는 개체입니다. 이 패턴은 Martin Fowler가 그의 저서 EAA 에서 처음 소개했습니다 .

Fowler는 패턴의 주요 목적이 단일 호출에서 여러 매개변수를 일괄 처리하여 서버 왕복을 줄이는 것이라고 설명했습니다. 이렇게 하면 이러한 원격 작업에서 네트워크 오버헤드가 줄어듭니다.

또 다른 이점은 직렬화 논리(객체 구조와 데이터를 저장하고 전송할 수 있는 특정 형식으로 변환하는 메커니즘)의 캡슐화입니다. 직렬화 뉘앙스에서 단일 변경 지점을 제공합니다. 또한 프리젠테이션 계층에서 도메인 모델을 분리하여 둘 다 독립적으로 변경할 수 있습니다.

3. 어떻게 사용하나요?

DTO는 일반적으로 POJO 로 생성됩니다 . 비즈니스 로직 을 포함하지 않는 플랫 데이터 구조입니다. 직렬화 또는 구문 분석과 관련된 저장소, 접근자 및 최종 메서드만 포함합니다.

데이터는 일반적으로 프리젠테이션 또는 파사드 레이어의 매퍼 구성 요소를 통해 도메인 모델 에서 DTO로 매핑됩니다.

아래 이미지는 구성 요소 간의 상호 작용을 보여줍니다.레이어 4

4. 언제 사용합니까?

DTO는 원격 호출 수를 줄이는 데 도움이 되므로 원격 호출이 있는 시스템에서 유용합니다.

DTO는 또한 도메인 모델이 많은 다른 개체로 구성되고 프레젠테이션 모델에 모든 데이터가 한 번에 필요한 경우 도움이 되거나 클라이언트와 서버 간의 왕복을 줄일 수도 있습니다.

DTO를 사용하면 도메인 모델에서 다양한 보기를 구축할 수 있으므로 동일한 도메인의 다른 표현을 만들 수 있지만 도메인 디자인에 영향을 주지 않고 클라이언트의 요구에 맞게 최적화할 수 있습니다. 이러한 유연성은 복잡한 문제를 해결하는 강력한 도구입니다.

5. 사용 사례

패턴 구현을 시연하기 위해 두 가지 주요 도메인 모델(이 경우 UserRole )이 포함된 간단한 애플리케이션을 사용합니다 . 패턴에 초점을 맞추기 위해 사용자 검색과 새 사용자 생성이라는 두 가지 기능의 예를 살펴보겠습니다.

5.1. DTO VS 도메인

다음은 두 모델의 정의입니다.

public class User {

    private String id;
    private String name;
    private String password;
    private List<Role> roles;

    public User(String name, String password, List<Role> roles) {
        this.name = Objects.requireNonNull(name);
        this.password = this.encrypt(password);
        this.roles = Objects.requireNonNull(roles);
    }

    // Getters and Setters

   String encrypt(String password) {
       // encryption logic
   }
}
public class Role {

    private String id;
    private String name;

    // Constructors, getters and setters
}

이제 도메인 모델과 비교할 수 있도록 DTO를 살펴보겠습니다.

이 시점에서 DTO는 API 클라이언트에서 보내거나 받는 모델을 나타냅니다.

따라서 작은 차이점은 서버로 전송된 요청을 함께 묶거나 클라이언트의 응답을 최적화하는 것입니다.

public class UserDTO {
    private String name;
    private List<String> roles;
    
    // standard getters and setters
}

위의 DTO는 예를 들어 Security상의 이유로 암호를 숨기고 관련 정보만 클라이언트에 제공합니다.

다음 DTO는 사용자를 생성하는 데 필요한 모든 데이터를 그룹화하고 API와의 상호 작용을 최적화하는 단일 요청으로 서버에 보냅니다.

public class UserCreationDTO {

    private String name;
    private String password;
    private List<String> roles;

    // standard getters and setters
}

5.2. 양쪽 연결

다음으로 두 클래스를 연결하는 레이어는 매퍼 구성 요소를 사용하여 데이터를 한 쪽에서 다른 쪽으로 또는 그 반대로 전달합니다.

이는 일반적으로 프레젠테이션 계층에서 발생합니다.

@RestController
@RequestMapping("/users")
class UserController {

    private UserService userService;
    private RoleService roleService;
    private Mapper mapper;

    // Constructor

    @GetMapping
    @ResponseBody
    public List<UserDTO> getUsers() {
        return userService.getAll()
          .stream()
          .map(mapper::toDto)
          .collect(toList());
    }


    @PostMapping
    @ResponseBody
    public UserIdDTO create(@RequestBody UserCreationDTO userDTO) {
        User user = mapper.toUser(userDTO);

        userDTO.getRoles()
          .stream()
          .map(role -> roleService.getOrCreate(role))
          .forEach(user::addRole);

        userService.save(user);

        return new UserIdDTO(user.getId());
    }

}

마지막으로 데이터를 전송 하는 매퍼 구성 요소가 있으므로 DTO와 도메인 모델이 서로에 대해 알 필요가 없습니다 .

@Component
class Mapper {
    public UserDTO toDto(User user) {
        String name = user.getName();
        List<String> roles = user
          .getRoles()
          .stream()
          .map(Role::getName)
          .collect(toList());

        return new UserDTO(name, roles);
    }

    public User toUser(UserCreationDTO userDTO) {
        return new User(userDTO.getName(), userDTO.getPassword(), new ArrayList<>());
    }
}

6. 일반적인 실수

DTO 패턴은 단순한 디자인 패턴이지만 이 기술을 구현하는 응용 프로그램에서 몇 가지 실수를 할 수 있습니다.

첫 번째 실수는 모든 경우에 대해 다른 DTO를 만드는 것입니다. 그것은 우리가 유지해야 하는 클래스와 매퍼의 수를 증가시킬 것입니다. 간결하게 유지하고 기존 항목을 추가하거나 재사용할 때의 장단점을 평가하십시오.

또한 여러 시나리오에서 단일 클래스를 사용하려고 하지 않으려고 합니다. 이 관행은 많은 속성이 자주 사용되지 않는 대규모 계약으로 이어질 수 있습니다.

또 다른 일반적인 실수는 이러한 클래스에 비즈니스 로직을 추가하는 것입니다. 이는 발생하지 않아야 합니다. 패턴의 목적은 데이터 전송과 계약 구조를 최적화하는 것입니다. 따라서 모든 비즈니스 로직은 도메인 계층에 있어야 합니다.

마지막 으로 DTO가 도메인 간에 데이터를 전달하는 이른바 LocalDTO 가 있습니다. 다시 한 번 문제는 모든 매핑의 유지 관리 비용입니다.

이 접근 방식을 지지하는 가장 일반적인 주장 중 하나는 도메인 모델의 캡슐화입니다. 그러나 여기서 문제는 도메인 모델을 지속성 모델과 결합하는 것입니다. 이를 분리하면 도메인 모델을 노출할 위험이 거의 사라집니다.

다른 패턴도 비슷한 결과에 도달하지만 일반적으로 CQRS , Data Mappers , CommandQuerySeparation 등과 같은 더 복잡한 시나리오에서 사용됩니다 .

7. 결론

이 기사에서는 DTO 패턴 의 정의 , 존재 이유 및 구현 방법을 살펴보았습니다.

또한 구현과 관련된 몇 가지 일반적인 실수와 이를 방지하는 방법도 확인했습니다.

늘 그렇듯이 예제의 소스 코드는 GitHub 에서 사용할 수 있습니다 .

Generic footer banner