본문 바로가기
TWIL

SpringBoot 3.3.x → 3.4.x Migration 트러블슈팅

by amungstudy 2025. 4. 20.

서버 모듈에 사용하는 SpringBoot 버전을 변경하는 이슈를 맡아서
마주한 문제점들을 공유해보겠습니다.
 
이번에 제가 맡은 일은 저희 팀에서 관리하는 자바 모듈의 SpringBoot 의존성을 3.4.3버전으로 변경하는 것이었어요.
아직 쭈니어 개발자인 저… 의존성 버전을 변경하는 이슈는 개발자 인생에서 처음이었는데요.

  • SpringBoot 3.3.1 → 3.4.3

“메이저 버전은 그대로고, 마이너 버전만 변경하는 것이니 별로 달라진거 없을거다. 다른 프로젝트에서도 똑같이 SpringBoot 버전을 변경했는데, 문제 없이 잘 동작했다. “는 말을 듣고
“아 그럼 버전 올리고 테스트 한 바퀴 돌리면 끝이겠다~ 빨리 끝나겠다~” 라는 생각을 했습니다.
그리고 버전을 올려서 서버를 패치했는데 제대로 동작하지 않았어요.
 

하고 싶지만....

문제상황 : Spring에서 Bean 생성 중 동시성 문제 발생

저의 경우 Netty ChannelHandler에서 만든 스레드가 빈을 가져오고 있는데,
다른 스레드에서도 똑같은 Bean을 동시에 요청하면서 
데드락이 발생했습니다. 
 
실제로 Spring Boot 3.4.0부터 3.4.3까지의 버전에서는 여러 스레드가 동시에 singleton Bean을 생성하려고 할 때 문제가 발생합니다.
 
아래 두 가지 주입 방법 모두 발생했습니다. 

ApplicationContext.getBean(slowService.class)

@AutoWired
SlowService service

 
재현해보려고 노력했으나... 샘플코드로는 재현이 되지 않네요....
 
 
실제로 찾아보니 Spring Framework의 github 에도 내부적으로 사용하는 락(singletonCreationLock)이 충돌하여 데드락이 발생하는 문제가 보고 되었는데요.
 
특히 WebFlux나 Netty 기반의 비동기 환경에서 이러한 경우가 발생할 수 있다고 합니다.
발생 시 로그는 다음과 같습니다.
 


2025-04-20T21:16:27.749+09:00 INFO 23340 --- [demo] [pool-2-thread-1] o.s.b.f.s.DefaultListableBeanFactory : Creating singleton bean 'slowService' in thread "pool-2-thread-1" while other thread holds singleton lock for other beans [beanB]

 
이게 의미하는 바는:

  • slowService라는 싱글톤 Bean을 pool-2-thread-1 스레드가 생성하고 있는데,
  • 동시에 다른 스레드가 beanB의 싱글톤 생성을 위해 락을 가지고 있어서,
  • 서로 다른 Bean을 만들고는 있지만, 이게 순환 참조나 락 순서 꼬임 등으로 이어지면 데드락 위험이 있을 수 있다는 경고입니다.

slowService와 beanB가 서로 의존하지 않고, 생성이 독립적이면, 락 충돌 없이 병렬로 안전하게 생성될 수 있어서 정상 동작합니다.하지만 만약 해당 빈들이 의존 관계인 경우 데드락이 발생 할 수 있습니다. 
실제로 로그만 찍히고 구동은 문제가 없는 경우도 있어서. 마이그레이션 시 실제로 기능이 동작하는지 확인이 필요합니다.
 
Spring Boot 3.4.4에서는 이러한 동시성 문제를 해결하기 위해 DefaultSingletonBeanRegistry의 락 처리 로직이 개선되었습니다.

  • Bean 생성 시 사용하는 락의 범위와 순서를 조정하여 데드락이 발생하지 않도록 변경
  • 여러 스레드가 동시에 Bean을 요청하더라도 안전하게 처리할 수 있도록 동기화 메커니즘 개선

이러한 변경으로 인해, 이전 버전에서 발생하던 데드락 문제가 해결되었다고 합니다.
이 문제는 Spring Framework 6.2.4 버전이후부터는 발생하지 않아서 피치않게 SpringBoot 3.4.4버전을 적용하는 것으로 해결할 수 있었습니다.
 
관련된 GitHub 이슈와 PR:

 

- WebFlux나 Netty 기반의 비동기 환경에서 Bean 초기화 시 동시성 문제로 인한 thread hang 발생 현상

- SpringBoot 3.4.x 버전 에서 사례 보고됨 (관련 Spring 이슈 : #34349, #34186, #34522)

- SpringBoot 3.4.4 버전에서는 위 문제에 대한 bugfix가 포함된 Spring Framework 6.2.4 버전을 의존하고 있어 3.4.4 버전 적용

💡 DefaultSingletonBeanRegistry란?

DefaultSingletonBeanRegistry는 Spring 컨테이너가 singleton 스코프의 bean들을 생성하고 캐싱하고 반환하는 로직을 담고 있는 **기반 클래스(base class)**입니다.

  • 모든 singleton bean은 여기서 생명주기 관리를 받아요.
  • 이 클래스는 registerSingleton(), getSingleton() 등의 메서드를 제공하면서 bean 이름 → 실제 객체를 Map으로 관리합니다.
  • 초기화 중인 bean인지, 이미 생성됐는지, 순환 참조 있는지 등도 여기서 체크합니다.
  • DefaultSingletonBeanRegistry는 직접적으로 BeanFactory를 구현하지 않지만, AbstractBeanFactory가 상속받아서 활용합니다.
  • 즉, DefaultListableBeanFactory나 ApplicationContext에서 bean을 생성하거나 조회할 때 내부적으로 이 registry를 사용해요.

🧠 예시 흐름 (Singleton Bean 생성 과정 중 일부):

  1. BeanFactory.getBean(" slowService ") 호출
  2. → 내부에서 DefaultSingletonBeanRegistry.getSingleton(" slowService ") 확인
    • 있으면 반환
    • 없으면 생성 시작
  3. 생성하면서 singleton cache에 등록 (addSingleton)
  4. 순환 참조나 생성 중 오류 → 여기도 관여

 

 

마무리하며

ChatGPT도 좋지만 라이브러리 관련 작업을 할 때는 해당 라이브러리의 공식문서를 잘 읽어봐야겠다는 생각을 했습니다. 물론 공식 문서에도 친절하게 적혀있는 건 아니었지만 공식 문서를 참고했다면 좀 더 빠르게 해결했을 것 같아요!