README
Kalidator
(for View) Validation. you can validate Object data, html Element, javascript FormData
Install
npm i @owneul/kalidator
Updates
검사실행을 프로미스 기반 로직으로 변경하였습니다.
배열 형태의 데이터를 수용하도록 변경하였습니다.
날짜관련 핸들링에 moment.js 의존성을 추가하였습니다.
Usage
window.Kalidator = require('@owneul/kalidator');
// Kalidator 객체를 생성할 때 데이터화 대상, 검사규칙, 검사미통과시 메세지를 지정한다.
// 이는 생성자로 지정할 수도 있고, 세터를 통해 각각 지정할 수도 있다.
// 각각의 세터는 메소드 체이닝을 위해 자기자신을 반환한다.
// new Kalidator(data?, rules?, messages?);
// data 파라미터는 javascript Object, HTML 엘리먼트, javascript FormData 객체가 지정 가능하다.
// * HTML 엘리먼트의 경우 엘리먼트 하위의 name attribute를 가진 엘리먼트를 이름 그대로 추출하므로 유의(구조화 하지 않음)
// * FormData의 경우 formData.get() / formData.getAll() 메소드를 통해 단순추출을 진행하므로 유의
//
// 지정이 완료된 후 run 메소드 실행을 통해 유효성 검사를 시작하며, run 함수는 프로미스를 반환한다.
const validator = new Kalidator();
const data = {
goodsList: [
{
id: 1004,
name: '민무늬 원피스',
price: 89000,
checks: ['black', 'red', 'yellow'],
},
{
id: 2007,
name: '닌텐도 스위치',
price: '공짜',
checks: ['free'],
},
{
name: '비닐하우스 미니어쳐 키트',
price: 199000,
},
],
buyer: {
memberId: 'owneul',
tel: '010-1234-5678',
address: '경상북도 울릉군 울릉읍 독도이사부길 63 (독도등대)',
deliveryMessage: '안전배송바랍니다12345678901234567890...........*(very long text)*',
},
};
const rules = {
'goodsList.*.id(상품코드)': ['required', 'minValue:1'],
'goodsList.*.name(상품명)': ['required'],
'goodsList.*.price(판매가)': ['required', 'number'],
'goodsList.*.checks.*(상품 선택사항)': ['in:black,red,yellow'],
'buyer.memberId(회원 아이디)': ['required', 'existMember'],
'buyer.tel(구매자 연락처)': ['required', 'phoneNumber'],
'buyer.address(구매자 주소)': ['required'],
'buyer.deliveryMessage(배송메세지)': ['required', 'maxLength:30'],
};
const messages = {
'goodsList.*.id.required': ':param(은/는) 필수입니다. (:*0번째 상품)',
'goodsList.*.id.minValue': ':param(은/는) 최소 :$1원 이상이어야합니다. (:*0번째 상품)',
'goodsList.*.name.required': ':param(은/는) 필수입니다. (:*0번째 상품)',
'goodsList.*.price.required': ':param(은/는) 필수입니다. (:*0번째 상품)',
'goodsList.*.price.number': ':param(은/는) 유효한 정수여야합니다. (:*0번째 상품)',
'goodsList.*.checks.*.in': ':param(은/는) :$concat 중의 하나여야합니다. (:*0번째 상품의 :*1번째 선택사항)',
'buyer.memberId.required': ':param(은/는) 필수입니다.',
'buyer.memberId.existMember': ':param [:value](은/는) 존재하지 않는 :param입니다.',
'buyer.memberId.phoneNumber': ':param(은/는) 올바른 휴대전화번호가 아닙니다.',
'buyer.deliveryMessage.maxLength': ':param(은/는) :$0자 이하여야합니다.',
};
validator
.setData(data)
.setRules(rules)
.setMessages(messages)
.registTester('phoneNumber', function (paramName, extraValues, data) {
const tel = Kalidator.getTargetValue(data, paramName)
return tel !== null && tel.match(/[0-9]{3}-[0-9]{4}-[0-9]{4}/) !== null;
})
.registTester('existMember', function (paramName, extraValues, data) {
const memberId = Kalidator.getTargetValue(data, paramName);
return fetch(`https://jsonplaceholder.typicode.com/users/${memberId}`)
.then(function (response) {
return response.json();
})
.then(function (json) {
return Object.keys(json).length > 0;
})
.catch(function (err) {
return false;
})
})
.run()
.then(function () {
alert('all validate passed!');
})
.catch(function (fail) {
console.error('모든 실패한 테스트에 대한 메세지 : ', fail.errors);
console.error('첫 번째 실패 테스트 메세지 : ', fail.firstErrorMessage);
})
Rules
// 데이터에 키값당 실행되어야하는 테스터의 이름을 배열로 지정한다.
// 키값(라벨)로 룰을 지정할 경우, 메세지에서 :param 문자열을 통해 라벨값을 사용할 수 있다.
// 애스터리스크(*) 문자는 배열로 해석된다.
var validator = new Kalidator();
validator.setRules({
'goodsList.*.id(상품코드)': ['required', 'minValue:1'],
'goodsList.*.name(상품명)': ['required'],
'goodsList.*.price(판매가)': ['required', 'number'],
'goodsList.*.checks.*(상품 선택사항)': ['in:black,red,yellow'],
'buyer.memberId(회원 아이디)': ['required', 'existMember'],
'buyer.tel(구매자 연락처)': ['required', 'phoneNumber'],
'buyer.address(구매자 주소)': ['required'],
'buyer.deliveryMessage(배송메세지)': ['required', 'maxLength:30'],
});
name | description | memo |
---|---|---|
required | 데이터 내에 __key 값이 반드시 존재해야 한다 | |
requiredIf:targetKey[,whitelist1,whitelist2...] | 데이터 내에 targetKey의 값이 존재하는 경우(화이트리스트를 입력하면 화이트리스트 내에 해당값이 존재하는 경우)__key 값 또한 반드시 존재해야 한다 | |
requiredNotIf:targetKey[,blacklist1,blacklist2...] | 데이터 내에 targetKey의 값이 존재하지 않는 경우, 또는 존재하지만 블랙리스트 내에 해당값이 존재하는 경우 __key 값은 반드시 존재해야 한다 | |
minLength:n | 데이터가 최소 n의 길이어야 한다 | |
maxLength:n | 데이터가 최대 n의 길이어야 한다 | |
betweenLength:n1,n2 | 데이터가 최소 n1, 최대 n2의 길이어야 한다 | |
minValue:n | 데이터가 최소 n의 값이어야 한다 | |
maxValue:n | 데이터가 최대 n의 값이어야 한다 | |
betweenValue:n1,n2 | 데이터가 최소 n1, 최대 n2의 값이어야 한다 | |
in:x,y,z... | 데이터가 주어진 값들 중 하나여야 한다 | |
notIn:x,y,z... | 데이터가 주어진 값들 중에 존재하지 않아야 한다 | |
number | 주어진 값이 숫자여야 한다 | |
주어진 값이 이메일 주소여야 한다 | @로 시작하거나 끝나지 않으며 @를 가지고 있는 여부만 체크함 | |
date | 주어진 값이 날짜로 추출 가능한 값이어야 한다 | moment 객체 |
file[:mimetype,extension1,extension2...] | 주어진 값이 파일 객체여야 한다(추가조건 입력으로 mimetype 및 확장자 제어) | javascript File 객체 사용 |
earlierThan | 주어진 값이 날짜로 추출 가능한 값이며 주어진 비교 데이터보다 빠른 날짜여야 한다 | timestamp 비교 |
earlierOrEqualThan | 주어진 값이 날짜로 추출 가능한 값이며 주어진 비교 데이터와 같거나 빠른 날짜여야 한다 | timestamp 비교 |
laterThan | 주어진 값이 날짜로 추출 가능한 값이며 주어진 비교 데이터보다 늦은 날짜여야 한다 | timestamp 비교 |
laterOrEqualThan | 주어진 값이 날짜로 추출 가능한 값이며 주어진 비교 데이터와 같거나 늦은 날짜여야 한다 | timestamp 비교 |
Regist Custom Tester
// 커스텀 테스터를 등록하는 예제.
// 등록한 테스터는 룰과 메세지에서 해당 이름으로 사용할 수 있으며, 성공/실패여부를 bool 값 또는 bool을 전달하는 프로미스로 반환해야한다.
// 콜백 함수의 arguments는 다음과 같다.
// paramName : 해당 테스터를 호출한 데이터 키값
// extraValues : 테스터에게 주어진 검사용 엑스트라 벨류 배열 (검사 룰이 isMailHostsIn:gmail.com,naver.com 으로 지정되었다면 ['gmail.com', 'naver.com'])
// data : 유효성 검사가 실행되는 스코프의 데이터 객체
const validator = new Kalidator();
validator
.registTester('phoneNumber', function (paramName, extraValues, data) {
// 닷(.)으로 concat 된 파라미터 이름을 통해 객체, 배열 형태의 대상값을 조회하기 위해 Kalidator.getTargetValue() 메소드를 활용
// 단순형태의 데이터라면 data[paramName] 식으로 접근해도 무방함
const tel = Kalidator.getTargetValue(data, paramName)
// bool 값을 반환하는 커스텀 테스터
return tel !== null && tel.match(/[0-9]{3}-[0-9]{4}-[0-9]{4}/) !== null;
})
.registTester('existMember', function (paramName, extraValues, data) {
const memberId = Kalidator.getTargetValue(data, paramName);
// 프로미스를 반환하는 커스텀 테스터(리소스 존재검사, 고유키 중복검사 등에 활용)
return fetch(`https://jsonplaceholder.typicode.com/users/${memberId}`)
.then(function (response) {
return response.json();
})
.then(function (json) {
return Object.keys(json).length > 0;
})
.catch(function (err) {
return false;
})
});
Set Custom Messages
// 메세지는 '키값.테스터이름' : '메세지' 형태의 배열로 등록한다.
// 메세지 출력의 편의를 위해 몇 가지 치환용 변수가 제공된다.
// 1. :param 문자는 룰 지정에 사용된 라벨 또는 키값으로 치환된다.
// 2. :$n 문자는 룰 지정에 사용된 엑스트라 벨류로 치환된다. 해당 엑스트라 벨류가 다른 데이터의 키값인 경우 해당 참조데이터의 라벨 또는 키값으로 우선 치환된다.(n은 0부터 시작)
// 3. (한국어 전용) '(은/는)', '(이/가)' 처럼 조사 분기는 다른 치환이 실행된 후 앞 문자열의 받침유무에 따라 적절하게 치환된다.
// 4. :$concat 문자는 룰 지정에 사용된 엑스트라 벨류가 concat된 문자열로 치환된다.
// 5. :value 문자는 대상 키 데이터 값 자체로 치환된다.
// 6. :*n 문자는 룰 지정에 사용된 애스터리스크(*) 반복회차로 치환된다. (n은 0부터 시작)
const validator = new Kalidator();
validator.setMessages({
'goodsList.*.id.required': ':param(은/는) 필수입니다. (:*0번째 상품)',
'goodsList.*.id.minValue': ':param(은/는) 최소 :$1원 이상이어야합니다. (:*0번째 상품)',
'goodsList.*.name.required': ':param(은/는) 필수입니다. (:*0번째 상품)',
'goodsList.*.price.required': ':param(은/는) 필수입니다. (:*0번째 상품)',
'goodsList.*.price.number': ':param(은/는) 유효한 정수여야합니다. (:*0번째 상품)',
'goodsList.*.checks.*.in': ':param(은/는) :$concat 중의 하나여야합니다. (:*0번째 상품의 :*1번째 선택사항)',
'buyer.memberId.required': ':param(은/는) 필수입니다.',
'buyer.memberId.existMember': ':param [:value](은/는) 존재하지 않는 :param입니다.',
'buyer.memberId.phoneNumber': ':param(은/는) 올바른 휴대전화번호가 아닙니다.',
'buyer.deliveryMessage.maxLength': ':param(은/는) :$0자 이하여야합니다.',
});
Regist Global Custom Tester
// 전역에서 사용할 커스텀 테스터를 등록할 수 있다.
Kalidator.registGlobalTester('divideBy', function (paramName, extraValues, data) {
const targetValue = Kalidator.getTargetValue(data, paramName);
if (targetValue === null) {
return false;
}
const divide = extraValues[0];
if (isNaN(divide) || divide.match(/[^0-9]/) !== null) {
return false;
}
return Number.parseFloat(targetValue) % Number.parseInt(divide) === 0;
});
Set Global Custom Message
// 전역에서 공통적으로 사용할 메세지를 설정할 수 있다
// 룰(테스터) 이름과 메세지를 설정
Kalidator.setGlobalMessage('divideBy', ':param(은/는) :$0으로 딱 나누어떨어지지 않아요... (사용된 식 : :value/:$0)');