๐ŸŽ Redis๋กœ Key๊ด€๋ฆฌํ•˜๊ธฐ

Redis๋ž€ ๋ฌด์—‡์ผ๊นŒ?


Redis(์›๊ฒฉ ๋”•์…”๋„ˆ๋ฆฌ ์„œ๋ฒ„ : Remote Dictionary Sever)

  • ์ฃผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์บ์‹œ ๋˜๋Š” ๋น ๋ฅธ ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ์‚ฌ์šฉ๋˜๋Š” ์˜คํ”ˆ์†Œ์Šค, ์ธ๋งค๋ชจ๋ฆฌ, NoSql <ํ‚ค, ๋ฒจ๋ฅ˜> ์ €์žฅ์†Œ์ด๋‹ค.

๋˜ํ•œ, redis๋Š” ๋ณด์กฐ๊ธฐ์–ต์žฅ์น˜(HDD / SSD)๊ฐ€ ์•„๋‹Œ ๋ฉ”๋ชจ๋ฆฌ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜์—ฌ ํƒ์›”ํ•œ ์†๋„, ์•ˆ์ •์„ฑ, ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.

Redis์˜ ์ ์šฉ ๋ชฉ์ 

์• ํ”Œ์ด์ผ€์ด์…˜์ด ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ ์†Œ์Šค์— ์˜์กดํ•˜๋Š” ๊ฒฝ์šฐ ํŠธ๋ ˆํ”ฝ์ด ์ฆ๊ฐ€ํ•˜๊ฑฐ๋‚˜, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ํ™•์žฅ๋ ๋•Œ ์†Œ์Šค์˜ ์ง€์—ฐ ์‹œ๊ฐ„๊ณผ ์ฒ˜๋ฆฌ๋Ÿ‰์œผ๋กœ ์ธํ•˜์—ฌ ๋ณ‘๋ชฉํ˜„์ƒ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ Redis๋ฅผ ์ ์šฉํ•˜๋ฉด, ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•˜์—ฌ ์ฝ๊ฑฐ๋‚˜ ์“ธ๋•Œ ์ง€์—ฐ ์‹œ๊ฐ„์„ ์ตœ์†Œํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.


Redis์˜ ๊ธฐ๋Šฅ

  • redis๋Š” ์•ฑ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ์œ„ํ•ด ํŠน๋ณ„ํžˆ ์„ค๊ณ„๋˜์–ด, ๊ธฐ์กด NoSQL ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ์™€ ์ฐจ๋ณ„ํ™” ๋˜๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค.

1. Redis ์บ์‹œ ์„ธ์…˜

  • MongoDB, PostgreSql ๊ณผ ๊ฐ™์€ NoSQL DB์™€ ๋‹ฌ๋ฆฌ, ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ €์žฅ์†Œ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ๊ธฐ ์“ฐ๊ธฐ ์„ฑ๋Šฅ์ด ์›”๋“ฑํžˆ ๋†’๋‹ค. ๋˜ํ•œ ๊ณ ๊ฐ€์šฉ์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๋ณด์žฅํ•˜๋Š”๋ฐ ๋„์›€์ด๋œ๋‹ค.
๊ณ ๊ฐ€์šฉ์„ฑ ์ด๋ž€?

๊ฐ€์šฉ์„ฑ : ์„œ๋ฒ„ ๋˜๋Š” ๋„คํŠธ์›Œํฌ ๋“ฑ์˜ ์ •๋ณด ์‹œ์Šคํ…œ์ด ์ •์ƒ์ ์œผ๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ •๋„๋ฅผ ์˜๋ฏธ.
== ์ •์ƒ์ ์ธ ์‚ฌ์šฉ์‹œ๊ฐ„ / ์ „์ฒด ์‚ฌ์šฉ์‹œ๊ฐ„ = ์‹œ์Šคํ…œ ๊ฐ€๋™๋ฅ (๊ฐ€์šฉ์„ฑ)

์—ฌ๊ธฐ์„œ, ๊ณ ๊ฐ€์šฉ์„ฑ์ด๋ž€ ๊ฐ€์šฉ์„ฑ์ด 99%, 99.9% ๋“ฑ๊ณผ ๊ฐ™์ด ๋†’์€ ๊ฐ€์šฉ์„ฑ์„ ์ง€๋‹Œ ์‹œ์Šคํ…œ์„ ์˜๋ฏธํ•œ๋‹ค.

2. Redis ๋Œ€๊ธฐ์—ด

  • redis๋Š” ์›น ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ‰์†Œ๋ณด๋‹ค ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ ์˜ค๋ž˜ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ๋Š” ์ž‘์—…์„ ๋Œ€๊ธฐ์—ด์— ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค. ์š”์ฒญ/์‘๋‹ต ์ฃผ๊ธฐ์˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰๋˜๋Š” ์ž๋™ํ™”๋œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

3. Redis ๋ฐ์ดํ„ฐ ํ˜•์‹

  • redis๋Š” ๊ธฐ์ˆ ์ ์œผ๋กœ๋Š” ํ‚ค/๋ฒจ๋ฅ˜ ์ €์žฅ์†Œ ์ด์ง€๋งŒ, ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ ์œ ํ˜•๊ณผ ๊ตฌ์กฐ๋ฅผ ์ง€์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์„œ๋ฒ„์ด๋‹ค.

์ง€์› ๋ฐ์ดํ„ฐ ex)

  1. ๊ณ ์œ ํ•˜๊ณ  ์ •๋ ฌ๋˜์ง€ ์•Š์€ ๋ฌธ์ž์—ด
  2. ๋ฐ”์ด๋„ˆ๋ฆฌ ์„ธ์ดํ”„ ๋ฐ์ดํ„ฐ
  3. ํ•˜์ดํผ๋กœ๊ทธ
  4. ๋น„ํŠธ ๋ฐฐ์—ด
  5. ํ•ด์‹œ
  6. ๋ชฉ๋ก

- RSA private key ์•”ํ˜ธํ™” ๋ฐฉ์‹ ์ ์šฉํ•˜๊ธฐ

- ์บ์‰ฌ ์ „๋žต์œผ๋กœ Redis๋ฅผ ์„ ํƒํ•œ ์ด์œ ?


Redis ์—ฐ๊ฒฐ ๋ฐ ๊ตฌํ˜„ With. Spring


1. ํ™˜๊ฒฝ ์„ค์ •

build.gradle์— ์˜์กด์„ฑ ์ถ”๊ฐ€

implementation 'org.springframework.boot:spring-boot-starter-data-redis'  

yml redis ์†์„ฑ ์ถ”๊ฐ€

cache:  
type: redis  
redis:  
cache-null-values: true

redis:  
host: `๋ ˆ๋””์Šค ํ˜ธ์ŠคํŠธ`
port: `๋ ˆ๋””์Šค ํฌํŠธ`

2. RedisConfiguration

  • @Configuration์œผ๋กœ redis์‚ฌ์šฉ์— ํ•„์š”ํ•œ ์…‹ํŒ…์„ Bean์œผ๋กœ ๋“ฑ๋กํ•  ํด๋ž˜์Šค.
@Configuration  
public class RedisConfiguration {  
  
    @Value("${spring.redis.host}")  
    private String host;  
  
    @Value("${spring.redis.port}")  
    private int port;
}


RedisConnectionFactory

Redis ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐ์„ ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌํ•ด์ฃผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค

@Bean  
public RedisConnectionFactory redisConnectionFactory() {  
    return new LettuceConnectionFactory(host, port);  
}
  • ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„์™€ Redis ์„œ๋ฒ„ ๊ฐ„์˜ ๋ฐ์ดํ„ฐ ์†ก์ˆ˜์‹ ์„ ํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ
  • ๋Œ€ํ‘œ์ ์œผ๋กœ Lettuce์™€ Jedis, Redisson ์ด ์žˆ๋‹ค.

Lettuce

  • ๋น„๋™๊ธฐ ๋ฐ ๋…ผ๋ธ”๋กœํ‚น I/O๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜์—ฌ ๊ณ ๋ถ€ํ•˜, ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์— ์ ํ•ฉ

Jedis

  • ๋ธ”๋กœํ‚น I/O(๋™๊ธฐ) ๋ฐฉ์‹์„ ์‚ฌ์šฉ.
  • ๊ณ ๋ถ€ํ•˜๋‚˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ์ค‘์š”ํ•œ ํ™˜๊ฒฝ์—์„œ๋Š” ํšจ์šธ์ด ๋–จ์–ด์ง„๋‹ค.

redisson

  • ๋‹จ์ˆœํžˆ Redis ์—ฐ๊ฒฐ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ๋„˜์–ด ๋ถ„์‚ฐ ๋ฝ, ๋ถ„์‚ฐ ์ปฌ๋ ‰์…˜, ๋ถ„์‚ฐ ์บ์‹œ ๋“ฑ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์„ ์ œ๊ณต.
  • ์ง์ ‘ RedisConnectionFactory๋กœ ์‚ฌ์šฉํ•˜๊ธฐ๋ณด๋‹ค๋Š” RedissonClient๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ  ์ด๋ฅผ ํ†ตํ•ด ๋ถ„์‚ฐ ๋ฝ์ด๋‚˜ ์บ์‹œ ๋งค๋‹ˆ์ €๋ฅผ ๊ตฌ์„ฑ.
  • redisson ์‚ฌ์šฉ ์˜ˆ

    โ–ถ ๐Ÿ” ์ƒํ’ˆ ํˆฌ์žํ•˜๊ธฐ ์„œ๋น„์Šค Lock๊ธฐ๋ฒ• ๊ฐœ์„ ์•ˆ

๋”ฐ๋ผ์„œ ํ•ด๋‹น ์ฝ”๋“œ์—๋Š” ๋น„๋™๊ธฐ ์„ฑ๋Šฅ์ด ๋†’์€ ์ข‹์€ Lettuce ์„ ํƒ.


RedisCacheDefaultConfiguration

Redis์— ์ €์žฅ๋  ์บ์‹œ์˜ ๊ธฐ๋ณธ ์ง๋ ฌํ™” ๋ฐ ๋งŒ๋ฃŒ ์‹œ๊ฐ„(TTL) ๋“ฑ์˜ ์„ค์ •์„ ๋‹ด๋‹น.

private RedisCacheConfiguration redisCacheDefaultConfiguration() {
    return RedisCacheConfiguration
            .defaultCacheConfig()
            .serializeKeysWith(RedisSerializationContext.SerializationPair
                    .fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                    .fromSerializer(new GenericJackson2JsonRedisSerializer());
}
  • serializeKeysWith : StringRedisSerializer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ‚ค๋ฅผ ๋ฌธ์ž์—ด๋กœ ์ง๋ ฌํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • serializeValuesWith : GenericJackson2JsonRedisSerializer์™€ ObjectMapper๋ฅผ ์‚ฌ์šฉํ•ด JSON ํ˜•์‹์œผ๋กœ ์ง๋ ฌํ™”

    GenericJackson2JsonRedisSerializer : ์ง๋ ฌํ™” ๋ฐฉ์‹ ์ค‘ ํ•˜๋‚˜๋กœ, JSONํ˜•์‹์„ ์ง€์›.


redisCacheConfigurationMap

์—ฌ๋Ÿฌ ์บ์‹œ ์ด๋ฆ„์— ๋Œ€ํ•ด ๊ฐ๊ธฐ ๋‹ค๋ฅธ TTL(Time To Live)์„ ๋™์ ์œผ๋กœ ์„ค์ •.

private Map<String, RedisCacheConfiguration> redisCacheConfigurationMap() {  
    Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();  
    for (Map.Entry<String, Long> cacheNameAndTimeout : cacheProperties.getTtl().entrySet()) {  
        cacheConfigurations  
                .put(cacheNameAndTimeout.getKey(), redisCacheDefaultConfiguration().entryTtl(  
                        Duration.ofSeconds(cacheNameAndTimeout.getValue())));  
    }  
    return cacheConfigurations;  
}

//cacheProperties.yml

cache:  
  ttl:  
    CacheName: 10 #๋งŒ๋ฃŒ ์‹œ๊ฐ„
  • ์™ธ๋ถ€ ์„ค์ •(CacheProperties)์—์„œ ์บ์‹œ๋ณ„ TTL ์ •๋ณด๋ฅผ ์ฝ์–ด์™€ ๊ฐ ์บ์‹œ์˜ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์ง€์ • ์ด๋ฅผ ํ†ตํ•ด ํŠน์ • ์บ์‹œ๋งŒ ๋ณ„๋„์˜ ๋งŒ๋ฃŒ ์ •์ฑ… ๋“ฑ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • entryTtl : ๊ธฐ๋ณธ ๋งŒ๋ฃŒ์‹œ๊ฐ„ ์„ค์ •

RedisCacheManager

Spring์˜ ์บ์‹œ ์ถ”์ƒํ™”์—์„œ Redis๋ฅผ ์บ์‹œ ์ €์žฅ์†Œ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์บ์‹œ ๋งค๋‹ˆ์ €๋ฅผ ์ƒ์„ฑ ์œ„์—์„œ ์„ค์ •ํ•œ redisCacheDefaultConfiguration๊ณผ cacheConfigurations(์ปค์Šคํ…€) ์ด ์‚ฝ์ž…๋œ๋‹ค.

@Bean
public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
    return RedisCacheManager.RedisCacheManagerBuilder
            .fromConnectionFactory(redisConnectionFactory)
            .cacheDefaults(redisCacheDefaultConfiguration())
            .withInitialCacheConfigurations(redisCacheConfigurationMap())
            .build();
}
  • withInitialCacheConfigurations : RedisCacheManager๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ๋ฏธ๋ฆฌ ์ •์˜๋œ ํŠน์ • ์บ์‹œ ์ด๋ฆ„์— ๋Œ€ํ•ด ๊ฐœ๋ณ„์ ์ธ ์„ค์ •์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.

3. Service Layer

Bean ์ฃผ์ž…

@Service  
public class TestServiceImpl {

	private final CacheManager cacheManager;  
	private final RedisTemplate<String, Object> redisTemplate;

	public TestServiceImpl(CacheManager cacheManager, RedisTemplate<String, Object> redisTemplate) {  
	    this.cacheManager = cacheManager;  
	    this.redisTemplate = redisTemplate;  
	}
}

(์ƒ์„ฑ์ž ์ฃผ์ž…)

  • ์œ„(RedisConfiguration)์—์„œ ์ƒ์„ฑํ•œ CacheManager ๋ฐ RedisTemplate์˜ ๋นˆ์„ ์ฃผ์ž…ํ•œ๋‹ค.
์ฃผ์˜

๋งŒ์•ฝ, Bean์œผ๋กœ ์ƒ์„ฑ๋œ CacheManager๊ฐ์ฒด๋‚˜, RedisTemplate๊ฐ์ฒด๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ๋ผ๋ฉด,
@Qualifier ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ Bean์ด๋ฆ„์„ ๋ช…์‹œํ•ด์•ผํ•œ๋‹ค.

`ex) @Qualifier("CustomCacheManager") CacheManager cacheManager ...`

Cache ์‚ฝ์ž… / ๊บผ๋‚ด๊ธฐ / ์‚ญ์ œ

	Cache privateKeyCache = cacheManager.getCache("CacheName");
	
	public void putCache() {
		
		if (privateKeyCache != null) {  
		    privateKeyCache.put(keyId, ๋ฒจ๋ฅ˜);  
		} else {  
		    // ์บ์‹œ๊ฐ€ ์—†์œผ๋ฉด ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋˜๋Š” ๋กœ๊น…  
		    throw new IllegalStateException("privateKeyCache ๊ฐ€ ์œ ์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");  
		}
	}

	public void getCache() {
		
		if (privateKeyCache == null) {  
			throw new IllegalStateException("rsaPrivateKeyCache ๊ฐ€ ์œ ์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");  
		}  
		String privateKeyValue = privateKeyCache.get(keyId, String.class);  
		// 1ํšŒ์šฉ ์‚ฌ์šฉ์„ ์œ„ํ•ด ์กฐํšŒ ํ›„ ์บ์‹œ์—์„œ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค.
		rsaPrivateKeyCache.evict(keyId); // 1ํšŒ์šฉ ์‚ฌ์šฉ: ์บ์‹œ์—์„œ ์ œ๊ฑฐ
		
	}
  • getCache.(CacheName)์œผ๋กœ ์บ์‰ฌ๋ฅผ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
  • put(keyId, ๋ฒจ๋ฅ˜); / get(keyId, String.class); ๋กœ ์‚ฝ์ž… / ๊ฐ€์ ธ์˜ค๊ธฐ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • .evict(keyId)๋กœ ์‚ญ์ œ ( 1ํšŒ์„ฑ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. )
1ํšŒ์„ฑ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

๋‚˜์˜ ๊ฒฝ์šฐ์— RSAํ‚ค๋ฅผ ๋งค๋ฒˆ ๋ฐœ๊ธ‰ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ’์„ ๊บผ๋ƒ„๊ณผ ๋™์‹œ์— ํ•ด๋‹น ํ‚ค๋ฒจ๋ฅ˜๋ฅผ ์‚ญ์ œํ•œ๋‹ค.
Exception์ด ํ„ฐ์ง€๋”๋ผ๋„, cacheProperties ์— ์„ค์ •ํ•œ TTL์ด ์ดˆ๊ณผ๋˜๋ฉด ์‚ญ์ œ๋œ๋‹ค.


Redis์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•œ Key ๊ด€๋ฆฌ๋กœ, ๋ฉ€ํ‹ฐ ์„œ๋ฒ„ ํ™˜๊ฒฝ์—์„œ ์ •ํ•ฉ์„ฑ๊ณผ ์•ˆ์ •์„ฑ์„ ์ฑ™๊ธธ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

+ TTL ์ฒดํฌ

Pasted image 20250328120415.png

+ TTL ์„ค์ •

application.yml / properties
      โ†“
CacheProperties (ttl map ๊ด€๋ฆฌ)
      โ†“
redisCacheConfigurationMap() โ†’ ์บ์‹œ๋ณ„ TTL ๋งคํ•‘
      โ†“
redisCacheDefaultConfiguration() โ†’ ๊ธฐ๋ณธ ์„ค์ • (ex: serializer, ๊ธฐ๋ณธ TTL)
      โ†“
redisCacheManager() โ†’ ์ตœ์ข… CacheManager ์ƒ์„ฑ

๋‹ค์Œ๊ณผ ๊ฐ™์ด TTL ์‹œ๊ฐ„ ๋ฐ Redis ๋งŒ๋ฃŒ ์ •์ฑ…์„ ๋งตํ•‘ ํ•  ์ˆ˜ ์žˆ๋‹ค.