일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- rest api 검증
- DispatcherServlet
- 프로세스 생성
- equals
- 옵티마이저
- HTTP프로토콜
- multipart바인딩
- HttpServlet
- 왜불변객체인가
- 디스패처서블릿
- 검증 실패 예외처리
- 데이터베이스파서
- fcmv1
- 동기비동기블로킹논블로킹
- fcm성능비교
- httpservlet기술
- 중첩클래스
- rest api
- 스프링요청반응
- java enum
- 클라이언트요청반응
- Wrapper class
- fcm데이터구조
- 옵티마저
- 공유기작동방식
- 래퍼클래스
- biblecash
- 동등성동일성
- multiparfile데이터
- 불변객체
- Today
- Total
개발은 아름다워
[ Spring ] HTTP 요청 데이터와 HtttpServletRequest,HttpServletResponse 객체의 역할 본문
[ Spring ] HTTP 요청 데이터와 HtttpServletRequest,HttpServletResponse 객체의 역할
do_it_zero 2024. 10. 12. 11:33HttpServlet은 뭘까?
클라이언트에서 서버로 요청시, 브라우저는 HTTP 프로토콜에 맞춰서 요청 메서지를 만들어서 서버로 보낸다.
HTTP 요청 메세지
POST /save HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
username=kim&age=20
서버에는 위와 같은 HTTP 요청 메세지가 온다. 그렇다면 서버에서는 HTTP 요청 메세지를 어떻게 처리해야하는걸까? HTTP 요청 메세지를 개발자가 직접 파싱해서 사용해도 되지만, 이 경우는 매우 매우 불편할 것이다. 그래서 서블릿은 개발자가 HTTP 요청 메세지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP 요청 메세지를 파싱한다. 그리고 그 결과를 HttpServletRequest 객체에 담아서 제공한
다.
또한 임시저장소 기능과 세션 관리 기능도 포함하고 있다.
즉, HttpServletRequest 란 HTTP 요청 메세지에 대한 데이터들을 갖고 있는 객체!
HttpServletRequest 기본 사용법
@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);
}
// start line 정보
private void printStartLine(HttpServletRequest request){
System.out.println("--REQUEST-LINE - start --");
System.out.println("request.getMethod() = " + request.getMethod());
System.out.println("request.getProtocol() = " + request.getProtocol());
System.out.println("request.getScheme() = " + request.getScheme());
System.out.println("request.getRequestURL() = " + request.getRequestURL());
System.out.println("request.getRequestURI() = " + request.getRequestURI());
System.out.println("request.getQueryString() = " + request.getQueryString());
System.out.println("request.isSecure() = " + request.isSecure());
System.out.println("--- REQUEST-LINE - end ---");
System.out.println();
}
// 헤더 정보
private void printHeaders(HttpServletRequest request){
System.out.println("--- Headers - start ---");
request.getHeaderNames().asIterator()
.forEachRemaining(
headerName ->
System.out.println(headerName+": " +request.getHeader(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());
System.out.println("request.getServerPort() = " + request.getServerPort());
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();
}
}
위 코드로 HTTP 요청 메세지를 HttpServletRequest가 start-line,header정보 조회를 하는지 알 수 있었다. 이번에는 HTTP 요청 데이터를 어떻게 조회하는지 알아보자
HTTP 요청 데이터 조회하기
먼저 HTTP 요청 메세지를 통해 클라리언트에서 서버로 데이터를 전달하는 방법을 알아보자
주로 다음 3가지 방법을 사용한다.
- GET - 쿼리 파라미터
- /url?username=j&age=20
- 메시지 바디 없이 URL 쿼리 파라미터에 데이터를 포함해서 전달
- 주로 검색,필터,페이징등에 많이 사용된다.
- POST - HTML FORM
- contenct-type: application/x-www-form-urlencoded
- 메시지 바디에 쿼리 파라미터 형식으로 전달 username=jj&age=20 / 형식은 쿼리 파라미터인데 데이터는 메세지 바디에 담긴다
- 주로 회원 가입,상품 주문,HTML Form에 사용됨
3.HTTP message body에 데이터 직접 담기
- HTTP API에서 주로 사용하며 JSON,XML,TEXT 등의 형식이 쓰임
- 데이터 형식은 주로 JSON 사용함
GET 쿼리 파라미터
메세지 바디 없이 URL의 쿼리 파라미터를 사용해서 데이터를 전달
서버에서는 HttpServletRequest가 제공하는 메서드들을 통해 쿼리 파라미터를 편리하게 조회할 수 있다.
http://localhost:8080/request-param?username=hello&age=20
위와 같이 URL에 ? 뒤에 데이터를 전달하는 방식이다.
쿼리 파라미터 조회 메서드
getParmeter("username") // 단일 파라미터 조회
getParameterNames() // 파라미터 이름들 모두 조회
getParameterMap() // 파라미터를 Map으로 조회
getParameterValues("username") // 복수 파라미터 조회
@WebServlet(name = "requestParamServlet",urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("[전체 파라미터 조회] - start");
req.getParameterNames().asIterator()
.forEachRemaining(paramName -> System.out.println(paramName + "=" + req.getParameter(paramName)));
System.out.println("[전체 파라미터 조회] - end");
System.out.println();
System.out.println("[단일 파라미터 조회]");
String username = req.getParameter("username");
System.out.println("request.getParameter(username) = " + username);
String age = req.getParameter("age");
System.out.println("request.getParameter(age) = " + age);
System.out.println();
System.out.println("[이름이 같은 복수 파라미터 조회]");
System.out.println("request.getParameterValues(username)");
String[] usernames = req.getParameterValues("username");
for(String name : usernames){
System.out.println("username="+name);
}
resp.getWriter().write("ok");
}
}
결과
[전체 파라미터 조회] - start
username=hello
age=20
[전체 파라미터 조회] - end
[단일 파라미터 조회]
request.getParameter(username) = hello
request.getParameter(age) = 20
[이름이 같은 복수 파라미터 조회]
request.getParameterValues(username)
username=hello
HTML Form 으로 POST 요청
이번에는 HTML의 Form을 사용해서 서버로 데이터 전송
주로 회원 가입, 상품 주문 등에서 사용하는 방식이다.
다음과 같은 특징이 있다.
- content-type: application/x-www-form-urlencoded
- 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달한다. username=hello&age=20
HTML 요청 Form
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/request-param" method="post">
username: <input type="text" name="username" />
age: <input type="text" name="age" />
<button type="submit">전송</button>
</form>
</body>
</html>
POST의 HTML Form을 전송하면 웹 브라우저는 다음 형식으로 HTTP 메시지를 만든다.
요청 URL: http://localhost:8080/request-param
content-type: application/x-www-form-urlencoded
message body: username=hello&age=20
application/x-www-form-urlencoded 형식은 앞서 GET에서 살펴본 쿼리 파라미터 형식과 같다. 메세지 바디에 데이터가 있지만 쿼리 파라미터 형식으로 전달할 뿐이다. 따라서 쿼리 파라미터 조회 메서드를 그대로 사용하면 된다.
클라이언트 입장에서는 두 방식에 차이가 있지만, 서버 입장에서는 둘의 형식이 동일하다. 그렇기에 request.getParameter()로 편리하게 구분없이 조회할 수 있는 것이다.
HTML Form 요청을 처리하는 서블릿은 위와 같다
@WebServlet(name = "requestParamServlet",urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("[전체 파라미터 조회] - start");
req.getParameterNames().asIterator()
.forEachRemaining(paramName -> System.out.println(paramName + "=" + req.getParameter(paramName)));
System.out.println("[전체 파라미터 조회] - end");
System.out.println();
System.out.println("[단일 파라미터 조회]");
String username = req.getParameter("username");
System.out.println("request.getParameter(username) = " + username);
String age = req.getParameter("age");
System.out.println("request.getParameter(age) = " + age);
System.out.println();
System.out.println("[이름이 같은 복수 파라미터 조회]");
System.out.println("request.getParameterValues(username)");
String[] usernames = req.getParameterValues("username");
for(String name : usernames){
System.out.println("username="+name);
}
resp.getWriter().write("ok");
}
}
HTML Form 요청 결과
Host: localhost:8080
Connection: keep-alive
Content-Length: 21
Cache-Control: max-age=0
sec-ch-ua: "Not/A)Brand";v="8", "Chromium";v="126", "Microsoft Edge";v="126"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/basic/hello-form.html
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: ko,en;q=0.9,en-US;q=0.8
username=hello&age=20]
[전체 파라미터 조회] - start
username=hello
age=20
[전체 파라미터 조회] - end
[단일 파라미터 조회]
request.getParameter(username) = hello
request.getParameter(age) = 20
[이름이 같은 복수 파라미터 조회]
request.getParameterValues(username)
username=hello
여기서 중요한 것은 HTML Form 요청일 경우에는 메세지 바디에 데이터가 들어가지만, content-type: application/x-www-form-urlencoded 이기 때문에 서버입장에서는 쿼리 파라미터 형식으로 받아들여서 처리하게 된다. 그래서 HttpServletRequest 객체인 request가 request.getParameter() 요청 데이터를 조회할 수 있는 것이다.
API 메세지 바디
HTTP message body에 데이터를 직접 담아서 요청하는 방법이다.
- HTTP API 에서 주로 사용된다.
- 데이터 형식은 JSON,XML,TEXT 등 이 있고 주로 데이터 형식은 JSON이 사용된다.
- POST,PUT,PATCH
- InputStream을 사용하면 HTTP 메시지 바디의 데이터를 직접 읽을 수 있다.
API 메세지 바디 JSON 데이터를 처리할 서블릿
- API 메세지 바디 JSON 요청 데이터를 JSON 형식으로 파싱할 객체
@Getter @Setter
public class HelloData {
private String username;
private int age;
}
- 서블릿
@WebServlet(name = "requestBodyJsonServlet",urlPatterns = "/request-body-json")
public class RequestBodyJsonServlet extends HttpServlet{
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream inputStream = req.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream,StandardCharsets.UTF_8);
System.out.println("messageBody = " + messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
System.out.println("helloData.username = " + helloData.getUsername());
System.out.println("helloData.age = " + helloData.getAge());
resp.getWriter().write("ok");
}
}
JSON 데이터 형식
- POST http://localhost:8080/request-body-json
- content-type: application/json
- message body: {"username": "hello", "age": 20}
결과
messageBody = {"username": "hello","age": 20}
helloData.username = hello
helloData.age = 20
HTTP API JSON 형식으로 content-type: appplication/json 요청시에 서버에서는 JSON데이터를 파싱해 줄 필요가 있기 때문에 HelloData 객체가 파싱된 데이터를 입력받았다.
이 때 중요한 것은 서버 요청시 클라이언트에서 어떤 content-type으로 보내는지 확인하는 것이 중요하다.
HttpServletResponse는 뭘까?
HttpServletResponse의 역할은 뭘까?
- HTTP 응답 메세지를 생성한다. HTTP 응답코드를 지정하고 헤더와 바디를 생성한다.
- Content-Type,쿠키,Redirect
기본 사용법
@WebServlet(name = "responseHeaderServlet",urlPatterns = "/response-header")
public class ResponseHeaderServlet extends HttpServlet{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// [ 상태코드 ]
resp.setStatus(HttpServletResponse.SC_BAD_GATEWAY);
// [ 응답 헤더 ]
resp.setHeader("Content-Type", "text.plain:charset=utf-8");
resp.setHeader("Cache-Control", "no-cache,no-store,must-revalidate");
resp.setHeader("Pragma", "no-cache");
resp.setHeader("my-header", "hello");
// [ 헤더 편의 메서드 ]
content(resp);
cookie(resp);
redirect(resp);
}
private void content(HttpServletResponse response){
// Content-Type: text/plain;charset=utf-8
// Content-Length: 2
// response.setHeader("Content-Type","text/plain;charset=utf-8")
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
// response.setContentLength(2);
}
private void cookie(HttpServletResponse response){
//Set-Cookie : myCookie=good; Max-Age=600;
//response.setHeader("Set-Cookie","myCookie=good; Max-Age=600")
Cookie cookie = new Cookie("myCookie", "good");
response.addCookie(cookie);
}
private void redirect(HttpServletResponse response) throws IOException{
// 상태 코드 302
// Location : /basic/hello-form.html
response.sendRedirect("/basic/hello-form.html");
}
}
브라우져에서 http://localhost8080/response-header로 요청 후
브라우져 검사를 열어서 네트워크 탭에서 header를 확인해보면 된다.
HTTP 응답 데이터 - 단순 텍스트,HTML
HTTP 응답 메세지는 주로 다음 내용을 담아서 전달한다.
- 단순 텍스트 응답
- HTML 응답
- HTTP API - MessageBody JSON 응답
@WebServlet(name = "responsrHtmlServlet",urlPatterns = "/response-html")
public class ResponseHtmlServlet extends HttpServlet{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Content-Type: text/html;charset=utf-8
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter write = resp.getWriter();
write.println("<html>");
write.println("<body>");
write.println(" <div>안녕?</div>");
write.println("</body>");
write.println("</html>");
}
}
클라이언트에서 http://localhost8080/response-html 요청을 보내면 서버에서는 응답데이터에 위와 같은 데이터를 내려준다. 브라우저는 응답 받은 데이터를 화면에 보여준다.
HTTP 응답 데이터 - API JSON
@WebServlet(name = "responseJsonServlet", urlPatterns = "/response-json")
public class ResponseJsonServlet extends HttpServlet{
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Content-Type : application/json
resp.setHeader("content-Type", "application/json");
resp.setCharacterEncoding("utf-8");
HelloData data = new HelloData();
data.setUsername("kim");
data.setAge(20);
// {"username":"kim","age":20}
String result = objectMapper.writeValueAsString(data);
resp.getWriter().write(result);
}
}
클라이언트에서 http://localhost:8080/response-json으로 요청하면 응답 헤더의 content-Type은 application/json 형식으로 CharacterEncoding은 utf-8로 그리고 메세지 바디로 받은 응답 데이터를 브라우져가 해석하여 화면에 보여준다.
정리
클라이언트가 요청한 데이터는 HttpServletRequest 객체가 받아서 처리하는 것을 배울 수 있었고, 서버에서 HttpServletResponse 객체를 통해 브라우져에 데어터를 응답값으로 보내는 것을 배울 수 있었다. 여기서 중요한 점은 클라이언트와 서버에서 content-type을 일치시켜야한다는 것이다. 형식이 일치하지 않으면 의도한대로 데이터를 보내거나 받을 수 없기 때문이다.
참고
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard
'스프링' 카테고리의 다른 글
[ Spring ] 고대의 서블릿을 찾아서(2) - 서블릿을 넘어 템플릿 엔진으로 (0) | 2024.10.12 |
---|---|
[ Spring ] 고대의 서블릿을 찾아서(1) - 서블릿으로만 웹 앱을 만들어보자 (2) | 2024.10.12 |
[ Spring ] DispatcherServlet 니 누구야? (0) | 2024.10.12 |
[ Spring ] customException API를 만들어보자(feat. 체크예외와 언체크예외 이해하기) (0) | 2024.10.12 |
[ Spring ] REST API 형식으로 검증 및 검증 실패 예외처리까지 (3) | 2024.10.12 |