🤔 왜 인증 구조에 대해 고민했는가
이번 최종 프로젝트는 온라인 교육 플랫폼을 목표로 한 백엔드 서버 구현이었다.
기능 자체는 회원가입, 로그인, 강의 수강, 주문 등 비교적 익숙한 영역이었지만 인증/인가 파트를 제대로 맡아서 해본 적이 없었다는 점과
프로젝트 초기 설계 단계부터 인증을 어떻게 가져갈 것인가는 단순히 구현하는 것으로 그칠 수 있는 문제가 아니었다.
설계 및 여러 교육 플랫폼들을 찾아보며 우리 프로젝트에 필요한 요구사항은 아래와 같았다.
- 다중 디바이스 로그인 가능 (PC / Mobile / Tablet 등...)
- 관리자 / 강사 / 수강생 역할 분리
- 향후 계정 공유 방지, 동시 로그인 제한 정책 확장 가능성
- API 서버의 수평 확장 고려
이를 통해 세션 기반 인증보다는 Stateless 한 구조가 더 적합하다고 생각했다.
🔗 세션 기반 인증을 선택하지 않은 이유
Spring Security를 사용할 때 가장 익숙한 방식은 세션 기반 인증이다.
하지만 이번 프로젝트는 다음과 같은 한계가 있을 것이라고 예상했다.
❌ 세션 기반 인증의 한계점
- 서버 메모리 또는 외부 저장소(Session Storage)에 상태 의존
- 수평 확장 시 세션 동기화 비용 발생
- 디바이스 단위 제어가 까다로움
- 로그아웃 / 강제 만료 정책이 세션에 강하게 결합됨
✅ JWT 기반 인증의 장점
- 인증 요청의 주 경로를 서버 상태 없이 처리 가능
- 토큰 자체로 사용자 식별 가능
- API 서버 확장에 유리
- 모바일 / 웹 클라이언트와의 궁합이 좋음
→ 따라서 AccessToken은 완전히 Stateless 하게 가져가는 방향으로 결정했다.
🔍 AccessToken / RefreshToken 분리 설계
JWT를 사용할 때 가장 먼저 부딪히는 문제는 토큰 탈취에 대한 문제였다.
위에서 정리한 세션 기반 / JWT 기반 인증의 특징을 고려하고, 교육 도메인의 특성을 고려하여 아래와 같은 구조로 선택을 진행했다.
AccessToken
- 짧은 만료 시간
- API 요청마다 사용
- 서버에 저장하지 않음 (완전 무결성)
RefreshToken
- 상대적으로 긴 만료 시간
- AccessToken 재발급 용도
- 서버에서 통제 가능해야 함
일반적인 JWT 설계에서 나는 RefreshToken을 어디에서 관리할 것인가에 대한 고민이 필요했다.
👉 RefreshToken을 Redis에 저장한 이유
RefreshToken을 Redis에 저장하는 방식을 선택함으로써 위에서 선택한 고민에 대한 내용에 상충하는 부분이 발생했다.
"Stateless가 아닌 것 아닌가?"
이는 RefreshToken을 서버에서 관리하고자 하는 의도적인 선택이었다.
온라인 교육 플랫폼은 강의 영상과 같은 유료 콘텐츠를 다루는 도메인이다.
단순히 로그인 성공 여부보다 접속을 통제하고 차단할 수 있는 지점이 필요하다고 판단했다.
- 계정 공유를 통한 다중 접속
- 토큰 탈취 후 장시간 접근
- 로그아웃 이후에도 계속되는 접근
이러한 상황을 고려했을 때 RefreshToken까지 완전히 Stateless로 두는 것은 보안 및 운영 측면에서 제어하기 어려운 구조라고 생각했다.
따라서 인증 구조 전체가 Stateless 하지는 않지만, 상태가 필요한 지점을 의도적으로 제한한 구조라고 생각한다.
☑️ Redis 화이트리스트 구조 선택
RefreshToken 저장소로 Redis를 선택했고, 단순 저장이 아니라 화이트리스트 방식으로 관리하도록 설계했다.
Redis 구조 예시
key: refresh:{userId}:{deviceId}
value: refreshToken
TTL: refreshToken 만료 시간
이 구조를 통해서 위에서 발생할 수 있는 여러 특이사항을 처리할 수 있게 되었는데,
- 로그아웃 시 즉시 무효화
- 디바이스별 토큰 관리
- 특정 디바이스만 강제 로그아웃
- 동시 로그인 제한 정책 확장 가능
즉, 보안과 운영 관점에서의 제어력을 확보하기 위한 선택이었다.
Spring Security에서 지킨 원칙
1. Stateless 우선 설계
→ AccessToken 자체는 서버에 상태를 두지 않고 처리하되, 필요 시 통제할 수 있는 지점을 남김으로써 완전한 stateless아 보안성 사이의 균형을 유지했다.
2. 통제 가능한 RefreshToken 정책
→ RefreshToken 을 서버(Redis)에 저장함으로써 로그아웃 즉시 만료 처리, 디바이스 별 토큰 관리, 세션 공유 차단 같은 보안 요구사항을 만족했다.
→ 이는 모든 RefreshToken을 상태로 저장한다는 점에서 stateless 철학과는 다르지만 통제와 보안 우선의 설계 선택이었다.
3. Whitelist를 통한 RefreshToken 관리
→ Redis에 등록된 RefreshToken만 유효하게 처리함으로써 공격자가 탈취한 토큰을 마음대로 재사용하지 못하도록 했다.
→ 이를 통해, 단순 JWT 기반 인증 구조보다 보안적으로 더 강한 제어력을 갖게 됐다.
마무리하며..
이번 설계에서는 JWT 기반 인증을 적용하면서도 보안성과 통제력을 높이는 방향으로 선택했다.
그 결과 다음과 같은 장점을 얻을 수 있었다.
✓ AccessToken은 무상태 처리로 확장성이 좋고
✓ RefreshToken은 서버에서 관리하며
✓ 디바이스 / 사용자 단위 제어가 가능하다.
즉, Stateless 구조의 장점을 살리면서 Stateful한 통제도 활용한 하이브리드 설계가 됐다.
https://stark77.tistory.com/110
[실시간 대용량 서비스] 최종 프로젝트 발제(작성중)
📄 프로젝트 개요본 프로젝트는 자유 주제로 진행된 최종 프로젝트로, 온라인 교육 플랫폼을 목표로 한 백엔드 서버 구현에 초점을 두었습니다. JWT 기반 인증·인가 구조를 중심으로 사용자, 강
stark77.tistory.com
'Spring > Spring 실습' 카테고리의 다른 글
| [실시간 대용량 서비스] 모니터링 시스템 구축은 왜 필요한가 ? (1) | 2026.02.23 |
|---|---|
| [실시간 대용량 서비스] JWT 인증에서 블랙리스트로도 충분한데, 왜 화이트리스트를 선택했을까? (0) | 2026.02.06 |
| [실시간 대용량 서비스] 최종 프로젝트 발제 (0) | 2026.01.30 |
| [과제 회고] K사 서버 개발하기 (0) | 2026.01.14 |
| [프로젝트 회고] 실시간 채팅 프로젝트 (0) | 2025.12.31 |