Dto란? 

Dto Data Transfer Object의 약자입니다. 계층 간 데이터 교환 역할을 합니다. DB에 저장되는 Entity를 감싸 Wrapper라고 생각할 수 있습니다. Entity를 클라이언트단과 연결되는 Controller에 사용할 때 문제점이 있습니다. 

 

1. Entity의 스펙 노출 -> Entity의 모든 값이 외부에 노출 될 수 있습니다. 

2. 기능에 따라 요구하는 데이터가 다름 -> signup 기능: 이름, 아이디 등등 , login 기능: 아이디, 패스워드 

 

이러한 문제를 해결하기 위해 Dto를 사용합니다. 예를 보겠습니다. 

@RestController
@RequiredArgsConstructor
@Slf4j
public class UserController {
private final UserService userService;
@PostMapping("/signup")
@ResponseStatus(HttpStatus.OK)
public SignUpDto signup(@RequestBody UserDto userDto)throws UserDupulicatedException {
User user = userService.join(userDto);
return new SignUpDto(user.getId(), true);
}
@GetMapping("/duplicate")
@ResponseStatus(HttpStatus.OK)
public DuplicateDto checkEmail(@RequestParam String email) throws UserDupulicatedException {
return new DuplicateDto(userService.checkEmail(email));
}
@PostMapping("/login")
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<String> login(@RequestBody UserDto userDto, HttpServletResponse response) throws UsernameNotFoundException, BadCredentialsException, Throwable {
String accessToken = userService.login(userDto);
response.setHeader("X-AUTH-TOKEN",accessToken);
return new ResponseEntity<String>("ok",HttpStatus.OK);
}
}
  • 클라이언트에게 정보를 받는 UserDto가 있습니다.
@Data
@AllArgsConstructor
public class UserDto {
public String email;
public String password;
}

 

  • 클라이언트에게 정보를 주는 SignupDto, DuplicateDto가 있습니다.
@AllArgsConstructor
@Data
public class DuplicateDto {
boolean success;
}
@AllArgsConstructor
@Data
public class SignUpDto {
Long userId;
boolean success;
}

Dto를 활용해서 위에서 언급되는 문제점들을 해결했습니다. 패키지를 한 번 보겠습니다.

데이터의 유형에 따라 Dto를 만드니 패키지가 더러워지는 것을 볼 수 있습니다. 

Dto를 활용해서 해결하라며?? -> 맞습니다. 하지만 다른 사람들이 봤을 때 이해하기 쉬운 코드가 좋은 코드라고 생각합니다. 패키지 구성 요소가 많다면 코드를 이해하는데 오랜 시간이 걸릴 수 있습니다. 이 문제를 Inner Class로 해결해보겠습니다.


하나의 Dto Inner Class 활용

UserDto 수정

@Data
@AllArgsConstructor
public class UserDto {
@Data
@AllArgsConstructor
public static class SignupAndLoginDto{
public String email;
public String password;
}
@AllArgsConstructor
@Data
public static class ResponseSignupDto {
Long userId;
boolean success;
}
@AllArgsConstructor
@Data
public static class DuplicateDto {
boolean success;
}
}
  • 하나의 클래스 안에 사용되는 목적이 명확한 이름을 가진 inner 클래스로 구성합니다. 

Controller 수정

@RestController
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping("/signup")
@ResponseStatus(HttpStatus.OK)
public UserDto.ResponseSignupDto signup(@RequestBody UserDto.SignupAndLoginDto userDto)throws UserDupulicatedException {
User user = userService.join(userDto);
return new UserDto.ResponseSignupDto(user.getId(), true);
}
@GetMapping("/duplicate")
@ResponseStatus(HttpStatus.OK)
public UserDto.DuplicateDto checkEmail(@RequestParam String email) throws UserDupulicatedException {
return new UserDto.DuplicateDto(userService.checkEmail(email));
}
@PostMapping("/login")
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<String> login(@RequestBody UserDto.SignupAndLoginDto userDto, HttpServletResponse response) throws UsernameNotFoundException, BadCredentialsException, Throwable {
String accessToken = userService.login(userDto);
response.setHeader("X-AUTH-TOKEN",accessToken);
return new ResponseEntity<String>("ok",HttpStatus.OK);
}
}
  • Dto의 이름은 길어졌지만 더 명확하게 어떤 곳에 사용되는지 알 수 있게됐습니다.

inner 클래스를 사용해서 패키지가 깔끔해졌습니다. 

정리

Dto의 사용은 선택이 아닌 필수입니다. 하지만 깔끔한 코드를 작성하기 위해 inner 클래스를 활용하는 방법이 있습니다. 

 

 

읽여주셔서 감사합니다.