나의개발일지
[Joi] 유효성 검사를 해 볼까????? 해보자!!!!!! 본문
본 글은 작성자가 어디선가 주워듣고 이해한 내용들을 개인적인 언어로 작성한 게시물입니다.
잘못된 내용이 존재할 수 있으니, 읽게 되신다면 이점을 감안해 주세요!!!
- 우리는 결국 자신이 가진 이야기로 상대방을 이해할 수 있을 뿐이다.-
웹애플리케이션에서 백엔드가 보통 다루는 것은 데이터이다. 상품데이터, 유저데이터, 기타 등등의 다양한 데이터를 다루게 된다. 이러한 데이터들은 데이터베이스에 저장되고, 데이터베이스에서 다시 꺼내와서 화면에 그려지게 되는 것이다. 그렇기 때문에 데이터가 데이터베이스에 저장 되기 전에는 알맞은 데이터인지에 대한 검사가 필요할 것이다. 만약 이름을 나타내는 데이터인데, 숫자가 들어간다면, 이는 잘못된 데이터이다. 그렇기 때문에 데이터를 저장하기 전에는 유효성 검사가 필요하고, node에서 유효성검사를 도와주는 joi모듈을 알게되서 joi모듈을 사용해서 간단한 데이터 유효성 검사를 해보고자 한다.
간단 프로젝트
이번에 해볼 프로젝트는 간단하게 mongoose에 데이터를 넣는데, 들어가는 데이터를 joi모듈을 이용해서 먼저 유효성 검사를 해보는 것이다. 그런데 mongoose에도 스키마 작성 시 유효성 검사를 도와줄 수 있게 스키마를 작성할 수 있는데, 그렇다면 어떤 것을 사용해야 할지 아직은 잘모르겠다. 우선, joi를 사용하기 전에 mongoose의 스키마를 통한 유효성 검사만을 이루어질 경우 어떻게 사용할 수 있는지 알아보자!!!
// 모델작성하기
const mongoose = require("mongoose");
const movieSchema = new mongoose.Schema({
title:{
type:String,
required:true,
},
genre:{
type:String,
required:true
},
date:{
type:Number,
required:true
},
rate:{
type:String,
enum:["A", "B", "C", "D", "E"],
required:true
},
actors:{
type:[],
required:true
},
email:{
type:String,
match: /^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/
}
});
const Movie = mongoose.model("Movie", movieSchema);
module.exports = Movie;
해당 코드는 mongoose 모델을 만드는 스키마를 정의한 것이다. 해당 스키마에 속하지 않는 데이터가 들어 올 경우 오류가 발생하게 된다.
// 오류클래스 정의
class ExpressError extends Error{
constructor(message, status){
super();
this.message = message;
this.status = status;
}
}
module.exports = ExpressError;
// 비동기함수 오류 핸들러
module.exports = func =>{
return (req,res,next) => {
func(req,res,next).catch(next);
}
}
위의 코드는 오류를 핸들링 하기 위해서 작성한 코드로 오류클래스를 정의해서 필요시 해당 오류를 발생시키기 위해서 정의했고, 비동기함수의 오류의 경우 try...catch말고 위의 비동기함수 오류 핸들러를 이용해서 처리할 수 있다.
const express = require("express");
const app = express();
const mongoose = require("mongoose");
const Movie = require("./models/movie");
const wrapAsync = require("./wrapAsync");
const ExpressError = require("./ExpressError");
mongoose.connect('mongodb://127.0.0.1:27017/movieAPP')
.then(()=>{
console.log("MongoDB CONNECT")
})
.catch((e)=>{
console.log("ERROR")
console.log(e)
});
app.use(express.json())
app.use(express.urlencoded({extended:true}));
app.get("/", (req,res)=>{
res.send("homepage");
});
app.get("/movies", async(req,res)=>{
const movies = await Movie.find();
res.send(movies);
})
// 비동기함수의 경우 try...catch를 이용해서 에러를 핸들링 후 에러가 발생하면
// next(e)가 제너릭 에러 핸들러를 호출하게 된다.
// app.post("/movies", async(req,res,next)=>{
// try{
// const movieData = req.body;
// const movie = new Movie(movieData);
// await movie.save()
// res.status(201).send("Create Movie Data")
// }catch(e){
// next(e)
// }
// })
// try...catch를 이용하지 않고, 비동기 함수를 콜백함수를 이용해서 에러처리를 할 수 있다.
app.post("/movies", wrapAsync(async(req,res,next)=>{
const movieData = req.body;
const movie = new Movie(movieData);
await movie.save()
res.status(201).send("Create Movie Data")
}))
// 위의 라우트 핸들러에 정의되지 않은 주소로 요청이 들어올 경우 실행되는 미들웨어
// 정의한 에러가 발생하게 된다. 그리고 next()로 해당 에러를 넘겨주면 아래의 제너릭 에러핸들러가 호출된다.
app.all("*", (req,res,next)=>{
next(new ExpressError("Page Not Found", 404))
})
// 제너릭 에러 핸들러 작성하기
app.use((err, req, res, next)=>{
const { status= 500 } = err;
if(!err.message){
err.message = "Error Default Message!!!!";
}
res.status(status).send(err.message);
})
app.listen(3000, ()=>{
console.log("SERVER STATING")
});
mongoose 스키마에 맞지 않는 데이터 유효성 검사
위에서는 정의한 코드에 이제 데이터를 보내보자!!! 각 타입을 잘못 설정하거나 누락하거나, 하는 등의 작업을 통해서 실행해보면 다음과 같다. 타이틀을 포함하지 않고 date는 데이터타입을 잘못 입력하고, rate는 enum에 해당하지 않고, email은 정규식에 맞지 않는 오류를 발생시켰다. 이렇게 데이터에 대한 유효성 검사 및 에러를 핸들링 해서 비정상적인 오류를 막는다.

Joi를 이용한 데이터 유효성 검사
위에서는 mongoose의 스키마에 맞지 않는 데이터가 있다면 에러를 핸들링 했다. 다음은 Joi모듈을 이용한 것으로 Joi모듈을 이용하기 위해서는 먼저 npm에서 install하고서 모델 별로 스키마를 작성해 주어야 한다.
//schemas.js
const Joi = require("joi");
module.exports.movieSchema = Joi.object({
title:Joi.string().required(),
genre:Joi.string().required(),
date:Joi.number().required(),
rate:Joi.string().valid('A', 'B',"C","D","E").required(),
actors:Joi.array().required(),
email:Joi.string().email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } })
})
위의 코드는 Joi모듈을 이용해서 새롭게 스키마를 작성한 것인데, 사실 mongoose와 어떤 차이점이 있는지 잘모르겠다. 에러 핸들링에 좀 더 간단히 할 수 있다고 하는데 어떤 점에 서 그런지는 아직 감이 잡히지 않는다. 우선 위와 같이 스키마를 작성한 뒤 유효성 검사를 하는 함수로 만들어서 유효성 검사가 필요한 부분에 콜백함수로 넣어주면 된다.
.
.
const {movieSchema} = require("./schemas.js");
.
.
const validateMovie = (req, res, next) => {
const { error } = movieSchema.validate(req.body);
if (error) {
const msg = error.details.map(el => el.message).join(',')
throw new ExpressError(msg, 400)
} else {
next();
}
}
.
.
app.post("/movies", validateMovie, wrapAsync(async(req,res,next)=>{
const movieData = req.body;
const movie = new Movie(movieData);
await movie.save()
res.status(201).send("Create Movie Data")
}))

정리하자면
사실 두개의 차이점에 대해서는 확실히 이렇다 라고 정의할 수 없다. 하지만, Joi 모듈을 이용한 유효성 검사는 좀 더 다양한 유효성 검사를 제공한다고 한다. 현재 위에 작성된 코드는 기본적인 유효성 검사일 뿐 Joi모듈에서 제공하는 것은 이것 이외에도 엄청 많다. 모든 기능을 알 수는 없으나 필요하다면 찾아서 사용하면 될거 같다. 그리고 중요한 점은 Joi의 유효성 검사는 mongoose의 스키마에 정의되 것과는 별개로 유효성검사를 실행한다는 점이다. 그렇기 때문에 Joi모듈에서 유효성검사를 제대로 마친다면 무리없이 mongoose의 스키마에 오류없이 넘어갈 수 있을 것이다. mongoose의 에러 처리가 Joi보다 어렵기 때문에 Joi모듈을 이용한다는 말도 있다. 다음에 좀 더 알아봐야 겠다. 오늘의 포스팅된 글에는 바로 이전에 작성된 에러핸들러의 내용이 부족하다고 느껴서 추가적으로 에러핸들러 내용도 같이 작성하게 되었다. 제너릭한 에러핸들러를 만들어서 에러가 발생할 경우 해당 에러핸들러로 들어 오게 해서 처리해주는 것이다. 또한 직접, 에러를 정의해서 해당 에러가 발생할 만한 곳에서 에러를 발생시키고 제너릭 에러 핸들러를 호출하게 했다.
본 글은 작성자가 공부한 내용을 토대로 작성되었기 때문에
부족한 내용이나 잘못된 내용이 있을 수 있습니다.
아 재미있다. 재미있다. 재미있다. 하하하하하
'NodeJS.Express.MongoDB' 카테고리의 다른 글
| [Express] 맛있는 cookie!!!! 멋있는 session!!! (0) | 2023.01.23 |
|---|---|
| [mongoose] mongoose의 1:N 관계를 설정해보자!!! (0) | 2023.01.22 |
| [Express] 알아보자!! Error-handler 사용하자!! Error-handler 포기하자..... (1) | 2023.01.20 |
| [Express] express는 Middleware가 엄청나게 많다. 와 middleware야 너는 정말 좋겠구나 (1) | 2023.01.20 |
| [nodeJS+Express+mongoose] 연결하자 연결하자!!!! (0) | 2023.01.20 |