Link
Today
Total
12-04 20:05
Archives
관리 메뉴

초보개발자 긍.응.성

SpringBoot에서 Redis KeyGenerator 생성 및 사용하기 본문

Redis

SpringBoot에서 Redis KeyGenerator 생성 및 사용하기

긍.응.성 2020. 11. 3. 00:12
반응형
redis logo

이전 글에서 SpringBoot에서 Redis를 사용해 Object를 캐싱하는 방법을 알아보았습니다.

 

SpringBoot에서 Redis를 사용해 Object 캐시하기

이전 글에는 문자열에 대하여 @Cacheable, @CacheEvict을 사용해 redis에 캐싱하는 방법을 알아보았습니다. Redis와 Springboot 연결하기 앞선 글에서 Redis 설치 및 실행하는 과정에 대하여 알아보았습니다. R

ckddn9496.tistory.com

하지만, @CacheEvict 애노테이션을 이용해 캐시를 삭제할 때, @Cacheable 메서드와 동일한 파라미터로 전달하여야만 캐시가 삭제되는 것을 확인할 수 있었습니다.

이번 글에서는 캐시의 key를 생성할 수 있는 Spring의 KeyGenerator를 알아보고, 커스텀한 KeyGenerator를 만들어 특정 Object에 대하여 key값을 생성하고 @Cacheable과 @CacheEvict에서 이를 사용하는 방법을 예제를 통해 살펴보겠습니다.

KeyGenerator

KeyGenerator는 캐시의 Key를 생성하는 인터페이스입니다. 캐시에 사용하는 메서드에 대와 파라미터에 기반하여 key를 생성하기 위해 사용됩니다.

@FunctionalInterface
public interface KeyGenerator {

	/**
	 * Generate a key for the given method and its parameters.
	 * @param target the target instance
	 * @param method the method being called
	 * @param params the method parameters (with any var-args expanded)
	 * @return a generated key
	 */
	Object generate(Object target, Method method, Object... params);

}

keyGenerator는 @Cacheable, @CacheEvict 애노테이션에 속성으로 사용할 수 있습니다. 이땐 Spring에서 기본으로 제공되는 SimpleKeyGenerator를 사용하거나 직접 keyGenerator interface를 상속하여 커스텀한 keyGenerator를 만들어 사용할 수 있습니다.

generate 메서드는 아래의 파라미터를 통해 key를 생성하는 역할을 합니다. 커스텀한 keyGenerator를 만든다면 이 함수에서 key를 생성하여 반환하여야 합니다. 

  • target - 애노테이션이 붙은 타깃 클래스
  • method - 애노테이션이 붙은 메서드
  • params - 메서드로 전달된 인자

SimpleKeyGenerator

우선 SimpleKeyGenerator부터 살펴봅시다. SimpleKeyGenerator는 Spring에서 기본으로 제공하는 KeyGenerator의 간단한 구현체입니다. 

public class SimpleKeyGenerator implements KeyGenerator {

   @Override
   public Object generate(Object target, Method method, Object... params) {
      return generateKey(params);
   }

   /**
    * Generate a key based on the specified parameters.
    */
   public static Object generateKey(Object... params) {
      if (params.length == 0) {
         return SimpleKey.EMPTY;
      }
      if (params.length == 1) {
         Object param = params[0];
         if (param != null && !param.getClass().isArray()) {
            return param;
         }
      }
      return new SimpleKey(params);
   }

}

generateKey함수에서 파라미터의 개수에 따라 SimpleKey객체 혹은 첫 번째 인자 값을 key로 반환하는 것을 확인할 수 있습니다.

궁금하여 추가로 SimpleKe의 생성방식을 살펴보았는데요, 둘 이상의 파라미터로 만들어지는 SimpleKey는 파라미터 배열(params)과 Arrays.deepHashCode(params)를 통해 생성한 해시 코드값을 갖고 있습니다. 또한 Serializable을 구현하고 있어 앞선 글에서도 보았듯 별도의 keySerializer를 등록하지 않아도 캐시에 잘 저장될 수 있었습니다.

728x90

커스텀한 KeyGenerator 생성 및 이용하기

먼저 @Cacheable과 @CacheEvict를 사용하는 클래스를 살펴보겠습니다. 현재 캐시 애노테이션이 붙은 get()과 delete() 메서드 모두 id와 name, age라는 인자를 전달받아 캐시를 생성/삭제하고 있습니다. delete의 경우 사용자의 id만 인자로 받아서 캐시를 제거해주고 싶은데 앞서 살펴본 SimpleKeyGenerator때문에 그렇게 구현하면 캐시가 정상적으로 삭제되지 않습니다. 

@RestController
@RequestMapping("/redis")
public class RedisController {
	private static final Logger logger = LoggerFactory.getLogger(RedisController.class);

	@GetMapping()
	@Cacheable(value = "user", cacheManager = "userCacheManager")
	public User get(@RequestParam(value = "id") String id, @RequestParam(value = "name") String name, @RequestParam(value = "age") int age) {
		logger.info("get user - userId:{}", id);
		try {
			Thread.sleep(1500);
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
		return new User(id, name, age);
	}

	@DeleteMapping()
	@CacheEvict(value = "user", cacheManager = "userCacheManager")
	public void delete(@RequestParam(value = "id") String id, @RequestParam(value = "name") String name, @RequestParam(value = "age") int age) {
		logger.info("delete user - userId:{}", id);
	}

}

이제 커스텀한 KeyGenerator를 만들어봅시다. 우선 KeyGenerator를 implements하고 generate() 메서들를 오버라이드 합니다. 준비한 KEY_FORMAT 문자열과 첫 인자로 받아올 파라미터 id에 대하여 연결한 문자열을 key로 사용하기 위해 아래와 같이 구현하였습니다. 이렇게 하면 id=ckddn9496 일 시 user:ckddn9496으로 라는 문자열을 key로 반환합니다.

public class UserCacheKeyGenerator implements KeyGenerator {
	private static final String KEY_FORMAT = "user:";

	@Override
	public Object generate(Object target, Method method, Object... params) {
		String id = (String)params[0];
		return KEY_FORMAT + id;
	}
}

만들어준 KeyGenerator를 사용하기 위해 빈으로 등록해줍니다.

@Configuration
@EnableCaching
public class RedisCacheConfig {

	@Bean
	public CacheManager userCacheManager(RedisConnectionFactory connectionFactory) {
		RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
				.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
				.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
				.entryTtl(Duration.ofMinutes(3L));

		return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory).cacheDefaults(redisCacheConfiguration).build();
	}

	@Bean
	public KeyGenerator userCacheKeyGenerator() {
		return new UserCacheKeyGenerator();
	}
}

마지막으로 @Cacheable, @CacheEvict 애노테이션의 속성으로 keyGenerator를 등록합니다. 또한 delete의 파라미터로는 id만 전달받도록 하여 동작을 확인해보겠습니다.

@RestController
@RequestMapping("/redis")
public class RedisController {
	private static final Logger logger = LoggerFactory.getLogger(RedisController.class);

	@GetMapping()
	@Cacheable(keyGenerator = "userCacheKeyGenerator", value = "user", cacheManager = "userCacheManager")
	public User get(@RequestParam(value = "id") String id, @RequestParam(value = "name") String name, @RequestParam(value = "age") int age) {
		logger.info("get user - userId:{}", id);
		try {
			Thread.sleep(1500);
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
		return new User(id, name, age);
	}

	@DeleteMapping()
	@CacheEvict(keyGenerator = "userCacheKeyGenerator", value = "user", cacheManager = "userCacheManager")
	public void delete(@RequestParam(value = "id") String id) {
		logger.info("delete user - userId:{}", id);
	}

}

Redis 서버를 동작시키고 첫 번째 get() 메서드를 호출시켜보겠습니다. 첫 호출이기에 캐싱된 데이터가 없어 메서드의 Thread.sleep(1500)의 지연을 갖습니다. 한번 더 요청을 보내면 캐싱된 데이터를 이용해 응답이 빠르게 오는것을 확인할 수 있습니다.

첫번째 요청. 1530ms 소요 (>1.5초)

redis-cli에서 캐싱이 잘 되었는지 확인할 수 있습니다. redis에 저장된 키는 @Cacheable 애노테이션에서 keyGenerator가 생성하는 key값과 value값이 합쳐져 저장되는 것을 확인할 수 있습니다. redis의 get command를 이용하여 값도 잘 저장되었는지 확인해봅시다.

redis에서 확인

이제 delete() 메서들 호출시키고 redis-cli에 방금 생성 해 주었던 key를 확인해봅시다. 첫 파라미터인 id만을 이용해 캐시의 key를 생성하고 제거하기 때문에 캐시 삭제 또한 잘 동작함을 확인할 수 있었습니다.

지금까지 Spring의 KeyGenerator가 무엇인지, 그리고 Spring에서 제공하는 SimpleKeyGenerator의 원리와 커스텀한 key를 생성하기 위해 KeyGenerator를 만들고 사용하는 방법에 대하여 알아보았습니다. 다음 시간에는 애노테이션 기반 대신 Spring의 강점인 템플릿 패턴이 redis에 적용된 RedisTemplate을 이용하여 캐시를 생성/제거하는 방법에 대하여 알아보겠습니다. 감사합니다.

반응형

'Redis' 카테고리의 다른 글

SpringBoot에서 Redis를 사용해 Object 캐시하기  (2) 2020.11.02
Redis와 Springboot 연결하기  (0) 2020.11.01
Redis 설치 및 실행하기  (0) 2020.10.31
Redis 란?  (0) 2020.10.28
Comments