Featured image of post [golang] Validation 모듈을 이용한 Structure 검증

[golang] Validation 모듈을 이용한 Structure 검증

백엔드 API 서버를 개발하다 보면 입력 값 검증은 항상 따라옵니다. Go 에도 여러가지 validation 라이브러리가 있는데, awesome-go 기준 star 가 많은 라이브러리는 크게 두 종류의 방식이 있습니다.

Struct tag 사용

validator(⭐10.2k)

validator

Go의 validator 라이브러리 중 가장 많은 Star 를 보유한 라이브러리 입니다. 제가 주로 사용하는 Fiber 프레임 워크의 문서에서도 우선적으로 소개하기 때문에 가장 많이 사용 했습니다.

struct 정의

1
2
3
4
5
6
7
type SignUp struct {
	ID       string `validate:"required,gte=4,lte=255,alphanum"`
	Password string `validate:"required,password"`
	Gender   string `validate:"gender"`
	Age      int    `validate:"gte=0"`
	Email    string `validate:"email"`
}

validator는 struct tag를 통해 validation을 관리합니다.

사용

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func NewSignUpValidator() *validator.Validate {
	v := validator.New()
	return v
}

signup := &SignUp{
	ID:       "foobar",
	Password: "Abcdefghijklmnop1!",
	Gender:   "male",
	Age:      10,
	Email:    "foo@bar.com",
}
v := NewSignUpValidator()
err := v.Struct(signup)

커스텀 validate

1
2
3
4
v := validator.New()
_ = v.RegisterValidation("gender", func(fl validator.FieldLevel) bool {
	return fl.Field().String() == "male" || fl.Field().String() == "female"
})

위 구조체의 validation 중 ‘password’와 ‘gender’는 기본적으로 제공 되지 않는 옵션입니다. 커스텀 옵션은 위와 같이 추가 됩니다. (password 함수는 길이상 블로그에 올리지 않았습니다.)

validator 는 기본적으로 정규표현식을 이용한 검증을 지원하지 않습니다. 공식 문서에서는 Go의 struct tag 구조상 ‘,’ 와 ‘=’ 를 넣을 수 없어서라는 이유를 제시합니다만, 안그래도 난잡한 struct tag에 정규표현식까지 들어간 지옥도는 그다지 보고 싶지 않기 때문에 전 좋다고 생각합니다.

위 예제 코드는 공개되어 있습니다.

비슷한 라이브러리로는 govalidator(⭐5.3k) 가 있습니다.

Struct tag 싫어!

위 예제에서는 struct tag에 validation 정보만 들어갔기 때문에 참사는 벌어지지 않았지만, json 등의 추가 태그가 들어가면 가독성이 점점 떨어지는 문제가 있습니다. 그런 이유에선지 Go 커뮤니티 에서도 호불호를 쉽게 찾아볼 수 있는데, 역시나 사용하지 않는 라이브러리도 있습니다.

ozzo-validation(⭐2.7k)

ozzo-validation

struct tag 를 사용하지 않는 라이브러리 중 가장 높은 Star를 받은 라이브러리 입니다. 구조체를 매개변수로 받는 함수를 구현하는 방식으로 사용되어 사용 중 magical 한 부분이 적다는 인상을 받았습니다.

이 포스트를 작성하며 처음으로 들여다 본 라이브러리인데 Go 에서 호불호가 갈리는 struct tag를 사용하지 않는 것 만으로 충분히 의의가 있지 않나 싶습니다.

기본 예제 코드

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
type Address struct {
	Street string
	City   string
	State  string
	Zip    string
}

func (a Address) Validate() error {
	return validation.ValidateStruct(&a,
		// Street cannot be empty, and the length must between 5 and 50
		validation.Field(&a.Street, validation.Required, validation.Length(5, 50)),
		// City cannot be empty, and the length must between 5 and 50
		validation.Field(&a.City, validation.Required, validation.Length(5, 50)),
		// State cannot be empty, and must be a string consisting of two letters in upper case
		validation.Field(&a.State, validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))),
		// State cannot be empty, and must be a string consisting of five digits
		validation.Field(&a.Zip, validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))),
	)
}
Licensed under CC BY-NC-SA 4.0
Hugo로 만듦
JimmyStack 테마 사용 중