안드로이드 모바일 어플리케이션을 개발해보는 프로젝트를 진행 중 인데, 나는 Spring JPA를 이용하여 백엔드를 개발 중 이다. 다른 사람들과 하는 프로젝트와 Spring과 JPA 모두 첫 경험이기에 다양한 난관들과 마주치며 진도를 나가고 있다. 이 게시글을 통해 최근 가장 많은 시간을 쏟고 다양한 것을 배울 수 있었던 문제를 해결한 과정을 기록해 보았다.
처음 이런저런 기능사항들을 정할 땐, 멍청하면 용감하다고 큰 생각없이 - 무한 스크롤 형태로 구현
을 적어 넣었다. 시간이 흘러 해당 부분을 내가 맡게 되었는데, 다시 생각해보니 막막함이 앞섰다.
구글링 결과 wbluke님의 https://wbluke.tistory.com/18 해당 게시글 뿐만 아니라 다양한 좋은 글들을 확인할 수 있었다. 무한 스크롤을 구현하기 전 Pagination
, Paging
정확히 뭐지에 대해 먼저 알아야 했다.
우리는 구글링만 해도 하단에 1, 2, 3... 등의 페이지를 통해 문서들을 탐색할 수 있다. 한꺼번에 너무 많은 데이터를 불러오게 되면 해당 페이지를 로드하는데 긴 시간이 걸리기 때문이다. 이렇게 페이지를 나누는 것을 Paging
한다고 표현하는 것 같았고, 이를 JPA에서 손쉽게 해주는 것이 Pageable
인터페이스의 구현체 PageRequest
였다.
이를 이해하니 무한 스크롤의 원리는 크게 어렵지 않았다. 첫 화면에서 페이지 1을 뿌려주고, 해당 화면에서 스크롤을 내려 하단에 도착하면 페이지 2를 요청하면 되는 것 이었다. 이렇게 끝 페이지까지 반복하는 것이 바로 무한 스크롤이었다.
우선 내가 구현해야 하는 것은 다음과 같았다.
A and B and ( C or D or E )
를 만족하는 데이터들을 Page
형태로 찾아서 가장 큰 문제가 1.
이었다. 이 모든것을 처음 접하는 나로선 인터넷의 다양한 코드를 최대한 참고해야 했는데, 대부분의 예제들이
Page
형태로 찾아서였다. 즉 내가 원하는 A and B and ( C or D or E )
이 조건을 적용하면서 Page
형태로 repository에서 가져오는 예제를 쉽게 찾을 수 없었다. 대부분 JpaRepository interface를 extends하면 따로 구현하지 않아도 함수 파라미터에 PageRequest를 구현해 넣기만 하면 됩니다~ 의 내용이었다.
1) interface를 구현하지 않았는데 작동된다는 점과, 2) 조건을 달면서 원하는 데이터를 가져오는 법이 궁금했다.
추가적으로 구글링을 하다 앞서 언급했던 wbluke님의 https://wbluke.tistory.com/18 게시글을 찾을 수 있었다. 해당 글도 repository에 대한 큰 코드 설명이 없었기 때문에, 나의 궁금증은 증폭되어가기만 했다. 나는 wbluke님의 github를 통해 해당 게시글 프로젝트의 소스코드까지 살펴보았지만, 그저 메소드 명만 적혀 있는 interface만 발견할 수 있었다.
결국 끙끙거리다가 프로젝트 멘토님에게 질문을 드렸다. 큰 설명없이 보내주신 링크 하나.
1) interface를 구현하지 않았는데 작동된다는 점 은 JPA의 장점 중 하나였다. ㅋㅋㅋㅋㅋㅋ JpaRepository를 extends하면 자동으로 구현해주기 때문에 그저 interface만 있어도 되는 것 이었다.
2) 조건을 달면서 원하는 데이터를 가져오는 법 은 Repository단계의 Query 메소드에 키워드를 활용하면 될 것 같았다.
A and B and ( C or D or E )
를 만족하는 데이터들을 Page
형태로 찾아서 를 구현하기 위해 키워드를 활용하여 Query메소드 이름을 정해보았다. 그랬더니 기괴한 형태의 엄청나게 긴 메소드 이름이 만들어졌다.
List<Measure> findByUserAndTemperatureHighBetweenOrTemperatureLowBetweenOrHumidityBetween(User user, Float tempHigh1, Float tempHigh2, Float tempLow1, Float tempLow2, Float humid1, Float humid2);
이런... 형태였는데 우선 잘 작동하는 듯 보였다. 하지만 몇 번 테스트를 거친 결과 오류를 찾을 수 있었다. 바로 이 메소드는 A and B and ( C or D or E )
가 아닌 A and B and C or D or E
를 표현하고 있었다.
즉 내가 원하는 것은 C, D, E 중 하나만 참이어도 해당 데이터를 불러오는 것 이었지만, 이 메소드는 C가 거짓이라면 데이터를 불러오지 않았다.
이 문제를 해결하기 위해 키워드로 괄호를 표현하는 법을 찾았지만 쉽지 않았다. 대신 다음 stackoverflow 게시글을 통해 해결할 수 있는 방법을 발견했다.
@Query
annotation을 사용하고, @Query("select m from ~~")
이런식으로 JPQL문을 직접 적어주면 되었다. 그러면 따로 interface를 구현하지 않아도 되었고, PageRequest 객체도 인자로 사용할 수 있었다.
JPA의 기능으로서 findOne 메소드가 repository단계에서 제공된다고 인지하고 있었다. 하지만 findOne
에서
Inferred type 'S' for type parameter 'S' is not within its bound;
와 같은 오류 메세지가 발생했다.
해당 stackoverflow에서 JpaRepository가 findOne
을 다르게 정의하고 있는 것을 알 수 있었다. 최신 버전에서 달라진 것 같았다
https://programmer7895.tistory.com/18
결국 해당 게시글을 바탕으로, findOne
-> getById
, delete
=> deleteById
등 비슷한 기능을 하는 메소드로 바꾸어주었다.
wbluke님의 무한 스크롤 게시글을 참고해 가며 열심히 코딩하던 중, ~Response
, ~Assembler
등의 클래스들을 발견할 수 있었다. 구글링 결과 Data Transfer Object의 약자 DTO와 관련한 개념들이었다. 처음 접하는 개념이라 낯설고 당황스러웠지만, 어찌어찌 비스무레하게 메소드를 구현할 수 있었다. 이에 대한 내용은 개인적으로 추가적인 정리가 필요하다고 느꼈다.
무한 스크롤을 구현하는 메소드 이전에는 API의 응답을 json객체를 만들어 일일히 추가하여 return해주었다. 하지만 ResponseEntity
라는 좋은 도구가 있었다. 이 역시 처음 접했기 때문에 당황스럽고 이해가 필요했지만, 구현 이후 훨씬 코드가 보기 좋고 간결해졌다.
프로젝트 진행을 Spring과 JPA의 공부와 병행하다보니 이런 일이 생기는 것 같다. JPA의 Query부분을 빠르게 넘기고 프로젝트로 뛰어들다보니.. 어쩔수없다 그래도 이렇게 겪으면서 절대 까먹지 않을 것 같다 ㅎㅋ