728x90

개요

Spring Boot 애플리케이션에서 DTO(Data Transfer Object)Entity 간의 변환은 시스템 설계와 아키텍처에서 중요한 결정 사항 중 하나입니다.

 

DTO는 주로 클라이언트와의 데이터 교환을 목적으로 하고, Entity데이터베이스와 상호작용하는 도메인 객체로 사용됩니다.

 

두 가지 객체의 변환은 애플리케이션의 데이터 흐름에서 필수적인 과정이지만, 변환을 어디에서 수행할지에 따라 코드의 가독성, 유지보수성, 그리고 성능에 큰 영향을 미칩니다.

 

 이 블로그 포스트에서는 DTO와 Entity 간 변환어느 Layer에서 수행하는 것이 가장 적절한지 살펴보겠습니다.

 

 

DTO를 사용하는 이유는❓

Entity는 종종 민감한 정보불필요한 정보를 포함하고 있습니다. DTO를 사용하면 클라이언트에게 필요한 최소한의 데이터만 전달할 수 있습니다.

Entity (민감한 정보 포함)

// Entity (민감한 정보 포함)
@Entity
public class User {
    private Long id;
    private String username;
    private String password; // 민감한 정보
    private String email;
    private List<Role> roles; // 내부 권한 정보
}

 

위의 User 엔티티 클래스에서는 비밀번호(password)사용자의 권한 정보(roles) 같은 민감한 정보가 포함되어 있습니다. 이러한 민감한 정보는 클라이언트에게 전달되지 않아야 합니다.

 

DTO (필요한 정보만 노출)

// DTO (필요한 정보만 노출)
public class UserResponseDto {
    private Long id;
    private String username;
    private String email;
}

 

UserResponseDto는 클라이언트에게 필요한 최소한의 데이터만 포함하고 있습니다. 비밀번호와 권한 정보는 제외되어 있습니다. 이렇게 함으로써, 보안성능을 동시에 고려할 수 있습니다.


DTO 사용의 장점

1. 보안성 강화

  • 민감한 정보클라이언트로 전달되지 않도록 하여 보안을 강화합니다.

2. 데이터 최적화

  • 클라이언트에 필요한 정보만 전달함으로써 네트워크 트래픽을 줄이고 성능을 향상시킬 수 있습니다.

3. 유연성

  • Entity 구조가 변경되더라도 DTO를 통해 클라이언트에게 전달되는 데이터 형식을 독립적으로 관리할 수 있습니다.

 

DTO와 Entity의 변환 작업 - Entity

@Entity
public class User {
    private Long id;
    private String username;
    private String password; // 민감 정보
    private String email;
    private List<Role> roles;

    // Entity에서 DTO로의 변환 메서드
    public UserResponseDto toDto() {
        return new UserResponseDto(
            this.id,
            this.username,
            this.email
        );
    }

    // DTO에서 Entity로의 생성
    public static User fromDto(UserCreateDto dto) {
        return new User(
            dto.getUsername(),
            dto.getPassword(),
            dto.getEmail()
        );
    }
}

 

toDto()

  • Entity -> DTO로의 변환을 담당
  • 인스턴스 메서드로 구현하여 현재 Entity의 데이터를 DTO로 변환
  • 주로 클라이언트에게 응답할 때 사용
  • 민감한 정보(password)불필요한 정보(roles)를 제외하고 변환

fromDto()

  • DTO -> Entity로의 변환을 담당
  • 정적 메서드로 구현하여 새로운 Entity 인스턴스를 생성
  • 주로 클라이언트로부터 데이터를 받아 저장할 때 사용
  • DTO의 데이터를 기반으로 새로운 Entity 객체를 생성

 

위와 같이 Entity 클래스에서 변환 메서드를 통해 DTO와 Entity 간의 변환을 수행할 수 있습니다. 하지만 이러한 변환 작업은 Entity 클래스 외에도 ControllerService , DTO 계층 에서도 수행할 수 있습니다.

 

그렇다면 변환 작업은 Controller, Service, DTO 중 어떤 계층에서 수행하는 것이 가장 적절할까요? 각 계층별 장단점을 살펴보도록 하겠습니다.

 

DTO와 Entity의 변환 작업 - DTO

public class UserDto {
    private Long id;
    private String username;
    private String email;

    // DTO에서 Entity로의 변환
    public User toEntity() {
        return new User(
            this.username,
            this.email
        );
    }

    // Entity에서 DTO로의 변환
    public static UserDto fromEntity(User user) {
        return new UserDto(
            user.getId(),
            user.getUsername(),
            user.getEmail()
        );
    }
}

 

toEntity()

  • DTO -> Entity로의 변환을 담당
  • 인스턴스 메서드로 구현하여 현재 DTO의 데이터로 Entity 생성
  • 클라이언트의 요청 데이터를 Entity로 변환할 때 사용

fromEntity()

  • Entity -> DTO로의 변환을 담당
  • 정적 메서드로 구현하여 Entity를 기반으로 새로운 DTO 생성
  • Entity의 데이터를 클라이언트에 맞게 변환할 때 사용

 

DTO와 Entity의 변환 수행 - Controller Layer

@RestController
@RequestMapping("/api/users")
public class UserController {
    private final UserService userService;
    
    @GetMapping("/{id}")
    public UserDto getUser(@PathVariable Long id) {
        User user = userService.getUser(id);
        return user.toDto();  // Controller에서 변환 수행
    }

    @PostMapping
    public UserDto createUser(@RequestBody UserCreateDto userDto) {
        User user = User.fromDto(userDto);
        return userService.saveUser(user).toDto();
    }
}

Controller Layer에서 변환 수행의 특징

 

  • 장점
    • Service 계층순수한 도메인 로직만 다룰 수 있음
    • 변환 지점이 명확하여 코드 추적이 용이
  • 단점
    • ControllerEntity 구조를 알아야 함
    • 여러 Controller에서 변환 로직이 중복될 수 있음

 

 

DTO와 Entity의 변환  수행 - Service Layer

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserDto getUserDto(Long id) {
        User user = userRepository.findById(id)
            .orElseThrow(() -> new NotFoundException());
        return user.toDto();  // Service에서 변환 수행
    }

    public UserDto saveUser(UserCreateDto userDto) {
        User user = User.fromDto(userDto);
        User savedUser = userRepository.save(user);
        return savedUser.toDto();  // Service에서 변환 수행
    }
}

 

Service Layer에서 변환 수행의 특징

  • 장점
    • ControllerEntity 구조를 알 필요가 없음
    • 비즈니스 로직함께 데이터 변환을 캡슐화
    • 변환 로직의 재사용성이 높음
  • 단점
    • Service 계층의 책임이 증가
    • Entity를 사용하는 다른 Service에서 재사용이 어려울 수 있음

 

결론

DTO와 Entity 간의 변환 작업은 애플리케이션의 구조유지보수성에 큰 영향을 미치는 중요한 설계 결정입니다.

 

변환 작업 자체는 Entity 클래스DTO 클래스에서 구현할 수 있으며, 이 변환 메서드를 실제로 호출하는 시점은 ControllerService 계층이 될 수 있습니다.

 

각 계층별 장단점을 고려했을 때

  • Controller 계층에서의 변환
    • Service 계층을 순수하게 유지 수 있지만, Entity 구조에 대한 의존성이 생기고 코드 중복이 발생할 수 있습니다.
  • Service 계층에서의 변환
    • Entity 구조를 캡슐화하고 재사용성을 높일 수 있지만, Service 계층의 책임이 증가한다는 단점이 있습니다.

결국 변환 작업의 위치 선택은 프로젝트의 특성과 팀의 선호도에 따라 달라질 수 있습니다.

 

중요한 것은 일관된 방식을 유지하고, 각 계층의 책임을 명확히 하는 것입니다. 일반적으로는 Service Layer에서 변환을 수행하는 것이 캡슐화와 재사용성 측면에서 더 나은 선택이 될 수 있습니다.