Skip to content

swag

swaggo/swag 는 Go 언어에서 Swagger API 2.0 을 구현한 것으로 지정된 형식의 주석을 작성하면 swagger.jsonswagger.yaml 타입의 인터페이스 문서를 생성할 수 있어 내보내기와 가져오기가 편리합니다.

저장소: swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)

문서: swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)

swag 가 기본적으로 지원하는 웹 프레임워크는 다음과 같으며 본 문서에서는 gin 을 예제로 하여 gin 과 swagger 를 결합하여 빠르게 인터페이스 문서를 생성하는 예제를 보여줍니다.

TIP

swagger 문법에 익숙하지 않다면 About Swagger Specification | Documentation | Swagger 를 참고하시기 바랍니다.

설치

먼저 swagger 명령줄 도구를 다운로드합니다.

go install github.com/swaggo/swag/cmd/swag@latest

그런 다음 swagger 소스 코드 의존성을 다운로드합니다.

go get github.com/swaggo/swag

TIP

문제가 발생하지 않도록 두 버전은 반드시 일치해야 합니다.

그런 다음 swagger 의 정적 파일 라이브러리를 다운로드합니다. html, css, js 등은 모두 Go 코드에 임베드됩니다.

go get github.com/swaggo/files@latest

마지막으로 swagger 의 gin 어댑터 라이브러리를 다운로드합니다.

go get github.com/swaggo/gin-swagger@latest

본 문서에서는 gin 만 예제로 사용하므로 다른 웹 프레임워크의 어댑터는 직접 확인하시기 바라며 기본적으로 모두 비슷합니다.

사용

go mod 를 사용하여 가장 기본적인 Go 프로젝트를 생성하고 main.go 를 새로 만든 다음 다음 내용을 작성합니다.

go
package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

// @title           Swagger Example API
// @version         1.0
// @description     This is a sample server celler server.

// @contact.name   API Support
// @contact.url    http://www.swagger.io/support
// @contact.email  support@swagger.io

// @BasePath  /api/v1
func main() {
  engine := gin.Default()
  engine.GET("/api/v1/ping", Ping)
  engine.Run(":80")
}

// Ping godoc
// @Summary      say hello world
// @Description  return hello world json format content
// @param       name query    string  true  "name"
// @Tags         system
// @Produce      json
// @Router       /ping [get]
func Ping(ctx *gin.Context) {
  ctx.JSON(200, gin.H{
    "message": fmt.Sprintf("Hello World!%s", ctx.Query("name")),
  })
}

이는 매우 간단한 gin 웹 예제로 main 함수의 주석은 문서의 기본 정보이며 Ping 함수는 일반적인 인터페이스입니다. 다음으로 명령을 실행하여 문서를 생성하며 기본적으로 main.go 와 동일한 디렉토리의 docs 디렉토리에 생성됩니다.

swag init

main.go 코드를 수정합니다.

go
package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    swaggerFiles "github.com/swaggo/files"
    ginSwagger "github.com/swaggo/gin-swagger"
    // 생성된 인터페이스 문서 패키지를 익명으로 가져오기
    _ "golearn/docs"
)

// @title           Swagger Example API
// @version         1.0
// @description     This is a sample server celler server.

// @contact.name   API Support
// @contact.url    http://www.swagger.io/support
// @contact.email  support@swagger.io

// @BasePath  /api/v1
func main() {
    engine := gin.Default()
    // swagger 정적 파일 라우팅 등록
    engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
    engine.GET("/api/v1/ping", Ping)
    engine.Run(":80")
}

// Ping godoc
// @Summary      say hello world
// @Description  return hello world json format content
// @param       name query    string  true  "name"
// @Tags         system
// @Produce      json
// @Router       /ping [get]
func Ping(ctx *gin.Context) {
    ctx.JSON(200, gin.H{
       "message": fmt.Sprintf("Hello World!%s", ctx.Query("name")),
    })
}

프로그램을 실행하고 127.0.0.1/swagger/index.html 에 접속하면 인터페이스는 다음과 같습니다.

이렇게 하면 기본적인 인터페이스 문서가 실행됩니다. 다음으로 몇 가지 특별히 주의해야 할 점을 제외하면 기본적으로 다른 언어로 사용하는 것과 큰 차이가 없습니다.

파라미터

파라미터 정의 형식은 다음과 같습니다.

@param name paramtype datatype isRequired comment

예제는 다음과 같습니다.

go
@param userId query int true "user unique id"

지원되는 파라미터 타입은 다음과 같습니다.

  • query
  • path
  • header
  • body
  • formData

데이터 타입은 다음과 같습니다.

  • string (string)
  • integer (int, uint, uint32, uint64)
  • number (float32)
  • boolean (bool)
  • user defined struct

파라미터 타입은 swagger 에서 스캔할 수 있다면 자신의 타입일 수도 있습니다.

응답

인터페이스 응답 정의 기본 형식은 다음과 같습니다.

go
// @Success      200  {array}   model.Account
// @Failure      400  {object}  httputil.HTTPError
// @Failure      404  {object}  httputil.HTTPError
// @Failure      500  {object}  httputil.HTTPError

상태 코드, 기본 타입, 데이터 타입으로 구성됩니다. {array} 는 배열임을 나타내며 데이터 타입의 배열 형식으로 표시됩니다. {object} 는 데이터 타입의 원래 형식으로 표시됩니다. 예를 들어 일반적으로 통일된 응답 본문을 정의합니다.

go
type JSONResult struct {
    Code    int          `json:"code" `
    Message string       `json:"message"`
    Data    interface{}  `json:"data"`
}

Data 필드의 타입은 불확실하므로 응답 예제를 설명할 때 다음과 같이 조합할 수 있습니다.

go
// 조합
@success 200 {object} jsonresult.JSONResult{data=Account} "desc"

// 배열
@success 200 {object} jsonresult.JSONResult{data=[]Account} "desc"

모델

구조체 필드에 주석을 추가하면 swagger 가 모델 필드 주석으로 스캔합니다.

go
package model

type Account struct {
  // account id
    ID   int    `json:"id" example:"1"`
    // username
    Name string `json:"name" example:"account name"`
}

그중 example 태그의 값은 페이지에 표시될 예제 값으로 사용되며 물론 필드 제한도 지원합니다.

go
type Foo struct {
    Bar string `minLength:"4" maxLength:"16"`
    Baz int `minimum:"10" maximum:"20" default:"15"`
    Qux []string `enums:"foo,bar,baz"`
}

모든 모델은 사용 시 swagger 에서 스캔할 수 있어야 하며 그렇지 않으면 작동하지 않습니다.

인증

인증 부분에서는 다음을 지원합니다.

  • Basic Auth
  • API Key
  • OAuth2 app auth
  • OAuth2 implicit auth
  • OAuth2 password auth
  • OAuth2 access code auth

만약 인터페이스 인증에 JWT 를 사용하며 header 의 Authorization 필드에 저장된다면 다음과 같이 정의할 수 있습니다.

go
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization

본질적으로 이는 apikey 일 뿐이며 bearer token 을 전송하는 경우 수동으로 Bearer 접두사를 추가해야 합니다.

그런 다음 인증이 필요한 인터페이스에 다음 주석을 추가합니다.

go
// @security Bearer

이 값은 securityDefinitions 정의의 이름입니다.

구성

swag 는 실제로 여러 개의 다른 swagger 인스턴스를 map 에 저장하며 ginSwagger 는 어댑터로 인스턴스에서 doc.json 즉 API 인터페이스 정의 파일을 읽고 swaggerFiles 는 웹 페이지 표시를 위한 정적 HTML 파일을 제공하며 API 정의를 구문 분석하여 인터페이스를 생성합니다. 전체 프로세스를 이해한 후 사용자 정의 작업을 수행할 수 있습니다.

go
// Name 은 swag 인스턴스를 등록하는 데 사용되는 고유한 이름입니다.
// 기본 인스턴스 이름
const Name = "swagger"

var (
  swaggerMu sync.RWMutex
    // 인스턴스 테이블
  swags     map[string]Swagger
)
go
func CustomWrapHandler(config *Config, handler *webdav.Handler) gin.HandlerFunc {
    var once sync.Once

    if config.InstanceName == "" {
       config.InstanceName = swag.Name
    }

    if config.Title == "" {
       config.Title = "Swagger UI"
    }

    // 이름으로 템플릿 생성
    index, _ := template.New("swagger_index.html").Parse(swaggerIndexTpl)

    var matcher = regexp.MustCompile(`(.*)(index\.html|doc\.json|favicon-16x16\.png|favicon-32x32\.png|/oauth2-redirect\.html|swagger-ui\.css|swagger-ui\.css\.map|swagger-ui\.js|swagger-ui\.js\.map|swagger-ui-bundle\.js|swagger-ui-bundle\.js\.map|swagger-ui-standalone-preset\.js|swagger-ui-standalone-preset\.js\.map)[?|.]*`)

    return func(ctx *gin.Context) {
       if ctx.Request.Method != http.MethodGet {
          ctx.AbortWithStatus(http.StatusMethodNotAllowed)

          return
       }

       // 라우팅 매칭
       matches := matcher.FindStringSubmatch(ctx.Request.RequestURI)

       if len(matches) != 3 {
          ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))

          return
       }

       path := matches[2]
       once.Do(func() {
          handler.Prefix = matches[1]
       })

       switch filepath.Ext(path) {
       case ".html":
          ctx.Header("Content-Type", "text/html; charset=utf-8")
       case ".css":
          ctx.Header("Content-Type", "text/css; charset=utf-8")
       case ".js":
          ctx.Header("Content-Type", "application/javascript")
       case ".png":
          ctx.Header("Content-Type", "image/png")
       case ".json":
          ctx.Header("Content-Type", "application/json; charset=utf-8")
       }

       switch path {
       // 메인 페이지
       case "index.html":
          _ = index.Execute(ctx.Writer, config.toSwaggerConfig())
       // API 설명 파일
       case "doc.json":
          doc, err := swag.ReadDoc(config.InstanceName)
          if err != nil {
             ctx.AbortWithStatus(http.StatusInternalServerError)

             return
          }

          ctx.String(http.StatusOK, doc)
       default:
          handler.ServeHTTP(ctx.Writer, ctx.Request)
       }
    }
}

생성된 Go 코드를 통해 인스턴스 등록을 자동으로 완료하며 아래는 자동으로 생성된 docs.go 의 일부 코드입니다.

go
// SwaggerInfo 는 클라이언트가 수정할 수 있도록 내보낸 Swagger Info 를 보유합니다.
var SwaggerInfo = &swag.Spec{
  Version:          "",
  Host:             "",
  BasePath:         "",
  Schemes:          []string{},
  Title:            "",
  Description:      "",
  InfoInstanceName: "swagger",
  SwaggerTemplate:  docTemplate,
  LeftDelim:        "{{",
  RightDelim:       "}}",
}

func init() {
    // 인스턴스 등록
  swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}

init 함수에 현재 인스턴스를 등록하는 Register 함수가 있음을 알 수 있습니다. 인스턴스 이름을 수정하려면 이 파일을 편집하는 것을 권장하지 않습니다. docs.go 파일은 자동으로 생성되므로 코드를 생성할 때 --instanceName appapi 파라미터를 사용하면 됩니다. 편의를 위해 go generate 명령을 Go 파일에 임베드하여 자동으로 코드를 생성할 수 있습니다.

go
// swagger declarative api comment

// @title App Internal API Documentation
// @version v1.0.0
// @description Wilson api documentation
// @BasePath /api/v1
//go:generate swag init --generatedTime --instanceName appapi -g api.go -d ./ --output ./swagger

개인적으로 swagger 의 일반 정보 주석을 main.gomain 함수에 작성하는 것을 좋아하지 않으며 이러한 주석을 go generate 위에 작성하는 것이 가장 적합합니다.

TIP

여러 인스턴스가 필요한 경우 인스턴스 이름이 고유해야 하며 그렇지 않으면 panic 이 발생합니다.

일부 구성을 커스터마이즈하려면 ginSwagger.CustomWrapHandler 를 사용해야 하며 이는 전자보다 Config 파라미터가 하나 더 많으며 의미는 다음과 같습니다.

go
// Config 는 ginSwagger 구성 변수를 저장합니다.
type Config struct {
  // API 정의 (일반적으로 swagger.json 또는 swagger.yaml) 를 가리키는 url. 기본값은 `doc.json`.
  URL                      string
    // 인터페이스 목록展开 상태
  DocExpansion             string
    // 인스턴스 이름
  InstanceName             string
    // 제목
  Title                    string
    // 展开 깊이
  DefaultModelsExpandDepth int
    // 말 그대로
  DeepLinking              bool
  PersistAuthorization     bool
  Oauth2DefaultClientID    string
}

swaggerFiles.NewHandler() 를 사용하여 기본 Handler 를 대체하며 여러 인스턴스일 때 특히 그렇습니다.

go
engine.GET(openapi.ApiDoc, ginSwagger.CustomWrapHandler(openapi.Config, swaggerFiles.NewHandler()))

이 외에도 타입 재작성 등 일련의 작업을 수행할 수 있으며 모두 비교적 간단합니다. 더 많은 내용은 공식 문서를 참고하시기 바랍니다.

주의 사항

  1. swag 는 주석을 기반으로 openapi 인터페이스 설명 파일을 생성하며 생성 시 지정된 디렉토리에는 인터페이스 문서의 기본 정보가 포함되어야 하며 기본적으로 main.go 에서 찾습니다.

  2. swag init 는 기본적으로 현재 디렉토리를 지정하며 값은 ./입니다. swag init -d 를 사용하여 여러 디렉토리를 지정할 수 있으며 쉼표로 구분합니다. 첫 번째로 지정된 디렉토리에는 인터페이스 문서의 기본 정보가 포함되어야 합니다. 예를 들어

    swag init -d ./,./api
  3. -g, 인터페이스 문서의 기본 정보 저장 파일은 사용자 정의 파일명으로 지정할 수 있으며 기본값은 main.go입니다. 문서 생성 시 -g 파라미터를 사용하여 파일명을 지정합니다.

    swag init -g api.go -d ./,./api

    이 명령은 ./api.go 에서 인터페이스 문서의 기본 정보를 구문 분석하고 동시에 ././api 두 디렉토리에서 다른 인터페이스의 주석 정보를 찾아 구문 분석하여 해당 문서를 생성한다는 의미입니다.

  4. -o 파라미터는 문서 설명 파일의 출력 경로를 지정할 수 있으며 기본값은 ./docs입니다. 예:

    swag init -o ./api/docs
  5. --ot 는 출력 파일 타입을 지정할 수 있으며 기본값은 (docs.go,swagger.json,swagger.yaml) 입니다. swagger ui 를 Go 프로그램으로 로드하려면 Go 파일이 필수적입니다.

    swag init --ot go,yaml

    나머지 생성된 json 과 yaml 파일은 다른 인터페이스 관리 소프트웨어에서 데이터를 가져오는 데 편리합니다.

  6. 주석을 어디에 작성하든 상관없으며 함수에 작성하지 않아도 구문 분석할 수 있지만 함수에 작성하는 것이 가독성이 좋을 뿐입니다. 본질적으로 주석 형식과 Go 소스 코드가 함께 있는 DSL 일 뿐입니다.

  7. swag 는 많은 다른 파라미터를 지원하며 swag init -h 를 사용하여 확인할 수 있습니다.

Golang by www.golangdev.cn edit