Vue.js에서 Form validator를 찾던 중 VeeValidate를 알게 되었습니다. PHP Framework Laravel’s validation syntax에서 영감을 얻었다고 합니다. 마침 Laravel로 작업중이여서 잘 되었다고 생각하고 사용하게 되었습니다.
Install
$ npm install --save-dev vee-validate
Example
먼저 간단한 사용법을 보시겠습니다.
- Submit을 바로 눌러보면,
The email field is required.
와 동시에 빨갛게 표시되는 것을 볼 수 있습니다. - Email에 아무 값이나 입력을 하게 되면,
The email field must be a valid email.
라는 에러 메시지를 볼 수 있습니다. - 이메일 형식에 맞게 입력 후 Submit을 누르면, submit alert을 볼 수 있습니다.
Validator
Vue.use(VeeValidate)
을 사용할 경우 vee-validate/mixin.js과 v-validate
디렉티브를 등록합니다.
mixin에서는 Validator를 생성해서 $validator로 대입하고 있습니다. 그리고 에러를 확인할 수 있는 errors를 computed로 등록하고 있습니다.
검증 순서는 다음과 같습니다.
- input tag에 v-validate로 rules를 설정합니다.
- this.$validator.validateAll()로 검증합니다.
- this.errors.any()로 에러가 있는지 확인합니다.
특정 필드의 에러를 확인하고 싶은 경우 this.errors.has(‘field’)로 에러가 있는지 확인 하고 this.errors.first(‘field’)로 첫번째 메시지를 가져올 수 있습니다.
Attributes
input tag에는 반듯이 name 또는 data-vv-name 속성이 있어야 하는데, 이 값은 필드명으로 사용됩니다.
필드명은 정의해서 출력할 수 있습니다.
- data-vv-as 속성 사용
- dictionary object 정의
먼저 data-vv-as를 설정할 경우
<input type="email" name="email" placeholder="Email" v-validate="'required|email'" v-model="email" data-vv-as="Email">
에러 메세지는 다음과 같이 바뀐 필드명으로 나오게 됩니다. The Email field is required.
하지만, 모든 필드에 data-vv-as 속성을 넣는 것은 여간 번거로운 일이 아닐 것입니다.
dictionary object를 이용해서 모든 email 필드명에 대해서 바뀐 이름으로 나오게 할 수 있습니다.
const config = {
dictionary: {
en: {
attributes: {
email: 'E-mail'
}
}
}
}
Vue.use(VeeValidate, config)
그러면 에러 메시지는 이렇게 나오게 됩니다. The E-mail field is required.
Localization
위에서 정의한 dictionary를 보면 en이 제일 먼저 정의 되어 있습니다. 즉, 다국어를 지원한다는 것인데, 다행히 한국어는 번역이 되어있는 상태였습니다. (@ChangJoo-Park님이 해주셧습니다.)
<script src="https://unpkg.com/vee-validate"></script>
<script src="https://unpkg.com/vee-validate/dist/locale/ko.js"></script>
<script>
Vue.use(VeeValidate, {
locale: 'ko'
})
</script>
vee-validate를 먼저 호출하고, 다음에 locale javascript를 호출합니다.
Submit을 클릭하면, email가 필요합니다.
라는 한글로된 에러메시지를 볼 수 있습니다.
Webpack을 사용할 경우 다음과 같이 설정할 수 있습니다.
import VeeValidate from 'vee-validate'
import ko from 'vee-validate/dist/locale/ko.js'
const config = {
locale: 'ko',
dictionary: {
ko
}
}
Vue.use(VeeValidate, config)
그리고 email을 이메일로 변경 하려면 다음과 같이 추가로 설정할 할 수 있습니다.
import { Validator } from 'vee-validate'
const dictionary = {
ko: {
attributes: {
email: '이메일'
}
}
}
Validator.updateDictionary(dictionary)
Messages
에러 메시지가 이메일가 필요합니다.
, 이메일는 올바른 이메일 형식이어야합니다.
처럼 조금 억양에 안맞게 출력되는 부분이 있습니다.
그래서 다음과 같이 변경할 수 있다.
import ko from 'vee-validate/dist/locale/ko.js'
ko.messages.email = (field) => `${field}은/는 올바른 이메일 형식이어야 합니다.`
ko.messages.required = (field) => `${field}이/가 필요합니다.`
또는 속성명에 조사를 붙이는 방법도 있을 것입니다.
import ko from 'vee-validate/dist/locale/ko.js'
ko.attributes.email = '이메일은'
ko.messages.required = (field) => `${field} 필수입력란 입니다.`
한글 번역본은 ko.js에서 확인할 수 있다.
여기까지 간단한 사용법을 알아 보았습니다.
field-input component
위에서 만든 예제로 폼을 구성하다보니 중복되는 코드가 너무 많아서 field-input 컴포넌트를 만들어 보았습니다.
Demo javascript란이 조금 길어서 링크로 연결하였습니다.
field-input 컴포넌트를 이용해서 간단한 가입 폼을 만들어 보았는데요. 다음과 같이 룰이 적용 되어 있습니다.
- 이메일:
required|email
- 이름:
required
- 패스워드:
required|min:6
- 패스워드 확인:
required|confirmed:password
- 홈페이지:
url
props
- value: v-model로 받기 위해서 사용
- field: scope.fieldName 형식으로 입력
- rules: 검사할 룰을 입력
- type: input tag의 타입을 입력, 기본값은
text
입니다.
rules를 [String, Object]타입으로 받는 이유는 정규화표현식 파라메터 때문입니다.
validator.js를 확인해보면 파라메터를 콤마(,)로 분리하는 것을 볼 수 있습니다.
예를 들어 rules="regex:^\d{2,3}-\d{3,4}-\d{4}$"
이렇게 넘긴다면, regex의 파라메터가 이렇게 배열로 ["^\d{2", "3}-\d{3", "4}-\d{4}$"]
넘어가서 에러가 발생합니다.
그래서 다음과 같이 Object 타입으로 파라메터를 넘길 수 있습니다.
v-bind:rules="{required: true, regex: [/^\d{2,3}-\d{3,4}-\d{4}$/]}"
Javascript로 처리하기 위해서 v-bind를 사용합니다.
computed
- prettyName: 프린트 되는 이름
- fieldName: 검사 받는 필드 이름
- scope: validator scope
prettyName은 vue-i18n과 함께 사용하는 것이 좋은 것 같습니다.
이렇게 this.$t(this.field) 사용할 수 있습니다.
methods
- updateValue(val): 값을 검사하고 model을 업데이트 합니다.
getter와 context는 validator에서 검사하기위해서 값을 가져갈 때 호출됩니다.
그리고 mounted와 destroyed에서 검사할 field를 붙이고 제거합니다.
Plugin
여기서 저는 Vue.use(VeeValidator)를 사용하지 않고 직접 플러그인을 만들어서 사용하였습니다.
이유는 VeeValidator로 추가하게 되면 모든 컴포넌트에 $validator를 매번 생성하기 때문입니다. vee-validate/mixin.js
다음과 같이 validator를 하나 생성 후 모든 컴포넌트에서 하나의 인스턴스만 바라보도록 하였습니다.
import Vue from 'vue'
import {Validator} from 'vee-validate'
const VeeValidatePlugin = {
install(Vue) {
const validator = new Validator()
Object.defineProperties(Vue.prototype, {
$validator: {
get() {
return validator
}
}
})
// errors를 computed로 했을 경우
// 캐싱되어서 그런지 에러 검사 후 표시가 되지 않음
Vue.mixin({
data() {
return {
errors: validator.errorBag
}
}
})
}
}
Vue.use(VeeValidatePlugin)
그리고 mixin으로 errors도 모두 같은 인스턴스를 바라보도록 하였습니다.
data를 사용한 이유는 computed가 캐시를 해서 그런지 validateAll()
에 반응하지 않아서 였습니다.
Conclusion
field-input 컴포넌트를 사용하니까 form이 매우 깔끔해진 것 같습니다. 물론 모든 input 타입에 대해서 확인해보진 않았지만, field-input 컴포넌트와 비슷하게 만들면 되지 않을까 생각됩니다.
하위 컴포넌트를 만드는 방법으로 Event Bus 를 사용한 예제도 있습니다.
위 링크를 통해 후원을 받고 있습니다. 모든 후원금은 Vue.js 한국 커뮤니티 운영에 사용됩니다.