3 분 소요

1. 모델

1.1 Model을 통해 컨트롤러에서 뷰에 데이터 전달하기

컨트롤러는 뷰가 응답화면을 구성하는데 필요한 데이터를 생성해서 전달해야 한다. 이때 사용하는 것이 Model이다. 다음은 그 예이다

1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.ui.Model;

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

뷰에 데이터를 전달하는 컨트롤러는 hello() 메서드처럼 다음 두 가지를 하면 된다.

  • 요청 매핑 애노테이션이 적용된 메서드의 파라미터로 Model을 추가
  • Model 파라미터의 addAttribute() 메서드로 뷰에서 사용할 데이터 전달

addAttribute() 메서드의 첫 번째 파라미터는 속성 이름이다. 뷰 코드는 이 이름을 사용해서 접근한다. JSP는 다음과 같이 표현식을 사용해서 속성값에 접근다.

${greeting}

1.1.1 예시

설문 항목을 컨트롤러에서 생성해서 뷰에 전달하는 방식으로 코드를 짜보자. 먼저 개별 설문 항목 데이터를 담기 위한 클래스를 아래와 같이 작성한다. Question 클래스의 title과 options는 각각 질문 제목과 답변 옵션을 보관하고 주관식이면 11행의 생성자를 사용해서 답변 옵션이 없는 Question 객체를 생성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Question{
    
    private String title;
    private List<String> options;

    public Question(String title, List<String> options){
        this.title = title;
        this.options = options;
    }

    public Question(String title){
        this(title, Collections.<String>emptyList());
    }

    public String getTitle(){
        return title;
    }

    public List<String> getOptions(){
        return options;
    }

    public boolean isChoice(){
        return options != null %% !options.isEmpty();
    }
}

다음 작업은 SurveyController가 Question 객체 목록을 생성해서 뷰에 전달하도록 구현하는 것이다. 실제로는 DB와 같은 곳에서 정보를 읽어와 Question 목록을 생성하겠지만 이 예제는 컨트롤러에서 직접 생성하도록 구현했다. SurveyController는 다음과 같다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Controller
@RequestMapping("/survey")
public class SurveyController{

    @GetMapping
    public String form(Model model){
        Lsit<Question> questions = createQuestions();
        model.addAttibute("questions", questions);
        return "servey/surveyForm";
    }

    private List<Question> createQuestions(){
        Question q1 = new Question("당신의 역할은 무엇입니까",
            Arrays.asList("서버", "프론트", "풀스택"));
        ...
        return Arrays.asList(q1, q2, q3);
    }

    @PostMapping
    public String submit(@ModelAttribute("ansData") AnsweredData data){
        return "survey/submitted";
    }
}

컨트롤러에서 전달한 Question 리스트를 사용해서 폼 화면을 생성하는 JSP 코드는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
<html>
...
<body>
    <h2>설문조사</h2>
    <form method="post">
    <c:forEach var="q" items="${quesitons}" varStatus="status">
    <p>
        ${status.index + 1}, ${q.title}<br/>
        <c:if test="${q.choice}">
            <c:forEach var="option" item="${q.options}">
            <label><input type="radio"
                name="responses[${status.index}]" value="${option}">
            </c:forEach>
        </c:if>
        <c:if test="${!q.choice}">
        <input type="text" name=responses[${status.index}]>
        </c:if>
    </p>
    </c:forEach>

    ...
</body>
</html>

1.2 ModelAndView를 통한 뷰 선택과 모델 전달

지금까지 구현한 컨트롤러는 두 가지 특징이 있다

  • Model을 이용해서 뷰에 전달할 데이터 설정
  • 결과를 보여줄 뷰 이름을 리턴

ModelAndView를 사용하면 이 두 가지를 한 번에 처리할 수 있다. 요청 매핑 에노테이션을 적용한 메서드는 String 타입 대신 ModelAndView를 리턴할 수 있다. ModelAndView는 모델과 뷰 이름을 함께 제공한다. 다음과 같이 ModelAndView 클래스를 이용해서 SurveyController 클래스의 form()메소드를 구현할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/survey")
public class ServeyController{

    @GetMapping
    public ModelAndView form(){
        List<Question> questions = createQuestions();
        ModelAndView mav = ModelAndView();
        mav.addObject("questions", questions);
        mav.setViewName("survey/surveyForm");
        return mav
    }
}

뷰에 전달할 모델 데이터는 addObject() 메서드로 추가한다. 뷰 이름은 setViewName() 메서드를 이용해서 지정한다.

1.3 GET 방식과 POST 방식에 동일 이름 커맨드 객체 사용하기

<.form:form> 태그를 사용하려면 커맨드 객체가 반드시 존재해야 한다. 최초에 폼을 보여주는 요청에 대해 <.form:form> 태그를 사용하려면 폼 표시 요청이 왔을 때에도 커맨드 객체를 생성해서 모델에 저장해야 한다. 이를 위해 handleStep2() 메서드는 다음과 같이 Model에 직접 객체를 추가했다.

1
2
3
4
5
6
7
8
9
10
@PostMapping("/register/step2")
public String handleStep2(
    @RequestiParam(value = "agree", defaultValue = "false") Boolean agree,
    Model model){
        if(!agree){
            return "register/step1";
        }
        model.addAttrivute("registerRequest", new RegisterRequest());
        return "register/step2";
}

커맨드 객체를 파라미터로 추가하면 좀 더 간단해진다.

1
2
3
4
5
6
7
8
9
@PostMapping("/register/step2")
public String handleStep2(
    @RequestParam(value = "agree", defaultValue = "false") Boolean agree,
    RegisterRequest registerRequest){
        if(!agree){
            return "register/step1";
        }
        return "register/step2";
}

이름을 명시적으로 지정하려면 @ModelAttribute 애노테이션을 사용한다. 예를 들어 “/login” 요청 경로일 때 GET방식이면 로그인 폼을 보여주고 POST 방식이면 로그인을 처리하도록 구현한 컨트롤러를 만들어야 한다고 하자. 입력 폼과 폼 전송 처리에서 사용할 커맨드 객체의 속성 이름이 클래스 이름과 다르다면 다음과 같이 GET 요청과 POST 요청을 처리하는 메서드에 @ModelAttribute 애노테이션을 붙인 커맨드 객체를 파라미터로 추가하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
@RequestMapping("/login")
public class LoginController{

    @GetMapping
    public String form(@ModelAttribute("login") LoginCommand loginCommand){
        return "login/loginForm"
    }
    @PostMapping
    public Sring form(@ModelAttribute("login") LoginCommand loginCommand){
        ...
    }
}

Ref.

  • 최범균, 스프링프로그래밍입문5, 가메출판사.

카테고리:

업데이트: