[Express] Swagger API 문서 직접 작성해보기

2023. 1. 18. 09:13
반응형

API 문서의 필요성

서비스의 개발은 기본적으로 팀단위로 이루어진다. 그리고 거의 대부분의 프로젝트들은 서버 백엔드 개발자들이 만들어둔 API를 사용하여 웹파트의 프론트엔드 개발자, 모바일이면 안드로이드, IOS 개발자, 그리고 그 외의 응용 프로그램 개발자들까지 서버의 비즈니스 로직과 데이터베이스의 접근을 위해서 API를 사용한다. 그런데 백엔드 개발자들이 일일이 API 사용법을 알려주기에는 비용적 측면에서 수지타산이 맞지 않다. 그래서 백엔드 개발자들은 API 개발과 함께 체계적인 문서를 다른 팀원들에게 제공해주어야 할 필요성이 있다.

가장 잘 알려진 문서화 도구들은 REST API 개발시에는 Swagger, GraphQL일 시에는 Playground 등이 있다. 

 

OpenAPI 사양 (이전의 Swagger 사양)은 REST API에 대한 API 설명 형식입니다. OpenAPI 파일을 사용하면 다음을 포함하여 전체 API를 설명할 수 있습니다.

 

 


Swagger란?

Swagger 는 REST API를 설계, 구축, 문서화 및 사용하는 데 도움이 되는 OpenAPI 사양을 기반으로 구축된 오픈 소스 도구 세트입니다.  - https://swagger.io/docs/specification/about/

Swagger를 사용해 본 경험으로는 주요 기능으로는 아래와 같은 API의 명세를 정의하는 것이 가능했다.

  • 기능별 URL과 HTTP Method 및 엔드 포인트 
  • 입력 및 출력(Request/Response) 파라미터의 유형과 자료형 및 모델 명시
  • API별 인증 방식 정의

Express에서 사용해보기

그렇다면 간단한 Express Project에서 Swagger를 장착해보려고 한다.

아래의 내용은 직접 Doc를 작성하지만 Swagger 문서화를 자동화해주는 프레임워크나 라이브러리들이 많이 나와있으니 현업에서는 문서화를 자동화하는 것이 좋다. 하지만 해당 과정을 통해 구조를 익히는 것을 목표로 하자. 

 

yarn add swagger-ui-express
yarn add swagger-jsdoc

Swagger를 위한 패키지는 이 두개를 사용한다. npm이나 yarn을 이용하여 설치해주자

 

 

 


프로젝트 구조

간단하게 만들었다.

 

index.js

import express from 'express'
const app = express();
app.use(express.json());    //body의 JSON 파싱

//DB라고 가정
let boardDB= [
    {number: 1, writer: "철수", title: "제목1임", contents: "내용임"},
    {number: 2, writer: "유리", title: "제목2임", contents: "내용임"},
    {number: 3, writer: "훈이", title: "제목3임", contents: "내용임"},
    {number: 4, writer: "맹구", title: "제목4임", contents: "내용임"},
] 

app.get('/boards/:id', (req, res) => {
    //1. 데이터 조회(DB에서 데이터 꺼내오기)
    const id = req.params.id
    const query = req.query.number
    const result = boardDB.find(x => {
        return x.number == id;
    });   //DB 썼다고 가정
    //2. 결과 응답하기
    res.status(200).send(result);
})

app.get('/boards', (req, res) => {
    //1. 데이터 조회(DB에서 데이터 꺼내오기)
    const result = boardDB;   //DB 썼다고 가정
    //2. 결과 응답하기
    res.status(200).send(result);
    
    
})

app.post('/boards', (req, res) => {
    //1. 데이터 등록(DB에서 데이터 저장하기)
    const data = req.body;
    console.log(data);
    if(data.writer && data.title && data.contents){
        const object = {
            number: boardDB.length + 1,
            ...data
        }
        boardDB.push(object)
        //임의로 등록 했다고 가정
    
        //2. 결과 응답하기
        res.status(200).send({
            success: true, 
            message : "게시물 등록 성공"
        });
    }
    else{
        res.status(400).send({
            success: false, 
            message : "요청이 잘못됨"
        });
    }

    
})

app.listen(3000, () => {
    console.log(`Example app listening on port ${3000}`);
})

루트 경로에 index.js 하나를 생성하고 그냥 대충 Express로 게시글 전체조회, 단일조회, 등록하는 API를 만들었다. 야매로 만든지라 라우터 모듈 구분, 예외처리나 데이터베이스는 사용은 하지 않았지만 Swagger 문서화가 목표이므로 그냥 넘어가자 ㅎㅎ..

 

./swagger/config.js

//swaggerJSDoc Option
export const swaggerOptions = {
    definition: {
      openapi: '3.0.0',
      info: {
        title: 'API 문서 작성하기',
        version: '1.0.0',
      },
    },
    apis: ['./swagger/*.swagger.js'], // files containing annotations as above
};

Swagger Doc에 대한 옵션을 설정해 주는 JS파일을 만들어주었다.

apis에는 문서화하려는 대상 파일을 지정해준다. yaml, json, js 등 다양한 포멧을 지원하는 것으로 알고 있으며, 위와 같이 설정하면 ./swagger안의 swagger.js로 끝나는 모든 파일들을 문서화한다. 

 

index.js

//...
app.use(express.json());    //body의 JSON 파싱

/* swagger setup */

//setup에서 쓰일 옵션 생성
const swaggerSpec = swaggerJsDoc(swaggerOptions)
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
/* end of swagger setup */
//...

다시 index.js로 돌아와서 방금 만든 옵션을 Spec으로 설정해주고, /api-docs로 접속 시 API 문서를 제공해주도록 해주자.

여기까지 하면 http://localhost:3000/api-docs로 접속 시 Swagger Document가 보이는 것을 확인해보자

 

이제 안에서 우리가 만든 세개의 API를 문서화해야 한다.

아까 option에서 apis에 지정한 파일 안에 세개의 API 사용 명세서를 작성할 것이다.

 

./swagger/boards.swagger.js

/**
 * @swagger
 * tags:
 *   name: Board
 *   description: 게시판 API
 *
 * @swagger
 * /boards:
 *   get:
 *     summary: 게시글 전체 가져오기
 *     tags: [Board]
 *     responses:
 *       200:
 *         description: 조회 결과
 *         content: 
 *           application/json:
 *              schema:
 *                  type: array
 *                  items: 
 *                      properties: 
 *                          number:
 *                              type: int
 *                              example: 3
 *                              description: 인덱스
 *                          writer:
 *                              type: string
 *                              example: 철수
 *                              description: 작성자
 *                          title:
 *                              type: string
 *                              example: 제목입니다
 *                              description: 제목
 *                          contents:
 *                              type: string
 *                              example: 내용입니다
 *                              description: 내용
 */

게시판 전체 조회 (GET, /boards)에 대한 명세서를 작성하였다. 들여쓰기에 매우 민감하니 들여쓰기를 잘 해주어야 한다.

주요 사항으로는 /boards라는 엔드 포인트를 정의하고, 그 아래에 HTTP Method를 명시하였다.

그리고 responses에 있는 200 아래의 내용은 HTTP Status Code가 200일 경우의 Response의 포멧에 대한 내용을 정의한 것이며, 해석해보면 application/json 형태의 Response가 반환될 것이며 타입은 number, writer, title, contents가 포함된 Object의 Array 형태일 것이다 라고 작성한 것이다.

 

그리고 나서 실행해보면 아래처럼 API 문서가 작성된 것을 확인할수 있다.

Response의 예시와 스키마를 제공해준다.
Swagger에서 실제 Try 결과

그러면 이제 나머지 2개의 API도 문서화해보자.

 

./swagger/boards.swagger.js


 * @swagger
 * /boards/{id}:
 *   get:
 *     summary: 게시글 가져오기
 *     tags: [Board]
 *     parameters:
 *         - in: query
 *           name: number
 *           type: int
 *         - in: path
 *           name: id
 *           type: int
 *     responses:
 *       200:
 *         description: 조회 결과
 *         content: 
 *           application/json:
 *              schema: 
 *                  $ref: "#/definitions/BoardResponseModel"

다음은 게시판 일부 조회 API이다.

parameters 속성에서 Input을 정의할 수 있다. Query Parameter(/boards?number=10)과 Path Parameter(/boards/10)의 API 문서 정의 방법을 둘다 적어두었다.

Path 파라미터가 사용되면 다음과 같이 엔드 포인트에 {}로 정의한다.

특이점으로는 schema에 아까처럼 직접 적어준 것이 아니라 $ref를 통해 다른 값을 참조하여 작성했다.

 

./swagger/boards.swagger.js


 * tags:
 *   name: Board
 *   description: 게시판 API
 * definitions:
 *   BoardResponseModel:
 *     type: object
 *     required:
 *       - id
 *       - writer
 *       - title
 *       - contents
 *     properties:
 *       id:
 *         type: int
 *         description: 인덱스
 *         example: 3
 *       writer:
 *         type: string
 *         description: 작성자
 *         example: 철수
 *       title:
 *         type: string
 *         description: 제목
 *         example: 제목입니다
 *       contents:
 *         type: string
 *         description: 내용
 *         example: 내용입니다

다음과 같이 위쪽에 definitions라는 속성을 정의하여 모델을 선언해주었다. Object 형태이며, 필수요소와 프로퍼티들을 정의하여주었으며 이는 위에서 한 것 처럼 $ref를 통하여 직접 참조하게 할 수 있다. 마지막 api도 완성해보자.

 

./swagger/boards.swagger.js

/**
 * @swagger
 * tags:
 *   name: Board
 *   description: 게시판 API
 * definitions:
 *   BoardRequestModel:
 *     type: object
 *     required:
 *       - writer
 *       - title
 *       - contents
 *     properties:
 *       writer:
 *         type: string
 *         description: 작성자
 *       title:
 *         type: string
 *         description: 제목
 *       contents:
 *         type: string
 *         description: 내용
 *   BoardResponseModel:
 *     type: object
 *     required:
 *       - id
 *       - writer
 *       - title
 *       - contents
 *     properties:
 *       id:
 *         type: int
 *         description: 인덱스
 *         example: 3
 *       writer:
 *         type: string
 *         description: 작성자
 *         example: 철수
 *       title:
 *         type: string
 *         description: 제목
 *         example: 제목입니다
 *       contents:
 *         type: string
 *         description: 내용
 *         example: 내용입니다
 *   ResponseModel:
 *     type: object
 *     required:
 *       - success
 *       - message
 *     properties:
 *       success:
 *         type: boolean
 *         description: 성공여부
 *         example: true
 *       message:
 *         type: string
 *         description: 메시지
 *         example: 성공하였습니다.
 * 
 * @swagger
 * /boards:
 *   get:
 *     summary: 게시글 전체 가져오기
 *     tags: [Board]
 *     responses:
 *       200:
 *         description: 조회 결과
 *         content: 
 *           application/json:
 *              schema:
 *                  type: array
 *                  items: 
 *                      properties: 
 *                          number:
 *                              type: int
 *                              example: 3
 *                              description: 인덱스
 *                          writer:
 *                              type: string
 *                              example: 철수
 *                              description: 작성자
 *                          title:
 *                              type: string
 *                              example: 제목입니다
 *                              description: 제목
 *                          contents:
 *                              type: string
 *                              example: 내용입니다
 *                              description: 내용
 * 
 * @swagger
 * /boards/{id}:
 *   get:
 *     summary: 게시글 가져오기
 *     tags: [Board]
 *     parameters:
 *         - in: query
 *           name: number
 *           type: int
 *         - in: path
 *           name: id
 *           type: int
 *     responses:
 *       200:
 *         description: 조회 결과
 *         content: 
 *           application/json:
 *              schema: 
 *                  $ref: "#/definitions/BoardResponseModel"
 * 
 *
 *  @swagger
 *  paths:
 *    /boards:
 *      post:
 *        tags:
 *        - "Board"
 *        summary: "게시글 쓰기"
 * 
 *        description: ""
 *        consumes:
 *          - "application/json"
 *        requestBody:
 *          x-name: body
 *          required: true
 *          content:
 *              application/json: 
 *                  schema:
 *                      $ref: "#/definitions/BoardRequestModel"
 *        responses:
 *          200:
 *            description: "결과"
 *            content: 
 *              application/json:
 *                  schema:
 *                      $ref: "#/definitions/ResponseModel"
 */

전체 완성 파일이다. Post하여 게시글을 등록하는 API 문서 작성이 추가되었으며, Request와 Response로 받을 스키마를 definitions에 정의하였다.

 

 

 

 

RequestBody를 사용하려면 아래와 같이 작성해주자. application.json 포멧의 Request Body의 스키마를 정의해주었다.

 *        requestBody:
 *          x-name: body
 *          required: true
 *          content:
 *              application/json: 
 *                  schema:
 *                      $ref: "#/definitions/BoardRequestModel"

 

결과

API의 Request/Response 포멧 정의
Try 결과
API의 Request/Response 포멧 정의
Try 결과

 

 

 

Swagger-DOCS

지금 작성한 내용은 Swagger에서 제공하는 기능 중 일부이며, 자세한 내용들은 공식 문서를 참조하면 좋다. 

https://swagger.io/docs/specification/about/

 

About Swagger Specification | Documentation | Swagger

What Is OpenAPI? OpenAPI Specification (formerly Swagger Specification) is an API description format for REST APIs. An OpenAPI file allows you to describe your entire API, including: Available endpoints (/users) and operations on each endpoint (GET /users,

swagger.io

 

반응형

BELATED ARTICLES

more