sitelink1 | |
---|---|
sitelink2 | |
sitelink3 | |
sitelink4 | |
sitelink5 | |
sitelink6 |
javax.servlet.http.HttpServlet
과 jakarta.servlet.http.HttpServlet
중 어떤 패키지를 사용해야 할지 모르는 상황에서 코드 한 벌로 양쪽 WAS에서 동작하게 만들고 싶을 때, 현실적으로 가능한 방법은 다음과 같습니다:
✅ 해결 전략
???? 1. 빌드 타임에 결정 → 멀티 빌드 구조 사용
하나의 소스에서 빌드 시점에 어떤 WAS(또는 어떤 서블릿 API)를 대상으로 할지 결정하는 구조로 설계합니다.
???? 방법: Gradle의 sourceSet
또는 build variant
활용
???? 디렉토리 구조 예:
src/
common/java/ ← 공통 로직
javaee/java/ ← javax.servlet 기반 코드
jakarta/java/ ← jakarta.servlet 기반 코드
???? build.gradle.kts
예:
val useJakarta = project.hasProperty("jakarta")
sourceSets {
val main by getting {
java {
if (useJakarta) {
setSrcDirs(listOf("src/common/java", "src/jakarta/java"))
} else {
setSrcDirs(listOf("src/common/java", "src/javaee/java"))
}
}
}
}
dependencies {
if (useJakarta) {
implementation("jakarta.servlet:jakarta.servlet-api:5.0.0")
} else {
implementation("javax.servlet:javax.servlet-api:4.0.1")
}
}
???? 실행 예:
# Java EE (javax.servlet) 빌드
./gradlew build
# Jakarta EE (jakarta.servlet) 빌드
./gradlew build -Pjakarta
???? 2. 동적 로딩 방식 (Class.forName + Reflection) [⚠️ 비추천]
Object servlet;
try {
// 시도 1: jakarta.servlet
Class<?> clazz = Class.forName("jakarta.servlet.http.HttpServlet");
servlet = clazz.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException e) {
// 시도 2: javax.servlet
Class<?> clazz = Class.forName("javax.servlet.http.HttpServlet");
servlet = clazz.getDeclaredConstructor().newInstance();
}
그러나
extends HttpServlet
구문 자체는 컴파일 타임에 결정되므로, 실제 상속 구조를 동적으로 처리하는 건 불가능합니다. 위 방식은 서블릿 클래스를 감싸는 어댑터 객체 등에서 간접적으로 활용하는 형태에 한정됩니다.
???? 현실적으로는…
**"동일한 서블릿 클래스에서 두 패키지를 동시에 고려해 상속받는 것"**은 불가능합니다.
결국 컴파일 타임에 둘 중 하나를 선택하도록 구조를 만들고, WAS에 맞는 빌드를 수행하는 구조를 택하는 게 가장 현실적입니다.
javax.servlet.http.HttpServlet
과 jakarta.servlet.http.HttpServlet
둘 다 지원하기 위한 상속 분리 구조 예제를 아래에 설명드릴게요.
???? 목표
-
공통 서블릿 로직은 중복 없이 유지하고,
-
두 개의 WAS (Java EE, Jakarta EE) 모두에서 사용할 수 있도록
-
각각
javax
/jakarta
패키지에 맞는 클래스를 제공
???? 디렉토리 구조
src/
main/
java/
common/ ← 공통 서블릿 로직
MyServletLogic.java
javaee/ ← javax.servlet 전용 클래스
JavaxServletWrapper.java
jakartaee/ ← jakarta.servlet 전용 클래스
JakartaServletWrapper.java
???? 공통 서블릿 로직 클래스 (common/MyServletLogic.java
)
package common;
import java.io.IOException;
import java.io.PrintWriter;
public class MyServletLogic {
public void handleRequest(Object request, Object response) {
try {
Object writer = response.getClass().getMethod("getWriter").invoke(response);
writer.getClass().getMethod("println", String.class).invoke(writer, "Hello from common logic!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
???? javax.servlet
기반 서블릿 (javaee/JavaxServletWrapper.java
)
package javaee;
import common.MyServletLogic;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JavaxServletWrapper extends HttpServlet {
private final MyServletLogic logic = new MyServletLogic();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
logic.handleRequest(req, resp);
}
}
???? jakarta.servlet
기반 서블릿 (jakartaee/JakartaServletWrapper.java
)
package jakartaee;
import common.MyServletLogic;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JakartaServletWrapper extends HttpServlet {
private final MyServletLogic logic = new MyServletLogic();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
logic.handleRequest(req, resp);
}
}
???? Gradle 빌드 스크립트 예시 (build.gradle.kts
)
val useJakarta = project.hasProperty("jakarta")
sourceSets["main"].java {
setSrcDirs(
listOf(
"src/main/java/common",
if (useJakarta) "src/main/java/jakartaee" else "src/main/java/javaee"
)
)
}
dependencies {
if (useJakarta) {
implementation("jakarta.servlet:jakarta.servlet-api:5.0.0")
} else {
implementation("javax.servlet:javax.servlet-api:4.0.1")
}
}
빌드 명령
# javax.servlet 기반 빌드
./gradlew build
# jakarta.servlet 기반 빌드
./gradlew build -Pjakarta
✅ 요약
항목 | 설명 |
---|---|
공통 로직 | MyServletLogic 클래스에 유지 |
Wrapper 클래스 | 각각 JavaxServletWrapper , JakartaServletWrapper 로 나눔 |
유연한 빌드 | Gradle 플래그에 따라 동적으로 패키지 선택 가능 |
장점 | 코드 중복 방지, 유지 보수 용이 |
GitHub 프로젝트 구조 예제를 아래와 같이 설계했습니다.
이 구조는 javax.servlet.http.HttpServlet
과 jakarta.servlet.http.HttpServlet
둘 다 사용할 수 있도록 상속 분리 방식으로 구성되어 있습니다:
dual-servlet-support/
├── build.gradle.kts
├── settings.gradle.kts
└── src/
└── main/
└── java/
├── common/
│ └── MyServletLogic.java // 실제 로직 구현
├── javaee/
│ └── JavaxServletWrapper.java // javax.servlet 기반 서블릿
└── jakartaee/
└── JakartaServletWrapper.java // jakarta.servlet 기반 서블릿