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 클래스 처리 과정

  1. 브라우저가 톰캣에 Servlet이 처리해야 하는 특정 경로 호출
  2. 톰캣은 해당 경로에 맞는 Servlet 클래서 로딩, 객체 생성(init() 메서드  실행하여 서블릿 객체 동작 전 수행해야 하는 일 처리)
  3. 생성된 Servlet 객체는 브라우저 요청에 대한 정보를 분석하여 GET / POST 등의 정보와 함께 전달되는 파라미터(쿼리 스트링의 내용)들을 HttpServletRequest라는 타입의 파라미터로 전달받고, 응답을 처리하는 데 필요한 기능들은 HttpServletResponse라는 타입의 객체로 전달받음
  4. Servlet 내부에서 GET / POST에 맞게 doGet(), doPost() 등의 메서드를 실행, 동일 주소의 호출이 있을 때 Servlet은 동일 객체 하나만을 이용해 처리
  5. 톰캣 종료 시 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')로 이동

  - 로그를 살펴보면

  1. Compose to view the input screen: 처음 '/todo/register' 경로에 접속하면 TodoRegisterController의 doGet() 메서드가 실행되어 출력
  2. Move to list page after processing input: '/todo/register' 경로에서 버튼 클릭을 통해 POST 동작이 실행되면 TodoRegisterController의 doPost() 메서드가 실행되어 출력
  3. /todo/list: TodoRegisterController의 doPost()에서 sendRedirect된 경로인 '/todo/list'로 이동하여 TodoListController의 doGet() 메서드가 실행되어 출력

+ Recent posts