Serverless ๋ง์ดํฌ๋ก์๋น์ค ํ๊ฒฝ์์ API Docs ํตํฉํ๊ธฐ
- #nestjs
- #api docs
- #swagger
- #serverless
- #webpack
- #Dependabot
- #microservice
API Docs
NestJS๋ฅผ ์ฌ์ฉํ๋ฉด, @nestjs/swagger ๋ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ์ฝ๊ฒ API Docs๋ฅผ ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด, ํ๋์ ์๋น์ค์์ API Docs๋ฅผ ์์ฑํ๋ ๊ฑด ๋งค์ฐ ์ฌ์ด ์ผ์ ๋๋ค.
์๋ ์ฝ๋๋ NestJs ๊ณต์ ๋ฌธ์์ ์๋ @nestjs/swagger๋ก Swagger UI๋ฅผ ๊ตฌ์ฑํ๋ ์ฝ๋์ ๋๋ค.
import {NestFactory} from '@nestjs/core';
import {SwaggerModule, DocumentBuilder} from '@nestjs/swagger';
import {AppModule} from './app.module';
async function bootstrap() {
const expressApp = express();
const app = await NestFactory.create(AppModule, new ExpressAdapter(expressApp));
const config = new DocumentBuilder().setTitle('Cats example').setDescription('The cats API description').setVersion('1.0').addTag('cats').build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();
NestFactory์์ AppModule์ ์ฌ์ฉํด app์ ๋ง๋ค๊ณ , ๊ทธ app๊ณผ swagger config๋ฅผ ์ฌ์ฉํด์ document๋ฅผ ๋ง๋ค๊ณ , ๋ง์ง๋ง์ผ๋ก setupํด์ฃผ๋ ๊ณผ์ ์ ํ์ธํ ์ ์์ต๋๋ค.
์ด์ฒ๋ผ ํ๋์ ํ๋ก์ ํธ์์ API Docs๋ฅผ ์์ฑํ๋๊ฑด ๋งค์ฐ ์ฌ์ด ์ผ์ ๋๋ค.
๊ทธ๋ผ ์๋น์ค๋ณ๋ก ํ๋ก์ ํธ๊ฐ ๋ถ๋ฆฌ๋์ด ์์ ๋ API Docs๋ฅผ ์ด๋ป๊ฒ ์์ฑํด์ผ ํ ๊น์?
์ด ๊ธ์์ , ์ ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ฌ๋ฌ ์๋น์ค์ ๋ถ๋ฆฌ๋์ด ์๋ ๋ชจ๋๋ค์ ํ๋์ API Docs๋ก ํฉ์น๋ ๊ณผ์ ์ ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค.
์์ ๋ Express ๊ธฐ๋ฐ์ด์ง๋ง, ๊ผญ Express ๊ธฐ๋ฐ์ด ์๋๋๋ผ๋ ๊ฐ๊ฐ์ ๊ธฐ๋ฐ์ด ๋๋ ํ๋ ์์ํฌ์ ๋ง์ถฐ ์กฐ๊ธ์ฉ๋ง ์์ ํ๋ฉด ์ถฉ๋ถํ ์ฌ์ฉํ ์ ์์ ๊ฒ์ ๋๋ค.
1. Module ์์ฑ
์ฌ๋ฌ ์๋น์ค์ ๋ถ๋ฆฌ๋์ด์๋ API Docs๋ฅผ ํตํฉํด์ฃผ๊ธฐ ์ํด์ ๊ฐ์ฅ ๋จผ์ ํด์ผ ํ ์ผ์, document๋ฅผ ๋ง๋ค app์ ํ๋ ์๋ก ๋ง๋ค์ด ์ฃผ๋ ๊ฒ์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ app์ ์๋ก ๋ง๋ค๊ธฐ ์ํด์ , ์๋ก์ด ๋ชจ๋์ด ํ์ํฉ๋๋ค. ์ด ๋ชจ๋์ด ๋ถ๋ฆฌ๋ ์ฌ๋ฌ ์๋น์ค์ ๋ชจ๋๋ค์ importํ๋ ํ๋์ ํฐ ๋ชจ๋์ด ๋ ๊ฒ์ ๋๋ค.
A ์๋น์ค์ A Module๊ณผ B ์๋น์ค์ B Module์ ํ๋์ docs๋ก ํฉ์น๋ค๊ณ ์๊ฐํ๋ฉด, ์๋์ ๊ฐ์ ๋ชจ๋์ด ๋ง๋ค์ด ์ง ๊ฒ์ ๋๋ค.
@Module({
imports: [AModule, BModule]
})
export class DocsModule {}
์ด ๋ชจ๋์ ๊ธฐ๋ฐ์ผ๋ก bootstrap ํจ์์์ Swagger๋ฅผ ๊ตฌ์ฑํ๋ค๋ฉด, ์๋์ ๊ฐ์ด ๋ฉ๋๋ค.
import {NestFactory} from '@nestjs/core';
import {SwaggerModule, DocumentBuilder} from '@nestjs/swagger';
import {AppModule, DocsModule} from './app.module';
async function bootstrap() {
const expressApp = express();
const app = await NestFactory.create(AppModule); // ์๋ฒ ์คํ์ ์ํ AppModule
const docsApp = await NestFactory.create(DocsModule); // Docs ์์ฑ์ ์ํ DocsModule
const config = new DocumentBuilder().setTitle('Cats example').setDescription('The cats API description').setVersion('1.0').addTag('cats').build();
const document = SwaggerModule.createDocument(docsApp, config); // Docs ์์ฑ์ docsApp ์ฌ์ฉ
// SwaggerModule.setup('api', docsApp, document); // Docs ์์ฑ์ Api๋ก ์งํํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์, setup์ ์ ๊ฑฐ
await app.listen(3000); // ์๋ฒ ์คํ์ app ์ฌ์ฉ
}
bootstrap();
2. SwaggerModule.setup ์ง์ ๊ตฌํ
๋ ๋ฒ์งธ ๊ณผ์ ์ ์ค๋ช ํ๊ธฐ ์ ์, SwaggerModule์ setup ํจ์๊ฐ ์ด๋ป๊ฒ ์๋ํ๋์ง๋ถํฐ ์์์ผ ํ ํ์๊ฐ ์์์ต๋๋ค.
SwaggerModule์ ์ฝ๋๋ฅผ ํ์ธํด๋ณด๋ฉด,
...
class SwaggerModule {
static createDocument(app, config, options = {}) {
const swaggerScanner = new swagger_scanner_1.SwaggerScanner();
const document = swaggerScanner.scanApplication(app, options);
document.components = Object.assign(Object.assign({}, (config.components || {})), document.components);
return Object.assign(Object.assign({ openapi: '3.0.0' }, config), document);
}
static setup(path, app, document, options) {
const httpAdapter = app.getHttpAdapter();
if (httpAdapter && httpAdapter.getType() === 'fastify') {
return this.setupFastify(path, httpAdapter, document);
}
return this.setupExpress(path, app, document, options);
}
static setupExpress(path, app, document, options) {
const httpAdapter = app.getHttpAdapter();
const finalPath = validate_path_util_1.validatePath(path);
const swaggerUi = load_package_util_1.loadPackage('swagger-ui-express', 'SwaggerModule', () => require('swagger-ui-express'));
const swaggerHtml = swaggerUi.generateHTML(document, options);
app.use(finalPath, swaggerUi.serveFiles(document, options));
httpAdapter.get(finalPath, (req, res) => res.send(swaggerHtml));
httpAdapter.get(finalPath + '-json', (req, res) => res.json(document));
}
static setupFastify(path, httpServer, document) {
const hasParserGetterDefined = Object.getPrototypeOf(httpServer).hasOwnProperty('isParserRegistered');
if (hasParserGetterDefined && !httpServer.isParserRegistered) {
httpServer.registerParserMiddleware();
}
httpServer.register((httpServer) => __awaiter(this, void 0, void 0, function* () {
httpServer.register(load_package_util_1.loadPackage('fastify-swagger', 'SwaggerModule', () => require('fastify-swagger')), {
swagger: document,
exposeRoute: true,
routePrefix: path,
mode: 'static',
specification: {
document
}
});
}));
}
}
...
์ด๋ฐ ํด๋์ค ํ๋๊ฐ ๋์ค๊ฒ ๋๋๋ฐ, ์ฌ๊ธฐ์ setup์ ์ฌ์ค์ httpAdapter์ ๋ฐ๋ผ ๋ค๋ฅธ ํจ์๋ฅผ ํธ์ถํ๋๊ฒ ์ ๋ถ์ด๋ฏ๋ก, setupExpress, setupFastify๊ฐ API Docs๋ฅผ ๊ตฌ์ฑํด์ฃผ๋ ํจ์๋ผ๊ณ ๋ณผ ์ ์์ต๋๋ค.
httpAdapter๊ฐ ์์ ๋ ๊ธฐ๋ณธ์ ์ผ๋ก setupExpress๋ฅผ ํธ์ถํ๋ฏ๋ก, Express๋ฅผ ๊ธฐ์ค์ผ๋ก ์ด์ผ๊ธฐ ํ๊ฒ ์ต๋๋ค.
setupExpress๊ฐ ํ๋ ์ผ์ ํฌ๊ฒ ๋ ๊ฐ์ง๋ก ๋ณผ ์ ์์ต๋๋ค.
- ๋ค์ด์จ path์ swaggerUi.serveFiles ํด์ฃผ๋ ๋ฏธ๋ค์จ์ด ์์ฑ
- ๋ค์ด์จ path์ get ์์ฒญ์ ๋ณด๋ผ ์ swaggerUi.generateHTML์ ๊ฒฐ๊ณผ๊ฐ์ sendํด์ฃผ๋ API ์์ฑ
์ด ๋ ๊ฐ์ง๋ฅผ ์ง์ ๊ตฌํํด์ฃผ๋ฉด API ํธ์ถ์ Swagger UI๋ฅผ ๋ณผ ์ ์์ ๊ฒ์ ๋๋ค.
์ฐ์ , ๋ฏธ๋ค์จ์ด๋ 1๋ฒ ๊ณผ์ ์์ ๋ง๋ bootstrap ํจ์์ ์ถ๊ฐํด ์ฃผ๋ฉด ๋ฉ๋๋ค.
import {NestFactory} from '@nestjs/core';
import {SwaggerModule, DocumentBuilder} from '@nestjs/swagger';
import SwaggerUi from 'swagger-ui-express';
import {AppModule, DocsModule} from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const docsApp = await NestFactory.create(DocsModule);
const config = new DocumentBuilder().setTitle('Cats example').setDescription('The cats API description').setVersion('1.0').addTag('cats').build();
const document = SwaggerModule.createDocument(docsApp, config);
app.use('/docs', SwaggerUi.serveFiles(docsApp)); // /docs ๊ฒฝ๋ก์ ์ ์ ํ์ผ ์ ๊ณต ๋ฏธ๋ค์จ์ด ์ถ๊ฐ
await app.listen(3000);
}
bootstrap();
๊ทธ ๋ค์ generateHTML์, ํด๋น ๋ฏธ๋ค์จ์ด์ ๊ฐ์ ๊ฒฝ๋ก์ API๋ฅผ ๋ง๋ค์ด์ ๋ฆฌํด๋ง ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
controller์ module์ ์๋ตํ๊ณ service์ ์ฝ๋๋ง ๋ณด๋ฉด ์๋์ ๊ฐ์ด ๊ฐ๋จํ ํด๊ฒฐ ๋ฉ๋๋ค.
import SwaggerUi from 'swagger-ui-express';
@Injectable()
export class DocsService {
getSwaggerHTML() {
return SwaggerUi.generateHTML();
}
}
์ด์ ๋ก์ปฌ์์ ์๋ฒ ์คํ ํ localhost:3000/docs๋ก ์ ์ํ๋ฉด API Docs๊ฐ ๋ณด์ผ ๊ฒ์ ๋๋ค.
3. Serverless Framework๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, Api Event์ ์ ์ ํ์ผ ๊ฒฝ๋ก ์ถ๊ฐ
๋ก์ปฌ์์ ์คํํ์ ๋ ๋ฌธ์ ๊ฐ ์์์ผ๋, Serverless๋ฅผ ์ฌ์ฉํ ๋ฐฐํฌ์์๋ ์ ๋์ฌ๊ฑฐ๋ผ ์๊ฐ ํ์ต๋๋ค.
ํ์ง๋ง ๋ฐฐํฌ ํ /docs ๊ฒฝ๋ก์ ์ ๊ทผํด๋ณด๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉฐ API Docs๊ฐ ๋ณด์ด์ง ์์์ต๋๋ค.
๊ฐ๋ฐ์ ๋๊ตฌ๋ฅผ ์ผ์ ํ์ธํด๋ณด๋, ์ ์ ํ์ผ๋ค์ get ํ๋ ๊ณผ์ ์์ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
serverless.yml์ events ์์ ์ ์ ํ์ผ๋ค ๊ฒฝ๋ก๋ฅผ ์ถ๊ฐํด์ค์ผ, ์ ๋๋ก docs๊ฐ ๋์ค๊ฒ ๋ฉ๋๋ค.
functions:
Docs:
handler: src/docs/lambda.handler
events:
- http:
path: /v2/docs
method: get
- http:
path: /v2/docs/swagger-ui.css
method: get
- http:
path: /v2/docs/swagger-ui-bundle.js
method: get
- http:
path: /v2/docs/swagger-ui-standalone-preset.js
method: get
- http:
path: /v2/docs/swagger-ui-init.js
method: get
- http:
path: /v2/docs/favicon-32x32.png
method: get
- http:
path: /v2/docs/favicon-16x16.png
method: get
- http:
path: /v2/docs/favicon.ico
method: get
3 - 1. Webpack์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ์ ์ ํ์ผ copy ๋ฐ ๋ฏธ๋ค์จ์ด ๊ฒฝ๋ก ์์
Webpack์ ์ฌ์ฉํ๋ค๋ฉด swagger-ui.css is not found ์๋ฌ๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ๊ฐ ์์ ๊ฒ์ ๋๋ค.
์ด์ ๋ฅผ ์๋ ค๋ฉด, serveFiles๊ฐ ์ด๋ป๊ฒ ์๋ํ๋์ง ์์์ผ ํฉ๋๋ค.
swagger-ui-express์ serveFiles๋ฅผ ํ์ธํด๋ณด๋ฉด,
...
var swaggerUi = require('swagger-ui-dist')
...
var swaggerAssetMiddleware = options => {
var opts = options || {};
opts.index = false;
return express.static(swaggerUi.getAbsoluteFSPath(), opts);
};
var serveFiles = function (swaggerDoc, opts) {
opts = opts || {};
var initOptions = {
swaggerDoc: swaggerDoc || undefined,
customOptions: opts.swaggerOptions || {},
swaggerUrl: opts.swaggerUrl || {},
swaggerUrls: opts.swaggerUrls || undefined
};
var swaggerInitWithOpts = swaggerInitFunction(swaggerDoc, initOptions);
return [swaggerInitWithOpts, swaggerAssetMiddleware()];
};
...
์ ์ ํ์ผ์ ๊ฐ์ ธ์ค๋ ๋ฏธ๋ค์จ์ด๊ฐ ํ๋ ์๊ณ , ์ด ์ ์ ํ์ผ๋ค์ ๊ฐ์ ธ์ฌ ๊ฒฝ๋ก ๋ฐ ์ ์ ํ์ผ๋ค์ด swagger-ui-dist ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ ์๋์ด์์ต๋๋ค.
404 Not Found ๊ฐ ๋ฐ์ํ๋ ์ด์ ๋, ์ ์ ํ์ผ์ ๊ฐ์ ธ์์ผ ํ ๊ฒฝ๋ก์ ํด๋น ํ์ผ๋ค์ด ์์ด์ ๋๋ ์ค๋ฅ์๊ณ , ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ ๋ ๊ฐ์ง ๊ณผ์ ์ด ํ์ํ์ต๋๋ค.
- webpack.config.js ํ์ผ์ CopyWebpackPlugin ์ฌ์ฉ
- /docs ์ serveFiles ๋ฏธ๋ค์จ์ด ๊ฒฝ๋ก ์์
์ฐ์ 1๋ฒ ๊ณผ์ ์, Webpack์ผ๋ก ๋น๋ํ๋ ๊ณผ์ ์์ swagger-ui-dist์ ์ ์ ํ์ผ๋ค์ด ๋น๋๋ ๋๋ ํ ๋ฆฌ์ ์กด์ฌํ์ง ์์๊ธฐ ๋๋ฌธ์ ํ์ํ ๊ณผ์ ์ด์์ต๋๋ค.
...
plugins: [
new CopyWebpackPlugin({
patterns: [
'./node_modules/swagger-ui-dist/swagger-ui.css',
'./node_modules/swagger-ui-dist/swagger-ui-bundle.js',
'./node_modules/swagger-ui-dist/swagger-ui-standalone-preset.js',
'./node_modules/swagger-ui-dist/swagger-ui-init.js',
'./node_modules/swagger-ui-dist/favicon-32x32.png',
'./node_modules/swagger-ui-dist/favicon-16x16.png',
'./node_modules/swagger-ui-dist/favicon.ico',
'.env'
]
}),
new webpack.IgnorePlugin(WebPackIgnorePlugin)
],
...
์ด๋ฐ ์์ผ๋ก webpack.config.js plugins์์ ์ ์ ํ์ผ๋ค์ copy ํด์ค์ผ ํฉ๋๋ค.
2๋ฒ ๊ณผ์ ์, ์ ๋ ๊ฒ copy๋ฅผ ํด๋ ๊ธฐ์กด ๋ฏธ๋ค์จ์ด์ ๊ฒฝ๋ก๋๋ก ์ฌ์ฉํ๋ฉด ์นดํผ๋ ํ์ผ๋ค์ ์ฐพ์ ์ ์๊ธฐ ๋๋ฌธ์ ํ์ํฉ๋๋ค.
app.use('/v2/docs', express.static(path.join(__dirname, '..', '..')), SwaggerUi.serveFiles(document, customOptions));
express.static()์ผ๋ก ์ ์ ํ์ผ๋ค์ ๊ฐ์ ธ ์ฌ ๊ฒฝ๋ก๋ฅผ ์ฌ์ค์ ํด์ค์ผ ์ ๋๋ก ์ ์ ํ์ผ๋ค์ด import ๊ฐ๋ฅํฉ๋๋ค.
Dependabot
์๋ก์ด API๊ฐ ์ถ๊ฐ๋ ๋ ๋ง๋ค ๊ฐ ํผ๋ธ๋ฆฌ์๋ ํจํค์ง๋ค์ ์๋ก ์ค์นํ๊ณ , ๋ฐฐํฌํด์ค์ผ ํ๋ค๋ ๋ถํธํจ์ด ์์์ต๋๋ค.
์ด ๋ถํธํจ ๋๋ฌธ์ ์ ํฌ ๊ฐ๋ฐํ์ด ์๊ฐํด ๋ธ ๊ฑด, GitHub์ Dependabot์ด๋ผ๋ ๊ธฐ๋ฅ์ ๋๋ค.
Dependabot์ ์ค์น๋์ด์๋ ํจํค์ง, ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฒ์ ์ ์๋์ผ๋ก ์ฒดํฌํ๊ณ , ์ ๋ฐ์ดํธ๊ฐ ์๋ ๊ฒฝ์ฐ Pull Request๋ฅผ ์์ฑํด ์ค๋๋ค.
์ด๋ฅผ ์ด์ฉํด์ ์๋ก์ด API๊ฐ ์ถ๊ฐ๋ ๋ ๋ง๋ค Dependabot์ด ์ด๋ฅผ ๊ฐ์งํด์ Pull Request๋ฅผ ์์ฑํ๊ณ , ์ฐ๋ฆฌ๋ ๊ฐ๋จํ ํ์ธ ํ Merge๋ง ํ๋ฉด ๋๋ ์์คํ ์ ๋ง๋ค์์ต๋๋ค.
Dependabot๋ ๋ค๋ฅธ GitHub Actions์ ๋ง์ฐฌ๊ฐ์ง๋ก .github ํด๋ ๋ด์ Dependabot.yml ํ์ผ์ ์์ฑํ์ฌ GitHub์ ํธ์ํด๋๋ฉด, Dependabot์ด ์๋ํ๋๋ก ๋์ด์์ต๋๋ค.
version: 2
updates:
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'daily'
time: '00:00'
open-pull-requests-limit: 30
allow:
- dependency-name: '@package/*'
ignore:
- dependency-name: '@package/ignore_package'
- dependency-name: '*'
update-types: ['version-update:semver-patch']
target-branch: 'master'
์ ์ฝ๋๋ ํ์ฌ Wonderwall API Docs์์ ์ฌ์ฉ์ค์ธ Dependabot.yml ํ์ผ์ ๋๋ค.
์ค์ํ ๋ถ๋ถ๋ง ์ค๋ช ์ ํ์๋ฉด
-
schedule: Dependabot์ด ์๋ํ ์ค์ผ์ค๋ฌ๋ฅผ ๋ง๋๋ ๋ถ๋ถ์ ๋๋ค. interval daily๋ ๋งค์ผ (์ฃผ๋ง ์ ์ธ), time 00:00 ์ UTC ๊ธฐ์ค 00์ 00๋ถ์ ๋์๊ฐ๋ค๋ ๋ป์ ๋๋ค.
-
allow: Dependabot์ด ๋ฒ์ ์ ์ ๊ฐ์งํ ํจํค์ง๋ฅผ ์ง์ ํด์ฃผ๋ ๋ถ๋ถ์ ๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋ ํจํค์ง์ ๋ํ ๋ฒ์ ์ ์ ๊ฐ์งํฉ๋๋ค. @packege/*์ @package/ ๋ก ์์ํ๋ ๋ชจ๋ ํจํค์ง๋ฅผ ํ์ฉํ๋ค๋ ๋ป์ ๋๋ค.
-
ignore: Dependabot์ด ๋ฒ์ ์ ์ ๋ฌด์ํ ํจํค์ง๋ฅผ ์ง์ ํฉ๋๋ค. @packege/*๋ฅผ ํ์ฉํด ์ฃผ๊ณ , ๊ทธ ์ค ๋ฌด์ํ ํจํค์ง๋ฅผ ์ง์ ํด ์ค๋๋ค.
ํจํค์ง๋ฅผ ignore ํ ์๋ ์๊ณ , update-types์์ ๋ฒ์ ์ด ์ค๋ฅด๋ ํจํด์ ๋ฌด์ํ ์๋ ์์ต๋๋ค. ํ์ฌ ์ฌ์ฉ์ค์ธ version-update:semver-patch๋ ์๋ฉํฑ ๋ฒ์ ๋์ ๊ธฐ์ค์ผ๋ก patch ๋ฒ์ ์ด ์ ๋ฐ์ดํธ ๋๋ ๊ฒฝ์ฐ๋ฅผ ๋ฌด์ํฉ๋๋ค. -
target-branch: Pull Request๋ฅผ ์์ฑํด ์ค ๋ธ๋์น๋ฅผ ์ง์ ํด์ค๋๋ค.
์ด ์ธ์ ๋ค๋ฅธ ์ต์ ์ GitHub Docs์ Dependabot ์์ ํ์ธ ํ ์ ์์ต๋๋ค.
Dependabot์ ์ฌ์ฉํ์ ๋์ ๋ฌธ์ ๋, ๋ฒ์ ์ ๋ ํจํค์ง๊ฐ ์ฌ๋ฌ๊ฐ์ผ ๋ Pull Request๊ฐ ํจํค์ง์ ๊ฐ์๋๋ก ์์ฑ๋๋ ๊ฒ์ ๋๋ค.
๊ทธ๋์ ์ฌ๋ฌ๊ฐ์ Pull Request๋ฅผ ํ๋๋ก ํฉ์น๋ ์์ ์ด ์ถ๊ฐ๋์ด์ผ ํ๊ณ combine Dependabot prs workflow ๋ฅผ ์ฐพ์์ต๋๋ค.
Repository์ ์๋ ymlํ์ผ์ .github ํด๋์ ๋ฃ๊ณ , README.md ํ์ผ์ ์ ํ์๋๋๋ก ๋ฐ๋ผ๊ฐ๋ฉด ๋ชจ๋ Dependabot Pull Request๋ฅผ ํฉ์น ํ๋์ Pull Request๋ฅผ ๋ง๋ค์ด ์ค๋๋ค.
๋ง๋ฌด๋ฆฌ
์๋๋ API Docs๋ฅผ ํฉ์น๋ฉฐ ํ๋ ๊ณ ๋ฏผ๋ค ์์ฃผ์ ๊ธ์ ์ ์ผ๋ ค๊ณ ํ๋๋ฐ, ์ ๋ค๋ณด๋ @nestjs/swagger ํํค์น๊ธฐ ๋๋์ ๊ธ์ด ๋์ค๊ฒ ๋์๋ค์.
๊ทธ๋ ๊ฒ ์ด๋ ค์ด ๋ด์ฉ์ ์๋์ง๋ง, ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ง์ ํ์ธํ๊ณ ๋ถ์ํ๋๋ผ ๋ง์ ์ฝ์ง์ ํ๋ ์์ ์ด์์ต๋๋ค.
์ ์ ๊ฐ์ ๊ณ ๋ฏผ์ ํ๊ณ ๊ณ์ ๋ถ๊ป ๋์์ด ๋์์ผ๋ฉด ์ข๊ฒ ์ต๋๋ค.
๊ธด ๊ธ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.