[Spring Study] - Spring MVC Detail01 :: @ModelAttribute 내용 추가 해야함

uHan2·2021년 2월 6일
0

TIL.BackEnd

목록 보기
10/12

비록 시작은 코딩일기지만, 그 끝은 창대하게
어엿한 개발자 블로그로 성장할 수 있도록.


Spring Study

본 게시글은
Endless Creation Spring Study
에 사용하는 자료입니다.


Spring MVC Detail01

Spring MVC Detail01 :: 개요

앞선 자료에서 MVC Model 1MVC Model 2 그리고 이를 바탕으로 구성된 Spring MVC 를 알아보았다. 또한, Spring MVC 에서 핵심적인 역할을 맡은 Dispatcher Servlet 도 알아보았다.

이번 장에서는 그래서 실제로 요청들이 어떻게 처리되는지, ControllerView 간에 데이터들이 어떻게 서로 전달 되는지 등에 관한
Request Mapping, Request Parameter, Command Objecgt, Model
등을 조금 더 자세하게 살펴보자.


Spring MVC Detail01 :: Request Mapping

웹 애플리케이션을 개발하는 것은 다음 코드를 작성하는 것이다.

  • 특정 URL(URI) 를 처리할 코드
  • 처리 결과를 HTML 과 같은 형식으로 응답하는 코드

이 중 @Controller Annotation 을 사용한 Controller 클래스로 첫 번째를 구현할 수 있다. Controller 클래스는 Request Mapping Annotation 을 사용해서 메소드가 처리할 요청 경로를 지정한다. 이 요청 매핑 Annotation에는 @RequestMapping, @GetMapping, @PostMapping ... 등이 있다. 다음과 같은 예시로 작성할 수 있다.

@Controller
public class HelloController
{
    @GetMapping("/hello")
    public String hello(Model model, 
    @RequestParam(value = "name", required = false) String name)
    {
        model.addAttribute("greeting", "안녕하세요. " + name);
        return "hello";
    }
}

이 Controller 는 "/hello" 라는 URL 로 Get 요청이 오면 hello() 메서드가 처리하도록 설정되있다. return "hello" 를 통해 hello 라는 이름을 가진 뷰를 띄우게 된다.

여기서 @GetMapping@PostMapping 은 각각

@RequestMapping(method = RequestMethod.GET)
@RequestMapping(method = RequestMethod.POST)

와 같다. 간결하게 작성할 수 있게 지원되는 Annotation 이다.

물론 앞서 보인 Get, Post 외에도 Put, Delete, Patch 에 대한 매핑도 가능하다.


Spring MVC Detail01 :: Request Parameter

요청에서 넘어오는 파라미터에 접근하는 것에 대해 알아보자.

약관 동의 화면을 생성하는 step1.jsp 라는 코드가 다음과 같다.

<form action="step2" method="post">
  <label>
    <input type="checkbokx" name="agree" value="true"> 약관 동의
  </label>
  <input type="submit" value="다음 단계" />
</form>

약관에 동의할 경우 값이 true'agree' 요청 파라미터 값을 Post 방식으로 전송한다. 따라서 폼에서 지정한 agree 요청 파라미터 값을 이용해서 약관 동의 여부를 확인할 수 있다.

agree 요청 파라미터를 접근 하는 첫 번째 방법은 HttpServletRequest 를 직접 이용하는 것이다.

  • 참고 HttpServletRequest 는 Document에 다음과 같이 정의되어 있다.

    (원본)
    Extends the ServletRequest interface to provide request information for HTTP servlets.

    The servlet container creates an HttpServletRequest object and passes it as an argument to the servlet's service methods (doGet, doPost, etc).

    (번역)
    HttpServletRequest 는 Http Servlet 에 대한 요청 정보를 제공하도록 ServletRequest 를 확장한 인터페이스 입니다.

    HttpServletRequest 는 서블릿 컨테이너가 생성하며 서블릿의 service() 메서드의 매개변수로 보냅니다.

그럼 HttpServletRequest 를 직접 이용하여 요청 파라미터 값을 구하는 예를 살펴보자.

@Controller
public class RegisterController
{
    @RequestMapping("/register/step1")
    public String handleStep1()
    {
        return "register/step1";
    }
    
    @PostMapping("/register/step2")
    public String handleStep2(HttpServletRequest request)
    {
        String agreeParam = request.getParameter("agree");
        if(agreeParam == null || !agreeParam.equals("true"))
        {
            return "register/step1";
        }
        
        return "register/step2";
    }
}

위와같이 파라미터로 HttpServletRequest 타입을 사용하고 getParameter() 메소드를 이용해서 파라미터의 값을 구하면 된다.

또 다른 방법으로는 @RequestParam Annotation을 사용하는 것이다. 요청 파라미터의 개수가 몇 개 안 되면 이 Annotation을 사용하여 간단하게 요청 파라미터의 값을 구할 수 있다. 코드는 다음과 같다.

@Controller
public class RegisterController
{
    @RequestMapping("/register/step1")
    public String handleStep1()
    {
        return "register/step1";
    }
    
    @PostMapping("/register/step2")
    public String handleStep2(
    @RequestParam(value = "agree", defaultValue = "false") Boolean agree)
    {
        if(!agree)
        {
            return "register/step1";
        }
        
        return "register/step2";
    }
}

@RequestParam 은 다음과 같은 속성을 제공한다.

  • value :: String 타입으로 Http 요청 파라미터의 이름을 지정한다.

  • required :: boolean 타입으로 필수 여부를 지정해준다. 이 값이 만약 true 인데 해당 요청 파라미터에 값이 없다면 예외가 발생한다. 기본값은 true 이다.

  • defaultValue :: String 타입으로 요청 파라미터가 값이 없을 때 사용할 문자열 값을 지정한다. 기본값은 따로 없다.

@RequestParam(value = "agree", defaultValue = "false") Boolean agree)

위 코드는 agree 요청 파라미터의 값을 읽어와 Boolean 타입으로 변환해서 agree 파라미터에 값을 전달한다. SPring MVC 가 이처럼 파라미터 타입에 맞게 String 값을 변환해준다. Boolean 타입 외에 int, long, Integer, LongPrimitive TypeWrapper Type 에 대한 변환을 지원해준다.


Spring MVC Detail01 :: Command Object

위에서 HttpServletRequest 혹은 @RequestParam 을 통해 요청 파라미터의 값을 구하는 예를 보았다. 위의 예시처럼 요청 파라미터의 갯수가 적다면 괜찮지만, 요청 파라미터의 갯수가 많아진다면 값을 하나하나 코드를 작성해줘야 하므로 상당히 길어질 수 있다. 이런 문제점을 해결할 수 있게 Spring 에서는 요청 파라미터의 값을 Comman Object 에 담을 수 있는 기능을 제공한다.

예를 들어 이름이 name 인 요청 파라미터의 값을 Comman ObjectsetName() 메소드를 이용해 Comman Object 에 전달하는 기능을 제공한다.

다음과 같은 요청 파라미터가 있다고 하자.

  • email
  • name
  • password
  • confirmPassword

만약 이 요청 파라미터를 HttpServletRequest 를 통해 받는 다면 다음과 같다.

@PostMapping("/register/step3")
public String handleStep3(HttpServletRequest request)
{
    String email = request.getParameter("email");
    String name = request.getParameter("name");
    String password = request.getParameter("password"); 
    String confirmPassword = request.getParameter("confirmPassword");
    
    RegisterRequest regReq = new RegisterRequest();
    regReq.setEmail(email);
    regReq.setName(name);
    ...
}

요청 파라미터의 갯수가 증가하여 코드가 상당히 길어짐을 볼 수 있다. 이 상황을 Command Object 을 이용한다면 다음과 같다.

@Controller
public class RegisterController
{
    private MemberRegisterService memberRegisterService;
    
    @Autowired
    public RegisterController(MemberRegisterService memberRegisterService)
    {
        this.memberRegisterService = memberRegisterService;
    }
    
    @PostMapping("/register/step3")
    public String handleStep3(RegisterRequest regReq)
    {
        try
        {
            memberRegisterService.regist(regReq);
            return "register/step3";
        }
        catch (DuplicateMemberException ex)
        {
            return "register/step2";
        }
    }
    
    @Getter
    @Setter
    static class RegisterRequest
    {
        private String email;
        private String name;
        private String password;
        private String confirmPassword;
    }
}

이처럼 Command Object 가 무슨 특별한 능력을 가진 새로운 객체가 아니라 DTO 의 역할을 담당한다. Command Object 를 생성하여 사용하는 것이 HttpServletRequest 를 사용할 때보다 코드가 훨씬 깔끔한 것을 확인 할 수 있다. 이렇게 해놓으면 유지보수 관점에서도 더 좋다.


Spring MVC Detail01 :: Model

지금까지 요청 파라미터의 값을 받아오는 내용을 살펴보았다.

ControllerView 가 응답 화면을 구성하는데 필요한 Data 를 생성해서 전달해야한다. 이때 사용하는 것이 바로 Model 이다. Model 에 대해서 살펴보자.

앞서 초반에 다음과 같은 예시 코드를 작성하였다.

@Controller
public class HelloController
{
    @GetMapping("/hello")
    public String hello(Model model, 
    @RequestParam(value = "name", required = false) String name)
    {
        model.addAttribute("greeting", "안녕하세요. " + name);
        return "hello";
    }
}

ControllerViewData 를 전달하기 위해서는 위 예시처럼 다음 두 가지를 하면 된다.

  • 요청 매핑 Annotation 이 적용된 메소드의 파라미터로 Model 을 추가
  • Model 파라미터의 addAttribute() 메소드로 Data 를 전달

addAttribute()Map(Key, Value) 형태로 DataView 에 전달한다.

model.addAttribute("greeting", "안녕하세요. " + name);

이 코드를 예로 들면 greeting 이라는 Key"안녕하세요. " + name 이라는 ValueView 에 넘겨준다.

기본적으로 Model 객체는 위 예시처럼 사용하면 된다. 더 나아가서 @ModelAttribute Annotation 을 활용하면 더 다양하게 사용이 가능하다.

(@ModelAttribute 내용 추가 해야함)


profile
For the 1% inspiration.

0개의 댓글