💡 오늘 학습 키워드
- Refresh Token
🎯 학습 내용 정리
JWT 기반 인증을 처음 구현하면 Access Token 하나만으로 인증을 처리해도 충분해 보인다. 하지만 Access Token의 유효 시간을 길게 설정하면 토큰이 탈취되었을 때 장기간 악용될 수 있고, 반대로 유효 시간을 너무 짧게 설정하면 사용자는 자주 로그인을 다시 해야 하는 불편함을 겪게 된다.
이러한 문제를 해결하기 위해 등장한 방식이 Access Token과 Refresh Token을 함께 사용하는 구조이다.
Access Token은 짧은 수명을 가지며 API 인증에 사용되고, Refresh Token은 긴 수명을 가지며 새로운 Access Token을 발급받는 용도로만 사용된다.
왜 Refresh Token이 필요할까?
Access Token만 사용하는 경우 다음과 같은 문제가 발생한다.
Access Token의 만료 시간이 긴 경우
- 사용자는 다시 로그인할 필요가 없다.
- 하지만 토큰이 탈취되면 오랫동안 악용될 수 있다.
Access Token의 만료 시간이 짧은 경우
- 탈취 피해를 줄일 수 있다.
- 하지만 사용자는 자주 로그인해야 한다.
Refresh Token은 이러한 두 문제를 동시에 해결하기 위한 방법이다.
두 토큰은 역할이 다르다.
| 구분 | Access Token | Refresh Token |
| 목적 | API 인증 | Access Token 재발급 |
| 사용 빈도 | 모든 요청 | 만료 시에만 |
| 만료 시간 | 짧음 | 김 |
| 노출 가능성 | 높음 | 낮음 |
Refresh Token은 API 인증에 사용해서는 안 된다.
오직 새로운 Access Token을 발급받기 위한 용도로만 사용한다.
사용자가 로그인에 성공하면 서버는 두 개의 토큰을 생성한다.
Login
↓
AuthenticationManager
↓
인증 성공
↓
Access Token 생성
↓
Refresh Token 생성
↓
Response
예를 들어 다음과 같이 구현할 수 있다.
Authentication authentication =
authenticationManager.authenticate(loginToken);
String accessToken =
jwtProvider.createAccessToken(authentication);
String refreshToken =
jwtProvider.createRefreshToken(authentication);
만약 Access Token이 만료되면 API 요청은 실패한다.
Client
↓
GET /users/me
↓
401 Unauthorized
클라이언트는 Refresh Token을 이용하여 재발급 API를 호출한다.
POST /auth/reissue
이때 Access Token은 사용하지 않는다.
Refresh Token만 사용한다.
Refresh Token으로 재발급하는 과정은 다음 순서로 이루어진다.
Refresh Token
↓
JWT 검증
↓
사용자 확인
↓
새 Access Token 생성
↓
Response
예시 코드이다.
public TokenResponse reissue(String refreshToken) {
jwtProvider.validateToken(refreshToken);
Long userId =
jwtProvider.getUserId(refreshToken);
String accessToken =
jwtProvider.createAccessToken(userId);
return new TokenResponse(accessToken);
}
Refresh Token은 어디에 저장할까?
대표적인 저장 방식은 세 가지이다.
| 저장 위치 | 장점 | 단점 |
| LocalStorage | 구현이 간단 | XSS에 취약 |
| HttpOnly Cookie | JavaScript 접근 불가 | CSRF 고려 필요 |
| DB / Redis | 서버에서 폐기 가능 | 저장소 관리 필요 |
실무에서는 다음 구조를 가장 많이 사용한다.
- Access Token → Authorization Header
- Refresh Token → HttpOnly Cookie
왜 Refresh Token을 저장소에 보관할까?
JWT는 기본적으로 Stateless이다.
한 번 발급하면 서버는 토큰을 기억하지 않는다.
하지만 Refresh Token을 저장소(DB 또는 Redis)에 보관하면 다음 기능을 구현할 수 있다.
- 로그아웃
- 강제 만료
- 중복 로그인 관리
- Token Rotation
예를 들어
userId
↓
Refresh Token
↓
Redis
형태로 저장할 수 있다.
실무에서는 Refresh Token을 계속 재사용하지 않는다. 재발급이 성공하면 새로운 Refresh Token도 함께 발급한다. 기존 Refresh Token은 즉시 폐기한다.
Refresh Token A
↓
재발급
↓
Access Token B
+
Refresh Token B
↓
Refresh Token A 삭제
이를 Refresh Token Rotation이라고 한다.
Refresh Token 탈취를 어떻게 막을까?
예를 들어 공격자가 Refresh Token을 탈취했다고 가정한다.
Rotation을 사용하지 않으면
Refresh Token
↓
무한 재발급 가능
Rotation을 사용하면
Refresh Token
↓
한 번 사용
↓
삭제
↓
재사용 실패
기존 Refresh Token으로 다시 요청하면 서버는 이를 거부할 수 있다.
로그아웃은 어떻게 처리할까?
로그아웃 시에는 Refresh Token을 저장소에서 삭제한다.
refreshTokenRepository.deleteByUserId(userId);
브라우저의 Cookie도 함께 제거한다.
Cookie cookie =
new Cookie("refreshToken", "");
cookie.setMaxAge(0);
이후에는 재발급이 불가능하다.
SecurityConfig와는 어떤 관계가 있을까?
Refresh Token은 일반적으로 재발급 API만 허용한다.
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/login").permitAll()
.requestMatchers("/auth/reissue").permitAll()
.anyRequest().authenticated()
);
재발급 API는 Access Token 없이도 호출할 수 있어야 하기 때문이다.
다만 Refresh Token 자체는 반드시 서버에서 검증해야 한다.
실무에서 자주 하는 실수에 대해서 알아보자.
1. Refresh Token으로 API 인증을 수행하는 경우
Refresh Token은 API 인증용이 아니다.
API 인증에는 항상 Access Token을 사용해야 한다.
2. Rotation 없이 동일한 Refresh Token을 계속 사용하는 경우
Refresh Token이 탈취되면 만료 시점까지 계속 재사용될 수 있다.
가능하다면 Rotation을 적용하는 것이 안전하다.
3. 로그아웃 시 Cookie만 삭제하는 경우
브라우저에서 Cookie만 삭제하면 서버는 해당 Refresh Token의 존재를 알지 못한다.
DB 또는 Redis에 저장하는 구조라면 서버 저장소에서도 함께 삭제해야 한다.
4. Refresh Token을 LocalStorage에 저장하는 경우
Refresh Token은 장기간 유지되는 토큰이므로 XSS 공격의 영향을 최소화할 수 있는 HttpOnly Cookie에 저장하는 것이 일반적이다.
5. 재발급 시 기존 Refresh Token을 그대로 유지하는 경우
Rotation을 적용하지 않으면 동일한 Refresh Token이 반복적으로 사용될 수 있다. 재발급 성공 시 새로운 Refresh Token을 발급하고 기존 토큰을 폐기하는 것이 보안 측면에서 유리하다.
전체 흐름을 정리해보면 다음과 같다.
Login
↓
Access Token 발급
↓
Refresh Token 발급
↓
API 요청
↓
Access Token 만료
↓
401
↓
Refresh API
↓
Refresh Token 검증
↓
새 Access Token
↓
(선택)
새 Refresh Token
↓
기존 Refresh Token 삭제
Access Token과 Refresh Token을 함께 사용하는 구조는 보안성과 사용자 경험을 모두 고려한 인증 전략이다. Access Token은 짧은 수명을 통해 토큰 탈취 위험을 줄이고, Refresh Token은 새로운 Access Token을 발급하는 역할만 수행하여 사용자가 반복해서 로그인하지 않아도 되도록 한다.
또한 Refresh Token을 저장소에 관리하면 로그아웃, 강제 만료, 중복 로그인 제어와 같은 기능을 구현할 수 있으며, Refresh Token Rotation을 적용하면 탈취된 토큰의 재사용 위험도 줄일 수 있다. 따라서 실무에서는 단순히 토큰을 발급하는 것을 넘어, 저장 방식과 재발급 전략까지 함께 설계하는 것이 중요하다.
📚 한줄 정리
Refresh Token은 로그인을 연장하기 위한 토큰이 아니라, 짧은 수명의 Access Token을 안전하게 재발급하기 위한 장기 자격 증명이며, Rotation과 서버 저장소를 함께 활용할 때 가장 안전한 인증 구조를 구성할 수 있다.
'Bootcamp > Implementation' 카테고리의 다른 글
| Spring Security에서 흔히 발생하는 문제와 해결 사례 (0) | 2026.07.17 |
|---|---|
| Spring Security 전체 과정 흐름 추적 (0) | 2026.07.16 |
| CORS와 CSRF (0) | 2026.07.14 |
| AuthenticationEntryPoint와 AccessDeniedHandler 예외 처리 (0) | 2026.07.13 |
| AuthenticationManager와 AuthenticationProvider (0) | 2026.07.10 |