본문으로 건너뛰기

Swagger와 OpenAPI 문서 ​

CLI로 OpenAPI 파일을 내보내고, 필요하면 Swaggo 문서와 Swagger UI를 추가해 브라우저에서 요청을 보냅니다. CLI의 정적 OpenAPI 내보내기와 Swaggo를 사용하는 UI 구성은 각각 진행합니다.

첫 번째 앱에서 만든 example.com/hello-spine 프로젝트를 사용합니다. 컨트롤러의 spine generate resource user로 src/user를 먼저 생성하세요. 다른 모듈 경로를 사용했다면 아래 import 경로의 example.com/hello-spine 부분만 해당 모듈 경로로 바꾸고 /docs 접미사는 유지합니다. 명령은 프로젝트 루트에서 실행합니다.

CLI로 OpenAPI 내보내기 ​

CLI는 등록된 경로와 핸들러 시그니처에서 OpenAPI 3.1 문서를 생성합니다. Swaggo 주석은 필요하지 않습니다.

bash
spine doctor
spine contract check
spine contract export --format openapi --output openapi.json

생성된 openapi.json에서 /health와 /user/{id}를 확인합니다. Spine의 /user/:id는 OpenAPI에서 /user/{id}로 표현되며, x-spine-* 필드에는 분석 근거와 한계가 기록됩니다. 예를 들어 실행 중에 선택하는 응답 상태 코드는 정적 분석만으로 확정할 수 없습니다.

이 명령은 문서 파일만 생성합니다. Swagger UI가 필요하면 다음 단계를 진행하세요. 기존 파일을 교체할 때는 내용을 확인한 뒤 --force를 사용합니다.

선택: Swaggo와 Swagger UI ​

Swaggo로 Swagger 2.0 문서를 만들고 Echo 전송 계층에 UI 경로를 추가합니다. 파일과 등록 코드는 직접 작성합니다. CLI의 openapi.json과 Swaggo의 docs/swagger.json은 서로 다른 문서이므로 각각 갱신해야 합니다.

1. 도구 설치 ​

bash
go install github.com/swaggo/swag/cmd/swag@v1.16.6
go get github.com/swaggo/swag@v1.16.6 github.com/swaggo/http-swagger@v1.3.4

2. API 기본 정보 추가 ​

internal/application/application.go의 BuildApplication() 바로 위에 다음 주석을 추가합니다. 생성된 main.go와 spine.toml의 composition = "./internal/application" 설정은 유지합니다.

go
// @title Hello Spine API
// @version 1.0.0
// @description Spine HTTP API
// @host localhost:8080
// @BasePath /
func BuildApplication() (spine.App, boot.Options, error) {
    // ...
}

3. GET 핸들러 문서화 ​

생성된 src/user/user_controller.go를 다음 코드로 교체합니다. UserIDResponse는 응답 스키마를 문서화하기 위해 추가한 타입입니다. 이 핸들러는 경로의 ID를 반환하며, /user/1의 응답 JSON은 기존과 같은 {"id":1}입니다. DB에서 사용자를 조회하지는 않습니다.

go
package user

import (
    "github.com/NARUBROWN/spine/pkg/httpx"
    "github.com/NARUBROWN/spine/pkg/path"
)

type UserController struct{ service *UserService }

func NewUserController(service *UserService) *UserController {
    return &UserController{service: service}
}

type UserIDResponse struct {
    ID int64 `json:"id" example:"1"`
}

// Get godoc
// @Summary Get user ID
// @Tags users
// @Produce json
// @Param id path int true "User ID"
// @Success 200 {object} UserIDResponse
// @Router /user/{id} [get]
func (c *UserController) Get(id path.Int) httpx.Response[UserIDResponse] {
    return httpx.Response[UserIDResponse]{Body: UserIDResponse{ID: id.Value}}
}

4. docs 패키지를 먼저 생성 ​

다음 명령으로 문서 패키지를 생성합니다. 생성 전에는 아직 존재하지 않는 example.com/hello-spine/docs를 import하지 마세요. --parseInternal은 API 기본 정보가 있는 internal 패키지도 읽도록 지정합니다.

bash
swag init -g internal/application/application.go --parseInternal

docs/docs.go, docs/swagger.json, docs/swagger.yaml이 생성됩니다. docs.go의 init 함수가 문서를 등록하므로 다음 단계에서 이 패키지를 부수 효과용 import로 추가합니다. 생성 파일은 직접 수정하지 않습니다.

5. UI 연결 ​

internal/application/application.go의 import 블록에 추가합니다.

go
import (
    _ "example.com/hello-spine/docs"
    "github.com/labstack/echo/v4"
    httpSwagger "github.com/swaggo/http-swagger"
)

같은 파일의 BuildApplication()에서 app := spine.New() 이후, return 이전에 다음 코드를 추가합니다. 기존 health와 user 등록은 유지하세요.

go
app.Transport(func(transport any) {
    e := transport.(*echo.Echo)
    e.GET("/swagger/*", echo.WrapHandler(httpSwagger.WrapHandler))
})

6. 검증하고 실행 ​

bash
gofmt -w internal/application/application.go src/user/user_controller.go
go mod tidy
spine contract check
spine verify
spine dev

브라우저에서 http://localhost:8080/swagger/index.html을 열어 GET 경로가 표시되는지 확인합니다. /swagger/doc.json에서는 생성한 Swagger 2.0 문서가 반환되어야 합니다. UI의 Try it out으로 /user/1에 요청하면 HTTP 200과 {"id":1}이 반환됩니다.

app.Transport로 직접 추가한 Echo 경로는 CLI의 정적 Spine 라우트 계약에 포함되지 않으므로, 계약 검사를 통과해도 UI와 문서 경로는 별도로 확인해야 합니다.

기능 확장에 사용할 주석과 DTO ​

아래 태그와 DTO는 기능을 추가할 때 참고할 예시입니다. resource 생성기는 이 요청과 응답 타입이나 POST, PUT, DELETE 핸들러를 생성하지 않습니다. src/user의 package user에 필요한 타입을 추가하고, 실제 라우트와 응답 상태 코드에 맞게 주석을 작성하세요.

요청 DTO는 핸들러에서 포인터로 받습니다. example, enums, minimum 등의 태그는 문서에 사용할 정보이며 입력 검증을 자동 실행하지 않습니다.

기본 주석 태그 ​

태그설명예시
@Summary한 줄 요약사용자 조회
@Description상세 설명ID로 사용자 정보를 조회합니다
@Tags그룹 태그users
@Router경로와 메서드/user [get]

요청 태그 ​

태그설명예시
@Accept요청 Content-Typejson
@Produce응답 Content-Typejson
@Param파라미터 정의id query int true "User ID"

응답 태그 ​

태그설명예시
@Success성공 응답200 {object} UserResponse
@Failure실패 응답404 {object} ErrorResponse

요청 파라미터 지정 ​

@Param [이름] [위치] [타입] [필수여부] "[설명]"

파라미터 위치 ​

위치설명예시
query쿼리 문자열/user?id=1
pathURL 경로/user/{id}
body요청 본문JSON 객체
header헤더Authorization
formData폼 데이터파일 업로드

타입 ​

타입설명
int, integer정수
string문자열
bool, boolean불리언
number실수
object객체 (DTO)
array배열

예시 ​

go
// 쿼리 파라미터
// @Param id query int true "User ID"
// @Param name query string false "User name"
// @Param active query bool false "Active status"

// 요청 본문
// @Param body body CreateUserRequest true "유저 생성 요청"

// 헤더
// @Param Authorization header string true "Bearer 토큰"

DTO 문서화 ​

요청 DTO ​

go
// src/user/user_request.go
package user

// CreateUserRequest 유저 생성 요청
type CreateUserRequest struct {
    Name  string `json:"name" example:"Alice"`
    Email string `json:"email" example:"alice@example.com"`
}

// UpdateUserRequest 유저 수정 요청
type UpdateUserRequest struct {
    Name  string `json:"name" example:"Alice Updated"`
    Email string `json:"email" example:"alice.new@example.com"`
}

응답 DTO ​

go
// src/user/user_response.go
package user

// UserResponse 유저 응답
type UserResponse struct {
    ID    int    `json:"id" example:"1"`
    Name  string `json:"name" example:"Alice"`
    Email string `json:"email" example:"alice@example.com"`
}

// ErrorResponse 에러 응답
type ErrorResponse struct {
    Message string `json:"message" example:"유저를 찾을 수 없습니다."`
}

DTO 태그 ​

태그설명예시
example예시 값example:"Alice"
enums허용 값 목록enums:"active,inactive"
minimum최소값minimum:"1"
maximum최대값maximum:"100"
default기본값default:"10"

문서 다시 생성하기 ​

주석이나 DTO를 변경하면 Swaggo 문서를 다시 생성해야 합니다. spine dev는 Swaggo를 자동 실행하지 않습니다. 다음 Makefile을 추가하면 make run으로 문서를 갱신한 뒤 앱을 실행할 수 있습니다. CLI의 OpenAPI 파일은 spine contract export로 따로 갱신합니다.

makefile
.PHONY: swagger run

swagger:
	swag init -g internal/application/application.go --parseInternal

run: swagger
	spine dev

다음 단계 ​