Servlet 에 대해 - 개념, 메소드

제훈·2024년 8월 6일

Java

목록 보기
29/34

Servlet 에 대해 개념도 알아보고 활용도 해보자.

Servlet

Network 통신

Server-Client Model

서버 : 특정 서비스를 제공하는 컴퓨터
클라이언트 : 서비스를 이용하는 사용자

나뉜 이유?

  • 책임 전가 + 서버 부담 감소
    - 실행 파일(.exe)을 클라이언트에게 제공함으로써 서버의 부담이 감소되면서, 클라이언트에게 책임을 전가할 수 있다. (ex. OS 문제로 인해 실행 못 하는 경우)
    - 하지만 책임을 전가한만큼 업데이트를 하지 않으면 보안에 문제가 생겨서 해킹을 당할 수 있는 위험도 있긴 하다..

Server 종류

종류설명
Web Server웹 브라우저와 HTTP 프로토콜을 사용하여 사용자의 요구에 따른 특정 서비스를 제공하는 서버
Mail Server인터넷을 통해 사용자 간의 전자 우편을 주고 받는 서비스 제공
FTP Server서버 내에 파일을 업로드, 다운로드 할 수 있도록 파일 관리 기능 제공
Telnet ServerTerminal, 텍스트로만 이루어진 창에서 특정 명령어를 통해 원격지 서버를 접속 및 관리
Database Server데이터를 저장하고, 원격지에 접속할 경우 권한에 따라 해당 데이터를 열람, 추가, 수정, 삭제하는 기능 처리

개별(local) 프로그램과 서버 프로그램의 특징

  • 개별(local) 프로그램 특징 및 단점
  1. 프로그램 업데이트 발생 시 각각 다시 다운로드 해야 한다.
  2. 각 프로그램에서 생성된 데이터 개별 저장되므로 공유 불가하다.
  • 서버 프로그램 특징
  1. 프로그램 업데이트 발생 시 서버가 상관하지 않아도 클라이언트가 서버에서 다운 받아 업데이트를 개별적으로 진행한다.
  2. 데이터는 서버에 일괄 저장된다.

Web 통신

Web 통신 구조


Web Server

사용자에게 HTML 페이지 같이 정적인 요소들을 화면에 보여주는 역할을 하는 HTTP 프로토콜을 통해 웹 브라우저에 제공하는 서버를 뜻한다.

  • APACHE
  • Microsoft IIS
  • NGINX

위와 같은 것들이 있다.


WAS란

사용자가 요청한 서비스의 결과를 스크립트 언어 등으로 가공하여 생성한 동적인 페이지를 사용자에게 보여주는 역할

웹 서버가 웹 애플리케이션 서버에 요청하면, 웹 애플리케이션 서버가 해당 프로그램을 실행하는 방식
-> 동일 프로그램에 여러 요청이 있을 시, 한 개의 프로그램을 실행해 다수 요청을 처리한다.

  • Web Application이란
    • 정적인 페이지를 제공하는 미리 완성된 클라이언트 프로그램이다.
    • 브라우저만 설치되어 있으면 서버에 요청해 언제든 볼 수 있다.
    • 클라이언트 요청(= url 요청)에 따라 서버가 응답을 html 태그 문법인 텍스트로 전송하면, 브라우저가 해당 텍스트를 해석해 띄워주는 방식이다.
    • 서버는 클라이언트 요청(request)에 반드시 응답(response)해야 한다.
      • 응답할 내용이 없으면, 응답 내용이 없다는 응답이라도 해야 한다.

예시)

  • Apache Tomcat
  • WildFly (or Jboss)
  • JEUS

위와 같은 것들이 있다.


Web 와 WAS

  • web(html)은 사전에 작성된 화면으로 정적인 페이지(사전에 작성된 화면)를 의미한다. 서버는 따로 두고 일단 클라이언트 요청에 대해 web에서 응답한 뒤에, 처리할 동적 요청 등 필요에 따라 was에 요청하여 응답한다.
  • WAS는 Servlet을 보관하다가 서블릿 라이프사이클에 따라 생성, 소멸 등을 주관하는 역할을 하여, 다른 말로 Servlet Container라고도 부른다.
구분장점단점
Web Server- 빠른 처리 속도
→ 요청에 대한 결과 페이지만 전송
- 쉬운 구현
→ HTML같은 단순한 문서만으로 구성
- 한정적인 서비스
→ 만들어진 정보만 보여주므로 서비스가 한정적임
- 글의 추가, 수정, 삭제가 어려움
→ 문서의 내용이 변경될 경우 직접 수정
WAS- 서비스의 다양성
→ 여러 데이터 활용 가능
- 글의 추가, 수정, 삭제가 쉬움
→ 문서 내용이 변경되면 직접 수정하지 않음
- 느린 처리 속도
→ 데이터를 처리하여 결과 전송
- 어려운 구현
→ 서비스에 해당하는 소스 직접 작성

Servlet mapping

Servlet mapping 방법

  1. web.xml 이용한 방법
  2. @annotation 이용한 방법

Servlet mapping 구조

클라이언트가 서블릿의 url로 요청을 보내면, 톰캣이 이를 해석해 매핑된 서블릿으로 연결하는 구조이다.

mapping 경로가 잘못 되면 tomcat 실행 자체가 되지 않는다.


web.xml 사용해보기

    <servlet>
        <servlet-name>xmlmapping</servlet-name>
        <servlet-class>실제 클래스 명t</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>xmlmapping</servlet-name>
        <url-pattern>/xml-lifecycle</url-pattern>
    </servlet-mapping>
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServlet;

import java.io.IOException;

public class LifeCycleTestServlet extends HttpServlet {

    /* 설명. 기본 생성자 */
    public LifeCycleTestServlet() {
        System.out.println("xml 방식 기본 생성자 실행");
    }

    /* 설명. 서블릿의 요청이 최초인 경우 서블릿 객체를 생성하고 자동으로 호출하게 되는 메소드 */
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("xml 매핑 init() 메소드 호출");
        System.out.println("실제로는 요청에 따라 doGet() 또는 doPost()가 실행됨");
    }

    /* 설명.
     *  서블릿 컨테이너에 의해 호출되며 최초 요청 시에만 init() 이후에 동작하고, 2번재부터는 service()만 호출되게 된다.
    * */
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("xml 매핑 service() 메소드 호출");
    }

    /* 설명. 컨테이너가 종료될 때 호출하는 메소드이며 주로 자원을 반납하는 용도로 사용된다. */
    @Override
    public void destroy() {
        System.out.println("xml 매핑 destroy() 메소드 호출");
    }
}

index.jsp

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>Servlet Life</title>
</head>
<body>
<h1 align="center">라이프 사이클 테스트</h1>
<br/>
<a href="xml-lifecycle">라이프 사이클 테스트(xml)</a>
</body>
</html>

LifeCycleTestServlet를 실행해서 링크도 클릭해보면 잘 출력되는지 확인 가능하다.


@annotation 사용

import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;

import java.io.IOException;

@WebServlet(value="/annotation-lifecycle")
public class LifeCycleTestServlet extends HttpServlet {

    public LifeCycleTestServlet() {
        System.out.println("어노테이션 방식 기본 생성자 실행");

    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("어노테이션 매핑 init() 메소드 호출");
        System.out.println("실제로는 요청에 따라 doGet() 또는 doPost()가 실행됨");
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("어노테이션 매핑 service() 메소드 호출");
    }

    @Override
    public void destroy() {
        System.out.println("어노테이션 매핑 destroy() 메소드 호출");

    }
}

index.jsp

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>Servlet Life</title>
</head>
<body>
<h1 align="center">라이프 사이클 테스트</h1>
<br/>
<a href="xml-lifecycle">라이프 사이클 테스트(xml)</a> <br>
<a href="annotation-lifecycle">라이프 사이클 테스트(annotation)</a>
</body>
</html>

또한 잘 되는 것을 알 수 있다.

  • 어노테이션만 추가해주면 되는 간편함이 있다.
  • 하지만 별도의 문서 등으로 관리하지 않으면 시스템의 모든 서블릿 매핑 정보를 한눈에 볼 수 없다는 단점이 있다.


Servlet 구동


Servlet Method

HTTP 데이터 전송 방식

  1. 브라우저에서 요청 정보를 HTTP 객체에 담아 전송하는 방식

요청과 응답을 주고받는 패킷은 요청 정보와 응답 정보를 직렬화한 Byte단위의 문자열 데이터로, 인코딩과 디코딩이 필요하다.

  1. 전달받은 HTTP객체를 서버(=Tomcat)이 해석하여 요청을 처리할 서블릿을 호출한다.
  • 서블릿의 service() 메소드에서는 request, response 요청 정보를 가지고 처리 로직을 거쳐 응답한다.

전송 방식 중에 get 방식, post 방식에 대해 메소드와 함께 알아보자.


데이터 전송 방식에 따른 Servlet Method

서블릿이 get/post의 두 방식 중 하나로 요청 정보를 전달 받으면, request와 response를 전달하면서 해당하는 처리 메소드(doGet() 메소드 또는 doPost() 메소드)를 호출한다.

HTML에서 method 속성을 이용해 방식을 결정하며, default는 get 방식이다.

import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/request-service")
public class ServiceMethodTestServlet extends HttpServlet {
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("req = " + req);
        System.out.println("res = " + res);

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        String httpMethod = request.getMethod();
        System.out.println("요청 방식 : " + httpMethod);

        if ("GET".equals(httpMethod)) {
            doGet(request, response);
        } else if("POST".equals(httpMethod)) {
            doPost(request, response);
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("GET 요청을 처리할 메소드 호출 중");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("POST 요청을 처리할 메소드 호출 중");
    }
}

index.jsp

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>JSP - Hello World</title>
</head>
<body>
    <h1 align="center">Service Method</h1>
    <h3>GET 방식의 요청</h3>
    <h4>a 태그의 href 속성값 변경</h4>
    <a href="request-service">서비스 메소드 요청하기</a>

    <h3>POST 방식의 요청</h3>
    <h4>form 태그의 method 속성을 post로 설정</h4>
    <form action="request-service" method="post">
        <input type="text" name="number" value="123">
        <input type="text" name="name" value="홍길동">
        <input type="submit" value="POST 방식 요청 전송">
    </form>
</body>
</html>

위 2가지 코드를 통해 실행해보면

이런 형태가 되는데 index.jsp 에서의 <form action="request-service" method="post"> 이 부분에서 method ="get" 으로 바꿔서 실행할 수도 있다.

post 상태에서 버튼을 눌렀을 때 호출되는 것들은 아래 사진과 같다.


Servlet Method Parameter

HttpServletRequest

역할

HTTP Servlet을 위한 요청 정보(request information)를 제공하는 메소드를 지정

method 명내용
getParameter(String)client가 전송한 값의 명칭이 매개변수와 같은 값 가져옴
getParameterNames()client가 전송한 값의 명칭 가져옴
getParameterValues(String)client가 전송한 값이 여러 개이면 배열로 가져옴
getParameterMap()client가 전송한 값 전체를 Map방식으로 가져옴
setAttribute(String, object)request 객체로 전달할 값을 String 이름-Object 값으로 설정
getAttribute(String)매개변수와 동일한 객체 속성 값 가져옴
removeAttribute(String)request객체에 저장된 매개변수와 동일한 속성 값 삭제
setCharacterEncoding(String)전송 받은 request객체 값들의 CharaterSet 설정
getRequestDispatcher(String)컨테이너 내에서 request, response객체를 전송하여 처리한 컴포넌트(jsp파일 등)를 가져옴 (forward() method와 같이 사용)

Request Header

HttpServletResponse 아래에 있는 활용에는 사용되지 않았지만, 그래도 HttpServletRequest 안에 포함되는 내용이기에 추가했다.

  1. General header

    • 요청 및 응답 모두에 적용되지만 최종적으로는 body에 전송되는 것과 관련이 없는 헤더이다.
  2. Request header

    • Fetch될 리소스나 클라이언트 자체에 대한 상세 정보를 포함하는 헤더이다.
  3. Response header

    • 위치나 서버 자체와 같은 응답에 대한 부가적인 정보를 갖는 헤더이다.
  4. Entity header

    • 컨텐츠 길이나 MIME타입과 같은 엔티티 body에 대한 상세 정보를 포함하는 헤더이다. (요청 응답 모두 사용되며, 메시지 바디의 컨텐츠를 나타내기에 GET요청은 해당하지 않는다.)
  5. getHeader() 메소드로 확인 가능한 값

    header 속성
    accept요청을 보낼 때 서버에게 요청할 응답 타입
    accept-encoding응답 시 원하는 인코딩 방식
    accept-language응답 시 원하는 언어
    connectionHTTP 통신이 완료된 후에 네트워크 접속을 유지할지 결정
    (기본값: keep-alive = 연결을 열린 상태로 유지)
    host서버의 도메인 네임과 서버가 현재 Listening 중인 TCP포트 지정
    (반드시 하나가 존재. 없거나 둘 이상이면 404)
    referer이 페이지 이전에 대한 주소
    sec-fetch-dest요청 대상
    sec-fetch-mode요청 모드
    sec-fetch-site출처(origin)와 요청된 resource 사이의 관계
    sec-fetch-user사용자가 시작한 요청일 때만 보내짐 (항상 ?1 값 가짐)
    cache-control캐시 설정
    upgrade-insecure-requestsHTTP 메시지 전송 시 보안 적용
    user-agent현재 사용자가 어떤 클라이언트(OS, browser 포함)을 이용해 보낸 요청인지 명시
  • request header 중 sec-fetch 속성값 및 활용 참고
    • sec-fetch-site
      • cross-site : 요청 개시자와 resource를 호스팅하는 서버가 다른 사이트일 경우
      • same-origin : 요청 개시자와 resource를 호스팅하는 서버가 동일한 출처(origin)를 가질 경우
      • same-site : 요청 개시자와 resource를 호스팅하는 서버가 동일한 scheme, 도메인/서브도메인을 가지지만 port가 다른 경우
      • none : 요청이 사용자로부터 시작되었을 경우. (ex. 주소창에 URL 입력, 브라우저 창에 파일 끌어다 놓기 등.)
    • sec-fetch-mode
      • cors : CORS protocol 요청
      • navigate : HTML document 사이 이동 시
      • no-cors : no-cors 요청
      • same-origin : 요청 중인 resource와 동일한 출처
      • websocket : websocket 연결을 설정하기 위한 요청
    • sec-fetch-dest
      • 서버는 요청이 사용되는 방식이 적절한지에 따라 요청의 허용 여부를 결정
    • sec-fetch-user
      • 서버는 해당 속성값을 통해 document, iframe 등의 탐색 요청이 사용자로부터 시작된 것인지 식별 가능

Request Header 참고하기 좋은 사이트


HttpServletResponse

요청에 대한 Servlet의 역할

  1. 요청을 받는다.
    • HTTP method GET/POST 요청에 따라 parameter로 전달받은 데이터를 꺼내어 활용할 수 있다.
  2. 비지니스 로직을 처리(DB접속과 CRUD에 대한 로직 처리)한다.
    • 서비스 메소드를 호출하여 처리 로직을 수행하도록 한다.
  3. 응답한다.
    • 문자열로 동적인 웹(HTML 태그)페이지를 만들고 스트림을 이용해 내보내거나 응답 데이터를 담아 결과 페이지를 호출한다.

역할

요청에 대한 처리 결과를 작성하기 위해 사용하는 객체이다.

method 명내용
setContentType(String)응답으로 작성하는 페이지의 MIME type을 설정
setCharacterEncoding(String)응답하는 데이터의 CharacterSet을 지정
getWriter()페이지에 문자 전송을 위한 Stream을 가져옴
getOutputStream()페이지에 byte단위의 전송을 위한 Stream을 가져옴
sendRedirect(String)client가 매개변수의 페이지를 다시 서버에 요청함

활용 (Request Header 이전까지)

doGet(), doPost() 메소드를 활용해서 같이 볼 수 있다.

index.jsp

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>JSP - Hello World</title>
</head>
<body>
<h1 align="center">Request Parameter</h1>
<h3>Get 방식의 요청</h3>
<h4>form 태그를 이용한 get 방식 요청</h4>
<form action="querystring" method="get">
    <label>이름 : </label><input type="text" name="name"> <br>
    <label>나이 : </label><input type="number" name="age"> <br>
    <label>생일 : </label><input type="date" name="birthday"> <br>
    <label>성별 : </label>
    <input type="radio" name="gender" id="male" value="M">
    <label for="male">남자</label>
    <input type="radio" name="gender" id="female" value="F">
    <label for="female">여자</label> <br>
    <label>국적 : </label>
    <select name="national">
        <option value="ko">한국</option>
        <option value="ch">중국</option>
        <option value="jp">일본</option>
        <option value="etc">기타</option>
    </select>
    <br>
    <label>취미 : </label>
    <input type="checkbox" name="hobbies" id="movie" value="movie"><label for="movie">영화</label>
    <input type="checkbox" name="hobbies" id="music" value="music"><label for="music">음악</label>
    <input type="checkbox" name="hobbies" id="sleep" value="sleep"><label for="sleep">취침</label>
    <br>

    <input type="submit" value="GET 요청">
    <button type="button">GET 요청</button>
</form>

<h4>a 태그의 href 속성에 직접 파라미터를 포함한 쿼리 스트링 방식으로 작성해get 요청하기</h4>
<a href="querystring?name=홍길동&age=30&birthday=2024-02-06&gender=M&national=ko&hobbies=movie">
    a 태그를 활용한 쿼리 스트링 값 전달
</a>

<h4>form 태그를 이용한 post 방식 요청</h4>
<form action="formdata" method="post">
    <label>이름 : </label><input type="text" name="name"> <br>
    <label>나이 : </label><input type="number" name="age"> <br>
    <label>생일 : </label><input type="date" name="birthday"> <br>
    <label>성별 : </label>
    <input type="radio" name="gender" id="male" value="M">
    <label for="male">남자</label>
    <input type="radio" name="gender" id="female" value="F">
    <label for="female">여자</label> <br>
    <label>국적 : </label>
    <select name="national">
        <option value="ko">한국</option>
        <option value="ch">중국</option>
        <option value="jp">일본</option>
        <option value="etc">기타</option>
    </select>
    <br>
    <label>취미 : </label>
    <input type="checkbox" name="hobbies" id="movie" value="movie"><label for="movie">영화</label>
    <input type="checkbox" name="hobbies" id="music" value="music"><label for="music">음악</label>
    <input type="checkbox" name="hobbies" id="sleep" value="sleep"><label for="sleep">취침</label>
    <br>

    <input type="submit" value="POST 요청">
    <button type="button">POST 요청</button>
</form>
</body>
</html>

사진과 같이 만들어진다. 이제 값들을 넣어보자.

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Arrays;

@WebServlet("/querystring")
public class QueryStringServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        System.out.println("name = " + name);

        int age = Integer.parseInt(req.getParameter("age"));
        System.out.println("age = " + age);

        java.sql.Date birthday = java.sql.Date.valueOf(req.getParameter("birthday"));
        System.out.println("birthday = " + birthday);

        char gender = req.getParameter("gender").charAt(0);
        System.out.println("gender = " + gender);

        String national = req.getParameter("national");
        System.out.println("national = " + national);

        String[] hobbies = req.getParameterValues("hobbies");
        System.out.println("hobbies = " + Arrays.toString(hobbies));
    }
}
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Enumeration;

@WebServlet("/formdata")
public class FormDataServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        System.out.println("name = " + name);

        Enumeration<String> names = req.getParameterNames();
        while(names.hasMoreElements()) {
            System.out.println(names.nextElement());
        }
    }
}

a 태그에 쿼리 스트링 방식으로 넣은건 querystring?name=홍길동&age=30&birthday=2024-02-06&gender=M&national=ko&hobbies=movie 이 부분인걸 알 수 있을 것이다.


실행결과

  • GET 요청 버튼 클릭 시 웹 브라우저 URL

  • GET 요청 버튼 클릭 시 인텔리제이 출력

  • a 태그 링크 클릭 시 웹 브라우저 URL

  • a 태그 링크 클릭 시 인텔리제이 출력

  • POST 요청 버튼 클릭 시


요약

  • 스프링 컨테이너는 애플리케이션의 객체 생명 주기와 구성을 관리
  • 서블릿 컨테이너는 HTTP 요청을 처리하고 응답을 관리하는 데 중점

컨테이너라는 이름으로 비슷하게 생각할 수 있지만, 둘은 다른 역할을 한다. 그것에 대해서는 다음 글에서 작성하겠다.

profile
백엔드 개발자 꿈나무

0개의 댓글