undefined

κ΅¬ν˜„ 동기

  1. ν•„μˆ˜κ°’, κΈ€μžμˆ˜ μ œν•œ, 이메일 ν˜•μ‹, μ „ν™”λ²ˆν˜Έ ν˜•μ‹ λ“± κ°„λ‹¨ν•œ κ²€μ‚¬λ§Œ ν•„μš”ν•˜λ‹€.
  2. joiλ‚˜ yup 같은 μ™ΈλΆ€ 라이브러리 없이 κ΅¬ν˜„ν•˜κ³  μ‹Άλ‹€. (λ³΅μž‘ν•œ κΈ°λŠ₯은 ν•„μš” μ—†λ‹€.)
  3. λ©”μ„œλ“œ 체이닝을 직접 κ΅¬ν˜„ν•΄λ³΄κ³  μ‹Άλ‹€

μ΄λŸ¬ν•œ λ™κΈ°λ‘œ μž…λ ₯의 μœ νš¨μ„±μ„ κ²€μ‚¬ν•˜λŠ” μœ ν‹Έλ¦¬ν‹°λ₯Ό 직접 κ΅¬ν˜„ν•΄ 보기둜 ν–ˆλ‹€.

μš”κ΅¬μ‚¬ν•­

  • λ©”μ„œλ“œ μ²΄μ΄λ‹μœΌλ‘œ 체이닝 된 μˆœμ„œλŒ€λ‘œ μœ νš¨μ„±μ„ κ²€μ¦ν•œλ‹€.
  • ν™•μž₯성을 μœ„ν•΄ 직접 μ •μ˜ν•œ λ©”μ„œλ“œλ‘œ μœ νš¨μ„±μ„ 검증할 수 μžˆμ–΄μ•Ό ν•œλ‹€.
  • μž…λ ₯ ν•„λ“œ μ»΄ν¬λ„ŒνŠΈμ—μ„œ μž…λ ₯ μ‹œλ§ˆλ‹€ μœ νš¨μ„±μ„ μžλ™μœΌλ‘œ κ²€μ¦ν•œλ‹€.

κ΅¬ν˜„ν•˜κΈ°

Validator 클래슀

μœ νš¨μ„±μ„ κ²€μ¦ν•˜λŠ” Validator 클래슀λ₯Ό μƒμ„±ν•œλ‹€.

클래슀의 λ©”μ„œλ“œλ‘œ μœ νš¨μ„±μ„ κ²€μ¦ν•˜λŠ” λ‘œμ§μ„ κ΅¬ν˜„ν•œλ‹€.

각 λ©”μ„œλ“œλŠ” 항상 thisλ₯Ό λ¦¬ν„΄ν•΄μ„œ 체이닝이 κ°€λŠ₯ν•˜λ„λ‘ ν•œλ‹€.

type ValidatorFunction = (input: string) => Promise<boolean> | boolean;
const emailPattern = /^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})*$/;

export default class Validator {
  isValid: boolean = true;
  private chain: Array<ValidatorFunction> = [];

  required = () => {
    this.chain.push((input: string) => input.length > 0);
    return this;
  };

  max = (max: number) => {
    this.chain.push((input: string) => input.length < max);
    return this;
  };

  min = (min: number) => {
    this.chain.push((input: string) => input.length >= min);
    return this;
  };

  test = (pattern: RegExp) => {
    this.chain.push((input: string) => new RegExp(pattern).test(input));
    return this;
  };

  email = () => {
    return this.test(emailPattern);
  };

  exec = async (fn: ValidatorFunction) => {
    this.chain.push(fn);
    return this;
  };

  validate = async (input: string) => {
    for await (const validator of this.chain) {
      const isValid = await validator(input);
      this.isValid = isValid;
      if (!isValid) return false;
    }
    this.isValid = true;
    return true;
  };
}

chain

validate λ©”μ„œλ“œμ—μ„œ chain μ•ˆμ˜ ValidatorFunction을 처음 체이닝 된 것뢀터 μ°¨λ‘€λŒ€λ‘œ μ‹€ν–‰ν•œλ‹€. (FIFO 큐처럼 λ™μž‘ν•œλ‹€.)

chain은 μ™ΈλΆ€μ—μ„œ μ ‘κ·Όν•˜κ±°λ‚˜ λ³€κ²½ν•  수 없도둝 private으둜 μ§€μ •ν•œλ‹€.

validate()

λͺ¨λ“  μœ νš¨μ„± 검사λ₯Ό ν†΅κ³Όν•˜λ©΄ trueλ₯Ό λ°˜ν™˜ν•˜κ³ , ν•˜λ‚˜λΌλ„ ν†΅κ³Όν•˜μ§€ λͺ»ν•˜λ©΄ falseλ₯Ό λ°˜ν™˜ν•œλ‹€.

이번 ν”„λ‘œμ νŠΈμ—μ„œλŠ” λ”°λ‘œ μ—λŸ¬ λ©”μ‹œμ§€λ₯Ό ν‘œμ‹œν•˜μ§€ μ•Šκ³  μžˆλ‹€. μΆ”ν›„ μ—λŸ¬λ©”μ‹œμ§€κ°€ μΆ”κ°€λœλ‹€λ©΄ false λŒ€μ‹  throw Erorr('μ—λŸ¬ λ©”μ‹œμ§€')둜 κ΅¬ν˜„ν•  수 μžˆκ² λ‹€.

exec(), test()

ν™•μž₯성을 μœ„ν•΄ μΆ”κ°€ν•œ λ©”μ„œλ“œμ΄λ‹€. Validator λ‚΄ 미리 μ •μ˜λœ μœ νš¨μ„± 검사 λ©”μ†Œλ“œ 외에도 λ‘œμ§μ„ 직접 κ΅¬ν˜„ν•˜μ—¬ λ„˜κ²¨μ€„ 수 μžˆλ‹€.

exec(): 직접 κ΅¬ν˜„ν•œ ν•¨μˆ˜λ‘œ 검사할 수 μžˆλ‹€.

test(): 직접 μ •μ˜ν•œ μ •κ·œμ‹μœΌλ‘œ 검사할 수 μžˆλ‹€.

 

Validator 클래슀 μ‚¬μš©ν•˜κΈ°

λ‹€μŒμ²˜λŸΌ input에 λŒ€ν•œ μœ νš¨μ„±μ„ μ •μ˜ν•  수 μžˆλ‹€.

const emailValidator = new Validator().required().email()
const nicknameValidator = new Validator().required().min(2).max(10).test(/^[a-zA-Z0-9]*$/)

 

input μ»΄ν¬λ„ŒνŠΈ λ‚΄λΆ€μ—μ„œλŠ” validatorκ°€ prop으둜 μ£Όμ–΄μ§ˆ λ•Œ,

ChangeEvent ν•Έλ“€λŸ¬μ—μ„œ validateλ₯Ό μ‹€ν–‰ν•˜κ³ , κ·Έ 결과에 따라 μœ νš¨μ„±μ„ λ‚˜νƒ€λ‚΄λŠ” uiλ₯Ό λ³€κ²½ν•œλ‹€.

<TextField
  label="λ‹‰λ„€μž„"
  validator={nicknameValidator}
  ...
/>

마무리

λ‹€λ₯Έ ν”„λ‘œμ νŠΈμ—μ„œλŠ” μœ νš¨μ„± 검증에 react-hook-formκ³Ό yup 라이브러리λ₯Ό μ‚¬μš©ν–ˆμ—ˆλ‹€.

yup을 처음 μ‚¬μš©ν–ˆμ„ λ•Œ λ©”μ„œλ“œ μ²΄μ΄λ‹μœΌλ‘œ input을 μ •μ˜ν•˜λŠ” 것에 감λͺ…을 λ°›μ•˜λ‹€.

λ³΅μž‘ν•œ form을 μ‚¬μš©ν•œλ‹€λ©΄ yupμ΄λ‚˜ 그와 λΉ„μŠ·ν•œ joi 같은 라이브러리λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 더 쒋을 것이닀.

ν•˜μ§€λ§Œ input이 λ§Žμ§€ μ•Šκ³ , λ³΅μž‘ν•œ 검증이 ν•„μš”ν•˜μ§€ μ•Šλ‹€λ©΄ μ΄λ ‡κ²Œ κ°„λ‹¨ν•œ Validator 클래슀λ₯Ό λ§Œλ“€μ–΄ μ‚¬μš©ν•΄ 보자