본문 바로가기

Java/Spring

[Spring] 간단한 SNS 만들기 #5

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