Skip to content

[로직] Native Query와 페이지네이션 함께 사용하기

이은비 edited this page Mar 29, 2023 · 1 revision

배경

명식이 Version 2에서 추가된 기능 중 하나인 찜꽁 리스트 구현에 사용되는 가게 데이터에 변경이 발생하여 로직을 변경하였습니다.

[기존] Scarp( ScarpId, UserId, StoreId ) User ( UserId, PhoneId )

이때 StoreId는 카카오 MAP API에서 넘겨 받는 가게별 고유 ID 값을 프론트엔드 측에서 전달해주는 값입니다.

찜꽁리스트 기능은 단순히 유저가 [맛집 탭]에서 추천순으로 보여지는 가게에 대해 찜꽁 버튼을 눌러 자신만의 찜꽁 리스트를 만들어 관리할 수 있습니다. 서버로부터 가게 고유의 ID 값을 받으면 프론트엔드 측에서 카카오맵 API로 가게 데이터를 조회해서 유저에게 노출시키는 식의 로직으로 구성되어 있었습니다. 여기에 검색 기능과 정렬조건의 추가 등으로 인해 서버에서 카카오맵 API로 부터 받던 데이터를 프론트엔드 측에 제공하는 식의 로직으로 변경이 되어야 했습니다.

아래와 같이 ERD 구성을 변경하였습니다.

[변경] Scarp( ScrapId, UserId, StoreId ) User ( UserId, PhoneId ) Store( StoreId, Code, Name, Distance, Category, Address, Contact, urlAddress, Campus )

StoreId는 "변경 전" StoreId 와는 완전히 다른 값입니다. Store 테이블에 대한 PK 값이며, 기존의 StoreId는 Store 테이블의 code로 사용 중입니다.

적용 과정

[맛집 탭]에서 가게별 유저가 담은 찜꽁 수를 페이지네이션으로 노출하기 위해서 아래와 같이 DTO를 정의하였습니다.

[ScrapCountResponse.java]

image

이때의 문제점은 JPA에서 기본적으로 제공해주는 Pageable 에 Count 집계함수로 데이터 조작을 겸할 수 없다는 것입니다. 이에 native query로 페이지네이션과 DTO 형식에 맞는 데이터를 가공하였습니다.

[ScrapRepository.java]

image

Scrap 테이블과 Store 테이블을 JOIN 하여 Count() 함수를 적용한 커스텀 함수입니다. 이때 리턴 타입을 보면, Scrap 테이블이 아닙니다. 커스터마이징으로 구현할 경우, 인터페이스 정의가 필요로 합니다.

[CountResponse.java]

image

getter 함수만을 정의하여 JPA 에서 데이터를 매핑할 수 있도록 하는 인터페이스를 구현하였습니다. Entity 클래스를 리턴 타입으로 두는 경우에는 기본적으로 Entity 클래스를 정의할 때, 개발자가 Getter 메소드 혹은 어노테이션을 정의합니다.

이 함수는 Service 계층에서 아래와 같이 사용합니다. image

적용 결과

POSTMAN 도구를 이용해 테스트를 진행하며 아래와 같이 응답이 이루어집니다.

image

적용 과정에서의 고민 포인트

native query로 조회 쿼리를 작성했는데, 이때 count()함수를 이용해 집계하는 것이 좋은 지, Store 테이블에 count() 함수를 대신할 컬럼을 두어 insert/delete 시, 값을 업데이트 하는 것이 좋은지에 대한 판단 기준이 명확하지 않아 count() 함수를 사용하는 것으로 로직을 구성하였습니다. 이는 현재 버전의 서비스가 안정화가 된 후에 Ngrinder 도구를 이용해 성능 테스트를 진행할 예정입니다.

추가 변경 포인트

[맛집 탭]에서 기본 조회 조건은 "추천순, 인기순"이 있었습니다. 여기에 "거리순" 조건을 추가함에 따라 현재 API에서 sort 값을 distance로 넘겼을 때를 테스트하였습니다.

distance를 기준으로 정렬을 진행하면 오류가 발생했습니다. 오류의 원인은 native query에서 INNER JOIN 방식으로 진행했는데, 기준 테이블에 해당 컬럼이 존재하지 않았기 때문입니다.

오류를 해결하기 위해 쿼리문을 아래와 같이 수정했습니다.

image

JOIN 기준 테이블을 scrap 에서 store로 변경하였더니 앞선 오류를 해결할 수 있었습니다.

하지만 조회된 결과 정렬이 올바르게 이루어지지 않았습니다. 콘솔 창에서도 어떠한 오류 메세지가 발생하지 않아서 정렬이 올바르게 이루어지지 않은 이유에 대해 고민하였습니다.

원인은 바로 데이터 타입이었습니다. 추가된 정렬 조건인 거리(distance)가 Store 테이블에 varchar(255)로 되어 있었습니다. 거리 속성을 String 타입으로 지정했던 이유는 최대 거리가 어느 정도인지 전달받지 않은 상태였기 때문이었습니다. 문자열 타입이어서 정렬이 올바르게 이루어지지 않았습니다.

이를 해결하기 위해 생각한 방법은 2가지 입니다.

  1. 조회 쿼리에서 출력되는 값 ( distance )에 타입 캐스트 함수 적용하기
  2. 운영 DB에 콘솔로 접근하여 distance 컬럼의 데이터 타입을 변경하는 ALTER 구문 실행하기

1번 방식으로 로직을 변경하였습니다. 2번을 선택하지 않은 이유는 운영 환경에서 DB에 테이블 조작 쿼리를 날리는 행위가 위험요소로 작용할 수 있다고 판단했기 때문입니다. 데이터가 모두 Long 타입 내의 해당하고 null 이나 문자열 데이터가 들어가 있지 않다는 보장이 없기 때문에 이슈가 발생할 수 있습니다. 따라서 1번 방식을 선택하였습니다.