SQL문 작성

개뉸·2022년 10월 9일
0
post-thumbnail

JPA.
java persistance API의 약자로 인터페이스 이다.
ORM 을 기준으로 만든 인터페이스 집단으로, 인터페이스 이기 때문에 구현체가 필요하고 이를 구현한 대표적인 오픈소스로는 Hibernate가 있다.
Spring boot 를 실행 시켜보면서 콘솔창에서 많이 봐왔던 익숙한 단어다.

그럼 ORM은 뭐지?
object relational mapping 이라고 해서 구현체인 class 와 RDB(관계형 데이터베이스) 를 매핑해주는 역할을 한다. 이렇게 되면 sql문을 통해 DB와 연결하는게 아니라 매서드 수준에서 DB와 연결한다. 이렇게 되면 개발자가 sql 쿼리를 신경쓰지 않고 비즈니스 로직에만 집중할 수 있으며 코드의 량도 현저히 줄어든다.

Spring Data Jpa 를 쓸때를 생각해보면 repository를 생성할때 가볍게 extend 해오고
findByUsername 과 같은 제공된 ORM 을 사용해서 조회를 매우 간편하게 사용해왔던걸 기억해보자. sql 문을 작성한다는 것 자체를 생각도 못해봤고 그런 것이 있다는 말을 들었을 때는 나와는 상관 없는 일이라고 지나갔던 날이 기억난다. 실전 프로젝트 때 이 생각이 뒤엎어졌다.

"내가 작성자 인 게시물은 제외하고 내가 숨김 처리한 게시물 까지 처리한 상태에서 다른 사람의 게시물에 참여한 게시물만 보여주세요."

우리 프로젝트는 그림을 여러사람이 이어 그리고 끝에는 이 그림들을 하나의 파일로 엮은 gif 로 만들어서 움직이게 만드는 서비스 이다. 그러다 보니 처음 게시물 작성자가 있을 것이고 그 게시물에 참여한 참여자들이 있을 것이다. 여기에 추가로 보기싫은 게시물을 해당 계정에게만 보이지 않게 숨김 처리하는 기능까지 만들었다. 여기서 사용자의 마이페이지에서 위와 같은 필터를 적용한 조회를 부탁받았다. 도저히 spring data jpa 로 구현할 방법이 떠오르지 않았다.

"이럴 바에는 얼른 sql을 직접 작성하는 방법을 공부해서 적용해봅시다"

해당 조건으로 필터를 건 조회는 중요한 도전이 될 것이었기에 반드시 적용했어야 했다.
나와는 상관 없을거라고 여겼던 쿼리문 에 대해 공부하게 된 계기였다.
이를 위해 QueryDsl 이라는 프레임워크를 사용하게 되었다.
문자가 아닌 코드로 쿼리를 작성함으로써, 컴파일 시점에 문법 오류를 쉽게 확인할 수 있고
자동 완성 등 IDE의 도움을 받을 수 있으며 동적인 쿼리 작성이 편리했다.
쿼리 작성 시 제약 조건 등을 메서드 추출을 통해 재사용할 수도 있어서 대표적으로 많이 소개되고 있었고 우리도 이것을 사용했다.

하지만 spring boot 에서 이를 적용하려면 먼저 작성할 설정들이 좀 많았다. gradle 설정도 해주고 compile 과정도 거쳐서 Q클래스도 생성해야 했다.
JPQLQueryFactory 라는 인터페이스를 implement 한 JPAQueryFactory를 다시 implement 해와서 구현체로 구현하여 사용했다.


QueryFactory에서 사용하는 것은 DB를 컴파일로 따온 Q클래스이다. 그러므로 우선 Q클래스들을
선언해주는 작업이 필요하다. 구현하려는 조건 조회를 위해서 빌더는 1과2 두개로 나눠서 조건을 정하고 적용했다.


tabNum 이란 '전체/작성한 게시글/참여한 게시글/숨긴 게시글' 을 나누는 것을 변수로 지정해서 사용한 것이다.
빌더2에는 들어가면 안되는 항목을 넣었고 빌더1에 들어갈 항목을 넣었다. 이 빌더2를 사용해서 내가 참여한 게시글의 필터에 작성한 글과 숨긴글을 필터로 뺄 수 있었다.
tabNum 의 값이 2일때가 참여한 게시글 조회 필터이다. 해당 유저와 이름이 같은 게시글 작성자를 모두 제외하고 불러온다.


필터한 빌더1과 빌더2를 적용하면 끝이다. 매우 간단하게 해당 문제를 해결했다.
밑으로는 리스폰스로 보낼 데이터를 만드는 과정이다.

"이왕 조회 부분을 JPQL로 작성했으니 마이페이지 말고도 공통 조회 부분도 싹 다 바꿔버리죠"

일관성을 위해서 현재 spring data jpa로 작성된 공통 조회 부분도 queryDsl 을 적용해서 작성하기로 했다. 공통 조회라 함은 이제 마이페이지가 아니라 모두가 볼 수 있는 조회 페이지로써, 전체/제시어o/제시어x 게시물들을 카테고리 조회/좋아요/댓글/최신 순으로 볼 수 있는 페이지 이다. 단순한 조건 조회 라서 기본 제공 쿼리로도 구현이 쉽게 되었지만 카테고리 4분류에 탭이 3가지라서 총 12개의 repository와 service를 구성해야 했으므로 중복 코드도 매우 많이 발생하긴 했다.
QueryDsl 로 작성했더니 response로 보낼 데이터를 만드는 과정은 중복되니 매서드로 빼고나서 조회 부분 필터를 정의하는 부분만 조건 별로 맞추고 나니까 엄청난 코드 리펙토링이 되어서 깜짝놀랐다.

if 문 투성이와 12개나 되는 repository로 난장판이 되었던 service 클래스가 이렇게 간단하게 필터링 과정을 거칠 수 있게 되었다.

우리 서비스는 조회 부분에 무한스크롤을 적용하고있다. 한번에 6개의 게시물을 불러오는데 사실 그렇기 때문에 QueryDsl로 작성한 조회부분이 성능 개선이 당장에 테스트 할 때에는 체감이 되질 않았다. 그러나 사용자가 많아져서 트레픽 량이 증가하게 될 경우를 생각했을때 돌아가는 로직이 이전과 비교했을때 렌더링 시간에 더 좋은 개선을 줄것이라 생각되었다.

다음은 jMeter 를 통해서 완료된 페이지에 대한 JPA기본/QueryDsl 각각의 API 부하테스트 이다.

JPA 기본 제공 query 를 적용 했을 때의 완료된 게시물 페이지의 100개의 게시물에 대한 15000 번의 API 테스트 결과 이다.




지연시간이 ms 기준으로 평균적으로 매우 높게 나오는게 보인다.
전체 응답 시간 평균은 ms 기준으로 4335 이다.

다음은 QueryDsl 을 적용 했을 때의 완료된 게시물 페이지의 100개의 게시물에 대한 15000 번의 API 테스트 결과 이다.




spring data jpa 의 query 를 사용했을 때 보다 지연시간이 현저하게 낮고 평균 응답시간도 3배 빨라졌다.

0개의 댓글