DTO Spring 프로젝트에 적용하기
DTO Spring 프로젝트에 적용하기 — #DTO #Spring프로젝트 #개발자의도구들 🚨📝 📖 📒✏️💡🔍 AI스쿨 msa기반 java 백엔드...
#DTO #Spring프로젝트 #개발자의도구들
🚨📝 📖 📒✏️💡🔍
AI스쿨 msa기반 java 백엔드 코스 중에 공부한 내용을 작성하였습니다
\* 현재 Spring 프로젝트를 진행 중입니다. 전체적인 목차를 보시려면 여기를 눌러주세요.
DTO를 Spring 프로젝트에 적용하기
어번에는 DTO에 대해 알아보고 Spring프로젝트에 직접 적용하는 사례를 알아보겠습니다. 우선은 Spring이 어떤 Design Pattern을 사용하는지 알아봅시다.
@Repository @Service @Controller
Spring은 기본적으로 MVC에서 V를 대체한 MSC 모델을 사용하는데요. Model, Service, Controller가 각각 @Repository, @Service, @controller로 구현됩니다. (MVC패턴이란?)
@Repository는 DB와 직접적인 연결을 담당하는데, 보통은 JPA를 함꼐 사용하기 때문에 Query를 날려 DB와 상호작용하는 Layer입니다.
@Service는 Repositort와 @Controller의 사이에 위치하여 Controller가 요청하면 @Service층에서 여러 @Repostory를 호출하여 다양한 DB 작업을 수행하여 값을 반환합니다.
@Controller는 Request를 직접 받아보고, url에 따라 request를 분리하여, Service에게 필요한 작업을 요청합니다.
전체적인 그림을 보면 아래와 같습니다.
이를 JAVA 코드로 표현하면 아래와 같습니다.
package com.example.demo.controller;import com.example.demo.entity.User;import com.example.demo.service.UserService;import lombok.RequiredArgsConstructor;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController@RequiredArgsConstructorpublic class UserController { private final UserService userService; @GetMapping("/users") public ResponseEntity<List<User>> findUsers() { return ResponseEntity.ok().body(userService.findUsers()); }} |
|---|
⛓️
package com.example.demo.service;import com.example.demo.entity.User;import com.example.demo.repository.UserRepository;import lombok.RequiredArgsConstructor;import org.springframework.stereotype.Service;import java.util.List;@Service@RequiredArgsConstructorpublic class UserService { private final UserRepository userRepository; public List<User> findUsers() { return userRepository.findAll(); }} |
|---|
⛓️
| package com.example.demo.repository;import com.example.demo.entity.User;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository;@Repositorypublic interface UserRepository extends JpaRepository<User, Long> {} |
|---|
현재는 return 값으로 Entity를 그대로 노출하고 있는 것을 볼 수 있습니다.
DTO
DTO는 Data Transfer Object의 약자로 데이터를 전송할 때 사용되는 객체입니다. 각 층은 서로 데이터를 주고 받게 되는데, 이 때 DTO를 서로 주고 받게 됩니다.
마치 내용물을 보호하기 위해 포장박스로 감싸듯이, DTO도 실제 Entity의 내용을 감싸서 내부에서 입맛에 맞게 사용할 수 있습니다.
그냥 내용물(Entity)를 사용하는 것보다 훨씬 안전하지만, 손이 많이 가는 작업이죠.
DTO를 만들어 적용하기
DTO를 직접 만들어 전송을 하도록 하겠습니다. 가장 이상적인 방안은 각 층마다 사용하는 DRO를 분리시키는 것입니다.
| package com.example.demo.dto;import com.example.demo.entity.User;import lombok.Builder;import lombok.Getter;import lombok.RequiredArgsConstructor;@Builder@Getter@RequiredArgsConstructorpublic class UserResultDto { private final long id; private final String username; private final String email; private final String password; public static UserResultDto of(User user) { return UserResultDto.builder() .id(user.getId()) .username(user.getUsername()) .email(user.getEmail()) .password(user.getPassword()) .build(); }} |
|---|
이건 Service에서 사용하는 DTO로 Service -> Controller로 전달됩니다. Repository에서 받은 User Entity를 DTO로 수정해서 보내는 것이지요
| package com.example.demo.dto;import lombok.Builder;import lombok.Getter;import lombok.RequiredArgsConstructor;@Builder@Getter@RequiredArgsConstructorpublic class UserResponseDto { private final long id; private final String username; private final String email; private final String password; public static UserResponseDto of(UserResultDto userResultDto) { return UserResponseDto.builder() .id(userResultDto.getId()) .username(userResultDto.getUsername()) .email(userResultDto.getEmail()) .password(userResultDto.getPassword()) .build(); }} |
|---|
Controller에서 사용되는 DTO로 Service에서 받은 UserResultDto를 UserResponseDto로 변환 후 Clinet에게 데이터를 전달합니다.
| 🖋️DTO를 만들기 위한 of()of()는 그 객체를 만드는 데 흔히 사용되는 메서드입니다. ex) Optional.of()하위 Layer에서 전달된 DTO를 다른 DTO로 변환하기 위해 각 DTO는 of를 포함하고 있어야 합니다. |
|---|
🚧Service, Controller 수정
package com.example.demo.service;import com.example.demo.dto.UserResponseDto;import com.example.demo.dto.UserResultDto;import com.example.demo.entity.User;import com.example.demo.repository.UserRepository;import lombok.RequiredArgsConstructor;import org.springframework.stereotype.Service;import java.util.List;import java.util.Optional;import java.util.stream.Collectors;@Service@RequiredArgsConstructorpublic class UserService { private final UserRepository userRepository; public List<UserResultDto> findUsers() { return userRepository.findAll() .stream() .map(UserResultDto::of) .collect(Collectors.toList()); }} |
|---|
package com.example.demo.controller;import com.example.demo.dto.UserResponseDto;import com.example.demo.entity.User;import com.example.demo.service.UserService;import lombok.RequiredArgsConstructor;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;import java.util.stream.Collectors;@RestController@RequiredArgsConstructorpublic class UserController { private final UserService userService; @GetMapping("/users") public ResponseEntity<List<UserResponseDto>> findUsers() { return ResponseEntity.ok() .body(userService.findUsers() .stream() .map(UserResponseDto::of) .collect(Collectors.toList())); }} |
|---|
| 🖋️DTO를 사용하는 이유? : DTO를 사용하지 않으면 Entity를 바로 노출하게 되는데, 이는 여러가지 문제를 야기할 수 있습니다.(보안, lazyinit) |
|---|
LazyInit에 대한 자세한 내용은 여기를 참고해주세요.
현실적인 DTO 적용안
위 코드에서 의문점이 드실 수 있는데요. Dto의 데이터 필드가 Response, Result가 서로 겹침에도 서로 다른 Dto로 만들어 전달을 하는게 이상해 보일 수 있습니다.
이유는 이와 같이 각 층을 서로 다른 DTO로 분리하는 것이 정석이기 때문입니다. DTO는 프로그램 내에서 어떤 데이터든지 DTO 형태로 전달되게 설계를 하면, 외부 종속성을 제거할 수 있다는 장점이 있습니다. 하지만, 너무 많은 DTO를 만들면, 코드가 복잡해지고 관리하는데 어려움이 생깁니다.
그렇기 때문에 더 복잡한 프로그램의 경우에 DTO를 어떻게 설계할지, 어디까지 만들지에 대한 고민을 통해 효율적인 프로그램 개발을 고안하는 것이 과제입니다.
레퍼런스
- AI스쿨 벡엔드 멘토님
2.
https://field-of-tiger.tistory.com/4
[DTO 패턴
\* 이 글은 Controller Service Repository 로 구현된 서버를 염두해두고 서술했습니다. DTO는 Data Transfer Object의 약자입니다. 데이터 전송시 사용하는 객체인 것이죠. DTO는 마틴 파울러(Martin Fowler)의 저서에서 처음 등장했습니다. DTO 비슷한 개념이 DAO입니다. DAO는 Data Access Object의 약자이죠. DTO가 데이터 전송을 위해 존재한다면, DAO는 접근(저장/수정/조회)을 위해 존재합니다. 데이터 전송과 접근이라는 구분이 애매하게 들릴지도 모르겠습니다. ...
field-of-tiger.tistory.com](https://field-of-tiger.tistory.com/4)



