TIL

24_02_21 TIL redis-cache 적용

nakgopsae 2024. 2. 21. 21:54

redis remote cache 를 사용해야 하는 요구사항 적용 


build.gradle.kts에 적용

dependencies{
	implementation ("org.springframework.boot:spring-boot-starter-data-redis")
 }
//build.gradle.kts

 


application.yml 파일에 redis 정보 추가

spring:
  //기타정보
  data:
    redis:
      host: localhost
      port: 6379
  //기타정보

 

기본 port는 6379 이다 


redis 설치 파일 다운로드

https://github.com/microsoftarchive/redis/releases에 접속하여 Redis-x64-3.0.504.msi ( window)용 64비트 파일을 받아준다

 

설치가 완료되면 redis-server 파일을 실행시키고 

 

실행화면

 

 

cli 파일을 열어서 ping을 쳐서 pong 이 나온다면 서버가 잘 작동중인상태

 


 

redisConfig 파일 작성

 

@Configuration
class RedisConfig() {
    @Value("\${spring.data.redis.port}")
    private val port = 0

    @Value("\${spring.data.redis.host}")
    private val host: String = ""

    @Bean
    fun redisConnectionFactory(): RedisConnectionFactory {
        return LettuceConnectionFactory(host, port)
    }

    @Bean
    fun redisTemplate(): RedisTemplate<String, Any> {
        return RedisTemplate<String, Any>().apply {
            this.connectionFactory = redisConnectionFactory()

            this.keySerializer = StringRedisSerializer()
            this.valueSerializer = StringRedisSerializer()

            this.hashKeySerializer = StringRedisSerializer()
            this.hashValueSerializer = StringRedisSerializer()
        }
    }
    
}

 

설정내용이 어려워서 다른분들의 블로그를 보고 가져왔다 

 

여기서 중요한 부분이 redistemplate 부분인데 레디스에 내가 가진 데이터를 레디스 서버에 올릴때 직렬화 역직렬화 과정이 필요하다 많은 사람들이 StringRedisSerializer 문자열로 직렬화 하는게 편하고 지금 가진 서비스가 그리 복잡하지 않기 때문에 적용에 목적을 두고 설정을 하였다

 

//추가적으로 공부가 필요해보인다

 


검색어를 캐시서버로 보내는 작업 

//@Service

@Transactional
    override fun searchHotelListWithPagingVersion2(name: String, pageable: Pageable): Page<HotelResponse> {
        saveSearchHistoryToCache(name)//입력받은 값을 조회수와 함께 캐시로
        return hotelRepository.searchHotelListByNameWithPagingVersion2(name, pageable)
    }
    
    fun saveSearchHistoryToCache(name: String) {
        redisTemplate.opsForHash<String, Long>().increment(SEARCH_HISTORY_CACHE_KEY, name, 1)
    } // 레디스템플릿을 불러와 opsForHash로 해시값을 캐시로 보내고 조회수를증감
    companion object {
        const val SEARCH_HISTORY_CACHE_KEY = "search_history"
    }키값을 만들어준다

 

이 부분이 되게 어려웠다 기존에 알던 데이터베이스와는 다른 데이터 구조를 가지고있고 키:값을 사용한다고 알고있는데 

 

우리가 필요한 저장형태는 history table에 keyword: String , search_number:Long 을 가진 row로 들어가야하는데 

 

어떤식으로 하든 저장은 되었겠지만 hash 형태로 하면 map 형태처럼 한줄씩 들어가게 된다고 보아서 hash로 직렬화를 시켜 캐시서버에 올려주었다


데이터베이스 반영 및 스케줄러 

@Component
class Scheduler(private val historyRepository: HistoryRepository,
    private val redisTemplate: RedisTemplate<String,Any>,
) {
    @Scheduled(initialDelay = 180000 , fixedRate = 180000)
    fun saveCachedSearchHistory() {
        val entries = redisTemplate.opsForHash<String, String>().entries(SEARCH_HISTORY_CACHE_KEY)

        if(entries.isEmpty()){
            return
        }

        for ((name, searchNumString) in entries) {
            val findKeyword = historyRepository.findByKeyWord(name)
            val searchNum = searchNumString.toLong()

            if (findKeyword != null){
                findKeyword.searchNumber += searchNum
                historyRepository.save(findKeyword)
            }else{
                val history = HistoryEntity(keyWord = name, searchNumber = searchNum)
                historyRepository.save(history)
            }

        }
        redisTemplate.delete(SEARCH_HISTORY_CACHE_KEY)
    }

}

 

사실 스케줄러는 한번도 사용해보지 않아서 어색했지만 경험이 있는 팀원분들이 미리 틀을 만들어주고 설명을 해주셔서 검색하면서 잘 적용했다 

 

30분마다 캐시를 데이터베이스에 반영하고 캐시를 날린다 

 

여기서 캐시서버에 올린 데이터에 접근하는게 어려웠는데 찾아보니 

 

val entries = redisTemplate.opsForHash<String, String>().entries(SEARCH_HISTORY_CACHE_KEY)

 

캐시서버에 내가 설정한 키값을 통해 저장된 해시데이터를 역직렬화 하여 가져오고있다 

 

redis 설정에서 모두 문자열로 바꿔 주었기 때문에 문자열로 가져오게 작성하였다 

 

캐시값이 없다면 에러가 나지 않고 잘 돌아가는데 에러가 날줄알아서 해시데이터가 없다면 다시 돌아가라는 식을 작성했는데 이건 없어도 될거 같지만 일단 명시는 해두었다 

 

데이터베이스에 반영할때 추출한 값이 문자열이기때문에 long데이터 타입으로 변경해주는 작업까지 하고 캐시를 날렸다 

 

잘 몰라서 하루밤을 꼴딱 새면서 정보검색을 하였는데 자바코드를 볼줄알고 바꿔서 들고오는게 좀 중요해보였다