월루를 꿈꾸는 대학생
[MVC-1] 서블릿 본문
서블릿
- http 요청을 어떻게 받고 http응답을 어떻게 내려줄 것인지 개발자가 편하게 해주도록 서버에서 구현해준 것
프로젝트 생성
gradle
lomnbok
hello 서블릿
스프링 부트 서블릿 환경 구성
@SpringBootApplication
@ServletComponentScan // 스프링이 내 패키지 하위부터 싹 훝어서 서블릿 자동등록 해줌 public class ServletApplication {
public static void main(String[] args) {
SpringApplication.run(ServletApplication.class, args);
}
}
/hello http요청이 오면 request, response 객체를 was에서 만들어주고 이걸 서블릿에 전달해줌
서블릿은 service 메서드를 호출함
@WebServlet(name="helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
// 서블릿이 호출되면 service메서드가 호출 됨
@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);
// 서블릿은 쿼리 파라미터를 쉽게 읽을 수 있음 ex localhost:8080/hello?username=choi String username = request.getParameter("username");
System.out.println("username = " + username);
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().write("hello "+ username);
}
}
서버에서 받은 로그
HelloServlet.service
request = org.apache.catalina.connector.RequestFacade@5fd594b3
response = org.apache.catalina.connector.ResponseFacade@36b54a34
username = choi
웹에서 확인
- 보면 response 객체에 설정한 것 처럼 타입이 잘 설정되어 있고 웹 화면도 내가 원한 출력 결과물이 있는 거 확인 가능
- 해당 클래스가 호출되는 경우 보니까 service가 자동 호출 되었음 왜 ?
참고
https://devraphy.tistory.com/495>
또 보니까 @WebServlet이 발전된게 getmapping, postmapping 인듯
서블릿 작동원리
- 스프링부트 실행시 내장된 톰켓 서버 was 실행
- 톰캣 실행되면 ServletApplication위에 붙었던 애노테이션 @ServletComponentScan이 동작
- @ServletComponentScan이 Servlet Container에 스캔한 서블릿 객체들을 등록
- client에서 request가 들어오면 was에서 HttpServeletRequest객체를 만들어서 서블릿 객체에게 넘겨줌
- 서블릿 객체는 오버라이딩 되어 있는 service메서드를 호출하고 로직 수행
- 로직 수행 결과를 HttpServletResponse에 담아서 클라이언트에 전달해줌!!!
http 요청 메세지 로그로 확인
- 요청 메세지들 전부 로그 확인이 가능해짐
- application.properties에다가 설정 추가
logging.level.org.apache.coyote.http11=debug
- 다만 이거는 그냥 확인용 운영서버에 이만큼 남기면 성능 저하
서블릿 컨테이너 동작 방식 설명
내장 톰캣 서버 생성
- 스프링부특가 기동하면 was가 실행되고 Was는 스캔된 서블릿 객체들을 서블릿 컨테이너에 등록한다
- 다음과 같이 http 요청이 오는 경우 해당 요청을 서블릿 컨테이너에 등록된 서블릿 객체에게 전달해줌
부가적인 정보들은 http 응답할 때 was가 알아서 만들어서 생성해줌
webapp폴더 만글고 ==index.html== 파일 넣으니까 알아서 welcome page로 등록되나 보네 ;;
HttpServletReqeust - 개요
서블릿이 없으면 http 요청 메세지를 파싱하고 써야하는데
서블릿이 해당 작업을 대신 해주고 그 결과를 HttpServeletReqeust객체에 담아서 제공해줌
http 요청 메세지
POST /save HTTP/1.1 ##START LINE
Host: localhost:8080. ##HEADER
Content-Type: application/x-www-form-urlencoded ##HEADER
username=kim&age=20 ##BODY
START LINE
HTTP 메소드
URL
쿼리 스트링
스키마, 프로토콜
헤더
헤더 조회
바디
form 파라미터 형식 조회
message body 데이터 직접 조회
임시 저장소 기능
- http 요청 시작-끝 동안 유지되는 임시 저장소
- 저장 : request.setAttribute(name, value)
- 조회 : request.getAttribute(name)
- 생각보다 많이 쓰게 됨
중요
결국 해당 객체들을 사용하는 것은 http 요청 , 응답 메세지를 편리하게 사용하게 해주는 것임
깊이 있게 이해하려면 http 스펙이 제공하는 요청 , 응답 메세지를 이해해야함
HttpServletRequest - 기본 사용법
딱히 특별한 거 없음
HTTP요청 데이터 - 개요
- GET - 쿼리 파라미터
- ?username=hello&age=20
- 메세지 바디없이 url에다가 데이터 포함해서 전달
- ex 검색 , 필터 ,페이징 등
- POST -HTML Form
- content-type: application/x-www-form-urlencoded
- 메시지 바디에 쿼리 파리미터 형식으로 전달 username=hello&age=20
- 예) 회원 가입, 상품 주문, HTML Form 사용
- HTTP message body에 데이터 직접 담아서 전달
1. API에 주로 사용 JSON 2. POST , PUT ,PATCH
HTTP요청 데이터 - GET 쿼리 파라미터
- url에다가 데이터 넣어서 전송
- url 뒤에 ? 붙여서 작성하고 구분은 &으로 한다
@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("RequestParamServlet.service");
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("age = " + age);
System.out.println("username = " + username);
System.out.println();
//http://localhost:8080/request-param?username=hello&age=20&username=hello2
System.out.println("이름이 같은 복수 파라미터 조회");
String[] usernames = request.getParameterValues("username");
for (String name : usernames){
System.out.println("usernmae = "+ name);
}
response.getWriter().write("ok");
}
}
출력
RequestParamServlet.service
전체 파라미터 조회 - start
username = hello
age = 20
전체 파라미터 조회 - end
단일 파라미터 조회
age = 20
username = hello
이름이 같은 복수 파라미터 조회
usernmae = hello
usernmae = hello2
복수 파라미터에 값이 세팅되어 있는 경우
?username=hello&age=20&username=hello2
request.getParametersValues() 사용해서 배열로 빼내면 됨
HTTP 요청 데이터 POST HTML Form
- 서버로 데이터를 전송하는 회원가입 혹은 상품 주문에서 사용함
- content-type: application/x-www-form-urlencoded
- 메시지 바디에 쿼리 파리미터 형식으로 데이터를 전달 ex username=hello&age=20
application/x-www-form-urlencoded 형식은 앞에 GET방식이랑 거의 동일하니까 메서드도 같은 거 써서 정보를 조회 가능
클라이언트 입장에서는 차이가 있지만 서버 입장에서는 받는 형식이 같아서 ㄱㅊ
HTTP 요청 데이터 - API 메세지 바디 - 단순 텍스트
- HTTP message body에 데이터 직접 담아서 보냄 JSON
- POST, PUT, PATCH
- HTTP 메세지 바디의 데이터를 InputStream 사용해서 읽기 가능
@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();
// 바이트 스트림을 스트링으롤 utf-8인코딩
String messageBody = StreamUtils.copyToString(inputStream,
StandardCharsets.UTF_8);
System.out.println("messageBody = " + messageBody);
response.getWriter().write("ok");
}
}
request 온 거를 inputStream으로 받은 다음 그걸 byte코드로 변환
변환된 byte 코드를 읽을 수 있는 문자열로 utf-8로 인코딩해서 출력
HTTP 요청 데이터 - API 메세지바디 JSON
- content-type : application/json
보통 json을 그냥 받아서 쓰는게 아니라 받은 데이터를 토대로 객체를 만들어서 코드 내에서 사용
@WebServlet(name = "requestBodyJsonServlet", urlPatterns = "/request-body-json")
public class RequestBodyJsonServlet extends HttpServlet {
// 스프링이 기본으로 제공해주는 json 라이브러리 Jackson
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);
// json 문자열 값을 읽은 후에 helloData에 맞게 매핑해주나보네
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");
}
}
JSON 데이터 읽은 다음 데이터 클래스에 맞게 읽기 위해서 스프링에서 제공해주는 Jakcson라이브러리의 ObjectMapper를 사용해서 클래스의 멤버변수를 뽑아서 읽고 사용 가능
HttpServletResponse - 기본 사용법
역할
- HTTP 응답 메세지 생성
- HTTP 응답 코드 지정
- 헤더 생성
- 바디 생성
그냥 200 넣어도 되는데 이미 자바 코드에서 만들어둔 거 있으니까 가시성도 있고하니 이거 쓰자
@WebServlet(name = "responseHeaderServlet", urlPatterns = "/response-header")
public class ResponseHeaderServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//200 써도 되는데 의미있는 코드로 쓰자
//[status-line]
response.setStatus(HttpServletResponse.SC_OK);
//[response-headers]
response.setHeader("Content-Type", "text/plain;charset=utf-8");
// cache 무효화
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
// 그냥 내가 원하는 임의 헤더 추가 가능
response.setHeader("my-header","hello");
// 위에서 처럼 그냥 setHeader 메서드로 지정해도 되는데 밑에 편의 메서드 써도 동일하게 설정 가능하다
//[Header 편의 메서드]
content(response);
cookie(response);
redirect(response);
//[message body]
PrintWriter writer = response.getWriter();
writer.println("ok");
}
private void content(HttpServletResponse response) {
//Content-Type: text/plain;charset=utf-8
//Content-Length: 2 //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");
// 302세팅하고 리다이렉트할 페이지 넣어주면 됨
response.sendRedirect("/basic/hello-form.html");
}
}
content
타입이랑 인코딩 할 때
response.setContentType("text/plain"); response.setCharacterEncoding("utf-8");
쿠키 관련
Cookie cookie = new Cookie("myCookie", "good"); cookie.setMaxAge(600); //600초 response.addCookie(cookie);
redirect편의 메서드
response.sendRedirect("/basic/hello-form.html");
HTTP 응답 데이터 - 단순 텍스트 ,html
- 단순 텍스트 응답 -> writer.println("ok");
- html 응답
- HTTP API - MessageBody Json 응답
html 응답의 경우 그냥 writer 써서 한 줄 한 줄 html 코드 적어준 것
writer.println("<html>");
writer.println("<body>");
writer.println(" <div>안녕?</div>");
HTTP 응답 데이터 - API JSON
- 응답을 반환할 때는 타입을 application/json 으로 지정
- 객체에 있는 값들을 json 형식으로 바꿀려면 ObjectMapper 써야함
objectMapper.writeValueAsString();
'Programing > Spring Boot' 카테고리의 다른 글
[MVC-1] MVC 프레임 워크 만들기 (0) | 2023.08.01 |
---|---|
[MVC-1] 서블릿 , JSP , MVC패턴 (0) | 2023.07.25 |
[MVC-1] 웹 애플리케이션 이해 (0) | 2023.07.25 |
[핵심원리 - 기본] 빈스코프 (0) | 2023.07.25 |
[핵심원리 - 기본] 빈 생명주기 콜백 (0) | 2023.07.25 |