What to do?
Redis 도입하기
데이터의 변경은 없고, 호출이 잦은 경우 캐시에 저장해놓으면 서버에 부하를 줄일 수 있다.
SNS 프로젝트에서 대표적인 케이스가 유저정보를 불러오는 것이다.
이번 포스팅에서는 Redis를 이용해 유저정보를 캐싱해보았다.
application.yaml
- redis host와 port 번호 설정
spring:
...
redis:
host: localhost
port: 6379
Configuration
- redis에 UserEntity를 저장
@Configuration
@EnableRedisRepositories
@RequiredArgsConstructor
public class RedisConfig {
private final RedisProperties redisProperties;
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}
@Bean
public RedisTemplate<String, UserEntity> userEntityRedisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, UserEntity> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<UserEntity>(UserEntity.class));
return redisTemplate;
}
}
Repository
- 데이터 저장시 키 값 : USER__USERNAME__[유저명]
- 데이터 저장 기간 : 3일
@Slf4j
@Repository
@RequiredArgsConstructor
public class UserCacheRepository {
private final RedisTemplate<String, UserEntity> userRedisTemplate;
private final static Duration USER_CACHE_DURATION = Duration.ofDays(3);
public void setUserByUsername(UserEntity user){
String key = getKey(user.getUsername());
log.info("setUser {}", key);
userRedisTemplate.opsForValue().set(key,user, USER_CACHE_DURATION);
}
public Optional<UserEntity> getUserByUsername(String username){
String key = getKey(username);
UserEntity user = userRedisTemplate.opsForValue().get(key);
log.info("getUser {}", key);
return Optional.ofNullable(user);
}
private static String getKey(String username){
return String.format("USER__USERNAME__%s", username);
}
}
Service
- Redis에서 유저가 조회 안되면, 유저 DB에서 조회
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final UserCacheRepository userCacheRepository;
@Value("${jwt.secret-key}") private String secretKey;
@Value("${jwt.duration}") private Long duration;
...
@Transactional(readOnly = true)
public UserEntity findByUsernameOrElseThrow(String username){
// redis cache 조회 후 없으면
return userCacheRepository.getUserByUsername(username).orElseGet(
// DB 조회 후 없으면 에러
()-> userRepository.findByUsername(username).orElseThrow(
()->{throw CustomException.of(CustomErrorCode.USERNAME_NOT_FOUND);})
);
}
}
Controller
- Redis와는 관련 없는 내용이지만, 성능을 높히기 위해 Controller도 수정
- findByUsername으로 유저를 조회하는 횟수를 줄이기 위해 Authentication에서 principal을 추출해서 Service로 던지도록 Controller 수정
- (Example)
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1")
public class PostController {
private final PostService postService;
...
@PutMapping("/post/{postId}")
public CustomResponse<Void> modifyPost(@PathVariable Long postId, @RequestBody ModifyPostRequest req, Authentication authentication){
UserEntity user = (UserEntity) authentication.getPrincipal();
postService.modifyPost(postId, req.getTitle(), req.getContent(), user, req.getHashtags());
return CustomResponse.success();
}
...
}
'Java > Spring' 카테고리의 다른 글
[Spring] 간단한 SNS 만들기 #7 (0) | 2023.01.21 |
---|---|
[Spring] 간단한 SNS 만들기 #6 (0) | 2023.01.15 |
[Spring] 간단한 SNS 만들기 #4 (3) | 2023.01.01 |
[Spring] 간단한 SNS 만들기 #3 (0) | 2022.12.22 |
[Spring] 간단한 SNS 만들기 #2 (0) | 2022.12.22 |