[Web] HTTP 세션(Session)이란 무엇인가? +(세션 관리 방법)

 HTTP 세션이란? 

HTTP 세션은 웹 서비스에서 사용자의 상태 정보를 유지하기 위한 방법입니다. 기본적으로 HTTP  프로토콜은 상태값을 유지할 수 있는 방법이 없습니다. HTTP 통신은 클라이언트와 서버 간의 통신이 각각의 요청과 응답 간에 독립적으로 이루어지기에 사용자를 특정할 수 있는 방법이 존재하지 않기 때문입니다. 하지만 웹 서비스에서는 사용자의 상태를 계속 유지해야 하는 경우들이 있습니다. 예를 들자면 로그인이나, 장바구니를 예시로 들 수 있겠습니다. 다른 페이지로 이동하더라도 로그인이 유지되고, 장바구니에 담긴 상품들이 유지되는 것처럼 말이죠. 

 

HTTP 세션의 동작 원리

로그인을 한다고 가정하고 세션을 통해 어떻게 사용자의 상태값을 유지할 수 있는지 알아보겠습니다. DB를 사용할 수도 있는데 일반적으로 로그인은 쿠키 + 세션 조합으로 구현되는 경우가 많습니다.

 

1. 로그인을 하면 먼저 클라이언트 측에서 HTTP 요청으로 사용자가 입력한 아이디와 패스워드 정보를 서버로 보내야 합니다. 그런 뒤 서버는 아이디와 패스워드를 회원 저장소(DB)를 통해 해당 정보가 맞는지 확인합니다.

 

2. 만약 사용자 아이디와 패스워드가 맞다면 로그인이 성공한 거겠죠? 그렇다면 서버의 세션 저장소에 세션 ID(추정 불가능한 값)을 생성해서 회원 정보와 함께 저장합니다.

 

3. 세션 저장소에 정보를 남긴 뒤에는 클라이언트에 로그인이 성공했다고 알려줘야 합니다. HTTP 응답 메시지를 클라이언트에 내려줍니다. 이때 쿠키로 로그인 정보를 남겨줘서 세션과 쿠키가 연결되도록 만들어줍니다. 그래야 이후 요청값에 사용자를 특정할 수 있습니다. HTTP 응답 메시지 헤더에 Set-Cookie를 함께 내려줘서 세션키값이 쿠키에 저장되도록 합니다.

 

※ 이때 사용자의 정보를 저장하는 것은 보안에 취약하니 반드시 세션키값처럼 특정이 불가능한 값들만 쿠키로 저장되도록 하셔야 합니다.

 

4. 클라이언트는 이후 서버로 HTTP 요청할 때 항상 최초 요청 시에 쿠키 저장소에 저장해 뒀던 세션키값을 서버로 함께 보내줍니다. 이 세션키값으로 서버는 세션 저장소에서 데이터를 꺼내 활용할 수 있습니다 이렇게 하면 서버로 수많은 요청이 들어오더라도 이 세션키값으로 서버는 특정 유저의 요청을 알아차릴 수 있게 됩니다. 이러한 방식으로 세션과 쿠키를 통해 로그인을 구현할 수 있습니다.

 


 

 서버가 여러 대일 때는 로그인을 어떻게 유지할 수 있을까? 

위와 같이 사용자는 서버로 로그인을 할 때 서버에는 세션을 남기고 클라이언트에게는 쿠키로 세션키값을 전달해서 사용자를 특정할 수 있습니다. 서버가 한대일 때는 아무 문제가 없을 겁니다. 하지만 서버를 한대만 사용하는 웹 서비스들은 거의 없죠? 부하를 분산하기 위해 여러 대의 서버를 가동하는 경우가 대부분입니다. 세션은 서버에 남는 데이터로 여러 대의 서버를 운용할 때는 정합성 문제가 발생하게 됩니다. 

 

예를 들어 위의 그림처럼 사용자의 로그인 요청이 2번 서버를 통해 들어왔다고 가정해 봅시다. 그러면 2번 서버에 세션키값이 남을 겁니다. 그 세션키값은 클라이언트의 쿠키에도 저장되겠죠. 여기서 클라이언트가 다음 요청을 1번 서버로 보냈다고 가정해 보겠습니다. 그러면 클라이언트는 2번 서버에서 전달받은 쿠키의 값도 1번 서버로 함께 넘기게 될 텐데 1번 서버는 세션 저장소에 그 세션이 없기 때문에 오류가 발생하게 됩니다. 이를 세션 불일치 문제라고 합니다.

 

세션 불일치 문제를 해결하기 위해 대표적으로 3가지 방법이 있습니다.

  • Sticky Session
  • Session Clustering
  • Redis Session

 

Sticky Session

가장 쉬운 해결방법이 있습니다. 클라이언트마다 통신하는 서버를 고정해 주는 것입니다. 서버를 세션 불일치 문제는 사용자가 최초 통신은 서버 A와 했다가 이후 통신을 서버 B와 했기 때문에 세션값이 없어서 발생하는 문제입니다. 그렇기 때문에 클라이언트가 최초 통신한 서버로만 계속 통신하면 이 문제에 대해서 벗어날 수 있습니다. 이 방법을 바로 스티키세션(Sticky Session) 직역하자면 "고정된 세션"이라고 합니다.

 

스티키세션(Sticky Session)은 위의 그림처럼 서버와 클라이언트 중간에 트래픽을 분산해 주는 로드 밸런스라는 서버가 있습니다. 이 로드밸런스가 요청이 들어온 서버를 할당하고 그 서버에서만 그 서버에서만 통신을 주고받게 합니다. 이러한 스티키세션(Sticky Session) 은 가장 간편한 해결방법이면서도 언뜻 효율적으로 보입니다. 하지만 고정된 세션을 사용한다는 것은 특정 서버에 트래픽이 몰렸을 때도 다른 서버를 사용할 수 없다는 큰 단점이 있습니다. 또한 하나의 서버가 장애를 일으키면 그 서버와 통신하던 사용자들은 세션 정보를 그대로 잃게 되어 장애로 이어진다는 단점도 생각해야 합니다.

 

스티키 세션을 구현하는 방법은 로드 밸런서에 따라 다를 수 있습니다. 쿠키를 사용하여 스티키 세션을 관리할 수도 있고, IP 주소를 기반으로 스티키 세션을 유지할 수 있습니다.

 

 

Session Clustering

그렇다면 모든 서버에서 세션 정보를 똑같이 가지고 있으면 되지 않을까요? 이 방법은 세션 클러스터링(Session Clustering)이라고 불리는 기술입니다. 모든 서버에 세션 스토리지를 복제하여 어떤 서버에 접속하더라도 세션 ID를 찾을 수 있게 하여 세션 불일치 문제를 해결할 수 있습니다. Nginx, Tomcat 등 많은 서버 프로그램들은 세션 클러스터링을 지원합니다. 하지만 이 방법도 문제가 있습니다. 바로 서버 과부하입니다. 특정 서버에 세션이 남을 때마다 모든 서버의 세션 저장소에 같은 세션 ID를 저장해야 하기 때문에 트래픽이 몰릴수록 성능저하 이슈가 발생합니다. 이를 줄이기 위해서 세션 클러스터링(Session Clustering)에는 all-to-all이라는 방식이 있습니다.

 

all-to-all 방식

  1. 클라이언트에서 최초 통신으로 세션 ID와 세션 Value값을 생성합니다. 그리고 이 값은 노드 A에 전달됩니다. 여기서 노드 A는 Primary 노드가 됩니다.
  2. 이것을 노드 A의 세션 저장소에 저장하고 노드 B에는 복제된 값을 저장합니다. 여기서 노드 B는 Backup 노드가 됩니다.
  3. 노드 C에는 세션 ID와 해당 세션 ID 값의 Primary 저장 위치값만 전달합니다.

이 방식은 all-to-all이라는 세션 클러스터링 기술로  세션 ID와 세션 Value를 모든 서버에 복제하는 게 아니라 세션 Value 대신 단순 위치값만 다른 서버로 보낸다는 점이 특징입니다. 당연히 값 자체가 들어가는 것보다 위치값만 넣는 것이 메모리 낭비가 적기 때문에 효율적입니다.

 

Redis Session

그런데 생각해 보면 모든 서버에서 동일한 세션을 각각 가지고 있어야 한다는 것 자체가 비효율적이라고 생각이 들 수 있습니다. 그냥 모든 서버에서 하나의 세션저장소를 보면 해결되는 문제가 아닌가요? 이러한 생각에서 나온 방법은 Redis Session라는 방식입니다. 여기서 Redis는 메모리 기반의 키-값 저장소로 Redis Session은 Redis를 사용하여 구현한 HTTP 세션을 뜻합니다. 이 방식은 아래 그림처럼 세션 저장소(Session Storage)를 별개로 둡니다.

 

이렇게 세션 저장소를 별개로 두면 서버가 아무리 많아도 세션 정합성에 문제가 상관없습니다. 모든 서버에서 같은 세션 저장소를 바라보고 사용하면 되기 때문입니다. 이 방식은 한 서버에 트래픽이 몰렸을 때 대응이 힘들다는 스티키 세션의 문제도, 매번 세션을 복제해야 한다는 세션 클러스터의 문제도 발생하지 않습니다. 다만 이렇게 사용하면 세션 저장소에 문제가 발생하면 모든 서버에서 세션 활용이 불가능하기 때문에 만약을 위해 백업 세션 저장소를 하나 만들어두는 것이 좋습니다.

 

 

댓글

Designed by JB FACTORY