💡 주제
클라이언트에서 서버, 컨트롤러에서 서비스 등 계층 간 데이터를 전송 하기 위해 주로 DTO 또는 Map을 사용한다.
각각의 개념과 장단점, 사용방법에 대해서 정리해본다.
📦 DTO
- DTO는 Data Transfer Object의 약어로 계층 간 데이터 전송을 위해 설계된 객체다.
- DTO는 주로 데이터베이스와 애플리케이션의 비즈니스 로직, 또는 애플리케이션과 클라이언트 간의 데이터 교환을 간소화하는 데 사용된다.
- DTO는 일반적으로 데이터 필드만 포함하고, 비즈니스 로직은 포함하지 않는다.
- 각 필드에 대한 getter와 setter 메서드를 제공하여 데이터의 접근을 쉽게 한다. (나는 중간에 데이터가 변경되지 않도록 setter를 추가하지 않는 편이다.)
👍🏻 장점
- 계층 간 데이터 전송 단순화
- DTO는 데이터베이스와 비즈니스 로직, 또는 클라이언트와 서버 간의 데이터 전송을 간소화한다. 필요한 데이터만 포함하여 전송하므로, 불필요한 데이터 전송을 방지할 수 있다.
- 유지보수성 향상
- DTO를 사용하면 데이터 구조를 명확하게 정의할 수 있어, 코드의 가독성이 높아지고 유지보수가 용이하다. DTO의 구조가 변경되더라도, 해당 DTO를 사용하는 코드에서만 수정하면 된다.
- 데이터 무결성
- DTO는 데이터의 유효성 검사를 쉽게 적용할 수 있다. 유효성 검사를 통해 잘못된 데이터가 시스템에 들어오는 것을 방지할 수 있다.
- 직렬화 용이
- DTO는 JSON, XML 등 다양한 형식으로 직렬화할 수 있어, API 응답으로 쉽게 사용할 수 있다.
- 보안성
- DTO를 사용하면 필요한 데이터만 클라이언트에게 전송하므로, 민감한 데이터 노출을 줄일 수 있다.
👎 단점
- 추가적인 복잡성
- DTO를 정의하고 관리하는 데 추가적인 유지보수가 필요하다. 특히 간단한 애플리케이션에서는 DTO가 오히려 불필요한 복잡성을 초래할 수 있다.
- 성능 오버헤드
- 데이터 변환 과정(예: 엔티티를 DTO로 변환하는 과정)에서 성능 오버헤드가 발생할 수 있다. 대량의 데이터를 처리할 때는 이 점을 고려해야 함.
- 데이터 동기화 문제
- DTO와 엔티티 간의 데이터 동기화가 필요할 수 있으며, 이 과정에서 버그가 발생할 가능성이 있다. DTO의 구조가 변경되면 관련된 모든 코드에서 수정이 필요할 수 있다.
- 비즈니스 로직 부재
- DTO는 단순히 데이터 전송을 위한 객체이므로, 비즈니스 로직을 포함하지 않는다. 따라서 DTO를 통해 데이터의 의미나 처리 방법을 이해하기 어려울 수 있다.
🔍 사용 예시
1. DTO 클래스 정의
- 나는 보통 요청 데이터를 받는 DTO와 처리 후 응답 데이터를 담는 DTO를 별도로 만드는 편이다.
- 유효성 검사를 위한 어노테이션을 추가할 수 있다.
/* 요청 데이터 dto */
@Getter
public class RequestUserDTO {
@NotBlank(message = "Name cannot be empty")
@Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters")
private String name;
@NotBlank(message = "Email cannot be empty")
@Email(message = "Email should be valid")
private String email;
}
/* 응답 데이터 dto */
@Builder
public class RequestUserDTO {
private long id
private String name;
private String email;
}
2. 컨트롤러에서 DTO 사용
- @Valid 어노테이션을 사용하여 요청 본문에 대한 유효성 검사를 수행한다. 유효성 검사에 실패하면, Spring은 자동으로 MethodArgumentNotValidException을 발생시킨다.
@RestController
@Validated
public class UserController {
@Autowired
private UserService service;
@PostMapping("/users")
public ResponseEntity<ResponseUserDTO> createUser(@Valid @RequestBody RequestUserDTO userDTO) {
// 유효성 검사를 통과한 경우, 사용자 생성 로직 수행
return ResponseEntity.ok(service.save(userDTO));
}
}
3. 서비스에서 DTO 사용
- dto를 엔티티로 변환해서 db와 통신하고 또 그 결과 값을 다시 엔티티로 변환하여 return한다.
@Service
public class UserService {
public ResponseUserDTO save(RequestUserDTO dto) {
// 데이터베이스에 사용자 정보를 저장하는 로직
UserVo vo = UserVo.from(dto);
UserVo resultVo = repository.save(vo);
ResponseUserDTO resultDto = UserVo.to(resultVo);
return resultDto;
}
}
📚 Map
- Map은 데이터의 키-값 쌍을 저장하는 컬렉션이다.
- 자바에서는 Map 인터페이스를 통해 다양한 구현체(예: HashMap, TreeMap, LinkedHashMap)를 제공하여 데이터를 효과적으로 관리할 수 있다.
- 계층 간 이동 시에는 주로 데이터 전송 및 변환을 용이하게 하기 위해 사용된다.
👍🏻 장점
- 유연성
- Map은 키-값 쌍으로 데이터를 저장하므로, 다양한 데이터 유형을 유연하게 처리할 수 있다. 필요한 데이터만 선택적으로 추가할 수 있다.
- 빠른 검색
- 해시 기반의 구현체인 HashMap을 사용할 경우, 평균적으로 O(1)의 시간 복잡도로 데이터를 검색할 수 있어 성능이 뛰어나다.
- 데이터 정렬
- TreeMap을 사용하면 키를 기준으로 데이터를 자동으로 정렬할 수 있어, 정렬된 데이터를 필요로 하는 경우 유용함.
- 전송이 간단함
- dto는 요청과 응답시에 엔티티로 변환하거나, 엔티티로 부터 변환하는 과정이 있지만 Map은 변환없이 계층마다 바로바로 사용가능하다.
👎 단점
- 키 중복 문제
- Map은 키의 중복을 허용하지 않으므로, 동일한 키로 여러 값을 저장할 수 없다. 이로 인해 데이터 덮어씌워지는 등 데이터 관리가 복잡해 질 수 있다.
- 데이터 불투명성
- Map 안에 어떤 데이터가 들어 있는지 알 수 없어, 모든 계층을 확인해야 하는 어려움이 있다. 이로 인해 디버깅이 복잡해질 수 있다.
- 변동 감지의 어려움
- 데이터 저장에 변동이 생겨도 쉽게 알아차리기 어려워, 데이터의 일관성을 유지하기 힘든 상황이 발생할 수 있다.
- 키 의존성
- 정확한 키 값을 알고 있어야만 값을 가져올 수 있기 때문에, 키의 관리가 중요하며, 잘못된 키로 인해 데이터 접근이 어려워질 수 있다.
🔍 사용방법
1. 컨트롤러에서 Map 사용
@RestController
@Validated
public class UserController {
@Autowired
private UserService service;
@PostMapping("/users")
public ResponseEntity<Map<String,Object>> createUser(Map<String,Object> paramMap) {
return ResponseEntity.ok(service.save(paramMap));
}
}
2.서비스에서 Map 사용
- 요청과 응답 데이터 전송 방식이 Map으로 통일된다면, 예시와 같이 코드가 간단해질 수 있다.
@Service
public class UserService {
public Map<String,Object> save(Map<String,Object> paramMap) {
// 데이터베이스에 사용자 정보를 저장하는 로직
return repository.save(paramMap);
}
}
📝 마무리
데이터 전송시 DTO와 Map을 사용하는 환경을 모두 경험해본 결과, 나는 개발시에 DTO를 조금 더 선호하게 되었다.
위에서 정리했다시피 Map은 별도 클래스 설계가 불필요하고, 데이터 담기가 용이하다.
쉽게 넣고 쉽게 갖다쓸 수 있다는 이점에서 아직도 많이 사용되지만 단점 또한 명확하다.
데이터의 일관성을 보장할 수 없고 예상치 못한 데이터가 들어올 수 있어서 내가 만든 서비스가 불안할 수도 있다.
가장 큰 불편함은 어떤 데이터가 넘어오는 건지 알 수 없다는 것이다. 프론트부터 DB까지 찾아봐야할 수도 있다.
DTO는 그런 불안함에서 벗어나게 한다.
유효성 검사를 통해 유효한 데이터가 아니라면 컨트롤러까지도 접근하지 못하게 할 수 있다.
정의된 자료형과 필드명으로 어떤 데이터가 담길지 짐작할 수 있다.
이런 설계를 위한 작업이 필요하고 계속 유지보수를 해야하지만 그런 점들을 감안하고도 내가 DTO를 선호하는 가장 큰 이유이다.
728x90
반응형
'개발 공부 > Java & Spring' 카테고리의 다른 글
[Spring] @transactional 속성을 알아보자. (2) | 2024.10.05 |
---|---|
스프링에서 예외처리를 하는 방법 (ExceptionHandler, RestControllerAdvice) (0) | 2024.08.23 |
27. File (0) | 2023.04.17 |
26. 표준 입출력과 RandomAccessFile (0) | 2023.04.16 |
25. 문자기반의 보조스트림 (0) | 2023.04.09 |