JPA | 성능(?) 올려볼게요. with Projections

Black
7 min readFeb 26, 2023

최근에 몇몇 개발자 분들 과 이야기를 나누었던 경험을 통해서 생각을 해본 계기가 되었다. 나의 토의 프로젝트를 보고 해당하는 코드 리뷰를 받아 보았다, 물론 해당하는 이벤트는 기회라고 생각이 들었다. 코드 리뷰를 통해서 많은 인사이트를 얻게 되었다.

외부의 한 개발자 분이 JPA 에 대해서 이야기를 하였다.

Q. JPA Projections 를 인터페이스를 사용하는 이유가 무엇이냐?

나의 대답은 “JPA 에서 공식 문서에서 인터페이스를 사용을 해서 사용을 했다 그리고 Class 도 가능하다” 였다.

사실 내가 JPA Projections 을 사용을 하면서 해당 이유를 고민을 한적이 없었다. 이 질문을 듣자 아차 싶었다. 그래서 나는 이 코드 리뷰를 통해서 더 고민을 하게 되었다.

Projections 에 대한 “사용법” 은 여기서 볼 수 있다. JPA 에서 Projections 을 사용을 하면서 Class 와 Interface 를 왜 사용을 하는지 자세히 알아 보려고 한다.

JPA 데이터 조회 별 성능 비교

먼저 성능적인 체크에 앞서서 테스트 시나리오 대해서 설명을 해보려고 한다. spring 프로젝트 내에서 테스트를 진행을 하려고 한다. 또한 JVM 을 웝업 관련 해서는 여러번 웝업을 시켜 놓고 진행을 하려고 한다.

비교 대상은 다음 과 같다.

  1. Entity projections
  2. Constructor projections
  3. Interface projections
  4. Tuple projections
  5. Dynamic projections

을 비교를 하려고 한다. 또한 100000 를 저장을 시켜서 100 번 조회를 한 뒤 평균을 내는 방식으로 진행을 하였다.

JPA Read 별 테스트 결과

Entiry Projections 와 Interface Projection 방식 과 다른 방식의 경우 10배 정도 차이가 발생이 된다.

그렇다면 왜 10배나 차이가 날까?

일단 Constructor projections 과 Tuple projections 방식의 경우 1차 캐시를 사용을 안하고 DB 로 바로 조회하는 것으로 알고 있다. 그렇기 때문에 Entiry Projections 와 Interface Projection 방식과는 다르게 조회 속도가 빠르게 나온다.

Entiry Projections 의 경우 1차 캐시를 통해서 가지고 오기 때문에 속도 면에서 느리게 측정이 되는 것이 맞다. 하지만 Interface Projection 이 Entiry Projections 와의 차이 점이 없는 이유는 무엇일까?

Interface Dynamic Projections vs Class Dynamic Projections

Dynamic Projections 을 사용을 하는데 Class 와 Interface 의 두개의 차이점이 발생이 되는것 같아서 해당 사항에 대해서 동일한 테스트 시나리오로 측정을 해보았다.

이 결과도 10배 이상 차이가 발생이 되는 것을 확인을 할 수 있다. 그렇다면 왜 차이가 나는지 상당히 궁금한점이 발생이되는데, 해당 로직에 대해서 디버그를 통해서 확인을 할 수 있다.

Class 로 Dynamic Projections 을 사용을 하면 바로 Class 로딩을 통해서 데이터를 가지고 오는 것을 확인을 할 수 있다.

또한 Interface Dynamic Projections 의 경우에는 JdkDynamicAopProxy 를 통해서 가지고 오는 것을 확인을 할 수 있다, 아마 interface 의 경우 Dynamic proxy를 통해서 매핑 과정을 거쳐서 해당 부분이 생기는 듯 보여진다.

또한 AbstractJpaQuery 클레스를 분석을 해보았는데 Tuple 로 데이터를 뽑아서 Convert 해주는 것을 볼 수 있다.

그리고 JdkDynamicAopProxy 를 왜 사용하는 지 궁금해서 해당 Class 를 분석을 해보았는데 Proxy 를 사용하는 것으로 확인을 할 수 있다.

Class 와 Interface 차이점이 Dynamic Proxy 를 사용하는 유무와 함께 DataBinding 하는 부분에서의 차이점을 발견을 할 수 있었다. Class 의 경우 바로 setter 와 생성자를 통해서 데이터 바이딩을 하는 것에 비해서, interface 의 경우 Tuple 로 변경을 하고 Proxy 를 사용을 해서 변경을 해주는 부분 때문에 속도의 차이가 발생이 되는 것을 볼 수 있다.

Summary

요약 하자면 다음 과 결과를 도출 할 수 있다.

  • JPA 조회의 경우 JPQL 의 비해 느리다.
  • JPA 조회의 경우 Tuple 의 비해 느리다.
  • JPA 조회의 경우 JPA Dynamic Projections 의 Class 비해 느리다.

또한 필자가 비교를 하기 위한 부분인 JPA Dynamic Projections 에서의 interface 와 class 의 경우 class 가 “속도 측면에서 더 빠르다” 라고 정의가 가능하다.

개발하는 서비스 혹은 솔루션에 따라 다르다.

만약 데이터가 수가 적고 다용도 적으로 사용을 하게 되면, interface 기반의 가벼운 Dynamic Projections 가 좋다고 할 수 있다. interface 의 경우 naming 만 같다면 재사용이 가능 하다.

그리고 데이터 수가 많아 데이터 바인딩하는 부분 과 해당하는 부분에 대해서 개선을 원한다면, Class 기반으로 작성을 해서 Setter 와 생성자를 사용하는 방법을 권장한다.

Reference

--

--