1) 기본,헤더 조회
- 헤더정보를 담는 클래스를 생성해준다.
- url 요청을 보내고 로그를 확인해보면 헤더 정보가 콘솔에 넘어옴을 확인할 수 있다.
- MultiValueMap : MAP과 유사한데, 하나의 키에 여러 값을 받을 수 있다. HTTP header, HTTP 쿼리 파라미터와 같이 하나의 키에 여러 값을 받을 때 사용한다. keyA=value1&keyA=value2
- 다음처럼 MultiValueMap의 한 키에 여러 밸류가 들어오면 여러 밸류를 담은 배열이 반환된다.
2) HTTP 요청 파라미터- 쿼리 파라미터, HTML-FORM
- 클라이언트에서 서버로 요청 데이터를 전달할 때는 GET,POST,Http Message Body 세 방식을 사용했다.
- HttpServletRequest 의 request.getParameter() 를 사용하면 get과 post의 요청 파라미터를 모두 조회할 수 있다. 둘 다 전송 형식이 같기 때문이다. 이를 요청 파라미터 조회라 한다.
- request.getParameter() 방식으로 요청 파라미터를 조회해 로그를 찍어 보겠다.
- "/request-param-v1?username=hello&age=20" 로 요청을 보내면 정상적으로 로그가 조회된다.
- static 하위에 파일을 생성하면 자동으로 도메인 하위 경로가 생성된다. html post Form을 만들고 이 경로로 post 요청을 보내본다
- "/basic/hello-form.html" 에서 요청을 보내면 다음 처럼 로그에 내용이 정상적으로 출력된다.
3) HTTP 요청 파라미터- @RequestParam
- 스프링이 제공하는 @RequestParam 을 사용하면 요청 파라미터를 매우 편리하게 사용할 수 있다.
- requestParamV2 메소드를 생성해 준다. requestParamV1과 달리 요청 파라미터를 @RequestParam 을 통해 더 쉽게 꺼내올 수 있다.
- @ResponseBody 를 사용하면 리턴값을 그대로 http Body 안에 넣어준다.
- @RequestParam 안의 파라미터를 생략할 수 있다. 이 경우 HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam(name="xx")을 생략 가능한 것이다.
- 심지어 위 V3처럼 HTTP 파라미터 이름이 변수 이름과 같은 상태에서 String, int 등의 단순 타입이면 @RequestParam 마저도 생략 가능하다.
- 즉 단순 타입이면 @RequestParam 이 알아서 적용되는 것과 같은 말이다.
- 반드시 들어와야 하는 필수 파라미터 여부를 세팅할 수 있다
- @RequestParam의 required="xx" 요소를 설정할 때, 이를 true로 있으면 반드시 넣어주어야 한고, false면 넣지 않아도 된다. 디폴트 값은 True다.
- "/request-param-required?age=20" 으로 True로 세팅한 username 값을 url에 전달하지 않으면 Bad request 400오류가 난다.
- 그러나 "/request-param-required?username=lee" 처럼 false값인 age를 빼고 서버를 돌렸는데 500 에러가 났다. 그 이유는 age의 자료형을 int로 선언했기 때문인데, int 자료형에는 null 값이 들어갈 수 없다.
- 따라서 age의 자료형을 Integer로 선언하면 Integer는 객체형이므로 객체에는 null 값이 들어갈 수 있다. 따라서 값을 전달하지 않아도 오류가 발생하지 않는다.
- 주의할 점 : null 과 "" 은 다르다. 따라서 "/request-param-required?username=" 처럼 username은 명시하고 값은 넣지 않으면, 이를 null이 아닌 빈 문자로 본다. 따라서 오류가 나지 않고 통과된다.
- 위와 같은 경우를 방지하기 위해, 값이 넘어오지 않았을 때 자동으로 들어가도록 하는 값을 설정할 수 있다.
- @RequestParam의 defaultValue="xx" 설정을 통해 그 값을 설정 가능하다.
- url에 요청 파라미터를 전혀 넘기지 않아도 디폴트 값이 요청 파라미터로 들어오는 것을 확인 가능하다.
- *defaultValue의 경우 빈 문자에도 defaultValue 설정값이 그대로 들어온다!
-
Map 방식을 통해 요청 파라미터 값을 받고, 이를 맵.get("키") 방식으로 요청 파라미터를 꺼내올 수도 있다.
-
파라미터의 값이 1개가 확실하다면 MAP을 사용해도 되지만, 확실하지 않다면 여러 값을 받을 수 있는 "MultiValueMap"을 사용하자. 그러나 대부분 파라미터 값은 하나가 들어온다.
3) HTTP 요청 파라미터- @ModelAttribute
- 위처럼 요청 파라미터를 받아 필요한 객체를 만들고, 그 객체에 값을 넣어주는 과정이 실제 개발 과정에서 필요하다.
- 스프링은 이 과정을 완전히 자동화해주는 @ModelAttribute 기능을 제공한다.
- 롬복의 @Data 를 사용하면 @Getter , @Setter , @ToString , @EqualsAndHashCode , @RequiredArgsConstructor 를 자동으로 적용해준다.'
- 위는 @ModelAttribute 를 사용하지 않고 요청 파라미터를 객체에 넣는 코드다. @ModelAttribute를 사용한 방식으로 위를 고쳐 보겠다.
- @ModelAttribute를 사용해 고친 부분이다. 메소드의 파라미터에 "@ModelAttribute HelloData helloData"만 선언했을 뿐인데 HelloData 객체가 생성되고 요청 파라미터의 값이 자동으로 들어간다.
- 먼저 "프로퍼티" 에 대한 이해가 필요하다.
- 객체에 getUsername() , setUsername() 메서드가 있으면, 이 객체는 username 이라는 프로퍼티를 가지고 있다. (getXxx->xxx 방식으로 스프링이 알아서 프로퍼티를 짜는 것인가?)
- username 프로퍼티의 값을 변경하면 setUsername() 이 호출되고, 조회하면 getUsername() 이 호출된다.
- 스프링MVC는 @ModelAttribute 가 있으면 다음을 실행한다.
1)HelloData 객체를 생성한다.
2)요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾는다.
3)해당 프로퍼티의 setter를 호출해서 파라미터의 값을 입력(바인딩) 한다.
예) 파라미터 이름이 username 이면 setUsername() 메서드를 찾아서 호출하면서 값을 입력한다.
- 그러나, @ModelAttribute 어노테이션을 생략해도 정상적으로 동작한다.
- 그런데 우리는 @RequestParam도 생략 시 정상 동작한다고 했다. 그러면 혼란이 일어나지 않을까?
- 이를 위해서는 스프링이 해당 어노테이션을 생략이 적용하는 규칙에 대해 알아야 한다.
- 스프링은 해당 생략시 다음과 같은 규칙을 적용한다.
- 먼저 String , int , Integer 같은 단순 타입은 = @RequestParam을 사용한다.
- 나머지(직접 만든 객체)는 = @ModelAttribute를 사용한다. 그러나 (HttpServletResponse 같은) argument resolver라는 것으로 지정해둔 타입은 @ModelAttribute를 적용하지 않는다.
4) HTTP 요청 메세지- 단순 텍스트
- 요청 파라미터와 다르게 http 바디를 통해 직접 데이터가 넘어오면 @ModelAttribute를 사용할 수 없다.
- 먼저 가장 단순한 텍스트 메세지를 바디에 담고 전송해 읽어보겠다. InputStream을 통해 http 바디 안의 데이터를 읽을 수 있다.]
- 컨트롤러를 선언하고 URL POST 요청에 대한 메소드를 생성한다. getInputStream을 통해 request의 메세지 바디 내용을 읽어와 인코딩하고 로그를 찍는 코드다.
- request, response 는 메세지 바디를 제외하고 서블릿 등등에 대한 정보를 모두 넘기기 때문에 굳이 그 정보를 받을 필요가 없다면, InputStream, Writer를 사용해 메세지 바디 내용만 받을 수 있다.
- 그러나 이마저도 더 줄일 수 있다. HttpEntity을 사용해 메소드 반환과 파라미터를 모두 지정하면 인코딩 등의 과정도 필요 없이 httpEntity.getBody()를 통해 httpEntity 안에서 다이렉트로 http Body 내용을 조회할 수 있다.
- return new HttpEntity<>를 사용하면 <>안의 첫 파라미터에 바디 메세지를 넣어 반환할 수 있다.
- HttpEntity는 응답에도 사용 가능하다
- 메시지 바디 정보를 직접 반환하고 헤더 정보 포함 가능하다
- view를 조회하지 않는다.
- Body를 조회하는 코드마저도 줄일 수 있다. @RequestBody 를 사용하면 getBody()를 사용하지 않고도 HTTP 메시지 바디 정보를 편리하게 조회할 수 있다.
- 또 메소드 맨 위에 @ResponseBody 를 사용하면 반환된 문자열을 httpBody 안에 넣어 응답해준다.
- 즉 요청은 @RequestBody, 응답은 @ResponseBody로 사용하는 것이다.
5) HTTP 요청 메세지- JSON
-
Json이니 ObjectMapper가 필요하다.
-
먼저 inputStream을 읽어와 메세지 바디 내용을 인코딩하는 코드를 세팅한다.
-
ObjectMapper로 메세지 바디와 HelloData 클래스 타입을 파라미터로 넘겨주어 Json 밸류를 반환하도록 한다.
-
postman으로 json 요청을 보내보면 정상적으로 로그에 조회가 된다.
- 위에서 배운 @RequestBody, @ResponseBody 를 사용해 코드를 축약할 수 있다.
- 그러나 더 편한 방법이 있다, 굳이 ObjectMapper으로 메세지 바디를 변환하지 않아도 메소드 파라미터에 HelloData 객체를 바로 넣어주면 오브젝트 맵퍼 없이 @RequestBody 에 직접 만든 객체를 저장할 수 있다.
-
HttpEntity , @RequestBody 를 사용하면 HTTP 메시지 컨버터가 HTTP 메시지 바디의 내용을 우리가 원하는 문자나 객체 등으로 변환해준다.
-
HTTP 메시지 컨버터는 문자 뿐만 아니라 JSON도 객체로 변환해주는데, 우리가 방금 V2에서 했던 작업을 대신 처리해준다. 즉 content-type이 JSON일 경우 알아서 처리해주는 것이다. 그렇게 해서 생긴 helloData를 파라미터로 넣어주는 것이다.
(여기는 복습 필요)
-
@RequestBody는 생략해서는 안 된다.
- 앞에서 배운 것 처럼 HttpEntity를 사용하고 .getBody()를 통해 바디 메세지를 꺼내와도 된다.
- 지금처럼 @ResponseBody가 파라미터로 받은 객체 자체를 응답해 http body에 넣을 수도 있다.
- @RequestBody 요청은 JSON 요청이 오면 HTTP 메시지 컨버터가 컨텐트타입을 보고 이를 객체화한다. 그 객체가 위 메소드 안의 파라미터로 있는 HelloData data로 넘어오는 것이다.
- @ResponseBody 응답은 @RequestBody를 통해 만들어진 객체를 HTTP 메시지 컨버터를 통해 JSON 응답하는 것이다.
- 실제 서버로 json 요청을 보내면 그대로 응답도 json으로 오는 것을 볼 수 있다.