스프링/스프링MVC

@RequestParam, @ModelAttribute, @RequestBody

더즈 2022. 4. 24. 00:31

HTTP 요청 데이터 3가지 전달 방법

GET – 쿼리 파라미터

  • /url?username=hello&age=20
  • 메시지 바디 없이 URL에 데이터를 전달

POST – HTML Form

  • content-type: application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파라미터 형식(usermane=hello&age=20)으로 전달

HTTP 메시지 바디에 데이터를 직접 담아서 요청

  • 주로 JSOM 데이터 형식을 사용
  • {“username” : “hello”, “age” : 20}

@RequestParam

Get 쿼리 파라미터 전송과 POST HTML Form 전송 방식 둘 다 조회할 수 있다.

 

다음과 같이 URL 요청이 들어올 때 @RequestParam을 이용하면 다음과 같이 사용할 수 있다.

Get 쿼리 파라미터 받기

/request-param?username=does&age=25
@GetMapping("/request-param")
public String requestParam(
	@RequestParam("username") String memberName,
	@RequestParam("age") int memberAge) {
	return memberName + memberAge;
}

파라미터로 받는 변수명과 URL의 변수명이 일치하면 어노테이션에 value 값을 생략할 수도 있다.

@GetMapping("/request-param")
public String requestParam(@RequestParam String username, @RequestParam int age) {
	return username + age;
}

받는 타입이 String, Integer, int 등의 단순 타입이면 @RequestParam 자체도 생략 가능하다. 하지만 가급적 @RequestParam을 붙이면 쿼리 파라미터에서 값을 읽는다는 것을 명시적으로 알 수 있으므로 붙이는 것이 좋을 것 같다.

@GetMapping("/request-param")
public String requestParam(String username, int age) {
	return username + age;
}

required, defaultValue

@RequestParam에는 파라미터 값이 없는 경우에 대비할 수 있는 requireddefaultValue 속성이 있다.

 

required는 기본값이 true이며 쿼리 파라미터가 URL에 생략되어 있으면 400 에러가 발생한다.

ex) /request-param?username=does

 

defaultValue는 쿼리 파라미터가 없을 경우 기본값을 설정할 수 있다. 기본값을 설정할 경우 required는 의미가 없다.

@RequestParam(required = true, defaultValue = "guest") String username

위와 같이 사용하면 반드시 파라미터가 있어야 하며 없을 경우 기본값을 "guest"라 설정한다는 뜻이다.

Map으로 받기

요청 파라미터 값을 변수명이 아닌 Map<String, Object> 형태로 조회할 수도 있다.

@GetMapping("/request-param-map")
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {
	return (String) paramMap.get("username") + paramMap.get("age");
}

HTML Form 데이터 받기

HTML Form으로 Post 요청을 보내면 다음과 같이 @PostMapping과 @RequestParam을 함께 사용할 수 있다.

<form action="/request-param" method="post">
    username: <input type="text" name="username" />
    age: <input type="text" name="age" />
    <button type="submit">전송</button>
</form>
@PostMapping("/request-param")
public String postRequestParam(@RequestParam String username, @RequestParam int age) {
	return username + age;
}

그런데 Form으로 보낼 데이터가 많아질 경우 위와 같이 파라미터를 나열해서 받으면 코드가 매우 길어진다. 데이터들을 필드로 가지는 객체를 파라미터로 받아올 수 있다면 문제는 해결될 것이다.

@ModelAttribute

Form을 통해 넘어온 요청 파라미터를 사용해 객체를 만드는 과정을 자동화해준다. 아래와 같이 사용하면 Form의 데이터를 통해 객체를 바로 받아올 수 있다.

@PostMapping("/model-attribute")
public String modelAttribute(@ModelAttribute User user) {
	return user.getUsername() + user.getAge();
}

이때 사용되는 User 객체는 모든 필드 값을 받는 생성자 혹은 모든 필드의 setter가 열려있어야 한다.

static class User {
	String username;
	int age;

	public User(String username, int age) {
		this.username = username;
		this.age = age;
	}

	// 생성자가 있다면 setter는 없어도 되고 setter가 있다면 생성자는 기본 생성자만 있어도 충분

	public void setUsername(String username) {
		this.username = username;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

 

@RequestParam처럼 @ModelAttribute도 생략이 가능하다. 스프링은 파라미터를 받는 어노테이션을 생략할 때 다음과 같은 규칙을 적용한다.

  • String , int , Integer 같은 단순 타입 = @RequestParam
  • 나머지 = @ModelAttribute (커스텀 객체)

주의할 점!

만약 User 객체를 응답 값으로 보낼 경우(return user) User 객체의 각 필드의 getter가 정의되어 있어야 한다. getter가 없으면 406 에러가 발생한다. 객체를 다시 JSON 데이터로 변환할 때 getter를 사용하기 때문이다.

@PostMapping("/model-attribute")
public User modelAttribute(@ModelAttribute User user) {
	return user; // 406 에러 발생!!
}

static class User {
	private String username;
	private int age;

	public User(String username, int age) {
		this.username = username;
		this.age = age;
	}
}

@RequestBody

Form을 통해서가 아닌 HTTP 바디에 직접 데이터가 넘어오는 경우에 @RequestParam과 @ModelAttribute는 사용할 수 없고 @RequestBody를 사용해야 한다.

HTTP 바디에 단순 텍스트 받기

HTTP 바디에 "hello"라는 텍스트가 들어있다면 아래와 같이 조회할 수 있다.

@PostMapping("/request-body")
public String requestBodyString(@RequestBody String body) {
	return body;
}

HTTP 바디에 JSON 데이터 받기 (MAP)

아래와 같이 Map으로 JSON 데이터를 받을 수 있다.

{"username" : "does""age" : 25}
@PostMapping("/request-body-map")
public String requestBodyJason(@RequestBody Map<String, Object> json) {
	return (String) json.get("username") + json.get("age");
}

JSON 데이터 자바 객체로 받기

@ModelAttribute처럼 @RequestBody를 이용하면 java 객체로 요청 데이터를 받을 수 있다.

@PostMapping("/request-body-object")
public User requestBodyJason(@RequestBody User user) {
	return user;
}

주의할 점!

  • @RequestBody로 객체를 받으려면 우선 객체의 기본 생성자가 반드시 있어야 한다. 다른 이유 때문에 기본 생성자 이외의 생성자가 존재한다면 기본 생성자도 꼭 명시적으로 가지고 있어야 한다.
  • JSON 데이터와 매핑되는 필드의 getter 혹은 setter가 존재해야 한다. 둘 다 존재하지 않으면 데이터를 객체의 필드로 매핑해주지 못한다.
  • @ModelAttribute와 마찬가지로 위와 같이 객체를 바로 응답으로 보낼 경우 필드들의 getter가 존재해야 한다.

 

참고

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술