1. Joi 란?
Joi는 JavaScript 유효성 검증을 위한 라이브러리 입니다.
Joi는 여러 타입과 규칙을 이용해 유효성을 검증하며,
유효성 검증에 실패하면 오류를 발생시킵니다.
클라이언트가 요청한 정보들이 서버로 전달될 때,
알맞는 형식인지 아닌지 검증하는 작업을 Joi가 한다.
2. 유효성 검증(Validation)이란?
말 그대로 어떤 것을 검증한다
와 같이 value가 1인지 판단해서
true, false를 반환하는 함수조차
Validation이라고 볼 수 있습니다.
3. Joi 설치하기
VScode의 터미널에서
yarn add joi
로 joi를 설치할 수 있다.
4. Joi를 이용한 Validation 시작하기
1. 문자열 길이 검증하기
import Joi from "joi";
// Joi 스키마를 정의합니다.
const schema = Joi.object({
// name Key는 문자열 타입이고, 필수로 존재해야합니다.
// 문자열은 최소 3글자, 최대 30글자로 정의합니다.
name: Joi.string().min(3).max(30).required(),
});
// schema를 이용해 user 데이터를 검증합니다.
const validation = schema.validate(user);
// 검증 결과값 중 error가 존재한다면 에러 메시지를 출력합니다.
if (validation.error) {
console.log(validation.error.message);
} else {
// 검증 결과값 중 error가 존재하지 않는다면, 데이터가 유효하다는 메시지를 출력합니다.
console.log("Valid Data!");
}
위의 코드는 객체의 name Key에 따른 Value의 문자열 길이가 3 ~ 30 사이인 이름를 요구합니다.
만약,
const user = { name: "Fo" };
객체의 name Key에 따른 Value의 길이가 "Fo"와 같이 2글자인 경우,
console.log(validation.error.message);
위의 코드가 실행 돼,
이 출력 됩니다.
const user = { name: "Foo Bar" };
과 같이 3 ~ 30글자 사이인 Value이면,
처럼 통과된 Data라고 출력 됩니다.
2. 이메일 검증하기
import Joi from "joi";
// Joi 스키마를 정의합니다.
const schema = Joi.object({
// name Key는 문자열 타입이고, 필수로 존재해야합니다.
// 문자열은 이메일 형식에 맞아야합니다.
email: Joi.string().email().required(),
});
// schema를 이용해 user 데이터를 검증합니다.
const validation = schema.validate(user);
// 검증 결과값 중 error가 존재한다면 에러 메시지를 출력합니다.
if (validation.error) {
console.log(validation.error.message);
} else {
// 검증 결과값 중 error가 존재하지 않는다면, 데이터가 유효하다는 메시지를 출력합니다.
console.log("Valid Email User!");
}
위의 코드는 email에 대한 Key에 따른 Value가 이메일 형식인지 판별해주는 Validation 입니다.
만약,
// 검증할 데이터를 정의합니다.
const user = { email: "foo" };
위의 코드처럼 @Emali.com이 없다면,
console.log(validation.error.message);
코드를 통해,
위와 같이 오류가 났다고 출력됩니다.
하지만,
// 검증할 데이터를 정의합니다.
const user = { email: "foo@example.com" };
같이 이메일 형식을 띄는 Value 라면,
위와 같이 통과된 이메일 데이터다고 출력됩니다.
5. Joi를 이용한 Validation 처리 (비동기)
Joi는 검증한 결과값에 에러 내용이 포함되도록 구현할 수 있지만,
검증에 실패했을 때, 에러가 발생하도록 구현하는 방법 또한 존재합니다.
import Joi from "joi";
// Joi 스키마를 정의합니다.
const schema = Joi.object({
// name Key는 문자열 타입이고, 필수로 존재해야합니다.
// 문자열은 최소 3글자, 최대 30글자로 정의합니다.
name: Joi.string().min(3).max(30).required(),
});
try {
// schema를 이용해 user 데이터를 검증합니다.
const validation = await schema.validateAsync(user);
// 검증 결과값 중 error가 존재하지 않는다면, 데이터가 유효하다는 메시지를 출력합니다.
console.log("Valid Data!");
} catch (error) {
// 검증에 실패한다면, 에러 메시지를 출력합니다.
console.log(error.message);
}
예제 코드에서 유효성 검증을 하기 위해 사용하는 메서드를
validate() → validateAsync()(비동기 처리 함수)로 변경하여 데이터를
비동기적으로 검증할 수 있다.
비동기로 Joi를 사용하게 된다면,
이전과 다르게 반환되는 결과값에
메시지가 포함되지 않고,
바로 에러가 발생하게 됩니다.
validate와 validateAsync() 차이
validateAsync는
위와 같이 바로 에러가 발생해 오류 메시지가 출력되고,
validate는
위와 같이 객체의 error Key를 바탕으로 Value인 오류 문구를 출력하게 됩니다.
(
try-catch-exception 문 + while + bool을
함께 사용해서 원하는 입력 값이 유저한테서
들어 올 때 까지 무한 반복시킬 수도 있다!
)
(
try-catch-exception 문을 사용해야겠지?
물론, 옛날에 자주 쓰다가 안 써서 익숙지 않지만...
)
6. 할 일 생성 API 리팩토링하기
import joi, { valid } from "joi";
const createTodoSchema = joi.object({
value: joi.string().min(1).max(50).required(),
});
// 할일 등록 API
// (중요) async를 줘서 비동기 함수로 구현한다.
router.post("/todos", async (req, res) => {
// 쿨라이언트에게 전달받은 데이터를 검증합니다.
const validation = await createTodoSchema.validateAsync(req.body.value);
// 클라이언트에게 전달받은 value 데이터를 변수에 저장합니다.
const { value } = validation;
const todoMaxOrder = await Todo.findOne().sort("-order").exec();
const order = todoMaxOrder ? todoMaxOrder.order + 1 : 1;
const todo = new Todo({ value, order });
await todo.save();
return res.status(201).json({ todo: todo });
});
joi를 활용한 유효성 검증을 진행하였습니다.
이제 try-catch-exception 문을 활용해
예외처리 및 에러 발생의 경우 에러 정보를
클라이언트에게 응답으로 보내보겠습니다.
// 할일 등록 API
// (중요) async를 줘서 비동기 함수로 구현한다.
router.post("/todos", async (req, res) => {
try {
// 쿨라이언트에게 전달받은 데이터를 검증합니다.
const validation = await createTodoSchema.validateAsync(req.body);
// 클라이언트에게 전달받은 value 데이터를 변수에 저장합니다.
const { value } = validation;
const todoMaxOrder = await Todo.findOne().sort("-order").exec();
const order = todoMaxOrder ? todoMaxOrder.order + 1 : 1;
const todo = new Todo({ value, order });
await todo.save();
return res.status(201).json({ todo: todo });
} catch (error) {
console.log(error);
// Joi에서 발생한 에러로 유효성 검사에서 발생한 에러이면
if (error.name === "ValidationError")
return res.status(400).json({ errorMessge: error.errorMessge });
// 그 밖의 에러이면 서버의 에러라고 클라이언트에게 보냅니다.
return res.status(500).json({ errorMessge: error.errorMessge });
//500 번대 상태는 서버의 에러라는 걸 의미한다.
}
});
위와 같이 코드를 작성하고 , Insomnia를 통해서
어떤 에러 메시지가 출력되는지 확인해보면
1) value가 존재하지 않을 때
value는 필수적으로 필요하다고 Clinet에게 알려줍니다.
2) value가 비었을 때
value가 공백이라고 Client에게 알려줍니다.
3) value가 숫자 타입인 경우
value는 문자열 타입이어야 한다고 Client에게 알려줍니다.
4) value가 길이가 50이상인 경우
value가 50 글자 이상이면 안 된다고 Client에게 알려줍니다.