11장의 키워드
#RequestMapping
#GetMapping
#PostMapping
#커맨드 객체
#리다이렉트
❓웹 어플리케이션 개발
❓요청 매핑 애노테이션
@RequestMapping, @GetMapping, @PostMapping
요청 애노테이션을 사용한 컨트롤러 클래스를 이용하여 URL을 처리할 코드를 구현한다.
@Controller
public class HelloController{
@GetMapping("/hello") // GET 매핑 애노테이션을 사용하여 요청 URL에 대한 처리
public String hello(Model model,
@RequestParam(value="name", required=false) String name){
model.addAttribute("greeting", "안녕하세요, " + name); // 속성 추가
return "hello"; // 뷰 이름
}
}
요청 매핑 애노테이션을 적용한 메서드를 2개 이상 정의할 수 있다.
클래스에 적용한 요청 매핑 애노테이션 경로 + 메서드에 적용한 요청 매핑 애노테이션
관련 요청 경로를 한 개의 컨트롤러 클래스에서 처리하여 코드 관리를 용이하게 할 수 있다.
@Controller
@RequestMapping("/register") // 각 메서드에 공통되는 경로
public class RegisterController{
@RequestMapping("/step1") // 나머지 경로
public String gendleStep1(){
return "reguster/step1";
}
@RequestMapping("/step2") // 나머지 경로
public String gendleStep1(){
return "reguster/step2";
}
}
Config 파일을 작성하고 이 파일에 다음 클래스를 빈에 등록한다.
@Configuration
public class ControllerConfig {
@Bean // 빈으로 등록
public RegisterController registerController() {
return controller;
}
}
❓GET과 POST의 차이가 뭘까요?
📝참고자료: GET과 POST의 차이를 아시나요? - Velog
@RequestMapping: GET, POST 상관 없이 애노테이션에서 지정한 경로와 일치하는 경로 요청
@GetMapping, PostMapping: GET, POST을 구분하여 요청을 처리한다.
GET, POST에 대한 처리를 다른 메서드가 처리하도록 설정한다.
이전 버전: RequestMapping 애노테이션의 method 속성을 사용하여 GET, POST 등을 제한했다.
@Controller
public class RegisterController {
@RequestMapping("/register/step1") // GET 요청과 POST 요청을 모두 처리
public String handleStep1() {
return "register/step1";
}
@PostMapping("/register/step2") // POST에 대한 요청 처리
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";
}
}
컨트롤러 메서드에서 요청 파라미터에 접근하는 방법
// HTTPServletRequest로 요청인자에 접근
@Controller
public class RegisterController {
@PostMapping("/register/step2") // POST에 대한 요청 처리
public String handleStep2(HTTPServletRequest request)( // 컨트롤러의 처리 메서드 인자
String agreeParam = request.getParameter("agree"); // getParameter 메서드, 요청 인자 찾음
if (agreeParam == null || !agreeParam.equals("true")) {
return "register/step1";
}
model.addAttribute("registerRequest", new RegisterRequest());
return "register/step2";
}
}
// RequestParam을 사용하여 요청인자에 접근
@Controller
public class RegisterController {
@PostMapping("/register/step2") // POST에 대한 요청 처리
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";
}
}
🧷@RequestParam 애노테이션 속성
웹 브라우저에서 http://localhost:8000/sp5-chap11/register/step2를 직접 입력하면 에러 화면이 출력된다.
❓웹 브라우저에서 주소를 직접 입력하면 에러 화면이 출력되는 이유?
직접 주소를 입력하여 웹 브라우저에 접속한다 ⇒ GET 방식의 요청
예제의 코드 ⇒ POST 방식의 요청
리다이렉트를 통해 에러 화면 대신 다른 화면으로 이동하도록 처리
컨트롤러에서 redirect:경로를 뷰 이름으로 리턴
@Controller
public class RegisterController {
...
@GetMapping("/register/step2")
public String handleStep2Get() {
return "redirect:/register/step1";
}
...
}
요청 파라미터를 처리할 때 다음과 같이 getParameter() 메서드를 사용하여 코드를 작성할 수 있다.
하지만 코드가 길어진다는 단점이 있다.
@Controller
public class RegisterController {
@PostMapping("/register/step2")
public String handleStep2(HTTPServletRequest request)(
// getParameter 메서드, 요청 인자 찾음
String email = request.getParameter("email");
String name = request.getParameter("name");
String age = request.getParameter("age");
String number = request.getParameter("number");
...
}
}
💡해결 방법 : 세터 메서드를 포함하는 객체를 커맨드 객체로 사용!
RegisterRequest 클래스에는 set 메서드가 있다.
public class RegisterRequest {
private String email;
private String password;
private String confirmPassword;
private String name;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isPasswordEqualToConfirmPassword() {
return password.equals(confirmPassword);
}
}
이 메서드를 사용하여 요청 파라미터의 값을 커맨드 객체에 복사한 뒤, regReq 파라미터로 전달
// RegisterController
@Controller
public class RegisterController {
...
// 회원가입을 처리하는 코드
@PostMapping("/register/step3")
public String handleStep3(RegisterRequest regReq) { // 인자의 타입: RegisterRequest
try {
memberRegisterService.regist(regReq); // regReq를 memberRegisterService로 넘김
return "register/step3";
} catch (DuplicateMemberException ex) {
return "register/step2";
}
}
...
}
// memberRegisterService.java
public Long regist(RegisterRequest req) { // regReq를 인자로 받는다.
Member member = memberDao.selectByEmail(req.getEmail());
if (member != null) {
throw new DuplicateMemberException("dup email " + req.getEmail());
}
Member newMember = new Member(
req.getEmail(), req.getPassword(), req.getName(),
LocalDateTime.now());
memberDao.insert(newMember);
return newMember.getId();
}
RegisterController 클래스는 MemberRegisterService 타입의 빈을 의존,
아래 코드와 같이 의존 주입을 설정한다.
@Configuration
public class ControllerConfig {
// 자동 의존 주입 설정
@Autowired
private MemberRegisterService memberRegSvc;
@Bean
public RegisterController registerController() {
RegisterController controller = new RegisterController();
controller.setMemberRegisterService(memberRegSvc);
return controller;
}
}
// HelloController.java
@RequestMapping("/hello")
public String hello(Model model,
@RequestParam(value = "name", required=false,) String name){
model.addAttribute("greeting", "안녕하세요, " + name); // JSP에서 사용할 속성 추가
return "hello";
}
// JSP 코드
<% %>
인사말: ${greeting} // 속성 사용
<% %>
${커맨드 객체.속성}
클래스 이름(맨 앞 글자를 소문자로)과 동일한 속성 이름을 사용
// register/step3.java
<%@ 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>
예시에서는 registerRequest.name으로 커맨드 객체를 통해 속성에 접근하였다.
1. 웹 어플리케이션을 개발하는 것은 주요 2가지의 코드를 작성한다. (특정 요청 URL)을/를 처리할 코드, 처리 결과를 (HTML/JSP)와/과 같은 형식으로 응답하는 코드가 있다.
2. 컨트롤러에서 GET 방식의 요청을 처리하고 싶을 때 (@GetMapping) 애노테이션을 사용하여 요청을 처리하고, POST 방식의 요청을 처리하고 싶을 때 (@PostMapping) 애노테이션을 사용하여 요청을 처리한다.
3. 요청 파라미터에 접근하는 방법은 (HttpServletRequest)를 이용하는 방법과 (@RequestParam) 애노테이션을 사용하는 방법이 있다.
4. @RequestParam 애노테이션 속성 중 (required)은/는 필수 여부를 지정하며 해당 요청 파라미터에 값이 없으면 익셉션이 발생한다.
5. 잘못된 전송 방식으로 요청이 왔을 때 알맞은 경로로 (리다이렉트) 처리를 해준다. 방법은 컨트롤러에서 뷰 이름을 (redirect:경로)으로 리턴한다.
6. getParameter, @RequestParam 을 사용하여 파라미터 값을 접근할 때, 요청 파라미터의 개수가 증가할 때마다 코드가 길어진다는 단점이 있다. 그래서 (커맨드 객체)를 사용하여 컨트롤러의 코드를 간결하게 한다. 요청 파라미터의 값을 전달받거나 설정할 수 있는 (세터 메서드)를 포함하는 객체를 커맨드 객체로 사용한다.
7. 스프링 MVC는 JSP 코드에서 커맨드 객체의 (클래스 이름과 동일한 속성 이름, 첫 글자는 소문자)을/를 사용하여 커맨드 객체를 뷰에 전달한다.
1. 요청 매핑 애노테이션을 적용한 메서드를 2개 이상 적용하여 하나의 컨트롤러에서 여러개의 요청을 처리할 수 있는 형태의 코드를 작성해보자. 클래스 요청 애노테이션 + 메서드 요청 애노테이션 작성
@Controller
public class MemberController{
@RequestMapping("/member/hello1")
public String hello1(){
...
}
@RequestMapping("/member/hello2")
public String hello2(){
}
}
2. 아래는 HttpServletRequest 객체를 사용하여 요청 인자의 값을 접근한 방법으로, @RequestParam 애노테이션을 사용하여 요청인자의 값을 접근할 수 있는 코드로 수정해보자. 이때 default 값은 christmas, 필수 여부는 true로 지정한다.
...
@PostMapping("/merry/christmas")
public String christmas1225(HttpServletRequest request){
String xmas = request.getParameter("xmas");
if(xmas == null){
return "/merry/error";
}
return "/merry/xmas";
}
Corner Spring #2
Editor : otcr
[스프링2] 12장. MVC 2: 메시지, 커맨드 객체 검증 (0) | 2022.12.29 |
---|---|
[스프링2] 11장. MVC 1 : 요청, 매핑, 커맨드 객체, 리다이렉트, 폼 태그, 모델 (2) (0) | 2022.12.22 |
[스프링2] 10장. 스프링 MVC 프레임워크 동작 방식 (0) | 2022.12.01 |
[스프링2] 9장. 스프링 MVC 시작하기 (0) | 2022.11.24 |
[스프링2] 8장. DB연동 (2) (0) | 2022.11.17 |