https://github.com/seoyoung927/social_login_ex
์บก์คํค ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ์์ ๋ก๊ทธ์ธ์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๊ณต๋ถํด๋ณด์๋ค. ์ฌ๋ฌ ๊ฐ์ง ์ด์ ๋ก ๊ฒฐ๊ตญ ์์ ๋ก๊ทธ์ธ ๋ฐฉ์์ด ์๋ ์์ด๋/๋น๋ฐ๋ฒํธ ๋ฐฉ์์ ๋ก๊ทธ์ธ์ผ๋ก ์บก์คํค ํ๋ก์ ํธ ๊ฐ๋ฐ์ ์งํํ์์ง๋ง, ๊ณต๋ถํ ๋ด์ฉ์ ๊ฐ๋จํ ์ ๋ฆฌํด๋ณด์๋ค.
์์ ๋ก๊ทธ์ธ์ ๊ตฌํํ๋ ๊ณผ์ ์ ์ผ๋ฐ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ๋ค.
1. ์ธ๊ฐ ์ฝ๋ ๋ฐ๊ธฐ
ํ๋ก ํธ์๋์์ ์นด์นด์คํก, ๊ตฌ๊ธ ๋ฑ์ผ๋ก ์ธ๊ฐ ์ฝ๋๋ฅผ ์์ฒญํ๋ค.
2. ํ ํฐ ๋ฐ๊ธฐ
์ป์ ์ธ๊ฐ ์ฝ๋๋ก ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ป๊ธฐ ์ํ access_token์ ๋ฐ๊ธ๋ฐ๋๋ค.
์ด๋, ๋ฐฑ์๋์์ ํ์๋ฅผ ํตํด ์ธ๊ฐ ์ฝ๋๊น์ง๋ง ๋ฐ๊ธ ๋ฐ์ ์ ๋ฌํ๊ฑฐ๋ ํน์ ํ ํฐ๊น์ง ๋ฐ๊ธ ๋ฐ์ ์ ๋ฌํ๋ค.
3. ์ฌ์ฉ์ ์ ๋ณด ์ป๊ธฐ ๋ฐ ๋ก์ง ์ฒ๋ฆฌ
์์์ ์ป์ access_token์ ๋ฐํ์ผ๋ก ์นด์นด์คํก, ๊ตฌ๊ธ์์ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ป์ ์ ์๋ค. ํด๋น ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ํ๋ก์ ํธ์์ ํ์๊ฐ์ ์ ์งํํ๊ฑฐ๋ ๋ก๊ทธ์ธ ํ ํ๋ก์ ํธ ์ ์ฉ ํ ํฐ์ ๋ฐ๊ธํ๋ค.
๐์นด์นด์คํก ์์ ๋ก๊ทธ์ธ ๊ตฌํ
์นด์นด์คํก ์์ ๋ก๊ทธ์ธ์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๊ณต์ ๋ฌธ์์ ์์ธํ ๋์์๋ค.
REST API | Kakao Developers ๋ฌธ์
(1) ํค ๋ฐ๊ธ ๋ฐ๊ธฐ
์์ ๋ก๊ทธ์ธ ๊ตฌํ์ ์ํด์๋ ๊ฐ์ฅ ๋จผ์ ์นด์นด์คํก์์ REST API KEY๋ฅผ ๋ฐ๊ธ๋ฐ์์ผ ํ๋ค.
๋ค์ ๋งํฌ์์ ๋ด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฑ๋กํ์ฌ REST_API_KEY๋ฅผ ๋ฐ๊ธ ๋ฐ๊ณ , REDIRECT_URI๋ฅผ ๋ฑ๋กํ ์ ์๋ค. REDIRECT_URI๋ ์ดํ ์๋ต์ ๋ฐ๊ธฐ ์ํ URI์ด๋ค. ์ธ๊ฐ์ฝ๋ ๋ฐ๊ธ์ ์ํด ์นด์นด์คํก์ ์์ฒญ์ ๋ณด๋ด๋ฉด ์์ ์ค์ ํ REDIRECT_URI๋ก ์๋ต์ ๋ฐ๊ฒ ๋๋ค.
๋๋ ์ ๋งํฌ์์ ๋ค์๊ณผ ๊ฐ์ด ๋ด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฑ๋กํ๋ค.
์ฐ์ ์ ์บก์คํค ํ๋ก์ ํธ๋ก React-Spring ๊ธฐ๋ฐ์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ๊ณ ์์ผ๋ฏ๋ก, ํ๋ซํผ>web์ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํด์ฃผ์๋ค.
(2) ์ธ๊ฐ ์ฝ๋ ๋ฐ๊ธฐ
์ธ๊ฐ ์ฝ๋๋ฅผ ๋ฐ์์ค์.
https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}
์์ ์์ ์ฝ๋์์๋ ์ ์ ์๋ฏ์ด ๋จผ์ REST_API_KEY๋ฅผ ๋ฐ๊ธ ๋ฐ๊ณ , REDIRECT_URI ๋ฑ์ ๋จผ์ ์ค์ ํด์ผ ํ๋ค. (1)๋จ๊ณ์์ ๋ฐ๊ธ๋ฐ์ REST_API_KEY์ REDIRECT_URI๋ฅผ ์ด์ฉํ์.
์๋๋ ์ธ๊ฐ ์ฝ๋๋ฅผ ๋ฐ๊ธ๋ฐ๋ ๊ณผ์ ์ ๊ตฌํํ React ์ฝ๋์ด๋ค.(.env๋ฅผ ์ด์ฉํ์๋ค.)
import styles from "./LoginPage.module.css";
function LoginPage(){
const KAKAO_REST_API_KEY = process.env.REACT_APP_KAKAO_REST_API_KEY;
const KAKAO_REDIRECT_URI = process.env.REACT_APP_KAKAO_REDIRECT_URI;
const onKakaoSocialLogin = () => {
window.location.href=`https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${KAKAO_REST_API_KEY}&redirect_uri=${KAKAO_REDIRECT_URI}`;
}
return <div className={styles.container}>
<div className={styles.button__wrapper}>
<button onClick={onKakaoSocialLogin}>
์นด์นด์คํก ๋ก๊ทธ์ธ
</button>
</div>
</div>
}
export default LoginPage;
REDIRECT_URI์์ ๋ฐ์ ์ธ๊ฐ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
*์ฐธ๊ณ
์๋น์ค์์ ํ์ํ ๋์ํญ๋ชฉ์ ๋ฐ๋ก ์ค์ ํ ์ ์๋ค. ๋ค์๊ณผ ๊ฐ์ด scope๋ฅผ ํตํด ๋ฐ๋ก ์ค์ ํ๋ฉด ๋ก๊ทธ์ธ์ ๋์ํ๋ฉด์ด ๋์ค๊ฒ ๋๋ค.
https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${KAKAO_REST_API_KEY}&redirect_uri=${KAKAO_REDIRECT_URI}&scope=profile_nickname,profile_image,account_email,gender
(3) ํ ํฐ ๋ฐ๊ธฐ
์ธ๊ฐ ์ฝ๋๋ฅผ ๋ฐ์ผ๋ฉด ํด๋น ์ธ๊ฐ ์ฝ๋๋ฅผ ์ด์ฉํด ์นด์นด์คํก์์ ํ ํฐ์ ๋ฐ์์ฌ ์ ์๋ค. ํ ํฐ๊น์ง ๋ฐ์์ ๋ฐฑ์๋์ ์ ๋ฌํ๋ค๊ณ ๊ฐ์ ํ๊ณ ๋ค์์ ๊ตฌํํ์๋ค.
https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&code=${code}
import styles from "./KakaoOAuth2RedirectPage.module.css";
import { useEffect } from "react";
function KakaoOAuth2RedirectPage() {
// 1. ์ธ๊ฐ์ฝ๋
const code = new URL(window.location.href).searchParams.get("code");
// 2. access Token ์์ฒญ
const getToken = async (code: string) => {
const KAKAO_REST_API_KEY = process.env.REACT_APP_KAKAO_REST_API_KEY;
const KAKAO_REDIRECT_URI = process.env.REACT_APP_KAKAO_REDIRECT_URI;
const response = await fetch(`https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id=${KAKAO_REST_API_KEY}&redirect_uri=${KAKAO_REDIRECT_URI}&code=${code}`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
},
});
return response.json();
}
useEffect(() => {
if (code) {
getToken(code).then((res) => {
console.log(res.access_token);
})
}
}, []);
return <div className={styles.container} >
<div className={styles.spinner} />
</div>
}
export default KakaoOAuth2RedirectPage;
๐๊ตฌ๊ธ ์์ ๋ก๊ทธ์ธ
์นด์นด์คํก ์์ ๋ก๊ทธ์ธ๊ณผ ๊ธฐ๋ณธ์ ์ธ ํ๋ฆ์ ๊ฐ๋ค.
https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow?hl=ko
(1) ํค ๋ฐ๊ธ ๋ฐ๊ธฐ
๋จผ์ ํค๋ฅผ ๋ฐ๊ธ๋ฐ๋๋ค. ํค๋ google cloud console์์ ๋ฐ๊ธ๋ฐ์ ์ ์๋ค.
https://console.cloud.google.com/
์นด์นด์คํก๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก study๋ผ๋ ์ด๋ฆ์ ํ๋ก์ ํธ๋ฅผ ๋ฑ๋กํ์๋ค.
์นด์นด์คํก์์ REST_API_KEY๋ฅผ ๋ฐ๊ธ๋ฐ์๋ค๋ฉด, ๊ตฌ๊ธ์ CLIENT_ID๋ฅผ ๋ฐ๊ธ๋ฐ๋๋ค. API ๋ฐ ์๋น์ค>์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ์ฐ์ธก์์ ํ์ธํ ์ ์๋ค. ํด๋ผ์ด์ธํธ ID๋ฅผ ๋ณต์ฌํด .env ํ์ผ์ ์ ์ฅํ๋ค.
์ถ๊ฐ์ ์ผ๋ก SECRET_KEY๋ .env ํ์ผ์ ์ ๋๋ค. ์ดํ access_token ๋ฐ๊ธ ์ ํ์ํ๋ค.
๊ทธ๋ฆฌ๊ณ ์นด์นด์คํก ๊ตฌํ ๋์ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํ๋ค.
์นด์นด์คํก ์์ ๋ก๊ทธ์ธ ๊ตฌํ๊ณผ ๋ค๋ฅธ ์ ์ OAuth ๋์ ํ๋ฉด์์ ํ ์คํธ ์ฌ์ฉ์๋ฅผ ๋ฑ๋กํด์ผํ๋ค๋ ์ ์ด๋ค. ๋ก๊ทธ์ธ์ ํ ์คํธํด ๋ณผ ์ฌ์ฉ์๋ฅผ ์ถ๊ฐํ๋ฉด ๋๋๋ฐ, ๊ฒ์ ์ํ๊ฐ "ํ ์คํธ ์ค"์ผ ๋๋ ํ ์คํธ ์ฌ์ฉ์๋ง ์ ์ํ ์ ์๋ค๊ณ ํ๋ค. ํ ์คํธํ ์์ ์ google ๊ณ์ ์ ๋ฑ๋กํด์ฃผ๋ฉด ๋๋ค.
(2) ์ธ๊ฐ ์ฝ๋ ๋ฐ๊ธฐ
์ธ๊ฐ ์ฝ๋๋ฅผ ๋ฐ์์ค์. ์นด์นด์คํก ๊ณต์ ๋ฌธ์๋ณด๋ค ์ฝ๊ธฐ ์ด๋ ค์ด ๊ฒ ๊ฐ์ง๋ง.. ์์๋๋ก ํ๋ฉด ๋๊ธด ํ๋ค.
https://accounts.google.com/o/oauth2/auth?client_id=${GOOGLE_CLIENT_ID}&redirect_uri=${GOOGLE_REDIRECT_URI}&response_type=code&scope=openid email profile
์๋๋ ์ธ๊ฐ ์ฝ๋๋ฅผ ๋ฐ๊ธ๋ฐ๋ ๊ณผ์ ์ ๊ตฌํํ React ์ฝ๋์ด๋ค.
import styles from "./LoginPage.module.css";
function LoginPage() {
const KAKAO_REST_API_KEY = process.env.REACT_APP_KAKAO_REST_API_KEY;
const KAKAO_REDIRECT_URI = process.env.REACT_APP_KAKAO_REDIRECT_URI;
const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID;
const GOOGLE_REDIRECT_URI = process.env.REACT_APP_GOOGLE_REDIRECT_URI;
const onKakaoSocialLogin = () => {
window.location.href = `https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${KAKAO_REST_API_KEY}&redirect_uri=${KAKAO_REDIRECT_URI}`;
}
const onGoogleSocialLogin = () => {
window.location.href = `https://accounts.google.com/o/oauth2/auth?client_id=${GOOGLE_CLIENT_ID}&redirect_uri=${GOOGLE_REDIRECT_URI}&response_type=code&scope=openid email profile`;
}
return <div className={styles.container}>
<div className={styles.button__wrapper}>
<button
className={styles.button__kakao__login} onClick={onKakaoSocialLogin}>
<img
className={styles.button__image}
src={`${process.env.PUBLIC_URL}/images/kakao_login_large_narrow.png`} />
</button>
</div>
<div className={styles.button__wrapper}>
<button
className={styles.button__google__login} onClick={onGoogleSocialLogin}>
๊ตฌ๊ธ ์์
๋ก๊ทธ์ธ
</button>
</div>
</div>
}
export default LoginPage;
(3) ํ ํฐ ๋ฐ๊ธฐ
https://oauth2.googleapis.com/token?grant_type=authorization_code&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&client_secret=${SECRET_KEY}&code=${code}
React๋ก ๊ตฌํํ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
import { useEffect } from "react";
import styles from "./GoogleOAuth2RedirectPage.module.css";
function GoogleOAuth2RedirectPage() {
// 1. ์ธ๊ฐ์ฝ๋
const code = new URL(window.location.href).searchParams.get("code");
// 2. access Token ์์ฒญ
const getToken = async (code: string) => {
const REST_API_KEY = process.env.REACT_APP_GOOGLE_CLIENT_ID;
const REDIRECT_URI = process.env.REACT_APP_GOOGLE_REDIRECT_URI;
const SECRET_KEY = process.env.REACT_APP_GOOGLE_SECRET_KEY;
const response = await fetch(`https://oauth2.googleapis.com/token?grant_type=authorization_code&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&client_secret=${SECRET_KEY}&code=${code}`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
},
});
return response.json();
}
useEffect(() => {
if (code) {
getToken(code).then((res) => {
console.log(res.access_token);
})
}
}, [code]);
return <div className={styles.container} >
<div className={styles.spinner} />
</div>
}
export default GoogleOAuth2RedirectPage;
๐์ฐธ๊ณ
REST-API ํ์ฉํ ์นด์นด์ค ์์ ๋ก๊ทธ์ธ ๊ตฌํ(feat. React) (tistory.com)
https://hymndev.tistory.com/72
https://notspoon.tistory.com/47
'๐ป์น(Web) > React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[React]Netlify๋ก React ๋ฐฐํฌํ๊ธฐ: ์๋ก๊ณ ์นจ, API ์ฐ๊ฒฐ ์๋ฌ ํด๊ฒฐ๋ฐฉ๋ฒ (0) | 2023.09.01 |
---|---|
[React]์น ํ์ค์ ์ค์ํ๋ฉฐ ๊ฐ๋ฐํ๋ ๋ฐฉ๋ฒ (0) | 2023.08.28 |
[React]React ํ๋ก์ ํธ์ CSS ์ ์ฉํ๋ ๋ฐฉ๋ฒ(Nomad Coders ๊ฐ์ ์ ๋ฆฌ) (0) | 2023.08.15 |
[React]Themes๋ฅผ ์ด์ฉํ ์ผ๊ด์ฑ ์๋ ์คํ์ผ ๊ด๋ฆฌ (0) | 2023.06.17 |
[React]CORS ์๋ฌ (0) | 2023.06.16 |