← back to posts
·Backend

Backend/Java 버전 변천사 및 Spring Boot 버전 선택 가이드

왜 이 버전을 선택했나요? 라고 물어봐도 대답할 수 있어야 한다!

Spring-Boot

Java 버전 변천사 및 Spring Boot 버전 선택 가이드

Java 버전 개요

Java는 1995년 출시 이후 꾸준히 발전해왔다. 2017년 Java 9부터 6개월 주기 릴리즈 정책으로 전환되었으며, 그 중 일부 버전이 LTS(Long-Term Support) 로 지정되어 장기 지원을 받는다.

LTS 버전은 최소 8년 이상 보안 패치와 버그 수정을 제공받기 때문에, 프로덕션 환경에서는 LTS 버전을 선택하는 것이 원칙이라고 한다.


주요 버전 변천사

Java 8 (2014) — 현대 Java의 시작

이전까지는 함수를 전달하려면 익명 클래스를 사용해야 했다. 아래 예시에서 볼 수 있듯, 코드가 장황하고 가독성이 낮다는 문제가 있었다.

// Java 8 이전 — 익명 클래스
list.sort(new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});

// Java 8 — 람다 표현식
list.sort((a, b) -> a.compareTo(b));

또한 null 처리를 매번 if (x != null)으로 해야 해서 NPE(NullPointerException)가 빈번했다고 한다. 그래서 이를 해결하고자 처음으로 Optional이 도입되어 null 가능성을 타입으로 표현할 수 있게 되었다.

// 이전 — null 체크를 명시하지 않으면 NPE 발생 가능
String name = user.getName();
System.out.println(name.toUpperCase()); // NPE 위험

// Java 8 — Optional로 null 가능성을 명시
Optional<String> name = Optional.ofNullable(user.getName());
name.ifPresent(n -> System.out.println(n.toUpperCase()));

Stream API도 이때 도입되어 컬렉션을 함수형 스타일로 처리할 수 있게 되었다고 한다.

List<String> result = users.stream()
    .filter(u -> u.getAge() >= 20)
    .map(User::getName)
    .collect(Collectors.toList());

핵심 추가 기능

  • 람다(Lambda) 표현식
  • Stream API
  • Optional
  • 인터페이스 default/static 메서드
  • Date/Time API (java.time 패키지)

Java 11 (2018) — 첫 번째 주요 LTS

Java 8 이후 Java 9, 10에서 모듈 시스템 등이 도입되었지만 현장 체감은 적었고, 11이 8 이후 첫 LTS로 자리잡았다.

이때 지역 변수 타입 추론 var가 정식 도입되어 반복적인 타입 선언을 줄일 수 있게 되었다.

// 이전
Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();

// Java 11
var map = new HashMap<String, List<Integer>>();

문자열 처리 편의 메서드도 추가되었다.

"  hello  ".strip();         // trim()의 개선판 (유니코드 공백까지 처리)
"  ".isBlank();              // true
"a\nb\nc".lines().toList();  // ["a", "b", "c"]

또한 이 시점에 Oracle JDK가 유료화되면서 OpenJDK 기반의 무료 배포판인 Eclipse Temurin(Adoptium), Amazon Corretto 등이 주류로 자리잡았다고 한다.

핵심 추가 기능

  • var 키워드 (지역 변수 타입 추론)
  • 문자열 API 개선 (strip, isBlank, lines)
  • HttpClient 표준 도입
  • Oracle JDK 유료화 → OpenJDK 배포판 주류화

Java 17 (2021) — Spring Boot 3.x의 기준 LTS

Java 17이 현재 가장 널리 쓰이는 LTS 버전으로 알려져 있으며, Spring Boot 3.x의 최소 요구 버전이기도 하다.

Record 가 정식 도입되었다. 불변 데이터 클래스를 한 줄로 선언할 수 있어 DTO 작성이 간결해졌다.

// 이전 — Lombok 없이 DTO 작성
public class UserDto {
    private final String name;
    private final int age;

    public UserDto(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
    // equals, hashCode, toString 구현 필요...
}

// Java 17 — record
record UserDto(String name, int age) {}
// constructor, getter, equals, hashCode, toString 자동 생성

Sealed Class가 도입되어 상속 가능한 클래스를 명시적으로 제한할 수 있게 되었다.

sealed interface Shape permits Circle, Rectangle {}

record Circle(double radius) implements Shape {}
record Rectangle(int width, int height) implements Shape {}

// Shape의 구현체는 Circle, Rectangle만 가능하다

Switch 표현식도 개선되어 fall-through 버그를 방지하고 값을 직접 반환할 수 있게 되었다.

// 이전 — break 빠뜨리면 fall-through 버그 발생
String result;
switch (day) {
    case MON: result = "월요일"; break;
    case TUE: result = "화요일"; break;
    default: result = "기타";
}

// Java 17 — switch 표현식
String result = switch (day) {
    case MON -> "월요일";
    case TUE -> "화요일";
    default  -> "기타";
};

핵심 추가 기능

  • Record (정식)
  • Sealed Class (정식)
  • Switch 표현식 (정식)
  • 텍스트 블록 """...""" (정식)
  • 패턴 매칭 instanceof (정식)

Java 21 (2023) — 현재 최신 LTS

Java 21 에서는 Virtual Thread(가상 스레드) 가 가장 큰 변화였다. 기존 Java 스레드는 OS 스레드와 1:1로 매핑되어 수천 개 이상 생성 시 메모리와 컨텍스트 스위칭 비용이 컸다. 그러나 21부터는 가상 스레드가 생겨남에 따라 JVM이 직접 스케줄링하여 수십만 개도 가볍게 처리할 수 있게 되었다고 한다. (나중에 가상 스레드 관련 실습 토이프로젝트 해보면 좋을 것 같기도 하다!)

// 기존 — OS 스레드 1:1 매핑, 수천 개가 한계
ExecutorService executor = Executors.newFixedThreadPool(200);

// Java 21 — 가상 스레드, 수십만 개 가능
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

Spring Boot 3.2부터 설정 한 줄로 가상 스레드를 활성화할 수 있다.

# application.yml
spring:
  threads:
    virtual:
      enabled: true

패턴 매칭 switch도 강력해졌다.

// 이전 — instanceof 체크 후 캐스팅을 별도로 해야 했다
if (shape instanceof Circle) {
    Circle c = (Circle) shape;
    return Math.PI * c.radius() * c.radius();
}

// Java 21 — switch 패턴 매칭
double area = switch (shape) {
    case Circle c    -> Math.PI * c.radius() * c.radius();
    case Rectangle r -> (double) r.width() * r.height();
};

핵심 추가 기능

  • Virtual Thread (정식)
  • 패턴 매칭 switch (정식)
  • Sequenced Collections (getFirst(), getLast() 등)
  • Record 패턴 (정식)

LTS 버전 한눈에 비교

버전출시LTS 지원 종료핵심 변화
Java 820142030람다, Stream, Optional
Java 1120182032var, HttpClient, 문자열 API
Java 1720212029Record, Sealed, Switch 개선
Java 2120232031Virtual Thread, 패턴 매칭

Spring Boot 버전과 JDK 선택

Spring Boot와 Java 버전 호환표

Spring Boot최소 Java권장 Java비고
2.x811 또는 172025년 11월 EOL
3.x1717 또는 21현재 메인스트림

Spring Boot 2.x는 2025년 11월에 공식 지원이 종료된다. 신규 프로젝트는 반드시 3.x로 시작해야 한다.


신규 프로젝트 버전 선택 기준

Spring Boot 3.x + Java 17 조합 을 기본으로 선택한다.

  • 가장 많은 레퍼런스와 커뮤니티 지원
  • 대부분의 라이브러리가 안정적으로 지원
  • Spring Security 6, Jakarta EE 10 기반

Spring Boot 3.2+ + Java 21 조합 은 아래 상황에서 선택한다.

  • 높은 동시성이 필요한 API 서버 (Virtual Thread 활용)
  • 새로운 문법을 적극적으로 사용하고 싶을 때
  • 팀 전체가 Java 21 환경을 갖출 수 있을 때

IntelliJ 프로젝트 설정 시 주의사항

SDK 버전과 언어 수준(Language Level)은 반드시 일치시켜야 한다. 불일치 시 컴파일 오류 또는 런타임 오류가 발생할 수 있다.

SDK: Java 17  →  언어 수준: 17 - Sealed classes...  ✅
SDK: Java 17  →  언어 수준: 24 - 스트림 수집기      ❌

build.gradle에도 동일하게 명시한다.

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

또는 build.gradle.kts (Kotlin DSL):

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

결론

  • 신규 프로젝트는 Spring Boot 3.x + Java 17 을 기본으로 선택한다.
  • 높은 동시성이 필요하거나 최신 기능을 활용하려면 Java 21 을 선택한다.
  • IntelliJ에서 SDK와 언어 수준은 항상 동일하게 맞춘다.