Java 17을 도입한 이유

Posted by 서명현 on August 26, 2023

안녕하세요, 서명현입니다. 🤚

저희 팀은 이번 프로젝트에서 Java 17을 도입하였는데요. 수년간 많은 기업에서 사용해온 Java 8 대신 Java 17을 도입한 이유에 대해 설명드리고자 합니다.

목차 소개

이번 포스팅은 다음과 같은 순서로 진행됩니다.

  1. 신규 LTS 버전을 위한 대비
  2. Java 17의 변경 사항
  3. Java 17 가비지 컬렉터의 성능 향상

1. 신규 LTS 버전을 위한 대비

현재 Oracle에서는 JDK 8은 2030년까지, JDK 17은 2029년까지 지원할 것이라고 발표하였는데요.
지원 기간만 본다면 JDK 8을 써도 괜찮아 보이지만, 2030년 이전에 모든 개발자는 8 이후의 LTS 버전으로 마이그레이션을 해야할 것입니다.
JDK 8에서 다른 LTS 버전(JDK 21 혹은 그 이상)으로 마이그레이션시 영향을 최소화하기 위해 현재의 LTS 버전인 JDK 17을 도입하기로 결정하였습니다.
이미 2022년도부터 많은 개발자들이 JDK 8에서 JDK 17로 마이그레이션한 것을 볼 수 있습니다. java-17-1

2. Java 17의 변경 사항

Java 17에서 추가된 기능은 이미 많은 곳에 소개되어 있는데요. 대표적으로 다음과 같은 기능들이 추가되었습니다.

  1. Sealed Classes
  2. Pattern Matching for Switch
  3. Foreign Function & Memory API (Incubator)
  4. Strong encapsulation of JDK internals
  5. Deprecation and Removals
  6. Security Updates

이 중 프로젝트에서 주로 쓰게 될 새로운 기능들을 중심으로 소개하겠습니다.

Record 클래스

Record 클래스는 불변(immutable) 클래스를 쉽게 만들 수 있도록 도와주는 데이터 클래스입니다.

기존의 불변 데이터 객체

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Person {
    private final String name;
    private final int age;

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

    public String name() {
        return name;
    }

    public int age() {
        return age;
    }
    // equals, hashCode, toString 등 생략
}

상태(name, age)를 가지고 있는 불변 객체를 생성하기 위해 많은 코드를 작성해야 합니다. 불변성을 위해 다음을 사용하여 데이터 클래스를 만듭니다.

  1. 각 필드에 대한 private, final 키워드
  2. 각 필드에 대한 getter 메서드
  3. 모든 필드를 인자로 받는 생성자
  4. 모든 필드가 일치할 때 동일한 클래스에 대해 true를 반환하는 equals() 메서드
  5. 모든 필드가 일치할 때 동일한 값을 반환하는 hashCode() 메서드
  6. 클래스 이름, 각 필드 이름 및 값을 포함하는 toString() 메서드

Lombok 을 사용하더라도 반복되는 어노테이션과 final 키워드를 여전히 사용해야 할 것입니다.

1
2
3
4
5
6
7
8
@Getter
@RequiredArgsConstructor
@ToString
@EqualsAndHashCode
public class Person {
    private final String name;
    private final int age;
}

레코드를 이용한 불변 객체

1
2
public record Person(String name, int age) {
}

Record 클래스를 사용하면 필드의 유형과 이름만 사용하여 불변 객체를 생성할 수 있습니다.
Equals, hashCode, toString 메서드와 private, final 필드 및 public 생성자는 Java 컴파일러에 의해 생성됩니다.

Stream.toList() 메서드

Stream 인터페이스에 toList() 메서드는 immutable한 리스트를 생성할 수 있는 메서드 입니다.
기존의 Collectors 메서드 대신 Stream.toList() 메서드를 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
public class OldStreamToListExample {
    
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> selectedNumbers = numbers.stream()
            .filter(n -> n >= 5)
            .collect(Collectors.toList());
    }
}
1
2
3
4
5
6
7
8
9
public class NewStreamToListExample {
    
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> selectedNumbers = numbers.stream()
            .filter(n -> n >= 5)
            .toList;
    }
}

Stream.toList()를 추가하는 주요 목적은 Collector API의 자세한 내용을 줄이기 위함이라고 합니다. 기존의 Collectors 메서드는 장황하기 때문에 Stream.toList()를 사용한다면 간결하게 사용할 수 있습니다.

switch 문의 패턴 매칭

Java 17에서는 switch 문에 패턴 매칭을 사용하여 더 간결하게 작성할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class OldSwitchExample {
    
    public static void main(String[] args) {
        int number = 2;
        String result;
        
        switch (number) {
            case 1:
                result = "One";
                break;
            case 2:
                result = "Two";
                break;
            default:
                result = "Unknown";
        }
        
        System.out.println(result);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class NewSwitchExample {
    
    public static void main(String[] args) {
        int number = 2;
        String result = switch (number) {
            case 1 -> "One";
            case 2 -> "Two";
            default -> "Unknown";
        };
        
        System.out.println(result);
    }
}

이 기능들 외의 Java 17을 사용하면서 적용할 새로운 기능들은 점차 넓혀 나가려고 합니다. 처음부터 좋다고 생각되는 기능들을 적용하는 것보다는 점진적으로 적용하면서 기능에 대한 피드백을 받아 개선해 나가는 것이 더 좋은 방법이라고 생각합니다.

3. Java 17 가비지 컬렉터의 성능 향상

Java 17에서는 기능적인 면 뿐만 아니라 성능적인 면에서도 많은 향상이 있었습니다.
특히 가비지 컬렉터의 성능 향상은 Java 17을 사용하는 가장 큰 이유 중 하나라고 생각합니다.

JDK 8 이후 가비지 컬렉터의 성능은 거의 모든 측면에서 향상되었음을 알 수 있습니다. 처리량 측정 항목에서 모든 컬렉터가 향상되었으며, 응답 시간 측정 항목에서의 결과는 눈에 띄게 향상되었습니다. java-17-2 java-17-3

이전 버전에 비해 JDK 17의 전반적인 성능은 어떤 가비지 컬렉터를 사용하는지와는 상관없이 모두 향상되었습니다.

저희 프로젝트는 JDK 17에서 기본으로 사용되고 있는 G1을 사용하고자 합니다. 가비지 컬렉터의 성능에 관한 자세한 정보는 하단의 링크을 참고하시면 좋을 것 같습니다.

마무리

이러한 이유들로 인해 저희는 Java 17을 사용하려고 합니다.

이상으로 Java 17을 도입한 이유에 대한 포스팅을 마치겠습니다.🖐️
끝까지 읽어주셔서 감사합니다.

이 포스팅은 다음 문헌을 참고하였습니다.