이전에는 타코 디자인 페이지를 작성했다.
이번에는 Submit your taco 버튼을 클릭했을 때 요청을 처리하는 컨트롤러의 메서드를 작성할 것이다.
ℓ 폼 제출 처리하기
뷰(design.html)의 form 태그를 다시 보면 method 속성이 POST로 설정되어 있는데도 form에는 action 속성이 선언되지 않은 것을 알 수 있다.
그러므로 지금부터는 design 경로의 POST 요청을 처리하는 새로운 메서드를 작성할 것이다.
DesignTacoController 클래스 안에 있는 showDesignForm() 메서드가 /design 경로의 HTTP GET 요청을 처리하도록 지정하기 위해 @GetMapping 애노테이션을 사용하였다. (유사한 POST 요청 처리에는 @PostMapping 애노테이션이 있다)
@PostMapping
public String processDesign(Taco design) {
log.info("Processing design: " + design);
return "redirect:/orders/current";
}
<@PostMapping을 사용해서 POST 요청 처리하기>
@PostMapping 애노테이션은 processDesign()이 /design 경로의 POST 요청을 처리함을 나타낸다.
따라서 타코를 디자인하는 사용자가 제출한 것을 여기서 처리해야 한다.
타코 디자인 폼이 제출될 때 이 폼의 필드는 processDesign()의 인자로 전달되는 Taco 객체의 속성으로 바인딩된다.
showDesignForm() 메서드처럼 processDesign()도 String 값을 반환하고 종료한다.
processDesign() 메서드에서 반환되는 값은 리디렉션 뷰를 나타내는 "redirect:"가 제일 앞에 붙는다.
즉, 메소드 실행이 끝난 후 사용자의 브라우저가 /orders/current 상대 경로로 재접속되어야 한다는 것을 나타낸다.
/orders/current 경로의 요청을 처리할 컨트롤러를 생성해보자.
package tacos.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import lombok.extern.slf4j.Slf4j;
import tacos.Order;
@Slf4j
@Controller
@RequestMapping("/orders")
public class OrderController {
@GetMapping("/current")
public String orderForm(Model model) {
model.addAttribute("order", new Order());
return "orderForm";
}
}
<타코 주문 폼을 나타내는 컨트롤러>
@RequestMapping 애노테이션은 /orders로 시작되는 경로의 요청을 이 컨트롤러의 요청 처리 메서드가 처리한다는 것을 알려준다.
@GetMapping을 함께 지정하여 /orders/current 경로의 HTTP GET 요청을 orderForm() 메서드가 처리한다는 것을 알려준다.
orderForm.html 뷰를 만들어보자
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="EUC-KR">
<title>Taco Cloud</title>
<link rel="stylesheet" th:href="@{/styles.css}" />
</head>
<body>
<form method="POST" th:action="@{/orders}" th:object="${order}">
<h1>Order your taco creations!</h1>
<img th:src="@{/images/TacoCloud.png}"/>
<a th:href="@{/design}" id="another">Design another taco</a><br/>
<div th:if="${#fields.hasErrors()}">
<span class="validationError">
Please correct the problems below and resubmit.
</span>
</div>
<h3>Deliver my taco masterpieces to...</h3>
<label for=" deliveryName">Name: </label>
<input type="text" th:field="*{deliveryName}"/>
<span class="validationError"
th:if="${#fields.hasErrors('deliveryName')}"
th:errors="*{deliveryName}">Name Error</span>
<br/>
<label for="deliveryStreet">Street address: </label>
<input type="text" th:field="*{deliveryStreet}"/>
<span class="validationError"
th:if="${#fields.hasErrors('deliveryStreet')}"
th:errors="*{deliveryStreet}">Street Error</span>
<br/>
<label for="deliveryCity">City: </label>
<input type="text" th:field="*{deliveryCity}"/>
<span class="validationError"
th:if="${#fields.hasErrors('deliveryCity')}"
th:errors="*{deliveryCity}">City Error</span>
<br/>
<label for="deliveryState">State: </label>
<input type="text" th:field="*{deliveryState}"/>
<span class="validationError"
th:if="${#fields.hasErrors('deliveryState')}"
th:errors="*{deliveryState}">State Error</span>
<br/>
<label for="deliveryZip">Zip code: </label>
<input type="text" th:field="*{deliveryZip}"/>
<span class="validationError"
th:if="${#fields.hasErrors('deliveryZip')}"
th:errors="*{deliveryZip}">Zip Error</span>
<br/>
<h3>Here's how I'll pay...</h3>
<label for="ccNumber">Credit Card #: </label>
<input type="text" th:field="*{ccNumber}"/>
<span class="validationError"
th:if="${#fields.hasErrors('ccNumber')}"
th:errors="*{ccNumber}">CC Num Error</span>
<br/>
<label for="ccExpiration">Expiration: </label>
<input type="text" th:field="*{ccExpiration}"/>
<span class="validationError"
th:if="${#fields.hasErrors('ccExpiration')}"
th:errors="*{ccExpiration}">CC Num Error</span>
<br/>
<label for="ccCVV">CVV: </label>
<input type="text" th:field="*{ccCVV}"/>
<span class="validationError"
th:if="${#fields.hasErrors('ccCVV')}"
th:errors="*{ccCVV}">CC Num Error</span>
<br/>
<input type="submit" value="Submit order"/>
</form>
</body>
</html>
<타코 주문 폼 뷰>
form 태크에 폼 액션을 지정하지 않으면 URL로 폼의 HTTP POST 요청이 제출될 것이다.
그러나 여기서는 /orders 경로로 제출되도록 지정하고 있다.
따라서 /orders 경로의 POST 요청을 처리하는 또 다른 메서드를 OrderController 클래스에 추가해야 한다.
타코 주문 제출 처리하도록 만들어보자.
package tacos.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import lombok.extern.slf4j.Slf4j;
import tacos.Order;
import org.springframework.web.bind.annotation.PostMapping;
@Slf4j
@Controller
@RequestMapping("/orders")
public class OrderController {
@GetMapping("/current")
public String orderForm(Model model) {
model.addAttribute("order", new Order());
return "orderForm";
}
@PostMapping
public String processOrder(Order order) {
log.info("Order submitted: " + order);
return "redirect:/";
}
}
<타코 주문 제출 처리하기>
processDesign() 메서드가 호출될 때는 제출된 폼 필드와 바인딩된 속성을 갖는 Order 객체가 인자로 전달된다.
타코 주문 정보를 갖는 도메인 객체를 만들어 보자.
package tacos;
import lombok.Data;
@Data
public class Order {
private String deliveryName;
private String deliveryStreet;
private String deliveryCity;
private String deliveryState;
private String deliveryZip;
private String ccNumber;
private String ccExpiration;
private String ccCVV;
}
<타코 주문 정보를 갖는 도메인 객체>
폼의 일부 필드에 아무 값이나 입력하고 Submit order 버튼을 누른다.
주문 처리가 끝나고 타코 홈페이지가 나타난다. 애플리케이션 로그에 주문 정보를 볼 수 있다.
2023-09-11 11:38:49.069 INFO 9260 --- [nio-8080-exec-8] tacos.web.OrderController
: Order submitted: Order(deliveryName=123, deliveryStreet=123, deliveryCity=123,
deliveryState=123, deliveryZip=123, ccNumber=123, ccExpiration=123, ccCVV=123)
<주문정보>
폼 제출을 처리했지만, 잘못된 정보의 입력을 허용한다는 것을 알 수 있다.
그러므로 다음 장에 필요한 정보에 맞도록 데이터 검사하는 것을 만들 것이다.
'개발 서적 > 스프링 인 액션' 카테고리의 다른 글
스프링 인 액션 Chapter 2.4 :: 뷰 컨트롤러로 작업하기 (0) | 2023.09.19 |
---|---|
스프링 인 액션 Chapter 2.3 :: 폼 입력 유효성 검사하기 (0) | 2023.09.12 |
스프링 인 액션 Chapter 2.1 :: 정보 보여주기 (0) | 2023.08.24 |
스프링 인 액션 Chapter 1.1 :: 스프링 시작하기 (1) | 2023.08.23 |