스프링 캐시 추상화, Redis 연동하기
문서 작성 중입니다. 스터디 일정으로 인해서 먼저 발행합니다... ㅠㅠ
아래와 같이, @EnableCaching 어노테이션을 선언해줍니다. 스프링 캐시 추상화를 사용하겠다는 의미입니다.
캐싱 적용할 곳에, @Cacheable 어노테이션을 선언해줍니다.
위와 같이 선언해주면, 캐시가 적용됩니다. 캐시를 지우는 것도 간단합니다.
끝입니다...
캐시 추상화의 주요 기능에 대해서는 각자 공부하시길 바랍니다. 구글링하면 수많은 글이 나옵니다.
- @Cacheable : 어노테이션이 선언된 메서드는, 결과를 캐시에 저장하고 다음 호출에서는 메서드의 내부 로직을 수행하지 않고 캐싱된 결과를 바로 리턴한다.
- @CacheEvict : 만약 불필요한 값을 모두 캐싱 처리한다면 리소스 낭비가 될 것이다. 사용하지 않는 데이터는 캐싱하지 않고 지우는 것이 좋다.
- @CachePut : 캐시 데이터를 업데이트한다.
- @Caching
어쨋든, 위에서 설명한 기능들은 모두 스프링 프레임워크에서 제공합니다. 스프링부트는, 이런 기능들을 쉽게 사용할 수 있도록 AutoConfiguration 을 제공해줄 뿐입니다. CacheManager 이라는 녀석을 자동으로 빈으로 등록해주며, Redis 의존성이 있다면 RedisCacheManager 를 빈으로 등록해줄 것입니다. 이 글에서는, 자세한 내용을 설명할 수는 없지만, 스프링부트가 어떤 역할을 하는지 제대로 이해해야 합니다.
조금 어려운 내용. 패스하세요~ 나중에 보시면 됩니다.
이렇게 간단하게 해결되는 것을... 과제를 왜 한걸까요? 그래도, 과제는 의미가 있었습니다. AOP 를 이해하기 위한 과정이었으며, ConcurrentMap 을 사용해본다는 것도 의미가 큽니다. 어쨋든, 위와 같이 설정하면 자동으로 캐시가 적용됩니다. 하지만, 스프링부트의 AutoConfiguration에 의해서 cacheManager Bean 을 자동으로 등록해줍니다. 아래와 같이, SimpleCacheConfiguration 는 레디스, EhCache 등의 지원하는 캐시 라이브러리를 사용하지 않는 경우에 동작하며, ConcurrentMapCacheManager 가 빈으로 등록됩니다.
캐시 추상화는, 반드시 @EnableCaching 어노테이션이 선어되어 있어야 합니다. 스프링에서 제공하는 CacheManager 는 ConcurrentMapCacheManager 외에도 여러가지 구현체를 제공합니다. 자세한 내용은 조금 이따 할게요. 암튼, ConcurrentMapCacheManager 에서는, 캐시 데이터를 ConcurrentHashMap 에 저장합니다.
캐시 추상화 기능에서는, AOP 를 사용했ㅅ브니다.
스프링 코어의 소스코드를 한번 보셔도 됩니다만, 주니어 개발자가 이해하기에는 조금 어려울 수 있습니다. 저도 많이 어렵습니다. 어쩃든, 위 코드에서는 구현체에 대한 의존성이 없습니다 . 메모리 캐싱을 사용하는지, 레디스 캐싱을 사용하는지 상관없이 추상화되니 기능을 제공합니다. 그래서, 우리는 간편하게 캐시 저장소인, 프로바이더(구현체)를 쉽게 변경할 수 있습니다.
자세한 내용은, 스프링 공식 레퍼런스를 참고하시고, 구글링하면 수많은 글이 나옵니다.
https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache
기본 ConcurrentMapCacheManager 를 사용하게 되면, 몇가지 단점이 존재합니다.
첫번째로는, 휘발성 메모리 입니다. 애플리케이션이 다운되면, 메모리 데이터는 사라지게 됩니다.
두번 째로는 서버간의 데이터 불일치가 발생할 수 있습니다.
3대의 서버에서의 메모리 캐시가 일치하지 않는 현상이 발생할 수 있습니다. 동기화 해주는 로직을 구현해야 하지만, 매우 까다롭습니다.
세번째로는, 스케일업 환경으로, 애플리케이션의 메모리가 부족해지는 현상이 발생할 수도 있습니다.
그래서, 외부에 별도로 구축한 레디스에 연동을 하게 되면 위와 같은 문제를 해결할 수 있습니다.
물론, 아주 간단한 캐시 등은 메모리 기반으로 동작하는 것이 효율적일 수 있습니다. 또한, 불필요한 장비 증설은 오버엔지니어링이 될 수도 있습니다.
레디스를 사용해봅시다.
spring-boot-starter-data-redis 의존성을 추가해봅시다.
레디스 의존성이 있고, @EnableCaching 어노테이션이 선언되어있다면, 자동으로 RedisCacheManager 빈이 등록됩니다.
그 어떤 컨피그 설정을 하지 않아도 됩니다. 단, 레디스 호스트 정보는 입력해줘야 합니다만,
로컬에 6379 포트로 실행하였다면, 위 설정도 필요 없습니다. 스프링부트가 알아서 기본 포트를 6379로 설정해서 커넥션을 맺습니다. 위 내용은 4장 AutoConfiguration 설명할 떄도 잠시 했었습니다.
실행을 해보면, 레디스에 캐시 데이터가 잘 저장되는 것을 확인할 수 있습니다.
또한, 별도의 설정을 하지 않으면, 만료시간은 없습니다.
하지만, 실무에서는, 스프링에서 기본으로 제공하는 CacheManager 를 그대로 사용하지 않는 경우가 더 많습니다.
직렬화 방식을 변경, 만료 시간을 변경하고 싶다면
아래와 같이 CacheManager 를 정의해줍니다. RedisConnectionFactory 는 스프링부트가 올려주는 Bean 을 사용하겠ㅅ브니다.
CacheManager 를 애플리케이션에서 직접 정의를 해준다면, 스프링 부트의 AutoConfiguration 은 동작하지 않습니다.
그래서, 중요한 사실은...
스프링부트의 AutoConfiguration 을 사용하지 않고, 커스텀하게 직접 Bean 을 정의하는 경우에는.. 빠진 부분이 없는지 꼼꼼히 확인해야 합니다. 스프링부트 및 스프링프레임워크의 버전이 변경되면서 바뀌는 기능에 대해서 인지를 하지 못한 상황에서, 섣불리 Bean 을 정의하게 되면.. 예상하지 못한 상황이 발생할 수 있습니다.
저는,
스프링부트를 알기 전에는 어려웠고
스프링부트를 조금 알게 되었을때, 스프링부트가 너무 쉬웠고..
스프링부트를 많이 알게 되면서 다시 어려워 졌습니다.
이유는, 스프링부트의 AutoConfiguration 때문입니다. 어느 정도 까지 커스텀하게 해야할지, 스프링부트에서 제공해주는 기본 Bean 을 사용하는게 좋을지? 아니면, 커스텀하게 Bean 을 생성해서 사용하는게 좋을지 항상 고민하게 됩니다.
필자의 샘플 코드에서는, RedisConnectionFactory 를 별도로 정의하지 않았습니다. 스프링부트의 AutoConfiguration 에 의해서 자동으로 생성되는 RedisConnectionFactory 빈을 사용하는 것입니다. 하지만, 이 경우에도, RedisConnectionFactory 를 애플리케이션에서 직접 Bean 을 정의할 수 있습니다. 실무에서는 보통 RedisConnectionFactory 를 직접 Bean 을 정의해서 사용하빈다.
ㅇㅇ
문서 작성 중...