Advance I/Java

[Servlet/JSP]

kinggora 2023. 2. 1. 21:21

Servlet

상태 유지를 위한 5가지 방법

  • application
  • session
  • cookie
  • hidden input
  • querystring

서버 저장소: application, session

클라이언트 저장소: cookie

 

Application

서블릿 컨텍스트: 서블릿들이 공유하는 공간(저장소)

 

Session

클라이언트(사용자)마다 개별적으로 부여되는 공간

 

크롬 브라우저 여러 개 -> 프로세스 하나를 여러 쓰레드로 띄움. 쓰레드는 자원을 공유하기 때문에 같은 프로세스, 같은 사용자(세션)로 인식한다.

MS 엣지, 크롬 브라우저 -> 서로 다른 세션

 

웹 서버가 사용자(세션)을 구분하는 방식

  • 사용자 첫 방문: 세션 ID 없음. session 사용 불가, application 사용 가능
  • 벗어날 때 세션 ID 부여 -> 쿠키에 저장. 개별 세션 생성
  • 다시 방문: 세션 ID로 사용자 식별. session 사용 가능

세션은 생성 시 설정된 timeout 동안 유지되며, 세션이 존재하는 동안 같은 사용자 다시 방문시 timeout이 갱신된다.

 

쿠키의 옵션

  • path: 설정 경로에 대한 요청에만 해당 쿠키를 전달. 서버 입장에서는 쿠키를 받아옴
  • maxAge: 메모리에 있던 쿠키를 파일에 저장하여 브라우저가 닫혀도 쿠키를 유지할 수 있다. 쿠키의 만료 시간 설정(초 단위). maxAge를 설정하지 않으면 쿠키의 수명은 브라우저의 생존주기와 같다.

 

* 탐색 세션 종료 = 브라우저가 닫힐 때

 

  사용 범위 생명 주기 저장 위치
Application 전역 범위 WAS 시작부터 종료까지 WAS 서버 메모리
Session 세션 범위 세션 시작부터 종료까지 WAS 서버 메모리
Cookie 웹 브라우저별
지정한 path 범주 공간
브라우저에 전달한 시간부터
만료 시간까지
웹 브라우저 메모리 또는 파일

 

  • 특정 url에서만 사용하거나 장기간 유지해야 하는 데이터는 유지해야 한다면 쿠키 사용
  • 쿠키는 서버의 생명주기와 독립적이다.
  • 세션(사용자별 데이터)과 애플리케이션(전역 데이터)은 서버 자원을 사용한다.
  • 세션 ID도 결국 쿠키에 저장되는데 쿠키가 사라져도 timeout 까지 세션 공간을 유지하는 것은 비효율적

동적 페이지: 요청에 의해 생성되는 페이지(html)

 

HttpServlet 의 super.service(request, response)

요청 Method에 맞는 함수를 호출한다. (doGet(), doPost()...) => 일종의 라우팅

만일 doXxx() 를 override 하지 않았다면 오류 발생(405) => url은 있지만 요청을 처리할 수 있는 로직이 없음

 

service() 에서 공통 로직을 처리하거나, 한 url에서 요청 Method에 따라 다른 처리가 필요할 경우 service() 없이 doXxx()를 override 하여 사용하자.

JSP

Java Server Pages

사용자에 대한 응답 = HTML(정적) + 데이터(동적)

하나의 jsp 파일에 HTML과 데이터를 분리하여 작성하고 데이터를 HTML 안에 출력할 수 있다.

 

Jasper

사용자 요청이 들어왔을 때 html 문서(.jsp)를 주면 서블릿 프로그램으로 변환시킨다.

ex) add.jsp -> add_jsp.java (in IDE의 임시 배포 디렉터리)

변환된 코드를 보면 out.write() 로 html을 생성하고 있다.

jsp 파일의 이름이 매핑 url 이 된다.

 

Code Block

  • (코드 블럭 없음): service() 메서드 내에서 out.write()
  • <% ... %>: 자바 코드
  • <%= ... %>: 변수 출력. out.print(변수)
  • <%! ... %>: 멤버 변수, 메서드
  • <%@ ... %>: 지시어 (language, contentType, pageEncoding...) 어떤 출력 블럭보다 먼저 실행됨

*out.print(): 다양한 자료형

*out.write(): 문자열

 

JSP 내장 객체

_jspService()

  • HttpServletRequest request, HttpServletResponse response
  • PageContext pageContext: 내부 저장소
  • HttpSession session
  • ServletContext application: 전역 저장소
  • ServletConfig config
  • JspWriter out: Jasper 를 통해 간접접으로 사용하므로 직접 사용하는 일은 거의 없다.
  • Object page

MVC Model-1

하나의 파일에서 입력 코드(자바)와 출력 코드(html)를 분리

  • Controller: 입력과 제어 [자바 코드]
  • View: 출력 [HTML]
  • Model: 출력 데이터

Controller 에서 출력을 위한 Model 생성

View 는 Model 에서 데이터 출력

<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8"%>
 
<%
    int num = 0;
    String num_ = request.getParameter("n");
    if(num_ != null && !num_.equals("")){
        num = Integer.parseInt(num_);
    }

    String result;

    if(num % 2 != 0) {
        result = "홀수";
    } else {
        result = "짝수";
    }
%>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <%= result %> 입니다.
</body>
</html>

 

MVC Model-2

컨트롤러와 뷰가 물리적으로 분리된 방식 (다른 파일)

컨트롤러(.java)와 뷰(.jsp)가 서블릿일 때 Dispatcher 를 통해 흐름을 forwarding한다.

일반적으로 Dispatcher 하나를 두고 사용자 요청에 따라 여러 컨트롤러(일반 클래스, Pojo Class)를 호출한다.

  • forward: 현재 작업을 이어감
  • redirect: 새로운 요청
@WebServlet("/spag")
public class Spag extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int num = 0; //사용자가 값을 넘겨주지 않으면 짝수
        String num_ = request.getParameter("n");
        if(num_ != null && !num_.equals("")){
            num = Integer.parseInt(num_);
        }

        String result;

        if(num % 2 != 0) {
            result = "홀수";
        } else {
            result = "짝수";
        }
        request.setAttribute("result", result);

        RequestDispatcher dispatcher = request.getRequestDispatcher("spag.jsp");
        dispatcher.forward(request, response);
    }
}

 

  • request.setAttribute() : 컨트롤러에서 뷰로 데이터를 전달하기 위한 저장소 역할
  • dispatcher.forward(request, response) : 디스패쳐를 통해 jsp 파일(뷰)로 포워딩
<%@ page language="java" contentType="text/html;charset=UTF-8"
 pageEncoding="UTF-8" isELIgnored="false"%>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <%= request.getAttribute("result") %> 입니다.
    Expression Language: ${result} 입니다.
</body>
</html>

 

  • request.getAttribute() : 데이터를 꺼내서 출력
  • EL(Expression Language) : 저장소에서 키워드 검색한 값 출력

EL 의 데이터 저장소

  • pageScope: JSP 내장 객체 pageContext 에 등록된 데이터를 저장하는 map 객체
  • requestScope: 요청마다 생성되는 HttpServletRequest 에 등록된 데이터를 저장하는 map 객체
  • sessionScope: HttpSession 에 등록된 데이터를 저장하는 map 객체
  • applicationScope: ServletContext 에 등록된 데이터를 저장하는 map 객체
  • param: 파라미터 값을 저장하는 저장소
  • paramValues: 파라미터 값을 배열로 저장하는 저장소
  • header: 헤더 정보를 저장하고 있는 저장소
  • headerValues: 헤더 정보를 저장하고 있는 저장소
  • cookie: 쿠키 정보를 저장하고 있는 저장소
  • initParam: 컨텍스트의 초기화 파라미터를 저장하고 있는 저장소
  • pageContext: 페이지 범위의 컨텍스트 저장소

setAttribute(key, value) 로 데이터를 등록한다.

xxxScope 이하는 View 에서 요청 메세지를 직접 접근하고 싶을 때 유용하다.

 

EL이 탐색하는 저장소의 우선순위

page > request > session > application

  • 키워드에 해당하는 데이터를 찾으면 다음 객체는 탐색하지 않는다.
  • scope 한정사를 붙이면 해당 저장소 한정으로 탐색한다. ex) ${sessionScope.cnt} => HttpSession.getAttribute("cnt")

EL getter 호출법

마치 필드에 직접 접근하듯이 표기

<%= pageContext.getRequest().getMethod() %>

${pageContext.request.method}

 

EL 연산자

${empty param.n} 는 아래 두 경우 모두 true 를 반환한다.

  • /spag (n = null)
  • /spag?n= (n = "")

${not empty param.n ? param.n : '값이 비어 있습니다.'}

 

View 은닉하기

WEB-INF 디렉터리 이하로 뷰 파일 이동

-> 사용자가 url로 접근할 수 없다.

JSTL(JSP Standard Tag Library)

MVC Model2 - View에서 자바 코드 제거

자바 반복문을 이용한 제어 구조에서 태그를 이용한 제어 구조로 변경

 

JSTL library download

https://mvnrepository.com/artifact/javax.servlet/jstl/1.2

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

접두사 "c"를 이용하여 JSTL Tag 사용

 

<%
List<Notice> list = (List<Notice>)request.getAttribute("list");
for(Notice n : list){
    pageContext.setAttribute("n", n);
%>
<tr>
    <td>${n.id}</td>
    <td class="title indent text-align-left"><a href="detail?id=${n.id}">${n.title}</a></td>
    <td>${n.writerId}</td>
    <td>
        ${n.regDate}
    </td>
    <td>${n.hit}</td>
</tr>
<% } %>

 

  • jsp 파일에 포함된 자바 반복문 코드

 

<c:forEach var="n" items="${list}">
<tr>
    <td>${n.id}</td>
    <td class="title indent text-align-left"><a href="detail?id=${n.id}">${n.title}</a></td>
    <td>${n.writerId}</td>
    <td>
        ${n.regDate}
    </td>
    <td>${n.hit}</td>
</tr>
</c:forEach>

 

  • ${list}: 저장소(request)에서 값(List<Notice> list)을 꺼낸다.
  • <c:forEach var="n" items="${list}">: 반복문을 돌리며 list의 element를 변수 n에 저장한다.

 

태그 라이브러리

  • core
  • formatting
  • function

* View에서 코드 블록이 꼭 필요한 경우: EL(저장소 데이터 접근), JSTL(제어, 포맷팅, 함수)