MSA) Spring Cloud 기반의 MSA 구조에서 Swagger 통합하기 + FastAPI의 Swagger까지

2024. 6. 17. 21:51·SpringBoot

 

 

들어가며

 

저번 시간에 FastAPI 서버를 Spring Cloud Eureka에 Client로 등록하는 방법에 이어서,

이번에는 각 서비스마다 Swagger를 설정하여 이를 한 곳에서 통합하여 볼 수 있게끔 적용하려고 합니다.

 

MSA 구조에서 프로젝트를 진행하면, 각 마이크로서비스들이 각각의 애플리케이션에서 돌아가기 때문에

모놀리식 구조와 다르게 API 문서들을 관리하는데 어려움이 존재합니다.

 

 

쉽게 설명해보자면

모놀리식 구조의 경우 하나의 애플리케이션으로 운영 되므로

애플리케이션에 Swagger를 하나만 띄우면 되지만

 

MSA 구조는 서비스들이 독립적으로 분리되어있기 때문에 서비스마다 Swagger를 띄워줘야합니다.

이러한 상황에서 Swagger를 통합해주지 않는다면,

API를 받아쓰는 프론트 입장에서는 호출하고 싶은 API를 테스트하거나 확인해보고 싶을 때마다

API가 해당하는 서비스쪽의 Swagger로 접속해야하는 번거로움이 존재합니다. 

 

 

그래서 이번에는 각 마이크로서비스에서 설정한 Swagger들을

API Gateway쪽에 통합하여 위 문제들을 해결해보고자 합니다.

 

 

 

백엔드 환경

  • Java 17
  • Spring Boot 3.2.4
  • Python 3.12.3
  • FastAPI 0.111.0

 

 

 

Spring Cloud API Gateway

1.  의존성 추가하기 (gradle)

// swagger
implementation 'org.springdoc:springdoc-openapi-starter-webflux-ui:2.5.0'

 

참고로 Spring Boot 3버전 이상은 springdoc-openapi v2 버전부터 사용해야합니다.

자세한 내용은 아래 공식 문서를 참고해주세요!

https://springdoc.org/

 

 

또한 API Gateway 쪽에는 위처럼 webflux로 의존성을 추가해줍니다.

그 이유가 중요합니다!!

Spring Cloud Gateway는 내부적으로 WebFlux를 사용하기 때문에,

이를 기반으로 하는 애플리케이션은 WebFlux의 특성을 따릅니다.

따라서, WebFlux와 호환되는 라이브러리를 사용해야 합니다.

만약에 WebFlux가 아닌 WebMVC 기반인

implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'로 의존성을 추가하면 오류가 발생합니다.

 

 

WebFlux vs WebMVC

Spring WebMVC는 전통적인 동기 처리 모델에 기반하며, 각 요청마다 하나의 스레드를 할당하는 방식입니다.

Spring WebFlux는 비동기적이고 논블로킹 방식으로 요청을 처리하며, 요청과 스레드가 N:1입니다. 
높은 확장성과 병렬 처리를 필요로 하는 애플리케이션에 적합합니다.

 

 

 

2.  yml 파일 설정하기

springdoc:
  swagger-ui:
    urls[0]:
      name: 회원 서비스
      url: http://localhost:8000/user-service/v3/api-docs
    urls[1]:
      name: 분석 서비스
      url: http://localhost:8000/analysis-service/v3/api-docs
    urls[2]:
      name: 상품 서비스
      url: http://localhost:8000/product-service/v3/api-docs
    urls[3]:
      name: 알림 서비스
      url: http://localhost:8000/notification-service/v3/api-docs

 

위와 같이 작성해주면 나중에 통합된 Swagger에서

아래 사진에 빨간색 박스로 표시한 부분처럼 원하는 서비스의 API 문서를 확인할 수 있습니다.

서비스를 클릭했을 때, 해당 서비스의 api-docs를 불러와서 API 문서를 보여주는 방식입니다.

 

미리보는 완성된 통합본

 

참고로 저는 아래와 같이 rewritepath를 적용하고 있습니다.

      routes:
        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user-service/**
          filters:
            - RewritePath=/user-service/(?<segment>.*), /$\{segment}

 

 

또한 Gateway로 접속했을 때,

바로 Swagger로 접속하고 싶다면 아래와 같이 use-root-path: true로 설정해주면 됩니다.

ex) localhost:8000 -> localhost:8000/webjars/swagger-ui/index.html로 접속됨

 

springdoc:
  swagger-ui:
    use-root-path: true

 

그러나 이러한 방식은 보안상으로 좋은 방법은 아닌 것 같아서 true로 설정하지는 않았습니다. (default가 false)

 

use-root-path: true를 하지 않는다면

localhost:8000으로 접속시 Whitelabel Error Page로 접속되므로

통합된 Swagger를 보고 싶다면 localhost:8000/swagger-ui.html로 접속해줘야 합니다.

 

/swagger-ui.html 경로를 바꾸고 싶다면 아래와 같이 경로를 수정해주면 됩니다.

springdoc:
  swagger-ui:
    path: {경로 설정} -> default가 /swagger-ui.html
    enabled: true

 

 

 

 

Microservice (SpringBoot)

1.  의존성 추가하기 (gradle)

// swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'

 

Gateway에서 설정했던 것처럼 springdoc을 추가해주는데

저의 경우 webmvc로 설정했습니다.

 

 

2.  yml 파일 설정하기

springdoc:
  api-docs:
    version: openapi_3_1
    enabled: true
  enable-spring-security: true
  default-consumes-media-type: application/json
  default-produces-media-type: application/json

 

api-docs 사용 여부와 버전을 설정해주고 Spring Security와 통합된 형태로 API 문서를 제공하며,

기본적으로 JSON 형식의 요청과 응답을 처리하도록 설정해주었습니다.

 

참고로 api-docs의 default 경로는 /v3/api-docs입니다.

그래서 별도의 경로 수정을 하지 않았습니다.

 

 

3. SwaggerConfig 코드 작성

api 요청할 때 개발 서버와 로컬 서버로 나누는 url들을 application.yml에서 관리하였고,

@Value를 통해 값을 가져오는 방식으로 구현했습니다.

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collections;

@Configuration
@RequiredArgsConstructor
@OpenAPIDefinition(
        info = @Info(title = "API Document",
                description = "USER SERVICE 명세서",
                version = "v1.0.0"
        )
)
public class SwaggerConfig {

    @Value("${server.url.development}")
    private String developmentServerUrl;

    @Value("${server.url.local}")
    private String localServerUrl;

    @Bean
    public OpenAPI openAPI() {
        SecurityScheme securityScheme = new SecurityScheme()
                .type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")
                .in(SecurityScheme.In.HEADER).name("Authorization");
        SecurityRequirement securityRequirement = new SecurityRequirement().addList("bearerAuth");

        return new OpenAPI()
                .components(new Components().addSecuritySchemes("bearerAuth", securityScheme))
                .security(Collections.singletonList(securityRequirement))
                .addServersItem(new Server().url(developmentServerUrl).description("개발 서버"))
                .addServersItem(new Server().url(localServerUrl).description("로컬 서버"));
    }

}

 

 

Microservice (FastAPI)

FastAPI는 그냥 뭐 없습니다...!

자체적으로 Swagger 설정이 되어있고, 저는 간단한 작업만 해줬습니다.

 

OpenAPI Specification(OAS) 경로만, 위 API Gateway의 yml 파일에서 설정한 경로인 /v3/api-docs로 변경해주었습니다.

default는 /openapi.json이라고 하는데, 저는 /v3/api-docs로 통일하고 싶어서 수정하려고합니다.

 

api 요청할 때 개발 서버와 로컬 서버로 나누는 url들을 dotenv에서 관리하였고,

필요할 때 코드 상에서 dotenv의 키를 통해 해당하는 값을 불러오도록 구현했습니다.

from fastapi import FastAPI
from contextlib import asynccontextmanager
from dotenv import load_dotenv
import py_eureka_client.eureka_client as eureka_client
import uvicorn
import random
import os

...

app = FastAPI(lifespan=lifespan,
              title = "API Document",
              description = "ANALYSIS SERVICE 명세서",
              version = "v1.0.0",
              openapi_url = "/v3/api-docs",
              servers=[
                  {"url": os.getenv('DEVELOPMENT_SERVER_URL'), "description": "개발 서버"},
                  {"url": os.getenv('LOCAL_SERVER_URL'), "description": "로컬 서버"}
              ]
)

...

 

설정이라고 말하기도 좀 그렇지만

FastAPI 설정은 끝..!

 

 

 

확인

아래 사진과 같이 각 서비스의 Swagger들이 잘 통합된 것을 확인할 수 있습니다!

 

 

'SpringBoot' 카테고리의 다른 글

Spring Boot와 Kafka 테스트: Embedded Kafka로 상품 좋아요 메시지 검증하기  (1) 2024.10.13
MSA) 서비스 별 각 인스턴스에서 애플리케이션을 Docker 컨테이너화 후, 발생한 Eureka Client 간의 통신 문제  (0) 2024.07.09
MSA) Spring Cloud Eureka에 FastAPI 서버를 Client로 등록하기  (0) 2024.06.01
Locust를 통해 부하 테스트를 진행하고 Prometheus/Grafana로 모니터링하기  (0) 2024.03.29
프로메테우스와 그라파나를 사용하여 모니터링 수행하기  (0) 2024.03.28
'SpringBoot' 카테고리의 다른 글
  • Spring Boot와 Kafka 테스트: Embedded Kafka로 상품 좋아요 메시지 검증하기
  • MSA) 서비스 별 각 인스턴스에서 애플리케이션을 Docker 컨테이너화 후, 발생한 Eureka Client 간의 통신 문제
  • MSA) Spring Cloud Eureka에 FastAPI 서버를 Client로 등록하기
  • Locust를 통해 부하 테스트를 진행하고 Prometheus/Grafana로 모니터링하기
개발이조아용
개발이조아용
IT 개발에서 배운 성장의 기록을 작성합니다.
  • 개발이조아용
    계속 하다 보면?!
    개발이조아용
  • 전체
    오늘
    어제
    • 분류 전체보기 (68)
      • Tibero DB (Tmax AI Bigdata .. (7)
      • Git (2)
      • CI CD (2)
      • Redis (3)
      • SpringBoot (16)
      • SQL 문제 풀이 (8)
      • Apache Kafka (8)
        • 오류 해결 (3)
        • 개념 정리 (4)
        • 보안 (1)
      • Nginx (3)
      • SW마에스트로 (3)
      • Kubernetes (4)
      • AWS (5)
      • gRPC (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Redis 개념
    K8S
    DynamoDB 연동
    KAFKA
    Kafka 오류
    leetcode
    SpringBoot
    Tibero
    SQL
    Git
    redis
    Kafka SASL
    redis script
    nginx
    소프트웨어 마에스트로
    MSA
    sql 문제
    SASL 인증
    Kafka 개념
    grpc
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
개발이조아용
MSA) Spring Cloud 기반의 MSA 구조에서 Swagger 통합하기 + FastAPI의 Swagger까지
상단으로

티스토리툴바