본문으로 바로가기

11

세션 관리

서버를 늘렸더니 로그인이 자꾸 풀린다

먼저 보면 좋은 강의: 지연 시간과 처리량, 오토스케일링

단계별로 보기

스페이스바로 재생/정지, 좌우 화살표로 단계 이동, R로 처음으로 돌아갑니다.

대기 중1 / 4 단계 — 서버 1에서 로그인한다

사용자가 로그인합니다. 로드밸런서는 이번 요청을 서버 1로 보냈습니다. 서버 1이 번호표 A7F2를 발행하고, 이 번호표가 김민수라는 사실을 어딘가에 적어둡니다. 문제는 그 '어딘가'가 어디냐입니다.

이 강의를 마치면

  • '로그인 상태'가 어딘가에 저장된 기억이며, 그 기억의 위치가 확장을 좌우함을 설명한다.
  • 서버 메모리에 세션을 두면 서버를 늘리거나 교체할 때 무슨 일이 벌어지는지 관찰한다.
  • 고정 배정(스티키 세션)과 공유 저장소의 차이와 각각의 대가를 비교한다.
  • 서버를 '상태 없게(stateless)' 만드는 것이 확장의 전제 조건임을 설명한다.

로그인은 어디에 저장되어 있나

로그인을 하면 화면에 이름이 뜹니다. 그다음 페이지를 눌러도 계속 로그인 상태입니다. 당연해 보이지만, 여기에는 기억이 필요합니다. HTTP 요청은 서로를 모릅니다. 요청 하나하나는 완전히 남남입니다.

그래서 서버는 로그인할 때 번호표를 하나 발행합니다. 이걸 세션 ID라고 하고, 브라우저는 이 번호표(쿠키)를 다음 요청부터 계속 내밉니다.

브라우저: "번호표 A7F2입니다. 마이페이지 주세요."
서버:     "A7F2... 아, 김민수 님이시군요. 여기 있습니다."

질문은 이것입니다. A7F2가 김민수라는 사실은 어디에 적혀 있나요?

방법 1 — 서버가 자기 머릿속에 적어둔다

가장 간단합니다. 서버가 자기 메모리에 A7F2 → 김민수를 적어둡니다. 빠르고 공짜입니다. 서버가 한 대일 때는 아무 문제가 없습니다.

그런데 앞 강의에서 우리는 서버를 네 대로 늘렸습니다. 여기서 두 가지가 깨집니다.

깨짐 1 — 다른 서버로 가면 남남이다

로드밸런서는 요청을 골고루 나눕니다. 로그인은 서버 1에서 했는데, 다음 요청이 서버 2로 갔습니다. 서버 2의 머릿속에 A7F2는 없습니다.

사용자 화면: "로그인이 필요합니다."

방금 로그인했는데 말입니다. 그리고 다시 로그인하면 이번엔 서버 3으로 갈 수도 있습니다.

깨짐 2 — 서버가 꺼지면 기억이 사라진다

오토스케일링은 한산해지면 서버를 끕니다. 배포를 해도 서버는 교체됩니다. 그때 그 서버 메모리에 있던 세션은 전부 함께 사라집니다. 그 서버에 배정되어 있던 사용자 전원이 동시에 로그아웃됩니다.

즉, 세션을 서버 메모리에 두는 순간 서버를 마음대로 끌 수 없게 됩니다. 오토스케일링과 세션은 정면으로 충돌합니다.

방법 2 — 같은 사람은 항상 같은 서버로 (스티키 세션)

"그럼 김민수는 항상 서버 1로 보내면 되잖아?" 맞습니다. 이것을 고정 배정 또는 스티키 세션이라고 합니다. 로드밸런서가 쿠키를 보고 늘 같은 서버로 보냅니다.

깨짐 1은 해결됩니다. 그런데 깨짐 2는 그대로입니다. 서버 1이 꺼지면 서버 1에 붙어 있던 사람들만 로그아웃됩니다. 게다가 새로 문제가 생깁니다.

고정 배정의 대가
균형우연히 헤비 유저가 서버 1에 몰리면 서버 1만 죽는다
축소서버를 끄려면 그 서버에 붙은 사람들을 버려야 한다
배포무중단 배포가 어려워진다

고정 배정은 문제를 없애는 게 아니라 미룹니다.

방법 3 — 기억을 서버 밖으로 꺼낸다

진짜 해법은 방향이 다릅니다. 기억을 서버 안에 두지 않습니다.

세션 저장소라는 공용 창고를 하나 두고, 모든 서버가 거기에 묻습니다.

브라우저 → 로드밸런서 → 아무 서버나 → 세션 저장소("A7F2가 누구지?")
                                    → "김민수"

이제 어떤 서버가 받아도 똑같이 답할 수 있습니다. 서버는 기억할 것이 없어졌습니다. 이것을 상태 없는(stateless) 서버라고 부릅니다.

상태가 없으면 서버는 부품이 됩니다. 언제든 늘리고, 끄고, 교체할 수 있습니다. 앞 강의의 오토스케일링이 실제로 성립하려면 이 강의가 먼저 필요합니다.

이것도 공짜는 아니다

  • 요청마다 창고에 한 번 더 다녀와야 합니다. 몇 밀리초가 더 듭니다.
  • 창고가 죽으면 전원이 로그아웃됩니다. 문제를 한곳에 모은 것이므로, 그 한곳은

반드시 이중화해야 합니다.

그래도 이 거래는 거의 항상 이득입니다. 몇 밀리초를 내고 "서버를 마음대로 다룰 수 있는 자유"를 사는 것이기 때문입니다.

직접 해보세요

아래 과제에는 서버 두 대가 로드밸런서 뒤에 있습니다. 세션을 담을 공용 창고를 두고, 두 서버가 모두 그곳을 보도록 연결하세요.

이 강의가 단순화한 것

교육용 모형은 언제나 무언가를 생략합니다. 무엇을 생략했는지 아는 것도 학습의 일부입니다.

전제한 것

  • 이 시뮬레이터는 '로그인이 풀리는 것'을 오류로 세지 않는다. 세션 저장소를 연결하면 조회 왕복이 실제로 발생하는지까지만 확인한다.
  • 세션 저장소는 죽지 않는다고 가정한다. 현실에서 이 창고는 단일 장애점이므로 반드시 이중화한다.
  • 세션 조회 시간은 2밀리초로 고정한다. 실제로는 네트워크 거리와 부하에 따라 달라진다.

다루지 않은 것

  • 쿠키·JWT 등 세션 식별자의 형식과 서명 검증
  • 토큰 자체에 정보를 담아 저장소를 없애는 방식(stateless token)과 그 무효화 문제
  • 세션 저장소의 복제·장애 조치
  • 세션 고정(session fixation) 등 보안 공격

더 읽을거리: RFC 6265 — HTTP State Management Mechanism

이제 직접 만들어 보세요

읽어서 아는 것과 만들어서 아는 것은 다릅니다. 컴포넌트를 배치하고 트래픽을 흘려서 실제로 동작하는지 확인해 보세요.

실습 과제 열기

이 강의에 물어보기

하루 5번

이 강의 본문만 근거로 답합니다. 강의에 없는 내용은 지어내지 않습니다.