Skip to content

Commit

Permalink
✨ 카카오 로그인 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
jjikky committed Jun 20, 2024
1 parent bf6d146 commit 6700946
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 42 deletions.
4 changes: 4 additions & 0 deletions src/common/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ const conf = {
mongoURL: process.env.MONGO_URL,
jwtSecret: process.env.JWT_SECRET,
cookieSecret: process.env.COOKIE_SECRET,

// social login
kakaoRestApiKey: process.env.KAKAO_REST_API_KEY,
kakaoCallback: process.env.KAKAO_CALLBACK,
};

module.exports = conf;
1 change: 1 addition & 0 deletions src/common/constants/error-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const ErrorMessage = Object.freeze({
EXIST_NICKNAME: '이미 존재하는 닉네임 입니다.',
EXIST_EMAIL: '이미 존재하는 이메일 입니다.',
LOGIN_ERROR: '로그인중 오류가 발생하였습니다.',
KAKAO_LOGIN_ERROR: '카카오 로그인중 오류가 발생하였습니다.',
});

module.exports = ErrorMessage;
2 changes: 2 additions & 0 deletions src/common/constants/success-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const SucesssMessage = Object.freeze({

// USER - 로그인
LOGIN_SUCCESSS: '로그인 성공',

GET_PROFILE_SUCCESS: '유저 정보 조회 성공',
});

module.exports = SucesssMessage;
8 changes: 5 additions & 3 deletions src/common/passport/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const passport = require('passport');
const local = require('./localStrategy');
const localStrategy = require('./localStrategy');
const jwtStrategy = require('./jwtStrategy');
const User = require('../../routes/user/user.model');

module.exports = () => {
passport.serializeUser((user, done) => {
Expand All @@ -16,6 +17,7 @@ module.exports = () => {
}
});

local(); // LocalStrategy 초기화
jwtStrategy(); // JWTStrategy 초기화
// 초기화
localStrategy();
jwtStrategy();
};
37 changes: 37 additions & 0 deletions src/common/utils/kakao.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const axios = require('axios');
const conf = require('../../common/config');

const header = {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
Authorization: 'Bearer ',
};
exports.getKakaoToken = async (code) => {
const data = {
grant_type: 'authorization_code',
client_id: conf.kakaoRestApiKey,
code,
};

const queryString = Object.keys(data)
.map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(data[k]))
.join('&');

const token = await axios.post('https://kauth.kakao.com/oauth/token', queryString, { headers: header });
return { accessToken: token.data.access_token };
};
exports.getUserInfo = async (accessToken) => {
// Authorization: 'Bearer access_token'
header.Authorization += accessToken;

// 카카오 사용자 정보 조회
const get = await axios.get('https://kapi.kakao.com/v2/user/me', { headers: header });
const result = get.data;

return {
snsId: result.id,
email: result.kakao_account.email ? result.kakao_account.email : `${result.id}@no.agreement`,
// NOTE: 닉네임 10글자 제한 때문에, 임시 처리
// kakao 닉네임 규정은 20글자. result.id는 10글자로 추정
nickname: result.id,
};
};
62 changes: 34 additions & 28 deletions src/routes/user/user.controller.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
const passport = require('passport');
const jwt = require('jsonwebtoken');

const userService = require('./user.service');
const sendResponse = require('../../common/utils/response-handler');
const ErrorMessage = require('../../common/constants/error-message');
Expand All @@ -12,8 +10,8 @@ const {
emailCheckReqQuerySchema,
loginBodySchema,
} = require('./user.schema');
const config = require('../../common/config');
const { generateToken } = require('../../common/utils/auth');
const { getKakaoToken, getUserInfo } = require('../../common/utils/kakao');

exports.register = async (req, res) => {
try {
Expand Down Expand Up @@ -85,30 +83,6 @@ exports.isEmailExist = async (req, res) => {
}
};

// const { email, password } = validateRequest(loginBodySchema, req.body);
// exports.localLogin = async (req, res) => {

// passport.authenticate('local', (authError, user, info) => {
// if (authError) {
// console.error(authError);
// return next(authError);
// }
// if (!user) {
// return sendResponse.unAuthorized(res, { message: info.message });
// }
// return req.login(user, (loginError) => {
// if (loginError) {
// console.error(loginError);
// return next(loginError);
// }
// return sendResponse.ok(res, {
// message: SucesssMessage.LOGIN_SUCCESSS,
// user_id: user._id,
// });
// });
// })(req, res, next);
// };

exports.localLogin = async (req, res, next) => {
try {
req.body = validateRequest(loginBodySchema, req.body);
Expand All @@ -120,7 +94,6 @@ exports.localLogin = async (req, res, next) => {
if (!user) {
return sendResponse.unAuthorized(res, { message: info.message });
}

const token = generateToken(user);

return sendResponse.ok(res, {
Expand All @@ -135,3 +108,36 @@ exports.localLogin = async (req, res, next) => {
sendResponse.fail(req, res, ErrorMessage.LOGIN_ERROR);
}
};

exports.kakaoLogin = async (req, res) => {
try {
const { code } = req.body;

const { accessToken } = await getKakaoToken(code);
const { snsId, email, nickname } = await getUserInfo(accessToken);

const kakaoUser = { snsId: snsId, email, nickname, provider: 'kakao' };

let user = await userService.isKaKaoUserExist(kakaoUser.snsId);
if (!user) {
user = await userService.kakaoRegister(kakaoUser);
}
const token = generateToken(user);
sendResponse.ok(res, {
message: SucesssMessage.LOGIN_SUCCESSS,
token,
});
} catch (err) {
console.log(err);
sendResponse.fail(req, res, ErrorMessage.KAKAO_LOGIN_ERROR);
}
};

exports.getProfile = (req, res) => {
const { _id, nickname, email } = req.user;
const data = { _id, nickname, email };
sendResponse.ok(res, {
message: SucesssMessage.GET_PROFILE_SUCCESS,
data,
});
};
10 changes: 7 additions & 3 deletions src/routes/user/user.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ const userSchema = new mongoose.Schema(
);

userSchema.pre('save', async function (next) {
if (this.isModified('password') || this.isNew) {
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
try {
if ((this.isNew && !this.provider) || this.isModified('password')) {
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
}
} catch (err) {
console.log(err);
}
next();
});
Expand Down
5 changes: 5 additions & 0 deletions src/routes/user/user.repository.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@ exports.findUserByEmail = async (email) => {
const userExists = await User.exists({ email });
return userExists ? true : false;
};

exports.getUserBySnsId = async (snsId) => {
const user = await User.findOne({ snsId, provider: 'kakao' });
return user;
};
8 changes: 3 additions & 5 deletions src/routes/user/user.route.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const express = require('express');
const { register, isNicknameExist, isEmailExist, localLogin } = require('./user.controller');
const { register, isNicknameExist, isEmailExist, localLogin, kakaoLogin, getProfile } = require('./user.controller');
const { isLoggedIn, isNotLoggedIn } = require('../../common/utils/auth');
const userRouter = express.Router();

Expand All @@ -10,10 +10,8 @@ userRouter.get('/check/email', isEmailExist);

// 로그인
userRouter.post('/local/login', isNotLoggedIn, localLogin);
userRouter.post('/kakao/login', isNotLoggedIn, kakaoLogin);

// NOTE : JWT 확인용. 삭제 예정
userRouter.get('/profile', isLoggedIn, (req, res) => {
res.json({ user: req.user });
});
userRouter.get('/profile', isLoggedIn, getProfile);

module.exports = userRouter;
13 changes: 10 additions & 3 deletions src/routes/user/user.service.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
const bcrypt = require('bcrypt');
const userRepository = require('./user.repository');

exports.register = async (userData) => {
const hash = await bcrypt.hash(userData.password, 12);
const newUser = {
email: userData.email,
password: hash,
password: userData.password,
nickname: userData.nickname,
};
return await userRepository.createUser(newUser);
};

exports.kakaoRegister = async (newUser) => {
return await userRepository.createUser(newUser);
};

exports.isNicknameExist = async (nickname) => {
const isUserExist = await userRepository.findUserByNickname(nickname);
return isUserExist;
Expand All @@ -20,3 +22,8 @@ exports.isEmailExist = async (email) => {
const isUserExist = await userRepository.findUserByEmail(email);
return isUserExist;
};

exports.isKaKaoUserExist = async (snsId) => {
const user = await userRepository.getUserBySnsId(snsId);
return user;
};

0 comments on commit 6700946

Please sign in to comment.