자바 버전 올리려다 과정을 기록해 두면 정말 많은 도움이 앞으로 될 것 같아서 기록해두려고 한 글입니다
이 글을 읽는 모든 분들은 비슷한 문제를 마주했을 것 같지만 천천히 편하게 올리면 결국 다 할 수 있습니다
파이팅
현재 상황은?
현재 저희 팀의 프로젝트는 자바 8로 되어있습니다
당연하지만 스프링부트도 2. 초반의 버전으로 이루어져 있습니다
자바 8이 유지가 되면 어떤 문제가 생길까요?
자바 8의 support 기간은 어마어마하게 많이 남아있는데요?
무려 앞으로 6년 10개월이나 남은 2030년 12월 31일에 서포트가 종료되는데요?
그전에 누군가가 자바 버전을 올려주지 않을까요? (하지만 그 일은 절대로 이루어지지 않습니다)
다른 사람을 설득할 수 있는 자바 버전을 꼭 올려야 하는 이유를 그나마 찾는다면 스프링 부트 2의 서포트가 종료된 부분이 그 사유가 될 수 있을 것 같은데요
현재는 부트 3.0까지 oss support 서포트가 종료되었습니다
자바 8이나 자바 11을 사용하면 스프링 부트 3으로 갈 수 없으니 당연히 서포트가 종료가 종료된 버전을 사용하게 되는 것이죠
어느 순간 보안 패치가 필요한 순간이 올 텐데, 부트 2에서는 관련된 기능을 돕기 쉽지 않은 문제가 생기죠
버전을 꾸준히 올리지 않으면, 어느 순간 올릴 때 한 번에 너무 많은 버전을 올려야 합니다
지금 저희 프로젝트가 마주한 자바 8에서 자바 17로, 부트 2에서 부트 3으로 크게 변화해야 합니다
최신 기능들을 사용할 수 없으니 개발 생산성이나, 성능에서 조금씩 계속해서 손해를 보게 됩니다.
그 손해는 언젠가 개발 조직 전체의 발목을 잡을 수 있는 부채가 될 수 있다고 생각합니다
그렇다면 어떻게 안전하게 버전을 올릴 수 있을까요?
그리고 다른 사람들은 어떻게 버전을 올려왔을까요?
물론 지금도 버전을 올리는 pr을 머지하지 못했지만 어떻게 하면 조금 더 안전하게 배포를 할 수 있을지 찾아봤던 것들 그리고 질문했던 것들에 대해서 적어보려고 합니다
구글에 java11 migration guide를 검색해 보자
microsoft 쪽에서 가이드를 올려줬던 것이 있는데요
jdeps와 jdeprscan를 통해서 java8에서 java11로 전환할 때 삭제된 api를 찾을 수 있습니다
jdeps를 build.gradle 안에 추가하는 방법
jderpscan을 build.gradle 안에 추가하는 방법
이렇게 추가를 하고 나면 gradle 플러그인을 통해서 사용할 수 있습니다
저희 서버에서는 왜인지 안되어서, bootjar를 만든 후에./gradlew jdeps와 같이 터미널을 통해서 사용했었습니다
그 결과 저희 서버에서는 문제가 하나도 없다는 결과가 나왔었습니다
자바 11로 프로젝트를 시작해 보자
그 이후에는 java와 kotlin을 컴파일하는 sourceCompatability와 targetCompatability... 그냥 1.8로 되어있던 대부분의 설정을 11로 변경했었습니다
그 이후에 로컬에서 정말 잘 실행이 되었던 것을 확인했었는데요
또 어떤 부분이 문제가 되었을까요?
jvm을 실행할 때 cli에서 주는 옵션들 중 일부가 java8에서만 존재하는 옵션을 사용하고 있어서 도커 이미지가 실행되지 않는 경우가 있었습니다.
그 이후에 다른 프로젝트 (java 11)의 설정을 그대로 복사했었습니다
그 이후에는 실제 실행이 잘 되는 것을 알 수 있었습니다
그러면 정말 프로젝트를 11로 올려도 될까?
나중에 실행을 하면서 cli 인수가 이상했던 것을 봤던 것처럼, 문제가 없다는 것을 보장할 수 있을까요?
그 부분이 정말 쉽지 않습니다
이때 서버 개발자 전체가 있는 슬랙 채널에서 질문을 했었는데요
관련된 답변을 잘해주신 분들이 많아서 간단하게 요약을 해보려고 합니다
슬랙 질문을 통해 알게 된 검증 방법
버전업을 위한 마음가짐
가장 첫 번째 분은 마음가짐을 알려주셨습니다
변경은 작게, 영향도 작게, 변경사항을 적용할 때 change log를 잘 읽고 진행하자
변경은 작게 - 프레임워크의 변경이라 쉽지 않겠지만 가장 작은 단위로 버전을 변경하자
영향도 작게 - 알파나 개인클러스터에서 테스트를 진행하고, QA를 해본 이후에 진행하자
No Silver Bullet
버전업은 이순서로 진행하자
라이브러리 먼저, 프레임워크 다음, 언어 버전업
라이브러리는 언어와 프레임워크를 모두 신경 썼을 것이고
프레임워크는 언어만 신경 썼을 것이고
언어는 아무것도 신경 쓰지 않았을 것이기에, 순서대로 올리는 것이 좋다
다른 프로젝트의 버전을 그대로 따라가자
QA는 서버 부팅이 잘 되는지, 직접 수정한 코드들이 문제없이 잘 되는지(스웨거 같은 진짜 기본적인 설정)
버전업이 모든 코드에 영향을 주기 때문에, 다 안되거나 다 되거나 인 경우가 많다
버전은 다른 프로젝트의 버전을 그대로 복사하면 문제없을 가능성이 좋다
테스트를 통해서 검증하자
테스트의 종류는 총 3가지가 있을 텐데요
1. restTemplate 같은 실제 http call을 통해서 테스트를 진행하는 방향으로 하면 안정적으로 검증할 수 있다
2. springBootTest 같은 콘텍스트를 직접 띄우는 방향으로 검증하자
3. mock 등을 활용한 단위테스트
여기서 1번 방식으로 작성된 테스트가 많다면 검증을 안정적으로 할 수 있습니다
모든 버전을 완벽하게 이해하고 업그레이드하자
스프링 부트 버전업의 방법
1. 강제로 버전을 설정하여 스프링 bom 의존성을 예외로 타고 있는 라이브러리 버전을 정리한다
2. 현재 버전의 패치버전으로 최대한 옮겨둔다 (2.3.3.RELEASE =>2.3.12.RELEASE)
3. 업그레이드할 부트의 릴리즈노트를 확인한다
4. 현재 빈과 업그레이드 시 존재하는 빈을 확인한다 /actuator/beans
5. 업그레이드 시 변경되는 라이브러리 확인 및 릴리즈 노트를 확인
위 과정을 반복해서 스프링부트 버전을 올릴 수 있습니다
추가적으로 openrewrite를 돌려보면 생각보다 놓친 부분을 많이 잡아줄 수 있다고도 하네요
더 안정성을 중요시해야 하는 프로젝트라면?
1. jdk 버전업의 경우에
프로젝트의 크기가 작다면, 롬복을 탈출한다
코틀린 코드를 추가한다(annotation processing이 적다면 그나마 버전에 둔감하게 바꿀 수 있습니다)
2. boot 버전업
사내 라이브러리 디펜던시와 충돌 나는 게 있는지(정말 안 쓴다면 직접 exclude를 진행한다)
3. 테스트 코드가 없다면 보장할 수 없다! 통합테스트를 해야 한다
알파에 반영해 두고 며칠은 지켜보자
대고객 배포를 진행하지 않고, 임직원만 배포를 해둔다면?
금감원에 보고를 하는 사고는 대고객 배포가 나갔을 때 문제가 됩니다
이것을 피하고, 트래픽을 조절해서 임직원 대상으로만 새 버전을 배포를 해둔다면 그 문제를 최대한 피할 수 있겠죠?
위와 같은 방식으로 천천히 왕도는 없이 올려서 프로젝트를 최신 상태로 유지해 볼 수 있지 않을까요?
단점은 임직원 상대로만 문제인지, 전체가 문제인지 인지하기 쉽지 않은 문제가 있겠죠
실제로 버전업을 했을 때 겪었던 문제
Lombok 1.6.20 에 변경된 사항
@ConstructorProperties 가 모든 인자를 받는 생성자를 기본으로 넣어주는데, 이 부분이 @AllArgsConstructor, @Builder 를 사용하는 경우에 기본으로 붙지 않도록 변경되어서 깨져서 롤백한 케이스
스프링 2.3.x 이하에서는 자바 17을 지원하지 않는다
한번에 자바 17 로 넘어가는 것은 2.3 이하에서는 불가능합니다
아마 이 글을 읽으시는 모든 분들은 버전 때문에 고통받으시다 오셨을 텐데, 저도 아직 java 11로 변경한 코드를 배포하지 못했습니다
마음을 편하게 먹고 아주 장기 프로젝트를 한다는 생각으로 짬짬이 도전하다 보면 결국 최신 버전의 프로젝트를 만들 수 있을 것이라고 믿어 의심치 않습니다
'Java' 카테고리의 다른 글
[4월 우아한테크세미나] ‘Java의 미래, Virtual Thread’ 정리 (2) | 2024.05.02 |
---|---|
우리 프로젝트에서 java 17을 사용하게 된 이유 (0) | 2023.07.02 |
reflection 을 주의해서 사용해야 하는 이유 (2) | 2023.03.26 |
Stream collect 알아보기 (0) | 2023.03.13 |
Java 멀티 쓰레드 아는체하기 (1) | 2023.03.05 |