Ch11. MVC 1 : 커맨드 객체 : 중첩, 콜렉션 프로퍼티
우선 다음 두 클래스를 보자.
다음 두 클래스는 세 개의 설문(survey) 항목과 응답자의 지역과 나이를 입력받는 설문조사를 담기 위한 클래스들이다.
Respondent.java
package survey;
// 응답자 정보
public class Respondent
{
private int age;
private String location;
public int getAge() {return age;}
public void setAge(int age) { this.age = age; }
public String getLocation() { return location; }
public void setLocation(String location) { this.location = location; }
}
AnsweredData.java
package survey;
import java.util.List;
// 설문 항목에 대한 답변, 응답자 정보
public class AnsweredData
{
private List<String> responses; // 답변 목록
private Respondent res; // 응답자 정보
public List<String> getResponses() { return responses; }
public void setResponses(List<String> responses) { this.responses = responses; }
public Respondent getRes() { return res; }
public void setRes(Respondent res) { this.res = res; }
}
Respondent 클래스는 응답자의 정보를담고,
AnswerdData 클래스는 설문 항목에 대한 답변과 응답자 정보 Respondent 를 담는다.
AnsweredData는 커맨드 객체로서 사용할 클래스다.
AnsweredData는 필드에 Respondent 타입이 있는데, Respondent는 그 자체로 age와 location 프로퍼티를 갖는다.
즉 Respondent.age, Respondent.location을 갖는데, 이를 중첩된 형식이라고 표현한다.
또한 AnsweredData는 List<String> responses의 리스트 타입의 프로퍼티가 있다.
커맨드 객체 : 중첩, 콜렉션 프로퍼티
스프링 MVC는 커맨드 객체가 리스트 타입의 프로퍼티를 가졌거나 중첩 프로퍼티를 가진 경우에 요청 파라미터의 값을 커맨드 객체에 설정해준다.
예를들어 AnsweredData 객체는 List<String> responses, Respondent 를 프로퍼티로 갖는다.
- HTTP 요청 파라미터 이름이 "프로퍼티이름[인덱스]" 형식이면 List 타입 프로퍼티의 값 목록으로 처리한다.
예를들어 <input> 태그가 다음과 같고 커맨드 객체가 AnsweredData 라면
<input type="radio" name="responses[0]" value="서버">서버개발자</label>
커맨드 객체인 AnsweredData의 List 타입인 List<String> responses의 0번째 인덱스에 매핑된다.
- HTTP 요청 파라미터 이름이 "프로퍼티이름.프로퍼티이름" 형식이면 중첩 프로퍼티 값을 처리한다.
예를들어 <input> 태그가 다음과 같고 커맨드 객체가 AnswerdData 라면
<input type="text" name="res.location">
커맨드 객체인 AnsweredData의 Respondent res의 location에 매핑된다.
설문 관련을 처리할 컨트롤러 클래스를 작성한다.
SurveyController.java
package survey;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/survey") // 이 클래스는 "/survey" 경로 처리
public class SurveyController
{
// GET 방식의 "/survey" 경로 요청 처리 (주소창 직접 입력은 GET 방식)
@GetMapping
public String form()
{
return "survey/surveyForm";
}
// POST 방식의 "/survey" 경로 요청 처리
// surveyForm.jsp에서 form이 submit되서 오면 여기서 처리됨
@PostMapping
public String submit(@ModelAttribute("ansData") AnsweredData data) // 커맨드 객체
{
return "survey/submitted";
}
}
해당 클래스는 @RequestMapping("/survey")로 "/survey" 요청 경로를 처리한다.
두 개의 메서드가 있는데 각각 @GetMapping, @PostMapping 으로서,
@GetMapping인 form() 메서드는 "/survey" 요청 경로이면서 GET 방식 요청을 처리한다. (주소창 입력이 GET 방식이다)
@PostMapping인 submit() 메서드는 "/survey" 요청 경로이면서 POST 방식 요청을 처리한다.
컨트롤러 클래스를 설정 파일에서 빈으로 추가.
@Configuration
public class ControllerConfig
{
... // 생략
@Bean
public SurveyController surveyController()
{
return new SurveyController();
}
}
WEB-INF/view/survey/surveyForm.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<!DOCTYPE html>
<html>
<head>
<title>설문조사</title>
</head>
<body>
<h2>설문조사</h2>
<form method="post">
<p>
1. 당신의 역할은? <br/>
<label><input type="radio" name="responses[0]" value="서버">서버개발자</label>
<label><input type="radio" name="responses[0]" value="프론트">프론트개발자</label>
<label><input type="radio" name="responses[0]" value="풀스택">풀스택개발자</label>
</p>
<p>
2. 가장 많이 사용하는 개발도구는? <br/>
<label><input type="radio" name="responses[1]" value="Eclipse">Eclipse</label>
<label><input type="radio" name="responses[1]" value="Intellij">Intellij</label>
<label><input type="radio" name="responses[1]" value="Sublime">Sublime</label>
</p>
<p>
3. 하고 싶은 말 <br/>
<input type="text" name="responses[2]">
</p>
<p>
<label>응답자 위치:<br>
<input type="text" name="res.location">
</label>
</p>
<p>
<label>응답자 나이:<br>
<input type="text" name="res.age">
</label>
</p>
<input type="submit" value="전송">
</form>
</body>
</html>
WEB-INF/view/survey/submitted.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<title>응답 내용</title>
</head>
<body>
<p>응답 내용</p>
<ul>
<c:forEach var="response" items="${ansData.responses}" varStatus="status">
<li>${status.index+1}번 문항: ${response } </li>
</c:forEach>
</ul>
<p>응답자 위치: ${ansData.res.location} </p>
<p>응답자 나이: ${ansData.res.age} </p>
</body>
</html>
흐름
서버를 시작하고 "http://localhost:8080/sp5-chap11/survey" 로 가면 아래 화면이 나온다.
SurveyController 클래스에서 @RequestMapping으로 "/survey" 경로를 처리한다고 하였고, 주소창에 직접 입력하는 것은 GET 방식 이므로 SurveyController 클래스의 form() 메서드가 이를 처리하고 이 메서드는 뷰 이름으로 "survey/surveyForm" 을 리턴한다. 따라서 surveyForm.jsp 에 따라 위 화면이 출력되는 것이다.
항목을 채우고 전송하는 경우를 생각해보자.
surveyForm.jsp의 <form>에 데이터들이 쌓일 것이다.
surveyForm.jsp의 다음 <input> 태그를 보자.
<label><input type="radio" name="responses[0]" value="서버">서버개발자</label>
responses[0]은 여기서 사용되는 커맨드 객체의 responses 프로퍼티의 (이 프로퍼티는 List일 것이다) 0번째 인덱스에 value 값인 "서버"가 저장될 것이다.
항목을 채우고 전송을 누른다. 이 <form> 태그의 method는 "post"이기 때문에, "/survey" 경로의 POST 방식을 처리하는 submit() 메서드가 이를 처리한다. submit() 메서드를 보면 커맨드 객체로 AnsweredData를 사용한다.
또한 뷰이름으로서 "survey/submitted"를 리턴하기 때문에 submitted.jsp가 뷰를 처리한다.
submitted.jsp를 보면 아래와 같은 foreach 문이 있다.
커맨드 객체 AnswerdData의 response를 순회하면서 출력하고 있다.
출처 : 스프링5 프로그래밍 입문 (최범균 저)