Skip to content

[성능 개선] 게시물 조회 캐시 적용

YoonTaeMin edited this page Jun 15, 2023 · 15 revisions

캐시 적용 위치

  • 메인페이지에서 [좋아요수를 가장 많이 받은 게시물 top5를 조회하여 보여주는 api]
  • 커뮤니티탭에서 [게시글 카테고리와 검색에 따른 게시물 조회 api(페이징처리)]

필요성

메인페이지는 사용자가 가장 많이 접속하는 페이지이기 때문에 메인페이지에 접속할 때마다 게시물 조회 api를 호출하여, db로부터 데이터를 조회해오는 것은 비효율적이라고 생각했습니다. 따라서, 메인페이지에서의 게시글 조회에 캐시를 적용하여 중복적인 쿼리의 수를 감소시켰습니다. 또한, 메인페이지 뿐 아니라 [검색조건과 페이징처리]가 되어있는 게시글 조회에서도 캐시를 적용하였습니다. 사용자는 1페이지에서 2,3,4..페이지로 이동 후, 다시 이전 페이지로 돌아와서 조회를 하는 경우가 많을 것이라고 판단하였기 때문에 캐시를 적용하여 반복적인 조회쿼리를 감소시키고자 하였습니다. 하지만, 하나의 게시글이라도 생성되거나 수정, 삭제가 발생할 경우, 캐시에 저장되어 있는 모든 데이터를 삭제하여 데이터의 불일치를 해결해줘야 합니다. 이는 trade-off 가 있지만, 서비스 초기 단계 즉, 사용자가 많지 않은 상황에서는 게시글의 변경이 빈번하게 일어나지 않을 것이기 때문에, 캐시적용을 시도하기에 적합하다고 판단했습니다.

구현

캐싱는 redis를 사용하여 구현하였습니다. local-memory 기반의 캐시인 ConcurrentMapCacheManager를 사용하여 구현할 수 있지만, 다중 서버에 대비하여 global하게 사용하기 위해 외부 캐시 db인 redis를 사용하였습니다.

메인페이지

  • 캐시 key : 게시글 카테고리
  • 캐시 value : 좋아요수 게시글 top5

페이징 조회

  • 캐시 key : 게시글 검색키워드 and 게시글 카테고리 and 페이지번호
  • 캐시 value : 페이지 별 게시글 목록

스프링에서 제공하는 @Cacheable , @CacheEvict 사용하여 구현하였습니다.
최초의 게시글 조회에서는 db에서 데이터를 조회하고, 조회한 데이터를 캐시에 key-value 형태로 저장합니다. 이 후, 요청에서는 db를 조회하지 않고, 캐시에서 데이터를 조회하여 반환합니다. -> @Cacheable
또한, 게시글의 변경(수정,삭제)가 발생할 경우, 캐시데이터를 삭제합니다. -> @CacheEvict

캐시 데이터의 유효기간은 3시간으로 설정하였습니다. 서비스 초기의 상태여서 캐시데이터의 변경빈도가 많이 발생하지 않을 것이라 판단하였고, 추후에 사용자가 늘어남에 따라 유효기간을 줄일 예정입니다.

PageImpl 커스터마이징

페이징객체를 반환하는 부분에 캐시를 적용하다가 org.springframework.data.redis.serializer.SerializationException 가 발생하였습니다.
object에서 json으로, json에서 object로 직렬화, 역직렬화를 수행하려면 dto class에 기본생성자가 존재하거나 @JsonProperty 어노테이션이 존재해야 합니다. 하지만, 페이징처리를 위해서 Page 인터페이스를 사용하기 때문에 이를 적용할 수 없었습니다. 따라서 Page의 구현체인 PageImpl 클래스를 상속하여 커스터마이징하였습니다. 스크린샷 2023-06-16 오전 3 44 56
추가적으로, Page에 포함된 Pageable 객체에 대해서도 직,역직렬화를 수행할 수 없기 때문에 @JsonIgnore를 사용하여 제외시켰습니다.

성능 개선

결과적으로 같은 요청에 대한 응답시간을 단축하였습니다. (158ms -> 39ms)
스크린샷 2023-05-29 오후 11 02 50

스크린샷 2023-05-29 오후 6 53 54
Clone this wiki locally