BE/Spring & Spring Boot

[Spring Boot] DTO

baek-dev 2025. 1. 4. 12:22

DTO (Data Trsnfer Object)

데이터 전송객체, 시스템 간 데이터를 주고받을 때 사용하는 객체

주로 계층 간 데이터를 전달하거나 데이터를 캡슐화하여 전송하는데 사용됨

 

특징

1. 데이터 전송 목적

  • 데이터베이스, 서비스, 컨트롤러 등 여러 계층 간 데이터를 전달하기 위해 사용함

2. 필드와 Getter, Setter 만 포함

  • 비즈니스 로직은 포함하지 않으며, 단순히 데이터를 저장하고 전달함

3. Immutable 또는 Mutable

  • 일반적으로 DTO는 불변객체로 만들어지는 경우가 많다
    하지만 필요에 따라 Setter를 제공하여 Mutable하게 구현하기도 한다

 

사용하는 이유

1. 계층 간 데이터 전달

  • 컨트롤러, 서비스, 데이터베이스 등의 계층 간 데이터를 전달하는 표준화된 객체로 사용

2. 보안성 및 안정성

  • 도메인(Entity) 개체를 직접 노출하면 내부 비즈니스 로직이나 불필요한 데이터가 외부로 공개될 수 있다
    DTO를 사용하면 필요한 데이터만 선택적으로 노출할 수 있다

3. 응집도와 책임 분리

  • 엔티티(Entity)는 데이터베이스와 밀접한 관련이 있지만, DTO는 전송 데이터에만 집중하므로 각 클래스의 책임이 분리된다

4. API 요청/응답 데이터 표현

  • REST API 에서 클라이언트와 서버 간에 주고받는 데이터를 표현하기 위해 사용된다

DTO, Entity 차이

항목 DTO Entity
역할 계층간 데이터 전송 데이터베이스와 직접 연관
책임 데이터 전달 데이터 저장 및 비즈니스 로직 포함
내용 필요한 데이터만 포함 모든 데이터와 연관관계 포함
영향 범위 계층 간 데이터 전달에 한정 데이터베이스와 연결된 전체 시스템
수명 주기 단기적 (요청/응답 단위) 장기적 (애플리케이션 수명 주기 내내)
연관 관계 일반적으로 없음 엔티티 간 연관 관계 포함

예제

예제 시나리오

  • 사용자 데이터를 저장하고 반환하는 애플리케이션
  • Entity 클래스 : 데이터베이스에 직접 매핑되는 객체
  • DTO 클래스 : 사용자에게 노출될 데이터만 포함

Entity 클래스 :

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;
    private String password; // 민감한 정보

    // Getter & Setter
}

 

DTO 클래스 :

public class UserDTO {
    private Long id;
    private String name;
    private String email;

    // Getter & Setter
}

 

Controller에서 DTO 활용 :

@RestController
@RequestMapping("/users")
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public UserDTO getUserById(@PathVariable Long id) {
        User user = userService.findUserById(id);
        return new UserDTO(user.getId(), user.getName(), user.getEmail());
    }
}

 

Service에서 Entity를 DTO로 변환 :

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public UserDTO findUserById(Long id) {
        User user = userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));
        return new UserDTO(user.getId(), user.getName(), user.getEmail());
    }
}

장점

1. 보안성 :

  • 민감한 데이터(예: 비밀번호 등)를 숨길 수 있음

2. 유지보수성 :

  • 데이터 전송 구조가 변경되어도, DTO만 수정하면 다른 계층에 영향을 주지 않음

3. 테스트 용이성 :

  • 단순 데이터 전송 객체이므로, 단위 테스트가 쉽습니다

4. API 응답 최적화 :

  • 필요한 데이터만 클라이언트에 반환하여 불필요한 데이터 전송을 줄일 수 있음

단점

1. 추가 작업 :

  • DTO를 생성하고, Entity와 변환(매핑)하는 코드가 필요하므로 작업량이 증가할 수 있음

2. 변환 비용 :

  • Entity ↔ DTO 간 변환 과정에서 성능 비용이 발생할 수 있음

DTO 변환 자동화 도구

  • 수동으로 Entity와 DTO를 변환하는 코드는 많아질 수 있다. 이를 자동화하는 라이브러리를 활용하면 효율성이 증가한다
    • ModelMapper : 객체간 매핑을 자동으로 수행
    • MapStruct : 컴파일 타임에 매핑 코드를 생성

MapStruct 예시

@Mapper
public interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    UserDTO toDTO(User user);
    User toEntity(UserDTO userDTO);
}

 

 

 

 

출처 : ChatGPT

 

'BE > Spring & Spring Boot' 카테고리의 다른 글

[Spring Boot] PSA  (0) 2025.01.05
[Spring Boot] AOP  (0) 2025.01.05
[Spring Boot, JPA] 관련 어노테이션  (0) 2025.01.01
[Spring Boot] 관련 어노테이션  (0) 2025.01.01
[Spring Boot] Validation 유효성 검사  (0) 2024.12.30