본문 바로가기
Ruby on Rails

📚 [Ruby on Rails] 카카오 로그인 기능 구현 (카카오 로그인 - JS)

by Nyanggu 2023. 4. 6.

사용버전

ruby "3.1.3"

gem "rails", "~> 7.0.3", ">= 7.0.3.1"

1. user 모델 생성

rails g model KakaoUser

 - db\migrate\20230329053439_create_kakao_users.rb 해당 경로 파일에 아래 코드 입력 (모델 만들면 자동 생성됨)

class CreateKakaoUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :kakao_users do |t|
      t.string :user_id
      t.string :email
      t.string :nickname
      t.string :remember_token

      t.timestamps
    end
  end
end

- 모델 만들어 줬으면 아래 명령어로 파일 업데이트

rake db:migrate

- app\models\kakao_user.rb 해당 경로 파일에 아래 코드 입력

class KakaoUser < ActiveRecord::Base
    before_save { self.user_id = user_id.downcase }
    before_create :create_remember_token

    def KakaoUser.new_remember_token
        SecureRandom.urlsafe_base64  # 키 랜덤 생성
    end

    def KakaoUser.encrypt(token)
        Digest::SHA1.hexdigest(token.to_s)  # 키 암호화
    end

    private

    def create_remember_token
        self.remember_token = KakaoUser.encrypt(KakaoUser.new_remember_token)  # 토큰 정보 저장
    end
end

2. 필요한 컨트롤러 생성

rails g controller KakaoSessions

2-1 컨트롤러 파일 작성

class KakaoSessionsController < ApplicationController
    include ApplicationHelper

    def new
    end

    def create
        user = KakaoUser.find_by(user_id: params[:user_id].downcase)
        if user
            sign_in user
            render json: { success: true, message: "로그인 성공" }
        else
            # 새로운 사용자 생성
            @user = KakaoUser.new
            @user.user_id = params[:user_id]
            @user.email = params[:email]
            @user.nickname = params[:nickname]

            # 사용자 정보 저장
            if @user.save
                sign_in @user
                puts "회원가입 성공"
                render json: { success: true, message: "회원가입 성공" }
            else
                puts "회원가입 실패"
                render json: { success: false, message: "회원가입 실패" }
            end
        end
    end
    
    def destroy
        sign_out
        redirect_to root_url
    end
end

3. 라우트 설정

Rails.application.routes.draw do
  resources :users
  resources :sessions, only: [:new, :create, :destroy] 
  root  'sessions#new'

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

4. view 생성

<% if signed_in? %>
    <a id="kakao-login-btn"></a>
    <%= button_to "로그아웃", signout_path, method: "delete" %>
<% else %>
    <a id="kakao-login-btn"></a>
<% end %>

<%= form_tag "/kakao_sessions", id: "my-form" do %>
    <%= hidden_field_tag :user_id %>
    <%= hidden_field_tag :email %>
    <%= hidden_field_tag :nickname %>
    <%= hidden_field_tag :authenticity_token, value: form_authenticity_token %>
    <%= submit_tag "Sign in", style: "display: none" %>
<% end %>
	<button class="api-btn" onclick="unlinkApp()">앱 탈퇴하기</button>



//JS
<script>
  function unlinkApp() {
    Kakao.API.request({
      url: '/v1/user/unlink',
      success: function(res) {
        alert('success: ' + JSON.stringify(res))
      },
      fail: function(err) {
        alert('fail: ' + JSON.stringify(err))
      },
    })
  }

  Kakao.init('발급받은 JS KEY');
  console.log(Kakao.isInitialized());

  Kakao.Auth.createLoginButton({
    container: '#kakao-login-btn',
      success: function(authObj) {
        Kakao.API.request({
          url: '/v2/user/me',
          success: function(result) {
            const formData = new FormData();
            formData.append('user_id', result.id);
            formData.append('email', result.kakao_account.email);
            formData.append('nickname', result.properties['nickname']);

            fetch('/kakao_sessions', {
                method: 'POST',
                body: formData,
                headers: {
                'X-CSRF-Token': '<%= form_authenticity_token %>'
                }
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    // 로그인 성공 시 처리
                    location.href = "/";
                } else {
                    // 로그인 실패 시 처리
                }
            });
          },
          fail: function(error) {
            alert(
              'login success, but failed to request user information: ' +
                JSON.stringify(error)
            );
          }
        });
      },
      fail: function(err) {
        alert('failed to login: ' + JSON.stringify(err));
      }
  });
</script>

5. 헬퍼 작성

module KakaoSessionsHelper
    # 로그인시 세션 토큰을 만들고 쿠키에 저장
    def sign_in(user)
        remember_token = KakaoUser.new_remember_token
        cookies.permanent[:remember_token] = remember_token
        user.update_attribute(:remember_token, KakaoUser.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 = KakaoUser.encrypt(cookies[:remember_token])
        @current_user ||= KakaoUser.find_by(remember_token: remember_token)
    end

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

6. app\controllers\application_controller.rb 작성

 - application_controller에 session helper include (어플리케이션 전체에서 사용할 것이기 때문에) 

class ApplicationController < ActionController::Base
    protect_from_forgery with: :exception
    include KakaoSessionsHelper

    helper_method :signed_in?, :current_user

    private
  
    def check_signed_in
      redirect_to 'http://39.120.151.57:3000/signin' unless signed_in?
    end
end

7. 로그인 체크하고싶은 컨트롤러에 아래와 같이 작성

before_action :check_signed_in

 

댓글