상세 컨텐츠

본문 제목

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

22-23/22-23 Spring 2

by dev otcroz 2022. 12. 22. 10:00

본문

728x90

 

11장의 키워드

#RequestMapping

#GetMapping

#PostMapping

#커맨드 객체

#리다이렉트


프로젝트 준비

  • pom.xml 작성, 코드 복사
  • DB 연동을 위한 모듈 설정
  • DataSource, 트랜잭션 설정
  • 스프링 MVC 기본 설정 파일

 

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

웹 어플리케이션 개발

  • 특정 요청 URL을 처리할 코드 작성  컨트롤러(Controller)
  • 처리 결과를 HTML 형식으로 응답하는 코드 ⇒ 뷰(View)

요청 매핑 애노테이션

@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 구분하기: @GetMapping, @PostMapping

GET과 POST의 차이가 뭘까요?

  • GET: 클라이언트에서 서버로부터 데이터를 요청할 때 사용하는 방법 / ex) 검색, 게시글 목록 불러오기
  • POST: 클라이언트에서 서버로 데이터를 생성하거나 업데이트할 때 사용하는 방법 / ex) 게시글 작성

📝참고자료: 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: getParameter()로 접근
  • @RequestParam 애노테이션 사용
// 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 애노테이션 속성

  • value(String): HTTP 요청 파라미터의 이름을 지정한다.
  • required(boolean): 필수 여부를 지정한다. 디폴트는 true이고, 해당 요청 파라미터에 값이 없으면 익셉션이 발생한다.
  • defaultValue(String): 디폴트 값 설정, 요청 파라미터 값이 없으면 false 문자열을 값으로 사용

 

리다이렉트 처리

웹 브라우저에서 http://localhost:8000/sp5-chap11/register/step2를 직접 입력하면 에러 화면이 출력된다.

Google의 에러 화면

웹 브라우저에서 주소를 직접 입력하면 에러 화면이 출력되는 이유?

직접 주소를 입력하여 웹 브라우저에 접속한다 GET 방식의 요청

예제의 코드 ⇒ POST 방식의 요청

 

리다이렉트를 통해 에러 화면 대신 다른 화면으로 이동하도록 처리

컨트롤러에서 redirect:경로를 뷰 이름으로 리턴

 

  • "/"로 시작하면 웹 어플리케이션을 기준으로 이동 경로 생성: /sp5-chap11/register/step1
  • "/"로 시작하지 않으면 현재 요청 경로를 기준으로 상대 경로 사용: ex) redirect:step1 ⇒ http://localhost:8000/sp5-chap11/register/step1
  • 완전한 경로 사용: redirect:http://localhost:8000/sp5-chap11/register/step2

 

@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 파라미터로 전달

 

 

  1. 스프링 MVC가 handleStep3() 메서드에 전달할 RegisterRequest 객체를 인자로 사용
  2. 객체의 세터 메서드를 이용하여 일치하는 요청 파라미터 값을 전달
// 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;
	}
 }

뷰 JSP 코드에서 커맨드 객체 사용하기

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

728x90

관련글 더보기