서블릿과 JSP(4) - JSTL(JSP Standard Tag Library)

BackEnd

이번 포스팅에서는 JSTL에 대해서 공부한 것을 정리할 예정이다

 

팀프로젝트로 세미 프로젝트를 진행 했을 당시에

 

자바 스프링에서 jstl을 참 많이 사용했는데 생각해보면 어떤 이론적인면이나 원리를 이해하고 쓰기보다는

동작하는 것에 급급해서 사용했었다

 

이번 기회를 통해서 내가 사용했던 jstl이 어떤 것이고 어떻게 동작하는지 배워보면 좋을 것 같다

 

우선 코드가 필요한 사람이 있을 수 있으니 코드 전문을 올려보겠다

<%@ page contentType="text/html;charset=utf-8"%>
<%@ 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"/>
<c:set var="arr"  value="10,20,30,40,50,60,70"/>
<c:forEach var="i" begin="1" end="${to}">
    ${i}
</c:forEach>
<br>
<c:if test="${not empty arr}">
    <c:forEach var="elem" items="${arr}" varStatus="status">
        ${status.count}. arr[${status.index}]=${elem}<BR>
    </c:forEach>
</c:if>
<c:if test="${param.msg != null}">
    msg=${param.msg}
    msg=<c:out value="${param.msg}"/>
</c:if>
<br>
<c:if test="${param.msg == null}">메시지가 없습니다.<br></c:if>
<c:set var="age" value="${param.age}"/>
<c:choose>
    <c:when test="${age >= 19}">성인입니다.</c:when>
    <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>

 

자 이제 코드를 하나하나 설명을 해보자
  

 기본적으로 <c:set var="" value=""/> 라는 코드를 사용하려면 

 

페이지 상단에 코드를 추가해줘야 사용할 수 있게 되는데 

 

해당 코드 2줄은 거의 기본적으로 들어간다고 생각하고 넣어주는게 좋다

 

<c: set> <c:if> 이런 태그를 쓸 수 있게 해주는게 taglib(태그라이브)이다

 

이렇게 JSP와 HTML이 쪼개져서 사용이 되는데 이 불편함을 해소하고

이런 것들을 태그화 하려고 생긴 것이 JSTL이다

 

여기 코드를 한번 보면 forEach를 사용하고 있다

위에서 to의 value를 10이라고 줬기때문에

1부터 10까지 반복해서 돌리는 것이다

그러면 ${i}가 1 2 3 .... 10 까지 출력된다

 

 

그리고 if문인데 not empty arr 

즉 배열이 비어있지 않으면인데

위에서 배열안에 값을 10 ... 70 까지 넣어줬기 때문에 비어있지 않다

그러므로 for문으로 돌리는 것이다

 

items="${arr}" 이곳에담겨있는 {10, 20 , 30 ... 70}  값들을

var="elem" 으로 하나씩 for문을 돌리게 되는 것이다

 

그래서 이렇게 elem이 값이 10, 20, 30 ... 이렇게 되는 것이다

 

 

그리고 status는 멤버변수로 count와 index를 가지고 있다

count는 1부터 ~ index는 0부터 ~ 출력되어지는데

 

밑에서 status.count , status.index 멤버변수를 선언함으로인해서 

 

이렇게 카운트는 1부터 7까지 출력되어지고 index는 0부터 6까지 출력되어진다

 

그리고 하단에 if문에서는 param의 메세지가 널이 아니면 출력이되고 널이면

메세지가 없습니다 라고 출력되게 코드를 작성해줬다

 

url에 메세지를 입력하게 되면 메세지가 나오는지 한번 적어보자

 

예를 들어 피카츄라고 입력해보자

이렇게 <p>태그를 사용해서 피카츄 라고 적어줬더니 

 

null이 아니므로 2개의 메시지를 출력하게 적혀진 코드가 실행되면서

 

메세지가 2번 찍히게 된다 

 

여기서 out이 무엇이냐면 주소창에 

 

이렇게 작성을 해줬는데 

msg=${param.msg}에서는 <p>태그가 적용되어서 출력이 된 것이다

<p>

피카츄

</P>

단 <p>태그가 적용되었을 뿐 메세지 자체에서는 보이지 않고 공백만 적용되어 출력된다

 

하지만 out은 다르다 내가 작성한 코드에 태그까지 같이 출력되게 된다

 

그래서

이렇게 out을 준 것과 그렇지 않은 것에 차이가 있는 것이다

 

그러면 out은 언제 쓰는 걸까?

 

스크립트로 공격을 하는 경우에 그것을 방어하기 위해서 사용된다 

 

자바스크립트 태그들을 넣어서 명령을 넣어서 공격하는 경우가 있다고 한다

 

그럴 경우를 대비해서 명령이 아니라 단순 태그라는 것을 알려줘야 할 때

out을 사용해서 단순태그로 인식하게 만드는 것이다

 

 

마지막으로 필터에 대해서 알아보자

 

필터가 무엇일까? 

 

서블릿들이 있을 때 서블릿이 시작하기전에 전처리 그리고 후처리가 있는데 

이 코드들이 중복코드가 되어지고 우리는 분리해야한다

 

그럴 때 사용하는 것이 필터이다

 

보통 로깅, 인코딩을 할 경우 사용된다

 

 

MVC패턴에서 배웠던 것 처럼

필터를 이용해서 전처리 서블릿 호출 후처리를 분리할 수 있다

 

필터가 있는 앞쪽으로 전처리와 후처리를 빼줌으로써 중복된 코드가 제거된다

 

이제 남은 것은 처리 작업만 남았으니 간결한 코드가 된다

 

요청이 오면 필터에서 전처리 하고 해당 서블릿을 호출하고 

해당 서블릿에서 처리하고 필터에서 받아서 후처리를 하고 응답을 하게 된다

 

필터 하나를 통해서 전처리와 후처리를 진행하니까 

해당 서블릿으로 던지게되면 서블릿은 단순 처리만 하게 됨으로 훨씬 효율적이고 코드도 간결해진다

 

필터는 이럴 때 사용한다 이전에 공부했던 DispatcherServlet하고 상당히 비슷하다

 

나중에 공부하면서 알게될 AOP가 있는데 필터의 개념과 아주 유사하다

 

우리는 왜 이런 기능들이 나왔는지를 알고 있어야 한다 

지금처럼 중복코드의 분리가 목적이 있었고 그것을 해결하기 위해서 나온 기능들이 앞서 배워왔던 것들이다

 

불편하고 다소 비효율적입 업무를 개선하고 발전시키기 위한 누군가의 노력으로 인해서

조금 더 효율적이게 조금 더 편리하게 개발을 할 수 있게 된 것을 기억하도록 하자

 

 

위 그림을 보면서 전체적으로 필터를 통해 어떻게 진행되는지 한눈에 파악할 수 있다

 

또 알아야 할 것이 보통 필터는 1개이지만 여러개가 사용 될 수 있다

 

이렇게 2개의 필터가 있을 경우 요청과 응답을 처리하는 과정을 보면

 

 

1. 요청이 오면 첫번째 필터에서 전처리를 하고 필터를 호출한다 

2. 두번째 필터에서 받아서 전처리를 하고 서블릿을 호출한다 

3. 서블릿에서 처리하고 두번째 필터에게 던진다

4. 두번째 필터에서 받은 후 후처리를 하고 첫번째 필터에게 던진다

5. 첫번째 필터에서 받아서 후처리를 하고 최종적으로 응답을 하게된다

2가지 필터에 흐름은 그림과 같다

위 코드는 필터의 수행시간을 측정하기 위한 코드이다

 

코드전문을 올려보면

package com.fastcampus.ch2;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

// 필터를 적용할 요청의 패턴 지정 - 모든 요청에 필터를 적용.
@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() {
        // 정리 작업
    }

}

 

이렇게 되어있다

 

그렇다면 수행시간 측정을 어떻게 하면 될까?

 

코드를 보면서 설명해보겠다

 

 

첫번째로 전처리 작업을 시작한다

시작시간을 먼저 구한다

 

그리고 서블릿을 호출한다

 

그리고 후처리 작업을 하면서 현재시간에서 전처리에서 구했던 시작시간을 빼면 소요시간이 나오게된다

 

어찌보면 간단한? 순서라고 할 수도 있다

 

우리가 만약 모든 서블릿에 이 코드를 넣으려면 어떻게 될까?

 

바로 중복된 코드가 남발하게 되는 것이다

 

그래서 PerformanceFileter를 사용해서 만들어놓으면

 

서블릿마다 이 코드를 일일이 넣어주지 않아도 되는 것이다

 

추가하기도 쉽고 변경하기도 쉽고 제거하기도 쉬워서 일의 효율이 더 간결해진다

 

위 그림에서 우측 코드처럼 원래는 매번 서블릿마다 저렇게 전처리 후처리를 일일이 작성해줘야 했는데

 

필터를 사용하면 해당하는 서블릿을 호출만 하면 되는 것이다 

 

이제 실습을 해보도록 하자

 

우선 코드 상단에 있는 

// 필터를 적용할 요청의 패턴 지정 - 모든 요청에 필터를 적용.
@WebFilter(urlPatterns="/*")


이 코드를 보게되면 @WebFilter라고 작성해줬는데 이건 필터를 사용하겠다고 추가한다는 뜻이다

 

서블릿을 사용할 때 @WebServlet이라고 적어준 것 처럼 필터를 사용하겠다고 명시해주고

urlPatterns라는 것이 있는데 이것은 필터를 적용할 요청의 패턴을 지정하는 것이다

 

/* 라고 작성해주면 모든 요청에 대해서 필터를 적용하라는 뜻이다

 

특정해서 하나의 요청이라고 쓰기보다는 이런식으로 패턴화해서 작성하는게 관례이다

 

이제 작동하는지 확인하기 위해서 

 

실행을 해보도록 하자 

 

서버를 껏다 켠 후 기존의 jstl파일에 피카츄라고 적었던 주소를 입력하고 엔터를 쳐보면

 

이렇게 코드가 잘 나오게된다

 

이번에는 el도 한번 해보자

 

 

그리고 새로고침으로 여러번 호출을 해보자

 

이제 로그를 한번보자

 

로그에 이렇게 소요시간이 찍히게 된다

 

제일먼저 톰캣이 실행되면서 최초의 로딩시간 538ms가 나오고 

그 후에 새로고침시 1ms라고 나온다 

 

jstl.jsp는 최초 로딩시 183ms

el.jsp는 최초 로딩시 88ms

그후에는 평균 4ms이다

 

모든 요청에 필터를 적용했기 때문에 

모든 파일에 적용이 쉽게되어 소요시간을 알려준다

 

최초에 필터가 적용되고 로딩되면서 시간이 꽤 걸리지만

적용된 이후에는 소요시간이 대폭 감소된 것을 볼 수 있다

 

필터를 수정하고 싶다면

@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");
}

 

2번은 고정이고 1번과 3번을 내가 원하는 코드로 수정해주면 된다

 

내가 작성한 코드로 실행되는 파일 그리고 프로젝트에서 시간이 얼마나 걸리는지

해당 파일이 로딩되고 새로고침 시 소요되는 시간이 얼마나 걸리는지를 확인할 때 요긴하게 사용될 것으로 기대가 된다

 

개발자들이 흔히 얼마정도 걸리던 소요시간을 몇소 단축했다 라고 하던데

 

그것도 이것으로 측정이 가능할지는? 잘 모르겠지만 아주 신기한 필터임에는 틀림이 없다!

 

이렇게 서블릿을 이해하는 것은 스프링을 이해하는 것에도 큰 도움이 된다

 

서블릿이 발전된게 스프링이기 때문이다

 

이번에 공부한 내용을 잘 기억해서 스프링공부에도 도움이 크게 되길 바란다

 

이상으로 서블릿 JSP에 대한 정리를 마치겠습니다

'BackEnd' 카테고리의 다른 글

@ModelAttribute 그리고 WebDataBinder  (1) 2022.12.21
@RequestParam 에 대한 정리  (0) 2022.12.18
서블릿과 JSP-(3) URL 패턴  (0) 2022.12.17
서블릿과 JSP(2) - 유효범위와 속성  (0) 2022.12.17
서블릿과 JSP  (0) 2022.12.16