Refresh Token 구현 방식 고도화
Random Hex String에서 JWT + Metadata JSON으로
요약
- 기존: Random Hex String 방식
-
변경: JWT + Metadata JSON 방식
- 이전에는 특정 토큰을 찾기 위해 Redis를 순회해야 할 수 있어 조회 비용이
O(N)에 가까워질 수 있었습니다. - 현재는 JWT 내부의 사용자 정보와 디바이스 정보를 활용해
O(1)수준의 직접 조회가 가능해졌습니다. - 성능뿐 아니라 운영 편의성과 보안 검증 수준도 함께 개선되었습니다.
버전별 상세 비교
| 항목 | 이전 버전 (Legacy) | 현재 버전 (Modern) | 비고 |
|---|---|---|---|
| 토큰 형태 | 64 bytes Random Hex | JWT (JSON Web Token) | 정보 포함형 |
| 조회 성능 | O(N) (Redis Full Scan) |
O(1) (Direct Access) |
90% 이상 향상 |
| 저장 구조 | String (토큰만 저장) | JSON (메타데이터 포함) | 가시성 확보 |
| 보안 체계 | Redis TTL 의존 | 서명 + 만료 + 타입 검증 | 다중 보안 계층 |
| 디버깅 | 불가능에 가까움 | 매우 용이 | 사용자 정보 포함 |
주요 개선 사항
성능 최적화: O(N) -> O(1)
- 이전에는 특정 디바이스 토큰을 찾기 위해
KEYS혹은SCAN으로 Redis 전체를 탐색해야 했습니다. - 현재는 JWT 내부
sub(userId)를 즉시 추출한 뒤refresh_token:{userId}:{deviceId}키로 직접 접근합니다. - Full Scan을 제거하면서 조회 비용을 단건 조회 수준으로 줄일 수 있었습니다.
운영 편의성 및 데이터 가시성
- 이전에는 Redis에 토큰 문자열만 저장되어 있어 어떤 사용자의 어떤 디바이스 토큰인지 바로 파악하기 어려웠습니다.
- 현재는 메타데이터 JSON을 함께 저장해 Redis GUI에서도 사용자 이메일, 생성일, 디바이스 정보를 즉시 확인할 수 있습니다.
저장 예시는 다음과 같습니다.
{
"token": "eyJhbGci...",
"userId": "user-123",
"email": "user@example.com",
"deviceId": "device-456",
"createdAt": 1709123456789
}
보안 로직 고도화
- 서명 검증: 위변조된 토큰은 Redis를 조회하기도 전에 서버에서 차단합니다.
- 만료 검증: JWT의 만료 정보를 기준으로 유효성을 검사합니다.
- 타입 검증: Payload 내
type: "refresh"를 확인해 Access Token 오용을 방지합니다.
예상 성능 지표
- 검증 속도: 기존 약
100ms-> 변경 후5ms미만 - Redis 부하: Full Scan 제거로 CPU 사용량 감소
- 트레이드오프: 저장 공간은 약간 늘어나지만 성능 이득이 더 큼
핵심 코드 변경 포인트
- 생성:
randomBytes(64)대신jwtService.sign(payload)사용 - 검증:
keys()패턴 매칭 루프 제거 - 검증 흐름:
jwtService.verify()후redis.get()단건 처리
정리
- 이번 변경의 핵심은 단순히 Refresh Token의 포맷만 바꾼 것이 아닙니다.
-
찾기 어려운 토큰을 검증 가능하고 식별 가능한 토큰으로 바꾼 것이 핵심입니다.
- 기존: Random Hex String + 단순 저장
- 변경: JWT + Metadata JSON
- 효과:
O(N)탐색 제거,O(1)조회 구조 확보 - 추가 이점: 보안 검증 강화, 운영 가시성 향상, 디버깅 편의성 개선