유동
[Express] 프론트에서 온 값을 검증하는 exception 모듈 만들기 본문
백엔드에서 api를 만들때 db연결, sql등 할게 많겠지만
제일중요한건 프론트엔드에서 온 값들이 유효한지 검증해야하는게 제일 중요하다
- 만약 로그인 api를 작성한다고 하자, 프론트에서 받는 값은 loginId, password 이 두가지 값을 받을것이다.
우리는 loginId, password 이 두 값을 들고 db에 조회해볼것이다.
하지만 받아온 두 값이 이상한 값이 들어오면 어떻게될까?
- 당연히 프론트에서 loginId 정규식, password 정규식 검사해서 넘겨주기때문에 그런 일은 거의 없을것이다.
하지만 백엔드가 항상 염두해두는 부분은 프론트엔드 코드는 조작이 가능하다는 점이다.
- 만약 유저가 불순한 마음을 품고 프론트의 검증하는 로직을 지운 후 loginId나 password를 100만자, 1000만자 보내버리면?
- 혹은 db 컬럼 자료형에 맞지 않는 값을 보내버린다면??
당연히 데이터베이스에서 에러를 내뱉어주기때문에 db가 터질일은 웬만하면 없을것이다.
하지만 백엔드에서 한번 더 검증해준다면 데이터의 무결성을 보장하고 db io를 줄여주는것에 일조할수 있을것이다...
로그인 api이기때문에 굳이 정규식 검사는 해주지 않고 그냥 값이 undefined 인지, ""(빈 문자열)인지, null인지, 길이 검사 등 만 해줄것임
if (loginId === undefined || loginId === "" || loginId === null ) {
// ... throw new Error("...")
}
if (loginId.length < 1 || loginId.length > maxLoginIdLength) {
// ... throw new Error("...")
}
- 이렇게 해줄수도 있지만?
- 프로그램이 커질수록 api개수가 수백개가 될수도 있는데 모든 api 하나하나 이렇게 해주면 지옥이 펼처질 것이다
이렇게 해주면 어떨까?
exception(loginId, "loginId").checkInput().checkLength(1, maxLoginIdLength);
exception(password, "password").checkInput().checkLength(1, maxPwLength);
코드를 보자마자 이해가 간다. input값을 check 해주고(undefined인지, ""인지) 길이가 1부터 maxLoginIdLength 까지인지?
일단 해당 모듈을 보자
const { loginIdRegex, pwRegex, nameRegex, phoneNumberRegex, emailRegex } = require("../module/regex");
const badRequestErrorCode = 400;
const errorMessage = {
invalidInput: "요청값이 잘못되었습니다",
length: "길이가 비정상적입니다",
regex: "정규표현식 실패",
isNumber: "정수가 아닙니다",
}
function Exception(input, name) {
this.checkInput = () => {
if (input === undefined) this.setError(errorMessage.invalidInput);
return this;
}
this.checkLength = (min, max) => {
if (input.length < min || input.length > max) this.setError(errorMessage.length);
return this;
}
this.checkIdRegex = () => {
if (!loginIdRegex.test(input)) this.setError(errorMessage.regex);
return this;
}
this.checkPwRegex = () => {
if (!pwRegex.test(input)) this.setError(errorMessage.regex);
return this;
}
this.checkNameRegex = () => {
if (!nameRegex.test(input)) this.setError(errorMessage.regex);
return this;
}
this.checkPhoneNumberRegex = () => {
if (!phoneNumberRegex.test(input)) this.setError(errorMessage.regex);
return this;
}
this.checkEmailRegex = () => {
if (!emailRegex.test(input)) this.setError(errorMessage.regex);
return this;
}
this.isNumber = () => {
if (isNaN(Number(input))) this.setError(errorMessage.isNumber);
return this;
}
this.setError = (message) => {
const error = new Error(`${name}: ${message}`);
error.status = badRequestErrorCode;
throw error;
}
}
const exception = (input, name) => {
const res = new Exception(input, name);
return res;
}
module.exports = exception;
이렇게 메서드 체이닝 방식을 사용하기 위해 조건을 만족하면 자기 자신을 리턴, 만족하지 못하면 에러 를 생성, 코드를 지정해주고 에러를 던져주는 거임
해당 모듈을 아래처럼 try-catch 안에서 사용해준다면?
router.post("/login", async (req, res, next) => {
const { loginId, password } = req.body;
const result = {
message: "",
token: null
};
let client = null;
try {
// request값 유효성 검증
exception(loginId, "loginId").checkInput().checkLength(1, maxLoginIdLength);
exception(password, "password").checkInput().checkLength(1, maxPwLength);
if (loginId === process.env.ADMIN_ID && password === process.env.ADMIN_PW) {
const token = await jwt.adminSign();
res.cookie('accessToken', token);
return res.redirect('/admin');
}
// db연결
client = createClient();
await client.connect();
const sql = "SELECT id FROM user_TB WHERE login_id = $1 AND password = $2";
const params = [loginId, password];
const data = await client.query(sql, params);
if (data.rows.length !== 0) {
const userPk = data.rows[0].id;
const token = await jwt.userSign(userPk, loginId);
result.token = token;
} else {
result.message = "아이디 또는 비밀번호가 올바르지 않습니다";
}
res.send(result);
} catch (error) {
console.error(error);
next(error);
} finally {
if (client) {
await client.end();
}
}
});
exception 모듈에서 throw 해주는 에러들은 전부다 400 status code를 가지고 던져주기 때문에 catch부분에서 status가 400인 것들만 잡아내서 프론트에 던져주면 된다.
다른 api도 그냥 exception 가져와서 그냥 값 넣어주기만 하면 된다
// 회원가입 api
router.post("/signup", async (req, res, next) => {
const { loginId, password, name, phoneNumber, email } = req.body;
const result = {
data: "",
message: "",
};
let client = null;
try {
exception(loginId, "loginId").checkInput().checkIdRegex();
exception(password, "password").checkInput().checkPwRegex();
exception(name, "name").checkInput().checkNameRegex();
exception(phoneNumber, "phoneNumber").checkInput().checkPhoneNumberRegex();
exception(email, "email").checkInput().checkEmailRegex();
....
postman 테스트 결과
하지만 아직 끝이 아니다
- catch부분에서 에러를 잡아서 하나하나 처리해주고있는데 이걸 next()를 이용해 에러를 한곳에다 몰빵처리할것이다
- express에서 지원해주는 에러처리 미들웨어를 이용해서 바꿔볼거임
'node.js > ExpressJS' 카테고리의 다른 글
[Express] 비동기함수에 반복되는 try-catch 없애기 (0) | 2024.01.05 |
---|---|
[Express] 요청 객체를 dto로 변환하기 (0) | 2023.12.13 |
[Express] jwt로 로그인 유지시켜주기 (0) | 2023.11.04 |
[Express] node-express에서 에러 핸들링 하기 (1) | 2023.10.24 |
[Express] Cannot read properties of undefined (0) | 2023.07.18 |