NYO_O

[스프링] OSIV란 무엇인가 본문

프로젝트/똑똣(TokTot)

[스프링] OSIV란 무엇인가

NYO_O 2026. 5. 23. 13:40
반응형

스프링 부트 프로젝트를 실행할 때마다 콘솔 창에 노란색으로 뜨는 길고 찝찝한 경고 메시지, 다들 한 번쯤 보셨을 텐데요. 오늘은 이 경고의 정체인 OSIV가 무엇인지, 왜 실무에서는 이것을 꺼야만 하는지 알아보겠습니다.

부팅 로그에서 발견한 찝찝한 경고 한 줄

스프링 부트와 JPA를 세팅하고 애플리케이션을 구동하면, 다음과 같은 경고 로그가 발생합니다.

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.

해석해 보자면 "OSIV 기능이 기본으로 켜져 있어서 화면을 렌더링하는 중에도 DB 쿼리가 발생할 수 있으니, 이 경고를 끄고 싶으면 명시적으로 설정을 변경하라"는 뜻입니다. 로컬 환경에서 개발할 때는 당장 에러가 나지 않으니 대수롭지 않게 넘기기 쉽습니다. 하지만 똑똣 프로젝트의 운영 배포를 앞두고 아키텍처를 점검하던 중, 이 경고가 단순한 권고가 아니라 서비스 장애를 유발할 수 있는 있다는 것을 알게 되었습니다.

OSIV(Open Session In View)

문제를 해결하려면 먼저 OSIV가 무엇인지 알아야 합니다. OSIV는 영속성 컨텍스트(EntityManager)를 언제까지 살려둘 것인가에 대한 설정입니다.

기본적으로 데이터베이스 트랜잭션은 서비스(Service) 계층에서 시작하고 끝납니다. 트랜잭션이 끝나면 영속성 컨텍스트도 닫히고, 데이터베이스 커넥션도 반환되는 것이 일반적인 상식입니다. 하지만 OSIV가 켜져 있으면 스프링은 트랜잭션이 끝나더라도 영속성 컨텍스트를 죽이지 않습니다. 사용자의 HTTP 요청이 들어올 때부터 응답이 완전히 끝날 때까지, 즉 컨트롤러(Controller)나 뷰(View) 렌더링 단계까지 영속성 컨텍스트를 살려둡니다.

왜 이런 기능을 기본적으로 켜두었을까요? 바로 지연 로딩(Lazy Loading) 때문입니다. OSIV가 켜져 있으면 컨트롤러 계층에서도 엔티티의 지연 로딩을 자유롭게 호출할 수 있습니다. 개발자 입장에서는 트랜잭션 범위에 신경 쓰지 않고 아무 곳에서나 데이터를 꺼내 쓸 수 있으니 개발 속도가 비약적으로 상승하게 됩니다.

도대체 무엇이 문제이길래 경고를 띄울까? (DB 커넥션 고갈)

개발하기 편한데 스프링 부트는 왜 굳이 경고 로그를 남길까요? OSIV의 치명적인 단점은 데이터베이스 커넥션을 너무 오래 물고 있다는 점입니다.

실무의 시나리오를 예로 들어보겠습니다. 사용자가 똑똣 서비스에 맛집 상세 정보를 요청합니다. 서비스 계층에서 DB 조회를 마치는데 0.1초가 걸렸습니다. 그런데 컨트롤러 계층에서 외부 API(카카오 지도 API 등)를 호출하여 좌표를 변환하느라 2초가 추가로 소요되었습니다.

OSIV가 켜져 있다면, 서비스 계층의 DB 조회가 끝났음에도 불구하고 외부 API를 호출하고 응답을 기다리는 2초 동안 데이터베이스 커넥션을 반환하지 않고 계속 점유하게 됩니다. 트래픽이 적을 때는 문제가 없지만, 사용자가 몰리는 트래픽 피크 타임에는 순식간에 DB 커넥션 풀이 말라버립니다. 결국 DB 커넥션을 얻지 못한 다른 사용자들의 요청은 대기 상태에 빠지고, 서비스 전체가 마비되는 대형 장애로 이어질 수 있습니다.

OSIV를 무작정 끄면 벌어지는 일

이러한 성능 상의 치명적인 문제 때문에 실무 환경, 특히 대규모 트래픽을 다루는 운영 환경에서는 spring.jpa.open-in-view: false로 설정하여 OSIV를 끄는 것이 권장됩니다.

하지만 설정 파일에서 값을 false로 바꾼 뒤 애플리케이션을 실행해 보면 곳곳에서 에러가 터지기 시작합니다. 가장 대표적인 것이 LazyInitializationException입니다.

OSIV를 끄게 되면 서비스 계층(트랜잭션)을 벗어나는 순간 영속성 컨텍스트가 닫힙니다. 이때 컨트롤러에서 지연 로딩으로 설정된 연관 관계 객체에 접근하려고 하면, 더 이상 DB와 통신할 수 없기 때문에 예외가 발생해 버립니다. 즉, OSIV를 비활성화한다는 것은 지금까지 컨트롤러에 느슨하게 퍼져 있던 지연 로딩 호출들을 모두 트랜잭션 내부(서비스 계층)로 끌어와야 한다는 것을 의미합니다.

마무리

정리하자면 OSIV는 개발자의 편의성을 위해 데이터베이스 리소스의 효율성을 희생하는 설정입니다. 초기 개발 단계에서는 유용할지 몰라도, 안정적인 운영 환경을 구축해야 하는 똑똣 프로젝트에서는 반드시 짚고 넘어가야 할 성능 병목의 원인입니다.

개념과 위험성을 파악했으니 이제 행동으로 옮길 차례입니다. 다음 포스팅에서는 똑똣 프로젝트의 OSIV를 실제로 비활성화하고, 그로 인해 쏟아지는 LazyInitializationException들을 페치 조인(Fetch Join)과 DTO 변환 로직을 통해 어떻게 안전하게 리팩터링했는지 그 해결 과정을 상세히 공유하겠습니다.

반응형