Skip to Content

Swagger (OpenAPI)

ASAPJS는 라우트 데코레이터와 DTO/TypeIs 정의로부터 OpenAPI 3.0 스펙을 자동으로 생성합니다. 별도의 Swagger 파일을 작성할 필요 없이, 코드가 곧 API 문서가 됩니다.


Swagger UI 접근

RouterModule은 부트스트랩 시 자동으로 다음 엔드포인트를 등록합니다:

엔드포인트설명
/{basePath}/docs/swagger-ui.htmlSwagger UI 인터페이스
/{basePath}/docs/swagger.json생성된 OpenAPI 3.0 JSON 스펙

예를 들어 basePath: 'api'인 경우:

  • Swagger UI: http://localhost:3000/api/docs/swagger-ui.html
  • JSON 스펙: http://localhost:3000/api/docs/swagger.json

Swagger 설정

IConfig.swagger 객체로 Swagger UI의 정보와 동작을 설정합니다.

// src/index.ts const config = { name: 'My API', port: 3000, basePath: 'api', extensions: ['@asapjs/sequelize'], swagger: { name: 'My API Documentation', version: '1.0.0', description: 'ASAPJS로 구축된 REST API', scheme: 'http', host: 'localhost:3000', auth_url: '/api/users/login', useAuth: true, userObject: { admin: 'secure-swagger-password', }, }, // ... auth, sequelize 설정 };

swagger 필드

필드타입필수설명
namestringYesSwagger UI 상단에 표시되는 API 이름
versionstringYesAPI 버전 문자열 (예: '1.0.0')
descriptionstringYesAPI 설명 텍스트
scheme'http' | 'https'Yes”Try it out” 요청에 사용할 URL 스킴
hoststringYes”Try it out” 요청에 사용할 호스트 (예: 'localhost:3000')
auth_urlstringNoOAuth2 Password Flow의 토큰 URL
useAuthbooleanNotrue일 때 Swagger UI 접근에 Basic Auth를 요구
userObject{ [username]: password }NoSwagger UI Basic Auth 사용자 계정

Basic Auth 보호

프로덕션 환경에서는 Swagger UI를 외부에 노출하지 않도록 Basic Auth로 보호할 수 있습니다.

// packages/router/src/router/index.ts 에서 자동 처리 if (config?.swagger?.useAuth && config?.swagger?.userObject) { this.app.use( `/${basePath}/docs/swagger-ui.html`, basicAuth({ users: config.swagger.userObject, // { admin: 'password' } challenge: true, realm: 'Developer', }), swaggerUi.serveFiles(undefined, options), swaggerUi.setup(undefined, options), ); }

useAuth: trueuserObject를 설정하면, Swagger UI 접근 시 브라우저 기본 인증 대화 상자가 나타납니다.

주의: useAuth를 설정하지 않으면 다음과 같은 경고 로그가 출력됩니다: @router SWAGGER useAuth is disabled! Please add config.swagger.useAuth & config.swagger.userObject


자동 생성 원리

전체 흐름

@Get/@Post/@Put/@Delete 데코레이터 RouterController.excute() ↓ body/response DTO → generateSwaggerDefaultsSet() addPaths() + addScheme() ← DocsApplication에 등록 GET /docs/swagger.json 요청 시 getSwaggerData(req) → 최종 OpenAPI JSON 반환

DocsApplication

Swagger 데이터를 수집하고 최종 OpenAPI 스펙을 생성하는 싱글턴 클래스입니다.

// packages/router/src/swagger/index.ts class DocsApplication { public swaggerData = { ...defaultSwagger }; // OpenAPI 3.0 기본 템플릿 private swaggerPath = []; // 경로 정보 (addPaths로 추가) private swaggerScheme = []; // 스키마 정보 (addScheme로 추가) public addPaths = async (data: any) => { this.swaggerPath.push(data); }; public addScheme = async (data: any) => { this.swaggerScheme.push(data); }; public generateSwaggerData = (req: any) => { const config = getConfig(); // IConfig.swagger에서 정보 설정 this.swaggerData.info.title = config?.swagger?.name || ''; this.swaggerData.info.version = config?.swagger?.version || ''; this.swaggerData.info.description = config?.swagger?.description || ''; // 서버 URL 설정 (요청 호스트 + config 호스트) this.swaggerData.servers = [ { url: `${config?.swagger?.scheme}://${req.headers.host}/${config?.basePath}` }, { url: `${config?.swagger?.scheme}://${config?.swagger?.host}/${config?.basePath}` }, ]; // 경로와 스키마를 OpenAPI 구조에 매핑 this.swaggerPath.forEach((v) => { if (!this.swaggerData.paths[v.path]) this.swaggerData.paths[v.path] = {}; const { path: _, method: __, ...props } = v; this.swaggerData.paths[v.path][v.method?.toLowerCase()] = props; }); this.swaggerScheme.forEach((v) => { this.swaggerData.components.schemas[v.name] = v.data; }); }; } export const { addPaths, addScheme, getSwaggerData } = new DocsApplication();

기본 OpenAPI 템플릿

DocsApplication은 다음 기본 구조에서 시작합니다:

// packages/router/src/assets/default-swagger.json { "openapi": "3.0.0", "servers": [], "info": { "version": "", "title": "", "description": "" }, "paths": {}, "components": { "schemas": {}, "securitySchemes": { "bearerAuth": { "type": "http", "scheme": "bearer" }, "OAuthLogin": { "type": "oauth2", "flows": { "password": { "tokenUrl": "/auth/login", "scopes": {} } } } } }, "security": [ { "OAuthLogin": [] }, { "bearerAuth": [] } ] }

내장된 보안 스킴:

  • bearerAuthAuthorization: Bearer <token> 헤더 방식
  • OAuthLogin — OAuth2 Password Flow (Swagger UI에서 직접 로그인 가능)

라우트 데코레이터 → Swagger 매핑

데코레이터 옵션과 Swagger 필드 매핑

@Post('/register', { title: '회원 가입', // → summary description: '새 사용자 등록', // → description deprecated: false, // → deprecated body: CreateUserDto, // → requestBody bodyContentType: 'application/json', // → requestBody content type query: PaginationQueryDto, // → parameters (in: query) response: UserInfoDto, // → responses.200 auth: true, // → 런타임 JWT 미들웨어만 제어 (Swagger 스키마에 미반영) })
데코레이터 옵션OpenAPI 필드설명
titlesummary엔드포인트 한 줄 설명
descriptiondescription상세 설명
deprecateddeprecated더 이상 사용되지 않는 엔드포인트 표시
bodyrequestBody요청 바디 스키마 (DTO 또는 TypeIs)
bodyContentTyperequestBody.contentapplication/json 또는 multipart/form-data
queryparameters쿼리 파라미터 스키마 (DTO)
responseresponses.200성공 응답 스키마 (DTO 또는 TypeIs)
auth(Swagger 미반영)런타임 jwtVerification 미들웨어만 제어. Swagger에는 per-endpoint security가 추가되지 않음

auth와 Swagger security의 관계: auth: true는 런타임에서 jwtVerification 미들웨어를 활성화하여 JWT 토큰 검증을 수행합니다. 그러나 Swagger 스펙에는 per-endpoint security 필드가 추가되지 않습니다. 대신 default-swagger.json에 정의된 전역 security (bearerAuth + OAuthLogin)가 모든 엔드포인트에 일괄 적용됩니다. 즉, Swagger UI에서는 auth 값과 관계없이 모든 엔드포인트에 동일한 인증 UI가 표시됩니다.

URL 파라미터 자동 처리

경로에 :param 형태의 파라미터가 있으면 자동으로 OpenAPI path parameter로 변환됩니다:

// packages/router/src/express/router.ts 에서 처리 @Get('/:id', { title: '사용자 상세', response: UserInfoDto }) async getUser({ path }: ExecuteArgs) { // path.id로 접근 가능 } // Swagger 변환 결과: // path: /users/{id} // parameters: [{ in: 'path', name: 'id', required: true, schema: { type: 'string' } }]

DTO → Swagger 스키마 변환

DTO 클래스 방식

ExtendableDto를 상속한 DTO는 swagger() 메서드로 $ref 참조를 생성하고, generateScheme()으로 속성 스키마를 생성합니다.

// packages/sequelize/src/dto/ExtendableDto.ts export default class ExtendableDto { public generateScheme = () => { const types = getTypesData(this); const properties = Object.keys(types).reduce((p, key) => { p[key] = types[key].toSwagger?.() || null; return p; }, {}); return { type: 'object', properties }; }; public swagger = () => { return { $ref: `#/components/schemas/${this.constructor.name}` }; }; }

DTO 예시와 생성되는 Swagger 스키마:

// DTO 정의 export default class CreateUserDto extends ExtendableDto { @TypeIs.STRING({ comment: '이메일' }) email: string; @TypeIs.PASSWORD({ comment: '비밀번호' }) password: string; @TypeIs.STRING({ comment: '사용자 이름' }) name: string; } // 생성되는 Swagger 스키마: // { // "CreateUserDto": { // "type": "object", // "properties": { // "email": { "type": "string", "description": "이메일" }, // "password": { "type": "string", "format": "password", "description": "비밀번호" }, // "name": { "type": "string", "description": "사용자 이름" } // } // } // }

TypeIs 팩토리 방식

DTO 클래스 외에 TypeIs 팩토리 함수를 직접 사용할 수도 있습니다:

// 배열 응답 @Get('/', { response: TypeIs.ARRAY(UserInfoDto), // → { type: 'array', items: { $ref: '...' } } }) // 페이지네이션 응답 @Get('/', { response: TypeIs.PAGING(PostInfoDto), // → 페이지네이션 래핑 스키마 })

추가 엔드포인트

RouterModule은 Swagger 외에도 개발에 유용한 엔드포인트를 자동 등록합니다:

엔드포인트조건설명
/{basePath}항상서버 동작 확인 메시지
/health-check항상DB 연결 상태 확인 (SELECT 1)
/{basePath}/sync@asapjs/sequelize 확장 활성화 시DB 테이블 동기화 (modelsSync())
/{basePath}/docs/generate-dbml@asapjs/sequelize 확장 활성화 시DBML 스키마 생성

관련 문서

  • BootstrapIConfig.swagger 설정 상세
  • Routing — 라우트 데코레이터와 RouterController
  • Deployment — 프로덕션 Swagger 보안 설정
Last updated on