채팅 기능 구현하기 (2) - feat. Spring Redis

bagt13·2024년 9월 28일
0

Project

목록 보기
19/19
post-thumbnail
post-custom-banner

이전 포스트

채팅 기능 구현하기 (1) - feat. Redis Pub/Sub, Scheduler

채팅 기능을 구현하고, 채팅을 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에 접근하고 있는 것이었다. 이런 작업이 채팅 메시지 전송때마다 반복되면 비효율적이기 때문에, 유저 정보까지 캐싱하도록 결정했다.


♻️ RedisUserRepository

@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() : 캐시 데이터를 업데이트한다. 이 메서드는 유저가 회원정보를 업데이트 할때마다 수행된다.


♻️ UserService.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를 통해 유저 정보를 가져오도록 했다.


Image Reference

profile
주니어 백엔드 개발자입니다😄
post-custom-banner

0개의 댓글