Spring 5.2.X 버전에서 @RequestParam required = true 옵션은 null safety 하지 않다.

kindof

·

2024. 1. 28. 16:26

2024년 1월 현재 기준으로 개인 프로젝트나 가까운 시일 내에 시작한 프로젝트라면 Springboot 3.X 버전, Spring 6.X 버전을 사용하게 됩니다.

 

하지만 프로젝트 개발을 한 지 오래된 서비스라면 Spring, Springboot 버전이 꽤 낮을 수 있는데요.

 

예를 들어, 3년 전인 2021년에 개발을 시작한 프로젝트만 하더라도 당시 Springboot의 최신 버전은 2.3.1.RELEASE 였으며 이 때 호환되는 Spring 버전은 5.2.X 이었습니다.

 

이번 글에서는 이런 점을 심각하게 고려하지 않고 개발을 하다가 마주친 문제에 대해 정리해보려고 합니다.

 

바로 예제 코드를 보겠습니다.

@GetMapping("/v1/book")
public Book getBook(@RequestParam(value = "bookId", required = true) Long bookId) {
    log.info("bookId = {}", bookId);
    Book book = bookRepository.findById(bookId).orElse(null);

    if (book == null) {
        return null;
    }

    // 비즈니스 로직 ...
이 코드에서 @RequestParam으로 들어오는 bookId는 null이 될 수 있을까요?

 

아마 최근에 이런 코드를 작성해보신 분이라면 bookId에 대한 null 체크를 따로 하지 않아도 된다는 것을 알고 계실 것입니다.

 

하지만 Spring 5.2.X 버전까지는 required = true 옵션이 Empty, Null 파라미터에 대한 체크를 하지 않았는데요. 

 

실제로 테스트를 해보기 위해 Springboot 2.3.12.RELEASE 버전(Spring 5.2.15.RELEASE Compile dependency) 환경을 만들어보았습니다. 

 

아래와 같이 build.gradle을 수정하고 jdk = 1.8, 해당 버전과 호환되는 Import 문을 작성해주시면 됩니다.

plugins {
   id 'java'
   id 'org.springframework.boot' version '2.3.12.RELEASE'
   id 'io.spring.dependency-management' version '1.1.0'
}

 

 

그리고 위에서 작성한 API 에 대한 호출을 해봤는데요.

500 Internal Server Error
bookId = null 로 들어온다.

 

실제로 bookId에 대한 값을 입력하지 않고 API 호출을 하면 bookId 에는 null 값이 들어가게 되고, JpaRepository에서 null 을 통해 조회를 하는 순간 IllegalArgumentException이 발생하는 것을 볼 수 있습니다.

 

required = true 옵션이 null 요청을 EntryPoint에서 자르지 못하기 때문인데요. 이를 간과하고 코드를 작성하게 되면 위와 같이 예상치 못한 예외를 발생시켜 장애를 발생시킬 수 있다는 것이 포인트입니다.

 

그렇다면 최신 버전의 Spring에서는 required = ture 옵션이 null을 방지할까요? 아래와 같이 최신 버전의 Springboot 환경에서 다시 테스트를 진행해보겠습니다. 마찬가지로 jdk는 17로 올려주셔야 합니다.

plugins {
   id 'java'
   id 'org.springframework.boot' version '3.0.0'
   id 'io.spring.dependency-management' version '1.1.0'
}

 

 

똑같은 API 호출을 하면서 bookId 파라미터에 값을 할당하지 않았습니다.

400 Bad Request
MissingServletRequestParameterException

 

그 결과, 이번에는 400 Bad Request 응답을 리턴하며 MissingServletRequestParameterException이 발생합니다.

 

즉, 최신 버전의 스프링에서는 required = true 옵션이 파라미터의 Null 체크를 한다는 것입니다.

 

왜 이런 차이가 있을까요? 오랜 시간 구글링을 하다가 spring 공식 프로젝트의 한 이슈를 발견했습니다.

 

https://github.com/spring-projects/spring-framework/issues/23939
https://github.com/spring-projects/spring-framework/issues/26088

 

해당 이슈에서 언급된 것처럼 Spring 5.3 버전 이후부터에서야 required=true 옵션이 non-null 값에 대한 Conversion을 지원하게 되었다는 것입니다.

 

따라서, 그 이전에는 null이 될 수 있는 파라미터에 대해서는 required=false 혹은 @Nullable 어노테이션을 사용하라고 가이드하고 있는 것이죠.

 


 

 

처음 이 문제를 겪었을 때는 'required = true 옵션이 null 체크를 해주지 않는구나'라고 생각했지만, 집에 와서 거의 비슷한 코드로 재현을 해보니 실제로는 null 체크가 되었습니다.

 

그래서 오랜 시간을 다른 원인이 있을까해서 많은 코드 베이스를 뒤져보았지만 왜 다른 결과가 나오는지 찾지 못했습니다.

 

그래서 혹여 Spring 버전에 따라 다르게 동작할까? 라는 생각으로 공식 문서를 뒤져서 원하는 답을 찾을 수 있었는데요.

 

이번 글에서 정리한 것처럼 스프링 프로젝트는 계속해서 버전업을 하고 있고, 그 전의 사용 패턴과 달라지는 부분이 반드시 존재하기 때문에 이런 점들을 한 번쯤은 생각해보고 개발한다면 앞으로도 도움이 될 것 같습니다.