지난 글에서는 JWT의 기본에 이어, 카카오 API를 활용한 OAuth2 로그인 구현 방법까지 알아보았습니다.
지금까지의 구현으로도 훌륭한 인증 시스템을 갖추었지만, JWT의 단점인 **'상태 비저장(Stateless)으로 인한 토큰 탈취 위험'**을 해결해야 완벽한 서비스 운영이 가능합니다. Access Token의 유효기간을 짧게 하고 Refresh Token을 도입하는 것이 일반적이지만, 만약 해커가 Refresh Token마저 탈취한다면 어떻게 될까요?
이번 글에서는 이 문제를 해결하기 위해 Redis를 활용하여 Refresh Token을 안전하게 관리하고, RTR(Refresh Token Rotation) 기법을 적용하는 방법을 알아보겠습니다.
1. 왜 Refresh Token 관리에 Redis를 사용할까?
Refresh Token을 DB에 저장하고 검증할 수도 있지만, 실무에서는 압도적으로 Redis를 선호합니다. 그 이유는 다음과 같습니다.
- In-Memory 기반의 빠른 속도: 토큰 재발급은 빈번하게 일어나는 작업입니다. RDBMS(Disk 기반)에 매번 I/O를 발생시키는 것보다, 메모리 기반인 Redis를 사용하는 것이 서버 부하를 줄이고 응답 속도를 극대화할 수 있습니다.
- 자체적인 TTL(Time To Live) 지원: Refresh Token은 만료 시간이 필수입니다. RDBMS는 만료된 토큰을 지우기 위해 별도의 스케줄러(Batch)를 돌려야 하지만, Redis는 데이터 저장 시 TTL을 설정하면 시간이 지남에 따라 알아서 데이터를 삭제해 줍니다.
2. RTR (Refresh Token Rotation) 이란?
RTR은 단어 뜻 그대로 Refresh Token을 계속 회전(교체)시키는 기법입니다.
기존에는 Access Token이 만료되면 동일한 Refresh Token으로 Access Token만 새로 발급받았습니다. 하지만 RTR을 적용하면, Access Token을 재발급받을 때마다 Refresh Token도 새로 발급하여 이전 Refresh Token은 즉시 폐기(1회용으로 사용)합니다.
이 방식을 사용하면 만약 해커가 Refresh Token을 탈취하더라도, 정상적인 사용자가 이미 해당 토큰을 사용해 버렸다면 해커의 토큰은 무효화됩니다. 반대로 해커가 먼저 사용해 버렸다면, 정상적인 사용자가 접근할 때 토큰이 만료/삭제된 상태이므로 시스템이 이를 감지하고 모든 기기에서 로그아웃 시키는 등의 강력한 보안 조치를 취할 수 있습니다.
3. 환경 세팅 (Redis & Spring Boot)
개발 환경이나 운영 중인 Linux 서버의 Docker 환경에서 손쉽게 Redis를 구성할 수 있습니다.
3.1. Redis Docker Compose 설정
3.2. Spring Boot 의존성 추가 (build.gradle)
3.3. application.yml 설정
4. 본격적인 구현
Spring Data Redis가 제공하는 @RedisHash를 사용하면 JPA와 매우 유사한 형태로 직관적인 개발이 가능합니다.
Step 1. Redis 엔티티 (Domain) 생성
Step 2. Redis Repository 생성
Step 3. 토큰 재발급 로직 구현 (RTR 적용)
이제 클라이언트가 토큰 갱신을 요청할 때 처리할 서비스 로직입니다. 핵심은 '조회 -> 검증 -> 삭제 -> 재발급 -> 저장' 의 사이클입니다.
5. 마무리
이렇게 Spring Boot와 Redis를 활용하여 Refresh Token을 관리하고 RTR 기법까지 적용해 보았습니다. 이를 통해 토큰 기반 인증의 한계였던 탈취 위험을 크게 낮추고, 사용자에게는 끊김 없는 로그인 경험을 제공하면서도 보안은 챙길 수 있게 되었습니다.