* 이 게시물은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
(자바 버전 여행 ⑪)
자바 8에서 11로 올리다 NoClassDefFoundError를 만나 당황하는 경우가 많다. 대부분 원인은 하나다 — 11에서 조용히 빠진 것들을 아직 쓰고 있어서다. 이번 편은 무엇이 왜 사라졌고, 어떻게 되살리는지 정리한다.
무엇이 사라졌나
| 빠진 것 | 무엇 | 대안 |
|---|---|---|
| Java EE 모듈 | JAXB(java.xml.bind)·JAX-WS 등 | 별도 의존성(jakarta.*)로 추가 |
| CORBA | 오래된 분산객체 기술 | 사실상 폐기, gRPC 등으로 |
| Applet API | 브라우저 자바 애플릿 | 웹 표준으로 대체(폐기) |
| Java Web Start | jnlp 배포 | jpackage 등으로 |
| JavaFX | GUI 툴킷 | OpenJFX로 분리(별도 추가) |
핵심은, 이것들이 JDK 밖으로 나갔을 뿐 사라진 게 아니라는 것(CORBA·Applet 제외). JAXB나 JavaFX는 의존성으로 다시 넣으면 그대로 쓴다.
가장 흔한 함정 — JAXB
XML을 객체로 바꾸는 JAXB(javax.xml.bind.*)는 자바 8엔 기본 포함이라 그냥 import해서 썼다. 11에선 빠져서, 같은 코드가 이렇게 터진다.
// Java 8: 잘 됨 / Java 11: 컴파일 또는 실행 시 실패
import javax.xml.bind.JAXBContext; // package does not exist
...
// java.lang.NoClassDefFoundError: javax/xml/bind/JAXBContext
해결은 간단하다. 빠진 걸 의존성으로 다시 넣으면 된다.
<!-- Maven -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.5</version>
</dependency>
직접 해보기 — 사라진 API 재현하고 되살리기
말로만 들으면 안 와닿는다. 직접 깨뜨렸다가 고쳐 보자. (자바 8과 11 전환은 2편 참고)
- 자바 8로
JAXBContext.newInstance(...)를 쓰는 짧은 클래스를 만들어 실행되는 걸 본다. sdk use java 11...로 11로 바꾼 뒤 같은 코드를 실행 →NoClassDefFoundError확인.- 위 의존성을 추가(또는 jar를 클래스패스에 넣고)하고 다시 실행 → 되살아나는 걸 확인.
// Convert.java — 객체 하나를 XML로
import javax.xml.bind.*;
import java.io.StringWriter;
public class Convert {
static class Book { public String title = "Java"; }
public static void main(String[] args) throws Exception {
var ctx = JAXBContext.newInstance(Book.class);
var w = new StringWriter();
ctx.createMarshaller().marshal(new Book(), w);
System.out.println(w);
}
}
실행해 보면 이렇게 나온다.
여기서 조금씩 바꿔 보자.
java -version으로 지금 버전을 확인하며 8↔11을 오가 본다.- 에러 메시지의
javax/xml/bind경로를 보고, 왜 이게 ‘패키지가 사라졌다’는 뜻인지 읽어 본다.
한 걸음 더 — 스프링 등 프레임워크는 이런 의존성을 알아서 넣어주는 경우가 많다. 내 프로젝트의 빌드 파일에서 jakarta 관련 의존성을 찾아보자.
✅ 확인 — 11에서 뭐가 왜 빠졌는지, javax.xml.bind 오류를 어떻게 되살리는지 설명할 수 있으면 모듈 C는 끝이다.
왜 뺐나 — JDK 다이어트
Java EE·CORBA 같은 건 이미 다른 생태계(Jakarta EE 등)로 옮겨갔거나 거의 안 쓰였다. JDK에 계속 품고 있는 건 유지보수 부담이라, 모듈 시스템(8편)을 계기로 JDK 밖으로 덜어냈다. ‘표준 라이브러리를 가볍고 집중되게‘가 방향이다. 그래서 사라진 게 아니라, 필요하면 의존성으로 다시 붙이면 된다.
되살리는 법 — 의존성으로
빠진 걸 쓰려면 빌드에 의존성을 추가한다. JAXB는 jakarta.xml.bind + 구현체, JAX-WS도 별도, JavaFX는 OpenJFX로. 즉 ‘삭제’가 아니라 ‘JDK 밖으로 이사‘했을 뿐이다. Maven·Gradle에 좌표만 넣으면 예전처럼 쓴다.
<!-- Maven — 사라진 JAXB 되살리기 -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.0</version>
</dependency>
8 → 11 마이그레이션 체크리스트
NoClassDefFoundError·ClassNotFound→ 빠진 모듈을 의존성으로 추가.sun.*내부 API 접근 코드를 걷어낸다.--add-modules임시방편은 지양 — 11엔 그 모듈 자체가 없다.- 서드파티 라이브러리를 최신화(구버전이 제거된 API를 참조할 수 있음).
- 테스트로 확인.
그 다음 — 17로 갈 땐 강한 캡슐화
11까지는 ‘빠진 걸 되살리기’가 핵심이었다면, 17로 갈 땐 강한 캡슐화(16편)라는 더 큰 벽이 있다. 내부 API 접근이 아예 막힌다. 그래서 8→17을 한 번에 하기보다, 11을 경유하며 이 단계들을 나눠 넘는 게 안전하다. 마이그레이션은 계단이지 점프가 아니다 — 이 원칙 하나만 지켜도 사고가 확 준다.
여기까지가 자바 9~11이다. 모듈로 구조를 정리하고, var로 문법을 다듬고, 표준 API를 채우고, 낡은 짐을 덜어냈다. 다음 모듈부터는 자바가 언어 문법 자체를 현대화하는 12~17로 넘어간다. 첫 편은 switch의 부활이다.
🔧 현장에서 — javax가 사라지면 한 줄이 아니라 연쇄로 터진다
실제로 8→21 · Boot 2→3 마이그레이션에서 가장 애를 먹은 게 이 javax.* 제거였다. 한 곳만 고치면 되는 게 아니라, 그 API를 끌어오는 여러 라이브러리의 호환성 문제가 연쇄로 터져 최초 원인이 잘 안 보였다. SpringFox처럼 더는 안 맞는 건 아예 제거하고 OpenAPI 계열로 옮겼다.
교훈 — 제거된 API는 import 한 줄을 고치는 게 아니라 그걸 끌어오는 의존성 전체를 봐야 한다. (전체 과정은 24편)
자주 묻는 질문 (FAQ)
자바 8에서 11로 올렸더니 컴파일이 안 돼요.
JAXB 같은 javax.* API가 11에서 빠져서 그렇습니다. jakarta.xml.bind 의존성을 추가하면 되살아납니다.
제거된 기능은 영영 못 쓰나요?
CORBA·Applet은 사실상 폐기지만, JAXB·JavaFX는 별도 의존성으로 다시 넣어 그대로 씁니다.

답글 남기기