๐ฑ Redis๋ฅผ ๋์ ํด์ ์บ์ฑ ์์ ์ ์งํํด๋ณด์!!! [2ํ]
๐ค Intro
์ค๋ ๋ณผ ๋ถ๋ถ์ REDIS ์บ์ฑ์ ์ค๊ณํ๋ฉด์, ๊ณผ์ฐ ์บ์ฑํด์ผ ํ๋ ๊ฐ๋ค์ โ์ด๋คโํํ๋ก ์ ์ฅํ๋๊ฒ ์ข์์ง, ๊ทธ๋ฆฌ๊ณ ๊ฐ๊ฐ์ ์ ๋ณด์ ๋ํ TTL์ ์ด๋์ ๋๋ก ์ง์ ํด์ผ ์ฌ์ฉ์ ๊ฒฝํ์ ํ๋์ํฌ ์ ์์์ง์ ๋ํ ๊ณ ๋ฏผ์ ๋ด๊ณ ์๋ค!
๐ฉถ Start
๐ซจ ๊ณ ๋ฏผโฆ REDIS์ ์ด๋ ํ ํํ๋ก ๊ฐ์ ์ ์ฅํ ๊ฒ์ธ๊ฐ?
์ฐ๋ฆฌ๋ ๊ฒฐ๊ตญ REDIS์ user์ ์ ๋ณด์ ํด๋นํ๋ ๊ฐ๋ค์ ๋ฃ์ด๋๊ณ ์ด๋ฅผ ์์ฒญ์ ๋ฃ์ด์ LLMํํ ์ํ ์ถ์ฒ์ ๋ฐ๋ ๊ฒ์ด ๋ชฉํ์ด๊ธฐ ๋๋ฌธ์, ํด๋น ๊ฐ๋ค์ JSON์ผ๋ก ๋ง๋ค์ด์ ์ ์ฅ ํด๋๋ ๊ฒ์ด ์ฌ๋ฌ๋ชจ๋ก ํธํ๋ค.
์ด๋ ๊ฒ JSON ์์ฒด๋ก ์ ์ฅํ๊ธฐ ์ํด์๋ Config์ ์ปค์คํ ์ผ๋ก ์ง๋ ฌํ ์ธํ ์ ํด๋๋ ๊ฒ์ด ์ข๋ค.
๊ทธ๋ฌ๋ JSON ์ง๋ ฌํ์ ๊ฒฝ์ฐ๋ ์น๋ช
์ ์ธ ๋จ์ ์ด ์๋ค. Spring Data Redis์์๋ ์ ์ฅ์ ๋จ์๊ฐ JSON(๊ฐ์ฒด ๋จ์)์ด๊ธฐ ๋๋ฌธ์ creditLeft
๊ฐ์ ํ๋ ํ๋๋ง ๋ฐ๊ฟ ๋๋ ์๋ก์ด DTO๋ฅผ ๋ง๋ค์ด์ set
ํด์ผ ํ๋ค.
์ฆ, ๋ง์ฝ ํ๋์์ update๊ฐ ํ์ํ ๊ฒฝ์ฐ๊ฐ ์๊ธฐ๋ฉด ๊ฐ์ฒด๋ฅผ ๋ค์ ๋ง๋ค์ด์ผ ํ๋ค๋ ๊ฒ์ด๋ค. ๋ด๋ถ์ ์ผ๋ก๋ ๋ฌธ์์ด ๋ฎ์ด์ฐ๊ธฐ ๋ฐ์ ์๊ธฐ ๋๋ฌธ์ด๋ค!
์ฌ๊ธฐ์ ๋ฌธ์ ๊ฐ ์๊ธด๋คโฆ JSON์ผ๋ก ์ ์ฅํ๋ฉด ์ฐ๋ฆฌ๊ฐ ์์ฒญ ๊ฐ์ ๋ฃ์๋ ํธ๋ฆฌํ ๊ฒ์ ์๊ฒ ๋๋ฐ, ์ฌ์ค ์ฐ๋ฆฌ์ ๊ฒฝ์ฐ, โ๊ด์ฌ์ฌโ ๊ฐ์ ๋งค๋ฒ ๋ฌผ์ด๋ณด๊ณ , ๊ฐฑ์ ํ๋ ๋ฐฉ์์ด๋ผ ์ ์ฅ๋ ๊ฐ์ฒด ์์์ ๋ณ๋์ด ์๊ธฐ๊ฒ ๋๋ค.
๊ทธ๋์ ๊ณ ๋ฏผ์ ํ๋๋ฐ, ๋ณ๋์ด ๋ง์ ๊ฐ์ Hash์ ๋๊ณ ๋ฉ์ด๋ฆฌ ์๋ต(JSON ์ค๋ ์ท)์ JSON์ผ๋ก ์ ์ฅํ๋ ์์ ํผํฉ ์ ๋ต์ ์ฌ์ฉํ๊ธฐ๋ ํ๋ค!!!
๋ ๋ค๋ฅธ ๊ณ ๋ฏผ๊ฑฐ๋ฆฌ,,, ๊ณผ์ฐ ์บ์ฑ์ TTL์ ์ผ๋ง๋ก ์ก์์ผ ํ๋๊ฐ? ๊ทธ๋ฆฌ๊ณ DB๋ฅผ ๋ ์ฐ๋ฅด๋ฉด์๋ ์์ก์ ์ ์ ํ๊ฒ ๊ฐ์ ธ๊ฐ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ๋๊ฐ?
์ฌ๊ธฐ์ ๋ ๋ค๋ฅธ ์ข ๋ฅ์ ๊ณ ๋ฏผ์ด ์๊ฒผ๋ค..
์๋ ์๊ฐํ๋ ๋ฐฉํฅ์, ์ด๋ฐ์ ์ฑํ ์ ์์ํ ๋, ์ฐ์ REDIS์ ์บ์ฑํด๋ ์ ์ ์ ๋ณด๊ฐ ์์ผ๋ฉด DB๋ฅผ ๊ฑฐ์น์ง ์๊ณ ๋ฐ๋ก REDIS์์ ๊ฐ์ ๊ฐ์ ธ์จ๋ค.
์ทจ๋ฏธ(๊ด์ฌ์ฌ)์ ๊ฒฝ์ฐ๋ ๋ฐ๋์๋ค๋ฉด ๋ค์ ์๋ต์์ ๋ฐ๋ ๊ฐ์ผ๋ก DB์ REDIS๋ฅผ ์ ๋ถ ๊ฐฑ์ ํด์ค๋ค.
์ด๋ ๊ฒ ํ ๊ฒฝ์ฐ, ๋ณ๋์ด ํ์ ์ธ ๊ด์ฌ์ฌ๋ง Hash๋ก ์ ์ฅํ๋ฉด ๋๋ค
๊ทธ๋ฌ๋, ์ฌ์ค ์ฐ๋ฆฌ ์ถ์ฒ ์๋น์ค๋ฅผ ์ํด์๋ ์์ก๊ณผ ์ต๊ทผ ๊ตฌ๋งคํ ์ํ์ ๋ํ ์นดํ ๊ณ ๋ฆฌ ์ ๋ณด๋ REDIS์ ๊ฐ์ด ์บ์ฑ๋๋๋ฐ, ์ด ์ ๋ณด๋ค ๋ณ๋์ฑ์ด ๋๋ฌด ํฌ๊ณ , ์์๊ฐ์ ๋ณํ๋ค.
์๋ฅผ ๋ค์ด์, ์ด๊ธฐ ์ ๋ณด๋ฅผ REDIS์ ์ ์ฅํ์ด๋ ๊ทธ ์งง์ 1~2๋ถ์ ์๊ฐ ์์ ๋ฌผ๊ฑด์ ๋๋ ๊ตฌ๋งคํด์ ์์ก์ด ํฌ๊ฒ ๋ณ๋๋ ์๋ ์๋ ๊ฒ์ด๋ค!!!!
๊ทธ๋ฌ๋ ์ฐ๋ฆฌ ์ถ์ฒ ์๋น์ค์ ๊ฒฝ์ฐ, ์ฌ์ฉ์ ์์ก์ ๊ธฐ๋ฐ์ผ๋ก ์ํ ์ถ์ฒ์ด ์ด๋ค์ง๊ณ , ์ฌ์ค์ ์์ก๊ณผ ์ถ์ฒ๋ ์ํ์ด ๋ง์ง ์์ผ๋ฉด ์ถ์ฒ ์๋น์ค์ ๋งค๋ ฅ๋๊ฐ ๋งค์ฐ ๋ง์ด ๊ฐ์ํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ๋๋ ๋งค์ฐ ๋จ์ด์ง๊ธฐ์โฆ..์ด ๋ถ๋ถ์ ๋ํ ๊ณ ๋ฏผ์ด ํ์ํ๋ค.
์ฆ, โDB๋ฅผ ๋ ์ฐ๋ฅด๋ฉด์๋ ์์ก์ ์ ์ ํ๊ฒโ๊ฐ ํฌ์ธํธ์ธ ๊ฒ์ด๋ค.
ํด๊ฒฐ์ฑ - ํ์ด๋ธ๋ฆฌ๋ ํค ์ค๊ณ
์ด ๋ถ๋ถ์ ํค ์ค๊ณ ๋ฐฉ์์ ๋ ๊ฐ๋ก ๋๋๊ณ , ๋ณ๋์ด ๋ง์ ์์๋ง TTL์ ์๊ฒ ์ค์ ํ์ฌ DB ์กฐํ ํ๋ ๋ฐฉ์์ผ๋ก ์ค๊ณํ๋ฉด ๊ณ ๋ฏผ์ ์ ์ด๋์ ๋ ํด์ํ ์ ์๋ค.
๋ณํ๊ฐ ์ ์ name, creditLimit์ JSON์ผ๋ก ๋ฌถ์ด์ TTL์ 1์๊ฐ ~ 1์ผ ์ ๋๋ก ๊ธธ๊ฒ ์ค์ ํ๊ณ , ๋ณํ๊ฐ ์๋์ ์ผ๋ก ๋ง์ creditLeft์ hobbies์ TTL์ ์งง๊ฒ (5๋ถ) ์ค์ ํ๊ณ , Hash๋ก ์ ์ฅ๋๋๋ก ํ์ฌ ์ด๋ฒคํธ๋ก ๊ฐฑ์ ๋๋๋ก ํ๋ ๊ฒ์ด๋ค ๋ํ, ์ฐธ๊ณ ๋ก ํค๋ฅผ ์์ฑํ ๋ userId๋ ํค์ ๋ฃ์ ๊ฒ์ด๋ฏ๋ก userId๋ ๋ฐ๋ก value์ ์ ์ฅํ ํ์๊ฐ ์๋ค.
๊ทธ๋ฌ๋, ์ฌ๊ธฐ๊น์ง ์ ํด๋๋๋ฐ ๋ค์ ๊ณ ๋ฏผ์ ์ด ์๊ฒผ๋คโฆ
์๋ ๊ทธ๋ฌ๋ฉด, ์ฑํ ๋ฐฉ์ ์ด์ด๋ ์ฑ๋ก 10๋ถ ์ ๋๊ฐ ์ง๋ฌ๋๋ฐ ๋ค์ ์ถ์ฒํด๋ฌ๋ผ๊ณ ํ๋ค๋๊ฐ ํ๋ฉด ์ด๋ป๊ฒ ํด?
๊ทธ๋ฌ๋ฉด ๋ ์ด๋ฐ ๋ฌธ์ ๊ฐ ์กด์ฌํ๋ค. TTL์ 5๋ถ ์ ๋๋ก ์งง๊ฒ ํด๋๋ค๊ณ ํ์. ๊ทธ๋ ๋ค๋ฉด ์ฌ์ฉ์๊ฐ ์ฑํ ๋ฐฉ์ ์ค๋ ์ผ ๋ ์ํ์์ TTL ์๊ฐ๋ณด๋ค ๋ง์ ์๊ฐ์ ํ๋ ค๋ณด๋ธ ๋ค์์ ๋ค์ ์ถ์ฒํด๋ฌ๋ผ๊ณ ํ๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
๊ทธ๋์ ์ด๋ด๋ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฐฉ๋ฒ์ด ๋ฐ๋ก ์บ์ ๋ฏธ์ค(Read-Through) ํจํด์ด๋ค.
์บ์ ๋ฏธ์ค(Read-Through) ํจํด?
์บ์ ๋ฏธ์ค๋, Redis์์ ๋จผ์ ์กฐํํ๋, ๊ฐ์ด ์์ ๊ฒฝ์ฐ DB์์ ๋ค์ โ์ ์ ํ ๊ฐโ์ ๊ฐ์ ธ์์ ์ธํ ํด์ ๋ฐํํ๋ ๋ฐฉ์์ ์๋ฏธํ๋ค.
์ด๋ ๊ฒ ์งํํด๋, TTL์ด ์กด์ฌํ๊ธฐ ๋๋ฌธ์ ํ์์๋ DB ๋ถํ๋ฅผ ์ด๋์ ๋ ๋ฐฉ์ง ํ ์ ์๋ค.
์ฆ, ๋ด๊ฐ ๋ด๋ฆฐ ์ต์ข ์ ๋ต์ ์ ๋ฆฌํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
์ข ๋ฅ | key name | values | TTL | ๊ธฐํ |
---|---|---|---|---|
snapshot | v1:snap:user:{userId} | {"name":"ํผ์ฉํฌ","creditLimit":30000} | 12h | Read-Through |
balance | v1:bal:user:{userId} | balance(int) | 3m | Read-Through |
hobby | v1:hb:user:{userId} | hobby(String) | 5m | Read-Through |
์ค๋ ์ท JSON ์ง๋ ฌํ๋ฅผ ์ํ Config ์ค์
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory cf) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(cf);
// Key๋ String, Value๋ JSON ์ง๋ ฌํ
var stringSer = new StringRedisSerializer();
var jsonSer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(stringSer);
template.setValueSerializer(jsonSer);
template.setHashKeySerializer(stringSer);
template.setHashValueSerializer(jsonSer);
template.afterPropertiesSet();
return template;
}
}
์ด๋ ๊ฒ Config์ ์ง๋ ฌํ๋ฅผ ์ ์ํด์ผ ํ๋ ์ด์ ๋, Spring Boot ๊ธฐ๋ณธ RedisTemplate<String, Object> ๋ JdkSerializationRedisSerializer๋ฅผ ์ฐ๋๋ฐ, ์ด ๋๋ฌธ์ Redis์ ๋ฐ์ดํธ ๋ฐฐ์ด(์ด์ํ ๋ฐ์ด๋๋ฆฌ)๋ก ์ ์ฅ์ด ๋๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋์ GenericJackson2JsonRedisSerializer๋ฅผ ์ด์ฉํด์ JdkSerializationRedisSerializer๊ฐ ์๋, ์ฐ๋ฆฌ๊ฐ ์ํ๋ Json ๋ฐฉ์์ผ๋ก ๋ฌธ์ ์์ด ์ ์ฅ๋๋๋ก ๊ตฌ์ฑํ ์ ์๋ค.
๋ณธ๊ฒฉ์ ์ผ๋ก ์บ์ฑ์ ์์ํด๋ณด์!
์ฐ๋๊น์ง ์๋ฃ ๋์์ผ๋, ์ด์ ์๋ฒ์์ > ๋ ๋์ค๋ก ์บ์ฑํ๋ ๋จ๊ณ๋ฅผ ์งํํด์ผ ํ๋ค.
๋์ ๊ฒฝ์ฐ, ์์๋ง๋ค TTL์ ๋ค๋ฅด๊ฒ ์ค์ ํ๊ธฐ ๋๋ฌธ์ ์ธ๋ถ์ ์ธ ์ค์ ์ด ํ์ํ๋ค. ์ฆ, JSON ์ง๋ ฌํ + ํญ๋ชฉ ์ข ๋ฅ๋ณ ์๋ก ๋ค๋ฅธ TTL + ์ปค๋ฐ ์ดํ ์บ์ฑ ์ผ๊ด์ฑ์ ์ง์ผ์ผ ํ๋ ๊ฒ์ธ๋ฐ, ๊ทธ๋ฌ๋ฏ๋ก RedisTemplate + ์ ์ฉ ์บ์ ์ปดํฌ๋ํธ ๋ถ๋ฆฌ๋ก ๋ง๋๋ ๊ฒ์ด ์ข๋ค.
๋์ ๊ฒฝ์ฐ๋ ์ด๋ฏธ RedisTemplate๋ฅผ ํตํด JSON ์ง๋ ฌํ๋ฅผ ์ค์ ํด ๋ ์ํ์ด๋ฏ๋ก, ์บ์ ์คํ๋ง ์ง๋๋ฉด ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
REDIS ์บ์ฑ์ ์ํ TTL ์ค์ ๊ฐ
ํค ์์ : v1:snap:user:123, v1:bal:user:123, v1:hb:user:123
*/
@Getter
@RequiredArgsConstructor
public enum CacheSpec {
// 1) ์ฌ์ฉ์ ์ค๋
์ท(JSON)
SNAPSHOT("v1:snap", Duration.ofHours(12)),
// 2) ์์ก(String) - 3๋ถ
BALANCE("v1:bal", Duration.ofMinutes(3)),
// 3) ์ทจ๋ฏธ(List<String> or Set<String>) - 5๋ถ
HOBBIES("v1:hb", Duration.ofMinutes(5));
private final String prefix; //ํค ๋ค์ ์คํ์ด์ค
private final Duration ttl;
}
๋ํ, ์๋น์ค ๋จ์์ ์ต๋ํ ๊ฐ๊ฒฐํ๊ฒ ์บ์ฑ์ ์ฌ์ฉํ๊ธฐ ์ํด์ ๋ ๋์ค๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ์ ํธ ํด๋์ค๊ฐ ํ์ํ๋ค.
์ด ์ ํธ ํด๋์ค๋, ๋ ๋์ค๋ฅผ ์ฌ์ฉํ๋ ์๋น์ค ๋จ์์ ์ผ๊ด์ ์ผ๋ก ์ฌ์ฉํ๋๋ก ํ๊ธฐ ์ํด ์ ์ํด๋๋ค.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@Component
@RequiredArgsConstructor
public class CacheStore {
// ์ ์ํด๋ REDIS TEMPLATE๋ฅผ ๊ฐ์ ธ์ด
private final RedisTemplate<String, Object> redis;
// ํค๋ฅผ ํ ๊ณณ์์ ๋ง๋ ๋ค.
private String key(CacheSpec spec, Object userId){
return spec.getPrefix() + ":user:" + userId;
}
// put ์ฒซ๋ฒ์งธ๋ ๊ทธ ์คํ(CacheSpec)์ ์ ํด๋ ๊ธฐ๋ณธ TTL
// put ๋๋ฒ์งธ๋ ์ ์ํ ttl์ ์ฌ์ฉํ๊ณ ์ถ์๋ ์ฌ์ฉํ๋ค.
public void put(CacheSpec spec, Object userId, Object value) {
put(spec, userId, value, spec.getTtl());
}
public void put(CacheSpec spec, Object userId, Object value, Duration ttl) {
redis.opsForValue().set(key(spec, userId), value, ttl);
}
// redis๋ Object๋ง ๋ฐํํ๊ธฐ ๋๋ฌธ์ DTO๋ฅผ ๊บผ๋ด ์ฐ๋ ค๋ฉด ์บ์คํ
์ด ํ์์ด๋ค.
// ๊ทธ๋์ redis.opsForValue().get(...)์ ๊ฐ์ ํญ์ Object์ด๊ธฐ ๋๋ฌธ์ ์ด๊ฑธ ์ ์ํด์ ์๋น์ค ๋จ์์ ์บ์คํ
์ ํ์ง ์๋๋ก ํด์ผ ํ๋ค.
@SuppressWarnings("unchecked")
public <T> T get(CacheSpec spec, Object userId, Class<T> type) {
Object raw = redis.opsForValue().get(key(spec, userId));
return type.cast(raw); //typecast๋ฅผ ํ ๋ฒ ํด์ฃผ๊ธฐ ๋๋ฌธ์, ์๋ชป๋ ํ์
์ผ๊ฒฝ์ฐ ClassCastException๊ฐ ๋ฐ์ํ๋ค.
}
// DB์์ ํด๋น ๋ฐ์ดํฐ๊ฐ ๋ฐ๋์๊ฑฐ๋ ์์ด์ก์ ๋, ์บ์๋ ์ง์์ ์ผ๊ด์ ์ผ๋ก ๋ง๋ค๊ธฐ ์ํจ์ด๋ค.
// โTTL ๋ง๋ฃโ๋ฅผ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ์ฆ์ ๋ฌดํจํํ ๋ ์ฌ์ฉํ๋ค.
public void evict(CacheSpec spec, Object userId) {
redis.delete(key(spec, userId));
}
// Read-Through: ์บ์ ๋ฏธ์ค ์ ๋ก๋ ์คํ โ ์บ์์ ์ ์ฅ ํ ๋ฐํํ๋๋ก ์ค์ ํ๋ค.
public <T> T getOrLoad(CacheSpec spec, Object userId, Class<T> type, Supplier<T> loader) {
// 1) ์บ์ ๋จผ์ ์กฐํ
T cached = this.get(spec, userId, type);
if (cached != null) {
return cached;
}
// 2) ๋ฏธ์ค๋ฉด "๋ก๋" ์คํ (์ฌ๊ธฐ์ DB ์ ๊ทผ์ด ์ํ๋จ)
// ์ฌ๊ธฐ์ Supplier<T>๋ฅผ ์ฌ์ฉํด์ ์กฐํ ๋ก์ง์ ์จ๊ธธ ์ ์๋ค. ์ด๋ฅผ ํตํด ์กฐํ ๋ก์ง์ด ์๋น์ค ๋จ์ผ๋ก ์์ด๋๊ฐ์ง ์๋๋ก ํ๋ค.
T loaded = loader.get();
// 3) ๊ฐ์ด ์์ผ๋ฉด ์บ์์ ๋ฃ๊ณ ๋ฐํ (ํค/TTL์ spec์์ ๊ฐ์ ธ์ด)
if (loaded != null) {
this.put(spec, userId, loaded, spec.getTtl());
}
return loaded;
}
}
redis.opsForValue()๋ฅผ ํตํด์ String ๊ธฐ๋ฐ ์ฐ์ฐ์๋ฅผ ๊ฐ์ ธ์จ๋ค. ์ด๋ฅผ ํตํด์ redis์ ๊ธฐ๋ณธ ์ฐ์ฐ์๋ค์ ์ด์ฉํ ์ค๋น๋ฅผ ํ๋ ๊ฒ์ด๋ค.
set(key(spec, userId), value, ttl)์ ํตํด์ ๋ด๊ฐ ์ํ๋ key๋ก ์กฐํฉ์ ํด์, ํด๋นํ๋ value๋ฅผ ์ ์ฅํ๋ฉด redis์ ์ ์ฅ์ด ๊ฐ๋ฅํ ๊ฒ์ด๋ค. ์ด๋ฅผ put์ผ๋ก ์ ์ํด์ ๋ชจ๋ ๊ณณ์์ ์ผ๊ด์ ์ผ๋ก ๊ฐ์ ์ ์ฅํ ์ ์๋๋ก ๊ตฌ์ฑํ๋ค.
getOrLoad์ redis์ ๊ฐ์ด ์์ผ๋ฉด Supplier๋ก ๊ฐ์ผ db ์กฐํ ๋ก์ง์ ์คํํ๊ณ , ์์ผ๋ฉด ๋ฐ๋ก cache์ ์๋ ๊ฐ์ ๊บผ๋ผ ์ ์๊ฒ ํ๊ธฐ ์ํ ๋ก์ง์ด๋ค. ์ด ๋ก์ง์ด ๋ฐ๋ก read-through๋ฅผ ์ํ ๋ถ๋ถ์ด๋ค.
Supplier๋ฅผ ์ฌ์ฉํ ์ด์ ๋ ๋ฌด์์ธ๊ฐ?
์ด๋ฐ ์๋ฌธ์ ๊ฐ์ง ์ ์๋ค. ๊ทธ๋ ๋ค๋ฉด ์ ๊ตณ์ด db๋ฅผ ๋ถ๋ฌ์ค๋ ๋ก์ง์ ๋ฐ๋ก ๋ฃ์ง ์๊ณ , ์ด๋ ๊ฒ Supplier
๋ํ์ ์ธ ์ด์ ๋ โ์ค๋ณต ๋ก์งโ์ ์ค์ด๊ธฐ ์ํจ์ด๋ค.
Supplier
์ค์ Supplier
1
2
3
4
5
6
7
8
public MemberCachingDto getSnapshot(Long memberId) {
return cacheStore.getOrLoad(
CacheSpec.SNAPSHOT,
memberId,
MemberCachingDto.class,
() -> memberRepository.findDtoByMemberId(memberId)
);
}
์ด๋ฅผ ์ด์ฉํด์ ๋ ๋์ค ์บ์ฑ์ ์ํ Service class๋ฅผ ๊ตฌ์ฑํด๋ณด์!
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
31
32
33
34
35
36
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true) // ๊ธฐ๋ณธ์ ์กฐํ ํธ๋์ญ์
public class MemberService {
private final MemberRepository memberRepository;
private final CacheStore cacheStore;
public int getBalance(Long memberId) {
return cacheStore.getOrLoad(
CacheSpec.BALANCE,
memberId,
Integer.class,
// ์บ์ ๋ฏธ์ค์ผ ๋๋ง ์คํ๋๋ DB ๋ก๋ฉ ํจ์์ด๋ค. ์ด๋ฅผ Supplier๋ฅผ ์ฌ์ฉํ๋ฉด ํ์ํ ํจ์๋ฅผ ์ ์ํ๋ฉด ๋๋ค.
() -> memberRepository.findBalanceByMemberId(memberId)
);
}
public String getHobby(Long memberId) {
return cacheStore.getOrLoad(
CacheSpec.HOBBIES,
memberId,
String.class,
() -> memberRepository.findHobbyByMemberId(memberId)
);
}
public MemberCachingDto getSnapshot(Long memberId) {
return cacheStore.getOrLoad(
CacheSpec.SNAPSHOT,
memberId,
MemberCachingDto.class,
() -> memberRepository.findDtoByMemberId(memberId)
);
}
}
์ด๋ ๊ฒ ์ ์ํ๋ฉด, ๊ฐ๊ฐ ์ ์๋ ํค - ๋ฐธ๋ฅ ์ ๋ง๋ค ๊ทธ ํํ์ ๋ง๋ ๊ฐ์ ์บ์์์ ๋ถ๋ฌ์ค๊ฑฐ๋, ์ ์ฅํ ์ ์๋ค.
์ด๋ฅผ ์ค์ ๋ก ์ด๋ป๊ฒ ํ์ฉํ๋์ง๋, ๋ค์ ํฌ์คํ ์์ ์ง์ Test Code๋ฅผ ์ดํด๋ณด๋ฉฐ ์์๋ณด๋๋ก ํ์.