2021-05-03글
예제
스프링을 사용하지는 않지만 스프링 부트 프로젝트를 만든다.
서블릿은 톰캣 같은 WAS를 직접 설치하고, 그 위에 서블릿 코드를 클래스 파일로 빌드해서 올린 다음 톰캣 서버를 실행해야 했는데,
스프링 부트는 톰캣 서버를 내장하고 있으니, 이로 진행한다.
보통은 Jar를 선택하지만, JSP를 돌리기 위해 War를 선택한다.
HelloServlet
// 서블릿이 호출되면 이 service가 호출된다.
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.service(req, resp);
System.out.println("HelloServlet");
System.out.println("Request : " + req);
System.out.println("Response : " + resp);
String name = req.getParameter("name");
System.out.println(name);
resp.setCharacterEncoding("utf-8");
resp.getWriter().write("hello" + name);
}
}
HelloServlet
Request : org.apache.catalina.connector.RequestFacade@de1a8e6
Response : org.apache.catalina.connector.ResponseFacade@2b3735f6
amazzi
@WebServlet
: 서블릿 어노테이션@ServletComponentScan
: 스프링이 자동으로 서블릿을 찾아 등록해준다.- HTTP 요청이 오면 서블릿 컨테이너인 WAS가 HTTP 요청, 응답 객체를 서블릿에 던져준다.
req.getParameter()
: 요청에서 해당 Parameter를 가져온다.resp.setCharacterEncoding("utf-8");
: 헤더에 CharacterEncoding을 지정한다.resp.getWriter().write();
:write()
에 바디에 담을 데이터를 넣어준다.
🤔 RequestFacade?
HttpServletRequest는 인터페이스이다.
여러가지 WAS 서버들이 이 인터페이스의 구현체를 구현하고 있어 다양한 WAS를 사용할 수 있는 것이다.
HTTP 요청 메시지 로그로 확인하기
application-properties에 다음을 추가한다.
logging.level.org.apache.coyote.http11=debug
웹 애플리케이션 서버의 요청 응답 구조
HttpServletRequest
서블릿은 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP 요청 메시지를 파싱한다.
그리고 결과를 HttpServletRequest 객체에 담아서 제공한다.
- START LINE
- HTTP 메소드
- URL
- 쿼리 스트링
- 스키마, 프로토콜
- HEADER
- 헤더 조회
- BODY
- form 파라미터의 형식 조회
- message body 데이터 직접 조회
- 부가 기능
- 임시 저장소 기능
- 해당 HTTP 요청이 시작부터 끝날 때 까지 유지되는 임시 저장소 기능
- 저장: request.setAttribute(name, value)
- 조회: request.getAttribute(name)
- 세션 관리 기능
- request.getSession(create: true)
- 임시 저장소 기능
기본 사용
Start Line
request.getMethod() = GET
request.getProtocal() = HTTP/1.1
request.getScheme() = http
request.getRequestURL() = http://localhost:8080/request-header
request.getRequestURI() = /request-header
request.getQueryString() = username=hello
request.isSecure() = false
헤더 정보
request.getHeaderNames().asIterator()
.forEachRemaining(headerName -> System.out.println(headerName + ": " +
request.getHeader(headerName)));
System.out.println("--- Headers - end ---");
System.out.println();
이 외에도 Header 정보를 추출하는 메서드들이 있다.
- request.getServerName()
- request.getServerPort()
- request.getLocale()
- request.getCookies()
- request.getContentType()
- request.getCharacterEncoding()
HTTP 요청 데이터 - GET 쿼리 파라미터
메시지 바디 없이, URL의 쿼리 파라미터를 사용해서 데이터를 전달하는 방법.
예) 검색, 필터, 페이징등에서 많이 사용
http://localhost:8080/request-param?username=hello&age=20
쿼리파라미터는URL에다음과같이 ?를시작으로보낼수있다.추가파라미터는 &로구분하면된다.
RequestParamServlet
/**
* 파라미터 전송 기능
* 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 req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("1. 전체 파라미터 조회");
req.getParameterNames()
.asIterator()
.forEachRemaining(paramName ->
System.out.println(paramName + "=" + req.getParameter(paramName)));
System.out.println("2. 단일 파라미터 조회 (더 많이 쓰는 방식)");
String username = req.getParameter("username");
System.out.println(username);
System.out.println("3. 파라미터 이름이 같은 여러개 값이 있을 경우");
String[] usernames = req.getParameterValues("username");
}
}
HTTP 요청 데이터 - POST HTML Form
메시지 바디에 데이터가 들어가기 때문에 content-type이 있다.content-type : application/x-www-form-urlencoded
바디에 쿼리 파리미터 형식으로 데이터를 전달한다. username=hello&age=20
hello-form.html 일부
<form action="/request-param" method="post">
username: <input type="text" name="username"/> age: <input type="text" name="age"/>
<button type="submit">전송</button>
</form>
이전에 만들었던 requestParamServlet에 요청을 보내면,
다음과 같은 결과를 얻는다!
그런데 requestParamServlet는 쿼리 파라미터만 조회하는데 ?
application/x-www-form-urlencoded 형식은 쿼리 파라미터 형식과 같다.
따라서 쿼리 파라미터 조회 메서드를 그대로 사용하면 된다.
서버 입장에서는 둘의 형식이 동일하므로, request.getParameter() 로 편리하게 구분없이 조회할 수 있다.
request.getParameter() 는 GET URL 쿼리 파라미터 형식도 지원하고, POST HTML Form 형식도 둘 다 지원한다.
💡 POSTMAN으로 테스트할 경우
Form 데이터를 입력하기 귀찮은데, 이때 Content-type을 application/x-www-form-urlencoded
로 설정한다.
HTTP 요청 데이터 - API 메시지 바디
HTTP message body에 데이터를 직접 담아서 요청한다.
HTTP API에서 주로 JSON을 사용한다.
단순 텍스트
RequestBodyStringServlet - Raw한 String
@WebServlet(name = "requestBodyStringServlet", urlPatterns = "/request-body-string")
public class RequestBodyStringServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream inputStream = req.getInputStream(); // byte 코드를 얻음
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); // 스프링이 제공하는 유틸리티
}
}
HTML form도 이렇게 조회할 수 있지만 번거로우니 쿼리 파라미터 조회를 사용하자!
JSON
- content-type: application/json
HelloData
@Getter
@Setter
public class HelloData {
private String userName;
private int age;
}
RequestBodyJsonServlet
@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(); // byte 코드를 얻음
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); // 스프링이 제공하는 유틸리티
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
}
}
JSON 결과를 파싱해서 객체로 변환하려면 Jackson, Gson 같은 JSON 변환 라이브러리를 추가해서 사용해야 한다.
스프링 부트로 Spring MVC를 선택하면 기본으로 Jackson 라이브러리( ObjectMapper)를 제공한다.
HTTPServletResponse - 기본 사용법
개발자가 직접 응답 객체를 만들기는 번거롭지 않게 서블릿이 제공해준다!
- HTTP 응답코드 지정
- 헤더 생성
- 바디 생성 등등...
ResponseHeaderServlet
@WebServlet(name = "responseHeaderServlet", urlPatterns = "/response-header")public class ResponseHeaderServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //[status-line] 상태코드 지정 resp.setStatus(HttpServletResponse.SC_OK); //200 //[response-headers] 응답 헤더 지정 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"); //[Header 편의 메서드] content(response); cookie(response); redirect(response); //[message body] 응답 바디 지정 PrintWriter writer = resp.getWriter(); writer.println("ok"); //Content-Type: text/plain;charset=utf-8 //Content-Length: 2 //response.setHeader("Content-Type", "text/plain;charset=utf-8"); resp.setContentType("text/plain"); resp.setCharacterEncoding("utf-8"); //response.setContentLength(2); //(생략시 자동 생성) }}
쿠키 설정하기
//Set-Cookie: myCookie=good; Max-Age=600; response.setHeader("Set-Cookie", "myCookie=good; Max-Age=600"); // 또는 Cookie cookie = new Cookie("myCookie", "good"); cookie.setMaxAge(600); //600초response.addCookie(cookie);
Redirect 설정하기
//Status Code 302//Location: /basic/hello-form.htmlresponse.setStatus(HttpServletResponse.SC_FOUND); //302response.setHeader("Location", "/basic/hello-form.html");// 또는response.sendRedirect("/basic/hello-form.html");
HTTP 요청 데이터
단순 텍스트, HTML
HttpServletResponse
@WebServlet(name = "responseHtmlServlet", urlPatterns = "/response-html")
public class HttpServletResponse extends HttpServlet {
@Override
protected void service(HttpServletRequest req, javax.servlet.http.HttpServletResponse resp) throws ServletException, IOException {
// Content-Type: text:html; charset=utf-8
resp.setContentType("text:html");
resp.setCharacterEncoding("utf-8");
PrintWriter writer = resp.getWriter();
writer.println("<html>");
writer.println("<body>");
writer.println(" <div>이건 아니지</div>");
writer.println("</body>");
writer.println("</html>");
}
}
간단하지만, HTML을 일일히 작성해주어야 한다?
API JSON
ResponseJsonServlet
@WebServlet(name = "responseJsonServlet", urlPatterns = "/response-json")
public class ResponseJsonServlet extends HttpServlet {
private final 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("mazzi");
data.setAge(100);
// JSON으로 변환
String result = objectMapper.writeValueAsString(data);
resp.getWriter().write(result);
}
}
(사실 스프링 쓰면 이렇게 길어지지도 않는다.)
✍️ 김영님의 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 강의 노트 ✍️
'스프링 부트' 카테고리의 다른 글
📋 Spring MVC - 4. MVC 프레임워크 만들기 (0) | 2021.08.06 |
---|---|
📋 Spring MVC - 3. 서블릿, JSP, MVC 패턴 (0) | 2021.08.06 |
@Valid와 @Validated (0) | 2021.08.06 |
@JsonProperty, @JsonNaming (0) | 2021.08.06 |
📋 Spring MVC - 1. 웹 서버, 웹 애플리케이션 서버 (0) | 2021.08.06 |