우리팀이 JDK 17을 도입한 이유

--

안녕하세요, T플랫폼개발실 개발3팀 제이든입니다. ✋

제가 속해있는 개발3팀은 망고플레이트 서비스와 여기어때 통합리뷰 시스템의 개발/서비스 운영을 맡고 있습니다. 😀

이번 포스팅에서는 저희팀에서 Java 17 버전을 도입하게 된 이유와 개발자 입장에서 주요하다 생각되는 업데이트 내용 및 사용사례를 소개드리려 합니다.

Java 17 Version

2021년 9월 새로 공개한 LTS(Long-Term Support) 버전으로, Oracle JDK 기준 2029년 9월까지 지원합니다.

Java 11과 비교하여 70가지 이상의 JEP가 더 추가 되었습니다.

  • JEP: (JDK 개선 제안, JDK Enhancement Proposals)
출처: Oracle Java

JDK 버전별 점유율

JetBrains에서 발표한 조사(위 링크)를 보면 Java언어를 이용하는 개발자분들께서 주로 사용하는 버전은 JDK 8입니다.

왜 JDK 8을 사용할까?

JDK 8은 2014년 등장한 다소 오래된 Java 버전입니다. 그렇지만 위에서 보신 바와 같이 국내/외에서 현재까지 많이 사용되는 버전이기도 합니다. 그렇다면 왜 많은 분들이 다른 버전도 아닌 JDK 8을 사용하고 계실까요?

그 이유는 다음과 같다고 생각합니다.

1. 발표된 LTS 버전 중 가장 오랜 Support을 보장

  • Oracle은 JDK 8이 현재까지 나온 버전 중 가장 오랜 기간 지원될 버전이라고 발표하였습니다.
Oracle 지원 로드맵

2. 기존 서비스와의 호환

  • 현재 국내에서 개발된 프로젝트는 대다수 Java 8로 개발되어 운영하고있는 상황입니다. 그렇기에 기존 프레임워크 또는 제품들과의 호환성을 유지하고 안정적으로 운영하기 위해 이후 연관된 프로젝트들 또한 JDK 8에서 벗어나지 않고 있습니다.

그렇다면 우리는 왜 JDK 17을 사용할까?

이제 아래에서 JDK 17의 업데이트 된 기능들을 소개드리며, 저희가 어떤 신규 기능에 매료되어 17버전을 선택하게 되었는지 설명 드리겠습니다. 그 전에, 다른 이유를 짧게 말씀드릴까 합니다.

1. Java Support 기간이 길다.

  • 현재까지 발표로는 8버전의 지원 기간이 17버전보다 상대적으로 조금 더 긴 게 사실입니다. 그러나, 17버전의 지원 기간이 절대적으로 짧다고 할 수는 없습니다. 또한, 해당 기간 만료 전 또 다른 LTS버전 공개를 준비하고 있지 않을까 생각하고 있습니다.

2. 신규 버전을 위한 대비

  • 현재 사용 중인 버전의 지원 종료일이 도래하여 다른 LTS 버전(JDK 21)을 찾아야 할 시점에, 8버전에서 바로 최신 버전으로 전환하는 것 보다 17버전까지의 기술 적응을 완료한 상태에서 전환 시 마이그레이션으로 인한 영향이 최소화될 것이라 생각했습니다.

3. 다음 세대 플랫폼 호환 준비

  • 이 부분이 저희가 JDK 17을 채택한 가장 주요한 이유이기도 합니다. 현재까지 나온 스프링 공식 로드맵 상, 2022년 11월 출시 예정인 Spring Boot 3.0부터는 JDK 17이상을 지원합니다.
  • (Preparing for Spring Boot 3.0)

또한, 저희팀은 기존 서비스에는 없었던 신규 시스템 개발을 착수하는 시점이라 기존 시스템과 연관관계가 복잡하지 않은 상황이었습니다. 그리하여 저희는 선택의 폭을 넓혀 사용할 기술을 결정할 수 있었습니다.

JDK 17 까지의 주요 업데이트

JDK 9부터 JDK 17까지 추가 및 변경 된 업데이트 내용 중 개발자가 관심 가질 항목을 리스트업 해 봤습니다.

JDK 9부터 11까지의 주요 업데이트

New Garbage Collector, ZGC 추가

JDK 11부터 공개되었고, “Stop-The-World”로 인한 성능저하를 개선하기 위한 목적을 가지고 Oracle에서 개발하였습니다.

ZGC 동작 원리 (Wiki, GitHub)

  • Pause Mark Start: Colored pointers 알고리즘을 기반해 ZGC Root에서 가리키는 객체의 상태값을 Mark(저장) 합니다.
  • Pause Mark End: 새로 들어온 객체를 대상으로 Mark가 일어나고, ZPage(ZGC에서 다루는 영역)를 찾아 RelocationSet()에 배치합니다.
  • Pause Relocate Start: Root 참조 객체에 대한 재배치를 하며, 이후 Load barriers 알고리즘을 통해 모든 객체를 안전하게 업데이트합니다.

특징

  • 대기 시간이 짧은 Application에 적합한 Garbage Collection입니다.
  • Thread가 실행 중일 때 동시 작업을 수행하기에 모든 작업을 동시에 수행합니다. (병렬 처리)
  • 처리 시간이 10ms를 초과하지 않아 짧은 지연시간을 보장합니다.
  • 8MB부터 16TB까지의 Heap 크기를 지원합니다.

적용 방법

  • Java Application 실행 시 다음 옵션 실행 (기본 설정: G1GC)
java -XX:+UseZGC -jar Application.java

Collection Factory Method 강화

Set, List, Map 인터페이스에 Immutable 생성할 수 있는 새로운 메서드가 추가 되었습니다.

변경 전

변경 후

기존보다 깔끔한 코드스타일을 가져갈 수 있고, Collection 초기화 값을 세팅하여 사용 가능합니다.

또한 본 개념대로라면 Immutable 속성을 가지고 있기에 데이터 변경 작업이 어렵지만 데이터의 추가, 수정, 삭제 작업이 필요할 경우, 아래와 같이 진행할 수 있습니다.

Reactive Stream

Non-Blocking Backpressure를 이용한 비동기 스트림 처리의 표준 제공이 목적인 기능으로, 기존 “Observer Pattern”에서 극복이 어려웠던 “C10K” 문제 해결을 위하여 “Subscription”이라는 중계 브로커가 추가된 패턴 방식 입니다.

(Reactive Stream reference)

  • C10K Problem: 동시에 만 개의 클라이언트 요청이 올 경우 처리할 수 없는 문제
  • Publisher: 데이터 상태 변경 시 생산한 데이터를 전달해주는 데이터 제공자
  • Subscriber: Publisher에서 발행한 데이터를 받아 처리하는 데이터 수신자
  • Subscription: Publisher에게 전달 받은 데이터를 제어하여 Subscriber에게 전달하는 중계 브로커

또한 이러한 Reactive Stream은 WebFlux와도 연관이 있습니다.

(Spring Webflux 가이드)

Reactive 프로그래밍을 지원하는 String WebFlux는 Spring 5에 포함되어 있습니다. Project Reactor와 Publisher 구현을 위해 “Flux/Mno” 클래스를 사용합니다.

  • Spring Webflux: 기존 Servlet API를 기반으로 한 HTTP API가 아닌 Reactive Stream기반 통신을 지원하는 Spring MVC 모듈.

아래에 간단한 Reactive Stream 예제를 구현해보았습니다.

  1. Reactive Stream 라이브러리

2. 인터페이스 구현

[Publisher]

Publisher

[Subscriber]

Subscriber

로컬 변수 타입 추론 “var”

로컬 변수 선언 시, “타입 추론”을 이용하여 명시적 타입 선언 없이도 변수 선언이 가능하도록 지원하는 신규 Keyword입니다.

기존 Lombok Library에서 지원하던 기능이었으나, JDK 10 버전에 등장하며Java에서 공식 지원하였습니다. 이후 LTS 버전인 JDK 11부터는 람다 타입에서도 사용 가능하도록 지원하고 있습니다.

컴파일 시 변수 타입을 추론하기 때문에 성능에 영향을 주지는 않습니다. 그러나 가독성 높은 코드 작성을 위해 무분별한 “var” Keyword 이용은 지양하는 것이 좋겠습니다.

제약

  • 멤버 변수 또는 Method 파라미터로 선언은 불가능하며, 오로지 Method 내 로컬 변수로만 선언 가능합니다.

신규 문자열 Method 추가

문자열 내 공백 확인/제거 등 JDK 11 버전부터 String에 여러 편리한 기능을 지원하는 새로운 Method들이 추가 되었습니다.

  • isBlank: 문자열이 비어있거나 공백이면 True 반환
  • lines: 줄 단위로 나뉘어 있는 문자를 배열로 반환
  • strip: 문자열 공백 제거 (기존 “trim()”이 ‘\u0020’ 이하 공백만을 제거 하였다면, “strip()”은 유니코드의 공백들을 전부 제거)
  • stripLeading: 문자열 앞의 공백을 제거
  • stripTrailing: 문자열 뒤의 공백을 제거
  • repeat: 문자열을 파라미터로 주어진 수 만큼 반복

아래에 간단한 예시를 준비했습니다.

JDK 12부터 17까지의 주요 업데이트

텍스트 블록

JDK 15부터 정식 지원하는 새로운 기능입니다.

“”” {String 문자열 } “”” 형식을 이용해 Java 문자열을 보다 가독성있게 작성하도록 도와줍니다.

현재 저희팀에서 TextBlock을 적용한 예시 입니다.

  • 위 코드는 서버에서 발생한 이벤트 로그를 슬랙메신저에서 직접 확인하기 위한 기능 구현에 사용되었습니다.
  • 예시 코드와 같이 TextBlock 내 데이터를 동적으로 다루게 될 경우 %s 문자와 format Method를 이용하여 구현할 수 있고, 또한 블록 내 “+” 연산자를 이용하여 구현할 수도 있습니다.

Switch 표현식 기능 향상

다소 늦은 업데이트라는 느낌이 있으나 Switch 표현식 기능이 업데이트 되었습니다. (람다식 지원 등)

1. Switch문 값 직접 반환

2. yield 예약어 이용한 값 리턴 방식 추가.

3. Case문 람다식 지원.

위 세가지 기능이 추가가 되었고, 아래는 실제 적용해 본 테스트 코드입니다.

Record Data Class 추가

Record Data Class란, JDK 14 버전부터 공개된 Immutable 객체를 생성하는 새로운 유형의 클래스입니다.

  • Record 선언을 하게 되면 기존 toString, equals, hashCode 메소드를 자동으로 구현해주며, 모든 인스턴스 필드를 초기화해주는 생성자가 생성이 됩니다.
  • Immutable 객체이기 때문에 모든 값은 생성자를 통해 설정되어야 합니다.
  • DTO와 같은 Data Object 용도로 활용 시 보다 편리하고 간결하게 구분할 수 있습니다.
  • 현재 우리가 사용 중 인 ModelMapper 라이브러리는 setter를 통해 값을 설정하기 때문에 Record Class를 ModelMapper에 사용할 수 없는 것으로 확인하였습니다.
  • Record Class는 상속이 불가능합니다. (모든 필드는 “private final ,,”로 선언이 되기에,,)

아래는 Record Class 구현/실행 예제입니다.

마무리 ,,,

이상으로 Java 17 업데이트 적용과 관련된 포스팅을 마치겠습니다. 😊

끝까지 읽어주셔서 감사합니다.

다음엔 좀 더 숙달 된 필력과 함께 좋은 내용으로 찾아뵙겠습니다.

To be continued … 😶‍🌫️

--

--