
JWT를 사용해서 로그인기능을 구현할 때, 보통은 RefreshToken도 함께 발급을 받는다.
그 이유는 간단히 말하면, AccessToken은 탈취 당할 위험이 있으니까!
좀 더 자세하게 말해보면
액세스 토큰은 일반적으로 클라이언트의 메모리나 로컬 스토리지, 쿠키에 저장된다.
이 저장소가 외부 공격자에 의해 침해될 경우 토큰이 쉽게 탈취될 수 있는거다.
외부 공격자는 아래 두가지 공격으로 해당 토큰을 탈취한다.
- XSS(크로스 사이트 스크립팅): 악성 스크립트를 통해 브라우저의 로컬 스토리지에 접근하여 토큰을 탈취
- CSRF(크로스 사이트 요청 위조): CSRF 공격을 통해 쿠키에 저장된 토큰을 이용해 인증된 요청을 위장
또는, 네트워크로 전송되는 과정에서 탈취가 될 수도 있는데
- HTTPS가 아닌 HTTP를 통해 토큰이 전송되면 패킷 스니핑 공격으로 토큰이 노출
- MITM(중간자 공격)으로 토큰 전송 데이터를 가로채는 시나리오도 있음
그렇기때문에 탈취 위험이 있는 accessToken은 짧게 두고
refreshToken이라는걸로 해당 토큰이 안전한지에 대한 유효성 검사 및 accessToken이 만료되었을때, 이를 기준으로 재발급을 진행하는거다.
그럼 해당 토큰을 왜? Redis에 저장하는가?

그 이유를 알기위해, 인메모리 db인 레디스의 특성을 알 필요가 있다.
- Redis는 데이터를 디스크가 아닌 메모리(RAM)에 저장한다.
- 메모리 접근 속도는 디스크에 비해 월등히 빠르며, 이는 대부분의 조회 요청이 마이크로초 단위로 처리되는 주요 이유이다.
- Redis는 단일 스레드로 동작하며, 멀티스레드 기반 DB와 달리 컨텍스트 스위칭(스레드 간 작업 교환)으로 인한 오버헤드가 없다.
- 이는 Redis가 높은 처리량을 유지하는 데 기여한다.
- Redis는 단순한 키-값 저장소뿐만 아니라, 리스트, 해시, 셋, 정렬된 셋 등 다양한 데이터 구조를 지원한다.
public void saveToken(String key, String value, long duration) {
redisTemplate.opsForValue().set(key, value, duration, TimeUnit.MILLISECONDS);
}
- 이들 데이터 구조는 특정 조회 및 업데이트 작업에 최적화되어 있다.
일반 로컬에 저장하는것과 redis에 저장했을 때의 속도 비교

이처럼 Redis는 대부분의 연산에서 MySQL/PostgreSQL보다 약 10~100배 빠른걸 확인할 수 있다.
아래 코드들은 레디스에서 토큰을 관리하는 코드이다.
package com.swyp.playground.common.redis;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class RedisService {
private final StringRedisTemplate redisTemplate;
public RedisService(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
// 토큰 저장
public void saveToken(String key, String value, long duration) {
redisTemplate.opsForValue().set(key, value, duration, TimeUnit.MILLISECONDS);
}
public void saveRefreshToken(String email, String refreshToken, long duration) {
String refreshKey = "refresh_" + email;
redisTemplate.opsForValue().set(refreshKey, refreshToken, duration, TimeUnit.MILLISECONDS);
}
// 토큰 조회
public String getToken(String key) {
return redisTemplate.opsForValue().get(key);
}
// 토큰 삭제
public void deleteToken(String key) {
redisTemplate.delete(key);
}
public void deleteRefreshToken(String email) {
String refreshKey = "refresh_" + email;
redisTemplate.delete(refreshKey);
}
// 토큰 검증
public boolean isTokenValid(String key) {
return redisTemplate.hasKey(key);
}
}
실제 유저가 로그인을 하게 되면

유저의 이메일과함께
액세스토큰이 출력되는걸 확인할 수 있다.
'SpringBoot' 카테고리의 다른 글
| 분산 이벤트 스트리밍 플랫폼, Kafka 아는척해보기 (3) | 2025.08.17 |
|---|---|
| Swagger를 사용해서 쉽게 api명세서를 만들어보자! (2) | 2024.12.01 |
| Springboot에서 SMTP(google)를 통해 인증번호를 보내보자! (1) | 2024.11.29 |
| TypeChange를 통해 Service코드를 간결하게 해보자! (0) | 2024.11.25 |
| Springboot + MySQL 연결하기 (2) | 2024.11.17 |