ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • React를 활용한 OAuth 2.0 카카오 로그인 연동
    Frontend/React 2023. 4. 24. 22:28

    네이버, 카카오, 구글 3사의 소셜 로그인을 오로지 React 웹 서버를 통한 구현방식을 우선 소개하고,

    백엔드를 이용하여 보안을 강화하는 방법도 함께 소개하겠다.

     

    OAuth 란?

    구글, 페이스북, 트위터와 같은 다양한 플랫폼의 특정한 사용자 데이터에 접근하기 위해 제3자 클라이언트(우리의 서비스, 앱)가 사용자의 접근 권한을 위임(Delegated Authorization)받을 수 있는 표준 프로토콜이다.

    쉽게 말하자면, 우리의 서비스가 우리 서비스를 이용하는 유저의 타사 플랫폼 정보에 접근하기 위해서 권한을 타사 플랫폼으로부터 위임 받는 것 이다.

     

    우선 전체적인 OAuth의 구동방식은 아래와 같다.

    우리는 우선 웹서버를 통하여 구현할 예정이니 위 그림을 참고하자.

    우리의 웹 서버에서 카카오 로그인 요청을 하면 카카오의 인증서버는 AccessToken이라는 코드를 준다.

     

    이 코드(AccessToken)를 다시 카카오 서버에 인증 요청을 하면,

    인증 서버가 token에 대한 유효성을 검증이 완료가 되면 로그인한 사용자에 대한 정보에 대한 권한을

    위임받을 수 있어 사용자에 대한 정보를 받아올 수 있다. (사용자가 동의한 항목에 대하여)

     

    아래는 React + 카카오를 활용하여 구현한 예제이다.

     

    1. kakao developers에 앱 등록

    https://developers.kakao.com/console/app

    우선 위 사이트에 들어가 앱을 등록해준다.

     

    앱 등록과 동시에 선행되어야 되는 몇몇 작업들이 있다.

     

    1-1. 카카오 로그인 ON 및 동의 항목 설정

    1-2. 플랫폼 > Web 등록 및 Redirect URI 등록하러가기 클릭

     

    저는 vite로 진행하여 기본 포트가 5173이지만, cra로 시작했다면 3000일 것입니다!

    도메인이 있다면 도메인을 입력합니다.

     

    그리고 위에 아래에 보이는 등록하러가기를 눌러줍니다.

    카카오 로그인 활성화를 ON 해주고, 아래의 Redirect URI를 입력해줍니다.

    처음에는 많은 예제에서 설명하듯, "[자신의 도메인]/auth/kakao/callback"으로 해줍니다.

    저는 아래와 같이 설정했습니다.

    카카오 로그인에 성공하면, 위 URI로 accessToken을 발급해줍니다.

    우리는 이 accessToken을 통하여 로그인 뿐 아니라 사용자 정보에 대한 권한을 위임받을 수 있습니다.

     

    이렇게 하면 기본 설정을 마쳤고, 이를 React 프로젝트에 녹여봅시다.

    // App.tsx
    
    import { Routes, Route } from 'react-router-dom';
    
    import Home from 'pages/Home';
    import Auth from 'pages/Auth';
    import Profile from 'pages/Profile';
    
    function App() {
      return (
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/auth/kakao/callback" element={<Auth />} />
          <Route path="/profile" element={<Profile />} />
          <Route path="*" element={<Navigate to="/" replace={true} />} />
        </Routes>
      );
    }
    
    export default App;

    먼저 저는 위와 같은 3개의 페이지를 만들어 구성했습니다.

    1. Home > 바로 진입했을 때의 페이지

    2. Auth > 카카오로부터 accesstoken을 받기 위한 리다이렉트 페이지

    3. Profile > 카카오 로그인을 통한 정보가 잘 받아와졌는지 확인하는 페이지

     

    실제로 서비스화를 할 때엔 보안상 2번까지 프론트엔드에서 구성하고 3번의 정보는 웹서버가 인증서버를 통해 직접 받지 않고, 백엔드 서버를 한번 거쳐 구현하는 것이 훨씬 좋지만, 현재의 예제에서는 전체적인 OAuth의 흐름을 이해하기 위해 웹서버에서만 모두 진행하도록 합니다.

     

    먼저 config.ts에 OAuth를 구현하기 위해 필요한 모든 정보들을 담아줍니다.

    // config.ts
    
    export const REST_API_KEY = '';
    export const REDIRECT_URI = 'http://localhost:5173/auth/kakao/callback';
    export const KAKAO_AUTH_URL = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`;
    export const CLIENT_SECRET = '';

    이 때, REST_API_KEY와 CLIENT_SECRET은 아래 이미지 경로를 통해 확인할 수 있습니다.

    보안에 들어가면 바로 Secret이 나오지 않고 생성하기를 눌러야 생성됩니다.

    config 설정을 마쳤으니 페이지를 구성해봅니다.

     

    먼저 Home.tsx를 작성해줄텐데, 첫 진입 화면입니다.

    로그인이 되어있지 않으면 로그인 하러가기를 보여주고,

    로그인이 되어있다면 마이페이지로 갈 수 있는 버튼을 보여줍니다. 

     

    이 때, 로그인이 되어있는지의 정보를 전역으로 관리하기 위해 저는 recoil을 사용했습니다.

    // Home.tsx
    import { useRecoilValue } from 'recoil';
    import { useNavigate } from 'react-router-dom';
    
    import { KAKAO_AUTH_URL } from 'config';
    import { isLoggedInAtom } from 'atoms/isLoggedInAtom';
    
    export default function Home() {
      const isLoggedIn = useRecoilValue(isLoggedInAtom);
      const navigate = useNavigate();
      const onClickBtn = () => {
        navigate('/profile');
      };
      return (
        <div>
          <p>Welcome!</p>
          <p>{isLoggedIn ? <button onClick={() => onClickBtn()}>마이페이지</button> : <Login />}</p>
        </div>
      );
    }
    
    function Login() {
      return <a href={KAKAO_AUTH_URL}>카카오로 로그인하기</a>;
    }

    여기에서 "카카오로 로그인하기"를 누르게 되면, 우리에게 익숙한 카카오 로그인 창이 뜨게되고,

    로그인에 성공하면 우리가 설정한 redirectURI로 이동합니다.

    // Auth.tsx
    import { REST_API_KEY, REDIRECT_URI, CLIENT_SECRET } from 'config';
    
    export default function Auth() {
      const params = new URL(document.URL).searchParams;
      const code = params.get('code');
    
      return <div>{code}</div>;
    }

    Auth를 위와 같이 구성하고 redirect가 되면 화면에 카카오로부터 받은 accessToken을 부여받을 수 있습니다.

    이는 URL을 통해 제공되므로 URL 객체에서 searchParams method를 통해 받아올 수 있습니다.

     

    이제 우리는 이것을 이용하여, 로그인한 유저가 동의한 정보들에 대해 조회하려고 합니다.

    일반적으로 프론트엔드와 백엔드를 나누게 되면 이 요청은 백엔드에서 수행하게됩니다. 

    실제로 프론트엔드는 코드가 노출되는 문제점이 있기도 하고 보안을 더 강화하기 위해 Auth Server와 서버에서 발급한 JWT를 함께 적용하여 사용하기도 합니다. 

     

    오늘의 예제에서는 웹서버를 통해서만 구현해보는 것이므로, 백엔드 서버 없이 웹서버를 통해 해당 요청을 진행하겠습니다.

     

    우선, 웹 서버에서 해당 요청을 진행하게 되면 CORS 문제를 마주하게 됩니다.

    *CORS : https://lulu-developmentlog.tistory.com/165

     

    그래서 해당 문제를 피하기 위해 우리는 index.html에 <head> 태그 아래 <script> 코드를 추가해줍니다.

    // index.html
    <script src="https://developers.kakao.com/sdk/js/kakao.min.js"></script>

    그리고 아까 Auth.tsx로 돌아와, 인증 서버를 통해 발급 받은 code(Access token)를 input 으로 하는 

    사용자 정보에 대한 요청을 작성해보자.

    import { useNavigate } from 'react-router-dom';
    import { useSetRecoilState } from 'recoil';
    import { useEffect } from 'react';
    import axios from 'axios';
    import qs from 'qs';
    
    import { REST_API_KEY, REDIRECT_URI, CLIENT_SECRET } from 'config';
    import { isLoggedInAtom } from 'atoms/isLoggedInAtom';
    
    export default function Auth() {
      const navigate = useNavigate();
      const setIsLoggedIn = useSetRecoilState(isLoggedInAtom);
    
      const params = new URL(document.URL).searchParams;
      const code = params.get('code');
    
      const getToken = async () => {
        const payload = qs.stringify({
          grant_type: 'authorization_code',
          client_id: REST_API_KEY,
          redirect_uri: REDIRECT_URI,
          code: code,
          client_secret: CLIENT_SECRET,
        });
        try {
          const res = await axios.post('https://kauth.kakao.com/oauth/token', payload);
          window.Kakao.init(REST_API_KEY); // Kakao Javascript SDK 초기화
          window.Kakao.Auth.setAccessToken(res.data.access_token); // access token 설정
          setIsLoggedIn(true);
          navigate('/profile');
        } catch (err) {
          console.log(err);
        }
      };
    
      useEffect(() => {
        getToken();
      }, []);
    
      return <div>로그인 진행중입니다..</div>;
    }

    인증서버로 부터 받은 code를 바로 해당 인증서버가 제공하는 api `https://kauth.kakao.com/oauth/token` 에 위와 같이 요청을 보낸다. 요청을 보내고 난 뒤, 로그인에 성공하면 `navigate('/profile')`를 통해 profile 페이지로 이동시켜 정보가 잘 불러와졌는지 확인한다. 

     

    만약 실패하면 다시 로그인을 시도할 수 있게 메인 페이지로 네비게이트 되도록 설정했다.

     

    마지막으로 정보가 잘 불러와졌는지 확인하는 Profile.tsx

     

    import { useRecoilValue } from 'recoil';
    import { useState, useEffect, Fragment } from 'react';
    import { useNavigate } from 'react-router-dom';
    
    import { isLoggedInAtom } from 'atoms/isLoggedInAtom';
    
    export default function Profile() {
      const isLoggedIn = useRecoilValue(isLoggedInAtom);
      const navigate = useNavigate();
    
      const [email, setEmail] = useState('');
      const [nickName, setNickName] = useState('');
      const [profileImage, setProfileImage] = useState('');
    
      const getProfile = async () => {
        try {
          const data = await window.Kakao.API.request({
            url: '/v2/user/me',
          });
          setEmail(data.kakao_account.email);
          setNickName(data.properties.nickname);
          setProfileImage(data.properties.profile_image);
        } catch (err) {
          console.log(err);
        }
      };
    
      const onClickBtn = () => {
        navigate('/');
      };
    
      useEffect(() => {
        getProfile();
      }, []);
    
      return (
        <Fragment>
          {isLoggedIn && (
            <div>
              <h2>WELCOME! {nickName} 🙌</h2>
              <h3>Your email is.. {email}</h3>
              <img src={profileImage}></img>
            </div>
          )}
          <button onClick={() => onClickBtn()}>Home으로 돌아가기</button>
        </Fragment>
      );
    }

    만약 정보가 잘 불러와졌다면 카카오로 로그인한 이름, 이메일, 프로필 사진이 보여질 것이고 실패하면 뜨지 않는다. 

     

    아래는 위 코드가 작성된 github 주소입니다.

     

    https://github.com/YoonJeongLulu/react-kakao-login

    백엔드 서버를 통하면 AcessToken을 백엔드로 보내어 이 토큰을 통해 백엔드 서버가 직접 인증 서버에 요청하여 검증하고 

    추가적인 보안(+JWT)를 거쳐서 웹서버에는 유저에 대한 정보나 jwt Token을 발급할 수도 있다.

     

    다음에는 백엔드 JWT를 append하여 작성해야겠다!

Designed by Tistory.