Programming/Study

더 이상 미룰 수 없다. 너의 마이그레이션, 나의 삽질 : Java 11 -> 21, Spring Boot 2.X -> 3.X

JKROH 2024. 4. 9. 18:32
반응형

 나는 보통 Spring 프로젝트를 만들 때 Spring Initializr 사이트를 애용한다. 원하는 빌드 툴, 언어, Spring Boot버전, 필요한 Dependency 등을 손쉽게 적용할 수 있기 때문이다. 최근에 과제 테스트를 진행하면서 해당 사이트에서 Spring 프로젝트를 하나 만들게 됐는데 아주 충격적인 사실을 알 수 있었다. 사랑하는(?) 자바 11버전이 아예 버전에서 삭제되었다. 현재 진행 중인 프로젝트에도 자바 11,과Spring Boot 2.X버전을 사용하고 있었는데, 이제는 역사의 뒤안길로 사라져야할 때인가보다.

미국으로 가버린 Spring Boot 2.X버전대와 Java 11

 

 시대의 흐름이 그렇다고 하니, 나도 자바 버전의 업데이트를 해야 할 필요성을 느꼈다. 언제까지고 구버전에 머물러있을 수는 없으니까. 자바 21 버전에서는 정말 다양한 기능이 추가되었는데, 사실 내가 지금 알고 가기에는 좀 먼 이야기인 것들이 많다. 대신 지금 알아두면 좋은 몇 가지 것들을 먼저 알아보자. 물론 이는 자바 11 버전과 비교한 것으로, 이전 버전에서 추가된 것들도 다룰 것이다.

 

Record의 도입

 아마 나같은 초보 개발자들에게 가장 크게 다가올 요소일 것인데, 기존의 Class / Interface / Enum / Annotation에 더해 Record가 추가되었다. Record는 자바 14버전부터 적용되어, 데이터 클래스를 만드는데 아주 용이하게 사용된다. 예를 들어, DTO를 굳이 final class로 만들 필요 없이 Record를 사용하면 된다. Record는 기본적으로 getter / setter / equals / hashCode 를 만들어준다. VO를 Record로 만드는 것도 괜찮을 것 같다. 아래는 기존 DTO와 Record의 차이 예시이다.

 

instance of 사용의 간편함

 기존에는 instance of를 사용해서, 해당 Object가 원하는 타입과 일치하면 타입 캐스팅을 진행해야했다. 하지만 이제는 그렇게 사용하지 않아도 된다. 이 역시 자바 14버전에서 추가되었다.

자바 11에서의 사용 방식

 

자바 14부터의 사용 방식

 

 이외에 sealed class를 통한 추상 클래스의 구현 클래스 제한, switch - case문법의 변화 등 다양한 변화가 있으니, 하단의 링크를 참고해보자.

 

 이제부터는 버전 마이그레이션에 따른 삽질 과정을 다뤄본다. 나와 같은 삽질 과정을 다른 이들이 조금만 덜 겪었으면 하는 마음에 다뤄본다.

javax -> jakarta

 자바 버전이 바뀌면서, 더이상 javax의 api를 사용하지 않는다. 회사 이름이 바뀌어서 어쩔 수가 없다. 대신, jakarta를 사용한다. migration tool이 있을 것 같은데, 내 경우에는 수정해야 할 코드가 별로 많지 않아 그냥 일일이 바꿨다.

 

Java 21을 사용할 것임을 명시적으로

 build파일, JDK setting, SDK setting을 모두 21버전으로 바꿔야한다. 나는 SDK를 세팅하는 것을 까먹어 external libraries에 11버전이 여전히 추가되었고, 이것 때문에 꽤 시간이 잡아먹혔다. SDK 세팅 이후로도 계속 11버전의 그것이 호출되었는데, intelli j에서 cache invalid를 수행하니 잘 되더라.

 

 Spring Security 업데이트

 Spring Boot 3 버전으로 올라오면서, Spring Security 역시 6버전으로 업데이트 되었다. 특히 SecuriyFilterChain을 Bean등록하는 과정에서 HttpSecurity 설정 코드가 많이 수정되었는데, 기존의 메서드 체인 형태는 유지되나 각 설정들을 람다 형태로 작성해야한다는 점에서 큰 차이를 보인다. 람다식을 활용해서 작성하는 것이 가독성이 더 좋다고 Spring 진영에서는 판단한 것 같다. 기존의 and()를 이용한 체이닝도 사라졌다.

@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .headers().frameOptions().sameOrigin()
                .and()
                .csrf().disable()
                .cors().configurationSource(corsConfigurationSource())
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .formLogin().disable()
                .httpBasic().disable()
                .exceptionHandling()
                .accessDeniedHandler(new MemberAccessDeniedHandler())
                .authenticationEntryPoint(new MemberAuthenticationEntryPoint())
                .and()
                .apply(new CustomFilterConfigurer())
                .and()
                .oauth2Login(oAuth2 -> oAuth2
                        .successHandler(new OAuth2MemberSuccessHandler(memberEntityRepository, successfulAuthenticationProcessor))
                        .userInfoEndpoint()
                        .userService(OAuth2MemberService))
                .authorizeHttpRequests(authorize -> authorize
                        .antMatchers(HttpMethod.POST, "/free-board/**", "/review/**", "/recruitment/**", "/comment/**").hasRole("CUSTOMER")
                        .antMatchers(HttpMethod.PUT, "/free-board/**", "/review/**", "/recruitment/**", "/comment/**", "/member/**").hasRole("CUSTOMER")
                        .antMatchers(HttpMethod.DELETE, "/free-board/**", "/review/**", "/recruitment/**", "/comment/**", "/member/**").hasRole("CUSTOMER")

                        .antMatchers(HttpMethod.POST, "/barber-shop/**").hasRole("BARBER")
                        .antMatchers(HttpMethod.PUT, "/barber-shop/**").hasRole("BARBER")
                        .antMatchers(HttpMethod.DELETE, "/barber-shop/**").hasRole("BARBER")

                        .anyRequest().permitAll()
                );

        return http.build();
    }

 

 위의 코드가 기존의 Security 5버전에서의 설정이다. 

Spring Security 6 버전에서의 설정

 

 언급했다싶이, 인자가 없이 메서드 체인 형태로 적용되던 설정이 인자와 람다식을 사용하는 형태로 바뀌었다. 또한 apply를 통해 AbstractHttpConfigurer를 상속한 CustomFilterConfigurer를 넣어주던 부분이 with()로 바뀌었으며, 해당 부분을 builder로 넣어주어야 한다. 길길이 메서드 체인을 연결하고, 이걸 and()로 구분하던 방식보다 훨씬 가독성 면에서 낫다고 느껴진다.

 

 또한 antMatchers가 사라지고 대신 requestMatchers가 사용된다. 기존의 Matcher들은 삭제되었다.

 

Spring Rest Docs 업데이트

 Spring Rest Docs역시 2버전에서 3버전으로 업데이트 해서 build파일에 설정해줘야 한다. 버전이 바뀌면서 request parameter를 넣어주기 위한 메서드 명이 requestParameters -> queryParameters로 수정되었다.

 

 해당 변경점이 좀 아쉬운 것은 Spring Web에서는 여전히 @RequestParam으로 parameter를 바인딩하는데 왜 굳이 이름을 바꿨을까 하는 부분이다. 뭐 많은 검색 브라우저에서 query라는 이름으로 request parameter들을 전달하는 점을 반영했다고하면 할 말은 없다만...

 

마치며

 버전 마이그레이션을 꽤 오랜 시간 진행했는데, 이런저런 오류 해결을 위한 방법을 찾는 시간이 오래 걸리긴 했나보다. 과정을 기록하고 나니까 별로 없는 것 같네... 앞으로는 21버전의 자바, 3버전의 Spring Boot와 함께 즐거운 프로젝트를 진행해보자.

 

참고자료

망나니 개발자님 : https://mangkyu.tistory.com/308

https://www.linkedin.com/pulse/differences-between-java-11-java21-hasan-akdogan-0rhlf/

 

Differences Between Java 11 and Java 21

Java, a versatile and widely used programming language, has undergone significant changes and improvements over the years. Java 11 and Java 21 represent two different points in this evolution.

www.linkedin.com

 

반응형