Huma, Go REST API Framework

Huma, Go REST API Framework

Created
Apr 18, 2024 06:50 AM
Tags
go
Huma는 Go로 된, REST/RPC API 프레임워크로 OpenAPI를 지원한다. 간단하게 서버 코드를 작성해도 API 문서가 생성되어 매우 간편하다.
 
 
Huma는 “쥬마” 라고 읽으며, router-agnostic 하다.
  • bunrouter
  • gin
  • net/http
  • fiber
  • echo
  • chi
  • gorilla/mux
등 여러 라이브러리를 지원한다.
 
Huma를 쓰면 좋은 점, API 문서를 비교적 간단하게 자동 생성할 수 있다.
보통 Go에서 Swagger 지원을 위해 swag 라이브러리를 많이 사용하는데, 주석 기반으로 문서를 생성해 주기 때문에, 개인적으로는 가독성이 상당히 떨어진다고 생각한다.
 

swag을 이용한 주석 기반의 API 문서화

// @title Swagger Example API // @version 1.0 // @description This is a sample server celler server. // @termsOfService http://swagger.io/terms/ // @contact.name API Support // @contact.url http://www.swagger.io/support // @contact.email support@swagger.io // @license.name Apache 2.0 // @license.url http://www.apache.org/licenses/LICENSE-2.0.html // @host localhost:8080 // @BasePath /api/v1 // @securityDefinitions.basic BasicAuth // @externalDocs.description OpenAPI // @externalDocs.url https://swagger.io/resources/open-api/ func main() { r := gin.Default() c := controller.NewController() v1 := r.Group("/api/v1") { accounts := v1.Group("/accounts") { accounts.GET(":id", c.ShowAccount) accounts.GET("", c.ListAccounts) accounts.POST("", c.AddAccount) accounts.DELETE(":id", c.DeleteAccount) accounts.PATCH(":id", c.UpdateAccount) accounts.POST(":id/images", c.UploadAccountImage) } //... } r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) r.Run(":8080") }
  • 코드에서 보다시피 주석은 모두 회색이므로, 가독성이 떨어진다.
  • typo가 발생하더라도 실제 문서 생성 전까지는 눈에 잘 안 띈다는 단점도 있다.
  • 주석부가 코드부만큼 길어지기 때문에, 본말 전도라는 생각도 든다.
 

Huma로 만드는 간단한 서버

 
package main import ( "context" "fmt" "net/http" "github.com/danielgtaylor/huma/v2" "github.com/danielgtaylor/huma/v2/adapters/humago" _ "github.com/danielgtaylor/huma/v2/formats/cbor" ) type Greeting struct { Body struct { Message string `json:"message"` } } func main() { router := http.NewServeMux() api := humago.New(router, huma.DefaultConfig("Greeting API", "v1.0.0")) huma.Get(api, "/greeting/{name}", func(ctx context.Context, i *struct { Name string `path:"name" maxLength:"30" example:"leon" doc:"Name to greet"` }) (*Greeting, error) { resp := &Greeting{} resp.Body.Message = "Hello, " + i.Name + "!" return resp, nil }) fmt.Println("Listening on http://localhost:8080") http.ListenAndServe(":8080", router) }
이렇게 코드를 작성하면 간단한 greeting route를 가지는 서버가 완성된다.
 
go run main.go로 서버를 띄우고 localhost:8080/docs 에 접근해보자.
notion image
 

보다 자세한 문서화 방법

아래 구조체를 통해 Open API 문서화를 위한 설정값을 제공할 수 있다.
huma.Operation{ OperationID: "greeting", Method: http.MethodGet, Path: "/greeting/{name}", Summary: "Greet someone", Description: "This endpoint greets someone by name.", Tags: []string{"greeting"}, }
 
실제 코드는 아래처럼 짜게 된다.
package main import ( "context" "fmt" "net/http" "github.com/danielgtaylor/huma/v2" "github.com/danielgtaylor/huma/v2/adapters/humago" _ "github.com/danielgtaylor/huma/v2/formats/cbor" ) type Greeting struct { Body struct { Message string `json:"message"` } } func main() { router := http.NewServeMux() api := humago.New(router, huma.DefaultConfig("Greeting API", "v1.0.0")) huma.Register(api, huma.Operation{ OperationID: "greeting", Method: http.MethodGet, Path: "/greeting/{name}", Summary: "Greet someone", Description: "This endpoint greets someone by name.", Tags: []string{"greeting"}, }, func(ctx context.Context, i *struct { Name string `path:"name" maxLength:"30" example:"leon" doc:"Name to greet"` }) (*Greeting, error) { resp := &Greeting{} resp.Body.Message = "Hello, " + i.Name + "!" return resp, nil }) fmt.Println("Listening on http://localhost:8080") http.ListenAndServe(":8080", router) }
 
HTTP 서버를 실행하고 localhost:8080/docs에 접근하면 아래와 같은 깔끔한 화면이 제공된다.
notion image
 
현재 Star 수는 1.2k 수준인데, 프로덕션 환경에서 사용할만큼 편리할지는 좀 더 사용해볼 필요가 있을 것 같다.