문제 상황
오늘은 JWT 로그인 유지 흐름을 확장하면서 RefreshTokenService를 만들었다.
처음에는 Refresh Token을 생성하는 것까지만 생각했는데, 막상 구현하려고 보니 한 가지가 더 필요했다.
Refresh Token을 발급만 하면 끝나는 것이 아니라, 서버가 이 토큰을 저장하고 조회하고 삭제할 수 있어야 했다.
그래서 RefreshTokenService를 따로 만들었다.
RefreshTokenService
RefreshTokenService는 Refresh Token을 Redis에 저장하고 관리하는 역할을 한다.
로그인에 성공하면 서버는 Refresh Token을 생성하고, 이 값을 Redis에 저장한다.
예를 들어 다음과 같은 형태이다.
key: refresh:{loginId}
value: refreshToken
TTL: 14일
TTL은 Redis에 저장된 데이터의 유효기간이다.
Refresh Token은 만료 시간과 Redis 저장 시간도 맞춰야 하기 때문에, TTL을 함께 설정한다.
이렇게 저장해두면 이후에 Access Token이 만료됐을 때,
재발급 요청에서 Redis에 저장된 Refresh Token과 요청으로 들어온 Refresh Token을 비교할 수 있다.
코드
@RequiredArgsConstructor
@Service
public class RefreshTokenService {
private static final String REFRESH_TOKEN_PREFIX = "refresh:";
private final StringRedisTemplate stringRedisTemplate;
public void save(String loginId, String refreshToken, long refreshTokenValidityMs) {
String key = createKey(loginId);
stringRedisTemplate.opsForValue().set(
key,
refreshToken,
Duration.ofMillis(refreshTokenValidityMs)
);
}
public String get(String loginId) {
String key = createKey(loginId);
return stringRedisTemplate.opsForValue().get(key);
}
public void delete(String loginId) {
stringRedisTemplate.delete(createKey(loginId));
}
private String createKey(String loginId) {
return REFRESH_TOKEN_PREFIX + loginId;
}
}
StringRedisTemplate
위 코드의 핵심은 StringRedisTemplate이다.
StringRedisTemplate은 Spring에서 Redis에 문자열 데이터를 저장하고 조회할 때 사용한다.
Redis는 key-value 형태로 데이터를 저장하는데, Refresh Token도 문자열이기 때문에 StringRedisTemplate을 사용한다.
StringRedisTemplate.opsForValue().set()
opsForValue()는 Redis의 String 타입 데이터를 다루겠다는 의미이다.
set()은 Redis에 값을 저장하는 메서드이고,
key에는 refresh:{loginId} 같은 저장 이름이
refreshToken은 실제 저장할 값이,
Duration.ofMillis(refreshTokenValidityMs)는 데이터가 Redis에서 유지될 시간이 저장된다
즉, 이 코드는 Refresh Token을 Redis에 저장하면서 만료 시간까지 함께 설정하는 코드이다.
정리
이번 작업을 통해 RefreshTokenService가 Redis에 Refresh Token을 저장하고 관리하는 역할이라는 것을 이해했다.
특히 StringRedisTemplate과 opsForValue().set()을 통해 key-value 형태로 값을 저장하고 TTL을 설정하는 흐름을 확인했다. Refresh Token 관리는 생성뿐만 아니라 저장, 조회, 삭제까지 함께 봐야 한다는 점을 배웠다.