Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/login api #15

Merged
merged 4 commits into from
Jan 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

153 changes: 153 additions & 0 deletions src/controller/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { Request, Response } from "express";

// slack
import slack from "../others/slack/slack";

// libraries
import constant from "../library/constant";
import response from "../library/response";
import returnCode from "../library/returnCode";

// services
import authService from "../service/auth";

/**
* @회원가입
* @route POST /auth/signup
* @access public
* @err 1. 필요한 값이 없을 때
* 2. 이메일 형식이 올바르지 않을 때
* 3. 닉네임 형식이 올바르지 않을 때
* 4. 패스워드 형식이 올바르지 않을 때
* 5. 이메일이 이미 존재할 때
* 6. 닉네임이 이미 존재할 때
*/
const postSignupController = async (req: Request, res: Response) => {
try {
const resData = await authService.postSignupService(req.body);

if (resData === constant.NULL_VALUE) {
response.basicResponse(
res,
returnCode.BAD_REQUEST,
false,
"필요한 값이 없습니다."
);
} else if (resData === constant.WRONG_EMAIL_CONVENTION) {
response.basicResponse(
res,
returnCode.BAD_REQUEST,
false,
"올바르지 않은 이메일 형식 입니다."
);
} else if (resData === constant.WRONG_NICKNAME_CONVENTION) {
response.basicResponse(
res,
returnCode.BAD_REQUEST,
false,
"올바르지 않은 닉네임 형식 입니다."
);
} else if (resData === constant.WRONG_PASSWORD_CONVENTION) {
response.basicResponse(
res,
returnCode.BAD_REQUEST,
false,
"올바르지 않은 패스워드 형식 입니다."
);
} else if (resData === constant.EMAIL_ALREADY_EXIST) {
response.basicResponse(
res,
returnCode.BAD_REQUEST,
false,
"이미 존재하는 이메일 입니다."
);
} else if (resData === constant.NICKNAME_ALREADY_EXIST) {
response.basicResponse(
res,
returnCode.BAD_REQUEST,
false,
"이미 존재하는 닉네임 입니다."
);
} else {
response.tokenResponse(
res,
returnCode.CREATED,
"이동근의 북스테어즈에 온 것을 환영합니다.",
true,
resData.token
);
}
} catch (err) {
slack.slackWebhook(req, err.message);
console.error(err.message);
response.basicResponse(
res,
returnCode.INTERNAL_SERVER_ERROR,
false,
"서버 오류"
);
}
};

/**
* @로그인
* @route Post auth/login
* @access public
* @err 1. 필요한 값이 없습니다.
* 2. 존재하지 않는 이메일입니다.
* 3. 비밀번호가 일치하지 않습니다.
*/

const postLoginController = async (req: Request, res: Response) => {
try {
const { email, password } = req.body;
const data = await authService.postLoginService(email, password);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

파라미터로 req.body 한번에 보내도록 통일하겠습니다!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resData로 통일하는 것 어떨까요?


if (data === -1) {
response.basicResponse(
res,
returnCode.BAD_REQUEST,
false,
"필요한 값이 없습니다."
);
} else if (data === -2) {
response.basicResponse(
res,
returnCode.NOT_FOUND,
false,
"존재하지 않는 이메일입니다."
);
} else if (data === -3) {
response.basicResponse(
res,
returnCode.BAD_REQUEST,
false,
"비밀번호가 일치하지 않습니다."
);
} else {
const { nickname, token } = data;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변수 선언부는 불필요하니 지워주는게 좋을것 같아요

response.dataResponse(
res,
returnCode.OK,
"장서현의 첫 api 소중히 다뤄주세요 💋",
true,
data
);
}
} catch (err) {
slack.slackWebhook(req, err.message);
console.error(err.message);
response.basicResponse(
res,
returnCode.INTERNAL_SERVER_ERROR,
false,
"서버 오류"
);
}
};
const authController = {
postSignupController,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프리티어 적용 확인해 주시면 감사합니다

postLoginController,
};

export default authController;
1 change: 1 addition & 0 deletions src/controller/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const getTestController = async (req: Request, res: Response) => {
);
}
};

const testController = {
getTestController,
};
Expand Down
8 changes: 8 additions & 0 deletions src/library/constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default {
NULL_VALUE: -1, // 필요한 값이 없을 때
WRONG_EMAIL_CONVENTION: -2, // 이메일 형식이 잘못 되었을 때
WRONG_NICKNAME_CONVENTION: -3, // 닉네임 형식이 잘못 되었을 때
WRONG_PASSWORD_CONVENTION: -4, // 비밀번호 형식이 잘못 되었을 때
EMAIL_ALREADY_EXIST: -5, // 이미 존재하는 이메일 일 때
NICKNAME_ALREADY_EXIST: -6, // 이미 존재하는 닉네임 일 때
};
8 changes: 8 additions & 0 deletions src/router/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import express from "express";
import authController from "../controller/auth";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import 시엔 항상 주석으로 description을 달아주세요!

const router = express.Router();

router.post("/login", authController.postLoginController);
router.post("/signup", authController.postSignupController);

module.exports = router;
1 change: 1 addition & 0 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ router.get("", async (req: Request, res: Response) => {
});

router.use("/test", require("./test"));
router.use("/auth", require("./auth"));

export default router;
135 changes: 135 additions & 0 deletions src/service/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// sequelize
import sequelize, { Op } from "sequelize";
// library
import jwt from "jsonwebtoken";
import bcrypt from "bcryptjs";

import { User } from "../models";
import index from "../config";
import constant from "../library/constant";
import isEmail from "validator/lib/isEmail";

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import 주석 확인!

/**
* @회원가입
* @route POST /auth/signup
* @access public
* @err 1. 필요한 값이 없을 때
* 2. 이메일 형식이 올바르지 않을 때
* 3. 닉네임 형식이 올바르지 않을 때
* 4. 패스워드 형식이 올바르지 않을 때
* 5. 이메일이 이미 존재할 때
* 6. 닉네임이 이미 존재할 때
*/
const postSignupService = async ({ email, nickname, password }) => {
// 필요한 값이 존재하지 않는 경우
if (!email || !nickname || !password) {
return constant.NULL_VALUE;
}

// email 형식이 잘못되었을 때
if (!isEmail(email)) {
return constant.WRONG_EMAIL_CONVENTION;
}

// nickname 형식이 잘못되었을 때
if (
/\W/.test(nickname) ||
/_/.test(nickname) ||
nickname.length < 2 ||
nickname.length > 8
) {
return constant.WRONG_NICKNAME_CONVENTION;
}

// password 형식이 잘못되었을 때
if (
!/^(?=.*[a-zA-Z])((?=.*\d)(?=.*\W)).{8,64}$/.test(password) ||
/\s/.test(password)
) {
return constant.WRONG_PASSWORD_CONVENTION;
}

// email이 이미 존재할 때
const emailExist = await User.findAll({
where: {
email,
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

find 쿼리시에는 항상 is_delete 를 포함하여 where절을 걸어주세요

});
if (emailExist.length > 0) {
return constant.EMAIL_ALREADY_EXIST;
}

// nickname이 이미 존재할 때
const nicknameExist = await User.findAll({
where: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where절에 is_delete 걸어주세요!

nickname,
},
});

if (nicknameExist.length > 0) {
return constant.NICKNAME_ALREADY_EXIST;
}

// 새로운 유저 생성 & 토큰 발급
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
const user = await User.create({
email,
password: hashedPassword,
nickname,
});

const payload = {
user: {
id: user.id,
},
};

const token = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: "14d",
});
return token;
};

/**
* @로그인
* @route Post auth/login
* @access public
*/

const postLoginService = async (email: string, password: string) => {
// 요청 바디 부족
if (!email || !password) {
return -1;
}

// 존재하지 않는 이메일
const user = await User.findOne({ where: { email: email } });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where절 is_delete 걸어주세요!

if (!user) {
return -2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constant library 적용!

}

// 비밀번호 일치 X
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return -3;
}

// 성공 시
// 토큰 만들기
const payload = {
user: {
id: user.id,
},
};
const nickname = user.nickname;
const token = jwt.sign(payload, index.jwtSecret, { expiresIn: "14d" });
return { nickname, token };
};

const authService = {
postSignupService,
postLoginService,
};

export default authService;