✅ 개요
오늘의 질문 조회에 대해 캐싱을 도입하게 된 계기는 다음과 같다.
1️⃣ 호출 빈도가 높음
현재 프로젝트에서 오늘의 질문 조회 API는 매우 잦게 호출되는 API이다.
다음과 같이 메인페이지에서 답변을 볼 때 항상 노출되게 된다.
2️⃣ 수정 빈도가 낮음
현재 프로젝트에서 질문은 하루에 한번 변경되기 때문에 변경빈도가 매우 낮다.
✅ 구현
캐싱은 Redis를 활용하기로 결정했다.
현재 프로젝트에서 Redis를 사용하고 있고 다른 팀원분께서 Redis 캐싱에 대한 인터페이스를 구현해두신게 있어 간편하게 구현이 가능할 것 같아서 Redis를 선택했다.
1️⃣ 캐싱 전 코드
/**
* 카테고리로 질문 조회
*/
public QuestionFindServiceResponse findQuestionByCategoryId(Long categoryId) {
Question question = questionRepository.findDailyQuestionByCategoryId(categoryId)
.orElseThrow(() -> QuestionNotFoundException.EXCEPTION);
QuestionFindServiceResponse serviceResponse = new QuestionFindServiceResponse(question);
return serviceResponse;
}
2️⃣ 캐싱 후 코드
/**
* 카테고리로 질문 조회
*/
public QuestionFindServiceResponse findQuestionByCategoryId(Long categoryId) {
// 1. 캐시 키 생성
String questionCacheKey = "QUESTION:" + LocalDate.now() + ":CATEGORY:" + categoryId; // 예: QUESTION:2024-12-16:CATEGORY:1
// 2. 캐시에서 데이터 조회
QuestionFindServiceResponse cachedQuestion = cacheManager.get(questionCacheKey, QuestionFindServiceResponse.class);
// 3. 캐시에 데이터가 있다면 해당 데이터 반환
if (cachedQuestion != null) {
log.info("cache hit: {}", cachedQuestion);
return cachedQuestion;
}
Question question = questionRepository.findDailyQuestionByCategoryId(categoryId)
.orElseThrow(() -> QuestionNotFoundException.EXCEPTION);
QuestionFindServiceResponse serviceResponse = new QuestionFindServiceResponse(question);
// 4. 캐시에 데이터가 없다면 캐시에 해당 Question 정보 저장
if(question.getQuestionDate().equals(LocalDate.now())) // 만약 질문 생성이 실패된 경우, 어제 질문이 오늘의 질문으로 캐시에 저장되는 현상을 방지
cacheManager.set(questionCacheKey, serviceResponse, 24 * 60);
return serviceResponse;
}
- 캐시 키를 생성
➡️ 질문은 하루에 한번 생성되기 때문에 키에 오늘 날짜를 포함시켰다. - 캐시에서 데이터 조회
- 캐시에 데이터가 있다면 해당 데이터 반환
- 캐시에 데이터가 없다면 해당 질문 정보 저장
➡️ 캐시에 데이터를 저장하기 전에 데이터베이스에서 받아온 Question의 날짜 정보와 오늘 날짜를 비교해서 검증하는데 이 이유는 오늘날짜에 대한 캐시 데이터로 어제 질문이 저장되는 현상을 방지하기 위해서이다.
✅ 테스트 결과
테스트는 500건 동시요청을 하는 상황과 단건 요청하는 상황, 2가지의 상황을 테스트하였다.
500건 동시요청의 경우 JMeter를, 단건 요청의 경우 Postman을 활용하였다.
1️⃣ 캐싱 전
500건 동시 요청
요청당 1.5초가 소요되며 간간히 오류도 발생하고 있는 것을 볼 수 있다.
단건 요청
단건 요청의 경우 32ms정도 소요되었다.
2️⃣ 캐싱 후
500건 동시요청
- 오류 발생이 사라졌고 평균 응답시간도 1.5초에서 0.95초로 33%정도 빨라진것을 알 수 있다.
단건 요청
- 32ms에서 16ms로 50%정도 빨라졌다.
💡 결론
500건 동시요청
- 1.5초 ➡️ 0.95초: 33% 향상
단건 요청
- 32ms ➡️ 16ms: 50% 향상
'데브코스 > 실습 & 프로젝트' 카테고리의 다른 글
[최종 프로젝트] 답변 조회 API 쿼리 최적화하기 (0) | 2025.01.22 |
---|---|
[최종 프로젝트] 좋아요 API 동시성 이슈 해결 - 2-2. Answer 테이블 데이터 정합성 문제 해결: 분산 락(Redisson 적용) (1) | 2024.12.31 |
[최종 프로젝트] 좋아요 API 동시성 이슈 해결 - 2-1. Answer 테이블 데이터 정합성 문제 해결: 비관적 락(@Lock) 적용 (0) | 2024.12.31 |
[최종 프로젝트] 좋아요 API 동시성 이슈 해결 - 1. Like 테이블 데이터 정합성 문제 (0) | 2024.12.31 |
[최종 프로젝트] 좋아요 API 동시성 이슈 해결 - 0. 배경 설명 (1) | 2024.12.31 |