JWT AccessToken / RefreshToken 재발급 구조 ( RTR 기법 )
JWT 기반 인증을 사용하다 보면 반드시 마주치는 문제가 있다. 바로 AccessToken 만료 이후 인증을 어떻게 유지할 것인가이다.
JWT AccessToken / RefreshToken 재발급 구조 ( RTR 기법 )
JWT 기반 인증을 사용하다 보면 반드시 마주치는 문제가 있다. 바로 AccessToken 만료 이후 인증을 어떻게 유지할 것인가이다.
이 글에서는
- 세션 / 쿠키 대신 JWT을 사용한 이유
- AccessToken / RefreshToken의 역할
- RefreshToken을 활용한 재발급 구조
- RefreshToken Rotation 개념
- Spring 기반 구현 흐름 의 순서로 정리한다.
왜 세션 / 쿠키 대신 토큰(JWT)을 사용했는가?
전통적인 웹 애플리케이션에서 서버가 세션을 생성하고, 클라이언트는 세션ID를 쿠키로 전달하는 세션 기반 인증을 많이 사용한다.
하지만 서비스 구조가 다음과 같다면 한계가 생긴다.
- 서버가 여러 대로 확장되는 환경
- API 서버와 프론트엔드가 분리된 구조
- 모바일 앱, 외부 클라이언트 등 다양한 접근 주체 존재
이런 환경에서 세션 기반 인증은
- 세션 공유 비용 발생
- 서버 상태를 유지해야 함
- 확장성과 관리 복잡도 증가
라는 단점이 있다.
JWT 기반 토큰 인증은 다음과 같은 이유로 선택했다.
- 서버가 인증 상태를 저장하지 않는 Stateless 구조
- 수평 확장에 유리
- API 서버 중심 구조에 적합
- 모바일 / 웹 / 외부 클라이언트 공통 적용 가능
다만, 토큰 기반 인증은 만료·재발급·탈취 대응 전략까지 함께 설계하지 않으면 오히려 보안에 취약해질 수 있다. 이 지점에서 AccessToken과 RefreshToken이 등장한다.
AccessToken과 RefreshToken의 역할
JWT 인증 구조에서는 일반적으로 두 종류의 토큰을 사용한다.
AccessToken - 실제 API 요청 시 사용되는 인증 토큰
- 유효 기간이 짧음
- 탈취되더라도 피해를 제한하기 위함
RefreshToken
- AccessToken이 만료됐을 때, 새로운 토큰을 발급받기 위한 토큰
- AccessToken보다 유효 기간이 김
- 보안적으로 더 민감함
핵심은 AccessToken은 자주 교체하고, RefreshToken은 최대한 안전하게 관리하는 것이다.
왜 AccessToken만 사용하지 않는가?
AccessToken의 유효 기간을 길게 가져가면 사용자는 편하지만, 토큰 탈취 시 위험이 커진다.
반대로 유효 기간을 짧게 가져가면 보안은 강화되지만, 사용자는 자주 로그아웃 된다.
이 트레이드오프를 해결하기 위한 구조가 AccessToken + RefreshToken 조합이다.
RefreshToken 재발급 기본 흐름
- 사용자가 로그인
- 서버가 AccessToken + RefreshToken 발급
- AccessToken 만료
- 클라이언트가 RefreshToken으로 재발급 요청
- 서버가 RefreshToken 검증 후 새로운 토큰 발급
이 방식은 인증 유지 측면에서는 효과적이지만, RefreshToken 탈취 시 재사용 위험이라는 문제가 있다.
문제점: RefreshToken 탈취와 재사용 공격
RefreshToken이 한 번 탈취 되면
- 공격자는 AccessToken을 반복적으로 재발급받을 수 있다.
- 서버는 정상 사용자와 공격자를 구분하기 어렵다
이를 보완하기 위해 사용한 전략이 RefreshToken Rotation (RTR) 이다.
RefreshToken Rotation 이란?
RefreshToken Rotation은 재발급 요청 시마다 RefreshToken을 새로 발급하고, 기존 토큰을 즉시 무효화하는 방식이다.
RefreshToken Rotation 흐름
- 로그인 -> AccessToken + RefreshToken 발급
- RefreshToken으로 재발급 요청
- 서버가 RefreshToken 검증
- 새로운 AccessToken + 새로운 RefreshToken 발급
- 기존 RefreshToken 폐기
이렇게 하면 탈취된 RefreshToken은 한 번 사용되면 더 이상 사용할 수 없다.
Spring 기반 RefreshToken 재발급 구현 흐름
아래는 재발급 로직의 핵심 흐름이다.
- RefreshToken 유효성 검증
- 만료 여부 확인
- 위조 변조 여부 확인
public void validateToken(String refreshToken){
try{
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(refreshToken);
} catch(ExpiredJwtException e) {
throw new ExpiredJwt("만료된 토큰 입니다.");
} catch(JwtException e) {
throw new TokenNotFound("존재 하지 않은 토큰 입니다.");
}
}
- 서버에 저장된 RefreshToken과 비교
- Redis에 저장된 RefreshToken과 비교
- 불일치 시 탈취 또는 중복 사용으로 판단
if (!storedRefreshToken.equals(refreshToken)) {
throw new InvalidTokenException("유효하지 않은 토큰 입니다.");
}
- 새로운 토큰 생성
String newAccessToken = tokenProvider.createAccessToken(memberId);
String newRefreshToken = tokenProvider.createRefreshToken(memberId);
- RefreshToken 교체 저장
saveRefreshToken(memberId, newRefreshToken);
JWT의 핵심 포인트
- RefreshToken은 재사용 불가
- 재발급 시 마다 기존 토큰 폐기
- 탈취된 토큰의 피해 범위를 최소화
마무리
JWT 인증은 단순히 토큰을 쓴다는 선택이 아니다. 상태 관리 방식, 확장성, 보안 전략을 함께 결정하는 설계 문제다
RefreshToken과 Rotation 전략을 통해 인증을 유지하면서 토큰 탈취로 인한 위험은 줄일 수 있다.
메타데이터
- post_id
- ccb810bd9491
- slug
- jwt-accesstoken-refreshtoken-재발급-구조-rtr-기법-ccb810bd9491
- url
- https://medium.com/@inwns00/jwt-accesstoken-refreshtoken-%EC%9E%AC%EB%B0%9C%EA%B8%89-%EA%B5%AC%EC%A1%B0-rtr-%EA%B8%B0%EB%B2%95-ccb810bd9491
- canonical_url
- https://medium.com/@inwns00/jwt-accesstoken-refreshtoken-%EC%9E%AC%EB%B0%9C%EA%B8%89-%EA%B5%AC%EC%A1%B0-rtr-%EA%B8%B0%EB%B2%95-ccb810bd9491
- author_url
- https://medium.com/@inwns00
- status
- ok
- fetched_at
- 2026-06-16 19:09:56