서블릿
Hello 서블릿
- @ServletComponentScan : 해당 패키지와 하위 패키지의 servlet을 모두 찾아서 등록해준다.
- @WebServlet : 서블릿 애노테이션
- name : 서블릿 이름
- urlPatterns : URL 매핑
- HTTP 요청을 통해 매핑된 URL이 호출되면 서블릿 컨테이너는 다음 메서드를 실행한다.
protected void service(HttpServletRequest req, HttpServletResponse resp)
- 개발시 편리하게 HTTP 요청 메시지를 다 보고싶다면? application properties에 다음 문장을 추가하자.
logging.level.org.apache.coyote.http11=debug
참고
운영서버에 모든 요청정보를 다 남기면 성능저하가 생길수 있으므로 개발 단계에서만 적용해야 한다.
서블릿 컨테이너 동작 방식 설명
1. 스프링 부트를 실행하면 스프링부트 자체에 내장되어 있는 톰켓 서버가 띄워진다.
2. 내장 톰켓 서버는 내부에 서블릿 컨테이너 기능을 가지고 있어 서블릿 객체를 전부 생성한다.
3. 웹 브라우저에서 request가 오면 request, response 객체를 생성한다.
4. 컨테이너에서 urlPatterns에 매핑되는 서블릿 객체에 생성된 request, response 객체를 전달한다.
5. 해당 서블릿 객체에서 필요한 작업을 수행하고 종료되면서 생성된 response 객체 정보로 HTTP 응답 메시지를 생성한다.
6. 응답 메시지를 웹 브라우저에 다시 반환해준다.
HttpServletRequest - 개요
HttpServeltRequest의 역할
- HTTP 요청 메시지를 개발자가 직접 파싱하는 것은 매우 불편한 일이다. 서블릿은 개발자가 HTTP 요청 메시지를 편리하게 사용할수 있도록 HTTP 요청 메시지를 파싱해준다.그리고 그 결과를
HttpServletRequest
객체에 담아서 제공한다. - 해당 HTTP 요청이 시작부터 끝날 때 까지 유지되는 임시 저장소 기능
- 저장 :
request.setAttribute(name, value)
- 조회 :
request.getAttribute(name)
- 저장 :
- 세션 관리 기능(로그인 유지)
request.getSession(create: true)
HttpServletRequest - 기본 사용법
request 메시지 StartLine 출력
System.out.println("--- REQUEST-LINE - start ---");
System.out.println("request.getMethod() = " + request.getMethod()); //GET
System.out.println("request.getProtocol() = " + 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-header
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 정보 출력
System.out.println("--- Headers - start ---");
/*
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
System.out.println(headerName + ": " + request.getHeader(headerName));
}
*/
request.getHeaderNames().asIterator()
.forEachRemaining(headerName -> System.out.println(headerName + ": " + request.getHeader(headerName)));
System.out.println("--- Headers - end ---");
System.out.println();
Header 편리한 조회
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();
기타 정보
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 요청 데이터 - 개요
HTTP 요청메시지를 통해 client에서 server로 데이터 전달 방법
GET - 쿼리 파라미터
- url__?username=hello&age=20__
- 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달
- 예) 검색, 필터, 페이징 등에서 많이 사용함
POST - HTML FORM
- content-type: application/x-www-form-urlencoded
- 메시지 바디에 쿼리 파리미터 형식으로 전달 username=hello&age=20
- 예) 회원 가입, 상품 주문, HTML Form 사용
HTTP message body
에 데이터를 직접 담아서 요청- HTTP API에서 주로 사용, JSON, XML, TEXT
- 데이터 형식은 주로 JSON 사용
- POST, PUT, PATC
HTTP 요청 데이터 - GET 쿼리 파라미터
파라미터 조회
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("[단일 파라미터 조회]");
String username = request.getParameter("username");
String age = request.getParameter("age");
System.out.println("username = " + username);
System.out.println("age = " + age);
System.out.println();
System.out.println("[이름이 같은 복수 파라미터 조회]");
String[] usernames = request.getParameterValues("username");
for (String name : usernames) {
System.out.println("username = " + name);
}
System.out.println();
response.getWriter().write("ok");
실행 - 파라미터 전송
http://localhost:8080/request-param?username=hello&age=20
결과
[전체 파라미터 조회] - start
username=hello
age=20
[전체 파라미터 조회] - end
[단일 파라미터 조회]
request.getParameter(username) = hello
request.getParameter(age) = 20
[이름이 같은 복수 파라미터 조회]
request.getParameterValues(username)
username=hello
실행 - 동일 파라미터 전송
http://localhost:8080/request-param?username=hello&username=kim&age=20
결과
[전체 파라미터 조회] - start
username=hello
age=20
[전체 파라미터 조회] - end
[단일 파라미터 조회]
request.getParameter(username) = hello
request.getParameter(age) = 20
[이름이 같은 복수 파라미터 조회]
request.getParameterValues(username)
username=hello
username=kim
복수 파라미터에서 단일 파라미터 조회
username=hello&username=kim
과 같이 파라미터 이름은 하나인데, 값이 중복이면 어떻게 될까?
request.getParameter()
는 하나의 파라미터 이름에 대해서 단 하나의 값만 있을 때 사용해야 한다.
지금처럼 중복일 때는 request.getParameterValues()
를 사용해야 한다.
참고로 이렇게 중복일 때 request.getParameter()
를 사용하면 request.getParameterValues()
의
첫 번째 값을 반환한다.
HTTP 요청 데이터 - POST HTML Form
src/main/webapp/basic/hello-form.html 생성
<!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>
실행해보자.http://localhost:8080/basic/hello-form.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()
로 편리하게 구분없이 조회할 수 있다.
정리하면 request.getParameter()
는 GET URL 쿼리 파라미터 형식
도 지원하고, POST HTML Form 형식
도 둘 다 지원한다.
Postman을 사용한 테스트
이런 간단한 테스트에 HTML form
을 만들기는 귀찮다. 이때는 Postman
을 사용하면 된다.
Postman 테스트 주의사항
- POST 전송시
Body
x-www-form-urlencoded
선택Headers
에서content-type: application/x-www-form-urlencoded
로 지정된 부분 꼭 확인
HTTP 요청 데이터 - API 메시지 바디 - 단순 텍스트
먼저 가장 단순한 텍스트 메시지를 HTTP 메시지 바디에 담아서 전송하고, 읽어보자.
//메시지 body의 내용을 바이트코드로 변환
ServletInputStream inputStream = request.getInputStream();
//바이트 코드를 String으로 변환
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
System.out.println("messageBody = " + messageBody);
response.getWriter().write("ok");
- HTTP 메시지 바디의 데이터를
InputStream
을 사용해서 직접 읽을 수 있다 - inputStream은 byte 코드를 반환한다. byte 코드를 우리가 읽을 수 있는 문자(String)로 보려면 문자표(Charset)를 지정해주어야 한다. 여기서는 UTF_8 Charset을 지정해주었다.
Postman을 사용해서 테스트 해보자.
POST
http://localhost:8080/request-body-string
content-type: text/plain
message body: hello
- 결과:
messageBody = hello
HTTP 요청 데이터 - API 메시지 바디 - JSON
JSON 형식 파싱 추가
- JSON 형식으로 파싱할 수 있게 객체를 하나 생성하자
hello.servlet.basic.HelloData
package hello.servlet.basic;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class HelloData {
private String username;
private int age;
}
private ObjectMapper objectMapper = new ObjectMapper();
@Override
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);
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으로 실행해보자.
- POST http://localhost:8080/request-body-json
- content-type: application/json (Body raw, 가장 오른쪽에서 JSON 선택)
- message body: {"username": "hello", "age": 20}
출력결과
messageBody={"username": "hello", "age": 20}
data.username=hello
data.age=20
참고
JSON 결과를 파싱해서 사용할 수 있는 자바 객체로 변환하려면 Jackson, Gson 같은 JSON 변환 라이브러리를 추가해서 사용해야 한다.
스프링 부트로 Spring MVC를 선택하면 기본으로 Jackson 라이브러리( ObjectMapper )를 함께 제공한다.
참고
HTML form 데이터도 메시지 바디를 통해 전송되므로 직접 읽을 수 있다.
하지만 편리한 파리미터 조회 기능( request.getParameter(...) )을 이미 제공하기 때문에 파라미터 조회 기능을 사용하면 된다.
HttpServletResponse - 기본 사용법
responseHeaderServlet으로 이동
HTTP 응답 데이터 - 단순 텍스트, HTML
//Content-Type: text/html;charset=utf-8
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>");
HTTP
응답으로HTML
을 반환할 때는content-type
을text/html
로 지정해야 한다.
실행
http://localhost:8080/response-html
- 페이지 소스보기를 사용하면 결과
HTML
을 확인할 수 있다.
HTTP 응답 데이터 - API JSON
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//Content-Type: application/json
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
HelloData helloData = new HelloData();
helloData.setUsername("kim");
helloData.setAge(20);
//{"username":"kim","age":20}
String result = objectMapper.writeValueAsString(helloData);
response.getWriter().write(result);
}
HTTP
응답으로 JSON
을 반환할 때는 content-type
을 application/json
로 지정해야 한다.
Jackson
라이브러리가 제공하는 objectMapper.writeValueAsString()
를 사용하면 객체를 JSON
문자로 변경할 수 있다.
'Spring boot > Spring MVC 1' 카테고리의 다른 글
[Spring MVC] 참고. Controller, Service, DAO 필요한 이유 (0) | 2023.02.05 |
---|---|
[Spring MVC] 8. @PathVariable VS @RequestParam (0) | 2023.01.25 |
[Spring MVC] 7. 스프링 MVC - 웹 페이지 만들기 (0) | 2023.01.25 |
[Spring MVC] 6. 스프링 MVC - 기본 기능 (0) | 2023.01.25 |
[Spring MVC] 5. 스프링 MVC - 구조 이해 (0) | 2023.01.25 |
[Spring MVC] 4. MVC 프레임워크 만들기 (0) | 2023.01.25 |
[Spring MVC] 3. 서블릿, JSP, MVC 패턴 (0) | 2023.01.25 |
[Spring MVC] 1. 웹 애플리케이션 이해 (0) | 2023.01.25 |