영호

[Spring] TaskScheduler 를 활용해 만료된 인증코드 제거하기 본문

Spring

[Spring] TaskScheduler 를 활용해 만료된 인증코드 제거하기

0h0 2024. 3. 12. 16:44

들어가면서

프로젝트를 진행하면서 인증코드를 redis 로 관리했습니다. 인증코드는 3분이란 유효시간이 있고 유효시간이 지난 인증코드로 인증 요청을 하면 실패해야 합니다.

redis 는 메모리 기반이기 때문에 유효하지 않은 데이터는 제거하여 유효한 데이터만 저장하고 싶었기 때문에 만료된 인증코드를 어떻게 바로바로 제거할지 고민한 과정을 포스팅 할 예정입니다.

redis 의 Set ex 파라미터 활용

redis 에는 다양한 command 가 있고, 그 중 key 를 저장하면서 만료시간을 설정할 수 있습니다.

 

redis 의 ttl 만료된 키 삭제 메커니즘

redis 는 만료된 키를 삭제하는 2가지의 메커니즘이 있습니다.

  1. 만료된 key 접근 시 삭제
  2. 일정 주기로 ttl 이 설정된 키를 선정해 만료된 key 를 제거하고, 선정된 key 중 25% 이상이 만료되었으면 다시 반복

 

개선하고 싶은 부분

redis ttl 만료 키 삭제 메커니즘으로 인해 만료된 key 가 redis 에 만료된 인증코드 데이터가 남아있을 수 있습니다.

저는 이 부분이 마음에 들지 않았고 인증코드의 유효시간이 지나면 바로 삭제시키고 싶었습니다. 그래서 Spring 의 스케줄링 관련 내용을 찾아봤고, TaskScheduler 를 활용해 인증코드의 유효시간이 지나면 바로 삭제하도록 구현했습니다.

만료된 key 순회하면서 제거하기

keys 명령어를 통해 현재 존재하는 모든 key 를 조회하고, 이를 순회하며 접근해 만료되었다면 삭제시키는 방법을 고려했습니다.

그러나, keys 명령어는 O(N) 의 시간 복잡도를 가지기 때문에 싱글 스레드 기반인 redis 에서는 주의해야 되는 명령어입니다. 물론 현재 인증코드 데이터가 많지 않기 때문에 상관 없겠지만, 이왕이면 더 나은 방법으로 해결하고 싶어서 선택하지 않았습니다.

TaskScheduler 활용

인증코드가 생성되었을 때, 만료시간 이후 삭제 코드를 실행시키면 keys 명령어 없이도 제가 원하는 개선을 이룰 수 있을 것 같았습니다.

 

Spring 의 TaskScheduler 문서를 보면 schedule() 메서드의 다양한 파라미터를 통해 스케줄링 태스크를 추가할 수 있습니다.

그 중 schedule(Runnable task, Instant startTime); 메서드를 활용하면 스케줄링 태스크가 추가되고 startTime 에 해당 태스크가 실행되도록 할 수 있습니다.

이를 통해, 인증코드 생성 이후, 만료시간이 되면 해당 인증코드를 삭제하는 명령어를 동적으로 실행시킬 수 있습니다.

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void scheduledAuthCodeRemove(AuthCodeCreateEvent authCodeCreateEvent) {
    String authCode = authCodeCreateEvent.getAuthCode();
    String destination = authCodeCreateEvent.getDestination();
    String authCodePlatform = authCodeCreateEvent.getAuthCodePlatform();
    String authCodeCategory = authCodeCreateEvent.getAuthCodeCategory();

    String authCodeKey = AuthCodeKeyConverter.convert(authCode, destination, authCodePlatform, authCodeCategory);
    // 스케줄 task 등록
    taskScheduler.schedule(() -> redisTemplate.delete(authCodeKey), Instant.now().plusSeconds(authCodeExpired));
}

이처럼 인증코드 생성 후 이를 저장하는 트랜잭션이 종료되면 해당 이벤트를 구독하는 쪽에서 인증코드 key 를 삭제하는 스케줄링 태스크를 현재 시간 + 유효기간 에 실행되도록 추가하여 문제를 해결했습니다.

 

최종 구조

 

Comments