swag
swaggo/swag 는 Go 언어에서 Swagger API 2.0 을 구현한 것으로 지정된 형식의 주석을 작성하면 swagger.json 과 swagger.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/swagTIP
문제가 발생하지 않도록 두 버전은 반드시 일치해야 합니다.
그런 다음 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 를 새로 만든 다음 다음 내용을 작성합니다.
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 initmain.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예제는 다음과 같습니다.
@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 에서 스캔할 수 있다면 자신의 타입일 수도 있습니다.
응답
인터페이스 응답 정의 기본 형식은 다음과 같습니다.
// @Success 200 {array} model.Account
// @Failure 400 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPError상태 코드, 기본 타입, 데이터 타입으로 구성됩니다. {array} 는 배열임을 나타내며 데이터 타입의 배열 형식으로 표시됩니다. {object} 는 데이터 타입의 원래 형식으로 표시됩니다. 예를 들어 일반적으로 통일된 응답 본문을 정의합니다.
type JSONResult struct {
Code int `json:"code" `
Message string `json:"message"`
Data interface{} `json:"data"`
}Data 필드의 타입은 불확실하므로 응답 예제를 설명할 때 다음과 같이 조합할 수 있습니다.
// 조합
@success 200 {object} jsonresult.JSONResult{data=Account} "desc"
// 배열
@success 200 {object} jsonresult.JSONResult{data=[]Account} "desc"모델
구조체 필드에 주석을 추가하면 swagger 가 모델 필드 주석으로 스캔합니다.
package model
type Account struct {
// account id
ID int `json:"id" example:"1"`
// username
Name string `json:"name" example:"account name"`
}그중 example 태그의 값은 페이지에 표시될 예제 값으로 사용되며 물론 필드 제한도 지원합니다.
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 필드에 저장된다면 다음과 같이 정의할 수 있습니다.
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
본질적으로 이는 apikey 일 뿐이며 bearer token 을 전송하는 경우 수동으로 Bearer 접두사를 추가해야 합니다.

그런 다음 인증이 필요한 인터페이스에 다음 주석을 추가합니다.
// @security Bearer이 값은 securityDefinitions 정의의 이름입니다.
구성
swag 는 실제로 여러 개의 다른 swagger 인스턴스를 map 에 저장하며 ginSwagger 는 어댑터로 인스턴스에서 doc.json 즉 API 인터페이스 정의 파일을 읽고 swaggerFiles 는 웹 페이지 표시를 위한 정적 HTML 파일을 제공하며 API 정의를 구문 분석하여 인터페이스를 생성합니다. 전체 프로세스를 이해한 후 사용자 정의 작업을 수행할 수 있습니다.
// Name 은 swag 인스턴스를 등록하는 데 사용되는 고유한 이름입니다.
// 기본 인스턴스 이름
const Name = "swagger"
var (
swaggerMu sync.RWMutex
// 인스턴스 테이블
swags map[string]Swagger
)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 의 일부 코드입니다.
// 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 파일에 임베드하여 자동으로 코드를 생성할 수 있습니다.
// 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.go 나 main 함수에 작성하는 것을 좋아하지 않으며 이러한 주석을 go generate 위에 작성하는 것이 가장 적합합니다.
TIP
여러 인스턴스가 필요한 경우 인스턴스 이름이 고유해야 하며 그렇지 않으면 panic 이 발생합니다.
일부 구성을 커스터마이즈하려면 ginSwagger.CustomWrapHandler 를 사용해야 하며 이는 전자보다 Config 파라미터가 하나 더 많으며 의미는 다음과 같습니다.
// 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 를 대체하며 여러 인스턴스일 때 특히 그렇습니다.
engine.GET(openapi.ApiDoc, ginSwagger.CustomWrapHandler(openapi.Config, swaggerFiles.NewHandler()))이 외에도 타입 재작성 등 일련의 작업을 수행할 수 있으며 모두 비교적 간단합니다. 더 많은 내용은 공식 문서를 참고하시기 바랍니다.
주의 사항
swag 는 주석을 기반으로 openapi 인터페이스 설명 파일을 생성하며 생성 시 지정된 디렉토리에는 인터페이스 문서의 기본 정보가 포함되어야 하며 기본적으로
main.go에서 찾습니다.swag init는 기본적으로 현재 디렉토리를 지정하며 값은./입니다.swag init -d를 사용하여 여러 디렉토리를 지정할 수 있으며 쉼표로 구분합니다. 첫 번째로 지정된 디렉토리에는 인터페이스 문서의 기본 정보가 포함되어야 합니다. 예를 들어swag init -d ./,./api-g, 인터페이스 문서의 기본 정보 저장 파일은 사용자 정의 파일명으로 지정할 수 있으며 기본값은main.go입니다. 문서 생성 시-g파라미터를 사용하여 파일명을 지정합니다.swag init -g api.go -d ./,./api이 명령은
./api.go에서 인터페이스 문서의 기본 정보를 구문 분석하고 동시에./와./api두 디렉토리에서 다른 인터페이스의 주석 정보를 찾아 구문 분석하여 해당 문서를 생성한다는 의미입니다.-o파라미터는 문서 설명 파일의 출력 경로를 지정할 수 있으며 기본값은./docs입니다. 예:swag init -o ./api/docs--ot는 출력 파일 타입을 지정할 수 있으며 기본값은 (docs.go,swagger.json,swagger.yaml) 입니다. swagger ui 를 Go 프로그램으로 로드하려면 Go 파일이 필수적입니다.swag init --ot go,yaml나머지 생성된 json 과 yaml 파일은 다른 인터페이스 관리 소프트웨어에서 데이터를 가져오는 데 편리합니다.
주석을 어디에 작성하든 상관없으며 함수에 작성하지 않아도 구문 분석할 수 있지만 함수에 작성하는 것이 가독성이 좋을 뿐입니다. 본질적으로 주석 형식과 Go 소스 코드가 함께 있는 DSL 일 뿐입니다.
swag 는 많은 다른 파라미터를 지원하며
swag init -h를 사용하여 확인할 수 있습니다.
