Bucket Place/Ruby on Rails

[Ruby on Rails] Session & User login

Cloud Travel 2014. 5. 12. 17:05


  들어가면서... 


 안녕하세요. Bucket Place개발자 Cloud Travel입니다. 오랜만에 형식에 맞춰서 글을 쓰는 느낌이 듭니다. Rails에 적응하고, 분석하고, 예제를 실험해보는 과정에서 필요한 내용을 간략하게 메모만 해왔는데, Session에 대해서는 잘 알고 넘어가야 하는 것 같아서 형식에 맞춰 잘 정리하려고 합니다.


 어떤 프레임워크 또는 언어를 사용하더라도 Session관리는 웹 어플리케이션을 제작하고 관리하는데 큰 이슈가 됩니다. 이에 대해서 정확히 알고 사용할 필요가 있습니다. 일단, 오늘의 목표는 다음과 같습니다.


 - Session에 관련된 컨트롤러 생성

 - Session에 필요한 각 모듈을 제작

 - Session의 정보를 Database와 사용자의 브라우져에 저장

 - 이와 동시에 사용자 로그인과 로그아웃 기능을 제작


 또한, 이 페이지는 이전에 사용자에 관련된 Database가 완성되있다는 가정하에 진행이 됩니다.

 



  Session controller 뼈대 생성 및 뷰 설정


 일단, 세션에 관련된 정보를 관리할 Session controller를 생성합니다.

 $ rails generate controller Sessions

 ...

 

 세션 정보를 RESTFul  Architecture에 맞게 설정을 하기 위해서  routes.rb 에 resource를 등록해줍니다.

$ vi config/routes.rb

...
  resources :users
  resources :sessions, only: [:new, :create, :destroy] 
  root  'static_pages#home'

  match '/signup',  to: 'users#new',            via: 'get'
  match '/signin',  to: 'sessions#new',         via: 'get'
  match '/signout', to: 'sessions#destroy',     via: 'delete'
...

resources :sessions, only: [:new, :create, :destroy]

 : Sessions을 RESTful 아키텍쳐를 이용하는데 이중에서 new, create 그리고 destroy Method만을 이용한다는 것입니다.

match '/signin', to: 'session#new',    via:'get'

 : /session/new 페이지로 들어가는 url을 /signin으로 변경을 합니다.

match '/signin', to: 'session#destroy',    via:'delete'

 : /session/destroy 페이지로 들어가는 url을 /signout으로 변경을 합니다.


 다음으로는 컨트롤러와 뷰를 제작해주면 됩니다. 위에서 new, create, destroy 메소드만을 사용한다고 했기 때문에 controller의 기본틀은 다음과 같이 됩니다.

$ vi app/controllers/sessions_controller.rb


class SessionsController < ApplicationController

  def new
  end

  def create
  end

  def destroy
  end
end

 회원 로그인을 위한 view를 생성해줍니다.

$ vi app/views/sessions/new.html.erb

<h1>Sign in</h1>

<div class="row">
  <div class="span6 offset3">
    <%= form_for(:session, url: sessions_path) do |f| %>

      <%= f.label :email %>
      <%= f.text_field :email %>

      <%= f.label :password %>
      <%= f.password_field :password %>

      <%= f.submit "Sign in", class: "btn btn-large btn-primary" %>
    <% end %>

    <p>New user? <%= link_to "Sign up now!", signup_path %></p>
  </div>
</div>



  Session 정보 저장하기


 세션을 사용하기 위해서는 몇가적인 추가적 설정 및 User 테이블을 변경하여 세션 정보를 저장할 필요가 있다.

 일단, 세션은 어플리케이션 전체에서 사용되기 때문에 application_controller에 session helper를 포함시켜준다.

$ vi app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  include SessionsHelper
end

 다음으로는 사용자 테이블을 아래의 그림과 같이 변경해야 한다. remember_token은 세션 정보를 저장할 특성이다.

 

새로운 특성 추가를 위해서는 다음과 같은 행동을 해주면된다. 이미 여러번 해봤을 꺼라고 생각한다.

$ rails generate migration add_remember_token_to_users

 ...

$ vi db/migrate/[Time_stamp]_add_remember_token_to_users.rb

class AddRememberTokenToUsers < ActiveRecord::Migration
  def change
    add_column :users, :remember_token, :string
    add_index  :users, :remember_token
  end
end

  변경된 내용을 추가시키기 위해서 migration을 실행해준다.

$ bundle exec rake db:migrate


 이제는 사용자의 세션 토큰을 생성하고 암호화하는 코드를 작성해야 한다. 이 정보들은 사용자 모델과 밀접하기 때문에 사용자 컨트롤러에서 설정을 해준다.

$ vi app/models/user.rb

class User < ActiveRecord::Base
  before_save { self.email = email.downcase }
  before_create :create_remember_token
  .
  .
  .
  def User.new_remember_token
    SecureRandom.urlsafe_base64
  end

  def User.encrypt(token)
    Digest::SHA1.hexdigest(token.to_s)
  end

  private

    def create_remember_token
      self.remember_token = User.encrypt(User.new_remember_token)
    end
end

 사용자 정보를 새로 저장할 때(즉, 회원가입할 경우) 저장하기 전에 세션 토큰을 생성하고 저장하는 부분을 만들어준 것이다.

 - before_create :create_remember_token : create 함수를 실행전에 create_remember_token함수를 실행

 - new_remember_token : 64bit 길이의 키를 랜덤하게 생성한다.

 - encrypt : 키를 암호화 한다.

 - create_remember_token : remember_token 변수를 설정하여 토큰 정보를 저장한다.



  로그인 & 로그 아웃


 로그인에 관련된 컨트롤러와 모듈을 제작해보자.

 

 일단, 로그인을 하게 되면 사용자가 정확한 정보를 입력했는지를 판단하고(비밀번호 확인), 결과를 알려준다. 성공을 했다면, sign_in 함수를 호출하고 사용자 정보를 보여주는 페이지로 이동을 시키고, 그 외에는 에러메세지를 flash를 이용하여 보여준다.

$ vi app/controllers/sessions_controller.rb

class SessionsController < ApplicationController
  .
  .
  .
  def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      sign_in user
      redirect_to user
    else
      flash.now[:error] = 'Invalid email/password combination'
      render 'new'
    end
  end
  def destroy
    sign_out
    redirect_to root_url
  end
end

 폼으로 받아들인 email이 존재하는지의 여부와 비밀번호로 생성된 인증코드와 일치여부를 판단한다. 다음으로는 세션에 관련된 부가적인 기능을 추가하도록 하자.

$ vi app/helpers/sessions_helper.rb

module SessionsHelper
	# 로그인시 세션 토큰을 만들고 쿠키에 저장
	def sign_in(user)
    	  remember_token = User.new_remember_token
    	  cookies.permanent[:remember_token] = remember_token
    	  user.update_attribute(:remember_token, User.encrypt(remember_token))
    	  self.current_user = user
  	end

  	# 로그인했는지 여부를 확인
  	def signed_in?
    	  !current_user.nil?
  	end

  	# 이후의 페이지에서 세션토큰을 위해서 사용자 정보를 가져온다.
  	def current_user=(user)
    	  @current_user = user
  	end

  	# 쿠키에 저장된 토큰을 가져와서 사용한다. 이 토큰을 이용해서 사용자 정보도 가져온다.
  	def current_user
    	  remember_token = User.encrypt(cookies[:remember_token])
    	  @current_user ||= User.find_by(remember_token: remember_token)
  	end

    # 로그 아웃
    def sign_out
      self.current_user = nil
      cookies.delete(:remember_token)
    end
end

 각각의 모듈은 다음과 같다.

 - sign_in(user) : 회원 로그인시 세션 토큰을 생성하고 쿠키에 저장을 한다.

 - signed_in? : 뷰에서 회원 로그인 여부를 이용해서 뷰를 관리하기 위해서 존재한다.

 - current_user=(user) : 로그인한 회원의 정보를 가져온다.

 - current_user : 쿠키에 저장된 정보를 이용하여 다른 페이지를 컨트롤 한다.

 - signe_out : 로그아웃



  마무리


 흠.. 먼가 간략하게 적으려다 보니 부족한 내용이 있을지도 모른다.

 이렇게 부분으로 설명하기 힘들다. Rails는... 그래서 왠만한 책들이 하나의 프로젝트를 다루는 것일지도 모르겠다...