📌 프로젝트 생성 사이트
주의 Jar❌ War🅾️
➡️ 실행 속도가 더 빨라짐 요 설정을 따라해주세요.
준비 완료 !
개발은 꼬옥 따라해보자 !!
이 챕터는 실습 내용을 올린 것이라 별 내용은 없다 ^^; (이실별)
: main이니 이걸 실행시켜보자.
package hello.servlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@ServletComponentScan // 서블릿 자동 등록
@SpringBootApplication
public class ServletApplication {
public static void main(String[] args) {
SpringApplication.run(ServletApplication.class, args);
}
}
⬇️ 실행 후 접속
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 {
System.out.println("HelloServlet.service");
System.out.println("request = " + request);
System.out.println("response = " + response);
String username = request.getParameter("username");
System.out.println("username = " + username);
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().write("hello " + username);
}
}
@WebServlet
서블릿 애노테이션name: 서블릿 이름
urlPatterns: URL 매핑이것들은 중복이 있음 안 됨 !!
⬇️ (톰캣 관련)
HelloServlet.service
request = org.apache.catalina.connector.RequestFacade@3b7952fe
response = org.apache.catalina.connector.ResponseFacade@574015ea
⬇️ 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>
<li><a href="/basic/hello-form.html">POST - HTML Form</a></
<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>
⬇️ 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>
: HTTP 요청 메시지를 개발자가 직접 파싱해 사용해도 되지만 매우 불편하다.
서블릿은 개발자가 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신 HTTP 요청 메시지를 파싱한다. 그리고 그 결과를 HttpServletRequest
객체에 담에 제공한다.
➡️ HttpServletRequest
를 사용하면 이런 http 요청 메시지를 편리하게 조회할 수 있다.
또한 여러가지 부가 기능도 함께 제공한다.
✔️ 임시 저장소 기능
: 해당 HTTP 요청의 시작부터 끝(생존 범위)까지 유지되는 임시 저장소
저장: request.setAttribute(name, value)
조회: request.getAttribute(name)
➡️ 이렇게 값을 넣고 꺼낼 수 있음
✔️ 세션 관리 기능
(로그인 했다 안했다 상태를 유지)
request.getSession(create: true)
📌
HttpServletRequest
,HttpServletResponse
를 사용할 때 이 객체들이 HTTP 요청 메시지, HTTP 응답 메시지를 편리하게 사용하도록 도와주는 객체라는 점을 기억하자 !!따라서, 이 기능에 대해 깊이있게 이해하게 위해서는 HTTP 스펙이 제공하는 요청, 응답 메시지 자체를 이해해야 한다.
이실별2 '^';
✔️ RequestHeaderServlet.java
package hello.servlet.basic.request;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
@WebServlet(name = "requestHeaderServlet", urlPatterns = "/request-header")
public class RequestHeaderServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException {
printStartLine(request);
printHeaders(request);
printHeaderUtils(request);
printEtc(request);
}
// start line 정보
private void printStartLine(HttpServletRequest request) {
System.out.println("--- REQUEST-LINE - start ---");
System.out.println("request.getMethod() = " + request.getMethod()); //GET
System.out.println("request.getProtocal() = " + 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-test
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 모든 정보
private void printHeaders(HttpServletRequest request) {
System.out.println("--- Headers-LINE - start ---");
/*
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
System.out.println(headerName + ": " + headerName);
}
*/
// 이렇게 하면 38-44 주석처리 한 줄과 같은 내용을 간단하게 할 수 있음.
request.getHeaderNames().asIterator()
.forEachRemaining(headerName -> System.out.println(headerName + ": " + headerName));
System.out.println("--- Headers-LINE - 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();
}
// 기타정보
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();
}
}
HTTP 요청 메시지를 통해 클라이언트 → 서버 데이터 전달 방법
GET - 쿼리 파라미터
ex. /url?username=hello&age=20
메시지 바디 없이 URL 쿼리 파라미터에 데이터를 포함해 전달하는 방법이다.
POST - HTML Form
바디에 대한 설명: content-type: application/x-www-form-urlencoded
메시지 바디에 쿼리 파라미터 형식으로 전달한다. (쿼리 파라미터와 비슷하게 생겼기 때문에 조회할 때 호환이 가능하다.)
HTTP message body에 직접 데이터를 담아 요청
HTTP API에서 주로 사용하며 JSON, XML, TEXT와 같은 것들인데 주로 JSON을 사용한다.
검색, 필터, 페이징 등에서 많이 사용하는 방식이다.
쿼리 파라미터는 ?를 시작으로 보내며 &를 통해 추가 파라미터를 붙일 수 있다.
ex. http://localhost:8080/request-param?username=hello&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;
import java.util.Enumeration;
/*
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 {
System.out.println("[전체 파라미터 조회] - start");
// request.getParameter()로 키 값을 꺼내는 것임
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> System.out.println(paramName + "=" + request.getParameter(paramName)));
System.out.println("[전체 파라미터 조회] - end");
System.out.println();
System.out.println("[단일 파라미터 조회]");
// request.getParameter() 해놓고 option+enter 하면 자동 완성
String username = request.getParameter("username");
String age = request.getParameter("age");
System.out.println("username = " + username);
System.out.println("age = " + age);
System.out.println();
// http://localhost:8080/request-param?username=hello2&age=20&username=hello2
System.out.println("[이름이 같은 복수 파라미터 조회]");
String[] usernames = request.getParameterValues("username");
for (String name : usernames) {
System.out.println("username = " + name);
}
response.getWriter().write("ok");
}
}
📌 복수 파라미터에서 단일 파라미터 조회
파라미터 이름은 하나인데 값이 중복일땐 어떻게 될까?
request.getParameter()
는 하나의 파라미터 이름에 대해 단 하나의 값만 있을 때 사용해야 한다.
값이 중복일 때는 request.getParameterValues()
를 사용하면 된다.
참고로 중복일 때 request.getParameter()
를 사용하면 request.getParameterValues()
의 첫번째 값을 반환한다.
: 웹 브라우저가 HTTP 메시지를 생성해 요청
(회원 가입, 상품 주문 등에서 주로 사용)
📌 특징
- content-type: application/x-www-form-urlencoded
- 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달 (→ 그래서 호환 가능)
<!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>
➡️ 위에서 쿼리문과 형태가 똑같아서 호환이 가능하다고 했다. 즉, 이렇게 GET 요청과 같이 request.getParameter()
, request.getParameterValues()
로 값을 꺼낼 수 있다.
GET 쿼리 파라미터 형식과 같아 쿼리 파라미터 조회 메소드를 그대로 사용
클라이언트 입장에서 두 방식에 차이가 있긴 하지만, 서버 입장에서는 둘의 형식이 같으므로 request.getParameter()
로 편리하게 조회가 가능하다.
➡️ request.getParameter()
는 GET URL쿼리 파라미터 형식, POST HTML Form 형식 둘 다 지원
📌 참고
- GET URL 쿼리 파라미터 형식은 클라이언트에서 서버로 데이터를 전달할 때 HTTP 메시지 바디를 사용하지 않아 content-type이 없다
- POST HTML Form 형식은 데이터를 전달할 때 HTTP 메시지 바디에 해당 데이터를 포함해 보내기 때문에 바디에 포함된 메시지가 어떤 형식인지 content-type을 꼭 지정해야 한다.
이런 방식을application/x-www-form-urlencoded
이라고 한다.
🤷🏻 위에서는 HTML form을 이용했으나 이런 단순한 테스트를 위해 앞으로 계속 html을 만들긴 너무 귀찮지 않아?
Postman을 이용하자 !!
: HTTP message body에 데이터를 직접 담아 요청하기
HTTP API에서 주로 사용하며 JSON, XML, TEXT 형식으로 보낼 수 있지만 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 {
@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);
response.getWriter().write("ok");
}
}
➡️ InputStream
으로 읽을 수 있다.
(InputStream
은 byte 코드를 반환한다. 우리는 이대로 읽을 수 없으므로 UTF-8로 지정해주어 변환하였다.)
그런데 보통은 json 형식으로 주고 받지 이렇게 주고받진 않는다. 바로 JSON 형식으로 주고 받는 것을 배워보자.
: HTTP API에서 주로 사용하는 JSON 형식으로 데이터 전달하기
JSON 형식 전송
POST
http://localhost:8080/request-body-json
content-type
: application/json
message body
: {"username": "hello", "age": 20}
결과
: messageBody = {"username": "hello", "age": 20}
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 {
private ObjectMapper objectMapper = new ObjectMapper();
// ser만 쳐도 알아서 option+enter로 생성됨
@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");
}
}
➡️ lombok 제공 @Getter
, @Setter
로 눈에 보이지 않게 코드가 추가되어 사용 가능하다.
📌
JSON 결과를 파싱해 사용할 수 있는 자바 객체로 변환하려면 Jackson, Gson 같은 JSON 변환 라이브러리를 추가해 사용해야 한다.
스프링 부트로 Spring MVC를 선택하면 기본으로 Jackson 라이브러리 (ObjectMapper
)를 함께 제공한다.
📌
HTML form 데이터도 메시지 바디를 통해 전송되므로 직접 읽을 수 있다.
하지만 편리한 파라미터 조회 기능(request.getParameter(...)
)을 이미 제공하기 때문에 파라미터 조회 기능을 사용하면 된다.