귀찮은 api 문서 swagger UI로 자동화 (nodejs)

연쇄코딩마·2021년 10월 19일
2

TIL

목록 보기
14/15
post-thumbnail

난 성격이 좀 이상한것 같다.

회사에서 개발을 하면서 한창 집중 중일때 누군가 말을 거는게 거슬린다. 한두번 그러면 뭐 그러려니 하는데 일이라는게 한두번 말을 해선 안되는게 많다. 그래서 좀 성격을 고치고자 노력을 해보느니 다시 태어나는게 빠를거 같고(노력해야지 🙏) api 문서 만이라도 자동화 시키는게 좋을것 같아 구글링중에 찾았다.

swagger-UI

live Demo

라이브 데모를 참고 하면 알겠지만 apiUI자체를 자동적으로 생성해준다. 코딩 과정에서 작성하는게 가능해서 신속하게 작성이 가능하고 api를 직접 기능이 잘 작동하는지 확인 가능하다. 예전에 엑셀이나 깃 위키 다른 문서를 작성하느라 시간을 허비한거 생각하면 힙하다고 생각한다.

설치과정 (nodejs 기반)

npm install -D swagger-jsdoc swagger-ui-express

기존 프로젝트에 UI 렌더링을 위한 패키지와 주석에 Swagger 태그를 추가 하여 api 를 문서화하는 패키지를 설치한다. 타입스크립트를 쓴다면 해당 swagger-jsdoc swagger-ui-express를 지원하는 타입을 따로 설치해줘야 바로 작동한다.(기본)

모듈을 추가하고 app.js 파일이나 src 폴더 안에 swagger.js 파일을 추가해서

const options = {
      swaggerDefinition: {
        openapi: '3.0.3',
        info: {
          title: ' product REST API SERVER',
          version: '1.0.0',
          description: 'product API with express',
        },
        servers: [
          {
            url: 'http://example.com/',
          },
        ],
      },
      apis: ['swagger.yaml'],
    };

작성한후 익스포트해서 app.js 받아서 적용시켜주면 일단 셋팅은 끝이다.

const specs = swaggerJSDoc(options);
    this.app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));

이제 실행하면 /api-docs 에 들어가면 기본 아무것도 안한 화면이 나온다.

이제 밑에 api를 작성해주는 작업을 해야 된다. 두가지 방법이 있다. 이 글을 다 읽는 다면 무슨 말인지 이해 하실것이다. 앞서 잠시만 말하자면

 apis: ['swagger.yaml']

위에 옵션 정의 한 부분에서 필자가 이렇게 정의 했는데 이것은 swagger.yml이라는 파일에 api정의 문을 다 떄려 박는 다는 말이고 이제 설명할 부분은 각각의 파일에 정의문을 각각에 파일에 관련된 것만 씀으로서 api기능을 구현함과 동시에 정의문을 쓸수 있어 더 편리하다. 이것은 사람마다 취향이 다르기 떄문에 선택사항이다.

전형적인 mvc 패턴으로 간다면 models에 스키마를 정의 할것인데 스키마 정의 부분에 스웨거 주석으로 api 정의를 할수 있다.

apis: ['./src/models/product.models.ts']

이렇게 정의하고

import mongoose, { model, Schema, Document } from 'mongoose';
import { Product } from '@interfaces/product.interface';
import { autoIdSetter } from '@utils/utils';
import moment from 'moment';
/**
 * @swagger
 *  components:
 *  schemas:
 *   Product:
 *     properties:
 *      _id:
 *        type: string
 *      productNameKO:
 *        type: string
 *      productNameEN:
 *        type: string
 *      productType:
 *        type: object
 *        properties:
 *          selectors:
 *            type: object
 *            properties:
 *              title:
 *                type: string
 *              code:
 *                type: string
 *          price:
 *            type: object
 *            properties:
 *              _id:
 *                type: string
 *              quantity:
 *                type: number
 *              value:
 *                type: number
 */

const productSchema: Schema = new Schema({
  selectors: [{ title: { type: String }, code: { type: String } }],
  price: [{ quantity: { type: Number }, value: { type: Number } }],
});

const productsSchema: Schema = new Schema({
  productNameKO: {
    type: String,
    required: true,
  },
  productNameEN: {
    type: String,
    required: true,
  },
  productTypes: [productSchema],
});

autoIdSetter(productsSchema, mongoose, 'product', 'product_id');

const productModel = model<Product & Document>('product', productsSchema);

export default productModel;

나의 경우에는 몽구스를 사용하고 있기 때문에 이러한 모양을 띄고 있다. 스웨거 주석을 입력할때 띄어쓰기가 정말 중요하다. 한 두번 작성하다 보면 감이 온다. 그리고 잘못되면 에러를 보여주기 때문에 작성하기 좋다. 한 가지 팁을 주자면 하위 요소는 스페이스바 3칸이다. 그런데 위에 쓴거 처럼 작성해도 되더라.

그러면 이렇게 작성이 된다. 그니까 스키마를 정의를 할때 아니면 수정할때 주석이랑 코드랑 건들여주면 api 문서에도 반영이 되고 코드에도 반영이 된다.

이제 컨트롤러에 정의 할줄 차례다.

 /**
   * @swagger
   * paths:
   *   /product:
   *    post:
   *      tags: [제품]
   *      summary: 제품의 명칭과 셀렉트, 카테고리를 POST요청
   *      description: 제품의 국,영문 명칭과 셀렉트, 카테고리를 요청해서 관리자페이지에 랜더
   *      parameters:
   *        - name: productNameKO
   *          in: body
   *          description: 제품 국문 이름
   *          enum: [연필 깍기, 명함]
   *          example: 공구류
   *        - name: productNameEN
   *          in: body
   *          description: 제품 영문 이름 이 부분이 나중에 url 끝부분이 됨
   *          enum: [hotsource]
   *          example: hotsource
   *      responses:
   *        200:
   *          description: OK 들어 간 데이터가 다시 반환
   *          content:
   *            application/json:
   *              schema:
   *                type: array
   *                items:
   *                  $ref: '#/components/schemas/Product'
   *        400:
   *          description: Invalid request
   *        409:
   *          description: Not have that kind of product
   */
  public postProduct = async (req: Request<{}, {}, InputBody, {}>, res: Response, next: NextFunction) => {
      ...

    try {
      ...
      res.status(201).json(postedResult);
    } catch (error) {
      next(error);
    }
  };

회사 실제 코드라 일부 생략해서 썼지만 기본적인 틀은 저렇게 작성하면 문제없이 작동한다.

parameters에 example를 미리 저렇게 기입해주면 swaggerUI상에서 기능을 시험해 볼수 있다.

또한 yaml파일 형식으로 이 swagger 정의를 한 파일에 다가 다 작성할수 있는데 필자가 생각했을땐 조금 비효율적인것 같다. 또한 스웨거 에디터를 참고하면 바로 써보고 확인할수있으니 참고하길 바란다.

profile
只要功夫深,铁杵磨成针

2개의 댓글

comment-user-thumbnail
2022년 2월 24일

요즘 필요한 사항이라 찾아보고있었는데 감사합니다.

1개의 답글