매핑된 핸들러 메소드로 우리가 입력하는 데이터가 전달되게 하려면 어떻게 해야할까?
핸들러 메서드의 다양한 파라미터 전달 방법에 대해 알아보자.
WebRequest로 요청 파라미터 값 전달받기
첫번째 방법으론 WebRequest로 파라미터 값을 전달받을 수 있다.
HttpServletRequest/Response, ServletRequest/Response도 핸들러 메소드 매개변수로 사용 가능하지만,
이 방식들은 Servlet에 종속이므로, Servlet에 종속적이지 않은 WebRequest가 Spring 기반의 프로젝트에서 더 자주 사용된다.
WebRequest는 Spring Framework에서 제공한다!
코드를 통해 알아보자.
index.html에서 GET 방식의 /first/regist 요청을 전달한다.
<!--resources/static/index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<h1>핸들러 메소드의 파라미터와 어노테이션</h1>
<h2>1. WebRequest로 요청 파라미터 전달 받기</h2>
<button onclick="location.href='/first/regist'">WebRequest</button>
</body>
</html>
Controller 클래스의 핸들러 메소드를 통해 파라미터 전달 테스트를 위해 값을 입력할 수 있는 view,
여기선 '/first/regist' view를 응답한다.
package org.example.handlermethod;
@Controller
@RequestMapping("/first/*")
public class FirstController {
/* 핸들러 메서드의 반환 값을 void로 설정하면 요청 주소가 곧 view의 이름이 된다.
* => /first/regist 뷰를 응답한다.
* => resources/templates/first/regist.html 파일을 만들어서 작업하자.
* */
@GetMapping("/regist")
public void regist(){}
}
resources/templates/first 하위에 regist.html 파일을 생성한다.
해당 화면에서 사용자 입력 양식에 값을 입력하고 submit을 누르면 POST 방식의 /first/regist 요청이 발생한다.
<!--resources/templates/first/regist.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>regist</title>
</head>
<body>
<h1>WebRequest로 파라미터 값 전달 받기</h1>
<h3>신규 메뉴 등록하기</h3>
<form action="regist" method="post">
등록 메뉴명: <input type="text" name="name"/><br>
등록 메뉴 가격: <input type="number" name="price"/><br>
등록 메뉴 카테고리:
<select name="categoryCode">
<option value="1">식사</option>
<option value="2">음료</option>
<option value="3">디저트</option>
</select><br>
<button>등록하기</button>
</form>
</body>
</html>
해당 POST 요청을 매핑할 Controller의 handler method를 FirstController 클래스에 추가해주자.
package org.example.handlermethod;
@Controller
@RequestMapping("/first/*")
public class FirstController {
/* 핸들러 메서드의 반환 값을 void로 설정하면 요청 주소가 곧 view의 이름이 된다.
* => /first/regist 뷰를 응답한다.
* => resources/templates/first/regist.html 파일을 만들어서 작업하자.
* */
@GetMapping("/regist")
public void regist(){}
@PostMapping("/regist")
public String registMenu(WebRequest request, Model model) {
// WebRequest 객체의 getParameter 등의 메서드를 통해 클라이언트로부터 전달된 파라미터를 가져올 수 있다.
String name = request.getParameter("name");
int price = Integer.parseInt(request.getParameter("price"));
int categoryCode = Integer.parseInt(request.getParameter("categoryCode"));
String message
= name + "을(를) 신규 메뉴 목록의 " + categoryCode + "번 카테고리에 " + price + "원으로 등록하였습니다.";
model.addAttribute("message", message);
return "first/messagePrinter";
}
}
resources/templates/first 하위에 messagePrinter.html 파일을 작성해준다.
<!--/resources/templates/first/messagePrinter.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>messagePrinter</title>
</head>
<body>
<h3 th:text="${message}"></h3>
</body>
</html>
@RequestParam으로 요청 파라미터 값 전달받기
요청 파라미터를 매핑하여 핸들러 메소드 호출 시 값을 넣어주는 어노테이션으로 매개변수 앞에 작성한다.
index.html에 GET 방식의 /first/modify 요청을 전달하는 코드를 추가해주었다.
<!--resources/static/index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<h1>핸들러 메소드의 파라미터와 어노테이션</h1>
<!-- ... 위에서 작성한 코드는 생략 -->
<h2>2. @RequestParam으로 요청 파라미터 전달 받기</h2>
<button onclick="location.href='/first/modify'">@RequestParam</button>
</body>
</html>
Controller 클래스의 핸들러 메소드를 통해 파라미터 전달 테스트를 위해 값을 입력할 수 있는 view를 응답한다.
package org.example.handlermethod;
@Controller
@RequestMapping("/first/*")
public class FirstController {
// ... 위에서 작성했던 내용은 생략
/* <WebRequest 방식>
/first/modify라는 GET 방식의 요청이 오면 /first/modify라는 view로 응답하겠다
(/first/modify.html로 처리한다.) */
@GetMapping("/modify")
public void modify() {}
}
resources/templates/first 의 하위에 modify.html 파일을 생성해준다.
<!--resources/templates/first/modify.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>modify</title>
</head>
<body>
<h1>@RequestParam으로 파라미터 값 전달 받기</h1>
<form action="modify" method="post">
수정 메뉴명: <input type="text" name="name"/><br>
수정 메뉴 가격: <input type="number" name="price"/><br>
<button>수정하기</button>
</form>
</body>
</html>
해당 화면에서 사용자 입력 양식에 값을 입력하고 submit을 누르면 POST 방식의 /first/modify 요청이 발생한다.
이 요청을 매핑할 Controller의 handler method에서 @RequestParam을 이용해보자.
package org.example.handlermethod;
@Controller
@RequestMapping("/first/*")
public class FirstController {
// ... 위에서 작성한 내용은 생략
@PostMapping("/modify")
public String modifyMenu(
/* 기본적으로 속성과 매개변수 이름은 같아야 한다.
@RequestParam String name,
@RequestParam int price, */
/* 그런데 그렇지 않을 경우 아래와 같이 쓸 수 있다.
@RequestParam(value = "name") String modifyName,
@RequestParam(value = "price") int modifyPrice,
*/
/* required의 default 값은 true인데,
그 말은 이 값이 꼭 이 파라미터가 요청에 반드시 포함돼야 함을 의미한다.
아래와 같이 작성하면 nam 파라미터가 요청에 없어도 null로 처리하여 오류가 발생하지 않는다.
defaultValue의 경우, 요청에 price 파라미터가 포함되지 않을 경우 ""와 같은 빈 문자열이 넘어오게 되는데,
이 때 parsing 관련 에러가 발생할 수 있다. defaultValue를 이용하면 기본값을 설정해줄 수 있다.*/
@RequestParam(value = "nam", required = false) String modifyName,
@RequestParam(value = "price", defaultValue = "0") int modifyPrice,
Model model
) {
String message
= modifyName + " 메뉴의 가격을 " + modifyPrice + "원으로 변경하였습니다.";
model.addAttribute("message", message);
return "first/messagePrinter";
}
}
resources/templates/first 하위에 modify.html 파일에 메뉴 수정하기2를 추가해보자.
<!--/resources/templates/first/modify.html-->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>modify</title>
</head>
<body>
<!--... 위에서 작성한 내용은 생략-->
<h3>메뉴 수정하기2</h3>
<form action="modifyAll" method="post">
수정할 메뉴의 이름 : <input type="text" name="modifyName2"><br>
수정할 메뉴의 가격 : <input type="number" name="modifyPrice2"><br>
<button type="submit">수정하기</button>
</form>
</body>
</html>
해당 화면에서 사용자 입력 양식에 값을 입력하고 submit을 누르면 POST 방식의 /first/modifyAll 요청이 발생한다.
발생하는 요청을 매핑할 Controller의 Handler Method이다.
파라미터가 여러 개인 경우 Map으로 한 번에 처리할 수 있는데, 이 때 Map의 키는 form의 name 속성값이 된다.
package org.example.handlermethod;
@Controller
@RequestMapping("/first/*")
public class FirstController {
// ... 위에서 작성한 내용은 생략
/* 파라미터가 여러 개인 경우 Map 으로 한 번에 처리할 수 있다. Map의 key는 name 속성이 된다. */
@PostMapping("/modifyAll")
public String modifyAllMenu(@RequestParam Map<String, String> parameters, Model model) {
String name = parameters.get("name");
int price = Integer.parseInt(parameters.get("price"));
String message = name + " 메뉴의 가격을 " + price + "원으로 변경하였습니다.";
model.addAttribute("message", message);
return "first/messagePrinter";
}
}
@ModelAttribute로 요청 파라미터 값 전달하기
하나의 덩어리인 MenuDTO 같은 모델들을 커멘드 객체로 전달받을 때 사용하는 어노테이션이다.
index.html에서 GET 방식의 /first/search 요청을 전달한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<h1>핸들러 메소드의 파라미터와 어노테이션</h1>
<!--... 위에서 작성한 내용은 생략-->
<h2>3. @ModelAttribute으로 요청 파라미터 전달 받기</h2>
<button onclick="location.href='/first/search'">@ModelAttribute</button>
</body>
</html>
Controller 클래스의 핸들러 메소드를 통해 파라미터 전달 테스트를 위해 값을 입력할 수 있는 뷰를 응답한다.
package com.ohgiraffers.handlermethod;
@Controller
@RequestMapping("/first/*")
public class FirstController {
// ... 위에서 작성한 코드는 생략
@GetMapping("/search")
public void search(){}
}
resources/templates/first의 하위에 search.html 파일을 생성한다.
<!--resources/templates/first/search.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>search</title>
</head>
<body>
<h1>@ModelAttribute로 파라미터 값 전달 받기</h1>
<h3>메뉴 검색하기</h3>
<form action="search" method="post">
메뉴명: <input type="text" name="name"/><br>
메뉴 가격: <input type="number" name="price"/><br>
메뉴 카테고리:
<select name="categoryCode">
<option value="1">식사</option>
<option value="2">음료</option>
<option value="3">디저트</option>
</select><br>
판매 상태 : <input type="text" name="orderableStatus"/><br>
<button>검색하기</button>
</form>
</body>
</html>
해당 화면에서 사용자 입력 양식에 값을 입력하고 submit을 누르면 POST 방식의 /first/search 요청이 발생한다.
MenuDTO, FirstController 클래스를 작성해주었다.
package com.ohgiraffers.handlermethod;
/* 커멘드 객체로 사용하기 위해서는 name 속성 값과 필드명이 일치하도록 작성해야 한다. */
public class MenuDTO {
private String name;
private int price;
private int categoryCode;
private String orderableStatus;
public MenuDTO(){}
// lombok 사용안하고 있었으므로 getter, setter 그냥 추가해줌
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getCategoryCode() {
return categoryCode;
}
public void setCategoryCode(int categoryCode) {
this.categoryCode = categoryCode;
}
public String getOrderableStatus() {
return orderableStatus;
}
public void setOrderableStatus(String orderableStatus) {
this.orderableStatus = orderableStatus;
}
@Override
public String toString() {
return "MenuDTO{" +
"name='" + name + '\'' +
", price=" + price +
", categoryCode=" + categoryCode +
", orderableStatus='" + orderableStatus + '\'' +
'}';
}
}
package com.ohgiraffers.handlermethod;
@Controller
@RequestMapping("/first/*")
public class FirstController {
@GetMapping("/search")
public void search(){}
/* 3. @ModelAttribute를 이용하는 방법
* DTO 같은 모델을 커멘드 객체로 전달 받는 어노테이션으로 Model에 담기는 작업도 자동으로 일어난다.
* @ModelAttribute 어노테이션은 생략이 가능한데, 생략을 할 경우 model에 클래스명 기준으로 첫글자만 소문자로 바뀌어 담긴다.
* (이 경우 menuDTO가 담기는 것)
* model에 담길 때 다른 이름으로 담기길 바라면 어노테이션을 이용하면 된다.(괄호 안에 key값 명시)
* 그럼 그 key대로 model에 담긴다.*/
@PostMapping("/search")
public String searchMenu(@ModelAttribute("menu") MenuDTO menu) {
return "first/searchResult";
}
}
응답 화면 구성을 위해 resources/templates/first 하위에 searchResult.html 파일을 생성한다.
<!--resources/templates/first/searchResult.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>searchResult</title>
</head>
<body>
<h1>Model에 담긴 커멘드 객체의 정보 출력</h1>
<h3 th:text="${menu.name}"></h3>
<h3 th:text="${menu.price}"></h3>
<h3 th:text="${menu.categoryCode}"></h3>
<h3 th:text="${menu.orderableStatus}"></h3>
</body>
</html>
@SessionAttributes로 요청 파라미터 값 전달하기
HttpSession을 전달받는 것도 가능하지만, servlet에 종속적이므로
Spring에서 제공하는 @SessionAttributes를 사용하는 것이 권장된다.
클래스 레벨에 @SessionAttributes("모델에 담을 key값")와 같이 지정하면
model에 해당 key가 추가될 때 session에도 자동 등록된다.
이제 코드로 살펴보자.
index.html에서 GET 방식의 /first/login 요청을 전달한다.
<!--resources/static/index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<!--위에서 작성한 코드는 생략-->
<h2>4. Session 이용하기</h2>
<button onclick="location.href='/first/login'">Session 이용하기</button>
</body>
</html>
Controller 클래스의 핸들러 메소드를 통해 파라미터 전달 테스트를 위해 값을 입력할 수 있는 뷰를 응답한다.
package org.example.handlermethod;
@Controller
@RequestMapping("/first/*")
public class FirstController {
@GetMapping("/login")
public void login(){}
}
resources/templates/first의 하위에 login.html 파일을 생성한다.
<!--resources/templates/first/login.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<h1>Session 이용하기</h1>
<h3>@SessionAttribute</h3>
<form action="login" method="post">
id : <input type="text" name="id"/><br>
pwd : <input type="password" name="pwd"/><br>
<button>로그인</button>
</form>
</body>
</html>
해당 화면에서 사용자 입력 양식에 값을 입력하고 submit을 누르면 POST 방식의 /first/login 요청이 발생한다.
발생하는 요청을 매핑할 Controller의 핸들러 메소드이다.
package org.example.handlermethod;
@Controller
@RequestMapping("/first/*")
/* 2) 클래스 레벨에 @SessionAttributes("id")를 설정한다.
@SessionAttributes 어노테이션을 이용하여 세션에 값을 담을 key값을 설정해두면
Model 영역에 해당 key로 값이 추가되는 경우 session에 자동 등록을 한다.*/
@SessionAttributes("id")
public class FirstController {
@PostMapping("/login")
public String loginTest(String id, Model model) {
// 1) model에 id를 추가한다.
model.addAttribute("id", id);
return "first/loginResult";
}
}
실행을 시켜보면 세션에 값이 잘 저장됨을 확인할 수 있는데,
이제는 로그아웃 버튼을 누르면 세션 객체가 제거되도록 하는 로직을 작성해보자.
loginResult.html 파일을 작성하자.
<!--resources/templates/first/logiResult.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>loginResult</title>
</head>
<body>
<h1>Session에 담긴 값 확인</h1>
<h3 th:text="|${session.id}님 환영합니다.|"></h3>
<button onclick="location.href='logout'">로그아웃</button>
</body>
</html>
SessionAttributes로 등록된 값은
session의 상태를 관리하는 SessionStatus의 setComplete() 메소드를 호출해야 사용이 만료된다.
package org.example.handlermethod;
@Controller
@RequestMapping("/first/*")
@SessionAttributes("id")
public class FirstController {
// ... 앞에서 작성된 내용은 생략
@GetMapping("/logout")
public String logout(SessionStatus status) {
status.setComplete();
return "first/loginResult";
}
}
@RequestBody로 요청 파라미터 값 전달하기
@RequestBody 어노테이션은 http 요청의 본문(body)에서 데이터를 읽어와서
모델(자바 객체)로 변환시켜주는 어노테이션이다.
출력해보면 쿼리스트링 형태의 문자열이 전송되며,
JSON으로 전달하는 경우 Jackson의 컨버터로 자동 파싱하여 사용할 수 있다.
주로 RestAPI 작성 시 많이 사용되며, 일반적인 form 전송을 할 때는 거의 사용하지 않는다.
추가적으로 @RequestHeader 어노테이션을 이용해서 헤더 정보를 가져올 수 있으며,
@CookieValue를 이용해서 쿠키 정보도 쉽게 불러올 수 있다.
index.html에서 GET 방식의 /first/body 요청을 전달한다.
<!--resources/static/index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<!-- ... 위에서 작성한 내용 생략-->
<h2>5. @RequestBody 이용하기 </h2>
<button onclick="location.href='/first/body'">@RequestBody 이용하기</button>
</body>
</html>
Controller 클래스의 핸들러 메소드를 통해 파라미터 전달 테스트를 위해 값을 입력할 수 있는 뷰를 응답한다.
package org.example.handlermethod;
@Controller
@RequestMapping("/first/*")
public class FirstController {
@GetMapping("/body")
public void body(){}
}
resources/templates/first 하위에 body.html 파일을 생성한다.
<!--resources/templates/first/body.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>body</title>
</head>
<body>
<h1>@RequestBody 이용하기</h1>
<form action="body" method="post">
메뉴명: <input type="text" name="name"/><br>
메뉴 가격: <input type="number" name="price"/><br>
메뉴 카테고리:
<select name="categoryCode">
<option value="1">식사</option>
<option value="2">음료</option>
<option value="3">디저트</option>
</select><br>
판매 상태 : <input type="text" name="orderableStatus"/><br>
<button>검색하기</button>
</form>
</body>
</html>
해당 화면에서 사용자 입력 양식에 값을 입력하고 submit을 누르면 POST 방식의 /first/body 요청이 발생한다.
요청을 매핑할 Controller의 핸들러 메소드를 아래와 같이 작성할 수 있다.
package org.example.handlermethod;
@Controller
@RequestMapping("/first/*")
public class FirstController {
// 위에서 작성한 내용 생략
@PostMapping("/body")
public void bodyTest(
@RequestBody String body,
@RequestHeader("content-type") String contentType,
@CookieValue("JSESSIONID") String sessionId
) {
System.out.println("body: " + body);
System.out.println("contentType: " + contentType);
System.out.println("sessionId: " + sessionId);
}
}
실행해보면 클라이언트에서 입력된 값이
@RequestBody 어노테이션을 설정한 값으로 잘 전달됨을 콘솔 출력을 통해 확인할 수 있다.
'Spring Framework > Spring Boot' 카테고리의 다른 글
[Spring Boot] 6. Interceptor (1) | 2024.09.10 |
---|---|
[Spring Boot] 5. Exception Handler (0) | 2024.09.09 |
[Spring Boot] 4. View Resolver (1) | 2024.09.09 |
[Spring Boot] 2. Request Mapping (0) | 2024.09.08 |
[Spring Boot] 1. Spring Boot 개요 및 개발환경 구축 (1) | 2024.09.07 |