[스프링부트] 스프링부트에서 JSP 사용하기

업데이트:



🟢 Spring Boot에서 JSP 사용

일반적으로 Spring Boot 환경에서 프로젝트를 만들때 상황에 따라 다르겠지만 보통 Spring Boot + 내장 톰캣 + Thymeleaf의 기술스택을 고려한다. 단순 스프링 환경에서는 JSP를 사용하는 경우가 많이 있지만, 스프링부트 환경에서 프로젝트를 구현할 때는 JSP를 거의 사용하지 않는다. 그렇다면 왜 스프링 부트 환경에서는 JSP를 사용하지 않는 것일까? 스프링 부트에서 JSP를 사용하지 않는 이유는 많지만 크게 보면 세가지이다.



1️⃣ Spring Boot에서 더이상 JSP를 공식 지원하지 않는다.



2️⃣ Spring Boot는 내장톰캣 사용이 Default이므로 JSP를 사용하기 위해서는 추가적인 많은 설정과 불편함이 따른다.

3️⃣ 강제로 JSP사용을 위한 설정을 해주더라도 페이지 방문시 Load되는 시간이 필요하다.



위와 같은 이유로 스프링부트에서는 JSP를 잘 사용하지 않는다. 그러나 상황에 따라 스프링부트 환경에서 JSP를 사용해야 할때가 있을 것이다. 예를 들어 회사의 레거시 시스템을 업그레이드 하는 과정을 들 수 있다. 기존 Spring + JSP 구조의 프로젝트에서 SpringBoot로 시스템을 업그레이드 하고싶은데, JSP코드까지 Thymeleaf나 React등의 프론트엔진으로 변경하기에는 시간상 여유가 없는 경우 JSP를 그대로 사용해야 할 수도 있다. 사실 본인이 처한 상황이 그랬다.

레거시 시스템을 바꾸는 것은 매우 신중해야 한다. 기존에 잘 돌아가던 프로그램이 망가질 수도 있고, 코드양이 매우 방대하기 때문에 어떤 구조로 코드들이 얽혀있는지 정확하게 파악하기도 어렵다. 때문에 본인은 Spring Boot 환경으로 전체 프레임워크는 변경하되 JSP 코드는 유지해야만 했다.

그러나 스프링 부트 환경에서 JSP 코드를 유지하기 위해서는 매우 다양한 추가 설정이 필요하다. 이에 대한 자세한 설명은 타임리프 (Thymeleaf)의 모든것 게시글을 참고해보면 좋다.

아무튼 여차저차 스프링 부트 환경에 JSP사용을 위한 설정을 모두 마친뒤, 프로젝트를 배포하고 실행하면 정상동작시킬 수 있다. 그러나 배포 후에 역시나 문제가 생긴다.

바로 JSP 페이지는 최초 배포 후 처음 로드될 때 다른 템플릿 엔진에 비해 로드되는데 걸리는 시간이 매우 길다는 것이다. 그 이유를 파악하기 위해서는 JSP가 무엇인지, 그리고 어떻게 동작하는지 자세히 알 필요가 있다.







✅ JSP 동작 과정

JSP는 동적 페이지를 처리하기 위한 일종의 뷰 템플릿 엔진이다. JSP 코드에 대한 처리요청이 들어오면 JSP는 다음과 같은 과정으로 요청을 처리한다.

1️⃣ 번역 (Translation)

  • 클라이언트가 Hello.jsp를 요청한다.
  • JSP엔진은 Hello.jsp를 서블릿(JAVA 클래스)으로 변환한다.
  • JSP파일이 서블릿코드로 변환되면서 Hello_jsp.java라는 파일이 생성된다.

🔹 관련 클래스 및 기술: Jasper (Apache Tomcat의 JSP 엔진)


2️⃣ 컴파일 (Compilation)

  • 생성된 Hello_jsp.java 파일이 자바 컴파일러(Javac)에 의해 .class 바이트 코드로 변환된다.
  • Hello_jsp.java -> Hello_jsp.class 형태로 변환되며, 이 클래스가 실제 서블릿 객체로 동작한다.

🔹 컴파일된 결과물: Hello_jsp.class
🔹 변경 사항이 없다면 이 과정은 다시 실행되지 않음


3️⃣ 로딩 및 초기화 (jspInit)

  • Hello_jsp.class가 서블릿 컨테이너(tomcat)에 의해 로드된다.
  • 서블릿의 jspInit() 메서드가 실행되면서 초기화 작업이 이루어진다.
  • 이때 jspInit() 메서드는 JSP 파일이 처음 요청될 때만 수행된다.


4️⃣ 실행 (jspService)

  • 클라이언트가 요청을 하면 jspService() 메서드가 실행된다.
  • 이 메서드는 doGet() 또는 doPost() 처럼 HTTP 요청을 받아서 실제 JSP 페이지 로직을 실행하는 역할을 수행한다.
  • 즉, 요청이 올 때마다 jspService() 메서드가 호출된다.

🔹 이 단계는 JSP가 실행될 때마다 반복됨

5️⃣ 소멸 (jspDestroy)

  • 서버가 종료되거나 JSP가 언로드(unload)될 때 jspDestroy() 메서드가 호출된다.
  • 사용하던 리소스(예: DB 연결)를 해제하는 역할.



JSP는 위와 같은 동작을 거쳐 요청을 처리한다. 쉽게 풀어서 설명하면 페이지를 요청하는 그 순간, 1️⃣ 해당 페이지에 대한 java 파일 생성, 2️⃣ 컴파일, 즉 class 파일을 생성, 3️⃣ JspInit() 이 수행된다. 1️⃣2️⃣3️⃣번 과정은 최초 JSP 요청을 수행할 때 딱 한번만 수행되는데 이 과정이 JSP를 사용할 때 최초 로드가 느린 원인이다. 일단 한번 class 파일이 생성되면 JSP파일이 변경되지 않는이상 해당 class 파일을 계속 사용해서 페이지를 로드하기 때문에 이후 부터는 4️⃣번 과정만 수행하여 로드 속도가 빨라진다.

그러나 여기서 드는 한가지 의문점. JSP의 로드방식 때문에 JSP 최초 로드시 속도가 느린 문제가 발생하는거라면, 스프링 부트 환경이 아닌 스프링 환경에서도 JSP를 사용했을 때 최초로드 속도가 느린 현상이 발생해야 하는것 아닌가? 그렇다. 스프링 환경에서는 JSP 사용을 아무도 문제 삼지 않는다. 오히려 스프링 환경을 많이 사용하던 때에는 JSP 사용을 권장하기도 했다.

Spring Boot 환경에서 JSP 로드 속도가 특히 느린 원인은 내장 톰캣 환경에서의 JSP 처리 방식에 있다.







✅ 스프링부트 내장 톰캣

스프링 부트는 기본적으로 내장 톰캣(Tomcat), 제티(Jetty), 언더토우(Undertow) 등의 서블릿 컨테이너를 포함하고 있으며, 이를 통해 애플리케이션을 실행하는 방식이 기존 스프링과 다르다.

기존 스프링 환경은 WAR 배포 방식이 기본이다. WAR 배포 방식은 JAVA 웹 애플리케이션을 패키징 하는데 최적화 되어있고, 웹 구성요소인 JSP, 서블릿, 리스너 등과 서블릿 컨테이너 실행을 위한 설정파일, 라이브러리, 리소스 등을 포함하여 한번에 배포 및 실행이 가능하다. 또한 WAS(외부톰캣)에서 JSP를 Web Application Context 안에서 직접 실행하여 로드 속도가 비교적 빠르다.

그러나 스프링 부트 환경은 JAR 배포 방식이 기본이다. JAR 배포방식은 독립적인 JAVA 애플리케이션을 패키징한 형태로 클래스, 리소스, 라이브러리 등을 포함하고 있어서 배포와 전달에 용이한 특징이 있다.

기존 스프링 WAR배포 환경에서는 src/main/webapp/WEB-INF/views에 JSP를 배치하고 외부 톰캣이 이를 참조하여 실행이 가능했다. 그러나 스프링부트의 JAR 배포 환경에서는 내장 톰캣을 사용하기 때문에 src/main/webapp 경로가 JAR 패키징시 포함되지 않는다. classpath:/static/ , classpath:/templates 등이 기본 리소스 로딩 경로로 설정된다. 하지만 JSP는 classpath 가 아닌 webapp 내에서 동작하면서 서블릿을 통해 class 파일로 변환되는 과정이 필요한데 내장 톰캣은 이를 지원하지 않는다.

때문에 스프링부트 환경의 JAR 배포 방식에서 JSP 파일을 처리하기 위해서는 src/main/webapp 경로에 있는 JSP 파일을 임시로 복사하는 작업이 발생한다. 이런 비효율적인 과정을 거쳐야 하기 때문에 스프링 부트 환경에서 JSP를 사용하면 속도가 느려지는 것이다.

구분 JAR 배포 (Spring Boot 기본) WAR 배포 (전통적인 Spring)
톰캣 내장 톰캣 포함 (spring-boot-starter-web) 외부 톰캣(WebLogic 등) 필요
실행 방식 java -jar myapp.jar WAS에 WAR 파일 배포 필요
View 위치 src/main/resources/templates/ src/main/webapp/WEB-INF/views/
JSP 지원 지원 안됨 ✅ 사용 가능
배포 방식 ✅ 단일 JAR 패키지로 배포 ❌ WAS 서버 설정 필요







✅ JSP 사용을 위한 설정

이제 스프링 부트 환경에서 JSP를 사용하지 말아야 할 이유를 충분히 알았으니 JSP를 사용하지 않는게 좋겠지만 앞서 말한대로 JSP를 사용해야만 하는 상황이 생긴경우에는 스프링부트에서 다양한 설정을 적용해줘야만 한다. JSP 사용을 위한 방식은 크게 두가지 방식이 있다.

  1. 스프링 부트의 내장 톰캣을 사용하면서 JSP를 지원하는 방식
  2. 스프링 부트의 JAR배포를 WAR 배포로 변경하는 방식

각각의 경우를 모두 알아보겠다.



📌 1. 프로젝트 구조

JSP 파일은 반드시 src/main/webapp/WEB-INF/views/ 경로에 위치

📂 src/main/webapp/
    ├── 📂 WEB-INF/
    │   ├── 📂 views/
    │   │   ├── index.jsp
    │   │   ├── error.jsp


📌 2. pom.xml 설정

기본적으로 스프링 부트 프로젝트를 생성할 때 적용하는 spring-boot-starter-web 만으로는 JSP가 동작하지 않는다. spring-boot-starter-tomcatjsp-api 관련 라이브러리를 추가해야 한다.

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- JSP 지원을 위한 Tomcat Embed Jasper -->
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
    </dependency>

    <!-- JSP에서 EL(Expression Language) 사용을 위한 라이브러리 -->
    <dependency>
        <groupId>jakarta.servlet</groupId>
        <artifactId>jakarta.servlet-api</artifactId>
        <scope>provided</scope>
    </dependency>

    <!-- JSTL (JSP 태그 라이브러리) 지원 -->
    <dependency>
        <groupId>jakarta.servlet.jsp.jstl</groupId>
        <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
        <version>3.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.web</groupId>
        <artifactId>jakarta.servlet.jsp.jstl</artifactId>
        <version>3.0.0</version>
    </dependency>
</dependencies>


📌 3. application.properties 설정

# JSP 뷰 리졸버 설정
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

# 톰캣에서 JSP 서블릿이 실행될 때 캐싱 활성화
server.jsp-servlet.init-parameters.development=false

# 정적 리소스 설정 (JS, CSS, 이미지 등)
spring.web.resources.static-locations=classpath:/static/,classpath:/public/,classpath:/resources/

위와 같은 설정을 모두 적용해주면 스프링부트 환경에서 내부 톰캣을 활용한 JSP사용이 가능해진다. 이제 스프링 부트를 실행하고 http://localhost:8080에 본인이 지정해둔 jsp 페이지로 접속하면 정상적으로 출력될 것이다.



위 방식은 Spring Boot의 내부 톰캣을 사용하여 JSP를 그대로 사용하는 방식이지만, 내부 톰캣을 사용하는 것이 아닌 외부 톰캣을 활용한 WAR 배포 방식으로 변경도 가능하다.


📌 1. pom.xml 변경

<packaging>war</packaging>

<dependencies>
    <!-- 기존 의존성 그대로 유지 -->
    
    <!-- 내장 톰캣을 사용하지 않도록 설정 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>


📌 2. SpringBootServletInitializer 상속

SpringBootServletInitializer 를 상속하면 외부 톰캣에서 JSP를 실행할 수 있다.

package kr.co.example.myApp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class myApp extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(SfaApplication.class, args);
    }
}


📌 3. WAR 배포

mvn clean package

이렇게 하면 target/myApp-0.0.1-SNAPSHOT.war 파일이 생성된다. 이를 외부 톰캣에 배포하면 JSP가 정상적으로 동작한다.







✅ 정리

  • 스프링 부트에서는 JSP 사용을 권장 하지 않는다.

  • JAR 배포 시 JSP 지원이 제한적이다.

  • 스프링 부트에서는 JSP 사용을 위한 추가설정이 많이 필요하다.

  • JSP 대신 Thymeleaf, FreeMarker 같은 템플릿 엔진을 사용하면 훨씬 효율적이다.

  • 스프링 부트에서 JSP를 사용하기 위한 방식은 크게 두가지이다.

  • 스프링 부트의 내장 톰캣을 사용하면서 JSP를 지원하는 방식

  • 스프링 부트의 JAR배포를 WAR 배포로 변경하는 방식

  • JSP 로드 성능 개선을 위한 PreCompile 이라는 기법이 존재한다.







댓글남기기