요청파라미터란?(네임파라미터)
클라이언트가 서버로 전송하는 데이터
고객이 뷰페이지에서 입력,선택한 자료들을 넘겨받아 작업하기 위해 먼저 서버로 전송하기 위한 데이터 입니다.
로그인 |
아이디,패스워드 |
게시글 |
글제목, 작성자, 글번호, 글내용 |
회원가입 |
이름,생년월일,아이디.비밀번호,이메일,폰번호,주소 |
자료실 |
글제목,작성자,글번호,글내용,첨부파일 |
상품페이지 |
종류, 수량, 옵션 |
결제페이지 |
배송지정보(받는분 성함, 핸드폰번호, 주소록, 배송메시지) |
이런 데이터들을 담는 변수가 요청 파라미터변수 입니다.
get방식같은경우엔 주소창에 노출됩니다 ?변수=값
post방식은 request Body request객체의 안쪽에 숨어서 서버로 들어가는겁니다.
(request객체에 Body본문 내용을 심어서 보냅니다)
서버는 전송을 해줬으면 캐치를합니다.
GET방식 POST방식 / <input type="hidden">
GET or POST 전송방식GET- <form>태그에서 입력한 파라미터 name과 값이 주소창에노출됩...
blog.naver.com
#1. Servlet자바의 API인 HttpServletRequest방식
* 정통적인 jsp/Servlet의 파라미터 읽기 처리법
- HttpServletRequest객체를 사용
먼저 GET 방식으로 접근해보겠습니다.
메소드 get방식의 특징은 주소창에 파라미터를 임의로 지정하여서 파라미터명을 추가하고 파라미터에 값을 넣어 컨트롤러로 요청을 보낼수 있습니다.
package com.spring.web.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class RequestController {
@GetMapping("request/param")
public String paramTest(HttpServletRequest request) {
System.out.println("/request/param 요청 : GET");
String name = request.getParameter("name");
int age = Integer.parseInt(request.getParameter("age"));
System.out.println("이름 : "+name);
System.out.println("나이 : "+age);
return "";
}
}
주소창에 request/param을 입력하고 ?name=홍길동&age=20라고 파라미터를 묻혀보겠습니다.
뷰페이지는 따로 만들지않고 파라미터 값을 받아오는지 확인만 할수있게끔 로직을 작성했기때문에 404 상태코드 에러를 띄웁니다.
jsp에서는 기본적으로 GET방식으로 url에 실려오는 파라미터를 읽어들일때 request.getParameter();
코드를 선언 해준후 파라미터 이름을 매개값 String으로 넣어줘서 파라미터를 읽어왔습니다.
정통적인 jsp방식은 스프링에서도 쓸수 있습니다
다음은 POST방식입니다.
먼저 컨트롤러에 Get방식으로 join페이지를 띄울수 있도록 매핑주소를/join으로입력한 후 void타입의 메서드 register()를 선언해줍니다.
@Controller
public class RequestController {
@RequestMapping(value="/join", method = RequestMethod.GET)
public void register() {
System.out.println("/request/join : GET");
}
}
/join 에 대한 View페이지 파일을 만들겠습니다.
join.jsp파일을 만듭니다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>요청파라미터 값 테스트!!</h2>
<form action="/request/join" method="post">
<fieldset>
<legend>회원가입 양식</legend>
<p>
- ID : <input type="text" name="userId" size="10" /> <br />
- PW : <input type="password" name="userPw" size="10" /> <br />
- Name : <input type="text" name="userName" size="10" /> <br />
- Hobby :
<input type="checkbox" name="hobby" value="scooer" >축구
<input type="checkbox" name="hobby" value="nook" >독서
<input type="checkbox" name="hobby" value="music" >음악
<br />
<input type="submit" value="회원가입"/>
</p>
</fieldset>
</form>
</body>
</html>
3개의 텍스트박스와 1개의 체크박스를 만들었습니다.
이제, form에서 전송요청을 눌렀을 때 컨트롤러에서 값을 받을수 있도록 메서드를 선언합니다.
@Controller
public class RequestController {
@RequestMapping(value="/join", method = RequestMethod.GET)
public void register() {
System.out.println("/request/join : GET");
}
@PostMapping("/join")
//메서드 오버로딩 (동일한이름의 메서드 중복선언)
public String register(HttpServletRequest request) {
System.out.println("/request/join : POST");
System.out.println("ID: "+request.getParameter("userId"));
System.out.println("PW: "+request.getParameter("userPw"));
System.out.println("HOBBY: "+Arrays.toString(request.getParameterValues("hobby")));
return "request/join";
}
}
Arrays.toString(request.getParameterValues("hobby"))
hobby라는 파라미터는 체크박스 입니다.
여러개를 선택할수 있도록 되어 있구요 그래서 배열로 받아와야합니다.
네임파라미터로 넘어오는 배열을 받아오기 위해서는 getParameterValues() 라는 메서드를 사용하며
배열을 출력하기 위해서는 arrays.toString()메서드를 활용 해야만 합니다.
페이지를 접속하자마자 GET방식인것을 로그로 출력했습니다
전송후 메서드에선언된 로그들을 출력합니다.
해당 로그들은 폼으로부터 받은 데이터와 메서드 요청방식 입니다.
정상적으로 동작합니다.
#2. 스프링 방식
스프링에서도 HttpServletRequest의 request를 사용할 수 있습니다
하지만 스프링은 스프링에서제공하는 스프링만의 API가 존재합니다.
기존 서블릿의 방식이 구리다는게 아니라 더 편한 스프링이 지원하는 스프링API 방식이 있습니다.
기존방식의 단점은 받아야할 파라미터가 엄청 많다면 request.getParameter()를 일일히 다 해줘야한다는 단점이 있습니다.
또한 받은 데이터를 변수에 담기위해서 필요에 따라 형변환도 필요합니다
넘겨받을때는 무조건 String으로 넘어오기 때문에 만약 int변수에 담아야 할 경우 (page 쪽번호 계산등)
int로 parsing해주는 Integer.ParseInt(파라미터값); 등의 작업을 해야합니다.
만약 받아야할 데이터가 '스트링이 아니라 인트나 더블로 써야한다' 혹은 '배열이아니라 ArrayList를 써야한다'
라고 할때, 내부에서 형태를 바꿔서 써야할때, 예를들어 나이를 입력 받았다면 나이를 숫자로 변환해서 처리해야 한다면 한가지 더 변환처리를 해야한다는것, 즉 request.getParameter()로 받은 값은 리턴타입이 String형태이기때문에 Integer.parseInt()로 한번 더 변환을 해줘야합니다.
혹은 배열 같은 경우도 우리가 List로 써야한다면 Arrays.asList()로 배열을 다시한번 List로 변환해줘야하는 작업을 해야한다는 불편함이 있다는 것입니다.
우선 변수명을 바꿀 필요가 없을때의 스프링의 기본적인 방법으로는
요청 전송한곳으로부터 응답을 받는 메서드의 매개값에 네임파라미터와 일치하도록 변수명을 지정 해 줍니다.
public String test(int age) {
System.out.println(age);
return "/test";
}
parseInt를 하지 않아도 자동으로 선언된 int타입으로 타입변환까지 알아서 해줍니다.
아주 쉽죠?
하지면 이때 네임파라미터 이름과 받을 변수명이 일치하지 않다면 에러가납니다.
@RequestParam어노테이션을 이용한 요청 파라미터 처리
@RequestParam어노테이션은 받아온 파라미터의 이름을 바꿔서 쓰기 위해 사용합니다.
요청메서드의 매개값으로 @RequestParam이라는 어노테이션을 활용하여 파라미터를 처리할 수 있습니다.
@RequestParam("userId") String id
1. @RequestParam
- 메서드 매개값으로 @RequestParam 어노테이션을 선언합니다
2. @RequestParam("userId")
- ("userId") : 선언한 어노테이션의 매개값으로 내가 읽어들일 파라미터 이름을 담습니다
3. @RequestParam("userId") String id
- String id : 읽어들일 파라미터를 읽어들인 후 어떤타입의 어떤이름의 변수로 담을것인지 선언합니다.
@PostMapping("/join")
public void register(@RequestParam("userId") String id) {
}
해당 순서를 지킨 메서드의 코드 예시입니다.
뷰페이지로 부터 텍스트박스와 체크박스에 입력한 데이터를 받을 수 있도록 컨트롤러의 메서드를 완성시켜보겠습니다.
@PostMapping("/join")
public void register(@RequestParam("userId") String id, @RequestParam("userPw") String pw,
@RequestParam("hobby") List<String> hobbys) {
}
hobby라는 파라미터는 체크박스 입니다 여러개를 선택되게 되어 있구요
선택된 값들을 알아서 List형태로 포장해서 hobbys 객체에 담아주겠다는 것입니다.
그러면 우리가 메서드 내부에서는 List메서드의 활용을 통해 hobbys에서 데이터를 인덱스를 통해 꺼내올 수 있게됩니다.
이런게 바로 @RequestParam 의 핵심입니다.
@Controller
public class RequestController {
@RequestMapping(value="/join", method = RequestMethod.GET)
public void register() {
System.out.println("/request/join : GET");
}
@PostMapping("/join")
public void register(@RequestParam("userId") String id, @RequestParam("userPw") String pw,
@RequestParam("hobby") List<String> hobbys) {
System.out.println("ID : "+id);
System.out.println("PW : "+pw);
System.out.println("HOBBY : "+hobbys.toString());
}
}
우리가 로그를 출력할때 받아온 hobbys배열은 hobbys라고만 써줘도 toString이 자동으로 작동하긴합니다
혹은 for문으로 List를 뽑아올수도 있습니다.
하지만 @RequestParam에도 예외가 있습니다.
우리가 만약 체크박스를 아무것도 선택하지 않으면 어떻게 될까요?
체크박스를 아무것도 선택하지 않게되면 400 BadRequest 에러를 띄우게됩니다
이때 메시지는 Required List parameter 'hobby' is not present로,
요구되는 필수값인 List의 Parameter인 hobby가 표현되지 않습니다. 라고 말합니다.
hobbys에 데이터가 안들어오면 오류가 뜹니다
해당 어노테이션이 어떻게 선언되어있는지 알아보기 위해 한번 뜯어볼까요?
메서드에 선언되있는 @RequestParam을 마우스로 클릭하고 키보드의 F3키를 누르면 클래스파일이 열립니다.
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
해당 어노테이션에 선언되있는 내용을 주석을 제거하고 핵심 코드만 가져왔습니다.
@AliasFor("name")
String name() default "";
파라미터 이름이 안넣어 줬다면 default가 공백으로 되어있습니다.
@AliasFor("value")
String name() default "";
이 선언은 혹시 벨류를 안넣어줬다면 default가 공백이라고 되있습니다.
이런 선언들을 보았을 때 value="파라미터명"이렇게 작성도 가능합니다.
이 방법은 다른 속성을 사용할때 지정하는것 같습니다.
구글을 참조하니 이런식으로 설명이 나오네요.
value : name ()의 별칭.
name : 바인딩 할 요청 매개 변수의 이름.
@RequestParam(value="param1", required=true) String param1
예를 들어value="param1"는 바인딩 할 요청 매개 변수의 이름이며String param1은 바인딩할 개체입니다.
그렇다면 name은 언제쓸까요? 둘의 기능적인 부분은 대체할 이름 지정으로 동일합니다.
name 혹은 value 어떤 것을 사용하든 동일한 기능을 사용할 수 있습니다.
어느 하나를 사용할 수 있지만 둘 다 사용한 경우 동일한 값을 사용해야합니다.
그렇지 않으면 예외가 발생합니다.
@RequestParam(value="param1", required=true)
@RequestParam(name="param1", required=true)
@RequestParam(value="param1", required=true, name="param1")
이렇게 사용할수 있지만
@RequestParam(value="param1", required=true, name="param3")
이렇게 다른이름으로 사용할 수는 없다고 합니다.
알고 사용해야 한다고 생각되어 조금 더 검색하고 참조하여 설명을 써놨습니다 (틀린부분이 있다면 댓글로 알려주세요)
어찌되었든 다음으로 넘어가보겠습니다.
boolean required() default true;
boolean required의 default가 true라고 선언되있습니다
required()의 default가 true라는것은 상식적으로 무엇을 의미할까요?
필수값이되지 않을까요?
@RequestParam(value="hobby",required=true) List<String> hobbys
이렇게 선언한다면 List가 필수값으로 지정되어 있는 것입니다.
@RequestParam(value="hobby",required=false) List<String> hobbys
false로 바꿔서 사용한다면 List가 필수값이 아닌게 됩니다.
컨트롤러에 위와 같이 코드를 변경해주고 페이지를 다시 실행한 후 파라미터 전송요청을 해 보겠습니다
@Controller
public class RequestController {
@RequestMapping(value="/join", method = RequestMethod.GET)
public void register() {
System.out.println("/request/join : GET");
}
@PostMapping("/join")
public void register(@RequestParam("userId") String id,
@RequestParam("userPw") String pw,
@RequestParam(value="hobby",required=false) List<String> hobbys) {
System.out.println("ID : "+id);
System.out.println("PW : "+pw);
System.out.println("HOBBY : "+hobbys.toString());
}
}
다시 값을입력 하였고 이번에도 체크박스를 선택하지 않고 실행하게되면 500 Error인 NullPointerException이 발생합니다.
우리가 required를 false로 바꿔서 제어했는데도 hobby에 체크된것이 아무것도 없으니까 객체가 널이들어오게 된것입니다.
이번엔 Bad Request는 아닙니다. 문법에러죠, 아까는 400이였지만 지금은 500입니다.
콘솔창에 로그할때 .toString()을 하는 과정에서 에러가 난 것 입니다. 받은 객체가 없기때문에 Null인거죠.
다시 어노테이션 파일에 선언된 마지막 옵션을 보겠습니다
String defaultValue() default ValueConstants.DEFAULT_NONE;
defaultValue()라는게 있습니다.
어노테이션에서 defaultValue(), required() 이런속성으로 작동하고 있는 것입니다.
만약에 취미를 하나도 체크 안했다면 defaultValue()를 설정 할 수 있습니다.
@RequestParam(value="hobby", defaultValue = "no hobby person") List<String> hobbys)
defaultValue = "no hobby person" 속성옵션을 어노테이션에 넣어주고 전송합니다
이 때엔 required=false옵션을 제거해도 상관없습니다.
정상적으로 값을 받으며 콘솔창과 뷰페이지에 요청에대한 에러를 띄우지 않습니다.
객체는 Null이지만 해당 경고대신 입력한 문자열 "no hobby person" 을 콘솔창에서 에러 대신 출력시켜줍니다.예외를 처리해주는 스프링 방식이라고 볼 수 있습니다.
@RequestParam어노테이션은 받아온 파라미터의 이름을 바꿔서 쓰기 위해,
사용합니다.
#3. 스프링방식의 커맨드 객체를 활용한 파라미터 처리
@RequestParam은 request.getParameter()보단 편하지만 그래도 쓸게 꽤나 많았습니다.
스프링의 핵심기술을 이용한 command객체를 활용해보도록 합시다
JSP 서블릿에서 우리가 유즈빈을 사용했을 때 setProperty로 *을 해버리면 파라미터이름과 VO의 필드이름이 같으면 알아서읽어들이는 처리가 있었습니다.
jsp usebean태그는 저의 블로그에 정리된게 있으니 참조하시기 바랍니다
이어서 말하겠습니다
유즈빈 태그와 흡사한 기능이 스프링에서도 가능합니다.
지금부터 스프링의 놀라운 기능을 보여 드리겠습니다.
음... 그렇다고 @RequestParam를 사용하지 않느냐? 아닙니다.
필요할때 따라서 같이 사용할 수 있습니다. 혼용으로요.
그렇기 때문에 알아 두시는게 좋습니다.
웹 어플리케이션의 흐름을 잘 파악해본다면 회원가입을 받는다고 했을때 join페이지의 4개의 파라미터는 DB에 저장이됩니다.
그렇게 되면 필연적으로 VO클래스가 나옵니다.
UserVO클래스를 만들겠습니다
package com.spring.web.model;
import java.util.List;
public class UserVO {
private String userId;
private String userPw;
private String userName;
private List<String> hobby;//배열로받고싶다면 배열로 선언해도 됩니다
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserPw() {
return userPw;
}
public void setUserPw(String userPw) {
this.userPw = userPw;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public List<String> getHobby() {
return hobby;
}
public void setHobby(List<String> hobby) {
this.hobby = hobby;
}
}
자바 빈 규약에 따르면 Field를 Private를 선언해줍니다
강제 캡슐화인 private를 필드의 모든 멤버변수에 지정해줬습니다
컬럼과 1:1매칭 대응하는 필드를 생성합니다.
필드이름은 파라미터이름과 맞춥니다
사실상 필드를 먼저 만들고 UI부분에서 파라미터이름을 필드명과 맞추는게 맞습니다.
자바 규약에 따라서 setter getter를 만듭니다
자바 기본문법상으로 기본생성자를 만들어야 하지만 만들어져있습니다.
setter getter를 만듭니다
이런규약에 맞게 자바빈 설계도를 만듭니다
우리가 컨트롤러로 돌아가서 메서드를 만들것입니다.
@PostMapping("/join")
public void register(UserVO user) {
}
매개변수로 UserVO클래스의 user객체를 선언합니다.
Post요청으로 join요청이 들어올때 파라미터이름이 만약 UserVO객체의 필드명 즉, 각각의 set메서드의 필드명과 일치하면 알아서 setter가 작동하여 객체에 파라미터값들을 이름이 서로 일치하는 변수로 다 주입(초기화) 시킵니다.
이것은 usebean태그와 매우 흡사합니다
음... 어떻게보면 의존성객체 주입시킬때 xml파일에 bean을 등록하면서 추가로 property를 했던것 기억 하실랑가요?
그때도 bean등록으로 클래스를 객체화 하였고 property태그를 사용하여 setter()로 객체를 주입하였었죠?
그것과도 비슷하다고 볼수 있겠네용.
Command객체를 사용했을때 내부적으로 자동으로 setter메서드가 호출되는지 확인하기
package com.spring.web.model;
public class BirthVO {
private String year;
private String month;
public String day;
public String getYear() {
return year;
}
public void setYear(String year) {
System.out.println("setYear() 호출됨");
this.year = year;
}
public String getMonth() {
return month;
}
public void setMonth(String month) {
System.out.println("setMonth() 호출됨");
this.month = month;
}
public String getDay() {
return day;
}
public void setDay(String day) {
System.out.println("setDay() 호출됨");
this.day = day;
}
}
직접 코드를 구현해 내부적으로 setter()메서드가 호출이 되는지 실험해 보았습니다.
데이터저장빈VO클래스에 선언된 setter()메서드에 출력확인로그 코드를 넣어봤습니다.
setter()메서드에 직접 출력로그를 작성해보면 메서드를 호출한 적이없는데 로그가 출력이 됩니다.
내부적으로 setter()메서드가 호출이 된것입니다.
음... EL표현식에서도 한번 언급한 적이 있는데
우리가 setter와 getter를 사용했던 이유중 하나가 관례적으로 자바빈VO 클래스는 필드가 pirvate로 캡슐화가 되어있어서 변수에 직접 접근할수가 없기때문에 setter와 getter로 접근합니다.
오? 그렇다면 private가아닌 public으로했을때는 setter를 이용안하고 내부적으로 필드명으로 접근될 수도 있는거 아닌가?
그래서 이것도 한번 실험을 해봅니다.
public class UserVO {
public String userId;
private String userPw;
private String userName;
public String getUserId() {
return userId;
}/*
public void setUserId(String userId) {
System.out.println("setUserId() 호출");
this.userId = userId;
}*/
public String getUserPw() {
return userPw;
}
public void setUserPw(String userPw) {
System.out.println("setUserPw() 호출");
this.userPw = userPw;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
System.out.println("setUserName() 호출");
this.userName = userName;
}
}
userId변수를 초기화해주는 setUserId() 메서드를 주석처리해봅니다.
동그라미 1번에서 값을 잘 담아 요청했습니다
동그라미 2번에서 값을 출력해 주지 않습니다.
동그라미 3번에서 setter메서드 호출 로깅이 확인되지 않습니다.
결과적으로 Setter()메서드가 호출되지 않았다는 것 입니다.
따라서, VO클래스에 Setter()메서드가 있어야만 작동합니다!!!!
Setter()메서드가 없으면 작동을 안합니다!!!!
주의해야 합니다!!!! 무조건 있어야 합니다!!!
우리는 이제 getter로 불러오기만 하면 되는것입니다.
@Controller
public class RequestController {
@RequestMapping(value="/join", method = RequestMethod.GET)
public void register() {
System.out.println("/request/join : GET");
}
@PostMapping("/join")
public void register(UserVO user) {
System.out.println("ID : "+user.getUserId());
System.out.println("PW : "+user.getUserPw());
System.out.println("NAME : "+user.getUserName());
System.out.println("HOBBY : "+user.getHobby());
}
}
이때에는 들어오는 값의 타입 변환도 VO빈클래스인 UserVO 클래스에 선언된 각 필드의 타입에서 set으로 초기화 될때 자동으로 타입변환을 하게됩니다.
Command객체 활용의 또하나의 장점은 객체화로 묶어버리기 때문에 DB로 값을 넘겨줄때 객체를 통째로 넘겨줄수도 있습니다
+번외
@ModelAttribute또한 @RequestMapping과 같이 넘겨받은 파라미터의 변수명을 다르게지정할때 사용합니다.
다만 Model의 기능을 추가로 갖고있게됩니다.
코드로 구현하여 예시를 들어보겠습니다.
public String test(@RequestParam("age") int age , Model model) {
model.addAttribute("age",age);
System.out.println(age);
return "/test";
}
@RequestParam과 Model객체를 매개값으로 넣은후 받은 변수 "age"라는 이름으로 담아주고 직접 model객체에 Attribute에 key,value 쌍으로 넣어줍니다.
public String test(@ModelAttribute("age") int age) {
System.out.println(age);
return "/test";
}
@
https://u-it.tistory.com/19?category=908594
블로깅을 마칩니다.
감사합니다.
'SpringFramework > BASIC' 카테고리의 다른 글
@Component,@Controller @Repository,@Service 의 bean id 설정/ @Component 어노테이션을 상속받은 Component기능의 어노테이션들을 bean 객체화 등록할때의 bean id설정법 (0) | 2020.08.29 |
---|---|
스프링의 어노테이션의 특징과 컨트롤러의 공통 URL 설정 (0) | 2020.08.29 |
MVC Model1,2 및 웹 Spring의 구동 원리 (0) | 2020.08.29 |
자동 스캔 명령태그 / 자동 의존성 주입 어노테이션 context:annotation-config @Autowired/@Qulifier/@Resource/@Inject (0) | 2020.08.29 |
싱글톤타입/프로토타입 Bean객체의 범위 (0) | 2020.08.29 |