sitelink1  
sitelink2  
sitelink3  
sitelink4  
sitelink5  
sitelink6  

javax.servlet.http.HttpServletjakarta.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.HttpServletjakarta.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.HttpServletjakarta.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 기반 서블릿

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

번호 제목 글쓴이 날짜 조회 수
» [ChatGPT] javax.* 패키지와 jakarta.* 패키지의 혼합 사용 방안 황제낙엽 2025.04.15 27
362 [ChatGPT] *.servlet.http.HttpServlet 클래스를 사용할 수 있는 주요 WAS 목록 황제낙엽 2025.04.14 36
361 [ChatGPT] JDBC API에서 java.sql과 javax.sql 패키지의 차이점 황제낙엽 2025.01.31 131
360 jakarta.servlet 패키지를 이용한 File Upload 구현 (2024.03.03, Servlet 5.0) 황제낙엽 2025.01.02 177
359 [ChatGPT] Java EE가 Jakarta EE로 전환 황제낙엽 2025.01.01 239
358 [ChatGPT] File.delete() 함수로 파일 삭제가 되지 않는 경우 황제낙엽 2024.12.10 266
357 [ChatGPT] json data 의 정렬 (jackson, json simple, gson) 황제낙엽 2024.07.23 92
356 [ChatGPT] <jsp:include>에서 flush="true"를 설정시의 몇 가지 문제점 과 대안 황제낙엽 2024.07.19 208
355 [Copilot] JSP, Servlet, web.xml, mysql 한글 설정 황제낙엽 2024.07.12 179
354 Base64 클래스의 encode, decode 에서 URL-safe 황제낙엽 2024.07.10 273
353 [Copilot] JSP 에서 다른 페이지를 현재 페이지에 포함시키는 문법 (include) 황제낙엽 2024.06.26 224
352 Exception 출력에 대한 고찰 황제낙엽 2024.06.10 177
351 [Gemini] HttpURLConnection 클래스를 이용한 데이터 전송 방식 비교 황제낙엽 2024.03.14 556
350 google-auth-library-oauth2-http 라이브러리 다운로드 황제낙엽 2023.11.19 1427
349 firebase-admin-java 라이브러리 다운로드 (firebase admin sdk library) 황제낙엽 2023.11.19 575
348 Enum 활용 (개인블로그, Effective Java) file 황제낙엽 2023.11.02 523
347 Enum 활용 (우아한기술블로그) file 황제낙엽 2023.11.02 514
346 servlet name 에 camel case 를 사용해서는 안되는 이유 황제낙엽 2023.10.24 209
345 org.apache.commons.fileupload.servlet.ServletFileUpload 이용 예시 황제낙엽 2023.08.25 503
344 ServletFileUpload.parseRequest() 실행 결과 리스트가 비어있는 오류 황제낙엽 2023.08.25 659