[Spring] 트러블슈팅 + 데이터 request 방식 총정리

Sun choi·2024년 8월 16일

NEW 지식

목록 보기
19/34

데이터 request 방식을 완벽히 이해하지 못한 나는 계속해서 이 부분에서 문제가 생겼다.

💥 첫번째 트러블슈팅

코드에서는 이상이 없는데 Postman에서 반환하려고 하면 오류가 뜸.

해결 1. url을 코드에서 쓴 것처럼 ' /{id} '라고 하면 안되고 '/:id' 라고 해야한다.

해결2. 코드에서 변수를 @RequestBody가 아니라 @PathVariable라고 했기 때문에 Body영역이 아니라 Params 영역에서 Value에 작성해야한다.

그러면 정상적으로 반환이 되는 걸 확인할 수 있다.


💥 두번째 트러블슈팅

선택한 일정 조회, 일정 수정, 삭제 시 id와 password를 받는데 그 때 PathVariable 방식을 썼던 걸 문제점을 파악하고 RequestBody로 변경해 바디값 안에 넣어주려고 했다.

그래서 처음엔 단순히 이렇게 @RequestBody로 변경하고 Postman으로 찍어봤는데

.w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type 'java.lang.Long' from Object value (token 'JsonToken.START_OBJECT')]

어떤 방법으로 해도 이런 에러가 찍혀서 내가 body 값에 넣는 방식이 문제인 줄 알고 많이 헤멨다.

그리고! RequestBody의 사용법을 다시 공부해본 결과
HTTP Body에 JSON 데이터를 담아 서버에 전달할 때 해당 Body 데이터를 Java의 객체로!!!! 전달 받는다는 것을 알게 됐다.

코드 수정 후 일정 수정, 삭제가 잘 구현이 되는 것을 볼 수 있다.
그리고 코드 수정하면서 알게 된 것은 한 메서드에 여러개의 @RequestBody를 선언하는 것은 불가능하다. 이걸 모르고 두개를 사용해 Postman에 어떻게 두개를 작성해서 넣을지 고민했던 내가 너무 모자르다...

오케이! 문제는 해결했고!
데이터 받는 법 다시 공부한다!

크게 param, query, body 방식이 있다.


🍄 Path Variable과 Request Param

@PathVariable

서버에 보내려는 데이터를 URL 경로에 추가할 수 있다.

  • 데이터를 받기 위해서는 /star/{name}/age/{age} 이처럼 URL 경로에서 데이터를 받고자 하는 위치의 경로에 {data} 중괄호를 사용.
  • (@PathVariable String name, @PathVariable int age)
    • 그리고 해당 요청 메서드 파라미터에 @PathVariable 애너테이션과 함께 {name} 중괄호에 선언한 변수명과 변수타입을 선언하면 해당 경로의 데이터를 받아올 수 있다.
// [Request sample]
// GET http://localhost:8080/hello/request/star/Robbie/age/95
@GetMapping("/star/{name}/age/{age}")
@ResponseBody
public String helloRequestPath(@PathVariable String name, @PathVariable int age)
{
    return String.format("Hello, @PathVariable.<br> name = %s, age = %d", name, age);
}

@RequestParam

@RequestParam 어노테이션을 활용하면, Request Header에 Parameter를 넣어서 전달해준다. 이로써 알 수 있는 것은 @RequestParam 어노테이션이 없어도 Spring에서 각 인자에 맞는 Object를 매핑해주어 전달해주는 것을 알 수 있다.

Request Param 방식

  • 서버에 보내려는 데이터를 URL 경로 마지막에 ?& 를 사용하여 추가할 수 있다.
  • @RequestParam은 생략이 가능합니다.
// [Request sample]
// GET http://localhost:8080/hello/request/form/param?name=Robbie&age=95
@GetMapping("/form/param")
@ResponseBody
public String helloGetRequestParam(@RequestParam String name, @RequestParam int age) {
    return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
}

param?name=Robbie&age=95

  • 데이터를 받기 위해서는 ?name=Robbie&age=95 에서 key 부분에 선언한 name과 age를 사용하여 value에 선언된 Robbie, 95 데이터를 받아올 수 있다.
  • (@RequestParam String name, @RequestParam int age)
    • 해당 요청 메서드 파라미터에 @RequestParam 애너테이션과 함께 key 부분에 선언한 변수명과 변수타입을 선언하면 데이터를 받아올 수 있다.

form 태그 POST

  • HTML의 form 태그를 사용하여 POST 방식으로 HTTP 요청을 보낼 수 있다.
  • 이때 해당 데이터는 HTTP Body에 name=Robbie&age=95 형태로 담겨져서 서버로 전달된다. 해당 데이터를 받는 방법은 앞서 본 방법 처럼 @RequestParam 애너테이션을 사용하여 받아올 수 있다.
// [Request sample]
// POST http://localhost:8080/hello/request/form/param
// Header
//  Content type: application/x-www-form-urlencoded
// Body
//  name=Robbie&age=95
@PostMapping("/form/param")
@ResponseBody
public String helloPostRequestParam(@RequestParam String name, @RequestParam int age) {
    return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
}

@RequestParam(required = false)
- 이렇게 required 옵션을 false로 설정하면 Client에서 전달받은 값들에서 해당 값이 포함되어있지 않아도 오류가 발생하지 않는다.
- @PathVariable(required = false) 도 해당 옵션이 동일하다.
- Client로 부터 값을 전달 받지 못한 해당 변수는 null로 초기화된다.


🍄 HTTP 데이터를 객체로 처리하는 방법

✔️ 우선 데이터를 Java의 객체로 받아올 때 주의할 점!

  • 해당 객체의 필드에 데이터를 넣어주기 위해 set or get 메서드 또는 오버로딩된 생성자가 필요하다.
  • 예를 들어 @ModelAttribute 사용하여 데이터를 받아올 때 해당 객체에 set 메서드 혹은 오버로딩된 생성자가 없다면 받아온 데이터를 해당 객체의 필드에 담을 수 없다.
  • 이처럼 객체로 데이터를 받아올 때 데이터가 제대로 들어오지 않는다면 우선 해당 객체의 set or get 메서드 또는 오버로딩된 생성자의 유무를 확인하기!

@ModelAttribute

form 태그 POST

  • HTML의 form 태그를 사용하여 POST 방식으로 HTTP 요청을 보낼 수 있다.
  • 이때 해당 데이터는 HTTP Body에 name=Robbie&age=95 형태로 담겨져서 서버로 전달된다.
  • 해당 데이터를 Java의 객체 형태로 받는 방법은 @ModelAttribute 애너테이션을 사용한 후 Body 데이터를 Star star 받아올 객체를 선언한다.
// [Request sample]
// POST http://localhost:8080/hello/request/form/model
// Header
//  Content type: application/x-www-form-urlencoded
// Body
//  name=Robbie&age=95
@PostMapping("/form/model")
@ResponseBody
public String helloRequestBodyForm(@ModelAttribute Star star) {
    return String.format("Hello, @ModelAttribute.<br> (name = %s, age = %d) ", star.name, star.age);
}

Query String 방식

  • ?name=Robbie&age=95 처럼 데이터가 두 개만 있다면 괜찮지만 여러 개 있다면 @RequestParam 애너테이션으로 하나 씩 받아오기 힘들 수 있다.
  • 이때 @ModelAttribute 애너테이션을 사용하면 Java의 객체로 데이터를 받아올 수 있다.
    • 파라미터에 선언한 Star 객체가 생성되고, 오버로딩된 생성자 혹은 Setter 메서드를 통해 요청된 name & age 의 값이 담겨진다.
// [Request sample]
// GET http://localhost:8080/hello/request/form/param/model?name=Robbie&age=95
@GetMapping("/form/param/model")
@ResponseBody
public String helloRequestParam(@ModelAttribute Star star) {
    return String.format("Hello, @ModelAttribute.<br> (name = %s, age = %d) ", star.name, star.age);
}

✔️ @ModelAttribute는 생략이 가능!
이때, Spring에서는 @ModelAttribute뿐만 아니라 @RequestParam도 생략이 가능하다.
Spring이 이를 구분하는 방법은 간단하게 설명하면 Spring은 해당 파라미터(매개변수)가 SimpleValueType(예를 들어 원시타입(int), Wrapper타입(Integer), Date등의 타입)이라면 @RequestParam으로 간주하고 아니라면 @ModelAttribute가 생략되어있다 판단한다.


@RequestBody

@RequestBody 어노테이션을 활용하면, Request Body에 Data를 넣어서 전달해준다.
HTTP Body에 JSON 데이터를 담아 서버에 전달할 때 해당 Body 데이터를 Java의 객체로 전달 받을 수 있다.
HTTP Body에 {"name":"Robbie","age":"95"} JSON 형태로 데이터가 서버에 전달되었을 때 @RequestBody 애너테이션을 사용해 데이터를 객체 형태로 받을 수 있다.

✔️ 민감한 정보는 이런 식으로 PathVariable 보다는 body 값안에 넣어주는 것이 좋다. url은 모든 사람이 쉽게 볼수 있고 기록이 다 남는데 비밀번호 아이디가 들어가면 누구나 쉽게 정보를 탈취할수 있기 때문이다.

// [Request sample]
// POST http://localhost:8080/hello/request/form/json
// Header
//  Content type: application/json
// Body
//  {"name":"Robbie","age":"95"}
@PostMapping("/form/json")
@ResponseBody
public String helloPostRequestJson(@RequestBody Star star) {
   return String.format("Hello, @RequestBody.<br> (name = %s, age = %d) ", star.name, star.age);
}
profile
풀스택 개발자의 공부기록 📖

0개의 댓글