스프링

dto를 사용해야 하는 이유

메오백 2024. 9. 1.

작년에 사이드프로젝트를 진행하면서 api서버를 개발했다. 프론트에서 http 요청 바디에 데이터를 넘겨주면 api서버에서는 무조건 dto로 받기로 했다. 팀원이 그렇게 처음부터 정하고 프로젝트를 진행했기 때문에 왜 dto를 사용해야 되는지 고민해보지를 않았다. 

dto를 쓰지 않은 코드를 보면서 역체감으로? 왜 dto를 쓰는게 좋은 지 느끼게 된 바가 있어 글을 작성해보겠습니다. 어디까지나 제 주관적인 생각입니다. 

 

HttpServletRequest getParameter 사용

    @RequestMapping("/messages")
    public ModelAndView make(HttpServletRequest request) {
        
        String title = request.getParameter("title");
        String vGrade = request.getParameter("vGrade");-
        String name = request.getParameter("name");
        String age = request.getParameter("age");

        // 비즈니스 로직 ..

        ModelAndView mv = new ModelAndView("/messageView.jsp");
        return mv;
    }

Http 요청을 통해 데이터를 전송하는 방법은 크게 3가지가 있습니다.

  1. url 쿼리파라미터(혹은 쿼리스트링)
  2. post 요청의 form data
  3. 요청 body의 데이터 

1, 2번은 HttpServletRequest의 getParameter로 바로 값을 얻을 수 있다. 

(3번은 @RequestBody + dto로 받는 편이 깔끔하다)

이런 방식을 사용할 경우 몇가지 단점이 있다. 

 

1. 파라미터 이름으로 값의 의미를 파악하기 힘들다. 

title, age, name 같은 직관적인 이름은 괜찮지만 , vGrade같은 이름은 코드를 처음 보는 사람이 이해하기 힘들다. 

2. 어떤 방식으로 데이터가 넘어왔는지 구분할 수 없다. 

vGrade가 어떤 값인지 궁금해서 /messages로 요청을 보내는 view 파일을 찾아볼 수 있다. 간단한 파일이라면 상관 없지만, view가 복잡하면 찾는 데 시간이 오래 걸린다. 심지어 form data인지 쿼리스트링인지 모르기 때문에 form 부분, 요청을 보내는 부분의 url을 모두 확인해야 할 수도 있다. 

3. 형변환 과정이 필요하다. 

age는 나이이므로 int값으로 사용할 것이지만 getParameter로 받으면 String 타입이라 형변환이 필요하다. 

(getAttribute 메서드를 사용하면 Object 타입으로 반환받을 수 있긴 하다)

4. 중복이 발생한다.

컨트롤러마다 getParameter로 값을 가져오는 과정이 중복되어 발생한다. 

 

@RequestParam 사용 

    @RequestMapping("/messagesV2")
    public ModelAndView makeV2(@RequestParam String title, @RequestParam(required = true, defaultValue = "19") int age,
                               @RequestParam String vGrade, @RequestParam String name) {

        // 비즈니스 로직 ..

        ModelAndView mv = new ModelAndView("/messageView.jsp");
        return mv;
    }

@RequestParam 애노테이션을 사용할 수 있다. 

 

getParameter의 중복이 해소되고, 형변환 과정도 사라졌다.

추가로 required, defaultValue같은 제한도 둘 수 있어서 유용하다.  

하지만 여전히 vGrade가 무엇인지 알기는 어렵고, 쿼리스트링인지 form data인지 알 수 없다. 

 

dto + @ModelAttribute 사용

// Controller
@RequestMapping("/messagesV3")
public ModelAndView makeV3(@ModelAttribute MessageMakeRequest messageMakeRequest,
                           @ModelAttribute MemberInfoRequest memberInfoRequest,
                           @RequestParam int page) {

    // 비즈니스 로직 ..

    ModelAndView mv = new ModelAndView("/messageView.jsp");
    return mv;
}
    
//dto
@Getter
@RequiredArgsConstructor
@ToString
@EqualsAndHashCode
public class MemberInfoRequest {
    private final int age;
    @NotBlank(message = "NAME 값이 BLANK 입니다")
    private final String name;
}

@Getter
@RequiredArgsConstructor
@ToString
@EqualsAndHashCode
public class MessageMakeRequest {
    private final String title;
    private final String vGrade;
}

dto를 만들고 @ModelAttribute를 사용할 수 있다. 

 

dto를 사용하면 vGrade가 MessageMakeRequest의 필드이기 때문에 메세지를 등록할 때 필요한 값 중 하나라는 것을 쉽게 이해할 수 있다. @RequestParam만 사용한 컨트롤러를 보면 vGrade가 무엇을 위한 값인지 알기 어렵다. 

 

사실 @ModelAttribute와 @RequestParam 모두 쿼리스트링 또는 html form 방식 둘 다 인식한다. 즉 @ModelAttribute로 dto를 사용한다고 해서 이 값은 form으로 넘어왔다고 할 순 없다. 하지만 "쿼리스트링인 경우 @RequestParam을, form data인 경우 관련있는 값 끼리 dto로 묶어서 @ModelAttribute를 사용하자" 와 같은 약속을 한다면 애노테이션을 보고 넘어온 방식을 알 수 있다. 이건 사실 쓰고 보니 좀 억지같다.. 

 

추가로 Bean Validation을 사용해 간편하게 검증할 수 있다. 예외메세지도 애노테이션 속성값으로 적을 수 있고 예외도 묶어서 편하게 처리할 수 있어서 편리하다. 

 

결론 

사실 별 대단한 내용은 아니지만, 내가 실제로 느낀 점을 토대로 글을 작성하니 뿌듯하다.

개인적인 취향으로는 관련있는 값은 @ModelAttribute로 dto로 묶어서 받고, 쿼리스트링은 @RequestParam으로 받는 편이 깔끔한 것 같다. 

 

 

 

 

 

 

 

'스프링' 카테고리의 다른 글

spring 요청 Validation 검증, 예외 처리  (0) 2025.01.16
테스트 시 @Value 사용  (0) 2024.11.04
ApplicationContext  (0) 2024.08.28
서블릿과 MVC 패턴과 프론트 컨트롤러 패턴  (1) 2024.08.27
카카오 로그인  (0) 2024.08.02

댓글