서블릿과 컨트롤러의 비교

  • @Controller와 @RequestMapping 애노테이션을 합친
    @WebServlet 애노테이션을 사용한다
  • 클래스에 @WebServlet("/ 맵핑할 주소") 애노테이션 붙이고
    extends HttpServlet 상속

    클래스에 맵핑하기 때문에 클래스에 속한 모든 메서드에 일일이 애노테이션을 붙일 필요가 없다

서블릿의 메서드

1. init() - 서블릿 초기화

2. service() - 호출될 때마다 반복적으로 수행할 내용

3. destroy() - 서블릿 제거할 때 한 번 수행

@WebServlet("hello")
public class HelloServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        //서블릿이 초기화될 때 자동 호출되는 메서드
        //1. 서블릿의 초기화 담당
        System.out.println("[HelloServlet] init() is called");
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1. 입력
        //2. 처리
        //3. 출력
        System.out.println("[HelloServlet] service() is called");
    }

    @Override
    public void destroy() {
        System.out.println("[HelloServlet] destroy() is called");
    }
}

서블릿 호출 과정

  • 여러번 호출할 경우 init()은 사용되지 않고 service()만 계속 사용된다

  • 등록된 서블릿들이 Servlet Context에 Map 형태로 저장되어 있어서 요청이 들어오면 Servlet Context 에서 확인한다

  • 서블릿은 기본적으로 싱글톤(1개의 인스턴스 재활용)이다

JSP

  • 서블릿과 유사
  • JSP를 작성하면 서블릿으로 자동으로 변환
  • Html 안에 자바 코드 작성하는 것
<%@ page contentType="text/html;charset=utf-8"%>
<%@ page import="java.util.Random" %>
<%-- <%! 클래스 영역 %> --%>
<%!  
	int getRandomInt(int range){
		return new Random().nextInt(range)+1;
	}
%>
<%-- <%  메서드 영역 - service()의 내부 %> --%>
<%
	int idx1 = getRandomInt(6);
	int idx2 = getRandomInt(6);
%>
<html>
<head>
	<title>twoDice.jsp</title>
</head>
<body>
	<img src='resources/img/dice<%=idx1%>.jpg'>
	<img src='resources/img/dice<%=idx2%>.jpg'>
</body>
</html>
  • JSP는 자동으로 URL 맵핑되어 view까지 함께 된다

JSP의 호출 과정

1. 첫 번째 호출만 인스턴스를 생성해야하니 시간이 오래 걸린다

2. 두 번째 호출부터 변환과 컴파일을 거치지 않는다

3. jsp파일이 변경되면 변경된 내용을 변환하고 컴파일한다

  • 서블릿은 lazt-init (늦은 초기화)
  • Spring은 이를 개선하기 위해 early-init
    요청이 오지 않아도 미리 객체를 만들어두고 초기화 하는 방법을 사용

JSP 변환 과정

  • twoDice.jsp -> twoDice_jsp.java 로 이름이 변환됨
  • <%! !%>안에 구현된 메서드는 클래스 안으로 이동
  • service 메서드 이름이 _jspService로 생성되고 메서드 영역의 코드가 그 안으로 이동

JSP의 기본 객체

  • 생성하지 않고 사용할 수 있는 객체
  • request, response, pageContext, session, application, config, out, page 등 원래 service 메서드의 지역변수들

유효범위와 속성

  • HTTP의 특징: 상태 정보를 저장하지 않는다
    = stateless
    그래서 저장소가 필요하다
  • 접근 범위와 생존 기간이 다른 4개의 저장소가 있다

1. pageContext

  • 기본 객체와 lv를 저장하는 저장소
  • 페이지 안에서만 접근(읽기, 쓰기)할 수 있음
  • EL ${}에서 사용하기 위해, 저장소에 저장된 변수만 EL에 사용할 수 있음
  • 위 그림에서 login.jsp페이지에서만 pageContext에 접근할 수 있다

2. application

  • WebApp 전체에서 접근할 수 있는 공통 저장소
  • 위 그림에서 login.jsp와 write.jsp에서 모두 접근할 수 있다
  • 전체 애플리케이션에서 공유하는 저장소이기 때문에 로그인에서 사용하는 아이디나 비밀번호 같은 정보를 저장하기에 올바르지 않다

3. session

  • 클라이언트마다 존재하는 개별 저장소
  • 로그인하게 되면 세션에 아이디와 비밀번호가 저장되어 다음 요청을 했을 때 저장되어 있는 아이디를 사용할 수 있으며 요청마다 로그인을 새로 할 필요가 없어진다
  • 로그아웃하면 개별 저장소가 사라진다
  • 아이디, 장바구니 등 클라이언트마다 가지고 있는 정보들을 저장
  • 사용자의 수만큼 세션 객체가 생기기 때문에 최소한의 데이터만 저장한다
    그래서 서버 부담이 가장 큰 저장소이다
  • 가장 쉬운 저장소

4. request

  • 요청할 때마다 생기는 저장소 요청마다 서로 독립적이다
  • 하나의 jsp파일이 담당한다
  • jsp파일 안에서 request 객체를 사용한다
  • 데이터도 저장할 수 있다
  • 한 jsp파일에서 사용할 수 있고 여러 jsp파일을 거쳐 사용될 수 있다(다른 jsp파일로 넘김)
  • 가장 부담이 적은 객체

저장소 정리

저장소에 저장할 때 사용하는 메서드

URL 패턴

  • @WebServlet으로 서블릿을 URL 맵핑할 때 사용
    @WebServlet(urlPatterns={"/hello", "/hello/*"}, loadOnStartup=1)
    loadOnStartup : 미리 초기화, 빠른 init을 위해 1은 우선순위

패턴 종류

1. exact mapping

  • 1순위
  • 정확히 일치하는 것
  • 패턴을 찾지 못하면 2순위의 패턴에 일치하는 것으로 이동

2. path mapping

  • 2순위
  • 가운데 주소가 일치하는 것

3. extension mapping

  • 3순위
  • 마지막 패턴이 일치하는 것

4. default mapping

  • 4순위
  • 모든 순위
  • 앞의 패턴에 모두 일치하지 않을 경우 사용되는 패턴

  • 만약 요청이 /hello로 들어왔다면 servletMappings 에서 일치하는 key를 찾아 value에 해당하는 서블릿으로 이동한다
  • 이 경우 HelloServlet이라는 서블릿이 있기 때문에 그 서블릿을 children 에서 찾는다
  • HelloServlet이 있기 때문에 요청을 처리한다
  • 만약 요청 url이 존재하지 않는다면 default 서블릿에 맵핑되어 default 서블릿이 요청을 처리한다 = 우선순위가 가장 낮다
  • 스프링은 DispatcherServlet을 사용한다
  • 스프링에서는 default 만 등록되어 있기 때문에 모든 요청은 default로 연결되어 있는 DispatcherServlet이 처리한다
  • DispatcherServlet 내부에서 mapping을 처리한다

EL(Expression Language)

  • 원래 서블릿에 값을 쓰려면 <%=값%> 과 같이 적어야 했다
  • EL을 사용하면 ${값} 과 같이 바꿀 수 있다
person.getCar().getColor()=<%=person.getCar().getColor()%> <br>
person.getCar().getColor()=${person.getCar().getColor()} <br>
person.getCar().getColor()=${person.car.color} <br>  

name=<%=request.getAttribute("name")%> <br>   
name=${requestScope.name} <br>
name=${name} <br>

id=<%=request.getParameter("id")%> <br>
id=${pageContext.request.getParameter("id")} <br>
id=${param.id} <br>

"1"+1 = ${"1"+1} <br> 2출력, ""안의 숫자를 숫자로 변환함
"1"+="1" = ${"1"+="1"} <br> 11출력
"2">1 = ${"2">1} <br>   true
null = ${null}<br>  null 출력 안함 
null+1 = ${null+1} <br> 1
null+null = ${null+null} <br> 0 
"" + null = ${""+null} <br> 0
""-1 = ${""-1} <br> -1
empty null=${empty null} <br> true
empty list=${empty list} <br> true
null==0 = ${null==0} <br> false
null eq 0 = ${null eq 0} <br> false
name == "남궁성"=${name=="남궁성"} <br> 
name != "남궁성"=${name!="남궁성"} <br>
name eq "남궁성"=${name eq "남궁성"} <br>  
name ne "남궁성"=${name ne "남궁성"} <br>  ne는 not equal
name.equals("남궁성")=${name.equals("남궁성")} <br>   

EL 사용방법

  • 저장소에 저장되어 있는 변수들을 사용하기 위해서 저장소.변수이름 으로 사용한다
  • 저장소명 생략 가능
    생략한다면 page-request-session-appication 저장소를 차례로 검색
  • request.getAttribute("name")을 EL에서는 사용할 수 없다
    지역변수이기 때문,
    그래서 어느 저장소에 있는지 명시하거나 pageContext.request.getParameter("id")
    저장소 부분을 생략하여 사용한다 param.id

JSTL (JSP Standard Tag Library)

  • 기존 jsp에선 html부분과 자바 코드를 분리해야하기 때문에 복잡하다
  • 블록 구성을 깨지 않고 if문 등의 자바 코드들을 태그화 하기 위해 사용하는 것이 JSTL이다
<%@ taglib prefix="c"   uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
	<title>JSTL</title>
</head>
<body>
<c:set var="to"   value="10"/> //EL은 lv를 사용할 수 없기 때문에 저장소에 우선 저장하는 것이다
<c:set var="arr"  value="10,20,30,40,50,60,70"/> 
<c:forEach var="i" begin="1" end="${to}"> //1부터 10까지 출력
	${i}
</c:forEach>
<br>
<c:if test="${not empty arr}"> //배열이 비어있지 않으면
	<c:forEach var="elem" items="${arr}" varStatus="status"> //배열의 값들을 elem에 하나씩 넣어서 출력
    //status에는 count와 index가 있는데 count는 1부터 시작, index는 0부터 시작. 
    //아래 출력문에 포함해서 출력
		${status.count}. arr[${status.index}]=${elem}<BR>
	</c:forEach>
</c:if>	
<c:if test="${param.msg != null}"> //url로 호출할 때 msg 파라미터에 값이 입력되었는지 확인하고 
	msg=${param.msg} //있으면 출력하는데 태그가 있다면 태그가 해석되어 적용되어 출력되고
	msg=<c:out value="${param.msg}"/> //out은 만약 태그와 함께 입력되었다면 태그를 무시하고 문자로 출력한다
</c:if>
<br>
<c:if test="${param.msg == null}">메시지가 없습니다.<br></c:if> //msg가 없으면
<c:set var="age" value="${param.age}"/> //저장소에 저장하고
<c:choose>
	<c:when test="${age >= 19}">성인입니다.</c:when> //위에 저장된 age의 값 비교
	<c:when test="${0 <= age && age < 19}">성인이 아닙니다.</c:when>
	<c:otherwise>값이 유효하지 않습니다.</c:otherwise>
</c:choose>
<br>
<c:set var="now" value="<%=new java.util.Date() %>"/>
Server time is <fmt:formatDate value="${now}" type="both" pattern="yyyy/MM/dd HH:mm:ss"/>	
</body>
</html>	

Filter

  • 공통적인 요청 전처리와 응답 후 처리에 사용

  • 로깅, 인코딩 등

    서블릿들이 있을 때 모든 서블릿에 공통으로

    1. 전처리
    2. 처리
    3. 후처리

    과정이 반복된다
    처리 부분만 각 서블릿이 상이하고 전처리와 후처리가 중복될 경우 따로 하나로 묶어서 관리한다

    • 서블릿의 코드가 간결해지고 중복이 제거된다
    • filter -> servlet -> filter
  • filter를 여러 개 적용할 수 있다

    filter1 전처리 -> filter2 전처리 -> servlet 처리 -> filter2 후처리 -> filter1 후처리

수행시간을 측정하는 filter

  • 필터를 만들면 일일이 같은 내용을 적지 않아도 공통으로 사용할 수 있다
  • 모든 요청마다 소요시간을 콘솔에 나타내준다
@WebFilter(urlPatterns="/*")
public class PerformanceFilter implements Filter {
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// 초기화 작업
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		// 1. 전처리 작업
		long startTime = System.currentTimeMillis();

		// 2. 서블릿 또는 다음 필터를 호출
		chain.doFilter(request, response); 
		
		// 3. 후처리 작업
		System.out.print("["+((HttpServletRequest)request).getRequestURI()+"]");
		System.out.println(" 소요시간="+(System.currentTimeMillis()-startTime)+"ms");
	}

	@Override
	public void destroy() {
		// 정리 작업
	}

}
profile
안녕하세요. Chat JooPT입니다.

0개의 댓글