MSA 기반 프로젝트의 SCG(Spring Cloud GateWay)에서 micro-service의 Swagger (OpenAPI) 통합 구현 연동
Registry Application (Eureka Server)
Client Applications
Spring cloud Gateway
ext {
    set('springCloudVersion', "2020.0.3")
}
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'io.projectreactor:reactor-test'
}
dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}
spring.application.name: registry
server.port: 8761
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
*Eureka clients will look for port 8761 by default.
@EnableEurekaServer
@SpringBootApplication
public class RegistryAppApplication {
    public static void main(String[] args) {
        SpringApplication.run(RegistryAppApplication.class, args);
    }
}
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
    ... 이하 생략
}
spring:
  application:
    name: board
    
    ...이하 생략
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
    implementation 'org.springframework.cloud:spring-cloud-starter-sleuth'
    implementation 'org.springdoc:springdoc-openapi-webflux-ui:1.5.10'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'io.projectreactor:reactor-test'
}
spring.application:
  name: gateway
spring.output:
  ansi.enabled: always # colorize console logs
logging:
  file:
    name: ./logs/${spring.application.name}.log
  logback.rollingpolicy.max-history: 7
  level:
    root: INFO
server:
  port: 8080
  shutdown: graceful
#  forward-headers-strategy: framework # 
#  # use-forward-headers: true
#  error.whitelabel.enabled: false
#
##springdoc.show-actuator: true
#
management:
  endpoints:
    web:
      exposure:
        include:
          - "gateway"
  endpoint:
    gateway:
      enabled: true
    health:
      show-details: always
#springdoc.api-docs.path : /gw
spring.cloud.gateway:
  default-filters:
    - name: GlobalFilter
      args:
        baseMessage: Spring Cloud Gateway GlobalFilter
        preLogger: true
        postLogger: true
  routes:
    - id: openapi
      uri: http://localhost:${server.port}
      predicates:
        - Path=/v3/api-docs/**
      filters:
        - RewritePath=/v3/api-docs/(?<path>.*), /$\{path}/v3/api-docs
    - id: board-service
      uri: lb://board
      predicates:
        - Path=/board/**
      filters:
        - RewritePath=/board/(?<path>.*), /$\{path}
    - id: board2-service
      uri: lb://board2
      predicates:
        - Path=/board2/**
      filters:
        - RewritePath=/board2/(?<path>.*), /$\{path}
-service suffix is given to separate micro service routes and open-api route.
Path and Rewrite-path may defer by given environment.
For some reason, setting forward-headers-strategy: framework breaks swagger. Not setting at all has not given such side-effect so far.
default-filters is optional setting.
To enable swagger for gateway itself, you can simply add route with application name. Perhaps you could use variable as below.
- id: ${spring.application.name}-service
  uri: lb://${spring.application.name}
  predicates:
  - Path=/${spring.application.name}/**
  filters:
  - RewritePath=/${spring.application.name}/(?<path>.*), /$\{path}
@Configuration
public class SwaggerConfig {
    @Bean
    public CommandLineRunner openApiGroups(
            RouteDefinitionLocator locator,
            SwaggerUiConfigParameters swaggerUiParameters) {
        return args -> locator
                .getRouteDefinitions().collectList().block()
                .stream()
                .map(RouteDefinition::getId)
                .filter(id -> id.matches(".*-service"))
                .map(id -> id.replace("-service", ""))
                .forEach(swaggerUiParameters::addGroup);
    }
}
This CommandLineRunner will be ran once after application context has loaded and started.
I added open-api route to application.yml to rewrite open-api request into corrent form.
RouterDefinitionLocater gives list of routes we'd like to add, filters non-service route, removes -service suffix, and finally add groups to SwaggerUiConfigParameters.
localhost:8080/swagger-ui.html

API call test

The way of adding groups to openApi might differ in later releases. Please refer this post is using version 1.5.
If on version 1.2 or below, see this document.
Get more information from official document
Examples for using GroupedOpenApi here.
Original reference here.
Simplified my code while keeping concepts.
spring:
  application:
    name: board
    
springdoc.api-docs.path : /openapi/${spring.application.name}
...이하 생략
...
springdoc.api-docs.path : /openapi
spring.cloud.gateway:
  default-filters:
    - name: GlobalFilter
      args:
        baseMessage: Spring Cloud Gateway GlobalFilter
        preLogger: true
        postLogger: true
  routes:
    - id: ${spring.application.name}
      uri: lb://${spring.application.name}
      predicates:
        - Path=/openapi/${spring.application.name}
      filters:
        - RewritePath=/openapi/${spring.application.name}, /openapi
    - id: sign
      uri: lb://sign
      predicates:
        - Path=/openapi/sign, /api/*/sign/**
    - id: board
      uri: lb://board
      predicates:
        - Path=/openapi/board, /api/*/board/**
  
  ...
    ...
    @Bean
    public CommandLineRunner openApiGroups(RouteDefinitionLocator locator, SwaggerUiConfigParameters swaggerUiParameters) {
        return args -> locator
                .getRouteDefinitions().collectList().block()
                .stream()
                .map(RouteDefinition::getId)
                .forEach(swaggerUiParameters::addGroup);
    }
    ...
Can you share your github repo? I get this error
Failed to load API definition.
Fetch error
Not Found /openapi/gateway/attendance