채팅 기능을 구현하고, 채팅을 Redis 메모리에 저장하고, 일정한 주기에 따라 RDB와 Redis를 동기화하는 작업까지 마쳤지만, 하지만 여전히 고치고 싶은 부분이 있었다.
public void sendMessage(ChatMessageRequest request) {
User user = userRepository.findById(request.getSenderId());
ChatMessage chatMessage = createChatMessage(request, user);
redisChatMessageRepository.save(chatMessage);
String topic = createTopic(chatMessage.getRoomId());
ChatMessageResponse chatMessageResponse = ChatMessageResponse.of(chatMessage);
redisPublisher.publish(topic, chatMessageResponse);
}
바로 채팅 메시지 전송 시 user 정보를 가져오기 위해 DB에 접근하고 있는 것이었다. 이런 작업이 채팅 메시지 전송때마다 반복되면 비효율적이기 때문에, 유저 정보까지 캐싱하도록 결정했다.
@Repository
public class RedisUserRepository {
private static final String USER_KEY_PREFIX = "user:";
private final RedisTemplate<String, UserCache> userRedisTemplate;
private final ValueOperations<String, UserCache> valueOperations;
private final UserRepository userRepository;
public RedisUserRepository(@Qualifier("userRedisTemplate") RedisTemplate<String, UserCache> userRedisTemplate,
UserRepository userRepository) {
this.userRedisTemplate = userRedisTemplate;
valueOperations = userRedisTemplate.opsForValue();
this.userRepository = userRepository;
}
public String getUserKey(Long userId) {
return USER_KEY_PREFIX + userId;
}
public UserCache findById(Long userId) {
Optional<UserCache> findUserCache = Optional.ofNullable(valueOperations.get(getUserKey(userId)));
if (findUserCache.isPresent()) {
return findUserCache.get();
} else {
User findUser = userRepository.findById(userId)
.orElseThrow(() -> new MogetherException(USER_NOT_FOUND));
UserCache userCache = UserCache.of(findUser);
return save(userCache);
}
}
public UserCache save(UserCache userCache) {
String userKey = getUserKey(userCache.getSenderId());
valueOperations.set(userKey, userCache);
return userCache;
}
public void update(UserCache userCache) {
String userKey = getUserKey(userCache.getSenderId());
valueOperations.set(userKey, userCache);
}
}
getUserKey()
: 지정한 PREFIX와 userId를 결합하여 KEY를 생성한다.
findById()
: 유저 정보 조회 시, 캐시 데이터가 있다면 해당 데이터를 가져오고, 없다면 RDB에서 조회 후 캐시 데이터를 생성/저장한다.
update()
: 캐시 데이터를 업데이트한다. 이 메서드는 유저가 회원정보를 업데이트 할때마다 수행된다.
public UserUpdateResponse update(Long userId, AppUser appUser,
UserUpdateRequest userUpdateRequest, MultipartFile image) {
validateUser(userId, appUser.getId());
User findUser = findById(userId);
profileImageService.update(findUser, image);
updateUser(userUpdateRequest, findUser);
//추가된 부분
redisUserRepository.update(UserCache.of(findUser));
return UserUpdateResponse.of(findUser);
}
public void sendMessage(ChatMessageRequest request) {
//수정한 부분
UserCache user = redisUserRepository.findById(request.getSenderId());
}
ChatService에서도 채팅 전송시마다 RDB Repository가 아닌 Redis Repository를 통해 유저 정보를 가져오도록 했다.