1. HttpServlet
- HttpServlet의 특징
- HttpServlet은 GET / POST 등에 맞게 doGet(), dePost() 등을 제공, 개발자는 필요한 메서드를 오버라이드 하는 것 만으로 GET / POST 방식을 나누어 처리 가능
- HttpServlet을 상속받은 클래스 객체는 톰캣 같은 WAS 내부에서 자동으로 객체를 생성·관리, 개발자가 생성·관리 신경쓸 필요 x
- HttpServlet은 멀티 스레드에 의해 동시에 실행될 수 있도록 처리, 개발자는 동시에 많은 사용자를 처리하는 방법 고민할 필요 x
- Servlet의 상속 구조
- GenericServlet은 HttpServlet가 상속받는 상위 클래스이고, 추상 클래스
- GenericServlet은 HttpServlet과 다르게 HTTP 프로토콜에 특화되지 않는 요청과 응답에 대한 기능 정의
- GenericServlet은 Http가 아닌 응답과 요청을 의미하는 ServletRequest / ServletResponse 이용
- HttpServlet은 GenericServlet을 상속받아 HttpServletRequest / HttpServletResponse 이용
1) HttpServlet 라이프사이클
- Servlet은 기본적으로 요청(Request)을 처리해서 응답(Response)하는 것이 목적
- Servlet 클래스 처리 과정
- 브라우저가 톰캣에 Servlet이 처리해야 하는 특정 경로 호출
- 톰캣은 해당 경로에 맞는 Servlet 클래서 로딩, 객체 생성(init() 메서드 실행하여 서블릿 객체 동작 전 수행해야 하는 일 처리)
- 생성된 Servlet 객체는 브라우저 요청에 대한 정보를 분석하여 GET / POST 등의 정보와 함께 전달되는 파라미터(쿼리 스트링의 내용)들을 HttpServletRequest라는 타입의 파라미터로 전달받고, 응답을 처리하는 데 필요한 기능들은 HttpServletResponse라는 타입의 객체로 전달받음
- Servlet 내부에서 GET / POST에 맞게 doGet(), doPost() 등의 메서드를 실행, 동일 주소의 호출이 있을 때 Servlet은 동일 객체 하나만을 이용해 처리
- 톰캣 종료 시 Servlet의 destroy() 메서드 실행
- 위의 과정에서 중요한 점
- Servlet 객체는 경로에 맞게 하나만 만들어짐
- 매번 호출 시 자동으로 doGet() / doPost()를 이용해 처리됨
- 동작 예시
package org.zerock.w1;
import javax.servlet.ServletConfig;
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;
// "/sample" 경로에서의 동작
@WebServlet(name = "sampleServlet", urlPatterns = "/sample")
public class SampleServlet extends HttpServlet{
// doGet() 메서드를 HttpServlet에서 상속받아와서 Override
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet..." + this);
}
// destroy() 메서드를 HttpServlet에서 상속받아와서 Override
@Override
public void destroy(){
System.out.println("destroy....................");
}
// init() 메서드를 HttpServlet에서 상속받아와서 Override
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("init(ServletConfig).........");
}
}
- 처음 '/sample'경로에 접속했을 때, init() 메서드가 실행되어 해당 메세지 출력
- 이후 doGet() 메서드도 실행되어 doGet......과 함께 Servlet 객체(this) 출력
- '/sample' 경로에서 새로고침 등으로 여러 번 호출하면 doGet() 메서드만 반복적으로 실행되고 init() 메서드의 메세지는 다시 출력되지 않음
- 또한, doGet() 메서드의 출력 메세지 중 Servlet 객체를 나타내는 this부분이 전부 동일한 것으로 보아, Servlet의 객체는 경로 당 하나만 생성됨을 확인
- 마지막으로 톰캣을 종료하면 destroy() 메서드가 실행되어 해당 메세지가 출력됨
- 위의 동작 예시로 보아, init()과 destroy()는 한 번씩만 호출되고, doGet() / doPost()는 동일한 객체를 이용해 여러 번 호출됨
2. HttpServletRequest의 주요 기능
- HTTP 메세지 형태로 들어오는 요청에 대한 정보를 파악
기능 | 메서드 | 설명 |
---|---|---|
HTTP 헤더 관련 | getHeaderNames() getHeader(이름) |
HTTP 헤더 내용들을 찾아내는 기능 |
사용자 관련 | getRemoteAddress() | 접속한 사용자의 IP주소 |
요청 관련 | getMethod() getRequestURL() getRequestURI() getServletPath() |
GET / POST 정보, 사용자가 호출에 사용한 URL 정보 등 |
쿼리 스트링 관련 | getParameter() getParameterValues() getParameterNames() |
쿼리 스트링 등으로 전달되는 데이터를 추출하는 용도 |
쿠키 관련 | getCookies() | 브라우저가 전송한 쿠키 정보 |
전달 관련 | getRequestDispatcher() | |
데이터 저장 | setAttribute() | 전달하기 전에 필요한 데이터를 저장하는 경우에 사용 |
1) getParameter()
- '?name=AAA&age=20'과 같은 쿼리 스트링에서 'name'이나 'age'라는 키(key)를 이용해 값(value)을 얻는 역할
- getParameter()의 결과는 항상 String, 해당 파라미터가 존재하지 않으면 null 반환
- 숫자 처리할 때는 예외가 발생할 수 있어 주의
2) getParameterValues()
- 동일한 이름의 파라미터가 여러 개 있는 경우 사용
- 여러 개 존재하는 이름에 대해 String[] 타입으로 반환
3) setAttribute()
- JSP로 전달할 데이터를 추가할 때 사용
- 키(key)와 값(value) 형태로 저장, 키(key)는 문자열, 값(value)는 모든 객체 타입
- JSP에는 Servlet에서 setAttribute()로 전달된 데이터를 화면에 출력
4) RequestDispatcher
- 웹 MVC 구조에서 getRequestDispatcher()를 이용해서 RequestDispatcher 타입의 객체 생성
- 현재의 요청을 다른 서버의 자원(Servlet 또는 JSP)에 전달하는 용도
- RequestDispatcher의 메서드
- forward(): 현재까지의 모든 응답 내용 무시, JSP가 작성하는 내용만 브라우저로 전달
- include(): 지금까지 만들어진 응답 내용 + JSP가 만든 내용을 브라우저로 전달
- 실제 개발에서는 forward()만 거의 이용
3. HttpServletResponse의 주요 기능
- HttpServletRequest가 '읽는' 기능, HttpServletReponse는 '쓰는' 기능 제공
기능 | 메서드 | 설명 |
---|---|---|
MIME 타입 | setContentType() | 응답 데이터의 종류를 지정(이미지 / hyml / xml 등) |
헤더 관련 | setHeader() | 특정 이름의 HTTP 헤더 지정 |
상태 관련 | setStatus() | 404, 200, 500 등 응답 상태 코드 지정 |
출력 관련 | getWriter() | PrintWriter를 이용해서 응답 메세지 작성 |
쿠키 관련 | getCookie() | 응답 시에 특정 쿠키 추가 |
전달 관련 | sendRedirect() | 브라우저에 이동을 지시 |
1) sendRedirect()
- 웹 MVC 구조에서 HttpServletResponse 메서드들 중 가장 많이 사용
- '다른 곳으로 가라'는 응답 메세지 전달
- 지정한 경로를 이름으로 갖는 HTTP 헤더로 전달되어 브라우저는 경로가 인자로 있는 응답을 받으면 화면을 처리하는 대신 주소창에 지정된 주소로 이동하고, 다시 호출
- sendRedirect()를 이용하면 주소가 아예 변경되어 새로고침 등의 요청 방지, 특정 작업 끝내고 새로 시작하는 흐름 만들 수 있음
실습
1) 와이어 프레임 구현
- 앞서 Todo 웹 애플리케이션을 만들기 위해 정리한 구현 목록에서 브라우저가 호출할 때 사용하는 URL 결정하여 추가
기능 | 동작 | 방식Controller(org.zerock.w1.todo) | 컨트롤러 URL | JSP |
---|---|---|---|---|
목록 | GET | TodoListController | /todo/list | WEB-INF/todo/list.jsp |
등록(입력) | GET | TodoRegisterController | /todo/register | WEB-INF/todo/register.jsp |
등록(처리) | POST | TodoRegisterController | /todo/register | Redirect |
조회 | GET | TodoReadController | /todo/read | WEB-INF/todo/read.jap |
수정(입력) | GET | TodoModifyController | /todo/modify | WEB-INF/todo/modify.jsp |
수정(처리) | POST | TodoModifyController | /todo/modify | Redirect |
삭제(처리) | POST | TodoRemoveController | /todo/remove | Redirect |
- 구현할 때는 GET / POST에 따라 doGet() / doPost()를 오버라이드
2) TodoListController 구현
- GET 방식만 처리하므로 doGet()만 오버라이드
// TodoListcontroller
package org.zerock.w1.todo;
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 = "todoListController", urlPatterns = "/todo/list")
public class TodoListController extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("/todo/list");
req.getRequestDispatcher("/WEB-INF/todo/list.jsp").forward(req, resp);
}
}
- "/WEB-INF/todo/list.jsp"의 경로에 List Page임을 명시하는 내용 작성
// list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>List Page</h1>
</body>
</html>
- 경로에 접속하면 JSP로 작성한 페이지가 출력되고, doGet() 메서드도 잘 실행되었다는 증거로 로그에 'Todo/list'가 출력됨
3) TodoRegisterController 구현
- GET 방식으로 호출되면 입력 화면, POST 방식으로 호출되면 등록이 처리된 후에 다시 목록 페이지('/todo/list') 호출(sendRedirect())
// TodoRegisterController
package org.zerock.w1.todo;
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 = "todoRegisterController", urlPatterns = "/todo/register")
public class TodoRegisterController extends HttpServlet{
// GET 방식에 대한 동작
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Compose to view the input screen");
// GET 방식의 호출은 등록 화면을 보게 하는 것이므로 RequestDispatcher를 사용하여 JSP를 보도록 작성
RequestDispatcher dispatcher =req.getRequestDispatcher("/WEB-INF/todo/register.jsp");
// forward는 지금까지 응답 무시하고 JSP가 작성하는 내용만 브라우저로 전달
dispatcher.forward(req, resp);
}
// POST 방식에 대한 동작
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Move to list page after processing input");
}
}
// register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action = "/todo/register" method = "post">
<button type = "submit">등록처리</button>
</form>
</body>
</html>
- 'http://localhost:8080/todo/register' 경로로 접속하면 'register.jsp'에서 작성한 화면이 나오며, 로그에는 doGet() 메서드의 실행 결과인 'Compose to view the input screen'이 출력됨
- '등록처리' 버튼을 누르면 POST 동작이 실행되고 doPost() 메서드의 실행 결과인 'Move to list page after processing input'이 출력됨
- doPost가 실행되었지만 이후 갈 곳이 정해지지 않아 화면은 빈 화면이고, 새로고침을 하면 경고창이 뜨고, 로그에는 doPost() 메서드의 메세지가 반복해서 출력됨
- 이를 막기 위해 PRG 패턴을 적용하며 이 때 sendRedirect 필요
// TodoRegisterController의 doPost() 수정
...
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Move to list page after processing input");
// 브라우저가 호출해야 하는 주소
resp.sendRedirect("/todo/list");
}
...
- '/todo/register' 경로에서 '등록처리' 버튼 눌렀을 때 목록 페이지('/todo/list')로 이동
- 로그를 살펴보면
- Compose to view the input screen: 처음 '/todo/register' 경로에 접속하면 TodoRegisterController의 doGet() 메서드가 실행되어 출력
- Move to list page after processing input: '/todo/register' 경로에서 버튼 클릭을 통해 POST 동작이 실행되면 TodoRegisterController의 doPost() 메서드가 실행되어 출력
- /todo/list: TodoRegisterController의 doPost()에서 sendRedirect된 경로인 '/todo/list'로 이동하여 TodoListController의 doGet() 메서드가 실행되어 출력
'back-end > Java' 카테고리의 다른 글
[자바 웹 개발 워크북] 2.2 - 프로젝트 내 JDBC 구현 (0) | 2023.01.05 |
---|---|
[자바 웹 개발 워크북] 2.1 - JDBC 프로그래밍 준비 (0) | 2023.01.04 |
[자바 웹 개발 워크북] 1.5 - 모델(Model) (0) | 2023.01.03 |
[자바 웹 개발 워크북] 1.3 - Web MVC 방식 (0) | 2022.12.30 |
[자바 웹 개발 워크북] 1.2 - 웹 기본 동작 방식 (1) | 2022.12.29 |