코드 리뷰 진행 중 토큰 재발급 요청 API에서 리프레쉬 토큰을 RequestParam으로 전달하도록 정의된 코드를 보고서 "리프레쉬 토큰을 왜 RequestParam에 담아서 전달할까?" 라는 의문이 들었다. 개인적인 생각으로 RequestBody에 담아서 전달해야 더 안전한게 아닐까 라는 의구심이 들었다.
이에 요청 데이터를 추출 시 어떤 데이터를 어떤 방식으로 가져와야 하는지에 대한 정리가 필요하다고 느껴, 해당 포스팅을 작성한다.
참고 포스팅
우선, 리프레쉬 토큰은 URL에 노출되더라도 크게 상관이 없다. 만약, 회원의 민감정보가 담겨있는 토큰인 경우에는 다를 수 있지만, 현재 프로젝트 팀 내에서 리프레쉬 토큰에는 회원을 구분할 수 있는 값 정도만 담겨있다. 따라서, 리프레쉬 토큰을 굳이 바디에 담지 않아도 된다.
그렇다면, @RequestBody와 @RequestParam을 어떻게 구분하여 사용해야 할까?
@RequestBody와 @RequestParam 어노테이션 모두 Spring MVC Controller 내에서 HTTP 요청 내 데이터를 가져오기 위해서 사용된다는 공통점이 있다.
그러나 이 두 어노테이션의 목적은 약간 다르다.
@RequestBody
는 HTTP 요청 바디에 담긴 데이터를 추출하기 위해서 사용된다. 주로, JSON 또는 XML 형식의 데이터를 Java 객체 형태로 역직렬화 할 때 사용된다.
@RequestParam
는 각각의 파라미터 값을 추출하기 위해서 사용된다. 주로, 요청 URL 또는 제출된 form 데이터 내 데이터를 추출하기 위해 사용된다.
즉, 요청 헤더의 Content-Type 에 따라서 다른 어노테이션이 사용된다.
일반적으로 form 데이터는 content-type이 application/x-www-form-urlencoded 로 전달된다. 즉, URL에 쿼리 스트링으로 전달이 된다. 하지만, @RequestBody로 받지 못하는 것은 아니다. 대신에 다음과 같은 결과가 출력된다.
/**
* POST
* content-type:application/x-www-form-urlencoded
* /test?name=kim
*/
@RestController
public class TestController {
@PostMapping("/test")
public ResponseEntity<?> test(@RequestBody String request) {
System.out.println("출력 결과 : " + request);
return ResponseEntity.ok().build();
}
}
// 출력 결과 : name=kim
이는 name 이라는 변수로 사용하기 위해서는 별도의 매핑과정이 추가로 필요하다.
그렇다면, 이를 @RequestParam으로 전달 받아보겠다.
/**
* POST
* content-type:application/x-www-form-urlencoded
* /test?name=kim
*/
@RestController
public class TestController {
@PostMapping("/test")
public ResponseEntity<?> test(@RequestParam String name) {
System.out.println("출력 결과 : " + name);
return ResponseEntity.ok().build();
}
}
// 출력 결과 : kim
위처럼 URL에 쿼리 스트링으로 명시된 키 값을 기준으로 매개변수에 자동 매핑하여 요청을 추출한다. 즉, 파라미터로 전달된 값을 name이라는 변수에 자동 할당되어 사용이 용이하다.
/**
* POST
* content-type:application/json
* /test
* { name : kim }
*/
@RestController
public class TestController {
@PostMapping("/test")
public ResponseEntity<?> test(@RequestParam String name) {
System.out.println("출력 결과 : " + name);
return ResponseEntity.ok().build();
}
}
// MissingServletRequestParameterException: Required String parameter 'name' is not present
위와 같이 컨트롤러 구현 후 데이터 전송 시 위와 같이 오류가 발생한다.
이는 name 파라미터를 읽지 못하여 발생하는 오류로, @RequestParam은 URL 내에 있는 데이터만 처리가 가능하기 때문에, 요청 바디에 담겨 넘어오는 json 데이터는 읽어내지 못하는 것이다.
/**
* POST
* content-type:application/json
* /test
* { name : kim }
*/
@RestController
public class TestController {
@PostMapping("/test")
public ResponseEntity<?> test(@RequestBody Person person) {
System.out.println("출력 결과 : " + person);
return ResponseEntity.ok().build();
}
}
// 출력 결과 : Person{name='kim'}
위처럼 @RequestBody로 받게 될 경우에, json 데이터를 자동으로 자바 객체로 매핑을 해주게 된다.