프로젝트 생성
JSP 실행하기 위해서 WAR 패키징 선택
Hello 서블릿
스프링부트 환경에서 서블릿 등록, 사용하기
- 서블릿 등록
@WebServlet(name="helloServlet", urlPatterns = "/hello") //서블릿 이름, url 매핑
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet.service");
System.out.println("req = " + req);
System.out.println("resp = " + resp);
String username = req.getParameter("username"); //url에 get 방식으로 ?username=kim 입력 시
System.out.println("username = " + username); //콘솔 출력됨
resp.setContentType("text/plain");
resp.setCharacterEncoding("utf-8");
resp.getWriter().write("hello " + username); //메시지 바디에 작성
}
}
콘솔
HelloServlet.service
req = org.apache.catalina.connector.RequestFacade@793223aa
resp = org.apache.catalina.connector.ResponseFacade@3304d8f
username = 김
※ HTTP 요청 메시지 => 로그로 확인하기
application.properties
logging.level.org.apache.coyote.http11=debug
콘솔
2023-08-13 19:54:40.435 DEBUG 6360 --- [nio-8080-exec-1] o.a.coyote.http11.Http11InputBuffer : Received [GET /hello?username=%EA%B9%80 HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Not/A)Brand";v="99", "Google Chrome";v="115", "Chromium";v="115"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36
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: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6
=> 운영 서버에서는 요청 정보 다 남기면 성능 저하 발생 가능
∴ 개발 단계에서만 적용하자
- 서블릿 컨테이너 동작 방식
- welcome 페이지 추가
main/webapp/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li><a href="basic.html">서블릿 basic</a></li>
</ul>
</body>
</html>
basic.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li>hello 서블릿
<ul>
<li><a href="/hello?username=servlet">hello 서블릿 호출</a></li>
</ul>
</li>
<li>HttpServletRequest
<ul>
<li><a href="/request-header">기본 사용법, Header 조회</a></li>
<li>HTTP 요청 메시지 바디 조회
<ul>
<li><a href="/request-param?username=hello&age=20">GET -
쿼리 파라미터</a></li>
<li><a href="/basic/hello-form.html">POST - HTML Form</a></
li>
<li>HTTP API - MessageBody -> Postman 테스트</li>
</ul>
</li>
</ul>
</li>
<li>HttpServletResponse
<ul>
<li><a href="/response-header">기본 사용법, Header 조회</a></li>
<li>HTTP 응답 메시지 바디 조회
<ul>
<li><a href="/response-html">HTML 응답</a></li>
<li><a href="/response-json">HTTP API JSON 응답</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</body>
</html>
HttpServletRequest - 개요
- HttpServletRequest 역할
서블릿 => 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 HTTP 요청 메시지를 파싱, HttpServletRequest 객체에 담아서 제공
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 요청 시작 ~ 끝날 때 까지 유지되는 임시 저장소 기능
저장 : request.setAttribute(name, value)
조회 : request.getAttribute(name)
- 세션 관리 기능
request.getSession(create: true)
HttpServletRequest - 기본 사용법
- StartLine 정보
@WebServlet(name = "RequestHeaderServlet", urlPatterns = "/request-header")
public class RequestHeaderServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
printStartLine(request);
}
private static void printStartLine(HttpServletRequest request) {
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();
}
}
- 출력
--- REQUEST-LINE - start ---
request.getMethod() = GET
request.getProtocol() = HTTP/1.1
request.getScheme() = http
request.getRequestURL() = http://localhost:8080/request-header
request.getRequestURI() = /request-header
request.getQueryString() = null
request.isSecure() = false
--- REQUEST-LINE - end ---
- 헤더 정보
private void printHeaders(HttpServletRequest request) {
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();
}
- 출력
--- Headers - start ---
host: localhost:8080
connection: keep-alive
cache-control: max-age=0
sec-ch-ua: "Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
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: none
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
accept-encoding: gzip, deflate, br
accept-language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6
--- Headers - end ---
- 헤더 유틸 정보
//Header 편리한 조회
private void printHeaderUtils(HttpServletRequest request) {
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();
}
- 출력
--- Header 편의 조회 start ---
[Host 편의 조회]
request.getServerName() = localhost
request.getServerPort() = 8080
[Accept-Language 편의 조회]
locale = ko_KR
locale = ko
locale = en_US
locale = en
locale = ja
request.getLocale() = ko_KR
[cookie 편의 조회]
[Content 편의 조회]
request.getContentType() = null
request.getContentLength() = -1
request.getCharacterEncoding() = UTF-8
--- Header 편의 조회 end ---
- 기타 정보
(HTTP 메시지 정보는 아님)
네트워크, 서버 관련 정보
//기타 정보
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();
}
- 출력
--- 기타 조회 start ---
[Remote 정보]
request.getRemoteHost() = 0:0:0:0:0:0:0:1
request.getRemoteAddr() = 0:0:0:0:0:0:0:1
request.getRemotePort() = 54305
[Local 정보]
request.getLocalName() = localhost
request.getLocalAddr() = 0:0:0:0:0:0:0:1
request.getLocalPort() = 8080
--- 기타 조회 end ---
HTTP 요청 데이터 - 개요
- HTTP 요청 메시지 통한 클라이언트 → 서버 데이터 전달 방식
- GET - 쿼리 파라미터
/url?username=hello&age=20
메시지 바디 없이, URL의 쿼리 파라미터에 데이터 포함해 전달
ex) 검색, 필터, 페이징
- POST - HTML Form
POST /save HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
username=hello&age=20
메시지 바디에 쿼리 파리미터 형식으로 전달
예) 회원 가입, 상품 주문 등
- HTTP message body에 직접 데이터 담아 요청
HTTP API에서 주로 사용
JSON, XML, TEXT 형식 데이터
POST, PUT, PATCH
HTTP 요청 데이터 - GET 쿼리 파라미터
주소?username=hello&pass=1234
@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("단일 파라미터 조회 - start");
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("username = " + username);
System.out.println("password = " + password);
System.out.println("단일 파라미터 조회 - end");
//하나의 이름에 여러 값 있을 때 해당 값들 모두 출력 가능
System.out.println("이름이 같은 복수 파라미터 조회 - start");
String[] usernames = req.getParameterValues("username");
for (String name : usernames){
System.out.println("username = " + name);
}
System.out.println("이름이 같은 복수 파라미터 조회 - end");
}
}
//
전체 파라미터 조회 - start
username = hello
password = 1234
전체 파라미터 조회 - end
단일 파라미터 조회 - start
username = hello
password = 1234
단일 파라미터 조회 - end
이름이 같은 복수 파라미터 조회 - start
username = hello
username = hello2
이름이 같은 복수 파라미터 조회 - end
HTTP 요청 데이터 - POST HTML form
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>
요청 URL: http://localhost:8080/request-param
content-type: application/x-www-form-urlencoded
message body: username=hello&age=20
- content-type: application/x-www-form-urlencoded
GET 쿼리 파라미터 형식과 동일
=> 쿼리 파라미터 조회 메소드 그대로 사용하면 됨 (req.getParameter())
(클라이언트 입장에서는 차이 잇으나 서버 입장에서는 두 형식이 동일함)
※ content-type
HTTP 메시지 바디의 데이터 형식 지정
GET URL 쿼리 파라미터 형식 => 메시지 바디 사용 안 하므로 content-type 없음
HTTP 요청 데이터 - API 메시지 바디 - 단순 텍스트
RequestBodyStringServlet
@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(); //메시지 바디를 바이트코드로 가져옴
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); //String 변환
System.out.println("messageBody = " + messageBody);
resp.getWriter().write("ok"); //응답으로 ok 받기
}
}
HTTP 요청 데이터 - API 메시지 바디 - JSON
POST http://localhost:8080/request-body-json
content-type: application/json
message body: {"username": "hello", "age": 20}
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();
//Jackson 라이브러리 => JSON 결과 파싱 -> 자바 객체로 변환
@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.getUsername() = " + helloData.getUsername());
System.out.println("helloData.getAge() = " + helloData.getAge());
resp.getWriter().write("ok");
}
}
※ HTML 폼 데이터도 메시지 바디 통해 전송되므로 inputstream으로 직접 읽을 수 있음
but request.getParameter()으로 더 편리하게 조회 가능함
HttpServeltResponse - 기본 사용법
HTTP 응답 코드 지정, 헤더 생성, 바디 생성
편의 기능 - Content-Type, 쿠키, Redirect
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);
//reponse-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(resp);
cookie(resp);
redirect(resp);
// message body
PrintWriter writer = resp.getWriter();
writer.print("ok");
}
private void content(HttpServletResponse response) {
//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");
cookie.setMaxAge(600); //600초
response.addCookie(cookie); /////
}
private void redirect(HttpServletResponse response) throws IOException {
//Status Code 302
//Location: /basic/hello-form.html
//response.setStatus(HttpServletResponse.SC_FOUND); //302
//response.setHeader("Location", "/basic/hello-form.html");
response.sendRedirect("/basic/hello-form.html");
}
}
HTTP 응답 데이터 - 단순 텍스트, HTML
HTTP 응답 데이터 종류 - 단순 텍스트 응답 (writer.print("ok")) / HTML 응답 / Messsage Body JSON 응답)
ResponseHtmlServlet
@WebServlet(name = "responseHtmlServlet", 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 writer = resp.getWriter();
writer.println("<html>");
writer.println("<body>");
writer.println(" <div>안녕?</div>");
writer.println("</body>");
writer.println("</html>");
}
}
HTTP 응답 데이터 - API JSON
응답으로 JSON 반환 시 content-type : application/json 지정
→ objectMapper.writeValueAsString()으로 자바 객체에서 JSON 변환
ResponseJsonServlet
@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 {
resp.setContentType("application/json"); ////
// resp.setCharacterEncoding("UTF-8");
HelloData helloData = new HelloData();
helloData.setUsername("park");
helloData.setAge(22);
//java object -> string 변환
String result = objectMapper.writeValueAsString(helloData); ////
resp.getWriter().write(result);
}
}
※
application/json => utf-8 사용하도록 정의되어 있어 파라미터 따로 작성 않아도 됨
but response.getWriter() 사용시 추가 파라미터 자동 추가함
=> response.getOutputStream()으로 출력하면 됨
'Programming > 스프링' 카테고리의 다른 글
[김영한 스프링 MVC 1] MVC 프레임워크 만들기 (0) | 2023.09.24 |
---|---|
[김영한 스프링 MVC 1] 서블릿, JSP, MVC 패턴 (0) | 2023.09.17 |
[김영한 스프링 MVC 1] 웹 애플리케이션 이해 (0) | 2023.08.06 |
[스프링] 컨트롤러 파라미터 어노테이션 - 클라이언트 → 서버 (0) | 2023.08.05 |
김영한 ORM 표준 JPA 프로그래밍 (기본) - 엔티티 매핑 (0) | 2022.10.27 |