Go로 Govaluate 규칙 엔진 구현
Wenhao Wang
Dev Intern · Leapcell

소개
2024년에 저는 govaluate를 사용하여 규칙 엔진을 작성했습니다. 이 엔진의 장점은 Go에 동적 언어의 기능을 부여하여 일부 계산 작업을 수행하고 결과를 얻을 수 있다는 것입니다. 이를 통해 해당 코드를 작성하지 않고도 기능을 구현할 수 있습니다. 대신 문자열을 구성하기만 하면 됩니다. 규칙 엔진을 구축하는 데 매우 적합합니다.
빠른 시작
먼저 설치합니다.
$ go get github.com/Knetic/govaluate
다음으로 사용합니다.
package main import ( "fmt" "log" "github.com/Knetic/govaluate" ) func main() { expr, err := govaluate.NewEvaluableExpression("5 > 0") if err != nil { log.Fatal("syntax error:", err) } result, err := expr.Evaluate(nil) if err != nil { log.Fatal("evaluate error:", err) } fmt.Println(result) }
govaluate를 사용하여 표현식을 계산하는 방법은 두 단계뿐입니다.
NewEvaluableExpression()
을 호출하여 표현식을 표현식 객체로 변환합니다.- 표현식 객체의
Evaluate
메서드를 호출하고 매개변수를 전달하여 표현식 값을 반환합니다.
위의 예제는 간단한 계산을 보여줍니다. govaluate를 사용하여 5 > 0
의 값을 계산합니다. 이 표현식은 매개변수가 필요하지 않으므로 nil
값이 Evaluate()
메서드에 전달됩니다. 물론 이 예제는 그다지 실용적이지 않습니다. 5 > 0
을 코드에서 직접 계산하는 것이 더 편리합니다. 그러나 경우에 따라 계산해야 하는 표현식에 대한 모든 정보를 알 수 없거나 표현식 구조조차 알 수 없는 경우가 있습니다. 여기서 govaluate의 역할이 두드러집니다.
매개변수
govaluate는 표현식에서 매개변수 사용을 지원합니다. 표현식 객체의 Evaluate()
메서드를 호출할 때 map[string]interface{}
유형을 통해 계산에 매개변수를 전달할 수 있습니다. 여기서 맵의 키는 매개변수 이름이고 값은 매개변수 값입니다. 예를 들어:
func main() { expr, _ := govaluate.NewEvaluableExpression("foo > 0") parameters := make(map[string]interface{}) parameters["foo"] = -1 result, _ := expr.Evaluate(parameters) fmt.Println(result) expr, _ = govaluate.NewEvaluableExpression("(leapcell_req_made * leapcell_req_succeeded / 100) >= 90") parameters = make(map[string]interface{}) parameters["leapcell_req_made"] = 100 parameters["leapcell_req_succeeded"] = 80 result, _ = expr.Evaluate(parameters) fmt.Println(result) expr, _ = govaluate.NewEvaluableExpression("(mem_used / total_mem) * 100") parameters = make(map[string]interface{}) parameters["total_mem"] = 1024 parameters["mem_used"] = 512 result, _ = expr.Evaluate(parameters) fmt.Println(result) }
첫 번째 표현식에서 foo > 0
의 결과를 계산하려고 합니다. 매개변수를 전달할 때 foo
를 -1로 설정하고 최종 출력은 false
입니다.
두 번째 표현식에서는 (leapcell_req_made * leapcell_req_succeeded / 100) >= 90
의 값을 계산하려고 합니다. 매개변수에서 leapcell_req_made
를 100으로, leapcell_req_succeeded
를 80으로 설정하면 결과는 true
입니다.
위의 두 표현식은 모두 부울 결과를 반환하고 세 번째 표현식은 부동 소수점 숫자를 반환합니다. (mem_used / total_mem) * 100
은 전달된 총 메모리 total_mem
과 현재 사용된 메모리 mem_used
에 따라 메모리 사용률을 반환하며 결과는 50입니다.
명명
govaluate 사용은 Go 코드를 직접 작성하는 것과 다릅니다. Go 코드에서 식별자는 -
, +
, $
등의 기호를 포함할 수 없습니다. 그러나 govaluate는 이스케이프를 통해 이러한 기호를 사용할 수 있으며 이스케이프 방법에는 두 가지가 있습니다.
- 이름을
[
및]
로 묶습니다. 예를 들어[leapcell_resp-time]
입니다. \
를 사용하여 바로 다음에 오는 문자를 이스케이프합니다.
예를 들어:
func main() { expr, _ := govaluate.NewEvaluableExpression("[leapcell_resp-time] < 100") parameters := make(map[string]interface{}) parameters["leapcell_resp-time"] = 80 result, _ := expr.Evaluate(parameters) fmt.Println(result) expr, _ = govaluate.NewEvaluableExpression("leapcell_resp\\-time < 100") parameters = make(map[string]interface{}) parameters["leapcell_resp-time"] = 80 result, _ = expr.Evaluate(parameters) fmt.Println(result) }
\
자체를 문자열에서 이스케이프해야 하므로 두 번째 표현식에서는 \\
를 사용해야 합니다. 또는 다음을 사용할 수 있습니다.
`leapcell_resp\-time` < 100