Spring JPA 무한 스크롤 구현기 근데 이제 다양한 오류를 곁들인...

timo·2021년 8월 8일
3

troubleshooting

목록 보기
6/11
post-thumbnail

안드로이드 모바일 어플리케이션을 개발해보는 프로젝트를 진행 중 인데, 나는 Spring JPA를 이용하여 백엔드를 개발 중 이다. 다른 사람들과 하는 프로젝트와 Spring과 JPA 모두 첫 경험이기에 다양한 난관들과 마주치며 진도를 나가고 있다. 이 게시글을 통해 최근 가장 많은 시간을 쏟고 다양한 것을 배울 수 있었던 문제를 해결한 과정을 기록해 보았다.

👀 무한 스크롤을 구현해야 한다고..?

처음 이런저런 기능사항들을 정할 땐, 멍청하면 용감하다고 큰 생각없이 - 무한 스크롤 형태로 구현 을 적어 넣었다. 시간이 흘러 해당 부분을 내가 맡게 되었는데, 다시 생각해보니 막막함이 앞섰다.

구글링 결과 wbluke님의 https://wbluke.tistory.com/18 해당 게시글 뿐만 아니라 다양한 좋은 글들을 확인할 수 있었다. 무한 스크롤을 구현하기 전 Pagination, Paging정확히 뭐지에 대해 먼저 알아야 했다.

우리는 구글링만 해도 하단에 1, 2, 3... 등의 페이지를 통해 문서들을 탐색할 수 있다. 한꺼번에 너무 많은 데이터를 불러오게 되면 해당 페이지를 로드하는데 긴 시간이 걸리기 때문이다. 이렇게 페이지를 나누는 것을 Paging한다고 표현하는 것 같았고, 이를 JPA에서 손쉽게 해주는 것이 Pageable 인터페이스의 구현체 PageRequest였다.

이를 이해하니 무한 스크롤의 원리는 크게 어렵지 않았다. 첫 화면에서 페이지 1을 뿌려주고, 해당 화면에서 스크롤을 내려 하단에 도착하면 페이지 2를 요청하면 되는 것 이었다. 이렇게 끝 페이지까지 반복하는 것이 바로 무한 스크롤이었다.

👀 왜 interface를 구현해주지 않지..?

우선 내가 구현해야 하는 것은 다음과 같았다.

  1. A and B and ( C or D or E ) 를 만족하는 데이터들을 Page 형태로 찾아서
  2. 무한 스크롤로 프론트 측에 넘겨주어야 했다.

가장 큰 문제가 1.이었다. 이 모든것을 처음 접하는 나로선 인터넷의 다양한 코드를 최대한 참고해야 했는데, 대부분의 예제들이

  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만 발견할 수 있었다.

결국 끙끙거리다가 프로젝트 멘토님에게 질문을 드렸다. 큰 설명없이 보내주신 링크 하나.

https://jobc.tistory.com/120

1) interface를 구현하지 않았는데 작동된다는 점 은 JPA의 장점 중 하나였다. ㅋㅋㅋㅋㅋㅋ JpaRepository를 extends하면 자동으로 구현해주기 때문에 그저 interface만 있어도 되는 것 이었다.

2) 조건을 달면서 원하는 데이터를 가져오는 법 은 Repository단계의 Query 메소드에 키워드를 활용하면 될 것 같았다.

👀 괄호를 어떤 키워드로 표현하지..?

  1. 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 게시글을 통해 해결할 수 있는 방법을 발견했다.

https://stackoverflow.com/questions/35788856/spring-data-jpa-how-to-combine-multiple-and-and-or-through-method-name

@Query annotation을 사용하고, @Query("select m from ~~")이런식으로 JPQL문을 직접 적어주면 되었다. 그러면 따로 interface를 구현하지 않아도 되었고, PageRequest 객체도 인자로 사용할 수 있었다.

👀 기타 마주친 오류들!

🧠 findOne이 왜 오류를..?

JPA의 기능으로서 findOne 메소드가 repository단계에서 제공된다고 인지하고 있었다. 하지만 findOne에서

Inferred type 'S' for type parameter 'S' is not within its bound;

와 같은 오류 메세지가 발생했다.

https://stackoverflow.com/questions/52634362/inferred-type-s-for-type-parameter-s-is-not-within-its-bound-should-extend

해당 stackoverflow에서 JpaRepository가 findOne을 다르게 정의하고 있는 것을 알 수 있었다. 최신 버전에서 달라진 것 같았다

https://programmer7895.tistory.com/18

결국 해당 게시글을 바탕으로, findOne -> getById, delete => deleteById 등 비슷한 기능을 하는 메소드로 바꾸어주었다.

🧠 DTO..? Assembler..?

wbluke님의 무한 스크롤 게시글을 참고해 가며 열심히 코딩하던 중, ~Response, ~Assembler 등의 클래스들을 발견할 수 있었다. 구글링 결과 Data Transfer Object의 약자 DTO와 관련한 개념들이었다. 처음 접하는 개념이라 낯설고 당황스러웠지만, 어찌어찌 비스무레하게 메소드를 구현할 수 있었다. 이에 대한 내용은 개인적으로 추가적인 정리가 필요하다고 느꼈다.

🧠 ResponseEntity..?

무한 스크롤을 구현하는 메소드 이전에는 API의 응답을 json객체를 만들어 일일히 추가하여 return해주었다. 하지만 ResponseEntity라는 좋은 도구가 있었다. 이 역시 처음 접했기 때문에 당황스럽고 이해가 필요했지만, 구현 이후 훨씬 코드가 보기 좋고 간결해졌다.

마치며

프로젝트 진행을 Spring과 JPA의 공부와 병행하다보니 이런 일이 생기는 것 같다. JPA의 Query부분을 빠르게 넘기고 프로젝트로 뛰어들다보니.. 어쩔수없다 그래도 이렇게 겪으면서 절대 까먹지 않을 것 같다 ㅎㅋ

profile
Backend Developer

0개의 댓글