Joi (유효성 검증 라이브러리)

2025. 8. 9. 23:23Coding Study/React

 

설치방법

npm install joi

 

 

1. 유효성 검증 예시 (이메일 검증)

  1) OK 일 경우 Value 값만 반환

  const schema = Joi.string().email();

  const result = schema.validate("test@example.com");

  console.log(result); // { "value": "test@example.com" }

 

2) error일 경우 에러 속성을 포함 해서 반환

  const schema = Joi.string().email();

  const result = schema.validate("test@example");

  console.log(result);

 

에러일 경우 아래와 같이 에러 속성을 포함해서 반환한다.

{
    "value": "test@example",
    "error": {
        "_original": "test@example",
        "details": [
            {
                "message": "\"value\" must be a valid email",
                "path": [],
                "type": "string.email",
                "context": {
                    "value": "test@example",
                    "invalids": [
                        "test@example"
                    ],
                    "label": "value"
                }
            }
        ]
    }
}

 

 

2. 회원 가입폼 전체 유효성 검증 방법

 1) 스키마 정의

//schema.ts

import Joi from "joi";

export interface SignupForm {
  name: string;
  email: string;
  nickname: string;
  phone: string;
  password: string;
  gender: "남" | "여";
}

const singupSchema = Joi.object<SignupForm>({
  name: Joi.string().required().messages({
    "string.empty": "이름을 입력해주세요.",
  }),

  email: Joi.string()
    .email({ tlds: { allow: false } })
    .required()
    .messages({
      "string.empty": "이메일을 입력해주세요.",
      "string.email": "올바른 이메일 형식이 아닙니다.",
    }),

  nickname: Joi.string()
    .pattern(/^[a-zA-Z0-9가-힣]+$/)
    .required()
    .messages({
      "string.empty": "닉네임을 입력해주세요.",
      "string.pattern.base": "닉네임은 특수문자를 포함할 수 없습니다.",
    }),

  phone: Joi.string().pattern(/^\d+$/).required().messages({
    "string.empty": "휴대전화를 입력해주세요.",
    "string.pattern.base": "휴대전화는 숫자만 입력 가능합니다.",
  }),

  password: Joi.string()
    .pattern(
      /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*()\-_=+{}[\]|;:'",.<>/?]).{8,20}$/
    )
    .required()
    .messages({
      "string.empty": "비밀번호를 입력해주세요.",
      "string.pattern.base":
        "비밀번호는 영문, 숫자, 특수문자를 포함한 8~20자여야 하며 공백이 없어야 합니다.",
    }),

  gender: Joi.string().valid("남", "여").required().messages({
    "any.only": "성별은 남 또는 여만 선택 가능합니다.",
    "string.empty": "성별을 선택해주세요.",
  }),
});

export default singupSchema;

 

 

 2) 검사할 데이터 준비

const formData = {
    name: "홍길동",
    email: "gildong@example.com",
    nickname: "gildong123",
    phone: "01012345678",
    password: "abc123!@#",
    gender: "남",
  };

 

 

3) 검사 데이터를 넣어서 유효성 검사 하여 error 및 value 를 반환 받는다

const { error, value } = singupSchema.validate(formData, {
    abortEarly: false, // 모든 에러 반환
    allowUnknown: false, // 정의되지 않은 키는 허용하지 않음
    stripUnknown: true, // 스키마에 정의되지 않은 속성 제거
  });

 

 

4)  에러가 있을 경우 메세지 출력 없으면 통과 시킨다

if (error) {
    // 유효성 오류 발생 → 메시지 출력
    console.log(
      "유효성 오류:",
      error.details.map((e) => e.message)
    );
  } else {
    // 유효성 통과 
    const validData = value;
    console.log("검증 통과 데이터:", validData);
  }

 

 

성공 시 value 값 형태 

{
    "name": "홍길동",
    "email": "gildong@example.com",
    "nickname": "gildong123",
    "phone": "01012345678",
    "password": "abc123!@#",
    "gender": "남"
}

 

 

실패시 

 //console.log( error.details.map((e) => e.message) );



[
    "이름을 입력해주세요.",
    "올바른 이메일 형식이 아닙니다.",
    "비밀번호는 영문, 숫자, 특수문자를 포함한 8~20자여야 하며 공백이 없어야 합니다."
]

 

 

 

5) 에러시 스키마 반환값 확인

 

 result 값을 바로 콜솔로 확인 해보면 아래의 긴 객체가 출력 된다.

const result = singupSchema.validate(formData, {
    abortEarly: false, // 모든 에러 반환
    allowUnknown: false, // 정의되지 않은 키는 허용하지 않음
    stripUnknown: true, // 스키마에 정의되지 않은 속성 제거
  });

  console.log(result);

 

 

 

 

//  console.log(result);

{
    "value": {
        "name": "",
        "email": "gildong@example",
        "nickname": "gildong123",
        "phone": "010123456",
        "password": "abc123",
        "gender": "남"
    },
    "error": {
        "_original": {
            "name": "",
            "email": "gildong@example",
            "nickname": "gildong123",
            "phone": "010123456",
            "password": "abc123",
            "gender": "남"
        },
        "details": [
            {
                "message": "이름을 입력해주세요.",
                "path": [
                    "name"
                ],
                "type": "string.empty",
                "context": {
                    "label": "name",
                    "value": "",
                    "key": "name"
                }
            },
            {
                "message": "올바른 이메일 형식이 아닙니다.",
                "path": [
                    "email"
                ],
                "type": "string.email",
                "context": {
                    "value": "gildong@example",
                    "invalids": [
                        "gildong@example"
                    ],
                    "label": "email",
                    "key": "email"
                }
            },
            {
                "message": "비밀번호는 영문, 숫자, 특수문자를 포함한 8~20자여야 하며 공백이 없어야 합니다.",
                "path": [
                    "password"
                ],
                "type": "string.pattern.base",
                "context": {
                    "regex": {},
                    "value": "abc123",
                    "label": "password",
                    "key": "password"
                }
            }
        ]
    }
}