(2022.08.06 수정) - 복습을 하면서, 기억할 부분 추가 및 코드 부분 삭제.
스프링 부트는 톰캣 서버를 내장하고 있으므로, 톰캣 서버 설치 없이 편리하게 서블릿 코드를 실행할 수 있다.
스프링 부트는 서블릿을 직접 등록해서 사용할 수 있도록 @ServletComponentScan을 지원한다.
다음과 같이 같이 코어 애플리케이션 클래스에 추가하자.
@ServletComponentScan
@SpringBootApplication
public class ServletApplication {
public static void main(String[] args) {
SpringApplication.run(ServletApplication.class, args);
}
}
이제 직접 실제로 동작하는 서블릿 코드를 작성해보자.@WebServlet는 서블릿 애노테이션이다.
해당 서블릿의 이름은 helloServlet이고, /hello 경로로 요청이 오면 호출된다. 즉 url을 매핑한다.
HTTP 요청을 통해 매핑된 URL이 오면 서블릿 컨테이너는 다음 메서드를 실행한다.
@WebServlet(name="helloServlet",urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {//서블릿은 이것을 상속 받아야함.
@Override //서블릿이 호출되면 서비스 메소드가 실행된다.
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
System.out.println("req = " + req);
System.out.println("res = " + res);
res.setContentType("text/plain");//리턴할 콘텐츠 타입 설정
res.setCharacterEncoding("utf-8"); //인코딩 설정 어디서나 웬만하면 utf-8 사용
res.getWriter().write("hello "+req.getParameter("username"));//http resposne body에 들어감
}
}
HttpServletRequest를 콘솔에 찍어보면 org.apache.catalina.connector.RequestFacade@랜덤값 이렇게 찍힌다
HttpServletResponse는 인터페이스로 Was, 우리의 경우는 톰캣이 서블릿 표준 인터페이스를 구현해 값을 넣는 것이다.
application.properties에 아래 설정을 추가하고, 서버를 다시 시작하면 서버가 받은 HTTP 요청 메시지를 모두 출력한다.
로깅 레벨을 debug 레벨로 설정한 것이다.
운영 서버에는 이런 모든 요청을 로깅하면 성능 저하가 발생하므로 개발 단계에서만 적용하는 것이 좋다고 한다.
logging.level.org.apache.coyote.http11=debug
참고로 HTTP 응답에서는 Content- Length는 WAS 서버가 자동으로 생성해준다.
HttpServletRequest
만약 Http 요청 메시지를 우리 개발자들이 직접 파싱해서 사용하면 같은 일을 매번 반복하고 불편할 것이다.
서블릿은 개발자가 Http 요청 메시지를 편리하게 사용할 수 있도록 Http 요청 메시지를 파싱해서 제공한다.
그 결과를 바로 HttpServletRequest에 담아서 개발자들에게 제공한다.
HTTP 요청 메시지 예시다.
POST /save HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
username=kim&age=20
- START LINE
- HTTP 메소드
- URL
- 쿼리 스트링
- 스키마, 프로토콜
- 헤더
- 헤더 조회
- 바디
- form 파라미터 조회 형식
- message body 데이터 직접 조회
이런 HTTP 요청 메시지를 HttpServletRequest를 사용하면 쉽게 조회할 수 있다.
실습을 통해 서블릿이 제공하는 다양한 메서드를 통해 HTTP 요청 메시지의 Start-Line, header 정보 조회 방법을 확인해봤다
좋은 내용이 많고 메서드들을 자세하게 설명해주시니 강의를 통해 설명을 들으면 좋을 것 같다.
기억할 부분
이전에 프로젝트하면서 클라이언트 ID를 가져오려고 했는데 계속 IPv6 정보가 나와서 곤란한적이 있었다.
이에 대한 해결법은 아래와 같다.
로컬에서 테스트하면 IPv6 정보가 나오는데, IPv4 정보를 보고 싶으면 다음 옵션을 VM options에 넣어주면 된다.
-Djava.net.preferIPv4Stack=true
HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법 3가지.
- GET 쿼리 파라미터 - /url?username=kim&age=20
- POST HTML FORM - 메시지 바디에 쿼리 파라미터 형식으로 전달
- HTTP message body에 데이터를 직접 담아서 요청 - HTTP API에서 주로 사용. 주로 JSON, XML, TEXT
GET 쿼리 파라미터
예시 URL http://localhost:8080/request-param?username=hello&age=20 을 가지고 실습을 진행했다.
전체 파라미터 조회, 단일 파라미터 조회, 이름이 같은 복수 파라미터 조회 모두 실행해 보았다.
실습을 하면서 기억할 부분
username=hello&username=kim 과 같이 파라미터 이름은 하나인데, 값이 중복이면 어떻게 될까?
지금처럼 중복일 때는 request.getParameterValues() 를 사용해야 한다.
참고로 이렇게 중복일 때 request.getParameter() 를 사용하면 request.getParameterValues()의 첫 번째 값을 반환한다.
POST HTML FORM
특징은 content-type가 application/x-www-form-urlencoded다.
또한 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달한다. 이 형식은 앞서 GET에서 살펴본 쿼리 파라미터 형식과 같다.
그러므로 쿼리 파라미터 조회 메서드를 그대로 사용하면 된다. 즉 request.getParameter()형식으로 구분 없이 조회할 수 있다.
결론은 request.getParameter() 메서드는 GET URL 쿼리 파라미터 형식, POST HTML FORM 형식도 지원한다.
따라서 쿼리 파라미터 실습 코드와 동일하게 POST 방식으로 테스트를 진행하면 잘 동작한다.
이렇게 Form 방식으로 데이터를 전달하는 실습도 마무리했다.
HTTP message Body에 데이터를 직접 담아서 요청
이 부분은 정말 많이 사용하므로 코드로 남겨보겠다.
HTTP API 형식에서 주로 사용하며 JSON, XML, TEXT 형식을 이용한다. 먼저 단순 TEXT일 경우에 알아보자.
HTTP 메시지 바디의 데이터를 InputStream을 사용해서 직접 읽을 수 있다.
@WebServlet(name = "requestBodyStringServlet", urlPatterns = "/request-body-string")
public class RequestBodyStringServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
ServletInputStream inputStream = req.getInputStream();
String messageBody=StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);//인코딩 방식을 알려줌
//> inputStream은 byte 코드를 반환한다. byte 코드를 우리가 읽을 수 있는 문자(String)로 보려면 문자표
//(Charset)를 지정해주어야 한다. 여기서는 UTF_8 Charset을 지정해주었다.
System.out.println("messageBody = " + messageBody);
res.getWriter().write("ok");
}
}
POSTMAN을 이용해서 TEXT 데이터를 보내면 잘 동작하는 것을 확인할 수 있다.
이제 TEXT가 아닌 JSON 방식으로 API에 데이터를 전달할 때를 확인해보자.
헤더에서 content-type은 application/json으로 지정한다.
우선 JSON 형식을 파싱할 객체를 하나 만들어보자.
//JSON 형식으로 파싱할 수 있는 객체를 만들기 위한 클래스.
@Getter @Setter
public class HelloData {
private String username;
private int age;
}
이제 JSON 결과를 읽어오는 코드를 작성해보자.
@WebServlet(name = "requestBodyJsonServlet", urlPatterns = "/request-body-json")
public class RequestBodyJsonServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
ServletInputStream inputStream = req.getInputStream();
String messageBody= StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
//여기까지는 TEXT와 동일
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
System.out.println("helloData.username = " + helloData.getUsername());
System.out.println("helloData.age = " + helloData.getAge());
res.getWriter().write("ok");
}
}
JSON 결과를 파싱해서 사용할 수 있는 자바 객체로 변환하려면 Jackson 같은 JSON 변환 라이브러리를 추가해서 사용해야 한다.
스프링 부트로 Spring MVC를 선택하면 기본으로 Jackson라이브러리(ObjectMapper)를 함께 제공한다.
HttpServletResponse
우선 HttpServletResponse의 역할은 HTTP 응답 메시지를 생성하는 것이다.
HTTP 상태 코드를 지정하고, 헤더를 생성하고, 바디를 생성한다. 편의 기능도 쿠키, Redirect처럼 몇 가지 제공한다.
기본 사용법을 실습을 통해 코드로 알아봤다. 다양한 메서드를 세세하게 설명해주시므로 강의를 참고하면 좋을 것 같다.
기억해야할 부분
캐시를 무효화하는 부분에 대해서 설명해주시는데 이는 기억하면 좋을 것 같다.
이제 HTTP 응답 데이터가 단순 TEXT, HTML일 때를 알아보겠다.
단순 데이터 응답은 writer.println("ok");로 알아보았고, HTML 응답에 대해 알아보겠다. 코드는 아래와 같다. 매우 더럽다.
@WebServlet(name="responseHtmlServlet",urlPatterns = "/response-html")
public class ResponseHtmlServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
res.setContentType("text/html"); //타입 정하기
res.setCharacterEncoding("utf-8"); //인코딩
PrintWriter writer = res.getWriter();//적기 시작. 노가다..
writer.println("<html>");
writer.println("<body>");
writer.println("</body>");
writer.println("</html>");
}
}
위 코드처럼 정말 귀찮게 다 일일이 작성해야 한다.
이제 HTTP 응답 데이터가 JSON 형식일 때를 알아보자. 코드는 아래와 같다.
@WebServlet(name="responseJsonServlet",urlPatterns ="/response-json")
public class ResponseJsonServlet extends HttpServlet {
private ObjectMapper objectMapper=new ObjectMapper();
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
res.setContentType("application/json"); //컨텐트 타입
res.setCharacterEncoding("utf-8"); // 인코딩
HelloData helloData=new HelloData();
helloData.setUsername("kim");
helloData.setAge(20);
String result=objectMapper.writeValueAsString(helloData);
res.getWriter().write(result);
}
}
Jackson 라이브러리가 제공하는 objectMapper.writeValueAsString() 를 사용하면 객체를 JSON 문자로 변경할 수 있다.
정상적으로 JSON 형식으로 응답 데이터가 온 것을 확인할 수 있다.
참고로 application/json 은 스펙상 utf-8 형식을 사용하도록 정의되어 있다.
그래서 스펙에서 charset=utf-8 과 같은 추가 파라미터를 지원하지 않는다. 따라서 application/json 이라고만 사용해야지, application/json;charset=utf-8 이라고 전달하는 것은 의미 없는 파라미터를 추가한 것이 된다.
response.getWriter()를 사용하면 추가 파라미터를 자동으로 추가해버린다.
이때는 response.getOutputStream()으로 출력하면 그런 문제가 없다.
이상으로 포스팅을 마치겠습니다.
댓글