앞선 글에서는 JWT 기반 인증 구조 위에 세션 통제와 Redis 검증 캐시를 얹어, 성능과 보안을 함께 가져가는 구조를 다뤘습니다. 이제 실제 운영 단계에서 반드시 부딪히는 질문이 남습니다.
“access token은 얼마나 짧게 가져가야 할까?” “refresh token은 언제, 어떤 기준으로 회전해야 할까?” “보안을 강화하면 사용자 경험이 나빠지는 건 피할 수 없는 걸까?”
토큰 수명 설계는 단순한 숫자 선택 문제가 아닙니다. 잘못 설계하면 보안 사고로 이어지고, 반대로 과도하면 로그인 풀림, 재인증 루프 같은 운영 이슈가 쌓입니다.
다음을 실무 기준으로 정리합니다.
- access token 만료 시간을 결정하는 현실적인 기준
- refresh token 회전(rotation)을 왜, 어떻게 적용해야 하는지
- 보안 강화와 사용자 경험 사이에서 균형을 잡는 방법
- 운영에서 실제로 자주 발생하는 문제와 대응 포인트
개념/배경 설명: 토큰 수명은 왜 중요한가
access token과 refresh token은 역할이 다릅니다. 하지만 실무에서는 종종 “둘 다 그냥 토큰”으로 취급되면서 문제가 시작됩니다.
- access token: 짧은 수명, 요청 인증용
- refresh token: 긴 수명, access token 재발급용
access token이 유출되면 만료될 때까지만 악용됩니다. 하지만 refresh token이 유출되면, 새로운 access token을 계속 발급받을 수 있습니다. 즉, refresh token이 실제 보안의 핵심입니다.
잘못 설계하면 생기는 문제는 명확합니다.
- access token이 너무 길면, 탈취 시 피해 범위가 커진다
- refresh token 회전이 없으면, 유출을 감지할 수 없다
- 둘 다 짧으면, 사용자는 계속 로그인을 다시 해야 한다
결국 핵심은 “각 토큰이 감당해야 할 책임만 정확히 지운다”는 것입니다.
설계 1: access token 만료 시간 결정 기준
실무에서 access token 만료 시간을 정할 때 가장 많이 듣는 질문은 “보통 몇 분으로 하나요?”입니다. 하지만 정답은 숫자가 아니라 기준입니다.
access token 만료 시간은 다음 요소의 영향을 받습니다.
- 토큰 탈취 가능성(IP 노출, 브라우저 저장 위치 등)
- API 호출 빈도
- 세션 revoke를 서버에서 즉시 통제할 수 있는지 여부
세션 상태를 서버가 통제하고, Redis나 DB fallback으로 즉시 revoke가 가능한 구조라면 access token은 비교적 짧게 가져가도 부담이 줄어듭니다.
실무에서 많이 사용하는 범위는 다음과 같습니다.
- 웹 서비스: 5~15분
- 모바일 앱: 10~30분
여기서 중요한 점은, access token 만료는 “로그아웃”이 아니라는 것입니다. 만료는 단지 refresh를 한 번 더 하게 만드는 신호일 뿐입니다.
실무 포인트 정리
- access token은 짧을수록 안전하지만, 세션 통제가 전제다
- 만료는 UX 문제보다 refresh 안정성이 더 중요하다
- 숫자보다 구조가 먼저다
설계 2: refresh token 회전(rotation)의 목적
refresh token 회전의 목적은 단 하나입니다. “탈취를 감지하기 위함”입니다.
회전이 없는 구조에서는, refresh token이 몇 번 사용되었는지 서버는 알 수 없습니다. 정상 사용과 공격을 구분할 수 있는 단서가 사라집니다.
반면 회전 구조에서는 다음이 가능합니다.
- 이전 refresh token은 즉시 폐기
- 이미 폐기된 refresh token 재사용 탐지
- 보안 이벤트 로깅 및 단계적 대응
즉, 회전은 보안을 “강화”한다기보다, 보안을 “관측 가능하게” 만듭니다.
실무 포인트 정리
- refresh token은 반드시 1회성으로 취급한다
- 회전 없이는 탈취 탐지가 불가능하다
- 회전은 로깅·대응 전략과 함께 설계해야 한다
설계 3: refresh token 만료 주기 설정
refresh token 만료 시간은 access token보다 훨씬 깁니다. 하지만 “무제한”으로 두는 것은 위험합니다.
실무에서 고려하는 기준은 다음과 같습니다.
- 사용자가 재로그인을 감수할 수 있는 주기
- 장기 미사용 계정의 자동 정리
- 세션 보관 비용(DB/Redis)
일반적인 범위는 다음과 같습니다.
- 웹 서비스: 7~30일
- 모바일 앱(자동 로그인 기대): 30~90일
여기서 중요한 점은, refresh token 만료는 “강제 로그아웃”의 기준이 된다는 것입니다. 즉, 보안 정책과 사용자 경험이 직접 충돌하는 지점입니다.
따라서 팀/서비스 특성에 따라 이 값은 달라질 수 있으며, 정책 변경 시 반드시 공지와 함께 점검이 필요합니다.
코드 예제: TypeScript 기준 토큰 정책 정의
export const TOKEN_POLICY = {
accessToken: {
expiresInSeconds: 15 * 60, // 15분
},
refreshToken: {
expiresInSeconds: 30 * 24 * 60 * 60, // 30일
rotateOnEveryUse: true,
},
};
정책을 코드 상수로 명확히 정의해두면, 운영 중 변경 시 영향 범위를 빠르게 파악할 수 있습니다. 숫자가 여기저기 흩어져 있으면, 정책 수정은 곧 장애로 이어집니다.
운영/실무에서 자주 겪는 문제
1. access token 만료 = 로그아웃으로 오해하는 경우
클라이언트에서 access token 만료를 바로 로그아웃으로 처리하면, 사용자는 이유 없이 튕겨 나간다고 느낍니다. refresh 흐름을 안정적으로 만드는 것이 먼저입니다.
2. refresh 회전 중 경합 문제
동시에 여러 요청이 refresh를 시도하면, 정상 사용자도 재사용 탐지로 오인될 수 있습니다. 이 문제는 정책(단계적 대응)과 클라이언트 구현을 함께 조정해야 합니다.
3. 토큰 정책 변경 시 대량 로그아웃
만료 시간을 줄이거나 회전 정책을 도입하면, 기존 세션이 한 번에 무효화될 수 있습니다. 운영 중 정책 변경은 항상 단계적으로 진행하는 것이 안전합니다.
실무 권장 체크리스트
- access token과 refresh token의 역할이 명확히 구분되어 있는가
- access token 만료가 UX 문제로 직결되지 않는 구조인가
- refresh token 회전을 사용하고 있는가
- refresh 재사용 탐지 시 로깅과 대응 정책이 준비되어 있는가
- 토큰 정책 변경 시 영향 범위를 예측할 수 있는가
토큰 수명 설계의 핵심은 “얼마나 길게”가 아니라 “어디까지를 감당하게 할 것인가”입니다.
access token은 짧고 가볍게, refresh token은 통제와 관측의 중심으로 두면 보안과 사용자 경험을 동시에 만족시키는 구조를 만들 수 있습니다. 결국 좋은 인증 설계는 숫자가 아니라, 책임 분리에서 나옵니다.
