[Redis] API ์š”์ฒญ ๋งˆ๋‹ค ์กฐํšŒํ•˜๋Š” User ๋ฐ์ดํ„ฐ ๋ ˆ๋””์Šค๋กœ ์ตœ์ ํ™”

Cache๋ž€ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ์ €์žฅํ•ด๋†“๊ณ  ๋‹ค์‹œ ์ด ๋ฐ์ดํ„ฐ๋ฅผ ํ•„์š”๋กœ ํ•  ๋•Œ๋งˆ๋‹ค ๋น ๋ฅด๊ฒŒ ์ฐธ์กฐํ•˜์—ฌ ์ฝ๊ธฐ ๊ธฐ๋Šฅ์„ ๊ฐœ์„ ํ•˜๋Š” ๊ธฐ์ˆ ์ด๋‹ค.

 

ํ˜„์žฌ ์ง„ํ–‰ ์ค‘์ธ ํ”„๋กœ์ ํŠธ์—์„œ ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ์œ„ํ•ด ์บ์‹œ๋ฅผ ๋„์ž…ํ•˜์˜€๊ณ , Redis๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

๊ฑฐ์˜ ๋ชจ๋“  API ์š”์ฒญ์—์„œ ํšŒ์›์„ ์กฐํšŒํ•˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜๊ฐ”๊ณ  ์ด๋ฅผ ์บ์‹ฑ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์œ„์™€ ๊ฐ™์ด ๊ตฌ์ƒํ•˜์˜€๋‹ค.

์ตœ์ดˆ ๋กœ๊ทธ์ธ ์‹œ redis์— ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๊ณ  ์ถ”ํ›„์— loadByUsername ํ˜ธ์ถœ๋  ๋•Œ redis ์—์„œ ๊ฐ’์„ ๋จผ์ € ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์˜€๋‹ค. 

 

 

Redis ์„ค์ •

build.gradle

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

 

RedisConfig 

@Configuration
@EnableRedisRepositories
@RequiredArgsConstructor
public class RedisConfiguration {
    /**
     * redis ์ปค๋งจ๋“œ๋“ค์„ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค
     */

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    private final RedisProperties redisProperties;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(host, port);
    }
    @Bean
    public RedisTemplate<String, User> userRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, User> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory); // redis ์„œ๋ฒ„์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๊ฐ–๊ณ  ์žˆ์Œ
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<User>(User.class));
        return redisTemplate;
    }
}

 

Redis Repository

  • expired ํƒ€์ž„์„ ๊ฑธ์–ด์ฃผ๋Š”๊ฑธ ๊ถŒ์žฅ
@Repository
@RequiredArgsConstructor
@Slf4j
public class UserCacheRepository {
    private final RedisTemplate<String, User> userRedisTemplate;
    private final static Duration USER_CACHE_TTL = Duration.ofDays(3); // 3์ผ์˜ ๋งŒ๋ฃŒ๊ธฐ๊ฐ„ ์„ธํŒ…

    public void setUser(User user) {
        log.info("Set User to Redis {}:{}", getUser(user.getUsername()), user, USER_CACHE_TTL);
        userRedisTemplate.opsForValue().set(getKey(user.getUsername()), user, USER_CACHE_TTL);
    }

    public User getUser(String userName) {
        User user = userRedisTemplate.opsForValue().get(getKey(userName));
        log.info("Get User to Redis {}:{}", getUser(userName), user);
        return user;
    }

    //key ๊ฐ’์— ํ”„๋ฆฌํ”ฝ์Šค๋ฅผ ๋ถ™์—ฌ์คŒ
    private String getKey(String userName) {
        return "USER:" + userName;
    }
}

 

 

loadUserByUsername ๋ฐ login ๋กœ์ง ์ˆ˜์ •

@Override
    public User loadUserByUsername(String userName) throws UsernameNotFoundException {
        // redis ์— ์žˆ๋Š”์ง€ ์ฒดํฌํ•˜๊ณ  ์—†๋‹ค๋ฉด orElseGet ์„ ์‚ฌ์šฉํ•˜์—ฌ db ์—์„œ ์กฐํšŒ
        User user = userCacheRepository.getUser(userName)
                .orElseGet(() -> userRepository.findByUserName(userName).map(User::fromEntity).orElseThrow(
                        () -> new SimpleSnsApplicationException(ErrorCode.USER_NOT_FOUND, String.format("userName is %s", userName))));
        return user;
    }

    public String login(String userName, String password) {
        User savedUser = loadUserByUsername(userName);
        System.out.println("===================");
        userCacheRepository.setUser(savedUser);

        //๋น„๋ฐ€๋ฒˆํ˜ธ ์ฒดํฌ
        if (!encoder.matches(password, savedUser.getPassword())) {
            throw new SimpleSnsApplicationException(ErrorCode.INVALID_PASSWORD, String.format("userName is %s", userName));
        }
        return JwtTokenUtils.generateAccessToken(userName, secretKey, expiredTimeMs);
    }