자바로 하는 HTTP 요청 사용과 스프링 컨트롤러 어노테이션 정리
개요
- 본 글은 넷플릭스 서버를 나름대로 클론 코딩을 해보며 데이터를 채우던 과정중의 기록이다.
- 외부 API를 사용하면서 어떻게 하면 HTTP 요청을 외부 API로 보내고, 응답을 받아 올 수 있는지 공부해볼 수 있었다.
- 깊이가 있다고는 말할 수 없지만, 이해하며 기록해 왔던 내용을 아래에 정리해 본다.
- 물론 세부적인 내용은 대부분 공식문서를 참고했으며, 자주 사용되어 꼭 알아야 했었던 개념 위주로 정리한다.
Body
Body는 보통 key, value의 쌍으로 이루어지기 때문에 자바에서 제공해주는 MultiValueMap 타입을 사용해야한다.
MultiValueMap<String, String> params =new LinkedMultiValueMap<>();
params.add('')
MultiValueMap 타입으로 만들어준 변수에 add()를 사용해 보낼 데이터를 추가해준다.
Header
HTTP POST를 요청할때 보내는 데이터(Body)를 설명해주는 헤더(Header)도 만들어서 같이 보내줘야 한다.
HttpHeaders headers =new HttpHeaders();
headers.add("");
Spring Framework에서 제공해주는 HttpHeaders 클래스는 Header를 만들어준다.
add()를 사용해 Header에 들어갈 내용을 추가해주자.
Body와 Header 결합
HttpEntity<MultiValueMap<String, String>> entity =new HttpEntity<>(params, headers);
Spring Framework에서 제공해주는 HttpEntity 클래스는 Header와 Body를 합쳐준다.
Post 요청
RestTemplate rt =new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://{요청할 서버 주소}",//{요청할 서버 주소}
HttpMethod.POST,//{요청할 방식}
entity,// {요청할 때 보낼 데이터}
String.class {요청시 반환되는 데이터 타입}
);
Spring Framework에서는 서버에 요청을 편하게 하기 위한 RestTemplate 클래스를 제공해준다.
RestTemplate
HTTP 메소드에 의한 평범한 기능 템플릿을 제공해주고, 더 나아가 특별한 케이스를 지원하는 exchange와 execute 메소드를 제공해준다.
exchage() : 모든 HTTP 요청 메소드를 지원하며 원하는 서버에 요청시켜주는 메소드
response에 서버에서 응답해준 데이터가 저장됨
RequestBody와 ResponseBody 차이
- @RequestBody 어노테이션을 이용하면 HTTP 요청 Body를 자바 객체로 전달받을 수 있다.
- @ResponseBody을 이용하면 자바 객체를 HTTP 응답 body로 전송할 수 있다.
consumes와 produces 차이
받고싶은 데이터를 강제를 함으로써 오류상황을 줄여서 매핑
consumes는 들어오는 데이터 타입을 정의할때 이용
produces는 그 반대
출처 : [https://mungto.tistory.com/438](https://mungto.tistory.com/438)
JSON Parsing (Simple하다)
링크 참조 : [https://codechacha.com/ko/java-parse-json/](https://codechacha.com/ko/java-parse-json/)
RESPONSE ENTITY의 JSON화
JSONParser jsonParser = new JSONParser();
JSONObject jsonObject = (JSONObject) jsonParser.parse(response.getBody().toString());
- Body 부분만 사용하는 경우에 해당
- 헤더가 필요하면 getBody()는 필요 없음
참고 : [https://a1010100z.tistory.com/5](https://a1010100z.tistory.com/5)
JSON VS GSON
서버에서 API를 통해 JSON 값들을 내려받으면 클라이언트인 안드로이드는 이를 파싱해서 필요한 곳에 알맞게 값들을 넣어주거나 표시해야 한다.
그러나 매번 JSONObject, JSONArray를 일일이 선언해서 파싱할 수는 없다. 귀찮다.
이 귀찮음을 해소하기 위해 존재하는 라이브러리가 바로 Gson이라는 라이브러리다. 이걸 쓰면 귀찮아서 숨 넘어갈 것 같은 JSON 파싱 과정을 매우 많이 단축시켜준다.
주의할 것은 JSON 파싱을 수작업으로 해본 적 없이 이 라이브러리를 먼저 사용하지 않는 것이다. 최소한 내가 받은 JSON이 어떤 구조인지 대충 짐작할 수 있고 로직이 어찌저찌 생각나는 정도가 되고 나서야 사용하면 좋다. 단순한 구조의 JSON이라면 Gson을 쓰는 것보다 수작업으로 파싱 때리는 게 더 나을 수도 있다. [https://github.com/google/gson](https://github.com/google/gson) 한줄설명 : 자바 객체 <-> JSON 변환할 때 사용할 수 있는 자바 라이브러리 출처 : https://onlyfor-me-blog.tistory.com/451
Spring 4.3부터 사용 가능해진 Spring MVC 컨트롤러 메소드를 위한 새 어노테이션
https://jira.spring.io/browse/SPR-13442
- @PostMapping
- @GetMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
→ 기존 RequestMapping을 간결하게 사용할 수 있게 도와준다.
→ 어떤 HttpMethods로 매핑시킬지 명확하고 코드 수도 짧아졌다.
예) Before
@RequestMapping(value = "/getList", method = { RequestMethod.POST })
After
@PostMapping("/getList")
Contents-type에 따른 차이
1. @RequestBody Person person
model 객체 앞에 @RequestBody 어노테이션을 붙이면, AnnotationMethodHandlerAdapter에 의해 MappingJacksonHttpMessageConverter가 등록되게 된다. MappingJackson2HttpMessageConverter는 HttpMessageConverter의 구현체로 JSON 형식의 데이터가 들어오면 (당연히 content-type은 application/json) 해당 json 데이터를 jackson 라이브러리를 사용하여 model 객체로 변환해주게 된다.
- Content-Type : application/json 이고 json 타입의 데이터
@PostMapping(value = "/add")
public String postHanlder(@RequestBody Person person) {
log.info("person :: {}", person);
return person.toString();
}
2. @RequestBody MulityValueMap<String, String> map
MultiValueMap 과 함께 @RequestBody 어노테이션을 붙이면, AnnotationMethodHandlerAdapter에 의해 FormHttpMessageConverter가 등록되게 된다. FormHttpMessageConverter는 미디어 타입이 application/x-www-form-urlencodede로 정의된 폼 데이터를 주고 받을 때 사용하게 된다.
- Content-Type : application/x-www-form-urlencoded 이고 key1=value1 타입의 데이터
@PostMapping(value = "/add")
public String postHanlder(@RequestBody MultiValueMap<String, String> data) {
log.info("data :: {}", data);
return data.toString();
}
물론 이론상으로는 2번 방법이 가능하지만, FormHttpMessageConverter를 더 효율적으로 사용하려면 아래 3번 방법이 더 낫다.
3. @ModelAttribute Person person
model 객체와 함께 @ModelAttribute를 사용하면 2번과 같이 FormHttpMessageConverter가 등록되어 key=value를 model로 converting 하게 된다. 앞에 아무런 어노테이션도 안붙이고 Person person 으로 선언하면 @ModelAttribute가 암묵적으로 사용된다.
- Content-Type : application/x-www-form-urlencoded 이고 key1=value1 타입의 데이터
@PostMapping(value = "/add")
public String postHanlder(Person person) {
log.info("data :: {}", data);
return data.toString();
}
리턴 타입 : JSONArray vs JSONObject
예시코드
@GetMapping("/test")
public ResponseEntity<List> testList(testVo testVo) {
String url = "http://testurl.com"; // api url
//get parameter 담아주기
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("test1", testVo.getUserId())
.queryParam("test2", testVo.getValue());
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpRequestFactory.setConnectTimeout(30000); // 연결시간 초과
//Rest template setting
RestTemplate restTpl = new RestTemplate(httpRequestFactory);
HttpHeaders headers = new HttpHeaders(); // 담아줄 header
HttpEntity entity = new HttpEntity<>(headers); // http entity에 header 담아줌
ResponseEntity<JSONArray> responseEntity = restTpl.exchange(url, HttpMethod.GET, entity, JSONArray.class);
L.info("responseEntity.getBody()" + responseEntity.getBody());
List result = (List) responseEntity.getBody();
return ResponseEntity.ok(result);
}
HttpComponentsClientHttpRequestFactory : RestTemplate 는 ClientHttpRequestFactory 로 부터 ClientHttpRequest 를 가져와서 요청을 보낸다.
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());이렇게 써도 가능하나 다양한 설정을 추가하지 못한다.
POST 방법으로 하고 싶으면 HttpEntity를 선언할때 body부 를 넣어주면된다.
String body =~~; // 보통 map형식
HttpEntity entity = new HttpEntity(body,headers);
그리고 여기서는 return을 List로 받고싶어서 JSONArray를 썼는데,
List 형태가 아닌 다양한 JSon 형태라면 JSONObject로 리턴받아 해당 값을 get해도 된다.
ResponseEntity<JSONObject> responseEntity = restTpl.exchange(url, HttpMethod.GET, entity, JSONObject.class);
responseEntity.getBody().get("example");
참조 문서
RequestParam과 RequestBody의 차이
@RequestBody 로 데이터를 받을 때는 메서드의 변수명이 상관이 없었지만, @RequestParam 으로 데이터를 받을때는 데이터를 저장하는 이름으로 메서드의 변수명을 설정 해주어야 한다.
- RequestBody는 날것의 데이터를 넣지 말고 자바 객체가 맵핑 되는점 주의
만약에…
기존에 json 방식으로 요청을 날리던 유저들에게는 그대로 제공하되 추가적으로 x-www-url-encoded 방식도 제공을 해야하는 상황이다.
@PostMapping(value = “/add”, consumes = MediaType.APPLICATION_JSON_VALUE) public String postHanlderForJsonRequest(@RequestBody Person person) { [log.info](http://log.info/)("» json type :: person :: {}", person); return person.toString(); }
@PostMapping(value = “/add”, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public String postHanlderForFormRequest(Person person) { log.info("» form type :: person :: {}", person); return person.toString(); } 위와 같이 두개의 method를 만들어주면 된다. 첫번째 method는 json 타입을, 두번째 method는 form-urlencoded 타입을 받게 된다.
HttpServletRequest
HttpServletRequest를 사용하면, 값을 받아올 수 있다.
예를들어, 아이디, 비밀번호 등의 데이터를 컨트롤러로 보냈을때,
HttpServletRequest 객체안에 모든 데이터들이 들어가게 된다.
@Controller
public class HomeController {
@RequestMapping("/board/confirmId")
public String confirmId(HttpServletRequest httpServletRequest, Model model) {
String id = httpServletRequest.getParameter("id");
String pwd = httpServletRequest.getParameter("pwd");
model.addAttribute("id", id);
model.addAttribute("pwd", pwd);
return "board/confirmId";
}
}
id와 pwd를 HttpServletRquest 객체를 통해 받아오고, Model 객체를 이용해서 뷰로 값을 넘기고 있다.