Frond-end/React

[React] 엄청 간단한 카카오 로그인 구현

Nellie Kim 2024. 3. 6. 10:39
728x90

리액트, NEXTJS로 카카오 로그인 구현을 해보았다. 

출처 : https://developers.kakao.com/docs/latest/ko/kakaologin/common

 

 

진행한 카카오 로그인의 전체 흐름을 간단히 정리해보았다.

 

1. 맨 처음 사용자가 '카카오로그인' 눌렀을 시, 카카오 디벨로퍼 사이트에서 받은 REST API KEY 와 설정한 Redirect URI 로 카카오에 CODE 요청 → CODE 발급 

2. 그 CODE 로 카카오에게 TOKEN 요청    TOKEN 발급 (이 시점에 사용자와 앱이 연결됨!!)

3. 그 TOKEN으로 카카오에게 유저정보(ID가 담긴) 를 요청   유저정보(ID가 담긴) 발급

4. 그 유저정보를 백엔드에 회원가입 API로 전송 (TOKEN 값을 ID로 설정, 닉네임, 이름 등...)

5. 그 이후에 로그인 요청은 기존 TOKEN 검증처럼 똑같이 진행

 

 

리액트, NEXT.JS 로 카카오 로그인을 하는 방법이 여러가지 있는 것 같았으나

백엔드만 하다가 프론트를 처음 해봤기 때문에 최대한 간단하게 하기위해서 ,

프론트에서는 CODE, TOKEN, 유저정보만 받고 바로 백엔드로 튀었다... 다른 방법은 나중에 차차 더 공부해보기로 해야겠다.  6시간 정도만에 정말 간단하게 구현이 완성되었다. 

 

단 3가지 파일만 필요했다. 

아래는 최종 코드이다. 

1. _app.tsx 

아래 한 줄 추가하기.

<script src="https://developers.kakao.com/sdk/js/kakao.js"
import "@styles/globals.css";
import "@styles/assets/css/style.css";
import "tailwindcss/tailwind.css";
import Script from 'next/script';

import React from "react";
import { Windmill } from "@roketid/windmill-react-ui";
import type { AppProps } from "next/app";

function MyApp({ Component, pageProps }: AppProps) {
  // suppress useLayoutEffect warnings when running outside a browser
  if (!process.browser) React.useLayoutEffect = React.useEffect;

  return (
    
    
    <Windmill usePreferences={true} dark={false}>
      <Script strategy='beforeInteractive' src="https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=mhvjuj53xv"/>
      {/* 카카오 로그인 위해 추가 */}
      <script src="https://developers.kakao.com/sdk/js/kakao.js"
          defer
        ></script>
      <Component {...pageProps} />
    </Windmill>
  );
}
export default MyApp;

 

 

2. kakaoLogin.ts

/* 카카오 로그인을 위한 API */


/** 카카오 요청 시 필요한 주소 값 등 */
// const KAKAO_SERVER = 'http://104.50.177.80:5000';
const KAKAO_SERVER = 'http://localhost:5000';
const REDIRECT_URI = KAKAO_SERVER + '/member/login/login';
const REST_API_KEY = 'd7831~~~~~~~~~~~~~5251'; //REST API KEY
const INITIAL_URL_BASE = 'https://kauth.kakao.com/oauth/authorize'; // '카카오로그인'을 클릭할 때 이동하는 URL 중 앞부분
const TOKEN_URL_BASE = 'https://kauth.kakao.com/oauth/token?grant_type=authorization_code'; // 카카오에서 CODE로 TOKEN을 받아오기 위한 BASE URL
const USER_INFO_URL = 'https://kapi.kakao.com/v2/user/me'; // 카카오에서 TOKEN으로 USER정보를 받아오기 위한 BASE URL
export const INITIAL_URL_BASE_FULL = INITIAL_URL_BASE + '?client_id=' + REST_API_KEY + '&redirect_uri=' + REDIRECT_URI + '&response_type=code'; // '카카오로그인'을 클릭할 때 이동하는 URL


/**  카카오에 getTokenFromKakao로 요청하면 받을 수 있는 응답 값*/
export interface TokenResponse {
    token_type?: string;
    access_token: string;
    refresh_token?: string;
    id_token?: string;
    expires_in?: number;
    refresh_token_expires_in?: string;
    scope?: string;
    token?: string;
    id?: number;
}

/**  카카오에 getUserFromKakao로 요청하면 받을 수 있는 응답 값*/
export interface UserInfo {
    id: number;
    connected_at: string;
    properties: {
        nickname: string;
        profile_image?: string; // 640x640
        thumbnail_image?: string; // 110x110
    };
}


/** 1. 카카오에서 CODE로 TOKEN을 받아오기 */
export default async function getTokenFromKakao(authCode: string) {
    const tokenUrl = `${TOKEN_URL_BASE}&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&code=${authCode}`;

    const response: TokenResponse = await fetch(tokenUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
    }).then((res) => res.json());

    if (response.access_token !== undefined) {
        console.log('response (카카오에서 토큰 값 받기 성공) ....................... : ', response)
        return response;
    }
}

/** 2. 카카오에서 TOKEN으로 USER정보를 받아오기 */
export async function getUserFromKakao({ access_token }: TokenResponse) {
    const response: UserInfo = await fetch(USER_INFO_URL, {
        headers: {
            'Content-Type': 'application/json',
            Authorization: `Bearer ${access_token}`,
        },
    }).then((res) => res.json());

    if (response.id !== undefined) {
        console.log('response (카카오에서 유저 정보 받기 성공) ....................... : ', response)
        return response;
    }

}

 

 

3. login.tsx

import React, { useState, useEffect } from "react";
import { signin, signup, LoginInfoModel } from "@apis/login";
import getTokenFromKakao, { getUserFromKakao, INITIAL_URL_BASE_FULL } from "@apis/kakaoLogin";
import { useRouter } from "next/router";


function LoginPage() {
  const router = useRouter();

  /**
  |--------------------------------------------------
  |  라우팅 함수 
  |--------------------------------------------------
  */

  /** 맨 처음 사용자가 '카카오로그인' 버튼을 눌렀을 시 */
  const handleKakaoLogin = () => {
      window.location.href = INITIAL_URL_BASE_FULL
  }

  async function moveHome() {
    window.location.href = "/member/mainPage/home"
  }

  /**
  |--------------------------------------------------
  | 카카오 로그인
  |--------------------------------------------------
  */
  async function kakaoLogin() {

    if(router.query.code !== undefined){
      console.log('router.query.code (라우터 쿼리로 코드 값 받기 성공)................: ', router.query.code);
    }

    /**1. 카카오 [코드]를 넣고 -> 카카오 [토큰]을 받아오기 */
    const response = await getTokenFromKakao(router.query.code as string);


    /**2. 카카오 [토큰]을 넣고 -> 카카오 [유저정보] (id, nickname 등.. )를 받아오기 */
    const responseUser = await getUserFromKakao({ access_token: response?.access_token as unknown as string }) // id, nickname 등이 담긴 객체

    const result = await signin({ token: responseUser?.id as unknown as string });
    if(result.status === 200){
      console.log('result (signin 호출 성공)............', result);
    }


    /** 3. 처음 로그인한 사람이면, 회원가입 페이지로 이동 시키고, 아니면 로그인 세션에 데이터 저장 */
    if (result.status == 400) { 
      const request = {
        "token": responseUser?.id as unknown as string,
        "nick": responseUser?.properties?.nickname,
        "loginType": "kakao"
      };
      await kakaoSignUp(request);

    } else if (result.status == 200) {
      // 로그인 토큰 저장
      sessionStorage.setItem("parkgolfMemberToken", result.data.accessToken);
      sessionStorage.setItem(
        "parkgolfMemberRefreshToken",
        result.data.refreshToken
      );
      console.log('sessionStorage...............', sessionStorage)
      window.location.href = "/member/mainPage/home"; // 페이지 이동
    } else {
    }
  }

  /**
|--------------------------------------------------
| 카카오 회원가입
|--------------------------------------------------
*/
  async function kakaoSignUp(request: LoginInfoModel) {
    if (request.token !== undefined) {
      console.log('request .....??', request)
      const resultSignUp = await signup(request);
      
      // 회원가입 성공하면 로그인 페이지로 라우팅
      if (resultSignUp.status == 200) {
        console.log(" ####################################  회원가입 완료 #################################### " )
        console.log('resultSignUp (signup 호출 성공)............', resultSignUp);
        router.push('/member/login/login')
      }
    }
  }


  /**
  |--------------------------------------------------
  | ⚡ 5. useEffect
  |--------------------------------------------------
  */
  useEffect(() => {
    kakaoLogin();
  }, [router.query.code]);


  /*
  |--------------------------------------------------
  | 🎨 화면
  |--------------------------------------------------
  */
  return (
    <div>
      <body className="white-type">
        <div className="header">
          <div className="top-bar">
            <div className="prev" >
              <a className="prev-btn" onClick={moveHome}>
                <i className="icon icon-prev" ></i>
                로그인
              </a>
            </div>
          </div>
        </div>
        <div className="section">
          <div className="wrapper">
            <div className="kakao-update-box">
              <div className="kakao-icon"></div>
              <p className="update-message">카카오톡 간편 로그인</p>
            </div>
          </div>
        </div>
        <div className="btn-wrap bottom-full-type">
          <a className="btn gray-btn" onClick={moveHome}>닫기</a>
          <a className="btn kakao-btn" onClick={handleKakaoLogin}>
            <i className="icon ico-kakao" ></i> 카카오로그인
          </a>
        </div>
      </body>
    </div>
  );
}

export default LoginPage;

 

 

 

 

 

참고 

 

https://developers.kakao.com/docs/latest/ko/kakaologin/common

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

https://velog.io/@leesangsu200/%EC%B9%B4%EC%B9%B4%EC%98%A4-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EB%A6%AC%EC%95%A1%ED%8A%B8

 

velog

 

velog.io