본문 바로가기
프로그래밍/Javascript

joi 라이브러리로 입력값 검증, messages로 커스텀 메시지 작성하기

by 리나그(ReenAG) 2024. 1. 5.
728x90
반응형

 이번에 같이 개발하게 된 친구와 함께 협의한 것 중 하나는 우선 서로 같이 쓸 수 있는 개발일지를 만들어서 배포해 보자는 것이었다. 그래서 실제로 코드를 지금 github organization에서 공유하게 되었고, 적어 둔 TODO 중에서 제일 만만해 보이는 걸로 우선 스타트를 끊기로 했다.

 

 그 만만하다는 작업이 "비밀번호 입력값 검증 강화"이다. 이미 joi 라이브러리를 이용해서 기본적인 입력값 검증을 하고 있었기 때문이다. 

const schema = Joi.object().keys({
    username: Joi.string()
    .alphanum()
    .min(3)
    .max(20)
    .required()
    password: Joi.string()
    .min(6)
    .max(20)
    .required()
});

 

이렇게 schema만 단순히 더하면 될 줄 알았다. 실제로 틀린 것은 아닌게 검증만 바라본다면 단순히 이렇게 더 많은 조건을 더해주기만 하면 된다.

const schema = Joi.object().keys({
    username: Joi.string()
    .alphanum()
    .min(3)
    .max(20)
    .required()
    password: Joi.string()
    .min(6)
    .max(20)
    .regex(/^[!@#$%^&*a-zA-Z0-9]{3,30}$/)
    .regex(/[a-z]/)
    .regex(/[A-Z]/)
	... 그외 다수 regex ...
    .required()
});

 

사실 어려운 파트는 각각의 에러마다 커스텀 오류 메시지를 작성하는 일이었다. 어차피 에러는 웹으로 전달되므로, 에러에  기본적으로 포함된 message 필드를 조작해서 프런트엔드에서 띄울 수 있게 뜸한 작업이었다. 근데 거기서 문제가 생길 줄은 몰랐다.

const schema = Joi.object().keys({
    username: Joi.string()
    .alphanum()
    .min(3)
    .max(20)
    .required()
    .messages({
        'string.alphanum': '아이디는 영문자 및 숫자로만 이루어질 수 있습니다.',
        'string.min': '아이디는 3자 이상이어야합니다.',
        'string.max': '아이디는 20자를 넘을 수 없습니다.',
        'any.required': '아이디를 입력해주세요.'
    }),
    password: Joi.string()
    .min(6)
    .max(20)
    .regex(/^[!@#$%^&*a-zA-Z0-9]{3,30}$/)
    .regex(/[a-z]/, 'lowercase')
    .regex(/[A-Z]/, 'uppercase')
        ...그외 다수 regex...
    .required()
    .messages({
        'string.min': '비밀번호는 6자 이상이어야합니다.',
        'string.max': '비밀번호는 20자를 넘을 수 없습니다.',
        'string.pattern.base': '비밀번호는 영문자 대/소, 숫자, 일부 특수문자(!@#$%^&*)만 사용하실 수 있습니다.',
        'string.pattern.name': '여기 구분이 안됨...',
        'any.required': '비밀번호를 입력해주세요.'
    }),
});

 

 처음에는 이것 말고 .message 메서드(잘보면 s가 없다!)를 이용해서 만들었었는데 정작 그렇게 만드니까

Cannot apply rules to empty ruleset or the last rule added does not support rule properties

 

에러메시지를 내뱉는 바람에 위의 코드로 바꾼 것이다. 바꾼 만큼 message를 잘 돌려 줄 것 같지만, 여러 개의 regex 혹은 pattern을 이용할 경우에는 그것 들을 messages에서 일일이 구분할 방법이 없다! 그래서 regex에 name이 있는 경우, 없는 경우 딱 2가지만 구분이 가능하고, 나머지들의 메시지가 통일되어 버리는 문제가 있었다.

 

심지어 이건 chatgpt - 4한테 물어봐도 그냥은 알 수 없는 문제였다.

이 녀석한테 물어봐도 그냥 뚱딴지 같은 코드를 뱉을 뿐이고 실제로 이게 api에서 호환되는 코드는 아니었다. 그래서 공식문서까지 들어가서 messages를 활용할 방법을 찾아보았는데...

이 2개가 끝이다. 더 이상 뭔가 구분할 방법이 doc에 적혀있지를 않았다. 다행히, 여기서 name 하고 pattern 자체를 콘텍스트에 돌려준다는 사실을 알아내었고, 그것으로 아래 코드를 작성하게 되었다.

코드 전체를 올리기에는 보안성에 문제가 생길 수 있기에...

 name 필드를 message필드로 이용한다는 단순하면서도 좀 파괴적인 발상이 도움이 되었다.

const schema = Joi.object().keys({
    username: Joi.string()
    .alphanum()
    .min(3)
    .max(20)
    .required()
    .messages({
        'string.alphanum': '아이디는 영문자 및 숫자로만 이루어질 수 있습니다.',
        'string.min': '아이디는 3자 이상이어야합니다.',
        'string.max': '아이디는 20자를 넘을 수 없습니다.',
        'any.required': '아이디를 입력해주세요.'
    }),
    password: Joi.string()
    .min(6)
    .max(20)
    .regex(/^[!@#$%^&*a-zA-Z0-9]{3,30}$/)
    .regex(/[a-z]/, '비밀번호는 영어 소문자를 포함해야합니다.')
    .regex(/[A-Z]/, '비밀번호는 영어 대문자를 포함해야합니다.')
		... 그외 다수 regex ...
    .required()
    .messages({
        'string.min': '비밀번호는 6자 이상이어야합니다.',
        'string.max': '비밀번호는 20자를 넘을 수 없습니다.',
        'string.pattern.base': '비밀번호는 영문자 대/소, 숫자, 일부 특수문자(!@#$%^&*)만 사용하실 수 있습니다.',
        'any.required': '비밀번호를 입력해주세요.'
    }),
});

 

regex의 name에 name대신 message를 넣어두면 된다. 딱히 name을 신경쓰는것 같지도 않은데 뭐. GPT가 만능까지는 아니라는 것을 알게 된 경험이었다. 가끔 새로운 코드는 자기가 머리를 굴려서 만들어야 하는 것도 있다.

728x90
반응형