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 클래스를 활용하는 방법이 있습니다. 

 

 

읽여주셔서 감사합니다.