๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ“— React

[React] OAuth Github ๋กœ๊ทธ์ธ ๊ตฌํ˜„ํ•˜๊ธฐ

by Tamii 2021. 6. 5.
๋ฐ˜์‘ํ˜•

์ „๋ถ€ํ„ฐ ๋„ˆ๋ฌด๋‚˜๋„ ํ•ด๋ณด๊ณ  ์‹ถ์—ˆ๋˜  ๊นƒํ—ˆ๋ธŒ OAuth  ๋กœ๊ทธ์ธ์„ ์„ฑ๊ณต์ ์œผ๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค. 

๊ตฌํ˜„๋‹จ๊ณ„์™€ ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ด๋ณผ ์˜ˆ์ •!


 

๐Ÿ“Œ ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ

* React  ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ (jsx) ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•œ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค!

* BE์™€ ํ•จ๊ป˜ํ•œ ํ”„๋กœ์ ํŠธ๋กœ BE ๋‹จ์—์„œ๋Š” access-token ๋ฐœ๊ธ‰๊ณผ ์ •๋ณด ์š”์ฒญ์„ ๋งก์•„ ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

* FE๋‹จ์—์„œ๋Š” code์š”์ฒญํ•œํ›„์— Github ๊ณ„์ • ์ •๋ณด๋ฅผ ๋ฐ”๋กœ ์š”์ฒญํ–ˆ์Šต๋‹ˆ๋‹ค.

 

 

๊นƒํ—ˆ๋ธŒ ๋กœ๊ทธ์ธ ๊ตฌํ˜„์„ ๊ฒ€์ƒ‰ํ•ด๋ณด๋ฉด  ๊ตฌํ˜„ํ•˜๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์ง€๋งŒ  ํฌ๊ฒŒ ๋‘๊ฐ€์ง€๋กœ ๋‚˜๋‰˜๋Š” ๋Š๋‚Œ์„ ๋ฐ›์•˜๊ณ , 2๋ฒˆ ๋ฐฉ๋ฒ•์œผ๋กœ ์ฒ˜๋ฆฌํ–ˆ๋‹ค .

1) FE๋‹จ์—์„œ code ์š”์ฒญํ•œ ํ›„ POST ๋กœ jwt ์š”์ฒญ  : ์ฐธ๊ณ  (https://devhyun.com/blog/post/15)

2) โœ… FE๋‹จ์—์„œ code ์š”์ฒญํ•œ ํ›„ GET์œผ๋กœ jwt ์š”์ฒญ (BE์—์„œ POST ์ฒ˜๋ฆฌ)  


๐Ÿ“Œ ๊ตฌํ˜„ ๋‹จ๊ณ„ 

1)  ๊นƒํ—ˆ๋ธŒ์—์„œ  Application ๋“ฑ๋ก

2) client-id , client-secret ๋ฐœ๊ธ‰๋ฐ›๊ธฐ (redirect url ์„ค์ •) 

3) code ๋ฐœ๊ธ‰๋ฐ›๊ธฐ

4)access-token ๋ฐœ๊ธ‰๋ฐ›๊ธฐ

5)jwt ํ† ๊ทผ ๋ฐœ๊ธ‰๋ฐ›์•„ ์‚ฌ์šฉ 

 

 

 

1)  ๊นƒํ—ˆ๋ธŒ์—์„œ  Application ๋“ฑ๋ก

๊นƒํ—ˆ๋ธŒ - settings - Developer settings - OAuth Apps - new OAuth App

 

 

2) client-id , client-secret ๋ฐœ๊ธ‰๋ฐ›๊ธฐ (redirect url ์„ค์ •) 

new OAuth App์„ ์„ค์ •ํ•˜๊ฒŒ ๋˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด URL ์ž…๋ ฅ ๋ž€์ด ๋‚˜์˜ค๋Š”๋ฐ 

Homepage URL : ํ”„๋กœ์ ํŠธ web ์ฃผ์†Œ 

Authorization callback URL : ๊นƒํ—ˆ๋ธŒ ๋กœ๊ทธ์ธ ์‹œ redirect ๋  ์ฃผ์†Œ

 

-> ํ˜„์žฌ React๋กœ ๊ตฌํ˜„์ค‘์ด์–ด์„œ   ๋กœ๊ทธ์ธ์„ ์ฒ˜๋ฆฌํ•  Component ๋ฅผ ์—ฐ๊ฒฐํ•œ Route path๋ฅผ ์ ์œผ๋ฉด ๋œ๋‹ค.

ex) <Callback> component์—์„œ ์ฒ˜๋ฆฌํ•  ๊ฒฝ์šฐ 

          <Route path="/callback" component={Callback} />

client secerts๋Š” ๋ฐœ๊ธ‰ ๋‹น์‹œ์— ๋ณด์—ฌ์ง€๋‹ˆ ๊ฐœ์ธ์ ์ธ ๊ณต๊ฐ„์— ๊ธฐ๋กํ•˜๊ณ , ๊ณต๊ฐœ๋˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.

Homepage URL๊ณผ callback URL์€ ๋ฐฐํฌ ์ „ localhost๋กœ ์‚ฌ์šฉํ•˜๋‹ค ๋ฐฐํฌ ์‹œ ๋ฐฐํฌ ์ฃผ์†Œ๋กœ ๋ณ€๊ฒฝํ•ด์ค˜์•ผ ํ•œ๋‹ค.

 

 

3) code ๋ฐœ๊ธ‰๋ฐ›๊ธฐ 

 

ํ•˜๋‹จ url ์— code GET์š”์ฒญ ๋ณด๋‚ด๊ธฐ 

https://github.com/login/oauth/authorize?client_id=๋ณธ์ธ์˜clientid&์„ ํƒ์‚ฌํ•ญ&redirect_uri=๋ณธ์ธ์˜redircet์ฃผ์†Œ

 

<LoginBtn.jsx>

import React from 'react';
import styled from 'styled-components';

const LoginBtn = () => {
  const loginUri = `https://github.com/login/oauth/authorize?client_id=๋ณธ์ธ์˜cliendid&scope=repo:status read:repo_hook user:email&redirect_uri=http://localhost:3000/callback`;

  return (
    <>
      <GithubBtn href={loginUri}></GithubBtn>
    </>
  );
};

const GithubBtn = styled.a`
//์ƒ๋žต
`;

export default LoginBtn;

๋กœ๊ทธ์ธ๋ฒ„ํŠผ์— a link๋ฅผ ๊ฑธ์–ด ๋ฒ„ํŠผ ํด๋ฆญ์‹œ ํ•ด๋‹น loginUri๋กœ ์—ฐ๋™๋˜๋„๋กํ–ˆ๋‹ค.

์ฟผ๋ฆฌ์ŠคํŠธ๋ง์œผ๋กœ ๋ฐœ๊ธ‰๋ฐ›์€ clientid , ์ง€์ •ํ–ˆ๋˜ redirect ์ฃผ์†Œ , ๊ทธ์™ธ ๋กœ๊ทธ์ธ ์‹œ ๋ ˆํฌ๊ด€๋ จํ•˜์—ฌ ๋ฌผ์–ด๋ณผ๊ฑด์ง€๋“ฑ์˜ ์˜ต์…˜์„ ๋„ฃ์–ด ์š”์ฒญํ•˜๋ฉด code๋ฅผ url๋กœ ์ „๋‹ฌ๋ฐ›์„ ์ˆ˜ ์ž‡๋‹ค . 

 

 

 <App.jsx>

function App() {
  return (
    <Router>
      <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/callback" component={Callback} />
      </Switch>
    </Router>
  );
}

App.jsx ์— ๋กœ๊ทธ์ธ์„ ์ฒ˜๋ฆฌํ•  component์— path ํ•ด๋†“๊ณ  

 

<Callback.jsx>

import { useEffect } from 'react';
import qs from 'qs';
import Loader from './Loader';

const Callback = ({ history, location }) => {
  const authUri = `BE์™€ํ˜‘์˜ํ•œ ์ฃผ์†Œ`;

  useEffect(() => {
    const getToken = async () => {
      const { code } = qs.parse(location.search, {
        ignoreQueryPrefix: true,
      });

      try {
        const response = await fetch(`${authUri}?code=${code}`);
        const data = await response.json();

        localStorage.setItem('token', data.jwt);
        localStorage.setItem('ProfileURL', data.avatar_url);

        history.push('/');
      } catch (error) {}
    };

    getToken();
  }, [location, history, authUri]);

  return <Loader />;
};

export default Callback;

Callback ์ปดํฌ๋„ŒํŠธ์—์„œ ์š”์ฒญํ•œ ๊ฒƒ๋“ค์„ ์ฒ˜๋ฆฌํ•œ๋‹ค .

 

์š”์ฒญ ํ›„  qs ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด code๋ฅผ ํŒŒ์‹ฑํ•ด์„œ ์–ป์–ด๋‚ธ ํ›„ -> ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์œผ๋กœ GET์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค .

๋กœ๊ทธ์ธ ์™„๋ฃŒ ํ›„์—๋Š” history pushํ•˜์—ฌ ๋‹ค์‹œ Homeํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•˜๊ฒŒ๋” ์„ค์ • 

 

 

 

 4)access-token ๋ฐœ๊ธ‰๋ฐ›๊ธฐ  5)jwt ํ† ๊ทผ ๋ฐœ๊ธ‰๋ฐ›์•„ ์‚ฌ์šฉ 

 

BE ๋‹จ์—์„œ access-token์„๋ฐœ๊ธ‰๊ณผ์ •๋ณด ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜์—ฌ  ์ „๋‹ฌํ•ด์คŒ์œผ๋กœ

response๋กœ ๋ฐ›์•„์„œ ์›ํ•˜๋Š” data๋Š”  localStorage์— ๋‹ด์•„์„œ ์‚ฌ์šฉํ•œ๋‹ค.

 

 

 

์ง„ํ–‰ ๋‹น์‹œ ๋ฏธ๋ฆฌ BE์™€ ํ˜‘์˜ํ•ด์„œ ๊นƒํ—ˆ๋ธŒ ํ”„๋กœํ•„ ์ด๋ฏธ์ง€ url๋„ ์š”์ฒญํ•ด๋†จ์–ด์„œ, 

jwt์™€ ํ•จ๊ป˜ ์ด๋ฏธ์ง€url์„ ์ „๋‹ฌ๋ฐ›์•˜๋‹ค . 

 

 

 

 

๊ทธ ํ›„ ๋งˆ์Œ๊ป ์‚ฌ์šฉ!!!!!!!! 

import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import LoginBtn from './LoginPage';

const AccountModal = ({ setProfileURL }) => {
  const [isLogin, setIsLogin] = useState(false);
  
  const token = localStorage.getItem('token');

  useEffect(() => {
    if (token) {
      setIsLogin(true);
    }
  }, [token]);


  const handleLogOut = () => {
    setIsLogin(false);
    setProfileURL(null);
    localStorage.removeItem('token');
    localStorage.removeItem('ProfileURL');
  };

  return (
    <AccountModalDiv>
      {isLogin ? (
        <AccountLogout onClick={() => handleLogOut()}>๋กœ๊ทธ์•„์›ƒ</AccountLogout>
      ) : (
        <AccountModalItem>
          <LoginBtn />
        </AccountModalItem>
      )}
    </AccountModalDiv>
  );
};


export default AccountModal;

localStorage์˜ token์กด์žฌ ์œ ๋ฌด์— ๋”ฐ๋ผ ๋กœ๊ทธ์ธ๊ณผ ๋กœ๊ทธ์•„์›ƒ์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ๊ณ , 

๋กœ๊ทธ์ธ ํ›„์— ์‚ฌ์šฉ์ž์ด๋ฏธ์ง€๊ฐ€ ๊นƒํ—ˆ๋ธŒ ์ด๋ฏธ์ง€๋กœ ๋ฐ”๋€Œ๋Š” ๊ท€์—ฌ์šด ๊ธฐ๋Šฅ๋„ ๊ตฌํ˜„ํ–ˆ๋‹ค!!

 

 

 

 

 FE๋‹จ์—์„œ POST ํ•˜๋Š” ๋ฐฉ๋ฒ•์€  ํƒ€๋ธ”๋กœ๊ทธ์— ๋งŽ์•„ ,

BE์™€ ํ˜‘์—…์‹œ ํ™œ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์ฒ˜๋ฆฌํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค!

 

 

 

 

์ฐธ๊ณ  (https://velog.io/@d-h-k/Oauth-%EC%B0%8D%EB%A8%B9%ED%95%B4%EB%B3%B4%EA%B8%B0-with-POSTMAN%EC%8B%A4%EC%8A%B5)

์ฐธ๊ณ (https://devhyun.com/blog/post/15)

๋Œ“๊ธ€