프로젝트 생성
- Packing을 반드시 War로 설정할 것 → 추후에 JSP를 사용하기 위해 필요
- 인텔리제이가 커뮤니티버전일 경우 build.gradle 파일에 providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' 를 지워야한다.
- Gradle로 바꿔야한다.
📌 ServletApplication 클래스
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);
}
}
- @ServletComponentScan
- 스프링 부트는 서블릿을 직접 등록해서 사용할 수 있도록 애노테이션 형식으로 지원한다.
📌 HelloServlet 클래스
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") // /hello 로 오면 실행이 된다.
public class HelloServlet extends HttpServlet { // 서블릿은 HttpServlet을 상속 받아야 한다.
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// http 요청이 오면 WAS 안에 있는 서블릿 컨테이너가 request, response 객체를 만들어서 서블릿에 던져준다.
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/pain"); // Http 메시지의 헤더 정보에 들어간다.
response.setCharacterEncoding("utf-8"); // Http 메시지의 헤더 정보에 들어간다.
response.getWriter().write("hello " + username); // Http 메시지 바디에 메시지가 들어간다.
}
}
// http://localhost:8080/hello?username=김
// 접속후 출력된 콘솔 메시지
HelloServlet.service
request = org.apache.catalina.connector.RequestFacade@416d1ba
response = org.apache.catalina.connector.ResponseFacade@3e03d467
username = 김
- 서블릿 클래스는 HttpServlet 클래스를 상속 받아야한다.
- HttpServlet은 서블릿이 웹 상에서 HTTP 프로토콜을 이용해 서비스를 처리하기 위해 반드시 상속 받아야하는 클래스다.
- 즉, 모든 서블릿 클래스의 상위 클래스는 HttpServlet 클래스이어야 한다.
- @WebServlet 서블릿 애노테이션
- name : 서블릿 이름
- urlPatterns : URL 매핑
- 해당 URL으로 접근시 service() 메서드가 호출된다.
- HttpServlet 클래스를 상속 받은 후에 인텔리제이 기능인 '컨트롤 + o' 를 눌러서 service 를 친다. 그리고 접근제어어자가 protect 인 service() 메서드를 가져온다.
- protected void service(HttpServletRequest request, HttpServletResponse response)
- request.getParameter() 메서드를 통해 HTTP 요청의 쿼리 파라미터값을 얻는다.
- 단일 파라미터만 가능하다.
- 단계
- 1) @WebServlet으로 이름, 연결 url 지정
- 2) HttpServlet 상속
- 3) protected 접근 지시자를 갖는 service 메서드를 오버라이딩한다.
- 비즈니스 로직 작성
HttpServletRequest 클래스
- Http 프로토콜의 request 정보를 서블릿에게 전달하기 위한 목적으로 사용한다.
- Header 정보, Parameter, Cookie, URI, URL 등의 정볼르 읽어들이는 메서드를 가진 클래스
- JSP(View)로 부터 Controller로 데이터를 보냈을 때, HttpServletRequest 객체 안에 데이터들이 들어가게 된다.
🔍 HttpServletRequest 역할
- 서블릿은 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP 요청 메시지를 파싱한다.
- 그리고 그 결과를 HttpServletRequest 객체에 담아서 제공한다.
- HttpServletRequest를 사용해서 HTTP request message의 다양한 정보를 조회할 수 있다.
- start-line
- headers
- body
- HttpServletRequest 객체는 임시 저장소 기능도 제공한다.
- 해당 HTTP 요청이 시작부터 끝날 떄 까지 유지되는 임시 저장소다.
- 데이터 저장 : request.setAttribute(String name, Object value)
- 데이터 조회 : request.getAttribute(String name)
- 세션 관리 기능도 지원한다.
- request.getSession(creat : true)
✔ 참고
- HttpServletRequest, HttpServleResponse를 사용할 때 가장 중요한 점은 이 객체들이 HTTP 요청 메시지, HTTP 응답 메시지를 편리하게 사용하도록 도와주는 객체라는 점이다.
- 따라서 이 가능에 대해 깊이있는 이해를 하려면 HTTP 스펙이 제공하는 요청, 응답 메시지 자체를 이해해야 한다.
🔍 HttpServletRequest 클래스의 주요 메서드
메서드 | 설명 |
getParameterNames() | 현재 요청에 포함된 매개변수 이름을 열거 형태로 넘겨준다. |
getParameter(String name) | 문자열 name과 같은 이름을 가진 매개변수 값을 가져온다. 반환타입이 String 타입이다. 클라이언트의 HTML 페이지에서 필요한 정보를 얻는데 사용한다. |
getParameterValues(String name) | 문자열 name과 같은 이름을 가진 매개변수 값을 배열 형태로 가져온다. ( 주로 checkbox, mutilple list 등에 사용 ) |
getCookies() | 모든 쿠키 값을 javax.servlet.http.Cookie의 배열 형태로 가져온다. |
getMethod() | 현재 요청이 Get인지, Post인지 파악해서 가져온다. |
getSession() | 현재 세션 객체를 가져온다. |
getRemoteAddr() | 클라이언트의 IP 주소를 알려준다. |
getProtocol() | 현재 서버의 프로토콜을 문자열 형태로 알려준다. |
setCharacterEncoding() | 현재 JSP로 전달되는 내용을 지정한 캐리터셋을로 변환해 준다. HTML 폼에서 한글을 입력할 때 정상적으로 처리하려면 반드시 필요하다. |
getAttribute(String name) | setAttribute() 메서드를 통해 HttpServletRequest 객체 안에 저장된 데이터를 가져온다. 단, setAttribute() 메서드의 속성을 통한 설정이 없으면 null 값을 리턴한다. 반환타입이 Object 타입이기에, 주로 빈 객체나 다른 클래스를 받아올 때 사용된다. |
setAttribute(String name, Object o) | 클라이언트로부터 받은 데이터를 HttpServletRequest 객체안에 데이터를 저장할 때 쓰인다. 데이터를 꺼내올 때는 getAttribute() 메서드를 사용한다. |
🔍 HTTP request message 정보 조회
📌 RequestHeaderServlet 클래스
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.io.IOException;
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, IOException {
printStartLine(request);
// printHeaders1(request);
printHeaders2(request);
printHeaderUtils(request);
printEtc(request);
response.getWriter().write("ok");
}
// 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.getProtocol() = " + request.getProtocol()); // HTTP/1.1
System.out.println("request.getScheme() = " + request.getScheme()); // http
// http://loclahost: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 printHeaders1(HttpServletRequest request) {
System.out.println("--- Headers - start ---");
Enumeration<String> headerNames = request.getHeaderNames();// Http 요청 메시지에 있는 모든 헤더 정보를 다 꺼낸다.
while(headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
System.out.println(headerName + " : " + request.getHeader(headerName));
}
System.out.println("--- Headers - end ---");
System.out.println();
}
// Header 모든 정보 (요즘 방식)
private void printHeaders2(HttpServletRequest request) {
System.out.println("=== Headers - start ===");
request.getHeaderNames().asIterator()
.forEachRemaining(headerName -> System.out.println(headerName + " : " + request.getHeader(headerName)));
System.out.println("=== Headers - end ===");
System.out.println();
}
// 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()); // Accept-Language 중 우선순위가 가장 높은 언어를 반환
System.out.println();
System.out.println("[cookie 편의 조회]");
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
System.out.println("cookie.getValue() = " + 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 ---"); // 기타 정본느 HTTP 메시지 정보가 아니다.
// 요청에 대한 정보 (메시지 정보가 아닌 네트워크 정보)
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-line 정보
// 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.getProtocol() = " + request.getProtocol()); // HTTP/1.1
System.out.println("request.getScheme() = " + request.getScheme()); // http
// http://loclahost: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 printHeaders1(HttpServletRequest request) {
System.out.println("--- Headers - start ---");
Enumeration<String> headerNames = request.getHeaderNames();// Http 요청 메시지에 있는 모든 헤더 정보를 다 꺼낸다.
while(headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
System.out.println(headerName + " : " + request.getHeader(headerName));
}
System.out.println("--- Headers - end ---");
System.out.println();
}
// Header 모든 정보 (요즘 방식)
private void printHeaders2(HttpServletRequest request) {
System.out.println("=== Headers - start ===");
request.getHeaderNames().asIterator()
.forEachRemaining(headerName -> System.out.println(headerName + " : " + request.getHeader(headerName)));
System.out.println("=== Headers - end ===");
System.out.println();
}
✔ Header 편리한 조회
// Hearder 편리한 조회
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()); // Accept-Language 중 우선순위가 가장 높은 언어를 반환
System.out.println();
System.out.println("[cookie 편의 조회]");
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
System.out.println("cookie.getValue() = " + 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 ---"); // 기타 정본느 HTTP 메시지 정보가 아니다.
// 요청에 대한 정보 (메시지 정보가 아닌 네트워크 정보)
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 메시지 정보가 아니다.
HttpServletResponse 클래스
- 서블릿은 HttpServletResponse 객체에 Content Type, 응답코드, 응답 메시지 등을 담아서 전송한다.
🔍 HttpServletRequest 클래스의 주요 메서드
메서드 | 설명 |
setContentType(type) | 문자열 형태의 type에 지정된 MIME Type으로 Content Type을 지정한다. |
setHeader(name, value) | 문자열 name의 이름으로 문자열 value 값을 헤더로 설정한다. |
setDateHeader(name, date) | 문자열 name의 이름으로 date에 설정된 밀리세컨드 시간 값을 헤더에 설정한다. |
sendRedirect(url) | 클라이언트 요청을 다른 페이지로 보낸다. |
🔍 Http 요청 메시지 로그로 확인하기
logging.level.org.apache.coyote.http11=debug
- application.properties 파일에 logging.level.org.apache.coyote.http11=debug 를 적고 서버를 다시 시작한다.
- 그 후 웹 브라우저를 통해 서버에 요청할 경우 서버가 웹 브라우저한테 받은 HTTP 요청 받은 메시지를 출력한다.
- 운영서버에 이렇게 모든 요청 정보를 다 남기면 성능저하가 발생할 수 있으므로 개발 단계에서만 적용한다.
서블릿 동작 원리
🔍 동작 원리
💡 1단계
- 스프링부트가 실행이 되면 내장된 톰켓 서버를 실행시킨다.
- 내장 톰켓 서버는 내부에 서블릿 컨테이너를 가지고 있다.
- 톰켓 서버가내부의 서블릿 컨테이너를 통해 요청 URL에 해당되는 서블릿 객체를 호출한다.
💡 2단계
- HTTP 요청 메시지를 기반으로 생성된 HttpServletRequest, HttpServletResponse 객체를 만든다.
- 생성된 두 객체는 서블릿 객체의 service() 메서드의 파라미터 값으로 넘겨주고 메서드를 실행한다.
- service() 메서드의 로직에 의해 HTTP 응답 메시지(HTTP Response Message)가 작성된다. 그렇게 작성된 HTTP 응답 메시지는 웹 브라우저로 전달된다.
✔ 웹 브라우저가 서버한테 보낸 HTTP 요청 메시지 / 서버가 웹 브라우저한테 보내는 HTTP 응답 메시지
- 참고로 HTTP 응답에서 Content-Length는 웹 애플리케이션 서버가 자동으로 생성해준다.
welcome 페이지 만들기
🔍 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>
📌 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>
👀 참고 자료
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1
스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의
웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., -
www.inflearn.com
https://kimmy100b.github.io/jsp%20servlet/2020/02/13/JSP-Servlet-05/
Servlet 구조, HttpServlet 클래스 그리고 컨텍스트 패스
kimmy100b.github.io
[JAVA] Servlet구조와 HttpServlet 클래스
Servelt 동작 구조 클라이언트 요청에 따라 서블릿 컨테이너는 service() 메서드를 호출하고, service() 메서드는 요청이 GET인지 POST인지 구분하여 각각 doGet(), doPost() 메서드를 호출한다. GET 방식 서버
woojong92.tistory.com
서블릿 구현 및 실행 (web.xml, @WebServlet 설정)
서블릿 구현 및 실행 서블릿 작성 <이클립스 디렉토리 구조> FirstServlet.java package com.edu.test; import java.io.IOException; import javax.servlet.*; import javax.servlet.annotation.WebServlet; impo..
kgvovc.tistory.com
'[ Spring ] > SpringMVC 1편' 카테고리의 다른 글
[Spring] HttpServletResponse / 응답 데이터 전달하는 방법 (0) | 2022.02.15 |
---|---|
[Spring] HttpServletRequest / HTTP 요청 데이터를 얻는 3가지 방법 (0) | 2022.02.15 |
[Spring] 자바 웹 기술 역사 (0) | 2022.02.12 |
[Spring] 서버 사이드 렌더링(SSR), 클라이언트 사이드 렌더링(CSR) (0) | 2022.02.11 |
[Spring] HTML, HTTP API (0) | 2022.02.11 |