지난 포스트에 이어, redis를 사용한 로그아웃 기능을 구현해보자.
로그아웃에 Redis 사용하기
Spring Security + JWT + Redis
Spring Security와 Redis를 사용해서 로그아웃 기능을 구현해보자.
왜 Redis를 사용하는가?
jwt는 보통 무상태(stateless)이다. 따라서 로그아웃을 해도 jwt는 만료되기 전까지 유효하므로, 서버에서 별도로 관리해야한다.
서버가 토큰 내용을 변경할 수 없어, jwt 만료 시간을 당길 수도 없기 때문에
redis를 사용하여 블랙리스트에 저장하는 방식으로 구현할 것이다.
빠른 처리 속도, 자원 낭비 최소화 등의 이유로 Redis를 쓴다.
로그아웃 요청 헤더에 포함된 accessToken을 Redis에 저장하고
해당 토큰이 만료되기 전까지 사용 불가능 상태로 저장해준다.
UserService
public void logout(String accessToken, String refreshToken){
try {
jwtTokenProvider.invalidateToken(accessToken);
} catch (Exception e) {
throw new IllegalArgumentException("Access Token 무효화 중 오류 발생: " + e.getMessage());
}
boolean deleted = jwtTokenProvider.deleteRefreshToken(refreshToken);
if (!deleted) {
// Refresh Token이 존재하지 않거나 이미 삭제된 경우
throw new IllegalArgumentException("유효하지 않거나 이미 만료/삭제된 Refresh Token입니다.");
}
}
우선 JwtTokenProvider의 invalidateToken 메서드를 호출해 엑세스 토큰을 무효화한다.
public void invalidateToken(String token) {
Date expiration = getExpirationDateFromToken(token);
if (expiration == null) {
throw new IllegalArgumentException("Access Token의 만료 시간을 확인할 수 없습니다.");
}
long remainingTime = expiration.getTime() - System.currentTimeMillis();
if (remainingTime <= 0) {
throw new IllegalArgumentException("유효하지 않거나 이미 만료된 Access Token입니다.");
} else{
redisTemplate.opsForValue().set("blacklist:" + token, "invalidated", remainingTime, TimeUnit.MILLISECONDS);
}
}
getExpirationDateFromToken 에서 만료 시간을 추출하고 잔여 시간을 밀리초 단위로 변환한다.
잔여 시간이 유효하다면 "blacklist: <token>" = " invalidated" 형식으로 블랙리스트에 저장된다 (key=value)
(TTL에는 잔여 시간)
다시 서비스로 돌아가서, 이제 리프레시 토큰을 삭제할 차례다.
public boolean deleteRefreshToken(String refreshToken) {
try {
String userId = getUsername(refreshToken);
// Redis에서 해당 userId에 연결된 Refresh Token 키 삭제
// Redis에 저장할 때 사용했던 키 패턴("refreshToken:{userId}")을 동일하게 사용
Boolean deleted = redisTemplate.delete("refreshToken:" + userId);
return Boolean.TRUE.equals(deleted); // null 체크를 위해 Boolean.TRUE.equals() 사용
} catch (JwtException | IllegalArgumentException e) {
System.err.println("유효하지 않은 Refresh Token: " + e.getMessage());
return false;
} catch (Exception e) {
System.err.println("Refresh Token 삭제 중 오류 발생: " + e.getMessage());
return false;
}
}
나는 토큰을 생성할 때 username 대신 userId를 사용했기 때문에 refreshToken에서 userId를 추출했다.
이제 redis에서 추출한 userId에 연결된 refreshToken 키를 삭제한다.
컨트롤러에서 서비스 로직을 호출하고 테스트 해보자.
Postman 테스트 결과

Header의 Authorization에 Bearer <Token>으로 엑세스 토큰을 넣고 요청 body에 리프레시 토큰을 넣고 POST 요청을 보내면 다음과 같은 결과를 얻을 수 있다. T/F 형식으로 값을 리턴했기 때문에 200과 함께 성공 메세지가 뜨는 것을 볼 수 있다.

감사합니다 (. ❛ ᴗ ❛.)
'Development > Back-end' 카테고리의 다른 글
| [JWT] JWT 서명 알고리즘 (1) - HMAC, RSA (0) | 2025.08.18 |
|---|---|
| [Spring Boot] @Scheduled 사용하기 (0) | 2025.08.10 |
| [Spring Boot] Pagination(페이지네이션) (0) | 2025.07.15 |
| [Socket.io] Socket.io 에 대하여 (0) | 2025.06.07 |
| [Express] 좋아요 기능 구현하기 (0) | 2025.06.06 |
