[Spring] Cache를 활용한 Query 중복 제거

김상현·2022년 11월 24일
0

Spring

목록 보기
8/13
post-thumbnail

📒 [실전! 스프링 부트와 JPA 활용 - 김영한] 프로젝트를 완성한 후 부족한 기능을 추가한 후 정리하는 글입니다.


📍 해결이 필요한 문제

🧷 회원 목록

🧷 Page 이동시 발생하는 쿼리

select
	member0_.member_id as member_i1_4_,
	member0_.city as city2_4_,
	member0_.street as street3_4_,
	member0_.zipcode as zipcode4_4_,
	member0_.email as email5_4_,
	member0_.password as password6_4_,
	member0_.username as username7_4_
from
	member member0_ limit ? offset ?

select
	count(member0_.member_id) as col_0_0_
from
	member member0_
  • 1번 페이지(page=1)에서 2번 페이지(page=2)로 이동한 후 다시 1번 페이지로 이동할 때 총 3번의 쿼리 전송이 발생합니다.
  • 페이지 번호가 바뀔때마다 쿼리문을 전송하고 결과값을 반환 받게 됩니다.
  • 하지만 첫 번째 쿼리와 세 번째 쿼리는 같은 쿼리이고 결과값도 같습니다.
  • 중복된 값을 얻기 위해 반복적인 쿼리가 발생하는 것은 네트워크 낭비라고 생각이 들었고, 이를 해결하기 위해 캐시(Cache)를 활용하기로 결정였습니다.

📍 문제 해결 과정

📒 [Spring 공식 문서] 의 Spring Cache를 적용하여 문제를 해결하였습니다.


📌 [1] Cache 생성

  • Spring 에서 제공하는 @EnableCaching@Cacheable 어노테이션을 이용하여 캐시를 생성하였습니다.
  • @EnableCaching 은 사용하고자 하는 클래스에 작성합니다.
  • @Cacheable 은 사용하고자 하는 메서드에 작성합니다.
    • @Cacheable 어노테이션이 적용된 메서드는 반환하는 결과값을 캐시에 저장해 두었다가 같은 결과값을 반환해야할 경우 캐시에 있는 값을 반환합니다.
  • 두 어노테이션을 Service 단계에 적용하여 코드를 작성하였습니다.

🧷 MemberService.class

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
@EnableCaching // 캐싱 기능 활성화
public class MemberService {
.
.
.
	@Cacheable("memberTotalPages") // 캐시 채우기
    public Page<MemberDto> findAll(Pageable pageable){
        return memberRepository.findAll(pageable).map(m -> new MemberDto(m));
    }
}
  • MemberService 클래스에 @EnableCaching 어노테이션을 적용하고, findAll(Pageable pageable) 메서드에 @Cacheable 어노테이션을 적용하였습니다.
  • 한번 방문했던 회원 목록 페이지를 다시 방문할 경우 쿼리를 전송하지 않고 캐싱에 저장된 정보를 화면에 뿌려줍니다.
  • 페이지 값에 해당하는 회원 정보 쿼리가 발생하지 않는다면 네트워크 상으로는 더 효울적인 방법은 맞지만, 새로운 데이터가 추가되었을 때 문제가 발생할 수 있다고 생각했습니다.
  • 만약 회원 목록의 모든 페이지가 캐시를 통해 저장된 상태일 때 회원이 추가되는 상황이 발생한다면, 새로운 데이터가 추가된 값을 화면에 뿌려주어야 하지만 캐시를 통해 데이터를 뿌리게 됩니다.
  • 인터페이스와 DB 간의 데이터가 불일치 하는 상황이 발생할 수 있습니다.
  • 이를 해결하기 위해 @CacheEvict 어노테이션과, Spring Scheduler를 이용하기로 하였습니다.

📌 [2] Spring Scheduler

  • @CacheEvict 어노테이션은 캐시의 값을 제거합니다.
  • @Scheduled 어노테이션은 Spring에서 제공하는 기능으로 fixedDelay 속성을 통해 반복적으로 수행될 수 있습니다.
@SpringBootApplication
@EnableScheduling // 스케줄링 기능 활성화
@EnableCaching // 캐싱 기능 활성화
public class JpashopApplication {
.
.
.
	@Scheduled(fixedDelay = 10000) // 10s에 한번씩 실행
	@CacheEvict(value = "members", allEntries = true) // 캐시 제거
	public void cacheInitialization(){}
}
  • ApllicationScheduler 를 추가하여 10초에 한번씩 캐시를 삭제해주는 코드를 구현하였습니다.

📍 고찰

  • 캐시를 적절한 곳에 적절하게 사용할 수 있다면 네트워크의 낭비를 줄일 수 있는 좋은 방법인 것 같습니다.
  • 하지만, 캐시가 제대로 갱신되지 않는다면 정보 불일치 문제가 발생할 수 있다는 것을 배웠습니다.
  • Spring을 공부하면서 항상 "어떻게 하면 조금이라도 네트워크에 전송되는 쿼리의 수를 줄일 수 있을까"에 대해서 많은 고민을 하는데, 쿼리의 수를 줄일려고 캐시를 남용하면 최신화 되지 않은 정보를 사용자가 이용하게 되는 문제가 발생할 수 있을 것 같습니다.
  • 개인적인 생각으로는 변화가 거의 발생하지 않는 데이터에 한에서만 캐시를 사용해야 하는 것 같습니다.
profile
목적 있는 글쓰기

0개의 댓글