위 이미지는 카카오 로그인 과정 시퀀스 다이어그램이다. 내가 이해한 방식으로 정리하자면 인가 코드 → 토큰 → 사용자 정보를 카카오를 통해 받는 과정이며, 회원가입과 로그인이 완료되었을 때 JWT을 프론트에 전달하는 것까지 이번 프로젝트에서 구현하였다.
또한 보안 강화를 위해 Client Secret을 설정해줄 수 있다.
// controller.js
const kakaoLogin = asyncErrorHandler(async (req, res) => {
const authCode = req.query.code; // 프론트에서 authCode를 전달 받아온다!
if (!authCode) throwCustomError("MISSING_AUTH_CODE", 400); // authCode를 받아오지 못할 경우 분기 처리
const accessToken = await userService.kakaoLogin(authCode); // service단에 authCode 가지고 가서 JWT 만들기
return res.status(200).json({ accessToken }); // 그리고 JWT를 프론트에 전달!
});
// service.js
const SocialTypeId = Object.freeze({
KAKAO: 1,
NAVER: 2,
GOOGLE: 3,
}); // 프로젝트가 끝난 뒤, Naver와 Google 로그인도 구현해보고 싶어 일단 추가해놓았다.
const kakaoLogin = async (authCode) => {
try {
const getKakaoToken = await axios.get(
"https://kauth.kakao.com/oauth/token",
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
params: {
grant_type: "authorization_code",
client_id: process.env.KAKAO_REST_API_KEY,
redirect_url: process.env.KAKAO_REDIRECT_URI,
code: authCode,
},
withCredentials: true,
}
); // client_id와 redirect_url은 .env에서 관리, params에 authCode를 포함하여 보내서 카카오 서버에서 authCode 검증. 이후 카카오 서버에서 access_token을 보내준다.
const getKakaoUserData = await axios.get(
"https://kapi.kakao.com/v2/user/me",
{
headers: {
Authorization: `Bearer ${getKakaoToken.data.access_token}`,
},
}
); // 받아온 access_token을 다시 카카오 서버에 보내서 userData를 받아온다.
const socialId = getKakaoUserData.data.id;
const email = getKakaoUserData.data.kakao_account.email;
const nickname = getKakaoUserData.data.properties.nickname;
const socialTypeId = SocialTypeId.KAKAO;
const userInfo = await userDao.getUserInfo(socialId, socialTypeId);
// 해당 사용자가 가입한 기록이 없는 경우 회원가입을 하고 JWT를 발급
if (!userInfo) {
const newUser = await userDao.createUser(
socialId,
email,
nickname,
socialTypeId
);
const accessToken = jwt.sign(
. . .
);
return accessToken;
}
// 가입 기록이 있는 경우 로그인 후 JWT 발급
const accessToken = jwt.sign(
. . .
);
return accessToken;
} catch (err) {
const error = new Error("ERROR_OCCURED_WHILE_ATTEMPING_TO_LOG_IN");
error.statusCode = 400;
throw error;
}
};