상세 컨텐츠

본문 제목

[스프링 1] 11장. MVC1: 요청 매핑, 커맨드 객체, 리다이렉트, 폼 태그, 모델

22-23/22-23 Spring 1

by dev otcroz 2022. 11. 24. 10:00

본문

728x90

해당 포스트는 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문 [최범균 저] 책 내용을 참고하였습니다.


❗프로젝트 준비

프로젝트 폴더 구조

  • src/main/java : 자바 코드, 설정 파일 위치
  • src/main/webapp : HTML, CSS, JS 등이 위치할 폴더
  • src/main/webapp/WEB-INF : web.xml 파일이 위치할 폴더
  • src/main/webapp/WEB-INF/view : 컨트롤러의 결과를 보여줄 JSP 파일 위치

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
		http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>sp5</groupId>
	<artifactId>sp5-chap11</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<dependencies>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>javax.servlet.jsp-api</artifactId>
			<version>2.3.2-b02</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>5.0.2.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>5.0.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat</groupId>
			<artifactId>tomcat-jdbc</artifactId>
			<version>8.5.27</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.45</version>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.25</version>
		</dependency>

		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.3</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.7.0</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<encoding>utf-8</encoding>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

 

8장에서 만든 아래 파일들을 spring 패키지에 저장해 사용한다.

  • ChangePasswordService.java
  • DuplicateMemberException.java
  • Member.java
  • MemberDao.java
  • MemberNotFoundException.java
  • MemberRegisterService.java
  • RegisterRequest.java
  • WrongIdPasswordException.java

설정 파일(3개)

1. MemberConfig.java : 서비스 클래스와 DAO 클래스를 위한 스프링 설정 클래스, DataSource와 트랜잭션 관련 설정도 포함한다.

@Configuration
@EnableTransactionManagement
public class MemberConfig {

	@Bean(destroyMethod = "close")
	public DataSource dataSource() {
		DataSource ds = new DataSource();
		ds.setDriverClassName("com.mysql.jdbc.Driver");
		ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8");
		ds.setUsername("spring5");
		ds.setPassword("spring5");
		ds.setInitialSize(2);
		ds.setMaxActive(10);
		ds.setTestWhileIdle(true);
		ds.setMinEvictableIdleTimeMillis(60000 * 3);
		ds.setTimeBetweenEvictionRunsMillis(10 * 1000);
		return ds;
	}

	@Bean
	public PlatformTransactionManager transactionManager() {
		DataSourceTransactionManager tm = new DataSourceTransactionManager();
		tm.setDataSource(dataSource());
		return tm;
	}

	@Bean
	public MemberDao memberDao() {
		return new MemberDao(dataSource());
	}

	@Bean
	public MemberRegisterService memberRegSvc() {
		return new MemberRegisterService(memberDao());
	}

	@Bean
	public ChangePasswordService changePwdSvc() {
		ChangePasswordService pwdSvc = new ChangePasswordService();
		pwdSvc.setMemberDao(memberDao());
		return pwdSvc;
	}
}

2. MvcConfig.java : 스프링 MVC 를 위한 설정 파일이다.

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

	@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		configurer.enable();
	}

	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.jsp("/WEB-INF/view/", ".jsp");
	}

}

3. web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
	version="3.1">

	<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>
			org.springframework.web.servlet.DispatcherServlet
		</servlet-class>
		<init-param>
			<param-name>contextClass</param-name>
			<param-value>
				org.springframework.web.context.support.AnnotationConfigWebApplicationContext
			</param-value>
		</init-param>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>
				config.MemberConfig
				config.MvcConfig
				config.ControllerConfig
			</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>
			org.springframework.web.filter.CharacterEncodingFilter
		</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

</web-app>

 

 

❗요청 매핑 애노테이션을 이용한 경로 매핑

  • 특정 요청 url을 처리하는 코드 : @Controller 를 사용한 컨트롤러 클래스로 구현
  • 처리 결과를 HTML과 같은 형식으로 응답하는 코드

📌요청 매핑 애노테이션

컨트롤러 클래스는 요청 매핑 애노테이션을 사용해서 메서드가 처리할 요청 경로를 지정한다.

  • @RequestMapping
  • @GetMapping
  • @PostMapping
@Controller
@RequestMapping("/register") /* /register */
public class RegistController {

	@RequestMapping("/step1") /* /register/step1 */
	public String handleStep1(){
		return "register/step1";
	}

	@RequestMapping("/step2") /* /register/step2 */
	public String handleStep2(){
		return "register/step2";
	}

	....
}

요청 매핑 애노테이션을 적용한 메서드를 두 개 이상 정의하는 것도 가능하다.

📌요청 매핑 애노테이션 사용법

1. 요청 경로 처리하는 컨트롤러 클래스(RegisterController.java) 수정

@Controller
public class RegisterController {

	@RequestMapping("/register/step1") /* /register/step1 */
	public String handleStep1() {
		return "register/step1";
	}

}

 

2. 리턴될 뷰 jsp 파일(register/step1.jsp) 생성 : 코드 생략

3. 컨트롤러 설정 클래스(ControllerConfig.java) 수정 : RegisterController 빈 등록

@Configuration
public class ControllerConfig {

	@Bean
	public RegisterController registerController() {
		return new RegisterController();
	}
}

 

 

❗GET과 POST 구분: @GetMapping, @PostMapping

스프링 MVC는 별도의 설정이 없으면 get, post 방식에 상관없이 @RequestMapping에 지정한 경로와 일치하는 요청을 처리한다.

@Controller
public class LoginController {
	@GetMapping("member/login")
	public String form(){
		...
	}

	@PostMapping("member/login")
	public String login(){
		...
	}
}

같은 경로에 대해 GET과 POST 방식을 각각 다른 메서드가 처리하도록 설정할 수 있다.

 

@RequestMapping(value="/member/login", method = RequestMethod.GET)
@RequestMapping(value="/member/login", method = RequestMethod.POST)

 

 

❗요청 파라미터 접근

register/step1.jsp 코드 일부

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

 

📌HttpServletRequest

@Controller
public class RegisterController {

	...

	@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";
	}
}

📌@RequestParam 애노테이션

@Controller
public class RegisterController {

	...

	@PostMapping("/register/step2")
	public String handleStep2(
			@RequestParam(value = "agree", defaultValue = "false") Boolean agree) {
		if (!agree) { //약관 동의X인 경우 -> step1 뷰로 리턴
			return "register/step1";
		}
		return "register/step2";
	}
}
속성 타입 설명
value String HTTP 요청 파라미터의 이름을 지정한다
required Boolean 필수 여부를 지정한다. 이 값이 true이면서 해당 요청 파라미터에 값이 없으면 익셉션이 발생한다. 기본값은 true 이다.
defaultValue String 요청 파라미터가 값이 없을 때 사용할 문자열 값을 지정한다. 기본값은 없다.

 

스프링 MVC는 파라미터 타입에 맞게 String 값을 변환해준다.

 

 

❗리다이렉트 처리

http://localhost:8080/sp5-chap11/register/step2 로 바로 접속하려고 하면 에러가 발생한다. 그 이유는 handleStep2() 메서드가 GET 요청 처리를 지원하지 않기 때문이다.

이때 에러 출력보다 알맞은 경로로 리다이렉션해주는 것이 좋다.

@Controller
public class RegisterController {

	...

	@GetMapping("/register/step2")
	public String handleStep2Get() {
		return "redirect:/register/step1"; //리다이렉션 코드
	}
}

 

 

❗커맨드 객체를 이용해서 요청 파라미터 사용하기

register/step2.jsp 코드 일부

<form action="step3" method="post">
    <p>
        <label>이메일:<br>
        <input type="text" name="email" id="email">
        </label>
    </p>
    <p>
        <label>이름:<br>
        <input type="text" name="name" id="name">
        </label>
    </p>
    <p>
        <label>비밀번호:<br>
        <input type="password" name="password" id="password">
        </label>
    </p>
    <p>
        <label>비밀번호 확인:<br>
        <input type="password" name="confirmPassword" id="confirmPassword">
        </label>
    </p>
    <input type="submit" value="가입 완료">
</form>
  • email
  • name
  • password
  • confirmPassword

📌커맨드(Command) 객체

HttpServletRequest로 위 요청 파라미터들을 모두 가져오려고 하면 코드가 굉장히 길어진다. 스프링은 이런 불편함을 줄이기 위해 요청 파라미터의 값을 커맨드(Command) 객체에 담아주는 기능을 제공한다. 특별한 코드를 추가적으로 작성하는 과정 없이, 요청 파라미터의 값을 전달받을 수 있는 세터 메서드를 포함하는 객체를 커맨드 객체로 사용하면 된다.

@Controller
public class RegisterController {

	private MemberRegisterService memberRegisterService;

	public void setMemberRegisterService(
			MemberRegisterService memberRegisterService) {
		this.memberRegisterService = memberRegisterService;
	}

	...

	@PostMapping("/register/step3")
	public String handleStep3(RegisterRequest regReq) { //커맨드 객체: RegisterRequest
		try {
			memberRegisterService.regist(regReq);
			return "register/step3"; //회원가입 성공
		} catch (DuplicateMemberException ex) {
			return "register/step2"; //회원가입 실패
		}
	}

}

MemberRegisterService.java

RegisterRequest.java

 

GitHub - madvirus/spring5fs: 스프링5 프로그래밍 입문 책 소스 코드

스프링5 프로그래밍 입문 책 소스 코드. Contribute to madvirus/spring5fs development by creating an account on GitHub.

github.com

 

RegisterController를 위와 같이 수정하면 memberRegisterService 타입 빈을 의존하게 되므로 설정 클래스에 의존 주입 코드를 추가해주어야 한다.

@Configuration
public class ControllerConfig {

	@Autowired
	private MemberRegisterService memberRegSvc;

	@Bean
	public RegisterController registerController() {
		RegisterController controller = new RegisterController();
		controller.setMemberRegisterService(memberRegSvc); //의존 주입
		return controller;
	}
	
}

 

 

❗뷰 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><strong>${registerRequest.name}님</strong> 
        회원 가입을 완료했습니다.</p>
    <p><a href="<c:url value='/main'/>">[첫 화면 이동]</a></p>
</body>
</html>

스프링 MVC는 커맨드 객체의 클래스 이름(RegisterRequest)와 동일한 속성 이름을 사용해서 커맨드 객체를 뷰에 전달한다.

 

❗@ModelAttribute 애노테이션으로 커맨드 객체 속성 이름 변경

📌@ModelAttribute

	@PostMapping("/register/step3")
	public String handleStep3(@ModelAttribute("formData") RegisterRequest regReq) {
		...
	}

커맨드 객체에 접근할 때 사용할 속성 이름을 변경하고 싶다면, @ModelAttribute 애노테이션을 사용하면 된다. 이제 뷰 코드에서 지정한 이름(formData)로 커맨드 객체에 접근할 수 있다.

 

 

❗커맨드 객체와 스프링 폼 연동

만약 register/step2 에서 중복된 이메일을 입력하면 다시 리턴된 step2 뷰는 텅 빈 폼을 보여준다. 이때 값을 다시 입력해야 하는 불편함이 생긴다. 이런 불편함은 다시 폼을 보여줄 때 커맨드 객체의 값을 폼에 채워주는 것으로 해소할 수 있다.

<input type="text" name="email" id="email" value="${registerRequest.email}">

위와 같이 value 속성에 커맨드 객체의 필드를 지정해주면 된다.

 

그러나 역시 하나하나 다 지정해주기가 귀찮은데….

스프링 MVC가 제공하는 커스텀 태그를 사용하면 더 간단하게 커맨드 객체의 값을 출력할 수 있다.

<form:form action="step3" modelAttribute="registerRequest">
    <p>
        <label>이메일:<br>
        <form:input path="email" />
        </label>
    </p>
    <p>
        <label>이름:<br>
        <form:input path="name" />
        </label>
    </p>
    <p>
        <label>비밀번호:<br>
        <form:password path="password" />
        </label>
    </p>
    <p>
        <label>비밀번호 확인:<br>
        <form:password path="confirmPassword" />
        </label>
    </p>
    <input type="submit" value="가입 완료">
 </form:form>

<form:form>

  • action : <form> 태그의 action 속성과 동일한 값을 사용한다.
  • modelAttribute : 커맨드 객체의 속성 이름을 지정한다. 설정하지 않은 경우 “command”를 기본값으로 사용한다. 예제에서 커맨드 객체의 속성 이름은 “registerRequest” 이므로 이 이름을 modelAttribute 속성값으로 설정했다.

<form:form> 태그를 사용하려면 커맨드 객체가 존재해야 한다. step2.jsp에서 <form:form> 태그를 사용하기 때문에 step1에서 step2로 넘어오는 단계에서 이름이 “registerRequest”인 객체를 모델에 넣어야 <form:form> 태그가 정상 동작한다.

@Controller
public class RegisterController {

	...

	@PostMapping("/register/step2")
	public String handleStep2(
			@RequestParam(value = "agree", defaultValue = "false") Boolean agree,
			Model model) {
		if (!agree) {
			return "register/step1";
		}
		model.addAttribute("registerRequest", new RegisterRequest()); //모델에 추가
		return "register/step2";
	}

}

 

 

❗컨트롤러 구현 없는 경로 매핑

register/step3.jsp 코드 일부

<p><a href="<c:url value='/main'/>">[첫 화면 이동]</a></p>

 

@Controller
public class MainController {
	@RequestMapping("/main")
	public String main() {
		return "main";
	}
}

이전에 했던 것과 같이 main 컨트롤러를 작성하여 요청경로와 뷰를 연결시킬 수 있다. 그러나 이렇게 특별한 로직이 없이 단순 연결을 위해서 컨트롤러 클래스를 만드는 것은 좋지 않다.

📌WebMvcConfigurer 인터페이스

WebMvcConfigurer 인터페이스의 addViewControllers() 메서드를 사용하면 위와 같이 컨트롤러 클래스를 만들 필요가 없다.

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

	...

	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/main").setViewName("main");
	}

}

 

 

❗주요 에러 발생 상황

📌요청 매핑 애노테이션 관련된 주요 익셉션

404 에러

  • 요청 경로가 올바른지
  • 컨트롤러에 설정한 경로가 올바른지
  • 컨트롤러 클래스를 빈으로 등록했는지
  • 컨트롤러 클래스에 @Controller 애노테이션을 적용했는지
  • 뷰 이름에 해당하는 JSP 파일이 있는지

405 에러

  • 해당 전송 방식(method)를 지원되는지

📌@RequestParam이나 커맨드 객체와 관련된 주요 익셉션

400 에러

  • 필수로 존재해야 하는(+ 기본값 없음) 파라미터가 전달됐는지
  • 요청 파라미터의 값이 @RequestParam이 적용된 파라미터의 타입으로 변환 가능한지

무엇보다 에러가 발생한 경우, 로그 메시지를 출력하여 에러 발생 위치와 원인을 확인하는 과정이 필요하다.

 

 

❗커맨드 객체 : 중첩, 콜랙션 프로퍼티

survey/Respondent.java : 응답자 정보

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;
	}

}

 

survey/AnsweredData.java : 설문 답변과 응답자 정보

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;
	}

}

AnsweredData의 특징

  • 리스트 타입의 프로퍼티 : responses
  • 중첩 프로퍼티 : res

스프링 MVC는 커맨드 객체가 리스트 타입의 프로퍼티를 가졌거나 중첩 프로퍼티를 가진 경우에도 요청 파라미터의 값을 적절히 커맨드 객체에 설정해주는 기능을 가진다.

  • 리스트 타입의 프로퍼티 : HTTP 요청 파라미터 이름을 “프로퍼티이름[인덱스]” 형식으로 지정
  • 중첩 프로퍼티 : HTTP 요청 파라미터 이름을 “프로퍼티이름.프로퍼티이름” 형식으로 지정

SurveyController.java

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

	@GetMapping
	public String form() {
		return "survey/surveyForm";
	}

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

}

 

ControllerConfig.java

@Configuration
public class ControllerConfig {

	...
	
	@Bean
	public SurveyController surveyController() {
		return new SurveyController();
	}
}

 

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>

 

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>

 

 

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

  • 요청 매핑 애노테이션이 적용된 메서드의 파라미터로 Model을 추가
  • Model 파라미터의 addAttribute() 메서드로 뷰에서 사용할 데이터 전달
model.addAttribute("greeting", "안녕하세요, "+ name); //속성이름: greeting => ${greeting}

 

기존에 jsp 파일에 하드코딩했던 설문 항목들을 컨트롤러에서 생성해서 뷰에 전달하는 방식으로 변경하려고 한다.

Question.java : 설문 클래스

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.java : 설문 항목 주입 및 전달

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

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

	private List<Question> createQuestions() {
		Question q1 = new Question("당신의 역할은 무엇입니까?",
				Arrays.asList("서버", "프론트", "풀스택"));
		Question q2 = new Question("많이 사용하는 개발도구는 무엇입니까?",
				Arrays.asList("이클립스", "인텔리J", "서브라임"));
		Question q3 = new Question("하고 싶은 말을 적어주세요.");
		return Arrays.asList(q1, q2, q3);
	}

	...

}

 

surveyForm.jsp : 모델에서 받은 Question 리스트를 이용해 폼 생성

<%@ 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>
    <h2>설문조사</h2>
    <form method="post">
    <c:forEach var="q" items="${questions}" varStatus="status">
    <p>
        ${status.index + 1}. ${q.title}<br/>
        <c:if test="${q.choice}">
            <c:forEach var="option" items="${q.options}">
            <label><input type="radio" 
                           name="responses[${status.index}]" value="${option}">
                ${option}</label>
            </c:forEach>
        </c:if>
        <c:if test="${! q.choice }">
        <input type="text" name="responses[${status.index}]">
        </c:if>
    </p>
    </c:forEach>

    <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>

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

지금까지 구현한 컨트롤러의 특징

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

ModelAndView를 사용하면 위 과정을 한번에 처리할 수 있다.

@Controller
@RequestMapping("/survey")
public class SurveyController{
	@GetMapping
	public ModelAndView form(){
		List<Question> questions=createQuestions();
		ModelAndView mav=new ModelAndView();
		mav.addObject("questions", questions); //모델 데이터 추가
		mav.setViewName("survey/surveyform"); //뷰 지정
		return mav;
	}
	...
}

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

Model을 거치지 않고 커맨드 객체를 직접 파라미터로 추가할 수 있다.

@Controller
public class RegisterController {

	...

	@PostMapping("/register/step2")
	public String handleStep2(
			@RequestParam(value = "agree", defaultValue = "false") Boolean agree,
			RegisterRequest registerRequest) { //커맨드 객체
		if (!agree) {
			return "register/step1";
		}
		return "register/step2";
	}

}

@ModelAttribute로 커맨드 객체의 이름을 명시한다.

@Controller
@RequestMapping("/login")
public class LoginController{
	@GetMapping
	public String form(@ModelAttribute("login") LoginCommand loginCommand){
		return "login/loginForm";
	}

	@PostMapping
	public String form(@ModelAttribute("login") LoginCommand loginCommand){
		...
	}
}

 

 

❗주요 폼 태그 설명

스프링 MVC는 HTML 폼과 커맨드 객체를 연동하기 위한 JSP 태그 라이브러리를 제공한다.

📌<form> 태그를 위한 커스텀 태그 : <form:form>

  • action : 폼 데이터를 전송할 URL을 입력(HTML <form> 태그 속성)
  • enctype : 전송될 데이터의 인코딩 타입(HTML <form> 태그 속성과 동일)
  • method : 전송방식. 속성을 지정하지 않을 시엔 “post”로 설정됨(HTML <form>태그 속성과 동일)
  • modelAttribute : 커맨드 객체의 이름 설정

📌<input> 관련 커스텀 태그 : <form:input>, <form:password>, <form:hidden>

커스텀 태그 설명
<form:input> text 타입의 <input> 태그
<form:password> password 타입의 <input> 태그
<form:hidden> hidden 타입의 <input> 태그
  • path : 연결할 커맨드 객체의 프로퍼티 지정

📌<select> 관련 커스텀 태그 : <form:select>, <form:options>, <form:option>

커스텀 태그 설명
<form:select> <select>태그를 생성한다. <option> 태그를 생성할 때 필요한 콜렉션을 전달받을 수도 있다.
<form:options> 지정한 콜렉션 객체를 이용하여 <option> 태그를 생성한다. items
<form:option> <option> 태그 한 개를 생성한다. path

📌체크박스 관련 커스텀 태그 : <form:checkboxes>, <form:checkbox>

커스텀 태그 설명
<form:checkboxes> 커맨드 객체의 특정 프로퍼티와 관련된 checkbox 타입의 <input> 태그 목록을 생성한다.
items, path
<form:checkbox> 커맨드 객체의 특정 프로퍼티와 관련된 한 개의 checkbox 타입 <input> 태그를 생성한다.
value, path

📌라디오버튼 관련 커스텀 태그 : <form:radiobuttons>, <form:radiobutton>

커스텀 태그 설명
<form:radiobuttons> 커맨드 객체의 특정 프로퍼티와 관련된 radio 타입의 <input> 태그 목록을 생성한다.
items, path
<form:radiobutton> 커맨드 객체의 특정 프로퍼티와 관련된 한 개의 radio 타입의 <input> 태그를 생성한다.
path

📌<textarea> 태그를 위한 커스텀 태그 : <form:textarea>

  • path : 커맨드 객체 지정

📌CSS 및 HTML 태그와 관련된 공통 속성

CSS

  • cssClass : HTML의 class 속성값
  • cssErrorClass : 폼 검증 에러가 발생했을 때 사용할 HTML의 class 속성값
  • cssStyle : HTML의 style 속성값

HTML

  • id, title, dir
  • disabled, tabindex
  • onfocus, onblur, onchange
  • onclick, onblclick
  • onkeydown, onkeypress, onkeyup
  • onmousedown, onmousemove, onmouseup
  • onmouseout, onmouseover

Spring 1

EDITOR: 로자

728x90

관련글 더보기