일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 데이터베이스
- 페이지네이션
- AWS
- buildSrc
- 자바
- Kafka
- DB
- Redis
- java
- 갱신 이상
- null
- 삽입 이상
- Spring
- 3-way handshaking
- 캐시 오염
- 낙관적 락
- JPA
- well-know port
- 자료구조
- HTTP
- 네트워크
- 비관적 락
- 삭제 이상
- ocp
- 정규화
- MSA
- Kotlin
- 스레드 풀
- Dirty Checking
- gatway
- Today
- Total
어 나 갱수.
[DB] Spring Boot에서 Redis Cache를 구현하기 😤 본문
프로젝트를 하면서 캐싱 기능을 추가해 보기로 했습니다.
Spring에서는 @Cacheable, @CachePut, @CacheEvict 어노테이션을 통해서 캐시를 구현할 수 있습니다.
Spring에서 캐시를 사용하려면 캐싱 기능을 사용할 스프링부트 애플리케이션에@EnableCaching이라는 어노테이션을 붙여준다. 이 어노테이션을 붙여주면 이제 해당 애플리케이션에서 캐싱 기능을 활성화시켜서 캐시를 통해 데이터를 불러올 수 있습니다.
@SpringBootApplication
@EnableCaching
class V2Application{
val log = LoggerFactory.getLogger(this::class.java.name)!!
@PostConstruct
fun timeZone() {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"))
log.info("Server Started At {}", Date())
}
}
fun main(args: Array<String>) {
runApplication<V2Application>(*args)
}
@Cacheable
@Cacheable 어노테이션은 캐시 데이터를 생성하고 캐시에 데이터를 저장하는 역할을 합니다. 아래 함수가 실행되면 반환 객체인
ListBoardResDto가 캐시에 저장됩니다.
@Cacheable(
value = ["boardList"],
key = "'boardList'",
cacheManager = "contentCacheManager",
)
override fun execute(): ListBoardResDto {
val boardList = boardRepository.findAllByOrderByCreatedDateDesc()
.map { BoardResDto.of(it) }
val listBoardResDto = ListBoardResDto(boardList)
return listBoardResDto
}
캐시를 조회하면 아래와 같이 캐시가 조회됩니다.
@CachePut
@CachePut 어노테이션은 캐시를 업데이트해 주는 역할을 합니다. 캐시를 업데이트하지 않는다면 데이터베이스에 데이터가 변경되는 삭제되든 서버는 데이터베이스에서 데이터를 조회하는 것이 아니라 캐시를 통해 데이터를 조회합니다. 그러면 데이터 적합성 부분에서 문제가 발생할 수 있습니다. 그렇기 때문에 캐시를 수정해 주는 작업도 굉장히 중요한 작업입니다.
@CachePut어노테이션이 명시된 메서드를 실행하면 key 값에 해당하는 데이터를 삭제하고 새롭게 반환되는 객체인 ListBoardResDto를 캐시에 저장합니다.
@CachePut(
value = ["boardList"],
key = "'boardList'",
cacheManager = "contentCacheManager"
)
override fun execute(modifyBoardReqDto: ModifyBoardReqDto, boardId: Long): ListBoardResDto {
val boardInfo: Board = boardRepository.findByIdOrNull(boardId)
?: throw BoardNotExistsException()
toDto(modifyBoardReqDto)
.let { boardInfo.updateBoard(title = it.title, content = it.content) }
updateBoardCache(boardInfo)
val boardList = boardRepository.findAllByOrderByCreatedDateDesc()
.map { BoardResDto.of(it) }
return ListBoardResDto(boardList)
}
이렇게 공지글을 수정하는 API를 요청하면 수정된 객체를 반환하면서
해당 key에 대한 캐시를 수정해 줍니다.
@CacheEvict
@CacheEvict 어노테이션은 캐시에 있는 해당 key에 해당하는 캐시 데이터를 삭제하는 기능을 합니다.
아래 코드는 공지글을 삭제하는 메서드입니다.
@CacheEvict(
cacheNames = ["boardList"],
key = "'boardList'",
cacheManager = "contentCacheManager"
)
override fun execute(boardId: Long) {
val boardInfo: Board = boardRepository.findByIdOrNull(boardId)
?: throw BoardNotExistsException()
val boardImages: List<BoardImage> = boardImageRepository.findAllByBoard_Id(boardId)
if (boardImages.isEmpty()) {
boardRepository.delete(boardInfo)
} else {
for (boardImage in boardImages) {
s3Service.deleteFile(boardImage.url.substring(54))
boardImageRepository.delete(boardImage)
}
boardRepository.delete(boardInfo)
}
}
캐시를 삭제하게 된다면 아래와 같이 캐시가 조회되지 않습니다.
Redis Template 사용
@CachePut 어노테이션을 통해서 캐시를 업데이트하려면 업데이트하는 메서드에서 꼭 업데이트된 객체를 반환해주어야 합니다.
그렇지만 캐시를 적용하기 전 원래 코드에서는 수정된 객체를 반환하지 않습니다. 굳이 반환하지 않아도 되는 객체라고 판단하고 개발을 진행했기 때문입니다. 그렇지만 @CachePut 어노테이션을 사용하려면 반환객체가 필요하기 때문에 반환 객체를 넣어주어야겠다고 생각을 했습니다. 그렇지만 다시 생각해 보니 @CachePut 어노테이션을 사용하기 위해서 굳이 필요하지 않은 객체를 반환하는 것은 리소스 낭비라는 생각이 들었기 때문에 메서드에서 객체를 반환시키는 것보다는 다른 방법을 생각해 봤고 그 결과 캐시를 저장하고 삭제하고 수정하는 작업을 Redis Template을 통해서 직접 구현하기로 결정하였습니다.
@Service
class RedisCacheService(
private val redisTemplate: RedisTemplate<String, Any>
) {
fun getFromCache(key: String): Any? {
return redisTemplate.opsForValue().get(key)
}
fun putToCache(key: String, value: Any) {
redisTemplate.opsForValue().set(key, value)
}
fun initCache(pattern: String) {
val keys = redisTemplate.keys(pattern)
if(keys.isNotEmpty()) {
redisTemplate.delete(pattern)
}
}
}
위와 같이 직접 구현을 하니 불필요한 리소스를 소모하지 않고도 캐시를 구현할 수 있었습니다.
'DB' 카테고리의 다른 글
[DB] 낙관적 락(Optimistic Lock), 비관적 락(Pessimistic Lock) (0) | 2024.08.20 |
---|---|
[DB] Redis Cache 🔥 (0) | 2024.07.08 |
[DB] INNER 조인 & OUTER조인 🤚 (0) | 2024.02.29 |
[DB] 정규화란 ? 🐼 (1) | 2024.02.04 |
[DB] 인덱스(Index)란? 😛 (1) | 2024.01.31 |