Joi & celebrate
Node 로 Rest API 서버를 만들면서 일반적으로 제일 손이 많이 가는 작업은 클라이언트에서 보내는 요청에대한 검증 로직을 만드는 부분과 DB에 적절한 요청을 보내는 부분인데, 그중 클라이언트에서 보내는 요청에 대한 검증 로직을 보다 명확하고 깔끔하게 만들어주는 @hapi/joi
와 @hapi/joi
를 express 에서 사용하기 좀더 쉽게 만들어주는 celebrate
를 이용해서 간단한 검증겸 잘못된 요청에 대해서 에러처리를 해보자
시작
서버에서 클라이언트에서 보내는 요청을 검증을 할 때, 검증을 하기 위해 필요한 부분은 우선적으로는 body 로 받을것인지 아니면 다른 형식으로 받을 것인지를 정하고 body 로 받는다면, 어떤 변수를 받을 것인지를 정해야 하며, 변수의 타입을 어떤식으로 받을것이며, 기본값은 무었으로 할것인지 등등을 정해줘야한다.
전부 정하면 서비스 로직에서 요청에대한 검증을 위한 코드로 검증을 하고 만약 틀리면 에러메세지를 보내는게 일반적인 검증 절차이다.
이때, @hapi/joi
를 이용하면 서비스 로직과 검증 로직이 분리가 되며, 검증을 위한 로직이 없이 데이터의 타입이나 변수에대한 부분을 정의해놓은 스키마를 통해 가독성이 좋고 실수를 적게 할수 있다.
일반적으로 페이징 처리를 위한 쿼리에대한 검증 스키마이다.
1 | const pagingSchema = { |
위 검증 스키마는 변수 명으로 offset 과 limit 를 받을것이며, 위 두가지 변수는 모두 숫자형이어야하며, 없는경우 기본값으로 0,30 을 자동으로 넣어주며, offset의 경우 0이 들어와도 Error 처리를 하지 않겠다는 의미이다.
위 스키마를 검증을 하려면 다음과 같이 사용하면 된다.
1 | const { error, value } = Joi.object(pagingSchema).validate(req.query) |
여기에 추가적으로 celebrate
를 이용하여 쿼리나 다른부분에 대해 검증스키마가 무었인지 정해주면 자동으로 에러처리를 해주게 된다.
1 | import { Joi, Segments, celebrate } from 'celebrate'; |
이때 Joi는 @hapi/joi
의 joi를 호출하면 않되고 celebrate
의 joi 를 호출해서 사용해야 한다.
celebrate
를 이용하면 검증이 통과되지 않은 경우 자동으로 에러를 던지게 되며 express 의 경우 에러처리 미들웨어로 로직이 넘어간다.
이 경우에 에러 메세지가 joi에서 자체적으로 만든 에러가 전달이 된다. 이때 isCelebrate(error)
를 이용하면 joi에서 나오는 에러만 구분이 가능해지며, 이때 클라이언트에게 전달할 에러 메세지를 만들고 보낼수 있다.
검증 스키마
스키마는 보통 데이터 명, 데이터 타입, 데이터가 없는경우의 기본값, 그리고 제한사항 정도를 joi 형식에 맞게 만든다고 생각하면 된다.
1 | 데이터 명 : Joi.데이터타입().default(데이터가 없는경우의 기본값).그외의 제한사항 |
위 와 같이 정의를 하면 된다.
이때, 그외의 제한사항 은 다음과 같이 chain 형식으로 작성을 해야한다.
- required() : 만약 요청시 없는경우에 에러
- min() : 최소값 제한
- max() : 최대값 제한
- length() : 문자열 길이 제한
- valid() : 허용하는 값의 목록 (ex. enum 같은 경우)
- allow() : 검증 실패한 데이터의 경우 예외 데이터
팁
우선은 기본적으로 모든 스키마는 joi.object를 통해서 묶어줘야 하며, 묶는부분은 파일에서 export 를 하는 부분에서 해주는게 좋다
포스트에 알려주는 방식은 주로 사용하는 검증 스키마이며, 공식 문서를 보면 훨씬 자유롭게 커스텀이 가능하다.
스키마는 별도의 파일로 분리해서 사용하는게 좋으며, 라우터를 분리하는 만큼 분리를 해야 가독성이 올라간다. 하나의 파일에 모든 스키마가 있으면 찾기가 좀 많이 힘들다.
그렇기 때문에 보통은 하나의 컨트롤러 또는 하나의 라우터당 한개의 스키마 파일을 사용하는것을 권한다.
그리고, 스키마 중 params 는 라우터(컨트롤러)당 겹치는 부분이 많아서 변수를 이용해서 분리를 해놓는게 좋다.
Custom Error
에러 처리 방식은 매우 다양하지만 대부분 에러 코드나 서버에서 직접적으로 에러 메세지를 보내는 방식을 사용을 한다. 하지만 joi에서 보내는 에러 메세지를 그대로 전달을 하기에는 우선적으로는 영어여서 직관적이지 못하고, 검증에 통과하지 못한 변수명에 `을 양쪽에 붙여서 보내서 상당히 성의가 없어 보인다는 단점이 있다. 그래서 자체적으로 에러 메세지를 만드는 것을 추천한다
1 | (err: CelebrateInternalError) => { |
만약 lodash를 사용한다면 다음과 같이 사용하는것도 좋다
1 | const joiTemplate = { |