* 스프링 입문 = window, 스프링 MVC 1편 = Mac 으로 진행합니다
* 진도 : 섹션6 - (5)~(10)
* : 자바 클래스명, : 코드, : 단축키
1. HTTP 요청 - 기본, 헤더 조회
[ src - main - java - hello.springmvc - basic - request - RequestHeaderController ]
- HTTP 요청 메세지를 손쉽게 작성해주는 기능
- 구조
< START LINE >
- HTTP 메소드
- URL
- 쿼리 스트링
- 스키마, 프로토콜
< 헤더 >
- 헤더 조회
< 바디 >
- form 파라미터 형식 조회
- message body 데이터 직접 조회
@Slf4j
@RestController
public class RequestHeaderController {
@RequestMapping("/headers")
public String headers(HttpServletRequest request,
HttpServletResponse response,
HttpMethod httpMethod,
Locale locale,
@RequestHeader MultiValueMap<String, String> headerMap,
@RequestHeader("host") String host,
@CookieValue(value = "myCookie", required = false) String cookie
){
log.info("request={}", request);
log.info("response={}", response);
log.info("httpMethod={}", httpMethod);
log.info("locale={}", locale);
log.info("headerMap={}", headerMap);
log.info("header host={}", host);
log.info("myCookie={}", cookie);
return "ok";
}
}
- 로그 선언 @Slf4j
- @RestController
- 반환값(= return)을 @Controller는 뷰로 연결, RestController는 문자로 연결
- 요청 정보 매핑 @RequestMapping
- 요청 정보를 매핑 (해당 URL이 호출되면 이 메서드가 호출)
- HTTP 메서드 조회 HttpMethod
- Locale 정보 조회 Locale
- 모든 헤더 조회 @RequestHeader MultiValueMap ~
- MAP과 유사
- 하나의 key에 여러 값 받을 수 있다
- 특정 헤더 조회 @RequestHeader("host") ~
- 특정 쿠키 조회 @CookieValue ~
- 필수 값 여부: required
- 기본 값: defaultValue
- 로그 출력 log.~
[ Servlet의 HTTP Request 비교 < 2 - (3) 포스트 참고 > ]
정보 조회
- start-line
- Header 모든 정보
- Header 특정 정보
- 기타 정보
@WebServlet(name = "requestHeaderServlet", urlPatterns = "/request-header")
public class RequestHeaderServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
printStartLine(request);
printHeaders(request);
printHeaderUtils(request);
printEtc(request);
}
private void printStartLine(HttpServletRequest request) {
System.out.println("--- REQUEST-LINE - start ---");
System.out.println("request.getMethod() = " + request.getMethod()); //GET
System.out.println("request.getProtocal() = " + request.getProtocol()); //HTTP/1.1
System.out.println("request.getScheme() = " + request.getScheme()); //http
// http://localhost:8080/request-header
System.out.println("request.getRequestURL() = " + request.getRequestURL());
// /request-test
System.out.println("request.getRequestURI() = " + request.getRequestURI());
//username=hi
System.out.println("request.getQueryString() = " + request.getQueryString());
System.out.println("request.isSecure() = " + request.isSecure()); //https 사용 유무
System.out.println("--- REQUEST-LINE - end ---");
System.out.println();
}
//Header 모든 정보
private void printHeaders(HttpServletRequest request) {
System.out.println("--- Headers - start ---");
request.getHeaderNames().asIterator()
.forEachRemaining(headerName -> System.out.println(headerName + ": " + headerName));
System.out.println("--- Headers - end ---");
System.out.println();
}
private void printHeaderUtils(HttpServletRequest request) {
System.out.println("--- Header 편의 조회 start ---");
System.out.println("[Host 편의 조회]");
System.out.println("request.getServerName() = " + request.getServerName()); //Host 헤더
System.out.println("request.getServerPort() = " + request.getServerPort()); //Host 헤더
System.out.println();
System.out.println("[Accept-Language 편의 조회]");
request.getLocales().asIterator()
.forEachRemaining(locale -> System.out.println("locale = " + locale));
System.out.println("request.getLocale() = " + request.getLocale());
System.out.println();
System.out.println("[cookie 편의 조회]");
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
System.out.println(cookie.getName() + ": " + cookie.getValue());
}
}
System.out.println();
System.out.println("[Content 편의 조회]");
System.out.println("request.getContentType() = " + request.getContentType());
System.out.println("request.getContentLength() = " + request.getContentLength());
System.out.println("request.getCharacterEncoding() = " + request.getCharacterEncoding());
System.out.println("--- Header 편의 조회 end ---");
System.out.println();
}
//기타 정보
private void printEtc(HttpServletRequest request) {
System.out.println("--- 기타 조회 start ---");
System.out.println("[Remote 정보]");
System.out.println("request.getRemoteHost() = " + request.getRemoteHost()); //
System.out.println("request.getRemoteAddr() = " + request.getRemoteAddr()); //
System.out.println("request.getRemotePort() = " + request.getRemotePort()); //
System.out.println();
System.out.println("[Local 정보]");
System.out.println("request.getLocalName() = " + request.getLocalName()); //
System.out.println("request.getLocalAddr() = " + request.getLocalAddr()); //
System.out.println("request.getLocalPort() = " + request.getLocalPort()); //
System.out.println("--- 기타 조회 end ---");
System.out.println();
}
}
2. HTTP 요청 파라미터 (Get 쿼리 파라미터 VS Post HTML Form)
클라이언트에서 서버로 요청 데이터를 보내는 방법은 딱 3가지이다
GET - 쿼리 파라미터 | 메세지 바디 없이 URL의 쿼리 파라미터에 데이터 포함해 전달 | |
POST - HTML Form | 메세지 바디에 쿼리 파라미터 형식으로 전달 | |
HTTP - Message body | HTTP API에 주로 사용 (JSON, XML, TEXT) |
1) GET - 쿼리 파라미터
[ src - main - java - hello.springmvc - basic - request - RequestParamServlet ]
- 메시지 바디 없이 URL의 쿼리 파라미터를 사용해서 데이터를 전달
- 쿼리파라미터는URL에서 ?를 넣어 시작하고 추가파라미터는 &로 구분
예) http://localhost:8080/request-param?username=hello&age=20
@Slf4j
@RestController
public class RequestParamController {
@RequestMapping("request-param-v1")
public void requestParamV1(HttpServletRequest request, HttpServletResponse response)throws IOException {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
log.info("username = {}, age = {}", username, age);
response.getWriter().write("ok");
}
}
- 로그 선언 @Slf4j
- @RestController
- 반환값(=return)을 @Controller는 뷰로 연결, RestController는 문자로 연결
- 요청 정보 매핑 @RequestMapping ~
- 요청 정보를 매핑 (해당 URL이 호출되면 이 메서드가 호출)
- 메서드 코드 직접 치기 public void requestParamV1 ~
- 요청 파라미터 조회 request.getParameter() (option + command + v)
- GET URL 쿼리 파라미터 형식, POST HTML Form 형식도 둘 다 지원 가능
- age는 int 형으로 한 번 더 바꿔주기
- 로그 출력 log.~
[ Servlet의 HTTP Request 비교 < 2 - (3) 포스트 참고 > ]
- @WebServlet
- protected service (control + o)
- 단일 파라미터 조회
파라미터 | 코드 |
모두 조회 | getParameterNames() |
단일 조회 | getParameter("파라미터 key값") |
복수 조회 | getParameterValues("파라미터 key값") |
@WebServlet(name = "requestParamServlet", urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[단일 파라미터 조회]");
String username = request.getParameter("username");
String age = request.getParameter("age");
System.out.println("username = " + username);
System.out.println("age = " + age);
System.out.println();
response.getWriter().write("ok");
}
}
2) HTTP 요청 데이터 - POST HTML Form
[ src - main - resources - static - basic - hello-form.html ]
- 강의에서 제공되는 html form으로 작성한 뒤 실행 (서블릿 버전이랑 같음)
1. http://localhost:8080/basic/hello-form.html 실행
2. 정적 html이 적용된 화면에서 데이터를 입력해 전송하면 콘솔로그에 찍힌다
3. http://localhost:8080/request-param 에서도 f12 누르고 form-data 확인가능
<참고>
- 매번 Form 작성하기 싫다면 Postman 사용
- Postman에서 http://localhost:8080/request-param-v1 실행
- body 클릭 → x-www-form-urlencoded 클릭 → 원하는 key, value 입력
- Headers에서 content-type이 application/x-www-form-urlencoded 로 바뀐 것 확인
- content-type: HTTP 메시지 바디의 데이터 형식을 지정 (→ Post)
- GET 방식은 HTTP 메시지 바디를 사용하지 않기 때문에 content-type이 없다
2. HTTP 요청 파라미터 개선하기 _ @RequestParam 버전
1) @RequestParam 사용
[ src - main - java - hello.springmvc - basic - request - RequestParamController ]
@Slf4j
@RestController
public class RequestParamController {
@RequestMapping("/request-param-v2")
public String requestParamV2(
@RequestParam("username") String memberName,
@RequestParam("age") int memberAge){
log.info("username = {}, age = {}", memberName, memberAge);
return "ok";
}
}
- 로그 선언 @Slf4j
- @RestController
- 반환값(=return) 뷰가 아닌 문자로 연결
- 만약 @Controller를 사용했다면 반환값이 ok 문자가 아닌 ok이름의 뷰를 연결하므로
메서드 안에 @ResponseBody를 사용하면 뷰 호출이 아닌 HTTP message body에 직접 해당 내용 입력해준다
- 요청 정보 매핑 @RequestMapping ~
- 요청 정보를 매핑 (해당 URL이 호출되면 이 메서드가 호출)
- 메서드 코드 직접 치기 public String ~
- 요청 파라미터 조회 RequestParam
- request.getParameter()와 같은 기능
- 파라미터 이름과 변수명이 같다면 @RequestParam(name="xx") 의 이름 생략 가능
- 데이터가 String, Integer, Int, 등 단순 타입이면 @RequestParam도 생략 가능
(하지만 실무에서는 쓰는걸 추천)
- 로그 출력 log.~
2) 변수명과 같은 이름 생략, required 추가
[ src - main - java - hello.springmvc - basic - request - RequestParamController ]
@ResponseBody
@RequestMapping("/request-param-required")
public String requestParamRequired(
@RequestParam(required = true) String username,
@RequestParam(required = false) Integer age){
log.info("username = {}, age = {}", username, age);
return "ok";
}
- 로그 선언 @Slf4j
- @ResponseBody
- @Controller를 사용했다면 메서드 안에 @ResponseBody를 사용할 것
- 요청 정보 매핑 @RequestMapping ~
- 요청 정보를 매핑 (해당 URL이 호출되면 이 메서드가 호출)
- 메서드 코드 직접 치기 public String ~
- 요청 파라미터 조회 @RequestParam
- 파라미터 이름과 변수명이 같다면 @RequestParam(name="xx") 의 이름 생략 가능
- request.getParameter()와 같은 기능
- 조건 추가 (required = ~ )
- true: url에 무조건 있어야 돌아간다 / false : url에 없어도 돌아간다
- 빈문자는 null이 아니기 때문에 돌아간다
예) username의 required = ture 라면
http://localhost:8080/request-param-required? → 에러
http://localhost:8080/request-param-required?username= → 돌아간다 - 참고로 기본형 int는 null을 받을 수 없어서 false로 설정했다 하더라도 값을 넣지 않으면 오류난다
객체형 integer null을 받을 수 있어서 값 안 넣어도 실행된다
- 로그 출력 log.~
3) defaultValue 추가
[ src - main - java - hello.springmvc - basic - request - RequestParamController ]
@ResponseBody
@RequestMapping("/request-param-default")
public String requestParamDefault(
@RequestParam(required = true, defaultValue = "guest") String username,
@RequestParam(required = false, defaultValue = "-1") int age){
log.info("username = {}, age = {}", username, age);
return "ok";
}
- 로그 선언 @Slf4j
- @ResponseBody
- @Controller를 사용했다면 메서드 안에 @ResponseBody를 사용할 것
- 요청 정보 매핑 @RequestMapping ~
- 요청 정보를 매핑 (해당 URL이 호출되면 이 메서드가 호출)
- 메서드 코드 직접 치기 public String ~
- 요청 파라미터 조회 @RequestParam
- 파라미터 이름과 변수명이 같다면 @RequestParam(name="xx") 의 이름 생략 가능
- request.getParameter()와 같은 기능
- 조건 추가 (required = ~ )
- true: url에 무조건 있어야 돌아간다 / false : url에 없어도 돌아간다
- 빈문자는 null이 아니기 때문에 돌아간다
예) username의 required = ture 라면
http://localhost:8080/request-param-required? → 에러
http://localhost:8080/request-param-required?username= → 돌아간다 - 참고로 기본형 int는 null을 받을 수 없어서 false로 설정했다 하더라도 값을 넣지 않으면 오류난다
객체형 integer null을 받을 수 있어서 값 안 넣어도 실행된다
- 조건 추가 defaultValue = ~
- 값이 없는 부분은 defaultValue로 채워주기 때문에 사실상 required 필요가 없다
- 빈문자인 경우에도 defaultValue로 채운다
- 로그 출력 log.~
4) ParamMap 조회
[ src - main - java - hello.springmvc - basic - request - RequestParamController ]
@ResponseBody
@RequestMapping("/request-param-map")
public String requestParamMap(@RequestParam Map<String, Object> paramMap){
log.info("username = {}, age = {}", paramMap.get("username"), paramMap.get("age"));
return "ok";
}
- 로그 선언 @Slf4j
- @ResponseBody
- @Controller를 사용했다면 메서드 안에 @ResponseBody를 사용할 것
- 요청 정보 매핑 @RequestMapping ~
- 요청 정보를 매핑 (해당 URL이 호출되면 이 메서드가 호출)
- 메서드 코드 직접 치기 public String ~
- map으로 파라미터 조회 @RequestParam Map ~
-
하나의 키에 들어간 여러값 조회하려면 아래와 같이 사용한다@RequestParam MultiValueMap
MultiValueMap(key=[value1, value2, ...]
- 로그 출력 log.~
3. HTTP 요청 파라미터 개선하기 _ @ModelAttribute 버전
[ src - main - java - hello.springmvc - basic - request - RequestParamController ]
1) 변경 전 (@RequestParam 버전)
public String modelAttributeV1(@RequestParam String username, @RequestParam int age){
HelloData hellodata = new HelloData();
hellodata.setUsername(username);
hellodata.setAge(age);
}
위의 코드처럼 요청 파라미터( = @RequestParam)를 받아서 필요한 객체( = hellodata)를 만들고 그 객체에 값을 넣어주어야 한다
이 과정을 완전히 자동화해주는 기능이 @ModelAttribute 이다
2) 변경 후 (@ModelAttribute 버전)
@ResponseBody
@RequestMapping("/model-attribute-v2")
public String modelAttributeV2(HelloData helloData) {
log.info("username={},age={}", helloData.getUsername(), helloData.getAge());
return "ok";
}
- 로그 선언 @Slf4j
- @ResponseBody
- @Controller를 사용했다면 메서드 안에 @ResponseBody를 사용할 것
- 요청 정보 매핑 @RequestMapping ~
- 요청 정보를 매핑 (해당 URL이 호출되면 이 메서드가 호출)
- 메서드 코드 직접 치기 public String ~
- 파라미터 자동화 @ModelAttruibute HelloData helloData
- @ModelAttribute 는 생략이 가능하다
-
@ModelAttribute 과정1. HelloData 객체를 생성
2. 요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾기
3. 해당 프로퍼티의 setter를 호출해서 파라미터의 값 입력
예) 파라미터 이름이 username 이면 setUsername() 메서드를 찾아서 호출하면서 값 입력** 프로퍼티란 **
객체에 getUsername() , setUsername() 메서드 있다 → 이 객체는 username 프로퍼티를 갖는다
username 프로퍼티의 값을 변경하면 setUsername() 이 호출, 조회하면 getUsername() 이 호출
- 로그 출력 log.~
<여기서 질문 !! >
@ModelAttribute, @RequestParam 둘 다 생략이 가능한데 그럼 어떻게 구분하죠?
→ String , int , Integer 같은 단순 타입 = @RequestParam 사용
나머지 = @ModelAttribute (argument resolver 로 지정해둔 타입 예외)
* argument resolver 예시 : HttpResponse
4. HTTP 요청 메세지 _ 단순 텍스트
[ src - main - java - hello.springmvc - basic - request - RequestBodyJsonController ]
HTTP message body에 데이터를 직접 담아서 요청
HTTP API에서 주로 사용, JSON, XML, TEXT 데이터 형식은 주로 JSON 사용
@RequestParam , @ModelAttribute 를 사용할 수 없다
- 1단계
@Slf4j
@Controller
public class RequestBodyJsonController {
private ObjectMapper objectMapper = new ObjectMapper();
@PostMapping("/request-body-json-v1")
public void requestBodyJsonV1(HttpServletRequest request, HttpServletResponse response)throws IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("messageBody={}",messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
response.getWriter().write("ok");
}
- 로그 선언 @Slf4j
- @Controller
- ObjectMapper
- HelloData 변환 객체 생성을 위해 Jackson 라이브러리가 필요하다
- Post 타입의 요청 정보 매핑 @PostMapping ~
- 메서드 코드 직접 치기 public void ~
- 값 읽어오기 request.getInputStream (option + command + v)
- InputStream(Reader): HTTP 요청 메시지 바디의 내용을 직접 조회
OutputStream(Writer): HTTP 응답 메시지의 바디에 직접 결과 출력 - HTTP Request로 넘어온 값들 중 일부는 getParameter()나 getParameterValues()로 읽을 수 없다
- POST 메서드를 사용하면서 CONTENT-TYPE이 "application/json" 형식일 때 발생
- StreamUtils (option + command + v)
- InputStream에 있는 내용을 String 으로 가져오는 등의 작업을 해야 하는 경우에 StreamUtils를 사용
- copyToString: 입력 스트림을 String으로 복사함
- message body를 HelloData 값으로 읽어오기(option + command + v)
- 2단계
@Slf4j
@Controller
public class RequestBodyJsonController {
private ObjectMapper objectMapper = new ObjectMapper();
@ResponseBody
@PostMapping("/request-body-json-v2")
public String requestBodyJsonV2(@RequestBody String messageBody)throws IOException{
log.info("messageBody={}",messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
return "ok";
}
}
- 로그 선언 @Slf4j
- @Controller
- @ResponseBody
- @Controller를 사용했다면 메서드 안에 @ResponseBody를 사용할 것
- ObjectMapper
- HelloData 변환 객체 생성을 위해 Jackson 라이브러리가 필요하다
- Post 타입의 요청 정보 매핑 @PostMapping ~
- 메서드 코드 직접 치기 public String ~
- InputStream 사용하지 않고 값 바로 읽어오기 (@RequestBody String messageBody)
- message body를 HelloData 값으로 읽어오기(option + command + v)
- 3단계
@Slf4j
@Controller
public class RequestBodyJsonController {
private ObjectMapper objectMapper = new ObjectMapper();
@ResponseBody
@PostMapping("/request-body-json-v3")
public String requestBodyJsonV3(@RequestBody HelloData helloData){
log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
return "ok";
}
}
- 로그 선언 @Slf4j
- @Controller
- @ResponseBody
- @Controller를 사용했다면 메서드 안에 @ResponseBody를 사용할 것
- ObjectMapper
- HelloData 변환 객체 생성을 위해 Jackson 라이브러리가 필요하다
- Post 타입의 요청 정보 매핑 @PostMapping ~
- 메서드 코드 직접 치기 public String ~
- InputStream 사용하지 않고 값 바로 읽어오기 (@RequestBody HelloData messageBody)
- @RequestBody 타입을 String → HelloData로 변경하면 읽어오는 과정 생략 가능
- 4단계
@Slf4j
@Controller
public class RequestBodyJsonController {
private ObjectMapper objectMapper = new ObjectMapper();
@ResponseBody
@PostMapping("/request-body-json-v4")
public String requestBodyJsonV4(HttpEntity<HelloData> httpEntity){
HelloData data = httpEntity.getBody();
log.info("username={}, age={}", data.getUsername(), data.getAge());
return "ok";
}
}
- 로그 선언 @Slf4j
- @Controller
- @ResponseBody
- @Controller를 사용했다면 메서드 안에 @ResponseBody를 사용할 것
- ObjectMapper
- HelloData 변환 객체 생성을 위해 Jackson 라이브러리가 필요하다
- Post 타입의 요청 정보 매핑 @PostMapping ~
- 메서드 코드 직접 치기 public String ~
- InputStream 사용하지 않고 값 바로 읽어오기 HttpEntity<HelloData> httpEntity
- 값 읽어올 때 @RequestBody 또는 HttpEntity<>를 사용한다
-
HTTP 메시지 컨버터가 HTTP 메시지 바디의 내용을 우리가 원하는 문자나 객체 등으로 변환
- 요청 파라미터를 조회하는 기능과 관계 없음 @RequestParam X, @ModelAttribute X
- 꺼내기 httpEntity.getBody()(option + command + v)
- 5단계
@Slf4j
@Controller
public class RequestBodyJsonController {
private ObjectMapper objectMapper = new ObjectMapper();
@ResponseBody
@PostMapping("/request-body-json-v5")
public HelloData requestBodyJsonV5(@RequestBody HelloData data){
log.info("username={}, age={}", data.getUsername(), data.getAge());
return data;
}
}
- 로그 선언 @Slf4j
- @Controller
- @ResponseBody
- @Controller를 사용했다면 메서드 안에 @ResponseBody를 사용할 것
- ObjectMapper
- HelloData 변환 객체 생성을 위해 Jackson 라이브러리가 필요하다
- Post 타입의 요청 정보 매핑 @PostMapping ~
- 메서드 코드 직접 치기 public HelloData ~
- 반환 타입 HelloData로 바로 설정
- InputStream 사용하지 않고 값 바로 읽어오기 (@RequestBody HelloData data)
- 값 읽어올 때 @RequestBody 또는 HttpEntity<>를 사용한다
-
HTTP 메시지 컨버터가 HTTP 메시지 바디의 내용을 우리가 원하는 문자나 객체 등으로 변환
- 요청 파라미터를 조회하는 기능과 관계 없음 @RequestParam X, @ModelAttribute X
- @RequestBody 요청
JSON 요청 → HTTP 메시지 컨버터 → 객체
@ResponseBody 응답
객체→ HTTP 메시지 컨버터 → JSON 응답
[출처] 김영한 강사님 인프런 스프링 MVC 1편
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard
스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의
웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 원
www.inflearn.com
'Spring > 스프링 MVC' 카테고리의 다른 글
[스프링 MVC 1편] 7 - (1) 프로젝트 생성 및 자바 버전 변경 (1) | 2023.12.01 |
---|---|
[스프링 MVC 1편] 6 - (4) 응답 & 메세지 컨버터 & 핸들러 어댑터 (1) | 2023.11.29 |
[스프링 MVC 1편] 6 - (2) 스프링 MVC 요청 매핑 (0) | 2023.11.23 |
[스프링 MVC 1편] 6 - (1) 스프링 MVC 프로젝트 생성 & 로깅 (1) | 2023.11.22 |
[스프링 MVC 1편] 5 - (2) 스프링 MVC (2) | 2023.11.17 |