목표
회원가입 시 아이디, 패스워드, 닉네임 등을 입력할 때 입력된 값을 유효성을 체크하는 기능을 구현
이전에는..
아래와 같이 각각 체크해야 하는 요소마다 함수를 구현해서 체크했었다.
이렇게 구현하면 router 내 체크해야 할 요소가 많아질수록 코드가 복잡해진다.
//router_register_sample.js 이전에는 이렇게 구현했었다.
const valCheckId = function (target_nickname) {
const regex_nick = /([a-z]|[A-Z]|[0-9]){3,}/g;
const nicknameCheckResult = target_nickname.match(regex_nick);
return nicknameCheckResult == target_nickname;
};
router.post('/', Validator('register'), async (req, res) => {
try {
let { userId, userPw, nickname } = req.body;
let { userId, userPw, nickname } = await userSchema.validateAsync(req.body);
if (!valCheckId(nickname)) {
res.status(400).send({
errorMessage:
'닉네임은 알파벳 대소문자와 숫자만 사용할 수 있으며, 최소 3자리 이상이어야 합니다.',
});
return;
}
이번 포스팅에 기록할 내용
Joi를 활용하여 좀 더 효율적이고 확장성이 있게 구현해본 내용을 기록하고자 한다.
여러 레퍼런스들을 참고해서 이해하며 적용해보았다. 앞으로 계속 활용하면서 더 깊게 이해하고자 한다.
- Setting
package
npm i joi
directory & files
middlewares
Validator.js
routers
router_register.js
validators
index.js
register.validator.js
- 코드 및 간략한 실행 흐름
routers/router_register
const { Users } = require('../models');
const express = require('express');
const router = express.Router();
const crypto = require('crypto');
const Validator = require('../middlewares/Validator');
//--- 1. 라우터 호출 시 옵션으로 미들웨어 Validator(argument)를 호출한다. ---
router.post('/', Validator('register'), async (req, res) => {
try {
let { userId, userPw, nickname } = req.body;
const salt = crypto.randomBytes(128).toString('base64');
userPw = crypto
.createHash('sha512')
.update(userPw + salt)
.digest('hex');
await Users.create({
userId: userId,
userPw: userPw,
nickname: nickname,
salt: salt,
});
res.status(200).send({ msg: '회원가입 완료!' });
} catch (error) {
console.log(error);
res.status(400).send({
errorMessage: '알 수 없는 오류가 발생했습니다. 관리자에게 문의해주세요.',
});
}
});
module.exports = router;
middlewares/Validator.js
const Joi = require("joi");
const Validators = require('../validators')
//--- 2. validators 경로 내 index 파일을 불러와 파라미터로 받은 항목이 있는지 체크한다 ---
module.exports = function(validator) {
//parameter로 받은 값이 validators/index.js 내 존재하지 않으면 throw error
if(!Validators.hasOwnProperty(validator))
throw new Error(`'${validator}' validator is not exist`)
//--- 3. 해당하는 파일이 있다면 그 파일 내 구현한 Schema로 체크한다 ---
return async function(req, res, next) {
try {
const validated = await Validators[validator].validateAsync(req.body)
req.body = validated
next()
} catch (err) {
if(err.isJoi)
res.status(401).send({
errorMessage: '입력정보를 다시 확인해주세요.',
});
}
}
}
validators/index.js
//--- 2번에서 본 위치에 export 된 요소가 있는지 체크한다. ---
const register = require('./register.validator')
module.exports = {
register,
}
//--- validator 기준이 늘어남에 따라 파일이 많아지면 아래와 같이 객체에 추가한다 ---
//const register = require('./register.validator')
//const post = require('./post.validator')
//module.exports = {
// register, post
//}
validators/register.validator.js
//--- 다양한 조건들을 설정할 수 있다. joi.dev 참고 ---
//--- 3번에서 본 위치의 Schema를 기준으로 체크한다 ---
const Joi = require('joi');
const registerSchema = Joi.object({
userId: Joi.string().required(),
userPw: Joi.string().required(),
nickname: Joi.string().required(),
});
module.exports = registerSchema;
위처럼 미들웨어로 joi를 구현하지 않고,
router 내 joi를 바로 적용한다면 아래와 같이 구현할 수 있다.
const { Users } = require('../models');
const express = require('express');
const router = express.Router();
const crypto = require('crypto');
const Joi = require('joi');
//--- Schema 선언 ---
const userSchema = Joi.object({
userId: Joi.string().required(),
userPw: Joi.string().required(),
nickname: Joi.string().required(),
});
//회원가입
router.post('/', async (req, res) => {
try {
//--- Schema 적용 ---
let { userId, userPw, nickname } = await userSchema.validateAsync(req.body);
const salt = crypto.randomBytes(128).toString('base64');
userPw = crypto
.createHash('sha512')
.update(userPw + salt)
.digest('hex');
await Users.create({
userId: userId,
userPw: userPw,
nickname: nickname,
salt: salt,
});
res.status(200).send({ msg: '회원가입 완료!' });
} catch (error) {
console.log(error);
res.status(400).send({
errorMessage: '알 수 없는 오류가 발생했습니다. 관리자에게 문의해주세요.',
});
}
});
만약 Schema 조건에 부합하지 않는 경우, 아래와 같이 콘솔 상 에러를 던져준다.
[Error [ValidationError]: "nickname" must be a string] {
_original: { userId: 'test009', userPw: 'testpw007', nickname: 23123 },
details: [
{
message: '"nickname" must be a string',
path: [Array],
type: 'string.base',
context: [Object]
}
]
}
이번에 joi를 활용해보면서..
- 미들웨어를 활용하여 확장성 있는 코드를 구축하는 것에 대해 더 깊이 고민해볼 수 있었다.
- joi의 검증 조건에 따라 출력되는 케이스별 에러 메시지를 기준으로 직접 error 메시지를 생성해서 던져주는 방법에 대한 공부가 필요하다.
References
Middleware Based Joi Validation in ExpressJS
https://dev.to/tayfunakgc/middleware-based-joi-validation-in-expressjs-2po5
joi.dev
Node.js + Express API - Request Schema Validation with Joi
https://jasonwatmore.com/post/2020/07/22/nodejs-express-api-request-schema-validation-with-joi
'개발일지' 카테고리의 다른 글
node.js - sequelize MySQL 기초 활용 기록 (DB, Seed) (0) | 2021.10.29 |
---|---|
AWS EC2에 MySQL 세팅하는 방법 기록 (0) | 2021.10.27 |
[Javascript] 요소 노드를 추가해서 html에 적용하기 (0) | 2021.08.05 |
[개발일지] Open Graph(og) & 스파르타 웹개발 종합반을 마친 후기 (0) | 2021.07.19 |
[Python] Beautiful Soup - 스크래핑한 데이터의 특정 태그 제거 방법 (0) | 2021.07.17 |