티스토리 뷰
흐름
* REDIRECT_URL : 프론트 쪽으로 지정(localstorage:3000/auth/kakao)
- 프론트 : 카카오 서버에서 인가코드 받기 (/oauth/authorize) => window.location.href 이용
- 프론트 : REDIRECT_URL 통해 인가코드를 받으면 react-router-dom 이용하여 카카오 로그인 처리용 컴포넌트(KakaoAuthHandle)로 이동 => 백엔드로 인가코드와 함께 JwtToken 요청
- 백엔드 : 카카오 서버에서 액세스 토큰 받기 (/oauth/token)
- 백엔드 : 액세스 토큰으로 카카오 유저 정보 받기 (/v2/user/me) => DB 조회 후 프론트에 JWT Token 전송 (회원 아닐 경우 가입 처리)
- 프론트 : JWT Token 받아 로그인 처리 (localStorage 이용)
1. 인가코드 받기 (프론트 - React)
- 카카오 서버로 인가코드를 요청하면(1번) → (2~4번 생략) → 카카오 서버는 Redirect URL로 인가코드를 전달(6번)
- 인가코드 요청 URL : ` https://kauth.kakao.com/oauth/authorize?client_id=$ {CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code`
- CLIENT_ID : 내 앱의 REST API 키
- REDIRECT_URL : 인가코드를 받을 백엔드 or 프론트엔드 URL (나는 프론트엔드 URL로 진행)
* 로그인 창에서 동의 화면이 뜨지 않음
이미 동의를 했다면 이후엔 동의 화면이 뜨지 않음
-> 동의화면 다시 출력해서 보고 싶으면 카카오 계정 '연결 끊기' 후 시도할 것 (https://devtalk.kakao.com/t/topic/116096)
소스 코드
// Login.js
const CLIENT_ID = process.env.REACT_APP_REST_API_KEY // 환경변수 사용
const REDIRECT_URI = process.env.REACT_APP_REDIRECT_URI // 환경변수 사용
const KAKAO_AUTH_URL= `https://kauth.kakao.com/oauth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code`
// 사이트 내 로그인 페이지에서 '카카오 로그인' 버튼 클릭 시
const clickLoginButton = async () => {
window.location.href = KAKAO_AUTH_URL
}
- 인가코드 전달 형식 : Redirect Url에 Query String으로 전달 => {REDIRECT_URL}?code=abc123....
2. 토큰 받기 + 프론트에 JWT Token 만들어 전달 (백엔드 - NestJS)
* 토큰을 받아 바로 회원가입 처리 + 프론트에 JWT Token 전달
- 토큰 요청 URL : 'https://kauth.kakao.com/oauth/token'
- POST로 아래 파라미터를 Body 에 담아 Request
* 발생했던 오류 코드 : KOE010
=> client_secret 값을 누락하지도, 잘못된 값을 전달하지도 않았을 경우 다음 사항 체크
- Content-Type : application/x-www-form-urlencoded
- JSON 아닌 body QueryString 형식으로 전달 (encodeURIComponent 사용)
- 참고 : https://kakao-tam.tistory.com/66?category=866966
* 발생했던 오류 코드 : KOE320
소스 코드
// auth.controller.ts
@Get('login/kakao')
async getKakaoUserInfo(
@Query('code') code: string
) {
// 1. 카카오 유저 정보 받기(카카오 공식 문서 내용)
const { uid } = await this.authService.getKakaoUserInfo(code)
// 2. 받은 유저 정보 이용해 나의 서비스 로그인 하기
const result = await this.authService.login(uid)
return result
}
// auth.service.ts
// 카카오 유저 정보 받기(카카오 공식 문서 내용)
async getKakaoUserInfo(code: string) {
const formUrlEncoded = x =>
Object.keys(x).reduce((p, c) => p + `&${c}=${encodeURIComponent(x[c])}`, '')
const GET_TOKEN_URL = 'https://kauth.kakao.com/oauth/token'
const GET_USER_INFO_URL = 'https://kapi.kakao.com/v2/user/me'
const GRANT_TYPE = "authorization_code"
const CLIENT_ID = process.env.KAKAO_REST_API_KEY
const REDIRECT_URI = process.env.KAKAO_REDIRECT_URL
const requestBody = formUrlEncoded({
grant_type: GRANT_TYPE,
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
code,
})
// 1. 토큰 받기
const { data } = await axios.post(
GET_TOKEN_URL,
requestBody,
)
// 2. 받은 토큰으로 유저 정보 받기
const { data: userInfo } = await axios.get(GET_USER_INFO_URL, {
headers: {
Authorization: 'Bearer ' + data.access_token,
}
})
return userInfo
}
// 내 서비스 로그인 로직
async login(uid: number): Promise<any> {
const user = await this.usersService.findOneByUid(uid)
if(!user) throw new NotFoundException('회원 정보가 존재하지 않습니다.') // => 가입 처리 필요
const jwtToken = this.getJwtToken(user)
return {
status: 200,
data: { jwtToken, userId: user.id },
}
}
3. 로그인 처리 (프론트엔드 - React)
* 백엔드에서 JWT Token 받아 localStorage에 저장
소스 코드
// App.js
const [isLogin, setIsLogin] = useState(false)
useEffect(() => {
const jwtToken = localStorage.getItem("JWT_TOKEN")
if(jwtToken) {
axios.defaults.headers.common['Authorization'] = jwtToken
setIsLogin(true)
}
}, [])
return (
<>
<BrowserRouter basename={process.env.PUBLIC_URL}>
<Routes>
{/* REDIRECT_URL로 설정 */}
<Route path="/auth/kakao" element={<KakaoAuthHandle setIsLogin={setIsLogin} />}></Route>
</BrowserRouter>
</>
);
// KakaoAuthHandle.js
import axios from "axios";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
function KakaoAuthHandle({ setIsLogin }) {
const navigate = useNavigate()
const kakaoLogin = async () => {
const code = new URL(window.location.href).searchParams.get('code')
const { data } = await axios.get(`${process.env.REACT_APP_API_URL}/auth/login/kakao`, {
params: { code }
})
const { jwtToken } = data.data
localStorage.setItem("JWT_TOKEN", jwtToken)
setIsLogin(true)
navigate("/")
}
useEffect(() => {
kakaoLogin()
}, [])
}
export default KakaoAuthHandle;
공식 문서
참고 블로그
'라이브러리, 프레임워크 > React' 카테고리의 다른 글
[React] 반응형 웹을 위한 미디어쿼리 작성 예시 (0) | 2023.01.31 |
---|---|
[React] 컴포넌트 여러 개를 조건에 맞게 보여주기 (0) | 2023.01.12 |
[React] Boolean state if문 없이 toggle 구현 예시 (0) | 2023.01.12 |
[React] 모달창 띄운 상태에서 스크롤 막는 코드 예시 (0) | 2023.01.10 |
[React] 여러 개 이미지를 한 번에 import하기 (0) | 2023.01.08 |
댓글