본문 바로가기

프로그래밍(TA, AA)/JVM 언어

[JSP] 적절한 include 사용하기

일반적으로 JSP에서 소요되는 시간은 많지 않습니다. JSP가 가장 처음에 호출되는 경우에만 시간이 소요되고, 그 이후의 시간에는 컴파일된 서블릿 클래스가 수행되기 때문입니다. 그럼 JSP의 라이프 사이클을 간단하게 리뷰해 보면 다음과 같습니다. JSP의 라이프 사이클은 다음의 단계를 거칩니다. 




여기서 해당 JSP 페이지가 이미 컴파일되어 있고, 클래스가 로드되어 있고, JSP 파일이 변경되지 않았다면, 가장 시간이 많이 소요되는 페이지 번역, JSP 페이지 컴파일, 클래스 로드, 인스턴스가 생성되는 과정을 생략되고 JSP URL이 호출되면 바로 인스턴스가 생성되어 바로 jspInit 메소드가 호출되는 방식으로 흐름이 이어집니다.


서버의 종류에 따라서 서버가 기동될 때 컴파일을 미리 수행하는 Pre-compile 옵션이 있으며, 이 옵션을 선택해 놓으면 서버에 신 버전을 반영한 이후에 사용자의 응답이 느려지는 현상을 방지할 수 있습니다. 하지만 개발 시에 이 옵션을 켜 놓으면 서버를 기동할 때마다 컴파일을 수행하기 때문에 시간이 오래 걸립니다. 개발 생산성이 떨어지므로 상황에 맞게 옵션을 지정해야 합니다.


JSP의 include 기능을 사용하면, 하나의 JSP에서 다른 JSP를 호출하여 여러 JSP 파일을 혼합하여 하나의 JSP로 만들 수 있습니다. JSP에서 사용할 수 있는 include 방식은 정적인 방식(include directive)과 동적인 방식(include action)이 있습니다.


정적인 방식은 JSP의 라이프 사이클 중 JSP 페이지 번역 및 컴파일 단계에서 필요한 JSP를 읽어서 메인 JSP의 자바 소스 및 클래스에 포함 시키는 방식입니다. 이와 반대로, 동적인 방식은 페이지가 호출될 때마다 지정된 페이지를 불러들여서 수행도록 되어 있습니다.


- 정적인 방식: <%@ include file="관련 URL" %>

- 동적인 방식: <jsp:include page="relativeURL" />


정적인 방식이 동적인 방식보다 빠를 수 밖에 없습니다. 다음은 간단한 예제입니다.


<%@ page import="com.perf.timer.*" %> <%!     StopWatchAverage sw1 = new StopWatchAverage("include 1");     StopWatchAverage sw2 = new StopWatchAverage("include 2"); %> <%     for(int loop=0;loop<10;loop++) {//for loop start         sw1.start(); %> <%@ include file="/jsp/include1.jsp" %> <%     sw1.stop(); %> <%     sw2.start(); %> <jsp:include page="/jsp/include2.jsp" flush="true"> <%     sw2.stop(); %> <%= sw1+"<BR>" %> <%= sw2 %> <% } //for loop end %>


처음 include되는 include1.jsp 파일의 내용은 다음과 같습니다.


<!-- include1.jsp start -->
<%= "inlcude1.jsp<BR>" %>
<!-- include1.jsp end -->


두번째 동적으로 include되는 include2.jsp 파일의 내용은 다음과 같습니다.


<!-- include2.jsp start -->
<%= "include2.jsp<BR>" %>
<!-- include2.jsp end -->


수행된 결과를 보기 전에 메인 JSP가 어떤 형태의 자바 파일로 생성되었는지 확인하면 다음과 같습니다. 참고로 이 예는 톰캣 서버의 경우이며 WAS의 종류에 따라서 이 내용은 달라집니다.


package org.apache.jsp.jsp;
import javax.servlet.*;
// 중략
public final class IncludeMain_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent
{
    // 중략
    public void _jspService(HttpServletRequest request, HttpServletResponse response) throws java.io.IOException, ServletException {
        // 중략
        // 아래 내용은 include1.jsp가 include 된 부분
        out.write("\r");
        out.write("\n");
        out.write("<!-- include1.jsp start -->\r\n");
        out.print("include1.jsp<BR>");
        out.write("\r\n");
        out.write("<!-- include1.jsp end --> \r\n");
        // 중략
        // 아래 내용은 include2.jsp가 include 된 부분
        out.write("\r");
        out.write("\n");
        org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/jsp/include2.jsp", out, true);
        out.write("\r");
        out.write("\n");
        // 이하 생략
    }
}

이와 같이 정적인 include와 동적인 include는 생성되는 자바 파일에서 다르게 구성 됩니다. 이제 메인 JSP를 10회 반복 수행해 보면 결과는 다음과 같습니다.


include1.jsp

include2.jsp

... 중략 ...

include1.jsp

include2.jsp

[include 1] Run Count: 100, Total: 0.88 ms, Average: 0.0088 ms

[include 2] Run Count: 100, Total: 34.23 ms, Average: 0.3423 ms


평균 응답 시간을 보면 동적인 방식이 38배 더 느리게 나타납니다. 즉, 성능을 더 빠르게 하려면 정적인 방식을 사용해야 한다는 의미가 됩니다. 하지만 모든 화면을 이렇게 구성했다가는 잘 수행되던 화면에서 오류가 발생할 수 있습니다. 정적인 방식을 사용하면 메인 JSP에 추가되는 JSP가 포함됩니다. 이 때 추가되는 JSP와 메인 JSP에 동일한 이름의 변수가 있으면 심각한 오류가 발생할 수 있습니다. 그러므로 상황에 맞게 알맞은 include를 선택하여 사용하여야 합니다.