Backend 개발/Backend 성능 개선

[애플리케이션 내 캐시 구현하여 백엔드 조회 API 성능 개선하기 ②]

수달리즘 2025. 4. 7. 10:51
반응형

1. 애플리케이션 내 캐시 만들어서 성능 개선

Redis를 이용하기에 앞서 애플리케이션 내에서 사용할 수 있는 캐시를 직접 구현해본다.

캐싱할 데이터가 잘 변하지 않을 경우에는 Redis와 같은 공유 캐시보다 애플리케이션 내에서 캐시를 사용하는 것이 더 유용할 수 있다. 다만 이런 경우에는 서버가 여러 대일 때 데이터가 서로 공유가 안되는 단점이 존재한다.

해당 예제에서는 다음과 같이 ConcurrentHashMap을 이용하여 캐시를 만든다.

..
import java.util.concurrent.ConcurrentHashMap;

@Repository
public class ShortenUrlRepositoryImpl implements ShortenUrlRepository {

    private final JpaShortenUrlRepository jpaShortenUrlRepository;
    private final ConcurrentHashMap<String, ShortenUrl> cache;

    @Autowired
    public ShortenUrlRepositoryImpl(JpaShortenUrlRepository jpaShortenUrlRepository) {
        this.jpaShortenUrlRepository = jpaShortenUrlRepository;
        this.cache = new ConcurrentHashMap<>();
    }

    @Override
    public void saveShortenUrl(ShortenUrl shortenUrl) {
        jpaShortenUrlRepository.save(shortenUrl);
        cache.put(shortenUrl.getShortenUrlKey(), shortenUrl);
    }

    @Async
    @Override
    public void asyncSaveShortenUrl(ShortenUrl shortenUrl) {
        jpaShortenUrlRepository.save(shortenUrl);
        cache.put(shortenUrl.getShortenUrlKey(), shortenUrl);
    }

    @Override
    public ShortenUrl findShortenUrlByShortenUrlKey(String shortenUrlKey) {
        // 먼저 캐시에서 조회하도록 성능 개선
        ShortenUrl shortenUrl = cache.get(shortenUrlKey);
        if(shortenUrl == null) {
            // 캐시에 없으면 db에서 조회
            shortenUrl = jpaShortenUrlRepository.findByShortenUrlKey(shortenUrlKey);
            if(shortenUrl != null) {
                // db에 있으면 캐시에 저장
                cache.put(shortenUrlKey, shortenUrl);
            }
        }
        return shortenUrl;
    }
}

조회 요청이 들어오면 다음의 순서로 동작한다.

  • ConcurrentHashMap(캐시)에 존재하는지 체크
    • 존재하면 해당 단축 데이터 리턴
    • 존재하지 않으면 DB 조회 후 캐시에 저장(Map)

실행 결과

2. JPA dirty check 로직 수정

코드를 위와 같이 수정하고 실행하면 update문과 select문이 번갈아가며 실행되는 것을 확인할 수 있다. JPA가 엔티티에 대해 dirty check를 하기 때문에 이러한 요소가 성능이 저하되는 요인이 될 수 있다. 데이터가 변할 때 select문이 실행되지 않도록 하여 성능 개선을 도모하고자 한다면 코드를 다음과 같이 수정한다.

..
// update문만 실행되도록 네이티브 쿼리 작성
@Modifying
@Transactional
@Query("UPDATE ShortenUrl s SET s.redirectCount = s.redirectCount + 1 WHERE s.shortenUrlKey = :shortenUrlKey")
int incrementRedirectCount(String shortenUrlKey);

실행 결과

성능 개선이 되긴 했지만 해당 예제에서 중요하게 알아야 될 점이 있다.

성능 개선을 한다고 이런저런 작업을 했는데 오히려 성능이 떨어질 수 있다. 이럴 때 원인을 파악하고 다양한 방법으로 개선해나가는 작업을 해야 한다는 것이 중요하다. 해당 예제에서는 JPA의 특징 때문에 SELECT문이 실행되는 것을 막았는데 이게 좋은 방안이라고는 할 수 없다. 이보다 더 좋은 방법을 찾을 수 있어야 한다.

다음 예제에서는 Redis를 이용하여 캐시를 사용해볼 것이다. → https://soodal0328.tistory.com/39

728x90
반응형