Spring

[SpringBoot] Request Parameter

shb 2022. 5. 12. 18:03

request parameter를 받는 다양한 방법

스프링에서 제공하는 다양한 방법들에 대해 배웁니다.
1. request parameter 처리
2. GET 방식 / POST 방식 처리
3. @RequestParam
4. 커맨드 객체
5. @ModelAttribute
6. @PathVariable
7. redirect:    forward:

 

프로젝트 생성
Bt011_RequestParam
Starter :  SpringWeb, DevTools, Lombok

 

* HomeController 작성

package com.lec.spring;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.lec.spring.domain.WriteDTO;

@Controller
public class HomeController {

	@RequestMapping(value="/member/delete")  // @RequestMapping: GET, POST, PUT, PATCH, DELETE,.. 어떠한 방식의 request 이든 관계없이 동작
//	public void delMember(HttpServletRequest request, Model model) {
	public void delMember(Model model, HttpServletRequest request) {  // ★ 핸들어의 매개변수 순서 관계 없다!
		String id = request.getParameter("id");
		model.addAttribute("mbId", id);
	}
	
	//@RequestMapping(value="/member/regOk", method= RequestMethod.POST)  // POST 방식 request 일때만 처리
	@PostMapping("/member/regOk")
	public void registPost() {
		System.out.println("/member/regOk : POST");
	}
	
//	@RequestMapping(value="/member/regOk", method= RequestMethod.GET)
	@GetMapping("/member/regOk")
	public void registGet() {
		System.out.println("/member/regOk : GET");
	}
	
	// GET, POST 두가지 방식만 매핑하려면?
	@RequestMapping(value="/member/regOk2", method= {RequestMethod.GET, RequestMethod.POST})
	public String regOkMember2(HttpServletRequest request) {
		System.out.println("/membre/regOk2 : " + request.getMethod());
		return "member/regOk";
	}
	
	@RequestMapping("/member/regist")
	public void registMember() {}
	
	
	@RequestMapping("/member/find")
//	public void findMember(String id, String name, Model model) {
//	public void findMember(Model model, String id, String name) {  // 매개변수 순서에 영향 받지 않는다.
//	public void findMember(double id, String name, Model model) {  // 숫자타입이면, 해당타입으로 변환하여 binding
	
//	public void findMember(Double id, String name, Model model) { //  Wrapper 타입을 사용하면 id parameter 가 없는 경우에도 에러 없이 동작		
//		System.out.println("member/find: id=" + id + ", name=" + name);
//		
//		model.addAttribute("id", id);
//		model.addAttribute("name", name);
//	}
	
	// 동일한 name 의 request parameter 복수개
//	public void findMember(Integer[] id, String [] name, Model model) {
//		model.addAttribute("id", Arrays.toString(id));
//		model.addAttribute("name", Arrays.toString(name));
//	}
	
	// 만약 request parameter 의 name 과 매개변수가 같을수 없는 상황이면 
	// @RequestParam 애노테이션 사용
//	public void findMember(Model model,
//			@RequestParam("id") String userid, // "id" 란 name 의 parameter 값을 userid 매개변수에 binding 해준다
//			@RequestParam("name") String username 
//			) {
//		model.addAttribute("id", userid);
//		model.addAttribute("name", username);
//	}
	
	// 위의 경우 id 값이 없거나 변환 불가능하면 에러 발생한다.
	// @RequestParam(value="test", required=false, defaultValue="0") 을 이용하면 가능하긴 하다.		
	// 또한, @RequestParam 과 Map<name, value> 을 사용하면 된다. 	
	
	public void findMember(Model model,
			@RequestParam Map<String, String> map
			) {
		
		model.addAttribute("id", map.get("id"));
		model.addAttribute("name", map.get("name"));
	}
	
	// ---------------------------------------------------
	// 커맨드 객체 (Command object) 사용
	
	// 게시글 등록 form
	@RequestMapping("/board/write")
	public void writeBoard() {}
	
	
	@PostMapping("/board/writeOk")
//	public void writeOkBoard(Model model,
//			String name,
//			String subject,
//			String content
//			) {
//		
//		WriteDTO dto = WriteDTO.builder()
//				.name(name)
//				.subject(subject)
//				.content(content)
//				.build();
//		
//		model.addAttribute("dto", dto);
//		
//	}
	// 커맨드 객체 사용방식
	// 코드 작업량이 매우 줄어든다. (성능도 더 좋다)
//	public void writeOkBoard(WriteDTO dto) {
	
//  @ModelAttribute : 커맨드 객체의 model attribute 이름을 변경할수 있다.
	public void writeOkBoard(@ModelAttribute("DTO") WriteDTO dto) {
		System.out.println(dto);
	}
	
	//-------------------------------------------------
	// @PathVariable
//	@RequestMapping("/board/writePath/{name}/{subject}/{content}")
//	public String writePathBoard(Model model,
//			@PathVariable String name,
//			@PathVariable String subject,
//			@PathVariable String content
//			) {
	@RequestMapping("/board/writePath/{k1}/{k2}/{k3}")
	public String writePathBoard(Model model,
			@PathVariable(name="k1") String name,
			@PathVariable(name="k2") String subject,
			@PathVariable(name="k3") String content
			) {
		model.addAttribute("name", name);
		model.addAttribute("subject", subject);
		model.addAttribute("content", content);
		return "board/writepath";
	}
	
	
}


* parameter :  HttpServletRequest 매개변수 

String id = request.getParameter("id");
-> request 안의 parameter 값을 뽑아서 Model에 담아 봅니다.

@RequestMapping(value="/member/delete")  // @RequestMapping: GET, POST, PUT, PATCH, DELETE,.. 어떠한 방식의 request 이든 관계없이 동작
//	public void delMember(HttpServletRequest request, Model model) {
	public void delMember(Model model, HttpServletRequest request) {  // ★ 핸들어의 매개변수 순서 관계 없다!
		String id = request.getParameter("id");
		model.addAttribute("mbId", id);
	}

 

핸들러 메소드에  HttpServletRequest 매개변수 받을 수 있다.
이 안에는 parameter 라든지 여러가지 request 관련 정보들이 있다.
JSP/Servlet 에서 배웠던 바로 그 객체이기 때문에 관련 메소드 그대로 사용 가능합니다.

 

* member/delete.jsp 파일 작성

당연히 forwarding 된 JSP니까 request 객체 그대로도 parameter사용 가능

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
${mbId}<br>
<%= request.getParameter("id") %><br>

* 결과 확인
/member/delete?id=55


POST, PUT, DELETE .. 방식 request 받기
기본적으로 @RequestMapping 은 GET 방식에 동작함.
만약 POST 나 PUT, DELETE 같은 다른 방식으로 동작하게 하려면?

 

POST 방식 request 받기

//@RequestMapping(value="/member/regOk", method= RequestMethod.POST)  // POST 방식 request 일때만 처리
	@PostMapping("/member/regOk")
	public void registPost() {
		System.out.println("/member/regOk : POST");
	}


member/regOk.jsp 생성, 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%= request.getParameter("name") %><br>

 

우선 resist.jsp와 핸들러 작성
다양한 방식의 method 로 request 하기 위한 폼을 만들어 보자


컨트롤러 작성

@RequestMapping("/member/regist")
	public void registMember() {}


member/regist.jsp 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%-- action 의 url 과 method 주목 ! --%>
<form action="regOk" method="GET">
	<input type="text" name="name" value="GET"/>
	<input type="submit" value="GET요청"/>
</form>
<hr>
<form action="regOk" method="POST">
	<input type="text" name="name" value="POST"/>
	<input type="submit" value="POST요청"/>
</form>

 

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

- >  동일 request url  & 다른 request method를 다른 handler로 처리 하기
동일한 request url 이지만 request method가 다르면 , 다른 메소드로 request mapping 가능.

 

@GetMapping, @PostMapping ... - Spring 4.3부터 등장
다음은 동일하게 동작한다.

	//@RequestMapping(value="/member/regOk", method= RequestMethod.POST)  // POST 방식 request 일때만 처리
	@PostMapping("/member/regOk")
	public void registPost() {
		System.out.println("/member/regOk : POST");
	}
	
//	@RequestMapping(value="/member/regOk", method= RequestMethod.GET)
	@GetMapping("/member/regOk")
	public void registGet() {
		System.out.println("/member/regOk : GET");
	}

 

* post 와 get 둘다 받으려면?
GET / POST 둘다 받는 request  /member/regOk2 설정

@RequestMapping(value="/member/regOk2", method= {RequestMethod.GET, RequestMethod.POST})
	public String regOkMember2(HttpServletRequest request) {
		System.out.println("/membre/regOk2 : " + request.getMethod());
		return "member/regOk";
	}

register.jsp 파일에도 추가

 

* Put, Delete 방식 request.
기본적으로 <form> 의 method 는 get, post 방식만 지원.
서버 내부의 JSP  요청도 get, post, head 만 지원. → 405  에러 발생
Postman 등의 툴을 활용하여 request 해보자.

 

URL 매핑은 정상적으로 이루어진다 JSP 요청(forwarding)이 안될 뿐

 

매개변수로 request parameter 받기
handler에 request parameter 의 name 값과 ‘같은 이름(name)의 매개변수’ 있으면 
바로 그 매개변수가  request parameter 값을 받아온다
(내부적으로 스프링이 알아서 주입하는 셈이다)

 

@RequestMapping("/member/find")
public void findMember(String id, String name, Model model) {
public void findMember(Model model, String id, String name) {  // 매개변수 순서에 영향 받지 않는다.
public void findMember(double id, String name, Model model) {  // 숫자타입이면, 해당타입으로 변환하여 binding

public void findMember(Double id, String name, Model model) { //  Wrapper 타입을 사용하면 id parameter 가 없는 경우에도 에러 없이 동작
System.out.println("member/find: id=" + id + ", name=" + name);

model.addAttribute("id", id);
model.addAttribute("name", name);
}

 

* 데이터 바인딩 (Data Binding)
"프로퍼티 값을 타겟 객체에 설정해주는 것"
사용자가 입력한 값을 애플리케이션 도메인 객체에 동적으로 할당하는 기능다. Spring에서는 사용자가 입력한 값은 '문자열'이고 도메인 객체에 맞는 자료형으로 변경 필요하기 때문에 추상화되어 있다.

id="aaa" name= "bbb" -> Request -> Dispatcher Servlet -> Binding -> String id String name

                                                                                                   Controller

숫자타입으로 request parameter 받기
스프링은 request parameter 를 받아올때,  handler 의 매개변수 타입으로 형변환,  parsing 가능하면 변환해준다.

 

가급적 Wrapper 타입으로 parameter 받자
parameter 가 없는 경우에도 에러없이 동작하니까.  (null 값으로 동작)

 

* 동일한 name의 request parameter 복수개
‘배열’로 받으면 된다.

 

동일한 name의 request parameter 복수개
public void findMember(Integer[] id, String [] name, Model model) {
model.addAttribute("id", Arrays.toString(id));
model.addAttribute("name", Arrays.toString(name));
}


@RequestParam
상황에 따라서는 handler 메소드의 ‘매개변수 이름’ 을  request parameter 와 일치시켜주기 어려울 때도 있다.
그런경우에는 @RequestParam을 사용하여,  request parameter와 다른 이름의 매개변수로  받을 수 있다.

 

@RequestParam 애노테이션 사용
public void findMember(Model model,
               @RequestParam("id") String userid, // "id" 란 name 의 parameter 값을 userid 매개변수에 binding 해준다
               @RequestParam("name") String username 
) {
    model.addAttribute("id", userid);
    model.addAttribute("name", username);
}

 

@RequestParam + Map<name, value>
위의 경우 id 값이 없거나 변환 불가능하면 에러 발생한다.
@RequestParam(value="text", required=false, defaultValue="0") 을 이용하면 없어도 동작 가능하긴 하다.
또한, @RequestParam 과 Map<name, value> 을 사용하면 된다. 

	public void findMember(Model model,
			@RequestParam Map<String, String> map
			) {
		
		model.addAttribute("id", map.get("id"));
		model.addAttribute("name", map.get("name"));
	}

 

그러나 ‘다량의  parameter’ 를 받기에 Map<> 도 가능하긴 하나 개발한 사람 외에는 유지보수 어렵다.
그래서 다음의 ‘커맨더’ 를 사용한 방식을 많이 사용한다.

 

* ‘커맨드 객체’를 사용한 parameter 전달
HttpServletRequest 를 받든지..  @RequestParam 을 사용하든지…
request parameter 자체가 많아지면,  잔코딩이 많아지고, 유지보수에 어려움이 발생한다.
스프링에서는 ‘커맨드 객체( Command Object )’를 통해, 여러 request parameter 들을 ‘한번에’ 받아올수 있다!!
커맨드 객체란?  : form 으로 부터 넘어오는 parameter들을 담는 ‘빈(bean)’ 객체

 

컨트롤러

	// 커맨드 객체 (Command object) 사용
	
	// 게시글 등록 form
	@RequestMapping("/board/write")
	public void writeBoard() {}
	
	
	@PostMapping("/board/writeOk")
//	public void writeOkBoard(Model model,
//			String name,
//			String subject,
//			String content
//			) {
//		
//		WriteDTO dto = WriteDTO.builder()
//				.name(name)
//				.subject(subject)
//				.content(content)
//				.build();
//		
//		model.addAttribute("dto", dto);
//		
//	}

writeOk.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%--
${dto}
<hr>
작성자: ${dto.name }<br>
글제목: ${dto.subject}<br>
내용: ${dto.content}<br> 
 --%>
<%-- 
작성자: ${writeDTO.name}<br>
글제목: ${writeDTO.subject}<br>
내용: ${writeDTO.content}<br> 
uid: ${writeDTO.uid }<br>
조회수: ${writeDTO.viewCnt }<br>
등록일: ${writeDTO.regDate }<br>
 --%>
 
작성자: ${DTO.name}<br>
글제목: ${DTO.subject}<br>
내용: ${DTO.content}<br> 
uid: ${DTO.uid }<br>
조회수: ${DTO.viewCnt }<br>
등록일: ${DTO.regDate }<br>

<button onclick="history.back()">이전으로</button>

 

커맨드 객체 사용

커맨드 객체(Command Object) handler 메소드 매개변수로 명시하면
스프링 내부에서 빈(bean) 객체가 생성되고 request parameter 들을 담습니다.

스프링 내부에서 WriteDTO 타입의 빈(bean) 객체가 생성되어,  request parameter에 대해 적절한 setter 가 호출되어 세팅됨.
그리고 Model 에 addAttribute() 되어 더해짐.

 

writeOk.jsp

EL 에 사용된 객체의 이름은 handler 매개변수 이름(dto) 가 아니라, 커맨드객체 ‘타입’ 입니다.
WriteDTO 타입의 bean 객체가 스프링 내부에 writeDTO 라는 id 로 생성된겁니다.


용어 문제
‘JSP MVC 모델2’ 의 경우 컨트롤러에서 수행하는 것을 Command 객체라 불렀습니다.
(스프링에서는 (나중에 언급되겠지만) 그와 같은 역할을 하는 객체를 Service 라고 도 합니다. 그러나 혼용되는게 문제)
한편, ‘스프링’에서 ‘커맨드 객체’라 함은 지금 배우고 있는 parameter 받기 위한  bean 객체 입니다
동일한 용어로 사용되는 것들이 있으니,   혼선일으키지 않도록, 문맥을 잘 살핍시다.

 

관찰:  커맨드 객체에 넘어오지 않은 필드값은 ?


@ModelAttribute
기본적으로 커맨드 객체는 객체의 타입명이  model의 ‘attribute’가 됩니다.

 

커맨드 객체의 model 의 ‘attribute’ 이름을 바꾸어 주기 위해선 @ModelAttribute를 사용합니다.

이제 Model 에는 “DTO” 라는 id 로 등록되어, EL 에서 사용 가능.

 

BindException
binding : parameter 들이 java 객체(command 객체 )에 담는 동작.  이 와중에 담길수 없는 (변환할수 없는) 상황이 발생하면 에러 발생

 

@PathVariable
request parameter를 GET 방식의 query string 이 아닌 ‘URL 경로’ 에 담아서 전달할수도 있다!

@RequestMapping("/board/writePath/{k1}/{k2}/{k3}")
	public String writePathBoard(Model model,
			@PathVariable(name="k1") String name,
			@PathVariable(name="k2") String subject,
			@PathVariable(name="k3") String content
			) {
		model.addAttribute("name", name);
		model.addAttribute("subject", subject);
		model.addAttribute("content", content);
		return "board/writepath";
	}

리다이렉트 redirect:     
handler 메소드의 뷰 리턴값 (문자열) 에 redirect: 를 사용하면  해당 URL 로 redirect 된다.
RedirectAttributes 
리다이렉트로 parameter 넘기기
스프링에서 제공하는 RedirectAttributes 객체를 사용해서 리다이텍트 되는 URL 로 parameter 를 넘겨줄수 있습니다.

 

* redirect, forward 방법 총정리

  redirect forward
Servlet response.sendRedirect(URL) RequestDispatcher dispatcher = request.getRequestDispatcher(URL);
dispatcher.forward(request, response);
JSP response.sendRedirect(URL) <jsp:forward page="URL">
pageContext.forward(URL)
Javascript location.href=URL 클라이언트에선 forward란게 있을 수 없다.
Spring handler 메소드에서
return "redirect:URL"
handler 메소드에서
return "forward:URL"

'Spring' 카테고리의 다른 글

[SpringBoot] MyBatis  (0) 2022.05.13
[Spring Boot] Validation  (0) 2022.05.12
[SpringBoot] RequestMapping  (0) 2022.05.12
[SpringBoot] MVC  (0) 2022.05.12
[SpringBoot] Dependency Injection 의존주입  (0) 2022.05.12