이번 포스트는 passport-kakao
를 사용하지 않고 passport-local
를 이용해 카카오 로그인을 처리한 방법에 대한 포스트입니다.
기존(1)에서 passport-kakao
를 이용하지 않은 이유에 대한 짧은 설명이 있었다. 다시 간단히 말해 카카오로 로그인을 하게되면 내가 필요한 정보를 DB에 저장을 해야하기 때문이였다.
카카오로그인을 구현하는 방법은 kakao-developers에서 제공하는 REST API와 Javascript SDK 에 대한 구현방법이 안내되어있다. 그중 나는 JavaScript를 이용한 kakao.Auth.login
로 구현을 하였다.
kakao.Auth.login
은 팝업형식으로 클라이언트에서 모든 인증처리가 가능하다.
kakaoStrategy
를 사용할 경우, 성공적인 인증에 대한 콜백으로 페이지를 리디렉션한다. 그러나 나의 목적은 프로젝트에서 로그인 이력이 없는 경우 클라이언트로부터 닉네임을 모달 창을 통해 요청받는 것이다. 이 방법이 실패하여, passport-kakao
를 사용하지 않고 카카오 JavaScript SDK를 사용하여 로그인 요청을 보내고, 해당 정보를 백엔드에서 passport의 login 함수를 사용하여 passport 전략을 직접 처리하여 프로젝트의 로그인 처리를 진행했다.
router.get('/kakao', passport.authenticate('kakao'));
// 카카오 로그인이 되면, 카카오 redirect url 설정에 따라 이동
router.get(
'/kakao/callback',
// passport 로그인 전략에 의해 kakaoStrategy로 가서
// 카카오계정 정보와 DB를 비교해서 회원가입시키거나 로그인 처리
passport.authenticate('kakao', {
// kakaoStrategy에서 실패한다면 '/login' 로 Redirect
failureRedirect: '/login',
}),
// kakaoStrategy에서 성공한다면 콜백 실행
(req, res) => {
res.redirect('/');
},
);
로그인을 하고 애플리케이션 추가
Web 플랫폼 등록
카카오 로그인 활성화
Redirect URI 등록
카카오로 부터 받을 정보 설정
JavaScript SDK 사용을 위한 JavaScript키 확인
카카오 Javascript SDK 등록
import Head from 'next/head';
<Head>
<script src="https://developers.kakao.com/sdk/js/kakao.js"></script>
</Head>
발급된 JavaScript 키를 이용하여 kakao 기능을 수행할 수 있도록 초기화 작업
export const kakaoInit = () => {
const kakao = (window as any).Kakao;
if(!kakao.isInitialized()) {
kakao.init('발급된 JavaScript Key를 입력하세요');
}
return kakao;
}
카카오 로그인을 하기위한 버튼 구현
<button onClick={KakaoLoginButton} type="button">카카오 로그인</button>
로그인 코드
const kakaoLogin = async () => {
// 카카오 초기화
const kakao = kakaoInit();
// 카카오 로그인 구현
kakao.Auth.login({
success: () => {
kakao.API.request({
url: '/v2/user/me', // 사용자 정보 가져오기
success: (res: any) => {
// 로그인 성공할 경우 정보 확인 후 /kakao 페이지로 push
console.log(res);
Router.push('/kakao');
},
fail: (error: any) => {
console.log(error);
},
});
},
fail: (error: any) => {
console.log(error);
},
});
};
위와 같이 kakao.Auth.login
을 이용한 방법에 대해 알아보았다.
이제 내가 프로젝트에 적용한 방법은 아래와 같다.
kakao.Auth.login
은 카카오에서 요청 결과에 따라 사용자의 정보를 토큰으로 발급해주는 용도이기 때문에.
프로젝트에서는 별도의 회원가입과 로그인 처리를 해주어야 한다.
success 콜백을 통해 카카오에서 받은 사용자 토큰을 백엔드와 통신하여 정보 전달
success: (res: any) => {
// 로그인 성공할 경우 정보 확인
console.log(res);
},
success 콜백에서는 로그인이 성공했을 때 받아온 정보를 확인한다.
나의 경우 카카오 개발자 콘솔에서 동의 항목으로 체크한 항목에 대한 정보를 포함하는데 동의 항목에 대한
요청이 없거나 체크한 항목이 없다면, 기본적인 사용자 정보를 카카오에서 제공한다.
백엔드에서 id의 값을 통해 클라이언트 요청
success 콜백에서 받은 정보(id)를 백엔드로 전달
success: async (res: IKakaoLoginSuccess) => {
const response: ISignupResult | ILoginResult = await kakaoLogin({
id: res.id,
});
},
// kakaoLogin API
export const kakaoLogin = async (data: { id: number }) => {
const response = await apiClient({
method: 'post',
url: '/user/kakao',
data,
});
return response.data;
};
백엔드는 받은 id 값을 사용하여 유저 정보를 찾고, 요청에 대한 응답을 제공
router.post('/kakao', async (req, res, next) => {
const kakaoLogin = req.body;
const client = await _client;
const userdb = client.db('TripLogV2').collection('user');
const user = await userdb.findOne({ id: kakaoLogin.id });
if (!user) {
return res.send({
type: 'signup',
success: true,
message: '닉네임 정보가 필요합니다.',
});
}
});
router.post('/kakao', async (req, res, next) => {
const kakaoLogin = req.body;
const client = await _client;
const userdb = client.db('TripLogV2').collection('user');
const user = await userdb.findOne({ id: kakaoLogin.id });
return req.login(user, async (error) => {
if (error) {
console.error(error);
return next(error);
}
return res.send({
type: 'login',
success: true,
message: '카카오 로그인이 성공했습니다.',
nickname: user.nickname,
});
});
});
클라이언트에서는 회원가입 및 로그인 요청에 따라 처리를 수행
kakao.Auth.login
코드// 요청 정보를 저장하는 useState
const [result, setResult] = useState<ISignupResult | ILoginResult>({
type: 'signup',
success: false,
message: '',
});
// 모달창의 상태를 저장하는 useState
const [show, setShow] = useState(false);
// 카카오 토큰의 개인 id의 상태를 저장하는 useState
const [kakaoId, setKakaoId] = useState(0);
kakao.Auth.login({
success: () => {
kakao.API.request({
url: '/v2/user/me',
success: async (res: IKakaoLoginSuccess) => {
const response: ISignupResult | ILoginResult = await kakaoLogin({
id: res.id,
});
// signup의 경우
if (response.type === 'signup') {
setResult(response);
setShow(response.success);
setKakaoId(res.id);
}
// login의 경우
if (response.type === 'login') {
setResult(response);
setShow(response.success);
}
},
fail: () => {
const result: ISignupResult = {
type: 'signup',
success: false,
message: '카카오 회원가입 도중에 문제가 발생했습니다.',
};
setResult(result);
setShow(true);
},
});
},
fail: () => {
const result: ILoginResult = {
type: 'login',
success: false,
message: '카카오 로그인 도중에 문제가 발생했습니다.',
nickname: '',
};
setResult(result);
setShow(true);
},
});
// 컴포넌트
return (
<>
<button onClick={KakaoLoginButton} type="button" >카카오 로그인</button>
{result.type === 'signup' && (
<KakaoFormModal show={show} setShow={setShow} id={kakaoId} />
)}
{result.type === 'login' && (
<SignSuccess show={show} setShow={setShow} result={result} />
)}
</>
);
백엔드에서 받은 result.type
정보를 기반으로 모달창을 표시하여 사용자에게 어떤 응답이 발생했는지 알려준다.
<KakaoFormModal />
은 회원가입이 필요한 경우에 사용되며, 사용자로부터 닉네임을 입력받는 모달창을 표시한다.
<SignSuccess />
는 로그인이 성공한 경우에 사용되며, 로그인 성공 메시지를 보여주어 사용자에게 알린다.
https://jjnooys.medium.com/nodejs-passport-kakao-회원가입-후-자동-로그인-구현하기-7d1be78e29ea
https://developers.kakao.com/
https://www.passportjs.org/packages/passport-kakao/
https://jforj.tistory.com/237