Skip to main content

Swagger UI 교체 (Docusaurus OpenAPI)

· 6 min read
Haekyu Cho
Software Engineer

Swagger UI에 대한 내,외부 불만

  • 에이전시 개발사, 인하우스 개발사에서 Swagger UI로 제공중인 문서화 기능에 불만이 많음
  • 현재 외부로 제공중인 api 엔드포인트는 Server API와 Shop API가 있으며 API의 갯수는 약 300개 이상임
  • 마이크로 서버(도메인) 단위로 OAS yaml파일을 생성하며 특정 API를 확인하려면 어떤 도메인에 속해 있는지 사용자가 인지 하고 있어야 함 -> 매우 불편
  • 특히 검색이 안됨 -> 강성 클레임

Web UI

AS-IS : Swagger UI

Shopby API Documentation

shopby-docs

TO-BE : Docusaurus Openapi

https://docusaurus-openapi.tryingpan.dev/

docusaurus-openapi

Docusaurus OpenAPI vs Swagger UI - 주요 장점

통합 문서화

  • API 문서와 가이드, 튜토리얼을 한 곳에서 관리
  • Swagger UI는 API 스펙만 보여주지만, Docusaurus는 사용법, 예제, 개념 설명 등을 함께 제공
  • 개발자가 API 이해부터 실제 구현까지 원스톱으로 학습 가능

더 나은 사용자 경험

  • 검색 기능, 다크모드, 반응형 디자인 등 현대적인 웹 경험
  • 사이드바 네비게이션으로 API 탐색이 더 직관적
  • Swagger UI보다 로딩 속도가 빠르고 인터페이스가 깔끔

커스터마이징과 확장성

  • 브랜딩, 테마, 레이아웃을 자유롭게 커스터마이징
  • React 컴포넌트로 대화형 예제나 코드 스니펫 추가 가능
  • 다국어 지원, 버전 관리, 블로그 등 추가 기능 통합

요약

Swagger UI는 API 스펙 확인용이라면,
Docusaurus OpenAPI는 완전한 개발자 포털을 구축하는 도구이다.

설치 방법

1. docusaurus 설치

npx create-docusaurus@3.7.0 my-website --package-manager yarn
// Git repository 선택 후 아래 url 붙여넣기
// https://github.com/PaloAltoNetworks/docusaurus-template-openapi-docs.git
// copy 선택

2. 플러그인 설치 후 시작

yarn add docusaurus-plugin-openapi-docs
yarn add docusaurus-theme-openapi-docs
yarn start # http://localhost:3000 확인

3. docusaurus.config.ts plugin 수정

plugins: [
[
"docusaurus-plugin-openapi-docs",
{
id: "api",
docsPluginId: "classic",
config: {
productShop: {
specPath: "https://docs.shopby.co.kr/spec/product-shop-public.yml ",
outputDir: "docs/product/shop",
sidebarOptions: {
groupPathsBy: "tag",
},
}
} satisfies Plugin.PluginOptions,
},
],

specPath: open api 빌드 후 추출된 yaml파일을 docusaurus 형식에 맞게 변환시킬 yml path
outputDir: OAS yaml 파일을 해당 위치로 변환하여 저장

4. open api build후 나오는 yml을 위의 specPath에 복사

5. docusaurus  api 문서 생성

yarn docusaurus gen-api-docs all

여기 까지 하면 위의 outputDir 경로에 아래와 같이 생성된다. asd

6. Sidebar 노출

  • 왼쪽 목록에 노출시키려면 sidebar.ts를 수정
import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";

const sidebars: SidebarsConfig = {
tutorialSidebar: [
{ type: "doc", id: "intro" },
{ type: "autogenerated", dirName: "tutorial-basics" },
{ type: "autogenerated", dirName: "tutorial-extras" },
],
openApiSidebar: [
{
type: "category",
label: "Product Shop",
link: {
type: "generated-index",
title: "Product Shop API",
description: "설명",
slug: "/category/product-api",
},
items: require("./docs/product/shop/sidebar.ts"),
},
],
};

export default sidebars;

openApiSidebar 배열에 API 문서 추가

상단메뉴바

docusaurus.config.ts 에 nav 수정

  navbar: {
title: "My Site",
logo: {
alt: "My Site Logo",
src: "img/logo.svg",
},
items: [
{ to: "http://localhost:3000", label: "Shop 바로가기", position: "left" },
{ to: "https://server-docs.shopby.co.kr/", label: "Server 바로가기", position: "left" },
{
label: "Product API",
position: "left",
to: "/docs/category/product-api",
},
{
label: "Order API",
position: "left",
to: "/docs/category/order-api",
},
{
label: "Admin API",
position: "left",
to: "/docs/category/admin-api",
},
],
},

CLI

yarn docusaurus clean-api-docs all
// mdx파일 clean

yarn docusaurus gen-api-docs all
// mdx파일 생성

yarn docusaurus gen-api-docs <specKey>
// 특정 yml만 생성

yarn build # html로 publish
yarn serve # 웹 서빙

주의사항

open api(yml) 파일 안에 <br >와 같이 태그가 안 닫힌 태그가 있다면 mdx파일 생성시 오류 발생함
로 모두 치환해야함

검색 플러그인

search search

docusaurus-search-local 플러그인을 적용하여 yarn build시 검색 인덱싱을 수행한다.

github url

npm install --save @easyops-cn/docusaurus-search-local

docusaurus.config.ts에 아래와 같이 수정

  plugins: [
// 검색 플러그인 추가
[
"@easyops-cn/docusaurus-search-local",
{
hashed: true,
language: ["en", "ko"], // 한국어 지원
docsRouteBasePath: ["/docs"], // 검색 인덱싱 경로
indexDocs: true,
indexPages: true,
indexBlog: true,
blogRouteBasePath: "/blog",
highlightSearchTermsOnTargetPage: true,
},
],
],
yarn build # 빌드 & 검색 인덱싱
yarn serve # 웹 서빙

특이사항

  • 테스트 코드 변경시마다 yml파일을 바꿔치기 해주어야 하는건가?
  • MDX라는 markdown + react 문법을 사용하며 리엑트에 대한 이해가 약간 필요해 보인다.

왼쪽 메뉴 한글로 변경하기

sidebar

label 노출하기

docusaurus-plugin-openapi-docs 에서 tag에 x-displayName이 존재하면 해당 값으로 쓰고 아니면 tag.name 을 사용한다. 원본소스 바로가기

return {
type: "category" as const,
label: tagObject?.["x-displayName"] ?? tag,
link: linkConfig,
collapsible: sidebarCollapsible,
collapsed: sidebarCollapsed,
items: [...taggedSchemaItems, ...taggedApiItems].map((item) =>
createDocItemFn(item, createDocItemFnContext)
),
};

test/resources/tags.yml에 아래와 같이 세팅되어 있고, build.gradle에서 tagDescriptionsPropertiesFile = "src/test/resources/tags.yml" 와 같이 세팅하게 되면  openapi 로 생성되는 yml에 tag가 name, desciption 항목이 생성된다. tag

여기서 tags에 x-displayName을 세팅해줘야한다. build.gradle에 openapi 테스크가 진행될 때 x-displayName을 세팅해서 넣어줄 수 있도록 한다.

import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory

// tagDescriptionsPropertiesFile = "src/test/resources/tags.yml" => 주석


val addDisplayNamesToOpenApi by tasks.registering {
doLast {
val tagsFile = project.file("src/test/resources/tags.yml")
val yamlText = tagsFile.readText()

val mapper = ObjectMapper(YAMLFactory())
val tagsMap: Map<String, String> = mapper.readValue(yamlText, object : TypeReference<Map<String, String>>() {})

val openApiFile = file("build/api-spec/product-shop-public.yml")
if (!openApiFile.exists()) {
throw GradleException("OpenAPI 파일이 없습니다: $openApiFile")
}

val rootNode = mapper.readTree(openApiFile) as com.fasterxml.jackson.databind.node.ObjectNode

// tags 노드가 없으면 새로 만들기
val tagsNode = rootNode.withArray("tags")

// 기존 태그가 있으면 초기화 (선택 사항)
tagsNode.removeAll()

// tags.yml 내용으로 tags 배열 채우기
tagsMap.forEach { (name, description) ->
val tagNode = mapper.createObjectNode()
tagNode.put("name", name)
tagNode.put("description", description)
tagNode.put("x-displayName", "$name-$description")
tagsNode.add(tagNode)
}

// 수정된 내용을 다시 파일에 저장
mapper.writeValue(openApiFile, rootNode)

println("✅ tags.yml 기반으로 tags 배열을 OpenAPI 파일에 새로 추가했습니다: $openApiFile")
}
}

afterEvaluate {
tasks.named("openapi3") {
finalizedBy(addDisplayNamesToOpenApi)
}
}

x-displayName 추가

x-display

Swagger UI - Authorize 대체

기존 docs에 Authorize 버튼이 있고, 입력하면 header에 Authorization, systemKey를 넣어주는 기능을 구현해야함

auth

swagger 플러그인 설치

npm install swagger-ui-react swagger-ui

React 18 다운그레이드 (swagger 플러그인 의존성!)

// package.json
"react": "^18.2.0",
"react-dom": "^18.2.0"

재설치

rm -rf node_modules package-lock.json
npm install

openapi.yaml 파일 추가

/static/openapi.yaml 파일에 아래 내용 추가

openapi: 3.0.1
info:
title: Example API
version: "1.0.0"

paths:
/example:
get:
summary: Example endpoint
security:
- BearerAuth: []
- SystemKeyAuth: []
responses:
"200":
description: Success

components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
SystemKeyAuth:
type: apiKey
in: header
name: systemKey

security:
- BearerAuth: []
- SystemKeyAuth: []

결과물

링크

크롤러 개발 프로젝트

· 3 min read
Haekyu Cho
Software Engineer

사업실에서 개인적으로 부탁하여 취미 삼아 만든 크롤링 프로그램이다. Cursor AI 비지니스 플랜을 구독하여 최대한 AI를 활용하여 프로젝트를 수행하였다. 구글 검색은 거의 하지 않았다. 퇴근 후 저녁에 틈틈히 개발하여 10일 정도 소요

데이터 플랫폼 구축기

· 17 min read
Haekyu Cho
Software Engineer

전사 공용 데이터 플랫폼의 부재로 인해 데이터 통합, 인사이트 도출, 거버넌스 구축을 시도조차 할 수 없는 상황에 직면했다. 흩어진 데이터와 사일로화된 시스템들 사이에서 비즈니스 의사결정에 필요한 데이터를 확보하기 어려웠고, 각 팀마다 다른 도구와 방식으로 데이터를 다루며 일관성 있는 분석이 불가능했다.

이러한 문제를 해결하기 위해 시니어 DevOps 엔지니어 1명과 함께 전사 데이터 플랫폼 TF를 작게 시작했다. 기술 스택 선정, 아키텍처 설계, 구현 과정에서 마주한 도전과 해결책들을 단계별로 기록하여 같은 고민을 하는 팀들에게 도움이 되고자 한다. 데이터 엔지니어링의 첫걸음부터 운영 가능한 플랫폼까지, 작은 팀이 만들어가는 데이터 플랫폼 구축 여정을 공유한다.

OpenTelemetry를 활용한 가시성 확보

· 17 min read
Haekyu Cho
Software Engineer

분산 트레이싱 오픈소스인 Pinpoint 사용중이였으나 hbase의 운영의 어려움 (고사양 인스턴스, zookeeper까지 최소 6대 이상 필요, 네트워크등 지연, 오류 발생시 복제가 깨지면 복구가 매우 힘들어 담당자가 hbase를 초기화하여 매번 다시 실행)과 PHP, Python등 non jvm기반에서도 가시성 확보를 위해 OpenTelemetry에 대해서 공부해보고 실제 환경과 유사한 시나리오를 계획하여 코드를 작성해 보았다.

커머스 플랫폼 개발 여정

· 5 min read
Haekyu Cho
Software Engineer

2014년부터 개발해온 커머스 플랫폼의 변화와 여정에 대해서 기록해보았다. 자료가 더 많지만 민감한 내용도 있을 수 있기에 매뉴얼, 기술 컨퍼런스, 유튜브 등에 공개된 내용만 적어본다.

Coroutine Dispatcher 분리

· 5 min read
Haekyu Cho
Software Engineer

ThreadPool을 분리해야 한다. 왜? 기존에는 Dispatchers.IO를 활용해 IO 작업을 처리하고 있었으나, 아래와 같은 문제가 발생하면서 ThreadPool을 용도별로 커스텀하여 분리하는 방향으로 개선