[Servlet/JSP]
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(제어, 포맷팅, 함수)