회고록/문제 해결

[ 문제 해결 ] 대용량 데이터 요청 개선기

hola. 2026. 2. 25. 15:03

[ 문제 상황 ]

 기존 시스템을 업데이트 후 200만건 정도의 조회 요청(대략 1.5G 정도)  들어와 jvm에서 oom이 떠버렸다...

확인해보니 tomcat에 할당된 heap 메모리 사이즈가 업데이트 하기 전 서버보다 작게 잡혀 있었다.

 

 부장님께 보고 드리니 heap 사이즈를 늘려주신다고는 하나

'과연 heap 사이즈를 올린다고 추후 같은 문제가 발생하지 않을까?' 하는 의문이 들었다.

왜냐하면 200만건 정도의 조회 요청을 한 번 정도 왔으니 다행이나 여러 명의 직원들이 동시에 요청을 하게 된다면?

즉, 대용량 요청을 여러명이 동시에 요청하게 되면 그만큼 jvm에서 heap사이즈를 차지하게 될것이고 결국에는 oom이 발생할 수 밖에 없는 여지를 남겨둔다는 생각이 들었다.

 

 요즘 같은 시대에 메모리는 이전보다 더 비싸다... 금 값인 메모리는 한정적이니 효율적으로 사용할 필요가 있다는 생각이 들었다.

이번 글은 대용량 요청을 처리하는 과정을 고민하고 해결했던 경험 글이다.


- 그렇다면 대체 어디서 왜 oom이 발생한 것일까?

 heap dump를 통해 보니  com.mysql.jdbc.JDBC4ResultSet” 인스턴스 하나가 1,641,591,440바이트(76.74%)를 차지합니다.

라고 되어 있었다.

JDBC4ResultSet은 뭘까? DB에서 조회한 데이터가 최초로 담기는 객체이다. 

heap 메모리가 최대 2기가 밖에 할당되지 않았는데(추후 늘림) 저렇게 큰 데이터가 객체에 할당되다 보니 oom이 발생한 것이다.

이 문제를 봤을 땐 첫번째로는 당연히 heap 메모리만 늘리면 되겠다! 라고 생각이 들었다.

하지만 더 나아가 여러 직원이 해당 요청을 동시에 한다면? 무조건 서버가 뻑갈 것이다.

단순히 메모리를 늘리는 것이 아닌 다른 방식으로 요청을 처리할 필요가 있다는 것이다.

 

 두가지 방식을 생각했다.

1. 해당 기능은 조회한 데이터를 브라우저에 보여주는 것이니 페이지 나눠서 보여주는 방식

2. Streaming 형식으로 db에서 받은 데이터를 바로 브라우저에 보내는 것

 

 첫번째 방식은 기존에 있던 쿼리문들을 다 수정해야 한다는 번거로움이 있어서 제외했다. 그리고 해당 기능을 이용하는 직원들의 이야기를 들어보니 데이터를 조회 후에 엑셀로 다운 받아서 2차 작업을 하는 경우가 있다고 했다. 페이지를 나눌 경우 페이지를 눌러가며 각각의 엑셀을 다운 받아야하는 번거로움까지 생기니, 개발하는 관점이나 실무적으로나 첫번째 방식은 합당하지 않다고 생각하여 제외하였다.

 

 그래서 선택한 것이 두번째 방식이였다. Streaming 방식으로 데이터를 보내기 위해서는 db 조회 데이터를 한 번에 가져오는 것이 아닌 일부분씩 가져와야하는데, 이것이 가능한지 조사하였고 ResultHandler를 이용하면 될 것 같다고 생각하였다.

ResultHandler 동작은 다음과 같다.

ResultSet에서 row 하나 읽을 때마다 handleResult()가 호출되는 것이다. 가져온 데이터는 handleResult 메서는 내부에서 outpuyStream을 통해 브라우저에 보내면 되겠다고 생각했다.

하지만 뭐든지 트레이드 오프가 있는 법!

 

 ResultHandler를 쓰는 동안 Connection을 점유한다는 점, 이 부분은 oom이 발생하여 서버가 다운되는 것보다 connection 점유가 되어 혹시라도 요청이 늦거나 못 받는 경우가 생기는게 더 낫다고 생각했고 개발을 시작하였다.


- 사용자 입장에서 다시 생각하기

1차 개발을 완료하였다! 테스트 해보니 서버에서는 oom이 발생하지 않았다. 

그러나 문제는 사용자 입장에서 발생했다. 50만건 정도 넘어가니 브라우저에서 버벅이며 스크롤하는데도 느렸다. 또한 100만건 정도의 데이터를 받으니 프론트에서 JSON 파싱 에러가 나는 경우도 있었다...

 

흐음...oom 발생하지 않으니 괜찮은걸까? 생각했다. 하지만 실무자들 입장에서는 분명히 불편할 것이라고 생각했고, 그렇다면 다른 직원들이 어떻게 이 기능을 사용했는지 다시 생각해보았다. 보통 데이터 조회 후 엑셀로 다운 받아서 2차 작업을 했다는 것이 생각이 이 났다.

즉, 조회한 데이터들이 렌더링 된 화면을 보는게 중요한 것이 아니라 조회한 데이터들을 엑셀로 다운 받는 것이 업무 목적인 것이였다. 따라서 이 부분에 대해서 부장님과 얘기하였고, 조회 데이터가 30만건 이상인 경우에는 화면에 보여주는 것이 아닌 엑셀로 다운로드가 되게 쪽으로 결정하였다.

 

그렇게 개발 완료하였고, 실제 운영 환경에서 직원들이 해당 기능을 사용하면서 oom이 발생하지 않은 것을 확인할 수 있었다.

 

[ 회고 ]

  이번 경험을 통해 개발 환경과 해당 기능을 쓰는 사람들의 실제 사용성에 맞춰서 개발 방향을 찾아가는 것이 중요하다는 것을 깨닫게 되었다. 특히, 개발적인 경험으로는 어떻게 메모리에 최대한 부담 없이 대용량 데이터를 처리할 수 있을지 기술적으로 풀어낸 좋은 경험이였다.