Spring Boot 오류 개선 2. Redis 사용 API 성능 향상

최민길(Gale)·2023년 7월 16일
1

Spring Boot 적용기

목록 보기
38/46

안녕하세요 이번 시간에는 Redis를 사용하는 API의 성능을 개선하는 방법에 대해서 포스팅하도록 하겠습니다.

발생한 이슈에 대해 먼저 말씀드리겠습니다. Redis 접속 로직이 복잡해짐에 따라 Redis를 사용하는 7개의 API 실행 시 약 1600ms 정도의 속도를 보였습니다.

이를 개선하기 위해 Redis 클래스를 수정했습니다. 기존 방식은 Redis에 저장된 값을 조회 또는 Redis에 값을 저장할 때마다 매번 Redis에 접속하였습니다. 하지만 이 경우 불필요하게 Redis에 접속을 많이 하게 되어 오버헤드가 발생할 수 있습니다.

이를 해결하기 위해 정적 팩토리 메서드 패턴을 적용하였습니다. Redis 접속 클래스에 유저 정보 객체를 static으로 선언한 후 해당 값이 null일 경우 Redis에 접속해서 데이터를 가져오고 값을 초기화합니다. 값이 이미 초기화되었다면 해당 값을 리턴합니다. 이는 유저 정보가 자주 변하지 않는 객체이기 때문에 사용하는데 지장이 없으며 이로 인해 약 62.5% 가량 성능을 향상시켰습니다.

@Component
@RequiredArgsConstructor
public final class RedisProvider extends AbstractProvider implements BaseProvider {
    private final AuthRepository authRepository;
    private final RedisTemplate<String, byte[]> redisTemplate;
    private static UserInfoAllDao userInfo = null;

    @Override
    public void afterPropertiesSet() throws Exception {

    }

    /**
     * Redis key를 매개변수로 Redis에 저장된 값 get
     * @param key
     * @return
     */
    private UserInfoAllDao getRedis(String key) {
        byte[] bytes = redisTemplate.opsForValue().get(key);
        if(bytes == null) return null;

        try {
            UserInfoProto.UserInfo userProto = UserInfoProto.UserInfo.parseFrom(bytes);
            return UserInfoAllDao.builder()
                    .userID(userProto.getUserID())
                    .userCode(userProto.getUserCode())
                    .email(userProto.getEmail())
                    .password(userProto.getPassword())
                    .userName(userProto.getUserName())
                    .appPassword(userProto.getAppPassword())
                    .build();
        }
        catch (Exception e){
            throw new WrongAccessException(WrongAccessException.of.REDIS_CONNECTION_EXCEPTION);
        }
    }


    private void setRedis(String key){
        UserInfoAllDao userInfoAllDao = authRepository.getUserInfoAll(key);
        UserInfoProto.UserInfo userProto = UserInfoProto.UserInfo.newBuilder()
                .setUserID(userInfoAllDao.getUserID())
                .setUserCode(userInfoAllDao.getUserCode())
                .setEmail(userInfoAllDao.getEmail())
                .setPassword(userInfoAllDao.getPassword())
                .setUserName(userInfoAllDao.getUserName())
                .setAppPassword(userInfoAllDao.getAppPassword())
                .build();
        redisTemplate.opsForValue().set(key,userProto.toByteArray());
    }


    public UserInfoAllDao getUserInfo(String email){
        if(userInfo == null){
            UserInfoAllDao userInfoAllDao = getRedis(email);
            if(userInfoAllDao == null){
                userInfoAllDao = authRepository.getUserInfoAll(email);
                setRedis(email);
            }
            if(!userInfoAllDao.getEmail().equals(email)) throw new WrongArgException(WrongArgException.of.WRONG_BODY_EXCEPTION);
            userInfo = userInfoAllDao;
        }
        return userInfo;
    }
}

추후 Redis 접속을 비동기로 처리하여 성능을 개선하는 방법에 대해서 조금 더 알아보는 작업을 진행하도록 하겠습니다. 그럼 이상으로 포스팅 마치도록 하겠습니다!

profile
저는 상황에 맞는 최적의 솔루션을 깊고 정확한 개념의 이해를 통한 다양한 방식으로 해결해오면서 지난 3년 동안 신규 서비스를 20만 회원 서비스로 성장시킨 Software Developer 최민길입니다.

0개의 댓글