⚒ 개발 환경
- Frontend: TypeScript + React + Next.js
- Backend: Express + TypeScript + Node.js + MongoDB
KAKAO DEVELOPER에서 선행작업하기
⚠️ Kakao developer 해당 사이트에 들어가서 해야 할 선행 작업을 수행한다.
1. 애플리케이션 만들기
→ 앱 아이콘, 이름, 사업자명, 카테고리 등을 넣어 등록한다. 나의 경우 개인으로 추가한 것이기에 사업자명은 내 별명을 넣어주었다.
해당 애플리케이션 만들어졌다.
2. REST API 키 등록하기
애플리케이션을 만들면 자동으로 만들어진다. 여기 중 REST API를 따로 저장해놓는다.
3. 사이트 도메인 설정하기
내 애플리케이션 > 앱 설정 > 플랫폼에 들어가면 맨 하단에 설정할 수 있다.
나중에도 이를 등록해줘야 하기에 루트를 기억해두면 좋다. 배포할 경우에도 도메인을 다시 등록해야 한다.
4. Redirect URI 설정하기
위에서 설정하고 나면 Redirect URI 등록해야 한다고 하이퍼링크가 뜨는데 이를 클릭한다. 이 사이트에서 활성화 설정을 OFF에서 ON으로 설정해 놓는다.
아래와 같이 내가 백엔드에 라우터에 등록한 주소를 적어야 한다. app에 바로 코드를 쓴 것이 아닌 라우터로 따로 뺐기에 api가 생략되어 있다. 이도 아까와 같이 배포의 경우를 생각하기에 기억해두면 좋다.
⚠️ BACK URL을 등록해야 한다.
→ Redirect URI는 사용자가 동의 항목에 동의하고 로그인을 요청하면 인가 코드를 넘겨 받는 역할이다. 인가 코드가 있어야 토큰 발급을 할 수 있다. 그래야 해당 정보를 얻어 백엔드 db에 저장할 수 있다.
2. 코딩하기(FRONT)
1) FRONT 환경변수 설정하기
환경 변수를 설정할 때 프론트의 env는 REST_API_KEY, REDIRECT_URI, KAKAO_AUTH_URL를 설정해줘야 한다.
그런데 KAKAO_AUTH_URL의 경우 내 애플리케이션 > 제품 설정 > 카카오 로그인 > 동의 항목에 들어가서 정보 동의를 했을 경우 어떠한 정보를 가져오고자 하는지 설정해줘야 한다.
그런데 그에 맞지 않은 정보를 가져올려고 하는 경우 에러가 난다.
<https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=profile_nickname>
2) 카카오 디벨로퍼에서 설정하기
위의 사진을 보게 되면 카카오 로그인이 on이 되어 있는지 확인한다. 그리고 나서 개인정보를 받고자 하는 것을 필수동의 설정한다. 이후의 다른 항목들도 받고 싶다면 카카오측에 개인정보 동의항목 심사 신청을 하면 된다. 나의 경우 개인 프로젝트임으로 여러 정보는 받지 않았다.
3) FRONT에서 코딩하기
// FRONT : src/app/Kakao.tsx
import React, { useEffect } from "react";
import Image from "next/image";
import KakaoImage from "../../public/kakao_login_medium_narrow.png";
function kakao() {
return (
<div>
<div className="font-bold text-4xl p-[20px]">
<a href={process.env.KAKAO_AUTH_URL}>
<Image src={KakaoImage} alt="kakao" placeholder="blur" />
</a>
</div>
</div>
);
}
export default kakao;
이미지 위에 a 태그를 씌어 누르면 페이지가 이동되게 한다.
3. 코딩하기(BACK)
1) 환경변수 설정하기
프론트에서 설정한 것과 같이 설정하면 되는데 여기서 추가할 부분은 SERCETKEY이다.
이 시크릿 키는 session store에 저장해야 되기에 지정하는 것이다.
2) 액세스 토큰 발급하기
const axiosInstance = axios.create({
headers: {
"Content-type": "application/x-www-form-urlencoded;charset=utf-8",
},
});
router.get("/oauth/callback/kakao", async (req, res) => {
try {
const { CLIENT_ID, REDIRECT_URI } = process.env;
const { code } = req.query;
const token = await axiosInstance.post(
"<https://kauth.kakao.com/oauth/token>",
qs.stringify({
grant_type: "authorization_code",
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
code,
})
);
- 첫번째 코드는 axios로 데이터를 요청하는 것이 많기에 따로 코드를 빼냈다. 반복을 좀 줄였다.
- 두번째 코드는 Redirect uri를 갔을 때 query를 통해 code를 전송해오기에 이를 따로 코드로 지정한다.
- 필수 파라미터인 headers, grant_type, client_id, redirect_uri, code를 쿼리 스트링으로 변환한다. https://kauth.kakao.com/oauth/token으로 POST 요청을 하면, 요청 성공 시 응답으로 토큰을 저장할 수 있다.
- 여기서 grant_type의 authorization_code은 고정이다.
3) 사용자 정보 가져오기
토큰으로 사용자 정보를 가져올 차례이다.
const user = await axiosInstance.get("<https://kapi.kakao.com/v2/user/me>", {
headers: {
Authorization: `Bearer ${token.data.access_token}`,
},
});
위에서 받아오고자 하는 데이터가 여기서 user로 담아와서 보여진다.
4) DB에 해당 정보 저장하기
const {id, kakao_account: { profile: { nickname },}, } = user.data;
const existingUser = await UserItem.findOne({ _id: id });
if (!existingUser) {
const userItem = new UserItem({
_id: id,
name: nickname,
});
await userItem.save();
}
req.session.userData = {
_id: id,
name: nickname,
};
return res.redirect("<http://localhost:3000?id=>"+nickname);
DB에 id, nick name을 저장하기 위해서 이를 user에서 가져온다. 만약 이가 db에 저장되어 있지 않다면 이를 db에 저장해놓는다. 그리고 세션에 저장한다.
5) 프론트에서 보이는 모습
url에 가져온 데이터를 쿼리 스트링으로 보이게 했다.
그리고 이 쿼리 스트링을 따로 프론트에서 빼내서 보이게 했다.
const queryString = location.search;
const encodedId = queryString ? decodeURIComponent(queryString.split('=')[1]) : null;
6) BACK 풀 코드
router.get("/oauth/callback/kakao", async (req, res) => {
try {
const { CLIENT_ID, REDIRECT_URI } = process.env;
const { code } = req.query;
const token = await axiosInstance.post(
"<https://kauth.kakao.com/oauth/token>",
qs.stringify({
grant_type: "authorization_code",
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
code,
})
);
const user = await axiosInstance.get("<https://kapi.kakao.com/v2/user/me>", {
headers: {
Authorization: `Bearer ${token.data.access_token}`,
},
});
const {id, kakao_account: { profile: { nickname },}, } = user.data;
const existingUser = await UserItem.findOne({ _id: id });
if (!existingUser) {
const userItem = new UserItem({
_id: id,
name: nickname,
});
await userItem.save();
}
req.session.userData = {
_id: id,
name: nickname,
};
return res.redirect(""+nickname);
} catch (error) {
console.error("Error in Kakao OAuth callback:", error.message);
res.status(500).json({ error: "Internal Server Error" });
}
});
👇🏻 참고
https://velog.io/@gbwlxhd97/express로-카카오-소셜-로그인-기능을-적용하기
https://velog.io/@cconac/Node.js-Express-카카오-로그인-구현해-보기