개요
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 클래스 외에도 Controller나 Service , 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 계층이 순수한 도메인 로직만 다룰 수 있음
- 변환 지점이 명확하여 코드 추적이 용이
- 단점
- Controller가 Entity 구조를 알아야 함
- 여러 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에서 변환 수행의 특징
- 장점
- Controller가
Entity 구조를 알 필요가 없음 - 비즈니스 로직과 함께 데이터 변환을 캡슐화
- 변환 로직의 재사용성이 높음
- Controller가
- 단점
- Service 계층의 책임이 증가
- Entity를 사용하는 다른 Service에서 재사용이 어려울 수 있음
결론
DTO와 Entity 간의 변환 작업은 애플리케이션의 구조와 유지보수성에 큰 영향을 미치는 중요한 설계 결정입니다.
변환 작업 자체는 Entity 클래스나 DTO 클래스에서 구현할 수 있으며, 이 변환 메서드를 실제로 호출하는 시점은 Controller나 Service 계층이 될 수 있습니다.
각 계층별 장단점을 고려했을 때
- Controller 계층에서의 변환
- Service 계층을 순수하게 유지할 수 있지만, Entity 구조에 대한 의존성이 생기고 코드 중복이 발생할 수 있습니다.
- Service 계층에서의 변환
- Entity 구조를 캡슐화하고 재사용성을 높일 수 있지만, Service 계층의 책임이 증가한다는 단점이 있습니다.
결국 변환 작업의 위치 선택은 프로젝트의 특성과 팀의 선호도에 따라 달라질 수 있습니다.
중요한 것은 일관된 방식을 유지하고, 각 계층의 책임을 명확히 하는 것입니다. 일반적으로는 Service Layer에서 변환을 수행하는 것이 캡슐화와 재사용성 측면에서 더 나은 선택이 될 수 있습니다.
'Framework > Spring\Spring boot' 카테고리의 다른 글
[Spring boot] Cache Manager와 @Cacheable 어노테이션 이해하기 (0) | 2024.09.02 |
---|---|
[Spring boot] @Pattern 어노테이션: 정규 표현식으로 입력 검증 간편하게 하기 (0) | 2024.08.27 |
[Spring boot] @PostConstruct : 빈의 안전한 초기화 콜백 (0) | 2024.08.25 |
[Spring Boot] Spring Boot에서 데이터 검증: @Valid와 @Column(nullable = false)의 차이와 함께 사용하는 방법 (0) | 2024.08.15 |
[Spring boot] @Builder 어노테이션의 장점 (0) | 2024.08.09 |