don't stop believing

[swag] Gin에서 Swagger 사용하기 본문

Golang/Gin

[swag] Gin에서 Swagger 사용하기

Tongchun 2019. 3. 4. 18:44

API 서버를 만드는데있어 문서화는 필수입니다.

Gin에서 Swagger를 사용하는 걸 확인해 보겠습니다.

https://github.com/swaggo/gin-swagger


먼저 Go에서 swag를 사용할 수 있게 source code를 다운로드 합니다.

1
$ go get -u github.com/swaggo/swag/cmd/swag
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

swag가 잘 다운로드 돼었는지 확인해 봅니다.

1
2
3
4
5
$ ll $GOPATH/src/github.com/swaggo
total 0
drwxr-xr-x 3 tongchunkim staff 96B 3 4 17:58 .
drwxr-xr-x 30 tongchunkim staff 960B 3 4 17:58 ..
drwxr-xr-x 34 tongchunkim staff 1.1K 3 4 17:58 swag
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이제 프로젝트 폴더를 하나 만들고 swag init을 해줍니다.

저는 testSwagger라고 폴더를 만들었습니다. 그리고 그 안에 swag init 을 했습니다.

1
2
3
4
5
$ mkdir testSwagger && cd testSwagger
$ swag init
2019/03/04 18:10:47 Generate swagger docs....
2019/03/04 18:10:47 Generate general API Info
2019/03/04 18:10:47 create docs.go at docs/docs.go
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

swag init을 하면 docs 폴더와 그 하외에 swagger 파일이 생성됩니다.


이제 main package를 만들어 봅니다.

저는 server.go로 만들고 이전에 사용했던 Parameters in path 예제를 그대로 가져다 사용해 만들었습니다. (조금 추가/수정했습니다.)

https://dejavuqa.tistory.com/320?category=320633

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main
import (
"github.com/gin-gonic/gin"
)
type welcomeModel struct {
ID int `json:"id" example:"1" format:"int64"`
Name string `json:"name" example:"account name"`
}
func setupRouter() *gin.Engine {
r := gin.Default()
r.GET("/welcome/:name", welcomePathParam)
return r
}
func welcomePathParam(c *gin.Context) {
name := c.Param("name")
message := name + " is very handsome!"
welcomeMessage := welcomeModel{1, message}
c.JSON(200, gin.H{"message": welcomeMessage})
}
func main() {
r := setupRouter()
r.Run() // Listen and Server in 0.0.0.0:8080
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이제 swagger 연동을 시작하겠습니다.

gin-swagger를 다운로드 합니다.

1
2
$ go get -u github.com/swaggo/gin-swagger
$ go get -u github.com/swaggo/gin-swagger/swaggerFiles
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

다운로드한 package를 server.go의 import에 추가합니다.

그리고 swag init으로 생성된 docs package도 추가해 줍니다. docs package를 추가하려면 GOPATH의 src 하위 경로로 잡아주면 됩니다.


import (

"github.com/gin-gonic/gin"


ginSwagger "github.com/swaggo/gin-swagger"

swaggerFiles "github.com/swaggo/gin-swagger/swaggerFiles"

"github.com/dejavuwing/testSwagger/docs"

)

그리고 setupRouter() 함수에 swagger route를 추가해 줍니다.

r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

이제 welcomePathParma() 함수 위에 annotation을 아래와 같이 추가합니다.

// Welcome godoc

// @Summary Summary를 적어 줍니다.

// @Description 자세한 설명은 이곳에 적습니다.

// @name get-string-by-int

// @Accept  json

// @Produce  json

// @Param name path string true "User name"

// @Router /welcome/{name} [get]

// @Success 200 {object} welcomeModel 

위에서 설명한 코드를 추가한 전체 코드는 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package main
import (
"github.com/gin-gonic/gin"
"github.com/dejavuwing/testSwagger/docs"
ginSwagger "github.com/swaggo/gin-swagger"
swaggerFiles "github.com/swaggo/gin-swagger/swaggerFiles"
)
type welcomeModel struct {
ID int `json:"id" example:"1" format:"int64"`
Name string `json:"name" example:"account name"`
}
func setupRouter() *gin.Engine {
r := gin.Default()
r.GET("/welcome/:name", welcomePathParam)
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
return r
}
// Welcome godoc
// @Summary Summary .
// @Description .
// @name get-string-by-int
// @Accept json
// @Produce json
// @Param name path string true "User name"
// @Router /welcome/{name} [get]
// @Success 200 {object} welcomeModel
func welcomePathParam(c *gin.Context) {
name := c.Param("name")
message := name + " is very handsome!"
welcomeMessage := welcomeModel{1, message}
// welcomeMessage := model.User{ID: 1, Name: name}
c.JSON(200, gin.H{"message": welcomeMessage})
}
func main() {
// programatically set swagger info
docs.SwaggerInfo.Title = "Swagger Example API"
docs.SwaggerInfo.Description = "This is a sample server for Swagger."
docs.SwaggerInfo.Version = "1.0"
docs.SwaggerInfo.Host = "petstore.swagger.io"
docs.SwaggerInfo.BasePath = "/v2"
r := setupRouter()
r.Run() // Listen and Server in 0.0.0.0:8080
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

swagger로 변환하기 위해 swag init을 한번 더 해줍니다.

annotation으로된 swagger 문서가 변경되거나 할 경우 swag init을 다시 해줘야 합니다.

1
2
3
4
$ swag init
2019/03/05 17:52:14 Generate swagger docs....
2019/03/05 17:52:14 Generate general API Info
2019/03/05 17:52:14 create docs.go at docs/docs.go
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이제 server.go를 실행해 봅니다.

1
2
3
4
5
6
7
8
9
10
11
$ go run server.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /welcome/:name --> main.welcomePathParam (3 handlers)
[GIN-debug] GET /swagger/*any --> github.com/swaggo/gin-swagger.WrapHandler.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

만약 server.go를 실행했을때 아래와 같은 메시지가 나온다면 github.com/alecthomas/template package를 다운받아야 합니다.

1
2
3
4
$ go run server.go
docs/docs.go:10:2: cannot find package "github.com/alecthomas/template" in any of:
/usr/local/opt/go/libexec/src/github.com/alecthomas/template (from $GOROOT)
/Users/tongchunkim/go/src/github.com/alecthomas/template (from $GOPATH)
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

swag init으로 생성된 docs에서 github.com/alecthomas/template를 사용하고 있습니다.

1
$ go get -u github.com/alecthomas/template
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

정상적으로 server.go가 실행되었다면 아래 경로로 swagger를 호출해 봅니다.

http://localhost:8080/swagger/index.html


그런데 Errors 메시지가 나오네요.

우선 GET /welcome/{name}을 클릭해 봅니다.

annotation으로 작성한 문서가 잘 보이네요.


Errors 메세지를 확인해 보겠습니다.

struct로 작성된 welcomeModel이 문제인것 같습니다.

이걸 해결하기 위해 Web Server (server.go)의 구조를 변경하겠습니다. Model을 별도의 package로 만들어 주겠습니다.


testSwagger폴더 아래 model 이란 폴더를 만들어 주고 그 안에 user.go를 만들어 줍니다.

tree 명령으로 확인하면 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
$ tree
.
├── docs
·· ├── docs.go
·· └── swagger
·· ├── swagger.json
·· └── swagger.yaml
├── model
·· └── user.go
└── server.go
3 directories, 5 files
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

user.go 파일안에 user 데이터를 담을 수 있는 struct를 만들어 줍니다.

package 명은 model로 해줍니다.

1
2
3
4
5
6
package model
type User struct {
ID int `json:"id" example:"1"`
Name string `json:"name" example:"user name"`
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이제 server.go에서 model package를 import하고 model.User에 데이터를 담아 json 형태로 response 해 줍니다.

그리고 swagger annotation 중 @Success 부분을 아래와 같이 변경합니다.

// @Success 200 {object} model.User

수정한 server.go 코드는 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main
import (
"github.com/gin-gonic/gin"
"github.com/dejavuwing/testSwagger/docs"
"github.com/dejavuwing/testSwagger/model"
ginSwagger "github.com/swaggo/gin-swagger"
swaggerFiles "github.com/swaggo/gin-swagger/swaggerFiles"
)
func setupRouter() *gin.Engine {
r := gin.Default()
r.GET("/welcome/:name", welcomePathParam)
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
return r
}
// Welcome godoc
// @Summary Summary .
// @Description .
// @name get-string-by-int
// @Accept json
// @Produce json
// @Param name path string true "User name"
// @Router /welcome/{name} [get]
// @Success 200 {object} model.User
func welcomePathParam(c *gin.Context) {
name := c.Param("name")
welcomeMessage := model.User{ID: 1, Name: name}
c.JSON(200, gin.H{"message": welcomeMessage})
}
func main() {
// programatically set swagger info
docs.SwaggerInfo.Title = "Swagger Example API"
docs.SwaggerInfo.Description = "This is a sample server for Swagger."
docs.SwaggerInfo.Version = "1.0"
docs.SwaggerInfo.Host = "petstore.swagger.io"
docs.SwaggerInfo.BasePath = "/v2"
r := setupRouter()
r.Run() // Listen and Server in 0.0.0.0:8080
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

코드에 수정이 있으니 다시 swag init을 해줍니다.

1
2
3
4
5
$ swag init
2019/03/05 18:23:27 Generate swagger docs....
2019/03/05 18:23:27 Generate general API Info
2019/03/05 18:23:27 Generating model.User
2019/03/05 18:23:27 create docs.go at docs/docs.go
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이전에는 없었던 model.User도 Generating되는게 보입니다.

 

server.go를 실행해 줍니다.

1
2
3
4
5
6
7
8
9
10
11
$ go run server.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /welcome/:name --> main.welcomePathParam (3 handlers)
[GIN-debug] GET /swagger/*any --> github.com/swaggo/gin-swagger.WrapHandler.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

swagger를 다시 실행해 보겠습니다.

Errors 메시지가 없어지고 Models package의 model.User struct가 새로 보입니다.

여기까지가 golang (gin)에서 swagger를 사용하는 방법이었습니다.

세부적인 annotation은 아래 설명 페이지나 example 에서 확인할 수 있습니다.


https://github.com/swaggo/swag#api-operation

https://github.com/swaggo/swag/tree/master/example


'Golang > Gin' 카테고리의 다른 글

Model binding and validation  (0) 2019.03.06
mysql curd (with Gin)  (0) 2019.03.06
Grouping routes  (0) 2019.03.04
Upload files  (0) 2019.02.27
Map as querystring or postform parameters  (0) 2019.02.27
Comments