이 튜토리얼에서는 사용자가 자신의 트위터 계정으로 애플리케이션에 로그인할 수 있도록 하는 방법을 알아봅니다. OAuth와 같은 도구 덕분에 이러한 기능 구현이 한결 쉬워졌습니다.
여기서는 OmniAuth의 트위터 전략(Twitter Strategy)을 담고 있는 OmniAuth-Twitter 젬(gem)을 활용하겠습니다.
그럼 바로 시작해 보겠습니다!
시작하기
먼저 Rails 애플리케이션을 생성합니다. 터미널에서 다음 명령어를 실행하세요.
rails new Tuts-Social -T
Gemfile을 열어 bootstrap 젬을 추가합니다.
#Gemfile ... gem 'bootstrap-sass'
다음 명령어로 젬을 설치합니다.
bundle install
app/assets/stylesheets/application.css 파일명을 app/assets/stylesheets/application.scss로 변경합니다.
변경 후 아래 코드를 추가하여 bootstrap을 임포트합니다.
#app/assets/stylesheets/application.scss ... @import 'bootstrap-sprockets'; @import 'bootstrap';
네비게이션 코드를 담을 _navigation.html.erb 파셜(partial)을 app/views/layouts 디렉터리에 생성합니다.
IDE에 아래 코드를 입력하세요. Bootstrap을 활용해 애플리케이션용 내비게이션 바를 만드는 코드입니다.
#app/views/layouts/_navigation.html.erb
<nav class="navbar navbar-inverse">
<div class="container">
<div class="navbar-header">
<%= link_to 'Tuts Social', root_path, class: 'navbar-brand' %>
</div>
<div id="navbar">
<ul class="nav navbar-nav pull-right">
<li><%= link_to 'Home', root_path %></li>
</ul>
</div>
</div>
</nav>네비게이션을 실제로 사용하려면 애플리케이션 레이아웃에서 이를 렌더링해야 합니다. 아래와 같이 레이아웃 파일을 수정하세요.
#app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Tuts Social</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= render "layouts/navigation" %>
<div id="flash">
<% flash.each do |key, value| %>
<div class="flash <%= key %>"><%= value %></div>
<% end %>
</div>
<div class="container-fluid">
<%= yield %>
</div>
</body>
</html>다음으로 index 액션을 가진 PagesController를 생성합니다. 터미널에 아래 명령어를 입력하세요.
rails generate controller Pages index
생성된 index 뷰 파일을 아래와 같이 수정합니다.
#app/views/pages/index.html.erb <div class="jumbotron"> <h1>Welcome to Tuts Social!</h1> <p>Thanks for checking us out!</p> </div>
위 코드에서는 jumbotron이라는 클래스를 사용했습니다. 이는 Bootstrap 컴포넌트로, 뷰포트를 확장하여 마케팅 메시지를 눈에 띄게 보여줄 때 유용합니다. 자세한 내용은 Bootstrap 공식 문서에서 확인할 수 있습니다.
루트 파일을 열어 root_path를 추가합니다.
#config/routes.rb Rails.application.routes.draw do # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html root to: "pages#index" end
OmniAuth-Twitter 설정하기
새로운 트위터 애플리케이션을 생성해야 합니다. 트위터 개발자 페이지에서 앱을 만들고, 아래 스크린샷과 비슷하게 필요한 정보를 모두 입력하세요.



콜백 URL(Callback URL)에는 웹사이트 주소 뒤에 "auth/twitter/callback"을 붙인 값을 입력합니다. 로컬 머신에서 작업하는 경우 콜백 URL은 다음과 같습니다. https://127.0.0.1:3000/auth/twitter/callback
앱 생성이 완료되면 트위터의 앱 정보 페이지로 리디렉션됩니다. Keys and Access Tokens 탭으로 이동해 키를 확인하고, Consumer Key(소비자 키)와 Consumer Secret(소비자 시크릿)을 복사해 안전한 곳에 붙여넣어 두세요. 곧바로 사용하게 됩니다.
콜백 URL이란 인증과 권한 승인이 성공적으로 완료된 후 사용자가 애플리케이션 내부로 리디렉션되는 URL을 의미하며, 요청에는 사용자 데이터와 토큰도 함께 포함됩니다. 모든 OmniAuth 전략은 콜백 URL이 "/auth/:provider/callback" 형태와 일치할 것으로 기대합니다. 여기서 :provider는 전략의 이름을 나타내며, 이번 예제에서는 초기화 파일에 지정할 대로 "twitter"가 됩니다.
Gemfile을 열어 omniauth-twitter 젬을 추가합니다.
#Gemfile ... gem 'omniauth-twitter'
이제 config/initializers 디렉터리에 OmniAuth용 초기화 파일을 생성합니다. 이 파일에는 OmniAuth 설정이 들어갑니다. 아래와 같이 작성하세요.
#config/initializers/omniauth.rb Rails.application.config.middleware.use OmniAuth::Builder do provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET'] end
이 시점에서 안전하게 보관해 둔 키와 액세스 토큰이 필요합니다. 코드를 커밋할 때 공개 저장소에 노출되지 않도록 반드시 안전하게 관리해야 합니다.
이를 위해 dotenv-rails 젬을 사용하겠습니다. Gemfile을 다시 열어 아래와 같이 추가하세요.
#Gemfile ... group :development, :test do ... gem 'dotenv-rails' ...
젬을 설치하려면 다음을 실행합니다.
bundle install
애플리케이션 홈 디렉터리에 .env라는 이름의 파일을 생성합니다.
파일을 열어 키와 토큰을 아래처럼 추가합니다.
#.env TWITTER_KEY=xxxxxxxxxxxxxx TWITTER_SECRET=xxxxxxxxxxxxxx
.gitignore 파일을 열어 방금 생성한 파일을 추가합니다.
#.gitignore ... # Ignore .env used for storing keys and access tokens .env
이렇게 하면 키와 액세스 토큰이 안전하게 보호됩니다! dotenv-rails 사용법에 대해 더 알고 싶다면 GitHub 페이지를 참고하세요.
이제 라우트 작업 차례입니다. 라우트 파일을 열고 아래 라우트를 추가하세요.
#config/routes.rb ... get '/auth/:provider/callback', to: 'sessions#create'
트위터 로그인 링크를 네비게이션에 추가해야 합니다. 네비게이션 파일을 열어 아래와 같이 수정하세요.
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Tuts Social</a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav navbar-right">
<li><%= link_to 'Home', root_path %></li>
<% if current_user %>
<li>Signed in as <%= current_user.name %></li>
<% else %>
<li><%= link_to "Sign in with Twitter", "/auth/twitter" %></li>
<% end %>
</ul>
</div>
</div>
</nav>위 코드의 핵심은 사용자가 로그인하지 않은 상태일 때만 트위터 로그인 링크를 보여주는 것입니다.
세션(Session) 생성하기
사용자 로그인을 처리하려면 세션 컨트롤러가 필요합니다. 컨트롤러 디렉터리에 해당 파일을 생성하고, 아래와 같이 작성하세요.
create 액션은 사용자를 위한 세션을 생성해 애플리케이션에 로그인된 상태로 만들어 줍니다. 이 액션이 없으면 사용자는 로그인할 방법이 없습니다.
#app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def create
@user = User.find_or_create_from_auth_hash(auth_hash)
session[:user_id] = @user.id
redirect_to root_path
end
protected
def auth_hash
request.env['omniauth.auth']
end
end이제 current_user 메서드가 필요합니다. 이 메서드를 통해 사용자의 로그인 여부를 확인할 수 있습니다.
app/controllers/application_controller.rb 파일을 열어 아래 코드를 추가합니다.
#app/controllers/application_controller.rb
...
def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
...User 모델 만들기
이제 User 모델을 생성합니다. 다음 명령어를 실행하세요.
rails generate model User provider:string uid:string name:string token:string secret:string profile_image:string
그러면 아래와 같은 마이그레이션 파일이 생성됩니다.
#xxxxxx_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :provider
t.string :uid
t.string :name
t.string :token
t.string :secret
t.string :profile_image
t.timestamps
end
end
end다음 명령어로 데이터베이스를 마이그레이션합니다.
rake db:migrate
User 모델 파일을 열어 아래와 같이 수정하세요.
#app/models/user.rb
class User < ApplicationRecord
def self.find_or_create_from_auth_hash(auth_hash)
user = where(provider: auth_hash.provider, uid: auth_hash.uid).first_or_create
user.update(
name: auth_hash.info.nickname,
profile_image: auth_hash.info.image,
token: auth_hash.credentials.token,
secret: auth_hash.credentials.secret
)
user
end
end위 코드는 사용자 정보 중 이름(name), 프로필 이미지(profile_image), 토큰(token), 시크릿(secret)을 저장합니다. 애플리케이션에서 더 많은 정보가 필요하다면 OmniAuth-Twitter 문서 페이지를 참고하세요.
세션 삭제하기 (로그아웃)
애플리케이션에서는 사용자가 로그아웃할 수 있는 기능도 제공해야 합니다. 이를 위해서는 SessionsController에 destroy 액션이 필요하며, 그다음 네비게이션에 로그아웃 링크를 추가하면 됩니다.
SessionsController에 destroy 액션을 추가합니다.
#app/controllers/sessions_controller.rb
...
def destroy
if current_user
session.delete(:user_id)
flash[:success] = "Sucessfully logged out!"
end
redirect_to root_path
end
...이어서 네비게이션에 로그아웃 링크를 추가해 아래와 같이 완성합니다.
#app/views/layouts/_navigation.html.erb
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Tuts Social</a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav navbar-right">
<li><%= link_to 'Home', root_path %></li>
<% if current_user %>
<li>Signed in as <%= current_user.name %></li>
<li><%= link_to 'Log Out', logout_path, method: :delete %></li>
<% else %>
<li><%= link_to "Sign in with Twitter", "/auth/twitter" %></li>
<% end %>
</ul>
</div>
</div>
</nav>방금 생성한 액션에 맞춰 라우트를 업데이트하기 위해 config/routes.rb 파일을 엽니다.
#config/routes.rb ... delete '/logout', to: 'sessions#destroy' ...
Rails 서버를 시작하고 브라우저에서 https://localhost:3000 에 접속해 결과를 확인해 보세요.
마무리
이 튜토리얼에서는 Rails 애플리케이션에 OmniAuth-Twitter를 적용하는 방법을 배웠습니다. User 모델에서 OmniAuth-Twitter를 통해 사용자 데이터를 가져오는 방법을 살펴보았고, 사용자의 로그인 및 로그아웃을 처리하는 SessionsController까지 직접 구현해 보았습니다.
즐거운 코딩 되시길 바랍니다. 다음에는 페이스북, 구글, 링크드인에서도 동일한 방식으로 소셜 로그인을 구현하는 방법을 다룰 예정입니다.