MySQL, React, SpringBoot로 구현한 게시판 서비스에 카카오 로그인 기능을 추가하는 과정으로, 실습에 사용한 코드는 https://myanjini.tistory.com/entry/board-%EC%86%8C%EC%8A%A4%EC%BD%94%EB%93%9C 에서 내려받을 수 있습니다.
애플리케이션 추가
카카오 개발자 사이트(https://developers.kakao.com/)에서 애플리케이션을 추가하고, 앱 키를 확인합니다. (여기에서는 JavaScript SDK를 사용하므로, JavaScript 키를 필요로 합니다.)
플랫폼 메뉴에서 사이트 도메인을 등록하고, Redirect URI 등록 페이지에서 카카오 로그인 서비스를 활성화하고, Redirect URI 주소를 등록합니다.
동의항목 메뉴에서 로그인 후 제공할 정보를 체크합니다.
라이브러리 등록
SDK 다운로드 페이지(https://developers.kakao.com/docs/latest/ko/sdk-download/js)에서 최신 버전의 코드를 포함하는 <script> 태그를 확인합니다.
index.html 파일에 앞에서 복사항 <script> 태그를 추가합니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
... (생략) ...
<!-- Kakao SDK for JavaScript 추가 -->
<script src="https://t1.kakaocdn.net/kakao_js_sdk/2.1.0/kakao.min.js" integrity="sha384-dpu02ieKC6NUeKFoGMOKz6102CLEWi9+5RQjWSV0ikYSFFd8M3Wp2reIcquJOemx" crossorigin="anonymous"></script>
</head>
<body>
... (생략) ...
</body>
</html>
카카오 로그인 컴포넌트 추가
KakaoLogin.js 파일을 생성하고, 아래 코드를 추가합니다. 해당 코드는 샘플 페이지(https://developers.kakao.com/docs/latest/ko/kakaologin/js)의 소스코드를 참고해서 작성했습니다.
import axios from 'axios';
import { useState } from 'react';
import { useEffect } from "react";
const KakaoLogin = () => {
const { Kakao } = window;
const JAVASCRIPT_APP_KEY = 'be837b733c72053ebc27ab95eea89861';
// 액세스 토큰을 상태 변수로 선언
// 로그인 버튼 출력 제어에 사용
const [accessToken, setAccessToken] = useState('');
const handlerLogin = () => {
// 간편 로그인을 요청
// 인증 성공 시 redirectUri 주소로 인가 코드를 전달
Kakao.Auth.authorize({
redirectUri: 'http://localhost:3000/kakaoLogin'
});
};
useEffect(() => {
Kakao.init(JAVASCRIPT_APP_KEY);
// 쿼리 스트링으로 부터 인가 코드를 추출
const code = window.location.search.split('=')[1];
if (code) {
// REST API로 토큰 받기를 요청
axios.post(
'https://kauth.kakao.com/oauth/token', {
grant_type: 'authorization_code', // 고정
client_id: JAVASCRIPT_APP_KEY, // 앱 REST API 키
redirect_uri: 'http://localhost:3000/kakaoLogin', // 인가 코드가 리다이렉트된 URI
code: code // 인가 코드 받기 요청으로 얻은 인가 코드
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
}
}
)
.then(response => {
const accessToken = response.data.access_token; // 사용자 액세스 토큰 값
setAccessToken(accessToken);
// 액세스 토큰 값을 할당
Kakao.Auth.setAccessToken(accessToken);
// 사용자 정보 가져오기
Kakao.API.request({
url: '/v2/user/me'
})
.then(response => {
// 사용자 정보 로깅
console.log(response);
// 애플리케이션에서 필요한 정보를 추출해서 로컬 스토리지에 저장
const { kakao_account } = response;
localStorage.setItem('userName', kakao_account.profile.nickname);
localStorage.setItem('userNickname', kakao_account.profile.nickname);
localStorage.setItem('userPhoto', kakao_account.profile.profile_image_url);
// 홈(/) 화면으로 이동
window.location.href = "/";
})
.catch(error => {
console.log(error);
});
})
.catch(error => console.log(error));
}
}, []);
return (
<>
{/* https://developers.kakao.com/tool/resource/login */}
{ !accessToken &&
<img style={{width: 277, height: 60, cursor: 'pointer'}}
src="https://developers.kakao.com/tool/resource/static/img/button/login/full/ko/kakao_login_medium_wide.png"
onClick={handlerLogin} />
}
</>
);
};
export default KakaoLogin;
Login.js 수정
로그인 컴포넌트에 카카오 로그인 컴포넌트를 추가합니다.
import KakaoLogin from "./KakaoLogin";
import NaverLogin from "./NaverLogin";
import { useEffect } from "react";
const Login = () => {
useEffect(() => {
// 로컬 스토리지에 userName이 존재하는 경우 로그인한 것으로 판단
// 이미 로그인한 경우 홈(/)으로 이동
const isLogin = !!window.localStorage.getItem('userName');
if (isLogin) {
window.location.href = '/';
}
}, []);
return (
<>
<div style={{ textAlign: 'center', padding: '30px' }}>
<NaverLogin />
<KakaoLogin /> {/* 카카로 로그인 컴포넌트 추가 */}
</div>
</>
);
};
export default Login;
App.js 수정
간편 인증 결과 및 토큰 요청 결과를 전달받을 콜백 URL을 라우터로 등록합니다.
import './App.css';
import { Route } from 'react-router-dom';
import BoardList from "./board/BoardList";
import BoardDetail from "./board/BoardDetail";
import BoardWrite from './board/BoardWrite';
import Login from './Login';
import KakaoLogin from './KakaoLogin';
const App = () => {
// 로그인 페이지로 이동
const handlerLogin = (e) => {
e.preventDefault();
window.location.href = '/login';
};
// 로그아웃 처리
// 로컬 스토리지 내용 삭제 후 홈(/)으로 이동
const handlerLogout = (e) => {
e.preventDefault();
localStorage.clear();
window.location.href = '/';
};
// 로그인 페이지가 아닌 경우 로그인/로그아웃 버튼을 제공
// 로그인 상태인 경우 로그인 정보와 로그아웃 버튼을
// 로그아웃 상태인 경우 로그인 버튼을 제공
const isNotLoginPage = window.location.pathname === '/login' ? false : true;
const isLogin = !!window.localStorage.getItem('userName');
return (
<>
<div style={{ textAlign: 'right', padding: '30px' }}>
{ isNotLoginPage && isLogin &&
<>
{ window.localStorage.getItem('userName') }님 환영합니다.
<button onClick={handlerLogout}>Logout</button>
</>
}
{ isNotLoginPage && !isLogin &&
<>
<button onClick={handlerLogin}>Login</button>
</>
}
</div>
<Route path="/" component={BoardList} exact={true} />
<Route path="/login" component={Login} exact={true} />
<Route path="/kakaoLogin" component={KakaoLogin} exact={true} /> {/* 라우터 추가 */ }
<Route path="/board" component={BoardList} exact={true} />
<Route path="/board/write" component={BoardWrite} />
<Route path="/board/detail/:boardIdx" component={BoardDetail} />
</>
);
};
export default App;
실행 결과
최종 코드
'개발' 카테고리의 다른 글
폼 데이터와 함께 파일 업로드 (다중 파일 선택창 & 다중 파일 선택) (0) | 2023.04.12 |
---|---|
웹 애플리케이션에서 도커 컨테이너 실행, 삭제, 조회 (0) | 2023.04.10 |
네이버 로그인 (0) | 2023.04.06 |
EC2 인스턴스에 React + SpringBoot + MySQL 연동 (0) | 2023.03.29 |
폼 데이터와 함께 파일 업로드 (0) | 2023.02.21 |
댓글