티스토리 뷰
서버가 로그인 상태를 기억하는 방법
Session 기반 인증의 등장
웹 애플리케이션에서 로그인, 장바구니, 사용자 개인화 같은 기능을 제공하려면 사용자의 상태를 어느 정도 유지해야 합니다. 하지만 HTTP는 기본적으로 Stateless 프로토콜로 설계되어 있기 때문에 서버는 기본적으로 이전 요청의 상태를 기억하지 않습니다.
이 문제를 해결하기 위해 웹에서는 다양한 상태 관리 방식이 등장했습니다. 이전 글에서는 HTTP의 Stateless 특성과 함께 Cookie가 등장하게 된 배경을 정리했습니다. Cookie는 클라이언트에 작은 데이터를 저장하고 이후 요청마다 서버로 전달하는 방식으로 상태를 유지하려는 시도였습니다.
그러나 Cookie에 인증 정보를 직접 저장하는 방식은 여러 보안 문제를 만들 수 있었습니다. 이러한 문제를 보완하기 위해 등장한 방식이 바로 Session 기반 인증 구조입니다.
이번 글에서는 Cookie 이후에 등장한 Session 아키텍처가 어떤 방식으로 동작하는지, 그리고 웹 서비스가 확장되면서 Session 방식이 어떤 한계를 드러내게 되었는지를 정리해 보겠습니다.
Session 기반 인증 구조
Session 기반 인증의 핵심 아이디어는 비교적 단순합니다. 사용자 상태를 클라이언트가 아니라 서버가 직접 관리하는 방식입니다.
Cookie 기반 인증에서는 인증 관련 정보가 클라이언트 측에 저장될 수 있습니다. 이 방식은 구현이 단순하지만 중요한 정보가 클라이언트 환경에 노출될 수 있다는 우려가 있습니다.
Session 기반 인증은 이러한 문제를 완화하기 위해 사용자의 인증 상태를 서버에 저장합니다. 클라이언트는 실제 상태 정보를 저장하지 않고, 단지 Session을 식별할 수 있는 ID만 전달합니다.
이 구조에서는 다음과 같은 역할 분리가 이루어집니다.
- 클라이언트는 Session ID를 저장합니다.
- 서버는 Session ID에 해당하는 실제 사용자 상태를 관리합니다.
이러한 방식은 인증 정보가 서버 내부에 저장된다는 점에서 Cookie 기반 방식보다 보안 관리 측면에서 상대적으로 안정적인 구조로 평가됩니다.
Session 기반 로그인 흐름
Session 기반 인증은 일반적으로 다음과 같은 흐름으로 동작합니다.
사용자가 로그인 화면에서 아이디와 비밀번호를 입력하면 브라우저는 서버로 로그인 요청을 보냅니다. 서버는 전달받은 자격 정보를 검증하고 인증이 성공하면 새로운 Session을 생성합니다.
이때 서버는 Session ID라는 고유한 식별자를 생성합니다. 이 Session ID는 서버 내부에 저장된 Session 데이터와 연결되는 키 역할을 합니다.
이후 서버는 응답 헤더에 Set-Cookie를 사용하여 Session ID를 브라우저로 전달합니다. 브라우저는 이 값을 Cookie 형태로 저장합니다.
그 다음부터 사용자가 동일한 서비스에 요청을 보내면 브라우저는 자동으로 해당 Cookie를 요청에 포함시킵니다. 서버는 요청에 포함된 Session ID를 확인하고, 서버에 저장된 Session 정보를 조회하여 사용자의 로그인 상태를 확인합니다.
이 구조의 중요한 특징은 다음과 같습니다.
브라우저에는 Session ID만 저장되고, 실제 사용자 정보나 인증 상태는 서버 내부에 저장된다는 점입니다.
이 구조 덕분에 서버는 사용자의 인증 상태를 비교적 안전하게 관리할 수 있습니다.
서버에서 Session을 저장하는 방식
Session 기반 인증이 동작하려면 서버가 Session 데이터를 저장할 수 있어야 합니다. 실제 서비스에서는 여러 가지 방식이 사용됩니다.
대표적인 방식 중 하나는 서버 메모리에 Session을 저장하는 방법입니다.
이 방식은 구현이 간단하고 조회 속도가 빠르다는 장점이 있습니다. Session 데이터가 애플리케이션 서버의 메모리에 저장되기 때문에 별도의 저장소를 조회할 필요가 없습니다.
하지만 서버가 재시작되면 Session 정보가 사라질 수 있다는 문제가 있습니다. 또한 서버가 여러 대로 확장되는 환경에서는 Session 공유 문제가 발생할 수 있습니다.
또 다른 방식은 데이터베이스에 Session을 저장하는 방법입니다.
이 방식은 서버 재시작 시에도 Session을 유지할 수 있다는 장점이 있습니다. 하지만 모든 요청에서 Session 조회를 위해 데이터베이스에 접근해야 하기 때문에 성능에 영향을 줄 수 있습니다.
특히 트래픽이 많은 서비스에서는 데이터베이스가 Session 조회 트래픽까지 처리해야 하기 때문에 병목이 발생할 가능성이 있습니다.
이러한 이유로 실제 서비스에서는 Session 저장 방식이 서비스 규모와 요구사항에 따라 다양하게 선택됩니다.
대규모 트래픽 환경에서의 Session 문제
서비스 규모가 커지고 서버 수가 증가하면 Session 방식은 또 다른 문제를 만들 수 있습니다.
로드 밸런서를 통해 여러 애플리케이션 서버가 요청을 처리하는 환경에서는 어떤 서버가 Session을 가지고 있는지가 중요한 문제가 됩니다.
예를 들어 사용자가 처음 로그인할 때 서버 A에서 Session이 생성되었다고 가정해 보겠습니다. 이후 사용자의 요청이 서버 B로 전달된다면 서버 B는 해당 Session 정보를 알지 못할 수 있습니다.
이 문제는 Session 공유 문제라고 불립니다.
이를 해결하기 위해 여러 가지 전략이 등장했습니다.
Sticky Session
Sticky Session은 로드밸런서가 같은 사용자의 요청을 항상 동일한 서버로 전달하도록 하는 방식입니다.
이 방식에서는 사용자의 Session이 특정 서버에 저장되어 있어도 문제가 발생하지 않습니다. 로드밸런서가 항상 같은 서버로 요청을 보내기 때문입니다.
구현이 비교적 간단하다는 장점이 있지만 몇 가지 한계도 있습니다.
특정 서버에 사용자가 집중될 수 있고 서버가 장애를 일으킬 경우 해당 서버에 연결된 사용자의 Session이 모두 사라질 수 있습니다. 또한 서버 확장성이 제한될 수 있습니다.
이러한 이유로 Sticky Session은 비교적 단순한 환경에서는 사용될 수 있지만 대규모 서비스에서는 한계가 있습니다.
Session Replication
또 다른 접근 방식은 Session Replication입니다.
이 방식에서는 여러 서버가 Session 데이터를 서로 복제합니다. 즉 하나의 서버에서 Session이 생성되면 다른 서버에도 동일한 Session 데이터가 전달됩니다.
이 방식의 장점은 어떤 서버가 요청을 처리하더라도 동일한 Session 정보를 사용할 수 있다는 점입니다.
하지만 서버 수가 증가할수록 Session 동기화 비용이 증가합니다. 특히 트래픽이 많은 환경에서는 Session 변경이 발생할 때마다 여러 서버로 데이터를 전달해야 하기 때문에 네트워크 비용이 증가할 수 있습니다.
또한 동기화 지연이나 데이터 일관성 문제를 관리해야 하는 부담도 있습니다.
이러한 이유로 Session Replication 방식 역시 대규모 분산 환경에서는 운영 복잡성이 높아질 수 있습니다.
분산 환경에서의 해결 전략
웹 서비스 규모가 커지면서 Session 공유 문제를 보다 효율적으로 해결하기 위한 방법이 등장했습니다.
대표적인 접근 방식은 중앙 Session 저장소를 사용하는 구조입니다.
여러 애플리케이션 서버가 동일한 Session 저장소를 공유하도록 구성하는 방식입니다. 이렇게 하면 어느 서버가 요청을 처리하더라도 동일한 Session 정보를 조회할 수 있습니다.
이 구조에서 많이 사용되는 기술이 Redis 기반 Session Store입니다.
Redis는 인메모리 데이터 저장소이기 때문에 빠른 조회 속도를 제공합니다. 또한 클러스터 구성과 고가용성 구조를 지원하기 때문에 Session 저장소로 활용되는 경우가 많습니다.
예를 들어 Spring 기반 애플리케이션에서는 Spring Session을 통해 Redis를 Session 저장소로 사용할 수 있습니다. 이 구조에서는 애플리케이션 서버가 Session을 직접 관리하는 대신 Redis에 Session 데이터를 저장하고 조회하게 됩니다.
이 방식은 서버 간 Session 공유 문제를 비교적 단순하게 해결할 수 있다는 장점이 있습니다.
Cache Cluster 기반 Session 구조
Redis 같은 인메모리 저장소를 활용하면 Cache Cluster 기반 Session 아키텍처를 구성할 수 있습니다.
이 구조에서는 여러 애플리케이션 서버가 하나의 중앙 Session 저장소를 공유합니다. Session 데이터는 Redis 같은 캐시 시스템에 저장되고 애플리케이션 서버는 필요할 때 해당 데이터를 조회합니다.
이 방식의 장점은 다음과 같습니다.
서버 간 Session 동기화 문제가 사라집니다. 또한 애플리케이션 서버를 수평 확장해도 Session 관리 구조를 크게 변경할 필요가 없습니다.
이러한 이유로 Redis 기반 Session Store는 많은 웹 서비스에서 사용되는 방식입니다.
MSA 환경에서 Session 방식의 한계
그러나 서비스 아키텍처가 Microservice Architecture 형태로 발전하면서 Session 기반 인증 방식은 또 다른 한계를 드러내기 시작했습니다.
MSA 환경에서는 여러 서비스가 서로 독립적으로 동작합니다. 각 서비스가 별도의 배포 단위로 운영되기 때문에 인증 상태를 하나의 Session 저장소에 의존하는 구조는 아키텍처 유연성을 제한할 수 있습니다.
또한 API 기반 서비스에서는 다양한 클라이언트가 존재합니다. 모바일 애플리케이션, 웹 애플리케이션, 외부 API 클라이언트 등 다양한 환경에서 인증을 처리해야 합니다.
이러한 환경에서는 Stateless한 인증 방식이 더 유리할 수 있습니다.
Session 기반 인증은 서버가 상태를 관리하는 구조이기 때문에 인증 정보를 조회하기 위해 항상 Session 저장소에 접근해야 합니다. 이 구조는 분산 시스템에서 추가적인 의존성을 만들 수 있습니다.
이러한 배경 속에서 등장한 방식이 Token 기반 인증, 특히 JWT(JSON Web Token) 방식입니다.
JWT는 인증 정보를 서버가 저장하지 않고 토큰 자체에 포함시키는 방식으로 Stateless 인증을 구현하려는 접근입니다.
정리하며
웹의 상태 관리 방식은 서비스 요구사항과 아키텍처 변화에 따라 계속 진화해 왔습니다.
초기의 Cookie 기반 방식은 상태 관리의 시작점이 되었지만 보안 문제를 완전히 해결하지는 못했습니다. 이를 보완하기 위해 등장한 Session 방식은 인증 상태를 서버에서 관리한다는 점에서 보안성과 관리 측면에서 한 단계 발전한 구조라고 볼 수 있습니다.
하지만 서비스 규모가 커지고 분산 환경이 일반화되면서 Session 방식은 확장성 측면에서 새로운 과제를 만들기도 했습니다. Sticky Session, Session Replication, Redis 기반 Session Store 같은 다양한 해결 전략이 등장한 것도 이러한 배경과 무관하지 않습니다.
최근에는 MSA 환경과 API 중심 아키텍처가 보편화되면서 Stateless 인증 방식이 다시 주목받고 있습니다.
다음 글에서는 이러한 흐름 속에서 등장한 JWT 기반 인증 구조가 어떤 문제를 해결하려 했는지, 그리고 왜 많은 서비스에서 사용되기 시작했는지를 정리해 보려고 합니다.
'STUDY' 카테고리의 다른 글
- Total
- Today
- Yesterday
- mybatis
- InterruptedException
- Cache Aside
- Enum 기반 싱글톤
- Java Performance
- 캐시와 인덱스
- 백엔드 성능 튜닝
- Double-Checked Locking
- Redis 캐시 전략
- DB 트랜잭션
- 백엔드 아키텍처
- Redis vs DB
- 트랜잭션 관리
- DB 인덱스 성능
- 트래픽 처리
- Redis 성능 개선
- 동시성처리
- Cache Avalanche
- 캐시 장애
- Spring Batch
- 백엔드 성능 설계
- spring batch 5
- Hot Key 문제
- TTL 설계
- 스레드 생명주기
- Cache Penetration
- 캐시 성능 비교
- Initialization-on-Demand Holder Idiom
- 백엔드 성능
- Eager Initialization
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |

