-
[Servlet] HttpServletRequest, HttpServletResponse 파헤치기프로그래밍 언어/JAVA 2022. 10. 11. 15:48
HttpServletRequest, HttpServletResponse에 대해 알아보기 전 Servlet에 대해 알아보려면 아래 링크 참조!
https://wonisdaily.tistory.com/117
📑 서블릿 환경 구현
1. 스프링부트에서 서블릿을 직접 등록해서 사용하려면 @ServletComponentScan 어노테이션을 ServletApplication에 추가하도록 한다.
2. 서블릿으로 구현할 클래스는 extends HttpServlet을 상속받아야한다.
3. 클래스 위에 @WebServlet(name = "helloServlet", urlPatterns = "/hello") name은 서블릿 이름 urlPatterns는 URL 매핑을 설정해주면 브라우저에서 해당 URL 입력 시 서블릿 클래스가 호출되어 생명주기에 맞춰 동작한다.
4. protected void service(HttpServletRequest request, HttpServletResponse response) 서블릿 클래스가 호출되면 service 메서드가 호출되는데 여기서 매개변수로 요청과 응답을 받는다.
package hello.servlet.basic; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(name = "helloServlet", urlPatterns = "/hello") public class HelloServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //was가 request, response 객체를 만들어 서버에 던져준다. //브라우저 url에서 호출한 /hello request를 서버에 던진 것.! System.out.println("HelloServlet.service"); System.out.println("request = " + request); System.out.println("response = " + response); //request.getParameter로 쿼리 파라미터 조회 가능 String username = request.getParameter("username"); System.out.println("username = " + username); response.setContentType("text/plain"); response.setCharacterEncoding("utf-8"); //write하면 body에 값이 들어간다. response.getWriter().write("hello" + username); } }
웹 브라우저에서 요청을 보내면 (url 입력) HTTP 요청 메시지를 기반으로 HTTP Request가 Servlet Container로 전송한다. 요청을 전송받은 Servlet Container는 HttpServletRequest, HttpServletResponse 객체를 생성한다.
📑 HttpServletRequest 역할
HTTP 요청 메시지를 개발자가 직접 파싱해서 사용해도 되지만, 매우 불편하다. 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신 HTTP 요청 메시지를 파싱한다. 그리고 그 결과를 HttpServletRequest 객체에 담아 제공한다.
다음과 같은 요청 메시지가 있을 때 START LINE에는 HTTP메서드, URL, 쿼리 스트링, 스키마, 프로토콜 등등과 헤더 바디를 조회할 수 있다.
또한, 중요한 기능으로 임시 저장소 기능이 있는데 해당 HTTP 요청이 시작부터 끝날 때 까지 유지되는 기능이다. 아래 2개의 set/get은 자주 쓰이니 그 역할을 알아두자.
📌 저장 : request.setAttribute(name, value)
📌 조회 : request.getAttribute(name)📑 HTTP 요청 데이터
HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법을 알아보자.
1. GET - 쿼리 파라미터
2. POST - HTML Form
3. HTTP message body에 데이터를 직접 담아서 요청🎃 1. GET - 쿼리 파라미터
만약 username=woni, age=20 이 2가지의 데이터를 서버로 전달하려면? 첫 번째 방식은 쿼리로 보내는 방법이다. 메시지 바디 없이 URL의 쿼리 파라미터를 사용해 데이터를 전달한다. ex) 검색, 필터, 페이징 등에서 많이 사용
쿼리 파라미터는 URL에 ?를 시작으로 보낼 수 있고, 여러개의 데이터가 있을 경우 &로 구분하면 된다.
http://localhost:8080/request-param?username=woni&age=20
브라우저에서 이와 같이 요청했을 경우 서버에서 HttpServletRequest가 제공하는 다양한 메서드를 통해 쿼리를 편리하게 조회할 수 있다.
package hello.servlet.basic.request; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /* * 1. 파라미터 전송 기능 * http://localhost:8080/request-param?username=hello&age=20 * */ @WebServlet(name = "requestParamServlet" , urlPatterns = "/request-param") public class RequestParamServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //request.getParameter는 get방식 post 방식 메서드의 값들 모두 꺼낼 수 있다. System.out.println("[전체 파라미터 조회] - start"); request.getParameterNames().asIterator() .forEachRemaining(paramName -> System.out.println(paramName + " = " + request.getParameter(paramName))); System.out.println("[전체파라미터 조회] -end"); System.out.println(); System.out.println("[단일 파라미터 조회] - start"); String username = request.getParameter("username"); System.out.println("username = " + username); String age = request.getParameter("age"); System.out.println("age = " + age); System.out.println("[단일 파라미터 조회] -end"); System.out.println(); //하나의 파라미터 이름에 여러개의 값을 넣을 수 있다. //key는 중복 안됨 value는 중복 가능 //중복 값은 꺼낼 때 getParameterValues //http://localhost:8080/request-param?username=hello&age=20&username=hello22 System.out.println("[이름이 같은 복수 파라미터 조회]"); String[] usernames = request.getParameterValues("username"); for(String name : usernames){ System.out.println("username = " + name); } response.getWriter().write("OK"); } }
파라미터 이름은 하나인데, 값이 중복이라면?? request.getParameter()는 하나의 파라미터 이름에 대해 단 하나의 값만 있을 때 사용하다.! http://localhost:8080/request-param?username=woni&age=20&username=wonini22 이와 같이 중복의 값을 입력했다면 request.getParameterValues()를 사용해야한다.
🎃 2. POST - HTML Form
클라이언트에서 서버로 데이터를 전송하는 방법 2번째는 post 방식을 이용하는 거다. HTML Form에서 정보를 입력하고 submit하면 POST 방식으로 서버에 데이터가 넘어가게 되는데 주로 회원 가입, 상품 주문 등등 에서 사용하는 방식이다.!
만약 POST의 HTML Form을 전송하면 웹 브라우저는 다음 형식으로 HTTP 메시지를 만든다.
요청 URL : http://localhost:8080/request-param
content-type : application/x-www-form-urlencoded
message body : username=woni&age=20application/x-www-form-urlencoded 형식은 GET에서 살펴본 쿼리 파라미터 형식과 같으므로 쿼리 파라미터 조회 메서드를 그대로 사용하면 된다. 즉) form이 있는 html 페이지에서
<form action="/request-param" method="post">
다음과 같이 action을 RequestParamServlet의 urlPattern으로 설정해두고 method는 post 방식으로 전송한다면 GET 방식의 쿼리데이터를 보낸 것과 같이 RequestParamServlet의 service 메서드가 호출되어 같은 값들이 출력된다.
만약) 간단한 테스트의 form을 만들기 귀찮다면!? postman을 사용하면 된다.!
POST 방식으로 선택 후 URL 입력하고 Body 부붙에 x-www-form-urlencoded 선택해서 key와 value값을 넘겨주면 위와 같은 동작이 실행된다.
🎃 3. HTTP message body에 데이터를 직접 담아서 요청
HTTP API, REST API라고 불리는 이 방식은 HTTP message를 body에 직접 담아서 요청한다. 데이터 형식은 주로 JSON을 사용하며, POST,PUT,PATCH가 메서드를 사용한다.
< 단순 텍스트>
package hello.servlet.basic.request; import org.springframework.util.StreamUtils; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; @WebServlet(name = "requestBodyStringServlet" , urlPatterns ="/request-body-string") public class RequestBodyStringServlet extends HttpServlet { protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletInputStream inputStream = request.getInputStream(); //메시지 바디의 내용을 바이트 코드로 얻을 수 있다. //바이트코드를 스트링으로 바꾼다. + 항상 바이트를 문자로 바꿀때 어떤 인코딩인지 알려줘야한다. String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); System.out.println("messageBody = " + messageBody); response.getWriter().write("OK"); } }
postman에서 body에 raw 를 text로 지정하고 데이터롤 전송해보자. 그럼 headers에 content-type이 text/plain인 걸 확인할 수 있다. inputStream은 byte 코드로 반환한다. byte 코드를 우리가 읽을 수 있는 문자(String)로 보려면 문자표(charset)을 지정해줘야 하는데 여기서 UTF_8을 지정해주었다.
<JSON>
HTTP API에서 주로 사용하는 방식은 JSON 형식으로 데이터를 전달하는 것이다. JSON을 사용하려면 JSON 형식으로 파싱할 수 있게 객체 클래스를 만들어줘야된다. 따라서 String username과 int age를 멤버변수로 갖는 클래스를 생성해주고 롬북을 이용해 @Getter, @Setter로 값을 받아주자.
package hello.servlet.basic; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; @Getter @Setter public class HelloData { private String username; private int age; }
다음과 같이 jackson 라이브러리 ObjectMapper를 사용해서 post로 body에 보낸 jackson 메시지를 읽어서 자바 객체로 변환하였다.
package hello.servlet.basic.request; import com.fasterxml.jackson.databind.ObjectMapper; import hello.servlet.basic.HelloData; import org.springframework.util.StreamUtils; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; @WebServlet(name = "requestBodyJsonServlet", urlPatterns = "/request-body-json") public class RequestBodyJsonServlet extends HttpServlet { //스프링 부트에선 jackson 라이브러리를 제공한다. //ObjectMapper를 이용하면 JSON을 Java 객체로 변환할 수 있다.(역직렬화) //Jackson 라이브러리는 Getter와 Setter를 이용하여 prefix를 잘라내고 맨 앞을 소문자로 만드는 것으로 필드를 식별한다 private ObjectMapper objectMapper = new ObjectMapper(); @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //inputStream으로 바디 내용을 읽어온다. ServletInputStream inputStream = request.getInputStream(); //읽어온 바디 내용을 인코딩한 후 String 타입으로 변환한다. String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); System.out.println("messageBody = " + messageBody); // JSON을 한번에 파싱하기 위해 내부 데이터를 위한 클래스를 HelloData 따로 만들어주었다 // JSON 데이터를 자바 객체로 역직렬화하기 //readValue () 함수는 또한 JSON 문자열을 포함하는 파일로서 다른 형태의 입력을 받아 들인다 HelloData helloData = objectMapper.readValue(messageBody, HelloData.class); System.out.println("helloData.username = " + helloData.getUsername()); System.out.println("helloData.age = " + helloData.getAge()); response.getWriter().write("ok"); } }
postman 이용해서 body에 raw -> json 형식으로 데이터를 보낸다.
json 형식의 데이터를 역직렬화하여 자바 객체로 읽어오기 성공!
🙄 여기서 잠깐 !!!! ObjectMapper란 ??
JSON 컨텐츠를 Java 객체로 deserialization(역직렬화)하거나 Java 객체를 JSON으로 serialization할 때 사용하는 jackson 라이브러리 클래스이다. (자바에서는 ObjectInputStream, ObjectOutputStream 사용)
package hello.servlet; import com.fasterxml.jackson.databind.ObjectMapper; import hello.servlet.basic.HelloData; import java.io.File; import java.io.IOException; public class ObjectMapperEx { public static void main(String[] args) { ObjectMapper objectMapper = new ObjectMapper(); //Java Object -> JSON //파라미터에 JSON을 저장할 파일과 직력화시킬 객체를 넣어주면 된다. HelloData helloData = new HelloData("woni", 26); try{ objectMapper.writeValue(new File("src/pseron.json"), helloData); }catch (IOException e){ e.printStackTrace(); } } }
만약 직렬화와 역직렬화에대해 궁금하다면 아래 포스팅 참고!
https://wonisdaily.tistory.com/118
📑 HttpServletResponse 역할
HttpServletResponse의 역할로는 HTTP 응답 코드 지정, 헤더 생성, 바디 생성 등이 있으며, 편의 기능으로 Content-Type, 쿠키, Redirect가 있다.
헤더는 다음과 같이 지정해주는게 간편하다.
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");상태 코드를 전송할 때 response.setStatus()와 같이 사용하는데 인자로 200 이렇게 숫자로 넣는 것 보다 HttpServletResponse.SC_OK라고 상태코드를 가르키는 이름으로 넣어주는 게 좋다. (결과는 다르지 않음)
리다이렉트를 지정할 때는 다음과 같이 사용한다.
//response.setStatus(HttpServletResponse.SC_FOUND); //302
//response.setHeader("Location", "/basic/hello-form.html");
response.sendRedirect("/basic/hello-form.html");📑 HTTP 응답 데이터
응답도 요청과 마찬가지로 데이터를 보낼 수 있는 방식이 3가지 있다.
1. 단순 텍스트
2. HTML 응답
3. HTTP API - MessageBody JSON 응답🎃 1. 단순 텍스트, HTML
: 단순 텍스트는 html을 반환해야하므로 content-type을 text/html로 지정해야 한다. 그 후 response.getWriter() 응답 개게에 println으로 html 코드를 추가해주면 된다. 이는 너무 번거롭다.
package hello.servlet.basic.response; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @WebServlet(name = "responseHtmlServlet", urlPatterns = "/response-html") public class ResponseHtmlServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //Content-Type response.setContentType("text/html"); response.setCharacterEncoding("utf-8"); PrintWriter writer = response.getWriter(); writer.println("<html>"); writer.println("<body>"); writer.println(" <div>안녕?</div>"); writer.println("</body>"); writer.println("</html>"); } }
🎃 2. API JSON
: HTTP 응답으로 JSON을 반환할때는 content-type을 json으로 지정해야 한다. 요청과 마찬가지러 Jackson 라이브러리가 제공하는 ObjectMapper를 이용하면 되는데 여기서는 응답에 값을 입력하는 것이므로 writeValueAsString()을 사용했다.
package hello.servlet.basic.response; import com.fasterxml.jackson.databind.ObjectMapper; import hello.servlet.basic.HelloData; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(name="responseJsonServlet", urlPatterns = "/response-json") public class ResponseJsonServlet extends HttpServlet { ObjectMapper objectMapper = new ObjectMapper(); @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); HelloData helloData = new HelloData(); helloData.setUsername("kim"); helloData.setAge(20); //직렬화 ( JAVA 객체를 JSON 데이터로 변환) String result = objectMapper.writeValueAsString(helloData); response.getWriter().write(result); } }
반응형'프로그래밍 언어 > JAVA' 카테고리의 다른 글
[JAVA] TreeSet과 이진 탐색 트리 (1) 2022.10.15 [JAVA] Object 클래스의 equals()와 hashCode() 재정의 (1) 2022.10.15 [JAVA ] 직렬화(Serialization)와 역직렬화(Deserialization) (0) 2022.10.11 [JAVA JUnit] assertThat의 isSameAs와 isEqaulTo 비교 분석 (1) 2022.10.01 [JAVA] static import에 대하여 ( +IntelliJ에서 사용하기) (0) 2022.09.29