본문 바로가기
기타 미분류

[Node.js] error handling, custom error message 등

by domo7304 2021. 8. 12.

팀프로젝트 중 사용자 인증, 허가 부분 백엔드 부분을 작성하게 되었는데, 비정상적인 입력 등 뭔가 예외적인 사항들을 프론트에서 처리하는 것인지 백엔드에서 처리하는 것인지 잘 몰라서 예외처리 부분을 알아보던 중, mongoose validation이라는 항목을 발견했다.

예외처리로 의미없는 false를 그냥 보내는 것보다는, 백엔드에서 뭐가 왜 잘못되었는지 메시지를 찍어서 json으로 보내주는 게 프론트분 입장에서도 편하실 것 같다는 생각이 들어 내가 작성하기로 했고, mongoose의 built in validator를 이용해서 cusmtom error message를 작성한 후 해당 message를 출력하는 error handling 방법에 대해 알아보았다.

https://mongoosejs.com/docs/validation.html

 

Mongoose v5.13.7: Validation

Validation Before we get into the specifics of validation syntax, please keep the following rules in mind: Validation is defined in the SchemaType Validation is middleware. Mongoose registers validation as a pre('save') hook on every schema by default. You

mongoosejs.com

 

1. 예외처리가 되어야 하는 부분들

회원가입 코드
입력이 email 형식이 아닌 경우
이미 존재하는 계정일 경우
비밀번호의 길이가 짧을 경우

내가 작성한 부분 중 회원가입 부분을 예로 들자면

  1. 사용자 입력이 email의 형식을 가지지 않는 경우 : false를 반환해주어야 하지만, 에러 핸들링이 없는 상태여서 true를 반환
  2. 이미 존재하는 계정일 경우 : try ~ catch 덕분에 false가 출력되긴하나, 어떤 에러인지 클라이언트 입장에서는 알 수가 없음. 
  3. 비밀번호의 길이가 기준보다 짧을 경우 : 역시 false가 출력되긴 하나, 어떤 에러인지 알 수가 없다.

위의 세 가지에 대해 에러 핸들링을 하고자 한다.

2. mongoose의 built-in validator

mongoose 공식문서의 내용이다. 이외에도 여러 내용들이 있지만 일단 필요한 부분만 살펴보았다.

Built-in Validators
1. 모든 schematypes는 bulit-in 'required' validator를 가진다. required validator는 checkRequired() 함수를 이용하여 값이 조건을 만족하는지 결정한다.
2. number 타입은 min, max validator를 갖는다.
3. string 타입은 enum, match, minLength, maxLength validator를 갖는다.

Custom Error Messages
각각의 validator를 위한 custom error message를 설정할 수 있으며, error message를 설정하는 방법에는 'Array syntax', 'Object syntax' 두 가지 방식이 있다.

에러 메시지를 작성하는 방법이 두 가지가 있는데, 일단은 Array syntax 방법으로 작성해보고 각 방법이 어떠한 차이를 가지는지 나중에 찾아보아야겠다. 

2-1. 이메일 입력에 대한 validation

사용자가 입력한 이메일 형식을 검사하기 위한 방법으로는 서드파티 모듈인 validator를 사용했다. 6번 line 처럼 require 후 18번 라인처럼 에러메시지를 작성해주었다. 사용자가 입력한 내용이 자동으로 isEmail로 넘어가 유효성검사를 하게 된다.

2-2. custom error message 작성

공식문서의 방법에 따라 배열 방식 syntax로 위와 같이 코드를 작성해주었다.

3. error 살펴보기

에러를 핸들링하기 위해, mongoose가 어떻게 error를 출력해주는지 먼저 살펴보자

첫 번째 이미지와 같이 error를 콘솔로 보기 위한 간단한 코드를 작성해주었고, try ~ catch에서 catch로 넘어왔을 경우 handleErrors()를 호출하기로 했다. err.code의 경우 대부분의 error는 code를 갖지 않지만 mongoose 스키마의 unique option의 경우 중복 예외가 발생하면 '11000'이라는 error code를 보여준다.

순서대로 이메일 형식 오류, 중복된 계정 입력, 비밀번호 길이 불충분에 따른 오류 메시지가 콘솔에 출력되는 것을 볼 수 있다. 앞에서 error code에 대해 말했듯이 첫 번째, 세 번째 error의 경우 문자열 마지막에 'undefined'가 에러 코드이고, 두 번째 error의 '11000' 이 중복에 대한 error code이다.

error message를 살펴봄으로써 error code를 갖는 error는 code를 통해 구분할 수 있고, 그 외의 error들은 'User validation failed' 라는 문자열이 공통적으로 출력됨을 알 수 있다.

3-1. errors 객체 내부

if문을 이용하여 'User validation failed' 라는 문자열을 갖는 error들에 대해 console.log(err)로 내용을 확인해보면, 무수히 많은 내용이 출력된다. 그 중 중간에 errors: { ... } 라는 object를 찾을 수 있다.

해당 object를 살펴보면 에러가 발생했을 경우 error object 안에 또 다른 object가 존재하며, 어디서 어떠한 에러가 있었는지 보여준다. 예를 들어 email에서 에러가 났을 경우 email object만 존재하고, email, password 둘 다에서 에러가 발생했을 경우 error object 안에 email, password object가 존재하게 된다.

error 내용이 전부 다 필요한 것이 아니므로, 우리가 필요한 error안에 email: { ... } 혹은 password: { ... } 부분만 살펴보기 위해 error를 파싱해주자. handleErrors에 36~38 line과 같은 코드를 추가해준 후 postman을 통해 invalid한 값을 request로 보내주면

error 객체로부터 위와 같은 형태의 값을 얻을 수 있다. 위 이미지의 마지막 줄을 살펴보면 대괄호로 닫혀있는 것을 볼 수 있는데, Object.values(err.errors) 가 내부 object를 배열로 출력해주기 때문이다.

error object 안의 내용이 배열로 출력되는 것을 확인했으니 forEach()를 이용하여 errors.properties의 값을 다음과 같이 얻을 수 있다.

이제 handlingErrors()를 다음과 같이 완성하고자 한다.

  1. err.message가 'User validation failed' 를 포함할 경우 혹은 err.code가 11000과 일치할 경우, 어떠한 동작을 하도록 if문 작성
  2. email 혹은 password 객체의 properties 안에 있는 'message' 에 접근하여
  3. handleErrors()가 해당 message를 return하도록 작성
  4. handleErrors()로부터 return받은 message를 json으로 client에게 넘겨주기

 

3-2. handleErrors() 함수 완성

postman 화면에서 볼 수 있듯 이메일 형식, 비밀번호 길이, 이미 존재하는 계정에 대한 에러가 정상적으로 json으로 전달됨을 확인할 수 있다.

 

작성을 다 끝내고 보니 그냥 error code 예외처리처럼 error.email, error.password 에 문자열을 하드코딩 해줘도 됐을걸 그랬다...생각보다 mongoose error를 파싱해서 그 안이 어떻게 구성 되어있는 것인지 파악하는데 많은 시간을 소요했다..

하지만 공통적으로 다뤄야할 에러가 이번처럼 2개가 아니라 3개, 4개, 훨씬 많다면 forEach를 이용한 처리가 훨씬 효율적일테니, 이번 기회를 통해 mongoose error 파싱하는 법을 익힐 수 있었고, 나만의 에러 핸들링 방식을 찾았다는 것에 의의를 둘 수 있을 것 같다.

댓글