<aside> 🔑
요약
로컬 환경에서 여러 GET 요청 후 백엔드가 무한로딩 상태에 빠지는 문제가 발생했었다. 로그를 분석해보니 HikariCP 커넥션 풀이 모두 점유되어 새로운 연결을 생성하지 못하고 있었다.
원인은 spring.jpa.open-in-view
설정이 명시하지않아도 기본적으로 true
로 되어 있었기 때문이었다. 이 설정은 컨트롤러에서 Lazy 로딩을 허용하지만, DB 커넥션을 뷰 렌더링까지 점유하게 만든다. 결과적으로 커넥션이 장시간 반환되지 않아 풀 고갈 문제가 발생한 것이었다.
따라서, 해당 설정을 false 로 비활성화하자 커넥션 누수 문제가 해결되었다. 추가로 DTO 반환, readOnly 트랜잭션 적용 등으로 성능 최적화도 함께 이루어졌다. 이를 통해 계층 간 책임 분리와 안정적인 커넥션 관리를 실현할 수 있었다.
</aside>
로컬 환경에서 스프링부트 서버를 실행하고, 프론트엔드단에서 여러 번의 GET 요청을 반복해서 보내고나면, 어느 순간부터 백엔드로부터 데이터를 받아오지 못하고, 무한로딩 상태에 걸리게 되었다(즉, 서버가 먹통이 되고, 클라이언트에서는 아무런 데이터를 받지 못하는 상태).
서버 측 로그를 살펴보니 DB 연결 풀이 최대 20개 인데, 그 중에서 20개 모두 사용 중이며, 이후 DB 연결 요청을 시간 내에 만료하지 못하고 있다는 사실을 알게 되었다.
SQL Error: 0, SQLState: null
HikariPool-1 - Connection is not available, request timed out after 30000ms (total=20, active=20, idle=0, waiting=0)
500:Unable to acquire JDBC Connection [HikariPool-1 - Connection is not available, request timed out after 30000ms (total=20, active=20, idle=0, waiting=0)] [n/a]
HikariPool 과 관련한 다양한 글을 찾아보니, 큰 테두리에서 보면 연결 풀 누수의 정황과 매우 유사했고, 그 원인으로는 트랜잭션 비종료를 일으키는 속성의 활성화, 무한 점유 및 대기 상태의 반복에 의한 데드락 문제 등이 있었다.
애플리케이션을 실행하는 초반에는 문제가 없다가 무한 점유 문제가 발생하고 있으므로, 의심스러운 두 정황을 기반으로 개선해보는게 좋다고 판단했다. 만일 첫 번째 방법으로 개선이 되었다면, 데드락 부분은 살펴보지 않을 것이다.
일단, 위 문제가 발생한 이유를 찾아보기 위해 백엔드 로그를 분석하였는데, 의심스러운 로그를 하나 찾게 되었다. 로깅 레벨이 warn
으로 표시되는 로그였는데, 전문을 보면 아래와 같다.
JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
<aside> 🔑
spring.jpa.open-in-view 가 무엇인지?
뷰나 컨트롤러에서 Lazy 로딩된 연관 엔티티에 접근 가능하게 하기 위해서 사용되는 속성이다.
true
로 설정되어 있으며,EntityManager
를 열어두는 방식 (Open Session in View 패턴).
</aside>해당 기능은 개발 환경에서는 사용 시에 지연로딩과 관련한 문제를 예방하는 것에는 효과적이지만, 배포 환경에서는 다음과 같은 문제점이 있을 수 있다고 한다.