Engineering
로그인 세션 만료 설계: 자동 연장과 보안의 균형
Mike•발행일 2026년 2월 25일•수정일 2026년 2월 27일•읽는 시간 약 2분
💡 Key Takeaways
- 세션 만료 정책은 숫자(30분/60분)보다 사용자 작업 손실을 먼저 기준으로 잡아야 합니다.
- 액세스 토큰 만료와 리프레시 실패를 분리해서 처리하면 대규모 재로그인 사고를 줄일 수 있습니다.
- 만료 직전 저장, 경고 배너, 재시도 흐름을 묶어야 사용자 이탈이 줄어듭니다.
- 장애 시 대응 순서를 런북으로 고정해야 야간에도 일관되게 복구할 수 있습니다.
로그인 세션 만료 설계: 자동 연장과 보안의 균형
세션 만료는 "30분이 맞나 60분이 맞나"로 시작하면 거의 항상 실패합니다. 실제 장애는 만료 시간 자체보다, 만료 순간에 사용자가 하던 일을 잃을 때 터집니다. 제가 현장에서 가장 자주 본 패턴도 이겁니다. 장시간 입력 폼에서 자동 저장이 없고, 토큰 갱신이 한 번 실패하자 바로 로그아웃이 되어 CS가 폭주합니다. 보안은 지켰는데 신뢰를 잃는 케이스입니다.
1) 먼저 정할 것: 시간보다 데이터 손실 허용치
정책 회의에서는 보통 "보안상 짧게"라는 말이 먼저 나오지만, 운영에서 필요한 질문은 다릅니다. "만료 때문에 사용자가 잃어도 되는 작업량이 어느 정도인가"를 먼저 정해야 합니다. 예를 들어 B2B 백오피스라면 5분 이상 입력한 데이터가 날아가면 사실상 장애로 봐야 합니다. 이 기준이 잡히면 토큰 TTL, 자동 연장 여부, 경고 타이밍이 자연스럽게 따라옵니다.
2) 바로 적용 순서
- 액세스 토큰은 짧게(예: 15~30분), 리프레시 토큰은 길게(예: 7~14일) 분리합니다.
- 만료 2~3분 전에 백그라운드 갱신을 시도하고, 실패하면 한 번 더 재시도합니다.
- 재시도도 실패하면 즉시 로그아웃하지 말고 "저장 후 다시 로그인" 배너를 먼저 띄웁니다.
- 사용자가 작성 중이면 임시 저장을 강제한 뒤 로그인 화면으로 보냅니다.
- 재로그인 후 임시 저장 내용을 복원합니다.
이 다섯 단계만 지켜도 "갑자기 로그아웃돼서 다 날아갔다"는 불만이 크게 줄어듭니다.
3) 최소 구현 예시 (흐름)
if (isAccessTokenExpiringSoon()) {
const refreshed = await refreshWithRetry(2)
if (!refreshed) {
await autosaveDraftIfNeeded()
showSessionWarningBanner()
redirectToLoginWithReturnUrl()
}
}
핵심은 실패를 예외가 아니라 정상 경로로 취급하는 것입니다. 갱신 실패는 언제든 일어납니다. 실패를 설계에 넣어두면 장애가 아니라 이벤트가 됩니다.
4) 배포 전에 꼭 보는 체크리스트
- 만료 직전 작성 데이터 자동 저장이 실제로 동작하는가
- 모바일 백그라운드 복귀 시에도 갱신 로직이 중복 실행되지 않는가
- 동시 다중 탭에서 한 탭의 로그아웃이 다른 탭 상태를 깨뜨리지 않는가
- 재로그인 후 원래 보던 페이지와 작업 상태가 복원되는가
- 모니터링에
refresh_fail_rate,forced_logout_count가 있는가
세션 만료는 인증 정책이 아니라 사용자 신뢰 정책입니다. 시간을 조정하는 것보다, 만료 순간의 사용자 경험을 먼저 설계하면 보안과 UX를 동시에 지킬 수 있습니다.