제5장 JSP 엔진

내용 목차

5.1. 개요
5.2. Apache Tomcat Jasper
5.3. JSP 엔진 기능
5.3.1. JSP Graceful Reloading
5.3.2. JSP 프리컴파일
5.3.3. 메모리에서의 JSP 컴파일 및 실행 기능
5.4. JSP 엔진 설정
5.4.1. 웹 엔진 레벨에서 설정
5.4.2. jeus-web-dd.xml 설정
5.4.3. JSP 하위 호환성을 위한 웹 컨텍스트 레벨의 옵션 설정
5.4.4. jarscan.properties 파일 설정

본 장에서는 JSP 엔진의 개념과 기능, 설정 방법에 대해 설명한다.

JSP 엔진은 웹 엔진에 내부적으로 포함되어 있는 형태이며 웹 컨텍스트마다 하나씩 존재한다. 본 장에서는 JSP 표준에 대한 설명은 포함하지 않는다. JSP 작성 등에 대한 자세한 정보는 JSP 표준을 참고한다.

JSP 엔진은 웹 클라이언트가 JSP 페이지를 요청했을 때 해당 페이지를 찾아서 서블릿으로 전환하는 역할을 한다. 서블릿으로 전환하는 과정에서 Java 파일과 SMAP 파일을 생성하고, Java 파일을 컴파일해서 서비스에 이용할 클래스 파일을 생성한다.

JSP 프리컴파일 기능을 사용하지 않았다면 JSP 컴파일은 최초 요청 시점에 수행한다. 따라서 최초 요청의 경우에는 OS 파일 시스템에 접근하는 일이 빈번하게 발생하기 때문에 응답시간이 늦을 수 있다.

NAS(Network Attached Storage)를 사용하는 환경에서 태그와 JSP include 관계때문에 컴파일해야 할 파일이 많은 경우에는 NAS로 많은 요청이 집중되서 응답시간이 지체되는 현상이 발생할 수 있다. 또한 NAS 드라이버 동작에 따라 java.io.IOException이 발생하지 않고 사이즈가 0인 JSP 파일을 읽는 경우도 발생한다. Jasper에서는 JVM File I/O API를 통해서 파일을 읽기 때문에 이런 경우 JSP 파일에 대한 파싱이 실패할 수 밖에 없다.

웹 컨텍스트 내에서의 JSP

JSP 파일은 웹 컨텍스트의 루트 아래에 존재한다. 별도로 디렉터리를 생성해서 패키징할 수도 있고 Servlet 3.0부터 정의한 META-INF/resources/ 디렉터리 아래에 패키징할 수도 있다.

WEB-INF/lib/ 아래의 *.jar 라이브러리 파일들 안에 META-INF/resources/ 디렉터리가 있는 경우에도 JSP 파일을 찾을 수 있다. 단, WEB-INF/ 디렉터리 아래에 있는 JSP 파일은 서비스되지 않는다. 웹 클라이언트가 WEB-INF/ 아래의 파일들에 보안상 접근할 수 없다. 웹 컨텍스트 내부 구조에 대한 자세한 내용은 “3.2.2. 웹 컨텍스트 내부 구조(WAR 파일 구조)”를 참고한다.

참고

META-INF/resources/에 대한 자세한 내용은 https://jakarta.ee/specifications/servlet/4.0/apidocs/javax/servlet/servletcontext의 리소스 관련 API 설명을 참고한다.

[주의]

내부 테스트 결과 Oracle JDK 6에서 제공하는 javac 라이브러리가 thread-safe하지 않다. 이에 따라 jeus.servlet.jsp.compile-java-source-concurrently 프로퍼티를 제공한다(기본값: false). 요청 스레드가 .java 파일을 컴파일할 때는 JVM Scope의 Lock을 잡고 수행한다. 만약 JDK에서 제공하는 javac 라이브러리가 thread-safe한 경우에는 다음과 같이 jeus-web-dd.xml에 true로 설정해서 사용해도 된다. 단, 반드시 단일 스레드가 아닌 멀티 스레드 상황에서 JSP 컴파일이 정상적으로 되는지 테스트해야 한다.

<properties>
    <property>
        <key>jeus.servlet.jsp.compile-java-source-concurrently</key>
        <value>true</value>
    </property>
</properties>

JEUS는 기존부터 Tomcat의 JSP Parser인 Japser를 도입해서 사용했으나 패키지 이름을 변경하고 이를 jeus.jar에 포함하여 제공하였다. JEUS 21은 Tomcat 8 기반의 Jasper를 사용하며 org.apache.jasper 패키지의 이름를 그대로 유지하고 별도의 jasper.jar 라이브러리로 패키징하여 제공한다. 이 라이브러리는 다음 경로에 위치한다.

$JEUS_HOME/lib/system/jasper.jar

Jasper를 사용할 때는 다음 사항에 주의한다.

  • jasper.jar는 일부 JEUS에 맞춰서 수정한 것이므로 Tomcat의 것으로 덮어쓰면 안 된다. 이 경우 서버 기동이 실패한다.

  • 웹 애플리케이션에서 WEB-INF/lib 내에 Tomcat의 jasper.jar를 사용하는 경우 jeus-web-dd.xml의 <webinf-first> 옵션을 true로 설정해야 한다. 그렇지 않으면 JEUS에서 사용하는 jasper.jar의 클래스를 사용하게 되어 기대하는 동작과 다를 수 있다.

Tomcat Jasper와 JEUS Jasper의 차이점

위에서 언급한 바와 같이 JEUS는 Tomcat에서 제공하는 Japser를 일부 수정해서 제공한다. 그러므로 사용할 때 다음의 사항을 고려해야 한다.

  • JEUS는 In-memory JSP Compiliation 기능과 같이 Tomcat Jasper에서 제공하지 않는 기능을 제공한다.

  • JEUS에서는 Tag Handler Pool, PageContext Pool을 사용하지 않는다.

Java 소스 컴파일할 때 64KB 메소드 크기 제한 문제

JSP를 작성할 때 하나의 .jsp 파일 내에 내용이 너무 많은 경우 이를 .java 파일로 생성하면 내부 메소드 크기가 64KB를 초과할 수 있다. 이 경우 해당 .java 파일은 Java Languague 표준 규약을 어긴 것이므로 Java 컴파일러로 컴파일되지 않는다. 그러나 .jsp의 내용 중 SQL 문이나 HTML 태그와 같은 문자열이 대부분을 차지하는 경우에는 64KB 제한을 피해갈 수 있다.

애플리케이션이 가지고 있는 web.xml에 다음과 같이 설정한다.

<servlet>
    <servlet-name>jeus.servlet.servlets.JspServlet</servlet-name>
    <servlet-class>jeus.servlet.servlets.JspServlet</servlet-class>
    <init-param>
      <param-name>genStringAsCharArray</param-name>
      <param-value>true</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>jeus.servlet.servlets.JspServlet</servlet-name>
    <url-pattern>*.jsp</url-pattern>
</servlet-mapping>

문자열이 아니라 실제로 Java 코드량이 많은 경우에는 컴파일이 실패할 수 밖에 없다. 이때는 <jsp:include>를 사용하여 응답 내용을 분리해서 구현한다. 다음은 <jsp:include>를 사용한 예이다.

<body>
    Template Page<br/>
    <jsp:include page="module_one.jsp" />
    <jsp:include page="module_two.jsp" />
</body>

JSP 엔진은 Graceful Reloading, 프리컴파일, 메모리에서의 JSP 컴파일 및 실행 기능 등을 제공한다.

JSP 파일은 사용 목적이 비즈니스 로직보다는 사용자 뷰에 가깝기 때문에 실제 서비스 운영 중에도 수정할 수 있기를 원하는 경우가 대부분이다. 이런 이유로 JSP를 많이 사용하는 웹 애플리케션의 경우 WAR 형태보다는 디렉터리 형태로 deploy한다.

NAS를 사용해서 여러 개의 웹 엔진이 이를 공유해서 하나의 소스로 동일한 서비스를 수행하는 경우 JSP 컴파일 시점에 필요한 OS 파일 시스템 접근 작업으로 인해 서비스 지연 현상 또는 에러가 발생할 수 있다. 이러한 문제점을 해결하기 위해서 JSP 프리컴파일, JSP Graceful Reloading 기능을 제공하지만 이를 좀 더 근본적으로 해결하기 위해서 메모리에서의 JSP 컴파일 및 실행 기능을 제공한다.

JSP 엔진은 요청 처리 스레드에서 JSP 컴파일의 결과물인 .java 파일 및 .class 파일을 저장하지 않고 모두 메모리에 두고 사용한다. 따라서 .jsp 파일의 메타 데이터와 .jsp 파일의 콘텐츠를 읽는 작업 이외에는 파일 시스템에 접근하지 않는다. 이렇게 파일 시스템 접근을 최소화하여 JSP 컴파일 타임에 발생할 수 있는 서비스 지연을 최소화하였다.

그러나 .java 파일 및 .class 파일은 추후 다른 용도를 위해서 필요하다. 이 파일들은 요청 처리 스레드가 아닌 JSP 엔진마다 하나씩 가지고 있는 Background Thread를 이용해서 파일 시스템에 사용한다. 순차적으로 처리하기 때문에 파일 I/O가 파일 시스템에 한 번에 몰릴 확률이 낮다. 하지만 .smap 파일은 사용하지 않는다. 만약 .smap 파일이 필요한 경우에는 기존의 JSP 컴파일 방식을 사용해야 한다.

JSP 엔진은 이 기능을 기본적으로 사용한다. 만약 기존과 같은 방식을 원하는 경우에는 jeus-web-dd.xml에 설정할 수 있다. 자세한 설정 사항은 “5.4.2. jeus-web-dd.xml 설정”을 참고한다.

JSP 엔진은 WebAdmin을 사용하거나 각 웹 애플리케이션의 jeus-web-dd.xml에 설정할 수 있다.

웹 엔진의 JSP 엔진 설정은 WebAdmin을 통해서 해야 한다.

  1. WebAdmin 왼쪽 메뉴에서 [서버]를 선택하면 서버 목록 조회 화면으로 이동한다. 서버 목록에서 실행할 서버를 선택하면 서버 설정 화면으로 이동한다. 설정 화면에서 [Engine] > [Web Engine] > [Jsp Engine] 메뉴를 선택한다.


  2. Jsp Engine 화면에서 기본 정보를 설정하고 [확인] 버튼을 클릭한다.


    다음은 설정 항목에 대한 설명이다.

    항목설명
    Jsp Work Dir

    JSP에서 생성된 Java 소스 파일들이 저장되는 루트 디렉터리를 지정한다. 설정했다고 루트 디렉터리에 바로 파일을 생성하는 것은 아니다. JSP 엔진이 속한 도메인, 서버 이름, 그리고 웹 애플리케이션 이름으로 디렉터리를 생성한 후 그 아래에 파일을 생성한다. 즉, 서로 다른 웹 엔진 간에 클래스 파일들을 공유하지 않는다.

    'Compile Output Dir' 항목을 설정하지 않은 경우 클래스 파일도 동일한 위치에 생성된다. 기본적으로 다음과 같은 위치에 생성된다.

    • EAR 애플리케이션

      INTERNALGENERATED_HOME/ear1/web1/__jsp_work
    • Standalone 웹 모듈

      INTERNALGENERATED_HOME/web1/__jsp_work
    Java Compiler

    Java Compiler 실행 명령어이다. JSP의 생성된 Java 소스를 서블릿 클래스로 컴파일할 컴파일러를 지정한다.

    기본 설정이 가장 효율적이기 때문에 사용하지 않는 것을 권장한다.

    Compile Output Dir

    JSP Parser가 생성한 Java 파일을 컴파일한 클래스 파일의 위치이다. 이 클래스 파일을 실제로 서비스에 사용한다.

    설정하지 않는 경우 Java 파일과 동일한 위치에 생성된다.

    Compile OptionJava 컴파일러 실행 옵션이다.
    Compile Encoding

    JSP Parser가 생성한 Java 파일의 인코딩을 지정한다. Java 컴파일러의 encoding 옵션으로 사용한다.

    JEUS 21은 더이상 이 값을 참조하지 않으며 JSP page encoding으로 결정된 값을 사용한다.

    Check Included Jspfile

    JSP 엔진은 기본적으로 요청한 JSP 페이지의 변경 여부뿐만 아니라 <%@ include file=”xxx.jsp” %> directive로 include된 모든 JSP 파일들 및 태그 파일들에 대해 변경되었는지 확인해서 컴파일한다.

    만약 false로 설정하면 요청한 JSP 페이지의 변경 여부만 확인하여 컴파일한다.

    Keep Generated

    JSP Parser가 생성한 Java 파일 및 SMAP 파일을 유지하는 옵션이다.

    디버깅할 때 유용하고, false로 설정하면 파일을 생성한 후 삭제하는 것이기 때문에 특별한 이유가 없다면 성능을 위해 별도의 설정을 하지 않는 것을 권장한다.

    Graceful Jsp ReloadingJSP 파일이 변경된 경우 지정된 주기마다 이를 감지하여 JSP 페이지 인스턴스를 새로 생성한다.
    Graceful Jsp Reloading PeriodGraceful Jsp Reloading이 동작되는 주기를 설정한다. (단위: ms)
    Use In Memory Compilation서비스 중인 JSP 파일을 새로 컴파일해야 할 때 .java 및 .class 파일을 메모리에 생성해서 컴파일하고 이를 실행하는 기능이다. 자세한 사항은 “5.3.3. 메모리에서의 JSP 컴파일 및 실행 기능”을 참고한다.

JEUS 4 및 5에서는 사용자의 편의성과 Servlet 2.3 이전에 개발된 애플리케이션을 위해서 표준이 아닌 기능도 제공하고, JSP 문법 체크도 최신 스펙에 비해서 엄격하지 않게 적용하였다. 하지만 JEUS 6부터는 좀 더 엄격한 문법 체크와 JSP 2.1 기능을 제대로 지원하기 위해 Jasper 기반의 JSP Parser로 교체하였다. 하지만 기존 JSP에 대한 하위 호환성을 위해서 기존의 JSP Parser도 지원한다.

따라서 JEUS 4 및 5에서는 문제가 없던 웹 모듈을 deploy하면 여러 가지 에러가 발생할 수 있다. JSP 2.1 등의 최신 스펙을 사용하려면 JSP 컴파일할 때 발생하는 에러 메시지를 확인 후 수정해서 업그레이드해야 한다.

만약 최신 스펙이 필요하지 않고 기존에 개발된 모듈을 그대로 사용하려면 다음과 같이 jeus-web-dd.xml에 JEUS 4 및 5 호환의 JSP Parser가 해당 웹 컨텍스트에만 적용되도록 설정할 수 있다.


jeus-web-dd.xml에 설정한 옵션은 해당 웹 컨텍스트에만 적용되고, 웹 엔진이나 가상 호스트 단위로도 옵션을 설정할 수 있다. 웹 엔진 레벨에서 옵션을 적용하려면 다음과 같은 VM 옵션을 설정한다. VM 옵션의 설정은 JEUS Server 안내서”의 “2.2. 서버 추가”를 참고한다.

-Djeus.servlet.jsp.modern=false

VM 옵션은 가상 호스트, 웹 컨텍스트 레벨에서 치환이 가능하며 jeus-web-dd.xml에 설정한 옵션이 최종적으로 적용된다. jeus-web-dd.xml에 옵션 설정이 없다면 상위의 기본 설정이 적용된다.

참고

이 옵션의 사용을 절대로 권장하지 않으며 부득이하게 하위 호환성이 필요한 경우만 적용하고, 신규 개발은 새로운 애플리케이션으로 표준을 준수하여 개발할 것을 권장한다. 이 호환성 옵션은 차기 버전이나 다음 Fix에서 삭제될 수 있다. 나머지 옵션에 대한 자세한 내용은 JEUS Reference 안내서”의 “1.7. 웹 엔진 프로퍼티”를 참고한다.