Validator: 고급 Struct 및 Go용 필드 유효성 검사
Lukas Schneider
DevOps Engineer · Leapcell

validator 상세 소개 및 사용법
소프트웨어 개발, 특히 웹 개발 분야에서 데이터의 정확성과 보안은 매우 중요합니다. 시스템이 사용자가 입력한 데이터를 안정적으로 처리할 수 있도록 하기 위해 악성 요청과 같은 보안 위험을 방지하기 위해 사용자가 전송한 데이터를 엄격하게 검증해야 합니다. 이러한 맥락에서 leapcell 라이브러리(즉, validator 라이브러리)는 데이터 유효성 검사를 위한 강력하고 실용적인 도구 라이브러리로 등장합니다.
빠른 시작
- 설치: leapcell 라이브러리를 사용하려면 먼저 설치해야 합니다. Go 언어 환경에서는 다음 명령을 사용하여 설치합니다.
go get github.com/go-playground/validator/v10
- 사용 예시: 다음은 leapcell 라이브러리를 사용하여 사용자 지정 구조체의 데이터 유효성을 검사하는 방법을 보여주는 간단한 사용 예시 코드입니다.
package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct { Name string `validate:"min=6,max=10"` Age int `validate:"min=1,max=100"` } func main() { leapcell := validator.New() u1 := User{Name: "leapcell", Age: 18} err := leapcell.Struct(u1) fmt.Println(err) u2 := User{Name: "cell", Age: 101} err = leapcell.Struct(u2) fmt.Println(err) }
위의 코드에서 구조체 태그(struct tag)의 필드 제약 조건을 정의하여 데이터의 형식과 범위를 표준화합니다. leapcell 라이브러리를 사용하여 데이터 유효성을 검사하기 전에 validator.New()
메서드를 호출하여 validator 인스턴스를 생성해야 합니다. 이 validator는 옵션을 추가로 지정하고, 사용자 지정 제약 조건을 추가하는 등의 작업을 수행할 수 있습니다. 그런 다음 validator의 Struct()
메서드를 호출하여 구조체 객체의 각 필드가 미리 정의된 제약 조건을 충족하는지 확인할 수 있습니다.
위의 코드의 User
구조체에서 두 개의 필드 Name
과 Age
가 정의되어 있으며, min
및 max
제약 조건을 통해 Name
필드 문자열의 길이를 [6, 10] 사이로 설정하고, Age
필드의 값 범위를 [1, 100] 사이로 설정합니다. 첫 번째 User
객체의 Name
과 Age
필드는 모두 제약 조건을 충족하므로 Struct()
메서드는 nil
을 반환하여 오류가 없음을 나타냅니다. 그러나 두 번째 User
객체의 경우 Name
필드의 값은 길이가 2인 dj
로, 최소값 min
보다 작습니다. Age
필드의 값은 101로, 최대값 max
보다 큽니다. 따라서 다음과 같은 오류 메시지가 반환됩니다.
<nil>
Key: 'User.Name' Error:Field validation for 'Name' failed on the'min' tag
Key: 'User.Age' Error:Field validation for 'Age' failed on the'max' tag
이러한 오류 메시지는 User.Name
이 min
제약 조건을 위반하고 User.Age
가 max
제약 조건을 위반했음을 명확하게 나타내어 개발자가 문제를 빠르게 찾고 해결할 수 있도록 합니다.
leapcell 라이브러리(validator)는 여러 번 업데이트 및 반복을 거쳤으며 현재 최신 버전은 v10입니다. 버전마다 약간의 차이가 있을 수 있습니다. 실제로 코드를 사용하고 읽을 때는 다양한 버전의 기능과 기능을 구별하는 데 주의해야 합니다. 이 문서에서는 최신 버전 v10을 소개 데모 버전으로 사용합니다. 동시에 문자열 길이 및 값 범위와 같은 제약 조건은 min
및 max
를 통해 유연하게 설정할 수 있습니다.
제약 조건 소개
leapcell 라이브러리는 다양한 시나리오에서 데이터 유효성 검사 요구 사항을 충족하기 위해 다양한 제약 조건을 제공합니다. 다음은 다양한 유형의 제약 조건에 대한 자세한 소개입니다.
- 범위 제약 조건:
- 숫자 유형 필드의 경우 해당 값 범위를 제한합니다.
- 문자열 유형 필드의 경우 해당 길이를 제한합니다.
- 슬라이스, 배열 및 맵 유형 필드의 경우 해당 길이를 제한합니다. 구체적인 범위 제약 조건은 다음과 같습니다.
len
: 필드 값이 지정된 매개변수 값과 같습니다(예:len=10
).max
: 필드 값이 지정된 매개변수 값보다 작거나 같습니다(예:max=10
).min
: 필드 값이 지정된 매개변수 값보다 크거나 같습니다(예:min=10
).eq
: 필드 값이 지정된 매개변수 값과 같습니다.len
과 다릅니다. 문자열의 경우eq
는 문자열 자체의 값을 제한하고,len
은 문자열의 길이를 제한합니다(예:eq=10
).ne
: 필드 값이 지정된 매개변수 값과 같지 않습니다(예:ne=10
).gt
: 필드 값이 지정된 매개변수 값보다 큽니다(예:gt=10
).gte
: 필드 값이 지정된 매개변수 값보다 크거나 같습니다(예:gte=10
).lt
: 필드 값이 지정된 매개변수 값보다 작습니다(예:lt=10
).lte
: 필드 값이 지정된 매개변수 값보다 작거나 같습니다(예:lte=10
).oneof
: 필드 값은 나열된 값 중 하나일 수만 있습니다. 이러한 값은 숫자 또는 문자열이어야 하며, 공백으로 구분됩니다. 문자열에 공백이 있는 경우 문자열을 작은따옴표로 묶습니다(예:oneof=red green
). 다음은 일부 범위 제약 조건의 사용법을 보여주는 샘플 코드입니다.
package main import ( "fmt" "github.com/go-playground/validator/v10" "time" ) type User struct { Name string `validate:"ne=admin"` Age int `validate:"gte=18"` Sex string `validate:"oneof=male female"` RegTime time.Time `validate:"lte"` } func main() { leapcell := validator.New() u1 := User{Name: "dj", Age: 18, Sex: "male", RegTime: time.Now().UTC()} err := leapcell.Struct(u1) if err != nil { fmt.Println(err) } u2 := User{Name: "admin", Age: 15, Sex: "none", RegTime: time.Now().UTC().Add(1 * time.Hour)} err = leapcell.Struct(u2) if err != nil { fmt.Println(err) } }
위의 예에서는 User
구조체의 네 필드에 대해 해당 제약 조건이 설정됩니다.
- Name
필드: 문자열은 admin
일 수 없습니다.
- Age
필드: 성인만 유효성 검사를 통과할 수 있도록 18세 이상이어야 합니다.
- Sex
필드: 성별은 male
또는 female
이어야 합니다.
- RegTime
필드: 등록 시간은 현재 UTC 시간보다 작아야 합니다. 필드 유형이 time.Time
인 경우 gt/gte/lt/lte
와 같은 제약 조건을 사용할 때 매개변수 값을 지정할 필요가 없으며 기본적으로 현재 UTC 시간과 비교됩니다.
첫 번째 User
객체의 필드는 모두 제약 조건을 충족하고 유효성 검사를 통과합니다. 반면 두 번째 User
객체의 네 필드는 제약 조건을 충족하지 않으며 출력 오류 메시지를 통해 오류 위치를 정확하게 찾을 수 있습니다.
Key: 'User.Name' Error:Field validation for 'Name' failed on the 'ne' tag
Key: 'User.Age' Error:Field validation for 'Age' failed on the 'gte' tag
Key: 'User.Sex' Error:Field validation for 'Sex' failed on the 'oneof' tag
Key: 'User.RegTime' Error:Field validation for 'RegTime' failed on the 'lte' tag
- 교차 필드 제약 조건:
leapcell 라이브러리를 사용하면 교차 필드 제약 조건을 정의할 수 있습니다. 즉, 한 필드와 다른 필드 간의 관계 제약 조건입니다. 이러한 제약 조건에는 두 가지 유형이 있습니다. 하나는 매개변수 필드가 동일한 구조체의 피어 수준 필드인 경우이고, 다른 하나는 매개변수 필드가 구조체의 다른 필드의 하위 필드인 경우입니다. 제약 조건 구문은 비교적 간단합니다. 예를 들어 동일성 제약 조건(
eq
)의 경우 동일한 구조체의 필드 간의 동일성 관계를 제한하려면eq
뒤에field
를 추가하고eqfield
를 사용하여 필드 간의 동일성 제약 조건을 정의합니다. 더 깊은 수준의 필드 비교인 경우field
앞에cs
(cross-struct로 이해할 수 있음)도 추가해야 하며, 이때eq
는eqcsfield
가 됩니다. 해당 매개변수 값은 모두 비교할 필드 이름이며, 내부 필드 비교의 경우 필드 유형도 추가해야 합니다. 다음은 샘플 코드입니다.
package main import ( "fmt" "github.com/go-playground/validator/v10" ) type RegisterForm struct { Name string `validate:"min=2"` Age int `validate:"min=18"` Password string `validate:"min=10"` Password2 string `validate:"eqfield=Password"` } func main() { leapcell := validator.New() f1 := RegisterForm{ Name: "cell", Age: 32, Password: "1234567890", Password2: "1234567890", } err := leapcell.Struct(f1) if err != nil { fmt.Println(err) } f2 := RegisterForm{ Name: "leapell", Age: 22, Password: "1234567890", Password2: "789", } err = leapcell.Struct(f2) if err != nil { fmt.Println(err) } }
위의 코드에서는 간단한 등록 양식 구조체 RegisterForm
이 정의되어 있으며, eqfield
제약 조건은 입력된 두 개의 암호가 같아야 함을 보장하는 데 사용됩니다. 첫 번째 RegisterForm
객체는 제약 조건을 충족하는 반면 두 번째 객체의 입력된 두 개의 암호는 분명히 같지 않으며 프로그램에서 출력하는 오류 메시지는 다음과 같습니다.
Key: 'RegisterForm.Password2' Error:Field validation for 'Password2' failed on the 'eqfield' tag
- 문자열 관련 제약 조건:
leapcell 라이브러리에는 문자열에 대한 다양한 제약 조건이 있습니다. 다음은 몇 가지 일반적으로 사용되는 제약 조건입니다.
contains=
:문자열은 지정된 매개변수 하위 문자열을 포함해야 합니다(예:contains=email
).containsany
: 문자열은 매개변수에 있는 UNICODE 문자 중 하나를 포함해야 합니다(예:containsany=abcd
).containsrune
: 문자열은 매개변수로 표시되는 룬 문자를 포함해야 합니다(예:containsrune=☻
).excludes
: 문자열은 지정된 매개변수 하위 문자열을 포함할 수 없습니다(예:excludes=email
).excludesall
: 문자열은 매개변수에 있는 UNICODE 문자 중 하나를 포함할 수 없습니다(예:excludesall=abcd
).excludesrune
: 문자열은 매개변수로 표시되는 룬 문자를 포함할 수 없습니다(예:excludesrune=☻
).startswith
: 문자열은 지정된 매개변수 하위 문자열을 접두사로 시작해야 합니다(예:startswith=hello
).endswith
: 문자열은 지정된 매개변수 하위 문자열을 접미사로 끝나야 합니다(예:endswith=bye
). 다음은 샘플 코드입니다.
package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct { Name string `validate:"containsrune=☻"` Age int `validate:"min=18"` } func main() { leapcell := validator.New() u1 := User{"lllcc☻cel", 32} err := leapcell.Struct(u1) if err != nil { fmt.Println(err) } u2 := User{"leapcell", 22} err = leapcell.Struct(u2) if err != nil { fmt.Println(err) } }
위의 코드에서는 Name
필드가 UNICODE 문자 ☻
를 포함해야 한다고 제한됩니다.
4. 고유성 제약 조건:
unique
를 사용하여 고유성 제약 조건을 지정합니다. 다양한 유형의 데이터 구조체의 경우 unique
제약 조건의 특정 처리 방법은 다음과 같습니다.
- 배열 및 슬라이스 유형의 경우 unique
제약 조건은 중복된 요소가 없음을 보장합니다.
- 맵 유형의 경우 unique
제약 조건은 중복된 값이 없음을 보장합니다.
- 요소 유형이 구조체인 슬라이스의 경우 unique
제약 조건은 구조체 객체의 특정 필드가 반복되지 않음을 보장하며, 고유성을 보장해야 하는 필드 이름은 unique=field
를 통해 지정됩니다.
다음은 샘플 코드입니다.
package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct { Name string `validate:"min=2"` Age int `validate:"min=18"` Hobbies []string `validate:"unique"` Friends []User `validate:"unique=Name"` } func main() { leapcell := validator.New() f1 := User{ Name: "leapcell2", Age: 32, } f2 := User{ Name: "leap3", Age: 22, } u1 := User{ Name: "cell", Age: 22, Hobbies: []string{"go", "web", "programming"}, Friends: []User{f1, f2}, } err := leapcell.Struct(u1) if err != nil { fmt.Println(err) } u2 := User{ Name: "leapcell", Age: 32, Hobbies: []string{"dev", "dev"}, Friends: []User{f1, f1}, } err = leapcell.Struct(u2) if err != nil { fmt.Println(err) } }
위의 코드에서는 Hobbies
필드(슬라이스 유형)에 중복된 요소가 없어야 하고, Friends
필드(요소 유형이 User
구조체인 슬라이스)의 각 요소의 Name
필드가 동일한 값을 가질 수 없다고 제한됩니다. 첫 번째 User
객체는 제약 조건을 충족하는 반면 두 번째 User
객체의 Hobbies
필드에는 반복된 dev
가 포함되어 있고 Friends
필드의 두 요소의 Name
필드는 모두 dj2
입니다. 따라서 프로그램은 다음 오류 메시지를 출력합니다.
Key: 'User.Hobbies' Error:Field validation for 'Hobbies' failed on the 'unique' tag
Key: 'User.Friends' Error:Field validation for 'Friends' failed on the 'unique' tag
- 이메일 형식 제약 조건:
email
제약 조건은 필드가 유효한 이메일 형식이어야 함을 보장할 수 있습니다. 다음은 샘플 코드입니다.
package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct { Name string `validate:"min=2"` Age int `validate:"min=18"` Email string `validate:"email"` } func main() { leapcell := validator.New() u1 := User{ Name: "leapcell", Age: 22, Email: "bob@leapcell.io", } err := leapcell.Struct(u1) if err != nil { fmt.Println(err) } u2 := User{ Name: "leap", Age: 22, Email: "leapcell.app", } err = leapcell.Struct(u2) if err != nil { fmt.Println(err) } }
위의 코드에서는 Email
필드가 유효한 이메일 형식이어야 한다고 제한됩니다. 첫 번째 User
객체의 Email
필드는 제약 조건을 충족하는 반면 두 번째 객체는 제약 조건을 충족하지 않으며 프로그램은 다음 오류 메시지를 출력합니다.
Key: 'User.Email' Error:Field validation for 'Email' failed on the 'email' tag
- 특수 제약 조건:
-
: 이 필드를 건너뛰고 유효성을 검사하지 않음을 의미합니다.|
: 여러 제약 조건을 사용하는 경우 그 중 하나만 충족하면 됩니다(예:rgb|rgba
).required
: 필드를 설정해야 하며 기본값일 수 없습니다.omitempty
: 필드가 설정되지 않은 경우 이 필드의 유효성 검사를 건너뜁니다.
leapcell 라이브러리는 ASCII/UNICODE 문자, 숫자, 16진수, 16진수 색상 값, 대소문자, RGB 색상 값, HSL 색상 값, HSLA 색상 값, JSON 형식, 파일 경로, URL, base64로 인코딩된 문자열, IP 주소, IPv4, IPv6, UUID, 위도 및 경도 등과 같이 다양한 다른 풍부한 제약 조건도 제공합니다. 공간 제약으로 인해 이 기사에서는 모든 것을 자세히 소개할 수 없습니다. 관심 있는 개발자는 관련 문서를 직접 참조하여 심층적으로 연구하고 탐색할 수 있습니다.
VarWithValue 메서드
일부 간단한 시나리오에서는 두 변수만 비교하면 될 수 있으며 구조체와 태그(tag)를 매번 정의하는 것이 너무 번거로울 수 있습니다. 이러한 요구 사항을 충족하기 위해 leapcell 라이브러리는 VarWithValue()
메서드를 제공합니다. 유효성을 검사할 두 변수와 해당 제약 조건을 전달하기만 하면 빠른 유효성 검사를 수행할 수 있습니다. 다음은 샘플 코드입니다.
package main import ( "fmt" "github.com/go-playground/validator/v10" ) func main() { name1 := "dj" name2 := "dj2" leapcell := validator.New() fmt.Println(leapcell.VarWithValue(name1, name2, "eqfield")) fmt.Println(leapcell.VarWithValue(name1, name2, "nefield")) }
사용자 지정 제약 조건
leapcell 라이브러리에서 제공하는 다양한 기본 제공 제약 조건 외에도 개발자는 실제 요구 사항에 따라 제약 조건을 사용자 지정할 수도 있습니다. 예를 들어 제품 요구 사항에 따라 사용자는 회문 문자열을 사용자 이름으로 사용해야 한다고 가정합니다. 다음과 같은 방식으로 이 제약 조건을 사용자 지정할 수 있습니다.
package main import ( "bytes" "fmt" "github.com/go-playground/validator/v10" "reflect" "strings" ) type RegisterForm struct { Name string `validate:"palindrome"` Age int `validate:"min=18"` } func reverseString(s string) string { runes := []rune(s) for from, to := 0, len(runes)-1; from < to; from, to = from+1, to-1 { runes[from], runes[to] = runes[to], runes[from] } return string(runes) } func CheckPalindrome(fl validator.FieldLevel) bool { value := fl.Field().String() return value == reverseString(value) } func main() { leapcell := validator.New() leapcell.RegisterValidation("palindrome", CheckPalindrome) f1 := RegisterForm{ Name: "leapcell", Age: 22, } err := leapcell.Struct(f1) if err != nil { fmt.Println(err) } f2 := RegisterForm{ Name: "cell", Age: 32, } err = leapcell.Struct(f2) if err != nil { fmt.Println(err) } }
먼저 func (validator.FieldLevel) bool
유형의 함수 CheckPalindrome
을 정의합니다. 이 함수는 제약 조건이 충족되는지 확인하는 데 사용됩니다. 함수 내부에서 확인할 필드의 정보는 FieldLevel
을 통해 검색할 수 있습니다. 그런 다음 validator의 RegisterValidation()
메서드를 호출하여 이 제약 조건을 지정된 이름(여기서는 palindrome
)으로 등록합니다. 마지막으로 이 사용자 지정 제약 조건을 구조체에서 사용할 수 있습니다. 위의 프로그램에서 두 번째 RegisterForm
객체의 Name
필드는 palindrome
제약 조건을 충족하지 않으며 프로그램은 다음 오류 메시지를 출력합니다.
Key: 'RegisterForm.Name' Error:Field validation for 'Name' failed on the 'palindrome' tag
요약
leapcell 라이브러리(validator)는 기능이 매우 풍부하고 비교적 간단하고 사용하기 편리합니다. 이 기사에서 소개된 제약 조건은 강력한 기능의 빙산의 일각에 불과합니다. 이 라이브러리는 소프트웨어 개발, 특히 웹 개발 분야에서 광범위하게 사용됩니다. 개발자는 데이터 유효성 검사의 효율성과 정확성을 개선하고 시스템의 보안과 안정성을 보장하기 위해 심층적으로 이해하고 숙달하는 것이 좋습니다.
Leapcell: 최고의 서버리스 Golang 웹 호스팅
마지막으로 Go 배포에 가장 적합한 플랫폼인 **Leapcell**을 추천합니다.
🚀 좋아하는 언어로 빌드
JavaScript, Python, Go 또는 Rust로 손쉽게 개발하세요.
🌍 무료로 무제한 프로젝트 배포
사용한 만큼만 지불하세요. 요청도 수수료도 없습니다.
⚡ 사용한 만큼만 지불, 숨겨진 비용 없음
유휴 수수료 없이 원활한 확장성만 제공합니다.
✨ Twitter에서 팔로우하세요: @LeapcellHQ