๐Ÿ” ๋ฆฌ์•กํŠธ๋กœ ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ (Google OAuth)

Doozuuยท2023๋…„ 7์›” 22์ผ
9

React

๋ชฉ๋ก ๋ณด๊ธฐ
20/23
post-thumbnail
post-custom-banner

๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ๊ณผ์ • ์ดํ•ดํ•ด๋ณด๊ธฐ

๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ์˜ ์ „์ฒด์ ์ธ ๊ณผ์ •์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. ํ”„๋ก ํŠธ์—์„œ ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ๋งŒ๋“ ๋‹ค.
  2. ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ์ฐฝ์„ ๋„์›Œ์ค€๋‹ค.
  3. ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ๊ธ€ ์ฐฝ์—์„œ ๋กœ๊ทธ์ธ์„ ๋งˆ์น˜๋ฉด ๊ตฌ๊ธ€์—์„œ access token์„ ๋ณด๋‚ด์ค€๋‹ค.
    (๋กœ๊ทธ์ธ ํ›„ ์ด๋™ํ•œ ์ฃผ์†Œ(redirect_uri)์— code๋ผ๋Š” ๋ถ€๋ถ„์ด ์ƒ๊ธด๋‹ค.)
  4. code์— ์žˆ๋Š” ๋ถ€๋ถ„์„ ์ถ”์ถœํ•ด ๋ฐฑ์—”๋“œ๋กœ ๋ณด๋‚ธ๋‹ค.
  5. ๋ฐฑ์—”๋“œ์—์„œ ์œ ํšจํ•œ ํ† ํฐ์ธ์ง€ ๊ฒ€์ฆํ•œ ํ›„, response๋กœ access token๊ณผ ๊ฐ€์ž… ์—ฌ๋ถ€๋ฅผ ๋ณด๋‚ด์ค€๋‹ค.
  6. access token์„ localstorage์— ์ €์žฅํ•˜๊ณ  ๊ฐ€์ž… ์—ฌ๋ถ€์— ๋”ฐ๋ผ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ ํ˜น์€ ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ redirect ์‹œ์ผœ์ค€๋‹ค.
  7. ์ฒ˜์Œ access token์„ localstorage์— ์ €์žฅํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” setItem์ด ์ž˜ ์ž‘๋™๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์–ด window.location.reload()๋กœ ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•ด์ค€๋‹ค.



๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ๊ตฌํ˜„

  1. ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ๋งŒ๋“ ๋‹ค.

  2. ๋ฒ„ํŠผ ํด๋ฆญ์‹œ ์•„๋ž˜ url๋กœ ์ด๋™์‹œํ‚จ๋‹ค. (๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™๋จ)

https://accounts.google.com/o/oauth2/v2/auth?
client_id=${process.env.REACT_APP_GOOGLE_AUTH_CLIENT_ID}
&redirect_uri=https://bageasy.vercel.app/loading
&response_type=code
&scope=email+profile
  • clitent_id : ๊ตฌ๊ธ€ ํด๋ผ์šฐ๋“œ์—์„œ ์„ค์ •ํ•œ ํด๋ผ์ด์–ธํŠธ id๋ฅผ ๋„ฃ์–ด์ค€๋‹ค.
    ๊ตฌ๊ธ€ ํด๋ผ์šฐ๋“œ ์„ค์ •์€ ์˜†์˜ ํฌ์ŠคํŒ… ์ฐธ๊ณ  -> ๊ตฌ๊ธ€ ํด๋ผ์šฐ๋“œ ์„ค์ •
    ๐Ÿ” ๋ณด์•ˆ์„ ์œ„ํ•ด env ํŒŒ์ผ์— ๋„ฃ์–ด์„œ ๋ถˆ๋Ÿฌ์˜จ๋‹ค.
  • redirect_uri : ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ํ›„ ์ด๋™ํ•  ์ฃผ์†Œ๋ฅผ ๋„ฃ์–ด์ค€๋‹ค.
    โš ๏ธ ์ฃผ์˜ํ•  ์  : ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์ฃผ์†Œ๋ฅผ ์„œ๋ฒ„๊ฐ€ ์•„๋‹Œ ํด๋ผ์ด์–ธํŠธ๋กœ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.
    -> ๋กœ๊ทธ์ธ ํ›„ ํด๋ผ์ด์–ธํŠธ์—์„œ access token์„ ๋ฐ›์€ ๋’ค ์„œ๋ฒ„๋กœ ๋„˜๊ฒจ์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ, ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์ฃผ์†Œ๊ฐ€ ์„œ๋ฒ„๋กœ ๋˜์–ด ์žˆ์œผ๋ฉด ํด๋ผ์ด์–ธํŠธ์—์„œ access token๋ฅผ ๋ฐ›์„ ์ˆ˜๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ.
    -> ๋‚˜๋Š” ๋ฐฑ์—”๋“œ์—์„œ access token์„ ๊ฒ€์ฆํ•  ๋™์•ˆ ๋กœ๋”ฉ ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ ์ฃผ๋„๋ก ์„ค์ •ํ–ˆ๋‹ค.
  • scope : ๋ฐ›์„ ์ •๋ณด์˜ ๋ฒ”์œ„๋ฅผ ์ ์–ด์ค€๋‹ค. ๋‚˜๋Š” ์ด๋ฉ”์ผ๊ณผ ํ”„๋กœํ•„์„ ๋ฐ›๋„๋ก ์„ค์ •ํ–ˆ๋‹ค.

GoogleLoginPage.js

const GoogleLogin = () => {
  const navigate = useNavigate();

  const handleNavigateBack = () => {
    navigate(-1);
  };

  const handleLogin = () => {
    // ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™์‹œํ‚ค๊ธฐ
    window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?
		client_id=${process.env.REACT_APP_GOOGLE_AUTH_CLIENT_ID}
		&redirect_uri=${process.env.REACT_APP_GOOGLE_AUTH_REDIRECT_URI}
		&response_type=code
		&scope=email profile`;
  };

  return (
    <SignUpContainer>
      <ArrowIcon src={Arrow} onClick={handleNavigateBack} />
      <Copy>์•ˆ๋…•ํ•˜์„ธ์š”</Copy>
      <Copy>๊ตฌ๊ธ€ ๊ณ„์ •์ด ์žˆ๋‚˜์š”?</Copy>
      <Character src={Duck} />
      <GBtn src={GoogleBtn} onClick={handleLogin} />
    </SignUpContainer>
  );
};

  1. ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ์„ ๋งˆ์น˜๋ฉด ๊ธฐ์กด์— ์„ค์ •ํ–ˆ๋˜ redirect_uri(๋‚ด๊ฐ€ ์„ค์ •ํ•ด๋‘” ๋กœ๋”ฉ ํŽ˜์ด์ง€)๋กœ ์ด๋™๋œ๋‹ค.
  2. ํ˜„์žฌ ์ ‘์† url์— ์˜ค๋ฅธ์ชฝ๊ณผ ๊ฐ™์ด code๊ฐ€ ๋ฐ›์•„์ง„๋‹ค. -> code=eydg123sadsad,,,
  3. code ๋’ท ๋ถ€๋ถ„์— ๋‚˜์˜ค๋Š” ๋ถ€๋ถ„์„ ๋ฐฑ์—”๋“œ๋กœ ๋ณด๋‚ด์ค€๋‹ค.
  4. ๋ฐฑ์—”๋“œ์—์„œ code๊ฐ€ ์œ ํšจํ•œ ํ† ํฐ์ธ์ง€ ๊ฒ€์ฆ์„ ๋งˆ์น˜๊ณ  access token์„ ๋ณด๋‚ด์ฃผ๋ฉด localStorage์— ์ €์žฅํ•œ๋‹ค.
  5. ์ด๋•Œ ๋ฐฑ์—”๋“œ์—์„œ response๋กœ ์‹ ๊ทœ/๊ธฐ์กด ํšŒ์› ์—ฌ๋ถ€๋ฅผ ํ•จ๊ป˜ ๋ฐ›์•„ ์‹ ๊ทœ ํšŒ์›์ด๋ฉด ๋‹‰๋„ค์ž„ ์„ค์ • ํŽ˜์ด์ง€๋กœ, ๊ธฐ์กด ํšŒ์›์ด๋ฉด ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ redirect ์‹œํ‚จ๋‹ค.

    ๐Ÿ“Œ ์—ฌ๊ธฐ์„œ ์ค‘๊ฐ„์— ๋ฐœ์ƒํ•œ ๋ฌธ์ œ์ 

    ์ฒ˜์Œ ๊ฐ€์ž…์‹œ localstorage์— ํ† ํฐ์ด ๋ฐ”๋กœ ์ €์žฅ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ๋‹ค.
    (์ด๋ฏธ ๊ฐ€์ž…ํ•œ ๊ฒฝ์šฐ์—๋Š” ๋ฌธ์ œ x)
    ์›์ธ์„ ์ฐพ์•„๋ณด๋‹ˆ localStorage์˜ setItem ์ด๋ฒคํŠธ๋Š” localstorage๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๊ฐฑ์‹ ๋˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ window๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ํŠธ๋ฆฌ๊ฑฐ๊ฐ€ ๋œ๋‹ค๊ณ  ํ•œ๋‹ค.
    ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด window.location.reload()๋ฅผ ์ด์šฉํ•ด ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•ด์ฃผ๋‹ˆ ํ•ด๊ฒฐ๋๋‹ค.
    ๊ด€๋ จ ํฌ์ŠคํŒ… : REACT) localstorage๊ฐ€ ๋ฐ”๋กœ ์ €์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด?

LoadingPage.js

const Loading = () => {
  const navigate = useNavigate();

  // ์ด๋ฏธ ๊ฐ€์ž…ํ•œ ์œ ์ €์ผ ์‹œ : ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™
  const handleHome = () => {
    navigate("/home");
    window.location.reload();
  };

  // ์ฒ˜์Œ ๊ฐ€์ž…ํ•œ ์œ ์ €์ผ ์‹œ : ๋‹‰๋„ค์ž„ ์„ค์ • ํŽ˜์ด์ง€๋กœ ์ด๋™
  const handleNickName = () => {
    navigate("/nickname");
    window.location.reload();
  };

  // ํ˜„์žฌ url์—์„œ code ๋ถ€๋ถ„ ์ถ”์ถœ
  const params = new URLSearchParams(window.location.search);
  const code = params.get("code");

  const handleLoginPost = async code => {
    const data = {
      code: code,
    };
    try {
      const res = await axios.post(
        "https://server.bageasy.net/auth/login",
        data,
      );
       // ํ† ํฐ localstorage์— ์ €์žฅ
       const accessToken = res.data.accessToken;
       localStorage.setItem("bagtoken", accessToken);
       // ์‹ ๊ทœ/๊ธฐ์กด ํšŒ์› ์—ฌ๋ถ€์— ๋”ฐ๋ผ ํŽ˜์ด์ง€ ์ด๋™
       res.data.isExistingMember ? handleHome() : handleNickName();
    } catch (error) {
      console.log(error);
    }
  };

  useEffect(() => {
    if (code) {
      handleLoginPost(code);
    } else {
      console.log("๋กœ๊ทธ์ธ ์žฌ์‹œ๋„ํ•˜์„ธ์š”.");
    }
  }, [code, navigate]);

  return (
    <LoadingConatiner>
      <LoadingIcon src={loading} />
      <H2>๋กœ๊ทธ์ธ์ค‘์ž…๋‹ˆ๋‹ค...</H2>
    </LoadingConatiner>
  );
};



+ ์ถ”๊ฐ€ ์ •๋ณด

@react-oauth/google์„ ํ™œ์šฉํ•˜๋ฉด ์•„์ฃผ ๊ฐ„๋‹จํ•˜๊ฒŒ access token์„ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค.(์œ ์ € ์ •๋ณด๋„ ํ”„๋ก ํŠธ์—์„œ ๋ฐ”๋กœ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ์Œ.)

@react-oauth/google ์„ค์น˜

npm install @react-oauth/google@latest

๋””์ฝ”๋” ์„ค์น˜

npm install jwt-decode

๊ตฌํ˜„ ์ฝ”๋“œ

import { GoogleLogin } from "@react-oauth/google";
import { GoogleOAuthProvider } from "@react-oauth/google";
import jwt_decode from "jwt-decode";

const GoogleLoginButton = () => {
  const clientId ="";

  return (
    <>
      <GoogleOAuthProvider clientId={clientId}>
        <GoogleLogin
          onSuccess={credentialResponse => {
            console.log(jwt_decode(credentialResponse.credential));
          }}
          onError={() => {
            console.log("Login Failed");
          }}
        />
      </GoogleOAuthProvider>
    </>
  );
};

export default GoogleLoginButton;

์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์„ ์ž์„ธํžˆ ์•Œ ์ˆ˜ ์žˆ๋‹ค. https://www.npmjs.com/package/@react-oauth/google?activeTab=code



์ฐธ๊ณ  ์ž๋ฃŒ

  1. https://velog.io/@nuri00/Google-OAuth-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84
  2. https://velog.io/@seongsimk/%EA%B5%AC%EA%B8%80-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84-react-oauthgoogle
  3. https://velog.io/@nuri00/Google-OAuth-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84
profile
๋ชจ๋“ ๊ฒŒ ์ƒˆ๋กญ๊ณ  ์žฌ๋ฐŒ๋Š” ํ”„๋ก ํŠธ์—”๋“œ ์ƒˆ์‹น
post-custom-banner

2๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2023๋…„ 7์›” 22์ผ

์ข‹์€ ๊ธ€์ด๋„ค์š”. ๊ณต์œ ํ•ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2023๋…„ 10์›” 6์ผ

๊ทธ๋Ÿผ @react-oauth/google๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์œ„์— ๋‹จ๊ณ„ ๋‹ค ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋Š”๊ฑด๊ฐ€์š”? ๊ทธ๋Ÿผ ์„œ๋ฒ„๋ž‘์€ ์–ด๋–ค๋ถ€๋ถ„์„ ์ฃผ๊ณ ๋ฐ›์œผ๋ฉด ๋˜๋Š”๊ฑธ๊นŒ์š”..ใ… ใ… 

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ