🔐 들어가며
안녕하세요! 이번 포스팅에서는 기존 리스트에 무한 스크롤링 방식을 추가하여 성능을 개선한 사례를 소개해드리려고 합니다.
🐤 기존 로직
SELECT *
FROM 테이블
WHERE 검색조건
ORDER BY id
기존에 리스트를 가져올 때는 SELECT 된 결괐값들을 모두 가져와서 리스트에 담아서 View 단에서 반복문을 돌려서 뿌리는 방법을 사용하곤 했습니다.
위 사진과 같이 조회한 결괏값이 51,000개가 나오면 51,000개의 결괏값이 모두 리스트에 담겨 뷰에서 51,000개를 반복을 돌리곤 했죠.
그런데 ...
너무 느렸습니다!
안 그래도 하루에 많은 양의 데이터가 쌓이는데 단순 로그성 데이터들을 보려고 몇 초나 기다려야 하는 것은 정말 고된 기다림이였습니다 ... 😱
그래서 어떻게하면 성능을 개선할 수 있을까 방안을 모색하다가, 무한 스크롤(Infinite Scroll)을 도입하여 성능을 개선하기로 결정했습니다.
처음에는 페이지네이션(Pagination)으로 구현할까 생각을 했지만, 이 리스트를 보는 목적과 상황을 고려했을 때, 무한 스크롤을 선택하는 것이 더 맞는 것 같아 무한 스크롤로 결정했습니다.
🐶 개선 방안
이 포스팅에서 굉장히 많은 인사이트를 얻고 개선방안을 고민해보았습니다.
먼저, 페이지에 진입하게 되면 최초 1회 넘겨주는 데이터를 전체 결과 리스트에서 아래와 같이 변경했습니다.
- 조건에 맞는 총 카운트 개수
- 첫 페이지에서 가장 상단에 있어야 할 건의 id
public String getLogs(Model model) {
int count = // 조건에 맞는 카운트 개수
int lastId = // 첫 페이지에서 가장 상단에 존재하는 건의 id
model.addAttribute("count", count);
model.addAttribute("lastId", lastId);
// ...
}
그리고 스크롤이 맨 아래에 닿으면 다음 데이터 50개를 가져올 API용 메소드를 만들었습니다.
public ResponseEntity getLogs() {
List<Log> logs = // 보여줘야할 50개 로그 데이터들
return ResponseEntity.ok()
.body(logs);
}
// 메소드에서 사용되는 쿼리
SELECT *
FROM 테이블
WHERE id > #{가장 마지막 데이터의 id} AND 검색조건
ORDER BY id
LIMIT 50
API에서 전달받을 가장 마지막 데이터의 id보다 큰 50개를 가져오는 쿼리를 작성했습니다. 만약 DESC
정렬을 사용한다면 괄호의 방향을 바꿔주어야 합니다!
이 쿼리는 언뜻 보면
SELECT *
FROM 테이블
WHERE 검색조건
ORDER BY dcs.seq
OFFSET #{현재까지 총 보여준 갯수}
LIMIT 50
위 쿼리와 다른게 없어보이지만, OFFSET
을 사용하게 되면 버리지만 읽어야하는 행의 개수가 많아지기 때문에 점점 느려지게 됩니다. 예를 들어 OFFSET 500000 LIMIT 50
이라면, 앞의 50만개는 사용하지 않기 않는 데이터이기 때문에 버려야하지만, 읽어야하기 때문에 당연히 성능면에서 좋지 않습니다. 때문에 OFFSET
을 사용하지 않고, 인덱스로 걸려있는 PK를 WHERE
에 사용하여 성능을 개선했습니다.
위 사진은 제가 사용한 무한 스크롤링 방법입니다. 스크롤이 바닥에 닿게 되면 맨 마지막 요소의 PK를 요청과 함께 보내고, 응답으로는 리스트마다 각 요소들의 내용과 PK를 함께 전달했습니다.
(지금 생각하니까 요소마다 PK를 리스트에 담아줄 필요 없이 맨 마지막 PK만 줘도 무관할 것 같네요... 시간이 된다면 수정해야 할 것 같습니다 ㅎㅎ...)
🚀 프론트 관련 코드는 이 포스팅에서 확인하실 수 있습니다!
🏰 성능 차이
당연히 무한 스크롤을 적용하기 전/후는 성능면에서 굉장히 많은 차이가 났습니다.
적용하기 전
적용한 후
속도 면에서는 약 60배 이상이 차이 났고, 데이터가 많아지면 많아질수록 훨씬 더 차이가 커지는 것을 확인할 수 있었습니다.
✨ 맺으며
어떻게 보면 처음부터 스크롤링(또는 페이징)을 적용해서 구현을 해야 했지만, 맨 초기 개발할 당시에는 빨리 결과물이 나와야했고, 쌓이는 양이 굉장히 적었기 때문에 이런 부분을 간과해서 짰던 것 같습니다. (아마 쌓이는 양이 많아지지 않았다면 리팩토링을 할 필요성도 못 느꼈을 것 같습니다 😂)
누구나 생각할 수 있는 간단한 아이디어와 코드로 성능을 훨씬 개선할 수 있는데요. 만약에 저와 같은 상황이라면 이 방법을 고려해봐도 좋을 것 같습니다!
혹시 글을 읽으면서 잘못된 내용이 있거나 더 좋은 아이디어가 있다면 댓글로 알려주시면 감사하겠습니다. 읽어주셔서 감사합니다! 😊
👏 참고링크
'Database' 카테고리의 다른 글
데이터베이스의 트랜잭션(Database Transaction) (0) | 2019.03.03 |
---|