Backend/RubyOnRails

How Rails Sessions Work(어떻게 레일즈 세션은 동작하는가)

Seyun(Marco) 2021. 12. 24. 18:41
728x90

How Rails Sessions Work

Rails App에 누가 방문했는지 알수 없다면 어떻게 될까요? 만약 같은 사람이 두개의 다른 페이지를 요청했다는 사실을 몰랐다면? 만약 응답을 반환하자 마자 저장한 모든 데이터가 사라진다면?

이런 경우에는 대부분 정적 사이트에는 적합할수 있습니다. 그러나 대부분 앱은 사용자에 대한 일부 데이터를 저장할 수 있어야 합니다. ID, 언어, 아이패드에서 데스크톱 버전으로 보고싶어하는지 등등에 대한 여부가 저장해야 할 데이터 일겁니다.

session 은 이러한 한 개 이상의 요청에 대해 보관하려는 작은 데이터를 저장하는데 완벽한 장소입니다.

세션은 사용하기 굉장히 쉽습니다.

session[:current_user_id] = @user.**id**

그러나 이것은 약간의 매직일수 있습니다. 세션은 무엇일까요? 어떻게 Rails는 올바른 사용자에게 이러한 정보를 표시해줄까요? 그리고 세션 데이터를 보관할 위치를 어떻게 결정할까요?

Session이란?

세션이란 오직 나중에 요청동안 읽을수 있는 한 요청의 데이터를 저장하는 장소일뿐입니다.

컨트롤러 작업에서 일부 데이터를 설정할 수 있습니다.

# **app/controllers/sessions_controller.rb**

def create
  # ...
  session[:current_user_id] = @user.id
  # ...
end

위의 작업을 이용해 다른 컨트롤러에서 읽을 수 있습니다.

# **app/controllers/users_controller.rb**

def index
  current_user = User.find_by_id(session[:current_user_id])
  # ...
end

이것은 흥미롭지 않을 수 있습니다. 그러나 위와 같은 행위를 하기 위해 사용자의 브라우저와 Rails App간의 연결이 필요합니다. 이러한 작업은 모두 쿠키로 시작됩니다.

클라이언트가 웹 페이지를 요청할때, 서버는 응답으로 쿠키를 설정할 수 있습니다.

~ jweiss$ curl -I http://www.google.com | grep Set-Cookie

Set-Cookie: NID=67=J2xeyegolV0SSneukSOANOCoeuDQs7G1FDAK2j-nVyaoejz-4K6aouUQtyp5B_rK3Z7G-EwTIzDm7XQ3_ZUVNnFmlGfIHMAnZQNd4kM89VLzCsM0fZnr_N8-idASAfBEdS; expires=Wed, 16-Sep-2015 05:44:42 GMT; path=/; domain=.google.com; HttpOnly

브라우저는 이 쿠키를 저장합니다. 그리고 쿠키가 만료될 때까지 요청할 때마다 브라우저는 쿠키를 다시 서버로 보내게 됩니다.

...
> GET / HTTP/1.1
> User-Agent: curl/7.37.1
> Host: www.google.com
> Accept: */*
> Cookie: NID=67=J2xeyegolV0SSneukSOANOCoeuDQs7G1FDAK2j-nVyaoejz-4K6aouUQtyp5B_rK3Z7G-EwTIzDm7XQ3_ZUVNnFmlGfIHMAnZQNd4kM89VLzCsM0fZnr_N8-idASAfBEdS; expires=Wed, 16-Sep-2015 05:44:42 GMT; path=/; domain=.google.com; HttpOnly
...

많은 쿠키는 알아들을 수 없는 말로 되어 있을것 입니다. 그리고 쿠키는 그렇게 보여여 합니다. 왜냐하면 쿠키 안의 정보는 사용자를 위함이 아니기 때문입니다. Rails app은 쿠키의 의미를 파악하기 위해 동작을 합니다. 즉, 앱에서 설정해서 앱에서 읽을 수 있습니다.

세션과 어떤 관련이 있는가?

한 쿠키를 가지고 있습니다. 요청을 보내 데이터를 얻으면 다음 요청에서도 동일한 데이터를 얻습니다. 그렇다면 세션과 쿠키의 차이점은 무엇일까요?

기본적으로 Rails에서는 큰 차이가 없습니다. Rails는 쿠키를 좀 더 안전하게 만들기 위해 일부 작업을 하지만 그 외에는 예상대로 작업합니다. Rails App은 일부 데이터를 쿠키에 넣고 동일한 데이터가 쿠키에서 나오게 됩니다. 그러나 이것이 전부였다면 쿠키와 세션을 구별할 필요가 없었습니다.

따라서 쿠키가 항상 세션 데이터에 대한 정답은 아닙니다.

  • 쿠키에는 약 4kb의 데이터만 저장할 수 있습니다.
    • 이 것이 일반적으로 충분하지만 그렇지 않을 때도 있습니다.
  • 요청하는 모든 요청과 함께 쿠키와 전송됩니다.
    • 즉, 큰 쿠키는 더 큰 요청과 응답을 의미하게 되며 더 느린 웹사이트가 될 수 있습니다.
  • 실수로 secret_key_base 를 노출하면 쿠키에 넣은 데이터를 변경할 수 있습니다.
    • current_user_id 를 조작할 수 있습니다.
  • 쿠키 내부에 잘못된 종류의 데이터를 저장하는 것이 안전하지 않을 수 있습니다.

그러나 이것들은 조심하면 큰 문제가 되지 않습니다.

그러나 다음과 같은 이유로 쿠키 안에 세션 데이터를 저장할 수 없을 때는 Rails에서는 세션을 보관할 수 있는 몇가지 다른 저장소가 있습니다.

대체 세션 저장소

쿠키 세션 저장소가 아닌, 모든 세션 저장소는 비슷하게 작동합니다. 실제 사용하는 것은 쉽습니다.

ActvieRecord로 세션을 추적하는 경우 :

  1. session[:current_user_id] = 1 을 앱에서 호출 할때 세션에 이미 존재하지 않는 경우
  2. Rails는 sessions 테이블에 임의의 세션 ID(예: 09497d46978bf6f32265fefb5cc52264)의 새 레코드를 생성합니다.
  3. 해당 레코드의 속성에 {current_use_id: 1}( Base64 인코딩) 값이 저장됩니다.
  4. 그리고 생성된 세션 ID(09497d46978bf6f32265fefb5cc52264)를 브라우저에 set-Cookie 를 사용해 보냅니다.

다음 페이지에서 요청을 보낼때 :

  1. 브라우저는 Cookie: 헤더를 사용하여 동일한 쿠키를 앱애 보냅니다.

     Cookie: _my_app_session=09497d46978bf6f32265fefb5cc52264;path=/; HttpOnly
  2. session[:current_user_id]를 호출하면

  3. 앱은 쿠키에서 세션 ID를 가져와 sessions 테이블에서 해당 레코드를 찾습니다.

  4. 그리고 current_user_id 의 데이터를 반환합니다.

세션을 데이터베이스에 저장하든, Memcached, Redis에 저장하든 다른 모든 위치에 저장하든 대부분 동일한 프로세스를 따르게 됩니다.

쿠키에는 세션 ID만 포함되고 Rails 앱은 해당 ID를 사용하여 세션 저장소에서 데이터를 조회하게 됩니다.

쿠키 저장소, 캐시 저장소, 데이터베이스 저장소?

세션을 쿠키에 저장하는 것이 가장 쉬운 방법입니다. 왜냐하면 추가 인프라나 설정이 필요하지 않기 때문입니다.

그러나 쿠키 세션 저장소 이상으로 이동해야 하는 경우가 있습니다. 이때 2가지 옵션이 있습니다.

세션을 데이터베이스에 저장하거나 캐시에 저장하는 방법입니다.

캐시에 세션을 저장하기

데이터를 캐시하기 위해 Memcache와 같은 것을 사용하고 있을 수 잇습니다. 그렇다면 캐시 저장소는 이미 설정되어 있으므로 세션 데이터를 저장하기 두번째로 쉬운 장소가 될겁니다.

이전 세션이 너무 커지면 캐시에서 자동으로 쫓겨나기 때문에 세션 저장소가 제어할 수 없게 증가하는 것에 대해 걱정할 필요가 없습니다. 그리고 캐시가 메모리에 보관될 가능성이 높기 때문에 속도가 빠릅니다.

그러나 완벽하지 않습니다.

  • 실제로 오래된 세션을 유지해야 한다면 캐시에서 쫓겨나는 걸 원치 않을 겁니다.
  • 세션과 캐시된 데이터는 공간을 가지고 싸우게 됩니다. 메모리가 충분하지 않으면 수 많은 캐시 누락 및 조기 만료 세션에 직면할 수 있습니다.
  • 캐시를 재 설정해야 하는 경우(예를 들어 Rails를 업그레이드했는데 이전에 캐시된 데이터가 더 이상 정확하지 않을 때) 모든 사람의 세션이 만료되지 않고 이를 수행할 방법이 없습니다.

그래도 이것을 Avvo에 세션 데이터를 저장하는 방법으로 잘 동작했습니다.

데이터베이스에 세션을 저장하기

세션 데이터가 만료될 때까지 유지하려면 일종에 데이터베이스에 보관하는 것이 좋습니다. Active Record, Redis 등

그러나 데이터 베이스 세션 저장소도 다음과 같은 단점이 있습니다.

  • 일부 데이터베이스 저장소의 경우 세션이 자동으로 정리되지 않습니다.
    • 따라서 만료된 세션을 직접 살펴보고 정리해야 합니다.
  • 데이터베이스가 세션 데이터로 가득 찾을 때 데이터베이스가 어떻게 작동하는지 알아야 합니다.
    • Redis를 세션 저장소를 사용하고 있습니까? 메모리에 모든 세션데이터를 모두 유지하려고 합니까? 서버에 충분한 메모리가 있습니까? 너무 심하게 스와핑을 시작해서 ssh 를 사용해 문제를 해결할수 없습니까?
  • 세션 데이터를 생성할 때 더 주의해야 합니다. 그렇지 않으면 데이터베이스가 쓸모없는 세션으로 채워집니다.
    • 예를들어 모든 요청에서 실수로 세션을 건들면 google Bot이 수십만 개의 쓸모 없는 세션을 생성할 수 있습니다.

그러나 이러한 문제는 드물지만, 이러한 문제가 있다는걸 알고 있어야 합니다.

그렇다면 어떻게 세션을 저장해야 합니까?

쿠키 저장소에 제한 사항에 부딪치지 않는 다고 확신할 경우에는 쿠키 저장소를 사용하면 됩니다. 많은 설정이 필요하지 않으며 유지 관리가 번거롭지 않습니다.

캐시에 세션을 저장하는 것과 데이터베이스에 저장하는 것은 세션을 일찍 만료시키는 것이 얼마나 나쁜지에 판단에 따라 사용하면 됩니다. 세션 데이터를 임시로 처리하므로 캐시 저장소에서 잘 동작할 겁니다. 그래서 나는 보통 쿠키를 먼저 시도한 다음 캐시를 시도한 다음 데이터베이스를 시도합니다.

728x90
728x90