3 minute read

이 글은 백엔드 크루 테오가 작성했습니다.

들어가면서

최근 하루스터디팀은 프로젝트에서 사용할 Java 버전에 대해 논의했다. 나를 포함한 팀원들은 모두 Java 11에 익숙한 상황이었기에 무엇을 사용해야하는지에 대해 고민이 많았다. 하지만 논의 끝에 Java 17을 선택하게 되었는데, 이 아티클을 통해 차이점을 비교하면서 Java 17 선택 배경에 대해 기술하고자 한다.


점유율

2022년 기준, Java 버전별 시장 점유율은 다음과 같다(JetBrain사 조사).

2021년 9월 Java 17이 등장했음을 감안해도 시장에서 높은 점유율을 보여주고 있다.

지원 기간

Java 11과 Java 17은 지원 기간에 있어서도 차이를 가진다. 여기서 말하는 지원이란, 다음과 같은 항목들을 의미한다.

  1. 보안 업데이트
  2. 버그 수정
  3. 기술 지원

Java 버전에 따라 이런 기술 지원 기간이 결정되는데, 일반적으로 기술 지원 기간은 다음 Java 버전이 나올 때까지이다. 즉, Java 12의 경우 Java 13이 나오기 전까지만 지원을 받을 수 있다는 것이다. 하지만 LTS(Long Term Support) 버전의 경우에는 다소 긴 기간의 지원 기간을 가지는데, Java 11과 Java 17이 바로 LTS에 해당한다.

Java 11과 Java 17의 구체적인 지원 종료 일정은 다음과 같다.

  • Java 11의 경우: 2026.09에 지원 종료
  • Java 17의 경우: 2029.09에 지원 종료

기능

Java 17에서는 Java 11 대비 다음과 같은 기능이 추가되었다. 간략하게 소개해보고자 한다.

텍스트 블록

여러 개의 큰 따옴표를 사용해 문자열들을 하나의 블록처럼 다룰 수 있게 되었다.

String text = """
            {
              "name": "John Doe",
              "age": 45,
              "address": "Doe Street, 23, Java Town"
            }
            """;
System.out.println(text);

switch문 개선

switch문의 경우 매 case문마다 break가 있어야 의도한 결과를 얻을 수 있지만, Java 17부터는 이를 생략할 수 있는 방법을 제공한다.

switch (fruit) {
    case APPLE, PEAR -> System.out.println("Common fruit");
    case ORANGE, AVOCADO -> System.out.println("Exotic fruit");
    default -> System.out.println("Undefined fruit");
}

또한, yield 키워드를 사용해 case 문에서 원하는 값을 반환할 수도 있다.

String text = switch (fruit) {
    case APPLE, PEAR -> {
        System.out.println("the given fruit was: " + fruit);
        yield "Common fruit";
    }
    case ORANGE, AVOCADO -> "Exotic fruit";
    default -> "Undefined fruit";
};

Records

불변 데이터 객체를 만드는데 Records를 활용할 수 있다. Lombok이 접목된 클래스라고 생각하면 편할 것이다. 즉, Record를 만들면 다음과 같은 항목들이 자동으로 생성된다.

  • 생성자
  • getter
  • equals & hashCode
  • toString
public record GrapeRecord(Color color, int nbrOfPits) {
}

sealed classes

sealed calsses를 활용하면 상속받는 자식 클래스들에 대한 제어를 수행할 수 있다. 즉, 부모 클래스에서 어떤 클래스만 상속을 가능하게 할지를 명시할 수 있다는 것이다.

public abstract sealed class FruitSealed permits AppleSealed, PearSealed {
}
public non-sealed class AppleSealed extends FruitSealed {
}
public final class PearSealed extends FruitSealed {
}

Pattern matching for instanceof

instanceof 연산자를 사용하는 경우, 아래처럼 형변환 후 변수 대입 과정이 간소화될 수 있다.

// Java 11
Object o = new GrapeClass(Color.BLUE, 2);
if (o instanceof GrapeClass) {
    GrapeClass grape = (GrapeClass) o;
    System.out.println("This grape has " + grape.getNbrOfPits() + " pits.");
}

// Java 17
Object o = new GrapeClass(Color.BLUE, 2);
if (o instanceof GrapeClass grape && grape.getNbrOfPits() == 2) {
    System.out.println("This grape has " + grape.getNbrOfPits() + " pits.");
}

NPE 지점 명시

NPE가 발생한 근본적인 이유, 즉 null이 반환되기 시작한 지점을 정확하게 명시해준다.

// Java 11
Exception in thread "main" java.lang.NullPointerException
        at com.mydeveloperplanet.myjava17planet.HelpfulNullPointerExceptions.main(HelpfulNullPointerExceptions.java:13)

// Java 17
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "com.mydeveloperplanet.myjava17planet.GrapeClass.getColor()" 
because the return value of "java.util.HashMap.get(Object)" is null
    at com.mydeveloperplanet.myjava17planet.HelpfulNullPointerExceptions.main(HelpfulNullPointerExceptions.java:13)

compact number formatting support

NumberFormat이라는 팩토리가 추가되어 유니코드 표준에 맞는 숫자 형식을 간편하게 생성할 수 있다.

NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.SHORT);
System.out.println(fmt.format(1000));
System.out.println(fmt.format(100000));
System.out.println(fmt.format(1000000));

// 결과
1K
100K
1M

Stream.toList()

Stream에서 간소화된 toList() 메소드를 제공한다.

Stream<String> stringStream = Stream.of("a", "b", "c");
List<String> stringList =  stringStream.toList();
  for(String s : stringList) {
      System.out.println(s);
  }

성능 비교

research

Java 11과 Java 17은 성능 측면에서도 차이를 가진다.

  • G1 GC의 경우 8.66% 정도 Java 17이 Java 11 대비 우세하다.
  • Parallel GC의 경우 6.54% 정도 Java 17이 Java 11 대비 우세하다.

결론

이상으로 Java 11과 Java 17의 차이점에 대해 알아보았다.

사실 위 내용에 대해 알아보기 전까지는 팀원들이 Java 11 환경에 익숙하다는 측면에서 빠르게 개발을 진행할 수 있는 Java 11을 선택하려고 했다. 빠르게 고객에게 가치를 전달해야 하는 애자일 개발방법론을 적용하는데 있어서 팀원들의 학습 배경 역시도 중요하다고 생각했기 때문이다. 하지만 위 내용에 대해 조사한 뒤, 하루스터디 팀은 다음의 이유로 Java 17을 선택하게 되었다.

  1. Java의 경우 강력한 하위호환성(backward compatibility)를 보장한다. 따라서 Java 17을 처음 사용하더라도 러닝 커브가 크지 않을 것이라 판단했다.
  2. Java 11 대비 공식 지원을 받을 수 있는 기간이 길다. 추후 보안 이슈나 버그가 발견되어도 장기간 서포팅을 받을 수 있다.
  3. 유의미한 GC 성능 차이가 존재한다. 특히 G1 GC의 경우에는 약 10% 포인트 정도 차이가 존재했다.

참고자료

https://dzone.com/articles/whats-new-between-java-11-and-java-17
https://ko.wikipedia.org/wiki/자바_버전_역사
https://foojay.io/today/should-you-update-java-or-upgrade-and-which-version-should-you-use/
https://www.jetbrains.com/ko-kr/lp/devecosystem-2022/java/
https://www.optaplanner.org/blog/2021/09/15/HowMuchFasterIsJava17.html