[Node.js], [JS] Joi를 이용한 데이터 Validation
![[Node.js], [JS] Joi를 이용한 데이터 Validation](/content/images/size/w1920/2024/12/joi.png)
Joi를 이용한 데이터 Validation 🛠️✨
1. Joi란 무엇인가? 🤔
Joi
는 JavaScript 애플리케이션에서 데이터 유효성 검사를 쉽게 처리할 수 있도록 도와주는 강력한 라이브러리이다. 입력 데이터가 사전에 정의된 스키마와 일치하는지 검증하며, 이 과정에서 발생하는 오류를 효율적으로 처리할 수 있게 해준다.
🎯 Joi의 주요 특징
- 스키마 기반 검증: 데이터 구조를 정의하고, 이를 기반으로 입력 데이터를 검증할 수 있다.
- 유연한 커스터마이징: 내장 메서드를 활용하거나,
custom
메서드를 사용해 특정 조건을 추가로 정의할 수 있다. - 읽기 쉬운 문법: 간단하고 직관적인 코드로 데이터 검증 로직을 작성할 수 있다.
- 다양한 데이터 타입 지원: 문자열, 숫자, 배열, 객체 등 다양한 데이터 타입에 대해 정교한 검증이 가능하다.
2. Joi를 사용해야 하는 이유 🌟
현대 애플리케이션은 클라이언트에서 서버로, 서버에서 데이터베이스로 이동하는 많은 데이터가 있다. 이 데이터가 올바르지 않으면 예상치 못한 오류나 보안 취약점으로 이어질 수 있다.
Joi를 사용하면:
- 입력 데이터가 유효하지 않을 때 즉시 검출할 수 있다. 🚦
- 비즈니스 로직에서 데이터 검증 코드를 분리해, 유지보수를 쉽게 한다. 🛠️
- 커스터마이징 가능한 에러 메시지를 통해 사용자 경험을 개선할 수 있다. 💡
3. Joi 설치하기 📥
먼저, 프로젝트에 Joi를 설치해야 한다.
npm install joi
설치 후, 다음과 같이 사용할 수 있다:
import Joi from 'joi';
4. Joi의 기본 사용법 🧑🏫
간단한 예제: 사용자 입력 데이터 검증
import Joi from 'joi';
const schema = Joi.object({
username: Joi.string().min(3).max(30).required(),
password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).required(),
email: Joi.string().email({ tlds: { allow: false } })
});
const data = {
username: 'Austin',
password: '12345',
email: 'austin@example.com'
};
const result = schema.validate(data);
if (result.error) {
console.error(result.error.details); // 유효성 검사 실패 시
} else {
console.log('입력 데이터가 유효합니다.'); // 유효성 검사 통과
}
5. 실무 예제: 차량 데이터 검증 🚗
내가 실무에서 사용하는 소스를 에제로, 데이터 스키마를 정의하고 검증하는 과정을 살펴보자.
예제: 차량 입출차 데이터 검증
import Joi from 'joi';
// 차량 입출차 입력 스키마 정의
export const LprInput = Joi.object({
serial: Joi.string()
.pattern(/^\d{4}-\d{4}$/)
.custom((value, helpers) => {
// 특정 조건 검증: 7번째 문자는 '0' 또는 '1'이어야 함
if (value.substring(6, 7) !== '0' && value.substring(6, 7) !== '1') {
return helpers.error('any.invalid');
}
return value;
})
.required(),
date: Joi.string().required(), // 날짜 필수
carNum: Joi.string().required(), // 차량 번호 필수
carBin: Joi.string().optional().allow(null) // LPR 이미지 빈 값 허용
});
// 데이터 예제
const inputData = {
serial: '1234-5678',
date: '2024-12-09',
carNum: '123가4568',
carBin: null
};
const validationResult = LprInput.validate(inputData);
if (validationResult.error) {
console.error(validationResult.error.details); // 검증 실패 시
} else {
console.log('데이터 검증 성공:', validationResult.value); // 검증 성공 시
}
예제: 차량 정보 스키마 검증
더 복잡한 데이터를 다룰 때도 Joi는 유연하게 사용 가능하다.
// schema.js
export const LprInfo = Joi.object({
serial: Joi.string()
.pattern(/^\d{4}-\d{4}$/)
.custom((value, helpers) => {
if (value.substring(6, 7) !== '0' && value.substring(6, 7) !== '1') {
return helpers.error('any.invalid');
}
return value;
})
.required(),
cdDistObsv: Joi.string().required(), // 장비 관리번호 필수
date: Joi.string().required(), // 날짜 필수
carNum: Joi.string().required(), // 차량 번호 필수
carBin: Joi.string().optional().allow(null), // LPR 이미지빈 값 허용
dateConvert: Joi.string().max(14).required(), // 변환된 날짜
inOut: Joi.string().valid('0', '1').required(), // 입출차 구분
tableParkCarinOut: Joi.string().required(), // 테이블명 필수
MR: Joi.string().max(2).required(), // HH 필수
RegData: Joi.string().max(8).required() // yyyymmdd 필수
});
// 데이터 예제
const infoData = {
serial: '2001-1101',
cdDistObsv: '2001',
date: '2024-12-09',
carNum: '123나5678',
carBin: null,
dateConvert: '20241209120000',
inOut: '1',
tableParkCarinOut: 'parking_table',
MR: '12',
RegData: '20241209'
};
const infoValidationResult = LprInfo.validate(infoData);
if (infoValidationResult.error) {
console.error(infoValidationResult.error.details); // 검증 실패 시
} else {
console.log('데이터 검증 성공:', infoValidationResult.value); // 검증 성공 시
}
// 데이터 예제
const infoData = {
serial: '2001-1101',
cdDistObsv: '2001',
date: '2024-12-09',
carNum: '123나5678',
carBin: null,
dateConvert: '20241209120000',
inOut: '1',
tableParkCarinOut: 'parking_table',
MR: '12',
RegData: '20241209'
};
// service.js
import * as schema from '#schema/schema.js';
export const service = async (data, onErrorCallback) => {
try{
const lprInfo = {
serial: lprData.serial,
cdDistObsv: lprData.serial.substring(0, 4),
date: lprData.date,
carNum: lprData.carNum,
carBin: lprData.carBin,
dateConvert: lib.convertToCompactFormat(lprData.date),
inOut: lprData.serial.substring(6, 7) === '0' ? '0' : '1',
tableParkCarinOut: getTableNameByInOut(extractInOut(lprData.serial)),
MR: `MR${parseInt(lib.convertToCompactFormat(lprData.date).substring(8, 10))}`,
RegDate: lib.convertToCompactFormat(lprData.date).substring(0, 8),
};
await schema.LprInfo.validationAsync(lprInfo); // 서비스단에서 validation 검증 실행
}catch(error){
console.error('[service] Error!');
if (onErrorCallback) {
onErrorCallback(error);
// validation 검증 에러 발생 시 에러처리를 하여 호출단에서 catch(라우터)
}
}
}
6. Joi의 주요 메서드 🔧
required()
: 필수 값 설정optional()
: 선택적 값 설정pattern(regex)
: 정규식을 기반으로 데이터 검증custom(callback)
: 사용자 정의 검증 로직 추가valid(...values)
: 특정 값만 허용allow(value)
: 특정 값 허용(예:null
)
7. 결론 🏁
Joi는 데이터 검증을 간결하고 효과적으로 처리할 수 있는 도구로, 특히 Node.js 환경에서 필수적인 라이브러리이다. 데이터를 유효성 검증하는 과정에서 코드의 가독성과 유지보수를 크게 향상시킬 수 있다.
보통 schema 소스를 별도로 구현하여 해당 소스에서 벨리데이션을 정의하면 서비스, 라우터등의 소스에서 별도로 데이터 검증을 위한 소스가 필요 없게 되어 소스를 깔끔하게 관리할 수 있다.
참고
: https://joi.dev/
댓글