From d056127017df0d6beb34ee606e2dd89d983b548a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B9=80=EA=B8=B0=EC=B0=BD?= <sperr@ajou.ac.kr>
Date: Fri, 26 May 2023 16:50:43 +0900
Subject: [PATCH] engineeo migration

---
 .gitignore                                    |    41 +
 @types/global.d.ts                            |    14 +
 Dockerfile                                    |    20 +
 README.md                                     |    73 +
 _old/.dockerignore                            |     9 +
 _old/.gitignore                               |    11 +
 _old/.prettierrc                              |    10 +
 _old/Dockerfile                               |    11 +
 _old/Dockerfile.dev                           |    11 +
 _old/appspec.yml                              |    10 +
 _old/config.zip                               |   Bin 0 -> 60695 bytes
 _old/config/admission.js                      |    23 +
 _old/config/authCheck.js                      |    90 +
 _old/config/baseResponseStatus.js             |   485 +
 _old/config/cache.js                          |    15 +
 _old/config/constant.js                       |    11 +
 _old/config/database.js                       |    14 +
 _old/config/express.js                        |    50 +
 _old/config/fcmSecret.json                    |    12 +
 _old/config/imPortToken.js                    |    27 +
 _old/config/jwt.js                            |    42 +
 _old/config/response.js                       |    18 +
 _old/config/s3.js                             |    38 +
 _old/config/swagger.json                      | 16625 ++++++++++++++++
 _old/config/winston.js                        |    43 +
 _old/ecosystem.config.js                      |    20 +
 _old/install.sh                               |     8 +
 _old/middlewares/authCheck.js                 |    68 +
 _old/middlewares/errorHandler.js              |    35 +
 _old/middlewares/jwtMiddleware.js             |    42 +
 _old/middlewares/makeAuthLog.js               |    49 +
 _old/package-lock.json                        | 11246 +++++++++++
 _old/package.json                             |    76 +
 _old/src/Admin/adminController.js             |  3634 ++++
 _old/src/Admin/adminDao.js                    |  3430 ++++
 _old/src/Admin/adminProvider.js               |  1857 ++
 _old/src/Admin/adminRoute.js                  |   372 +
 _old/src/Admin/adminService.js                |  1583 ++
 _old/src/Admin/companyExam/companyRouter.js   |    49 +
 .../subjectTiveExam/subjectTiveExamRoute.js   |    19 +
 _old/src/Calendar/calendarController.js       |   375 +
 _old/src/Calendar/calendarDao.js              |   280 +
 _old/src/Calendar/calendarProvider.js         |   152 +
 _old/src/Calendar/calendarRoute.js            |    19 +
 _old/src/Calendar/calendarService.js          |   494 +
 _old/src/ChannelTalk/channelTalkController.js |    38 +
 _old/src/ChannelTalk/channelTalkRoute.js      |    10 +
 _old/src/ChannelTalk/channelTalkService.js    |    39 +
 _old/src/Community/communityController.js     |   136 +
 _old/src/Community/communityDao.js            |   170 +
 _old/src/Community/communityProvider.js       |   111 +
 _old/src/Community/communityRoute.js          |    25 +
 _old/src/Community/communityService.js        |    78 +
 _old/src/Exam/companyExam/companyRouter.js    |    32 +
 _old/src/Exam/examController.js               |   813 +
 _old/src/Exam/examDao.js                      |  1588 ++
 _old/src/Exam/examProvider.js                 |  1137 ++
 _old/src/Exam/examRoute.js                    |    92 +
 _old/src/Exam/examService.js                  |   158 +
 .../Exam/subjectiveExam/subjectivExamRoute.js |    11 +
 _old/src/Restore/restoreController.js         |   538 +
 _old/src/Restore/restoreDao.js                |   543 +
 _old/src/Restore/restoreProvider.js           |   580 +
 _old/src/Restore/restoreRoute.js              |    72 +
 _old/src/Restore/restoreService.js            |   252 +
 _old/src/Store/store.spec.js                  |    78 +
 _old/src/Store/storeController.js             |   529 +
 _old/src/Store/storeDao.js                    |   751 +
 _old/src/Store/storeProvider.js               |   593 +
 _old/src/Store/storeRoute.js                  |    57 +
 _old/src/Store/storeService.js                |   204 +
 .../subjectiveUserRoute.js                    |    33 +
 _old/src/User/userController.js               |  1913 ++
 _old/src/User/userDao.js                      |  1672 ++
 _old/src/User/userProvider.js                 |  1690 ++
 _old/src/User/userRoute.js                    |   168 +
 _old/src/User/userService.js                  |   645 +
 _old/utils/adminAuthenticator.js              |    15 +
 _old/utils/asyncHandler.js                    |     7 +
 _old/utils/errorResponse.js                   |    11 +
 buildspec/build.yml                           |    36 +
 config/database.js                            |    26 +
 config/email.js                               |    13 +
 config/secret.js                              |    33 +
 config/swagger.js                             |    67 +
 nest-cli.json                                 |     8 +
 package-lock.json                             | 14979 ++++++++++++++
 package.json                                  |   136 +
 prisma/migrations/0_init/migration.sql        |   978 +
 .../migration.sql                             |   128 +
 .../migration.sql                             |    78 +
 .../migration.sql                             |    11 +
 .../migration.sql                             |    14 +
 .../migration.sql                             |     2 +
 .../migration.sql                             |     8 +
 .../migration.sql                             |     8 +
 .../migration.sql                             |    18 +
 .../migration.sql                             |     2 +
 .../migration.sql                             |     2 +
 .../migration.sql                             |     2 +
 prisma/migrations/migration_lock.toml         |     3 +
 prisma/schema.prisma                          |   660 +
 src/admin/admin.controller.ts                 |    84 +
 src/admin/admin.module.ts                     |    13 +
 src/admin/admin.service.ts                    |    26 +
 .../dtos/query/restore-exam-details.dto.ts    |    10 +
 src/app.controller.spec.ts                    |    22 +
 src/app.controller.ts                         |    12 +
 src/app.module.ts                             |   102 +
 src/app.service.ts                            |     8 +
 .../attached-attachment.module.ts             |     8 +
 .../attached-attachment.service.ts            |    14 +
 .../dtos/attached-attachment.dto.ts           |     8 +
 .../entities/attached-attachment.entity.ts    |     9 +
 src/attachment/attachment.controller.ts       |   176 +
 src/attachment/attachment.module.ts           |    12 +
 src/attachment/attachment.service.ts          |   180 +
 .../dtos/req/create-attachment.dto.ts         |    21 +
 src/attachment/dtos/res/attachment.dto.ts     |    62 +
 .../dtos/res/prepare-upload-response.dto.ts   |    21 +
 .../dtos/res/presigned-url-redirect.dto.ts    |    18 +
 .../dtos/res/presigned-url-response.dto.ts    |    11 +
 src/attachment/entities/attachment.entity.ts  |    14 +
 src/attachment/enums/attachment-model.enum.ts |     4 +
 src/attachment/enums/upload-state.enum.ts     |     5 +
 src/auth/auth.controller.ts                   |    52 +
 src/auth/auth.module.ts                       |    27 +
 src/auth/auth.service.ts                      |    52 +
 src/auth/dtos/login-response.dto.ts           |     9 +
 src/auth/dtos/login.dto.ts                    |    17 +
 src/auth/guards/jwt-auth.guard.ts             |     5 +
 src/auth/guards/local-auth.guard.ts           |     5 +
 src/auth/strategies/jwt.strategy.ts           |    24 +
 src/auth/strategies/local.strategy.ts         |    40 +
 src/common/constants/attachment.constant.ts   |     2 +
 src/common/constants/comment.constant.ts      |     3 +
 src/common/constants/exam.constant.ts         |     1 +
 src/common/constants/http.constant.ts         |     9 +
 src/common/constants/post.constant.ts         |     1 +
 src/common/constants/user.constant.ts         |     9 +
 src/common/constants/validation.constant.ts   |     1 +
 src/common/decorators/self.decorator.ts       |     8 +
 src/common/entities/base.entity.ts            |    15 +
 src/common/enums/exam-relation-type.enum.ts   |     5 +
 src/common/enums/exam-type.enum.ts            |     4 +
 .../enums/post-recommend-amount.enum.ts       |     4 +
 src/common/enums/public-status.enum.ts        |     4 +
 src/common/enums/status.enum.ts               |     4 +
 src/common/errors/validation-http-error.ts    |    24 +
 src/common/filters/http-exception.filter.ts   |    59 +
 .../filters/unhandled-exception.filter.ts     |    29 +
 src/common/guards/admin.guard.ts              |    30 +
 src/common/guards/author.guard.ts             |    56 +
 .../interceptors/serialize.interceptor.ts     |    51 +
 src/common/paginate/page-meta.dto.ts          |    31 +
 src/common/paginate/paginate-query.dto.ts     |    20 +
 src/common/paginate/paginated.dto.ts          |    19 +
 src/common/pipes/custom-validation.pipe.ts    |    26 +
 src/common/utils/hash-password.util.ts        |     5 +
 src/common/utils/multer.util.ts               |    29 +
 src/common/utils/regex.util.ts                |     3 +
 src/common/utils/validate-result.util.ts      |     7 +
 src/company/company.module.ts                 |     4 +
 src/company/company.service.ts                |     0
 src/config/aws.config.ts                      |     6 +
 src/config/config.schema.ts                   |    38 +
 src/config/database.config.ts                 |    11 +
 src/config/jwt.config.ts                      |     6 +
 src/config/ncp.config.ts                      |     8 +
 src/config/s3.config.ts                       |     6 +
 src/exam/dtos/req/get-exam-field.dto.ts       |     8 +
 src/exam/dtos/req/get-exam.dto.ts             |    12 +
 src/exam/dtos/res/exam.dto.ts                 |   126 +
 .../dtos/res/get-exam-info-response.dto.ts    |    27 +
 .../res/get-exam-sub-category-response.dto.ts |    10 +
 src/exam/entities/exam.entity.ts              |    26 +
 src/exam/exam.controller.ts                   |    94 +
 src/exam/exam.module.ts                       |    15 +
 src/exam/exam.service.ts                      |   219 +
 src/examDetail/dtos/res/exam-detail.dto.ts    |    54 +
 src/examDetail/entities/exam-detail.entity.ts |    15 +
 src/examDetail/exam-detail.controller.ts      |    93 +
 src/examDetail/exam-detail.module.ts          |    12 +
 src/examDetail/exam-detail.service.ts         |    23 +
 .../dtos/res/exam-relation.dto.ts             |    58 +
 .../entities/exam-relation.entity.ts          |    12 +
 src/examRelation/exam-relation.module.ts      |     8 +
 src/examRelation/exam-relation.service.ts     |    55 +
 src/examsubject/dtos/exam-subject.dto.ts      |    57 +
 .../entities/exam-subject.entity.ts           |     9 +
 src/examsubject/exam-subject.module.ts        |     8 +
 src/examsubject/exam-subject.service.ts       |    25 +
 .../dtos/exam-subject-multi.dto.ts            |    62 +
 .../entities/exam-subject-multi.entity.ts     |    14 +
 src/file/file.controller.ts                   |    21 +
 src/file/file.module.ts                       |    19 +
 src/file/file.service.ts                      |    12 +
 src/main.ts                                   |    58 +
 src/payment/entities/payment.entity.ts        |    21 +
 src/payment/enums/payment-level.enum.ts       |     5 +
 src/payment/payment.module.ts                 |    10 +
 src/payment/payment.service.ts                |    66 +
 .../entities/payment-product.entity.ts        |    17 +
 src/paymentProduct/payment-product.module.ts  |     9 +
 src/prisma/prisma.module.ts                   |     9 +
 src/prisma/prisma.service.ts                  |    11 +
 src/problem/problem.module.ts                 |     4 +
 src/product/dtos/res/product.dto.ts           |    99 +
 src/product/entities/product.entity.ts        |    16 +
 src/product/enums/product.enum.ts             |     4 +
 src/product/product.controller.ts             |    45 +
 src/product/product.module.ts                 |    11 +
 src/product/product.service.ts                |   105 +
 src/restore/restore.controller.ts             |    13 +
 src/restore/restore.module.ts                 |    12 +
 .../dtos/req/create-comment.dto.ts            |    16 +
 .../dtos/req/delete-comment.dto.ts            |    12 +
 .../dtos/req/update-comment.dto.ts            |    12 +
 .../dtos/res/restore-comment.dto.ts           |   102 +
 .../entities/restore-comment.entity.ts        |    19 +
 .../restore-comment-controller.ts             |   200 +
 src/restoreComment/restore-comment-service.ts |   212 +
 src/restoreComment/restore-comment.module.ts  |    13 +
 .../dtos/query/get-restore-posts-query.dto.ts |    18 +
 .../dtos/req/create-restore-post.dto.ts       |    41 +
 src/restorePost/dtos/res/restore-post.dto.ts  |   139 +
 .../dtos/res/restore-posts-response.dto.ts    |    20 +
 .../entities/restore-post.entity.ts           |    27 +
 src/restorePost/restore-post.controller.ts    |   222 +
 src/restorePost/restore-post.module.ts        |    18 +
 src/restorePost/restore-post.service.ts       |   340 +
 .../dtos/res/restore-question.dto.ts          |    49 +
 .../entities/restore-question.entity.ts       |    12 +
 .../entities/restore-recommend-log.entity.ts  |     9 +
 .../restore-recommend-log.controller.ts       |    73 +
 .../restore-recommend-log.module.ts           |    12 +
 .../restore-recommend-log.service.ts          |   126 +
 src/s3-manager/s3-manager.module.ts           |    10 +
 src/s3-manager/s3-manager.service.ts          |    30 +
 src/store/store.module.ts                     |     4 +
 src/user/decorators/current-user.decorator.ts |    15 +
 .../query/user-purchase-history-query.dto.ts  |    18 +
 src/user/dtos/req/email-recovery.dto.ts       |    16 +
 src/user/dtos/req/forgot-password.dto.ts      |    19 +
 src/user/dtos/req/update-user-cart.dto.ts     |    13 +
 src/user/dtos/res/my-exams-response.dto.ts    |    74 +
 src/user/dtos/res/user-cart-response.dto.ts   |    28 +
 .../res/user-purchase-history-response.dto.ts |    16 +
 src/user/dtos/res/user.dto.ts                 |    98 +
 src/user/dtos/update-user.dto.ts              |    36 +
 src/user/entities/user.entity.ts              |    23 +
 src/user/enums/user-provider.enum.ts          |     4 +
 src/user/enums/user-status.enum.ts            |     7 +
 src/user/user.controller.ts                   |   343 +
 src/user/user.module.ts                       |    14 +
 src/user/user.service.spec.ts                 |   156 +
 src/user/user.service.ts                      |   254 +
 src/userAuth/entities/user-auth.entity.ts     |    13 +
 src/userAuth/user-auth.module.ts              |    10 +
 src/userAuth/user-auth.service.ts             |    55 +
 src/userCart/entities/user-cart.entity.ts     |    12 +
 src/userCart/user-cart.module.ts              |    10 +
 src/userCart/user-cart.service.ts             |    91 +
 .../constants/user-recommend-code.constant.ts |     2 +
 .../dtos/user-recommend-code-response.dto.ts  |    22 +
 .../user-recommend-code-use-response.dto.ts   |    28 +
 .../entities/user-recommend-log.entity.ts     |    13 +
 .../enums/user-point-type.enum.ts             |     4 +
 .../user-recommend.controller.spec.ts         |    18 +
 .../user-recommend.controller.ts              |    89 +
 src/userRecommend/user-recommend.module.ts    |    20 +
 .../user-recommend.service.spec.ts            |    18 +
 src/userRecommend/user-recommend.service.ts   |   164 +
 test/app.e2e-spec.ts                          |    24 +
 test/jest-e2e.json                            |     9 +
 tsconfig.build.json                           |     4 +
 tsconfig.json                                 |    24 +
 277 files changed, 80230 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 @types/global.d.ts
 create mode 100644 Dockerfile
 create mode 100644 _old/.dockerignore
 create mode 100644 _old/.gitignore
 create mode 100644 _old/.prettierrc
 create mode 100644 _old/Dockerfile
 create mode 100644 _old/Dockerfile.dev
 create mode 100644 _old/appspec.yml
 create mode 100644 _old/config.zip
 create mode 100644 _old/config/admission.js
 create mode 100644 _old/config/authCheck.js
 create mode 100644 _old/config/baseResponseStatus.js
 create mode 100644 _old/config/cache.js
 create mode 100644 _old/config/constant.js
 create mode 100644 _old/config/database.js
 create mode 100644 _old/config/express.js
 create mode 100644 _old/config/fcmSecret.json
 create mode 100644 _old/config/imPortToken.js
 create mode 100644 _old/config/jwt.js
 create mode 100644 _old/config/response.js
 create mode 100644 _old/config/s3.js
 create mode 100644 _old/config/swagger.json
 create mode 100644 _old/config/winston.js
 create mode 100644 _old/ecosystem.config.js
 create mode 100644 _old/install.sh
 create mode 100644 _old/middlewares/authCheck.js
 create mode 100644 _old/middlewares/errorHandler.js
 create mode 100644 _old/middlewares/jwtMiddleware.js
 create mode 100644 _old/middlewares/makeAuthLog.js
 create mode 100644 _old/package-lock.json
 create mode 100644 _old/package.json
 create mode 100644 _old/src/Admin/adminController.js
 create mode 100644 _old/src/Admin/adminDao.js
 create mode 100644 _old/src/Admin/adminProvider.js
 create mode 100644 _old/src/Admin/adminRoute.js
 create mode 100644 _old/src/Admin/adminService.js
 create mode 100644 _old/src/Admin/companyExam/companyRouter.js
 create mode 100644 _old/src/Admin/subjectTiveExam/subjectTiveExamRoute.js
 create mode 100644 _old/src/Calendar/calendarController.js
 create mode 100644 _old/src/Calendar/calendarDao.js
 create mode 100644 _old/src/Calendar/calendarProvider.js
 create mode 100644 _old/src/Calendar/calendarRoute.js
 create mode 100644 _old/src/Calendar/calendarService.js
 create mode 100644 _old/src/ChannelTalk/channelTalkController.js
 create mode 100644 _old/src/ChannelTalk/channelTalkRoute.js
 create mode 100644 _old/src/ChannelTalk/channelTalkService.js
 create mode 100644 _old/src/Community/communityController.js
 create mode 100644 _old/src/Community/communityDao.js
 create mode 100644 _old/src/Community/communityProvider.js
 create mode 100644 _old/src/Community/communityRoute.js
 create mode 100644 _old/src/Community/communityService.js
 create mode 100644 _old/src/Exam/companyExam/companyRouter.js
 create mode 100644 _old/src/Exam/examController.js
 create mode 100644 _old/src/Exam/examDao.js
 create mode 100644 _old/src/Exam/examProvider.js
 create mode 100644 _old/src/Exam/examRoute.js
 create mode 100644 _old/src/Exam/examService.js
 create mode 100644 _old/src/Exam/subjectiveExam/subjectivExamRoute.js
 create mode 100644 _old/src/Restore/restoreController.js
 create mode 100644 _old/src/Restore/restoreDao.js
 create mode 100644 _old/src/Restore/restoreProvider.js
 create mode 100644 _old/src/Restore/restoreRoute.js
 create mode 100644 _old/src/Restore/restoreService.js
 create mode 100644 _old/src/Store/store.spec.js
 create mode 100644 _old/src/Store/storeController.js
 create mode 100644 _old/src/Store/storeDao.js
 create mode 100644 _old/src/Store/storeProvider.js
 create mode 100644 _old/src/Store/storeRoute.js
 create mode 100644 _old/src/Store/storeService.js
 create mode 100644 _old/src/User/subjectiveUserRoute.js/subjectiveUserRoute.js
 create mode 100644 _old/src/User/userController.js
 create mode 100644 _old/src/User/userDao.js
 create mode 100644 _old/src/User/userProvider.js
 create mode 100644 _old/src/User/userRoute.js
 create mode 100644 _old/src/User/userService.js
 create mode 100644 _old/utils/adminAuthenticator.js
 create mode 100644 _old/utils/asyncHandler.js
 create mode 100644 _old/utils/errorResponse.js
 create mode 100644 buildspec/build.yml
 create mode 100644 config/database.js
 create mode 100644 config/email.js
 create mode 100644 config/secret.js
 create mode 100644 config/swagger.js
 create mode 100644 nest-cli.json
 create mode 100644 package-lock.json
 create mode 100644 package.json
 create mode 100644 prisma/migrations/0_init/migration.sql
 create mode 100644 prisma/migrations/20230424071857_remove_deprecated_table/migration.sql
 create mode 100644 prisma/migrations/20230424075555_add_restore_models/migration.sql
 create mode 100644 prisma/migrations/20230425021150_change_restore_model_string_type_text/migration.sql
 create mode 100644 prisma/migrations/20230425073348_add_attachment_model/migration.sql
 create mode 100644 prisma/migrations/20230426061654_add_restore_post_approved_at_column/migration.sql
 create mode 100644 prisma/migrations/20230426083751_remove_attachment_path/migration.sql
 create mode 100644 prisma/migrations/20230427054536_add_restore_post_attachment_relation/migration.sql
 create mode 100644 prisma/migrations/20230508070151_add_attachment_pivot_model/migration.sql
 create mode 100644 prisma/migrations/20230510092422_add_restore_post_deleted_at_column/migration.sql
 create mode 100644 prisma/migrations/20230511044552_add_index_attached_attachment_model_attached_column/migration.sql
 create mode 100644 prisma/migrations/20230515061438_add_foreign_key_attachment_column_in_attached_attachment/migration.sql
 create mode 100644 prisma/migrations/migration_lock.toml
 create mode 100644 prisma/schema.prisma
 create mode 100644 src/admin/admin.controller.ts
 create mode 100644 src/admin/admin.module.ts
 create mode 100644 src/admin/admin.service.ts
 create mode 100644 src/admin/dtos/query/restore-exam-details.dto.ts
 create mode 100644 src/app.controller.spec.ts
 create mode 100644 src/app.controller.ts
 create mode 100644 src/app.module.ts
 create mode 100644 src/app.service.ts
 create mode 100644 src/attachedAttachment/attached-attachment.module.ts
 create mode 100644 src/attachedAttachment/attached-attachment.service.ts
 create mode 100644 src/attachedAttachment/dtos/attached-attachment.dto.ts
 create mode 100644 src/attachedAttachment/entities/attached-attachment.entity.ts
 create mode 100644 src/attachment/attachment.controller.ts
 create mode 100644 src/attachment/attachment.module.ts
 create mode 100644 src/attachment/attachment.service.ts
 create mode 100644 src/attachment/dtos/req/create-attachment.dto.ts
 create mode 100644 src/attachment/dtos/res/attachment.dto.ts
 create mode 100644 src/attachment/dtos/res/prepare-upload-response.dto.ts
 create mode 100644 src/attachment/dtos/res/presigned-url-redirect.dto.ts
 create mode 100644 src/attachment/dtos/res/presigned-url-response.dto.ts
 create mode 100644 src/attachment/entities/attachment.entity.ts
 create mode 100644 src/attachment/enums/attachment-model.enum.ts
 create mode 100644 src/attachment/enums/upload-state.enum.ts
 create mode 100644 src/auth/auth.controller.ts
 create mode 100644 src/auth/auth.module.ts
 create mode 100644 src/auth/auth.service.ts
 create mode 100644 src/auth/dtos/login-response.dto.ts
 create mode 100644 src/auth/dtos/login.dto.ts
 create mode 100644 src/auth/guards/jwt-auth.guard.ts
 create mode 100644 src/auth/guards/local-auth.guard.ts
 create mode 100644 src/auth/strategies/jwt.strategy.ts
 create mode 100644 src/auth/strategies/local.strategy.ts
 create mode 100644 src/common/constants/attachment.constant.ts
 create mode 100644 src/common/constants/comment.constant.ts
 create mode 100644 src/common/constants/exam.constant.ts
 create mode 100644 src/common/constants/http.constant.ts
 create mode 100644 src/common/constants/post.constant.ts
 create mode 100644 src/common/constants/user.constant.ts
 create mode 100644 src/common/constants/validation.constant.ts
 create mode 100644 src/common/decorators/self.decorator.ts
 create mode 100644 src/common/entities/base.entity.ts
 create mode 100644 src/common/enums/exam-relation-type.enum.ts
 create mode 100644 src/common/enums/exam-type.enum.ts
 create mode 100644 src/common/enums/post-recommend-amount.enum.ts
 create mode 100644 src/common/enums/public-status.enum.ts
 create mode 100644 src/common/enums/status.enum.ts
 create mode 100644 src/common/errors/validation-http-error.ts
 create mode 100644 src/common/filters/http-exception.filter.ts
 create mode 100644 src/common/filters/unhandled-exception.filter.ts
 create mode 100644 src/common/guards/admin.guard.ts
 create mode 100644 src/common/guards/author.guard.ts
 create mode 100644 src/common/interceptors/serialize.interceptor.ts
 create mode 100644 src/common/paginate/page-meta.dto.ts
 create mode 100644 src/common/paginate/paginate-query.dto.ts
 create mode 100644 src/common/paginate/paginated.dto.ts
 create mode 100644 src/common/pipes/custom-validation.pipe.ts
 create mode 100644 src/common/utils/hash-password.util.ts
 create mode 100644 src/common/utils/multer.util.ts
 create mode 100644 src/common/utils/regex.util.ts
 create mode 100644 src/common/utils/validate-result.util.ts
 create mode 100644 src/company/company.module.ts
 create mode 100644 src/company/company.service.ts
 create mode 100644 src/config/aws.config.ts
 create mode 100644 src/config/config.schema.ts
 create mode 100644 src/config/database.config.ts
 create mode 100644 src/config/jwt.config.ts
 create mode 100644 src/config/ncp.config.ts
 create mode 100644 src/config/s3.config.ts
 create mode 100644 src/exam/dtos/req/get-exam-field.dto.ts
 create mode 100644 src/exam/dtos/req/get-exam.dto.ts
 create mode 100644 src/exam/dtos/res/exam.dto.ts
 create mode 100644 src/exam/dtos/res/get-exam-info-response.dto.ts
 create mode 100644 src/exam/dtos/res/get-exam-sub-category-response.dto.ts
 create mode 100644 src/exam/entities/exam.entity.ts
 create mode 100644 src/exam/exam.controller.ts
 create mode 100644 src/exam/exam.module.ts
 create mode 100644 src/exam/exam.service.ts
 create mode 100644 src/examDetail/dtos/res/exam-detail.dto.ts
 create mode 100644 src/examDetail/entities/exam-detail.entity.ts
 create mode 100644 src/examDetail/exam-detail.controller.ts
 create mode 100644 src/examDetail/exam-detail.module.ts
 create mode 100644 src/examDetail/exam-detail.service.ts
 create mode 100644 src/examRelation/dtos/res/exam-relation.dto.ts
 create mode 100644 src/examRelation/entities/exam-relation.entity.ts
 create mode 100644 src/examRelation/exam-relation.module.ts
 create mode 100644 src/examRelation/exam-relation.service.ts
 create mode 100644 src/examsubject/dtos/exam-subject.dto.ts
 create mode 100644 src/examsubject/entities/exam-subject.entity.ts
 create mode 100644 src/examsubject/exam-subject.module.ts
 create mode 100644 src/examsubject/exam-subject.service.ts
 create mode 100644 src/examsubjectmulti/dtos/exam-subject-multi.dto.ts
 create mode 100644 src/examsubjectmulti/entities/exam-subject-multi.entity.ts
 create mode 100644 src/file/file.controller.ts
 create mode 100644 src/file/file.module.ts
 create mode 100644 src/file/file.service.ts
 create mode 100644 src/main.ts
 create mode 100644 src/payment/entities/payment.entity.ts
 create mode 100644 src/payment/enums/payment-level.enum.ts
 create mode 100644 src/payment/payment.module.ts
 create mode 100644 src/payment/payment.service.ts
 create mode 100644 src/paymentProduct/entities/payment-product.entity.ts
 create mode 100644 src/paymentProduct/payment-product.module.ts
 create mode 100644 src/prisma/prisma.module.ts
 create mode 100644 src/prisma/prisma.service.ts
 create mode 100644 src/problem/problem.module.ts
 create mode 100644 src/product/dtos/res/product.dto.ts
 create mode 100644 src/product/entities/product.entity.ts
 create mode 100644 src/product/enums/product.enum.ts
 create mode 100644 src/product/product.controller.ts
 create mode 100644 src/product/product.module.ts
 create mode 100644 src/product/product.service.ts
 create mode 100644 src/restore/restore.controller.ts
 create mode 100644 src/restore/restore.module.ts
 create mode 100644 src/restoreComment/dtos/req/create-comment.dto.ts
 create mode 100644 src/restoreComment/dtos/req/delete-comment.dto.ts
 create mode 100644 src/restoreComment/dtos/req/update-comment.dto.ts
 create mode 100644 src/restoreComment/dtos/res/restore-comment.dto.ts
 create mode 100644 src/restoreComment/entities/restore-comment.entity.ts
 create mode 100644 src/restoreComment/restore-comment-controller.ts
 create mode 100644 src/restoreComment/restore-comment-service.ts
 create mode 100644 src/restoreComment/restore-comment.module.ts
 create mode 100644 src/restorePost/dtos/query/get-restore-posts-query.dto.ts
 create mode 100644 src/restorePost/dtos/req/create-restore-post.dto.ts
 create mode 100644 src/restorePost/dtos/res/restore-post.dto.ts
 create mode 100644 src/restorePost/dtos/res/restore-posts-response.dto.ts
 create mode 100644 src/restorePost/entities/restore-post.entity.ts
 create mode 100644 src/restorePost/restore-post.controller.ts
 create mode 100644 src/restorePost/restore-post.module.ts
 create mode 100644 src/restorePost/restore-post.service.ts
 create mode 100644 src/restoreQuestion/dtos/res/restore-question.dto.ts
 create mode 100644 src/restoreQuestion/entities/restore-question.entity.ts
 create mode 100644 src/restoreRecommendLog/entities/restore-recommend-log.entity.ts
 create mode 100644 src/restoreRecommendLog/restore-recommend-log.controller.ts
 create mode 100644 src/restoreRecommendLog/restore-recommend-log.module.ts
 create mode 100644 src/restoreRecommendLog/restore-recommend-log.service.ts
 create mode 100644 src/s3-manager/s3-manager.module.ts
 create mode 100644 src/s3-manager/s3-manager.service.ts
 create mode 100644 src/store/store.module.ts
 create mode 100644 src/user/decorators/current-user.decorator.ts
 create mode 100644 src/user/dtos/query/user-purchase-history-query.dto.ts
 create mode 100644 src/user/dtos/req/email-recovery.dto.ts
 create mode 100644 src/user/dtos/req/forgot-password.dto.ts
 create mode 100644 src/user/dtos/req/update-user-cart.dto.ts
 create mode 100644 src/user/dtos/res/my-exams-response.dto.ts
 create mode 100644 src/user/dtos/res/user-cart-response.dto.ts
 create mode 100644 src/user/dtos/res/user-purchase-history-response.dto.ts
 create mode 100644 src/user/dtos/res/user.dto.ts
 create mode 100644 src/user/dtos/update-user.dto.ts
 create mode 100644 src/user/entities/user.entity.ts
 create mode 100644 src/user/enums/user-provider.enum.ts
 create mode 100644 src/user/enums/user-status.enum.ts
 create mode 100644 src/user/user.controller.ts
 create mode 100644 src/user/user.module.ts
 create mode 100644 src/user/user.service.spec.ts
 create mode 100644 src/user/user.service.ts
 create mode 100644 src/userAuth/entities/user-auth.entity.ts
 create mode 100644 src/userAuth/user-auth.module.ts
 create mode 100644 src/userAuth/user-auth.service.ts
 create mode 100644 src/userCart/entities/user-cart.entity.ts
 create mode 100644 src/userCart/user-cart.module.ts
 create mode 100644 src/userCart/user-cart.service.ts
 create mode 100644 src/userRecommend/constants/user-recommend-code.constant.ts
 create mode 100644 src/userRecommend/dtos/user-recommend-code-response.dto.ts
 create mode 100644 src/userRecommend/dtos/user-recommend-code-use-response.dto.ts
 create mode 100644 src/userRecommend/entities/user-recommend-log.entity.ts
 create mode 100644 src/userRecommend/enums/user-point-type.enum.ts
 create mode 100644 src/userRecommend/user-recommend.controller.spec.ts
 create mode 100644 src/userRecommend/user-recommend.controller.ts
 create mode 100644 src/userRecommend/user-recommend.module.ts
 create mode 100644 src/userRecommend/user-recommend.service.spec.ts
 create mode 100644 src/userRecommend/user-recommend.service.ts
 create mode 100644 test/app.e2e-spec.ts
 create mode 100644 test/jest-e2e.json
 create mode 100644 tsconfig.build.json
 create mode 100644 tsconfig.json

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7b9c9c9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,41 @@
+# compiled output
+/dist
+/node_modules
+
+# env files
+.env
+.env.production
+.env.dev
+
+# Logs
+logs
+*.log
+npm-debug.log*
+pnpm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+/log
+
+# OS
+.DS_Store
+
+# Tests
+/coverage
+/.nyc_output
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
diff --git a/@types/global.d.ts b/@types/global.d.ts
new file mode 100644
index 0000000..26af7f0
--- /dev/null
+++ b/@types/global.d.ts
@@ -0,0 +1,14 @@
+import { UserEntity } from '../src/user/entities/user.entity';
+
+declare global {
+  namespace Express {
+    export interface Request {
+      user?: UserEntity;
+    }
+    namespace MulterS3 {
+      interface File {
+        location: string;
+      }
+    }
+  }
+}
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..71dc279
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,20 @@
+FROM node:18-alpine
+
+EXPOSE 3000
+
+ENV PORT=3000
+ENV NODE_ENV=production
+
+WORKDIR /app
+
+COPY package.json ./
+COPY package-lock.json ./
+COPY /dist ./dist
+COPY prisma ./prisma/
+COPY /.platform/nginx/conf.d /etc/nginx/conf.d
+
+RUN npm install
+RUN npx prisma generate
+
+
+CMD npm run start:prod
\ No newline at end of file
diff --git a/README.md b/README.md
index e69de29..00a13b1 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,73 @@
+<p align="center">
+  <a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
+</p>
+
+[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
+[circleci-url]: https://circleci.com/gh/nestjs/nest
+
+  <p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
+    <p align="center">
+<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
+<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
+<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
+<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
+<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
+<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
+<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
+<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
+  <a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
+    <a href="https://opencollective.com/nest#sponsor"  target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
+  <a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
+</p>
+  <!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
+  [![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
+
+## Description
+
+[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
+
+## Installation
+
+```bash
+$ npm install
+```
+
+## Running the app
+
+```bash
+# development
+$ npm run start
+
+# watch mode
+$ npm run start:dev
+
+# production mode
+$ npm run start:prod
+```
+
+## Test
+
+```bash
+# unit tests
+$ npm run test
+
+# e2e tests
+$ npm run test:e2e
+
+# test coverage
+$ npm run test:cov
+```
+
+## Support
+
+Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
+
+## Stay in touch
+
+- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
+- Website - [https://nestjs.com](https://nestjs.com/)
+- Twitter - [@nestframework](https://twitter.com/nestframework)
+
+## License
+
+Nest is [MIT licensed](LICENSE).
diff --git a/_old/.dockerignore b/_old/.dockerignore
new file mode 100644
index 0000000..5b144d8
--- /dev/null
+++ b/_old/.dockerignore
@@ -0,0 +1,9 @@
+**/node_modules/
+node_modules/
+**/dist
+.git
+npm-debug.log
+.coverage
+.coverage.*
+.env
+.aws
diff --git a/_old/.gitignore b/_old/.gitignore
new file mode 100644
index 0000000..d23b87f
--- /dev/null
+++ b/_old/.gitignore
@@ -0,0 +1,11 @@
+node_modules/*
+log/*
+config/secret.js
+config/email.js
+config/swagger.js
+index.js
+.env
+config/crawling.js
+config/easyelec.pem
+.DS_Store
+.idea/*
diff --git a/_old/.prettierrc b/_old/.prettierrc
new file mode 100644
index 0000000..c070a1d
--- /dev/null
+++ b/_old/.prettierrc
@@ -0,0 +1,10 @@
+{
+  "semi": true,
+  "printWidth": 120,
+  "endOfLine": "auto",
+  "singleQuote": false,
+  "useTabs": true,
+  "tabWidth": 2,
+  "trailingComma": "all",
+  "arrowParens": "always"
+}
diff --git a/_old/Dockerfile b/_old/Dockerfile
new file mode 100644
index 0000000..551415e
--- /dev/null
+++ b/_old/Dockerfile
@@ -0,0 +1,11 @@
+FROM node:18-alpine3.16
+WORKDIR /app
+COPY ./package.json ./
+RUN npm install
+
+ADD package.json /tmp/package.json
+RUN cd /tmp && npm install
+RUN mkdir -p /app && cp -a /tmp/node_modules /app/
+
+COPY . .
+CMD ["npm", "run", "start"]
\ No newline at end of file
diff --git a/_old/Dockerfile.dev b/_old/Dockerfile.dev
new file mode 100644
index 0000000..b1c48ab
--- /dev/null
+++ b/_old/Dockerfile.dev
@@ -0,0 +1,11 @@
+FROM node:18-alpine3.16
+WORKDIR /app
+COPY ./package.json ./
+RUN npm install
+
+ADD package.json /tmp/package.json
+RUN cd /tmp && npm install
+RUN mkdir -p /app && cp -a /tmp/node_modules /app/
+
+COPY . .
+CMD ["npm", "run", "backend"]
diff --git a/_old/appspec.yml b/_old/appspec.yml
new file mode 100644
index 0000000..328a073
--- /dev/null
+++ b/_old/appspec.yml
@@ -0,0 +1,10 @@
+version: 0.0
+os: linux
+files:
+  - source: /
+    destination: /home/ubuntu/engineeo-server/
+hooks:
+  AfterInstall:
+    - location: /install.sh
+      runas: root
+file_exists_behavior: OVERWRITE
diff --git a/_old/config.zip b/_old/config.zip
new file mode 100644
index 0000000000000000000000000000000000000000..936ce9ad9053d7c44adf9448abe935c90448d1db
GIT binary patch
literal 60695
zcmWIWW@Zs#0D<1*8DStAhS?bu7?Shz(lXQaLqm8O*rUF?rhu@FW^hVr1vdjD%U4DQ
z2C!y`Ash@G49JF9FfuR*GAJ;_$NM@u`v*tp>%~LNLov<_)41dW(d6`mq=XN?K4BmD
z!#V;Qm?lUts|!dQ8W?P1%v6|vv8hqxfrO9{OJZYb4&x&+6IGvpLVh;3e^36z>^;)d
zG$&;5og+_VI{wHjY)ecKa6KV9O-)So+0>M0ON}-d*-W`J<H_PjnwRQSC)D4azsh_K
z58Lnc|L<<ScQ`rnj_|p;HYa6{?ARP||G<Gm7jB(7a^=PeCDWU?EEj9on_s<k<LK3^
zCy!oqy?*rS9nb4W4xI?Se(~&)nCn;X9OBV<m-66Q(h&xR0B?2<r#ROQhZq<bK*4|r
zAhX`7@KCm6WMB|NgmOw^Nn%oBajITcF*t-geVkH2f&D=PBfv{9_;MdI;9+~P`LF2V
z$xS{YdvYpP<UDYkyGFP_Xm>+v?o8k3?1%s7?f$*)r0^tjF6m8<f0w3<<}fp#_miIC
z+kU9(rqmkKgC%mt%h+xzRaty-PIZW~*nMeD`egP=y-end8Z=s-iK^{+V6AuQww>_N
zCf_KN<VSO!#+9(7xx~24vJUjpem^O}W$q8dud`JyO8Z)F>*X#y`{{_M_-e*fQ#1D1
z?DBhAn-lEicO2WaTA`)ies;`@d+G<Yq~<*cIP(t_JdGFX!uK;UFn|I9d+=OgU|^6%
zPCt;;qpuf_E2KzCON<|t6xcqlHxOWzadv82;4`65(jljLS&N{e-mE$ES5J=!m@s)&
zc*LxTDKo++&76KJU`2jHLSja4T2fX{%EVbYZ|4L~nLj%#_syeMubw=5vFP=qtanSY
zlM<h-di^3TY2E9ryhJvUa-)J`!z4(0$h+`YwvB;-0pu-&hgWq?gr|pC)bvo4T3nEy
zhe!{NjJCMa!`cbHT+IdoZr7iQY|c^q!k}5z@r|WSrt63BNtFPOdwVC(>e^AHm}2;O
z;`@&hTa>E{jx7An|Hw0}Nk~sHs*cSi=~}}!Mc(E!UuP#=`}tq=6xSjxnV%1inTTee
z%=tA*y(!XSQ|hFj%E8+deBHc_?oYL8wLchsR=3QZ^}NVwspecKfAe2H*DA#tcUaH5
zXYKWx8x+Ium>+mgWME(bc?-LTK|z5%hH?3Q2*&V@0z?d3GBPmmBXWFlVsZvNyT7<>
zgDZM<Cmi%T>>$u~{^oLtDlcaf&8BuX{(H8Zb`GBHW;600sU7SR>3X8jF`+r>DvJ$g
zE7!(pLM-tsX6|X8y<~dgga2tW@^kvx73a*2KfuYcr%R?W>$vE}M%xX?Z@i4^xyk$J
z=R#%a&bvmbMl-%WJYn*6nXPQ1eATkdCbr|b6C1m0MEXM49ag<p@><h1L4uRdwp-KK
zlV|RM?@S@7>zMNeotr-yF3$-*`crQ{^WEBu?on!*A@2=Cx|_CXX1HxmNHaXlaLev$
z;Ld&1H3Pyfq`R$W-K*lTpVPJ`SnRZ^c2#uP`R<~fjo+SpO5Yd#|Lt3;=$FSn?y76s
zwSDR@;e3gm6&=C%g%j7_Um*6I9h9t2FRwqy$;iL}3QI(?LJnn6vXa7Da9|DaA(*Cq
zA*HEl%nS^oh%}X%8<1aA5|W>tng^>$G_TmC$Z7_sV5vx2C!g)QZ6MNm{abL1kGNUN
zlf8#UoK7n7TxL-}?c}yKU)XfpQZJ*)0(&wvTg&cWEm_dI@U_lopWx@RT9)VJV@pzB
z|ELLf-*uf+e+pY`rwi|Og^$iBRes5FexD^?a(BtcH4k+|bs~?fXb}`x<-I@Wt8n6<
zijw$>^ZR>DuP(I;EIGNuYP)%p_ym#r7mY5*ZZ|y_!2d~bdetHE2#Kaup%WYO5A-bz
zW#@`{#jq~^(5=!#0<Yfhe|7%$uiL_J_Wik2d*_FVXMpB~Y5Wh)KRf>U+v-Xin|$+i
zi=Un<I#;;)vDx=2TMcWo3%q8!J~+EoOpD7|OCr%kTumbK#D)-!(24_l`&f&v6wi}#
zD_FXhZ${4IhZRiKcdedz1Rr*Nc5QF?o(mB#R-g1*kglNRa!hEIr2e7rUE*>fzMcQ2
zbDvJhk}_p4mYLT5k|{j!cKxAQS8NaF6<nRt^II)6-=yL0^5x1K&Hr=v&i<;p{kl%n
z&B@`*PWL5CrL6dTPIg`Ujglt@p0UxZ>h4_IFMqpF^2WC-+f%g++`XSYSUe?$?ReZr
z9?xUxYjeIEua@+>#PKiIpJRs=FaMoQCTBm)^J+QvaaqTMqq{0qcS`bG9e%a$>{{>0
z8yA(hy07g2Bmd{~Lbc}G7Y*k5s2`sF?{%hqUwh$&mNISQ{g(dTlZB>#dv@jDpY+9d
zO8+x~^3#>3+o%3BGBAMB5h6b!r!Y`{Qox#@@Fcb&n5!I1%iwjf0w+qYN=wcSPE9UK
zEz!#=&d&ok&$OmWr4&z<!j`M1hTUAZ`i5wYU%l&VuS%uwM*h04T$>LzNaiF@SgPE1
zHs!{Qo6Dxw_t*aN$c$ADI>am#5SslqDoU&F|5N?@%bv^D{C;~qUs+<B7OQ;WtTN*{
z`~JTD`qr+*)MI(SR<$yNjuwZ*_qi$_LW}ncTov+v@R)60yhhj3k2a@Y&QUa$TCO=S
z-MHs)P**~s5u4$(37q{KPoDo`{{8oFAyMD$^`$m{b!4W-fA{+F^Bb3LY`9psIBVpF
zqmQml-|qD|+(#tFRg@)O==;kkkvd(GE`<{PIogFc`%J%1Sas*GMEt{!v|A4!USyk=
z##d?Z+U=HMN$fK%mpuZHY=nNZ>~x)Nnc+0s^5u$E?P<Zqi`O=KD2M;(5x=^_GoYx(
z%ejA!@zc4l+&*7;^kwGx6IZ57osc=Y!D(g%zv12or+zbgHYgvxYLy!<(Xk}>7H{me
z*HwJUvC1}oD?Lv9cyM#4&V~v$o=a2ScB?iUU-s`hSk!;#%dwdWYK`yK1qfRmba2b~
zd2ZK3R;3?b>`u>)`N}Kawlc4C+mw4ZZ!<1lR-kY$b8)Pk*i~s|i{y}>K?<dv90FE8
zI}W6%i!S|HCXpg^=y~da*S(TWMgfO{n{uPXo0L~w?<zR8w{7+!>-s&Z;WxyhN)N4d
z)zAqP5LsDKsrRxtUod5w@>H?XD?R7<^U{A*D3_moC9?3EkGQsaU-pan>o&N&wc~si
zJhQ#zz548nN2-oI?k;hDn764bEv(|@nNQ4zlGrDGNikN>)vd3Xl(XT&z4*laS41V=
zBpEJ9yL?c@Id-*4(V^qtc2rgNiArD8Hg?)R#f_nKEq|NIw6i|1HP{7;_Z`}@ufs!1
z|DV92Pcdtr=(zJtyS`VugHg!N_{tgXTOE0drR$F`JnEO0v-IrbV*lD2g=yDy{P+)k
zp7ZEJr1C36^R*K*dva43&q}G@z0&dEhTT7BntSgp?=3pyn4+5aTg&@O#D_EM12{iD
zUGXUC$;Uf;7F6dahwO6hsrqpCQorlVeKH#a1=Gv*#n#Q4wU^gZdEMK~cHA9LKF(sz
z>%Q07!(_?s?qoX8G~E45E9<YATNP5r8a4)9OiP;G;9d~u?j4!BfwTDh`53J|ejizG
zJ~|r`xk&5Xk7z5y^uJ~AeO#9K`NVcdE$wHS#K<qa=3MPo&yN$V7Jm6>vSWhF6zwUN
zLh>AwFSgn44=P;PJE>4xf9;04vbk@1)L8kpZ;`FK@@7K(>BZN>a;22_&U+DE+NyM%
zZ>cjc&!Oz7G|#*}s^4F>+)4Eobgl_e?N?k;XM8wtp_pfqepmJ<gJ+>@P0ow|=9=qk
zbndy<O5L4rlx7xtR(flPy80YmwDIW7GdXr$>EhbV#ZMNNCEhGraO!~2in-UeeG%p2
zFB3jjDOFpgyVFkOi3@YilP3ooZr-+&-utF;dfXOu{^bFjO=W_`ddu%b3S57*HPZap
zu7lh+t4ik|xO~lAZM9hcKPO(B<3FeF+P*35!<Mw;^H$~=d+as*w%z&S<C^{i)=j$)
zb?>qjsZN>4m@Kr3<HofMXCJqOxi7z^oadOh_qzVCmRaVrC0AFdeaP77x}oa(p6(yl
z;|fyVRhWN2pry9-sSd+cvtG`*RfTo-cB`J=yS>TehoBqh*UuR{)9bWV*NZIPc7OfB
z>{(a2dbw`A%9=ajW<jWRWzV*hPv1=Pi#y(Jojg6p#r|B$#jQ(jt@*Khr}X(YN!~Rx
z{PpF_Olwc*#GC#&{5SH_ESuS$9~P9>w67K6N$HB3@<qeMX*nm~{u|L9^WQ%%oUfH-
zDf{($*=siy-n4Gj=PNTN7_5-%HlD$Ee(BO{@6OF#uk)Pg^@gQPFY{7s8BJJ1UfDfL
zTNIgG(OTJ>mQ&g=Z?k~Q>6nVCi&w;U3q*c>qOpk6%j421E0;2}v`lruHKH<mXV$-6
z>sRz-HGA4Vm8!ajM`q_e*|b*c_Rpfqw)*{t&U*yq{IoxEC4JpeU)Bd-7;o*!x_59|
zIJ3ti!J7Wf+nSQge%o!^5c~gQ@RENk9({BFIQ2-p;~(X>x9qe3P26PhuPa#Yc?8q)
z$hM^)t_%O<?0&xT&y4n0y;{p(+Wl)=6d&}sf6?nMwmqsB`QF|OV5+@uKQp+T`9u0E
z`33q(HS3T0e^UQ)|7qNRPVcFW4<9=w|8eJPd#QT)Ph?u`_4%gzPU&0xQtPR=d;DM7
zxWv<ZZ)lXk(MX%4FJ|}t6F>gnEAjv8=llPBo?g!gs=hTt;~r(OF))BCEJXE<T$SOj
zzVXy*L%jN)`IC)77||#$E>BEPPc1^Nym5@JyxCh_EF8W||IhXxB7b+?vr7JUVq4Uw
zy<M{y56yeaBf#{;uT5wZ18ejrkD`W!HmS`g3O8&{PAf3ze53nq27~5uw~egH_ngoD
zvwL-|yyE(+->>4f?|YFH5$vN=y1nY}wYNLhKi^rZaV>n;ynRKg(Qjr?`8BDzbDzbI
zDW~K08s2Z`JDOA*pVqxJMlt`+HTjOOQ;VO@)Y(&}RIfVyd>Z$qi^jp1tXq5Ps;#0=
zi4+&7K5oC}{Qhj}=Vy~uUskQF3AIW$S^qarZTiV)-jR#nM#S6MuHIUd^{PgG$A$up
zZ44i$UJmzq@G1R)(62k}s*io;I5u27-MVOrEn~ds_dh?5^nTI#d;FI04|#e0Gw04d
zFMc}vi}{WIhw;TFRc6zd?{}(a`Tu$I&7hqDXD-Dj*tOIjwfx3?F4Ep!<hRT@cg<o`
z>)BVAEqR}{)8@MP^ey|B+Py7XHbMXF?C+P0ixZ|@{xH`$e#(LFt?tjdT3`O;`8%=X
zXV%G>sm`Ah<h-4i-kJL6z@Ir=<DPLX-SqQloZ7EVKWx^;vj(JHK014Y^V;{}SLLtG
z`s!NxI&XeqwO%ZXav)p2d05053B?NI<-LcO9@=7f_FSX$`dw}3ZVJt4us`2yIiXK+
zzedyQok?X<HgS6A($~Je^<dfBxJ=pImdi~ecYJX*3*HvjxaaX^yAP}H@XYDjS+cTZ
zZ@`>ucJaDTHZOmk{xIF5ZMkCAhf1s1bNkAFMy!k2$F=`i@yzVX$6<!E8CRRlo^O{M
z_vcQYpwFF{Z4u{Ez5QAY7Z*NPl`dNubLQ##8yof>UYch%<vIVAv)5~{8FPPoT-M&S
zFTOG~@KyJzcSYIB`{VCM1eJYTdTnp`t50k{mgKG7y6<l0TKmbz4!S=*KKC$-$g_Y)
zflod?UE_I!!yxKv+ooMNer?lx8Svq4#AT5RlhvkMw`m-7{JXI!|FGrX#@&nW-3)mb
zc18Zd5zoJ04sI5Uc%T{cGiqhd>S)ousuy-%`^C1<@I4ow%@lq46}v-Ai{m%lDmSS-
z@%7-3)&C7&`Wr^iVrIFxGx1l7<-~%7Pg|mXF5UTkk-mVZLhgl_mv?H|0`3SU>gb7b
z8Q(0KXC-mJ`QVSpOBX#~9hQCiRH=^1s5SG_jL&OUpZ$G`E91d+_EYy)pJ<LeFyXn0
zBTJvcw#)<NUN5y3)9&2d!Lss_S7_}nkA_4)(L~8<&(cq^sJ!b`_Mhu0v}{@a4!vOZ
ztGA6$h+aL*`*LpbrI0$S>c!_jemJCf#{Ji?YyWm9aEYq5U;A<3V&&{LKb~F`)1DC3
zf9>G0Z2kFV+pk5&-4)#@)s}IxDPv;$tnQ4HvPw}?Yo#vk+Pk>!e+<*3l|}D8v-fI6
zzGmrIed^SvO`B{_>`wZSav<e<qDJG{kJgTych;762|cOhxOtH?aN>pL7J($EOU*9F
zTO_P(Ikb)xD1Y>yv}UQ4sHa!Ef-B$A+<bW<_7C-6kNei@ZoN6lA$wP_%c-DR%Z`vQ
zSw;_L@5=Lxvf(@sK6&NCi5E8(I^JMOe7&V?nrE}<hvRR27gd#=yR7eX{@;Gn(x=m}
zz0LJB_gu*@eW|1T=HkEJ@%puQ<ZStZPA}BoeDqH0>S#g1vwa@LU$$AhnyqaWWDjO#
zn=-lS(RJZeQ`Q*oDN~=^(NOy&Ai~_Y*YULbON$aKy%jqaJQC58YnYcgPq}vv%Vk&j
z@Y(-w<?k2DGP@i)KXBcb<2hyPCSTJ$bR>TF&!b09xP1El)8v6m^UOmz{X5z@3cO#w
z)L-}a-~WTnCTyk#F4?IL@6TO!Z8q`v+v$07-||1j@%v?`r#vtHt0cHXW@4p*>I>bC
zvUZ;nR;j;y%lI|ZqW<RhqhF0SYQE>Fimv*;_;b+f)lu0i{lt!MHow2O>LvfoJJmbO
zm)rd~J*CFVJapEn`LZWv=1a}pW%m7J`|<m8C0O`R&V15te!jM(tm0Moq29^I@6Y}H
z<nj07_qByem9M(LyZilp`l9!8e2lj5**XiJ`ifV7)IYt{zvn-X?fZS}a@*;iwKJIa
zpZ2Y<sZuM>P5XEG=Eplvzq3z1elB+JkCz6O3|>!5D(~5H|2uY|MCX2F*4>p}XMf#a
z^6&EGweF!C5*}SrpWM(hE8fr7OuD-vjH#<~hE3e^+lzL^c*lE-v!!u`H*K!|8p{0d
z_KmU&7d%&fU9iYz-PU(+SMy)L!ga&rYgY09?4|#jckRqr!&|zUDZ?}L=30sKGpdgB
zi*l}U_%Zofc|8BNn=4x#?<-a+t)2Mzb8GfZoBlr!YHTzL7oY3073o{X=GpRo?)N+y
z-x{l;;s>98AOB}j_wQ4$|9x*imH&SauYUT`KK|eL$Ita^tNy;39?m5^^XH+9i{1a%
ze);=-Sx|T%SL#yr8R_e_w3{mBe4a__S~vZA&fdr@o@>;`B386n<@C%;cdB(6Lzn%m
zH#@L3x0KajMU?B`SB1+Zg3KQ&&Gk|13OH`wn`y}rlEJ=NPmyUOb3s$sQa#7#v2Qys
z>fiqD^TMlm|GKdM>o;w>^KFBjluyhV`OX{mYs|lAow}Uv#3o?WFoCg!$APQ8iS0~*
zmZpl!Y~BSCavoDf+W+Z>dcIxw;NiQ5RQcNd0lywCF28p+wg2h8|0a{Q#MSI7>+`<Y
z-?RLgTKMgmeE<KlFWYze2Z#T&`S$$LR{MB4&oeb2Uw*tlZ%@#V;*VVk-c7ok+f;4{
zP1S7;_RTFSQwR-as#!Seo5*J&tyKYmtvU)RjEdY_e8qMIX~fJt*>*W_wtm^H-`(s%
zj$a+>y|)G@%d~6{*`Ro^jYayEh4ik}Ggl7@%s6*?r)0J&+v%^1u3ekDw{3~ty5{$G
z7ALOeRNdOBpd(k5?6ACtIW;z2IY7bZ@um8V^(;Dq#(Q1%J&H1GpK$frAs*pp6@s&~
z@AF?Ve&Bnl{biC~ZKfKxz~OR-b!%!qcUR@lmfyN_{fZ6D>DL_%%RAx^FSJNLCS7ch
z^W>+-qsg9AN~A8lz5ieC!*Y!utLI1v|CxG&FJ9w)g#L%{rT-i%cP(2#Cr$PlOV!$a
zw=&k+f0tXC%l-W8EQgv;PPMCN@9gP#v*Bk};NI_C92-*B^;oVJkm-JYqbb|?-1&xs
z8fTgt|NIrbp;uLLke9bU<=Xm&qywK-W-2Z6Ing=s;>If-Hq%s+mD9SoIQ;i+_LSYF
zGQo#kSowjxr0_j9i5#<yY$nqu{Q6=SF!jK<%y(C>Or8F!oY%<Y{5?JkpHpvnH=OI3
zF=??2%giJ(pPOGU78tF_V^2R55+vu-vol?5g^I-WrF%*&ll4vZC*AwR_T!4Kp?~(D
zgpNw9E6u9UUe0Kp@m%opBaNO5^VlEHn0@Sdp7|ob8g=f|ygOy}P6oZ>Jl$Jd>~u2d
zW^~TGf2-EyuHc`3wOXHPY0kyS15<N6Y~x>*zf&=Yk8fOBvm-jkG2?$<rC3;O;?-WS
z?Q_nai)&hKJ!Q^noBM4Sd0thqZraVx|7-1&oyV%|eFB|ZinOOh+n>_Ck}Is-Dfp@D
z)rO?s8G(QIt-B)M@PFIQS@BvaMVZ;Hzn*-26~6k_-BnlatacDRQ2uYhgf}wRtJZ%i
z)(Ja!fz{cbgGbg!&PlbxZR%Fmo}bDS9c~ux<qhEvvze*4Y`wKcya99g>vKh~>&>m)
zKhIhFO>3>QL0Rvy#{ZmB?m6w}7hDwf+&K5NXYGzO9>45b=erR+Sr-a|+fMKN^F=^u
zHM@WR`fLC5i}$~4R%!9-U%&m@#w9o01oq6|v-#nowYFLk&Wz5V`d#BC+89_?U+J*0
zQe3drwAozQTmG<|zzn}#mw)}Vo7p1KXzieD$XjODznH)4=Ce1wYf3-51q4_cYH4U}
zl-=jCGBD@zT(SSxq)(mFUb%H2zuflg6F=?HI;g~={Xn$u?OK~k-p9|bu9tIHRdCX{
zHbv-7a+IytoPZrcxwdx<{+dq~s=2<?Y2zBZs4Mp8<C|EYK5*>57VfYl*W;Idf<<xm
z-}I-;pWl+?dHa0rSFIOq{7#%Nq6M8beR)!D2C?&&t^KrVk<HBszYZp~9Dgl$ueVt-
zKKq?rc>SFo@3sH$gl~U$E0U!)bDBxPuLT=Eti1i*@W%o7uiyColvT4Y*vq?q*8KzV
zPwxqTUi>Zmee<q87o+Nryy+CU-E!lY(Cte{lYXgvx)x(;{d(hyiDwH$tmpIz6vs6s
zb|}A7IL-CAMyjypg>O}N+$Hw;*Hof@8MQ@xU$o&%=98)y(+q>SZ++L?-BT36)o1ZY
zT{b0ZKQm|5=lIaCSCxAc*00yvnp;{O_}Kc7h;)9QvHDzB{*@b<Hq;;cDDf~%;K_RR
zJ6tST&n~Ziuq;)jpXugKZ>7S0b9QSqDmOA*%Gvt!QTo=fz%2ig$ffdZ`u6wa8Llq~
z`Bc&=bc4b4eX(T37way=?6vmnn`9JZvK<(doaDSGJQU$>*mXuu|IG7~j%Vux4zBrU
zI^)1DFXty~3|_Z|oI9K5|17Qe;seHnEm}R=SJUR&tUM`bB=_SAyX&UsJ*$}}@}<e`
zY1^@Gi`M(e&dxXgOpURdt@|X6ai@gNQnt&*%#{jn<IgVhe%kmXp~~#<bSJyoy)32w
z7G_`md2r_+(N9g<XS1b$rL1$23+O-b_^o5{d(#W4Dl^Le+$(!M&)SMbUFl-0%BmSD
zJ!dRst^MMXynK%63B2I3k~XT!R`hdg7hM~1NR)N%^j`}u{3<b8{Qvdm#>>*`B1s~J
zhED(5CU|f*FIXhtByzRj5bJA!kJ0Q$Zk2CJU4Hn~`~>dGv!_=%7WlS(_`52q{@MS4
zJcFH6I~_PSI(5_<msZq1cUy7tek4=!!Q~eYUpSR9rSfT+Z*5enB>y?4mluS&WH!vV
zs$8&jhW6Z<`%NaFcDc3SioPnt&%n6Q1xq)^^?DoqSXDob&wH*}`is@>`!m+6{GE1i
zZSyA<=PrY=;Px8~%Py7$)a{X-)pPUS;)fGozZY7=&F#HG&%7rtEmmZ$mPBW&MwrUn
z1Fqk_Q%sM_=P%CG@W{0L>nNqaXr_D3BBr3C?7!(JSI=AUeBamO5i&EERk2Cwthx0v
z)N0}Tg8lcR>aQ@iS#p2#;M-E?HlO)~0<+Q4$H&h_-#S@!)g;jMh|FGt&O65%nGZgA
zXmPKc>sWKEszkrEn>z1*zWFl8Z?EX-TpD7i>lE^Ge|+VK1*PSocWY{A<oqk<d1O(P
z&gd+<_{fQ7k2Ix2vlS)7JmmERWULLE41TS6u;EsgLqgj=FUDy;Cl(tNcg<O_Sn2iF
zx`mQbx~!9Ve)5R&9u<=+d3c#;k;;7mHVIz_uCCrk%0-hyv@8}a*Z2{pQr#_(vSL5S
z{#^!k%Itb`u6J;per)b^Jv?Qea*cb*Dw)J`p2<GntCk++W%d!&%?_TiI^*i$uc^9U
zvqcZ*zh80RemY<9FUvEV|7%DX?z86Uy`*Fi{aX6eUiM?(KL25Pe<*pw7m2y658swx
zl>97l`{`jN^_2Z`ULkIG%kR$9)X-y^SGqJ{kEH62a9)L`167Ps|K#=7Xo{B}Yfi25
zVt#1aX>vN#OE7pw0q45QA1bEKe|M=d=xZd?2cfK)N14?ss&7{OF0F2D52+{!Pc8QQ
zQ+`lEy&}9|Mxy$TX3Z&mTn3j6(|TWAPW^G`TCD#ROUX2=_+9gK-7I<xzT`i34OyJ=
z$y~=VZ9|_#tEff2&jU6?HRl2*m5Zw-t}7fXXyfW-zkDK;XK6%HQWDeWp7M~nJ3Ni$
z#moyCcR2iJJLBSgCs<^|c4>zliA`^3@0~RFTaK2}qnMW~w(V4si_fi33>HgJNIrhO
z`fT;dVpji0`_4?&=ssaTH(2}jh0Q<LubR7P@`cTIk$!w?J9oq;rp?<?^v9V+NW<YI
zL;1r)XO<>e6`wtOYYC&Kp&f^_-uxU7aXpc*?Rro25Bz_+yf0>lL9JxWuCM<Nt^L&W
zRd;@C{9|#w1NBiyH?K7<Ek68b-=2%}?bn3GYxl3dru^E$;&Qy!)3x7yri$mKzP;Y3
z!oaekYMM&r!)Z1avpjBz3vFk6(%IUotXXNGX|4DFrPz)&dnL`5nqHW1`{>`t`CH;=
zcTAm{p0#z+>qYJ<TQ}y!#m8Rpdt2a>wC;<$!bvyn!*$FjRxJt3KIP}!q`6mRLp+1V
zhuk19gCpW{KQ9O{dMr#_xMWII?Ao;#75fSt<}l95-#6pvsv`#(%wE6#pmB&j-QQ?i
z$4b%o{dKh;(h~RC)N8JOxz`}^_LqQve=pkC1lSbneEoRkz1P!{jWsDLf9g{|Uv_sp
zzUTB)fv?v22{Ml=RXR>wak$(pV0G&GizRCp^SPugQs~`$>F-VH=1Im$Ax2l&-}|}V
zXqkWDQU+7P-Q+XE?5m48*8IDCuOe7ZTYJ_#y&LbWT7G(bStj-NyQ;9VN&39&3e5d?
zwwYbKI@Nc^T-VB7SMS7{u2OQ^_qYH0g{NQsHXpRhcizHddu(TXes$E&mgBso+3a#3
zTR;8!WqaN*K3&`M{@sliyyJefuldi$eYr?;<&r0yGVfkw#9mxup_})=*#B78PiJ}0
zJ!1QW9Vh*L@YDTHv8(l;xS#AcO*iiR4gI@MPja?Ly}&7ss^?ReuYR#<;TOGvSKQBU
z3id4#KeAZOv3d4|mj!;Dc@u2iLvDx!|5kpa<PzhOf83She%<<C--1IF4z2A!taj(u
zp|$_F?fg3T{+;EO8aowc9It9!>KYN%va~fT^W%ccmGY)nSl>$?)t0%C($u+LQFdy-
zpm|cyR`KMXw=Z7EUfg)<u^qQux-<WlgOz_G6ge9iHa7{a_*Ww3#pbd}c&fG0!T1a&
zF>PB5#T#4;+SPx^`&YFvS-Uh!Ej}Q;<bdGP1ER|gyzJ+bSaCpjWyANy#YeSTSp2)g
z5_VpWTyb+krXaVYlh}?VF^>(Af!vMmvt+j)kjp&dx-WyxMsbO|KFh+ko*%4z3~V}K
zSscy+bB-vm2_6-Q6k#q?%PizMC=s>5gY8$?;uE}2650Yj@oLU#lseyV^khc{N6RFs
z%NLd{VDn+Qrugc6nRQZd-^UVufk)q+=1iM*Ug$Bm?6=QueCzd=bqab3x7l-LcAlSK
zed5>p<8Q5&r~a{sn=#k>c|Ke5p<ibwCq9vW*QY-1-k*aXj<daNKC$TG4)r-@hcd-y
zUOtx}7WDA+=cJ(bI+|a3{li=zUKNhBeR%bC%z6RIm>;KR+!fzvq*L=yTX>(*L+xZH
zF%Si23-2@HHF~z_!&J`rnPB4Qo)1$i_jATeuIt?P<J6yu<85A_r>7M!oz%R+y&!61
zSZMsLPZs_CZ?ep6-`-%GHvi=@hp5jpd8Tk*-?S~i<?)00Z4STq-|lT-*!%lp<;t(2
zYqy4Mk3UqT^>K<t@0%G*`@TEu`1)tz`;E!dqGIeslpd&EPRJHZiZ$5M!^5!0IeOx+
zv&PeQtqj)Y?A%y;m}%m(*$l>&HyZfOzJI@ZmMv#pWc`wL;a9$1pA`43{DVQY4U_zt
zD_6tr70>Kje!Em>vhVY)_J^{y_iPKS%KabUvzvWt;>8z++t>DQo_4p`y6@88^v_Fk
z1dW<EM>gbH%{p^KP0?J_Q0LFzir`C=D$J++W&b<R?Wa-o>63<4T5mP>K1jHoarCWn
zahvm6L!$z#@at2~s!CZ{TF#W>e!t}ToSzfa^SlLfCmd6Ai*~V5|6)5)d#>m1n_dbE
zFZ_<q+SbvvO<F`^wvs}}uCKaG1rEMJ=Hfe*SzgTSJKb7TrMBuq>oGNZt?MpU`#)CB
z*WSNktLn1vGtaHsp}j$?T6DAPm&}J7#Q3uBNVvb<VAjErxcrxVo}AL7)9GskCwYi5
zNzAzG)^dX5%t?DAYe|9I$KUsz{&Yy`qt^c&D{{J$1m2~YnH-)ae23x0M8>lj%9pmJ
z^WBLtXe|^qKJ+-GyZN=NLEO@v;epw&=V>q>*IwzybJv4&b9G@!yx)aqJRWZ!%%3K3
zW`kQOPqxT)1^G??UK<*SZ;pSq>AJu8smLlpv-xc)rODFAT<_d%7qduR%J?|PI?ef*
z$vxfA8~d9IEw1Dl=WO10Ge@UsuWO&B5W7yx%5|%kS<POQAi}w8%EOoU=lV88Ml!#?
zv(u7eSIg$rE007J$pl`$rRX<f_wMA#(A!h}rYiSmMgM$Z(ff1d)mc%|E}PQWq+b@!
z5@^m;O^Av-EL*)OLMI~8zwYO9&wJrnXZJ=uw@|;7^Sd&QVZD6Uhn%p_Z#|D6@K>n5
z6WgkG?ysxanFIGMWZb9!I<?bC!RCSUG6s(5d8dPqubTR_o%f>Cgt_0=3tE4>;rX}p
zN8`;4hfSaEFO!i=iO86Fq}fdRZ2YUB*L9)FDGm*LH?wSCtGedcojZ~8=QB1`9+sJV
zW6H5vo31;!JfBfe#C4KW*>WQ*`$1LD7@?}qUd~d=R#yB|)Y!WJFxd1ZPqcDoGN}*z
zTe!JcM<{dKUmc+j+m+hfnTmq-L^iAMSX6W6Y45eeybeW*^$aUaC%bHATAX^XeSLY>
zj8pfxzqXrxR#0%;%d^Jql##EH=#dG&JO??xF&Vm)F)Y#h^}-=*+r}4xt}zjdFB>aL
za;{IjwUIq%X^@<pBh#C}>-WX4pKSLld?#Zr@}b(C^`pXxdo|x*EwxW6djIs^++Pp$
zgFn5z6OyS_U#3!AlU4HS)}7<>a`oj&0yZk?ljQH&e7|_};Yxijb3eKI@^_Q_@1NiM
z{b=WtmHPMOEx%4*Z0=WIxYoq<_d|m}4_AJke`5Lm3j0c_JN57He2r22n#lJ@&;0Ml
zmov|7o%z#0{dn>8`|>_=*5zLxEkFE((ZgT*;_?Zr=4&rM{aMbwdH>0a8S}p^b(}Nh
zc<5xSh0CX2tm3NXeR|GlNx`XKT5D!6xN|S#ZY0b9R-0=PzUB*;^XLSwyzea~9hAM7
zX<Kdf-e+}3Hy@t#yK=Ho2IHrDv$b!py_#;2G0!Gzv%$38KjdFtx*D+B{qNF6A=jef
zZKpAWYG>bxf6pKzSXwLDaWm}rs!4ZO7z#a6E&uuHi$~ZrecyiG)0aCpzC3u)u_*n)
zydyi!{+7B<Hr#N}M}5Py)vizHuaD)F39$GX^DN3v$>P+h>t3nRcNZzl(p$QGvZm#Z
zo1IE}2OBoLZhcfD(dh{u#BZCt`*M-+vX2@|)fe_~ruHoAxmeSEZYpQ5(Auf4OShe`
z^l`pFcRNpF%E5>JzwgROOjh15_Ah*8fX?Cl3E$JYKQPbup1u6i(d-ACw@&>idDg>P
z(zIRr<7<1{_Q1*eCeLzxnRD3j<+Rslr|>s3msj7<IFw{^T!G`3T#TpUuZMx3qW<q>
z)Xl#)`Bd&();sKh-wy3qyQk&3+i&5?RUG~?lhhMCF25^2koYL5=#p2g`1GI;-J32m
z7^xg@?A7?S=Iu|j5~+E<E8iaQ+rMI!wrT0kRbQp|<Yzm7o~|2ieZ_h54A)yHr+c5e
z{PfzTzJniCQp(ouK3J`>q;-3E)B{tuA2#Ywd0!oMf5~RQ!KtH^M@0XxudG2s*wS@#
zC-0BZ@M<{v<WuI4Gl?(nH{9deYw^rEpqTB~;;6M2=MG&pKUntWy8DOCN1iuJS!GR>
zG1+xpR>+T6n&X0ZUjw7aJR2h~{wVkU5I3>6>m?0;O=aib@>y^F&%SLv#uLB2xu}*a
zVLhkLNRwYhjqO6mgaq~IMPBxthMMKtLO#7mC$>$R@%-TTRiEeIZ~T3N(@=cM`HRKw
zJ1-s+)W~@CY}L$%i#M(PQ*!;!rrP^|wrpgVX!)7?ciyhdy3BX?7~|`XTXeg79(np(
zgjbhQ{QF1AuqO@zDKZON<7N~k^>CR4pJ__9uANeLroDKx@|T!y?#H)(o@`|K<39gU
z&%B<RtZT|MSQ=iPnELmu-4@BbeajMcmM)nY^G`~4fof`de52mYS@qlHm1;PpqSS4k
z{Ok2{e%H#-D=&Gw^ugtlClNgQ@+PmlonO73pWAf%V?eIWEapT9rNsApcr`t=c<0Tt
zNPc@uBEJ0<V_|z^qw<ozMJ_)s<<C6Dyvm~Iv1zQ$#c!h5+6`MQ)IDZPW(a>gb1RW4
zYWA}39cGC#f`z$rAKqG;^Rs+z5wj)t<}cf?X|MJATQFV1YFpSD#|O`1T=RB4<NNg9
z!|~V0Z5Ba0tZFaFYWDbXxqIn6tD87M^j?wsse=WkE-zCaI30CSnIj)zap>p6ps4p@
zQ}VS7JMVK&GLc)#q*Hdv`0!I+4qg`vO9_$VIyZzmeeZqBG?`MrYv!ElEnADizFZXI
zlX%$hu_1dm^D)-WpmWE}t(Q&N_;GWhrl`RmJ#(+8+-_I7{bzY3ZROs-hdKIb)AmD0
zPb~Pgg=yL{-<@x>L>uCn=C0Wp{&Cv=f_<B|?qNPZV~zc*>-Y1o?_#*_ll4Ez_wFrA
zo+zI_KCgx=jk{01+x6?!#IJduuRcFF-y->smtD?2ZmC0BSzl-Firjc_$Kv#z)4p(v
zf4b)Uz^iQCl-Zg$o|@eI8C>)*?340ZskW9a6TfU@<nO<F?cwzHY_HVFZjZJ4O&2fp
zeadJx__D0@z`FFW?!D9No;W2aM(<$#WvgAQw%6L^_wp;xxf5=$T{(5tM>pZFk78c!
zYq<ZOJeas;)$tpL65lGmesv;&t5#|2hJ-UKN?g2n`gnG~JH3hZ^ZoWTMvv?@2R|z}
z&pSSO-SPVkF9elK)A)jCn=`Oq5LSA%RcYm->d<X+>mq(nwVShLmO`Tkm(1B~dsl7R
zuchffogsUHquN!LoqVzf_y7KMVruZ?;HBkH?C0pnE-h~k%IcZ6YRZy+>v}g0{bL%s
z$8@xhoeAuCtbC>@X-!blJDD!N1xup*4>k&Uw_ntHlOPcEI*+yS_=@dMh2^%+lGNtb
zs{G&)dS=dVc5h$XuSFU&IGwU29=Pm1()#yMYmA+-vHje=K|beW&!&3KudUOLFunI!
z+<(rF89pag9@$}IH(^)Bqbo;))7hSMrziBrohgu6RF_}u=l`&n=RtAH>1X9prxNde
z%uo_^KU;Np`;FP|XV0(-FVdIOpYrp{t3?l!N-Bh8<&Isef0y0*zD3{b$D*nu;;j>t
zHH~yG<{qA~y~ZlnW09EFrU`!)N;Hr5i?^KTY&y8KdeR=(RY_Y`Dm8!JHPOTLnCB{P
z=i-IhdOFV~`VYw7b8Pc*FS=8DS^4~=KbkT@Iw!?lzy7Xxb7<Q0<GMLj3)48hvNJij
zD#e;wTPt3-XWCwWBe*}*;{dx8(>Co4H;w<ZI@_xp{<$uYQTZv(dav@QdBo2147Q4f
z#fta1KAL5HyjJe(y6DOy<6b#C#o4djD|U+d-ZX8V$l09Gx%<dOi`LuKM<ZgprcUCl
zWb)E9x^v^NkrH#sdOKcI%WD156@rmZ7EjAK<8^->*Mq<gvkaBO{rj?al@>gBFjwNm
zx4=e&&;x%<TH<v@KlM(?-=%loCvg#H6MJ8|vd(Nd6U9TDIt%`^q`F6PSncAvV54|*
z?#eB9_7*)!{&MN`h3<MI^V!{t`Zk1WNvC;S^GdxuExf3u?~K6JMOsBYGT)P~S(aUA
z<PCTc!9Q2&Ri@kb6U&P%x@2Y-KB|bWmF|)`A9!f%O|z<Ba|%Q)rnz6N&I>j-<+*R~
zl%-&2xnVj-cU$M?B*T9jn7y{o*E_uYPM4gZj4Geifh9po3;*^=sP5<!jCJQ*pM324
zMy5A61j}p|CORg)nc-MAqd~iDMy$k^BM#PC8q%U_%*uuuXUZ+28!F8FS;UQ(a62<=
zJp8+llTr7;e9?#RS>5|Se&^+pf2x1zkL!&3^{qdvw=)`EuxC*IBLB)?``5dh8@6QB
z->;q~_5Qv6gZUihN{WXW4oow$a2H%3&MxP=Chcw5+D>QpKpVb9``^X$TXb^!KWDqd
z-j=(5j8P}=f7*`VjL4Z^?g}e-MD0H#uDbTU7OTPExDIi}1}gzOF*osh9THjo>s?H?
z9+hjcUUvMBq0+2bswrD-XU&>raZui@>=sMdH5T!0F()I}p6l)^{`XF2>%VP0SA(Cv
z+@==0M$d`6_Vd$KX)BJ;_4s1A@_C7(LQ_PEdf@hx7Vp+fv2}Xe<z`+b8f&p@d-0r!
zFY<(EneV#YIDNs1mY&bcUYpLSb-$Er@L*+EqRWoytFw+~8<nr#I%(l`H$gx7oY;cn
zm(}kISg&&83e|LYGE>woKg%~mXO?fi5~o}71pjo4pz=_4rWMUgtS0Wd;QcWx_jPbc
z<BvM)Cl8LkeIY(kn!DFOqb4xBW*^fB9>0%fM;=^!^0whXL{OPP%|w<5fk!iYgig;4
z)x6c9DXTJNL&<BaHJjO7Eay&f`Z*=|Sc$Z@%Z14tRYH#bECEU{PJW$jZt<dXR)UU%
zn_JgLb5nzEra5!6bU3Q~g;Ml%Y<BhuI@m3nWB+{Pw`_J*uVs54+zoeWk(Qg$Q6Mas
zr*z2IWyiw@T^#0REwiPS7tix)n0hiau<cRc!JOH_HzQwJ9f`QK;l`Bb{8Q%7YitaB
zXUkX;pT%^m^gX9%R<T2inunU#n!^?+IM@oLg0HJC-}yk`neuH_)#WSZ$*%GCGR~jz
zYEcJ!%F?492Cv;5#Ak6Tq#R9i_IsB#H%+~b`Qbk&LHPr-&T#x&7n1aE&f7IF8(*DP
z30nPEaNpYPm$tjc3Nuc6?~^oH&&A_fwd!v^u4fEFRoqqjN6lHz96$VOqQ{xXH4_)i
zja@u<^0BtkN#|a^@iE)NqBB#eaieI7?25-OlNc6nKdH=FqS9ixp(V4mx{_(B#S-5f
zg^u;To}3JdA}pnOzEL;a86>t8@T<>C?7J(uQOGIE=EL$!(`CCi9De*=H1YUtVZmVa
ziuCfYe7fJm&4M+ePVWD6Eco`6I|>HBzpfK`yYSJA$aS9Xcl~OeCiHBdo?@|Jk1xCa
z@075EGy3HBrp(`?^4#T%rQep24!?xOoKkZZ9n(1^pKQW$_ja7a^7FD2nl0VgXP)5f
z_IGcebENu5!|9WTd~bgAEMH^mDwpo?^MJb3r{L=+)Fyp$NJ>&LJk`eO?0SnK`1HG-
zN2HFQydoi;Sdu!$;<cJ8Tgtjco~kwbt~qE6&0Z>+x>9(tkIT+WGq*&ya9D-0omDx~
zW!3&hcxAv8zw+%hlQn)V*zfD2`r%dmOz+g$TU)lym$NU63#zMHmX~MpJ!`{;&Rw@N
zbl*no<ov@X%(6kH(6Qs(=C_kd0yZnlJb8TXW39V%oWZ+UKaM0;r|fAA&v-7aoVqrP
zN7%9dhmYT~pp`pV{_*r5S(;)rQ|jj&-+5c?oi5fmi=3UBC7u)f>tVh3zgUS~K|cQJ
zhp*pJ_B^nkA#cOAJ8R9q$9?ZpJk9;;K;%|~*cYnPcdkv?x-*@pcJ=C&{EOZ!+z>SD
zJIm)66H^#B9JF_M_wXA3;iX(JZcZ}3tG&|5OF!mN;?+;Q6C8G%OpbBBS%2Yu=9KkM
zCd`pp{M@c%(+2*lM?{zG<G;5<R3K*Us(mVw+a%I-V-9uPTVd_8A@OHieXs8ZZ4He{
z@2V{G7MmX|5-nPuv^M-srR^!uIK-k7$vv&DZ7(0QEL+WyVe&F%M}CO^_A6&y->q_2
z)-Zc+K7Dh`hITE`_(aiJ<;OZwbG-|fFcd!daW&dyexDsHhp}?X1rvocJ`;}I{3!RY
zXxYk6jkT;yLH6AmU(@q>yr%W@7iCNGsvK!5uhfwIu-J0V3%TTf#@#i?#d^<rEl^+-
zT3_%cY|0BGhAbAnNv#3R%P%RrxP4m7Vx!J|K2WGeXUd|y9iJz&uy^Ojl>GPFe$JE2
zzU!meL-)=DVU}xIWoA?;83n%DX{xgH-`XYjBAGg>kNJ5zm0jqWdZccTq}XYNyWKZ(
zR{j;-W}A5X^;@qyw%NB{mzcgxNis0Bdcn8+)w0m`yd5uYW?kAFx>+(=_szth>m3p2
zQzH{YCY$MK@9WXZUoo>~s(|MVPpcfYg#oJ%B>OH67pc}Z^vs>mv$`Uf#V*;%P;Sl+
z!Q?f2C6aU8-QO?ry>n{jL5sFL@A3+5wF6h#WHR4{RXHV#Jlh~Yg{PxVxZ7yK#vmEt
z4VQ#uk29@fh}!iyqT%C`r?!2D=1D!<+LL5%YaF}h={M!bU6IGq=Op|dC#yU6E&R<n
zo8PicHeS8rkrM~+yghO;CzuzcIjiNHaU~^5C5aT?e<Ql!cXDE~UedZseZdu6y0v=o
z`L(JSOG|~PyH9VJI?2~u!q3SsR@^Bn>h4F!g22j656{lAPEF0Jt$!GH^W6Pe=MSwf
zG?rYecJ0Q>ipCV=q5FpZ*ZNc69N7^3v^6^N_f}<2YxmcObSn!?CYGq=?lo2L*U^#j
zJlX6idGaZjUUq()Zzk`T^!^?7f7IVzS4&ELIx}kRi&w`5BXf0TU*B4@v;Ak<q&Wg+
zViT-cJh-Kk12}9xluuakk7L4X$1gFQSA_ROOL1|jd^A_^3tq7Dx?!NC-W=t)Iq$UB
znA{h+`&HoE<^oU7=^@gwLC2O^1?q^ck(l}5-f~`sEa%vjrr)z>Iehr=Jg;g_M+RTR
z^v<5xCDjh`Dsyyg-{cfd67N~%o_=G;7EcEi(=t}a{l#s?r~D2wnSR>D-Pv-`ZTs^A
zCzX_aA3D2deLuu5l;qZuP{EU5q!E$wdz!tE$ZRq7Nk!{yocj(P(aihumH7$liq1Fs
zPTM(j7W*(QwY)QBx2Ps(WOMXHZxzS+C;Iw)7<>Az?-b$8OU;+((PlMwVB;uSTbeE`
zu+l=ITRy9MAs^?Hb_ee%Q?~McwN|*EquXrHx0CI%;GF);@%r1|F;{hb&EGWHb;tCk
z>k|c7xb-UJHtP5Is<~<Sttr}N<G+sg1oIJr7h6Ls(pQ$Y8yR@GF$Z#4XDzg~wVi!>
z`kxc8+#l)}9ID#9kz=pALzGG774X28mEN+;g)W*4xMzl)*rGFY+g{Bx5>6ZLi^TGz
zFD#C0bDt1pGb1KV)_3i(#`ZZC+6r2$7QAGZIXY7+<KG53x#epMr!XBeQE8D??s|FP
z{SOC^K$T;~4oOdrW$%j$dA~Qv{NkmFdJ*6O5w8f?fQX{><x5qy%Y8rJGM2Jg^XP&2
zZ2qf?E$<$*`CX7xV(PyvzbHh(W?|9}1JC)firRuUn$K;tmH#YK%@!&NJ=Dv(-sgwQ
z<3RN#|1Mvs*}0pM<zWBgOX`z<9shnuPIu8F!Oqhz8L~WUo&CSJYREfpk5THK8{IyI
z_e{G}Qa}rzxai`?FQy$oplikE^8TZwLP)p;-<oQtlUc=CmCY;8oOf)qezNHh|J_fs
zo+>Nq-n(?~v~(GdzxzsVMz8H0JjeKj_VDK!6?4^A>W7-7lrO7o<&6zEvt!SWCmq^b
z_WPV|Ix9Q9XzBM}?hg%SJmHl$E_mI0_uko;lg<7A7oVN{5(l>TUpaf-AVG>ty=?Wl
zHIvOg8Z6F#JkLAe^4GYl7cACCAI$E{4qKx5Y}V_o9{O|5i<TP4WqebxSbr;U=ZVEt
z8e&B!If_1&dGl*It-t-T*<#P-6RXn?@SGRlEH9EiCp@sZt#YpNQgt)6Tl}8ary0aB
zZ_Q1=vq#-cSn<uV{#C~w?|ATn^^X<jnI9eN%yU?zmzCDuWsy}sJ>^=!KhDZ|CMud!
z>t{Ulx_bT8=e8;H=g(|>rLsUYA=~r8GVNcNE!#dM{>gaQY~y@pWu@8lV!yA)Ba6HG
z1(&t`xwYKzz>A%ZQvR==pIspKNHL~gaf_Bq%KsZHbKm}3RTqEV?(OwPD>h;7St9FR
z)k<e9jEoPN9~SUPoIB^$(G}a)bSkpDSf{)F6kEo4(Qil2I!%8Qu65V6Hhec=JYN1i
z@t4pA-^W+OB6D+(ttftecfuBl&*s&?3d#cR{roJI@iB4hmzxI!HQv;`XrHU)@btnt
z$0(JR>CfFSz4h+<;m39G&8M_2?ZU}n-;CF8ix8UjT+(BIG@F5GLR3R}J=3b^x7PMQ
zo|IGTxHie>-3x&!6}EACj63(K+~f|9jJ*=JR8LBJ<BMO8QMotQnrzAO-celn&|D+a
z!^uZU&FB8~edqX2x9=9+Z!h%AkR!XEVV#eN#s?{TyQur&>^(33<SkH_S@fAT-r6L+
zXoFdj>U_%&tkV_xveWr)an3L6xD(F)!TmVnm-=S8#dn{->eu@!xvZgn$1<B((~4XB
ze;)F+k$urzm-*++QANhBQ8$+r2flsal*>8Ec&=rX&(E6*o~Qg&QYxL;{;S?#W8Ani
zNaG%lIqQTcYs|i9HJmWw-h4{=$ybLD8`vZGs-E%k8JJCTUdO-aPxD9TM0>ufH7BzV
z^1KUvZ290xuKuOXem~x^l-@gg{;bHQ%X!wjbA$A!FuCzptzEXgo>gIK-N_`5!UOto
z4n}`I=>56!>s9NOgTG$ntjOKI!#!u(wZQLFB6rCy%h+?*GV8O^wX`=v(>r=5C;nSD
zbKY5RSp%kG$=dr-!TOmFMN!%jG7Eio#Hii&lgQfI;x+%T+kV~uYI>6LlfNCG^0)AZ
z`TWJ#+RbL=^R0NhC3)MaxUIfY+ONzDKi_}8;nLIe{TH_hM<o7gU-DBiwNB!j&5N2P
zkGb`otP_9x`owPC^<|%|)8y%gZYrLLO!@P|y=9HzlpI4p9=_{E2`pEmUdM&rjAuP{
zAt*buz2#BXPMh#qGE%$h{{DJ!_E1gbzo0UM;2E;J_>}YOzCWF)z5Lj6ulFbFEh4SD
zj!UhrZ4}%6{tU;=LrE{2uBxV{zYv<nm-vz+;ibUE6Ezo4+~t;e6BS{^c#4r<=E!k%
zcX<nq6YCUqEApO^-K?|y^b(m{)7Bi#u}-Pn+tsOi!`-PU)~N0})7C<Zsyk^tt#|C2
z4TFx~o9k_@XE`afIKq16&K-XRxZT+Daz5p%d^TIe@aS_cd*QT6n{uofdsQq>OiM^@
zn%e5Ym^u0Nw<6;$YfpT3Ty!@>*<V_#l1s+r_&px$qV)xqEPmTfxWndlCCvRO)0ndL
z(%;FRTi%sSSe?CUZuRAlo4$CwY2eu%Eq}1+R@!P~&a)dHJZx&`mb>RDx9+HgWn9MR
zmj^!juCuLA7xqd1{&RER<AltGR!_{mxOop4XJlzK)|N(m7s>J7u(t1c`?AiAFB^GP
zmMv7aeYfMGeyu>C>_Wa(e9@k}pG`gV@Wr%89vt_-aPGU?wP?Y@e0^TgMfJB`{=Z21
zw~qTayXUF*D=PX=z1-E%KY99Hvjt9q;^+DG=bw!$i`#Zu%KH9lcEyw}PG?d+3;pAJ
zXmhCYM(k&!<#9r-(LFEuHy1TD)t}m{?onAd{}d-LU%#7C*YbxgmoEQfisan7%(5?F
z(&-|03!aB-5BC-s>M%W@#FrcO_x<iDC5NyMRZ)&RE(=AUiaR8%Flg*aXjIs(RWT#)
ztjA%78Csd`5sW^E4Yc+JIiC~Taa=9r#JaulUn{;;s%#Z8%Q2t5f70pn&8)gLbsMHg
z`JCr{a_|qszYr~c752-UU+x$A^zyZ;?h5nC^G-fne);Ia78{H7ja+v`Hv7Cdv-!r7
z4Y{v>G}Ki&^r=5y{PE)so%*^6sR?IVHdbk7Jl5FrO|5h@lXy>E+P<C9pHsRy>(4bQ
ze7S9Zu>J3Dje3>GPwldk_`|g9WaJBFCwA<3?Y6;w+kKVHundd*^=+5Uv;&`OCHg9?
z{C-%vEoR|qbqD8No*GHf0oQvS?r#70!|3;gx3-5aB;ONG*`VDvuP>rvSIElgZfd;~
z)_j}jshb`7-FcmW410TqRp9pCl5H2`T6?O_&tBVd*6YTadn&U-Uu<1)b9Kix$=ZM0
z=LPgghE$9G|HyPU#x-X0v&fRFcYKNKgq~@wtp!sHmsduMS4H{1&Ybmr?vmxL-w(Y$
zX}{}zPx%82i!7<8Uka}>J=m=zZ2$1-uw=@VTXM5J%e=SYVY!W{%HPxe_ijz}m}8(L
zqp8k5Njc@Ed%KKPLd5Ae-Uk}}w<j<E*Xh(}V*KA=(pOet)&6*!!beF)ipzx_fBNC@
z>QQH~xc^+q3HtL2Ij1=#2rxMx{}X-W&~>}LF;WLjbXeao-8iQ>UAWKM@$^cr!p_`l
z*Oo_#&svk&f8|K-nab<Fcl>1TPMN!BWp!<mx@W+WvsbH|LXP+I$}>26y>oTc3b!(@
z&s3Yvd2d_bbjGy!_2H#<dSAUabn%^WUKu;}i<8wc@xEJ5PGUlv0;GG?#S-)O`y`l(
z?o4SYQA$ZV+;@sSmS3Xik;Q^fjT}l{JL6SetX{C*);-?v-2HvpHz(Xw>~ZGqf9>$(
zm6EJ$fs3a*>(|QamHg|Mo=(?gTc6mIB)K`+%x_tD_$G<h5{GTvuJDJ8AM9&%pZ{yE
zQfOiN%T?Fv-!Xd?&qy&>zS88-c9!|RG>_~2iyuCmTu^AJWq9O`c;b`Y*1yzE9-8*A
zEuH;A$>Ns&fq#{D(MKd&Y^qfh>fV0cafN-|=E_ME(pc>+AN+X|Qf6h|Zyh2sVd09~
z-+8;v95ig3YtA9#%q}5&dIMw745lY4=U&{5aFKD$Hf+ktu~L-U^df9~v-6z>zK~aJ
zLO=YLWnFf<lgr08)%c!H?t_m37oWDhdS%Dq-)&l+-+zMtu=0!&k0{sO*4|lHPc~c?
z5B)rAvBShL{$>BnCGxnmWAEo3SP;LUdVTnmeC<<GCwU$1@5t+UNJeH(zI1X&_8#@6
z!AlO>sVW(^*j()O;K|A3ofgy3e>mXU8VjCXB@b+znwBi>ete-a!zZJJ&GaGL%#tJ?
zEm0Yzm!0axOFvy$a$8W!Dchv<uHtT`t=Un#mU|W@y`ENmWL9f~*V8$tjVI06qHTI=
za@hP<FU^PpjG>z83vNDTv2%6J_?+VLdvWVp*=_DVop&Vqdj(r9R%`BhzvOi0`3pg5
z25vKsNYu(DUs<{3;e=(z$L0inI<V1~<;4fy-S2dF$d@G?^~>)uJzNuEIJt-EE%R=n
z7p@YI9eK9OSL{=|e=E7V?Ac@1MXeKi!o@!-^{zD7F;9;FLci9{yshjV(GOTJ@xI(}
zMRX=J`_9>()v=sXwMy!&@jHrcfAsGXaBDFTs@>9NW8*cu%BL_sW#^@=KbtnQHOq=A
zv9!!Qd%dDoBz1?C<C3olFPL4I?3QzupW$&?`GvLrlH2Kl?$vh^t#?JfXYmwPURZX=
z@8-3yf*EVCSDoJD*RnKn|D0tdX-ZKuyNtLN@9aF=d1|XAhgfNn&HfE5xh0o*8}&Rq
zQ`MY0<K>~Rd!`9Cifn&>xu$oH0@LE35qo!QG%gI@pRdus`rD&dD*~L{gllhTdWKwK
zi?n{3>)mFj%>Bm3_0{3sccp!KdgAzH4=D8coGX<48DRLlD37gxm5<S_-k>1lz^hfe
zen$O#sn?k%b&`2f=aG!05{I_?esd)yBzIQi9hf`At03ZFVx^@{@8%uCnr-~^uO9gz
z$1%V3(Tl1dYh;BaHZWKHS<}aowPV`+$q{#yq^@qwO+2_u?)AHA99&{gJ}^`ja7JyL
z#y{is^=!3AT^bn{e;!WaU~ZXtFGlxr-fpLsjWPLJ^*b4w8<if{obo?E_4w?&&ucjP
z))m{OyJgP5a9@3n<U^Z<OaA;7*FA9c4hP@ryeT>t#J@j&z2L3l`|@4;9p&>6@0)dh
z&6F#*+&_fBa+p3*<Z<BFaPiV#bAN7&e%){I!dd4;kXh`Rm|mW$;_|?~pSLbqV-vhT
zdqV@4p-YFN6Hn*P*$?d>cXfKb+j@Cz#RI{U{v6H*fxjQ|rk!ti#Ok5_L}ZKIBbAME
zq5^>tEmk~+_g_zsn8?U#HfuqH;*_)x{B02n7+D{$x!52nm9&)Ye?!Mtsh9>K6Ng<D
z)keQl%hYt{Jv$wm+c3@i%3D_>IVq_hQ`Gn7=p3m!CZ_H1af|uMHQW22yb{=Foi(+7
zLdR95nA)XcJqpRGYpy3Gd~MbK>Rm7`54;Rh<H>}wW!siaw<yS(6gT09K(e{p;!_Qg
zw-5ZSh*p|pYUZku^{{IH{Rt`l2RYvJa~d_IPl)xfeX7!=<jQUB(yqU%Av2rfX5ys=
z`>3kP>OQxar=3w{?p5v$Uu4lPT*vk)#xi!^@wL&?$<MNG{hzdF^Oaq}OZ%T^ep9e&
zn8%b<yDm0E{@U!_UmF(i2ku+M_$e-JMZ08+dQQd$$LBAl4S$<{s=BYPw_@k|wZ|$?
z9^ayFxR(3AWqrHO9?vY>1zSS4Hg{f0Nb1Son0)D4n~Y07SG4{q5osB_wYdQ~ZjYjB
zilU41qV<b*UO%_`h1v#1XF&sZ?HvzqUtqdtnHF)lMq)3g+b(<Ix5e+aoe;D9@Zj34
zUmFy^rstn%4%i;P|CifJeXaXpOB&N^W#d--S@h0q$(!CM3%*3BE@v!lzu)xx!J~)V
zm497dmGjzOJ(%qo89#MXWyaxO58g}>Q(Jbt`EKokb^E8)Ny|56a!!5Pwdv>TO>Lgu
zH#n=S*L`t!a5}Lp<J*syMFvxp9P+=ko6LPxA9wg=-Hv7L-F3_vbEma_Jh<xD;Z;%0
zDh1h*JHIoW`f}k*&Ks%MW;?jKr`hIvusL0rBr`oh!Ke1{JROb4Iacz`hjUVAH9Wrg
zXX&~-_c~^O?^n5TEcb1{!EUb2*&&)T4AcE~uM137YzlCmp1DHF_r8vZspInVFI;5w
zuNlmgi*z)5!4kR7QLX;W(TfI`W=_8PKtX7VqL`D^I=j@Tk_xI>xi1g3TKrhPHt6NG
zR_>#bFO4(HzWJX#FaGG*+Gqdl7T8Yumn%~F<}<4S*Ry24eT*;W6o%eiyHb0C*{u6>
z_qgpZ^RZ%bVf(yPK33{dRQr{)KYBlvg#P@!E$DXMyQ@9k4R4(GCGIQP?3nfLI(tLk
zs@bC4PiF1Pt7DW}_$lS^#R+Ma|D@ak@^x<QXWVo4PssXJ<z|e`%vbJ6$-L+d&hkIQ
zCu7Ucp8By|{K5ZEe-q|B)-cP?XgG2usx7D~s+O%TF@}|SO>otc<?X+JOuKkxP5UC2
zDDP`h=Dj6i(u>xu;ubIZdFITYl5bvyw&kYNHEX~9Tx%xrK`df%;-+65-}WB8wD2b%
zhf_mgc(zr0+kLgAE#b#hUz_e>(tXr*N@Ne$va9{4ja#>L+f{Ki>z!K>zm{oV#*7oH
zJB+Sov2vL9riSy+dG_F-%b}aCCsf}GZ|hXpvnBTib4~ZHiASd^Fzk|!cx15U=h3%U
zrRRj19bU+9d_7Z6-NW8DvVys5d7Cnq$APu^<x#KCCoEEa!W-p!@7JPVTa<SC?rJ!a
zw?!_u;%S(muELQp{weQVxMyVFtiQl;v|m7{tl^>K2Vo}%!!y6Xs08|aimNxhr*i+a
z-fN+I9lo0rCbxMQM|l6e^wQII`my7MFNGgJt~SY^eY)pvSn!Ny$7C*EcI(jewc!y<
z_r88}n|*~}I)lRAiQA1f&R_RY{G#ACm4j1QgX@0JD2l#urbaWm=fwoat;?>7RX>=(
z$kwjGt@7ecv`_j&wf?39v;J-0a76S`r;^F_#&*Yu+12}3NqfI~mOX18bK=|?zR#W7
zBb0W`a~8H>y|s1r&J?YmziWP-&Jny77s+uxM)%3)9UR8?S`!>qqF?_`P~Ng{?PKRN
za}0!wocxxxxw{=dmdTMg<3#oJ{-<3}R}1QGx1N3KZr3m7WEFKcVa>`ZZF=)h$8?uG
z)YOjGo|;?Qed@~f>{pAuiu0puf6igk3qI3yaJHS4mCAj~yba4(&91O@GS8Hdm-kRP
zXfL&oWAVb=4#6X?;+qq$pGo_Cc_K@nwcC5u2OVENd^D5R>Jgc5W&EyuhUPuvvm%8r
zuPjmh^}`@?N7%iqInSbAG+t)uNc)=~w13lOhNAG#)7!3FJIn0Z=COU3;8SJ?Q{~C|
zyPw~6U?@4edm+E(*9&X-E}WQnA^(@In(E}Y-i5ObrKX#1<6am2MI^?&bL*9x$<y2>
zTzku=z<fwhsf2MMcY+~DtiuZ?CvkyY4HsBj^a>P87%yM4EjgXg$bCz7Myk5e(*rie
zi#I-bcl$tL!LF&Ucc)k%YW!Z|shQN_aa66PQt=FT;=b-ff500;U+QyxZhm>@!B(Z<
zIsVhVu5-J-7d&=A>E&v!p0DXAKi|A~rlP7uWvYba-kQvk7&-r*Q=O+5pZvJd#cHyz
zPfxs*thC(RbA0|i{!7$0C10NY^yQU^Fu(pr_DL&Wo}R9LEO_OW0=IK<QhRM`G%G#j
z<UE&8UcC6`m6EJSpMH4!y0rAmj5&TjJ}JkJA3GjA{kWU^GWAQ-pH93uu|#dptjEPM
z;7!phu4%1x=daizyWr&#w<m_HJnp#Wy3f0CRkm&0<o7$aEq?2hnyS2H74zjQ41!f<
zeeqLQ=&y<{zj=$#CedbsgtSM`Jbx86cQrNP<mLwVTCdoNQG4fgFdpuCbpHxt<i$lB
z7B}9FGpxFlU>B!p<+5_owY}j6uh;1^9pdm!&EL55ceeSH46Zr%rZ3KXa9h*;aka44
zXS=o9va3u|R(z1L{84zyF7WdA0G7pG=i7pAU(@Qah+uqEYOfbDbIniTD|O5Fc$+-^
zA^1=5a__WB57uAe*eKG!xK3ay3uo$^wIRovHmL5sr1|AbRL@G@KOVR9ngnG9+*C3;
z7AQV+x$WIrQfM{nLq>_PNqP7~#|M9p>PdCJ?BJ7%{P;k83Ty4e1v<8KwznEoetG%X
zx>3)YElud<<rm3HCxc(IHO}>W)xtI_=+}~6jt|wYHLf)7EHQZ|9QsRUw$-O6XFX4@
zGnp|@L|)1w=U>6GI~NaTZk?WSV$Br|>DwPX<)%C@ek!^CuCd6OsL-D;Ek&38>sbCo
zT~JfesUgWSA@Rua$1XMM>)L<&Hap$16MB2HrnuwB+Td?)x|fs9f;0M4(tbVu_WG{v
zv14wf7gS7g<Xfh+?ep-+WIwmCeuCH3jwXpOZ<Jj5<3BFqoDlS@aO=<Yf6di4Z@IU+
z?&-niYq1dqKMc|*y<DO#sjL>bNo-}}$L}YM|4FZD*H<k|GHF_KW6#VlYhz30YaaRa
z*-ns<_vrg(nCMhrBAt-6^Ubsu22VGhyuu=#IC18#+v+ZxBdpysnQ~Q9HJ%$LPFu9v
zGr`j;DlBQUkl1F2MjOLZE;F?cdMIi6>~s0A=4oo&ThF9o@>eQAFyY$A>8Fn=_xL=X
zU!r_@M<d6X_ZFdNs_z7BFH5);XxndGvfJ#tmP66k75V4Fmu0NKq7rBQp8w2=y&Ko9
z%Q>WT_0(nE1(lv3o^DllTa>48r{+Jy^-~-6hpn0Jb#Hr7W@K4`$*Q!M<^q>wEO<qi
zHzgeT|7{uDGnVoLqJ~Sf=koI?e&-csvAW{@B)si`T1epMc>%7~M-mrgPU~A>-M9Us
zDchoTr$lmVvQ|I&az@XwZRtUlBTt>H3t#E^Ecvl0is$3i{-U$XW2-kj44sm&<3m-a
z_cT8qHMfKIR$5*==LLK|wf{zkky_Fn&4nj7+Da6zQ+UP6-z2zz_n&gwOPl*~D)$am
zPGNbo<K@Dzv)mj)9JjKTa)oMF@$PL1_{jRDrh3YXr=Kf?nUfY=I`#agTtL-F8?U!E
z`Z{~Pn6AY<&AL+Xd7@7mPqEC+WyiLx+W01`%$(zzrd%|`%mbUcW*@mwI??0ANtOvU
zHdFT)q%lfcF0@NEc=F6gFe~KJHVIi(W~GyIE^ZymN)9TU$f;T7aZb4W^U~KG!%Ky&
z?u?B$PMWUM)SHlYFzU~yw6`lHa)Xard<*T_yLFC6=WXp{=JO2ah%FUoTK0CG#IA(Y
z@Yama^)o(}HmR>`x$xlRYsF(XvU0BEoOt5v?&x!;T;q{l^WNJ1TvzgT`}N2DKe0}e
z<-Cbq+?Tx5GmfwD*!66$%L1qPXI@)&@;G!|Ic2uy%8&J0H}2|qshmD}P(}WyrfAx;
zlfn`V>L%V{+blhwwQ?Iz?|OO0^rY%i4`v5V`78TpJ)N@U<W#*FjaU13Ot{W%Y8lVC
z+DJFQ;r{p6?~PxZXI?+!`VqXSZO!%s9nRHWSK9vB_UTAO%s9OxVOyTryLCKEmBX^T
z*M6Pf>z%HD&ddD(|8o6i`Fpb>ca%On^kz><-ouX9myWh9`K#Gj9p<`fVZYwNMIpRT
z=XWpo{7YK@$=A}m_Q{9loP4%^%6hpn{gTC3#O_-uh{tuYrZ_R4(myIu!omMX_AP^Z
z%Za%^gCr#OipaGK&Qj^ms<HE$(l=>ZL)^A^Ia$wF)O`4-@|;0lU!+^Y*Nml1R^>WJ
za#-W<uHQ``1itQSUoqpa(e%A_y8_N-ol_EaPkFbXH+RFnDZ77v@Yu3azSMg69{-NJ
zx=vvl)0iv$X6*em?V0JDX;FXHeV88cy=A{k%RAXC7i>Facc)ipDDllK-j#mu*M^;i
z+f$wxmYsXxW`8V7&Sizej{VVr`_>-eJyUpC@8+81AcbQE+3xXHLOpAk)=!!=A@d9W
zZhm17$@dB;E7~H%^qtdlC%#zRP;jX|QS5!{#`ByX54f@zT3^vFbkwMP=V1RcbNhtC
z)*k7tJ=#f420^PjI1e2XG;~~;+!WKGktkBf7}%`Qv3#;k#{<W{$vbb#xZV10>#}uK
zT2@=SMaF3_xrNR~N1SR7mHuC0!lye^=hy0d9zGuBN*=%7>rOHq?iN=$wres-&%X4~
z`Tvz0LiZP1pD7S3*Xiu(R8?&K=pwS-wO{_D;Cxp;>5tOWUHK&(7rDk|zB{$nbnCA(
zpY*c+?N@%>+4AyMaD4=G*Zh~)qAQo#<geJO>Tsdg`;z6$jyVQ@T4&z0SGA8fsK52=
z+v{ihv$iE)DpHWDE4*B(Av^KOMQ-tDMGI<me``#r{a2E$kXfaezL5P=Nx?R`+!f{1
z&P;!G&;RsWi&-8Hn=-4N>swEzUp?D(Gp}~-&UD310uud^>nHktU%ol<z``l&(hjbs
zjX8M?ma;tTk~1u`wo5zoxzucFuvjsB+V$_t|L4{i-MU)4?)TYWkKQks9G;|`6~)s3
zU&3Y8|8vJ@Eo*&KV#w4dxmftb{1bw6n_HeOQFF8Lzwl_@BHr^!t&e|vYrMR;fQfas
z!K3^M8IupHcv%H;&zQmdU3A)IG3UjZj;3{-rlxb+M7<xMJ?PZ8=p(mJ|NMsu3R7;(
zJaPoIbo7hc<R`iJ%Y!bJ{&*9odMaX8i;c$I*-ZO)ufG!?yg%C^ZfThIy#~o0uGgcd
zvhnaVx?Q^ctecU2*Q{cfQ#+E9{QPzA%!(|y^Irbjr!^Z}4_{+F;Ij7o-y){kf*T(n
zta$8L`A}@0_@QZrukV~RI-@u5{8?Eu`_mlhZSM;n8_d+%b3P{3&BM+u@Va=Eq6f#$
zIMt=`>h6WkkzbBI+Yuyk^4j^@*6EWE_xve7#lYw$^6#YPPx(VNPYM)zF0A?6k;=63
zP_$dc`qcWCw42}eOgvoOkeSqSiRH)2O`p7+qAod2jJYT9^&a;oewQ7gFY>1PRH@CL
z@5jE*arK%v4P~au$KJ~R_Y-hx=}2Q%lDtspTeaA(<Z}O&`FjmDUcS^&V-s53SG4w@
zjqEM?L;-QOkMCG(8v^yti54%-VOc2u>=~!zuSCZ#1%@L{M^>%c@>~1D;o16iVRe5(
zBV$>V7zKo;?*09WKl_T1g-5oca_E({zyB^R+7(g9F0ksbgG|J&H|!}_ek_Y-{cU=T
zb$x(U@}G{--Rdey=Vs|na0pucP~<?(Wv%d{M#k#ZJK2w{tljhJxaHM?35{aRs)tiA
z>{RvqyOb$z*?yZvLB?X{)7(?IGOxRvbE(;saNm5=cvw6}=8}iLph3DPi;mRE{D%*(
zxJ0SUxz^-fem&DSd1p!A>;8vAKXhNQE-H@J68v^Bn|VW&$TJ_k_bV?-v&`{3)p%*k
zm8e^a$$O&o&inH5if*3ysAio_g{pmhsX=gos7c9lvE!c%;}u@U=wzw2Jvx83&ChG!
zZyq(@?6rp)m~timOrAGIrmu*Tr`QZ~K9f|-!JsSyTk{F}BB#SQzYMBWu$(c^_y5uf
z{_*+;xjvf3d!J}7o~GCOa-ynt>#M1sE*pP+S?f9d{Dyt?F@1-o_#8{^N*7$fq@<*-
zvR&d?Q;Nq_7azu&gLX_?9#$!`*(UFrB%{WioUwn|bK}f8CGU!(uU$F*J7}TCL8)2&
zcRPLN+zb3`*XnWRoY<%1ipe|XoOZE!z&Pi<oXy0iKPxW&devICuH^g9Uys$okM;k*
zobmT><m#a5QT?kL5;VWhb0|Dt*M8^D|5>RkT5ER%U0}Jkp>~E9fBK%SajYR?oA`cn
ze-ZxkS3E0y*}2UV((<Qtzcg-@(Q7;Mzv8<1)mu`wHxdmda?QAPFV|-W&k>^w1y#$+
z#f%t3LrzCNU;4~T#>lSr|AL?1&Gs{S_566BXITVlF*+(u|0b577L{U9biT?p_TC1e
zSyJ()<<0+oCg0a<H&0cK*Av;iQ~T%Smw%>K&U$lsrsx|d;ftqOw{ZOm`#Js9&+mRd
zvhOptseW}0{hKva`Szt5)`p$z;%8-^*T4DlSMa)*_WgCtHB~$Mz6s_j9bn$#bL<}1
zw|9<H-GvPnAD^rGJ9h6eIX8CqhzVUccg`$Pn`GHubSaOykL|%Hw!&A_)hC`V)HKwT
z44*Rn^sl4qe!KBU{x6NV({(dQ@=e>N0L|HYx2BqNif!Bc>vDwj?R(rS4(=`X$$F=^
zs(c?`e&BE8n{%FJoz8YPUU&GxfwiVz6PbVQed4Gr<jeT^W3H=I#<j$lwy2-1=Yqev
z6$SiSyXkU6<F@^*-wz%=EdJ?Nx5>`T^$YK?9s2ic)s(Z&r!TRmFE^=Q_HM_X-wv_$
zx_`~=7-Bb_DssK~Cp7Z#juhr!w;$EHo%`~WUEW^hdh5RlVhw-n*Du`kSs>$8noh&B
z#<~rw{%u%w?b|^HeeLgZ4RP5|jy||^Ew><i?Y^*i<|@n!&z()Y`}0?uZRCV&`Bw7H
zhu84dAC@@(OuzK0*@FCIf0g+(9VDDv4Yw#CyYRBa^6vH6gV|yws_a&^YL^_oq}6V|
zd7;SQC#UCh;dNb#xjjG2l$W&E&NjT0w!t`JX`0APlWvxR*NL~>Uabs#VyNqIvuoA1
zpKZ#g&Wq0O{UG)CmZ7fP*_xN`)7!ROQ!F)BUOa!DR{j03tlMQ*woME^C4Km9-Z|-C
zYuC+=UlWnHSurE%?tiJaUpbKR9<MW@q3?5;*w|L=G_^2z!E?)WO-f`zz?TVcxqsFf
zoGTQWZEAAvM71@K49^C?mlNkzb=?r0c2VTq3+V*i$m}~Ce%{LI5agb9Iz8>#^3CC0
zFL$oq>GN{)`FvJmF8TcxkB%Hoaq?3=_Vi=pm7~JT_2yX_E5zF{`yM&Nag<s9-g6_P
zs$%7czdLM#J$CcE?BZ<JJIAn;=Y#9x9aFBXcR$9oaM{K;uNsZl-$+nu{4q;u`r7to
zTUXe6ADLHNc5Qi2?z*VElix%?+7~k8SV!-R&_i~G+cf5$&1(E`$?(Z^m6O-{PduqO
zo^E0H-7<PtQsMChg<d=UJ~{LLl||Mr2f?g6XRekhzi_OYfAV8Ujn2oN%?g*M&lkvR
zEx5fk!-g;LO1g*Kb_uT=)$<B;C;fS89Mi2=trT2Z&3>YAT0lrNd(sR|!Av%b-y1rf
zahUsauafp&`+lCqkxjq&w@I*-zHYaFt@-)vmnFYe&#qpkvb5$mhgI#|MH+iTqML4g
zPu@K<RbfV>Jkva;DYc(dz4r3056ybudHnEJuh$pZ1SDiVw#{3y{=xVAf!=PHj+o9*
ztlqOI>N>0Gb%_ZrCi{P$tl`d>Q@Hxc<nAwP5^_CX9kp05p|Di^^BSe1phBmz`5|AP
zeMp)U#M3je#A)Irr)fNWT|L$(CH}@JEz0=6<8oBiftm+fkN(?y^Ni2JDY|8<y%M2P
z{QC9U+Pn*RehX}OUbf9?R&M>SRSKsVn>IK{H5x8dpYwd-qoCQPtbCc1{0j>6GIy)A
zc+dL$Mc+azx$BN-_{$Sd5}UTzTEFI$_@ekp>GaaAahK<48OYCiX1((-PuQ2;-yMu*
z3;ycAP=Df=zk-^<l$q@EZ%l3+tZd8_yUB8(q2i9{u?I&kT>itzcuUdj;fV&}={l+g
zdN+%Ne&kG&`XK0CcCggf^Tz^<!-xGmBFk$8bAr~%t_gU5XJUlwb^c1VrB@u84=*^U
zc`)Pn|6fr--{c*pul*Kp(|X8zspDE<iH}p`i(DTS1)poU!>aRT!yCRy+n!6W`;m~M
zV=jF*KH<ZQUAs2%KIUAr`QgR`N{r^>-!uguq>5%2m*kY?DmQ$0XerJqm5tL{`r)#U
z3YTJ_i*jI$){^ea&XafOTA57NOV4FgE+`1iUVN$QOH5N@J9szP?9T}qKITXGn0=>T
zQh$8YL_DedUV@*KMqfOakkTBZLz>DU;xgm%Gd}O6^v-VgFZsWB(Yo*i10FMB{gpav
zxx3C~UshS)<L{%F*yiitrg`Cuk;HY4)ia70%wat57MnO@5!a-KwiMpQVx4<p0_SD?
zRE{cNb<F3mcUr-wT{|=G`vjCZDlOV0bMWb<ormuH;W54`x<6sp&h>N6g_~9i__QWy
zu>>p5;5rz>vCPTf=)&sHESJqr=5Me}KYHwdlhj(B^%IyTl(mRln8-8F{L8N6jISnc
zTRTVNNU>S9Wby5~pO)utSRPn5b^3<~qPBT{*4?4u^7p>BoZ4BRrr5(GC!%uWgPn0u
z!<X83Zsi@HM0uZdS1;QezHH9hfYPmxwoh2&S=~FOzs7Jy%xi_LC3`+@jCsIe>A-Yk
z{w1ze3x9j~7qYls_Y$2Kzb5q8*DvABZm0e%_r8}A!e8~g>p)l>_XSRI=3`=s&c7Ee
z=Vt!zcJ8$D<W80!<{UFlF5mY-_|;}sMbRxnx$h!x1iXrp5E0J$`RdP`rF$NG#H?_1
zTdVT<sHBzO_7Hn}^I#AE&nLgB+gUw6cfbApLeKS!@2x&7?yzR%hrFAHd2@8mJukoh
z@B3Yio$>2R(*9lY(Gxx!aH#EdsJ3V8lm@o#;i7+wVkRYu3H}v)zcKv(m02gZ-Zt36
z_L%S7_S;J?T5Q?2P_q54U5&`Hl_6!iESE23@jHktYu&o|<F9F(&I!7C2ES)MvhQ=-
z<KlJsiH_pCibGDOc;xxqdXu;-EyQno**5U{>>0){Ll1wkJM{K0V^Ds}MK{+^;s5X5
ziGQPZvYhcn=A`$>ZdFdIkC|Wlf6D!jhcECPkYGE=!0}i`qJh0FfnlBhKZ%BQVUK?}
zqy+8Xxq54`>)lCz1O;r*aurnUUd(85F81phqXzp#(W{IRDS9uSuT4oUzsa$psWJ1g
zfuHZSrZsu_EBfUsy6ldzd>6W-z3H&7-!I$FcP{+4{IBMh1*+{Y-FeID+*hI4*j|+_
zCsP@oyz4l$`J49Am4CJvyKgwJ;8f$z!P&{}nvzvha$xq%9oyDjDch1W<#zX9H-_x}
zqLoz^dZL>pH^}^68ywXBaDB|gmA|4NRKDFiYstDjEYq(})$*}i^VLB+F8n5&|IMof
zU*{aYl;5LP&pfZQx-V(s{om5EVt-~{S@ZvF&Al8JyG3hk%`<lNZ(5VMY-!Al?zEsO
zcV|u9b*|!q=oMw95Z30dsJ&VFIb7|GO@^8?79M=RvcNR`^UgC{Ykp*HR-3W*x54_4
z({?{%nicRfn{~q4wOqO#g0D6Q_RQcGJD<@Tn2^Z&eL?flHP^G|C|}gd4Y~5Q)Z@E3
z<J|b0VRd_63tl&DwqRe~RTcZ=--TDr?fljbNqkQfEe)qgOcz>tG9)WVZlR+wn^D(d
zbGgSUoB#P|xx6q)zPnfPdE<q?r(VYyJgv?}?Vs~>-F47$QO!)5-j|LZIA~TZT6jU`
z(Yxc1W?jFt=Kg}%MWN->Udq}S&Rx8&?A*-gdkjibTchV)(bvd6pAaebxjS@ba`9Aa
zo?imridoc*cAVb0Zjz^#bW>Jy>?YNobxV1@wwLPG-rKc4+mg-7gPo&&M}&{Pl%u`T
z?gjUz^W9{vjbL<+zE_cxTlGHip!Ao*b941Xrf&}_<aGPHe6#UQz2N`1?Qg4J-sSe!
zRDgZvuh+6K1ip%wxLug};u3?;quT6+ml<EQ%eja(%saz$!Oov$ul$k6Q=8bhq$IXZ
zyb;dBXRXb6S47Z6ol#SjF-bvDRb{~~roT&-D_(CFOkkIp-F@KtiqC0EFUpjdw)}1}
zVfnYM;k^nA%VTM--iY-rWldXnznZ^X>NUfC5{G2>&r`Woucz$bmor>7g{9SfMWB;j
zpwkgo=A+Rn^P=V*DK8becR1igR!Upmr?*qiS;%z!W830?Lq`6eRmv}k3+pV-%T~^@
z=`iPFEa&*Vv38-4^JD`frBxMb<vA@KIaVDLeSh?^^e*`L!cD2c4NS0Hh&W$v*s#oB
zZtjc+9RE7fj<|^Qb}V|CAaeG$XbMxbChuLNPprBb_hnO~I5z2SiF(WuZTy{O#?|Gb
zoO_P%*Xb5te5S@tC9vzt2KT-ueJ;&^oEJSi{B$m!oE|xI!kW{|D;s7lIel?u;qeQ*
zwpLw#_g&%O(&nT**G*jVXW}w7UhPove#n_rRb@8aJeT2`mGXpxi$6SU;55Cx{+hG<
z1ODs@=iiv#T5yirNaIApl9UTKiVarE>HSu|TH+tT4Blxv>C@xBJnN6?@4qiCoE1Iy
z)*|(0`JMZmWsHrYcJ1qEJJun@VPR79O4{FBW+mgy$3=(y{KPN52q;?NYH8wO9HZRg
z-*(*8scMSn7Uq<KZ4Ik68k~ELWmo54pPKEoLnh~iZohE%T=gsK^}DsbSEzfgm?PoQ
zv-56*$c80L7BF5E3Tpb4!t!;&5|zHMZ^f4^>;0P6+qz73={J@)_6j#XJy3a4%+n=2
z|LjC{e>Fi_yXol;4ONE%*&JqMhu7{p!sOxk_+@qSY)z|=kv6I(y~|w=&reveOm+2A
ztpJNF8t+5)-)#T(;OzBTsf*)-;!Pjy?|pkMykD_m#?|M)pY!~=s~OuUx#H)ai$6bI
zExvd}=0L;Dv$bE3`>x#jZ_iHm*v5BPuE?cSo&DZ1VdlzdUs$YGBscX=6;*opVV+C(
z_MN*|-1~XUJg$7jNySIzlNdM0)~P9bbt|<eY(Dw8S@eQLbnOI_=C59hcivaH@*&eZ
zsVZFO(Z<=&H@!@HmX*@E#B!5@r2Uu4FJ~-X7i_SlE<w$GhK1+RxpN-un7sS37;D!G
z|Jq$^j{Zr!8*0+>!^z`d;w{IHk^`}csxuN5+<qx0H{5<`_~~4ap?P}GR{Oa(I9A&n
z+k7F};;^JLSHSOyli#YUsqN{nIChCsQeIF(%t+DbLhp6;FtLD3YzrP7ap&Gu@a=5e
zNx$9U8E4M^-VlCAeAl$1xe4#oQewWOzrUu+DgW)=gY?OTZDp@>Hfe7U6ltIA`R)1a
z$lYP9Udvic-q5Vd=sP!Qed$c`iPJ596<gW{Z~9!K6c)8U$p50}hf|j9b_*6|#J#un
z{*}MPDR5Jq0xx4KcZ|oO|4W!knpW&6Dm!{@LglVS(odTf^Ke|eGSN~&%GIvP@yo41
z3q_~pj=7R&On4XFp2xOUhp}Or$xJf|pR;k!vTk!7wqDCVSP;aywxEae_zVyc@nBj@
zvX8~F3%d^)9<%&*$WV-FZb8r2b;)Z&{H8zOcr9W6jezWJ@BVGFNm-uT9})A5rEq#B
z_g%~1A33?}&+k5QE8%dB@!YwG+SO|+?wtoyiWj)#<4g=%`Okk1et5W>Z_|f|f_f!q
z+oyY;Px*ZL<)W=EoXbP|q|YbHR%ly0bY8A_^j7x2dML+J=LZ|3HofM%tg@`2W7!#R
z@X1DTpX-to3|}q&(`m6qDd)4CMyyZu1lGkdbF`m%-Q?hVKYO-%OcS%T(6zQF=bN$}
z58db&SbY55`d4$8KI@)8RlfdeU$uSKzKGy&@0c5>mf1<?>hc84aLIdJSHE(O-n50X
zp9*4vrkVX_>@?Z;vdXFc+J@juuf!zm7cm{5X;mwAeCA449VXB7nhejZON!Vs+-|I9
zY@9rIexuRVf(choZ40a7F3Ix@s$bbK{iVM8>`%3ZzRVHvL2?r{m&D4)uH7Im@G#qd
z3k%Et3f|5$-}%`D*_@snaNa)Q?Op*{_iM&aXVvdIFn_LY^-YtB0-cAyHaWVVUGP_S
z-SUso^EqFyxHC=LP<8IrAk7W@Zqg0scWwUtbiFI1-}1+kK9#K8y2bez$63C~&dx`3
zmRZc#=8rphK3?{W2-|wmlN_8rXT&|MxVvXH@0_%}%};9eJG*ALf7f1VeW?=gv3odI
zS}kAN>%_bqZjXK)N3D?cQ|8ZG>Hpq)S3z6p(<;_^9~(hKKc~_J7chNNR9D%~^UQa%
z<53X>Yu)u{xLu-l#8ogCCWKEqJjYS==F6WxQtGpQ-<ftnmaEz2U>NiK6Un)Eo-f#@
z?xp%cb-LPevxkeaUW=Ai9(LKr{p9d_d6W7~gOl#BSI^q@(@Dx(Wr?Eaf1`j&PgGR*
zuVVhyTV(h)duhd<rmwDF%jSyf_-xx7-~NTCX!_c%&)yZknDu)4q^w+Dt23-S-xQ|*
zV%`&<e>d_8tIo?O5@~PhIG3zhB~q9aq3kBN^6+DhEeEHc*`QJ%Bq*1pQ@eR1hxzks
zvu+>0`%ufKM23A)4%=exCBK9NMK1{KI&*vV>6T>%H~w4>3=qvJXi(5ID@;tA`%KE4
zztnNQ=#`^;)K_??uQ>hZj*p`4+o|z6vmSlEAYaIzeKE+^!TZaKwc9RFVmK})Il($4
z^s3h?t;(cL7rm>V81G19FQ_}G#&%=Nw0+w&RA0zQr#;`KFlq6b%=8mA$EH0#bZWBZ
zqR`1gK5`OMHdc77FbI6&7tpdGMm}Q^$I-WWi=+yt`5WBqn6YMYNVRzni>%%izvG1)
zdReYyz0F;>Nph*r#fx5VT^yMvD)%(&$T+q9J|0+frh#X(xGMJ_O(FXwRzGeEzS%hO
z-ZX<-Rp;C}gq>z|xXgGkMQIM3^K;E-1)i_ZJ~nLgU2U!UQX_(|CFkHp-iIw4%qJ*V
z?G^g?W}0qcSzZo@<8H4bt^zu}=GWS}=jw?RhZiL(Pn_tq;*#}omAz@~sz!%jF7{WO
zvRl;CjKeJ`tZaJb#r;cy3+<dIZfsYX%dvfHRgvjlFY~>R#J2Tpxqq`%e3DwScff%I
zo3`!Pyrb^%r-_nVz1Jn4f1os_rRnSnR&h(4#Rf9mw>em)blA(M)y=Y$IWn~^?4Gav
zy*K<X72iCca8O}Zu66xR1Cy7Vihhgk@{!)Y*~|a=5uKRVcZJsI<g+km7j&N3)tG$O
zDL|+xs7F=fr`~HZt5dTl^|h-ATzKqP__Dl7;n35qj!w%gHzrJa%Oa%^<7V`nVP<2`
zTTVWgH~#|L-z7W^ySwUkzI(<oRqcy{;Oz;y4ArM?8Q*lsof5eGxS}J=h5rX*#@?h|
z%v(~HFZ|0WBUTp^w3=aAqR9^~&$OlSzi*sKOSSnZH|tE-gyXBW=e`N?QgxZRJ!lH&
zp}l(=mme>U6;4b#wl!dnPf~#TW{=A2hXQY|sA`yMnWi*Ju5hEpXZ1r$z747?CQG|5
zF*N-Uy^^J1&iT}Jsi${6eX3DqsCDa!U}9s}#a;1JXX%GWzg}>^y8G<i_-f}J$CO?#
z;$$~g4*Yt`ciEfgRY8BAy$*TL@o1;enU|dXo{#tne@!Yr>(jl?j=78D#Ujn`vm)(F
zH(viUXN#Tp+4xrhK5AZvwk5lH9BR5=V)EqB(H@;;!a7e5IV)McWq*)R%dY>b=?bf0
zVd8sv$KooJM~C9JzUAM%fcsL+WRJIwCnUIE#Plf`&YgLB_S6}EvGHu)u`6>nZ`!#R
za$3EV*tNO7PxD;=K5SgQXd3s`BN0hTmTt{cG}9PYyj+|pyxa1buXXRSGgj=cGkQEu
zK1oq>J?y@Eoq$R==e{q@?!FQ>YcdQHSKDsLwoNc_-S|jzsm%t_yDE7lc}0z96J#sy
z-n|z%{}O}yypt!SC%Xi<Chf~yxbjox&dGOw^&bgos7Rj4muTO9wmN9Xyz72^&Q>)s
ztJfu@)TPU0eUj#EUb!{Fs`Q%t>T+?_)YRy|`xe@)yVc~Vo|IDa&`N-5(|Ns8w}i5_
zc8i^_?JV_vETy$rOZ?%DUy+NK=;kf8R2ANF{>+c%eDX2NAHO)+{Q6(>1|>_+wDlQ#
zCI%+k-&uY#BlOar3@w?h7X>UcnbvE3_!w;O)sq}68Odm<DUrz&dcU|P=j)M|_C9m%
z3Low5KG{9}a{M!+);|x#iyv7`xiDFI^V!HX4JVjCEOHYNyYP}BMyv1JW(KFEjQUF~
zd-?B8;^Wz*`6joi&m}kiv*?c5^S(!w{z>inmdC05NIUUWnLP8-^MC%#^FHX@($?`f
zR5yz2Hiw`6#;<pGS9^#s78+=%uVrq^;o5Zadd2z)XFbkYKiKB7|KC{+#r5|hr8XIr
zc%->y|Bz|zQ4>Dd^5lo;%N;HiOxY1<&s?gzC23f6)a5>3@?o#bvYDdStG4sR%`i+o
zD5id#Mb*||ib=^<p;?h!FRyHv%y8_kMb&$j=oeXhM|!;F<|*=@ILWyDE?+zQ<QZB5
z3i&}=BIgYx*><a}-@nz_zt6!UtJld$LSY$4rh>+fO^ai)XH0m1V17W;$+82{Kd(1G
zJ1w&%;o`B@?2{qe9v)m+ANsbWULs-B92uXWivq9t*(DTi-?>xIyR2lviR6jPfB2m0
z3`xpy*jMq##^;o&S=UOtp0a%*GiDzDSyo%RXX`x%W5?Ie&wx**oM7=V@$BEK?7tID
z6jYC~A9Q_svaRHyQ(*lIr+-X;;_F_WXyv{<{ovB*_xg9V^ds(niu_@^Zt0bd!{JTe
zSI(Ax_Wfz^zdwmve??WsY?X@)ue);*?QqJLBEAhlo^d-=`TNu+eQQ0U>%br`*0<!z
z!6k2Y{8=9TVAHp>8&j@1)PL*veZ2lsU9M7C*u0fjxfYb2t$NzA#%$Wz*Q>h|J_mSp
z3lz2dNa<L|qZnlWgZoLCBm={JwWTbT2PbJ)J@tB@v3$8xQn6gL$|4VeKmqTJ$;#)F
z&P;N;vFYZONjH0nZuYF;l$n}5<>a#IC*nGUA3XF<W^Z-Z==9ZV_dVju?5fe|d#FXf
z{_PUq|N1vBo}0^+5p*hc?c9zjA)D<KVv3Gd&6)k_*Vot0=G>p3{`8LQWlu@hPhH;r
z>3#pdhs}>VlUMdHuUjsvY;9!pZ~vyBFO?T3zl)Pqs>}B0;oYJk-nH}7*#%YmW#{Kc
zOtN14OPIlB<=^c37Af5;FIHT65#xE^f1$<Z^oP#!eWiirk0au(U!GJ<lgqq*GxxK_
zhC03o|6qfZbbW?L8Luz4m3%o9a@^J-$AkUx1+(~aj)xT!=bk_IUEg7YM11+JITP-j
zx^=LW$tb6d-#B;fwbTW(BwvP2*}Cdt{p606e4bk~?HdlI^|)o%GK-xF4Uf~(zJ2O*
zTZ+>xpChXO+Mga)`selG_KC;qHD0c5>v=Q9x%lXv>3qz&4=ylWewRLxd-A?g^%D(z
z%h_*m^T#jze(-F#45#hVm48aUxgC*d6>tpHJJb56!@MME`!e~3n=1qrj+~Zm{H?6<
z{I<7crK#w9&huue$G&xai<#UcDRr*K{@%`_dj)fsoO*6_{nNw#O*0d=ZCP_EIy=Rn
z_SC8C8!S$QwU!;=o|oE{bR=d&g<EzH)9Hjo7kR|4v2^rIF}pZxO4RBVuV0`2b^pp)
zHk;pz|8x|mR<Z>K+uE-4-~asImV<|@k^{@v*r=$t2|W%{GM(w=drJAr;SIkW=ADUM
zyvC-!I7jtk-nIWPQ{?299PTL<XVs{1`Sknwg}EW|CT44X39O3hc3|GPQOiHh`&5x@
z<tF~a%c9Cxwe4n(%gQLb{<S(PDfe?)lMy55t%m2YQJ8;|>ZPMX4j(>{c;`)U?0%zQ
z%~KL~^4IyIR?S{N^;cVAXVEmFtrse+E0V0-W8dC6{wVREukd!8%&d;{3{kI7x0zb5
z7CEBv(6;5T%#}SU^QM{dKe$<@{HJpF<71yACf238ue80$$8+!A#4ES=uZk|W);O3P
z_5S4H(uf6bSKoe?b@$7gBdYDw&-`0tS@WPT_*LoGMCQ7zcki9|pWVwo>C<K7r*;bV
z_wATw^R`7gooh5dapRG&g2+v!0CDd~-D4*dBhEhgbbZc!)BL_TzjHr7XP#GG{c4W0
z-Zh!fbM0QL6&tK=i<8t=3u-@MeVQayzITq{`dQ+ZXFS*Ou}zKm`t?EYd?)W~p^W^;
zyI0=IXv>~cSQD^+^}okke_hp*Yqo86Y-~_nX1ACBaqYqb4bM(}aLH9Y#vWuO*SRyt
z=~2KuJ@ePuJ~G10Cli_Ys(w1~xwh@jQ*bf#JCP~#YJujJec!Vj{uIcV%+)_9P^$Sv
z@z07K=IIYgxu48WRghzt`fYWnl0wdzhfJLcEON_!Evnjmu9ekhx!s=C%9Fm_U%7Rw
z$`5<S`Mxul7T;dK{hf}{g{{}*P2<<BjP_Ug`jokJyVjZ0hdhK1?8*5fH(_bvt1I?-
zE26C(`V5vNXe60@*K#oWb|k_oe&(XRLEe>#E1w4#X63F~tv!3ztuH03ckb3YIQ{y(
z0~72HoOEd4U*C|OdLv%@nz!ov=$TF1pWT|f<tc~U|F!&%dk!C6RJgWWH&oYX**<~Y
zZ=XE7D_69K%hhSWvIyrw4-Qu+4I!tA0$nWuDlJMMr)def)z1H>DI}|Ry*c}@gQn1r
zP1X}Ng_@GMx->PG)pvDHbN(zMvnXwMuKP~KX1|iY4MomX!CX9o8iiA)wz3$C9l3G7
zaURcwr|U(!%oqDeU)lAYD@yNd@~;mHU$jDY)ZckoV^hj6*;xBTAgOObdmG24#Jj>v
z7Q6eooLkZzzUeqGQ?XI_Czn^H7O@SUuNQfVH{^J)Tf+E4_k3Zwu);Q}cIJiK{Y?2C
zbnhqckJLRc*!O$Us(;^Or<~exnBn5n#c%BLLSL;p{5vhbKsBO?=lfjMtx=AN-@MgL
zf82}Skj%#ru<^wC^JflhQl8aVnjLvJW@-84@@?l{%W_&rr-?eJ|DQ5F>eRH!K?^nJ
zHvPABpF4f>8qJw!{hmKd&x|&5yFb<N(XpP5-_m+E+9scom3kauI-~LFo#lopI_fFs
zbH8UCyJ2m1ERHSbz{793J;|?)tlIA%*x0c7;oa3dXa1!%ZIDiGOjCL=&*kIyxsRmZ
zUCOCSYujj^eCDm>;<pzhj;r-<{GQp9zWUgYYwkK{+Ikh4=N`(rB~i8}IVRft*w(Zg
zvK)IGlWVGH+zo%^*T*_vXWw6$<iK#VicjbISm&;I@LT$=|9wmQ&4tay%a2!>RrhSX
zpIj4dcWmqH<T=vz$M()iQruqO)6R6nBUte9Qf9+@qQ~C(i(FAS<}0vcm!C5C#(?0H
zzA{NNUm92IhiD0M8}=^DaP4W2IdUO6^vvlSt9NwI$iC-2Px~GBy7zlB&(2{^dd6Uy
zDSuI+VR4Abua;X<2D|L%vRv|%?|!oJyL*6;`@|+&>&BhTGqmsbUfOysTzu_QKi5~W
zcfU-1Z1iw})vMVSQ{`T{&GTkT<ztxKE>vE=bD{Fi+WQf?%OzHyT^#!}Dz`)9o0^{4
zdXFp3tIx6@Gt=F^KCJg?+rtMJFK(00|5GGw$#MGZ`FSrsRsFv4yh*t$vE4x4^@m@_
z2U!cxr3<6ozV@^(OtP|%oniY>r^L-8Zh>txck!K7TK99J<Kp+PD=HG;HVqTFyyEz7
z9>MLZ+Tjxx9he&R`kb0h!M!`xYyW(?qvrE>rgv(^lIQP3cL?~_p4GUyW#Qa!KdaCF
zxcYDh%lzs;o8zjZ-1e(oT(4QSo;kfo=WO<NQ*D9c4`rS|t`J{)J|w_G$=hP{L%IAf
z<~NLF{dT>wZEM`8m#5-2dBJZ#?=?a<7s`a^t~=<GWnk#`b$Ryl(lZ=-k2i3M`Mzkj
zywxca?yd4>Q=?3GikkUsWnHd$zb>({I!xxZoaJxjB<|7U{98H6!-dCl$<dXcrgLA6
z?BaO3?naS%qn6Oqq#!Y~{xAov8rQkDT1xpJwXfgFT)s^u|F5?G4$$RSdd!=byO}1e
z{rdcDti$*0?0ZMjPApJ;aeyZ{DN$}Rla7X2GsiBYvZ{?IIjt-ro<CYLU9R1MEx~c7
zyMDT}<20Ft%FX_D0dp2UIHYaD60xp$vYtWh!Bsl3v(Fta@><v0xx(y4ojR}N8X3=(
zCzqTKa=CPA<_Z&wsOKdrfvn34JF>$r-#e8x@1d)sTj*If#+Bk*FA7hMJ|3;uIf=J$
zuGDvx&5!2I`5?KmbkEe3nB<<tJMt#2zW(@<chN?H;1hWdHZBv2Yg*#&u>5k|8qqF6
z+qO`q1x4#$XB%)cpE;9|(z?}z@qG0yiB>cBHQ6t+4*K5^J``WE_`;LG^SLok1a|&P
zEI2U5t3z(a;kp1L1)*>9f{NNdy25lP_L$Dqk3BD@EE{m5H!RM@V%`z09KD{qy~2yH
zXB2OJYW$@mbp6>Ml3TN8|Fd^m!ne06TI9jW*9Ti|7_!V#KBX;IICj12v%)*jdG5#B
zT@&>_wQ4+A$+hvAW_kYJSk<f6U&}5Bf1T1ZHH5{)z%RxzK;J1)%p@T);FFJ#SIaK7
zOZgLa%`aLwFY_7qWHnI%-o`mGK3AFz5AI&NaDjtRJ@X?I)>&3QzK0K39%?OS`*8GU
zlGtircGsJ0);q@^Wl#6tR8|`DRFiY==||`0w!fcWsWvN^;o0mCC&O~p>Gqe|G$Z~d
z`rh1hq|ELi?`FZf;YwU<ty_Mccz9WMp=H9vN_UyX)8enZ&XHYc8Eu>Yq~Jx;EQy6C
zi9dc#n3k}XX?t_j-tUK=PP0q-d%fChSBdfbol!bZit8J1=6tA>_#hWoB*m8gPVe*S
zd)yz6?pX4kKXpCRW+5lDzb_owjg43wuOEHF^R33^rlNlp+xhbefi|h{TR(l87%Rdr
zw(Y{nmQUYvBV3*CMzu}jVPTy3t8??ML+7uB+Dq>+w(Z~QaQUX1n#k&}+f#+=)jL+d
zllT~OO!<4@RAnZP1B|nFpYP;25oaKBzGU5$M!#joAu)As%eTfcGaotRE~ofq?ZV@p
zf3vnmt`9oK8D#u6(BsL!U)T3L6@UL+6?4gcZO-K5=OV%^7FR1Qk+9yaJwNC3(en=M
ztdCd(?lz=x*#)GP#p!2><pn8LmhXNtM=7Vt)av>AbLZnuG5d*~6sX<I&pm0OWZcJD
z&Kb++oSr*Px6bpnqD8sXLjI|%KCN1$dH7b#@wYB(bIXr2O6qBePh2e?@?GG|TdDMe
zayHYW>Z4g!cRf{R3i!BddHj;J6*5c`#&<TFtz8^(;Ihl*S%1pi?z<*llRd++hd=nn
zETLJB?{nCvTzP*{)lB<>w7#OklzeTurOY0U0gnshpJgAurE2_9qI*RmTkE+~$tR*b
z`i!q>O<URS(E50;o=2xk)6?TiHhmL#QNniqm`48w!=Q)^9fm+9<4ZHzl`1PWB|@L(
zxlT$<DcZnW!}0lI{4=A)Hd_q!oVULT-JkIC>5rb&=N_R8&Iaz>%#!kPh0=Q2-4j>L
zOS<*-)%+#38dKaBnz*es*|vRxL93&o$6EhqE;`YY<qr>DthMs}dU(Q*B)z)_b3&Gh
z<jm2KQhy&2IOSY|a$B%<^Xs|yR;q43n@~`wb9wrwmu)i?=Q8uO_1FC3mAtanbOXy3
zdGnRt6BbDPj;*!0I%9>jaN2|wvg|Tyd+P3}9X)ncwW-Ue|0?Hd72(G<hvu58YM*-J
z!FGSurubON;suje=>Beh?Hr#s`_P-?=`Ylth^)PNKj=-{y*taE`dppcJlAts_IZ=9
zlACKhxk?H$SiVkK=$yP@<CYf>1Xfm=I8Qv*r|3JC&tcn&Qza6)yOaeP+t>WJ+VZ;c
zz+x}w?GNPNMr{6eu#lm9K~z11++piITNhuOwCR&+%I;`UW9DfK-?UxZ#JeCxG&(;%
z<NPz*g|^>2PK&O)a~U*;8}WR`*6nLF$~V0~UN!B_@rwnmWj73MN`KXw_kDPBI`rCs
zMN$8lODB}bRs~+>V|$bkwVf&Vjg#_QuAQ8drZG0}sC7-4D8l8zc6>+6lj(&uSB-WS
zEQwH1Z~5X}xW`l8^UATgdj%KnToPir&qH6W*s7B$IIyDl;SUMHn13!B4tH02sUKXl
z?egiPOM@@G%2j3P_J01L+&GypQ$n`#fW%AA4=RUO)aQ!399$C26;RIkc70sKlIW5n
zq5qcrEn(5$WXXPwzo0+4W!}71)sk5(nj%X+BusExc#flVS@b!FtVg-^+iDG1PF?5S
z*4=jcr=$1c7nlDhKK%4@sZGmY8KZ<li-mvkOuFE=>~C_Yk#(z70Sl+&EkPBR2@}ja
zS{7{0y1iPtbQh2Ay06SDlm)X+yRBB{T>0^>>QBcMzQd27`GiY_y!lWut+qXriFZ%O
z#iu*BNooD6_&)K5e$xIgOFFI`o8aBWaD-#cm#Yfe8HHEg{?1RgSt>eF;cK+~RrhY8
zCQ&v$v)BDorsh7aRMuN*Uzh7*w4*utK<>#UTpt1qnMCELhJTYXxPQ(*Zn2@^ya^KS
zzZzANO_cZ_ugTH2zq{1)%n8Xi@1<USzWR(ii{%Yt$K4()i;Fx8lcy~AUaMNekY46i
z9q%>k<kR(kzpmHVlkv26a_#c`b7^nCtWwd}YAw&V4SdCaphR2yG~3}m@3VJzO*>n{
ze9ZZGKQpKHyU$LWN*I(|mQ0_sc2Dh#3AYcX8SSZj6T82o#dy)C>BUDjCcj8Zzp>@>
z6<_5iR*KhM=hQQX^GvCl@YUDK<&@TrbCp7qHs7=Q@9E(_<#RLF0-?26)AK%xH%|);
zH&KZ3_#AJ<m-mZ7^6SPJ1#3pZ8C?E;j+Jv+*Su9PW=^ni`Qv7{h_CtOIT3~vk6SAb
zs4UV=O*po1lJmpL;xm(*QuZb{r=@&WdD<S^%j5t0w(JU%%FFiF*_At)_urjhbnAe7
zsM-OE75`Um-S_wZ?cLw*scq8vU;abH)MK}>iuy)bb%!Mv^TaObNWD16wD#`Jt6D~W
zjy6BGZgVa_zrvj}H(=M~nJm8177tS9&b;7xV?n8UBWIA?@7kcJ?>Br8e7Y*S^!?i9
zr`2Lw4y<4G>+W^S=l{;_e7<GwX%?RybBq$D-z=2rdM9U7SF~W?9d>WGpZ~Vr`}AUM
znY&Kl%-1FR=GA)KE_Ob`Sn#>G<!aTu8y-46hZU5!stUjPW8QZ7`OFpHB!&Bg&wnWr
zo$X<u7El|{zPmK{Yw_Ka9Lt#NU-GFR?<z@k{uY)y)iCzr)rfgN)y_0}%yQ}Qvz>Te
zHsM(Zi<F>kh)(mn8I1K?jhE~FP<*XwD;&lnce-UFb8*N1_;-<h+>dswaE)m2J;-yF
z>w7C(+RLNcy256*rcM@K{!3SmqwP$A+Vg~@uxwHF&1%d=$znQhem<I$zju{>$J4e=
zn~q;vHnXI%uTLR*W?jTIW(m<#x7d7s^sb%u@Q6sA{NzW$r`o0JiaS%Y0|Z06ua@*J
z(%#e1^r(I}E1M+G{;2G%JdKch;mR}nul`zTrkudpa5%YZcHhgz!dq4u1T0<{`nkx`
zc)ngpy7(qB!GpQdOH1o5L;3tNI3IA{%P9KuDMf3lSDdd(@XCs3+TS!!A6RZ@dXV+*
zVf}kvbALsLDH?6lFMI6bbwI?%XTnRHUbBbVLeb}C^DiwqcJt(>sQYP3bA2XywHzs}
zf0(_~MtTo-qvW35%+0^&{8}r`cH*6O4`;{hhRPVvfCR~pK|2@iI8*QP|4@vg--}g6
z88@P~o;$|x_gZmYN59IX$C_XG)x8azdw)j@mW7@Rezy0<v4)8nGrQOP_%J*Bx5*Wy
z(#34e=atlNcP!`sFt6=av|w)Trk_HWboI8G{PM5WzF}pf&;KT0;txmukqc`+ScV4)
zPwwmtv6<*|Ayg;Yu*$Jzic;<2BsUj#<sOGb7PrUI$CE4z{J4Gh&-v?OujbULZk?l<
zZ5m^FWXfxH$vDq-4_jaGd#Nt|cWcew3zsh$TseODm6vzz&h>Q`Y@eS6@o+pZS;%B}
z=V0lrbA4H_=7&2jZ|+t5@$KW^l7|-!QUdqK9*S?|_x^me$>vU`oc8MOxm&mW$`QRW
z{YT(~+wL#jUn(8Y=Used;^_zb91@N$iI1J#$tb;x@%`Vwe$V?qv~DV2HE&D6(naa-
z11`ShKmH-#`FC7U`SdH-iaASG|B|~WbH_Y*$(@@e+%>8Z;@i55%|e!)&O7V#|5lmn
z(oYl0&)(g=Kjp!f+Qh99D?hAbX=C`oz!UPkU;WpMsNWn#K@Zqc!jGw3IFLR6@tsz;
z`+@8%d8I0!B(;6l=4^QSd2Q(lkCK&I8t?!48T0E!Uf%Fo$NBV|^XFpyZ+k!gIqUcA
z<qS5`o<{S0dQ^mzPjYHboZ%Ilwm9jc?Z54reWji!dLs*7M_R|Eg_LXxxm{FUG`0AE
zk9I@vWRa}YS$#T{42!;JRy$n%&YY?DI=3=o($%xRHjYL2)gC?(d$w5CtKymRX0aXS
zjH{>Dxu+hud1$KUi_Z%zBR?#bOe?LbuV4y&l@xmC3tQ`&^_qqgeGDdUICZP)h~t$_
zQy)!udtiQz>d_@FAGe)2c0x@z^5wq2$NxXQ^tAnxX_#1HyGFwDmK}SH-}Kpfzm9ER
zd}7PFEz^RwE-xwV*i_21`RlB;C)h47xDuPXFzq@2r_SjiYu#Qgh!&Fn7Mm}bezKa;
z(SCaDiTTf0mS6pQXLIV3PrR4s&3!dThd(BEtG#wh%bl}JOK&GO-r?Kgp!KsvuYBJ1
z<3EmXaIi^u^O&o0+p0BhR<6G$TpKS_m1OxcPk-w1@aJ`fJ~ceuhfj08d;BV6n^o_x
z<C{(Xoqc?3$AOB=%_0AmvcCAd`o#Y^t^cx)-?`zZ`FCpqgF)Nc>w%lnrYvuNKgaDq
zr<mskkHn20g4JL2mQQ*duD;GkZC~A=7yFeyW>%DB7948Sx>{0tP%h)`@v}{nUh%zt
zUyyQQ3QM%?>v^IPH(#!K*XS?5WV2;u-($I^>73736!g{2Iu}^8WxD0xo$TB8Ufi&Z
z&0+nLxZOgmJz0^{RkrTqi92||fiKhCN}Bi2MY-Lqohh%h)R%1$?+dBx$vz~>YZWzV
zw_MVFrOlrGcdmD3oAo4r+;H5wwC}UI<w5Q<Z=F~47JC#Icv}UZc`nWsx#Ie9uA{3m
zMI_d}$iBrIf7ySf>|W+?90zW%+&^`S`R`NTmhrzgR-T*4Im=Jy@yu^2zP}FL30{(z
zXf{)RNwH}CyA-xFU31uW3#wf>w_1Geum3;Vl+KhV?rYE5_c+x5tkT0khS}i?*F;xe
z=(CR8vUHMxu$Sku6Gu<5DHLA#tF$51*-QG$Taoga%{otwb8n__^%!M4-|6&pck8)-
zt=Qmif<a$kcrWwkMZXsD{#<g`cKH|c75eW}SMU6wxP6wxyW+rq?E4<rtNQ9T%$<Ab
z7vGGoQ~mi1&o0Q@TXraZ+U4S&DcW{H-xaQUoZNEAY+1$(BZiRBd9$A-72RA~@u2bI
z-JtTds%PHbzIXA*)Qdk-m)jlYp6Mj`<E3#j<HZ|H-q(Gt6i$cPP2SIws<J;eY}@Qt
zr;|@^IZ(LasKJcL*$e!qepQ=O{5av#+}em%{k13k+AZ`hyf_iM+tb1<b!x-G-oqPK
zEh(G0(}+isvFewvOU^ul(`=`7lBMkCDX+R=)^pt_QBOzWkoa-`q+@0YGA81&g@w9e
zvkq=z{<*yBt&+IP`HYVom#<WZRJW$B+rd!2mV>Q-Hv2hkn~FcbRK?{|Vy?|QBsV{x
zEId)?Oz@ve2@kGptzDa^znxQN@u^}t>uH`(6a9+bXr3>qcsAwktX*6@THfJ?rna8`
zQ=cB6U#p~)ydYfp?$O7R5;FcPBc^eE3jbyxXk5-)_dNU10qJhe&o6x?zSPEl6Z(8~
zj||iCJ2xZReoB7p-#hv2Av^JJmG^~Qj9M2Tf0KG{(Zy6N(`8T3PPbvqbWXo?foZbx
zo@R!np6?Yx7P6Y&Ui5X=n!d#In>d}ayL^`!zFzygV%i$+s0&s<XG~e}{;9)K=9wuA
zSo!C2e3=trz#?RD>(b7pCC>yWoOL{PqcrThm4Dd<sf(9vgr2@`*`zv8c}lLxsjw?;
zuYGeZ%;W;+_F84n$<o{ZgHcD%!_llv#W+FFM`yEhL#EJOKaZ)0#WuA}Tk_7;rsw7<
zpU89R&9BQNA_Nj{+pzvuatriVUu`xmQ7>|9+~K>&FD@28=`6PQ>=)jW&}S1*H*@&R
z?OOfJv&tuW^Xe%6ers>`OJC0T^7n0Ca9l`cBTo^Rt@sO9P0PpWe7F3kOcG90V0Ck`
zyO@*utI%q8vtRY>qtA7nJ$A3%XVF%g8TjwLaI~V-Q-5m>dmEv2gNlgh^12muj6A=L
zi>G|t$a1)EzDdKhplex^c5Ph~dZ72U;GM%CH9x7}KK|;OqmP%5X8yM+&tKpAs~Tf=
zJc@Pt)J-Zo-Yq&(W${u+-%xIbQHNjX#d&_Omam=lYgIYtvOiPSCOGtYXwJN`b%x8X
zNwFFlV&=~sFMN>E3bA|l{L=fYYnR_x{ik@>k;LjFwTzpq51l>0W3t#uoonuii=F#a
zKRadW#ZKLQuwwt^SWY*_Jwdx>&E(fuGhI4s^*!^%-uZG%H}I*>6FAIgKW~HkucI$g
z7sj{LEnFb<>#6V+{bG&73G2Ud`x)HO|JyYG-^G{WJkdv1b6Z|iw|qDui~U63s+D>I
zhs)ngDPTVqe8unAobq-yC9R~{g;RFSjyh^xQo5PV;kv@(zxp4Zx!g>Rx!Y2#5%ok+
zNJ!;VlG=^ih78YZEiW?vaa>kzw*SXV0pqyyV%lfE2Nb6>$V-|=%ATJ7uH@Ea`N{|T
znZM>}ZaL7Qn3uuA`lLChaiXv)gOlIXPQ}-U$_^Y~Q~FwZ?(3@UDe+N8r!Q>S<URj4
z<7xeRyCXgQ$xPhY8w<D=MvI-kW+hSo)!p{s`J2B(mzy58VtMe_WzWi>I}z!N7w(Ba
zmcOH;(<CWujrffBH<o*!tM#*plF%_#kGa3;;87LM{{8c0^LlFd_s-fdBYbyeakS9>
z=D17eJ}=vK>B*7{Nq!0wJ^fN93D11;#j-DG{>3X*3rvb4R&V~Y^MtVarz3(Kic^Ip
z<gT4v$K_KW5widPf;_!Xf@d9GO7|Uou(Hc|&HXn4hb7M+6fjCDb>2ODPfvu4(;Dt6
z(;E*3pZ?U`>J<LB%J%g|Q(gn_B$XS@N9XtTEnaW(obUXC%N6UQ6eVnv4LRK2D<^%)
z6O$8RU7h|%aLv4guT=+MnI2yJh2cVWhYffCrQq4iJG>ko=UR0*-d3t$*ReQpKt^R7
zk8_-DLER(SZRf0#gO68U2^K!rd`@S}ljw|3UOa-kXDu!d$lF&T#TMPQZCCk?yYFj%
zpM9D7E^M3o?cR+>eP%qn-u?*rE^7D4?yjlAB8`ArW|7GsYIZ0u*FM_xk+0Dq*j}yD
zUd_GStio{K%H7Kqi;mux@Q?Fdc5Hpm|KID^^>5ZmUtGBF;0tE<DOH<~GA*9-has~1
zi@tpK#jXF!&7Q<;;uf8D#q9CRr;%<>GpaUTerdKV`l9-_Q%S33ZiW4Pm}4q^M!T&x
zzJs}~X7%F7U!J9#dw*VPZG3ru+xkoK_nDiRcIgVA{PFb2(-0-onY)Ti=j}L@=4L6D
zyxvEwJJjQ_@QuT(yRGw6_v=}I753Ht;gWWlSM2eZyNqWJZLQSNbT^$b@x@WAh@1A0
z8PA+}pcy}<c;SI;orBG43GX*D<ehM_j9)uVD>ym%rqeg?pZ_#|=uckTlh*Zf<Lgh&
zCK2KPweHW?n|dt4^Xf*QN}b8_r{hoGKNyg{u-5$7Y_FH{Tt};K?u_3t_e_1f#yvN_
z7x4m?icSVKmy3VqFwVPrO7wDZ^3vAdJ9C~z8OD2w%UpKwIqdzi?EUG`EeitQt<X3d
z|7d5d@%J|sf@%ymq}B*;i49PBwc?W14EJAo8+$m8%}Dt#Q^@e3o9Ew)dw-n%)iECP
z+kKM7vAU!F>+VZ$_GEaxnm_;0ru8A0Pps+S?P@l7ac07sKW*aO;nHT(s}rY*8H7B&
z8@)9&IBNYvK~CdjPbsx&_rB(;MObkz6MQ&>bC=0vZvM@zk$P#L54cIL?df0Ut;}kW
zkl+}$O8Ml)61%^*lNA4|PAi#N;-`0J>FMN;M>1CKHfxj7^`G{}@wtoSruM>?<B>mH
zY7M5;ygRN|WALhuLw?$WmpdJ}RC;!;zVFDjbk5QV0zFO!d^=4~9aA{=VA{-Ul7}3A
zTkx3piiK^iw48o6eYJ<dwC?Z4MRM#vjs%FfvnK6l&*$H-AGT?Q?t`s1+b4J~HlN7l
z*_JS=O?CATmCq-wxC3qoZx8TiVQW{)y&5L$p~BY2sb-$R<KVE7<?;Rr+pkF_r>+xs
zXm{+{{r}#Itpexo<&;X5P7AQf&Tk4`xGrbegUI_)-76+ImGs(uuPHzMZqg;Igog(N
z86KE_SuVZVcEzTQs}A`MFRRL~1Z{8J718B+>eZruLavPO4?mf-=4Q2E{0(O2n~T_M
zqEALSw{Emqam>hB?>@ufe7k=C$+yKSS7!3HF(iA6+*-A4o#|iW^-^y6o&k>QIs?={
ze}C1&Jvk=BcH!5SYllyHhga^}_j>B4X7g16Q)KSQm5a*SGBI}vglUP(ykDekmCJhQ
zvdH`FYdi8E3aZXiwpq0A&TqNmf3JgWdiK0{@}%s{qrNAc`m($FbX%HO!gBgd8>%8*
zThy{l7M*nU6kN9D;;kFLX1SW0j}EO$n{&jVzq#4<?3;v%L7pB54o+#16nlQ=J&#xY
zj4OL5e>nVS^QLVJug&7xU>|KdPvMNgHdg09Cm%6f3}jmt<8bP>TEK+7--kJM>bF&%
zdb#t|jwz?b<i2h)WA(MOKk=;LMN4~8Q)hZbme{S2>#wQG<jOgV`ZP>nC<<OXzfEn6
z@weU`+3J%7t~o`x3mrIq!{k$nz4NbXQL75xN2(``gUtP>mN7ql@@M6-X_iaYEqr%)
zamM7x2^LZJ60Fy*+p=id;t!i&Yy3_O2;RQ;I&<)L@u11)ST^Upeb<%n>EK(}rF+-w
z=-!r>d9Y<`Z%nB|`;p9yg^FyK=1kA5*>~6=z{=UXpqZ7|=$1*@CDu{_^^?rjD;rr8
zJf?6@Xx7^<{VLHra`nRkan55uwfwVJaQN?L{L*OB(|zIC!Ta8RTX^yuJ&y2X9kjgF
zJK;vevxfBMqh9Bt7c}IB{QGTvl6CDdqg{La_Wfcz^DX=Pqlm_xn-@0SmY%hLmWhSd
zxj(kIeRVl%7QSbTxqp#4<nW}%hZBFEGhFWcXXj(S^!U!&Sqxl`-&*#cUFdFYoHAv>
zx7HjR_DzOa-)iQhZ?J0F{h9qV<IKq`?p3a{iw@;0zjByupWEHFrDll|dp2!)?%DF)
z;i|>0iR&1RlD;*UtnKD)+<)M&doim~LcNC6;~5=~cyeUENBCP_`JcGXXW{&ox?db$
zgiQsmCi-O9KAyE)N$AZ-6-^hhxS9a*1=i<ImAGU}xHI*YC=2R;bxqh+5K|#IEC1Wn
zx4v_|B)0u!Xs<6{EXT}oxoeJI`m>u#UkU>MezVSwb-gBV+x^u1+Y8m1<}n#rN=nST
zY<Kc#o{7?uYcFd2(l;$>)m{?bs&-VI%h#&ppUKMDF#ZKWOA~lv*GEN7To>{#E#ch3
z0-dRTwg2A~#0N#c-uCC6i```lcHze<UHU%hZ)Yv`Z{}{RbvbG+_e@Y!$Umv6nSFl8
zZdLnfmeGmRSx)>8ozhaabKyIkO%Fd*vH$CuWOQ=1InR07=v7MuzCF60mVal4?=j)W
zAGJHRl^<_5&pUr_o}}lQb2rLgq@2Ey-n7TOztrHwC8u{c6|4`Ph?)3pu~V5r;!een
zm5O{)I(K6}yFHw1R^Tk8cJ~lhA@j3X=N;;Io6gtrKAjV@>fGT7v!|x+A7b}NZ#{qV
zv!~E^=R1>&Ee;Dk^G!a`5F4I#I$)hf{V&cxva)-RL?6C)Bdo*r)4}~`o2PqEmp^5G
z$lB?N(pg^4e)D%MI|Po+VKLV!HJ*8Vf#_-(nbqsBCAT?ROO?Fg{4=rt_u73Q?5^Jx
zd0Te=kB0l!JQ?lD%!j_sf6&0f{zP;oi29OQ=&<nE(piuG-f3N~_Q>Xy`jQnX{EjDN
z42q}3od5TRH_9>Id)M<LS10~>;$d^}E>{!xI}u;eZ%qpiH6PoYzc2QX;dz^Yo2Pgt
zEpN0GR>-*RyJI?g+Kl~-#%X04XFAO`Ry!^**eq~MT1+j!y|~>((7rXoroQ$r^S3_l
zyQ(`wlI)phOuCczDxP7ou&v<1GKKki4O1DizqV|UJy0!w_L#Wh(?b(3xz{Po$^7fT
z&nRHYikBRrR~AlkG5<7&>HRh9^Y=Wxs?RR?qtrj^%I>c+Ek5T?THc)Y|5N!j*H6CT
z!WHM*GE*u{G7qZfFMKJG=;y=NmDhA8Zs}2$ShIZgck?8YoaZx^n_aJ27U3}I_qGm>
zpoOd2eUj}{rmNigDn7aMic0>A?fqBRHv8>u{Ilol`^2gKxx&q?dv<*diHr;jD|6G2
zdM`8ofv=Wr^z=`^LSC7?dUZ-oM}5_syt%ULygsa3dwuS&jlZS`-*_<H{zBgTlFHxj
z?yCHL`$A~zl&xvgkBh$JdHB+B>7I>WzRoJO@U4nxQRiIvJm|8!QKC@d6U`Q1)!oV4
z`gt!m|KPjkX<WM@VB+a((i2j*osHL-axz6~g}awps`CbIjZNYI5BVx3F1DRi?|5XD
zO<w<s4PvtY+pc#ye!QTw{luc|eH(ol|3|s4o{_uSCGVWB!O0-wIb84V{hkuJZSI5S
zhybn63&Ph+o2U6&n%8dHy*iI+Z}9EFdp4!U&sBch|E;FB<=B$!|0VuSGhf?XS@73H
zZDp_Q0-veNS9Dp4{&C)^F#Yn**DQSdXGuR5e$X7==VwybBbAlswY+c9<O!QX&9e>m
z8)R@i;XT`H@<sobbAG7UjAfGM$&c==+FTZUH_v*DmLZqOPd=a9j0H>+PCew$65f)K
z%UsemMP{l+MB|+ro@X0AbL{YnS=(<buRlv&+j_xo)u+5Zi<yc|>@6FP^?hnLv(edB
za-s5@u+G+qY^lBz&pQspws5=tl)EAR`<Tat+sjV>WC+@E^VOa^&6Ag$6*(kaI5pHl
zf?bbAEXd-4ztprT&EH==Ta_2!<M}b-y6DpSDG6zZo?R3Ax2Wfe40m?^%%!bGe9^9q
zH(3khPkhzR|EBtLwfCe?`_&IH1^4wG{Bcy}>rJhq9VXKp;!Bg~TClYIERqXkn)j=6
zCigvA`L+xC<_D*If4k^{n^A&!R`JQ%!W`1?-fj8Yd%mvn^JZytyZOH7>^zuX2J7_v
zTXy6thh(m?dffNhm))3qFRZXwW3&GH{igV%2ZClf8Ht>i_>t{bBJy(mPKVmRyrry{
zitk5CrNuYAv$;I!Qr>F)zPtLdlTL3~urHy~aoIWXp7sPAmd6k0emqb*WkZYf>`QSo
zWON)?B;Rd1aJ2E4SAE1Pt*?D1Y`&`lGLjd^&&?31J~7*!EkeNfE<cN*a)+ej8IB}3
z0pq3_h7UNCo-I~L<Kank(VSg&SMLzd&gYMF%!(WW7#H`mG^%pwI3{p1c?(1|9r*Qx
zL&sLY$cM>0{Io?B{|o(u@Ndn#U!`4oAy(9<^JU|IooNDHP8z$qJJL4ZG=91MVdjjD
zoBkv$G+=jOuCsTZD6o(DkJake3H@u|Z?=-=efoFPtyRpM%LO9xrRM$P<d6Pte7o?5
z+oik!&cdkeZioArIZN2jKI=B*oOpYMH0QxLO`X^Dzv#!z(%rh%<No%b)sF*KuresH
za2;Vhs3OWJ#?i5amx1v-Q-Yk%>aV$c_NN;Q*x$ToDwr+B6mf9#&7`8)+SLisI-2eW
zCL~la8_dYO_Uxpsz-__sPd7@9EQS30RE{roDbf<1FXeAo5mejvTTW_+^i{*ZrT(?s
zy2=;F&d^=ZH20;!Ix(SXof~|5{vRznyS25=KSE?P>;LE4R@v+h9~RH~cf-`yw%FLo
z`1H3Qjy55Q%#sruo7}}_seEAlw0HN%-ox_j7mrqD?|qfXyfpuAdWp$(P5zZf!(XSr
zb=^>-!n{7f-03by>E+iS%yyhQzU)GM;|#~kNyl&R+m|`7F8|rrbtR0Y1>fFp`zP!b
z_&-L&b)m?~gMwZk_h=pMikWv%>f#}ms(YQ8E(<o^IatHGd0T$gM4zbJ=5KGLrb#vw
z>X*LK?L4`Juc7bCF(WV8m6vaN#P157cyLC_yoE+Cue`TydNX&@ybf-+pbY1Qyw%Hi
zJ+&?+A9I=Z%+kJPiGc3>bs2_^A7}iWm>c}!X~o_4kg8s(<+EA%9v@?s-LQ`Bs1rxS
zu}3-E4jA~WvQ0ABWLLE9yNW_l`}%$BnSGU4{=fceO=Qy{{ebx09P>o0&Rvm}v0eWD
z>xM(SLu+1yKfn3C=Jwgm`{&KEziR%W+HT?O);zvo^T(xkszvXrE@%+pQl7fmSBUlU
z^(!AN-fyUIEP8h^WCwd$Oy+C-2RG-av%fn1cZLaX{Pt;6<o_SLcr*UPIib|91z&Bd
zkBO?)uAAsO_f0_EdifJ<R#n#*_NB}So0c!E9X#!F=uI)7=lh#wx@R6(vFqc+AEKIe
zmZtM&N%?aeURC{DP9jYF!2Nu^KlTCM>>S)%h1*poGBC{i$;J@i&CDXgz`?-5;8<D~
z#sCFZ7#JAj7!(-d<9!{S{evU)lk@Y^GSl_-;)}}@)6-Ln^s<Wc^Fl*-8Q8U^N~OqX
z2B(;52B(x(a5FHnd}U-{U=d+pU`S37O-@fpO8DUG6ZU~WtRtX-X@Uf^x_~s}M<oTe
zkLwKtSY@1@nilv>=#zBFX<pVMsHit<&ivKWBLXH&UKJiOD`Luw@JTbLp9)x!pOBE4
zk(-v3m6I}YR?gcwfm7zs&dPoB=+&zyk6tW#{V40*lI*0!C#zn+NK0DxIx8=cO{CnY
zpx7{pfdS;>ybFJ2+ZY%aK>ot+<BiM=41x>_3{a1jXXX``<mc&S6~jF#s~Mc~K?B2+
zwZXUZ<`@XnwyiG+RBqgIq5IR3uCTzHTKv9Irh!XWt=h%Mb3}LB%3QPQy#MEIY;1MO
znmFNu<zwS?J&~Xc=Wj~_9C&LcmGWk}YOS;|)lL!--8J=8#-+(U8b59uvL+TB{xD6%
zam%;)$%USg#p2qT0s8AAn9WudWNPgdS-B<Ejq3>C&ajhATl_vunNxqxe81WA`zH=F
zb9&uyRF395#4<Tb`Sl#lJ)#f97ul`$__65F?d?9&wT0?k1s$DAo4(to{Fpy!rRv6S
zzfU%P{(JT2zm9eK%P$!=Hq3U_p7bX(rQ=rb*XdF7!((OF7)S~=6?vTe_O#C9+J383
z{(9!_8=r;D-0}8HY10|8#okkXIrTEC20XF0`*<^Q&hxOB^S0_vQD3zq&3cOQDrOhU
zy?YbBSMc52_v|1;gt62_^W&;YfA8Em)+@>#=~tO}+tmK_ylpf0%s-{L;-rgeY;2zK
zr9$UPPM@^Z6u#`8=*jbV0q@hbSHJUp>|S)@W#*~MRa>uq{*_@G|8MP54&8+NJ0ENQ
zY&1N->;t3sWasaDo&T%PzE|HPw@Gx<M6TH{jLsFTo?#Fw5w*4><^3C$x8Gx;{N0W$
zeS7(3$ChQLUv4`Z%zFL%dXeJyVj0otsn;3w-hbGiT9(>=aOq<P!-BW=OrSKh>H^P+
zr;H2?pp=3=4S`aZ4AxwOBRQE3y*yJ{(E=|e;+Pp2co2D}*cg^wj_=h@!I523C!O!Q
zZ6I*${I}qaIWh%rn)CUhId)eY=+0hRsA<dUdTm*j$myvmW(!%ka`Rf>ZEQ__x$mgl
zHvcW>tV>d!_IKr9I+LHa@285A*V2iLT!MPfO;|ENTP^cKcarq$ukCd**Jl-qu2%WA
zq~=ms&prPbnft36wZz))F`u55trdIBjpvcj<RzzhLLQZ@+VJm%(X)3;98Ze)ndjI|
zEjj1Hx#p@|<EqbQpSSt)%<X#f=#$p@PyE*(f1Lg1QOF^#sH|nzuRDEM6Cvwmpcu09
zddb@f-Zsk@JO5$0l_EZ8%X2=v)h8F3)}6ogyU;xKjONR=6J>lSJ?MTfJ2l0%YLe)$
zdB5tNswOdczV^9gs{O@WUHf$xcgz8g-#lq^E;%**`SK%5?Zrx#Tk03CZ*IE$S4oX&
ztx3g<_1ep|r1qXZe);pqP`+*c2fv*bIBJ+B_b#sPU*5a<@pA72KYzcTSAE^)ujB6g
z_}T5cS^MnY*5xczRkLy3yJ}vc8{4Eui_+P2&rD*h=@z(a;OFd;R$KCoOR_Oeruf6u
zbq-yd>JQb-=3r8dyL3Rr#c7kJwxPM80pqF@IvUyMau4U#F(;e<-J9wic!l9&sOpk1
z1BPc&%x9AuEN3tL&kxEL)^Y6-XBin7K#2j7Es&E5C|ihQ%@&v`W(ej8&zeQ>9C4|M
zfdNzkKx>ht#NyPT)Z&8tyyDd0lEjkIVptA&wJrcx4mlfpH(%O)QvJ67U5;^PjC(eE
zyfSXDUa(@Kt$NoLKF|6)+z)T;icS{RR=-)!D8}s}_BPmuHSAiDi{+GQUMZ)~U5q%j
zNT%xi?s|drANDa{kMk_Au3DB|(tTL>k3i(Tf1%-Ec)ez4$@7Ea-ShS8{$9QG>96Qx
z`QH2SdkTJT3al#l{q51IQ%AX%dg-N4^O`TY-~8RW5BvX~*|Gmm!pBL4)i2LIN_Wdj
zNqI8qqw1f(YwKI(?TfP2|6bnQ-(w$Rq!(YtZ{7TS_J*F)r2%VoLIh7QJ@WLy|9Ag3
zTz$W07kmGmySmM~_BS$j^oD*vIWzcv!7KOqLOZ&}&-`37b^Wuh*2vTIjwdc<xg@{s
z?(_AGrz<}tZMbZ5=kL1DRk!c{XSl%>aPZVw(M_MzjMkLMObh;hY^Cf@c2mjjP3N>8
zC>S|%Jl}Bh|I+90zL(T!ix!8O#jAQmEC@{0?>E2ezJXOI?x0Efsu<NriN};e6ZdEH
zlx4m9oO@``zs$3_Z%@pby_L)OYHQBQlD!${A8a$#d+@eo?yu^GmzL*eH7}PvT)EpY
z<4eM9wu!;AWq<ZPYhbw<zyC*q#oT4Te+2B!oIfGv;<mGwoHjNr$#j~K$slNvDDbrA
zzQmh7VacIlX`51o+l?jLAJ19$?f$>3H?wx^mR2mRTCT`>Yf8g2ZTk*WzEwd+xfVQ|
zkE%pywmLGF7W(bpH8*gF<n9jRn3W+C=^OSL{y4wxokNUqZuYfPsi(sP%l`#6=}Ow<
z@m{lX`~P;;@1po@mHa8b0efe5=^l>VQ{c$fvj3B!@ss5YFX9eYZcp5?e^1k6|0(ki
zyxqoIUYq;jcyHZ$X7<S)m+!va!LIN0r}Zd@>CKsy2A10_qZwxv-E=y4=CP(!Xy6f!
zB$*#q3TEqW-}>sEIZxdz$x65Xch#1zPqTO**7!zXy+!TPQ(r72OqZoibr)7<^pLix
zXYF}ap4B4oeZ?}B#A9b>FL)UuzIp1WP#ej|LXtk8Q{yVuFa+AxJl-~I(u%e@8oD!@
z&R$yJ@A2Z!tRkg^{RtcU!w>fClDB#o8*g~}D(h~G;FD87PMEBevSm%<>jVF#N<Y1k
zx3u41#`k@0y89FpEzRv)pPAVU6!Tvy{&%J9`!?mBhdh1N=C9QF*}|c||5k>Jrsnq6
z=L;2Wn-(c9S#u`KO|azXt}?%(Ag`jb?Z5KPRZFkVjalTtvO6RCzfXVuz6w@<_o$_w
zbHsVBGo-m>^K=G9?Okx}M~h>{+~{AQPA{z%*Pqf|oVGCQoc@%2uV??_^J+Ify8U~L
zNRn~2f1=`(OvgVrO6z|;xK<^=SEzkHOzh7o9hT#ww|kgRX*BLgHoWueV#4RpC%;y#
zcG60#{Qsz6qQS|yKik>0zxN(}`gCb(N5-2+CHLmEKUf*05&rq+ofw<LZmk~YGQS_#
zAh@>ezto#OjhkI!?KwVuNRhCSe79Tek9EiAZ3itbG`#aXoGE+q46ktT*=Z{_wTg64
ze3~t$>T_YuLM!3*tjl{eBqqh2@3}rpVBJptK8~Xa#~;QCniR%<`mKKZ&kunm{~~W&
z+Hqz19=Q8=@=Wc+AJ3*QKPW2R^>kI`VXdi?3#5I1C2V)h_4wN{Y2$6STa8=heSX{N
zp6*dBnXf2fkSqL%p-p>ru#Um=_RqR=dfzR$w$Ng(+|HJFCLG*<?q9g@bHi)7El)4Z
zWZTpsY^l}qROieB@nh3XA0|j``eeXxTkqD@PiI?iJm%1tyiue1SYPyV!TH5C4!$Wi
zI$?8yxz-tdP<Sl$U>|S!b5DyyXU;7yw@{Oqb98-vZRXvdv)U&A*c<5Z<leEMuDyj3
zf1Z8$WjL+xM))7Iv@=F06B8JWj1HW6^f$VEzXwaR=%oyuFsH!6f;9(@DX~18+#qzj
zX3^iSZ)g0ORjrG7gm<6iygE}_seEI!OUAjBC+`-SKVB|!!Ddd^DRq^QnTnh0vL7oK
zE-+lA6|<3RS>zhEiIG;uQu@XMm*1Ira36VM*4MWG<EK!U71pN9_o=JpuM^`=Ti9g2
z@bM(i$(KSocfMV$ykqV)M)$`PUgs!<Tg>}ez4MTwNmBRX=Ov%RAB1s;mwkE1qRqZ=
zL-DMVXgQ^%;Kx}TUTi)2caw#6?#+Vf&iS4_6Rv%q>u&nJz4%5Tqs+$LCoS&G;5g}>
zyvST)>R}(T89z7tTI<{6qV(!cfSk`9b(_DssZP7v*?Xdx_4e8Rn0e2mkKgGWhu*|@
z2gPia`FHDt%k5kI>2+KFE!9_#A|~{0`cQT0(5op7=E7@3En@2IgdO@;^=a~*xxT8U
zXu8pKM$yZsYK3&m<W|0DUc`Czs~=C-gLeDxO7DvH={p}w5MCwWQ<gm=qxgl3;DX5!
z7jCSrQtuSoD{#uxQc!aK!MQ*43v!pYvdsDvC@z%YBz5wp=SCNujr}oaud3Q~eVpC3
zG&-)QA>*v-F1;7RcJp<gugeM(>)sZ#Tsm)W>W_mA=d*THuVy=)@zuIMW!K59;=m}U
zmj|q7dzStXKCwcjr@`P~T9EAD6_+e7CR$IsoE-SeV@iEMd}-8P5AF6q$JA9C5fweQ
z3EL{q8m&JZSCDY(cFf}wE9YDGS2e5uV%EL(#PeNPvxb}Ps;-(dk>@U5$(@>7Dwq&F
zDUN5}ey$fk_mvAgne3kTf9aywV=wN7Xc&e>MeSSfC)+m7U2fIN5D~X8iIW^Rw_Yl}
ze6HC`rQmIFQ{>Ll{HOL}(?6GoYnT*o=3HpVbJit!k-`Qa+1_K8;?WcB(z1MdyNhnv
zEbdQSylB$`@7&WG5q^Fz7JT^gWy8iZb9$POZJ4rc=aYh2nlmQf_C9^Od;8<7m*QoP
zd#+rr(=cI1<FzxqN2V<~wrTye>308<45u*mH6O^+k?o!QV8hL_Z)*2yJZsbbo|F2s
z@aZZ(m)_W@q(jn%LGo=8l~SUUPRvbza4YVe_9x$s8WH#UjygWvDa|2Uu-yM{yBL?e
z3Ht_?c9zMZ6RghgY~T7c&ty@PwR42b>60fpSVGR6Qal}I*?;@|l$L8Hb#<~+l?`p0
zgEVHd_H3PW>m<*Pce~D)p7s~t{Eg>QNk?wVvFF~~r@cQSQtHIvF|$KAg~>-~<&Pa6
zSN&359)6gnykqs=yCtvB*5#K<n5<IxV$OL<T#ci{THnG>Uy4z}@^>Cjv&RSB9Zk%c
zU%8Sd3S5d~`QJ1lLV!bi^T&!UDoxx6l3mROd*$*c@EqL#a25BG+1`rBe%xYW-&J$-
zfP2Xjer=v5jtLJn)4py=(R-CH6&<McOnb{hw#_>-v@<pzpO{u#<(*V3r@?X2?~A{E
zgXvXuMvEDtvrbR*opRUPd)mX=@((Yf9TYmnmfyX~@ON3#G>+>NPEF;z*>Uik^qitf
z4~-dysyxS1S(+XH-t_67=IEun;hFfd>pJ0jw<8vbobd6|vR!yIN!KB6p%2^R*gMzP
zIGvld?bEbr-IG)w>}}U&Uc_<uZmo|%<?;nWToIm^q@VS$rFrdKzn%Aa>g<XY931l&
zef%7I{cX}iFPk8@Eu8Nh5C8nRpR*<4yaZEaz!uTDIi8#Mq=^2h$vDE-B&%5UG5u1+
zN8ugkRvXB4EDBJ&se6R;REi3BxU$o7$*?U`oqw5coFiY9uH)^yX8xo`8J@^f-l;C9
z4_F?%-oI8~+n~@)-&l^fb5qT=KTaD%HXYCjU7z|{R5&u!D|Ja&!6kp&nx85sgM9p(
z_S-1w=dx8>KPcLx&c=9XkC#&U_kGFE9o0eK=El`mJ)f%I(UxGAylLA#&a=}tzbMc%
zUb^T&evr(X&C~sQXGkwoR6R6tm7Ue1(9)yLTNXUKXrj%e(fUo)-md9~jnEmhbVDn<
zSz)h!MBc7!w+cv!j*v^+V$*Z~R!yOrqmzxPmDE$#W{p|OG6#+nmFUQs3FdREP22l8
z;#(0{%C~ukReHQ8p3PhSf1C5Y!!`WXRyQQv0$2S@RNfHI7A9*I#=Gv4$Va=Zs9lQx
zkNODbde?t#414tAY2+4mIl<OVnff=vIG^^osXDVb@ZT2vxy67XXhCw-?86Ptla{QH
z7Pu`?U9ET~Z|Ct{-E;0+_ju@Z*zR34<3M7;eZiV(l9i&{ytt>n+%t7?9Mj|?(c85L
z?dN{A_PrM2A78R@w*ARW6Ti9VwtZeC&FZ_e^X|rkVhLx>JD0Z=|KZmB^7ob6pZC{4
zf8M{Z_<6WP-)?6)rmm$vtB=gvka;M7<HhnB5=!sIuFYPUe(_Dn`S4Gv+<T=%wKvwE
zTH)$=q=w1POPAIAnEr*sAv<rcvi!Pj)f~^;DG%mNIU8+tuk&k~+=tC(tsj)jghDL!
zs}A}}DKy)8^s6|(So*PIZRhV}OTJgUdAZF;+}85!y3ISazVCL~=Ko>A`Ti=ey4&;G
ze4FMf+v>I7by#p%{&(rN-}SF|epP;7rKx_cHH6vdiuCj}l`SWhF}>wIcqG=vZvNF5
zpP!iu-#sx;D7ty?_8Mv4?;X$Vg%7`)8vBNGn!w}FacS4i#k#twCYc*lI&S;Nt0%&F
zAYrD3RP)m0Md>Bo*SUWtiCvyGRj9i0B<l;ymn(|rnJ&I<v8UuX6XVRS1+79W`76E)
zIx8N$n^1gtGk5Q{4=m@jXQUK|pSAp~#^uhn`roFf*ZkS|7k521Vlvt%F+XJSf}qPw
zi?+pRE;Lh4?Oajr)zTQUyHk)=;QLp(CC9E$_NmBwd2jF0+uz^o-QArsWqSVI=$)?h
z26NKYzjVg&zT0LRC(CF%_geOg6<@rr?BCGjm_2co{>ht7_J4zKTNi#aUH@iPWVp=n
zL!R6^suc@&F3L^&YP(iBNPn}V%~`+8KR>tsyD43|d})B#q-dk|U<aG}Yh2Y_`i71w
z59Z9<X`fR$lhwZ1kL&PKBi+T1?yvWJCBD&QnT<|Ech&mqZhhKMeopW;P|EwDld*_%
zr+TbE^D39~$((x_XL$UNmuxum{}uzwmi2+sZ}#$K$BRXu64-t5XsB?hBWvMAFZ+dD
zcPu#{a#!v-_L(<1)nHxF?-nn$&O<kAuDxID)~OUeXTE!xQpev{r@OVkPoI`A_}K3m
zx6ZxY>tCA(9a6KL7*r$g={Tu*g_70^HLF=m7D}y>o;&H>0oM1E%vU&yNL<{~th;0D
z#IlJ7iH1!=x9_X|UD};2SD|)+XQSf1#r82}Jm0Sxa@1(gxS{YqH*IRqqzn7mIg^}P
z9M(_W@;73-w!rR(wrxcP1xMNM@7yYUfWNZXUPo!+smY5EPM)V8JIPNmZjQ>8St$pO
zG9A+H;{7C;eaX0D&#P@^huj(uDdbMPqA0($v03-|a^IpyCw^b<{l7@d`;$>nl-e>`
zXU=t5)vhM@MBlQUR|rnJzi0PmNu7_AX2$ATmAA&I9j)OJTv@&6*|vS3=U)6}_ktnc
z<;&4os~bvZuAk?8ek$)qo@S91clPI5ua{QfbIP<@@yF|3bxVckE0z|Yqr!(?hdLaU
zn8me!DWiYejd|-Iu=^G&oJ=S$>PZx~{&q8V4$A|M+iBt_dklHneyq89ewX{YITkau
zw>>)kwCJ|r!?{772Gzp1(^u}vc+6Pp_9CJyDq!LIb8EN%TCV%}etzx8go``=-adMJ
z|7Ttv>pv2Yj)hnqz3#rxN=QL)Qb5^f?+F(<zr9$_(!-Qevmo!w^c4=BN5hXEd6O1(
zBK_-;??IPm*4|j#dGh_cRi8Kco^ecg6=QMODqVEl9R35jxd*<+u{&6^%&n6Crk?d?
z!>ZP|j|E(hb49FRWUO3m(9Sd?gdyZj%^%B#)yra!T|0Z^yLgJy{e$JRUd;?=nd6wW
zWa*TPAKmtEZau7E8lvOjb&KO9&jYhZ;l^u!z7ds)h_bU=u4}Pt&+$f!{ShZlPycJD
zG5H2}NWy%%iA?dgcv-tYNzU0=V1BC1jC0D2Z|~OZ;<t`iQKZK?A)~!&$wyY!BUhZV
zpQtV~taS+WX<zu%^X$r8UO9QGbFY=3>&{)p=E%2Q>a$v9c#&!Ju2lw6RoCnyB4f6_
zlgRPSdb%_E+gh6&)vUg2rDh)I==+^G`^b?a?>6jDvbXU6&Y}N(Tl=wIzMUH<=0?91
ze5l3yzw+T?1GUPpXJo_vukp=`O=)?5*|z+D^B1L%EVe&0uGmKFznF7tuebB1hv!v4
z^*=o4;de>#NZH)wDs|=i-*^3&NIIkZy!LFi=eK3gN<C#J?V9pUATn@Ph<U_3sXU)G
zKi>$~%QYUK@!GYT@ATX$CYHer3Y%0C^|A}NxU~`wUGw+Yu<nI#QfAyX*XvW84;L)W
zxX&Yg;Y<3t*!bevrimYaJTGU|S#nG}-lR1#P@mh<DsA?oTBDc$Hp~w0EwK2%?ofHE
z>WU(d_yAj_$Jc*&x7y#mr0#LLVN2GExQ^S0RF0i6)4IQJ>N|a1{kS~_v6=@T>P%gw
z)ci=d&TDFc@_UZDTRjg1-CP8hJlM4K<rJG>^*)&`C-(fi{^sl}r)zF!)qJ}B_y5Yb
zK9biYtA4Tawdh_xIcDcs=U%QduPRym?2S8*=l*GDH#)A6*qQQVMpA7r&&wZ$lgn-e
zctsQ!R2(?zE9L)U`5C5D85Q*#jW6y!Ix%zAmLIRUb0jp(st+Gq@XGgj<_l*&xsI|u
zPEj?_jy_#H^TCFIEeW|+Qu5b%RTuKi+veG9`eWV0jGFhcO<u25jnij8)q2a&F|}Go
zWd4#f-CsWS@E_i<`-$<wk&HLy7XL!CAMTOzuUaoBKWX3M;!K<3BRMwP=Dc6<`PA1s
z&y)^x0Xb`zhNcHGlYgZ7vpMy-CCql3lebj<OS$L_$2D>_CnikztaIsQ;L&Rx`!1}p
z=xX0FE67UM_voY8H}@n`^V@7J7acxv?Dq46ZvU4&w#jL!XMLWq!Fg)`oLBAc3Jgn~
zzgTgu=v{tXMw$Dg-}%|UPncxi%6Kj7$mLP**(X_<Rd9>-xXB;(DgAeOEp)iLV_LO~
z`AS)Z;~jUU`+h&}uEO(ve{FAv=ZnjevyL8GlD{;8<;xk{6@p>Sdn*gR^SM3EJj?Wb
zg_^EiKZ`4~a;v<^G5eat**Aq&am^Q#_pFki#=z)gAh`PDou3NovzLW_HGOyYmwIee
z)TNueeHzlPcOxg;zY;tm);#5a;^d5n>gTK%bp4y-aOcROE{AM4o9rXUOMRHGe4oFe
zP<+cy4KpEur4zN!=~vvnXm!tHe!@qG7u>F^@~TZaG#3l8H%6<>esXlTF{`{=ocN|h
z$rrP#r<|#H+A^b7>{ZqFIJNDG)|=8L&)Xian;|JxCdgrIzDMlU=Uf$wN@fj?uABZw
z*7x&*XI;O($iFTB<j;-Ak8FCxo%F9$S}X1G<JaBu|CeRHET3Pn_R;F1**zOnyO#FW
z<sZIl86tj(W%ktFS<jw%#c`W7Z4cVqk-mFg{_gzyKQ=rNwUjk+6;^4tR{VP1&LZ$>
zlI3jopUM|)joRP+`E#oB8rQ9<VZ0Z3-%seif0Waxc~8%l(-C1QORiizvqkX5!Y`M~
zOt?bjQ=LC#9W;<U$FbL{=-H<2r*+sxZ=C$N?BU-1HEYwBytq;Nd*c3Eiv)Dve)|6N
zN9}Z5k%c=O7QUGp`(lp4xukz5MOs>pD<5X~cUN%v<u~8=3I6hS6*wX|>q5hkZ(liP
z+>{OzeA;m2`iDho2Kt`*1#-WcZ!g|9UqtIf!Fl&fcU!0Nb{d}&+n{@0NK<fqx<$;7
z+IbB#ew<mgPTbkxDR1-7ea+%eTG@B>{bD=0YvH|(%Z|Fgzh`{yK+u=Ee|<8y95R$r
z8HCJ}XK%jgwJ&Dw#&waZ`?C!@Jt}riunjYq^Y-n*%j?9yo(`5id^gR-V8=q&O*wmv
zG@d!V&Sc1*cfq;yu<#DO>ingTt8dR(_Hf^Wg~tLz${rV_=^FR%+L977^)zF~hs|nT
zl`K4G4{YJmmSolxGrFkKSMWii@{8;Wl@*qy#j{txVXx|vt6zQf6hrYwm!4G}6?-gI
z4lRE%$3>y}X^%*9)sheHcY00Bzn?vBR6C!Cb#doA+gqC|cZ8ffak82vdk^>YboZ6J
zvfCd2%45E6GVxr?zSmxBS`CcZR(Jf^nUGodueHIe{Dq;kZhieDrgyd}QS+J=Z-@vf
za6XUox||vB{Lj9ASFjOx#|Jhsq50t(mSz7-jeYyu+)AsjD`i^A@5~8H%v%l~ESUA=
zSWKB-^qRJPQ-!qu8y>1Q?EkUP`=3eQk2ys}>ubuK?6f?JnpUZAyr}Q<XX~H-(^vj1
z)a=r$-qv_wdO(Tv1n-DK=?|B?+zxd8nQN|4_u0dJ@1MZq5wBYN?Fy|z`1cB3;uXx;
zX%TGFuy1#KaA?cq*R=`Lt{SgllF#qH5p-kb1^$w<%C~9{j6>q@%hm;py*<DX{BPIV
zFDC<1t6c8iIk0bu*WsAB0GA0$^*)9L!fc#o8Ce~5X<>%@vwxU!U6{9O`gf-KrUO&=
z=f19;x3&J364!bCZO!lZC*Ehv<-PX(>iTmLqP@>O43C;{$*rsIn6GB*@7Vi_Y27y_
z6ODb|9*;wghV?2={%I%K|9G1C{g>_ge%#_xVKklate2HrY2gZi7uswZ-?)#||C;6#
zAu_+nLo#3g%PhwC0^83Qo|LRkRjumeUsk;M-Q+K7%6&SMCU-JDX!%ol{Q(QluJgzE
zj{O!bTAFwxI96ktPSW0z<7%ctWevx(|G2VmoO?Xmgl*MD6YbekPPBNR;$5<I&Ak-W
zZ7*v6$^U!!=syE!E{P*r!Qs0E0|RIp1u>U|Ji!8*OH#)=mqd83WeCnK@ux0<&n@M$
zp{!6yttco;Erw4h>1zh37-|NmfM~R37_}j%^DaAx#G1z&o;tNs=={7cR~DTJ(XA!D
zJ<rW#v(C@>xI=f9R;l1d2agb?+7(``90Ez=TUnbz>NLv!H(GBKw(p8OYWe(*@pse5
zUV>Rn)s0OCD&ISsrVEBAY%N}KH!^eQ%41XOx(YN;?GlQ~sJ#$9Y3;j)pgE3_^9*fg
z2B%hwhSi3?{7`URlE=YzN?v60Wtr{qEl(d8mo!g5YI&o(;=TOVlev8UGNMhZ)Dl0$
zO%Q60VSGF_CHTOH&nyQ*7tDV+-`8$M>YN}|-N{l%{353)x=pIRIkP`Cspg~-f6f{Q
zt=ys;JuR!x`mEu*nXj8V&rd*M>h#P<(*!my7FZ+WEuB`{^H^3ZNRA`iDB+r2RRY_O
z%Ki*dM#~j#A<ttz8w>AY7Oh<unEQ%l&+#SF+jbvi$Q60#AbKG%LG<?pRkf1`CUb?D
zdhYW1b%TAi{)|a;N;%_W-%m*G;!*v$JMmBLlo+4M-HM)deQA>q-+tVmQub5ba{h!T
zkE+T_r_Pa^Xeyc6vHzZqN&3lxS#$c|T-$Sf!}A|2S8cL1^NZT~MQ~Ed!i;hqo$Ecy
z+(&nXCcj{tRI+eIyVaTJiww8rM?_`3y11@cInD3$L;fPhwHmL@cIZj{zV~9&w2xcX
ztgilZKhd@~zwg-f<NYkx*U!?Pshts8eLVBDAk&`ZecSK83C+le-T&eAwbz#|>g?4%
zrbyM#P&vHu=7QVT?eqHAf8_V{TlaXfehYibw`{5Ue{*~C4H_QQoz=TP>BkG5I%BVW
zvT4(k8Km^tE6&Ta*B-l6zB+o>*BQyzd6f44t<<eqcP49t^UJ*EZK6MZ%zGjtwy$M>
z#e9~{VvNh=*((L7&Yr2a(Q;=*`QoKpzHHff>gLxS5+ZAlPS*=MWaF;Awt4Eccu<xv
zxiU3-CKCe#D0L&UJaW1RWqHt?D&|@w99dup=5t4+wO2}v3=BdH3b2(($@zK3C5d?@
zu$g1fdMg}jD^8#F*F5Wa)qAbR34JZiunnOro;@llEG_2w%Gp`|?K^M5m+qeL#a~Ld
z1_e}Yt$e*&R{EY#e&{8~pvO`ok6u1|`9|UqSMc|=doAI!!}dj*iAT=OZdshap+3N4
z&*FZ5kF!nF%x7;?&@TAM^O@({m+#H9H!Wbzklxg#Q2&2Ih5U7c@Cl4|5nm6b)H-fr
z{5P*o@uT8I#&sY2^z<A8-AyiDNSNTdFk{iVzPfV>&T|@twqA<?7y4pMQrVjr7#KkQ
zMx+4b5CNqCS*(RVuD}_BNnlp#RCrOJ&&<Fej7S2BDY==&#hHlZXjhe-Qb23&uq;RG
z4RXvoWFTOpzP`Y6fs4~QW+knK+uG%{LR<NFd<$BmVzwri!|Bi7G9itHVOL+jJ5y0+
z6QiBX*|bsWq3qW<E(fFUljlz6xY=^)kb=Hll}lm!oF}QLtUH4R3b%eeuvVVIvOk1r
z<LvuJb~7jL`DK6j@xv~;#C&t_(`$?yO)t#ISYjBo+Fa9xm%Xj=?3uvANtF{LH`pn2
zMa`c4Q`vH<XW$P>;pqu+QP*smQ#aL#>P9Zk>o{!XTUDFRaClF#%lC#NUB@*gtX+J&
zKQ;AoeLFIj+2)eO0vR6H6;92HUeQczP0W0s%u3-mOp!b1C9uZ%>fBjB8zvRcQhRgY
z$$BoATASR4@W_z6?Q`!e-TSGYdm;ZmQN{N!jN=oUFC_V#YP*qkadGp<g7hz{T4yg6
z@*SJ-(j>3#oA24(Hf3MBA}w~mJT3g@-@zqkl=pUe_oUa?E^b}j=(PK4hC7q}TJ6jE
zpz`Wy*4Nk=Mg|5@6d_V8a^!(hEB3XMIOA~$CfCsRc6f3PXJugEMI_g(a(H27d{!S<
zLY*4o>wnup!1nn5q*<4vxcWj??sW-S6e=Lds^y^_8rmvW-Nf5x{$`!0lEbM+X7;`X
z-<qxjpXX4y@=sOfpL2S-Z^v!9ThV3D&K|GMpA(Scb@*PuVI>cn6T+Qq_APfYJ0h$5
z&2aT^UHvTztqWcD`u4|dvgYTXrT3M^cF~ek!FxNi92i?BYsF3b__IrP-K2v5_q93t
zjZ0Xq=ij^(^ZeCHttpQi#XeVv8+HgeC9P2nobqUb_vM*_TM{`;6%J4QmiSRrrgCfA
z<Rlv=S>=eO3)EfZle!kNC@M~08D6X#dykJ-#-VmqzgUIDq9AU+=jRJ*av0eyPZcIK
z>slWRb!QSyaboThTDmvt67#n+%W5|J_41rZP5Z_pk|wzN)yZeBoV%n~zy135+pXR2
z+~;>(_p`C?@OdKDc(`GPipuJ~ooyV>f*0RNUUUy*PMY#i;GU(4^1>+|{1-d6wm<Y)
zDWxeTv&g3^IZfJR)pn79d#q9i0#30-6f^~1zo>mX;?$Jyr~iE`Kdo}=;F_rPAoXeU
z46h3;Ve4$0q5a~cimQ?Tj>yMto;;IHT7%r@F>Y0t{+%Lt|ET*VrR>`u7R9=rxLNjp
z{r~oFY3VyZr>(qwGva<++^apc`)`(&S=>L#S}Y&-<C5TdGmaNC4ZoFl&N}<(d1j$)
z<D+M99=_(+Sf}50^5>V+H^00R={jA!WB*K_{qc^4=9=&P606D!{U5ZS&M&C1&i0gH
znR@QX=1*p`tQ3FUxbJwglVju3)JbnQ*>^R^NFF)ye{WXn>KU)@EKld*x$|=Em&y~b
zw4R-h-?PU2LFv8M3wh3zA6+Fbd?_*S<_WDy<zFS-OYd)=`^nK|?v2M)dJ?L;SC|y<
z2=m;s>Gb8pOU|~RyYrGo@p|GJult&A?<#5qpK7vMNO7zbof`hnB<h|%DBG8~ET~<@
z#J~Vb>WFNQoC!eLUIJ?+g(W`>!3=MOw9KuJm4QJ3k>QI|lZ#T}dHw?KrER4lr;8q0
zh{U$9PpFz^+@NxL)0q%`na>=GOE_CpHY`lB?Ww%cm+E}x%AemSEIgO4PA^-xy7=5)
zyYGb_E*q!k{+y;3xo-CT+4bh@wk?nGo_BK3?LTTW^`6Fk+RC}^^@}R?)22)u3`{4t
z+e`PZ_IrMBzS-Z}ppIjr+Mmw;VUjt0Nn_8xGhy2&OTYa(@9#mL`A;90xGy*RcJp21
z+92!du7e6Qf4IFo^wnZ|)b*Hlm5WIe`AWY%nEH6*(>ZaA`0vD|9q%|TCGmO7YW`ij
zPwzUx$PmyXdt5D6vOQ2IHE5#HQl5YNELe<;MG_oeMICVaYTx21^ls_?{**cWEtTyI
z$qA8S$HWfkM!(EW4DQnO=k~}6@9y2K8T7VIdg{UrXD@|p&z|UXx<|b*Z^|tj%NQ=Z
zO#9AIk#i{$sv9S!XKI^G%kMc@$sIbwL&9mw^vfzY`;Xk#pRukehC{b8YL4}y*31LZ
zj0GOuMP7=*EDG=9TKIIP#OHoFrp*6+*}aO==LuV!%Kd+Q6pr}*O8d@yO}W#bj-OsI
z=L64f*Ag4``UMIl>u(n8U9nuW{DQao)FXBebL>rB%VyvBk*DN%u>9+bJ=OImgAy<6
zSZ#`l`)|MC!t6}JH_CD1MR#ZK-*|6_x|zf})jfPBy}fTsL~IlGooG9K&|%HRg&_*N
z-`|%LiqyF&H2Kk=Sx<}py)f!(`+WUz|H;b*A#K^l#S8Ywezgz2`t{1rd7JNLa_4_N
z6MS%`275m9^*G(-{68O9*z~8heEG7C^>5IfIa+r2H~e`$h26)p%jhMSP{CzRrH#Cz
zB^{^Fb||XoCVF!-YOM^hX;3)O$IH|qStNVtt=Hyxa{9gZcAHskuDm&WpI+RXY=^~<
z-?aFfAN}yf?4IhwHTE{gwhKmF>s@p0`(#hfqbyNZHZ?pI@~Lk+V;^>VA=AxMQ<4Q=
zYOo4U>nTX#D&2T`rL@5+1-2Q?516EPGj3uCHW1)Q)?u7t!mw?%!e)uqY1Jnuc?!OC
z-Ddu2`uV)&pD#Y0cC%~onW#A*dvmx8^-6A;DKnpab!0_s`_0C=lW*jBZ?%X`vYg)N
zH(BG~$+uHdt&>;tzPZ2Zd)C8cGk2!gd_R-=!@uYK-8=WczrWUh?{3B0pLO+i@%_KO
znN=h-{bC+6wJ`HY7)F2NPQLd&{qOPewzC?765I)b?w+Z)UL>fpGB{qX&5-<NUvFwP
z_x@}vO(Q+^XBIAnJ7(OiUGJY!z4|u$G_!xKpc3wiY`Vb=W(EdO-bR#g$hjU=!bxK-
z;jk9~L$HuTF3@-Kp*F%xOER1@Qj@b`9Wi`uo3Q9?X?KyjFopk#oA?u_pVHo)-&@e+
znPPDE=#NW6P6n^9ZrrkMkM{X9US^#KbDcET$_Hh1+}@?Sbg6H*ywj^Cbtm*MJ+&{%
zv$ozmDO6qH$=u?7mc{3Oo>TQnauQs~+2%T@WRX>ZNw>k9C4y|%KmO%9n3y{AwDz=V
zk3;q2xmc}K8`K-ZXYNRPC2?WFKG~zf8;WD%Hg4fDdBb*F_<*U}x*1k%Qye-P*KO?Y
zU6Gim9hCB79XrdkhAlVQZ>(CDGLy|jmF<lnyW7KQZSQU;tU1SfB5nFCtJx}@yEphv
z*6rZ_7{_*}(`klC-cqFot9zQc_i~(e9Bt5Zo+deGN+8efE6$s*UtD~8x|i;RiF;X1
zI-<D0#%3PLXScYawoi4H)URaIsebd;t8xg($gf`QYU#9Noyly04KkD1&%Fx~XmdC*
zh3Aa(!}yg!jxv+C9LQJYz5Z~*T#*lQyV~X$U7BwrqkL-Hex?N3x!jwssB1o}%Py@o
z5mSj3KlR9e=CPnHjd!#^HT-l5mia95i{;U-?9XRSZ@yfA=l7Pf^0j5@g-`dT$EdE2
z*{hZNL$^bEOXs@9{x%0$r?#2-ZBu3{?y=R8G%UHe$fi)T^TuTB1I&9k6VELQ>YRA<
zJwu<_w<S|I#g^DjKHY3o#Q4}q=#=1<@FnHHls*P5d#EY>==~}Y&Clz4ryi>Pxw?)=
zzU9ht-;TQtZ;mP!oi{yu@@#k8QN!GeS#>x3u15z}NLyV9T3%V`Ri+;-bCmaYR($8?
z=i-y+Ep)xby-SHpEO(Q;)YGLB()lWHSRKtf-h9(}bN!2|wx@6Moj|Wo^+){@q)&1b
zFE03(@M~fGnw1v0sS9syw>@>*r&?vl&g*R{i*IzNO`N=@Sjs|fk?)+6iN*VZSF{y2
z%{?=7q7<{(wuj;Q)&KXDZ#ix>WA1{fO??YZfBl(Iejzk9MC#F&O|_CI<29cP?he1B
ze^^8?YOjaV*}ycH@Gr&XTVgfaf2%M5d!O&(hvSD2z7GF8|Ni&?R<3)?+YdiKzT*G8
zgb$BgR!X(Vc^cP0*LdhUN#};ntZx&ZKJ%GY$f9e;boNV{Y_NP?#*@-B$K8G_J-oO5
z#fc_Wxs?%HI|SzYI@~N06m*O~u2s5mn!#ix##@tWwr$<?<B|8Xm-SKyc38U_ODJD>
zH>WyMj<K@+-?`h*`{V_$UO(Hg@yx>G{pwLuz07a?&(8QYNBjd*Ft^9FvcoFU*5PWu
zmaYt3`Nfa_SN$BGy2@++pH*M^|8>Xr_mWb3Y~=nFge?fUr*-4EyQSK*^o1{<)CRrv
zsXOAPwrozupM~2eyZOdVS$cln_f3ICuVNmk<zC;Hy``-C$(AD1VAnm{{?}do|8Az%
z$Agpg+x+`J?}=(|pWg6uZO@0J=dV5vI(O7yLE`19%;xefVNv$SJ?|t~H>}}nlKCR2
zWnv#YJ^A>l_I$nhSEq?R6VE%hR_D&{-DdXN3#v<3KD*gGk2U4P+m9cP3UZ&2e9ZQ0
zukFkKKdoQwwUnr-Y`SxA_VrgK6<<nzTn;w*@hjuQr=80;7?mHJm5|HvwSleDW=56f
z{4X)ne80U>J!WMvUH5LQT;kWyB`I?v{wC>o?vOm&VL0tg-SI7-9a3MH+TIA3zPq-e
zDSE<j&*}1uZ=RXO@Pl{3=QYyz+8K7sdGCGgQ_Qv_s!F{xGW*#g$@8B%r3D`N)#j|o
z^3A+@;URy*l>6!XZ+qIN_B7uuYtjtl_1bZ~<v$~+M$zQFsmR32zyK;p5j6^O5euqO
zaCDV%m9|5$QaN0K*jUNQ$iTplXgR0mCT8ZqDwK1xAUi3sjOv8;`|>pz2)JGUA~ID}
zt!)9fuY%gUJq>aTG@C`ZkIqc`!&zRg)p_J-&c5$ypL)w=D{t-;ib~a4aL2plc7nj9
z3ctsxieIiTx%OQ>MD{D6?di>vvsj)!WS_jfgX2S<$rB?xHqE$B58i#}R5xcNoMI8N
zJ=pXuqhwobKL7Lu%WiFT?5}b?GHutS{0*zjZ{`Vpns_{VmY$-?p89u(a@(J=rs`>@
za;g<R`L?L^X@X*_`wO=_f8I}assRu0@h_Ury`6!90pxQ;DnJesP%4nZ+OEJFJVPi2
zFfxfSgEqo4F!UzRfDe~|_SmvBD1az1e(MOLk|8^6!P`xvzPqM?FldYqZ89go8)7JE
z$1ccFr#ROQhZq<bEEpNU`&U6mf$-Z#R|W>e{$0Izi1{eSV+l!Q<IQ@fhBaQO3*XPc
zz+eX*lm;0M!b=*{7#Uzg)G3K2iJ<8>SVH#n!8tsMZa(tHT+k3N$ZQZ^(%8y`<a~(Z
z_4VR$Sw+ArtGXtJy<>jhJ&}QdAr`|cWz2A|6s1CTYr(wI$Y_ge01MeGAoFp1C7lJE
zS8!QHz$-TjCWf6}UVjjjpDdxJ2`I!scu8XfE8Hu|iOCtMFn_$bY(tDcK!zjx1C%X6
z27~aD#(iwq{DIXT0-pF)Ffr^()9q9L85tO+F*7iTB4vjqjceH9p2*A%$S*1h$<I!O
z@5R=<VuNez9CDO^tU&gP0*V!UT-dyV$2I~U0!>$FhQ>Y0U}IoV;6(Qj6F1yLY00_4
zu;CJ<9rVRhrLb)-NA?iN3fvw#D~QcQcx)rWL)=K`1c0U^K&b$Pmoy#}f_n((fdE*l
zF?`+uwF5wggYc3@w<XxTgVQb|JcGP_TM#8JSS*Em2FE^a61@VNtps@kgqJkdEXU>*
z95xZ~2xwa^@`hW`?rV@OAiSh8X9e6Nm^*6;_yJ@xa_SOC38KlXu=xSg5(3T#t>i#n
z<AEAbb*tgd2h}DdZ=6L=T_Br~y#rb^0tzG$Ueb8~2sZB!wv>SPK(p4!^Vz7$?AB4Z
z_i#*HV_Ca_>?e@%$bLc%y-O#s`3Z+j1Uv#NYLKU8LDSKokOSc*jfYOcJ%Vfgl|W(x
znUCxf)SCawDQrH$WfcLhfM({9=j>3+v7@KqUcoszN5C&23y}SST8=fH#pV~Bb`kIl
zsPBY469JlZ1H}mlFKH|}2lot?DF*`H02z(!4bW5u$XF0w(pY&Bn>Vo7Lcjx{;Y8$t
zM$jYz$OaHz(wKS)?g8v0iUj-sG9B3uputLz$soL>G5Q)dKVY|rfHy#ah1|PEO;ldj
z;oeBZ)r}?K6OaYSK0!@XYwuw52~N8Rcm~uJL++%3x~HHJ1K}l&^Y6kvgS9tCz#|~T
kkv)Ripxk^9n@6zPgVhtPY#>J}F(@%4@G>wgybt050MzrW&;S4c

literal 0
HcmV?d00001

diff --git a/_old/config/admission.js b/_old/config/admission.js
new file mode 100644
index 0000000..c697eab
--- /dev/null
+++ b/_old/config/admission.js
@@ -0,0 +1,23 @@
+const restoreProvider = require("../src/Restore/restoreProvider");
+const { resultResponse } = require("./response");
+const { basickResponse } = require("./response");
+const baseResponse = require("./baseResponseStatus");
+
+const admissionMiddleware = async (req, res, next) => {
+	// read restoreExamDetailIdx from url
+	const userIdx = req.verifiedToken.userIdx;
+	const restoreExamDetailIdx = req.query.restoreExamDetailIdx;
+	// restoreExamDetailIdx does not exist
+	if (!restoreExamDetailIdx) {
+		return next();
+	}
+
+	await restoreProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	const admissionTicketResult = await restoreProvider.getExamAdmissionTicket(userIdx, restoreExamDetailIdx);
+	if (admissionTicketResult.isSuccess && admissionTicketResult.result)
+		req.admissionAuthStatus = admissionTicketResult.result.auth;
+	next();
+};
+
+module.exports = admissionMiddleware;
diff --git a/_old/config/authCheck.js b/_old/config/authCheck.js
new file mode 100644
index 0000000..9e5f4e0
--- /dev/null
+++ b/_old/config/authCheck.js
@@ -0,0 +1,90 @@
+const storeProvider = require("../src/Store/storeProvider");
+const userProvider = require("../src/User/userProvider");
+const adminProvider = require("../src/Admin/adminProvider");
+const examProvider = require("../src/Exam/examProvider");
+const secret_config = require("./secret");
+const baseResponse = require("./baseResponseStatus");
+const { basickResponse } = require("./response");
+const jwt = require("jsonwebtoken");
+const userService = require("../src/User/userService");
+
+const authCheck = (req, res, next) => {
+	let examDetailIdx = req.params.examDetailIdx;
+	let examIdx = req.params.examIdx;
+	const examRecordIdx = req.params.examRecordIdx;
+	const token = req.headers["x-access-token"];
+
+	// console.log("req.query : ", req.query);
+	// console.log("examDetailIdx : ", examDetailIdx);
+	// console.log("examIdx : ", examIdx);
+	// console.log("examRecordIdx : ", examRecordIdx);
+	// console.log("token : ", token);
+
+	if (!token) return res.send(basickResponse(baseResponse.TOKEN_EMPTY));
+
+	if (token && token.length > 9) {
+		const promiseAuth = new Promise((resolve, reject) => {
+			jwt.verify(token, secret_config.jwtsecret, (err, verifiedToken) => {
+				if (err) reject(err);
+				resolve(verifiedToken);
+			});
+		});
+		promiseAuth
+			.then(async (verifiedToken) => {
+				let authResult;
+				let publicLevel;
+				const userIdx = String(verifiedToken.userIdx);
+				// console.log("userIdx : ", userIdx);
+				if (examRecordIdx) {
+					const examDetailInfo = await userProvider.userExamRecordToExamDetail(examRecordIdx);
+					examDetailIdx = examDetailInfo.length ? examDetailInfo[0].examDetailIdx : null;
+				}
+				if (examDetailIdx) {
+					if (typeof examDetailIdx == "object") throw "userExamRecordToExamDetail Error";
+					// console.log("test 통과");
+					const value = await storeProvider.checkUserExamDetailAuth(examDetailIdx, userIdx);
+					authResult = value[0].exist;
+					const examDetailInfo = await examProvider.examDetailCheck(examDetailIdx);
+					// 무료 회차라면 권한 부여
+					publicLevel = examDetailInfo[0].publicLevel;
+					if (publicLevel == 0) authResult = 1;
+				} else {
+					if (examIdx) {
+						const value = await storeProvider.checkUserExamAuth(examIdx, userIdx);
+						authResult = value[0].exist;
+					}
+				}
+				// else throw "P  arameter is null";
+				const adminIdxCheckResult = await adminProvider.adminCheck(userIdx);
+				// console.log("adminIdxCheckResult : ", adminIdxCheckResult[0].exist);
+				// console.log("authResult : ", authResult);
+				req.isAuth = authResult == 1 || adminIdxCheckResult[0].exist == 1 ? 1 : 0;
+				// console.log("test");
+
+				if (req.isAuth && !adminIdxCheckResult[0].exist) {
+					//관리자는 로그 제외
+					if (examDetailIdx || examIdx) {
+						if (!examDetailIdx) await userService.insertUserAuthLogExamIdx(userIdx, examIdx);
+						else await userService.insertUserAuthLogDetailIdx(userIdx, examDetailIdx);
+					}
+				}
+				//req.isAuth = 1; // 모두 무료상태
+				req.verifiedToken = verifiedToken;
+				next();
+			})
+			.catch((err) => {
+				console.log("Wrong Prameters " + examDetailIdx, examIdx, examRecordIdx);
+				return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+			});
+		// 시험 문제 구매 검사 (결제 구현후 수정)
+		// await promiseAuth.then(async (data) => {
+		// 	userIdx = data.userIdx;
+		// 	const userAuthCheckResult = await examProvider.userExamDetailAuthCheck(userIdx, examDetailIdx);
+		// 	isAuth = userAuthCheckResult[0].exist;
+		// });
+		// const today = new Date();
+		// const nowDate =
+		// 	today.getFullYear() + ("0" + (today.getMonth() + 1)).slice(-2) + ("0" + today.getDate()).slice(-2);
+	}
+};
+module.exports = authCheck;
diff --git a/_old/config/baseResponseStatus.js b/_old/config/baseResponseStatus.js
new file mode 100644
index 0000000..2b1d5d3
--- /dev/null
+++ b/_old/config/baseResponseStatus.js
@@ -0,0 +1,485 @@
+module.exports = {
+	// Success
+	SUCCESS: { isSuccess: true, code: 1000, message: "성공" }, //조회
+	SIGN_UP_SUCCESS: { isSuccess: true, code: 1100, message: "회원가입에 성공하였습니다." },
+	EMAIL_CONFIRM_SUCCESS: { isSuccess: true, code: 1101, message: "사용가능한 이메일입니다." },
+	NICKNAME_CONFIRM_SUCCESS: { isSuccess: true, code: 1102, message: "사용가능한 닉네임입니다." },
+	SIGN_IN_SUCCESS: { isSuccess: true, code: 1103, message: "로그인에 성공하였습니다." },
+	SOCIAL_SIGNIN_SUCCESS: { isSuccess: true, code: 1104, message: "소셜 로그인에 성공하였습니다." },
+	NEED_SIGN_UP: { isSuccess: true, code: 1105, message: "회원가입이 필요한 계정입니다." },
+	FIND_PASSWORD_SUCCESS: { isSuccess: true, code: 1106, message: "해당 핸드폰 번호로 임시 비밀번호를 발송하였습니다." },
+	PASSWORD_CORRESPOND_SUCCESS: { isSuccess: true, code: 1107, message: "비밀번호가 일치합니다." },
+	PASSWORD_UPDATE_SUCCESS: { isSuccess: true, code: 1108, message: "비밀번호가 변경되었습니다." },
+	NICKNAME_UPDATE_SUCCESS: { isSuccess: true, code: 1109, message: "닉네임이 변경되었습니다." },
+	USER_INFO_UPDATE_SUCCESS: { isSuccess: true, code: 1110, message: "회원정보가 변경되었습니다." },
+	USER_DROP_SUCCESS: { isSuccess: true, code: 1111, message: "회원탈퇴에 성공하였습니다." },
+	VERIFICATION_SUCCESS: { isSuccess: true, code: 1112, message: "토큰 검증에 성공하였습니다." },
+	EXTEND_DURATION_SUCCESS: {
+		isSuccess: true,
+		code: 1113,
+		message: "환불 취소를 하여 상품기한이 연장되었습니다.",
+	},
+	ADMIN_REFUND_CANCEL_SUCCESS: {
+		isSuccess: true,
+		code: 1114,
+		message: "환불취소성공. 유저의 해당 상품 권한을 부여하였습니다.",
+	},
+	SUB_DURATION_SUCCESS: {
+		isSuccess: true,
+		code: 1115,
+		message: "환불 성공. 상품 유효기간을 줄였습니다.",
+	},
+	DELETE_USER_AUTH_SUCCESS: {
+		isSuccess: true,
+		code: 1116,
+		message: "회원의 해당 상품권한이 삭제되었습니다",
+	},
+	SUSPEND_SUCCESS: {
+		isSuccess: true,
+		code: 1117,
+		message: "해당 회원을 정지시켰습니다.",
+	},
+	GET_PRODUCT_SUCCESS: {
+		isSuccess: true,
+		code: 1118,
+		message: "상품 권한을 부여하기 위한 상품 목록들을 성공적으로 불러왔습니다",
+	},
+	SUSPEND_PRODUCTAUTH_SUCCESS: {
+		isSuccess: true,
+		code: 1119,
+		message: "회원의 상품 권한을 회수하였습니다.",
+	},
+	UPDATE_PRODUCTAUTH_SUCCESS: {
+		isSuccess: true,
+		code: 1120,
+		message: "회원의 상품 권한 유효기간을 변경하였습니다.",
+	},
+	GIVE_PRODUCTAUTH_SUCCESS: {
+		isSuccess: true,
+		code: 1121,
+		message: "회원에게 상품권한을 부여하였습니다.",
+	},
+	GET_AUTHORIZATIONLOG_SUCCESS: {
+		isSuccess: true,
+		code: 1122,
+		message: "상품권한 부여 기록들을 성공적으로 가져왔습니다.",
+	},
+	// Common
+	TOKEN_EMPTY: { isSuccess: false, code: 2000, message: "토큰을 입력해주세요." },
+	TOKEN_VERIFICATION_FAILURE: { isSuccess: false, code: 3000, message: "토큰 검증 실패" },
+	TOKEN_VERIFICATION_SUCCESS: { isSuccess: true, code: 1001, message: "토큰 검증 성공" },
+
+	//Requst error
+	PROBLEM_COUNT_LACK: { isSuccess: false, code: 2046, message: "문제의 문항수가 부족합니다. 출제 범위를 늘려주세요." },
+	PROBLEM_COUNT_LACK_AS_AUTH: {
+		isSuccess: false,
+		code: 2046,
+		message: "권한을 소유한 문제의 문항수가 부족합니다. 해당 서비스를 이용하기 위해서는 상품을 구매해주세요",
+	},
+	SUBJECTIVELABEL_NOT_EXIST: { isSuccess: false, code: 2046, message: "시험의 실기 여부 인덱스를 입력해주세요" },
+	REFERENCE_EXISTS: { isSuccess: false, code: 2046, message: "해당 데이터를 참조하는 데이터가 존재합니다." },
+	NAME_EMPTY: { isSuccess: false, code: 2047, message: "유저 이름을 입력해 주세요." },
+	NAME_ERROR_TYPE: { isSuccess: false, code: 2048, message: "유저 이름을 형식에 맞게 입력해 주세요." },
+	NAME_NOT_EXIST: { isSuccess: false, code: 2049, message: "해당 유저가 존재하지 않습니다." },
+	EXAM_ERROR_TYPE: { isSuccess: false, code: 2050, message: "examIdx를 형식에 맞게 입력해 주세요." },
+	EXAM_NOT_EXIST: { isSuccess: false, code: 2051, message: "해당 examIdx가 존재하지 않습니다." },
+	EXAM_EMPTY: { isSuccess: false, code: 2052, message: "examIdx를 입력해 주세요." },
+	EXAMDETAIL_EMPTY: { isSuccess: false, code: 2053, message: "examDetailIdx를 입력해 주세요." },
+	EXAMDETAIL_ERROR_TYPE: { isSuccess: false, code: 2054, message: "examDetailIdx를 형식에 맞게 입력해 주세요." },
+	EXAMDETAIL_NOT_EXIST: { isSuccess: false, code: 2055, message: "해당 examDetailIdx가 존재하지 않습니다." },
+	EMAIL_ERROR_TYPE: { isSuccess: false, code: 2056, message: "이메일을 형식에 맞게 입력해 주세요." },
+	EMAIL_NOT_EXIST: { isSuccess: false, code: 2057, message: "해당 이메일이 존재하지 않습니다." },
+	EMAIL_EMPTY: { isSuccess: false, code: 2058, message: "이메일을 입력해 주세요." },
+	PASSWORD_ERROR_TYPE: { isSuccess: false, code: 2059, message: "비밀번호를 형식에 맞게 입력해 주세요." },
+	PASSWORD_EMPTY: { isSuccess: false, code: 2060, message: "비밀번호를 입력해 주세요." },
+	ENGINEEO_EMAIL_NOT_EXIST: { isSuccess: false, code: 2234, message: "해당 이메일은 엔지니오 이메일 계정이 아닙니다." },
+	PHONENUM_ERROR_TYPE: { isSuccess: false, code: 2061, message: "핸드폰 번호를 형식에 맞게 입력해 주세요." },
+	PHONENUM_EMPTY: { isSuccess: false, code: 2062, message: "핸드폰 번호를 입력해 주세요." },
+	USERNAME_ERROR_TYPE: { isSuccess: false, code: 2063, message: "유저 이름을 형식에 맞게 입력해 주세요." },
+	USERNAME_EMPTY: { isSuccess: false, code: 2064, message: "유저 이름을 입력해 주세요." },
+	GENDER_ERROR_TYPE: { isSuccess: false, code: 2065, message: "성별을 형식에 맞게 입력해 주세요." },
+	GENDER_EMPTY: { isSuccess: false, code: 2066, message: "성별을 입력해 주세요." },
+	DATEOFBIRTH_ERROR_TYPE: { isSuccess: false, code: 2067, message: "생년월일을 형식에 맞게 입력해 주세요." },
+	DATEOFBIRTH_EMPTY: { isSuccess: false, code: 2068, message: "생년월일을 입력해 주세요." },
+	NICKNAME_ERROR_TYPE: { isSuccess: false, code: 2069, message: "닉네임을 형식에 맞게 입력해 주세요." },
+	NICKNAME_EMPTY: { isSuccess: false, code: 2070, message: "닉네임을 입력해 주세요." },
+	EMAIL_LENGTH_ERROR: {
+		isSuccess: false,
+		code: 2071,
+		message: "이메일의 길이가 맞지 않습니다. 30자 이내로 입력해주세요.",
+	},
+	PASSWORD_LENGTH_ERROR: { isSuccess: false, code: 2072, message: "비밀번호의 길이가 맞지 않습니다." },
+	DATEOFBIRTH_LENGTH_ERROR: { isSuccess: false, code: 2073, message: "생년월일의 길이가 맞지 않습니다." },
+	GENDER_LENGTH_ERROR: { isSuccess: false, code: 2074, message: "성별의 길이가 맞지 않습니다." },
+	NICKNAME_LENGTH_ERROR: {
+		isSuccess: false,
+		code: 2075,
+		message: "닉네임의 길이가 맞지 않습니다. 8자 이내로 입력해주세요.",
+	},
+	EMAIL_EXIST: { isSuccess: false, code: 2076, message: "해당 이메일이 이미 존재합니다." },
+	PHONENUM_NOT_EXIST: { isSuccess: false, code: 2077, message: "해당 핸드폰 번호가 존재하지 않습니다." },
+	PHONENUM_EXIST: { isSuccess: false, code: 2078, message: "해당 핸드폰 번호가 이미 존재합니다." },
+	PASSWORD_FIND_ERROR: { isSuccess: false, code: 20879, message: "비밀번호 찾기에 실패하였습니다." },
+	USER_NOT_EXIST: { isSuccess: false, code: 2080, message: "존재하지 않는 계정입니다." },
+	USER_QUIT: { isSuccess: false, code: 2081, message: "탈퇴한 계정입니다." },
+	USER_NOT_MATCH: { isSuccess: false, code: 2082, message: "입력하신 유저 인덱스와 토큰값이 일치하지 않습니다." },
+	USER_ERROR_TYPE: { isSuccess: false, code: 2083, message: "유저 인덱스를 형식에 맞게 입력해 주세요." },
+	USER_EMPTY: { isSuccess: false, code: 2084, message: "유저 인덱스를 입력해 주세요." },
+	PASSWORD_NOT_MATCH: { isSuccess: false, code: 2085, message: "비밀번호가 틀렸습니다." },
+	NICKNAME_EXIST: { isSuccess: false, code: 2086, message: "해당 닉네임이 이미 존재합니다." },
+	REVIEWNOTE_EXIST: { isSuccess: false, code: 2087, message: "해당 오답노트가 이미 존재합니다." },
+	SOCIAL_SIGNIN_FAIL: { isSuccess: false, code: 2088, message: "소셜 로그인에 실패하였습니다." },
+	ACCESS_TOKEN_FAIL: { isSuccess: false, code: 2089, message: "유효하지 않은 토큰입니다." },
+	EXAMSORT_EMPTY: { isSuccess: false, code: 2090, message: "examSort를 입력해주세요." },
+	EXAMSORT_ERROR_TYPE: { isSuccess: false, code: 2091, message: "examSort를 형식에 맞게 입력해주세요." },
+	ANSWERLIST_LENGTH_ERROR: { isSuccess: false, code: 2092, message: "answerList를 길이에 맞게 입력해주세요." },
+	ANSWERLIST_EMPTY: { isSuccess: false, code: 2093, message: "answerList를 입력해주세요." },
+	ANSWERLIST_ERROR_TYPE: { isSuccess: false, code: 2094, message: "answerList를 형식에 맞게 입력해주세요." },
+	ANSWERLIST_NOT_EXIST: { isSuccess: false, code: 2095, message: "answerList가 존재하지 않습니다." },
+	PROBLEM_EMPTY: { isSuccess: false, code: 2096, message: "문제 인덱스를 입력해주세요." },
+	PROBLEM_ERROR_TYPE: { isSuccess: false, code: 2097, message: "문제 인덱스를 형식에 맞게 입력해주세요." },
+	PROBLEM_NOT_EXIST: { isSuccess: false, code: 2098, message: "문제 인덱스가 존재하지 않습니다." },
+	CORRECT_EMPTY: { isSuccess: false, code: 2099, message: "correct를 입력해주세요." },
+	CORRECT_ERROR_TYPE: { isSuccess: false, code: 2100, message: "correct를 형식에 맞게 입력해주세요." },
+	PROBLEMIDX_EMPTY: { isSuccess: false, code: 2101, message: "problemIdx를 입력해주세요." },
+	PROBLEMIDX_ERROR_TYPE: { isSuccess: false, code: 2102, message: "problemIdx를 형식에 맞게 입력해주세요." },
+	ARRAY_LENGTH_ERROR: { isSuccess: false, code: 2103, message: "각각의 배열을 길이에 맞게 입력해주세요." },
+	AUTH_NUMBER_EMPTY: { isSuccess: false, code: 2104, message: "인증번호를 입력해주세요." },
+	AUTH_NUMBER_ERROR_TYPE: { isSuccess: false, code: 2105, message: "인증번호를 형식에 맞게 입력해주세요." },
+	REVIEWNOTE_NOT_EXIST: { isSuccess: false, code: 2106, message: "해당 오답노트가 존재하지 않습니다." },
+	SCORE_EMPTY: { isSuccess: false, code: 2107, message: " 점수를 입력해 주세요. " },
+	SCORE_ERROR_TYPE: { isSuccess: false, code: 2108, message: " 점수를 형식에 맞게 입력해 주세요. " },
+	TITLE_EMPTY: { isSuccess: false, code: 2109, message: " 제목을 입력해 주세요. " },
+	CONTENT_EMPTY: { isSuccess: false, code: 2110, message: " 내용을 입력해 주세요. " },
+	BOARDIDX_EMPTY: { isSuccess: false, code: 2111, message: " boardIdx를 입력해 주세요. " },
+	BOARDIDX_ERROR_TYPE: { isSuccess: false, code: 2112, message: " boardIdx를 형식에 맞게 입력해 주세요. " },
+	BOARDIDX_NOT_EXIST: { isSuccess: false, code: 2113, message: " 삭제된 게시글입니다. " },
+	BOARDIDX_NOT_MATCH: { isSuccess: false, code: 2114, message: " 문제 오류 게시글이 아닙니다. " },
+	EXAMRECORD_EMPTY: { isSuccess: false, code: 2115, message: " examRecordIdx를 입력해 주세요. " },
+	EXAMRECORD_ERROR_TYPE: { isSuccess: false, code: 2116, message: " examRecordIdx를 형식에 맞게 입력해 주세요. " },
+	EXAMRECORD_NOT_EXIST: { isSuccess: false, code: 2117, message: " examRecordIdx가 존재하지 않습니다. " },
+	PROBLEMNUM_EMPTY: { isSuccess: false, code: 2118, message: " 문제 번호를 입력해 주세요. " },
+	// PROBLEM_EMPTY: { isSuccess: false, code: 2119, message: " 문제를 입력해 주세요. " },
+	ANSWER_EMPTY: { isSuccess: false, code: 2120, message: " 답을 입력해 주세요. " },
+	SOLUTION_EMPTY: { isSuccess: false, code: 2121, message: " 해설을 입력해 주세요. " },
+	EXAMSUBJECT_EMPTY: { isSuccess: false, code: 2122, message: " 과목 인덱스를 입력해 주세요. " },
+	ISKATEX_EMPTY: { isSuccess: false, code: 2123, message: " 카텍스 유무를 입력해 주세요. " },
+	QUESTIONNUM_EMPTY: { isSuccess: false, code: 2124, message: " 문항 번호를 입력해 주세요. " },
+	QUESTION_EMPTY: { isSuccess: false, code: 2125, message: " 문항 내용을 입력해 주세요. " },
+	PROBLEMIDX_EXIST: { isSuccess: false, code: 2126, message: " 이미 존재하는 문제번호입니다. " },
+	EXAMNAME_EXIST: { isSuccess: false, code: 2127, message: " 이미 존재하는 시험 이름입니다. " },
+	USER_INVINCIBILITY: { isSuccess: false, code: 2128, message: " 변경할 수 없는 회원입니다. " },
+	USER_PROCESSED: { isSuccess: false, code: 2129, message: " 이미 처리된 회원입니다. " },
+	SIGNIN_PASSWORD_WRONG: { isSuccess: false, code: 2130, message: " 잘못된 비밀번호입니다. . " },
+	DROP_USER: { isSuccess: false, code: 2131, message: " 탈퇴한 회원입니다. " },
+	EXAMDETAIL_EXIST: { isSuccess: false, code: 2132, message: " 이미 존재하는 시험 회차입니다. " },
+	ESSENTIAL_EMPTY: { isSuccess: false, code: 2133, message: " 필수 값을 입력해 주세요. " },
+	EXAMSUBJECT_NOT_EXIST: { isSuccess: false, code: 2134, message: " 존재하지 않는 과목입니다. " },
+	EXAMSUBJECTNAME_EMPTY: { isSuccess: false, code: 2135, message: " 시험 과목이름을 입력해 주세요. " },
+	EXAMSUBJECTNUM_EMPTY: { isSuccess: false, code: 2136, message: " 시험 과목번호를 입력해 주세요. " },
+	IMPOSSIBLE_VALUE_ERROR: {
+		isSuccess: false,
+		code: 2137,
+		message: " 불가능한 값을 입력하였습니다. 다시 확인하여 주세요. ",
+	},
+	PROBLEM_SUBJECT_EXIST: { isSuccess: false, code: 2138, message: " 해당 회차 과목의 문제번호가 이미 존재합니다. " },
+	DATA_ERROR_TYPE: { isSuccess: false, code: 2139, message: " 해당 데이터타입의 맞게 입력해주세요. " },
+	NICKNAME_NOT_EXIST: { isSuccess: false, code: 2140, message: " 존재하지 않는 닉네임입니다. " },
+	EXAMSORT_EXIST: { isSuccess: false, code: 2141, message: " 이미 존재하는 시험 종류입니다. " },
+	EXAMSORTREF_EMPTY: { isSuccess: false, code: 2142, message: " 시험 구분을 입력해 주세요. " },
+	EXAMSORT_NOT_EXIST: { isSuccess: false, code: 2143, message: " 존재하지 않는 시험 종류입니다. " },
+	MODEXAMSORT_EMPTY: { isSuccess: false, code: 2144, message: " 변경할 시험 종류를 입력해 주세요. " },
+	MODEXAMSORTREF_EMPTY: { isSuccess: false, code: 2145, message: " 변경할 시험 구분을 입력해 주세요. " },
+	EXAMSORTREF_NOT_EXIST: { isSuccess: false, code: 2146, message: " 존재하지 않는 시험 구분입니다. " },
+	PROBLEM_SUBJECT_EXIST: { isSuccess: false, code: 2147, message: " 연결되어 있는 문제가 존재합니다.  " },
+	EXAM_EXIST_EXAMDETAIL: { isSuccess: false, code: 2148, message: " 해당 시험에 시험회차를 먼저 삭제해주세요.  " },
+	ENTERPRISE_EMPTY: { isSuccess: false, code: 2149, message: " 회사를 입력해주세요. " },
+	ENTERPRISE_NOT_EXIST: { isSuccess: false, code: 2150, message: " 존재하지 않는 회사입니다. " },
+	DATE_EMPTY: { isSuccess: false, code: 2151, message: " 날짜를 입력해 주세요. " },
+	EXAM_NOT_MATCH: { isSuccess: false, code: 2152, message: " 해당 시험과 회차가 일치하지 않습니다. " },
+	ISPUBLIC_TYPE_ERROR: { isSuccess: false, code: 2153, message: " 공개여부의 형식이 옳바르지 않습니다. " },
+	DATE_NOT_MATCH: { isSuccess: false, code: 2154, message: "날짜의 포맷은 'YYYY-MM-dd'여야 합니다." },
+	ISDELETED: { isSuccess: false, code: 2155, message: "삭제된 상태입니다." },
+	SUBJECTMULTI_NOT_MATCH: { isSuccess: false, code: 2155, message: "해당 시험에는 존재하지 않는 과목입니다." },
+	SUBJECT_EMPTY: { isSuccess: false, code: 2156, message: "과목 배열을 전달해주세요." },
+	EMAIL_INVALID: { isSuccess: false, code: 2157, message: "탈퇴하신 이메일입니다. 다른 이메일을 사용해주세요." },
+	EMAIL_PHONENUM_NOT_MATCH: {
+		isSuccess: false,
+		code: 2158,
+		message: "가입하신 이메일과 휴대폰 번호가 일치하지 않습니다.",
+	},
+	NICKNAME_SAME_ERROR: { isSuccess: false, code: 2159, message: "기존 닉네임으로 바꾸실 수 없습니다." },
+	QUESTIONNUM_ERROR_TYPE: { isSuccess: false, code: 2160, message: "선지 번호가 형식에 맞지 않습니다." },
+	ACCESSLEVEL_ERROR_TYPE: { isSuccess: false, code: 2161, message: "권한 수준이 형식에 맞지 않습니다." },
+	ACCESSLEVEL_EMPTY: { isSuccess: false, code: 2162, message: "권한 수준을 입력해 주세요. " },
+	SEARCHREF_EMPTY: { isSuccess: false, code: 2163, message: "검색 구분을 입력해 주세요. " },
+	SEARCHREF_ERROR_TYPE: { isSuccess: false, code: 2164, message: "검색 구분이 형식에 맞지 않습니다. " },
+	DOWNLOAD_ERROR: { isSuccess: false, code: 2165, message: "다운로드에 실패하였습니다. " },
+	UPLOADFILE_EMPTY: { isSuccess: false, code: 2166, message: "업로드할 파일을 올려주세요. " },
+	CACHEKEY_EMPTY: { isSuccess: false, code: 2167, message: " 캐싱키를 입력해주세요. " },
+	CACHEKEY_NOT_EXIST: { isSuccess: false, code: 2168, message: " 캐싱키가 존재하지 않습니다. " },
+	STATUS_EMPTY: { isSuccess: false, code: 2169, message: " 상태가 존재하지 않습니다. " },
+	STATUS_ERROR_TYPE: { isSuccess: false, code: 2170, message: " 상태의 형태가 옳바르지 않습니다. " },
+
+	//Connection, Transaction 등의 서버 오류
+	DB_ERROR: { isSuccess: false, code: 4000, message: "데이터 베이스 에러" },
+	SERVER_ERROR: { isSuccess: false, code: 4001, message: "서버 에러" },
+	PHONENUM_NOT_KOREA: {
+		isSuccess: false,
+		code: 2001,
+		message: " 대한민국 핸드폰 번호가 아닙니다. 해당 핸드폰번호로 가입할 수 없습니다. ",
+	},
+
+	//복원 회차 인덱스
+	RESTORE_EXAM_NOT_EXIST: { isSuccess: false, code: 2169, message: "복원 회차 인덱스가 존재하지 않습니다." },
+	RESTORE_EXAM_EMPTY: { isSucess: false, code: 2170, message: "복원 시험 회차 인덱스를 입력해주세요." },
+	RESTORE_EXAM_ERROR_TYPE: {
+		isSuccess: false,
+		code: 2171,
+		message: "복원 시험 회차 인덱스를 형식에 맞게 입력해주세요.",
+	},
+	STATUS_EMPTY: { isSuccess: false, code: 2172, message: " 상태가 존재하지 않습니다. " },
+	STATUS_ERROR_TYPE: { isSuccess: false, code: 2173, message: " 상태의 형태가 옳바르지 않습니다. " },
+	//복원 현황 인덱스
+	RESTORATION_EMPTY: { isSuccess: false, code: 2174, message: "복원 현황 문제 인덱스를 입력해주세요." },
+	RESTORATION_NOT_EXIST: { isSuccess: false, code: 2175, message: "해당 복원 문제가 존재하지 않습니다." },
+	RESTORATION_ERROR_TYPE: { isSuccess: false, code: 2176, message: "복원 인덱스를 형식에 맞게 입력해주세요" },
+	//복원 댓글 인덱스
+	RESTORATION_COMMENT_EMPTY: { isSuccess: false, code: 2177, message: "restorationCommentIdx를 입력해주세요." },
+	RESTORATION_COMMENT_ERROR_TYPE: {
+		isSuccess: false,
+		code: 2178,
+		message: "restorationCommentIdx를 형식에 맞게 입력해주세요.",
+	},
+	RESTORATION_COMMENT_NOT_EXIST: {
+		isSuccess: false,
+		code: 2179,
+		message: "존재하지 않는 restorationCommentIdx입니다.",
+	},
+	//복원 댓글 문자열
+	COMMENT_NOT_EXIST: { isSuccess: false, code: 2180, message: "해당 복원 댓글이 존재하지 않습니다." },
+	COMMENT_EMPTY: { isSuccess: false, code: 2181, message: "댓글 내용을 입력해주세요." },
+	COMMENT_LENGTH_ERROR: { isSuccess: false, code: 2182, message: "댓글은 300자 이내로 입력해주세요." },
+	COMMENTER_MATCH_FAILED: { isSuccess: false, code: 2183, message: "해당 댓글의 작성자가 아닙니다." },
+
+	//베스트 댓글 개수 제한
+	TOP_EMPTY: { isSucess: false, code: 2184, message: "베스트 댓글 개수를 지정해주세요" },
+	TOP_ERROR_TYPE: { isSuccess: false, code: 2185, message: "베스트 댓글 개수 자료형은 숫자여야합니다." },
+
+	PROBLEMNUM_EMPTY: { isSucess: false, code: 2186, message: "문제 번호를 입력해주세요." },
+	ORDER_ERROR_TYPE: { isSuccess: false, code: 2187, message: "알 수 없는 정렬 기준입니다." },
+
+	//직접 문제 생성 길이 제한
+	CUSTOM_COMMENT_LENGTH_ERROR: { isSuccess: false, code: 2188, message: "문제 생성 시 코멘트는 200자 이내여야합니다." },
+	CUSTOM_PROBLEM_LENGTH_ERROR: { isSuccess: false, code: 2189, message: "문제 생성 시 문제는 500자 이내여야합니다." },
+	CUSTOM_QUESTION_LENGTH_ERROR: { isSuccess: false, code: 2190, message: "문제 생성 시 선지는 500자 이내여야합니다." },
+	SEARCH_EMPTY: { isSuccess: false, code: 2191, message: "검색할 내용을 입력해주세요." },
+	ROUND_EMPTY: { isSuccess: false, code: 2192, message: "회차를 입력해주세요." },
+	ISPUBLIC_TYPE_ERROR: { isSuccess: false, code: 2193, message: "공개여부의 형식이 올바르지 않습니다." },
+	CUSTOM_PROBLEM_EMPTY: { isSuccess: false, code: 2194, message: "생성된 복원문제가 존재하지 않습니다." },
+
+	//문제 오류 신고 value check
+	PROBLEMERROR_EMPTY: { isSuccess: false, code: 2195, message: "문제 오류 인덱스를 입력해 주세요." },
+	PROBLEMERROR_ERROR: { isSuccess: false, code: 2196, message: "문제 오류 인덱스의 형식이 옳바르지않습니다." },
+	PROBLEMERROR_NOT_EXIST: { isSuccess: false, code: 2197, message: "문제 오류 인덱스가 존재하지 않습니다." },
+
+	ISPUBLIC_EMPTY: { isSuccess: false, code: 2195, message: "공개 여부를 입력해주세요." },
+	ADMIN_NOT_EXIST: { isSuccess: false, code: 2196, message: "관리자 권한이 없습니다." },
+	KING_NOT_EXIST: { isSuccess: false, code: 2196, message: "절대자 권한이 없습니다." },
+
+	// 달력 value check
+	ENTERPRISENAME_EMPTY: { isSuccess: false, code: 2197, message: "회사 이름을 입력해 주세요." },
+	LINK_EMPTY: { isSuccess: false, code: 2198, message: "회사 URL를 입력해 주세요." },
+	STARTDATE_EMPTY: { isSuccess: false, code: 2199, message: "시작 날짜를 입력해 주세요." },
+	ENDDATE_EMPTY: { isSuccess: false, code: 2200, message: "마감 날짜를 입력해 주세요." },
+	EXAMDATE_EMPTY: { isSuccess: false, code: 2201, message: "시험 날짜를 입력해 주세요." },
+	ENTERPRISE_IMAGE_EMPTY: { isSuccess: false, code: 2202, message: "공고문 이미지를 입력해 주세요." },
+	//ESSENTIALELEMENT_EMPTY: { isSuccess: false, code: 2203, message: "필수 요소를 입력해 주세요." },
+	//EXTRAELEMENT_EMPTY: { isSuccess: false, code: 2204, message: "가산 요소를 입력해 주세요." },
+	//ETCELEMENT_EMPTY: { isSuccess: false, code: 2205, message: "기타 요소를 입력해 주세요." },
+	DATE_ERROR: { isSuccess: false, code: 2203, message: "날짜 형식이 옳바르지 않습니다." },
+
+	//채널톡 멤버해시 check
+	MEMBERHASH_NOT_EXIST: { isSucess: false, code: 2204, message: "userId 해시가 생성되지 않았습니다." },
+
+	//전기공사기사 전기기사 동기화 check
+	ENGINEER_ERROR: {
+		isSuccess: false,
+		code: 2209,
+		message:
+			"전기공사기사와 문제 구성이 동일하지 않은 회차입니다.(전력공학, 전기기기, 회로이론 및 제어공학, 전기 설비 과목 문제이고 1,2 회차여야 업데이트 가능)",
+	},
+	ENGINEER_WORK_ERROR: {
+		isSuccess: false,
+		code: 2210,
+		message: "업데이트 해야하는 전기공사기사 회차 인덱스가 존재하지 않습니다.",
+	},
+	// 카테고리 value check
+	CATEGORYREF_EMPTY: { isSucess: false, code: 2205, message: "상위 카테고리를 입력해주세요." },
+	CATEGORYREF_NOT_EXIST: { isSucess: false, code: 2206, message: "해당 상위 카테고리가 존재하지 않습니다." },
+	CATEGORY_EMPTY: { isSucess: false, code: 2207, message: "카테고리를 입력해주세요." },
+	CATEGORY_NOT_EXIST: { isSucess: false, code: 2208, message: "해당 카테고리가 존재하지 않습니다." },
+	CATEGORY_ERROR_TYPE: { isSucess: false, code: 2209, message: "해당 카테고리 인덱스의 형식이 올바르지 않습니다." },
+	PROBLEM_CATEGORY_EXIST: { isSucess: false, code: 2221, message: "해당 카테고리를 참조하는 문제가 존재합니다." },
+
+	//Store
+	BUSINESS_CATEGORY_ERROR: { isSuccess: false, code: 2210, message: "해당 유료 카테고리 인덱스가 존재하지 않습니다." },
+	BUSINESS_CATEGORY_ERROR_TYPE: {
+		isSuccess: false,
+		code: 2211,
+		message: "유료 카테고리 인덱스의 형식이 올바르지 않습니다.",
+	},
+	PAY_PARAM_EMPTY: { isSuccess: false, code: 2212, message: "결제와 관련된 매개변수가 누락되었습니다." },
+	VACCOUNT_ERROR: { isSuccess: false, code: 2213, message: "가상계좌는 결제불가합니다." },
+	PAYMENT_SUCCESS: { isSuccess: true, code: 2214, message: "결제 성공하였습니다." },
+	PAYMENT_ERROR_TYPE: { isSuccess: false, code: 2215, message: "위조된 결제 시도입니다." },
+	PAYMENT_FAIL: { isSuccess: false, code: 2216, message: "결제에 실패하였습니다." },
+	POINT_INFO_NOT_MATCH: { isSuccess: false, code: 2216, message: "포인트 정보가 잘못되었습니다." },
+	SECRET_KEY_ERROR: { isSuccess: false, cod: 2216, message: "포인트 부여 키가 틀렸습니다. 관리자에게 문의" },
+	SHIP_PARAM_EMPTY: { isSuccess: false, code: 2212, message: "배송과 관련된 매개변수가 누락되었습니다." },
+
+	// 상품 value check
+	PRODUCT_EMPTY: { isSucess: false, code: 2216, message: "상품 인덱스를 입력해주세요." },
+	PRODUCT_NOT_EXIST: { isSucess: false, code: 2217, message: "상품 인덱스가 존재하지 않습니다." },
+	PRODUCT_ERROR: { isSuccess: false, code: 2218, message: "해당 인덱스는 상품이 아닙니다." },
+	PRODUCTLIST_EMPTY: { isSuccess: false, code: 2224, message: "상품 리스트를 입력해주세요." },
+	PRODUCT_EXIST: {
+		isSuccess: false,
+		code: 2225,
+		message: "이미 구매한 상품에 해당 상품이 있거나 해당 상품이 포함된 패키지가 존재합니다.",
+	},
+
+	PAY_POINT_ERROR: { isSuccess: false, code: 2219, message: "사용하려는 포인트가 사용자의 보유 포인트를 초과합니다." },
+	PAY_PRICE_ERROR: { isSuccess: false, code: 2220, message: "최종 가격이 올바르지 않습니다." },
+
+	IMP_UID_EMPTY: { isSuccess: false, code: 2221, message: "아임포트 uid가 존재하지 않습니다." },
+	ORDERNUM_EMPTY: { isSuccess: false, code: 2222, message: "주문번호가 존재하지 않습니다." },
+	REFUND_REASON_EMPTY: { isSuccess: false, code: 2223, message: "환불 사유를 입력해주세요." },
+	PAYMENT_NOT_EXIST: { isSuccess: false, code: 2223, message: "해당 결제 건은 존재하지 않습니다." },
+	PAYMENT_PRODUCT_EMPTY: {
+		isSuccess: false,
+		code: 2224,
+		message: "해당 결제와 관련된 상품이 존재하지 않거나 구매할 수 없습니다.",
+	},
+	PAYMENT_STATUS_ERROR: { isSuccess: false, code: 2231, message: "환불된 건에 대한 결제 시도입니다." },
+
+	PAYMENT_NOT_COMPLETED: {
+		isSuccess: false,
+		code: 2223,
+		message: "결제 처리가 완료되지 않았습니다.",
+	},
+	// 유저 권한 value check
+	USERAUTH_NOT_EXIST: {
+		isSuccess: false,
+		code: 2224,
+		message: "해당 회차는 유료 회차 상품입니다. 해당 서비스를 이용하기 위해서는 상품을 구매해 주세요",
+	},
+	REVIEWNOTE_NOT_VALID: {
+		isSuccess: false,
+		code: 2224,
+		message: "해당 오답노트는에 대한 권한이 더 이상 존재하지 않습니다",
+	},
+
+	REFUND_SUCCESS: { isSuccess: true, code: 2225, message: "환불완료 (Webhook)" },
+
+	REFUND_PRICE_EMPTY: { isSuccess: false, code: 2225, message: "환불 금액을 입력해주세요" },
+	REFUND_CHECKSUM_ERROR: {
+		isSuccess: false,
+		code: 2228,
+		message: "환불 요청 금액이 환불 가능 금액을 초과하였습니다.",
+	},
+	REFUND_TOTAL_ERROR: { isSuccess: false, code: 2229, message: "이미 모든 상품이 전액 환불되었습니다" },
+	REFUND_IMPORT_ERROR: {
+		isSuccess: false,
+		code: 2230,
+		message: "DB 데이터 동기화가 되지 않았습니다. 아임포트 관리자에서의 환불 처리는 전액 환불만 가능합니다.",
+	},
+	REFUND_PRICE_ERROR: {
+		isSuccess: false,
+		code: 2231,
+		message: "환불 요청 금액과 환불되어야 하는 금액이 맞지 않습니다.",
+	},
+	IMPORT_TOKEN_ERROR: { isSuccess: false, code: 2226, message: "아임포트 토큰 발급 에러" },
+	IMPORT_TOKEN_EMPTY: { isSuccess: false, code: 2227, message: "아임포트 토큰이 존재하지 않습니다" },
+	IMPORT_AXIOS_ERROR: { isSuccess: false, code: 2228, message: "아임포트 결제정보 요청 에러" },
+
+	// 공기업 value check
+	PUBLICCOMPANY_NOT_EXIST: { isSuccess: false, code: 2228, message: " 존재하지 않는 공기업입니다. " },
+	PUBLICCOMPANY_ERROR: { isSuccess: false, code: 2229, message: "해당 인덱스는 공기업이 아닙니다." },
+
+	// 장바구니
+	CART_EXIST: { isSuccess: false, code: 2232, message: "이미 장바구니에 추가된 상품입니다." },
+	//Connection, Transaction 등의 서버 오류
+	DEFAULT_DIVISION_ERROR: { isSuccess: false, code: 2228, message: "회사 구분 디폴트가 유효하지 않습니다." },
+	COMPANYIDX_EMPTY: { isSuccess: false, code: 2229, message: "회사 인덱스를 입력해주세요." },
+	COMPANY_TYPE_EMPTY: { isSuccess: false, code: 2230, message: "기업 타입을 입력해주세요." },
+	COMPANY_EXAM_EMPTY: { isSuccess: false, code: 2230, message: "기업 이름을 입력해주세요." },
+	COMPANY_EXAMFIELD_EMPTY: { isSuccess: false, code: 2231, message: "시험 필드를 입력해주세요." },
+	COMPANY_EXAMTYPE_EMPTY: { isSuccess: false, code: 2232, message: "시험 타입을 입력해주세요." },
+	COMPANY_EXAMTYPE_NOT_MATCH: { isSuccess: false, code: 2233, message: "기업 중분류가 대분류에 속하지 않습니다." },
+	COMPANY_EXAMFIELD_NOT_MATCH: { isSuccess: false, code: 2234, message: "기업 소분류가 중분류에 속하지 않습니다." },
+	COMPANY_EXAMFIELD_NOT_EXIST: {
+		isSuccess: false,
+		code: 2234,
+		message: "기업 시험 영역(기업 소분류)을 입력되지 않았습니다.",
+	},
+	COMPANY_ALREADY_EXIST: { isSuccess: false, code: 2234, message: "해당 기업이름이 이미 존재합니다." },
+	COMPANY_NAME_EMPTY: { isSuccess: false, code: 2235, message: "회사 시험 이름을 입력해주세요." },
+	COMPANY_EXAMIDX_NOT_MATCH: { isSuccess: false, code: 2236, message: "회사 시험이 소분류에 속하지 않습니다." },
+	COMPANY_NAME_NOT_MATCH: {
+		isSuccess: false,
+		code: 2237,
+		message: "기업 이름과 시험 필드 인덱스가 일치하지 않습니다.",
+	},
+	DB_ERROR: { isSuccess: false, code: 4000, message: "데이터 베이스 에러" },
+	SERVER_ERROR: { isSuccess: false, code: 4001, message: "서버 에러" },
+	SENS_ERROR: { isSuccess: false, code: 4002, message: " 문자 발송에 실패하였습니다. " },
+	BOARD_NOT_FOUND: { isSuccess: false, code: 4003, message: "게시글이 존재하지 않습니다." },
+	KAKAO_PHONENUM_EMPTY: {
+		isSuccess: false,
+		code: 5000,
+		message: "카카오계정에 등록된 휴대폰 번호가 존재하지 않습니다. 카카오 계정을 확인해주세요.",
+	},
+	KAKAO_USERNAME_EMPTY: {
+		isSuccess: false,
+		code: 5001,
+		message: "카카오계정에 등록된 사용자 이름이 존재하지 않습니다. 카카오 계정을 확인해주세요.",
+	},
+	KAKAO_EMAIL_EMPTY: {
+		isSuccess: false,
+		code: 5002,
+		message: "카카오계정에 등록된 이메일을 확인할 수 없습니다. 카카오 계정을 확인해주세요.",
+	},
+	// TODO : fix code
+	DURATION_PARAM_INCORRECT: {
+		isSuccess: false,
+		code: 5004,
+		message: "기간이 잘못 입력되었습니다.",
+	},
+	USER_AUTH_NOT_EXIST: {
+		isSuccess: false,
+		code: 5005,
+		message: "회원의 상품에 대한 권한이 존재하지 않습니다.",
+	},
+	SUSPENSION_ACCOUNT: {
+		isSuccess: false,
+		code: 5006,
+		message: "관리자에 의해 정지된 회원입니다",
+	},
+	EMPTY_USERAUTHIDX: {
+		isSuccess: false,
+		code: 5007,
+		message: "회수하고자 하는 행을 선택해주세요.",
+	},
+	SECRET_PRODUCTKEY_ERROR: { isSuccess: false, cod: 5007, message: "상품 권한 부여 키가 틀렸습니다. 관리자에게 문의" },
+	USER_LIST_EMPTY: { isSuccess: false, code: 5008, message: "유저를 선택해주세요." },
+	EMPTY_PRODUCTIDX: { isSuccess: false, code: 5009, message: "권한을 부여할 상품을 선택해주세요." },
+	EMPTY_EXPIREAT: { isSuccess: false, cpde: 5010, message: "상품 권한 유효기간을 설정해주세요." },
+	PAYMENT_ALREADY_DONE: { isSuccess: false, code: 6000, message: "이미 결제가 완료된 상품입니다." },
+	EXAM_SORT_NOT_EXISTS: { isSuccess: false, code: 7000, message: "해당 시험 유형이 존재하지 않습니다." },
+	NCS_NOT_MATCH_POSITION: {
+		isSuccess: false,
+		code: 10000,
+		message: "직렬 기계직과 세트문항 선택은 동시에 할 수 없습니다.",
+	},
+};
diff --git a/_old/config/cache.js b/_old/config/cache.js
new file mode 100644
index 0000000..2478589
--- /dev/null
+++ b/_old/config/cache.js
@@ -0,0 +1,15 @@
+const LRU = require("lru-cache");
+
+const options = {
+	max: 300,
+	maxAge: 1000 * 60 * 60,
+	length(n, key) {
+		return 1;
+	},
+	dispose(key, n) {
+		/* 데이터가 삭제된 후 호출 */
+	},
+};
+// 추후 번호 인증외에 캐싱을 사용하면 maxAge변경
+const cache = new LRU(options);
+module.exports = cache;
diff --git a/_old/config/constant.js b/_old/config/constant.js
new file mode 100644
index 0000000..94e3f33
--- /dev/null
+++ b/_old/config/constant.js
@@ -0,0 +1,11 @@
+module.exports = {
+	DEFAULT_COMPANY_NAME: "공기업",
+	DEFAULT_CERTIFICATE_NAME: "자격증",
+	DEFAULT_EXAMSORT_ALL: "전체",
+	DEFAULT_DIVISION_NAME: "전기",
+	DEFAULT_ROOT_LEVEL: "L",
+	DEFAULT_DIVISION_LEVEL: "M",
+	POINT_TYPE_ADD: "A",
+	POINT_TYPE_USE: "U",
+	POINT_TYPE_CANCEL: "C",
+};
diff --git a/_old/config/database.js b/_old/config/database.js
new file mode 100644
index 0000000..b63400d
--- /dev/null
+++ b/_old/config/database.js
@@ -0,0 +1,14 @@
+const mysql = require("mysql2/promise");
+
+const pool = mysql.createPool({
+	host: process.env.DATABASE_HOST,
+	port: process.env.DATABASE_PORT,
+	user: process.env.DATABASE_USER,
+	database: process.env.DATABASE_NAME,
+	password: process.env.DATABASE_PASSWORD,
+	connectionLimit: 15,
+});
+
+module.exports = {
+	pool: pool,
+};
diff --git a/_old/config/express.js b/_old/config/express.js
new file mode 100644
index 0000000..59a5898
--- /dev/null
+++ b/_old/config/express.js
@@ -0,0 +1,50 @@
+const express = require("express");
+const compression = require("compression");
+const methodOverride = require("method-override");
+const cors = require("cors");
+const swaggerDoc = require("./swagger.json");
+const swaggerUi = require("swagger-ui-express");
+const cookieParser = require("cookie-parser");
+const redis = require("redis");
+
+const secret_config = require("./secret");
+
+const dotenv = require("dotenv");
+dotenv.config();
+
+module.exports = function () {
+	const app = express();
+	app.use(express.json());
+
+	app.use(express.urlencoded({ extended: true }));
+
+	app.use(methodOverride());
+
+	app.use(cors());
+
+	app.use(cookieParser());
+
+	app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDoc, { explorer: true }));
+
+	/* App (Android, iOS) */
+	// 도메인을 추가할 경우 이곳에 Route를 추가하세요.
+
+	const examRouter = require("../src/Exam/examRoute")(app);
+	const userRouter = require("../src/User/userRoute")(app);
+	const adminRouter = require("../src/Admin/adminRoute")(app);
+	const calendarRouter = require("../src/Calendar/calendarRoute")(app);
+	const restoreRouter = require("../src/Restore/restoreRoute")(app);
+	const storeRouter = require("../src/Store/storeRoute")(app);
+	const communityRouter = require("../src/Community/communityRoute")(app);
+	const channelTalkRouter = require("../src/ChannelTalk/channelTalkRoute")(app);
+
+	// 로드 밸런서를 통해서 어떤 ip 그룹으로 가게 되는지
+	app.get("/elbIpAddress", function (req, res) {
+		const ipAddress = req.socket.remoteAddress;
+		res.send(ipAddress);
+	});
+
+	//app.use("/api/exams", examRouter);
+	//require("./crawling")(app);
+	return app;
+};
diff --git a/_old/config/fcmSecret.json b/_old/config/fcmSecret.json
new file mode 100644
index 0000000..1cd30f5
--- /dev/null
+++ b/_old/config/fcmSecret.json
@@ -0,0 +1,12 @@
+{
+	"type": "service_account",
+	"project_id": "engineeo-323305",
+	"private_key_id": "4751fdf5f63d5154997d080c42713aea0b3b907b",
+	"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8dH7LIY4iQKsa\ns/dGOxR5J0cdP3OLjlCH65jhfFn+RYn19+aS23zZzLrGAmdPcsFFOlzFfJmjeB5+\nyavFJkCnCPS4G/T8PtfoFQZurm15V9d3cSCln6TBYi/6aKn0Ig8hpx+bePCaiwtz\nWB3PhNl5hjwwVYaIG2RIyOb3aORNfDA71deJ3LDNXMDeV6qtEJ78xfLRsu26tA5o\nGAoAJ+snMSPwz1g+AS5KdflfFSJQnYcIMBNCcp7kO04tYr2VclGXxM4qJseV04ho\n3IC2W+5Yb8WL7tF/59mrrD3nnyXr1jz6rWhWEYUb0zGoYeT/IjEAUfRl8tZp9Tkd\nmgGiU1MvAgMBAAECggEACIuqM23df+iGZo4u/ux8HJS1lDolwPgYGltXMf2TwYSU\nh/NqbWuUhfY5qSbzJlaIZ91vCk/nHtMgluE9CV79r+70O33RDLf3NGfxRCDJ7nNK\nqvjierNgvJnGN5WtanIGiG3t1smTwRff0gz2qEu+5m4G2eFwjOJCshlu5I/8e+Lu\ncjXB+cavSrYnhVgw3uDZ60fjqQABW+Yyd/Lfg5JZkY4pjsrm0ULaMcly8QUqaO2v\nGUxARRPgOoMs/R6kGlD507mDJitDNzkFWg67HuyflvjKQJht1eZMbYmXB+2j0Wyf\nEWfvTF1BZR98uv/KRz6F5o+JjrSLhSaBCpvO0s+BuQKBgQDcLqNn3JajNUKdW2bx\nm1NBRU3ZWDfeT2FJyj8jZ5X7eIuhN1cYae6McBN6aN2nTeXFsMEwDezAuq9u8cAv\nyedvRu4hh/F1PSzct6VIkpoL+B4U6jjBGiIs2+deR0AMoQ8uv49n4U/5KUrQn3+E\nD831r2L6GrK9w4agGsCJkhXmAwKBgQDbHJlceP8vmo/TGNY9whTRmimca+qIbIzi\nB87rhP4r6+9GGdMCQazbptU+pDMEBY1sFCR624qzLYsE4vCC7XEQL9na9FGhLVXN\nPzCJVVibjMDhoZD8Q3DzciFF+LJM50vPtLduTBVQ0hWLs9nGX77hr4hWMA4HGmm5\nAx7zAVfcZQKBgQCQSnADbGe7ZPfWr0NzKdsRdx1hRZK3OUYEWWmvBf8f4QuABwgo\nk6MknX3mRjqbnUzqlL7FgxqX4r988SiFKMdmARGlH7V4ozJ3IlJKkOgq19q+0g+H\nXFzxENs6yW6L2DBe7mJILTt/iTPiK+Qg40qRqvIt/LkN6siau21ZukZniQKBgHZ9\nq83fPRkQO7EL4Gf7eeiztsb5cRafpTxivpqNDCrSZ04AjyTMQV9zhCRhBZmqKwWE\niwnJieNFwMU+uJQFNbxsNCL9NtuzuY/KGURbXBSadIBQMCjLi9yLHsVJLZr7Bfto\nHj5OdMSFJ9Opyh5GDuLbWx5I1H0w4nWD44vEcTAlAoGAP9vNzY1VpsTKcEFhtTIK\nIJZCrLm0bVtgl+YWixvPrLP98chGmHVoSEvVg0o0viecHw7SAel2nEjqZ8u2lwan\nfi/d7iOnRPv22m6X9F844Hm+2diy842lWhn35EwrZNdsdcoC27MWTRu9NzkI383K\n6uXbIfX8BkIJfDNFEzVXXyY=\n-----END PRIVATE KEY-----\n",
+	"client_email": "firebase-adminsdk-smnmw@engineeo-323305.iam.gserviceaccount.com",
+	"client_id": "112719187215767417193",
+	"auth_uri": "https://accounts.google.com/o/oauth2/auth",
+	"token_uri": "https://oauth2.googleapis.com/token",
+	"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
+	"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-smnmw%40engineeo-323305.iam.gserviceaccount.com"
+}
diff --git a/_old/config/imPortToken.js b/_old/config/imPortToken.js
new file mode 100644
index 0000000..f75e4e4
--- /dev/null
+++ b/_old/config/imPortToken.js
@@ -0,0 +1,27 @@
+const secret = require("./secret");
+const { basickResponse } = require("./response");
+const baseResponse = require("./baseResponseStatus");
+const axios = require("axios").default;
+
+const getImPortToken = async (req, res, next) => {
+	// 액세스 토큰(access token) 발급 받기
+	try {
+		const getToken = await axios({
+			url: "https://api.iamport.kr/users/getToken",
+			method: "post", // POST method
+			headers: { "Content-Type": "application/json" }, // "Content-Type": "application/json"
+			data: {
+				imp_key: secret.importApiKey, // REST API 키
+				imp_secret: secret.importApiSecret, // REST API Secret
+			},
+		});
+		req.imPortToken = getToken.data.response; // 인증 토큰
+		next();
+	} catch (error) {
+		console.log(error);
+		logger.error(`App - getImPort Middleware error\n: ${error.message}`);
+		return res.send(basickResponse(baseResponse.IMPORT_TOKEN_ERROR));
+	}
+};
+
+module.exports = getImPortToken;
diff --git a/_old/config/jwt.js b/_old/config/jwt.js
new file mode 100644
index 0000000..dc55c00
--- /dev/null
+++ b/_old/config/jwt.js
@@ -0,0 +1,42 @@
+const jwt = require("jsonwebtoken");
+const secret_config = require("./secret");
+const { resultResponse } = require("./response");
+const { basickResponse } = require("./response");
+const baseResponse = require("./baseResponseStatus");
+const crypto = require("crypto");
+
+const jwtMiddleware = (req, res, next) => {
+	// read the token from header or url
+	const token = req.headers["x-access-token"] || req.query.token;
+	// token does not exist
+	if (!token) {
+		return res.send(basickResponse(baseResponse.TOKEN_EMPTY));
+	}
+
+	// create a promise that decodes the token
+	const p = new Promise((resolve, reject) => {
+		jwt.verify(token, secret_config.jwtsecret, (err, verifiedToken) => {
+			if (err) reject(err);
+			resolve(verifiedToken);
+		});
+	});
+
+	// if it has failed to verify, it will return an error message
+	const onError = (error) => {
+		return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+	};
+	// process the promise
+	p.then((verifiedToken) => {
+		//비밀 번호 바뀌었을 때 검증 부분 추가 할 곳
+		req.verifiedToken = verifiedToken;
+		const memberId = String(req.verifiedToken.userIdx);
+		const memberHash = crypto
+			.createHmac("sha256", Buffer.from(secret_config.chatSecretKey, "hex"))
+			.update(memberId)
+			.digest("hex");
+		req.memberHash = memberHash;
+		next();
+	}).catch(onError);
+};
+
+module.exports = jwtMiddleware;
diff --git a/_old/config/response.js b/_old/config/response.js
new file mode 100644
index 0000000..e9e8d4e
--- /dev/null
+++ b/_old/config/response.js
@@ -0,0 +1,18 @@
+const resultResponse = ({ isSuccess, code, message }, result) => {
+	return {
+		isSuccess: isSuccess,
+		code: code,
+		message: message,
+		result: result,
+	};
+};
+
+const basickResponse = ({ isSuccess, code, message }) => {
+	return {
+		isSuccess: isSuccess,
+		code: code,
+		message: message,
+	};
+};
+
+module.exports = { resultResponse, basickResponse };
diff --git a/_old/config/s3.js b/_old/config/s3.js
new file mode 100644
index 0000000..15f8fbf
--- /dev/null
+++ b/_old/config/s3.js
@@ -0,0 +1,38 @@
+const secret = require("./secret");
+//utils/s3.js
+const AWS = require("aws-sdk");
+
+const multer = require("multer");
+const multerS3 = require("multer-s3");
+
+const path = require("path");
+
+const AWS_config_region = secret.awsConfigRegion;
+const AWS_IDENTITYPOOLID = secret.identityPoolIdx;
+const accessKey = secret.imageAccessKey;
+const secretKey = secret.imageSecretAccessKey;
+const bucket = secret.bucket;
+
+const s3 = new AWS.S3({
+	accessKeyId: accessKey,
+	secretAccessKey: secretKey,
+	region: AWS_config_region,
+});
+
+const upload = multer({
+	storage: multerS3({
+		s3: s3,
+		bucket: bucket,
+		contentType: multerS3.AUTO_CONTENT_TYPE, // 자동으로 콘텐츠 타입 세팅
+		acl: "public-read",
+		key: (req, file, cb) => {
+			//let extension = path.extname(file.originalname);
+			cb(null, file.originalname);
+		},
+	}),
+});
+
+module.exports = {
+	upload,
+	s3,
+};
diff --git a/_old/config/swagger.json b/_old/config/swagger.json
new file mode 100644
index 0000000..3c8bbda
--- /dev/null
+++ b/_old/config/swagger.json
@@ -0,0 +1,16625 @@
+{
+	"swagger": "2.0",
+	"info": {
+		"version": "2.0.0",
+		"title": "Engineeo API Doc",
+		"description": "Engineeo Open API Docs with Swagger",
+		"license": {
+			"name": "MIT"
+		}
+	},
+	"tags": [
+		{
+			"name": "User",
+			"description": "API for User"
+		},
+		{
+			"name": "Exam",
+			"description": "API for Exam"
+		},
+		{
+			"name": "Community",
+			"description": "API for Community"
+		},
+		{
+			"name": "Admin",
+			"description": "API for Admin"
+		},
+		{
+			"name": "Restore",
+			"description": "API for Restore"
+		},
+		{
+			"name": "Store",
+			"description": "API for Store"
+		}
+	],
+	"schemes": ["http", "https"],
+	"consumes": ["application/json"],
+	"produces": ["application/json"],
+	"paths": {
+		"/users/sign-up": {
+			"post": {
+				"tags": ["User"],
+				"summary": "회원가입",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "회원가입 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["email", "password", "phoneNum", "userName", "dateOfBirth", "nickname", "recommendNickname"],
+							"properties": {
+								"email": {
+									"type": "string",
+									"description": "이메일"
+								},
+								"password": {
+									"type": "string",
+									"description": "비밀번호(영문 특수문자포함 8자리)"
+								},
+								"phoneNum": {
+									"type": "integer",
+									"description": "휴대폰 번호"
+								},
+								"userName": {
+									"type": "string",
+									"description": "유저 이름"
+								},
+								"dateOfBirth": {
+									"type": "integer",
+									"description": "생년월일 (8자리)"
+								},
+								"nickname": {
+									"type": "string",
+									"description": "닉네임 (8자이하)"
+								},
+								"recommendNickname": {
+									"type": "string",
+									"description": "추천인 닉네임"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1100": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1100
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메시지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"userIdx": {
+											"type": "integer",
+											"description": "User의 DB Index"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/emailAuth": {
+			"post": {
+				"tags": ["User"],
+				"summary": "이메일 확인",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "이메일 확인 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["email"],
+							"properties": {
+								"email": {
+									"type": "string",
+									"description": "이메일"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1101": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1101
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "사용 가능한 이메일입니다."
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/nicknameAuth": {
+			"post": {
+				"tags": ["User"],
+				"summary": "닉네임 확인",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "닉네임 확인 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["nickname"],
+							"properties": {
+								"nickname": {
+									"type": "string",
+									"description": "닉네임"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "string",
+									"description": "응답 메세지",
+									"example": "사용 가능한 닉네임입니다."
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/auth": {
+			"post": {
+				"tags": ["User"],
+				"summary": "휴대폰 본인 인증요청",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "휴대폰 본인 인증요청 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["phoneNum"],
+							"properties": {
+								"phoneNum": {
+									"type": "integer",
+									"description": "휴대폰 번호 11자리",
+									"example": "01012345678"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"cacheKey": {
+											"type": "String",
+											"description": "캐싱키 문자열 5자리",
+											"example": "awsXx"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/auth/verify": {
+			"post": {
+				"tags": ["User"],
+				"summary": "휴대폰 본인 인증 확인",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "휴대폰 본인 인증 확인 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["cacheKey", "authNum"],
+							"properties": {
+								"cacheKey": {
+									"type": "String",
+									"description": "캐싱키 문자열 5자리",
+									"example": "awsXx"
+								},
+								"authNum": {
+									"type": "integer",
+									"description": "인증번호 6자리",
+									"example": "123456"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/sign-in": {
+			"post": {
+				"tags": ["User"],
+				"summary": "로그인",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "로그인 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["email", "password", "isKeep"],
+							"properties": {
+								"email": {
+									"type": "string",
+									"description": "이메일",
+									"example": "kyj9592@naver.com"
+								},
+								"password": {
+									"type": "string",
+									"description": "비밀번호(영문 특수문자포함 8자리)",
+									"example": "sadjk@!"
+								},
+								"isKeep": {
+									"type": "string",
+									"description": "로그인 유지 여부 (0: 로그인 미유지, 1: 로그인 유지)",
+									"example": "1"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1103": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1103
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"userIdx": {
+											"type": "integer",
+											"description": "User의 DB Index"
+										},
+										"jwt": {
+											"type": "string",
+											"description": "JWT 토큰",
+											"example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWR4IjoxLCJpYXQiOjE2MjQ5MzAxNjksImV4cCI6MTY1NjQ2NjE2OSwic3ViIjoidXNlckluZm8ifQ.2BSh_Z4"
+										},
+										"status": {
+											"type": "string",
+											"description": "유저 상태 값 (N:활성, Y:비활성, M:관리자)",
+											"example": "N"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/kakao-login": {
+			"post": {
+				"tags": ["User"],
+				"summary": "카카오 로그인 : 프론트에서 발급받은 토큰을 서버에서 인증 후 jwt 발행.",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "카카오 로그인 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["accessToken"],
+							"properties": {
+								"accessToken": {
+									"type": "string",
+									"description": "프론트에서 발급받은 카카오 accessToken.",
+									"example": "DOxA0Z5y-fQBK8TfxZO292FVht8qfFCTbZwRvgo9cuoAAAF6QOI3gA"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"userIdx": {
+											"type": "integer",
+											"description": "User의 DB Index"
+										},
+										"jwt": {
+											"type": "string",
+											"description": "JWT 토큰",
+											"example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWR4IjoxLCJpYXQiOjE2MjQ5MzAxNjksImV4cCI6MTY1NjQ2NjE2OSwic3ViIjoidXNlckluZm8ifQ.2BSh_Z4"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/kakao-signUp": {
+			"post": {
+				"tags": ["User"],
+				"summary": "카카오 회원가입 : 프론트에서 발급받은 토큰을 닉네임과 함께 다시 전송",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "카카오 회원가입 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["accessToken", "nickname", "recommendNickname"],
+							"properties": {
+								"accessToken": {
+									"type": "string",
+									"description": "프론트에서 발급받은 카카오 accessToken.",
+									"example": "DOxA0Z5y-fQBK8TfxZO292FVht8qfFCTbZwRvgo9cuoAAAF6QOI3gA"
+								},
+								"nickname": {
+									"type": "string",
+									"description": "유저로부터 입력받은 닉네임",
+									"example": "기윾니"
+								},
+								"recommendNickname": {
+									"type": "string",
+									"description": "유저로부터 입력받은 추천인 닉네임",
+									"example": "연고맨"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"userIdx": {
+											"type": "integer",
+											"description": "User의 DB Index"
+										},
+										"jwt": {
+											"type": "string",
+											"description": "JWT 토큰",
+											"example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWR4IjoxLCJpYXQiOjE2MjQ5MzAxNjksImV4cCI6MTY1NjQ2NjE2OSwic3ViIjoidXNlckluZm8ifQ.2BSh_Z4"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/naver-login": {
+			"post": {
+				"tags": ["User"],
+				"summary": "네이버 로그인 : 프론트에서 발급받은 토큰을 서버에서 인증 후 jwt 발행.",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "네이버 로그인 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["accessToken"],
+							"properties": {
+								"accessToken": {
+									"type": "string",
+									"description": "프론트에서 발급받은 네이버 accessToken.",
+									"example": "AAAAQosjWDJieBiQZc3to9YQp6HDLvrmyKC+6+iZ3gq7qrkqf50ljZC+Lgoqrg"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"userIdx": {
+											"type": "integer",
+											"description": "User의 DB Index"
+										},
+										"jwt": {
+											"type": "string",
+											"description": "JWT 토큰",
+											"example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWR4IjoxLCJpYXQiOjE2MjQ5MzAxNjksImV4cCI6MTY1NjQ2NjE2OSwic3ViIjoidXNlckluZm8ifQ.2BSh_Z4"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/naver-signUp": {
+			"post": {
+				"tags": ["User"],
+				"summary": "네이버 회원가입 : 프론트에서 발급받은 토큰을 닉네임과 함께 다시 전송",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "네이버 회원가입 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["accessToken", "nickname", "recommendNickname"],
+							"properties": {
+								"accessToken": {
+									"type": "string",
+									"description": "프론트에서 발급받은 네이버 accessToken.",
+									"example": "AAAAQosjWDJieBiQZc3to9YQp6HDLvrmyKC+6+iZ3gq7qrkqf50ljZC+Lgoqrg"
+								},
+								"nickname": {
+									"type": "string",
+									"description": "유저로부터 입력받은 닉네임",
+									"example": "기윾니"
+								},
+								"recommendNickname": {
+									"type": "string",
+									"description": "유저로부터 입력받은 추천인 닉네임",
+									"example": "연고맨"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"userIdx": {
+											"type": "integer",
+											"description": "User의 DB Index"
+										},
+										"jwt": {
+											"type": "string",
+											"description": "JWT 토큰",
+											"example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWR4IjoxLCJpYXQiOjE2MjQ5MzAxNjksImV4cCI6MTY1NjQ2NjE2OSwic3ViIjoidXNlckluZm8ifQ.2BSh_Z4"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/google-login": {
+			"post": {
+				"tags": ["User"],
+				"summary": "구글 로그인 : 프론트에서 발급받은 토큰을 서버에서 인증 후 jwt 발행.",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "구글 로그인 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["accessToken"],
+							"properties": {
+								"accessToken": {
+									"type": "string",
+									"description": "프론트에서 발급받은 구글 accessToken.",
+									"example": "DOxA0Z5y-fQBK8TfxZO292FVht8qfFCTbZwRvgo9cuoAAAF6QOI3gA"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"userIdx": {
+											"type": "integer",
+											"description": "User의 DB Index"
+										},
+										"jwt": {
+											"type": "string",
+											"description": "JWT 토큰",
+											"example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWR4IjoxLCJpYXQiOjE2MjQ5MzAxNjksImV4cCI6MTY1NjQ2NjE2OSwic3ViIjoidXNlckluZm8ifQ.2BSh_Z4"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/profile": {
+			"post": {
+				"tags": ["User"],
+				"summary": "회원정보 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "회원정보 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "회원정보 조회 Path Variable : user 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "회원정보 조회 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["userPassword"],
+							"properties": {
+								"userPassword": {
+									"type": "string",
+									"description": "비밀번호",
+									"example": "dssa@@ss"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1107": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1107
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "바말번호가 일치합니다."
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"name": {
+											"type": "string",
+											"description": "회원 이름",
+											"example": "김승은"
+										},
+										"email": {
+											"type": "string",
+											"description": "회원 이메일",
+											"example": "duliesoures@gmail.com"
+										},
+										"phoneNum": {
+											"type": "integer",
+											"description": "회원 휴대폰 번호",
+											"example": "01056735172"
+										},
+										"dateOfBirth": {
+											"type": "integer",
+											"description": "회원 생일",
+											"example": "2000.05.05"
+										},
+										"nickname": {
+											"type": "string",
+											"description": "회원 닉네임",
+											"example": "집에가고싶어"
+										},
+										"age": {
+											"type": "integer",
+											"description": "회원 나이",
+											"example": 26
+										},
+										"createdAt": {
+											"type": "string",
+											"description": "가입일",
+											"example": "2021.07.05"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			},
+			"patch": {
+				"tags": ["User"],
+				"summary": "회원정보 수정",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "회원정보 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "회원정보 수정 Path Variable : user 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "회원정보 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["userPassword"],
+							"properties": {
+								"userPassword": {
+									"type": "string",
+									"description": "비밀번호",
+									"example": "dssa@@ss"
+								},
+								"userNickname": {
+									"type": "string",
+									"description": "8자리 이내 닉네임"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1110": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1110
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "회원정보가 변경되었습니다."
+								}
+							}
+						}
+					}
+				}
+			},
+			"get": {
+				"tags": ["User"],
+				"summary": "회원정보 조회 (비밀번호 없이)",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "회원정보 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "회원정보 조회 Path Variable : user 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"memberId": {
+											"type": "string",
+											"description": "멤버 아이디(유저 인덱스)"
+										},
+										"name": {
+											"type": "string",
+											"description": "회원 이름",
+											"example": "김승은"
+										},
+										"email": {
+											"type": "string",
+											"description": "회원 이메일",
+											"example": "duliesoures@gmail.com"
+										},
+										"phoneNum": {
+											"type": "integer",
+											"description": "회원 휴대폰 번호",
+											"example": "01056735172"
+										},
+										"dateOfBirth": {
+											"type": "integer",
+											"description": "회원 생일",
+											"example": 20000505
+										},
+										"nickname": {
+											"type": "string",
+											"description": "회원 닉네임",
+											"example": "와우바우"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/social": {
+			"get": {
+				"tags": ["User"],
+				"summary": "소셜 계정 확인",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "소셜 계정 확인 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "소셜 계정 확인 Path Variable : user 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"isSocial": {
+											"type": "integer",
+											"description": "소셜 계정 여부",
+											"example": 1
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/withdrawal": {
+			"patch": {
+				"tags": ["User"],
+				"summary": "회원탈퇴",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "회원탈퇴 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "회원탈퇴 Path Variable : user 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1111": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1111
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "회원탈퇴에 성공하였습니다."
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/find/email": {
+			"post": {
+				"tags": ["User"],
+				"summary": "이메일 찾기",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "이메일 찾기 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["name", "phoneNum"],
+							"properties": {
+								"name": {
+									"type": "string",
+									"description": "이름",
+									"example": "김승은"
+								},
+								"phoneNum": {
+									"type": "integer",
+									"description": "휴대폰 번호",
+									"example": "01056735172"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "string",
+									"description": "이메일",
+									"example": "duliesoures@gmail.com"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/find/password": {
+			"post": {
+				"tags": ["User"],
+				"summary": "비밀번호 찾기",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "비밀번호 찾기 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["email", "phoneNum"],
+							"properties": {
+								"email": {
+									"type": "string",
+									"description": "이메일",
+									"example": "duliesoures@gmail.com"
+								},
+								"phoneNum": {
+									"type": "integer",
+									"description": "휴대폰 번호",
+									"example": "01056735172"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1106": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1106
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "해당 핸드폰 번호로 임시 비밀번호를 발송하였습니다."
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/exams": {
+			"get": {
+				"tags": ["Exam"],
+				"summary": "시험 조회 : examSort와 examSubName을 통해 시험의 항목을 조회한다",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "examSort",
+						"description": "시험 대분류 (공기업, 자격증, 편입)",
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "examSubName",
+						"description": "시험 중분류 (공기업, 공사기사, 산업기사, 기능사, 기사, 편입수학)",
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examIdx": {
+												"type": "integer",
+												"description": "시험 인덱스"
+											},
+											"examName": {
+												"type": "string",
+												"description": "시험 이름"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/exams/{examIdx}/examInfo": {
+			"get": {
+				"tags": ["Exam"],
+				"summary": "시험 정보 조회 : 시험의 정보와 규칙을 알려준다.",
+				"parameters": [
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "시험 정보 조회 Path Variable : Exam Index",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examIdx": {
+											"type": "integer",
+											"description": "시험 인덱스",
+											"example": 5
+										},
+										"examName": {
+											"type": "string",
+											"description": "시험 이름",
+											"example": "한국전력공사(고졸-전기직)"
+										},
+										"passScore": {
+											"type": "integer",
+											"description": "시험 합격 점수",
+											"example": 70
+										},
+										"problemCount": {
+											"type": "integer",
+											"description": "시험 문제 개수",
+											"example": 100
+										},
+										"timeLimit": {
+											"type": "integer",
+											"description": "시험 제한 시간",
+											"example": 250
+										},
+										"examUrl": {
+											"type": "String",
+											"description": "시험 url",
+											"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+										},
+										"subjectList": {
+											"type": "array",
+											"description": "과목 리스트",
+											"items": {
+												"type": "object",
+												"properties": {
+													"examSubjectIdx": {
+														"type": "integer",
+														"description": "과목 인덱스",
+														"example": 1
+													},
+													"examSubjectName": {
+														"type": "string",
+														"description": "과목 이름",
+														"example": "전기기기"
+													},
+													"examSubjectNum": {
+														"type": "integer",
+														"description": "과목 번호",
+														"example": 1
+													},
+													"passScore": {
+														"type": "integer",
+														"description": "합격 점수",
+														"example": 40
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/exams/{examIdx}/examDetail": {
+			"get": {
+				"tags": ["Exam"],
+				"summary": "기출문제 시험 회차 조회",
+				"parameters": [
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "기출문제 시험 회차 조회 Path Variable : Exam Index",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examDetailIdx": {
+												"type": "integer",
+												"description": "시험 상세 인덱스"
+											},
+											"date": {
+												"type": "string",
+												"description": "시험 날짜"
+											},
+											"examRound": {
+												"type": "integer",
+												"description": "시험 회차"
+											},
+											"isPublic": {
+												"type": "integer",
+												"description": "시험 회차 ( 0 : 비공개, 1 : 공개 ) "
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/exams/{examIdx}/examDetail/{examDetailIdx}/examSubject": {
+			"get": {
+				"tags": ["Exam"],
+				"summary": "과목 상세 조회",
+				"parameters": [
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "과목 상세 조회 Path Variable : Exam Index",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examDetailIdx",
+						"description": "과목 상세 조회 Path Variable : Exam Detail Index",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examSubjectIdx": {
+												"type": "integer",
+												"description": "시험 과목 인덱스",
+												"example": 1
+											},
+											"subjectNum": {
+												"type": "string",
+												"description": "과목 번호",
+												"example": "1과목"
+											},
+											"problemCount": {
+												"type": "string",
+												"description": "문항 수",
+												"example": "20문항"
+											},
+											"examSubjectName": {
+												"type": "string",
+												"description": "과목 이름",
+												"example": "전기자기학"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/exams/{multipleProblemIdx}": {
+			"get": {
+				"tags": ["Exam"],
+				"summary": "CBT 시험 한 문제 조회",
+				"parameters": [
+					{
+						"in": "path",
+						"name": "multipleProblemIdx",
+						"description": "CBT 시험 문제 조회 Path Variable : multipleProblemIdx Index",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examInfo": {
+											"type": "object",
+											"properties": {
+												"examDetailIdx": {
+													"type": "integer",
+													"description": "시험 회차(디테일) 인덱스",
+													"example": 8
+												},
+												"examIdx": {
+													"type": "integer",
+													"description": "시험 인덱스",
+													"example": 1
+												},
+												"examName": {
+													"type": "string",
+													"description": "시험 이름",
+													"example": "전기 기사"
+												},
+												"date": {
+													"type": "string",
+													"description": "시험 날짜",
+													"example": "2016.07.10"
+												},
+												"examRound": {
+													"type": "string",
+													"description": "회차",
+													"example": "1회"
+												},
+												"passScore": {
+													"type": "integer",
+													"description": "시험 합격 점수",
+													"example": 70
+												},
+												"problemCount": {
+													"type": "integer",
+													"description": "시험 문제 개수",
+													"example": 100
+												},
+												"timeLimit": {
+													"type": "integer",
+													"description": "시험 제한 시간",
+													"example": 250
+												},
+												"examUrl": {
+													"type": "String",
+													"description": "시험 url",
+													"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+												},
+												"examDetailUrl": {
+													"type": "String",
+													"description": "시험 회차 url",
+													"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+												},
+												"questionCount": {
+													"type": "integer",
+													"description": "문제 선지 개수 ( 미사용 )",
+													"example": 4
+												},
+												"problem": {
+													"type": "array",
+													"description": "해당하는 문제",
+													"items": {
+														"type": "object",
+														"properties": {
+															"multipleProblemIdx": {
+																"type": "integer",
+																"description": "시험 문제 인덱스",
+																"example": 16
+															},
+															"categoryIdx": {
+																"type": "integer",
+																"description": "카테고리 인덱스",
+																"example": 370
+															},
+															"examSubjectIdx": {
+																"type": "integer",
+																"description": "문제 과목 인덱스",
+																"example": 1
+															},
+															"examSubjectName": {
+																"type": "integer",
+																"description": "문제 과목명",
+																"example": "전기자기학"
+															},
+															"subject": {
+																"type": "string",
+																"description": "문제 과목명(번호)",
+																"example": "제1과목 전기자기학"
+															},
+															"problemNum": {
+																"type": "integer",
+																"description": "문제 번호",
+																"example": 4
+															},
+															"problem": {
+																"type": "string",
+																"description": "문제",
+																"example": "질문입니다."
+															},
+															"answerNum": {
+																"type": "integer",
+																"description": "정답번호",
+																"example": 4
+															},
+															"questions": {
+																"type": "array",
+																"items": {
+																	"type": "object",
+																	"properties": {
+																		"questionIdx": {
+																			"type": "integer",
+																			"description": "선지 인덱스",
+																			"example": 11099
+																		},
+																		"questionNum": {
+																			"type": "integer",
+																			"description": "선지 번호",
+																			"example": 1
+																		},
+																		"question": {
+																			"type": "string",
+																			"description": "선지 내용",
+																			"example": "mged"
+																		},
+																		"isAnswer": {
+																			"type": "integer",
+																			"description": "정답 여부",
+																			"example": 1
+																		}
+																	}
+																}
+															},
+															"provisionNum": {
+																"type": "string",
+																"description": "설비규정",
+																"example": "112.2"
+															},
+															"solution": {
+																"type": "string",
+																"description": "해설",
+																"example": "\\lbrack 전기자기학 - 08.전류 \\rbrack \n\\\\\nI=\\dfrac{Q}{t}=\\dfrac{ne}{t},\\,\\, 전자의\\,\\, 수(n)으로\\,\\, 정리\\\\\n n=\\dfrac{It}{e}=\\dfrac{50 \\times 1}{1.602 \\times 10{-19}}=31.21 \\times 10^{19}\\\\\\,\\\\\n (e=1.602 \\times 10^{-19},\\,\\, 단위시간=1초\\,\\, 대입)"
+															},
+															"isKatex": {
+																"type": "integer",
+																"description": "카텍스 유무 (유 : 1, 무 : 0)",
+																"example": 1
+															},
+															"problemScore": {
+																"type": "integer",
+																"description": "문제 배점",
+																"example": 5
+															},
+															"lectureUrl": {
+																"type": "integer",
+																"description": "강의 URL",
+																"example": "https://www.youtube.com/embed/nQ6GUrEB3XI?start=58&end=67"
+															}
+														}
+													}
+												}
+											}
+										},
+										"subject": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"examSubjectIdx": {
+														"type": "integer",
+														"description": "시험 과목 인덱스",
+														"example": 5
+													},
+													"examSubjectNum": {
+														"type": "integer",
+														"description": "시험 과목 번호 (ex. 전기기기 1과목)",
+														"example": "1"
+													},
+													"examSubjectName": {
+														"type": "string",
+														"description": "시험 과목 이름",
+														"example": "전자기기학"
+													},
+													"passScore": {
+														"type": "integer",
+														"description": "시험 합격 점수",
+														"example": 20
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "multipleProblemIdx를 형식에 맞게 입력해주세요."
+					},
+					"2051": {
+						"description": "해당 multipleProblemIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "multipleProblemIdx를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/exams/{examIdx}/examDetail/{examDetailIdx}/problem": {
+			"post": {
+				"tags": ["Exam"],
+				"summary": "CBT 시험 문제 조회",
+				"parameters": [
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "CBT 시험 문제 조회 Path Variable : Exam Index",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examDetailIdx",
+						"description": "CBT 시험 문제 조회 Path Variable : Exam Detail Index",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "시험 과목 인덱스, 과목 인덱스를 넣을 경우 해당 과목의 문제만 조회",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["subjectList"],
+							"properties": {
+								"subjectList": {
+									"type": "array",
+									"items": {
+										"type": "integer"
+									},
+									"description": "과목 리스트"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examInfo": {
+											"type": "object",
+											"properties": {
+												"examName": {
+													"type": "string",
+													"description": "시험 이름",
+													"example": "전기 기사"
+												},
+												"date": {
+													"type": "string",
+													"description": "시험 날짜",
+													"example": "2016.07.10"
+												},
+												"examRound": {
+													"type": "string",
+													"description": "회차",
+													"example": 3
+												},
+												"passScore": {
+													"type": "integer",
+													"description": "시험 합격 점수",
+													"example": 70
+												},
+												"problemCount": {
+													"type": "integer",
+													"description": "시험 문제 개수",
+													"example": 100
+												},
+												"timeLimit": {
+													"type": "integer",
+													"description": "시험 제한 시간",
+													"example": 250
+												},
+												"examUrl": {
+													"type": "String",
+													"description": "시험 url",
+													"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+												},
+												"examDetailUrl": {
+													"type": "String",
+													"description": "시험 회차 url",
+													"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+												},
+												"questionCount": {
+													"type": "integer",
+													"description": "문제 선지 개수 ( 미사용 )",
+													"example": 4
+												}
+											},
+											"description": "시험 정보"
+										},
+										"subject": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"examSubjectIdx": {
+														"type": "integer",
+														"description": "시험 과목 인덱스",
+														"example": 5
+													},
+													"examSubjectNum": {
+														"type": "integer",
+														"description": "시험 과목 번호 (ex. 전기기기 1과목)",
+														"example": "1"
+													},
+													"examSubjectName": {
+														"type": "string",
+														"description": "시험 과목 이름",
+														"example": "전자기기학"
+													},
+													"passScore": {
+														"type": "integer",
+														"description": "시험 합격 점수",
+														"example": 20
+													},
+													"problems": {
+														"type": "array",
+														"description": "과목에 해당하는 문제 리스트",
+														"items": {
+															"type": "object",
+															"properties": {
+																"problemIdx": {
+																	"type": "integer",
+																	"description": "시험 문제 인덱스",
+																	"example": 16
+																},
+																"problemNum": {
+																	"type": "integer",
+																	"description": "문제 번호",
+																	"example": 4
+																},
+																"problem": {
+																	"type": "string",
+																	"description": "문제",
+																	"example": "질문입니다."
+																},
+																"answerNum": {
+																	"type": "integer",
+																	"description": "정답번호",
+																	"example": 4
+																},
+																"questions": {
+																	"type": "array",
+																	"items": {
+																		"type": "object",
+																		"properties": {
+																			"questionIdx": {
+																				"type": "integer",
+																				"description": "선지 인덱스",
+																				"example": 11099
+																			},
+																			"questionNum": {
+																				"type": "integer",
+																				"description": "선지 번호",
+																				"example": 1
+																			},
+																			"question": {
+																				"type": "string",
+																				"description": "선지 내용",
+																				"example": "mged"
+																			},
+																			"isAnswer": {
+																				"type": "integer",
+																				"description": "정답 여부",
+																				"example": 1
+																			}
+																		}
+																	}
+																},
+																"provisionNum": {
+																	"type": "string",
+																	"description": "설비규정",
+																	"example": "112.2"
+																},
+																"solution": {
+																	"type": "string",
+																	"description": "해설",
+																	"example": "\\lbrack 전기자기학 - 08.전류 \\rbrack \n\\\\\nI=\\dfrac{Q}{t}=\\dfrac{ne}{t},\\,\\, 전자의\\,\\, 수(n)으로\\,\\, 정리\\\\\n n=\\dfrac{It}{e}=\\dfrac{50 \\times 1}{1.602 \\times 10{-19}}=31.21 \\times 10^{19}\\\\\\,\\\\\n (e=1.602 \\times 10^{-19},\\,\\, 단위시간=1초\\,\\, 대입)"
+																},
+																"isKatex": {
+																	"type": "integer",
+																	"description": "카텍스 유무 (유 : 1, 무 : 0)",
+																	"example": 1
+																},
+																"problemScore": {
+																	"type": "integer",
+																	"description": "문제 배점",
+																	"example": 5
+																},
+																"lectureUrl": {
+																	"type": "integer",
+																	"description": "강의 URL",
+																	"example": "https://www.youtube.com/embed/nQ6GUrEB3XI?start=58&end=67"
+																}
+															}
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2053": {
+						"description": "examDetailIdx를 입력해주세요"
+					},
+					"2054": {
+						"description": "examDetailIdx를 형식에 맞게 입력해주세요"
+					},
+					"2055": {
+						"description": "해당 examDetailIdx가 존재하지 않습니다."
+					}
+				}
+			}
+		},
+		"/exams/publicCompany/{examIdx}/examDetail/{examDetailIdx}/problem": {
+			"get": {
+				"tags": ["Exam"],
+				"summary": "공기업 시험 문제 조회",
+				"parameters": [
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "공기업 시험 문제 조회 Path Variable : Exam Index",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examDetailIdx",
+						"description": "공기업 시험 문제 조회 Path Variable : Exam Detail Index",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examInfo": {
+											"type": "object",
+											"properties": {
+												"examName": {
+													"type": "string",
+													"description": "시험 이름",
+													"example": "한국전력공사(고졸-전기직)"
+												},
+												"date": {
+													"type": "string",
+													"description": "시험 날짜",
+													"example": "2016.07.10"
+												},
+												"examRound": {
+													"type": "string",
+													"description": "회차",
+													"example": "상반기"
+												},
+												"passScore": {
+													"type": "integer",
+													"description": "시험 합격 점수",
+													"example": 70
+												},
+												"problemCount": {
+													"type": "integer",
+													"description": "시험 문제 개수",
+													"example": 100
+												},
+												"timeLimit": {
+													"type": "integer",
+													"description": "시험 제한 시간",
+													"example": 250
+												},
+												"examUrl": {
+													"type": "String",
+													"description": "시험 url",
+													"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+												},
+												"examDetailUrl": {
+													"type": "String",
+													"description": "시험 회차 url",
+													"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+												}
+											},
+											"description": "시험 정보"
+										},
+										"subject": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"examSubjectIdx": {
+														"type": "integer",
+														"description": "시험 과목 인덱스",
+														"example": 5
+													},
+													"examSubjectNum": {
+														"type": "integer",
+														"description": "시험 과목 번호 (ex. 전기기기 1과목)",
+														"example": "1"
+													},
+													"examSubjectName": {
+														"type": "string",
+														"description": "시험 과목 이름",
+														"example": "전자기기학"
+													},
+													"passScore": {
+														"type": "integer",
+														"description": "시험 합격 점수",
+														"example": 20
+													},
+													"problems": {
+														"type": "array",
+														"description": "과목에 해당하는 문제 리스트",
+														"items": {
+															"type": "object",
+															"properties": {
+																"problemIdx": {
+																	"type": "integer",
+																	"description": "시험 문제 인덱스",
+																	"example": 16
+																},
+																"problemNum": {
+																	"type": "integer",
+																	"description": "문제 번호",
+																	"example": 4
+																},
+																"problem": {
+																	"type": "string",
+																	"description": "문제",
+																	"example": "질문입니다."
+																},
+																"answerNum": {
+																	"type": "integer",
+																	"description": "정답번호",
+																	"example": 4
+																},
+																"questions": {
+																	"type": "array",
+																	"items": {
+																		"type": "object",
+																		"properties": {
+																			"questionIdx": {
+																				"type": "integer",
+																				"description": "선지 인덱스",
+																				"example": 11099
+																			},
+																			"questionNum": {
+																				"type": "integer",
+																				"description": "선지 번호",
+																				"example": 1
+																			},
+																			"question": {
+																				"type": "string",
+																				"description": "선지 내용",
+																				"example": "mged"
+																			},
+																			"isAnswer": {
+																				"type": "integer",
+																				"description": "정답 여부",
+																				"example": 1
+																			}
+																		}
+																	}
+																},
+																"provisionNum": {
+																	"type": "string",
+																	"description": "설비규정",
+																	"example": "112.2"
+																},
+																"solution": {
+																	"type": "string",
+																	"description": "해설",
+																	"example": "\\lbrack 전기자기학 - 08.전류 \\rbrack \n\\\\\nI=\\dfrac{Q}{t}=\\dfrac{ne}{t},\\,\\, 전자의\\,\\, 수(n)으로\\,\\, 정리\\\\\n n=\\dfrac{It}{e}=\\dfrac{50 \\times 1}{1.602 \\times 10{-19}}=31.21 \\times 10^{19}\\\\\\,\\\\\n (e=1.602 \\times 10^{-19},\\,\\, 단위시간=1초\\,\\, 대입)"
+																},
+																"isKatex": {
+																	"type": "integer",
+																	"description": "카텍스 유무 (유 : 1, 무 : 0)",
+																	"example": 1
+																},
+																"problemScore": {
+																	"type": "integer",
+																	"description": "문제 배점",
+																	"example": 5
+																},
+																"lectureUrl": {
+																	"type": "integer",
+																	"description": "강의 URL",
+																	"example": "https://www.youtube.com/embed/nQ6GUrEB3XI?start=58&end=67"
+																}
+															}
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2053": {
+						"description": "examDetailIdx를 입력해주세요"
+					},
+					"2054": {
+						"description": "examDetailIdx를 형식에 맞게 입력해주세요"
+					},
+					"2055": {
+						"description": "해당 examDetailIdx가 존재하지 않습니다."
+					}
+				}
+			}
+		},
+		"/exams/{examIdx}/examDetail/{examDetailIdx}/problem/submission": {
+			"post": {
+				"tags": ["Exam"],
+				"summary": "CBT 시험 문제 제출",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "CBT 시험 문제 제출 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "CBT 시험 문제 제출 Path Variable : Exam Index",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examDetailIdx",
+						"description": "CBT 시험 문제 제출 Path Variable : Exam Detail Index",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "사용자가 제출한 정답 리스트 : questionIdx ( 해당 회차의 시험 문제 개수와 정답 배열의 길이가 같아야 한다.)",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["answerList"],
+							"properties": {
+								"answerList": {
+									"type": "array",
+									"items": {
+										"type": "integer"
+									}
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examGrade": {
+											"type": "object",
+											"properties": {
+												"examName": {
+													"type": "string",
+													"description": "시험 이름",
+													"example": "전기 기사"
+												},
+												"passScore": {
+													"type": "integer",
+													"description": "시험 합격 점수",
+													"example": 60
+												},
+												"userExamRecordIdx": {
+													"type": "integer",
+													"description": "사용자 시험 기록 테이블(userExamRecordIdx) 인덱스",
+													"example": 1271
+												},
+												"date": {
+													"type": "string",
+													"description": "시험 응시 날짜",
+													"example": "2016.07.10"
+												},
+												"time": {
+													"type": "string",
+													"description": "시험 제출 시각",
+													"example": "12:37"
+												},
+												"examRound": {
+													"type": "integer",
+													"description": "회차",
+													"example": 3
+												},
+												"score": {
+													"type": "integer",
+													"description": "총점",
+													"example": 200
+												},
+												"isPass": {
+													"type": "integer",
+													"description": "시험 합격 여부",
+													"example": 1
+												},
+												"correctAnswers": {
+													"type": "integer",
+													"description": "맞은 문제 개수",
+													"example": 4
+												},
+												"totalCount": {
+													"type": "integer",
+													"description": "총 문제 개수",
+													"example": 40
+												}
+											},
+											"description": "시험 성적"
+										},
+										"subjectGrade": {
+											"type": "array",
+											"description": "과목별 성적",
+											"items": {
+												"type": "object",
+												"properties": {
+													"examSubjectIdx": {
+														"type": "integer",
+														"description": "시험 과목 인덱스",
+														"example": 5
+													},
+													"examSubjectNum": {
+														"type": "integer",
+														"description": "시험 과목 번호 (ex. 전기기기 1과목)",
+														"example": "1"
+													},
+													"examSubjectName": {
+														"type": "string",
+														"description": "시험 과목 이름",
+														"example": "전자기기학"
+													},
+													"correctCount": {
+														"type": "integer",
+														"description": "맞은 문제 개수",
+														"example": 20
+													},
+													"total": {
+														"type": "integer",
+														"description": "전체 문제 개수",
+														"example": 20
+													},
+													"score": {
+														"type": "integer",
+														"description": "과목의 점수",
+														"example": 100
+													},
+													"isPass": {
+														"type": "integer",
+														"description": "합격 여부",
+														"example": 1
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examIdx를 형식에 맞게 입력해주세요"
+					},
+					"2051": {
+						"description": "해당 examIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examIdx를 입력해주세요"
+					},
+					"2053": {
+						"description": "examDetailIdx를 입력해주세요"
+					},
+					"2054": {
+						"description": "examDetailIdx를 형식에 맞게 입력해주세요"
+					},
+					"2055": {
+						"description": "해당 examDetailIdx가 존재하지 않습니다."
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2000": {
+						"description": "토큰을 입력해주세요"
+					},
+					"2103": {
+						"description": "각각의 배열을 길이에 맞게 입력해주세요"
+					}
+				}
+			}
+		},
+		"/exams/reviewNote/{multipleProblemIdx}": {
+			"post": {
+				"tags": ["Exam"],
+				"summary": "오답노트 담기",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "오답노트 담기 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "multipleProblemIdx",
+						"description": "오답노트 담기 Path Variable : 객관식 문제 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"reviewNoteIdx": {
+											"type": "integer",
+											"description": "생성된 오답노트 인덱스",
+											"example": "30728"
+										}
+									}
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "multipleIdx를 형식에 맞게 입력해주세요."
+					},
+					"2051": {
+						"description": "해당 multipleIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "multipleIdx를 입력해 주세요."
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/exams/problemError": {
+			"post": {
+				"tags": ["Exam"],
+				"summary": "문제 신고",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "문제 신고 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "문제 신고 Request Body : 게시글",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["multipleProblemIdx", "title", "content"],
+							"properties": {
+								"multipleProblemIdx": {
+									"type": "integer",
+									"description": "문제 인덱스",
+									"example": 456
+								},
+								"title": {
+									"type": "string",
+									"description": "에러 문제 제목",
+									"example": "이거 표가 이상해요"
+								},
+								"content": {
+									"type": "string",
+									"description": "에러 문제 내용",
+									"example": "봐보세요"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/users/tokenAuth": {
+			"post": {
+				"tags": ["User"],
+				"summary": "토큰 검증",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "토큰 검증 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "restoreExamDetailIdx",
+						"description": "수험표 인증을 위한 Query Variable : 복원 회차 인덱스",
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1112": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1112
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "토큰 검증에 성공하였습니다."
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"userIdx": {
+											"type": "integer",
+											"description": "사용자 인덱스",
+											"example": "1452"
+										},
+										"jwt": {
+											"type": "string",
+											"description": "jwt 토큰",
+											"example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWR4IjoxLCJpYXQiOjE2MjQ5MzAxNjksImV4cCI6MTY1NjQ2NjE2OSwic3ViIjoidXNlckluZm8ifQ.2BSh_Z4"
+										},
+										"admissionAuthStatus": {
+											"type": "integer",
+											"description": "query variable로 복원 시험 회차 인덱스를 보냈을 때만 나타나는 property이다. (수험표 인증이 필요할 때만 나타남) 수험표 인증 성공 : 1, 수험표 인증 실패 : 0"
+										},
+										"memberHash": {
+											"type": "string",
+											"description": "채널톡 boot를 할 때 보안상 필요한 memberHash이다.",
+											"example": "ffd5e11bd741d07d..."
+										},
+										"userName": {
+											"type": "string",
+											"description": "유저 이름",
+											"example": "김민기"
+										},
+										"nickname": {
+											"type": "string",
+											"description": "유저 닉네임",
+											"example": "dsad231"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/passwordAuth": {
+			"post": {
+				"tags": ["User"],
+				"summary": "비밀번호 검증",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "비밀번호 검증 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2085": {
+						"description": "비밀번호가 틀렸습니다."
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/reviewNote?page={}&sort={}&order={}&bookMark={}": {
+			"get": {
+				"tags": ["User"],
+				"summary": "오답노트 시험 목록 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "오답노트 시험 목록 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "오답노트 시험 목록 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "page",
+						"description": "오답노트 시험 목록 조회 Query Variable : 페이지 번호",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "sort",
+						"description": "오답노트 시험 목록 조회 Query Variable : 목록 분류(0:전체, 1:자격증, 2:공기업, 3:공무원)",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "order",
+						"description": "오답노트 시험 목록 조회 Query Variable : 목록 정렬(0:최신순, 1:오래된 순)",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "bookMark",
+						"description": "오답노트 시험 목록 조회 Query Variable : 북마크 여부 (0:전체, 1:북마크)",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "array",
+										"items": {
+											"type": "object",
+											"properties": {
+												"total": {
+													"type": "integer",
+													"description": "총 데이터 수(for pagination) *실제데이터는 상위배열의 객체(result[0].total)",
+													"example": 45
+												},
+												"reviewNoteIdx": {
+													"type": "integer",
+													"description": "오답노트 인덱스",
+													"example": 41564
+												},
+												"examDetailIdx": {
+													"type": "integer",
+													"description": "시험 회차(디테일) 인덱스",
+													"example": 8
+												},
+												"examName": {
+													"type": "string",
+													"description": "시험 이름",
+													"example": "전기 기사"
+												},
+												"examDate": {
+													"type": "string",
+													"description": "시험 날짜",
+													"example": "2021-09-28"
+												},
+												"examRound": {
+													"type": "string",
+													"description": "회차",
+													"example": "2021년 하반기"
+												},
+												"isKatex": {
+													"type": "integer",
+													"description": "카텍스 유무 (유 : 1, 무 : 0)",
+													"example": 1
+												},
+												"problem": {
+													"type": "string",
+													"description": "객관식 문제",
+													"example": "다음\\,\\, 설명\\,\\, 중\\,\\, 옳지\\,\\, 않은\\,\\, 것은?"
+												},
+												"problemNum": {
+													"type": "integer",
+													"description": "문제 번호",
+													"example": 4
+												},
+												"multipleProblemIdx": {
+													"type": "integer",
+													"description": "객관식 문제 인덱스",
+													"example": 107
+												},
+												"createdAt": {
+													"type": "string",
+													"description": "오답노트 생성 날짜",
+													"example": "2022-02-02"
+												},
+												"companyImage": {
+													"type": "string",
+													"description": "공기업 썸네일",
+													"example": "s3 link"
+												},
+												"bookMark": {
+													"type": "integer",
+													"description": "북마크 여부",
+													"example": 1
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/reviewNote/bookMark": {
+			"patch": {
+				"tags": ["User"],
+				"summary": "오답노트 북마크 추가/삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "오답노트 북마크 추가/삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "오답노트 북마크 추가/삭제 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "오답노트 북마크 추가/삭제 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["reviewNoteIdx", "bookMark"],
+							"properties": {
+								"reviewNoteIdx": {
+									"type": "integer",
+									"description": "오답노트 인덱스",
+									"example": 41580
+								},
+								"bookMark": {
+									"type": "integer",
+									"description": "북마크 여부 (0: 북마크 삭제, 1: 북마크 추가)",
+									"example": 1
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/reviewNote/set?sort={}&order={}&bookMark={}": {
+			"get": {
+				"tags": ["User"],
+				"summary": "오답노트 시험 문제 조회(필터)",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "오답노트 시험 문제 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "오답노트 시험 문제 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "sort",
+						"description": "오답노트 시험 문제 조회 Query Variable : 목록 분류(0:전체, 1:자격증, 2:공기업, 3:공무원)",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "order",
+						"description": "오답노트 시험 문제 조회 Query Variable : 목록 정렬(0:최신순, 1:오래된 순)",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "bookMark",
+						"description": "오답노트 시험 문제 조회 Query Variable : 북마크 여부 (0:전체, 1:북마크)",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "array",
+										"items": {
+											"type": "object",
+											"properties": {
+												"reviewNoteIdx": {
+													"type": "integer",
+													"description": "오답노트 인덱스",
+													"example": 41564
+												},
+												"examIdx": {
+													"type": "integer",
+													"description": "시험 인덱스",
+													"example": 4
+												},
+												"examDetailIdx": {
+													"type": "integer",
+													"description": "시험 회차(디테일) 인덱스",
+													"example": 8
+												},
+												"examName": {
+													"type": "string",
+													"description": "시험 이름",
+													"example": "전기 기사"
+												},
+												"examDate": {
+													"type": "string",
+													"description": "시험 날짜",
+													"example": "2021-09-28"
+												},
+												"examRound": {
+													"type": "string",
+													"description": "년도, 회차",
+													"example": "2021년 2회차"
+												},
+												"round": {
+													"type": "string",
+													"description": "회차",
+													"example": "2"
+												},
+												"problem": {
+													"type": "string",
+													"description": "객관식 문제",
+													"example": "다음\\,\\, 설명\\,\\, 중\\,\\, 옳지\\,\\, 않은\\,\\, 것은?"
+												},
+												"problemNum": {
+													"type": "integer",
+													"description": "문제 번호",
+													"example": 4
+												},
+												"problemImage": {
+													"type": "string",
+													"description": "문제 이미지",
+													"example": "image"
+												},
+												"multipleProblemIdx": {
+													"type": "integer",
+													"description": "객관식 문제 인덱스",
+													"example": 107
+												},
+												"createdAt": {
+													"type": "string",
+													"description": "오답노트 생성 날짜",
+													"example": "2022-02-02"
+												},
+												"companyImage": {
+													"type": "string",
+													"description": "공기업 썸네일",
+													"example": "s3 link"
+												},
+												"bookMark": {
+													"type": "integer",
+													"description": "북마크 여부",
+													"example": 1
+												},
+												"answerNum": {
+													"type": "integer",
+													"description": "정답번호",
+													"example": 4
+												},
+												"provisionNum": {
+													"type": "string",
+													"description": "설비규정",
+													"example": "112.2"
+												},
+												"solution": {
+													"type": "string",
+													"description": "해설",
+													"example": "\\lbrack 전기자기학 - 05.진공\\,\\, 중의\\,\\, 도체계 \\rbrack \n\\\\\n\\lbrack 전투수학(전기\\,\\, 수학\\,\\, 교재) - 26. 지수법칙 \\rbrack\n\\\\\nSTEP\\,\\, 1)\\,\\, 유전율을\\,\\, 구한다 \\,\\,\n유전율 \\epsilon=\\dfrac{D^2}{2W}\\\\ \nSTEP\\,\\, 2)\\,\\, 유전율에 정전에너지 밀도를 대입한다\\\\\n정전에너지밀도\\,\\, W=\\dfrac{D^2}{2\\epsilon}{\\rm [ J/m^3]} 에서\\\\\n유전율\\,\\,  \\epsilon=\\dfrac{D^2}{2W}=\\dfrac{(2.4 \\times 10^{-7})^2}{2 \\times 5.3 \\times 10^{-3}} \n\\\\ = 0.543 \\times 10^{-11}{\\rm [ F/m]}  \\,\\,\n = 5.43 \\times 10^{-12}{\\rm [ F/m]} "
+												},
+												"isKatex": {
+													"type": "integer",
+													"description": "카텍스 유무 (유 : 1, 무 : 0)",
+													"example": 1
+												},
+												"subjectinfo": {
+													"type": "string",
+													"description": "과목 정보",
+													"example": "제1과목 전자기학"
+												},
+												"lectureUrl": {
+													"type": "string",
+													"description": "해설 유튜브 링크",
+													"example": "https://www.youtube.com/watch?v=gM8IsD1GLsA"
+												},
+												"questions": {
+													"type": "array",
+													"items": {
+														"type": "object",
+														"properties": {
+															"questionIdx": {
+																"type": "integer",
+																"description": "선지 인덱스",
+																"example": 11099
+															},
+															"questionNum": {
+																"type": "integer",
+																"description": "선지 번호",
+																"example": 1
+															},
+															"isAnswer": {
+																"type": "integer",
+																"description": "정답 여부",
+																"example": 1
+															},
+															"question": {
+																"type": "string",
+																"description": "선지 내용",
+																"example": "5.43 \\times 10^{-12}"
+															},
+															"questionImage": {
+																"type": "string",
+																"description": "선지 이미지",
+																"example": "\\includegraphics[]{https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png}"
+															}
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/reviewNote/exam?examSort={}": {
+			"get": {
+				"tags": ["User"],
+				"summary": "오답노트 시험 목록 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "오답노트 시험 목록 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "오답노트 시험 목록 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "examSort",
+						"description": "오답노트 시험 목록 조회 Query Variable : 시험 대분류(공기업, 자격증, 편입)",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examIdx": {
+												"type": "integer",
+												"description": "시험 인덱스",
+												"example": 1
+											},
+											"examName": {
+												"type": "string",
+												"description": "시험 이름",
+												"example": "전기 기사"
+											},
+											"examDetailList": {
+												"type": "array",
+												"description": "시험 회차(디테일) 리스트",
+												"items": {
+													"type": "object",
+													"properties": {
+														"examDetailIdx": {
+															"type": "integer",
+															"description": "시험 회차(디테일) 인덱스",
+															"example": 28
+														},
+														"round": {
+															"type": "string",
+															"description": "시험 년도, 회차",
+															"example": "2021년 2회"
+														},
+														"count": {
+															"type": "string",
+															"description": "오답노트 문제개수",
+															"example": "(4)"
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/reviewNote/exam/{examIdx}": {
+			"get": {
+				"tags": ["User"],
+				"summary": "오답노트 시험 회차 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "오답노트 시험 회차 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "오답노트 시험 회차 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "오답노트 시험 회차 조회 Path Variable : 시험 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examDetailIdx": {
+											"type": "integer",
+											"description": "시험 회차 인덱스",
+											"example": 1
+										},
+										"list": {
+											"type": "integer",
+											"description": "시험, 년도, 회차, 문제개수",
+											"example": "전기기사 2019년 1회 (1)"
+										}
+									}
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examIdx를 형식에 맞게 입력해 주세요."
+					},
+					"2051": {
+						"description": "해당 examIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examIdx를 입력해 주세요."
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/reviewNote/problem/{examDetailIdx}": {
+			"get": {
+				"tags": ["User"],
+				"summary": "오답노트 문제 조회 (회차)",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "오답노트 문제 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "오답노트 문제 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examDetailIdx",
+						"description": "오답노트 문제 조회 Path Variable : 시험 회차(디테일) 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examInfo": {
+											"type": "object",
+											"properties": {
+												"examIdx": {
+													"type": "integer",
+													"description": "시험 인덱스",
+													"example": 1
+												},
+												"examDetailIdx": {
+													"type": "integer",
+													"description": "시험 회차(디테일) 인덱스",
+													"example": 8
+												},
+												"examName": {
+													"type": "string",
+													"description": "시험 이름",
+													"example": "전기 기사"
+												},
+												"date": {
+													"type": "string",
+													"description": "시험 날짜",
+													"example": "2016.07.10"
+												},
+												"examRound": {
+													"type": "integer",
+													"description": "회차",
+													"example": 3
+												},
+												"passScore": {
+													"type": "integer",
+													"description": "시험 합격 점수",
+													"example": 70
+												},
+												"timeLimit": {
+													"type": "integer",
+													"description": "시험 제한 시간",
+													"example": 250
+												},
+												"problemCount": {
+													"type": "integer",
+													"description": "시험 문제 개수",
+													"example": 100
+												}
+											},
+											"description": "시험 정보"
+										},
+										"problems": {
+											"type": "array",
+											"description": "문제 리스트",
+											"items": {
+												"type": "object",
+												"properties": {
+													"problemIdx": {
+														"type": "integer",
+														"description": "시험 문제 인덱스",
+														"example": 16
+													},
+													"problemNum": {
+														"type": "integer",
+														"description": "문제 번호",
+														"example": 4
+													},
+													"problem": {
+														"type": "string",
+														"description": "문제",
+														"example": "질문입니다."
+													},
+													"answerNum": {
+														"type": "integer",
+														"description": "정답번호",
+														"example": 4
+													},
+													"questions": {
+														"type": "array",
+														"items": {
+															"type": "object",
+															"properties": {
+																"questionIdx": {
+																	"type": "integer",
+																	"description": "선지 인덱스",
+																	"example": 11099
+																},
+																"questionNum": {
+																	"type": "integer",
+																	"description": "선지 번호",
+																	"example": 1
+																},
+																"question": {
+																	"type": "string",
+																	"description": "선지 내용",
+																	"example": "5.43 \\times 10^{-12}"
+																}
+															}
+														}
+													},
+													"provisionNum": {
+														"type": "string",
+														"description": "설비규정",
+														"example": "112.2"
+													},
+													"solution": {
+														"type": "string",
+														"description": "해설",
+														"example": "\\lbrack 전기자기학 - 05.진공\\,\\, 중의\\,\\, 도체계 \\rbrack \n\\\\\n\\lbrack 전투수학(전기\\,\\, 수학\\,\\, 교재) - 26. 지수법칙 \\rbrack\n\\\\\nSTEP\\,\\, 1)\\,\\, 유전율을\\,\\, 구한다 \\,\\,\n유전율 \\epsilon=\\dfrac{D^2}{2W}\\\\ \nSTEP\\,\\, 2)\\,\\, 유전율에 정전에너지 밀도를 대입한다\\\\\n정전에너지밀도\\,\\, W=\\dfrac{D^2}{2\\epsilon}{\\rm [ J/m^3]} 에서\\\\\n유전율\\,\\,  \\epsilon=\\dfrac{D^2}{2W}=\\dfrac{(2.4 \\times 10^{-7})^2}{2 \\times 5.3 \\times 10^{-3}} \n\\\\ = 0.543 \\times 10^{-11}{\\rm [ F/m]}  \\,\\,\n = 5.43 \\times 10^{-12}{\\rm [ F/m]} "
+													},
+													"isKatex": {
+														"type": "integer",
+														"description": "카텍스 유무 (유 : 1, 무 : 0)",
+														"example": 1
+													},
+													"subjectinfo": {
+														"type": "string",
+														"description": "과목 정보",
+														"example": "제1과목 전자기학"
+													},
+													"subjectNum": {
+														"type": "integer",
+														"description": "과목 번호",
+														"example": 1
+													},
+													"subjectName": {
+														"type": "string",
+														"description": "과목 이름",
+														"example": "전자기학"
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2053": {
+						"description": "examDetailIdx를 입력해주세요."
+					},
+					"2054": {
+						"description": "examDetailIdx를 형식에 맞게 입력해주세요."
+					},
+					"2055": {
+						"description": "해당 examDetailIdx가 존재하지 않습니다."
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"users/{userIdx}/reviewNote/subject?examSort={}": {
+			"get": {
+				"tags": ["User"],
+
+				"summary": "오답노트 시험 목록 과목별 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "오답노트 시험 목록 과목별 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "오답노트 시험 목록 과목별 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "examSort",
+						"description": "오답노트 시험 목록 과목별 조회 Query Variable : 시험 대분류(공기업, 자격증, 편입)",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examIdx": {
+												"type": "integer",
+												"description": "시험 인덱스",
+												"example": 1
+											},
+											"examName": {
+												"type": "string",
+												"description": "시험 이름",
+												"example": "전기 기사"
+											},
+											"companyImage": {
+												"type": "string",
+												"description": "공기업 썸네일",
+												"example": "s3 link"
+											},
+											"isAuth": {
+												"type": "integer",
+												"description": "상품 구매에 대한 권한 여부",
+												"example": 1
+											},
+											"examSubjectList": {
+												"type": "array",
+												"description": "오답노트 과목별 목록 조회",
+												"items": {
+													"type": "object",
+													"properties": {
+														"examSubjectIdx": {
+															"type": "integer",
+															"description": "오답노트 과목 인덱스",
+															"example": 1
+														},
+														"examSubjectName": {
+															"type": "string",
+															"description": "오답노트 과목 이름",
+															"example": "전기자기학"
+														},
+														"subjectNum": {
+															"type": "string",
+															"description": "오답노트 과목 순서",
+															"example": "제 1과목"
+														},
+														"count": {
+															"type": "integer",
+															"description": "오답노트 문제개수",
+															"example": 2
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/reviewNote/{examIdx}/subject/problem/{examSubjectIdx}": {
+			"get": {
+				"tags": ["User"],
+				"summary": "오답노트 과목별 문제 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "오답노트 시험 과목별 문제 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "오답노트 시험 과목별 문제 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "오답노트 시험 과목별 문제 조회 Path Variable : 시험 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examSubjectIdx",
+						"description": "오답노트 시험 과목별 문제 조회 Path Variable : 과목 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"subjectInfo": {
+											"type": "object",
+											"properties": {
+												"examIdx": {
+													"type": "integer",
+													"description": "시험 인덱스",
+													"example": 1
+												},
+												"examSubjectIdx": {
+													"type": "integer",
+													"description": "시험 과목 인덱스",
+													"example": 1
+												},
+												"examSubjectName": {
+													"type": "string",
+													"description": "과목 이름",
+													"example": "전기 자기학"
+												},
+												"problemCount": {
+													"type": "integer",
+													"description": "과목에 해당하는 문제수"
+												}
+											}
+										},
+										"problems": {
+											"type": "array",
+											"description": "문제 리스트",
+											"items": {
+												"type": "object",
+												"properties": {
+													"problemIdx": {
+														"type": "integer",
+														"description": "시험 문제 인덱스",
+														"example": 16
+													},
+													"problemNum": {
+														"type": "integer",
+														"description": "문제 번호",
+														"example": 4
+													},
+													"problem": {
+														"type": "string",
+														"description": "문제",
+														"example": "질문입니다."
+													},
+													"answerNum": {
+														"type": "integer",
+														"description": "정답번호",
+														"example": 4
+													},
+													"questions": {
+														"type": "array",
+														"items": {
+															"type": "object",
+															"properties": {
+																"questionIdx": {
+																	"type": "integer",
+																	"description": "선지 인덱스",
+																	"example": 11099
+																},
+																"questionNum": {
+																	"type": "integer",
+																	"description": "선지 번호",
+																	"example": 1
+																},
+																"isAnswer": {
+																	"type": "integer",
+																	"description": "정답 여부",
+																	"example": 1
+																},
+																"question": {
+																	"type": "string",
+																	"description": "선지 내용",
+																	"example": "5.43 \\times 10^{-12}"
+																},
+																"questionImage": {
+																	"type": "string",
+																	"description": "문제 이미지"
+																},
+																"multipleProblemIdx": {
+																	"type": "integer",
+																	"description": "객관식 문제 인덱스",
+																	"example": 423
+																}
+															}
+														}
+													},
+													"provisionNum": {
+														"type": "string",
+														"description": "설비규정",
+														"example": "112.2"
+													},
+													"solution": {
+														"type": "string",
+														"description": "해설",
+														"example": "\\lbrack 전기자기학 - 05.진공\\,\\, 중의\\,\\, 도체계 \\rbrack \n\\\\\n\\lbrack 전투수학(전기\\,\\, 수학\\,\\, 교재) - 26. 지수법칙 \\rbrack\n\\\\\nSTEP\\,\\, 1)\\,\\, 유전율을\\,\\, 구한다 \\,\\,\n유전율 \\epsilon=\\dfrac{D^2}{2W}\\\\ \nSTEP\\,\\, 2)\\,\\, 유전율에 정전에너지 밀도를 대입한다\\\\\n정전에너지밀도\\,\\, W=\\dfrac{D^2}{2\\epsilon}{\\rm [ J/m^3]} 에서\\\\\n유전율\\,\\,  \\epsilon=\\dfrac{D^2}{2W}=\\dfrac{(2.4 \\times 10^{-7})^2}{2 \\times 5.3 \\times 10^{-3}} \n\\\\ = 0.543 \\times 10^{-11}{\\rm [ F/m]}  \\,\\,\n = 5.43 \\times 10^{-12}{\\rm [ F/m]} "
+													},
+													"isKatex": {
+														"type": "integer",
+														"description": "카텍스 유무 (유 : 1, 무 : 0)",
+														"example": 1
+													},
+													"subjectinfo": {
+														"type": "string",
+														"description": "과목 정보",
+														"example": "제1과목 전자기학"
+													},
+													"subjectNum": {
+														"type": "integer",
+														"description": "과목 번호",
+														"example": 1
+													},
+													"subjectName": {
+														"type": "string",
+														"description": "과목 이름",
+														"example": "전자기학"
+													},
+													"reviewNoteIdx": {
+														"type": "integer",
+														"description": "오답노트 인덱스",
+														"example": 2
+													},
+													"isMemo": {
+														"type": "boolean",
+														"description": "메모기능의 가능 여부",
+														"example": true
+													},
+													"isAuth": {
+														"type": "integer",
+														"description": "상품 구매에 대한 권한 여부",
+														"example": 1
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2134": {
+						"description": "존재하지 않는 과목입니다"
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					},
+					"2050": {
+						"description": "examIdx를 형식에 맞게 입력해 주세요"
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/reviewNote": {
+			"patch": {
+				"tags": ["User"],
+				"summary": "오답노트 문제 삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "오답노트 문제 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "오답노트 문제 삭제 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "오답노트 문제 삭제 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["reviewNoteIdx"],
+							"properties": {
+								"reviewNoteIdx": {
+									"type": "integer",
+									"description": "오답노트 인덱스",
+									"example": 41580
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/reviewNote/problem/:examDetailIdx": {
+			"patch": {
+				"tags": ["User"],
+				"summary": "오답노트 문제 삭제 (회차)",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "오답노트 문제 삭제 (회차) Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "오답노트 문제 삭제 (회차) Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examDetailIdx",
+						"description": "오답노트 문제 삭제 (회차) Path Variable : 시험 회차(디테일) 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/:userIdx/reviewNote/delete": {
+			"patch": {
+				"tags": ["User"],
+				"summary": "오답 노트 삭제 (과목)",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "오답 노트 삭제 (과목) Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "오답 노트 삭제 (과목) Path Variable : 사용자 인덱스",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["examIdx", "examSubjectIdx"],
+							"properties": {
+								"examIdx": {
+									"type": "integer",
+									"description": "시험 인덱스",
+									"example": "2"
+								},
+								"examSubjectIdx": {
+									"type": "integer",
+									"description": "시험 과목 인덱스",
+									"example": "4"
+								}
+							}
+						}
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "오답 노트 삭제 (과목) body variable : 시험 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "examSubjectIdx",
+						"description": "오답 노트 삭제 (과목) body variable : 과목 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/examRecord": {
+			"get": {
+				"tags": ["User"],
+				"summary": "사용자 시험 기록 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "사용자 시험 기록 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "사용자 시험 기록 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examIdx": {
+												"type": "integer",
+												"description": "시험 인덱스",
+												"example": "1"
+											},
+											"examName": {
+												"type": "string",
+												"description": "시험 이름",
+												"example": "전기기사"
+											},
+											"passScore": {
+												"type": "integer",
+												"description": "합격 점수",
+												"example": "60"
+											},
+											"recordList": {
+												"type": "array",
+												"description": "기록 리스트",
+												"items": {
+													"type": "object",
+													"properties": {
+														"examDetailIdx": {
+															"type": "integer",
+															"description": "시험 회차(디테일) 인덱스",
+															"example": "2"
+														},
+														"userExamRecordIdx": {
+															"type": "integer",
+															"description": "사용자 시험 기록 인덱스",
+															"example": "1272"
+														},
+														"date": {
+															"type": "string",
+															"description": "시험 응시 날짜",
+															"example": "2021-09-06"
+														},
+														"time": {
+															"type": "string",
+															"description": "시험 응시 시각",
+															"example": "12:37"
+														},
+														"round": {
+															"type": "string",
+															"description": "회차",
+															"example": "2020-1회"
+														},
+														"score": {
+															"type": "integer",
+															"description": "점수",
+															"example": 196
+														},
+														"isPass": {
+															"type": "string",
+															"description": "합격 여부",
+															"example": "Y or N"
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examIdx를 형식에 맞게 입력해 주세요."
+					},
+					"2051": {
+						"description": "해당 examIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examIdx를 입력해 주세요."
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/cbtExamRecord/delete": {
+			"patch": {
+				"tags": ["User"],
+				"summary": "CBT 모의고사 체크 목록 삭제",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "CBT 모의고사 체크 목록 삭제 Request Body",
+						"required": true,
+						"schema": {
+							"type": "array",
+							"required": ["userCbtExamRecordDeleteIdxList"],
+							"properties": {
+								"userCbtExamRecordDeleteIdxList": {
+									"type": "array",
+									"description": "CBT 모의고사 체크된 목록",
+									"example": [12333, 12345, 22235]
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메시지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/examRecord/{examRecordIdx}/grade/{examIdx}": {
+			"get": {
+				"tags": ["User"],
+				"summary": "사용자 시험 기록 채점결과 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "사용자 시험 기록 채점결과 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "사용자 시험 기록 채점결과 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examRecordIdx",
+						"description": "사용자 시험 기록 채점결과 조회 Path Variable : 사용자 시험 기록 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "사용자 시험 기록 채점결과 조회 Path Variable : 사용자 시험 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examGrade": {
+											"type": "object",
+											"properties": {
+												"examName": {
+													"type": "string",
+													"description": "시험 이름",
+													"example": "전기 기사"
+												},
+												"passScore": {
+													"type": "integer",
+													"description": "시험 합격 점수",
+													"example": 60
+												},
+												"userExamRecordIdx": {
+													"type": "integer",
+													"description": "사용자 시험 기록 테이블(userExamRecordIdx) 인덱스",
+													"example": 1271
+												},
+												"date": {
+													"type": "string",
+													"description": "시험 응시 날짜",
+													"example": "2016.07.10"
+												},
+												"time": {
+													"type": "string",
+													"description": "시험 제출 시각",
+													"example": "12:37"
+												},
+												"examRound": {
+													"type": "integer",
+													"description": "회차",
+													"example": 3
+												},
+												"score": {
+													"type": "integer",
+													"description": "총점",
+													"example": 200
+												},
+												"isPass": {
+													"type": "integer",
+													"description": "시험 합격 여부",
+													"example": 1
+												},
+												"totalCount": {
+													"type": "integer",
+													"description": "문제 개수",
+													"example": 40
+												},
+												"correctAnswer": {
+													"type": "integer",
+													"description": "맞은 문제 개수",
+													"example": 5
+												}
+											},
+											"description": "시험 성적"
+										},
+										"subjectGrade": {
+											"type": "array",
+											"description": "과목별 성적",
+											"items": {
+												"type": "object",
+												"properties": {
+													"examSubjectIdx": {
+														"type": "integer",
+														"description": "시험 과목 인덱스",
+														"example": 5
+													},
+													"examSubjectNum": {
+														"type": "integer",
+														"description": "시험 과목 번호 (ex. 전기기기 1과목)",
+														"example": "1"
+													},
+													"examSubjectName": {
+														"type": "string",
+														"description": "시험 과목 이름",
+														"example": "전자기기학"
+													},
+													"correctCount": {
+														"type": "integer",
+														"description": "맞은 문제 개수",
+														"example": 20
+													},
+													"total": {
+														"type": "integer",
+														"description": "전체 문제 개수",
+														"example": 20
+													},
+													"score": {
+														"type": "integer",
+														"description": "과목의 점수",
+														"example": 100
+													},
+													"isPass": {
+														"type": "integer",
+														"description": "합격 여부",
+														"example": 1
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examIdx를 형식에 맞게 입력해 주세요."
+					},
+					"2051": {
+						"description": "해당 examIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examIdx를 입력해 주세요."
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/examRecord/{examRecordIdx}/detail": {
+			"get": {
+				"tags": ["User"],
+				"summary": "사용자 시험 기록 해설 및 상세 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "사용자 시험 기록 해설 및 상세 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "사용자 시험 기록 해설 및 상세 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examRecordIdx",
+						"description": "사용자 시험 기록 해설 및 상세 조회 Path Variable : 사용자 시험 기록 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examInfo": {
+											"type": "object",
+											"properties": {
+												"examName": {
+													"type": "string",
+													"description": "시험 이름",
+													"example": "전기 기사"
+												},
+												"date": {
+													"type": "string",
+													"description": "시험 날짜",
+													"example": "2020.06.06"
+												},
+												"examRound": {
+													"type": "integer",
+													"description": "회차",
+													"example": 1
+												},
+												"passScore": {
+													"type": "integer",
+													"description": "시험 합격 점수",
+													"example": 70
+												},
+												"problemCount": {
+													"type": "integer",
+													"description": "시험 문제 개수",
+													"example": 100
+												},
+												"timeLimit": {
+													"type": "integer",
+													"description": "시험 제한 시간",
+													"example": 250
+												}
+											},
+											"description": "시험 정보"
+										},
+										"subject": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"examSubjectIdx": {
+														"type": "integer",
+														"description": "시험 과목 인덱스",
+														"example": 1
+													},
+													"examSubjectNum": {
+														"type": "integer",
+														"description": "시험 과목 번호 (ex. 전기기기 1과목)",
+														"example": "1"
+													},
+													"examSubjectName": {
+														"type": "string",
+														"description": "시험 과목 이름",
+														"example": "전자기기학"
+													},
+													"passScore": {
+														"type": "integer",
+														"description": "시험 합격 점수",
+														"example": 40
+													},
+													"problems": {
+														"type": "array",
+														"description": "과목에 해당하는 문제 리스트",
+														"items": {
+															"type": "object",
+															"properties": {
+																"problemIdx": {
+																	"type": "integer",
+																	"description": "시험 문제 인덱스",
+																	"example": 16
+																},
+																"problemNum": {
+																	"type": "integer",
+																	"description": "문제 번호",
+																	"example": 4
+																},
+																"problem": {
+																	"type": "string",
+																	"description": "문제",
+																	"example": "질문입니다."
+																},
+																"answerNum": {
+																	"type": "integer",
+																	"description": "정답번호",
+																	"example": 4
+																},
+																"questions": {
+																	"type": "array",
+																	"items": {
+																		"type": "object",
+																		"properties": {
+																			"questionIdx": {
+																				"type": "integer",
+																				"description": "선지 인덱스",
+																				"example": 11099
+																			},
+																			"questionNum": {
+																				"type": "integer",
+																				"description": "선지 번호",
+																				"example": 1
+																			},
+																			"question": {
+																				"type": "string",
+																				"description": "선지 내용",
+																				"example": "mged"
+																			}
+																		}
+																	}
+																},
+																"provisionNum": {
+																	"type": "string",
+																	"description": "설비규정",
+																	"example": "112.2"
+																},
+																"solution": {
+																	"type": "string",
+																	"description": "해설",
+																	"example": "\\lbrack 전기자기학 - 08.전류 \\rbrack \n\\\\\nI=\\dfrac{Q}{t}=\\dfrac{ne}{t},\\,\\, 전자의\\,\\, 수(n)으로\\,\\, 정리\\\\\n n=\\dfrac{It}{e}=\\dfrac{50 \\times 1}{1.602 \\times 10{-19}}=31.21 \\times 10^{19}\\\\\\,\\\\\n (e=1.602 \\times 10^{-19},\\,\\, 단위시간=1초\\,\\, 대입)"
+																},
+																"isKatex": {
+																	"type": "integer",
+																	"description": "카텍스 유무 (유 : 1, 무 : 0)",
+																	"example": 1
+																},
+																"isDelete": {
+																	"type": "integer",
+																	"description": "삭제 여부",
+																	"example": 0
+																},
+																"problemScore": {
+																	"type": "integer",
+																	"description": "문제 배점",
+																	"example": 5
+																}
+															}
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examIdx를 형식에 맞게 입력해 주세요."
+					},
+					"2051": {
+						"description": "해당 examIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examIdx를 입력해 주세요."
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/problemError": {
+			"get": {
+				"tags": ["User"],
+				"summary": "사용자 오류신고 내역 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "사용자 오류신고 내역 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "사용자 오류신고 내역 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"problemErrorIdx": {
+											"type": "integer",
+											"description": "오류 신고 인덱스",
+											"example": 310
+										},
+										"multipleProblemIdx": {
+											"type": "integer",
+											"description": "오류 신고 문제 인덱스",
+											"example": 310
+										},
+										"examIdx": {
+											"type": "integer",
+											"description": "시험 인덱스",
+											"example": 310
+										},
+										"examDetailIdx": {
+											"type": "integer",
+											"description": "시험 상세 인덱스",
+											"example": 310
+										},
+										"problemNum": {
+											"type": "integer",
+											"description": "문제 번호",
+											"example": 310
+										},
+										"title": {
+											"type": "string",
+											"description": "오류 신고 게시글 제목",
+											"example": "2020년 2회 전기자기학 02번"
+										},
+										"content": {
+											"type": "string",
+											"description": "오류 신고 게시글 본문",
+											"example": "2번 선택지, 10V인데 100V로 되어 있어요."
+										},
+										"date": {
+											"type": "string",
+											"description": "작성 날짜",
+											"example": "2021-09-06"
+										},
+										"time": {
+											"type": "string",
+											"description": "작성 시각",
+											"example": "17:05"
+										},
+										"status": {
+											"type": "string",
+											"description": "해당 글 상태",
+											"example": "N"
+										}
+									}
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/problemError": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 문제 에러 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 문제 에러 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"problemErrorIdx": {
+												"type": "integer",
+												"description": "문제 인덱스",
+												"example": 1452
+											},
+											"examIdx": {
+												"type": "integer",
+												"description": "시험 인덱스",
+												"example": 1452
+											},
+											"examDetailIdx": {
+												"type": "integer",
+												"description": "시험 상세 인덱스",
+												"example": 1452
+											},
+											"problemNum": {
+												"type": "integer",
+												"description": "문제번호",
+												"example": 56
+											},
+											"userIdx": {
+												"type": "integer",
+												"description": "사용자 인덱스",
+												"example": 1452
+											},
+											"userName": {
+												"type": "string",
+												"description": "사용자 이름",
+												"example": "김승은"
+											},
+											"nickname": {
+												"type": "string",
+												"description": "사용자 닉네임",
+												"example": "7시 언제됨"
+											},
+											"title": {
+												"type": "string",
+												"description": "오류 신고 게시글 제목",
+												"example": "2020년 2회 전기자기학 02번"
+											},
+											"content": {
+												"type": "string",
+												"description": "오류 신고 게시글 본문",
+												"example": "2번 선택지, 10V인데 100V로 되어 있어요."
+											},
+											"date": {
+												"type": "string",
+												"description": "작성 날짜",
+												"example": "2021-09-06"
+											},
+											"time": {
+												"type": "string",
+												"description": "작성 시각",
+												"example": "17:05"
+											},
+											"status": {
+												"type": "string",
+												"description": "해당 글 상태 (N : 처리X, Y:처리O)",
+												"example": "N"
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/delete/problemError/{problemErrorIdx}": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 문제 오류 삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 문제 오류 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "problemErrorIdx",
+						"description": "관리자 문제 오류 삭제 Path Variable : 오류 신고  인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/update/multipleProblem/{multipleProblemIdx}": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 객관식 문제 수정",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 객관식 문제 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "multipleProblemIdx",
+						"description": "관리자 객관식 문제 수정 Path Variable : MultipleProblem 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 객관식 문제 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"description": "question, questionNum, questionImage, isQuestion의 배열의 길이는 모두 동일하다.",
+							"required": [
+								"problem",
+								"problemNum",
+								"problemImage",
+								"solution",
+								"question",
+								"questionNum",
+								"questionImage",
+								"isProblem",
+								"isQuestion",
+								"answerNum",
+								"isKatex",
+								"isDelete",
+								"problemScore",
+								"provisionNum",
+								"questionIdx",
+								"lectureUrl",
+								"examDetailIdx"
+							],
+							"properties": {
+								"problem": {
+									"type": "string",
+									"description": "수정하려는 문제 내용",
+									"example": "수정한 문제입니다."
+								},
+								"problemNum": {
+									"type": "integer",
+									"description": "문제 번호",
+									"example": 1
+								},
+								"problemImage": {
+									"type": "string",
+									"description": "문제 이미지 / 넣지 않으면 null로 자동 지정",
+									"example": "null"
+								},
+								"solution": {
+									"type": "string",
+									"description": "수정하려는 해설 내용",
+									"example": "수정한 해설입니다."
+								},
+								"question": {
+									"description": "선지 내용 리스트 / 수정하려는 선지 개수를 배열의 길이로 한다. 수정하지 않는 경우는 기존과 동일한 배열의 길이를 가진다.",
+									"type": "array",
+									"items": {
+										"type": "string",
+										"description": "수정하려는 선지 내용",
+										"example": "도체에 흐르는 전하의 양은 일정하다"
+									}
+								},
+								"questionNum": {
+									"description": "선지 번호 리스트 / 수정하려는 선지 개수를 배열의 길이로 한다. 수정하지 않는 경우는 기존과 동일한 배열의 길이를 가진다.",
+									"type": "array",
+									"items": {
+										"type": "integer",
+										"description": "수정하려는 선지 번호",
+										"example": 1
+									}
+								},
+								"questionImage": {
+									"description": "선지에 필요한 이미지 리스트 / 수정하려는 선지 개수를 배열의 길이로 한다. 수정하지 않는 경우는 기존과 동일한 배열의 길이를 가진다. 작성하지 않으면 null로 자동 지정.",
+									"type": "array",
+									"items": {
+										"type": "string",
+										"description": "수정하려는 선지 이미지 이름",
+										"example": 1
+									}
+								},
+								"isProblem": {
+									"type": "integer",
+									"description": "문제 수정했는지 유무(0 : 수정X, 1 : 수정O)",
+									"example": 1
+								},
+								"isQuestion": {
+									"type": "array",
+									"description": "각 선지에 대한 수정 유무 정보를 담은 리스트",
+									"items": {
+										"type": "integer",
+										"description": "0 : 수정X, 1 : 수정O",
+										"example": 1
+									}
+								},
+								"isKatex": {
+									"type": "integer",
+									"description": "카텍스 유무 (유 : 1, 무 : 0) / 작성하지 않으면 0으로 자동 설정",
+									"example": 0
+								},
+								"isDelete": {
+									"type": "integer",
+									"description": "삭제 유무 (유 : 1, 무 : 0) / 작성하지 않으면 0으로 자동 설정",
+									"example": 0
+								},
+								"answerNum": {
+									"type": "integer",
+									"description": "정답 번호 / 작성하지 않으면 0으로 자동 설정",
+									"example": 0
+								},
+								"problemScore": {
+									"type": "integer",
+									"description": "수정하려는 문제 배점 / 작성하지 않으면 0으로 자동 설정",
+									"example": 0
+								},
+								"examSubjectIdx": {
+									"type": "integer",
+									"description": "시험 과목 인덱스 / *자격증 문제 수정시에만 필요*",
+									"example": 0
+								},
+								"questionIdx": {
+									"type": "array",
+									"description": "기존 선지 개수보다 수정하려는 선지 개수가 적은 경우 필요함.",
+									"example": "001191001"
+								},
+								"categoryIdx": {
+									"type": "integer",
+									"description": "카테고리 인덱스",
+									"example": 150
+								},
+								"examDetailIdx": {
+									"type": "integer",
+									"description": "시험 회차 인덱스",
+									"example": 15
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "multipleProblemIdx를 형식에 맞게 입력해주세요."
+					},
+					"2051": {
+						"description": "해당 multipleProblemIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "multipleProblemIdx를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/upload/multipleProblem": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": "관리자 객관식 문제 삽입 ( 자격증, 공기업 공통 )",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 객관식 문제 삽입 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 객관식 문제 삽입 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"description": "question, questionNum, questionImage의 배열의 길이는 모두 동일하다.",
+							"required": [
+								"problemNum",
+								"problem",
+								"question",
+								"questionNum",
+								"questionImage",
+								"answerNum",
+								"examSubjectIdx",
+								"examDetailIdx",
+								"solution",
+								"isKatex",
+								"isDelete"
+							],
+							"properties": {
+								"problem": {
+									"type": "string",
+									"description": "문제 내용",
+									"example": "문제입니다."
+								},
+								"problemNum": {
+									"type": "integer",
+									"description": "문제 번호",
+									"example": 1
+								},
+								"problemImage": {
+									"type": "string",
+									"description": "문제 이미지",
+									"example": "image"
+								},
+								"solution": {
+									"type": "string",
+									"description": "해설 내용",
+									"example": "해설입니다."
+								},
+								"question": {
+									"description": "선지 내용 리스트 (ex. ['선지1', '선지2', '선지3', '선지4'])",
+									"type": "array",
+									"items": {
+										"type": "string",
+										"description": "선지 내용",
+										"example": "도체에 흐르는 전하의 양은 일정하다"
+									}
+								},
+								"questionNum": {
+									"description": "선지 번호 리스트",
+									"type": "array",
+									"items": {
+										"type": "integer",
+										"description": "선지 번호",
+										"example": 1
+									}
+								},
+								"questionImage": {
+									"description": "선지에 필요한 이미지 리스트 (ex. ['','','',''] or ['image1','image2','image3','image4'] or ['image1','','image3',''])",
+									"type": "array",
+									"items": {
+										"type": "string",
+										"description": "선지 이미지 이름",
+										"example": 1
+									}
+								},
+								"isKatex": {
+									"type": "integer",
+									"description": "카텍스 유무 (유 : 1, 무 : 0)",
+									"example": 0
+								},
+								"answerNum": {
+									"type": "integer",
+									"description": "정답 번호",
+									"example": 0
+								},
+								"examSubjectIdx": {
+									"type": "integer",
+									"description": "시험 과목 인덱스 / *자격증 문제 삽입시에만 필요*",
+									"example": 0
+								},
+								"provisionNum": {
+									"type": "string",
+									"description": "설비규정",
+									"example": "112.2"
+								},
+								"lectureUrl": {
+									"type": "string",
+									"description": "유튜브 링크",
+									"example": "https://www.youtube.com/watch?v=gM8IsD1GLsA"
+								},
+								"examDetailIdx": {
+									"type": "integer",
+									"description": "시험 상세 인덱스",
+									"example": 23
+								},
+								"categoryIdx": {
+									"type": "integer",
+									"description": "카테고리 인덱스",
+									"example": 134
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/delete/multipleProblem/{multipleProblemIdx}": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 객관식 문제 삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 객관식 문제 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "multipleProblemIdx",
+						"description": "관리자 객관식 문제 삭제 Path Variable : MultipleProblem 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "multipleIdx를 형식에 맞게 입력해주세요."
+					},
+					"2051": {
+						"description": "해당 multipleIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "multipleIdx를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/update/examSubject/{examSubjectIdx}": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 과목 수정",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 과목 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 시험 과목 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["examSubjectName", "passScore"],
+							"properties": {
+								"examSubjectName": {
+									"type": "string",
+									"description": "시험 과목 이름",
+									"example": "전자기학"
+								},
+								"passScore": {
+									"type": "integer",
+									"description": "시험 합격 점수",
+									"example": "50"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examSubjectIdx를 형식에 맞게 입력해주세요."
+					},
+					"2051": {
+						"description": "해당 examSubjectIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examSubjectIdx를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/update/examSubject/{examSubjectIdx}/subjectNum": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 과목 번호 수정",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 과목 번호 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "시험 과목 인덱스",
+						"description": "관리자 시험 과목 번호 수정 Path Variable : 시험 과목 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 시험 과목 번호 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["examIdx", "examSubjectIdx"],
+							"properties": {
+								"examIdx": {
+									"type": "integer",
+									"description": "시험 인덱스",
+									"example": "2"
+								},
+								"examSubjectIdx": {
+									"type": "integer",
+									"description": "시험 과목 인덱스",
+									"example": "4"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examSubjectIdx를 형식에 맞게 입력해주세요."
+					},
+					"2051": {
+						"description": "해당 examSubjectIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examSubjectIdx를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/upload/examSubject": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 과목 생성",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 과목 생성 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 시험 과목 생성 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["examSubjectName", "passScore"],
+							"properties": {
+								"examSubjectName": {
+									"type": "integer",
+									"description": "시험 과목 이름",
+									"example": "2"
+								},
+								"passScore": {
+									"type": "integer",
+									"description": "시험 합격 점수",
+									"example": "4"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examSubjectIdx": {
+											"type": "integer",
+											"description": "시험 과목 인덱스",
+											"example": 18
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"admins/delete/examSubject/{examSubjectIdx}": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 과목 삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 과목 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examSubjectIdx",
+						"description": "관리자 시험 과목 삭제 Path Variable : 시험 과목 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examSubjectIdx를 형식에 맞게 입력해주세요."
+					},
+					"2051": {
+						"description": "해당 examSubjectIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examSubjectIdx를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/upload/exam": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 생성",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 생성 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 시험 생성 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": [
+								"examName",
+								"passScore",
+								"examSortIdx",
+								"examSortRef",
+								"timeLimit",
+								"problemcount",
+								"examSubjectIdx",
+								"examSubjectNum",
+								"questionType"
+							],
+							"properties": {
+								"examName": {
+									"type": "string",
+									"description": "시험 이름",
+									"example": "전기자기기능사"
+								},
+								"passScore": {
+									"type": "int",
+									"description": "합격 점수",
+									"example": 60
+								},
+								"timeLimit": {
+									"type": "integer",
+									"description": "제한 시간",
+									"example": 150
+								},
+								"problemCount": {
+									"type": "integer",
+									"description": "문제 개수",
+									"example": 100
+								},
+								"examSortIdx": {
+									"type": "integer",
+									"description": "시험 종류 인덱스",
+									"example": 2
+								},
+								"examSortRef": {
+									"type": "integer",
+									"description": "시험 구분 인덱스",
+									"example": 5
+								},
+								"questionType": {
+									"type": "string",
+									"description": "선지 유형",
+									"example": "객관식4지선다"
+								},
+								"examUrl": {
+									"type": "string",
+									"description": "시험 이미지 url",
+									"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/calendar/01.png"
+								},
+								"examSubjectIdx": {
+									"type": "array",
+									"description": "시험 과목 인덱스 리스트(ex. [1,2,6,8,11]) - 해당 과목으로 시험을 구성하겠다.",
+									"items": {
+										"type": "integer",
+										"description": "시험 과목 인덱스"
+									}
+								},
+								"examSubjectNum": {
+									"type": "array",
+									"description": "시험 과목 번호 리스트(ex. [1,2,3,4,5])",
+									"items": {
+										"type": "integer",
+										"description": "시험 과목 번호"
+									}
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examIdx": {
+											"type": "integer",
+											"description": "시험 인덱스",
+											"example": 956
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/delete/Exam/{examIdx}": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "관리자 시험 삭제 Path Variable : 시험 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examIdx를 형식에 맞게 입력해주세요."
+					},
+					"2051": {
+						"description": "해당 examIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examIdx를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/update/Exam/{examIdx}": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 정보 수정",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 정보 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "관리자 시험 정보 수정Path Variable : 시험 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 시험 정보 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": [
+								"examName",
+								"passScore",
+								"examSort",
+								"examSortRef",
+								"timeLimit",
+								"problemcount",
+								"examSubjectIdx",
+								"examSubjectNum",
+								"questionType"
+							],
+							"properties": {
+								"examName": {
+									"type": "string",
+									"description": "시험 이름",
+									"example": "전기자기기능사"
+								},
+								"passScore": {
+									"type": "int",
+									"description": "합격 점수",
+									"example": 60
+								},
+								"timeLimit": {
+									"type": "integer",
+									"description": "제한 시간",
+									"example": 150
+								},
+								"problemCount": {
+									"type": "integer",
+									"description": "문제 개수",
+									"example": 100
+								},
+								"examSort": {
+									"type": "string",
+									"description": "시험 종류",
+									"example": "기능장"
+								},
+								"examSortRef": {
+									"type": "string",
+									"description": "시험 구분",
+									"example": "자격증"
+								},
+								"questionType": {
+									"type": "string",
+									"description": "선지 유형",
+									"example": "객관식4지선다"
+								},
+								"examUrl": {
+									"type": "string",
+									"description": "시험 이미지 url",
+									"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/calendar/01.png"
+								},
+								"examSubjectIdx": {
+									"type": "array",
+									"description": "시험 과목 인덱스 리스트(ex. [1,2,6,8,11]) - 해당 과목으로 시험을 구성하겠다.",
+									"items": {
+										"type": "integer",
+										"description": "시험 과목 인덱스"
+									}
+								},
+								"examSubjectNum": {
+									"type": "array",
+									"description": "시험 과목 번호 리스트(ex. [1,2,3,4,5])",
+									"items": {
+										"type": "integer",
+										"description": "시험 과목 번호"
+									}
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examIdx를 형식에 맞게 입력해주세요."
+					},
+					"2051": {
+						"description": "해당 examIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examIdx를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/upload/Exam/{examIdx}": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 상세 생성",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 상세 생성 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "관리자 시험 상세 생성 Path Variable : 시험 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 시험 상세 생성 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["examDate", "examRound"],
+							"properties": {
+								"examDate": {
+									"type": "string",
+									"description": "시험 날짜",
+									"example": "2021-12-25"
+								},
+								"examRound": {
+									"type": "integer",
+									"description": "시험 회차",
+									"example": 3
+								},
+								"examDetailUrl": {
+									"type": "string",
+									"description": "시험 상세 이미지",
+									"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examDetailIdx": {
+											"type": "integer",
+											"description": "생성한 시험 상세 인덱스",
+											"example": 110
+										}
+									}
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examIdx를 형식에 맞게 입력해주세요."
+					},
+					"2051": {
+						"description": "해당 examIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examIdx를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/update/ExamDetail/{examDetailIdx}": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 상세 수정",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 상세 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examDetailIdx",
+						"description": "관리자 시험 상세 수정 Path Variable : 시험 상세 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 시험 상세 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["examDate", "examRound"],
+							"properties": {
+								"examDate": {
+									"type": "string",
+									"description": "시험 날짜",
+									"example": "2021-12-15"
+								},
+								"examRound": {
+									"type": "integer",
+									"description": "시험 회차",
+									"example": 3
+								},
+								"isPublic": {
+									"type": "integer",
+									"description": "회차 공개여부 ( 0 : 비공개 , 1 : 공개 )",
+									"example": 1
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examDetailIdx를 형식에 맞게 입력해주세요."
+					},
+					"2051": {
+						"description": "해당 examDetailIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examDetailIdx를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/delete/ExamDetail/{examDetailIdx}": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 상세 삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 상세 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examDetailIdx",
+						"description": "관리자 시험 상세 삭제 Path Variable : 시험 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2050": {
+						"description": "examDetailIdx를 형식에 맞게 입력해주세요."
+					},
+					"2051": {
+						"description": "해당 examDetailIdx가 존재하지 않습니다."
+					},
+					"2052": {
+						"description": "examDetailIdx를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/examSortRef": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 구분 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 구분 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examSortLargeIdx": {
+												"type": "integer",
+												"description": "시험 대분류 (공기업, 자격증, 편입) 인덱스",
+												"example": 1
+											},
+											"examSortLargeName": {
+												"type": "string",
+												"description": "시험 대분류 (공기업, 자격증, 편입)",
+												"example": "자격증"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/examSort": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 종류 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 종류 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "examSortLargeIdx",
+						"description": "관리자 시험 종류 조회 query Variable : 시험 구분 인덱스 ",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examSortMediumIdx": {
+												"type": "integer",
+												"description": "시험 중분류 인덱스",
+												"example": 4
+											},
+											"examSortMediumName": {
+												"type": "string",
+												"description": "시험 중분류 ",
+												"example": "기사"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			},
+			"post": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 구분 / 종류 생성",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 구분 / 종류  생성 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 시험 구분 / 종류  생성 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["examSort", "examSortRef"],
+							"properties": {
+								"examSortName": {
+									"type": "string",
+									"description": "해당 시험 종류 이름",
+									"example": "기사"
+								},
+								"examSortRef": {
+									"type": "integer",
+									"description": "해당 시험종류의 상위 인덱스 최상위 카테고리는 값을 보내지않아도됨",
+									"example": 2
+								},
+								"examSortType": {
+									"type": "string",
+									"description": "시험 구분 : L, 시험 종류 : M",
+									"example": "M"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examSortIdx": {
+											"type": "integer",
+											"description": "생성된 시험 분류 인덱스",
+											"example": 68
+										},
+										"examSortRef": {
+											"type": "integer",
+											"description": "생성된 시험 상위 분류 인덱스",
+											"example": 2
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/examSortList/:isPublic": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 자격증/공기업 시험 분류 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 자격증/공기업 시험 분류 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "isPublic",
+						"description": "관리자 시험 종류 조회 path Variable : 0:자격증 1:공기업",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"largeExamIdx": {
+												"type": "integer",
+												"description": "시험 대분류 인덱스",
+												"example": 1
+											},
+											"largeExamName": {
+												"type": "string",
+												"description": "시험 대분류 이름",
+												"example": "자격증"
+											},
+											"mediumDatas": {
+												"type": "array",
+												"items": {
+													"type": "object",
+													"properties": {
+														"mediumExamIdx": {
+															"type": "integer",
+															"description": "시험 중분류 인덱스",
+															"example": 4
+														},
+														"mediumExamName": {
+															"type": "string",
+															"description": "시험 중분류 이름",
+															"example": "공사기사"
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/delete/examSort/{examSortIdx}": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 종류 삭제 ( 시험 구분, 시험 종류 상관 x ) ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 종류 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examSortIdx",
+						"description": "관리자 시험 종류 삭제 Path Variable : 시험 종류 인덱스 ( 시험 구분, 시험 종류 상관 x )",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2146": {
+						"description": "존재하지 않는 시험 구분입니다."
+					},
+					"2050": {
+						"description": "examSort를 형식에 맞게 입력해주세요."
+					},
+					"2052": {
+						"description": "examSort를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/update/examSort/{examSortIdx}": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 종류 수정 ( 시험 구분, 시험 종류 상관 x ) ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 종류 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examSortIdx",
+						"description": "관리자 시험 종류 수정 Path Variable : 시험 종류 인덱스 ( 시험 구분, 시험 종류 상관 x )",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 시험 종류 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["examSortName", "examSortRef", "examSortType"],
+							"properties": {
+								"examSortName": {
+									"type": "string",
+									"description": "수정할 시험 종류 이름",
+									"example": "기사"
+								},
+								"examSortRef": {
+									"type": "integer",
+									"description": "수정할 시험 종류 상위카테고리 인덱스",
+									"example": 1
+								},
+								"examSortType": {
+									"type": "string",
+									"description": "시험 종류 카테고리 ( 시험 구분 : L, 시험 종류 : M )",
+									"example": "M"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2146": {
+						"description": "존재하지 않는 시험 종류입니다."
+					},
+					"2050": {
+						"description": "examSort를 형식에 맞게 입력해주세요."
+					},
+					"2052": {
+						"description": "examSort를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/download/{examDetailIdx}": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "시험 회차 액셀 다운로드",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 회차 액셀 다운로드 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					}
+				]
+			}
+		},
+		"/admins/upload/image": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": "이미지 업로드",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 이미지 업로드 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 이미지 업로드 Request Body",
+						"required": true,
+						"schema": {
+							"type": "array",
+							"required": ["files"],
+							"properties": {
+								"files": {
+									"type": "string",
+									"description": "파일명",
+									"example": "C_EE_20200606-1_EE_MP-55_S-1.png"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/examName": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 이름 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 이름 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "examSortMediumIdx",
+						"description": "관리자 시험 이름 조회 query string : 자격증 구분 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "examSortLargeIdx",
+						"description": "관리자 시험 종류 수정 query string : 시험 구분 인덱스 ",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"description": "시험 정보",
+										"properties": {
+											"examIdx": {
+												"type": "integer",
+												"description": "시험 인덱스",
+												"example": 1
+											},
+											"examName": {
+												"type": "string",
+												"description": "시험 이름",
+												"example": "전기기사"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/examInfoList": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 시험 목록 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 시험 목록 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "examSortRef",
+						"description": "관리자 시험 목록 조회 Query Variable : 시험 대분류 (공기업, 자격증, 편입등의 인덱스)",
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "examSortIdx",
+						"description": "관리자 시험 목록 조회 Query Variable : 시험 중분류 (공사기사, 산업기사, 기능사, 기사 등의 인덱스)",
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "examName",
+						"description": "관리자 시험 목록 조회 Query Variable : 시험 이름 (전기자기학)",
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "search",
+						"description": "관리자 시험 목록 조회 Query Variable : 검색 단어",
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"description": "시험 정보",
+										"properties": {
+											"examIdx": {
+												"type": "integer",
+												"description": "시험 인덱스",
+												"example": 1
+											},
+											"examSortRef": {
+												"type": "string",
+												"description": "시험 대분류 (공기업, 자격증, 편입)",
+												"example": "자격증"
+											},
+											"examSort": {
+												"type": "string",
+												"description": "시험 중분류 (공기업, 공사기사, 산업기사, 기능사, 기사, 편입수학 등)",
+												"example": "기사"
+											},
+											"examName": {
+												"type": "string",
+												"description": "시험 이름",
+												"example": "전기기사"
+											},
+											"passScore": {
+												"type": "integer",
+												"description": "합격 점수",
+												"example": 60
+											},
+											"timeLimit": {
+												"type": "integer",
+												"description": "제한 시간",
+												"example": 150
+											},
+											"problemCount": {
+												"type": "integer",
+												"description": "문제 개수",
+												"example": 100
+											},
+											"accessLevel": {
+												"type": "integer",
+												"description": "권한 수준",
+												"example": 0
+											},
+											"examUrl": {
+												"type": "string",
+												"description": "시험 이미지 url",
+												"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png"
+											},
+											"questionType": {
+												"type": "string",
+												"description": "객관식 선지 유형",
+												"example": "객관식4지선다"
+											},
+											"questionCount": {
+												"type": "string",
+												"description": "객관식 선지 개수 ( 미사용 )",
+												"example": 4
+											},
+											"subjectList": {
+												"type": "array",
+												"description": "과목 리스트",
+												"items": {
+													"type": "object",
+													"properties": {
+														"examSubjectIdx": {
+															"type": "integer",
+															"description": "시험 과목 인덱스",
+															"example": 1
+														},
+														"examSubjectNum": {
+															"type": "integer",
+															"description": "과목 번호",
+															"example": 1
+														},
+														"examSubjectName": {
+															"type": "string",
+															"description": "과목 이름",
+															"example": "전자기학"
+														},
+														"passScore": {
+															"type": "integer",
+															"description": "합격 점수",
+															"example": 40
+														},
+														"examSubjectStatus": {
+															"type": "char",
+															"description": "시험 과목 상태",
+															"example": "N"
+														},
+														"examSubjectMultiStatus": {
+															"type": "char",
+															"description": "시험 과목 연결 상태",
+															"example": "N"
+														}
+													}
+												}
+											},
+											"examDetailList": {
+												"type": "array",
+												"description": "시험 회차 리스트",
+												"items": {
+													"type": "object",
+													"properties": {
+														"examDetailIdx": {
+															"type": "integer",
+															"description": "시험 상세 인덱스",
+															"example": 1
+														},
+														"date": {
+															"type": "String",
+															"description": "시험 날짜",
+															"example": "2021.03.07"
+														},
+														"examRound": {
+															"type": "integer",
+															"description": "시험 회차",
+															"example": "3"
+														},
+														"isPublic": {
+															"type": "integer",
+															"description": "공개 여부 ( 0 : 비공개, 1 : 공개 )",
+															"example": "1"
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/search/problem": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 문제 DB 검색",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "q",
+						"description": "문제 DB 검색 Query Variable : 검색 문자열 (ex1. 자속밀도 -강자성체 ex2. +무한 +도체)\n검색어 보낼 때 encodeURI를 해주셔야 +,-가 누락되지 않습니다.",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "isKatex",
+						"description": "문제 DB 검색 Query Variable : 사용자가 검색한 쿼리에 카텍스가 씌워져 있는지의 여부 (씌워져있으면 isKatex : 1, 일반 문자열이라면 isKatex를 보내지 않거나, isKatex=0)",
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"data": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"multipleProblemIdx": {
+														"type": "integer",
+														"description": "객관식 문제 인덱스",
+														"example": 107
+													},
+													"problem": {
+														"type": "string",
+														"description": "객관식 문제",
+														"example": "단면적\\,\\, 1000 {\\rm [ mm^2]},\\,\\, 길이 \\,\\,600 {\\rm [ mm]}인\\,\\, 강자성체의\\,\\, 철심에\\,\\, 자속밀도 \\,\\,B = 1 {\\rm [ Wb/m^2]}를 \\,\\, 만들려고\\,\\, 한다.\\,\\, 이\\,\\, 철심에\\,\\, 코일을\\,\\, 감아\\,\\, 전류를 \\,\\, 공급하였을\\,\\, 때\\,\\, 발생되는\\,\\, 기자력 {\\rm [AT]}은?\\\\\n(단,\\,\\, 철심의\\,\\, 비투자율은\\,\\, 600이라\\,\\, 한다)"
+													},
+													"problemImage": {
+														"type": "string",
+														"description": "문제 이미지 문자열",
+														"example": "\\includegraphics[]{https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png}"
+													},
+													"isKatex": {
+														"type": "integer",
+														"description": "카텍스 유무 (유 : 1, 무 : 0)",
+														"example": 1
+													}
+												}
+											}
+										},
+										"cost": {
+											"type": "float",
+											"description": "검색 소요 시간 (단위: 초)",
+											"example": 0.001
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/search/ExamSubject": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 과목 검색",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 과목 검색 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "examSubjectName",
+						"description": "관리자 과목 검색 Query Variable : 과목 이름 단어 / 전기를 검색하면 전기가 포함된 과목 이름을 가진 과목 리스트 반환",
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"description": "시험 과목 정보",
+										"properties": {
+											"examSubjectIdx": {
+												"type": "integer",
+												"description": "시험 과목 인덱스",
+												"example": 1
+											},
+											"examSubjectName": {
+												"type": "string",
+												"description": "과목 이름",
+												"example": "전기자기학"
+											},
+											"passScore": {
+												"type": "integer",
+												"description": "합격 점수",
+												"example": 60
+											},
+											"status": {
+												"type": "char",
+												"description": "과목 상태 ( N:삭제X, Y:삭제O )",
+												"example": "N"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/search/ExamSubject/{examIdx}": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "시험 인덱스로 관리자 과목 검색",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "시험 인덱스로 관리자 과목 검색 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"required": true,
+						"name": "examIdx",
+						"description": "시험 인덱스로 관리자 과목 검색 Path Variable : 시험 인덱스",
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"description": "시험 과목 정보",
+										"properties": {
+											"examSubjectIdx": {
+												"type": "integer",
+												"description": "시험 과목 인덱스",
+												"example": 1
+											},
+											"examSubjectName": {
+												"type": "string",
+												"description": "과목 이름",
+												"example": "전기자기학"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/userList": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 유저 목록 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 유저 목록 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"description": "시험 과목 정보",
+										"properties": {
+											"userIdx": {
+												"type": "integer",
+												"description": "사용자 인덱스",
+												"example": 1
+											},
+											"userName": {
+												"type": "string",
+												"description": "사용자 이름",
+												"example": "곽영일"
+											},
+											"userEmail": {
+												"type": "string",
+												"description": "사용자 이메일",
+												"example": "kyi232@naver.com"
+											},
+											"userPhoneNum": {
+												"type": "char",
+												"description": "유저 핸드폰 번호",
+												"example": "01012345678"
+											},
+											"dateOfBirth": {
+												"type": "integer",
+												"description": "사용자 생년월일",
+												"example": "960316"
+											},
+											"nickName": {
+												"type": "string",
+												"description": "사용자 닉네임",
+												"example": "애완곤충"
+											},
+											"createdAt": {
+												"type": "string",
+												"description": "사용자 생성 날짜",
+												"example": "1988-02-02"
+											},
+											"accessLevel": {
+												"type": "integer",
+												"description": "사용자 권한 수준",
+												"example": 0
+											},
+											"recommendCount": {
+												"type": "integer",
+												"description": "사용자 추천수",
+												"example": 0
+											},
+											"status": {
+												"type": "char",
+												"description": "삭제 여부(N:삭제X, O:삭제O, M:관리자)",
+												"example": "N"
+											},
+											"check": {
+												"type": "integer",
+												"description": "요청값 (무조건 0)",
+												"example": 0
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/searchUser": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 유저 검색",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 유저 검색 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "searchRef",
+						"description": "검색 구분 ( 이름 : userName , 이메일 : userEmail , 닉네임 : nickName )",
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "search",
+						"description": "검색 내용",
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"description": "시험 과목 정보",
+										"properties": {
+											"userIdx": {
+												"type": "integer",
+												"description": "사용자 인덱스",
+												"example": 1
+											},
+											"userName": {
+												"type": "string",
+												"description": "사용자 이름",
+												"example": "곽영일"
+											},
+											"userEmail": {
+												"type": "string",
+												"description": "사용자 이메일",
+												"example": "kyi232@naver.com"
+											},
+											"userPhoneNum": {
+												"type": "char",
+												"description": "유저 핸드폰 번호",
+												"example": "01012345678"
+											},
+											"dateOfBirth": {
+												"type": "integer",
+												"description": "사용자 생년월일",
+												"example": "960316"
+											},
+											"nickName": {
+												"type": "string",
+												"description": "사용자 닉네임",
+												"example": "애완곤충"
+											},
+											"createdAt": {
+												"type": "string",
+												"description": "사용자 생성 날짜",
+												"example": "1988-02-02"
+											},
+											"accessLevel": {
+												"type": "integer",
+												"description": "사용자 권한 수준",
+												"example": 0
+											},
+											"recommendCount": {
+												"type": "integer",
+												"description": "사용자 추천수",
+												"example": 0
+											},
+											"status": {
+												"type": "char",
+												"description": "삭제 여부(N:삭제X, O:삭제O, M:관리자)",
+												"example": "N"
+											},
+											"check": {
+												"type": "integer",
+												"description": "요청값 (무조건 0)",
+												"example": 0
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/userList/{userIdx}/userInfo": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 유저 상세 정보 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 유저 상세 정보 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "관리자 유저 상세 정보 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"description": "시험 과목 정보",
+										"properties": {
+											"userIdx": {
+												"type": "integer",
+												"description": "사용자 인덱스",
+												"example": 1
+											},
+											"userName": {
+												"type": "string",
+												"description": "사용자 이름",
+												"example": "곽영일"
+											},
+											"userEmail": {
+												"type": "string",
+												"description": "사용자 이메일",
+												"example": "kyi9592@ajou.ac.kr"
+											},
+											"userPassword": {
+												"type": "string",
+												"description": "사용자 비밀번호",
+												"example": "e6c8a653056fb68180610f62870ace098b6746de498939e7df1d37e1a770a23dff983334c1eb6f4b2b145aa266e3d39f85abba71a099c87883f765d9af1565ac"
+											},
+											"userPhoneNum": {
+												"type": "string",
+												"description": "사용자 휴대전화번호",
+												"example": "01056735172"
+											},
+											"dateOfBirth": {
+												"type": "integer",
+												"description": "사용자 생년월일",
+												"example": 19960316
+											},
+											"nickname": {
+												"type": "string",
+												"description": "사용자 닉네임",
+												"example": "영일님"
+											},
+											"createdAt": {
+												"type": "timestamp",
+												"description": "사용자 가입 날짜",
+												"example": "2021-07-06T00:59:07.000Z"
+											},
+											"updatedAt": {
+												"type": "timestamp",
+												"description": "사용자 업데이트 날짜",
+												"example": "2021-07-06T00:59:07.000Z"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/userList/:userIdx/userInfo": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 특정 유저 상태 변경",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 특정 유저 상태 변경 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "관리자 특정 유저 상태 변경 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 특정 유저 상태 변경 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["status"],
+							"properties": {
+								"status": {
+									"type": "char",
+									"description": "수정하려는 사용자 상태(N:삭제X, Y:삭제O, M:관리자)",
+									"example": "N"
+								},
+								"accessLevel": {
+									"type": "integer",
+									"description": "권한 수준 ( 0 ~ 5 )",
+									"example": 1
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/deleteUser/:userIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 특정 유저 삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 특정 유저 상태 변경 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "관리자 특정 유저 상태 변경 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/userList/:userIdx/boardList": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 특정 유저 게시글 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 특정 유저 게시글 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "관리자 특정 유저 게시글 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"isSuccess": {
+											"type": "boolean",
+											"description": "요청 성공 여부"
+										},
+										"code": {
+											"type": "integer",
+											"description": "응답 코드",
+											"example": 1000
+										},
+										"message": {
+											"type": "string",
+											"description": "응답 코드 메세지",
+											"example": "성공"
+										},
+										"result": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"boardIdx": {
+														"type": "integer",
+														"description": "게시글 인덱스",
+														"example": 310
+													},
+													"boardSort": {
+														"type": "string",
+														"description": "게시글 종류",
+														"example": "문제 오류"
+													},
+													"boardTitle": {
+														"type": "string",
+														"description": "게시글 제목",
+														"example": "제목입니다."
+													},
+													"status": {
+														"type": "char",
+														"description": "게시글 상태",
+														"example": "게시글 상태(Y:삭제, N:삭제X)"
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/userList/:userIdx/examRecordList": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 특정 유저 시험 기록 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 특정 유저 시험 기록 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "관리자 특정 유저 시험 기록 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examIdx": {
+												"type": "integer",
+												"description": "시험 인덱스",
+												"example": 1
+											},
+											"examName": {
+												"type": "string",
+												"description": "시험 이름",
+												"example": "전기기사"
+											},
+											"passScore": {
+												"type": "integer",
+												"description": "합격 점수",
+												"example": 60
+											},
+											"recordList": {
+												"type": "array",
+												"description": "시험 회차 로그 리스트",
+												"items": {
+													"type": "object",
+													"properties": {
+														"examDetailIdx": {
+															"type": "integer",
+															"description": "시험 회차(디테일) 인덱스",
+															"example": 27
+														},
+														"userExamRecordIdx": {
+															"type": "integer",
+															"description": "시험 기록 인덱스",
+															"example": 1061
+														},
+														"date": {
+															"type": "string",
+															"description": "시험 응시 날짜",
+															"example": "2021-08-26"
+														},
+														"time": {
+															"type": "string",
+															"description": "시험 응시 시각",
+															"example": "17:39"
+														},
+														"round": {
+															"type": "string",
+															"description": "시험 회차 이름",
+															"example": "2021-1회"
+														},
+														"score": {
+															"type": "integer",
+															"description": "사용자의 시험 점수",
+															"example": 196
+														},
+														"isPass": {
+															"type": "char",
+															"description": "합격 여부 ( Y : 합격, N : 불합격 )",
+															"example": "Y"
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/userList/{userIdx}/examRecordList/{userExamRecordIdx}": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 특정 유저 시험 기록 상세 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 특정 유저 시험 기록 상세 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "관리자 특정 유저 시험 기록 상세 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "userExamRecordIdx",
+						"description": "관리자 특정 유저 시험 기록 상세 조회 Path Variable : 사용자 시험 기록 로그 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examInfo": {
+											"type": "object",
+											"properties": {
+												"examIdx": {
+													"type": "integer",
+													"description": "시험 인덱스",
+													"example": 1
+												},
+												"examDetailIdx": {
+													"type": "integer",
+													"description": "시험 회차(디테일) 인덱스",
+													"example": 2
+												},
+												"examName": {
+													"type": "string",
+													"description": "시험 이름",
+													"example": "전기기사"
+												},
+												"date": {
+													"type": "string",
+													"description": "시험 날짜",
+													"example": "2020.06.06"
+												},
+												"examRound": {
+													"type": "integer",
+													"description": "시험 회차",
+													"example": 1
+												}
+											}
+										},
+										"subject": {
+											"type": "array",
+											"description": "시험 과목 리스트",
+											"items": {
+												"type": "object",
+												"properties": {
+													"examSubjectIdx": {
+														"type": "integer",
+														"description": "시험 과목 인덱스",
+														"example": 1
+													},
+													"examSubjectNum": {
+														"type": "integer",
+														"description": "시험에서의 과목 번호",
+														"example": 1
+													},
+													"examSubjectName": {
+														"type": "string",
+														"description": "과목 이름",
+														"example": "전기자기학"
+													},
+													"passScore": {
+														"type": "integer",
+														"description": "합격 점수",
+														"example": 40
+													},
+													"problems": {
+														"description": "문제 리스트",
+														"type": "array",
+														"items": {
+															"type": "object",
+															"properties": {
+																"problemIdx": {
+																	"type": "integer",
+																	"description": "시험 문제 인덱스",
+																	"example": "001201001"
+																},
+																"problemNum": {
+																	"type": "integer",
+																	"description": "문제 번호",
+																	"example": 1
+																},
+																"problem": {
+																	"type": "string",
+																	"description": "문제 내용",
+																	"example": "문제입니다."
+																},
+																"answerNum": {
+																	"type": "integer",
+																	"description": "정답 번호",
+																	"example": 3
+																},
+																"questions": {
+																	"type": "array",
+																	"items": {
+																		"type": "object",
+																		"properties": {
+																			"questionIdx": {
+																				"type": "integer",
+																				"description": "선지 인덱스",
+																				"example": 11100
+																			},
+																			"questionNum": {
+																				"type": "integer",
+																				"description": "선지 번호",
+																				"example": 1
+																			},
+																			"question": {
+																				"type": "string",
+																				"description": "선지 내용",
+																				"example": "자기회로의\\,\\, 길이에\\,\\, 비례"
+																			}
+																		}
+																	}
+																},
+																"provisionNum": {
+																	"type": "string",
+																	"description": "설비규정",
+																	"example": "112.2"
+																},
+																"solution": {
+																	"type": "string",
+																	"description": "해설 내용",
+																	"example": "\\lbrack 전기자기학 - 10.자기\\,\\, 회로 \\rbrack \n\\\\\n\\lbrack 전투수학(전기\\,\\, 수학\\,\\, 교재) - 10. 비례\\,\\, 반비례,\\,\\, 비례식 \\,\\, \\rbrack\n\\\\\n철심의\\,\\, 자기저항\\,\\, R_m = \\dfrac{l}{\\mu S}=\\dfrac{l}{\\mu_0 \\mu_s S}{\\rm [AT/Wb]}이므로\\,\\,\n길이(l,\\,\\, length)에\\,\\, 비례한다."
+																},
+																"isKatex": {
+																	"type": "integer",
+																	"description": "카텍스 유무 (유 : 1, 무 : 0)",
+																	"example": 1
+																},
+																"isDelete": {
+																	"type": "integer",
+																	"description": "삭제 여부",
+																	"example": 0
+																},
+																"userAnswer": {
+																	"type": "integer",
+																	"description": "사용자가 선택한 정답 번호",
+																	"example": 2
+																},
+																"isCorrect": {
+																	"type": "integer",
+																	"description": "정답 여부",
+																	"example": 1
+																}
+															}
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/userList/{userIdx}/reviewNote/exam": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 특정 유저 오답노트 시험 목록 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 특정 유저 오답노트 시험 목록 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "examSortIdx",
+						"description": "관리자 특정 유저 오답노트 시험 목록 조회 Query Variable : 시험 종류 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examIdx": {
+												"type": "integer",
+												"description": "시험 인덱스",
+												"example": 1
+											},
+											"examName": {
+												"type": "string",
+												"description": "시험 이름",
+												"example": "전기 기사"
+											},
+											"examDetailList": {
+												"type": "array",
+												"description": "시험 회차(디테일) 리스트",
+												"items": {
+													"type": "object",
+													"properties": {
+														"examDetailIdx": {
+															"type": "integer",
+															"description": "시험 회차(디테일) 인덱스",
+															"example": 28
+														},
+														"list": {
+															"type": "string",
+															"description": "시험, 년도, 회차, 문제개수",
+															"example": "전기기사 2021년 2회 (4)"
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/userList/{userIdx}/reviewNote/problem/{examDetailIdx}": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 특정 유저 오답노트 시험 문제 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 특정 유저 오답노트 시험 문제 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "관리자 특정 유저 오답노트 시험 문제 조회 Path Variable : 사용자 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "examDetailIdx",
+						"description": "관리자 특정 유저 오답노트 시험 문제 조회 Path Variable : 시험 회차(디테일) 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examInfo": {
+											"type": "object",
+											"properties": {
+												"examIdx": {
+													"type": "integer",
+													"description": "시험 인덱스",
+													"example": 1
+												},
+												"examDetailIdx": {
+													"type": "integer",
+													"description": "시험 회차(디테일) 인덱스",
+													"example": 2
+												},
+												"examName": {
+													"type": "string",
+													"description": "시험 이름",
+													"example": "전기기사"
+												},
+												"date": {
+													"type": "string",
+													"description": "시험 날짜",
+													"example": "2020.06.06"
+												},
+												"examRound": {
+													"type": "integer",
+													"description": "시험 회차",
+													"example": 1
+												},
+												"passScore": {
+													"type": "integer",
+													"description": "합격 점수",
+													"example": 60
+												},
+												"timeLimit": {
+													"type": "integer",
+													"description": "제한 시간",
+													"example": 150
+												},
+												"problemCount": {
+													"type": "integer",
+													"description": "오답 노트 속 시험의 문제 개수",
+													"example": 5
+												}
+											}
+										},
+										"problems": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"problemIdx": {
+														"type": "integer",
+														"description": "문제 인덱스",
+														"example": "001211001"
+													},
+													"problemNum": {
+														"type": "integer",
+														"description": "문제 번호",
+														"example": 1
+													},
+													"problem": {
+														"type": "string",
+														"description": "문제 내용",
+														"example": "비투자율\\,\\, \\mu_s =800,\\,\\, 원형\\,\\, 단면적이\\,\\,  S =10{\\rm [cm^2]},\\,\\, 평균\\,\\, 자로\\,\\, 길이\\,\\, l = 16\\pi \\times 10^{-2}{\\rm [m]}의\\,\\, 환상\\,\\, 철심에\\,\\, 600회의\\,\\, 코일을\\,\\, 감고\\,\\, 이\\,\\, 코일에\\,\\, 1{\\rm [A]}의\\,\\, 전류를\\,\\, 흘리면\\,\\, 환상\\,\\, 철심\\,\\, 내부의\\,\\, 자속은\\,\\, 몇\\,\\, {\\rm [Wb]}인가?\\\\ "
+													},
+													"answerNum": {
+														"type": "integer",
+														"description": "정답 번호",
+														"example": 1
+													},
+													"questions": {
+														"type": "array",
+														"items": {
+															"type": "object",
+															"properties": {
+																"questionIdx": {
+																	"type": "integer",
+																	"description": "선지 인덱스",
+																	"example": 11395
+																},
+																"questionNum": {
+																	"type": "integer",
+																	"description": "선지 번호",
+																	"example": 1
+																},
+																"question": {
+																	"type": "string",
+																	"description": "선지 내용",
+																	"example": "1.2 \\times 10^{-3}"
+																}
+															}
+														}
+													},
+													"provisionNum": {
+														"type": "string",
+														"description": "설비규정",
+														"example": "112.2"
+													},
+													"solution": {
+														"type": "string",
+														"description": "해설내용",
+														"example": "\\lbrack 전기자기학 - 09.진공\\,\\, 중의\\,\\, 정자계 \\rbrack \n\\\\\n\\lbrack 전투수학(전기\\,\\, 수학\\,\\, 교재) - 01. SI 접두어 단위 \\rbrack\n\\\\\n인덕턴스 계산\\,\\, \\phi = \\dfrac{\\mu_0 \\mu_s SNI}{l} = \\dfrac{(4\\pi \\times 10^{-7}) \\times 800 \\times (10 \\times 10^{-4}) \\times 600 \\times 1}{16\\pi \\times 10^{-2}} = 1.2 \\times 10^{-3} {\\rm [Wb]}\n\\\\\nTip)\n\\\\\n1{\\rm [cm^2]} = 1 \\times 10^{-4}{\\rm [m^2]}"
+													},
+													"subjectInfo": {
+														"tyoe": "string",
+														"description": "과목 정보 (번호, 이름)",
+														"example": "제1과목 전기자기학"
+													},
+													"isKatex": {
+														"type": "integer",
+														"description": "카텍스 유무 (유 : 1, 무 : 0)",
+														"example": 1
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2053": {
+						"description": "examDetailIdx를 입력해주세요"
+					},
+					"2054": {
+						"description": "examDetailIdx를 형식에 맞게 입력해주세요"
+					},
+					"2055": {
+						"description": "해당 examDetailIdx가 존재하지 않습니다."
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/calendar": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "달력 회사 조회",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "date",
+						"description": "달력 회사 조회 Query Variable : 날짜 (ex. 2021-09-06)",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"enterpriseIdx": {
+												"type": "integer",
+												"description": "회사 인덱스",
+												"example": 1
+											},
+											"enterpriseName": {
+												"type": "string",
+												"description": "회사 이름",
+												"example": "경상국립대학교병원"
+											},
+											"description": {
+												"type": "string",
+												"description": "시작(startDate), 끝(endDate), 시험(examDate)",
+												"example": "endDate"
+											},
+											"date": {
+												"type": "string",
+												"description": "날짜",
+												"example": "2021-09-06"
+											},
+											"content": {
+												"type": "string",
+												"description": "공고문 내용",
+												"example": "경상국립대학교병원 시설기술직 채용공고"
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2151": {
+						"description": "날짜를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/calendar/{enterpriseIdx}": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "달력 회사 상세 상세 조회",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "date",
+						"description": "달력 회사 상세 조회 Query Variable : 회사 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"enterpriseIdx": {
+											"type": "integer",
+											"description": "회사 인덱스",
+											"example": 1
+										},
+										"enterpriseName": {
+											"type": "string",
+											"description": "회사 이름",
+											"example": "경상국립대학교병원"
+										},
+										"link": {
+											"type": "string",
+											"description": "회사 공고 링크",
+											"example": "http://recruit.gnuh.co.kr"
+										},
+										"content": {
+											"type": "string",
+											"description": "공고문 내용",
+											"example": "경상국립대학교병원 시설기술직 채용공고"
+										},
+										"image": {
+											"type": "string",
+											"description": "첨부 파일 이미지",
+											"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/calendar/01.png"
+										},
+										"startDate": {
+											"type": "string",
+											"description": "모집 시작 날짜",
+											"example": "2021-08-31"
+										},
+										"endDate": {
+											"type": "string",
+											"description": "모집 끝 날짜",
+											"example": "2021-09-06"
+										},
+										"examDate": {
+											"type": "array",
+											"items": {
+												"type": "string",
+												"description": "시험일 리스트",
+												"example": "2021-09-24"
+											}
+										},
+										"education": {
+											"type": "string",
+											"description": "고졸 or 대졸",
+											"example": "대졸"
+										},
+										"commonCertificate": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"commonCertificateName": {
+														"type": "string",
+														"description": "공통 자격증 이름",
+														"example": "컴퓨터 활용능력 자격증 1급"
+													},
+													"category": {
+														"type": "string",
+														"description": "IT or 한국사 or 한국어 등",
+														"example": "IT"
+													},
+													"extraPoint": {
+														"type": "string",
+														"description": "가점",
+														"example": "0.03"
+													}
+												}
+											}
+										},
+										"professionalCertificate": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"professionalCertificateName": {
+														"type": "string",
+														"description": "기술자격증/자격요건 이름",
+														"example": "전기기사"
+													},
+													"isEssential": {
+														"type": "integer",
+														"description": "필수 여부 (필수 : 1, 필수X : 0)",
+														"example": 1
+													},
+													"extraPoint": {
+														"type": "string",
+														"description": "가점",
+														"example": "0.03"
+													}
+												}
+											}
+										},
+										"foreignLanguage": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"foreignLanguageName": {
+														"type": "string",
+														"description": "어학 요건 이름",
+														"example": "TOEIC"
+													},
+													"passScore": {
+														"type": "integer",
+														"description": "최소 점수",
+														"example": 700
+													},
+													"description": {
+														"type": "string",
+														"description": "필수, 가산점, 환산점수 등의 정보",
+														"example": "160이상 가산점"
+													},
+													"extraPoint": {
+														"type": "string",
+														"description": "가점",
+														"example": "6.5"
+													},
+													"replace": {
+														"type": "integer",
+														"description": "대체 가능 여부(1:대체 가능, 0: 대체 불가능)",
+														"example": 1
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/search": {
+			"get": {
+				"tags": ["Restore"],
+				"summary": "복원 DB 검색",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "q",
+						"description": "복원 DB 검색 Query Variable : 검색 문자열 (ex1. 자속밀도 -강자성체 ex2. +무한 +도체)\n검색어 보낼 때 encodeURI를 해주셔야 +,-가 누락되지 않습니다.",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "isKatex",
+						"description": "복원 DB 검색 Query Variable : 사용자가 검색한 쿼리에 카텍스가 씌워져 있는지의 여부 (씌워져있으면 isKatex : 1, 일반 문자열이라면 isKatex를 보내지 않거나, isKatex=0)",
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"data": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"multipleProblemIdx": {
+														"type": "integer",
+														"description": "객관식 문제 인덱스",
+														"example": 107
+													},
+													"problem": {
+														"type": "string",
+														"description": "객관식 문제",
+														"example": "단면적\\,\\, 1000 {\\rm [ mm^2]},\\,\\, 길이 \\,\\,600 {\\rm [ mm]}인\\,\\, 강자성체의\\,\\, 철심에\\,\\, 자속밀도 \\,\\,B = 1 {\\rm [ Wb/m^2]}를 \\,\\, 만들려고\\,\\, 한다.\\,\\, 이\\,\\, 철심에\\,\\, 코일을\\,\\, 감아\\,\\, 전류를 \\,\\, 공급하였을\\,\\, 때\\,\\, 발생되는\\,\\, 기자력 {\\rm [AT]}은?\\\\\n(단,\\,\\, 철심의\\,\\, 비투자율은\\,\\, 600이라\\,\\, 한다)"
+													},
+													"problemImage": {
+														"type": "string",
+														"description": "문제 이미지 문자열",
+														"example": "\\includegraphics[]{https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png}"
+													},
+													"isKatex": {
+														"type": "integer",
+														"description": "카텍스 유무 (유 : 1, 무 : 0)",
+														"example": 1
+													}
+												}
+											}
+										},
+										"cost": {
+											"type": "float",
+											"description": "검색 소요 시간 (단위: 초)",
+											"example": 0.001
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/search/detail": {
+			"post": {
+				"tags": ["Restore"],
+				"summary": "복원 DB 문제 상세 검색",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "문제 상세 검색 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["multipleProblemIdx"],
+							"properties": {
+								"multipleProblemIdx": {
+									"type": "array",
+									"items": {
+										"type": "integer",
+										"example": 32
+									},
+									"description": "문제 인덱스 리스트 (ex. [1,3,70])"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"multipleProblemIdx": {
+												"type": "integer",
+												"description": "객관식 문제 인덱스",
+												"example": 32
+											},
+											"problem": {
+												"type": "string",
+												"description": "객관식 문제",
+												"example": "사고,\\,\\, 정전\\,\\, 등의\\,\\, 중대한\\,\\, 영향을\\,\\, 받는\\,\\, 지역에서\\,\\,\n정전과\\,\\, 동시에\\,\\, 자동적으로\\,\\, 예비전원용\\,\\,\n배전선로로\\,\\, 전환하는\\,\\, 장치는?"
+											},
+											"problemImage": {
+												"type": "string",
+												"description": "문제 이미지 문자열",
+												"example": "\\includegraphics[]{https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png}"
+											},
+											"isKatex": {
+												"type": "integer",
+												"description": "카텍스 유무 (유 : 1, 무 : 0)",
+												"example": 1
+											},
+											"questions": {
+												"type": "array",
+												"items": {
+													"type": "object",
+													"properties": {
+														"questionIdx": {
+															"type": "integer",
+															"description": "선지 인덱스",
+															"example": 11915
+														},
+														"questionNum": {
+															"type": "integer",
+															"description": "선지 번호",
+															"example": 2
+														},
+														"question": {
+															"type": "string",
+															"description": "선지 내용",
+															"example": "리클로저(Recloser)"
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores": {
+			"post": {
+				"tags": ["Restore"],
+				"summary": "복원 담기",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 담기 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "복원 담기 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["restoreExamDetailIdx, multipleProblemIdx"],
+							"properties": {
+								"multipleProblemIdx": {
+									"type": "integer",
+									"description": "객관식 문제 인덱스",
+									"example": 23
+								},
+								"restoreExamDetailIdx": {
+									"type": "integer",
+									"description": "복원 시험 회차 인덱스",
+									"example": 14
+								},
+								"comment": {
+									"type": "string",
+									"description": "복원 담기할 때 user가 남기는 comment"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/custom": {
+			"post": {
+				"tags": ["Restore"],
+				"summary": "복원 문제 생성하기",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 문제 생성 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "복원 문제 생성 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["problemNum", "problem", "question", "comment", "restoreExamDetailIdx", "imageUrl"],
+							"properties": {
+								"problemNum": {
+									"type": "integer",
+									"description": "문제 번호",
+									"example": 1
+								},
+								"problem": {
+									"type": "string",
+									"description": "문제 내용",
+									"example": "정전 용량 블라블라"
+								},
+								"question": {
+									"type": "array",
+									"items": {
+										"type": "string",
+										"description": "선지 내용",
+										"example": "234"
+									}
+								},
+								"comment": {
+									"type": "string",
+									"description": "코멘트",
+									"example": "이거 확실해요"
+								},
+								"restoreExamDetailIdx": {
+									"type": "string",
+									"description": "복원 시험 회차 인덱스",
+									"example": 13
+								},
+								"imageUrl": {
+									"type": "array",
+									"items": {
+										"type": "string",
+										"description": "이미지 링크",
+										"example": "amazon s3"
+									}
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/board/:restoreExamDetailIdx/latest": {
+			"get": {
+				"tags": ["Restore"],
+				"summary": "복원 현황 최신순으로 보기",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 현황 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restoreExamDetailIdx",
+						"description": "복원 현황 Path Variable : 복원 시험 회차 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"multipleProblemIdx": {
+												"type": "integer",
+												"description": "객관식 문제 인덱스",
+												"example": 32
+											},
+											"problem": {
+												"type": "string",
+												"description": "객관식 문제",
+												"example": "사고,\\,\\, 정전\\,\\, 등의\\,\\, 중대한\\,\\, 영향을\\,\\, 받는\\,\\, 지역에서\\,\\,\n정전과\\,\\, 동시에\\,\\, 자동적으로\\,\\, 예비전원용\\,\\,\n배전선로로\\,\\, 전환하는\\,\\, 장치는?"
+											},
+											"problemImage": {
+												"type": "string",
+												"description": "문제 이미지 문자열",
+												"example": "\\includegraphics[]{https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png}"
+											},
+											"isKatex": {
+												"type": "integer",
+												"description": "카텍스 유무 (유 : 1, 무 : 0)",
+												"example": 1
+											},
+											"likes": {
+												"type": "integer",
+												"description": "좋아요 개수",
+												"example": 3
+											},
+											"restorationIdx": {
+												"type": "integer",
+												"description": "복원 현황 인덱스",
+												"example": 6
+											},
+											"userLike": {
+												"type": "integer",
+												"description": "사용자가 문제에 대해 좋아요를 누른 상태 : 1, 누르지 않은 상태 : 0",
+												"example": "0"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/board/:restoreExamDetailIdx/popular": {
+			"get": {
+				"tags": ["Restore"],
+				"summary": "복원 현황 인기순으로 보기",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 현황 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restoreExamDetailIdx",
+						"description": "복원 현황 Path Variable : 복원 시험 회차 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"multipleProblemIdx": {
+												"type": "integer",
+												"description": "객관식 문제 인덱스",
+												"example": 32
+											},
+											"problem": {
+												"type": "string",
+												"description": "객관식 문제",
+												"example": "사고,\\,\\, 정전\\,\\, 등의\\,\\, 중대한\\,\\, 영향을\\,\\, 받는\\,\\, 지역에서\\,\\,\n정전과\\,\\, 동시에\\,\\, 자동적으로\\,\\, 예비전원용\\,\\,\n배전선로로\\,\\, 전환하는\\,\\, 장치는?"
+											},
+											"problemImage": {
+												"type": "string",
+												"description": "문제 이미지 문자열",
+												"example": "\\includegraphics[]{https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png}"
+											},
+											"isKatex": {
+												"type": "integer",
+												"description": "카텍스 유무 (유 : 1, 무 : 0)",
+												"example": 1
+											},
+											"likes": {
+												"type": "integer",
+												"description": "좋아요 개수",
+												"example": 3
+											},
+											"restorationIdx": {
+												"type": "integer",
+												"description": "복원 현황 인덱스",
+												"example": 6
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/examList": {
+			"get": {
+				"tags": ["Restore"],
+				"summary": "복원 시험 목록 조회",
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examSortList": {
+												"type": "array",
+												"items": {
+													"type": "object",
+													"properties": {
+														"examSortRef": {
+															"type": "String",
+															"description": "대분류",
+															"example": "공기업"
+														},
+														"examSort": {
+															"type": "String",
+															"description": "중분류",
+															"example": ["전기"]
+														}
+													}
+												}
+											},
+											"examList": {
+												"type": "array",
+												"items": {
+													"type": "object",
+													"properties": {
+														"restoreExamDetailIdx": {
+															"type": "integer",
+															"description": "시험 복원 상세 인덱스",
+															"example": 1
+														},
+														"examIdx": {
+															"type": "integer",
+															"description": "시험 인덱스",
+															"example": 11
+														},
+														"examName": {
+															"type": "string",
+															"description": "시험 이름",
+															"example": "한국동서발전(고졸-전기직)"
+														},
+														"restoreExamDate": {
+															"type": "string",
+															"description": "복원 시험 날짜",
+															"example": "2021-01-05T15:00:00.000Z"
+														},
+														"restoreExamRound": {
+															"type": "String",
+															"description": "시험 회차",
+															"example": "하반기"
+														},
+														"thumbnail": {
+															"type": "string",
+															"description": "시험 회차 url",
+															"example": "https://engineeouploadimage.s3.ap-northeast-2.amazonaws.com/EWP.png"
+														},
+														"examSortIdx": {
+															"type": "integer",
+															"description": "시험 구분 인덱스",
+															"example": 17
+														},
+														"examSortRef": {
+															"type": "string",
+															"description": "시험 종류",
+															"example": "공기업"
+														},
+														"isPublic": {
+															"type": "integer",
+															"description": "공개 여부 ( 0 : 비공개, 1 : 공개 )",
+															"example": 1
+														},
+														"examSort": {
+															"type": "string",
+															"description": "시험 종류",
+															"example": "전기"
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/restoration/like": {
+			"post": {
+				"tags": ["Restore"],
+				"summary": "복원 문제 좋아요",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 문제 좋아요 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "복원 문제 좋아요 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["restorationIdx"],
+							"properties": {
+								"restorationIdx": {
+									"type": "integer",
+									"description": "복원 문제 인덱스",
+									"example": 2
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"userLike": {
+											"type": "boolean",
+											"description": "유저 좋아요 체크 여부",
+											"example": true
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/restoration/comment/like": {
+			"post": {
+				"tags": ["Restore"],
+				"summary": "복원 댓글 좋아요",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 댓글 좋아요 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "복원 댓글 좋아요 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["restorationCommentIdx"],
+							"properties": {
+								"restorationCommentIdx": {
+									"type": "integer",
+									"description": "복원 댓글 인덱스",
+									"example": 2
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"userLike": {
+											"type": "boolean",
+											"description": "유저 좋아요 체크 여부",
+											"example": true
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/upload/restoration/comment": {
+			"post": {
+				"tags": ["Restore"],
+				"summary": "복원 문제 댓글 생성",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 문제 댓글 생성 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "복원 문제 댓글 생성 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["restorationIdx", "comment"],
+							"properties": {
+								"restorationIdx": {
+									"type": "integer",
+									"description": "복원 문제 인덱스",
+									"example": 1
+								},
+								"comment": {
+									"type": "string",
+									"description": "복원 문제 댓글 내용",
+									"example": "1번 보기에 4가 1로 나왔습니다."
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/restoration/:restorationIdx/comment": {
+			"get": {
+				"tags": ["Restore"],
+				"summary": "복원 문제 댓글 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 문제 댓글 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restorationIdx",
+						"description": "복원 문제 댓글 조회 Path Variable : Restoration Index",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "order",
+						"description": "복원 문제 댓글 조회 Query Variable : 정렬 기준(popular(인기순), recent(최신순) : 인기순으로 정렬하고 싶으시면 query를 설정하지 않거나 'popular'를, 최신순으로 정렬하고 싶으시면 'recent'를 값으로 설정하면 됩니다.)",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "top",
+						"description": "복원 문제 댓글 조회 Query Variable : 베스트 댓글 개수",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"restorationCommentIdx": {
+												"type": "integer",
+												"description": "복원 문제 댓글 인덱스",
+												"example": 1
+											},
+											"nickname": {
+												"type": "string",
+												"description": "댓글 작성자",
+												"example": "닉네임"
+											},
+											"userIdx": {
+												"type": "integer",
+												"description": "댓글 작성자 인덱스",
+												"example": 7
+											},
+											"comment": {
+												"type": "string",
+												"description": "댓글 내용",
+												"example": "문제의 전압값이 다릅니다."
+											},
+											"updated": {
+												"type": "string",
+												"description": "작성 날짜",
+												"example": "21/10/08 03:33:15"
+											},
+											"commentLike": {
+												"type": "integer",
+												"description": "댓글 좋아요 갯수",
+												"example": 4
+											},
+											"userLike": {
+												"type": "integer",
+												"description": "사용자가 문제에 대해 좋아요를 누른 상태 : 1, 누르지 않은 상태 : 0",
+												"example": "0"
+											},
+											"isBestComment": {
+												"type": "integer",
+												"description": "해당 댓글이 베스트 댓글인지의 여부 ( 1 : 베스트 댓글, 0 : 일반 댓글)",
+												"example": "1"
+											},
+											"isEdited": {
+												"type": "integer",
+												"description": "수정 여부 (1:수정됨, 0:수정되지 않음)",
+												"example": 1
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/update/restoration/comment/:restorationCommentIdx": {
+			"patch": {
+				"tags": ["Restore"],
+				"summary": "복원 문제 댓글 수정",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 문제 댓글 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restorationCommentIdx",
+						"description": "복원 문제 댓글 수정 Path Variable : 복원 문제 댓글 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "복원 문제 댓글 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["comment"],
+							"properties": {
+								"comment": {
+									"type": "string",
+									"description": "댓글 내용",
+									"example": "수정한 댓글 내용"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/delete/restoration/comment/:restorationCommentIdx": {
+			"patch": {
+				"tags": ["Restore"],
+				"summary": "복원 문제 댓글 삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 문제 댓글 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restorationCommentIdx",
+						"description": "복원 문제 댓글 삭제 Path Variable : 복원 문제 댓글 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/board/:restoreExamDetailIdx/:restorationIdx": {
+			"get": {
+				"tags": ["Restore"],
+				"summary": "복원 현황 상세 보기",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 현황 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restoreExamDetailIdx",
+						"description": "복원 현황 Path Variable : 복원 시험 회차 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "restorationIdx",
+						"description": "복원 현황 Path Variable : 복원 현황 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"multipleProblemIdx": {
+											"type": "integer",
+											"description": "객관식 문제 인덱스",
+											"example": 32
+										},
+										"problem": {
+											"type": "string",
+											"description": "객관식 문제",
+											"example": "사고,\\,\\, 정전\\,\\, 등의\\,\\, 중대한\\,\\, 영향을\\,\\, 받는\\,\\, 지역에서\\,\\,\n정전과\\,\\, 동시에\\,\\, 자동적으로\\,\\, 예비전원용\\,\\,\n배전선로로\\,\\, 전환하는\\,\\, 장치는?"
+										},
+										"problemImage": {
+											"type": "string",
+											"description": "문제 이미지 문자열",
+											"example": "\\includegraphics[]{https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png}"
+										},
+										"isKatex": {
+											"type": "integer",
+											"description": "카텍스 유무 (유 : 1, 무 : 0)",
+											"example": 1
+										},
+										"questions": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"questionIdx": {
+														"type": "integer",
+														"description": "선지 인덱스",
+														"example": 11915
+													},
+													"questionNum": {
+														"type": "integer",
+														"description": "선지 번호",
+														"example": 2
+													},
+													"question": {
+														"type": "string",
+														"description": "선지 내용",
+														"example": "리클로저(Recloser)"
+													}
+												}
+											}
+										},
+										"userLike": {
+											"type": "integer",
+											"description": "사용자가 문제에 대해 좋아요를 누른 상태 : 1, 누르지 않은 상태 : 0",
+											"example": "0"
+										},
+										"likes": {
+											"type": "integer",
+											"description": "해당 문제에 대한 좋아요 수",
+											"example": "2"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/restoration/:restorationIdx/comment/:top": {
+			"get": {
+				"tags": ["Restore"],
+				"summary": "복원 문제 베스트 댓글 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 문제 댓글 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restorationIdx",
+						"description": "복원 문제 댓글 조회 Path Variable : Restoration Index",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "path",
+						"name": "top",
+						"description": "복원 문제 댓글 조회 Path Variable : 베스트 댓글 개수",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"restorationCommentIdx": {
+												"type": "integer",
+												"description": "복원 문제 댓글 인덱스",
+												"example": 1
+											},
+											"nickname": {
+												"type": "string",
+												"description": "댓글 작성자",
+												"example": "닉네임"
+											},
+											"userIdx": {
+												"type": "integer",
+												"description": "댓글 작성자 인덱스",
+												"example": "1699"
+											},
+											"comment": {
+												"type": "string",
+												"description": "댓글 내용",
+												"example": "문제의 전압값이 다릅니다."
+											},
+											"updated": {
+												"type": "string",
+												"description": "작성 날짜",
+												"example": "21/10/08 03:33:15"
+											},
+											"commentLike": {
+												"type": "integer",
+												"description": "댓글 좋아요 갯수",
+												"example": 4
+											},
+											"userLike": {
+												"type": "integer",
+												"description": "사용자가 문제에 대해 좋아요를 누른 상태 : 1, 누르지 않은 상태 : 0",
+												"example": "0"
+											},
+											"isEdited": {
+												"type": "integer",
+												"description": "수정 여부 (1:수정됨, 0:수정되지 않음)",
+												"example": 1
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/exams/{examIdx}/examUrl": {
+			"get": {
+				"tags": ["Exam"],
+				"summary": "시험 사진 Url 조회",
+				"parameters": [
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "시험 사진 Url 조회 Path Variable : exam Index",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examIdx": {
+											"type": "integer",
+											"description": "시험 인덱스",
+											"example": 800
+										},
+										"examUrl": {
+											"type": "String",
+											"description": "시험 사진 Url",
+											"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/restore/EWP.png"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"admins/restore": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "복원 시험 목록 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "회원정보 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"restoreDateList": {
+											"type": "array",
+											"description": "복원 년도 리스트",
+											"items": {
+												"type": "integer",
+												"description": "복원 시험 년도 리스트",
+												"example": "2021"
+											}
+										},
+										"restoreRoundList": {
+											"type": "array",
+											"description": "복원 회차 리스트",
+											"items": {
+												"type": "string",
+												"description": "복원 회차 리스트",
+												"example": "하반기"
+											}
+										},
+										"restoreExamSortRefList": {
+											"type": "array",
+											"description": "복원 시험 종류",
+											"items": {
+												"type": "string",
+												"description": "복원 시험 종류 리스트",
+												"example": "공기업"
+											}
+										},
+										"restoreExamDetailList": {
+											"type": "array",
+											"description": "복원 시험 목록 리스트",
+											"items": {
+												"type": "object",
+												"properties": {
+													"restoreExamDetailIdx": {
+														"type": "integer",
+														"description": "복원 시험 인덱스",
+														"example": 1
+													},
+													"restoreExamDate": {
+														"type": "string",
+														"description": "복원 시험 날짜",
+														"example": "2021-10-07"
+													},
+													"restoreExamRound": {
+														"type": "string",
+														"description": "복원 회차",
+														"example": "하반기"
+													},
+													"examName": {
+														"type": "string",
+														"description": "시험 이름",
+														"example": "한국 전력공사"
+													},
+													"examSort": {
+														"type": "string",
+														"description": "시험 종류",
+														"example": "공기업"
+													},
+													"examSortRef": {
+														"type": "string",
+														"description": "시험 구분",
+														"example": "고졸직"
+													},
+													"isPublic": {
+														"type": "integer",
+														"description": " 공개 여부 ( 0 : 비공개 , 1 : 공개 ) ",
+														"example": 0
+													},
+													"thumbnail": {
+														"type": "String",
+														"description": " 복원 회차 썸네일 Url ( 카카오톡 공유용 ) ",
+														"example": "\\includegraphics[]{https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png}"
+													},
+													"unAuthSum": {
+														"type": "integer",
+														"description": "미인증 합계",
+														"example": 11
+													},
+													"customSum": {
+														"type": "integer",
+														"description": "유저 생성 복원 문제 합계",
+														"example": 2
+													},
+													"restorationSum": {
+														"type": "integer",
+														"description": "유저 검색 복원 문제 합계",
+														"example": 5
+													}
+												}
+											}
+										}
+									},
+									"items": {
+										"type": "object",
+										"properties": {
+											"problems": {
+												"type": "array",
+												"description": "해당 시험 회차 복원에 담긴 문제",
+												"items": {
+													"type": "object",
+													"properties": {
+														"multipleProblemBySearchIdx": {
+															"type": "integer",
+															"description": "복원 문제 인덱스",
+															"example": 1
+														},
+														"multipleProblemIdx": {
+															"type": "integer",
+															"description": "문제 인덱스",
+															"example": 25
+														},
+														"problem": {
+															"type": "string",
+															"description": "문제",
+															"example": "질문입니다."
+														},
+														"problemImage": {
+															"type": "string",
+															"description": "문제 이미지",
+															"example": "includegraphics[]{https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png}"
+														},
+														"questions": {
+															"type": "array",
+															"items": {
+																"type": "object",
+																"properties": {
+																	"questionIdx": {
+																		"type": "integer",
+																		"description": "선지 인덱스",
+																		"example": 11099
+																	},
+																	"questionNum": {
+																		"type": "integer",
+																		"description": "선지 번호",
+																		"example": 1
+																	},
+																	"question": {
+																		"type": "string",
+																		"description": "선지 내용",
+																		"example": "mged"
+																	}
+																}
+															}
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/restore": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": "복원 시험 목록 검색",
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "복원 시험 목록 조회 Request query",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"properties": {
+								"year": {
+									"type": "integer",
+									"example": 2021,
+									"description": " 시험 년도 ",
+									"required": false
+								},
+								"round": {
+									"type": "String",
+									"example": "하반기",
+									"description": " 시험 일정 상반기 or 하반기 / 1 or 2 or 3 ",
+									"required": false
+								},
+								"examSortRef": {
+									"type": "String",
+									"example": "공기업",
+									"description": " 시험 종류 ",
+									"required": false
+								},
+								"examSort": {
+									"type": "String",
+									"example": "고졸직",
+									"description": " 시험 구분 ",
+									"required": false
+								},
+								"examName": {
+									"type": "String",
+									"example": "코레일",
+									"description": "복원 시험 이름 ",
+									"required": false
+								},
+								"search": {
+									"type": "String",
+									"example": "전력",
+									"description": " 검색할 시험 이름 ",
+									"required": false
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"restoreDateList": {
+											"type": "array",
+											"description": "복원 년도 리스트",
+											"items": {
+												"type": "integer",
+												"description": "복원 시험 년도 리스트",
+												"example": "2021"
+											}
+										},
+										"restoreRoundList": {
+											"type": "array",
+											"description": "복원 회차 리스트",
+											"items": {
+												"type": "string",
+												"description": "복원 회차 리스트",
+												"example": "하반기"
+											}
+										},
+										"restoreExamSortRefList": {
+											"type": "array",
+											"description": "복원 시험 종류",
+											"items": {
+												"type": "string",
+												"description": "복원 시험 종류 리스트",
+												"example": "공기업"
+											}
+										},
+										"restoreExamDetailList": {
+											"type": "array",
+											"description": "복원 시험 목록 리스트",
+											"items": {
+												"type": "object",
+												"properties": {
+													"restoreExamDetailIdx": {
+														"type": "integer",
+														"description": "복원 시험 인덱스",
+														"example": 1
+													},
+													"restoreExamDate": {
+														"type": "string",
+														"description": "복원 시험 날짜",
+														"example": "2021-10-07"
+													},
+													"restoreExamRound": {
+														"type": "string",
+														"description": "복원 회차",
+														"example": "하반기"
+													},
+													"examName": {
+														"type": "string",
+														"description": "시험 이름",
+														"example": "한국 전력공사"
+													},
+													"examSort": {
+														"type": "string",
+														"description": "시험 종류",
+														"example": "공기업"
+													},
+													"examSortRef": {
+														"type": "string",
+														"description": "시험 구분",
+														"example": "고졸직"
+													},
+													"status": {
+														"type": "string",
+														"description": "해당 시험 공개여부 ( N : 공개 , Y : 비공개 )",
+														"example": "Y"
+													},
+													"thumbnail": {
+														"type": "String",
+														"description": " 복원 회차 썸네일 Url ( 카카오톡 공유용 ) ",
+														"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png"
+													},
+													"isPublic": {
+														"type": "integer",
+														"description": " 공개 여부 ( 0 : 비공개 , 1 : 공개 ) ",
+														"example": 0
+													},
+													"unAuthSum": {
+														"type": "integer",
+														"description": "미인증 합계",
+														"example": 11
+													},
+													"customSum": {
+														"type": "integer",
+														"description": "유저 생성 복원 문제 합계",
+														"example": 2
+													},
+													"restorationSum": {
+														"type": "integer",
+														"description": "유저 검색 복원 문제 합계",
+														"example": 5
+													}
+												}
+											}
+										}
+									},
+									"items": {
+										"type": "object",
+										"properties": {
+											"problems": {
+												"type": "array",
+												"description": "해당 시험 회차 복원에 담긴 문제",
+												"items": {
+													"type": "object",
+													"properties": {
+														"multipleProblemBySearchIdx": {
+															"type": "integer",
+															"description": "복원 문제 인덱스",
+															"example": 1
+														},
+														"multipleProblemIdx": {
+															"type": "integer",
+															"description": "문제 인덱스",
+															"example": 25
+														},
+														"problem": {
+															"type": "string",
+															"description": "문제",
+															"example": "질문입니다."
+														},
+														"problemImage": {
+															"type": "string",
+															"description": "문제 이미지",
+															"example": "includegraphics[]{https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png}"
+														},
+														"questions": {
+															"type": "array",
+															"items": {
+																"type": "object",
+																"properties": {
+																	"questionIdx": {
+																		"type": "integer",
+																		"description": "선지 인덱스",
+																		"example": 11099
+																	},
+																	"questionNum": {
+																		"type": "integer",
+																		"description": "선지 번호",
+																		"example": 1
+																	},
+																	"question": {
+																		"type": "string",
+																		"description": "선지 내용",
+																		"example": "mged"
+																	}
+																}
+															}
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/calendars": {
+			"get": {
+				"tags": ["Calendar"],
+				"summary": "달력 회사 조회",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "date",
+						"description": "달력 회사 조회 Query Variable : 날짜 (ex. 2021-09-06)",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"enterpriseIdx": {
+												"type": "integer",
+												"description": "회사 인덱스",
+												"example": 1
+											},
+											"enterpriseName": {
+												"type": "string",
+												"description": "회사 이름",
+												"example": "경상국립대학교병원"
+											},
+											"description": {
+												"type": "string",
+												"description": "시작(startDate), 끝(endDate), 시험(examDate)",
+												"example": "endDate"
+											},
+											"date": {
+												"type": "string",
+												"description": "날짜",
+												"example": "2021-09-06"
+											},
+											"content": {
+												"type": "string",
+												"description": "공고문 내용",
+												"example": "경상국립대학교병원 시설기술직 채용공고"
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2151": {
+						"description": "날짜를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/calendars/{enterpriseIdx}": {
+			"get": {
+				"tags": ["Calendar"],
+				"summary": "달력 회사 상세 상세 조회",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "date",
+						"description": "달력 회사 상세 조회 Query Variable : 회사 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"enterpriseIdx": {
+											"type": "integer",
+											"description": "회사 인덱스",
+											"example": 1
+										},
+										"enterpriseName": {
+											"type": "string",
+											"description": "회사 이름",
+											"example": "경상국립대학교병원"
+										},
+										"link": {
+											"type": "string",
+											"description": "회사 공고 링크",
+											"example": "http://recruit.gnuh.co.kr"
+										},
+										"content": {
+											"type": "string",
+											"description": "공고문 내용",
+											"example": "경상국립대학교병원 시설기술직 채용공고"
+										},
+										"image": {
+											"type": "string",
+											"description": "첨부 파일 이미지",
+											"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/calendar/01.png"
+										},
+										"startDate": {
+											"type": "string",
+											"description": "모집 시작 날짜",
+											"example": "2021-08-31"
+										},
+										"endDate": {
+											"type": "string",
+											"description": "모집 끝 날짜",
+											"example": "2021-09-06"
+										},
+										"examDate": {
+											"type": "array",
+											"items": {
+												"type": "string",
+												"description": "시험일 리스트",
+												"example": "2021-09-24"
+											}
+										},
+										"education": {
+											"type": "string",
+											"description": "고졸 or 대졸",
+											"example": "대졸"
+										},
+										"enterpriseEssential": {
+											"type": "string",
+											"description": "필수 or 대체 요건 내용",
+											"example": "토익 700 / 토익-s 120"
+										},
+										"enterpriseExtra": {
+											"type": "array",
+											"description": " 가산 요건 내용",
+											"example": "전기기사 자격증 ( 2점 )"
+										},
+										"enterpriseEtc": {
+											"type": "array",
+											"description": " 기타 요건 내용",
+											"example": " 학교장추천 "
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/delete/restoreExamDetail/:restoreExamDetailIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 복원 회차 삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 복원 회차 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restoreExamDetailIdx",
+						"description": "관리자 복원 회차 삭제 Path Variable : 복원 회차 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/delete/restoration/:restorationIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 검색 복원 문제 삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 복원 회차 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restorationIdx",
+						"description": "관리자 검색 복원 문제 삭제 Path Variable : 검색 복원 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/upload/restoreExamDetail": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": "관리자 복원 회차 생성",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 복원 회차 생성 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 복원 회차 생성 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["examIdx", "restoreExamDate", "restoreExamRound", "thumbnail"],
+							"properties": {
+								"examIdx": {
+									"type": "integer",
+									"description": "시험 인덱스",
+									"example": 2
+								},
+								"restoreExamDate": {
+									"type": "String",
+									"description": "복원 시험 날짜",
+									"example": "2021-10-23"
+								},
+								"restoreExamRound": {
+									"type": "String",
+									"description": "복원 시험 회차 ",
+									"example": "하반기"
+								},
+								"thumbnail": {
+									"type": "String",
+									"description": " 복원 회차 썸네일 Url ( 카카오톡 공유용 ) ",
+									"example": "\\includegraphics[]{https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png}"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/update/restoreExamDetail/:restoreExamDetailIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 복원 회차 수정",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 복원 회차 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restoreExamDetailIdx",
+						"description": "관리자 복원 회차 수정 Path Variable : 복원 회차 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 복원 회차 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["restoreExamDate", "restorExamRound", "status"],
+							"properties": {
+								"restorExamRound": {
+									"type": "String",
+									"description": "복원 시험 회차",
+									"example": "하반기"
+								},
+								"restoreExamDate": {
+									"type": "String",
+									"description": "복원 시험 날짜",
+									"example": "2021-10-23"
+								},
+								"isPublic": {
+									"type": "integer",
+									"description": " 공개 여부 ( 0 : 비공개 , 1 : 공개 ) ",
+									"example": 0
+								},
+								"thumbnail": {
+									"type": "String",
+									"description": " 복원 회차 썸네일 Url ( 카카오톡 공유용 ) ",
+									"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/restoreExamDetail/:restoreExamDetailIdx/restorationList": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "복원 시험 회차 문제 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 시험 회차 문제 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restoreExamDetailIdx",
+						"description": "복원 시험 회차 문제 조회 Path Variable : 복원 회차 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"description": "검색 복원 문제 리스트",
+									"items": {
+										"type": "object",
+										"properties": {
+											"restorationIdx": {
+												"type": "integer",
+												"description": "검색 복원 문제 인덱스",
+												"example": 1
+											},
+											"origin": {
+												"type": "string",
+												"description": "해당 문제 DB 정보",
+												"example": "전기기사20년 1회차 21번"
+											},
+											"problem": {
+												"type": "string",
+												"description": "문제",
+												"example": "중성점\\,\\"
+											},
+											"isKatex": {
+												"type": "integer",
+												"description": "카텍스 유무",
+												"example": 0
+											},
+											"likeSum": {
+												"type": "integer",
+												"description": "해당 문제 좋아요 합계",
+												"example": 10
+											},
+											"commentSum": {
+												"type": "integer",
+												"description": "해당 문제 댓글 합계",
+												"example": 5
+											},
+											"userRestorationSum": {
+												"type": "integer",
+												"description": "해당 문제 담은 유저 합계",
+												"example": 5
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/examInfo/:examIdx": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "시험 정보 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "시험 정보 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "시험 인덱스",
+						"required": true,
+						"type": "integer",
+						"example": 1000
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examName": {
+											"type": "string",
+											"description": "시험 이름",
+											"example": "전기기사"
+										},
+										"examSort": {
+											"type": "string",
+											"description": "시험 구분",
+											"example": "기사"
+										},
+										"examSortRef": {
+											"type": "string",
+											"description": "시험 종류",
+											"example": "자격증"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/update/restoration/:restorationIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "검색 복원 문제 수정 ( 자격증, 공기업 공통 ) ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 복원 회차 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restorationIdx",
+						"description": "검색 복원 문제 수정 Path Variable : 검색 복원 문제 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 검색 복원 문제 수정 Request Body",
+						"required": false,
+						"schema": {
+							"type": "object",
+							"required": [],
+							"properties": {
+								"status": {
+									"type": "String",
+									"description": " 복원 문제 삭제 여부 ( N : 삭제X , Y : 삭제 O )",
+									"example": "Y"
+								},
+								"isPublic": {
+									"type": "integer",
+									"description": " 공개 여부 ( 0 : 비공개 , 1 : 공개 ) ",
+									"example": 0
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/delete/restorationComment/:restorationCommentIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": " 관리자 검색 복원 문제 댓글 삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 검색 복원 문제 댓글 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restorationCommentIdx",
+						"description": "관리자 검색 복원 문제 댓글 삭제 Path Variable : 검색 복원 문제 댓글 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/update/admissionTicket/:examAdmissionTicketIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": " 관리자 복원 회차 수험표 권한 변경 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 복원 회차 수험표 권한 변경 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examAdmissionTicketIdx",
+						"description": "관리자 복원 회차 수험표 권한 변경 Path Variable : 수험표 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 복원 회차 수험표 권한 변경 Request Body",
+						"required": false,
+						"schema": {
+							"type": "object",
+							"required": [],
+							"properties": {
+								"isAuthentication": {
+									"type": "integer",
+									"description": " 해당 수험표 권한부여 ( 0 : 미인증, 1 : 인증, 2 : 인증 거부 ) ",
+									"example": 1
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/restoreExamDetail/:restoreExamDetailIdx/admissionTicket": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "복원 회차 수험표 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": " 관리자 복원 회차 수험표 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restoreExamDetailIdx",
+						"description": "복원 회차 수험표 조회 Path Variable : 복원 회차 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"description": "수험표 리스트",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examAdmissionTicketIdx": {
+												"type": "integer",
+												"description": "수험표 인덱스",
+												"example": 1
+											},
+											"userName": {
+												"type": "string",
+												"description": "유저 이름",
+												"example": "안요네"
+											},
+											"dateOfBirth": {
+												"type": "string",
+												"description": "생년월일",
+												"example": "970927"
+											},
+											"createdAt": {
+												"type": "string",
+												"description": "수험표 생성날짜",
+												"example": "10-05 22:03"
+											},
+											"examSortRef": {
+												"type": "string",
+												"description": "시험 종류",
+												"example": "공기업"
+											},
+											"examSort": {
+												"type": "string",
+												"description": "시험 구분",
+												"example": "고졸직"
+											},
+											"examName": {
+												"type": "string",
+												"description": "시험 이름",
+												"example": "코레일 고졸직"
+											},
+											"examAdmissionTicketImage": {
+												"type": "string",
+												"description": "수험표 사진",
+												"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/calendar/01.png"
+											},
+											"isAuthentication": {
+												"type": "integer",
+												"description": "권한 번호 ( 0 : 미인증 , 1 : 인증, 2 : 인증 거부 )",
+												"example": 0
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/download/userCustomProblem/:restoreExamDetailIdx": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "유저 생성 복원문제 액셀 다운로드",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "유저 생성 복원문제 액셀 다운로드 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restoreExamDetailIdx",
+						"description": "유저 생성 복원문제 액셀 다운로드 Path Variable : 복원 회차 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				]
+			}
+		},
+		"/restores/upload/admission/ticket": {
+			"post": {
+				"tags": ["Restore"],
+				"summary": "수험표 인증 정보 생성",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "수험표 인증 정보 생성 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "시험 인덱스",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["restoreExamDetailIdx", "examAdmissionTicketImage"],
+							"properties": {
+								"restoreExamDetailIdx": {
+									"type": "integer",
+									"description": "복원 시험 인덱스",
+									"example": "19"
+								},
+								"examAdmissionTicketImage": {
+									"type": "string",
+									"description": "수험표 이미지 링크",
+									"example": "https://engineeouploadimage.s3.ap-northeast-2.amazonaws.com/ENGINEEO_2_favicon.jpg"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/admission/ticket": {
+			"get": {
+				"tags": ["Restore"],
+				"summary": "수험표 인증 정보 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "수험표 인증 정보 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "restoreExamDetailIdx",
+						"description": "수험표 인증 정보 조회 Query Variable : 복원 시험 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"auth": {
+											"type": "integer",
+											"description": "수험표 인증 여부 (0: 인증X, 1: 인증O)",
+											"example": "1"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/exam": {
+			"get": {
+				"tags": ["Restore"],
+				"summary": "복원 시험 이름 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 시험 이름 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "restoreExamDetailIdx",
+						"description": "복원 시험 이름 조회 Query Variable : 복원 시험 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examName": {
+											"type": "string",
+											"description": "복원 시험 이름(시험년도 상/하반기 시험이름)",
+											"example": "2020 하반기 전기기사"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/problemInfo/:multipleProblemIdx": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "문제 정보 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "문제 정보 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "multipleProblemIdx",
+						"description": "문제 인덱스",
+						"required": true,
+						"type": "integer",
+						"example": 1000
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examIdx": {
+											"type": "integer",
+											"description": "시험 인덱스",
+											"example": 4
+										},
+										"examName": {
+											"type": "string",
+											"description": "시험 이름",
+											"example": "전기기사"
+										},
+										"examDate": {
+											"type": "string",
+											"description": "시험 날짜",
+											"example": "2021-12-19"
+										},
+										"examRound": {
+											"type": "string",
+											"description": "시험회차",
+											"example": "4"
+										},
+										"multipleProblemIdx": {
+											"type": "integer",
+											"description": "문제 인덱스",
+											"example": 5
+										},
+										"problemNum": {
+											"type": "integer",
+											"description": "문제 번호",
+											"example": 4
+										},
+										"problem": {
+											"type": "string",
+											"description": "문제",
+											"example": "질문입니다."
+										},
+										"provisionNum": {
+											"type": "string",
+											"description": "설비규정",
+											"example": "112.2"
+										},
+										"problemImage": {
+											"type": "string",
+											"description": "문제 이미지(katex)",
+											"example": "\\includegraphics[]{https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20210307-1_CT_MP-76_P.png}"
+										},
+										"solution": {
+											"type": "string",
+											"description": "해설",
+											"example": "\\lbrack 전기자기학 - 08.전류 \\rbrack \n\\\\\nI=\\dfrac{Q}{t}=\\dfrac{ne}{t},\\,\\, 전자의\\,\\, 수(n)으로\\,\\, 정리\\\\\n n=\\dfrac{It}{e}=\\dfrac{50 \\times 1}{1.602 \\times 10{-19}}=31.21 \\times 10^{19}\\\\\\,\\\\\n (e=1.602 \\times 10^{-19},\\,\\, 단위시간=1초\\,\\, 대입)"
+										},
+										"isKatex": {
+											"type": "integer",
+											"description": "카텍스 유무 (유 : 1, 무 : 0)",
+											"example": 1
+										},
+										"isDelete": {
+											"type": "integer",
+											"description": "삭제 여부",
+											"example": 0
+										},
+										"questions": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"questionIdx": {
+														"type": "integer",
+														"description": "선지 인덱스",
+														"example": 11099
+													},
+													"questionNum": {
+														"type": "integer",
+														"description": "선지 번호",
+														"example": 1
+													},
+													"question": {
+														"type": "string",
+														"description": "선지 내용",
+														"example": "$1.2\\times 10^{ - 3}"
+													},
+													"questionImage": {
+														"type": "string",
+														"description": "선지 이미지",
+														"example": "https://www.youtube.com/embed/nQ6GUrEB3XI?start=58&end=67"
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/authority": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "관리자 여부 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 여부 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"isAdmin": {
+											"type": "integer",
+											"description": "관리자 여부 (0: 관리자X, 1: 관리자O)",
+											"example": "1"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/update/restorationComment/:restorationCommentIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "복원 댓글 공개/비공개 전환 API",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 댓글 공개/비공개 전환 API Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restorationCommentIdx",
+						"description": "복원 댓글 공개/비공개 전환 Path Variable : 복원 댓글 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "복원 댓글 공개/비공개 전환 Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["isPublic"],
+							"properties": {
+								"isPublic": {
+									"type": "integer",
+									"description": "해당 댓글의 수정된 공개여부 (ex. 비공개로 전환하고 싶다면 isPublic:0, 공개로 전환하고 싶다면 isPublic:1)"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/restores/restoration/:restorationIdx/comment/private": {
+			"get": {
+				"tags": ["Restore"],
+				"summary": "복원 문제 비공개 댓글 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "복원 문제 비공개 댓글 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "restorationIdx",
+						"description": "복원 문제 비공개 댓글 조회 Path Variable : Restoration Index",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "top",
+						"description": "복원 문제 비공개 댓글 조회 Query Variable : 베스트 댓글 개수",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"restorationCommentIdx": {
+												"type": "integer",
+												"description": "복원 문제 댓글 인덱스",
+												"example": 1
+											},
+											"nickname": {
+												"type": "string",
+												"description": "댓글 작성자",
+												"example": "닉네임"
+											},
+											"userIdx": {
+												"type": "integer",
+												"description": "댓글 작성자 인덱스",
+												"example": 7
+											},
+											"comment": {
+												"type": "string",
+												"description": "댓글 내용",
+												"example": "문제의 전압값이 다릅니다."
+											},
+											"updated": {
+												"type": "string",
+												"description": "작성 날짜",
+												"example": "21/10/08 03:33:15"
+											},
+											"commentLike": {
+												"type": "integer",
+												"description": "댓글 좋아요 갯수",
+												"example": 4
+											},
+											"userLike": {
+												"type": "integer",
+												"description": "사용자가 문제에 대해 좋아요를 누른 상태 : 1, 누르지 않은 상태 : 0",
+												"example": "0"
+											},
+											"isBestComment": {
+												"type": "integer",
+												"description": "해당 댓글이 베스트 댓글인지의 여부 ( 1 : 베스트 댓글, 0 : 일반 댓글)",
+												"example": "1"
+											},
+											"isEdited": {
+												"type": "integer",
+												"description": "수정 여부 (1:수정됨, 0:수정되지 않음)",
+												"example": 1
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/calendars/upload": {
+			"post": {
+				"tags": ["Calendar"],
+				"summary": "달력 회사 삽입",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "달력 회사 삽입 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "달력 회사 삽입 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"description": "question, questionNum, questionImage의 배열의 길이는 모두 동일하다.",
+							"required": [
+								"enterpriseName",
+								"link",
+								"startDate",
+								"endDate",
+								"examDate",
+								"image",
+								"essentialElement",
+								"extraElement",
+								"etcElement"
+							],
+							"properties": {
+								"enterpriseName": {
+									"type": "string",
+									"description": "회사이름",
+									"example": "한전"
+								},
+								"link": {
+									"type": "string",
+									"description": "회사 링크",
+									"example": "링크주소"
+								},
+								"startDate": {
+									"type": "string",
+									"description": "모집 시작일",
+									"example": "2021-08-31"
+								},
+								"endDate": {
+									"type": "string",
+									"description": "모집 마감일",
+									"example": "2021-09-24"
+								},
+								"examDate": {
+									"type": "string",
+									"description": "시험 날짜",
+									"example": "2021-09-28"
+								},
+								"image": {
+									"type": "string",
+									"description": "공고문 이미지 url",
+									"example": "이미지 url"
+								},
+								"essentialElement": {
+									"type": "string",
+									"description": "필수 요소",
+									"example": "전기기사"
+								},
+								"extraElement": {
+									"type": "string",
+									"description": "가산 요소",
+									"example": "전기산업기사 가산점2점"
+								},
+								"etcElement": {
+									"type": "string",
+									"description": "기타 요소",
+									"example": "학교장 추천"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/calendars/update/:enterpriseIdx": {
+			"patch": {
+				"tags": ["Calendar"],
+				"summary": "달력 회사 수정",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "달력 회사 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "enterpriseIdx",
+						"description": "달력 회사 수정 Path Variable : 회사 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "달력 회사 삽입 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"description": "question, questionNum, questionImage의 배열의 길이는 모두 동일하다.",
+							"required": [
+								"enterpriseName",
+								"link",
+								"startDate",
+								"endDate",
+								"examDate",
+								"image",
+								"essentialElement",
+								"extraElement",
+								"etcElement"
+							],
+							"properties": {
+								"enterpriseName": {
+									"type": "string",
+									"description": "회사이름",
+									"example": "한전"
+								},
+								"link": {
+									"type": "string",
+									"description": "회사 링크",
+									"example": "링크주소"
+								},
+								"startDate": {
+									"type": "string",
+									"description": "모집 시작일",
+									"example": "2021-08-31"
+								},
+								"endDate": {
+									"type": "string",
+									"description": "모집 마감일",
+									"example": "2021-09-24"
+								},
+								"examDate": {
+									"type": "string",
+									"description": "시험 날짜",
+									"example": "2021-09-28"
+								},
+								"image": {
+									"type": "string",
+									"description": "공고문 이미지 url",
+									"example": "이미지 url"
+								},
+								"essentialElement": {
+									"type": "string",
+									"description": "필수 요소",
+									"example": "전기기사"
+								},
+								"extraElement": {
+									"type": "string",
+									"description": "가산 요소",
+									"example": "전기산업기사 가산점2점"
+								},
+								"etcElement": {
+									"type": "string",
+									"description": "기타 요소",
+									"example": "학교장 추천"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/calendars/delete/:enterpriseIdx": {
+			"patch": {
+				"tags": ["Calendar"],
+				"summary": "달력 회사 삭제",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "달력 회사 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "enterpriseIdx",
+						"description": "달력 회사 삭제 Path Variable : 회사 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/admins/delete/admissionTicket/:restoreExamDetailIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": " 관리자 복원 회차 수험표 삭제 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 복원 회차 수험표 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examAdmissionTicketIdx",
+						"description": "관리자 복원 회차 수험표 삭제 Path Variable : 수험표 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/category/:multipleProblemIdx": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "특정 문제 카테고리 조회",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "달력 회사 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "multipleProblemIdx",
+						"description": "특정 문제 카테고리 조회 Path Variable :  인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"largeCategoryList": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"categoryIdx": {
+														"type": "integer",
+														"description": "카테고리 인덱스",
+														"example": 1
+													},
+													"categoryName": {
+														"type": "String",
+														"description": "카테고리 이름 ( 해당 문제의 최종 카테고리 )",
+														"example": "벡터"
+													},
+													"categoryRef": {
+														"type": "integer",
+														"description": "상위 카테고리 ( L 최상위 카테고리의 categoryRef는 null일 수 있음 )",
+														"example": 1
+													},
+													"categoryType": {
+														"type": "String",
+														"description": "해당 카테고리 분류 L or M or S",
+														"example": "L"
+													},
+													"mediumCategoryList": {
+														"type": "array",
+														"items": {
+															"type": "object",
+															"properties": {
+																"categoryIdx": {
+																	"type": "integer",
+																	"description": "카테고리 인덱스",
+																	"example": 91
+																},
+																"categoryName": {
+																	"type": "String",
+																	"description": "카테고리 이름 ( 해당 문제의 최종 카테고리 )",
+																	"example": "벡터"
+																},
+																"categoryRef": {
+																	"type": "integer",
+																	"description": "상위 카테고리",
+																	"example": 1
+																},
+																"categoryType": {
+																	"type": "String",
+																	"description": "해당 카테고리 분류 L or M or S",
+																	"example": "M"
+																},
+																"smallCategoryList": {
+																	"type": "array",
+																	"items": {
+																		"type": "object",
+																		"properties": {
+																			"categoryIdx": {
+																				"type": "integer",
+																				"description": "카테고리 인덱스",
+																				"example": 391
+																			},
+																			"categoryName": {
+																				"type": "String",
+																				"description": "카테고리 이름 ( 해당 문제의 최종 카테고리 )",
+																				"example": "벡터"
+																			},
+																			"categoryRef": {
+																				"type": "integer",
+																				"description": "상위 카테고리",
+																				"example": 91
+																			},
+																			"categoryType": {
+																				"type": "String",
+																				"description": "해당 카테고리 분류 L or M or S",
+																				"example": "S"
+																			},
+																			"xsmallCategoryList": {
+																				"type": "array",
+																				"items": {
+																					"type": "object",
+																					"properties": {
+																						"categoryIdx": {
+																							"type": "integer",
+																							"description": "카테고리 인덱스",
+																							"example": 391
+																						},
+																						"categoryName": {
+																							"type": "String",
+																							"description": "카테고리 이름 ( 해당 문제의 최종 카테고리 )",
+																							"example": "벡터"
+																						},
+																						"categoryRef": {
+																							"type": "integer",
+																							"description": "상위 카테고리",
+																							"example": 91
+																						},
+																						"categoryType": {
+																							"type": "String",
+																							"description": "해당 카테고리 분류 L or M or S",
+																							"example": "S"
+																						}
+																					}
+																				}
+																			}
+																		}
+																	}
+																}
+															}
+														}
+													}
+												}
+											}
+										},
+										"problemCategory": {
+											"type": "object",
+											"properties": {
+												"largeCategory": {
+													"type": "object",
+													"properties": {
+														"categoryIdx": {
+															"type": "integer",
+															"description": "카테고리 인덱스",
+															"example": 1
+														},
+														"categoryType": {
+															"type": "String",
+															"description": " 카테고리 대분류 L ",
+															"example": "S"
+														}
+													}
+												},
+												"mediumCategory": {
+													"type": "object",
+													"properties": {
+														"categoryIdx": {
+															"type": "integer",
+															"description": "카테고리 인덱스",
+															"example": 91
+														},
+														"categoryRef": {
+															"type": "integer",
+															"description": "상위 카테고리",
+															"example": 1
+														},
+														"categoryType": {
+															"type": "String",
+															"description": "카테고리 중분류 M ",
+															"example": "M"
+														}
+													}
+												},
+												"smallCategory": {
+													"type": "object",
+													"properties": {
+														"categoryIdx": {
+															"type": "integer",
+															"description": "카테고리 인덱스",
+															"example": 391
+														},
+														"categoryRef": {
+															"type": "integer",
+															"description": "상위 카테고리",
+															"example": 91
+														},
+														"categoryType": {
+															"type": "String",
+															"description": "카테고리 소분류 S",
+															"example": "S"
+														}
+													}
+												},
+												"xsmallCategory": {
+													"type": "object",
+													"properties": {
+														"categoryIdx": {
+															"type": "integer",
+															"description": "카테고리 인덱스",
+															"example": 591
+														},
+														"categoryRef": {
+															"type": "integer",
+															"description": "상위 카테고리",
+															"example": 391
+														},
+														"categoryType": {
+															"type": "String",
+															"description": "카테고리 소분류 S",
+															"example": "XS"
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2080": {
+						"description": "존재하지 않는 계정입니다."
+					},
+					"2083": {
+						"description": "유저인덱스를 형식에 맞게 입력해주세요."
+					},
+					"2084": {
+						"description": "유저인덱스를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/exams/{examIdx}/randomExam": {
+			"post": {
+				"tags": ["Exam"],
+				"summary": "랜덤 문제 조회",
+				"parameters": [
+					{
+						"in": "path",
+						"name": "examIdx",
+						"description": "랜덤 문제 조회 Path Variable : Exam Index",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "선택한 과목 인덱스(cbt모드는 해당 시험의 모든 과목), 드롭다운1, 드롭다운2, 드롭다운3",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["subjectList"],
+							"properties": {
+								"subjectList": {
+									"type": "array",
+									"items": {
+										"type": "integer",
+										"description": "시험 과목 인덱스",
+										"example": 5
+									},
+									"description": "과목 인덱스, 개수 리스트"
+								},
+								"startDate": {
+									"type": "string",
+									"example": "2020.03.07",
+									"description": "시작 회차 날짜"
+								},
+								"endDate": {
+									"type": "string",
+									"description": "마감 회차 날짜",
+									"example": "21.03.07"
+								},
+								"limit": {
+									"type": "integer",
+									"description": "문제 수",
+									"example": 20
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"examInfo": {
+											"type": "object",
+											"properties": {
+												"examIdx": {
+													"type": "integer",
+													"description": "시험 인덱스",
+													"example": 7
+												},
+												"examName": {
+													"type": "string",
+													"description": "시험 이름",
+													"example": "전기 기사"
+												},
+												"passScore": {
+													"type": "integer",
+													"description": "시험 합격 점수",
+													"example": 70
+												},
+												"problemCount": {
+													"type": "integer",
+													"description": "시험 문제 개수",
+													"example": 100
+												},
+												"timeLimit": {
+													"type": "integer",
+													"description": "시험 제한 시간",
+													"example": 250
+												},
+												"examUrl": {
+													"type": "String",
+													"description": "시험 url",
+													"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+												}
+											},
+											"description": "시험 정보"
+										},
+										"subject": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"examSubjectIdx": {
+														"type": "integer",
+														"description": "시험 과목 인덱스",
+														"example": 5
+													},
+													"examSubjectNum": {
+														"type": "integer",
+														"description": "시험 과목 번호 (ex. 전기기기 1과목)",
+														"example": "1"
+													},
+													"examSubjectName": {
+														"type": "string",
+														"description": "시험 과목 이름",
+														"example": "전자기기학"
+													},
+													"passScore": {
+														"type": "integer",
+														"description": "시험 합격 점수",
+														"example": 20
+													},
+													"problems": {
+														"type": "array",
+														"description": "과목에 해당하는 문제 리스트",
+														"items": {
+															"type": "object",
+															"properties": {
+																"problemIdx": {
+																	"type": "integer",
+																	"description": "시험 문제 인덱스",
+																	"example": 16
+																},
+																"problemNum": {
+																	"type": "integer",
+																	"description": "문제 번호",
+																	"example": 4
+																},
+																"problem": {
+																	"type": "string",
+																	"description": "문제",
+																	"example": "질문입니다."
+																},
+																"answerNum": {
+																	"type": "integer",
+																	"description": "정답번호",
+																	"example": 4
+																},
+																"questions": {
+																	"type": "array",
+																	"items": {
+																		"type": "object",
+																		"properties": {
+																			"questionIdx": {
+																				"type": "integer",
+																				"description": "선지 인덱스",
+																				"example": 11099
+																			},
+																			"questionNum": {
+																				"type": "integer",
+																				"description": "선지 번호",
+																				"example": 1
+																			},
+																			"question": {
+																				"type": "string",
+																				"description": "선지 내용",
+																				"example": "mged"
+																			}
+																		}
+																	}
+																},
+																"provisionNum": {
+																	"type": "string",
+																	"description": "설비규정",
+																	"example": "112.2"
+																},
+																"solution": {
+																	"type": "string",
+																	"description": "해설",
+																	"example": "\\lbrack 전기자기학 - 08.전류 \\rbrack \n\\\\\nI=\\dfrac{Q}{t}=\\dfrac{ne}{t},\\,\\, 전자의\\,\\, 수(n)으로\\,\\, 정리\\\\\n n=\\dfrac{It}{e}=\\dfrac{50 \\times 1}{1.602 \\times 10{-19}}=31.21 \\times 10^{19}\\\\\\,\\\\\n (e=1.602 \\times 10^{-19},\\,\\, 단위시간=1초\\,\\, 대입)"
+																},
+																"isKatex": {
+																	"type": "integer",
+																	"description": "카텍스 유무 (유 : 1, 무 : 0)",
+																	"example": 1
+																},
+																"isDelete": {
+																	"type": "integer",
+																	"description": "삭제 여부",
+																	"example": 0
+																},
+																"problemScore": {
+																	"type": "integer",
+																	"description": "문제 배점",
+																	"example": 5
+																},
+																"lectureUrl": {
+																	"type": "integer",
+																	"description": "강의 URL",
+																	"example": "https://www.youtube.com/embed/nQ6GUrEB3XI?start=58&end=67"
+																},
+																"examDetailIdx": {
+																	"type": "integer",
+																	"description": "회차 인덱스",
+																	"example": 5
+																},
+																"originProblemNum": {
+																	"type": "integer",
+																	"description": "원래 문제 번호",
+																	"example": 41
+																}
+															}
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2053": {
+						"description": "examDetailIdx를 입력해주세요"
+					},
+					"2054": {
+						"description": "examDetailIdx를 형식에 맞게 입력해주세요"
+					},
+					"2055": {
+						"description": "해당 examDetailIdx가 존재하지 않습니다."
+					}
+				}
+			}
+		},
+		"/admins/synchronize/:examDetailIdx": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": " 전기기사 전기공사기사 회차별 동기화 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "전기기사 전기공사기사 회차별 동기화 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examDetailIdx",
+						"description": "전기기사 전기공사기사 회차별 동기화 Path Variable : 전기공사기사로 복붙하고 싶은 문제들이 있는 전기기사 회차 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"productIdx": {
+											"type": "integer",
+											"description": "상품 인덱스",
+											"example": 1
+										},
+										"price": {
+											"type": "string",
+											"description": "상품 가격",
+											"example": "전기 기사"
+										},
+										"discountPrice": {
+											"type": "integer",
+											"description": "상품 할인 가격",
+											"example": 1
+										},
+										"description": {
+											"type": "string",
+											"description": "상품 소개",
+											"example": "이 상품은 ~~~이며, ~~이다"
+										},
+										"productName": {
+											"type": "string",
+											"description": "상품 이름",
+											"example": "한국수력원자력"
+										},
+										"duration": {
+											"type": "integer",
+											"description": "기간",
+											"example": 50
+										},
+										"productThumbnail": {
+											"type": "string",
+											"description": "상품 썸네일",
+											"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+										},
+										"service": {
+											"type": "array",
+											"description": "상품 서비스 이름, 이미지",
+											"items": {
+												"type": "object",
+												"properties": {
+													"tagName": {
+														"type": "string",
+														"description": "서비스 이름",
+														"example": "한 문제씩 풀기"
+													},
+													"tagUrl": {
+														"type": "string",
+														"description": "해당 서비스 이미지",
+														"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+													}
+												}
+											}
+										},
+										"detailProduct": {
+											"type": "array",
+											"description": "공기업 회차  상품 리스트 (존재하지 않을 시 detailProduct도 존재하지 않는다. )",
+											"items": {
+												"type": "object",
+												"properties": {
+													"productIdx": {
+														"type": "integer",
+														"description": "상품 인덱스",
+														"example": 28
+													},
+													"price": {
+														"type": "integer",
+														"description": "상품 가격",
+														"example": 20000
+													},
+													"discountPrice": {
+														"type": "integer",
+														"description": "상품 할인가격",
+														"example": 15000
+													},
+													"productName": {
+														"type": "string",
+														"description": "상품 이름",
+														"example": "한국수력원자력 20년 1회"
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/stores/product/{productIdx}": {
+			"get": {
+				"tags": ["Store"],
+				"summary": "상품 상세 조회",
+				"parameters": [
+					{
+						"in": "path",
+						"name": "productIdx",
+						"description": "상품 상세 조회 Path Variable : 상품 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"productIdx": {
+												"type": "integer",
+												"description": "상품 인덱스",
+												"example": 1
+											},
+											"price": {
+												"type": "string",
+												"description": "상품 가격",
+												"example": "전기 기사"
+											},
+											"discountPrice": {
+												"type": "integer",
+												"description": "상품 할인 가격",
+												"example": 1
+											},
+											"description": {
+												"type": "string",
+												"description": "상품 소개",
+												"example": "이 상품은 ~~~이며, ~~이다"
+											},
+											"productName": {
+												"type": "string",
+												"description": "상품 이름",
+												"example": "한국수력원자력"
+											},
+											"category": {
+												"type": "string",
+												"description": "상품 카테고리",
+												"example": "자격증"
+											},
+											"duration": {
+												"type": "integer",
+												"description": "기간",
+												"example": 50
+											},
+											"productThumbnail": {
+												"type": "string",
+												"description": "상품 썸네일",
+												"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+											},
+											"service": {
+												"type": "array",
+												"description": "상품 서비스 이름, 이미지",
+												"items": {
+													"type": "object",
+													"properties": {
+														"tagName": {
+															"type": "string",
+															"description": "서비스 이름",
+															"example": "한 문제씩 풀기"
+														},
+														"tagUrl": {
+															"type": "string",
+															"description": "해당 서비스 이미지",
+															"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+														}
+													}
+												}
+											},
+											"detailProduct": {
+												"type": "array",
+												"description": "공기업 회차  상품 리스트 (존재하지 않을 시 detailProduct도 존재하지 않는다. )",
+												"items": {
+													"type": "object",
+													"properties": {
+														"productIdx": {
+															"type": "integer",
+															"description": "상품 인덱스",
+															"example": 28
+														},
+														"price": {
+															"type": "integer",
+															"description": "상품 가격",
+															"example": 20000
+														},
+														"discountPrice": {
+															"type": "integer",
+															"description": "상품 할인가격",
+															"example": 15000
+														},
+														"productName": {
+															"type": "string",
+															"description": "상품 이름",
+															"example": "한국수력원자력 20년 1회"
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/userInfo": {
+			"get": {
+				"tags": ["User"],
+				"summary": " 유저 닉네임 이름 조회 ",
+				"parameters": [
+					{
+						"in": "path",
+						"name": "userIdx",
+						"description": "유저 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"userNickname": {
+											"type": "string",
+											"description": "닉네임입니다",
+											"example": "닉네임"
+										},
+										"userName": {
+											"type": "string",
+											"description": "이름",
+											"example": "이름임"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/category": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": " 카테고리 삽입 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "카테고리 삽입 Header : 관리자 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "카테고리 삽입 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["categoryName", "categoryRef", "categoryType"],
+							"properties": {
+								"categoryName": {
+									"type": "string",
+									"description": "카테고리 이름",
+									"example": "벡터"
+								},
+								"categoryRef": {
+									"type": "integer",
+									"description": "상위 카테고리 ",
+									"example": 1
+								},
+								"categoryType": {
+									"type": "string",
+									"description": "해당 카테고리 상태 L : 대분류 , M : 중분류 , S : 소분류",
+									"example": "M"
+								}
+							}
+						}
+					}
+				]
+			}
+		},
+		"/admins/update/category/:categoryIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": " 카테고리 수정 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "카테고리 수정 Header : 관리자 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "categoryIdx",
+						"description": "카테고리 수정 Path Variable : 카테고리 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "카테고리 삽입 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["categoryName", "categoryRef", "categoryType"],
+							"properties": {
+								"categoryName": {
+									"type": "string",
+									"description": "카테고리 이름",
+									"example": "벡터"
+								},
+								"categoryRef": {
+									"type": "integer",
+									"description": "상위 카테고리 ",
+									"example": 1
+								},
+								"categoryType": {
+									"type": "string",
+									"description": "해당 카테고리 상태 L : 대분류 , M : 중분류 , S : 소분류",
+									"example": "M"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"categoryName": {
+												"type": "string",
+												"description": "메뉴 이름",
+												"example": "자격증"
+											},
+											"categoryIdx": {
+												"type": "integer",
+												"description": "메뉴 인덱스",
+												"example": 1
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/stores/category": {
+			"get": {
+				"tags": ["Store"],
+				"summary": " 유료 메뉴 조회 ",
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"categoryName": {
+												"type": "string",
+												"description": "메뉴 이름",
+												"example": "자격증"
+											},
+											"categoryIdx": {
+												"type": "integer",
+												"description": "메뉴 인덱스",
+												"example": 1
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/delete/category/:categoryIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": " 카테고리 삭제 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "카테고리 삭제 Header : 관리자 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "categoryIdx",
+						"description": "카테고리 삭제 Path Variable : 카테고리 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/stores/productList": {
+			"get": {
+				"tags": ["Store"],
+				"summary": " 유료 상품 리스트 조회",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "categoryIdx",
+						"description": "유료 메뉴 인덱스",
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"productIdx": {
+												"type": "integer",
+												"description": "상품 인덱스"
+											},
+											"price": {
+												"type": "integer",
+												"description": "상품 가격",
+												"example": 65000
+											},
+											"discountPrice": {
+												"type": "integer",
+												"description": "할인가 (할인가와 상품 가격이 같다면, 즉 할인하지 않는다면 response로 discountPrice를 보내주지 않는다.)",
+												"example": 42000
+											},
+											"description": {
+												"type": "string",
+												"description": "상품 설명",
+												"example": "전기기사 어쩌구"
+											},
+											"productName": {
+												"type": "string",
+												"description": "상품 이름",
+												"example": "한국수력원자력"
+											},
+											"productThumbnail": {
+												"type": "string",
+												"description": "이미지 링크",
+												"exampel": "s3 link~~"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/stores/payment": {
+			"post": {
+				"tags": ["Store"],
+				"summary": " 장바구니 조회, 결제하기 조회 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "장바구니 조회, 결제하기 조회 Header : 유저 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "장바구니 조회, 결제하기 조회 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["productIdxList"],
+							"properties": {
+								"productIdxList": {
+									"type": "array",
+									"items": {
+										"type": "integer"
+									},
+									"description": "상품 인덱스 리스트"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"productList": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"productIdx": {
+														"type": "integer",
+														"description": "상품 인덱스",
+														"example": 1
+													},
+													"productName": {
+														"type": "String",
+														"description": "상품 이름",
+														"example": "공기업 프리패스"
+													},
+													"categoryName": {
+														"type": "String",
+														"description": "상품 구분",
+														"example": "프리패스"
+													},
+													"price": {
+														"type": "integer",
+														"description": "상품 원가",
+														"example": "135000"
+													},
+													"discountPrice": {
+														"type": "integer",
+														"description": "상품 할인가",
+														"example": "135000"
+													}
+												}
+											}
+										},
+										"userPointCouponInfo": {
+											"type": "object",
+											"properties": {
+												"userIdx": {
+													"type": "integer",
+													"description": "유저 인덱스",
+													"example": 5
+												},
+												"point": {
+													"type": "integer",
+													"description": "유저 포인트",
+													"example": 3000
+												},
+												"couponCount": {
+													"type": "integer",
+													"description": "유저 쿠폰 개수",
+													"example": 5
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/stores/payment/{productIdx}": {
+			"get": {
+				"tags": ["Store"],
+				"summary": " 상품 사용가능 쿠폰 조회 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "상품 사용가능 쿠폰 조회 Header : 유저 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "상품 인덱스",
+						"description": "상품 사용가능 쿠폰 조회  Path Variable : 상품 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"couponIdx": {
+												"type": "integer",
+												"description": "쿠폰 인덱스",
+												"example": 1
+											},
+											"couponName": {
+												"type": "String",
+												"description": "쿠폰 이름",
+												"example": "회원가입 축하 쿠폰"
+											},
+											"couponPrice": {
+												"type": "String",
+												"description": "상품 구분",
+												"example": "프리패스"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/stores/payment/request": {
+			"post": {
+				"tags": ["Store"],
+				"summary": " 결제 요청 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "결제 요청 Header : 유저 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "결제 요청 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["orderPerProduct"],
+							"properties": {
+								"orderPerProduct": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"required": ["productIdx"],
+										"properties": {
+											"productIdx": {
+												"type": "integer",
+												"description": "상품 인덱스",
+												"example": 8
+											},
+											"usePoint": {
+												"type": "integer",
+												"description": "상품에 사용하는 포인트",
+												"example": 5000
+											}
+										}
+									},
+									"description": "상품 별 정보"
+								},
+								"pay_method": {
+									"type": "string",
+									"description": "결제 수단",
+									"example": "card"
+								},
+								"isAgree": {
+									"type": "integer",
+									"description": "동의 여부(1:동의, 0:비동의)",
+									"example": 1
+								},
+								"totalPrice": {
+									"type": "integer",
+									"description": "프론트에서 보내는 결제해야하는 전체 가격",
+									"example": 65000
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"merchant_uid": {
+											"type": "string",
+											"description": "주문번호",
+											"example": "163764442912440cx"
+										},
+										"name": {
+											"type": "string",
+											"description": "결제 내역 항목 이름",
+											"example": "전기기사 외 1개"
+										},
+										"amount": {
+											"type": "integer",
+											"description": "총 결제해야하는 금액",
+											"example": 64000
+										},
+										"buyer_name": {
+											"type": "string",
+											"description": "구매자 이름",
+											"example": "관리자"
+										},
+										"buyer_email": {
+											"type": "string",
+											"description": "구매자 이메일",
+											"example": "admin@admin.com"
+										},
+										"buyer_tel": {
+											"type": "string",
+											"description": "구매자 전화번호",
+											"example": "010-1234-1234"
+										},
+										"pay_method": {
+											"type": "string",
+											"description": "결제 수단",
+											"example": "card"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/stores/payment/complete": {
+			"post": {
+				"tags": ["Store"],
+				"summary": " 상품 결제완료 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "상품 결제완료 Header : 유저 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": " 상품 결제완료 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["imp_uid"],
+							"properties": {
+								"imp_uid": {
+									"type": "string",
+									"description": "아임포트 uid",
+									"example": "imp_84561540121"
+								},
+								"merchant_uid": {
+									"type": "string",
+									"description": "주문 인덱스",
+									"example": "84561540121"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"paymentName": {
+											"type": "string",
+											"description": "결제 건 이름",
+											"example": "한국철도공사 20년 1회 외 2개"
+										},
+										"orderNum": {
+											"type": "string",
+											"description": "주문번호",
+											"example": "163764442912440cx"
+										},
+										"pay_method": {
+											"type": "string",
+											"description": "결제 수단 (card(신용카드), trans(실시간계좌이체), vbank(가상계좌), phone(휴대폰소액결제), kakaopay (이니시스, KCP, 나이스페이먼츠를 통한 카카오페이 직접 호출), payco (이니시스, KCP를 통한 페이코 직접 호출), lpay (이니시스를 통한 LPAY 직접 호출), ssgpay (이니시스를 통한 SSG페이 직접 호출), ssgpay (이니시스를 통한 SSG페이 직접 호출), tosspay (이니시스를 통한 토스간편결제 직접 호출), point (카카오페이, PAYCO, 이니시스, 나이스페이먼츠 내 간편결제 시 해당 간편결제 자체 포인트 100% 결제))",
+											"example": "card"
+										},
+										"paidDate": {
+											"type": "string",
+											"description": "결제 일시",
+											"example": "2021-11-25 11:47"
+										},
+										"totalPrice": {
+											"type": "integer",
+											"description": "총 결제 가격",
+											"example": 100
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/examDetail/:examDetailIdx/category": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": " 시험 회차 문제별 카테고리 조회 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "시험 회차 문제별 카테고리 조회 Header : 관리자 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "examDetailIdx",
+						"description": "시험 회차 문제별 카테고리 조회 Path Variable : examDetailIdx ",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"answerNum": {
+											"type": "integer",
+											"description": "문제 정답",
+											"example": 1
+										},
+										"multipleProblemIdx": {
+											"type": "integer",
+											"description": "문제 인덱스",
+											"example": 1
+										},
+										"problemNum": {
+											"type": "integer",
+											"description": " 문제 번호 ",
+											"example": 5
+										},
+										"isDelete": {
+											"type": "integer",
+											"description": " 삭제 여부 0 : 삭제 x , 1 : 삭제 o ",
+											"example": 0
+										},
+										"category": {
+											"type": "object",
+											"properties": {
+												"largeCategory": {
+													"type": "object",
+													"properties": {
+														"categoryIdx": {
+															"type": "integer",
+															"description": "카테고리 인덱스",
+															"example": 1
+														},
+														"categoryName": {
+															"type": "String",
+															"description": "카테고리 이름",
+															"example": "전기기기"
+														},
+														"categoryType": {
+															"type": "String",
+															"description": " 카테고리 대분류 L ",
+															"example": "S"
+														}
+													}
+												},
+												"mediumCategory": {
+													"type": "object",
+													"properties": {
+														"categoryIdx": {
+															"type": "integer",
+															"description": "카테고리 인덱스",
+															"example": 91
+														},
+														"categoryRef": {
+															"type": "integer",
+															"description": "상위 카테고리",
+															"example": 1
+														},
+														"categoryName": {
+															"type": "String",
+															"description": "카테고리 이름",
+															"example": "전기동기기"
+														},
+														"categoryType": {
+															"type": "String",
+															"description": "카테고리 중분류 M ",
+															"example": "M"
+														}
+													}
+												},
+												"smallCategory": {
+													"type": "object",
+													"properties": {
+														"categoryIdx": {
+															"type": "integer",
+															"description": "카테고리 인덱스",
+															"example": 391
+														},
+														"categoryRef": {
+															"type": "integer",
+															"description": "상위 카테고리",
+															"example": 91
+														},
+														"categoryName": {
+															"type": "String",
+															"description": "카테고리 이름",
+															"example": "소단원1"
+														},
+														"categoryType": {
+															"type": "String",
+															"description": "카테고리 소분류 S",
+															"example": "S"
+														}
+													}
+												},
+												"xsmallCategory": {
+													"type": "object",
+													"properties": {
+														"categoryIdx": {
+															"type": "integer",
+															"description": "카테고리 인덱스",
+															"example": 591
+														},
+														"categoryRef": {
+															"type": "integer",
+															"description": "상위 카테고리",
+															"example": 391
+														},
+														"categoryName": {
+															"type": "String",
+															"description": "카테고리 이름",
+															"example": "유형1"
+														},
+														"categoryType": {
+															"type": "String",
+															"description": "카테고리 소분류 S",
+															"example": "XS"
+														}
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/purchaseHistory": {
+			"post": {
+				"tags": ["User"],
+				"summary": " 유저 구매이력 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "유저 구매이력 Header : 유저 JWT 토큰",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"orderNum": {
+											"type": "string",
+											"description": "주문번호",
+											"example": "1637742395861yvkq"
+										},
+										"paidDate": {
+											"type": "string",
+											"description": "결제일시",
+											"example": "2021-11-25 11:44"
+										},
+										"status": {
+											"type": "string",
+											"description": "결제 상태(결제 완료 : paid, 결제 실패 : failed)",
+											"example": "paid"
+										},
+										"pay_method": {
+											"type": "string",
+											"description": "결제 수단 (card(신용카드), trans(실시간계좌이체), vbank(가상계좌), phone(휴대폰소액결제), kakaopay (이니시스, KCP, 나이스페이먼츠를 통한 카카오페이 직접 호출), payco (이니시스, KCP를 통한 페이코 직접 호출), lpay (이니시스를 통한 LPAY 직접 호출), ssgpay (이니시스를 통한 SSG페이 직접 호출), ssgpay (이니시스를 통한 SSG페이 직접 호출), tosspay (이니시스를 통한 토스간편결제 직접 호출), point (카카오페이, PAYCO, 이니시스, 나이스페이먼츠 내 간편결제 시 해당 간편결제 자체 포인트 100% 결제))",
+											"example": "point"
+										},
+										"discountPrice": {
+											"type": "integer",
+											"description": "결제 시 총 할인 금액(할인 안할 시, 반환 안함)",
+											"example": 124900
+										},
+										"totalPrice": {
+											"type": "integer",
+											"description": "총 결제 금액",
+											"example": 100
+										},
+										"totalProductlDiscountPrice": {
+											"type": "integer",
+											"description": "구매하는 상품의 할인가 총합",
+											"example": 125000
+										},
+										"paymentName": {
+											"type": "string",
+											"description": "결제 건 이름",
+											"example": "전기기사 외 1개"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/communities/notice": {
+			"get": {
+				"tags": ["Community"],
+				"summary": " 공지사항 목록 조회 ",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "page",
+						"description": "공지사항 목록 페이지 번호",
+						"required": false,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "search",
+						"description": "훗핫",
+						"required": false,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"noticeCount": {
+											"type": "integer",
+											"description": "공지사항 총 게시글수",
+											"example": 52
+										},
+										"noticeList": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"boardIdx": {
+														"type": "integer",
+														"description": "게시글 인덱스",
+														"example": 8
+													},
+													"nickname": {
+														"type": "string",
+														"description": "공지사항 작성자 닉네임",
+														"example": "연고맨"
+													},
+													"boardTitle": {
+														"type": "string",
+														"description": "공지사항 제목",
+														"example": "[공지] 이지 일렉트릭 상장"
+													},
+													"readCount": {
+														"type": "integer",
+														"description": "조회수",
+														"example": 100
+													},
+													"createdAt": {
+														"type": "string",
+														"description": "작성 날짜",
+														"example": "2021.08.07"
+													},
+													"noticeNum": {
+														"type": "integer",
+														"description": "게시글 번호",
+														"example": 16
+													},
+													"isNew": {
+														"type": "integer",
+														"description": "새로운 게시글 여부 ( 생성된지 3일까지 )",
+														"example": 1
+													}
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/communities/notice/:boardIdx": {
+			"get": {
+				"tags": ["Community"],
+				"summary": " 공지사항 조회 ",
+				"parameters": [
+					{
+						"in": "path",
+						"name": "boardIdx",
+						"description": "공지사항 조회 Path Variable : boardIdx ",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"notice": {
+											"type": "object",
+											"properties": {
+												"boardIdx": {
+													"type": "integer",
+													"description": "게시글 인덱스",
+													"example": 390
+												},
+												"nickname": {
+													"type": "string",
+													"description": "작성자 닉네임",
+													"example": "연고매니아"
+												},
+												"boardTitle": {
+													"type": "string",
+													"description": "게시글 제목",
+													"example": "[공지]"
+												},
+												"boardContent": {
+													"type": "string",
+													"description": "게시글 내용",
+													"example": "구독자 5만기념 5만포인트 쏩니다"
+												},
+												"readCount": {
+													"type": "integer",
+													"description": "조회수",
+													"example": 390
+												},
+												"createdAt": {
+													"type": "integer",
+													"description": "작성 날짜",
+													"example": "2021.09.08"
+												}
+											}
+										},
+										"prevNotice": {
+											"type": "object",
+											"properties": {
+												"boardIdx": {
+													"type": "integer",
+													"description": "게시글 인덱스",
+													"example": 390
+												},
+												"boardTitle": {
+													"type": "string",
+													"description": "게시글 제목",
+													"example": "[공지]"
+												},
+												"status": {
+													"type": "string",
+													"description": "prev or next",
+													"example": "prev"
+												}
+											}
+										},
+										"nextNotice": {
+											"type": "object",
+											"properties": {
+												"boardIdx": {
+													"type": "integer",
+													"description": "게시글 인덱스",
+													"example": 390
+												},
+												"boardTitle": {
+													"type": "string",
+													"description": "게시글 제목",
+													"example": "[공지]"
+												},
+												"status": {
+													"type": "string",
+													"description": "prev or next",
+													"example": "next"
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/communities/home/notice": {
+			"get": {
+				"tags": ["Community"],
+				"summary": "홈화면 공지사항 목록 조회 ",
+				"parameters": [],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"boardIdx": {
+												"type": "integer",
+												"description": "게시글 인덱스",
+												"example": 8
+											},
+											"nickname": {
+												"type": "string",
+												"description": "공지사항 작성자 닉네임",
+												"example": "연고맨"
+											},
+											"boardTitle": {
+												"type": "string",
+												"description": "공지사항 제목",
+												"example": "[공지] 이지 일렉트릭 상장"
+											},
+											"readCount": {
+												"type": "integer",
+												"description": "조회수",
+												"example": 100
+											},
+											"createdAt": {
+												"type": "string",
+												"description": "작성 날짜",
+												"example": "2021.08.07"
+											},
+											"noticeNum": {
+												"type": "integer",
+												"description": "게시글 번호",
+												"example": 16
+											},
+											"isNew": {
+												"type": "integer",
+												"description": "새로운 게시글 여부 ( 생성된지 3일까지 )",
+												"example": 1
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/stores/packageList": {
+			"get": {
+				"tags": ["Store"],
+				"summary": "패키지 상품 조회",
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"productIdx": {
+												"type": "integer",
+												"description": "상품 인덱스"
+											},
+											"price": {
+												"type": "integer",
+												"description": "상품 가격",
+												"example": 65000
+											},
+											"discountPrice": {
+												"type": "integer",
+												"description": "할인가",
+												"example": 42000
+											},
+											"description": {
+												"type": "string",
+												"description": "상품 설명",
+												"example": "전기기사 어쩌구"
+											},
+											"productName": {
+												"type": "string",
+												"description": "상품 이름",
+												"example": "엔지니오 전기기사"
+											},
+											"packageName": {
+												"type": "string",
+												"description": "패키지 이름",
+												"example": "베이직 패키지"
+											},
+											"isPremium": {
+												"type": "integer",
+												"description": "하이라이트 여부(1:예, 0:아니오)",
+												"example": "1"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/stores/payment/checkHistory": {
+			"post": {
+				"tags": ["Store"],
+				"summary": "이미 구매한 상품 체크 API",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "이미 구매한 상품 체크 API Header : 유저 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "이미 구매한 상품 체크 API Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["productIdxList"],
+							"properties": {
+								"productIdxList": {
+									"type": "array",
+									"items": {
+										"type": "integer"
+									},
+									"description": "상품 인덱스 리스트"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"isAvailable": {
+											"type": "integer",
+											"description": "배열로 보낸 상품 인덱스들에 유저가 구매할 수 없는 상품이 하나라도 있다면 0 반환 (0:하나라도 존재, 1:모두 구매 가능.)",
+											"example": 1
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/communities/notice/": {
+			"post": {
+				"tags": ["Community"],
+				"summary": " 공지사항 작성 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "공지사항 작성 API Header : 임원 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "공지사항 작성 API Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["boardTitle", "boardContent"],
+							"properties": {
+								"boardTitle": {
+									"type": "string",
+									"description": "게시글 제목",
+									"example": "[공지] ' ~입니다'"
+								},
+								"boardContent": {
+									"type": "string",
+									"description": "게시글 내용",
+									"example": "~입니다."
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/communities/update/notice/:{boardIdx}": {
+			"patch": {
+				"tags": ["Community"],
+				"summary": " 공지사항 수정 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "공지사항 수정 API Header : 임원 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "boardIdx",
+						"description": "공지사항 수정 Path Variable : boardIdx ",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "공지사항 수정 API Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["boardTitle", "boardContent"],
+							"properties": {
+								"boardTitle": {
+									"type": "string",
+									"description": "게시글 제목",
+									"example": "[공지] ' ~입니다'"
+								},
+								"boardContent": {
+									"type": "string",
+									"description": "게시글 내용",
+									"example": "~입니다."
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/communities/delete/notice/:{boardIdx}": {
+			"patch": {
+				"tags": ["Community"],
+				"summary": " 공지사항 삭제 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "공지사항 삭제 API Header : 임원 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "boardIdx",
+						"description": "공지사항 삭제 Path Variable : boardIdx ",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/users/{userIdx}/purchaseHistory/detail": {
+			"get": {
+				"tags": ["User"],
+				"summary": " 유저 상세 구매이력 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "유저 상세 구매이력 Header : 유저 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "orderNum",
+						"description": "유저 상세 구매이력을 위한 Query Variable : 주문 번호",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"productList": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"productIdx": {
+														"type": "integer",
+														"description": "상품 인덱스",
+														"example": 8
+													},
+													"productName": {
+														"type": "string",
+														"description": "상품 이름",
+														"example": "전기기사"
+													},
+													"discountPrice": {
+														"type": "integer",
+														"description": "상품 할인 가격",
+														"example": 65000
+													},
+													"finalPrice": {
+														"type": "integer",
+														"description": "상품 결제 가격",
+														"example": 100
+													},
+													"usePoint": {
+														"type": "integer",
+														"description": "상품에 사용하는 포인트",
+														"example": 64900
+													},
+													"couponIdx": {
+														"type": "integer",
+														"description": "쿠폰 인덱스",
+														"example": 1
+													},
+													"couponName": {
+														"type": "string",
+														"description": "쿠폰 이름",
+														"example": "자격증 할인권"
+													},
+													"status": {
+														"type": "string",
+														"description": "결제완료/환불완료",
+														"example": "결제 완료"
+													}
+												}
+											}
+										},
+										"orderNum": {
+											"type": "string",
+											"description": "주문번호",
+											"example": "1637742395861yvkq"
+										},
+										"paidDate": {
+											"type": "string",
+											"description": "결제날짜",
+											"example": "2021-11-24"
+										},
+										"status": {
+											"type": "string",
+											"description": "결제 상태(결제 완료,  환불 완료 / 환불 완료가 한개라도 있다면 환불 완료로 표시)",
+											"example": "결제 완료"
+										},
+										"pay_method": {
+											"type": "string",
+											"description": "결제 수단 (card(신용카드), trans(실시간계좌이체), vbank(가상계좌), phone(휴대폰소액결제), kakaopay (이니시스, KCP, 나이스페이먼츠를 통한 카카오페이 직접 호출), payco (이니시스, KCP를 통한 페이코 직접 호출), lpay (이니시스를 통한 LPAY 직접 호출), ssgpay (이니시스를 통한 SSG페이 직접 호출), ssgpay (이니시스를 통한 SSG페이 직접 호출), tosspay (이니시스를 통한 토스간편결제 직접 호출), point (카카오페이, PAYCO, 이니시스, 나이스페이먼츠 내 간편결제 시 해당 간편결제 자체 포인트 100% 결제))",
+											"example": "point"
+										},
+										"totalPoint": {
+											"type": "integer",
+											"description": "결제 시 총 사용 포인트",
+											"example": 124900
+										},
+										"totalPrice": {
+											"type": "integer",
+											"description": "총 결제 금액",
+											"example": 100
+										},
+										"totalProductlDiscountPrice": {
+											"type": "integer",
+											"description": "구매하는 상품의 할인가 총합",
+											"example": 125000
+										},
+										"paymentName": {
+											"type": "string",
+											"description": "결제 건 이름",
+											"example": "전기기사 외 1개"
+										},
+										"userName": {
+											"type": "string",
+											"description": "주문자 이름",
+											"example": "김승은"
+										},
+										"userPhoneNum": {
+											"type": "string",
+											"description": "주문자 전화번호",
+											"example": "01012341234"
+										},
+										"productCount": {
+											"type": "integer",
+											"description": "상품 개수",
+											"example": 2
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/stores/payment/refund": {
+			"get": {
+				"tags": ["Store"],
+				"summary": " 환불 요청 페이지 정보 조회 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "환불 요청 페이지 정보 조회 Header : 유저 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "orderNum",
+						"description": "환불 요청 페이지 정보 조회을 위한 Query Variable : 주문 번호",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"productInfoList": {
+											"type": "array",
+											"items": {
+												"type": "object",
+												"properties": {
+													"productIdx": {
+														"type": "integer",
+														"description": "상품 인덱스",
+														"example": 8
+													},
+													"productName": {
+														"type": "string",
+														"description": "상품 이름",
+														"example": "전기기사"
+													},
+													"discountPrice": {
+														"type": "integer",
+														"description": "상품 할인 가격",
+														"example": 65000
+													},
+													"finalPrice": {
+														"type": "integer",
+														"description": "상품 결제 가격",
+														"example": 100
+													},
+													"usePoint": {
+														"type": "integer",
+														"description": "상품에 사용한 포인트",
+														"example": 64900
+													},
+													"useCoupon": {
+														"type": "integer",
+														"description": "쿠폰으로 할인한 상품 금액",
+														"example": 64900
+													},
+													"couponIdx": {
+														"type": "integer",
+														"description": "쿠폰 인덱스",
+														"example": 1
+													},
+													"couponName": {
+														"type": "string",
+														"description": "쿠폰 이름",
+														"example": "자격증 할인권"
+													},
+													"refundPrice": {
+														"type": "integer",
+														"description": "상품에 해당하는 환불 금액",
+														"example": 4500
+													},
+													"category": {
+														"type": "string",
+														"description": "상품 카테고리",
+														"example": "공기업"
+													}
+												}
+											}
+										},
+										"totalRefundPrice": {
+											"type": "integer",
+											"description": "총 환불 금액",
+											"example": 125000
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/stores/payment/refund/request": {
+			"get": {
+				"tags": ["Store"],
+				"summary": " 환불 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "환불 요청 페이지 정보 조회 Header : 유저 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "환불 요청 페이지 정보 조회 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["orderNum, refundReason, refundRequestPrice, productIdxList"],
+							"properties": {
+								"orderNum": {
+									"type": "string",
+									"description": "주문 번호",
+									"example": "E32924383ZKD3"
+								},
+								"refundReason": {
+									"type": "string",
+									"description": "환불 사유",
+									"example": "블라블라"
+								},
+								"refundRequestPrice": {
+									"type": "integer",
+									"description": "환불 요청 금액",
+									"example": 4500
+								},
+								"productIdxList": {
+									"type": "array",
+									"items": {
+										"type": "integer",
+										"description": "상품 인덱스",
+										"example": 8
+									}
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"pg_tid": {
+											"type": "string",
+											"description": "카드사 승인번호",
+											"example": "INIphpRPAYINIpayTest20211207174146489018"
+										},
+										"refundPrice": {
+											"type": "integer",
+											"description": "총 환불 금액",
+											"example": 4500
+										},
+										"refundDate": {
+											"type": "string",
+											"description": "환불 일시",
+											"example": "2021-11-25 11:47"
+										},
+										"refundReason": {
+											"type": "string",
+											"description": "환불 사유",
+											"example": "구성이 별로에요!"
+										},
+										"receiptUrl": {
+											"type": "string",
+											"description": "환불 영수증 url",
+											"example": "https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/mCmReceipt_head.jsp?noTid=INIphpRPAYINIpayTest20211207174146489018&noMethod=1"
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/upload/publicCompany": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": " 회사 생성 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "회사 생성 Header : 관리자 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 회사 생성 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["publicCompanySortIdx", "publicCompanySortName", "companyThumbnail"],
+							"properties": {
+								"publicCompanySortIdx": {
+									"type": "integer",
+									"description": "시험 종류 인덱스",
+									"example": 11
+								},
+								"publicCompanyName": {
+									"type": "string",
+									"description": "회사 이름",
+									"example": "한국전력공사"
+								},
+								"companyThumbnail": {
+									"type": "string",
+									"description": "시험 종류 이미지",
+									"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/calendar/01.png"
+								}
+							}
+						}
+					}
+				]
+			}
+		},
+		"/admins/update/publicCompany": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": " 회사 수정 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "회사 수정 Header : 관리자 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 회사 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["publicCompanyIdx", "publicCompanySortIdx", "publicCompanySortName", "companyThumbnail"],
+							"properties": {
+								"publicCompanyIdx": {
+									"type": "integer",
+									"description": "회사 인덱스",
+									"example": 14
+								},
+								"publicCompanySortIdx": {
+									"type": "integer",
+									"description": "시험 종류 인덱스",
+									"example": 11
+								},
+								"publicCompanyName": {
+									"type": "string",
+									"description": "회사 이름",
+									"example": "한국전력공사"
+								},
+								"companyThumbnail": {
+									"type": "string",
+									"description": "시험 종류 이미지",
+									"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/calendar/01.png"
+								}
+							}
+						}
+					}
+				]
+			}
+		},
+		"/admins/upload/publicCompany/exam": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": " 회사 시험 생성 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "회사 생성 Header : 관리자 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 회사 생성 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["publicCompanyIdx", "timelimit", "problemCount", "questionType", "position", "education"],
+							"properties": {
+								"publicCompanyIdx": {
+									"type": "integer",
+									"description": "시험 종류 인덱스 ( 회사 인덱스 )",
+									"example": 14
+								},
+								"timeLimit": {
+									"type": "integer",
+									"description": "시험 시간제한",
+									"example": 50
+								},
+								"problemCount": {
+									"type": "string",
+									"description": "시험 문제 개수",
+									"example": 30
+								},
+								"questionType": {
+									"type": "integer",
+									"description": "선지 유형",
+									"example": "객관식4지선다"
+								},
+								"position": {
+									"type": "string",
+									"description": "직렬",
+									"example": "전기직"
+								},
+								"education": {
+									"type": "string",
+									"description": "시험 전형 ( 고졸직, 대졸직, 통합직 )",
+									"example": "고졸"
+								}
+							}
+						}
+					}
+				]
+			}
+		},
+		"/admins/update/publicCompany/exam": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": " 회사 시험 수정 ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "회사 수정 Header : 관리자 JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "관리자 회사 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": [
+								"publicCompanyExamIdx",
+								"publicCompanyIdx",
+								"timelimit",
+								"problemCount",
+								"questionType",
+								"position",
+								"education"
+							],
+							"properties": {
+								"publicCompanyExamIdx": {
+									"type": "integer",
+									"description": "시험 문제 인덱스",
+									"example": 954
+								},
+								"publicCompanyIdx": {
+									"type": "integer",
+									"description": "시험 종류 인덱스 ( 회사 인덱스 )",
+									"example": 14
+								},
+								"timeLimit": {
+									"type": "integer",
+									"description": "시험 시간제한",
+									"example": 50
+								},
+								"problemCount": {
+									"type": "string",
+									"description": "시험 문제 개수",
+									"example": 30
+								},
+								"questionType": {
+									"type": "integer",
+									"description": "선지 유형",
+									"example": "객관식4지선다"
+								},
+								"position": {
+									"type": "string",
+									"description": "직렬",
+									"example": "전기직"
+								},
+								"education": {
+									"type": "string",
+									"description": "시험 전형 ( 고졸, 대졸, 통합 )",
+									"example": "고졸"
+								}
+							}
+						}
+					}
+				]
+			}
+		},
+		"/exams/publicCompany/examSort": {
+			"get": {
+				"tags": ["Exam"],
+				"summary": " 공기업 시험 회사 구분 조회 ",
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examSortName": {
+												"type": "string",
+												"description": "회사 구분 이름",
+												"example": "전기"
+											},
+											"examSortIdx": {
+												"type": "integer",
+												"description": "회사 구분 인덱스",
+												"example": 1
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/exams/publicCompany/companyList": {
+			"get": {
+				"tags": ["Exam"],
+				"summary": " 회사 리스트 조회",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "examSortIdx",
+						"description": "회사 구분 인덱스",
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"companyName": {
+												"type": "string",
+												"description": "공기업 이름",
+												"example": "한국동서발전"
+											},
+											"companyIdx": {
+												"type": "integer",
+												"description": "공기업 인덱스",
+												"example": 4
+											},
+											"companyImage": {
+												"type": "string",
+												"description": "공기업 썸네일",
+												"example": "s3 link"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/exams/publicCompany/filterInfo": {
+			"get": {
+				"tags": ["Exam"],
+				"summary": " 회사의 지원 자격, 전공 분야 조회",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "companyIdx",
+						"description": "회사 인덱스",
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "object",
+									"properties": {
+										"education": {
+											"type": "array",
+											"items": {
+												"type": "string",
+												"description": "지원자격",
+												"example": "일반 공채"
+											}
+										},
+										"position": {
+											"type": "array",
+											"items": {
+												"type": "string",
+												"description": "전공 분야",
+												"example": "전기통신"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/exams/publicCompany/examDetailList": {
+			"get": {
+				"tags": ["Exam"],
+				"summary": " 회차 리스트 조회",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "companyIdx",
+						"description": "회사 인덱스",
+						"type": "integer"
+					},
+					{
+						"in": "query",
+						"name": "education",
+						"description": "가방끈",
+						"type": "string"
+					},
+					{
+						"in": "query",
+						"name": "position",
+						"description": "직렬",
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examIdx": {
+												"type": "integer",
+												"description": "해당 회차의 시험 인덱스",
+												"example": 101
+											},
+											"examDetailName": {
+												"type": "string",
+												"description": "회차 이름",
+												"example": "한국철도공사 2021년 하반기 (2021.00.00)"
+											},
+											"examDetailIdx": {
+												"type": "integer",
+												"description": "회차 인덱스",
+												"example": 4
+											},
+											"companyImage": {
+												"type": "string",
+												"description": "공기업 썸네일",
+												"example": "s3 link"
+											},
+											"position": {
+												"type": "string",
+												"description": "직렬",
+												"example": "차량 전기"
+											},
+											"education": {
+												"type": "string",
+												"description": "학력 제한",
+												"example": "일반 공채"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/publicCompany/sort": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": " 공기업 시험 회사 구분 조회 ",
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"publicCompanySortName": {
+												"type": "string",
+												"description": "공기업 구분 이름",
+												"example": "전기"
+											},
+											"publicCompanySortIdx": {
+												"type": "integer",
+												"description": "공기업 구분 인덱스",
+												"example": 1
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/publicCompany": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": " 공기업 리스트 조회",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "publicCompanySortIdx",
+						"description": "회사 구분 인덱스",
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"publicCompanyIdx": {
+												"type": "integer",
+												"description": "공기업 인덱스",
+												"example": 34
+											},
+											"publicCompanyName": {
+												"type": "string",
+												"description": "공기업 이름",
+												"example": "중소벤처기업진흥공단"
+											},
+											"publicCompanySortName": {
+												"type": "string",
+												"description": "공기업 구분",
+												"example": "일반"
+											},
+											"publicCompanyThumbnail": {
+												"type": "string",
+												"description": "공기업 썸네일",
+												"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/publicCompany/exam": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": " 공기업 시험 리스트 조회",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "publicCompanyIdx",
+						"description": "공기업 인덱스",
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"publicCompanyExamIdx": {
+												"type": "integer",
+												"description": "시험 인덱스",
+												"example": 8
+											},
+											"publicCompanyExamName": {
+												"type": "string",
+												"description": "회사 시험 이름",
+												"example": "한국전력공사(대졸-전기직)"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/publicCompany/examList": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": " 공기업 시험 목록 조회 ( 공기업 시험관리 )",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "search",
+						"description": "검색 내용",
+						"type": "string",
+						"example": "한국"
+					},
+					{
+						"in": "query",
+						"name": "publicCompanyIdx",
+						"description": "공기업 인덱스",
+						"type": "integer",
+						"example": 26
+					},
+					{
+						"in": "query",
+						"name": "publicCompanySortIdx",
+						"description": "공기업 구분 인덱스",
+						"type": "integer",
+						"example": 26
+					},
+					{
+						"in": "query",
+						"name": "publicCompanyExamIdx",
+						"description": "공기업 시험 인덱스",
+						"type": "integer",
+						"example": 29
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"publicCompanyExamIdx": {
+												"type": "integer",
+												"description": " 공기업 시험 인덱스",
+												"example": 8
+											},
+											"publicCompanyExamName": {
+												"type": "string",
+												"description": "공기업 시험 이름",
+												"example": "한국전력공사(대졸-전기직)"
+											},
+											"passScore": {
+												"type": "integer",
+												"description": "시험 합격 점수",
+												"example": 60
+											},
+											"timeLimit": {
+												"type": "integer",
+												"description": "시간 제한",
+												"example": 60
+											},
+											"problemCount": {
+												"type": "integer",
+												"description": "문제 개수",
+												"example": 15
+											},
+											"accessLevel": {
+												"type": "integer",
+												"description": "권한 수준",
+												"example": 0
+											},
+											"examUrl": {
+												"type": "string",
+												"description": "시험 url",
+												"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png"
+											},
+											"questionType": {
+												"type": "string",
+												"description": "선지 유형",
+												"example": "객관식4지선다"
+											},
+											"position": {
+												"type": "string",
+												"description": "직렬",
+												"example": "전기직"
+											},
+											"education": {
+												"type": "string",
+												"description": "전형",
+												"example": "대졸"
+											},
+											"publicCompanyIdx": {
+												"type": "integer",
+												"description": "회사 인덱스",
+												"example": 32
+											},
+											"publicCompanyName": {
+												"type": "string",
+												"description": "회사 이름",
+												"example": "한국전력공사"
+											},
+											"publicCompanyType": {
+												"type": "string",
+												"description": "회사 depth 분류",
+												"example": "S"
+											},
+											"publicCompanySortIdx": {
+												"type": "integer",
+												"description": "회사 구분 인덱스",
+												"example": 32
+											},
+											"publicCompanySortName": {
+												"type": "string",
+												"description": "회사 구분 ( 일반, 에너지 등등..)",
+												"example": "일반"
+											},
+											"publicCompanySortType": {
+												"type": "string",
+												"description": "회사 depth 분류",
+												"example": "M"
+											},
+											"largeExamSortIdx": {
+												"type": "integer",
+												"description": "시험 종류 인덱스",
+												"example": 2
+											},
+											"largeExamSortName": {
+												"type": "string",
+												"description": "시험 종류 ( 자격증, 공무원, 공기업...)",
+												"example": "일반"
+											},
+											"largeExamSortType": {
+												"type": "string",
+												"description": "회사 depth 분류",
+												"example": "L"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/publicCompany/examDetail": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": " 공기업 시험별 회차 목록 조회 ( 공기업 문제관리 )",
+				"parameters": [
+					{
+						"in": "query",
+						"name": "search",
+						"description": "검색 내용",
+						"type": "string",
+						"example": "한국"
+					},
+					{
+						"in": "query",
+						"name": "publicCompanyIdx",
+						"description": "공기업 인덱스",
+						"type": "integer",
+						"example": 26
+					},
+					{
+						"in": "query",
+						"name": "publicCompanySortIdx",
+						"description": "공기업 구분 인덱스",
+						"type": "integer",
+						"example": 26
+					},
+					{
+						"in": "query",
+						"name": "publicCompanyExamIdx",
+						"description": "공기업 시험 인덱스",
+						"type": "integer",
+						"example": 29
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"examDetailIdx": {
+												"type": "integer",
+												"description": "시험 회차 인덱스",
+												"example": 108
+											},
+											"examIdx": {
+												"type": "integer",
+												"description": "시험 인덱스",
+												"example": 1
+											},
+											"date": {
+												"type": "string",
+												"description": "시험 날짜",
+												"example": "2019-06-01"
+											},
+											"examRound": {
+												"type": "string",
+												"description": "시험 회차",
+												"example": "상반기"
+											},
+											"isPublic": {
+												"type": "integer",
+												"description": "공개 유무",
+												"example": 1
+											},
+											"examDetailUrl": {
+												"type": "string",
+												"description": "시험 회차 url",
+												"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/calendar/01.png"
+											},
+											"timeLimit": {
+												"type": "integer",
+												"description": "시험 제한 시간",
+												"example": 1
+											},
+											"problemCount": {
+												"type": "integer",
+												"description": "문제 수",
+												"example": 20
+											},
+											"publicCompanyIdx": {
+												"type": "integer",
+												"description": "공기업 인덱스",
+												"example": 21
+											},
+											"publicCompanyName": {
+												"type": "string",
+												"description": "공기업 이름",
+												"example": "한국전력공사"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/publicCompany/management": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": " 공기업 회사관리 조회 ",
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"publicCompanyIdx": {
+												"type": "integer",
+												"description": "회사 구분 인덱스",
+												"example": 1
+											},
+											"publicCompanyName": {
+												"type": "string",
+												"description": "회사 이름",
+												"example": "한국철도공사"
+											},
+											"publicCompanySortName": {
+												"type": "string",
+												"description": "회사 구분",
+												"example": "교통 및 에너지"
+											},
+											"publicCompanyThumbnail": {
+												"type": "string",
+												"description": "회사 썸네일",
+												"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/kdn.png"
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/admins/delete/publicCompany/{publicCompanyIdx}": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 공기업 삭제  ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 공기업 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "path",
+						"name": "publicCompanyIdx",
+						"description": "관리자 공기업 삭제 Path Variable : 공기업 인덱스 ",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2146": {
+						"description": "존재하지 않는 시험 구분입니다."
+					},
+					"2050": {
+						"description": "examSort를 형식에 맞게 입력해주세요."
+					},
+					"2052": {
+						"description": "examSort를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/api/admins/upload/product": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": "관리자 상품 생성  ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 상품 생성 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "상품 생성 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": [
+								"productName",
+								"productThumbnail",
+								"price",
+								"userName",
+								"discountPrice",
+								"duration",
+								"depth",
+								"examDetailList"
+							],
+							"properties": {
+								"productName ": {
+									"type": "string",
+									"description": "상품 이름",
+									"example": "전기기사"
+								},
+								"productThumbnail": {
+									"type": "string",
+									"description": "상품 썸네일",
+									"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png"
+								},
+								"price": {
+									"type": "integer",
+									"description": "상품 가격",
+									"example": 46000
+								},
+								"discountPrice": {
+									"type": "string",
+									"description": "상품 할인 가격",
+									"example": 20000
+								},
+								"shortDescription ": {
+									"type": "integer",
+									"description": "짧은 설명",
+									"example": "전기기사 기출문제/ CBT 서비스 / 오답노트"
+								},
+								"generalDescription": {
+									"type": "string",
+									"description": "일반 설명",
+									"example": "본 상품은 전기기사 필기시험을 대비한 기출문제입니다......"
+								},
+								"impactDescription ": {
+									"type": "string",
+									"description": "강조 설명",
+									"example": "1. 매년 출제범위 동일 2. ....."
+								},
+								"serviceDescription": {
+									"type": "integer",
+									"description": "서비스 설명",
+									"example": "CBT 실전 모의고사는 데스크톱에서 시험 응시가 가능합니다...."
+								},
+								"duration ": {
+									"type": "string",
+									"description": "유효기간",
+									"example": "50일"
+								},
+								"depth ": {
+									"type": "integer",
+									"description": "상품 유형 ( 묶음 상품 : 0, 회차 상품 : 1 )",
+									"example": 0
+								},
+								"productUrlList ": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"urlHeader": {
+												"type": "string",
+												"description": "url 제목 ex) 미리보기, 소개 영상 등등..",
+												"example": "미리보기"
+											},
+											"urlOrder": {
+												"type": "integer",
+												"description": "url 순서",
+												"example": 1
+											},
+											"productUrl": {
+												"type": "string",
+												"description": "상품 url",
+												"example": "https://engineeouploadimage.s3.ap-northeast-2.amazonaws.com/ENGINEEO_2_favicon.jpg"
+											},
+											"productUrlSort": {
+												"type": "integer",
+												"description": "상품 url 종류 ( 0 : 이미지 url, 1 : 동영상 url )",
+												"example": 0
+											}
+										}
+									}
+								},
+								"examDetailList": {
+									"type": "array",
+									"description": "시험 회차 인덱스 배열",
+									"items": {
+										"type": "integer"
+									}
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2146": {
+						"description": "존재하지 않는 시험 구분입니다."
+					},
+					"2050": {
+						"description": "examSort를 형식에 맞게 입력해주세요."
+					},
+					"2052": {
+						"description": "examSort를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/api/admins/update/product/:productIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "관리자 상품 수정  ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 상품 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "params",
+						"name": "productIdx",
+						"description": "상품 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "상품 생성 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": [
+								"productName",
+								"productThumbnail",
+								"price",
+								"userName",
+								"discountPrice",
+								"duration",
+								"depth",
+								"examDetailList"
+							],
+							"properties": {
+								"productName ": {
+									"type": "string",
+									"description": "상품 이름",
+									"example": "전기기사"
+								},
+								"productThumbnail": {
+									"type": "string",
+									"description": "상품 썸네일",
+									"example": "https://engineeoimage.s3.ap-northeast-2.amazonaws.com/C_EE_20130818-3_EM_MP-1_P.png"
+								},
+								"price": {
+									"type": "integer",
+									"description": "상품 가격",
+									"example": 46000
+								},
+								"discountPrice": {
+									"type": "string",
+									"description": "상품 할인 가격",
+									"example": 20000
+								},
+								"shortDescription ": {
+									"type": "integer",
+									"description": "짧은 설명",
+									"example": "전기기사 기출문제/ CBT 서비스 / 오답노트"
+								},
+								"generalDescription": {
+									"type": "string",
+									"description": "일반 설명",
+									"example": "본 상품은 전기기사 필기시험을 대비한 기출문제입니다......"
+								},
+								"impactDescription ": {
+									"type": "string",
+									"description": "강조 설명",
+									"example": "1. 매년 출제범위 동일 2. ....."
+								},
+								"serviceDescription": {
+									"type": "integer",
+									"description": "서비스 설명",
+									"example": "CBT 실전 모의고사는 데스크톱에서 시험 응시가 가능합니다...."
+								},
+								"duration ": {
+									"type": "string",
+									"description": "유효기간",
+									"example": "50일"
+								},
+								"depth ": {
+									"type": "integer",
+									"description": "상품 유형 ( 묶음 상품 : 0, 회차 상품 : 1 )",
+									"example": 0
+								},
+								"productUrlList ": {
+									"type": "array",
+									"items": {
+										"type": "object",
+										"properties": {
+											"productUrlIdx": {
+												"type": "integer",
+												"description": "상품 url 인덱스",
+												"example": 22
+											},
+											"productIdx": {
+												"type": "integer",
+												"description": "상품 인덱스",
+												"example": 1
+											},
+											"urlHeader": {
+												"type": "string",
+												"description": "url 제목 ex) 미리보기, 소개 영상 등등..",
+												"example": "미리보기"
+											},
+											"urlOrder": {
+												"type": "integer",
+												"description": "url 순서",
+												"example": 1
+											},
+											"productUrl": {
+												"type": "string",
+												"description": "상품 url",
+												"example": "https://engineeouploadimage.s3.ap-northeast-2.amazonaws.com/ENGINEEO_2_favicon.jpg"
+											},
+											"productUrlSort": {
+												"type": "integer",
+												"description": "상품 url 종류 ( 0 : 이미지 url, 1 : 동영상 url )",
+												"example": 0
+											}
+										}
+									}
+								},
+								"examDetailList": {
+									"type": "array",
+									"description": "시험 회차 인덱스 배열",
+									"items": {
+										"type": "integer"
+									}
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2146": {
+						"description": "존재하지 않는 시험 구분입니다."
+					},
+					"2050": {
+						"description": "examSort를 형식에 맞게 입력해주세요."
+					},
+					"2052": {
+						"description": "examSort를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/api/admins/delete/product/:productIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "상품 삭제 기능",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 객관식 문제 삭제 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "params",
+						"name": "productIdx",
+						"description": "상품 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					}
+				}
+			}
+		},
+		"/api/admins/update/productPayment/refund/:paymentProductIdx": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": "관리자 판매 상품 환불정보 수정  ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "관리자 상품 환불정보 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "params",
+						"name": "paymentProductIdx",
+						"description": "결제 상품 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "상품 환불정보 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["isRefund", "userIdx", "productIdx"],
+							"properties": {
+								"userIdx": {
+									"type": "integer",
+									"description": "유저 인덱스",
+									"example": 4
+								},
+								"productIdx ": {
+									"type": "integer",
+									"description": "상품 인덱스",
+									"example": 6
+								},
+								"refundPrice ": {
+									"type": "integer",
+									"description": "환불 금액",
+									"example": 2000
+								},
+								"refundReason": {
+									"type": "string",
+									"description": "환불 사유",
+									"example": "단순 변심"
+								},
+								"refundAt": {
+									"type": "string",
+									"description": "환불 시간",
+									"example": "2022-02-23 12:40:34"
+								},
+								"isRefund": {
+									"type": "integer",
+									"description": "0 : 환불 취소, 1 : 환불 신청",
+									"example": 0
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2146": {
+						"description": "존재하지 않는 시험 구분입니다."
+					},
+					"2050": {
+						"description": "examSort를 형식에 맞게 입력해주세요."
+					},
+					"2052": {
+						"description": "examSort를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/api/admins/upload/point": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": "포인트 생성 API ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "포인트 생성 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "포인트 생성 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["point", "pointName"],
+							"properties": {
+								"point ": {
+									"type": "integer",
+									"description": "포인트",
+									"example": 2000
+								},
+								"pointName": {
+									"type": "string",
+									"description": "포인트 이름",
+									"example": "회원가입 포인트"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2146": {
+						"description": "존재하지 않는 시험 구분입니다."
+					},
+					"2050": {
+						"description": "examSort를 형식에 맞게 입력해주세요."
+					},
+					"2052": {
+						"description": "examSort를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/api/admins/update/point/:pointIdx": {
+			"patch": {
+				"tags": ["Admin"],
+				"summary": "포인트 수정 API ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "포인트 수정 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "params",
+						"name": "pointIdx",
+						"description": "포인트 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "포인트 수정 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["point", "pointName"],
+							"properties": {
+								"point ": {
+									"type": "integer",
+									"description": "포인트",
+									"example": 2000
+								},
+								"pointName": {
+									"type": "string",
+									"description": "포인트 이름",
+									"example": "회원가입 포인트"
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2146": {
+						"description": "존재하지 않는 시험 구분입니다."
+					},
+					"2050": {
+						"description": "examSort를 형식에 맞게 입력해주세요."
+					},
+					"2052": {
+						"description": "examSort를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/api/admins/userPoint/:userIdx": {
+			"post": {
+				"tags": ["Admin"],
+				"summary": "유저 포인트 부여 API ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "포인트 목록 조회 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "params",
+						"name": "userIdx",
+						"description": "부여할 유저 인덱스",
+						"required": true,
+						"type": "integer"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "포인트 부여 Request Body",
+						"required": true,
+						"schema": {
+							"type": "object",
+							"required": ["pointIdx", "point"],
+							"properties": {
+								"pointIdx ": {
+									"type": "integer",
+									"description": "포인트 인덱스",
+									"example": 2
+								},
+								"point": {
+									"type": "integer",
+									"description": "포인트",
+									"example": 500
+								}
+							}
+						}
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								}
+							}
+						}
+					},
+					"2146": {
+						"description": "존재하지 않는 시험 구분입니다."
+					},
+					"2050": {
+						"description": "examSort를 형식에 맞게 입력해주세요."
+					},
+					"2052": {
+						"description": "examSort를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/api/admins/point": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "포인트 목록 조회 API ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "포인트 생성 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"description": "포인트 리스트",
+									"items": {
+										"type": "object",
+										"properties": {
+											"pointIdx ": {
+												"type": "integer",
+												"description": "포인트 인덱스",
+												"example": 2
+											},
+											"point": {
+												"type": "integer",
+												"description": "포인트액",
+												"example": 2000
+											},
+											"pointName": {
+												"type": "string",
+												"description": "포인트 이름",
+												"example": "회원가입 축하 쿠폰"
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2146": {
+						"description": "존재하지 않는 시험 구분입니다."
+					},
+					"2050": {
+						"description": "examSort를 형식에 맞게 입력해주세요."
+					},
+					"2052": {
+						"description": "examSort를 입력해 주세요."
+					}
+				}
+			}
+		},
+		"/api/admins/:userIdx/pointLog": {
+			"get": {
+				"tags": ["Admin"],
+				"summary": "유저 포인트 로그 조회 API ",
+				"parameters": [
+					{
+						"in": "header",
+						"name": "x-access-token",
+						"description": "포인트 생성 Header : JWT 토큰",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "params",
+						"name": "userIdx",
+						"description": "부여할 유저 인덱스",
+						"required": true,
+						"type": "integer"
+					}
+				],
+				"responses": {
+					"1000": {
+						"description": "성공",
+						"schema": {
+							"type": "object",
+							"properties": {
+								"isSuccess": {
+									"type": "boolean",
+									"description": "요청 성공 여부"
+								},
+								"code": {
+									"type": "integer",
+									"description": "응답 코드",
+									"example": 1000
+								},
+								"message": {
+									"type": "string",
+									"description": "응답 코드 메세지",
+									"example": "성공"
+								},
+								"result": {
+									"type": "array",
+									"description": "포인트 로그 리스트",
+									"items": {
+										"type": "object",
+										"properties": {
+											"userPointLogIdx": {
+												"type": "integer",
+												"description": "포인트 로그 인덱스",
+												"example": 2
+											},
+											"userIdx": {
+												"type": "integer",
+												"description": "유저 인덱스",
+												"example": 2
+											},
+											"sort": {
+												"type": "string",
+												"description": "U : 사용, A : 적립",
+												"example": "U"
+											},
+											"content": {
+												"type": "string",
+												"description": "로그 내용",
+												"example": "~상품 구매"
+											},
+											"point": {
+												"type": "integer",
+												"description": "포인트",
+												"example": 2000
+											},
+											"createdAt": {
+												"type": "string",
+												"description": "로그 날짜",
+												"example": "2022-03-08 06:34:31"
+											}
+										}
+									}
+								}
+							}
+						}
+					},
+					"2146": {
+						"description": "존재하지 않는 시험 구분입니다."
+					},
+					"2050": {
+						"description": "examSort를 형식에 맞게 입력해주세요."
+					},
+					"2052": {
+						"description": "examSort를 입력해 주세요."
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/_old/config/winston.js b/_old/config/winston.js
new file mode 100644
index 0000000..36c45f9
--- /dev/null
+++ b/_old/config/winston.js
@@ -0,0 +1,43 @@
+const { createLogger, format, transports } = require("winston");
+require("winston-daily-rotate-file");
+const fs = require("fs");
+
+const env = process.env.NODE_ENV || "development";
+const logDir = "log";
+
+if (!fs.existsSync(logDir)) {
+	fs.mkdirSync(logDir);
+}
+
+const dailyRotateFileTransport = new transports.DailyRotateFile({
+	level: "debug",
+	filename: `${logDir}/%DATE%-smart-push.log`,
+	datePattern: "YYYY-MM-DD",
+	zippedArchive: true,
+	maxSize: "20m",
+	maxFiles: "14d",
+});
+
+const logger = createLogger({
+	level: env === "development" ? "debug" : "info",
+	format: format.combine(
+		format.timestamp({
+			format: "YYYY-MM-DD HH:mm:ss",
+		}),
+		format.json(),
+	),
+	transports: [
+		new transports.Console({
+			level: "info",
+			format: format.combine(
+				format.colorize(),
+				format.printf((info) => `${info.timestamp} ${info.level}: ${info.message}`),
+			),
+		}),
+		dailyRotateFileTransport,
+	],
+});
+
+module.exports = {
+	logger: logger,
+};
diff --git a/_old/ecosystem.config.js b/_old/ecosystem.config.js
new file mode 100644
index 0000000..a454e58
--- /dev/null
+++ b/_old/ecosystem.config.js
@@ -0,0 +1,20 @@
+module.exports = {
+	apps: [
+		{
+			script: "index.js", //for cluster test
+			instances: "max",
+			exec_mode: "cluster",
+			merge_logs: true,
+			watch: false,
+			ignore_watch: ["log"],
+			time: true,
+			env: {
+				NODE_ENV: "development",
+			},
+			env_production: {
+				NODE_ENV: "production",
+				PORT: "4000",
+			},
+		},
+	],
+};
diff --git a/_old/install.sh b/_old/install.sh
new file mode 100644
index 0000000..93f4345
--- /dev/null
+++ b/_old/install.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+echo "move directory"
+cd /home/ubuntu/engineeo-server/
+echo "sudo npm install"
+sudo npm install
+echo "pm2 reload"
+sudo pm2 reload ecosystem.config.js --log-date-format="YYYY-MM-DD HH:mm Z" --env production
diff --git a/_old/middlewares/authCheck.js b/_old/middlewares/authCheck.js
new file mode 100644
index 0000000..2814f30
--- /dev/null
+++ b/_old/middlewares/authCheck.js
@@ -0,0 +1,68 @@
+const storeProvider = require("../src/Store/storeProvider");
+const userProvider = require("../src/User/userProvider");
+const adminProvider = require("../src/Admin/adminProvider");
+const examProvider = require("../src/Exam/examProvider");
+const secret_config = require("../config/secret");
+const baseResponse = require("../config/baseResponseStatus");
+const { basickResponse } = require("../config/response");
+const jwt = require("jsonwebtoken");
+
+const errorResponse = require("../utils/errorResponse");
+const authCheck = (req, res, next) => {
+	let examDetailIdx = req.params.examDetailIdx;
+	let examIdx = req.params.examIdx;
+	let promiseAuth;
+
+	const examRecordIdx = req.params.examRecordIdx;
+	const token = req.headers["x-access-token"];
+	if (!token) return res.send(basickResponse(baseResponse.TOKEN_EMPTY));
+	if (token && token.length > 9) {
+		promiseAuth = new Promise((resolve, reject) => {
+			jwt.verify(token, secret_config.jwtsecret, (err, verifiedToken) => {
+				if (err) {
+					return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+				}
+				resolve(verifiedToken);
+			});
+		});
+		promiseAuth
+			.then(async (verifiedToken) => {
+				let authResult = 0;
+				req.verifiedToken = verifiedToken;
+				const userIdx = String(verifiedToken.userIdx);
+
+				const adminIdxCheckResult = await adminProvider.adminCheck(userIdx);
+				if (adminIdxCheckResult[0].exist == 1) {
+					req.isAuth = 1;
+					return next();
+				}
+				if (examIdx) {
+					const value = await storeProvider.checkUserExamAuth(examIdx, userIdx);
+					authResult = value[0].exist;
+				}
+
+				if (examRecordIdx) {
+					const examDetailInfo = await userProvider.userExamRecordToExamDetail(examRecordIdx);
+					examDetailIdx = examDetailInfo.length ? examDetailInfo[0].examDetailIdx : null;
+				}
+				if (examDetailIdx) {
+					if (typeof examDetailIdx == "object") throw new errorResponse(baseResponse.EXAMDETAIL_NOT_EXIST, 400);
+					const value = await storeProvider.checkUserExamDetailAuth(examDetailIdx, userIdx);
+					authResult = value[0].exist;
+
+					// 무료 회차라면 권한 부여
+					const examDetailInfo = await examProvider.examDetailCheck(examDetailIdx);
+					let publicLevel = examDetailInfo[0].publicLevel;
+					if (publicLevel == 0) authResult = 1;
+				}
+				req.isAuth = authResult;
+				next();
+			})
+			.catch((err) => {
+				// token expired
+				console.log("Wrong Parameters authCheck " + examDetailIdx, examIdx, examRecordIdx);
+				return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+			});
+	}
+};
+module.exports = authCheck;
diff --git a/_old/middlewares/errorHandler.js b/_old/middlewares/errorHandler.js
new file mode 100644
index 0000000..d0afccf
--- /dev/null
+++ b/_old/middlewares/errorHandler.js
@@ -0,0 +1,35 @@
+const baseResponseStatus = require("../config/baseResponseStatus");
+const { logger } = require("../config/winston");
+const errorResponse = require("../utils/errorResponse");
+
+const errorHandler = (err, req, res, next) => {
+	let error = { ...err };
+	error.message = err.message;
+	error.stack = err.stack;
+	error.statusCode = err.statusCode || 500;
+
+	// If errorResponse with baseResponse
+	let data = {
+		isSuccess: false,
+		code: error.code || 500, //TODO : modify
+		message: error.statusCode === 500 ? "INTERNAL_SERVER_ERROR" : error.message,
+	};
+	// If errorResponse with resultResponse
+	if (error.result) data["result"] = error.result;
+
+	// Log to console for dev
+	if (error.statusCode === 400 || error.statusCode === 404) {
+		// console.log(error.stack.split("\n")[0].trim() + error.stack.split("\n")[1].trim());
+	}
+
+	// status code 500 or unknown error
+	if (error.statusCode === 500 || !error.statusCode) {
+		logger.error("UNHANDLED ERROR : \n", error);
+		console.log("UNHANDLED ERROR : \n", error);
+	}
+
+	// res.status(error.statusCode).json(data);
+	res.status(200).json(data);
+};
+
+module.exports = errorHandler;
diff --git a/_old/middlewares/jwtMiddleware.js b/_old/middlewares/jwtMiddleware.js
new file mode 100644
index 0000000..46f44eb
--- /dev/null
+++ b/_old/middlewares/jwtMiddleware.js
@@ -0,0 +1,42 @@
+const jwt = require("jsonwebtoken");
+const secret_config = require("../config/secret");
+const { resultResponse } = require("../config/response");
+const { basickResponse } = require("../config/response");
+const baseResponse = require("../config/baseResponseStatus");
+const crypto = require("crypto");
+
+const jwtMiddleware = (req, res, next) => {
+	// read the token from header or url
+	const token = req.headers["x-access-token"] || req.query.token;
+	// token does not exist
+	if (!token) {
+		return res.send(basickResponse(baseResponse.TOKEN_EMPTY));
+	}
+
+	// create a promise that decodes the token
+	const p = new Promise((resolve, reject) => {
+		jwt.verify(token, secret_config.jwtsecret, (err, verifiedToken) => {
+			if (err) reject(err);
+			resolve(verifiedToken);
+		});
+	});
+
+	// if it has failed to verify, it will return an error message
+	const onError = (error) => {
+		return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+	};
+	// process the promise
+	p.then((verifiedToken) => {
+		//비밀 번호 바뀌었을 때 검증 부분 추가 할 곳
+		req.verifiedToken = verifiedToken;
+		const memberId = String(req.verifiedToken.userIdx);
+		const memberHash = crypto
+			.createHmac("sha256", Buffer.from(secret_config.chatSecretKey, "hex"))
+			.update(memberId)
+			.digest("hex");
+		req.memberHash = memberHash;
+		next();
+	}).catch(onError);
+};
+
+module.exports = jwtMiddleware;
diff --git a/_old/middlewares/makeAuthLog.js b/_old/middlewares/makeAuthLog.js
new file mode 100644
index 0000000..49d7a69
--- /dev/null
+++ b/_old/middlewares/makeAuthLog.js
@@ -0,0 +1,49 @@
+const adminProvider = require("../src/Admin/adminProvider");
+const baseResponse = require("../config/baseResponseStatus");
+const { basickResponse } = require("../config/response");
+const userService = require("../src/User/userService");
+const secret_config = require("../config/secret");
+const examProvider = require("../src/Exam/examProvider");
+const jwt = require("jsonwebtoken");
+
+const makeAuthLog = async (req, res, next) => {
+	let examDetailIdx = req.params.examDetailIdx;
+	let type = req.query.type;
+	let publicLevel;
+
+	const token = req.headers["x-access-token"];
+	if (!token) return res.send(basickResponse(baseResponse.TOKEN_EMPTY));
+	if (token && token.length > 9) {
+		authLog = new Promise((resolve, reject) => {
+			jwt.verify(token, secret_config.jwtsecret, (err, verifiedToken) => {
+				if (err) {
+					return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+				}
+				resolve(verifiedToken);
+			});
+		});
+
+		authLog
+			.then(async (verifiedToken) => {
+				const userIdx = String(verifiedToken.userIdx);
+				// 관리자는 로그 제외
+				const adminIdxCheckResult = await adminProvider.adminCheck(userIdx);
+				if (adminIdxCheckResult[0].exist === 1) return next();
+
+				// 유료회차만 로그 기록
+				const examDetailInfo = await examProvider.examDetailCheck(examDetailIdx);
+				publicLevel = examDetailInfo[0].publicLevel;
+				if (publicLevel > 0 && req.isAuth == 1) {
+					await userService.insertUserAuthLogDetailIdx(userIdx, examDetailIdx, type);
+				}
+
+				next();
+			})
+			.catch((err) => {
+				console.log(err);
+				console.log("Wrong Parameters makeAuthLog" + examDetailIdx, publicLevel, type);
+				return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+			});
+	}
+};
+module.exports = makeAuthLog;
diff --git a/_old/package-lock.json b/_old/package-lock.json
new file mode 100644
index 0000000..235b815
--- /dev/null
+++ b/_old/package-lock.json
@@ -0,0 +1,11246 @@
+{
+	"name": "exam210506",
+	"version": "1.0.0",
+	"lockfileVersion": 2,
+	"requires": true,
+	"packages": {
+		"": {
+			"name": "exam210506",
+			"version": "1.0.0",
+			"license": "ISC",
+			"dependencies": {
+				"@sentry/node": "^7.36.0",
+				"@sentry/tracing": "^7.36.0",
+				"aws-sdk": "^2.1072.0",
+				"axios": "^0.21.4",
+				"bcrypt": "^5.0.1",
+				"body-parser": "^1.19.0",
+				"cheerio": "^1.0.0-rc.10",
+				"compression": "^1.7.4",
+				"concurrently": "^6.0.2",
+				"connect-redis": "^6.1.3",
+				"cookie-parser": "^1.4.6",
+				"cors": "^2.8.5",
+				"crypto": "^1.0.1",
+				"crypto-js": "^4.0.0",
+				"dotenv": "^15.0.0",
+				"exceljs": "^4.3.0",
+				"express": "^4.17.1",
+				"express-mysql-session": "^2.1.8",
+				"express-session": "^1.17.3",
+				"form-data": "^4.0.0",
+				"google-auth-library": "^7.3.0",
+				"googleapis": "^89.0.0",
+				"iconv-lite": "^0.6.3",
+				"jsonwebtoken": "^8.5.1",
+				"lru-cache": "^6.0.0",
+				"method-override": "^3.0.0",
+				"multer": "^1.4.3",
+				"multer-s3": "^2.10.0",
+				"mysql2": "^2.2.5",
+				"node-cache": "^5.1.2",
+				"node-cron": "^3.0.0",
+				"node-sens": "^1.3.0",
+				"nodemailer": "^6.6.2",
+				"passport": "^0.4.1",
+				"passport-kakao": "^1.0.1",
+				"pm2": "^5.1.0",
+				"readline": "^1.3.0",
+				"redis": "^4.3.1",
+				"regex-email": "^1.0.2",
+				"web-push": "^3.4.5",
+				"winston": "^3.3.3",
+				"winston-daily-rotate-file": "^4.5.5"
+			},
+			"devDependencies": {
+				"nodemon": "^2.0.20",
+				"should": "^13.2.3",
+				"should-http": "^0.1.1",
+				"supertest": "^6.1.6",
+				"swagger-jsdoc": "^6.1.0",
+				"swagger-ui-express": "^4.1.6"
+			}
+		},
+		"node_modules/@apidevtools/json-schema-ref-parser": {
+			"version": "9.0.9",
+			"resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz",
+			"integrity": "sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w==",
+			"dev": true,
+			"dependencies": {
+				"@jsdevtools/ono": "^7.1.3",
+				"@types/json-schema": "^7.0.6",
+				"call-me-maybe": "^1.0.1",
+				"js-yaml": "^4.1.0"
+			}
+		},
+		"node_modules/@apidevtools/openapi-schemas": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
+			"integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/@apidevtools/swagger-methods": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
+			"integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==",
+			"dev": true
+		},
+		"node_modules/@apidevtools/swagger-parser": {
+			"version": "10.0.2",
+			"resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.2.tgz",
+			"integrity": "sha512-JFxcEyp8RlNHgBCE98nwuTkZT6eNFPc1aosWV6wPcQph72TSEEu1k3baJD4/x1qznU+JiDdz8F5pTwabZh+Dhg==",
+			"dev": true,
+			"dependencies": {
+				"@apidevtools/json-schema-ref-parser": "^9.0.6",
+				"@apidevtools/openapi-schemas": "^2.0.4",
+				"@apidevtools/swagger-methods": "^3.0.2",
+				"@jsdevtools/ono": "^7.1.3",
+				"call-me-maybe": "^1.0.1",
+				"z-schema": "^4.2.3"
+			},
+			"peerDependencies": {
+				"openapi-types": ">=7"
+			}
+		},
+		"node_modules/@babel/code-frame": {
+			"version": "7.14.5",
+			"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz",
+			"integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==",
+			"dependencies": {
+				"@babel/highlight": "^7.14.5"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/helper-validator-identifier": {
+			"version": "7.14.5",
+			"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz",
+			"integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==",
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/highlight": {
+			"version": "7.14.5",
+			"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
+			"integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
+			"dependencies": {
+				"@babel/helper-validator-identifier": "^7.14.5",
+				"chalk": "^2.0.0",
+				"js-tokens": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/highlight/node_modules/ansi-styles": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+			"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+			"dependencies": {
+				"color-convert": "^1.9.0"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/@babel/highlight/node_modules/chalk": {
+			"version": "2.4.2",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+			"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+			"dependencies": {
+				"ansi-styles": "^3.2.1",
+				"escape-string-regexp": "^1.0.5",
+				"supports-color": "^5.3.0"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/@babel/highlight/node_modules/color-convert": {
+			"version": "1.9.3",
+			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+			"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+			"dependencies": {
+				"color-name": "1.1.3"
+			}
+		},
+		"node_modules/@babel/highlight/node_modules/color-name": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+			"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+		},
+		"node_modules/@babel/highlight/node_modules/has-flag": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+			"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/@babel/highlight/node_modules/supports-color": {
+			"version": "5.5.0",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+			"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+			"dependencies": {
+				"has-flag": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/@dabh/diagnostics": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz",
+			"integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==",
+			"dependencies": {
+				"colorspace": "1.1.x",
+				"enabled": "2.0.x",
+				"kuler": "^2.0.0"
+			}
+		},
+		"node_modules/@fast-csv/format": {
+			"version": "4.3.5",
+			"resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz",
+			"integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==",
+			"dependencies": {
+				"@types/node": "^14.0.1",
+				"lodash.escaperegexp": "^4.1.2",
+				"lodash.isboolean": "^3.0.3",
+				"lodash.isequal": "^4.5.0",
+				"lodash.isfunction": "^3.0.9",
+				"lodash.isnil": "^4.0.0"
+			}
+		},
+		"node_modules/@fast-csv/parse": {
+			"version": "4.3.6",
+			"resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz",
+			"integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==",
+			"dependencies": {
+				"@types/node": "^14.0.1",
+				"lodash.escaperegexp": "^4.1.2",
+				"lodash.groupby": "^4.6.0",
+				"lodash.isfunction": "^3.0.9",
+				"lodash.isnil": "^4.0.0",
+				"lodash.isundefined": "^3.0.1",
+				"lodash.uniq": "^4.5.0"
+			}
+		},
+		"node_modules/@jsdevtools/ono": {
+			"version": "7.1.3",
+			"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
+			"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
+			"dev": true
+		},
+		"node_modules/@mapbox/node-pre-gyp": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz",
+			"integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==",
+			"dependencies": {
+				"detect-libc": "^1.0.3",
+				"https-proxy-agent": "^5.0.0",
+				"make-dir": "^3.1.0",
+				"node-fetch": "^2.6.1",
+				"nopt": "^5.0.0",
+				"npmlog": "^4.1.2",
+				"rimraf": "^3.0.2",
+				"semver": "^7.3.4",
+				"tar": "^6.1.0"
+			},
+			"bin": {
+				"node-pre-gyp": "bin/node-pre-gyp"
+			}
+		},
+		"node_modules/@opencensus/core": {
+			"version": "0.0.9",
+			"resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.9.tgz",
+			"integrity": "sha512-31Q4VWtbzXpVUd2m9JS6HEaPjlKvNMOiF7lWKNmXF84yUcgfAFL5re7/hjDmdyQbOp32oGc+RFV78jXIldVz6Q==",
+			"dependencies": {
+				"continuation-local-storage": "^3.2.1",
+				"log-driver": "^1.2.7",
+				"semver": "^5.5.0",
+				"shimmer": "^1.2.0",
+				"uuid": "^3.2.1"
+			},
+			"engines": {
+				"node": ">=6.0"
+			}
+		},
+		"node_modules/@opencensus/core/node_modules/semver": {
+			"version": "5.7.1",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+			"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+			"bin": {
+				"semver": "bin/semver"
+			}
+		},
+		"node_modules/@opencensus/propagation-b3": {
+			"version": "0.0.8",
+			"resolved": "https://registry.npmjs.org/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz",
+			"integrity": "sha512-PffXX2AL8Sh0VHQ52jJC4u3T0H6wDK6N/4bg7xh4ngMYOIi13aR1kzVvX1sVDBgfGwDOkMbl4c54Xm3tlPx/+A==",
+			"dependencies": {
+				"@opencensus/core": "^0.0.8",
+				"uuid": "^3.2.1"
+			},
+			"engines": {
+				"node": ">=6.0"
+			}
+		},
+		"node_modules/@opencensus/propagation-b3/node_modules/@opencensus/core": {
+			"version": "0.0.8",
+			"resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.8.tgz",
+			"integrity": "sha512-yUFT59SFhGMYQgX0PhoTR0LBff2BEhPrD9io1jWfF/VDbakRfs6Pq60rjv0Z7iaTav5gQlttJCX2+VPxFWCuoQ==",
+			"dependencies": {
+				"continuation-local-storage": "^3.2.1",
+				"log-driver": "^1.2.7",
+				"semver": "^5.5.0",
+				"shimmer": "^1.2.0",
+				"uuid": "^3.2.1"
+			},
+			"engines": {
+				"node": ">=6.0"
+			}
+		},
+		"node_modules/@opencensus/propagation-b3/node_modules/semver": {
+			"version": "5.7.1",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+			"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+			"bin": {
+				"semver": "bin/semver"
+			}
+		},
+		"node_modules/@pm2/agent": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.1.tgz",
+			"integrity": "sha512-QKHMm6yexcvdDfcNE7PL9D6uEjoQPGRi+8dh+rc4Hwtbpsbh5IAvZbz3BVGjcd4HaX6pt2xGpOohG7/Y2L4QLw==",
+			"dependencies": {
+				"async": "~3.2.0",
+				"chalk": "~3.0.0",
+				"dayjs": "~1.8.24",
+				"debug": "~4.3.1",
+				"eventemitter2": "~5.0.1",
+				"fast-json-patch": "^3.0.0-1",
+				"fclone": "~1.0.11",
+				"nssocket": "0.6.0",
+				"pm2-axon": "~4.0.1",
+				"pm2-axon-rpc": "~0.7.0",
+				"proxy-agent": "~5.0.0",
+				"semver": "~7.2.0",
+				"ws": "~7.4.0"
+			}
+		},
+		"node_modules/@pm2/agent/node_modules/chalk": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+			"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+			"dependencies": {
+				"ansi-styles": "^4.1.0",
+				"supports-color": "^7.1.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/@pm2/agent/node_modules/semver": {
+			"version": "7.2.3",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.2.3.tgz",
+			"integrity": "sha512-utbW9Z7ZxVvwiIWkdOMLOR9G/NFXh2aRucghkVrEMJWuC++r3lCkBC3LwqBinyHzGMAJxY5tn6VakZGHObq5ig==",
+			"bin": {
+				"semver": "bin/semver.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/@pm2/agent/node_modules/supports-color": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+			"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+			"dependencies": {
+				"has-flag": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/@pm2/io": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/@pm2/io/-/io-5.0.0.tgz",
+			"integrity": "sha512-3rToDVJaRoob5Lq8+7Q2TZFruoEkdORxwzFpZaqF4bmH6Bkd7kAbdPrI/z8X6k1Meq5rTtScM7MmDgppH6aLlw==",
+			"dependencies": {
+				"@opencensus/core": "0.0.9",
+				"@opencensus/propagation-b3": "0.0.8",
+				"async": "~2.6.1",
+				"debug": "~4.3.1",
+				"eventemitter2": "^6.3.1",
+				"require-in-the-middle": "^5.0.0",
+				"semver": "6.3.0",
+				"shimmer": "^1.2.0",
+				"signal-exit": "^3.0.3",
+				"tslib": "1.9.3"
+			},
+			"engines": {
+				"node": ">=6.0"
+			}
+		},
+		"node_modules/@pm2/io/node_modules/async": {
+			"version": "2.6.3",
+			"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+			"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+			"dependencies": {
+				"lodash": "^4.17.14"
+			}
+		},
+		"node_modules/@pm2/io/node_modules/eventemitter2": {
+			"version": "6.4.4",
+			"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.4.tgz",
+			"integrity": "sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw=="
+		},
+		"node_modules/@pm2/io/node_modules/semver": {
+			"version": "6.3.0",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+			"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+			"bin": {
+				"semver": "bin/semver.js"
+			}
+		},
+		"node_modules/@pm2/io/node_modules/tslib": {
+			"version": "1.9.3",
+			"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+			"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
+		},
+		"node_modules/@pm2/js-api": {
+			"version": "0.6.7",
+			"resolved": "https://registry.npmjs.org/@pm2/js-api/-/js-api-0.6.7.tgz",
+			"integrity": "sha512-jiJUhbdsK+5C4zhPZNnyA3wRI01dEc6a2GhcQ9qI38DyIk+S+C8iC3fGjcjUbt/viLYKPjlAaE+hcT2/JMQPXw==",
+			"dependencies": {
+				"async": "^2.6.3",
+				"axios": "^0.21.0",
+				"debug": "~4.3.1",
+				"eventemitter2": "^6.3.1",
+				"ws": "^7.0.0"
+			},
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/@pm2/js-api/node_modules/async": {
+			"version": "2.6.3",
+			"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+			"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+			"dependencies": {
+				"lodash": "^4.17.14"
+			}
+		},
+		"node_modules/@pm2/js-api/node_modules/eventemitter2": {
+			"version": "6.4.4",
+			"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.4.tgz",
+			"integrity": "sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw=="
+		},
+		"node_modules/@redis/bloom": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.1.0.tgz",
+			"integrity": "sha512-9QovlxmpRtvxVbN0UBcv8WfdSMudNZZTFqCsnBszcQXqaZb/TVe30ScgGEO7u1EAIacTPAo7/oCYjYAxiHLanQ==",
+			"peerDependencies": {
+				"@redis/client": "^1.0.0"
+			}
+		},
+		"node_modules/@redis/client": {
+			"version": "1.4.2",
+			"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.4.2.tgz",
+			"integrity": "sha512-oUdEjE0I7JS5AyaAjkD3aOXn9NhO7XKyPyXEyrgFDu++VrVBHUPnV6dgEya9TcMuj5nIJRuCzCm8ZP+c9zCHPw==",
+			"dependencies": {
+				"cluster-key-slot": "1.1.1",
+				"generic-pool": "3.9.0",
+				"yallist": "4.0.0"
+			},
+			"engines": {
+				"node": ">=14"
+			}
+		},
+		"node_modules/@redis/graph": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz",
+			"integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==",
+			"peerDependencies": {
+				"@redis/client": "^1.0.0"
+			}
+		},
+		"node_modules/@redis/json": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz",
+			"integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==",
+			"peerDependencies": {
+				"@redis/client": "^1.0.0"
+			}
+		},
+		"node_modules/@redis/search": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.0.tgz",
+			"integrity": "sha512-NyFZEVnxIJEybpy+YskjgOJRNsfTYqaPbK/Buv6W2kmFNaRk85JiqjJZA5QkRmWvGbyQYwoO5QfDi2wHskKrQQ==",
+			"peerDependencies": {
+				"@redis/client": "^1.0.0"
+			}
+		},
+		"node_modules/@redis/time-series": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz",
+			"integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==",
+			"peerDependencies": {
+				"@redis/client": "^1.0.0"
+			}
+		},
+		"node_modules/@sentry/core": {
+			"version": "7.36.0",
+			"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.36.0.tgz",
+			"integrity": "sha512-lq1MlcMhvm7QIwUOknFeufkg4M6QREY3s61y6pm1o+o3vSqB7Hz0D19xlyEpP62qMn8OyuttVKOVK1UfGc2EyQ==",
+			"dependencies": {
+				"@sentry/types": "7.36.0",
+				"@sentry/utils": "7.36.0",
+				"tslib": "^1.9.3"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/@sentry/node": {
+			"version": "7.36.0",
+			"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.36.0.tgz",
+			"integrity": "sha512-nAHAY+Rbn5OlTpNX/i6wYrmw3hT/BtwPZ/vNU52cKgw7CpeE1UrCeFjnPn18rQPB7lIh7x0vNvoaPrfemRzpSQ==",
+			"dependencies": {
+				"@sentry/core": "7.36.0",
+				"@sentry/types": "7.36.0",
+				"@sentry/utils": "7.36.0",
+				"cookie": "^0.4.1",
+				"https-proxy-agent": "^5.0.0",
+				"lru_map": "^0.3.3",
+				"tslib": "^1.9.3"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/@sentry/node/node_modules/cookie": {
+			"version": "0.4.2",
+			"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+			"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/@sentry/tracing": {
+			"version": "7.36.0",
+			"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.36.0.tgz",
+			"integrity": "sha512-5R5mfWMDncOcTMmmyYMjgus1vZJzIFw4LHaSbrX7e1IRNT/6vFyNeVxATa2ePXb9mI3XHo5f2p7YrnreAtaSXw==",
+			"dependencies": {
+				"@sentry/core": "7.36.0",
+				"@sentry/types": "7.36.0",
+				"@sentry/utils": "7.36.0",
+				"tslib": "^1.9.3"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/@sentry/types": {
+			"version": "7.36.0",
+			"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.36.0.tgz",
+			"integrity": "sha512-uvfwUn3okAWSZ948D/xqBrkc3Sn6TeHUgi3+p/dTTNGAXXskzavgfgQ4rSW7f3YD4LL+boZojpoIARVLodMGuA==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/@sentry/utils": {
+			"version": "7.36.0",
+			"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.36.0.tgz",
+			"integrity": "sha512-mgDi5X5Bm0sydCzXpnyKD/sD98yc2qnKXyRdNX4HRRwruhC/P53LT0hGhZXsyqsB/l8OAMl0zWXJLg0xONQsEw==",
+			"dependencies": {
+				"@sentry/types": "7.36.0",
+				"tslib": "^1.9.3"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/@tootallnate/once": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+			"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/@types/json-schema": {
+			"version": "7.0.9",
+			"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
+			"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
+			"dev": true
+		},
+		"node_modules/@types/node": {
+			"version": "14.17.19",
+			"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.19.tgz",
+			"integrity": "sha512-jjYI6NkyfXykucU6ELEoT64QyKOdvaA6enOqKtP4xUsGY0X0ZUZz29fUmrTRo+7v7c6TgDu82q3GHHaCEkqZwA=="
+		},
+		"node_modules/@types/normalize-package-data": {
+			"version": "2.4.0",
+			"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+			"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA=="
+		},
+		"node_modules/abbrev": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+			"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+		},
+		"node_modules/abort-controller": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+			"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+			"dependencies": {
+				"event-target-shim": "^5.0.0"
+			},
+			"engines": {
+				"node": ">=6.5"
+			}
+		},
+		"node_modules/accepts": {
+			"version": "1.3.7",
+			"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+			"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+			"dependencies": {
+				"mime-types": "~2.1.24",
+				"negotiator": "0.6.2"
+			},
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/agent-base": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+			"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+			"dependencies": {
+				"debug": "4"
+			},
+			"engines": {
+				"node": ">= 6.0.0"
+			}
+		},
+		"node_modules/amp": {
+			"version": "0.3.1",
+			"resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz",
+			"integrity": "sha1-at+NWKdPNh6CwfqNOJwHnhOfxH0="
+		},
+		"node_modules/amp-message": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/amp-message/-/amp-message-0.1.2.tgz",
+			"integrity": "sha1-p48cmJlQh602GSpBKY5NtJ49/EU=",
+			"dependencies": {
+				"amp": "0.3.1"
+			}
+		},
+		"node_modules/ansi-colors": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+			"integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/ansi-regex": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+			"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/ansi-styles": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+			"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+			"dependencies": {
+				"color-convert": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-styles?sponsor=1"
+			}
+		},
+		"node_modules/anymatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+			"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+			"dependencies": {
+				"normalize-path": "^3.0.0",
+				"picomatch": "^2.0.4"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/append-field": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+			"integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY="
+		},
+		"node_modules/aproba": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+			"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+		},
+		"node_modules/archiver": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz",
+			"integrity": "sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==",
+			"dependencies": {
+				"archiver-utils": "^2.1.0",
+				"async": "^3.2.0",
+				"buffer-crc32": "^0.2.1",
+				"readable-stream": "^3.6.0",
+				"readdir-glob": "^1.0.0",
+				"tar-stream": "^2.2.0",
+				"zip-stream": "^4.1.0"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/archiver-utils": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
+			"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
+			"dependencies": {
+				"glob": "^7.1.4",
+				"graceful-fs": "^4.2.0",
+				"lazystream": "^1.0.0",
+				"lodash.defaults": "^4.2.0",
+				"lodash.difference": "^4.5.0",
+				"lodash.flatten": "^4.4.0",
+				"lodash.isplainobject": "^4.0.6",
+				"lodash.union": "^4.6.0",
+				"normalize-path": "^3.0.0",
+				"readable-stream": "^2.0.0"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/archiver/node_modules/readable-stream": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+			"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+			"dependencies": {
+				"inherits": "^2.0.3",
+				"string_decoder": "^1.1.1",
+				"util-deprecate": "^1.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/are-we-there-yet": {
+			"version": "1.1.5",
+			"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+			"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+			"dependencies": {
+				"delegates": "^1.0.0",
+				"readable-stream": "^2.0.6"
+			}
+		},
+		"node_modules/argparse": {
+			"version": "1.0.10",
+			"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+			"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+			"dependencies": {
+				"sprintf-js": "~1.0.2"
+			}
+		},
+		"node_modules/array-flatten": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+			"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+		},
+		"node_modules/arrify": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+			"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/asn1.js": {
+			"version": "5.4.1",
+			"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+			"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+			"dependencies": {
+				"bn.js": "^4.0.0",
+				"inherits": "^2.0.1",
+				"minimalistic-assert": "^1.0.0",
+				"safer-buffer": "^2.1.0"
+			}
+		},
+		"node_modules/ast-types": {
+			"version": "0.13.4",
+			"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
+			"integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
+			"dependencies": {
+				"tslib": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/ast-types/node_modules/tslib": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+			"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+		},
+		"node_modules/async": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
+			"integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
+		},
+		"node_modules/async-listener": {
+			"version": "0.6.10",
+			"resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz",
+			"integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==",
+			"dependencies": {
+				"semver": "^5.3.0",
+				"shimmer": "^1.1.0"
+			},
+			"engines": {
+				"node": "<=0.11.8 || >0.11.10"
+			}
+		},
+		"node_modules/async-listener/node_modules/semver": {
+			"version": "5.7.1",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+			"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+			"bin": {
+				"semver": "bin/semver"
+			}
+		},
+		"node_modules/asynckit": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+			"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+		},
+		"node_modules/aws-sdk": {
+			"version": "2.1072.0",
+			"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1072.0.tgz",
+			"integrity": "sha512-b0gEHuC6xTGduPTS+ZCScurw9RTyOned9gf6H0rDagW8hdSMebsFQy84ZreeiZHHChyKyWrNUbUFugOYdw+WXw==",
+			"dependencies": {
+				"buffer": "4.9.2",
+				"events": "1.1.1",
+				"ieee754": "1.1.13",
+				"jmespath": "0.16.0",
+				"querystring": "0.2.0",
+				"sax": "1.2.1",
+				"url": "0.10.3",
+				"uuid": "3.3.2",
+				"xml2js": "0.4.19"
+			},
+			"engines": {
+				"node": ">= 10.0.0"
+			}
+		},
+		"node_modules/aws-sdk/node_modules/sax": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
+			"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
+		},
+		"node_modules/aws-sdk/node_modules/uuid": {
+			"version": "3.3.2",
+			"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+			"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+			"deprecated": "Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.",
+			"bin": {
+				"uuid": "bin/uuid"
+			}
+		},
+		"node_modules/axios": {
+			"version": "0.21.4",
+			"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+			"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+			"dependencies": {
+				"follow-redirects": "^1.14.0"
+			}
+		},
+		"node_modules/balanced-match": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+			"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+		},
+		"node_modules/base64-js": {
+			"version": "1.5.1",
+			"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+			"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
+		"node_modules/bcrypt": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz",
+			"integrity": "sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==",
+			"hasInstallScript": true,
+			"dependencies": {
+				"@mapbox/node-pre-gyp": "^1.0.0",
+				"node-addon-api": "^3.1.0"
+			},
+			"engines": {
+				"node": ">= 10.0.0"
+			}
+		},
+		"node_modules/big-integer": {
+			"version": "1.6.49",
+			"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.49.tgz",
+			"integrity": "sha512-KJ7VhqH+f/BOt9a3yMwJNmcZjG53ijWMTjSAGMveQWyLwqIiwkjNP5PFgDob3Snnx86SjDj6I89fIbv0dkQeNw==",
+			"engines": {
+				"node": ">=0.6"
+			}
+		},
+		"node_modules/bignumber.js": {
+			"version": "9.0.1",
+			"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
+			"integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==",
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/binary": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
+			"integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=",
+			"dependencies": {
+				"buffers": "~0.1.1",
+				"chainsaw": "~0.1.0"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/binary-extensions": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+			"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/bl": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+			"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+			"dependencies": {
+				"buffer": "^5.5.0",
+				"inherits": "^2.0.4",
+				"readable-stream": "^3.4.0"
+			}
+		},
+		"node_modules/bl/node_modules/buffer": {
+			"version": "5.7.1",
+			"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+			"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			],
+			"dependencies": {
+				"base64-js": "^1.3.1",
+				"ieee754": "^1.1.13"
+			}
+		},
+		"node_modules/bl/node_modules/readable-stream": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+			"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+			"dependencies": {
+				"inherits": "^2.0.3",
+				"string_decoder": "^1.1.1",
+				"util-deprecate": "^1.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/blessed": {
+			"version": "0.1.81",
+			"resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz",
+			"integrity": "sha1-+WLWh+wsNpVwrnGvhDJW5tDKESk=",
+			"bin": {
+				"blessed": "bin/tput.js"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/bluebird": {
+			"version": "3.4.7",
+			"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
+			"integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM="
+		},
+		"node_modules/bn.js": {
+			"version": "4.12.0",
+			"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+			"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+		},
+		"node_modules/bodec": {
+			"version": "0.1.0",
+			"resolved": "https://registry.npmjs.org/bodec/-/bodec-0.1.0.tgz",
+			"integrity": "sha1-vIUVVUMPI8n3ZQp172TGqUw0GMw="
+		},
+		"node_modules/body-parser": {
+			"version": "1.19.0",
+			"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+			"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+			"dependencies": {
+				"bytes": "3.1.0",
+				"content-type": "~1.0.4",
+				"debug": "2.6.9",
+				"depd": "~1.1.2",
+				"http-errors": "1.7.2",
+				"iconv-lite": "0.4.24",
+				"on-finished": "~2.3.0",
+				"qs": "6.7.0",
+				"raw-body": "2.4.0",
+				"type-is": "~1.6.17"
+			},
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/body-parser/node_modules/debug": {
+			"version": "2.6.9",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+			"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+			"dependencies": {
+				"ms": "2.0.0"
+			}
+		},
+		"node_modules/body-parser/node_modules/iconv-lite": {
+			"version": "0.4.24",
+			"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+			"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+			"dependencies": {
+				"safer-buffer": ">= 2.1.2 < 3"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/body-parser/node_modules/ms": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+			"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+		},
+		"node_modules/boolbase": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+			"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
+		},
+		"node_modules/boolean": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.2.tgz",
+			"integrity": "sha512-YN6UmV0FfLlBVvRvNPx3pz5W/mUoYB24J4WSXOKP/OOJpi+Oq6WYqPaNTHzjI0QzwWtnvEd5CGYyQPgp1jFxnw=="
+		},
+		"node_modules/brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"dependencies": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"node_modules/braces": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+			"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+			"dependencies": {
+				"fill-range": "^7.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/buffer": {
+			"version": "4.9.2",
+			"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+			"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+			"dependencies": {
+				"base64-js": "^1.0.2",
+				"ieee754": "^1.1.4",
+				"isarray": "^1.0.0"
+			}
+		},
+		"node_modules/buffer-crc32": {
+			"version": "0.2.13",
+			"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+			"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=",
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/buffer-equal-constant-time": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+			"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+		},
+		"node_modules/buffer-from": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+			"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
+		},
+		"node_modules/buffer-indexof-polyfill": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz",
+			"integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==",
+			"engines": {
+				"node": ">=0.10"
+			}
+		},
+		"node_modules/buffers": {
+			"version": "0.1.1",
+			"resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
+			"integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=",
+			"engines": {
+				"node": ">=0.2.0"
+			}
+		},
+		"node_modules/busboy": {
+			"version": "0.2.14",
+			"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
+			"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
+			"dependencies": {
+				"dicer": "0.2.5",
+				"readable-stream": "1.1.x"
+			},
+			"engines": {
+				"node": ">=0.8.0"
+			}
+		},
+		"node_modules/busboy/node_modules/isarray": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+			"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+		},
+		"node_modules/busboy/node_modules/readable-stream": {
+			"version": "1.1.14",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+			"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+			"dependencies": {
+				"core-util-is": "~1.0.0",
+				"inherits": "~2.0.1",
+				"isarray": "0.0.1",
+				"string_decoder": "~0.10.x"
+			}
+		},
+		"node_modules/busboy/node_modules/string_decoder": {
+			"version": "0.10.31",
+			"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+			"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+		},
+		"node_modules/bytes": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+			"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/call-bind": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+			"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+			"dev": true,
+			"dependencies": {
+				"function-bind": "^1.1.1",
+				"get-intrinsic": "^1.0.2"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/call-me-maybe": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
+			"integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=",
+			"dev": true
+		},
+		"node_modules/chainsaw": {
+			"version": "0.1.0",
+			"resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
+			"integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=",
+			"dependencies": {
+				"traverse": ">=0.3.0 <0.4"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/chalk": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+			"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
+			"dependencies": {
+				"ansi-styles": "^4.1.0",
+				"supports-color": "^7.1.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/chalk?sponsor=1"
+			}
+		},
+		"node_modules/chalk/node_modules/supports-color": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+			"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+			"dependencies": {
+				"has-flag": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/charm": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz",
+			"integrity": "sha1-BsIe7RobBq62dVPNxT4jJ0usIpY="
+		},
+		"node_modules/cheerio": {
+			"version": "1.0.0-rc.10",
+			"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz",
+			"integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==",
+			"dependencies": {
+				"cheerio-select": "^1.5.0",
+				"dom-serializer": "^1.3.2",
+				"domhandler": "^4.2.0",
+				"htmlparser2": "^6.1.0",
+				"parse5": "^6.0.1",
+				"parse5-htmlparser2-tree-adapter": "^6.0.1",
+				"tslib": "^2.2.0"
+			},
+			"engines": {
+				"node": ">= 6"
+			},
+			"funding": {
+				"url": "https://github.com/cheeriojs/cheerio?sponsor=1"
+			}
+		},
+		"node_modules/cheerio-select": {
+			"version": "1.5.0",
+			"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz",
+			"integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==",
+			"dependencies": {
+				"css-select": "^4.1.3",
+				"css-what": "^5.0.1",
+				"domelementtype": "^2.2.0",
+				"domhandler": "^4.2.0",
+				"domutils": "^2.7.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/fb55"
+			}
+		},
+		"node_modules/cheerio/node_modules/tslib": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+			"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+		},
+		"node_modules/chokidar": {
+			"version": "3.5.2",
+			"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
+			"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+			"dependencies": {
+				"anymatch": "~3.1.2",
+				"braces": "~3.0.2",
+				"glob-parent": "~5.1.2",
+				"is-binary-path": "~2.1.0",
+				"is-glob": "~4.0.1",
+				"normalize-path": "~3.0.0",
+				"readdirp": "~3.6.0"
+			},
+			"engines": {
+				"node": ">= 8.10.0"
+			},
+			"optionalDependencies": {
+				"fsevents": "~2.3.2"
+			}
+		},
+		"node_modules/chownr": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+			"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/cli-tableau": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/cli-tableau/-/cli-tableau-2.0.1.tgz",
+			"integrity": "sha512-he+WTicka9cl0Fg/y+YyxcN6/bfQ/1O3QmgxRXDhABKqLzvoOSM4fMzp39uMyLBulAFuywD2N7UaoQE7WaADxQ==",
+			"dependencies": {
+				"chalk": "3.0.0"
+			},
+			"engines": {
+				"node": ">=8.10.0"
+			}
+		},
+		"node_modules/cli-tableau/node_modules/chalk": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+			"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+			"dependencies": {
+				"ansi-styles": "^4.1.0",
+				"supports-color": "^7.1.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/cli-tableau/node_modules/supports-color": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+			"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+			"dependencies": {
+				"has-flag": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/cliui": {
+			"version": "7.0.4",
+			"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+			"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+			"dependencies": {
+				"string-width": "^4.2.0",
+				"strip-ansi": "^6.0.0",
+				"wrap-ansi": "^7.0.0"
+			}
+		},
+		"node_modules/cliui/node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/cliui/node_modules/is-fullwidth-code-point": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+			"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/cliui/node_modules/string-width": {
+			"version": "4.2.2",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+			"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/cliui/node_modules/strip-ansi": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+			"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+			"dependencies": {
+				"ansi-regex": "^5.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/clone": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+			"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
+		"node_modules/cluster-key-slot": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz",
+			"integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/code-point-at": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+			"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/color": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz",
+			"integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==",
+			"dependencies": {
+				"color-convert": "^1.9.1",
+				"color-string": "^1.5.2"
+			}
+		},
+		"node_modules/color-convert": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+			"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+			"dependencies": {
+				"color-name": "~1.1.4"
+			},
+			"engines": {
+				"node": ">=7.0.0"
+			}
+		},
+		"node_modules/color-name": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+			"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+		},
+		"node_modules/color-string": {
+			"version": "1.5.5",
+			"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz",
+			"integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==",
+			"dependencies": {
+				"color-name": "^1.0.0",
+				"simple-swizzle": "^0.2.2"
+			}
+		},
+		"node_modules/color/node_modules/color-convert": {
+			"version": "1.9.3",
+			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+			"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+			"dependencies": {
+				"color-name": "1.1.3"
+			}
+		},
+		"node_modules/color/node_modules/color-name": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+			"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+		},
+		"node_modules/colors": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+			"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+			"engines": {
+				"node": ">=0.1.90"
+			}
+		},
+		"node_modules/colorspace": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz",
+			"integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==",
+			"dependencies": {
+				"color": "3.0.x",
+				"text-hex": "1.0.x"
+			}
+		},
+		"node_modules/combined-stream": {
+			"version": "1.0.8",
+			"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+			"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+			"dependencies": {
+				"delayed-stream": "~1.0.0"
+			},
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/commander": {
+			"version": "2.15.1",
+			"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+			"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
+		},
+		"node_modules/component-emitter": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+			"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+			"dev": true
+		},
+		"node_modules/compress-commons": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz",
+			"integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==",
+			"dependencies": {
+				"buffer-crc32": "^0.2.13",
+				"crc32-stream": "^4.0.2",
+				"normalize-path": "^3.0.0",
+				"readable-stream": "^3.6.0"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/compress-commons/node_modules/readable-stream": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+			"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+			"dependencies": {
+				"inherits": "^2.0.3",
+				"string_decoder": "^1.1.1",
+				"util-deprecate": "^1.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/compressible": {
+			"version": "2.0.18",
+			"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+			"integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+			"dependencies": {
+				"mime-db": ">= 1.43.0 < 2"
+			},
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/compression": {
+			"version": "1.7.4",
+			"resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+			"integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+			"dependencies": {
+				"accepts": "~1.3.5",
+				"bytes": "3.0.0",
+				"compressible": "~2.0.16",
+				"debug": "2.6.9",
+				"on-headers": "~1.0.2",
+				"safe-buffer": "5.1.2",
+				"vary": "~1.1.2"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/compression/node_modules/bytes": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+			"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/compression/node_modules/debug": {
+			"version": "2.6.9",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+			"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+			"dependencies": {
+				"ms": "2.0.0"
+			}
+		},
+		"node_modules/compression/node_modules/ms": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+			"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+		},
+		"node_modules/concat-map": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+			"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+		},
+		"node_modules/concat-stream": {
+			"version": "1.6.2",
+			"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+			"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+			"engines": [
+				"node >= 0.8"
+			],
+			"dependencies": {
+				"buffer-from": "^1.0.0",
+				"inherits": "^2.0.3",
+				"readable-stream": "^2.2.2",
+				"typedarray": "^0.0.6"
+			}
+		},
+		"node_modules/concurrently": {
+			"version": "6.2.0",
+			"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.2.0.tgz",
+			"integrity": "sha512-v9I4Y3wFoXCSY2L73yYgwA9ESrQMpRn80jMcqMgHx720Hecz2GZAvTI6bREVST6lkddNypDKRN22qhK0X8Y00g==",
+			"dependencies": {
+				"chalk": "^4.1.0",
+				"date-fns": "^2.16.1",
+				"lodash": "^4.17.21",
+				"read-pkg": "^5.2.0",
+				"rxjs": "^6.6.3",
+				"spawn-command": "^0.0.2-1",
+				"supports-color": "^8.1.0",
+				"tree-kill": "^1.2.2",
+				"yargs": "^16.2.0"
+			},
+			"bin": {
+				"concurrently": "bin/concurrently.js"
+			},
+			"engines": {
+				"node": ">=10.0.0"
+			}
+		},
+		"node_modules/connect-redis": {
+			"version": "6.1.3",
+			"resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-6.1.3.tgz",
+			"integrity": "sha512-aaNluLlAn/3JPxRwdzw7lhvEoU6Enb+d83xnokUNhC9dktqBoawKWL+WuxinxvBLTz6q9vReTnUDnUslaz74aw==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/console-control-strings": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+			"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+		},
+		"node_modules/content-disposition": {
+			"version": "0.5.3",
+			"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+			"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+			"dependencies": {
+				"safe-buffer": "5.1.2"
+			},
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/content-type": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+			"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/continuation-local-storage": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz",
+			"integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==",
+			"dependencies": {
+				"async-listener": "^0.6.0",
+				"emitter-listener": "^1.1.1"
+			}
+		},
+		"node_modules/cookie": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+			"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/cookie-parser": {
+			"version": "1.4.6",
+			"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
+			"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
+			"dependencies": {
+				"cookie": "0.4.1",
+				"cookie-signature": "1.0.6"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/cookie-parser/node_modules/cookie": {
+			"version": "0.4.1",
+			"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+			"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/cookie-signature": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+			"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+		},
+		"node_modules/cookiejar": {
+			"version": "2.1.3",
+			"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz",
+			"integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==",
+			"dev": true
+		},
+		"node_modules/core-util-is": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+			"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+		},
+		"node_modules/cors": {
+			"version": "2.8.5",
+			"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+			"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+			"dependencies": {
+				"object-assign": "^4",
+				"vary": "^1"
+			},
+			"engines": {
+				"node": ">= 0.10"
+			}
+		},
+		"node_modules/crc-32": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz",
+			"integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==",
+			"dependencies": {
+				"exit-on-epipe": "~1.0.1",
+				"printj": "~1.1.0"
+			},
+			"bin": {
+				"crc32": "bin/crc32.njs"
+			},
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
+		"node_modules/crc32-stream": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz",
+			"integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==",
+			"dependencies": {
+				"crc-32": "^1.2.0",
+				"readable-stream": "^3.4.0"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/crc32-stream/node_modules/readable-stream": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+			"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+			"dependencies": {
+				"inherits": "^2.0.3",
+				"string_decoder": "^1.1.1",
+				"util-deprecate": "^1.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/cron": {
+			"version": "1.8.2",
+			"resolved": "https://registry.npmjs.org/cron/-/cron-1.8.2.tgz",
+			"integrity": "sha512-Gk2c4y6xKEO8FSAUTklqtfSr7oTq0CiPQeLBG5Fl0qoXpZyMcj1SG59YL+hqq04bu6/IuEA7lMkYDAplQNKkyg==",
+			"dependencies": {
+				"moment-timezone": "^0.5.x"
+			}
+		},
+		"node_modules/crypto": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz",
+			"integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==",
+			"deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in."
+		},
+		"node_modules/crypto-js": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
+			"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
+		},
+		"node_modules/css-select": {
+			"version": "4.1.3",
+			"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz",
+			"integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==",
+			"dependencies": {
+				"boolbase": "^1.0.0",
+				"css-what": "^5.0.0",
+				"domhandler": "^4.2.0",
+				"domutils": "^2.6.0",
+				"nth-check": "^2.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/fb55"
+			}
+		},
+		"node_modules/css-what": {
+			"version": "5.1.0",
+			"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
+			"integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==",
+			"engines": {
+				"node": ">= 6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/fb55"
+			}
+		},
+		"node_modules/culvert": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/culvert/-/culvert-0.1.2.tgz",
+			"integrity": "sha1-lQL18BVKLVoioCPnn3HMk2+m728="
+		},
+		"node_modules/data-uri-to-buffer": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
+			"integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==",
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/date-fns": {
+			"version": "2.22.1",
+			"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.22.1.tgz",
+			"integrity": "sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg==",
+			"engines": {
+				"node": ">=0.11"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/date-fns"
+			}
+		},
+		"node_modules/dayjs": {
+			"version": "1.8.36",
+			"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz",
+			"integrity": "sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw=="
+		},
+		"node_modules/debug": {
+			"version": "4.3.4",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+			"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+			"dependencies": {
+				"ms": "2.1.2"
+			},
+			"engines": {
+				"node": ">=6.0"
+			},
+			"peerDependenciesMeta": {
+				"supports-color": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/deep-is": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+			"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
+		},
+		"node_modules/degenerator": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.1.tgz",
+			"integrity": "sha512-LFsIFEeLPlKvAKXu7j3ssIG6RT0TbI7/GhsqrI0DnHASEQjXQ0LUSYcjJteGgRGmZbl1TnMSxpNQIAiJ7Du5TQ==",
+			"dependencies": {
+				"ast-types": "^0.13.2",
+				"escodegen": "^1.8.1",
+				"esprima": "^4.0.0",
+				"vm2": "^3.9.3"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/delayed-stream": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+			"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
+		"node_modules/delegates": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+			"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+		},
+		"node_modules/denque": {
+			"version": "1.5.0",
+			"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
+			"integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==",
+			"engines": {
+				"node": ">=0.10"
+			}
+		},
+		"node_modules/depd": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+			"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/destroy": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+			"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+		},
+		"node_modules/detect-libc": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+			"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
+			"bin": {
+				"detect-libc": "bin/detect-libc.js"
+			},
+			"engines": {
+				"node": ">=0.10"
+			}
+		},
+		"node_modules/dicer": {
+			"version": "0.2.5",
+			"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
+			"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
+			"dependencies": {
+				"readable-stream": "1.1.x",
+				"streamsearch": "0.1.2"
+			},
+			"engines": {
+				"node": ">=0.8.0"
+			}
+		},
+		"node_modules/dicer/node_modules/isarray": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+			"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+		},
+		"node_modules/dicer/node_modules/readable-stream": {
+			"version": "1.1.14",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+			"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+			"dependencies": {
+				"core-util-is": "~1.0.0",
+				"inherits": "~2.0.1",
+				"isarray": "0.0.1",
+				"string_decoder": "~0.10.x"
+			}
+		},
+		"node_modules/dicer/node_modules/string_decoder": {
+			"version": "0.10.31",
+			"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+			"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+		},
+		"node_modules/doctrine": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+			"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+			"dev": true,
+			"dependencies": {
+				"esutils": "^2.0.2"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/dom-serializer": {
+			"version": "1.3.2",
+			"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+			"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+			"dependencies": {
+				"domelementtype": "^2.0.1",
+				"domhandler": "^4.2.0",
+				"entities": "^2.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+			}
+		},
+		"node_modules/domelementtype": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+			"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/fb55"
+				}
+			]
+		},
+		"node_modules/domhandler": {
+			"version": "4.2.2",
+			"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz",
+			"integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==",
+			"dependencies": {
+				"domelementtype": "^2.2.0"
+			},
+			"engines": {
+				"node": ">= 4"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/domhandler?sponsor=1"
+			}
+		},
+		"node_modules/domutils": {
+			"version": "2.8.0",
+			"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+			"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+			"dependencies": {
+				"dom-serializer": "^1.0.1",
+				"domelementtype": "^2.2.0",
+				"domhandler": "^4.2.0"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/domutils?sponsor=1"
+			}
+		},
+		"node_modules/dotenv": {
+			"version": "15.0.0",
+			"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-15.0.0.tgz",
+			"integrity": "sha512-/l1sXXm79ry34KwwS0y4oVZjB468iw/6u9g1W26dtexKcIJAnVL2pMF+hxQwzZ7LutxOwEgtym9eIxvX33CMKg==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/duplexer2": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+			"integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+			"dependencies": {
+				"readable-stream": "^2.0.2"
+			}
+		},
+		"node_modules/ecdsa-sig-formatter": {
+			"version": "1.0.11",
+			"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+			"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+			"dependencies": {
+				"safe-buffer": "^5.0.1"
+			}
+		},
+		"node_modules/ee-first": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+			"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+		},
+		"node_modules/emitter-listener": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz",
+			"integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==",
+			"dependencies": {
+				"shimmer": "^1.2.0"
+			}
+		},
+		"node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+		},
+		"node_modules/enabled": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
+			"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
+		},
+		"node_modules/encodeurl": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+			"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/end-of-stream": {
+			"version": "1.4.4",
+			"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+			"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+			"dependencies": {
+				"once": "^1.4.0"
+			}
+		},
+		"node_modules/enquirer": {
+			"version": "2.3.6",
+			"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+			"integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+			"dependencies": {
+				"ansi-colors": "^4.1.1"
+			},
+			"engines": {
+				"node": ">=8.6"
+			}
+		},
+		"node_modules/entities": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+			"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+			"funding": {
+				"url": "https://github.com/fb55/entities?sponsor=1"
+			}
+		},
+		"node_modules/error-ex": {
+			"version": "1.3.2",
+			"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+			"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+			"dependencies": {
+				"is-arrayish": "^0.2.1"
+			}
+		},
+		"node_modules/escalade": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+			"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/escape-html": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+			"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+		},
+		"node_modules/escape-string-regexp": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+			"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+			"engines": {
+				"node": ">=0.8.0"
+			}
+		},
+		"node_modules/escodegen": {
+			"version": "1.14.3",
+			"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+			"integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+			"dependencies": {
+				"esprima": "^4.0.1",
+				"estraverse": "^4.2.0",
+				"esutils": "^2.0.2",
+				"optionator": "^0.8.1"
+			},
+			"bin": {
+				"escodegen": "bin/escodegen.js",
+				"esgenerate": "bin/esgenerate.js"
+			},
+			"engines": {
+				"node": ">=4.0"
+			},
+			"optionalDependencies": {
+				"source-map": "~0.6.1"
+			}
+		},
+		"node_modules/esprima": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+			"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+			"bin": {
+				"esparse": "bin/esparse.js",
+				"esvalidate": "bin/esvalidate.js"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/estraverse": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+			"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/esutils": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+			"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/etag": {
+			"version": "1.8.1",
+			"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+			"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/event-target-shim": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+			"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/eventemitter2": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz",
+			"integrity": "sha1-YZegldX7a1folC9v1+qtY6CclFI="
+		},
+		"node_modules/events": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+			"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
+			"engines": {
+				"node": ">=0.4.x"
+			}
+		},
+		"node_modules/exceljs": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.3.0.tgz",
+			"integrity": "sha512-hTAeo5b5TPvf8Z02I2sKIT4kSfCnOO2bCxYX8ABqODCdAjppI3gI9VYiGCQQYVcBaBSKlFDMKlAQRqC+kV9O8w==",
+			"dependencies": {
+				"archiver": "^5.0.0",
+				"dayjs": "^1.8.34",
+				"fast-csv": "^4.3.1",
+				"jszip": "^3.5.0",
+				"readable-stream": "^3.6.0",
+				"saxes": "^5.0.1",
+				"tmp": "^0.2.0",
+				"unzipper": "^0.10.11",
+				"uuid": "^8.3.0"
+			},
+			"engines": {
+				"node": ">=8.3.0"
+			}
+		},
+		"node_modules/exceljs/node_modules/readable-stream": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+			"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+			"dependencies": {
+				"inherits": "^2.0.3",
+				"string_decoder": "^1.1.1",
+				"util-deprecate": "^1.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/exceljs/node_modules/uuid": {
+			"version": "8.3.2",
+			"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+			"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+			"bin": {
+				"uuid": "dist/bin/uuid"
+			}
+		},
+		"node_modules/exit-on-epipe": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
+			"integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==",
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
+		"node_modules/express": {
+			"version": "4.17.1",
+			"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+			"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+			"dependencies": {
+				"accepts": "~1.3.7",
+				"array-flatten": "1.1.1",
+				"body-parser": "1.19.0",
+				"content-disposition": "0.5.3",
+				"content-type": "~1.0.4",
+				"cookie": "0.4.0",
+				"cookie-signature": "1.0.6",
+				"debug": "2.6.9",
+				"depd": "~1.1.2",
+				"encodeurl": "~1.0.2",
+				"escape-html": "~1.0.3",
+				"etag": "~1.8.1",
+				"finalhandler": "~1.1.2",
+				"fresh": "0.5.2",
+				"merge-descriptors": "1.0.1",
+				"methods": "~1.1.2",
+				"on-finished": "~2.3.0",
+				"parseurl": "~1.3.3",
+				"path-to-regexp": "0.1.7",
+				"proxy-addr": "~2.0.5",
+				"qs": "6.7.0",
+				"range-parser": "~1.2.1",
+				"safe-buffer": "5.1.2",
+				"send": "0.17.1",
+				"serve-static": "1.14.1",
+				"setprototypeof": "1.1.1",
+				"statuses": "~1.5.0",
+				"type-is": "~1.6.18",
+				"utils-merge": "1.0.1",
+				"vary": "~1.1.2"
+			},
+			"engines": {
+				"node": ">= 0.10.0"
+			}
+		},
+		"node_modules/express-mysql-session": {
+			"version": "2.1.8",
+			"resolved": "https://registry.npmjs.org/express-mysql-session/-/express-mysql-session-2.1.8.tgz",
+			"integrity": "sha512-EnFdhG6RNmC/gZpC+mmcGKZeX8kcp5Lh90ozFTa8LNJmHrGWzqOt8564IvnTw5c4d3/Aj0EAXnpGuzLCoKzcig==",
+			"dependencies": {
+				"debug": "4.3.4",
+				"express-session": "1.17.2",
+				"mysql": "2.18.1",
+				"underscore": "1.13.3"
+			},
+			"funding": {
+				"type": "individual",
+				"url": "https://degreesofzero.com/donate.html?project=express-mysql-session"
+			}
+		},
+		"node_modules/express-mysql-session/node_modules/cookie": {
+			"version": "0.4.1",
+			"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+			"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/express-mysql-session/node_modules/depd": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+			"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/express-mysql-session/node_modules/express-session": {
+			"version": "1.17.2",
+			"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz",
+			"integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==",
+			"dependencies": {
+				"cookie": "0.4.1",
+				"cookie-signature": "1.0.6",
+				"debug": "2.6.9",
+				"depd": "~2.0.0",
+				"on-headers": "~1.0.2",
+				"parseurl": "~1.3.3",
+				"safe-buffer": "5.2.1",
+				"uid-safe": "~2.1.5"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/express-mysql-session/node_modules/express-session/node_modules/debug": {
+			"version": "2.6.9",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+			"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+			"dependencies": {
+				"ms": "2.0.0"
+			}
+		},
+		"node_modules/express-mysql-session/node_modules/ms": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+			"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+		},
+		"node_modules/express-mysql-session/node_modules/safe-buffer": {
+			"version": "5.2.1",
+			"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+			"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
+		"node_modules/express-session": {
+			"version": "1.17.3",
+			"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
+			"integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
+			"dependencies": {
+				"cookie": "0.4.2",
+				"cookie-signature": "1.0.6",
+				"debug": "2.6.9",
+				"depd": "~2.0.0",
+				"on-headers": "~1.0.2",
+				"parseurl": "~1.3.3",
+				"safe-buffer": "5.2.1",
+				"uid-safe": "~2.1.5"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/express-session/node_modules/cookie": {
+			"version": "0.4.2",
+			"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+			"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/express-session/node_modules/debug": {
+			"version": "2.6.9",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+			"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+			"dependencies": {
+				"ms": "2.0.0"
+			}
+		},
+		"node_modules/express-session/node_modules/depd": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+			"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/express-session/node_modules/ms": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+			"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+		},
+		"node_modules/express-session/node_modules/safe-buffer": {
+			"version": "5.2.1",
+			"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+			"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
+		"node_modules/express/node_modules/debug": {
+			"version": "2.6.9",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+			"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+			"dependencies": {
+				"ms": "2.0.0"
+			}
+		},
+		"node_modules/express/node_modules/ms": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+			"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+		},
+		"node_modules/extend": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+			"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+		},
+		"node_modules/fast-csv": {
+			"version": "4.3.6",
+			"resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz",
+			"integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==",
+			"dependencies": {
+				"@fast-csv/format": "4.3.5",
+				"@fast-csv/parse": "4.3.6"
+			},
+			"engines": {
+				"node": ">=10.0.0"
+			}
+		},
+		"node_modules/fast-json-patch": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.0.tgz",
+			"integrity": "sha512-IhpytlsVTRndz0hU5t0/MGzS/etxLlfrpG5V5M9mVbuj9TrJLWaMfsox9REM5rkuGX0T+5qjpe8XA1o0gZ42nA=="
+		},
+		"node_modules/fast-levenshtein": {
+			"version": "2.0.6",
+			"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+			"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
+		},
+		"node_modules/fast-printf": {
+			"version": "1.6.6",
+			"resolved": "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.6.tgz",
+			"integrity": "sha512-Uz/uW6R1Fd8YqCGeoQosRIfB4dBbr8uMbFVdEci2AyXYcfucFqhpSMAGs8skRRdZd+MGCDBu48+B8Zmu7Pta5A==",
+			"dependencies": {
+				"boolean": "^3.0.2"
+			},
+			"engines": {
+				"node": ">=10.0"
+			}
+		},
+		"node_modules/fast-safe-stringify": {
+			"version": "2.0.7",
+			"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
+			"integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
+		},
+		"node_modules/fast-text-encoding": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz",
+			"integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig=="
+		},
+		"node_modules/fclone": {
+			"version": "1.0.11",
+			"resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz",
+			"integrity": "sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA="
+		},
+		"node_modules/fecha": {
+			"version": "4.2.1",
+			"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz",
+			"integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q=="
+		},
+		"node_modules/file-stream-rotator": {
+			"version": "0.5.7",
+			"resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.5.7.tgz",
+			"integrity": "sha512-VYb3HZ/GiAGUCrfeakO8Mp54YGswNUHvL7P09WQcXAJNSj3iQ5QraYSp3cIn1MUyw6uzfgN/EFOarCNa4JvUHQ==",
+			"dependencies": {
+				"moment": "^2.11.2"
+			}
+		},
+		"node_modules/file-type": {
+			"version": "3.9.0",
+			"resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
+			"integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/file-uri-to-path": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz",
+			"integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==",
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/fill-range": {
+			"version": "7.0.1",
+			"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+			"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+			"dependencies": {
+				"to-regex-range": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/finalhandler": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+			"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+			"dependencies": {
+				"debug": "2.6.9",
+				"encodeurl": "~1.0.2",
+				"escape-html": "~1.0.3",
+				"on-finished": "~2.3.0",
+				"parseurl": "~1.3.3",
+				"statuses": "~1.5.0",
+				"unpipe": "~1.0.0"
+			},
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/finalhandler/node_modules/debug": {
+			"version": "2.6.9",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+			"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+			"dependencies": {
+				"ms": "2.0.0"
+			}
+		},
+		"node_modules/finalhandler/node_modules/ms": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+			"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+		},
+		"node_modules/fn.name": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
+			"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
+		},
+		"node_modules/follow-redirects": {
+			"version": "1.14.5",
+			"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
+			"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
+			"funding": [
+				{
+					"type": "individual",
+					"url": "https://github.com/sponsors/RubenVerborgh"
+				}
+			],
+			"engines": {
+				"node": ">=4.0"
+			},
+			"peerDependenciesMeta": {
+				"debug": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/form-data": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+			"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+			"dependencies": {
+				"asynckit": "^0.4.0",
+				"combined-stream": "^1.0.8",
+				"mime-types": "^2.1.12"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/formidable": {
+			"version": "1.2.6",
+			"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz",
+			"integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==",
+			"deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau",
+			"dev": true,
+			"funding": {
+				"url": "https://ko-fi.com/tunnckoCore/commissions"
+			}
+		},
+		"node_modules/forwarded": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+			"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/fresh": {
+			"version": "0.5.2",
+			"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+			"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/fs-constants": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+			"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+		},
+		"node_modules/fs-extra": {
+			"version": "8.1.0",
+			"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+			"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+			"dependencies": {
+				"graceful-fs": "^4.2.0",
+				"jsonfile": "^4.0.0",
+				"universalify": "^0.1.0"
+			},
+			"engines": {
+				"node": ">=6 <7 || >=8"
+			}
+		},
+		"node_modules/fs-minipass": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+			"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+			"dependencies": {
+				"minipass": "^3.0.0"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/fs.realpath": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+			"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+		},
+		"node_modules/fsevents": {
+			"version": "2.3.2",
+			"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+			"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+			"hasInstallScript": true,
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+			}
+		},
+		"node_modules/fstream": {
+			"version": "1.0.12",
+			"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
+			"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
+			"dependencies": {
+				"graceful-fs": "^4.1.2",
+				"inherits": "~2.0.0",
+				"mkdirp": ">=0.5 0",
+				"rimraf": "2"
+			},
+			"engines": {
+				"node": ">=0.6"
+			}
+		},
+		"node_modules/fstream/node_modules/mkdirp": {
+			"version": "0.5.5",
+			"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+			"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+			"dependencies": {
+				"minimist": "^1.2.5"
+			},
+			"bin": {
+				"mkdirp": "bin/cmd.js"
+			}
+		},
+		"node_modules/fstream/node_modules/rimraf": {
+			"version": "2.7.1",
+			"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+			"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+			"dependencies": {
+				"glob": "^7.1.3"
+			},
+			"bin": {
+				"rimraf": "bin.js"
+			}
+		},
+		"node_modules/ftp": {
+			"version": "0.3.10",
+			"resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz",
+			"integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=",
+			"dependencies": {
+				"readable-stream": "1.1.x",
+				"xregexp": "2.0.0"
+			},
+			"engines": {
+				"node": ">=0.8.0"
+			}
+		},
+		"node_modules/ftp/node_modules/isarray": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+			"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+		},
+		"node_modules/ftp/node_modules/readable-stream": {
+			"version": "1.1.14",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+			"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+			"dependencies": {
+				"core-util-is": "~1.0.0",
+				"inherits": "~2.0.1",
+				"isarray": "0.0.1",
+				"string_decoder": "~0.10.x"
+			}
+		},
+		"node_modules/ftp/node_modules/string_decoder": {
+			"version": "0.10.31",
+			"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+			"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+		},
+		"node_modules/function-bind": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+			"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+		},
+		"node_modules/gauge": {
+			"version": "2.7.4",
+			"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+			"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+			"dependencies": {
+				"aproba": "^1.0.3",
+				"console-control-strings": "^1.0.0",
+				"has-unicode": "^2.0.0",
+				"object-assign": "^4.1.0",
+				"signal-exit": "^3.0.0",
+				"string-width": "^1.0.1",
+				"strip-ansi": "^3.0.1",
+				"wide-align": "^1.1.0"
+			}
+		},
+		"node_modules/gaxios": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.0.tgz",
+			"integrity": "sha512-pHplNbslpwCLMyII/lHPWFQbJWOX0B3R1hwBEOvzYi1GmdKZruuEHK4N9V6f7tf1EaPYyF80mui1+344p6SmLg==",
+			"dependencies": {
+				"abort-controller": "^3.0.0",
+				"extend": "^3.0.2",
+				"https-proxy-agent": "^5.0.0",
+				"is-stream": "^2.0.0",
+				"node-fetch": "^2.3.0"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/gcp-metadata": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.0.tgz",
+			"integrity": "sha512-L9XQUpvKJCM76YRSmcxrR4mFPzPGsgZUH+GgHMxAET8qc6+BhRJq63RLhWakgEO2KKVgeSDVfyiNjkGSADwNTA==",
+			"dependencies": {
+				"gaxios": "^4.0.0",
+				"json-bigint": "^1.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/generate-function": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+			"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+			"dependencies": {
+				"is-property": "^1.0.2"
+			}
+		},
+		"node_modules/generic-pool": {
+			"version": "3.9.0",
+			"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
+			"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
+			"engines": {
+				"node": ">= 4"
+			}
+		},
+		"node_modules/get-caller-file": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+			"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+			"engines": {
+				"node": "6.* || 8.* || >= 10.*"
+			}
+		},
+		"node_modules/get-intrinsic": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+			"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+			"dev": true,
+			"dependencies": {
+				"function-bind": "^1.1.1",
+				"has": "^1.0.3",
+				"has-symbols": "^1.0.1"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/get-uri": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz",
+			"integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==",
+			"dependencies": {
+				"@tootallnate/once": "1",
+				"data-uri-to-buffer": "3",
+				"debug": "4",
+				"file-uri-to-path": "2",
+				"fs-extra": "^8.1.0",
+				"ftp": "^0.3.10"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/git-node-fs": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/git-node-fs/-/git-node-fs-1.0.0.tgz",
+			"integrity": "sha1-SbIV4kLr5Dqkx1Ybu6SZUhdSCA8="
+		},
+		"node_modules/git-sha1": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/git-sha1/-/git-sha1-0.1.2.tgz",
+			"integrity": "sha1-WZrBkrcYdYJeE6RF86bgURjC90U="
+		},
+		"node_modules/glob": {
+			"version": "7.1.7",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+			"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+			"dependencies": {
+				"fs.realpath": "^1.0.0",
+				"inflight": "^1.0.4",
+				"inherits": "2",
+				"minimatch": "^3.0.4",
+				"once": "^1.3.0",
+				"path-is-absolute": "^1.0.0"
+			},
+			"engines": {
+				"node": "*"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/glob-parent": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+			"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+			"dependencies": {
+				"is-glob": "^4.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/google-auth-library": {
+			"version": "7.3.0",
+			"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.3.0.tgz",
+			"integrity": "sha512-MPeeMlnsYnoiiVFMwX3hgaS684aiXrSqKoDP+xL4Ejg4Z0qLvIeg4XsaChemyFI8ZUO7ApwDAzNtgmhWSDNh5w==",
+			"dependencies": {
+				"arrify": "^2.0.0",
+				"base64-js": "^1.3.0",
+				"ecdsa-sig-formatter": "^1.0.11",
+				"fast-text-encoding": "^1.0.0",
+				"gaxios": "^4.0.0",
+				"gcp-metadata": "^4.2.0",
+				"gtoken": "^5.0.4",
+				"jws": "^4.0.0",
+				"lru-cache": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/google-p12-pem": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.0.tgz",
+			"integrity": "sha512-JUtEHXL4DY/N+xhlm7TC3qL797RPAtk0ZGXNs3/gWyiDHYoA/8Rjes0pztkda+sZv4ej1EoO2KhWgW5V9KTrSQ==",
+			"dependencies": {
+				"node-forge": "^0.10.0"
+			},
+			"bin": {
+				"gp12-pem": "build/src/bin/gp12-pem.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/googleapis": {
+			"version": "89.0.0",
+			"resolved": "https://registry.npmjs.org/googleapis/-/googleapis-89.0.0.tgz",
+			"integrity": "sha512-eH91BN+6R/Mp5uulrhKWGwKAbibfDNOIu1Oq8n12aFTiqb23/A9kVdRhituYVqhRqSWLr/kv1dpFbd6n+uN+7Q==",
+			"dependencies": {
+				"google-auth-library": "^7.0.2",
+				"googleapis-common": "^5.0.2"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/googleapis-common": {
+			"version": "5.0.5",
+			"resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-5.0.5.tgz",
+			"integrity": "sha512-o2dgoW4x4fLIAN+IVAOccz3mEH8Lj1LP9c9BSSvkNJEn+U7UZh0WSr4fdH08x5VH7+sstIpd1lOYFZD0g7j4pw==",
+			"dependencies": {
+				"extend": "^3.0.2",
+				"gaxios": "^4.0.0",
+				"google-auth-library": "^7.0.2",
+				"qs": "^6.7.0",
+				"url-template": "^2.0.8",
+				"uuid": "^8.0.0"
+			},
+			"engines": {
+				"node": ">=10.10.0"
+			}
+		},
+		"node_modules/googleapis-common/node_modules/uuid": {
+			"version": "8.3.2",
+			"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+			"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+			"bin": {
+				"uuid": "dist/bin/uuid"
+			}
+		},
+		"node_modules/graceful-fs": {
+			"version": "4.2.6",
+			"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+			"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ=="
+		},
+		"node_modules/gtoken": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.0.tgz",
+			"integrity": "sha512-mCcISYiaRZrJpfqOs0QWa6lfEM/C1V9ASkzFmuz43XBb5s1Vynh+CZy1ECeeJXVGx2PRByjYzb4Y4/zr1byr0w==",
+			"dependencies": {
+				"gaxios": "^4.0.0",
+				"google-p12-pem": "^3.0.3",
+				"jws": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/has": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+			"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+			"dependencies": {
+				"function-bind": "^1.1.1"
+			},
+			"engines": {
+				"node": ">= 0.4.0"
+			}
+		},
+		"node_modules/has-flag": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+			"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/has-symbols": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+			"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/has-unicode": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+			"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+		},
+		"node_modules/hosted-git-info": {
+			"version": "2.8.9",
+			"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+			"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
+		},
+		"node_modules/html-comment-regex": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
+			"integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ=="
+		},
+		"node_modules/htmlparser2": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+			"integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+			"funding": [
+				"https://github.com/fb55/htmlparser2?sponsor=1",
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/fb55"
+				}
+			],
+			"dependencies": {
+				"domelementtype": "^2.0.1",
+				"domhandler": "^4.0.0",
+				"domutils": "^2.5.2",
+				"entities": "^2.0.0"
+			}
+		},
+		"node_modules/http_ece": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.1.0.tgz",
+			"integrity": "sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==",
+			"dependencies": {
+				"urlsafe-base64": "~1.0.0"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/http-errors": {
+			"version": "1.7.2",
+			"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+			"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+			"dependencies": {
+				"depd": "~1.1.2",
+				"inherits": "2.0.3",
+				"setprototypeof": "1.1.1",
+				"statuses": ">= 1.5.0 < 2",
+				"toidentifier": "1.0.0"
+			},
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/http-errors/node_modules/inherits": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+			"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+		},
+		"node_modules/http-proxy-agent": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+			"integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+			"dependencies": {
+				"@tootallnate/once": "1",
+				"agent-base": "6",
+				"debug": "4"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/https-proxy-agent": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+			"integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+			"dependencies": {
+				"agent-base": "6",
+				"debug": "4"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/iconv-lite": {
+			"version": "0.6.3",
+			"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+			"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+			"dependencies": {
+				"safer-buffer": ">= 2.1.2 < 3.0.0"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/ieee754": {
+			"version": "1.1.13",
+			"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+			"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+		},
+		"node_modules/ignore-by-default": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+			"integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
+			"dev": true
+		},
+		"node_modules/immediate": {
+			"version": "3.0.6",
+			"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+			"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
+		},
+		"node_modules/inflight": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+			"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+			"dependencies": {
+				"once": "^1.3.0",
+				"wrappy": "1"
+			}
+		},
+		"node_modules/inherits": {
+			"version": "2.0.4",
+			"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+			"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+		},
+		"node_modules/ini": {
+			"version": "1.3.7",
+			"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz",
+			"integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ=="
+		},
+		"node_modules/ip": {
+			"version": "1.1.5",
+			"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+			"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
+		},
+		"node_modules/ipaddr.js": {
+			"version": "1.9.1",
+			"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+			"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+			"engines": {
+				"node": ">= 0.10"
+			}
+		},
+		"node_modules/is-arrayish": {
+			"version": "0.2.1",
+			"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+			"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+		},
+		"node_modules/is-binary-path": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+			"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+			"dependencies": {
+				"binary-extensions": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/is-core-module": {
+			"version": "2.4.0",
+			"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
+			"integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==",
+			"dependencies": {
+				"has": "^1.0.3"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/is-extglob": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+			"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/is-fullwidth-code-point": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+			"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+			"dependencies": {
+				"number-is-nan": "^1.0.0"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/is-glob": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+			"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+			"dependencies": {
+				"is-extglob": "^2.1.1"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/is-number": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+			"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+			"engines": {
+				"node": ">=0.12.0"
+			}
+		},
+		"node_modules/is-property": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+			"integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
+		},
+		"node_modules/is-stream": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
+			"integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/isarray": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+			"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+		},
+		"node_modules/jmespath": {
+			"version": "0.16.0",
+			"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
+			"integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==",
+			"engines": {
+				"node": ">= 0.6.0"
+			}
+		},
+		"node_modules/js-git": {
+			"version": "0.7.8",
+			"resolved": "https://registry.npmjs.org/js-git/-/js-git-0.7.8.tgz",
+			"integrity": "sha1-UvplWrYYd9bxB578ZTS1VPMeVEQ=",
+			"dependencies": {
+				"bodec": "^0.1.0",
+				"culvert": "^0.1.2",
+				"git-sha1": "^0.1.2",
+				"pako": "^0.2.5"
+			}
+		},
+		"node_modules/js-tokens": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+			"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+		},
+		"node_modules/js-yaml": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+			"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+			"dev": true,
+			"dependencies": {
+				"argparse": "^2.0.1"
+			},
+			"bin": {
+				"js-yaml": "bin/js-yaml.js"
+			}
+		},
+		"node_modules/js-yaml/node_modules/argparse": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+			"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+			"dev": true
+		},
+		"node_modules/json-bigint": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+			"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+			"dependencies": {
+				"bignumber.js": "^9.0.0"
+			}
+		},
+		"node_modules/json-parse-even-better-errors": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+			"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+		},
+		"node_modules/json-stringify-safe": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+			"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+			"optional": true
+		},
+		"node_modules/jsonfile": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+			"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+			"optionalDependencies": {
+				"graceful-fs": "^4.1.6"
+			}
+		},
+		"node_modules/jsonwebtoken": {
+			"version": "8.5.1",
+			"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+			"integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+			"dependencies": {
+				"jws": "^3.2.2",
+				"lodash.includes": "^4.3.0",
+				"lodash.isboolean": "^3.0.3",
+				"lodash.isinteger": "^4.0.4",
+				"lodash.isnumber": "^3.0.3",
+				"lodash.isplainobject": "^4.0.6",
+				"lodash.isstring": "^4.0.1",
+				"lodash.once": "^4.0.0",
+				"ms": "^2.1.1",
+				"semver": "^5.6.0"
+			},
+			"engines": {
+				"node": ">=4",
+				"npm": ">=1.4.28"
+			}
+		},
+		"node_modules/jsonwebtoken/node_modules/jwa": {
+			"version": "1.4.1",
+			"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+			"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+			"dependencies": {
+				"buffer-equal-constant-time": "1.0.1",
+				"ecdsa-sig-formatter": "1.0.11",
+				"safe-buffer": "^5.0.1"
+			}
+		},
+		"node_modules/jsonwebtoken/node_modules/jws": {
+			"version": "3.2.2",
+			"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+			"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+			"dependencies": {
+				"jwa": "^1.4.1",
+				"safe-buffer": "^5.0.1"
+			}
+		},
+		"node_modules/jsonwebtoken/node_modules/semver": {
+			"version": "5.7.1",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+			"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+			"bin": {
+				"semver": "bin/semver"
+			}
+		},
+		"node_modules/jszip": {
+			"version": "3.7.1",
+			"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
+			"integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
+			"dependencies": {
+				"lie": "~3.3.0",
+				"pako": "~1.0.2",
+				"readable-stream": "~2.3.6",
+				"set-immediate-shim": "~1.0.1"
+			}
+		},
+		"node_modules/jszip/node_modules/pako": {
+			"version": "1.0.11",
+			"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+			"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+		},
+		"node_modules/jwa": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+			"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+			"dependencies": {
+				"buffer-equal-constant-time": "1.0.1",
+				"ecdsa-sig-formatter": "1.0.11",
+				"safe-buffer": "^5.0.1"
+			}
+		},
+		"node_modules/jws": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+			"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+			"dependencies": {
+				"jwa": "^2.0.0",
+				"safe-buffer": "^5.0.1"
+			}
+		},
+		"node_modules/kuler": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+			"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
+		},
+		"node_modules/lazy": {
+			"version": "1.0.11",
+			"resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz",
+			"integrity": "sha1-2qBoIGKCVCwIgojpdcKXwa53tpA=",
+			"engines": {
+				"node": ">=0.2.0"
+			}
+		},
+		"node_modules/lazystream": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
+			"integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
+			"dependencies": {
+				"readable-stream": "^2.0.5"
+			},
+			"engines": {
+				"node": ">= 0.6.3"
+			}
+		},
+		"node_modules/levn": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+			"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+			"dependencies": {
+				"prelude-ls": "~1.1.2",
+				"type-check": "~0.3.2"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/lie": {
+			"version": "3.3.0",
+			"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+			"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+			"dependencies": {
+				"immediate": "~3.0.5"
+			}
+		},
+		"node_modules/lines-and-columns": {
+			"version": "1.1.6",
+			"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
+			"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
+		},
+		"node_modules/listenercount": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz",
+			"integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc="
+		},
+		"node_modules/lodash": {
+			"version": "4.17.21",
+			"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+			"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+		},
+		"node_modules/lodash.defaults": {
+			"version": "4.2.0",
+			"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+			"integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
+		},
+		"node_modules/lodash.difference": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
+			"integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
+		},
+		"node_modules/lodash.escaperegexp": {
+			"version": "4.1.2",
+			"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
+			"integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c="
+		},
+		"node_modules/lodash.flatten": {
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+			"integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
+		},
+		"node_modules/lodash.get": {
+			"version": "4.4.2",
+			"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+			"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
+			"dev": true
+		},
+		"node_modules/lodash.groupby": {
+			"version": "4.6.0",
+			"resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz",
+			"integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E="
+		},
+		"node_modules/lodash.includes": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+			"integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+		},
+		"node_modules/lodash.isboolean": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+			"integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+		},
+		"node_modules/lodash.isequal": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+			"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+		},
+		"node_modules/lodash.isfunction": {
+			"version": "3.0.9",
+			"resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
+			"integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw=="
+		},
+		"node_modules/lodash.isinteger": {
+			"version": "4.0.4",
+			"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+			"integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+		},
+		"node_modules/lodash.isnil": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz",
+			"integrity": "sha1-SeKM1VkBNFjIFMVHnTxmOiG/qmw="
+		},
+		"node_modules/lodash.isnumber": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+			"integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+		},
+		"node_modules/lodash.isplainobject": {
+			"version": "4.0.6",
+			"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+			"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+		},
+		"node_modules/lodash.isstring": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+			"integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+		},
+		"node_modules/lodash.isundefined": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz",
+			"integrity": "sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g="
+		},
+		"node_modules/lodash.mergewith": {
+			"version": "4.6.2",
+			"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+			"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+			"dev": true
+		},
+		"node_modules/lodash.once": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+			"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+		},
+		"node_modules/lodash.union": {
+			"version": "4.6.0",
+			"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
+			"integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
+		},
+		"node_modules/lodash.uniq": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+			"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
+		},
+		"node_modules/log-driver": {
+			"version": "1.2.7",
+			"resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz",
+			"integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==",
+			"engines": {
+				"node": ">=0.8.6"
+			}
+		},
+		"node_modules/logform": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz",
+			"integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==",
+			"dependencies": {
+				"colors": "^1.2.1",
+				"fast-safe-stringify": "^2.0.4",
+				"fecha": "^4.2.0",
+				"ms": "^2.1.1",
+				"triple-beam": "^1.3.0"
+			}
+		},
+		"node_modules/long": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+			"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+		},
+		"node_modules/lru_map": {
+			"version": "0.3.3",
+			"resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
+			"integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ=="
+		},
+		"node_modules/lru-cache": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+			"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+			"dependencies": {
+				"yallist": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/make-dir": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+			"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+			"dependencies": {
+				"semver": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/make-dir/node_modules/semver": {
+			"version": "6.3.0",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+			"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+			"bin": {
+				"semver": "bin/semver.js"
+			}
+		},
+		"node_modules/media-typer": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+			"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/merge-descriptors": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+			"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+		},
+		"node_modules/method-override": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz",
+			"integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==",
+			"dependencies": {
+				"debug": "3.1.0",
+				"methods": "~1.1.2",
+				"parseurl": "~1.3.2",
+				"vary": "~1.1.2"
+			},
+			"engines": {
+				"node": ">= 0.10"
+			}
+		},
+		"node_modules/method-override/node_modules/debug": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+			"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+			"dependencies": {
+				"ms": "2.0.0"
+			}
+		},
+		"node_modules/method-override/node_modules/ms": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+			"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+		},
+		"node_modules/methods": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+			"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/mime": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+			"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+			"bin": {
+				"mime": "cli.js"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/mime-db": {
+			"version": "1.48.0",
+			"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz",
+			"integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/mime-types": {
+			"version": "2.1.31",
+			"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz",
+			"integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==",
+			"dependencies": {
+				"mime-db": "1.48.0"
+			},
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/minimalistic-assert": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+			"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+		},
+		"node_modules/minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"dependencies": {
+				"brace-expansion": "^1.1.7"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/minimist": {
+			"version": "1.2.5",
+			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+			"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+		},
+		"node_modules/minipass": {
+			"version": "3.1.3",
+			"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
+			"integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
+			"dependencies": {
+				"yallist": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/minizlib": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+			"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+			"dependencies": {
+				"minipass": "^3.0.0",
+				"yallist": "^4.0.0"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/mkdirp": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+			"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+			"bin": {
+				"mkdirp": "bin/cmd.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/module-details-from-path": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
+			"integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is="
+		},
+		"node_modules/moment": {
+			"version": "2.29.1",
+			"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
+			"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/moment-timezone": {
+			"version": "0.5.33",
+			"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz",
+			"integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==",
+			"dependencies": {
+				"moment": ">= 2.9.0"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/ms": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+			"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+		},
+		"node_modules/multer": {
+			"version": "1.4.3",
+			"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.3.tgz",
+			"integrity": "sha512-np0YLKncuZoTzufbkM6wEKp68EhWJXcU6fq6QqrSwkckd2LlMgd1UqhUJLj6NS/5sZ8dE8LYDWslsltJznnXlg==",
+			"deprecated": "Multer 1.x is affected by CVE-2022-24434. This is fixed in v1.4.4-lts.1 which drops support for versions of Node.js before 6. Please upgrade to at least Node.js 6 and version 1.4.4-lts.1 of Multer. If you need support for older versions of Node.js, we are open to accepting patches that would fix the CVE on the main 1.x release line, whilst maintaining compatibility with Node.js 0.10.",
+			"dependencies": {
+				"append-field": "^1.0.0",
+				"busboy": "^0.2.11",
+				"concat-stream": "^1.5.2",
+				"mkdirp": "^0.5.4",
+				"object-assign": "^4.1.1",
+				"on-finished": "^2.3.0",
+				"type-is": "^1.6.4",
+				"xtend": "^4.0.0"
+			},
+			"engines": {
+				"node": ">= 0.10.0"
+			}
+		},
+		"node_modules/multer-s3": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/multer-s3/-/multer-s3-2.10.0.tgz",
+			"integrity": "sha512-RZsiqG19C9gE82lB7v8duJ+TMIf70fWYHlIwuNcsanOH1ePBoPXZvboEQxEow9jUkk7WQsuyVA2TgriOuDrVrw==",
+			"dependencies": {
+				"file-type": "^3.3.0",
+				"html-comment-regex": "^1.1.2",
+				"run-parallel": "^1.1.6"
+			}
+		},
+		"node_modules/multer/node_modules/mkdirp": {
+			"version": "0.5.5",
+			"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+			"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+			"dependencies": {
+				"minimist": "^1.2.5"
+			},
+			"bin": {
+				"mkdirp": "bin/cmd.js"
+			}
+		},
+		"node_modules/mute-stream": {
+			"version": "0.0.8",
+			"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+			"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
+		},
+		"node_modules/mysql": {
+			"version": "2.18.1",
+			"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
+			"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
+			"dependencies": {
+				"bignumber.js": "9.0.0",
+				"readable-stream": "2.3.7",
+				"safe-buffer": "5.1.2",
+				"sqlstring": "2.3.1"
+			},
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/mysql/node_modules/bignumber.js": {
+			"version": "9.0.0",
+			"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
+			"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==",
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/mysql/node_modules/sqlstring": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
+			"integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/mysql2": {
+			"version": "2.2.5",
+			"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.2.5.tgz",
+			"integrity": "sha512-XRqPNxcZTpmFdXbJqb+/CtYVLCx14x1RTeNMD4954L331APu75IC74GDqnZMEt1kwaXy6TySo55rF2F3YJS78g==",
+			"dependencies": {
+				"denque": "^1.4.1",
+				"generate-function": "^2.3.1",
+				"iconv-lite": "^0.6.2",
+				"long": "^4.0.0",
+				"lru-cache": "^6.0.0",
+				"named-placeholders": "^1.1.2",
+				"seq-queue": "^0.0.5",
+				"sqlstring": "^2.3.2"
+			},
+			"engines": {
+				"node": ">= 8.0"
+			}
+		},
+		"node_modules/mysql2/node_modules/iconv-lite": {
+			"version": "0.6.3",
+			"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+			"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+			"dependencies": {
+				"safer-buffer": ">= 2.1.2 < 3.0.0"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/named-placeholders": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz",
+			"integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==",
+			"dependencies": {
+				"lru-cache": "^4.1.3"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/named-placeholders/node_modules/lru-cache": {
+			"version": "4.1.5",
+			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+			"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+			"dependencies": {
+				"pseudomap": "^1.0.2",
+				"yallist": "^2.1.2"
+			}
+		},
+		"node_modules/named-placeholders/node_modules/yallist": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+			"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+		},
+		"node_modules/needle": {
+			"version": "2.4.0",
+			"resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz",
+			"integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==",
+			"dependencies": {
+				"debug": "^3.2.6",
+				"iconv-lite": "^0.4.4",
+				"sax": "^1.2.4"
+			},
+			"bin": {
+				"needle": "bin/needle"
+			},
+			"engines": {
+				"node": ">= 4.4.x"
+			}
+		},
+		"node_modules/needle/node_modules/debug": {
+			"version": "3.2.7",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+			"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+			"dependencies": {
+				"ms": "^2.1.1"
+			}
+		},
+		"node_modules/needle/node_modules/iconv-lite": {
+			"version": "0.4.24",
+			"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+			"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+			"dependencies": {
+				"safer-buffer": ">= 2.1.2 < 3"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/negotiator": {
+			"version": "0.6.2",
+			"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+			"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/netmask": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
+			"integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
+			"engines": {
+				"node": ">= 0.4.0"
+			}
+		},
+		"node_modules/node-addon-api": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
+			"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
+		},
+		"node_modules/node-cache": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz",
+			"integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==",
+			"dependencies": {
+				"clone": "2.x"
+			},
+			"engines": {
+				"node": ">= 8.0.0"
+			}
+		},
+		"node_modules/node-cron": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.0.tgz",
+			"integrity": "sha512-DDwIvvuCwrNiaU7HEivFDULcaQualDv7KoNlB/UU1wPW0n1tDEmBJKhEIE6DlF2FuoOHcNbLJ8ITL2Iv/3AWmA==",
+			"dependencies": {
+				"moment-timezone": "^0.5.31"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/node-fetch": {
+			"version": "2.6.1",
+			"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+			"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
+			"engines": {
+				"node": "4.x || >=6.0.0"
+			}
+		},
+		"node_modules/node-forge": {
+			"version": "0.10.0",
+			"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+			"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
+			"engines": {
+				"node": ">= 6.0.0"
+			}
+		},
+		"node_modules/node-sens": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/node-sens/-/node-sens-1.3.0.tgz",
+			"integrity": "sha512-d89Z5mDBLaMfmQYcrIsHcPbKf568cX+ajpOjDywQVyVDhgRxzz5dyGAuEI/1HCB5Fi71eIALsylkxSJ17NTlBA==",
+			"dependencies": {
+				"axios": "^0.19.2",
+				"crypto": "^1.0.1"
+			}
+		},
+		"node_modules/node-sens/node_modules/axios": {
+			"version": "0.19.2",
+			"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
+			"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
+			"deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410",
+			"dependencies": {
+				"follow-redirects": "1.5.10"
+			}
+		},
+		"node_modules/node-sens/node_modules/debug": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+			"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+			"dependencies": {
+				"ms": "2.0.0"
+			}
+		},
+		"node_modules/node-sens/node_modules/follow-redirects": {
+			"version": "1.5.10",
+			"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+			"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+			"dependencies": {
+				"debug": "=3.1.0"
+			},
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/node-sens/node_modules/ms": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+			"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+		},
+		"node_modules/nodemailer": {
+			"version": "6.6.2",
+			"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.2.tgz",
+			"integrity": "sha512-YSzu7TLbI+bsjCis/TZlAXBoM4y93HhlIgo0P5oiA2ua9Z4k+E2Fod//ybIzdJxOlXGRcHIh/WaeCBehvxZb/Q==",
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/nodemon": {
+			"version": "2.0.20",
+			"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz",
+			"integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==",
+			"dev": true,
+			"dependencies": {
+				"chokidar": "^3.5.2",
+				"debug": "^3.2.7",
+				"ignore-by-default": "^1.0.1",
+				"minimatch": "^3.1.2",
+				"pstree.remy": "^1.1.8",
+				"semver": "^5.7.1",
+				"simple-update-notifier": "^1.0.7",
+				"supports-color": "^5.5.0",
+				"touch": "^3.1.0",
+				"undefsafe": "^2.0.5"
+			},
+			"bin": {
+				"nodemon": "bin/nodemon.js"
+			},
+			"engines": {
+				"node": ">=8.10.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/nodemon"
+			}
+		},
+		"node_modules/nodemon/node_modules/debug": {
+			"version": "3.2.7",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+			"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+			"dev": true,
+			"dependencies": {
+				"ms": "^2.1.1"
+			}
+		},
+		"node_modules/nodemon/node_modules/has-flag": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+			"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+			"dev": true,
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/nodemon/node_modules/semver": {
+			"version": "5.7.1",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+			"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+			"dev": true,
+			"bin": {
+				"semver": "bin/semver"
+			}
+		},
+		"node_modules/nodemon/node_modules/supports-color": {
+			"version": "5.5.0",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+			"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+			"dev": true,
+			"dependencies": {
+				"has-flag": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/nopt": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+			"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+			"dependencies": {
+				"abbrev": "1"
+			},
+			"bin": {
+				"nopt": "bin/nopt.js"
+			},
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/normalize-package-data": {
+			"version": "2.5.0",
+			"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+			"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+			"dependencies": {
+				"hosted-git-info": "^2.1.4",
+				"resolve": "^1.10.0",
+				"semver": "2 || 3 || 4 || 5",
+				"validate-npm-package-license": "^3.0.1"
+			}
+		},
+		"node_modules/normalize-package-data/node_modules/semver": {
+			"version": "5.7.1",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+			"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+			"bin": {
+				"semver": "bin/semver"
+			}
+		},
+		"node_modules/normalize-path": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+			"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/npmlog": {
+			"version": "4.1.2",
+			"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+			"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+			"dependencies": {
+				"are-we-there-yet": "~1.1.2",
+				"console-control-strings": "~1.1.0",
+				"gauge": "~2.7.3",
+				"set-blocking": "~2.0.0"
+			}
+		},
+		"node_modules/nssocket": {
+			"version": "0.6.0",
+			"resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.6.0.tgz",
+			"integrity": "sha1-Wflvb/MhVm8zxw99vu7N/cBxVPo=",
+			"dependencies": {
+				"eventemitter2": "~0.4.14",
+				"lazy": "~1.0.11"
+			},
+			"engines": {
+				"node": ">= 0.10.x"
+			}
+		},
+		"node_modules/nssocket/node_modules/eventemitter2": {
+			"version": "0.4.14",
+			"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
+			"integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas="
+		},
+		"node_modules/nth-check": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+			"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+			"dependencies": {
+				"boolbase": "^1.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/nth-check?sponsor=1"
+			}
+		},
+		"node_modules/number-is-nan": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+			"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/oauth": {
+			"version": "0.9.15",
+			"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
+			"integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE="
+		},
+		"node_modules/object-assign": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+			"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/object-hash": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
+			"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/object-inspect": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz",
+			"integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==",
+			"dev": true,
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/on-finished": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+			"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+			"dependencies": {
+				"ee-first": "1.1.1"
+			},
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/on-headers": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+			"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/once": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+			"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+			"dependencies": {
+				"wrappy": "1"
+			}
+		},
+		"node_modules/one-time": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
+			"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+			"dependencies": {
+				"fn.name": "1.x.x"
+			}
+		},
+		"node_modules/openapi-types": {
+			"version": "12.0.0",
+			"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.0.0.tgz",
+			"integrity": "sha512-6Wd9k8nmGQHgCbehZCP6wwWcfXcvinhybUTBatuhjRsCxUIujuYFZc9QnGeae75CyHASewBtxs0HX/qwREReUw==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/optionator": {
+			"version": "0.8.3",
+			"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+			"integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+			"dependencies": {
+				"deep-is": "~0.1.3",
+				"fast-levenshtein": "~2.0.6",
+				"levn": "~0.3.0",
+				"prelude-ls": "~1.1.2",
+				"type-check": "~0.3.2",
+				"word-wrap": "~1.2.3"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/pac-proxy-agent": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz",
+			"integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==",
+			"dependencies": {
+				"@tootallnate/once": "1",
+				"agent-base": "6",
+				"debug": "4",
+				"get-uri": "3",
+				"http-proxy-agent": "^4.0.1",
+				"https-proxy-agent": "5",
+				"pac-resolver": "^5.0.0",
+				"raw-body": "^2.2.0",
+				"socks-proxy-agent": "5"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/pac-resolver": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.0.tgz",
+			"integrity": "sha512-H+/A6KitiHNNW+bxBKREk2MCGSxljfqRX76NjummWEYIat7ldVXRU3dhRIE3iXZ0nvGBk6smv3nntxKkzRL8NA==",
+			"dependencies": {
+				"degenerator": "^3.0.1",
+				"ip": "^1.1.5",
+				"netmask": "^2.0.1"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/pako": {
+			"version": "0.2.9",
+			"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+			"integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU="
+		},
+		"node_modules/parse-json": {
+			"version": "5.2.0",
+			"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+			"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+			"dependencies": {
+				"@babel/code-frame": "^7.0.0",
+				"error-ex": "^1.3.1",
+				"json-parse-even-better-errors": "^2.3.0",
+				"lines-and-columns": "^1.1.6"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/parse5": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+			"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
+		},
+		"node_modules/parse5-htmlparser2-tree-adapter": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+			"integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+			"dependencies": {
+				"parse5": "^6.0.1"
+			}
+		},
+		"node_modules/parseurl": {
+			"version": "1.3.3",
+			"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+			"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/passport": {
+			"version": "0.4.1",
+			"resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz",
+			"integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==",
+			"dependencies": {
+				"passport-strategy": "1.x.x",
+				"pause": "0.0.1"
+			},
+			"engines": {
+				"node": ">= 0.4.0"
+			}
+		},
+		"node_modules/passport-kakao": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz",
+			"integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==",
+			"dependencies": {
+				"passport-oauth2": "~1.1.2",
+				"pkginfo": "~0.3.0"
+			}
+		},
+		"node_modules/passport-oauth2": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz",
+			"integrity": "sha1-vXFjsbYJA3GGjcTvb58uHkzEuUg=",
+			"dependencies": {
+				"oauth": "0.9.x",
+				"passport-strategy": "1.x.x",
+				"uid2": "0.0.x"
+			},
+			"engines": {
+				"node": ">= 0.4.0"
+			}
+		},
+		"node_modules/passport-strategy": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
+			"integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=",
+			"engines": {
+				"node": ">= 0.4.0"
+			}
+		},
+		"node_modules/path-is-absolute": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+			"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/path-parse": {
+			"version": "1.0.7",
+			"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+			"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+		},
+		"node_modules/path-to-regexp": {
+			"version": "0.1.7",
+			"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+			"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+		},
+		"node_modules/pause": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+			"integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
+		},
+		"node_modules/picomatch": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
+			"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+			"engines": {
+				"node": ">=8.6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/jonschlinkert"
+			}
+		},
+		"node_modules/pidusage": {
+			"version": "2.0.21",
+			"resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz",
+			"integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==",
+			"dependencies": {
+				"safe-buffer": "^5.2.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/pidusage/node_modules/safe-buffer": {
+			"version": "5.2.1",
+			"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+			"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
+		"node_modules/pkginfo": {
+			"version": "0.3.1",
+			"resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz",
+			"integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=",
+			"engines": {
+				"node": ">= 0.4.0"
+			}
+		},
+		"node_modules/pm2": {
+			"version": "5.1.0",
+			"resolved": "https://registry.npmjs.org/pm2/-/pm2-5.1.0.tgz",
+			"integrity": "sha512-reJ35NOxM4+g7H0enW47HJsp32CszKkseCojAuUMUkffyXsGDKBMnDqhxAZMZKtHUUjl0cWFEqKehqB0ODH8Kw==",
+			"dependencies": {
+				"@pm2/agent": "~2.0.0",
+				"@pm2/io": "~5.0.0",
+				"@pm2/js-api": "~0.6.7",
+				"@pm2/pm2-version-check": "latest",
+				"async": "~3.2.0",
+				"blessed": "0.1.81",
+				"chalk": "3.0.0",
+				"chokidar": "^3.5.1",
+				"cli-tableau": "^2.0.0",
+				"commander": "2.15.1",
+				"cron": "1.8.2",
+				"dayjs": "~1.8.25",
+				"debug": "^4.3.1",
+				"enquirer": "2.3.6",
+				"eventemitter2": "5.0.1",
+				"fast-printf": "^1.3.0",
+				"fclone": "1.0.11",
+				"mkdirp": "1.0.4",
+				"needle": "2.4.0",
+				"pidusage": "2.0.21",
+				"pm2-axon": "~4.0.1",
+				"pm2-axon-rpc": "~0.7.1",
+				"pm2-deploy": "~1.0.2",
+				"pm2-multimeter": "^0.1.2",
+				"promptly": "^2",
+				"semver": "^7.2",
+				"source-map-support": "0.5.19",
+				"vizion": "~2.2.1",
+				"yamljs": "0.3.0"
+			},
+			"bin": {
+				"pm2": "bin/pm2",
+				"pm2-dev": "bin/pm2-dev",
+				"pm2-docker": "bin/pm2-docker",
+				"pm2-runtime": "bin/pm2-runtime"
+			},
+			"engines": {
+				"node": ">=10.0.0"
+			},
+			"optionalDependencies": {
+				"pm2-sysmonit": "^1.2.8"
+			}
+		},
+		"node_modules/pm2-axon": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/pm2-axon/-/pm2-axon-4.0.1.tgz",
+			"integrity": "sha512-kES/PeSLS8orT8dR5jMlNl+Yu4Ty3nbvZRmaAtROuVm9nYYGiaoXqqKQqQYzWQzMYWUKHMQTvBlirjE5GIIxqg==",
+			"dependencies": {
+				"amp": "~0.3.1",
+				"amp-message": "~0.1.1",
+				"debug": "^4.3.1",
+				"escape-string-regexp": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=5"
+			}
+		},
+		"node_modules/pm2-axon-rpc": {
+			"version": "0.7.1",
+			"resolved": "https://registry.npmjs.org/pm2-axon-rpc/-/pm2-axon-rpc-0.7.1.tgz",
+			"integrity": "sha512-FbLvW60w+vEyvMjP/xom2UPhUN/2bVpdtLfKJeYM3gwzYhoTEEChCOICfFzxkxuoEleOlnpjie+n1nue91bDQw==",
+			"dependencies": {
+				"debug": "^4.3.1"
+			},
+			"engines": {
+				"node": ">=5"
+			}
+		},
+		"node_modules/pm2-axon/node_modules/escape-string-regexp": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+			"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/pm2-deploy": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/pm2-deploy/-/pm2-deploy-1.0.2.tgz",
+			"integrity": "sha512-YJx6RXKrVrWaphEYf++EdOOx9EH18vM8RSZN/P1Y+NokTKqYAca/ejXwVLyiEpNju4HPZEk3Y2uZouwMqUlcgg==",
+			"dependencies": {
+				"run-series": "^1.1.8",
+				"tv4": "^1.3.0"
+			},
+			"engines": {
+				"node": ">=4.0.0"
+			}
+		},
+		"node_modules/pm2-multimeter": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz",
+			"integrity": "sha1-Gh5VFT1BoFU0zqI8/oYKuqDrSs4=",
+			"dependencies": {
+				"charm": "~0.1.1"
+			}
+		},
+		"node_modules/pm2-sysmonit": {
+			"version": "1.2.8",
+			"resolved": "https://registry.npmjs.org/pm2-sysmonit/-/pm2-sysmonit-1.2.8.tgz",
+			"integrity": "sha512-ACOhlONEXdCTVwKieBIQLSi2tQZ8eKinhcr9JpZSUAL8Qy0ajIgRtsLxG/lwPOW3JEKqPyw/UaHmTWhUzpP4kA==",
+			"optional": true,
+			"dependencies": {
+				"async": "^3.2.0",
+				"debug": "^4.3.1",
+				"pidusage": "^2.0.21",
+				"systeminformation": "^5.7",
+				"tx2": "~1.0.4"
+			}
+		},
+		"node_modules/pm2/node_modules/@pm2/pm2-version-check": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@pm2/pm2-version-check/-/pm2-version-check-1.0.4.tgz",
+			"integrity": "sha512-SXsM27SGH3yTWKc2fKR4SYNxsmnvuBQ9dd6QHtEWmiZ/VqaOYPAIlS8+vMcn27YLtAEBGvNRSh3TPNvtjZgfqA==",
+			"dependencies": {
+				"debug": "^4.3.1"
+			}
+		},
+		"node_modules/pm2/node_modules/chalk": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+			"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+			"dependencies": {
+				"ansi-styles": "^4.1.0",
+				"supports-color": "^7.1.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/pm2/node_modules/supports-color": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+			"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+			"dependencies": {
+				"has-flag": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/prelude-ls": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+			"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/printj": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
+			"integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==",
+			"bin": {
+				"printj": "bin/printj.njs"
+			},
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
+		"node_modules/process-nextick-args": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+			"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+		},
+		"node_modules/promptly": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/promptly/-/promptly-2.2.0.tgz",
+			"integrity": "sha1-KhP6BjaIoqWYOxYf/wEIoH0m/HQ=",
+			"dependencies": {
+				"read": "^1.0.4"
+			}
+		},
+		"node_modules/proxy-addr": {
+			"version": "2.0.7",
+			"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+			"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+			"dependencies": {
+				"forwarded": "0.2.0",
+				"ipaddr.js": "1.9.1"
+			},
+			"engines": {
+				"node": ">= 0.10"
+			}
+		},
+		"node_modules/proxy-agent": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz",
+			"integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==",
+			"dependencies": {
+				"agent-base": "^6.0.0",
+				"debug": "4",
+				"http-proxy-agent": "^4.0.0",
+				"https-proxy-agent": "^5.0.0",
+				"lru-cache": "^5.1.1",
+				"pac-proxy-agent": "^5.0.0",
+				"proxy-from-env": "^1.0.0",
+				"socks-proxy-agent": "^5.0.0"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/proxy-agent/node_modules/lru-cache": {
+			"version": "5.1.1",
+			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+			"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+			"dependencies": {
+				"yallist": "^3.0.2"
+			}
+		},
+		"node_modules/proxy-agent/node_modules/yallist": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+			"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+		},
+		"node_modules/proxy-from-env": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+			"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+		},
+		"node_modules/pseudomap": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+			"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+		},
+		"node_modules/pstree.remy": {
+			"version": "1.1.8",
+			"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+			"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+			"dev": true
+		},
+		"node_modules/punycode": {
+			"version": "1.3.2",
+			"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+			"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
+		},
+		"node_modules/qs": {
+			"version": "6.7.0",
+			"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+			"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
+			"engines": {
+				"node": ">=0.6"
+			}
+		},
+		"node_modules/querystring": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+			"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+			"deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
+			"engines": {
+				"node": ">=0.4.x"
+			}
+		},
+		"node_modules/queue-microtask": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+			"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
+		"node_modules/random-bytes": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
+			"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=",
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/range-parser": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+			"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/raw-body": {
+			"version": "2.4.0",
+			"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+			"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+			"dependencies": {
+				"bytes": "3.1.0",
+				"http-errors": "1.7.2",
+				"iconv-lite": "0.4.24",
+				"unpipe": "1.0.0"
+			},
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/raw-body/node_modules/iconv-lite": {
+			"version": "0.4.24",
+			"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+			"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+			"dependencies": {
+				"safer-buffer": ">= 2.1.2 < 3"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/read": {
+			"version": "1.0.7",
+			"resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
+			"integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
+			"dependencies": {
+				"mute-stream": "~0.0.4"
+			},
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
+		"node_modules/read-pkg": {
+			"version": "5.2.0",
+			"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
+			"integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
+			"dependencies": {
+				"@types/normalize-package-data": "^2.4.0",
+				"normalize-package-data": "^2.5.0",
+				"parse-json": "^5.0.0",
+				"type-fest": "^0.6.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/readable-stream": {
+			"version": "2.3.7",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+			"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+			"dependencies": {
+				"core-util-is": "~1.0.0",
+				"inherits": "~2.0.3",
+				"isarray": "~1.0.0",
+				"process-nextick-args": "~2.0.0",
+				"safe-buffer": "~5.1.1",
+				"string_decoder": "~1.1.1",
+				"util-deprecate": "~1.0.1"
+			}
+		},
+		"node_modules/readdir-glob": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.1.tgz",
+			"integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==",
+			"dependencies": {
+				"minimatch": "^3.0.4"
+			}
+		},
+		"node_modules/readdirp": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+			"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+			"dependencies": {
+				"picomatch": "^2.2.1"
+			},
+			"engines": {
+				"node": ">=8.10.0"
+			}
+		},
+		"node_modules/readline": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz",
+			"integrity": "sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw="
+		},
+		"node_modules/redis": {
+			"version": "4.5.1",
+			"resolved": "https://registry.npmjs.org/redis/-/redis-4.5.1.tgz",
+			"integrity": "sha512-oxXSoIqMJCQVBTfxP6BNTCtDMyh9G6Vi5wjdPdV/sRKkufyZslDqCScSGcOr6XGR/reAWZefz7E4leM31RgdBA==",
+			"dependencies": {
+				"@redis/bloom": "1.1.0",
+				"@redis/client": "1.4.2",
+				"@redis/graph": "1.1.0",
+				"@redis/json": "1.0.4",
+				"@redis/search": "1.1.0",
+				"@redis/time-series": "1.0.4"
+			}
+		},
+		"node_modules/regex-email": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/regex-email/-/regex-email-1.0.2.tgz",
+			"integrity": "sha1-1nKFy9FvML5e+vaEF1wVPwy8gSE="
+		},
+		"node_modules/require-directory": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+			"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/require-in-the-middle": {
+			"version": "5.1.0",
+			"resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.1.0.tgz",
+			"integrity": "sha512-M2rLKVupQfJ5lf9OvqFGIT+9iVLnTmjgbOmpil12hiSQNn5zJTKGPoIisETNjfK+09vP3rpm1zJajmErpr2sEQ==",
+			"dependencies": {
+				"debug": "^4.1.1",
+				"module-details-from-path": "^1.0.3",
+				"resolve": "^1.12.0"
+			}
+		},
+		"node_modules/resolve": {
+			"version": "1.20.0",
+			"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+			"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+			"dependencies": {
+				"is-core-module": "^2.2.0",
+				"path-parse": "^1.0.6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/rimraf": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+			"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+			"dependencies": {
+				"glob": "^7.1.3"
+			},
+			"bin": {
+				"rimraf": "bin.js"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/run-parallel": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+			"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			],
+			"dependencies": {
+				"queue-microtask": "^1.2.2"
+			}
+		},
+		"node_modules/run-series": {
+			"version": "1.1.9",
+			"resolved": "https://registry.npmjs.org/run-series/-/run-series-1.1.9.tgz",
+			"integrity": "sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
+		"node_modules/rxjs": {
+			"version": "6.6.7",
+			"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+			"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+			"dependencies": {
+				"tslib": "^1.9.0"
+			},
+			"engines": {
+				"npm": ">=2.0.0"
+			}
+		},
+		"node_modules/safe-buffer": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+			"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+		},
+		"node_modules/safer-buffer": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+			"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+		},
+		"node_modules/sax": {
+			"version": "1.2.4",
+			"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+			"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+		},
+		"node_modules/saxes": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
+			"integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
+			"dependencies": {
+				"xmlchars": "^2.2.0"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/semver": {
+			"version": "7.3.5",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+			"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+			"dependencies": {
+				"lru-cache": "^6.0.0"
+			},
+			"bin": {
+				"semver": "bin/semver.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/send": {
+			"version": "0.17.1",
+			"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+			"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+			"dependencies": {
+				"debug": "2.6.9",
+				"depd": "~1.1.2",
+				"destroy": "~1.0.4",
+				"encodeurl": "~1.0.2",
+				"escape-html": "~1.0.3",
+				"etag": "~1.8.1",
+				"fresh": "0.5.2",
+				"http-errors": "~1.7.2",
+				"mime": "1.6.0",
+				"ms": "2.1.1",
+				"on-finished": "~2.3.0",
+				"range-parser": "~1.2.1",
+				"statuses": "~1.5.0"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/send/node_modules/debug": {
+			"version": "2.6.9",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+			"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+			"dependencies": {
+				"ms": "2.0.0"
+			}
+		},
+		"node_modules/send/node_modules/debug/node_modules/ms": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+			"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+		},
+		"node_modules/send/node_modules/ms": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+			"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+		},
+		"node_modules/seq-queue": {
+			"version": "0.0.5",
+			"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+			"integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4="
+		},
+		"node_modules/serve-static": {
+			"version": "1.14.1",
+			"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+			"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+			"dependencies": {
+				"encodeurl": "~1.0.2",
+				"escape-html": "~1.0.3",
+				"parseurl": "~1.3.3",
+				"send": "0.17.1"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/set-blocking": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+			"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+		},
+		"node_modules/set-immediate-shim": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+			"integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/setimmediate": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+			"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
+		},
+		"node_modules/setprototypeof": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+			"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+		},
+		"node_modules/shimmer": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
+			"integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="
+		},
+		"node_modules/should": {
+			"version": "13.2.3",
+			"resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz",
+			"integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==",
+			"dev": true,
+			"dependencies": {
+				"should-equal": "^2.0.0",
+				"should-format": "^3.0.3",
+				"should-type": "^1.4.0",
+				"should-type-adaptors": "^1.0.1",
+				"should-util": "^1.0.0"
+			}
+		},
+		"node_modules/should-equal": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz",
+			"integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==",
+			"dev": true,
+			"dependencies": {
+				"should-type": "^1.4.0"
+			}
+		},
+		"node_modules/should-format": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz",
+			"integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=",
+			"dev": true,
+			"dependencies": {
+				"should-type": "^1.3.0",
+				"should-type-adaptors": "^1.0.1"
+			}
+		},
+		"node_modules/should-http": {
+			"version": "0.1.1",
+			"resolved": "https://registry.npmjs.org/should-http/-/should-http-0.1.1.tgz",
+			"integrity": "sha1-m3k4Q/QCSIV4HrarrMQDDh6fIfA=",
+			"dev": true,
+			"dependencies": {
+				"content-type": "^1.0.2"
+			},
+			"peerDependencies": {
+				"should": ">= 4.x"
+			}
+		},
+		"node_modules/should-type": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz",
+			"integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=",
+			"dev": true
+		},
+		"node_modules/should-type-adaptors": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz",
+			"integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==",
+			"dev": true,
+			"dependencies": {
+				"should-type": "^1.3.0",
+				"should-util": "^1.0.0"
+			}
+		},
+		"node_modules/should-util": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz",
+			"integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==",
+			"dev": true
+		},
+		"node_modules/side-channel": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+			"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+			"dev": true,
+			"dependencies": {
+				"call-bind": "^1.0.0",
+				"get-intrinsic": "^1.0.2",
+				"object-inspect": "^1.9.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/signal-exit": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+			"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
+		},
+		"node_modules/simple-swizzle": {
+			"version": "0.2.2",
+			"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+			"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+			"dependencies": {
+				"is-arrayish": "^0.3.1"
+			}
+		},
+		"node_modules/simple-swizzle/node_modules/is-arrayish": {
+			"version": "0.3.2",
+			"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+			"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+		},
+		"node_modules/simple-update-notifier": {
+			"version": "1.0.7",
+			"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz",
+			"integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==",
+			"dev": true,
+			"dependencies": {
+				"semver": "~7.0.0"
+			},
+			"engines": {
+				"node": ">=8.10.0"
+			}
+		},
+		"node_modules/simple-update-notifier/node_modules/semver": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
+			"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
+			"dev": true,
+			"bin": {
+				"semver": "bin/semver.js"
+			}
+		},
+		"node_modules/smart-buffer": {
+			"version": "4.2.0",
+			"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+			"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+			"engines": {
+				"node": ">= 6.0.0",
+				"npm": ">= 3.0.0"
+			}
+		},
+		"node_modules/socks": {
+			"version": "2.6.1",
+			"resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz",
+			"integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==",
+			"dependencies": {
+				"ip": "^1.1.5",
+				"smart-buffer": "^4.1.0"
+			},
+			"engines": {
+				"node": ">= 10.13.0",
+				"npm": ">= 3.0.0"
+			}
+		},
+		"node_modules/socks-proxy-agent": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz",
+			"integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==",
+			"dependencies": {
+				"agent-base": "^6.0.2",
+				"debug": "4",
+				"socks": "^2.3.3"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/source-map": {
+			"version": "0.6.1",
+			"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+			"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/source-map-support": {
+			"version": "0.5.19",
+			"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+			"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+			"dependencies": {
+				"buffer-from": "^1.0.0",
+				"source-map": "^0.6.0"
+			}
+		},
+		"node_modules/spawn-command": {
+			"version": "0.0.2-1",
+			"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
+			"integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A="
+		},
+		"node_modules/spdx-correct": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+			"integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+			"dependencies": {
+				"spdx-expression-parse": "^3.0.0",
+				"spdx-license-ids": "^3.0.0"
+			}
+		},
+		"node_modules/spdx-exceptions": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+			"integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="
+		},
+		"node_modules/spdx-expression-parse": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+			"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+			"dependencies": {
+				"spdx-exceptions": "^2.1.0",
+				"spdx-license-ids": "^3.0.0"
+			}
+		},
+		"node_modules/spdx-license-ids": {
+			"version": "3.0.9",
+			"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz",
+			"integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ=="
+		},
+		"node_modules/sprintf-js": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+			"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
+		},
+		"node_modules/sqlstring": {
+			"version": "2.3.2",
+			"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz",
+			"integrity": "sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/stack-trace": {
+			"version": "0.0.10",
+			"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+			"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/statuses": {
+			"version": "1.5.0",
+			"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+			"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/streamsearch": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+			"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=",
+			"engines": {
+				"node": ">=0.8.0"
+			}
+		},
+		"node_modules/string_decoder": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+			"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+			"dependencies": {
+				"safe-buffer": "~5.1.0"
+			}
+		},
+		"node_modules/string-width": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+			"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+			"dependencies": {
+				"code-point-at": "^1.0.0",
+				"is-fullwidth-code-point": "^1.0.0",
+				"strip-ansi": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/strip-ansi": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+			"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+			"dependencies": {
+				"ansi-regex": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/superagent": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz",
+			"integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==",
+			"deprecated": "Please upgrade to v7.0.2+ of superagent.  We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing.  See the releases tab for more information at <https://github.com/visionmedia/superagent/releases>.",
+			"dev": true,
+			"dependencies": {
+				"component-emitter": "^1.3.0",
+				"cookiejar": "^2.1.2",
+				"debug": "^4.1.1",
+				"fast-safe-stringify": "^2.0.7",
+				"form-data": "^3.0.0",
+				"formidable": "^1.2.2",
+				"methods": "^1.1.2",
+				"mime": "^2.4.6",
+				"qs": "^6.9.4",
+				"readable-stream": "^3.6.0",
+				"semver": "^7.3.2"
+			},
+			"engines": {
+				"node": ">= 7.0.0"
+			}
+		},
+		"node_modules/superagent/node_modules/form-data": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+			"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+			"dev": true,
+			"dependencies": {
+				"asynckit": "^0.4.0",
+				"combined-stream": "^1.0.8",
+				"mime-types": "^2.1.12"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/superagent/node_modules/mime": {
+			"version": "2.6.0",
+			"resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+			"integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+			"dev": true,
+			"bin": {
+				"mime": "cli.js"
+			},
+			"engines": {
+				"node": ">=4.0.0"
+			}
+		},
+		"node_modules/superagent/node_modules/qs": {
+			"version": "6.10.2",
+			"resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz",
+			"integrity": "sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==",
+			"dev": true,
+			"dependencies": {
+				"side-channel": "^1.0.4"
+			},
+			"engines": {
+				"node": ">=0.6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/superagent/node_modules/readable-stream": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+			"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+			"dev": true,
+			"dependencies": {
+				"inherits": "^2.0.3",
+				"string_decoder": "^1.1.1",
+				"util-deprecate": "^1.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/supertest": {
+			"version": "6.1.6",
+			"resolved": "https://registry.npmjs.org/supertest/-/supertest-6.1.6.tgz",
+			"integrity": "sha512-0hACYGNJ8OHRg8CRITeZOdbjur7NLuNs0mBjVhdpxi7hP6t3QIbOzLON5RTUmZcy2I9riuII3+Pr2C7yztrIIg==",
+			"dev": true,
+			"dependencies": {
+				"methods": "^1.1.2",
+				"superagent": "^6.1.0"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/supports-color": {
+			"version": "8.1.1",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+			"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+			"dependencies": {
+				"has-flag": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/supports-color?sponsor=1"
+			}
+		},
+		"node_modules/swagger-jsdoc": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.1.0.tgz",
+			"integrity": "sha512-xgep5M8Gq31MxpCbQLvJZpNqHfGPfI+sILCzujZbEXIQp2COtkZgoGASs0gacRs4xHmLDH+GuMGdorPITSG4tA==",
+			"dev": true,
+			"dependencies": {
+				"commander": "6.2.0",
+				"doctrine": "3.0.0",
+				"glob": "7.1.6",
+				"lodash.mergewith": "^4.6.2",
+				"swagger-parser": "10.0.2",
+				"yaml": "2.0.0-1"
+			},
+			"bin": {
+				"swagger-jsdoc": "bin/swagger-jsdoc.js"
+			},
+			"engines": {
+				"node": ">=12.0.0"
+			}
+		},
+		"node_modules/swagger-jsdoc/node_modules/commander": {
+			"version": "6.2.0",
+			"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz",
+			"integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
+			"dev": true,
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/swagger-jsdoc/node_modules/glob": {
+			"version": "7.1.6",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+			"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+			"dev": true,
+			"dependencies": {
+				"fs.realpath": "^1.0.0",
+				"inflight": "^1.0.4",
+				"inherits": "2",
+				"minimatch": "^3.0.4",
+				"once": "^1.3.0",
+				"path-is-absolute": "^1.0.0"
+			},
+			"engines": {
+				"node": "*"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/swagger-parser": {
+			"version": "10.0.2",
+			"resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.2.tgz",
+			"integrity": "sha512-9jHkHM+QXyLGFLk1DkXBwV+4HyNm0Za3b8/zk/+mjr8jgOSiqm3FOTHBSDsBjtn9scdL+8eWcHdupp2NLM8tDw==",
+			"dev": true,
+			"dependencies": {
+				"@apidevtools/swagger-parser": "10.0.2"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/swagger-ui-dist": {
+			"version": "3.52.0",
+			"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.52.0.tgz",
+			"integrity": "sha512-SGfhW8FCih00QG59PphdeAUtTNw7HS5k3iPqDZowerPw9mcbhKchUb12kbROk99c1X6RTWW1gB1kqgfnYGuCSg==",
+			"dev": true
+		},
+		"node_modules/swagger-ui-express": {
+			"version": "4.1.6",
+			"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.1.6.tgz",
+			"integrity": "sha512-Xs2BGGudvDBtL7RXcYtNvHsFtP1DBFPMJFRxHe5ez/VG/rzVOEjazJOOSc/kSCyxreCTKfJrII6MJlL9a6t8vw==",
+			"dev": true,
+			"dependencies": {
+				"swagger-ui-dist": "^3.18.1"
+			},
+			"engines": {
+				"node": ">= v0.10.32"
+			},
+			"peerDependencies": {
+				"express": ">=4.0.0"
+			}
+		},
+		"node_modules/systeminformation": {
+			"version": "5.7.7",
+			"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.7.7.tgz",
+			"integrity": "sha512-aQ7MBeVI2MKPYOi3YJAoZ45JVlRkBA7IXoqGgtVBamvtE0I6JLOyJzD/VVc9pnMXDb3yqaMwssAjhwtJax4/Rw==",
+			"optional": true,
+			"os": [
+				"darwin",
+				"linux",
+				"win32",
+				"freebsd",
+				"openbsd",
+				"netbsd",
+				"sunos"
+			],
+			"bin": {
+				"systeminformation": "lib/cli.js"
+			},
+			"engines": {
+				"node": ">=4.0.0"
+			},
+			"funding": {
+				"type": "Buy me a coffee",
+				"url": "https://www.buymeacoffee.com/systeminfo"
+			}
+		},
+		"node_modules/tar": {
+			"version": "6.1.11",
+			"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
+			"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
+			"dependencies": {
+				"chownr": "^2.0.0",
+				"fs-minipass": "^2.0.0",
+				"minipass": "^3.0.0",
+				"minizlib": "^2.1.1",
+				"mkdirp": "^1.0.3",
+				"yallist": "^4.0.0"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/tar-stream": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+			"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+			"dependencies": {
+				"bl": "^4.0.3",
+				"end-of-stream": "^1.4.1",
+				"fs-constants": "^1.0.0",
+				"inherits": "^2.0.3",
+				"readable-stream": "^3.1.1"
+			},
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/tar-stream/node_modules/readable-stream": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+			"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+			"dependencies": {
+				"inherits": "^2.0.3",
+				"string_decoder": "^1.1.1",
+				"util-deprecate": "^1.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/text-hex": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+			"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
+		},
+		"node_modules/tmp": {
+			"version": "0.2.1",
+			"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
+			"integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
+			"dependencies": {
+				"rimraf": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=8.17.0"
+			}
+		},
+		"node_modules/to-regex-range": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+			"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+			"dependencies": {
+				"is-number": "^7.0.0"
+			},
+			"engines": {
+				"node": ">=8.0"
+			}
+		},
+		"node_modules/toidentifier": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+			"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
+			"engines": {
+				"node": ">=0.6"
+			}
+		},
+		"node_modules/touch": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
+			"integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
+			"dev": true,
+			"dependencies": {
+				"nopt": "~1.0.10"
+			},
+			"bin": {
+				"nodetouch": "bin/nodetouch.js"
+			}
+		},
+		"node_modules/touch/node_modules/nopt": {
+			"version": "1.0.10",
+			"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+			"integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
+			"dev": true,
+			"dependencies": {
+				"abbrev": "1"
+			},
+			"bin": {
+				"nopt": "bin/nopt.js"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/traverse": {
+			"version": "0.3.9",
+			"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
+			"integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=",
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/tree-kill": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+			"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+			"bin": {
+				"tree-kill": "cli.js"
+			}
+		},
+		"node_modules/triple-beam": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
+			"integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
+		},
+		"node_modules/tslib": {
+			"version": "1.14.1",
+			"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+			"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+		},
+		"node_modules/tv4": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz",
+			"integrity": "sha1-0CDIRvrdUMhVq7JeuuzGj8EPeWM=",
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/tx2": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/tx2/-/tx2-1.0.4.tgz",
+			"integrity": "sha512-rU+y30nUY3PyIi+znvv74HzxlpULKwMPAyRK+YiCjvGkk3rY3fic3D6Z+avLpun3V5A6HFwPQ9JrBTMNEV/dxg==",
+			"optional": true,
+			"dependencies": {
+				"json-stringify-safe": "^5.0.1"
+			}
+		},
+		"node_modules/type-check": {
+			"version": "0.3.2",
+			"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+			"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+			"dependencies": {
+				"prelude-ls": "~1.1.2"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/type-fest": {
+			"version": "0.6.0",
+			"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
+			"integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/type-is": {
+			"version": "1.6.18",
+			"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+			"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+			"dependencies": {
+				"media-typer": "0.3.0",
+				"mime-types": "~2.1.24"
+			},
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/typedarray": {
+			"version": "0.0.6",
+			"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+			"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+		},
+		"node_modules/uid-safe": {
+			"version": "2.1.5",
+			"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
+			"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
+			"dependencies": {
+				"random-bytes": "~1.0.0"
+			},
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/uid2": {
+			"version": "0.0.3",
+			"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
+			"integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I="
+		},
+		"node_modules/undefsafe": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+			"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+			"dev": true
+		},
+		"node_modules/underscore": {
+			"version": "1.13.3",
+			"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.3.tgz",
+			"integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA=="
+		},
+		"node_modules/universalify": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+			"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+			"engines": {
+				"node": ">= 4.0.0"
+			}
+		},
+		"node_modules/unpipe": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+			"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/unzipper": {
+			"version": "0.10.11",
+			"resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz",
+			"integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==",
+			"dependencies": {
+				"big-integer": "^1.6.17",
+				"binary": "~0.3.0",
+				"bluebird": "~3.4.1",
+				"buffer-indexof-polyfill": "~1.0.0",
+				"duplexer2": "~0.1.4",
+				"fstream": "^1.0.12",
+				"graceful-fs": "^4.2.2",
+				"listenercount": "~1.0.1",
+				"readable-stream": "~2.3.6",
+				"setimmediate": "~1.0.4"
+			}
+		},
+		"node_modules/url": {
+			"version": "0.10.3",
+			"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
+			"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
+			"dependencies": {
+				"punycode": "1.3.2",
+				"querystring": "0.2.0"
+			}
+		},
+		"node_modules/url-template": {
+			"version": "2.0.8",
+			"resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
+			"integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE="
+		},
+		"node_modules/urlsafe-base64": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz",
+			"integrity": "sha1-I/iQaabGL0bPOh07ABac77kL4MY="
+		},
+		"node_modules/util-deprecate": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+			"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+		},
+		"node_modules/utils-merge": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+			"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+			"engines": {
+				"node": ">= 0.4.0"
+			}
+		},
+		"node_modules/uuid": {
+			"version": "3.4.0",
+			"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+			"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+			"deprecated": "Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.",
+			"bin": {
+				"uuid": "bin/uuid"
+			}
+		},
+		"node_modules/validate-npm-package-license": {
+			"version": "3.0.4",
+			"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+			"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+			"dependencies": {
+				"spdx-correct": "^3.0.0",
+				"spdx-expression-parse": "^3.0.0"
+			}
+		},
+		"node_modules/validator": {
+			"version": "13.7.0",
+			"resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz",
+			"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.10"
+			}
+		},
+		"node_modules/vary": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+			"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/vizion": {
+			"version": "2.2.1",
+			"resolved": "https://registry.npmjs.org/vizion/-/vizion-2.2.1.tgz",
+			"integrity": "sha512-sfAcO2yeSU0CSPFI/DmZp3FsFE9T+8913nv1xWBOyzODv13fwkn6Vl7HqxGpkr9F608M+8SuFId3s+BlZqfXww==",
+			"dependencies": {
+				"async": "^2.6.3",
+				"git-node-fs": "^1.0.0",
+				"ini": "^1.3.5",
+				"js-git": "^0.7.8"
+			},
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/vizion/node_modules/async": {
+			"version": "2.6.3",
+			"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+			"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+			"dependencies": {
+				"lodash": "^4.17.14"
+			}
+		},
+		"node_modules/vm2": {
+			"version": "3.9.5",
+			"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.5.tgz",
+			"integrity": "sha512-LuCAHZN75H9tdrAiLFf030oW7nJV5xwNMuk1ymOZwopmuK3d2H4L1Kv4+GFHgarKiLfXXLFU+7LDABHnwOkWng==",
+			"bin": {
+				"vm2": "bin/vm2"
+			},
+			"engines": {
+				"node": ">=6.0"
+			}
+		},
+		"node_modules/web-push": {
+			"version": "3.4.5",
+			"resolved": "https://registry.npmjs.org/web-push/-/web-push-3.4.5.tgz",
+			"integrity": "sha512-2njbTqZ6Q7ZqqK14YpK1GGmaZs3NmuGYF5b7abCXulUIWFSlSYcZ3NBJQRFcMiQDceD7vQknb8FUuvI1F7Qe/g==",
+			"dependencies": {
+				"asn1.js": "^5.3.0",
+				"http_ece": "1.1.0",
+				"https-proxy-agent": "^5.0.0",
+				"jws": "^4.0.0",
+				"minimist": "^1.2.5",
+				"urlsafe-base64": "^1.0.0"
+			},
+			"bin": {
+				"web-push": "src/cli.js"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/wide-align": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+			"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+			"dependencies": {
+				"string-width": "^1.0.2 || 2"
+			}
+		},
+		"node_modules/winston": {
+			"version": "3.3.3",
+			"resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz",
+			"integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==",
+			"dependencies": {
+				"@dabh/diagnostics": "^2.0.2",
+				"async": "^3.1.0",
+				"is-stream": "^2.0.0",
+				"logform": "^2.2.0",
+				"one-time": "^1.0.0",
+				"readable-stream": "^3.4.0",
+				"stack-trace": "0.0.x",
+				"triple-beam": "^1.3.0",
+				"winston-transport": "^4.4.0"
+			},
+			"engines": {
+				"node": ">= 6.4.0"
+			}
+		},
+		"node_modules/winston-daily-rotate-file": {
+			"version": "4.5.5",
+			"resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.5.5.tgz",
+			"integrity": "sha512-ds0WahIjiDhKCiMXmY799pDBW+58ByqIBtUcsqr4oDoXrAI3Zn+hbgFdUxzMfqA93OG0mPLYVMiotqTgE/WeWQ==",
+			"dependencies": {
+				"file-stream-rotator": "^0.5.7",
+				"object-hash": "^2.0.1",
+				"triple-beam": "^1.3.0",
+				"winston-transport": "^4.4.0"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"peerDependencies": {
+				"winston": "^3"
+			}
+		},
+		"node_modules/winston-transport": {
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz",
+			"integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==",
+			"dependencies": {
+				"readable-stream": "^2.3.7",
+				"triple-beam": "^1.2.0"
+			},
+			"engines": {
+				"node": ">= 6.4.0"
+			}
+		},
+		"node_modules/winston/node_modules/readable-stream": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+			"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+			"dependencies": {
+				"inherits": "^2.0.3",
+				"string_decoder": "^1.1.1",
+				"util-deprecate": "^1.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/word-wrap": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+			"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/wrap-ansi": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+			"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+			"dependencies": {
+				"ansi-styles": "^4.0.0",
+				"string-width": "^4.1.0",
+				"strip-ansi": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+			}
+		},
+		"node_modules/wrap-ansi/node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+			"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/wrap-ansi/node_modules/string-width": {
+			"version": "4.2.2",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+			"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/wrap-ansi/node_modules/strip-ansi": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+			"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+			"dependencies": {
+				"ansi-regex": "^5.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/wrappy": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+			"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+		},
+		"node_modules/ws": {
+			"version": "7.4.6",
+			"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+			"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
+			"engines": {
+				"node": ">=8.3.0"
+			},
+			"peerDependencies": {
+				"bufferutil": "^4.0.1",
+				"utf-8-validate": "^5.0.2"
+			},
+			"peerDependenciesMeta": {
+				"bufferutil": {
+					"optional": true
+				},
+				"utf-8-validate": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/xml2js": {
+			"version": "0.4.19",
+			"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
+			"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
+			"dependencies": {
+				"sax": ">=0.6.0",
+				"xmlbuilder": "~9.0.1"
+			}
+		},
+		"node_modules/xmlbuilder": {
+			"version": "9.0.7",
+			"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
+			"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/xmlchars": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+			"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
+		},
+		"node_modules/xregexp": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz",
+			"integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=",
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/xtend": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+			"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+			"engines": {
+				"node": ">=0.4"
+			}
+		},
+		"node_modules/y18n": {
+			"version": "5.0.8",
+			"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+			"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/yallist": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+			"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+		},
+		"node_modules/yaml": {
+			"version": "2.0.0-1",
+			"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz",
+			"integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==",
+			"dev": true,
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/yamljs": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz",
+			"integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==",
+			"dependencies": {
+				"argparse": "^1.0.7",
+				"glob": "^7.0.5"
+			},
+			"bin": {
+				"json2yaml": "bin/json2yaml",
+				"yaml2json": "bin/yaml2json"
+			}
+		},
+		"node_modules/yargs": {
+			"version": "16.2.0",
+			"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+			"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+			"dependencies": {
+				"cliui": "^7.0.2",
+				"escalade": "^3.1.1",
+				"get-caller-file": "^2.0.5",
+				"require-directory": "^2.1.1",
+				"string-width": "^4.2.0",
+				"y18n": "^5.0.5",
+				"yargs-parser": "^20.2.2"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/yargs-parser": {
+			"version": "20.2.9",
+			"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+			"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/yargs/node_modules/ansi-regex": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+			"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/yargs/node_modules/is-fullwidth-code-point": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+			"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/yargs/node_modules/string-width": {
+			"version": "4.2.2",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+			"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/yargs/node_modules/strip-ansi": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+			"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+			"dependencies": {
+				"ansi-regex": "^5.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/z-schema": {
+			"version": "4.2.4",
+			"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-4.2.4.tgz",
+			"integrity": "sha512-YvBeW5RGNeNzKOUJs3rTL4+9rpcvHXt5I051FJbOcitV8bl40pEfcG0Q+dWSwS0/BIYrMZ/9HHoqLllMkFhD0w==",
+			"dev": true,
+			"dependencies": {
+				"lodash.get": "^4.4.2",
+				"lodash.isequal": "^4.5.0",
+				"validator": "^13.6.0"
+			},
+			"bin": {
+				"z-schema": "bin/z-schema"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			},
+			"optionalDependencies": {
+				"commander": "^2.7.1"
+			}
+		},
+		"node_modules/zip-stream": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz",
+			"integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==",
+			"dependencies": {
+				"archiver-utils": "^2.1.0",
+				"compress-commons": "^4.1.0",
+				"readable-stream": "^3.6.0"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/zip-stream/node_modules/readable-stream": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+			"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+			"dependencies": {
+				"inherits": "^2.0.3",
+				"string_decoder": "^1.1.1",
+				"util-deprecate": "^1.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		}
+	},
+	"dependencies": {
+		"@apidevtools/json-schema-ref-parser": {
+			"version": "9.0.9",
+			"resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz",
+			"integrity": "sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w==",
+			"dev": true,
+			"requires": {
+				"@jsdevtools/ono": "^7.1.3",
+				"@types/json-schema": "^7.0.6",
+				"call-me-maybe": "^1.0.1",
+				"js-yaml": "^4.1.0"
+			}
+		},
+		"@apidevtools/openapi-schemas": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
+			"integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==",
+			"dev": true
+		},
+		"@apidevtools/swagger-methods": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
+			"integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==",
+			"dev": true
+		},
+		"@apidevtools/swagger-parser": {
+			"version": "10.0.2",
+			"resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.2.tgz",
+			"integrity": "sha512-JFxcEyp8RlNHgBCE98nwuTkZT6eNFPc1aosWV6wPcQph72TSEEu1k3baJD4/x1qznU+JiDdz8F5pTwabZh+Dhg==",
+			"dev": true,
+			"requires": {
+				"@apidevtools/json-schema-ref-parser": "^9.0.6",
+				"@apidevtools/openapi-schemas": "^2.0.4",
+				"@apidevtools/swagger-methods": "^3.0.2",
+				"@jsdevtools/ono": "^7.1.3",
+				"call-me-maybe": "^1.0.1",
+				"z-schema": "^4.2.3"
+			}
+		},
+		"@babel/code-frame": {
+			"version": "7.14.5",
+			"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz",
+			"integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==",
+			"requires": {
+				"@babel/highlight": "^7.14.5"
+			}
+		},
+		"@babel/helper-validator-identifier": {
+			"version": "7.14.5",
+			"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz",
+			"integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg=="
+		},
+		"@babel/highlight": {
+			"version": "7.14.5",
+			"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
+			"integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
+			"requires": {
+				"@babel/helper-validator-identifier": "^7.14.5",
+				"chalk": "^2.0.0",
+				"js-tokens": "^4.0.0"
+			},
+			"dependencies": {
+				"ansi-styles": {
+					"version": "3.2.1",
+					"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+					"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+					"requires": {
+						"color-convert": "^1.9.0"
+					}
+				},
+				"chalk": {
+					"version": "2.4.2",
+					"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+					"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+					"requires": {
+						"ansi-styles": "^3.2.1",
+						"escape-string-regexp": "^1.0.5",
+						"supports-color": "^5.3.0"
+					}
+				},
+				"color-convert": {
+					"version": "1.9.3",
+					"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+					"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+					"requires": {
+						"color-name": "1.1.3"
+					}
+				},
+				"color-name": {
+					"version": "1.1.3",
+					"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+					"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+				},
+				"has-flag": {
+					"version": "3.0.0",
+					"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+					"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+				},
+				"supports-color": {
+					"version": "5.5.0",
+					"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+					"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+					"requires": {
+						"has-flag": "^3.0.0"
+					}
+				}
+			}
+		},
+		"@dabh/diagnostics": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz",
+			"integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==",
+			"requires": {
+				"colorspace": "1.1.x",
+				"enabled": "2.0.x",
+				"kuler": "^2.0.0"
+			}
+		},
+		"@fast-csv/format": {
+			"version": "4.3.5",
+			"resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz",
+			"integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==",
+			"requires": {
+				"@types/node": "^14.0.1",
+				"lodash.escaperegexp": "^4.1.2",
+				"lodash.isboolean": "^3.0.3",
+				"lodash.isequal": "^4.5.0",
+				"lodash.isfunction": "^3.0.9",
+				"lodash.isnil": "^4.0.0"
+			}
+		},
+		"@fast-csv/parse": {
+			"version": "4.3.6",
+			"resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz",
+			"integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==",
+			"requires": {
+				"@types/node": "^14.0.1",
+				"lodash.escaperegexp": "^4.1.2",
+				"lodash.groupby": "^4.6.0",
+				"lodash.isfunction": "^3.0.9",
+				"lodash.isnil": "^4.0.0",
+				"lodash.isundefined": "^3.0.1",
+				"lodash.uniq": "^4.5.0"
+			}
+		},
+		"@jsdevtools/ono": {
+			"version": "7.1.3",
+			"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
+			"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
+			"dev": true
+		},
+		"@mapbox/node-pre-gyp": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz",
+			"integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==",
+			"requires": {
+				"detect-libc": "^1.0.3",
+				"https-proxy-agent": "^5.0.0",
+				"make-dir": "^3.1.0",
+				"node-fetch": "^2.6.1",
+				"nopt": "^5.0.0",
+				"npmlog": "^4.1.2",
+				"rimraf": "^3.0.2",
+				"semver": "^7.3.4",
+				"tar": "^6.1.0"
+			}
+		},
+		"@opencensus/core": {
+			"version": "0.0.9",
+			"resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.9.tgz",
+			"integrity": "sha512-31Q4VWtbzXpVUd2m9JS6HEaPjlKvNMOiF7lWKNmXF84yUcgfAFL5re7/hjDmdyQbOp32oGc+RFV78jXIldVz6Q==",
+			"requires": {
+				"continuation-local-storage": "^3.2.1",
+				"log-driver": "^1.2.7",
+				"semver": "^5.5.0",
+				"shimmer": "^1.2.0",
+				"uuid": "^3.2.1"
+			},
+			"dependencies": {
+				"semver": {
+					"version": "5.7.1",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+					"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+				}
+			}
+		},
+		"@opencensus/propagation-b3": {
+			"version": "0.0.8",
+			"resolved": "https://registry.npmjs.org/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz",
+			"integrity": "sha512-PffXX2AL8Sh0VHQ52jJC4u3T0H6wDK6N/4bg7xh4ngMYOIi13aR1kzVvX1sVDBgfGwDOkMbl4c54Xm3tlPx/+A==",
+			"requires": {
+				"@opencensus/core": "^0.0.8",
+				"uuid": "^3.2.1"
+			},
+			"dependencies": {
+				"@opencensus/core": {
+					"version": "0.0.8",
+					"resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.8.tgz",
+					"integrity": "sha512-yUFT59SFhGMYQgX0PhoTR0LBff2BEhPrD9io1jWfF/VDbakRfs6Pq60rjv0Z7iaTav5gQlttJCX2+VPxFWCuoQ==",
+					"requires": {
+						"continuation-local-storage": "^3.2.1",
+						"log-driver": "^1.2.7",
+						"semver": "^5.5.0",
+						"shimmer": "^1.2.0",
+						"uuid": "^3.2.1"
+					}
+				},
+				"semver": {
+					"version": "5.7.1",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+					"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+				}
+			}
+		},
+		"@pm2/agent": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.1.tgz",
+			"integrity": "sha512-QKHMm6yexcvdDfcNE7PL9D6uEjoQPGRi+8dh+rc4Hwtbpsbh5IAvZbz3BVGjcd4HaX6pt2xGpOohG7/Y2L4QLw==",
+			"requires": {
+				"async": "~3.2.0",
+				"chalk": "~3.0.0",
+				"dayjs": "~1.8.24",
+				"debug": "~4.3.1",
+				"eventemitter2": "~5.0.1",
+				"fast-json-patch": "^3.0.0-1",
+				"fclone": "~1.0.11",
+				"nssocket": "0.6.0",
+				"pm2-axon": "~4.0.1",
+				"pm2-axon-rpc": "~0.7.0",
+				"proxy-agent": "~5.0.0",
+				"semver": "~7.2.0",
+				"ws": "~7.4.0"
+			},
+			"dependencies": {
+				"chalk": {
+					"version": "3.0.0",
+					"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+					"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+					"requires": {
+						"ansi-styles": "^4.1.0",
+						"supports-color": "^7.1.0"
+					}
+				},
+				"semver": {
+					"version": "7.2.3",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-7.2.3.tgz",
+					"integrity": "sha512-utbW9Z7ZxVvwiIWkdOMLOR9G/NFXh2aRucghkVrEMJWuC++r3lCkBC3LwqBinyHzGMAJxY5tn6VakZGHObq5ig=="
+				},
+				"supports-color": {
+					"version": "7.2.0",
+					"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+					"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+					"requires": {
+						"has-flag": "^4.0.0"
+					}
+				}
+			}
+		},
+		"@pm2/io": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/@pm2/io/-/io-5.0.0.tgz",
+			"integrity": "sha512-3rToDVJaRoob5Lq8+7Q2TZFruoEkdORxwzFpZaqF4bmH6Bkd7kAbdPrI/z8X6k1Meq5rTtScM7MmDgppH6aLlw==",
+			"requires": {
+				"@opencensus/core": "0.0.9",
+				"@opencensus/propagation-b3": "0.0.8",
+				"async": "~2.6.1",
+				"debug": "~4.3.1",
+				"eventemitter2": "^6.3.1",
+				"require-in-the-middle": "^5.0.0",
+				"semver": "6.3.0",
+				"shimmer": "^1.2.0",
+				"signal-exit": "^3.0.3",
+				"tslib": "1.9.3"
+			},
+			"dependencies": {
+				"async": {
+					"version": "2.6.3",
+					"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+					"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+					"requires": {
+						"lodash": "^4.17.14"
+					}
+				},
+				"eventemitter2": {
+					"version": "6.4.4",
+					"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.4.tgz",
+					"integrity": "sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw=="
+				},
+				"semver": {
+					"version": "6.3.0",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+					"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+				},
+				"tslib": {
+					"version": "1.9.3",
+					"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+					"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
+				}
+			}
+		},
+		"@pm2/js-api": {
+			"version": "0.6.7",
+			"resolved": "https://registry.npmjs.org/@pm2/js-api/-/js-api-0.6.7.tgz",
+			"integrity": "sha512-jiJUhbdsK+5C4zhPZNnyA3wRI01dEc6a2GhcQ9qI38DyIk+S+C8iC3fGjcjUbt/viLYKPjlAaE+hcT2/JMQPXw==",
+			"requires": {
+				"async": "^2.6.3",
+				"axios": "^0.21.0",
+				"debug": "~4.3.1",
+				"eventemitter2": "^6.3.1",
+				"ws": "^7.0.0"
+			},
+			"dependencies": {
+				"async": {
+					"version": "2.6.3",
+					"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+					"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+					"requires": {
+						"lodash": "^4.17.14"
+					}
+				},
+				"eventemitter2": {
+					"version": "6.4.4",
+					"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.4.tgz",
+					"integrity": "sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw=="
+				}
+			}
+		},
+		"@redis/bloom": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.1.0.tgz",
+			"integrity": "sha512-9QovlxmpRtvxVbN0UBcv8WfdSMudNZZTFqCsnBszcQXqaZb/TVe30ScgGEO7u1EAIacTPAo7/oCYjYAxiHLanQ==",
+			"requires": {}
+		},
+		"@redis/client": {
+			"version": "1.4.2",
+			"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.4.2.tgz",
+			"integrity": "sha512-oUdEjE0I7JS5AyaAjkD3aOXn9NhO7XKyPyXEyrgFDu++VrVBHUPnV6dgEya9TcMuj5nIJRuCzCm8ZP+c9zCHPw==",
+			"requires": {
+				"cluster-key-slot": "1.1.1",
+				"generic-pool": "3.9.0",
+				"yallist": "4.0.0"
+			}
+		},
+		"@redis/graph": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz",
+			"integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==",
+			"requires": {}
+		},
+		"@redis/json": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz",
+			"integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==",
+			"requires": {}
+		},
+		"@redis/search": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.0.tgz",
+			"integrity": "sha512-NyFZEVnxIJEybpy+YskjgOJRNsfTYqaPbK/Buv6W2kmFNaRk85JiqjJZA5QkRmWvGbyQYwoO5QfDi2wHskKrQQ==",
+			"requires": {}
+		},
+		"@redis/time-series": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz",
+			"integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==",
+			"requires": {}
+		},
+		"@sentry/core": {
+			"version": "7.36.0",
+			"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.36.0.tgz",
+			"integrity": "sha512-lq1MlcMhvm7QIwUOknFeufkg4M6QREY3s61y6pm1o+o3vSqB7Hz0D19xlyEpP62qMn8OyuttVKOVK1UfGc2EyQ==",
+			"requires": {
+				"@sentry/types": "7.36.0",
+				"@sentry/utils": "7.36.0",
+				"tslib": "^1.9.3"
+			}
+		},
+		"@sentry/node": {
+			"version": "7.36.0",
+			"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.36.0.tgz",
+			"integrity": "sha512-nAHAY+Rbn5OlTpNX/i6wYrmw3hT/BtwPZ/vNU52cKgw7CpeE1UrCeFjnPn18rQPB7lIh7x0vNvoaPrfemRzpSQ==",
+			"requires": {
+				"@sentry/core": "7.36.0",
+				"@sentry/types": "7.36.0",
+				"@sentry/utils": "7.36.0",
+				"cookie": "^0.4.1",
+				"https-proxy-agent": "^5.0.0",
+				"lru_map": "^0.3.3",
+				"tslib": "^1.9.3"
+			},
+			"dependencies": {
+				"cookie": {
+					"version": "0.4.2",
+					"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+					"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
+				}
+			}
+		},
+		"@sentry/tracing": {
+			"version": "7.36.0",
+			"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.36.0.tgz",
+			"integrity": "sha512-5R5mfWMDncOcTMmmyYMjgus1vZJzIFw4LHaSbrX7e1IRNT/6vFyNeVxATa2ePXb9mI3XHo5f2p7YrnreAtaSXw==",
+			"requires": {
+				"@sentry/core": "7.36.0",
+				"@sentry/types": "7.36.0",
+				"@sentry/utils": "7.36.0",
+				"tslib": "^1.9.3"
+			}
+		},
+		"@sentry/types": {
+			"version": "7.36.0",
+			"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.36.0.tgz",
+			"integrity": "sha512-uvfwUn3okAWSZ948D/xqBrkc3Sn6TeHUgi3+p/dTTNGAXXskzavgfgQ4rSW7f3YD4LL+boZojpoIARVLodMGuA=="
+		},
+		"@sentry/utils": {
+			"version": "7.36.0",
+			"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.36.0.tgz",
+			"integrity": "sha512-mgDi5X5Bm0sydCzXpnyKD/sD98yc2qnKXyRdNX4HRRwruhC/P53LT0hGhZXsyqsB/l8OAMl0zWXJLg0xONQsEw==",
+			"requires": {
+				"@sentry/types": "7.36.0",
+				"tslib": "^1.9.3"
+			}
+		},
+		"@tootallnate/once": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+			"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="
+		},
+		"@types/json-schema": {
+			"version": "7.0.9",
+			"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
+			"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
+			"dev": true
+		},
+		"@types/node": {
+			"version": "14.17.19",
+			"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.19.tgz",
+			"integrity": "sha512-jjYI6NkyfXykucU6ELEoT64QyKOdvaA6enOqKtP4xUsGY0X0ZUZz29fUmrTRo+7v7c6TgDu82q3GHHaCEkqZwA=="
+		},
+		"@types/normalize-package-data": {
+			"version": "2.4.0",
+			"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+			"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA=="
+		},
+		"abbrev": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+			"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+		},
+		"abort-controller": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+			"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+			"requires": {
+				"event-target-shim": "^5.0.0"
+			}
+		},
+		"accepts": {
+			"version": "1.3.7",
+			"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+			"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+			"requires": {
+				"mime-types": "~2.1.24",
+				"negotiator": "0.6.2"
+			}
+		},
+		"agent-base": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+			"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+			"requires": {
+				"debug": "4"
+			}
+		},
+		"amp": {
+			"version": "0.3.1",
+			"resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz",
+			"integrity": "sha1-at+NWKdPNh6CwfqNOJwHnhOfxH0="
+		},
+		"amp-message": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/amp-message/-/amp-message-0.1.2.tgz",
+			"integrity": "sha1-p48cmJlQh602GSpBKY5NtJ49/EU=",
+			"requires": {
+				"amp": "0.3.1"
+			}
+		},
+		"ansi-colors": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+			"integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA=="
+		},
+		"ansi-regex": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+			"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+		},
+		"ansi-styles": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+			"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+			"requires": {
+				"color-convert": "^2.0.1"
+			}
+		},
+		"anymatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+			"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+			"requires": {
+				"normalize-path": "^3.0.0",
+				"picomatch": "^2.0.4"
+			}
+		},
+		"append-field": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+			"integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY="
+		},
+		"aproba": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+			"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+		},
+		"archiver": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz",
+			"integrity": "sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==",
+			"requires": {
+				"archiver-utils": "^2.1.0",
+				"async": "^3.2.0",
+				"buffer-crc32": "^0.2.1",
+				"readable-stream": "^3.6.0",
+				"readdir-glob": "^1.0.0",
+				"tar-stream": "^2.2.0",
+				"zip-stream": "^4.1.0"
+			},
+			"dependencies": {
+				"readable-stream": {
+					"version": "3.6.0",
+					"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+					"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+					"requires": {
+						"inherits": "^2.0.3",
+						"string_decoder": "^1.1.1",
+						"util-deprecate": "^1.0.1"
+					}
+				}
+			}
+		},
+		"archiver-utils": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
+			"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
+			"requires": {
+				"glob": "^7.1.4",
+				"graceful-fs": "^4.2.0",
+				"lazystream": "^1.0.0",
+				"lodash.defaults": "^4.2.0",
+				"lodash.difference": "^4.5.0",
+				"lodash.flatten": "^4.4.0",
+				"lodash.isplainobject": "^4.0.6",
+				"lodash.union": "^4.6.0",
+				"normalize-path": "^3.0.0",
+				"readable-stream": "^2.0.0"
+			}
+		},
+		"are-we-there-yet": {
+			"version": "1.1.5",
+			"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+			"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+			"requires": {
+				"delegates": "^1.0.0",
+				"readable-stream": "^2.0.6"
+			}
+		},
+		"argparse": {
+			"version": "1.0.10",
+			"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+			"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+			"requires": {
+				"sprintf-js": "~1.0.2"
+			}
+		},
+		"array-flatten": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+			"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+		},
+		"arrify": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+			"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug=="
+		},
+		"asn1.js": {
+			"version": "5.4.1",
+			"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+			"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+			"requires": {
+				"bn.js": "^4.0.0",
+				"inherits": "^2.0.1",
+				"minimalistic-assert": "^1.0.0",
+				"safer-buffer": "^2.1.0"
+			}
+		},
+		"ast-types": {
+			"version": "0.13.4",
+			"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
+			"integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
+			"requires": {
+				"tslib": "^2.0.1"
+			},
+			"dependencies": {
+				"tslib": {
+					"version": "2.3.1",
+					"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+					"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+				}
+			}
+		},
+		"async": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
+			"integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
+		},
+		"async-listener": {
+			"version": "0.6.10",
+			"resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz",
+			"integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==",
+			"requires": {
+				"semver": "^5.3.0",
+				"shimmer": "^1.1.0"
+			},
+			"dependencies": {
+				"semver": {
+					"version": "5.7.1",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+					"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+				}
+			}
+		},
+		"asynckit": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+			"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+		},
+		"aws-sdk": {
+			"version": "2.1072.0",
+			"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1072.0.tgz",
+			"integrity": "sha512-b0gEHuC6xTGduPTS+ZCScurw9RTyOned9gf6H0rDagW8hdSMebsFQy84ZreeiZHHChyKyWrNUbUFugOYdw+WXw==",
+			"requires": {
+				"buffer": "4.9.2",
+				"events": "1.1.1",
+				"ieee754": "1.1.13",
+				"jmespath": "0.16.0",
+				"querystring": "0.2.0",
+				"sax": "1.2.1",
+				"url": "0.10.3",
+				"uuid": "3.3.2",
+				"xml2js": "0.4.19"
+			},
+			"dependencies": {
+				"sax": {
+					"version": "1.2.1",
+					"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
+					"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
+				},
+				"uuid": {
+					"version": "3.3.2",
+					"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+					"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
+				}
+			}
+		},
+		"axios": {
+			"version": "0.21.4",
+			"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+			"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+			"requires": {
+				"follow-redirects": "^1.14.0"
+			}
+		},
+		"balanced-match": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+			"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+		},
+		"base64-js": {
+			"version": "1.5.1",
+			"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+			"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
+		},
+		"bcrypt": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz",
+			"integrity": "sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==",
+			"requires": {
+				"@mapbox/node-pre-gyp": "^1.0.0",
+				"node-addon-api": "^3.1.0"
+			}
+		},
+		"big-integer": {
+			"version": "1.6.49",
+			"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.49.tgz",
+			"integrity": "sha512-KJ7VhqH+f/BOt9a3yMwJNmcZjG53ijWMTjSAGMveQWyLwqIiwkjNP5PFgDob3Snnx86SjDj6I89fIbv0dkQeNw=="
+		},
+		"bignumber.js": {
+			"version": "9.0.1",
+			"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
+			"integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA=="
+		},
+		"binary": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
+			"integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=",
+			"requires": {
+				"buffers": "~0.1.1",
+				"chainsaw": "~0.1.0"
+			}
+		},
+		"binary-extensions": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+			"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
+		},
+		"bl": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+			"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+			"requires": {
+				"buffer": "^5.5.0",
+				"inherits": "^2.0.4",
+				"readable-stream": "^3.4.0"
+			},
+			"dependencies": {
+				"buffer": {
+					"version": "5.7.1",
+					"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+					"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+					"requires": {
+						"base64-js": "^1.3.1",
+						"ieee754": "^1.1.13"
+					}
+				},
+				"readable-stream": {
+					"version": "3.6.0",
+					"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+					"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+					"requires": {
+						"inherits": "^2.0.3",
+						"string_decoder": "^1.1.1",
+						"util-deprecate": "^1.0.1"
+					}
+				}
+			}
+		},
+		"blessed": {
+			"version": "0.1.81",
+			"resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz",
+			"integrity": "sha1-+WLWh+wsNpVwrnGvhDJW5tDKESk="
+		},
+		"bluebird": {
+			"version": "3.4.7",
+			"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
+			"integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM="
+		},
+		"bn.js": {
+			"version": "4.12.0",
+			"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+			"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+		},
+		"bodec": {
+			"version": "0.1.0",
+			"resolved": "https://registry.npmjs.org/bodec/-/bodec-0.1.0.tgz",
+			"integrity": "sha1-vIUVVUMPI8n3ZQp172TGqUw0GMw="
+		},
+		"body-parser": {
+			"version": "1.19.0",
+			"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+			"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+			"requires": {
+				"bytes": "3.1.0",
+				"content-type": "~1.0.4",
+				"debug": "2.6.9",
+				"depd": "~1.1.2",
+				"http-errors": "1.7.2",
+				"iconv-lite": "0.4.24",
+				"on-finished": "~2.3.0",
+				"qs": "6.7.0",
+				"raw-body": "2.4.0",
+				"type-is": "~1.6.17"
+			},
+			"dependencies": {
+				"debug": {
+					"version": "2.6.9",
+					"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+					"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+					"requires": {
+						"ms": "2.0.0"
+					}
+				},
+				"iconv-lite": {
+					"version": "0.4.24",
+					"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+					"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+					"requires": {
+						"safer-buffer": ">= 2.1.2 < 3"
+					}
+				},
+				"ms": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+					"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+				}
+			}
+		},
+		"boolbase": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+			"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
+		},
+		"boolean": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.2.tgz",
+			"integrity": "sha512-YN6UmV0FfLlBVvRvNPx3pz5W/mUoYB24J4WSXOKP/OOJpi+Oq6WYqPaNTHzjI0QzwWtnvEd5CGYyQPgp1jFxnw=="
+		},
+		"brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"requires": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"braces": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+			"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+			"requires": {
+				"fill-range": "^7.0.1"
+			}
+		},
+		"buffer": {
+			"version": "4.9.2",
+			"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+			"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+			"requires": {
+				"base64-js": "^1.0.2",
+				"ieee754": "^1.1.4",
+				"isarray": "^1.0.0"
+			}
+		},
+		"buffer-crc32": {
+			"version": "0.2.13",
+			"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+			"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
+		},
+		"buffer-equal-constant-time": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+			"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+		},
+		"buffer-from": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+			"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
+		},
+		"buffer-indexof-polyfill": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz",
+			"integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A=="
+		},
+		"buffers": {
+			"version": "0.1.1",
+			"resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
+			"integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s="
+		},
+		"busboy": {
+			"version": "0.2.14",
+			"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
+			"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
+			"requires": {
+				"dicer": "0.2.5",
+				"readable-stream": "1.1.x"
+			},
+			"dependencies": {
+				"isarray": {
+					"version": "0.0.1",
+					"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+					"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+				},
+				"readable-stream": {
+					"version": "1.1.14",
+					"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+					"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+					"requires": {
+						"core-util-is": "~1.0.0",
+						"inherits": "~2.0.1",
+						"isarray": "0.0.1",
+						"string_decoder": "~0.10.x"
+					}
+				},
+				"string_decoder": {
+					"version": "0.10.31",
+					"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+					"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+				}
+			}
+		},
+		"bytes": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+			"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+		},
+		"call-bind": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+			"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+			"dev": true,
+			"requires": {
+				"function-bind": "^1.1.1",
+				"get-intrinsic": "^1.0.2"
+			}
+		},
+		"call-me-maybe": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
+			"integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=",
+			"dev": true
+		},
+		"chainsaw": {
+			"version": "0.1.0",
+			"resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
+			"integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=",
+			"requires": {
+				"traverse": ">=0.3.0 <0.4"
+			}
+		},
+		"chalk": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+			"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
+			"requires": {
+				"ansi-styles": "^4.1.0",
+				"supports-color": "^7.1.0"
+			},
+			"dependencies": {
+				"supports-color": {
+					"version": "7.2.0",
+					"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+					"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+					"requires": {
+						"has-flag": "^4.0.0"
+					}
+				}
+			}
+		},
+		"charm": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz",
+			"integrity": "sha1-BsIe7RobBq62dVPNxT4jJ0usIpY="
+		},
+		"cheerio": {
+			"version": "1.0.0-rc.10",
+			"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz",
+			"integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==",
+			"requires": {
+				"cheerio-select": "^1.5.0",
+				"dom-serializer": "^1.3.2",
+				"domhandler": "^4.2.0",
+				"htmlparser2": "^6.1.0",
+				"parse5": "^6.0.1",
+				"parse5-htmlparser2-tree-adapter": "^6.0.1",
+				"tslib": "^2.2.0"
+			},
+			"dependencies": {
+				"tslib": {
+					"version": "2.3.1",
+					"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+					"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+				}
+			}
+		},
+		"cheerio-select": {
+			"version": "1.5.0",
+			"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz",
+			"integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==",
+			"requires": {
+				"css-select": "^4.1.3",
+				"css-what": "^5.0.1",
+				"domelementtype": "^2.2.0",
+				"domhandler": "^4.2.0",
+				"domutils": "^2.7.0"
+			}
+		},
+		"chokidar": {
+			"version": "3.5.2",
+			"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
+			"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+			"requires": {
+				"anymatch": "~3.1.2",
+				"braces": "~3.0.2",
+				"fsevents": "~2.3.2",
+				"glob-parent": "~5.1.2",
+				"is-binary-path": "~2.1.0",
+				"is-glob": "~4.0.1",
+				"normalize-path": "~3.0.0",
+				"readdirp": "~3.6.0"
+			}
+		},
+		"chownr": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+			"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
+		},
+		"cli-tableau": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/cli-tableau/-/cli-tableau-2.0.1.tgz",
+			"integrity": "sha512-he+WTicka9cl0Fg/y+YyxcN6/bfQ/1O3QmgxRXDhABKqLzvoOSM4fMzp39uMyLBulAFuywD2N7UaoQE7WaADxQ==",
+			"requires": {
+				"chalk": "3.0.0"
+			},
+			"dependencies": {
+				"chalk": {
+					"version": "3.0.0",
+					"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+					"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+					"requires": {
+						"ansi-styles": "^4.1.0",
+						"supports-color": "^7.1.0"
+					}
+				},
+				"supports-color": {
+					"version": "7.2.0",
+					"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+					"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+					"requires": {
+						"has-flag": "^4.0.0"
+					}
+				}
+			}
+		},
+		"cliui": {
+			"version": "7.0.4",
+			"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+			"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+			"requires": {
+				"string-width": "^4.2.0",
+				"strip-ansi": "^6.0.0",
+				"wrap-ansi": "^7.0.0"
+			},
+			"dependencies": {
+				"ansi-regex": {
+					"version": "5.0.1",
+					"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+					"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+				},
+				"is-fullwidth-code-point": {
+					"version": "3.0.0",
+					"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+					"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+				},
+				"string-width": {
+					"version": "4.2.2",
+					"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+					"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+					"requires": {
+						"emoji-regex": "^8.0.0",
+						"is-fullwidth-code-point": "^3.0.0",
+						"strip-ansi": "^6.0.0"
+					}
+				},
+				"strip-ansi": {
+					"version": "6.0.0",
+					"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+					"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+					"requires": {
+						"ansi-regex": "^5.0.0"
+					}
+				}
+			}
+		},
+		"clone": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+			"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="
+		},
+		"cluster-key-slot": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz",
+			"integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw=="
+		},
+		"code-point-at": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+			"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+		},
+		"color": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz",
+			"integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==",
+			"requires": {
+				"color-convert": "^1.9.1",
+				"color-string": "^1.5.2"
+			},
+			"dependencies": {
+				"color-convert": {
+					"version": "1.9.3",
+					"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+					"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+					"requires": {
+						"color-name": "1.1.3"
+					}
+				},
+				"color-name": {
+					"version": "1.1.3",
+					"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+					"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+				}
+			}
+		},
+		"color-convert": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+			"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+			"requires": {
+				"color-name": "~1.1.4"
+			}
+		},
+		"color-name": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+			"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+		},
+		"color-string": {
+			"version": "1.5.5",
+			"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz",
+			"integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==",
+			"requires": {
+				"color-name": "^1.0.0",
+				"simple-swizzle": "^0.2.2"
+			}
+		},
+		"colors": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+			"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
+		},
+		"colorspace": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz",
+			"integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==",
+			"requires": {
+				"color": "3.0.x",
+				"text-hex": "1.0.x"
+			}
+		},
+		"combined-stream": {
+			"version": "1.0.8",
+			"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+			"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+			"requires": {
+				"delayed-stream": "~1.0.0"
+			}
+		},
+		"commander": {
+			"version": "2.15.1",
+			"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+			"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
+		},
+		"component-emitter": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+			"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+			"dev": true
+		},
+		"compress-commons": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz",
+			"integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==",
+			"requires": {
+				"buffer-crc32": "^0.2.13",
+				"crc32-stream": "^4.0.2",
+				"normalize-path": "^3.0.0",
+				"readable-stream": "^3.6.0"
+			},
+			"dependencies": {
+				"readable-stream": {
+					"version": "3.6.0",
+					"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+					"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+					"requires": {
+						"inherits": "^2.0.3",
+						"string_decoder": "^1.1.1",
+						"util-deprecate": "^1.0.1"
+					}
+				}
+			}
+		},
+		"compressible": {
+			"version": "2.0.18",
+			"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+			"integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+			"requires": {
+				"mime-db": ">= 1.43.0 < 2"
+			}
+		},
+		"compression": {
+			"version": "1.7.4",
+			"resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+			"integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+			"requires": {
+				"accepts": "~1.3.5",
+				"bytes": "3.0.0",
+				"compressible": "~2.0.16",
+				"debug": "2.6.9",
+				"on-headers": "~1.0.2",
+				"safe-buffer": "5.1.2",
+				"vary": "~1.1.2"
+			},
+			"dependencies": {
+				"bytes": {
+					"version": "3.0.0",
+					"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+					"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+				},
+				"debug": {
+					"version": "2.6.9",
+					"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+					"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+					"requires": {
+						"ms": "2.0.0"
+					}
+				},
+				"ms": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+					"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+				}
+			}
+		},
+		"concat-map": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+			"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+		},
+		"concat-stream": {
+			"version": "1.6.2",
+			"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+			"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+			"requires": {
+				"buffer-from": "^1.0.0",
+				"inherits": "^2.0.3",
+				"readable-stream": "^2.2.2",
+				"typedarray": "^0.0.6"
+			}
+		},
+		"concurrently": {
+			"version": "6.2.0",
+			"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.2.0.tgz",
+			"integrity": "sha512-v9I4Y3wFoXCSY2L73yYgwA9ESrQMpRn80jMcqMgHx720Hecz2GZAvTI6bREVST6lkddNypDKRN22qhK0X8Y00g==",
+			"requires": {
+				"chalk": "^4.1.0",
+				"date-fns": "^2.16.1",
+				"lodash": "^4.17.21",
+				"read-pkg": "^5.2.0",
+				"rxjs": "^6.6.3",
+				"spawn-command": "^0.0.2-1",
+				"supports-color": "^8.1.0",
+				"tree-kill": "^1.2.2",
+				"yargs": "^16.2.0"
+			}
+		},
+		"connect-redis": {
+			"version": "6.1.3",
+			"resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-6.1.3.tgz",
+			"integrity": "sha512-aaNluLlAn/3JPxRwdzw7lhvEoU6Enb+d83xnokUNhC9dktqBoawKWL+WuxinxvBLTz6q9vReTnUDnUslaz74aw=="
+		},
+		"console-control-strings": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+			"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+		},
+		"content-disposition": {
+			"version": "0.5.3",
+			"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+			"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+			"requires": {
+				"safe-buffer": "5.1.2"
+			}
+		},
+		"content-type": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+			"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+		},
+		"continuation-local-storage": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz",
+			"integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==",
+			"requires": {
+				"async-listener": "^0.6.0",
+				"emitter-listener": "^1.1.1"
+			}
+		},
+		"cookie": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+			"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
+		},
+		"cookie-parser": {
+			"version": "1.4.6",
+			"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
+			"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
+			"requires": {
+				"cookie": "0.4.1",
+				"cookie-signature": "1.0.6"
+			},
+			"dependencies": {
+				"cookie": {
+					"version": "0.4.1",
+					"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+					"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
+				}
+			}
+		},
+		"cookie-signature": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+			"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+		},
+		"cookiejar": {
+			"version": "2.1.3",
+			"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz",
+			"integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==",
+			"dev": true
+		},
+		"core-util-is": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+			"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+		},
+		"cors": {
+			"version": "2.8.5",
+			"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+			"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+			"requires": {
+				"object-assign": "^4",
+				"vary": "^1"
+			}
+		},
+		"crc-32": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz",
+			"integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==",
+			"requires": {
+				"exit-on-epipe": "~1.0.1",
+				"printj": "~1.1.0"
+			}
+		},
+		"crc32-stream": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz",
+			"integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==",
+			"requires": {
+				"crc-32": "^1.2.0",
+				"readable-stream": "^3.4.0"
+			},
+			"dependencies": {
+				"readable-stream": {
+					"version": "3.6.0",
+					"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+					"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+					"requires": {
+						"inherits": "^2.0.3",
+						"string_decoder": "^1.1.1",
+						"util-deprecate": "^1.0.1"
+					}
+				}
+			}
+		},
+		"cron": {
+			"version": "1.8.2",
+			"resolved": "https://registry.npmjs.org/cron/-/cron-1.8.2.tgz",
+			"integrity": "sha512-Gk2c4y6xKEO8FSAUTklqtfSr7oTq0CiPQeLBG5Fl0qoXpZyMcj1SG59YL+hqq04bu6/IuEA7lMkYDAplQNKkyg==",
+			"requires": {
+				"moment-timezone": "^0.5.x"
+			}
+		},
+		"crypto": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz",
+			"integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig=="
+		},
+		"crypto-js": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
+			"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
+		},
+		"css-select": {
+			"version": "4.1.3",
+			"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz",
+			"integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==",
+			"requires": {
+				"boolbase": "^1.0.0",
+				"css-what": "^5.0.0",
+				"domhandler": "^4.2.0",
+				"domutils": "^2.6.0",
+				"nth-check": "^2.0.0"
+			}
+		},
+		"css-what": {
+			"version": "5.1.0",
+			"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
+			"integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw=="
+		},
+		"culvert": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/culvert/-/culvert-0.1.2.tgz",
+			"integrity": "sha1-lQL18BVKLVoioCPnn3HMk2+m728="
+		},
+		"data-uri-to-buffer": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
+			"integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og=="
+		},
+		"date-fns": {
+			"version": "2.22.1",
+			"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.22.1.tgz",
+			"integrity": "sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg=="
+		},
+		"dayjs": {
+			"version": "1.8.36",
+			"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz",
+			"integrity": "sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw=="
+		},
+		"debug": {
+			"version": "4.3.4",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+			"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+			"requires": {
+				"ms": "2.1.2"
+			}
+		},
+		"deep-is": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+			"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
+		},
+		"degenerator": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.1.tgz",
+			"integrity": "sha512-LFsIFEeLPlKvAKXu7j3ssIG6RT0TbI7/GhsqrI0DnHASEQjXQ0LUSYcjJteGgRGmZbl1TnMSxpNQIAiJ7Du5TQ==",
+			"requires": {
+				"ast-types": "^0.13.2",
+				"escodegen": "^1.8.1",
+				"esprima": "^4.0.0",
+				"vm2": "^3.9.3"
+			}
+		},
+		"delayed-stream": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+			"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+		},
+		"delegates": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+			"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+		},
+		"denque": {
+			"version": "1.5.0",
+			"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
+			"integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ=="
+		},
+		"depd": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+			"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+		},
+		"destroy": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+			"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+		},
+		"detect-libc": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+			"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
+		},
+		"dicer": {
+			"version": "0.2.5",
+			"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
+			"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
+			"requires": {
+				"readable-stream": "1.1.x",
+				"streamsearch": "0.1.2"
+			},
+			"dependencies": {
+				"isarray": {
+					"version": "0.0.1",
+					"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+					"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+				},
+				"readable-stream": {
+					"version": "1.1.14",
+					"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+					"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+					"requires": {
+						"core-util-is": "~1.0.0",
+						"inherits": "~2.0.1",
+						"isarray": "0.0.1",
+						"string_decoder": "~0.10.x"
+					}
+				},
+				"string_decoder": {
+					"version": "0.10.31",
+					"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+					"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+				}
+			}
+		},
+		"doctrine": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+			"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+			"dev": true,
+			"requires": {
+				"esutils": "^2.0.2"
+			}
+		},
+		"dom-serializer": {
+			"version": "1.3.2",
+			"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+			"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+			"requires": {
+				"domelementtype": "^2.0.1",
+				"domhandler": "^4.2.0",
+				"entities": "^2.0.0"
+			}
+		},
+		"domelementtype": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+			"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
+		},
+		"domhandler": {
+			"version": "4.2.2",
+			"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz",
+			"integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==",
+			"requires": {
+				"domelementtype": "^2.2.0"
+			}
+		},
+		"domutils": {
+			"version": "2.8.0",
+			"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+			"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+			"requires": {
+				"dom-serializer": "^1.0.1",
+				"domelementtype": "^2.2.0",
+				"domhandler": "^4.2.0"
+			}
+		},
+		"dotenv": {
+			"version": "15.0.0",
+			"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-15.0.0.tgz",
+			"integrity": "sha512-/l1sXXm79ry34KwwS0y4oVZjB468iw/6u9g1W26dtexKcIJAnVL2pMF+hxQwzZ7LutxOwEgtym9eIxvX33CMKg=="
+		},
+		"duplexer2": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+			"integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+			"requires": {
+				"readable-stream": "^2.0.2"
+			}
+		},
+		"ecdsa-sig-formatter": {
+			"version": "1.0.11",
+			"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+			"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+			"requires": {
+				"safe-buffer": "^5.0.1"
+			}
+		},
+		"ee-first": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+			"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+		},
+		"emitter-listener": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz",
+			"integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==",
+			"requires": {
+				"shimmer": "^1.2.0"
+			}
+		},
+		"emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+		},
+		"enabled": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
+			"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
+		},
+		"encodeurl": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+			"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+		},
+		"end-of-stream": {
+			"version": "1.4.4",
+			"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+			"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+			"requires": {
+				"once": "^1.4.0"
+			}
+		},
+		"enquirer": {
+			"version": "2.3.6",
+			"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+			"integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+			"requires": {
+				"ansi-colors": "^4.1.1"
+			}
+		},
+		"entities": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+			"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
+		},
+		"error-ex": {
+			"version": "1.3.2",
+			"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+			"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+			"requires": {
+				"is-arrayish": "^0.2.1"
+			}
+		},
+		"escalade": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+			"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
+		},
+		"escape-html": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+			"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+		},
+		"escape-string-regexp": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+			"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+		},
+		"escodegen": {
+			"version": "1.14.3",
+			"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+			"integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+			"requires": {
+				"esprima": "^4.0.1",
+				"estraverse": "^4.2.0",
+				"esutils": "^2.0.2",
+				"optionator": "^0.8.1",
+				"source-map": "~0.6.1"
+			}
+		},
+		"esprima": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+			"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
+		},
+		"estraverse": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+			"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
+		},
+		"esutils": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+			"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
+		},
+		"etag": {
+			"version": "1.8.1",
+			"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+			"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+		},
+		"event-target-shim": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+			"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
+		},
+		"eventemitter2": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz",
+			"integrity": "sha1-YZegldX7a1folC9v1+qtY6CclFI="
+		},
+		"events": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+			"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
+		},
+		"exceljs": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.3.0.tgz",
+			"integrity": "sha512-hTAeo5b5TPvf8Z02I2sKIT4kSfCnOO2bCxYX8ABqODCdAjppI3gI9VYiGCQQYVcBaBSKlFDMKlAQRqC+kV9O8w==",
+			"requires": {
+				"archiver": "^5.0.0",
+				"dayjs": "^1.8.34",
+				"fast-csv": "^4.3.1",
+				"jszip": "^3.5.0",
+				"readable-stream": "^3.6.0",
+				"saxes": "^5.0.1",
+				"tmp": "^0.2.0",
+				"unzipper": "^0.10.11",
+				"uuid": "^8.3.0"
+			},
+			"dependencies": {
+				"readable-stream": {
+					"version": "3.6.0",
+					"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+					"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+					"requires": {
+						"inherits": "^2.0.3",
+						"string_decoder": "^1.1.1",
+						"util-deprecate": "^1.0.1"
+					}
+				},
+				"uuid": {
+					"version": "8.3.2",
+					"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+					"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
+				}
+			}
+		},
+		"exit-on-epipe": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
+			"integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw=="
+		},
+		"express": {
+			"version": "4.17.1",
+			"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+			"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+			"requires": {
+				"accepts": "~1.3.7",
+				"array-flatten": "1.1.1",
+				"body-parser": "1.19.0",
+				"content-disposition": "0.5.3",
+				"content-type": "~1.0.4",
+				"cookie": "0.4.0",
+				"cookie-signature": "1.0.6",
+				"debug": "2.6.9",
+				"depd": "~1.1.2",
+				"encodeurl": "~1.0.2",
+				"escape-html": "~1.0.3",
+				"etag": "~1.8.1",
+				"finalhandler": "~1.1.2",
+				"fresh": "0.5.2",
+				"merge-descriptors": "1.0.1",
+				"methods": "~1.1.2",
+				"on-finished": "~2.3.0",
+				"parseurl": "~1.3.3",
+				"path-to-regexp": "0.1.7",
+				"proxy-addr": "~2.0.5",
+				"qs": "6.7.0",
+				"range-parser": "~1.2.1",
+				"safe-buffer": "5.1.2",
+				"send": "0.17.1",
+				"serve-static": "1.14.1",
+				"setprototypeof": "1.1.1",
+				"statuses": "~1.5.0",
+				"type-is": "~1.6.18",
+				"utils-merge": "1.0.1",
+				"vary": "~1.1.2"
+			},
+			"dependencies": {
+				"debug": {
+					"version": "2.6.9",
+					"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+					"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+					"requires": {
+						"ms": "2.0.0"
+					}
+				},
+				"ms": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+					"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+				}
+			}
+		},
+		"express-mysql-session": {
+			"version": "2.1.8",
+			"resolved": "https://registry.npmjs.org/express-mysql-session/-/express-mysql-session-2.1.8.tgz",
+			"integrity": "sha512-EnFdhG6RNmC/gZpC+mmcGKZeX8kcp5Lh90ozFTa8LNJmHrGWzqOt8564IvnTw5c4d3/Aj0EAXnpGuzLCoKzcig==",
+			"requires": {
+				"debug": "4.3.4",
+				"express-session": "1.17.2",
+				"mysql": "2.18.1",
+				"underscore": "1.13.3"
+			},
+			"dependencies": {
+				"cookie": {
+					"version": "0.4.1",
+					"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+					"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
+				},
+				"depd": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+					"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+				},
+				"express-session": {
+					"version": "1.17.2",
+					"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz",
+					"integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==",
+					"requires": {
+						"cookie": "0.4.1",
+						"cookie-signature": "1.0.6",
+						"debug": "2.6.9",
+						"depd": "~2.0.0",
+						"on-headers": "~1.0.2",
+						"parseurl": "~1.3.3",
+						"safe-buffer": "5.2.1",
+						"uid-safe": "~2.1.5"
+					},
+					"dependencies": {
+						"debug": {
+							"version": "2.6.9",
+							"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+							"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+							"requires": {
+								"ms": "2.0.0"
+							}
+						}
+					}
+				},
+				"ms": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+					"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+				},
+				"safe-buffer": {
+					"version": "5.2.1",
+					"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+					"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+				}
+			}
+		},
+		"express-session": {
+			"version": "1.17.3",
+			"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
+			"integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
+			"requires": {
+				"cookie": "0.4.2",
+				"cookie-signature": "1.0.6",
+				"debug": "2.6.9",
+				"depd": "~2.0.0",
+				"on-headers": "~1.0.2",
+				"parseurl": "~1.3.3",
+				"safe-buffer": "5.2.1",
+				"uid-safe": "~2.1.5"
+			},
+			"dependencies": {
+				"cookie": {
+					"version": "0.4.2",
+					"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+					"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
+				},
+				"debug": {
+					"version": "2.6.9",
+					"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+					"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+					"requires": {
+						"ms": "2.0.0"
+					}
+				},
+				"depd": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+					"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+				},
+				"ms": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+					"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+				},
+				"safe-buffer": {
+					"version": "5.2.1",
+					"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+					"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+				}
+			}
+		},
+		"extend": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+			"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+		},
+		"fast-csv": {
+			"version": "4.3.6",
+			"resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz",
+			"integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==",
+			"requires": {
+				"@fast-csv/format": "4.3.5",
+				"@fast-csv/parse": "4.3.6"
+			}
+		},
+		"fast-json-patch": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.0.tgz",
+			"integrity": "sha512-IhpytlsVTRndz0hU5t0/MGzS/etxLlfrpG5V5M9mVbuj9TrJLWaMfsox9REM5rkuGX0T+5qjpe8XA1o0gZ42nA=="
+		},
+		"fast-levenshtein": {
+			"version": "2.0.6",
+			"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+			"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
+		},
+		"fast-printf": {
+			"version": "1.6.6",
+			"resolved": "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.6.tgz",
+			"integrity": "sha512-Uz/uW6R1Fd8YqCGeoQosRIfB4dBbr8uMbFVdEci2AyXYcfucFqhpSMAGs8skRRdZd+MGCDBu48+B8Zmu7Pta5A==",
+			"requires": {
+				"boolean": "^3.0.2"
+			}
+		},
+		"fast-safe-stringify": {
+			"version": "2.0.7",
+			"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
+			"integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
+		},
+		"fast-text-encoding": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz",
+			"integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig=="
+		},
+		"fclone": {
+			"version": "1.0.11",
+			"resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz",
+			"integrity": "sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA="
+		},
+		"fecha": {
+			"version": "4.2.1",
+			"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz",
+			"integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q=="
+		},
+		"file-stream-rotator": {
+			"version": "0.5.7",
+			"resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.5.7.tgz",
+			"integrity": "sha512-VYb3HZ/GiAGUCrfeakO8Mp54YGswNUHvL7P09WQcXAJNSj3iQ5QraYSp3cIn1MUyw6uzfgN/EFOarCNa4JvUHQ==",
+			"requires": {
+				"moment": "^2.11.2"
+			}
+		},
+		"file-type": {
+			"version": "3.9.0",
+			"resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
+			"integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek="
+		},
+		"file-uri-to-path": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz",
+			"integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg=="
+		},
+		"fill-range": {
+			"version": "7.0.1",
+			"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+			"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+			"requires": {
+				"to-regex-range": "^5.0.1"
+			}
+		},
+		"finalhandler": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+			"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+			"requires": {
+				"debug": "2.6.9",
+				"encodeurl": "~1.0.2",
+				"escape-html": "~1.0.3",
+				"on-finished": "~2.3.0",
+				"parseurl": "~1.3.3",
+				"statuses": "~1.5.0",
+				"unpipe": "~1.0.0"
+			},
+			"dependencies": {
+				"debug": {
+					"version": "2.6.9",
+					"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+					"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+					"requires": {
+						"ms": "2.0.0"
+					}
+				},
+				"ms": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+					"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+				}
+			}
+		},
+		"fn.name": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
+			"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
+		},
+		"follow-redirects": {
+			"version": "1.14.5",
+			"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
+			"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA=="
+		},
+		"form-data": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+			"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+			"requires": {
+				"asynckit": "^0.4.0",
+				"combined-stream": "^1.0.8",
+				"mime-types": "^2.1.12"
+			}
+		},
+		"formidable": {
+			"version": "1.2.6",
+			"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz",
+			"integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==",
+			"dev": true
+		},
+		"forwarded": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+			"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
+		},
+		"fresh": {
+			"version": "0.5.2",
+			"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+			"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+		},
+		"fs-constants": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+			"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+		},
+		"fs-extra": {
+			"version": "8.1.0",
+			"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+			"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+			"requires": {
+				"graceful-fs": "^4.2.0",
+				"jsonfile": "^4.0.0",
+				"universalify": "^0.1.0"
+			}
+		},
+		"fs-minipass": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+			"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+			"requires": {
+				"minipass": "^3.0.0"
+			}
+		},
+		"fs.realpath": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+			"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+		},
+		"fsevents": {
+			"version": "2.3.2",
+			"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+			"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+			"optional": true
+		},
+		"fstream": {
+			"version": "1.0.12",
+			"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
+			"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
+			"requires": {
+				"graceful-fs": "^4.1.2",
+				"inherits": "~2.0.0",
+				"mkdirp": ">=0.5 0",
+				"rimraf": "2"
+			},
+			"dependencies": {
+				"mkdirp": {
+					"version": "0.5.5",
+					"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+					"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+					"requires": {
+						"minimist": "^1.2.5"
+					}
+				},
+				"rimraf": {
+					"version": "2.7.1",
+					"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+					"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+					"requires": {
+						"glob": "^7.1.3"
+					}
+				}
+			}
+		},
+		"ftp": {
+			"version": "0.3.10",
+			"resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz",
+			"integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=",
+			"requires": {
+				"readable-stream": "1.1.x",
+				"xregexp": "2.0.0"
+			},
+			"dependencies": {
+				"isarray": {
+					"version": "0.0.1",
+					"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+					"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+				},
+				"readable-stream": {
+					"version": "1.1.14",
+					"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+					"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+					"requires": {
+						"core-util-is": "~1.0.0",
+						"inherits": "~2.0.1",
+						"isarray": "0.0.1",
+						"string_decoder": "~0.10.x"
+					}
+				},
+				"string_decoder": {
+					"version": "0.10.31",
+					"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+					"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+				}
+			}
+		},
+		"function-bind": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+			"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+		},
+		"gauge": {
+			"version": "2.7.4",
+			"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+			"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+			"requires": {
+				"aproba": "^1.0.3",
+				"console-control-strings": "^1.0.0",
+				"has-unicode": "^2.0.0",
+				"object-assign": "^4.1.0",
+				"signal-exit": "^3.0.0",
+				"string-width": "^1.0.1",
+				"strip-ansi": "^3.0.1",
+				"wide-align": "^1.1.0"
+			}
+		},
+		"gaxios": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.0.tgz",
+			"integrity": "sha512-pHplNbslpwCLMyII/lHPWFQbJWOX0B3R1hwBEOvzYi1GmdKZruuEHK4N9V6f7tf1EaPYyF80mui1+344p6SmLg==",
+			"requires": {
+				"abort-controller": "^3.0.0",
+				"extend": "^3.0.2",
+				"https-proxy-agent": "^5.0.0",
+				"is-stream": "^2.0.0",
+				"node-fetch": "^2.3.0"
+			}
+		},
+		"gcp-metadata": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.0.tgz",
+			"integrity": "sha512-L9XQUpvKJCM76YRSmcxrR4mFPzPGsgZUH+GgHMxAET8qc6+BhRJq63RLhWakgEO2KKVgeSDVfyiNjkGSADwNTA==",
+			"requires": {
+				"gaxios": "^4.0.0",
+				"json-bigint": "^1.0.0"
+			}
+		},
+		"generate-function": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+			"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+			"requires": {
+				"is-property": "^1.0.2"
+			}
+		},
+		"generic-pool": {
+			"version": "3.9.0",
+			"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
+			"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g=="
+		},
+		"get-caller-file": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+			"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+		},
+		"get-intrinsic": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+			"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+			"dev": true,
+			"requires": {
+				"function-bind": "^1.1.1",
+				"has": "^1.0.3",
+				"has-symbols": "^1.0.1"
+			}
+		},
+		"get-uri": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz",
+			"integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==",
+			"requires": {
+				"@tootallnate/once": "1",
+				"data-uri-to-buffer": "3",
+				"debug": "4",
+				"file-uri-to-path": "2",
+				"fs-extra": "^8.1.0",
+				"ftp": "^0.3.10"
+			}
+		},
+		"git-node-fs": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/git-node-fs/-/git-node-fs-1.0.0.tgz",
+			"integrity": "sha1-SbIV4kLr5Dqkx1Ybu6SZUhdSCA8="
+		},
+		"git-sha1": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/git-sha1/-/git-sha1-0.1.2.tgz",
+			"integrity": "sha1-WZrBkrcYdYJeE6RF86bgURjC90U="
+		},
+		"glob": {
+			"version": "7.1.7",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+			"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+			"requires": {
+				"fs.realpath": "^1.0.0",
+				"inflight": "^1.0.4",
+				"inherits": "2",
+				"minimatch": "^3.0.4",
+				"once": "^1.3.0",
+				"path-is-absolute": "^1.0.0"
+			}
+		},
+		"glob-parent": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+			"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+			"requires": {
+				"is-glob": "^4.0.1"
+			}
+		},
+		"google-auth-library": {
+			"version": "7.3.0",
+			"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.3.0.tgz",
+			"integrity": "sha512-MPeeMlnsYnoiiVFMwX3hgaS684aiXrSqKoDP+xL4Ejg4Z0qLvIeg4XsaChemyFI8ZUO7ApwDAzNtgmhWSDNh5w==",
+			"requires": {
+				"arrify": "^2.0.0",
+				"base64-js": "^1.3.0",
+				"ecdsa-sig-formatter": "^1.0.11",
+				"fast-text-encoding": "^1.0.0",
+				"gaxios": "^4.0.0",
+				"gcp-metadata": "^4.2.0",
+				"gtoken": "^5.0.4",
+				"jws": "^4.0.0",
+				"lru-cache": "^6.0.0"
+			}
+		},
+		"google-p12-pem": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.0.tgz",
+			"integrity": "sha512-JUtEHXL4DY/N+xhlm7TC3qL797RPAtk0ZGXNs3/gWyiDHYoA/8Rjes0pztkda+sZv4ej1EoO2KhWgW5V9KTrSQ==",
+			"requires": {
+				"node-forge": "^0.10.0"
+			}
+		},
+		"googleapis": {
+			"version": "89.0.0",
+			"resolved": "https://registry.npmjs.org/googleapis/-/googleapis-89.0.0.tgz",
+			"integrity": "sha512-eH91BN+6R/Mp5uulrhKWGwKAbibfDNOIu1Oq8n12aFTiqb23/A9kVdRhituYVqhRqSWLr/kv1dpFbd6n+uN+7Q==",
+			"requires": {
+				"google-auth-library": "^7.0.2",
+				"googleapis-common": "^5.0.2"
+			}
+		},
+		"googleapis-common": {
+			"version": "5.0.5",
+			"resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-5.0.5.tgz",
+			"integrity": "sha512-o2dgoW4x4fLIAN+IVAOccz3mEH8Lj1LP9c9BSSvkNJEn+U7UZh0WSr4fdH08x5VH7+sstIpd1lOYFZD0g7j4pw==",
+			"requires": {
+				"extend": "^3.0.2",
+				"gaxios": "^4.0.0",
+				"google-auth-library": "^7.0.2",
+				"qs": "^6.7.0",
+				"url-template": "^2.0.8",
+				"uuid": "^8.0.0"
+			},
+			"dependencies": {
+				"uuid": {
+					"version": "8.3.2",
+					"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+					"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
+				}
+			}
+		},
+		"graceful-fs": {
+			"version": "4.2.6",
+			"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+			"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ=="
+		},
+		"gtoken": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.0.tgz",
+			"integrity": "sha512-mCcISYiaRZrJpfqOs0QWa6lfEM/C1V9ASkzFmuz43XBb5s1Vynh+CZy1ECeeJXVGx2PRByjYzb4Y4/zr1byr0w==",
+			"requires": {
+				"gaxios": "^4.0.0",
+				"google-p12-pem": "^3.0.3",
+				"jws": "^4.0.0"
+			}
+		},
+		"has": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+			"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+			"requires": {
+				"function-bind": "^1.1.1"
+			}
+		},
+		"has-flag": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+			"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+		},
+		"has-symbols": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+			"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
+			"dev": true
+		},
+		"has-unicode": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+			"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+		},
+		"hosted-git-info": {
+			"version": "2.8.9",
+			"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+			"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
+		},
+		"html-comment-regex": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
+			"integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ=="
+		},
+		"htmlparser2": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+			"integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+			"requires": {
+				"domelementtype": "^2.0.1",
+				"domhandler": "^4.0.0",
+				"domutils": "^2.5.2",
+				"entities": "^2.0.0"
+			}
+		},
+		"http_ece": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.1.0.tgz",
+			"integrity": "sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==",
+			"requires": {
+				"urlsafe-base64": "~1.0.0"
+			}
+		},
+		"http-errors": {
+			"version": "1.7.2",
+			"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+			"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+			"requires": {
+				"depd": "~1.1.2",
+				"inherits": "2.0.3",
+				"setprototypeof": "1.1.1",
+				"statuses": ">= 1.5.0 < 2",
+				"toidentifier": "1.0.0"
+			},
+			"dependencies": {
+				"inherits": {
+					"version": "2.0.3",
+					"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+					"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+				}
+			}
+		},
+		"http-proxy-agent": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+			"integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+			"requires": {
+				"@tootallnate/once": "1",
+				"agent-base": "6",
+				"debug": "4"
+			}
+		},
+		"https-proxy-agent": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+			"integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+			"requires": {
+				"agent-base": "6",
+				"debug": "4"
+			}
+		},
+		"iconv-lite": {
+			"version": "0.6.3",
+			"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+			"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+			"requires": {
+				"safer-buffer": ">= 2.1.2 < 3.0.0"
+			}
+		},
+		"ieee754": {
+			"version": "1.1.13",
+			"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+			"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+		},
+		"ignore-by-default": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+			"integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
+			"dev": true
+		},
+		"immediate": {
+			"version": "3.0.6",
+			"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+			"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
+		},
+		"inflight": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+			"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+			"requires": {
+				"once": "^1.3.0",
+				"wrappy": "1"
+			}
+		},
+		"inherits": {
+			"version": "2.0.4",
+			"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+			"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+		},
+		"ini": {
+			"version": "1.3.7",
+			"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz",
+			"integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ=="
+		},
+		"ip": {
+			"version": "1.1.5",
+			"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+			"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
+		},
+		"ipaddr.js": {
+			"version": "1.9.1",
+			"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+			"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+		},
+		"is-arrayish": {
+			"version": "0.2.1",
+			"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+			"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+		},
+		"is-binary-path": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+			"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+			"requires": {
+				"binary-extensions": "^2.0.0"
+			}
+		},
+		"is-core-module": {
+			"version": "2.4.0",
+			"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
+			"integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==",
+			"requires": {
+				"has": "^1.0.3"
+			}
+		},
+		"is-extglob": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+			"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
+		},
+		"is-fullwidth-code-point": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+			"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+			"requires": {
+				"number-is-nan": "^1.0.0"
+			}
+		},
+		"is-glob": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+			"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+			"requires": {
+				"is-extglob": "^2.1.1"
+			}
+		},
+		"is-number": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+			"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
+		},
+		"is-property": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+			"integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
+		},
+		"is-stream": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
+			"integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw=="
+		},
+		"isarray": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+			"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+		},
+		"jmespath": {
+			"version": "0.16.0",
+			"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
+			"integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw=="
+		},
+		"js-git": {
+			"version": "0.7.8",
+			"resolved": "https://registry.npmjs.org/js-git/-/js-git-0.7.8.tgz",
+			"integrity": "sha1-UvplWrYYd9bxB578ZTS1VPMeVEQ=",
+			"requires": {
+				"bodec": "^0.1.0",
+				"culvert": "^0.1.2",
+				"git-sha1": "^0.1.2",
+				"pako": "^0.2.5"
+			}
+		},
+		"js-tokens": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+			"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+		},
+		"js-yaml": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+			"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+			"dev": true,
+			"requires": {
+				"argparse": "^2.0.1"
+			},
+			"dependencies": {
+				"argparse": {
+					"version": "2.0.1",
+					"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+					"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+					"dev": true
+				}
+			}
+		},
+		"json-bigint": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+			"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+			"requires": {
+				"bignumber.js": "^9.0.0"
+			}
+		},
+		"json-parse-even-better-errors": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+			"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+		},
+		"json-stringify-safe": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+			"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+			"optional": true
+		},
+		"jsonfile": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+			"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+			"requires": {
+				"graceful-fs": "^4.1.6"
+			}
+		},
+		"jsonwebtoken": {
+			"version": "8.5.1",
+			"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+			"integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+			"requires": {
+				"jws": "^3.2.2",
+				"lodash.includes": "^4.3.0",
+				"lodash.isboolean": "^3.0.3",
+				"lodash.isinteger": "^4.0.4",
+				"lodash.isnumber": "^3.0.3",
+				"lodash.isplainobject": "^4.0.6",
+				"lodash.isstring": "^4.0.1",
+				"lodash.once": "^4.0.0",
+				"ms": "^2.1.1",
+				"semver": "^5.6.0"
+			},
+			"dependencies": {
+				"jwa": {
+					"version": "1.4.1",
+					"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+					"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+					"requires": {
+						"buffer-equal-constant-time": "1.0.1",
+						"ecdsa-sig-formatter": "1.0.11",
+						"safe-buffer": "^5.0.1"
+					}
+				},
+				"jws": {
+					"version": "3.2.2",
+					"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+					"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+					"requires": {
+						"jwa": "^1.4.1",
+						"safe-buffer": "^5.0.1"
+					}
+				},
+				"semver": {
+					"version": "5.7.1",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+					"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+				}
+			}
+		},
+		"jszip": {
+			"version": "3.7.1",
+			"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
+			"integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
+			"requires": {
+				"lie": "~3.3.0",
+				"pako": "~1.0.2",
+				"readable-stream": "~2.3.6",
+				"set-immediate-shim": "~1.0.1"
+			},
+			"dependencies": {
+				"pako": {
+					"version": "1.0.11",
+					"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+					"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+				}
+			}
+		},
+		"jwa": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+			"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+			"requires": {
+				"buffer-equal-constant-time": "1.0.1",
+				"ecdsa-sig-formatter": "1.0.11",
+				"safe-buffer": "^5.0.1"
+			}
+		},
+		"jws": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+			"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+			"requires": {
+				"jwa": "^2.0.0",
+				"safe-buffer": "^5.0.1"
+			}
+		},
+		"kuler": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+			"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
+		},
+		"lazy": {
+			"version": "1.0.11",
+			"resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz",
+			"integrity": "sha1-2qBoIGKCVCwIgojpdcKXwa53tpA="
+		},
+		"lazystream": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
+			"integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
+			"requires": {
+				"readable-stream": "^2.0.5"
+			}
+		},
+		"levn": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+			"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+			"requires": {
+				"prelude-ls": "~1.1.2",
+				"type-check": "~0.3.2"
+			}
+		},
+		"lie": {
+			"version": "3.3.0",
+			"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+			"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+			"requires": {
+				"immediate": "~3.0.5"
+			}
+		},
+		"lines-and-columns": {
+			"version": "1.1.6",
+			"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
+			"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
+		},
+		"listenercount": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz",
+			"integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc="
+		},
+		"lodash": {
+			"version": "4.17.21",
+			"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+			"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+		},
+		"lodash.defaults": {
+			"version": "4.2.0",
+			"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+			"integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
+		},
+		"lodash.difference": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
+			"integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
+		},
+		"lodash.escaperegexp": {
+			"version": "4.1.2",
+			"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
+			"integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c="
+		},
+		"lodash.flatten": {
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+			"integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
+		},
+		"lodash.get": {
+			"version": "4.4.2",
+			"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+			"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
+			"dev": true
+		},
+		"lodash.groupby": {
+			"version": "4.6.0",
+			"resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz",
+			"integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E="
+		},
+		"lodash.includes": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+			"integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+		},
+		"lodash.isboolean": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+			"integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+		},
+		"lodash.isequal": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+			"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+		},
+		"lodash.isfunction": {
+			"version": "3.0.9",
+			"resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
+			"integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw=="
+		},
+		"lodash.isinteger": {
+			"version": "4.0.4",
+			"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+			"integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+		},
+		"lodash.isnil": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz",
+			"integrity": "sha1-SeKM1VkBNFjIFMVHnTxmOiG/qmw="
+		},
+		"lodash.isnumber": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+			"integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+		},
+		"lodash.isplainobject": {
+			"version": "4.0.6",
+			"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+			"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+		},
+		"lodash.isstring": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+			"integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+		},
+		"lodash.isundefined": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz",
+			"integrity": "sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g="
+		},
+		"lodash.mergewith": {
+			"version": "4.6.2",
+			"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+			"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+			"dev": true
+		},
+		"lodash.once": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+			"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+		},
+		"lodash.union": {
+			"version": "4.6.0",
+			"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
+			"integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
+		},
+		"lodash.uniq": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+			"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
+		},
+		"log-driver": {
+			"version": "1.2.7",
+			"resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz",
+			"integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg=="
+		},
+		"logform": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz",
+			"integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==",
+			"requires": {
+				"colors": "^1.2.1",
+				"fast-safe-stringify": "^2.0.4",
+				"fecha": "^4.2.0",
+				"ms": "^2.1.1",
+				"triple-beam": "^1.3.0"
+			}
+		},
+		"long": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+			"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+		},
+		"lru_map": {
+			"version": "0.3.3",
+			"resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
+			"integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ=="
+		},
+		"lru-cache": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+			"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+			"requires": {
+				"yallist": "^4.0.0"
+			}
+		},
+		"make-dir": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+			"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+			"requires": {
+				"semver": "^6.0.0"
+			},
+			"dependencies": {
+				"semver": {
+					"version": "6.3.0",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+					"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+				}
+			}
+		},
+		"media-typer": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+			"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+		},
+		"merge-descriptors": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+			"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+		},
+		"method-override": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz",
+			"integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==",
+			"requires": {
+				"debug": "3.1.0",
+				"methods": "~1.1.2",
+				"parseurl": "~1.3.2",
+				"vary": "~1.1.2"
+			},
+			"dependencies": {
+				"debug": {
+					"version": "3.1.0",
+					"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+					"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+					"requires": {
+						"ms": "2.0.0"
+					}
+				},
+				"ms": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+					"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+				}
+			}
+		},
+		"methods": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+			"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+		},
+		"mime": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+			"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+		},
+		"mime-db": {
+			"version": "1.48.0",
+			"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz",
+			"integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ=="
+		},
+		"mime-types": {
+			"version": "2.1.31",
+			"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz",
+			"integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==",
+			"requires": {
+				"mime-db": "1.48.0"
+			}
+		},
+		"minimalistic-assert": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+			"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+		},
+		"minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"requires": {
+				"brace-expansion": "^1.1.7"
+			}
+		},
+		"minimist": {
+			"version": "1.2.5",
+			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+			"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+		},
+		"minipass": {
+			"version": "3.1.3",
+			"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
+			"integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
+			"requires": {
+				"yallist": "^4.0.0"
+			}
+		},
+		"minizlib": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+			"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+			"requires": {
+				"minipass": "^3.0.0",
+				"yallist": "^4.0.0"
+			}
+		},
+		"mkdirp": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+			"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
+		},
+		"module-details-from-path": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
+			"integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is="
+		},
+		"moment": {
+			"version": "2.29.1",
+			"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
+			"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
+		},
+		"moment-timezone": {
+			"version": "0.5.33",
+			"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz",
+			"integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==",
+			"requires": {
+				"moment": ">= 2.9.0"
+			}
+		},
+		"ms": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+			"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+		},
+		"multer": {
+			"version": "1.4.3",
+			"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.3.tgz",
+			"integrity": "sha512-np0YLKncuZoTzufbkM6wEKp68EhWJXcU6fq6QqrSwkckd2LlMgd1UqhUJLj6NS/5sZ8dE8LYDWslsltJznnXlg==",
+			"requires": {
+				"append-field": "^1.0.0",
+				"busboy": "^0.2.11",
+				"concat-stream": "^1.5.2",
+				"mkdirp": "^0.5.4",
+				"object-assign": "^4.1.1",
+				"on-finished": "^2.3.0",
+				"type-is": "^1.6.4",
+				"xtend": "^4.0.0"
+			},
+			"dependencies": {
+				"mkdirp": {
+					"version": "0.5.5",
+					"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+					"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+					"requires": {
+						"minimist": "^1.2.5"
+					}
+				}
+			}
+		},
+		"multer-s3": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/multer-s3/-/multer-s3-2.10.0.tgz",
+			"integrity": "sha512-RZsiqG19C9gE82lB7v8duJ+TMIf70fWYHlIwuNcsanOH1ePBoPXZvboEQxEow9jUkk7WQsuyVA2TgriOuDrVrw==",
+			"requires": {
+				"file-type": "^3.3.0",
+				"html-comment-regex": "^1.1.2",
+				"run-parallel": "^1.1.6"
+			}
+		},
+		"mute-stream": {
+			"version": "0.0.8",
+			"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+			"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
+		},
+		"mysql": {
+			"version": "2.18.1",
+			"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
+			"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
+			"requires": {
+				"bignumber.js": "9.0.0",
+				"readable-stream": "2.3.7",
+				"safe-buffer": "5.1.2",
+				"sqlstring": "2.3.1"
+			},
+			"dependencies": {
+				"bignumber.js": {
+					"version": "9.0.0",
+					"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
+					"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="
+				},
+				"sqlstring": {
+					"version": "2.3.1",
+					"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
+					"integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ=="
+				}
+			}
+		},
+		"mysql2": {
+			"version": "2.2.5",
+			"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.2.5.tgz",
+			"integrity": "sha512-XRqPNxcZTpmFdXbJqb+/CtYVLCx14x1RTeNMD4954L331APu75IC74GDqnZMEt1kwaXy6TySo55rF2F3YJS78g==",
+			"requires": {
+				"denque": "^1.4.1",
+				"generate-function": "^2.3.1",
+				"iconv-lite": "^0.6.2",
+				"long": "^4.0.0",
+				"lru-cache": "^6.0.0",
+				"named-placeholders": "^1.1.2",
+				"seq-queue": "^0.0.5",
+				"sqlstring": "^2.3.2"
+			},
+			"dependencies": {
+				"iconv-lite": {
+					"version": "0.6.3",
+					"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+					"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+					"requires": {
+						"safer-buffer": ">= 2.1.2 < 3.0.0"
+					}
+				}
+			}
+		},
+		"named-placeholders": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz",
+			"integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==",
+			"requires": {
+				"lru-cache": "^4.1.3"
+			},
+			"dependencies": {
+				"lru-cache": {
+					"version": "4.1.5",
+					"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+					"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+					"requires": {
+						"pseudomap": "^1.0.2",
+						"yallist": "^2.1.2"
+					}
+				},
+				"yallist": {
+					"version": "2.1.2",
+					"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+					"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+				}
+			}
+		},
+		"needle": {
+			"version": "2.4.0",
+			"resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz",
+			"integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==",
+			"requires": {
+				"debug": "^3.2.6",
+				"iconv-lite": "^0.4.4",
+				"sax": "^1.2.4"
+			},
+			"dependencies": {
+				"debug": {
+					"version": "3.2.7",
+					"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+					"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+					"requires": {
+						"ms": "^2.1.1"
+					}
+				},
+				"iconv-lite": {
+					"version": "0.4.24",
+					"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+					"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+					"requires": {
+						"safer-buffer": ">= 2.1.2 < 3"
+					}
+				}
+			}
+		},
+		"negotiator": {
+			"version": "0.6.2",
+			"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+			"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+		},
+		"netmask": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
+			"integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="
+		},
+		"node-addon-api": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
+			"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
+		},
+		"node-cache": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz",
+			"integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==",
+			"requires": {
+				"clone": "2.x"
+			}
+		},
+		"node-cron": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.0.tgz",
+			"integrity": "sha512-DDwIvvuCwrNiaU7HEivFDULcaQualDv7KoNlB/UU1wPW0n1tDEmBJKhEIE6DlF2FuoOHcNbLJ8ITL2Iv/3AWmA==",
+			"requires": {
+				"moment-timezone": "^0.5.31"
+			}
+		},
+		"node-fetch": {
+			"version": "2.6.1",
+			"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+			"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
+		},
+		"node-forge": {
+			"version": "0.10.0",
+			"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+			"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
+		},
+		"node-sens": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/node-sens/-/node-sens-1.3.0.tgz",
+			"integrity": "sha512-d89Z5mDBLaMfmQYcrIsHcPbKf568cX+ajpOjDywQVyVDhgRxzz5dyGAuEI/1HCB5Fi71eIALsylkxSJ17NTlBA==",
+			"requires": {
+				"axios": "^0.19.2",
+				"crypto": "^1.0.1"
+			},
+			"dependencies": {
+				"axios": {
+					"version": "0.19.2",
+					"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
+					"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
+					"requires": {
+						"follow-redirects": "1.5.10"
+					}
+				},
+				"debug": {
+					"version": "3.1.0",
+					"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+					"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+					"requires": {
+						"ms": "2.0.0"
+					}
+				},
+				"follow-redirects": {
+					"version": "1.5.10",
+					"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+					"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+					"requires": {
+						"debug": "=3.1.0"
+					}
+				},
+				"ms": {
+					"version": "2.0.0",
+					"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+					"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+				}
+			}
+		},
+		"nodemailer": {
+			"version": "6.6.2",
+			"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.2.tgz",
+			"integrity": "sha512-YSzu7TLbI+bsjCis/TZlAXBoM4y93HhlIgo0P5oiA2ua9Z4k+E2Fod//ybIzdJxOlXGRcHIh/WaeCBehvxZb/Q=="
+		},
+		"nodemon": {
+			"version": "2.0.20",
+			"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz",
+			"integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==",
+			"dev": true,
+			"requires": {
+				"chokidar": "^3.5.2",
+				"debug": "^3.2.7",
+				"ignore-by-default": "^1.0.1",
+				"minimatch": "^3.1.2",
+				"pstree.remy": "^1.1.8",
+				"semver": "^5.7.1",
+				"simple-update-notifier": "^1.0.7",
+				"supports-color": "^5.5.0",
+				"touch": "^3.1.0",
+				"undefsafe": "^2.0.5"
+			},
+			"dependencies": {
+				"debug": {
+					"version": "3.2.7",
+					"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+					"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+					"dev": true,
+					"requires": {
+						"ms": "^2.1.1"
+					}
+				},
+				"has-flag": {
+					"version": "3.0.0",
+					"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+					"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+					"dev": true
+				},
+				"semver": {
+					"version": "5.7.1",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+					"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+					"dev": true
+				},
+				"supports-color": {
+					"version": "5.5.0",
+					"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+					"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+					"dev": true,
+					"requires": {
+						"has-flag": "^3.0.0"
+					}
+				}
+			}
+		},
+		"nopt": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+			"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+			"requires": {
+				"abbrev": "1"
+			}
+		},
+		"normalize-package-data": {
+			"version": "2.5.0",
+			"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+			"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+			"requires": {
+				"hosted-git-info": "^2.1.4",
+				"resolve": "^1.10.0",
+				"semver": "2 || 3 || 4 || 5",
+				"validate-npm-package-license": "^3.0.1"
+			},
+			"dependencies": {
+				"semver": {
+					"version": "5.7.1",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+					"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+				}
+			}
+		},
+		"normalize-path": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+			"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
+		},
+		"npmlog": {
+			"version": "4.1.2",
+			"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+			"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+			"requires": {
+				"are-we-there-yet": "~1.1.2",
+				"console-control-strings": "~1.1.0",
+				"gauge": "~2.7.3",
+				"set-blocking": "~2.0.0"
+			}
+		},
+		"nssocket": {
+			"version": "0.6.0",
+			"resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.6.0.tgz",
+			"integrity": "sha1-Wflvb/MhVm8zxw99vu7N/cBxVPo=",
+			"requires": {
+				"eventemitter2": "~0.4.14",
+				"lazy": "~1.0.11"
+			},
+			"dependencies": {
+				"eventemitter2": {
+					"version": "0.4.14",
+					"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
+					"integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas="
+				}
+			}
+		},
+		"nth-check": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+			"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+			"requires": {
+				"boolbase": "^1.0.0"
+			}
+		},
+		"number-is-nan": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+			"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+		},
+		"oauth": {
+			"version": "0.9.15",
+			"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
+			"integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE="
+		},
+		"object-assign": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+			"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+		},
+		"object-hash": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
+			"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="
+		},
+		"object-inspect": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz",
+			"integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==",
+			"dev": true
+		},
+		"on-finished": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+			"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+			"requires": {
+				"ee-first": "1.1.1"
+			}
+		},
+		"on-headers": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+			"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
+		},
+		"once": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+			"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+			"requires": {
+				"wrappy": "1"
+			}
+		},
+		"one-time": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
+			"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+			"requires": {
+				"fn.name": "1.x.x"
+			}
+		},
+		"openapi-types": {
+			"version": "12.0.0",
+			"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.0.0.tgz",
+			"integrity": "sha512-6Wd9k8nmGQHgCbehZCP6wwWcfXcvinhybUTBatuhjRsCxUIujuYFZc9QnGeae75CyHASewBtxs0HX/qwREReUw==",
+			"dev": true,
+			"peer": true
+		},
+		"optionator": {
+			"version": "0.8.3",
+			"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+			"integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+			"requires": {
+				"deep-is": "~0.1.3",
+				"fast-levenshtein": "~2.0.6",
+				"levn": "~0.3.0",
+				"prelude-ls": "~1.1.2",
+				"type-check": "~0.3.2",
+				"word-wrap": "~1.2.3"
+			}
+		},
+		"pac-proxy-agent": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz",
+			"integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==",
+			"requires": {
+				"@tootallnate/once": "1",
+				"agent-base": "6",
+				"debug": "4",
+				"get-uri": "3",
+				"http-proxy-agent": "^4.0.1",
+				"https-proxy-agent": "5",
+				"pac-resolver": "^5.0.0",
+				"raw-body": "^2.2.0",
+				"socks-proxy-agent": "5"
+			}
+		},
+		"pac-resolver": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.0.tgz",
+			"integrity": "sha512-H+/A6KitiHNNW+bxBKREk2MCGSxljfqRX76NjummWEYIat7ldVXRU3dhRIE3iXZ0nvGBk6smv3nntxKkzRL8NA==",
+			"requires": {
+				"degenerator": "^3.0.1",
+				"ip": "^1.1.5",
+				"netmask": "^2.0.1"
+			}
+		},
+		"pako": {
+			"version": "0.2.9",
+			"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+			"integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU="
+		},
+		"parse-json": {
+			"version": "5.2.0",
+			"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+			"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+			"requires": {
+				"@babel/code-frame": "^7.0.0",
+				"error-ex": "^1.3.1",
+				"json-parse-even-better-errors": "^2.3.0",
+				"lines-and-columns": "^1.1.6"
+			}
+		},
+		"parse5": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+			"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
+		},
+		"parse5-htmlparser2-tree-adapter": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+			"integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+			"requires": {
+				"parse5": "^6.0.1"
+			}
+		},
+		"parseurl": {
+			"version": "1.3.3",
+			"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+			"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+		},
+		"passport": {
+			"version": "0.4.1",
+			"resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz",
+			"integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==",
+			"requires": {
+				"passport-strategy": "1.x.x",
+				"pause": "0.0.1"
+			}
+		},
+		"passport-kakao": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz",
+			"integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==",
+			"requires": {
+				"passport-oauth2": "~1.1.2",
+				"pkginfo": "~0.3.0"
+			}
+		},
+		"passport-oauth2": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz",
+			"integrity": "sha1-vXFjsbYJA3GGjcTvb58uHkzEuUg=",
+			"requires": {
+				"oauth": "0.9.x",
+				"passport-strategy": "1.x.x",
+				"uid2": "0.0.x"
+			}
+		},
+		"passport-strategy": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
+			"integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
+		},
+		"path-is-absolute": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+			"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+		},
+		"path-parse": {
+			"version": "1.0.7",
+			"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+			"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+		},
+		"path-to-regexp": {
+			"version": "0.1.7",
+			"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+			"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+		},
+		"pause": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+			"integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
+		},
+		"picomatch": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
+			"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw=="
+		},
+		"pidusage": {
+			"version": "2.0.21",
+			"resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz",
+			"integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==",
+			"requires": {
+				"safe-buffer": "^5.2.1"
+			},
+			"dependencies": {
+				"safe-buffer": {
+					"version": "5.2.1",
+					"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+					"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+				}
+			}
+		},
+		"pkginfo": {
+			"version": "0.3.1",
+			"resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz",
+			"integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE="
+		},
+		"pm2": {
+			"version": "5.1.0",
+			"resolved": "https://registry.npmjs.org/pm2/-/pm2-5.1.0.tgz",
+			"integrity": "sha512-reJ35NOxM4+g7H0enW47HJsp32CszKkseCojAuUMUkffyXsGDKBMnDqhxAZMZKtHUUjl0cWFEqKehqB0ODH8Kw==",
+			"requires": {
+				"@pm2/agent": "~2.0.0",
+				"@pm2/io": "~5.0.0",
+				"@pm2/js-api": "~0.6.7",
+				"@pm2/pm2-version-check": "latest",
+				"async": "~3.2.0",
+				"blessed": "0.1.81",
+				"chalk": "3.0.0",
+				"chokidar": "^3.5.1",
+				"cli-tableau": "^2.0.0",
+				"commander": "2.15.1",
+				"cron": "1.8.2",
+				"dayjs": "~1.8.25",
+				"debug": "^4.3.1",
+				"enquirer": "2.3.6",
+				"eventemitter2": "5.0.1",
+				"fast-printf": "^1.3.0",
+				"fclone": "1.0.11",
+				"mkdirp": "1.0.4",
+				"needle": "2.4.0",
+				"pidusage": "2.0.21",
+				"pm2-axon": "~4.0.1",
+				"pm2-axon-rpc": "~0.7.1",
+				"pm2-deploy": "~1.0.2",
+				"pm2-multimeter": "^0.1.2",
+				"pm2-sysmonit": "^1.2.8",
+				"promptly": "^2",
+				"semver": "^7.2",
+				"source-map-support": "0.5.19",
+				"vizion": "~2.2.1",
+				"yamljs": "0.3.0"
+			},
+			"dependencies": {
+				"@pm2/pm2-version-check": {
+					"version": "1.0.4",
+					"resolved": "https://registry.npmjs.org/@pm2/pm2-version-check/-/pm2-version-check-1.0.4.tgz",
+					"integrity": "sha512-SXsM27SGH3yTWKc2fKR4SYNxsmnvuBQ9dd6QHtEWmiZ/VqaOYPAIlS8+vMcn27YLtAEBGvNRSh3TPNvtjZgfqA==",
+					"requires": {
+						"debug": "^4.3.1"
+					}
+				},
+				"chalk": {
+					"version": "3.0.0",
+					"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+					"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+					"requires": {
+						"ansi-styles": "^4.1.0",
+						"supports-color": "^7.1.0"
+					}
+				},
+				"supports-color": {
+					"version": "7.2.0",
+					"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+					"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+					"requires": {
+						"has-flag": "^4.0.0"
+					}
+				}
+			}
+		},
+		"pm2-axon": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/pm2-axon/-/pm2-axon-4.0.1.tgz",
+			"integrity": "sha512-kES/PeSLS8orT8dR5jMlNl+Yu4Ty3nbvZRmaAtROuVm9nYYGiaoXqqKQqQYzWQzMYWUKHMQTvBlirjE5GIIxqg==",
+			"requires": {
+				"amp": "~0.3.1",
+				"amp-message": "~0.1.1",
+				"debug": "^4.3.1",
+				"escape-string-regexp": "^4.0.0"
+			},
+			"dependencies": {
+				"escape-string-regexp": {
+					"version": "4.0.0",
+					"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+					"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
+				}
+			}
+		},
+		"pm2-axon-rpc": {
+			"version": "0.7.1",
+			"resolved": "https://registry.npmjs.org/pm2-axon-rpc/-/pm2-axon-rpc-0.7.1.tgz",
+			"integrity": "sha512-FbLvW60w+vEyvMjP/xom2UPhUN/2bVpdtLfKJeYM3gwzYhoTEEChCOICfFzxkxuoEleOlnpjie+n1nue91bDQw==",
+			"requires": {
+				"debug": "^4.3.1"
+			}
+		},
+		"pm2-deploy": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/pm2-deploy/-/pm2-deploy-1.0.2.tgz",
+			"integrity": "sha512-YJx6RXKrVrWaphEYf++EdOOx9EH18vM8RSZN/P1Y+NokTKqYAca/ejXwVLyiEpNju4HPZEk3Y2uZouwMqUlcgg==",
+			"requires": {
+				"run-series": "^1.1.8",
+				"tv4": "^1.3.0"
+			}
+		},
+		"pm2-multimeter": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz",
+			"integrity": "sha1-Gh5VFT1BoFU0zqI8/oYKuqDrSs4=",
+			"requires": {
+				"charm": "~0.1.1"
+			}
+		},
+		"pm2-sysmonit": {
+			"version": "1.2.8",
+			"resolved": "https://registry.npmjs.org/pm2-sysmonit/-/pm2-sysmonit-1.2.8.tgz",
+			"integrity": "sha512-ACOhlONEXdCTVwKieBIQLSi2tQZ8eKinhcr9JpZSUAL8Qy0ajIgRtsLxG/lwPOW3JEKqPyw/UaHmTWhUzpP4kA==",
+			"optional": true,
+			"requires": {
+				"async": "^3.2.0",
+				"debug": "^4.3.1",
+				"pidusage": "^2.0.21",
+				"systeminformation": "^5.7",
+				"tx2": "~1.0.4"
+			}
+		},
+		"prelude-ls": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+			"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
+		},
+		"printj": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
+			"integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ=="
+		},
+		"process-nextick-args": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+			"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+		},
+		"promptly": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/promptly/-/promptly-2.2.0.tgz",
+			"integrity": "sha1-KhP6BjaIoqWYOxYf/wEIoH0m/HQ=",
+			"requires": {
+				"read": "^1.0.4"
+			}
+		},
+		"proxy-addr": {
+			"version": "2.0.7",
+			"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+			"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+			"requires": {
+				"forwarded": "0.2.0",
+				"ipaddr.js": "1.9.1"
+			}
+		},
+		"proxy-agent": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz",
+			"integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==",
+			"requires": {
+				"agent-base": "^6.0.0",
+				"debug": "4",
+				"http-proxy-agent": "^4.0.0",
+				"https-proxy-agent": "^5.0.0",
+				"lru-cache": "^5.1.1",
+				"pac-proxy-agent": "^5.0.0",
+				"proxy-from-env": "^1.0.0",
+				"socks-proxy-agent": "^5.0.0"
+			},
+			"dependencies": {
+				"lru-cache": {
+					"version": "5.1.1",
+					"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+					"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+					"requires": {
+						"yallist": "^3.0.2"
+					}
+				},
+				"yallist": {
+					"version": "3.1.1",
+					"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+					"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+				}
+			}
+		},
+		"proxy-from-env": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+			"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+		},
+		"pseudomap": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+			"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+		},
+		"pstree.remy": {
+			"version": "1.1.8",
+			"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+			"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+			"dev": true
+		},
+		"punycode": {
+			"version": "1.3.2",
+			"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+			"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
+		},
+		"qs": {
+			"version": "6.7.0",
+			"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+			"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+		},
+		"querystring": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+			"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
+		},
+		"queue-microtask": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+			"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
+		},
+		"random-bytes": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
+			"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
+		},
+		"range-parser": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+			"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+		},
+		"raw-body": {
+			"version": "2.4.0",
+			"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+			"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+			"requires": {
+				"bytes": "3.1.0",
+				"http-errors": "1.7.2",
+				"iconv-lite": "0.4.24",
+				"unpipe": "1.0.0"
+			},
+			"dependencies": {
+				"iconv-lite": {
+					"version": "0.4.24",
+					"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+					"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+					"requires": {
+						"safer-buffer": ">= 2.1.2 < 3"
+					}
+				}
+			}
+		},
+		"read": {
+			"version": "1.0.7",
+			"resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
+			"integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
+			"requires": {
+				"mute-stream": "~0.0.4"
+			}
+		},
+		"read-pkg": {
+			"version": "5.2.0",
+			"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
+			"integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
+			"requires": {
+				"@types/normalize-package-data": "^2.4.0",
+				"normalize-package-data": "^2.5.0",
+				"parse-json": "^5.0.0",
+				"type-fest": "^0.6.0"
+			}
+		},
+		"readable-stream": {
+			"version": "2.3.7",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+			"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+			"requires": {
+				"core-util-is": "~1.0.0",
+				"inherits": "~2.0.3",
+				"isarray": "~1.0.0",
+				"process-nextick-args": "~2.0.0",
+				"safe-buffer": "~5.1.1",
+				"string_decoder": "~1.1.1",
+				"util-deprecate": "~1.0.1"
+			}
+		},
+		"readdir-glob": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.1.tgz",
+			"integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==",
+			"requires": {
+				"minimatch": "^3.0.4"
+			}
+		},
+		"readdirp": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+			"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+			"requires": {
+				"picomatch": "^2.2.1"
+			}
+		},
+		"readline": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz",
+			"integrity": "sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw="
+		},
+		"redis": {
+			"version": "4.5.1",
+			"resolved": "https://registry.npmjs.org/redis/-/redis-4.5.1.tgz",
+			"integrity": "sha512-oxXSoIqMJCQVBTfxP6BNTCtDMyh9G6Vi5wjdPdV/sRKkufyZslDqCScSGcOr6XGR/reAWZefz7E4leM31RgdBA==",
+			"requires": {
+				"@redis/bloom": "1.1.0",
+				"@redis/client": "1.4.2",
+				"@redis/graph": "1.1.0",
+				"@redis/json": "1.0.4",
+				"@redis/search": "1.1.0",
+				"@redis/time-series": "1.0.4"
+			}
+		},
+		"regex-email": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/regex-email/-/regex-email-1.0.2.tgz",
+			"integrity": "sha1-1nKFy9FvML5e+vaEF1wVPwy8gSE="
+		},
+		"require-directory": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+			"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+		},
+		"require-in-the-middle": {
+			"version": "5.1.0",
+			"resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.1.0.tgz",
+			"integrity": "sha512-M2rLKVupQfJ5lf9OvqFGIT+9iVLnTmjgbOmpil12hiSQNn5zJTKGPoIisETNjfK+09vP3rpm1zJajmErpr2sEQ==",
+			"requires": {
+				"debug": "^4.1.1",
+				"module-details-from-path": "^1.0.3",
+				"resolve": "^1.12.0"
+			}
+		},
+		"resolve": {
+			"version": "1.20.0",
+			"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+			"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+			"requires": {
+				"is-core-module": "^2.2.0",
+				"path-parse": "^1.0.6"
+			}
+		},
+		"rimraf": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+			"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+			"requires": {
+				"glob": "^7.1.3"
+			}
+		},
+		"run-parallel": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+			"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+			"requires": {
+				"queue-microtask": "^1.2.2"
+			}
+		},
+		"run-series": {
+			"version": "1.1.9",
+			"resolved": "https://registry.npmjs.org/run-series/-/run-series-1.1.9.tgz",
+			"integrity": "sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g=="
+		},
+		"rxjs": {
+			"version": "6.6.7",
+			"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+			"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+			"requires": {
+				"tslib": "^1.9.0"
+			}
+		},
+		"safe-buffer": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+			"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+		},
+		"safer-buffer": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+			"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+		},
+		"sax": {
+			"version": "1.2.4",
+			"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+			"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+		},
+		"saxes": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
+			"integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
+			"requires": {
+				"xmlchars": "^2.2.0"
+			}
+		},
+		"semver": {
+			"version": "7.3.5",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+			"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+			"requires": {
+				"lru-cache": "^6.0.0"
+			}
+		},
+		"send": {
+			"version": "0.17.1",
+			"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+			"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+			"requires": {
+				"debug": "2.6.9",
+				"depd": "~1.1.2",
+				"destroy": "~1.0.4",
+				"encodeurl": "~1.0.2",
+				"escape-html": "~1.0.3",
+				"etag": "~1.8.1",
+				"fresh": "0.5.2",
+				"http-errors": "~1.7.2",
+				"mime": "1.6.0",
+				"ms": "2.1.1",
+				"on-finished": "~2.3.0",
+				"range-parser": "~1.2.1",
+				"statuses": "~1.5.0"
+			},
+			"dependencies": {
+				"debug": {
+					"version": "2.6.9",
+					"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+					"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+					"requires": {
+						"ms": "2.0.0"
+					},
+					"dependencies": {
+						"ms": {
+							"version": "2.0.0",
+							"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+							"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+						}
+					}
+				},
+				"ms": {
+					"version": "2.1.1",
+					"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+					"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+				}
+			}
+		},
+		"seq-queue": {
+			"version": "0.0.5",
+			"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+			"integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4="
+		},
+		"serve-static": {
+			"version": "1.14.1",
+			"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+			"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+			"requires": {
+				"encodeurl": "~1.0.2",
+				"escape-html": "~1.0.3",
+				"parseurl": "~1.3.3",
+				"send": "0.17.1"
+			}
+		},
+		"set-blocking": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+			"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+		},
+		"set-immediate-shim": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+			"integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
+		},
+		"setimmediate": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+			"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
+		},
+		"setprototypeof": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+			"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+		},
+		"shimmer": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
+			"integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="
+		},
+		"should": {
+			"version": "13.2.3",
+			"resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz",
+			"integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==",
+			"dev": true,
+			"requires": {
+				"should-equal": "^2.0.0",
+				"should-format": "^3.0.3",
+				"should-type": "^1.4.0",
+				"should-type-adaptors": "^1.0.1",
+				"should-util": "^1.0.0"
+			}
+		},
+		"should-equal": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz",
+			"integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==",
+			"dev": true,
+			"requires": {
+				"should-type": "^1.4.0"
+			}
+		},
+		"should-format": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz",
+			"integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=",
+			"dev": true,
+			"requires": {
+				"should-type": "^1.3.0",
+				"should-type-adaptors": "^1.0.1"
+			}
+		},
+		"should-http": {
+			"version": "0.1.1",
+			"resolved": "https://registry.npmjs.org/should-http/-/should-http-0.1.1.tgz",
+			"integrity": "sha1-m3k4Q/QCSIV4HrarrMQDDh6fIfA=",
+			"dev": true,
+			"requires": {
+				"content-type": "^1.0.2"
+			}
+		},
+		"should-type": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz",
+			"integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=",
+			"dev": true
+		},
+		"should-type-adaptors": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz",
+			"integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==",
+			"dev": true,
+			"requires": {
+				"should-type": "^1.3.0",
+				"should-util": "^1.0.0"
+			}
+		},
+		"should-util": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz",
+			"integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==",
+			"dev": true
+		},
+		"side-channel": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+			"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+			"dev": true,
+			"requires": {
+				"call-bind": "^1.0.0",
+				"get-intrinsic": "^1.0.2",
+				"object-inspect": "^1.9.0"
+			}
+		},
+		"signal-exit": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+			"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
+		},
+		"simple-swizzle": {
+			"version": "0.2.2",
+			"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+			"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+			"requires": {
+				"is-arrayish": "^0.3.1"
+			},
+			"dependencies": {
+				"is-arrayish": {
+					"version": "0.3.2",
+					"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+					"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+				}
+			}
+		},
+		"simple-update-notifier": {
+			"version": "1.0.7",
+			"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz",
+			"integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==",
+			"dev": true,
+			"requires": {
+				"semver": "~7.0.0"
+			},
+			"dependencies": {
+				"semver": {
+					"version": "7.0.0",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
+					"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
+					"dev": true
+				}
+			}
+		},
+		"smart-buffer": {
+			"version": "4.2.0",
+			"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+			"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="
+		},
+		"socks": {
+			"version": "2.6.1",
+			"resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz",
+			"integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==",
+			"requires": {
+				"ip": "^1.1.5",
+				"smart-buffer": "^4.1.0"
+			}
+		},
+		"socks-proxy-agent": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz",
+			"integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==",
+			"requires": {
+				"agent-base": "^6.0.2",
+				"debug": "4",
+				"socks": "^2.3.3"
+			}
+		},
+		"source-map": {
+			"version": "0.6.1",
+			"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+			"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+		},
+		"source-map-support": {
+			"version": "0.5.19",
+			"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+			"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+			"requires": {
+				"buffer-from": "^1.0.0",
+				"source-map": "^0.6.0"
+			}
+		},
+		"spawn-command": {
+			"version": "0.0.2-1",
+			"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
+			"integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A="
+		},
+		"spdx-correct": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+			"integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+			"requires": {
+				"spdx-expression-parse": "^3.0.0",
+				"spdx-license-ids": "^3.0.0"
+			}
+		},
+		"spdx-exceptions": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+			"integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="
+		},
+		"spdx-expression-parse": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+			"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+			"requires": {
+				"spdx-exceptions": "^2.1.0",
+				"spdx-license-ids": "^3.0.0"
+			}
+		},
+		"spdx-license-ids": {
+			"version": "3.0.9",
+			"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz",
+			"integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ=="
+		},
+		"sprintf-js": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+			"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
+		},
+		"sqlstring": {
+			"version": "2.3.2",
+			"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz",
+			"integrity": "sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg=="
+		},
+		"stack-trace": {
+			"version": "0.0.10",
+			"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+			"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
+		},
+		"statuses": {
+			"version": "1.5.0",
+			"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+			"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+		},
+		"streamsearch": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+			"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
+		},
+		"string_decoder": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+			"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+			"requires": {
+				"safe-buffer": "~5.1.0"
+			}
+		},
+		"string-width": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+			"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+			"requires": {
+				"code-point-at": "^1.0.0",
+				"is-fullwidth-code-point": "^1.0.0",
+				"strip-ansi": "^3.0.0"
+			}
+		},
+		"strip-ansi": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+			"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+			"requires": {
+				"ansi-regex": "^2.0.0"
+			}
+		},
+		"superagent": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz",
+			"integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==",
+			"dev": true,
+			"requires": {
+				"component-emitter": "^1.3.0",
+				"cookiejar": "^2.1.2",
+				"debug": "^4.1.1",
+				"fast-safe-stringify": "^2.0.7",
+				"form-data": "^3.0.0",
+				"formidable": "^1.2.2",
+				"methods": "^1.1.2",
+				"mime": "^2.4.6",
+				"qs": "^6.9.4",
+				"readable-stream": "^3.6.0",
+				"semver": "^7.3.2"
+			},
+			"dependencies": {
+				"form-data": {
+					"version": "3.0.1",
+					"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+					"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+					"dev": true,
+					"requires": {
+						"asynckit": "^0.4.0",
+						"combined-stream": "^1.0.8",
+						"mime-types": "^2.1.12"
+					}
+				},
+				"mime": {
+					"version": "2.6.0",
+					"resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+					"integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+					"dev": true
+				},
+				"qs": {
+					"version": "6.10.2",
+					"resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz",
+					"integrity": "sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==",
+					"dev": true,
+					"requires": {
+						"side-channel": "^1.0.4"
+					}
+				},
+				"readable-stream": {
+					"version": "3.6.0",
+					"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+					"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+					"dev": true,
+					"requires": {
+						"inherits": "^2.0.3",
+						"string_decoder": "^1.1.1",
+						"util-deprecate": "^1.0.1"
+					}
+				}
+			}
+		},
+		"supertest": {
+			"version": "6.1.6",
+			"resolved": "https://registry.npmjs.org/supertest/-/supertest-6.1.6.tgz",
+			"integrity": "sha512-0hACYGNJ8OHRg8CRITeZOdbjur7NLuNs0mBjVhdpxi7hP6t3QIbOzLON5RTUmZcy2I9riuII3+Pr2C7yztrIIg==",
+			"dev": true,
+			"requires": {
+				"methods": "^1.1.2",
+				"superagent": "^6.1.0"
+			}
+		},
+		"supports-color": {
+			"version": "8.1.1",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+			"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+			"requires": {
+				"has-flag": "^4.0.0"
+			}
+		},
+		"swagger-jsdoc": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.1.0.tgz",
+			"integrity": "sha512-xgep5M8Gq31MxpCbQLvJZpNqHfGPfI+sILCzujZbEXIQp2COtkZgoGASs0gacRs4xHmLDH+GuMGdorPITSG4tA==",
+			"dev": true,
+			"requires": {
+				"commander": "6.2.0",
+				"doctrine": "3.0.0",
+				"glob": "7.1.6",
+				"lodash.mergewith": "^4.6.2",
+				"swagger-parser": "10.0.2",
+				"yaml": "2.0.0-1"
+			},
+			"dependencies": {
+				"commander": {
+					"version": "6.2.0",
+					"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz",
+					"integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
+					"dev": true
+				},
+				"glob": {
+					"version": "7.1.6",
+					"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+					"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+					"dev": true,
+					"requires": {
+						"fs.realpath": "^1.0.0",
+						"inflight": "^1.0.4",
+						"inherits": "2",
+						"minimatch": "^3.0.4",
+						"once": "^1.3.0",
+						"path-is-absolute": "^1.0.0"
+					}
+				}
+			}
+		},
+		"swagger-parser": {
+			"version": "10.0.2",
+			"resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.2.tgz",
+			"integrity": "sha512-9jHkHM+QXyLGFLk1DkXBwV+4HyNm0Za3b8/zk/+mjr8jgOSiqm3FOTHBSDsBjtn9scdL+8eWcHdupp2NLM8tDw==",
+			"dev": true,
+			"requires": {
+				"@apidevtools/swagger-parser": "10.0.2"
+			}
+		},
+		"swagger-ui-dist": {
+			"version": "3.52.0",
+			"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.52.0.tgz",
+			"integrity": "sha512-SGfhW8FCih00QG59PphdeAUtTNw7HS5k3iPqDZowerPw9mcbhKchUb12kbROk99c1X6RTWW1gB1kqgfnYGuCSg==",
+			"dev": true
+		},
+		"swagger-ui-express": {
+			"version": "4.1.6",
+			"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.1.6.tgz",
+			"integrity": "sha512-Xs2BGGudvDBtL7RXcYtNvHsFtP1DBFPMJFRxHe5ez/VG/rzVOEjazJOOSc/kSCyxreCTKfJrII6MJlL9a6t8vw==",
+			"dev": true,
+			"requires": {
+				"swagger-ui-dist": "^3.18.1"
+			}
+		},
+		"systeminformation": {
+			"version": "5.7.7",
+			"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.7.7.tgz",
+			"integrity": "sha512-aQ7MBeVI2MKPYOi3YJAoZ45JVlRkBA7IXoqGgtVBamvtE0I6JLOyJzD/VVc9pnMXDb3yqaMwssAjhwtJax4/Rw==",
+			"optional": true
+		},
+		"tar": {
+			"version": "6.1.11",
+			"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
+			"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
+			"requires": {
+				"chownr": "^2.0.0",
+				"fs-minipass": "^2.0.0",
+				"minipass": "^3.0.0",
+				"minizlib": "^2.1.1",
+				"mkdirp": "^1.0.3",
+				"yallist": "^4.0.0"
+			}
+		},
+		"tar-stream": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+			"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+			"requires": {
+				"bl": "^4.0.3",
+				"end-of-stream": "^1.4.1",
+				"fs-constants": "^1.0.0",
+				"inherits": "^2.0.3",
+				"readable-stream": "^3.1.1"
+			},
+			"dependencies": {
+				"readable-stream": {
+					"version": "3.6.0",
+					"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+					"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+					"requires": {
+						"inherits": "^2.0.3",
+						"string_decoder": "^1.1.1",
+						"util-deprecate": "^1.0.1"
+					}
+				}
+			}
+		},
+		"text-hex": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+			"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
+		},
+		"tmp": {
+			"version": "0.2.1",
+			"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
+			"integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
+			"requires": {
+				"rimraf": "^3.0.0"
+			}
+		},
+		"to-regex-range": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+			"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+			"requires": {
+				"is-number": "^7.0.0"
+			}
+		},
+		"toidentifier": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+			"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+		},
+		"touch": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
+			"integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
+			"dev": true,
+			"requires": {
+				"nopt": "~1.0.10"
+			},
+			"dependencies": {
+				"nopt": {
+					"version": "1.0.10",
+					"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+					"integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
+					"dev": true,
+					"requires": {
+						"abbrev": "1"
+					}
+				}
+			}
+		},
+		"traverse": {
+			"version": "0.3.9",
+			"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
+			"integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk="
+		},
+		"tree-kill": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+			"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="
+		},
+		"triple-beam": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
+			"integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
+		},
+		"tslib": {
+			"version": "1.14.1",
+			"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+			"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+		},
+		"tv4": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz",
+			"integrity": "sha1-0CDIRvrdUMhVq7JeuuzGj8EPeWM="
+		},
+		"tx2": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/tx2/-/tx2-1.0.4.tgz",
+			"integrity": "sha512-rU+y30nUY3PyIi+znvv74HzxlpULKwMPAyRK+YiCjvGkk3rY3fic3D6Z+avLpun3V5A6HFwPQ9JrBTMNEV/dxg==",
+			"optional": true,
+			"requires": {
+				"json-stringify-safe": "^5.0.1"
+			}
+		},
+		"type-check": {
+			"version": "0.3.2",
+			"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+			"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+			"requires": {
+				"prelude-ls": "~1.1.2"
+			}
+		},
+		"type-fest": {
+			"version": "0.6.0",
+			"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
+			"integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="
+		},
+		"type-is": {
+			"version": "1.6.18",
+			"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+			"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+			"requires": {
+				"media-typer": "0.3.0",
+				"mime-types": "~2.1.24"
+			}
+		},
+		"typedarray": {
+			"version": "0.0.6",
+			"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+			"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+		},
+		"uid-safe": {
+			"version": "2.1.5",
+			"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
+			"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
+			"requires": {
+				"random-bytes": "~1.0.0"
+			}
+		},
+		"uid2": {
+			"version": "0.0.3",
+			"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
+			"integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I="
+		},
+		"undefsafe": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+			"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+			"dev": true
+		},
+		"underscore": {
+			"version": "1.13.3",
+			"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.3.tgz",
+			"integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA=="
+		},
+		"universalify": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+			"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
+		},
+		"unpipe": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+			"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+		},
+		"unzipper": {
+			"version": "0.10.11",
+			"resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz",
+			"integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==",
+			"requires": {
+				"big-integer": "^1.6.17",
+				"binary": "~0.3.0",
+				"bluebird": "~3.4.1",
+				"buffer-indexof-polyfill": "~1.0.0",
+				"duplexer2": "~0.1.4",
+				"fstream": "^1.0.12",
+				"graceful-fs": "^4.2.2",
+				"listenercount": "~1.0.1",
+				"readable-stream": "~2.3.6",
+				"setimmediate": "~1.0.4"
+			}
+		},
+		"url": {
+			"version": "0.10.3",
+			"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
+			"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
+			"requires": {
+				"punycode": "1.3.2",
+				"querystring": "0.2.0"
+			}
+		},
+		"url-template": {
+			"version": "2.0.8",
+			"resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
+			"integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE="
+		},
+		"urlsafe-base64": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz",
+			"integrity": "sha1-I/iQaabGL0bPOh07ABac77kL4MY="
+		},
+		"util-deprecate": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+			"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+		},
+		"utils-merge": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+			"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+		},
+		"uuid": {
+			"version": "3.4.0",
+			"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+			"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
+		},
+		"validate-npm-package-license": {
+			"version": "3.0.4",
+			"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+			"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+			"requires": {
+				"spdx-correct": "^3.0.0",
+				"spdx-expression-parse": "^3.0.0"
+			}
+		},
+		"validator": {
+			"version": "13.7.0",
+			"resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz",
+			"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==",
+			"dev": true
+		},
+		"vary": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+			"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+		},
+		"vizion": {
+			"version": "2.2.1",
+			"resolved": "https://registry.npmjs.org/vizion/-/vizion-2.2.1.tgz",
+			"integrity": "sha512-sfAcO2yeSU0CSPFI/DmZp3FsFE9T+8913nv1xWBOyzODv13fwkn6Vl7HqxGpkr9F608M+8SuFId3s+BlZqfXww==",
+			"requires": {
+				"async": "^2.6.3",
+				"git-node-fs": "^1.0.0",
+				"ini": "^1.3.5",
+				"js-git": "^0.7.8"
+			},
+			"dependencies": {
+				"async": {
+					"version": "2.6.3",
+					"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+					"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+					"requires": {
+						"lodash": "^4.17.14"
+					}
+				}
+			}
+		},
+		"vm2": {
+			"version": "3.9.5",
+			"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.5.tgz",
+			"integrity": "sha512-LuCAHZN75H9tdrAiLFf030oW7nJV5xwNMuk1ymOZwopmuK3d2H4L1Kv4+GFHgarKiLfXXLFU+7LDABHnwOkWng=="
+		},
+		"web-push": {
+			"version": "3.4.5",
+			"resolved": "https://registry.npmjs.org/web-push/-/web-push-3.4.5.tgz",
+			"integrity": "sha512-2njbTqZ6Q7ZqqK14YpK1GGmaZs3NmuGYF5b7abCXulUIWFSlSYcZ3NBJQRFcMiQDceD7vQknb8FUuvI1F7Qe/g==",
+			"requires": {
+				"asn1.js": "^5.3.0",
+				"http_ece": "1.1.0",
+				"https-proxy-agent": "^5.0.0",
+				"jws": "^4.0.0",
+				"minimist": "^1.2.5",
+				"urlsafe-base64": "^1.0.0"
+			}
+		},
+		"wide-align": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+			"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+			"requires": {
+				"string-width": "^1.0.2 || 2"
+			}
+		},
+		"winston": {
+			"version": "3.3.3",
+			"resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz",
+			"integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==",
+			"requires": {
+				"@dabh/diagnostics": "^2.0.2",
+				"async": "^3.1.0",
+				"is-stream": "^2.0.0",
+				"logform": "^2.2.0",
+				"one-time": "^1.0.0",
+				"readable-stream": "^3.4.0",
+				"stack-trace": "0.0.x",
+				"triple-beam": "^1.3.0",
+				"winston-transport": "^4.4.0"
+			},
+			"dependencies": {
+				"readable-stream": {
+					"version": "3.6.0",
+					"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+					"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+					"requires": {
+						"inherits": "^2.0.3",
+						"string_decoder": "^1.1.1",
+						"util-deprecate": "^1.0.1"
+					}
+				}
+			}
+		},
+		"winston-daily-rotate-file": {
+			"version": "4.5.5",
+			"resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.5.5.tgz",
+			"integrity": "sha512-ds0WahIjiDhKCiMXmY799pDBW+58ByqIBtUcsqr4oDoXrAI3Zn+hbgFdUxzMfqA93OG0mPLYVMiotqTgE/WeWQ==",
+			"requires": {
+				"file-stream-rotator": "^0.5.7",
+				"object-hash": "^2.0.1",
+				"triple-beam": "^1.3.0",
+				"winston-transport": "^4.4.0"
+			}
+		},
+		"winston-transport": {
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz",
+			"integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==",
+			"requires": {
+				"readable-stream": "^2.3.7",
+				"triple-beam": "^1.2.0"
+			}
+		},
+		"word-wrap": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+			"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
+		},
+		"wrap-ansi": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+			"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+			"requires": {
+				"ansi-styles": "^4.0.0",
+				"string-width": "^4.1.0",
+				"strip-ansi": "^6.0.0"
+			},
+			"dependencies": {
+				"ansi-regex": {
+					"version": "5.0.1",
+					"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+					"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+				},
+				"is-fullwidth-code-point": {
+					"version": "3.0.0",
+					"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+					"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+				},
+				"string-width": {
+					"version": "4.2.2",
+					"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+					"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+					"requires": {
+						"emoji-regex": "^8.0.0",
+						"is-fullwidth-code-point": "^3.0.0",
+						"strip-ansi": "^6.0.0"
+					}
+				},
+				"strip-ansi": {
+					"version": "6.0.0",
+					"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+					"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+					"requires": {
+						"ansi-regex": "^5.0.0"
+					}
+				}
+			}
+		},
+		"wrappy": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+			"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+		},
+		"ws": {
+			"version": "7.4.6",
+			"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+			"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
+			"requires": {}
+		},
+		"xml2js": {
+			"version": "0.4.19",
+			"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
+			"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
+			"requires": {
+				"sax": ">=0.6.0",
+				"xmlbuilder": "~9.0.1"
+			}
+		},
+		"xmlbuilder": {
+			"version": "9.0.7",
+			"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
+			"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
+		},
+		"xmlchars": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+			"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
+		},
+		"xregexp": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz",
+			"integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM="
+		},
+		"xtend": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+			"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+		},
+		"y18n": {
+			"version": "5.0.8",
+			"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+			"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+		},
+		"yallist": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+			"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+		},
+		"yaml": {
+			"version": "2.0.0-1",
+			"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz",
+			"integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==",
+			"dev": true
+		},
+		"yamljs": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz",
+			"integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==",
+			"requires": {
+				"argparse": "^1.0.7",
+				"glob": "^7.0.5"
+			}
+		},
+		"yargs": {
+			"version": "16.2.0",
+			"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+			"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+			"requires": {
+				"cliui": "^7.0.2",
+				"escalade": "^3.1.1",
+				"get-caller-file": "^2.0.5",
+				"require-directory": "^2.1.1",
+				"string-width": "^4.2.0",
+				"y18n": "^5.0.5",
+				"yargs-parser": "^20.2.2"
+			},
+			"dependencies": {
+				"ansi-regex": {
+					"version": "5.0.0",
+					"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+					"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
+				},
+				"is-fullwidth-code-point": {
+					"version": "3.0.0",
+					"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+					"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+				},
+				"string-width": {
+					"version": "4.2.2",
+					"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+					"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+					"requires": {
+						"emoji-regex": "^8.0.0",
+						"is-fullwidth-code-point": "^3.0.0",
+						"strip-ansi": "^6.0.0"
+					}
+				},
+				"strip-ansi": {
+					"version": "6.0.0",
+					"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+					"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+					"requires": {
+						"ansi-regex": "^5.0.0"
+					}
+				}
+			}
+		},
+		"yargs-parser": {
+			"version": "20.2.9",
+			"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+			"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
+		},
+		"z-schema": {
+			"version": "4.2.4",
+			"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-4.2.4.tgz",
+			"integrity": "sha512-YvBeW5RGNeNzKOUJs3rTL4+9rpcvHXt5I051FJbOcitV8bl40pEfcG0Q+dWSwS0/BIYrMZ/9HHoqLllMkFhD0w==",
+			"dev": true,
+			"requires": {
+				"commander": "^2.7.1",
+				"lodash.get": "^4.4.2",
+				"lodash.isequal": "^4.5.0",
+				"validator": "^13.6.0"
+			}
+		},
+		"zip-stream": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz",
+			"integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==",
+			"requires": {
+				"archiver-utils": "^2.1.0",
+				"compress-commons": "^4.1.0",
+				"readable-stream": "^3.6.0"
+			},
+			"dependencies": {
+				"readable-stream": {
+					"version": "3.6.0",
+					"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+					"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+					"requires": {
+						"inherits": "^2.0.3",
+						"string_decoder": "^1.1.1",
+						"util-deprecate": "^1.0.1"
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/_old/package.json b/_old/package.json
new file mode 100644
index 0000000..8ffd245
--- /dev/null
+++ b/_old/package.json
@@ -0,0 +1,76 @@
+{
+	"name": "exam210506",
+	"version": "1.0.0",
+	"description": "Node.js API Server",
+	"main": "index.js",
+	"scripts": {
+		"start": "node index",
+		"backend": "nodemon server/index",
+		"prod": "sudo pm2 restart ecosystem.config.js --env production",
+		"test": "mocha ./src/Store/store.spec.js --timeout 10000",
+		"dev": "concurrently \"npm run backend\" \"npm run start --prefix client\""
+	},
+	"author": "jaewon",
+	"license": "ISC",
+	"dependencies": {
+		"@sentry/node": "^7.36.0",
+		"@sentry/tracing": "^7.36.0",
+		"aws-sdk": "^2.1072.0",
+		"axios": "^0.21.4",
+		"bcrypt": "^5.0.1",
+		"body-parser": "^1.19.0",
+		"cheerio": "^1.0.0-rc.10",
+		"compression": "^1.7.4",
+		"concurrently": "^6.0.2",
+		"connect-redis": "^6.1.3",
+		"cookie-parser": "^1.4.6",
+		"cors": "^2.8.5",
+		"crypto": "^1.0.1",
+		"crypto-js": "^4.0.0",
+		"dotenv": "^15.0.0",
+		"exceljs": "^4.3.0",
+		"express": "^4.17.1",
+		"express-mysql-session": "^2.1.8",
+		"express-session": "^1.17.3",
+		"form-data": "^4.0.0",
+		"google-auth-library": "^7.3.0",
+		"googleapis": "^89.0.0",
+		"iconv-lite": "^0.6.3",
+		"jsonwebtoken": "^8.5.1",
+		"lru-cache": "^6.0.0",
+		"method-override": "^3.0.0",
+		"multer": "^1.4.3",
+		"multer-s3": "^2.10.0",
+		"mysql2": "^2.2.5",
+		"node-cache": "^5.1.2",
+		"node-cron": "^3.0.0",
+		"node-sens": "^1.3.0",
+		"nodemailer": "^6.6.2",
+		"passport": "^0.4.1",
+		"passport-kakao": "^1.0.1",
+		"pm2": "^5.1.0",
+		"readline": "^1.3.0",
+		"redis": "^4.3.1",
+		"regex-email": "^1.0.2",
+		"web-push": "^3.4.5",
+		"winston": "^3.3.3",
+		"winston-daily-rotate-file": "^4.5.5"
+	},
+	"devDependencies": {
+		"nodemon": "^2.0.20",
+		"should": "^13.2.3",
+		"should-http": "^0.1.1",
+		"supertest": "^6.1.6",
+		"swagger-jsdoc": "^6.1.0",
+		"swagger-ui-express": "^4.1.6"
+	},
+	"repository": {
+		"type": "git",
+		"url": "git+https://github.com/Easy-Electric/engineeo-server.git"
+	},
+	"keywords": [],
+	"bugs": {
+		"url": "https://github.com/Easy-Electric/engineeo-server/issues"
+	},
+	"homepage": "https://github.com/Easy-Electric/engineeo-server#readme"
+}
diff --git a/_old/src/Admin/adminController.js b/_old/src/Admin/adminController.js
new file mode 100644
index 0000000..18ea107
--- /dev/null
+++ b/_old/src/Admin/adminController.js
@@ -0,0 +1,3634 @@
+const adminProvider = require("./adminProvider");
+const adminService = require("./adminService");
+const secret = require("../../config/secret");
+const examProvider = require("../Exam/examProvider");
+const userProvider = require("../User/userProvider");
+const channelTalkService = require("../ChannelTalk/channelTalkService");
+const restoreProvider = require("../Restore/restoreProvider");
+const regNumber = /^[0-9]/;
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const baseResponseStatus = require("../../config/baseResponseStatus");
+const { logger } = require("../../config/winston");
+const { promises } = require("winston-daily-rotate-file");
+const { eq } = require("cheerio/lib/api/traversing");
+const errorResponse = require("../../utils/errorResponse");
+const asyncHandler = require("../../utils/asyncHandler");
+const AdminAuthenticator = require("../../utils/adminAuthenticator");
+
+//const { admin } = require("googleapis/build/src/apis/admin");
+const regKorean = /^[가-힣]{2,4}$/;
+const regUrl = /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/;
+const regDate = (dayRegExp = /^(19|20)\d{2}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[0-1])$/);
+const helperTerms = [
+	"이므로",
+	"위해서",
+	"로부터",
+	"에서",
+	"으로",
+	"것은",
+	"하게",
+	"하고",
+	"한다",
+	"이다",
+	"이고",
+	"이며",
+	"하면",
+	"라고",
+	"로는",
+	"대한",
+	"동안",
+	"하는",
+	"하게",
+	"관한",
+	"부터",
+	"되는",
+	"되면",
+	"는가",
+	"을",
+	"를",
+	"때",
+	"은",
+	"는",
+	"의",
+	"에",
+	"겠",
+	"했",
+];
+
+// 패스워드 초기화 API
+exports.passwordInit = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { userIdx, password } = req.body;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+	// if ((adminCheckResult[0].exist == userInfoByEmail) == 0)
+	// 	return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+
+	await adminService.initPassword(userIdx, password);
+
+	return res.send(basickResponse(baseResponse.SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 필기 문제 에러 조회 API
+ * [GET] /admins/problemError
+ * query => recently(최신순이면 1, 아니면 0), subjectiveLabel(실기면 1 아니면 0)
+ */
+exports.getProblemError = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const recently = req.query.recently;
+	const subjectiveLabel = req.query.subjectiveLabel;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const providerResult = await adminProvider.getProblemError(recently, subjectiveLabel);
+
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 유저 목록 조회 API
+ * [GET] /admins/userList
+ */
+exports.getUserList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const providerResult = await adminProvider.getUserList();
+
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 문제 에러 삭제 API
+ * [PATCH]] /admins/delete/problemError/:problemErrorIdx
+ */
+exports.deleteProblemError = asyncHandler(async function (req, res) {
+	const problemErrorIdx = req.params.problemErrorIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+	const solution = req.body.solution; // 수정이 되었는지 판단하기 위함.
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!problemErrorIdx) throw new errorResponse(baseResponse.PROBLEMERROR_EMPTY, 400);
+
+	await adminProvider.problemErrorIdxCheck(problemErrorIdx);
+
+	const serviceResult = await adminService.patchProblemError(problemErrorIdx, solution);
+
+	return res.send(serviceResult);
+});
+
+/**
+ * API No.
+ * API Name : 문제 수정 API
+ * [PATCH]] /admins/update/multipleProblem/:multipleProblemIdx
+ */
+exports.patchMultipleProblem = asyncHandler(async function (req, res) {
+	const multipleProblemIdx = req.params.multipleProblemIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+	const {
+		problem,
+		problemNum,
+		problemImage,
+		solution,
+		question,
+		questionNum,
+		questionImage,
+		answerNum,
+		isKatex,
+		isDelete,
+		provisionNum,
+		examSubjectIdx,
+		questionIdx,
+		problemUrl,
+		lectureUrl,
+		examDetailIdx,
+		examHistory,
+	} = req.body;
+
+	let { problemScore, categoryIdx } = req.body;
+	let userInputProblem = {
+		problem,
+		problemImage,
+		solution,
+		provisionNum,
+		isKatex,
+		problemScore,
+		problemUrl,
+		lectureUrl,
+		isDelete,
+		examHistory,
+	};
+	//let isProblemNum = 0;
+	// let problemSet, questionSet = [],
+	//let mFlag, qFlag = [];
+	if (!categoryIdx) categoryIdx = undefined;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!multipleProblemIdx) throw new errorResponse(baseResponse.PROBLEMIDX_EMPTY, 400);
+
+	if (!regNumber.test(multipleProblemIdx)) throw new errorResponse(baseResponse.PROBLEMIDX_ERROR_TYPE, 400);
+
+	await examProvider.multipleProblemCheck(multipleProblemIdx);
+
+	if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+
+	if (!regNumber.test(examDetailIdx)) throw new errorResponse(baseResponse.EXAMDETAIL_ERROR_TYPE, 400);
+
+	await examProvider.examDetailCheck(examDetailIdx);
+
+	if (!answerNum) throw new errorResponse(baseResponse.ANSWER_EMPTY, 400);
+	if (question.length < answerNum) throw new errorResponse(baseResponse.QUESTIONNUM_ERROR_TYPE, 400);
+
+	if (questionNum.length != question.length) throw new errorResponse(baseResponse.ARRAY_LENGTH_ERROR, 400);
+
+	if (question.length != questionImage.length) throw new errorResponse(baseResponse.ARRAY_LENGTH_ERROR, 400);
+	for (let content of questionNum) {
+		if (!content) throw new errorResponse(baseResponse.QUESTIONNUM_EMPTY, 400);
+	}
+
+	for (let content of question) {
+		if (!content) throw new errorResponse(baseResponse.QUESTION_EMPTY, 400);
+	}
+
+	const problemInfo = await adminProvider.getMultipleProblemInfo(multipleProblemIdx);
+	if (problemInfo.length == 0) throw new errorResponse(baseResponse.PROBLEM_NOT_EXIST, 404);
+
+	if (problemInfo[0].problemNum != problemNum) {
+		await examProvider.multipleProblemNumCheck(problemInfo[0].examDetailIdx, problemNum);
+	}
+
+	const isCertification = await adminProvider.isCertificationCheck(examDetailIdx);
+	if (isCertification == 1) {
+		userInputProblem.problemScore = userInputProblem.problemScore ?? 5;
+		if (!examSubjectIdx) throw new errorResponse(baseResponse.SUBJECT_EMPTY, 400);
+		if (!regNumber.test(examSubjectIdx)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+		if (problemInfo[0].examSubjectIdx != examSubjectIdx) {
+			await adminProvider.problemSubjectCheck(problemInfo[0].examDetailIdx, examSubjectIdx, problemNum);
+		}
+	}
+
+	if (categoryIdx) {
+		await adminProvider.categoryCheck(categoryIdx);
+	}
+	// isCertification에 따라 문제 수정 쿼리가 다르다./
+
+	const updateResult = await adminService.patchMultipleProblem(
+		userInputProblem,
+		multipleProblemIdx,
+		question,
+		questionNum,
+		questionImage,
+		answerNum,
+		examSubjectIdx,
+		questionIdx,
+		examDetailIdx,
+		categoryIdx,
+		problemNum,
+		isCertification,
+	);
+	return res.send(updateResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 생성 API
+ * [POST] /admins/upload/Exam
+ */
+exports.postExam = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const {
+		examName,
+		passScore,
+		timeLimit,
+		problemCount,
+		examSortMediumIdx: examSortIdx,
+		examSortLargeIdx: examSortRef,
+		examSubjectIdx,
+		examSubjectNum,
+		examThumbnailUrl,
+		subjectiveLabel,
+	} = req.body;
+	let insertExamParams;
+
+	if (!examSubjectIdx) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+	if (subjectiveLabel !== 0 && subjectiveLabel !== 1)
+		return res.send(basickResponse(baseResponse.SUBJECTIVELABEL_NOT_EXIST));
+
+	if (!examName) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	if (!passScore) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	if (!examSortIdx) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	if (!timeLimit) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	if (!problemCount) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	// examIdx가 있으면 해당 시험이 존재하는 것.
+	const examIdx = await adminProvider.getExamIdx(examName);
+	if (examIdx) throw new errorResponse(baseResponse.EXAMNAME_EXIST, 400);
+
+	if (examSubjectIdx.length != examSubjectNum.length) throw new errorResponse(baseResponse.ARRAY_LENGTH_ERROR, 400);
+
+	await adminProvider.examSortRefValueCheck(examSortIdx, examSortRef);
+	// exanSortIdx :75  examSortRef : 5
+
+	for (let i = 0; i < examSubjectIdx.length; i++) {
+		if (!examSubjectNum[i]) return res.send(basickResponse(baseResponse.EXAMSUBJECTNUM_EMPTY));
+		await examProvider.examSubjectCheck(examSubjectIdx[i]);
+	}
+
+	insertExamParams = [examName, passScore, examSortIdx, timeLimit, problemCount, examThumbnailUrl, subjectiveLabel];
+	const createResult = await adminService.createExam(insertExamParams, examSubjectIdx, examSubjectNum);
+
+	return res.send(createResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 상세 생성 API
+ * [POST] /admins/upload/Exam/:examIdx
+ * @todo examDetailUrl ExamDetail 테이블에서 삭제
+ *
+ */
+exports.postExamDetail = asyncHandler(async function (req, res) {
+	const examIdx = req.params.examIdx;
+	const examDate = req.body.examDate;
+	const examRound = req.body.examRound;
+	const publicLevel = req.body.publicLevel;
+
+	const year = examDate.split("-", 4);
+
+	await AdminAuthenticator.authenticateAdmin(req);
+
+	if (!examIdx) return res.send(basickResponse(baseResponse.EXAM_EMPTY));
+	if (!regNumber.test(examIdx)) return res.send(basickResponse(baseResponse.EXAM_ERROR_TYPE));
+
+	await examProvider.examCheck(examIdx);
+
+	await adminProvider.examDetailDoubleCheck(examIdx, examRound, year[0] + "%");
+
+	const createResult = await adminService.createExamDetail(examIdx, examDate, examRound, publicLevel);
+	return res.send(createResult);
+});
+
+/**
+ * API No.
+ * API Name : 공기업 회사 구분 조회 API
+ * [GET] /admins/PublicCompany/Sort
+ */
+exports.getPublicCompanySort = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const publicCompanySortResult = await adminProvider.getPublicCompanySort();
+	return res.send(publicCompanySortResult);
+});
+
+/**
+ * API No.
+ * API Name : 기업 회사 구분 조회 API
+ * [GET] /admins/company/sort
+ * @param companyType
+ * @changeFrom getPublicCompanySort
+ */
+exports.getCompanySort = asyncHandler(async function (req, res) {
+	// await AdminAuthenticator.authenticateAdmin(req);
+	const { companyType } = req.query;
+	if (!companyType) throw new errorResponse(baseResponse.COMPANY_TYPE_EMPTY, 400);
+
+	const getCompanySortResult = await adminProvider.getCompanySort(companyType);
+	return res.send(getCompanySortResult);
+});
+
+/**
+ * API No.
+ * API Name : 공기업 회사 생성 API
+ * [POST] /admins/upload/publicCompany
+ */
+exports.postPublicCompany = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { publicCompanySortIdx, publicCompanySortName, companyThumbnail } = req.body;
+	const { companySortLabel } = req.query;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!publicCompanySortIdx) return res.send(basickResponse(baseResponse.USER_NOT_EXIST)); // 문구 수정
+
+	if (!publicCompanySortName) return res.send(basickResponse(baseResponse.USER_NOT_EXIST)); // 문구 수정
+
+	// companyName에 존재성 파악
+	await adminProvider.companyNameCheck(publicCompanySortName);
+
+	await adminProvider.examSortIdxCheck(publicCompanySortIdx);
+
+	const createResult = await adminService.insertPublicCompany(
+		publicCompanySortIdx,
+		publicCompanySortName,
+		companyThumbnail,
+		companySortLabel,
+	);
+	return res.send(createResult);
+});
+
+/**
+ * API No.
+ * API Name : 기업 회사 생성 API
+ * [POST] /admins/company/upload/companyName/?companyType={String} 공기업 / 대기업
+ * @changeFrom postPublicCompany
+ * @param {String} companyType 공기업인지 대기업인지 확인
+ * @param {Number} companyExamTypeIdx (기업 시험 종류 Idx) 중분류
+ * @param {Number} companyExamFieldIdx (기업 시험 영역 Idx) 소분류
+ * @param {String} companyName (기업 이름)
+ * @param {String} companyThumbnail 썸네일
+ * @author 김기창
+ */
+exports.postCompany = asyncHandler(async function (req, res) {
+	const { companyType } = req.query;
+	const { companyExamTypeIdx, companyExamFieldIdx, companyName, companyThumbnail } = req.body;
+
+	// await AdminAuthenticator.authenticateAdmin(req);
+
+	if (!companyExamTypeIdx) throw new errorResponse(baseResponse.COMPANY_TYPE_EMPTY, 400);
+	if (!companyExamFieldIdx) throw new errorResponse(baseResponse.COMPANY_EXAMFIELD_NOT_EXIST, 400);
+	if (!companyName) throw new errorResponse(baseResponse.COMPANY_EXAM_EMPTY, 400);
+
+	// 기업의 중분류 소분류 조합에 대한 존재성 파악.
+	await adminProvider.companySortIdxCheck(companyType, companyExamTypeIdx, companyExamFieldIdx);
+
+	// companyName에 존재성 파악, 이때 companyExamFieldIdx에 대해서도 함께 생각해줘야 한다.
+	await adminProvider.companyNameCheck(companyExamFieldIdx, companyName);
+
+	const createResult = await adminService.insertCompany(companyExamFieldIdx, companyName, companyThumbnail);
+	return res.send(createResult);
+});
+
+/**
+ * API No.
+ * API Name : 기업 시험 소분류 생성 API
+ * [POST] /admins/company/companyExamField/?companyType={String} 공기업 / 대기업
+ * @param {Number} companyExamTypeIdx (기업 시험 종류 Idx) 중분류
+ * @param {Number} companyExamFiecompanyExamFieldNameldIdx (기업 시험 영역 name) 소분류
+ * @author 김기창
+ */
+exports.postCompanyExamField = asyncHandler(async function (req, res) {
+	const { companyExamTypeIdx, companyExamFieldName } = req.body;
+
+	if (!companyExamTypeIdx) throw new errorResponse(baseResponse.COMPANY_TYPE_EMPTY, 400);
+	if (!companyExamFieldName) throw new errorResponse(baseResponse.ESSENTIAL_EMPTY, 400);
+
+	// 중분류에 속해있는 companyExamFieldName에 대한 존재성 파악
+	await adminProvider.companyExamFieldNameCheck(companyExamTypeIdx, companyExamFieldName);
+
+	const createResult = await adminService.postCompanyExamField(companyExamTypeIdx, companyExamFieldName);
+	return res.send(createResult);
+});
+
+/**
+ * API No.
+ * API Name : 기업 회사 시험 생성 API
+ * [POST] /api/admins/company/upload/exam
+ */
+exports.postCompanyExam = asyncHandler(async function (req, res) {
+	const {
+		companyExamFieldIdx,
+		companyName,
+		education,
+		category,
+		domain,
+		timeLimit,
+		problemCount,
+		questionType,
+		isBody,
+	} = req.body;
+	if (!companyExamFieldIdx || !companyName) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+	if (!education || !domain) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	if (!timeLimit) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	if (!problemCount) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+	if (!questionType) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	// let examName = await adminProvider.getExamSortName(publicCompanyIdx);
+	// examName += "(" + education + "-" + position + ")";
+
+	let examName = companyName + "(" + education + "-" + domain + ")";
+
+	// examIdx가 존재하면 해당 시험이 존재하는 것.
+	const examIdx = await adminProvider.getExamIdx(examName);
+	if (examIdx) {
+		// 이름이 같더라도 category가 다를 수 있다.
+		// category가 다르면 새로운 시험으로 생성할 수 있도록 한다.
+		await adminProvider.checkCompanyExamCategory(examIdx, category);
+	}
+
+	const createResult = await adminService.insertCompanyExam(
+		companyExamFieldIdx,
+		companyName,
+		examName,
+		education,
+		category,
+		domain,
+		timeLimit,
+		problemCount,
+		questionType,
+		isBody,
+	);
+	return res.send(createResult);
+});
+
+/**
+ * API No.
+ * API Name : 기업 회사 시험 수정 API
+ * [patch] /admins/company/update/exam
+ * 이 세가지를 수정하는 API
+ * 1. 문제 유형 ( questionType )
+ * 2. 제한 시간 ( timeLimit )
+ * 3. 문제 수 ( problemCount )
+ */
+exports.updateCompanyExam = asyncHandler(async function (req, res) {
+	const { companyExamFieldIdx, companyName, domain, education, category, questionType, timeLimit, problemCount } =
+		req.body;
+
+	console.log(companyExamFieldIdx, companyName, domain, education, category, questionType, timeLimit, problemCount);
+	// await AdminAuthenticator.authenticateAdmin(req);
+
+	if (!companyExamFieldIdx || !companyName) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	if (!problemCount) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	//if (!questionType) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); // 문구 수정
+
+	// if (!domain) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	if (!education) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	const updateResult = await adminService.updatePublicCompanyExam(
+		companyExamFieldIdx,
+		companyName,
+		domain,
+		education,
+		category,
+		questionType,
+		timeLimit,
+		problemCount,
+	);
+
+	return res.send(updateResult);
+});
+
+/**
+ * API No.
+ * API Name : 공기업 회사 시험 목록 조회 API
+ * [GET] /admins/PublicCompany/ExamList
+ */
+exports.publicCompanyExamList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { publicCompanySortIdx, publicCompanyIdx, publicCompanyExamIdx, search } = req.query;
+	let where = `where e.status = 'N' `;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (search) where += ` and examName like '%${search}%'`;
+	if (publicCompanyExamIdx) where += `and e.examIdx = ${publicCompanyExamIdx}`;
+	else if (publicCompanyIdx) where += `and S.examSortIdx = ${publicCompanyIdx}`;
+	else if (publicCompanySortIdx) where += `and M.examSortIdx = ${publicCompanySortIdx}`;
+	else where += ` and M.examSortRef = 2`;
+	const examList = await adminProvider.getPublicCompanyExamInfoList(where);
+	return res.send(resultResponse(baseResponse.SUCCESS, examList));
+});
+
+/**
+ * API No.
+ * API Name : 기업 회사 시험 목록 조회 API
+ * [GET] /api/admins/company/examList/?companyType={String} 공기업 / 대기업
+ * @author 김기창
+ * @param {String} companyType 기업이 공기업인지 대기업인지 판단하기 위함.
+ * @changeFrom publicCompanyExamList
+ */
+exports.companyExamList = asyncHandler(async function (req, res) {
+	const { companyType } = req.query;
+	if (!companyType) throw new errorResponse(baseResponse.COMPANY_TYPE_EMPTY, 400);
+
+	// const adminIdx = req.verifiedToken.userIdx;
+	// if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	// if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+	// const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	// if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const companyExamListResults = await adminProvider.getCompanyExamList(companyType);
+	return res.send(resultResponse(baseResponse.SUCCESS, companyExamListResults));
+});
+
+/**
+ * API No.
+ * API Name : 기업 시험 회차 목록 조회 API
+ * [GET] /api/admins/company/examDetail/?companyType={String} 공기업 / 대기업
+ * @param {String} companyType 기업이 공기업인지 대기업인지 판단하기 위함.
+ * @author 김기창
+ */
+exports.companyExamDetailList = asyncHandler(async function (req, res) {
+	let { companyType } = req.query;
+	if (!companyType) throw new errorResponse(baseResponse.COMPANY_TYPE_EMPTY, 400);
+	if (companyType === "전체") {
+		companyType = ["공기업", "대기업"];
+	}
+
+	// const adminIdx = req.verifiedToken.userIdx;
+	//const { publicCompanySortIdx, publicCompanyIdx, publicCompanyExamIdx, search } = req.query;
+
+	// if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	// if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	// const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	// if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const companyExamDetailListResult = await adminProvider.getCompanyExamDetailList(companyType);
+	return res.send(companyExamDetailListResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 객관식 문제 생성 API
+ * [POST] /admins/upload/multipleProblem
+ */
+exports.postMultipleProblem = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const PROBLEMSCORE = 5; // 자격증 배점
+	const {
+		problemNum,
+		problem,
+		answerNum,
+		provisionNum,
+		problemImage,
+		solution,
+		examSubjectIdx,
+		examDetailIdx,
+		isKatex,
+		questionNum,
+		question,
+		questionImage,
+		problemUrl,
+		lectureUrl,
+		categoryIdx,
+		examHistory,
+	} = req.body;
+	let { isDelete, problemScore } = req.body; //필수
+	if (!userIdx) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USERNAME_ERROR_TYPE));
+
+	if (!problemNum) return res.send(basickResponse(baseResponse.PROBLEMNUM_EMPTY));
+
+	if (!problem) return res.send(basickResponse(baseResponse.PROBLEM_EMPTY));
+
+	if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+
+	if (!isDelete) isDelete = 0;
+
+	if (isDelete == 0) {
+		if (!answerNum) throw new errorResponse(baseResponse.ANSWER_EMPTY, 400);
+
+		if (!solution) return res.send(basickResponse(baseResponse.SOLUTION_EMPTY));
+
+		if (isKatex != 0 && isKatex != 1) return res.send(basickResponse(baseResponse.ISKATEX_EMPTY));
+
+		//if (!questionNum) return res.send(basickResponse(baseResponse.QUESTIONNUM_EMPTY));
+
+		if (!question) return res.send(basickResponse(baseResponse.QUESTION_EMPTY));
+
+		//if (questionNum.length != question.length) throw new errorResponse(baseResponse.ARRAY_LENGTH_ERROR,400)
+
+		if (question.length > 5) {
+			// console.log("more than 5 question");
+			//console.log(question);
+			throw new errorResponse(baseResponse.ARRAY_LENGTH_ERROR, 400);
+		}
+
+		if (problemUrl && !regUrl.test(problemUrl)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+		if (lectureUrl && !regUrl.test(lectureUrl)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+	}
+
+	await examProvider.multipleProblemNumCheck(examDetailIdx, problemNum);
+
+	await adminProvider.adminCheck(userIdx);
+
+	if (categoryIdx) {
+		await adminProvider.categoryCheck(categoryIdx);
+	}
+	const isCertification = await adminProvider.isCertificationCheck(examDetailIdx);
+	problemScore = isCertification == 1 ? PROBLEMSCORE : problemScore;
+	if (isCertification == 1 && !examSubjectIdx) return res.send(basickResponse(baseResponse.EXAMSUBJECT_EMPTY));
+	// if (isDelete == 1) {
+	// 	problemInsertInfo = "(problemNum, problem, answerNum, examDetailIdx, isKatex, isDelete)";
+	// 	problemValues = "(?, ?, ?, ?, ?, ?, ?)";
+	// 	insertProblemParams = [problemNum, problem, answerNum, examDetailIdx, 0, isDelete];
+	// } else {
+	// 	problemInsertInfo = "(problemNum, problem, answerNum, solution, examSubjectIdx, examDetailIdx, isKatex";
+	// 	problemValues = "(?, ?, ?, ?, ?, ?, ?";
+	// 	insertProblemParams = [problemNum, problem, answerNum, solution, examSubjectIdx, examDetailIdx, isKatex];
+	// 	if (provisionNum) {
+	// 		problemInsertInfo += ", provisionNum";
+	// 		problemValues += ", ?";
+	// 		insertProblemParams.push(provisionNum);
+	// 	}
+	// 	if (problemImage) {
+	// 		problemInsertInfo += ", problemImage";
+	// 		problemValues += ", ?";
+	// 		insertProblemParams.push(problemImage);
+	// 	}
+	// 	if (lectureUrl) {
+	// 		problemInsertInfo += ", lectureUrl";
+	// 		problemValues += ", ?";
+	// 		insertProblemParams.push(lectureUrl);
+	// 	}
+	// 	problemInsertInfo += ")";
+	// 	problemValues += ")";
+
+	// 	for (let i = 0; i < questionNum.length; i++) {
+	// 		if (questionImage) {
+	// 			if (questionImage[i]) {
+	// 				questionInsertInfo.push("(questionNum, question, questionImage, multipleProblemIdx)");
+	// 			} else {
+	// 				questionInsertInfo.push("(questionNum, question, multipleProblemIdx)");
+	// 			}
+	// 		} else {
+	// 			questionInsertInfo.push("(questionNum, question, multipleProblemIdx)");
+	// 		}
+	// 	}
+	// }
+	let insertProblemParams = [
+		problem,
+		provisionNum,
+		problemImage,
+		solution,
+		isKatex,
+		problemScore,
+		problemUrl,
+		lectureUrl,
+		isDelete,
+		examHistory,
+	];
+	const content = problem + " " + question.join(" ");
+	const createResult = await adminService.createMultipleProblem(
+		insertProblemParams,
+		questionNum,
+		question,
+		questionImage,
+		isDelete,
+		content,
+		answerNum,
+		examSubjectIdx,
+		categoryIdx,
+		examDetailIdx,
+		problemNum,
+		isCertification,
+	);
+
+	return res.send(createResult);
+});
+
+/*
+ * API Name : 실기 문제 생성 API
+ * [POST] /api/admins/practical/upload/subjectiveProblem
+ */
+exports.postSubjectiveProblem = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const {
+		problemNum, //문제 번호
+		problem, // 문제
+		problemImage, // 문제 이미지
+		solution, // 해설
+		solutionImage, //해설 이미지
+		examSubjectIdx, //시험 과목 인덱스
+		examDetailIdx, // 시험 회차 인덱스
+		isKatex, // 카텍스 여부
+		problemUrl, // 문제 url
+		lectureUrl, // 해설 url
+		problemScore, // 문제 점수
+		categoryIdx, // 카테고리
+		examHistory,
+	} = req.body;
+	let { isDelete } = req.body; //삭제 여부
+
+	if (!userIdx) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USERNAME_ERROR_TYPE));
+
+	if (!problemNum) return res.send(basickResponse(baseResponse.PROBLEMNUM_EMPTY));
+
+	if (!problem) return res.send(basickResponse(baseResponse.PROBLEM_EMPTY));
+
+	if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+
+	if (!problemScore) return res.send(basickResponse(baseResponse.SCORE_EMPTY));
+
+	if (!isDelete) isDelete = 0;
+
+	if (isDelete == 0) {
+		if (!solution) return res.send(basickResponse(baseResponse.SOLUTION_EMPTY));
+
+		if (isKatex != 0 && isKatex != 1) return res.send(basickResponse(baseResponse.ISKATEX_EMPTY));
+
+		if (problemUrl && !regUrl.test(problemUrl)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+		if (lectureUrl && !regUrl.test(lectureUrl)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+	}
+
+	await examProvider.multipleProblemNumCheck(examDetailIdx, problemNum);
+
+	await adminProvider.adminCheck(userIdx);
+
+	if (categoryIdx) {
+		await adminProvider.categoryCheck(categoryIdx);
+	}
+	// const isCertification = await adminProvider.isCertificationCheck(examDetailIdx);
+	// problemScore = isCertification == 1 ? PROBLEMSCORE : problemScore;
+	// 실기의 경우 해당 문제의 점수는 수동으로 넣는 방향으로 진행.
+
+	if (!examSubjectIdx) return res.send(basickResponse(baseResponse.EXAMSUBJECT_EMPTY));
+
+	let insertProblemParams = [
+		problem,
+		problemImage,
+		solution,
+		solutionImage,
+		isKatex,
+		problemScore,
+		problemUrl,
+		lectureUrl,
+		isDelete,
+		examHistory,
+	];
+	const createResult = await adminService.createSubjectiveProblem(
+		insertProblemParams,
+		examSubjectIdx,
+		categoryIdx,
+		examDetailIdx,
+		problemNum,
+	);
+
+	return res.send(createResult);
+});
+
+/*
+ * API Name : 실기 문제 수정 API
+ * [PATCH] /api/admins/practical/patchSubjectiveProblem/:subjectiveProblemIdx
+ */
+exports.patchSubjectiveProblem = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const subjectiveProblemIdx = req.params.subjectiveProblemIdx;
+	const {
+		problemNum, //문제 번호
+		problem, // 문제
+		problemImage, // 문제 이미지
+		solution, // 해설
+		solutionImage, //해설 이미지
+		examSubjectIdx, //시험 과목 인덱스
+		examDetailIdx, // 시험 회차 인덱스
+		isKatex, // 카텍스 여부
+		problemUrl, // 문제 url
+		lectureUrl, // 해설 url
+		problemScore, // 문제 점수,
+		examHistory,
+	} = req.body;
+
+	let { categoryIdx, isDelete } = req.body;
+
+	if (!userIdx) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!subjectiveProblemIdx) throw new errorResponse(baseResponse.PROBLEMIDX_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USERNAME_ERROR_TYPE));
+
+	if (!problemNum) return res.send(basickResponse(baseResponse.PROBLEMNUM_EMPTY));
+
+	if (!problem) return res.send(basickResponse(baseResponse.PROBLEM_EMPTY));
+
+	if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+
+	if (!problemScore) return res.send(basickResponse(baseResponse.SCORE_EMPTY));
+
+	if (!isDelete) isDelete = 0;
+
+	if (isDelete == 0) {
+		if (!solution) return res.send(basickResponse(baseResponse.SOLUTION_EMPTY));
+
+		if (isKatex != 0 && isKatex != 1) return res.send(basickResponse(baseResponse.ISKATEX_EMPTY));
+
+		if (problemUrl && !regUrl.test(problemUrl)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+		if (lectureUrl && !regUrl.test(lectureUrl)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+	}
+
+	const problemInfo = await adminProvider.getSubjectiveProblemInfo(subjectiveProblemIdx);
+
+	if (problemInfo[0].problemNum != problemNum) {
+		await examProvider.subjectiveProblemNumCheck(problemInfo[0].examDetailIdx, problemNum);
+	}
+
+	await adminProvider.adminCheck(userIdx);
+
+	if (categoryIdx) {
+		await adminProvider.categoryCheck(categoryIdx);
+	}
+
+	if (!categoryIdx) categoryIdx = undefined;
+	// const isCertification = await adminProvider.isCertificationCheck(examDetailIdx);
+	// problemScore = isCertification == 1 ? PROBLEMSCORE : problemScore;
+	// 실기의 경우 해당 문제의 점수는 수동으로 넣는 방향으로 진행.
+
+	if (!examSubjectIdx) return res.send(basickResponse(baseResponse.EXAMSUBJECT_EMPTY));
+
+	let insertProblemParams = {
+		problem,
+		problemImage,
+		solution,
+		solutionImage,
+		isKatex,
+		problemScore,
+		problemUrl,
+		lectureUrl,
+		isDelete,
+		examHistory,
+	};
+	const updateResult = await adminService.patchSubjectiveProblem(
+		insertProblemParams,
+		subjectiveProblemIdx,
+		examSubjectIdx,
+		categoryIdx,
+		examDetailIdx,
+		problemNum,
+	);
+
+	return res.send(updateResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 수정 API
+ * [PATCH] /admins/update/Exam/:examIdx
+ */
+exports.updateExam = asyncHandler(async function (req, res) {
+	const examIdx = req.params.examIdx;
+	// examSubjectIdx, examSubjectNum 합쳐서 subjectList로 받는다?
+
+	const {
+		examName,
+		passScore,
+		// examSort,
+		// examSortRef,
+		timeLimit,
+		problemCount,
+		examSubjectIdx,
+		examSubjectNum,
+		accessLevel,
+		examThumbnailUrl,
+	} = req.body;
+	const adminIdx = req.verifiedToken.userIdx;
+	let setInfo, updateExamParams;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examIdx) return res.send(basickResponse(baseResponse.EXAM_EMPTY));
+
+	if (!regNumber.test(examIdx)) return res.send(basickResponse(baseResponse.EXAM_ERROR_TYPE));
+
+	await examProvider.examCheck(examIdx);
+
+	if (!examSubjectIdx) return res.send(basickResponse(baseResponse.EXAMSUBJECT_EMPTY));
+
+	if (!examSubjectNum) return res.send(basickResponse(baseResponse.EXAMSUBJECTNUM_EMPTY));
+
+	if (examSubjectIdx.length != examSubjectNum.length) throw new errorResponse(baseResponse.ARRAY_LENGTH_ERROR, 400);
+
+	for (let i = 0; i < examSubjectIdx.length; i++) {
+		if (!examSubjectNum[i]) return res.send(basickResponse(baseResponse.EXAMSUBJECTNUM_EMPTY));
+		await examProvider.examSubjectCheck(examSubjectIdx[i]);
+	}
+
+	//const lDivision = await examProvider.getExamDivision(examIdx, "L");
+	//const mDivision = await examProvider.getExamDivision(examIdx, "M");
+	//if (lDivision && mDivision) await adminService.updateExamSort(examSort, examSortRef, examSort);
+
+	// 상위 division 없다면 division부터 만들고 시험 생성 하도록 (자동생성 X)
+	//const examSortDoubleCheckResult = await adminProvider.examSortDoubleCheck(examSort, examSortRef);
+	//if (examSortDoubleCheckResult[0].exist === 0) await adminService.updateExamSort(examSort, examSortRef, examSort);
+
+	if (examThumbnailUrl) {
+		setInfo = "examName = ?, passScore = ?, timeLimit = ?, problemCount = ?, accessLevel = ?, examUrl = ?";
+		updateExamParams = [examName, passScore, timeLimit, problemCount, accessLevel, examThumbnailUrl, examIdx];
+	} else {
+		setInfo = "examName = ?, passScore = ?, timeLimit = ?, problemCount = ?, accessLevel = ?";
+		updateExamParams = [examName, passScore, timeLimit, problemCount, accessLevel, examIdx];
+	}
+
+	const patchResult = await adminService.patchExam(updateExamParams, setInfo, examIdx, examSubjectIdx, examSubjectNum);
+	return res.send(patchResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 삭제 API
+ * [PATCH] /admins/delete/Exam/:examIdx
+ */
+exports.deleteExam = asyncHandler(async function (req, res) {
+	const examIdx = req.params.examIdx;
+
+	await AdminAuthenticator.authenticateAdmin(req);
+
+	await examProvider.examCheck(examIdx);
+
+	const examDetailList = await examProvider.getExamDetail(examIdx);
+
+	if (examDetailList.length > 0) return res.send(basickResponse(baseResponse.EXAM_EXIST_EXAMDETAIL));
+
+	const deleteResult = await adminService.deleteExam(examIdx);
+	return res.send(deleteResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 상세 수정 API
+ * [PATCH] /admins/update/ExamDetail/:examDetailIdx
+ */
+exports.updateExamDetail = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examDetailIdx = req.params.examDetailIdx;
+	const examDate = req.body.examDate;
+	const examRound = req.body.examRound;
+	const isPublic = req.body.isPublic;
+	const publicLevel = req.body.publicLevel;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+	if (!regNumber.test(examDetailIdx)) throw new errorResponse(baseResponse.EXAMDETAIL_ERROR_TYPE, 400);
+	await examProvider.examDetailCheck(examDetailIdx);
+
+	const patchResult = await adminService.patchExamDetail(examDate, examRound, examDetailIdx, isPublic, publicLevel);
+	return res.send(patchResult);
+});
+
+/**
+ * API No.
+ * API Name : 객관식 문제 삭제 API
+ * [PATCH] /admins/delete/multipleProblem/:multipleProblemIdx
+ */
+exports.deleteMultipleProblem = asyncHandler(async function (req, res) {
+	const multipleProblemIdx = req.params.multipleProblemIdx;
+
+	await AdminAuthenticator.authenticateAdmin(req);
+	await examProvider.multipleProblemCheck(multipleProblemIdx);
+	const deleteResult = await adminService.deleteMultipleProblemIdx(multipleProblemIdx);
+	return res.send(deleteResult);
+});
+
+/**
+ * API No.
+ * API Name : 주관식 문제 삭제 API
+ * [PATCH] /admins/practical/delete/subjectiveProblem/:subjectiveProblemIdx
+ */
+exports.deleteSubjectiveProblem = asyncHandler(async function (req, res) {
+	const subjectiveProblemIdx = req.params.subjectiveProblemIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	await examProvider.subjectiveProblemCheck(subjectiveProblemIdx);
+
+	const deleteResult = await adminService.deleteSubjectiveProblemIdx(subjectiveProblemIdx);
+	return res.send(deleteResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 상세 삭제 API
+ * [PATCH] /admins/delete/ExamDetail/:examDetailIdx
+ */
+exports.deleteExamDetail = asyncHandler(async function (req, res) {
+	const examDetailIdx = req.params.examDetailIdx;
+
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+	if (!regNumber.test(examDetailIdx)) throw new errorResponse(baseResponse.EXAMDETAIL_ERROR_TYPE, 400);
+
+	await examProvider.examDetailCheck(examDetailIdx);
+
+	const deleteResult = await adminService.deleteExamDetailIdx(examDetailIdx);
+	return res.send(deleteResult);
+});
+
+/**
+ * API No.
+ * API Name : 특정 유저 작성글 조회 API
+ * [GET] /admins/userList/:userIdx/boardList
+ */
+exports.getUserBoardList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userIdx = req.params.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!userIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	const providerResult = await userProvider.userBoard(userIdx);
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 특정 유저 정보 조회 API
+ * [GET] /admins/userList/:userIdx/userInfo
+ */
+exports.getUserInfo = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userIdx = req.params.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!userIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	// await userProvider.userIdxCheck(userIdx);
+
+	const providerResult = await adminProvider.getUserInfo(userIdx);
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 특정 유저 시험기록 조회 API
+ * [GET] /admins/userList/:userIdx/examRecordList
+ */
+exports.getUserExamRecordList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userIdx = req.params.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!userIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	// await userProvider.userIdxCheck(userIdx);
+
+	const providerResult = await userProvider.userExamRecordList(userIdx);
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 특정 유저 시험기록 상세 조회 API
+ * [GET] /admins/userList/:userIdx/examRecordList/:userExamRecordIdx
+ */
+exports.getUserExamRecordInfo = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userIdx = req.params.userIdx;
+	const userExamRecordIdx = req.params.userExamRecordIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!userIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	// await userProvider.userIdxCheck(userIdx);
+
+	if (!userExamRecordIdx) return res.send(basickResponse(baseResponse.EXAMRECORD_EMPTY));
+	if (!regNumber.test(userExamRecordIdx)) return res.send(basickResponse(baseResponse.EXAMRECORD_ERROR_TYPE));
+	await userProvider.userExamRecordCheckIdx(userExamRecordIdx);
+
+	const providerResult = await userProvider.userExamDetailRecord(userExamRecordIdx);
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 회원 상태 수정 API
+ * [PATCH] /admins/userList/:userIdx/userStatus
+ */
+exports.updateUserStatus = asyncHandler(async function (req, res) {
+	const status = req.body.status;
+	const accessLevel = req.body.accessLevel;
+	const adminIdx = req.verifiedToken.userIdx;
+	const userIdx = req.params.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!userIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (!status) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (status != "N" && status != "M") throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	const userCheckResult = await userProvider.userIdxCheck(userIdx);
+	if (userCheckResult[0].status === "K") return res.send(basickResponse(baseResponse.USER_INVINCIBILITY));
+	if (userCheckResult[0].status == "M") await adminProvider.kingCheck(adminIdx);
+
+	if (accessLevel == null || accessLevel == undefined) return res.send(basickResponse(baseResponse.ACCESSLEVEL_EMPTY));
+	if (!(accessLevel < 5)) return res.send(basickResponse(baseResponse.ACCESSLEVEL_ERROR_TYPE));
+
+	const patchResult = await adminService.patchUserStatus(status, accessLevel, userIdx);
+	return res.send(patchResult);
+});
+
+/**
+ * API No.
+ * API Name : 유저 오답노트 목록 조회 API
+ * [GET] /admins/userList/:userIdx/reviewNote/exam
+ */
+exports.getUserRiviewNoteList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userIdx = req.params.userIdx;
+	let examSort = req.query.examSort;
+	let isValidExamSort = false;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!userIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	if (!examSort) examSort = "자격증";
+	else {
+		let examSortList = await adminProvider.getExamSortRef();
+
+		for (let i = 0; i < examSortList.result.length; i++) {
+			if (examSort == examSortList.result[i].examSortRef) {
+				isValidExamSort = true;
+			}
+		}
+
+		if (!isValidExamSort) return res.send(basickResponse(baseResponse.EXAMSORT_ERROR_TYPE));
+	}
+	const providerResult = await userProvider.userReviewNoteList(userIdx, examSort);
+	return res.send(providerResult);
+});
+
+/**
+ * @deprecated
+ * API No.
+ * API Name : 유저 오답노트 문제 조회 API
+ * [GET] /admins/userList/:userIdx/reviewNote/problem/:examDetailIdx
+ */
+exports.getUserRiviewNoteProblem = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userIdx = req.params.userIdx;
+	let examDetailIdx = req.params.examDetailIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!userIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+
+	if (!regNumber.test(examDetailIdx)) throw new errorResponse(baseResponse.EXAMDETAIL_ERROR_TYPE, 400);
+
+	await examProvider.examDetailCheck(examDetailIdx);
+
+	const reviewNoteExamProblemResult = await userProvider.reviewNoteExamProblem(userIdx, examDetailIdx);
+	return res.send(reviewNoteExamProblemResult);
+});
+
+/**
+ * @description Large ExamSort 조회
+ *
+ * @route [GET] /admins/largeExamSort
+ *
+ */
+exports.getLargeExamSort = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const largeExamSortList = await adminProvider.getLargeExamSort();
+
+	return res.send(largeExamSortList);
+});
+
+/**
+ * @description 문제 검색 api
+ * @route [GET] /admins/search/problem
+ *
+ * @query examSortIdx 시험분류idx Number
+ * @query q 검색어 String
+ * @query isSubjective 주관식 여부 0 or 1
+ *
+ * @access private
+ *
+ * @returns {Object} 문제 검색 결과
+ */
+exports.problemSearch = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examSortIdx = req.query.examSortIdx;
+	const searchString = req.query.q || "";
+
+	const isSubjective = req.query.isSubjective ? JSON.parse(req.query.isSubjective) : 0;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	let searchStringQuery = "";
+	let keywordList = searchString.trim().split(" ");
+
+	for (_ of keywordList) {
+		isSubjective
+			? (searchStringQuery += "AND sp.problem LIKE CONCAT('%', ?, '%') ")
+			: (searchStringQuery += "AND mp.problem LIKE CONCAT('%', ?, '%') ");
+	}
+
+	const searchProblemResult = isSubjective
+		? await adminProvider.searchSubjectiveProblem(examSortIdx, searchStringQuery, keywordList)
+		: await adminProvider.searchMultipleProblem(examSortIdx, searchStringQuery, keywordList);
+
+	return res.send(searchProblemResult);
+});
+
+/**
+ * @description 해설 검색 api
+ * @route [GET] /admins/search/solution
+ *
+ * @query examSortIdx 시험분류idx Number
+ * @query q 검색어 String
+ * @query isSubjective 주관식 여부 0 or 1
+ *
+ * @access private
+ *
+ * @returns {Object} 문제 검색 결과
+ */
+exports.solutionSearch = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examSortIdx = req.query.examSortIdx;
+	const searchString = req.query.q || "";
+	const isSubjective = req.query.isSubjective ? JSON.parse(req.query.isSubjective) : 0;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	let searchStringQuery = "";
+	let keywordList = searchString.trim().split(" ");
+
+	for (_ of keywordList) {
+		isSubjective
+			? (searchStringQuery += "AND sp.solution LIKE CONCAT('%', ?, '%') ")
+			: (searchStringQuery += "AND mp.solution LIKE CONCAT('%', ?, '%') ");
+	}
+
+	const searchProblemResult = isSubjective
+		? await adminProvider.searchSubjectiveProblem(examSortIdx, searchStringQuery, keywordList)
+		: await adminProvider.searchMultipleProblem(examSortIdx, searchStringQuery, keywordList);
+
+	return res.send(searchProblemResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 과목 검색 API
+ * [GET] /admins/search/ExamSubject
+ */
+exports.examSubjectSearch = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examSubjectName = req.query.examSubjectName;
+	let searchResult;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	searchResult = await adminProvider.getExamSubjectName("%" + examSubjectName + "%");
+
+	return res.send(searchResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 인덱스로 과목 검색 API
+ * [GET] /admins/search/ExamSubject/:examIdx
+ */
+exports.examSubjectSearchByExam = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examIdx = req.params.examIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examIdx) return res.send(basickResponse(baseResponse.EXAM_EMPTY));
+
+	await examProvider.examCheck(examIdx);
+
+	const searchResult = await adminProvider.getExamSubjectByExam(examIdx);
+
+	return res.send(searchResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 과목 생성 API
+ * [POST] /admins/upload/examSubject
+ */
+exports.uploadExamSubject = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { examSubjectName, passScore } = req.body;
+	//if(!examIdx)
+	//  throw new errorResponse(baseResponse.TOKEN_EMPTY,400)
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examSubjectName) return res.send(basickResponse(baseResponse.EXAMSUBJECTNAME_EMPTY));
+
+	if (!passScore) return res.send(basickResponse(baseResponse.SCORE_EMPTY));
+
+	const createResult = await adminService.createExamSubject(examSubjectName, passScore);
+
+	return res.send(createResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 과목 수정 API
+ * [PATCH] /admins/update/examSubject/:examSubjectIdx
+ */
+exports.updateExamSubject = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { examSubjectName, passScore } = req.body;
+	const examSubjectIdx = req.params.examSubjectIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examSubjectIdx) return res.send(basickResponse(baseResponse.EXAMSUBJECT_EMPTY));
+
+	if (!regNumber.test(examSubjectIdx)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+
+	await examProvider.examSubjectCheck(examSubjectIdx);
+
+	if (!examSubjectName) return res.send(basickResponse(baseResponse.EXAMSUBJECTNAME_EMPTY));
+
+	if (!passScore) return res.send(basickResponse(baseResponse.SCORE_EMPTY));
+
+	const updateResult = await adminService.patchExamSubject(examSubjectName, passScore, examSubjectIdx);
+
+	return res.send(updateResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 과목 번호 수정 API
+ * [PATCH] /admins/update/examSubject/:examSubjectIdx/subjectNum
+ */
+exports.updateExamSubjectNum = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examIdx = req.body.examIdx;
+	const examSubjectNum = req.body.examSubjectNum;
+	const examSubjectIdx = req.params.examSubjectIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examIdx) return res.send(basickResponse(baseResponse.EXAM_EMPTY));
+
+	if (!regNumber.test(examIdx)) return res.send(basickResponse(baseResponse.EXAM_ERROR_TYPE));
+
+	await examProvider.examCheck(examIdx);
+
+	if (!examSubjectIdx) return res.send(basickResponse(baseResponse.EXAMSUBJECT_EMPTY));
+
+	if (!regNumber.test(examSubjectIdx)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+
+	await examProvider.examSubjectCheck(examSubjectIdx);
+
+	const updateResult = await adminService.patchExamSubjectNum(examSubjectNum, examIdx, examSubjectIdx);
+
+	return res.send(updateResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 과목 삭제 API
+ * [PATCH] /admins/delete/examSubject/:examSubjectIdx
+ */
+exports.deleteExamSubject = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examSubjectIdx = req.params.examSubjectIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examSubjectIdx) return res.send(basickResponse(baseResponse.EXAMSUBJECT_EMPTY));
+
+	if (!regNumber.test(examSubjectIdx)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+
+	await examProvider.examSubjectCheck(examSubjectIdx);
+
+	await adminProvider.SubjectProblemCheck(examSubjectIdx);
+
+	const deleteResult = await adminService.deleteExamSubjectIdx(examSubjectIdx);
+
+	return res.send(deleteResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 구분 조회 API
+ * [GET] /admins/examSortRef
+ */
+exports.getExamSortRef = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const providerResult = await adminProvider.getExamSortRef();
+
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 종류 조회 API
+ * [GET] /admins/examSort
+ */
+exports.getExamSort = asyncHandler(async function (req, res) {
+	const examSortIdx = req.query.examSortLargeIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+	let examSortRefCondition = "WHERE M.status = 'N' and M.examSortType = 'M' ";
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!isNaN(examSortIdx) && examSortIdx) {
+		const checkResult = await adminProvider.examSortLargeCheck(examSortIdx);
+		if (checkResult[0].exist == 1) examSortRefCondition += ` and examSortRef = '${examSortIdx}'`;
+	} else {
+		examSortRefCondition += ` and M.examSortRef != 2 `;
+	}
+
+	const providerResult = await adminProvider.getExamSort(examSortRefCondition);
+
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 자격증/공기업 시험 분류 조회 API
+ * [GET] /admins/examSortList/?largeExamName="자격증 || 공기업 || 대기업"
+ * @param {string} largeExamName 자격증 공기업 대기업에 대한 시험 종류를 String 값으로 전달
+ */
+exports.getExamSortList = asyncHandler(async function (req, res) {
+	const largeExamName = req.query.largeExamName || "자격증"; // 디폴트로 해당 부분 자격증으로 등록
+
+	await AdminAuthenticator.authenticateAdmin(req);
+
+	const providerResult = await adminProvider.getExamSortList(largeExamName);
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 관리자 시험 이름 조회 API
+ * [GET] /admins/examName
+ */
+exports.getExamName = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examSortRef = req.query.examSortLargeIdx;
+	const examSort = req.query.examSortMediumIdx;
+	let examSortCondition = ` where es.examSortRef !=2 and es.examSortType = 'M' and es.status ='N' and e.status ='N' `;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (examSort) {
+		//if (!regKorean.test(examSort)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE,400)
+
+		examSortCondition += ` AND  e.examSortIdx = ${examSort}`;
+	} else if (examSortRef) {
+		//if (!regKorean.test(examSortRef)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE,400)
+		examSortCondition += ` AND examSortRef =   ${examSortRef}`;
+	}
+
+	const providerResult = await adminProvider.getExamName(examSortCondition);
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 관리자 시험정보 목록 조회 API
+ * [GET] /admins/examInfoList
+ */
+exports.getExamInfoList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+
+	// 현재 검색기능 사용 x 프론트에서 분류 통해 필터링
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const providerResult = await adminProvider.getExamInfoList();
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 관리자 기업 문제 관리 시험 목록 조회
+ * [GET] /admins/company/examInfoList
+ */
+
+/**
+ * API No.
+ * API Name : 시험 종류 생성 API
+ * [POST] /admins/examSort
+ */
+exports.createExamSort = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { examSortName, examSortRef, examSortType } = req.body;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examSortName) return res.send(basickResponse(baseResponse.EXAMSORT_EMPTY));
+
+	if (!examSortRef && examSortRef != null) return res.send(basickResponse(baseResponse.EXAMSORTREF_EMPTY));
+
+	if (!examSortType || (examSortType !== "M" && examSortType !== "S"))
+		return res.send(basickResponse(baseResponse.EXAMSORT_ERROR_TYPE));
+
+	await adminProvider.examSortNameCheck(examSortName, examSortType);
+
+	const serviceResult = await adminService.createExamSort(examSortName, examSortRef, examSortType);
+	return res.send(serviceResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 종류 삭제 API
+ * [PATCH] /admins/delete/examSort/:examSortIdx
+ */
+exports.deleteExamSort = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examSortIdx = req.params.examSortIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examSortIdx) return res.send(basickResponse(baseResponse.EXAMSORT_EMPTY));
+
+	await adminProvider.examSortIdxCheck(examSortIdx);
+
+	/**
+	 * @todo examByExamSortCheck 이부분에서 해당 examSortIdx가 존재하면 errorResponse를보내는데
+	 * 삭제하는 과정에서 이 부분은 의미가 없지 않나...? 해당 기능이 다를텐데
+	 */
+	await adminProvider.examByExamSortCheck(examSortIdx);
+
+	const serviceResult = await adminService.deleteExamSort(examSortIdx);
+
+	return res.send(serviceResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 종류 수정 API
+ * [PATCH] /admins/update/examSort/:examSortIdx
+ */
+exports.updateExamSort = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examSortIdx = req.params.examSortIdx;
+	const { examSortName, examSortRef, examSortType } = req.body;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examSortIdx) return res.send(basickResponse(baseResponse.EXAMSORT_EMPTY));
+
+	if (!examSortName) return res.send(basickResponse(baseResponse.MODEXAMSORT_EMPTY));
+
+	if (!examSortRef) return res.send(basickResponse(baseResponse.MODEXAMSORTREF_EMPTY));
+
+	await adminProvider.examSortIdxCheck(examSortIdx);
+
+	const updateExamSortParams = [examSortName, examSortRef, examSortType, examSortIdx];
+	const serviceResult = await adminService.updateExamSort(updateExamSortParams);
+	return res.send(serviceResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 구분 수정 API
+ * [PATCH] /admins/update/examSortRef/:examSortRef
+ */
+// exports.updateExamSortRef = asyncHandler(async function (req,res) {
+// 	const adminIdx = req.verifiedToken.userIdx;
+// 	const examSortRef = req.params.examSortRef;
+// 	const modExamSortRef = req.body.modExamSortRef;
+
+// 	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY,400)
+
+// 	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE,400)
+
+// 	const adminCheckResult =await adminProvider.adminCheck(adminIdx); if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+//
+
+// 	if (!modExamSortRef) return res.send(basickResponse(baseResponse.MODEXAMSORTREF_EMPTY));
+
+// 	const examSortRefCheckResult = await adminProvider.examSortRefCheck(examSortRef);
+
+// 	if (examSortRefCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.EXAMSORTREF_NOT_EXIST));
+
+// 	const serviceResult = await adminService.updateExamSortRef(examSortRef, modExamSortRef);
+// 	return res.send(serviceResult);
+// });
+
+/**
+ * API No.
+ * API Name : 회사 날짜 조회 API
+ * [GET] /admins/calendar
+ */
+exports.getCalendarList = asyncHandler(async function (req, res) {
+	const date = req.query.date;
+
+	if (!date) return res.send(basickResponse(baseResponse.DATE_EMPTY));
+
+	const providerResult = await adminProvider.getEnterpriseList(date);
+
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 회사 날짜 조회 API
+ * [GET] /admins/calendar/:enterpriseIdx
+ */
+exports.getCalendarInfo = asyncHandler(async function (req, res) {
+	const enterpriseIdx = req.params.enterpriseIdx;
+
+	if (!enterpriseIdx) return res.send(basickResponse(baseResponse.ENTERPRISE_EMPTY));
+
+	await adminProvider.enterpriseIdxCheck(enterpriseIdx);
+
+	const providerResult = await adminProvider.getEnterpriseInfo(enterpriseIdx);
+
+	return res.send(providerResult);
+});
+
+/**
+ * API No.
+ * API Name : 이미지 업로드 API
+ * [POST] /admins/upload/image
+ */
+
+exports.uploadImage = asyncHandler(async function (req, res) {
+	const files = req.files;
+	const adminIdx = req.verifiedToken.userIdx;
+	const { s3 } = require("../../config/s3");
+	const secret = require("../../config/secret");
+	let fileList = [];
+	try {
+		if (!files || files == undefined) return res.send(basickResponse(baseResponse.UPLOADFILE_EMPTY));
+		if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+		if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+		const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+		if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+		for (let object of files) {
+			let file = {
+				fileName: object.key,
+				fileUrl: object.location,
+			};
+			fileList.push(file);
+		}
+		return res.send(resultResponse(baseResponseStatus.SUCCESS, fileList));
+	} catch (error) {
+		let params,
+			Objects = new Array();
+
+		for (let object of files) {
+			let Object = {
+				Key: object.key,
+			};
+			Objects.push(Object);
+		}
+		params = {
+			Bucket: secret.bucket,
+			Delete: {
+				Objects: Objects,
+				Quiet: false,
+			},
+		};
+		s3.deleteObjects(params, function (err, data) {
+			if (err) logger.error(`App - s3 deleteObject error\n: ${err}`);
+		});
+		console.log(error);
+		logger.error(`App - getExamInfoList Provider error\n: ${error.message}`);
+		return basickResponse(baseResponse.DB_ERROR);
+	}
+});
+
+/**
+ * API No.
+ * API Name : 문제 액셀 다운로드 API
+ * [POST] /admins/download/:examDetailIdx
+ */
+
+exports.downloadProblemExcel = asyncHandler(async function (req, res) {
+	const Excel = require("exceljs");
+	const adminIdx = req.verifiedToken.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	await adminProvider.kingCheck(adminIdx);
+
+	const examDetailIdx = req.params.examDetailIdx;
+	//console.log(examDetailIdx);
+	const result = await adminProvider.getExcelProblemList(examDetailIdx);
+	if (result.length == 0 || result.isSuccess == false) return res.send(basickResponse(baseResponse.DOWNLOAD_ERROR));
+	//console.log("HI");
+	const fileName = await adminProvider.getExcelFileName(examDetailIdx);
+	let columns = [];
+
+	const workbook = new Excel.Workbook();
+	const worksheet = workbook.addWorksheet(fileName[0].fileName);
+	for (let object in result[0]) {
+		let column = {
+			header: object,
+			key: object,
+		};
+		columns.push(column);
+	}
+	worksheet.columns = columns;
+	for (let object of result) worksheet.addRow(object);
+	const newFileName = encodeURIComponent(fileName[0].fileName);
+	res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+	res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;  charset=utf-8");
+	res.setHeader("Content-Disposition", `attachment;filename*=UTF-8\'\'${newFileName}.xlsx`);
+	res.attachment(`${newFileName}.xlsx`);
+	await workbook.xlsx.write(res);
+	return res.status(200).end();
+});
+
+/**
+ * API No.
+ * API Name : 유저 검색 API
+ * [GET] /admins/searchUser
+ */
+exports.searchUser = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { searchRef, search } = req.query;
+	let like;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!searchRef) return res.send(basickResponse(baseResponse.SEARCHREF_EMPTY));
+	else if (searchRef != "userName" && searchRef != "userEmail" && searchRef != "nickName")
+		return res.send(basickResponse(baseResponse.SEARCHREF_ERROR_TYPE));
+	else like = searchRef + " like '%" + search + "%'";
+
+	const searchResult = await adminProvider.getSearchUser(like);
+	return res.send(searchResult);
+});
+
+/**
+ * API No.
+ * API Name : 유저 삭제 API
+ * [POST] /admins/deleteUser/:userIdx
+ */
+exports.deleteUser = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userIdx = req.params.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const userCheckResult = await userProvider.userIdxCheck(userIdx);
+	if (userCheckResult[0].status === "M") {
+		await adminProvider.kingCheck(adminIdx);
+	}
+	if (userCheckResult[0].status === "K") return res.send(basickResponse(baseResponse.USER_INVINCIBILITY));
+
+	const deleteResult = await adminService.deleteUser(userIdx);
+	return res.send(deleteResult);
+});
+
+/**
+ * API No.
+ * API Name : 복원 목록 조회 API
+ * [GET] /admins/restore
+ */
+exports.restoreList = asyncHandler(async function (req, res) {
+	const where = "";
+	const adminIdx = req.verifiedToken.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const restoreList = await adminProvider.getRestoreExamDetailList(where);
+	return res.send(restoreList);
+});
+
+/**
+ * API No.
+ * API Name : 복원 회차 생성 API
+ * [POST] /admins/upload/restoreExamDetail
+ */
+exports.createRestoreExamDetail = asyncHandler(async function (req, res) {
+	const { examIdx, restoreExamDate, restoreExamRound, thumbnail } = req.body;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examIdx) return res.send(basickResponse(baseResponse.EXAM_EMPTY));
+
+	if (!regNumber.test(examIdx)) return res.send(basickResponse(baseResponse.EXAM_ERROR_TYPE));
+
+	await examProvider.examCheck(examIdx);
+
+	if (!restoreExamDate) return res.send(basickResponse(baseResponse.DATE_EMPTY));
+
+	if (!regDate.test(restoreExamDate)) return res.send(basickResponse(baseResponse.DATE_NOT_MATCH));
+
+	if (!restoreExamRound) return res.send(basickResponse(baseResponse.ROUND_EMPTY));
+
+	const createResult = await adminService.createRestoreExamDetail(
+		examIdx,
+		restoreExamDate,
+		restoreExamRound,
+		thumbnail,
+	);
+	return res.send(createResult);
+});
+
+/**
+ * API No.
+ * API Name : 복원 회차 수정 API
+ * [PATCH] /admins/update/restoreExamDetail/:restoreExamDetailIdx
+ */
+exports.updateRestoreExamDetail = asyncHandler(async function (req, res) {
+	const restoreExamDetailIdx = req.params.restoreExamDetailIdx;
+	const { restoreExamDate, restoreExamRound, thumbnail, isPublic } = req.body;
+
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!restoreExamDetailIdx) return res.send(basickResponse(baseResponse.RESTORE_EXAM_EMPTY));
+
+	await adminProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!restoreExamDate) return res.send(basickResponse(baseResponse.DATE_EMPTY));
+
+	if (!regDate.test(restoreExamDate)) return res.send(basickResponse(baseResponse.DATE_NOT_MATCH));
+
+	if (!restoreExamRound) return res.send(basickResponse(baseResponse.ROUND_EMPTY));
+
+	if (isPublic != 0 && isPublic != 1) return res.send(basickResponse(baseResponse.ISPUBLIC_TYPE_ERROR));
+
+	const updateResult = await adminService.updateRestoreExamDetail(
+		restoreExamDate,
+		restoreExamRound,
+		thumbnail,
+		isPublic,
+		restoreExamDetailIdx,
+	);
+	return res.send(updateResult);
+});
+
+/**
+ * API No.
+ * API Name : 복원 회차 삭제 API
+ * [PATCH] /admins/delete/restoreExamDetail/:restoreExamDetailIdx
+ */
+exports.deleteRestoreExamDetail = asyncHandler(async function (req, res) {
+	const restoreExamDetailIdx = req.params.restoreExamDetailIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!restoreExamDetailIdx) return res.send(basickResponse(baseResponse.RESTORE_EXAM_EMPTY));
+
+	await adminProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const restoreList = await adminService.deleteRestoreExamDetail(restoreExamDetailIdx);
+	return res.send(restoreList);
+});
+
+/**
+ * API No.
+ * API Name : 검색 복원 문제 삭제 API
+ * [PATCH] /admins/delete/restoration/:restorationIdx
+ */
+exports.deleteRestoration = asyncHandler(async function (req, res) {
+	const restorationIdx = req.params.restorationIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!restorationIdx) return res.send(basickResponse(baseResponse.RESTORATION_EMPTY));
+
+	if (!regNumber.test(restorationIdx)) return res.send(basickResponse(baseResponse.RESTORATION_ERROR_TYPE));
+
+	await adminProvider.restorationIdxCheck(restorationIdx);
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const restoreList = await adminService.deleteRestoration(restorationIdx);
+	return res.send(restoreList);
+});
+
+/**
+ * API No.
+ * API Name : 복원 회차 문제 조회 API
+ * [GET] /admins/restoreExamDetail/:restoreExamDetailIdx
+ */
+exports.selectRestorationList = asyncHandler(async function (req, res) {
+	const restoreExamDetailIdx = req.params.restoreExamDetailIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!restoreExamDetailIdx) return res.send(basickResponse(baseResponse.RESTORE_EXAM_EMPTY));
+
+	await adminProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const restoreList = await adminProvider.getRestorationList(restoreExamDetailIdx);
+	return res.send(restoreList);
+});
+
+/**
+ * API No.
+ * API Name : 시험 정보 조회 API
+ * [GET] /admins/examInfo
+ */
+exports.selectExamInfo = asyncHandler(async function (req, res) {
+	const examIdx = req.params.examIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examIdx) return res.send(basickResponse(baseResponse.EXAM_EMPTY));
+
+	if (!regNumber.test(examIdx)) return res.send(basickResponse(baseResponse.EXAM_ERROR_TYPE));
+
+	await examProvider.examCheck(examIdx);
+
+	const restoreList = await adminProvider.getExamInfo(examIdx);
+	return res.send(restoreList);
+});
+
+/**
+ * API No.
+ * API Name : 검색 복원 문제 수정 API
+ * [PATCH] /admins/update/restoration/:restorationIdx
+ */
+exports.setRestoration = asyncHandler(async function (req, res) {
+	const { status, isPublic } = req.body;
+	let set = "";
+	const restorationIdx = req.params.restorationIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!restorationIdx) return res.send(basickResponse(baseResponse.RESTORATION_EMPTY));
+
+	if (!regNumber.test(restorationIdx)) return res.send(basickResponse(baseResponse.RESTORATION_ERROR_TYPE));
+
+	await adminProvider.restorationIdxCheck(restorationIdx);
+
+	if (status) set = ' status = "Y" ';
+	if (isPublic) set = ' isPublic = "' + isPublic + '"';
+	const restoreList = await adminService.setRestoration(set, restorationIdx);
+	return res.send(restoreList);
+});
+
+/**
+ * API No.
+ * API Name : 검색 복원 문제 댓글 삭제 API
+ * [PATCH] /admins/delete/restorationComment/:restorationCommentIdx
+ */
+exports.deleteRestorationComment = asyncHandler(async function (req, res) {
+	const restorationCommentIdx = req.params.restorationCommentIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!restorationCommentIdx) return res.send(basickResponse(baseResponse.RESTORATION_COMMENT_EMPTY));
+
+	if (!regNumber.test(restorationCommentIdx))
+		return res.send(basickResponse(baseResponse.RESTORATION_COMMENT_ERROR_TYPE));
+
+	await adminProvider.restorationCommentIdxCheck(restorationCommentIdx);
+
+	const restoreList = await adminService.deleteRestorationComment(restorationCommentIdx);
+	return res.send(restoreList);
+});
+
+/**
+ * API No.
+ * API Name : 복원 회차 수험표 조회 API
+ * [GET] /admins/restoreExamDetail/:restoreExamDetailIdx/admissionTicket
+ */
+exports.selectAdmissionTicketList = asyncHandler(async function (req, res) {
+	const restoreExamDetailIdx = req.params.restoreExamDetailIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!restoreExamDetailIdx) return res.send(basickResponse(baseResponse.RESTORE_EXAM_EMPTY));
+
+	await adminProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	let restoreList = await adminProvider.getRestorationTicketList(restoreExamDetailIdx);
+	if (!restoreList.result.length == 0) {
+		const examIdx = restoreList.result[0].examIdx;
+		const lDivision = await examProvider.getExamDivision(examIdx, "L");
+		const mDivision = await examProvider.getExamDivision(examIdx, "M");
+		restoreList.result[0].examSortRef = lDivision[0].examSortName;
+		restoreList.result[0].examSort = mDivision[0].examSortName;
+	}
+	return res.send(restoreList);
+});
+
+/**
+ * API No.
+ * API Name : 복원 회차 수험표 권한 변경 API
+ * [PATCH] /admins/update/admissionTicket/:examAdmissionTicketIdx
+ */
+exports.updateAdmissionTicketAuth = asyncHandler(async function (req, res) {
+	const examAdmissionTicketIdx = req.params.examAdmissionTicketIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+	const isAuthentication = req.body.isAuthentication;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (isAuthentication != 0 && isAuthentication != 1 && isAuthentication != 2)
+		return res.send(basickResponse(baseResponse.USER_NOT_EXIST)); //응답 문구 변경
+
+	const restoreList = await adminService.updateAdmissionTicketAuth(isAuthentication, examAdmissionTicketIdx);
+	return res.send(restoreList);
+});
+
+/**
+ * API No.
+ * API Name : 유저 생성 복원 문제 액셀 다운로드 API
+ * [POST] /admins/download/userCustomProblem/:restoreExamDetailIdx
+ */
+
+exports.downloadCustomProblem = asyncHandler(async function (req, res) {
+	const Excel = require("exceljs");
+	const adminIdx = req.verifiedToken.userIdx;
+	const restoreExamDetailIdx = req.params.restoreExamDetailIdx;
+	//const fileName = await adminProvider.getExcelFileName(examDetailIdx);
+	let columns = [];
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	await adminProvider.kingCheck(adminIdx);
+
+	if (!restoreExamDetailIdx) return res.send(basickResponse(baseResponse.RESTORE_EXAM_EMPTY));
+
+	await adminProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	const result = await adminProvider.getExcelCustomProblem(restoreExamDetailIdx);
+
+	if (result.isSuccess != true) return basickResponse(baseResponse.CUSTOM_PROBLEM_EMPTY);
+
+	const workbook = new Excel.Workbook();
+	const worksheet = workbook.addWorksheet("유저 복원 문제");
+	for (let object in result.result[0]) {
+		let column = {
+			header: object,
+			key: object,
+		};
+		columns.push(column);
+	}
+	worksheet.columns = columns;
+
+	for (let object of result.result) worksheet.addRow(object);
+
+	worksheet.columns.forEach(function (column, i) {
+		switch (i) {
+			case 0:
+				column.width = 8;
+				break;
+			case 1:
+				column.width = 70;
+				break;
+			case 2:
+				column.width = 13;
+				break;
+			case 3:
+				column.width = 13;
+				break;
+			case 4:
+				column.width = 13;
+				break;
+			case 5:
+				column.width = 13;
+				break;
+			case 6:
+				column.width = 40;
+				break;
+			case 7:
+				column.width = 90;
+				break;
+			case 8:
+				column.width = 12;
+				break;
+			case 9:
+				column.width = 7;
+				break;
+			case 10:
+				column.width = 19;
+				break;
+			case 11:
+				column.width = 11;
+				break;
+			default:
+				column.width = 10;
+		}
+	});
+
+	worksheet.columns.forEach(function (column, i) {
+		worksheet.getCell("B" + i).alignment = { wrapText: true };
+		worksheet.getCell("G" + i).alignment = { wrapText: true };
+		worksheet.getCell("H" + i).alignment = { wrapText: true };
+	});
+
+	const newFileName = encodeURIComponent("유저 복원 문제");
+	res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+	res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;  charset=utf-8");
+	res.setHeader("Content-Disposition", `attachment;filename*=UTF-8\'\'${newFileName}.xlsx`);
+	await workbook.xlsx.write(res);
+	return res.status(200).end();
+});
+
+/**
+ * API No.
+ * API Name : 특정 객관식 문제 조회 API
+ * [GET] /admins/problemInfo/:multipleProblemIdx
+ */
+exports.getProblemInfo = asyncHandler(async function (req, res) {
+	const multipleProblemIdx = req.params.multipleProblemIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!multipleProblemIdx) throw new errorResponse(baseResponse.PROBLEMIDX_EMPTY, 400);
+	if (!regNumber.test(multipleProblemIdx)) throw new errorResponse(baseResponse.PROBLEMIDX_ERROR_TYPE, 400);
+
+	await examProvider.multipleProblemCheck(multipleProblemIdx);
+
+	const problemInfo = await adminProvider.getMultipleProblemByIdx(multipleProblemIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, problemInfo));
+});
+
+/**
+ * API No.
+ * API Name : 특정 주관식 문제 조회 API
+ * [GET] /admins/problemInfo/:subjectiveProblemIdx
+ */
+exports.getSubjectiveProblemInfo = asyncHandler(async function (req, res) {
+	const subjectiveProblemIdx = req.params.subjectiveProblemIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!subjectiveProblemIdx) throw new errorResponse(baseResponse.PROBLEMIDX_EMPTY, 400);
+	if (!regNumber.test(subjectiveProblemIdx)) throw new errorResponse(baseResponse.PROBLEMIDX_ERROR_TYPE, 400);
+
+	await examProvider.subjectiveProblemCheck(subjectiveProblemIdx);
+
+	const subjectiveProblemInfo = await adminProvider.getSubjectiveProblemByIdx(subjectiveProblemIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, subjectiveProblemInfo));
+});
+
+/**
+ * API No.
+ * API Name : 관리자인지 권한 검사하는 api
+ * [GET] /admins/authority
+ */
+exports.checkAuthority = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	let isAdmin, isKing;
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	isAdmin = adminCheckResult[0].exist === 0 ? 0 : 1;
+
+	// TODO
+	try {
+		const kingIdxCheckResult = await adminProvider.kingCheck(adminIdx);
+		isKing = kingIdxCheckResult[0].exist === 0 ? 0 : 1;
+	} catch (error) {
+		if (error.code == 2128) isKing = 0;
+		else throw error;
+	}
+
+	return res.send(resultResponse(baseResponseStatus.SUCCESS, { isAdmin, isKing }));
+});
+
+/**
+ * API No.
+ * API Name : 복원 문제 댓글 공개/비공개 전환 API
+ * [PATCH] /admins/update/restorationComment/:restorationCommentIdx
+ */
+exports.setRestorationCommentIsPublic = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const restorationCommentIdx = req.params.restorationCommentIdx;
+	const isPublic = req.body.isPublic;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!restorationCommentIdx) return res.send(basickResponse(baseResponse.RESTORATION_COMMENT_EMPTY));
+	if (!regNumber.test(restorationCommentIdx))
+		return res.send(basickResponse(baseResponse.RESTORATION_COMMENT_ERROR_TYPE));
+
+	await restoreProvider.restorationCommentIdxCheck(restorationCommentIdx);
+
+	if (isPublic == undefined) return res.send(basickResponse(baseResponse.ISPUBLIC_EMPTY));
+
+	const updateResult = await adminService.updateRestorationCommentIsPublic(restorationCommentIdx, isPublic);
+	return res.send(updateResult);
+});
+
+/**
+ * API No.
+ * API Name : 수험표 삭제 API
+ * [PATCH]] /admins/delete/admissionTicket/:restoreExamDetailIdx
+ */
+exports.deleteAdmissionTicket = asyncHandler(async function (req, res) {
+	const examAdmissionTicketIdx = req.params.examAdmissionTicketIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const deleteResult = await adminService.deleteExamAdmissionTicket(examAdmissionTicketIdx);
+
+	return res.send(deleteResult);
+});
+
+/**
+ * API No.
+ * API Name : 전기기사 전기공사기사 회차별 동기화 API
+ * [PATCH]] /admins/synchronize/:examDetailIdx
+ */
+exports.synchronize = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examDetailIdx = req.params.examDetailIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+
+	await examProvider.checkEngineerExamDetailIdx(examDetailIdx);
+
+	const examDetailResult = await examProvider.getExamDetailBasicInfo(examDetailIdx);
+
+	const engineerExamDate = examDetailResult[0].examDate;
+	const engineerExamRound = examDetailResult[0].examRound;
+
+	const workExamDetailIdxResult = await examProvider.selectWorkEngineerExamDetailIdx(
+		engineerExamDate,
+		engineerExamRound,
+	);
+
+	if (workExamDetailIdxResult.length === 0) return res.send(basickResponse(baseResponse.ENGINEER_WORK_ERROR));
+	const workExamDetailIdx = workExamDetailIdxResult[0].examDetailIdx;
+
+	const copyTargetResult = await examProvider.selectEngineerDataset(examDetailIdx);
+	const workMultipleProblemIdxSet = await examProvider.selectworkEngineerIdxSet(workExamDetailIdx);
+	const updateResult = await adminService.updateWorkMultipleProblem(
+		copyTargetResult,
+		workMultipleProblemIdxSet,
+		workExamDetailIdx,
+	);
+
+	return res.send(updateResult);
+});
+
+/**
+ * API No.
+ * API Name : 카테고리 조회 API
+ * [GET] /admins/category/:multipleProblemIdx
+ */
+exports.selectCategory = asyncHandler(async function (req, res) {
+	const multipleProblemIdx = req.params.multipleProblemIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (multipleProblemIdx && multipleProblemIdx != "0") {
+		await examProvider.multipleProblemCheck(multipleProblemIdx);
+	}
+
+	const categoryResult = await adminProvider.getCategoryList(multipleProblemIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, categoryResult));
+});
+
+/**
+ * API No.
+ * API Name : 카테고리 삽입 API
+ * [POST] /admins/category
+ */
+exports.insertCategory = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { categoryName, categoryType } = req.body;
+	let categoryRef = req.body.categoryRef;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (categoryType !== "L" && categoryType !== "M" && categoryType !== "S" && categoryType !== "XS")
+		return res.send(basickResponse(baseResponse.CATEGORY_ERROR_TYPE));
+
+	if (categoryType == "L") categoryRef = null;
+	else {
+		const categoryRefInfo = await adminProvider.getCategoryInfo(categoryRef);
+		if (!categoryRef) return res.send(basickResponse(baseResponse.CATEGORYREF_EMPTY));
+		await adminProvider.categoryCheck(categoryRef);
+		if (
+			(categoryType == "M" && categoryRefInfo.categoryType != "L") ||
+			(categoryType == "S" && categoryRefInfo.categoryType != "M") ||
+			(categoryType == "XS" && categoryRefInfo.categoryType != "S")
+		)
+			return res.send(basickResponse(baseResponse.CATEGORY_ERROR_TYPE));
+	}
+	const categoryResult = await adminService.insertCategory(categoryName, categoryRef, categoryType);
+	return res.send(categoryResult);
+});
+
+/**
+ * API No.
+ * API Name : 카테고리 수정 API
+ * [PATCH] /admins/update/category/:categoryIdx
+ */
+exports.updateCategory = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const categoryIdx = req.params.categoryIdx;
+	const { categoryName, categoryRef, categoryType } = req.body;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!categoryIdx) return res.send(basickResponse(baseResponse.CATEGORY_EMPTY));
+
+	if (!regNumber.test(categoryIdx)) return res.send(basickResponse(baseResponse.CATEGORY_ERROR_TYPE));
+
+	await adminProvider.categoryCheck(categoryIdx);
+
+	if (categoryType !== "L" && categoryType !== "M" && categoryType !== "S" && categoryType !== "XS")
+		return res.send(basickResponse(baseResponse.CATEGORY_ERROR_TYPE));
+
+	const categoryRefInfo = await adminProvider.getCategoryInfo(categoryRef);
+
+	if (categoryType == "L") categoryRef = null;
+	else {
+		if (!categoryRef) return res.send(basickResponse(baseResponse.CATEGORYREF_EMPTY));
+		const categoryRefCheckResult = await adminProvider.categoryCheck(categoryRef);
+		if (categoryRefCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.CATEGORYREF_NOT_EXIST));
+		if (
+			(categoryType == "M" && categoryRefInfo.categoryType != "L") ||
+			(categoryType == "S" && categoryRefInfo.categoryType != "M") ||
+			(categoryType == "XS" && categoryRefInfo.categoryType != "S")
+		)
+			return res.send(basickResponse(baseResponse.CATEGORY_ERROR_TYPE));
+	}
+
+	const categoryResult = await adminService.updateCategory(categoryName, categoryRef, categoryType, categoryIdx);
+	return res.send(categoryResult);
+});
+
+/**
+ * API No.
+ * API Name : 카테고리 삭제 API
+ * [PATCH] /admins/delete/category/:categoryIdx
+ */
+exports.deleteCategory = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const categoryIdx = req.params.categoryIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!categoryIdx) return res.send(basickResponse(baseResponse.CATEGORY_EMPTY));
+
+	if (!regNumber.test(categoryIdx)) return res.send(basickResponse(baseResponse.CATEGORY_ERROR_TYPE));
+
+	await adminProvider.problemCategoryIdxCheck(categoryIdx);
+
+	await adminProvider.categoryCheck(categoryIdx);
+
+	const categoryResult = await adminService.deleteCategory(categoryIdx);
+	return res.send(categoryResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 회차 카테고리 조회 API
+ * [GET] /admins/examDetail/:examDetailIdx/category
+ */
+exports.examDetailCategoryList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examDetailIdx = req.params.examDetailIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+	if (!regNumber.test(examDetailIdx)) throw new errorResponse(baseResponse.EXAMDETAIL_ERROR_TYPE, 400);
+	await examProvider.examDetailCheck(examDetailIdx);
+
+	const result = await adminProvider.getExamDetailCategoryList(examDetailIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, result));
+});
+
+/**
+ * API No.
+ * API Name : 공기업 조회 API
+ * [GET] /admins/publicCompany
+ */
+exports.publicCompany = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { companySortLabel } = req.query;
+	const examSortIdx = req.query.publicCompanySortIdx;
+	let where;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (examSortIdx && examSortIdx != "") where = `where S.status = 'N' and M.examSortIdx = ${examSortIdx}`;
+	else where = `where S.status = 'N' and L.examSortName = '공기업'`;
+	const result = await adminProvider.getPublicCompany(where, companySortLabel);
+	return res.send(result);
+});
+
+/**
+ * API No.
+ * API Name : 기업 조회 API
+ * @changeFrom publicCompany
+ * @author 김기창
+ */
+exports.getCompanys = asyncHandler(async function (req, res) {
+	const { companyType } = req.query;
+
+	// await AdminAuthenticator.authenticateAdmin(req);
+
+	const getCompanysResult = await adminProvider.getCompanys(companyType);
+	return res.send(getCompanysResult);
+});
+
+/**
+ * API No.
+ * API Name : 공기업 수정 API
+ * [GET] /admins/update/publicCompany
+ */
+exports.updatePublicCompany = asyncHandler(async function (req, res) {
+	const { publicCompanyIdx, publicCompanySortIdx, publicCompanySortName, companyThumbnail } = req.body;
+	const { companySortLabel } = req.query;
+
+	await AdminAuthenticator.authenticateAdmin(req);
+
+	if (!publicCompanySortIdx) return res.send(basickResponse(baseResponse.USER_NOT_EXIST)); // 문구 수정
+
+	if (!publicCompanySortName) return res.send(basickResponse(baseResponse.USER_NOT_EXIST)); // 문구 수정
+
+	//const companyNameCheckResult = await adminProvider.publicCompanyNameCheck(publicCompanySortName);
+	//if (companyNameCheckResult[0].exist === 1) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+
+	await adminProvider.examSortIdxCheck(publicCompanySortIdx);
+
+	const updateResult = await adminService.updatePublicCompany(
+		publicCompanyIdx,
+		publicCompanySortIdx,
+		publicCompanySortName,
+		companyThumbnail,
+		companySortLabel,
+	);
+	return res.send(updateResult);
+});
+
+/**
+ * API No.
+ * API Name : 기업 회사 수정 API
+ * [GET] /api/admins/company/update/?prevSorIdx=?&prevCompanyName=?
+ * @changeFrom updatePublicCompany
+ * @param {Number} prevSortIdx 이전의 examSortIdx_S
+ * @param {String} prevCompanyName 이전의 기업이름
+ * @body {Number} companyExamFieldIdx 소분류
+ * @body {String} companyName 기업 이름
+ * @body {String} companyThumbnail 기업 이미지
+ *
+ *
+ */
+exports.updateCompany = asyncHandler(async function (req, res) {
+	// await AdminAuthenticator.authenticateAdmin(req);
+	const { prevSortIdx, prevCompanyName } = req.query;
+	const { companyExamFieldIdx, companyName, companyThumbnail } = req.body;
+
+	if (!prevSortIdx) throw new errorResponse(baseResponse.ESSENTIAL_EMPTY, 400);
+	if (!prevCompanyName) throw new errorResponse(baseResponse.ESSENTIAL_EMPTY, 400);
+
+	if (!companyExamFieldIdx) throw new errorResponse(baseResponse.ESSENTIAL_EMPTY, 400);
+	if (!companyName) throw new errorResponse(baseResponse.ESSENTIAL_EMPTY, 400);
+
+	const updateResult = await adminService.updateCompany(
+		prevSortIdx,
+		prevCompanyName,
+		companyExamFieldIdx,
+		companyName,
+		companyThumbnail,
+	);
+	return res.send(updateResult);
+});
+
+/**
+ * API No.
+ * API Name : 공기업 시험 조회 API
+ * [GET] /admins/publicCompany/Exam
+ */
+exports.publicCompanyExam = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examSortIdx = req.query.publicCompanyIdx;
+	let where = "where status = 'N'";
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (examSortIdx) where += ` and examSortIdx = ${examSortIdx} `;
+	else
+		where += `and examSortIdx in (
+		with recursive CTE AS   (SELECT 
+					examSortIdx, examSortType
+					FROM ExamSort 
+					WHERE examSortIdx = 2
+					UNION ALL
+					SELECT
+					a.examSortIdx, a.examSortType
+					FROM ExamSort a
+					INNER JOIN CTE b ON a.examSortRef = b.examSortIdx 
+					where a.status = 'N'
+					)
+					SELECT examSortIdx FROM CTE where examSortType = 'S')`;
+	const result = await adminProvider.getExamByPublicCompany(where);
+	return res.send(result);
+});
+
+/**
+ * API No.
+ * API Name : 기업의 회사관리 조회 API ( 현재로선 공기업과 대기업 분류)
+ * [GET] /admins/publicCompany/management
+ */
+exports.publicCompanyManagement = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { companySortLabel } = req.query; // 공기업인지 대기업인지 분리하기 위해서
+	const where = `where S.status = 'N' and L.examSortIdx = 2 and ci.companySortLabel = ${companySortLabel || 0}`;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const result = await adminProvider.getPublicCompanyManagement(where);
+	return res.send(result);
+});
+
+/**
+ * API No.
+ * API Name : 기업의 회사관리 조회 API
+ * [GET] /api/admins/company/management/?companyType=
+ * @changeFrom publicCompanyManagement
+ * @param {String} companyType 기업이 공기업인지 대기업인지 판단하기 위함.
+ * @author 김기창
+ *
+ */
+exports.getCompanyList = asyncHandler(async function (req, res) {
+	const { companyType } = req.query;
+	if (!companyType) throw new errorResponse(baseResponse.COMPANY_TYPE_EMPTY, 400);
+
+	// await AdminAuthenticator.authenticateAdmin(req); // admin 인증 관련 class
+	const getCompanyListResult = await adminProvider.getCompanyList(companyType);
+	return res.send(getCompanyListResult);
+});
+
+/**
+ * API No.
+ * API Name : 공기업 회사 삭제 API
+ * [PATCH] /admins/delete/publicCompany/:publicComapanyIdx
+ */
+exports.deletepublicCompany = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const examSortIdx = req.params.publicComapanyIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!examSortIdx) return res.send(basickResponse(baseResponse.EXAMSORT_EMPTY));
+
+	await adminProvider.publicCompanyCheck(examSortIdx);
+
+	await adminProvider.examByExamSortCheck(examSortIdx);
+
+	const serviceResult = await adminService.deletePublicCompany(examSortIdx);
+
+	return res.send(serviceResult);
+});
+
+/**
+ * API No.
+ * API Name : 기업 회사 삭제 API
+ * [PATCH] /admins/company/delete/?companyName=?&companyExamFieldIdx=?
+ * @changeFrom deletepublicCompany
+ */
+exports.deleteCompany = asyncHandler(async function (req, res) {
+	// await AdminAuthenticator.authenticateAdmin(req); // admin 인증 관련 class
+	const { companyName, companyExamFieldIdx } = req.query;
+	if (!companyExamFieldIdx) throw new errorResponse(baseResponse.ESSENTIAL_EMPTY, 400);
+	if (!companyName) throw new errorResponse(baseResponse.ESSENTIAL_EMPTY, 400);
+
+	const deleteCompanyResult = await adminService.deleteCompany(companyName, companyExamFieldIdx);
+	return res.send(deleteCompanyResult);
+});
+
+/**
+ * API No.
+ * API Name : 상품 생성 API
+ * [POST] /admins/upload/product
+ */
+exports.createProduct = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const {
+		productRef,
+		productName,
+		productThumbnail,
+		price,
+		discountPrice,
+		shortDescription,
+		generalDescription,
+		detailDescription,
+		duration,
+		depth,
+		examDetailList,
+		examField,
+	} = req.body;
+
+	//	console.log(req.body);
+	if (isNaN(productRef)) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+	if (!productName) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+	if (isNaN(depth)) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+	// if (!productUrlList) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+	if (!examDetailList) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	await Promise.all(
+		examDetailList?.map(async (v, i, a) => {
+			if (!v) throw new Error("Bad Request Err");
+			if (!regNumber.test(v)) throw new Error("Bad Request Err");
+			await examProvider.examDetailCheck(v);
+		}),
+	);
+	//data가 빈문자열이 아니면 true를 반납한다.
+	const checkParams = (data) => data !== "";
+
+	let insertProductParams = [
+		productName,
+		checkParams(productThumbnail) ? productThumbnail : null,
+		checkParams(price) ? price : null,
+		checkParams(discountPrice) ? discountPrice : null,
+		checkParams(shortDescription) ? shortDescription : null,
+		checkParams(generalDescription) ? generalDescription : null,
+		checkParams(detailDescription) ? detailDescription : null,
+		checkParams(duration) ? duration : null,
+		depth,
+	];
+	// console.log(insertProductParams);
+	// console.log("examDetailList : ", examDetailList);
+
+	const createResult = await adminService.createProduct(
+		insertProductParams,
+		productRef,
+		//productUrlList,
+		examDetailList,
+		examField,
+	);
+
+	return res.send(createResult);
+});
+
+/**
+ * API No.
+ * API Name : 상품 수정 API
+ * [PATCH] /admins/update/product/:productIdx
+ */
+exports.updateProduct = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const productIdx = req.params.productIdx;
+	const {
+		productName,
+		productThumbnail,
+		price,
+		discountPrice,
+		shortDescription,
+		generalDescription,
+		detailDescription,
+		duration,
+		depth,
+		//productUrlList,
+		examDetailList,
+	} = req.body;
+
+	if (!productIdx) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+	if (!productName) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+	if (isNaN(depth)) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+	// if (!productUrlList) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+	if (!examDetailList) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	// if (!price) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+	// if (!discountPrice) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	await Promise.all(
+		examDetailList.map(async (idx) => {
+			await examProvider.examDetailCheck(idx);
+		}),
+	);
+
+	// examDetailList.forEach(async (obj) => {
+	// 	if (!obj) throw new Error("Bad Request Error");
+	// 	if (!regNumber.test(obj)) throw new Error("Bad Request Error");
+	// 	console.log(obj);
+	// 	await examProvider.examDetailCheck(obj);
+	// });
+	const checkParams = (data) => data !== "";
+	let updateProductParams = [
+		productName,
+		checkParams(productThumbnail) ? productThumbnail : null,
+		checkParams(price) ? price : null,
+		checkParams(discountPrice) ? discountPrice : null,
+		checkParams(shortDescription) ? shortDescription : null,
+		checkParams(generalDescription) ? generalDescription : null,
+		checkParams(detailDescription) ? detailDescription : null,
+		checkParams(duration) ? duration : null,
+		depth,
+		productIdx,
+	];
+	const updateResult = await adminService.updateProduct(updateProductParams, examDetailList);
+
+	return res.send(updateResult);
+});
+
+/**
+ * API Name : 상품 삭제 API
+ * [POST]] /admins/store/category
+ * author : 김기창
+ */
+exports.deleteProduct = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx; //admin 토큰을 받아오는것
+	const productIdx = req.params.productIdx;
+
+	if (!productIdx) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY));
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const result = await adminService.deleteProduct(productIdx);
+	return res.send(result);
+});
+
+/*
+ * API Name : 상품 카테고리 조회 API
+ * [GET] /admins/store/category
+ */
+exports.storeCategoryList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const result = await adminProvider.getStoreCategoryList();
+	return res.send(result);
+});
+
+/*
+ * API Name : 전체 상품 조회 API
+ * [GET] /admins/store/product
+ */
+exports.storeProductList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const result = await adminProvider.getStoreProductList();
+	return res.send(result);
+});
+
+exports.storeAuthProductList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const result = await adminProvider.getStoreAuthProductList();
+	return res.send(resultResponse(baseResponse.GET_PRODUCT_SUCCESS, result));
+});
+
+/*
+ * API Name : 판매 내역 조회 API
+ * [GET] /admins/store/sell
+ */
+exports.sellList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const result = await adminProvider.getSellList();
+	return res.send(result);
+});
+
+exports.searchSellList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const searchType = req.query.searchType;
+	const searchValue = req.query.searchValue;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const result = await adminProvider.searchSellList(searchType, searchValue);
+	return res.send(result);
+});
+/*
+ * API Name : 판매 내역 상세 조회 API
+ * [GET] /api/admins/store/sell
+ */
+exports.sellDetailList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const paymentIdx = req.query.paymentIdx;
+	if (!adminIdx) return res.send(basickResponse(baseResponse.TOKEN_EMPTY));
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminIdxCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminIdxCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+
+	const result = await adminProvider.getSellDetailList(paymentIdx);
+	return res.send(result);
+});
+
+/*
+ * API Name : 유저 구매 및 상품 사용 내역 조회 API
+ * [GET] /admins/store/userBuy/:userIdx
+ */
+exports.userBuyList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userIdx = req.params.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!userIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	// await userProvider.userIdxCheck(userIdx);
+
+	const result = await adminProvider.getUserBuyList(userIdx);
+	return res.send(result);
+});
+
+/*
+ * API Name : 구매 상품 환불정보 수정 API
+ * [POST] /api/admins/update/productPayment/refund/:paymentProductIdx
+ */
+exports.paymentProductRefund = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { paymentProductIdx } = req.params;
+	const { userIdx, productIdx, refundPrice, refundReason, refundAt, isRefund } = req.body;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const updatePaymentProductParams =
+		isRefund == 1
+			? [refundPrice, refundReason, refundAt, isRefund, paymentProductIdx]
+			: [null, null, null, 0, paymentProductIdx];
+
+	const deleteUserAuthParams = [userIdx, productIdx];
+	const result = await adminService.updatePaymentProductRefund(updatePaymentProductParams, deleteUserAuthParams);
+	await channelTalkService.updateChannelTalkUserProductInfo(userIdx);
+	return res.send(result);
+});
+
+/*
+ * API Name : 포인트 생성 api
+ * [POST] /api/admins/upload/point
+ */
+exports.createPoint = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { point, pointName } = req.body;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const result = await adminService.createPoint(point, pointName);
+	return res.send(result);
+});
+
+/*
+ * API Name : 포인트 수정 api
+ * [PATCH] /api/admins/update/point/:pointIdx
+ */
+exports.updatePoint = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const pointIdx = req.params.pointIdx;
+	const { point, pointName } = req.body;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!pointIdx || !point || !pointName) return res.send(basickResponse(baseResponse.POINT_INFO_NOT_MATCH));
+
+	const pointInfo = await adminProvider.getPoint(pointIdx);
+	if (!pointInfo.length || pointInfo[0].status == "Y")
+		return res.send(basickResponse(baseResponse.POINT_INFO_NOT_MATCH));
+
+	const result = await adminService.updatePoint(pointName, point, pointIdx);
+	return res.send(result);
+});
+
+/*
+ * API Name : 포인트 목록 조회 api
+ * [GET] /api/admins/point
+ */
+exports.getPointList = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const result = await adminProvider.getPointList();
+	return res.send(result);
+});
+
+/*
+ * API Name : 유저 포인트 부여 api
+ * [POST] /api/admins/userPoint/:userList
+ */
+exports.createUserPoint = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userList = req.body.userList;
+	const { pointIdx, point, pointSecretKey, reason } = req.body;
+	let insertUserPointParams = [],
+		insertUserPointLog = [];
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (pointSecretKey != secret.pointSecretKey) return res.send(basickResponse(baseResponse.SECRET_KEY_ERROR));
+	if (!pointIdx || !point) return res.send(basickResponse(baseResponse.POINT_INFO_NOT_MATCH));
+	const pointInfo = await adminProvider.getPoint(pointIdx);
+	if (!pointInfo.length || pointInfo[0].point !== point)
+		return res.send(basickResponse(baseResponse.POINT_INFO_NOT_MATCH));
+	if (typeof userList !== "object") throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+	await Promise.all(
+		userList.map((v, i, a) => {
+			if (!regNumber.test(v)) throw new Error("Bad Request Err");
+		}),
+		//userListParams = "( '" + userList.join("', ' ") + "' )"
+	).catch((err) => {
+		return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+	});
+	// const userListParams = userList.unshift("User");
+	// console.log(userList);
+	// console.log(userListParams);
+	const userListInfo = await userProvider.userListCheck(userList);
+	if (userList.length !== userListInfo.length) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+	await Promise.all(
+		userListInfo.map(async (v, i, a) => {
+			if (v.status == "Y") throw new Error("Bad Request Err");
+			insertUserPointParams.push([v.userIdx, pointIdx, adminIdx, reason]);
+			insertUserPointLog.push([v.userIdx, "A", pointInfo[0].pointName, point]);
+		}),
+	).catch((err) => {
+		return res.send(basickResponse(baseResponse.USER_QUIT));
+	});
+
+	const result = await adminService.createUserPoint(insertUserPointParams, insertUserPointLog, userList, point);
+	return res.send(result);
+});
+/*
+ * API Name : 유저 포인트 로그 조회 api
+ * [GET] /api/admins/userList/:userIdx/pointLog
+ */
+exports.getUserPointLog = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userIdx = req.params.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+	// await userProvider.userIdxCheck(userIdx);
+
+	const result = await userProvider.userPointLogs(userIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, result));
+});
+
+/*
+ * API Name : 유저 상품 이용 로그 조회 api
+ * [GET] /api/admins/userList/:userIdx/authLog
+ */
+exports.getUserAuthLog = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userIdx = req.params.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	// await userProvider.userIdxCheck(userIdx);
+
+	const result = await userProvider.userAuthLogs(userIdx);
+
+	return res.send(result);
+});
+
+/*
+ * API Name : 유저 포인트 관리자 지급 로그 조회 api
+ * [GET] /api/admins/pointLog
+ */
+exports.getAdminPointLog = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const result = await adminProvider.adminPointLogs();
+	return res.send(result);
+});
+
+exports.updateFrequency = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+
+	// history는 배열형태로 준다.
+	const { problemIdx, subjectiveLabel, history } = req.body;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const result = await adminService.updateFrequency(problemIdx, subjectiveLabel, history);
+
+	return res.send(result);
+});
+
+// 자격증 분류 순서 수정
+exports.updateExamOrder = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { prioArr } = req.body;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const isExam = Object.keys(prioArr[0]).includes("examIdx");
+	if (!isExam) return res.send(basickResponse(baseResponse.EXAM_ERROR_TYPE));
+
+	const checkExamResult = await adminProvider.ExamCheck(prioArr);
+	if (checkExamResult.includes(0)) {
+		res.send(basickResponse(baseResponse.EXAM_NOT_EXIST));
+	} else {
+		const orderResult = await adminService.updateExamOrder(prioArr);
+		return res.send(orderResult);
+	}
+});
+
+// 공기업 분류 순서 수정
+exports.updateCompanyOrder = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { prioArr } = req.body;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const isCompany = Object.keys(prioArr[0]).includes("companyIdx");
+	if (!isCompany) return res.send(basickResponse(baseResponse.PUBLICCOMPANY_ERROR));
+
+	const checkCompanyResult = await adminProvider.CompanyCheck(prioArr);
+	if (checkCompanyResult.includes(0)) {
+		res.send(basickResponse(baseResponse.PUBLICCOMPANY_NOT_EXIST));
+	} else {
+		const orderResult = await adminService.updateCompanyOrder(prioArr);
+		return res.send(orderResult);
+	}
+});
+
+// 상품 분류 순서 수정
+exports.updateProductOrder = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { prioArr } = req.body;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const isProduct = Object.keys(prioArr[0]).includes("productIdx");
+	if (!isProduct) return res.send(basickResponse(baseResponse.PRODUCT_ERROR));
+
+	const checkProductResult = await adminProvider.ProductCheck(prioArr);
+	if (checkProductResult.includes(0)) {
+		res.send(basickResponse(baseResponse.PRODUCT_NOT_EXIST));
+	} else {
+		const orderResult = await adminService.updateProductOrder(prioArr);
+		return res.send(orderResult);
+	}
+});
+
+// 유저 회원 정지
+exports.suspendUser = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userIdx = req.query.userIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	await adminService.suspendUser(userIdx);
+	return res.send(basickResponse(baseResponse.SUSPEND_SUCCESS));
+});
+
+exports.suspendUserProductAuth = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userAuthIdx = req.query.userAuthIdx;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (!userAuthIdx) return res.send(basickResponse(baseResponse.EMPTY_USERAUTHIDX));
+
+	const userIdxResult = await userProvider.getUserIdxByUserAuthIdx(userAuthIdx);
+
+	// 권한 회수
+	await adminService.suspendProductAuth(userAuthIdx);
+
+	await channelTalkService.updateChannelTalkUserProductInfo(userIdxResult[0].userIdx);
+	return res.send(basickResponse(baseResponse.SUSPEND_PRODUCTAUTH_SUCCESS));
+});
+
+exports.updateUserProductAuth = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const userAuthIdx = req.body.userAuthIdx;
+	const expireAt = req.body.expireAt;
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	await adminService.updateProductAuth(userAuthIdx, expireAt);
+	return res.send(basickResponse(baseResponse.UPDATE_PRODUCTAUTH_SUCCESS));
+});
+
+exports.giveUserProductAuth = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+
+	const { userList, productIdx, authKey, expireAt } = req.body;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	if (userList.length === 0) return res.send(basickResponse(baseResponse.USER_LIST_EMPTY));
+	if (!productIdx) return res.send(basickResponse(baseResponse.EMPTY_PRODUCTIDX));
+	if (!expireAt) return res.send(basickResponse(baseResponse.EMPTY_EXPIREAT));
+	if (authKey !== secret.userProductAuthKey) return res.send(basickResponse(baseResponse.SECRET_PRODUCTKEY_ERROR));
+
+	await adminService.giveUserProductAuth(userList, productIdx, expireAt, adminIdx);
+	return res.send(basickResponse(baseResponse.GIVE_PRODUCTAUTH_SUCCESS));
+});
+
+exports.searchAuthorizationLog = asyncHandler(async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const searchType = req.query.searchType;
+	const searchValue = req.query.searchValue;
+	const infoType = req.query.infoType;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const result = await adminProvider.searchAuthorLogList(searchType, searchValue, infoType);
+	return res.send(resultResponse(baseResponse.GET_AUTHORIZATIONLOG_SUCCESS, result));
+});
diff --git a/_old/src/Admin/adminDao.js b/_old/src/Admin/adminDao.js
new file mode 100644
index 0000000..9faa2fa
--- /dev/null
+++ b/_old/src/Admin/adminDao.js
@@ -0,0 +1,3430 @@
+const { async } = require("rxjs");
+
+//유저 패스워드 초기화
+async function initPassword(connection, userIdx, password) {
+	const initPasswordQuery = `
+		UPDATE User SET userPassword = ?  WHERE userIdx = ?;
+	`;
+	const [initPasswordRow] = await connection.query(initPasswordQuery, [password, userIdx]);
+	return initPasswordRow;
+}
+
+// 어드민 체크
+async function checkAdmin(connection, userIdx) {
+	const checkAdminQuery = `
+       select exists(select userIdx from User where userIdx = ? and (status = 'M' or status = 'K')) as exist;
+       `;
+	const [checkAdminRow] = await connection.query(checkAdminQuery, userIdx);
+	return checkAdminRow;
+}
+
+// 어드민 체크
+async function checkKing(connection, userIdx) {
+	const checkAdminQuery = `
+       select exists(select userIdx from User where userIdx = ? and status = 'K') as exist;
+       `;
+	const [checkAdminRow] = await connection.query(checkAdminQuery, userIdx);
+	return checkAdminRow;
+}
+
+// 시험 체크
+
+async function getExamIdx(connection, examname) {
+	const getExamIdxQuery = `
+		select examIdx from Exam where examName = ? and status = 'N'
+     `;
+	const [checkExamNameRow] = await connection.query(getExamIdxQuery, examname);
+	return checkExamNameRow;
+}
+
+// 시험이름 중복 체크
+async function checkExamNameDuplication(connection, examname, examIdx) {
+	const checkExamNameDuplicationQuery = `
+     select exists(select examIdx from Exam where examName = ? and status = 'N' and examIdx != ?) as exist;
+     `;
+	const [checkExamNameDuplicationRow] = await connection.query(checkExamNameDuplicationQuery, [examname, examIdx]);
+	return checkExamNameDuplicationRow;
+}
+
+// 시험 구분 체크
+async function checkExamSortLarge(connection, examSortIdx) {
+	const checkExamSortLargeQuery = `
+     select exists(select examSortIdx from ExamSort where examSortIdx = ? and status = 'N' and examSortType = 'L') as exist;
+     `;
+	const [checkExamSortLargeRow] = await connection.query(checkExamSortLargeQuery, examSortIdx);
+	return checkExamSortLargeRow;
+}
+
+// 시험 과목 다대다 체크
+async function checkExamSubjectMulti(connection, examIdx, examSubjectIdx, examSubjectNum) {
+	const checkExamSubjectMultiQuery = `
+     select exists(select examSubjectMultiIdx from ExamSubjectMulti where examIdx = ? and examSubjectIdx = ? and examSubjectNum = ?) as exist;
+     `;
+	const [checkExamSubjectMultiRow] = await connection.query(checkExamSubjectMultiQuery, [
+		examIdx,
+		examSubjectIdx,
+		examSubjectNum,
+	]);
+	return checkExamSubjectMultiRow;
+}
+
+// 문제 과목 중복 체크
+async function checkProblemSubject(connection, examDetailIdx, examSubjectIdx, problemNum) {
+	const checkProblemSubjectQuery = `
+	select exists(select mp.multipleProblemIdx 
+		from MultipleProblem  mp
+		LEFT JOIN ProblemExam pe on pe.multipleProblemIdx = mp.multipleProblemIdx
+		LEFT JOIN ProblemSubject ps on ps.multipleProblemIdx = mp.multipleProblemIdx
+		where status = 'N' AND examDetailIdx = ? and examSubjectIdx = ? and problemNum = ?) as exist;
+     `;
+	const [checkProblemSubjectRow] = await connection.query(checkProblemSubjectQuery, [
+		examDetailIdx,
+		examSubjectIdx,
+		problemNum,
+	]);
+	return checkProblemSubjectRow;
+}
+
+// 시험 종류 인덱스 체크
+async function checkExamSort(connection, examSortIdx) {
+	const checkExamSortQuery = `
+     select exists(select examSortIdx from ExamSort where examSortIdx = ? and status = 'N' ) as exist;
+     `;
+	const [checkExamSortRow] = await connection.query(checkExamSortQuery, examSortIdx);
+	return checkExamSortRow;
+}
+
+/**
+ * 기업 시험 종류 체크
+ * @author 김기창
+ */
+async function checkCompanySortIdx(connection, companySortCheckParams) {
+	const checkCompanySortIdxQuery = `	
+	select exists(
+		with recursive CTE as (
+			select
+			a.* from ExamSort a where examSortName = ?
+			union all
+			select
+			b.*
+			from ExamSort b
+			join CTE ON b.examSortRef=CTE.examSortIdx where b.status='N'
+			)
+			select 
+			M.examSortIdx as companyExamTypeIdx, M.examSortName as companyExamType,
+			S.examSortIdx as companyExamFieldIdx ,S.examSortName as companyExamField
+			from CTE L 
+			join CTE M on M.examSortRef = L.examSortIdx
+			join CTE S on S.examSortRef = M.examSortIdx
+			where  M.examSortIdx = ? and S.examSortIdx = ?
+			)
+			as exist;
+			; 
+			`;
+	const [checkCompanySortIdxRow] = await connection.query(checkCompanySortIdxQuery, companySortCheckParams);
+	return checkCompanySortIdxRow;
+}
+
+// 시험 종류 구분 일치 여부 확인
+async function checkExamSortRefValue(connection, examSortIdx, examSortRef) {
+	const checkExamSortRefQuery = `
+     select exists(select examSortIdx from ExamSort where examSortIdx = ? and examSortRef = ? and status = 'N'  and examSortType = 'S') as exist;
+     `;
+	const [checkExamSortRefRow] = await connection.query(checkExamSortRefQuery, [examSortIdx, examSortRef]);
+	return checkExamSortRefRow;
+}
+
+// 시험 구분 체크
+async function checkExamSortRef(connection, examSortRef) {
+	const checkExamSortRefQuery = `
+     select exists(select examSort from ExamSort where examSortRef = ? AND status = 'N') as exist;
+     `;
+	const [checkExamSortRefRow] = await connection.query(checkExamSortRefQuery, examSortRef);
+	return checkExamSortRefRow;
+}
+
+// 시험 종류 체크
+async function checkExamSortDouble(connection, examSort, examSortRef) {
+	const checkExamSortDoubleQuery = `
+     select exists(select examSort from ExamSort where examSort = ? AND examSortRef = ? AND status = 'N' ) as exist;
+     `;
+	const [checkExamSortDoubleRow] = await connection.query(checkExamSortDoubleQuery, [examSort, examSortRef]);
+	return checkExamSortDoubleRow;
+}
+
+// 시험 종류 이름 체크
+async function checkExamSortName(connection, examSortName, examSortType) {
+	const checkExamSortDoubleQuery = `
+     select exists(select examSortIdx from ExamSort where examSortName = ? and examSortType = ? and status = 'N') as exist;
+     `;
+	const [checkExamSortDoubleRow] = await connection.query(checkExamSortDoubleQuery, [examSortName, examSortType]);
+	return checkExamSortDoubleRow;
+}
+
+// 회사 체크
+async function checkEnterpriseIdx(connection, enterpriseIdx) {
+	const checkEnterpriseIdxQuery = `
+     select exists(select enterpriseIdx from Enterprise where enterpriseIdx = ? AND status = 'N' ) as exist;
+     `;
+	const [checkEnterpriseIdxeRow] = await connection.query(checkEnterpriseIdxQuery, enterpriseIdx);
+	return checkEnterpriseIdxeRow;
+}
+
+//  복원 회차 체크
+async function checkRestoreExamDetailIdx(connection, restoreExamDetailIdx) {
+	const checkRestoreExamDetailIdxQuery = `
+     select exists(select restoreExamDetailIdx from RestoreExamDetail where restoreExamDetailIdx = ? AND status = 'N') as exist;
+     `;
+	const [checkRestoreExamDetailRow] = await connection.query(checkRestoreExamDetailIdxQuery, restoreExamDetailIdx);
+	return checkRestoreExamDetailRow;
+}
+
+// 검색 복원 문제 체크
+async function checkRestorationIdx(connection, restorationIdx) {
+	const checkRestorationIdxQuery = `
+     select exists(select restorationIdx from Restoration where restorationIdx = ? AND status = 'N' ) as exist;
+     `;
+	const [checkRestorationIdxRow] = await connection.query(checkRestorationIdxQuery, restorationIdx);
+	return checkRestorationIdxRow;
+}
+
+// 검색 복원 문제 댓글 체크
+async function checkRestorationCommentIdx(connection, restorationCommentIdx) {
+	const checkRestorationCommentIdxQuery = `
+     select exists(select restorationCommentIdx from RestorationComment where restorationCommentIdx = ? AND status = 'N') as exist;
+     `;
+	const [checkRestorationCommentIdxRow] = await connection.query(
+		checkRestorationCommentIdxQuery,
+		restorationCommentIdx,
+	);
+	return checkRestorationCommentIdxRow;
+}
+
+// 문제 에러 인덱스 체크
+async function checkProblemErrorIdx(connection, problemErrorIdx) {
+	const checkProblemErrorIdxQuery = `
+     select exists(select problemErrorIdx from ProblemError where problemErrorIdx = ? and status = 'N' ) as exist;
+     `;
+	const [checkProblemErrorIdxRow] = await connection.query(checkProblemErrorIdxQuery, problemErrorIdx);
+	return checkProblemErrorIdxRow;
+}
+
+// 카테고리 인덱스 체크
+async function checkCategoryIdx(connection, categoryIdx) {
+	const checkCategoryIdxQuery = `
+     select exists(select categoryIdx from Category where categoryIdx = ? and status = 'N' ) as exist;
+     `;
+	const [checkCategoryIdxRow] = await connection.query(checkCategoryIdxQuery, categoryIdx);
+	return checkCategoryIdxRow;
+}
+
+// 공기업 회사 이름 체크
+async function checkPublicCompanyName(connection, companyName) {
+	const checkPublicCompanyNameQuery = `
+     select exists(select examSortIdx from ExamSort where examSortName = ? AND status = 'N' ) as exist;
+     `;
+	const [checkPublicCompanyNameRow] = await connection.query(checkPublicCompanyNameQuery, companyName);
+	return checkPublicCompanyNameRow;
+}
+/**
+ * 기업 회사 이름 체크
+ * @changeFrom checkPublicCompanyName
+ */
+async function checkCompanyName(connection, companyExamFieldIdx, companyName) {
+	const companyNameCheckQuery = `
+     select exists(select examSortIdx_S from CompanyExam where examSortIdx_S = ? AND companyName = ? AND status = 'N' ) as exist;
+     `;
+	const [companyNameCheckRow] = await connection.query(companyNameCheckQuery, [companyExamFieldIdx, companyName]);
+	return companyNameCheckRow;
+}
+/**
+ * 기업 시험 소분류 이름 체크
+ * @changeFrom checkPublicCompanyName
+ */
+async function examSortNameCheck(connection, companyExamTypeIdx, companyExamFieldName) {
+	const companyNameCheckQuery = `
+	select exists(select examSortIdx from ExamSort where examSortRef = ? AND examSortName = ? AND status = 'N' ) as exist;
+	`;
+	const [companyNameCheckRow] = await connection.query(companyNameCheckQuery, [
+		companyExamTypeIdx,
+		companyExamFieldName,
+	]);
+	return companyNameCheckRow;
+}
+/**
+ * 기업 시험인지 자격증 시험인지 체크하는 쿼리
+ */
+async function checkCompanyExam(connection, examIdx) {
+	const checkCompanyExamQuery = `
+	select exists(	
+	select * from CompanyExam 
+	where examIdx = ?) as exist
+	`;
+	const [checkCompanyExamRow] = await connection.query(checkCompanyExamQuery, examIdx);
+	return checkCompanyExamRow;
+}
+
+// 문제 카테고리 존재 여부
+async function checkProblemCategory(connection, multipleProblemIdx) {
+	const checkProblemCategoryQuery = `
+	select exists(
+		SELECT categoryIdx
+		FROM ProblemCategory
+		WHERE multipleProblemIdx = ?
+	) as exist;
+	`;
+	const checkProblemCategoryRow = await connection.query(checkProblemCategoryQuery, multipleProblemIdx);
+	return checkProblemCategoryRow;
+}
+
+// 문제 카테고리 인덱스 체크
+async function checkProblemCategoryIdx(connection, categoryIdx) {
+	const checkProblemCategoryIdxQuery = `
+	select exists(
+		SELECT mp.multipleProblemIdx
+		FROM MultipleProblem mp 
+		LEFT JOIN ProblemCategory pc ON mp.multipleProblemIdx = pc.multipleProblemIdx
+		WHERE categoryIdx in 
+		(
+		with recursive CTE AS   (SELECT 
+			categoryIdx, categoryName, status, categoryRef
+			FROM Category 
+			WHERE categoryIdx = ?
+			UNION ALL
+			SELECT
+			a.categoryIdx, a.categoryName,a.status,a.categoryRef
+			FROM Category a
+			INNER JOIN CTE b ON a.categoryRef = b.categoryIdx 
+			)
+			SELECT categoryIdx FROM CTE WHERE status = 'N')
+		 ) as exist;
+		;
+     `;
+	const [checkProblemCategoryIdxRow] = await connection.query(checkProblemCategoryIdxQuery, categoryIdx);
+	return checkProblemCategoryIdxRow;
+}
+
+// 자격증 시험 여부 체크
+async function checkIsCertification(connection, examDetailIdx) {
+	// const checkIsCertificationQuery = `
+	// select exists(
+	// 	with recursive CTE AS   (SELECT
+	// 				es.examSortIdx, examSortRef, es.status, examSortType
+	// 				FROM ExamDetail ed
+	// 				LEFT JOIN Exam e on ed.examIdx = e.examIdx and e.status = 'N'
+	// 				LEFT JOIN ExamSort es on es.examSortIdx = e.examSortIdx and es.status = 'N'
+	// 				WHERE examDetailIdx = ? and ed.status = 'N'
+	// 				UNION ALL
+	// 				SELECT
+	// 				a.examSortIdx, a.examSortRef, a.status, a.examSortType
+	// 				FROM ExamSort a
+	// 				INNER JOIN CTE b ON a.examSortIdx = b.examSortRef
+	//                 where a.status = 'N'
+	// 				)
+	// 				SELECT examSortIdx FROM CTE where examSortType = 'L' AND examSortIdx not in(select 2)
+	// 				) as isCertification
+	// `;
+	// 이제는 기업인지 자격증인지 구분하기 위해서는 해당 ExamDetail이 CompanyExam과 관계를 맺고 있는지 아닌지만 판단할 수 있다.
+	const checkIsCertificationQuery = `
+	select not exists(
+		select * from ExamDetail ed
+		join Exam e on e.examIdx = ed.examIdx
+		join CompanyExam ce on ce.examIdx = e.examIdx
+		where ed.examDetailIdx = ? and ce.status= "N") as exist
+	`;
+
+	const [checkIsCertificationRow] = await connection.query(checkIsCertificationQuery, examDetailIdx);
+	return checkIsCertificationRow;
+}
+
+// 문제 카테고리 인덱스 체크
+async function checkPublicCompanyIdx(connection, examSortIdx) {
+	const checkPublicCompanyIdxQuery = `
+	select exists(
+		select examSortIdx from ExamSort where examSortIdx in (
+		with recursive CTE AS   (SELECT 
+					examSortIdx, examSortName, status, examSortRef, examSortType
+					FROM ExamSort	
+					WHERE examSortIdx = ? and status = 'N' and examSortType = 'S'
+					UNION ALL
+					SELECT
+					a.examSortIdx, a.examSortName,a.status,a.examSortRef, a.examSortType
+					FROM ExamSort a
+					INNER JOIN CTE b ON a.examSortIdx = b.examSortRef 
+					where a.status = 'N'
+					)
+					SELECT examSortIdx FROM CTE
+		) 
+		)as exist
+		;
+     `;
+	const [checkPublicCompanyIdxRow] = await connection.query(checkPublicCompanyIdxQuery, examSortIdx);
+	return checkPublicCompanyIdxRow;
+}
+
+// 시험 종류를 참조하고있는 시험 확인
+async function checkExamBySort(connection, examSortIdx) {
+	const checkExamBySortQuery = `
+     select exists(select examIdx from Exam where examSortIdx = ? and status = 'N' ) as exist;
+     `;
+	const [checkExamBySortRow] = await connection.query(checkExamBySortQuery, examSortIdx);
+	return checkExamBySortRow;
+}
+async function selectProblemErrorList(connection, subjectiveLabel) {
+	const selectProblemErrorListQuery = `
+	select pe.multipleProblemIdx from ProblemError pe where status ="N"	and subjectiveLabel = ? 
+	group by pe.multipleProblemIdx order by count(pe.multipleProblemIdx) desc,pe.createdAt;`;
+	const [selectProblemErrorListRow] = await connection.query(selectProblemErrorListQuery, subjectiveLabel);
+	return selectProblemErrorListRow;
+}
+
+// // 에러 신고 조회
+// async function selectProblemError(connection, multipleProblemIdx) {
+// 	const selectProblemErrorQuery = `
+// 	SELECT problemErrorIdx, mp.multipleProblemIdx, ed.examIdx, ed.examDetailIdx, problemNum,pe.userIdx, userName,nickname, title, content, pe.type,
+// 	 date_format(DATE_ADD(pe.createdAt, INTERVAL 9 HOUR), '%Y-%m-%d') as 'date', date_format(DATE_ADD(pe.createdAt, INTERVAL 9 HOUR), '%H:%i') as 'time', pe.status
+// 	FROM ProblemError pe
+// 	LEFT JOIN User u ON u.userIdx = pe.userIdx
+//     LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = pe.multipleProblemIdx
+// 	LEFT JOIN ProblemExam PE ON PE.multipleProblemIdx = mp.multipleProblemIdx
+//     LEFT JOIN ExamDetail ed ON ed.examDetailIdx = PE.examDetailIdx
+// 	where pe.status = 'N'
+//     ORDER BY  FIELD(pe.multipleProblemIdx, ? ),mp.multipleProblemIdx DESC, pe.createdAt;
+//     `;
+
+// 	const [selectProblemErrorRow] = await connection.query(selectProblemErrorQuery, [multipleProblemIdx]);
+// 	console.log([multipleProblemIdx]);
+// 	return selectProblemErrorRow;
+// }
+
+// 필기 문제 에러 신고 조회
+async function selectProblemError(connection, condition) {
+	const selectProblemErrorQuery = `
+	SELECT count, problemErrorIdx, mp.multipleProblemIdx, ed.examIdx, ed.examDetailIdx, problemNum,pe.userIdx, userName,nickname, title, content, pe.type,
+	date_format(DATE_ADD(pe.createdAt, INTERVAL 9 HOUR), '%Y-%m-%d') as 'date', date_format(DATE_ADD(pe.createdAt, INTERVAL 9 HOUR), '%H:%i') as 'time', pe.status
+	FROM ProblemError pe
+	LEFT JOIN User u ON u.userIdx = pe.userIdx
+	LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = pe.multipleProblemIdx
+	LEFT JOIN ProblemExam PE ON PE.multipleProblemIdx = mp.multipleProblemIdx
+	LEFT JOIN ExamDetail ed ON ed.examDetailIdx = PE.examDetailIdx
+	LEFT JOIN (select count(multipleProblemIdx) as count, multipleProblemIdx
+	FROM ProblemError where status ='N' and subjectiveLabel = 0 GROUP BY multipleProblemIdx) tmp on tmp.multipleProblemIdx = pe.multipleProblemIdx 
+	where pe.status = 'N' and pe.subjectiveLabel = 0 ${condition}
+    `;
+	const [selectProblemErrorRow] = await connection.query(selectProblemErrorQuery);
+	return selectProblemErrorRow;
+}
+
+// 실기 문제 에러 신고 조회
+async function selectSubjectiveProblemError(connection, condition) {
+	const selectSubjectiveProblemErrorQuery = `
+	SELECT count, problemErrorIdx, sp.subjectiveProblemIdx, ed.examIdx, ed.examDetailIdx, problemNum,pe.userIdx, userName,nickname, title, content, pe.type,
+	date_format(DATE_ADD(pe.createdAt, INTERVAL 9 HOUR), '%Y-%m-%d') as 'date', date_format(DATE_ADD(pe.createdAt, INTERVAL 9 HOUR), '%H:%i') as 'time', pe.status
+	FROM ProblemError pe
+	LEFT JOIN User u ON u.userIdx = pe.userIdx
+	LEFT JOIN SubjectiveProblem sp ON sp.subjectiveProblemIdx = pe.multipleProblemIdx
+	LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+	LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx
+	LEFT JOIN (select count(multipleProblemIdx) as count, multipleProblemIdx
+	FROM ProblemError where status ='N' and subjectiveLabel = 1 GROUP BY multipleProblemIdx) tmp on tmp.multipleProblemIdx = pe.multipleProblemIdx 
+	where pe.status = 'N' and pe.subjectiveLabel = 1 ${condition}
+    `;
+	const [selectProblemErrorRow] = await connection.query(selectSubjectiveProblemErrorQuery);
+	return selectProblemErrorRow;
+}
+// 게시판 조회
+async function selectBoardInfo(connection, boardIdx) {
+	const selectBoardInfoQuery = `
+       SELECT boardIdx, boardSort, status
+       FROM Board b
+       WHERE boardIdx = ?
+       `;
+	const [selectBoardInfoRow] = await connection.query(selectBoardInfoQuery, boardIdx);
+	return selectBoardInfoRow;
+}
+
+// 유저 목록 조회
+async function selectUserList(connection) {
+	const selectUserListQuery = `
+	SELECT userIdx, userName, userEmail, userPhoneNum, dateOfBirth, nickname as 'nickName', date_format(createdAt, '%Y-%m-%d') as createdAt, accessLevel, recommendCount, status,
+    case when status != 'X' then false else false end as 'check' 
+    FROM User
+     `;
+	const [selectUserListRow] = await connection.query(selectUserListQuery);
+	return selectUserListRow;
+}
+
+// 유저 상세 조회
+async function selectUserInfo(connection, userIdx) {
+	const selectUserInfoQuery = `
+  SELECT *
+  FROM User
+  WHERE userIdx = ?
+     `;
+	const [selectUserInfoRow] = await connection.query(selectUserInfoQuery, userIdx);
+	return selectUserInfoRow;
+}
+
+// 시험 상세 중복 조회
+async function selectExamDetailDoubleCheck(connection, examIdx, examRound, year) {
+	const selectExamDetailDoubleCheckQuery = `
+  select exists(
+  SELECT examDetailIdx
+  FROM ExamDetail
+  WHERE examIdx = ? and examRound = ? and examDate like ? AND status = 'N'
+  ) as exist;
+     `;
+	const [selectExamDetailDoubleCheckRow] = await connection.query(selectExamDetailDoubleCheckQuery, [
+		examIdx,
+		examRound,
+		year,
+	]);
+	return selectExamDetailDoubleCheckRow;
+}
+
+// 시험 상세 날짜정보 조회
+async function selectExamDetailDateInfo(connection, examDetailIdx) {
+	const selectExamDetailInfoQuery = `
+    SELECT examIdx, concat(date_format(examDate, '%y'), examRound) as 'date'
+    FROM ExamDetail
+    WHERE examDetailIdx = ? AND status = 'N'
+     `;
+	const [selectExamDetailInfoRow] = await connection.query(selectExamDetailInfoQuery, examDetailIdx);
+	return selectExamDetailInfoRow;
+}
+
+// 연결된 시험 과목 조회
+async function selectExamSubjectByExam(connection, examIdx) {
+	const selectExamSubjectNameQuery = `
+  SELECT es.examSubjectIdx, es.examSubjectName
+  FROM ExamSubjectMulti esm
+  LEFT JOIN ExamSubject es ON es.examSubjectIdx = esm.examSubjectIdx
+  where examIdx = ? AND status = 'N'
+  `;
+	const [selectExamSubjectNameRow] = await connection.query(selectExamSubjectNameQuery, examIdx);
+	return selectExamSubjectNameRow;
+}
+
+// select every examIdx, examName from exam
+async function selectAllExam(connection) {
+	const selectAllExamQuery = `
+  SELECT examIdx, examName
+	FROM Exam
+	WHERE status = 'N'
+  `;
+	const [selectAllExamQueryRow] = await connection.query(selectAllExamQuery);
+	return selectAllExamQueryRow;
+}
+
+async function selectLargeExamSort(connection) {
+	const selectLargeExamSortQuery = `
+	SELECT examSortIdx, examSortName
+	FROM ExamSort
+	WHERE status = 'N' AND examSortType = "L";
+	`;
+	const [selectLargeExamSortQueryRow] = await connection.query(selectLargeExamSortQuery);
+	return selectLargeExamSortQueryRow;
+}
+
+async function searchMultipleProblem(connection, examSortIdx, searchStringQuery, keywordList) {
+	const searchMultipleProblemQuery = `
+	select distinct e.examIdx ,ed.examDate ,ed.examRound , mp.multipleProblemIdx , mp.problem,mp.solution 
+	FROM MultipleProblem mp 
+	JOIN ProblemExam pe on pe.multipleProblemIdx = mp.multipleProblemIdx 
+	JOIN ExamDetail ed on ed.examDetailIdx = pe.examDetailIdx 
+	JOIN Exam e on e.examIdx = ed.examIdx 
+	JOIN ExamSort es on es.examSortIdx = e.examSortIdx
+	WHERE es.examSortRef =? ${searchStringQuery}
+	`;
+
+	const [searchMultipleProblemQueryRow] = await connection.query(searchMultipleProblemQuery, [
+		examSortIdx,
+		...keywordList,
+	]);
+
+	return searchMultipleProblemQueryRow;
+}
+
+async function searchSubjectiveProblem(connection, examSortIdx, searchStringQuery, keywordList) {
+	const searchSubjectiveProblemQuery = `
+	select distinct e.examIdx ,ed.examDate ,ed.examRound , sp.subjectiveProblemIdx , sp.problem ,sp.solution
+	FROM SubjectiveProblem sp 
+	JOIN SubjectiveProblemExam spe on spe.subjectiveProblemIdx = sp.subjectiveProblemIdx 
+	JOIN ExamDetail ed on ed.examDetailIdx = spe.examDetailIdx 
+	JOIN Exam e on e.examIdx = ed.examIdx 
+	JOIN ExamSort es on es.examSortIdx = e.examSortIdx
+	WHERE es.examSortRef =? ${searchStringQuery}
+	`;
+	const [searchSubjectiveProblemQueryRow] = await connection.query(searchSubjectiveProblemQuery, [
+		examSortIdx,
+		...keywordList,
+	]);
+	return searchSubjectiveProblemQueryRow;
+}
+
+// 시험 과목 이름 검색
+async function selectExamSubjectByName(connection, subjectName) {
+	const selectExamSubjectNameQuery = `
+  SELECT examSubjectIdx, examSubjectName, passScore, status
+  FROM ExamSubject
+  WHERE examSubjectName like ? AND status = 'N'
+     `;
+	const [selectExamSubjectNameRow] = await connection.query(selectExamSubjectNameQuery, subjectName);
+	return selectExamSubjectNameRow;
+}
+
+// 시험 과목 다대다 조회
+// async function selectExamSubjectMulti(connection, examIdx) {
+// 	const selectExamSubjectMultiQuery = `
+//   SELECT examSubjectMultiIdx, es.examSubjectIdx, esm.examSubjectNum, examSubjectName
+//   FROM ExamSubjectMulti esm
+//   LEFT JOIN ExamSubject es ON es.examSubjectIdx = esm.examSubjectIdx
+//   WHERE examIdx = ?
+//      `;
+// 	const [selectExamSubjectMultiRow] = await connection.query(selectExamSubjectMultiQuery, examIdx);
+// 	return selectExamSubjectMultiRow;
+// }
+
+// 특정 문제 정보 조회
+async function selectMultipleProblem(connection, multipleProblemIdx) {
+	const selectMultipleProblemQuery = `
+  SELECT *
+  FROM MultipleProblem mp
+  LEFT JOIN ProblemExam pe on pe.multipleProblemIdx = mp.multipleProblemIdx
+  LEFT JOIN ProblemSubject ps on ps.multipleProblemIdx = mp.multipleProblemIdx
+  where mp.multipleProblemIdx = ? AND status = 'N'
+  `;
+	const [selectMultipleProblemRow] = await connection.query(selectMultipleProblemQuery, multipleProblemIdx);
+	return selectMultipleProblemRow;
+}
+
+// 시험 문항 조회
+async function selectExamQuestion(connection, multipleProblemIdx) {
+	const selectExamQuestionQuery = `
+  SELECT questionIdx, questionNum 
+  FROM Question
+  where multipleProblemIdx = ? AND status = 'N'
+  order by questionNum
+     `;
+	const [selectExamQuestionRow] = await connection.query(selectExamQuestionQuery, multipleProblemIdx);
+	return selectExamQuestionRow;
+}
+
+// 공기업, 자격증 시험 특정 계층 조회
+// async function selectExamSortDivision(connection, isPublic, sortType) {
+// 	const selectExamSortDivisionQuery = `
+// 	SELECT examSortIdx, examSortName, examSortRef, status
+// 	FROM ExamSort
+// 	WHERE examSortType = ? and status = 'N' and exam
+// 	` + where;
+// 	const [selectExamSortDivisionRow] = await connection.query(selectExamSortDivisionQuery, sortType);
+// 	return selectExamSortDivisionRow;
+// }
+
+// 공기업, 자격증 시험 분류 조회
+async function selectExamSortList(connection, largeExamName) {
+	const selectExamSortListQuery = `
+	WITH RECURSIVE CTE(examSortIdx, examSortRef, examSortName, examSortType, status) AS(
+		SELECT examSortIdx, examSortRef, examSortName, examSortType, status
+		FROM ExamSort es
+		where es.status = 'N'
+		UNION ALL
+		SELECT t.examSortIdx, t.examSortRef, t.examSortName, t.examSortType, t.status
+		FROM ExamSort t
+        JOIN CTE es ON es.examSortRef = t.examSortIdx
+		)
+	  SELECT DISTINCT
+		L.examSortIdx as 'largeExamIdx',
+		L.examSortName as 'largeExamName',
+		L.examSortType as 'largeExam',
+		M.examSortIdx as 'mediumExamIdx',
+		M.examSortName as 'mediumExamName',
+		M.examSortType as 'mediumExam',
+		S.examSortIdx as 'smallExamIdx',
+		S.examSortName as 'smallExamName',
+		S.examSortType as 'smallExam'
+	  FROM
+		CTE as L
+	  LEFT JOIN CTE M on M.examSortRef = L.examSortIdx and M.examSortType = 'M' AND M.status = 'N'
+	  LEFT JOIN CTE S on S.examSortRef = M.examSortIdx and S.examSortType = 'S' AND S.status = 'N'
+	  WHERE  L.status = 'N' AND L.examSortName = ?
+	  `;
+
+	const [selectExamSortListRow] = await connection.query(selectExamSortListQuery, largeExamName);
+	return selectExamSortListRow;
+}
+
+// 시험 구분 조회
+async function selectExamSortRef(connection) {
+	const selectExamRefQuery = `
+  SELECT examSortIdx as 'examSortLargeIdx', examSortName as 'examSortLargeName'
+  FROM ExamSort
+  WHERE examSortType = 'L' AND examSortName != '공기업' AND status = 'N';
+  `;
+	const [selectExamRefRow] = await connection.query(selectExamRefQuery);
+	return selectExamRefRow;
+}
+
+// 시험 종류 조회
+async function selectExamSort(connection, where) {
+	const selectExamSortQuery =
+		`
+  SELECT M.examSortIdx as 'examSortMediumIdx', M.examSortName as 'examSortMediumName'
+  FROM ExamSort as M
+  ` + where;
+	const [selectExamSortRow] = await connection.query(selectExamSortQuery);
+	return selectExamSortRow;
+}
+
+// 시험 이름 조회
+async function selectExamName(connection, where) {
+	const selectExamNameQuery =
+		`
+  SELECT examIdx, examName
+  FROM Exam e
+  LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx
+  ` + where;
+	const [selectExamNameRow] = await connection.query(selectExamNameQuery);
+	return selectExamNameRow;
+}
+
+// 관리자 시험 조회
+
+async function selectExamInfoList(connection) {
+	//examSortRef를 자격증에 대한 인덱싱으로 분리하도록 한다. 4일 경우 전기, 5일 경우 화학, 확장되어 디비에 6으로 기계라 적혀있으면 6은 기계 이런식으로.
+	const selectExamInfoListQuery = `
+		with recursive CTE AS (SELECT 
+			es.examSortIdx, es.examSortRef, es.examSortName, es.examSortType
+			from ExamSort es
+			where  examSortRef =1 
+			UNION ALL
+			SELECT
+			a.examSortIdx, a.examSortRef, a.examSortName, a.examSortType
+			from ExamSort a
+			JOIN CTE b ON a.examSortRef=b.examSortIdx
+			)
+				select e.examIdx,  examSortRef, es.examSortName as 'examSort', 
+				examName, passScore,timeLimit, problemCount, accessLevel, examUrl, questionType,
+				case when instr(questionType, 4) = 0 then 5 else 4 end as 'questionCount',subjectiveLabel
+				from Exam e 
+				join ExamSort es on e.examSortIdx=es.examSortIdx
+				where exists (select examSortIdx from CTE where e.examSortIdx=CTE.examSortIdx) and e.status ='N';
+  `;
+	const [selectExamInfoListRow] = await connection.query(selectExamInfoListQuery);
+	return selectExamInfoListRow;
+}
+
+// // 관리자 시험 조회
+// async function selectExamInfoList(connection, where) {
+// 	const selectExamInfoListQuery =
+// 		`
+// 	SELECT e.examIdx, L.examSortName as  'examSortRef', M.examSortName as 'examSort', examName , passScore, timeLimit, problemCount, accessLevel, examUrl, questionType,
+// 	case when instr(questionType, 4) = 0 then 5 else 4 end as 'questionCount'
+// 	FROM Exam e
+// 	LEFT JOIN ExamSort M ON M.examSortIdx = e.examSortIdx and M.examSortType = 'M'
+// 	LEFT JOIN ExamSort L ON L.examSortIdx = M.examSortRef and L.examSortType = 'L'
+//   ` + where;
+// 	const [selectExamInfoListRow] = await connection.query(selectExamInfoListQuery);
+// 	return selectExamInfoListRow;
+// }
+
+// 관리자 시험 회차 조회
+async function selectExamDetailList(connection, examIdx) {
+	const selectExamDetailListQuery = `
+  SELECT examDetailIdx,date_format(examDate, '%Y-%m-%d') as 'date', examRound, isPublic, examDetailUrl, publicLevel
+  FROM ExamDetail ed
+  where examIdx = ? and ed.status = 'N'
+  ORDER BY examDate DESC
+  `;
+	const [selectExamDetailListRow] = await connection.query(selectExamDetailListQuery, examIdx);
+	return selectExamDetailListRow;
+}
+
+// 관리자 시험 과목 조회
+async function selectExamSubject(connection, examIdx) {
+	const selectExamSubjectQuery = `
+  SELECT esm.examSubjectIdx, examSubjectNum, examSubjectName, passScore, es.status as 'examSubjectStatus', es.status as 'examSubjectStatus'
+  FROM ExamSubjectMulti esm
+  LEFT JOIN ExamSubject es ON es.examSubjectIdx = esm.examSubjectIdx and es.status = 'N'
+  WHERE esm.examIdx = ? AND status = 'N'
+  `;
+	const [selectExamSubjectRow] = await connection.query(selectExamSubjectQuery, examIdx);
+	return selectExamSubjectRow;
+}
+
+// 관리자 시험 과목 조회
+async function selectSubjectProblem(connection, examSubjectIdx) {
+	const selectSubjectProblemQuery = `
+  SELECT mp.multipleProblemIdx 
+  FROM MultipleProblem mp
+  LEFT JOIN ProblemSubject ps on ps.multipleProblemIdx =  mp.multipleProblemIdx
+  WHERE examSubjectIdx = ? AND status = 'N'
+  `;
+	const [selectSubjectProblemRow] = await connection.query(selectSubjectProblemQuery, examSubjectIdx);
+	return selectSubjectProblemRow;
+}
+
+// 달력 기업 목록 조회
+async function selectEnterpriseList(connection) {
+	const selectEnterpriseListQuery = `
+  SELECT enterpriseIdx, enterpriseName,link,startDate, endDate, examDate, image
+  FROM Enterprise
+  WHERE status = 'N'
+
+  `;
+	const selectEnterpriseListRow = await connection.query(selectEnterpriseListQuery);
+	return selectEnterpriseListRow[0];
+}
+
+// 달력 기업 목록 조회
+async function selectEnterpriseInfo(connection, enterpriseIdx) {
+	const selectEnterpriseInfoQuery = `
+  SELECT enterpriseIdx, enterpriseName,link,image,startDate,endDate,examDate
+  FROM Enterprise
+  WHERE enterpriseIdx = ? AND status = 'N'
+  `;
+	const selectEnterpriseInfoRow = await connection.query(selectEnterpriseInfoQuery, enterpriseIdx);
+	return selectEnterpriseInfoRow[0];
+}
+
+// 액셀 다운로드 문제 조회
+async function selectExcelProblem(connection, examDetailIdx) {
+	const selectExcelProblemQuery = `
+	SELECT mp.multipleProblemIdx , problemNum, problem, questionNum as answerNum, problemImage, solution, es.examSubjectName as 'subject', isKatex, problemUrl, lectureUrl
+	FROM ProblemExam pe 
+    LEFT JOIN MultipleProblem mp on pe.multipleProblemIdx = mp.multipleProblemIdx AND mp.status = 'N'
+    LEFT JOIN ProblemSubject ps on ps.multipleProblemIdx = mp.multipleProblemIdx 
+	LEFT JOIN ExamSubject es ON es.examSubjectIdx = ps.examSubjectIdx AND es.status = 'N'
+	LEFT JOIN Question q on q.multipleProblemIdx = mp.multipleProblemIdx AND isAnswer = 1 AND q.status = 'N' 
+	WHERE examDetailIdx = ?
+	GROUP BY mp.multipleProblemIdx
+	ORDER BY problemNum
+  `;
+	const selectExcelProblemRow = await connection.query(selectExcelProblemQuery, examDetailIdx);
+	return selectExcelProblemRow[0];
+}
+
+// 액셀 다운로드 문제 조회
+async function selectExcelQuestion(connection, multipleProblemIdx) {
+	const selectExcelQuestionQuery = `
+	SELECT questionNum, question, questionImage
+    FROM Question
+	WHERE multipleProblemIdx = ? AND status = 'N'
+  `;
+	const selectExcelQuestionRow = await connection.query(selectExcelQuestionQuery, multipleProblemIdx);
+	return selectExcelQuestionRow[0];
+}
+
+// 액셀 파일명 조회
+async function selectExcelFileName(connection, examDetailIdx) {
+	const selectExcelFileNameQuery = `
+	SELECT concat(e.examName, date_format(examDate, '%Y-0'), examRound) as 'fileName'
+    FROM ExamDetail ed
+	LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+	WHERE ed.examDetailIdx = ?
+  `;
+	const selectExcelFileNameRow = await connection.query(selectExcelFileNameQuery, examDetailIdx);
+	return selectExcelFileNameRow[0];
+}
+
+// 유저 검색
+async function selectSearchUser(connection, like) {
+	const selectExcelQuestionQuery =
+		`
+	SELECT userIdx, userName, userEmail, userPhoneNum, dateOfBirth, nickname as 'nickName', date_format(createdAt, '%Y-%m-%d') as createdAt, provider,accessLevel, recommendCount, status,
+    case when status != 'X' then false else false end as 'check' 
+    FROM User
+	WHERE ` + like;
+	const selectExcelQuestionRow = await connection.query(selectExcelQuestionQuery);
+	return selectExcelQuestionRow[0];
+}
+
+// 복원 회차 검색
+async function selectRestoreExamDetail(connection, where) {
+	const selectRestoreExamDetailQuery =
+		`
+		SELECT restoreExamDetailIdx, e.examName, e.examIdx, es.examSortRef, restoreExamRound , red.status, date_format(restoreExamDate, '%Y-%m-%d') as 'restoreExamDate', thumbnail, isPublic
+		FROM RestoreExamDetail red
+		LEFT JOIN Exam e ON red.examIdx = e.examIdx
+		LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx 
+	` + where;
+	const selectRestoreExamDetailRow = await connection.query(selectRestoreExamDetailQuery);
+	return selectRestoreExamDetailRow[0];
+}
+
+// 복원 회차로 문제 인덱스 조회
+
+// 미인증 합계 조회
+async function selectUnAuthenticationSum(connection, restoreExamDetailIdx) {
+	const selectUnAuthenticationSumQuery = `
+	SELECT count(*)  as 'unAuthSum'  
+    FROM ExamAdmissionTicket
+	WHERE restoreExamDetailIdx = ? AND isAuthentication = 0 
+	`;
+	const selectUnAuthenticationSumRow = await connection.query(selectUnAuthenticationSumQuery, restoreExamDetailIdx);
+	return selectUnAuthenticationSumRow[0];
+}
+
+// 생성 복원 문제 합계 조회
+async function selectCustomProblemSum(connection, restoreExamDetailIdx) {
+	const selectCustomProblemSumQuery = `
+	SELECT count(*)  as 'customSum' 
+    FROM CustomProblem 
+	WHERE restoreExamDetailIdx = ?
+	`;
+	const selectCustomProblemSumRow = await connection.query(selectCustomProblemSumQuery, restoreExamDetailIdx);
+	return selectCustomProblemSumRow[0];
+}
+
+// 검색 복원 문제 합계 조회
+async function selectRestorationSum(connection, restoreExamDetailIdx) {
+	const selectRestorationSumQuery = `
+	SELECT count(*)  as 'restorationSum' 
+    FROM Restoration
+	WHERE restoreExamDetailIdx = ?
+	`;
+	const selectRestorationSumRow = await connection.query(selectRestorationSumQuery, restoreExamDetailIdx);
+	return selectRestorationSumRow[0];
+}
+
+// 검색 복원 문제 합계 조회
+async function selectRestoreDate(connection) {
+	const selectRestoreDateQuery = `
+	SELECT distinct date_format(restoreExamDate, '%Y') as 'ExamDate'
+    FROM RestoreExamDetail
+	WHERE status = 'N'
+	order by restoreExamDate DESC
+	`;
+	const selectRestoreDateRow = await connection.query(selectRestoreDateQuery);
+	return selectRestoreDateRow[0];
+}
+
+// 검색 복원 문제 합계 조회
+async function selectRestoreRound(connection) {
+	const selectRestoreRoundQuery = `
+	SELECT distinct restoreExamRound
+    FROM RestoreExamDetail
+	WHERE status = 'N'
+	order by restoreExamRound
+	`;
+	const selectRestoreRoundRow = await connection.query(selectRestoreRoundQuery);
+	return selectRestoreRoundRow[0];
+}
+
+// 복원 회차 검색
+// async function selectRestoreExamSortRef(connection) {
+// 	const selectRestoreExamSortRefQuery = `
+// 		SELECT distinct es.examSortRef
+// 		FROM RestoreExamDetail red
+// 		LEFT JOIN Exam e ON red.examIdx = e.examIdx
+// 		LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx
+// 	`;
+// 	const selectRestoreExamSortRefRow = await connection.query(selectRestoreExamSortRefQuery);
+// 	return selectRestoreExamSortRefRow[0];
+// }
+
+// 검색 복원 문제 리스트 조회
+async function selectRestorationList(connection, restoreExamDetailIdx) {
+	const selectRestorationListQuery = `
+	SELECT distinct r.restorationIdx, concat(examName,date_format(examDate, '%y'), '년 ', examRound, '회차 ', problemNum, '번') as 'origin', mp.problem, isKatex, r.status
+    FROM Restoration r
+    LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = r.multipleProblemIdx and mp.status = 'N'
+    LEFT JOIN Question q ON q.multipleProblemIdx = mp.multipleProblemIdx and q.status = 'N'
+    LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = q.multipleProblemIdx
+    LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx and ed.status = 'N'
+    LEFT JOIN Exam e ON e.examIdx = ed.examIdx and e.status = 'N'
+	WHERE restoreExamDetailIdx = ?
+	`;
+	const selectRestorationListRow = await connection.query(selectRestorationListQuery, restoreExamDetailIdx);
+	return selectRestorationListRow[0];
+}
+
+// 검색 복원 문제 좋아요 합계 조회
+async function selectRestorationLikeSum(connection, restorationIdx) {
+	const selectRestorationLikeSumQuery = `
+	SELECT count(*) as 'sum'
+    FROM RestorationLike rl
+    WHERE restorationIdx = ? and isLike = 1
+	`;
+	const selectRestorationLikeSumRow = await connection.query(selectRestorationLikeSumQuery, restorationIdx);
+	return selectRestorationLikeSumRow[0];
+}
+
+// 검색 복원 담은 유저수  조회
+async function selectUserRestorationSum(connection, restorationIdx) {
+	const selectUserRestorationSumQuery = `
+	SELECT count(*) as 'sum'
+    FROM UserRestoration ur
+    WHERE restorationIdx = ? and status = 'N'
+	`;
+	const selectUserRestorationSumRow = await connection.query(selectUserRestorationSumQuery, restorationIdx);
+	return selectUserRestorationSumRow[0];
+}
+
+// 검색 복원 문제 댓글 합계 조회
+async function selectRestorationCommentSum(connection, restorationIdx) {
+	const selectRestorationCommentSumQuery = `
+	SELECT count(*) as 'sum'
+    FROM RestorationComment 
+    WHERE restorationIdx = ? AND status = 'N'
+	`;
+	const selectRestorationCommentSumRow = await connection.query(selectRestorationCommentSumQuery, restorationIdx);
+	return selectRestorationCommentSumRow[0];
+}
+// 검색 복원 문제 댓글 합계 조회
+async function selectExamInfo(connection, examIdx) {
+	const selectExamInfoQuery = `
+	SELECT examName, e.examSortIdx, es.examSortRef
+    FROM Exam e
+	LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx
+    WHERE examIdx = ?
+	`;
+	const selectExamInfoRow = await connection.query(selectExamInfoQuery, examIdx);
+	return selectExamInfoRow[0];
+}
+
+// 수험표 목록 조회
+async function selectRestoreTicketList(connection, restoreExamDetailIdx) {
+	const selectRestoreTicketListQuery = `
+	SELECT examAdmissionTicketIdx, userName, dateOfBirth, date_format(eat.createdAt, '%m-%d %H:%i') as 'createdAt', examSortRef, e.examIdx, e.examSortIdx, examName, examAdmissionTicketImage, isAuthentication
+    FROM ExamAdmissionTicket eat
+    LEFT JOIN User u ON eat.userIdx = u.userIdx
+    LEFT JOIN RestoreExamDetail red ON red.restoreExamDetailIdx = eat.restoreExamDetailIdx
+    LEFT JOIN Exam e ON e.examIdx = red.examIdx
+    LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx
+    where eat.restoreExamDetailIdx =  ?
+	`;
+	const selectRestoreTicketListRow = await connection.query(selectRestoreTicketListQuery, restoreExamDetailIdx);
+	return selectRestoreTicketListRow[0];
+}
+
+// 유저 생성 복원 문제 조회
+async function selectCustomProblemList(connection, restoreExamDetailIdx) {
+	const selectCustomProblemListQuery = `
+	SELECT customProblemIdx, userIdx, problemNum ,problem, comment, createdAt
+    FROM CustomProblem cp
+    WHERE restoreExamDetailIdx = ?
+	ORDER BY problemNum, createdAt DESC
+	`;
+	const selectCustomProblemListRow = await connection.query(selectCustomProblemListQuery, restoreExamDetailIdx);
+	return selectCustomProblemListRow[0];
+}
+
+// 유저 생성 복원 문제 이미지 조회
+async function selectCustomProblemImageList(connection, customProblemIdx) {
+	const selectCustomProblemImageListQuery = `
+	SELECT imageUrl
+    FROM CustomProblemImage
+    WHERE customProblemIdx = ?
+	`;
+	const selectCustomProblemImageListRow = await connection.query(selectCustomProblemImageListQuery, customProblemIdx);
+	return selectCustomProblemImageListRow[0];
+}
+
+// 유저 생성 복원 문항 조회
+async function selectCustomQuestionList(connection, customProblemIdx) {
+	const selectCustomQuestionListQuery = `
+	SELECT questionNum ,question
+    FROM CustomQuestion
+    WHERE customProblemIdx = ? AND status = 'N'
+	order by questionNum
+	`;
+	const selectCustomQuestionListRow = await connection.query(selectCustomQuestionListQuery, customProblemIdx);
+	return selectCustomQuestionListRow[0];
+}
+
+// 검색 복원 문제 조회
+async function selectRestoration(connection, restoreExamDetailIdx) {
+	const selectRestorationQuery = `
+	SELECT *
+    FROM Restoration
+	WHERE restoreExamDetailIdx = ?
+	`;
+	const selectRestorationRow = await connection.query(selectRestorationQuery, restoreExamDetailIdx);
+	return selectRestorationRow[0];
+}
+
+// 관리자 객관식 문제 정보 조회
+async function selectMultipleProblemInfo(connection, multipleProblemIdx) {
+	const selectMultipleProblemInfoQuery = `
+	SELECT e.examIdx, examName, date_format(examDate, '%Y-%m-%d') as examDate, examRound, mp.multipleProblemIdx, problemNum, problem, provisionNum, problemImage, solution, isKatex, mp.isDelete
+	FROM MultipleProblem mp
+	LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+	LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx AND ed.status = 'N'
+	LEFT JOIN Exam e ON e.examIdx = ed.examIdx AND e.status = 'N'
+	where mp.multipleProblemIdx = ?;
+	`;
+	const selectMultipleProblemInfoRow = await connection.query(selectMultipleProblemInfoQuery, multipleProblemIdx);
+	return selectMultipleProblemInfoRow[0];
+}
+// 관리자 주관식문제 정보 조회
+async function selectSubjectiveProblemInfo(connection, subjectiveProblemIdx) {
+	const selectSubjectiveProblemInfoQuery = `
+	SELECT e.examIdx, examName, date_format(examDate, '%Y-%m-%d') as examDate, examRound, sp.subjectiveProblemIdx,examHistory, problemNum, problem, problemImage, solution,solutionImage, isKatex, sp.isDelete,sp.examHistory
+	FROM SubjectiveProblem sp
+	LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+	LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx AND ed.status = 'N'
+	LEFT JOIN Exam e ON e.examIdx = ed.examIdx AND e.status = 'N'
+	where sp.subjectiveProblemIdx = ?;
+	`;
+	const selectSubjectiveProblemInfoRow = await connection.query(selectSubjectiveProblemInfoQuery, subjectiveProblemIdx);
+	return selectSubjectiveProblemInfoRow[0];
+}
+
+// 유저 사이트 오류 신고 조회
+async function selectSiteErrorReport(connection) {
+	const selectSiteErrorReportQuery = `
+  SELECT boardIdx, b.userIdx, userName, nickName, userEmail, boardSort, boardTitle
+  FROM Board b
+  LEFT JOIN User u ON u.userIdx = b.userIdx
+  WHERE status = 'N'
+  order by b.createdAt desc
+     `;
+	const [selectSiteErrorReportRow] = await connection.query(selectSiteErrorReportQuery);
+	return selectSiteErrorReportRow;
+}
+
+// 대분류 조회
+async function selectLargeCategory(connection) {
+	const selectLargeCategoryQuery = `
+	SELECT categoryIdx, categoryName, categoryRef, status
+	FROM Category
+	WHERE categoryType = 'L' and status = 'N'
+    `;
+	const [selectLargeCategoryRow] = await connection.query(selectLargeCategoryQuery);
+	return selectLargeCategoryRow;
+}
+
+// 카테고리 분류로 조회
+async function selectCategoryByRef(connection, categoryIdx) {
+	const selectCategoryByRefQuery = `
+	SELECT categoryIdx, categoryName, categoryRef, status
+	FROM Category
+	WHERE categoryRef = ? and status = 'N'
+    `;
+	const [selectCategoryByRefRow] = await connection.query(selectCategoryByRefQuery, categoryIdx);
+	return selectCategoryByRefRow;
+}
+
+// 카테고리 조회
+async function selectCategory(connection, categoryIdx) {
+	const selectCategoryQuery = `
+	SELECT categoryIdx, categoryName, categoryRef, categoryType
+	FROM Category
+	WHERE categoryIdx = ? and status = 'N'
+    `;
+	const [selectCategoryRow] = await connection.query(selectCategoryQuery, categoryIdx);
+	return selectCategoryRow;
+}
+
+// 공기업 회사 구분 조회
+async function selectPublicCompanySort(connection) {
+	const selectPublicCompanySortQuery = `
+	SELECT examSortIdx as 'publicCompanySortIdx', examSortName as 'publicCompanySortName'
+	FROM ExamSort
+	WHERE examSortRef = 2 and status = 'N'
+    `;
+	const [selectPublicCompanySortRow] = await connection.query(selectPublicCompanySortQuery);
+	return selectPublicCompanySortRow;
+}
+
+/**
+ * 기업 회사 구분 조회
+ */
+async function getCompanySort(connection, companyType) {
+	const getCompanySortQuery = `
+	with recursive CTE as (
+		select
+		a.* from ExamSort a where examSortName = ?
+		union all
+		select
+			 b.*
+			  from ExamSort b
+			  join CTE ON b.examSortRef=CTE.examSortIdx where b.status='N'
+		  )
+		  select
+		  M.examSortIdx as companyExamTypeIdx, M.examSortName as companyExamType,
+		  S.examSortIdx as companyExamFieldIdx ,S.examSortName as companyExamField
+		  from CTE L 
+		   join CTE M on M.examSortRef = L.examSortIdx
+		   join CTE S on S.examSortRef = M.examSortIdx
+		   ; 
+	`;
+	const [getCompanySortRow] = await connection.query(getCompanySortQuery, companyType);
+	return getCompanySortRow;
+}
+
+// 공기업의 정보 (companyInfo)에 대한 insert
+async function insertCompanyInfo(connection, examSortIdx, publicCompanyName, companySortLabel) {
+	const insertCompanyInfoQuery = `
+	INSERT INTO CompanyInfo(examSortIdx,companyName,companySortLabel) VALUES ( ?, ? ,? )
+	`;
+
+	const [insertCompanyInfoQueryRow] = await connection.query(insertCompanyInfoQuery, [
+		examSortIdx,
+		publicCompanyName,
+		companySortLabel || 0,
+	]);
+	return insertCompanyInfoQueryRow;
+}
+
+// 공기업 시험관리 조회
+async function selectPublicCompanyExam(connection) {
+	const selectPublicCompanyExamQuery = `
+	SELECT e.*, domain, education
+	FROM Exam e
+	LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx AND ce.status = 'N'
+	LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx AND es.status = 'N'
+	WHERE e.status = 'N'
+    `;
+	const [selectPublicCompanyExamRow] = await connection.query(selectPublicCompanyExamQuery);
+	return selectPublicCompanyExamRow;
+}
+// 특정 문제 카테고리 조회
+async function selectCategoryByProblemIdx(connection, multipleProblemIdx) {
+	const selectCategoryByProblemIdxQuery = `
+	WITH RECURSIVE CTE(categoryIdx, categoryName, categoryRef, categoryType, status) AS(
+		SELECT c.categoryIdx, categoryName, categoryRef, c.categoryType, status
+		FROM Category c
+        JOIN ProblemCategory pc on pc.categoryIdx = c.categoryIdx
+		where c.status = 'N' and multipleProblemIdx = ?
+		UNION ALL
+		SELECT t.categoryIdx, t.categoryName, t.categoryRef, t.categoryType, t.status
+		FROM Category t  
+        JOIN CTE c ON c.categoryRef = t.categoryIdx
+		WHERE t.status = 'N'
+		)
+	  SELECT
+		L.categoryIdx as 'largeCategoryIdx',
+		L.categoryName as 'largeCategoryName',
+		L.categoryType as 'largeCategoryType',
+		M.categoryIdx as 'mediumCategoryIdx',
+		M.categoryName as 'mediumCategoryName',
+		M.categoryType as 'mediumCategoryType',
+		S.categoryIdx as 'smallCategoryIdx',
+		S.categoryName as 'smallCategoryName',
+		S.categoryType as 'smallCategoryType',
+		XS.categoryIdx as 'xsmallCategoryIdx',
+		XS.categoryName as 'xsmallCategoryName',
+		XS.categoryType 
+        as 'xsmallCategoryType'
+	  FROM
+		CTE as L
+	  LEFT JOIN CTE M on M.categoryRef = L.categoryIdx and M.categoryType = 'M' AND M.status = 'N'
+	  LEFT JOIN CTE S on S.categoryRef = M.categoryIdx and S.categoryType = 'S' AND S.status = 'N'
+	  LEFT JOIN CTE XS on XS.categoryRef = S.categoryIdx and XS.categoryType = 'XS' AND XS.status = 'N'
+	  where L.categoryType = 'L' AND L.status = 'N'
+`;
+	const [selectCategoryByProblemIdxRow] = await connection.query(selectCategoryByProblemIdxQuery, multipleProblemIdx);
+	return selectCategoryByProblemIdxRow;
+}
+
+// 공기업 특정 회사 시험 조회
+async function selectExamByCompany(connection, where) {
+	const selectExamByCompanyQuery =
+		`
+	SELECT e.examIdx as 'publicCompanyExamIdx', examName as 'publicCompanyExamName'
+	FROM Exam e
+	` + where;
+	const [selectExamByCompanyRow] = await connection.query(selectExamByCompanyQuery);
+	return selectExamByCompanyRow;
+}
+
+// 해당 기업 시험의 category에 대한 중복 여부 검사
+async function checkCompanyExamCategory(connection, examIdx, category) {
+	const categoryCondition = category.length ? `category = '${category}'` : `category is null`;
+	const checkCompanyExamCategoryQuery = `
+	select exists ( select * from CompanyExam where examIdx = ? and ${categoryCondition} and status ="N" ) as exist;
+	`;
+	const [checkCompanyExamCategoryRow] = await connection.query(checkCompanyExamCategoryQuery, examIdx);
+	return checkCompanyExamCategoryRow;
+}
+
+// 공기업 특정 회사 시험 조회
+async function selectExamBySearch(connection, examSortIdx) {
+	const selectExamBySearchQuery = `
+	SELECT e.examIdx as 'publicCompanyExamIdx', examName as 'publicCompanyExamName', domain, education
+	FROM Exam e
+	LEFT JOIN CompanyExam ce on ce.examIdx = e.examIdx
+    WHERE examSortIdx = ? AND status = 'N' `;
+	const [selectExamBySearchRow] = await connection.query(selectExamBySearchQuery, examSortIdx);
+	return selectExamBySearchRow;
+}
+
+// 공기업 시험 회차 조회
+async function selectExamDetailByExam(connection, examIdx) {
+	const selectExamDetailByExamQuery = `
+    SELECT examDetailIdx, date_format(examDate, '%Y-%m-%d') as 'date', examRound, isPublic, examDetailUrl, publicLevel
+    FROM ExamDetail ed
+    WHERE examIdx = ? and status = 'N'
+    `;
+	const [selectExamDetailByExamRow] = await connection.query(selectExamDetailByExamQuery, examIdx);
+	return selectExamDetailByExamRow;
+}
+
+// 카테고리 분류
+async function selectLargeCategoryRef(connection, categoryIdx) {
+	const selectLargeCategoryRefQuery = `
+	with recursive CTE AS   (SELECT 
+		categoryIdx, categoryName, status, categoryRef, categoryType
+		FROM Category 
+		WHERE categoryRef = ?  and status = 'N'
+		UNION ALL
+		SELECT
+		a.categoryIdx, a.categoryName,a.status,a.categoryRef, a.categoryType
+		FROM Category a
+		INNER JOIN CTE b ON a.categoryRef = b.categoryIdx 
+		)
+		SELECT categoryIdx, categoryName, status, categoryRef, categoryType FROM CTE where status = 'N';
+    `;
+	const [selectLargeCategoryRefRow] = await connection.query(selectLargeCategoryRefQuery, categoryIdx);
+	return selectLargeCategoryRefRow;
+}
+
+// 공기업 분류 조회 & 대기업
+async function selectPublicCompanyExamList(connection, where) {
+	const selectPublicCompanyExamListQuery =
+		`
+	SELECT 
+	    e.examIdx as 'publicCompanyExamIdx', examName as 'publicCompanyExamName', passScore, timeLimit, problemCount, accessLevel, examUrl, questionType,
+        domain, cs.education,
+		S.examSortIdx as 'publicCompanyIdx', 
+        S.examSortName as 'publicCompanyName',
+        S.examSortType as 'publicCompanyType',
+		M.examSortIdx as 'publicCompanySortIdx', 
+        M.examSortName as 'publicCompanySortName',
+        M.examSortType as 'publicCompanySortType',
+        L.examSortIdx as 'largeExamSortIdx', 
+        L.examSortName as 'largeExamSortName', 
+        L.examSortType as 'largeExamSortType'
+        FROM Exam as e
+        LEFT JOIN CompanyExam cs on cs.examIdx = e.examIdx
+        LEFT JOIN ExamSort S on e.examSortIdx = S.examSortIdx and S.status = 'N'
+        LEFT JOIN ExamSort M on S.examSortRef = M.examSortIdx and M.status = 'N'
+        LEFT JOIN ExamSort L on M.examSortRef = L.examSortIdx and L.status = 'N'
+	` + where;
+	const [selectPublicCompanyExamListRow] = await connection.query(selectPublicCompanyExamListQuery);
+	return selectPublicCompanyExamListRow;
+}
+
+/**
+ * 기업 분류 조회 Query
+ * @changeFrom selectPublicCompanyExamList
+ * @param {String} companyType 기업의 종류에 대한 변수
+ * 기업의 중분류에 해당하는 companyExamType을 반환값으로 전달해줘서 클라이언트가 이를 확인하고 볼 수 있도록 해야한다.
+ */
+async function selectCompanyExamList(connection, companyType) {
+	const selectCompanyExamListQuery = `
+	WITH RECURSIVE CTE AS (
+		SELECT
+		A.* FROM ExamSort A WHERE examSortName IN (?)
+		UNION ALL
+		SELECT
+		B.*
+		FROM ExamSort B
+		JOIN CTE ON B.examSortRef=CTE.examSortIdx WHERE B.status='N'
+		)
+		SELECT L.examSortIdx AS companyTypeIdx, L.examSortName AS companyType, M.examSortIdx AS companyExamTypeIdx,
+		M.examSortName AS companyExamType,
+		S.examSortIdx AS companyExamFieldIdx ,S.examSortName AS companyExamField,
+		E.examIdx,E.examName,passScore, timeLimit, problemCount, accessLevel, examUrl, questionType,
+		CE.companyName, CE.education, CE.category, CE.domain
+		FROM CTE L
+		JOIN CTE M ON M.examSortRef = L.examSortIdx
+		JOIN CTE S ON S.examSortRef = M.examSortIdx
+		JOIN CompanyExam CE ON CE.examSortIdx_S = S.examSortIdx
+		JOIN Exam E ON E.examIdx = CE.examIdx
+		WHERE E.status = "N"
+		;
+	`;
+	const [selectCompanyExamListRow] = await connection.query(selectCompanyExamListQuery, [companyType]);
+	return selectCompanyExamListRow;
+}
+
+// // 공기업 회사목록 조회
+// async function selectPublicCompanyList(connection) {
+// 	const selectPublicCompanyExamListQuery = `
+// 	SELECT
+// 	S.examSortIdx, S.examSortName as 'companyName'
+//     FROM Exam as e
+//     LEFT JOIN CompanyExam cs on cs.examIdx = e.examIdx
+//     LEFT JOIN ExamSort S on e.examSortIdx = S.examSortIdx
+//     LEFT JOIN ExamSort M on S.examSortRef = M.examSortIdx
+//     LEFT JOIN ExamSort L on M.examSortRef = L.examSortIdx
+//     where e.status = 'N' and L.examSortName = '공기업'
+// 	`;
+// 	const [selectPublicCompanyExamListRow] = await connection.query(selectPublicCompanyExamListQuery);
+// 	return selectPublicCompanyExamListRow;
+// }
+
+// // 공기업 회사관리 조회
+// async function selectPublicCompanyManagement(connection) {
+// 	const selectPublicCompanyManagementQuery = `
+// 	SELECT
+// 	S.examSortIdx, S.examSortName as 'companyName', M.examSortName as 'companySort', examSortUrl as 'companyThumbnail'
+//     FROM ExamSort S
+//     LEFT JOIN ExamSort M on S.examSortRef = M.examSortIdx AND
+//     LEFT JOIN ExamSort L on M.examSortRef = L.examSortIdx AND
+//     LEFT JOIN ExamSortImage esi on esi.examSortIdx = S.examSortIdx
+//     where S.status = 'N' and L.examSortName = '공기업'
+// 	`;
+// 	const [selectPublicCompanyManagementRow] = await connection.query(selectPublicCompanyManagementQuery);
+// 	return selectPublicCompanyManagementRow;
+// }
+
+// examSortName 조회
+async function selectExamSortName(connection, examSortIdx) {
+	const selectExamSortNameQuery = `
+	select examSortName
+	from ExamSort
+	where examSortIdx = ? AND status = 'N'
+	`;
+	const [selectExamSortNameRow] = await connection.query(selectExamSortNameQuery, examSortIdx);
+	return selectExamSortNameRow;
+}
+
+// 공기업 목록 조회
+// 프론트에서 검색하는거로 바뀌면 where 필요없을듯
+async function selectPublicCompanyConditionList(connection, where, companySortLabel) {
+	const selectPublicCompanyConditionListQuery =
+		`
+		SELECT 
+	S.examSortIdx as 'publicCompanyIdx', S.examSortName as 'publicCompanyName', 
+	M.examSortName as 'publicCompanySortName', examSortUrl as 'publicCompanyThumbnail'
+    FROM ExamSort S 
+    LEFT JOIN ExamSort M on S.examSortRef = M.examSortIdx and M.status = 'N'
+    LEFT JOIN ExamSort L on M.examSortRef = L.examSortIdx and L.status = 'N'
+    LEFT JOIN ExamSortImage esi on esi.examSortIdx = S.examSortIdx
+	LEFT JOIN CompanyInfo ci on ci.examSortIdx = S.examSortIdx
+	` + where;
+	const [selectPublicCompanyConditionListRow] = await connection.query(selectPublicCompanyConditionListQuery);
+	return selectPublicCompanyConditionListRow;
+}
+
+/**
+ * 기업 회사 관리 조회 쿼리
+ * @changeFrom selectPublicCompanyConditionList
+ */
+async function getCompanyList(connection, companyType) {
+	const getCompanyListQuery = `
+	with recursive CTE as (
+		select
+		a.* from ExamSort a where examSortName = ?
+		union all
+		select
+			b.*
+			from ExamSort b
+		join CTE ON b.examSortRef=CTE.examSortIdx where b.status='N'
+		)
+		select distinct
+		M.examSortIdx as companyExamTypeIdx, M.examSortName as companyExamType,
+		S.examSortIdx as companyExamFieldIdx ,S.examSortName as companyExamField,
+		ce.companyName, COALESCE(NULLIF( ce.companyUrl, ''), '') as companyUrl
+		from CTE L 
+		join CTE M on M.examSortRef = L.examSortIdx
+		join CTE S on S.examSortRef = M.examSortIdx
+		join CompanyExam ce on ce.examSortIdx_S = S.examSortIdx
+		where ce.status = "N" ;
+	`;
+	const [getCompanyListRow] = await connection.query(getCompanyListQuery, companyType);
+	return getCompanyListRow;
+}
+
+// 객관식 문제에 대한 정보 조회
+async function selectProblem(connection, multipleProblemIdx) {
+	const selectProblemQuery = `
+	SELECT pe.problemNum, 
+    problem, solution, provisionNum, pc.categoryIdx, isKatex, problemScore, problemUrl, lectureUrl, problemImage, isDelete,
+	questionNum as answerNum, examSubjectIdx
+    FROM MultipleProblem mp 
+	join ProblemExam pe on mp.multipleProblemIdx=pe.multipleProblemIdx
+    left join ProblemSubject ps on ps.multipleProblemIdx=mp.multipleProblemIdx
+    left join ProblemCategory pc on pc.multipleProblemIdx=mp.multipleProblemIdx
+    left join Category c on pc.categoryIdx=c.categoryIdx and c.status = 'N'
+	left join Question q on q.multipleProblemIdx = mp.multipleProblemIdx and isAnswer = 1 and q.status = 'N' 
+    WHERE mp.status = 'N' AND mp.multipleProblemIdx = ?
+    group by mp.multipleProblemIdx
+	`;
+	const selectProblemRow = await connection.query(selectProblemQuery, multipleProblemIdx);
+	return selectProblemRow[0];
+}
+
+// 주관식 문제에 대한 정보 조회
+async function selectSubjectiveProblem(connection, subjectiveProblemIdx) {
+	const subjectiveSelectProblemQuery = `
+	SELECT spe.problemNum,examHistory 
+    problem,problemImage, solution,solutionImage, spc.categoryIdx, isKatex, problemScore, problemUrl, lectureUrl, isDelete,
+	 sps.examSubjectIdx
+    FROM SubjectiveProblem sp 
+	join SubjectiveProblemExam spe on sp.subjectiveProblemIdx=spe.subjectiveProblemIdx
+    left join SubjectiveProblemSubject sps on sps.subjectiveProblemIdx=sp.subjectiveProblemIdx
+    left join SubjectiveProblemCategory spc on spc.subjectiveProblemIdx=sp.subjectiveProblemIdx
+    left join Category c on spc.categoryIdx=c.categoryIdx and c.status = 'N'
+    WHERE sp.status = 'N' AND sp.subjectiveProblemIdx = ? group by sp.subjectiveProblemIdx;
+	`;
+	const subjectiveSelectProblemRow = await connection.query(subjectiveSelectProblemQuery, subjectiveProblemIdx);
+	return subjectiveSelectProblemRow[0];
+}
+
+// 객관식 문제, 이미지, 해설 수정
+async function updateMultipleProblem(connection, updateMultipleProblemParams) {
+	const updateMultipleProblemQuery = `
+	UPDATE MultipleProblem mp
+      SET problem = ?,solution = ?, provisionNum = ?,  isKatex = ?,  problemScore = ?, problemUrl = ?, lectureUrl = ?, problemImage = ?, isDelete = ?, examHistory=?
+	  WHERE mp.multipleProblemIdx = ?`;
+
+	const [updateMultipleProblemRow] = await connection.query(updateMultipleProblemQuery, updateMultipleProblemParams);
+	return updateMultipleProblemRow;
+}
+// 주관식 문제, 이미지, 해설 수정
+async function updateSubjectiveProblem(connection, updateSubjectiveProblemParams) {
+	const updateSubjectiveProblemQuery = `
+	UPDATE SubjectiveProblem sp
+      SET  problem = ?, problemImage = ?, solution = ?,  solutionImage = ?,  isKatex = ?  , problemScore = ?,problemUrl = ?, lectureUrl = ?, isDelete = ?, examHistory = ?
+	  WHERE sp.subjectiveProblemIdx = ?`;
+
+	const [updateSubjectiveProblemRow] = await connection.query(
+		updateSubjectiveProblemQuery,
+		updateSubjectiveProblemParams,
+	);
+	return updateSubjectiveProblemRow;
+}
+//  공기업문제, 이미지, 해설 수정
+// async function updateMultipleCompanyProblem(connection, updateMultipleProblemParams) {
+// 	const updateMultipleCompanyProblemQuery = `
+// 	UPDATE MultipleProblem mp
+// 	LEFT JOIN ProblemExam pe on pe.multipleProblemIdx = mp.multipleProblemIdx and examDetailIdx = ?
+// 	LEFT JOIN ProblemCategory pc on pc.multipleProblemIdx = mp.multipleProblemIdx
+//       SET problemNum = ? , problem = ?, problemImage = ?,  solution = ?, isKatex = ?, provisionNum = ?, isDelete = ?, problemScore = ?  , lectureUrl = ?
+// 	  WHERE mp.multipleProblemIdx = ?`;
+
+// 	const [updateMultipleCompanyProblemRow] = await connection.query(
+// 		updateMultipleCompanyProblemQuery,
+// 		updateMultipleProblemParams,
+// 	);
+// 	return updateMultipleCompanyProblemRow;
+// }
+
+// 객관식 문항 수정
+async function updateQuestionProblemIdx(connection, updateQuestionProblemIdxParams) {
+	const updateQuestionProblemIdxQuery = `
+      UPDATE Question 
+      SET multipleProblemIdx = ?
+      WHERE multipleProblemIdx = ?`;
+	const [updateQuestionProblemIdxRow] = await connection.query(
+		updateQuestionProblemIdxQuery,
+		updateQuestionProblemIdxParams,
+	);
+	return updateQuestionProblemIdxRow;
+}
+
+// 객관식 문항 수정
+async function updateQuestion(connection, questionUpdate, questionImageUpdate, updateQuestionParams) {
+	const updateQuestionQuery =
+		` UPDATE Question SET question = CASE questionNum ` +
+		questionUpdate +
+		`
+  ELSE question
+  END, 
+  questionImage = CASE questionNum 
+  ` +
+		questionImageUpdate +
+		`
+  ELSE questionImage
+  END,
+  isAnswer = case questionNum
+  when ? then 1 else 0 end
+  WHERE multipleProblemIdx = ?;
+  `;
+
+	const [updateQuestionProblemIdxRow] = await connection.query(updateQuestionQuery, updateQuestionParams);
+	return updateQuestionProblemIdxRow;
+}
+
+// 시험 정보 수정
+async function updateExam(connection, setInfo, updateQuestionParams) {
+	const updateQuestionQuery =
+		`
+      UPDATE Exam 
+      SET ` +
+		setInfo +
+		`
+      WHERE examIdx = ?
+      `;
+	const [updateQuestionRow] = await connection.query(updateQuestionQuery, updateQuestionParams);
+	return updateQuestionRow;
+}
+
+// 시험 상세 정보 수정
+async function updateExamDetail(connection, updateQuestionParams) {
+	const updateQuestionQuery = `
+      UPDATE ExamDetail 
+      SET examDate = ?, examRound = ?, isPublic = ?, publicLevel = ?
+      WHERE examDetailIdx = ?
+      `;
+	const [updateQuestionRow] = await connection.query(updateQuestionQuery, updateQuestionParams);
+	return updateQuestionRow;
+}
+
+// 시험 과목 수정
+async function updateExamSubject(connection, updateExamSubjectParams) {
+	const updateExamSubjectQuery = `
+      UPDATE ExamSubject 
+      SET examSubjectName = ? , passScore = ?
+      WHERE examSubjectIdx = ?
+      `;
+	const [updateExamSubjectRow] = await connection.query(updateExamSubjectQuery, updateExamSubjectParams);
+	return updateExamSubjectRow;
+}
+
+// 오답노트 문제 인덱스 수정
+async function updateReviewNoteTrigger(connection, preMultipleProblemIdx, multipleProblemIdx) {
+	const updateReviewNoteTriggerQuery = `
+      UPDATE ReviewNote 
+      SET multipleProblemIdx = ?
+      WHERE multipleProblemIdx = ?
+      `;
+	const [updateReviewNoteTriggerRow] = await connection.query(updateReviewNoteTriggerQuery, [
+		preMultipleProblemIdx,
+		multipleProblemIdx,
+	]);
+	return updateReviewNoteTriggerRow;
+}
+
+// 시험기록 문제 인덱스 수정
+async function updateExamRecordTrigger(connection, preMultipleProblemIdx, multipleProblemIdx) {
+	const updateExamRecordTriggerQuery = `
+      UPDATE UserExamDetailRecord 
+      SET multipleProblemIdx = ?
+      WHERE multipleProblemIdx = ?
+      `;
+	const [updateExamRecordTriggerRow] = await connection.query(updateExamRecordTriggerQuery, [
+		preMultipleProblemIdx,
+		multipleProblemIdx,
+	]);
+	return updateExamRecordTriggerRow;
+}
+
+// 시험 과목 번호 수정
+async function updateExamSubjectNum(connection, examSubjectNum, examIdx, examSubjectIdx) {
+	const updateExamSubjectNumQuery = `
+      UPDATE ExamSubjectMulti 
+      SET examSubjectNum = ?
+      WHERE examIdx = ? and examSubjectIdx = ?
+      `;
+	const [updateExamSubjectNumRow] = await connection.query(updateExamSubjectNumQuery, [
+		examSubjectNum,
+		examIdx,
+		examSubjectIdx,
+	]);
+	return updateExamSubjectNumRow;
+}
+
+//유저 권한 기한 차감
+async function subUserAuth(connection, deleteUserAuthParams) {
+	const updateUserAuthQuery = `
+	UPDATE UserAuth SET expireAt = DATE_SUB(expireAt, INTERVAL ? day) WHERE userIdx = ? AND productIdx = ?
+	`;
+	const [updateUserAuthRow] = await connection.query(updateUserAuthQuery, deleteUserAuthParams);
+	return updateUserAuthRow;
+}
+
+//유저 권한 기한 연장
+async function extendUserAuth(connection, deleteUserAuthParams) {
+	const updateUserAuthQuery = `
+	UPDATE UserAuth SET expireAt = DATE_ADD(expireAt, INTERVAL ? day) WHERE userIdx = ? AND productIdx = ?
+	`;
+	const [updateUserAuthRow] = await connection.query(updateUserAuthQuery, deleteUserAuthParams);
+	return updateUserAuthRow;
+}
+//유저 권한 기간 확인
+async function getUserAuthExpireAt(connection, userIdx) {
+	const selectUserAuthExpireAtQuery = `
+	SELECT expireAt from UserAuth where userIdx = ?
+	`;
+	const [selectUserAuthExpireAtRow] = await connection.query(selectUserAuthExpireAtQuery, userIdx);
+	return selectUserAuthExpireAtRow;
+}
+
+//자식 상품 기간 확인
+async function selectChildDuration(connection, productIdx) {
+	const selectChildDurationQuery = `
+	SELECT duration from Product p
+	LEFT JOIN ProductRelation pr on p.productIdx = pr.productIdx
+	where pr.parentIdx = ?
+	UNION
+	SELECT duration from Product
+	where productIdx = ?
+	`;
+	const [selectChildDurationRow] = await connection.query(selectChildDurationQuery, [productIdx, productIdx]);
+	return selectChildDurationRow;
+}
+// 유저 상태 수정
+async function updateUserStatus(connection, status, accessLevel, userIdx) {
+	const updateUserStatusQuery = `
+      UPDATE User 
+      SET status = ?, accessLevel = ?
+      WHERE userIdx = ?
+      `;
+	const [updateUserStatusRow] = await connection.query(updateUserStatusQuery, [status, accessLevel, userIdx]);
+	return updateUserStatusRow;
+}
+
+// 회사 수정
+// connection, publicCompanyIdx, publicCompanySortIdx, publicCompanyName;
+async function updatePublicCompany(connection, examSortIdx, examSortRef) {
+	const updatePublicCompanyQuery = `
+    UPDATE ExamSort SET examSortRef = ?, examSortName = ?
+    WHERE examSortIdx = ?
+	`;
+	const updatePublicCompanyRow = await connection.query(updatePublicCompanyQuery, [
+		examSortRef,
+		examSortName,
+		examSortIdx,
+	]);
+	return updatePublicCompanyRow;
+}
+
+async function updateCompany(
+	connection,
+	prevSortIdx,
+	prevCompanyName,
+	companyExamFieldIdx,
+	companyName,
+	companyThumbnail,
+) {
+	const updateCompanyQuery = `
+	UPDATE CompanyExam SET examSortIdx_S = ?, companyName = ?, companyUrl = ?
+    WHERE examSortIdx_S = ? AND companyName = ? 
+	`;
+	const [updateCompanyRow] = await connection.query(updateCompanyQuery, [
+		companyExamFieldIdx,
+		companyName,
+		companyThumbnail,
+		prevSortIdx,
+		prevCompanyName,
+	]);
+	return updateCompanyRow;
+}
+
+// 공기업인지 대기업인지 수정
+async function updateCompanySort(connection, publicCompanyIdx, companySortLabel) {
+	const updateCompanySortQuery = `
+	UPDATE CompanyInfo SET companySortLabel = ? 
+	where examSortIdx = ?
+`;
+	const [updateCompanySortRow] = await connection.query(updateCompanySortQuery, [companySortLabel, publicCompanyIdx]);
+	return updateCompanySortRow;
+}
+
+// 시험 종류 수정
+async function updateExamSort(connection, updateExamSortParams) {
+	const updateExamSortQuery = `
+      UPDATE ExamSort 
+      SET examSortName = ? , examSortRef = ?, examSortType = ?
+      WHERE examSortIdx = ?
+      `;
+	const [updateExamSortRow] = await connection.query(updateExamSortQuery, updateExamSortParams);
+	return updateExamSortRow;
+}
+
+// 시험 구분 수정
+// async function updateExamSortRef(connection, modExamSortRef, examSortRef) {
+// 	const updateExamSortRefQuery = `
+//       UPDATE ExamSort
+//       SET examSortRef = ?
+//       WHERE examSortRef = ?
+//       `;
+// 	const [updateExamSortRefRow] = await connection.query(updateExamSortRefQuery, [modExamSortRef, examSortRef]);
+// 	return updateExamSortRefRow;
+// }
+
+// 검색 복원 문제 수정
+async function updateRestoration(connection, set, restorationIdx) {
+	const updateRestorationQuery =
+		`
+	UPDATE Restoration
+	SET  ` +
+		set +
+		`
+     WHERE restorationIdx = ? 
+     `;
+	const [updateRestorationRow] = await connection.query(updateRestorationQuery, restorationIdx);
+	return updateRestorationRow;
+}
+
+// 복원 회차 수정
+async function updateRestoreExamDetail(connection, updateRestoreExamDetailParams) {
+	const updateRestoreExamDetailQuery = `
+     UPDATE RestoreExamDetail
+     SET  restoreExamDate = ?, restoreExamRound = ?, thumbnail = ? , isPublic = ?
+     WHERE restoreExamDetailIdx = ? 
+     `;
+	const [updateRestoreExamDetailRow] = await connection.query(
+		updateRestoreExamDetailQuery,
+		updateRestoreExamDetailParams,
+	);
+	return updateRestoreExamDetailRow;
+}
+
+// 수험표 권한 수정
+async function updateAdmissionTicketAuth(connection, isAuthentication, examAdmissionTicketIdx) {
+	const updateAdmissionTicketAuthQuery = `
+     UPDATE ExamAdmissionTicket
+     SET  isAuthentication = ?
+     WHERE examAdmissionTicketIdx = ? 
+     `;
+	const [updateAdmissionTicketAuthRow] = await connection.query(updateAdmissionTicketAuthQuery, [
+		isAuthentication,
+		examAdmissionTicketIdx,
+	]);
+	return updateAdmissionTicketAuthRow;
+}
+
+//복원 댓글 공개, 비공개 전환
+async function updateRestorationCommentIsPublic(connection, restorationCommentIdx, isPublic) {
+	const updationCommentQuery = `
+	update RestorationComment set isPublic=? where restorationCommentIdx=?
+	`;
+	const [updationCommentRow] = await connection.query(updationCommentQuery, [isPublic, restorationCommentIdx]);
+	return updationCommentRow;
+}
+
+// 검색 문제 수정
+async function updateRestorationSearch(connection, content, multipleProblemIdx) {
+	const updateRestorationSearchQuery = `
+	update RestorationSearch set content = ? where multipleProblemIdx = ?
+	`;
+	const [updateRestorationSearchRow] = await connection.query(updateRestorationSearchQuery, [
+		content,
+		multipleProblemIdx,
+	]);
+	return updateRestorationSearchRow;
+}
+
+// 문제 카테고리 수정
+async function updateCategory(connection, categoryName, categoryRef, categoryType, categoryIdx) {
+	const updateCategoryQuery = `
+	update Category set categoryName = ?,categoryRef = ?, categoryType = ? where categoryIdx = ?
+	`;
+	const [updateCategoryRow] = await connection.query(updateCategoryQuery, [
+		categoryName,
+		categoryRef,
+		categoryType,
+		categoryIdx,
+	]);
+	return updateCategoryRow;
+}
+
+// 객관식 문제 검색 삽입  중복시 update
+async function upsertProblemSubject(connection, multipleProblemIdx, originExamSubjectIdx, examSubjectIdx) {
+	const insertProblemSubjectQuery = `
+  INSERT INTO ProblemSubject (multipleProblemIdx, examSubjectIdx)
+  VALUES (?, ?) ON DUPLICATE KEY UPDATE examSubjectIdx = ?`;
+	const insertProblemSubjectRow = await connection.query(insertProblemSubjectQuery, [
+		multipleProblemIdx,
+		originExamSubjectIdx,
+		examSubjectIdx,
+	]);
+	return insertProblemSubjectRow;
+}
+
+// 주관식 문제 과목 삽입 중복시 update
+async function upsertSubjectiveProblemSubject(connection, subjectiveProblemIdx, originExamSubjectIdx, examSubjectIdx) {
+	const upsertSubjectiveProblemSubjectQuery = `
+  INSERT INTO SubjectiveProblemSubject (subjectiveProblemIdx, examSubjectIdx)
+  VALUES (?, ?) ON DUPLICATE KEY UPDATE examSubjectIdx = ?`;
+	const upsertSubjectiveProblemSubjectRow = await connection.query(upsertSubjectiveProblemSubjectQuery, [
+		subjectiveProblemIdx,
+		originExamSubjectIdx,
+		examSubjectIdx,
+	]);
+	return upsertSubjectiveProblemSubjectRow;
+}
+
+// 객관식 문제 검색 삽입  중복시 update
+async function upsertProblemCategory(connection, multipleProblemIdx, originCategoryIdx, categoryIdx) {
+	const insertProblemCategoryQuery = `
+  INSERT INTO ProblemCategory (multipleProblemIdx, categoryIdx)
+  VALUES (?, ?) ON DUPLICATE KEY UPDATE categoryIdx = ?`;
+	const insertProblemCategoryRow = await connection.query(insertProblemCategoryQuery, [
+		multipleProblemIdx,
+		originCategoryIdx,
+		categoryIdx,
+	]);
+	return insertProblemCategoryRow;
+}
+
+// 주관식 문제 유형 삽입 중복시 update
+async function upsertSubjectiveProblemCategory(connection, subjectiveProblemIdx, originCategoryIdx, categoryIdx) {
+	const insertProblemCategoryQuery = `
+  INSERT INTO SubjectiveProblemCategory (subjectiveProblemIdx, categoryIdx)
+  VALUES (?, ?) ON DUPLICATE KEY UPDATE categoryIdx = ?`;
+	const insertProblemCategoryRow = await connection.query(insertProblemCategoryQuery, [
+		subjectiveProblemIdx,
+		originCategoryIdx,
+		categoryIdx,
+	]);
+	return insertProblemCategoryRow;
+}
+
+// 회차 문제 번호 삽입  중복시 update
+async function upsertProblemExam(connection, updateParams) {
+	const insertProblemCategoryQuery = `
+  INSERT INTO ProblemExam (examDetailIdx, multipleProblemIdx, problemNum)
+  VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE problemNum = ?`;
+	const insertProblemCategoryRow = await connection.query(insertProblemCategoryQuery, updateParams);
+	return insertProblemCategoryRow;
+}
+
+// 회차 주관식 문제 번호 삽입  중복시 update
+async function upsertSubjectiveProblemExam(connection, updateParams) {
+	const upsertSubjectiveProblemExamQuery = `
+  INSERT INTO SubjectiveProblemExam (examDetailIdx, subjectiveProblemIdx, problemNum)
+  VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE problemNum = ?`;
+	const upsertSubjectiveProblemExamRow = await connection.query(upsertSubjectiveProblemExamQuery, updateParams);
+	return upsertSubjectiveProblemExamRow;
+}
+
+// 구매 상품 환불 정보 수정
+async function updatePaymentProductRefund(connection, updatePaymentProductParams) {
+	const updatePaymentProductQuery = `
+	update PaymentProduct set refundPrice = ?, refundReason = ?, refundAt = ?, isRefund = ? where paymentProductIdx = ?
+	`;
+	const [updatePaymentProductRow] = await connection.query(updatePaymentProductQuery, updatePaymentProductParams);
+	return updatePaymentProductRow;
+}
+
+//복원 댓글 공개, 비공개 전환
+async function updateProblemNum(connection, problemNum, multipleProblemIdx) {
+	const updateProblemNumQuery = `
+	update ProblemExam set problemNum = ? where restorationCommentIdx=?
+	`;
+	const [updateProblemNumRow] = await connection.query(updateProblemNumQuery, [problemNum, multipleProblemIdx]);
+	return updateProblemNumRow;
+}
+
+// 수험표 삭제
+async function deleteExamAdmissionTicket(connection, examAdmissionTicketIdx) {
+	const deleteExamAdmissionTicketQuery = `
+	UPDATE ExamAdmissionTicket 
+    SET status = 'Y'
+    WHERE examAdmissionTicketIdx = ?
+	`;
+	const deleteExamAdmissionTicketRow = await connection.query(deleteExamAdmissionTicketQuery, examAdmissionTicketIdx);
+	return deleteExamAdmissionTicketRow[0];
+}
+
+// 복원 회차 삭제
+async function deleteRestoreExamDetail(connection, restoreExamDetailIdx) {
+	const deleteRestoreExamDetailQuery = `
+	UPDATE RestoreExamDetail red
+	LEFT JOIN ExamAdmissionTicket eat ON eat.restoreExamDetailIdx = red.restoreExamDetailIdx
+	SET  red.status = 'Y', eat.status = 'Y'
+     WHERE red.restoreExamDetailIdx = ? 
+     `;
+	const [deleteRestoreExamDetailRow] = await connection.query(deleteRestoreExamDetailQuery, restoreExamDetailIdx);
+	return deleteRestoreExamDetailRow;
+}
+
+// 에러 신고 삭제
+async function deleteProblemError(connection, boardIdx, solution) {
+	const deleteProblemErrorQuery = `
+     UPDATE ProblemError
+     SET status = 'Y', solution = ?
+     WHERE problemErrorIdx = ? 
+     `;
+	const [deleteProblemErrorRow] = await connection.query(deleteProblemErrorQuery, [solution, boardIdx]);
+	return deleteProblemErrorRow;
+}
+
+// 검색 복원 문제 댓글 수정
+async function deleteRestorationComment(connection, restorationCommentIdx) {
+	const deleteRestorationCommentQuery = `
+	UPDATE RestorationComment
+	SET  status = 'Y'
+     WHERE restorationCommentIdx = ? 
+     `;
+	const [deleteRestorationCommentRow] = await connection.query(deleteRestorationCommentQuery, restorationCommentIdx);
+	return deleteRestorationCommentRow;
+}
+
+// 검색 복원 문제 댓글 삭제 by Foreign Key
+async function deleteRestorationCommentFk(connection, restorationIdx) {
+	const deleteRestorationCommentFkQuery = `
+	UPDATE RestorationComment
+	SET  status = 'Y'
+     WHERE restorationIdx = ? 
+     `;
+	const [deleteRestorationCommentFkRow] = await connection.query(deleteRestorationCommentFkQuery, restorationIdx);
+	return deleteRestorationCommentFkRow;
+}
+
+// 검색 복원 문제 삭제 by Foreign Key
+async function deleteRestorationFk(connection, restoreExamDetailIdx) {
+	const deleteRestorationFkQuery = `
+	update Restoration as r
+left join RestorationComment as rc on rc.restorationIdx = r.restorationIdx
+left join UserRestoration as ur on ur.restorationIdx = r.restorationIdx 
+set r.status = 'Y', rc.status='Y', ur.status = 'Y'
+where r.restoreExamDetailIdx = ?
+     `;
+	const [deleteRestorationFkRow] = await connection.query(deleteRestorationFkQuery, restoreExamDetailIdx);
+	return deleteRestorationFkRow;
+}
+
+// 검색 복원 문제 삭제 by Foreign Key
+async function deleteRestoration(connection, restorationIdx) {
+	const deleteRestorationQuery = `
+	update Restoration as r
+left join RestorationComment as rc on rc.restorationIdx = r.restorationIdx
+left join UserRestoration as ur on ur.restorationIdx = r.restorationIdx 
+set r.status = 'Y', rc.status='Y', ur.status = 'Y'
+where r.restorationIdx = ?
+     `;
+	const [deleteRestorationRow] = await connection.query(deleteRestorationQuery, restorationIdx);
+	return deleteRestorationRow;
+}
+// 검색 복원 문제 삭제 by Foreign Key
+async function deleteCustomProblem(connection, restoreExamDetailIdx) {
+	const deleteCustomProblemQuery = `
+	update CustomProblem as cp
+left join CustomProblemImage as cpi on cpi.customProblemIdx = cp.customProblemIdx
+left join CustomQuestion as cq on cq.customProblemIdx = cp.customProblemIdx 
+set cp.status = 'Y', cq.status='Y', cpi.status = 'Y'
+where cp.restoreExamDetailIdx =  ?
+     `;
+	const [deleteCustomProblemRow] = await connection.query(deleteCustomProblemQuery, restoreExamDetailIdx);
+	return deleteCustomProblemRow;
+}
+
+// 검색 복원 문제 삭제 by Foreign Key
+async function deleteUserRestoration(connection, restorationIdx) {
+	const deleteUserRestorationQuery = `
+	UPDATE UserRestoration
+	SET  status = 'Y'
+     WHERE restorationIdx = ? 
+     `;
+	const [deleteUserRestorationRow] = await connection.query(deleteUserRestorationQuery, restorationIdx);
+	return deleteUserRestorationRow;
+}
+
+// 시험 과목 다대다 수정
+// async function updateExamSubjectMultiple(connection, examIdx, examSubjectIdx, examSubjectNum) {
+//   const updateExamSubjectMultipleQuery = `
+//     UPDATE ExamSubjectMulti
+//     SET
+//     WHERE examIdx = ? AND examSubjectIdx = ?
+//     `  ;
+//   const updateExamSubjectMultipleRow = await connection.query(updateExamSubjectMultipleQuery, [examIdx, examSubjectIdx, examSubjectNum]);
+//   return updateExamSubjectMultipleRow;
+// }
+
+// 공기업 삭제
+async function deletePublicCompany(connection, examSortIdx) {
+	const deletePublicCompanyQuery = `
+    update ExamSort es
+	set status = 'Y'
+    WHERE es.examSortIdx = ?
+    `;
+	const deletePublicCompanyRow = await connection.query(deletePublicCompanyQuery, examSortIdx);
+	return deletePublicCompanyRow;
+}
+/**
+ * 기업 회사 삭제
+ * @changeFrom deletePublicCompany
+ */
+async function deleteCompany(connection, companyName, companyExamFieldIdx) {
+	const deleteCompanyQuery = `
+    update CompanyExam 
+	set status = 'Y'
+    WHERE  companyName = ? and examSortIdx_S = ?
+    `;
+	const deleteCompanyRow = await connection.query(deleteCompanyQuery, [companyName, companyExamFieldIdx]);
+	return deleteCompanyRow;
+}
+// 객관식 문제 삭제
+async function deleteMultipleProblem(connection, multipleProblemIdx) {
+	const deleteProblemQuery = `
+    update MultipleProblem mp
+	left join Question q on q.multipleProblemIdx = mp.multipleProblemIdx
+	set mp.status = 'Y', q.status= 'Y'
+    WHERE mp.multipleProblemIdx = ?
+    `;
+	const deleteProblemRow = await connection.query(deleteProblemQuery, multipleProblemIdx);
+	return deleteProblemRow;
+}
+
+// 주관식 문제 삭제
+async function deleteSubjectiveProblem(connection, subjectiveProblemIdx) {
+	const deleteSubjectiveProblemQuery = `
+    update SubjectiveProblem sp
+	set sp.status = 'Y'
+    WHERE sp.subjectiveProblemIdx = ?
+    `;
+	const deleteSubjectiveProblemRow = await connection.query(deleteSubjectiveProblemQuery, subjectiveProblemIdx);
+	return deleteSubjectiveProblemRow;
+}
+
+// 객관식 문제 문항 삭제
+async function deleteQuestion(connection, questionIdx) {
+	const deleteQuestionQuery = `
+    update Question
+	SET status = 'Y'
+    WHERE questionIdx = ? 
+    `;
+	const deleteQuestionRow = await connection.query(deleteQuestionQuery, questionIdx);
+	return deleteQuestionRow;
+}
+
+// 시험 삭제
+async function deleteExam(connection, examIdx) {
+	const deleteExamQuery = `
+    update Exam
+	set status = 'Y'
+    WHERE examIdx = ?
+    `;
+	const deleteExamRow = await connection.query(deleteExamQuery, examIdx);
+	return deleteExamRow;
+}
+
+// 기업 시험 삭제
+async function deleteCompanyExam(connection, examIdx) {
+	const deleteCompanyExamQuery = `
+	update CompanyExam
+	set status = 'Y'
+    WHERE examIdx = ?
+	`;
+	const deleteCompanyExamRow = await connection.query(deleteCompanyExamQuery, examIdx);
+	return deleteCompanyExamRow;
+}
+// 시험 상세 정보 삭제
+async function deleteExamDetail(connection, examDetailIdx) {
+	const deleteExamDetailQuery = `
+    update ExamDetail ed
+	set status = 'Y'
+    WHERE examDetailIdx = ?
+    `;
+	const deleteExamDetailRow = await connection.query(deleteExamDetailQuery, examDetailIdx);
+	return deleteExamDetailRow;
+}
+
+// 시험 상세 정보 삭제
+async function deleteProblemExamDetail(connection, examDetailIdx) {
+	const deleteProblemExamDetailQuery = `
+    delete from ProblemExam
+	where examDetailIdx = ?
+    `;
+	const deleteProblemExamDetailRow = await connection.query(deleteProblemExamDetailQuery, examDetailIdx);
+	return deleteProblemExamDetailRow;
+}
+
+/**
+ * @description 시험 회차 삭제 시 연결된 상품에서 시험 회차 삭제
+ * @param {*} connection
+ * @param {*} examDetailIdx
+ * @author neo
+ */
+async function deleteProductExamDetailRelation(connection, examDetailIdx) {
+	const deleteProductExamDetailRelationQuery = `
+		delete from ProductExamDetail
+		where examDetailIdx = ?;
+	`;
+	const deleteProductExamDetailRelationRow = await connection.query(
+		deleteProductExamDetailRelationQuery,
+		examDetailIdx,
+	);
+	return deleteProductExamDetailRelationRow;
+}
+
+// 특정 상품 회차 삭제
+async function deleteProductExamDetail(connection, productIdx) {
+	const deleteProductExamDetailQuery = `
+    delete from ProductExamDetail
+	where productIdx = ?;
+    `;
+	const deleteProductExamDetailRow = await connection.query(deleteProductExamDetailQuery, productIdx);
+	return deleteProductExamDetailRow;
+}
+
+// 특정 상품 url 삭제
+async function deleteProductUrl(connection, productIdx) {
+	const deleteProductUrlQuery = `
+    delete from ProductUrl
+	where productIdx = ?;
+    `;
+	const deleteProductUrlRow = await connection.query(deleteProductUrlQuery, productIdx);
+	return deleteProductUrlRow;
+}
+
+// 객관식 시험 상세 정보 삭제
+async function deleteProblemExam(connection, multipleProblemIdx) {
+	const deleteProblemExamQuery = `
+    delete from ProblemExam
+	where multipleProblemIdx = ?
+    `;
+	const deleteProblemExamRow = await connection.query(deleteProblemExamQuery, multipleProblemIdx);
+	return deleteProblemExamRow;
+}
+
+// 주관식 시험 상세 정보 삭제
+async function deleteSubjectiveProblemExam(connection, subjectiveProblemIdx) {
+	const deleteSubjectiveProblemExamQuery = `
+    delete from SubjectiveProblemExam
+	where subjectiveProblemIdx = ?
+    `;
+	const deleteSubjectiveProblemExamRow = await connection.query(deleteSubjectiveProblemExamQuery, subjectiveProblemIdx);
+	return deleteSubjectiveProblemExamRow;
+}
+
+// 객관식 시험 과목 상세 정보 삭제
+async function deleteProblemSubject(connection, multipleProblemIdx) {
+	const deleteProblemSubjectQuery = `
+    delete from ProblemSubject
+	where multipleProblemIdx = ?
+    `;
+	const deleteProblemSubjectRow = await connection.query(deleteProblemSubjectQuery, multipleProblemIdx);
+	return deleteProblemSubjectRow;
+}
+
+// 주관식 시험 과목 상세 정보 삭제
+async function deleteSubjectiveProblemSubject(connection, subjectiveProblemIdx) {
+	const deleteSubjectiveProblemSubjectQuery = `
+    delete from SubjectiveProblemSubject
+	where subjectiveProblemIdx = ?
+    `;
+	const deleteSubjectiveProblemSubjectRow = await connection.query(
+		deleteSubjectiveProblemSubjectQuery,
+		subjectiveProblemIdx,
+	);
+	return deleteSubjectiveProblemSubjectRow;
+}
+
+// 객관식 문제 유형 정보 삭제
+async function deleteProblemCategory(connection, multipleProblemIdx) {
+	const deleteProblemCategoryQuery = `
+    delete from ProblemCategory
+	where multipleProblemIdx = ?
+    `;
+	const ddeleteProblemCategoryRow = await connection.query(deleteProblemCategoryQuery, multipleProblemIdx);
+	return ddeleteProblemCategoryRow;
+}
+
+// 주관식 문제 유형 정보 삭제
+async function deleteSubjectiveProblemCategory(connection, subjectiveProblemIdx) {
+	const deleteSubjectiveProblemCategoryQuery = `
+    delete from SubjectiveProblemCategory
+	where subjectiveProblemIdx = ?
+    `;
+	const ddeleteSubjectiveProblemCategoryRow = await connection.query(
+		deleteSubjectiveProblemCategoryQuery,
+		subjectiveProblemIdx,
+	);
+	return ddeleteSubjectiveProblemCategoryRow;
+}
+
+// 시험 과목 삭제
+async function deleteExamSubject(connection, examSubjectIdx) {
+	const deleteExamSubjectQuery = `
+    update ExamSubject
+	set status = 'Y'
+    WHERE examSubjectIdx = ?
+    `;
+	const deleteExamSubjectRow = await connection.query(deleteExamSubjectQuery, examSubjectIdx);
+	return deleteExamSubjectRow;
+}
+
+// 시험 과목 다대다 삭제
+async function deleteExamSubjectMulti(connection, examIdx) {
+	const deleteExamSubjectMultiQuery = `
+    DELETE FROM ExamSubjectMulti
+    WHERE examIdx = ?
+    `;
+	const deleteExamSubjectMultiRow = await connection.query(deleteExamSubjectMultiQuery, examIdx);
+	return deleteExamSubjectMultiRow;
+}
+
+// 시험 종류 삭제
+async function deleteExamSort(connection, examSortIdx) {
+	const deleteExamSortQuery = `
+    UPDATE ExamSort 
+SET status = 'Y'
+WHERE examSortIdx in (
+with recursive CTE AS   (SELECT 
+			examSortIdx
+			FROM ExamSort 
+			WHERE examSortIdx = ?
+			UNION ALL
+			SELECT
+			a.examSortIdx
+			FROM ExamSort a
+			INNER JOIN CTE b ON a.examSortRef = b.examSortIdx 
+			)
+			SELECT examSortIdx FROM CTE)
+    `;
+	const deleteExamSortRow = await connection.query(deleteExamSortQuery, examSortIdx);
+	return deleteExamSortRow;
+}
+
+// 유저 삭제 회원탈퇴와 다르게 디비에서 바로삭제
+async function deleteUser(connection, userIdx) {
+	const deleteUserQuery = `
+    delete from User
+    WHERE userIdx = ?
+    `;
+	const deleteUserRow = await connection.query(deleteUserQuery, userIdx);
+	return deleteUserRow;
+}
+
+// 문제 검색 삭제
+async function deleteRestorationSearch(connection, multipleProblemIdx) {
+	const deleteRestorationSearchQuery = `
+    delete from RestorationSearch
+    WHERE multipleProblemIdx = ?
+    `;
+	const deleteRestorationSearchRow = await connection.query(deleteRestorationSearchQuery, multipleProblemIdx);
+	return deleteRestorationSearchRow;
+}
+
+// 카테고리 삭제
+async function deleteCategory(connection, categoryIdx) {
+	const deleteCategoryQuery = `
+    UPDATE Category 
+SET status = 'Y'
+WHERE categoryIdx in (
+with recursive CTE AS   (SELECT 
+			categoryIdx, categoryName, status, categoryRef
+			FROM Category 
+			WHERE categoryIdx = ?
+			UNION ALL
+			SELECT
+			a.categoryIdx, a.categoryName,a.status,a.categoryRef
+			FROM Category a
+			INNER JOIN CTE b ON a.categoryRef = b.categoryIdx 
+			)
+			SELECT categoryIdx FROM CTE)
+    `;
+	const deleteCategoryRow = await connection.query(deleteCategoryQuery, categoryIdx);
+	return deleteCategoryRow;
+}
+
+// 상품 삭제 - 김기창
+async function deleteProduct(connection, productIdx) {
+	/** 
+	const deleteProductExamDetailQuery = `
+		DELETE from ProductExamDetail where productIdx = ? 
+	`;
+	ProdectExamDetail은 건들 필요없다. 
+	*/
+	const deleteProductQuery = `
+		UPDATE Product SET status='Y' where productIdx = ? 
+	`;
+	//status값만 수정해주면 된다.
+	const deleteProductRow = await connection.query(deleteProductQuery, productIdx);
+	return deleteProductRow;
+}
+
+// 상위 카테고리로 삭제
+async function deleteCategoryByRef(connection, categoryIdx) {
+	const deleteCategoryByRefQuery = `
+    update Category
+	set status = 'Y'
+    WHERE categoryRef = ?
+    `;
+	const deleteCategoryByRefRow = await connection.query(deleteCategoryByRefQuery, categoryIdx);
+	return deleteCategoryByRefRow;
+}
+
+// 시험 생성 -> 생성한 시험에 대한 prio는 MAX 값 지정 (100)
+async function insertExam(connection, insertExamParams) {
+	const insertExamQuery = `
+    INSERT INTO Exam 
+	(examName, passScore, examSortIdx, timeLimit, problemCount, examUrl, subjectiveLabel)
+	VALUES
+	(?,?,?,?,?,?,?)
+	;`;
+	const insertExamRow = await connection.query(insertExamQuery, insertExamParams);
+	return insertExamRow;
+}
+
+// 기업 생성
+async function insertCompany(connection, companyExamFieldIdx, companyName, companyThumbnail) {
+	const insertCompanyQuery = `
+    INSERT INTO CompanyExam (examSortIdx_S, companyName,companyUrl)
+    VALUES (?, ?, ?)`;
+	const insertCompanyRow = await connection.query(insertCompanyQuery, [
+		companyExamFieldIdx,
+		companyName,
+		companyThumbnail,
+	]);
+	return insertCompanyRow;
+}
+
+// 기업 시험 종류 소분류 생성
+async function postCompanyExamField(connection, companyExamTypeIdx, companyExamFieldName) {
+	const postCompanyExamFieldQuery = `
+		Insert INTO ExamSort (examSortRef, ExamSortName, examSortType) 
+		VALUES (?, ?, "S")`;
+
+	const postCompanyExamFieldRow = await connection.query(postCompanyExamFieldQuery, [
+		companyExamTypeIdx,
+		companyExamFieldName,
+	]);
+	return postCompanyExamFieldRow;
+}
+// 기업 시험 생성
+async function insertCompanyExam(connection, insertPublicCompanyExamParams) {
+	const insertPublicCompanyExamParamsQuery = `
+    INSERT INTO Exam ( examName, timeLimit, problemCount, questionType,isBody )
+    VALUES (?, ?, ?, ?, ?)`;
+	const insertPublicCompanyExamParamsRow = await connection.query(
+		insertPublicCompanyExamParamsQuery,
+		insertPublicCompanyExamParams,
+	);
+	return insertPublicCompanyExamParamsRow;
+}
+
+// 기업 시험 중에서 examSortIdx_S와 companyName (슈퍼 키 개념)에 대한 정보 가져오기
+async function getCompanyExamInfo(connection, companyExamFieldIdx, companyName) {
+	const getCompanyExamInfoQuery = `
+	select * from CompanyExam 
+	where examSortIdx_S = ? and companyName = ? and status ="N" 
+`;
+
+	const [getCompanyExamInfoRow] = await connection.query(getCompanyExamInfoQuery, [companyExamFieldIdx, companyName]);
+	return getCompanyExamInfoRow;
+}
+
+// 기업 시험에 대한 examIdx 가져오기 ( category까지 비교하여 단 한개의 examIdx만 가져올 수 있도록 한다. )
+async function getCompanyExamIdx(connection, companyExamFieldIdx, companyName, domain, education, category) {
+	const getCompanyExamIdxQuery = `
+	select examIdx from CompanyExam 
+	where examSortIdx_S = ?
+	AND companyName = ? 
+	AND (domain = ? OR domain IS NULL)
+	AND (education = ? OR education IS NULL)
+	AND (category = ? OR category IS NULL)
+	AND status = "N"
+`;
+	const [getCompanyExamIdxRow] = await connection.query(getCompanyExamIdxQuery, [
+		companyExamFieldIdx,
+		companyName,
+		domain,
+		education,
+		category,
+	]);
+	return getCompanyExamIdxRow;
+}
+
+// 공기업 시험(직력, 전형) 생성
+async function insertCompanyExamInfo(connection, insertCompanyExamParams) {
+	// companyExamFieldIdx, companyName, examIdx, education, category, domain, companyUrl
+	const insertCompanyExamQuery = `
+    INSERT INTO CompanyExam ( examSortIdx_S, companyName, examIdx, education, category, domain ,companyUrl)
+    VALUES (?, ?, ?, ?, ?, ?, ?)`;
+	const insertCompanyExamRow = await connection.query(insertCompanyExamQuery, insertCompanyExamParams);
+	return insertCompanyExamRow;
+}
+
+// 상품 생성
+async function insertProduct(connection, insertProductParams) {
+	const insertProductQuery = `
+    INSERT INTO Product (productName, productThumbnail, price, discountPrice, shortDescription, generalDescription, detailDescription, duration, depth)
+    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`;
+	const insertProductRow = await connection.query(insertProductQuery, insertProductParams);
+	return insertProductRow;
+}
+
+// 상품 회차 생성   비동기 병렬처리 or bulk insert 고민
+async function insertProductExamDetail(connection, insertProductExamDetailParams) {
+	const insertProductExamDetailQuery = `
+    INSERT INTO ProductExamDetail (productIdx, examDetailIdx)
+    VALUES ?`;
+	const insertProductExamDetailRow = await connection.query(insertProductExamDetailQuery, [
+		insertProductExamDetailParams,
+	]);
+	return insertProductExamDetailRow;
+}
+
+// 상품 url 생성
+async function insertProductUrl(connection, insertProductUrlParams) {
+	const insertProductUrlQuery = `
+    INSERT INTO ProductUrl (productIdx, urlHeader, urlOrder, productUrl, productUrlSort)
+    VALUES ?`;
+	const insertProductUrlRow = await connection.query(insertProductUrlQuery, [insertProductUrlParams]);
+	return insertProductUrlRow;
+}
+
+// 상품 관계 생성
+async function insertProductRelation(connection, insertProductRelationParams) {
+	const insertProductRelationQuery = `
+    INSERT INTO ProductRelation VALUES (?, ?, ?);
+	`;
+	const insertProductRelationRow = await connection.query(insertProductRelationQuery, insertProductRelationParams);
+	return insertProductRelationRow;
+}
+
+// 기업 시험의 questionType, timeLimit, problemCount 수정
+async function updateCompanyExamInfo(connection, updateCompanyExamInfoParams) {
+	const updateCompanyExamInfoQuery = `
+    UPDATE Exam SET questionType=?, timeLimit=?, problemCount=?
+    WHERE examIdx = ? 
+	`;
+	const updateCompanyExamInfoRow = await connection.query(updateCompanyExamInfoQuery, updateCompanyExamInfoParams);
+	return updateCompanyExamInfoRow;
+}
+
+/**
+ *  기업 시험 수정
+ * examIdx가 있다면 시험 생성 시의 로직 없다면 수정 시의 로직
+ * 1. 생성 시 필요한 로직
+ * 2. 수정 시 필요한 로직이 다르다.
+ * */
+async function updateCompanyExam(connection, updateCompanyExamParams) {
+	const { companyExamFieldIdx, companyName, examIdx, education, category, domain } = updateCompanyExamParams;
+
+	const updateCompanyExamQuery = `
+    UPDATE CompanyExam SET
+        ${examIdx ? "examIdx = ?," : ""}
+        education = ?,
+        category = ?,
+        domain = ?
+    WHERE examSortIdx_S = ? AND companyName = ? AND status = 'N'
+`;
+	const params = [...(examIdx ? [examIdx] : []), education, category, domain, companyExamFieldIdx, companyName];
+
+	const updateCompanyExamRow = await connection.query(updateCompanyExamQuery, params);
+	return updateCompanyExamRow;
+}
+
+// 상품 수정
+async function updateProduct(connection, updateProductParams) {
+	const updateProductQuery = `
+    UPDATE Product SET productName =?, productThumbnail =?, price =?, discountPrice =?, shortDescription =?, generalDescription =?
+	, detailDescription =?, duration =?, depth =?
+	WHERE productIdx = ?
+	`;
+	const updateProductRow = await connection.query(updateProductQuery, updateProductParams);
+	return updateProductRow;
+}
+
+// 상품 Url 수정
+async function updateProductUrl(connection, updateProductUrlParams) {
+	const updateProductUrlQuery = `
+    UPDATE ProductUrl SET urlHeader = ?, urlOrder = ?, productUrl = ?, productUrlSort = ?
+	WHERE productUrlIdx = ?
+	`;
+	const updateProductUrlRow = await connection.query(updateProductUrlQuery, updateProductUrlParams);
+	return updateProductUrlRow;
+}
+
+// 시험 과목 생성
+async function insertExamSubject(connection, insertExamSubjectParams) {
+	const insertExamSubjectQuery = `
+    INSERT INTO ExamSubject (examSubjectName, passScore)
+    VALUES (?, ?)`;
+	const insertExamSubjectRow = await connection.query(insertExamSubjectQuery, insertExamSubjectParams);
+	return insertExamSubjectRow;
+}
+
+// 시험 과목 다대다 생성
+async function insertExamSubjectMultiple(connection, examIdx, examSubjectIdx, examSubjectNum) {
+	const insertExamSubjectMultipleQuery = `
+    INSERT INTO ExamSubjectMulti (examIdx, examSubjectIdx, examSubjectNum)
+    VALUES (?, ?, ?)`;
+	const insertExamSubjectMultipleRow = await connection.query(insertExamSubjectMultipleQuery, [
+		examIdx,
+		examSubjectIdx,
+		examSubjectNum,
+	]);
+	return insertExamSubjectMultipleRow;
+}
+
+// 시험 디테일 생성 - 주관식 여부도 포함.
+async function insertExamDetail(connection, insertExamDetailParams) {
+	const insertExamDetailQuery = `
+  INSERT INTO ExamDetail (examIdx, examDate, examRound, publicLevel)
+  VALUES (?, ?, ?, ?)`;
+	const insertExamDetailRow = await connection.query(insertExamDetailQuery, insertExamDetailParams);
+	return insertExamDetailRow;
+}
+
+// 객관식 문제 삽입
+async function insertMultipleProblem(connection, insertProblemParams) {
+	const insertMultipleProblemQuery = `insert into MultipleProblem
+	(problem, provisionNum, problemImage, solution, isKatex, problemScore, problemUrl, lectureUrl, isDelete, examHistory)
+	values (?,?,?,?,?,?,?,?,?,?)`;
+	const insertMultipleProblemRow = await connection.query(insertMultipleProblemQuery, insertProblemParams);
+	return insertMultipleProblemRow;
+}
+
+// 주관식 문제 삽입
+async function insertSubjectiveProblem(connection, insertProblemParams) {
+	const insertSubjectiveProblemQuery = `
+	insert into SubjectiveProblem
+	(problem, problemImage, solution,solutionImage, isKatex, problemScore, problemUrl, lectureUrl, isDelete,examHistory)
+	values (?,?,?,?,?,?,?,?,?,?)
+	`;
+	const insertSubjectiveProblemRow = await connection.query(insertSubjectiveProblemQuery, insertProblemParams);
+	return insertSubjectiveProblemRow;
+}
+
+// 객관식 문제 문항 삽입
+async function insertQuestion(connection, insertQuestionParams) {
+	const insertQuestionQuery = `
+    INSERT INTO Question 
+	(questionNum, question, questionImage, multipleProblemIdx, isAnswer)
+    VALUES (?,?,?,?,?) `;
+	const insertQuestionRow = await connection.query(insertQuestionQuery, insertQuestionParams);
+	return insertQuestionRow;
+}
+
+async function insertProblemQuestion(connection, questionInsertInfo, questionValues, insertQuestionParams) {
+	const insertProblemQuestionQuery =
+		`
+  INSERT INTO Question ` +
+		questionInsertInfo +
+		`
+   VALUES ` +
+		questionValues;
+	const insertProblemQuestionRow = await connection.query(insertProblemQuestionQuery, insertQuestionParams);
+	return insertProblemQuestionRow;
+}
+
+// 시험 종류 삽입
+async function insertExamSort(connection, examSort, examSortRef, examSortType) {
+	const insertExamSortQuery = `
+  INSERT INTO ExamSort (examSortName, examSortRef, examSortType)
+  VALUES (?, ?, ?)`;
+	const insertExamSortRow = await connection.query(insertExamSortQuery, [examSort, examSortRef, examSortType]);
+	return insertExamSortRow;
+}
+
+// 복원 회차 삽입
+async function insertRestoreExamDetail(connection, insertRestoreExamDetailParams) {
+	const insertRestoreExamDetailQuery = `
+     INSERT INTO RestoreExamDetail (examIdx, restoreExamDate, restoreExamRound, thumbnail)
+     VALUES (?, ?, ?, ?)
+     `;
+	const [insertRestoreExamDetailRow] = await connection.query(
+		insertRestoreExamDetailQuery,
+		insertRestoreExamDetailParams,
+	);
+	return insertRestoreExamDetailRow;
+}
+
+// 문제 검색 삽입
+async function insertRestorationSearch(connection, multipleProblemIdx, content) {
+	const insertRestorationSearchQuery = `
+  INSERT INTO RestorationSearch (multipleProblemIdx, content)
+  VALUES (?, ?)`;
+	const insertRestorationSearchRow = await connection.query(insertRestorationSearchQuery, [
+		multipleProblemIdx,
+		content,
+	]);
+	return insertRestorationSearchRow;
+}
+
+// 객관식 문제 과목 맵핑
+async function insertProblemSubject(connection, multipleProblemIdx, examSubjectIdx) {
+	const insertProblemSubjectQuery = `
+  INSERT INTO ProblemSubject (multipleProblemIdx, examSubjectIdx)
+  VALUES (?, ?)`;
+	const insertProblemSubjectRow = await connection.query(insertProblemSubjectQuery, [
+		multipleProblemIdx,
+		examSubjectIdx,
+	]);
+	return insertProblemSubjectRow;
+}
+// 주관식 문제 과목 맵핑
+async function insertSubjectiveProblemSubject(connection, subjectiveProblemIdx, examSubjectIdx) {
+	const insertSubjectiveProblemSubjectQuery = `
+  INSERT INTO SubjectiveProblemSubject (subjectiveProblemIdx, examSubjectIdx)
+  VALUES (?, ?)`;
+	const insertSubjectiveProblemSubjectRow = await connection.query(insertSubjectiveProblemSubjectQuery, [
+		subjectiveProblemIdx,
+		examSubjectIdx,
+	]);
+	return insertSubjectiveProblemSubjectRow;
+}
+
+// 객관식 문제 유형 맵핑
+async function insertProblemCategory(connection, multipleProblemIdx, categoryIdx) {
+	const insertProblemCategoryQuery = `
+  INSERT INTO ProblemCategory (multipleProblemIdx, categoryIdx)
+  VALUES (?, ?)`;
+	const insertProblemCategoryRow = await connection.query(insertProblemCategoryQuery, [
+		multipleProblemIdx,
+		categoryIdx,
+	]);
+	return insertProblemCategoryRow;
+}
+
+// 주관식 문제 유형 맵핑
+async function insertSubjectiveProblemCategory(connection, subjectiveProblemIdx, categoryIdx) {
+	const insertProblemCategoryQuery = `
+  INSERT INTO SubjectiveProblemCategory (subjectiveProblemIdx, categoryIdx)
+  VALUES (?, ?)`;
+	const insertSubjectiveProblemCategoryRow = await connection.query(insertProblemCategoryQuery, [
+		subjectiveProblemIdx,
+		categoryIdx,
+	]);
+	return insertSubjectiveProblemCategoryRow;
+}
+
+async function deleteWorkMultipleProblem(connection, deleteIdxString, deleteIdxSet) {
+	//2,3,4,5 하드코딩 어떻게 할지.
+	const deleteWorkMultipleProblemQuery =
+		`
+	DELETE FROM MultipleProblem WHERE multipleProblemIdx in ` +
+		deleteIdxString +
+		`
+	`;
+	const deleteWorkMultipleProblemRow = await connection.query(deleteWorkMultipleProblemQuery, deleteIdxSet);
+	return deleteWorkMultipleProblemRow;
+}
+
+async function insertWorkMultipleProblem(connection, insertWorkEngineerParams) {
+	const insertWorkMultipleProblemQuery = `
+	INSERT into MultipleProblem(problemNum, problem, answerNum, provisionNum, categoryIdx, status, problemImage, solution, examSubjectIdx, examDetailIdx, isKatex, isDelete, problemScore, problemUrl, lectureUrl)
+	VALUES ?
+	`;
+	const insertWorkMultipleProblemRow = await connection.query(insertWorkMultipleProblemQuery, insertWorkEngineerParams);
+	return insertWorkMultipleProblemRow[0];
+}
+
+async function updateWorkMultipleProblem(connection, insertWorkEngineerParams) {
+	const updateWorkMultipleProblemQuery = `
+	INSERT INTO MultipleProblem(multipleProblemIdx, problemNum, problem,answerNum, provisionNum,categoryIdx, status, problemImage, solution, examSubjectIdx, isKatex, isDelete, problemScore, problemUrl, lectureUrl)
+	VALUES ? ON DUPLICATE KEY UPDATE
+	problemNum=VALUES(problemNum)
+	, problem=VALUES(problem)
+	, answerNum=VALUES(answerNum)
+	, provisionNum=VALUES(provisionNum)
+	, categoryIdx=VALUES(categoryIdx)
+	, status=VALUES(status)
+	, problemImage=VALUES(problemImage)
+	, solution=VALUES(solution)
+	, examSubjectIdx=VALUES(examSubjectIdx)
+	, isKatex=VALUES(isKatex)
+	, isDelete=VALUES(isDelete)
+	, problemScore=VALUES(problemScore)
+	, problemUrl=VALUES(problemUrl)
+	, lectureUrl=VALUES(lectureUrl)
+	`;
+	const updateWorkMultipleProblemRow = await connection.query(updateWorkMultipleProblemQuery, insertWorkEngineerParams);
+	return updateWorkMultipleProblemRow[0];
+}
+// 카테고리 삽입
+async function insertCategory(connection, categoryName, categoryRef, categoryType) {
+	const insertCategoryQuery = `
+  INSERT INTO Category (categoryName, categoryRef, categoryType)
+  VALUES (?, ?, ?)`;
+	const insertCategoryRow = await connection.query(insertCategoryQuery, [categoryName, categoryRef, categoryType]);
+	return insertCategoryRow;
+}
+
+// 시험 종류 이미지 삽입
+async function insertExamSortImage(connection, examSortIdx, examSortUrl) {
+	const insertExamSortImageQuery = `
+  INSERT INTO ExamSortImage (examSortIdx, examSortUrl)
+  VALUES (?, ?)`;
+	const insertExamSortImageRow = await connection.query(insertExamSortImageQuery, [examSortIdx, examSortUrl]);
+	return insertExamSortImageRow;
+}
+
+// 시험 종류 이미지 수정
+async function upsertExamSortImage(connection, examSortIdx, examSortUrl) {
+	const upsertExamSortImageQuery = `
+	INSERT INTO ExamSortImage (examSortIdx, examSortUrl)
+  VALUES (?, ?) ON DUPLICATE KEY UPDATE examSortUrl = ?
+  `;
+	const upsertExamSortImageRow = await connection.query(upsertExamSortImageQuery, [
+		examSortIdx,
+		examSortUrl,
+		examSortUrl,
+	]);
+	return upsertExamSortImageRow;
+}
+
+// 자격증 시험 이미지 수정
+async function updateExamThumbnail(connection, examIdx, examSortUrl) {
+	const upsertExamThumbnailQuery = `
+	update ExamSortImage esi 
+	set esi.examSortUrl = ?
+	where esi.examSortIdx = ( 
+		select es.examSortIdx from ExamSort es 
+		left join Exam e on e.examSortIdx = es.examSortIdx
+		where examIdx = ?
+	)`;
+	const upsertExamThumbnailRow = await connection.query(upsertExamThumbnailQuery, [examSortUrl, examIdx]);
+	return upsertExamThumbnailRow;
+}
+
+// 시험과 문제 인덱스 맵핑 삽입
+async function insertProblemExam(connection, examDetailIdx, multipleProblemIdx, problemNum) {
+	const insertProblemExamQuery = `
+  INSERT INTO ProblemExam (examDetailIdx, multipleProblemIdx, problemNum)
+  VALUES (?, ?,?)`;
+	const insertProblemExamRow = await connection.query(insertProblemExamQuery, [
+		examDetailIdx,
+		multipleProblemIdx,
+		problemNum,
+	]);
+	return insertProblemExamRow;
+}
+
+// 주관시 시험 문제 인덱스 맵핑 삽입
+async function insertSubjectiveProblemExam(connection, examDetailIdx, subjectiveProblemIdx, problemNum) {
+	const insertSubjectiveProblemExamQuery = `
+  INSERT INTO SubjectiveProblemExam (examDetailIdx, subjectiveProblemIdx, problemNum)
+  VALUES (?, ?, ?)`;
+	const insertSubjectiveProblemExamRow = await connection.query(insertSubjectiveProblemExamQuery, [
+		examDetailIdx,
+		subjectiveProblemIdx,
+		problemNum,
+	]);
+	return insertSubjectiveProblemExamRow;
+}
+
+// 관리자 포인트 생성
+async function insertPoint(connection, pointName, point) {
+	const insertPointQuery = `
+  INSERT INTO Point (pointName, point)
+  VALUES (?, ?)`;
+	const insertPointRow = await connection.query(insertPointQuery, [pointName, point]);
+	//console.log(insertPointRow, ":결과값.");
+
+	return insertPointRow;
+}
+
+// 관리자 포인트 목록 조회
+async function selectPointList(connection) {
+	const selectPointListQuery = `
+	SELECT *
+	FROM Point
+	WHERE status = 'N';
+	`;
+	const selectPointListRow = await connection.query(selectPointListQuery);
+	return selectPointListRow[0];
+}
+
+// 관리자 특정 포인트 조회
+async function selectPoint(connection, pointIdx) {
+	const selectPointQuery = `
+	SELECT *
+	FROM Point
+	WHERE pointIdx = ? and status = 'N';
+	`;
+	const [selectPointRow] = await connection.query(selectPointQuery, pointIdx);
+	return selectPointRow;
+}
+
+// 관리자 포인트 로그 조회
+async function selectAdminPointLog(connection) {
+	const selectAdminPointLogQuery = `
+    SELECT pointName, p.point, u.userName, u.userPhoneNum, au.userName as adminName, reason, up.createdAt 
+	FROM UserPoint up
+    LEFT JOIN Point p ON p.pointIdx = up.pointIdx
+    LEFT JOIN User u ON u.userIdx = up.userIdx
+    LEFT JOIN User au ON au.userIdx = up.adminIdx
+    WHERE up.status = 'N' AND u.status = 'N' AND au.status IN ('M', 'K') AND p.pointIdx NOT IN (1, 2, 3)
+	ORDER BY createdAt DESC;
+	`;
+	const [selectAdminPointLogRow] = await connection.query(selectAdminPointLogQuery);
+	return selectAdminPointLogRow;
+}
+
+// 관리자 유저 포인트 부여
+async function insertUserPoint(connection, insertUserPointParams) {
+	const insertUserPointQuery = `
+
+  INSERT INTO UserPoint (userIdx, pointIdx, adminIdx, reason)
+  VALUES ?`;
+	const insertUserPointRow = await connection.query(insertUserPointQuery, [insertUserPointParams]);
+	return insertUserPointRow;
+}
+
+//  유저 포인트 로그 생성
+async function insertUserPointLog(connection, insertUserPointLog) {
+	const insertUserPointLogQuery = `
+  INSERT INTO UserPointLog (userIdx, sort, content, point)
+  VALUES ?`;
+	const insertUserPointLogRow = await connection.query(insertUserPointLogQuery, [insertUserPointLog]);
+	return insertUserPointLogRow;
+}
+
+//  유저 포인트 로그 생성
+async function updatePoint(connection, pointName, point, pointIdx) {
+	const updatePointQuery = `
+	update Point set pointName = ?, point = ?
+	where pointIdx = ?`;
+	const updatePointRow = await connection.query(updatePointQuery, [pointName, point, pointIdx]);
+	return updatePointRow;
+}
+
+// 관리자 유저 포인트 부여
+async function updateUsersPoint(connection, point, userIdx) {
+	const updateUsersPointQuery = `
+  update User set point = point + ?
+  where userIdx = ?`;
+	const updateUsersPointRow = await connection.query(updateUsersPointQuery, [point, userIdx]);
+	return updateUsersPointRow;
+}
+
+// 상품 카테고리 조회
+async function selectStoreCategory(connection) {
+	const selectStoreCategoryQuery = `
+	SELECT p.productIdx, productName, depth, pr.parentIdx as productRef , pr.productTypeIdx
+	FROM Product as p
+	JOIN ProductRelation as pr on pr.productIdx = p.productIdx
+	WHERE status = 'N';
+    `;
+	const selectStoreCategoryRow = await connection.query(selectStoreCategoryQuery);
+	return selectStoreCategoryRow;
+}
+
+// 중분류 전부 조회
+async function selectStoreExamField(connection) {
+	const selectStoreCategoryQuery = `
+	SELECT * from ProductType
+	where productTypeIdx >= 1
+    `;
+	const selectStoreCategoryRow = await connection.query(selectStoreCategoryQuery);
+	return selectStoreCategoryRow[0];
+}
+// 상품 전체 조회
+async function selectAllProductInfo(connection) {
+	const selectAllProductInfoQuery = `
+	SELECT p.productIdx, productName, parentIdx as productRef, depth, productThumbnail, price, discountPrice, shortDescription, generalDescription, detailDescription, duration 
+	FROM Product as p
+	JOIN ProductRelation as pr on pr.productIdx = p.productIdx
+	WHERE status = 'N' AND depth != 0;
+    `;
+	const selectAllProductInfoRow = await connection.query(selectAllProductInfoQuery);
+	return selectAllProductInfoRow;
+}
+
+// 상품에 연동된 회차 조회
+async function selectProductExamDetail(connection, productIdx) {
+	const selectProductExamDetailQuery = `
+	SELECT examDetailIdx
+	FROM Product as p
+	JOIN ProductExamDetail as ped on p.productIdx = ped.productIdx
+	WHERE p.productIdx = ? AND status = 'N';
+    `;
+	const selectProductExamDetailRow = await connection.query(selectProductExamDetailQuery, productIdx);
+	return selectProductExamDetailRow;
+}
+
+// 상품에 연동된 상세페이지 url 조회
+async function selectProductUrl(connection, productIdx) {
+	const selectProductUrlQuery = `
+	SELECT urlHeader, urlOrder, productUrl, productUrlSort
+	FROM Product as p
+	JOIN ProductUrl as pu on p.productIdx = pu.productIdx
+	WHERE p.productIdx = ? AND p.status = 'N' AND pu.status = 'N'
+    `;
+	const selectProductUrlRow = await connection.query(selectProductUrlQuery, productIdx);
+	return selectProductUrlRow;
+}
+
+// 판매 내역 조회
+async function selectPaymentList(connection) {
+	const selectPaymentListQuery = `
+	SELECT paymentIdx, paymentName, p.userIdx, userEmail, userName, userPhoneNum, paidAt, pay_method, totalPrice, totalPoint, level, receipt_url, p.status
+  FROM Payment p
+  JOIN User u ON u.userIdx = p.userIdx 
+  WHERE level IN ("paid", "cancelled","pointPaid") AND p.status = 'N'
+  ORDER BY p.createdAt desc 
+  limit 200;
+    `;
+	const selectPaymentListRow = await connection.query(selectPaymentListQuery);
+	return selectPaymentListRow;
+}
+
+// 유저 권한 해제
+async function deleteUserAuth(connection, deleteUserAuthParams) {
+	const deleteUserAuthQuery = `
+	UPDATE UserAuth SET status = 'Y', expireAt = now() WHERE userIdx = ? AND productIdx = ?
+    `;
+	const deleteUserAuthRow = await connection.query(deleteUserAuthQuery, deleteUserAuthParams);
+	return deleteUserAuthRow;
+}
+
+//유저 상품 권한 및 만료기한 확인
+async function selectUserProductAuth(connection, UserAuthParams) {
+	const selectUserProductAuthQuery = `
+	SELECT expireAt, ua.status
+	FROM UserAuth ua
+	LEFT JOIN Product p on ua.productIdx = p.productIdx
+	where userIdx = ? and ua.productIdx = ?
+    `;
+	const selectUserProductAuthRow = await connection.query(selectUserProductAuthQuery, UserAuthParams);
+	return selectUserProductAuthRow[0];
+}
+
+// 문제 빈도수 업데이트 하기
+async function updateFrequency(connection, problemIdx, subjectiveLabel, history) {
+	const updateFrequencyParams = [problemIdx, subjectiveLabel, history, history];
+	const updateFrequencyQuery = `
+	INSERT INTO ProblemFrequency (problemIdx, subjectiveLabel, history)
+	VALUES (? ,?, ?)  ON DUPLICATE KEY UPDATE history = ?;
+	  `;
+	const [updateFrequencyRow] = await connection.query(updateFrequencyQuery, updateFrequencyParams);
+	return updateFrequencyRow;
+}
+
+// 문제 빈도수 업데이트 하기
+async function updateFrequency(connection, problemIdx, subjectiveLabel, history) {
+	const updateFrequencyParams = [problemIdx, subjectiveLabel, history, history];
+	const updateFrequencyQuery = `
+	INSERT INTO ProblemFrequency (problemIdx, subjectiveLabel, history)
+	VALUES (? ,?, ?)  ON DUPLICATE KEY UPDATE history = ?;
+	  `;
+	const [updateFrequencyRow] = await connection.query(updateFrequencyQuery, updateFrequencyParams);
+	return updateFrequencyRow;
+}
+
+// 자격증 분류 순서 수정
+async function updateExamOrder(connection, prio, examIdx) {
+	const updateExamOrderQuery = `
+	UPDATE Exam e
+	SET e.prio = ?
+	WHERE e.examIdx = ?;
+	`;
+	const updateExamOrderRow = await connection.query(updateExamOrderQuery, [prio, examIdx]);
+	return updateExamOrderRow;
+}
+
+// 공기업 분류 순서 수정
+async function updateCompanyOrder(connection, prio, companyIdx) {
+	const updateExamOrderQuery = `
+	UPDATE CompanyInfo ci
+	SET ci.prio = ?
+	WHERE ci.examSortIdx = ?
+	`;
+	const updateExamOrderRow = await connection.query(updateExamOrderQuery, [prio, companyIdx]);
+	return updateExamOrderRow;
+}
+
+// 상품 분류 순서 수정
+async function updateProductOrder(connection, prio, examIdx) {
+	const updateExamOrderQuery = `
+	UPDATE Product p
+	SET p.prio = ?
+	WHERE p.productIdx = ?;
+	`;
+	const updateExamOrderRow = await connection.query(updateExamOrderQuery, [prio, examIdx]);
+	return updateExamOrderRow;
+}
+
+// 자격증 인덱스 체크
+async function checkExam(connection, examIdx, examName) {
+	const checkExamQuery = `select exists (
+		select examIdx from Exam where examIdx= ? and examName = ? and status='N') AS exist;`;
+	const [checkExamRow] = await connection.query(checkExamQuery, [examIdx, examName]);
+	return checkExamRow;
+}
+
+// 공기업 인덱스 체크
+async function checkCompany(connection, companyIdx, companyName) {
+	const checkCompanyQuery = `select exists (
+		select examSortIdx from CompanyInfo where examSortIdx= ? and companyName = ? and status='N') AS exist;`;
+	const [checkCompanyRow] = await connection.query(checkCompanyQuery, [companyIdx, companyName]);
+	return checkCompanyRow;
+}
+
+// 상품 인덱스 체크
+async function checkProduct(connection, productIdx, productName) {
+	const checkProductQuery = `select exists (
+		select productIdx from Product where productIdx= ? and productName = ? and status='N') AS exist;`;
+	const [checkProductRow] = await connection.query(checkProductQuery, [productIdx, productName]);
+	return checkProductRow;
+}
+
+async function searchSellList(connection, searchType, searchValue) {
+	const searchSellListQuery = `
+	SELECT p.paymentIdx, p.paymentName, u.userName ,u.userEmail,u.userPhoneNum,p.paidAt,p.pay_method,p.totalPrice
+	FROM Payment p
+	JOIN User u ON u.userIdx = p.userIdx
+	WHERE level IN ("paid", "cancelled","pointPaid") AND p.status = 'N' AND ${searchType} LIKE '%${searchValue}%'
+	ORDER BY p.createdAt desc;
+	`;
+
+	const [searchSellListRow] = await connection.query(searchSellListQuery);
+	return searchSellListRow;
+}
+
+async function userPurchaseDetail(connection, paymentIdx) {
+	const selectPurchaseDetailQuery = `
+	SELECT paymentName, pt.productName, pt.discountPrice, pp.paymentProductIdx,  pt.productIdx, pp.isRefund,p.userIdx,  userName, refundPrice,  pay_method, totalPrice, totalPoint, level, p.receipt_url
+  FROM Payment p
+  JOIN User u ON u.userIdx = p.userIdx 
+	JOIN PaymentProduct pp ON pp.paymentIDx = p.paymentIdx
+	JOIN Product pt On pt.productIdx = pp.productIdx
+  WHERE level IN ("paid", "cancelled","pointPaid") AND p.status = 'N' and pp.paymentIdx = ?
+  ORDER BY p.createdAt desc 
+	`;
+	const [selectPurchaseDetailRow] = await connection.query(selectPurchaseDetailQuery, paymentIdx);
+	return selectPurchaseDetailRow;
+}
+
+//
+async function selectProductInfo(connection, productIdx) {
+	const checkParentProductIdxQuery = `
+	SELECT p.duration, pr.parentIdx, pr.productTypeIdx
+	FROM ProductRelation pr
+	LEFT JOIN Product p on p.productIdx = pr.productIdx
+	where p.productIdx = ?
+	`;
+	const [checkParentProductRow] = await connection.query(checkParentProductIdxQuery, productIdx);
+	return checkParentProductRow;
+}
+
+//TODO: 함수명 변경하기
+async function restoreUserAuth(connection, deleteUserAuthParams) {
+	const insertUserAuthQuery = `
+	UPDATE UserAuth set status='N', expireAt = ? where userIdx = ? and productIdx = ?
+	`;
+	const [insertUserAuthRow] = await connection.query(insertUserAuthQuery, deleteUserAuthParams);
+	return insertUserAuthRow;
+}
+
+async function updatePaymentProductInfo(connection, paymentProductIdx) {
+	const updatePaymentProductInfoQuery = `
+	UPDATE PaymentProduct set isRefund = ? where paymentProductIdx = ?
+	`;
+	const [updatePaymentProductInfoRow] = await connection.query(updatePaymentProductInfoQuery, paymentProductIdx);
+	return updatePaymentProductInfoRow;
+}
+
+async function suspendUser(connection, userIdx) {
+	const updateUserStatusQuery = `
+	UPDATE User set status = 'F' where userIdx = ?
+	`;
+	const [updateUserStatusRow] = await connection.query(updateUserStatusQuery, userIdx);
+	return updateUserStatusRow;
+}
+
+async function selectAuthProductList(connection) {
+	const selectAuthProductListQuery = `
+	SELECT p.productIdx, productName, pr.productTypeIdx from Product p
+	LEFT JOIN ProductRelation pr on p.productIdx = pr.productIdx
+	where depth = 1 and status = 'N'
+	`;
+	const [selectAuthProductRow] = await connection.query(selectAuthProductListQuery);
+	return selectAuthProductRow;
+}
+async function selectChildAuthProduct(connection, productIdx) {
+	const selectChildAuthProductQuery = `
+	SELECT p.productIdx, productName, pr.productTypeIdx from Product p
+	LEFT JOIN ProductRelation pr on p.productIdx = pr.productIdx
+	where depth = 2 and status = 'N' and pr.parentIdx = ?
+	`;
+	const [selectChildAuthProductRow] = await connection.query(selectChildAuthProductQuery, productIdx);
+	return selectChildAuthProductRow;
+}
+async function suspendProductAuth(connection, userAuthIdx) {
+	const updateUserProductAuthQuery = `
+	UPDATE UserAuth set status = "Y", expireAt = now() where userAuthIdx = ? and status = "N"
+	`;
+	const [updateUserProductAuthRow] = await connection.query(updateUserProductAuthQuery, userAuthIdx);
+	return updateUserProductAuthRow;
+}
+
+async function updateProductAuth(connection, userAuthIdx, expireAt) {
+	const updateUserProductAuthQuery = `
+	UPDATE UserAuth set expireAt = ? where userAuthIdx = ? and status = "N" 
+	`;
+	const [updateUserProductAuthRow] = await connection.query(updateUserProductAuthQuery, [expireAt, userAuthIdx]);
+	return updateUserProductAuthRow;
+}
+
+async function insertAuthorizationAuth(connection, authorizationParams) {
+	const insertAuthorizationAuthQuery = `
+	INSERT AuthorizationLog  (userAuthIdx, adminIdx) VALUES ?;
+	`;
+	const [insertAuthorizationAuthRow] = await connection.query(insertAuthorizationAuthQuery, [authorizationParams]);
+	return insertAuthorizationAuthRow;
+}
+
+async function deleteAuthorizationLog(connection, userAuthIdx) {
+	const deleteAuthorizationLogQuery = `
+	DELETE FROM AuthorizationLog where userAuthIdx = ?
+	`;
+	const [deleteAuthorizationLogRow] = await connection.query(deleteAuthorizationLogQuery, userAuthIdx);
+	return deleteAuthorizationLogRow;
+}
+
+async function selectAuthorLogList(connection, searchType, searchValue, infoType) {
+	let condition = "";
+	switch (parseInt(infoType)) {
+		case 0:
+			condition = `AND us.${searchType} LIKE '%${searchValue}%'`;
+			break;
+		case 1:
+			condition = `AND u.${searchType} LIKE '%${searchValue}%' `;
+			break;
+		case 2:
+			condition = `AND p.${searchType} LIKE '%${searchValue}%'`;
+			break;
+		default:
+			break;
+	}
+
+	const selectAuthorizationLogQuery =
+		`
+	SELECT ua.userAuthIdx, us.userName, u.nickname as adminNickname, us.nickname as userNickname,
+	ua.createdAt, p.productName, us.userEmail, us.userPhoneNum, ua.expireAt
+	from AuthorizationLog al
+	LEFT JOIN User u on u.userIdx = al.adminIdx
+	LEFT JOIN UserAuth ua on al.userAuthIdx = ua.userAuthIdx
+	LEFT JOIN User us on us.userIdx = ua.userIdx
+	LEFT JOIN Product p on ua.productIdx = p.productIdx
+	WHERE ua.status= "N" 
+	` + condition;
+	const [selectAuthorizationLogRow] = await connection.query(selectAuthorizationLogQuery);
+	return selectAuthorizationLogRow;
+}
+
+module.exports = {
+	initPassword,
+	checkAdmin,
+	checkKing,
+	getExamIdx,
+	checkExamSubjectMulti,
+	checkProblemSubject,
+	checkExamSort,
+	checkCompanySortIdx,
+	checkExamSortRef,
+	checkExamSortDouble,
+	checkEnterpriseIdx,
+	checkRestoreExamDetailIdx,
+	checkRestorationIdx,
+	checkRestorationCommentIdx,
+	checkProblemErrorIdx,
+	checkCategoryIdx,
+	checkProblemCategoryIdx,
+	checkPublicCompanyName,
+	checkCompanyName,
+	examSortNameCheck,
+	checkExamSortLarge,
+	checkExamSortRefValue,
+	checkIsCertification,
+	checkPublicCompanyIdx,
+	checkExamBySort,
+	checkExamNameDuplication,
+	checkProblemCategory,
+	checkExam,
+	checkCompany,
+	checkProduct,
+	selectProblemErrorList,
+	selectProblemError,
+	selectBoardInfo,
+	selectUserList,
+	selectUserInfo,
+	selectExamSubject,
+	selectExamDetailDoubleCheck,
+	selectExamSubjectByExam,
+	selectAllExam,
+	selectLargeExamSort,
+	searchMultipleProblem,
+	searchSubjectiveProblem,
+	selectExamSubjectByName,
+	selectExamDetailDateInfo,
+	selectExamQuestion,
+	selectMultipleProblem,
+	selectExamSortList,
+	selectExamSortRef,
+	selectExamSort,
+	selectExamName,
+	selectExamInfoList,
+	selectExamDetailList,
+	selectSubjectProblem,
+	selectEnterpriseList,
+	selectEnterpriseInfo,
+	selectExcelProblem,
+	selectCustomProblemImageList,
+	selectExcelQuestion,
+	selectSearchUser,
+	selectExcelFileName,
+	selectRestoreExamDetail,
+	selectUnAuthenticationSum,
+	selectCustomProblemSum,
+	selectRestorationSum,
+	selectUserRestorationSum,
+	selectRestoreDate,
+	selectRestoreRound,
+	selectRestorationList,
+	selectRestorationLikeSum,
+	selectRestorationCommentSum,
+	selectExamInfo,
+	selectRestoreTicketList,
+	selectCustomProblemList,
+	selectCustomQuestionList,
+	selectRestoration,
+	selectMultipleProblemInfo,
+	selectSubjectiveProblemInfo,
+	selectLargeCategory,
+	selectCategoryByRef,
+	selectCategoryByProblemIdx,
+	selectCategory,
+	selectLargeCategoryRef,
+	selectPublicCompanySort,
+	getCompanySort,
+	selectPublicCompanyExam,
+	selectPublicCompanyExamList,
+	selectCompanyExamList,
+	selectPublicCompanyConditionList,
+	getCompanyList,
+	selectExamByCompany,
+	checkCompanyExamCategory,
+	checkCompanyExam,
+	selectExamDetailByExam,
+	selectExamBySearch,
+	selectExamSortName,
+	selectProblem,
+	selectSubjectiveProblem,
+	selectStoreCategory,
+	selectAllProductInfo,
+	selectProductExamDetail,
+	selectProductUrl,
+	checkExamSortName,
+	updateQuestionProblemIdx,
+	updateExamSubjectNum,
+	updateQuestion,
+	updateMultipleProblem,
+	updateSubjectiveProblem,
+	updateExam,
+	updateExamDetail,
+	updateExamSubject,
+	updateUserStatus,
+	updatePublicCompany,
+	updateCompany,
+	updateCompanyExamInfo,
+	updateCompanyExam,
+	updateExamSort,
+	upsertExamSortImage,
+	updateExamThumbnail,
+	updatePaymentProductRefund,
+	updateExamRecordTrigger,
+	updateReviewNoteTrigger,
+	updateRestoreExamDetail,
+	updateRestoration,
+	updateAdmissionTicketAuth,
+	updateRestorationCommentIsPublic,
+	updateRestorationSearch,
+	updateCategory,
+	upsertProblemSubject,
+	upsertSubjectiveProblemSubject,
+	upsertProblemCategory,
+	upsertSubjectiveProblemCategory,
+	upsertProblemExam,
+	upsertSubjectiveProblemExam,
+	updateProduct,
+	updateProductUrl,
+	updateUsersPoint,
+	deleteProductExamDetail,
+	deleteProductUrl,
+	deleteExamAdmissionTicket,
+	deleteRestoreExamDetail,
+	deleteRestorationCommentFk,
+	deleteRestorationFk,
+	deleteRestoration,
+	deleteUserRestoration,
+	deleteCustomProblem,
+	deleteRestorationComment,
+	deleteProblemError,
+	deleteMultipleProblem,
+	deleteSubjectiveProblem,
+	deleteExam,
+	deleteCompanyExam,
+	deleteExamDetail,
+	deleteQuestion,
+	deleteExamSubject,
+	deleteExamSubjectMulti,
+	deleteExamSort,
+	deleteUser,
+	deleteRestorationSearch,
+	deleteCategory,
+	deleteCategoryByRef,
+	deleteProblemExamDetail,
+	deleteProductExamDetailRelation,
+	deleteProduct,
+	deletePublicCompany,
+	deleteCompany,
+	deleteProblemExam,
+	deleteSubjectiveProblemExam,
+	deleteProblemSubject,
+	deleteSubjectiveProblemSubject,
+	deleteProblemCategory,
+	deleteSubjectiveProblemCategory,
+	deleteUserAuth,
+	insertExam,
+	insertExamDetail,
+	insertMultipleProblem,
+	insertSubjectiveProblem,
+	insertQuestion,
+	insertExamSubject,
+	insertExamSubjectMultiple,
+	insertExamSort,
+	insertProblemQuestion,
+	insertRestoreExamDetail,
+	insertRestorationSearch,
+	deleteWorkMultipleProblem,
+	insertWorkMultipleProblem,
+	updateWorkMultipleProblem,
+	insertCategory,
+	insertCompany,
+	postCompanyExamField,
+	insertCompanyExam,
+	getCompanyExamInfo,
+	getCompanyExamIdx,
+	insertCompanyExamInfo,
+	insertExamSortImage,
+	insertProblemSubject,
+	insertSubjectiveProblemSubject,
+	insertProblemCategory,
+	insertSubjectiveProblemCategory,
+	insertProblemExam,
+	insertSubjectiveProblemExam,
+	insertProduct,
+	insertProductUrl,
+	insertProductExamDetail,
+	insertProductRelation,
+	insertPoint,
+	selectPoint,
+	selectAdminPointLog,
+	selectPointList,
+	insertUserPoint,
+	insertUserPointLog,
+	updatePoint,
+	updateFrequency,
+	insertCompanyInfo,
+	updateCompanySort,
+	updateExamOrder,
+	updateCompanyOrder,
+	updateProductOrder,
+	selectStoreExamField,
+	subUserAuth,
+	getUserAuthExpireAt,
+	selectChildDuration,
+	searchSellList,
+	selectPaymentList,
+	userPurchaseDetail,
+	selectUserProductAuth,
+	selectProductInfo,
+	restoreUserAuth,
+	updatePaymentProductInfo,
+	extendUserAuth,
+	selectSubjectiveProblemError,
+	suspendUser,
+	selectAuthProductList,
+	selectChildAuthProduct,
+	suspendProductAuth,
+	updateProductAuth,
+	insertAuthorizationAuth,
+	deleteAuthorizationLog,
+	selectAuthorLogList,
+};
diff --git a/_old/src/Admin/adminProvider.js b/_old/src/Admin/adminProvider.js
new file mode 100644
index 0000000..926ec1b
--- /dev/null
+++ b/_old/src/Admin/adminProvider.js
@@ -0,0 +1,1857 @@
+const { pool } = require("../../config/database");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const adminDao = require("./adminDao");
+const examDao = require("../Exam/examDao");
+const examProvider = require("../Exam/examProvider");
+const userProvider = require("../User/userProvider");
+const { logger } = require("../../config/winston");
+const errorResponse = require("../../utils/errorResponse");
+const { async } = require("rxjs");
+
+// 관리자 체크
+exports.kingCheck = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const kingCheckResult = await adminDao.checkKing(connection, userIdx);
+		if (kingCheckResult[0].exist === 0) throw new errorResponse(baseResponse.USER_INVINCIBILITY, 400);
+		return kingCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 관리자 체크
+exports.adminCheck = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const adminCheckResult = await adminDao.checkAdmin(connection, userIdx);
+		// if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+		return adminCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 이름 체크
+exports.getExamIdx = async function (examname) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const [result] = await adminDao.getExamIdx(connection, examname);
+
+		return result ? result.examIdx : "";
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 상세 중복 체크
+exports.examDetailDoubleCheck = async function (examIdx, examRound, year) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examDetailDoubleCheckResult = await adminDao.selectExamDetailDoubleCheck(
+			connection,
+			examIdx,
+			examRound,
+			year,
+		);
+		if (examDetailDoubleCheckResult[0].exist === 1) throw new errorResponse(baseResponse.EXAMDETAIL_EXIST, 400);
+
+		return examDetailDoubleCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 게시판 체크 //
+exports.boardCheck = async function (boardIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const boardCheckResult = await adminDao.selectBoardInfo(connection, boardIdx);
+
+		return boardCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문항 번호 체크
+exports.questionNumCheck = async function (multipleProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const questionNumCheckResult = await adminDao.selectExamQuestion(connection, multipleProblemIdx);
+
+		return questionNumCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문항 번호 체크
+exports.categoryCheck = async function (categoryIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const categoryCheckResult = await adminDao.checkCategoryIdx(connection, categoryIdx);
+		if (categoryCheckResult[0].exist === 0) throw new errorResponse(baseResponse.CATEGORY_NOT_EXIST, 404);
+		return categoryCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문제 과목 중복 체크
+exports.problemSubjectCheck = async function (examDetailIdx, examSubjectIdx, problemNum) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const checkProblemSubjectResult = await adminDao.checkProblemSubject(
+			connection,
+			examDetailIdx,
+			examSubjectIdx,
+			problemNum,
+		);
+		if (checkProblemSubjectResult[0].exist === 1) throw new errorResponse(baseResponse.PROBLEM_SUBJECT_EXIST, 400);
+		return checkProblemSubjectResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 종류 체크
+exports.examSortIdxCheck = async function (examSortIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examSortCheckResult = await adminDao.checkExamSort(connection, examSortIdx);
+		if (examSortCheckResult[0].exist === 0) throw new errorResponse(baseResponse.EXAMSORT_NOT_EXIST, 404);
+		return examSortCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+/**
+ * 기업 시험 종류 체크
+ * 기업 시험 종류 체크 중분류와 소분류의 조합에 대해서 존재성을 검사해야한다.
+ * 기업과 자격증은 이제 아예 다른 차원으로 시험 종류를 판단하기 때문이다.
+ * @author 김기창
+ */
+//
+exports.companySortIdxCheck = async function (companyType, companyExamTypeIdx, companyExamFieldIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const companySortCheckParams = [companyType, companyExamTypeIdx, companyExamFieldIdx];
+		const companySortIdxCheckResult = await adminDao.checkCompanySortIdx(connection, companySortCheckParams);
+		if (companySortIdxCheckResult[0].exist === 0)
+			throw new errorResponse(baseResponse.COMPANY_EXAMFIELD_NOT_MATCH, 404);
+		return companySortIdxCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 종류 이름 체크
+exports.examSortRefValueCheck = async function (examSortIdx, examSortRef) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examSortRefValueCheckResult = await adminDao.checkExamSortRefValue(connection, examSortIdx, examSortRef);
+		if (examSortRefValueCheckResult[0].exist === 0) throw new errorResponse(baseResponse.EXAMSORT_NOT_EXIST, 404);
+		return examSortRefValueCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 회사 인덱스 체크
+exports.enterpriseIdxCheck = async function (enterpriseIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const checkEnterpriseIdxResult = await adminDao.checkEnterpriseIdx(connection, enterpriseIdx);
+		if (enterpriseIdxCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ENTERPRISE_NOT_EXIST, 404);
+		return checkEnterpriseIdxResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 구분 체크
+exports.examSortRefCheck = async function (examSortRef) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examSortRefCheckResult = await adminDao.checkExamSortRef(connection, examSortRef);
+
+		return examSortRefCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 종류 중복 체크
+exports.examSortDoubleCheck = async function (examSort, examSortRef) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examSortDoubleCheckResult = await adminDao.checkExamSortDouble(connection, examSort, examSortRef);
+
+		return examSortDoubleCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문제에 사용중인 과목 체크
+exports.SubjectProblemCheck = async function (examSubjectIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const SubjectProblemCheckResult = await adminDao.selectSubjectProblem(connection, examSubjectIdx);
+		if (SubjectProblemCheckResult.length > 0) throw new errorResponse(baseResponse.PROBLEM_SUBJECT_EXIST, 400);
+		return SubjectProblemCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+//  복원 회차 체크
+exports.restoreExamDetailIdxCheck = async function (restoreExamDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const RestoreExamDetailIdxCheckResult = await adminDao.checkRestoreExamDetailIdx(connection, restoreExamDetailIdx);
+		if (RestoreExamDetailIdxCheckResult[0].exist === 0)
+			throw new errorResponse(baseResponse.RESTORE_EXAM_NOT_EXIST, 404);
+		return RestoreExamDetailIdxCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문제에 사용중인 과목 체크
+exports.restorationIdxCheck = async function (restorationIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const RestorationIdxCheckResult = await adminDao.checkRestorationIdx(connection, restorationIdx);
+
+		return RestorationIdxCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문제에 사용중인 과목 체크
+exports.restorationCommentIdxCheck = async function (restorationCommentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const restorationCommentIdxCheckResult = await adminDao.checkRestorationCommentIdx(
+			connection,
+			restorationCommentIdx,
+		);
+		if (restorationCommentIdxCheckResult[0].exist === 0) throw new errorResponse(baseResponse.COMMENT_NOT_EXIST, 404);
+		return restorationCommentIdxCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문제에 사용중인 과목 체크
+exports.problemErrorIdxCheck = async function (problemErrorIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const problemErrorIdxCheckResult = await adminDao.checkProblemErrorIdx(connection, problemErrorIdx);
+		if (problemErrorIdxCheckResult[0].exist === 0) throw new errorResponse(baseResponse.PROBLEMERROR_NOT_EXIST, 400);
+		return problemErrorIdxCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문제에 사용중인 과목 체크
+exports.problemCategoryIdxCheck = async function (categoryIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const problemCategoryIdxCheckResult = await adminDao.checkProblemCategoryIdx(connection, categoryIdx);
+		if (problemCategoryCheck[0].exist === 1) throw new errorResponse(baseResponse.PROBLEM_CATEGORY_EXIST, 400);
+		return problemCategoryIdxCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 회사 이름 체크
+exports.publicCompanyNameCheck = async function (companyName) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const checkPublicCompanyNameResult = await adminDao.checkPublicCompanyName(connection, companyName);
+		if (checkPublicCompanyNameResult[0].exist === 1) throw new errorResponse(baseResponse.USER_NOT_EXIST, 400); //문구이상한데
+		return checkPublicCompanyNameResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+/**
+ * 회사 이름 체크
+ * @changeFrom publicCompanyNameCheck
+ */
+exports.companyNameCheck = async function (companyExamFieldIdx, companyName) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const checkCompanyNameResult = await adminDao.checkCompanyName(connection, companyExamFieldIdx, companyName);
+		if (checkCompanyNameResult[0].exist === 1) throw new errorResponse(baseResponse.COMPANY_ALREADY_EXIST, 400);
+		return checkCompanyNameResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+/**
+ * 기업 시험 소분류 이름 체크
+ * @param {*} examDetailIdx
+ * @returns
+ */
+exports.companyExamFieldNameCheck = async function (companyExamTypeIdx, companyExamFieldName) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const companyExamFieldNameCheckResult = await adminDao.examSortNameCheck(
+			connection,
+			companyExamTypeIdx,
+			companyExamFieldName,
+		);
+		if (companyExamFieldNameCheckResult[0].exist === 1) throw new errorResponse(baseResponse.EXAMSORT_EXIST, 400);
+		return companyExamFieldNameCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 자격증 시험 여부 체크
+exports.isCertificationCheck = async function (examDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const isCertificationCheckResult = await adminDao.checkIsCertification(connection, examDetailIdx);
+		return isCertificationCheckResult[0].exist;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 종류 참조 여부 체크
+exports.examByExamSortCheck = async function (examSortIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examByExamSortCheckResult = await adminDao.checkExamBySort(connection, examSortIdx);
+		if (examByExamSortCheckResult[0].exist === 1) throw new errorResponse(baseResponse.REFERENCE_EXISTS, 400);
+		return examByExamSortCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 에러 신고 조회
+exports.getProblemError = async function (recently, subjectiveLabel) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	let condition =
+		recently == "true"
+			? "ORDER BY date desc, time desc,ed.examIdx asc,count desc, ed.examDate asc, ed.examDetailIdx asc,pe.multipleProblemIdx asc;"
+			: `ORDER BY ed.examIdx asc,count desc, ed.examDate asc, ed.examDetailIdx asc,pe.multipleProblemIdx asc, date desc, time desc;`;
+	try {
+		const selectProblemErrorResult =
+			subjectiveLabel == "1"
+				? await adminDao.selectSubjectiveProblemError(connection, condition)
+				: await adminDao.selectProblemError(connection, condition);
+		return resultResponse(baseResponse.SUCCESS, selectProblemErrorResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 객관식 문제 정보 조회
+exports.getMultipleProblemInfo = async function (multipleProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectMultipleProblemInfoResult = await adminDao.selectMultipleProblem(connection, multipleProblemIdx);
+
+		return selectMultipleProblemInfoResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 주관식 문제 정보 조회
+exports.getSubjectiveProblemInfo = async function (subjectiveProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectSubjectiveProblemInfoResult = await adminDao.selectSubjectiveProblem(connection, subjectiveProblemIdx);
+		if (selectSubjectiveProblemInfoResult.length == 0) throw new errorResponse(baseResponse.PROBLEM_NOT_EXIST, 404);
+		return selectSubjectiveProblemInfoResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 목록 조회
+exports.getUserList = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectUserListResult = await adminDao.selectUserList(connection);
+
+		return resultResponse(baseResponse.SUCCESS, selectUserListResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 특정 유저 정보 조회
+exports.getUserInfo = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectUserInfoResult = await adminDao.selectUserInfo(connection, userIdx);
+
+		return resultResponse(baseResponse.SUCCESS, selectUserInfoResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 특정 유저 작성글 조회
+exports.getUserBoardList = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectUserBoardListResult = await adminDao.selectUserList(connection);
+
+		return resultResponse(baseResponse.SUCCESS, selectUserBoardListResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 과목 이름으로 검색
+exports.getExamSubjectName = async function (subjectName) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectExamSubjectNameResult = await adminDao.selectExamSubjectByName(connection, subjectName);
+
+		return resultResponse(baseResponse.SUCCESS, selectExamSubjectNameResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 인덱스로 과목 검색
+exports.getExamSubjectByExam = async function (examIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectExamSubjectByExamResult = await adminDao.selectExamSubjectByExam(connection, examIdx);
+
+		return resultResponse(baseResponse.SUCCESS, selectExamSubjectByExamResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험과 시험에 해당하는 과목들 조회
+exports.getExamSubjectBelongsToExam = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examList = await adminDao.selectAllExam(connection);
+		for (exam of examList) {
+			const selectExamSubjectByExamResult = await adminDao.selectExamSubjectByExam(connection, exam.examIdx);
+			exam.subjectList = selectExamSubjectByExamResult;
+		}
+		return resultResponse(baseResponse.SUCCESS, examList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getLargeExamSort = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectLargeExamSortResult = await adminDao.selectLargeExamSort(connection);
+		return resultResponse(baseResponse.SUCCESS, selectLargeExamSortResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 객관식 문제 검색
+exports.searchMultipleProblem = async function (examSortIdx, searchStringQuery, keywordList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const searchProblemResult = await adminDao.searchMultipleProblem(
+			connection,
+			examSortIdx,
+			searchStringQuery,
+			keywordList,
+		);
+		return resultResponse(baseResponse.SUCCESS, searchProblemResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 실기 문제 검색
+exports.searchSubjectiveProblem = async function (examSortIdx, searchStringQuery, keywordList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const searchProblemResult = await adminDao.searchSubjectiveProblem(
+			connection,
+			examSortIdx,
+			searchStringQuery,
+			keywordList,
+		);
+		return resultResponse(baseResponse.SUCCESS, searchProblemResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 날짜 정보 조회
+exports.getExamDetailDate = async function (examDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let idxInfo;
+		const infoResult = await adminDao.selectExamDetailDateInfo(connection, examDetailIdx);
+		if (!infoResult) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 404);
+
+		if (infoResult[0].examIdx < 10) idxInfo = "00" + infoResult[0].examIdx + infoResult[0].date;
+		else if (infoResult[0].examIdx < 100) idxInfo = "0" + infoResult[0].examIdx + infoResult[0].date;
+		else idxInfo = infoResult[0].examIdx + infoResult[0].date;
+		return idxInfo;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 종류 참조 조회
+exports.getExamSortRef = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamSortRefResult = await adminDao.selectExamSortRef(connection);
+
+		return resultResponse(baseResponse.SUCCESS, getExamSortRefResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 종류 조회
+exports.getExamSort = async function (where) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamSortResult = await adminDao.selectExamSort(connection, where);
+
+		return resultResponse(baseResponse.SUCCESS, getExamSortResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 자격증/공기업 시험 분류 조회
+exports.getExamSortList = async function (largeExamName) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examSortList = await adminDao.selectExamSortList(connection, largeExamName);
+
+		// 각 인덱스에 대한 중분류 리스트 조회
+		function setMediumDataIndex(datas) {
+			return new Set(
+				datas.map((data) => {
+					return data.mediumExamIdx;
+				}),
+			);
+		}
+
+		// 소분류에 대한 인덱스를 가지고 시험에 대한 정보 가져오기
+		async function getExamInfo(largeExamName, smallExamIdx) {
+			// 자격증과 기업에 대한 분기문
+			const getExamInfoResult =
+				largeExamName == "자격증"
+					? await examDao.getExamInfo(connection, smallExamIdx)
+					: await examDao.getCompanyExamInfo(connection, smallExamIdx);
+
+			return getExamInfoResult;
+		}
+
+		async function getDatas(datas, origin) {
+			return await Promise.all(
+				Array.from(datas).map(async (index) => {
+					// 중분류를 기준으로 기업을 나눈다고 판단.
+					const mediumExamInfo = origin.filter((data) => data.mediumExamIdx === index);
+					const temp = await Promise.all(
+						mediumExamInfo.map(async (item) => {
+							const examInfo = await getExamInfo(largeExamName, item.smallExamIdx);
+							return {
+								smallExamIdx: item.smallExamIdx,
+								smallExamName: item.smallExamName,
+								examInfo,
+							};
+						}),
+					);
+
+					return {
+						mediumExamIdx: mediumExamInfo[0].mediumExamIdx,
+						mediumExamName: mediumExamInfo[0].mediumExamName,
+						smallDatas: temp,
+					};
+				}),
+			);
+		}
+
+		/**
+		 * @todo 기존에는 smallDataIndex의 값이 존재하지 않으면 mediumDatas로 변경하도록 하였다.
+		 * 하지만 자격증과 공기업, 대기업이 모두 같은 분류 체계를 같게 된 시점부터는 모두 smallDataIndex가 전부 존재하게 된다.
+		 * 따라서 각자의 시험명에 smallExamSort에 따라서 자격증은 시험의 내용들을 불러올 수 있도록 해야하고
+		 * 공기업 /대기업의 경우는 CompanyExam으로부터 회사들을 가져와 시험을 불러올 수 있도록 해야한다.
+		 */
+
+		const mediumDataIndex = setMediumDataIndex(examSortList);
+
+		let getExamSortListResult = await getDatas(mediumDataIndex, examSortList);
+		return resultResponse(baseResponse.SUCCESS, getExamSortListResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 관리자 시험 조회
+exports.getExamName = async function (where) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamNameResult = await adminDao.selectExamName(connection, where);
+		return resultResponse(baseResponse.SUCCESS, getExamNameResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 관리자 자격증 시험 회차 목록 조회
+exports.getExamInfoList = async function (where) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let examInfoList = new Array();
+
+		//examSortRef에 대한 스트링값을 하드코딩으로 넣어주고 있음.
+		const examInfoListResult = await adminDao.selectExamInfoList(connection);
+		for (var object of examInfoListResult) {
+			const examSortRef = await examDao.selectExamSortRefName(connection, object.examSortRef);
+			let examDetailList = new Array();
+			object.subjectList = await adminDao.selectExamSubject(connection, object.examIdx);
+			let tmpExamDetailList = await adminDao.selectExamDetailList(connection, object.examIdx);
+			for (let object2 of tmpExamDetailList) {
+				object2.check = false;
+				examDetailList.push(object2);
+			}
+			object.examSortRef = examSortRef;
+			object.examDetailList = examDetailList;
+			examInfoList.push(object);
+		}
+		return resultResponse(baseResponse.SUCCESS, examInfoList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 날짜별 기업 조회
+exports.getEnterpriseList = async function (date) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let array = [];
+		const electEnterpriseList = await adminDao.selectEnterpriseList(connection);
+
+		for (var object of electEnterpriseList) {
+			var examDate = object.examDate.split(", ");
+
+			for (var examDateInfo of examDate) {
+				if (examDateInfo.indexOf(date) != -1) {
+					var endObject = {
+						enterpriseIdx: object.enterpriseIdx,
+						enterpriseName: object.enterpriseName,
+						description: "examDate",
+						date: examDateInfo,
+						content: object.content,
+					};
+					array.push(endObject);
+				}
+			}
+		}
+		for (var object of electEnterpriseList) {
+			if (object.startDate.indexOf(date) != -1) {
+				var startObject = {
+					enterpriseIdx: object.enterpriseIdx,
+					enterpriseName: object.enterpriseName,
+					description: "startDate",
+					date: object.startDate,
+					content: object.content,
+				};
+				array.push(startObject);
+			}
+		}
+		for (var object of electEnterpriseList) {
+			if (object.endDate.indexOf(date) != -1) {
+				var endObject = {
+					enterpriseIdx: object.enterpriseIdx,
+					enterpriseName: object.enterpriseName,
+					description: "endDate",
+					date: object.endDate,
+					content: object.content,
+				};
+				array.push(endObject);
+			}
+		}
+
+		return resultResponse(baseResponse.SUCCESS, array);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 특정 기업 공고 조회
+exports.getEnterpriseInfo = async function (enterpriseIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let notice = new Object();
+		let enterpriseInfo = await adminDao.selectEnterpriseInfo(connection, enterpriseIdx);
+		var examDate = enterpriseInfo[0].examDate.split(", ");
+		var enterpriseObject = {
+			enterpriseIdx: enterpriseInfo[0].enterpriseIdx,
+			enterpriseName: enterpriseInfo[0].enterpriseName,
+			link: enterpriseInfo[0].link,
+			content: enterpriseInfo[0].content,
+			image: enterpriseInfo[0].image,
+			startDate: enterpriseInfo[0].startDate,
+			endDate: enterpriseInfo[0].endDate,
+			examDate: examDate,
+			education: enterpriseInfo[0].education,
+		};
+		notice = enterpriseObject;
+		notice.commonCertificate = await adminDao.selectCommonCertificate(connection, enterpriseIdx);
+		notice.professionalCertificate = await adminDao.selectProfessionalCertificate(connection, enterpriseIdx);
+		notice.foreignLanguage = await adminDao.selectForeignLanguage(connection, enterpriseIdx);
+		return resultResponse(baseResponse.SUCCESS, notice);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 액셀 다운로드 문제 조회
+exports.getExcelProblemList = async function (examDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const problemInfo = await adminDao.selectExcelProblem(connection, examDetailIdx);
+		if (problemInfo.length == 0) throw new errorResponse(baseResponse.DOWNLOAD_ERROR, 404);
+		const multipleProblemList = problemInfo.map((v) => v.multipleProblemIdx);
+		const questions = await examDao.selectQuestions(connection, multipleProblemList);
+		const examInfoList = await Promise.all(
+			problemInfo.map((v, i, a) => {
+				let problem = {
+					"문제 번호": v.problemNum,
+					문제: v.problem,
+					"문제 이미지": v.problemImage,
+					"정답 번호": v.answerNum,
+					해설: v.solution,
+					과목: v.subject,
+					"카텍스 유무": v.isKatex,
+					"강의 Url": v.lectureUrl,
+					"문제 Url": v.problemUrl,
+				};
+				const problemQuestion = questions.filter((val) => {
+					if (v.multipleProblemIdx == val.multipleProblemIdx) return true;
+				});
+				problemQuestion.map((v, i, a) => {
+					i++;
+					(problem["선지 번호" + i] = v.questionNum),
+						(problem["선지 내용" + i] = v.question),
+						(problem["선지 이미지" + i] = v.questionImage);
+				});
+				return problem;
+			}),
+		);
+
+		return examInfoList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 회차 파일이름 조회
+exports.getExcelFileName = async function (examDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExcelFileNameResult = await adminDao.selectExcelFileName(connection, examDetailIdx);
+
+		return getExcelFileNameResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 관리자 시험 조회
+exports.getSearchUser = async function (like) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getSearchUserResult = await adminDao.selectSearchUser(connection, like);
+
+		return resultResponse(baseResponse.SUCCESS, getSearchUserResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 관리자 복원 시험 목록 검색 swagger수정필요
+exports.getRestoreExamDetailList = async function (where) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let restoreExamDetailList = [],
+			restoreDateList = [],
+			restoreRoundList = [],
+			restoreExamSortList = [],
+			restoreExamSortRefList = [];
+		const restoreExamList = await adminDao.selectRestoreExamDetail(connection, where);
+		const restoreDate = await adminDao.selectRestoreDate(connection);
+		const restoreRound = await adminDao.selectRestoreRound(connection);
+		//const restoreExamSortRef = await adminDao.selectRestoreExamSortRef(connection);
+		// restoreExamSort 다시 넣어서 이름으로 찾아서?
+		for (let object of restoreDate) restoreDateList.push(object.ExamDate);
+		for (let object of restoreRound) restoreRoundList.push(object.restoreExamRound);
+		// for (let object of restoreExamSortRef) {
+		// 	const examIdx = object.examIdx;
+		// 	const lDivision = await examProvider.getExamDivision(examIdx, "L");
+		// 	restoreExamSortRefList.push(object.lDivision);
+		// }
+
+		for (let object of restoreExamList) {
+			if (object.status == "N") {
+				const examIdx = object.examIdx;
+
+				const lDivision = await examProvider.getExamDivision(examIdx, "L");
+				const mDivision = await examProvider.getExamDivision(examIdx, "M");
+				let examSort = null;
+				let examSortRef = null;
+
+				if (lDivision[0] != undefined) {
+					examSortRef = lDivision[0].examSortName;
+					if (!restoreExamSortRefList.includes(examSortRef)) restoreExamSortRefList.push(examSortRef);
+				}
+				if (mDivision[0] != undefined) {
+					examSort = mDivision[0].examSortName;
+					if (!restoreExamSortList.includes(examSort)) restoreExamSortList.push(examSort);
+				}
+
+				let unAuthSum = await adminDao.selectUnAuthenticationSum(connection, object.restoreExamDetailIdx);
+				let customSum = await adminDao.selectCustomProblemSum(connection, object.restoreExamDetailIdx);
+				let restorationSum = await adminDao.selectRestorationSum(connection, object.restoreExamDetailIdx);
+				let info = {
+					restoreExamDetailIdx: object.restoreExamDetailIdx,
+					restoreExamDate: object.restoreExamDate,
+					restoreExamRound: object.restoreExamRound,
+					examIdx: examIdx,
+					examName: object.examName,
+					examSortIdx: object.examSortIdx,
+					examSort: examSort,
+					examSortRef: examSortRef,
+					status: object.status,
+					thumbnail: object.thumbnail,
+					isPublic: object.isPublic,
+					unAuthSum: unAuthSum[0].unAuthSum,
+					customSum: customSum[0].customSum,
+					restorationSum: restorationSum[0].restorationSum,
+				};
+				restoreExamDetailList.push(info);
+			}
+		}
+
+		return resultResponse(baseResponse.SUCCESS, {
+			restoreDateList,
+			restoreRoundList,
+			restoreExamSortList,
+			restoreExamSortRefList,
+			restoreExamDetailList,
+		});
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 관리자 특정 회차 복원 문제조회
+exports.getRestorationList = async function (restoreExamDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let restorationList = [];
+		const restorationResult = await adminDao.selectRestorationList(connection, restoreExamDetailIdx);
+		for (let object of restorationResult) {
+			let likeSum = await adminDao.selectRestorationLikeSum(connection, object.restorationIdx); // 문제 좋아요 수
+			let commentSum = await adminDao.selectRestorationCommentSum(connection, object.restorationIdx); // 댓글 갯수
+			let userRestorationSum = await adminDao.selectUserRestorationSum(connection, object.restorationIdx); // 담은 유저 수
+			let restorationInfo = {
+				restorationIdx: object.restorationIdx,
+				origin: object.origin,
+				problem: object.problem,
+				isKatex: object.isKatex,
+				status: object.status,
+				likeSum: likeSum[0].sum,
+				commentSum: commentSum[0].sum,
+				userRestorationSum: userRestorationSum[0].sum,
+			};
+			restorationList.push(restorationInfo);
+		}
+
+		return resultResponse(baseResponse.SUCCESS, restorationList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 정보 조회
+exports.getExamInfo = async function (examIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getselectExamInfoResult = await adminDao.selectExamInfo(connection, examIdx);
+
+		return resultResponse(baseResponse.SUCCESS, getselectExamInfoResult[0]);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 정보 조회
+exports.getRestorationTicketList = async function (restoreExamDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getRestorationTicketListResult = await adminDao.selectRestoreTicketList(connection, restoreExamDetailIdx);
+
+		return resultResponse(baseResponse.SUCCESS, getRestorationTicketListResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 정보 조회
+exports.getPublicCompanySort = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectPublicCompanySortResult = await adminDao.selectPublicCompanySort(connection);
+
+		return resultResponse(baseResponse.SUCCESS, selectPublicCompanySortResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+/**
+ * 기업 회사 구분 조회
+ * @param {String} companyType
+ * @changeFrom getPublicCompanySort
+ */
+exports.getCompanySort = async function (companyType) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getCompanySortResult = await adminDao.getCompanySort(connection, companyType);
+
+		const result = getCompanySortResult.reduce((acc, curr) => {
+			const { companyExamType, companyExamFieldIdx, companyExamField } = curr;
+
+			const type = acc.find((item) => item.companyExamType === companyExamType);
+
+			if (!type) {
+				acc.push({
+					companyExamTypeIdx: curr.companyExamTypeIdx,
+					companyExamType,
+					companyExamFieldList: [{ companyExamFieldIdx, companyExamField }],
+				});
+			} else {
+				type.companyExamFieldList.push({ companyExamFieldIdx, companyExamField });
+			}
+
+			return acc;
+		}, []);
+
+		return resultResponse(baseResponse.SUCCESS, result);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 액셀 다운로드 문제 조회
+exports.getExcelCustomProblem = async function (restoreExamDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let examInfoList = new Array();
+		const problemInfo = await adminDao.selectCustomProblemList(connection, restoreExamDetailIdx);
+		if (problemInfo.length == 0) throw new errorResponse(baseResponse.CUSTOM_PROBLEM_EMPTY, 404);
+		for (let object of problemInfo) {
+			const imageInfo = await adminDao.selectCustomProblemImageList(connection, object.customProblemIdx);
+			let images = "";
+			for (let object2 of imageInfo) {
+				images += object2.imageUrl + " \n";
+			}
+			let userInfo = await adminDao.selectUserInfo(connection, object.userIdx);
+			let questionInfo = await adminDao.selectCustomQuestionList(connection, object.customProblemIdx);
+
+			if (userInfo.length == 0) {
+				userInfo.push({
+					userName: "삭제된 회원",
+					userEmail: "삭제된 회원",
+					userPhoneNum: "삭제된 회원",
+				});
+			}
+			let problem = {
+				"문제 번호": object.problemNum,
+				문제: object.problem,
+			};
+			questionInfo.map((number, index, source) => {
+				index++;
+				problem["문항" + index] = number.question;
+			});
+			(problem["코멘트"] = object.comment), (problem["참고 사진"] = images), (problem["생성시간"] = object.createdAt);
+			problem["이름"] = userInfo[0].userName;
+			problem["이메일"] = userInfo[0].userEmail;
+			problem["핸드폰 번호"] = userInfo[0].userPhoneNum;
+			examInfoList.push(problem);
+		}
+
+		return resultResponse(baseResponse.SUCCESS, examInfoList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 객관식 문제 정보 조회
+exports.getMultipleProblemByIdx = async function (multipleProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectMultipleProblemInfoResult = await adminDao.selectMultipleProblemInfo(connection, multipleProblemIdx);
+		const questionlist = await examDao.selectQuestion(connection, multipleProblemIdx);
+		let result = selectMultipleProblemInfoResult[0];
+		result["questions"] = questionlist;
+
+		return result;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 주관식 문제 정보 조회
+exports.getSubjectiveProblemByIdx = async function (subjectiveProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectSubjectiveProblemInfoResult = await adminDao.selectSubjectiveProblemInfo(
+			connection,
+			subjectiveProblemIdx,
+		);
+		let result = selectSubjectiveProblemInfoResult[0];
+		return result;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 카테고리 참조 조회
+exports.getCategoryInfo = async function (categoryIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const result = await adminDao.selectCategory(connection, categoryIdx);
+
+		return result[0];
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+// 카테고리 조회
+exports.getCategoryList = async function (multipleProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let largeCategoryList = [],
+			result;
+		const largeCategory = await adminDao.selectLargeCategory(connection);
+		//test version
+		for (let object3 of largeCategory) {
+			let mediumList = [],
+				smallList = [],
+				xsmallList = [];
+			const categoryRefInfo = await adminDao.selectLargeCategoryRef(connection, object3.categoryIdx); //해당 카테고리에 속한 모든 카테고리
+			for (let object4 of categoryRefInfo) {
+				if (object4.categoryType == "M") mediumList.push(object4);
+				else if (object4.categoryType == "S") smallList.push(object4);
+				else xsmallList.push(object4);
+			}
+			smallList = smallList.map((e, i, a) => {
+				e.xsmallCategoryList = xsmallList.filter((list) => list.categoryRef == e.categoryIdx);
+				return e;
+			});
+			object3.mediumCategoryList = mediumList.map((e, i, a) => {
+				e.smallCategoryList = smallList.filter((list) => list.categoryRef == e.categoryIdx);
+				return e;
+			});
+			largeCategoryList.push(object3);
+		}
+
+		let problemCategoryResult = await adminDao.selectCategoryByProblemIdx(connection, multipleProblemIdx);
+		if (problemCategoryResult.length == 0) result = {};
+		else {
+			result = {
+				largeCategory: {
+					categoryIdx: problemCategoryResult[0].largeCategoryIdx,
+					category: problemCategoryResult[0].largeCategory,
+					categoryName: problemCategoryResult[0].largeCategoryName,
+				},
+				mediumCategory: {
+					categoryIdx: problemCategoryResult[0].mediumCategoryIdx,
+					category: problemCategoryResult[0].mediumCategory,
+					categoryName: problemCategoryResult[0].mediumCategoryName,
+				},
+				smallCategory: {
+					categoryIdx: problemCategoryResult[0].smallCategoryIdx,
+					category: problemCategoryResult[0].smallCategory,
+					categoryName: problemCategoryResult[0].smallCategoryName,
+				},
+				xsmallCategory: {
+					categoryIdx: problemCategoryResult[0].xsmallCategoryIdx,
+					category: problemCategoryResult[0].xsmallCategory,
+					categoryName: problemCategoryResult[0].xsmallCategoryName,
+				},
+			};
+		}
+
+		return {
+			largeCategoryList,
+			problemLargeCategory: result,
+		};
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 회차 문제별 카테고리 조회
+exports.getExamDetailCategoryList = async function (examDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let problemList = await examDao.selectMultipleProblemAnswer(connection, "", examDetailIdx);
+		problemList = await Promise.all(
+			problemList.map(async (e, i, a) => {
+				delete e.questionIdx;
+				let problemCategoryResult = await adminDao.selectCategoryByProblemIdx(connection, e.multipleProblemIdx);
+				if (problemCategoryResult.length == 0) e.category = {};
+				else {
+					e.category = {
+						largeCategory: {
+							categoryIdx: problemCategoryResult[0].largeCategoryIdx,
+							categoryName: problemCategoryResult[0].largeCategoryName,
+							categoryType: problemCategoryResult[0].largeCategoryType,
+						},
+						mediumCategory: {
+							categoryIdx: problemCategoryResult[0].mediumCategoryIdx,
+							categoryRef: problemCategoryResult[0].mediumCategoryRef,
+							categoryName: problemCategoryResult[0].mediumCategoryName,
+							categoryType: problemCategoryResult[0].mediumCategoryType,
+						},
+						smallCategory: {
+							categoryIdx: problemCategoryResult[0].smallCategoryIdx,
+							categoryRef: problemCategoryResult[0].smallCategoryRef,
+							categoryName: problemCategoryResult[0].smallCategoryName,
+							categoryType: problemCategoryResult[0].smallCategoryType,
+						},
+						xsmallCategory: {
+							categoryIdx: problemCategoryResult[0].xsmallCategoryIdx,
+							categoryRef: problemCategoryResult[0].xsmallCategoryRef,
+							categoryName: problemCategoryResult[0].xsmallCategoryName,
+							categoryType: problemCategoryResult[0].xsmallCategoryType,
+						},
+					};
+				}
+				return e;
+			}),
+		);
+		return problemList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 공기업 시험 목록
+exports.getPublicCompanyExamInfoList = async function (where) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let publicCompanyExamList = await adminDao.selectPublicCompanyExamList(connection, where);
+
+		return publicCompanyExamList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+/**
+ * @changeFrom getPublicCompanyExamInfoList
+ * @param {String} companyType 공기업인지 대기업인지 확인
+ * 기업 관련 시험 목록 조회
+ */
+exports.getCompanyExamList = async function (companyType) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let publicCompanyExamList = await adminDao.selectCompanyExamList(connection, companyType);
+
+		return publicCompanyExamList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 공기업 목록
+exports.getPublicCompany = async function (where, companySortLabel) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let publicCompanyList = await adminDao.selectPublicCompanyConditionList(connection, where, companySortLabel);
+
+		return resultResponse(baseResponse.SUCCESS, publicCompanyList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+/**
+ * 공기업 목록 가져오기
+ * @changeFrom getPublicCompany
+ * @author 김기창
+ */
+exports.getCompanys = async function (companyType) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examSortList = await adminDao.selectExamSortList(connection, companyType);
+
+		// 각 인덱스에 대한 중분류 리스트 조회
+		function setMediumDataIndex(datas) {
+			return new Set(
+				datas.map((data) => {
+					return data.mediumExamIdx;
+				}),
+			);
+		}
+
+		async function getDatas(datas, origin) {
+			return await Promise.all(
+				Array.from(datas).map(async (index) => {
+					// 중분류를 기준으로 기업을 나눈다고 판단.
+					const mediumExamInfo = origin.filter((data) => data.mediumExamIdx === index);
+					const temp = await Promise.all(
+						mediumExamInfo.map(async (item) => {
+							const companyInfo = await examDao.getCompanysOfField(connection, item.smallExamIdx);
+							return {
+								smallExamIdx: item.smallExamIdx,
+								smallExamName: item.smallExamName,
+								companyInfo,
+							};
+						}),
+					);
+
+					return {
+						mediumExamIdx: mediumExamInfo[0].mediumExamIdx,
+						mediumExamName: mediumExamInfo[0].mediumExamName,
+						smallDatas: temp,
+					};
+				}),
+			);
+		}
+
+		const mediumDataIndex = setMediumDataIndex(examSortList);
+
+		let getExamSortListResult = await getDatas(mediumDataIndex, examSortList);
+		return resultResponse(baseResponse.SUCCESS, getExamSortListResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 공기업 시험 목록
+exports.getExamByPublicCompany = async function (where) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let publicCompanyExamList = await adminDao.selectExamByCompany(connection, where);
+
+		return resultResponse(baseResponse.SUCCESS, publicCompanyExamList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 기업 시험 category 중복 조회
+exports.checkCompanyExamCategory = async function (examIdx, category) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		// category까지 똑같은 것이 존재한다면 아예 동일한 시험이다.
+		let [{ exist: checkCompanyExamCategoryResult }] = await adminDao.checkCompanyExamCategory(
+			connection,
+			examIdx,
+			category,
+		);
+		if (checkCompanyExamCategoryResult) throw new errorResponse(baseResponse.EXAMNAME_EXIST, 400);
+		return checkCompanyExamCategoryResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+// 공기업 회사 관리
+exports.getPublicCompanyManagement = async function (where) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		// examSortType이 S인걸 조회함으로서 시험과 같아짐.
+		// 공기업의 경우엔 시험 이름과 examSortType이 S인 것의 examSortName이 같다.
+		let publicCompanyManagementList = await adminDao.selectPublicCompanyConditionList(connection, where);
+
+		return resultResponse(baseResponse.SUCCESS, publicCompanyManagementList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+/**
+ * 기업 회사 관리 조회
+ * @param {String} companyType 공기업인지 대기업인지 확인
+ * @changeFrom getPublicCompanyManagement
+ *
+ */
+exports.getCompanyList = async function (companyType) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let getCompanyListResult = await adminDao.getCompanyList(connection, companyType);
+
+		return resultResponse(baseResponse.SUCCESS, getCompanyListResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+/**
+ * 기업 시험 회차 목록 가져오기
+ * @param {String} companyType 기업이 공기업인지 대기업인지 판단하기 위함.
+ * 기존에 for문으로 반복하던 것을 병렬적으로 처리하기 위해 promise.all로 변경
+ *
+ */
+exports.getCompanyExamDetailList = async function (companyType) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		// examList: 시험 목록 => 이 시험 목록에 대한 회차들을 가져와야 한다.
+		let examList = await this.getCompanyExamList(companyType);
+
+		await Promise.all(
+			examList.map(async (exam) => {
+				const { examIdx } = exam;
+				exam["examDetailList"] = await adminDao.selectExamDetailByExam(connection, examIdx);
+			}),
+		);
+
+		return resultResponse(baseResponse.SUCCESS, examList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// examSortName 조회
+exports.getExamSortName = async function (examSortIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examSortName = await adminDao.selectExamSortName(connection, examSortIdx);
+
+		return examSortName[0].examSortName;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 이름 중복 조회
+exports.getExamNameDoubleCheck = async function (examName, examIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const checkResult = await adminDao.checkExamNameDuplication(connection, examName, examIdx);
+		if (examNameCheckResult[0].exist === 1) throw new errorResponse(baseResponse.EXAMNAME_EXIST, 400);
+		return checkResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 구분 확인
+exports.examSortLargeCheck = async function (examSortIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const checkResult = await adminDao.checkExamSortLarge(connection, examSortIdx);
+
+		return checkResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 종류 이름 확인
+exports.examSortNameCheck = async function (examSortName, examSortType) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examSortCheckResult = await adminDao.checkExamSortName(connection, examSortName, examSortType);
+		if (examSortCheckResult[0].exist === 1) throw new errorResponse(baseResponse.EXAMSORT_EXIST, 400);
+		return examSortCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 공기업 체크
+exports.publicCompanyCheck = async function (examSortIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const publicCompanyCheckResult = await adminDao.checkPublicCompanyIdx(connection, examSortIdx);
+		if (publicCompanyCheckResult[0].exist === 0) throw new errorResponse(baseResponse.PUBLICCOMPANY_NOT_EXIST, 404);
+		return publicCompanyCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 특정 문제 조회
+exports.selectProblem = async function (multipleProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectProblemResult = await adminDao.selectProblem(connection, multipleProblemIdx);
+
+		return selectProblemResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 상품 카테고리 조회
+exports.getStoreCategoryList = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const storeCategoryList = await adminDao.selectStoreCategory(connection);
+		let largeCategory = [],
+			mediumCategory = [],
+			productType = [];
+
+		const examField = await adminDao.selectStoreExamField(connection);
+		examField.forEach((obj) => {
+			let category = { productTypeIdx: obj.productTypeIdx, examField: obj.examField };
+			productType.push(category);
+		});
+		storeCategoryList[0].forEach((obj) => {
+			let category = {
+				productIdx: obj.productIdx,
+				productName: obj.productName,
+				productRef: obj.productRef,
+				productTypeIdx: obj.productTypeIdx,
+			};
+			if (obj.depth == 0) largeCategory.push(category);
+			else if (obj.depth == 1) mediumCategory.push(category);
+		});
+
+		return resultResponse(baseResponse.SUCCESS, {
+			largeCategory: largeCategory,
+			productType: productType,
+			mediumCategory: mediumCategory,
+		});
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 전체 상품 조회
+exports.getStoreProductList = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const storeProductList = await adminDao.selectAllProductInfo(connection);
+
+		for (let i = 0; i < storeProductList[0].length; i++) {
+			let examDetailList = await adminDao.selectProductExamDetail(connection, storeProductList[0][i].productIdx);
+			//let productUrlList = await adminDao.selectProductUrl(connection, storeProductList[0][i].productIdx);
+
+			storeProductList[0][i].examDetailList = [];
+			examDetailList[0].map((obj) => {
+				storeProductList[0][i].examDetailList.push(obj.examDetailIdx);
+			});
+
+			//storeProductList[0][i].productUrlList = productUrlList[0];
+		}
+
+		return resultResponse(baseResponse.SUCCESS, storeProductList[0]);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 판매 내역 조회
+exports.getSellList = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const sellList = await adminDao.selectPaymentList(connection);
+		return resultResponse(baseResponse.SUCCESS, sellList[0]);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 판매 내역 검색
+exports.searchSellList = async function (searchType, searchValue) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const sellList = await adminDao.searchSellList(connection, searchType, searchValue);
+		return resultResponse(baseResponse.SUCCESS, sellList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getSellDetailList = async function (paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const sellDetailList = await adminDao.userPurchaseDetail(connection, paymentIdx);
+		let totalPrice = sellDetailList[0].totalPrice + sellDetailList[0].totalPoint;
+		let { totalPoint, userName, level, pay_method, receipt_url } = sellDetailList[0];
+		let refundPrice = 0;
+		let detailList = [];
+		sellDetailList.forEach((obj) => {
+			if (!obj.refundPrice) obj.refundPrice = 0;
+			refundPrice += obj.refundPrice;
+			delete obj.userName;
+			delete obj.totalPoint;
+			delete obj.pay_method;
+			delete obj.totalPoint;
+			delete obj.paymentName;
+			delete obj.receipt_url;
+			detailList.push(obj);
+		});
+		let result = {
+			level: level,
+			userName: userName,
+			totalPrice: totalPrice,
+			pay_method: pay_method,
+			totalPoint: totalPoint,
+			refundPrice: refundPrice,
+			receipt_url: receipt_url,
+			detailList: detailList,
+		};
+		return resultResponse(baseResponse.SUCCESS, result);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 자식(depth 2) 상품 기간 조회
+exports.selectChildDuration = async function (productIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectChildDuration = await adminDao.selectChildDuration(connection, productIdx);
+		return selectChildDuration;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 구매 및 사용 내역 조회
+exports.getUserBuyList = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const buyList = await userProvider.userPurchaseHistory(userIdx);
+		const authList = await userProvider.userProductAuth(userIdx);
+		return resultResponse(baseResponse.SUCCESS, { buyList: buyList, authList: authList.result });
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 포인트 목록 조회
+exports.getPointList = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const pointList = await adminDao.selectPointList(connection);
+
+		return resultResponse(baseResponse.SUCCESS, pointList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 특정 포인트 조회
+exports.getPoint = async function (pointIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const pointList = await adminDao.selectPoint(connection, pointIdx);
+
+		return pointList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 관리자 포인트 지급 로그 조회
+exports.adminPointLogs = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const adminPointLogList = await adminDao.selectAdminPointLog(connection);
+
+		return resultResponse(baseResponse.SUCCESS, adminPointLogList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 자격증 인덱스 체크
+exports.ExamCheck = async function (prioArr) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		let isExamResult = [];
+		try {
+			await Promise.all(
+				prioArr.map(async (v) => {
+					let isExam = await adminDao.checkExam(connection, v.examIdx, v.examName);
+					isExamResult.push(isExam[0].exist);
+				}),
+			);
+			return isExamResult;
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 공기업 인덱스 체크
+exports.CompanyCheck = async function (prioArr) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		let isCompanyResult = [];
+		try {
+			await Promise.all(
+				prioArr.map(async (v) => {
+					let isCompany = await adminDao.checkExamSort(connection, v.companyIdx, v.companyName);
+					isCompanyResult.push(isCompany[0].exist);
+				}),
+			);
+			return isCompanyResult;
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 상품 인덱스 체크
+exports.ProductCheck = async function (prioArr) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		let isProductResult = [];
+		try {
+			await Promise.all(
+				prioArr.map(async (v) => {
+					let isProduct = await adminDao.checkProduct(connection, v.productIdx, v.productName);
+					isProductResult.push(isProduct[0].exist);
+				}),
+			);
+			return isProductResult;
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 상품 권한을 부여하기 위한 상품 목록 조회
+exports.getStoreAuthProductList = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let productResult = [];
+		let childProductResult = [];
+		const authProductList = await adminDao.selectAuthProductList(connection);
+		for (let item of authProductList) {
+			if (item.productTypeIdx == 1) {
+				item = await adminDao.selectChildAuthProduct(connection, item.productIdx);
+				childProductResult = childProductResult.concat(item);
+			} else if (item.productTypeIdx >= 2) productResult.push(item);
+		}
+		productResult = productResult.concat(childProductResult);
+		return productResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.searchAuthorLogList = async function (searchType, searchValue, infoType) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const authorLogList = await adminDao.selectAuthorLogList(connection, searchType, searchValue, infoType);
+		return authorLogList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
diff --git a/_old/src/Admin/adminRoute.js b/_old/src/Admin/adminRoute.js
new file mode 100644
index 0000000..64104b3
--- /dev/null
+++ b/_old/src/Admin/adminRoute.js
@@ -0,0 +1,372 @@
+module.exports = function (app) {
+	const admin = require("./adminController");
+	const jwtMiddleware = require("../../middlewares/jwtMiddleware");
+	const { upload } = require("../../config/s3");
+	const subjectTiveExam = require("./subjectTiveExam/subjectTiveExamRoute");
+	const companyRouter = require("./companyExam/companyRouter");
+
+	app.patch("/api/admins/password/initiate", jwtMiddleware, admin.passwordInit);
+
+	// API No ?. 이미지 업로드 test API
+	app.post("/api/admins/upload/image", jwtMiddleware, upload.array(["files"]), admin.uploadImage);
+
+	// API No ?. 문제 액셀 다운로드 test API
+	app.get("/api/admins/download/:examDetailIdx", jwtMiddleware, admin.downloadProblemExcel);
+
+	// API No ?. 문제 오류 신고 조회 API
+	app.get("/api/admins/problemError", jwtMiddleware, admin.getProblemError);
+
+	// API No ?. 문제 오류 삭제 API
+	app.patch("/api/admins/delete/problemError/:problemErrorIdx", jwtMiddleware, admin.deleteProblemError);
+
+	// API No ?.  객관식 문제 수정 API  upload 추가 s3 이미지 삭제
+	app.patch("/api/admins/update/multipleProblem/:multipleProblemIdx", jwtMiddleware, admin.patchMultipleProblem);
+
+	// API No ?.  객관식 문제 삽입 API  upload 추가
+	app.post("/api/admins/upload/multipleProblem", jwtMiddleware, admin.postMultipleProblem);
+
+	// API No ?.  객관식 문제 삭제 API  s3 이미지 삭제
+	app.patch("/api/admins/delete/multipleProblem/:multipleProblemIdx", jwtMiddleware, admin.deleteMultipleProblem);
+
+	// API No ?. 시험 과목 수정 API
+	app.patch("/api/admins/update/examSubject/:examSubjectIdx", jwtMiddleware, admin.updateExamSubject);
+
+	// API No ?. 시험 과목 번호 수정 API
+	app.patch("/api/admins/update/examSubject/:examSubjectIdx/subjectNum", jwtMiddleware, admin.updateExamSubjectNum);
+
+	// API No ?. 시험 과목 생성 API
+	app.post("/api/admins/upload/examSubject", jwtMiddleware, admin.uploadExamSubject);
+
+	// API No ?. 시험 과목 삭제 API
+	app.patch("/api/admins/delete/examSubject/:examSubjectIdx", jwtMiddleware, admin.deleteExamSubject);
+
+	// API No ?. 시험 생성 API
+	app.post("/api/admins/upload/exam", jwtMiddleware, admin.postExam);
+
+	/**
+	 * 자격증과 기업을 분리
+	 */
+
+	// API No ?. 공기업 회사 구분 조회 API
+	app.get("/api/admins/publicCompany/sort", jwtMiddleware, admin.getPublicCompanySort);
+
+	// API No ?. 공기업 조회 API
+	app.get("/api/admins/publicCompany", jwtMiddleware, admin.publicCompany);
+
+	// API No ?. 공기업 시험 조회 API
+	app.get("/api/admins/publicCompany/exam", jwtMiddleware, admin.publicCompanyExam);
+
+	// API No ?. 공기업 회사 생성 API
+	app.post("/api/admins/upload/publicCompany", jwtMiddleware, admin.postPublicCompany);
+
+	// API No ?. 공기업 회사 수정 API
+	app.patch("/api/admins/update/publicCompany", jwtMiddleware, admin.updatePublicCompany);
+
+	// API No ?. 공기업 회사 삭제 API
+	app.patch("/api/admins/delete/publicCompany/:publicComapanyIdx", jwtMiddleware, admin.deletepublicCompany);
+
+	// API No ?. 공기업 시험 생성 API
+	app.post("/api/admins/upload/publicCompany/exam", jwtMiddleware, admin.postCompanyExam);
+
+	// API No ?. 공기업 시험 수정 API
+	app.patch("/api/admins/update/publicCompany/exam", jwtMiddleware, admin.updateCompanyExam);
+
+	// API No ?. 공기업 회사관리 조회 API
+	app.get("/api/admins/publicCompany/management", jwtMiddleware, admin.publicCompanyManagement);
+
+	// API No ?. 공기업 시험 목록 조회 API (관리자페이지 공기업 시험)
+	app.get("/api/admins/publicCompany/examList", jwtMiddleware, admin.publicCompanyExamList);
+
+	/**
+	 *  API No ?. 공기업 시험 회차 목록 조회 (관리자페이지 공기업 문제) API
+	 * @deprecated
+	 * app.get("/api/admins/publicCompany/examDetail", jwtMiddleware, admin.CompanyExamDetailList);
+	 */
+
+	// API No ?. 시험 삭제 API - 자격증 / 기업 시험 모두에 적용
+	app.patch("/api/admins/delete/Exam/:examIdx", jwtMiddleware, admin.deleteExam);
+
+	// API No ?. 시험 정보 수정 API
+	app.patch("/api/admins/update/Exam/:examIdx", jwtMiddleware, admin.updateExam);
+
+	// API No ?. 시험 회차 생성 API   upload 추가
+	app.post("/api/admins/upload/Exam/:examIdx", jwtMiddleware, admin.postExamDetail);
+
+	// API No ?. 시험 상세 정보 수정 API   upload 추가 s3 이미지 삭제
+	app.patch("/api/admins/update/ExamDetail/:examDetailIdx", jwtMiddleware, admin.updateExamDetail);
+
+	// API No ?. 시험 상세 삭제 API   upload 추가 s3 이미지 삭제
+	app.patch("/api/admins/delete/ExamDetail/:examDetailIdx", jwtMiddleware, admin.deleteExamDetail);
+
+	// API No ?.  시험 구분 조회 API
+	app.get("/api/admins/examSortRef", jwtMiddleware, admin.getExamSortRef);
+
+	// API No ?. 자격증/공기업 분류 조회 API
+	app.get("/api/admins/examSortList", jwtMiddleware, admin.getExamSortList);
+
+	// API Np ?. 자격증 분류 순서 수정 API
+	app.patch("/api/admins/examOrder", jwtMiddleware, admin.updateExamOrder);
+
+	// API Np ?. 공기업 분류 순서 수정 API
+	app.patch("/api/admins/publicCompanyOrder", jwtMiddleware, admin.updateCompanyOrder);
+
+	// API Np ?. 상품 분류 순서 수정 API
+	app.patch("/api/admins/productOrder", jwtMiddleware, admin.updateProductOrder);
+
+	// API No ?.  시험 종류 조회 API
+	app.get("/api/admins/examSort", jwtMiddleware, admin.getExamSort);
+
+	// API No ?.  시험 이름 조회 API
+	app.get("/api/admins/examName", jwtMiddleware, admin.getExamName);
+
+	// API No ?.  시험 종류 삽입 API
+	app.post("/api/admins/examSort", jwtMiddleware, admin.createExamSort);
+
+	// API No ?.  시험 종류 삭제 API
+	app.patch("/api/admins/delete/examSort/:examSortIdx", jwtMiddleware, admin.deleteExamSort);
+
+	// API No ?.  시험 종류 수정 API
+	app.patch("/api/admins/update/examSort/:examSortIdx", jwtMiddleware, admin.updateExamSort);
+
+	// API No ?.  시험 구분 수정 API
+	//app.patch("/api/admins/update/examSortRef/:examSortRef", jwtMiddleware, admin.updateExamSortRef);
+
+	// API No ?.  문제관리 시험 목록 조회 API
+	app.get("/api/admins/examInfoList", jwtMiddleware, admin.getExamInfoList);
+
+	// API No ?.  Get all large examsorts
+	app.get("/api/admins/largeExamSort", jwtMiddleware, admin.getLargeExamSort);
+
+	// API No ?. 시험 문제 검색 API
+	app.get("/api/admins/search/problem", jwtMiddleware, admin.problemSearch);
+
+	// API No ?. 시험 해설 검색 API
+	app.get("/api/admins/search/solution", jwtMiddleware, admin.solutionSearch);
+
+	// API No ?. 과목 검색 API
+	app.get("/api/admins/search/ExamSubject", jwtMiddleware, admin.examSubjectSearch);
+
+	// API No ?. 시험 인덱스로 과목 이름 조회 API
+	app.get("/api/admins/search/ExamSubject/:examIdx", jwtMiddleware, admin.examSubjectSearchByExam);
+
+	// API No ?. 유저 목록 조회 API
+	app.get("/api/admins/userList", jwtMiddleware, admin.getUserList);
+
+	// API No ?. 유저 검색 API
+	app.get("/api/admins/searchUser", jwtMiddleware, admin.searchUser);
+
+	// API No ?. 유저 상세 정보 조회 API
+	app.get("/api/admins/userList/:userIdx/userInfo", jwtMiddleware, admin.getUserInfo);
+
+	// API No ?. 특정 회원 상태 변경 API
+	app.patch("/api/admins/userList/:userIdx/userInfo", jwtMiddleware, admin.updateUserStatus);
+
+	// API No ?. 유저 정지 API
+	app.patch("/api/admins/user/suspension", jwtMiddleware, admin.suspendUser);
+
+	// API No ?. 특정 유저 게시글 조회 API
+	app.get("/api/admins/userList/:userIdx/boardList", jwtMiddleware, admin.getUserBoardList);
+
+	// API No ?. 특정 유저 시험기록 조회 API
+	app.get("/api/admins/userList/:userIdx/examRecordList", jwtMiddleware, admin.getUserExamRecordList);
+
+	// API No ?. 특정 유저 시험기록 상세 조회 API
+	app.get(
+		"/api/admins/userList/:userIdx/examRecordList/:userExamRecordIdx",
+		jwtMiddleware,
+		admin.getUserExamRecordInfo,
+	);
+
+	// API No ?. 특정 유저 오답노트 시험 목록 조회 API
+	app.get("/api/admins/userList/:userIdx/reviewNote/exam", jwtMiddleware, admin.getUserRiviewNoteList);
+
+	// API No ?. 특정 유저 오답노트 시험 문제 조회 API
+	app.get(
+		"/api/admins/userList/:userIdx/reviewNote/problem/:examDetailIdx",
+		jwtMiddleware,
+		admin.getUserRiviewNoteProblem,
+	);
+
+	// API No ?. 특정 유저 삭제 API
+	app.get("/api/admins/deleteUser/:userIdx", jwtMiddleware, admin.deleteUser);
+
+	// API No ?. 달력 회사 조회 API
+	app.get("/api/admins/calendar", admin.getCalendarList);
+
+	// API No ?. 달력 회사 조회 API
+	app.get("/api/admins/calendar/:enterpriseIdx", admin.getCalendarInfo);
+
+	// API No ?. 복원 시험 목록 조회 API
+	app.get("/api/admins/restore", jwtMiddleware, admin.restoreList);
+
+	// API No ?. 복원 시험 목록 검색 API
+	//app.post("/api/admins/restore", jwtMiddleware, admin.searchRestoreList);
+
+	// API No ?. 복원 시험 회차 생성 API
+	app.post("/api/admins/upload/restoreExamDetail", jwtMiddleware, admin.createRestoreExamDetail);
+
+	// API No ?. 복원 시험 회차 수정 API
+	app.patch("/api/admins/update/restoreExamDetail/:restoreExamDetailIdx", jwtMiddleware, admin.updateRestoreExamDetail);
+
+	// API No ?. 복원 시험 회차 삭제 API
+	app.patch("/api/admins/delete/restoreExamDetail/:restoreExamDetailIdx", jwtMiddleware, admin.deleteRestoreExamDetail);
+
+	// API No ?. 검색 복원 시험 문제 삭제 API
+	app.patch("/api/admins/delete/restoration/:restorationIdx", jwtMiddleware, admin.deleteRestoration);
+
+	// API No ?. 복원 시험 회차 문제 조회 API
+	app.get(
+		"/api/admins/restoreExamDetail/:restoreExamDetailIdx/restorationList",
+		jwtMiddleware,
+		admin.selectRestorationList,
+	);
+
+	// API No ?. 시험 정보 조회 API
+	app.get("/api/admins/examInfo/:examIdx", jwtMiddleware, admin.selectExamInfo);
+
+	// API No ?. 검색 복원 문제 수정 API
+	app.patch("/api/admins/update/restoration/:restorationIdx", jwtMiddleware, admin.setRestoration);
+
+	// API No ?. 검색 복원 문제 댓글 삭제 API
+	app.patch(
+		"/api/admins/delete/restorationComment/:restorationCommentIdx",
+		jwtMiddleware,
+		admin.deleteRestorationComment,
+	);
+
+	// API No ?. 복원 회차 수험표 조회 API
+	app.get(
+		"/api/admins/restoreExamDetail/:restoreExamDetailIdx/admissionTicket",
+		jwtMiddleware,
+		admin.selectAdmissionTicketList,
+	);
+
+	// API No ?. 복원 회차 수험표 권한 변경 API
+	app.patch(
+		"/api/admins/update/admissionTicket/:examAdmissionTicketIdx",
+		jwtMiddleware,
+		admin.updateAdmissionTicketAuth,
+	);
+
+	// API No ?. 유저 생성 복원 문제 액셀 다운로드 API
+	app.get("/api/admins/download/userCustomProblem/:restoreExamDetailIdx", jwtMiddleware, admin.downloadCustomProblem);
+
+	// API No ?. 관리자 계정인지 확인하는 API
+	app.get("/api/admins/authority", jwtMiddleware, admin.checkAuthority);
+
+	// API No ?. 특정 문제 조회 API
+	app.get("/api/admins/problemInfo/:multipleProblemIdx", jwtMiddleware, admin.getProblemInfo);
+
+	// API No ?. 복원 문제 댓글 공개/비공개 전환 API
+	app.patch(
+		"/api/admins/update/restorationComment/:restorationCommentIdx",
+		jwtMiddleware,
+		admin.setRestorationCommentIsPublic,
+	);
+
+	// API No ?. 수험표 삭제 API
+	app.get("/api/admins/delete/admissionTicket/:restoreExamDetailIdx", jwtMiddleware, admin.deleteAdmissionTicket);
+
+	// API No ?. 전기기사 전기공사기사 회차별 동기화 API
+	app.post("/api/admins/synchronize/:examDetailIdx", jwtMiddleware, admin.synchronize);
+
+	// API No ?. 카테고리 조회 API
+	app.get("/api/admins/category/:multipleProblemIdx", jwtMiddleware, admin.selectCategory);
+
+	// API No ?. 카테고리 삽입 API
+	app.post("/api/admins/category", jwtMiddleware, admin.insertCategory);
+
+	// API No ?. 카테고리 수정 API
+	app.patch("/api/admins/update/category/:categoryIdx", jwtMiddleware, admin.updateCategory);
+
+	// API No ?. 카테고리 삭제 API
+	app.patch("/api/admins/delete/category/:categoryIdx", jwtMiddleware, admin.deleteCategory);
+
+	// API No ?. 시험 삭제 API
+	//app.patch('/admins/delete/Exam/:examIdx', jwtMiddleware, )
+
+	// API No ?. 시험 회차 카테고리 조회 API
+	app.get("/api/admins/examDetail/:examDetailIdx/category", jwtMiddleware, admin.examDetailCategoryList);
+
+	// API No ?. 상품 카테고리 조회 API
+	app.get("/api/admins/store/category", jwtMiddleware, admin.storeCategoryList);
+
+	// API No ?. 상품 생성 API
+	app.post("/api/admins/upload/product", jwtMiddleware, admin.createProduct);
+
+	// API No ?. 상품 수정 API
+	app.patch("/api/admins/update/product/:productIdx", jwtMiddleware, admin.updateProduct);
+
+	// API No ?. 상품 삭제 API -김기창
+	app.patch("/api/admins/delete/product/:productIdx", jwtMiddleware, admin.deleteProduct);
+
+	// API No ?. 상품 전체 조회 API
+	app.get("/api/admins/store/product", jwtMiddleware, admin.storeProductList);
+
+	// API No ?. 판매 내역 조회 API
+	app.get("/api/admins/store/sell", jwtMiddleware, admin.sellList);
+
+	// API No ?. 판매 내역 검색 API
+	app.get("/api/admins/store/sell/search", jwtMiddleware, admin.searchSellList);
+
+	// API No ?. 판매 내역 상세 조회 API
+	app.get("/api/admins/store/sellDetail", jwtMiddleware, admin.sellDetailList);
+
+	// API No ?. 유저 구매 및 상품 사용 내역 조회 API
+	// swagger 작성 필요
+	app.get("/api/admins/store/userBuy/:userIdx", jwtMiddleware, admin.userBuyList);
+
+	// API No ?. 구매 상품 환불 정보변경 API
+	app.post("/api/admins/update/productPayment/refund/:paymentProductIdx", jwtMiddleware, admin.paymentProductRefund);
+
+	// API No ?. 포인트 생성 API
+	app.post("/api/admins/upload/point", jwtMiddleware, admin.createPoint);
+
+	// API No ?. 포인트 수정 API
+	app.patch("/api/admins/update/point/:pointIdx", jwtMiddleware, admin.updatePoint);
+
+	// API No ?. 포인트 목록 조회 API
+	app.get("/api/admins/point", jwtMiddleware, admin.getPointList);
+
+	// API No ?. 유저 포인트 부여 API
+	app.post("/api/admins/userPoint", jwtMiddleware, admin.createUserPoint);
+
+	// API No ?. 유저 포인트 로그 조회 API
+	app.get("/api/admins/userList/:userIdx/pointLog", jwtMiddleware, admin.getUserPointLog);
+
+	// API No ?. 유저 상품 이용 로그 조회 API
+	app.get("/api/admins/userList/:userIdx/authLog", jwtMiddleware, admin.getUserAuthLog);
+
+	// API No ?. 유저 포인트 관리자 지급 로그 조회 API
+	app.get("/api/admins/pointLog", jwtMiddleware, admin.getAdminPointLog);
+
+	// API No ?. 유저 정지 API
+	app.patch("/api/admins/user/suspension", jwtMiddleware, admin.suspendUser);
+
+	// API NO ?. 상품 권한 부여를 위한 상품 일부 조회 API
+	app.get("/api/admins/store/productAuth", jwtMiddleware, admin.storeAuthProductList);
+
+	// // API No ?. 판매 내역 검색 API
+	// app.get("/api/admins/store/sell/search", jwtMiddleware, admin.searchSellList);
+
+	// API NO ?. 유저 상품 권한 부여
+	app.post("/api/admins/user/productAuth", jwtMiddleware, admin.giveUserProductAuth);
+
+	// API NO?. 유저 상품 권한 유효기간 변경 API (현재 사용하지 않음. 추후 사용할 수 있음.)
+	app.patch("/api/admins/productAuth", jwtMiddleware, admin.updateUserProductAuth);
+
+	// API NO?. 유저 상품 권한 회수 API
+	app.patch("/api/admins/productAuth/suspension", jwtMiddleware, admin.suspendUserProductAuth);
+
+	// API NO?. 관리자가 부여한 유저 상품 권한 목록 조회 API
+	app.get("/api/admins/search/productAuthLog", jwtMiddleware, admin.searchAuthorizationLog);
+
+	// 문제 빈도수 체크하기
+	app.patch("/api/admins/frequency", jwtMiddleware, admin.updateFrequency);
+
+	// 실기 API 모듈 분리
+	app.use("/api/admins/practical", subjectTiveExam);
+
+	// 기업 API 라우터 분리
+	app.use("/api/admins/company", companyRouter);
+};
diff --git a/_old/src/Admin/adminService.js b/_old/src/Admin/adminService.js
new file mode 100644
index 0000000..2412100
--- /dev/null
+++ b/_old/src/Admin/adminService.js
@@ -0,0 +1,1583 @@
+const { pool } = require("../../config/database");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const adminDao = require("./adminDao");
+const adminProvider = require("./adminProvider");
+const channelTalkService = require("../ChannelTalk/channelTalkService");
+const { logger } = require("../../config/winston");
+const { connect } = require("pm2");
+const { getProductInfo } = require("../Store/storeProvider");
+const userDao = require("../User/userDao");
+const storeDao = require("../Store/storeDao");
+const examDao = require("../Exam/examDao");
+const crypto = require("crypto");
+const { admin } = require("googleapis/build/src/apis/admin");
+const { log } = require("winston");
+const errorResponse = require("../../utils/errorResponse");
+
+// 유저 패스워드 초기화
+exports.initPassword = async function (userIdx, password) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			//123456789a 로 비밀번호 초기화
+			const hashedPassword = await crypto.createHash("sha512").update(password).digest("hex");
+
+			await adminDao.initPassword(connection, userIdx, hashedPassword);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+//에러 신고 삭제
+exports.patchProblemError = async function (problemErrorIdx, solution) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.deleteProblemError(connection, problemErrorIdx, solution);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 객관식 문제 수정  최대한 분리 후 transaction
+exports.patchMultipleProblem = async function (
+	userInputProblem,
+	multipleProblemIdx,
+	question,
+	questionNum,
+	questionImage,
+	answerNum,
+	examSubjectIdx,
+	questionIdx,
+	examDetailIdx,
+	categoryIdx,
+	problemNum,
+	isCertification,
+) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let questionUpdate = "",
+			questionImageUpdate = "",
+			questionList = [],
+			questionImageList = [],
+			updateQuestionProblemParams = [];
+		await connection.beginTransaction();
+
+		// *중요* question 과 questionImage 배열의 길이가 같지 않으면 (1:1 대응 필요)
+		// multipleProblemIdx에 isAnswer 값이 들어가 다른 문제의 선지가 수정됨.
+		let problemInfo = await adminDao.selectProblem(connection, multipleProblemIdx);
+		problemInfo = problemInfo[0];
+
+		const questionInfo = await adminDao.selectExamQuestion(connection, multipleProblemIdx);
+		const originProblemNum = problemInfo.problemNum,
+			originSubjectIdx = problemInfo.examSubjectIdx,
+			originCategoryIdx = !problemInfo.categoryIdx ? undefined : problemInfo.categoryIdx,
+			originAnswerNum = problemInfo.answerNum; // 선지부분 리팩토링 검토
+		delete problemInfo.problemNum;
+		delete problemInfo.examSubjectIdx;
+		delete problemInfo.categoryIdx;
+		delete problemInfo.answerNum;
+
+		if (categoryIdx !== originCategoryIdx) {
+			// 아예없는데 추가하는경우, 있는데 없애는 경우
+			if (!originCategoryIdx && categoryIdx)
+				// 카테고리 추가하는 경우
+				await adminDao.upsertProblemCategory(connection, multipleProblemIdx, categoryIdx, categoryIdx);
+			else if (originCategoryIdx && !categoryIdx)
+				// 카테고리 없애는 경우
+				await adminDao.deleteProblemCategory(connection, multipleProblemIdx);
+			// 카테고리 변경하는 경우
+			else if (originCategoryIdx && categoryIdx)
+				await adminDao.upsertProblemCategory(connection, multipleProblemIdx, originCategoryIdx, categoryIdx);
+		}
+
+		if (originProblemNum !== problemNum)
+			await adminDao.upsertProblemExam(connection, [examDetailIdx, multipleProblemIdx, originProblemNum, problemNum]);
+
+		if (JSON.stringify(userInputProblem) !== JSON.stringify(problemInfo))
+			await adminDao.updateMultipleProblem(connection, [
+				userInputProblem.problem,
+				userInputProblem.solution,
+				userInputProblem.provisionNum,
+				userInputProblem.isKatex,
+				userInputProblem.problemScore,
+				userInputProblem.problemUrl,
+				userInputProblem.lectureUrl,
+				userInputProblem.problemImage,
+				userInputProblem.isDelete,
+				userInputProblem.examHistory,
+				multipleProblemIdx,
+			]);
+
+		if (isCertification == 1 && originSubjectIdx !== examSubjectIdx)
+			await adminDao.upsertProblemSubject(connection, multipleProblemIdx, originSubjectIdx, examSubjectIdx);
+
+		// 선지 수정 부분
+		// 선지 그대로인 경우
+		if (question.length !== 0 && question.length == questionInfo.length) {
+			for (let i = 0; i < question.length; i++) {
+				if (question[i]) {
+					questionUpdate += ` WHEN ` + (i + 1) + ` THEN  ?`;
+					questionImageUpdate += ` WHEN ` + (i + 1) + ` THEN  ?`;
+					questionList.push(question[i]);
+					questionImageList.push(questionImage[i]);
+				}
+			}
+			updateQuestionProblemParams = questionList.concat(questionImageList, answerNum, multipleProblemIdx);
+			await adminDao.updateQuestion(connection, questionUpdate, questionImageUpdate, updateQuestionProblemParams);
+		} else if (question.length > questionInfo.length) {
+			// 선지가 늘어날 경우
+			for (let i = 0; i < question.length; i++) {
+				//updateQuestionParams = [question[i], questionImage[i], questionIdx[i]];
+				if (question[i]) {
+					questionUpdate += ` WHEN ` + (i + 1) + ` THEN  ? `;
+					questionImageUpdate += ` WHEN ` + (i + 1) + ` THEN  ? `;
+					questionList.push(question[i]);
+					questionImageList.push(questionImage[i]);
+				}
+			}
+			if (questionInfo.length !== 0) {
+				updateQuestionProblemParams = question.concat(questionImageList, answerNum, multipleProblemIdx);
+
+				await adminDao.updateQuestion(connection, questionUpdate, questionImageUpdate, updateQuestionProblemParams);
+			}
+			for (let i = questionInfo.length; i < questionNum.length; i++) {
+				let insertQuestionParams = [];
+				insertQuestionParams.push(questionNum[i]);
+				insertQuestionParams.push(question[i]);
+				insertQuestionParams.push(questionImage[i]);
+				insertQuestionParams.push(multipleProblemIdx);
+				if (answerNum == questionNum[i]) insertQuestionParams.push(1);
+				else insertQuestionParams.push(0);
+				await adminDao.insertQuestion(connection, insertQuestionParams);
+			}
+		} else {
+			// 선지가 줄어들 경우
+			for (let i = 0; i < questionNum.length; i++) {
+				if (question[i]) {
+					questionUpdate += ` WHEN ` + (i + 1) + ` THEN  ?`;
+					questionImageUpdate += ` WHEN ` + (i + 1) + ` THEN  ?`;
+					questionList.push(question[i]);
+					questionImageList.push(questionImage[i]);
+				}
+			}
+			if (question.length !== 0) {
+				updateQuestionProblemParams = question.concat(questionImageList, answerNum, multipleProblemIdx);
+				await adminDao.updateQuestion(connection, questionUpdate, questionImageUpdate, updateQuestionProblemParams);
+			}
+			for (let i = 0; i < questionInfo.length - questionNum.length; i++) {
+				await adminDao.deleteQuestion(connection, questionIdx[questionInfo.length - i - 1]);
+			}
+		}
+
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 주관식 문제 수정
+exports.patchSubjectiveProblem = async function (
+	userInputProblem, //problem, problemImage,solution,solutionImage,isKatex,problemScore,lectureUrl,isDelete,examHistory,subjectiveProblemIdx,
+	subjectiveProblemIdx,
+	examSubjectIdx,
+	categoryIdx,
+	examDetailIdx,
+	problemNum,
+) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		await connection.beginTransaction();
+
+		let problemInfo = await adminDao.selectSubjectiveProblem(connection, subjectiveProblemIdx);
+		problemInfo = problemInfo[0];
+
+		const originProblemNum = problemInfo.problemNum,
+			originSubjectIdx = problemInfo.examSubjectIdx,
+			originCategoryIdx = !problemInfo.categoryIdx ? undefined : problemInfo.categoryIdx;
+
+		delete problemInfo.problemNum;
+		delete problemInfo.examSubjectIdx;
+		delete problemInfo.categoryIdx;
+
+		if (categoryIdx !== originCategoryIdx) {
+			// 아예없는데 추가하는경우, 있는데 없애는 경우
+			if (!originCategoryIdx && categoryIdx)
+				// 카테고리 추가하는 경우
+				await adminDao.upsertSubjectiveProblemCategory(connection, subjectiveProblemIdx, categoryIdx, categoryIdx);
+			else if (originCategoryIdx && !categoryIdx)
+				// 카테고리 없애는 경우
+				await adminDao.deleteSubjectiveProblemCategory(connection, subjectiveProblemIdx);
+			// 카테고리 변경하는 경우
+			else if (originCategoryIdx && categoryIdx)
+				await adminDao.upsertSubjectiveProblemCategory(
+					connection,
+					subjectiveProblemIdx,
+					originCategoryIdx,
+					categoryIdx,
+				);
+		}
+
+		if (originProblemNum !== problemNum)
+			await adminDao.upsertSubjectiveProblemExam(connection, [
+				examDetailIdx,
+				subjectiveProblemIdx,
+				originProblemNum,
+				problemNum,
+			]);
+
+		if (JSON.stringify(userInputProblem) !== JSON.stringify(problemInfo))
+			//problem, problemImage,solution,solutionImage,isKatex,problemScore,lectureUrl,isDelete,
+			await adminDao.updateSubjectiveProblem(connection, [
+				userInputProblem.problem,
+				userInputProblem.problemImage,
+				userInputProblem.solution,
+				userInputProblem.solutionImage,
+				userInputProblem.isKatex,
+				userInputProblem.problemScore,
+				userInputProblem.problemUrl,
+				userInputProblem.lectureUrl,
+				userInputProblem.isDelete,
+				userInputProblem.examHistory,
+				subjectiveProblemIdx,
+			]);
+
+		if (originSubjectIdx !== examSubjectIdx)
+			await adminDao.upsertSubjectiveProblemSubject(connection, subjectiveProblemIdx, originSubjectIdx, examSubjectIdx);
+
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 생성
+exports.createExam = async function (insertExamParams, examSubjectIdx, examSubjectNum) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		try {
+			connection.beginTransaction();
+
+			const insertResult = await adminDao.insertExam(connection, insertExamParams);
+			for (var i = 0; i < examSubjectIdx.length; i++) {
+				await adminDao.insertExamSubjectMultiple(
+					connection,
+					insertResult[0].insertId,
+					examSubjectIdx[i],
+					examSubjectNum[i],
+				);
+			}
+			connection.commit();
+			return resultResponse(baseResponse.SUCCESS, { examIdx: insertResult[0].insertId });
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		connection.rollback();
+
+		throw error;
+	}
+};
+
+// 시험 디테일 생성
+exports.createExamDetail = async function (examIdx, examDate, examRound, publicLevel) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			const insertExamDetailParams = [examIdx, examDate, examRound, publicLevel];
+			const insertInfo = await adminDao.insertExamDetail(connection, insertExamDetailParams);
+			return resultResponse(baseResponse.SUCCESS, { examDetailIdx: insertInfo[0].insertId });
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+//  객관식 문제 생성 수정필요
+exports.createMultipleProblem = async function (
+	insertProblemParams,
+	questionNum,
+	question,
+	questionImage,
+	isDelete,
+	content,
+	answerNum,
+	examSubjectIdx,
+	categoryIdx,
+	examDetailIdx,
+	problemNum,
+	isCertification,
+) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await connection.beginTransaction();
+			const insertIdx = await adminDao.insertMultipleProblem(connection, insertProblemParams);
+
+			await adminDao.insertProblemExam(connection, examDetailIdx, insertIdx[0].insertId, problemNum);
+			if (categoryIdx) await adminDao.insertProblemCategory(connection, insertIdx[0].insertId, categoryIdx);
+			if (isCertification == 1) await adminDao.insertProblemSubject(connection, insertIdx[0].insertId, examSubjectIdx);
+			if (isDelete == 0) {
+				for (let i = 0; i < question.length; i++) {
+					let insertQuestionParams = [];
+					insertQuestionParams.push(i + 1);
+					insertQuestionParams.push(question[i]);
+					insertQuestionParams.push(questionImage[i]);
+					insertQuestionParams.push(insertIdx[0].insertId);
+					if (i + 1 == answerNum) insertQuestionParams.push(1);
+					else insertQuestionParams.push(0);
+					await adminDao.insertQuestion(connection, insertQuestionParams);
+				}
+			}
+			// // 문제 빈도수 넣기
+			// if (examHistory) {
+			// 	const problemIdx = insertIdx[0].insertId;
+			// 	await adminDao.updateFrequency(connection, problemIdx, 0, examHistory);
+			// }
+
+			await connection.commit();
+			return basickResponse(baseResponse.SUCCESS);
+		} catch (error) {
+			await connection.rollback();
+
+			throw error;
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+//주관식 문제 생성 - 주관식의 경우 반드시 자격증이기 때문에 자격증에 대한 검사를 하지 않아도 괜찮다.
+exports.createSubjectiveProblem = async function (
+	insertProblemParams,
+	examSubjectIdx,
+	categoryIdx,
+	examDetailIdx,
+	problemNum,
+) {
+	{
+		try {
+			const connection = await pool.getConnection(async (conn) => conn);
+			try {
+				await connection.beginTransaction();
+				const insertIdx = await adminDao.insertSubjectiveProblem(connection, insertProblemParams);
+				await adminDao.insertSubjectiveProblemExam(connection, examDetailIdx, insertIdx[0].insertId, problemNum);
+				if (categoryIdx) await adminDao.insertSubjectiveProblemCategory(connection, insertIdx[0].insertId, categoryIdx);
+				await adminDao.insertSubjectiveProblemSubject(connection, insertIdx[0].insertId, examSubjectIdx);
+
+				await connection.commit();
+				return basickResponse(baseResponse.SUCCESS);
+			} catch (error) {
+				await connection.rollback();
+
+				throw error;
+			} finally {
+				connection.release();
+			}
+		} catch (error) {
+			throw error;
+		}
+	}
+};
+
+// 시험 수정
+exports.patchExam = async function (updateExamParams, setInfo, examIdx, examSubjectIdx, examSubjectNum) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		await adminDao.updateExam(connection, setInfo, updateExamParams);
+		await adminDao.deleteExamSubjectMulti(connection, examIdx);
+		for (var i = 0; i < examSubjectIdx.length; i++) {
+			await adminDao.insertExamSubjectMultiple(connection, examIdx, examSubjectIdx[i], examSubjectNum[i]);
+		}
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 디테일 수정
+exports.patchExamDetail = async function (examDate, examRound, examDetailIdx, isPublic, publicLevel) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			const updateQuestionParams = [examDate, examRound, isPublic, publicLevel, examDetailIdx];
+			await adminDao.updateExamDetail(connection, updateQuestionParams);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 시험 과목 번호 수정
+exports.patchExamSubjectNum = async function (examSubjectNum, examIdx, examSubjectIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.updateExamSubjectNum(connection, examSubjectNum, examIdx, examSubjectIdx);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+// 시험 객관식 문제 삭제
+exports.deleteMultipleProblemIdx = async function (multipleProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		await adminDao.deleteMultipleProblem(connection, multipleProblemIdx);
+		await adminDao.deleteProblemCategory(connection, multipleProblemIdx);
+		await adminDao.deleteProblemExam(connection, multipleProblemIdx);
+		await adminDao.deleteProblemSubject(connection, multipleProblemIdx);
+		//await deleteQuestion(connection, multipleProblemIdx);
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+// 시험 주관식 문제 삭제
+exports.deleteSubjectiveProblemIdx = async function (subjectiveProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		await adminDao.deleteSubjectiveProblem(connection, subjectiveProblemIdx);
+		await adminDao.deleteSubjectiveProblemCategory(connection, subjectiveProblemIdx);
+		await adminDao.deleteSubjectiveProblemExam(connection, subjectiveProblemIdx);
+		await adminDao.deleteSubjectiveProblemSubject(connection, subjectiveProblemIdx);
+		//await deleteQuestion(connection, subjectiveProblemIdx);
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 상세 정보 삭제
+exports.deleteExamDetailIdx = async function (examDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		await adminDao.deleteExamDetail(connection, examDetailIdx);
+		await adminDao.deleteProblemExamDetail(connection, examDetailIdx);
+		await adminDao.deleteProductExamDetailRelation(connection, examDetailIdx);
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 삭제
+exports.deleteExam = async function (examIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		// examIdx가 CompanyExam 테이블에 존재하게 된다면 공기업 시험이고 이에 대해서도 status를 변경해줘야 한다.
+
+		const isCompanyExam = await adminDao.checkCompanyExam(connection, examIdx);
+		const { exist } = isCompanyExam[0];
+		if (exist) {
+			// 이 경우엔 해당 시험이 공기업이라는 뜻이다.
+			// CompanyExam에 대한 status도 수정해줘야 한다.
+			await adminDao.deleteCompanyExam(connection, examIdx);
+		}
+
+		await adminDao.deleteExam(connection, examIdx);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 상태 업데이트
+exports.patchUserStatus = async function (status, accessLevel, userIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.updateUserStatus(connection, status, accessLevel, userIdx);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 시험 과목 업데이트
+exports.patchExamSubject = async function (examSubjectName, passScore, examSubjectIdx) {
+	try {
+		const updateExamSubjectParams = [examSubjectName, passScore, examSubjectIdx];
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.updateExamSubject(connection, updateExamSubjectParams);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 시험 과목 삭제
+exports.deleteExamSubjectIdx = async function (examSubjectIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.deleteExamSubject(connection, examSubjectIdx);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 시험 과목 생성
+exports.createExamSubject = async function (examSubjectName, passScore) {
+	try {
+		const insertExamSubjectParams = [examSubjectName, passScore];
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			const insertInfo = await adminDao.insertExamSubject(connection, insertExamSubjectParams);
+			return resultResponse(baseResponse.SUCCESS, { examSubjectIdx: insertInfo[0].insertId });
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 시험 종류 생성
+exports.createExamSort = async function (examSort, examSortRef, examSortType) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			const insertInfo = await adminDao.insertExamSort(connection, examSort, examSortRef, examSortType);
+			return resultResponse(baseResponse.SUCCESS, {
+				examSortIdx: insertInfo[0].insertId,
+				examSortRef: examSortRef,
+			});
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 시험 종류 삭제
+exports.deleteExamSort = async function (examSortIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.deleteExamSort(connection, examSortIdx);
+			return resultResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 시험 종류 수정
+exports.updateExamSort = async function (updateExamSortParams) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.updateExamSort(connection, updateExamSortParams);
+			return resultResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 유저 삭제
+exports.createRestoreExamDetail = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		await userDao.deleteUserAllReviewNote(connection, userIdx);
+		await userDao.deleteUserAllUserExamRecord(connection, userIdx);
+		await userDao.deleteUser(connection, userIdx);
+		await connection.commit();
+		return basickResponse(baseResponse.USER_DROP_SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 복원 회차 삭제
+exports.deleteRestoreExamDetail = async function (restoreExamDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		await adminDao.deleteRestoreExamDetail(connection, restoreExamDetailIdx);
+		await adminDao.deleteRestorationFk(connection, restoreExamDetailIdx);
+		await adminDao.deleteCustomProblem(connection, restoreExamDetailIdx);
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 검색 복원 삭제 검색복원, 담은 유저, 댓글 삭제
+exports.deleteRestoration = async function (restorationIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await adminDao.deleteRestoration(connection, restorationIdx);
+
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+// 복원 회차 수정
+exports.updateRestoreExamDetail = async function (
+	restoreExamDate,
+	restorExamRound,
+	thumbnail,
+	isPublic,
+	restoreExamDetailIdx,
+) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const updateRestoreExamDetailParams = [restoreExamDate, restorExamRound, thumbnail, isPublic, restoreExamDetailIdx];
+		await adminDao.updateRestoreExamDetail(connection, updateRestoreExamDetailParams);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 복원 회차 생성
+exports.createRestoreExamDetail = async function (examIdx, restoreExamDate, restoreExamRound, thumbnail) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const insertRestoreExamDetailParams = [examIdx, restoreExamDate, restoreExamRound, thumbnail];
+		await adminDao.insertRestoreExamDetail(connection, insertRestoreExamDetailParams);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 검색 복원 문제 수정
+exports.setRestoration = async function (set, restorationIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const isDelete = set.indexOf("status");
+		await connection.beginTransaction();
+		await adminDao.updateRestoration(connection, set, restorationIdx);
+		if (isDelete != -1) await adminDao.deleteRestorationCommentFk(connection, restorationIdx);
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 검색 복원 문제 댓글 삭제
+exports.deleteRestorationComment = async function (restorationCommentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await adminDao.deleteRestorationComment(connection, restorationCommentIdx);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 수험표 권한 수정
+exports.updateAdmissionTicketAuth = async function (isAuthentication, examAdmissionTicketIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await adminDao.updateAdmissionTicketAuth(connection, isAuthentication, examAdmissionTicketIdx);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 복원 문제 댓글 공개/비공개 전환
+exports.updateRestorationCommentIsPublic = async function (restorationCommentIdx, isPublic) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.updateRestorationCommentIsPublic(connection, restorationCommentIdx, isPublic);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 수험표 삭제
+exports.deleteExamAdmissionTicket = async function (examAdmissionTicketIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.deleteExamAdmissionTicket(connection, examAdmissionTicketIdx);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+//전기공사기사 전기기사 회차별 동기화
+exports.updateWorkMultipleProblem = async function (copyTargetResult, workMultipleProblemIdxSet, workExamDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let insertWorkEngineerParams = [];
+		let updateIndexRange =
+			copyTargetResult.length >= workMultipleProblemIdxSet.length
+				? workMultipleProblemIdxSet.length
+				: copyTargetResult.length;
+
+		for (let i = 0; i < updateIndexRange; i++) {
+			insertWorkEngineerParams.push([
+				workMultipleProblemIdxSet[i].multipleProblemIdx,
+				copyTargetResult[i].problemNum,
+				copyTargetResult[i].problem,
+				copyTargetResult[i].answerNum,
+				copyTargetResult[i].provisionNum,
+				copyTargetResult[i].categoryIdx,
+				copyTargetResult[i].status,
+				copyTargetResult[i].problemImage,
+				copyTargetResult[i].solution,
+				copyTargetResult[i].examSubjectIdx,
+				copyTargetResult[i].isKatex,
+				copyTargetResult[i].isDelete,
+				copyTargetResult[i].problemScore,
+				copyTargetResult[i].problemUrl,
+				copyTargetResult[i].lectureUrl,
+			]);
+		}
+		await connection.beginTransaction();
+
+		await adminDao.updateWorkMultipleProblem(connection, [insertWorkEngineerParams]);
+		insertWorkEngineerParams = [];
+		if (updateIndexRange < copyTargetResult.length) {
+			for (let i = updateIndexRange; i < copyTargetResult.length; i++) {
+				insertWorkEngineerParams.push([
+					copyTargetResult[i].problemNum,
+					copyTargetResult[i].problem,
+					copyTargetResult[i].answerNum,
+					copyTargetResult[i].provisionNum,
+					copyTargetResult[i].categoryIdx,
+					copyTargetResult[i].status,
+					copyTargetResult[i].problemImage,
+					copyTargetResult[i].solution,
+					copyTargetResult[i].examSubjectIdx,
+					workExamDetailIdx,
+					copyTargetResult[i].isKatex,
+					copyTargetResult[i].isDelete,
+					copyTargetResult[i].problemScore,
+					copyTargetResult[i].problemUrl,
+					copyTargetResult[i].lectureUrl,
+				]);
+			}
+
+			await adminDao.insertWorkMultipleProblem(connection, [insertWorkEngineerParams]);
+		} else if (updateIndexRange < workMultipleProblemIdxSet.length) {
+			let deleteIdxSet = [];
+			let deleteIdxString = "(";
+			for (let i = updateIndexRange; i < workMultipleProblemIdxSet.length; i++) {
+				deleteIdxString += i == updateIndexRange ? "?" : ",?";
+				deleteIdxSet.push(workMultipleProblemIdxSet[i].multipleProblemIdx);
+			}
+			deleteIdxString += ")";
+			await adminDao.deleteWorkMultipleProblem(connection, deleteIdxString, deleteIdxSet);
+		}
+
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 카테고리 삽입
+exports.insertCategory = async function (categoryName, categoryRef, categoryType) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.insertCategory(connection, categoryName, categoryRef, categoryType);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 카테고리 수정
+exports.updateCategory = async function (categoryName, categoryRef, categoryType, categoryIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.updateCategory(connection, categoryName, categoryRef, categoryType, categoryIdx);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 카테고리 삭제   해당 카테고리에 해당하는 하위 카테고리를 삭제처리 어떻게할지 안정했음.
+exports.deleteCategory = async function (categoryIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		//const categoryInfo = await adminProvider.getCategoryInfo(categoryIdx);
+		await adminDao.deleteCategory(connection, categoryIdx);
+
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 공기업 회사 생성
+exports.insertPublicCompany = async function (
+	publicCompanySortIdx,
+	publicCompanyName,
+	companyThumbnail,
+	companySortLabel,
+) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			connection.beginTransaction();
+			const pulblicCompanyInfo = await adminDao.insertPublicCompany(
+				connection,
+				publicCompanySortIdx,
+				publicCompanyName,
+			);
+			if (companyThumbnail)
+				await adminDao.insertExamSortImage(connection, pulblicCompanyInfo[0].insertId, companyThumbnail);
+			await adminDao.insertCompanyInfo(connection, pulblicCompanyInfo[0].insertId, publicCompanyName, companySortLabel);
+			connection.commit();
+			return basickResponse(baseResponse.SUCCESS);
+		} catch (error) {
+			connection.rollback();
+
+			throw error;
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+/**
+ * 기업 생성
+ * 기업을 생성할 땐 앞으로 소분류에 해당하는 기업 시험 영역에 대한 정보만 있으면 가능하다.
+ * @changeFrom insertPublicCompany
+ * @author 김기창
+ */
+exports.insertCompany = async function (companyExamFieldIdx, companyName, companyThumbnail) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			connection.beginTransaction();
+			await adminDao.insertCompany(connection, companyExamFieldIdx, companyName, companyThumbnail);
+			// if (companyThumbnail) await adminDao.insertExamSortImage(connection, companyInfo[0].insertId, companyThumbnail);
+			connection.commit();
+			return basickResponse(baseResponse.SUCCESS);
+		} catch (error) {
+			connection.rollback();
+
+			throw error;
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+/**
+ * 기업 시험의 소분류 생성
+ */
+exports.postCompanyExamField = async function (companyExamTypeIdx, companyExamFieldName) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			connection.beginTransaction();
+			const companyInfo = await adminDao.postCompanyExamField(connection, companyExamTypeIdx, companyExamFieldName);
+			connection.commit();
+			return basickResponse(baseResponse.SUCCESS);
+		} catch (error) {
+			connection.rollback();
+
+			throw error;
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 공기업 회사 수정
+exports.updatePublicCompany = async function (
+	publicCompanyIdx,
+	publicCompanySortIdx,
+	publicCompanyName,
+	companyThumbnail,
+	companySortLabel,
+) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			connection.beginTransaction();
+			await adminDao.updatePublicCompany(connection, publicCompanyIdx, publicCompanySortIdx, publicCompanyName);
+			if (companySortLabel) {
+				await adminDao.updateCompanySort(connection, publicCompanyIdx, companySortLabel);
+			}
+
+			if (companyThumbnail) await adminDao.upsertExamSortImage(connection, publicCompanyIdx, companyThumbnail);
+			connection.commit();
+			return basickResponse(baseResponse.SUCCESS);
+		} catch (error) {
+			connection.rollback();
+
+			throw error;
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+/**
+ * 기업 회사 수정
+ * @param {Number} companyExamFieldIdx
+ * @param {String} companyName
+ * @param {String} companyThumbnail
+ * @changeFrom updatePublicCompany
+ */
+exports.updateCompany = async function (
+	prevSortIdx,
+	prevCompanyName,
+	companyExamFieldIdx,
+	companyName,
+	companyThumbnail,
+) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			connection.beginTransaction();
+
+			const [companyExamInfo] = await adminDao.getCompanyExamInfo(connection, prevSortIdx, prevCompanyName);
+
+			if (!companyExamInfo) throw new errorResponse(baseResponse.ENTERPRISE_NOT_EXIST, 404);
+
+			await adminDao.updateCompany(
+				connection,
+				prevSortIdx,
+				prevCompanyName,
+				companyExamFieldIdx,
+				companyName,
+				companyThumbnail,
+			);
+
+			connection.commit();
+			return basickResponse(baseResponse.SUCCESS);
+		} catch (error) {
+			connection.rollback();
+
+			throw error;
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 공기업 회사 삭제
+exports.deletePublicCompany = async function (examSortIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.deletePublicCompany(connection, examSortIdx);
+			return basickResponse(baseResponse.SUCCESS);
+		} catch (error) {
+			throw error;
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+/**
+ * 기업 회사 삭제
+ * @changeFrom deletePublicCompany
+ */
+exports.deleteCompany = async function (companyName, companyExamFieldIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			connection.beginTransaction();
+
+			const getCompanyExamInfo = await adminDao.getCompanyExamInfo(connection, companyExamFieldIdx, companyName);
+
+			// 해당 기업이 삭제되면서 이에 등록되어있는 시험들도 함께 삭제해준다.
+			await Promise.all(
+				getCompanyExamInfo.map(async (v) => {
+					if (v.examIdx) await adminDao.deleteExam(connection, v.examIdx);
+				}),
+			);
+
+			await adminDao.deleteCompany(connection, companyName, companyExamFieldIdx);
+			connection.commit();
+
+			return basickResponse(baseResponse.SUCCESS);
+		} catch (error) {
+			connection.rollback();
+
+			throw error;
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 기업 시험 생성
+exports.insertCompanyExam = async function (
+	companyExamFieldIdx,
+	companyName,
+	examName,
+	education,
+	category,
+	domain,
+	timeLimit,
+	problemCount,
+	questionType,
+	isBody,
+) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		/**
+		1. Exam 테이블에서 examName으로 먼저 존재하는 지 존재하지 않는 지 검사 후 insert
+		2. 1번 처리 이후  CompanyExam테이블에 upsert과정
+			a. CompanyExam 테이블에서 ExamIdx가 없는 열이 있는경우
+				> 이 경우에는 기업만 만들어져 있고 아직 시험이 만들어지지 않은 경우이다. 
+				CompanyExam 테이블에서 ExamSortIdx_S와 companyName, status이 있고 examIdx가 없는지를 검사하면 된다. 
+				> 기업만 만들어져 있다면 해당 부분에 학력, 시험유형, 영역(domain)을 update해주면 된다. 
+			b. CompanyExam 테이블에서 ExamIdx가 있는 열이 있는경우
+				> 이 경우에는 기업에 대한 시험이 이미 존재하는 경우이다. 
+				> 해당 경우에는 CompanyExam 테이블에 SET( examSortIdx_S, 기업이름, 학력, 시험유형, 영역(domain))이 
+				동일한지를 판단하고 insert를 진행해야 한다.
+		 */
+		await connection.beginTransaction();
+		const insertPublicCompanyExamParams = [examName, timeLimit, problemCount, questionType, isBody];
+		const insertInfo = await adminDao.insertCompanyExam(connection, insertPublicCompanyExamParams);
+		const examIdx = insertInfo[0].insertId;
+
+		const [companyExamInfo] = await adminDao.getCompanyExamInfo(connection, companyExamFieldIdx, companyName);
+		/**
+		 * @todo CompanyExam 테이블에서 각 로직에 따라 row가 추가될 수 있는 것을 다르게 설정하여야 한다.
+		 */
+		if (!companyExamInfo) {
+			throw new errorResponse(baseResponse.COMPNAY_NOT_EXIST, 404);
+		}
+
+		if (!companyExamInfo.examIdx) {
+			// 기업 시험 중에서 examSortIdx_S와 companyName은 있지만 (슈퍼 키 개념) examIdx는 없는 경우
+			// 단순히 examIdx를 업데이트 해주면 된다.
+			const updateCompanyExamParams = { companyExamFieldIdx, companyName, examIdx, education, category, domain };
+			await adminDao.updateCompanyExam(connection, updateCompanyExamParams);
+		} else {
+			// 기업 시험 중에서 examSortIdx_S와 companyName은 있지만 (슈퍼 키 개념) examIdx가 있는 경우
+			// 새로운 시험에 대해서 insert해주어야 한다.
+			const { companyUrl } = companyExamInfo;
+			const insertCompanyExamParams = [
+				companyExamFieldIdx,
+				companyName,
+				examIdx,
+				education,
+				category,
+				domain,
+				companyUrl,
+			];
+			await adminDao.insertCompanyExamInfo(connection, insertCompanyExamParams);
+		}
+
+		await connection.commit();
+
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 기업 시험 수정
+exports.updatePublicCompanyExam = async function (
+	companyExamFieldIdx,
+	companyName,
+	domain,
+	education,
+	category,
+	questionType,
+	timeLimit,
+	problemCount,
+) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+
+		const [{ examIdx }] = await adminDao.getCompanyExamIdx(
+			connection,
+			companyExamFieldIdx,
+			companyName,
+			domain,
+			education,
+			category,
+		);
+		const updatePublicCompanyExamParams = [questionType, timeLimit, problemCount, examIdx];
+		await adminDao.updateCompanyExamInfo(connection, updatePublicCompanyExamParams);
+		await connection.commit();
+
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 상품 생성
+exports.createProduct = async function (insertProductParams, productRef, examDetailList, examField) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		const productInsertResult = await adminDao.insertProduct(connection, insertProductParams);
+
+		// 회차 정보 연결
+		if (examDetailList.length) {
+			const insertProductExamDetailParams = examDetailList.map((v) => {
+				return [productInsertResult[0].insertId, v];
+			});
+			await adminDao.insertProductExamDetail(connection, insertProductExamDetailParams);
+		}
+
+		// 상품 참조 관계 연결
+		// TODO
+		const insertProductRelationParams = [productInsertResult[0].insertId, productRef, examField];
+		await adminDao.insertProductRelation(connection, insertProductRelationParams);
+
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 상품 수정
+exports.updateProduct = async function (updateProductParams, examDetailList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		// Params data 순서 조심
+		await connection.beginTransaction();
+
+		const productIdx = updateProductParams[updateProductParams.length - 1];
+
+		// 상품 정보 update
+		await adminDao.updateProduct(connection, updateProductParams);
+
+		// 상품 회차 재연동
+		await adminDao.deleteProductExamDetail(connection, productIdx);
+		const insertProductExamDetailParams = examDetailList.map((v) => {
+			return [productIdx, v];
+		});
+
+		if (insertProductExamDetailParams.length)
+			await adminDao.insertProductExamDetail(connection, insertProductExamDetailParams);
+
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+//상품 삭제 - 김기창
+exports.deleteProduct = async function (productIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await adminDao.deleteProduct(connection, productIdx);
+
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// PaymentProduct 환불정보 수정
+// TODO: 만약 50일 상품을 2일 이용하다가 환불하고 다시 취소하면은 48일을 부여하나? 50일을 부여하나?
+exports.updatePaymentProductRefund = async function (updatePaymentProductParams, deleteUserAuthParams) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+
+		await adminDao.updatePaymentProductRefund(connection, updatePaymentProductParams);
+		const childIdx = deleteUserAuthParams[1];
+		const product = await adminDao.selectProductInfo(connection, childIdx);
+		const { parentIdx, duration, productTypeIdx } = product[0];
+		const length = updatePaymentProductParams.length;
+		const now = new Date();
+
+		const isRefund = updatePaymentProductParams[length - 2];
+		const paymentProductIdx = updatePaymentProductParams[length - 1];
+		const paymentProductInfo = [isRefund, paymentProductIdx];
+
+		if (productTypeIdx >= 2) {
+			deleteUserAuthParams[1] = parentIdx;
+		}
+
+		let checkUserAuth = await adminDao.selectUserProductAuth(connection, deleteUserAuthParams);
+		if (checkUserAuth.length == 0) {
+			deleteUserAuthParams[1] = childIdx;
+			checkUserAuth = await adminDao.selectUserProductAuth(connection, deleteUserAuthParams);
+		}
+
+		if (isRefund == 0 && checkUserAuth[0].status == "Y") {
+			const productExpireAt = new Date(now.setDate(now.getDate() + duration));
+			deleteUserAuthParams.unshift(productExpireAt);
+			await adminDao.restoreUserAuth(connection, deleteUserAuthParams);
+			await adminDao.updatePaymentProductInfo(connection, paymentProductInfo);
+			return basickResponse(baseResponse.ADMIN_REFUND_CANCEL_SUCCESS);
+		} else if (isRefund == 0 && checkUserAuth[0].status == "N") {
+			deleteUserAuthParams.unshift(duration);
+			await adminDao.extendUserAuth(connection, deleteUserAuthParams);
+			return basickResponse(baseResponse.EXTEND_DURATION_SUCCESS);
+		}
+
+		const expireAt = new Date(checkUserAuth[0].expireAt);
+		const changedExpireAt = new Date(expireAt.setDate(expireAt.getDate() - duration));
+		let response;
+		if (now < changedExpireAt) {
+			deleteUserAuthParams.unshift(duration);
+			await adminDao.subUserAuth(connection, deleteUserAuthParams);
+			response = basickResponse(baseResponse.SUB_DURATION_SUCCESS);
+		} else {
+			await adminDao.deleteUserAuth(connection, deleteUserAuthParams);
+			response = basickResponse(baseResponse.DELETE_USER_AUTH_SUCCESS);
+		}
+
+		await adminDao.updatePaymentProductInfo(connection, paymentProductInfo);
+		await connection.commit();
+
+		return response;
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 관리자 포인트 생성
+exports.createPoint = async function (point, pointName) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await adminDao.insertPoint(connection, pointName, point);
+
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 관리자 유저 포인트 부여
+exports.createUserPoint = async function (insertUserPointParams, insertUserPointLog, userList, point) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		await adminDao.insertUserPoint(connection, insertUserPointParams);
+		await adminDao.insertUserPointLog(connection, insertUserPointLog);
+		await Promise.all(
+			userList.map(async (v, i, a) => {
+				await adminDao.updateUsersPoint(connection, point, v);
+			}),
+		);
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 포인트 수정
+exports.updatePoint = async function (pointName, point, pointIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await adminDao.updatePoint(connection, pointName, point, pointIdx);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문제 빈도수 업데이트하기
+exports.updateFrequency = async function (problemIdx, subjectiveLabel, examHistory) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const result = await adminDao.updateFrequency(connection, problemIdx, subjectiveLabel, examHistory);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 자격증 분류 순서 수정
+exports.updateExamOrder = async function (prioArr) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await Promise.all(
+				prioArr.map(async (v) => {
+					await adminDao.updateExamOrder(connection, v.prio, v.examIdx);
+				}),
+			);
+			return resultResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 공기업 분류 순서 수정
+exports.updateCompanyOrder = async function (prioArr) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await Promise.all(
+				prioArr.map(async (v) => {
+					await adminDao.updateCompanyOrder(connection, v.prio, v.companyIdx);
+				}),
+			);
+			return resultResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 상품 분류 순서 수정
+exports.updateProductOrder = async function (prioArr) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await Promise.all(
+				prioArr.map(async (v) => {
+					await adminDao.updateProductOrder(connection, v.prio, v.productIdx);
+				}),
+			);
+			return resultResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		throw error;
+	}
+};
+
+// 유저 정지
+exports.suspendUser = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await adminDao.suspendUser(connection, userIdx);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 상품 권한 회수
+exports.suspendProductAuth = async function (userAuthIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await adminDao.suspendProductAuth(connection, userAuthIdx);
+		await adminDao.deleteAuthorizationLog(connection, userAuthIdx);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 상품 권한 유효기간 변경
+exports.updateProductAuth = async function (userAuthIdx, expireAt) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await adminDao.updateProductAuth(connection, userAuthIdx, expireAt);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 권한 부여
+// TODO:  시간 정리 한번 싹다 해야함.
+exports.giveUserProductAuth = async function (userList, productIdx, expireAt, adminIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		for (let i = 0; i < userList.length; i++) {
+			let userAuthParams = [];
+			let userAuthLogParams = [];
+			const userAuth = await storeDao.checkUserAuth(connection, userList[i], productIdx);
+			if (!userAuth[0].exist) {
+				let date = new Date(expireAt);
+				date.setHours(date.getHours() + 6);
+				date.setSeconds(date.getSeconds() - 1);
+				userAuthParams.push([userList[i], productIdx, date]);
+				const result = await storeDao.insertUserAuth(connection, [userAuthParams]);
+				userAuthLogParams.push([result.insertId, adminIdx]);
+				await adminDao.insertAuthorizationAuth(connection, userAuthLogParams);
+			}
+		}
+		userList.map(async (userIdx) => await channelTalkService.updateChannelTalkUserProductInfo(userIdx));
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
diff --git a/_old/src/Admin/companyExam/companyRouter.js b/_old/src/Admin/companyExam/companyRouter.js
new file mode 100644
index 0000000..621ede4
--- /dev/null
+++ b/_old/src/Admin/companyExam/companyRouter.js
@@ -0,0 +1,49 @@
+const express = require("express");
+const router = express.Router();
+const jwtMiddleware = require("../../../middlewares/jwtMiddleware");
+const authCheck = require("../../../middlewares/authCheck");
+const makeAuthLog = require("../../../middlewares/makeAuthLog");
+const admin = require("../adminController");
+
+// 기업 관련 라우터
+/**
+ * /api/admins/company/{variable} 이런식으로 url이 구성된다.
+ * @default jwtMiddleware 기업관련 라우터에 대해서는 반드시 이것이 들어가야 한다. 개발이 완료된 이후에 추가할것.
+ */
+
+router.get("/", admin.getCompanys);
+
+// API No ?. 기업 회사관리 조회 API
+router.get("/management", admin.getCompanyList);
+
+// 기업 회사의 시험 목록 조회
+router.get("/examList", admin.companyExamList);
+
+// 기업 회사의 회차 목록 조회
+router.get("/examDetail", admin.companyExamDetailList);
+
+// 기업 회사 구분 조회
+router.get("/sort", admin.getCompanySort);
+
+// 기업 회사 생성 API
+router.post("/upload/companyName", admin.postCompany);
+
+// 기업 시험 종류에 따른 구분, 소분류에 해당하는 companyExamField 생성
+router.post("/companyExamField", admin.postCompanyExamField);
+
+// 기업 회사 수정 API
+router.patch("/update", admin.updateCompany);
+
+// 기업 회사 삭제 API
+router.patch("/delete", admin.deleteCompany);
+
+// 기업 시험 생성 API
+router.post("/upload/exam", admin.postCompanyExam);
+
+// 기업 시험 수정 API
+router.patch("/update/exam", admin.updateCompanyExam);
+
+// 기업 문제 관리 시험 목록 조회 API
+// router.get("/examInfoList", admin.getcompanyExamInfoList);
+
+module.exports = router;
diff --git a/_old/src/Admin/subjectTiveExam/subjectTiveExamRoute.js b/_old/src/Admin/subjectTiveExam/subjectTiveExamRoute.js
new file mode 100644
index 0000000..d52ef28
--- /dev/null
+++ b/_old/src/Admin/subjectTiveExam/subjectTiveExamRoute.js
@@ -0,0 +1,19 @@
+const express = require("express");
+const router = express.Router();
+const admin = require("../adminController");
+const jwtMiddleware = require("../../../middlewares/jwtMiddleware");
+const authCheck = require("../../../middlewares/authCheck");
+
+// API No . 실기 문제 생성하기
+router.post("/upload/subjectiveProblem", jwtMiddleware, admin.postSubjectiveProblem);
+
+// API No . 실기 문제 수정하기
+router.patch("/patchSubjectiveProblem/:subjectiveProblemIdx", jwtMiddleware, admin.patchSubjectiveProblem);
+
+// API No . 실기 문제 삭제하기
+router.patch("/delete/subjectiveProblem/:subjectiveProblemIdx", jwtMiddleware, admin.deleteSubjectiveProblem);
+
+// API No ?. 특정 실기 문제 조회 API
+router.get("/problemInfo/:subjectiveProblemIdx", jwtMiddleware, admin.getSubjectiveProblemInfo);
+
+module.exports = router;
diff --git a/_old/src/Calendar/calendarController.js b/_old/src/Calendar/calendarController.js
new file mode 100644
index 0000000..9b37ee7
--- /dev/null
+++ b/_old/src/Calendar/calendarController.js
@@ -0,0 +1,375 @@
+const calendarProvider = require("./calendarProvider");
+const calendarService = require("./calendarService");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const regKorean = /^[가-힣]{2,4}$/;
+const regDate = /^(19|20)\d{2}-(0[1-9]|1[012])$/;
+const regNumber = /^[0-9]/;
+const adminProvider = require("../Admin/adminProvider");
+
+/**
+ * API No.
+ * API Name : 회사 날짜 조회 API
+ * [GET] /calendar
+ */
+exports.getCalendarList = async function (req, res) {
+	const date = req.query.date;
+
+	if (!date) return res.send(basickResponse(baseResponse.DATE_EMPTY));
+	if (!regDate.test(date)) return res.send(basickResponse(baseResponse.DATE_NOT_MATCH));
+	const providerResult = await calendarProvider.getEnterpriseList(date);
+
+	return res.send(providerResult);
+};
+
+/**
+ * API No.
+ * API Name : 회사 날짜 조회 API
+ * [GET] /calendar/:enterpriseIdx
+ */
+exports.getCalendarInfo = async function (req, res) {
+	const enterpriseIdx = req.params.enterpriseIdx;
+
+	if (!enterpriseIdx) return res.send(basickResponse(baseResponse.ENTERPRISE_EMPTY));
+
+	const enterpriseIdxCheckResult = await calendarProvider.enterpriseIdxCheck(enterpriseIdx);
+
+	if (enterpriseIdxCheckResult.length === 0) return res.send(basickResponse(baseResponse.ENTERPRISE_NOT_EXIST));
+	if (enterpriseIdxCheckResult[0].status == "Y") return res.send(basickResponse(baseResponse.ISDELETED));
+	const providerResult = await calendarProvider.getEnterpriseInfo(enterpriseIdx);
+
+	return res.send(providerResult);
+};
+
+/**
+ * API No.
+ * API Name : 달력 생성 API
+ * [POST] /calendar/upload
+ */
+exports.postCalendar = async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const { enterpriseName, link, startDate, endDate } = req.body;
+	let { image, examDate, essentialElement, extraElement, etcElement } = req.body;
+	if (!adminIdx) return res.send(basickResponse(baseResponse.TOKEN_EMPTY));
+
+	if (!regNumber.test(adminIdx)) return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+
+	const adminIdxCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminIdxCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+
+	if (!enterpriseName) return res.send(basickResponse(baseResponse.ENTERPRISENAME_EMPTY));
+
+	if (!link) return res.send(basickResponse(baseResponse.LINK_EMPTY));
+
+	if (!startDate) return res.send(basickResponse(baseResponse.STARTDATE_EMPTY));
+
+	if (!endDate) return res.send(basickResponse(baseResponse.ENDDATE_EMPTY));
+
+	if (!examDate) examDate = "";
+
+	if (!image) image = "";
+	//return res.send(basickResponse(baseResponse.ENTERPRISE_IMAGE_EMPTY));
+
+	if (!essentialElement) essentialElement = "";
+	if (!extraElement) extraElement = "";
+	if (!etcElement) etcElement = "";
+
+	if (regDate.test(startDate)) return res.send(basickResponse(baseResponse.DATE_ERROR));
+	if (regDate.test(endDate)) return res.send(basickResponse(baseResponse.DATE_ERROR));
+
+	const insertExamParams = [
+		enterpriseName,
+		link,
+		startDate,
+		endDate,
+		examDate,
+		image,
+		essentialElement,
+		extraElement,
+		etcElement,
+	];
+	const createResult = await calendarService.createCalendar(insertExamParams);
+
+	return res.send(createResult);
+};
+
+/**
+ * API No.
+ * API Name : 달력 수정 API
+ * [PATCH] /calendar/update/:enterpriseIdx
+ */
+exports.patchCalendar = async function (req, res) {
+	const enterpriseIdx = req.params.enterpriseIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+	const { enterpriseName, link, startDate, endDate } = req.body;
+	let { image, examDate, essentialElement, extraElement, etcElement } = req.body;
+
+	if (!adminIdx) return res.send(basickResponse(baseResponse.TOKEN_EMPTY));
+
+	if (!regNumber.test(adminIdx)) return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+
+	const adminIdxCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminIdxCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+
+	if (!enterpriseIdx) return res.send(basickResponse(baseResponse.ENTERPRISE_EMPTY));
+
+	const enterpriseIdxCheckResult = await calendarProvider.enterpriseIdxCheck(enterpriseIdx);
+
+	if (enterpriseIdxCheckResult.length === 0) return res.send(basickResponse(baseResponse.ENTERPRISE_NOT_EXIST));
+
+	if (enterpriseIdxCheckResult[0].status == "Y") return res.send(basickResponse(baseResponse.ISDELETED));
+
+	if (!enterpriseName) return res.send(basickResponse(baseResponse.ENTERPRISENAME_EMPTY));
+
+	if (!link) return res.send(basickResponse(baseResponse.LINK_EMPTY));
+
+	if (!startDate) return res.send(basickResponse(baseResponse.STARTDATE_EMPTY));
+
+	if (!endDate) return res.send(basickResponse(baseResponse.ENDDATE_EMPTY));
+
+	if (!examDate) examDate = "";
+
+	if (!image) image = "";
+	//return res.send(basickResponse(baseResponse.ENTERPRISE_IMAGE_EMPTY));
+
+	if (!essentialElement) essentialElement = "";
+	if (!extraElement) extraElement = "";
+	if (!etcElement) etcElement = "";
+
+	if (regDate.test(startDate)) return res.send(basickResponse(baseResponse.DATE_ERROR));
+	if (regDate.test(endDate)) return res.send(basickResponse(baseResponse.DATE_ERROR));
+
+	const updateCalendarParams = [
+		enterpriseName,
+		link,
+		startDate,
+		endDate,
+		examDate,
+		image,
+		essentialElement,
+		extraElement,
+		etcElement,
+		enterpriseIdx,
+	];
+	const updateResult = await calendarService.updateCalendar(updateCalendarParams);
+
+	return res.send(updateResult);
+};
+
+/**
+ * API No.
+ * API Name : 달력 삭제 API
+ * [PATCH] /calendar/delete/:enterpriseIdx
+ */
+exports.deleteCalendar = async function (req, res) {
+	const enterpriseIdx = req.params.enterpriseIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) return res.send(basickResponse(baseResponse.TOKEN_EMPTY));
+
+	if (!regNumber.test(adminIdx)) return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+
+	const adminIdxCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminIdxCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+
+	if (!enterpriseIdx) return res.send(basickResponse(baseResponse.ENTERPRISE_EMPTY));
+
+	const enterpriseIdxCheckResult = await calendarProvider.enterpriseIdxCheck(enterpriseIdx);
+
+	if (enterpriseIdxCheckResult.length === 0) return res.send(basickResponse(baseResponse.ENTERPRISE_NOT_EXIST));
+	if (enterpriseIdxCheckResult[0].status == "Y") return res.send(basickResponse(baseResponse.ISDELETED));
+	const serviceResult = await calendarService.deleteCalendar(enterpriseIdx);
+
+	return res.send(serviceResult);
+};
+/**
+ * API No.
+ * API Name : 회사 생성 API
+ * [POST] /calendar/upload
+ */
+/*exports.postCalendar = async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const enterpriseName = req.body.enterpriseName; //필수
+	let recruitment = req.body.recruitment;
+	let link = req.body.link;
+	let content = req.body.content;
+	const startDate = req.body.startDate; //필수
+	const endDate = req.body.endDate; //필수
+	let examDate = req.body.examDate;
+	let education = req.body.education;
+	const thirdLanguage = req.body.thirdLanguage;
+	const image = req.body.image;
+	const commonCertificate = req.body.commonCertificate;
+	const professionalCertificate = req.body.professionalCertificate;
+	const foreignLanguage = req.body.foreignLanguage;
+
+	if (!adminIdx) return res.send(basickResponse(baseResponse.TOKEN_EMPTY));
+	if (regNumber.test(adminIdx)) return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+	const adminIdxCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminIdxCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+
+	if (!enterpriseName) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+	if (!startDate) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+	if (!endDate) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+	if (!recruitment) recruitment = "";
+	if (!link) link = "";
+	if (!content) content = "";
+	if (!examDate) examDate = "";
+	if (!education) education = "";
+
+	if (commonCertificate) {
+		for (let i = 0; i < commonCertificate.length; i++) {
+			if (!commonCertificate[i].commonCertificateName) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+			if (!commonCertificate[i].category) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+		}
+	}
+
+	if (professionalCertificate) {
+		for (let i = 0; i < professionalCertificate.length; i++) {
+			if (!professionalCertificate[i].professionalCertificateName)
+				return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+			if (!professionalCertificate[i].isEssential) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+		}
+	}
+
+	if (foreignLanguage) {
+		for (let i = 0; i < foreignLanguage.length; i++) {
+			if (!foreignLanguage[i].foreignLanguageName) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+			if (!foreignLanguage[i].passScore) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+		}
+	}
+
+	let enterpriseInfo = "(enterpriseName, recruitment, link, content, startDate, endDate, examDate, education";
+	let enterpriseValues = "(?, ?, ?, ?, ?, ?, ?, ?";
+	let enterpriseParam = [enterpriseName, recruitment, link, content, startDate, endDate, examDate, education];
+
+	if (thirdLanguage) {
+		enterpriseInfo += ", thirdLanguage";
+		enterpriseValues += ", ?";
+		enterpriseParam.push(thirdLanguage);
+	}
+	if (image) {
+		enterpriseInfo += ", image";
+		enterpriseValues += ", ?";
+		enterpriseParam.push(image);
+	}
+	enterpriseInfo += ")";
+	enterpriseValues += ")";
+
+	const enterpriseInsertResult = await calendarService.createEnterprise(
+		enterpriseInfo,
+		enterpriseValues,
+		enterpriseParam,
+		commonCertificate,
+		professionalCertificate,
+		foreignLanguage,
+	);
+
+	return res.send(enterpriseInsertResult);
+};
+*/
+/**
+ * API No.
+ * API Name : 회사 수정 API
+ * [PATCH] /calendar/update
+ */
+/*
+exports.patchCalendar = async function (req, res) {
+	const adminIdx = req.verifiedToken.userIdx;
+	const enterpriseIdx = req.params.enterpriseIdx;
+	const {
+		enterpriseName,
+		recruitment,
+		link,
+		content,
+		startDate,
+		endDate,
+		examDate,
+		education,
+		thirdLanguage,
+		image,
+		commonCertificate,
+		professionalCertificate,
+		foreignLanguage,
+	} = req.body;
+
+	if (!adminIdx) return res.send(basickResponse(baseResponse.TOKEN_EMPTY));
+	if (regNumber.test(adminIdx)) return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+	const adminIdxCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminIdxCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+
+	if (!enterpriseIdx) return res.send(basickResponse(baseResponse.ENTERPRISE_EMPTY));
+	if (!regNumber.test(enterpriseIdx)) return res.send(basickResponse(baseResponse.DATA_ERROR_TYPE)); //에러 문구 수정
+	const enterpriseIdxCheckResult = await calendarProvider.enterpriseIdxCheck(enterpriseIdx);
+	if (enterpriseIdxCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.ENTERPRISE_NOT_EXIST));
+
+	/* if (!enterpriseName) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+	if (!startDate) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+	if (!endDate) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+
+	if (commonCertificate) {
+		for (let i = 0; i < commonCertificate.length; i++) {
+			if (!commonCertificate[i].commonCertificateName) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+			if (!commonCertificate[i].category) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+		}
+	}
+
+	if (professionalCertificate) {
+		for (let i = 0; i < professionalCertificate.length; i++) {
+			if (!professionalCertificate[i].professionalCertificateName) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+			if (!professionalCertificate[i].isEssential) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+		}
+	}
+
+	if (foreignLanguage) {
+		for (let i = 0; i < foreignLanguage.length; i++) {
+			if (!foreignLanguage[i].foreignLanguageName) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+			if (!foreignLanguage[i].passScore) return res.send(basickResponse(baseResponse.ESSENTIAL_EMPTY)); //에러문구 수정
+		}
+	} 
+
+	let enterpriseUpdateInfo = " enterpriseName = ?, startDate = ?, endDate = ?";
+	let enterpriseUpdateParams = [enterpriseName, startDate, endDate];
+
+	if (recruitment) {
+		enterpriseUpdateInfo += ", recruitment = ?";
+		enterpriseUpdateParams.push(recruitment);
+	}
+	if (link) {
+		enterpriseUpdateInfo += ", link = ? ";
+		enterpriseUpdateParams.push(link);
+	}
+	if (content) {
+		enterpriseUpdateInfo += ", content = ?";
+		enterpriseUpdateParams.push(content);
+	}
+	if (examDate) {
+		enterpriseUpdateInfo += ", examDate = ?";
+		enterpriseUpdateParams.push(examDate);
+	}
+	if (education) {
+		enterpriseUpdateInfo += ", education = ?";
+		enterpriseUpdateParams.push(education);
+	}
+	if (thirdLanguage) {
+		enterpriseUpdateInfo += ", thirdLanguage = ?";
+		enterpriseUpdateParams.push(thirdLanguage);
+	}
+	if (image) {
+		enterpriseUpdateInfo += ", image = ?";
+		enterpriseUpdateParams.push(image);
+	}
+	enterpriseUpdateParams.push(enterpriseIdx);
+
+	const enterpriseUpdateResult = await calendarService.updateEnterprise(
+		enterpriseUpdateInfo,
+		enterpriseUpdateParams,
+		enterpriseIdx,
+		commonCertificate,
+		professionalCertificate,
+		foreignLanguage,
+	);
+
+	return res.send(enterpriseUpdateResult);
+}*/
diff --git a/_old/src/Calendar/calendarDao.js b/_old/src/Calendar/calendarDao.js
new file mode 100644
index 0000000..e8cb148
--- /dev/null
+++ b/_old/src/Calendar/calendarDao.js
@@ -0,0 +1,280 @@
+// 달력 기업 목록 조회
+async function selectEnterpriseList(connection) {
+	const selectEnterpriseListQuery = `
+  SELECT enterpriseIdx, enterpriseName, link,date_format(startDate, '%Y-%m-%d') as 'startDate',date_format(endDate, '%Y-%m-%d') as 'endDate', examDate, image
+  FROM Enterprise
+  WHERE status = 'N'
+  `;
+	const selectEnterpriseListRow = await connection.query(selectEnterpriseListQuery);
+	return selectEnterpriseListRow[0];
+}
+
+// 달력 기업 상세 조회
+async function selectEnterpriseInfo(connection, enterpriseIdx) {
+	const selectEnterpriseInfoQuery = `
+  SELECT enterpriseIdx, enterpriseName,link,image,date_format(startDate, '%Y-%m-%d') as 'startDate',date_format(endDate, '%Y-%m-%d') as 'endDate',examDate, essentialElement, extraElement, etcElement
+  FROM Enterprise
+  WHERE enterpriseIdx = ? AND status = 'N'
+  `;
+	const selectEnterpriseInfoRow = await connection.query(selectEnterpriseInfoQuery, enterpriseIdx);
+	return selectEnterpriseInfoRow[0];
+}
+
+// 달력 필수 조건 조회
+async function selectEnterpriseEssential(connection, enterpriseIdx) {
+	const selectEnterpriseEssentialQuery = `
+  SELECT content
+  FROM EnterpriseEssential
+  WHERE enterpriseIdx = ?
+  `;
+	const selectEnterpriseEssentialRow = await connection.query(selectEnterpriseEssentialQuery, enterpriseIdx);
+	return selectEnterpriseEssentialRow[0];
+}
+
+// 달력 가산 조건 조회
+async function selectEnterpriseExtra(connection, enterpriseIdx) {
+	const selectEnterpriseExtraQuery = `
+  SELECT content
+  FROM EnterpriseExtra
+  WHERE enterpriseIdx = ?
+  `;
+	const selectEnterpriseExtraRow = await connection.query(selectEnterpriseExtraQuery, enterpriseIdx);
+	return selectEnterpriseExtraRow[0];
+}
+// 달력 기타 조건 조회
+async function selectEnterpriseEtc(connection, enterpriseIdx) {
+	const selectEnterpriseEtcQuery = `
+  SELECT content
+  FROM EnterpriseEtc
+  WHERE enterpriseIdx = ?
+  `;
+	const selectEnterpriseEtcRow = await connection.query(selectEnterpriseEtcQuery, enterpriseIdx);
+	return selectEnterpriseEtcRow[0];
+}
+
+// // 특정 회사 어학 조건 조회
+// async function selectForeignLanguage(connection, enterpriseIdx) {
+// 	const selectForeignLanguageQuery = `
+//   SELECT foreignLanguageName, passScore, description as 'description', extraPoint, isReplace as 'replace'
+//   FROM ForeignLanguage
+//   WHERE enterpriseIdx = ?
+//   `;
+// 	const selectForeignLanguageRow = await connection.query(selectForeignLanguageQuery, enterpriseIdx);
+// 	return selectForeignLanguageRow[0];
+// }
+
+// // 특정 회사 전문 자격증 조건 조회
+// async function selectProfessionalCertificate(connection, enterpriseIdx) {
+// 	const selectProfessionalCertificateQuery = `
+//   SELECT professionalCertificateName, isEssential, extraPoint
+//   FROM ProfessionalCertificate
+//   WHERE enterpriseIdx = ?
+//   `;
+// 	const selectProfessionalCertificateRow = await connection.query(selectProfessionalCertificateQuery, enterpriseIdx);
+// 	return selectProfessionalCertificateRow[0];
+// }
+
+// // 특정 회사 전문 자격증 조건 조회
+// async function selectCommonCertificate(connection, enterpriseIdx) {
+// 	const selectCommonCertificateQuery = `
+//   SELECT commonCertificateName, category, extraPoint
+//   FROM CommonCertificate
+//   WHERE enterpriseIdx = ?
+//   `;
+// 	const selectCommonCertificateRow = await connection.query(selectCommonCertificateQuery, enterpriseIdx);
+// 	return selectCommonCertificateRow[0];
+// }
+
+// 회사 인덱스 체크
+async function checkEnterpriseIdx(connection, enterpriseIdx) {
+	const checkEnterpriseIdxQuery = `
+     select enterpriseIdx, status from Enterprise where enterpriseIdx = ?;
+     `;
+	const [checkEnterpriseIdxeRow] = await connection.query(checkEnterpriseIdxQuery, enterpriseIdx);
+	return checkEnterpriseIdxeRow;
+}
+
+// 시험 생성
+async function insertCalendar(connection, insertExamParams) {
+	const iinsertCalendarQuery = `
+    INSERT INTO Enterprise (enterpriseName, link, startDate, endDate, examDate, image, essentialElement, extraElement, etcElement)
+    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`;
+	const insertCalendarRow = await connection.query(iinsertCalendarQuery, insertExamParams);
+	return insertCalendarRow;
+}
+
+// 객관식 문항 수정
+async function updateCalendar(connection, updateCalendarParams) {
+	const updateCalendarQuery = `
+      UPDATE Enterprise 
+      SET enterpriseName = ?, link = ?, startDate = ?, endDate = ?, examDate = ?, image = ?, essentialElement = ?, extraElement = ?, etcElement = ?
+      WHERE enterpriseIdx = ?`;
+	const [updateCalendarRow] = await connection.query(updateCalendarQuery, updateCalendarParams);
+	return updateCalendarRow;
+}
+
+// 객관식 문제 삭제
+async function deleteCalendar(connection, enterpriseIdx) {
+	const deleteCalendarQuery = `
+    UPDATE Enterprise 
+      SET status = 'Y'
+      WHERE enterpriseIdx = ?
+    `;
+	const deleteCalendarRow = await connection.query(deleteCalendarQuery, enterpriseIdx);
+	return deleteCalendarRow;
+}
+
+//달력 회사 생성
+async function insertEnterprise(connection, enterpriseInfo, enterpriseValues, enterpriseParam) {
+	const insertEnterpriseQuery = `
+	INSERT INTO Enterprise
+	${enterpriseInfo}
+	VALUES
+	${enterpriseValues}
+	`;
+	const [insertEnterpriseRow] = await connection.query(insertEnterpriseQuery, enterpriseParam);
+	return insertEnterpriseRow;
+}
+
+//달력 회사 자격증 조건 생성
+async function insertCommonCertificate(
+	connection,
+	commonCertificateInfo,
+	commonCertificateValues,
+	commonCertificateParams,
+) {
+	const insertCommonCertificateQuery = `
+	INSERT INTO Commoncertificate
+	${commonCertificateInfo}
+	VALUES
+	${commonCertificateValues}
+	`;
+	const [insertCommonCertificateRow] = await connection.query(insertCommonCertificateQuery, commonCertificateParams);
+	return insertCommonCertificateRow;
+}
+
+//달력 회사 전문 자격증 조건 생성
+async function insertProfessionalCertificate(
+	connection,
+	professionalCertificateInfo,
+	professionalCertificateValues,
+	professionalCertificateParams,
+) {
+	const insertProfessionalCertificateQuery = `
+	INSERT INTO ProfessionalCertificate
+	${professionalCertificateInfo}
+	VALUES
+	${professionalCertificateValues}
+	`;
+	const [insertProfessionalCertificateRow] = await connection.query(
+		insertProfessionalCertificateQuery,
+		professionalCertificateParams,
+	);
+	return insertProfessionalCertificateRow;
+}
+
+//달력 회사 어학 조건 생성
+async function insertForeignLanguage(connection, foreignLanguageInfo, foreignLanguageValues, foreignLanguageParams) {
+	const insertForeignLanguageQuery = `
+	INSERT INTO ForeignLanguage
+	${foreignLanguageInfo}
+	VALUES
+	${foreignLanguageValues}
+	`;
+	const [insertForeignLanguageRow] = await connection.query(insertForeignLanguageQuery, foreignLanguageParams);
+	return insertForeignLanguageRow;
+}
+
+//달력 회사 수정
+async function updateEnterprise(connection, enterpriseUpdateInfo, enterpriseUpdateParams) {
+	const updateEnterpriseQuery = `
+	UPDATE Enterprise
+	SET ${enterpriseUpdateInfo}
+	WHERE enterpriseIdx = ?
+	`;
+	const [updateEnterpriseRow] = await connection.query(updateEnterpriseQuery, enterpriseUpdateParams);
+	return updateEnterpriseRow;
+}
+
+//자격증 조건 수정
+async function updateCommonCertificate(connection, commonCertificateUpdateInfo, commonCertificateUpdateParams) {
+	const updateCommonCertificateQuery = `
+	UPDATE CommonCertificate SET 
+	${commonCertificateUpdateInfo}
+	WHERE commonCertificateIdx = ?
+	`;
+	const [updateCommonCertificateRow] = await connection.query(
+		updateCommonCertificateQuery,
+		commonCertificateUpdateParams,
+	);
+	return updateCommonCertificateRow;
+}
+
+//전문 자격증 조건 수정
+async function updateProfessionalCertificate(connection, professionalInfo, professionalParams) {
+	const updateProfessionalQuery = `
+	UPDATE ProfessionalCertificate 
+	SET ${professionalInfo}
+	WHERE professionalCertificateIdx = ?
+	`;
+	const [updateProfessionalRow] = await connection.query(updateProfessionalQuery, professionalParams);
+	return updateProfessionalRow;
+}
+
+//어학 조건 수정
+async function updateForeignLanguage(connection, foreignLanguageInfo, foreignLanguageParams) {
+	const updateForeignLanguageQuery = `
+		UPDATE ForeignLanguage
+		SET ${foreignLanguageInfo}
+		WHERE foreignLanguageIdx = ?
+	`;
+	const [updateForeignLanguageRow] = await connection.query(updateForeignLanguageQuery, foreignLanguageParams);
+	return updateForeignLanguageRow;
+}
+
+//자격증 조건 삭제
+async function deleteCommonCertificate(connection, commonCertificateIdx) {
+	const deleteCommonCertificateQuery = `DELETE FROM CommonCertificate WHERE commonCertificateIdx = ?`;
+	const [deleteCommonCertificateRow] = await connection.query(deleteCommonCertificateQuery, commonCertificateIdx);
+	return deleteCommonCertificateRow;
+}
+
+//전문 자격증 조건 삭제
+async function deleteProfessionalCertificate(connection, professionalCertificateIdx) {
+	const deleteProfessionalCertificateQuery = `DELETE FROM ProfessionalCertificate WHERE professionalCertificateIdx = ?`;
+	const [deleteProfessionalCertificateRow] = await connection.query(
+		deleteProfessionalCertificateQuery,
+		professionalCertificateIdx,
+	);
+	return deleteProfessionalCertificateRow;
+}
+
+//어학 조건 삭제
+async function deleteForeignLanguage(connection, foreignLanguageIdx) {
+	const deleteForeignLanguageQuery = `DELETE FROM ForeignLanguage WHERE foreignLanguageIdx = ?`;
+	const [deleteForeignLanguageRow] = await connection.query(deleteForeignLanguageQuery, foreignLanguageIdx);
+	return deleteForeignLanguageRow;
+}
+
+module.exports = {
+	selectEnterpriseList,
+	selectEnterpriseInfo,
+	selectEnterpriseEssential,
+	selectEnterpriseExtra,
+	selectEnterpriseEtc,
+	checkEnterpriseIdx,
+	insertCalendar,
+	insertEnterprise,
+	insertCommonCertificate,
+	insertProfessionalCertificate,
+	insertForeignLanguage,
+	updateCalendar,
+	updateEnterprise,
+	updateCommonCertificate,
+	updateProfessionalCertificate,
+	updateForeignLanguage,
+	deleteCommonCertificate,
+	deleteProfessionalCertificate,
+	deleteForeignLanguage,
+	deleteCalendar,
+};
diff --git a/_old/src/Calendar/calendarProvider.js b/_old/src/Calendar/calendarProvider.js
new file mode 100644
index 0000000..92c3e37
--- /dev/null
+++ b/_old/src/Calendar/calendarProvider.js
@@ -0,0 +1,152 @@
+"use strict";
+
+const { pool } = require("../../config/database");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const calendarDao = require("./calendarDao");
+const { logger } = require("../../config/winston");
+
+// 월별 기업 조회
+exports.getEnterpriseList = async function (date) {
+	try {
+		let array = [];
+		const connection = await pool.getConnection(async (conn) => conn);
+		const selectEnterpriseList = await calendarDao.selectEnterpriseList(connection);
+		for (let object of selectEnterpriseList) {
+			let examDate = object.examDate.split(", ");
+
+			for (let examDateInfo of examDate) {
+				if (examDateInfo.indexOf(date) != -1) {
+					let endObject = {
+						enterpriseIdx: object.enterpriseIdx,
+						enterpriseName: object.enterpriseName,
+						description: "examDate",
+						date: examDateInfo,
+					};
+					array.push(endObject);
+				}
+			}
+
+			if (object.startDate.indexOf(date) != -1) {
+				let startObject = {
+					enterpriseIdx: object.enterpriseIdx,
+					enterpriseName: object.enterpriseName,
+					description: "startDate",
+					date: object.startDate,
+				};
+				array.push(startObject);
+			}
+
+			if (object.endDate.indexOf(date) != -1) {
+				let endObject = {
+					enterpriseIdx: object.enterpriseIdx,
+					enterpriseName: object.enterpriseName,
+					description: "endDate",
+					date: object.endDate,
+				};
+				array.push(endObject);
+			}
+		}
+		array.sort((a, b) => {
+			if (a.description == "examDate" || b.description == "endDate") return -1;
+			else if (a.description == "endDate" || b.description == "examDate") return 1;
+		});
+
+		connection.release();
+
+		return resultResponse(baseResponse.SUCCESS, array);
+	} catch (error) {
+		console.log(error);
+		logger.error(`App - getEnterpriseList Provider error\n: ${error.message}`);
+		return basickResponse(baseResponse.DB_ERROR);
+	}
+};
+
+// 특정 기업 공고 조회
+exports.getEnterpriseInfo = async function (enterpriseIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		const enterpriseInfo = await calendarDao.selectEnterpriseInfo(connection, enterpriseIdx);
+		//const enterpriseEssential = await calendarDao.selectEnterpriseEssential(connection, enterpriseIdx);
+		//const enterpriseExtra = await calendarDao.selectEnterpriseExtra(connection, enterpriseIdx);
+		//const enterpriseEtc = await calendarDao.selectEnterpriseEtc(connection, enterpriseIdx);
+		let enterpriseObject = {
+			enterpriseIdx: enterpriseInfo[0].enterpriseIdx,
+			enterpriseName: enterpriseInfo[0].enterpriseName,
+			link: enterpriseInfo[0].link,
+			content: enterpriseInfo[0].content,
+			image: enterpriseInfo[0].image,
+			startDate: enterpriseInfo[0].startDate,
+			endDate: enterpriseInfo[0].endDate,
+			examDate: enterpriseInfo[0].examDate.split(", "),
+			education: enterpriseInfo[0].education,
+			enterpriseEssential: enterpriseInfo[0].essentialElement,
+			enterpriseExtra: enterpriseInfo[0].extraElement,
+			enterpriseEtc: enterpriseInfo[0].etcElement,
+		};
+		connection.release();
+		return resultResponse(baseResponse.SUCCESS, enterpriseObject);
+	} catch (error) {
+		console.log(error);
+		logger.error(`App - getEnterpriseInfo Provider error\n: ${error.message}`);
+		return basickResponse(baseResponse.DB_ERROR);
+	}
+};
+
+// 회사 인덱스 체크
+exports.enterpriseIdxCheck = async function (enterpriseIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		const checkEnterpriseIdxResult = await calendarDao.checkEnterpriseIdx(connection, enterpriseIdx);
+		connection.release();
+
+		return checkEnterpriseIdxResult;
+	} catch (error) {
+		console.log(error);
+		logger.error(`App - enterpriseIdxCheck Provider error\n: ${error.message}`);
+		return basickResponse(baseResponse.DB_ERROR);
+	}
+};
+
+// 회사 자격증 조건 조회
+exports.getCommonCertificate = async function (enterpriseIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		const getCommonCertificateResult = await calendarDao.selectCommonCertificate(connection, enterpriseIdx);
+		connection.release();
+		return getCommonCertificateResult;
+	} catch (error) {
+		console.log(error);
+		logger.error(`App - getCommonCertificate Provider error\n: ${error.message}`);
+		return basickResponse(baseResponse.DB_ERROR);
+	}
+};
+
+// 회사 전문 자격증 조건 조회
+exports.getProfessionalCertificate = async function (enterpriseIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		const getProfessionalCertificateResult = await calendarDao.selectProfessionalCertificate(connection, enterpriseIdx);
+		connection.release();
+		return getProfessionalCertificateResult;
+	} catch (error) {
+		console.log(error);
+		logger.error(`App - getProfessionalCertificate Provider error\n: ${error.message}`);
+		return basickResponse(baseResponse.DB_ERROR);
+	}
+};
+
+//회사 어학 조건 조회
+exports.getForeignLanguage = async function (enterpriseIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		const getForeignLanguageResult = await calendarDao.selectForeignLanguage(enterpriseIdx);
+		connection.release();
+		return getForeignLanguageResult;
+	} catch (error) {
+		console.log(error);
+		logger.error(`App - getForeignLanguage Provider error\n: ${error.message}`);
+		return basickResponse(baseResponse.DB_ERROR);
+	}
+};
diff --git a/_old/src/Calendar/calendarRoute.js b/_old/src/Calendar/calendarRoute.js
new file mode 100644
index 0000000..c54ce81
--- /dev/null
+++ b/_old/src/Calendar/calendarRoute.js
@@ -0,0 +1,19 @@
+module.exports = function (app) {
+	const calendar = require("./calendarController");
+	const jwtMiddleware = require("../../middlewares/jwtMiddleware");
+
+	// API No ?. 달력 회사 조회 API
+	app.get("/api/calendars", calendar.getCalendarList);
+
+	// API No ?. 달력 회사 조회 API
+	app.get("/api/calendars/:enterpriseIdx", calendar.getCalendarInfo);
+
+	// API NO ?. 달력 회사 생성 API
+	app.post("/api/calendars/upload", jwtMiddleware, calendar.postCalendar);
+
+	// API NO ?. 달력 회사 수정 API
+	app.patch("/api/calendars/update/:enterpriseIdx", jwtMiddleware, calendar.patchCalendar);
+
+	// API NO ?. 달력 회사 삭제 API
+	app.patch("/api/calendars/delete/:enterpriseIdx", jwtMiddleware, calendar.deleteCalendar);
+};
diff --git a/_old/src/Calendar/calendarService.js b/_old/src/Calendar/calendarService.js
new file mode 100644
index 0000000..521272f
--- /dev/null
+++ b/_old/src/Calendar/calendarService.js
@@ -0,0 +1,494 @@
+const { pool } = require("../../config/database");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const calendarDao = require("./calendarDao");
+const { logger } = require("../../config/winston");
+const calendarProvider = require("./calendarProvider");
+
+exports.createEnterprise = async function (
+	enterpriseInfo,
+	enterpriseValues,
+	enterpriseParam,
+	commonCertificate,
+	professionalCertificate,
+	foreignLanguage,
+) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		const enterpriseIdx = await calendarDao.insertEnterprise(
+			connection,
+			enterpriseInfo,
+			enterpriseValues,
+			enterpriseParam,
+		);
+		if (commonCertificate) {
+			for (let i = 0; i < commonCertificate.length; i++) {
+				let commonCertificateInfo = "(commonCertificateName, category, enterpriseIdx";
+				let commonCertificateValues = "(?, ?, ?";
+				let commonCertificateParams = [
+					commonCertificate[i].commonCertificateName,
+					commonCertificate[i].category,
+					enterpriseIdx.insertId,
+				];
+				if (commonCertificate[i].isEssential) {
+					commonCertificateInfo += ", isEssential";
+					commonCertificateValues += ", ?";
+					commonCertificateParams.push(commonCertificate[i].isEssential);
+				}
+				if (commonCertificate[i].extraPoint) {
+					commonCertificateInfo += ", extraPoint";
+					commonCertificateValues += ", ?";
+					commonCertificateParams.push(commonCertificate[i].extraPoint);
+				}
+				commonCertificateInfo += ")";
+				commonCertificateValues += ")";
+				await calendarDao.insertCommonCertificate(
+					connection,
+					commonCertificateInfo,
+					commonCertificateValues,
+					commonCertificateParams,
+				);
+			}
+		}
+		if (professionalCertificate) {
+			for (let i = 0; i < professionalCertificate.length; i++) {
+				let professionalCertificateInfo = "(professionalCertificateName, isEssential, enterpriseIdx";
+				let professionalCertificateValues = "(?, ?, ?";
+				let professionalCertificateParams = [
+					professionalCertificate[i].professionalCertificateName,
+					professionalCertificate[i].isEssential,
+					enterpriseIdx.insertId,
+				];
+				if (professionalCertificate[i].extraPoint) {
+					professionalCertificateInfo += ", extraPoint";
+					professionalCertificateValues += ", ?";
+					professionalCertificateParams.push(professionalCertificate[i].extraPoint);
+				}
+				professionalCertificateInfo += ")";
+				professionalCertificateValues += ")";
+				await calendarDao.insertProfessionalCertificate(
+					connection,
+					professionalCertificateInfo,
+					professionalCertificateValues,
+					professionalCertificateParams,
+				);
+			}
+		}
+		if (foreignLanguage) {
+			for (let i = 0; i < foreignLanguage.length; i++) {
+				let foreignLanguageInfo = "(passScore, extraPoint, foreignLanguageName, enterpriseIdx";
+				let foreignLanguageValues = "(?, ?, ?, ?";
+				let foreignLanguageParams = [
+					foreignLanguage[i].passScore,
+					"",
+					foreignLanguage[i].foreignLanguageName,
+					enterpriseIdx.insertId,
+				];
+				if (foreignLanguage[i].extraPoint) {
+					foreignLanguageParams[1] = foreignLanguage[i].extraPoint;
+				}
+				if (foreignLanguage[i].isReplace) {
+					foreignLanguageInfo += ", isReplace";
+					foreignLanguageValues += ", ?";
+					foreignLanguageParams.push(foreignLanguage[i].isReplace);
+				}
+				if (foreignLanguage[i].description) {
+					foreignLanguageInfo += ", description";
+					foreignLanguageValues += ", ?";
+					foreignLanguageParams.push(foreignLanguage[i].description);
+				}
+				foreignLanguageInfo += ")";
+				foreignLanguageValues += ")";
+				await calendarDao.insertForeignLanguage(
+					connection,
+					foreignLanguageInfo,
+					foreignLanguageValues,
+					foreignLanguageParams,
+				);
+			}
+		}
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+		console.log(error);
+		logger.error(`App - createEnterprise Service error\n: ${error.message}`);
+		return basickResponse(baseResponse.DB_ERROR);
+	} finally {
+		connection.release();
+	}
+};
+
+exports.updateEnterprise = async function (
+	enterpriseUpdateInfo,
+	enterpriseUpdateParams,
+	enterpriseIdx,
+	commonCertificate,
+	professionalCertificate,
+	foreignLanguage,
+) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		await calendarDao.updateEnterprise(connection, enterpriseUpdateInfo, enterpriseUpdateParams);
+
+		//commonCertificate
+		const commonCertificateResult = await calendarProvider.getCommonCertificate(enterpriseIdx); //commonCertificateIdx 필요.
+		if (commonCertificate) {
+			if (commonCertificate.length == commonCertificateResult.length) {
+				//내용만 변경된 경우
+				for (let i = 0; i < commonCertificate.length; i++) {
+					let commonCertificateUpdateInfo = "commonCertificateName = ?, category = ?";
+					let commonCertificateUpdateParams = [
+						commonCertificate[i].commonCertificateName,
+						commonCertificate[i].category,
+					];
+					if (commonCertificate[i].isEssential) {
+						commonCertificateUpdateInfo += ", isEssential = ?";
+						commonCertificateUpdateParams.push(commonCertificate[i].isEssential);
+					}
+					if (commonCertificate[i].extraPoint) {
+						commonCertificateUpdateInfo += ", extraPoint = ?";
+						commonCertificateUpdateParams.push(commonCertificate[i].extraPoint);
+					}
+					commonCertificateUpdateParams.push(commonCertificate[i].commonCertificateIdx);
+					await calendarDao.updateCommonCertificate(
+						connection,
+						commonCertificateUpdateInfo,
+						commonCertificateUpdateParams,
+					);
+				}
+			} else if (commonCertificate.length > commonCertificateResult.length) {
+				//늘어난 경우
+				if (commonCertificateResult.length !== 0) {
+					for (let i = 0; i < commonCertificateResult; i++) {
+						let commonCertificateUpdateInfo = "commonCertificateName = ?, category = ?";
+						let commonCertificateUpdateParams = [
+							commonCertificate[i].commonCertificateName,
+							commonCertificate[i].category,
+						];
+						if (commonCertificate[i].isEssential) {
+							commonCertificateUpdateInfo += ", isEssential = ?";
+							commonCertificateUpdateParams.push(commonCertificate[i].isEssential);
+						}
+						if (commonCertificate[i].extraPoint) {
+							commonCertificateUpdateInfo += ", extraPoint = ?";
+							commonCertificateUpdateParams.push(commonCertificate[i].extraPoint);
+						}
+						commonCertificateUpdateParams.push(commonCertificate[i].commonCertificateIdx);
+						await calendarDao.updateCommonCertificate(
+							connection,
+							commonCertificateUpdateInfo,
+							commonCertificateUpdateParams,
+						);
+					}
+				}
+				for (let i = commonCertificateResult.length; i < commonCertificate.length; i++) {
+					let commonCertificateInfo = "(commonCertificateName, category, enterpriseIdx";
+					let commonCertificateValues = "(?, ?, ?";
+					let commonCertificateParams = [
+						commonCertificate[i].commonCertificateName,
+						commonCertificate[i].category,
+						enterpriseIdx.insertId,
+					];
+					if (commonCertificate[i].isEssential) {
+						commonCertificateInfo += ", isEssential";
+						commonCertificateValues += ", ?";
+						commonCertificateParams.push(commonCertificate[i].isEssential);
+					}
+					if (commonCertificate[i].extraPoint) {
+						commonCertificateInfo += ", extraPoint";
+						commonCertificateValues += ", ?";
+						commonCertificateParams.push(commonCertificate[i].extraPoint);
+					}
+					commonCertificateInfo += ")";
+					commonCertificateValues += ")";
+					await calendarDao.insertCommonCertificate(
+						connection,
+						commonCertificateInfo,
+						commonCertificateValues,
+						commonCertificateParams,
+					);
+				}
+			} else {
+				//줄어든 경우
+				for (let i = 0; i < commonCertificate.length; i++) {
+					let commonCertificateUpdateInfo = "commonCertificateName = ?, category = ?";
+					let commonCertificateUpdateParams = [
+						commonCertificate[i].commonCertificateName,
+						commonCertificate[i].category,
+					];
+					if (commonCertificate[i].isEssential) {
+						commonCertificateUpdateInfo += ", isEssential = ?";
+						commonCertificateUpdateParams.push(commonCertificate[i].isEssential);
+					}
+					if (commonCertificate[i].extraPoint) {
+						commonCertificateUpdateInfo += ", extraPoint = ?";
+						commonCertificateUpdateParams.push(commonCertificate[i].extraPoint);
+					}
+					commonCertificateUpdateParams.push(commonCertificateResult[i].commonCertificateIdx);
+					await calendarDao.updateCommonCertificate(
+						connection,
+						commonCertificateUpdateInfo,
+						commonCertificateUpdateParams,
+					);
+				}
+				for (let i = commonCertificate.length; i < commonCertificateResult.length; i++) {
+					await calendarDao.deleteCommonCertificate(connection, commonCertificateResult[i].commonCertificateIdx);
+				}
+			}
+		} else {
+			//전달 받은 commonCertificate가 없을 경우
+			if (commonCertificateResult.length != 0) {
+				for (let i = 0; i < commonCertificateResult.length; i++) {
+					await calendarDao.deleteCommonCertificate(connection, commonCertificateResult[i].commonCertificateIdx);
+				}
+			}
+		}
+
+		//professionalCertificate
+		const professionalCertificateResult = await calendarDao.selectProfessionalCertificate(connection, enterpriseIdx);
+		if (professionalCertificate) {
+			if (professionalCertificate.length == professionalCertificateResult.length) {
+				//내용만 변경되었을 경우
+				for (let i = 0; i < professionalCertificate.length; i++) {
+					let professionalInfo = "professionalCertificateName = ?, isEssential = ?";
+					let professionalParams = [
+						professionalCertificate[i].professionalCertificateName,
+						professionalCertificate[i].isEssential,
+					];
+					if (professionalCertificate[i].extraPoint) {
+						professionalInfo += ", extraPoint = ?";
+						professionalParams.push(professionalCertificate[i].extraPoint);
+					}
+					professionalParams.push(professionalCertificate[i].professionalCertificateIdx);
+					await calendarDao.updateProfessionalCertificate(connection, professionalInfo, professionalParams);
+				}
+			} else if (professionalCertificate.length > professionalCertificateResult.length) {
+				//늘어난 경우
+				if (professionalCertificateResult.length != 0) {
+					for (let i = 0; i < professionalCertificateResult.length; i++) {
+						let professionalInfo = "professionalCertificateName = ?, isEssential = ?";
+						let professionalParams = [
+							professionalCertificate[i].professionalCertificateName,
+							professionalCertificate[i].isEssential,
+						];
+						if (professionalCertificate[i].extraPoint) {
+							professionalInfo += ", extraPoint = ?";
+							professionalParams.push(professionalCertificate[i].extraPoint);
+						}
+						professionalParams.push(professionalCertificate[i].professionalCertificateIdx);
+						await calendarDao.updateProfessionalCertificate(connection, professionalInfo, professionalParams);
+					}
+				}
+				for (let i = professionalCertificateResult.length; i < professionalCertificate.length; i++) {
+					let professionalCertificateInfo = "(professionalCertificateName, isEssential, enterpriseIdx";
+					let professionalCertificateValues = "(?, ?, ?";
+					let professionalCertificateParams = [
+						professionalCertificate[i].professionalCertificateName,
+						professionalCertificate[i].isEssential,
+						enterpriseIdx.insertId,
+					];
+					if (professionalCertificate[i].extraPoint) {
+						professionalCertificateInfo += ", extraPoint";
+						professionalCertificateValues += ", ?";
+						professionalCertificateParams.push(professionalCertificate[i].extraPoint);
+					}
+					professionalCertificateInfo += ")";
+					professionalCertificateValues += ")";
+					await calendarDao.insertProfessionalCertificate(
+						connection,
+						professionalCertificateInfo,
+						professionalCertificateValues,
+						professionalCertificateParams,
+					);
+				}
+			} else {
+				//줄어든 경우
+				for (let i = 0; i < professionalCertificate.length; i++) {
+					let professionalInfo = "professionalCertificateName = ?, isEssential = ?";
+					let professionalParams = [
+						professionalCertificate[i].professionalCertificateName,
+						professionalCertificate[i].isEssential,
+					];
+					if (professionalCertificate[i].extraPoint) {
+						professionalInfo += ", extraPoint = ?";
+						professionalParams.push(professionalCertificate[i].extraPoint);
+					}
+					professionalParams.push(professionalCertificateResult[i].professionalCertificateIdx);
+					await calendarDao.updateProfessionalCertificate(connection, professionalInfo, professionalParams);
+				}
+				for (let i = professionalCertificate.length; i < professionalCertificateResult.length; i++) {
+					await calendarDao.deleteProfessionalCertificate(
+						connection,
+						professionalCertificateResult[i].professionalCertificateIdx,
+					);
+				}
+			}
+		} else {
+			if (professionalCertificateResult.length != 0) {
+				for (let i = 0; i < professionalCertificateResult.length; i++) {
+					await calendarDao.deleteProfessionalCertificate(
+						connection,
+						professionalCertificateResult[i].professionalCertificateIdx,
+					);
+				}
+			}
+		}
+
+		//foreignLanguage
+		const foreignLanguageResult = await calendarDao.selectForeignLanguage(connection, enterpriseIdx);
+		if (foreignLanguage) {
+			if (foreignLanguage.length == foreignLanguageResult.length) {
+				for (let i = 0; i < foreignLanguage.length; i++) {
+					let foreignLanguageInfo = "passScore = ?, extraPoint = ?, foreignLanguageName = ?";
+					let foreignLanguageParams = [
+						foreignLanguage[i].passScore,
+						foreignLanguage[i].extraPoint,
+						foreignLanguage[i].foreignLanguageName,
+					];
+					if (foreignLanguage[i].isReplace) {
+						foreignLanguageInfo += ", isReplace = ?";
+						foreignLanguageParams.push(foreignLanguage[i].isReplace);
+					}
+					foreignLanguageParams.push(foreignLanguage[i].foreignLanguageIdx);
+					await updateForeignLanguage(connection, foreignLanguageInfo, foreignLanguageParams);
+				}
+			} else if (foreignLanguage.length > foreignLanguageResult.length) {
+				if (foreignLanguageResult.length != 0) {
+					for (let i = 0; i < foreignLanguageResult.length; i++) {
+						let foreignLanguageInfo = "passScore = ?, extraPoint = ?, foreignLanguageName = ?";
+						let foreignLanguageParams = [
+							foreignLanguage[i].passScore,
+							foreignLanguage[i].extraPoint,
+							foreignLanguage[i].foreignLanguageName,
+						];
+						if (foreignLanguage[i].isReplace) {
+							foreignLanguageInfo += ", isReplace = ?";
+							foreignLanguageParams.push(foreignLanguage[i].isReplace);
+						}
+						foreignLanguageParams.push(foreignLanguage[i].foreignLanguageIdx);
+						await updateForeignLanguage(connection, foreignLanguageInfo, foreignLanguageParams);
+					}
+				}
+				for (let i = foreignLanguageResult.length; i < foreignLanguage.length; i++) {
+					let foreignLanguageInfo = "(passScore, extraPoint, foreignLanguageName, enterpriseIdx";
+					let foreignLanguageValues = "(?, ?, ?, ?";
+					let foreignLanguageParams = [
+						foreignLanguage[i].passScore,
+						"",
+						foreignLanguage[i].foreignLanguageName,
+						enterpriseIdx.insertId,
+					];
+					if (foreignLanguage[i].extraPoint) {
+						foreignLanguageParams[1] = foreignLanguage[i].extraPoint;
+					}
+					if (foreignLanguage[i].isReplace) {
+						foreignLanguageInfo += ", isReplace";
+						foreignLanguageValues += ", ?";
+						foreignLanguageParams.push(foreignLanguage[i].isReplace);
+					}
+					if (foreignLanguage[i].description) {
+						foreignLanguageInfo += ", description";
+						foreignLanguageValues += ", ?";
+						foreignLanguageParams.push(foreignLanguage[i].description);
+					}
+					foreignLanguageInfo += ")";
+					foreignLanguageValues += ")";
+					await calendarDao.insertForeignLanguage(
+						connection,
+						foreignLanguageInfo,
+						foreignLanguageValues,
+						foreignLanguageParams,
+					);
+				}
+			} else {
+				for (let i = 0; i < foreignLanguage.length; i++) {
+					let foreignLanguageInfo = "passScore = ?, extraPoint = ?, foreignLanguageName = ?";
+					let foreignLanguageParams = [
+						foreignLanguage[i].passScore,
+						foreignLanguage[i].extraPoint,
+						foreignLanguage[i].foreignLanguageName,
+					];
+					if (foreignLanguage[i].isReplace) {
+						foreignLanguageInfo += ", isReplace = ?";
+						foreignLanguageParams.push(foreignLanguage[i].isReplace);
+					}
+					foreignLanguageParams.push(foreignLanguageResult[i].foreignLanguageIdx);
+					await updateForeignLanguage(connection, foreignLanguageInfo, foreignLanguageParams);
+				}
+				for (let i = foreignLanguage.length; i < foreignLanguageResult.length; i++) {
+					await calendarDao.deleteForeignLanguage(connection, foreignLanguageResult[i].foreignLanguageIdx);
+				}
+			}
+		} else {
+			if (foreignLanguageResult.length != 0) {
+				for (let i = 0; i < foreignLanguageResult.length; i++) {
+					await calendarDao.deleteForeignLanguage(connection, foreignLanguageResult[i].foreignLanguageIdx);
+				}
+			}
+		}
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+		console.log(error);
+		logger.error(`App - updateEnterprise Service error\n: ${error.message}`);
+		return basickResponse(baseResponse.DB_ERROR);
+	} finally {
+		connection.release();
+	}
+};
+
+// 달력 삭제
+exports.deleteCalendar = async function (enterpriseIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await calendarDao.deleteCalendar(connection, enterpriseIdx);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		console.log(error);
+		logger.error(`App - deleteCalendar Service error\n: ${error.message}`);
+		return basickResponse(baseResponse.DB_ERROR);
+	}
+};
+
+// 달력 생성
+exports.createCalendar = async function (insertExamParams) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await calendarDao.insertCalendar(connection, insertExamParams);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		console.log(error);
+		logger.error(`App - createCalendar Service error\n: ${error.message}`);
+		return basickResponse(baseResponse.DB_ERROR);
+	}
+};
+
+// 달력 수정
+exports.updateCalendar = async function (updateCalendarParams) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await calendarDao.updateCalendar(connection, updateCalendarParams);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		console.log(error);
+		logger.error(`App - updateCalendar Service error\n: ${error.message}`);
+		return basickResponse(baseResponse.DB_ERROR);
+	}
+};
diff --git a/_old/src/ChannelTalk/channelTalkController.js b/_old/src/ChannelTalk/channelTalkController.js
new file mode 100644
index 0000000..db50fa3
--- /dev/null
+++ b/_old/src/ChannelTalk/channelTalkController.js
@@ -0,0 +1,38 @@
+const channelTalkService = require("./channelTalkService");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const userProvider = require("../User/userProvider");
+const asyncHandler = require("../../utils/asyncHandler");
+
+/**
+ * API No.
+ * API Name : 채널톡 유료 상품 구매 사용자 정보 수정
+ * [GET] /channelTalks/userProductInfo
+ **/
+exports.updateChannelTalkUserInfo = asyncHandler(async function (req, res) {
+	// 유료 상품을 구매한 기록이 있는 모든 사용자의 userIdx 가져오기
+	const userIdxList = await userProvider.selectPurchasedUserIdxList();
+
+	// 채널톡 사용자 업데이트 api 호출
+	userIdxList.forEach(async (v) => {
+		let productNameResult = await userProvider.userAllProductsName(v.userIdx);
+		let updateData = { validProduct: productNameResult.validProduct, invalidProduct: productNameResult.invalidProduct };
+		await channelTalkService.updateChannelTalkUserInfo(userIdx, updateData);
+	});
+
+	return res.send(resultResponse(baseResponse.SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 사용자 현재 페이지 정보 채널톡 수정
+ * [GET] /channelTalks/user/:userIdx/currentPage
+ **/
+exports.updateChannelTalkUserCurrentPage = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const currentPage = req.body.currentPage;
+
+	const updateData = { currentLocation: currentPage };
+	await channelTalkService.updateChannelTalkUserInfo(userIdx, updateData);
+	return res.send(resultResponse(baseResponse.SUCCESS));
+});
diff --git a/_old/src/ChannelTalk/channelTalkRoute.js b/_old/src/ChannelTalk/channelTalkRoute.js
new file mode 100644
index 0000000..377428e
--- /dev/null
+++ b/_old/src/ChannelTalk/channelTalkRoute.js
@@ -0,0 +1,10 @@
+module.exports = function (app) {
+	const channelTalk = require("./channelTalkController");
+
+	// API No ?. 채널톡 유료 상품 구매 사용자 전체 정보 수정 API
+	//* client와 연동된 api는 아님, 서버에서 전체 유저에 대한 정보 업데이트할 때 사용함 *//
+	app.post("/api/channelTalks/userProductInfo", channelTalk.updateChannelTalkUserInfo);
+
+	// API No ?. 채널톡 사용자 현재 페이지 정보 수정
+	app.post("/api/channelTalks/user/:userIdx/currentPage", channelTalk.updateChannelTalkUserCurrentPage);
+};
diff --git a/_old/src/ChannelTalk/channelTalkService.js b/_old/src/ChannelTalk/channelTalkService.js
new file mode 100644
index 0000000..fa42d9e
--- /dev/null
+++ b/_old/src/ChannelTalk/channelTalkService.js
@@ -0,0 +1,39 @@
+const userProvider = require("../User/userProvider");
+const userService = require("../User/userService");
+const { pool } = require("../../config/database");
+const secret = require("../../config/secret");
+const baseResponse = require("../../config/baseResponseStatus");
+const { basickResponse } = require("../../config/response");
+
+// 채널톡 사용자 정보 수정
+exports.updateChannelTalkUserInfo = async function (userIdx, updateData) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const method = "PUT";
+		const header = {
+			"x-access-key": secret.CAHNNEL_TALK_ACCESS_KEY,
+			"x-access-secret": secret.CAHNNEL_TALK_SECRET_KEY,
+		};
+
+		// 채널톡 사용자 업데이트 api 호출
+		let uri = `https://api.channel.io/open/v4/users/@${userIdx}`;
+		let data = { profile: updateData };
+		await userService.axiosCall(method, uri, data, header);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (err) {
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// 채널톡 사용자 상품 정보 수정
+exports.updateChannelTalkUserProductInfo = async function (userIdx) {
+	try {
+		let updateData = await userProvider.userAllProductsName(userIdx);
+		await this.updateChannelTalkUserInfo(userIdx, updateData);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (err) {
+		throw err;
+	}
+};
diff --git a/_old/src/Community/communityController.js b/_old/src/Community/communityController.js
new file mode 100644
index 0000000..8c9bf0c
--- /dev/null
+++ b/_old/src/Community/communityController.js
@@ -0,0 +1,136 @@
+const communityProvider = require("./communityProvider");
+const communityService = require("./communityService");
+const adminProvider = require("../Admin/adminProvider");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const asyncHandler = require("../../utils/asyncHandler");
+const errorResponse = require("../../utils/errorResponse");
+const regNumber = /^[0-9]/;
+
+/**
+ * API No.
+ * API Name : 게시글 작성 API
+ * [POST] /communities/board
+ */
+exports.postBoard = asyncHandler(async function (req, res) {
+	const boardTitle = req.body.boardTitle;
+	const boardContent = req.body.boardContent;
+	const userIdx = req.verifiedToken.userIdx;
+
+	userIdx, boardSort, boardTitle, boardContent;
+	const { id: userId } = req.foundUser; //지금 로그인한 회원의 정보로 접근
+	const requestedFields = req.body; //request로 들어온 문자열을 저장
+	const profile_picture = req.file.location; //request로 들어온 파일의 경로를 저장. 파일의 수가 많다면 files[index_num] 혹은 files[field_name]
+	const addInfo = await UserService.updateInfo({ userId, requestedFields, profile_picture });
+	res.status(201).json({
+		message: "information successfully added",
+	});
+});
+
+/**
+ * API No.
+ * API Name : 공지사항 목록 조회 API
+ * [GET] /communities/Notice
+ */
+exports.getNoticeList = asyncHandler(async function (req, res) {
+	let page = req.query.page;
+	let search = req.query.search;
+	if (!regNumber.test(page)) throw new errorResponse(baseResponse.ACCESS_TOKEN_FAIL, 400); // 페이지 번호 오류
+	let where = "";
+	if (!page) page = 0;
+	else page = page - 1;
+	if (search) {
+		where = " and boardTitle like ?";
+		search = "%" + search + "%";
+		search = search.replace(/\"/g, "");
+	}
+
+	const noticeList = await communityProvider.getBoardList(page, where, search);
+	//console.log(noticeList);
+	return res.send(resultResponse(baseResponse.SUCCESS, noticeList));
+});
+
+/**
+ * API No.
+ * API Name : 홈화면 공지사항 목록 조회 API
+ * [GET] /communities/notice/home
+ */
+exports.getHomeNoticeList = asyncHandler(async function (req, res) {
+	const noticeList = await communityProvider.getHomeNotice();
+	return res.send(resultResponse(baseResponse.SUCCESS, noticeList));
+});
+/**
+ * API No.
+ * API Name : 공지사항 상세 조회 API
+ * [GET] /communities/Notice/:boardIdx
+ */
+exports.getNotice = asyncHandler(async function (req, res) {
+	const boardIdx = req.params.boardIdx;
+	await communityProvider.boardCheck(boardIdx);
+
+	const notice = await communityService.viewNotice(boardIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, notice));
+});
+
+/**
+ * API No.
+ * API Name : 공지사항 작성 API
+ * [POST] /communities/Notice
+ */
+exports.postNotice = asyncHandler(async function (req, res) {
+	const { boardTitle, boardContent } = req.body;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	await adminProvider.kingCheck(adminIdx);
+
+	const responseResult = await communityService.insertBoard(adminIdx, "공지사항", boardTitle, boardContent);
+	return res.send(responseResult);
+});
+
+/**
+ * API No.
+ * API Name : 공지사항 삭제 API
+ * [PATCH] /communities/Notice/:boardIdx
+ */
+exports.deleteNotice = asyncHandler(async function (req, res) {
+	const boardIdx = req.params.boardIdx;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	await adminProvider.kingCheck(adminIdx);
+
+	await communityProvider.boardCheck(boardIdx);
+
+	const responseResult = await communityService.deleteBoard(boardIdx);
+	return res.send(responseResult);
+});
+
+/**
+ * API No.
+ * API Name : 공지사항 수정 API
+ * [PATCH] /communities/Notice/:boardIdx
+ */
+exports.updateNotice = asyncHandler(async function (req, res) {
+	const boardIdx = req.params.boardIdx;
+	const { boardTitle, boardContent } = req.body;
+	const adminIdx = req.verifiedToken.userIdx;
+
+	if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 400);
+
+	if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+	await adminProvider.kingCheck(adminIdx);
+
+	await communityProvider.boardCheck(boardIdx);
+
+	const responseResult = await communityService.updateBoard(boardTitle, boardContent, boardIdx);
+	return res.send(responseResult);
+});
diff --git a/_old/src/Community/communityDao.js b/_old/src/Community/communityDao.js
new file mode 100644
index 0000000..ded3ada
--- /dev/null
+++ b/_old/src/Community/communityDao.js
@@ -0,0 +1,170 @@
+// 어드민 체크
+async function checkBoardIdx(connection, boardIdx) {
+	const checkBoardIdxQuery = `
+       select exists(select boardIdx from Board where boardIdx = ? and status = 'N') as exist;
+       `;
+	const [checkBoardIdxRow] = await connection.query(checkBoardIdxQuery, boardIdx);
+	return checkBoardIdxRow;
+}
+async function selectNoticeCount(connection) {
+	const selectNoticeCountQuery = `
+    select count(boardIdx) as 'count'
+    from Board b
+    where boardSort = '공지사항' and b.status = 'N'
+    `;
+	const [selectNoticeCountRow] = await connection.query(selectNoticeCountQuery);
+	return selectNoticeCountRow;
+}
+async function selectSearchNoticeCount(connection, search) {
+	const selectSearchNoticeCountQuery = `
+    select count(boardIdx) as 'count'
+    from Board b
+    where boardSort = '공지사항' and b.status = 'N' and boardTitle like ?
+    `;
+	const [selectSearchNoticeCountRow] = await connection.query(selectSearchNoticeCountQuery, search);
+	return selectSearchNoticeCountRow;
+}
+// 공지사항 목록 조회
+async function selectNoticeList(connection, where, selectNoticeListParams) {
+	const selectNoticeListQuery =
+		`
+    select boardIdx, nickname, boardTitle, readCount, b.createdAt,
+    case when DATEDIFF(CURDATE(), b.createdAt) < 4 then 1 else 0 end as 'isNew'
+    FROM Board b
+    LEFT JOIN User u ON u.userIdx = b.userIdx
+    WHERE boardSort = '공지사항' and b.status = 'N' ` +
+		where +
+		`
+    ORDER BY createdAt desc
+    LIMIT ?, 10
+    `;
+
+	const [selectNoticeListRow] = await connection.query(selectNoticeListQuery, selectNoticeListParams);
+	return selectNoticeListRow;
+}
+// 공지사항 이전글 다음글 조회
+async function selectPrevNextPost(connection, boardIdx) {
+	const selectPrevNextPostQuery = `
+    SELECT
+    boardIdx, boardTitle,
+    case when b.createdAt < tmp.createdAt then 'prev' else 'next' end as 'status'
+ FROM
+    Board b, 
+    (SELECT createdAt
+FROM Board
+where boardIdx = ?) tmp
+ WHERE
+  boardIdx IN (
+    (SELECT boardIdx FROM Board b,
+    (SELECT createdAt
+FROM Board
+where boardIdx = ?  ) tmp
+    WHERE b.createdAt < tmp.createdAt and boardSort = '공지사항' and status = 'N' ORDER BY b.createdAt DESC LIMIT 1),
+    (SELECT boardIdx FROM Board b,
+    (SELECT createdAt
+FROM Board
+where boardIdx = ? ) tmp
+    WHERE b.createdAt > tmp.createdAt and boardSort = '공지사항' and status = 'N' ORDER BY b.createdAt  LIMIT 1)
+   );
+    `;
+	const [selectPrevNextPostRow] = await connection.query(selectPrevNextPostQuery, [boardIdx, boardIdx, boardIdx]);
+	return selectPrevNextPostRow;
+}
+
+// 홈화면 공지사항 조회
+async function selectHomeNoticeList(connection) {
+	const selectHomeNoticeListQuery = `
+    SELECT *
+    FROM (
+    select boardIdx,  @rownum := @rownum + 1 AS noticeNum, nickname, boardTitle, readCount, b.createdAt,
+    case when DATEDIFF(CURDATE(), b.createdAt) < 4 then 1 else 0 end as 'isNew'
+    FROM (SELECT @rownum :=0) AS r, Board b
+    LEFT JOIN User u ON u.userIdx = b.userIdx
+    WHERE boardSort = '공지사항' and b.status = 'N'
+    ORDER BY createdAt ) tmp
+    ORDER BY noticeNum desc
+    Limit 5
+    `;
+	const [selectHomeNoticeListRow] = await connection.query(selectHomeNoticeListQuery);
+	return selectHomeNoticeListRow;
+}
+
+// 공지사항 상세 조회
+async function selectNoticeDetail(connection, boardIdx) {
+	const selectNoticeDetailQuery = `
+    select boardIdx, nickname, boardTitle, boardContent, readCount, date_format(b.createdAt, '%Y.%m.%d') as 'createdAt'
+    FROM  Board b
+    LEFT JOIN User u ON u.userIdx = b.userIdx
+    WHERE boardIdx = ? and b.status = 'N'
+    ORDER BY createdAt desc
+    `;
+	const [selectNoticeDetailRow] = await connection.query(selectNoticeDetailQuery, boardIdx);
+	return selectNoticeDetailRow;
+}
+
+// 게시글 생성
+async function insertBoard(connection, insertBoardParams) {
+	const insertBoardQuery = `
+       INSERT INTO Board(userIdx, boardSort, boardTitle, boardContent)
+       VALUES (?,?,?,?)
+       `;
+	const [insertBoardQueryRow] = await connection.query(insertBoardQuery, insertBoardParams);
+	return insertBoardQueryRow;
+}
+
+// 게시판 이미지 삽입
+async function insertBoardImage(connection, boardImageUrl, boardIdx) {
+	const insertBoardImageQuery = `
+    INSERT INTO BoardImage(boardImageUrl, boardIdx)
+    VALUES (?, ?)
+    `;
+	const insertBoardImageRow = await connection.query(insertBoardImageQuery, [boardImageUrl, boardIdx]);
+	return insertBoardImageRow;
+}
+
+// 게시글 조회수 업데이트
+async function updateReadCount(connection, boardIdx) {
+	const updateReadCountQuery = `
+    UPDATE Board
+    SET readCount = readCount + 1
+    WHERE boardIdx = ?
+    `;
+	const updateReadCountRow = await connection.query(updateReadCountQuery, boardIdx);
+	return updateReadCountRow;
+}
+
+// 게시글 조회수 업데이트
+async function deleteBoardIdx(connection, boardIdx) {
+	const deleteBoardIdxQuery = `
+    UPDATE Board
+    SET status = 'Y'
+    WHERE boardIdx = ?
+    `;
+	const deleteBoardIdxRow = await connection.query(deleteBoardIdxQuery, boardIdx);
+	return deleteBoardIdxRow;
+}
+
+// 공지사항 수정
+async function updateNotice(connection, boardTitle, boardContent, boardIdx) {
+	const updateNoticeQuery = `
+    UPDATE Board
+    SET boardTitle = ?, boardContent = ?
+    WHERE boardIdx = ?
+    `;
+	const updateNoticeRow = await connection.query(updateNoticeQuery, [boardTitle, boardContent, boardIdx]);
+	return updateNoticeRow;
+}
+module.exports = {
+	checkBoardIdx,
+	selectNoticeCount,
+	selectSearchNoticeCount,
+	selectNoticeList,
+	selectNoticeDetail,
+	selectPrevNextPost,
+	selectHomeNoticeList,
+	updateReadCount,
+	updateNotice,
+	insertBoard,
+	insertBoardImage,
+	deleteBoardIdx,
+};
diff --git a/_old/src/Community/communityProvider.js b/_old/src/Community/communityProvider.js
new file mode 100644
index 0000000..ac15ae6
--- /dev/null
+++ b/_old/src/Community/communityProvider.js
@@ -0,0 +1,111 @@
+const { pool } = require("../../config/database");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const communityDao = require("./communityDao");
+const { logger } = require("../../config/winston");
+const errorResponse = require("../../utils/errorResponse");
+
+// 게시글 인덱스 체크
+exports.boardCheck = async function (boardIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const boardIdxCheckResult = await communityDao.checkBoardIdx(connection, boardIdx);
+		if (boardIdxCheckResult[0].exist === 0) throw new errorResponse(baseResponse.BOARD_NOT_FOUND, 404); //문구 수정
+		return boardIdxCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 공지사항 목록 조회
+exports.getBoardList = async function (page, where, search) {
+	let pageNum = page * 10,
+		noticeCount;
+	// today = new Date(),
+	// year = today.getFullYear(),
+	// month = ("0" + (today.getMonth() + 1)).slice(-2),
+	// day = ("0" + today.getDate()).slice(-2),
+	// dateString = year + month + day;
+	//console.log(dateString);
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let selectNoticeListParams;
+		if (search) {
+			selectNoticeListParams = [search, pageNum];
+			noticeCount = await communityDao.selectSearchNoticeCount(connection, search);
+		} else {
+			selectNoticeListParams = pageNum;
+			noticeCount = await communityDao.selectNoticeCount(connection);
+		}
+
+		let noticeList = await communityDao.selectNoticeList(connection, where, selectNoticeListParams);
+		noticeList = noticeList.map((v, i, a) => {
+			v.createdAt =
+				v.createdAt.getFullYear() +
+				"-" +
+				("0" + (v.createdAt.getMonth() + 1)).slice(-2) +
+				"-" +
+				("00" + v.createdAt.getDate()).slice(-2);
+
+			return v;
+		});
+
+		let result = {
+			noticeCount: noticeCount[0].count,
+			noticeList: noticeList,
+		};
+		return result;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 공지사항 상세 조회
+exports.getBoardDetail = async function (boardIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let prevNotice, nextNotice;
+		const noticeResult = await communityDao.selectNoticeDetail(connection, boardIdx);
+		const prevNextPost = await communityDao.selectPrevNextPost(connection, boardIdx);
+		for (let info of prevNextPost) {
+			if (info.status == "prev") prevNotice = info;
+			else nextNotice = info;
+		}
+		return {
+			notice: noticeResult[0],
+			prevNotice: prevNotice,
+			nextNotice: nextNotice,
+		};
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 홈화면 게시글 조회
+exports.getHomeNotice = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let boardResult = await communityDao.selectHomeNoticeList(connection);
+		boardResult = boardResult.map((v, i, a) => {
+			v.createdAt =
+				v.createdAt.getFullYear() +
+				"-" +
+				("0" + (v.createdAt.getMonth() + 1)).slice(-2) +
+				"-" +
+				("00" + v.createdAt.getDate()).slice(-2);
+			return v;
+		});
+		return boardResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
diff --git a/_old/src/Community/communityRoute.js b/_old/src/Community/communityRoute.js
new file mode 100644
index 0000000..c828002
--- /dev/null
+++ b/_old/src/Community/communityRoute.js
@@ -0,0 +1,25 @@
+module.exports = function (app) {
+	const community = require("./communityController");
+	const jwtMiddleware = require("../../middlewares/jwtMiddleware");
+
+	// API No ?. 커뮤니티 게시글 작성 API
+	app.post("/api/communities/board", jwtMiddleware, community.postBoard);
+
+	// API No ?. 공지사항 목록 조회 API
+	app.get("/api/communities/notice", community.getNoticeList);
+
+	// API No ?. 공지사항 상세 조회 API
+	app.get("/api/communities/notice/:boardIdx", community.getNotice);
+
+	// API No ?. 공지사항 작성 API
+	app.post("/api/communities/notice", jwtMiddleware, community.postNotice);
+
+	// API No ?. 공지사항 삭제 API
+	app.patch("/api/communities/delete/notice/:boardIdx", jwtMiddleware, community.deleteNotice);
+
+	// API No ?. 공지사항 수정 API
+	app.patch("/api/communities/update/notice/:boardIdx", jwtMiddleware, community.updateNotice);
+
+	// API No ?. 홈화면 공지사항 목록 조회 API
+	app.get("/api/communities/home/notice", community.getHomeNoticeList);
+};
diff --git a/_old/src/Community/communityService.js b/_old/src/Community/communityService.js
new file mode 100644
index 0000000..070a787
--- /dev/null
+++ b/_old/src/Community/communityService.js
@@ -0,0 +1,78 @@
+const { pool } = require("../../config/database");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const communityDao = require("./communityDao");
+const communityProvider = require("./communityProvider");
+const { logger } = require("../../config/winston");
+const errorResponse = require("../../utils/errorResponse");
+// 게시글 생성
+exports.insertBoard = async function (userIdx, boardSort, boardTitle, boardContent) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const insertBoardParams = [userIdx, boardSort, boardTitle, boardContent];
+		await communityDao.insertBoard(connection, insertBoardParams);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 이미지 업로드
+exports.insertBoardImage = async function () {
+	const { userId, requestedFields, profile_picture } = fields;
+
+	return prisma.users.update({
+		where: {
+			id: Number(userId),
+		},
+		data: {
+			phone_number: requestedFields.phone_number,
+			profile_picture,
+		},
+	});
+};
+
+// 게시글 조회수 업데이트
+exports.viewNotice = async function (boardIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		const boardInfo = await communityProvider.getBoardDetail(boardIdx);
+		await communityDao.updateReadCount(connection, boardIdx);
+		await connection.commit();
+		return boardInfo;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 게시글 삭제
+exports.deleteBoard = async function (boardIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await communityDao.deleteBoardIdx(connection, boardIdx);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 게시글 수정
+exports.updateBoard = async function (boardTitle, boardContent, boardIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await communityDao.updateNotice(connection, boardTitle, boardContent, boardIdx);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
diff --git a/_old/src/Exam/companyExam/companyRouter.js b/_old/src/Exam/companyExam/companyRouter.js
new file mode 100644
index 0000000..ecaf9af
--- /dev/null
+++ b/_old/src/Exam/companyExam/companyRouter.js
@@ -0,0 +1,32 @@
+const express = require("express");
+const router = express.Router();
+const exam = require("../examController");
+const jwtMiddleware = require("../../../middlewares/jwtMiddleware");
+const authCheck = require("../../../middlewares/authCheck");
+const makeAuthLog = require("../../../middlewares/makeAuthLog");
+
+// 공기업 관련 라우터
+// /api/exams/company
+
+// API No ?. 기업 중분류 조회 (기업의 시험에 따른 유형 조회)
+router.get("/companyExamType", exam.getCompanyExamType);
+
+// API No ?. 기업 소분류 조회
+router.get("/companyExamField/:companyExamTypeIdx", exam.getCompanyExamField);
+
+// API No ?. 기업 회사 리스트 조회
+router.get("/companyList", exam.getCompanyList);
+
+// API No ?. 기업 지원 자격 조회 - education, domain
+router.get("/filterInfo", exam.getCompanyExamFilterInfo);
+
+// API No ?. 기업 회차 리스트 조회
+router.get("/examDetailList", exam.getCompanyExamDetailList);
+
+// API No ?. 기업 카테고리 조회 - category
+router.get("/categoryList", exam.getCategoryList);
+
+// API No ?. 기업 시험 문제 전체 조회 API
+router.get("/:examIdx/examDetail/:examDetailIdx/problem", authCheck, makeAuthLog, exam.getCompanyProblemList);
+
+module.exports = router;
diff --git a/_old/src/Exam/examController.js b/_old/src/Exam/examController.js
new file mode 100644
index 0000000..e239106
--- /dev/null
+++ b/_old/src/Exam/examController.js
@@ -0,0 +1,813 @@
+const jwtMiddleware = require("../../middlewares/jwtMiddleware");
+const userProvider = require("../User/userProvider");
+const examProvider = require("./examProvider");
+const examService = require("./examService");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const { logger } = require("../../config/winston");
+const secret_config = require("../../config/secret");
+const regNumber = /^[0-9]/;
+const regKorean = /[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/;
+const regDate = /^\d{4}.(0[1-9]|1[012]).(0[1-9]|[12][0-9]|3[01])$/;
+const constant = require("../../config/constant");
+const adminProvider = require("../Admin/adminProvider");
+const asyncHandler = require("../../utils/asyncHandler");
+const errorResponse = require("../../utils/errorResponse");
+// 디폴트 관리, 설정 관리 리팩토링 필요.
+/**getCompanyList
+ * API No.
+ * API Name : 시험 조회 API - 기창 : 자격증에 국한.
+ * [GET] /exams
+ */
+exports.examList = asyncHandler(async function (req, res) {
+	const jwt = require("jsonwebtoken");
+	let examSort = req.query.examSort; // 기존에는 자격증이라고 출력된다.
+	let examField = req.query.examField; // 전기,화학, 철도, 소방 이런 시험 분류들을 보내는 쿼리.
+	let examSortRef;
+
+	const examSubName = req.query.examSubName;
+	let where, whereInfo, userInfo;
+	const token = req.headers["x-access-token"];
+	if (token && token.length > 4) {
+		const userAuth = new Promise((resolve, reject) => {
+			jwt.verify(token, secret_config.jwtsecret, (err, verifiedToken) => {
+				if (err) {
+					return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+				}
+				resolve(verifiedToken);
+			});
+		});
+		await userAuth.then(async (data) => {
+			const userIdx = data.userIdx;
+			userInfo = await userProvider.userInfo(userIdx);
+		});
+	}
+	if (examSort) {
+		if (!regKorean.test(examSort)) return res.send(basickResponse(baseResponse.EXAMSORT_ERROR_TYPE));
+		if (examSort == constant.DEFAULT_EXAMSORT_ALL) {
+			const examAllResult = await examProvider.getExamAll();
+			return res.send(examAllResult);
+		}
+		examSortRef = await examProvider.getExamSortRef(examSort);
+		whereInfo = examSubName ? examSubName : examSortRef;
+	} else {
+		whereInfo = constant.DEFAULT_CERTIFICATE_NAME;
+	}
+
+	if (userInfo && userInfo.length > 0 && userInfo[0].status != "Y") {
+		where = "and accessLevel <= " + userInfo[0].accessLevel;
+	} else where = "and accessLevel = 0";
+
+	if (!examField) examField = "전기";
+
+	const examResult = await examProvider.getExam(where, whereInfo, examField);
+
+	let result = [];
+	await Promise.all(
+		examResult.result.map(async (x) => {
+			const examIdx = x.examIdx;
+			const { ExamDetailPublic } = await examProvider.getExamDetailPublic(examIdx);
+			if (ExamDetailPublic) {
+				result.push(x);
+			}
+		}),
+	);
+	examResult.result = result.sort((a, b) => a.prio - b.prio);
+	return res.send(examResult);
+});
+
+//자격증의 시험 분야에 대한 종류를 가져오기
+exports.examFieldList = asyncHandler(async function (req, res) {
+	const examSort = req.query.examSort; // 기존에는 자격증이라고 출력된다.
+	const examSortRef = await examProvider.getExamSortRef(examSort);
+	const examFieldListResult = await examProvider.getExamFieldList(examSortRef);
+	return res.send(examFieldListResult);
+});
+// /**
+//  * API No.
+//  * API Name : 자격증 시험 조회 API
+//  * [GET] /exams
+//  */
+// exports.examList = async function (req, res) {
+// 	try {
+// 		const jwt = require("jsonwebtoken");
+// 		let examSort = req.query.examSort;
+// 		const examSubName = req.query.examSubName;
+// 		let where, whereInfo, userInfo;
+// 		const token = req.headers["x-access-token"];
+// 		if (token && token.length > 4) {
+// 			const userAuth = new Promise((resolve, reject) => {
+// 				jwt.verify(token, secret_config.jwtsecret, (err, verifiedToken) => {
+// 					if (err) reject(err);
+// 					resolve(verifiedToken);
+// 				});
+// 			});
+// 			await userAuth.then(async (data) => {
+// 				const userIdx = data.userIdx;
+// 				userInfo = await userProvider.userInfo(userIdx);
+// 			});
+
+// 			/*const p = new Promise((resolve, reject) => {
+
+// 			jwt.verify(token, secret_config.jwtsecret, (err, verifiedToken) => {
+// 				if (err) reject(err);
+// 				resolve(verifiedToken);
+// 			});
+// 		});
+// 		const onError = (error) => {
+// 			return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+// 		};
+// 		// process the promise
+// 		p.then((verifiedToken) => {
+// 			verifiedToken;
+// 			userInfo = userProvider.userInfo(verifiedToken.userIdx);
+// 		}).catch(onError);*/
+// 		}
+// 		if (examSort) {
+// 			if (!regKorean.test(examSort)) return res.send(basickResponse(baseResponse.EXAMSORT_ERROR_TYPE));
+// 			if (examSort == "전체") {
+// 				const examAllResult = await examProvider.getExamAll();
+// 				return res.send(examAllResult);
+// 			}
+// 			where = `WHERE examSortRef =   ? `;
+// 			whereInfo = examSort;
+// 			if (examSubName) {
+// 				where = `WHERE e.examSort =  ? `;
+// 				whereInfo = examSubName;
+// 			}
+// 		} else {
+// 			where = "WHERE examSortRef =  ?";
+// 			whereInfo = "자격증";
+// 		}
+
+// 		if (userInfo && userInfo.length > 0 && userInfo[0].status != "Y") {
+// 			where += "and accessLevel <= " + userInfo[0].accessLevel;
+// 		} else where += "and accessLevel = 0";
+
+// 		const examResult = await examProvider.getExam(where, whereInfo);
+
+// 		return res.send(examResult);
+// 	} catch (error) {
+// 		return res.send(basickResponse(baseResponse.TOKEN_VERIFICATION_FAILURE));
+// 	}
+// };
+
+/**
+ * API No
+ * API Name : 특정 시험 회차 조회 API
+ * [GET] /exams/:examIdx/examDetail
+ */
+exports.examDetailList = asyncHandler(async function (req, res) {
+	const examIdx = req.params.examIdx;
+
+	if (!examIdx) throw new errorResponse(baseResponse.EXAM_EMPTY, 400);
+
+	if (!regNumber.test(examIdx)) throw new errorResponse(baseResponse.EXAM_ERROR_TYPE, 400);
+
+	await examProvider.examCheck(examIdx);
+
+	const examDetailResult = await examProvider.getExamDetail(examIdx);
+
+	return res.send(resultResponse(baseResponse.SUCCESS, examDetailResult));
+});
+
+/**
+ * API No.
+ * API Name : 시험 정보 조회 API
+ * [GET] /exams/:examIdx/examInfo
+ */
+exports.examInfo = asyncHandler(async function (req, res) {
+	const examIdx = req.params.examIdx;
+	if (!examIdx) throw new errorResponse(baseResponse.EXAM_EMPTY, 400);
+
+	if (!regNumber.test(examIdx)) throw new errorResponse(baseResponse.EXAM_ERROR_TYPE, 400);
+
+	await examProvider.examCheck(examIdx);
+
+	const examInfoResult = await examProvider.getExamInfo(examIdx);
+
+	return res.send(examInfoResult);
+});
+
+/**
+ * API No.
+ * API Name : 과목 조회 API
+ * [GET] /exams/:examIdx/examDetail/:examDetailIdx/examSubject/?subjectiveLabel
+ */
+exports.examSubjectInfo = asyncHandler(async function (req, res) {
+	const examIdx = req.params.examIdx;
+	const examDetailIdx = req.params.examDetailIdx;
+	const subjectiveLabel = req.query.subjectiveLabel;
+	// const subjectiveLabel = req.params.subjectiveLabel;
+
+	if (!examIdx) throw new errorResponse(baseResponse.EXAM_EMPTY, 400);
+
+	if (!regNumber.test(examIdx)) throw new errorResponse(baseResponse.EXAM_ERROR_TYPE, 400);
+
+	await examProvider.examCheck(examIdx);
+
+	if (examDetailIdx != 0) {
+		if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+		if (!regNumber.test(examDetailIdx)) throw new errorResponse(baseResponse.EXAMDETAIL_ERROR_TYPE, 400);
+
+		await examProvider.examDetailCheck(examDetailIdx);
+
+		await examProvider.examMatchCheck(examDetailIdx, examIdx);
+	}
+
+	let examSubjectInfoResult = await examProvider.getExamSubjectInfo(examDetailIdx, examIdx, subjectiveLabel);
+
+	return res.send(examSubjectInfoResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 한 문제 조회 API
+ * [GET] /exams/:multipleProblemIdx
+ */
+exports.problem = asyncHandler(async function (req, res) {
+	const problemIdx = req.params.problemIdx;
+	const subjectiveLabel = req.params.subjectiveLabel;
+	if (!problemIdx) throw new errorResponse(baseResponse.PROBLEM_EMPTY, 400);
+
+	subjectiveLabel == "1"
+		? await examProvider.subjectiveProblemCheck(problemIdx)
+		: await examProvider.multipleProblemCheck(problemIdx);
+
+	const problemResult = await examProvider.getProblem(problemIdx, subjectiveLabel);
+
+	return res.send(problemResult);
+});
+
+/**
+ * API No.
+ * API Name : CBT 시험 문제 조회 API
+ * [POST] /exams/:examIdx/examDetail/:examDetailIdx/problem/type/:type
+ */
+exports.problemList = asyncHandler(async function (req, res) {
+	const examIdx = req.params.examIdx;
+	const examDetailIdx = req.params.examDetailIdx;
+	const { subjectList } = req.body;
+	const isAuth = req.isAuth;
+
+	// if (!isAuth) return res.send(resultResponse(baseResponse.USERAUTH_NOT_EXIST, isAuth));
+
+	if (!examIdx) throw new errorResponse(baseResponse.EXAM_EMPTY, 400);
+
+	if (!regNumber.test(examIdx)) throw new errorResponse(baseResponse.EXAM_ERROR_TYPE, 400);
+
+	if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+
+	if (!regNumber.test(examDetailIdx)) throw new errorResponse(baseResponse.EXAMDETAIL_ERROR_TYPE, 400);
+
+	await examProvider.examCheck(examIdx);
+
+	await examProvider.examDetailCheck(examDetailIdx);
+
+	await examProvider.examMatchCheck(examDetailIdx, examIdx);
+
+	if (!subjectList) throw new errorResponse(baseResponse.SUBJECT_EMPTY, 400);
+	if (subjectList.length != 0) {
+		const subjectMatchCheck = await examProvider.subjectMatchCheck(examIdx, subjectList);
+		//해당 과목이 그 시험에 존재하는지 검토
+		if (subjectMatchCheck[0].count !== subjectList.length)
+			return res.send(basickResponse(baseResponse.SUBJECTMULTI_NOT_MATCH));
+	}
+	// if (token && token.length > 4) {
+	// 	const promiseAuth = new Promise((resolve, reject) => {
+	// 		jwt.verify(token, secret_config.jwtsecret, (error, verifiedToken) => {
+	// 			if (error) reject(error);
+	// 			resolve(verifiedToken);
+	// 		});
+	// 	});
+	// 	await promiseAuth.then(async (verifiedToken) => {
+	// 		adminIdxCheckResult = await adminProvider.adminCheck(verifiedToken.userIdx);
+	// 	});
+	// 시험 문제 구매 검사 (결제 구현후 수정)
+	// await promiseAuth.then(async (data) => {
+	// 	userIdx = data.userIdx;
+	// 	const userAuthCheckResult = await examProvider.userExamDetailAuthCheck(userIdx, examDetailIdx);
+	// 	isAuth = userAuthCheckResult[0].exist;
+	// });
+
+	// const today = new Date();
+	// const nowDate =
+	// 	today.getFullYear() + ("0" + (today.getMonth() + 1)).slice(-2) + ("0" + today.getDate()).slice(-2);
+	//}
+
+	// if (examDetailCheckResult[0].isPublic !== 1 && adminIdxCheckResult[0].exist === 0)
+	// 	return res.send(basickResponse(baseResponse.USERAUTH_NOT_EXIST));
+	//erase param isAuth
+
+	const problemListResult = await examProvider.getProblemList(examIdx, examDetailIdx, subjectList, isAuth);
+	return res.send(problemListResult);
+});
+
+/**
+ * API No.
+ * API Name : 시험 제출 API
+ * [POST] /exams/:examIdx/examDetail/:examDetailIdx/problem/submission
+ */
+exports.submissionProblem = asyncHandler(async function (req, res) {
+	const examIdx = req.params.examIdx;
+	const examDetailIdx = req.params.examDetailIdx;
+	const userAnswer = req.body.answerList;
+	const userIdx = req.verifiedToken.userIdx;
+	const PROBLEMSCORE = 5;
+	const SUBJECT_PASSSCORE = 40;
+	const SUBJECT_STANDARD_COUNT = 20;
+
+	let isCorrect = [],
+		multipleProblemIdx = [],
+		subjectList = [],
+		totalCount = 0,
+		wrongMultipleProblemList = [],
+		score = 0,
+		isPass = 1;
+	// Validation Check (Request Error)
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+
+	if (!examIdx) throw new errorResponse(baseResponse.EXAM_EMPTY, 400);
+	if (!regNumber.test(examIdx)) throw new errorResponse(baseResponse.EXAM_ERROR_TYPE, 400);
+
+	if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+	if (!regNumber.test(examDetailIdx)) throw new errorResponse(baseResponse.EXAMDETAIL_ERROR_TYPE, 400);
+
+	if (!userAnswer) throw new errorResponse(baseResponse.ANSWERLIST_EMPTY, 400);
+
+	const examDivision = await examProvider.getExamDivision(examIdx, "L");
+	const useSubject = examDivision[0].examSortName == "자격증" ? 1 : 0;
+	const problemInfo = await examProvider.getAnswer(examDetailIdx, useSubject);
+	if (problemInfo.length != userAnswer.length) return res.send(basickResponse(baseResponse.ARRAY_LENGTH_ERROR));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	const examCheckResult = await examProvider.examCheck(examIdx);
+	const EXAM_PASSSCORE = examCheckResult[0].passScore;
+
+	await examProvider.examDetailCheck(examDetailIdx);
+
+	await examProvider.examMatchCheck(examDetailIdx, examIdx);
+
+	//Validation End
+	for (let i = 0; i < userAnswer.length; i++) {
+		let index = 0;
+		if (useSubject == 1) {
+			const firstSubjectIndex = subjectList.findIndex((obj) => obj.examSubjectIdx == problemInfo[i].examSubjectIdx);
+
+			if (-1 == firstSubjectIndex)
+				subjectList.push({
+					examSubjectIdx: problemInfo[i].examSubjectIdx,
+					score: 0,
+					subjectProblemCount: 0,
+				});
+
+			index = subjectList.findIndex((obj) => obj.examSubjectIdx == problemInfo[i].examSubjectIdx);
+			subjectList[index].subjectProblemCount += 1;
+		}
+		multipleProblemIdx.push(problemInfo[i].multipleProblemIdx);
+		// isPass 과목별 점수
+		if (!regNumber.test(userAnswer[i])) throw new errorResponse(baseResponse.ANSWERLIST_ERROR_TYPE, 400);
+		if (!regNumber.test(multipleProblemIdx[i])) throw new errorResponse(baseResponse.PROBLEM_ERROR_TYPE, 400);
+		totalCount++;
+		if (
+			problemInfo[i].questionIdx === userAnswer[i]
+			// || problemInfo[i].isDelete == 1 삭제된것은 채점하지 않는 게 맞다고 생각하여서.
+		) {
+			isCorrect.push(1);
+			//score += problemInfo[i].problemScore;
+			if (useSubject == 1) {
+				// 자격증일 때 ( 과목이 존재하는 시험일 경우에 )
+				subjectList[index].score += problemInfo[i].problemScore;
+			} else {
+				// 공기업일 때 ( 기존 공기업은 문제 채점 시 점수가 아닌 갯수로 한다. )
+			}
+		} else {
+			isCorrect.push(0);
+			wrongMultipleProblemList.push(multipleProblemIdx[i]);
+		}
+	}
+	// console.log(wrongMultipleProblemList);/
+	const correctAnswers = isCorrect.filter((element) => element == 1);
+	// console.log(correctAnswers);
+	if (useSubject == 1) {
+		subjectList.map((v, i, a) => {
+			// console.log(v);
+			// v.score =
+			// 	v.subjectProblemCount < SUBJECT_STANDARD_COUNT
+			// 		? v.score + (SUBJECT_STANDARD_COUNT - v.subjectProblemCount) * PROBLEMSCORE
+			// 		: v.score;
+
+			isPass = v.score < SUBJECT_PASSSCORE ? 0 : isPass;
+			score += v.score;
+		});
+
+		score = score / examCheckResult[0].subjectCount;
+		isPass = score < EXAM_PASSSCORE ? 0 : isPass;
+	} else score = correctAnswers.length;
+
+	const createExamRecordResult = await examService.createExamRecord(
+		userIdx,
+		examDetailIdx,
+		userAnswer,
+		isCorrect,
+		multipleProblemIdx,
+		score,
+		useSubject,
+		correctAnswers,
+		isPass,
+	);
+
+	let userNicknameResult = await userProvider.selectUserNickname(userIdx);
+
+	createExamRecordResult.result["wrongMultipleProblemList"] = wrongMultipleProblemList;
+	createExamRecordResult.result["userInfo"] = userNicknameResult;
+	// const result = { createExamRecordResult, wrongMultipleProblemList };
+
+	return res.send(createExamRecordResult);
+});
+
+/**
+ * API No.
+ * API Name : 오답노트 담기 API
+ * [POST] /exams/reviewNote/:multipleProblemIdx
+ */
+exports.postReviewNote = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const multipleProblemIdx = req.params.multipleProblemIdx;
+	if (!multipleProblemIdx) throw new errorResponse(baseResponse.PROBLEM_EMPTY, 400);
+
+	//if (!regNumber.test(multipleProblemIdx)) throw new errorResponse(baseResponse.PROBLEM_ERROR_TYPE, 400);
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USERNAME_ERROR_TYPE, 400);
+
+	const multipleProblemIdxCheckResult = await examProvider.multipleProblemCheck(multipleProblemIdx);
+
+	await userProvider.userIdxCheck(userIdx);
+
+	const examDetailIdx = multipleProblemIdxCheckResult[0].examDetailIdx;
+	await examProvider.examDetailCheck(examDetailIdx);
+
+	// const userAuthCheckResult = await examProvider.userExamDetailAuthCheck(userIdx, examDetailIdx);
+	// if (userAuthCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.USERAUTH_NOT_EXIST));
+	const result = await examService.createReviewNote(userIdx, multipleProblemIdx);
+	return res.send(result);
+});
+
+/**
+ * API No.
+ * API Name : 오답노트 한번에 담기 API
+ * [POST] /exams/reviewNote
+ * author : 김기창
+ */
+exports.postReviewNoteList = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const multipleProblemList = req.body.multipleProblemList;
+
+	let result;
+
+	if (!multipleProblemList) throw new errorResponse(baseResponse.PROBLEM_EMPTY, 400);
+
+	//if (!regNumber.test(multipleProblemIdx)) throw new errorResponse(baseResponse.PROBLEM_ERROR_TYPE, 400);
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USERNAME_ERROR_TYPE, 400);
+
+	multipleProblemList.map(async (value, idx) => {
+		const multipleProblemIdxCheckResult = await examProvider.multipleProblemCheck(value);
+		const examDetailIdx = multipleProblemIdxCheckResult[0].examDetailIdx;
+		await examProvider.examDetailCheck(examDetailIdx);
+	});
+
+	// const multipleProblemIdxCheckResult = await examProvider.multipleProblemCheck(multipleProblemIdx);
+	// if (multipleProblemIdxCheckResult.length === 0) return res.send(basickResponse(baseResponse.PROBLEM_NOT_EXIST));
+	// if (multipleProblemIdxCheckResult[0].status === "Y") return res.send(basickResponse(baseResponse.ISDELETED));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	// const examDetailIdx = multipleProblemIdxCheckResult[0].examDetailIdx;
+	// const examDetailCheckResult = await examProvider.examDetailCheck(examDetailIdx);
+	// if (examDetailCheckResult.length === 0) return res.send(basickResponse(baseResponse.ANSWERLIST_NOT_EXIST));
+	// if (examDetailCheckResult[0].status === "Y") return res.send(basickResponse(baseResponse.ISDELETED));
+
+	// const userAuthCheckResult = await examProvider.userExamDetailAuthCheck(userIdx, examDetailIdx);
+	// if (userAuthCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.USERAUTH_NOT_EXIST));
+	result = await examService.createTotalReviewNote(userIdx, multipleProblemList);
+
+	return res.send(result);
+});
+
+/**
+ * API No.
+ * API Name : 주관식 오답노트 담기 API
+ * [POST] /exams/practical/reviewNote/:subjectiveProblemIdx
+ */
+exports.postSubjectiveReviewNote = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const subjectiveProblemIdx = req.params.subjectiveProblemIdx;
+	if (!subjectiveProblemIdx) throw new errorResponse(baseResponse.PROBLEM_EMPTY, 400);
+
+	//if (!regNumber.test(subjectiveProblemIdx)) throw new errorResponse(baseResponse.PROBLEM_ERROR_TYPE, 400);
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USERNAME_ERROR_TYPE, 400);
+
+	const subjectiveProblemIdxCheckResult = await examProvider.subjectiveProblemCheck(subjectiveProblemIdx);
+
+	await userProvider.userIdxCheck(userIdx);
+
+	const examDetailIdx = subjectiveProblemIdxCheckResult[0].examDetailIdx;
+	await examProvider.examDetailCheck(examDetailIdx);
+
+	// const userAuthCheckResult = await examProvider.userExamDetailAuthCheck(userIdx, examDetailIdx);
+	// if (userAuthCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.USERAUTH_NOT_EXIST));
+	const result = await examService.createSubjectiveReviewNote(userIdx, subjectiveProblemIdx);
+	return res.send(result);
+});
+
+/**
+ * API No.
+ * API Name : 문제 에러 신고 API
+ * [POST] /exams/problemError
+ */
+exports.postProblemError = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const { multipleProblemIdx, title, content, type, subjectiveLabel } = req.body;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+
+	if (!multipleProblemIdx) throw new errorResponse(baseResponse.PROBLEM_EMPTY, 400);
+
+	if (!title) throw new errorResponse(baseResponse.TITLE_EMPTY, 400);
+
+	if (!content) throw new errorResponse(baseResponse.CONTENT_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USERNAME_ERROR_TYPE, 400);
+
+	await examProvider.multipleProblemCheck(multipleProblemIdx);
+
+	// if (!boardTitle) return res.send(basickResponse(baseResponse.BOARDTITLE_EMPTY));
+
+	// if (!boardContent) return res.send(basickResponse(baseResponse.BOARDCONTENT_EMPTY));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	const createResult = await examService.createErrorReport(
+		userIdx,
+		multipleProblemIdx,
+		title,
+		content,
+		type,
+		subjectiveLabel,
+	);
+
+	return res.send(createResult);
+});
+
+exports.getExamUrl = asyncHandler(async function (req, res) {
+	const examIdx = req.params.examIdx;
+	if (!examIdx) throw new errorResponse(baseResponse.EXAM_EMPTY, 400);
+
+	if (!regNumber.test(examIdx)) throw new errorResponse(baseResponse.EXAM_ERROR_TYPE, 400);
+
+	await examProvider.examCheck(examIdx);
+
+	const getResult = await examProvider.getExamUrl(examIdx);
+	return res.send(getResult);
+});
+
+/**
+ * API No.
+ * API Name : 랜덤 문제 조회 API
+ * [POST] /exams/:examIdx/randomExam
+ * @param {int} type 시험 풀이 유형 - 0 : 한 문제로 풀기, 1 : CBT 문제
+ */
+exports.getRandomExam = asyncHandler(async function (req, res) {
+	const examIdx = req.params.examIdx;
+	const { subjectList, startDate, endDate, limit, problemCountList } = req.body;
+	// problemCountList 가 있다는 것은 각 과목별로 문제수를 몇 개 넣을 것인지에 대한 리스트
+	const isAuth = req.isAuth;
+	if (!examIdx) throw new errorResponse(baseResponse.EXAM_EMPTY, 400);
+	if (!limit) throw new errorResponse(baseResponse.ESSENTIAL_EMPTY, 400);
+	if (!startDate) throw new errorResponse(baseResponse.ESSENTIAL_EMPTY, 400);
+	if (!endDate) throw new errorResponse(baseResponse.ESSENTIAL_EMPTY, 400);
+	if (!Array.isArray(subjectList) || subjectList.length == 0)
+		throw new errorResponse(baseResponse.ESSENTIAL_EMPTY, 400);
+
+	if (!regNumber.test(examIdx)) throw new errorResponse(baseResponse.EXAM_ERROR_TYPE, 400);
+	if (!regNumber.test(limit)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+
+	if (!regDate.test(endDate)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+
+	if (!regDate.test(startDate)) throw new errorResponse(baseResponse.DATA_ERROR_TYPE, 400);
+
+	await examProvider.examCheck(examIdx);
+
+	const getResult = await examProvider.getRandomExam(examIdx, subjectList, startDate, endDate, limit, isAuth);
+
+	// 권한이 없는 상태에서 문제는 볼수 있는 count가 Limit보다 적다면 다음과 같은 response
+	if (!getResult) {
+		if (!isAuth) {
+			throw new errorResponse(baseResponse.PROBLEM_COUNT_LACK_AS_AUTH, 400);
+		}
+		throw new errorResponse(baseResponse.PROBLEM_COUNT_LACK, 400);
+	}
+	getResult.result["isAuth"] = isAuth;
+	getResult.result.examInfo["publicLevel"] = 0;
+
+	return res.send(getResult);
+});
+
+/**
+ * API No ?. 기업 회사 시험의 type 조회
+ * @param {string} companyType 기업의 종류 : 대기업 & 공기업
+ * */
+exports.getCompanyExamType = asyncHandler(async function (req, res) {
+	const { companyType } = req.query;
+
+	const getCompanyExamTypeResult = await examProvider.getCompanyExamType(
+		companyType, // 공기업인지 대기업인지.
+	);
+
+	return res.send(getCompanyExamTypeResult);
+});
+
+/**
+ * API No ?. 기업 회사 시험의 examType에 따른 시험 종류 조회
+ * @param {number} companyExamTypeIdx 어떤 유형의 시험인지를 가리키는 idx
+ * ex: 공기업의 전공, NCS 대기업의 직무 적성 검사
+ * getCompanyDivision => getCompanyExamField로 전환하고자함.
+ * 무료회차에 대해서는 이후에 생각.
+ *
+ */
+
+exports.getCompanyExamField = asyncHandler(async function (req, res) {
+	const { companyExamTypeIdx } = req.params;
+	if (!companyExamTypeIdx) throw new errorResponse(baseResponse.COMPANY_TYPE_EMPTY, 400);
+
+	const getCompanyExamFieldResult = await examProvider.getCompanyExamField(
+		companyExamTypeIdx, // 공기업인지 대기업인지.
+	);
+
+	return res.send(getCompanyExamFieldResult);
+});
+
+// API No ?. 기업 회사 구분 조회
+// 기업 => 공기업 & 대기업
+// 공기업 => 전공 & NCS
+// 대기업 => 직무 적성 검사
+
+exports.getCompanyDivision = asyncHandler(async function (req, res) {
+	const { examSort_M, companySortLabel } = req.query;
+
+	const getResult = await examProvider.getCompanyDivision(
+		companySortLabel ? "대기업" : "공기업", // 공기업
+		constant.DEFAULT_DIVISION_LEVEL, // M
+	);
+	return res.send(getResult);
+});
+
+/**
+ * API No ?. 기업 회사 리스트 조회
+ * @param {string} companyType 어떤 유형의 회사인지 ex) 대기업, 공기업
+ * @param {number} companyExamType 기업 시험의 중분류에 대해서 어떤 유형의 시험인지. ex) 전공, NCS, 직무 적성 검사
+ * @param  {number} companyExamField 시험에 대한 소분류. 가장 말단의 idx으로 이는 입력할 수도 있고 안할 수도 있음
+ */
+exports.getCompanyList = asyncHandler(async function (req, res) {
+	let { companyType, companyExamType, companyExamField } = req.query;
+	let examFieldList = [];
+	let checkType = []; // 대분류 - 중분류 확인
+	let checkField = []; // 중분류 - 소분류 확인
+
+	if (!companyType) throw new errorResponse(baseResponse.COMPANY_TYPE_EMPTY, 400); // ex. 공기업
+	if (!companyExamType) throw new errorResponse(baseResponse.COMPANY_EXAMTYPE_EMPTY, 400); // ex. 139 (전공)
+
+	const companyTypeResult = await examProvider.getCompanyExamType(companyType);
+
+	companyTypeResult["result"].map((v) => checkType.push(v.examSortIdx));
+	if (!checkType.includes(Number(companyExamType)))
+		throw new errorResponse(baseResponse.COMPANY_EXAMTYPE_NOT_MATCH, 400);
+
+	if (companyExamField) {
+		const companyExamFieldResult = await examProvider.getCompanyExamField(companyExamType);
+		companyExamFieldResult["result"].map((v) => checkField.push(v.examSortIdx));
+		if (!checkField.includes(Number(companyExamField)))
+			throw new errorResponse(baseResponse.COMPANY_EXAMFIELD_NOT_MATCH, 400);
+		examFieldList = companyExamField;
+	} else {
+		const companyExamTypeResult = await examProvider.getCompanyExamField(companyExamType);
+		companyExamTypeResult["result"].map((v) => {
+			examFieldList.push(v.examSortIdx);
+		});
+	}
+	const getResult = await examProvider.getCompanyList(examFieldList);
+	return res.send(getResult);
+});
+
+/**
+ * @param {number} companyExamField 특정 기업의 idx를 의미한다.
+ * 특정 기업에 해당하는 시험이 어떤 자격 조건들을 가지고 있는지에 대해서 나타낸다.
+ */
+exports.getCompanyExamFilterInfo = asyncHandler(async function (req, res) {
+	let companyExamField = req.query.companyExamField;
+	if (!companyExamField) throw new errorResponse(baseResponse.COMPANY_EXAMFIELD_EMPTY, 400);
+
+	let checkType = await examProvider.examSortTypeCheck(companyExamField);
+	if (checkType[0].examSortType != "S") throw new errorResponse(baseResponse.EXAMSORT_ERROR_TYPE, 400);
+
+	const getResult = await examProvider.getCompanyExamFilterInfo(companyExamField);
+	return res.send(getResult);
+});
+
+/**  API No ?. 공기업 회차 리스트 조회
+ * @param  {number} companyExamField 시험에 대한 소분류.
+ * @param {number} companyName 기업 시험 이름
+ * @param {string} category 기업 시험에 대한 카테고리 분류 (복원, 출제예상, 핵심적중 / 기출변형, 기출복원, 핵심유형)
+ * ex) 전기직 > 한국수자원공사, 전기직 > 한국전력공사 > 복원
+ * */
+exports.getCompanyExamDetailList = asyncHandler(async function (req, res) {
+	let { companyExamField, companyName, category } = req.query;
+	if (!companyExamField) throw new errorResponse(baseResponse.COMPANY_EXAMFIELD_EMPTY, 400);
+	if (!companyName) throw new errorResponse(baseResponse.COMPANY_NAME_EMPTY, 400);
+
+	const checkExamField = await examProvider.getFieldMatchName(companyExamField, companyName);
+	if (checkExamField["result"][0].exist == 0) throw new errorResponse(baseResponse.COMPANY_NAME_NOT_MATCH, 400);
+
+	let isCategory = "";
+	if (category) isCategory = `and ce.category = "${category}" `; // category 존재하면 쿼리 추가
+
+	let queryParams = [companyExamField, companyName];
+	const getResult = await examProvider.getCompanyExamDetailList(queryParams, isCategory);
+	return res.send(getResult);
+});
+
+// API No ?. 공기업 무료 회차 리스트 조회
+exports.getCompanyExamDetailListFree = asyncHandler(async function (req, res) {
+	let { companyIdx: examSortIdx, education, domain } = req.query;
+	if (!examSortIdx) throw new errorResponse(baseResponse.COMPANYIDX_EMPTY, 400);
+	let queryParams = [examSortIdx];
+	if (education) queryParams.push(education);
+	if (domain) {
+		queryParams.push(domain);
+	}
+	const getResult = await examProvider.getCompanyExamDetailListFree(queryParams);
+	return res.send(getResult);
+});
+
+// 공기업 문제 조회 API
+// [GET] /exams/publicCompany/:examIdx/examDetail/:examDetailIdx/problem/type/:type
+exports.getCompanyProblemList = asyncHandler(async function (req, res) {
+	const examIdx = req.params.examIdx;
+	const examDetailIdx = req.params.examDetailIdx;
+	const isAuth = req.isAuth;
+
+	// Validation Check (Request Error)
+	if (isAuth !== 1) throw new errorResponse(baseResponse.USERAUTH_NOT_EXIST, { isAuth: isAuth }, 400);
+	if (!examIdx) throw new errorResponse(baseResponse.EXAM_EMPTY, 400);
+	if (!regNumber.test(examIdx)) throw new errorResponse(baseResponse.EXAM_ERROR_TYPE, 400);
+
+	await examProvider.examCheck(examIdx);
+
+	if (!examDetailIdx) throw new errorResponse(baseResponse.EXAMDETAIL_EMPTY, 400);
+	if (!regNumber.test(examDetailIdx)) throw new errorResponse(baseResponse.EXAMDETAIL_ERROR_TYPE, 400);
+
+	await examProvider.examDetailCheck(examDetailIdx);
+
+	await examProvider.examMatchCheck(examDetailIdx, examIdx);
+
+	const problemListResult = await examProvider.getCompanyProblemList(examIdx, examDetailIdx, isAuth);
+	return res.send(problemListResult);
+});
+
+// 오답노트 시험 구분시 분야에 대한 목록들을 가져오기
+exports.getExamField = asyncHandler(async function (req, res) {
+	let examSortName = req.query.examSortName;
+	if (!examSortName) throw new errorResponse(baseResponse.EXAMSORT_EMPTY, 400);
+
+	const examSortLargeIdx = await examProvider.getExamSortLargeIdx(examSortName);
+	const getExamFieldResult = await examProvider.getExamFieldList(examSortLargeIdx);
+	return res.send(getExamFieldResult);
+});
+
+// 문제의 빈도수 가져오기
+exports.getFrequency = asyncHandler(async function (req, res) {
+	// 문제의 인덱스
+	const { problemIdx } = req.params;
+	if (!problemIdx) throw new errorResponse(baseResponse.PROBLEMIDX_EMPTY, 400);
+	const getFrequencyResult = await examProvider.getFrequency(problemIdx);
+
+	return res.send(getFrequencyResult);
+});
+
+exports.getCategoryList = asyncHandler(async function (req, res) {
+	const companyExamType = req.query.companyExamType;
+	if (!companyExamType) throw new errorResponse(baseResponse.COMPANY_EXAMTYPE_EMPTY, 400);
+	const getCategoryListReult = await examProvider.getCategoryList(companyExamType);
+	return res.send(getCategoryListReult);
+});
diff --git a/_old/src/Exam/examDao.js b/_old/src/Exam/examDao.js
new file mode 100644
index 0000000..e86ff6a
--- /dev/null
+++ b/_old/src/Exam/examDao.js
@@ -0,0 +1,1588 @@
+// 시험 인덱스 확인
+async function selectExamIdx(connection, examIdx) {
+	const selectExamIdxQuery = `
+	select e.examIdx, status, problemCount , count(esm.examIdx) as subjectCount, e.examSortIdx,passScore
+	from Exam e
+	left join ExamSubjectMulti esm on esm.examIdx = e.examIdx
+	where e.examIdx = ?
+       `;
+	const [selectExamIdxRow] = await connection.query(selectExamIdxQuery, examIdx);
+	return selectExamIdxRow;
+}
+// 유저가 특정 시험에 권한이 있는지 확인
+async function selectUserExamAuth(connection, examIdx, userIdx) {
+	const selectUserExamAuthQuery = `
+  select exists
+  ( select * from Exam e  
+    LEFT JOIN ExamDetail ed on ed.examIdx = e.examIdx
+    LEFT JOIN ProductExamDetail ped on ped.examDetailIdx = ed.examDetailIdx
+    LEFT JOIN UserAuth ua on ua.productIdx = ped.productIdx
+    where e.examIdx = ? and ua.userIdx = ? and ua.status= 'N'
+  ) as exist 
+  `;
+	const [selectUserExamAuthRow] = await connection.query(selectUserExamAuthQuery, [examIdx, userIdx]);
+	return selectUserExamAuthRow;
+}
+
+// 시험 디테일 인덱스 확인
+async function selectExamDetailIdx(connection, examDetailIdx) {
+	const selectExamDetailIdxQuery = `
+      select examDetailIdx, status, isPublic, publicLevel, examIdx from ExamDetail where examDetailIdx = ?
+       `;
+
+	const [selectExamDetailIdxRow] = await connection.query(selectExamDetailIdxQuery, examDetailIdx);
+	return selectExamDetailIdxRow;
+}
+
+// 그 시험에 과목 인덱스 존재하는지 확인
+async function selectSubjectMultiIdx(connection, examIdx, subjectList) {
+	const selectSubjectMultiIdxQuery = `
+        select count(examIdx) as count from ExamSubjectMulti where examIdx = ? and examSubjectIdx in (?)
+       `;
+	const selectSubjectMultiParams = [examIdx, subjectList];
+	const [selectSubjectMultiRow] = await connection.query(selectSubjectMultiIdxQuery, selectSubjectMultiParams);
+	return selectSubjectMultiRow;
+}
+
+// 시험 과목 인덱스 확인
+async function selectExamSubjectIdx(connection, examSubjectIdx) {
+	const selectExamSubjectIdxQuery = `
+      select examSubjectIdx, status from ExamSubject where examSubjectIdx = ?
+       `;
+	const [selectExamSubjectIdxRow] = await connection.query(selectExamSubjectIdxQuery, examSubjectIdx);
+	return selectExamSubjectIdxRow;
+}
+
+// 시험 과목 인덱스 확인
+async function selectExamMatchCheck(connection, examDetailIdx, examIdx) {
+	const selectExamMatchCheckQuery = `
+    select examDetailIdx, status from ExamDetail where examDetailIdx = ? and examIdx = ?
+    `;
+	const [selectExamMatchCheckRow] = await connection.query(selectExamMatchCheckQuery, [examDetailIdx, examIdx]);
+	return selectExamMatchCheckRow;
+}
+
+// 객관식 문제 인덱스 확인
+async function selectMultipleProblemIdx(connection, multipleProblemIdx) {
+	const selectMultipleProblemIdxQuery = `
+      select mp.multipleProblemIdx, pe.examDetailIdx, mp.status from MultipleProblem mp join 
+			ProblemExam pe on mp.multipleProblemIdx=pe.multipleProblemIdx 
+			where mp.multipleProblemIdx = ?
+       `;
+	const [selectMultipleProblemIdxRow] = await connection.query(selectMultipleProblemIdxQuery, multipleProblemIdx);
+	return selectMultipleProblemIdxRow;
+}
+// 주관식 문제 인덱스 확인
+async function selectSubjectiveProblemIdx(connection, subjectiveProblemIdx) {
+	const selectSubjectiveProblemIdxQuery = `
+      select sp.SubjectiveProblemIdx, spe.examDetailIdx, sp.status from SubjectiveProblem sp join 
+			SubjectiveProblemExam spe on sp.SubjectiveProblemIdx= spe.SubjectiveProblemIdx 
+			where sp.subjectiveProblemIdx = ?
+       `;
+	const [selectSubjectiveProblemIdxRow] = await connection.query(selectSubjectiveProblemIdxQuery, subjectiveProblemIdx);
+	return selectSubjectiveProblemIdxRow;
+}
+// 문제 인덱스 확인(복수)
+async function selectMultipleProblemIdxes(connection, multipleProblemIdxes, multipleParams) {
+	const selectMultipleProblemIdxesQuery =
+		` select multipleProblemIdx from MultipleProblem where multipleProblemIdx in ` + multipleParams + ` and status='N'`;
+	const [selectMultipleProblemIdxesRow] = await connection.query(selectMultipleProblemIdxesQuery, multipleProblemIdxes);
+	return selectMultipleProblemIdxesRow;
+}
+
+// 문제 인덱스 확인
+async function selectMultipleProblemNumIdx(connection, examDetailIdx, problemNum) {
+	const selectMultipleProblemNumIdxQuery = `
+	select exists(select mp.multipleProblemIdx 
+		from ProblemExam pe
+		left join MultipleProblem mp on  pe.multipleProblemIdx = mp.multipleProblemIdx
+		where examDetailIdx = ? and problemNum = ? and status = 'N') as exist;
+       `;
+	const [selectMultipleProblemNumIdxRow] = await connection.query(selectMultipleProblemNumIdxQuery, [
+		examDetailIdx,
+		problemNum,
+	]);
+	return selectMultipleProblemNumIdxRow;
+}
+
+// 주관식 문제 인덱스 확인
+async function selectSubjectiveProblemNumIdx(connection, examDetailIdx, problemNum) {
+	const selectSubjectiveProblemNumIdxQuery = `
+	select exists(select sp.subjectiveProblemIdx 
+		from SubjectiveProblemExam spe
+		left join SubjectiveProblem sp on  spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+		where examDetailIdx = ? and problemNum = ? and status = 'N') as exist;
+       `;
+	const [selectSubjectiveProblemNumIdxRow] = await connection.query(selectSubjectiveProblemNumIdxQuery, [
+		examDetailIdx,
+		problemNum,
+	]);
+	return selectSubjectiveProblemNumIdxRow;
+}
+
+// 문제 정보 조회
+async function selectMultipleProblemInfo(connection, multipleProblemIdx) {
+	const selectMultipleProblemInfoQuery = `
+    SELECT e.examIdx, ed.examDetailIdx, examName, examDate, date_format(examDate, '%Y.%m.%d.') as 'date', examRound, mp.multipleProblemIdx, problemNum, problemScore, isKatex, pc.categoryIdx,   
+	case 
+    when problemImage is null then problem
+    when problemImage = ' ' then problem
+    when problemImage = '' then problem
+    else
+    concat(problem, '\\\\\\\\ ', problemImage)  end as problem,mp.examHistory,
+	provisionNum, questionNum as 'answerNum', solution,
+    concat('제', examSubjectNum,'과목 ', examSubjectName) as subject, examSubjectName, es.examSubjectIdx, problemUrl, lectureUrl, mp.isDelete
+	FROM MultipleProblem mp
+    LEFT JOIN Question q ON q.multipleProblemIdx = mp.multipleProblemIdx AND q.isAnswer = 1 AND q.status = 'N'
+    LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+	LEFT JOIN ProblemSubject ps ON ps.multipleProblemIdx = mp.multipleProblemIdx
+	LEFT JOIN ProblemCategory pc on pc.multipleProblemIdx = mp.multipleProblemIdx
+    LEFT JOIN ExamSubject es ON ps.examSubjectIdx = es.examSubjectIdx
+    LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx AND ed.status = 'N'
+    LEFT JOIN Exam e ON e.examIdx = ed.examIdx AND e.status = 'N'
+    LEFT JOIN ExamSubjectMulti esm ON esm.examSubjectIdx = es.examSubjectIdx and ed.examIdx = esm.examIdx
+    WHERE mp.multipleProblemIdx = ?
+	group by mp.multipleProblemIdx
+       `;
+	const [selectMultipleProblemInfoRow] = await connection.query(selectMultipleProblemInfoQuery, [multipleProblemIdx]);
+	return selectMultipleProblemInfoRow;
+}
+
+// 실기 문제 정보 조회
+async function selectSubjectiveProblemInfo(connection, subjectiveProblemIdx) {
+	const selectSubjectiveProblemInfoQuery = `
+	SELECT e.examIdx, ed.examDetailIdx, e.examName, ed.examDate, date_format(examDate, '%Y.%m.%d.') as 'date', examRound,es.examSubjectName, 
+	sp.subjectiveProblemIdx, spe.problemNum, sp.examHistory, es.examSubjectIdx,
+	case 
+	when problemImage is null then problem
+	when problemImage = ' ' then problem
+	when problemImage = '' then problem
+	else
+	concat(problem, '\\\\\\\\ ', problemImage)  end as problem, solution,solutionImage, isKatex, problemScore, problemUrl, lectureUrl, sp.isDelete
+	FROM SubjectiveProblem sp 
+	join SubjectiveProblemExam spe on sp.subjectiveProblemIdx=spe.subjectiveProblemIdx
+	join SubjectiveProblemSubject sps on sps.subjectiveProblemIdx=sp.subjectiveProblemIdx
+	LEFT JOIN ExamSubject es ON sps.examSubjectIdx = es.examSubjectIdx
+  LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx AND ed.status = 'N'
+  LEFT JOIN Exam e ON e.examIdx = ed.examIdx AND e.status = 'N'
+	WHERE sp.status = 'N' AND sp.subjectiveProblemIdx = ?
+group by sp.subjectiveProblemIdx
+	ORDER BY problemNum
+	`;
+	const [selectSubjectiveProblemInfoRow] = await connection.query(
+		selectSubjectiveProblemInfoQuery,
+		subjectiveProblemIdx,
+	);
+	return selectSubjectiveProblemInfoRow;
+}
+// 오답노트 인덱스 확인  exclusive lock 유의
+async function selectReviewNoteIdx(connection, multipleProblemIdx, userIdx) {
+	const selectReviewNoteIdxQuery = `
+      select reviewNoteIdx, status
+      from ReviewNote 
+      where multipleProblemIdx = ? and userIdx = ?
+	  ;
+       `;
+	const [selectReviewNoteIdxRow] = await connection.query(selectReviewNoteIdxQuery, [multipleProblemIdx, userIdx]);
+	return selectReviewNoteIdxRow;
+}
+// 주관식 오답노트 인덱스 확인  exclusive lock 유의
+async function selectSubjectiveReviewNoteIdx(connection, subjectiveProblemIdx, userIdx) {
+	const selectSubjectiveReviewNoteIdxQuery = `
+      select reviewNoteIdx, status
+      from ReviewNote 
+      where subjectiveProblemIdx = ? and userIdx = ?
+	  ;
+       `;
+	const [selectSubjectiveReviewNoteIdxRow] = await connection.query(selectSubjectiveReviewNoteIdxQuery, [
+		subjectiveProblemIdx,
+		userIdx,
+	]);
+	return selectSubjectiveReviewNoteIdxRow;
+}
+// 시험 날짜 회차 조회
+async function selectExamList(connection) {
+	const selectExamListQuery = `
+          SELECT examIdx, examName, examDate, examRound
+          FROM Exam
+		  where status = 'N'
+          ORDER BY examDate DESC 
+          `;
+	const [selectExamListRow] = await connection.query(selectExamListQuery);
+	return selectExamListRow;
+}
+
+// 시험 전체 조회
+async function selectExamAll(connection) {
+	const selectExamAllQuery = `
+          SELECT examIdx, examName
+          FROM Exam
+		  where status = 'N'
+          `;
+	const [selectExamAllRow] = await connection.query(selectExamAllQuery);
+	return selectExamAllRow;
+}
+
+// 시험 종류 조회
+async function selectExamSort(connection, examSort) {
+	const selectExamSortQuery = `
+		  SELECT examSort
+          FROM ExamSort
+          WHERE examSortRef = ? and status = 'N'
+          `;
+	const [selectExamSortRow] = await connection.query(selectExamSortQuery, examSort);
+	return selectExamSortRow;
+}
+
+// 년도별 시험 조회
+async function selectExamDate(connection, examDate) {
+	const selectExamDateQuery = `
+          SELECT examIdx, examName, examDate, examRound
+          FROM Exam
+          WHERE examDate = ? and status = 'N'
+          `;
+	const [selectExamDateRow] = await connection.query(selectExamDateQuery, examDate);
+	return selectExamDateRow;
+}
+
+// exam의 examSortRef를 가져오기.
+async function selectExamSortRef(connection, examSortName) {
+	const selectExamSortRefQuery = `
+		select examSortIdx from ExamSort
+		where examSortName = ?
+	`;
+	const [[selectExamSortRefRow]] = await connection.query(selectExamSortRefQuery, examSortName);
+	return selectExamSortRefRow;
+}
+
+// exam의 examField를 가져오는 DAO
+async function selectExamField(connection, largeExamIdx, mediumExamIdx) {
+	// largeExamIdx는 자격증 공기업 공무원 대기업 등등에 대한 idx,
+	// MediumExamIdx는 자격증의 경우 전기, 화학 등에 대한 Idx
+	const selectExamFieldQuery = `
+	with recursive CTE as (
+		select 
+		examSortIdx,examSortName, examSortRef 
+		from ExamSort a 
+		where examSortIdx= ?
+		union all
+		select 
+		  b.examSortIdx,b.examSortName, b.examSortRef 
+		  from ExamSort b
+		  join CTE ON b.examSortRef=CTE.examSortIdx where b.status='N' and b.examSortType="M"
+	)
+	select * from CTE c
+	where c.examSortName = ?
+	`;
+	const [[selectExamFieldRow]] = await connection.query(selectExamFieldQuery, [largeExamIdx, mediumExamIdx]);
+	return selectExamFieldRow;
+}
+
+// 시험 조회   자격증 조회
+async function selectExam(connection, where, whereInfo, examField) {
+	const selectExamQuery =
+		`
+		with recursive CTE AS   (SELECT 
+			es.examSortIdx, es.examSortRef, es.examSortName, es.examSortType
+			from ExamSort es
+			where  es.examSortRef= ? and es.examSortName=?
+			UNION ALL
+			SELECT
+			a.examSortIdx, a.examSortRef, a.examSortName, a.examSortType
+			from ExamSort a
+			JOIN CTE b ON a.examSortRef=b.examSortIdx
+			)
+			select e.examIdx, e.examName, e.examSortIdx, e.prio, e.examUrl as examThumbnailUrl
+			from Exam e 
+			join ExamSort es on e.examSortIdx=es.examSortIdx
+			where exists (select examSortIdx from CTE where e.examSortIdx=CTE.examSortIdx) and e.status='N'
+    ` + where;
+	const [selectExamRow] = await connection.query(selectExamQuery, [whereInfo, examField]);
+
+	return selectExamRow;
+}
+async function selectExamFieldList(connection, examSortRef) {
+	const selectExamSortRefQuery = `
+	select examSortIdx,examSortName from ExamSort
+	where examSortRef =? and examSortType = "M" and status = "N"
+	`;
+	const [selectExamSortRefRow] = await connection.query(selectExamSortRefQuery, examSortRef);
+	return selectExamSortRefRow;
+}
+
+// 시험 디테일 조회
+async function selectExamDetail(connection, examIdx) {
+	const selectExamDetailQuery = `
+    SELECT ed.examDetailIdx, date_format(ed.examDate, '%Y.%m.%d') as 'date', ed.examRound, ed.isPublic, ed.publicLevel, e.subjectiveLabel
+          FROM ExamDetail ed left join
+          Exam e on e.examIdx = ed.examIdx
+          WHERE ed.examIdx = ? and ed.status = 'N' and ed.isPublic = 1
+          ORDER BY examDate DESC
+          `;
+	const [selectExamDetailRow] = await connection.query(selectExamDetailQuery, examIdx);
+	return selectExamDetailRow;
+}
+// 시험 주관식 여부 판단 조회
+async function selectSubjectiveOfExam(connection, examIdx) {
+	const selectExamDetailQuery = `
+    SELECT  subjectiveLabel from Exam 
+	where examIdx = ?
+          `;
+	const [[selectExamDetailRow]] = await connection.query(selectExamDetailQuery, examIdx);
+	return selectExamDetailRow;
+}
+// 특정 시험 디테일 조회
+async function selectExamDetailInfo(connection, examDetailIdx) {
+	const selectExamDetailInfoQuery = `
+          SELECT ed.examDetailIdx, ed.examIdx, examName , examDate, date_format(examDate, '%Y.%m.%d.') as 'date', examRound, passScore,
+		   timeLimit, problemCount, ed.examDetailUrl, e.examUrl, e.examSortIdx, questionType, publicLevel,subjectiveLabel, year(examDate) as examYear
+          FROM ExamDetail ed
+          LEFT JOIN Exam e ON e.examIdx = ed.examIdx and e.status = 'N'
+          WHERE examDetailIdx = ? and ed.status = 'N'
+          `;
+	const [selectExamDetailInfoRow] = await connection.query(selectExamDetailInfoQuery, examDetailIdx);
+	return selectExamDetailInfoRow;
+}
+
+// 특정 시험 날짜, 회차 조회
+async function selectExamDetailBasicInfo(connection, examDetailIdx) {
+	const selectExamDetailInfoQuery = `
+          SELECT ed.examDetailIdx,date_format(examDate, '%Y-%m-%d') as 'examDate', examRound, publicLevel
+          FROM ExamDetail ed
+          WHERE examDetailIdx = ? and ed.status = 'N'
+          `;
+	const [selectExamDetailInfoRow] = await connection.query(selectExamDetailInfoQuery, examDetailIdx);
+	return selectExamDetailInfoRow;
+}
+
+// 특정 시험 정보 조회
+async function selectExamInfo(connection, examIdx) {
+	const selectExamInfoQuery = `
+          SELECT examIdx, examName ,  passScore, timeLimit, problemCount, examUrl, subjectiveLabel
+          FROM Exam e
+          WHERE examIdx = ? and status='N'
+          `;
+	const [selectExamInfoRow] = await connection.query(selectExamInfoQuery, examIdx);
+	return selectExamInfoRow;
+}
+
+// 특정 시험 과목 조회
+async function selectExamSubject(connection, examIdx) {
+	const selectExamSubjectQuery = `
+    SELECT es.examSubjectIdx, esm.examSubjectNum, examSubjectName, es.passScore,e.subjectiveLabel
+    FROM ExamSubject es
+    LEFT JOIN ExamSubjectMulti esm ON esm.examSubjectIdx = es.examSubjectIdx 
+	LEFT JOIN Exam e on e.examIdx = esm.examIdx
+    WHERE esm.examIdx = ? and es.status = 'N'
+    order by examSubjectNum
+          `;
+	const [selectExamSubjectRow] = await connection.query(selectExamSubjectQuery, examIdx);
+	return selectExamSubjectRow;
+}
+
+// 인덱스로 특정 시험 과목 조회
+async function selectExamSubjectByIdx(connection, examIdx, examSubjectIdx) {
+	const selectExamSubjectByIdxQuery = `
+    SELECT es.examSubjectIdx, esm.examSubjectNum, examSubjectName, passScore
+    FROM ExamSubject es
+    JOIN ExamSubjectMulti esm ON esm.examSubjectIdx = es.examSubjectIdx and esm.examIdx = ?
+    WHERE es.examSubjectIdx = ? and es.status = 'N'
+    order by esm.examSubjectNum
+    `;
+	const [selectExamSubjectByIdxRow] = await connection.query(selectExamSubjectByIdxQuery, [examIdx, examSubjectIdx]);
+	return selectExamSubjectByIdxRow;
+}
+//과목 인덱스로 시험 과목 이름 조회
+async function selectExamSubjectNamebyIdx(connection, examSubjecIdx) {
+	const selectExamSubjectNamebyIdxQuery = `
+	select ExamSubjectName from ExamSubject
+  	where examSubjectIdx =?
+	`;
+	const [selectExamSubjectNamebyIdxRow] = await connection.query(selectExamSubjectNamebyIdxQuery, examSubjecIdx);
+	return selectExamSubjectNamebyIdxRow;
+}
+// 특정 카테고리 조회
+// async function selectProblemCategory(connection, categoryIdx) {
+//     const selectProblemCategoryQuery = `
+//     SELECT es.examSubjectIdx, concat(examSubjectNum, '과목') as subjectNum, examSubjectName, concat(count(multipleProblemIdx), '문항') as problemCount
+//     FROM ExamSubject es
+//     LEFT JOIN MultipleProblem mp ON es.examSubjectIdx = mp.examSubjectIdx
+//     WHERE examDetailIdx = ?
+//           `;
+//     const [selectProblemCategoryRow] = await connection.query(selectProblemCategoryQuery, categoryIdx);
+//     return selectProblemCategoryRow;
+// }
+
+// 객관식 과목 상세 조회 -- slow query
+async function selectExamSubjectInfo(connection, examDetailIdx, subjectiveLabel) {
+	const selectExamSubjectInfoQuery = `
+    SELECT es.examSubjectIdx,e.examIdx, concat(esm.examSubjectNum, '과목') as subjectNum, concat(count(mp.multipleProblemIdx), '문항') as problemCount, examSubjectName
+    FROM MultipleProblem mp
+    join ProblemExam pe on pe.multipleProblemIdx=mp.multipleProblemIdx
+    join ProblemSubject ps on ps.multipleProblemIdx=mp.multipleProblemIdx
+    LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx and ed.status = 'N'
+    LEFT JOIN Exam e ON e.examIdx = ed.examIdx and e.status = 'N'
+    LEFT JOIN ExamSubjectMulti esm ON esm.examIdx = e.examIdx AND esm.examSubjectIdx = ps.examSubjectIdx
+    JOIN ExamSubject es ON es.examSubjectIdx = esm.examSubjectIdx and es.status = 'N'
+    WHERE ed.examDetailIdx = ? and mp.status = 'N'
+    group by ps.examSubjectIdx
+    order by esm.examSubjectNum
+    `;
+	const [selectExamSubjectInfoRow] = await connection.query(selectExamSubjectInfoQuery, examDetailIdx);
+
+	return selectExamSubjectInfoRow;
+}
+
+// 주관식 시험 과목 목록 확인하기.
+async function selectSubjectiveExamSubjectInfo(connection, examDetailIdx) {
+	const selectSubjectiveExamSubjectInfoQuery = `
+		select es.examSubjectIdx,  concat(esm.examSubjectNum, '과목') as subjectNum, concat(count(sps.subjectiveProblemIdx), '문항') as problemCount,es.examSubjectName
+		from SubjectiveProblem sp 
+		left join SubjectiveProblemExam spe on spe.subjectiveProblemIdx =  sp.subjectiveProblemIdx
+		left join SubjectiveProblemSubject sps on sps.subjectiveProblemIdx = sp.subjectiveProblemIdx
+		left join ExamDetail ed on ed.examDetailIdx = spe.examDetailIdx and ed.status = 'N'
+		left join Exam e on e.examIdx = ed.examIdx and  e.status = 'N'
+		left join ExamSubjectMulti esm on esm.examIdx = e.examIdx and esm.examSubjectIdx = sps.examSubjectIdx
+		left join ExamSubject es on es.examSubjectIdx = esm.examSubjectIdx 
+		where e.subjectiveLabel = 1 and ed.examDetailIdx = ? and sp.status = 'N'
+			group by sps.examSubjectIdx
+			order by esm.examSubjectNum;
+	`;
+	const [selectSubjectiveExamSubjectInfoRow] = await connection.query(
+		selectSubjectiveExamSubjectInfoQuery,
+		examDetailIdx,
+	);
+
+	return selectSubjectiveExamSubjectInfoRow;
+}
+
+// 회차별 시험 문제 조회
+async function selectProblemByExamDetail(connection, examDetailIdx) {
+	const selectProblemByExamDetailQuery = `
+    SELECT mp.multipleProblemIdx as 'problemIdx', pe.problemNum,mp.examHistory, 
+    case 
+    when problemImage is null then problem
+    when problemImage = ' ' then problem
+    when problemImage = '' then problem
+    else
+    concat(problem, '\\\\\\\\ ', problemImage)  end as problem, solution, provisionNum, pc.categoryIdx, isKatex, problemScore, problemUrl, lectureUrl,
+	questionNum as 'answerNum'
+    FROM MultipleProblem mp 
+	join ProblemExam pe on mp.multipleProblemIdx=pe.multipleProblemIdx
+    left join ProblemSubject ps on ps.multipleProblemIdx=mp.multipleProblemIdx
+    left join ProblemCategory pc on pc.multipleProblemIdx=mp.multipleProblemIdx
+    left join Category c on pc.categoryIdx=c.categoryIdx and c.status = 'N'
+	left join Question q on q.multipleProblemIdx = mp.multipleProblemIdx and isAnswer = 1 and q.status = 'N' 
+    WHERE examDetailIdx = ? and mp.status = 'N'
+	group by mp.multipleProblemIdx
+    ORDER BY problemNum
+    `;
+	const selectProblemByExamDetailRow = await connection.query(selectProblemByExamDetailQuery, examDetailIdx);
+	return selectProblemByExamDetailRow[0];
+}
+
+//  시험 문제 인덱스 조회
+//  자격증 join, 공기업 left join
+async function selectMultipleProblemAnswer(connection, condition, examDetailIdx) {
+	const selectMultipleProblemAnswerQuery = `
+    SELECT problemNum, mp.multipleProblemIdx, q.questionIdx, questionNum as 'answerNum', isDelete, problemScore,examSubjectIdx
+	FROM MultipleProblem mp 
+	join ProblemExam pe on mp.multipleProblemIdx=pe.multipleProblemIdx
+	${condition} join ProblemSubject ps on ps.multipleProblemIdx=mp.multipleProblemIdx
+	left join Question q on q.multipleProblemIdx = mp.multipleProblemIdx and isAnswer = 1 and q.status = 'N' 
+	WHERE pe.examDetailIdx = ? and mp.status='N'
+	group by mp.multipleProblemIdx
+	order by problemNum
+    `;
+	const selectMultipleProblemAnswerRow = await connection.query(selectMultipleProblemAnswerQuery, examDetailIdx);
+	return selectMultipleProblemAnswerRow[0];
+}
+
+// 회차별 과목별 객관식 시험 문제 조회   -- slow query
+async function selectProblemByExamSubject(connection, examDetailIdx, examSubjectIdx) {
+	const selectProblemByExamSubjectQuery = `
+    SELECT mp.multipleProblemIdx, pe.problemNum,mp.examHistory,
+    case 
+    when problemImage is null then problem
+    when problemImage = ' ' then problem
+    when problemImage = '' then problem
+    else
+    concat(problem, '\\\\\\\\ ', problemImage)  end as problem, solution, provisionNum, pc.categoryIdx, isKatex, problemScore, mp.problemUrl, mp.lectureUrl,
+	questionNum as 'answerNum',isDelete
+    FROM MultipleProblem mp 
+	join ProblemExam pe on mp.multipleProblemIdx=pe.multipleProblemIdx
+    join ProblemSubject ps on ps.multipleProblemIdx=mp.multipleProblemIdx
+    left join ProblemCategory pc on pc.multipleProblemIdx=mp.multipleProblemIdx
+    left join Category c on pc.categoryIdx=c.categoryIdx and c.status = 'N'
+	left join Question q on q.multipleProblemIdx = mp.multipleProblemIdx and isAnswer = 1 and q.status = 'N' 
+    WHERE mp.status = 'N' AND examDetailIdx = ? AND examSubjectIdx = ?
+	group by mp.multipleProblemIdx
+    ORDER BY problemNum
+        `;
+	const selectProblemByExamSubjectRow = await connection.query(selectProblemByExamSubjectQuery, [
+		examDetailIdx,
+		examSubjectIdx,
+	]);
+	return selectProblemByExamSubjectRow[0];
+}
+
+// 회차별 과목별 주관식 시험 문제 조회   -- slow query
+async function selectSubjectiveProblemByExamSubject(connection, examDetailIdx, examSubjectIdx) {
+	const selectSubjectiveProblemByExamSubjectQuery = `
+    SELECT sp.subjectiveProblemIdx, spe.problemNum, sp.examHistory,
+    case 
+    when problemImage is null then problem
+    when problemImage = ' ' then problem
+    when problemImage = '' then problem
+    else
+    concat(problem, '\\\\\\\\ ', problemImage)  end as problem, solution,solutionImage, spc.categoryIdx, isKatex, problemScore, problemUrl, lectureUrl,isDelete
+    FROM SubjectiveProblem sp 
+	join SubjectiveProblemExam spe on sp.subjectiveProblemIdx=spe.subjectiveProblemIdx
+    join SubjectiveProblemSubject sps on sps.subjectiveProblemIdx=sp.subjectiveProblemIdx
+    left join SubjectiveProblemCategory spc on spc.subjectiveProblemIdx=sp.subjectiveProblemIdx
+    left join Category c on spc.categoryIdx=c.categoryIdx and c.status = 'N'
+    WHERE sp.status = 'N' AND examDetailIdx = ? AND examSubjectIdx = ?
+	group by sp.subjectiveProblemIdx
+    ORDER BY problemNum
+        `;
+	const selectSubjectiveProblemByExamSubjectRow = await connection.query(selectSubjectiveProblemByExamSubjectQuery, [
+		examDetailIdx,
+		examSubjectIdx,
+	]);
+	return selectSubjectiveProblemByExamSubjectRow[0];
+}
+
+/* 정답률 쿼리
+LEFT JOIN (SELECT multipleProblemIdx ,concat(round( count(if(isCorrect='Y', isCorrect, null)) / count(*)*100),'%') AS correctPercentage
+	FROM correctanswerrate
+	GROUP BY multipleProblemIdx) as v ON mp.multipleProblemIdx = v.multipleProblemIdx
+*/
+
+// 시험 문제 문항 조회   slow query
+async function selectQuestion(connection, multipleProblemIdx) {
+	const selectQuestionQuery = `
+    SELECT questionIdx, questionNum, isAnswer,
+    case 
+    when questionImage is null then question
+    when questionImage = ' ' then question
+    when questionImage = '' then question
+    when question is null then questionImage
+    else
+    concat(question, '\\\\\\\\ ', questionImage)  end as question, questionImage,multipleProblemIdx
+    FROM Question
+    WHERE multipleProblemIdx = ? and status='N'
+    order by questionNum
+    `;
+	const selectQuestionRow = await connection.query(selectQuestionQuery, multipleProblemIdx);
+	return selectQuestionRow[0];
+}
+
+// 시험 문제 문항 조회(복수)   slow query
+async function selectQuestions(connection, multipleProblemList) {
+	const selectQuestionQuery = `
+		SELECT questionIdx, questionNum, isAnswer,
+		case 
+		when questionImage is null then question
+		when questionImage = ' ' then question
+		when questionImage = '' then question
+		when question is null then questionImage
+		else
+		concat(question, '\\\\\\\\ ', questionImage)  end as question, questionImage, multipleProblemIdx
+		FROM Question
+    WHERE multipleProblemIdx in (?)
+    order by multipleProblemIdx , questionNum
+    `;
+	const selectQuestionRow = await connection.query(selectQuestionQuery, [multipleProblemList]);
+	return selectQuestionRow[0];
+}
+//시험 문제 문항 (과목별) 복수 조회 -김기창
+async function selectSubjectQuestions(connection, userIdx, examIdx, multipleProblemList) {
+	const selectSubjectiveProblemsQuery = `
+	SELECT distinct bookMark, mp.multipleProblemIdx, problemNum, examSubjectNum, examSubjectName, problem,rn.reviewNoteIdx,
+	case 
+	when problemImage is null then problem
+	when problemImage = ' ' then problem
+	when problemImage = '' then problem
+	else
+	concat(problem, '\\\\\\\\ ', problemImage)  
+	end as problem,
+	provisionNum, questionNum as 'answerNum', solution,
+	concat('제', examSubjectNum,'과목 ', examSubjectName) as subject, isKatex
+	FROM ReviewNote rn
+	LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = rn.multipleProblemIdx
+	left join ProblemExam pe on pe.multipleProblemIdx = mp.multipleProblemIdx
+	LEFT JOIN Question q ON q.multipleProblemIdx = mp.multipleProblemIdx and q.isAnswer = 1
+	LEFT JOIN ProblemSubject ps ON ps.multipleProblemIdx = mp.multipleProblemIdx
+	LEFT JOIN ExamSubject es ON ps.examSubjectIdx = es.examSubjectIdx
+	left join ExamSubjectMulti esm on esm.examSubjectIdx =es.examSubjectIdx
+	left join Exam e on e.examIdx = esm.examIdx
+	WHERE userIdx = ? AND mp.multipleProblemIdx in (?) AND rn.status = 'N' AND e.examIdx = ? 
+	ORDER BY problemNum;
+	`;
+	const [selectSubjectiveProblemsRow] = await connection.query(selectSubjectiveProblemsQuery, [
+		userIdx,
+		multipleProblemList,
+		examIdx,
+	]);
+	return selectSubjectiveProblemsRow;
+}
+//시험 문제 문항 (과목별) 단수 조회 -김기창
+async function selectSubjectQuestion(connection, userIdx, examIdx, multipleProblemIdx) {
+	const selectSubjectQuestionQuery = `
+	SELECT distinct bookMark, mp.multipleProblemIdx, problemNum, examSubjectNum, examSubjectName, problem,rn.reviewNoteIdx,
+	case when rn.memo is null then 0 else 1 end as isMemo, publicLevel,
+	case 
+	when problemImage is null then problem
+	when problemImage = ' ' then problem
+	when problemImage = '' then problem
+	else
+	concat(problem, '\\\\\\\\ ', problemImage)  
+	end as problem,
+	provisionNum, questionNum as 'answerNum', solution, e.examName, ed.examRound, year(examDate) as examYear,
+	concat('제', examSubjectNum,'과목 ', examSubjectName) as subject, isKatex, problemUrl, lectureUrl
+	FROM ReviewNote rn
+	LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = rn.multipleProblemIdx
+	left join ProblemExam pe on pe.multipleProblemIdx = mp.multipleProblemIdx
+	LEFT JOIN ExamDetail ed on ed.examDetailIdx = pe.examDetailIdx
+	LEFT JOIN Question q ON q.multipleProblemIdx = mp.multipleProblemIdx and q.isAnswer = 1
+	LEFT JOIN ProblemSubject ps ON ps.multipleProblemIdx = mp.multipleProblemIdx
+	LEFT JOIN ExamSubject es ON ps.examSubjectIdx = es.examSubjectIdx
+	left join ExamSubjectMulti esm on esm.examSubjectIdx =es.examSubjectIdx
+	left join Exam e on e.examIdx = esm.examIdx
+	WHERE userIdx = ? AND mp.multipleProblemIdx = ? AND rn.status = 'N' AND e.examIdx = ? 
+	ORDER BY problemNum;
+	`;
+	const [selectSubjectQuestionRow] = await connection.query(selectSubjectQuestionQuery, [
+		userIdx,
+		multipleProblemIdx,
+		examIdx,
+	]);
+	return selectSubjectQuestionRow;
+}
+//시험 주관식 문제 문항 (과목별) 복수 조회 -김기창
+async function selectSubjectiveProblems(connection, userIdx, examIdx, subjectiveProblemList) {
+	const selectSubjectiveProblemsQuery = `
+	SELECT distinct bookMark,e.subjectiveLabel, sp.subjectiveProblemIdx, problemNum, examSubjectNum, examSubjectName, problem,rn.reviewNoteIdx,
+	case 
+	when problemImage is null then problem
+	when problemImage = ' ' then problem
+	when problemImage = '' then problem
+	else
+	concat(problem, '\\\\\\\\ ', problemImage)  
+	end as problem, solution,
+	concat('제', examSubjectNum,'과목 ', examSubjectName) as subject, isKatex
+	FROM ReviewNote rn
+	LEFT JOIN SubjectiveProblem mp ON sp.subjectiveProblemIdx = rn.subjectiveProblemIdx
+	left join SubjectiveProblemExam spe on spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+	LEFT JOIN Question q ON q.subjectiveProblemIdx = mp.subjectiveProblemIdx and q.isAnswer = 1
+	LEFT JOIN SubjectiveProblemSubject sps ON sps.subjectiveProblemIdx = sp.subjectiveProblemIdx
+	LEFT JOIN ExamSubject es ON sps.examSubjectIdx = es.examSubjectIdx
+	left join ExamSubjectMulti esm on esm.examSubjectIdx =es.examSubjectIdx
+	left join Exam e on e.examIdx = esm.examIdx
+	WHERE userIdx = ? AND sp.subjectiveProblemIdx in (?) AND rn.status = 'N' AND e.examIdx = ? 
+	ORDER BY problemNum;
+	`;
+	const [selectSubjectiveProblemsRow] = await connection.query(selectSubjectiveProblemsQuery, [
+		userIdx,
+		subjectiveProblemList,
+		examIdx,
+	]);
+	return selectSubjectiveProblemsRow;
+}
+
+//시험 주관식 문제 문항 (과목별) 단수 조회 -김기창
+async function selectSubjectiveProblem(connection, userIdx, examIdx, subjectiveProblemIdx) {
+	const selectSubjectiveProblemQuery = `
+	SELECT distinct bookMark, e.subjectiveLabel,sp.subjectiveProblemIdx, problemNum, examSubjectNum, examSubjectName, problem,rn.reviewNoteIdx,
+	case when rn.memo is null then 0 else 1 end as isMemo, publicLevel, year(ed.examDate) as examYear, e.examName, ed.examRound,
+	case 
+	when problemImage is null then problem
+	when problemImage = ' ' then problem
+	when problemImage = '' then problem
+	else
+	concat(problem, '\\\\\\\\ ', problemImage)  
+	end as problem, solution,
+	concat('제', examSubjectNum,'과목 ', examSubjectName) as subject, isKatex, problemUrl, lectureUrl
+	FROM ReviewNote rn
+	LEFT JOIN SubjectiveProblem sp ON sp.subjectiveProblemIdx = rn.subjectiveProblemIdx
+	left join SubjectiveProblemExam spe on spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+	LEFT JOIN ExamDetail ed on ed.examDetailIdx = spe.examDetailIdx
+	LEFT JOIN SubjectiveProblemSubject sps ON sps.subjectiveProblemIdx = sp.subjectiveProblemIdx
+	LEFT JOIN ExamSubject es ON sps.examSubjectIdx = es.examSubjectIdx
+	left join ExamSubjectMulti esm on esm.examSubjectIdx =es.examSubjectIdx
+	left join Exam e on e.examIdx = esm.examIdx
+	WHERE userIdx = ? AND sp.subjectiveProblemIdx = ? AND rn.status = 'N' AND e.examIdx = ? 
+	ORDER BY problemNum;
+	`;
+	const [selectSubjectiveProblemRow] = await connection.query(selectSubjectiveProblemQuery, [
+		userIdx,
+		subjectiveProblemIdx,
+		examIdx,
+	]);
+	return selectSubjectiveProblemRow;
+}
+
+// 특정 유형 출제 빈도 조회
+async function selectProblemFrequency(connection, categoryIdx) {
+	const selectProblemFrequencyQuery = `
+    SELECT examName, count(*) as 'count'
+    FROM Exam e
+    LEFT JOIN ExamDetail ed ON ed.examIdx = e.examIdx
+	LEFT JOIN ProblemExam pe ON pe.examDetailIdx = ed.examDetailIdx
+    LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = pe.multipleProblemIdx
+    WHERE mp.categoryIdx = ?
+    GROUP BY e.examIdx
+          `;
+	const selectProblemFrequencyRow = await connection.query(selectProblemFrequencyQuery, categoryIdx);
+	return selectProblemFrequencyRow[0];
+}
+
+// 특정 시험 오답노트 조회
+async function selectExamReviewNote(connection, userIdx, examDetailIdx) {
+	const selectExamReviewNoteQuery = `
+    SELECT multipleProblemIdx, problemNum
+    FROM ReviewNote rn
+    LEFT JOIN MultipleProblem mp ON rn.multipleProblemIdx = mp.multipleProblemIdx
+	LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx =mp.multipleProblemIdx
+    WHERE userIdx = ? AND rn.status = 'N' AND pe.examDetailIdx = ?
+    `;
+	const selectExamReviewNoteRow = await connection.query(selectExamReviewNoteQuery, [userIdx, examDetailIdx]);
+	return selectExamReviewNoteRow[0];
+}
+
+// 특정 시험 오답노트 조회
+async function selectExamUrl(connection, examIdx) {
+	const selectExamUrlQuery = `
+    SELECT examIdx, examUrl
+    FROM Exam
+    WHERE examIdx = ? and status = 'N'
+    `;
+	const selectExamUrlRow = await connection.query(selectExamUrlQuery, examIdx);
+	return selectExamUrlRow[0];
+}
+
+// 문제 해설 이미지 조회
+// async function selectSolutionImage(connection, multipleProblemIdx) {
+//     const selectSolutionImageQuery = `
+//     SELECT solutionImage
+//     FROM engineeo.solutionimage
+//     WHERE multipleProblemIdx = ?
+//     ORDER BY solutionImageIdx;
+//     `;
+//     const selectSolutionImageRow = await connection.query(selectSolutionImageQuery, multipleProblemIdx);
+//     return selectSolutionImageRow[0];
+// }
+
+// 특정 시험 과목 조회
+async function selectExamSubjectRandom(connection, examIdx) {
+	const selectExamSubjectRandomQuery = `
+    SELECT es.examSubjectIdx, esm.examSubjectNum, examSubjectName, passScore
+    FROM ExamSubject es
+    LEFT JOIN ExamSubjectMulti esm ON esm.examSubjectIdx = es.examSubjectIdx 
+    WHERE esm.examIdx = ? and es.status = 'N'
+    order by examSubjectNum
+          `;
+	const [selectExamSubjectRandomRow] = await connection.query(selectExamSubjectRandomQuery, examIdx);
+	return selectExamSubjectRandomRow;
+}
+
+// 필기 랜덤 문제 조회
+async function selectExamSubjectRandomByIdx(connection, examIdx, examSubjectIdx, startDate, endDate, limit, isAuth) {
+	let problemCondition = "";
+	if (isAuth == 0) {
+		//isAuth가 0이면 publicLevel이 1이하인것에서만 가져오기 문제가 무료인것들 위주로.
+		problemCondition += " and ed.publicLevel <=1 ";
+	}
+
+	const selectExamSubjectRandomByIdxQuery = `
+    SELECT mp.multipleProblemIdx,
+	case 
+	when problemImage is null then problem
+	when problemImage = ' ' then problem
+	when problemImage = '' then problem
+	else
+	concat(problem, '\\\\\\\\ ', problemImage)  end as problem, mp.examHistory,solution, provisionNum, isKatex, problemScore, problemUrl, lectureUrl, questionNum as 'answerNum',ed.publicLevel,
+	ed.examDetailIdx, examName, problemNum as 'originProblemNum', examRound as 'originExamRound', date_format(examDate, '%Y') as 'originYear'
+    FROM Exam e
+    left join ExamDetail ed on ed.examIdx = e.examIdx and ed.status = 'N' and isPublic = 1 ${problemCondition}
+    left join ProblemExam pe on pe.examDetailIdx = ed.examDetailIdx 
+    left join MultipleProblem mp on mp.multipleProblemIdx = pe.multipleProblemIdx and mp.status = 'N'
+	left join Question q on q.multipleProblemIdx = mp.multipleProblemIdx and isAnswer = 1 and q.status = 'N' 
+    left join ProblemSubject ps on ps.multipleProblemIdx = mp.multipleProblemIdx
+    where e.examIdx = ? and e.status = 'N' and examSubjectIdx = ? and ed.examDate between date(?) and date(?)
+	group by mp.multipleProblemIdx
+    order by rand()
+    limit ?
+    `;
+	const [selectExamSubjectRandomByIdxRow] = await connection.query(selectExamSubjectRandomByIdxQuery, [
+		examIdx,
+		examSubjectIdx,
+		startDate,
+		endDate,
+		limit,
+	]);
+	return selectExamSubjectRandomByIdxRow;
+}
+
+// 실기 랜덤 문제 조회
+async function selectSubjectiveExamSubjectRandomByIdx(
+	connection,
+	examIdx,
+	examSubjectIdx,
+	startDate,
+	endDate,
+	limit,
+	isAuth,
+	problemCount,
+) {
+	let problemCondition = "";
+	if (isAuth == 0) {
+		//isAuth가 0이면 publicLevel이 1이하인것에서만 가져오기 문제가 무료인것들 위주로.
+		problemCondition += " and ed.publicLevel <=1 ";
+	}
+	let randomCount = problemCount > 0 ? problemCount : limit;
+
+	const selectSubjectiveExamSubjectRandomByIdxQuery = `
+    SELECT sp.subjectiveProblemIdx,
+	case 
+	when problemImage is null then problem
+	when problemImage = ' ' then problem
+	when problemImage = '' then problem
+	else
+	concat(problem, '\\\\\\\\ ', problemImage)  end as problem, sp.examHistory,solution, isKatex, problemScore, problemUrl, lectureUrl, ed.publicLevel,
+	ed.examDetailIdx, examName, problemNum as 'originProblemNum', examRound as 'originExamRound', date_format(examDate, '%Y') as 'originYear'
+    FROM Exam e
+    left join ExamDetail ed on ed.examIdx = e.examIdx and ed.status = 'N' and isPublic = 1 ${problemCondition}
+    left join SubjectiveProblemExam spe on spe.examDetailIdx = ed.examDetailIdx 
+    left join SubjectiveProblem sp on sp.subjectiveProblemIdx = spe.subjectiveProblemIdx and sp.status = 'N'
+    left join SubjectiveProblemSubject sps on sps.subjectiveProblemIdx = sp.subjectiveProblemIdx
+    where e.examIdx = ? and e.status = 'N' and examSubjectIdx = ? and ed.examDate between date(?) and date(?) 
+	group by sp.subjectiveProblemIdx
+    order by rand()
+    limit ?
+    `;
+	const [selectSubjectiveExamSubjectRandomByIdxRow] = await connection.query(
+		selectSubjectiveExamSubjectRandomByIdxQuery,
+		[examIdx, examSubjectIdx, startDate, endDate, randomCount],
+	);
+	return selectSubjectiveExamSubjectRandomByIdxRow;
+}
+
+// 인덱스로 특정 시험 과목 조회
+async function selectNewKecRandomProblem(connection) {
+	const selectNewKecRandomProblemQuery = `
+    SELECT mp.multipleProblemIdx, 
+	case 
+	when problemImage is null then problem
+	when problemImage = ' ' then problem
+	when problemImage = '' then problem
+	else
+	concat(problem, '\\\\\\\\ ', problemImage)  end as problem, examName, problemNum as 'originProblemNum', examRound as 'originExamRound', date_format(examDate, '%Y') as 'originYear', solution, provisionNum, isKatex, problemScore, problemUrl, lectureUrl, questionNum as 'answerNum'
+    FROM  MultipleProblem mp 
+	left join ProblemExam pe on pe.multipleProblemIdx = mp.multipleProblemIdx 
+	left join ExamDetail ed on ed.examDetailIdx = pe.examDetailIdx and ed.status = 'N' and isPublic = 1
+	left join Exam e on e.examIdx = ed.examIdx
+	left join Question q on q.multipleProblemIdx = mp.multipleProblemIdx and isAnswer = 1 and q.status = 'N' 
+    left join ProblemSubject ps on ps.multipleProblemIdx = mp.multipleProblemIdx
+    where examSubjectIdx = 8 and mp.status = 'N'
+	group by mp.multipleProblemIdx
+    order by rand()
+    limit 5
+    `;
+	const [selectNewKecRandomProblemRow] = await connection.query(selectNewKecRandomProblemQuery);
+	return selectNewKecRandomProblemRow;
+}
+
+// 유저 시험 기록 테이블 삽입
+async function insertUserExamRecord(connection, userIdx, examDetailIdx, score, isPass) {
+	const insertUserExamRecordQuery = `
+    INSERT INTO UserExamRecord(userIdx, examDetailIdx, score, isPass)
+    VALUES(? , ?, ?, ?);
+    `;
+	const insertUserExamRecordRow = await connection.query(insertUserExamRecordQuery, [
+		userIdx,
+		examDetailIdx,
+		score,
+		isPass,
+	]);
+	return insertUserExamRecordRow;
+}
+
+// 유저 시험 기록 상세 테이블 삽입
+async function insertUserExamDetailRecord(connection, insertUserExamDetailRecordParams) {
+	const insertUserExamDetailRecordQuery = `
+    INSERT INTO UserExamDetailRecord(userExamRecordIdx, multipleProblemIdx, questionIdx)
+    VALUES ?;
+    `;
+	const insertUserExamDetailRecordRow = await connection.query(insertUserExamDetailRecordQuery, [
+		insertUserExamDetailRecordParams,
+	]);
+	return insertUserExamDetailRecordRow;
+}
+
+// 유저 오답노트 삽입
+async function insertReviewNote(connection, userIdx, multipleProblemIdx) {
+	const insertReviewNoteQuery = `
+    INSERT INTO  ReviewNote(userIdx, multipleProblemIdx)
+    select ?, ?
+	WHERE NOT EXISTS(
+		SELECT reviewNoteIdx FROM ReviewNote WHERE userIdx = ? AND multipleProblemIdx = ?
+	);
+    `;
+	const insertReviewNoteRow = await connection.query(insertReviewNoteQuery, [
+		userIdx,
+		multipleProblemIdx,
+		userIdx,
+		multipleProblemIdx,
+	]);
+	return insertReviewNoteRow;
+}
+
+// 유저 오답노트 삽입 or 업데이트  ----> ON DUPLICATE KEY UPDATE 구문으로 조회과정이 한번 더들어가 락 점유 발생 함수 이름만 유지하고 이부분 다시 뺐음
+async function upsertReviewNote(connection, userIdx, multipleProblemIdx) {
+	const upsertReviewNoteQuery = `
+    INSERT INTO  ReviewNote(userIdx, multipleProblemIdx)
+    values(?,?)
+	ON DUPLICATE KEY UPDATE status = 'N'
+    `;
+	const upsertReviewNoteRow = await connection.query(upsertReviewNoteQuery, [userIdx, multipleProblemIdx]);
+	return upsertReviewNoteRow;
+}
+
+// 유저 주관식 오답노트 삽입 or 업데이트  ----> ON DUPLICATE KEY UPDATE 구문으로 조회과정이 한번 더들어가 락 점유 발생 함수 이름만 유지하고 이부분 다시 뺐음
+async function upsertSubjectiveReviewNote(connection, userIdx, subjectiveProblemIdx) {
+	const upsertSubjectiveReviewNoteQuery = `
+    INSERT INTO  ReviewNote(userIdx, subjectiveProblemIdx)
+    values(?,?)
+	ON DUPLICATE KEY UPDATE status = 'N'
+    `;
+	const upsertSubjectiveReviewNoteRow = await connection.query(upsertSubjectiveReviewNoteQuery, [
+		userIdx,
+		subjectiveProblemIdx,
+	]);
+	return upsertSubjectiveReviewNoteRow;
+}
+
+// insertUserExamDetailRecordParams = [userExamRecordIdx, multipleProblemIdx, userAnswer] -> [ [1,1,2], [1,2,3], [1,3,4] .... ]
+
+// 유저 정답율 삽입
+async function insertCorrectRate(connection, insertCorrectRateParams) {
+	const insertCorrectRateQuery = `
+    INSERT INTO CorrectAnswerRate(multipleProblemIdx, userIdx, isCorrect)
+    VALUES ?;
+    `;
+	const insertCorrectRateRow = await connection.query(insertCorrectRateQuery, [insertCorrectRateParams]);
+	return insertCorrectRateRow;
+}
+
+// 문제 오류 신고
+async function insertProblemError(connection, insertProblemErrorParams) {
+	const insertProblemErrorQuery = `
+    INSERT INTO ProblemError(userIdx, multipleProblemIdx, title, content, type, subjectiveLabel)
+    VALUES (?, ?, ?, ?, ?, ?)
+    `;
+	const insertProblemErrorRow = await connection.query(insertProblemErrorQuery, insertProblemErrorParams);
+	return insertProblemErrorRow;
+}
+
+// 오답노트 상태 업데이트
+async function updateReviewNote(connection, reviewNoteIdx) {
+	const updateReviewNoteQuery = `
+    UPDATE ReviewNote
+    SET status = 'N'
+    WHERE reviewNoteIdx = ?
+    `;
+	const updateReviewNoteRow = await connection.query(updateReviewNoteQuery, reviewNoteIdx);
+	return updateReviewNoteRow;
+}
+
+//전기공사기사에 업데이트 할 수 있는 전기기사 회차 인덱스인지 확인
+async function selectEngineerExamDetailIdx(connection, examDetailIdx) {
+	const checkQuery = `select exists(SELECT examDetailIdx FROM engineeo.ExamDetail as ed join engineeo.Exam as e on ed.examIdx=e.examIdx where e.examName='전기기사' and (ed.examRound=1 or ed.examRound=2) and examDetailIdx=? and ed.status='N') as exist;`;
+	const [checkIdxRow] = await connection.query(checkQuery, examDetailIdx);
+	return checkIdxRow;
+}
+
+//전기기사 회차 인덱스에 대응되는 전기공사기사 회차 인덱스 확인
+async function selectWorkEngineerExamDetailIdx(connection, engineerExamDate, engineerExamRound) {
+	const selectQuery = `
+    SELECT examDetailIdx 
+    FROM engineeo.ExamDetail as ed join engineeo.Exam as e on ed.examIdx=e.examIdx 
+    where e.examName='전기공사기사' and ed.examRound=? and examDate=? and ed.status='N';`;
+	const [workExamDetailIdxRow] = await connection.query(selectQuery, [engineerExamRound, engineerExamDate]);
+	return workExamDetailIdxRow;
+}
+
+async function selectEngineerDataset(connection, examDetailIdx) {
+	const selectEngineerDatasetQuery = `
+	SELECT problemNum, problem,answerNum, provisionNum, categoryIdx, status, problemImage, solution, examSubjectIdx, isKatex, isDelete, problemScore, problemUrl, lectureUrl
+	FROM MultipleProblem 
+	WHERE examDetailIdx=? and examSubjectIdx in (2,3,4,5)`;
+	const [selectEngineerDatasetRow] = await connection.query(selectEngineerDatasetQuery, examDetailIdx);
+	return selectEngineerDatasetRow;
+}
+
+async function selectworkEngineerIdxSet(connection, workExamDetailIdx) {
+	const selectworkEngineerIdxSetQuery = `
+	SELECT multipleProblemIdx
+	FROM MultipleProblem 
+	WHERE examDetailIdx=? and examSubjectIdx in (2,3,4,5)`;
+	const [selectworkEngineerIdxSetRow] = await connection.query(selectworkEngineerIdxSetQuery, workExamDetailIdx);
+	return selectworkEngineerIdxSetRow;
+}
+
+// 유저 시험 권한 검증
+// async function checkUserExamDetailAuth(connection, userIdx, examDetailIdx) {
+// 	const checkUserExamDetailAuthQuery = `
+// 	select exists(
+// 	with recursive CTE AS   (SELECT
+// 		p.productIdx, productName, depth, currentExamIdx, isSelling
+// 		FROM ProductRelation pr
+// 		JOIN Product p ON p.productIdx = pr.productIdx
+// 		JOIN UserAuth ua ON ua.productIdx = p.productIdx
+// 		WHERE userIdx = ? and ua.status = 'N'
+// 		UNION ALL
+// 		SELECT
+// 		a.productIdx, p2.productName, p2.depth, p2.currentExamIdx, p2.isSelling
+// 		FROM ProductRelation a
+// 		JOIN Product p2 ON p2.productIdx = a.productIdx
+// 		INNER JOIN CTE b ON a.parentIdx = b.productIdx
+// 		)
+// 		SELECT productIdx, productName, depth, currentExamIdx
+// 		FROM CTE
+// 		where currentExamIdx = ? and depth = 2) as exist`;
+// 	const [checkUserExamDetailAuthRow] = await connection.query(checkUserExamDetailAuthQuery, [userIdx, examDetailIdx]);
+// 	return checkUserExamDetailAuthRow;
+// }
+
+async function getCompanyExamType(connection, companyType) {
+	const getCompanyExamTypeQuery = `
+	with recursive CTE AS   (
+		SELECT 
+		examSortIdx, examSortRef, examSortName, examSortType
+		from ExamSort where examSortName = ? and status='N'
+		UNION ALL
+		SELECT 
+		b.examSortIdx, b.examSortRef, b.examSortName, b.examSortType
+		from ExamSort b 
+		JOIN CTE ON b.examSortRef=CTE.examSortIdx where b.status='N'
+		)
+		SELECT examSortIdx, examSortRef,examSortName, examSortType FROM CTE  
+		where examSortType = "M"  ;
+	`;
+	const [getCompanyExamTypeRow] = await connection.query(getCompanyExamTypeQuery, companyType);
+	return getCompanyExamTypeRow;
+}
+
+async function getCompanyExamField(connection, companyExamTypeIdx) {
+	const getCompanyExamFieldQuery = `
+	with recursive CTE AS   (
+		SELECT 
+		examSortIdx, examSortRef, examSortName, examSortType
+		from ExamSort where examSortIdx = ? and status='N'
+		UNION ALL
+		SELECT 
+		b.examSortIdx, b.examSortRef, b.examSortName, b.examSortType
+		from ExamSort b 
+		JOIN CTE ON b.examSortRef=CTE.examSortIdx where b.status='N'
+		)
+		SELECT  examSortIdx, examSortRef,examSortName, examSortType FROM CTE  
+		where examSortType = "S"  ;
+	`;
+	const [getCompanyExamFieldRow] = await connection.query(getCompanyExamFieldQuery, companyExamTypeIdx);
+	return getCompanyExamFieldRow;
+}
+
+async function selectCompanyExamSort(connection, defaultCompanyName, defaultCompanyDivisionLevel, companySortLabel) {
+	const selectCompanyExamSortQuery = `
+	with recursive CTE AS   (
+	SELECT 
+	examSortIdx, examSortRef, examSortName, examSortType
+	from ExamSort a where examSortName=? and status='N'
+	UNION ALL
+	SELECT 
+	b.examSortIdx, b.examSortRef, b.examSortName, b.examSortType
+	from ExamSort b 
+	JOIN CTE ON b.examSortRef=CTE.examSortIdx where b.status='N'
+	)
+	SELECT distinct  M.examSortIdx, M.examSortRef, M.examSortName, M.examSortType FROM CTE M 
+    left join CTE S on S.examSortRef = M.examSortIdx
+    where M.examSortType=?`;
+	const [selectCompanyExamSortRow] = await connection.query(selectCompanyExamSortQuery, [
+		defaultCompanyName,
+		defaultCompanyDivisionLevel,
+	]);
+	return selectCompanyExamSortRow;
+}
+
+async function selectDefaultDivisionIdx(
+	connection,
+	defaultCompanyName,
+	defaultDivisionLevel,
+	defaultDivisionName,
+	companySortLabel,
+) {
+	const selectDefaultDivisionIdxQuery = `
+	with recursive CTE AS   (
+	SELECT 
+	examSortIdx, examSortRef, examSortName, examSortType
+	from ExamSort a where examSortName=? and status='N'
+	UNION ALL
+	SELECT 
+	b.examSortIdx, b.examSortRef, b.examSortName, b.examSortType
+	from ExamSort b
+	JOIN CTE ON b.examSortRef=CTE.examSortIdx where b.status='N'
+	)
+	SELECT distinct M.examSortIdx FROM CTE M 
+	left join CTE S on S.examSortRef = M.examSortIdx
+	left join CompanyInfo ci on ci.examSortIdx = S.examSortIdx
+    where M.examSortType=? and M.examSortName= ?  and ci.companySortLabel = ?;
+	`;
+	const [selectDefaultDivisionIdxRow] = await connection.query(selectDefaultDivisionIdxQuery, [
+		defaultCompanyName,
+		defaultDivisionLevel,
+		defaultDivisionName,
+		companySortLabel,
+	]);
+	return selectDefaultDivisionIdxRow;
+}
+
+async function selectCompanyList(connection, examFieldList) {
+	const selectCompanyListQuery = `
+	select distinct ce.companyName, e.examIdx, ce.companyUrl, e.prio, es.examSortIdx
+	from ExamSort as es 
+	left join CompanyExam ce on ce.examSortIdx_S = es.examSortIdx and es.status='N'
+	left join Exam e on e.examIdx = ce.examIdx and e.status='N'
+	where ce.examSortIdx_S IN (${examFieldList}) AND ce.status ='N'
+	group by ce.companyName
+	order by ce.prio asc;
+	`;
+	const [selectCompanyListRow] = await connection.query(selectCompanyListQuery);
+	return selectCompanyListRow;
+}
+
+async function selectCompanyExamDetailList(connection, queryParams, isCategory) {
+	let selectCompanyExamDetailListQuery = `
+	select ed.examDetailIdx, e.examIdx, ce.companyUrl, ce.companyName,
+	concat(ce.companyName,' ', year(ed.examDate),'년 ',ed.examRound) as examDetailName, 
+	date_format(ed.examDate, "%Y-%m-%d") as examDate, e.examName, ce.education, ce.domain, e.isBody
+	from ExamDetail as ed 
+	join Exam as e on ed.examIdx=e.examIdx
+	left join CompanyExam as ce on ce.examIdx=e.examIdx 
+	where ed.isPublic = 1 and ce.examSortIdx_S = ? and ce.companyName = ? and ed.status ="N" ${isCategory}
+	`;
+	selectCompanyExamDetailListQuery += ` order by ed.examDate desc;`;
+	const [selectCompanyExamDetailListRow] = await connection.query(selectCompanyExamDetailListQuery, queryParams);
+	return selectCompanyExamDetailListRow;
+}
+
+async function selectCompanyExamDetailListFree(connection, queryParams) {
+	let selectCompanyExamDetailListFreeQuery = `
+	select ed.examDetailIdx, e.examIdx, esi.examSortUrl, concat(e.examName,' ', year(ed.examDate),'년 ',ed.examRound,' (', ed.examDate, ')') as examDetailName, e.examName, ce.education, ce.domain,
+	ed.isPublic
+	from ExamDetail as ed join Exam as e on ed.examIdx=e.examIdx
+	left join CompanyExam as ce on ce.examIdx=e.examIdx 
+	left join ExamSortImage as esi on esi.examSortIdx=e.examSortIdx
+    left join ProductExamDetail ped on ped.examDetailIdx = ed.examDetailIdx
+	where ped.productIdx=99 and e.examSortIdx =?
+	`;
+	//refactoring required
+	if (queryParams.length >= 2) selectCompanyExamDetailListFreeQuery += ` and ce.education=?`;
+	if (queryParams.length == 3) selectCompanyExamDetailListFreeQuery += ` and ce.domain=?`;
+	selectCompanyExamDetailListFreeQuery += ` order by ed.examDate desc;`;
+
+	const [selectCompanyExamDetailListFreeRow] = await connection.query(
+		selectCompanyExamDetailListFreeQuery,
+		queryParams,
+	);
+	return selectCompanyExamDetailListFreeRow;
+}
+
+async function selectCompanyFilterList(connection, companyExamField, filterType) {
+	let selectCompanyFilterListQuery = `
+		SELECT distinct ${filterType} FROM CompanyExam
+		where examSortIdx_S=?
+		order by companyName asc;
+	`;
+	//refactoring required
+	const [selectCompanyFilterListRow] = await connection.query(selectCompanyFilterListQuery, companyExamField);
+	return selectCompanyFilterListRow;
+}
+
+async function selectExamDivision(connection, examIdx, sortType) {
+	const selectExamDivisionQuery = `
+	with recursive CTE AS   (SELECT 
+	es.examSortIdx, es.examSortRef, es.examSortName, es.examSortType
+	from Exam e join ExamSort es on e.examSortIdx=es.examSortIdx
+	where e.examIdx=?
+	UNION ALL
+	SELECT
+	a.examSortIdx, a.examSortRef, a.examSortName, a.examSortType
+	from ExamSort a
+	JOIN CTE b ON a.examSortIdx=b.examSortRef
+	)
+	select examSortIdx, examSortName from CTE where examSortType=?;
+	`;
+
+	const [selectExamDivisionRow] = await connection.query(selectExamDivisionQuery, [examIdx, sortType]);
+	return selectExamDivisionRow;
+}
+async function selectExamSortList(connection, examSortName) {
+	const selectExamSortListQuery = `
+	with recursive CTE AS   (
+		SELECT 
+		examSortIdx, examSortRef, examSortName, examSortType
+		from ExamSort a where examSortName= ? and status='N'
+		UNION ALL
+		SELECT 
+		b.examSortIdx, b.examSortRef, b.examSortName, b.examSortType
+		from ExamSort b
+		JOIN CTE ON b.examSortRef=CTE.examSortIdx where b.status='N'
+		)
+		SELECT examSortIdx
+		FROM CTE 
+	`;
+
+	const [selectExamSortListRow] = await connection.query(selectExamSortListQuery, examSortName);
+	return selectExamSortListRow;
+}
+
+async function selectExamSortListWithField(connection, examField, examSortRef) {
+	const selectExamSortListQuery = `
+	with recursive CTE AS   (
+		SELECT 
+		examSortIdx, examSortRef, examSortName, examSortType
+		from ExamSort a where examSortName= ? and status='N' and examSortRef = ?
+		UNION ALL
+		SELECT 
+		b.examSortIdx, b.examSortRef, b.examSortName, b.examSortType
+		from ExamSort b
+		JOIN CTE ON b.examSortRef=CTE.examSortIdx where b.status='N'
+		)
+		SELECT examSortIdx
+		FROM CTE 
+	`;
+
+	const [selectExamSortListRow] = await connection.query(selectExamSortListQuery, [examField, examSortRef]);
+	return selectExamSortListRow;
+}
+
+// examSort의 대분류에 대한 인덱스를 가져오는 DAO
+async function selectLargeExamIdx(connection, examSortName) {
+	const selectLargeExamIdxQuery = `
+		select examSortIdx from ExamSort where examSortName = ? and examSortType = 'L';
+	`;
+	const [selectLargeExamIdxRow] = await connection.query(selectLargeExamIdxQuery, examSortName);
+	return selectLargeExamIdxRow;
+}
+
+async function selectMediumExamIdx(connection, examSortRef) {
+	const selectMediumExamIdxQeury = `
+		select examSortIdx from ExamSort where examSortRef = ? and examSortType = 'M';
+	`;
+	const [selectMediumExamIdxRow] = await connection.query(selectMediumExamIdxQeury, [examSortRef]);
+	return selectMediumExamIdxRow;
+}
+
+// examSort의 대분류에 대한 이름을 가져오는 DAO
+async function selectLargeExamName(connection, examSortRef) {
+	const selectLargeExamIdxQuery = `
+		select examSortRef from ExamSort where examSortIdx= ?
+	`;
+	const [[selectLargeExamIdxRow]] = await connection.query(selectLargeExamIdxQuery, examSortRef);
+	return selectLargeExamIdxRow;
+}
+
+// 시험의 examSortRef에 대한 인덱스를 가져오는 DAO
+async function selectExamSortRefIdx(connection, examIdx) {
+	const selectExamSortRefQuery = `
+		select es.examSortRef, es.examSortName from ExamSort es
+		left join Exam e on e.examSortIdx = es.examSortIdx
+		left join CompanyExam ce on ce.examSortIdx_S = es.examSortIdx
+		where ce.examIdx = ? or e.examIdx = ?;
+	`;
+	const [[selectExamSortRefRow]] = await connection.query(selectExamSortRefQuery, [examIdx, examIdx]);
+	return selectExamSortRefRow;
+}
+
+async function getExamDivisionByExamIdx(connection, examIdx, examSortType) {
+	const getExamDivisionByExamIdxQuery = `with recursive CTE as (
+		select es.examSortRef, es.examSortIdx, es.examSortName, es.examSortType 
+		from ExamSort es
+		left join Exam e on e.examSortIdx = es.examSortIdx
+		left join CompanyExam ce on ce.examSortIdx_S = es.examSortIdx
+		where (ce.examIdx = ? or e.examIdx = ?)
+		union all
+		select b.examSortRef, b.examSortIdx, b.examSortName, b.examSortType
+		from ExamSort b
+		join CTE on b.examSortIdx = CTE.examSortRef
+		) 
+		select examSortName from CTE c where c.examSortType=?`;
+	const [getExamDivisionByExamIdxRow] = await connection.query(getExamDivisionByExamIdxQuery, [
+		examIdx,
+		examIdx,
+		examSortType,
+	]);
+	return getExamDivisionByExamIdxRow;
+}
+
+// examSort의 smallType의 examSortRef를 가져오는 쿼리.
+async function selectSmallExamIdx(connection, examSortName) {
+	const selectSmallExamIdxQuery = `
+	with recursive CTE AS   (
+		SELECT 
+		examSortIdx, examSortRef, examSortName, examSortType
+		from ExamSort a where examSortName= ? and status='N'
+		UNION ALL
+		SELECT 
+		b.examSortIdx, b.examSortRef, b.examSortName, b.examSortType
+		from ExamSort b
+		JOIN CTE ON b.examSortRef=CTE.examSortIdx where b.status='N'
+		)
+		SELECT examSortRef
+		FROM CTE c where c.examSortType="S" group by c.examSortRef;
+	`;
+	const [selectSmallExamIdxRow] = await connection.query(selectSmallExamIdxQuery, examSortName);
+	return selectSmallExamIdxRow;
+}
+
+// examSortRef에 해당하는 시험 구분 이름 가져오기.
+async function selectExamSortRefName(connection, examSortRef) {
+	const selectExamSortRefNameQuery = `
+		select examSortName from ExamSort
+		where examSortIdx = ?
+	`;
+	const [[selectExamSortRefNameRow]] = await connection.query(selectExamSortRefNameQuery, examSortRef);
+	return selectExamSortRefNameRow.examSortName;
+}
+async function getExamDetailPublic(connection, examIdx) {
+	const getExamDetailPublicQuery = `
+	select exists (
+		select * from Exam  e left join ExamDetail ed on e.examIdx = ed.examIdx where
+		e.examIdx = ?  and ed.isPublic = 1 ) as ExamDetailPublic; 
+	`;
+	const [[ExamDetailPublic]] = await connection.query(getExamDetailPublicQuery, examIdx);
+	return ExamDetailPublic;
+}
+
+// 과목별 각 문제의 갯수 가져오기 => 랜덤모의고사의 과목별 문제를 보여주기 위해서
+async function getRandomExamSubjectProblemCount(connection, examIdx, examSubjectIdx) {
+	const getExamSubjectProblemCountQuery = `
+	select count(mp.multipleProblemIdx) as count from ExamSubject es
+	left join ExamSubjectMulti esm on esm.examSubjectIdx = es.examSubjectIdx
+	left join Exam e on e.examIdx = esm.examIdx
+	left join ProblemSubject ps on ps.examSubjectIdx = es.examSubjectidx
+	left join MultipleProblem mp on mp.multipleProblemIdx = ps.multipleProblemIdx
+	left join ProblemExam pe on pe.multipleProblemIdx = mp.multipleProblemIdx
+    left join ExamDetail ed on ed.examDetailIdx = pe.examDetailIdx 
+	where e.examIdx = ? and es.examSubjectIdx = ? and ed.ispublic = 1
+	`;
+	const [[getRandomExamSubjectProblemCountRow]] = await connection.query(getExamSubjectProblemCountQuery, [
+		examIdx,
+		examSubjectIdx,
+	]);
+	return getRandomExamSubjectProblemCountRow;
+}
+// 과목별 각 주관식 문제의 갯수 가져오기 => 랜덤모의고사의 과목별 문제를 보여주기 위해서
+async function getRandomSubjectiveExamSubjectProblemCount(connection, examIdx, examSubjectIdx) {
+	const getRandomSubjectiveExamSubjectProblemCountQuery = `
+	select count(sp.subjectiveProblemIdx) as count from ExamSubject es
+	left join ExamSubjectMulti esm on esm.examSubjectIdx = es.examSubjectIdx
+	left join Exam e on e.examIdx = esm.examIdx
+	left join SubjectiveProblemSubject sps on sps.examSubjectIdx = es.examSubjectidx
+	left join SubjectiveProblem sp on sp.subjectiveProblemIdx = sps.subjectiveProblemIdx
+    left join SubjectiveProblemExam spe on spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+    left join ExamDetail ed on ed.examDetailIdx = spe.examDetailIdx 
+	where e.examIdx = ? and es.examSubjectIdx = ? and ed.ispublic = 1;
+	`;
+	const [[getRandomSubjectiveExamSubjectProblemCountRow]] = await connection.query(
+		getRandomSubjectiveExamSubjectProblemCountQuery,
+		[examIdx, examSubjectIdx],
+	);
+	return getRandomSubjectiveExamSubjectProblemCountRow;
+}
+
+// 문제의 빈도수 가져오기
+async function getFrequency(connection, problemIdx, subjectiveLabel) {
+	const getFrequencyQuery = `
+	select history from ProblemFrequency where problemIdx = ? and subjectiveLabel =? ;
+	`;
+	const [[getFrequencyRow]] = await connection.query(getFrequencyQuery, [problemIdx, subjectiveLabel]);
+	return getFrequencyRow;
+}
+
+// 자격증 시험에 대한 이름 조회
+async function getExamInfo(connection, examSortIdx) {
+	const getExamInfoQuery = `
+		SELECT e.examIdx,e.examName FROM ExamSort es
+		LEFT JOIN Exam e ON e.examSortIdx = es.examSortIdx
+		WHERE es.examSortIdx = ? AND e.status = "N";
+	`;
+
+	const [getExamInfoRow] = await connection.query(getExamInfoQuery, examSortIdx);
+	return getExamInfoRow;
+}
+
+// 기업 관련 시험에 대한 이름 조회
+async function getCompanyExamInfo(connection, examSortIdx) {
+	const getExamInfoQuery = `
+		SELECT e.examIdx, e.examName, ce.category FROM ExamSort es
+		LEFT JOIN CompanyExam ce ON ce.examSortIdx_S = es.examSortIdx
+		LEFT JOIN Exam e ON e.examIdx = ce.examIdx
+		WHERE es.examSortIdx = ? AND e.status = "N";
+	`;
+
+	const [getExamInfoRow] = await connection.query(getExamInfoQuery, examSortIdx);
+	return getExamInfoRow;
+}
+
+// 기업의 소분류에 따른 회사들 가져오기
+async function getCompanysOfField(connection, smallExamSortIdx) {
+	const getCompanysOfFieldQuery = `
+	select distinct examSortIdx_S, companyName from CompanyExam 
+	where examSortIdx_S = ? and status = 'N'
+	;
+	`;
+	const [getCompanysOfField] = await connection.query(getCompanysOfFieldQuery, smallExamSortIdx);
+	return getCompanysOfField;
+}
+
+async function getCategoryList(connection, companyExamType) {
+	const getCategoryListQuery = `
+		select distinct category
+		from ExamSort es
+		left join CompanyExam ce on ce.examSortIdx_S = es.examSortIdx
+		where es.examSortRef = ? and ce.category is not null;
+	`;
+	const [getCategoryListRow] = await connection.query(getCategoryListQuery, companyExamType);
+	return getCategoryListRow;
+}
+
+async function getFieldMatchNameCheck(connection, companyExamField, companyName) {
+	const getFieldMatchNameCheckQuery = `
+		select exists ( 
+			select * from ExamSort es
+			left join CompanyExam ce on ce.examSortIdx_S = es.examSortIdx
+			where ce.examSortIdx_S = ? and ce.companyName = ?
+		) as exist
+	`;
+	const [getFieldMatchNameCheckRow] = await connection.query(getFieldMatchNameCheckQuery, [
+		companyExamField,
+		companyName,
+	]);
+	return getFieldMatchNameCheckRow;
+}
+
+async function getExamSortType(connection, companyExamField) {
+	const getFieldTypeCheckQuery = `
+		select examSortType, examSortIdx from ExamSort es
+		where es.examSortIdx = ?;
+	`;
+	const [getFieldTypeCheckRow] = await connection.query(getFieldTypeCheckQuery, companyExamField);
+	return getFieldTypeCheckRow;
+}
+
+async function getCompanyUrl(connection, queryParams) {
+	const getCompanyInfoByExamIdxQuery = `
+		select distinct ce.companyName, ce.companyUrl
+		from CompanyExam ce
+		where ce.examSortIdx_S = ? and ce.companyName=? and ce.status='N';
+	`;
+	const [getCompanyInfoByExamIdxRow] = await connection.query(getCompanyInfoByExamIdxQuery, queryParams);
+	return getCompanyInfoByExamIdxRow;
+}
+
+module.exports = {
+	selectExamSortList,
+	selectExamSortListWithField,
+	selectExamIdx,
+	selectExamDetailIdx,
+	selectExamSubjectIdx,
+	selectMultipleProblemAnswer,
+	selectSubjectMultiIdx,
+	selectReviewNoteIdx,
+	selectSubjectiveReviewNoteIdx,
+	selectMultipleProblemNumIdx,
+	selectSubjectiveProblemNumIdx,
+	selectMultipleProblemInfo,
+	selectExamMatchCheck,
+	selectSubjectMultiIdx,
+	selectExamList,
+	selectExamDate,
+	selectExamSortRef,
+	selectExamField,
+	selectExam,
+	selectExamFieldList,
+	selectExamInfo,
+	selectExamAll,
+	selectExamSort,
+	selectLargeExamIdx,
+	selectLargeExamName,
+	selectMediumExamIdx,
+	selectExamSortRefIdx,
+	selectSmallExamIdx,
+	selectExamDetail,
+	selectSubjectiveOfExam,
+	selectExamSubject,
+	selectExamSubjectByIdx,
+	selectExamSubjectNamebyIdx,
+	selectExamSubjectInfo,
+	selectSubjectiveExamSubjectInfo,
+	selectProblemByExamDetail,
+	selectProblemByExamSubject,
+	selectSubjectiveProblemByExamSubject,
+	selectMultipleProblemIdx,
+	selectSubjectiveProblemIdx,
+	selectMultipleProblemIdxes,
+	selectQuestion,
+	selectQuestions,
+	selectSubjectQuestion,
+	selectSubjectQuestions,
+	selectSubjectiveProblem,
+	selectSubjectiveProblems,
+	selectExamDetailInfo,
+	selectProblemFrequency,
+	selectExamReviewNote,
+	selectExamUrl,
+	selectExamSubjectRandom,
+	selectExamSubjectRandomByIdx,
+	selectSubjectiveExamSubjectRandomByIdx,
+	insertUserExamRecord,
+	insertUserExamDetailRecord,
+	insertReviewNote,
+	upsertReviewNote,
+	upsertSubjectiveReviewNote,
+	insertCorrectRate,
+	insertProblemError,
+	updateReviewNote,
+	selectEngineerExamDetailIdx,
+	selectWorkEngineerExamDetailIdx,
+	selectExamDetailBasicInfo,
+	selectEngineerDataset,
+	selectworkEngineerIdxSet,
+	selectCompanyExamSort,
+	getCompanyExamType,
+	getCompanyExamField,
+	selectDefaultDivisionIdx,
+	selectCompanyList,
+	selectCompanyExamDetailList,
+	selectCompanyExamDetailListFree,
+	selectCompanyFilterList,
+	selectExamDivision,
+	selectNewKecRandomProblem,
+	selectExamSortRefName,
+	getExamDetailPublic,
+	getRandomExamSubjectProblemCount,
+	getRandomSubjectiveExamSubjectProblemCount,
+	getFrequency,
+	selectSubjectiveProblemInfo,
+	selectUserExamAuth,
+	getExamDivisionByExamIdx,
+	getExamInfo,
+	getCompanyExamInfo,
+	getCompanysOfField,
+	getCategoryList,
+	getFieldMatchNameCheck,
+	getExamSortType,
+	getCompanyUrl,
+};
diff --git a/_old/src/Exam/examProvider.js b/_old/src/Exam/examProvider.js
new file mode 100644
index 0000000..9b0e878
--- /dev/null
+++ b/_old/src/Exam/examProvider.js
@@ -0,0 +1,1137 @@
+const { pool } = require("../../config/database");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const examDao = require("./examDao");
+const storeDao = require("../Store/storeDao");
+const { get } = require("../../config/email");
+const errorResponse = require("../../utils/errorResponse");
+const asyncHandler = require("../../utils/asyncHandler");
+
+// 시험 인덱스 체크
+exports.examCheck = async function (examIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examCheckResult = await examDao.selectExamIdx(connection, examIdx);
+		if (examCheckResult.length === 0) throw new errorResponse(baseResponse.EXAM_NOT_EXIST, 404);
+		if (examCheckResult[0].status === "Y") throw new errorResponse(baseResponse.ISDELETED, 400);
+		return examCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 디테일 인덱스 체크
+exports.examDetailCheck = async function (examDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examDetailCheckResult = await examDao.selectExamDetailIdx(connection, examDetailIdx);
+		if (examDetailCheckResult.length === 0) throw new errorResponse(baseResponse.EXAMDETAIL_NOT_EXIST, 404);
+		if (examDetailCheckResult[0].exist === 0) throw new errorResponse(baseResponse.EXAMDETAIL_NOT_EXIST, 404);
+		if (examDetailCheckResult[0].status === "Y") throw new errorResponse(baseResponse.ISDELETED, 400);
+		return examDetailCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+// 시험 디테일 인덱스 체크
+exports.examMatchCheck = async function (examDetailIdx, examIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examMatchCheckResult = await examDao.selectExamMatchCheck(connection, examDetailIdx, examIdx);
+		if (examMatchCheckResult.length === 0) throw new errorResponse(baseResponse.EXAM_NOT_MATCH, 404);
+		if (examMatchCheckResult.status === "Y") throw new errorResponse(baseResponse.ISDELETED, 400);
+		return examMatchCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 그 시험에서 과목 인덱스 존재하는지 체크
+exports.subjectMatchCheck = async function (examIdx, subjectList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const subjectMatchCheckResult = await examDao.selectSubjectMultiIdx(connection, examIdx, subjectList);
+
+		return subjectMatchCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+// 시험의 examSort의 LargeType 알아오기
+exports.getExamSortLargeIdx = async function (examSortName) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamSortLargeIdxResult = await examDao.selectLargeExamIdx(connection, examSortName); // 자격증 목록
+		if (!getExamSortLargeIdxResult) throw new errorResponse(baseResponse.EXAM_SORT_NOT_EXISTS, 404);
+		return getExamSortLargeIdxResult[0].examSortIdx;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+//시험의 examSort의 mediumType 알아오기.
+exports.selectMediumExamIdx = async function (examSortName) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamSortSmallIdxResult = await examDao.selectMediumExamIdx(connection, examSortName); // 자격증 목록
+		return getExamSortSmallIdxResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+//시험의 examSort의 smallType 알아오기.
+exports.getExamSortSmallIdx = async function (examSortName) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamSortSmallIdxResult = await examDao.selectSmallExamIdx(connection, examSortName); // 자격증 목록
+		return getExamSortSmallIdxResult.map((x) => x.examSortRef);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 자격증의 examSort의 examSortRef는 무엇인지 알아오기.
+exports.getExamSortRef = async function (examSortName) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamSortRefResult = await examDao.selectExamSortRef(connection, examSortName); // 자격증 목록
+		return getExamSortRefResult.examSortIdx;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 종류에 따른 인덱스 목록
+exports.getExamSortList = async function (CERTIFICATIONEXAM) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const certificationExamSortList = await examDao.selectExamSortList(connection, CERTIFICATIONEXAM); // 자격증 목록
+		return certificationExamSortList.map((v) => {
+			return v.examSortIdx;
+		});
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 과목 인덱스 체크
+exports.examSubjectCheck = async function (examSubjectIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examSubjectCheckResult = await examDao.selectExamSubjectIdx(connection, examSubjectIdx);
+		if (examSubjectCheckResult.length === 0) throw new errorResponse(baseResponse.EXAMSUBJECT_NOT_EXIST, 404);
+		if (examSubjectCheckResult[0].status === "Y") throw new errorResponse(baseResponse.ISDELETED, 400);
+		return examSubjectCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 객관식 문제 인덱스 체크
+exports.multipleProblemCheck = async function (multipleProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const multipleProblemIdxCheckResult = await examDao.selectMultipleProblemIdx(connection, multipleProblemIdx);
+		if (multipleProblemIdxCheckResult.length === 0) throw new errorResponse(baseResponse.PROBLEM_NOT_EXIST, 404);
+		if (multipleProblemIdxCheckResult[0].status === "Y") throw new errorResponse(baseResponse.ISDELETED, 404);
+		return multipleProblemIdxCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 주관식 문제 인덱스 체크
+exports.subjectiveProblemCheck = async function (subjectiveProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const problemCheckResult = await examDao.selectSubjectiveProblemIdx(connection, subjectiveProblemIdx);
+		if (problemCheckResult.length === 0) throw new errorResponse(baseResponse.PROBLEM_NOT_EXIST, 404);
+		if (problemCheckResult[0].status === "Y") throw new errorResponse(baseResponse.ISDELETED, 400);
+		return problemCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+//문제 인덱스 체크(복수)
+exports.multipleProblemsCheck = async function (multipleProblemIdxes, multipleParams) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const problemCheckResult = await examDao.selectMultipleProblemIdxes(
+			connection,
+			multipleProblemIdxes,
+			multipleParams,
+		);
+		if (problemCheckResult.length !== multipleProblemIdxes.length)
+			throw new errorResponse(baseResponse.PROBLEM_NOT_EXIST, 404);
+		return problemCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문제 인덱스 체크
+exports.multipleProblemNumCheck = async function (examDetailIdx, problemNum) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const problemNumCheckResult = await examDao.selectMultipleProblemNumIdx(connection, examDetailIdx, problemNum);
+		if (problemNumCheckResult[0].exist === 1) throw new errorResponse(baseResponse.PROBLEMIDX_EXIST, 400);
+		return problemNumCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 주관식 문제 인덱스 체크
+exports.subjectiveProblemNumCheck = async function (examDetailIdx, problemNum) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const problemNumCheckResult = await examDao.selectSubjectiveProblemNumIdx(connection, examDetailIdx, problemNum);
+		return problemNumCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 시험 회차 권한 체크(구) store쪽으로 이동
+// exports.userExamDetailAuthCheck = async function (userIdx, examDetailIdx) {
+// 	const connection = await pool.getConnection(async (conn) => conn);
+// 	try {
+// 		const checkResult = await examDao.checkUserExamDetailAuth(connection, userIdx, examDetailIdx);
+// 		return checkResult;
+// 	} catch (error) {
+// 		throw error;
+//
+// 	} finally {
+// 		connection.release();
+// 	}
+// };
+
+// 문제 인덱스 체크
+exports.getAnswer = async function (examDetailIdx, userSubject) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const condition = userSubject == 1 ? "" : "left";
+		const getAnswerResult = await examDao.selectMultipleProblemAnswer(connection, condition, examDetailIdx);
+		return getAnswerResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문제 인덱스 체크
+exports.reviewNoteIdxCheck = async function (multipleProblemIdx, userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const reviewNoteIdxResult = await examDao.selectReviewNoteIdx(connection, multipleProblemIdx, userIdx);
+		return reviewNoteIdxResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 조회
+exports.getExam = async function (where, whereInfo, examField) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamRows = await examDao.selectExam(connection, where, whereInfo, examField);
+		return resultResponse(baseResponse.SUCCESS, getExamRows);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getExamFieldList = async function (examSortRef) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamFieldListResult = await examDao.selectExamFieldList(connection, examSortRef);
+		return resultResponse(baseResponse.SUCCESS, getExamFieldListResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+//시험의 field에 대해서 알아오는 API
+exports.getExamField = async function (largeExamIdx, mediumExamIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamFieldResult = await examDao.selectExamField(connection, largeExamIdx, mediumExamIdx);
+		if (!getExamFieldResult && largeExamIdx != 0) throw new errorResponse(baseResponse.EXAMSORTREF_NOT_EXIST, 404);
+		return getExamFieldResult?.examSortIdx;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+// 전체 시험 조회
+exports.getExamAll = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamRows = await examDao.selectExamAll(connection);
+		return resultResponse(baseResponse.SUCCESS, getExamRows);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 특정 시험 목록 조회
+exports.getExamDetail = async function (examIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamDetailRows = await examDao.selectExamDetail(connection, examIdx);
+		return getExamDetailRows;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getExamDetailBasicInfo = async function (examDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamDetailRows = await examDao.selectExamDetailBasicInfo(connection, examDetailIdx);
+		return getExamDetailRows;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 한 문제 조회
+exports.getProblem = async function (problemIdx, subjectiveLabel) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let questionCount;
+		let getProblemRows;
+		// 시험 정보
+		let problemInfo =
+			subjectiveLabel == "1"
+				? await examDao.selectSubjectiveProblemInfo(connection, problemIdx)
+				: await examDao.selectMultipleProblemInfo(connection, problemIdx);
+		let problem =
+			subjectiveLabel == "1"
+				? {
+						categoryIdx: problemInfo[0].categoryIdx,
+						subject: problemInfo[0].subject,
+						examSubjectIdx: problemInfo[0].examSubjectIdx,
+						examSubjectName: problemInfo[0].examSubjectName,
+						isDelete: problemInfo[0].isDelete,
+						isKatex: problemInfo[0].isKatex,
+						problemUrl: problemInfo[0].problemUrl,
+						lectureUrl: problemInfo[0].lectureUrl,
+						multipleProblemIdx: problemInfo[0].subjectiveProblemIdx,
+						problem: problemInfo[0].problem,
+						problemNum: problemInfo[0].problemNum,
+						problemScore: problemInfo[0].problemScore,
+						solution: problemInfo[0].solution,
+				  }
+				: {
+						answerNum: problemInfo[0].answerNum,
+						categoryIdx: problemInfo[0].categoryIdx,
+						subject: problemInfo[0].subject,
+						examSubjectIdx: problemInfo[0].examSubjectIdx,
+						examSubjectName: problemInfo[0].examSubjectName,
+						isDelete: problemInfo[0].isDelete,
+						isKatex: problemInfo[0].isKatex,
+						problemUrl: problemInfo[0].problemUrl,
+						lectureUrl: problemInfo[0].lectureUrl,
+						multipleProblemIdx: problemInfo[0].multipleProblemIdx,
+						problem: problemInfo[0].problem,
+						problemNum: problemInfo[0].problemNum,
+						problemScore: problemInfo[0].problemScore,
+						provisionNum: problemInfo[0].provisionNum,
+						solution: problemInfo[0].solution,
+						questions: await examDao.selectQuestion(connection, problemIdx),
+				  };
+
+		let examDetail = await examDao.selectExamDetailInfo(connection, problemInfo[0].examDetailIdx);
+		let examIdx = problemInfo[0].examIdx;
+		if (!examDetail.length) return basickResponse(baseResponse.EXAMDETAIL_NOT_EXIST);
+		if (examDetail[0].questionType && examDetail[0].questionType.indexOf(5) !== -1) questionCount = 5;
+		else questionCount = 4;
+
+		let examInfo = {
+			...examDetail[0],
+			questionCount: questionCount,
+			problem: problem,
+		};
+		examInfo.examRound += "회";
+		// 시험 과목 정보\
+		let examSubject = await examDao.selectExamSubject(connection, examIdx);
+		let subject = [];
+		for (let subjectInfo of examSubject) subject.push(subjectInfo);
+
+		getProblemRows = { examInfo, subject };
+
+		return resultResponse(baseResponse.SUCCESS, getProblemRows);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문제 조회
+exports.getProblemList = async function (examIdx, examDetailIdx, subjectList, isAuth) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	let examSubject, questionCount;
+	try {
+		let getProblemListRows,
+			subject = [],
+			subjectsTmp = [];
+		let subjectTmp, getProblem;
+		// 시험 정보
+		let examDetail = await examDao.selectExamDetailInfo(connection, examDetailIdx);
+		if (!examDetail.length) return basickResponse(baseResponse.EXAMDETAIL_NOT_EXIST);
+		// if (!examDetail[0].publicLevel) isAuth = 1;
+
+		const subjectiveLabel = examDetail[0].subjectiveLabel;
+		if (subjectiveLabel == 0) {
+			// 객관식 문제의경우.
+			if (examDetail[0].questionType && examDetail[0].questionType.indexOf(5) !== -1) questionCount = 5;
+			else questionCount = 4;
+			let examInfo = {
+				...examDetail[0],
+				questionCount: questionCount,
+			};
+			examInfo.examRound += "회";
+			// 시험 과목별 문제
+			if (subjectList.length < 1) {
+				examSubject = await examDao.selectExamSubject(connection, examIdx);
+			} else {
+				for (let i = 0; i < subjectList.length; i++) {
+					subjectTmp = await examDao.selectExamSubjectByIdx(connection, examIdx, subjectList[i]); // 과목 인덱스, 정보 반환
+					subjectsTmp.push(subjectTmp[0]);
+				}
+				examSubject = subjectsTmp;
+			}
+			for (let subjectInfo of examSubject) {
+				let problems = [];
+				getProblem = await examDao.selectProblemByExamSubject(connection, examDetailIdx, subjectInfo.examSubjectIdx);
+				for (let object of getProblem) {
+					// const { multipleProblemIdx } = object;
+					let questions = await examDao.selectQuestion(connection, object.multipleProblemIdx);
+
+					// const examHistory = await examDao.getFrequency(connection, multipleProblemIdx, 0);
+					// if (examHistory) {
+					// 	const { history } = examHistory;
+					// 	object["examHistory"] = history;
+					// }
+					// if (examHistory) {
+					// object.examHistory = examHistory;
+					// }
+					let problem = {
+						...object,
+						questions: questions,
+					};
+					// if (examHistory in object) {
+					// console.log(object);
+					// }
+					// console.log(problem);
+					if (isAuth == 0) {
+						const flag = examDetail[0].publicLevel;
+						switch (flag) {
+							case 0: //문제 해설 모두 무료.
+								break;
+							case 1: //문제 무료, 해설 유료
+								problems.push(problem);
+								delete problem.solution;
+								break;
+							case 2: // 문제 해설 모두 유료
+								return resultResponse(baseResponse.USERAUTH_NOT_EXIST, isAuth);
+						}
+						// if (examDetail[0].publicLevel !== 0) delete problem.solution;
+						// if (examDetail[0].publicLevel !== 2) problems.push(problem);
+					} else {
+						problems.push(problem);
+					}
+				}
+				subjectInfo.problems = problems;
+				subject.push(subjectInfo);
+			}
+			getProblemListRows = { examInfo, subject, isAuth: isAuth };
+		} else {
+			let examInfo = {
+				...examDetail[0],
+			};
+			examInfo.examRound += "회";
+			// 시험 과목별 문제
+			if (subjectList.length < 1) {
+				examSubject = await examDao.selectExamSubject(connection, examIdx);
+			} else {
+				for (let i = 0; i < subjectList.length; i++) {
+					subjectTmp = await examDao.selectExamSubjectByIdx(connection, examIdx, subjectList[i]); // 과목 인덱스, 정보 반환
+					subjectsTmp.push(subjectTmp[0]);
+				}
+				examSubject = subjectsTmp;
+			}
+			for (let subjectInfo of examSubject) {
+				let problems = [];
+				getSubjectiveProblem = await examDao.selectSubjectiveProblemByExamSubject(
+					connection,
+					examDetailIdx,
+					subjectInfo.examSubjectIdx,
+				);
+				for (let object of getSubjectiveProblem) {
+					// const { subjectiveProblemIdx } = object;
+
+					// const examHistory = await examDao.getFrequency(connection, subjectiveProblemIdx, 1);
+					// if (examHistory) {
+					// 	const { history } = examHistory;
+					// 	object["examHistory"] = history;
+					// }
+
+					let problem = {
+						...object,
+					};
+					if (isAuth == 0) {
+						const flag = examDetail[0].publicLevel;
+						switch (flag) {
+							case 0: //문제 해설 모두 무료.
+								break;
+							case 1: //문제 무료, 해설 유료
+								problems.push(problem);
+								delete problem.solution;
+								break;
+							case 2: // 문제 해설 모두 유료
+								return resultResponse(baseResponse.USERAUTH_NOT_EXIST, isAuth);
+						}
+						// if (examDetail[0].publicLevel !== 0) delete problem.solution;
+						// if (examDetail[0].publicLevel !== 2) problems.push(problem);
+					} else {
+						problems.push(problem);
+					}
+				}
+				subjectInfo.problems = problems;
+				subject.push(subjectInfo);
+			}
+			getProblemListRows = { examInfo, subject, isAuth: isAuth };
+		}
+
+		return resultResponse(baseResponse.SUCCESS, getProblemListRows);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+//공기업 문제 조회
+exports.getCompanyProblemList = async function (examIdx, examDetailIdx, isAuth) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let getProblemListRows;
+		let getProblem;
+		let problems = [],
+			subject = [];
+		// 시험 정보
+		let examDetail = await examDao.selectExamDetailInfo(connection, examDetailIdx);
+		if (examDetail[0].publicLevel == 0) isAuth = 1;
+		if (!examDetail.length) return basickResponse(baseResponse.EXAMDETAIL_NOT_EXIST);
+		getProblem = await examDao.selectProblemByExamDetail(connection, examDetailIdx);
+
+		let examInfo = {
+			examIdx: examIdx,
+			...examDetail[0],
+			problemCount: getProblem.length,
+		};
+
+		for (let object of getProblem) {
+			let questions = await examDao.selectQuestion(connection, object.problemIdx);
+
+			// const examHistory = await examDao.getFrequency(connection, multipleProblemIdx, 0);
+			// if (examHistory) {
+			// 	const { history } = examHistory;
+			// 	object["examHistory"] = history;
+			// }
+			let problem = {
+				...object,
+				questions: questions,
+			};
+			// if (isAuth == 1) problem.solution = object.solution;
+			// else problem.solution = null;
+			if (!isAuth) {
+				if (examDetail[0].publicLevel !== 0) delete problem.solution;
+				if (examDetail[0].publicLevel !== 2) problems.push(problem);
+			} else {
+				problems.push(problem);
+			}
+		}
+		subject.push({
+			examSubjectIdx: "",
+			examSubjectNum: "",
+			examSubjectName: "",
+			passScore: "",
+			problems: problems,
+		});
+		getProblemListRows = { examInfo, subject, isAuth: isAuth };
+		return resultResponse(baseResponse.SUCCESS, getProblemListRows);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 시험 정보 조회
+exports.getExamInfo = async function (examIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamInfoRows = await examDao.selectExamInfo(connection, examIdx);
+		const subjectList = await examDao.selectExamSubject(connection, examIdx);
+
+		let examInfo = {
+			...getExamInfoRows[0],
+			subjectList: subjectList,
+		};
+		return resultResponse(baseResponse.SUCCESS, examInfo);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 특정 시험 과목 목록 조회
+exports.getExamSubjectInfo = async function (examDetailIdx, examIdx, subjectiveLabel) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		// 여기서 해당 과목에 대한 문제 갯수를 찾아야한다.
+		const getExamSubjectInfoRows =
+			examDetailIdx != 0 // 0이면 랜덤모의고사에서 시험에 대한 과목 인덱스들을 가져오기 위해서.
+				? subjectiveLabel == 0
+					? await examDao.selectExamSubjectInfo(connection, examDetailIdx)
+					: await examDao.selectSubjectiveExamSubjectInfo(connection, examDetailIdx)
+				: await examDao.selectExamSubject(connection, examIdx);
+
+		await Promise.all(
+			getExamSubjectInfoRows.map(async (o) => {
+				if (o.passScore) {
+					// passScore 가 있다는 것은 랜덤모의고사라는것
+					delete o.passScore;
+					let { count } =
+						subjectiveLabel == 0
+							? await examDao.getRandomExamSubjectProblemCount(connection, examIdx, o.examSubjectIdx)
+							: await examDao.getRandomSubjectiveExamSubjectProblemCount(connection, examIdx, o.examSubjectIdx);
+					o.problemCount = count;
+				}
+				return o;
+			}),
+		);
+		return resultResponse(baseResponse.SUCCESS, getExamSubjectInfoRows);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 특정 주관식 시험 과목 목록 조회 => 지워야 할 수도 있음.
+exports.getSubjectiveExamSubjectInfo = async function (examDetailIdx, examIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getSubjectiveExamSubjectInfoRows =
+			examDetailIdx != 0
+				? await examDao.selectSubjectiveExamSubjectInfo(connection, examDetailIdx)
+				: await examDao.selectExamSubject(connection, examIdx);
+		return resultResponse(baseResponse.SUCCESS, getSubjectiveExamSubjectInfoRows);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 특정 시험 목록 조회
+exports.getExamUrl = async function (examIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamUrlRows = await examDao.selectExamUrl(connection, examIdx);
+		return resultResponse(baseResponse.SUCCESS, getExamUrlRows[0]);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.checkEngineerExamDetailIdx = async function (examDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamDetailIdxRows = await examDao.selectEngineerExamDetailIdx(connection, examDetailIdx);
+		if (getExamDetailIdxRows[0].exist === 0) throw new errorResponse(baseResponse.ENGINEER_ERROR, 400);
+		return getExamDetailIdxRows;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.selectWorkEngineerExamDetailIdx = async function (engineerExamDate, engineerExamRound) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getExamDetailIdxRows = await examDao.selectWorkEngineerExamDetailIdx(
+			connection,
+			engineerExamDate,
+			engineerExamRound,
+		);
+		return getExamDetailIdxRows;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.selectEngineerDataset = async function (examDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectEngineerDatasetRows = await examDao.selectEngineerDataset(connection, examDetailIdx);
+		return selectEngineerDatasetRows;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.selectworkEngineerIdxSet = async function (workExamDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectworkEngineerIdxSetRows = await examDao.selectworkEngineerIdxSet(connection, workExamDetailIdx);
+		return selectworkEngineerIdxSetRows;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+// 랜덤 문제 조회
+exports.getRandomExam = async function (examIdx, subjectList, startDate, endDate, limit, isAuth, problemCountList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let subject = [],
+			subjectsTmp = [];
+		let getProblem,
+			problemNum = 0;
+		const examInfoResult = await examDao.selectExamInfo(connection, examIdx);
+		const examInfo = {
+			...examInfoResult[0],
+			problemCount: limit * subjectList.length,
+		};
+		// 시험 과목별 문제
+		for (let i = 0; i < subjectList.length; i++) {
+			let subjectTmp;
+			subjectTmp = await examDao.selectExamSubjectByIdx(connection, examIdx, subjectList[i]); // 과목 인덱스, 정보 반환
+			subjectTmp = subjectTmp[0];
+			subjectTmp.limit = limit;
+			subjectsTmp.push(subjectTmp); // sujbectList의 인덱스 순서대로 넣기 때문에 problemCountList의 순서와 동일 할 것이다.
+		}
+		let subjectiveLabelFlag = examInfo.subjectiveLabel; // 해당 시험이 실기일 경우 선지를 가져오는 것을 제외하기 위함. .
+
+		for (let subjectInfo of subjectsTmp) {
+			getProblem =
+				subjectiveLabelFlag == 0 // 0이면 필기로 아니면 실기로
+					? await examDao.selectExamSubjectRandomByIdx(
+							connection,
+							examIdx,
+							subjectInfo.examSubjectIdx,
+							startDate,
+							endDate,
+							parseInt(limit),
+							isAuth,
+					  )
+					: await examDao.selectSubjectiveExamSubjectRandomByIdx(
+							connection,
+							examIdx,
+							subjectInfo.examSubjectIdx,
+							startDate,
+							endDate,
+							parseInt(limit),
+							isAuth,
+							// problemCountList.at(idx++) ?? undefined, 이부분에서 problemCountList를 해결해야함.
+					  );
+			if (getProblem.length < limit) return false;
+			if (isAuth != 1) {
+				//권한이 없다면 해설은 없앤다.
+				for (let object of getProblem) {
+					if (object.publicLevel >= 1) delete object.solution;
+				}
+			}
+			if (subjectiveLabelFlag == 0) {
+				let problems = [];
+
+				for (let object of getProblem) {
+					problemNum++;
+					let questions = await examDao.selectQuestion(connection, object.multipleProblemIdx);
+					let problem = {
+						problemNum: problemNum,
+						...object,
+						questions: questions,
+					};
+					problems.push(problem);
+				}
+				subjectInfo.problems = problems;
+				subject.push(subjectInfo);
+			} else {
+				for (let object of getProblem) {
+					problemNum++;
+					object.problemNum = problemNum;
+				}
+				subjectInfo.problems = getProblem;
+				subject.push(subjectInfo);
+			}
+		}
+
+		if ((limit == 20 || limit == 50) && subjectList[4] == 5 && subjectiveLabelFlag == 0) {
+			getProblem = await examDao.selectNewKecRandomProblem(connection);
+			delete subject[subject.length - 1].problems[limit - 1];
+			let questions = await examDao.selectQuestion(connection, getProblem[0].multipleProblemIdx);
+			let problem = {
+				problemNum: problemNum,
+				...getProblem[0],
+				questions: questions,
+			};
+			subject[subject.length - 1].problems[limit - 1] = problem;
+		}
+
+		return resultResponse(baseResponse.SUCCESS, { examInfo, subject });
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 기업 시험의 유형에 대해서 가져오는 provider
+exports.getCompanyExamType = async function (companyType) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getCompanyExamTypeResult = await examDao.getCompanyExamType(connection, companyType);
+
+		return resultResponse(baseResponse.SUCCESS, getCompanyExamTypeResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 기업 시험의 유형에 따라서 어떤 분야의 시험들이 있는 지 가져온다.
+exports.getCompanyExamField = async function (companyExamTypeIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getCompanyExamFieldResult = await examDao.getCompanyExamField(connection, companyExamTypeIdx);
+
+		return resultResponse(baseResponse.SUCCESS, getCompanyExamFieldResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 기업에 따른 소분류의 examSort를 가져오는 provider.
+exports.getCompanyDivision = async function (defaultCompanyName, defaultDivisionLevel, companySortLabel) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const companyDivisionResult = await examDao.selectCompanyExamSort(
+			connection,
+			defaultCompanyName,
+			defaultDivisionLevel,
+			companySortLabel || 0,
+		);
+
+		const response = [];
+
+		//하드 코딩으로 examSortIdx를 99로 넣어줌.
+		const freeCompanyExamDetail = {
+			examSortIdx: 99,
+			examSortName: "무료회차",
+		};
+		for (let obj of companyDivisionResult) {
+			let resObj = {
+				examSortIdx: obj.examSortIdx,
+				examSortName: obj.examSortName,
+			};
+			response.push(resObj);
+		}
+		response.push(freeCompanyExamDetail);
+		return resultResponse(baseResponse.SUCCESS, response);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getDefaultDivisionIdx = async function (
+	defaultCompanyName,
+	defaultDivisionLevel,
+	defaultDivisionName,
+	companySortLabel,
+) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const defaultDivisionIdx = await examDao.selectDefaultDivisionIdx(
+			connection,
+			defaultCompanyName,
+			defaultDivisionLevel,
+			defaultDivisionName,
+			companySortLabel,
+		);
+		return defaultDivisionIdx;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 기업 리스트 조회 - 수정
+exports.getCompanyList = async function (examFieldList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let getCompanyListResult;
+		let response = [];
+
+		// todo : 일단 무료 회차 제외
+		// if (examFieldList[0] == 99) {
+		// 	//하드 코딩으로 무료회차에 대한 99를 정해주면 무료회차에 대한 것을 가져옴
+		// 	getCompanyListResult = await storeDao.selectFreeCompanyExam(connection, 99);
+		// } else {
+		// 	getCompanyListResult = await examDao.selectCompanyList(connection, examFieldList);
+		// }
+
+		getCompanyListResult = await examDao.selectCompanyList(connection, examFieldList);
+
+		for (let obj of getCompanyListResult) {
+			let resObj = {
+				companyExamField: obj.examSortIdx,
+				companyName: obj.companyName,
+				companyImage: obj.companyUrl,
+				prio: obj.prio,
+			};
+			response.push(resObj);
+		}
+		response = response.sort((a, b) => a.prio - b.prio);
+		return resultResponse(baseResponse.SUCCESS, response);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getCompanyExamDetailList = async function (queryParams, isCategory) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getCompanyExamDetailList = await examDao.selectCompanyExamDetailList(connection, queryParams, isCategory);
+		const examDetailInfo = [];
+		const getCompanyUrl = await examDao.getCompanyUrl(connection, queryParams);
+		const companyInfo = {
+			companyName: getCompanyUrl[0].companyName,
+			companyUrl: getCompanyUrl[0].companyUrl,
+		};
+		for (let obj of getCompanyExamDetailList) {
+			let resObj = {
+				examIdx: obj.examIdx,
+				examDetailIdx: obj.examDetailIdx,
+				examDetailName: obj.examDetailName,
+				examDate: obj.examDate,
+				domain: obj.domain,
+				education: obj.education,
+				isBody: obj.isBody,
+			};
+			examDetailInfo.push(resObj);
+		}
+		return resultResponse(baseResponse.SUCCESS, { companyInfo, examDetailInfo });
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getCompanyExamDetailListFree = async function (queryParams) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getCompanyExamDetailList = await examDao.selectCompanyExamDetailListFree(connection, queryParams);
+		const response = [];
+		for (let obj of getCompanyExamDetailList) {
+			let resObj = {
+				examIdx: obj.examIdx,
+				examDetailIdx: obj.examDetailIdx,
+				examDetailName: obj.examDetailName,
+				companyImage: obj.examSortUrl,
+				domain: obj.domain,
+				education: obj.education,
+				isPublic: obj.isPublic,
+			};
+			response.push(resObj);
+		}
+		return resultResponse(baseResponse.SUCCESS, response);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getCompanyExamFilterInfo = async function (companyExamField) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const educationList = await examDao.selectCompanyFilterList(connection, companyExamField, "education");
+		const domainList = await examDao.selectCompanyFilterList(connection, companyExamField, "domain");
+
+		const resObj = {
+			education: educationList.map((obj) => obj.education),
+			domain: domainList.map((obj) => obj.domain),
+		};
+		return resultResponse(baseResponse.SUCCESS, resObj);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getExamDivision = async function (examIdx, examSortType) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examDivision = await examDao.getExamDivisionByExamIdx(connection, examIdx, examSortType);
+		return examDivision;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getExamBasicInfo = async function (examIdx, examSortType) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examDivision = await examDao.selectExamDivision(connection, examIdx, examSortType);
+		return examDivision;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+// 특정 시험에 회차가 공개여부로 존재하는 지에 대해서
+exports.getExamDetailPublic = async function (examIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const ExamDetailPublic = await examDao.getExamDetailPublic(connection, examIdx);
+		return ExamDetailPublic;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 문제의 빈도수 가져오기
+exports.getFrequency = async function (problemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getFrequencyResult = await examDao.getFrequency(connection, problemIdx);
+		const { history } = getFrequencyResult;
+		const length = history.split(", ").length;
+
+		const result = { ...getFrequencyResult, length };
+
+		return resultResponse(baseResponse.SUCCESS, result);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getCategoryList = async function (companyExamType) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const getCategoryListResult = await examDao.getCategoryList(connection, companyExamType);
+
+		let categoryList = [];
+		getCategoryListResult.map((v) => categoryList.push(v.category));
+
+		return resultResponse(baseResponse.SUCCESS, categoryList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getFieldMatchName = async function (companyExamField, companyName) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examFieldCheckResult = await examDao.getFieldMatchNameCheck(connection, companyExamField, companyName);
+		return resultResponse(baseResponse.SUCCESS, examFieldCheckResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.examSortTypeCheck = async function (companyExamField) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examFieldTypeResult = await examDao.getExamSortType(connection, companyExamField);
+		return examFieldTypeResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
diff --git a/_old/src/Exam/examRoute.js b/_old/src/Exam/examRoute.js
new file mode 100644
index 0000000..ae0b8a2
--- /dev/null
+++ b/_old/src/Exam/examRoute.js
@@ -0,0 +1,92 @@
+module.exports = function (app) {
+	const exam = require("./examController");
+	const jwtMiddleware = require("../../middlewares/jwtMiddleware");
+	const authCheck = require("../../middlewares/authCheck");
+	const makeAuthLog = require("../../middlewares/makeAuthLog");
+	const subjectTiveExam = require("./subjectiveExam/subjectivExamRoute");
+	const companyRouter = require("./companyExam/companyRouter");
+
+	// API No ?. 시험  종목 조회 API  쿼리스트링으로 시험종류 필터링
+	app.get("/api/exams", exam.examList);
+
+	app.get("/api/examField", exam.examFieldList);
+	// API No ?. 시험정보 조회 API
+	//app.get('/exams/:examIdx/examInfo', )
+
+	// API No ?. 시험 정보 조회 API
+	app.get("/api/exams/:examIdx/examInfo", exam.examInfo);
+
+	// API No ?. 시험 회차 조회 API
+	app.get("/api/exams/:examIdx/examDetail", exam.examDetailList);
+
+	// API No ?. 핵심 적중 조회 API ( 과목, 문제들의 중복수 조회 ) 보류
+	//app.get('/exams/:examIdx/pointExam', )
+
+	// API No ?. 과목 조회 API ( 과목번호, 과목이름, 문항수 )
+	app.get("/api/exams/:examIdx/examDetail/:examDetailIdx/examSubject", exam.examSubjectInfo);
+
+	// API No ?. 자격증 시험 문제 전체 조회 API
+	app.post("/api/exams/:examIdx/examDetail/:examDetailIdx/problem", authCheck, makeAuthLog, exam.problemList);
+
+	// API No ?. 시험 문제 제출 API
+	app.post("/api/exams/:examIdx/examDetail/:examDetailIdx/problem/submission", jwtMiddleware, exam.submissionProblem);
+
+	// API No ?. 특정 시험 오답노트 조회 API
+	//app.get('/exams/:examDetailIdx/reviewNote', exam)
+
+	// API No ?. 오답노트 담기 API
+	app.post("/api/exams/reviewNote/:multipleProblemIdx", jwtMiddleware, exam.postReviewNote);
+
+	// API No ?. 오답노트 한번에 담기 API
+	app.post("/api/exams/reviewNote", jwtMiddleware, exam.postReviewNoteList);
+
+	// API No ?. 문제 오류 신고 API
+	app.post("/api/exams/problemError", jwtMiddleware, exam.postProblemError);
+
+	// API No ?. 시험 사진 Url 조회 API
+	app.get("/api/exams/:examIdx/examUrl", exam.getExamUrl);
+
+	// API No ?. 랜덤 시험 조회 API
+	app.post("/api/exams/:examIdx/randomExam", authCheck, exam.getRandomExam);
+
+	// API No ?. 시험 분야 조회 API 김기창 => todo1
+	app.get("/api/examFieldList", exam.getExamField);
+
+	// API No ?. 랜덤 시험 제출 API
+	//app.post("/api/exams/:examIdx/randomExam/submit", exam);
+
+	// API No ?. 공기업 회사 구분 조회 & 대기업 조회
+	app.get("/api/exams/publicCompany/examSort", exam.getCompanyDivision);
+
+	// API No ?. 공기업 회사 리스트 조회
+	app.get("/api/exams/publicCompany/companyList", exam.getCompanyList);
+
+	// API No ?. 공기업 회사 지원 자격, 전공 분야 조회
+	app.get("/api/exams/publicCompany/filterInfo", exam.getCompanyExamFilterInfo);
+
+	// API No ?. 공기업 회차 리스트 조회
+	app.get("/api/exams/publicCompany/examDetailList", exam.getCompanyExamDetailList);
+
+	// API No ?. 공기업 무료 회차 리스트 조회
+	app.get("/api/exams/publicCompany/examDetailList/free", exam.getCompanyExamDetailListFree);
+
+	// API No ?. 공기업 시험 문제 전체 조회 API
+	app.get(
+		"/api/exams/publicCompany/:examIdx/examDetail/:examDetailIdx/problem",
+		authCheck,
+		makeAuthLog,
+		exam.getCompanyProblemList,
+	);
+
+	// 실기 API 라우터 분리
+	app.use("/api/exams/practical", subjectTiveExam);
+
+	// 기업 API 라우터 분리
+	app.use("/api/exams/company", companyRouter);
+
+	// 문제 빈도수 가져오기
+	app.get("/api/exams/problemFrequency/:problemIdx", exam.getFrequency);
+
+	// API No ?. 시험 한 문제 조회 API
+	app.get("/api/exams/:problemIdx/:subjectiveLabel", exam.problem);
+};
diff --git a/_old/src/Exam/examService.js b/_old/src/Exam/examService.js
new file mode 100644
index 0000000..94c5e03
--- /dev/null
+++ b/_old/src/Exam/examService.js
@@ -0,0 +1,158 @@
+const { pool } = require("../../config/database");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const examDao = require("./examDao");
+const userProvider = require("../User/userProvider");
+const examProvider = require("../Exam/examProvider");
+
+// 시험기록 생성  검토 필요
+exports.createExamRecord = async function (
+	userIdx,
+	examDetailIdx,
+	userAnswer,
+	isCorrect,
+	multipleProblemIdx,
+	score,
+	useSubject,
+	correctAnswers,
+	isPass,
+) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const insertCorrectRateParams = [];
+		const insertUserExamDetailRecordParams = [];
+		//insertUserExamDetailRecordParams = [userExamRecordIdx, multipleProblemIdx, userAnswer];
+		await connection.beginTransaction();
+
+		//const multipleProblemResult = await examDao.selectMultipleProblemInfo(connection, examDetailIdx) // 시험문제 인덱스 반환
+		const userExamRecordResult = await examDao.insertUserExamRecord(connection, userIdx, examDetailIdx, score, isPass); // 유저 시험 기록 테이블에 삽입
+		for (let i = 0; i < multipleProblemIdx.length; i++) {
+			let recordArray = [];
+			let correctRateArray = [];
+
+			recordArray.push(userExamRecordResult[0].insertId);
+			recordArray.push(multipleProblemIdx[i]);
+			recordArray.push(userAnswer[i]);
+			correctRateArray.push(multipleProblemIdx[i]);
+			correctRateArray.push(userIdx);
+			correctRateArray.push(isCorrect[i]);
+
+			insertUserExamDetailRecordParams.push(recordArray);
+			insertCorrectRateParams.push(correctRateArray);
+		}
+
+		await examDao.insertUserExamDetailRecord(connection, insertUserExamDetailRecordParams); // 유저 시험 기록 상세 테이블 삽입
+		await examDao.insertCorrectRate(connection, insertCorrectRateParams); // 정답률 삽입
+		await connection.commit();
+
+		let result = await userProvider.userExamRecord(userExamRecordResult[0].insertId, useSubject);
+		if (result.isSuccess == false) return result;
+		result.result.examGrade.correctAnswers = correctAnswers.length;
+		return result;
+	} catch (err) {
+		await connection.rollback();
+
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// 오답노트 생성
+exports.createReviewNote = async function (userIdx, multipleProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	let result;
+	try {
+		await connection.beginTransaction();
+		const reviewNoteResult = await examDao.selectReviewNoteIdx(connection, multipleProblemIdx, userIdx);
+		if (reviewNoteResult.length && reviewNoteResult[0].status == "N")
+			result = basickResponse(baseResponse.REVIEWNOTE_EXIST);
+		else {
+			await examDao.upsertReviewNote(connection, userIdx, multipleProblemIdx);
+			result = basickResponse(baseResponse.SUCCESS);
+		}
+
+		await connection.commit();
+		return result;
+	} catch (err) {
+		await connection.rollback();
+
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+//자격증 주관식 오답노트 생성
+exports.createSubjectiveReviewNote = async function (userIdx, subjectiveProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	let result;
+	try {
+		await connection.beginTransaction();
+		const reviewNoteResult = await examDao.selectSubjectiveReviewNoteIdx(connection, subjectiveProblemIdx, userIdx);
+		if (reviewNoteResult.length && reviewNoteResult[0].status == "N")
+			result = basickResponse(baseResponse.REVIEWNOTE_EXIST);
+		else {
+			await examDao.upsertSubjectiveReviewNote(connection, userIdx, subjectiveProblemIdx);
+			result = basickResponse(baseResponse.SUCCESS);
+		}
+		await connection.commit();
+		return result;
+	} catch (err) {
+		await connection.rollback();
+
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+//오답노트 여러문제 한번에 생성
+exports.createTotalReviewNote = async function (userIdx, multipleProblemList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		try {
+			const result = [];
+			await connection.beginTransaction();
+
+			for (var i = 0; i < multipleProblemList.length; i++) {
+				let reviewNoteCheckResult = await examDao.selectReviewNoteIdx(connection, multipleProblemList[i], userIdx);
+				if (reviewNoteCheckResult.length === 0) {
+					// 만약 reviewNoteCheckResult가 만약 존재하지 않는다면 insertReviewNote를
+					let insertResult = await examDao.insertReviewNote(connection, userIdx, multipleProblemList[i]);
+					result.push({ reviewNoteIdx: insertResult[0].insertId });
+				} else if (reviewNoteCheckResult[0].status === "N") {
+					continue;
+				} else {
+					await examDao.updateReviewNote(connection, reviewNoteCheckResult[0].reviewNoteIdx);
+					result.push({ reviewNoteIdx: reviewNoteCheckResult[0].reviewNoteIdx });
+				}
+			}
+			await connection.commit();
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (err) {
+		await connection.rollback();
+
+		throw err;
+	}
+};
+
+// 오류 신고 생성
+exports.createErrorReport = async function (userIdx, multipleProblemIdx, title, content, type, subjectiveLabel) {
+	try {
+		const insertProblemErrorParams = [userIdx, multipleProblemIdx, title, content, type, subjectiveLabel];
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await examDao.insertProblemError(connection, insertProblemErrorParams);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (err) {
+		throw err;
+	}
+};
diff --git a/_old/src/Exam/subjectiveExam/subjectivExamRoute.js b/_old/src/Exam/subjectiveExam/subjectivExamRoute.js
new file mode 100644
index 0000000..1a8d661
--- /dev/null
+++ b/_old/src/Exam/subjectiveExam/subjectivExamRoute.js
@@ -0,0 +1,11 @@
+const express = require("express");
+const router = express.Router();
+const exam = require("../examController");
+const jwtMiddleware = require("../../../middlewares/jwtMiddleware");
+
+// API No ?. 주관식 오답노트 담기 API
+router.post("/reviewNote/:subjectiveProblemIdx", jwtMiddleware, exam.postSubjectiveReviewNote);
+
+// API No ?. 주관식 오답노트 문제별 가져오기.
+
+module.exports = router;
diff --git a/_old/src/Restore/restoreController.js b/_old/src/Restore/restoreController.js
new file mode 100644
index 0000000..0895db9
--- /dev/null
+++ b/_old/src/Restore/restoreController.js
@@ -0,0 +1,538 @@
+const restoreProvider = require("./restoreProvider");
+const adminProvider = require("../Admin/adminProvider");
+const restoreService = require("./restoreService");
+const examProvider = require("../Exam/examProvider");
+const userProvider = require("../User/userProvider");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const { Console } = require("winston/lib/winston/transports");
+const asyncHandler = require("../../utils/asyncHandler");
+const errorResponse = require("../../utils/errorResponse");
+const regKorean = /^[가-힣]{2,4}$/;
+const regDate = /^(19|20)\d{2}-(0[1-9]|1[012])$/;
+const regNumber = /^[0-9]/;
+const helperTerms = [
+	"이므로",
+	"위해서",
+	"로부터",
+	"에서",
+	"으로",
+	"것은",
+	"하게",
+	"하고",
+	"한다",
+	"이다",
+	"이고",
+	"이며",
+	"하면",
+	"라고",
+	"로는",
+	"대한",
+	"동안",
+	"하는",
+	"하게",
+	"관한",
+	"부터",
+	"되는",
+	"되면",
+	"는가",
+	"을",
+	"를",
+	"때",
+	"은",
+	"는",
+	"의",
+	"에",
+	"겠",
+	"했",
+];
+
+exports.getSearchResult = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const isKatex = req.query.isKatex; //사용자가 보낸 쿼리에 카텍스가 씌워져있는지 확인하는 쿼리.
+	let searchString = req.query.q;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!searchString) throw new errorResponse(baseResponse.SEARCH_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	await userProvider.userIdxCheck(userIdx);
+	let start = new Date();
+	let providerResult;
+
+	if (isKatex == 1) {
+		searchString = searchString.replace("\\", "\\\\");
+		let katexStringList = searchString.split(" ");
+		let searchQuery = "";
+
+		for (let i = 0; i < katexStringList.length; i++) {
+			katexStringList[i] = "%" + katexStringList[i] + "%";
+			searchQuery = i == 0 ? searchQuery + "content like ?" : searchQuery + " or content like ? ";
+		}
+
+		const orderQuery = katexStringList.length > 1 ? searchQuery.replace("or", "and") : "";
+
+		providerResult = await restoreProvider.getKatexSearchResult(
+			[...katexStringList, ...katexStringList],
+			searchQuery,
+			orderQuery,
+		);
+	} else {
+		let removedString = searchString;
+
+		for (let i = 0; i < helperTerms.length; i++) {
+			removedString = removedString.replace(new RegExp(`${helperTerms[i]}`, "g"), "");
+		}
+
+		let { finalString, highScoreString } = restoreProvider.makeSearchString(removedString);
+		let { highScoreString: BestScoreString } = restoreProvider.makeSearchString(searchString);
+
+		providerResult = await restoreProvider.getSearchResult(finalString, highScoreString, BestScoreString);
+	}
+
+	let end = new Date();
+	let cost = (end - start) / 1000.0;
+	if (providerResult.isSuccess) providerResult.result.cost = cost;
+	return res.send(providerResult);
+});
+
+exports.getDetailResult = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	let multipleProblemIdxes = req.body.multipleProblemIdx;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+
+	await userProvider.userIdxCheck(userIdx);
+
+	if (!multipleProblemIdxes || multipleProblemIdxes.length == 0)
+		throw new errorResponse(baseResponse.PROBLEM_EMPTY, 400);
+
+	let multipleParams = "(";
+	for (let i = 0; i < multipleProblemIdxes.length; i++) {
+		if (!regNumber.test(multipleProblemIdxes[i])) throw new errorResponse(baseResponse.PROBLEM_ERROR_TYPE, 400);
+		if (i == 0) multipleParams += "?";
+		else multipleParams += ",?";
+	}
+	multipleParams += ")";
+
+	await examProvider.multipleProblemsCheck(multipleProblemIdxes, multipleParams);
+
+	const providerResult = await restoreProvider.getDetailResult(multipleProblemIdxes, multipleParams);
+	return res.send(providerResult);
+});
+
+exports.restore = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restoreExamDetailIdx = req.body.restoreExamDetailIdx;
+	const comment = req.body.comment;
+	const multipleProblemIdx = req.body.multipleProblemIdx;
+
+	// Validation Check (Request Error)
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!multipleProblemIdx) throw new errorResponse(baseResponse.PROBLEM_EMPTY, 400);
+	if (!restoreExamDetailIdx) throw new errorResponse(baseResponse.RESTORE_EXAM_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!regNumber.test(multipleProblemIdx)) throw new errorResponse(baseResponse.PROBLEM_ERROR_TYPE, 400);
+	if (!regNumber.test(restoreExamDetailIdx)) throw new errorResponse(baseResponse.RESTORE_EXAM_ERROR_TYPE, 400);
+
+	await userProvider.userIdxCheck(userIdx);
+
+	// multipleProblemIdx Check
+	await examProvider.multipleProblemCheck(multipleProblemIdx);
+
+	//restoreExamDetailIdx validation
+	await restoreProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	const insertResult = await restoreService.insertRestoration(
+		userIdx,
+		restoreExamDetailIdx,
+		multipleProblemIdx,
+		comment,
+	);
+
+	return res.send(insertResult);
+});
+
+exports.customizeProblem = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const { problemNum, problem, question, comment, restoreExamDetailIdx, imageUrl } = req.body;
+	//console.log(req.body);
+	//Empty Check
+	if (!problem) throw new errorResponse(baseResponse.PROBLEM_EMPTY, 400);
+	if (!restoreExamDetailIdx) throw new errorResponse(baseResponse.RESTORE_EXAM_EMPTY, 400);
+	if (!question) throw new errorResponse(baseResponse.QUESTION_EMPTY, 400);
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!regNumber.test(restoreExamDetailIdx)) throw new errorResponse(baseResponse.EXAM_ERROR_TYPE, 400);
+
+	// User validation
+	await userProvider.userIdxCheck(userIdx);
+
+	//Length Check
+	if (comment && comment.length > 200) throw new errorResponse(baseResponse.CUSTOM_COMMENT_LENGTH_ERROR, 400);
+	if (problem.length > 500) throw new errorResponse(baseResponse.CUSTOM_PROBLEM_LENGTH_ERROR, 400);
+	for (let i = 0; i < question.length; i++) {
+		if (question[i].length > 500) throw new errorResponse(baseResponse.CUSTOM_QUESTION_LENGTH_ERROR, 400);
+	}
+
+	//restoreExamDetailIdx validation
+	await restoreProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	const customResult = await restoreService.insertCustom(
+		userIdx,
+		problemNum,
+		problem,
+		comment,
+		restoreExamDetailIdx,
+		imageUrl,
+		question,
+	);
+	return res.send(customResult);
+});
+
+exports.getLatestRestoration = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restoreExamDetailIdx = req.params.restoreExamDetailIdx;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!restoreExamDetailIdx) throw new errorResponse(baseResponse.RESTORE_EXAM_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!regNumber.test(restoreExamDetailIdx)) throw new errorResponse(baseResponse.EXAM_ERROR_TYPE, 400);
+
+	// User validation
+	await userProvider.userIdxCheck(userIdx);
+
+	// restoreExamDetailIdx validation
+	await restoreProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	const restorationOrder = "recent";
+	const providerResult = await restoreProvider.getRestoration(restorationOrder, userIdx, restoreExamDetailIdx);
+	return res.send(providerResult);
+});
+
+exports.getPopularRestoration = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restoreExamDetailIdx = req.params.restoreExamDetailIdx;
+	// Validation Check (Request Error)
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!restoreExamDetailIdx) throw new errorResponse(baseResponse.RESTORE_EXAM_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!regNumber.test(restoreExamDetailIdx)) throw new errorResponse(baseResponse.EXAM_ERROR_TYPE, 400);
+
+	await userProvider.userIdxCheck(userIdx);
+
+	//restoreExamDetailIdx validation
+	await restoreProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	const restorationOrder = "popular";
+	const providerResult = await restoreProvider.getRestoration(restorationOrder, userIdx, restoreExamDetailIdx);
+	return res.send(providerResult);
+});
+
+exports.getRestorationDetail = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restoreExamDetailIdx = req.params.restoreExamDetailIdx;
+	const restorationIdx = req.params.restorationIdx;
+
+	// Validation Check (Request Error)
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!restoreExamDetailIdx) throw new errorResponse(baseResponse.RESTORE_EXAM_EMPTY, 400);
+	if (!restorationIdx) throw new errorResponse(baseResponse.RESTORATION_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!regNumber.test(restoreExamDetailIdx)) throw new errorResponse(baseResponse.EXAM_ERROR_TYPE, 400);
+	if (!regNumber.test(restorationIdx)) throw new errorResponse(baseResponse.RESTORATION_ERROR_TYPE, 400);
+
+	await userProvider.userIdxCheck(userIdx);
+
+	//restoreExamDetailIdx validation
+	await restoreProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	//restorationIdx validation
+	await restoreProvider.restorationIdxCheck(restorationIdx);
+
+	const providerResult = await restoreProvider.getRestorationDetail(userIdx, restorationIdx);
+	return res.send(providerResult);
+});
+
+exports.getRestoreExamList = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+
+	await userProvider.userIdxCheck(userIdx);
+
+	const examList = await restoreProvider.getExamList();
+
+	return res.send(examList);
+});
+
+//문제 좋아요
+exports.postRestorationLike = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restorationIdx = req.body.restorationIdx;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!restorationIdx) throw new errorResponse(baseResponse.RESTORATION_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!regNumber.test(restorationIdx)) throw new errorResponse(baseResponse.RESTORATION_ERROR_TYPE, 400);
+
+	await restoreProvider.restorationIdxCheck(restorationIdx);
+
+	let postRestorationLikeResult;
+	const restorationLikeCheckResult = await restoreProvider.restorationLikeCheck(userIdx, restorationIdx);
+	if (restorationLikeCheckResult.length === 0) {
+		postRestorationLikeResult = await restoreService.createRestorationLike(userIdx, restorationIdx);
+	} else {
+		const restorationLikeIdx = restorationLikeCheckResult[0].restorationLikeIdx;
+		const isLike = restorationLikeCheckResult[0].isLike;
+		postRestorationLikeResult = await restoreService.updateRestorationLike(
+			restorationLikeIdx,
+			isLike,
+			userIdx,
+			restorationIdx,
+		);
+	}
+
+	return res.send(postRestorationLikeResult);
+});
+
+//댓글 좋아요
+exports.postRestorationCommentLike = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restorationCommentIdx = req.body.restorationCommentIdx;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!restorationCommentIdx) throw new errorResponse(baseResponse.RESTORATION_COMMENT_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!regNumber.test(restorationCommentIdx)) throw new errorResponse(baseResponse.RESTORATION_COMMENT_ERROR_TYPE, 400);
+
+	await restoreProvider.restorationCommentIdxCheck(restorationCommentIdx);
+
+	const restorationCommentLikeCheckResult = await restoreProvider.restorationCommentLikeCheck(
+		userIdx,
+		restorationCommentIdx,
+	);
+	let postRestorationCommentLikeResult;
+	if (restorationCommentLikeCheckResult.length === 0) {
+		postRestorationCommentLikeResult = await restoreService.createRestorationCommentLike(
+			userIdx,
+			restorationCommentIdx,
+		);
+	} else {
+		const restorationCommentLikeIdx = restorationCommentLikeCheckResult[0].restorationCommentLikeIdx;
+		const isLike = restorationCommentLikeCheckResult[0].isLike;
+		postRestorationCommentLikeResult = await restoreService.updateRestorationCommentLike(
+			restorationCommentLikeIdx,
+			isLike,
+			userIdx,
+			restorationCommentIdx,
+		);
+	}
+
+	return res.send(postRestorationCommentLikeResult);
+});
+
+//댓글 입력
+exports.postRestorationComment = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const { restorationIdx, comment } = req.body;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!restorationIdx) throw new errorResponse(baseResponse.RESTORATION_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!regNumber.test(restorationIdx)) throw new errorResponse(baseResponse.RESTORATION_ERROR_TYPE, 400);
+
+	await restoreProvider.restorationIdxCheck(restorationIdx);
+
+	if (!comment) throw new errorResponse(baseResponse.COMMENT_EMPTY, 400);
+	if (comment.length > 300) throw new errorResponse(baseResponse.COMMENT_LENGTH_ERROR, 400);
+
+	const createRestorationCommentResult = await restoreService.createRestorationComment(
+		userIdx,
+		restorationIdx,
+		comment,
+	);
+
+	return res.send(createRestorationCommentResult);
+});
+
+//댓글 조회
+exports.getRestorationComment = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restorationIdx = req.params.restorationIdx;
+	let order = req.query.order;
+	let top = req.query.top;
+
+	// Validation Check (Request Error)
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!restorationIdx) throw new errorResponse(baseResponse.RESTORATION_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!regNumber.test(restorationIdx)) throw new errorResponse(baseResponse.RESTORATION_ERROR_TYPE, 400);
+
+	await userProvider.userIdxCheck(userIdx);
+
+	await restoreProvider.restorationIdxCheck(restorationIdx);
+
+	if (!order) order = "popular";
+	if (order != "popular" && order != "recent") throw new errorResponse(baseResponse.ORDER_ERROR_TYPE, 400);
+
+	if (!top) throw new errorResponse(baseResponse.TOP_EMPTY, 400);
+	if (!regNumber.test(top)) throw new errorResponse(baseResponse.TOP_ERROR_TYPE, 400);
+
+	const getRestorationCommentResult = await restoreProvider.getRestorationComment(userIdx, restorationIdx, order, top);
+
+	return res.send(getRestorationCommentResult);
+});
+
+//댓글 수정
+exports.updateRestorationComment = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restorationCommentIdx = req.params.restorationCommentIdx;
+	const comment = req.body.comment;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!comment) throw new errorResponse(baseResponse.COMMENT_EMPTY, 400);
+	if (!restorationCommentIdx) throw new errorResponse(baseResponse.RESTORATION_COMMENT_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!regNumber.test(restorationCommentIdx)) throw new errorResponse(baseResponse.RESTORATION_COMMENT_ERROR_TYPE, 400);
+
+	await restoreProvider.commentUserCheck(restorationCommentIdx, userIdx);
+
+	const updateRestorationCommentResult = await restoreService.updateRestorationComment(restorationCommentIdx, comment);
+	return res.send(updateRestorationCommentResult);
+});
+
+//댓글 삭제
+exports.deleteRestorationComment = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restorationCommentIdx = req.params.restorationCommentIdx;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+
+	if (!restorationCommentIdx) throw new errorResponse(baseResponse.RESTORATION_COMMENT_EMPTY, 400);
+	if (!regNumber.test(restorationCommentIdx)) throw new errorResponse(baseResponse.RESTORATION_COMMENT_ERROR_TYPE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	const deleteRestorationCommentResult = await restoreService.deleteRestorationComment(restorationCommentIdx);
+	return res.send(deleteRestorationCommentResult);
+});
+
+//베스트 댓글 조회
+exports.getTopRestorationComment = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restorationIdx = req.params.restorationIdx;
+	const top = req.params.top;
+
+	// Validation Check (Request Error)
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!restorationIdx) throw new errorResponse(baseResponse.RESTORATION_EMPTY, 400);
+	if (!top) throw new errorResponse(baseResponse.TOP_EMPTY, 400);
+
+	if (!regNumber.test(restorationIdx)) throw new errorResponse(baseResponse.RESTORATION_ERROR_TYPE, 400);
+	if (!regNumber.test(top)) throw new errorResponse(baseResponse.TOP_ERROR_TYPE, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+
+	await userProvider.userIdxCheck(userIdx);
+
+	await restoreProvider.restorationIdxCheck(restorationIdx);
+
+	const providerResult = await restoreProvider.getTopRestorationComment(userIdx, restorationIdx, top);
+	return res.send(providerResult);
+});
+
+//수험표 인증 생성
+exports.postExamAdmissionTicket = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const { restoreExamDetailIdx, examAdmissionTicketImage } = req.body;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+
+	if (!restoreExamDetailIdx) throw new errorResponse(baseResponse.RESTORE_EXAM_EMPTY, 400);
+	if (!regNumber.test(restoreExamDetailIdx)) throw new errorResponse(baseResponse.RESTORE_EXAM_ERROR_TYPE, 400);
+	await restoreProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	//if (!examAdmissionTicketImage) return res.send(basickResponse(baseResponse.EXAMADMISSIONTICKETIMAGE_EMPTY)); //이미지 링크 입력바랍니다.
+
+	//const examAdmissionTicketCheckResult = await restoreProvider.examAdmissionTicketCheck(userIdx, restoreExamDetailIdx);
+	//if(examAdmissionTicketCheckResult.length !== 0) return res.send(basickResponse(baseResponse.EXAMADMISSIONTICKET_IDX_EXIST)); //이미 인증 정보 존재합니다.
+
+	const createExamAdmissionTicketResult = await restoreService.createExamAdmissionTicket(
+		userIdx,
+		restoreExamDetailIdx,
+		examAdmissionTicketImage,
+	);
+	return res.send(createExamAdmissionTicketResult);
+});
+
+//수험표 인증 조회
+exports.getExamAdmissionTicket = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restoreExamDetailIdx = req.query.restoreExamDetailIdx;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+
+	if (!restoreExamDetailIdx) throw new errorResponse(baseResponse.RESTORE_EXAM_EMPTY, 400);
+	if (!regNumber.test(restoreExamDetailIdx)) throw new errorResponse(baseResponse.RESTORE_EXAM_ERROR_TYPE, 400);
+	await restoreProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	const getExamAdmissionTicketResult = await restoreProvider.getExamAdmissionTicket(userIdx, restoreExamDetailIdx);
+	return res.send(getExamAdmissionTicketResult);
+});
+
+//복원 시험 이름 가져오기
+exports.getRestoreExamName = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restoreExamDetailIdx = req.query.restoreExamDetailIdx;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+
+	if (!restoreExamDetailIdx) throw new errorResponse(baseResponse.RESTORE_EXAM_EMPTY, 400);
+	if (!regNumber.test(restoreExamDetailIdx)) throw new errorResponse(baseResponse.RESTORE_EXAM_ERROR_TYPE, 400);
+	await restoreProvider.restoreExamDetailIdxCheck(restoreExamDetailIdx);
+
+	const getRestoreExamName = await restoreProvider.getRestoreExamName(restoreExamDetailIdx);
+	return res.send(getRestoreExamName);
+});
+
+exports.getPrivateRestorationComment = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const restorationIdx = req.params.restorationIdx;
+	const top = req.query.top;
+
+	// Validation Check (Request Error)
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!restorationIdx) throw new errorResponse(baseResponse.RESTORATION_EMPTY, 400);
+
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!regNumber.test(restorationIdx)) throw new errorResponse(baseResponse.RESTORATION_ERROR_TYPE, 400);
+
+	const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+	if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+
+	await restoreProvider.restorationIdxCheck(restorationIdx);
+
+	if (!top) throw new errorResponse(baseResponse.TOP_EMPTY, 400);
+	if (!regNumber.test(top)) throw new errorResponse(baseResponse.TOP_ERROR_TYPE, 400);
+
+	const getRestorationCommentResult = await restoreProvider.getPrivateRestorationComment(userIdx, restorationIdx, top);
+	return res.send(getRestorationCommentResult);
+});
diff --git a/_old/src/Restore/restoreDao.js b/_old/src/Restore/restoreDao.js
new file mode 100644
index 0000000..5d7569d
--- /dev/null
+++ b/_old/src/Restore/restoreDao.js
@@ -0,0 +1,543 @@
+// 검색 결과 조회
+async function selectSearchResult(connection, searchString, highScoreString, BestScoreString) {
+	let searchParams = [searchString, searchString, highScoreString, BestScoreString];
+	const selectSearchResultQuery = `
+	select rs.multipleProblemIdx, problem, isKatex, problemImage from MultipleProblem as mp join RestorationSearch as rs on rs.multipleProblemIdx=mp.multipleProblemIdx
+	where match(content) against (? in boolean mode) order by (match(content) against(? in boolean mode)+10*match(content) against (? in boolean mode)+20*match(content) against (? in boolean mode)) desc, multipleProblemIdx
+  `;
+	const selectSearchResultRow = await connection.query(selectSearchResultQuery, searchParams);
+	return selectSearchResultRow[0];
+}
+
+async function selectKatexSearchResult(connection, searchParams, searchQuery, orderQuery) {
+	let selectSearchResultQuery =
+		`
+		select rs.multipleProblemIdx,problem,isKatex, problemImage from MultipleProblem as mp join RestorationSearch as rs on rs.multipleProblemIdx=mp.multipleProblemIdx
+		WHERE ` + searchQuery;
+
+	if (orderQuery) selectSearchResultQuery += "order by case when (" + orderQuery + ") then 1 else 2 end";
+	const selectSearchResultRow = await connection.query(selectSearchResultQuery, searchParams);
+	return selectSearchResultRow[0];
+}
+
+// 상세 보기 조회 : 시험 객관식 문제와 해당하는 문항들 조회(복수)
+async function selectMultipleSet(connection, multipleProblemIdxes, multipleParams) {
+	const selectMultipleSetQuery =
+		`
+        SELECT distinct(mp.multipleProblemIdx),problem,isKatex,problemImage, questionIdx, questionNum,
+        case 
+            when questionImage is null then question
+            when questionImage = ' ' then question
+            when questionImage = '' then question
+            when question is null then questionImage
+            else
+            concat(question, '\\\\\\\\ ', questionImage)  end as question
+        FROM MultipleProblem as mp join Question as qt on mp.multipleProblemIdx=qt.multipleProblemIdx WHERE mp.multipleProblemIdx in ` +
+		multipleParams +
+		`
+        and mp.status='N' order by mp.multipleProblemIdx, questionNum
+    `;
+	const selectMultipleSetRow = await connection.query(selectMultipleSetQuery, multipleProblemIdxes);
+	return selectMultipleSetRow[0];
+}
+
+async function selectRestorationIdx(connection, restoreExamDetailIdx, multipleProblemIdx) {
+	const selectRestorationParams = [restoreExamDetailIdx, multipleProblemIdx];
+	const selectRestorationIdxQuery = `
+	select restorationIdx from Restoration where restoreExamDetailIdx=? and multipleProblemIdx=? and status='N'
+	`;
+	const selectRestorationIdxRow = await connection.query(selectRestorationIdxQuery, selectRestorationParams);
+	return selectRestorationIdxRow[0];
+}
+
+async function selectRestoreExamDetailIdx(connection, restoreExamDetailIdx) {
+	const selectRestoreExamDetailQuery = `
+	select restoreExamDetailIdx from RestoreExamDetail where restoreExamDetailIdx=? and status='N'
+	`;
+	const selectRestoreExamDetailRow = await connection.query(selectRestoreExamDetailQuery, restoreExamDetailIdx);
+	return selectRestoreExamDetailRow[0];
+}
+
+async function selectLatestRestoration(connection, restorationParams) {
+	const selectLatestRestorationQuery = `
+	select count(if(rl.isLike=1,rl.isLike,null)) as likes, rs.restorationIdx, rs.multipleProblemIdx, problem, isKatex, problemImage, time, 
+	(case
+		when B.isLike=1 then true
+		else false
+	end) as userLike
+	from ((Restoration as rs left join RestorationLike as rl on rs.restorationIdx=rl.restorationIdx)
+	join MultipleProblem as mp on rs.multipleProblemIdx=mp.multipleProblemIdx)
+	join (select max(ur.createdAt) as time, rs.restorationIdx from Restoration as rs join UserRestoration as ur on rs.restorationIdx=ur.restorationIdx group by rs.restorationIdx) as A
+	on rs.restorationIdx=A.restorationIdx
+	left join (select isLike,rl.restorationIdx from RestorationLike as rl where userIdx=?) as B on B.restorationIdx=rs.restorationIdx
+	where rs.restoreExamDetailIdx=? and rs.status='N' and mp.status='N'
+	group by rs.restorationIdx
+	order by time desc
+	`;
+	const selectLatestRestorationRow = await connection.query(selectLatestRestorationQuery, restorationParams);
+	return selectLatestRestorationRow[0];
+}
+
+async function selectPopularRestoration(connection, restorationParams) {
+	const selectPopularRestorationQuery = `
+	select count(if(rl.isLike=1,rl.isLike,null)) as likes, rs.restorationIdx, rs.multipleProblemIdx, problem, isKatex, problemImage,
+	(case
+		when B.isLike=1 then true
+		else false
+	end) as userLike
+	from ((Restoration as rs left join RestorationLike as rl on rs.restorationIdx=rl.restorationIdx)
+	join MultipleProblem as mp on rs.multipleProblemIdx=mp.multipleProblemIdx)
+	left join (select isLike,rl.restorationIdx from RestorationLike as rl where userIdx=?) as B on B.restorationIdx=rs.restorationIdx
+	where rs.restoreExamDetailIdx=? and rs.status='N' and mp.status='N'
+	group by rs.restorationIdx
+	order by likes desc
+	`;
+	const selectPopularRestorationRow = await connection.query(selectPopularRestorationQuery, restorationParams);
+	return selectPopularRestorationRow[0];
+}
+
+async function selectRestorationDetail(connection, restorationIdx) {
+	const selectRestorationDetailQuery = `
+	SELECT distinct(mp.multipleProblemIdx),B.likes,rs.restorationIdx,problem,isKatex,problemImage, questionIdx, questionNum,
+	case 
+		when questionImage is null then question
+		when questionImage = ' ' then question
+		when questionImage = '' then question
+		when question is null then questionImage
+		else
+		concat(question, '\\\\\\\\ ', questionImage)  end as question
+	FROM Restoration as rs join MultipleProblem as mp on rs.multipleProblemIdx=mp.multipleProblemIdx
+	join Question as qt on mp.multipleProblemIdx=qt.multipleProblemIdx
+	left join (select  rs.restorationIdx,count(if(rl.isLike=1,rl.isLike,null)) as likes
+	from Restoration as rs left join RestorationLike as rl on rs.restorationidx=rl.restorationIdx
+	group by rs.restorationIdx) as B on B.restorationIdx=rs.restorationIdx
+	WHERE rs.restorationIdx = ? and mp.status='N' order by questionNum
+	`;
+	const selectRestorationDetailRow = await connection.query(selectRestorationDetailQuery, restorationIdx);
+	return selectRestorationDetailRow[0];
+}
+
+async function insertRestoration(connection, restoreExamDetailIdx, multipleProblemIdx) {
+	const insertRestorationParams = [restoreExamDetailIdx, multipleProblemIdx];
+	const insertRestorationQuery = `
+	insert into Restoration(restoreExamDetailidx, multipleProblemIdx) values(?,?)
+	`;
+	const insertRestorationIdxRow = await connection.query(insertRestorationQuery, insertRestorationParams);
+	return insertRestorationIdxRow[0];
+}
+
+async function insertUserRestoration(connection, userIdx, restorationIdx) {
+	const insertUserRestorationParams = [userIdx, restorationIdx];
+	const insertUserRestorationQuery = `
+	insert into UserRestoration(userIdx, restorationIdx) values(?,?)
+	`;
+	const insertUserRestorationIdxRow = await connection.query(insertUserRestorationQuery, insertUserRestorationParams);
+	return insertUserRestorationIdxRow[0];
+}
+
+async function insertCustomProblem(connection, insertCustomProblemParams) {
+	const insertCustomProblemQuery = `
+	insert into CustomProblem(userIdx, problem, problemNum, comment, restoreExamDetailIdx) values(?,?,?,?,?)
+	`;
+	const insertCustomProblemIdxRow = await connection.query(insertCustomProblemQuery, insertCustomProblemParams);
+	return insertCustomProblemIdxRow[0];
+}
+
+async function insertCustomQuestion(connection, insertCustomQuestionParams) {
+	const insertCustomQuestionQuery = `
+	insert into CustomQuestion(customProblemIdx, questionNum, question) values ?
+	`;
+	const insertCustomQuestionRow = await connection.query(insertCustomQuestionQuery, insertCustomQuestionParams);
+	return insertCustomQuestionRow[0];
+}
+
+async function insertCustomImage(connection, insertCustomImageParams) {
+	const insertCustomImageQuery = `
+	insert into CustomProblemImage(customProblemIdx, imageUrl) values ?
+	`;
+	const insertCustomImageRow = await connection.query(insertCustomImageQuery, insertCustomImageParams);
+	return insertCustomImageRow[0];
+}
+
+// 복원 가능 시험 조회
+async function selectRestoreExam(connection) {
+	const selectRestoreExamQuery = `
+	SELECT red.restoreExamDetailIdx, red.examIdx, examName, restoreExamDate, restoreExamRound, red.thumbnail, e.examSortIdx, es.examSortRef, red.isPublic
+	FROM RestoreExamDetail red
+	 JOIN Exam e ON e.examIdx = red.examIdx and e.status = 'N'
+	 JOIN ExamSort es ON es.examSortIdx = e.examSortIdx and es.status = 'N'
+	where red.status = 'N'
+     `;
+	const selectRestoreExamRow = await connection.query(selectRestoreExamQuery);
+	return selectRestoreExamRow[0];
+}
+
+//문제 좋아요 체크
+async function checkRestorationLike(connection, checkRestorationLikeParams) {
+	const checkRestorationLikeQuery = `
+	SELECT restorationLikeIdx, isLike FROM RestorationLike WHERE userIdx = ? and restorationIdx = ?;
+	`;
+	const [checkRestorationLikeRow] = await connection.query(checkRestorationLikeQuery, checkRestorationLikeParams);
+	return checkRestorationLikeRow;
+}
+
+//문제 좋아요 생성
+async function insertRestorationLike(connection, insertRestorationLikeParams) {
+	const insertRestorationLikeQuery = `
+	INSERT INTO RestorationLike (userIdx, restorationIdx, isLike) 
+	VALUES (?, ?, 1);
+	`;
+	const [insertRestorationLikeRow] = await connection.query(insertRestorationLikeQuery, insertRestorationLikeParams);
+	return insertRestorationLikeRow;
+}
+
+//문제 좋아요 수정
+async function updateRestorationLike(connection, updateRestorationLikeParams) {
+	const updateRestorationLikeQuery = `
+	UPDATE RestorationLike 
+	SET isLike = ? 
+	WHERE restorationLikeIdx = ?;
+	`;
+	const [updateRestorationLikeRow] = await connection.query(updateRestorationLikeQuery, updateRestorationLikeParams);
+	return updateRestorationLikeRow;
+}
+
+//댓글 좋아요 체크
+async function checkRestorationCommentLike(connection, checkRestorationCommentLikeParams) {
+	const checkRestorationCommentLikeQuery = `
+	SELECT restorationCommentLikeIdx, isLike FROM RestorationCommentLike WHERE userIdx = ? and restorationCommentIdx = ?;
+	`;
+	const [checkRestorationCommentLikeRow] = await connection.query(
+		checkRestorationCommentLikeQuery,
+		checkRestorationCommentLikeParams,
+	);
+	return checkRestorationCommentLikeRow;
+}
+
+//댓글 좋아요 생성
+async function insertRestorationCommentLike(connection, insertCommentLikeParams) {
+	const insertRestorationCommentLikeQuery = `
+	INSERT INTO RestorationCommentLike (userIdx, restorationCommentIdx, isLike) 
+	VALUES (?, ?, 1);
+	`;
+	const [insertRestorationCommentLikeRow] = await connection.query(
+		insertRestorationCommentLikeQuery,
+		insertCommentLikeParams,
+	);
+	return insertRestorationCommentLikeRow;
+}
+
+//댓글 좋아요 수정
+async function updateRestorationCommentLike(connection, updateCommentLikeParams) {
+	const updateRestorationCommentLikeQuery = `
+	UPDATE RestorationCommentLike 
+	SET isLike = ? 
+	WHERE restorationCommentLikeIdx = ?
+	`;
+	const [updateRestorationCommentLikeRow] = await connection.query(
+		updateRestorationCommentLikeQuery,
+		updateCommentLikeParams,
+	);
+	return updateRestorationCommentLikeRow;
+}
+
+//댓글 입력
+async function insertRestorationComment(connection, insertRestorationCommentParams) {
+	const insertRestorationCommentQuery = `
+	INSERT INTO RestorationComment (userIdx, restorationIdx, comment) 
+	VALUES (?, ?, ?);
+	`;
+	const [insertRestorationCommentRow] = await connection.query(
+		insertRestorationCommentQuery,
+		insertRestorationCommentParams,
+	);
+	return insertRestorationCommentRow;
+}
+
+//댓글 조회
+async function selectRestorationComment(connection, selectRestorationCommentParams, order) {
+	const selectRestorationCommentQuery =
+		`
+	SELECT rc.restorationCommentIdx, u.nickname, u.userIdx, comment, count(rcl.restorationCommentLikeIdx) as commentLike, date_format(rc.createdAt, '%y/%m/%d %H:%i:%S') as updated,
+	(case
+		when A.isLike=1 then true
+		else false
+	end) as userLike,
+	(case
+		when rc.createdAt != rc.updatedAt then true
+        else false
+	end) as isEdited
+	FROM RestorationComment rc
+	JOIN User u on u.userIdx=rc.userIdx
+	LEFT JOIN RestorationCommentLike rcl ON rcl.restorationCommentIdx = rc.restorationCommentIdx and rcl.isLike = 1
+	LEFT JOIN (select isLike,rl.restorationCommentIdx from RestorationCommentLike as rl where userIdx=?) as A on A.restorationCommentIdx=rc.restorationCommentIdx
+	WHERE rc.restorationIdx = ? and rc.status='N' and rc.isPublic=1
+	GROUP BY rc.restorationCommentIdx
+	order by ` + order;
+	const [selectRestorationCommentRow] = await connection.query(
+		selectRestorationCommentQuery,
+		selectRestorationCommentParams,
+	);
+	return selectRestorationCommentRow;
+}
+
+//비공개 댓글 조회 (반환하는 것 바뀔 수도 있음. 만약에 바뀌지 않는다면 기존 dao 사용하는게 맞는 것 같음.)
+async function selectPrivateRestorationComment(connection, selectRestorationCommentParams) {
+	const selectRestorationCommentQuery = `
+	SELECT rc.restorationCommentIdx, u.nickname, u.userIdx, comment, count(rcl.restorationCommentLikeIdx) as commentLike, date_format(rc.createdAt, '%y/%m/%d %H:%i:%S') as updated,
+	(case
+		when A.isLike=1 then true
+		else false
+	end) as userLike,
+	(case
+		when rc.createdAt != rc.updatedAt then true
+        else false
+	end) as isEdited
+	FROM RestorationComment rc
+	JOIN User u on u.userIdx=rc.userIdx
+	LEFT JOIN RestorationCommentLike rcl ON rcl.restorationCommentIdx = rc.restorationCommentIdx and rcl.isLike = 1
+	LEFT JOIN (select isLike,rl.restorationCommentIdx from RestorationCommentLike as rl where userIdx=?) as A on A.restorationCommentIdx=rc.restorationCommentIdx
+	WHERE rc.restorationIdx = ? and rc.status='N' and rc.isPublic=0
+	GROUP BY rc.restorationCommentIdx
+	order by updated desc`;
+	const [selectRestorationCommentRow] = await connection.query(
+		selectRestorationCommentQuery,
+		selectRestorationCommentParams,
+	);
+	return selectRestorationCommentRow;
+}
+
+//베스트 댓글 조회
+async function selectTopRestorationComment(connection, selectRestorationCommentParams, top) {
+	const selectRestorationCommentQuery =
+		`
+	SELECT rc.restorationCommentIdx, u.nickname, u.userIdx, comment, count(rcl.restorationCommentLikeIdx) as commentLike, date_format(rc.createdAt, '%y/%m/%d %H:%i:%S') as updated,
+	(case
+		when A.isLike=1 then true
+		else false
+	end) as userLike,
+	(case
+		when rc.createdAt != rc.updatedAt then true
+        else false
+	end) as isEdited
+	FROM RestorationComment rc
+	JOIN User u on u.userIdx=rc.userIdx
+	LEFT JOIN RestorationCommentLike rcl ON rcl.restorationCommentIdx = rc.restorationCommentIdx and rcl.isLike = 1
+	LEFT JOIN (select isLike,rl.restorationCommentIdx from RestorationCommentLike as rl where userIdx=?) as A on A.restorationCommentIdx=rc.restorationCommentIdx
+	WHERE rc.restorationIdx = ? and rc.status='N' and rc.isPublic=1
+	GROUP BY rc.restorationCommentIdx
+	HAVING commentLike>0
+	order by commentLike desc, updated limit ` + top;
+	const [selectRestorationCommentRow] = await connection.query(
+		selectRestorationCommentQuery,
+		selectRestorationCommentParams,
+	);
+	return selectRestorationCommentRow;
+}
+
+//베스트 댓글 인덱스 조회
+async function selectTopRestorationCommentIdx(connection, restorationIdx, top) {
+	const selectRestorationCommentQuery =
+		`
+	SELECT rc.restorationCommentIdx
+	FROM RestorationComment rc
+	LEFT JOIN RestorationCommentLike rcl ON rcl.restorationCommentIdx = rc.restorationCommentIdx and rcl.isLike = 1
+	WHERE rc.restorationIdx = ? and rc.status='N' and rc.isPublic=1
+	GROUP BY rc.restorationCommentIdx
+	HAVING count(rcl.restorationCommentLikeIdx)>0
+	order by count(rcl.restorationCommentLikeIdx) desc, rc.updatedAt desc limit ` + top;
+	const [selectRestorationCommentRow] = await connection.query(selectRestorationCommentQuery, restorationIdx);
+	return selectRestorationCommentRow;
+}
+
+//비공개 베스트 댓글 인덱스 조회
+async function selectPrivateTopRestorationCommentIdx(connection, restorationIdx, top) {
+	const selectRestorationCommentQuery =
+		`
+	SELECT rc.restorationCommentIdx
+	FROM RestorationComment rc
+	LEFT JOIN RestorationCommentLike rcl ON rcl.restorationCommentIdx = rc.restorationCommentIdx and rcl.isLike = 1
+	WHERE rc.restorationIdx = ? and rc.status='N' and rc.isPublic=0
+	GROUP BY rc.restorationCommentIdx
+	HAVING count(rcl.restorationCommentLikeIdx)>0
+	order by count(rcl.restorationCommentLikeIdx) desc, rc.updatedAt desc limit ` + top;
+	const [selectRestorationCommentRow] = await connection.query(selectRestorationCommentQuery, restorationIdx);
+	return selectRestorationCommentRow;
+}
+
+//댓글 유저 체크
+async function checkCommentUser(connection, restorationCommentIdx) {
+	const checkCommentUserQuery = `
+	SELECT userIdx, status 
+	FROM RestorationComment 
+	WHERE restorationCommentIdx = ?
+	`;
+	const [checkCommentUserRow] = await connection.query(checkCommentUserQuery, restorationCommentIdx);
+	return checkCommentUserRow;
+}
+
+//댓글 수정
+async function updateRestorationComment(connection, updateCommentParams) {
+	const updateRestorationCommentQuery = `
+	UPDATE RestorationComment 
+	SET comment = ?, updatedAt=current_timestamp()
+	WHERE restorationCommentIdx = ?;
+	`;
+	const [updateRestorationCommentRow] = await connection.query(updateRestorationCommentQuery, updateCommentParams);
+	return updateRestorationCommentRow;
+}
+
+//댓글 삭제
+async function deleteRestorationComment(connection, restorationCommentIdx) {
+	const deleteRestorationCommentQuery = `
+	UPDATE RestorationComment
+	SET status = 'Y'
+	WHERE restorationCommentIdx = ?
+	`;
+	const [deleteRestorationCommentRow] = await connection.query(deleteRestorationCommentQuery, restorationCommentIdx);
+	return deleteRestorationCommentRow;
+}
+
+//restorationIdx 체크
+async function checkRestorationIdx(connection, restorationIdx) {
+	const checkRestorationIdxQuery = `
+	SELECT restorationIdx FROM Restoration WHERE restorationIdx = ? and status ='N';
+	`;
+	const [checkRestorationIdxRow] = await connection.query(checkRestorationIdxQuery, restorationIdx);
+	return checkRestorationIdxRow;
+}
+
+//restorationCommentIdx 체크
+async function checkRestorationCommentIdx(connection, restorationCommentIdx) {
+	const checkRestorationCommentIdxQuery = `
+	SELECT restorationCommentIdx FROM RestorationComment WHERE restorationCommentIdx = ? and status='N';
+	`;
+	const [checkRestorationCommentIdxRow] = await connection.query(
+		checkRestorationCommentIdxQuery,
+		restorationCommentIdx,
+	);
+	return checkRestorationCommentIdxRow;
+}
+
+async function insertExamAdmissionTicket(connection, insertExamAdmissionTicketParams) {
+	const insertExamAdmissionTicketQuery = `
+	INSERT INTO ExamAdmissionTicket (userIdx, restoreExamDetailIdx, examAdmissionTicketImage) VALUES (?, ?, ?);
+	`;
+	const [insertExamAdmissionTicketRow] = await connection.query(
+		insertExamAdmissionTicketQuery,
+		insertExamAdmissionTicketParams,
+	);
+	return insertExamAdmissionTicketRow;
+}
+
+async function checkExamAdmissionTicket(connection, checkExamAdmissionTicketParams) {
+	const checkExamAdmissionTicketQuery = `
+	SELECT examAdmissionTicketIdx
+	FROM ExamAdmissionTicket
+	WHERE userIdx = ? and restoreExamDetailIdx = ? and status = 'N';
+	`;
+	const [checkExamAdmissionTicketRow] = await connection.query(
+		checkExamAdmissionTicketQuery,
+		checkExamAdmissionTicketParams,
+	);
+	return checkExamAdmissionTicketRow;
+}
+
+async function selectExamAdmissionTicket(connection, selectExamAdmissionTicketParams) {
+	const selectExamAdmissionTicketQuery = `
+	SELECT isAuthentication as auth
+	FROM ExamAdmissionTicket
+	WHERE userIdx = ? and restoreExamDetailIdx = ? and status = 'N';
+	`;
+	const [selectExamAdmissionTicketRow] = await connection.query(
+		selectExamAdmissionTicketQuery,
+		selectExamAdmissionTicketParams,
+	);
+	return selectExamAdmissionTicketRow;
+}
+
+async function selectRestoreExamName(connection, restoreExamDetailIdx) {
+	const selectRestoreExamNameQuery = `
+	select concat(year(re.restoreExamDate),' ', re.restoreExamRound,' ', e.examName) as examName from RestoreExamDetail as re join Exam as e on re.examIdx=e.examIdx where re.restoreExamDetailIdx=?
+	`;
+	const [selectRestoreExamRow] = await connection.query(selectRestoreExamNameQuery, restoreExamDetailIdx);
+	return selectRestoreExamRow;
+}
+
+//복원 시험 종류 조회
+// async function selectRestoreExamSortRef(connection) {
+// 	const selectRestoreExamSortQuery = `
+// 	SELECT distinct es.examSortRef
+// 	FROM RestoreExamDetail red
+// 	LEFT JOIN Exam e ON e.examIdx = red.examIdx
+// 	LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx
+// 	where red.status = 'N' and isPublic = 1
+// 	`;
+// 	const [selectRestoreExamSortRow] = await connection.query(selectRestoreExamSortQuery);
+// 	return selectRestoreExamSortRow;
+// }
+
+//복원 시험 종류 조회
+async function selectRestoreExamSort(connection, where) {
+	const selectRestoreExamSortQuery =
+		`
+	SELECT distinct es.examSortIdx
+	FROM RestoreExamDetail red
+	LEFT JOIN Exam e ON e.examIdx = red.examIdx
+	LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx
+	` + where;
+	const [selectRestoreExamSortRow] = await connection.query(selectRestoreExamSortQuery);
+	return selectRestoreExamSortRow;
+}
+
+//복원 시험 분류 조회
+// async function selectRestoreSortLevel(connection, category) {
+// 	const selectRestoreExamSortQuery =
+// 		`
+// 	SELECT distinct es.examSortIdx
+// 	FROM RestoreExamDetail red
+// 	LEFT JOIN Exam e ON e.examIdx = red.examIdx
+// 	LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx
+// 	` + where;
+// 	const [selectRestoreExamSortRow] = await connection.query(selectRestoreExamSortQuery);
+// 	return selectRestoreExamSortRow;
+// }
+
+module.exports = {
+	selectSearchResult,
+	selectMultipleSet,
+	selectRestorationIdx,
+	selectRestoreExamDetailIdx,
+	selectLatestRestoration,
+	selectPopularRestoration,
+	selectRestoreExam,
+	selectRestorationDetail,
+	selectPrivateRestorationComment,
+	selectPrivateTopRestorationCommentIdx,
+	insertRestoration,
+	insertUserRestoration,
+	insertCustomProblem,
+	insertCustomQuestion,
+	insertCustomImage,
+	checkRestorationLike,
+	insertRestorationLike,
+	updateRestorationLike,
+	checkRestorationCommentLike,
+	insertRestorationCommentLike,
+	updateRestorationCommentLike,
+	insertRestorationComment,
+	selectRestorationComment,
+	selectTopRestorationCommentIdx,
+	selectTopRestorationComment,
+	checkCommentUser,
+	updateRestorationComment,
+	deleteRestorationComment,
+	checkRestorationIdx,
+	checkRestorationCommentIdx,
+	insertExamAdmissionTicket,
+	checkExamAdmissionTicket,
+	selectExamAdmissionTicket,
+	selectRestoreExamName,
+	selectRestoreExamSort,
+	selectKatexSearchResult,
+};
diff --git a/_old/src/Restore/restoreProvider.js b/_old/src/Restore/restoreProvider.js
new file mode 100644
index 0000000..5644661
--- /dev/null
+++ b/_old/src/Restore/restoreProvider.js
@@ -0,0 +1,580 @@
+"use strict";
+
+const { pool } = require("../../config/database");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const restoreDao = require("./restoreDao");
+const adminDao = require("../Admin/adminDao");
+const examDao = require("../Exam/examDao");
+const examProvider = require("../Exam/examProvider");
+const { logger } = require("../../config/winston");
+const errorResponse = require("../../utils/errorResponse");
+
+// 일반 검색 결과 조회
+exports.getSearchResult = async function (finalString, highScoreString, BestScoreString) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let array = [];
+		let resultObj = {};
+		const selectSearchResult = await restoreDao.selectSearchResult(
+			connection,
+			finalString,
+			highScoreString,
+			BestScoreString,
+		);
+		for (let i = 0; i < selectSearchResult.length; i++) {
+			let current = selectSearchResult[i];
+			let object = {
+				multipleProblemIdx: current.multipleProblemIdx,
+				problem: current.problem,
+				isKatex: current.isKatex,
+				problemImage: current.problemImage,
+			};
+			array.push(object);
+		}
+		resultObj.data = array;
+		return resultResponse(baseResponse.SUCCESS, resultObj);
+	} catch (error) {
+		logger.error(`App - getSearchResult Provider error\n: ${error.message}`);
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+/**
+ * API No.
+ * API Name : 검색 API
+ * [GET] /restore/search
+ */
+//refactoring necessary
+
+exports.makeSearchString = function (newString) {
+	let plusStrArr = [];
+	let minusStrArr = [];
+	let defaultStrArr = [];
+	let splitString = newString.split(" ");
+
+	for (let i = 0; i < splitString.length; i++) {
+		// 추가검색어, 제외검색어, 기본검색어 분리
+		if (splitString[i].charAt(0) == "+") plusStrArr.push(splitString[i]);
+		else if (splitString[i].charAt(0) == "-") minusStrArr.push(splitString[i]);
+		else defaultStrArr.push(splitString[i]);
+
+		// 가산 문자열 생성
+		splitString[i] =
+			splitString[i] != "" &&
+			splitString[i].charAt(0) != "+" &&
+			splitString[i].charAt(0) != "-" &&
+			splitString[i].length > 1
+				? "+" + splitString[i]
+				: splitString[i];
+	}
+
+	let finalString = ["+(" + defaultStrArr.join(" ") + ")", plusStrArr.join(" "), minusStrArr.join(" ")].join(" ");
+
+	//창우님 추가,제외,기본 검색어 분리 코드
+	//기본 검색어에 추가 검색어하면 0개 나와야하는 부분은 보완되어야 합니다.
+	// newString = searchString;
+	// let SplitText = searchString.split(" ").sort().reverse();
+	// let point = SplitText.findIndex((item) => item.includes("+") || item.includes("-"));12
+	// if (point > 0) newString = "+(" + SplitText.slice(0, point).join(" ") + ") " + SplitText.slice(point).join(" ");
+
+	let highScoreString = splitString.join(" ");
+
+	return { finalString, highScoreString };
+};
+
+exports.getKatexSearchResult = async function (searchParams, searchQuery, orderQuery) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let array = [];
+		let resultObj = {};
+		const selectSearchResult = await restoreDao.selectKatexSearchResult(
+			connection,
+			searchParams,
+			searchQuery,
+			orderQuery,
+		);
+		for (let i = 0; i < selectSearchResult.length; i++) {
+			let current = selectSearchResult[i];
+			let object = {
+				multipleProblemIdx: current.multipleProblemIdx,
+				problem: current.problem,
+				isKatex: current.isKatex,
+				problemImage: current.problemImage,
+			};
+			array.push(object);
+		}
+		resultObj.data = array;
+		return resultResponse(baseResponse.SUCCESS, resultObj);
+	} catch (error) {
+		logger.error(`App - getKatexSearchResult Provider error\n: ${error.message}`);
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getDetailResult = async function (multipleProblemIdxes, multipleParams) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let array = [];
+		let object = {};
+		const selectDetailResult = await restoreDao.selectMultipleSet(connection, multipleProblemIdxes, multipleParams);
+
+		for (let i = 0; i < selectDetailResult.length; i++) {
+			let current = selectDetailResult[i];
+			let prev;
+			let next;
+
+			if (i != 0) prev = selectDetailResult[i - 1];
+			if (i + 1 != selectDetailResult.length) next = selectDetailResult[i + 1];
+
+			if (!prev || current.multipleProblemIdx != prev.multipleProblemIdx) {
+				object = {};
+				object.multipleProblemIdx = current.multipleProblemIdx;
+				object.problem = current.problem;
+				object.isKatex = current.isKatex;
+				object.problemImage = current.problemImage;
+				object.questions = [];
+			}
+			object.questions.push({
+				questionNum: current.questionNum,
+				question: current.question,
+				questionIdx: current.questionIdx,
+			});
+			if (!next || current.multipleProblemIdx != next.multipleProblemIdx) {
+				array.push(object);
+			}
+		}
+		return resultResponse(baseResponse.SUCCESS, array);
+	} catch (error) {
+		logger.error(`App - getDetailResult Provider error\n: ${error.message}`);
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getRestorationDetail = async function (userIdx, restorationIdx) {
+	let object = {};
+	let daoArray = [];
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		daoArray.push(restoreDao.selectRestorationDetail(connection, restorationIdx));
+		daoArray.push(restoreDao.checkRestorationLike(connection, [userIdx, restorationIdx]));
+		const promiseResult = await Promise.all(daoArray);
+
+		const restorationResult = promiseResult[0];
+		const restorationCheckResult = promiseResult[1];
+
+		if (restorationCheckResult.length === 0 || !restorationCheckResult[0].isLike) object.userLike = 0;
+		else if (restorationCheckResult[0].isLike) {
+			object.userLike = 1;
+		} else throw "unknown isLike Value : " + restorationCheckResult[0].isLike;
+
+		object.multipleProblemIdx = restorationResult[0].multipleProblemIdx;
+		object.problem = restorationResult[0].problem;
+		object.isKatex = restorationResult[0].isKatex;
+		object.problemImage = restorationResult[0].problemImage;
+		object.likes = restorationResult[0].likes;
+		object.questions = [];
+
+		for (let i = 0; i < restorationResult.length; i++) {
+			object.questions.push({
+				questionNum: restorationResult[i].questionNum,
+				question: restorationResult[i].question,
+				questionIdx: restorationResult[i].questionIdx,
+			});
+		}
+		return resultResponse(baseResponse.SUCCESS, object);
+	} catch (error) {
+		logger.error(`App - getRestorationDetail Provider error\n: ${error.message}`);
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getRestorationIdx = async function (restoreExamDetailIdx, multipleProblemIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const restorationCheckResult = await restoreDao.selectRestorationIdx(
+			connection,
+			restoreExamDetailIdx,
+			multipleProblemIdx,
+		);
+		return restorationCheckResult;
+	} catch (error) {
+		logger.error(`App - getRestorationIdx Provider error\n: ${error.message}`);
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+exports.restoreExamDetailIdxCheck = async function (restoreExamDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const restoreExamDetailIdxCheckResult = await restoreDao.selectRestoreExamDetailIdx(
+			connection,
+			restoreExamDetailIdx,
+		);
+		if (restoreExamDetailIdxCheckResult.length === 0) throw new errorResponse(baseResponse.RESTORE_EXAM_NOT_EXIST, 404);
+		return restoreExamDetailIdxCheckResult;
+	} catch (error) {
+		logger.error(`App - restoreExamDetailIdxCheck Provider error\n: ${error.message}`);
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getExamList = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let examSortList = [];
+		//const examSortRefList = await restoreDao.selectRestoreExamSortRef(connection);
+
+		const restoreExamResult = await restoreDao.selectRestoreExam(connection);
+		for (let object of restoreExamResult) {
+			let examSortInfo = {};
+
+			const examIdx = object.examIdx;
+			const lDivision = await examProvider.getExamDivision(examIdx, "L");
+			const mDivision = await examProvider.getExamDivision(examIdx, "M");
+			let examSort = null;
+			let examSortRef = null;
+
+			if (lDivision[0] != undefined) {
+				examSortRef = lDivision[0].examSortName;
+			}
+
+			if (mDivision[0] != undefined) {
+				examSort = mDivision[0].examSortName;
+			}
+			//examSortInfo[lDivision[0].examSortName] = mDivision[0].examSortName;
+
+			if (examSortList.length != 0) {
+				for (let i = 0; i < examSortList.length; i++) {
+					if (examSortList[i].examSortRef == examSortRef) {
+						if (examSortList[i].examSort.includes(examSort)) {
+							break;
+						}
+						examSortList[i].examSort.push(examSort);
+						i = -1;
+					}
+					if (i == examSortList.length - 1) {
+						examSortInfo = { examSortRef: examSortRef, examSort: [examSort] };
+						examSortList.push(examSortInfo);
+					}
+				}
+
+				// if (object2.examSortRef == lDivision[0].examSortName) {
+				// 	if (object2.examSort.includes(mDivision[0].examSortName)) {
+				// 		console.log(object2);
+				// 		console.log(mDivision[0].examSortName);
+				// 		break;
+				// 	}
+				// 	object2.examSort.push(mDivision[0].examSortName);
+				// 	break;
+				// } else {
+				// 	examSortInfo = { examSortRef: lDivision[0].examSortName, examSort: [mDivision[0].examSortName] };
+				// 	examSortList.push(examSortInfo);
+				// }
+			} else {
+				examSortInfo = { examSortRef: examSortRef, examSort: [examSort] };
+				examSortList.push(examSortInfo);
+			}
+
+			// examSortInfo = { examSortRef: lDivision[0].examSortName, examSort: mDivision[0].examSortName };
+			// examSortList.push(examSortInfo);
+
+			// let where = "where  examSortRef = '" + object.examSortRef + "' AND red.status= 'N'";
+			// let examSort = await restoreDao.selectRestoreExamSort(connection, where);
+			// examSortInfo[object.examSortRef] = examSort;
+			object.examSort = examSort;
+			object.examSortRef = examSortRef;
+		}
+		let RestoreExamList = {
+			examSortList: examSortList,
+			examList: restoreExamResult,
+		};
+		//console.log(RestoreExamList);
+		return resultResponse(baseResponse.SUCCESS, RestoreExamList);
+	} catch (error) {
+		logger.error(`App - getExamList Provider error\n: ${error.message}`);
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getRestoration = async function (restorationOrder, userIdx, restoreExamDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let restorationResult;
+
+		let restorationParams = [userIdx, restoreExamDetailIdx];
+
+		if (restorationOrder == "recent") {
+			restorationResult = await restoreDao.selectLatestRestoration(connection, restorationParams);
+		} else if (restorationOrder == "popular") {
+			restorationResult = await restoreDao.selectPopularRestoration(connection, restorationParams);
+		} else throw "unknown restorationOrder : " + restorationOrder;
+		return resultResponse(baseResponse.SUCCESS, restorationResult);
+	} catch (error) {
+		logger.error(`App - restoration Provider error\n: ${error.message}`);
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+//문제 좋아요 체크
+exports.restorationLikeCheck = async function (userIdx, restorationIdx) {
+	const connection = await pool.getConnection((conn) => conn);
+	try {
+		const checkRestorationLikeParams = [userIdx, restorationIdx];
+		const restorationLikeCheckResult = await restoreDao.checkRestorationLike(connection, checkRestorationLikeParams);
+		return restorationLikeCheckResult;
+	} catch (error) {
+		logger.error(`App - restorationLikeCheck Provider error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+//댓글 좋아요 체크
+exports.restorationCommentLikeCheck = async function (userIdx, restorationCommentIdx) {
+	const connection = await pool.getConnection((conn) => conn);
+	try {
+		const restorationCommentLikeCheckParams = [userIdx, restorationCommentIdx];
+		const restorationCommentLikeCheckResult = await restoreDao.checkRestorationCommentLike(
+			connection,
+			restorationCommentLikeCheckParams,
+		);
+		return restorationCommentLikeCheckResult;
+	} catch (error) {
+		logger.error(`App - restorationCommentLikeCheck Provider error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+//restorationIdx 체크
+exports.restorationIdxCheck = async function (restorationIdx) {
+	const connection = await pool.getConnection((conn) => conn);
+	try {
+		const restorationIdxCheckResult = await restoreDao.checkRestorationIdx(connection, restorationIdx);
+		if (restorationIdxCheckResult.length === 0) throw new errorResponse(baseResponse.RESTORATION_NOT_EXIST, 404);
+		return restorationIdxCheckResult;
+	} catch (error) {
+		logger.error(`App - restorationIdxCheck Provider error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+//restorationCommentIdx 체크
+exports.restorationCommentIdxCheck = async function (restorationCommentIdx) {
+	const connection = await pool.getConnection((conn) => conn);
+	try {
+		const restorationCommentIdxCheckResult = await restoreDao.checkRestorationCommentIdx(
+			connection,
+			restorationCommentIdx,
+		);
+		if (restorationCommentIdxCheckResult.length === 0)
+			throw new errorResponse(baseResponse.RESTORATION_COMMENT_NOT_EXIST, 404);
+		return restorationCommentIdxCheckResult;
+	} catch (error) {
+		logger.error(`App - restorationCommentIdxCheck Provider error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+//댓글 조회
+//refactoring required
+exports.getRestorationComment = async function (userIdx, restorationIdx, order, top) {
+	const connection = await pool.getConnection((conn) => conn);
+	const selectRestorationCommentParams = [userIdx, restorationIdx];
+	try {
+		let orderString;
+		if (order == "recent") {
+			orderString = "updated desc";
+		} else if (order == "popular") {
+			orderString = "commentLike desc, updated";
+		} else throw "unknown comment order : " + order;
+
+		const selectRestorationCommentResult = await restoreDao.selectRestorationComment(
+			connection,
+			selectRestorationCommentParams,
+			orderString,
+		);
+
+		if (order == "recent") {
+			const selectTopCommentIdxResult = await restoreDao.selectTopRestorationCommentIdx(
+				connection,
+				restorationIdx,
+				top,
+			);
+			for (let i = 0; i < selectRestorationCommentResult.length; i++) {
+				selectRestorationCommentResult[i].isBestComment = 0;
+				for (let j = 0; j < selectTopCommentIdxResult.length; j++) {
+					if (
+						selectRestorationCommentResult[i].restorationCommentIdx ==
+						selectTopCommentIdxResult[j].restorationCommentIdx
+					) {
+						selectRestorationCommentResult[i].isBestComment = 1;
+						break;
+					}
+				}
+			}
+		} else if (order == "popular") {
+			for (let i = 0; i < selectRestorationCommentResult.length; i++) {
+				selectRestorationCommentResult[i].isBestComment =
+					i < top && selectRestorationCommentResult[i].commentLike > 0 ? 1 : 0;
+			}
+		}
+		return resultResponse(baseResponse.SUCCESS, selectRestorationCommentResult);
+	} catch (error) {
+		logger.error(`App - getRestorationComment Provider error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getTopRestorationComment = async function (userIdx, restorationIdx, top) {
+	const selectRestorationCommentParams = [userIdx, restorationIdx];
+	const connection = await pool.getConnection((conn) => conn);
+	try {
+		const selectRestorationCommentResult = await restoreDao.selectTopRestorationComment(
+			connection,
+			selectRestorationCommentParams,
+			top,
+		);
+		return resultResponse(baseResponse.SUCCESS, selectRestorationCommentResult);
+	} catch (error) {
+		logger.error(`App - getRestorationComment Provider error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+//댓글 사용자 체크
+exports.commentUserCheck = async function (restorationCommentIdx, userIdx) {
+	const connection = await pool.getConnection((conn) => conn);
+	try {
+		const checkCommentUserResult = await restoreDao.checkCommentUser(connection, restorationCommentIdx);
+		if (commentUserCheckResult.length === 0) throw new errorResponse(baseResponse.RESTORATION_COMMENT_NOT_EXIST, 404);
+		if (commentUserCheckResult[0].userIdx !== userIdx)
+			throw new errorResponse(baseResponse.COMMENTER_MATCH_FAILED, 400);
+		if (commentUserCheckResult[0].status === "Y") throw new errorResponse(baseResponse.ISDELETED, 400); //에러 문구 수정?
+		return checkCommentUserResult;
+	} catch (error) {
+		logger.error(`App - commentUserCheck Provider error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+exports.examAdmissionTicketCheck = async function (userIdx, restoreExamDetailIdx) {
+	const connection = await pool.getConnection((conn) => conn);
+
+	try {
+		const checkExamAdmissionTicketParams = [userIdx, restoreExamDetailIdx];
+		const checkExamAdmissionTicketResult = await restoreDao.checkExamAdmissionTicket(
+			connection,
+			checkExamAdmissionTicketParams,
+		);
+		return checkExamAdmissionTicketResult;
+	} catch (error) {
+		logger.error(`App - examAdmissionTicketCheck Provider error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getExamAdmissionTicket = async function (userIdx, restoreExamDetailIdx) {
+	const connection = await pool.getConnection((conn) => conn);
+	try {
+		const selectExamAdmissionTicketParams = [userIdx, restoreExamDetailIdx];
+		const selectExamAdmissionTicketResult = await restoreDao.selectExamAdmissionTicket(
+			connection,
+			selectExamAdmissionTicketParams,
+		);
+		return resultResponse(baseResponse.SUCCESS, selectExamAdmissionTicketResult[0]);
+	} catch (error) {
+		logger.error(`App - getExamAdmissionTicket Provider error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getRestoreExamName = async function (restoreExamDetailIdx) {
+	const connection = await pool.getConnection((conn) => conn);
+	try {
+		const getRestoreExamNameResult = await restoreDao.selectRestoreExamName(connection, restoreExamDetailIdx);
+		return resultResponse(baseResponse.SUCCESS, getRestoreExamNameResult[0]);
+	} catch (error) {
+		logger.error(`App - getRestoreExamName Provider error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getPrivateRestorationComment = async function (userIdx, restorationIdx, top) {
+	const connection = await pool.getConnection((conn) => conn);
+	const selectRestorationCommentParams = [userIdx, restorationIdx];
+	try {
+		const selectRestorationCommentResult = await restoreDao.selectPrivateRestorationComment(
+			connection,
+			selectRestorationCommentParams,
+		);
+
+		const selectTopCommentIdxResult = await restoreDao.selectPrivateTopRestorationCommentIdx(
+			connection,
+			restorationIdx,
+			top,
+		);
+		for (let i = 0; i < selectRestorationCommentResult.length; i++) {
+			selectRestorationCommentResult[i].isBestComment = 0;
+			for (let j = 0; j < selectTopCommentIdxResult.length; j++) {
+				if (
+					selectRestorationCommentResult[i].restorationCommentIdx == selectTopCommentIdxResult[j].restorationCommentIdx
+				) {
+					selectRestorationCommentResult[i].isBestComment = 1;
+					break;
+				}
+			}
+		}
+		return resultResponse(baseResponse.SUCCESS, selectRestorationCommentResult);
+	} catch (error) {
+		logger.error(`App - getRestorationComment Provider error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	} finally {
+		connection.release();
+	}
+};
diff --git a/_old/src/Restore/restoreRoute.js b/_old/src/Restore/restoreRoute.js
new file mode 100644
index 0000000..cb724c9
--- /dev/null
+++ b/_old/src/Restore/restoreRoute.js
@@ -0,0 +1,72 @@
+module.exports = function (app) {
+	const restore = require("./restoreController");
+	const jwtMiddleware = require("../../middlewares/jwtMiddleware");
+	// API No ?. 복원 DB 검색
+	app.get("/api/restores/search", jwtMiddleware, restore.getSearchResult);
+
+	// API No ?. 복원 DB 상세 검색
+	app.post("/api/restores/search/detail", jwtMiddleware, restore.getDetailResult);
+
+	// API No ?. 복원 담기
+	app.post("/api/restores", jwtMiddleware, restore.restore);
+
+	// API No ?. 복원 문제 직접 생성
+	app.post("/api/restores/custom", jwtMiddleware, restore.customizeProblem);
+
+	// API No ?. 복원 현황 조회(최신순)
+	app.get("/api/restores/board/:restoreExamDetailIdx/latest", jwtMiddleware, restore.getLatestRestoration);
+
+	// API No ?. 복원 현황 조회(인기순)
+	app.get("/api/restores/board/:restoreExamDetailIdx/popular", jwtMiddleware, restore.getPopularRestoration);
+
+	// API No ?. 복원 현황 상세 조회
+	app.get("/api/restores/board/:restoreExamDetailIdx/:restorationIdx", jwtMiddleware, restore.getRestorationDetail);
+
+	// API No ?. 복원 시험 목록 페이지
+	app.get("/api/restores/examList", jwtMiddleware, restore.getRestoreExamList);
+
+	// API No ?. 복원 문제 좋아요
+	app.post("/api/restores/restoration/like", jwtMiddleware, restore.postRestorationLike);
+
+	// API No ?. 복원 댓글 좋아요
+	app.post("/api/restores/restoration/comment/like", jwtMiddleware, restore.postRestorationCommentLike);
+
+	// API No ?. 복원 댓글 입력
+	app.post("/api/restores/upload/restoration/comment", jwtMiddleware, restore.postRestorationComment);
+
+	// API No ?. 복원 댓글 조회
+	app.get("/api/restores/restoration/:restorationIdx/comment", jwtMiddleware, restore.getRestorationComment);
+
+	// API No ?. 복원 댓글 수정
+	app.patch(
+		"/api/restores/update/restoration/comment/:restorationCommentIdx",
+		jwtMiddleware,
+		restore.updateRestorationComment,
+	);
+
+	// API No ?. 복원 댓글 삭제
+	app.patch(
+		"/api/restores/delete/restoration/comment/:restorationCommentIdx",
+		jwtMiddleware,
+		restore.deleteRestorationComment,
+	);
+
+	// API No ?. 비공개 댓글 리스트 조회
+	app.get(
+		"/api/restores/restoration/:restorationIdx/comment/private",
+		jwtMiddleware,
+		restore.getPrivateRestorationComment,
+	);
+
+	// API No ?. 복원 베스트 댓글
+	app.get("/api/restores/restoration/:restorationIdx/comment/:top", jwtMiddleware, restore.getTopRestorationComment);
+
+	// API No ?. 수험표 인증 생성
+	app.post("/api/restores/upload/admission/ticket", jwtMiddleware, restore.postExamAdmissionTicket);
+
+	// API No ?. 수험표 인증 조회
+	app.get("/api/restores/admission/ticket", jwtMiddleware, restore.getExamAdmissionTicket);
+
+	// API No ?. 복원 시험 이름 조회
+	app.get("/api/restores/exam/", jwtMiddleware, restore.getRestoreExamName);
+};
diff --git a/_old/src/Restore/restoreService.js b/_old/src/Restore/restoreService.js
new file mode 100644
index 0000000..d290ea9
--- /dev/null
+++ b/_old/src/Restore/restoreService.js
@@ -0,0 +1,252 @@
+const { pool } = require("../../config/database");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const errorResponse = require("../../utils/errorResponse");
+const restoreDao = require("./restoreDao");
+const restoreProvider = require("../Restore/restoreProvider");
+const { logger } = require("../../config/winston");
+
+exports.insertRestoration = async function (userIdx, restoreExamDetailIdx, multipleProblemIdx, comment) {
+	let restorationIdx;
+	let daoArray = [];
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			const restorationCheckResult = await restoreProvider.getRestorationIdx(restoreExamDetailIdx, multipleProblemIdx);
+			await connection.beginTransaction();
+			if (restorationCheckResult.length) {
+				restorationIdx = restorationCheckResult[0].restorationIdx;
+			} else {
+				insertRestorationResult = await restoreDao.insertRestoration(
+					connection,
+					restoreExamDetailIdx,
+					multipleProblemIdx,
+				);
+				restorationIdx = insertRestorationResult.insertId;
+			}
+
+			//(userIdx, restorationIdx) 중복되지 않아야함. Validation
+
+			daoArray.push(restoreDao.insertUserRestoration(connection, userIdx, restorationIdx));
+			if (comment) daoArray.push(restoreDao.insertRestorationComment(connection, [userIdx, restorationIdx, comment]));
+
+			await Promise.all(daoArray);
+			await connection.commit();
+			return basickResponse(baseResponse.SUCCESS);
+		} catch (error) {
+			await connection.rollback();
+			logger.error(`App - insertRestoration Service error\n: ${error.message}`);
+			throw new errorResponse(baseResponse.DB_ERROR, 500);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		logger.error(`App - insertRestoration Service connection error\n: ${error.message}`);
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	}
+};
+
+exports.insertCustom = async function (
+	userIdx,
+	problemNum,
+	problem,
+	comment,
+	restoreExamDetailIdx,
+	imageUrl,
+	question,
+) {
+	let customProblemParams = [userIdx, problem, problemNum, comment, restoreExamDetailIdx];
+	let customQuestionParams = [];
+	let imageUrlParams = [];
+	let daoArray = [];
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await connection.beginTransaction();
+
+			//CustomProblem Insert
+			const customProblemInsertResult = await restoreDao.insertCustomProblem(connection, customProblemParams);
+			const customProblemIdx = customProblemInsertResult.insertId;
+
+			//CustomQuestion Insert
+			for (let i = 0; i < question.length; i++) {
+				customQuestionParams.push([customProblemIdx, i + 1, question[i]]);
+			}
+			daoArray.push(restoreDao.insertCustomQuestion(connection, [customQuestionParams]));
+
+			//CustomProblemImage Insert
+			if (imageUrl && imageUrl.length) {
+				for (let i = 0; i < imageUrl.length; i++) {
+					imageUrlParams.push([customProblemIdx, imageUrl[i]]);
+				}
+				daoArray.push(restoreDao.insertCustomImage(connection, [imageUrlParams]));
+			}
+			await Promise.all(daoArray);
+			await connection.commit();
+			return basickResponse(baseResponse.SUCCESS);
+		} catch (error) {
+			await connection.rollback();
+			logger.error(`App - insertCustom Service error\n: ${error.message}`);
+
+			throw new errorResponse(baseResponse.DB_ERROR, 500);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		logger.error(`App - insertCustom Service connection error\n: ${error.message}`);
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	}
+};
+
+//문제 좋아요 생성
+exports.createRestorationLike = async function (userIdx, restorationIdx) {
+	const insertRestorationLikeParams = [userIdx, restorationIdx];
+	try {
+		const connection = await pool.getConnection((conn) => conn);
+		try {
+			await restoreDao.insertRestorationLike(connection, insertRestorationLikeParams);
+			return resultResponse(baseResponse.SUCCESS, { userLike: 1 });
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		logger.error(`App createRestorationLike Service error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	}
+};
+
+//문제 좋아요 수정
+exports.updateRestorationLike = async function (restorationLikeIdx, isLike, userIdx, restorationIdx) {
+	const updateRestorationLikeParams = [!isLike, restorationLikeIdx];
+	try {
+		const connection = await pool.getConnection((conn) => conn);
+		try {
+			await restoreDao.updateRestorationLike(connection, updateRestorationLikeParams);
+			let checkRestorationLikeResult = await restoreProvider.restorationLikeCheck(userIdx, restorationIdx);
+			return resultResponse(baseResponse.SUCCESS, { userLike: checkRestorationLikeResult[0].isLike });
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		logger.error(`App - updateRestorationLike Service error:\n ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	}
+};
+
+//댓글 좋아요 생성
+exports.createRestorationCommentLike = async function (userIdx, restorationCommentIdx) {
+	const insertCommentLikeParams = [userIdx, restorationCommentIdx];
+	try {
+		const connection = await pool.getConnection((conn) => conn);
+		try {
+			await restoreDao.insertRestorationCommentLike(connection, insertCommentLikeParams);
+			return resultResponse(baseResponse.SUCCESS, { userLike: 1 });
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		logger.error(`App - createRestorationCommentLike Service error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	}
+};
+
+//댓글 좋아요 수정
+exports.updateRestorationCommentLike = async function (
+	restorationCommentLikeIdx,
+	isLike,
+	userIdx,
+	restorationCommentIdx,
+) {
+	const updateRestorationCommentLikeParams = [!isLike, restorationCommentLikeIdx];
+	try {
+		const connection = await pool.getConnection((conn) => conn);
+		try {
+			await restoreDao.updateRestorationCommentLike(connection, updateRestorationCommentLikeParams);
+			let checkRestorationCommentLikeResult = await restoreProvider.restorationCommentLikeCheck(
+				userIdx,
+				restorationCommentIdx,
+			);
+			return resultResponse(baseResponse.SUCCESS, { userLike: checkRestorationCommentLikeResult[0].isLike });
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		logger.error(`App - updateRestorationCommentLike Service error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	}
+};
+
+//댓글 입력
+exports.createRestorationComment = async function (userIdx, restorationIdx, comment) {
+	const insertRestorationCommentParams = [userIdx, restorationIdx, comment];
+	try {
+		const connection = await pool.getConnection((conn) => conn);
+		try {
+			await restoreDao.insertRestorationComment(connection, insertRestorationCommentParams);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		logger.error(`App - createRestorationComment Service error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	}
+};
+
+//댓글 수정
+exports.updateRestorationComment = async function (restorationCommentIdx, comment) {
+	const updateCommentParams = [comment, restorationCommentIdx];
+	try {
+		const connection = await pool.getConnection((conn) => conn);
+		try {
+			await restoreDao.updateRestorationComment(connection, updateCommentParams);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		logger.error(`App - updateRestorationComment Service error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	}
+};
+
+//댓글 삭제
+exports.deleteRestorationComment = async function (restorationCommentIdx) {
+	try {
+		const connection = await pool.getConnection((conn) => conn);
+		try {
+			await restoreDao.deleteRestorationComment(connection, restorationCommentIdx);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		logger.error(`App - deleteRestorationComment Service error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	}
+};
+
+exports.createExamAdmissionTicket = async function (userIdx, restoreExamDetailIdx, examAdmissionTicketImage) {
+	try {
+		const connection = await pool.getConnection((conn) => conn);
+		try {
+			const insertExamAdmissionTicketParams = [userIdx, restoreExamDetailIdx, examAdmissionTicketImage];
+			await restoreDao.insertExamAdmissionTicket(connection, insertExamAdmissionTicketParams);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (error) {
+		logger.error(`App - createExamAdmissionTicket Service error\n: ${error.message}`);
+
+		throw new errorResponse(baseResponse.DB_ERROR, 500);
+	}
+};
diff --git a/_old/src/Store/store.spec.js b/_old/src/Store/store.spec.js
new file mode 100644
index 0000000..a7fe838
--- /dev/null
+++ b/_old/src/Store/store.spec.js
@@ -0,0 +1,78 @@
+const should = require("should");
+const request = require("supertest");
+const app = require("../../index");
+const storeDao = require("./storeDao");
+const { pool } = require("../../config/database");
+
+//exmple
+// const refundInfoTestSet = [
+// 	{
+// 		//jwt token
+// 		"x-access-token":
+// 			"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWR4IjoxNjk5LCJpc0tlZXAiOiIxIiwiaWF0IjoxNjM5MDk5NTI0LCJleHAiOjE2NDE2OTE1MjQsInN1YiI6InVzZXJJbmZvIn0.y_7e3luR12xyD39IFSznlavY6OR2OVZaeTRVriYhF-4",
+// 		orderNum: "E654587902ZOU",
+// 	},
+// ];
+
+describe("GET /stores/payment/refund", () => {
+	it("check 200 status code", (done) => {
+		request(app)
+			.get("/stores/payment/refund")
+			.set("x-access-token", refundInfoTestSet[0]["x-access-token"])
+			.query({ orderNum: refundInfoTestSet[0].orderNum })
+			.expect(200)
+			.end((err, res) => {
+				if (err) return done(err);
+				return done();
+			});
+	});
+	it("check element and type", (done) => {
+		request(app)
+			.get("/stores/payment/refund")
+			.set("x-access-token", refundInfoTestSet[0]["x-access-token"])
+			.query({ orderNum: refundInfoTestSet[0].orderNum })
+			.expect(200)
+			.end((err, res) => {
+				if (err) return done(err);
+				res.body.should.be.an.instanceOf(Object);
+				res.body.should.have.property("result");
+				let result = res.body.result;
+				should(result).have.property("productInfoList").and.be.an.instanceOf(Array);
+				should(result).have.property("totalRefundPrice").and.be.a.Number();
+				result.productInfoList.map((product) => {
+					product.should.have.properties([
+						"productName",
+						"productIdx",
+						"category",
+						"useCoupon",
+						"usePoint",
+						"finalPrice",
+						"discountPrice",
+						"isRefund",
+						"couponIdx",
+						"couponName",
+						"refundPrice",
+						"category",
+					]);
+					product.productName.should.be.a.String();
+					product.productIdx.should.be.a.Number();
+					product.category.should.be.a.String();
+					product.useCoupon.should.be.a.Number();
+					product.usePoint.should.be.a.Number();
+					product.finalPrice.should.be.a.Number();
+					product.discountPrice.should.be.a.Number();
+				});
+				return done();
+			});
+	});
+
+	it("check dao response : selectOrderPerProduct", async () => {
+		try {
+			const connection = await pool.getConnection(async (conn) => conn);
+			const orderPerProduct = await storeDao.selectOrderPerProduct(connection, refundInfoTestSet[0].orderNum);
+			connection.release();
+		} catch (err) {
+			console.log(err);
+		}
+	});
+});
diff --git a/_old/src/Store/storeController.js b/_old/src/Store/storeController.js
new file mode 100644
index 0000000..99f09fa
--- /dev/null
+++ b/_old/src/Store/storeController.js
@@ -0,0 +1,529 @@
+const storeProvider = require("./storeProvider");
+const storeService = require("./storeService");
+const examProvider = require("../Exam/examProvider");
+const userProvider = require("../User/userProvider");
+const userService = require("../User/userService");
+const channelTalkService = require("../ChannelTalk/channelTalkService");
+const regNumber = /^[0-9]/;
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const baseResponseStatus = require("../../config/baseResponseStatus");
+const { logger } = require("../../config/winston");
+const { USERNAME_EMPTY } = require("../../config/baseResponseStatus");
+const axios = require("axios").default;
+const constant = require("../../config/constant");
+const asyncHandler = require("../../utils/asyncHandler");
+const errorResponse = require("../../utils/errorResponse");
+const regKorean = /^[가-힣]{2,4}$/;
+const regUrl = /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/;
+const regDate = (dayRegExp = /^(19|20)\d{2}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[0-1])$/);
+
+function isEmptyObj(obj) {
+	if (obj.constructor === Object && Object.keys(obj).length === 0) {
+		return true;
+	}
+	return false;
+}
+
+exports.getCategoryList = asyncHandler(async function (req, res) {
+	const categoryResult = await storeProvider.getCategoryList();
+	return res.send(categoryResult);
+});
+
+exports.getProductList = asyncHandler(async function (req, res) {
+	const category = req.query.category; // ex : 1 : 프리패스, 2 : 자격증, 3 공기업, 4 : 공무원 (DB productIdx)
+
+	const examField = req.query.examField;
+
+	if (!regNumber.test(category)) throw new errorResponse(baseResponse.BUSINESS_CATEGORY_ERROR_TYPE, 400);
+	await storeProvider.checkCategoryIdx(category);
+
+	// TODO: validation
+
+	const productListResult = await storeProvider.getProductInfoList(category, examField);
+	return res.send(resultResponse(baseResponse.SUCCESS, productListResult));
+});
+
+exports.getProductDetail = asyncHandler(async function (req, res) {
+	const parentIdx = req.params.parentIdx;
+	if (!parentIdx) throw new errorResponse(baseResponse.PRODUCT_EMPTY, 400);
+
+	await storeProvider.productIdxCheck(parentIdx);
+
+	const productDetailInfo = await storeProvider.getProductDetail(parentIdx);
+	return res.send(productDetailInfo);
+});
+
+/**
+ * @description Give user auth, consume point, delete cart when payment success
+ *
+ * @param merchant_uid
+ * @param {Number} userIdx
+ * @returns {Object} paymentInfo
+ *
+ */
+async function cleanUpPayment(merchant_uid, userIdx) {
+	//권한 부여
+	const paymentInfo = await storeProvider.getPaymentInfo(merchant_uid);
+	const buyProductInfoResult = await storeProvider.getProductInfoListByPayment(merchant_uid);
+	if (!buyProductInfoResult.length) throw new errorResponse(baseResponse.PAYMENT_PRODUCT_EMPTY, 400);
+
+	let finalProductInfoResult = []; // depth2의 상품들중 부모가 같은 상품을 합쳐서 최종적으로 담는 배열.
+	let parentIdx = []; // 부모 인덱스를 체크하는 배열.
+
+	//buyProductInfoResult에서 하나씩 꺼내면서 단일회차인지 아닌지부터 조회: 단일회차가 아니라면 -> if문, 맞다면 else문
+	// productTypeIdx가 0이면 대분류, 1이면 단일회차 2~6은 전기,소방,기계와 같은 중분류 -> 단일회차이면 부모와 합치면 안된다.
+	// 단일회차가 아닌 경우에는 부모인덱스가 이미 배열에 있는지 체크하는 로직 -> 첫번째 if문 안
+	buyProductInfoResult.forEach((obj) => {
+		let perProductParentIdx = obj.parentIdx;
+		let index = parentIdx.indexOf(perProductParentIdx);
+		if (obj.productTypeIdx >= 2 && index === -1) {
+			parentIdx.push(perProductParentIdx);
+			obj.productIdx = obj.parentIdx;
+			finalProductInfoResult.push(obj);
+		} else if (obj.productTypeIdx >= 2) finalProductInfoResult[index].duration += obj.duration;
+		else finalProductInfoResult.push(obj);
+	});
+
+	await storeService.giveUserExamAuth(userIdx, finalProductInfoResult);
+
+	// 포인트 차감
+	if (paymentInfo[0].totalPoint > 0 && !buyProductInfoResult[0].pointConsumed) {
+		const content = `${paymentInfo[0].paymentName} 상품 구매`;
+		await storeService.consumePoint(userIdx, constant.POINT_TYPE_USE, content, paymentInfo[0].totalPoint, merchant_uid);
+	}
+
+	// 장바구니 삭제
+	const orderPerProduct = await storeProvider.getPaymentProductInfo(merchant_uid);
+	const productIdxList = [];
+	orderPerProduct.forEach((v) => {
+		productIdxList.push(v.productIdx);
+	});
+	await userService.updateUserCartProductDrop(userIdx, productIdxList);
+
+	let response = {
+		paymentName: paymentInfo[0].paymentName,
+		orderNum: paymentInfo[0].paymentIdx,
+		pay_method: storeProvider.changePayMethodToText(paymentInfo[0].pay_method),
+		paidDate: paymentInfo[0].paidAt,
+		totalPrice: paymentInfo[0].totalPrice,
+	};
+
+	// 유저 구매 상품 채널톡 업데이트
+	let updateData = await userProvider.userAllProductsName(userIdx);
+	await channelTalkService.updateChannelTalkUserInfo(userIdx, updateData);
+
+	return response;
+}
+
+exports.buyProductRequest = asyncHandler(async function (req, res) {
+	console.log("buyProductRequest");
+	const userIdx = req.verifiedToken.userIdx;
+	const { totalPrice, productIdxList, totalPoint, pay_method, isAgree } = req.body;
+
+	// 배송지 정보
+	let isShip = 0;
+	let shipInfo;
+	const { recipient, phoneNum, postcode, address } = req.body;
+	if (!userIdx) return res.send(basickResponse(baseResponse.USER_EMPTY));
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	const userCheckResult = await userProvider.userIdxCheck(userIdx);
+	if (userCheckResult.length === 0) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+	if (userCheckResult[0].status === "Y") return res.send(basickResponse(baseResponse.USER_QUIT));
+
+	if (!pay_method || !isAgree || totalPrice == undefined || !productIdxList)
+		throw new errorResponse(baseResponse.PAY_PARAM_EMPTY, 400);
+
+	await userProvider.userIdxCheck(userIdx);
+	const userInfoResult = await userProvider.userInfo(userIdx);
+	if (totalPoint > userInfoResult[0].point) return res.send(basickResponse(baseResponse.PAY_POINT_ERROR));
+
+	if (recipient && phoneNum && address) {
+		isShip = 1;
+		shipInfo = [recipient, phoneNum, postcode, address];
+	}
+
+	const userPhoneNum = userInfoResult[0].userPhoneNum;
+
+	let totalPriceToBePaid = 0;
+	let paymentName = "";
+
+	let paymentProductParams = [];
+	for (let i = 0; i < productIdxList.length; i++) {
+		const productInfoResult = await storeProvider.getProductInfo(productIdxList[i]);
+		let element = [productIdxList[i], productInfoResult[0].discountPrice];
+		paymentProductParams.push(element);
+
+		if (i == 0) {
+			paymentName = productInfoResult[0].productName;
+			if (productIdxList.length > 1) paymentName += " 외 " + (productIdxList.length - 1) + "개";
+		}
+
+		totalPriceToBePaid += productInfoResult[0].discountPrice;
+	}
+	totalPriceToBePaid -= totalPoint;
+	if (totalPriceToBePaid != totalPrice) return res.send(basickResponse(baseResponse.PAY_PRICE_ERROR));
+
+	// 결제하기 버튼 누르는 순간 주문번호 생성, DB저장
+	const paymentIdx = await storeService.updatePayment(
+		userIdx,
+		paymentName,
+		totalPriceToBePaid,
+		totalPoint,
+		pay_method,
+		paymentProductParams,
+		shipInfo,
+	);
+
+	/**
+	 * 포인트 전액 사용 결제
+	 * Import api 호출 안하고 바로 결제정보 저장 및 결제 완료
+	 */
+	if (totalPriceToBePaid === 0) {
+		const status = "pointPaid";
+		const receipt_url = null;
+		const imp_uid = null;
+		const updatePaymentParams = [status, receipt_url, pay_method, imp_uid];
+		const paidAt = storeProvider.unixToTimestamp(new Date().getTime() / 1000);
+		const refundAt = null;
+		updatePaymentParams.push(paidAt);
+		updatePaymentParams.push(refundAt);
+		updatePaymentParams.push(paymentIdx);
+		await storeService.completePayment(updatePaymentParams);
+
+		const response = await cleanUpPayment(paymentIdx, userIdx);
+
+		return res.send(resultResponse(baseResponse.PAYMENT_SUCCESS, response));
+	}
+
+	const resObj = {
+		merchant_uid: paymentIdx,
+		name: paymentName,
+		amount: totalPrice,
+		buyer_name: userInfoResult[0].userName,
+		buyer_email: userInfoResult[0].userEmail,
+		buyer_tel: [userPhoneNum.slice(0, 3), userPhoneNum.slice(3, 7), userPhoneNum.slice(7, 11)].join("-"),
+		pay_method: pay_method,
+	};
+
+	return res.send(resultResponse(baseResponse.SUCCESS, resObj));
+});
+
+exports.getPaymentInfo = asyncHandler(async function (req, res) {
+	const productIdxList = req.body.productIdxList;
+	const userIdx = req.verifiedToken.userIdx;
+
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!productIdxList || productIdxList.length == 0) throw new errorResponse(baseResponse.PRODUCT_EMPTY, 400);
+
+	await userProvider.userIdxCheck(userIdx);
+
+	const productListInfo = await storeProvider.getProductPaymentList(productIdxList, userIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, productListInfo));
+});
+
+exports.getProductCouponList = asyncHandler(async function (req, res) {
+	const productIdx = req.params.productIdx;
+	const userIdx = req.verifiedToken.userIdx;
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	if (!productIdx) throw new errorResponse(baseResponse.PRODUCT_EMPTY, 400);
+	await userProvider.userIdxCheck(userIdx);
+	await storeProvider.productIdxSellingCheck(productIdx);
+
+	const productCouponInfo = await storeProvider.getProductCouponList(userIdx, productIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, productCouponInfo));
+});
+
+/**
+ * 전액 포인트 사용이 아닌 아임포트 api 호출
+ */
+exports.completePaymentHandler = function (state) {
+	return async function (req, res) {
+		try {
+			const userIdx = req.verifiedToken.userIdx;
+			if (!userIdx) return res.send(basickResponse(baseResponse.USER_EMPTY));
+			if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+			console.log("completePaymentHandler userIdx : " + userIdx + " state : " + state);
+			await userProvider.userIdxCheck(userIdx);
+
+			const imPortToken = req.imPortToken.access_token;
+			const { imp_uid, merchant_uid } = state == "mobile" ? req.query : req.body; // req의 body에서 imp_uid, merchant_uid 추출
+
+			if (!imPortToken) return res.send(basickResponse(baseResponse.IMPORT_TOKEN_EMPTY));
+
+			//imp_uid, merchant_uid validation
+			if (!imp_uid || isEmptyObj(imp_uid)) return res.send(basickResponse(baseResponse.IMP_UID_EMPTY));
+			if (!merchant_uid || isEmptyObj(merchant_uid)) return res.send(basickResponse(baseResponse.ORDERNUM_EMPTY));
+
+			// imp_uid로 아임포트 서버에서 결제 정보 조회
+			let getPaymentData;
+			try {
+				getPaymentData = await axios({
+					url: `https://api.iamport.kr/payments/${imp_uid}`, // imp_uid 전달
+					method: "get", // GET method
+					headers: { Authorization: imPortToken }, // 인증 토큰 Authorization header에 추가
+				});
+			} catch (error) {
+				console.log("CompletePaymentHandler axios error");
+				console.log(error);
+				return res.send(basickResponse(baseResponse.IMPORT_AXIOS_ERROR));
+			}
+
+			const paymentData = getPaymentData.data.response; // 조회한 결제 정보
+
+			if (!paymentData) {
+				return res.send(basickResponse(baseResponse.PAYMENT_NOT_COMPLETED));
+			}
+			const paymentPriceInfo = await storeProvider.getPaymentPrice(paymentData.merchant_uid); //db에서 결제 금액조회 Payment 테이블 paymentIdx = paymentData.merchant_uid
+
+			if (!paymentPriceInfo.length) return res.send(basickResponse(baseResponse.PAYMENT_NOT_EXIST));
+
+			// TODO
+			const paymentInfo = await storeProvider.getPaymentInfo(paymentData.merchant_uid);
+
+			if (paymentInfo[0].level == "paid") {
+				console.log(paymentData.merchant_uid + " 결재 완료된 상품!!");
+				return res.send(basickResponse(baseResponse.PAYMENT_ALREADY_DONE));
+			}
+
+			// 결제 검증하기
+			const { amount, status, receipt_url, pay_method, fail_reason, paid_at, cancelled_at } = paymentData;
+
+			if (amount === paymentPriceInfo[0].totalPrice) {
+				const updatePaymentParams = [status, receipt_url, pay_method, imp_uid];
+				const paidAt = storeProvider.unixToTimestamp(paid_at);
+				const refundAt = storeProvider.unixToTimestamp(cancelled_at);
+				status == "paid" ? updatePaymentParams.push(paidAt) : updatePaymentParams.push(null);
+				status == "cancelled" ? updatePaymentParams.push(refundAt) : updatePaymentParams.push(null);
+				updatePaymentParams.push(merchant_uid);
+				await storeService.completePayment(updatePaymentParams);
+				let response;
+				switch (status) {
+					case "ready":
+						return res.send(basickResponse(baseResponse.VACCOUNT_ERROR));
+					case "paid":
+						response = await cleanUpPayment(paymentData.merchant_uid, userIdx);
+						return res.send(resultResponse(baseResponse.PAYMENT_SUCCESS, response));
+					case "failed":
+						response = {
+							...baseResponse.PAYMENT_FAIL,
+							message: fail_reason,
+						};
+						return res.send(response);
+					default:
+						response = {
+							...baseResponse.PAYMENT_FAIL,
+							message: "알 수 없는 상태입니다. status : " + status,
+						};
+						return res.send(response);
+				}
+			} else {
+				// 금액 불일치, 위/변조 결제
+				return res.send(basickResponse(baseResponse.PAYMENT_ERROR_TYPE));
+			}
+		} catch (error) {
+			console.log(error);
+			return res.send(basickResponse(baseResponse.SERVER_ERROR));
+		}
+	};
+};
+
+exports.paymentWebhook = async function (req, res) {
+	try {
+		console.log("paymentWebhook");
+
+		const imPortToken = req.imPortToken.access_token;
+		const { imp_uid, merchant_uid } = req.body;
+		if (!imPortToken) return res.send(basickResponse(baseResponse.IMPORT_TOKEN_EMPTY));
+
+		//imp_uid, merchant_uid validation
+		if (!imp_uid || isEmptyObj(imp_uid)) return res.send(basickResponse(baseResponse.IMP_UID_EMPTY));
+		if (!merchant_uid || isEmptyObj(merchant_uid)) return res.send(basickResponse(baseResponse.ORDERNUM_EMPTY));
+
+		// imp_uid로 아임포트 서버에서 결제 정보 조회
+		let getPaymentData;
+		try {
+			getPaymentData = await axios({
+				url: `https://api.iamport.kr/payments/${imp_uid}`, // imp_uid 전달
+				method: "get", // GET method
+				headers: { Authorization: imPortToken }, // 인증 토큰 Authorization header에 추가
+			});
+		} catch (error) {
+			console.log("IMPORT_AXIOS_ERROR");
+			return res.send(basickResponse(baseResponse.IMPORT_AXIOS_ERROR));
+		}
+
+		const paymentData = getPaymentData.data.response; // 조회한 결제 정보
+		if (!paymentData) return res.send(basickResponse(baseResponse.PAYMENT_NOT_EXIST));
+		const paymentPriceInfo = await storeProvider.getPaymentPrice(paymentData.merchant_uid); //db에서 결제 금액조회 Payment 테이블 paymentIdx = paymentData.merchant_uid
+		if (!paymentPriceInfo.length) return res.send(basickResponse(baseResponse.PAYMENT_NOT_EXIST));
+
+		// 결제 검증하기
+		const { amount, status, receipt_url, pay_method, fail_reason, paid_at, cancelled_at } = paymentData;
+		//console.log(paymentData);
+
+		if (amount === paymentPriceInfo[0].totalPrice) {
+			const updatePaymentParams = [status, receipt_url, pay_method, imp_uid];
+
+			const paidAt = storeProvider.unixToTimestamp(paid_at);
+			let refundAt;
+			cancelled_at == 0 ? (refundAt = null) : (refundAt = storeProvider.unixToTimestamp(cancelled_at));
+			updatePaymentParams.push(paidAt);
+			updatePaymentParams.push(refundAt);
+			// status == "paid" ? updatePaymentParams.push(paidAt) : updatePaymentParams.push(null);
+			// status == "cancelled" ? updatePaymentParams.push(refundAt) : updatePaymentParams.push(null);
+			updatePaymentParams.push(paymentData.merchant_uid);
+			//console.log(updatePaymentParams);
+			// TODO
+			// await storeService.completePayment(updatePaymentParams);
+
+			let response;
+			switch (status) {
+				case "ready":
+					return res.send(basickResponse(baseResponse.VACCOUNT_ERROR));
+				case "paid":
+					return res.send(resultResponse(baseResponse.PAYMENT_SUCCESS));
+				case "failed":
+					await storeService.deletePaymentProduct(merchant_uid);
+					response = {
+						...baseResponse.PAYMENT_FAIL,
+						message: fail_reason,
+					};
+					return res.send(response);
+				case "cancelled":
+					return res.send(basickResponse(baseResponse.REFUND_SUCCESS));
+				//return res.send(basickResponse(baseResponse.REFUND_IMPORT_ERROR));
+				default:
+					response = {
+						...baseResponse.PAYMENT_FAIL,
+						message: "알 수 없는 상태입니다. status : " + status,
+					};
+					return res.send(response);
+			}
+		} else {
+			// 금액 불일치, 위/변조 결제
+			return res.send(basickResponse(baseResponse.PAYMENT_ERROR_TYPE));
+		}
+	} catch (error) {
+		console.log(error);
+		logger.error(`App - paymentWebhook Controller error\n: ${error.message}`);
+		return res.send(basickResponse(baseResponse.SERVER_ERROR));
+	}
+};
+
+exports.getPackageList = asyncHandler(async function (req, res) {
+	let packageListResult = await storeProvider.getPackageList();
+	packageListResult = packageListResult.map((obj) => {
+		if (obj.price == obj.discountPrice) {
+			delete obj.discountPrice;
+		}
+		return obj;
+	});
+
+	return res.send(resultResponse(baseResponse.SUCCESS, packageListResult));
+});
+
+exports.checkPurchaseHistory = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const productIdxList = req.body.productIdxList;
+
+	if (!productIdxList) throw new errorResponse(baseResponse.PRODUCT_EMPTY, 400);
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	await userProvider.userIdxCheck(userIdx);
+
+	const productBuyableCheckResult = await storeProvider.productBuyableCheck(userIdx, productIdxList);
+	const isAvailable = productBuyableCheckResult[0].exist ? 0 : 1;
+	return res.send(resultResponse(baseResponse.SUCCESS, { isAvailable }));
+});
+
+exports.refundRequest = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const { orderNum, refundReason, refundRequestPrice, productIdxList } = req.body;
+	const imPortToken = req.imPortToken.access_token;
+
+	//User Validation
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	await userProvider.userIdxCheck(userIdx);
+
+	//Request Validation
+	if (!imPortToken) throw new errorResponse(baseResponse.IMPORT_TOKEN_EMPTY, 400);
+	if (!orderNum || isEmptyObj(orderNum)) throw new errorResponse(baseResponse.ORDERNUM_EMPTY, 400);
+	if (!refundReason) throw new errorResponse(baseResponse.REFUND_REASON_EMPTY, 400);
+	if (!refundRequestPrice) throw new errorResponse(baseResponse.REFUND_PRICE_EMPTY, 400);
+	if (!productIdxList) throw new errorResponse(baseResponse.PRODUCT_EMPTY, 400);
+
+	const paymentInfo = await storeProvider.getPaymentInfo(orderNum);
+
+	if (paymentInfo[0].checkSum < 0) throw new errorResponse(baseResponse.REFUND_TOTAL_ERROR, 400);
+	if (paymentInfo[0].checkSum < refundRequestPrice) throw new errorResponse(baseResponse.REFUND_CHECKSUM_ERROR, 400);
+
+	//상품별로 환불되어야 하는 금액 검증
+	const refundPricePerProduct = await storeProvider.computeProductRefundPrice(userIdx, orderNum, productIdxList);
+	const refundPriceToBeReturn = refundPricePerProduct.reduce((sum, currentValue) => sum + currentValue);
+
+	if (refundPriceToBeReturn != refundRequestPrice) throw new errorResponse(baseResponse.REFUND_PRICE_ERROR, 400);
+
+	//아임포트 REST API로 결제 환불 요청
+	const getRefundData = await axios({
+		url: "https://api.iamport.kr/payments/cancel",
+		method: "post",
+		headers: {
+			"Content-Type": "application/json",
+			Authorization: imPortToken, // 아임포트 서버로부터 발급받은 엑세스 토큰
+		},
+		data: {
+			reason: refundReason, // 가맹점 클라이언트로부터 받은 환불사유
+			imp_uid: paymentInfo[0].imp_uid, // imp_uid를 환불 `unique key`로 입력
+			amount: refundPriceToBeReturn, // 가맹점 클라이언트로부터 받은 환불금액
+			checksum: paymentInfo[0].checksum, // [권장] 환불 가능 금액 입력
+		},
+	});
+
+	const { response: refundResponse, message } = getRefundData.data;
+	if (!refundResponse) throw new errorResponse(baseResponse.REFUND_TOTAL_ERROR, 400);
+
+	const cancelHistoryList = refundResponse.cancel_history;
+	const lastRefundInfo = cancelHistoryList[cancelHistoryList.length - 1];
+
+	//상품 환불
+	await storeService.refundProducts(userIdx, orderNum, refundPricePerProduct, productIdxList, lastRefundInfo);
+
+	const refundAt = storeProvider.unixToTimestamp(lastRefundInfo.cancelled_at);
+	const responseObj = {
+		pg_tid: lastRefundInfo.pg_tid,
+		refundPrice: lastRefundInfo.amount,
+		refundDate: refundAt.substring(0, refundAt.length - 3),
+		refundReason: lastRefundInfo.reason,
+		receiptUrl: lastRefundInfo.receipt_url,
+	};
+
+	return res.send(resultResponse(baseResponse.SUCCESS, responseObj));
+
+	// } catch (error) {
+	// 	console.log(error);
+	// 	logger.error(`App - refundRequest Controller error\n: ${error.message}`);
+	// 	return res.send(basickResponse(baseResponse.SERVER_ERROR));
+	// }
+});
+
+exports.getRefundInfo = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const orderNum = req.query.orderNum;
+	if (!userIdx) throw new errorResponse(baseResponse.USER_EMPTY, 400);
+	if (!regNumber.test(userIdx)) throw new errorResponse(baseResponse.USER_ERROR_TYPE, 400);
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	if (!orderNum) throw new errorResponse(baseResponse.ORDERNUM_EMPTY, 400);
+
+	await storeProvider.paymentIdxCheck(orderNum);
+
+	const getRefundProductInfoResult = await storeProvider.getRefundProductInfo(orderNum);
+	return res.send(resultResponse(baseResponse.SUCCESS, getRefundProductInfoResult));
+});
diff --git a/_old/src/Store/storeDao.js b/_old/src/Store/storeDao.js
new file mode 100644
index 0000000..1296329
--- /dev/null
+++ b/_old/src/Store/storeDao.js
@@ -0,0 +1,751 @@
+//스토어 페이지 상품 종류 존재 체크 자격증/공무원/공기업/프리패스
+async function checkCategoryIdx(connection, productIdx) {
+	const checkCategoryIdxQuery = `
+    select exists (select productIdx from Product where productIdx=? and depth=0 and status='N') as exist;
+    `;
+	const [checkCategoryIdxRow] = await connection.query(checkCategoryIdxQuery, productIdx);
+	return checkCategoryIdxRow;
+}
+
+//스토어 페이지 상품 종류 존재 체크 자격증/공무원/공기업/프리패스
+async function checkUserExamDetailAuth(connection, examDetailIdx, userIdx) {
+	const checkUserExamDetailAuthQuery = `
+	select exists(
+	SELECT distinct examDetailIdx
+	FROM UserAuth ua
+	join Product pd on pd.productIdx = ua.productIdx and pd.status = 'N'
+	join ProductExamDetail ped on ped.productIdx = pd.productIdx
+	where examDetailIdx = ? and userIdx = ? and ua.status = 'N') as exist;
+    `;
+	const [checkUserExamDetailAuthRow] = await connection.query(checkUserExamDetailAuthQuery, [examDetailIdx, userIdx]);
+	return checkUserExamDetailAuthRow;
+}
+
+//상품 카테고리 조회 자격증/공무원/공기업/프리패스/책
+async function selectProductCategory(connection, productIdx) {
+	const selectProductCategoryQuery = `
+	select p.productIdx, (SELECT productName from Product where productIdx = pr.parentIdx) as category from Product p join ProductRelation pr on pr.productIdx=p.productIdx where status='N' and p.productIdx = ?;
+    `;
+	const [selectProductCategoryRow] = await connection.query(selectProductCategoryQuery, productIdx);
+	return selectProductCategoryRow;
+}
+
+//스토어 카테고리 조회 자격증/공무원/공기업/프리패스/책
+async function selectCategoryList(connection) {
+	const selectCategoryListQuery = `
+	select productName, p.productIdx from Product p join ProductRelation pr on pr.productIdx=p.productIdx where parentIdx=0 and depth=0 and status='N'
+       `;
+	const [selectCategoryListRow] = await connection.query(selectCategoryListQuery);
+	return selectCategoryListRow;
+}
+
+//자격증 카테고리 인덱스 조회 //미완
+async function selectDefaultCategoryIdx(connection, examSortRef) {
+	const selectDefaultCategoryIdxQuery = `
+        select productIdx from Product where productName=? and depth=0 and status='N'
+    `;
+	const [selectDefaultCategoryIdxRow] = await connection.query(selectDefaultCategoryIdxQuery, examSortRef);
+	return selectDefaultCategoryIdxRow;
+}
+
+//공기업 카테고리 인덱스 조회 //미완
+async function selectCompanyCategoryIdx(connection, examSortRef) {
+	const selectCompanyCategoryIdxQuery = `
+        select productIdx from Product where productName=? and depth=0 and status='N'
+    `;
+	const [selectCompanyCategoryIdxRow] = await connection.query(selectCompanyCategoryIdxQuery, examSortRef);
+	return selectCompanyCategoryIdxRow;
+}
+
+// 상품의 대분류 카테고리 구분 조회
+async function selectProductTopCategory(connection, productIdx) {
+	const selectProductTopCategoryQuery = `
+    WITH RECURSIVE cte as (
+		SELECT productIdx, parentIdx
+		FROM ProductRelation as pr
+		WHERE productIdx = ?
+		UNION ALL
+		SELECT pr2.productIdx, pr2.parentIdx
+		FROM ProductRelation as pr2
+		JOIN cte on cte.parentIdx = pr2.productIdx
+		)
+		SELECT productName FROM cte
+		JOIN Product p ON p.productIdx = cte.productIdx
+		WHERE parentIdx = 0;
+	`;
+	const [selectProductTopCategoryRow] = await connection.query(selectProductTopCategoryQuery, productIdx);
+	return selectProductTopCategoryRow;
+}
+
+// 상품의 상위 카테고리 구분 조회
+async function selectProductCategory(connection, productIdx) {
+	const selectProductCategoryQuery = `
+	SELECT productName 
+	FROM Product WHERE productIdx =
+    (SELECT parentIdx
+	FROM Product as p 
+	JOIN ProductRelation as pr on p.productIdx = pr.productIdx 
+	WHERE p.productIdx = ? and status = 'N');
+	`;
+	const [selectProductCategoryRow] = await connection.query(selectProductCategoryQuery, productIdx);
+	return selectProductCategoryRow;
+}
+
+// 스토어 상품 종류에 해당하는 상품들중 middleSort 분류를 통해 리스트 조회
+async function selectProductInfoListWithExamField(connection, category, examFieldQuery) {
+	const selectProductInfoListQuery = `
+	SELECT p.productIdx, productName, productThumbnail, shortDescription, detailDescription, generalDescription, p.prio, price, discountPrice
+	FROM Product as p 
+	JOIN ProductRelation as pr on p.productIdx = pr.productIdx 
+  LEFT JOIN ProductType as pt on pr.productTypeIdx = pt.productTypeIdx
+	WHERE parentIdx = ? ${examFieldQuery} and status='N' and depth = 1
+	ORDER BY p.prio;
+	`;
+	const [selectProductInfoListRow] = await connection.query(selectProductInfoListQuery, category);
+	return selectProductInfoListRow;
+}
+
+// depth1 상품 정보 조회
+
+async function selectParentProductInfo(connection, productIdx) {
+	const selectParentProductInfoQuery = `
+	SELECT p.productIdx, productName, productThumbnail, shortDescription, detailDescription, generalDescription, p.prio, price, discountPrice, pr.productTypeIdx
+	FROM Product as p
+	LEFT JOIN ProductRelation as pr on p.productIdx = pr.productIdx 
+	WHERE p.productIdx = ? and depth = 1
+	`;
+	const [selectParentProductRow] = await connection.query(selectParentProductInfoQuery, productIdx);
+	return selectParentProductRow;
+}
+// 스토어 상품 상세 조회
+async function selectProductInfo(connection, productIdx) {
+	const selectProductInfoQuery = `
+	SELECT p.productIdx, price, discountPrice, duration, productName
+	FROM Product p 
+	LEFT JOIN ProductRelation pr on p.productIdx = pr.productIdx
+	WHERE pr.parentIdx = ? and status = 'N' and depth = 2
+  `;
+	const [selectProductInfoRow] = await connection.query(selectProductInfoQuery, productIdx);
+	return selectProductInfoRow;
+}
+
+//카테고리 인덱스에 해당하는 상품 리스트 조회
+// async function selectProductInfoList(connection, isSellingQuery, productInfoListParams) {
+// 	let selectProductInfoListQuery = `
+//         select p.productIdx, price, discountPrice, description, productName, productThumbnail from Product as p join ProductRelation as pr on p.productIdx=pr.productIdx where parentIdx=? and status='N'
+//     `;
+// 	selectProductInfoListQuery += isSellingQuery;
+// 	const [selectProductInfoListRow] = await connection.query(selectProductInfoListQuery, productInfoListParams);
+// 	return selectProductInfoListRow;
+// }
+
+//현재 판매중인 패키지 상품 조회
+async function selectPackageInfoList(connection) {
+	const selectPackageInfoListQuery = `
+		select productIdx, price, discountPrice, description, productName, packageName, duration, isPremium from Product where depth=-1 and isSelling=1 and status='N';
+	`;
+	const [selectPackageInfoListRow] = await connection.query(selectPackageInfoListQuery);
+	return selectPackageInfoListRow;
+}
+
+//공기업 시험별 회차 가격 조회
+async function selectExamDetailPriceList(connection, examProductIdxList) {
+	const selectExamDetailPriceListQuery = `
+    select price, discountPrice, parentIdx, duration from Product as p join ProductRelation as pr on p.productIdx=pr.productIdx where parentIdx in ? and status='N' group by parentIdx;
+    `;
+	const [selectExamDetailPriceListRow] = await connection.query(selectExamDetailPriceListQuery, [[examProductIdxList]]);
+	return selectExamDetailPriceListRow;
+}
+
+// 상품 체크
+async function checkProductIdx(connection, productIdx) {
+	const checkProductIdxQuery = `
+       select exists(select productIdx from Product where productIdx = ? and status = 'N' and depth =1) as exist;
+       `;
+	const [checkProductIdxRow] = await connection.query(checkProductIdxQuery, productIdx);
+	return checkProductIdxRow;
+}
+
+// 판매 상품 체크
+async function checkSellingProductIdx(connection, productIdx) {
+	const checkProductIdxQuery = `
+       select exists(select productIdx from Product where productIdx = ? and status = 'N' and isSelling = 1 ) as exist;
+       `;
+	const [checkProductIdxRow] = await connection.query(checkProductIdxQuery, productIdx);
+	return checkProductIdxRow;
+}
+
+async function selectProduct(connection, productIdx) {
+	const selectProductQuery = `
+		SELECT productIdx, productName, productThumbnail, price, discountPrice, shortDescription, duration, depth
+		FROM Product WHERE productIdx =?;
+	`;
+	const selectProductRow = await connection.query(selectProductQuery, productIdx);
+	return selectProductRow[0];
+}
+
+// 상품에 포함된 회차 조회
+async function selectProductExamDetail(connection, productIdx) {
+	const selectProductExamDetailQuery = `
+    SELECT e.examIdx, ed.examDetailIdx, examName, YEAR(examDate) examYear, date_format(examDate, '%Y.%m.%d') as 'date', examRound,
+    CASE WHEN domain IS NULL THEN 0
+	ELSE 1 END as isPublic
+	FROM Product p
+	LEFT JOIN ProductExamDetail ped on ped.productIdx = p.productIdx
+    LEFT JOIN ExamDetail ed on ed.examDetailIdx = ped.examDetailIdx and ed.status = 'N'
+    LEFT JOIN Exam e on e.examIdx = ed.examIdx and e.status = 'N'
+	LEFT JOIN CompanyExam as ce on ce.examIdx = e.examIdx 
+    WHERE p.productIdx = ? and p.status = 'N' and ed.isPublic = 1
+	ORDER BY examDate DESC;
+    `;
+	const selectProductExamDetailRow = await connection.query(selectProductExamDetailQuery, productIdx);
+	return selectProductExamDetailRow[0];
+}
+
+// 상품에 포함된 회차 조회 (depth 2 상품에 연동된 단일 회차 상품)
+async function selectProductExamDetailSingle(connection, productIdx) {
+	const selectProductExamDetailSingleQuery = `
+		SELECT pr.productIdx ,e.examIdx, ed.examDetailIdx, examName, examDate, date_format(examDate, '%Y.%m.%d') as 'date', examRound
+		FROM Product p
+		JOIN ProductRelation pr ON pr.productIdx = p.productIdx
+		LEFT JOIN ProductExamDetail ped on ped.productIdx = pr.productIdx
+		LEFT JOIN ExamDetail ed on ed.examDetailIdx = ped.examDetailIdx and ed.status = 'N'
+		LEFT JOIN Exam e on e.examIdx = ed.examIdx and e.status = 'N'
+		WHERE pr.parentIdx = ? and p.status = 'N'
+		ORDER BY examDate DESC;
+    `;
+	const selectProductExamDetailSingleRow = await connection.query(selectProductExamDetailSingleQuery, productIdx);
+	return selectProductExamDetailSingleRow[0];
+}
+
+//상품 설명 이미지, 동영상 url 조회
+async function selectProductUrl(connection, productIdx) {
+	const selectProductImageQuery = `
+		SELECT urlHeader, urlOrder, productUrl, productUrlSort
+		FROM ProductUrl WHERE productIdx=? and status='N'
+	`;
+	const [selectProductImageRow] = await connection.query(selectProductImageQuery, productIdx);
+	return selectProductImageRow;
+}
+
+//주문번호 생성하여 결제 저장
+async function insertPayment(connection, paymentParams) {
+	const insertPaymentQuery = `
+		insert into Payment(paymentIdx, paymentName, pay_method, userIdx, totalPrice, totalPoint, checkSum) values(?,?,?,?,?,?,?)
+	`;
+	const insertPaymentRow = await connection.query(insertPaymentQuery, paymentParams);
+	return insertPaymentRow;
+}
+
+//결제와 관련된 상품들 저장
+async function insertPaymentProduct(connection, paymentProductParams) {
+	const insertPaymentProductQuery = `
+		insert into PaymentProduct(paymentIdx, productIdx, finalPrice) values ?
+	`;
+	const insertPaymentProductRow = await connection.query(insertPaymentProductQuery, [paymentProductParams]);
+	return insertPaymentProductRow;
+}
+
+//결제 배송지 저장
+async function insertPaymentAddress(connection, paymentAddressParams) {
+	const insertPaymentAddressQuery = `
+		insert into PaymentAddress(paymentIdx, recipient, phoneNum, postcode, address) values (?, ?, ?, ?, ?)
+	`;
+	const insertPaymentAddressRow = await connection.query(insertPaymentAddressQuery, paymentAddressParams);
+	return insertPaymentAddressRow;
+}
+
+//oneDayPrice 업데이트
+async function updateOneDayPrice(connection, paymentIdx) {
+	const updateOneDayPriceQuery = `
+	update PaymentProduct set oneDayPrice=(select oneDayPrice from (select finalPrice/p.duration as oneDayPrice from PaymentProduct as pp join Product as p on pp.productIdx=p.productIdx where PaymentProduct.paymentProductIdx=pp.paymentProductIdx) tmp)
+	where PaymentProduct.paymentIdx=?;
+	`;
+	const updateOneDayPriceRow = await connection.query(updateOneDayPriceQuery, paymentIdx);
+	return updateOneDayPriceRow;
+}
+
+// 유저 포인트 조회
+async function selectPoint(connection, userIdx) {
+	const selectCouponPointQuery = `
+	SELECT userIdx, point FROM User WHERE userIdx = ?;
+    `;
+	const selectCouponPointRow = await connection.query(selectCouponPointQuery, userIdx);
+	return selectCouponPointRow[0];
+}
+
+// 유저 쿠폰 포인트 조회
+async function selectCouponPoint(connection, userIdx) {
+	const selectCouponPointQuery = `
+	SELECT u.userIdx, point, count(userCouponIdx) as 'couponCount'
+    FROM User u
+    LEFT JOIN UserCoupon uc ON u.userIdx = uc.userIdx and uc.status = 'N'
+    WHERE u.userIdx = ?
+    `;
+	const selectCouponPointRow = await connection.query(selectCouponPointQuery, userIdx);
+	return selectCouponPointRow[0];
+}
+
+// 유저 사용가능 쿠폰 조회
+async function selectProductCoupon(connection, userIdx, productIdx) {
+	const selectCouponPointQuery = `
+	SELECT c.couponIdx, couponName, couponPrice
+	FROM UserCoupon uc
+	LEFT JOIN Coupon c ON uc.couponIdx = c.couponIdx
+	LEFT JOIN CouponProduct cp ON cp.couponIdx = c.couponIdx
+	WHERE userIdx = ? and productIdx = ? and uc.status = 'N'
+	`;
+	const selectCouponPointRow = await connection.query(selectCouponPointQuery, [userIdx, productIdx]);
+	return selectCouponPointRow[0];
+}
+
+//쿠폰 인덱스로 쿠폰 조회
+async function selectCouponInfo(connection, couponIdx) {
+	const selectCouponInfoQuery = `
+	select * from Coupon where couponIdx=? and status='N'
+	`;
+	const [selectCouponInfoRow] = await connection.query(selectCouponInfoQuery, couponIdx);
+	return selectCouponInfoRow;
+}
+
+// 상품 최상위 카테고리 조회
+async function selectProductDivision(connection, productIdx) {
+	const selectProductDivisionQuery = `
+	WITH RECURSIVE CTE(productIdx, parentIdx) AS(
+		SELECT pr.productIdx, pr.parentIdx
+		FROM ProductRelation pr
+		WHERE productIdx = ?
+		UNION ALL
+		SELECT pr2.productIdx, pr2.parentIdx
+		FROM ProductRelation pr2
+		JOIN CTE c ON c.parentIdx = pr2.productIdx
+		)
+	  SELECT
+		L.productIdx as 'division', productName
+	  FROM
+		CTE as L
+		LEFT JOIN Product p ON p.productIdx = L.productIdx
+		WHERE L.parentIdx=0
+	`;
+	const selectProductDivisionRow = await connection.query(selectProductDivisionQuery, productIdx);
+	return selectProductDivisionRow[0];
+}
+
+// 유저 쿠폰 포인트 조회
+async function selectProduct(connection, productIdx) {
+	const selectProductQuery = `
+	SELECT *
+    FROM Product p
+    WHERE productIdx = ? and status='N'
+    `;
+	const selectProductRow = await connection.query(selectProductQuery, productIdx);
+	return selectProductRow[0];
+}
+
+// Payment 정보 조회
+async function selectPaymentByIdx(connection, paymentIdx) {
+	const selectPaymentInfoQuery = `
+	SELECT paymentIdx, level, pay_method, paidAt , receipt_url, userIdx, imp_uid, totalPrice, paymentName, totalPoint,
+	refundAt , checkSum
+    FROM Payment
+    WHERE paymentIdx = ? and status = 'N'
+    `;
+	const selectPaymentInfoRow = await connection.query(selectPaymentInfoQuery, paymentIdx);
+	return selectPaymentInfoRow[0];
+}
+
+// 결제되어야 하는 금액 조회
+async function selectPaymentPrice(connection, paymentIdx) {
+	const selectPaymentTotalPriceQuery = `
+	SELECT totalPrice
+    FROM Payment
+    WHERE paymentIdx = ? and status = 'N'
+    `;
+	const selectPaymentTotalPriceRow = await connection.query(selectPaymentTotalPriceQuery, paymentIdx);
+	return selectPaymentTotalPriceRow[0];
+}
+
+// 결제 정보 수정
+async function updatePayment(connection, updatePaymentParams) {
+	const updatePaymentQuery = `
+    UPDATE Payment 
+    SET level = ?, receipt_url = ?, pay_method = ?, imp_uid = ?, paidAt = ?, refundAt = ?
+    WHERE paymentIdx = ? AND status = 'N';
+    `;
+	const [updatePaymentRow] = await connection.query(updatePaymentQuery, updatePaymentParams);
+	return updatePaymentRow;
+}
+
+// 결제 정보 포인트 소비 상태 변경
+async function updatePaymentPointConsumed(connection, paymentIdx) {
+	const updatePaymentPointConsumedQuery = `
+    UPDATE Payment 
+    SET pointConsumed = 1
+    WHERE paymentIdx = ? AND status = 'N';
+    `;
+	const [updatePaymentPointConsumed] = await connection.query(updatePaymentPointConsumedQuery, paymentIdx);
+	return updatePaymentPointConsumed;
+}
+
+async function selectTotalPoint(connection, paymentIdx) {
+	const selectTotalPointQuery =
+		// 결제 단위로 포인트 사용할 경우
+		`
+		SELECT totalPoint FROM Payment WHERE paymentIdx = ? and status='N'
+		`;
+	// 상품 단위로 포인트 사용할 경우
+	// `
+	// 	SELECT SUM(usePoint) as totalPoint FROM PaymentProduct group by paymentIdx, status having paymentIdx=? and status='N'
+	// `;
+	const [selectTotalPointRow] = await connection.query(selectTotalPointQuery, paymentIdx);
+	return selectTotalPointRow;
+}
+
+async function insertPointLog(connection, pointLogParams) {
+	const insertPointLogQuery = `
+		INSERT INTO UserPointLog(userIdx, sort, content, point) values(?, ?, ?, ?)
+	`;
+	const insertPointLogRow = await connection.query(insertPointLogQuery, pointLogParams);
+	return insertPointLogRow;
+}
+
+async function consumeUserPoint(connection, userIdx, totalPoint) {
+	const consumeUserPointQuery = `
+		update User set point=point-? where userIdx=?
+	`;
+	const consumeUserPointRow = await connection.query(consumeUserPointQuery, [totalPoint, userIdx]);
+	return consumeUserPointRow;
+}
+
+async function earnUserPoint(connection, userIdx, totalPoint) {
+	const earnUserPointQuery = `
+		update User set point=point+? where userIdx=?
+	`;
+	const earnUserPointRow = await connection.query(earnUserPointQuery, [totalPoint, userIdx]);
+	return earnUserPointRow;
+}
+
+async function selectTotalUseCoupon(connection, paymentIdx) {
+	const selectTotalUseCouponQuery = `
+		SELECT couponIdx from Coupon as c join PaymentProduct as pp on pp.useCoupon=c.couponIdx where pp.paymentIdx=?
+	`;
+	const [selectTotalUseCouponRow] = await connection.query(selectTotalUseCouponQuery, paymentIdx);
+	return selectTotalUseCouponRow;
+}
+
+async function deletePayment(connection, paymentIdx) {
+	const deletePaymentQuery = `
+		update Payment set status='Y' where paymentIdx=?
+	`;
+	const [deletePaymentRow] = await connection.query(deletePaymentQuery, paymentIdx);
+	return deletePaymentRow;
+}
+
+async function deletePaymentProduct(connection, paymentIdx) {
+	const deletePaymentProductQuery = `
+		update PaymentProduct set status='Y' where paymentIdx=?
+	`;
+	const [deletePaymentProductRow] = await connection.query(deletePaymentProductQuery, paymentIdx);
+	return deletePaymentProductRow;
+}
+
+async function deleteUserCoupon(connection, userIdx, couponList) {
+	const deleteUserCouponQuery = `
+		update UserCoupon set status='Y' where userIdx=? and couponIdx in ?
+	`;
+	const [deleteUserCouponRow] = await connection.query(deleteUserCouponQuery, [userIdx, [couponList]]);
+	return deleteUserCouponRow;
+}
+
+async function checkPaymentIdx(connection, paymentIdx) {
+	const checkPaymentIdxQuery = `
+		select exists(select paymentIdx from Payment where paymentIdx=? and status='N') as exist;
+	`;
+	const [checkPaymentIdxRow] = await connection.query(checkPaymentIdxQuery, paymentIdx);
+	return checkPaymentIdxRow;
+}
+
+async function checkPrimaryPaymentIdx(connection, paymentIdx) {
+	const checkPrimaryPaymentIdxQuery = `
+		select exists(select paymentIdx from Payment where paymentIdx=?) as exist;
+	`;
+	const [checkPrimaryPaymentIdxRow] = await connection.query(checkPrimaryPaymentIdxQuery, paymentIdx);
+	return checkPrimaryPaymentIdxRow;
+}
+
+// 유저 권한 들어가 있는지 검사
+async function checkUserAuth(connection, userIdx, productIdx) {
+	const checkUserAuthQuery = `
+		SELECT exists (SELECT userAuthIdx FROM UserAuth WHERE userIdx = ? and productIdx = ? and status='N') as exist;
+	`;
+	const [checkUserAuthRow] = await connection.query(checkUserAuthQuery, [userIdx, productIdx]);
+	return checkUserAuthRow;
+}
+
+//유저 상세 구매내역 - 주문 상품 정보 조회 (상품단위)
+async function selectOrderPerProduct(connection, paymentIdx) {
+	const selectOrderPerProductQuery = `
+	SELECT paymentProductIdx, p.productIdx, p.productName, p.discountPrice, p.productThumbnail, refundPrice, isRefund
+	FROM PaymentProduct as pp 
+	JOIN Product as p on pp.productIdx = p.productIdx
+	WHERE paymentIdx = ? AND pp.status ='N' AND p.status = 'N';
+	`;
+	const [selectOrderPerProductRow] = await connection.query(selectOrderPerProductQuery, paymentIdx);
+	return selectOrderPerProductRow;
+}
+
+//유저 상세 구매내역 - 주문 결제 정보 조회
+async function selectPaymentFullInfo(connection, paymentIdx) {
+	const selectPaymentFullInfoQuery = `
+	select p.paymentIdx, p.level, paymentName, totalProductDiscountPrice, totalPrice, totalPoint, totalCoupon, date_format(paidAt, '%Y-%m-%d %H:%i') as paidDate, userName, concat(0, userPhoneNum) as userPhoneNum, pay_method
+	from Payment as p join User as u on p.userIdx=u.userIdx 
+	left join (select cast(sum(discountPrice) as signed integer) as totalProductDiscountPrice , paymentIdx, pp.status as ppstatus, p.status as pstatus  from PaymentProduct as pp join Product as p on pp.productIdx=p.productIdx group by pp.paymentIdx) as S 
+	on p.paymentIdx=S.paymentIdx 
+	where p.paymentIdx=? and p.status='N' and ppstatus='N' and pstatus='N';
+  `;
+	const [selectPaymentFullInfoRow] = await connection.query(selectPaymentFullInfoQuery, paymentIdx);
+	return selectPaymentFullInfoRow;
+}
+
+// 유저 권한 삽입
+async function insertUserAuth(connection, userAuthParams) {
+	const insertUserAuthQuery = `
+		insert into UserAuth(userIdx, productIdx, expireAt) values ?
+	`;
+	const [insertUserAuthRow] = await connection.query(insertUserAuthQuery, userAuthParams);
+	return insertUserAuthRow;
+}
+
+async function updateUserAuth(connection, userIdx, productIdx, expireAt) {
+	const updateUserAuthQuery = `
+		UPDATE UserAuth SET expireAt =  date_add(date_format(UserAuth.expireAt, '%Y-%m-%d %h:%m:%i'), interval ? day)
+		where userIdx = ? AND productIdx = ?
+	`;
+	const [updateUserAuthRow] = await connection.query(updateUserAuthQuery, [expireAt, userIdx, productIdx]);
+	return updateUserAuthRow;
+}
+//수정
+// 주문번호로 유저가 이번에 구매한 상품 인덱스들 조회
+async function selectProductInfoListByPayment(connection, paymentIdx) {
+	const selectProductInfoListByPaymentQuery = `
+		SELECT distinct(pp.productIdx) as productIdx, pr.parentIdx,  pd.duration, pointConsumed, p.updatedAt, pr.productTypeIdx
+		FROM Payment as p 
+		JOIN PaymentProduct as pp ON p.paymentIdx = pp.paymentIdx 
+		JOIN Product as pd ON pp.productIdx = pd.productIdx
+		JOIN ProductRelation pr on pr.productIdx = pd.productIdx
+		WHERE p.paymentIdx =  ? AND pp.status = 'N'
+	`;
+	const [selectProductInfoListByPaymentRow] = await connection.query(selectProductInfoListByPaymentQuery, paymentIdx);
+	return selectProductInfoListByPaymentRow;
+}
+
+//지금 사려고 하는 상품들과 이미 유저가 구매한 상품의 교집합 상품들 조회
+async function selectExistProductIdx(connection, userIdx, productIdxList) {
+	const selectExistProductIdxQuery = `
+		select productIdx from UserAuth where userIdx=? and status='N' and productIdx in ?
+	`;
+	const [selectExistProductIdxRow] = await connection.query(selectExistProductIdxQuery, [userIdx, [productIdxList]]);
+	return selectExistProductIdxRow;
+}
+
+// //상품 인덱스 리스트에 대하여 구매 가능한지 판단
+// async function checkProductBuyable(connection, userIdx, productIdxList) {
+// 	const checkProductBuyableQuery = `
+// 	select exists(
+// 	select productIdx from UserAuth where userIdx=? and status='N' and productIdx in (
+// 		with recursive cte as(
+// 			select productIdx, parentIdx from ProductRelation as pr
+// 			where productIdx in ?
+// 			union
+// 			select pr2.productIdx, pr2.parentIdx from ProductRelation as pr2
+// 			join cte on cte.parentIdx=pr2.productIdx
+// 		)
+// 		select distinct(cte.productIdx) from cte join Product as p on cte.productIdx=p.productIdx where p.isSelling=1)) as exist;
+// 	`;
+// 	const [checkProductBuyableRow] = await connection.query(checkProductBuyableQuery, [userIdx, [productIdxList]]);
+// 	return checkProductBuyableRow;
+// }
+
+//상품 인덱스 리스트에서 사용자가 구매할 수 있는 상품 인덱스들만 반환
+async function selectBuyableProductInfo(connection, paymentIdx, userIdx) {
+	const selectBuyableProductInfoQuery = `
+	SELECT distinct(pp.productIdx) as productIdx, date_add(date_format(p.updatedAt, '%Y-%m-%d 14:59:59'), interval pd.duration day) as expireAt 
+	FROM Payment as p
+	JOIN PaymentProduct as pp ON p.paymentIdx = pp.paymentIdx 
+	JOIN Product as pd ON pp.productIdx = pd.productIdx 
+	WHERE p.paymentIdx = ? AND pp.status = 'N' AND pp.productIdx NOT IN
+	(
+		SELECT productIdx FROM UserAuth WHERE userIdx = ? AND status='N' AND productIdx IN (
+		with recursive cte as(
+			SELECT productIdx, parentIdx FROM ProductRelation as pr
+			WHERE productIdx = pp.productIdx
+			UNION
+			SELECT pr2.productIdx, pr2.parentIdx FROM ProductRelation as pr2
+			JOIN cte ON cte.parentIdx = pr2.productIdx
+		)
+		SELECT distinct(cte.productIdx) FROM cte)
+	)
+	`;
+	const [selectBuyableProductInfoRow] = await connection.query(selectBuyableProductInfoQuery, [paymentIdx, userIdx]);
+	return selectBuyableProductInfoRow;
+}
+
+//결제완료에서 DB 업데이트한 것인지 체크
+async function checkPaidUpdate(connection, paymentIdx) {
+	const checkPaidUpdateQuery = `
+	select exists(select paymentIdx from Payment where level!='unverified' and paymentIdx=? and status='N') as exist;
+	`;
+	const [checkPaidUpdateRow] = await connection.query(checkPaidUpdateQuery, paymentIdx);
+	return checkPaidUpdateRow;
+}
+
+//상품별 환불 금액 계산
+async function selectProductRefundPrice(connection, refundParams) {
+	const refundPriceQuery = `
+	select case 
+	when datediff(now(), pa.paidAt)<=7 then 
+		case when pp.isUse=1 then round(pp.finalPrice-p.oneDayPrice*(datediff(now(), pa.paidAt)))
+			when pp.isUse=0 then pp.finalPrice
+		end
+	when datediff(now(), pa.paidAt)<=round((1/2)*p.duration) then round(pp.finalPrice-p.oneDayPrice*(datediff(now(), pa.paidAt)))
+	else 0 end as refundPrice
+	from PaymentProduct as pp join Product as p on p.productIdx=pp.productIdx join Payment as pa on pa.paymentIdx=pp.paymentIdx
+	where pp.paymentIdx=? and pp.productIdx=? and pp.status='N';
+	`;
+	const [refundPriceQueryRow] = await connection.query(refundPriceQuery, refundParams);
+	return refundPriceQueryRow;
+}
+
+async function selectCompanyProductRefundPrice(connection, paymentIdx, productIdx) {
+	const refundPriceQuery = `
+		select 
+		case 
+		when isUse=0 then finalPrice
+		when isUse=1 then 0
+   		end as refundPrice from PaymentProduct where paymentIdx=? and productIdx=? and status='N';
+	`;
+	const [refundPriceQueryRow] = await connection.query(refundPriceQuery, [paymentIdx, productIdx]);
+	return refundPriceQueryRow;
+}
+
+async function updatePaymentProductRefund(connection, refundParams) {
+	let updateRefundQuery = ``;
+	for (let i = 0; i < refundParams.length; i++) {
+		updateRefundQuery += `update PaymentProduct set refundPrice=?, refundReason=?, refundAt=?, pg_tid=?, receipt_url=?, isRefund=1 where paymentIdx=? and productIdx=?;`;
+	}
+	const [updateRefundRow] = await connection.query(updateRefundQuery, refundParams.flat());
+	return updateRefundRow;
+}
+
+async function updatePaymentRefund(connection, refundParams) {
+	const updateRefundQuery = `
+		update Payment set checksum=checksum-?, level='cancelled', refundAt=? where paymentIdx=?
+	`;
+	const [updateRefundRow] = await connection.query(updateRefundQuery, refundParams);
+	return updateRefundRow;
+}
+
+async function selectRefundPoint(connection, paymentIdx, productIdxList) {
+	const selectRefundPointQuery = `
+		select sum(usePoint) as refundTotalPoint from PaymentProduct where paymentIdx=? and productIdx in ?
+	`;
+	const [selectRefundPointRow] = await connection.query(selectRefundPointQuery, [paymentIdx, [productIdxList]]);
+	return selectRefundPointRow;
+}
+
+async function deleteUserAuth(connection, userIdx, productIdxList) {
+	const updateUserAuthQuery = `
+		update UserAuth set status='Y', expireAt = now() where userIdx=? and productIdx in ? and status='N'
+	`;
+	const [updateUserAuthRow] = await connection.query(updateUserAuthQuery, [userIdx, [productIdxList]]);
+	return updateUserAuthRow;
+}
+
+// 공기업 무료 회차 조회
+async function selectFreeCompanyExam(connection, productIdx) {
+	const selectFreeCompanyExamQuery = `
+		select distinct es.examSortIdx,es.examSortName,esi.examSortUrl, ci.prio from CompanyExam ce
+		left join Exam e on e.examidx = ce.examidx
+		left join ExamSort es on es.examSortIdx =e.examSortIdx 
+		left join ExamSortImage esi on esi.examSortIdx = es.examSortIdx
+		left join ExamDetail ed on ed.examIdx = e.examIdx
+		left join CompanyInfo ci on ci.examSortIdx=es.examSortIdx
+		left join ProductExamDetail ped on ped.examDetailIdx = ed.examDetailIdx
+		left join Product p on p.productIdx = ped.productIdx
+		where p.productIdx = ?;
+	`;
+	const [selectFreeCompanyExamRow] = await connection.query(selectFreeCompanyExamQuery, productIdx);
+	return selectFreeCompanyExamRow;
+}
+
+async function selectPerDetailProduct(connection, productIdx, discountPrice) {
+	const selectPerDetailProductQuery = `
+		select p.productIdx from Product p
+		LEFT JOIN ProductRelation pr on p.productIdx = pr.productIdx
+		where pr.productIdx = ? and p.discountPrice = ?
+	`;
+	const [selectPerDetailProductRow] = await connection.query(selectPerDetailProductQuery, [productIdx, discountPrice]);
+	return selectPerDetailProductRow;
+}
+
+module.exports = {
+	checkCategoryIdx,
+	checkUserExamDetailAuth,
+	checkSellingProductIdx,
+	checkUserAuth,
+	selectProductCategory,
+	selectCategoryList,
+	selectDefaultCategoryIdx,
+	selectCompanyCategoryIdx,
+	selectProductTopCategory,
+	selectProductCategory,
+	selectProductInfoListWithExamField,
+	selectExamDetailPriceList,
+	checkProductIdx,
+	checkPaymentIdx,
+	checkPrimaryPaymentIdx,
+	selectProduct,
+	selectProductInfo,
+	selectProductExamDetail,
+	selectProductExamDetailSingle,
+	selectProductUrl,
+	insertPayment,
+	insertPaymentProduct,
+	insertPaymentAddress,
+	selectPoint,
+	selectCouponPoint,
+	selectProductCoupon,
+	selectProduct,
+	selectProductDivision,
+	selectCouponInfo,
+	selectPaymentByIdx,
+	selectPaymentPrice,
+	selectTotalPoint,
+	updatePayment,
+	updateUserAuth,
+	updatePaymentPointConsumed,
+	updateOneDayPrice,
+	insertPointLog,
+	consumeUserPoint,
+	earnUserPoint,
+	selectTotalUseCoupon,
+	deletePaymentProduct,
+	deletePayment,
+	deleteUserCoupon,
+	selectOrderPerProduct,
+	selectPaymentFullInfo,
+	selectPackageInfoList,
+	insertUserAuth,
+	selectProductInfoListByPayment,
+	selectExistProductIdx,
+	checkPaidUpdate,
+	selectProductRefundPrice,
+	selectCompanyProductRefundPrice,
+	updatePaymentProductRefund,
+	updatePaymentRefund,
+	selectRefundPoint,
+	deleteUserAuth,
+	selectFreeCompanyExam,
+	selectPerDetailProduct,
+	selectParentProductInfo,
+};
diff --git a/_old/src/Store/storeProvider.js b/_old/src/Store/storeProvider.js
new file mode 100644
index 0000000..853b7a9
--- /dev/null
+++ b/_old/src/Store/storeProvider.js
@@ -0,0 +1,593 @@
+const { pool } = require("../../config/database");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const storeDao = require("./storeDao");
+const examDao = require("../Exam/examDao");
+const { logger } = require("../../config/winston");
+const constant = require("../../config/constant");
+const errorResponse = require("../../utils/errorResponse");
+
+exports.changePayMethodToText = function (pay_method) {
+	switch (pay_method) {
+		case "card":
+			return "신용카드";
+		case "pointPaid":
+			return "전액포인트결제";
+		case "point":
+			return "간편결제";
+		case "trans":
+			return "실시간 계좌이체";
+		case "kakaopay":
+			return "카카오페이";
+		case "payco":
+			return "페이코";
+		case "lpay":
+			return "LPAY";
+		case "ssgpay":
+			return "SSG페이";
+		case "tosspay":
+			return "토스 간편결제";
+		case "naverpay":
+			return "네이버페이";
+		default:
+			throw new Error("알 수 없는 결제 수단입니다 : " + pay_method);
+	}
+};
+
+exports.changePayStatusToText = function (status) {
+	switch (status) {
+		case "paid":
+			return "결제완료";
+		case "pointPaid":
+			return "전액포인트결제";
+		case "cancelling":
+			return "환불대기";
+		case "cancelled":
+			return "환불완료";
+		case "failed":
+			return "결제실패";
+		case "ready":
+			return "미결제";
+		case "unverified":
+			return "결제확인중";
+		default:
+			throw new Error("알 수 없는 상태입니다 : " + status);
+	}
+};
+
+exports.unixToTimestamp = function (t) {
+	let date = new Date(t * 1000);
+	let year = date.getUTCFullYear();
+	let month = "0" + (date.getUTCMonth() + 1);
+	let day = "0" + date.getUTCDate();
+	let hour = "0" + date.getUTCHours();
+	let minute = "0" + date.getUTCMinutes();
+	let second = "0" + date.getUTCSeconds();
+	return (
+		year +
+		"-" +
+		month.substring(month.length - 2) +
+		"-" +
+		day.substring(day.length - 2) +
+		" " +
+		hour.substring(hour.length - 2) +
+		":" +
+		minute.substring(minute.length - 2) +
+		":" +
+		second.substring(second.length - 2)
+	);
+};
+
+// //부모 상품의 정보를 가져오는 부분. TODO: 삭제하기
+// exports.getParentInfo = async function (merchant_uid) {
+// 	const connection = await pool.getConnection(async (conn) => conn);
+// 	try {
+//const getParentInfoList = await storeDao.getParentProduct(connection, merchant_uid);
+// 		if (getParentInfoList.length == 0) throw new errorResponse(baseResponse.PURCHASE_HISTROY_EMPTY, 400);
+// 		return getParentInfoList;
+// 	} catch (error) {
+// 		throw error;
+// 	} finally {
+// 		connection.release();
+// 	}
+// };
+
+exports.checkCategoryIdx = async function (category) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const checkCategoryIdxResult = await storeDao.checkCategoryIdx(connection, category);
+
+		if (!checkCategoryIdxResult[0].exist) throw new errorResponse(baseResponse.BUSINESS_CATEGORY_ERROR, 400);
+		return checkCategoryIdxResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.checkPaidUpdate = async function (paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const checkPaidUpdateResult = await storeDao.checkPaidUpdate(connection, paymentIdx);
+		return checkPaidUpdateResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 시험 권한 검증 회차별
+exports.checkUserExamDetailAuth = async function (examDetailIdx, userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const checkUserExamDetailAuthResult = await storeDao.checkUserExamDetailAuth(connection, examDetailIdx, userIdx);
+		return checkUserExamDetailAuthResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 시험 권한 검증 시험별 (랜덤모의고사용)
+exports.checkUserExamAuth = async function (examIdx, userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const checkProductAuth = await examDao.selectUserExamAuth(connection, examIdx, userIdx);
+		if (checkProductAuth[0].exist == 1) {
+			return checkProductAuth;
+		}
+
+		const examDetailList = await examDao.selectExamDetail(connection, examIdx);
+		let checkUserExamAuthResult;
+		//TODO: 시험회차를 다돌면서 권한을 체크해야할까? 상품의 권한을 가지고 있다면 이 아래의 반복문은 불필요한 과정으로 여겨진다.
+		for (let i = 0; i < examDetailList.length; i++) {
+			checkUserExamAuthResult = await storeDao.checkUserExamDetailAuth(
+				connection,
+				examDetailList[i].examDetailIdx,
+				userIdx,
+			);
+			if (examDetailList[i].publicLevel >= 1 && checkUserExamAuthResult[0].exist == 0) {
+				return checkUserExamAuthResult;
+			}
+		}
+		// console.log("checkUserExamAuthResult : ", checkUserExamAuthResult);
+		// 이곳으로 왔다는 것은 auth가 존재하거나 무료상품이라는 것.
+		checkUserExamAuthResult = [{ exist: 1 }];
+		return checkUserExamAuthResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getCategoryList = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const getCategoryListResult = await storeDao.selectCategoryList(connection);
+		let result = [];
+		for (let i = 0; i < getCategoryListResult.length; i++) {
+			result[i] = {
+				categoryName: getCategoryListResult[i].productName,
+				categoryIdx: getCategoryListResult[i].productIdx,
+			};
+		}
+		return resultResponse(baseResponse.SUCCESS, result);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getDefaultCategoryIdx = async function (examSortRef) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const defaultCategoryIdx = await storeDao.selectDefaultCategoryIdx(connection, examSortRef);
+		return defaultCategoryIdx;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getCompanyCategoryIdx = async function (examSortRef) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const defaultCategoryIdx = await storeDao.selectCompanyCategoryIdx(connection, examSortRef);
+		return defaultCategoryIdx;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getProductInfoList = async function (category, examField) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		let examFieldQuery;
+		if (!examField) {
+			examFieldQuery = "";
+		} else {
+			examFieldQuery = `AND pt.examField = "${examField}"`;
+		}
+		const productInfoListResult = await storeDao.selectProductInfoListWithExamField(
+			connection,
+			category,
+			examFieldQuery,
+		);
+		return productInfoListResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getProductDetail = async function (parentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		let parentProductInfo = await storeDao.selectParentProductInfo(connection, parentIdx);
+		parentProductInfo = parentProductInfo[0];
+		let isSingleRound = false; // 단일회차인지 아닌지 판별하는 변수
+		if (parentProductInfo.productTypeIdx == 1) isSingleRound = true;
+		parentProductInfo.flag = isSingleRound;
+		let productInfo = await storeDao.selectProductInfo(connection, parentIdx);
+
+		//if (productInfo.length == 0) return new errorResponse(baseResponse.PRODUCT_ERROR, 404);
+		if (productInfo.length == 0) return resultResponse(baseResponse.SUCCESS, parentProductInfo); // depth2(50일, 180일) 상품이 없는 경우 빈배열로 response
+		let category = await storeDao.selectProductCategory(connection, parentIdx);
+		parentProductInfo.category = category[0].productName;
+		let examDetailArray = [];
+		//const productUrls = await storeDao.selectProductUrl(connection, productIdx);
+		if (parentProductInfo.category == "공기업" && isSingleRound) {
+			const examDetails = await storeDao.selectProductExamDetailSingle(connection, parentIdx);
+
+			examDetails.forEach((obj) => {
+				if (!isNaN(obj.examRound)) obj.examRound += "회차";
+				obj.examYear = obj.examDate.getFullYear();
+				obj.examFullName = obj.examName + " " + obj.examYear + "년 " + obj.examRound;
+				delete obj.examDate;
+				delete obj.date;
+				// delete obj.examRound;
+				examDetailArray.push(obj);
+			});
+			parentProductInfo.productInfo = examDetailArray;
+		} else parentProductInfo.productInfo = productInfo;
+		return resultResponse(baseResponse.SUCCESS, parentProductInfo);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getPackageList = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const packageListResult = await storeDao.selectPackageInfoList(connection);
+		return packageListResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getExamDetailPriceList = async function (examProductIdxList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examDetailPriceResult = await storeDao.selectExamDetailPriceList(connection, examProductIdxList);
+		return examDetailPriceResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 상품 인덱스 확인
+exports.productIdxCheck = async function (productIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const productIdxCheckResult = await storeDao.checkProductIdx(connection, productIdx);
+		if (productIdxCheckResult[0].exist === 0) throw new errorResponse(baseResponse.PRODUCT_NOT_EXIST, 404);
+		return productIdxCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 판매 상품 인덱스 확인
+exports.productIdxSellingCheck = async function (productIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const productIdxCheckResult = await storeDao.checkSellingProductIdx(connection, productIdx);
+		if (productIdxCheckResult[0].exist === 0) throw new errorResponse(baseResponse.PRODUCT_NOT_EXIST, 404);
+		return productIdxCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getProductPaymentList = async function (productIdxList, userIdx) {
+	try {
+		let productList = [];
+		const connection = await pool.getConnection(async (conn) => conn);
+		for (let object of productIdxList) {
+			const productInfo = await storeDao.selectProduct(connection, object);
+			const categoryInfo = await storeDao.selectProductCategory(connection, productInfo[0].productIdx);
+			productList.push({
+				category: categoryInfo[0].productName,
+				productIdx: productInfo[0].productIdx,
+				productName: productInfo[0].productName,
+				productThumbnail: productInfo[0].productThumbnail,
+				shortDescription: productInfo[0].shortDescription,
+				duration: productInfo[0].duration,
+				price: productInfo[0].price,
+				discountPrice: productInfo[0].discountPrice,
+			});
+		}
+		// 유저 포인트 관련
+		let userPoint = await storeDao.selectPoint(connection, userIdx);
+		const result = { productList: productList, userPoint: userPoint[0].point };
+		connection.release();
+
+		return result;
+	} catch (error) {
+		throw error;
+	}
+};
+
+exports.getProductCouponList = async function (userIdx, productIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const productCouponList = await storeDao.selectProductCoupon(connection, userIdx, productIdx);
+
+		return productCouponList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getProductInfo = async function (productIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const productInfo = await storeDao.selectProduct(connection, productIdx);
+		return productInfo;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getCouponInfo = async function (couponIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const couponInfo = await storeDao.selectCouponInfo(connection, couponIdx);
+		return couponInfo;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getPaymentInfo = async function (paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const paymentInfo = await storeDao.selectPaymentByIdx(connection, paymentIdx);
+
+		return paymentInfo;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getTotalPoint = async function (paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const checkPaidUpdateResult = await storeDao.checkPaidUpdate(connection, paymentIdx);
+		// unverified 인 경우 오류메세지 부분 추가 필요
+		const totalPointInfo = await storeDao.selectTotalPoint(connection, paymentIdx);
+		return totalPointInfo;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getTotalUseCoupon = async function (paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const totalUseCouponInfo = await storeDao.selectTotalUseCoupon(connection, paymentIdx);
+		return totalUseCouponInfo;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.paymentIdxCheck = async function (paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const paymentIdxCheckInfo = await storeDao.checkPaymentIdx(connection, paymentIdx);
+		if (paymentCheckResult[0].exist === 0) throw new errorResponse(baseResponse.PAYMENT_NOT_EXIST, 404);
+		return paymentIdxCheckInfo;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getProductInfoListByPayment = async function (paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const productIdxListInfo = await storeDao.selectProductInfoListByPayment(connection, paymentIdx);
+		return productIdxListInfo;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+//지울수도
+exports.getExistProductIdx = async function (userIdx, productIdxList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const getExistProductIdxResult = await storeDao.selectExistProductIdx(connection, userIdx, productIdxList);
+		return getExistProductIdxResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.productBuyableCheck = async function (userIdx, productIdxList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const productBuyableCheckResult = await storeDao.checkProductBuyable(connection, userIdx, productIdxList);
+		return productBuyableCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getBuyableProductInfo = async function (userIdx, paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const selectBuyableProductInfoResult = await storeDao.selectBuyableProductInfo(connection, paymentIdx, userIdx);
+		return selectBuyableProductInfoResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getPaymentPrice = async function (paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const getPaymentPriceResult = await storeDao.selectPaymentPrice(connection, paymentIdx);
+		return getPaymentPriceResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.computeProductRefundPrice = async function (userIdx, paymentIdx, productIdxList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		let refundPriceList = [];
+		for (let i = 0; i < productIdxList.length; i++) {
+			let idxResult = await storeDao.selectProductDivision(connection, productIdxList[i]);
+			let refundResult;
+			if (idxResult[0].productName == constant.DEFAULT_COMPANY_NAME) {
+				refundResult = await storeDao.selectCompanyProductRefundPrice(connection, paymentIdx, productIdxList[i]);
+			} else {
+				refundResult = await storeDao.selectProductRefundPrice(connection, [paymentIdx, productIdxList[i]]);
+				//회차 포함하는 상품이면 한번 더 차감.
+			}
+			refundResult.length && refundResult[0] != 0
+				? refundPriceList.push(refundResult[0].refundPrice)
+				: refundPriceList.push(null);
+		}
+		return refundPriceList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getRefundProductInfo = async function (paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const orderPerProduct = await storeDao.selectOrderPerProduct(connection, paymentIdx);
+		for (let object of orderPerProduct) {
+			const divisionInfo = await storeDao.selectProductDivision(connection, object.productIdx);
+			object.category = divisionInfo[0].productName;
+		}
+		const totalRefundPrice = orderPerProduct.reduce((sum, currentValue) => sum + currentValue.refundPrice, 0);
+		let response = {
+			productInfoList: orderPerProduct,
+			totalRefundPrice: totalRefundPrice,
+		};
+		return response;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 결제 단위로 구매 상품 정보 조회
+exports.getPaymentProductInfo = async function (paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		const orderPerProduct = await storeDao.selectOrderPerProduct(connection, paymentIdx);
+		return orderPerProduct;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getPaymentDetailProductInfo = async function (productIdx, discountPrice) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const orderPerProduct = await storeDao.selectPerDetailProduct(connection, productIdx, discountPrice);
+		return orderPerProduct;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
diff --git a/_old/src/Store/storeRoute.js b/_old/src/Store/storeRoute.js
new file mode 100644
index 0000000..e3c2eed
--- /dev/null
+++ b/_old/src/Store/storeRoute.js
@@ -0,0 +1,57 @@
+module.exports = function (app) {
+	const store = require("./storeController");
+	const jwtMiddleware = require("../../middlewares/jwtMiddleware");
+	const getImPortToken = require("../../config/imPortToken");
+
+	// API No ?. 유료 메뉴 조회 API
+	app.get("/api/stores/category", store.getCategoryList);
+
+	// API No ?. 유료 상품들 조회 API
+	app.get("/api/stores/productList", store.getProductList);
+
+	// API No ?. 상품 상세 조회 API
+	app.get("/api/stores/product/:parentIdx", store.getProductDetail);
+
+	// API No ?. 구매하려는 상품 정보와 보유 포인트 반환 API
+	app.post("/api/stores/payment", jwtMiddleware, store.getPaymentInfo);
+
+	// API No ?. 유저가 이미 구매한 상품인지 조회하는 API
+	app.post("/api/stores/payment/checkHistory", jwtMiddleware, store.checkPurchaseHistory);
+
+	//API No ?. 환불 정보 조회 API
+	app.get("/api/stores/payment/refund", jwtMiddleware, store.getRefundInfo);
+
+	//API No ?. 환불 API
+	app.post("/api/stores/payment/refund/request", jwtMiddleware, getImPortToken, store.refundRequest);
+
+	// API No ?. 결제 요청 API
+	app.post("/api/stores/payment/request", jwtMiddleware, store.buyProductRequest);
+
+	// API No ?. 주문 결제 완료 API
+	app.post(
+		"/api/stores/payment/complete",
+		jwtMiddleware,
+		getImPortToken,
+		store.completePaymentHandler((state = "web")),
+	);
+
+	//API No ?. 아임포트 모바일 결제 완료 API
+	app.get(
+		"/api/stores/payment/complete/mobile",
+		jwtMiddleware,
+		getImPortToken,
+		store.completePaymentHandler((state = "mobile")),
+	);
+
+	//API No ?. 아임포트 웹훅 API
+	app.post("/api/stores/payment/imPortWebhook", getImPortToken, store.paymentWebhook);
+
+	// API No ?. 특정 상품 사용가능 쿠폰 조회 API
+	app.get("/api/stores/payment/:productIdx", jwtMiddleware, store.getProductCouponList);
+
+	// API No ?. 패키지 조회 API
+	// app.get("/api/stores/packageList", store.getPackageList);
+
+	// // API No ?. 권한 부여 test api
+	// app.post("/api/stores/test", jwtMiddleware, store.authTest);
+};
diff --git a/_old/src/Store/storeService.js b/_old/src/Store/storeService.js
new file mode 100644
index 0000000..b48999e
--- /dev/null
+++ b/_old/src/Store/storeService.js
@@ -0,0 +1,204 @@
+const { pool } = require("../../config/database");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const errorResponse = require("../../utils/errorResponse");
+const storeDao = require("./storeDao");
+const storeProvider = require("./storeProvider");
+const { logger } = require("../../config/winston");
+const CANCEL_POINT_STATUS = "C";
+
+exports.updatePayment = async function (
+	userIdx,
+	paymentName,
+	totalPrice,
+	totalPoint,
+	pay_method,
+	paymentProductParams,
+	shipInfo,
+) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		let paymentIdx;
+
+		while (true) {
+			paymentIdx =
+				"E" + Date.now().toString().substring(5, 14) + Math.random().toString(36).toUpperCase().substring(2, 5);
+			let checkPrimaryPaymentIdxResult = await storeDao.checkPrimaryPaymentIdx(connection, paymentIdx);
+			if (!checkPrimaryPaymentIdxResult[0].exist) break;
+		}
+
+		await storeDao.insertPayment(connection, [
+			paymentIdx,
+			paymentName,
+			pay_method,
+			userIdx,
+			totalPrice,
+			totalPoint,
+			totalPrice,
+		]);
+		for (let i = 0; i < paymentProductParams.length; i++) {
+			paymentProductParams[i].unshift(paymentIdx);
+		}
+		await storeDao.insertPaymentProduct(connection, paymentProductParams);
+
+		if (shipInfo) {
+			shipInfo.unshift(paymentIdx);
+			await storeDao.insertPaymentAddress(connection, shipInfo);
+		}
+		//await storeDao.updateOneDayPrice(connection, paymentIdx); 상품 생성할 때 oneDayPrice 입력해줘야.
+		await connection.commit();
+		return paymentIdx;
+	} catch (error) {
+		await connection.rollback();
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.consumePoint = async function (userIdx, sort, content, point, paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		await storeDao.insertPointLog(connection, [userIdx, sort, content, point]);
+		const consumePoint = await storeDao.consumeUserPoint(connection, userIdx, point);
+		const PaymentConsumePoint = await storeDao.updatePaymentPointConsumed(connection, paymentIdx);
+		await connection.commit();
+		return;
+	} catch (error) {
+		await connection.rollback();
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.consumeCoupon = async function (userIdx, couponList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await storeDao.deleteUserCoupon(connection, userIdx, couponList);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.completePayment = async function (updatePaymentParams) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		await storeDao.updatePayment(connection, updatePaymentParams);
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.deletePayment = async function (paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await storeDao.deletePayment(connection, paymentIdx);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.deletePaymentProduct = async function (paymentIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await storeDao.deletePaymentProduct(connection, paymentIdx);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 권한 부여
+exports.giveUserExamAuth = async function (userIdx, productInfoList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let userAuthParams = [];
+		for (let i = 0; i < productInfoList.length; i++) {
+			let now = new Date();
+			const userAuth = await storeDao.checkUserAuth(connection, userIdx, productInfoList[i].productIdx);
+			let expireAt = userAuth[0].exist
+				? productInfoList[i].duration
+				: new Date(now.setDate(now.getDate() + productInfoList[i].duration));
+			if (!userAuth[0].exist) userAuthParams.push([userIdx, productInfoList[i].productIdx, expireAt]);
+			else await storeDao.updateUserAuth(connection, userIdx, productInfoList[i].productIdx, expireAt);
+		}
+		if (userAuthParams.length > 0) await storeDao.insertUserAuth(connection, [userAuthParams]);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.refundProducts = async function (userIdx, merchant_uid, refundPricePerProduct, productIdxList, lastRefundInfo) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		//Payment Product 환불
+		let refundParamList = [];
+		let refundAt = storeProvider.unixToTimestamp(lastRefundInfo.cancelled_at);
+		for (let i = 0; i < productIdxList.length; i++) {
+			if (refundPricePerProduct[i] == null) continue;
+			let refundParams = [
+				refundPricePerProduct[i],
+				lastRefundInfo.reason,
+				refundAt,
+				lastRefundInfo.pg_tid,
+				lastRefundInfo.receipt_url,
+				merchant_uid,
+				productIdxList[i],
+			];
+			refundParamList.push(refundParams);
+		}
+		//console.log(refundParamList);
+		await storeDao.updatePaymentProductRefund(connection, refundParamList);
+		//console.log("update PaymentProduct complete");
+
+		//Payment Update
+		let paymentRefundParams = [lastRefundInfo.amount, refundAt, merchant_uid];
+		//console.log("paymentRefundParams : ");
+		//console.log(paymentRefundParams);
+		await storeDao.updatePaymentRefund(connection, paymentRefundParams);
+		//console.log("update Paymet complete");
+
+		//User의 Point 복구
+		let refundPoint = await storeDao.selectRefundPoint(connection, merchant_uid, productIdxList);
+		let refundTotalPoint = refundPoint[0].refundTotalPoint;
+		await storeDao.earnUserPoint(connection, userIdx, refundTotalPoint);
+		//console.log("restore user point complete");
+
+		await storeDao.insertPointLog(connection, [userIdx, refundTotalPoint, CANCEL_POINT_STATUS]);
+		//console.log("create point log complete");
+
+		//User Auth에서 관련된 권한 삭제
+		//console.log([userIdx, productIdxList]);
+		await storeDao.deleteUserAuth(connection, userIdx, productIdxList);
+
+		await connection.commit();
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (error) {
+		await connection.rollback();
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
diff --git a/_old/src/User/subjectiveUserRoute.js/subjectiveUserRoute.js b/_old/src/User/subjectiveUserRoute.js/subjectiveUserRoute.js
new file mode 100644
index 0000000..e8d4fd7
--- /dev/null
+++ b/_old/src/User/subjectiveUserRoute.js/subjectiveUserRoute.js
@@ -0,0 +1,33 @@
+const express = require("express");
+const router = express.Router();
+const jwtMiddleware = require("../../../middlewares/jwtMiddleware");
+const authCheck = require("../../../middlewares/authCheck");
+const user = require("../userController");
+
+// // API No ?. 오답노트 목록 조회 API
+// 	//http://localhost:3005/api/users/4/reviewNote?page=1&sort=1&order=0&bookMark=0&examField=전기 이런식으로 자격증 선택시 examField를 전기로 디폴트 설정 해주어야 함.
+// 	app.get("/api/users/:userIdx/reviewNote", jwtMiddleware, user.userReviewNote);
+
+// API NO ?. 주관식 오답노트 문제별로 조회하기
+router.get("/:userIdx/reviewNote", jwtMiddleware, user.userSubjectiveReviewNote);
+
+// API NO ?. 주관식 오답노트 회차별로 조회하기
+router.get("/:userIdx/reviewNote/exam", jwtMiddleware, user.userSubjectiveReviewNoteEd);
+
+// API NO ?. 주관식 오답노트 과목별로 조회하기
+router.get("/:userIdx/reviewNote/subject", jwtMiddleware, user.userSubjectiveReviewNoteSubject);
+
+// API NO ?. 주관식 오답노트 문제(문제별) 조회
+router.get("/:userIdx/reviewNote/set", jwtMiddleware, authCheck, user.userSubjectiveReviewNoteSet);
+
+// API No ?. 주관식 오답노트 문제 조회(회차) API  카텍스 유무
+router.get("/:userIdx/reviewNote/problem/:examDetailIdx", jwtMiddleware, authCheck, user.userSubjectiveReviewNoteRound);
+
+// API No ?. 주관식 오답노트 문제 조회(과목별) API  카텍스 유무 - 깁기창
+router.get(
+	"/:userIdx/reviewNote/:examIdx/subject/problem/:examSubjectIdx",
+	jwtMiddleware,
+	authCheck,
+	user.userSubjectiveReviewNoteSubjectProblem,
+);
+module.exports = router;
diff --git a/_old/src/User/userController.js b/_old/src/User/userController.js
new file mode 100644
index 0000000..82a34fe
--- /dev/null
+++ b/_old/src/User/userController.js
@@ -0,0 +1,1913 @@
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const crypto = require("crypto");
+const axios = require("axios");
+const smtpTransport = require("../../config/email");
+const { logger } = require("../../config/winston");
+const examProvider = require("../Exam/examProvider");
+const adminProvider = require("../Admin/adminProvider");
+const channelTalkService = require("../ChannelTalk/channelTalkService");
+const storeProvider = require("../Store/storeProvider");
+const secret = require("../../config/secret");
+const jwt = require("jsonwebtoken");
+const secret_config = require("../../config/secret");
+
+const errorResponse = require("../../utils/errorResponse");
+const asyncHandler = require("../../utils/asyncHandler");
+//test
+const userService = require("./userService");
+const userProvider = require("./userProvider");
+const regEmail = require("regex-email");
+const regexEmail = require("regex-email");
+const baseResponseStatus = require("../../config/baseResponseStatus");
+const cache = require("../../config/cache");
+const { userInfo } = require("os");
+const regPassword = /^(?=.*[a-zA-Z])(?=.*[`~!@#$%^&*-_+=\\(\\)\])(?=.*[0-9]).{8,16}/;
+const regPhoneNum = /^\d{3}\d{3,4}\d{4}$/;
+const regUserName = /^[가-힣]{2,4}$/;
+const regBirthDate = /^(19[0-9][0-9]|20\d{2})(0[0-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$/;
+const regAddress = /^[가-힣]{2,8}$/;
+const regNumber = /^[0-9]/;
+const regAlphabet = /^[a-zA-Z]*$/;
+const regNickname = /^[a-zA-Z0-9가-힣]*$/;
+const regAuthNumber = /^[0-9]{6}/;
+const constant = require("../../config/constant");
+const { Logform } = require("winston");
+const { KAKAO_PHONE_NUM_EMPTY } = require("../../config/baseResponseStatus");
+
+const ko_check = /[ㄱ-ㅎ|ㅏ-ㅣ]/; // 한글 모음 자음
+const ko_combination_check = /[가-힣]/; // 조합 한글
+const space_check = /[' ']/; // 공백
+const special_check = /[`~!@#$%^&*|\\\'\";:\/?]/gi; //특수문자
+const email_form_check = /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i; //eamil 검증
+const password_pattern3 = /[~!@#$%^&*()_+|<>?:{}]/; // 특수문자 - 비밀번호
+
+/**
+ * @deprecated
+ * API No.
+ * API Name : 회원가입 API
+ * [POST]] /users/sign-in
+ */
+//User Check Result .exist 변경할것
+exports.postUser = asyncHandler(async function (req, res) {
+	const { email, password, phoneNum, userName, dateOfBirth, nickname, recommendNickname } = req.body;
+
+	// Validation Check (Request Error)
+	if (!email) return res.send(basickResponse(baseResponse.EMAIL_EMPTY));
+
+	if (email.length > 45) return res.send(basickResponse(baseResponse.EMAIL_LENGTH_ERROR));
+
+	if (!regEmail.test(email)) return res.send(basickResponse(baseResponse.EMAIL_ERROR_TYPE));
+
+	if (!password) return res.send(basickResponse(baseResponse.PASSWORD_EMPTY));
+
+	if (password.length < 8) return res.send(basickResponse(baseResponse.PASSWORD_LENGTH_ERROR));
+
+	if (!regPassword.test(password)) return res.send(basickResponse(baseResponse.PASSWORD_ERROR_TYPE)); //비밀번호는 영문, 숫자, 특수문자 포함 8~16 자리를 입력해주세요.
+
+	if (!phoneNum) return res.send(basickResponse(baseResponse.PHONENUM_EMPTY));
+
+	if (!regPhoneNum.test(phoneNum)) return res.send(basickResponse(baseResponse.PHONENUM_ERROR_TYPE));
+
+	if (!userName) return res.send(basickResponse(baseResponse.USERNAME_EMPTY));
+
+	if (!regUserName.test(userName)) return res.send(basickResponse(baseResponse.USERNAME_ERROR_TYPE));
+
+	if (!dateOfBirth) return res.send(basickResponse(baseResponse.DATEOFBIRTH_EMPTY));
+
+	if (dateOfBirth.length != 8) return res.send(basickResponse(baseResponse.DATEOFBIRTH_LENGTH_ERROR));
+
+	if (!regBirthDate.test(dateOfBirth)) return res.send(basickResponse(baseResponse.DATEOFBIRTH_ERROR_TYPE));
+
+	if (!nickname) return res.send(basickResponse(baseResponse.NICKNAME_EMPTY));
+
+	if (nickname.length > 8 || nickname.length < 1) return res.send(basickResponse(baseResponse.NICKNAME_LENGTH_ERROR));
+
+	if (!regNickname.test(nickname)) return res.send(basickResponse(baseResponse.NICKNAME_ERROR_TYPE));
+
+	let isRejoin = false;
+	//provider는 소셜 로그인인지 웹 자체 로그인인지 판단하기 위한 변수
+	let provider = "engineeo";
+	// Validation Check (Response Error)
+	const emailCheckResult = await userProvider.userEmailCheck(email, provider);
+	if (emailCheckResult.length === 1) {
+		if (emailCheckResult[0].status === "Y") return res.send(basickResponse(baseResponse.EMAIL_INVALID));
+		return res.send(basickResponse(baseResponse.EMAIL_EXIST));
+	}
+
+	const phoneCheckResult = await userProvider.userPhoneNumCheck(phoneNum, provider);
+	for (let i = 0; i < phoneCheckResult.length; i++) {
+		if (phoneCheckResult[i].status !== "Y") return res.send(basickResponse(baseResponse.PHONENUM_EXIST));
+		if (phoneCheckResult[i].status === "Y") isRejoin = true;
+	}
+
+	const nicknameCheckResult = await userProvider.userNicknameCheck(nickname);
+	for (let i = 0; i < nicknameCheckResult.length; i++) {
+		if (nicknameCheckResult[i].status !== "Y") return res.send(basickResponse(baseResponse.NICKNAME_EXIST));
+	}
+
+	if (recommendNickname) {
+		let isValidRecommend = false;
+		const recommendNicknameCheckResult = await userProvider.userNicknameCheck(recommendNickname);
+		for (let i = 0; i < recommendNicknameCheckResult.length; i++) {
+			if (recommendNicknameCheckResult[i].status !== "Y") {
+				isValidRecommend = true;
+				break;
+			}
+		}
+		if (!isValidRecommend) {
+			recommendNickname = "";
+		}
+	}
+
+	const signInResponse = await userService.createUser(
+		email,
+		password,
+		phoneNum,
+		userName,
+		dateOfBirth,
+		nickname,
+		recommendNickname,
+		isRejoin,
+		provider,
+	);
+
+	return res.send(resultResponse(baseResponse.SIGN_UP_SUCCESS, signInResponse));
+});
+
+/**
+ * API No.
+ * API Name : 이메일 확인 API
+ * [GET] /users/emailAuth
+ */
+exports.checkEmail = asyncHandler(async function (req, res) {
+	const { email, provider } = req.body;
+
+	// Validation Check (Request Error)
+	if (!email) return res.send(basickResponse(baseResponse.EMAIL_EMPTY));
+
+	if (email.length > 45) return res.send(basickResponse(baseResponse.EMAIL_LENGTH_ERROR));
+
+	if (!regEmail.test(email)) return res.send(basickResponse(baseResponse.EMAIL_ERROR_TYPE));
+
+	// Validation Check (Response Error)
+	const emailCheckResult = await userProvider.userEmailCheck(email, provider ?? "engineeo");
+	if (emailCheckResult.length === 1) {
+		if (emailCheckResult[0].status === "Y") return res.send(basickResponse(baseResponse.EMAIL_INVALID));
+		return res.send(basickResponse(baseResponse.EMAIL_EXIST));
+	}
+	return res.send(basickResponse(baseResponse.EMAIL_CONFIRM_SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 닉네임 확인 API
+ * [POST] /users/nicknameAuth
+ */
+exports.checkNickname = asyncHandler(async function (req, res) {
+	const nickname = req.body.nickname;
+
+	// Validation Check (Request Error)
+	if (!nickname) return res.send(basickResponse(baseResponse.NICKNAME_EMPTY));
+
+	if (nickname.length > 8 || nickname.length < 1) return res.send(basickResponse(baseResponse.NICKNAME_LENGTH_ERROR));
+
+	if (!regNickname.test(nickname)) return res.send(basickResponse(baseResponse.NICKNAME_ERROR_TYPE));
+
+	// Validation Check (Response Error)
+	const nicknameCheckResult = await userProvider.userNicknameCheck(nickname);
+
+	for (let i = 0; i < nicknameCheckResult.length; i++) {
+		if (nicknameCheckResult[i].status !== "Y") return res.send(basickResponse(baseResponse.NICKNAME_EXIST));
+	}
+
+	return res.send(basickResponse(baseResponse.NICKNAME_CONFIRM_SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 비밀번호 확인 API
+ * [POST] /users/passwordAuth
+ */
+exports.checkPassword = asyncHandler(async function (req, res) {
+	const password = req.body.password;
+	const userIdx = req.verifiedToken.userIdx;
+	// Validation Check (Request Error)
+	if (!userIdx) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	if (!password) return res.send(basickResponse(baseResponse.PASSWORD_EMPTY));
+
+	const hashedPassword = crypto.createHash("sha512").update(password).digest("hex");
+
+	// Validation Check (Response Error)
+	const passwordCheckResult = await userProvider.userPasswordCheck(userIdx);
+	if (passwordCheckResult[0].userPassword !== hashedPassword)
+		return res.send(basickResponse(baseResponse.PASSWORD_NOT_MATCH));
+
+	return res.send(basickResponse(baseResponse.SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 토큰 확인 API
+ * [POST] /users/tokenAuth
+ */
+exports.checkToken = asyncHandler(async function (req, res) {
+	let expiresIn;
+	const userIdx = req.verifiedToken.userIdx;
+	const isKeep = req.verifiedToken.isKeep;
+	const admissionAuthStatus = req.admissionAuthStatus;
+	const memberHash = req.memberHash;
+
+	if (!memberHash) return res.send(basickResponse(baseResponse.MEMBERHASH_NOT_EXIST));
+
+	if (isKeep == 1) expiresIn = "30d";
+	else expiresIn = "6h";
+	//토큰 생성 Service
+	let token = jwt.sign(
+		{
+			userIdx: userIdx,
+			isKeep: isKeep,
+		}, // 토큰의 내용(payload)
+		secret_config.jwtsecret, // 비밀키
+		{
+			expiresIn: expiresIn,
+			subject: "userInfo",
+		}, // 유효 기간 365일
+	);
+	const userInfo = await userProvider.userInfo(userIdx);
+	if (userInfo.length == 0) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+	let result = {
+		userIdx: userIdx,
+		jwt: token,
+		memberHash: memberHash,
+		userName: userInfo[0].userName,
+		nickname: userInfo[0].nickname,
+	};
+	if (admissionAuthStatus != undefined) result.admissionAuthStatus = admissionAuthStatus;
+	return res.send(resultResponse(baseResponse.VERIFICATION_SUCCESS, result));
+});
+
+/**
+ * API No.
+ * API Name : 로그인 API
+ * [POST] /users/sign-in
+ */
+exports.login = asyncHandler(async function (req, res) {
+	const { email, password, provider } = req.body;
+	let isKeep = req.body.isKeep;
+
+	// Validation Check (Request Error)
+	if (!email) return res.send(basickResponse(baseResponse.EMAIL_EMPTY));
+
+	if (email.length > 45) return res.send(basickResponse(baseResponse.EMAIL_LENGTH_ERROR));
+
+	if (!regEmail.test(email)) return res.send(basickResponse(baseResponse.EMAIL_ERROR_TYPE));
+
+	if (!password) return res.send(basickResponse(baseResponse.PASSWORD_EMPTY));
+
+	if (isKeep != 1) isKeep = 0;
+
+	const emailCheckResult = await userProvider.userEmailCheck(email, provider ?? "engineeo");
+
+	if (emailCheckResult.length === 0) return res.send(basickResponse(baseResponse.EMAIL_NOT_EXIST));
+	if (emailCheckResult[0].status == "Y") return res.send(basickResponse(baseResponse.EMAIL_INVALID));
+	if (emailCheckResult[0].status == "F") return res.send(basickResponse(baseResponse.SUSPENSION_ACCOUNT));
+	const signInResponse = await userService.loginUser(email, password, isKeep);
+	return res.send(signInResponse);
+});
+
+/**
+ * API No.
+ * API Name : 카카오 로그인 API
+ * [POST] /users/kakao-login
+ */
+
+// exports.kakaoLogin = asyncHandler(async function (req, res) {
+// 	try {
+// 		const baseUrl = "https://kauth.kakao.com/oauth/authorize";
+// 		const { REST_API_KEY, REDIRECT_URI } = require("../../config/secret");
+// 		const config = {
+// 			client_id: REST_API_KEY,
+// 			redirect_uri: REDIRECT_URI,
+// 			response_type: "code",
+// 		};
+// 		const params = new URLSearchParams(config).toString();
+
+// 		const url = `${baseUrl}?${params}`;
+
+// 		return res.redirect(url);
+// 	} catch (err) {
+// 		console.log(err);
+// 		logger.error(`App - kakaoLogin Controller error\n: ${err.message}`);
+// 		return basickResponse(baseResponse.SOCIAL_SIGNIN_FAIL);
+// 	}
+// };
+
+// 카카오 콜백 redirect_uri에 대해 처리하는 컨트롤러
+exports.kakaoLogin = asyncHandler(async function (req, res) {
+	// const { REST_API_KEY, REDIRECT_URI } = require("../../config/secret");
+	const { access_token } = req.body;
+
+	let isKeep;
+	let isRejoin = false;
+
+	const header = { "content-type": "application/x-www-form-urlencoded" };
+
+	// const baseUrl = "https://kauth.kakao.com/oauth/token";
+	// const config = {
+	// 	client_id: REST_API_KEY,
+	// 	// client_secret: process.env.KAKAO_SECRET,
+	// 	grant_type: "authorization_code",
+	// 	redirect_uri: REDIRECT_URI,
+	// 	code: code,
+	// };
+	// const params = new URLSearchParams(config).toString();
+	// let axiosVal = await axiosCall("POST", baseUrl, params, header);
+	// const { access_token: accessToken } = axiosVal;
+
+	// accessToken 만료시에는 refresh 토큰을 통해 갱신하는 과정 추가할 것 .
+	let kakao_profile = await userService.axiosCall("GET", "https://kapi.kakao.com/v2/user/me", "", {
+		Authorization: "Bearer " + access_token,
+	});
+
+	let provider = "kakao";
+
+	if (!kakao_profile || kakao_profile.code == -401) {
+		logger.error(`kakao api call error`);
+		throw new errorResponse(baseResponse.SOCIAL_SIGNIN_FAIL, 400);
+	}
+
+	let { kakao_account } = kakao_profile;
+
+	let email = kakao_account.email;
+
+	if (!email) {
+		console.log("카카오 이메일 없음");
+		console.log(kakao_account);
+		throw new errorResponse(baseResponse.KAKAO_EMAIL_EMPTY, 400);
+	}
+
+	const emailCheckResult = await userProvider.userEmailCheck(email, provider);
+
+	// 카카오계정 가입된 사용자가 아닌 경우
+	// let { name: userName, phone_number: phoneNum, birthyear, birthday } = kakao_account;
+	let userName = kakao_account.name || "";
+	let nickname = "";
+	let phoneNum = kakao_account.phone_number || "";
+	/**
+	 * phone_number from kakao_account : +82 10-1234-5678
+	 */
+	if (phoneNum) phoneNum = "0" + phoneNum.substr(3).replace(/-/g, "").trim();
+
+	let birthyear = kakao_account.birthyear || 0;
+	let birthday = kakao_account.birthday || 0;
+	let dateOfBirth = birthyear + birthday;
+	if (dateOfBirth == 0) dateOfBirth = "";
+
+	// 탈퇴한 회원의 재가입
+	if (emailCheckResult.length === 1 && emailCheckResult[0].status === "Y") {
+		isRejoin = true;
+		message = "탈퇴한 회원입니다. 다시 가입해주세요.";
+		return res.send({ email, userName, phoneNum, dateOfBirth, nickname, provider, isRejoin, message });
+	}
+
+	// 관리자에 의해 정지된 계정의 재가입
+	if (emailCheckResult.length === 1 && emailCheckResult[0].status === "F") {
+		message =
+			"관리자에 의해 정지된 계정입니다. 가입할 수 없습니다. 추가 문의는 채널톡을 통해 문의주시면 감사하겠습니다.";
+		return res.send({ message });
+	}
+
+	// 카카오계정 가입된 사용자인 경우
+	if (emailCheckResult.length === 1 && emailCheckResult[0].status !== "Y") {
+		let { userIdx } = emailCheckResult[0];
+		let kakaoLoginResult = await userService.kakaoLogin(userIdx, isKeep ?? 0);
+
+		return res.send(resultResponse(baseResponse.SIGN_IN_SUCCESS, kakaoLoginResult));
+	}
+
+	return res.send({ email, userName, phoneNum, dateOfBirth, nickname, provider, isRejoin });
+});
+
+/**
+ * API No.
+ * API Name : 카카오계정 회원가입 API
+ * [POST]] /users/kakao-sign-up
+ */
+exports.kakaoPostUser = asyncHandler(async function (req, res) {
+	const { email, phoneNum, userName, dateOfBirth, nickname, isRejoin } = req.body;
+
+	// Validation Check (Request Error)
+	if (!email) return res.send(basickResponse(baseResponse.EMAIL_EMPTY));
+
+	if (email.length > 45) return res.send(basickResponse(baseResponse.EMAIL_LENGTH_ERROR));
+
+	if (!regEmail.test(email)) return res.send(basickResponse(baseResponse.EMAIL_ERROR_TYPE));
+
+	if (!phoneNum) return res.send(basickResponse(baseResponse.PHONENUM_EMPTY));
+
+	if (!regPhoneNum.test(phoneNum)) return res.send(basickResponse(baseResponse.PHONENUM_ERROR_TYPE));
+
+	if (!userName) return res.send(basickResponse(baseResponse.USERNAME_EMPTY));
+
+	if (!regUserName.test(userName)) return res.send(basickResponse(baseResponse.USERNAME_ERROR_TYPE));
+
+	if (!dateOfBirth) return res.send(basickResponse(baseResponse.DATEOFBIRTH_EMPTY));
+
+	if (dateOfBirth.length != 8) return res.send(basickResponse(baseResponse.DATEOFBIRTH_LENGTH_ERROR));
+
+	if (!regBirthDate.test(dateOfBirth)) return res.send(basickResponse(baseResponse.DATEOFBIRTH_ERROR_TYPE));
+
+	if (!nickname) return res.send(basickResponse(baseResponse.NICKNAME_EMPTY));
+
+	if (nickname.length > 8 || nickname.length < 1) return res.send(basickResponse(baseResponse.NICKNAME_LENGTH_ERROR));
+
+	if (!regNickname.test(nickname)) return res.send(basickResponse(baseResponse.NICKNAME_ERROR_TYPE));
+
+	//provider는 소셜 로그인인지 웹 자체 로그인인지 판단하기 위한 변수
+	let provider = "kakao";
+	// Validation Check (Response Error)
+	const emailCheckResult = await userProvider.userEmailCheck(email, provider);
+	if (emailCheckResult.length === 1 && isRejoin == false) {
+		if (emailCheckResult[0].status === "Y") return res.send(basickResponse(baseResponse.EMAIL_INVALID));
+		return res.send(basickResponse(baseResponse.EMAIL_EXIST));
+	}
+
+	const phoneCheckResult = await userProvider.userPhoneNumCheck(phoneNum, provider);
+	for (let i = 0; i < phoneCheckResult.length; i++) {
+		if (phoneCheckResult[i].status !== "Y" && isRejoin == false)
+			return res.send(basickResponse(baseResponse.PHONENUM_EXIST));
+		if (phoneCheckResult[i].status === "Y") isRejoin = true;
+	}
+
+	const nicknameCheckResult = await userProvider.userNicknameCheck(nickname);
+	for (let i = 0; i < nicknameCheckResult.length; i++) {
+		if (nicknameCheckResult[i].status !== "Y") return res.send(basickResponse(baseResponse.NICKNAME_EXIST));
+	}
+
+	if (isRejoin == true) {
+		const userRejoinResult = await userService.userRejoin(email, nickname, provider);
+		return res.send(resultResponse(baseResponse.SIGN_UP_SUCCESS, userRejoinResult));
+	}
+
+	let password = "";
+	let recommendNickname = "";
+	const signInResponse = await userService.createUser(
+		email,
+		password,
+		phoneNum,
+		userName,
+		dateOfBirth,
+		nickname,
+		recommendNickname,
+		isRejoin,
+		provider,
+	);
+	await channelTalkService.updateChannelTalkUserProductInfo(signInResponse.userIdx);
+	return res.send(resultResponse(baseResponse.SIGN_UP_SUCCESS, signInResponse));
+});
+
+// 이건 필요없는 기능일 수도 있음 + 2023/02/06 현재 쓰지 않는 기능으로 수정x - 유정호
+exports.kakaoLogout = async function (req, res) {
+	const { accessToken } = req.body;
+	if (!accessToken) return res.send(basickResponse(baseResponse.TOKEN_EMPTY)); // 2052 : accessToken을 입력해주세요.
+	try {
+		let kakaoId;
+
+		try {
+			kakaoId = await axios.post("https://kapi.kakao.com/v1/user/logout", {
+				headers: {
+					Authorization: "Bearer " + accessToken,
+					"Content-Type": "application/x-www-form-urlencoded",
+				},
+			});
+		} catch (err) {
+			return res.send(kakaoId); // 2053 : 유효하지 않는 엑세스 토큰입니다.
+		}
+
+		// const name = data.profile.nickname;
+		// const email = data.email;gg
+
+		// const emailCheckResult = await userProvider.emailCheck(email);
+
+		res.send(kakaoId);
+	} catch (error) {
+		console.log(error);
+		return res.send(basickResponse(baseResponse.EXAM_EMPTY)); // 문구 수정
+	}
+};
+
+/**
+ * API No.
+ * API Name : 이메일 찾기 API
+ * [POST] /users/find/email
+ */
+exports.findEmail = asyncHandler(async function (req, res) {
+	const name = req.body.name;
+	const phoneNum = req.body.phoneNum;
+
+	if (!name) return res.send(basickResponse(baseResponse.NAME_EMPTY));
+
+	if (!regUserName.test(name)) return res.send(basickResponse(baseResponse.NAME_ERROR_TYPE));
+
+	if (!phoneNum) return res.send(basickResponse(baseResponse.PHONENUM_EMPTY));
+
+	if (!regPhoneNum.test(phoneNum)) return res.send(basickResponse(baseResponse.PHONENUM_ERROR_TYPE));
+
+	const findResult = await userProvider.userEmailByPhoneNum(name, phoneNum);
+	if (findResult.length == 0) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+
+	let userEmail;
+	for (let i = 0; i < findResult.length; i++) {
+		if (findResult[i].status !== "Y") {
+			userEmail = findResult[i].userEmail;
+			break;
+		}
+
+		if (i == findResult.length - 1) return res.send(basickResponse(baseResponse.USER_QUIT));
+	}
+
+	return res.send(resultResponse(baseResponse.SUCCESS, userEmail));
+});
+
+/**
+ * API No.
+ * API Name : 비밀번호 찾기 API
+ * [POST] /users/find/password
+ */
+//비밀번호 랜덤 함수
+async function createCode(objArr, iLength) {
+	let arr = objArr;
+	let randomStr = "";
+	for (let j = 0; j < iLength; j++) {
+		randomStr += arr[Math.floor(Math.random() * arr.length)];
+	}
+	return randomStr;
+}
+
+exports.findPassword = asyncHandler(async function (req, res) {
+	const email = req.body.email;
+	const phoneNum = req.body.phoneNum;
+	if (!email) return res.send(basickResponse(baseResponse.EMAIL_EMPTY)); // 문구 수정
+
+	if (!regexEmail.test(email)) return res.send(basickResponse(baseResponse.EMAIL_ERROR_TYPE)); // 문구 수정
+
+	if (email.length > 45) return res.send(basickResponse(baseResponse.EMAIL_LENGTH_ERROR)); // 문구 수정
+
+	const userInfoByEmail = await userProvider.userIdxByEmail(email); // 이메일 상태값으로 탈퇴한 계정인지 존재한 계정인지 확인
+
+	const userPhoneNumByEmailResult = await userProvider.userPhoneNumByEmail(email);
+	if (userPhoneNumByEmailResult.length == 0) {
+		return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+	}
+	if (userPhoneNumByEmailResult[0].userPhoneNum != phoneNum) {
+		return res.send(basickResponse(baseResponse.EMAIL_PHONENUM_NOT_MATCH));
+	}
+
+	// 엔지니오 계정인지 확인 -> 카카오계정은 비밀번호 없음
+	const isEngineeoUser = await userProvider.userEmailCheck(email, "engineeo");
+	if (!isEngineeoUser[0]) return res.send(basickResponse(baseResponse.ENGINEEO_EMAIL_NOT_EXIST));
+
+	//숫자 + 문자 + 특수문자 새로운 비밀번호 생성
+	const arr =
+		"0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z".split(
+			",",
+		);
+	const password = await createCode(arr, 6);
+
+	// 이메일 인증 시
+	/*
+	const emailOptions = {
+		from: "easyelectric0616@gmail.com",
+		to: email,
+		subject: 'EngineeO 임시 비밀번호 발급',
+		text: `임시 비밀번호는 ${password}입니다.`
+	};
+
+	await smtpTransport.sendMail(emailOptions, (err, response) => {
+		if (err) {
+			smtpTransport.close();
+			console.log(err);
+		    
+			return response.send(basickResponse(baseResponse.EMAIL_SEND_ERROR)); 
+		}
+		smtpTransport.close();
+		// 성공시 유저 테이블에 비밀번호를 넣는다.
+	})*/
+	const flag = await userService.updateUserPassword(password, userInfoByEmail[0].userIdx, 1, phoneNum);
+	//console.log(result);
+	if (flag == true) return res.send(basickResponse(baseResponse.FIND_PASSWORD_SUCCESS));
+	else return res.send(basickResponse(baseResponse.PASSWORD_FIND_ERROR));
+});
+
+/**
+ * API No.
+ * API Name : 소셜 계정 확인 API
+ * [GET] /users/:userIdx/social
+ */
+exports.checkUserSocial = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	const userPasswordCheckResult = await userProvider.userPasswordCheck(userIdx);
+
+	if (userPasswordCheckResult[0].userPassword === null) {
+		return res.send(resultResponse(baseResponse.SUCCESS, { isSocial: 1 }));
+	}
+
+	return res.send(resultResponse(baseResponse.SUCCESS, { isSocial: 0 }));
+});
+
+/**
+ * API No.
+ * API Name : 회원 정보 조회 API
+ * [POST] /users/:userIdx/profile
+ */
+exports.checkUserInfo = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	let { userPassword, provider } = req.body;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+
+	provider = provider ?? "engineeo";
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!userPassword && provider == "engineeo") return res.send(basickResponse(baseResponse.PASSWORD_EMPTY));
+
+	// if (!regPassword.test(userPassword))
+	//   return res.send(basickResponse(baseResponse.PASSWORD_ERROR_TYPE))
+
+	const userInfo = await userProvider.userInfo(userIdx);
+	if (userInfo.length == 0) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+	if (userInfo[0].status == "Y") return res.send(basickResponse(baseResponse.USER_QUIT));
+
+	if (provider == "engineeo") {
+		const hashedPassword = await crypto.createHash("sha512").update(userPassword).digest("hex");
+		//if (userInfo[0].userIdx != userIdx) return res.send(baseResponse.USER_NOT_MATCH, )); 다를리가 없음
+		if (userInfo[0].userPassword != hashedPassword) return res.send(basickResponse(baseResponse.PASSWORD_NOT_MATCH));
+	}
+
+	const result = {
+		name: userInfo[0].userName,
+		email: userInfo[0].userEmail,
+		phoneNum: userInfo[0].userPhoneNum,
+		dateOfBirth: userInfo[0].dateOfBirth,
+		nickname: userInfo[0].nickname,
+	};
+	return res.send(resultResponse(baseResponse.PASSWORD_CORRESPOND_SUCCESS, result));
+});
+
+/**
+ * API No.
+ * API Name : 회원 정보 조회 API
+ * [GET] /users/:userIdx/profile
+ */
+exports.checkUserInfoWithToken = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	const userInfo = await userProvider.userInfo(userIdx);
+	if (userInfo.length == 0) return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+	if (userInfo[0].status == "Y") return res.send(basickResponse(baseResponse.USER_QUIT));
+	let dateOfBirth = String(userInfo[0].dateOfBirth);
+	const date = userInfo[0].createdAt;
+	const year = dateOfBirth.substring(0, 4);
+	const month = dateOfBirth.substring(4, 6);
+	const day = dateOfBirth.substring(6, 8);
+	const today = new Date();
+	const birthDate = new Date(year, month, day);
+	const formatDate = date.getFullYear() + "." + (date.getMonth() + 1) + "." + date.getDate();
+	let age = today.getFullYear() - birthDate.getFullYear() + 1;
+
+	const userProductResult = await userProvider.userAllProductsName(userIdx);
+
+	const result = {
+		memberId: String(userInfo[0].userIdx),
+		name: userInfo[0].userName,
+		email: userInfo[0].userEmail,
+		mobileNumber: userInfo[0].userPhoneNum,
+		dateOfBirth: year + "." + month + "." + day,
+		age: age,
+		nickname: userInfo[0].nickname,
+		createdAt: formatDate,
+		provider: userInfo[0].provider,
+		validProduct: userProductResult.validProduct,
+		invalidProduct: userProductResult.invalidProduct,
+	};
+	return res.send(resultResponse(baseResponse.SUCCESS, result));
+});
+
+/**
+ * API No.
+ * API Name : 회원 정보 수정 API
+ * [PATCH] /users/:userIdx/profile
+ */
+exports.patchUserInfo = asyncHandler(async function (req, res) {
+	const { userPassword, userNickname } = req.body;
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	if (userNickname) {
+		const nicknameCheckResult = await userProvider.userNicknameCheck(userNickname);
+		for (let i = 0; i < nicknameCheckResult.length; i++) {
+			if (nicknameCheckResult[i].status !== "Y") {
+				if (nicknameCheckResult[i].userIdx == userIdx)
+					return res.send(basickResponse(baseResponse.NICKNAME_SAME_ERROR));
+				return res.send(basickResponse(baseResponse.NICKNAME_EXIST));
+			}
+		}
+	}
+	if (userPassword && userNickname) {
+		if (!regPassword.test(userPassword)) return res.send(basickResponse(baseResponse.PASSWORD_ERROR_TYPE));
+		if (!regNickname.test(userNickname)) return res.send(basickResponse(baseResponse.NICKNAME_ERROR_TYPE));
+
+		await userService.updateUserInfo(userPassword, userNickname, userIdx);
+		return res.send(basickResponse(baseResponse.USER_INFO_UPDATE_SUCCESS));
+	}
+
+	if (userPassword) {
+		if (!regPassword.test(userPassword)) return res.send(basickResponse(baseResponse.PASSWORD_ERROR_TYPE));
+		const updateUserPasswordResult = await userService.updateUserPassword(userPassword, userIdx, 0);
+
+		if (updateUserPasswordResult) return res.send(basickResponse(baseResponse.PASSWORD_UPDATE_SUCCESS));
+		else return res.send(basickResponse(baseResponse.PASSWORD_UPDATE_FAIL));
+	}
+
+	if (userNickname) {
+		if (!regNickname.test(userNickname)) return res.send(basickResponse(baseResponse.NICKNAME_ERROR_TYPE));
+
+		await userService.updateUserNickname(userNickname, userIdx);
+		return res.send(basickResponse(baseResponse.NICKNAME_UPDATE_SUCCESS));
+	}
+});
+
+/**
+ * API No.
+ * API Name : 회원탈퇴 API
+ * [PATCH] /users/:userIdx/withdrawal
+ */
+exports.dropUser = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	await userService.updateUserDrop(userIdx);
+	await channelTalkService.updateChannelTalkUserProductInfo(userIdx);
+	return res.send(basickResponse(baseResponse.USER_DROP_SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 본인인증 요청 API
+ * [POST] /users/auth
+ */
+exports.getSelfAuth = asyncHandler(async function (req, res) {
+	//const cache = require("../../config/cache");
+	// const redis = require("redis");
+
+	//const { ElastiCacheClient, AddTagsToResourceCommand } = require("@aws-sdk/client-elasticache");
+	//const client = new ElastiCacheClient({ region: "engineeo-server-cache.ddzusp.0001.apn2.cache.amazonaws.com:6379" });
+
+	const phoneNum = req.body.phoneNum;
+	if (!phoneNum) return res.send(basickResponse(baseResponse.PHONENUM_EMPTY));
+
+	if (!regPhoneNum.test(phoneNum)) return res.send(basickResponse(baseResponse.PHONENUM_ERROR_TYPE));
+
+	// const phoneCheckResult = await userProvider.userPhoneNumCheck(phoneNum);
+
+	// if (phoneCheckResult[0].exist === 1)
+	// return res.send(basickResponse(baseResponse.PHONENUM_EXIST));
+
+	// const cacheKey = text;
+
+	// cache.set(cacheKey, authNum);
+
+	// const authResult = { cacheKey: cacheKey };
+	sensResult = await userService.sendPhoneVerification(phoneNum);
+
+	return res.send(resultResponse(baseResponse.SUCCESS, sensResult));
+});
+
+/**
+ * API No.
+ * API Name : 휴대폰 인증 확인 API
+ * [POST] /users/auth/verify
+ */
+exports.confirmSelfAuth = asyncHandler(async function (req, res) {
+	//const cache = require("../../config/cache");
+	const { cacheKey, authNum } = req.body;
+
+	if (!cacheKey) return res.send(basickResponse(baseResponse.CACHEKEY_EMPTY));
+
+	if (!authNum) return res.send(basickResponse(baseResponse.AUTH_NUMBER_EMPTY));
+
+	// if (!cache.has(cacheKey)) return res.send(basickResponse(baseResponse.CACHEKEY_NOT_EXIST));
+
+	// if (authNum != cache.peek(cacheKey)) return res.send(basickResponse(baseResponse.AUTH_NUMBER_ERROR_TYPE));
+
+	//cache.del(cacheKey);
+
+	//cache.del(cacheKey);
+	await userService.checkPhoneVerification(cacheKey, authNum);
+	return res.send(resultResponse(baseResponse.SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 회원 오답노트 문제별 목록 조회 API
+ * [GET] /users/:userIdx/reviewNote?page={}&examSortName={}&order={}&bookMark={}&examField={}
+ */
+exports.userReviewNote = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	let page = Math.ceil((req.query.page - 1) * 16);
+	const examSortName = req.query.examSortName; // 0 : 전체, 1 : 자격증, 2 : 공기업 , 3 : 공무원, 141 : 대기업
+	const examField = req.query.examField; // 문자열로 "전기, "화학"을 입력 받는다. 기창
+	const bookMark = req.query.bookMark; // 0 : 전체, 1 : 북마크된 것
+	const order = req.query.order; // 0: 최신순, 1: 오래된 순
+	let condition = "AND es.examSortRef ";
+	let sort;
+	let examSortJoinType = `e.examSortIdx`;
+	if (examSortName == "전체") sort = 0;
+	else sort = await examProvider.getExamSortLargeIdx(examSortName);
+
+	// Validation Check (Request Error)
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (!page || page < 0) page = 0;
+	if (!regNumber.test(page)) return res.send(basickResponse(baseResponse.IMPOSSIBLE_VALUE_ERROR));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	let examFieldIdx;
+	if (examField) examFieldIdx = await examProvider.getExamField(sort, examField);
+
+	// 자격증
+	if (sort == "1") {
+		if (examField) {
+			condition += `= ${examFieldIdx} `;
+		} else {
+			const certExamSortArr = await examProvider.getExamSortSmallIdx("자격증");
+			condition += `IN (${certExamSortArr}) `;
+		}
+	}
+	// 공기업 & 대기업
+	else if (sort == "2" || sort == "145") {
+		if (examField) {
+			condition += `= ${examFieldIdx} `;
+		} else {
+			const companyField = await examProvider.getCompanyExamType(examSortName);
+			const companyArr = companyField["result"].map((v) => v.examSortIdx);
+			condition += `IN (${companyArr}) `;
+		}
+		examSortJoinType = `ce.examSortIdx_S`;
+	} else condition = "";
+
+	if (bookMark == "1") condition += "AND bookMark = 1 ";
+	if (order == "1") condition += "ORDER BY rn.createdAt asc, problemNum  ";
+	else condition += "ORDER BY rn.createdAt desc, problemNum  ";
+	const reviewNoteResult = await userProvider.reviewNote(userIdx, page, condition, examSortJoinType);
+	return res.send(resultResponse(baseResponse.SUCCESS, reviewNoteResult));
+});
+
+/**
+ * API No.
+ * API Name : 회원 실기 오답노트 문제별 목록 조회 API
+ * [GET] /api/users/practical/:userIdx/reviewNote?page={}&order={}&bookMark={}&examField={}
+ * 실기는 자격증밖에 존재하지 않는다
+ */
+exports.userSubjectiveReviewNote = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	let page = Math.ceil((req.query.page - 1) * 16);
+	let sort = 1; // 1은 자격증이라는 뜻이다.
+	let examField = req.query.examField; // 문자열로 "전기, "화학"을 입력 받는다. 기창
+	const bookMark = req.query.bookMark; // 0 : 전체, 1 : 북마크된 것
+	const order = req.query.order; // 0: 최신순, 1: 오래된 순
+	const isAuth = req.isAuth;
+
+	let condition = "AND es.examSortRef ";
+
+	// Validation Check (Request Error)
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (!page || page < 0) page = 0;
+	if (!regNumber.test(page)) return res.send(basickResponse(baseResponse.IMPOSSIBLE_VALUE_ERROR));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	if (examField) {
+		let examFieldIdx;
+		examFieldIdx = await examProvider.getExamField(sort, examField);
+		condition += `= ${examFieldIdx} `;
+	} else {
+		const certExamSortArr = await examProvider.getExamSortSmallIdx("자격증");
+		condition += `IN (${certExamSortArr}) `;
+	}
+
+	if (bookMark == "1") condition += "AND bookMark = 1 ";
+
+	if (order == "1") condition += "ORDER BY rn.createdAt asc, problemNum  ";
+	else condition += "ORDER BY rn.createdAt desc, problemNum  ";
+
+	const reviewNoteResult = await userProvider.subjectiveReviewNote(userIdx, page, condition, examField);
+
+	return res.send(resultResponse(baseResponse.SUCCESS, reviewNoteResult));
+});
+
+/**
+ * API No.
+ * API Name : 회원 오답노트 북마크 API
+ * [GET] /users/:userIdx/reviewNote/bookMark
+ */
+exports.userReviewNoteBookMark = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const reviewNoteIdx = req.body.reviewNoteIdx;
+	const bookMark = req.body.bookMark;
+
+	// Validation Check (Request Error)
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	await userService.reviewNoteBookMark(reviewNoteIdx, bookMark);
+	return res.send(basickResponse(baseResponse.SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 회원 오답노트 메모 API
+ * [GET] /users/:userIdx/reviewNote/:reviewNoteIdx/memo
+ */
+exports.userReviewNoteMemo = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const reviewNoteIdx = req.params.reviewNoteIdx;
+
+	// Validation Check (Request Error)
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	const reviewNoteMemoResult = await userProvider.reviewNoteMemo(reviewNoteIdx, userIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, reviewNoteMemoResult));
+});
+
+/**
+ * API No.
+ * API Name : 회원 오답노트 메모 API
+ * [PATCH] /users/:userIdx/reviewNote/memo
+ */
+exports.userReviewNoteMemoPatch = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const reviewNoteIdx = req.params.reviewNoteIdx;
+	const memo = req.body.memo;
+
+	// Validation Check (Request Error)
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	await userService.reviewNoteMemoPatch(reviewNoteIdx, userIdx, memo);
+	return res.send(basickResponse(baseResponse.SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 회원 오답노트 문제 조회 API
+ * [GET] /users/:userIdx/reviewNote/set?sort={}&order={}&bookMark={}
+ */
+exports.userReviewNoteSet = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examSortName = req.query.sort; // 0 : 전체, 1 : 자격증, 2 : 공기업 , 3 : 공무원
+	const bookMark = req.query.bookMark; // 0 : 전체, 1 : 북마크된 것
+	const order = req.query.order; // 0: 최신순, 1: 오래된 순
+	let examField = req.query.examField; // 문자열로 "전기, "화학"을 입력 받는다. 기창
+
+	let sort;
+	let isCert = `e.examSortIdx`;
+	if (examSortName == "전체") sort = 0;
+	else sort = await examProvider.getExamSortLargeIdx(examSortName);
+
+	let condition = "AND es.examSortRef ";
+
+	// Validation Check (Request Error)
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	if (examField == "전체") examField = "";
+	let examFieldIdx;
+	if (examField) examFieldIdx = await examProvider.getExamField(sort, examField);
+
+	// 자격증 -> 분야 있음
+	if (sort == 1) {
+		if (examField) condition += `= ${examFieldIdx} `;
+		else {
+			const certExamSortArr = await examProvider.getExamSortSmallIdx("자격증");
+			condition += `IN (${certExamSortArr}) `;
+		}
+	}
+	// 공기업 & 대기업 -> 분야 없음
+	else if (sort == 2 || sort == 141) {
+		const companyField = await examProvider.getCompanyExamType(examSortName);
+		let companyArr = [];
+		companyField["result"].map((v) => companyArr.push(v.examSortIdx));
+		condition += `IN (${companyArr}) `;
+		isCert = `ce.examSortIdx_S`;
+	} else condition = "";
+
+	if (bookMark == "1") condition += "AND bookMark = 1 ";
+
+	if (order == "1") condition += "ORDER BY rn.createdAt asc, problemNum ";
+	else condition += "ORDER BY rn.createdAt desc, problemNum ";
+	const reviewNoteSetResult = await userProvider.reviewNoteSet(userIdx, condition, isCert);
+
+	return res.send(resultResponse(baseResponse.SUCCESS, reviewNoteSetResult));
+});
+
+/**
+ * API No.
+ * API Name : 회원 주관식 오답노트 문제 조회 API
+ * [GET] /users/practical/:userIdx/reviewNote/set?sort={}&order={}&bookMark={}
+ */
+exports.userSubjectiveReviewNoteSet = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const sort = req.query.sort; // 0 : 전체, 1 : 자격증, 2 : 공기업 , 3 : 공무원
+	const bookMark = req.query.bookMark; // 0 : 전체, 1 : 북마크된 것
+	const order = req.query.order; // 0: 최신순, 1: 오래된 순
+	let page = (req.query.page - 1) * 15;
+
+	if (!page || page < 0) page = 0;
+
+	let condition = "AND es.examSortRef ";
+
+	// Validation Check (Request Error)
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	if (sort == "1") condition += "= 1 ";
+	else if (sort == "2") condition += "NOT IN (1, 3) ";
+	else if (sort == "3") condition += "= 3 ";
+	else condition = "";
+
+	if (bookMark == "1") condition += "AND bookMark = 1 ";
+
+	if (order == "1") condition += "ORDER BY rn.createdAt asc, problemNum ";
+	else condition += "ORDER BY rn.createdAt desc, problemNum ";
+
+	const reviewNoteSetResult = await userProvider.subjectiveReviewNoteSet(userIdx, condition);
+	return res.send(resultResponse(baseResponse.SUCCESS, reviewNoteSetResult));
+});
+
+/**
+ * API No.
+ * API Name : 회원 오답노트 회차별 목록 API
+ * [GET] /users/:userIdx/reviewNote/exam?examSort={}&examField={}
+ */
+exports.userReviewNoteExamDetail = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examSort = req.query.examSort;
+	const examField = req.query.examField;
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!examSort) return res.send(basickResponse(baseResponse.EXAMSORT_EMPTY));
+	await userProvider.userIdxCheck(userIdx);
+	const result = await userProvider.reviewNoteExamList(userIdx, examSort, examField);
+	return res.send(resultResponse(baseResponse.SUCCESS, result));
+});
+
+/**
+ * API No.
+ * API Name : 회원 주관식 오답노트 회차별 목록 API
+ * [GET] /users/practical/:userIdx/reviewNote/exam?examSort={}&examField={}
+ */
+exports.userSubjectiveReviewNoteEd = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examSort = req.query.examSort;
+	let examField = req.query.examField;
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!examSort) return res.send(basickResponse(baseResponse.EXAMSORT_EMPTY));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	const result = await userProvider.subjectiveReviewNoteExamList(userIdx, examSort, examField);
+	return res.send(resultResponse(baseResponse.SUCCESS, result));
+});
+
+/**
+ * API No.
+ * API Name : 회원 오답노트 시험 과목별 목록  조회 API
+ * [GET] /users/:userIdx/reviewNote/subject
+ * author : 김기창
+ */
+exports.userReviewNoteSubject = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examSort = req.query.examSort;
+	let examField = req.query.examField;
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!examSort) return res.send(basickResponse(baseResponse.EXAMSORT_EMPTY));
+
+	await userProvider.userIdxCheck(userIdx); //유저가 있는지 먼저 체크.
+
+	const result = await userProvider.reviewNoteSubjectList(userIdx, examSort, examField);
+	return res.send(resultResponse(baseResponse.SUCCESS, result));
+	//여기까지는 기존 코드와 같을 수밖에 없다.
+});
+
+/**
+ * API No.
+ * API Name : 회원 주관식 오답노트 시험 과목별 목록  조회 API
+ * [GET] /users/practical/:userIdx/reviewNote/subject
+ * author : 김기창
+ */
+exports.userSubjectiveReviewNoteSubject = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examSort = req.query.examSort;
+	let examField = req.query.examField;
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!examSort) return res.send(basickResponse(baseResponse.EXAMSORT_EMPTY));
+
+	await userProvider.userIdxCheck(userIdx); //유저가 있는지 먼저 체크.
+
+	const result = await userProvider.subjectiveReviewNoteSubjectList(userIdx, examSort, examField);
+	return res.send(resultResponse(baseResponse.SUCCESS, result));
+	//여기까지는 기존 코드와 같을 수밖에 없다.
+});
+
+/**
+ * API No.
+ * API Name : 회원 오답노트 시험 문제 조회 (회차) API
+ * [GET] /users/:userIdx/reviewNote/problem/:examDetailIdx
+ */
+exports.userReviewNoteRound = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examDetailIdx = req.params.examDetailIdx;
+	const isAuth = req.isAuth;
+
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!examDetailIdx) return res.send(basickResponse(baseResponse.EXAMDETAIL_EMPTY));
+
+	if (!regNumber.test(examDetailIdx)) return res.send(basickResponse(baseResponse.EXAMDETAIL_ERROR_TYPE));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	const examDetailCheckResult = await examProvider.examDetailCheck(examDetailIdx);
+
+	if (!isAuth && examDetailCheckResult[0].publicLevel == 2)
+		return res.send(basickResponse(baseResponse.USERAUTH_NOT_EXIST));
+
+	await examProvider.examCheck(examDetailCheckResult[0].examIdx);
+
+	const reviewNoteExamRoundResult = await userProvider.reviewNoteExamRound(userIdx, examDetailIdx, isAuth);
+	return res.send(resultResponse(baseResponse.SUCCESS, reviewNoteExamRoundResult));
+});
+
+/**
+ * API No.
+ * API Name : 회원 주관식 오답노트 시험 문제 조회 (회차) API
+ * [GET] /users/practical/:userIdx/reviewNote/problem/:examDetailIdx
+ */
+exports.userSubjectiveReviewNoteRound = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examDetailIdx = req.params.examDetailIdx;
+	const isAuth = req.isAuth;
+
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!examDetailIdx) return res.send(basickResponse(baseResponse.EXAMDETAIL_EMPTY));
+
+	if (!regNumber.test(examDetailIdx)) return res.send(basickResponse(baseResponse.EXAMDETAIL_ERROR_TYPE));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	const examDetailCheckResult = await examProvider.examDetailCheck(examDetailIdx);
+
+	if (!isAuth && examDetailCheckResult[0].publicLevel == 2)
+		return res.send(basickResponse(baseResponse.USERAUTH_NOT_EXIST));
+
+	await examProvider.examCheck(examDetailCheckResult[0].examIdx);
+
+	const result = await userProvider.subjectiveReviewNoteExamRound(userIdx, examDetailIdx, isAuth);
+	return res.send(resultResponse(baseResponse.SUCCESS, result));
+});
+
+/**
+ * API No.
+ * API Name : 회원 오답노트 시험 문제 조회 (과목별) API
+ * [GET] "/api/users/:userIdx/reviewNote/:examIdx/subject/problem/:examSubjectIdx",
+ 
+ */
+exports.userReviewNoteSubjectProblem = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examSubjectIdx = req.params.examSubjectIdx;
+	const examIdx = req.params.examIdx;
+	const isAuth = req.isAuth;
+	const CERTIFICATIONEXAM = "자격증";
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!examIdx) return res.send(basickResponse(baseResponse.EXAM_ERROR_TYPE));
+	if (!examSubjectIdx) return res.send(basickResponse(baseResponse.EXAMSUBJECT_NOT_EXIST));
+
+	if (!regNumber.test(examSubjectIdx)) return res.send(basickResponse(baseResponse.DATA_ERROR_TYPE));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	await examProvider.examSubjectCheck(examSubjectIdx);
+
+	const result = await userProvider.reviewNoteExamSubject(userIdx, examIdx, examSubjectIdx, isAuth);
+	return res.send(resultResponse(baseResponse.SUCCESS, result));
+});
+
+/**
+ * API No.
+ * API Name : 회원 주관식 오답노트 시험 문제 조회 (과목별) API
+ * [GET] "/api/users/practical/:userIdx/reviewNote/:examIdx/subject/problem/:examSubjectIdx",
+ 
+ */
+exports.userSubjectiveReviewNoteSubjectProblem = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examSubjectIdx = req.params.examSubjectIdx;
+	const examIdx = req.params.examIdx;
+	const isAuth = req.isAuth;
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!examIdx) return res.send(basickResponse(baseResponse.EXAM_ERROR_TYPE));
+	if (!examSubjectIdx) return res.send(basickResponse(baseResponse.EXAMSUBJECT_NOT_EXIST));
+
+	if (!regNumber.test(examSubjectIdx)) return res.send(basickResponse(baseResponse.DATA_ERROR_TYPE));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	await examProvider.examSubjectCheck(examSubjectIdx);
+
+	const reviewNoteExamSubjectResult = await userProvider.subjectiveReviewNoteExamSubject(
+		userIdx,
+		examIdx,
+		examSubjectIdx,
+		isAuth,
+	);
+	return res.send(resultResponse(baseResponse.SUCCESS, reviewNoteExamSubjectResult));
+});
+
+/**
+ * API No.
+ * API Name : 오답노트 삭제 API
+ * [PATCH]] /users/:userIdx/reviewNote
+ */
+exports.patchReviewNote = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const reviewNoteIdxList = req.body.reviewNoteIdxList;
+	//const multipleProblemIdx = req.params.multipleProblemIdx;
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!reviewNoteIdxList || reviewNoteIdxList.length === 0)
+		return res.send(basickResponse(baseResponse.PROBLEMIDX_EMPTY));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	await userService.updateUserReviewNote(reviewNoteIdxList);
+	return res.send(basickResponse(baseResponse.SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 오답노트 삭제 (회차) API
+ * [PATCH]] /users/:userIdx/reviewNote/problem/:examDetailIdx
+ */
+exports.patchReviewNoteRound = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examDetailIdx = req.params.examDetailIdx;
+	//const multipleProblemIdx = req.params.multipleProblemIdx;
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	//if (!multipleProblemIdx) return res.send(basickResponse(baseResponse.PROBLEMIDX_EMPTY));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	await userService.updateUserReviewNoteRound(userIdx, examDetailIdx);
+	return res.send(basickResponse(baseResponse.SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 오답노트 삭제 (과목) API
+ * [PATCH]] /users/:userIdx/reviewNote/problem/:examDetailIdx
+ */
+exports.patchReviewNoteSubject = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examIdx = req.body.examIdx;
+	const examSubjectIdx = req.body.examSubjectIdx;
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!examIdx) return res.send(basickResponse(baseResponse.EXAM_ERROR_TYPE));
+	if (!examSubjectIdx) return res.send(basickResponse(baseResponse.EXAMSUBJECT_EMPTY));
+	//if (!multipleProblemIdx) return res.send(basickResponse(baseResponse.PROBLEMIDX_EMPTY));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	await userService.updateUserReviewNoteSubject(userIdx, examIdx, examSubjectIdx);
+
+	return res.send(basickResponse(baseResponse.SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 유저 시험 기록 조회 API
+ * [GET] /users/:userIdx/examRecord
+ */
+exports.getUserExamRecord = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	const examInfoList = await userProvider.userExamRecordList(userIdx);
+	return res.send(examInfoList);
+});
+
+/**
+ * API No.
+ * API Name : 유저 시험 기록 삭제 API
+ * [Post] /api/users/:userIdx/examRecord/delete
+ * author : 김기창
+ */
+exports.deleteUserCbtExamRecord = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const userCbtExamRecordDeleteIdxList = req.body.userCbtExamRecordDeleteIdxList; // 이제 지우고자 하는 것들에 대한 index값들을 처리.
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	// Validation Check (Response Error)
+
+	await userProvider.userIdxCheck(userIdx);
+
+	if (!userCbtExamRecordDeleteIdxList.length) return res.send(basickResponse(baseResponse.ARRAY_LENGTH_ERROR));
+
+	//이부분에 userCbtExamRecordIdxList을 이용해서 미들웨어를 구체화하면 된다.
+	await userService.updateUserCbtExamDrop(userCbtExamRecordDeleteIdxList);
+	return res.send(basickResponse(baseResponse.SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 유저 시험 기록 채점결과 조회 API
+ * [GET] /users/:userIdx/examRecord/:examRecordIdx/grade/:examIdx
+ */
+exports.getUserExamRecordGrade = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examRecordIdx = req.params.examRecordIdx;
+	const examIdx = req.params.examIdx;
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!examRecordIdx) return res.send(basickResponse(baseResponse.EXAMRECORD_ERROR_TYPE));
+
+	if (!regNumber.test(examRecordIdx)) return res.send(basickResponse(baseResponse.EXAMRECORD_ERROR_TYPE));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	await userProvider.userExamRecordCheckIdx(examRecordIdx);
+
+	const examDivision = await examProvider.getExamDivision(examIdx, "L");
+	const useSubject = examDivision[0].examSortName == "자격증" ? 1 : 0;
+
+	let userExamRecordResult = await userProvider.userExamRecord(examRecordIdx, useSubject);
+	userExamRecordResult.result.examGrade.correctAnswers = userExamRecordResult.result.examGrade.score;
+
+	const userNicknameResult = await userProvider.selectUserNickname(userIdx);
+	userExamRecordResult.result.userInfo = userNicknameResult;
+	return res.send(userExamRecordResult);
+});
+
+/**
+ * API No.
+ * API Name : 유저 시험 기록 상세 조회 API
+ * [GET] /users/:userIdx/examRecord/:examRecordIdx/detail
+ */
+exports.getUserExamDetailRecord = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const examRecordIdx = req.params.examRecordIdx;
+	const isAuth = req.isAuth;
+	// Validation Check (Request Error)
+	// if (isAuth !== 1) return res.send(resultResponse(baseResponse.USERAUTH_NOT_EXIST, { isAuth: isAuth }));
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	if (!examRecordIdx) return res.send(basickResponse(baseResponse.EXAMRECORD_ERROR_TYPE));
+
+	if (!regNumber.test(examRecordIdx)) return res.send(basickResponse(baseResponse.EXAMRECORD_ERROR_TYPE));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	await userProvider.userExamRecordCheckIdx(examRecordIdx);
+
+	let result;
+
+	result = await userProvider.userExamDetailRecord(examRecordIdx, isAuth);
+	result["isAuth"] = isAuth;
+	// const userExamDetailRecordResult = await userProvider.userExamDetailRecord(userIdx, examRecordIdx);
+	// userExamDetailRecordResult["result"]["isAuth"] = isAuth;
+
+	return res.send(result);
+});
+
+/**
+ * API No.
+ * API Name : 유저 오류신고 조회 API
+ * [GET] /users/:userIdx/problemError
+ *
+ */
+exports.getUserReportProblem = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	// Validation Check (Response Error)
+
+	await userProvider.userIdxCheck(userIdx);
+
+	const userProblemErrorResult = await userProvider.userProblemError(userIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, userProblemErrorResult));
+});
+
+/**
+ * API No.
+ * API Name : 사이트 에러 신고 API
+ * [POST] /exams/problemError
+ */
+exports.postSiteError = asyncHandler(async function (req, res) {
+	const userIdx = req.verifiedToken.userIdx;
+	const title = req.body.title;
+	const content = req.body.content;
+
+	if (!userIdx) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!title) return res.send(basickResponse(baseResponse.TITLE_EMPTY));
+
+	if (!content) return res.send(basickResponse(baseResponse.CONTENT_EMPTY));
+
+	if (!regNumber.test(userIdx)) return res.send(basickResponse(baseResponse.USERNAME_ERROR_TYPE));
+
+	// if (!boardTitle) return res.send(basickResponse(baseResponse.BOARDTITLE_EMPTY));
+
+	// if (!boardContent) return res.send(basickResponse(baseResponse.BOARDCONTENT_EMPTY));
+
+	await userProvider.userIdxCheck(userIdx);
+
+	await userService.createSiteErrorReport(userIdx, title, content);
+	return res.send(basickResponse(baseResponse.SUCCESS));
+});
+
+/**
+ * API No.
+ * API Name : 유저 이름, 닉네임 조회 API
+ * [GET] /users/:userIdx/userInfo
+ *
+ */
+exports.getUserInfo = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	const userInfoResult = await userProvider.userInfo(userIdx);
+	if (userInfoResult.length > 0) {
+		return res.send(
+			resultResponse(baseResponse.SUCCESS, {
+				userNickname: userInfoResult[0].nickname,
+				userName: userInfoResult[0].userName,
+				provider: userInfoResult[0].provider,
+			}),
+		);
+	} else {
+		return res.send(basickResponse(baseResponse.USER_NOT_EXIST));
+	}
+});
+
+/**
+ * API No.
+ * API Name : 유저 장바구니 조회
+ * [GET] /users/:userIdx/cart
+ *
+ */
+exports.getUserCart = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+	const userCart = await userProvider.userCart(userIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, userCart));
+});
+
+/**
+ * API No.
+ * API Name : 유저 장바구니 추가
+ * [POST] /users/:userIdx/cart
+ *
+ */
+exports.postUserCart = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const productIdxList = req.body.productIdxList;
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	const postUserCartResult = await userService.updateUserCart(userIdx, productIdxList);
+	return res.send(resultResponse(baseResponse.SUCCESS, postUserCartResult));
+});
+
+/**
+ * API No.
+ * API Name : 유저 장바구니 삭제 (다중)
+ * [DELETE] /users/:userIdx/cart
+ *
+ */
+exports.deleteUserCart = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	const userCartIdxList = req.body.userCartIdxList;
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	const deleteUserCartResult = await userService.updateUserCartDrop(userCartIdxList);
+	return res.send(resultResponse(baseResponse.SUCCESS, deleteUserCartResult));
+});
+
+/**
+ * API No.
+ * API Name : 유저 거래 내역 조회
+ * [GET] /users/:userIdx/purchaseHistory
+ *
+ */
+exports.getUserPurchaseHistory = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	const userPurchaseHistory = await userProvider.userPurchaseHistory(userIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, userPurchaseHistory));
+});
+
+/**
+ * API No.
+ * API Name : 유저 거래 내역 상세 조회
+ * [GET] /users/purchaseHistory/detail
+ *
+ */
+// exports.getUserPurchaseHistoryDetail = asyncHandler(async function (req, res) {
+// 	const userIdx = req.verifiedToken.userIdx;
+// 	const orderNum = req.query.orderNum;
+
+// 	if (!userIdx) return res.send(baseResponse.USER_EMPTY, ));
+
+// 	if (!regNumber.test(userIdx)) return res.send(baseResponse.USER_ERROR_TYPE, ));
+
+// 	// Validation Check (Response Error)
+// await userProvider.userIdxCheck(userIdx)
+//
+//
+
+// 	if (!orderNum) return res.send(basickResponse(baseResponse.ORDERNUM_EMPTY));
+
+// 	const paymentCheckResult = await storeProvider.paymentIdxCheck(orderNum);
+// 	if (paymentCheckResult[0].exist === 0) return res.send(basickResponse(baseResponse.PAYMENT_NOT_EXIST));
+
+// 	const userPurchaseHistoryDetail = await userProvider.userPurchaseHistoryDetail(orderNum);
+// 	return res.send(userPurchaseHistoryDetail);
+// };
+
+/**
+ * API No.
+ * API Name : 유저 환불 내역 조회
+ * [GET] /users/refundHistory
+ *
+ */
+// exports.getUserRefundHistory = asyncHandler(async function (req, res) {
+// 	const userIdx = req.verifiedToken.userIdx;
+// 	if (!userIdx) return res.send(baseResponse.USER_EMPTY, ));
+
+// 	if (!regNumber.test(userIdx)) return res.send(baseResponse.USER_ERROR_TYPE, ));
+
+// 	// Validation Check (Response Error)
+// 	 await userProvider.userIdxCheck(userIdx)
+//
+//
+
+// 	const userPurchaseHistory = await userProvider.userRefundHistory(userIdx);
+// 	return res.send(userPurchaseHistory);
+// };
+
+/**
+ * API No.
+ * API Name : 유저 나의 시험 조회
+ * [GET] /users/:userIdx/product
+ *
+ */
+exports.getUserProduct = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+
+	// Validation Check (Response Error)
+	await userProvider.userIdxCheck(userIdx);
+
+	const userProduct = await userProvider.userProduct(userIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, userProduct));
+});
+
+/**
+ * API No.
+ * API Name : 유저 나의 포인트 조회
+ * [GET] /users/:userIdx/product
+ *
+ */
+exports.getUserPointLog = asyncHandler(async function (req, res) {
+	const userIdx = req.params.userIdx;
+	const userIdxFromJWT = req.verifiedToken.userIdx;
+	// Validation Check (Request Error)
+
+	if (!userIdx || !userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_EMPTY));
+
+	if (!regNumber.test(userIdx) || !regNumber.test(userIdxFromJWT))
+		return res.send(basickResponse(baseResponse.USER_ERROR_TYPE));
+
+	//관리자 권한이 있을 경우에는 모든포인트를 조회할 수 있도록 한다.
+	const adminIdxCheckResult = await adminProvider.adminCheck(userIdxFromJWT);
+
+	await userProvider.userIdxCheck(userIdx);
+
+	if (adminIdxCheckResult[0].exist === 1) {
+		const userPoint = await userProvider.userPointLogs(userIdx);
+		return res.send(resultResponse(baseResponse.SUCCESS, userPoint));
+	}
+
+	//관리자 권한이 없을 경우에는 userIdx와 userIdxFromJwt가 모두 일치해야만 확인이 가능하다.
+	if (userIdx != userIdxFromJWT) return res.send(basickResponse(baseResponse.USER_NOT_MATCH));
+	const userPoint = await userProvider.userPointLogs(userIdx);
+	return res.send(resultResponse(baseResponse.SUCCESS, userPoint));
+});
diff --git a/_old/src/User/userDao.js b/_old/src/User/userDao.js
new file mode 100644
index 0000000..a9bd04c
--- /dev/null
+++ b/_old/src/User/userDao.js
@@ -0,0 +1,1672 @@
+//  유저 인덱스 조회
+async function selectUserIdx(connection, userIdx) {
+	const seletUserIdxQuery = `
+     select userIdx, status from User where userIdx = ?;
+     `;
+	const [seletUserIdxRow] = await connection.query(seletUserIdxQuery, userIdx);
+	return seletUserIdxRow;
+}
+
+//  유저 인덱스 조회
+async function selectUserList(connection, userList) {
+	const selectUserListQuery =
+		`
+     select userIdx, status from User where userIdx in (` +
+		userList +
+		`);
+     `;
+	const [selectUserListRow] = await connection.query(selectUserListQuery, userList);
+	return selectUserListRow;
+}
+
+// 이메일 조회
+async function selectUserEmail(connection, userEmail, provider) {
+	const selectUserEmailQuery = `
+    select userIdx, userEmail,status from User where userEmail = ? and provider = ?;
+    `;
+	const [emailRows] = await connection.query(selectUserEmailQuery, [userEmail, provider]);
+	return emailRows;
+}
+
+// 유저 핸드폰 번호 확인
+async function selectUserPhoneNum(connection, userPhoneNum, provider) {
+	const selectUserPhoneNumQuery = `
+    select userPhoneNum, status from User where userPhoneNum = ? and status != 'Y' and provider = ?;
+     `;
+	const [selectUserPhoneNumRow] = await connection.query(selectUserPhoneNumQuery, [userPhoneNum, provider]);
+	return selectUserPhoneNumRow;
+}
+
+// 유저 카카오톡 토큰 확인
+async function checkUserKakaoToken(connection, userPhoneNum, kakaoToken) {
+	const checkUserKakaoTokenQuery = `
+    select userIdx, userPhoneNum, status from User where userPhoneNum = ? and kakaoId = ? and status != 'Y';
+     `;
+	const [checkUserKakaoTokenRow] = await connection.query(checkUserKakaoTokenQuery, [userPhoneNum, kakaoToken]);
+	return checkUserKakaoTokenRow;
+}
+
+// 유저 네이버 토큰 확인
+async function checkUserNaverToken(connection, userPhoneNum, naverId) {
+	const checkUserNaverTokenQuery = `
+    select userIdx, userPhoneNum, status from User
+    left join SocialAccount
+    where userPhoneNum = ? and snsName = 'naver' and status != 'Y';
+     `;
+	const [checkUserNaverTokenRow] = await connection.query(checkUserNaverTokenQuery, [userPhoneNum, naverId]);
+	return checkUserNaverTokenRow;
+}
+
+// 유저 비밀번호 확인 / userPassword controller에서 hashed된거랑 비교.
+async function selectUserPassword(connection, userIdx) {
+	const selectUserPasswordQuery = `
+    select userPassword from User where userIdx = ?;
+     `;
+	const [selectUserPasswordRow] = await connection.query(selectUserPasswordQuery, userIdx);
+	return selectUserPasswordRow;
+}
+
+// 유저 닉네임 확인
+async function selectUserNickname(connection, userNickname) {
+	const selectUserNicknameQuery = `
+    select userIdx, nickname, status from User where nickname = ?;
+     `;
+	const [selectUserNicknameRow] = await connection.query(selectUserNicknameQuery, userNickname);
+	return selectUserNicknameRow;
+}
+
+// 유저 오답노트 확인     slow query  reviewNoteIdx를 받아와서 조회하는 것으로 변경
+async function selectUserReviewNoteIdx(connection, multipleProblemIdx, userIdx) {
+	const selectUserReviewNoteIdxQuery = `
+    select reviewNoteIdx from ReviewNote where multipleProblemIdx = ? and userIdx = ? and  status = 'N';
+     `;
+	const [selectUserReviewNoteIdxRow] = await connection.query(selectUserReviewNoteIdxQuery, [
+		multipleProblemIdx,
+		userIdx,
+	]);
+	return selectUserReviewNoteIdxRow;
+}
+
+// 유저 시험기록 확인
+async function selectUserExamRecordIdx(connection, userExamRecordIdx) {
+	const selectUserExamRecordIdxQuery = `
+    select userExamRecordIdx, status from UserExamRecord where userExamRecordIdx = ?;
+     `;
+	const [selectUserExamRecordIdxRow] = await connection.query(selectUserExamRecordIdxQuery, userExamRecordIdx);
+	return selectUserExamRecordIdxRow;
+}
+
+// 이메일로 유저 인덱스 조회
+async function selectUserIdxByEmail(connection, userEmail) {
+	const seletUserIdxByEmailQuery = `
+    select userIdx, status
+    from User
+    where userEmail = ?;
+     `;
+	const [seletUserIdxByEmailRow] = await connection.query(seletUserIdxByEmailQuery, userEmail);
+	return seletUserIdxByEmailRow;
+}
+
+// 핸드폰 번호로 유저 인덱스 조회
+async function selectUserIdxByPhoneNum(connection, phoneNum) {
+	const selectUserIdxByPhoneNumQuery = `
+    select userIdx, status
+    from User
+    where userPhoneNum = ? and status != 'Y';
+     `;
+	const [selectUserIdxByPhoneNumRow] = await connection.query(selectUserIdxByPhoneNumQuery, phoneNum);
+	return selectUserIdxByPhoneNumRow;
+}
+
+// 유저 인덱스로 상태 조회
+async function selectUserStatus(connection, userIdx) {
+	const selectUserStatusQuery = `
+    select userIdx, status
+    from User
+    where userIdx = ?;
+     `;
+	const [selectUserStatusRow] = await connection.query(selectUserStatusQuery, userIdx);
+	return selectUserStatusRow;
+}
+
+// 유저 이메일 패스워드 체크
+async function selectLoginResult(connection, selectLoginResultParams) {
+	const selectLoginResultQuery = `
+          SELECT userIdx, userEmail, userPassword, status
+          FROM User
+          WHERE userEmail = ? AND userPassword = ?;`;
+	const [selectLoginResultRow] = await connection.query(selectLoginResultQuery, selectLoginResultParams);
+
+	return selectLoginResultRow;
+}
+
+// 유저 정보 조회
+async function selectUserInfo(connection, userIdx) {
+	const selectUserInfoQuery = `
+        SELECT userIdx ,userName, userEmail, userPassword, concat(0, userPhoneNum) as userPhoneNum, dateOfBirth, nickname, status, createdAt,recommendCount, accessLevel,point,provider
+        FROM User
+        WHERE userIdx = ?`;
+	const [selectUserInfoRow] = await connection.query(selectUserInfoQuery, userIdx);
+	return selectUserInfoRow;
+}
+
+// 유저 닉네임 조회
+async function selectUserNickname(connection, userIdx) {
+	const selectUserNicknameQuery = `
+        SELECT nickname,userName
+        FROM User
+        WHERE userIdx = ?`;
+	const [selectUserNicknameRow] = await connection.query(selectUserNicknameQuery, userIdx);
+	return selectUserNicknameRow;
+}
+
+// 유저 소셜 계정 연동 확인
+async function selectUserSocialLink(connection, userIdx, snsName) {
+	const selectUserSocialLinkQuery = `
+    select userIdx from SocialAccount where userIdx = ? and snsName = ?;
+     `;
+	const [selectUserSocialLinkRow] = await connection.query(selectUserSocialLinkQuery, [userIdx, snsName]);
+	return selectUserSocialLinkRow;
+}
+
+// 이메일 찾기
+async function selectUserEmailByPhone(connection, userName, userPhoneNum) {
+	const selectUserEmailByPhoneQuery = `
+        SELECT userEmail, status
+        FROM User
+        WHERE userName = ? AND userPhoneNum = ?`;
+	const [selectUserEmailByPhoneRow] = await connection.query(selectUserEmailByPhoneQuery, [userName, userPhoneNum]);
+	return selectUserEmailByPhoneRow;
+}
+
+// 비밀번호 찾기 인증
+async function selectPhoneNumByEmail(connection, userEmail) {
+	const selectPhoneNumByEmailQuery = `
+    SELECT userPhoneNum
+    FROM User
+    WHERE userEmail=?
+    `;
+	const [selectPhoneNumByEmailRow] = await connection.query(selectPhoneNumByEmailQuery, [userEmail]);
+	return selectPhoneNumByEmailRow;
+}
+
+// // 객관식 오답노트에 대해서 페이징하여 가져오기
+// async function selectUserReviewNotePageCert(connection, userIdx, page, condition) {
+// 	const selectUserReviewNotePageCertQuery =
+// 		`
+//     SELECT distinct reviewNoteIdx, e.subjectiveLabel,ed.examDetailIdx, examName, examDate, examRound, publicLevel,
+//     rn.multipleProblemIdx, problemNum, problem, isKatex, bookMark, e.examUrl as companyImage, domain,
+//     rn.createdAt, e.examSortIdx
+//     FROM ReviewNote rn
+//     JOIN MultipleProblem mp ON mp.multipleProblemIdx = rn.multipleProblemIdx
+//     LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+//     LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx AND ed.status = 'N'
+//     LEFT JOIN Exam e ON e.examIdx = ed.examIdx AND e.status = 'N'
+//     LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+//     LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx AND es.status = 'N'
+//     LEFT JOIN ExamSortImage esi ON esi.examSortIdx = es.examSortIdx
+//     WHERE rn.userIdx = ? and rn.status = 'N' and rn.subjectiveProblemIdx is null ` +
+// 		condition +
+// 		`LIMIT ?, 16`;
+
+// 	const [selectUserReviewNotePageCertRow] = await connection.query(selectUserReviewNotePageCertQuery, [userIdx, page]);
+// 	return selectUserReviewNotePageCertRow;
+// }
+
+// 객관식 오답노트에 대해서 페이징하여 가져오기
+async function selectUserReviewNotePage(connection, userIdx, page, condition, examSortJoinType) {
+	const selectUserReviewNotePageQuery =
+		`
+    select distinct reviewNoteIdx, ed.examDetailIdx, e.examName, ed.examDate, ed.examRound, ed.publicLevel, year(ed.examDate) as examYear,
+    rn.multipleProblemIdx, pe.problemNum, mp.problem, mp.isKatex, rn.bookMark, e.examUrl, ce.companyUrl, rn.createdAt, e.examSortIdx
+    from ReviewNote rn
+    left join MultipleProblem mp on mp.multipleProblemIdx = rn.multipleProblemIdx
+    left join ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+    left join ExamDetail ed on ed.examDetailIdx = pe.examDetailIdx and ed.status = 'N'
+    left join Exam e on e.examIdx = ed.examIdx and e.status = 'N'
+    left join CompanyExam ce on ce.examIdx = e.examIdx
+    left join ExamSort es on es.examSortIdx = ${examSortJoinType}
+    where rn.userIdx = ? and rn.status = 'N' and rn.subjectiveProblemIdx is null ` +
+		condition +
+		`LIMIT ?, 16`;
+
+	const [selectUserReviewNotePageRow] = await connection.query(selectUserReviewNotePageQuery, [userIdx, page]);
+	return selectUserReviewNotePageRow;
+}
+
+// 실긴 오답노트에 대해서 페이징하여 가져오기
+async function selectUserSubjectiveReviewNotePage(connection, userIdx, page, condition) {
+	const selectUserSubjectiveReviewNotePageQuery =
+		`
+    SELECT distinct reviewNoteIdx, e.subjectiveLabel,ed.examDetailIdx, examName, examDate, examRound, publicLevel, year(examDate) as examYear,
+    rn.subjectiveProblemIdx,rn.subjectiveProblemIdx, problemNum, problem, isKatex, bookMark, examUrl as companyImage, 
+    rn.createdAt, e.examSortIdx
+    FROM ReviewNote rn
+     JOIN SubjectiveProblem sp ON sp.subjectiveProblemIdx = rn.subjectiveProblemIdx
+    LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+	LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx AND ed.status = 'N'
+    LEFT JOIN Exam e ON e.examIdx = ed.examIdx AND e.status = 'N'
+    LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx AND es.status = 'N'
+    WHERE rn.userIdx = ? and rn.status = 'N' and rn.subjectiveProblemIdx is not null ` +
+		condition +
+		`LIMIT ?, 16`;
+
+	const [selectUserSubjectiveReviewNotePageRow] = await connection.query(selectUserSubjectiveReviewNotePageQuery, [
+		userIdx,
+		page,
+	]);
+	return selectUserSubjectiveReviewNotePageRow;
+}
+
+// 오답노트 문제 전부 가져오기
+async function selectUserReviewNoteAllProblem(connection, userIdx, condition, isCert) {
+	const selectUserReviewNoteAllQuery =
+		`
+    SELECT distinct rn.reviewNoteIdx, mp.multipleProblemIdx as problemIdx,mp.examHistory, publicLevel, date_format(examDate, '%Y.%m.%d.') as 'date', rn.bookMark, 
+    case when rn.memo is null then 0 else 1 end as isMemo,    
+    ed.examDetailIdx, e.examIdx, ed.examDetailIdx, examName, examDate, year(examDate) as examYear, examRound,  problemNum, problemScore, isKatex, expireAt, e.examSortIdx,
+    case 
+      when problemImage is null then problem
+      when problemImage = ' ' then problem
+      when problemImage = '' then problem
+      else
+      concat(problem, '\\\\\\\\ ', problemImage)  end as problem,
+    provisionNum, questionNum as 'answerNum', solution,
+      concat('제', examSubjectNum,'과목 ', examSubjectName) as subject, examSubjectName, ess.examSubjectIdx, problemUrl, lectureUrl, mp.isDelete
+    FROM MultipleProblem mp
+    LEFT JOIN Question q ON q.multipleProblemIdx = mp.multipleProblemIdx AND q.isAnswer = 1 AND q.status = 'N'
+    LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+    LEFT JOIN ReviewNote rn ON rn.multipleProblemIdx = pe.multipleProblemIdx AND rn.status = 'N'
+    LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx AND ed.status = 'N'
+    LEFT JOIN Exam e ON e.examIdx = ed.examIdx AND e.status = 'N'
+    LEFT JOIN CompanyExam ce on ce.examIdx = e.examIdx
+    LEFT JOIN ExamSort es ON es.examSortIdx = ${isCert} AND es.status = 'N'
+    LEFT JOIN ProblemSubject ps ON ps.multipleProblemIdx = mp.multipleProblemIdx
+    LEFT JOIN ExamSubject ess ON ps.examSubjectIdx = ess.examSubjectIdx
+	LEFT JOIN ExamSubjectMulti esm ON esm.examSubjectIdx = ess.examSubjectIdx and ed.examIdx = esm.examIdx
+    LEFT JOIN (
+    SELECT ped.examDetailIdx, ua.expireAt
+    FROM UserAuth ua
+    JOIN Product p on p.productIdx = ua.productIdx and p.status = 'N'
+    JOIN ProductExamDetail ped on ped.productIdx = p.productIdx
+    WHERE ua.userIdx = ? and ua.status = 'N'
+    group by ped.examDetailIdx
+    )tmp on tmp.examDetailIdx = ed.examDetailIdx
+    WHERE rn.userIdx = ?  ` + condition;
+
+	const [selectUserReviewNoteAllRow] = await connection.query(selectUserReviewNoteAllQuery, [userIdx, userIdx]);
+	return selectUserReviewNoteAllRow;
+}
+
+// 주관식 오답노트 문제 전부 가져오기
+async function selectUserSubjectiveReviewNoteAllProblem(connection, userIdx, condition) {
+	const selectUserReviewNoteAllQuery =
+		`
+    SELECT distinct rn.reviewNoteIdx, e.subjectiveLabel,sp.subjectiveProblemIdx as problemIdx, sp.examHistory,publicLevel, date_format(examDate, '%Y.%m.%d.') as 'date', rn.bookMark, 
+    case when rn.memo is null then 0 else 1 end as isMemo,    
+    ed.examDetailIdx, e.examIdx, ed.examDetailIdx, examName, examDate, examRound,  problemNum, problemScore, isKatex, expireAt, e.examSortIdx,
+    case 
+      when problemImage is null then problem
+      when problemImage = ' ' then problem
+      when problemImage = '' then problem
+      else
+      concat(problem, '\\\\\\\\ ', problemImage)  end as problem, solution,
+      concat('제', examSubjectNum,'과목 ', examSubjectName) as subject, examSubjectName, ess.examSubjectIdx, problemUrl, lectureUrl, sp.isDelete
+    FROM SubjectiveProblem sp
+    LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+    LEFT JOIN ReviewNote rn ON rn.subjectiveProblemIdx = spe.subjectiveProblemIdx AND rn.status = 'N'
+    LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx AND ed.status = 'N'
+    LEFT JOIN Exam e ON e.examIdx = ed.examIdx AND e.status = 'N'
+    LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx AND es.status = 'N'
+    LEFT JOIN CompanyExam ce on ce.examSortIdx_S = es.examSortIdx
+    LEFT JOIN SubjectiveProblemSubject sps ON sps.subjectiveProblemIdx = sp.subjectiveProblemIdx
+    LEFT JOIN ExamSubject ess ON sps.examSubjectIdx = ess.examSubjectIdx
+	LEFT JOIN ExamSubjectMulti esm ON esm.examSubjectIdx = ess.examSubjectIdx and ed.examIdx = esm.examIdx
+    LEFT JOIN (
+    SELECT ped.examDetailIdx, ua.expireAt
+    FROM UserAuth ua
+    JOIN Product p on p.productIdx = ua.productIdx and p.status = 'N'
+    JOIN ProductExamDetail ped on ped.productIdx = p.productIdx
+    WHERE ua.userIdx = ? and ua.status = 'N'
+    group by ped.examDetailIdx
+    )tmp on tmp.examDetailIdx = ed.examDetailIdx
+    WHERE rn.userIdx = ?  ` + condition;
+
+	const [selectUserReviewNoteAllRow] = await connection.query(selectUserReviewNoteAllQuery, [userIdx, userIdx]);
+	return selectUserReviewNoteAllRow;
+}
+
+// 유저의 필기 오답노트의 총 문제수
+async function selectUserReviewNoteCount(connection, userIdx, condition, examSortJoinType) {
+	const selectUserReviewNoteCountQuery =
+		`
+    SELECT COUNT(*) as total
+    FROM MultipleProblem mp
+    LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+    LEFT JOIN ReviewNote rn ON rn.multipleProblemIdx = pe.multipleProblemIdx AND rn.status = 'N'
+    LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx AND ed.status = 'N'
+    LEFT JOIN Exam e ON e.examIdx = ed.examIdx AND e.status = 'N'
+    LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+    LEFT JOIN ExamSort es ON es.examSortIdx = ${examSortJoinType} AND es.status = 'N'
+    WHERE mp.status = 'N' and rn.userIdx = ? ` + condition;
+
+	const [selectUserReviewNoteCountRow] = await connection.query(selectUserReviewNoteCountQuery, [userIdx]);
+	return selectUserReviewNoteCountRow;
+}
+
+// 유저의 실기 오답노트의 총 문제수
+async function selectUserSubjectiveReviewNoteCount(connection, userIdx, condition) {
+	const selectUserSubjectiveReviewNoteCountQuery =
+		`
+    SELECT COUNT(*) as total
+    FROM SubjectiveProblem sp
+    LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+    LEFT JOIN ReviewNote rn ON rn.subjectiveProblemIdx = spe.subjectiveProblemIdx AND rn.status = 'N'
+    LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx AND ed.status = 'N'
+    LEFT JOIN Exam e ON e.examIdx = ed.examIdx AND e.status = 'N'
+    LEFT JOIN ExamSort es ON es.examSortIdx = e.examSortIdx AND es.status = 'N'
+    WHERE sp.status = 'N' and rn.userIdx = ? ` + condition;
+
+	const [selectUserSubjectiveReviewNoteCountRow] = await connection.query(selectUserSubjectiveReviewNoteCountQuery, [
+		userIdx,
+	]);
+	return selectUserSubjectiveReviewNoteCountRow;
+}
+// 유저 오답노트 시험 조회  slow query
+async function selectUserReviewNoteExam(connection, examSort, userIdx, examField, largeExamIdx, examSortJoinType) {
+	//examField가 있을 경우엔 쿼리를 다르게 한다.
+	const selectUserReviewNoteExamFieldQuery = `
+  with recursive CTE AS (SELECT 
+    examSortIdx
+    FROM ExamSort 
+    WHERE examSortType = 'M' and  examSortName = ? and examSortRef = ? 
+    UNION ALL
+    SELECT
+    a.examSortIdx
+    FROM ExamSort a
+    INNER JOIN CTE b ON a.examSortRef = b.examSortIdx 
+    ) 
+     SELECT distinct e.examIdx, examName, e.examUrl, ce.companyUrl, e.examSortIdx
+     FROM ReviewNote rn
+     JOIN MultipleProblem mp ON rn.multipleProblemIdx = mp.multipleProblemIdx
+     JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+     JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx
+     LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+     LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+     JOIN CTE cte ON cte.examSortIdx = ${examSortJoinType}
+     WHERE userIdx = ? AND rn.status = 'N';
+  `;
+
+	const selectUserReviewNoteExamQuery = `
+  with recursive CTE AS (SELECT 
+    examSortIdx
+    FROM ExamSort
+    WHERE examSortType = 'L' and  examSortName = ?
+    UNION ALL
+    SELECT
+    a.examSortIdx
+    FROM ExamSort a
+    INNER JOIN CTE b ON a.examSortRef = b.examSortIdx 
+    ) 
+    SELECT distinct e.examIdx, examName, e.examUrl, ce.companyUrl, e.examSortIdx
+    FROM ReviewNote rn
+    JOIN MultipleProblem mp ON rn.multipleProblemIdx = mp.multipleProblemIdx
+    JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+    JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx
+    JOIN Exam e ON e.examIdx = ed.examIdx 
+    LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+    JOIN CTE cte ON cte.examSortIdx = ${examSortJoinType}
+    WHERE userIdx = ? AND rn.status = 'N';
+  `;
+	const [selectUserReviewNoteExamRow] = examField
+		? await connection.query(selectUserReviewNoteExamFieldQuery, [examField, largeExamIdx, userIdx])
+		: await connection.query(selectUserReviewNoteExamQuery, [examSort, userIdx]);
+	return selectUserReviewNoteExamRow;
+}
+// 유저 주관식 오답노트 시험 조회  slow query
+async function selectSubjectiveUserReviewNoteExam(connection, examSort, userIdx, examField, largeExamIdx) {
+	//examField가 있을 경우엔 쿼리를 다르게 한다.
+	const selectUserSubjectiveReviewNoteExamFieldQuery = `
+  with recursive CTE AS (SELECT 
+    examSortIdx
+    FROM ExamSort 
+    WHERE examSortType = 'M' and  examSortName = ? and examSortRef = ? 
+    UNION ALL
+    SELECT
+    a.examSortIdx
+    FROM ExamSort a
+    INNER JOIN CTE b ON a.examSortRef = b.examSortIdx 
+    ) 
+     SELECT distinct e.examIdx, examName, e.examUrl as companyImage
+     FROM ReviewNote rn
+     JOIN SubjectiveProblem sp ON rn.subjectiveProblemIdx = sp.subjectiveProblemIdx
+     JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+     JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx
+     JOIN Exam e ON e.examIdx = ed.examIdx
+     JOIN CTE cte ON cte.examSortIdx = e.examSortIdx
+     WHERE userIdx = ? AND rn.status = 'N';
+  `;
+
+	const selectUserSubjectiveReviewNoteExamQuery = `
+  with recursive CTE AS (SELECT 
+    examSortIdx
+    FROM ExamSort 
+    WHERE examSortType = 'L' and  examSortName = ?
+    UNION ALL
+    SELECT
+    a.examSortIdx
+    FROM ExamSort a
+    INNER JOIN CTE b ON a.examSortRef = b.examSortIdx 
+    ) 
+     SELECT distinct e.examIdx, examName, e.examUrl as companyImage
+     FROM ReviewNote rn
+     JOIN SubjectiveProblem sp ON rn.subjectiveProblemIdx = sp.subjectiveProblemIdx
+     JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+     JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx
+     JOIN Exam e ON e.examIdx = ed.examIdx
+     JOIN CTE cte ON cte.examSortIdx = e.examSortIdx
+     LEFT JOIN ExamSortImage esi ON esi.examSortIdx = cte.examSortIdx
+     WHERE userIdx = ? AND rn.status = 'N';
+        `;
+	const [selectUserSubjectiveReviewNoteExamRow] = examField
+		? await connection.query(selectUserSubjectiveReviewNoteExamFieldQuery, [examField, largeExamIdx, userIdx])
+		: await connection.query(selectUserSubjectiveReviewNoteExamQuery, [examSort, userIdx]);
+	return selectUserSubjectiveReviewNoteExamRow;
+}
+
+// 유저 오답노트 목록 조회
+async function selectUserReviewNoteList(connection, userIdx, examIdx) {
+	const selectUserReviewNoteListQuery = `
+  SELECT distinct ed.examDetailIdx, count(rn.reviewNoteIdx) as 'count',
+  case when ce.examIdx is null then concat(date_format(examDate, '%Y'), '년 ', examRound, '회') else concat(date_format(examDate, '%Y'), '년 ', examRound)  end as 'round'
+          FROM ReviewNote rn
+          LEFT JOIN MultipleProblem mp ON rn.multipleProblemIdx = mp.multipleProblemIdx
+          LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+          LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx
+          LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+          LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+        WHERE userIdx = ? AND ed.examIdx = ? AND rn.status = 'N'
+        GROUP BY ed.examDetailIdx
+        ORDER BY examDate DESC, examRound
+        `;
+	const [selectUserReviewNoteListRow] = await connection.query(selectUserReviewNoteListQuery, [userIdx, examIdx]);
+	return selectUserReviewNoteListRow;
+}
+
+// 유저 주관식 오답노트 목록 조회
+async function selectUserSubjectiveReviewNoteList(connection, userIdx, examIdx) {
+	const selectUserReviewNoteListQuery = `
+  SELECT distinct ed.examDetailIdx, count(rn.reviewNoteIdx) as 'count',
+  case when ce.examIdx is null then concat(date_format(examDate, '%Y'), '년 ', examRound, '회차') else concat(date_format(examDate, '%Y'), '년 ', examRound)  end as 'round'
+          FROM ReviewNote rn
+          LEFT JOIN SubjectiveProblem sp ON rn.subjectiveProblemIdx = sp.subjectiveProblemIdx
+          LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+          LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx
+          LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+          LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+        WHERE userIdx = ? AND ed.examIdx = ? AND rn.status = 'N' 
+        GROUP BY ed.examDetailIdx
+        ORDER BY examDate DESC, examRound
+        `;
+	const [selectUserReviewNoteListRow] = await connection.query(selectUserReviewNoteListQuery, [userIdx, examIdx]);
+	return selectUserReviewNoteListRow;
+}
+
+// 유저 오답노트 목록 조회 과목별.
+async function selectUserReviewNoteSubjectList(connection, userIdx, examIdx) {
+	//이 쿼리를 통해서 userIdx와 examIdx를 만족하는 과목별 리스트를 뽑아오면 된다.
+	//지금 과목별로 정렬하기 위해 테이블에 대한 순서를 정리한것.
+	//총 6가지의 테이블을 조인하여 examSubjectIdx와 examSubjectName에 대해서 추출해낸다.
+	//examSubjectIdxs와 examSubjectName은 ExamSubject 테이블에서 가져오고
+	//ReviewNote에 대한 left 조인이기 때문에 reviewNote에 대한 카운터로 가져올 수 있도록 한다.
+	const selectUserReviewNoteSubjectListQuery = `
+  select distinct es.examSubjectIdx,es.examSubjectName,concat(esm.examSubjectNum, '과목') as subjectNum,mp.multipleProblemIdx,ed.examDetailIdx
+          FROM ReviewNote rn
+          LEFT JOIN MultipleProblem mp ON rn.multipleProblemIdx = mp.multipleProblemIdx
+          LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+          LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx
+          LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+          left join ProblemSubject ps on ps.multipleProblemIdx = mp.multipleProblemIdx
+          left join ExamSubject es on es.examSubjectIdx = ps.examSubjectIdx
+          left join ExamSubjectMulti esm on esm.examSubjectIdx = es.examSubjectIdx and ed.examIdx = esm.examIdx
+          LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+          WHERE userIdx = ? AND e.examIdx = ? AND rn.status = 'N'
+          GROUP BY mp.multipleProblemIdx
+          order by e.examIdx,esm.examSubjectNum,es.examSubjectIdx
+          `;
+	const [selectUserReviewNoteSubjectListRow] = await connection.query(selectUserReviewNoteSubjectListQuery, [
+		userIdx,
+		examIdx,
+	]);
+	return selectUserReviewNoteSubjectListRow;
+}
+// 유저 주관시 오답노트 목록 조회 과목별.
+async function selectUserSubjectiveReviewNoteSubjectList(connection, userIdx, examIdx) {
+	//이 쿼리를 통해서 userIdx와 examIdx를 만족하는 과목별 리스트를 뽑아오면 된다.
+	//지금 과목별로 정렬하기 위해 테이블에 대한 순서를 정리한것.
+	//총 6가지의 테이블을 조인하여 examSubjectIdx와 examSubjectName에 대해서 추출해낸다.
+	//examSubjectIdxs와 examSubjectName은 ExamSubject 테이블에서 가져오고
+	//ReviewNote에 대한 left 조인이기 때문에 reviewNote에 대한 카운터로 가져올 수 있도록 한다.
+	const selectUserReviewNoteSubjectListQuery = `
+  select distinct es.examSubjectIdx,es.examSubjectName,concat(esm.examSubjectNum, '과목') as subjectNum,sp.subjectiveProblemIdx,ed.examDetailIdx
+          FROM ReviewNote rn
+          LEFT JOIN SubjectiveProblem sp ON rn.subjectiveProblemIdx = sp.subjectiveProblemIdx
+          LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+          LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx
+          LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+          left join SubjectiveProblemSubject sps on sps.subjectiveProblemIdx = sp.subjectiveProblemIdx
+          left join ExamSubject es on es.examSubjectIdx = sps.examSubjectIdx
+          left join ExamSubjectMulti esm on esm.examSubjectIdx = es.examSubjectIdx and ed.examIdx = esm.examIdx
+          LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+          WHERE userIdx = ? AND e.examIdx = ? AND rn.status = 'N'
+          GROUP BY sp.subjectiveProblemIdx
+          order by e.examIdx,esm.examSubjectNum,es.examSubjectIdx
+          `;
+	const [selectUserReviewNoteSubjectListRow] = await connection.query(selectUserReviewNoteSubjectListQuery, [
+		userIdx,
+		examIdx,
+	]);
+	return selectUserReviewNoteSubjectListRow;
+}
+
+//유저 오답노트 과목별 문제 갯수
+async function selectUserReviewNoteSubjectCount(connection, userIdx, examIdx) {
+	const selectUserReviewNoteSubjectCountQuery = `
+  select distinct es.examSubjectIdx,es.examSubjectName,concat(esm.examSubjectNum, '과목') as subjectNum,count(es.examSubjectName) as count
+  FROM ReviewNote rn
+  LEFT JOIN MultipleProblem mp ON rn.multipleProblemIdx = mp.multipleProblemIdx
+  LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+  LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx
+  LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+  left join ProblemSubject ps on ps.multipleProblemIdx = mp.multipleProblemIdx
+  left join ExamSubject es on es.examSubjectIdx = ps.examSubjectIdx
+  left join ExamSubjectMulti esm on esm.examSubjectIdx = es.examSubjectIdx and ed.examIdx = esm.examIdx
+  LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+    WHERE userIdx = ? AND e.examIdx = ? AND rn.status = 'N'
+    GROUP BY es.examSubjectName
+    order by e.examIdx,esm.examSubjectNum,es.examSubjectIdx
+  `;
+	const [selectUserReviewNoteSubjectCountRow] = await connection.query(selectUserReviewNoteSubjectCountQuery, [
+		userIdx,
+		examIdx,
+	]);
+	return selectUserReviewNoteSubjectCountRow;
+}
+
+//유저 주관식 오답노트 과목별 문제 갯수
+async function selectUserSubjectiveReviewNoteSubjectCount(connection, userIdx, examIdx) {
+	const selectUserReviewNoteSubjectCountQuery = `
+  select distinct es.examSubjectIdx,es.examSubjectName,concat(esm.examSubjectNum, '과목') as subjectNum,count(es.examSubjectName) as count
+  FROM ReviewNote rn
+  LEFT JOIN SubjectiveProblem sp ON rn.subjectiveProblemIdx = sp.subjectiveProblemIdx
+  LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+  LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx
+  LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+  left join SubjectiveProblemSubject sps on sps.subjectiveProblemIdx = sp.subjectiveProblemIdx
+  left join ExamSubject es on es.examSubjectIdx = sps.examSubjectIdx
+  left join ExamSubjectMulti esm on esm.examSubjectIdx = es.examSubjectIdx and ed.examIdx = esm.examIdx
+  LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+    WHERE userIdx = ? AND e.examIdx = ? AND rn.status = 'N'
+    GROUP BY es.examSubjectName
+    order by e.examIdx,esm.examSubjectNum,es.examSubjectIdx
+  `;
+	const [selectUserReviewNoteSubjectCountRow] = await connection.query(selectUserReviewNoteSubjectCountQuery, [
+		userIdx,
+		examIdx,
+	]);
+	return selectUserReviewNoteSubjectCountRow;
+}
+
+//유저 오답노트 과목별로 문제들에 대한 examDetailIndex,multipleProblemIdx 조회
+async function selectUserReviewNoteSubjectProblem(connection, examSubjecIdx, examIdx, userIdx) {
+	const selectUserReviewNoteSubjectProblemQuery = `
+	select distinct mp.multipleProblemIdx,mp.examHistory,ed.examDetailIdx,rn.reviewNoteIdx
+          FROM ReviewNote rn
+          LEFT JOIN MultipleProblem mp ON rn.multipleProblemIdx = mp.multipleProblemIdx
+          LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+          LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx
+          LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+          left join ProblemSubject ps on ps.multipleProblemIdx = mp.multipleProblemIdx
+          left join ExamSubject es on es.examSubjectIdx = ps.examSubjectIdx
+          left join ExamSubjectMulti esm on esm.examSubjectIdx = es.examSubjectIdx and ed.examIdx = esm.examIdx
+          LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+          WHERE userIdx = ? AND e.examIdx = ? AND es.examSubjectIdx =? AND rn.status =  'N'
+          GROUP BY mp.multipleProblemIdx
+          ORDER BY examDate DESC, examRound, pe.problemNum;     
+  `;
+	const [selectUserReviewNoteSubjectProblemRow] = await connection.query(selectUserReviewNoteSubjectProblemQuery, [
+		userIdx,
+		examIdx,
+		examSubjecIdx,
+	]);
+	return selectUserReviewNoteSubjectProblemRow;
+}
+
+//유저 주관식 오답노트 과목별로 문제들에 대한 examDetailIndex,multipleProblemIdx 조회
+async function selectUserSubjectiveReviewNoteSubjectProblem(connection, examSubjecIdx, examIdx, userIdx) {
+	const selectUserSubjectiveReviewNoteSubjectProblemQuery = `
+	select distinct sp.subjectiveProblemIdx,ed.examDetailIdx,rn.reviewNoteIdx
+          FROM ReviewNote rn
+          LEFT JOIN SubjectiveProblem sp ON rn.subjectiveProblemIdx = sp.subjectiveProblemIdx
+          LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+          LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx
+          LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+          left join SubjectiveProblemSubject sps on sps.subjectiveProblemIdx = sp.subjectiveProblemIdx
+          left join ExamSubject es on es.examSubjectIdx = sps.examSubjectIdx
+          left join ExamSubjectMulti esm on esm.examSubjectIdx = es.examSubjectIdx and ed.examIdx = esm.examIdx
+          LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+          WHERE userIdx = ? AND e.examIdx = ? AND es.examSubjectIdx =? AND rn.status =  'N'
+          GROUP BY sp.subjectiveProblemIdx
+          ORDER BY examDate DESC, examRound;     
+  `;
+	const [selectUserSubjectiveReviewNoteSubjectProblemRow] = await connection.query(
+		selectUserSubjectiveReviewNoteSubjectProblemQuery,
+		[userIdx, examIdx, examSubjecIdx],
+	);
+	return selectUserSubjectiveReviewNoteSubjectProblemRow;
+}
+
+//유저 오답노트 문제 과목별 인덱스 조회
+async function selectReviewNoteSubjectMultipleProblem(connection, userIdx, examSubjectIdx) {
+	const selectReviewNoteSubjectMultipleProblemQuery = `
+  select rn.multipleProblemIdx from ReviewNote rn 
+	  left join MultipleProblem mp on rn.multipleProblemIdx =mp.multipleProblemIdx
+    left join ProblemSubject ps on ps.multipleProblemIdx = mp.multipleProblemIdx
+    left join ExamSubject es on es.examSubjectIdx = ps.examSubjectIdx
+    where  userIdx=? and es.examSubjectIdx =? and rn.status = 'N'
+  `;
+	const [selectReviewNoteSubjectMultipleProblemRow] = await connection.query(
+		selectReviewNoteSubjectMultipleProblemQuery,
+		[userIdx, examSubjectIdx],
+	);
+	return selectReviewNoteSubjectMultipleProblemRow;
+}
+
+// 유저 오답노트 문제 조회 - 회차
+async function selectUserReviewNoteProblem(connection, userIdx, examDetailIdx) {
+	const selectUserReviewNoteProblemQuery = `
+      SELECT reviewNoteIdx, bookMark, mp.multipleProblemIdx, mp.examHistory,problemNum, examSubjectNum, examSubjectName, problem, domain,
+      case when rn.memo is null then 0 else 1 end as isMemo,
+      case 
+      when problemImage is null then problem
+      when problemImage = ' ' then problem
+      when problemImage = '' then problem
+      else
+      concat(problem, '\\\\\\\\ ', problemImage)  
+      end as problem,
+      provisionNum, questionNum as 'answerNum', solution,
+      concat('제', examSubjectNum,'과목 ', examSubjectName) as subject, isKatex, problemUrl, lectureUrl
+      FROM ReviewNote rn
+      LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = rn.multipleProblemIdx
+      LEFT JOIN Question q ON q.multipleProblemIdx = mp.multipleProblemIdx and q.isAnswer = 1
+      LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+      LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx
+      LEFT JOIN CompanyExam ce ON ce.examIdx = ed.examIdx
+      LEFT JOIN ProblemSubject ps ON ps.multipleProblemIdx = mp.multipleProblemIdx
+      LEFT JOIN ExamSubject es ON ps.examSubjectIdx = es.examSubjectIdx
+      LEFT JOIN ExamSubjectMulti esm ON esm.examSubjectIdx = es.examSubjectIdx and ed.examIdx = esm.examIdx
+      WHERE rn.userIdx = ? AND pe.examDetailIdx = ? AND rn.status = 'N'
+      ORDER BY problemNum
+      `;
+	const [selectUserReviewNoteProblemRow] = await connection.query(selectUserReviewNoteProblemQuery, [
+		userIdx,
+		examDetailIdx,
+	]);
+	return selectUserReviewNoteProblemRow;
+}
+// 유저 주관식 오답노트 문제 조회 - 회차
+async function selectUserSubjectiveReviewNoteProblem(connection, userIdx, examDetailIdx) {
+	const selectUserSubjectiveReviewNoteProblemQuery = `
+      SELECT e.subjectiveLabel,reviewNoteIdx, bookMark, sp.subjectiveProblemIdx,sp.examHistory, problemNum, examSubjectNum, examSubjectName, problem, domain,
+      case when rn.memo is null then 0 else 1 end as isMemo,
+      case 
+      when problemImage is null then problem
+      when problemImage = ' ' then problem
+      when problemImage = '' then problem
+      else
+      concat(problem, '\\\\\\\\ ', problemImage)  
+      end as problem, solution,
+      concat('제', examSubjectNum,'과목 ', examSubjectName) as subject, isKatex, problemUrl, lectureUrl
+      FROM ReviewNote rn
+      LEFT JOIN SubjectiveProblem sp ON sp.subjectiveProblemIdx = rn.subjectiveProblemIdx
+      LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+      LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx
+      LEFT JOIN Exam e on e.examIdx = ed.examIdx
+      LEFT JOIN CompanyExam ce ON ce.examIdx = ed.examIdx
+      LEFT JOIN SubjectiveProblemSubject sps ON sps.subjectiveProblemIdx = sp.subjectiveProblemIdx
+      LEFT JOIN ExamSubject es ON sps.examSubjectIdx = es.examSubjectIdx
+      LEFT JOIN ExamSubjectMulti esm ON esm.examSubjectIdx = es.examSubjectIdx and ed.examIdx = esm.examIdx
+      WHERE rn.userIdx = ? AND spe.examDetailIdx = ? AND rn.status = 'N'
+      ORDER BY problemNum
+      `;
+	const [selectUserSubjectiveReviewNoteProblemRow] = await connection.query(
+		selectUserSubjectiveReviewNoteProblemQuery,
+		[userIdx, examDetailIdx],
+	);
+	return selectUserSubjectiveReviewNoteProblemRow;
+}
+// 특정 회차 오답노트 개수 조회
+async function selectReviewNoteCount(connection, userIdx, examDetailIdx) {
+	const selectReviewNoteCountQuery = `
+        SELECT count(rn.reviewNoteIdx) as 'count'
+        FROM ReviewNote rn
+        LEFT JOIN MultipleProblem mp ON rn.multipleProblemIdx = mp.multipleProblemIdx
+        LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+        LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx
+        WHERE userIdx = ? AND ed.examDetailIdx = ? AND rn.status = 'N'
+        `;
+	const [selectReviewNoteCountRow] = await connection.query(selectReviewNoteCountQuery, [userIdx, examDetailIdx]);
+	return selectReviewNoteCountRow;
+}
+
+// 특정 회차 주관식 오답노트 개수 조회
+async function selectSubjectiveReviewNoteCount(connection, userIdx, examDetailIdx) {
+	const selectSubjectiveReviewNoteCountQuery = `
+        SELECT count(rn.reviewNoteIdx) as 'count'
+        FROM ReviewNote rn
+        LEFT JOIN SubjectiveProblem sp ON rn.subjectiveProblemIdx = sp.subjectiveProblemIdx
+        LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+        LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx
+        WHERE userIdx = ? AND ed.examDetailIdx = ? AND rn.status = 'N'
+        `;
+	const [selectSubjectiveReviewNoteCountRow] = await connection.query(selectSubjectiveReviewNoteCountQuery, [
+		userIdx,
+		examDetailIdx,
+	]);
+	return selectSubjectiveReviewNoteCountRow;
+}
+
+// 오답노트 메모 조회
+async function selectUserReviewNoteMemo(connection, reviewNoteIdx, userIdx) {
+	const selectUserReviewNoteMemoQuery = `
+        SELECT memo FROM ReviewNote WHERE reviewNoteIdx = ? AND userIdx = ?
+        `;
+	const [selectUserReviewNoteMemoRow] = await connection.query(selectUserReviewNoteMemoQuery, [reviewNoteIdx, userIdx]);
+	return selectUserReviewNoteMemoRow;
+}
+
+// 유저 시험 조회
+async function selectUserExamRecord(connection, userIdx) {
+	const selectUserExamRecordQuery = `
+        SELECT distinct e.examIdx, examName, passScore, e.examSortIdx
+        FROM UserExamRecord uer
+        LEFT JOIN ExamDetail ed ON uer.examDetailIdx = ed.examDetailIdx
+        LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+        WHERE userIdx = ? and e.status='N'
+        `;
+	const [selectUserExamRecordRow] = await connection.query(selectUserExamRecordQuery, userIdx);
+	return selectUserExamRecordRow;
+}
+
+// 유저 시험 기록 조회
+async function selectUserExamRecordList(connection, userIdx, examIdx) {
+	// kec는 자격증이지만 공기업처럼 문제수로 결과 표시 -> uer.examDetailIdx!=45
+	const selectUserExamRecordQuery = `
+  SELECT  
+    uer.examDetailIdx, uer.userExamRecordIdx, date_format(DATE_ADD(uer.createdAt, INTERVAL 9 HOUR), '%Y-%m-%d') as 'date', 
+    date_format(DATE_ADD(uer.createdAt, INTERVAL 9 HOUR), '%H:%i') as 'time',
+    case 
+      when ce.examIdx is null 
+      then concat(date_format(examDate, '%Y'), '년 ', examRound, '회') 
+      else concat(date_format(examDate, '%Y'), '년 ', examRound)  
+    end as 'round',
+    case 
+      when ce.examIdx is null and ed.examDetailIdx != 45
+      then concat(score, '점') 
+      else concat(count(q.questionIdx) ,' 문제 ', '/ ', count(uedr.userExamDetailRecordIdx) ,' 문제 ')  
+    end as 'score', uer.isPass
+  FROM UserExamRecord uer
+  LEFT JOIN ExamDetail ed ON uer.examDetailIdx = ed.examDetailIdx and ed.status ='N'
+  left join Exam e on e.examIdx = ed.examIdx
+  left join ExamSort es on es.examSortIdx= e.examSortIdx
+  LEFT JOIN CompanyExam ce ON ce.examIdx = ed.examIdx 
+  LEFT JOIN UserExamDetailRecord uedr ON uedr.userExamRecordIdx = uer.userExamRecordIdx and uedr.status = 'N'
+  LEFT JOIN Question q ON q.questionIdx = uedr.questionIdx and isAnswer = 1 and q.status = 'N'
+  WHERE userIdx = ?  and ed.examIdx = ? and uer.status = 'N'
+  group by uer.userExamRecordIdx
+  ORDER BY uer.createdAt DESC
+  `;
+	const [selectUserExamRecordRow] = await connection.query(selectUserExamRecordQuery, [userIdx, examIdx]);
+	return selectUserExamRecordRow;
+}
+
+//유저 시험 기록 삭제 - CBT - 김기창
+async function deleteCbtUserExamMulti(connection, userCbtExamIdx) {
+	//다중 삭제지만 useService에서 해당 userCbtExamIdx에 대해서 다 짤라서 보내게 된다.
+	const deleteCbtUserExamMultiQuery = `
+  UPDATE UserExamRecord SET status='Y' where userExamRecordIdx = ?
+  `;
+	const [deleteCbtUserExamMultiRow] = await connection.query(deleteCbtUserExamMultiQuery, userCbtExamIdx);
+	return deleteCbtUserExamMultiRow;
+}
+
+// 유저 시험 상세 기록 정보 조회
+async function selectUserExamDetailRecordInfo(connection, userExamRecordIdx) {
+	const selectUserExamDetailRecordInfoQuery = `
+        SELECT userExamDetailRecordIdx, userAnswer, answerNum
+        FROM UserExamDetailRecord uedr
+        LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = uedr.multipleProblemIdx
+        WHERE userExamRecordIdx = ?
+        `;
+	const [selectUserExamDetailRecordInfoRow] = await connection.query(
+		selectUserExamDetailRecordInfoQuery,
+		userExamRecordIdx,
+	);
+	return selectUserExamDetailRecordInfoRow;
+}
+
+// 유저 시험 기록 상세 조회  수정 필요
+async function selectUserExamDetailRecord(connection, userExamRecordIdx) {
+	const selectUserExamDetailRecordQuery = `
+        SELECT mp.problemNum, 
+        case when problemImage is null then problem
+        else
+        concat(problem, '\\\\\\\\ ', problemImage)  end as problem
+        , userAnswer, answerNum, provisionNum, solution
+        FROM UserExamDetailRecord uedr
+        LEFT JOIN MultipleProblem mp ON rn.multipleProblemIdx = mp.multipleProblemIdx
+        WHERE userExamRecordIdx = ?
+        `;
+	const [selectUserExamDetailRecordRow] = await connection.query(selectUserExamDetailRecordQuery, userExamRecordIdx);
+	return selectUserExamDetailRecordRow;
+}
+
+// 유저 시험 기록 정답 조회
+async function selectUserExamCorrectRecord(connection, userExamRecordIdx) {
+	const selectUserExamCorrectRecordQuery = `
+  SELECT 
+  case
+    when uedr.questionIdx = 0 or uedr.questionIdx != q.questionIdx or mp.isDelete = 1 then 0
+    else 1 end as 'isCorrect', 
+  case
+    when qu.questionIdx is null or qu.questionIdx = 0 then 0
+    else qu.questionNum end as 'userAnswer'
+  from UserExamDetailRecord uedr
+  LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = uedr.multipleProblemIdx and mp.status = 'N'
+  LEFT JOIN Question q ON q.multipleProblemIdx = mp.multipleProblemIdx and isAnswer = 1  and q.status = 'N'
+  LEFT JOIN Question qu ON qu.status = 'N' and qu.questionIdx = uedr.questionIdx
+  where uedr.userExamRecordIdx = ? and uedr.status = 'N'
+  `;
+	const [selectUserExamCorrectRecordRow] = await connection.query(selectUserExamCorrectRecordQuery, userExamRecordIdx);
+	return selectUserExamCorrectRecordRow;
+}
+
+// 유저 시험 기록 정보 조회  수정 필요
+async function selectUserExamRecordInfo(connection, userExamRecordIdx) {
+	const selectUserExamRecordInfoQuery = `
+  SELECT ed.examDetailIdx, ed.examIdx, examName, e.passScore, date_format(examDate, '%Y.%m.%d') as 'date', examRound 
+  FROM UserExamRecord uer
+  LEFT JOIN ExamDetail ed ON ed.examDetailIdx = uer.examDetailIdx
+  LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+  WHERE uer.userExamRecordIdx = ?
+  `;
+	const [selectUserExamRecordInfoRow] = await connection.query(selectUserExamRecordInfoQuery, userExamRecordIdx);
+	return selectUserExamRecordInfoRow;
+}
+
+// 유저 공기업 시험 기록 문제 조회
+async function selectUserExamRecordNonSubject(connection, userExamRecordIdx) {
+	const selectUserExamRecordNonSubjectQuery = `
+  SELECT uedr.multipleProblemIdx as 'problemIdx', problemNum, 
+  case
+  when uedr.questionIdx = 0 or uedr.questionIdx != q.questionIdx or mp.isDelete = 1 then 0
+  else 1 end as 'isCorrect',
+  case when Q.questionIdx is null or Q.questionIdx = 0 then 0 else Q.questionNum end as 'userAnswer', problemNum, 
+    case 
+    when problemImage is null then problem
+    when problemImage = ' ' then problem
+    when problemImage = '' then problem
+    else
+    concat(problem, '\\\\\\\\ ', problemImage)  end as problem, solution, provisionNum, isKatex, problemScore,
+	q.questionNum as 'answerNum', lectureUrl, problemUrl
+  FROM UserExamDetailRecord uedr
+  LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = uedr.multipleProblemIdx and mp.status = 'N'
+  LEFT JOIN Question q ON q.multipleProblemIdx = mp.multipleProblemIdx and isAnswer = 1 and q.status = 'N'
+  LEFT JOIN Question Q ON Q.questionIdx = uedr.questionIdx and Q.status = 'N'
+  LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+  WHERE userExamRecordIdx = ? and uedr.status = 'N'
+  `;
+	const [selectUserExamRecordNonSubjectRow] = await connection.query(
+		selectUserExamRecordNonSubjectQuery,
+		userExamRecordIdx,
+	);
+	return selectUserExamRecordNonSubjectRow;
+}
+
+// 유저 자격증 시험 기록 문제 조회  수정 필요
+async function selectUserExamRecordSubject(connection, userExamRecordIdx, examSubjectIdx) {
+	const selectUserExamRecordSubjectQuery = `
+  SELECT uedr.multipleProblemIdx as 'problemIdx', problemNum, 
+  case
+  when uedr.questionIdx = 0 or uedr.questionIdx != q.questionIdx or mp.isDelete = 1 then 0
+  else 1 end as 'isCorrect',
+  case when Q.questionIdx is null or Q.questionIdx = 0 then 0 else Q.questionNum end as 'userAnswer', problemNum, 
+    case 
+    when problemImage is null then problem
+    when problemImage = ' ' then problem
+    when problemImage = '' then problem
+    else
+    concat(problem, '\\\\\\\\ ', problemImage)  end as problem, solution, provisionNum, isKatex, problemScore,
+	q.questionNum as 'answerNum', lectureUrl, problemUrl
+  FROM UserExamDetailRecord uedr
+  LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = uedr.multipleProblemIdx and mp.status = 'N'
+  LEFT JOIN Question q ON q.multipleProblemIdx = mp.multipleProblemIdx and isAnswer = 1 and q.status = 'N'
+  LEFT JOIN Question Q ON Q.questionIdx = uedr.questionIdx and Q.status = 'N'
+  LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx
+  LEFT JOIN ProblemSubject ps ON ps.multipleProblemIdx = mp.multipleProblemIdx
+  WHERE userExamRecordIdx = ? and examSubjectIdx = ? and uedr.status = 'N'
+  ORDER BY problemNum ASC;
+  `;
+	const [selectUserExamRecordSubjectRow] = await connection.query(selectUserExamRecordSubjectQuery, [
+		userExamRecordIdx,
+		examSubjectIdx,
+	]);
+	return selectUserExamRecordSubjectRow;
+}
+
+// 유저 게시글 조회
+async function selectUserBoard(connection, userIdx) {
+	const selectUserListQuery = `
+  SELECT boardIdx, boardSort, boardTitle, b.status
+  FROM User u
+  LEFT JOIN Board b ON u.userIdx = b.userIdx 
+  WHERE b.userIdx = ?
+  order by b.boardSort
+     `;
+	const [selectUserListRow] = await connection.query(selectUserListQuery, userIdx);
+	return selectUserListRow;
+}
+
+// 문제 에러 신고 조회
+async function selectUserProblemError(connection, userIdx) {
+	const selectUserProblemErrorQuery = `
+  SELECT problemErrorIdx, ed.examIdx, ed.examDetailIdx, problemNum, title, pe.solution,content, date_format(pe.createdAt, '%Y-%m-%d') as 'date', date_format(pe.createdAt, '%H:%i') as 'time', pe.status
+	FROM ProblemError pe
+  LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = pe.multipleProblemIdx
+  LEFT JOIN ProblemExam PE ON PE.multipleProblemIdx = mp.multipleProblemIdx 
+  LEFT JOIN ExamDetail ed ON ed.examDetailIdx = PE.examDetailIdx
+	where userIdx = ? 
+  ORDER BY pe.createdAt DESC
+     `;
+	const [selectUserProblemErrorRow] = await connection.query(selectUserProblemErrorQuery, userIdx);
+	return selectUserProblemErrorRow;
+}
+
+// 유저 시험기록 인덱스로 시험 회차 인덱스 조회
+async function selectExamDetailByExamRecord(connection, userExamRecordIdx) {
+	const selectExamDetailByExamRecordQuery = `
+  SELECT examDetailIdx FROM UserExamRecord WHERE userExamRecordIdx = ?
+  `;
+	const [selectExamDetailByExamRecordRow] = await connection.query(
+		selectExamDetailByExamRecordQuery,
+		userExamRecordIdx,
+	);
+	return selectExamDetailByExamRecordRow;
+}
+
+// 유저 시험기록 채점결과 조회
+async function selectExamRecordByExamDetail(connection, userExamRecordIdx) {
+	const selectExamRecordByExamDetailQuery = `
+  SELECT  e.examName, e.passScore, uer.userExamRecordIdx, uer.createdAt as 'userExamRecordCreatedAt', 
+  examRound  , date_format(examDate, "%Y-%m-%d") as 'date',
+  case
+  when e.passScore > score then 0
+  else 1 end as 'isPass', count(uedr.userExamRecordIdx) as 'totalCount',
+  case when ce.examIdx is null then score else count(q.questionIdx)  end as 'score'
+  FROM UserExamRecord uer
+  LEFT JOIN UserExamDetailRecord uedr ON uedr.userExamRecordIdx = uer.userExamRecordIdx
+  LEFT JOIN ExamDetail ed ON uer.examDetailIdx = ed.examDetailIdx
+  LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+  LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+  LEFT JOIN Question q ON q.questionIdx = uedr.questionIdx and isAnswer = 1
+  WHERE uer.userExamRecordIdx = ?
+  ORDER BY uer.createdAt DESC
+  `;
+	const [selectExamRecordByExamDetailRow] = await connection.query(
+		selectExamRecordByExamDetailQuery,
+		userExamRecordIdx,
+	);
+	return selectExamRecordByExamDetailRow;
+}
+
+// 유저 시험기록 채점결과 과목별 조회
+// 여기 과락 해야함
+async function selectExamRecordBySubject(connection, userExamRecordIdx) {
+	const selectExamRecordBySubjectQuery = `
+  SELECT es.examSubjectIdx, esm.examSubjectNum, es.examSubjectName, count(case when uedr.questionIdx = q.questionIdx then 1 end ) as 'correctCount', count(mp.multipleProblemIdx) as 'total',
+  cast(sum(IFNULL(case when  uedr.questionIdx = q.questionIdx then mp.problemScore end, 0)) as unsigned) as score, problemScore, es.problemCount, es.passScore
+  FROM UserExamRecord uer
+  LEFT JOIN UserExamDetailRecord uedr ON uedr.userExamRecordIdx = uer.userExamRecordIdx and uedr.status ='N'
+   JOIN MultipleProblem mp ON mp.multipleProblemIdx = uedr.multipleProblemIdx and mp.status='N'
+  LEFT JOIN Question q ON mp.multipleProblemIdx=q.multipleProblemIdx and q.isAnswer=1 and q.status='N'
+  LEFT JOIN ProblemSubject ps on ps.multipleProblemIdx=mp.multipleProblemIdx
+  LEFT JOIN ExamSubject es on ps.examSubjectIdx=es.examSubjectIdx
+  LEFT JOIN ProblemExam pe on pe.multipleProblemIdx=mp.multipleProblemIdx
+  LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx and ed.status = 'N'
+  LEFT JOIN Exam e ON e.examIdx = ed.examIdx and e.status = 'N'
+  LEFT JOIN ExamSubjectMulti esm ON esm.examIdx = ed.examIdx and esm.examSubjectIdx = es.examSubjectIdx
+  WHERE uer.userExamRecordIdx = ? and uer.status = 'N'
+  group by es.examSubjectIdx
+  `;
+	const [selectExamRecordBySubjectRow] = await connection.query(selectExamRecordBySubjectQuery, userExamRecordIdx);
+	return selectExamRecordBySubjectRow;
+}
+
+// 관리자 특정 유저 포인트 로그
+async function selectUserPointLog(connection, userIdx) {
+	const selectUserPointLogQuery = `
+	select * from UserPointLog where userIdx =?;
+	`;
+	const [selectUserPointLogRow] = await connection.query(selectUserPointLogQuery, userIdx);
+	return selectUserPointLogRow;
+}
+
+// 관리자 특정 유저 상품 이용 로그
+async function selectUserAuthLog(connection, userIdx) {
+	const selectUserAuthLogQuery = `
+	SELECT examDetailIdx, examIdx, createdAt, type
+  FROM UserAuthLog WHERE userIdx =?;
+	`;
+	const [selectUserAuthLogRow] = await connection.query(selectUserAuthLogQuery, userIdx);
+	return selectUserAuthLogRow;
+}
+
+/**  관리자 특정 유저 포인트의 남은 금액 - 김기창
+async functioconnection, userIdx) {
+  const selectUserPointTotalQuery = `
+  SELECT cast(sum(case when sort="A" then point else point*(-1) end)as signed)  as point_total FROM UserPointLog where userIdx=?;
+  `;
+  const [selectUserPointTotalRow] = await connection.query(selectUserPointTotalQuery, userIdx);
+  return selectUserPointTotalRow;
+}
+*/
+
+// 탈퇴한 회원 재가입
+async function updateUserRejoin(connection, updateUserRejoinParams) {
+	const updateUserRejoinQuery = `
+  UPDATE User SET nickname=?, status='N'
+  WHERE userIdx=? and provider = ?
+  `;
+	const updateUserRejoinRow = await connection.query(updateUserRejoinQuery, updateUserRejoinParams);
+	return updateUserRejoinRow;
+}
+
+// 회원가입
+async function insertUser(connection, insertUserParams) {
+	const insertUserQuery = `
+          insert User(userEmail, userPassword, userPhoneNum, userName, dateOfBirth, nickname,provider)
+          values(?, ?, ?, ?, ?, ?, ?)
+          `;
+	const insertUserdRow = await connection.query(insertUserQuery, insertUserParams);
+	return insertUserdRow;
+}
+
+// 소셜 계정 연동 추가
+async function insertUserSocialId(connection, insertUserSocialIdParams) {
+	const insertUserSocialIdQuery = `
+        insert SocialAccount(userIdx, snsName, snsId) 
+        values(?, ?, ?)
+        `;
+	const [insertUserSocialIdRow] = await connection.query(insertUserSocialIdQuery, insertUserSocialIdParams);
+	return insertUserSocialIdRow;
+}
+
+// 유저 로그 로그인 삽입
+async function insertUserLog(connection, userIdx, action) {
+	const insertUserLoginQuery = `
+        insert UserLog(userIdx, action)
+        values(?, ?)
+        `;
+	const insertUserLoginRow = await connection.query(insertUserLoginQuery, [userIdx, action]);
+	return insertUserLoginRow;
+}
+
+// 유저 오류신고 삽입
+async function insertReportSiteError(connection, insertsiteErrorParams) {
+	const insertUserLoginQuery = `
+  INSERT INTO Board(userIdx, boardSort, boardTitle, boardContent)
+  VALUES (?, '오류신고', ?, ?)
+  `;
+	const insertUserLoginRow = await connection.query(insertUserLoginQuery, insertsiteErrorParams);
+	return insertUserLoginRow;
+}
+
+// 비밀번호 수정
+async function updateUserPassword(connection, userPassword, userIdx) {
+	const updateUserQuery = `
+  update User
+  set userPassword = ?
+  where userIdx = ? and provider = "engineeo";
+  `;
+	const [updateUserRow] = await connection.query(updateUserQuery, [userPassword, userIdx]);
+	return updateUserRow;
+}
+
+// 닉네임 수정
+async function updateUserNickname(connection, nickname, userIdx) {
+	const updateUserNicknameQuery = `
+  update User
+  set nickname = ?
+  where userIdx = ?;
+  `;
+	const [updateUserNicknameRow] = await connection.query(updateUserNicknameQuery, [nickname, userIdx]);
+	return updateUserNicknameRow;
+}
+
+// 회원정보 수정
+async function updateUserInfo(connection, userPassword, nickname, userIdx) {
+	const updateUserNicknameQuery = `
+  update User
+  set userPassword = ?, nickname = ?
+  where userIdx = ?;
+  `;
+	const [updateUserNicknameRow] = await connection.query(updateUserNicknameQuery, [userPassword, nickname, userIdx]);
+	return updateUserNicknameRow;
+}
+
+// 추천인 카운트
+async function updateUserRecommendCountUp(connection, nickname) {
+	const updateUserRecommendCountUpQuery = `
+  update User
+  set recommendCount = recommendCount + 1
+  where nickname = ?;
+  `;
+	const [updateUserRecommendCountUpRow] = await connection.query(updateUserRecommendCountUpQuery, nickname);
+	return updateUserRecommendCountUpRow;
+}
+
+// 오답노트 북마크 설정
+async function updateUserReviewNoteBookMark(connection, reviewNoteIdx, bookMark) {
+	const updateUserReviewNoteBookMarkQuery = `
+  update ReviewNote 
+  set bookMark = ? 
+  where reviewNoteIdx = ?;
+  `;
+	const [updateUserReviewNoteBookMarkRow] = await connection.query(updateUserReviewNoteBookMarkQuery, [
+		bookMark,
+		reviewNoteIdx,
+	]);
+	return updateUserReviewNoteBookMarkRow;
+}
+
+// 오답노트 메모 설정
+async function updateUserReviewNoteMemo(connection, reviewNoteIdx, userIdx, memo) {
+	const updateUserReviewNoteMemoQuery = `
+  update ReviewNote 
+  set memo = ? 
+  where reviewNoteIdx = ? and userIdx = ?;
+  `;
+	const [updateUserReviewNoteMemoRow] = await connection.query(updateUserReviewNoteMemoQuery, [
+		memo,
+		reviewNoteIdx,
+		userIdx,
+	]);
+	return updateUserReviewNoteMemoRow;
+}
+
+// 회원 탈퇴
+async function withdrawalUser(connection, userIdx) {
+	const withdrawalUserQuery = `
+  update User
+  set status = 'Y', point = 0
+  where userIdx = ?;
+  `;
+	const [withdrawalUserRow] = await connection.query(withdrawalUserQuery, userIdx);
+	return withdrawalUserRow;
+}
+
+// 유저 오답노트 삭제
+async function deleteUserReviewNote(connection, reviewNoteIdxList) {
+	const deleteUserReviewNoteQuery = `
+  update ReviewNote
+  set status = 'Y'
+  where  reviewNoteIdx in (?);
+  `;
+
+	const [deleteUserReviewNoteRow] = await connection.query(deleteUserReviewNoteQuery, [reviewNoteIdxList]);
+	return deleteUserReviewNoteRow;
+}
+
+async function deleteUserReviewNoteSubjectByExamSubject(connection, userIdx, examIdx, examSubjectIdx) {
+	const deleteUserReviewNoteSubjectByExamSubjectQuery = `
+  UPDATE ReviewNote rn
+  LEFT JOIN SubjectiveProblem sp ON rn.subjectiveProblemIdx = sp.subjectiveProblemIdx
+  LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+  LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx
+  LEFT JOIN Exam e ON e.examIdx = ed.examIdx
+  LEFT JOIN SubjectiveProblemSubject sps on sps.subjectiveProblemIdx = sp.subjectiveProblemIdx
+  LEFT JOIN ExamSubject es on es.examSubjectIdx = sps.examSubjectIdx
+  LEFT JOIN ExamSubjectMulti esm on esm.examSubjectIdx = es.examSubjectIdx and ed.examIdx = esm.examIdx
+  LEFT JOIN CompanyExam ce ON ce.examIdx = e.examIdx
+  SET rn.status ="Y"
+  WHERE userIdx = ? AND e.examIdx = ? and es.examSubjectIdx = ?  AND rn.status = 'N'
+  `;
+	const [deleteUserReviewNoteSubjectByExamSubjectQueryResult] = await connection.query(
+		deleteUserReviewNoteSubjectByExamSubjectQuery,
+		[userIdx, examIdx, examSubjectIdx],
+	);
+	return deleteUserReviewNoteSubjectByExamSubjectQueryResult;
+}
+//객관식 시험인지를 판단하기 위한 쿼리
+async function selectExamInfo(connection, examDetailIdx) {
+	const selectExamInfoQuery = `
+  SELECT exists(
+    SELECT examIdx from ExamDetail ed
+    LEFT JOIN SubjectiveProblemExam spe on ed.examDetailIdx = spe.examDetailIdx
+    where spe.examDetailIdx = ?
+  ) as exist
+  `;
+	const [selectExamInfoQueryResult] = await connection.query(selectExamInfoQuery, examDetailIdx);
+	return selectExamInfoQueryResult;
+}
+
+// 유저 오답노트 실기 회차 삭제
+async function deleteUserReviewNoteSubjectiveRound(connection, userIdx, examDetailIdx) {
+	const deleteUserReviewNoteSubjectiveRoundQuery = `
+UPDATE ReviewNote rn 
+LEFT JOIN SubjectiveProblem sp ON sp.subjectiveProblemIdx = rn.subjectiveProblemIdx 
+LEFT JOIN SubjectiveProblemExam spe ON spe.subjectiveProblemIdx = sp.subjectiveProblemIdx
+LEFT JOIN ExamDetail ed ON ed.examDetailIdx = spe.examDetailIdx 
+SET rn.status = 'Y' 
+WHERE userIdx = ? AND ed.examDetailIdx = ? 
+`;
+	const [deleteUserReviewNoteSubjectiveRoundRow] = await connection.query(deleteUserReviewNoteSubjectiveRoundQuery, [
+		userIdx,
+		examDetailIdx,
+	]);
+	return deleteUserReviewNoteSubjectiveRoundRow;
+}
+
+// 유저 오답노트 필기 회차 삭제
+async function deleteUserReviewNoteRound(connection, userIdx, examDetailIdx) {
+	const deleteUserReviewNoteRoundQuery = `
+  UPDATE ReviewNote rn 
+  LEFT JOIN MultipleProblem mp ON mp.multipleProblemIdx = rn.multipleProblemIdx 
+  LEFT JOIN ProblemExam pe ON pe.multipleProblemIdx = mp.multipleProblemIdx 
+  LEFT JOIN ExamDetail ed ON ed.examDetailIdx = pe.examDetailIdx 
+  SET rn.status = 'Y' 
+  WHERE userIdx = ? AND ed.examDetailIdx = ? 
+  `;
+	const [deleteUserReviewNoteRoundRow] = await connection.query(deleteUserReviewNoteRoundQuery, [
+		userIdx,
+		examDetailIdx,
+	]);
+	return deleteUserReviewNoteRoundRow;
+}
+
+// 유저 모든 오답노트 삭제
+async function deleteUserAllReviewNote(connection, userIdx) {
+	const deleteUserAllReviewNoteQuery = `
+  UPDATE ReviewNote
+  SET status = 'Y'
+  WHERE  userIdx = ?;
+  `;
+	const [deleteUserAllReviewNoteRow] = await connection.query(deleteUserAllReviewNoteQuery, userIdx);
+	return deleteUserAllReviewNoteRow;
+}
+
+// 유저 시험기록 삭제
+async function deleteUserAllUserExamRecord(connection, userIdx) {
+	const deleteUserAllUserExamRecordQuery = `
+  UPDATE UserExamRecord
+  SET status = 'Y'
+  WHERE  userIdx = ?;
+  `;
+	const [deleteUserAllUserExamRecordRow] = await connection.query(deleteUserAllUserExamRecordQuery, userIdx);
+	return deleteUserAllUserExamRecordRow;
+}
+
+// 유저 모든 권한 삭제
+async function deleteUserAllUserAuth(connection, userIdx) {
+	const deleteUserAllUserAuthQuery = `
+  UPDATE UserAuth
+  SET status = 'Y', expireAt = now()
+  WHERE userIdx = ? and status = 'N';
+  `;
+	const [deleteUserAllUserAuthRow] = await connection.query(deleteUserAllUserAuthQuery, userIdx);
+	return deleteUserAllUserAuthRow;
+}
+
+// 유저 모든 장바구니 기록 삭제
+async function deleteUserAllUserCart(connection, userIdx) {
+	const deleteUserAllUserCartQuery = `
+  DELETE FROM UserCart 
+  WHERE userIdx=?
+  `;
+	const [deleteUserAllUserCartRow] = await connection.query(deleteUserAllUserCartQuery, userIdx);
+	return deleteUserAllUserCartRow;
+}
+
+// 유저 결제 내역 삭제
+async function deleteUserAllPayment(connection, userIdx) {
+	const deleteUserAllPaymentQuery = `
+  UPDATE Payment
+  SET status = 'Y'
+  WHERE userIdx = ?;
+  `;
+	const [deleteUserAllPaymentRow] = await connection.query(deleteUserAllPaymentQuery, userIdx);
+	return deleteUserAllPaymentRow;
+}
+
+//유저 장바구니에 이미 상품있는지 확인
+async function checkUserCartProduct(connection, userIdx, productIdx) {
+	const checkUserCartProductQuery = `
+    SELECT exists
+    (SELECT userCartIdx FROM UserCart WHERE userIdx = ? AND productIdx = ?) as exist;
+    `;
+	const [checkUserCartProductRow] = await connection.query(checkUserCartProductQuery, [userIdx, productIdx]);
+	return checkUserCartProductRow;
+}
+
+// 유저 장바구니 조회
+async function selectUserCart(connection, userIdx) {
+	const selectUserCartQuery = `
+  SELECT userCartIdx, (SELECT productName from Product where productIdx = pr.parentIdx) as category, uc.productIdx, pr.parentIdx, productName, shortDescription, discountPrice, productThumbnail, duration
+  FROM UserCart as uc
+  JOIN Product as p on p.productIdx = uc.productIdx
+  JOIN ProductRelation as pr on pr.productIdx = p.productIdx
+  WHERE userIdx = ?;
+  `;
+	const [selectUserCartRow] = await connection.query(selectUserCartQuery, userIdx);
+	return selectUserCartRow;
+}
+
+// 유저 장바구니 추가
+async function insertUserCart(connection, cartInfo) {
+	const insertUserCartQuery = `
+  INSERT INTO UserCart (userIdx , productIdx) VALUES ?;`;
+
+	const [insertUserCartRow] = await connection.query(insertUserCartQuery, [cartInfo]);
+	return insertUserCartRow;
+}
+
+// 유저 장바구니 삭제 (다중)
+async function deleteUserCartMulti(connection, userCartIdx) {
+	const deleteUserCartMultiQuery =
+		`
+  DELETE FROM UserCart WHERE userCartIdx in (` +
+		userCartIdx +
+		`)
+  `;
+	const [deleteUserCartMultiRow] = await connection.query(deleteUserCartMultiQuery, userCartIdx);
+	return deleteUserCartMultiRow;
+}
+
+// 유저 장바구니 특정 상품 삭제 (다중)
+async function deleteUserCartProductMulti(connection, userIdx, productIdx) {
+	const deleteUserCartProductQuery =
+		`
+  DELETE FROM UserCart WHERE userIdx = ? AND productIdx in (` +
+		productIdx +
+		`)
+  `;
+	const [deleteUserCartProductRow] = await connection.query(deleteUserCartProductQuery, userIdx);
+	return deleteUserCartProductRow;
+}
+
+//유저 구매내역 조회 (결제단위)
+async function selectUserPurchaseHistory(connection, userIdx) {
+	const selectUserPurchaseHistoryQuery = `
+  SELECT p.paymentIdx as orderNum, p.paymentName, p.userIdx, userEmail, pp.isRefund, userName, userPhoneNum, p.paidAt, pp.refundPrice as totalRefund, p.pay_method, totalPrice, totalPoint, level, p.receipt_url, p.status
+  FROM Payment p
+  JOIN User u ON u.userIdx = p.userIdx and u.userIdx = ? 
+  JOIN PaymentProduct pp on p.paymentIdx = pp.paymentIdx
+  WHERE level IN ("paid", "cancelled", "pointPaid") AND p.status = 'N' AND u.status = 'N'
+  order by paidAt desc
+  `;
+	const [selectUserPurchaseHistoryRow] = await connection.query(selectUserPurchaseHistoryQuery, userIdx);
+	return selectUserPurchaseHistoryRow;
+}
+
+//유저 구매내역 조회 (상품단위)
+async function selectUserProduct(connection, userIdx) {
+	const selectUserProductQuery = `
+	SELECT distinct p.productIdx, p.productName, p.productThumbnail, duration, paidAt, date_add(paidAt, interval +duration day) expireAt
+	FROM PaymentProduct as pp
+	JOIN Payment as pa ON pa.paymentIdx = pp.paymentIdx and pa.status = 'N' and level = 'paid'
+	JOIN Product as p ON pp.productIdx = p.productIdx and p.status = 'N'
+	WHERE userIdx = ? AND isRefund = 0;
+	`;
+	const [selectUserProductRow] = await connection.query(selectUserProductQuery, userIdx);
+	return selectUserProductRow;
+}
+
+//유저 구매 내역 조회 (권한 단위)
+async function selectAuthUserProduct(connection, userIdx) {
+	const selectAuthUserProductQuery = `
+  select DISTINCT ua.userAuthIdx,ua.productIdx,p.productName, ua.status,
+  p.productThumbnail,p.duration,ua.createdAt as paidAt,ua.expireAt  
+  from UserAuth ua 
+  INNER JOIN Product p on p.productIdx = ua.productIdx 
+  where ua.userIdx= ? and ua.status= 'N';
+  `;
+
+	const [selectAuthUserProductRow] = await connection.query(selectAuthUserProductQuery, userIdx);
+	return selectAuthUserProductRow;
+}
+
+//유저 환불내역 조회
+// async function selectUserRefundHistory(connection, userIdx) {
+// 	const selectUserRefundHistoryQuery = `
+//   select p.paymentIdx, date_format(pprefundAt, '%Y-%m-%d %H:%i') as refundDate, status, pay_method as refund_method, finalPrice, refundPrice, productname
+//   from Payment as p
+//   left join (select pp.status as ppstatus, p.status as pstatus, paymentIdx, finalPrice, refundPrice, isRefund, pp.refundAt as pprefundAt, p.productName
+//   from PaymentProduct as pp
+//   left join Product as p on pp.productIdx=p.productIdx
+//   group by pp.paymentIdx) as S
+//   on p.paymentIdx=S.paymentIdx
+//   where userIdx= ? and isRefund = 1 and p.status='N' and ppstatus='N' and pstatus='N'
+//   order by refundDate desc;
+//   `;
+// 	const [selectUserRefundHistoryRow] = await connection.query(selectUserRefundHistoryQuery, userIdx);
+// 	return selectUserRefundHistoryRow;
+// }
+
+//유저 무료 상품 입력
+async function insertUserFreeProduct(connection, userIdx) {
+	const insertUserFreeProductQuery = `
+  INSERT INTO UserAuth(userIdx, productIdx, expireAt)
+  VALUES (?, 99, '2035-12-31 14:59:59')
+  `;
+	const [insertUserFreeProductRow] = await connection.query(insertUserFreeProductQuery, userIdx);
+	return insertUserFreeProductRow;
+}
+
+// 탈퇴한 회원의 재가입시 무료회차 상품 권한 변경
+async function updateUserFreeProduct(connection, userIdx) {
+	const updateUserFreeProductQuery = `
+  UPDATE UserAuth SET status = 'N', expireAt = '2035-12-31 14:59:59' where userIdx = ? and productIdx = 99
+  `;
+	const updateUserFreeProductRow = await connection.query(updateUserFreeProductQuery, userIdx);
+	return updateUserFreeProductRow;
+}
+
+//유저 포인트 지급
+async function insertUserPoint(connection, userIdx, pointIdx) {
+	const insertUserPointQuery = `
+  INSERT INTO UserPoint (userIdx, pointIdx)
+  VALUES (?, ?)
+  `;
+	const [insertUserPointRow] = await connection.query(insertUserPointQuery, [userIdx, pointIdx]);
+	return insertUserPointRow;
+}
+
+//유저 포인트 로그 입력
+async function insertUserPointLog(connection, pointLogParams) {
+	const insertUserPointQuery = `
+  INSERT INTO UserPointLog (userIdx, sort, content, point)
+  VALUES (?, ?, ?, ?)
+  `;
+	const [insertUserPointRow] = await connection.query(insertUserPointQuery, pointLogParams);
+	return insertUserPointRow;
+}
+
+//포인트 정보 조회
+async function selectPointInfo(connection, pointIdx) {
+	const selectPointInfoQuery = `
+  SELECT pointName, point FROM Point WHERE pointIdx = ? AND status = 'N';
+  `;
+	const [[selectPointInfoRow]] = await connection.query(selectPointInfoQuery, pointIdx);
+	return selectPointInfoRow;
+}
+
+//포인트 정보 조회
+async function updateUserPoint(connection, userIdx, point) {
+	const updateUserPointQuery = `
+  UPDATE User SET point = point + ?
+  WHERE userIdx = ?;
+  `;
+	const [updateUserPointRow] = await connection.query(updateUserPointQuery, [point, userIdx]);
+	return updateUserPointRow;
+}
+
+async function createUserAuthLogDetailIdx(connection, userIdx, examDetailIdx, type) {
+	const createUserAuthLogQuery = `
+  insert into UserAuthLog(userIdx,examDetailIdx, type) Values (?,?,?)
+    `;
+	const [createUserAuthLogRow] = await connection.query(createUserAuthLogQuery, [userIdx, examDetailIdx, type]);
+	return createUserAuthLogRow;
+}
+
+async function createUserAuthLogExamIdx(connection, userIdx, examIdx, type) {
+	const createUserAuthLogQuery = `
+  insert into UserAuthLog(userIdx,examIdx, type) Values (?,?,?)
+    `;
+	const [createUserAuthLogRow] = await connection.query(createUserAuthLogQuery, [userIdx, examIdx, type]);
+	return createUserAuthLogRow;
+}
+
+async function selectExamImage(connection, examName) {
+	const selectExamImageQuery = `select examUrl from Exam where examName=?`;
+	const selectExamImageRow = await connection.query(selectExamImageQuery, examName);
+	return selectExamImageRow[0];
+}
+
+async function selectUserAllProductsName(connection, userIdx) {
+	const selectUserAllProductQuery = `
+    select distinct(p.productName), ua.status, ua.expireAt from UserAuth ua
+    left join Product p on ua.productIdx = p.productIdx
+    where userIdx = ?;
+  `;
+	const [selectUserAllProductRow] = await connection.query(selectUserAllProductQuery, userIdx);
+	return selectUserAllProductRow;
+}
+
+// 상품 구매한 기록이 있는 모든 유저의 index 가져오기 (무료 엔지니오 상품 제외)
+async function selectPurchasedUserIdxList(connection) {
+	const selectAlluserIdxListQuery = `
+  select distinct(u.userIdx) from User u
+  left join UserAuth ua on u.userIdx = ua.userIdx
+  where u.status='N' and productIdx != 99;
+`;
+	const [selectAlluserIdxListRow] = await connection.query(selectAlluserIdxListQuery);
+	return selectAlluserIdxListRow;
+}
+
+async function selectUserIdxByUserAuthIdx(connection, userAuthIdx) {
+	const selectUserIdxByUserAuthIdxQuery = `
+    select userIdx from UserAuth where userAuthIdx = ?
+  `;
+	const selectUserIdxByUserAuthIdxRow = await connection.query(selectUserIdxByUserAuthIdxQuery, userAuthIdx);
+	return selectUserIdxByUserAuthIdxRow;
+}
+
+module.exports = {
+	selectUserEmail,
+	selectUserList,
+	selectUserPhoneNum,
+	selectUserNickname,
+	selectUserPassword,
+	selectLoginResult,
+	selectUserInfo,
+	selectUserNickname,
+	selectUserIdx,
+	selectUserStatus,
+	selectUserSocialLink,
+	selectUserReviewNoteIdx,
+	selectUserExamRecordIdx,
+	selectUserIdxByEmail,
+	selectUserIdxByPhoneNum,
+	selectUserEmailByPhone,
+	selectUserReviewNotePage,
+	selectUserSubjectiveReviewNotePage,
+	selectUserReviewNoteAllProblem,
+	selectUserSubjectiveReviewNoteAllProblem,
+	selectUserReviewNoteCount,
+	selectUserSubjectiveReviewNoteCount,
+	selectUserReviewNoteExam,
+	selectSubjectiveUserReviewNoteExam,
+	selectUserReviewNoteList,
+	selectUserSubjectiveReviewNoteList,
+	selectUserReviewNoteSubjectList,
+	selectUserSubjectiveReviewNoteSubjectList,
+	selectUserReviewNoteSubjectCount,
+	selectUserSubjectiveReviewNoteSubjectCount,
+	selectUserReviewNoteSubjectProblem,
+	selectUserSubjectiveReviewNoteSubjectProblem,
+	selectReviewNoteSubjectMultipleProblem,
+	selectUserReviewNoteProblem,
+	selectUserSubjectiveReviewNoteProblem,
+	selectReviewNoteCount,
+	selectSubjectiveReviewNoteCount,
+	selectUserReviewNoteMemo,
+	selectUserExamRecord,
+	selectUserExamRecordList,
+	selectExamImage,
+	deleteCbtUserExamMulti,
+	selectUserExamDetailRecordInfo,
+	selectUserExamDetailRecord,
+	selectUserExamCorrectRecord,
+	selectUserExamRecordInfo,
+	selectUserExamRecordNonSubject,
+	selectUserExamRecordSubject,
+	selectUserBoard,
+	selectPhoneNumByEmail,
+	selectUserProblemError,
+	selectExamDetailByExamRecord,
+	selectExamRecordBySubject,
+	selectExamRecordByExamDetail,
+	selectUserCart,
+	selectUserPurchaseHistory,
+	selectUserProduct,
+	selectAuthUserProduct,
+	selectUserPointLog,
+	selectUserAuthLog,
+	selectPointInfo,
+	selectUserAllProductsName,
+	insertUser,
+	insertUserLog,
+	insertReportSiteError,
+	insertUserSocialId,
+	insertUserCart,
+	insertUserFreeProduct,
+	insertUserPoint,
+	insertUserPointLog,
+	updateUserPassword,
+	updateUserNickname,
+	updateUserInfo,
+	updateUserRejoin,
+	updateUserRecommendCountUp,
+	updateUserReviewNoteBookMark,
+	updateUserReviewNoteMemo,
+	updateUserPoint,
+	withdrawalUser,
+	deleteUserReviewNote,
+	deleteUserReviewNoteSubjectByExamSubject,
+	deleteUserReviewNoteRound,
+	deleteUserAllReviewNote,
+	deleteUserAllUserExamRecord,
+	deleteUserAllUserAuth,
+	deleteUserAllUserCart,
+	deleteUserAllPayment,
+	deleteUserCartMulti,
+	deleteUserCartProductMulti,
+	checkUserCartProduct,
+	createUserAuthLogDetailIdx,
+	createUserAuthLogExamIdx,
+	selectExamInfo,
+	deleteUserReviewNoteSubjectiveRound,
+	updateUserFreeProduct,
+	selectPurchasedUserIdxList,
+	selectUserIdxByUserAuthIdx,
+};
diff --git a/_old/src/User/userProvider.js b/_old/src/User/userProvider.js
new file mode 100644
index 0000000..13b2f8c
--- /dev/null
+++ b/_old/src/User/userProvider.js
@@ -0,0 +1,1690 @@
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const userDao = require("./userDao");
+const examDao = require("../Exam/examDao");
+const storeDao = require("../Store/storeDao");
+const adminProvider = require("../Admin/adminProvider");
+const storeProvider = require("../Store/storeProvider");
+const examProvider = require("../Exam/examProvider");
+const userService = require("./userService");
+const { logger } = require("../../config/winston");
+const { pool } = require("../../config/database");
+const errorResponse = require("../../utils/errorResponse");
+
+// 유저 인덱스 체크
+exports.userIdxCheck = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userCheckResult = await userDao.selectUserIdx(connection, userIdx);
+		if (userCheckResult.length === 0) throw new errorResponse(baseResponse.USER_NOT_EXIST, 404);
+		if (userCheckResult[0].status === "Y") throw new errorResponse(baseResponse.USER_QUIT, 400);
+		return userCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 인덱스 체크,
+exports.userListCheck = async function (userList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userLists = userList.join(",");
+		const userCheckResult = await userDao.selectUserList(connection, userLists);
+		return userCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 이메일 체크
+exports.userEmailCheck = async function (userEmail, provider) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userEmailCheckResult = await userDao.selectUserEmail(connection, userEmail, provider);
+		return userEmailCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 닉네임 체크
+exports.userNicknameCheck = async function (nickname) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userNicknameResult = await userDao.selectUserNickname(connection, nickname);
+		return userNicknameResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 핸드폰 번호 체크
+exports.userPhoneNumCheck = async function (userPhoneNum, provider) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userPhoneNumCheckResult = await userDao.selectUserPhoneNum(connection, userPhoneNum, provider);
+		return userPhoneNumCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 소셜 연동 확인
+exports.userSocialLinkCheck = async function (userIdx, snsName) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userSocialLinkCheckResult = await userDao.selectUserSocialLink(connection, userIdx, snsName);
+		return userSocialLinkCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 비밀번호 가져오기
+exports.userPasswordCheck = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const passwordCheckResult = await userDao.selectUserPassword(connection, userIdx);
+		return passwordCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 로그인 체크
+exports.userCheck = async function (userEmail, userPassword) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectLoginResultParams = [userEmail, userPassword];
+		const userCheckResult = await userDao.selectLoginResult(connection, selectLoginResultParams);
+		return userCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 오답노트 체크
+exports.reviewNoteCheck = async function (multipleProblemIdx, userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const reviewNoteCheckResult = await userDao.selectUserReviewNoteIdx(connection, multipleProblemIdx, userIdx);
+		return reviewNoteCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 상태 체크
+exports.userStatusCheck = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userStatusCheckResult = await userDao.selectUserStatus(connection, userIdx);
+		return userStatusCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 핸드폰 번호 이메일 확인
+exports.userPhoneNumByEmail = async function (userEmail) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userPhoneNumByEmailResult = await userDao.selectPhoneNumByEmail(connection, userEmail);
+		return userPhoneNumByEmailResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 시험 기록 체크
+exports.userExamRecordCheckIdx = async function (userExamRecordIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const reviewNoteCheckResult = await userDao.selectUserExamRecordIdx(connection, userExamRecordIdx);
+		if (reviewNoteCheckResult.length === 0) throw new errorResponse(baseResponse.EXAMRECORD_NOT_EXIST, 404);
+		if (reviewNoteCheckResult[0].status === "Y") throw new errorResponse(baseResponse.ISDELETED, 400);
+		return reviewNoteCheckResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 인덱스 조회 (이메일)
+exports.userIdxByEmail = async function (userEmail) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userIdxByEmailResult = await userDao.selectUserIdxByEmail(connection, userEmail);
+		if (!userIdxByEmailResult.length) throw new errorResponse(baseResponse.USER_NOT_EXIST, 400); // 문구 수정 -> 없는 계정
+		if (userIdxByEmailResult[0].status == "Y") throw new errorResponse(baseResponse.USER_QUIT, 400); // 문구 수정 -> 탈퇴한 계정
+		return userIdxByEmailResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 인덱스 조회 (핸드폰 번호)
+exports.userIdxByPhoneNum = async function (userPhoneNum) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userIdxByPhoneNumResult = await userDao.selectUserIdxByPhoneNum(connection, userPhoneNum);
+		return userIdxByPhoneNumResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 정보 조회
+exports.userInfo = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userInfoResult = await userDao.selectUserInfo(connection, userIdx);
+		if (userInfoResult.length == 0) throw new errorResponse(baseResponse.USER_NOT_EXIST, 404);
+		if (userInfoResult[0].status == "Y") throw new errorResponse(baseResponse.USER_QUIT, 400);
+		return userInfoResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 닉네임 조회
+exports.selectUserNickname = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userNicknameResult = await userDao.selectUserNickname(connection, userIdx);
+		if (userNicknameResult.length == 0) throw new errorResponse(baseResponse.USER_NOT_EXIST, 404);
+		return userNicknameResult[0];
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 이메일 찾기
+exports.userEmailByPhoneNum = async function (userName, userPhoneNum) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userInfoResult = await userDao.selectUserEmailByPhone(connection, userName, userPhoneNum);
+		return userInfoResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 오답노트 문제별 목록 조회
+exports.reviewNote = async function (userIdx, page, condition, examSortJoinType) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const reviewNoteResult = [];
+		const isAdmin = await adminProvider.adminCheck(userIdx);
+
+		const total = await userDao.selectUserReviewNoteCount(connection, userIdx, condition, examSortJoinType);
+		reviewNoteResult.push({ total: total[0].total });
+		const pageResult = await userDao.selectUserReviewNotePage(connection, userIdx, page, condition, examSortJoinType);
+
+		const reviewNoteList = await Promise.all(
+			pageResult.map(async (v) => {
+				//let year = v.examDate?.getFullYear();
+				let round = isNaN(v.examRound) ? v.examRound : v.examRound + "회차";
+				let Auth = await storeProvider.checkUserExamDetailAuth(v.examDetailIdx, userIdx);
+				let isAuth = isAdmin[0].exist === 1 ? 1 : Auth[0].exist;
+				let companyImage = v.examUrl ? v.examUrl : v.companyUrl;
+				delete v.examUrl;
+				delete v.companyUrl;
+				return {
+					...v,
+					companyImage: companyImage,
+					examSortIdx: undefined, // undefined 는 res에 포함안됨
+					expireAt: undefined,
+					isAuth: isAuth,
+					examRound: v.examYear + "년 " + round,
+					problem: isAuth || v.publicLevel <= 1 ? v.problem : "문제에 대한 권한이 없습니다.", // 자격증이 아니고 권한이 없다면 보여주지 않는다. 자격증이거나 권한이 있으면 문제를 보여준다.
+				};
+			}),
+		);
+		reviewNoteResult.push(reviewNoteList);
+
+		return reviewNoteResult;
+	} catch (error) {
+		"mysql float error", userIdx, page, condition;
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+// 유저 오답노트 실기 문제별 목록 조회 - 실기는 반드시 자격증에 대한 것 밖에 없다.
+exports.subjectiveReviewNote = async function (userIdx, page, condition, examField) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const reviewNoteResult = [];
+		const isAdmin = await adminProvider.adminCheck(userIdx);
+
+		const total = await userDao.selectUserSubjectiveReviewNoteCount(connection, userIdx, condition);
+		reviewNoteResult.push({ total: total[0].total });
+
+		const pageResult = await userDao.selectUserSubjectiveReviewNotePage(connection, userIdx, page, condition);
+		const reviewNoteList = await Promise.all(
+			pageResult.map(async (v) => {
+				//const { examSortIdx, examDate, examRound, isAuth, problem, examDetailIdx } = v;
+
+				//let year = v.examDate?.getFullYear();
+				let year = v.examYear;
+				let round = isNaN(v.examRound) ? v.examRound : v.examRound + "회차";
+				let Auth = await storeProvider.checkUserExamDetailAuth(v.examDetailIdx, userIdx);
+
+				let isAuth = isAdmin[0].exist === 1 ? 1 : Auth[0].exist;
+
+				return {
+					...v,
+					examSortIdx: undefined, // undefined 는 res에 포함안됨
+					domain: undefined,
+					expireAt: undefined,
+					sort: "자격증", // isCert가 존재하면 자격증으로! 없으면 공기업으로!
+					isAuth: isAuth,
+					examRound: year + "년 " + round,
+					problem: isAuth || v.publicLevel <= 1 ? v.problem : "문제에 대한 권한이 없습니다.", // 권한이 없고 publicLevel이 2일 때는 문제도 보여줘선 안된다.
+					publicLevel: v.publicLevel,
+				};
+			}),
+		);
+		reviewNoteResult.push(reviewNoteList);
+
+		return reviewNoteResult;
+	} catch (error) {
+		"mysql float error", userIdx, page, condition;
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 오답노트 특정 문제부터 전체 조회
+exports.reviewNoteSet = async function (userIdx, condition, isCert) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const CERTIFICATIONEXAM = "자격증";
+		let reviewNoteSetResult = [];
+		const examDetailIdxList = [];
+
+		// 문제별, 회차별, 과목별에 대한 problemIdx 네이밍을 통일하기 위해서 일부 코드 수정.
+
+		// 조건에 맞는 저장된 오답노트 정보 가져오기
+		const problemIdxList = await userDao.selectUserReviewNoteAllProblem(connection, userIdx, condition, isCert);
+		if (problemIdxList.length) {
+			const isAdmin = await adminProvider.adminCheck(userIdx);
+
+			// 자격증 목록
+			const selectExamSortListResult = await examDao.selectExamSortList(connection, CERTIFICATIONEXAM);
+			const certificationExamSortList = selectExamSortListResult.map((v) => v.examSortIdx);
+
+			// 객관식 문제에 대한 문항 가져오기
+			const multipleProblemList = problemIdxList.map((v) => v.problemIdx);
+			const questions = await examDao.selectQuestions(connection, multipleProblemList);
+
+			for (var i = 0; i < problemIdxList.length; i++) {
+				if (examDetailIdxList.includes(problemIdxList[i].examDetailIdx)) continue;
+				examDetailIdxList.push(problemIdxList[i].examDetailIdx);
+			}
+
+			reviewNoteSetResult = await Promise.all(
+				problemIdxList.map(async (v) => {
+					let Auth = await storeProvider.checkUserExamDetailAuth(v.examDetailIdx, userIdx);
+					let isAuth = isAdmin[0].exist === 1 ? 1 : Auth[0].exist;
+
+					if (!isAuth && !isAdmin[0].exist) {
+						//권한이 없거나 관리자 권한이 없다면??
+						//권한이 없고 관리자가 아니라면 examDetailIdxList에서 빠져야겠지?
+
+						if (examDetailIdxList.includes(v.examDetailIdx)) {
+							var index = examDetailIdxList.indexOf(v.examDetailIdx);
+							examDetailIdxList.splice(index, 1);
+						}
+					}
+					//const isAuth = 1; // 모두 무료 상태
+					const sort = certificationExamSortList.includes(v.examSortIdx) ? "자격증" : "공기업";
+					if (!(!isAuth && sort == "공기업")) {
+						const problemQuestions = questions.filter((val) => {
+							if (v.problemIdx == val.multipleProblemIdx) return true;
+						});
+						//let year = v.examDate.getFullYear();
+						let year = v.examYear;
+						let examRound = v.examRound;
+						if (!isNaN(examRound)) examRound += "회차";
+						else v.subject = null;
+						examRound = year + "년 " + examRound;
+						delete v.domain;
+						delete v.expireAt;
+
+						const problemInfo = `${v.examName} ${examRound} ${v.problemNum}번`;
+
+						let problem = {
+							...v,
+							problemInfo: problemInfo,
+							reviewNoteIdx: v.reviewNoteIdx,
+							bookMark: v.bookMark,
+							examRound: examRound,
+							isAuth,
+							sort,
+							questions: problemQuestions,
+						};
+						if (!isAuth) {
+							const flag = v.publicLevel;
+							if (flag !== 0) {
+								delete problem.solution;
+							}
+						}
+						return problem;
+					}
+				}),
+			);
+		}
+		await connection.beginTransaction();
+		await connection.commit();
+		return reviewNoteSetResult;
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 주관식 오답노트 특정 문제부터 전체 조회
+exports.subjectiveReviewNoteSet = async function (userIdx, condition) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const CERTIFICATIONEXAM = "자격증";
+		const reviewNoteSetResult = [];
+		const examDetailIdxList = [];
+
+		//문제별 회차별 과목별에 대한 problemIdx 네이밍을 통일하기 위해서 일부 코드 수정.
+		const problemIdxList = await userDao.selectUserSubjectiveReviewNoteAllProblem(connection, userIdx, condition);
+		if (problemIdxList.length) {
+			const selectExamSortListResult = await examDao.selectExamSortList(connection, CERTIFICATIONEXAM); // 자격증 목록
+
+			const certificationExamSortList = selectExamSortListResult.map((v) => v.examSortIdx);
+			//const publicCompanyExamSortList = await examDao.selectExamSortList(connection, PUBLICCOMPANYEXAM); // 공기업 목록
+			const isAdmin = await adminProvider.adminCheck(userIdx);
+
+			for (var i = 0; i < problemIdxList.length; i++) {
+				// let Auth = await storeProvider.checkUserExamDetailAuth(i.examDetailIdx, userIdx);
+				// let isAuth = isAdmin[0].exist === 1 ? 1 : Auth[0].exist;
+				if (examDetailIdxList.includes(problemIdxList[i].examDetailIdx)) continue;
+
+				examDetailIdxList.push(problemIdxList[i].examDetailIdx);
+			}
+
+			await problemIdxList.reduce(async (prev, v, index, array) => {
+				let Auth = await storeProvider.checkUserExamDetailAuth(v.examDetailIdx, userIdx);
+				let isAuth = isAdmin[0].exist === 1 ? 1 : Auth[0].exist;
+
+				// if (isAuth && isAdmin[0].exist != 1 && prev.examDetailIdx != v.examDetailIdx) {
+				// 	examDetailIdxList.push(v.examDetailIdx);
+				// }
+				if (!isAuth && !isAdmin[0].exist) {
+					//권한이 없거나 관리자 권한이 없다면??
+					//권한이 없고 관리자가 아니라면 examDetailIdxList에서 빠져야겠지?
+
+					if (examDetailIdxList.includes(v.examDetailIdx)) {
+						var index = examDetailIdxList.indexOf(v.examDetailIdx);
+						examDetailIdxList.splice(index, 1);
+					}
+				}
+
+				// const isAuth = isAdmin[0].exist === 1 || (v.expireAt && v.expireAt !== "null") ? 1 : 0;
+				//const isAuth = 1; // 모두 무료 상태
+				const sort = certificationExamSortList.includes(v.examSortIdx) ? "자격증" : "공기업";
+				if (!(!isAuth && sort == "공기업")) {
+					//(권한이 없고 공기업이라면)이 아니라면 == 권한이 있거나 공기업이 아니라면
+					//let problemInfo = await examDao.selectMultipleProblemInfo(connection, v.problemIdx);
+					//let questions = await examDao.selectQuestion(connection, v.problemIdx);
+					let year = v.examDate.getFullYear();
+
+					let examRound = v.examRound;
+
+					if (!isNaN(examRound)) examRound += "회차";
+					else v.subject = null;
+					examRound = year + "년 " + examRound;
+					delete v.domain;
+					delete v.expireAt;
+
+					let problem = {
+						...v,
+						reviewNoteIdx: v.reviewNoteIdx,
+						bookMark: v.bookMark,
+						examRound: examRound,
+						isAuth,
+						sort,
+					};
+					if (!isAuth) {
+						const flag = v.publicLevel;
+						if (flag !== 0) {
+							delete problem.solution;
+						}
+					}
+
+					reviewNoteSetResult.push(problem);
+				}
+			}, Promise.resolve());
+		}
+
+		await connection.beginTransaction();
+		// examDetailIdxList.map(async (v) => {
+		// 	await userDao.createUserAuthLogDetailIdx(connection, userIdx, v);
+		// });
+		await connection.commit();
+		return reviewNoteSetResult;
+	} catch (error) {
+		await connection.rollback();
+
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 오답노트 시험 조회
+exports.reviewNoteExam = async function (examSort, userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const reviewNoteExamResult = await userDao.selectUserReviewNoteExam(connection, examSort, userIdx);
+		return reviewNoteExamResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+// 유저 오답노트 시험 회차 조회
+exports.reviewNoteExamDetail = async function (userIdx, examIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const reviewNoteExamDetailResult = await userDao.selectUserReviewNoteList(connection, userIdx, examIdx);
+		return reviewNoteExamDetailResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 오답노트 시험 회차별 조회
+exports.reviewNoteExamList = async function (userIdx, examSort, examField) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let reviewNoteList = [];
+		let largeExamIdx;
+		let sort = examSort;
+		let examSortJoinType = "e.examSortIdx ";
+
+		if (examField && examField != "전체") {
+			largeExamIdx = await examDao.selectLargeExamIdx(connection, sort);
+			largeExamIdx = largeExamIdx[0].examSortIdx;
+		}
+
+		if (sort == "공기업" || sort == "대기업") {
+			examSortJoinType = `ce.examSortIdx_S `;
+		}
+
+		const reviewNoteExamResult = await userDao.selectUserReviewNoteExam(
+			connection,
+			sort,
+			userIdx,
+			examField,
+			largeExamIdx,
+			examSortJoinType,
+		);
+		const isAdmin = await adminProvider.adminCheck(userIdx);
+
+		await Promise.all(
+			reviewNoteExamResult.map(async (v, i, a) => {
+				const reviewNoteExamDetailResult = await userDao.selectUserReviewNoteList(connection, userIdx, v.examIdx);
+				let companyImage = v.examUrl ? v.examUrl : v.companyUrl;
+				delete v.examUrl;
+				delete v.companyUrl;
+				return Promise.all(
+					reviewNoteExamDetailResult.map(async (o, i, a) => {
+						const authResult = await storeProvider.checkUserExamDetailAuth(o.examDetailIdx, userIdx);
+						const isAuth = isAdmin[0].exist === 1 ? 1 : authResult[0].exist;
+
+						const examDetail = {
+							examDetailIdx: o.examDetailIdx,
+							round: o.round,
+							count: o.count,
+							isAuth,
+						};
+						return examDetail;
+					}),
+				).then(function (value) {
+					const reviewNoteInfo = {
+						examIdx: v.examIdx,
+						examName: v.examName,
+						companyImage: companyImage,
+						examDetailList: value,
+					};
+					reviewNoteList.push(reviewNoteInfo);
+				});
+			}),
+		);
+
+		return reviewNoteList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 주관식 오답노트 시험 목록 회차별 조회
+exports.subjectiveReviewNoteExamList = async function (userIdx, examSort, examField) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let reviewNoteList = [];
+		let largeExamIdx; // 자격증 공기업, 공무원 등을 예기한다.
+		if (examField) {
+			largeExamIdx = await examDao.selectLargeExamIdx(connection, examSort);
+			largeExamIdx = largeExamIdx[0].examSortIdx;
+		}
+		const subjectiveReviewNoteExamResult = await userDao.selectSubjectiveUserReviewNoteExam(
+			connection,
+			examSort,
+			userIdx,
+			examField,
+			largeExamIdx,
+		);
+		const isAdmin = await adminProvider.adminCheck(userIdx);
+		await Promise.all(
+			subjectiveReviewNoteExamResult.map(async (v, i, a) => {
+				const subjectiveReviewNoteExamDetailResult = await userDao.selectUserSubjectiveReviewNoteList(
+					connection,
+					userIdx,
+					v.examIdx,
+				);
+				return Promise.all(
+					subjectiveReviewNoteExamDetailResult.map(async (o, i, a) => {
+						const authResult = await storeProvider.checkUserExamDetailAuth(o.examDetailIdx, userIdx);
+						const isAuth = isAdmin[0].exist === 1 ? 1 : authResult[0].exist;
+						//const isAuth = 1; // 모두 무료 상태
+						const examDetail = {
+							examDetailIdx: o.examDetailIdx,
+							round: o.round,
+							count: o.count,
+							isAuth,
+						};
+						return examDetail;
+					}),
+				).then(function (value) {
+					const reviewNoteInfo = {
+						examIdx: v.examIdx,
+						examName: v.examName,
+						companyImage: v.companyImage,
+						examDetailList: value,
+					};
+					reviewNoteList.push(reviewNoteInfo);
+				});
+			}),
+		);
+
+		return reviewNoteList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 오답노트 시험 목록 과목별 조회
+exports.reviewNoteSubjectList = async function (userIdx, examSort, examField) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let reviewNoteSubjectList = [];
+		let largeExamIdx;
+		let sort = examSort;
+		let examSortJoinType = "e.examSortIdx ";
+
+		if (examField && examField != "전체") {
+			largeExamIdx = await examDao.selectLargeExamIdx(connection, sort);
+			largeExamIdx = largeExamIdx[0].examSortIdx;
+		}
+
+		if (sort == "공기업" || sort == "대기업") {
+			examSortJoinType = `ce.examSortIdx_S `;
+		}
+
+		const reviewNoteExamResult = await userDao.selectUserReviewNoteExam(
+			connection,
+			sort,
+			userIdx,
+			examField,
+			largeExamIdx,
+			examSortJoinType,
+		);
+
+		//reviewNoteExamResult에 시험인덱스, 시험이름, 회사이미지를 결과로넘겨준다. 여기까지는 똑같이
+		const isAdmin = await adminProvider.adminCheck(userIdx); //계정이 있는지 검사.
+		await Promise.all(
+			reviewNoteExamResult.map(async (v, i, a) => {
+				const reviewNoteSubjectResult = await userDao.selectUserReviewNoteSubjectList(connection, userIdx, v.examIdx);
+				const examSubjectCount = await userDao.selectUserReviewNoteSubjectCount(connection, userIdx, v.examIdx);
+				let companyImage = v.examUrl ? v.examUrl : v.companyUrl;
+				return Promise.all(
+					reviewNoteSubjectResult.map(async (o, j, a) => {
+						const authResult = await storeProvider.checkUserExamDetailAuth(o.examDetailIdx, userIdx);
+						const isAuth = isAdmin[0].exist === 1 ? 1 : authResult[0].exist;
+						return isAuth;
+					}),
+				).then(function (value) {
+					const reviewNoteSubjectInfo = {
+						examIdx: v.examIdx,
+						examName: v.examName,
+						companyImage: companyImage,
+						isAuth: value,
+						examSubjectList: examSubjectCount,
+					};
+
+					reviewNoteSubjectList.push(reviewNoteSubjectInfo);
+				});
+			}),
+		);
+
+		return reviewNoteSubjectList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 주관식 오답노트 시험 목록 과목별 조회
+exports.subjectiveReviewNoteSubjectList = async function (userIdx, examSort, examField) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let reviewNoteSubjectList = [];
+		let largeExamIdx;
+		if (examField) {
+			largeExamIdx = await examDao.selectLargeExamIdx(connection, examSort);
+			largeExamIdx = largeExamIdx[0].examSortIdx;
+		}
+
+		const reviewNoteExamResult = await userDao.selectSubjectiveUserReviewNoteExam(
+			connection,
+			examSort,
+			userIdx,
+			examField,
+			largeExamIdx,
+		);
+		//reviewNoteExamResult에 시험인덱스, 시험이름, 회사이미지를 결과로넘겨준다. 여기까지는 똑같이
+		const isAdmin = await adminProvider.adminCheck(userIdx); //계정이 있는지 검사.
+		await Promise.all(
+			reviewNoteExamResult.map(async (v, i, a) => {
+				const reviewNoteSubjectResult = await userDao.selectUserSubjectiveReviewNoteSubjectList(
+					connection,
+					userIdx,
+					v.examIdx,
+				);
+				const examSubjectCount = await userDao.selectUserSubjectiveReviewNoteSubjectCount(
+					connection,
+					userIdx,
+					v.examIdx,
+				);
+
+				return Promise.all(
+					reviewNoteSubjectResult.map(async (o, j, a) => {
+						const authResult = await storeProvider.checkUserExamDetailAuth(o.examDetailIdx, userIdx);
+						const isAuth = isAdmin[0].exist === 1 ? 1 : authResult[0].exist;
+						return isAuth;
+					}),
+				).then(function (value) {
+					const reviewNoteSubjectInfo = {
+						examIdx: v.examIdx,
+						examName: v.examName,
+						companyImage: v.companyImage,
+						isAuth: value,
+						examSubjectList: examSubjectCount,
+					};
+
+					reviewNoteSubjectList.push(reviewNoteSubjectInfo);
+				});
+			}),
+		);
+		return reviewNoteSubjectList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 오답노트 시험 목록 조회
+exports.userReviewNoteList = async function (userIdx, examSort) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let examInfo = [];
+		const reviewNoteInfo = await userDao.selectUserReviewNoteExam(connection, examSort, userIdx);
+		for (let i = 0; i < reviewNoteInfo.length; i++) {
+			let examInfoTmp = {
+				examIdx: reviewNoteInfo[i].examIdx,
+				examName: reviewNoteInfo[i].examName,
+			};
+			let examDetailList = new Array();
+			examInfo.push(examInfoTmp);
+			const detailList = await userDao.selectUserReviewNoteList(connection, userIdx, reviewNoteInfo[i].examIdx);
+			for (let j = 0; j < detailList.length; j++) {
+				let detailInfo = {
+					examDetailIdx: detailList[j].examDetailIdx,
+					list: detailList[j].list,
+				};
+				examDetailList.push(detailInfo);
+			}
+
+			examInfo[i].examDetailList = examDetailList;
+		}
+		return resultResponse(baseResponse.SUCCESS, examInfo);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 오답노트 시험 문제 조회 (과목별)
+exports.reviewNoteExamSubject = async function (userIdx, examIdx, examSubjectIdx, isAuth) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let result = {};
+		let problems = [];
+
+		//오답문제 가져올 리스트
+		const multipleProblemList = [];
+
+		//해당 과목별 문제들 리스트
+		const subjectProblems = [];
+
+		//한 multipleProblemIdx에 대한 선지 리스트. 이를 후에 subJectProblems와 연동시킬것.
+		const questions = [];
+
+		//오답노트 문제 목록에 대한 정보 (과목)
+		//객관식 문제들에 대한 multipleProblemIdx와 examDetailIndex를 배열로 가져온다.
+		const reviewNoteSubjectMultipleProblem = await userDao.selectUserReviewNoteSubjectProblem(
+			connection,
+			examSubjectIdx,
+			examIdx,
+			userIdx,
+		);
+		if (reviewNoteSubjectMultipleProblem.length < 1) {
+			throw new errorResponse(baseResponse.REVIEWNOTE_NOT_EXIST, 404);
+		}
+
+		await Promise.all(
+			reviewNoteSubjectMultipleProblem.map(async (v, i, a) => {
+				multipleProblemList.push(v.multipleProblemIdx);
+				return multipleProblemList;
+			}),
+		).then(async function (value) {
+			//subjectProblems는 과목에 대한 문제들을 가리킴.
+			for (var i = 0; i < value[0].length; i++) {
+				const problem = await examDao.selectSubjectQuestion(connection, userIdx, examIdx, value[0][i]);
+				subjectProblems.push(problem[0]);
+			}
+			const ExamSubjectName = await examDao.selectExamSubjectNamebyIdx(connection, examSubjectIdx);
+			let subjectInfo = {
+				examIdx: examIdx,
+				examSubjectIdx: examSubjectIdx,
+				examSubjectName: ExamSubjectName[0].ExamSubjectName,
+				problemCount: multipleProblemList.length,
+			};
+
+			//객관식 문제 인덱스에 해당하는 선지들을 가져온다.
+
+			for (var i = 0; i < value[0].length; i++) {
+				const question = await examDao.selectQuestion(connection, value[0][i]);
+				questions.push(question);
+			}
+
+			// const questions = await examDao.selectQuestions(connection, multipleProblemList);
+
+			//선지의 인덱스와 발문의 인덱스가 같은 것끼리 새롭게 배열로 만들어야 한다.
+
+			await subjectProblems.reduce(async (prev, v, index, array) => {
+				const problemQuestions = questions.filter((val) => {
+					if (v?.multipleProblemIdx == val[0]?.multipleProblemIdx) return true;
+					//선지의 MPI와 발문의 mpi가 일치할시 선지를 리스트화해서 묶어주기 위한 과정.
+				});
+
+				let examRound = v.examRound;
+				if (!isNaN(examRound)) examRound += "회차";
+				examRound = v.examYear + "년 " + examRound;
+				const problemInfo = `${v.examName} ${examRound} ${v.problemNum}번`;
+
+				let problem = {
+					problemInfo: problemInfo,
+					reviewNoteIdx: v.reviewNoteIdx,
+					examIdx: examIdx,
+					bookMark: v.bookMark,
+					problemIdx: v.multipleProblemIdx,
+					examHistory: v.examHistory,
+					problemNum: v.problemNum,
+					problem: v.problem,
+					answerNum: v.answerNum,
+					questions: problemQuestions[0], // 선지를 얘기한다.
+					provisionNum: v.provisionNum,
+					solution: v.solution,
+					problemUrl: v.problemUrl,
+					lectureUrl: v.lectureUrl,
+					subjectInfo: v.subject,
+					isKatex: v.isKatex,
+					subjectNum: v.examSubjectNum,
+					isMemo: v.isMemo,
+					isAuth: isAuth,
+				};
+
+				problems.push(problem);
+				if (!isAuth) delete problem.solution;
+			}, Promise.resolve());
+
+			result = { subjectInfo, problems };
+		});
+
+		//  reviewNoteSubjectMultipleProblem.map(async (v, i, a) => {
+		// 	//문제들에 대해서 isAuth를 조사한다.
+		// 	const authResult = await storeProvider.checkUserExamDetailAuth(v.examDetailIdx, userIdx);
+		// 	const isAuth = isAdmin[0].exist === 1 ? 1 : authResult[0].exist;
+		// 	if (isAuth == 1) {
+		// 		multipleProblemList.push(v.multipleProblemIdx)
+		// 	}
+		// 	return multipleProblemList;
+		// })
+
+		// const examSubjectName = await examDao.selectExamSubjectNamebyIdx(connection, examSubjectIdx);
+		// let subjectInfo = {
+		// 	examIdx: examIdx,
+		// 	examSubjectIdx: examSubjectIdx,
+		// 	examSubjectName: examSubjectName,
+		// 	problemCount: multipleProblemList.length,
+		// }
+
+		// const questions = await examDao.selectQuestions(connection, multipleProblemList);
+
+		return result;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 주관식 오답노트 시험 문제 조회 (과목별)
+exports.subjectiveReviewNoteExamSubject = async function (userIdx, examIdx, examSubjectIdx, isAuth) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let result = {};
+		let problems = [];
+
+		//오답문제 가져올 리스트
+		const subjectiveProblemList = [];
+
+		//해당 과목별 주관식 문제들 리스트
+		const subjectProblems = [];
+
+		//오답노트 문제 목록에 대한 정보 (과목)
+		//주관식 문제들에 대한 multipleProblemIdx와 examDetailIndex를 배열로 가져온다.
+		const reviewNoteSubjectSubjectiveProblem = await userDao.selectUserSubjectiveReviewNoteSubjectProblem(
+			connection,
+			examSubjectIdx,
+			examIdx,
+			userIdx,
+		);
+
+		await Promise.all(
+			reviewNoteSubjectSubjectiveProblem.map(async (v, i, a) => {
+				subjectiveProblemList.push(v.subjectiveProblemIdx);
+				return subjectiveProblemList;
+			}),
+		).then(async function (value) {
+			//subjectProblems는 과목에 대한 문제들을 가리킴.
+			for (var i = 0; i < value[0].length; i++) {
+				const problem = await examDao.selectSubjectiveProblem(connection, userIdx, examIdx, value[0][i]);
+				subjectProblems.push(problem[0]);
+			}
+
+			const ExamSubjectName = await examDao.selectExamSubjectNamebyIdx(connection, examSubjectIdx);
+			let subjectInfo = {
+				examIdx: examIdx,
+				examSubjectIdx: examSubjectIdx,
+				examSubjectName: ExamSubjectName[0].ExamSubjectName,
+				problemCount: subjectiveProblemList.length,
+			};
+
+			await subjectProblems.reduce(async (prev, v, index, array) => {
+				let examRound = v.examRound;
+				if (!isNaN(examRound)) {
+					examRound += "회차";
+				}
+				examRound = v.examYear + "년 " + examRound;
+				let problemInfo = `${v.examName} ${examRound} ${v.problemNum}번`;
+
+				let problem = {
+					problemInfo: problemInfo,
+					reviewNoteIdx: v.reviewNoteIdx,
+					examIdx: examIdx,
+					bookMark: v.bookMark,
+					problemIdx: v.subjectiveProblemIdx,
+					problemNum: v.problemNum,
+					problem: v.problem,
+					examHistory: v.examHistory,
+					solution: v.solution,
+					subjectInfo: v.subject,
+					isKatex: v.isKatex,
+					problemUrl: v.problemUrl,
+					lectureUrl: v.lectureUrl,
+					subjectNum: v.examSubjectNum,
+					isMemo: v.isMemo,
+					isAuth: isAuth,
+					subjectiveLabel: v.subjectiveLabel,
+				};
+				problems.push(problem);
+				if (!isAuth) delete problem.solution;
+			}, Promise.resolve());
+
+			result = { subjectInfo, problems };
+		});
+
+		return result;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 오답노트 시험 문제 조회 (회차)
+exports.reviewNoteExamRound = async function (userIdx, examDetailIdx, isAuth) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let problems = [];
+		const multipleProblemList = [];
+		// 시험 정보
+		let examDetail = await examDao.selectExamDetailInfo(connection, examDetailIdx);
+		let problemCount = await userDao.selectReviewNoteCount(connection, userIdx, examDetailIdx);
+
+		//let year = examDetail[0].examDate.getFullYear();
+		let year = examDetail[0].examYear;
+		let examRound = examDetail[0].examRound;
+
+		if (!isNaN(examRound)) examRound += "회차";
+		else examRound[0].subject = null;
+		examRound = year + "년 " + examRound;
+
+		let examInfo = {
+			...examDetail[0],
+			round: examDetail[0].examRound,
+			examRound,
+			problemCount: problemCount[0].count,
+			isAuth: isAuth,
+		};
+
+		const examProblems = await userDao.selectUserReviewNoteProblem(connection, userIdx, examDetailIdx);
+
+		if (examProblems.length) {
+			examProblems.forEach((obj) => multipleProblemList.push(obj.multipleProblemIdx));
+			const questions = await examDao.selectQuestions(connection, multipleProblemList);
+			examProblems.forEach((obj) => {
+				const problemQuestions = questions.filter((val) => {
+					if (obj.multipleProblemIdx == val.multipleProblemIdx) return true;
+				});
+				let problemInfo = `${examDetail[0].examName} ${examRound} ${obj.problemNum}번`;
+				let problem = {
+					...obj,
+					subjectNum: obj.examSubjectNum,
+					subjectInfo: obj.subject,
+					problemInfo: problemInfo,
+					problemIdx: obj.multipleProblemIdx,
+					questions: problemQuestions,
+					isAuth: isAuth,
+				};
+
+				if (!isAuth) delete problem.solution;
+				problems.push(problem);
+			});
+		}
+		return { examInfo, problems };
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 오답노트 주관식 시험 문제 조회 (회차)
+exports.subjectiveReviewNoteExamRound = async function (userIdx, examDetailIdx, isAuth) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let problems = [];
+		// 시험 정보
+		//isAuth = 1; // 모두 무료 상태
+		let examDetail = await examDao.selectExamDetailInfo(connection, examDetailIdx);
+		// console.log(examDetail);
+		let problemCount = await userDao.selectSubjectiveReviewNoteCount(connection, userIdx, examDetailIdx);
+
+		//let year = examDetail[0].examDate.getFullYear();
+		let year = examDetail[0].examYear;
+		let examRound = examDetail[0].examRound;
+		if (!isNaN(examRound)) examRound += "회차";
+		else examRound[0].subject = null;
+		examRound = year + "년 " + examRound;
+
+		let examInfo = {
+			examIdx: examDetail[0].examIdx,
+			examDetailIdx: examDetail[0].examDetailIdx,
+			examName: examDetail[0].examName,
+			date: examDetail[0].date,
+			examRound: examRound,
+			round: examDetail[0].examRound,
+			problemCount: problemCount[0].count,
+			isAuth: isAuth,
+		};
+		// 오답노트 시험문제
+		// 회차별로 문제들을 가져와서 뿌려준다.
+		const examProblems = await userDao.selectUserSubjectiveReviewNoteProblem(connection, userIdx, examDetailIdx);
+		if (examProblems.length) {
+			await examProblems.reduce(async (prev, v, index, array) => {
+				let problemInfo = `${examInfo.examName} ${examInfo.examRound} ${v.problemNum}번`;
+				let problem = {
+					problemInfo: problemInfo,
+					reviewNoteIdx: v.reviewNoteIdx,
+					bookMark: v.bookMark,
+					problemIdx: v.multipleProblemIdx,
+					problemNum: v.problemNum,
+					problem: v.problem,
+					examHistory: v.examHistory,
+					answerNum: v.answerNum,
+					solution: v.solution,
+					subjectInfo: v.subject,
+					problemUrl: v.problemUrl,
+					lectureUrl: v.lectureUrl,
+					isKatex: v.isKatex,
+					subjectNum: v.examSubjectNum,
+					isMemo: v.isMemo,
+					isAuth: isAuth,
+					subjectiveLabel: v.subjectiveLabel,
+				};
+				if (!isAuth) delete problem.solution;
+				problems.push(problem);
+			}, Promise.resolve());
+		}
+		return { examInfo, problems };
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 오답노트 메모 조회
+exports.reviewNoteMemo = async function (reviewNoteIdx, userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const reviewNoteMemoResult = await userDao.selectUserReviewNoteMemo(connection, reviewNoteIdx, userIdx);
+		return reviewNoteMemoResult[0];
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 시험기록 목록 조회 과락에 따른 합격여부도 같이
+exports.userExamRecordList = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let examInfoList = new Array();
+		const result = await userDao.selectUserExamRecord(connection, userIdx); // 시험 인덱스, 시험 이름, 시험 합격 기준 점수
+
+		if (result.length == 0) {
+			return basickResponse(baseResponse.SUCCESS);
+		}
+		for (let i = 0; i < result.length; i++) {
+			let recordInfoList = [];
+			const { examIdx } = result[i];
+			const examRecordResult = await userDao.selectUserExamRecordList(connection, userIdx, examIdx);
+
+			const { examSortRef: examMediumSort } = await examDao.selectExamSortRefIdx(connection, examIdx);
+			const { examSortRef: examLargeSortIdx } = await examDao.selectLargeExamName(connection, examMediumSort);
+			const examType = await examDao.selectExamSortRefName(connection, examLargeSortIdx); // 자격증, 공기업, 대기업
+
+			for (let j = 0; j < examRecordResult.length; j++) {
+				let isPass;
+				if (examRecordResult[j].score.includes("점")) isPass = examRecordResult[j].isPass == 1 ? "Y" : "N";
+				else isPass = "X";
+
+				let recordInfo = {
+					...examRecordResult[j],
+					isPass: isPass,
+				};
+				recordInfoList.push(recordInfo);
+			}
+			const examInfo = {
+				examIdx: result[i].examIdx,
+				examName: result[i].examName,
+				passScore: result[i].passScore,
+				recordList: recordInfoList,
+				examType,
+			};
+
+			if (examInfo.recordList.length == 0) {
+				continue;
+			}
+			examInfoList.push(examInfo);
+		}
+		return resultResponse(baseResponse.SUCCESS, examInfoList);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 시험 상세기록  조회
+exports.userExamDetailRecord = async function (userExamRecordIdx) {
+	let examDetail;
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let getProblemListRows, getProblem;
+		let subject = [];
+		let answerCount = 0;
+		// 시험 정보
+
+		examDetail = await userDao.selectUserExamRecordInfo(connection, userExamRecordIdx);
+		if (examDetail.length == 0) return basickResponse(baseResponse.EXAMRECORD_NOT_EXIST);
+
+		let examInfo = {
+			examIdx: examDetail[0].examIdx,
+			examDetailIdx: examDetail[0].examDetailIdx,
+			examName: examDetail[0].examName,
+			date: examDetail[0].date,
+			examRound: examDetail[0].examRound,
+		};
+
+		// let Auth = await storeDao.checkUserExamDetailAuth(connection, examInfo.examDetailIdx, userIdx);
+
+		//isAuth를 판단하여 만약 권한이 없으면 solution을 보내주지 않는다.
+		// const isAuth = isAdmin[0].exist === 1 ? 1 : Auth[0].exist;
+
+		const examDivision = await examProvider.getExamDivision(examDetail[0].examIdx, "L");
+		if (examDivision[0].examSortName == "자격증") {
+			const examSubject = await examDao.selectExamSubject(connection, examDetail[0].examIdx);
+			const answerResult = await userDao.selectUserExamCorrectRecord(connection, userExamRecordIdx);
+			if (answerResult.length == 0) return basickResponse(baseResponse.CORRECT_EMPTY);
+
+			subject = [...examSubject];
+			for (let i = 0; i < examSubject.length; i++) {
+				let problems = [];
+
+				getProblem = await userDao.selectUserExamRecordSubject(
+					connection,
+					userExamRecordIdx,
+					examSubject[i].examSubjectIdx,
+				);
+				for (let j = 0; j < getProblem.length; j++) {
+					let questions = await examDao.selectQuestion(connection, getProblem[j].problemIdx);
+					let problem = {
+						...getProblem[j],
+						questions,
+						userAnswer: answerResult[answerCount].userAnswer,
+						isCorrect: answerResult[answerCount].isCorrect,
+					};
+					problems.push(problem);
+					answerCount++;
+				}
+				subject[i].problems = problems;
+			}
+		} else {
+			let problems = await userDao.selectUserExamRecordNonSubject(connection, userExamRecordIdx);
+			for (let i = 0; i < problems.length; i++) {
+				problems[i].questions = await examDao.selectQuestion(connection, problems[i].problemIdx);
+			}
+			subject.push({ problems: problems });
+		}
+
+		getProblemListRows = { examInfo, subject };
+		return resultResponse(baseResponse.SUCCESS, getProblemListRows);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 시험 상세기록  조회 (auth가 없는 유저에겐 solution 제공 x)
+exports.userExamDetailRecordFree = async function (userExamRecordIdx) {
+	let examDetail;
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let getProblemListRows, getProblem;
+		let subject = new Array();
+		let answerCount = 0;
+		// 시험 정보
+
+		examDetail = await userDao.selectUserExamRecordInfo(connection, userExamRecordIdx);
+		if (examDetail.length == 0) return basickResponse(baseResponse.EXAMRECORD_NOT_EXIST);
+
+		let examInfo = {
+			examIdx: examDetail[0].examIdx,
+			examDetailIdx: examDetail[0].examDetailIdx,
+			examName: examDetail[0].examName,
+			date: examDetail[0].date,
+			examRound: examDetail[0].examRound,
+		};
+
+		const examDivision = await examProvider.getExamDivision(examDetail[0].examIdx, "L");
+		if (examDivision[0].examSortName == "자격증") {
+			const examSubject = await examDao.selectExamSubject(connection, examDetail[0].examIdx);
+			const answerResult = await userDao.selectUserExamCorrectRecord(connection, userExamRecordIdx);
+			if (answerResult.length == 0) return basickResponse(baseResponse.CORRECT_EMPTY);
+			for (let i = 0; i < examSubject.length; i++) {
+				let problems = [];
+				subject.push(examSubject[i]);
+
+				getProblem = await userDao.selectUserExamRecordSubject(
+					connection,
+					userExamRecordIdx,
+					examSubject[i].examSubjectIdx,
+				);
+
+				for (let j = 0; j < getProblem.length; j++) {
+					let questions = await examDao.selectQuestion(connection, getProblem[j].problemIdx);
+					//var frequency = await examDao.selectProblemFrequency(connection, getProblem[i].categoryIdx) // 문제별 출제 빈도
+					//var solutionImages = await examDao.selectSolutionImage(connection, getProblem[i].multipleProblemIdx) 삭제 예정
+					delete getProblem[j].solution;
+
+					let problem = {
+						...getProblem[j],
+						questions: questions,
+						userAnswer: answerResult[answerCount].answerNum,
+						isCorrect: answerResult[answerCount].isCorrect,
+						//solutionImages : solutionImages,
+						//correctRate : getProblem[i].correctPercentage
+						//frequency : frequency
+						// 유저가 적은 답안, 맞았는지 틀렸는지
+					};
+					problems.push(problem);
+					answerCount++;
+				}
+				subject[i].problems = problems;
+			}
+		} else {
+			// 공기업에겐 어떤 자료도 주어선 안된다.
+			return basickResponse(baseResponse.USERAUTH_NOT_EXIST);
+			// let problems = await userDao.selectUserExamRecordNonSubject(connection, userExamRecordIdx);
+			// for (let i = 0; i < problems.length; i++) {
+			// 	problems[i].questions = await examDao.selectQuestion(connection, problems[i].problemIdx);
+			// }
+			// subject.push({ problems: problems });
+		}
+		// 시험 과목별 문제
+		//const subjectInfo = await userDao.selectUserExamRecordSubject(connection, examDetail[0].examDetailIdx);
+
+		// for (let i = 0; i < subjectInfo.length; i++) {
+		// 	subjectTmp = await examDao.selectExamSubjectByIdx(
+		// 		connection,
+		// 		examDetail[0].examIdx,
+		// 		subjectInfo[i].examSubjectIdx,
+		// 	); // 과목 인덱스, 정보 반환
+		// 	subjectsTmp.push(subjectTmp[0]);
+		// }
+		// examSubject = subjectsTmp;
+		getProblemListRows = { examInfo, subject };
+		return resultResponse(baseResponse.SUCCESS, getProblemListRows);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 작성글 조회
+exports.userBoard = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userBoardResult = await userDao.selectUserBoard(connection, userIdx);
+		return resultResponse(baseResponse.SUCCESS, userBoardResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 오류신고 조회
+exports.userProblemError = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userProblemErrorResult = await userDao.selectUserProblemError(connection, userIdx);
+		return userProblemErrorResult;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 사이트 오류신고 조회
+exports.siteErrorReport = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const selectSiteErrorReportResult = await userDao.selectSiteErrorReport(connection);
+		return resultResponse(baseResponse.SUCCESS, selectSiteErrorReportResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 시험기록 채점결과 조회
+exports.userExamRecord = async function (userExamRecordIdx, useSubject) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let gradeResult = new Object();
+		const recordResult = await userDao.selectExamRecordByExamDetail(connection, userExamRecordIdx);
+		gradeResult.examGrade = recordResult[0];
+		if (useSubject) {
+			gradeResult.subjectGrade = await userDao.selectExamRecordBySubject(connection, userExamRecordIdx);
+			for (const object of gradeResult.subjectGrade) {
+				if (object.score < object.passScore) {
+					object.isPass = 0;
+					gradeResult.examGrade.isPass = 0;
+				} else object.isPass = 1;
+			}
+		}
+		return resultResponse(baseResponse.SUCCESS, gradeResult);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+//유저 장바구니 조회
+exports.userCart = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userCart = await userDao.selectUserCart(connection, userIdx);
+		return userCart;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+//유저의 구매내역 조회
+exports.userPurchaseHistory = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userPurchaseHistory = await userDao.selectUserPurchaseHistory(connection, userIdx);
+		userPurchaseHistory.forEach((obj) => {
+			if (!obj.totalRefund) obj.totalRefund = 0;
+			obj.paymentPrice = obj.totalPrice + obj.totalPoint;
+			obj.pay_method = storeProvider.changePayMethodToText(obj.pay_method);
+			obj.level = storeProvider.changePayStatusToText(obj.level);
+		});
+		return userPurchaseHistory;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 상품 권한 조회
+exports.userProductAuth = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userProductAuth = await userDao.selectAuthUserProduct(connection, userIdx);
+		return resultResponse(baseResponse.SUCCESS, userProductAuth);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+//유저의 상세 구매내역 - 상품별 주문 정보 조회
+// exports.userPurchaseHistoryDetail = async function (paymentIdx) {
+// 	const connection = await pool.getConnection(async (conn) => conn);
+// 	try {
+// 		const orderPerProductResult = await storeDao.selectOrderPerProduct(connection, paymentIdx);
+// 		const paymentInfo = await storeDao.selectPaymentFullInfo(connection, paymentIdx);
+// 		const productList = orderPerProductResult.map((obj) => {
+// 			obj.status = !obj.isRefund ? "결제완료" : "환불완료";
+// 			delete obj.isRefund;
+// 			return obj;
+// 		});
+// 		let resObj = {
+// 			productList: productList,
+// 			...paymentInfo[0],
+// 			orderNum: paymentInfo[0].paymentIdx,
+// 			pay_method: storeProvider.changePayMethodToText(paymentInfo[0].pay_method),
+// 			status: storeProvider.changePayStatusToText(paymentInfo[0].level),
+// 			productCount: productList.length,
+// 		};
+// 		return resultResponse(baseResponse.SUCCESS, resObj);
+// 	} catch (error) {
+//
+// 		throw error;
+// 	} finally {
+// 		connection.release();
+// 	}
+// };
+
+// 유저 환불내역 조회
+exports.userRefundHistory = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userRefundHistory = await userDao.selectUserRefundHistory(connection, userIdx);
+		userRefundHistory.map((obj) => {
+			obj.pay_method = storeProvider.changePayMethodToText(obj.pay_method);
+			return obj;
+		});
+
+		return resultResponse(baseResponse.SUCCESS, userRefundHistory);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 나의 시험 조회
+exports.userProduct = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let userProduct = await userDao.selectAuthUserProduct(connection, userIdx);
+
+		for (let i = 0; i < userProduct.length; i++) {
+			let category = await storeDao.selectProductTopCategory(connection, userProduct[i].productIdx);
+			let examDetails = await storeDao.selectProductExamDetail(connection, userProduct[i].productIdx);
+
+			let cert = [];
+			let public = [];
+			examDetails.map((v) => {
+				v.isPublic ? public.push(v) : cert.push(v);
+			});
+			userProduct[i].category = category[0].productName;
+			userProduct[i].examDetails = { cert: cert, public: public };
+			await Promise.all(
+				cert.map(async (v, i, o) => {
+					if (v.examIdx) {
+						let { subjectiveLabel } = await examDao.selectSubjectiveOfExam(connection, v.examIdx);
+						v.subjectiveLabel = subjectiveLabel;
+					}
+				}),
+			);
+		}
+
+		//console.log(userProduct);
+		return userProduct;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 포인트 로그 조회
+exports.userPointLogs = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const result = {};
+		const userPointLogList = await userDao.selectUserPointLog(connection, userIdx);
+		const userInfo = await this.userInfo(userIdx);
+
+		result.pointLog = userPointLogList;
+		result.totalPoint = userInfo[0].point;
+
+		//userPointLogList.unshift(userInfo[0].point);
+
+		return result;
+		// return userPointLogList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 상품 이용 로그 조회
+exports.userAuthLogs = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const result = [];
+		let userAuthLogList = await userDao.selectUserAuthLog(connection, userIdx);
+		for (let i = 0; i < userAuthLogList.length; i++) {
+			let examName;
+			let type = "";
+
+			if (userAuthLogList[i].type != null) {
+				if (userAuthLogList[i].type == 0) type += " (한 문제씩 풀기)";
+				else if (userAuthLogList[i].type == 1) type += " (CBT 모의고사)";
+			}
+			if (userAuthLogList[i].examDetailIdx) {
+				let examDetailInfo = await examDao.selectExamDetailInfo(connection, userAuthLogList[i].examDetailIdx);
+				if (examDetailInfo.length) {
+					if (!isNaN(examDetailInfo[0].examRound)) examDetailInfo[0].examRound += "회차";
+					//examDetailInfo[0].examYear = examDetailInfo[0].examDate.getFullYear();
+					examName =
+						examDetailInfo[0].examName + " " + examDetailInfo[0].examYear + "년 " + examDetailInfo[0].examRound + type;
+				}
+			} else if (userAuthLogList[i].examIdx) {
+				let examInfo = await examDao.selectExamInfo(connection, userAuthLogList[i].examIdx);
+				if (examInfo.length) {
+					examName = examInfo[0].examName + type;
+				}
+			}
+			result.push({ examName, createdAt: userAuthLogList[i].createdAt });
+		}
+		return resultResponse(baseResponse.SUCCESS, result);
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 시험 기록으로 시험 회차 조회
+exports.userExamRecordToExamDetail = async function (userExamRecordIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const examDetailIdx = await userDao.selectExamDetailByExamRecord(connection, userExamRecordIdx);
+		return examDetailIdx;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.userAllProductsName = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const productList = await userDao.selectUserAllProductsName(connection, userIdx);
+		let validProduct = [];
+		let invalidProduct = [];
+		let now = new Date();
+
+		// 유저 상품 유효성 판단
+		productList.map((v) => {
+			if (now < v.expireAt) {
+				validProduct.push(v.productName);
+			} else if (now >= v.expireAt) {
+				invalidProduct.push(v.productName);
+			}
+		});
+		const result = { validProduct: validProduct, invalidProduct: invalidProduct };
+		return result;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+// 상품 구매한 기록이 있는 모든 유저의 index 가져오기 (무료 엔지니오 상품 제외)
+exports.selectPurchasedUserIdxList = async function () {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const allUserIdxList = await userDao.selectPurchasedUserIdxList(connection);
+		return allUserIdxList;
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.getUserIdxByUserAuthIdx = async function (userAuthIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userIdxResult = await userDao.selectUserIdxByUserAuthIdx(connection, userAuthIdx);
+		return userIdxResult[0];
+	} catch (error) {
+		throw error;
+	} finally {
+		connection.release();
+	}
+};
diff --git a/_old/src/User/userRoute.js b/_old/src/User/userRoute.js
new file mode 100644
index 0000000..9b45c95
--- /dev/null
+++ b/_old/src/User/userRoute.js
@@ -0,0 +1,168 @@
+module.exports = function (app) {
+	const user = require("./userController");
+	const jwtMiddleware = require("../../middlewares/jwtMiddleware");
+	const admission = require("../../config/admission");
+	const authCheck = require("../../middlewares/authCheck");
+	const subjectiveUser = require("./subjectiveUserRoute.js/subjectiveUserRoute");
+
+	//const authCheck = require("../../config/authCheck")
+
+	app.get("/api/testcase", (req, res) => {
+		res.send("test다 이쉐키야!");
+	});
+
+	// API No ?. 회원가입 API  휴대폰  인증 후 번호로 유저 존재하는지 검토
+	app.post("/api/users/sign-up", user.postUser);
+
+	// API No ?. 이메일 확인 API
+	app.post("/api/users/emailAuth", user.checkEmail);
+
+	// API No ?. 닉네임 확인 API
+	app.post("/api/users/nicknameAuth", user.checkNickname);
+
+	// API No ?. 로그인 API
+	app.post("/api/users/sign-in", user.login);
+
+	// API No ?. 카카오 로그인 API
+	app.post("/api/users/kakao-login", user.kakaoLogin);
+	// app.get("/auth/kakao/callback", user.kakaoCallback);
+
+	// API No ?. 카카오 회원가입 API
+	app.post("/api/users/kakao-sign-up", user.kakaoPostUser);
+
+	// API No ?. 회원 소셜 가입 계정 조회 API
+	app.get("/api/users/:userIdx/social", jwtMiddleware, user.checkUserSocial);
+
+	// API No ?. 회원 정보 조회 API ( 비밀번호 없이 토큰으로 조회 )
+	app.get("/api/users/:userIdx/profile", jwtMiddleware, user.checkUserInfoWithToken);
+
+	// API No ?. 회원 정보 조회 API ( 회원정보 수정 시도 시 )
+	app.post("/api/users/:userIdx/profile", jwtMiddleware, user.checkUserInfo);
+
+	// API No ?. 회원 정보 수정 API
+	app.patch("/api/users/:userIdx/profile", jwtMiddleware, user.patchUserInfo);
+
+	// API No ?. 비밀번호 확인 API
+	app.post("/api/users/passwordAuth", jwtMiddleware, user.checkPassword);
+
+	// API No ?. 토큰 확인 API
+	app.post("/api/users/tokenAuth", jwtMiddleware, admission, user.checkToken);
+
+	// API No ?. 회원 탈퇴 API
+	app.patch("/api/users/:userIdx/withdrawal", jwtMiddleware, user.dropUser);
+
+	// 로그아웃 보류
+	//app.post('/users/kakao-logout', user.kakaoLogout);
+
+	// API No ?. 이메일 찾기 API
+	app.post("/api/users/find/email", user.findEmail);
+
+	// API No ?. 비밀번호 찾기 API
+	app.post("/api/users/find/password", user.findPassword);
+
+	// API No ?. 핸드폰 인증 요청 API
+	app.post("/api/users/auth", user.getSelfAuth);
+
+	// API No ?. 핸드폰 본인인증 확인 API
+	app.post("/api/users/auth/verify", user.confirmSelfAuth);
+
+	// API No ?. 오답노트 목록 조회 API
+	//http://localhost:3005/api/users/4/reviewNote?page=1&examSortName=1&order=0&bookMark=0&examField=전기 이런식으로 자격증 선택시 examField를 전기로 디폴트 설정 해주어야 함.
+	app.get("/api/users/:userIdx/reviewNote", jwtMiddleware, user.userReviewNote);
+
+	// API No ?. 오답노트 북마크 API
+	app.post("/api/users/:userIdx/reviewNote/bookMark", jwtMiddleware, user.userReviewNoteBookMark);
+
+	// API No ?. 오답노트 메모 조회 API
+	app.get("/api/users/:userIdx/reviewNote/:reviewNoteIdx/memo", jwtMiddleware, user.userReviewNoteMemo);
+
+	// API No ?. 오답노트 메모 수정 API
+	app.patch("/api/users/:userIdx/reviewNote/:reviewNoteIdx/memo", jwtMiddleware, user.userReviewNoteMemoPatch);
+
+	// API No ?. 오답노트 문제 조회 API  카텍스 유무
+	app.get("/api/users/:userIdx/reviewNote/set", jwtMiddleware, user.userReviewNoteSet);
+
+	// API No ?. 오답노트 시험(회차) 목록 조회 API
+	app.get("/api/users/:userIdx/reviewNote/exam", jwtMiddleware, user.userReviewNoteExamDetail);
+
+	// API No ?. 오답노트 시험(과목별) 목록 조회 API - 김기창
+	app.get("/api/users/:userIdx/reviewNote/subject", jwtMiddleware, user.userReviewNoteSubject);
+
+	// // API No ?. 오답노트 시험 시험 분야별 필터링 API - 김기창
+	// app.get("/api/users/:userIdx/reviewNote/examField", jwtMiddleware, user.userReviewNoteExamField);
+
+	// API No ?. 오답노트 문제 조회(회차) API  카텍스 유무
+	app.get("/api/users/:userIdx/reviewNote/problem/:examDetailIdx", jwtMiddleware, authCheck, user.userReviewNoteRound);
+
+	// API No ?. 오답노트 문제 조회(과목별) API  카텍스 유무 - 깁기창
+	app.get(
+		"/api/users/:userIdx/reviewNote/:examIdx/subject/problem/:examSubjectIdx",
+		jwtMiddleware,
+		authCheck,
+		user.userReviewNoteSubjectProblem,
+	);
+
+	// API No ?. 오답노트 문제 삭제 API
+	app.patch("/api/users/:userIdx/reviewNote", jwtMiddleware, user.patchReviewNote);
+	// app.patch("/api/users/:userIdx/reviewNote/problem/:multipleProblemIdx", jwtMiddleware, user.patchReviewNote);
+
+	// API No ?. 오답노트 문제 삭제 (회차) API
+	app.patch("/api/users/:userIdx/reviewNote/problem/:examDetailIdx", jwtMiddleware, user.patchReviewNoteRound);
+
+	// API No ?. 오답노트 문제 삭제 (과목) API
+	app.patch("/api/users/:userIdx/reviewNote/delete", jwtMiddleware, user.patchReviewNoteSubject);
+
+	// API No ?. 오답노트 최신순 문제 조회 API
+	//app.get("/api/users/:userIdx/reviewNote/problem/latestOrder", jwtMiddleware, user);
+
+	// API No ?. 유저 시험 기록 조회 API
+	app.get("/api/users/:userIdx/examRecord", jwtMiddleware, user.getUserExamRecord);
+
+	// API No ?. 유저 시험 기록 삭제 API - 김기창
+	app.patch("/api/users/:userIdx/cbtExamRecord/delete", jwtMiddleware, user.deleteUserCbtExamRecord);
+
+	// API No ?. 유저 시험 기록 채점결과 조회 API
+	app.get("/api/users/:userIdx/examRecord/:examRecordIdx/grade/:examIdx", jwtMiddleware, user.getUserExamRecordGrade);
+
+	// API No ?. 유저 시험 기록 해설 및 상세 조회 API
+	app.get("/api/users/:userIdx/examRecord/:examRecordIdx/detail", authCheck, user.getUserExamDetailRecord);
+
+	// API No ?. 유저 사이트 에러 신고 API
+	app.post("/api/users/reportError", jwtMiddleware, user.postSiteError);
+
+	// API No ?. 유저 문제 에러 신고 내역조회 API
+	app.get("/api/users/:userIdx/problemError", jwtMiddleware, user.getUserReportProblem);
+
+	// API No ?. 유저 이름, 닉네임, provider 조회 API
+	app.get("/api/users/:userIdx/userInfo", jwtMiddleware, user.getUserInfo);
+
+	// API No ?. 유저 장바구니 담은 갯수 조회 API
+	//app.get("/api/users/cartCount", jwtMiddleware, user.getUserCart);
+
+	// API No ?. 유저 장바구니 조회 API
+	app.get("/api/users/:userIdx/cart", jwtMiddleware, user.getUserCart);
+
+	// API No ?. 유저 장바구니 추가 API
+	app.post("/api/users/:userIdx/cart", jwtMiddleware, user.postUserCart);
+
+	// API No ?. 유저 장바구니 삭제 API
+	app.delete("/api/users/:userIdx/cart", jwtMiddleware, user.deleteUserCart);
+
+	// API No ?. 유저 구매 내역 조회 API
+	app.get("/api/users/:userIdx/purchaseHistory", jwtMiddleware, user.getUserPurchaseHistory);
+
+	// API No ?. 유저 구매 내역 상세 조회 API
+	// app.get("/api/users/purchaseHistory/detail", jwtMiddleware, user.getUserPurchaseHistoryDetail);
+
+	// API No ?. 유저 환불 내역 조회 API
+	// app.get("/api/users/refundHistory", jwtMiddleware, user.getUserRefundHistory);
+
+	// API No ?. 유저 나의 시험 조회 API
+	app.get("/api/users/:userIdx/product", jwtMiddleware, user.getUserProduct);
+
+	// API No ?. 유저 포인트 로그 조회 API
+	app.get("/api/users/:userIdx/pointLog", jwtMiddleware, user.getUserPointLog);
+
+	// API No ?. 주관식에 대한 유저 Route 처리 API 모듈화
+	app.use("/api/users/practical", subjectiveUser);
+};
diff --git a/_old/src/User/userService.js b/_old/src/User/userService.js
new file mode 100644
index 0000000..dfe614a
--- /dev/null
+++ b/_old/src/User/userService.js
@@ -0,0 +1,645 @@
+const { pool } = require("../../config/database");
+const secret_config = require("../../config/secret");
+const userProvider = require("./userProvider");
+const userDao = require("./userDao");
+const channelTalkService = require("../ChannelTalk/channelTalkService");
+const baseResponse = require("../../config/baseResponseStatus");
+const { resultResponse } = require("../../config/response");
+const { basickResponse } = require("../../config/response");
+const jwt = require("jsonwebtoken");
+const crypto = require("crypto");
+const { logger } = require("../../config/winston");
+const errorResponse = require("../../utils/errorResponse");
+const axios = require("axios");
+const secret = require("../../config/secret");
+
+exports.axiosCall = async function (method, uri, param, header) {
+	let axiosVal;
+	try {
+		axiosVal = await axios({
+			method: method,
+			url: uri,
+			headers: header,
+			data: param,
+		});
+		if (axiosVal && axiosVal.headers["content-type"].includes("application/json") && axiosVal.data) {
+			return axiosVal.data;
+		}
+	} catch (err) {
+		throw err;
+	}
+};
+// Sign-Up
+exports.createUser = async function (
+	email,
+	password,
+	phoneNum,
+	userName,
+	dateOfBirth,
+	nickname,
+	recommendNickname,
+	isRejoin,
+	provider,
+) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		await connection.beginTransaction();
+		let insertUserParams;
+		// Password Hash
+		if (password && provider == "engineeo") {
+			const hashedPassword = await crypto.createHash("sha512").update(password).digest("hex");
+			insertUserParams = [email, hashedPassword, phoneNum, userName, dateOfBirth, nickname, provider];
+		} else {
+			insertUserParams = [email, "", phoneNum, userName, dateOfBirth, nickname, provider];
+		}
+
+		const signUpResult = await userDao.insertUser(connection, insertUserParams).catch((err) => {
+			console.error(err);
+			console.log(
+				" email : ",
+				email,
+				"phoneNum : ",
+				phoneNum,
+				"userName : ",
+				userName,
+				" dateOfBirth : ",
+				dateOfBirth,
+				"nickname : ",
+				nickname,
+				"provider : ",
+				provider,
+			);
+		});
+		const userIdx = signUpResult[0].insertId;
+		let token = jwt.sign(
+			{
+				userIdx: userIdx,
+				isKeep: 0,
+			}, // 토큰의 내용(payload)
+			secret_config.jwtsecret, // 비밀키
+			{
+				expiresIn: "6h",
+				subject: "userInfo",
+			}, // 유효 기간 365일
+		);
+		// 유저 로그
+		await userDao.insertUserLog(connection, userIdx, "login");
+		// 유저 무료 상품 입력
+		await userDao.insertUserFreeProduct(connection, userIdx);
+		await channelTalkService.updateChannelTalkUserProductInfo(userIdx);
+		// 유저 신규가입 포인트 지급
+		if (!isRejoin) {
+			await userDao.insertUserPoint(connection, userIdx, 1);
+			const newPointInfo = await userDao.selectPointInfo(connection, 1);
+			await userDao.insertUserPointLog(connection, [userIdx, "A", newPointInfo.pointName, newPointInfo.point]);
+			await userDao.updateUserPoint(connection, userIdx, newPointInfo.point);
+		}
+
+		// 유저 추천인 카운트, 포인트 지급
+		if (recommendNickname && !isRejoin) {
+			const recUserInfo = await userDao.selectUserNickname(connection, recommendNickname);
+			const recUserIdx = recUserInfo[0].userIdx;
+			await userDao.updateUserRecommendCountUp(connection, recommendNickname);
+
+			await userDao.insertUserPoint(connection, userIdx, 2);
+			await userDao.insertUserPoint(connection, recUserIdx, 2);
+			const recPointInfo = await userDao.selectPointInfo(connection, 2);
+			await userDao.insertUserPointLog(connection, [userIdx, "A", recPointInfo.pointName, recPointInfo.point]);
+			await userDao.insertUserPointLog(connection, [recUserIdx, "A", recPointInfo.pointName, recPointInfo.point]);
+			await userDao.updateUserPoint(connection, userIdx, recPointInfo.point);
+			await userDao.updateUserPoint(connection, recUserIdx, recPointInfo.point);
+		}
+		await connection.commit();
+
+		return {
+			userIdx: userIdx,
+			jwt: token,
+		};
+	} catch (err) {
+		await connection.rollback();
+
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// Social Sign-Up
+exports.createSocialUser = async function (
+	userEmail,
+	userPhoneNum,
+	userName,
+	dateOfBirth,
+	nickname,
+	recommendNickname,
+) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		await connection.beginTransaction();
+		// Password Hash
+
+		const insertUserParams = [userEmail, NULL, userPhoneNum, userName, dateOfBirth, nickname];
+		const signUpResult = await userDao.insertUser(connection, insertUserParams);
+		if (recommendNickname) await userDao.updateUserRecommendCountUp(connection, recommendNickname);
+
+		let token = jwt.sign(
+			{
+				userIdx: signUpResult[0].insertId,
+				isKeep: 0,
+			}, // 토큰의 내용(payload)
+			secret_config.jwtsecret, // 비밀키
+			{
+				expiresIn: "6h",
+				subject: "userInfo",
+			}, // 유효 기간 365일
+		);
+
+		await connection.commit();
+		await userDao.insertUserLog(connection, signUpResult[0].insertId, "login");
+
+		return resultResponse(baseResponse.SIGN_UP_SUCCESS, {
+			userIdx: signUpResult[0].insertId,
+			jwt: token,
+		});
+	} catch (err) {
+		await connection.rollback();
+
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// 카카오 로그인 => isKeep은 추후 변경
+exports.kakaoLogin = async function (userIdx, isKeep) {
+	const connection = await pool.getConnection(async (conn) => conn);
+
+	try {
+		let expiresIn;
+
+		if (isKeep == 1) expiresIn = "7d";
+		else expiresIn = "6h";
+
+		//토큰 생성 Service
+		let token = jwt.sign(
+			{
+				userIdx: userIdx,
+				isKeep: isKeep,
+			}, // 토큰의 내용(payload)
+			secret_config.jwtsecret, // 비밀키
+			{
+				expiresIn: expiresIn,
+				subject: "userInfo",
+			}, // 유효 기간 365일
+		);
+		await userDao.insertUserLog(connection, userIdx, "login");
+
+		return {
+			userIdx: userIdx,
+			jwt: token,
+			status: "N",
+		};
+	} catch (err) {
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// Sign-In
+exports.loginUser = async function (email, password, isKeep) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let expiresIn;
+		// Hashed Password
+		const hashedPassword = crypto.createHash("sha512").update(password).digest("hex");
+		const userInfo = await userProvider.userCheck(email, hashedPassword);
+
+		if (!userInfo.length || hashedPassword !== userInfo[0].userPassword)
+			return resultResponse(baseResponse.SIGNIN_PASSWORD_WRONG); // 비밀번호가 잘못 되었습니다.
+
+		if (userInfo[0].status == "Y") return resultResponse(baseResponse.SIGNIN_PASSWORD_WRONG); //  탈퇴한 계정입니다.
+
+		if (isKeep == 1) expiresIn = "7d";
+		else expiresIn = "6h";
+
+		//토큰 생성 Service
+		let token = jwt.sign(
+			{
+				userIdx: userInfo[0].userIdx,
+				isKeep: isKeep,
+			}, // 토큰의 내용(payload)
+			secret_config.jwtsecret, // 비밀키
+			{
+				expiresIn: expiresIn,
+				subject: "userInfo",
+			}, // 유효 기간 365일
+		);
+		await userDao.insertUserLog(connection, userInfo[0].userIdx, "login");
+
+		return resultResponse(baseResponse.SIGN_IN_SUCCESS, {
+			userIdx: userInfo[0].userIdx,
+			jwt: token,
+			status: userInfo[0].status,
+		});
+	} catch (err) {
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+exports.userRejoin = async function (userEmail, nickname, provider) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		const selectUserByEmailResult = await userDao.selectUserEmail(connection, userEmail, provider);
+		const userIdx = selectUserByEmailResult[0].userIdx;
+
+		try {
+			const updateUserRejoinParams = [nickname, userIdx, provider];
+			const updateUserResjoinResult = await userDao.updateUserRejoin(connection, updateUserRejoinParams);
+			// 유저 무료 상품 권한 재입력
+			await userDao.updateUserFreeProduct(connection, userIdx);
+			await channelTalkService.updateChannelTalkUserProductInfo(userIdx);
+			// 유저 로그
+			await userDao.insertUserLog(connection, userIdx, "rejoin");
+			return updateUserResjoinResult;
+		} finally {
+			connection.release();
+		}
+	} catch (err) {
+		throw err;
+	}
+};
+
+// 유저 비밀번호 수정
+exports.updateUserPassword = async function (password, userIdx, usePhoneCheck, phoneNum) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const { NCPClient } = require("node-sens");
+		const hashedPassword = crypto.createHash("sha512").update(password).digest("hex");
+		try {
+			await connection.beginTransaction();
+			await userDao.updateUserPassword(connection, hashedPassword, userIdx);
+			if (usePhoneCheck) {
+				const ncp = new NCPClient({
+					phoneNumber: secret_config.officeNumber,
+					serviceId: secret_config.authServiceKey,
+					secretKey: secret_config.authSecretKey,
+					accessKey: secret_config.authAccessKey,
+				});
+				const { success, msg, status } = await ncp.sendSMS({
+					to: phoneNum,
+					content: `EngineeO 임시 비밀번호는  ${password} 입니다.`,
+				});
+				if (status != 202) return false;
+			}
+			await connection.commit();
+			return true;
+		} finally {
+			connection.release();
+		}
+	} catch (err) {
+		await connection.rollback();
+
+		throw err;
+	}
+};
+
+// 유저 닉네임 수정
+exports.updateUserNickname = async function (nickname, userIdx) {
+	try {
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await userDao.updateUserNickname(connection, nickname, userIdx);
+		} finally {
+			connection.release();
+		}
+	} catch (err) {
+		throw err;
+	}
+};
+
+// 유저 정보 수정
+exports.updateUserInfo = async function (password, nickname, userIdx) {
+	try {
+		// Hashed Password
+		const hashedPassword = crypto.createHash("sha512").update(password).digest("hex");
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await userDao.updateUserInfo(connection, hashedPassword, nickname, userIdx);
+		} finally {
+			connection.release();
+		}
+	} catch (err) {
+		throw err;
+	}
+};
+
+// 회원 탈퇴
+exports.updateUserDrop = async function (userIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		connection.beginTransaction();
+		const getUserInfo = await userDao.selectUserInfo(connection, userIdx);
+		const userPoint = getUserInfo[0].point;
+		const pointLogParams = [userIdx, "U", "회원탈퇴", userPoint];
+
+		await userDao.withdrawalUser(connection, userIdx);
+		await userDao.deleteUserAllReviewNote(connection, userIdx);
+		await userDao.deleteUserAllUserExamRecord(connection, userIdx);
+		await userDao.deleteUserAllUserAuth(connection, userIdx);
+		await userDao.deleteUserAllUserCart(connection, userIdx);
+		await userDao.deleteUserAllPayment(connection, userIdx);
+		await userDao.insertUserPointLog(connection, pointLogParams);
+		await userDao.insertUserLog(connection, userIdx, "quit");
+
+		connection.commit();
+	} catch (err) {
+		connection.rollback();
+
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// 오답노트 삭제
+exports.updateUserReviewNote = async function (reviewNoteIdxList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await userDao.deleteUserReviewNote(connection, reviewNoteIdxList);
+	} catch (err) {
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// 오답노트 삭제 (회차)
+exports.updateUserReviewNoteRound = async function (userIdx, examDetailIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		//examDetailIdx로 이 회차가 실기인지 필기인지 판단
+		//그 이후 각각에 맞게 로직진행
+		const isSubjectiveExam = await userDao.selectExamInfo(connection, examDetailIdx);
+		isSubjectiveExam[0].exist
+			? await userDao.deleteUserReviewNoteSubjectiveRound(connection, userIdx, examDetailIdx)
+			: await userDao.deleteUserReviewNoteRound(connection, userIdx, examDetailIdx);
+	} catch (err) {
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// 오답노트 삭제 (목록)
+exports.updateUserReviewNoteSubject = async function (userIdx, examIdx, examSubjectIdx) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		const reviewNoteSubjectMultipleProblem = await userDao.selectUserReviewNoteSubjectProblem(
+			connection,
+			examSubjectIdx,
+			examIdx,
+			userIdx,
+		);
+		const reviewNoteIdxList = reviewNoteSubjectMultipleProblem.map((v) => v.reviewNoteIdx);
+
+		// 길이 1 이상 필기 과목별 삭제 0 이면 실기 과목별 삭제
+		reviewNoteIdxList.length
+			? await userDao.deleteUserReviewNote(connection, reviewNoteIdxList)
+			: await userDao.deleteUserReviewNoteSubjectByExamSubject(connection, userIdx, examIdx, examSubjectIdx);
+
+		await connection.commit();
+	} catch (err) {
+		await connection.rollback();
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// 오류 신고 생성
+exports.createSiteErrorReport = async function (userIdx, title, content) {
+	try {
+		const insertsiteErrorParams = [userIdx, title, content];
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await userDao.insertReportSiteError(connection, insertsiteErrorParams);
+		} finally {
+			connection.release();
+		}
+	} catch (err) {
+		throw err;
+	}
+};
+
+// 유저 소셜 아이디 연동 생성
+exports.createUserSocialLink = async function (userIdx, snsName, snsId) {
+	try {
+		const insertUserSocialIdParams = [userIdx, snsName, snsId];
+		const connection = await pool.getConnection(async (conn) => conn);
+		try {
+			await userDao.insertUserSocialId(connection, insertUserSocialIdParams);
+			return basickResponse(baseResponse.SUCCESS);
+		} finally {
+			connection.release();
+		}
+	} catch (err) {
+		throw err;
+	}
+};
+
+exports.sendPhoneVerification = async function (phoneNum) {
+	const redis = require("redis");
+
+	const redisClient = redis.createClient({
+		url: secret_config.redisUrl,
+	});
+
+	try {
+		await redisClient.connect();
+
+		const { NCPClient } = require("node-sens");
+		redisClient.on("error", (err) => {
+			throw err;
+		});
+		//redis_client["auth"] = null;
+
+		let authNum = "";
+		for (let i = 0; i < 6; i++) {
+			authNum += Math.floor(Math.random() * 10);
+		}
+		const ncp = new NCPClient({
+			phoneNumber: secret_config.officeNumber,
+			serviceId: secret_config.authServiceKey,
+			secretKey: secret_config.authSecretKey,
+			accessKey: secret_config.authAccessKey,
+		});
+		const { success, msg, status } = await ncp.sendSMS({
+			to: phoneNum,
+			content: `EngineeO 인증번호 ${authNum} 입니다.`,
+		});
+		if (status != 202) {
+			throw new errorResponse(baseResponse.SENS_ERROR, 400);
+		}
+		let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+		let text = "";
+		for (let i = 0; i < 5; i++) text += possible.charAt(Math.floor(Math.random() * possible.length));
+		await redisClient.set(text, JSON.stringify(authNum), {
+			EX: 180,
+			NX: true,
+		});
+		return { cacheKey: text };
+	} catch (err) {
+		throw err;
+	} finally {
+		await redisClient.disconnect();
+	}
+};
+//
+
+exports.checkPhoneVerification = async function (cacheKey, authNum) {
+	const redis = require("redis");
+	const redisClient = redis.createClient({
+		url: secret_config.redisUrl,
+	});
+	try {
+		redisClient.on("error", (err) => {
+			throw err;
+		});
+		//redis_client["auth"] = null;
+		await redisClient.connect();
+
+		let redisAuthNum = await redisClient.get(cacheKey);
+
+		if (redisAuthNum !== '"' + authNum + '"') throw new errorResponse(baseResponse.PHONENUM_ERROR_TYPE, 400);
+	} catch (err) {
+		throw err;
+	} finally {
+		await redisClient.disconnect();
+	}
+};
+
+// 유저 오답노트 북마크 추가
+exports.reviewNoteBookMark = async function (reviewNoteIdx, bookMark) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await userDao.updateUserReviewNoteBookMark(connection, reviewNoteIdx, bookMark);
+	} catch (err) {
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 오답노트 메모 수정
+exports.reviewNoteMemoPatch = async function (reviewNoteIdx, userIdx, memo) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await userDao.updateUserReviewNoteMemo(connection, reviewNoteIdx, userIdx, memo);
+	} catch (err) {
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 장바구니 추가
+exports.updateUserCart = async function (userIdx, productIdxList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		let cartInfo = [];
+		for (let i = 0; i < productIdxList.length; i++) {
+			const checkUserCartProductResult = await userDao.checkUserCartProduct(connection, userIdx, productIdxList[i]);
+			if (checkUserCartProductResult[0].exist) return basickResponse(baseResponse.CART_EXIST);
+			let element = [userIdx, productIdxList[i]];
+			cartInfo.push(element);
+		}
+		const updateUserCartResult = await userDao.insertUserCart(connection, cartInfo);
+		return updateUserCartResult;
+	} catch (err) {
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 장바구니 삭제 (다중)
+exports.updateUserCartDrop = async function (userCartIdxList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const userCartIdxs = userCartIdxList.join(",");
+
+		const updateUserCartDropResult = await userDao.deleteUserCartMulti(connection, userCartIdxs);
+		return updateUserCartDropResult;
+	} catch (err) {
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+// 유저 장바구니 특정 상품 삭제 (다중)
+exports.updateUserCartProductDrop = async function (userIdx, productIdxList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		const productIdxs = productIdxList.join(",");
+
+		const updateUserCartProductDropResult = await userDao.deleteUserCartProductMulti(connection, userIdx, productIdxs);
+		return resultResponse(baseResponse.SUCCESS, updateUserCartProductDropResult);
+	} catch (err) {
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+//  CBT 시험 목록 삭제 (다중) - 김기창.
+exports.updateUserCbtExamDrop = async function (userCbtExamRecordDeleteIdxList) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await connection.beginTransaction();
+		for (let i = 0; i < userCbtExamRecordDeleteIdxList.length; i++) {
+			await userDao.deleteCbtUserExamMulti(connection, userCbtExamRecordDeleteIdxList[i]);
+		}
+		//delete할 index들이 list형식으로 오게된다. 그렇다면 이 부분에서
+		//for문으로 돌아가면서 각각의 index에 대한 것들에 데이터 처리를 해준다.
+		// 해당 인덱스 인것을 y => n으로 바꾸어주도록 한다.
+		await connection.commit();
+	} catch (err) {
+		await connection.rollback();
+
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+
+//유저의 authLog 생성하기 - examDetailIdx 방식
+exports.insertUserAuthLogDetailIdx = async function (userIdx, examDetailIdx, type) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await userDao.createUserAuthLogDetailIdx(connection, userIdx, examDetailIdx, type);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (err) {
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
+//유저의 authLog 생성하기 - examIdx 방식
+exports.insertUserAuthLogExamIdx = async function (userIdx, examIdx, type) {
+	const connection = await pool.getConnection(async (conn) => conn);
+	try {
+		await userDao.createUserAuthLogExamIdx(connection, userIdx, examIdx, type);
+		return basickResponse(baseResponse.SUCCESS);
+	} catch (err) {
+		throw err;
+	} finally {
+		connection.release();
+	}
+};
diff --git a/_old/utils/adminAuthenticator.js b/_old/utils/adminAuthenticator.js
new file mode 100644
index 0000000..041d509
--- /dev/null
+++ b/_old/utils/adminAuthenticator.js
@@ -0,0 +1,15 @@
+const adminProvider = require("../src/Admin/adminProvider");
+const regNumber = /^[0-9]/;
+
+class AdminAuthenticator {
+	static async authenticateAdmin(req) {
+		const adminIdx = req.verifiedToken.userIdx;
+		if (!adminIdx) throw new errorResponse(baseResponse.TOKEN_EMPTY, 401);
+
+		if (!regNumber.test(adminIdx)) throw new errorResponse(baseResponse.TOKEN_VERIFICATION_FAILURE, 400);
+
+		const adminCheckResult = await adminProvider.adminCheck(adminIdx);
+		if (adminCheckResult[0].exist === 0) throw new errorResponse(baseResponse.ADMIN_NOT_EXIST, 400);
+	}
+}
+module.exports = AdminAuthenticator;
diff --git a/_old/utils/asyncHandler.js b/_old/utils/asyncHandler.js
new file mode 100644
index 0000000..e00c525
--- /dev/null
+++ b/_old/utils/asyncHandler.js
@@ -0,0 +1,7 @@
+const asyncHandler = (fn) => (req, res, next) => {
+	Promise.resolve(fn(req, res, next)).catch((err) => {
+		next(err);
+	});
+};
+
+module.exports = asyncHandler;
diff --git a/_old/utils/errorResponse.js b/_old/utils/errorResponse.js
new file mode 100644
index 0000000..5f51dfe
--- /dev/null
+++ b/_old/utils/errorResponse.js
@@ -0,0 +1,11 @@
+class errorResponse extends Error {
+	constructor(baseResponse, statusCode) {
+		super(baseResponse.message);
+		this.statusCode = statusCode || 500;
+		this.code = baseResponse.code;
+		this.isSuccess = baseResponse.isSuccess;
+		this.result = baseResponse.result;
+	}
+}
+
+module.exports = errorResponse;
diff --git a/buildspec/build.yml b/buildspec/build.yml
new file mode 100644
index 0000000..d993369
--- /dev/null
+++ b/buildspec/build.yml
@@ -0,0 +1,36 @@
+version: 0.2
+
+phases:
+  install:
+    runtime-versions:
+      nodejs: 18
+    commands:
+      - echo Installing dependencies...
+      - npm install
+  pre_build:
+    commands:
+      - unzip -o _old/config.zip -d _old/
+  build:
+    commands:
+      - echo Build started on `date`
+      - npm run build
+  post_build:
+    commands:
+      - echo Build completed on `date`
+      - echo prisma migrate on `date`
+      - npx prisma migrate deploy
+      - echo migrate completed on `date`
+      - echo build dev finished on `date`
+
+artifacts:
+  files:
+    - dist/**/*
+    - package-lock.json
+    - package.json
+    - Dockerfile
+    - .platform/**/*
+    - prisma/**/*
+
+cache:
+  paths:
+    - node_modules/**/*
diff --git a/config/database.js b/config/database.js
new file mode 100644
index 0000000..c6a4585
--- /dev/null
+++ b/config/database.js
@@ -0,0 +1,26 @@
+const mysql = require('mysql2/promise');
+
+// 배포용 디비
+// const pool = mysql.createPool({
+// 	host: "engineeo.ca62pi8mthdp.ap-northeast-2.rds.amazonaws.com",
+// 	port: "3306",
+// 	user: "admin",
+// 	database: "production",
+// 	password: "park1610784!",
+// 	connectionLimit: 15,
+
+// 	// database: "production",
+// });
+
+// // 테스트 서버용 디비
+const pool = mysql.createPool({
+  host: process.env.DATABASE_HOST,
+  port: process.env.DATABASE_PORT,
+  user: process.env.DATABASE_USER,
+  database: process.env.DATABASE_NAME,
+  password: process.env.DATABASE_PASSWORD,
+  connectionLimit: 15,
+});
+module.exports = {
+  pool: pool,
+};
diff --git a/config/email.js b/config/email.js
new file mode 100644
index 0000000..d35250a
--- /dev/null
+++ b/config/email.js
@@ -0,0 +1,13 @@
+const nodemailer = require("nodemailer");
+const smtpTransport = nodemailer.createTransport({
+	service: "Gmail",
+	auth: {
+		user: "easyelectric0616@gmail.com",
+		pass: "akstjrfh207!",
+	},
+	tls: {
+		rejectUnauthorized: false,
+	},
+});
+
+module.exports = smtpTransport;
diff --git a/config/secret.js b/config/secret.js
new file mode 100644
index 0000000..b9e710b
--- /dev/null
+++ b/config/secret.js
@@ -0,0 +1,33 @@
+module.exports = {
+	jwtsecret: "easyelec_jwt_secret_key_20200616",
+
+	// NCP Cloud
+	authSecretKey: "qXRZJMHsADWCFR4zeTPk8P54CakQaZp5rVn0FoMC",
+	authAccessKey: "76oiUUpGsrQAyKK37wXi",
+	authServiceKey: "ncp:sms:kr:270008247756:engineeo",
+	officeNumber: "0318930526",
+
+	//kakao developer
+	REST_API_KEY: "e9041a1f89b8584e80297f6abdb4a303",
+	REDIRECT_URI: "http://localhost:3005/auth/kakao/callback",
+	CLIENT_SECRET: "RtQRc6gIlhliNmxE6PzrDcm8rC6kkU8u",
+
+	googleClientId: "1044033344304-5fsju58848cpu1659sdh7hups9ghfr2g.apps.googleusercontent.com",
+	googleClientSecret: "e6fw0Q-TR7CIKc2pLs3RznrA",
+	awsConfigRegion: "ap-northeast-2",
+	identityPoolIdx: "ap-northeast-2:f79113d2-cc7d-48bc-b076-8c881af602c9",
+	imageAccessKey: "AKIA4DPAKZL742Z2LV4T",
+	imageSecretAccessKey: "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",
+	localPort: 3005,
+	bucket: "engineeouploadimage",
+	privateBucket: "engineeoimage",
+	pushPublicKey: "BDSxaDKDdh41_e1Bbs8ndh78IiQ4cUrGYFFd_2myf0rNEJmoeuYaY46uzqrteST9a82GSqvOMVGIyg_54C0EjZE",
+	pushPrivateKey: "WrDwj1JsRAfbwuyFxF95ZYPQtFvGzIR66Dsj-1CydHw",
+	chatSecretKey: "baf7f8ae9d6bc6026e451f1769c05222e3d5e3208e8efcb10b4750b14a2976c5",
+	googleApiKey: "AIzaSyDPqUX5B08ivRw4wHuGwxKkogREGvnJi0Y",
+	importApiKey: "8684613833039648",
+	importApiSecret: "78d25f8ef36e7ff124ea6c23d5949c89ba8f1404c0090549b230af1bd2444e01e8b059f1a3a81398",
+	redisUrl: "redis://engineeo-server-cache.ddzusp.0001.apn2.cache.amazonaws.com:6379",
+	pointSecretKey: "qrohiphop",
+	// ebb6e90c73294fc88530eeefe03d48f7 sms Secret key
+};
diff --git a/config/swagger.js b/config/swagger.js
new file mode 100644
index 0000000..604b2df
--- /dev/null
+++ b/config/swagger.js
@@ -0,0 +1,67 @@
+const swaggerUi = require("swagger-ui-express");
+const swaggereJsdoc = require("swagger-jsdoc");
+
+const options = {
+	swaggerDefinition: {
+		info: {
+			title: "Test API",
+			version: "3.0.0",
+			description: "Test API with express",
+		},
+		host: "localhost:4000",
+		basePath: "/",
+	},
+
+	apis: ["../src/Admin/*.js", "../src/Community/*.js", "../src/Exam/*.js", "../src/User/*.js"],
+};
+const specs = swaggereJsdoc(options);
+
+/*
+definition: { openapi: "3.0.0" // 정보 
+              info : { 
+              title: 'EngineeO API'
+              version: '0.1.9' 
+              description: 'EngineeO Server API' } // 주소 
+              servers :
+              host: 'localhost:4000' // 기본 root path 
+              basePath: "/" 
+              contact: {
+                   email: "담당자 이메일"
+                 } // 각 api에서 설명을 기록할 때 사용할 constant들을 미리 등록해놓는것 
+              components: { 
+                  securitySchemes: { // BearerAuth 인 경우 
+                    bearerAuth: { 
+                        type: "http" 
+                        scheme: "bearer" 
+                    } 
+                } 
+            } schemes: ["http", "https"] // 가능한 통신 방식 
+            definitions: // 모델 정의 
+            { 
+                CommonResponse: { 
+                    type: 'object' 
+                    required: 
+                    [ // 필수 필드 설정 
+                        "code",
+                         "message" 
+                        ] 
+                    properties: { 
+                        code: { 
+                            type: "string" 
+                            description: "결과코드" 
+                        } 
+                        message: { 
+                            type: "string" 
+                            description: "개발용 메시지" 
+                        } 
+                    } 
+                } 
+            } 
+        }
+        apis: ['./routes/*.js'] // api 파일 위치들(경로 수정 가능)
+*/
+
+module.exports = {
+	swaggerUi,
+	specs,
+};
diff --git a/nest-cli.json b/nest-cli.json
new file mode 100644
index 0000000..f9aa683
--- /dev/null
+++ b/nest-cli.json
@@ -0,0 +1,8 @@
+{
+  "$schema": "https://json.schemastore.org/nest-cli",
+  "collection": "@nestjs/schematics",
+  "sourceRoot": "src",
+  "compilerOptions": {
+    "deleteOutDir": true
+  }
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..70a93e3
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,14979 @@
+{
+  "name": "engineeo-server",
+  "version": "0.0.1",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "engineeo-server",
+      "version": "0.0.1",
+      "license": "UNLICENSED",
+      "dependencies": {
+        "@aws-sdk/client-s3": "^3.309.0",
+        "@aws-sdk/types": "^3.310.0",
+        "@nestjs/common": "^9.0.0",
+        "@nestjs/config": "^2.3.1",
+        "@nestjs/core": "^9.4.0",
+        "@nestjs/jwt": "^10.0.2",
+        "@nestjs/passport": "^9.0.3",
+        "@nestjs/platform-express": "^9.0.0",
+        "@nestjs/swagger": "^6.3.0",
+        "@prisma/client": "^4.13.0",
+        "@sentry/node": "^7.47.0",
+        "@sentry/tracing": "^7.47.0",
+        "@types/aws-sdk": "^2.7.0",
+        "aws-sdk": "^2.1369.0",
+        "axios": "^0.21.4",
+        "bcrypt": "^5.0.1",
+        "body-parser": "^1.19.0",
+        "cheerio": "1.0.0-rc.10",
+        "class-transformer": "^0.5.1",
+        "class-validator": "^0.14.0",
+        "compression": "^1.7.4",
+        "concurrently": "^6.0.2",
+        "connect-redis": "^6.1.3",
+        "cookie-parser": "^1.4.6",
+        "cors": "^2.8.5",
+        "cross-env": "^7.0.3",
+        "crypto": "^1.0.1",
+        "crypto-js": "^4.0.0",
+        "dotenv": "^15.0.1",
+        "dotenv-cli": "^7.1.0",
+        "exceljs": "^4.3.0",
+        "express": "^4.17.1",
+        "express-mysql-session": "^2.1.8",
+        "express-session": "^1.17.3",
+        "form-data": "^4.0.0",
+        "google-auth-library": "^7.3.0",
+        "googleapis": "^89.0.0",
+        "iconv-lite": "^0.6.3",
+        "joi": "^17.9.1",
+        "jsonwebtoken": "^9.0.0",
+        "lru-cache": "^6.0.0",
+        "method-override": "^3.0.0",
+        "multer": "^1.4.3",
+        "multer-s3": "^2.10.0",
+        "mysql2": "^2.2.5",
+        "nest-aws-sdk": "^3.0.1",
+        "nest-raven": "^9.2.0",
+        "node-cache": "^5.1.2",
+        "node-cron": "^3.0.0",
+        "node-sens": "^1.0.1",
+        "nodemailer": "^6.6.2",
+        "passport": "^0.6.0",
+        "passport-jwt": "^4.0.1",
+        "passport-kakao": "^1.0.1",
+        "passport-local": "^1.0.0",
+        "pm2": "^5.1.0",
+        "prisma": "^4.12.0",
+        "readline": "^1.3.0",
+        "redis": "^4.3.1",
+        "reflect-metadata": "^0.1.13",
+        "regex-email": "^1.0.2",
+        "rxjs": "^7.2.0",
+        "swagger-ui-express": "^4.6.2",
+        "web-push": "^3.4.5",
+        "winston": "^3.3.3",
+        "winston-daily-rotate-file": "^4.5.5"
+      },
+      "devDependencies": {
+        "@nestjs/cli": "^9.0.0",
+        "@nestjs/schematics": "^9.0.0",
+        "@nestjs/testing": "^9.4.0",
+        "@types/express": "^4.17.17",
+        "@types/jest": "29.2.4",
+        "@types/node": "18.11.18",
+        "@types/passport-jwt": "^3.0.8",
+        "@types/passport-local": "^1.0.35",
+        "@types/supertest": "^2.0.11",
+        "@typescript-eslint/eslint-plugin": "^5.0.0",
+        "@typescript-eslint/parser": "^5.0.0",
+        "eslint": "^8.0.1",
+        "eslint-config-prettier": "^8.3.0",
+        "eslint-plugin-prettier": "^4.0.0",
+        "jest": "29.3.1",
+        "nodemon": "^2.0.20",
+        "prettier": "^2.3.2",
+        "should": "^13.2.3",
+        "should-http": "^0.1.1",
+        "source-map-support": "^0.5.20",
+        "supertest": "^6.1.6",
+        "swagger-jsdoc": "^6.1.0",
+        "ts-jest": "29.0.3",
+        "ts-loader": "^9.2.3",
+        "ts-node": "^10.0.0",
+        "tsconfig-paths": "4.1.1",
+        "typescript": "^4.7.4"
+      }
+    },
+    "node_modules/@ampproject/remapping": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
+      "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.1.0",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@angular-devkit/core": {
+      "version": "15.2.4",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.4.tgz",
+      "integrity": "sha512-yl+0j1bMwJLKShsyCXw77tbJG8Sd21+itisPLL2MgEpLNAO252kr9zG4TLlFRJyKVftm2l1h78KjqvM5nbOXNg==",
+      "dev": true,
+      "dependencies": {
+        "ajv": "8.12.0",
+        "ajv-formats": "2.1.1",
+        "jsonc-parser": "3.2.0",
+        "rxjs": "6.6.7",
+        "source-map": "0.7.4"
+      },
+      "engines": {
+        "node": "^14.20.0 || ^16.13.0 || >=18.10.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      },
+      "peerDependencies": {
+        "chokidar": "^3.5.2"
+      },
+      "peerDependenciesMeta": {
+        "chokidar": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/core/node_modules/rxjs": {
+      "version": "6.6.7",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+      "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "^1.9.0"
+      },
+      "engines": {
+        "npm": ">=2.0.0"
+      }
+    },
+    "node_modules/@angular-devkit/core/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+      "dev": true
+    },
+    "node_modules/@angular-devkit/schematics": {
+      "version": "15.2.4",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.2.4.tgz",
+      "integrity": "sha512-/W7/vvn59PAVLzhcvD4/N/E8RDhub8ny1A7I96LTRjC5o+yvVV16YJ4YJzolrRrIEN01KmLVQJ9A58VCaweMgw==",
+      "dev": true,
+      "dependencies": {
+        "@angular-devkit/core": "15.2.4",
+        "jsonc-parser": "3.2.0",
+        "magic-string": "0.29.0",
+        "ora": "5.4.1",
+        "rxjs": "6.6.7"
+      },
+      "engines": {
+        "node": "^14.20.0 || ^16.13.0 || >=18.10.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli": {
+      "version": "15.2.4",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-15.2.4.tgz",
+      "integrity": "sha512-QTTKEH5HOkxvQtCxb2Lna2wubehkaIzA6DKUBISijPQliLomw74tzc7lXCywmMqRTbQPVRLG3kBK97hR4x67nA==",
+      "dev": true,
+      "dependencies": {
+        "@angular-devkit/core": "15.2.4",
+        "@angular-devkit/schematics": "15.2.4",
+        "ansi-colors": "4.1.3",
+        "inquirer": "8.2.4",
+        "symbol-observable": "4.0.0",
+        "yargs-parser": "21.1.1"
+      },
+      "bin": {
+        "schematics": "bin/schematics.js"
+      },
+      "engines": {
+        "node": "^14.20.0 || ^16.13.0 || >=18.10.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/inquirer": {
+      "version": "8.2.4",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz",
+      "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==",
+      "dev": true,
+      "dependencies": {
+        "ansi-escapes": "^4.2.1",
+        "chalk": "^4.1.1",
+        "cli-cursor": "^3.1.0",
+        "cli-width": "^3.0.0",
+        "external-editor": "^3.0.3",
+        "figures": "^3.0.0",
+        "lodash": "^4.17.21",
+        "mute-stream": "0.0.8",
+        "ora": "^5.4.1",
+        "run-async": "^2.4.0",
+        "rxjs": "^7.5.5",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0",
+        "through": "^2.3.6",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/@angular-devkit/schematics/node_modules/rxjs": {
+      "version": "6.6.7",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+      "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "^1.9.0"
+      },
+      "engines": {
+        "npm": ">=2.0.0"
+      }
+    },
+    "node_modules/@angular-devkit/schematics/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+      "dev": true
+    },
+    "node_modules/@apidevtools/json-schema-ref-parser": {
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz",
+      "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==",
+      "dev": true,
+      "dependencies": {
+        "@jsdevtools/ono": "^7.1.3",
+        "@types/json-schema": "^7.0.6",
+        "call-me-maybe": "^1.0.1",
+        "js-yaml": "^4.1.0"
+      }
+    },
+    "node_modules/@apidevtools/openapi-schemas": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
+      "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@apidevtools/swagger-methods": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
+      "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==",
+      "dev": true
+    },
+    "node_modules/@apidevtools/swagger-parser": {
+      "version": "10.0.3",
+      "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz",
+      "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==",
+      "dev": true,
+      "dependencies": {
+        "@apidevtools/json-schema-ref-parser": "^9.0.6",
+        "@apidevtools/openapi-schemas": "^2.0.4",
+        "@apidevtools/swagger-methods": "^3.0.2",
+        "@jsdevtools/ono": "^7.1.3",
+        "call-me-maybe": "^1.0.1",
+        "z-schema": "^5.0.1"
+      },
+      "peerDependencies": {
+        "openapi-types": ">=7"
+      }
+    },
+    "node_modules/@aws-crypto/crc32": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz",
+      "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==",
+      "dependencies": {
+        "@aws-crypto/util": "^3.0.0",
+        "@aws-sdk/types": "^3.222.0",
+        "tslib": "^1.11.1"
+      }
+    },
+    "node_modules/@aws-crypto/crc32/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/@aws-crypto/crc32c": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-3.0.0.tgz",
+      "integrity": "sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==",
+      "dependencies": {
+        "@aws-crypto/util": "^3.0.0",
+        "@aws-sdk/types": "^3.222.0",
+        "tslib": "^1.11.1"
+      }
+    },
+    "node_modules/@aws-crypto/crc32c/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/@aws-crypto/ie11-detection": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz",
+      "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==",
+      "dependencies": {
+        "tslib": "^1.11.1"
+      }
+    },
+    "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/@aws-crypto/sha1-browser": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-3.0.0.tgz",
+      "integrity": "sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==",
+      "dependencies": {
+        "@aws-crypto/ie11-detection": "^3.0.0",
+        "@aws-crypto/supports-web-crypto": "^3.0.0",
+        "@aws-crypto/util": "^3.0.0",
+        "@aws-sdk/types": "^3.222.0",
+        "@aws-sdk/util-locate-window": "^3.0.0",
+        "@aws-sdk/util-utf8-browser": "^3.0.0",
+        "tslib": "^1.11.1"
+      }
+    },
+    "node_modules/@aws-crypto/sha1-browser/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/@aws-crypto/sha256-browser": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz",
+      "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==",
+      "dependencies": {
+        "@aws-crypto/ie11-detection": "^3.0.0",
+        "@aws-crypto/sha256-js": "^3.0.0",
+        "@aws-crypto/supports-web-crypto": "^3.0.0",
+        "@aws-crypto/util": "^3.0.0",
+        "@aws-sdk/types": "^3.222.0",
+        "@aws-sdk/util-locate-window": "^3.0.0",
+        "@aws-sdk/util-utf8-browser": "^3.0.0",
+        "tslib": "^1.11.1"
+      }
+    },
+    "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/@aws-crypto/sha256-js": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz",
+      "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==",
+      "dependencies": {
+        "@aws-crypto/util": "^3.0.0",
+        "@aws-sdk/types": "^3.222.0",
+        "tslib": "^1.11.1"
+      }
+    },
+    "node_modules/@aws-crypto/sha256-js/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/@aws-crypto/supports-web-crypto": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz",
+      "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==",
+      "dependencies": {
+        "tslib": "^1.11.1"
+      }
+    },
+    "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/@aws-crypto/util": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz",
+      "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==",
+      "dependencies": {
+        "@aws-sdk/types": "^3.222.0",
+        "@aws-sdk/util-utf8-browser": "^3.0.0",
+        "tslib": "^1.11.1"
+      }
+    },
+    "node_modules/@aws-crypto/util/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/@aws-sdk/abort-controller": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.306.0.tgz",
+      "integrity": "sha512-ewCvdUrMJMlnkNaqXdG7L2H6O7CDI036y6lkTU8gQqa2lCzZvqBkzz6R5NbWqb8TJPi69Z7lXEITgk2b0+pl6w==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/abort-controller/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/chunked-blob-reader": {
+      "version": "3.303.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/chunked-blob-reader/-/chunked-blob-reader-3.303.0.tgz",
+      "integrity": "sha512-Cofcujz08TTKA7CtsIcWyFTyfe84KBa72kTjsXA9GQgn0cuUHZgGoFpBzoyDT8Ff3TPMKmAGDXmllRGFZw4mQw==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      }
+    },
+    "node_modules/@aws-sdk/client-s3": {
+      "version": "3.309.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.309.0.tgz",
+      "integrity": "sha512-StSSRX4PSIaZRNOSkpuEamQLjbDyjHm4p4OqXWTBZYzkiiCntyxCkettYPwkZum2K35uFgfoRda4zmtfnW+kCw==",
+      "dependencies": {
+        "@aws-crypto/sha1-browser": "3.0.0",
+        "@aws-crypto/sha256-browser": "3.0.0",
+        "@aws-crypto/sha256-js": "3.0.0",
+        "@aws-sdk/client-sts": "3.309.0",
+        "@aws-sdk/config-resolver": "3.306.0",
+        "@aws-sdk/credential-provider-node": "3.309.0",
+        "@aws-sdk/eventstream-serde-browser": "3.306.0",
+        "@aws-sdk/eventstream-serde-config-resolver": "3.306.0",
+        "@aws-sdk/eventstream-serde-node": "3.306.0",
+        "@aws-sdk/fetch-http-handler": "3.306.0",
+        "@aws-sdk/hash-blob-browser": "3.306.0",
+        "@aws-sdk/hash-node": "3.306.0",
+        "@aws-sdk/hash-stream-node": "3.306.0",
+        "@aws-sdk/invalid-dependency": "3.306.0",
+        "@aws-sdk/md5-js": "3.306.0",
+        "@aws-sdk/middleware-bucket-endpoint": "3.306.0",
+        "@aws-sdk/middleware-content-length": "3.306.0",
+        "@aws-sdk/middleware-endpoint": "3.306.0",
+        "@aws-sdk/middleware-expect-continue": "3.306.0",
+        "@aws-sdk/middleware-flexible-checksums": "3.306.0",
+        "@aws-sdk/middleware-host-header": "3.306.0",
+        "@aws-sdk/middleware-location-constraint": "3.306.0",
+        "@aws-sdk/middleware-logger": "3.306.0",
+        "@aws-sdk/middleware-recursion-detection": "3.306.0",
+        "@aws-sdk/middleware-retry": "3.306.0",
+        "@aws-sdk/middleware-sdk-s3": "3.306.0",
+        "@aws-sdk/middleware-serde": "3.306.0",
+        "@aws-sdk/middleware-signing": "3.306.0",
+        "@aws-sdk/middleware-ssec": "3.306.0",
+        "@aws-sdk/middleware-stack": "3.306.0",
+        "@aws-sdk/middleware-user-agent": "3.306.0",
+        "@aws-sdk/node-config-provider": "3.306.0",
+        "@aws-sdk/node-http-handler": "3.306.0",
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/signature-v4-multi-region": "3.306.0",
+        "@aws-sdk/smithy-client": "3.309.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/url-parser": "3.306.0",
+        "@aws-sdk/util-base64": "3.303.0",
+        "@aws-sdk/util-body-length-browser": "3.303.0",
+        "@aws-sdk/util-body-length-node": "3.303.0",
+        "@aws-sdk/util-defaults-mode-browser": "3.309.0",
+        "@aws-sdk/util-defaults-mode-node": "3.309.0",
+        "@aws-sdk/util-endpoints": "3.306.0",
+        "@aws-sdk/util-retry": "3.306.0",
+        "@aws-sdk/util-stream-browser": "3.306.0",
+        "@aws-sdk/util-stream-node": "3.306.0",
+        "@aws-sdk/util-user-agent-browser": "3.306.0",
+        "@aws-sdk/util-user-agent-node": "3.306.0",
+        "@aws-sdk/util-utf8": "3.303.0",
+        "@aws-sdk/util-waiter": "3.306.0",
+        "@aws-sdk/xml-builder": "3.303.0",
+        "fast-xml-parser": "4.1.2",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/client-sso": {
+      "version": "3.309.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.309.0.tgz",
+      "integrity": "sha512-2Tr3AROBzZOy+BuANlmDrwgyX+Q2kb6SIlANg6b9mrIzlflC48hRH0ngEe4C5RT6RruKIP+6R0al6vAq8lCk6A==",
+      "dependencies": {
+        "@aws-crypto/sha256-browser": "3.0.0",
+        "@aws-crypto/sha256-js": "3.0.0",
+        "@aws-sdk/config-resolver": "3.306.0",
+        "@aws-sdk/fetch-http-handler": "3.306.0",
+        "@aws-sdk/hash-node": "3.306.0",
+        "@aws-sdk/invalid-dependency": "3.306.0",
+        "@aws-sdk/middleware-content-length": "3.306.0",
+        "@aws-sdk/middleware-endpoint": "3.306.0",
+        "@aws-sdk/middleware-host-header": "3.306.0",
+        "@aws-sdk/middleware-logger": "3.306.0",
+        "@aws-sdk/middleware-recursion-detection": "3.306.0",
+        "@aws-sdk/middleware-retry": "3.306.0",
+        "@aws-sdk/middleware-serde": "3.306.0",
+        "@aws-sdk/middleware-stack": "3.306.0",
+        "@aws-sdk/middleware-user-agent": "3.306.0",
+        "@aws-sdk/node-config-provider": "3.306.0",
+        "@aws-sdk/node-http-handler": "3.306.0",
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/smithy-client": "3.309.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/url-parser": "3.306.0",
+        "@aws-sdk/util-base64": "3.303.0",
+        "@aws-sdk/util-body-length-browser": "3.303.0",
+        "@aws-sdk/util-body-length-node": "3.303.0",
+        "@aws-sdk/util-defaults-mode-browser": "3.309.0",
+        "@aws-sdk/util-defaults-mode-node": "3.309.0",
+        "@aws-sdk/util-endpoints": "3.306.0",
+        "@aws-sdk/util-retry": "3.306.0",
+        "@aws-sdk/util-user-agent-browser": "3.306.0",
+        "@aws-sdk/util-user-agent-node": "3.306.0",
+        "@aws-sdk/util-utf8": "3.303.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/client-sso-oidc": {
+      "version": "3.309.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.309.0.tgz",
+      "integrity": "sha512-5hQMibuKWxDJo6IN+4ah0gskjJa16R41PqkeAOwExthTTyNzgoVyP9wyhnETyntYlHIBrHEmHTwdG06YiAxm4A==",
+      "dependencies": {
+        "@aws-crypto/sha256-browser": "3.0.0",
+        "@aws-crypto/sha256-js": "3.0.0",
+        "@aws-sdk/config-resolver": "3.306.0",
+        "@aws-sdk/fetch-http-handler": "3.306.0",
+        "@aws-sdk/hash-node": "3.306.0",
+        "@aws-sdk/invalid-dependency": "3.306.0",
+        "@aws-sdk/middleware-content-length": "3.306.0",
+        "@aws-sdk/middleware-endpoint": "3.306.0",
+        "@aws-sdk/middleware-host-header": "3.306.0",
+        "@aws-sdk/middleware-logger": "3.306.0",
+        "@aws-sdk/middleware-recursion-detection": "3.306.0",
+        "@aws-sdk/middleware-retry": "3.306.0",
+        "@aws-sdk/middleware-serde": "3.306.0",
+        "@aws-sdk/middleware-stack": "3.306.0",
+        "@aws-sdk/middleware-user-agent": "3.306.0",
+        "@aws-sdk/node-config-provider": "3.306.0",
+        "@aws-sdk/node-http-handler": "3.306.0",
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/smithy-client": "3.309.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/url-parser": "3.306.0",
+        "@aws-sdk/util-base64": "3.303.0",
+        "@aws-sdk/util-body-length-browser": "3.303.0",
+        "@aws-sdk/util-body-length-node": "3.303.0",
+        "@aws-sdk/util-defaults-mode-browser": "3.309.0",
+        "@aws-sdk/util-defaults-mode-node": "3.309.0",
+        "@aws-sdk/util-endpoints": "3.306.0",
+        "@aws-sdk/util-retry": "3.306.0",
+        "@aws-sdk/util-user-agent-browser": "3.306.0",
+        "@aws-sdk/util-user-agent-node": "3.306.0",
+        "@aws-sdk/util-utf8": "3.303.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/client-sts": {
+      "version": "3.309.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.309.0.tgz",
+      "integrity": "sha512-rBVm50ft5o1FLaCNjSFY4c/lI7qPG5MMhOr4sdvEUaU1Mkniyd6M+3Pch9S3a5NtF0Kfzw9dWQpjAL+nqJaITQ==",
+      "dependencies": {
+        "@aws-crypto/sha256-browser": "3.0.0",
+        "@aws-crypto/sha256-js": "3.0.0",
+        "@aws-sdk/config-resolver": "3.306.0",
+        "@aws-sdk/credential-provider-node": "3.309.0",
+        "@aws-sdk/fetch-http-handler": "3.306.0",
+        "@aws-sdk/hash-node": "3.306.0",
+        "@aws-sdk/invalid-dependency": "3.306.0",
+        "@aws-sdk/middleware-content-length": "3.306.0",
+        "@aws-sdk/middleware-endpoint": "3.306.0",
+        "@aws-sdk/middleware-host-header": "3.306.0",
+        "@aws-sdk/middleware-logger": "3.306.0",
+        "@aws-sdk/middleware-recursion-detection": "3.306.0",
+        "@aws-sdk/middleware-retry": "3.306.0",
+        "@aws-sdk/middleware-sdk-sts": "3.306.0",
+        "@aws-sdk/middleware-serde": "3.306.0",
+        "@aws-sdk/middleware-signing": "3.306.0",
+        "@aws-sdk/middleware-stack": "3.306.0",
+        "@aws-sdk/middleware-user-agent": "3.306.0",
+        "@aws-sdk/node-config-provider": "3.306.0",
+        "@aws-sdk/node-http-handler": "3.306.0",
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/smithy-client": "3.309.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/url-parser": "3.306.0",
+        "@aws-sdk/util-base64": "3.303.0",
+        "@aws-sdk/util-body-length-browser": "3.303.0",
+        "@aws-sdk/util-body-length-node": "3.303.0",
+        "@aws-sdk/util-defaults-mode-browser": "3.309.0",
+        "@aws-sdk/util-defaults-mode-node": "3.309.0",
+        "@aws-sdk/util-endpoints": "3.306.0",
+        "@aws-sdk/util-retry": "3.306.0",
+        "@aws-sdk/util-user-agent-browser": "3.306.0",
+        "@aws-sdk/util-user-agent-node": "3.306.0",
+        "@aws-sdk/util-utf8": "3.303.0",
+        "fast-xml-parser": "4.1.2",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/config-resolver": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.306.0.tgz",
+      "integrity": "sha512-kpqHu6LvNMYxullm+tLCsY6KQ2mZUxZTdyWJKTYLZCTxj4HcGJxf4Jxj9dwFAZVl/clcVPGWcHJaQJjyjwzBzw==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-config-provider": "3.295.0",
+        "@aws-sdk/util-middleware": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/config-resolver/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-env": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.306.0.tgz",
+      "integrity": "sha512-DTH+aMvMu+LAoWW+yfPkWzFXt/CPNFQ7+/4xiMnc7FWf+tjt+HZIrPECAV2rBVppNCkh7PC+xDSN61PFvBYOsw==",
+      "dependencies": {
+        "@aws-sdk/property-provider": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-env/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-imds": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.306.0.tgz",
+      "integrity": "sha512-WdrNhq2MwvjZk2I8Of+bZ/qWHG2hREQpwlBiG3tMeEkuywx7M1x3Rt0eHgiR1sTcm05kxNn0rB4OeWOeek37cA==",
+      "dependencies": {
+        "@aws-sdk/node-config-provider": "3.306.0",
+        "@aws-sdk/property-provider": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/url-parser": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-imds/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-ini": {
+      "version": "3.309.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.309.0.tgz",
+      "integrity": "sha512-7xAqfbuvEdQdz2YcS5OPWH6uv09pMEW6lvmEwM8tf3gn/c3mxFm0/geFeO3+hnkIjByPM02PW7qQJXmPu1l7AA==",
+      "dependencies": {
+        "@aws-sdk/credential-provider-env": "3.306.0",
+        "@aws-sdk/credential-provider-imds": "3.306.0",
+        "@aws-sdk/credential-provider-process": "3.306.0",
+        "@aws-sdk/credential-provider-sso": "3.309.0",
+        "@aws-sdk/credential-provider-web-identity": "3.306.0",
+        "@aws-sdk/property-provider": "3.306.0",
+        "@aws-sdk/shared-ini-file-loader": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-node": {
+      "version": "3.309.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.309.0.tgz",
+      "integrity": "sha512-rgf53RH9mcATr+5rRGGqRmoOEceX+XSbQvGM1QRHxROJJiYsZWdBQu9w+UuKcQF03qLMfi4G+6iNHect5TVs2Q==",
+      "dependencies": {
+        "@aws-sdk/credential-provider-env": "3.306.0",
+        "@aws-sdk/credential-provider-imds": "3.306.0",
+        "@aws-sdk/credential-provider-ini": "3.309.0",
+        "@aws-sdk/credential-provider-process": "3.306.0",
+        "@aws-sdk/credential-provider-sso": "3.309.0",
+        "@aws-sdk/credential-provider-web-identity": "3.306.0",
+        "@aws-sdk/property-provider": "3.306.0",
+        "@aws-sdk/shared-ini-file-loader": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-process": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.306.0.tgz",
+      "integrity": "sha512-2RezGskHqJeHtGbK7CqhGNAoqXgQJb7FfPFqwUQ9oVDZS8f145jVwajjHcc7Qn3IwGoqylMF3uXIljUv89uDzA==",
+      "dependencies": {
+        "@aws-sdk/property-provider": "3.306.0",
+        "@aws-sdk/shared-ini-file-loader": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-process/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-sso": {
+      "version": "3.309.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.309.0.tgz",
+      "integrity": "sha512-uMphs47O2S9NK7I5CsDttp88X7b/JktGOrW8RTLRw1QURQ8v0uP+MLHFogRtWi4E7+zo86Equ0njlpYlFvrpSA==",
+      "dependencies": {
+        "@aws-sdk/client-sso": "3.309.0",
+        "@aws-sdk/property-provider": "3.306.0",
+        "@aws-sdk/shared-ini-file-loader": "3.306.0",
+        "@aws-sdk/token-providers": "3.309.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-web-identity": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.306.0.tgz",
+      "integrity": "sha512-MOQGQaOtdo4zLQZ1bRjD2n1PUzfNty+sKe+1wlm5bIqTN93UX3S8f0QznucZr7uJxI4Z14ZLwuYeAUV4Tgchlw==",
+      "dependencies": {
+        "@aws-sdk/property-provider": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/eventstream-codec": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.306.0.tgz",
+      "integrity": "sha512-tQCo0tl/NdD3rVt9hDkGO3SLPXdohlRx6j9IMor5HpM7tCFcB4WNpDHPHyClxRoprLT04BZksJZcbVUybAeTcA==",
+      "dependencies": {
+        "@aws-crypto/crc32": "3.0.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-hex-encoding": "3.295.0",
+        "tslib": "^2.5.0"
+      }
+    },
+    "node_modules/@aws-sdk/eventstream-codec/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/eventstream-serde-browser": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-browser/-/eventstream-serde-browser-3.306.0.tgz",
+      "integrity": "sha512-Vbx0KQzZ5YAeYwC3tbCGsJZ2nSqKQlQFbuwhXe/Btz/dh1HaJPMxoIC8Dr0b6Z7S8l2Pq4ucqdNaUzLeJJbezw==",
+      "dependencies": {
+        "@aws-sdk/eventstream-serde-universal": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/eventstream-serde-browser/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/eventstream-serde-config-resolver": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.306.0.tgz",
+      "integrity": "sha512-MBNJspHTbP3+bZeDgfH+FcAxBodKH3NnXmSCyUosMJMaNQ8JtqsaW+y6FfvC6aVht3FyC6qKWBjuKfEeG2jLCA==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/eventstream-serde-config-resolver/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/eventstream-serde-node": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-node/-/eventstream-serde-node-3.306.0.tgz",
+      "integrity": "sha512-Fq6n8vUMxMSqWFD2xSjh/X7Wyr+CggGHjsVgIFfXIXEK14iTOnOK7/yS86mDR6GBRupIfrw/Q+Jf0A7b8o2Wlg==",
+      "dependencies": {
+        "@aws-sdk/eventstream-serde-universal": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/eventstream-serde-node/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/eventstream-serde-universal": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-universal/-/eventstream-serde-universal-3.306.0.tgz",
+      "integrity": "sha512-Xp2JCRkZm3dyVv2O/EJnOP1vwUwEmXXminbX1MgnuVIMifSbbjeo6lGqCsjrIkXyymkx4TvrmPmSWlwzPCnyVw==",
+      "dependencies": {
+        "@aws-sdk/eventstream-codec": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/eventstream-serde-universal/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/fetch-http-handler": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.306.0.tgz",
+      "integrity": "sha512-T8OODOnPpDqkXS+XSMIkd6hf90h833JLN93wq3ibbyD/WvGveufFFHsbsNyccE9+CSv/BjEuN5QbHqTKTp3BlA==",
+      "dependencies": {
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/querystring-builder": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-base64": "3.303.0",
+        "tslib": "^2.5.0"
+      }
+    },
+    "node_modules/@aws-sdk/fetch-http-handler/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/hash-blob-browser": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/hash-blob-browser/-/hash-blob-browser-3.306.0.tgz",
+      "integrity": "sha512-OK3xQ60Vz1VTDgMGk8PjFESQlDjXCLR/q1xc9ODyXBO2/A+ntUxMBcGfu7KYjtGI69g91H/BGhPAy+W5Gg+rmw==",
+      "dependencies": {
+        "@aws-sdk/chunked-blob-reader": "3.303.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      }
+    },
+    "node_modules/@aws-sdk/hash-blob-browser/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/hash-node": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.306.0.tgz",
+      "integrity": "sha512-EcSLd6gKoDEEBPZqEv+Ky9gIyefwyyrAJGILGKoYBmcOIY7Y0xKId0hxCa9/1xvWTaVC1u+rA06DGgksZOa78w==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-buffer-from": "3.303.0",
+        "@aws-sdk/util-utf8": "3.303.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/hash-node/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/hash-stream-node": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/hash-stream-node/-/hash-stream-node-3.306.0.tgz",
+      "integrity": "sha512-9owhr/kmCRwRZWAeGnf3k+L5tD/DcLAEMfbIuH8uolEigI+YGRMXnJB0PpyzIyd1N8TM/bm6WW3uvQiFHy7Hxw==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-utf8": "3.303.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/hash-stream-node/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/invalid-dependency": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.306.0.tgz",
+      "integrity": "sha512-9Mkcr+qG7QR4R5bJcA8bBNd8E2x6WaZStsQ3QeFbdQr3V3Tunvra/KlCFsEL55GgU8BZt5isOaHqq7uxs5ILtQ==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      }
+    },
+    "node_modules/@aws-sdk/invalid-dependency/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/is-array-buffer": {
+      "version": "3.303.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.303.0.tgz",
+      "integrity": "sha512-IitBTr+pou7v5BrYLFH/SbIf3g1LIgMhcI3bDXBq2FjzmDftj4bW8BOmg05b9YKf2TrrggvJ4yk/jH+yYFXoJQ==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/md5-js": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/md5-js/-/md5-js-3.306.0.tgz",
+      "integrity": "sha512-QCv5hfFL3AsSQPZvO8YOaBoNlRu6+B2lVSVZHLaWrortlaRGQi5+fYgYPhpTho/2cw0qeogB4pQIho3UgCAmfg==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-utf8": "3.303.0",
+        "tslib": "^2.5.0"
+      }
+    },
+    "node_modules/@aws-sdk/md5-js/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-bucket-endpoint": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.306.0.tgz",
+      "integrity": "sha512-DL8Q146s0nJ7MSHxt8sr6MDR4tIh+q7z9Yki7o1soCMNGTaYCUC20nSffs7IhQAUo1/q3pRh8LoJNtIpa7Q3pA==",
+      "dependencies": {
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-arn-parser": "3.295.0",
+        "@aws-sdk/util-config-provider": "3.295.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-content-length": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.306.0.tgz",
+      "integrity": "sha512-JbONf2Ms+/DVRcpFNsKGdOQU94Js56KV+AhlPJmCwLxfyWvQjTt0KxFC1Dd+cjeNEXUduvBarrehgsqFlWnoHQ==",
+      "dependencies": {
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-content-length/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-endpoint": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.306.0.tgz",
+      "integrity": "sha512-i3QRiwgkcsuVN55O7l8I/QGwCypGRZXdYkPjU56LI2w2oiZ82f/nVMNXVc+ZFm2YH7WbCE+5jguw2J7HXdOlyQ==",
+      "dependencies": {
+        "@aws-sdk/middleware-serde": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/url-parser": "3.306.0",
+        "@aws-sdk/util-middleware": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-endpoint/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-expect-continue": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.306.0.tgz",
+      "integrity": "sha512-k+UgyQpTqFaizClQRdVTpcGJduRXKS0GCDDV/pdz1NVx09YHCIxVpkpdkYj+2T1Ufp3bK7ju2FLaXJwoO6/r+Q==",
+      "dependencies": {
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-expect-continue/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-flexible-checksums": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.306.0.tgz",
+      "integrity": "sha512-0Z3eKIVdP9son45Id18pDWwfcqoYTzDsSfePMD//HByBQQusL8pS1D1mlWqVpnV++ZfNmPcnph+v6cIfHYVM4Q==",
+      "dependencies": {
+        "@aws-crypto/crc32": "3.0.0",
+        "@aws-crypto/crc32c": "3.0.0",
+        "@aws-sdk/is-array-buffer": "3.303.0",
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-utf8": "3.303.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-host-header": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.306.0.tgz",
+      "integrity": "sha512-mHDHK9E+c7HwMlrCJ+VFSB6tkq8oJVkYEHCvPkdrnzN/g9P/d/UhPIeGapZXMbAIZEaLpEGqs536mYzeRKZG8A==",
+      "dependencies": {
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-host-header/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-location-constraint": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.306.0.tgz",
+      "integrity": "sha512-TfSPLDcl/QTs2OHplJhbJMzdWe3/du5wrNn0LECligzX4f7Ygn2zV/UtqHet36tv206ssDm6b9g8KvRvG3FT2g==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-location-constraint/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-logger": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.306.0.tgz",
+      "integrity": "sha512-1FRHp/QB0Lb+CgP+c9CYW6BZh+q+5pnuOKo/Rd6hjYiM+kT1G/cWdXnMJQBR4rbTCTixbqCnObNJ1EyP/ofQhQ==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-logger/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-recursion-detection": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.306.0.tgz",
+      "integrity": "sha512-Hpj42ZLmwCy/CtVxi57NTeOEPoUJlivF3VIgowZ9JhaF61cakVKyrJ+f3jwXciDUtuYrdKm5Wf6prW6apWo0YA==",
+      "dependencies": {
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-recursion-detection/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-retry": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.306.0.tgz",
+      "integrity": "sha512-eMyfr/aeurXXDz4x+WVrvLI8fVDP6klJOjziBEWZ/MUNP/hTFhkiQsMVbvT6O4Pspp7+FgCSdcUPG6Os2gK+CQ==",
+      "dependencies": {
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/service-error-classification": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-middleware": "3.306.0",
+        "@aws-sdk/util-retry": "3.306.0",
+        "tslib": "^2.5.0",
+        "uuid": "^8.3.2"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-retry/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-retry/node_modules/uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-sdk-s3": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.306.0.tgz",
+      "integrity": "sha512-x1V4NKpY8waDBTUjg2TLcqLZjD6OoED0NMFvKPxul/l2rzInvcNK6L2ifxxhYb7YG9N4MtLnCvAUqEW5h3eLYw==",
+      "dependencies": {
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-arn-parser": "3.295.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-sdk-sts": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.306.0.tgz",
+      "integrity": "sha512-2rSAR3nc5faYuEnh1KxQMCMCkEkJyaDfA3zwWLqZ+/TBCH0PlPkBv+Z9yXmteEki0vI5Hr+e+atTutJZoyG13g==",
+      "dependencies": {
+        "@aws-sdk/middleware-signing": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-sdk-sts/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-serde": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.306.0.tgz",
+      "integrity": "sha512-M3gyPLPduZXMvdgt4XEpVO+3t0ZVPdgeQQwG6JnXv0dgyUizshYs4lrVOAb1KwF6StsmkrAgSN+I273elLiKjA==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-serde/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-signing": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.306.0.tgz",
+      "integrity": "sha512-JhpSriN4xa4a/p5gAPL0OWFKJF4eWYU3K+LLlXBNGMbxg/qNL4skgT4dMFe3ii9EW8kI+r6tpvSgC+lP7/Tyng==",
+      "dependencies": {
+        "@aws-sdk/property-provider": "3.306.0",
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/signature-v4": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-middleware": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-signing/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-ssec": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.306.0.tgz",
+      "integrity": "sha512-4oXDBsQSPWxyr5K9OjQA1Y+WChGYLD85mvcycd3WqKn9BKsQ9F4enE3XHmRg0qlgXV9CJ/fG1axLC3R9Aa6u/g==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-ssec/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-stack": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.306.0.tgz",
+      "integrity": "sha512-G//a6MVSxyFVpOMZ+dzT3+w7XblOd2tRJ5g+/okjn3pNBLbo5o9Hu33K/bz0SQjT/m5mU2F9m0wcdCPYbRPysg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-user-agent": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.306.0.tgz",
+      "integrity": "sha512-tP6I+Lbs68muPfdMA6Rfc+8fYo49nEn9A3RMiOU2COClWsmiZatpbK9UYlqIOxeGB/s2jI7hXmQq6tT2LStLSg==",
+      "dependencies": {
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-endpoints": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/middleware-user-agent/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/node-config-provider": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.306.0.tgz",
+      "integrity": "sha512-+m+ALxNx5E1zLPPijO1pAbT5tnofLzZFWlnSYBEiOIwzaRU44rLYDqAhgXJkMMbOECkffDrv6ym0oWJIwJI+DA==",
+      "dependencies": {
+        "@aws-sdk/property-provider": "3.306.0",
+        "@aws-sdk/shared-ini-file-loader": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/node-config-provider/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/node-http-handler": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.306.0.tgz",
+      "integrity": "sha512-qvNSIVdGf0pnWEXsAulIqXk7LML25Zc1yxbujxoAj8oX5y+mDhzQdHKrMgc0FuI4RKoEd9px4DYoUbmTWrrxwA==",
+      "dependencies": {
+        "@aws-sdk/abort-controller": "3.306.0",
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/querystring-builder": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/node-http-handler/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/property-provider": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.306.0.tgz",
+      "integrity": "sha512-37PnbjpANjHys0Y+DVmKUz1JbSGZ/mAndZeplTUsFDUtbNwJRw/fDyWUvGC82JWB4gNSP5muWscFvetZnK2l8A==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/property-provider/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/protocol-http": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.306.0.tgz",
+      "integrity": "sha512-6Z8bqB8Ydz/qG7+lJzjwsjIca2w2zp4nZ2HjxMoUm0NBbVXGDx7H9qy9eOUqEiCbdXbsfK2BmVQreLhFLt056Q==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/protocol-http/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/querystring-builder": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.306.0.tgz",
+      "integrity": "sha512-kvz6fLwE4KojTxbphuo9JPwKKuhau2mmSurnqhtf77t9+0cOh2uzyYhIUtOFewpLj+qGoh4b2EODlJqczc7IKg==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-uri-escape": "3.303.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/querystring-builder/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/querystring-parser": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.306.0.tgz",
+      "integrity": "sha512-YjOdLcyS/8sNkFPgnxyUx+cM/P2XFGCA2WjQ0e9AXX8xFFkmnY6U5w2EknQ5zyvKy+R/KAV0KAMJBUB+ofjg0A==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/querystring-parser/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/service-error-classification": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.306.0.tgz",
+      "integrity": "sha512-lmXIVHWU5J60GmmTgyj79kupWYg5ntyNrUPt1P9FYTsXz+tdk4YYH7/2IxZ1XjBr4jEsN56gfSI0cfT07ztQJA==",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/shared-ini-file-loader": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.306.0.tgz",
+      "integrity": "sha512-mDmBRN+Y0+EBD5megId97UIJGV/rmRsAds22qy0mmVdD3X7qlxn974btXVgfZyda6qw/pX6hgi8X99Qj6Wjb0w==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/shared-ini-file-loader/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/signature-v4": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.306.0.tgz",
+      "integrity": "sha512-yoQTo6wLirKHg34Zhm8tKmfEaK8fOn+psVdMtRs2vGq3uzKLb+YW5zywnujoVwBvygQTWxiDMwRxDduWAisccA==",
+      "dependencies": {
+        "@aws-sdk/is-array-buffer": "3.303.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-hex-encoding": "3.295.0",
+        "@aws-sdk/util-middleware": "3.306.0",
+        "@aws-sdk/util-uri-escape": "3.303.0",
+        "@aws-sdk/util-utf8": "3.303.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/signature-v4-multi-region": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.306.0.tgz",
+      "integrity": "sha512-c+J/7oYZf1MWgEqHyzW6EM25FZfqHer+pMWwyaDCEQFsAuuNeqbvr5MWFrjYpSeF8MpfVbFsEfho5XmjHMq6jw==",
+      "dependencies": {
+        "@aws-sdk/protocol-http": "3.306.0",
+        "@aws-sdk/signature-v4": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "@aws-sdk/signature-v4-crt": "^3.118.0"
+      },
+      "peerDependenciesMeta": {
+        "@aws-sdk/signature-v4-crt": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/signature-v4/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/smithy-client": {
+      "version": "3.309.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.309.0.tgz",
+      "integrity": "sha512-2+LJD8/J9yoYmfjLZuMTI/IF8qFMMclWdDJaalj4Rzzd7qBWDS3Q23UxpZi9VR155nqpgr/R+TFZMgze1EhRHg==",
+      "dependencies": {
+        "@aws-sdk/middleware-stack": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/smithy-client/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/token-providers": {
+      "version": "3.309.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.309.0.tgz",
+      "integrity": "sha512-rB79nQArhVT3l8UglZyinZVm13hFRF4xqzrmSLNknxdlMLamrON/94H7S6lFLywdTags2SUdAxQ/LlStlFf78A==",
+      "dependencies": {
+        "@aws-sdk/client-sso-oidc": "3.309.0",
+        "@aws-sdk/property-provider": "3.306.0",
+        "@aws-sdk/shared-ini-file-loader": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/token-providers/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/types": {
+      "version": "3.310.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.310.0.tgz",
+      "integrity": "sha512-j8eamQJ7YcIhw7fneUfs8LYl3t01k4uHi4ZDmNRgtbmbmTTG3FZc2MotStZnp3nZB6vLiPF1o5aoJxWVvkzS6A==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/url-parser": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.306.0.tgz",
+      "integrity": "sha512-mhyOjtycZgxKYo2CoDhDQONuRd5TLfEwmyGWVgFrfubF0LejQ3rkBRLC5zT9TBZ8RJHNlqU2oGdsZCy3JV6Rlw==",
+      "dependencies": {
+        "@aws-sdk/querystring-parser": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      }
+    },
+    "node_modules/@aws-sdk/url-parser/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-arn-parser": {
+      "version": "3.295.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.295.0.tgz",
+      "integrity": "sha512-kSSVymcbjyQQHvCZaTt1teKKW4MSSMPRdPNxSNO1aLsVwxrWdnAggDrpHwFjvPCRUcKtpThepATOz75PfUm9Bg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-base64": {
+      "version": "3.303.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.303.0.tgz",
+      "integrity": "sha512-oj+p/GHHPcZEKjiiOHU/CyNQeh8i+8dfMMzU+VGdoK5jHaVG8h2b+V7GPf7I4wDkG2ySCK5b5Jw5NUHwdTJ13Q==",
+      "dependencies": {
+        "@aws-sdk/util-buffer-from": "3.303.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-body-length-browser": {
+      "version": "3.303.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.303.0.tgz",
+      "integrity": "sha512-T643m0pKzgjAvPFy4W8zL+aszG3T22U8hb6stlMvT0z++Smv8QfIvkIkXjWyH2KlOt5GKliHwdOv8SAi0FSMJQ==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-body-length-node": {
+      "version": "3.303.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.303.0.tgz",
+      "integrity": "sha512-/hS8z6e18Le60hJr2TUIFoUjUiAsnQsuDn6DxX74GXhMOHeSwZDJ9jHF39quYkNMmAE37GrVH4MI9vE0pN27qw==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-buffer-from": {
+      "version": "3.303.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.303.0.tgz",
+      "integrity": "sha512-hUU+NW+SW6RNojtAKnnmz+tDShVKlEx2YsS4a5fSfrKRUes+zWz10cxVX0RQfysd3R6tdSHhbjsSj8eCIybheg==",
+      "dependencies": {
+        "@aws-sdk/is-array-buffer": "3.303.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-config-provider": {
+      "version": "3.295.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.295.0.tgz",
+      "integrity": "sha512-/5Dl1aV2yI8YQjqwmg4RTnl/E9NmNsx7HIwBZt+dTcOrM0LMUwczQBFFcLyqCj/qv5y+VsvLoAAA/OiBT7hb3w==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-defaults-mode-browser": {
+      "version": "3.309.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.309.0.tgz",
+      "integrity": "sha512-KTmoR24PhUCT9A8/f5rb7MQvzXqGJY7/VnYxNaQ6AzJZfZ3y3UYfvuJR9LRjWn+zQDy1lnTyjSh5eokf2VBOoQ==",
+      "dependencies": {
+        "@aws-sdk/property-provider": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "bowser": "^2.11.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-defaults-mode-browser/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-defaults-mode-node": {
+      "version": "3.309.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.309.0.tgz",
+      "integrity": "sha512-3YIEWY6O5kyW6dbV+1jWdlsqjEN76sxY62841v5A9Vr/MGLowhm6YYW8MYWPye9RABl9osTs0NCeL2p6Re+IPw==",
+      "dependencies": {
+        "@aws-sdk/config-resolver": "3.306.0",
+        "@aws-sdk/credential-provider-imds": "3.306.0",
+        "@aws-sdk/node-config-provider": "3.306.0",
+        "@aws-sdk/property-provider": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-defaults-mode-node/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-endpoints": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.306.0.tgz",
+      "integrity": "sha512-aPTqU4VGhec8LDhKZrfA3/sBHTYRa0favKEo8aEa/vIZJTNBAFlUhvr5z7peAr8gBOtZZcElzX8PiK3jjn3ILw==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-endpoints/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-hex-encoding": {
+      "version": "3.295.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.295.0.tgz",
+      "integrity": "sha512-XJcoVo41kHzhe28PBm/rqt5mdCp8R6abwiW9ug1dA6FOoPUO8kBUxDv6xaOmA2hfRvd2ocFfBXaUCBqUowkGcQ==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-locate-window": {
+      "version": "3.295.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.295.0.tgz",
+      "integrity": "sha512-d/s+zhUx5Kh4l/ecMP/TBjzp1GR/g89Q4nWH6+wH5WgdHsK+LG+vmsk6mVNuP/8wsCofYG4NBqp5Ulbztbm9QA==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-middleware": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.306.0.tgz",
+      "integrity": "sha512-14CSm1mTrfSNBGbkZu8vSjXYg7DUMfZc74IinOajcFtTswa/6SyiyhU9DK0a837qqwxSfFGpnE2thVeJIF/7FA==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-retry": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.306.0.tgz",
+      "integrity": "sha512-zcgTEIehQAIAm4vBNWfXZpDNbIrDM095vZmpbozQwK/pfDqMGvq7j3r9atKuEGTtoomoGoYwj3x/KEhO6JXJLg==",
+      "dependencies": {
+        "@aws-sdk/service-error-classification": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-stream-browser": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-stream-browser/-/util-stream-browser-3.306.0.tgz",
+      "integrity": "sha512-g/UdUlVtMrdctzu8Yk6UKIlN9OtqGyTDC3bCzzr6dNmfdRMuilL5LZCAgusEEzH2MZGTNtw5BbYZBN//UNhxwQ==",
+      "dependencies": {
+        "@aws-sdk/fetch-http-handler": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-base64": "3.303.0",
+        "@aws-sdk/util-hex-encoding": "3.295.0",
+        "@aws-sdk/util-utf8": "3.303.0",
+        "tslib": "^2.5.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-stream-browser/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-stream-node": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-stream-node/-/util-stream-node-3.306.0.tgz",
+      "integrity": "sha512-hF8P52E3borUp6Z4K8cQOGm+bJgkbkE/VSl0EiO29E563ZiPnT/VBYVICX7MIDNPgHKDN5n/OSVfe1egjhOigQ==",
+      "dependencies": {
+        "@aws-sdk/node-http-handler": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "@aws-sdk/util-buffer-from": "3.303.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-stream-node/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-uri-escape": {
+      "version": "3.303.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.303.0.tgz",
+      "integrity": "sha512-N3ULNuHCL3QzAlCTY+XRRkRQTYCTU8RRuzFCJX0pDpz9t2K+tLT7DbxqupWGNFGl5Xlulf1Is14J3BP/Dx91rA==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-user-agent-browser": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.306.0.tgz",
+      "integrity": "sha512-uZAtpvCasUdWRlB/nEjN0gf6G7810hT50VyWjpd6mQW78myV8M5fu/R03UFAZ+D8fhqqIdzR/IXDY1QUGp8bCA==",
+      "dependencies": {
+        "@aws-sdk/types": "3.306.0",
+        "bowser": "^2.11.0",
+        "tslib": "^2.5.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-user-agent-browser/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-user-agent-node": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.306.0.tgz",
+      "integrity": "sha512-zLp9wIx7FZ0qFLimYW3lJ1uJM5gqxmmcQjNimUaUq/4a1caDkaiF/QeyyMFva+wIjyHRv22P5abUBjIEZrs5WA==",
+      "dependencies": {
+        "@aws-sdk/node-config-provider": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "aws-crt": ">=1.0.0"
+      },
+      "peerDependenciesMeta": {
+        "aws-crt": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@aws-sdk/util-user-agent-node/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-utf8": {
+      "version": "3.303.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.303.0.tgz",
+      "integrity": "sha512-tZXVuMOIONPOuOGBs/XRdzxv6jUvTM620dRFFIHZwlGiW8bo0x0LlonrzDAJZA4e9ZwmxJIj8Ji13WVRBGvZWg==",
+      "dependencies": {
+        "@aws-sdk/util-buffer-from": "3.303.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-utf8-browser": {
+      "version": "3.259.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz",
+      "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==",
+      "dependencies": {
+        "tslib": "^2.3.1"
+      }
+    },
+    "node_modules/@aws-sdk/util-waiter": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-waiter/-/util-waiter-3.306.0.tgz",
+      "integrity": "sha512-/cCmEaxGJOVKHuuzm4zM3aY2Un7pJGyewcd9WWvLjZIoF9jCCqyjmsxM+OXdCjs7NOdo41cValYhILYI+nD8Tg==",
+      "dependencies": {
+        "@aws-sdk/abort-controller": "3.306.0",
+        "@aws-sdk/types": "3.306.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/util-waiter/node_modules/@aws-sdk/types": {
+      "version": "3.306.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.306.0.tgz",
+      "integrity": "sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@aws-sdk/xml-builder": {
+      "version": "3.303.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.303.0.tgz",
+      "integrity": "sha512-Oht8XdmCkLhwZx2WTjOOLN8rt9000zJS4Hehv9NG7+kKfaA6sKFGIculmumaS+h8hAwWFndtgpOTlKC95zHSWQ==",
+      "dependencies": {
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
+      "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+      "dev": true,
+      "dependencies": {
+        "@babel/highlight": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/compat-data": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz",
+      "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/core": {
+      "version": "7.21.3",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz",
+      "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==",
+      "dev": true,
+      "dependencies": {
+        "@ampproject/remapping": "^2.2.0",
+        "@babel/code-frame": "^7.18.6",
+        "@babel/generator": "^7.21.3",
+        "@babel/helper-compilation-targets": "^7.20.7",
+        "@babel/helper-module-transforms": "^7.21.2",
+        "@babel/helpers": "^7.21.0",
+        "@babel/parser": "^7.21.3",
+        "@babel/template": "^7.20.7",
+        "@babel/traverse": "^7.21.3",
+        "@babel/types": "^7.21.3",
+        "convert-source-map": "^1.7.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.2",
+        "semver": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/babel"
+      }
+    },
+    "node_modules/@babel/core/node_modules/convert-source-map": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+      "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+      "dev": true
+    },
+    "node_modules/@babel/core/node_modules/semver": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/generator": {
+      "version": "7.21.3",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz",
+      "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.21.3",
+        "@jridgewell/gen-mapping": "^0.3.2",
+        "@jridgewell/trace-mapping": "^0.3.17",
+        "jsesc": "^2.5.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+      "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/set-array": "^1.0.1",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz",
+      "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/compat-data": "^7.20.5",
+        "@babel/helper-validator-option": "^7.18.6",
+        "browserslist": "^4.21.3",
+        "lru-cache": "^5.1.1",
+        "semver": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "dev": true
+    },
+    "node_modules/@babel/helper-environment-visitor": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
+      "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-function-name": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
+      "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/template": "^7.20.7",
+        "@babel/types": "^7.21.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-hoist-variables": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
+      "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-imports": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
+      "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-transforms": {
+      "version": "7.21.2",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz",
+      "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-module-imports": "^7.18.6",
+        "@babel/helper-simple-access": "^7.20.2",
+        "@babel/helper-split-export-declaration": "^7.18.6",
+        "@babel/helper-validator-identifier": "^7.19.1",
+        "@babel/template": "^7.20.7",
+        "@babel/traverse": "^7.21.2",
+        "@babel/types": "^7.21.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-plugin-utils": {
+      "version": "7.20.2",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz",
+      "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-simple-access": {
+      "version": "7.20.2",
+      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
+      "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.20.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-split-export-declaration": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
+      "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.19.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+      "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.19.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+      "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-option": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz",
+      "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helpers": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz",
+      "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/template": "^7.20.7",
+        "@babel/traverse": "^7.21.0",
+        "@babel/types": "^7.21.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/highlight": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+      "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.18.6",
+        "chalk": "^2.0.0",
+        "js-tokens": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+      "dev": true
+    },
+    "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.21.3",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz",
+      "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==",
+      "dev": true,
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-async-generators": {
+      "version": "7.8.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+      "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-bigint": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
+      "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-class-properties": {
+      "version": "7.12.13",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+      "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.12.13"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-import-meta": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+      "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-json-strings": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+      "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-jsx": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz",
+      "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+      "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+      "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-numeric-separator": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+      "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-object-rest-spread": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+      "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-optional-catch-binding": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+      "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-optional-chaining": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+      "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-top-level-await": {
+      "version": "7.14.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+      "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.14.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-typescript": {
+      "version": "7.20.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz",
+      "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.19.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/template": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+      "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.18.6",
+        "@babel/parser": "^7.20.7",
+        "@babel/types": "^7.20.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/traverse": {
+      "version": "7.21.3",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz",
+      "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.18.6",
+        "@babel/generator": "^7.21.3",
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-function-name": "^7.21.0",
+        "@babel/helper-hoist-variables": "^7.18.6",
+        "@babel/helper-split-export-declaration": "^7.18.6",
+        "@babel/parser": "^7.21.3",
+        "@babel/types": "^7.21.3",
+        "debug": "^4.1.0",
+        "globals": "^11.1.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/traverse/node_modules/globals": {
+      "version": "11.12.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.21.3",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz",
+      "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.19.4",
+        "@babel/helper-validator-identifier": "^7.19.1",
+        "to-fast-properties": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@bcoe/v8-coverage": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+      "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+      "dev": true
+    },
+    "node_modules/@colors/colors": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+      "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+      "engines": {
+        "node": ">=0.1.90"
+      }
+    },
+    "node_modules/@cspotcode/source-map-support": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+      "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/trace-mapping": "0.3.9"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.9",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+      "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.0.3",
+        "@jridgewell/sourcemap-codec": "^1.4.10"
+      }
+    },
+    "node_modules/@dabh/diagnostics": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
+      "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
+      "dependencies": {
+        "colorspace": "1.1.x",
+        "enabled": "2.0.x",
+        "kuler": "^2.0.0"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz",
+      "integrity": "sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA==",
+      "dev": true,
+      "dependencies": {
+        "eslint-visitor-keys": "^3.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
+    },
+    "node_modules/@eslint-community/regexpp": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz",
+      "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==",
+      "dev": true,
+      "engines": {
+        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@eslint/eslintrc": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz",
+      "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==",
+      "dev": true,
+      "dependencies": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^9.5.0",
+        "globals": "^13.19.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "node_modules/@eslint/js": {
+      "version": "8.36.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz",
+      "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@fast-csv/format": {
+      "version": "4.3.5",
+      "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz",
+      "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==",
+      "dependencies": {
+        "@types/node": "^14.0.1",
+        "lodash.escaperegexp": "^4.1.2",
+        "lodash.isboolean": "^3.0.3",
+        "lodash.isequal": "^4.5.0",
+        "lodash.isfunction": "^3.0.9",
+        "lodash.isnil": "^4.0.0"
+      }
+    },
+    "node_modules/@fast-csv/format/node_modules/@types/node": {
+      "version": "14.18.40",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.40.tgz",
+      "integrity": "sha512-pGteXO/JQX7wPxGR8lyT+doqjMa7XvlVowwrDwLfX92k5SdLkk4cwC7CYSLBxrenw/R5oQwKioVIak7ZgplM3g=="
+    },
+    "node_modules/@fast-csv/parse": {
+      "version": "4.3.6",
+      "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz",
+      "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==",
+      "dependencies": {
+        "@types/node": "^14.0.1",
+        "lodash.escaperegexp": "^4.1.2",
+        "lodash.groupby": "^4.6.0",
+        "lodash.isfunction": "^3.0.9",
+        "lodash.isnil": "^4.0.0",
+        "lodash.isundefined": "^3.0.1",
+        "lodash.uniq": "^4.5.0"
+      }
+    },
+    "node_modules/@fast-csv/parse/node_modules/@types/node": {
+      "version": "14.18.40",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.40.tgz",
+      "integrity": "sha512-pGteXO/JQX7wPxGR8lyT+doqjMa7XvlVowwrDwLfX92k5SdLkk4cwC7CYSLBxrenw/R5oQwKioVIak7ZgplM3g=="
+    },
+    "node_modules/@graphql-tools/merge": {
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.4.0.tgz",
+      "integrity": "sha512-3XYCWe0d3I4F1azNj1CdShlbHfTIfiDgj00R9uvFH8tHKh7i1IWN3F7QQYovcHKhayaR6zPok3YYMESYQcBoaA==",
+      "optional": true,
+      "dependencies": {
+        "@graphql-tools/utils": "9.2.1",
+        "tslib": "^2.4.0"
+      },
+      "peerDependencies": {
+        "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+      }
+    },
+    "node_modules/@graphql-tools/schema": {
+      "version": "9.0.17",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.17.tgz",
+      "integrity": "sha512-HVLq0ecbkuXhJlpZ50IHP5nlISqH2GbNgjBJhhRzHeXhfwlUOT4ISXGquWTmuq61K0xSaO0aCjMpxe4QYbKTng==",
+      "optional": true,
+      "dependencies": {
+        "@graphql-tools/merge": "8.4.0",
+        "@graphql-tools/utils": "9.2.1",
+        "tslib": "^2.4.0",
+        "value-or-promise": "1.0.12"
+      },
+      "peerDependencies": {
+        "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+      }
+    },
+    "node_modules/@graphql-tools/utils": {
+      "version": "9.2.1",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz",
+      "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==",
+      "optional": true,
+      "dependencies": {
+        "@graphql-typed-document-node/core": "^3.1.1",
+        "tslib": "^2.4.0"
+      },
+      "peerDependencies": {
+        "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+      }
+    },
+    "node_modules/@graphql-typed-document-node/core": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
+      "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==",
+      "optional": true,
+      "peerDependencies": {
+        "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+      }
+    },
+    "node_modules/@hapi/hoek": {
+      "version": "9.3.0",
+      "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
+      "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="
+    },
+    "node_modules/@hapi/topo": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
+      "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
+      "dependencies": {
+        "@hapi/hoek": "^9.0.0"
+      }
+    },
+    "node_modules/@humanwhocodes/config-array": {
+      "version": "0.11.8",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+      "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+      "dev": true,
+      "dependencies": {
+        "@humanwhocodes/object-schema": "^1.2.1",
+        "debug": "^4.1.1",
+        "minimatch": "^3.0.5"
+      },
+      "engines": {
+        "node": ">=10.10.0"
+      }
+    },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/object-schema": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+      "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+      "dev": true
+    },
+    "node_modules/@istanbuljs/load-nyc-config": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+      "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+      "dev": true,
+      "dependencies": {
+        "camelcase": "^5.3.1",
+        "find-up": "^4.1.0",
+        "get-package-type": "^0.1.0",
+        "js-yaml": "^3.13.1",
+        "resolve-from": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dev": true,
+      "dependencies": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "^5.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+      "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+      "dev": true,
+      "dependencies": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "dev": true,
+      "dependencies": {
+        "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+      "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@istanbuljs/schema": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jest/console": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz",
+      "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "jest-message-util": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/core": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz",
+      "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/console": "^29.5.0",
+        "@jest/reporters": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "ansi-escapes": "^4.2.1",
+        "chalk": "^4.0.0",
+        "ci-info": "^3.2.0",
+        "exit": "^0.1.2",
+        "graceful-fs": "^4.2.9",
+        "jest-changed-files": "^29.5.0",
+        "jest-config": "^29.5.0",
+        "jest-haste-map": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-regex-util": "^29.4.3",
+        "jest-resolve": "^29.5.0",
+        "jest-resolve-dependencies": "^29.5.0",
+        "jest-runner": "^29.5.0",
+        "jest-runtime": "^29.5.0",
+        "jest-snapshot": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-validate": "^29.5.0",
+        "jest-watcher": "^29.5.0",
+        "micromatch": "^4.0.4",
+        "pretty-format": "^29.5.0",
+        "slash": "^3.0.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@jest/environment": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz",
+      "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/fake-timers": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "jest-mock": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/expect": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz",
+      "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==",
+      "dev": true,
+      "dependencies": {
+        "expect": "^29.5.0",
+        "jest-snapshot": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/expect-utils": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz",
+      "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==",
+      "dev": true,
+      "dependencies": {
+        "jest-get-type": "^29.4.3"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/fake-timers": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz",
+      "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "@sinonjs/fake-timers": "^10.0.2",
+        "@types/node": "*",
+        "jest-message-util": "^29.5.0",
+        "jest-mock": "^29.5.0",
+        "jest-util": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/globals": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz",
+      "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/environment": "^29.5.0",
+        "@jest/expect": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "jest-mock": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/reporters": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz",
+      "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==",
+      "dev": true,
+      "dependencies": {
+        "@bcoe/v8-coverage": "^0.2.3",
+        "@jest/console": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@jridgewell/trace-mapping": "^0.3.15",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "collect-v8-coverage": "^1.0.0",
+        "exit": "^0.1.2",
+        "glob": "^7.1.3",
+        "graceful-fs": "^4.2.9",
+        "istanbul-lib-coverage": "^3.0.0",
+        "istanbul-lib-instrument": "^5.1.0",
+        "istanbul-lib-report": "^3.0.0",
+        "istanbul-lib-source-maps": "^4.0.0",
+        "istanbul-reports": "^3.1.3",
+        "jest-message-util": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-worker": "^29.5.0",
+        "slash": "^3.0.0",
+        "string-length": "^4.0.1",
+        "strip-ansi": "^6.0.0",
+        "v8-to-istanbul": "^9.0.1"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@jest/schemas": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz",
+      "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==",
+      "dev": true,
+      "dependencies": {
+        "@sinclair/typebox": "^0.25.16"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/source-map": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz",
+      "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.15",
+        "callsites": "^3.0.0",
+        "graceful-fs": "^4.2.9"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/test-result": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz",
+      "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/console": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/istanbul-lib-coverage": "^2.0.0",
+        "collect-v8-coverage": "^1.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/test-sequencer": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz",
+      "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/test-result": "^29.5.0",
+        "graceful-fs": "^4.2.9",
+        "jest-haste-map": "^29.5.0",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/transform": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz",
+      "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.11.6",
+        "@jest/types": "^29.5.0",
+        "@jridgewell/trace-mapping": "^0.3.15",
+        "babel-plugin-istanbul": "^6.1.1",
+        "chalk": "^4.0.0",
+        "convert-source-map": "^2.0.0",
+        "fast-json-stable-stringify": "^2.1.0",
+        "graceful-fs": "^4.2.9",
+        "jest-haste-map": "^29.5.0",
+        "jest-regex-util": "^29.4.3",
+        "jest-util": "^29.5.0",
+        "micromatch": "^4.0.4",
+        "pirates": "^4.0.4",
+        "slash": "^3.0.0",
+        "write-file-atomic": "^4.0.2"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/types": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz",
+      "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==",
+      "dev": true,
+      "dependencies": {
+        "@jest/schemas": "^29.4.3",
+        "@types/istanbul-lib-coverage": "^2.0.0",
+        "@types/istanbul-reports": "^3.0.0",
+        "@types/node": "*",
+        "@types/yargs": "^17.0.8",
+        "chalk": "^4.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
+      "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/set-array": "^1.0.0",
+        "@jridgewell/sourcemap-codec": "^1.4.10"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+      "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/set-array": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/source-map": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
+      "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.0",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      }
+    },
+    "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+      "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/set-array": "^1.0.1",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.4.14",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+      "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+      "dev": true
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.17",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
+      "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/resolve-uri": "3.1.0",
+        "@jridgewell/sourcemap-codec": "1.4.14"
+      }
+    },
+    "node_modules/@jsdevtools/ono": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
+      "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
+      "dev": true
+    },
+    "node_modules/@lukeed/csprng": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.0.1.tgz",
+      "integrity": "sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@mapbox/node-pre-gyp": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz",
+      "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==",
+      "dependencies": {
+        "detect-libc": "^2.0.0",
+        "https-proxy-agent": "^5.0.0",
+        "make-dir": "^3.1.0",
+        "node-fetch": "^2.6.7",
+        "nopt": "^5.0.0",
+        "npmlog": "^5.0.1",
+        "rimraf": "^3.0.2",
+        "semver": "^7.3.5",
+        "tar": "^6.1.11"
+      },
+      "bin": {
+        "node-pre-gyp": "bin/node-pre-gyp"
+      }
+    },
+    "node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@nestjs/cli": {
+      "version": "9.3.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.3.0.tgz",
+      "integrity": "sha512-v/E8Y3zFk30+FljETvPgpoGIUiOfWuOe6WUFw3ExGfDeWrF/A8ceupDHPWNknBAqvNtz2kVrWu5mwsZUEKGIgg==",
+      "dev": true,
+      "dependencies": {
+        "@angular-devkit/core": "15.2.4",
+        "@angular-devkit/schematics": "15.2.4",
+        "@angular-devkit/schematics-cli": "15.2.4",
+        "@nestjs/schematics": "^9.0.4",
+        "chalk": "4.1.2",
+        "chokidar": "3.5.3",
+        "cli-table3": "0.6.3",
+        "commander": "4.1.1",
+        "fork-ts-checker-webpack-plugin": "8.0.0",
+        "inquirer": "8.2.5",
+        "node-emoji": "1.11.0",
+        "ora": "5.4.1",
+        "os-name": "4.0.1",
+        "rimraf": "4.4.0",
+        "shelljs": "0.8.5",
+        "source-map-support": "0.5.21",
+        "tree-kill": "1.2.2",
+        "tsconfig-paths": "4.1.2",
+        "tsconfig-paths-webpack-plugin": "4.0.1",
+        "typescript": "4.9.5",
+        "webpack": "5.76.2",
+        "webpack-node-externals": "3.0.0"
+      },
+      "bin": {
+        "nest": "bin/nest.js"
+      },
+      "engines": {
+        "node": ">= 12.9.0"
+      }
+    },
+    "node_modules/@nestjs/cli/node_modules/strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@nestjs/cli/node_modules/tsconfig-paths": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz",
+      "integrity": "sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw==",
+      "dev": true,
+      "dependencies": {
+        "json5": "^2.2.2",
+        "minimist": "^1.2.6",
+        "strip-bom": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/@nestjs/common": {
+      "version": "9.3.10",
+      "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.3.10.tgz",
+      "integrity": "sha512-wj2bM9TXBlAvzgznkID0s7bN/niVn90sZIDtRFDnvaB1qagEpkWA0Bt39qilIuqdReluIaCjeEW106U0oyz+mQ==",
+      "dependencies": {
+        "iterare": "1.2.1",
+        "tslib": "2.5.0",
+        "uid": "2.0.1"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/nest"
+      },
+      "peerDependencies": {
+        "cache-manager": "<=5",
+        "class-transformer": "*",
+        "class-validator": "*",
+        "reflect-metadata": "^0.1.12",
+        "rxjs": "^7.1.0"
+      },
+      "peerDependenciesMeta": {
+        "cache-manager": {
+          "optional": true
+        },
+        "class-transformer": {
+          "optional": true
+        },
+        "class-validator": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/config": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-2.3.1.tgz",
+      "integrity": "sha512-Ckzel0NZ9CWhNsLfE1hxfDuxJuEbhQvGxSlmZ1/X8awjRmAA/g3kT6M1+MO1SHj1wMtPyUfd9WpwkiqFbiwQgA==",
+      "dependencies": {
+        "dotenv": "16.0.3",
+        "dotenv-expand": "10.0.0",
+        "lodash": "4.17.21",
+        "uuid": "9.0.0"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0",
+        "reflect-metadata": "^0.1.13",
+        "rxjs": "^6.0.0 || ^7.2.0"
+      }
+    },
+    "node_modules/@nestjs/config/node_modules/dotenv": {
+      "version": "16.0.3",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
+      "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@nestjs/config/node_modules/uuid": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
+      "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/@nestjs/core": {
+      "version": "9.4.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.4.0.tgz",
+      "integrity": "sha512-yTLryCgFD0462wPe4HIzhyTcDgibt8Stfwb5YzcX7Ma0NM4m8uBIpcPG109KBubp8ZmV85e5mw4rl20qLQQVsQ==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "@nuxtjs/opencollective": "0.3.2",
+        "fast-safe-stringify": "2.1.1",
+        "iterare": "1.2.1",
+        "path-to-regexp": "3.2.0",
+        "tslib": "2.5.0",
+        "uid": "2.0.2"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/nest"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^9.0.0",
+        "@nestjs/microservices": "^9.0.0",
+        "@nestjs/platform-express": "^9.0.0",
+        "@nestjs/websockets": "^9.0.0",
+        "reflect-metadata": "^0.1.12",
+        "rxjs": "^7.1.0"
+      },
+      "peerDependenciesMeta": {
+        "@nestjs/microservices": {
+          "optional": true
+        },
+        "@nestjs/platform-express": {
+          "optional": true
+        },
+        "@nestjs/websockets": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/core/node_modules/uid": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz",
+      "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==",
+      "dependencies": {
+        "@lukeed/csprng": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@nestjs/graphql": {
+      "version": "11.0.4",
+      "resolved": "https://registry.npmjs.org/@nestjs/graphql/-/graphql-11.0.4.tgz",
+      "integrity": "sha512-D4KqFOfow18R9KrxgQBPntsKGsbZi5XQQnjwngbHXKrarRWv79yjUyHdMwZ7qnoryVx/REbFmdglF6ZpdnjiNg==",
+      "optional": true,
+      "dependencies": {
+        "@graphql-tools/merge": "8.4.0",
+        "@graphql-tools/schema": "9.0.17",
+        "@graphql-tools/utils": "9.2.1",
+        "@nestjs/mapped-types": "1.2.2",
+        "chokidar": "3.5.3",
+        "fast-glob": "3.2.12",
+        "graphql-tag": "2.12.6",
+        "graphql-ws": "5.12.0",
+        "lodash": "4.17.21",
+        "normalize-path": "3.0.0",
+        "subscriptions-transport-ws": "0.11.0",
+        "tslib": "2.5.0",
+        "uuid": "9.0.0",
+        "ws": "8.13.0"
+      },
+      "peerDependencies": {
+        "@apollo/subgraph": "^2.0.0",
+        "@nestjs/common": "^9.3.8",
+        "@nestjs/core": "^9.3.8",
+        "class-transformer": "*",
+        "class-validator": "*",
+        "graphql": "^16.6.0",
+        "reflect-metadata": "^0.1.13",
+        "ts-morph": "^15.0.0 || ^16.0.0 || ^17.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@apollo/subgraph": {
+          "optional": true
+        },
+        "class-transformer": {
+          "optional": true
+        },
+        "class-validator": {
+          "optional": true
+        },
+        "ts-morph": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/graphql/node_modules/uuid": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
+      "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
+      "optional": true,
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/@nestjs/graphql/node_modules/ws": {
+      "version": "8.13.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
+      "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
+      "optional": true,
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/jwt": {
+      "version": "10.0.2",
+      "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.0.2.tgz",
+      "integrity": "sha512-MLxjCSbO7C9fN2hst5kpIhnJAgglJmrKppXAXqElB8A9ip3ZuCowMDjjmNWWJyfOzE98NV0E0iEQGE2StMUC+Q==",
+      "dependencies": {
+        "@types/jsonwebtoken": "9.0.1",
+        "jsonwebtoken": "9.0.0"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^8.0.0 || ^9.0.0"
+      }
+    },
+    "node_modules/@nestjs/mapped-types": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.2.2.tgz",
+      "integrity": "sha512-3dHxLXs3M0GPiriAcCFFJQHoDFUuzTD5w6JDhE7TyfT89YKpe6tcCCIqOZWdXmt9AZjjK30RkHRSFF+QEnWFQg==",
+      "peerDependencies": {
+        "@nestjs/common": "^7.0.8 || ^8.0.0 || ^9.0.0",
+        "class-transformer": "^0.2.0 || ^0.3.0 || ^0.4.0 || ^0.5.0",
+        "class-validator": "^0.11.1 || ^0.12.0 || ^0.13.0 || ^0.14.0",
+        "reflect-metadata": "^0.1.12"
+      },
+      "peerDependenciesMeta": {
+        "class-transformer": {
+          "optional": true
+        },
+        "class-validator": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/passport": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-9.0.3.tgz",
+      "integrity": "sha512-HplSJaimEAz1IOZEu+pdJHHJhQyBOPAYWXYHfAPQvRqWtw4FJF1VXl1Qtk9dcXQX1eKytDtH+qBzNQc19GWNEg==",
+      "peerDependencies": {
+        "@nestjs/common": "^8.0.0 || ^9.0.0",
+        "passport": "^0.4.0 || ^0.5.0 || ^0.6.0"
+      }
+    },
+    "node_modules/@nestjs/platform-express": {
+      "version": "9.4.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.4.0.tgz",
+      "integrity": "sha512-PpnfghpNq7mwG43z3+pacHulsabUCBMla4nUikntXT525ORpZSDvh/nLi1HLfE4w5+FcINc8/RBOyYTeRVmiRQ==",
+      "dependencies": {
+        "body-parser": "1.20.2",
+        "cors": "2.8.5",
+        "express": "4.18.2",
+        "multer": "1.4.4-lts.1",
+        "tslib": "2.5.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/nest"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^9.0.0",
+        "@nestjs/core": "^9.0.0"
+      }
+    },
+    "node_modules/@nestjs/platform-express/node_modules/busboy": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+      "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+      "dependencies": {
+        "streamsearch": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=10.16.0"
+      }
+    },
+    "node_modules/@nestjs/platform-express/node_modules/multer": {
+      "version": "1.4.4-lts.1",
+      "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4-lts.1.tgz",
+      "integrity": "sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==",
+      "dependencies": {
+        "append-field": "^1.0.0",
+        "busboy": "^1.0.0",
+        "concat-stream": "^1.5.2",
+        "mkdirp": "^0.5.4",
+        "object-assign": "^4.1.1",
+        "type-is": "^1.6.4",
+        "xtend": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 6.0.0"
+      }
+    },
+    "node_modules/@nestjs/platform-express/node_modules/streamsearch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+      "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/@nestjs/schematics": {
+      "version": "9.0.4",
+      "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.4.tgz",
+      "integrity": "sha512-egurCfAc4e5i1r2TmeAF0UrOKejFmT5oTdv4b7HcOVPupc3QGU7CbEfGleL3mkM5AjrixTQeMxU9bJ00ttAbGg==",
+      "dev": true,
+      "dependencies": {
+        "@angular-devkit/core": "15.0.4",
+        "@angular-devkit/schematics": "15.0.4",
+        "fs-extra": "11.1.0",
+        "jsonc-parser": "3.2.0",
+        "pluralize": "8.0.0"
+      },
+      "peerDependencies": {
+        "typescript": "^4.3.5"
+      }
+    },
+    "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": {
+      "version": "15.0.4",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.0.4.tgz",
+      "integrity": "sha512-4ITpRAevd652SxB+qNesIQ9qfbm7wT5UBU5kJOPPwGL77I21g8CQpkmV1n5VSacPvC9Zbz90feOWexf7w7JzcA==",
+      "dev": true,
+      "dependencies": {
+        "ajv": "8.11.0",
+        "ajv-formats": "2.1.1",
+        "jsonc-parser": "3.2.0",
+        "rxjs": "6.6.7",
+        "source-map": "0.7.4"
+      },
+      "engines": {
+        "node": "^14.20.0 || ^16.13.0 || >=18.10.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      },
+      "peerDependencies": {
+        "chokidar": "^3.5.2"
+      },
+      "peerDependenciesMeta": {
+        "chokidar": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": {
+      "version": "15.0.4",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.0.4.tgz",
+      "integrity": "sha512-/gXiLFS0+xFdx6wPoBpe/c6/K9I5edMpaASqPf4XheKtrsSvL+qTlIi3nsbfItzOiDXbaBmlbxGfkMHz/yg0Ig==",
+      "dev": true,
+      "dependencies": {
+        "@angular-devkit/core": "15.0.4",
+        "jsonc-parser": "3.2.0",
+        "magic-string": "0.26.7",
+        "ora": "5.4.1",
+        "rxjs": "6.6.7"
+      },
+      "engines": {
+        "node": "^14.20.0 || ^16.13.0 || >=18.10.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@nestjs/schematics/node_modules/ajv": {
+      "version": "8.11.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+      "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/@nestjs/schematics/node_modules/magic-string": {
+      "version": "0.26.7",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz",
+      "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==",
+      "dev": true,
+      "dependencies": {
+        "sourcemap-codec": "^1.4.8"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@nestjs/schematics/node_modules/rxjs": {
+      "version": "6.6.7",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+      "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "^1.9.0"
+      },
+      "engines": {
+        "npm": ">=2.0.0"
+      }
+    },
+    "node_modules/@nestjs/schematics/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+      "dev": true
+    },
+    "node_modules/@nestjs/swagger": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-6.3.0.tgz",
+      "integrity": "sha512-Gnig189oa1tD+h0BYIfUwhp/wvvmTn6iO3csR2E4rQrDTgCxSxZDlNdfZo3AC+Rmf8u0KX4ZAX1RZN1qXTtC7A==",
+      "dependencies": {
+        "@nestjs/mapped-types": "1.2.2",
+        "js-yaml": "4.1.0",
+        "lodash": "4.17.21",
+        "path-to-regexp": "3.2.0",
+        "swagger-ui-dist": "4.18.2"
+      },
+      "peerDependencies": {
+        "@fastify/static": "^6.0.0",
+        "@nestjs/common": "^9.0.0",
+        "@nestjs/core": "^9.0.0",
+        "class-transformer": "*",
+        "class-validator": "*",
+        "reflect-metadata": "^0.1.12"
+      },
+      "peerDependenciesMeta": {
+        "@fastify/static": {
+          "optional": true
+        },
+        "class-transformer": {
+          "optional": true
+        },
+        "class-validator": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/testing": {
+      "version": "9.4.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.4.0.tgz",
+      "integrity": "sha512-xZWp363P4otcebg++gSjUcdCfTK0RorORzyFq3aLaSAQOlq8kxfFDRIKzEATR4aOUfqTMMsAA8lhnMJWf35N6A==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "2.5.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/nest"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^9.0.0",
+        "@nestjs/core": "^9.0.0",
+        "@nestjs/microservices": "^9.0.0",
+        "@nestjs/platform-express": "^9.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@nestjs/microservices": {
+          "optional": true
+        },
+        "@nestjs/platform-express": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "devOptional": true,
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "devOptional": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "devOptional": true,
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nuxtjs/opencollective": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz",
+      "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "consola": "^2.15.0",
+        "node-fetch": "^2.6.1"
+      },
+      "bin": {
+        "opencollective": "bin/opencollective.js"
+      },
+      "engines": {
+        "node": ">=8.0.0",
+        "npm": ">=5.0.0"
+      }
+    },
+    "node_modules/@opencensus/core": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.9.tgz",
+      "integrity": "sha512-31Q4VWtbzXpVUd2m9JS6HEaPjlKvNMOiF7lWKNmXF84yUcgfAFL5re7/hjDmdyQbOp32oGc+RFV78jXIldVz6Q==",
+      "dependencies": {
+        "continuation-local-storage": "^3.2.1",
+        "log-driver": "^1.2.7",
+        "semver": "^5.5.0",
+        "shimmer": "^1.2.0",
+        "uuid": "^3.2.1"
+      },
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/@opencensus/core/node_modules/semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/@opencensus/core/node_modules/uuid": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+      "deprecated": "Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.",
+      "bin": {
+        "uuid": "bin/uuid"
+      }
+    },
+    "node_modules/@opencensus/propagation-b3": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz",
+      "integrity": "sha512-PffXX2AL8Sh0VHQ52jJC4u3T0H6wDK6N/4bg7xh4ngMYOIi13aR1kzVvX1sVDBgfGwDOkMbl4c54Xm3tlPx/+A==",
+      "dependencies": {
+        "@opencensus/core": "^0.0.8",
+        "uuid": "^3.2.1"
+      },
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/@opencensus/propagation-b3/node_modules/@opencensus/core": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.8.tgz",
+      "integrity": "sha512-yUFT59SFhGMYQgX0PhoTR0LBff2BEhPrD9io1jWfF/VDbakRfs6Pq60rjv0Z7iaTav5gQlttJCX2+VPxFWCuoQ==",
+      "dependencies": {
+        "continuation-local-storage": "^3.2.1",
+        "log-driver": "^1.2.7",
+        "semver": "^5.5.0",
+        "shimmer": "^1.2.0",
+        "uuid": "^3.2.1"
+      },
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/@opencensus/propagation-b3/node_modules/semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/@opencensus/propagation-b3/node_modules/uuid": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+      "deprecated": "Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.",
+      "bin": {
+        "uuid": "bin/uuid"
+      }
+    },
+    "node_modules/@pm2/agent": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.1.tgz",
+      "integrity": "sha512-QKHMm6yexcvdDfcNE7PL9D6uEjoQPGRi+8dh+rc4Hwtbpsbh5IAvZbz3BVGjcd4HaX6pt2xGpOohG7/Y2L4QLw==",
+      "dependencies": {
+        "async": "~3.2.0",
+        "chalk": "~3.0.0",
+        "dayjs": "~1.8.24",
+        "debug": "~4.3.1",
+        "eventemitter2": "~5.0.1",
+        "fast-json-patch": "^3.0.0-1",
+        "fclone": "~1.0.11",
+        "nssocket": "0.6.0",
+        "pm2-axon": "~4.0.1",
+        "pm2-axon-rpc": "~0.7.0",
+        "proxy-agent": "~5.0.0",
+        "semver": "~7.2.0",
+        "ws": "~7.4.0"
+      }
+    },
+    "node_modules/@pm2/agent/node_modules/chalk": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+      "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@pm2/agent/node_modules/dayjs": {
+      "version": "1.8.36",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz",
+      "integrity": "sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw=="
+    },
+    "node_modules/@pm2/agent/node_modules/semver": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.2.3.tgz",
+      "integrity": "sha512-utbW9Z7ZxVvwiIWkdOMLOR9G/NFXh2aRucghkVrEMJWuC++r3lCkBC3LwqBinyHzGMAJxY5tn6VakZGHObq5ig==",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@pm2/io": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/@pm2/io/-/io-5.0.0.tgz",
+      "integrity": "sha512-3rToDVJaRoob5Lq8+7Q2TZFruoEkdORxwzFpZaqF4bmH6Bkd7kAbdPrI/z8X6k1Meq5rTtScM7MmDgppH6aLlw==",
+      "dependencies": {
+        "@opencensus/core": "0.0.9",
+        "@opencensus/propagation-b3": "0.0.8",
+        "async": "~2.6.1",
+        "debug": "~4.3.1",
+        "eventemitter2": "^6.3.1",
+        "require-in-the-middle": "^5.0.0",
+        "semver": "6.3.0",
+        "shimmer": "^1.2.0",
+        "signal-exit": "^3.0.3",
+        "tslib": "1.9.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/@pm2/io/node_modules/async": {
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+      "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+      "dependencies": {
+        "lodash": "^4.17.14"
+      }
+    },
+    "node_modules/@pm2/io/node_modules/eventemitter2": {
+      "version": "6.4.9",
+      "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz",
+      "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg=="
+    },
+    "node_modules/@pm2/io/node_modules/semver": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@pm2/io/node_modules/tslib": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+      "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
+    },
+    "node_modules/@pm2/js-api": {
+      "version": "0.6.7",
+      "resolved": "https://registry.npmjs.org/@pm2/js-api/-/js-api-0.6.7.tgz",
+      "integrity": "sha512-jiJUhbdsK+5C4zhPZNnyA3wRI01dEc6a2GhcQ9qI38DyIk+S+C8iC3fGjcjUbt/viLYKPjlAaE+hcT2/JMQPXw==",
+      "dependencies": {
+        "async": "^2.6.3",
+        "axios": "^0.21.0",
+        "debug": "~4.3.1",
+        "eventemitter2": "^6.3.1",
+        "ws": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/@pm2/js-api/node_modules/async": {
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+      "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+      "dependencies": {
+        "lodash": "^4.17.14"
+      }
+    },
+    "node_modules/@pm2/js-api/node_modules/eventemitter2": {
+      "version": "6.4.9",
+      "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz",
+      "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg=="
+    },
+    "node_modules/@pm2/pm2-version-check": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@pm2/pm2-version-check/-/pm2-version-check-1.0.4.tgz",
+      "integrity": "sha512-SXsM27SGH3yTWKc2fKR4SYNxsmnvuBQ9dd6QHtEWmiZ/VqaOYPAIlS8+vMcn27YLtAEBGvNRSh3TPNvtjZgfqA==",
+      "dependencies": {
+        "debug": "^4.3.1"
+      }
+    },
+    "node_modules/@prisma/client": {
+      "version": "4.13.0",
+      "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.13.0.tgz",
+      "integrity": "sha512-YaiiICcRB2hatxsbnfB66uWXjcRw3jsZdlAVxmx0cFcTc/Ad/sKdHCcWSnqyDX47vAewkjRFwiLwrOUjswVvmA==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "@prisma/engines-version": "4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a"
+      },
+      "engines": {
+        "node": ">=14.17"
+      },
+      "peerDependencies": {
+        "prisma": "*"
+      },
+      "peerDependenciesMeta": {
+        "prisma": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@prisma/engines": {
+      "version": "4.12.0",
+      "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.12.0.tgz",
+      "integrity": "sha512-0alKtnxhNB5hYU+ymESBlGI4b9XrGGSdv7Ud+8TE/fBNOEhIud0XQsAR+TrvUZgS4na5czubiMsODw0TUrgkIA==",
+      "hasInstallScript": true
+    },
+    "node_modules/@prisma/engines-version": {
+      "version": "4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a",
+      "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a.tgz",
+      "integrity": "sha512-fsQlbkhPJf08JOzKoyoD9atdUijuGBekwoOPZC3YOygXEml1MTtgXVpnUNchQlRSY82OQ6pSGQ9PxUe4arcSLQ=="
+    },
+    "node_modules/@redis/bloom": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
+      "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
+      "peerDependencies": {
+        "@redis/client": "^1.0.0"
+      }
+    },
+    "node_modules/@redis/client": {
+      "version": "1.5.6",
+      "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.6.tgz",
+      "integrity": "sha512-dFD1S6je+A47Lj22jN/upVU2fj4huR7S9APd7/ziUXsIXDL+11GPYti4Suv5y8FuXaN+0ZG4JF+y1houEJ7ToA==",
+      "dependencies": {
+        "cluster-key-slot": "1.1.2",
+        "generic-pool": "3.9.0",
+        "yallist": "4.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@redis/graph": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz",
+      "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==",
+      "peerDependencies": {
+        "@redis/client": "^1.0.0"
+      }
+    },
+    "node_modules/@redis/json": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz",
+      "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==",
+      "peerDependencies": {
+        "@redis/client": "^1.0.0"
+      }
+    },
+    "node_modules/@redis/search": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.2.tgz",
+      "integrity": "sha512-/cMfstG/fOh/SsE+4/BQGeuH/JJloeWuH+qJzM8dbxuWvdWibWAOAHHCZTMPhV3xIlH4/cUEIA8OV5QnYpaVoA==",
+      "peerDependencies": {
+        "@redis/client": "^1.0.0"
+      }
+    },
+    "node_modules/@redis/time-series": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz",
+      "integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==",
+      "peerDependencies": {
+        "@redis/client": "^1.0.0"
+      }
+    },
+    "node_modules/@sentry-internal/tracing": {
+      "version": "7.47.0",
+      "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.47.0.tgz",
+      "integrity": "sha512-udpHnCzF8DQsWf0gQwd0XFGp6Y8MOiwnl8vGt2ohqZGS3m1+IxoRLXsSkD8qmvN6KKDnwbaAvYnK0z0L+AW95g==",
+      "dependencies": {
+        "@sentry/core": "7.47.0",
+        "@sentry/types": "7.47.0",
+        "@sentry/utils": "7.47.0",
+        "tslib": "^1.9.3"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@sentry-internal/tracing/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/@sentry/core": {
+      "version": "7.47.0",
+      "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.47.0.tgz",
+      "integrity": "sha512-EFhZhKdMu7wKmWYZwbgTi8FNZ7Fq+HdlXiZWNz51Bqe3pHmfAkdHtAEs0Buo0v623MKA0CA4EjXIazGUM34XTg==",
+      "dependencies": {
+        "@sentry/types": "7.47.0",
+        "@sentry/utils": "7.47.0",
+        "tslib": "^1.9.3"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@sentry/core/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/@sentry/node": {
+      "version": "7.47.0",
+      "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.47.0.tgz",
+      "integrity": "sha512-LTg2r5EV9yh4GLYDF+ViSltR9LLj/pcvk8YhANJcMO3Fp//xh8njcdU0FC2yNthUREawYDzAsVzLyCYJfV0H1A==",
+      "dependencies": {
+        "@sentry-internal/tracing": "7.47.0",
+        "@sentry/core": "7.47.0",
+        "@sentry/types": "7.47.0",
+        "@sentry/utils": "7.47.0",
+        "cookie": "^0.4.1",
+        "https-proxy-agent": "^5.0.0",
+        "lru_map": "^0.3.3",
+        "tslib": "^1.9.3"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/cookie": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+      "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/@sentry/tracing": {
+      "version": "7.47.0",
+      "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.47.0.tgz",
+      "integrity": "sha512-hJCpKdekwaFNbCVXxfCz5IxfSEJIKnkPmRSVHITOm5VhKwq2e5kmy4Rn6bzSETwJFSDE8LGbR/3eSfGTqw37XA==",
+      "dependencies": {
+        "@sentry-internal/tracing": "7.47.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@sentry/types": {
+      "version": "7.47.0",
+      "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.47.0.tgz",
+      "integrity": "sha512-GxXocplN0j1+uczovHrfkykl9wvkamDtWxlPUQgyGlbLGZn+UH1Y79D4D58COaFWGEZdSNKr62gZAjfEYu9nQA==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@sentry/utils": {
+      "version": "7.47.0",
+      "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.47.0.tgz",
+      "integrity": "sha512-A89SaOLp6XeZfByeYo2C8Ecye/YAtk/gENuyOUhQEdMulI6mZdjqtHAp7pTMVgkBc/YNARVuoa+kR/IdRrTPkQ==",
+      "dependencies": {
+        "@sentry/types": "7.47.0",
+        "tslib": "^1.9.3"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@sentry/utils/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/@sideway/address": {
+      "version": "4.1.4",
+      "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
+      "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==",
+      "dependencies": {
+        "@hapi/hoek": "^9.0.0"
+      }
+    },
+    "node_modules/@sideway/formula": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
+      "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="
+    },
+    "node_modules/@sideway/pinpoint": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
+      "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
+    },
+    "node_modules/@sinclair/typebox": {
+      "version": "0.25.24",
+      "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz",
+      "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==",
+      "dev": true
+    },
+    "node_modules/@sinonjs/commons": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+      "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+      "dev": true,
+      "dependencies": {
+        "type-detect": "4.0.8"
+      }
+    },
+    "node_modules/@sinonjs/fake-timers": {
+      "version": "10.0.2",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz",
+      "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==",
+      "dev": true,
+      "dependencies": {
+        "@sinonjs/commons": "^2.0.0"
+      }
+    },
+    "node_modules/@tootallnate/once": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+      "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/@tsconfig/node10": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+      "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+      "dev": true
+    },
+    "node_modules/@tsconfig/node12": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+      "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+      "dev": true
+    },
+    "node_modules/@tsconfig/node14": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+      "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+      "dev": true
+    },
+    "node_modules/@tsconfig/node16": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
+      "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+      "dev": true
+    },
+    "node_modules/@types/aws-sdk": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/@types/aws-sdk/-/aws-sdk-2.7.0.tgz",
+      "integrity": "sha512-bF6brnwPN9+kheqdKCpinMgCkj+sJIUEj+0v0LPug9OQwL5/1jy+kiJwl+Nkw4Kh+7oaL1phhC4gMz6Oq60jMg==",
+      "deprecated": "This is a stub types definition for aws-sdk (https://github.com/aws/aws-sdk-js). aws-sdk provides its own type definitions, so you don't need @types/aws-sdk installed!",
+      "dependencies": {
+        "aws-sdk": "*"
+      }
+    },
+    "node_modules/@types/babel__core": {
+      "version": "7.20.0",
+      "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz",
+      "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/parser": "^7.20.7",
+        "@babel/types": "^7.20.7",
+        "@types/babel__generator": "*",
+        "@types/babel__template": "*",
+        "@types/babel__traverse": "*"
+      }
+    },
+    "node_modules/@types/babel__generator": {
+      "version": "7.6.4",
+      "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz",
+      "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "node_modules/@types/babel__template": {
+      "version": "7.4.1",
+      "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
+      "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==",
+      "dev": true,
+      "dependencies": {
+        "@babel/parser": "^7.1.0",
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "node_modules/@types/babel__traverse": {
+      "version": "7.18.3",
+      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz",
+      "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.3.0"
+      }
+    },
+    "node_modules/@types/body-parser": {
+      "version": "1.19.2",
+      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+      "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+      "dev": true,
+      "dependencies": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/connect": {
+      "version": "3.4.35",
+      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+      "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/cookiejar": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz",
+      "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==",
+      "dev": true
+    },
+    "node_modules/@types/eslint": {
+      "version": "8.21.3",
+      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.3.tgz",
+      "integrity": "sha512-fa7GkppZVEByMWGbTtE5MbmXWJTVbrjjaS8K6uQj+XtuuUv1fsuPAxhygfqLmsb/Ufb3CV8deFCpiMfAgi00Sw==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "*",
+        "@types/json-schema": "*"
+      }
+    },
+    "node_modules/@types/eslint-scope": {
+      "version": "3.7.4",
+      "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
+      "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
+      "dev": true,
+      "dependencies": {
+        "@types/eslint": "*",
+        "@types/estree": "*"
+      }
+    },
+    "node_modules/@types/estree": {
+      "version": "0.0.51",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
+      "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
+      "dev": true
+    },
+    "node_modules/@types/express": {
+      "version": "4.17.17",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
+      "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
+      "dev": true,
+      "dependencies": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^4.17.33",
+        "@types/qs": "*",
+        "@types/serve-static": "*"
+      }
+    },
+    "node_modules/@types/express-serve-static-core": {
+      "version": "4.17.33",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz",
+      "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*"
+      }
+    },
+    "node_modules/@types/graceful-fs": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
+      "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/istanbul-lib-coverage": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
+      "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
+      "dev": true
+    },
+    "node_modules/@types/istanbul-lib-report": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+      "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
+      "dev": true,
+      "dependencies": {
+        "@types/istanbul-lib-coverage": "*"
+      }
+    },
+    "node_modules/@types/istanbul-reports": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
+      "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
+      "dev": true,
+      "dependencies": {
+        "@types/istanbul-lib-report": "*"
+      }
+    },
+    "node_modules/@types/jest": {
+      "version": "29.2.4",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.4.tgz",
+      "integrity": "sha512-PipFB04k2qTRPePduVLTRiPzQfvMeLwUN3Z21hsAKaB/W9IIzgB2pizCL466ftJlcyZqnHoC9ZHpxLGl3fS86A==",
+      "dev": true,
+      "dependencies": {
+        "expect": "^29.0.0",
+        "pretty-format": "^29.0.0"
+      }
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.11",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+      "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
+      "dev": true
+    },
+    "node_modules/@types/jsonwebtoken": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz",
+      "integrity": "sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/mime": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
+      "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==",
+      "dev": true
+    },
+    "node_modules/@types/node": {
+      "version": "18.11.18",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
+      "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA=="
+    },
+    "node_modules/@types/parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
+      "dev": true
+    },
+    "node_modules/@types/passport": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.12.tgz",
+      "integrity": "sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==",
+      "dev": true,
+      "dependencies": {
+        "@types/express": "*"
+      }
+    },
+    "node_modules/@types/passport-jwt": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.8.tgz",
+      "integrity": "sha512-VKJZDJUAHFhPHHYvxdqFcc5vlDht8Q2pL1/ePvKAgqRThDaCc84lSYOTQmnx3+JIkDlN+2KfhFhXIzlcVT+Pcw==",
+      "dev": true,
+      "dependencies": {
+        "@types/express": "*",
+        "@types/jsonwebtoken": "*",
+        "@types/passport-strategy": "*"
+      }
+    },
+    "node_modules/@types/passport-local": {
+      "version": "1.0.35",
+      "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.35.tgz",
+      "integrity": "sha512-K4eLTJ8R0yYW8TvCqkjB0pTKoqfUSdl5PfZdidTjV2ETV3604fQxtY6BHKjQWAx50WUS0lqzBvKv3LoI1ZBPeA==",
+      "dev": true,
+      "dependencies": {
+        "@types/express": "*",
+        "@types/passport": "*",
+        "@types/passport-strategy": "*"
+      }
+    },
+    "node_modules/@types/passport-strategy": {
+      "version": "0.2.35",
+      "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",
+      "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==",
+      "dev": true,
+      "dependencies": {
+        "@types/express": "*",
+        "@types/passport": "*"
+      }
+    },
+    "node_modules/@types/prettier": {
+      "version": "2.7.2",
+      "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
+      "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==",
+      "dev": true
+    },
+    "node_modules/@types/qs": {
+      "version": "6.9.7",
+      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+      "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+      "dev": true
+    },
+    "node_modules/@types/range-parser": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+      "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+      "dev": true
+    },
+    "node_modules/@types/semver": {
+      "version": "7.3.13",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
+      "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
+      "dev": true
+    },
+    "node_modules/@types/serve-static": {
+      "version": "1.15.1",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz",
+      "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/mime": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/stack-utils": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
+      "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
+      "dev": true
+    },
+    "node_modules/@types/superagent": {
+      "version": "4.1.16",
+      "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz",
+      "integrity": "sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/cookiejar": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/supertest": {
+      "version": "2.0.12",
+      "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz",
+      "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/superagent": "*"
+      }
+    },
+    "node_modules/@types/triple-beam": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz",
+      "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g=="
+    },
+    "node_modules/@types/validator": {
+      "version": "13.7.14",
+      "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.14.tgz",
+      "integrity": "sha512-J6OAed6rhN6zyqL9Of6ZMamhlsOEU/poBVvbHr/dKOYKTeuYYMlDkMv+b6UUV0o2i0tw73cgyv/97WTWaUl0/g=="
+    },
+    "node_modules/@types/yargs": {
+      "version": "17.0.23",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.23.tgz",
+      "integrity": "sha512-yuogunc04OnzGQCrfHx+Kk883Q4X0aSwmYZhKjI21m+SVYzjIbrWl8dOOwSv5hf2Um2pdCOXWo9isteZTNXUZQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/yargs-parser": "*"
+      }
+    },
+    "node_modules/@types/yargs-parser": {
+      "version": "21.0.0",
+      "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
+      "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
+      "dev": true
+    },
+    "node_modules/@typescript-eslint/eslint-plugin": {
+      "version": "5.56.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.56.0.tgz",
+      "integrity": "sha512-ZNW37Ccl3oMZkzxrYDUX4o7cnuPgU+YrcaYXzsRtLB16I1FR5SHMqga3zGsaSliZADCWo2v8qHWqAYIj8nWCCg==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/regexpp": "^4.4.0",
+        "@typescript-eslint/scope-manager": "5.56.0",
+        "@typescript-eslint/type-utils": "5.56.0",
+        "@typescript-eslint/utils": "5.56.0",
+        "debug": "^4.3.4",
+        "grapheme-splitter": "^1.0.4",
+        "ignore": "^5.2.0",
+        "natural-compare-lite": "^1.4.0",
+        "semver": "^7.3.7",
+        "tsutils": "^3.21.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "@typescript-eslint/parser": "^5.0.0",
+        "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/parser": {
+      "version": "5.56.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.56.0.tgz",
+      "integrity": "sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/scope-manager": "5.56.0",
+        "@typescript-eslint/types": "5.56.0",
+        "@typescript-eslint/typescript-estree": "5.56.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/scope-manager": {
+      "version": "5.56.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.56.0.tgz",
+      "integrity": "sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "5.56.0",
+        "@typescript-eslint/visitor-keys": "5.56.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/type-utils": {
+      "version": "5.56.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.56.0.tgz",
+      "integrity": "sha512-8WxgOgJjWRy6m4xg9KoSHPzBNZeQbGlQOH7l2QEhQID/+YseaFxg5J/DLwWSsi9Axj4e/cCiKx7PVzOq38tY4A==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/typescript-estree": "5.56.0",
+        "@typescript-eslint/utils": "5.56.0",
+        "debug": "^4.3.4",
+        "tsutils": "^3.21.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/types": {
+      "version": "5.56.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.56.0.tgz",
+      "integrity": "sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree": {
+      "version": "5.56.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz",
+      "integrity": "sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "5.56.0",
+        "@typescript-eslint/visitor-keys": "5.56.0",
+        "debug": "^4.3.4",
+        "globby": "^11.1.0",
+        "is-glob": "^4.0.3",
+        "semver": "^7.3.7",
+        "tsutils": "^3.21.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/utils": {
+      "version": "5.56.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.56.0.tgz",
+      "integrity": "sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.2.0",
+        "@types/json-schema": "^7.0.9",
+        "@types/semver": "^7.3.12",
+        "@typescript-eslint/scope-manager": "5.56.0",
+        "@typescript-eslint/types": "5.56.0",
+        "@typescript-eslint/typescript-estree": "5.56.0",
+        "eslint-scope": "^5.1.1",
+        "semver": "^7.3.7"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys": {
+      "version": "5.56.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.56.0.tgz",
+      "integrity": "sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "5.56.0",
+        "eslint-visitor-keys": "^3.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@webassemblyjs/ast": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
+      "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
+      "dev": true,
+      "dependencies": {
+        "@webassemblyjs/helper-numbers": "1.11.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
+      }
+    },
+    "node_modules/@webassemblyjs/floating-point-hex-parser": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
+      "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==",
+      "dev": true
+    },
+    "node_modules/@webassemblyjs/helper-api-error": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
+      "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==",
+      "dev": true
+    },
+    "node_modules/@webassemblyjs/helper-buffer": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
+      "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==",
+      "dev": true
+    },
+    "node_modules/@webassemblyjs/helper-numbers": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
+      "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
+      "dev": true,
+      "dependencies": {
+        "@webassemblyjs/floating-point-hex-parser": "1.11.1",
+        "@webassemblyjs/helper-api-error": "1.11.1",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
+      "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==",
+      "dev": true
+    },
+    "node_modules/@webassemblyjs/helper-wasm-section": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
+      "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
+      "dev": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.11.1",
+        "@webassemblyjs/helper-buffer": "1.11.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+        "@webassemblyjs/wasm-gen": "1.11.1"
+      }
+    },
+    "node_modules/@webassemblyjs/ieee754": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
+      "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
+      "dev": true,
+      "dependencies": {
+        "@xtuc/ieee754": "^1.2.0"
+      }
+    },
+    "node_modules/@webassemblyjs/leb128": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
+      "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
+      "dev": true,
+      "dependencies": {
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@webassemblyjs/utf8": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
+      "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==",
+      "dev": true
+    },
+    "node_modules/@webassemblyjs/wasm-edit": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
+      "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
+      "dev": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.11.1",
+        "@webassemblyjs/helper-buffer": "1.11.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+        "@webassemblyjs/helper-wasm-section": "1.11.1",
+        "@webassemblyjs/wasm-gen": "1.11.1",
+        "@webassemblyjs/wasm-opt": "1.11.1",
+        "@webassemblyjs/wasm-parser": "1.11.1",
+        "@webassemblyjs/wast-printer": "1.11.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-gen": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
+      "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
+      "dev": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.11.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+        "@webassemblyjs/ieee754": "1.11.1",
+        "@webassemblyjs/leb128": "1.11.1",
+        "@webassemblyjs/utf8": "1.11.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-opt": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
+      "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
+      "dev": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.11.1",
+        "@webassemblyjs/helper-buffer": "1.11.1",
+        "@webassemblyjs/wasm-gen": "1.11.1",
+        "@webassemblyjs/wasm-parser": "1.11.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-parser": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
+      "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
+      "dev": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.11.1",
+        "@webassemblyjs/helper-api-error": "1.11.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+        "@webassemblyjs/ieee754": "1.11.1",
+        "@webassemblyjs/leb128": "1.11.1",
+        "@webassemblyjs/utf8": "1.11.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wast-printer": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
+      "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
+      "dev": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.11.1",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@xtuc/ieee754": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+      "dev": true
+    },
+    "node_modules/@xtuc/long": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+      "dev": true
+    },
+    "node_modules/abbrev": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+    },
+    "node_modules/abort-controller": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+      "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+      "dependencies": {
+        "event-target-shim": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=6.5"
+      }
+    },
+    "node_modules/accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "dependencies": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/acorn": {
+      "version": "8.8.2",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+      "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-import-assertions": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
+      "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
+      "dev": true,
+      "peerDependencies": {
+        "acorn": "^8"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/acorn-walk": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+      "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/agent-base": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+      "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+      "dependencies": {
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6.0.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "8.12.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
+      "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ajv-formats": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+      "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+      "dev": true,
+      "dependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "ajv": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/amp": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz",
+      "integrity": "sha512-OwIuC4yZaRogHKiuU5WlMR5Xk/jAcpPtawWL05Gj8Lvm2F6mwoJt4O/bHI+DHwG79vWd+8OFYM4/BzYqyRd3qw=="
+    },
+    "node_modules/amp-message": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/amp-message/-/amp-message-0.1.2.tgz",
+      "integrity": "sha512-JqutcFwoU1+jhv7ArgW38bqrE+LQdcRv4NxNw0mp0JHQyB6tXesWRjtYKlDgHRY2o3JE5UTaBGUK8kSWUdxWUg==",
+      "dependencies": {
+        "amp": "0.3.1"
+      }
+    },
+    "node_modules/ansi-colors": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+      "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/ansi-escapes": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+      "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+      "dev": true,
+      "dependencies": {
+        "type-fest": "^0.21.3"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ansi-escapes/node_modules/type-fest": {
+      "version": "0.21.3",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+      "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/append-field": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+      "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
+    },
+    "node_modules/aproba": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+      "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
+    },
+    "node_modules/archiver": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz",
+      "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==",
+      "dependencies": {
+        "archiver-utils": "^2.1.0",
+        "async": "^3.2.3",
+        "buffer-crc32": "^0.2.1",
+        "readable-stream": "^3.6.0",
+        "readdir-glob": "^1.0.0",
+        "tar-stream": "^2.2.0",
+        "zip-stream": "^4.1.0"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/archiver-utils": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
+      "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
+      "dependencies": {
+        "glob": "^7.1.4",
+        "graceful-fs": "^4.2.0",
+        "lazystream": "^1.0.0",
+        "lodash.defaults": "^4.2.0",
+        "lodash.difference": "^4.5.0",
+        "lodash.flatten": "^4.4.0",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.union": "^4.6.0",
+        "normalize-path": "^3.0.0",
+        "readable-stream": "^2.0.0"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/archiver/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/are-we-there-yet": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
+      "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+      "dependencies": {
+        "delegates": "^1.0.0",
+        "readable-stream": "^3.6.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/are-we-there-yet/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "dev": true
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+    },
+    "node_modules/array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
+    },
+    "node_modules/array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/arrify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+      "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/asap": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+      "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+      "dev": true
+    },
+    "node_modules/asn1.js": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+      "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+      "dependencies": {
+        "bn.js": "^4.0.0",
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0",
+        "safer-buffer": "^2.1.0"
+      }
+    },
+    "node_modules/ast-types": {
+      "version": "0.13.4",
+      "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
+      "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
+      "dependencies": {
+        "tslib": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/async": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
+      "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ=="
+    },
+    "node_modules/async-listener": {
+      "version": "0.6.10",
+      "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz",
+      "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==",
+      "dependencies": {
+        "semver": "^5.3.0",
+        "shimmer": "^1.1.0"
+      },
+      "engines": {
+        "node": "<=0.11.8 || >0.11.10"
+      }
+    },
+    "node_modules/async-listener/node_modules/semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/available-typed-arrays": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+      "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/aws-sdk": {
+      "version": "2.1369.0",
+      "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1369.0.tgz",
+      "integrity": "sha512-DdCQjlhQDi9w8J4moqECrrp9ARWCay0UI38adPSS0GG43gh3bl3OoMlgKJ8aZxi4jUvzE48K9yhFHz4y/mazZw==",
+      "dependencies": {
+        "buffer": "4.9.2",
+        "events": "1.1.1",
+        "ieee754": "1.1.13",
+        "jmespath": "0.16.0",
+        "querystring": "0.2.0",
+        "sax": "1.2.1",
+        "url": "0.10.3",
+        "util": "^0.12.4",
+        "uuid": "8.0.0",
+        "xml2js": "0.5.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/aws-sdk/node_modules/buffer": {
+      "version": "4.9.2",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+      "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+      "dependencies": {
+        "base64-js": "^1.0.2",
+        "ieee754": "^1.1.4",
+        "isarray": "^1.0.0"
+      }
+    },
+    "node_modules/aws-sdk/node_modules/events": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+      "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==",
+      "engines": {
+        "node": ">=0.4.x"
+      }
+    },
+    "node_modules/aws-sdk/node_modules/ieee754": {
+      "version": "1.1.13",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+      "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+    },
+    "node_modules/axios": {
+      "version": "0.21.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+      "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+      "dependencies": {
+        "follow-redirects": "^1.14.0"
+      }
+    },
+    "node_modules/babel-jest": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz",
+      "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==",
+      "dev": true,
+      "dependencies": {
+        "@jest/transform": "^29.5.0",
+        "@types/babel__core": "^7.1.14",
+        "babel-plugin-istanbul": "^6.1.1",
+        "babel-preset-jest": "^29.5.0",
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.9",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.8.0"
+      }
+    },
+    "node_modules/babel-plugin-istanbul": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
+      "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.0.0",
+        "@istanbuljs/load-nyc-config": "^1.0.0",
+        "@istanbuljs/schema": "^0.1.2",
+        "istanbul-lib-instrument": "^5.0.4",
+        "test-exclude": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/babel-plugin-jest-hoist": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz",
+      "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==",
+      "dev": true,
+      "dependencies": {
+        "@babel/template": "^7.3.3",
+        "@babel/types": "^7.3.3",
+        "@types/babel__core": "^7.1.14",
+        "@types/babel__traverse": "^7.0.6"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/babel-preset-current-node-syntax": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
+      "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/plugin-syntax-async-generators": "^7.8.4",
+        "@babel/plugin-syntax-bigint": "^7.8.3",
+        "@babel/plugin-syntax-class-properties": "^7.8.3",
+        "@babel/plugin-syntax-import-meta": "^7.8.3",
+        "@babel/plugin-syntax-json-strings": "^7.8.3",
+        "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
+        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+        "@babel/plugin-syntax-numeric-separator": "^7.8.3",
+        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+        "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+        "@babel/plugin-syntax-top-level-await": "^7.8.3"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/babel-preset-jest": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz",
+      "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==",
+      "dev": true,
+      "dependencies": {
+        "babel-plugin-jest-hoist": "^29.5.0",
+        "babel-preset-current-node-syntax": "^1.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/backo2": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+      "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==",
+      "optional": true
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/bcrypt": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz",
+      "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "@mapbox/node-pre-gyp": "^1.0.10",
+        "node-addon-api": "^5.0.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/big-integer": {
+      "version": "1.6.51",
+      "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
+      "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/bignumber.js": {
+      "version": "9.1.1",
+      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz",
+      "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/binary": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
+      "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==",
+      "dependencies": {
+        "buffers": "~0.1.1",
+        "chainsaw": "~0.1.0"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/bl": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+      "dependencies": {
+        "buffer": "^5.5.0",
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.4.0"
+      }
+    },
+    "node_modules/bl/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/blessed": {
+      "version": "0.1.81",
+      "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz",
+      "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==",
+      "bin": {
+        "blessed": "bin/tput.js"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/bluebird": {
+      "version": "3.4.7",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
+      "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA=="
+    },
+    "node_modules/bn.js": {
+      "version": "4.12.0",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+      "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+    },
+    "node_modules/bodec": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/bodec/-/bodec-0.1.0.tgz",
+      "integrity": "sha512-Ylo+MAo5BDUq1KA3f3R/MFhh+g8cnHmo8bz3YPGhI1znrMaf77ol1sfvYJzsw3nTE+Y2GryfDxBaR+AqpAkEHQ=="
+    },
+    "node_modules/body-parser": {
+      "version": "1.20.2",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
+      "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "content-type": "~1.0.5",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "on-finished": "2.4.1",
+        "qs": "6.11.0",
+        "raw-body": "2.5.2",
+        "type-is": "~1.6.18",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/body-parser/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/body-parser/node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/body-parser/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+    },
+    "node_modules/bowser": {
+      "version": "2.11.0",
+      "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",
+      "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dependencies": {
+        "fill-range": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/browserslist": {
+      "version": "4.21.5",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
+      "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        }
+      ],
+      "dependencies": {
+        "caniuse-lite": "^1.0.30001449",
+        "electron-to-chromium": "^1.4.284",
+        "node-releases": "^2.0.8",
+        "update-browserslist-db": "^1.0.10"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
+    "node_modules/bs-logger": {
+      "version": "0.2.6",
+      "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
+      "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
+      "dev": true,
+      "dependencies": {
+        "fast-json-stable-stringify": "2.x"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/bser": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
+      "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+      "dev": true,
+      "dependencies": {
+        "node-int64": "^0.4.0"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
+      }
+    },
+    "node_modules/buffer-crc32": {
+      "version": "0.2.13",
+      "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+      "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/buffer-equal-constant-time": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+      "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+    },
+    "node_modules/buffer-indexof-polyfill": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz",
+      "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==",
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/buffers": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
+      "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==",
+      "engines": {
+        "node": ">=0.2.0"
+      }
+    },
+    "node_modules/busboy": {
+      "version": "0.2.14",
+      "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
+      "integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==",
+      "dependencies": {
+        "dicer": "0.2.5",
+        "readable-stream": "1.1.x"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/busboy/node_modules/isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
+    },
+    "node_modules/busboy/node_modules/readable-stream": {
+      "version": "1.1.14",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+      "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.1",
+        "isarray": "0.0.1",
+        "string_decoder": "~0.10.x"
+      }
+    },
+    "node_modules/busboy/node_modules/string_decoder": {
+      "version": "0.10.31",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+      "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/call-me-maybe": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
+      "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==",
+      "dev": true
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001468",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001468.tgz",
+      "integrity": "sha512-zgAo8D5kbOyUcRAgSmgyuvBkjrGk5CGYG5TYgFdpQv+ywcyEpo1LOWoG8YmoflGnh+V+UsNuKYedsoYs0hzV5A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        }
+      ]
+    },
+    "node_modules/chainsaw": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
+      "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==",
+      "dependencies": {
+        "traverse": ">=0.3.0 <0.4"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/char-regex": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+      "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/chardet": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+      "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+      "dev": true
+    },
+    "node_modules/charm": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz",
+      "integrity": "sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ=="
+    },
+    "node_modules/cheerio": {
+      "version": "1.0.0-rc.10",
+      "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz",
+      "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==",
+      "dependencies": {
+        "cheerio-select": "^1.5.0",
+        "dom-serializer": "^1.3.2",
+        "domhandler": "^4.2.0",
+        "htmlparser2": "^6.1.0",
+        "parse5": "^6.0.1",
+        "parse5-htmlparser2-tree-adapter": "^6.0.1",
+        "tslib": "^2.2.0"
+      },
+      "engines": {
+        "node": ">= 6"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
+      }
+    },
+    "node_modules/cheerio-select": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.6.0.tgz",
+      "integrity": "sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g==",
+      "dependencies": {
+        "css-select": "^4.3.0",
+        "css-what": "^6.0.1",
+        "domelementtype": "^2.2.0",
+        "domhandler": "^4.3.1",
+        "domutils": "^2.8.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/chownr": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/chrome-trace-event": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+      "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/ci-info": {
+      "version": "3.8.0",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
+      "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/sibiraj-s"
+        }
+      ],
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cjs-module-lexer": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
+      "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==",
+      "dev": true
+    },
+    "node_modules/class-transformer": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
+      "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw=="
+    },
+    "node_modules/class-validator": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.0.tgz",
+      "integrity": "sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==",
+      "dependencies": {
+        "@types/validator": "^13.7.10",
+        "libphonenumber-js": "^1.10.14",
+        "validator": "^13.7.0"
+      }
+    },
+    "node_modules/cli-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+      "dev": true,
+      "dependencies": {
+        "restore-cursor": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cli-spinners": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz",
+      "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-table3": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz",
+      "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "^4.2.0"
+      },
+      "engines": {
+        "node": "10.* || >= 12.*"
+      },
+      "optionalDependencies": {
+        "@colors/colors": "1.5.0"
+      }
+    },
+    "node_modules/cli-tableau": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/cli-tableau/-/cli-tableau-2.0.1.tgz",
+      "integrity": "sha512-he+WTicka9cl0Fg/y+YyxcN6/bfQ/1O3QmgxRXDhABKqLzvoOSM4fMzp39uMyLBulAFuywD2N7UaoQE7WaADxQ==",
+      "dependencies": {
+        "chalk": "3.0.0"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/cli-tableau/node_modules/chalk": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+      "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cli-width": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
+      "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/clone": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+      "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/cluster-key-slot": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
+      "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/co": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+      "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+      "dev": true,
+      "engines": {
+        "iojs": ">= 1.0.0",
+        "node": ">= 0.12.0"
+      }
+    },
+    "node_modules/collect-v8-coverage": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
+      "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==",
+      "dev": true
+    },
+    "node_modules/color": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
+      "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
+      "dependencies": {
+        "color-convert": "^1.9.3",
+        "color-string": "^1.6.0"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+    },
+    "node_modules/color-string": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+      "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+      "dependencies": {
+        "color-name": "^1.0.0",
+        "simple-swizzle": "^0.2.2"
+      }
+    },
+    "node_modules/color-support": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+      "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+      "bin": {
+        "color-support": "bin.js"
+      }
+    },
+    "node_modules/color/node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/color/node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+    },
+    "node_modules/colorspace": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
+      "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
+      "dependencies": {
+        "color": "^3.1.3",
+        "text-hex": "1.0.x"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/commander": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+      "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/component-emitter": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+      "dev": true
+    },
+    "node_modules/compress-commons": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz",
+      "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==",
+      "dependencies": {
+        "buffer-crc32": "^0.2.13",
+        "crc32-stream": "^4.0.2",
+        "normalize-path": "^3.0.0",
+        "readable-stream": "^3.6.0"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/compress-commons/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/compressible": {
+      "version": "2.0.18",
+      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+      "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+      "dependencies": {
+        "mime-db": ">= 1.43.0 < 2"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/compression": {
+      "version": "1.7.4",
+      "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+      "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+      "dependencies": {
+        "accepts": "~1.3.5",
+        "bytes": "3.0.0",
+        "compressible": "~2.0.16",
+        "debug": "2.6.9",
+        "on-headers": "~1.0.2",
+        "safe-buffer": "5.1.2",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/compression/node_modules/bytes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+      "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/compression/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/compression/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/compression/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+    },
+    "node_modules/concat-stream": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+      "engines": [
+        "node >= 0.8"
+      ],
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "node_modules/concurrently": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz",
+      "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "date-fns": "^2.16.1",
+        "lodash": "^4.17.21",
+        "rxjs": "^6.6.3",
+        "spawn-command": "^0.0.2-1",
+        "supports-color": "^8.1.0",
+        "tree-kill": "^1.2.2",
+        "yargs": "^16.2.0"
+      },
+      "bin": {
+        "concurrently": "bin/concurrently.js"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/concurrently/node_modules/cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "node_modules/concurrently/node_modules/rxjs": {
+      "version": "6.6.7",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+      "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+      "dependencies": {
+        "tslib": "^1.9.0"
+      },
+      "engines": {
+        "npm": ">=2.0.0"
+      }
+    },
+    "node_modules/concurrently/node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/concurrently/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "node_modules/concurrently/node_modules/yargs": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "dependencies": {
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/concurrently/node_modules/yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/connect-redis": {
+      "version": "6.1.3",
+      "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-6.1.3.tgz",
+      "integrity": "sha512-aaNluLlAn/3JPxRwdzw7lhvEoU6Enb+d83xnokUNhC9dktqBoawKWL+WuxinxvBLTz6q9vReTnUDnUslaz74aw==",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/consola": {
+      "version": "2.15.3",
+      "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz",
+      "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw=="
+    },
+    "node_modules/console-control-strings": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+      "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
+    },
+    "node_modules/content-disposition": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+      "dependencies": {
+        "safe-buffer": "5.2.1"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/content-type": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/continuation-local-storage": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz",
+      "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==",
+      "dependencies": {
+        "async-listener": "^0.6.0",
+        "emitter-listener": "^1.1.1"
+      }
+    },
+    "node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true
+    },
+    "node_modules/cookie": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+      "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-parser": {
+      "version": "1.4.6",
+      "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
+      "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
+      "dependencies": {
+        "cookie": "0.4.1",
+        "cookie-signature": "1.0.6"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/cookie-parser/node_modules/cookie": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+      "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
+    },
+    "node_modules/cookiejar": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
+      "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
+      "dev": true
+    },
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+    },
+    "node_modules/cors": {
+      "version": "2.8.5",
+      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+      "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+      "dependencies": {
+        "object-assign": "^4",
+        "vary": "^1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/cosmiconfig": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+      "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+      "dev": true,
+      "dependencies": {
+        "@types/parse-json": "^4.0.0",
+        "import-fresh": "^3.2.1",
+        "parse-json": "^5.0.0",
+        "path-type": "^4.0.0",
+        "yaml": "^1.10.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/crc-32": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+      "bin": {
+        "crc32": "bin/crc32.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/crc32-stream": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz",
+      "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==",
+      "dependencies": {
+        "crc-32": "^1.2.0",
+        "readable-stream": "^3.4.0"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/crc32-stream/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/create-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+      "dev": true
+    },
+    "node_modules/croner": {
+      "version": "4.1.97",
+      "resolved": "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz",
+      "integrity": "sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ=="
+    },
+    "node_modules/cross-env": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+      "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+      "dependencies": {
+        "cross-spawn": "^7.0.1"
+      },
+      "bin": {
+        "cross-env": "src/bin/cross-env.js",
+        "cross-env-shell": "src/bin/cross-env-shell.js"
+      },
+      "engines": {
+        "node": ">=10.14",
+        "npm": ">=6",
+        "yarn": ">=1"
+      }
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/crypto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz",
+      "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==",
+      "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in."
+    },
+    "node_modules/crypto-js": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
+      "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
+    },
+    "node_modules/css-select": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+      "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^6.0.1",
+        "domhandler": "^4.3.1",
+        "domutils": "^2.8.0",
+        "nth-check": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/css-what": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+      "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+      "engines": {
+        "node": ">= 6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/culvert": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/culvert/-/culvert-0.1.2.tgz",
+      "integrity": "sha512-yi1x3EAWKjQTreYWeSd98431AV+IEE0qoDyOoaHJ7KJ21gv6HtBXHVLX74opVSGqcR8/AbjJBHAHpcOy2bj5Gg=="
+    },
+    "node_modules/data-uri-to-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
+      "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/date-fns": {
+      "version": "2.29.3",
+      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz",
+      "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==",
+      "engines": {
+        "node": ">=0.11"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/date-fns"
+      }
+    },
+    "node_modules/dayjs": {
+      "version": "1.11.7",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
+      "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
+    },
+    "node_modules/debug": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+      "dependencies": {
+        "ms": "2.1.2"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/dedent": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
+      "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==",
+      "dev": true
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
+    },
+    "node_modules/deepmerge": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+      "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/defaults": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+      "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+      "dev": true,
+      "dependencies": {
+        "clone": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/degenerator": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.2.tgz",
+      "integrity": "sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ==",
+      "dependencies": {
+        "ast-types": "^0.13.2",
+        "escodegen": "^1.8.1",
+        "esprima": "^4.0.0",
+        "vm2": "^3.9.8"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/delegates": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+      "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
+    },
+    "node_modules/denque": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
+      "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/destroy": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
+      "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/detect-newline": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
+      "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/dezalgo": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
+      "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
+      "dev": true,
+      "dependencies": {
+        "asap": "^2.0.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/dicer": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
+      "integrity": "sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg==",
+      "dependencies": {
+        "readable-stream": "1.1.x",
+        "streamsearch": "0.1.2"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/dicer/node_modules/isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
+    },
+    "node_modules/dicer/node_modules/readable-stream": {
+      "version": "1.1.14",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+      "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.1",
+        "isarray": "0.0.1",
+        "string_decoder": "~0.10.x"
+      }
+    },
+    "node_modules/dicer/node_modules/string_decoder": {
+      "version": "0.10.31",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+      "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
+    },
+    "node_modules/diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.3.1"
+      }
+    },
+    "node_modules/diff-sequences": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz",
+      "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==",
+      "dev": true,
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/dir-glob": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+      "dev": true,
+      "dependencies": {
+        "path-type": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/doctrine": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "dev": true,
+      "dependencies": {
+        "esutils": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/dom-serializer": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+      "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.2.0",
+        "entities": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/domelementtype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+      "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ]
+    },
+    "node_modules/domhandler": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+      "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+      "dependencies": {
+        "domelementtype": "^2.2.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
+    "node_modules/domutils": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+      "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+      "dependencies": {
+        "dom-serializer": "^1.0.1",
+        "domelementtype": "^2.2.0",
+        "domhandler": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
+    "node_modules/dotenv": {
+      "version": "15.0.1",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-15.0.1.tgz",
+      "integrity": "sha512-4OnbwRfzR+xQThp7uq1xpUS9fmgZ//njexOtPjPSbK3yHGrSHSJnaJRsXderSSm2elfvVj+Y5awDC0I8Oy8rkA==",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/dotenv-cli": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.1.0.tgz",
+      "integrity": "sha512-motytjZFQB3ZtGTIN4c0vnFgv4kuNZ2WxVnGY6PVFiygCzkm3IFBBguDUzezd9HgNA0OYYd6vNCWlozs0Q1Zxg==",
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "dotenv": "^16.0.0",
+        "dotenv-expand": "^10.0.0",
+        "minimist": "^1.2.6"
+      },
+      "bin": {
+        "dotenv": "cli.js"
+      }
+    },
+    "node_modules/dotenv-cli/node_modules/dotenv": {
+      "version": "16.0.3",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
+      "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/dotenv-expand": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz",
+      "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/duplexer2": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+      "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==",
+      "dependencies": {
+        "readable-stream": "^2.0.2"
+      }
+    },
+    "node_modules/ecdsa-sig-formatter": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.4.334",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.334.tgz",
+      "integrity": "sha512-laZ1odk+TRen6q0GeyQx/JEkpD3iSZT7ewopCpKqg9bTjP1l8XRfU3Bg20CFjNPZkp5+NDBl3iqd4o/kPO+Vew==",
+      "dev": true
+    },
+    "node_modules/emitter-listener": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz",
+      "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==",
+      "dependencies": {
+        "shimmer": "^1.2.0"
+      }
+    },
+    "node_modules/emittery": {
+      "version": "0.13.1",
+      "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
+      "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/emittery?sponsor=1"
+      }
+    },
+    "node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+    },
+    "node_modules/enabled": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
+      "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
+    },
+    "node_modules/encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/enhanced-resolve": {
+      "version": "5.12.0",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz",
+      "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==",
+      "dev": true,
+      "dependencies": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/enquirer": {
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+      "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+      "dependencies": {
+        "ansi-colors": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/entities": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "node_modules/es-module-lexer": {
+      "version": "0.9.3",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
+      "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
+      "dev": true
+    },
+    "node_modules/escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/escodegen": {
+      "version": "1.14.3",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+      "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+      "dependencies": {
+        "esprima": "^4.0.1",
+        "estraverse": "^4.2.0",
+        "esutils": "^2.0.2",
+        "optionator": "^0.8.1"
+      },
+      "bin": {
+        "escodegen": "bin/escodegen.js",
+        "esgenerate": "bin/esgenerate.js"
+      },
+      "engines": {
+        "node": ">=4.0"
+      },
+      "optionalDependencies": {
+        "source-map": "~0.6.1"
+      }
+    },
+    "node_modules/escodegen/node_modules/levn": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+      "dependencies": {
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/escodegen/node_modules/optionator": {
+      "version": "0.8.3",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+      "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+      "dependencies": {
+        "deep-is": "~0.1.3",
+        "fast-levenshtein": "~2.0.6",
+        "levn": "~0.3.0",
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2",
+        "word-wrap": "~1.2.3"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/escodegen/node_modules/prelude-ls": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+      "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/escodegen/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "optional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/escodegen/node_modules/type-check": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+      "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+      "dependencies": {
+        "prelude-ls": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/eslint": {
+      "version": "8.36.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz",
+      "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.2.0",
+        "@eslint-community/regexpp": "^4.4.0",
+        "@eslint/eslintrc": "^2.0.1",
+        "@eslint/js": "8.36.0",
+        "@humanwhocodes/config-array": "^0.11.8",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@nodelib/fs.walk": "^1.2.8",
+        "ajv": "^6.10.0",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.3.2",
+        "doctrine": "^3.0.0",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^7.1.1",
+        "eslint-visitor-keys": "^3.3.0",
+        "espree": "^9.5.0",
+        "esquery": "^1.4.2",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "globals": "^13.19.0",
+        "grapheme-splitter": "^1.0.4",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "is-path-inside": "^3.0.3",
+        "js-sdsl": "^4.1.4",
+        "js-yaml": "^4.1.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.1",
+        "strip-ansi": "^6.0.1",
+        "strip-json-comments": "^3.1.0",
+        "text-table": "^0.2.0"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint-config-prettier": {
+      "version": "8.8.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz",
+      "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==",
+      "dev": true,
+      "bin": {
+        "eslint-config-prettier": "bin/cli.js"
+      },
+      "peerDependencies": {
+        "eslint": ">=7.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-prettier": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
+      "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
+      "dev": true,
+      "dependencies": {
+        "prettier-linter-helpers": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "eslint": ">=7.28.0",
+        "prettier": ">=2.0.0"
+      },
+      "peerDependenciesMeta": {
+        "eslint-config-prettier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+      "dev": true,
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
+      "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/eslint/node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/eslint/node_modules/eslint-scope": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+      "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+      "dev": true,
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/eslint/node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/eslint/node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/eslint/node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "node_modules/espree": {
+      "version": "9.5.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz",
+      "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^8.8.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "bin": {
+        "esparse": "bin/esparse.js",
+        "esvalidate": "bin/esvalidate.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/esquery": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+      "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+      "dev": true,
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esquery/node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esrecurse/node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/event-target-shim": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+      "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/eventemitter2": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz",
+      "integrity": "sha512-5EM1GHXycJBS6mauYAbVKT1cVs7POKWb2NXD4Vyt8dDqeZa7LaDK1/sjtL+Zb0lzTpSNil4596Dyu97hz37QLg=="
+    },
+    "node_modules/eventemitter3": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
+      "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==",
+      "optional": true
+    },
+    "node_modules/events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.x"
+      }
+    },
+    "node_modules/exceljs": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.3.0.tgz",
+      "integrity": "sha512-hTAeo5b5TPvf8Z02I2sKIT4kSfCnOO2bCxYX8ABqODCdAjppI3gI9VYiGCQQYVcBaBSKlFDMKlAQRqC+kV9O8w==",
+      "dependencies": {
+        "archiver": "^5.0.0",
+        "dayjs": "^1.8.34",
+        "fast-csv": "^4.3.1",
+        "jszip": "^3.5.0",
+        "readable-stream": "^3.6.0",
+        "saxes": "^5.0.1",
+        "tmp": "^0.2.0",
+        "unzipper": "^0.10.11",
+        "uuid": "^8.3.0"
+      },
+      "engines": {
+        "node": ">=8.3.0"
+      }
+    },
+    "node_modules/exceljs/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/exceljs/node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/exceljs/node_modules/tmp": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
+      "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
+      "dependencies": {
+        "rimraf": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8.17.0"
+      }
+    },
+    "node_modules/exceljs/node_modules/uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/execa": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+      "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+      "dev": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^6.0.0",
+        "human-signals": "^2.1.0",
+        "is-stream": "^2.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^4.0.1",
+        "onetime": "^5.1.2",
+        "signal-exit": "^3.0.3",
+        "strip-final-newline": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/exit": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+      "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/expect": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz",
+      "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==",
+      "dev": true,
+      "dependencies": {
+        "@jest/expect-utils": "^29.5.0",
+        "jest-get-type": "^29.4.3",
+        "jest-matcher-utils": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-util": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/express": {
+      "version": "4.18.2",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
+      "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
+      "dependencies": {
+        "accepts": "~1.3.8",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.20.1",
+        "content-disposition": "0.5.4",
+        "content-type": "~1.0.4",
+        "cookie": "0.5.0",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "1.2.0",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "merge-descriptors": "1.0.1",
+        "methods": "~1.1.2",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "~2.0.7",
+        "qs": "6.11.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.2.1",
+        "send": "0.18.0",
+        "serve-static": "1.15.0",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      }
+    },
+    "node_modules/express-mysql-session": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/express-mysql-session/-/express-mysql-session-2.1.8.tgz",
+      "integrity": "sha512-EnFdhG6RNmC/gZpC+mmcGKZeX8kcp5Lh90ozFTa8LNJmHrGWzqOt8564IvnTw5c4d3/Aj0EAXnpGuzLCoKzcig==",
+      "dependencies": {
+        "debug": "4.3.4",
+        "express-session": "1.17.2",
+        "mysql": "2.18.1",
+        "underscore": "1.13.3"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://degreesofzero.com/donate.html?project=express-mysql-session"
+      }
+    },
+    "node_modules/express-mysql-session/node_modules/cookie": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+      "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/express-mysql-session/node_modules/express-session": {
+      "version": "1.17.2",
+      "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz",
+      "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==",
+      "dependencies": {
+        "cookie": "0.4.1",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "~2.0.0",
+        "on-headers": "~1.0.2",
+        "parseurl": "~1.3.3",
+        "safe-buffer": "5.2.1",
+        "uid-safe": "~2.1.5"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/express-mysql-session/node_modules/express-session/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/express-mysql-session/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/express-session": {
+      "version": "1.17.3",
+      "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
+      "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
+      "dependencies": {
+        "cookie": "0.4.2",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "~2.0.0",
+        "on-headers": "~1.0.2",
+        "parseurl": "~1.3.3",
+        "safe-buffer": "5.2.1",
+        "uid-safe": "~2.1.5"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/express-session/node_modules/cookie": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+      "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/express-session/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/express-session/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/express/node_modules/body-parser": {
+      "version": "1.20.1",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
+      "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "content-type": "~1.0.4",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "on-finished": "2.4.1",
+        "qs": "6.11.0",
+        "raw-body": "2.5.1",
+        "type-is": "~1.6.18",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/express/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/express/node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/express/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/express/node_modules/path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
+    },
+    "node_modules/express/node_modules/raw-body": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+      "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+    },
+    "node_modules/external-editor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+      "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+      "dev": true,
+      "dependencies": {
+        "chardet": "^0.7.0",
+        "iconv-lite": "^0.4.24",
+        "tmp": "^0.0.33"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/external-editor/node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "dev": true,
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/fast-csv": {
+      "version": "4.3.6",
+      "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz",
+      "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==",
+      "dependencies": {
+        "@fast-csv/format": "4.3.5",
+        "@fast-csv/parse": "4.3.6"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "node_modules/fast-diff": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+      "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+      "dev": true
+    },
+    "node_modules/fast-glob": {
+      "version": "3.2.12",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+      "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+      "devOptional": true,
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/fast-json-patch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz",
+      "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ=="
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
+    },
+    "node_modules/fast-safe-stringify": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+      "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
+    },
+    "node_modules/fast-text-encoding": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz",
+      "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w=="
+    },
+    "node_modules/fast-xml-parser": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz",
+      "integrity": "sha512-CDYeykkle1LiA/uqQyNwYpFbyF6Axec6YapmpUP+/RHWIoR1zKjocdvNaTsxCxZzQ6v9MLXaSYm9Qq0thv0DHg==",
+      "dependencies": {
+        "strnum": "^1.0.5"
+      },
+      "bin": {
+        "fxparser": "src/cli/cli.js"
+      },
+      "funding": {
+        "type": "paypal",
+        "url": "https://paypal.me/naturalintelligence"
+      }
+    },
+    "node_modules/fastq": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+      "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+      "devOptional": true,
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/fb-watchman": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
+      "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
+      "dev": true,
+      "dependencies": {
+        "bser": "2.1.1"
+      }
+    },
+    "node_modules/fclone": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz",
+      "integrity": "sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw=="
+    },
+    "node_modules/fecha": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+      "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
+    },
+    "node_modules/figures": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+      "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+      "dev": true,
+      "dependencies": {
+        "escape-string-regexp": "^1.0.5"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/figures/node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/file-entry-cache": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+      "dev": true,
+      "dependencies": {
+        "flat-cache": "^3.0.4"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
+      }
+    },
+    "node_modules/file-stream-rotator": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz",
+      "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==",
+      "dependencies": {
+        "moment": "^2.29.1"
+      }
+    },
+    "node_modules/file-type": {
+      "version": "3.9.0",
+      "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
+      "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/file-uri-to-path": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz",
+      "integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/finalhandler": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+      "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+      "dependencies": {
+        "debug": "2.6.9",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "statuses": "2.0.1",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/finalhandler/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/finalhandler/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+      "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+      "dev": true,
+      "dependencies": {
+        "flatted": "^3.1.0",
+        "rimraf": "^3.0.2"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
+      }
+    },
+    "node_modules/flat-cache/node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+      "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+      "dev": true
+    },
+    "node_modules/fn.name": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
+      "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.2",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+      "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/for-each": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+      "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+      "dependencies": {
+        "is-callable": "^1.1.3"
+      }
+    },
+    "node_modules/fork-ts-checker-webpack-plugin": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz",
+      "integrity": "sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.16.7",
+        "chalk": "^4.1.2",
+        "chokidar": "^3.5.3",
+        "cosmiconfig": "^7.0.1",
+        "deepmerge": "^4.2.2",
+        "fs-extra": "^10.0.0",
+        "memfs": "^3.4.1",
+        "minimatch": "^3.0.4",
+        "node-abort-controller": "^3.0.1",
+        "schema-utils": "^3.1.1",
+        "semver": "^7.3.5",
+        "tapable": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=12.13.0",
+        "yarn": ">=1.0.0"
+      },
+      "peerDependencies": {
+        "typescript": ">3.6.0",
+        "webpack": "^5.11.0"
+      }
+    },
+    "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dev": true,
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/formidable": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz",
+      "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==",
+      "dev": true,
+      "dependencies": {
+        "dezalgo": "^1.0.4",
+        "hexoid": "^1.0.0",
+        "once": "^1.4.0",
+        "qs": "^6.11.0"
+      },
+      "funding": {
+        "url": "https://ko-fi.com/tunnckoCore/commissions"
+      }
+    },
+    "node_modules/forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fs-constants": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+      "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+    },
+    "node_modules/fs-extra": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz",
+      "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==",
+      "dev": true,
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=14.14"
+      }
+    },
+    "node_modules/fs-minipass": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/fs-minipass/node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/fs-monkey": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
+      "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
+      "dev": true
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/fstream": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
+      "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
+      "dependencies": {
+        "graceful-fs": "^4.1.2",
+        "inherits": "~2.0.0",
+        "mkdirp": ">=0.5 0",
+        "rimraf": "2"
+      },
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/fstream/node_modules/rimraf": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+      "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      }
+    },
+    "node_modules/ftp": {
+      "version": "0.3.10",
+      "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz",
+      "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==",
+      "dependencies": {
+        "readable-stream": "1.1.x",
+        "xregexp": "2.0.0"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/ftp/node_modules/isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
+    },
+    "node_modules/ftp/node_modules/readable-stream": {
+      "version": "1.1.14",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+      "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.1",
+        "isarray": "0.0.1",
+        "string_decoder": "~0.10.x"
+      }
+    },
+    "node_modules/ftp/node_modules/string_decoder": {
+      "version": "0.10.31",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+      "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+    },
+    "node_modules/gauge": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
+      "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+      "dependencies": {
+        "aproba": "^1.0.3 || ^2.0.0",
+        "color-support": "^1.1.2",
+        "console-control-strings": "^1.0.0",
+        "has-unicode": "^2.0.1",
+        "object-assign": "^4.1.1",
+        "signal-exit": "^3.0.0",
+        "string-width": "^4.2.3",
+        "strip-ansi": "^6.0.1",
+        "wide-align": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/gaxios": {
+      "version": "4.3.3",
+      "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.3.tgz",
+      "integrity": "sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==",
+      "dependencies": {
+        "abort-controller": "^3.0.0",
+        "extend": "^3.0.2",
+        "https-proxy-agent": "^5.0.0",
+        "is-stream": "^2.0.0",
+        "node-fetch": "^2.6.7"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/gcp-metadata": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz",
+      "integrity": "sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==",
+      "dependencies": {
+        "gaxios": "^4.0.0",
+        "json-bigint": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/generate-function": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+      "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+      "dependencies": {
+        "is-property": "^1.0.2"
+      }
+    },
+    "node_modules/generic-pool": {
+      "version": "3.9.0",
+      "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
+      "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
+      "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-package-type": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+      "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/get-uri": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz",
+      "integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==",
+      "dependencies": {
+        "@tootallnate/once": "1",
+        "data-uri-to-buffer": "3",
+        "debug": "4",
+        "file-uri-to-path": "2",
+        "fs-extra": "^8.1.0",
+        "ftp": "^0.3.10"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/get-uri/node_modules/fs-extra": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+      "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^4.0.0",
+        "universalify": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=6 <7 || >=8"
+      }
+    },
+    "node_modules/get-uri/node_modules/jsonfile": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+      "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/get-uri/node_modules/universalify": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/git-node-fs": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/git-node-fs/-/git-node-fs-1.0.0.tgz",
+      "integrity": "sha512-bLQypt14llVXBg0S0u8q8HmU7g9p3ysH+NvVlae5vILuUvs759665HvmR5+wb04KjHyjFcDRxdYb4kyNnluMUQ=="
+    },
+    "node_modules/git-sha1": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/git-sha1/-/git-sha1-0.1.2.tgz",
+      "integrity": "sha512-2e/nZezdVlyCopOCYHeW0onkbZg7xP1Ad6pndPy1rCygeRykefUS6r7oA5cJRGEFvseiaz5a/qUHFVX1dd6Isg=="
+    },
+    "node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/glob-to-regexp": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+      "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+      "dev": true
+    },
+    "node_modules/globals": {
+      "version": "13.20.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+      "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+      "dev": true,
+      "dependencies": {
+        "type-fest": "^0.20.2"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/globby": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+      "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+      "dev": true,
+      "dependencies": {
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.2.9",
+        "ignore": "^5.2.0",
+        "merge2": "^1.4.1",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/google-auth-library": {
+      "version": "7.14.1",
+      "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz",
+      "integrity": "sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA==",
+      "dependencies": {
+        "arrify": "^2.0.0",
+        "base64-js": "^1.3.0",
+        "ecdsa-sig-formatter": "^1.0.11",
+        "fast-text-encoding": "^1.0.0",
+        "gaxios": "^4.0.0",
+        "gcp-metadata": "^4.2.0",
+        "gtoken": "^5.0.4",
+        "jws": "^4.0.0",
+        "lru-cache": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/google-p12-pem": {
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.4.tgz",
+      "integrity": "sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg==",
+      "dependencies": {
+        "node-forge": "^1.3.1"
+      },
+      "bin": {
+        "gp12-pem": "build/src/bin/gp12-pem.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/googleapis": {
+      "version": "89.0.0",
+      "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-89.0.0.tgz",
+      "integrity": "sha512-eH91BN+6R/Mp5uulrhKWGwKAbibfDNOIu1Oq8n12aFTiqb23/A9kVdRhituYVqhRqSWLr/kv1dpFbd6n+uN+7Q==",
+      "dependencies": {
+        "google-auth-library": "^7.0.2",
+        "googleapis-common": "^5.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/googleapis-common": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-5.1.0.tgz",
+      "integrity": "sha512-RXrif+Gzhq1QAzfjxulbGvAY3FPj8zq/CYcvgjzDbaBNCD6bUl+86I7mUs4DKWHGruuK26ijjR/eDpWIDgNROA==",
+      "dependencies": {
+        "extend": "^3.0.2",
+        "gaxios": "^4.0.0",
+        "google-auth-library": "^7.14.0",
+        "qs": "^6.7.0",
+        "url-template": "^2.0.8",
+        "uuid": "^8.0.0"
+      },
+      "engines": {
+        "node": ">=10.10.0"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+      "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+      "dependencies": {
+        "get-intrinsic": "^1.1.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
+    },
+    "node_modules/grapheme-splitter": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+      "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+      "dev": true
+    },
+    "node_modules/graphql": {
+      "version": "16.6.0",
+      "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz",
+      "integrity": "sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==",
+      "optional": true,
+      "peer": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
+      }
+    },
+    "node_modules/graphql-tag": {
+      "version": "2.12.6",
+      "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz",
+      "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "peerDependencies": {
+        "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
+      }
+    },
+    "node_modules/graphql-ws": {
+      "version": "5.12.0",
+      "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.12.0.tgz",
+      "integrity": "sha512-PA3ImUp8utrpEjoxBMhvxsjkStvFEdU0E1gEBREt8HZIWkxOUymwJBhFnBL7t/iHhUq1GVPeZevPinkZFENxTw==",
+      "optional": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "peerDependencies": {
+        "graphql": ">=0.11 <=16"
+      }
+    },
+    "node_modules/gtoken": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz",
+      "integrity": "sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==",
+      "dependencies": {
+        "gaxios": "^4.0.0",
+        "google-p12-pem": "^3.1.3",
+        "jws": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dependencies": {
+        "function-bind": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+      "dependencies": {
+        "has-symbols": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-unicode": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+      "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
+    },
+    "node_modules/hexoid": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz",
+      "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/html-comment-regex": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
+      "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ=="
+    },
+    "node_modules/html-escaper": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+      "dev": true
+    },
+    "node_modules/htmlparser2": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+      "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+      "funding": [
+        "https://github.com/fb55/htmlparser2?sponsor=1",
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ],
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.0.0",
+        "domutils": "^2.5.2",
+        "entities": "^2.0.0"
+      }
+    },
+    "node_modules/http_ece": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.1.0.tgz",
+      "integrity": "sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==",
+      "dependencies": {
+        "urlsafe-base64": "~1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/http-errors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+      "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+      "dependencies": {
+        "depd": "2.0.0",
+        "inherits": "2.0.4",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "toidentifier": "1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/http-proxy-agent": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+      "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+      "dependencies": {
+        "@tootallnate/once": "1",
+        "agent-base": "6",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/https-proxy-agent": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+      "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+      "dependencies": {
+        "agent-base": "6",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/human-signals": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+      "dev": true,
+      "engines": {
+        "node": ">=10.17.0"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/ignore": {
+      "version": "5.2.4",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+      "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/ignore-by-default": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+      "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
+      "dev": true
+    },
+    "node_modules/immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dev": true,
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/import-local": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
+      "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+      "dev": true,
+      "dependencies": {
+        "pkg-dir": "^4.2.0",
+        "resolve-cwd": "^3.0.0"
+      },
+      "bin": {
+        "import-local-fixture": "fixtures/cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "node_modules/ini": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+    },
+    "node_modules/inquirer": {
+      "version": "8.2.5",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz",
+      "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==",
+      "dev": true,
+      "dependencies": {
+        "ansi-escapes": "^4.2.1",
+        "chalk": "^4.1.1",
+        "cli-cursor": "^3.1.0",
+        "cli-width": "^3.0.0",
+        "external-editor": "^3.0.3",
+        "figures": "^3.0.0",
+        "lodash": "^4.17.21",
+        "mute-stream": "0.0.8",
+        "ora": "^5.4.1",
+        "run-async": "^2.4.0",
+        "rxjs": "^7.5.5",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0",
+        "through": "^2.3.6",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/interpret": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+      "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/ip": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
+      "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
+    },
+    "node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/is-arguments": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+      "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+      "dev": true
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-callable": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+      "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-core-module": {
+      "version": "2.11.0",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+      "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+      "dependencies": {
+        "has": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-generator-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+      "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/is-generator-function": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+      "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+      "dependencies": {
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-interactive": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+      "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-path-inside": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-property": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+      "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
+    },
+    "node_modules/is-stream": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-typed-array": {
+      "version": "1.1.10",
+      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
+      "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
+      "dependencies": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "gopd": "^1.0.1",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-unicode-supported": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+    },
+    "node_modules/istanbul-lib-coverage": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+      "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-instrument": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+      "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.12.3",
+        "@babel/parser": "^7.14.7",
+        "@istanbuljs/schema": "^0.1.2",
+        "istanbul-lib-coverage": "^3.2.0",
+        "semver": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-instrument/node_modules/semver": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/istanbul-lib-report": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+      "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+      "dev": true,
+      "dependencies": {
+        "istanbul-lib-coverage": "^3.0.0",
+        "make-dir": "^3.0.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-source-maps": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
+      "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^4.1.1",
+        "istanbul-lib-coverage": "^3.0.0",
+        "source-map": "^0.6.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/istanbul-lib-source-maps/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/istanbul-reports": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
+      "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
+      "dev": true,
+      "dependencies": {
+        "html-escaper": "^2.0.0",
+        "istanbul-lib-report": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/iterall": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz",
+      "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==",
+      "optional": true
+    },
+    "node_modules/iterare": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz",
+      "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/jest": {
+      "version": "29.3.1",
+      "resolved": "https://registry.npmjs.org/jest/-/jest-29.3.1.tgz",
+      "integrity": "sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==",
+      "dev": true,
+      "dependencies": {
+        "@jest/core": "^29.3.1",
+        "@jest/types": "^29.3.1",
+        "import-local": "^3.0.2",
+        "jest-cli": "^29.3.1"
+      },
+      "bin": {
+        "jest": "bin/jest.js"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jest-changed-files": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz",
+      "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==",
+      "dev": true,
+      "dependencies": {
+        "execa": "^5.0.0",
+        "p-limit": "^3.1.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-circus": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz",
+      "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==",
+      "dev": true,
+      "dependencies": {
+        "@jest/environment": "^29.5.0",
+        "@jest/expect": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "co": "^4.6.0",
+        "dedent": "^0.7.0",
+        "is-generator-fn": "^2.0.0",
+        "jest-each": "^29.5.0",
+        "jest-matcher-utils": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-runtime": "^29.5.0",
+        "jest-snapshot": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "p-limit": "^3.1.0",
+        "pretty-format": "^29.5.0",
+        "pure-rand": "^6.0.0",
+        "slash": "^3.0.0",
+        "stack-utils": "^2.0.3"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-cli": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz",
+      "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==",
+      "dev": true,
+      "dependencies": {
+        "@jest/core": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "chalk": "^4.0.0",
+        "exit": "^0.1.2",
+        "graceful-fs": "^4.2.9",
+        "import-local": "^3.0.2",
+        "jest-config": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-validate": "^29.5.0",
+        "prompts": "^2.0.1",
+        "yargs": "^17.3.1"
+      },
+      "bin": {
+        "jest": "bin/jest.js"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jest-config": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz",
+      "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.11.6",
+        "@jest/test-sequencer": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "babel-jest": "^29.5.0",
+        "chalk": "^4.0.0",
+        "ci-info": "^3.2.0",
+        "deepmerge": "^4.2.2",
+        "glob": "^7.1.3",
+        "graceful-fs": "^4.2.9",
+        "jest-circus": "^29.5.0",
+        "jest-environment-node": "^29.5.0",
+        "jest-get-type": "^29.4.3",
+        "jest-regex-util": "^29.4.3",
+        "jest-resolve": "^29.5.0",
+        "jest-runner": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-validate": "^29.5.0",
+        "micromatch": "^4.0.4",
+        "parse-json": "^5.2.0",
+        "pretty-format": "^29.5.0",
+        "slash": "^3.0.0",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "@types/node": "*",
+        "ts-node": ">=9.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "ts-node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jest-diff": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz",
+      "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.0.0",
+        "diff-sequences": "^29.4.3",
+        "jest-get-type": "^29.4.3",
+        "pretty-format": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-docblock": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz",
+      "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==",
+      "dev": true,
+      "dependencies": {
+        "detect-newline": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-each": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz",
+      "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "chalk": "^4.0.0",
+        "jest-get-type": "^29.4.3",
+        "jest-util": "^29.5.0",
+        "pretty-format": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-environment-node": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz",
+      "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==",
+      "dev": true,
+      "dependencies": {
+        "@jest/environment": "^29.5.0",
+        "@jest/fake-timers": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "jest-mock": "^29.5.0",
+        "jest-util": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-get-type": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz",
+      "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==",
+      "dev": true,
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-haste-map": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz",
+      "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "@types/graceful-fs": "^4.1.3",
+        "@types/node": "*",
+        "anymatch": "^3.0.3",
+        "fb-watchman": "^2.0.0",
+        "graceful-fs": "^4.2.9",
+        "jest-regex-util": "^29.4.3",
+        "jest-util": "^29.5.0",
+        "jest-worker": "^29.5.0",
+        "micromatch": "^4.0.4",
+        "walker": "^1.0.8"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "^2.3.2"
+      }
+    },
+    "node_modules/jest-leak-detector": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz",
+      "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==",
+      "dev": true,
+      "dependencies": {
+        "jest-get-type": "^29.4.3",
+        "pretty-format": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-matcher-utils": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz",
+      "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.0.0",
+        "jest-diff": "^29.5.0",
+        "jest-get-type": "^29.4.3",
+        "pretty-format": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-message-util": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz",
+      "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.12.13",
+        "@jest/types": "^29.5.0",
+        "@types/stack-utils": "^2.0.0",
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.9",
+        "micromatch": "^4.0.4",
+        "pretty-format": "^29.5.0",
+        "slash": "^3.0.0",
+        "stack-utils": "^2.0.3"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-mock": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz",
+      "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "jest-util": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-pnp-resolver": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
+      "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      },
+      "peerDependencies": {
+        "jest-resolve": "*"
+      },
+      "peerDependenciesMeta": {
+        "jest-resolve": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jest-regex-util": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz",
+      "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==",
+      "dev": true,
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-resolve": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz",
+      "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.9",
+        "jest-haste-map": "^29.5.0",
+        "jest-pnp-resolver": "^1.2.2",
+        "jest-util": "^29.5.0",
+        "jest-validate": "^29.5.0",
+        "resolve": "^1.20.0",
+        "resolve.exports": "^2.0.0",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-resolve-dependencies": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz",
+      "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==",
+      "dev": true,
+      "dependencies": {
+        "jest-regex-util": "^29.4.3",
+        "jest-snapshot": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-runner": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz",
+      "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/console": "^29.5.0",
+        "@jest/environment": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "emittery": "^0.13.1",
+        "graceful-fs": "^4.2.9",
+        "jest-docblock": "^29.4.3",
+        "jest-environment-node": "^29.5.0",
+        "jest-haste-map": "^29.5.0",
+        "jest-leak-detector": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-resolve": "^29.5.0",
+        "jest-runtime": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-watcher": "^29.5.0",
+        "jest-worker": "^29.5.0",
+        "p-limit": "^3.1.0",
+        "source-map-support": "0.5.13"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-runner/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/jest-runner/node_modules/source-map-support": {
+      "version": "0.5.13",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
+      "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
+      "dev": true,
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/jest-runtime": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz",
+      "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==",
+      "dev": true,
+      "dependencies": {
+        "@jest/environment": "^29.5.0",
+        "@jest/fake-timers": "^29.5.0",
+        "@jest/globals": "^29.5.0",
+        "@jest/source-map": "^29.4.3",
+        "@jest/test-result": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "cjs-module-lexer": "^1.0.0",
+        "collect-v8-coverage": "^1.0.0",
+        "glob": "^7.1.3",
+        "graceful-fs": "^4.2.9",
+        "jest-haste-map": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-mock": "^29.5.0",
+        "jest-regex-util": "^29.4.3",
+        "jest-resolve": "^29.5.0",
+        "jest-snapshot": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "slash": "^3.0.0",
+        "strip-bom": "^4.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-snapshot": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz",
+      "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.11.6",
+        "@babel/generator": "^7.7.2",
+        "@babel/plugin-syntax-jsx": "^7.7.2",
+        "@babel/plugin-syntax-typescript": "^7.7.2",
+        "@babel/traverse": "^7.7.2",
+        "@babel/types": "^7.3.3",
+        "@jest/expect-utils": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/babel__traverse": "^7.0.6",
+        "@types/prettier": "^2.1.5",
+        "babel-preset-current-node-syntax": "^1.0.0",
+        "chalk": "^4.0.0",
+        "expect": "^29.5.0",
+        "graceful-fs": "^4.2.9",
+        "jest-diff": "^29.5.0",
+        "jest-get-type": "^29.4.3",
+        "jest-matcher-utils": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "natural-compare": "^1.4.0",
+        "pretty-format": "^29.5.0",
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-util": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz",
+      "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "ci-info": "^3.2.0",
+        "graceful-fs": "^4.2.9",
+        "picomatch": "^2.2.3"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-validate": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz",
+      "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "camelcase": "^6.2.0",
+        "chalk": "^4.0.0",
+        "jest-get-type": "^29.4.3",
+        "leven": "^3.1.0",
+        "pretty-format": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-validate/node_modules/camelcase": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+      "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/jest-watcher": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz",
+      "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==",
+      "dev": true,
+      "dependencies": {
+        "@jest/test-result": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "ansi-escapes": "^4.2.1",
+        "chalk": "^4.0.0",
+        "emittery": "^0.13.1",
+        "jest-util": "^29.5.0",
+        "string-length": "^4.0.1"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-worker": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz",
+      "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*",
+        "jest-util": "^29.5.0",
+        "merge-stream": "^2.0.0",
+        "supports-color": "^8.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-worker/node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/jmespath": {
+      "version": "0.16.0",
+      "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
+      "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/joi": {
+      "version": "17.9.1",
+      "resolved": "https://registry.npmjs.org/joi/-/joi-17.9.1.tgz",
+      "integrity": "sha512-FariIi9j6QODKATGBrEX7HZcja8Bsh3rfdGYy/Sb65sGlZWK/QWesU1ghk7aJWDj95knjXlQfSmzFSPPkLVsfw==",
+      "dependencies": {
+        "@hapi/hoek": "^9.0.0",
+        "@hapi/topo": "^5.0.0",
+        "@sideway/address": "^4.1.3",
+        "@sideway/formula": "^3.0.1",
+        "@sideway/pinpoint": "^2.0.0"
+      }
+    },
+    "node_modules/js-git": {
+      "version": "0.7.8",
+      "resolved": "https://registry.npmjs.org/js-git/-/js-git-0.7.8.tgz",
+      "integrity": "sha512-+E5ZH/HeRnoc/LW0AmAyhU+mNcWBzAKE+30+IDMLSLbbK+Tdt02AdkOKq9u15rlJsDEGFqtgckc8ZM59LhhiUA==",
+      "dependencies": {
+        "bodec": "^0.1.0",
+        "culvert": "^0.1.2",
+        "git-sha1": "^0.1.2",
+        "pako": "^0.2.5"
+      }
+    },
+    "node_modules/js-git/node_modules/pako": {
+      "version": "0.2.9",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+      "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="
+    },
+    "node_modules/js-sdsl": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
+      "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==",
+      "dev": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/js-sdsl"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/jsesc": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+      "dev": true,
+      "bin": {
+        "jsesc": "bin/jsesc"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/json-bigint": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+      "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+      "dependencies": {
+        "bignumber.js": "^9.0.0"
+      }
+    },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "dev": true
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "dev": true
+    },
+    "node_modules/json-stringify-safe": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+      "optional": true
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/jsonc-parser": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
+      "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
+      "dev": true
+    },
+    "node_modules/jsonfile": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+      "dev": true,
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/jsonwebtoken": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
+      "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
+      "dependencies": {
+        "jws": "^3.2.2",
+        "lodash": "^4.17.21",
+        "ms": "^2.1.1",
+        "semver": "^7.3.8"
+      },
+      "engines": {
+        "node": ">=12",
+        "npm": ">=6"
+      }
+    },
+    "node_modules/jsonwebtoken/node_modules/jwa": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+      "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+      "dependencies": {
+        "buffer-equal-constant-time": "1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/jsonwebtoken/node_modules/jws": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+      "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+      "dependencies": {
+        "jwa": "^1.4.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/jszip": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+      "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+      "dependencies": {
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
+      }
+    },
+    "node_modules/jwa": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+      "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+      "dependencies": {
+        "buffer-equal-constant-time": "1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/jws": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+      "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+      "dependencies": {
+        "jwa": "^2.0.0",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/kleur": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+      "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/kuler": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+      "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
+    },
+    "node_modules/lazy": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz",
+      "integrity": "sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==",
+      "engines": {
+        "node": ">=0.2.0"
+      }
+    },
+    "node_modules/lazystream": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
+      "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+      "dependencies": {
+        "readable-stream": "^2.0.5"
+      },
+      "engines": {
+        "node": ">= 0.6.3"
+      }
+    },
+    "node_modules/leven": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+      "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/libphonenumber-js": {
+      "version": "1.10.24",
+      "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.24.tgz",
+      "integrity": "sha512-3Dk8f5AmrcWqg+oHhmm9hwSTqpWHBdSqsHmjCJGroULFubi0+x7JEIGmRZCuL3TI8Tx39xaKqfnhsDQ4ALa/Nw=="
+    },
+    "node_modules/lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "dependencies": {
+        "immediate": "~3.0.5"
+      }
+    },
+    "node_modules/lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+      "dev": true
+    },
+    "node_modules/listenercount": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz",
+      "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ=="
+    },
+    "node_modules/loader-runner": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+      "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.11.5"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+    },
+    "node_modules/lodash.defaults": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+      "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
+    },
+    "node_modules/lodash.difference": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
+      "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA=="
+    },
+    "node_modules/lodash.escaperegexp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
+      "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw=="
+    },
+    "node_modules/lodash.flatten": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+      "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="
+    },
+    "node_modules/lodash.get": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+      "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
+      "dev": true
+    },
+    "node_modules/lodash.groupby": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz",
+      "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw=="
+    },
+    "node_modules/lodash.isboolean": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+      "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
+    },
+    "node_modules/lodash.isequal": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+      "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
+    },
+    "node_modules/lodash.isfunction": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
+      "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw=="
+    },
+    "node_modules/lodash.isnil": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz",
+      "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng=="
+    },
+    "node_modules/lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+    },
+    "node_modules/lodash.isundefined": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz",
+      "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA=="
+    },
+    "node_modules/lodash.memoize": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+      "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+      "dev": true
+    },
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
+    },
+    "node_modules/lodash.mergewith": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+      "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+      "dev": true
+    },
+    "node_modules/lodash.union": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
+      "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw=="
+    },
+    "node_modules/lodash.uniq": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+      "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="
+    },
+    "node_modules/log-driver": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz",
+      "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==",
+      "engines": {
+        "node": ">=0.8.6"
+      }
+    },
+    "node_modules/log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/logform": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz",
+      "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==",
+      "dependencies": {
+        "@colors/colors": "1.5.0",
+        "@types/triple-beam": "^1.3.2",
+        "fecha": "^4.2.0",
+        "ms": "^2.1.1",
+        "safe-stable-stringify": "^2.3.1",
+        "triple-beam": "^1.3.0"
+      }
+    },
+    "node_modules/long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+    },
+    "node_modules/lru_map": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
+      "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ=="
+    },
+    "node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/macos-release": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz",
+      "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.29.0",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.29.0.tgz",
+      "integrity": "sha512-WcfidHrDjMY+eLjlU+8OvwREqHwpgCeKVBUpQ3OhYYuvfaYCUgcbuBzappNzZvg/v8onU3oQj+BYpkOJe9Iw4Q==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.4.13"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/make-dir": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+      "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+      "dependencies": {
+        "semver": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/make-dir/node_modules/semver": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+      "dev": true
+    },
+    "node_modules/makeerror": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
+      "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
+      "dev": true,
+      "dependencies": {
+        "tmpl": "1.0.5"
+      }
+    },
+    "node_modules/media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/memfs": {
+      "version": "3.4.13",
+      "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz",
+      "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==",
+      "dev": true,
+      "dependencies": {
+        "fs-monkey": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
+    },
+    "node_modules/merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true
+    },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "devOptional": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/method-override": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz",
+      "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==",
+      "dependencies": {
+        "debug": "3.1.0",
+        "methods": "~1.1.2",
+        "parseurl": "~1.3.2",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/method-override/node_modules/debug": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+      "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/method-override/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+      "devOptional": true,
+      "dependencies": {
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/minimalistic-assert": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/minipass": {
+      "version": "4.2.5",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz",
+      "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minizlib": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+      "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+      "dependencies": {
+        "minipass": "^3.0.0",
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/minizlib/node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/mkdirp": {
+      "version": "0.5.6",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+      "dependencies": {
+        "minimist": "^1.2.6"
+      },
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      }
+    },
+    "node_modules/module-details-from-path": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
+      "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A=="
+    },
+    "node_modules/moment": {
+      "version": "2.29.4",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+      "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+    },
+    "node_modules/multer": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz",
+      "integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==",
+      "deprecated": "Multer 1.x is affected by CVE-2022-24434. This is fixed in v1.4.4-lts.1 which drops support for versions of Node.js before 6. Please upgrade to at least Node.js 6 and version 1.4.4-lts.1 of Multer. If you need support for older versions of Node.js, we are open to accepting patches that would fix the CVE on the main 1.x release line, whilst maintaining compatibility with Node.js 0.10.",
+      "dependencies": {
+        "append-field": "^1.0.0",
+        "busboy": "^0.2.11",
+        "concat-stream": "^1.5.2",
+        "mkdirp": "^0.5.4",
+        "object-assign": "^4.1.1",
+        "on-finished": "^2.3.0",
+        "type-is": "^1.6.4",
+        "xtend": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      }
+    },
+    "node_modules/multer-s3": {
+      "version": "2.10.0",
+      "resolved": "https://registry.npmjs.org/multer-s3/-/multer-s3-2.10.0.tgz",
+      "integrity": "sha512-RZsiqG19C9gE82lB7v8duJ+TMIf70fWYHlIwuNcsanOH1ePBoPXZvboEQxEow9jUkk7WQsuyVA2TgriOuDrVrw==",
+      "dependencies": {
+        "file-type": "^3.3.0",
+        "html-comment-regex": "^1.1.2",
+        "run-parallel": "^1.1.6"
+      }
+    },
+    "node_modules/mute-stream": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+      "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
+    },
+    "node_modules/mysql": {
+      "version": "2.18.1",
+      "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
+      "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
+      "dependencies": {
+        "bignumber.js": "9.0.0",
+        "readable-stream": "2.3.7",
+        "safe-buffer": "5.1.2",
+        "sqlstring": "2.3.1"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mysql/node_modules/bignumber.js": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
+      "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/mysql/node_modules/readable-stream": {
+      "version": "2.3.7",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+      "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/mysql/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "node_modules/mysql2": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
+      "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
+      "dependencies": {
+        "denque": "^2.0.1",
+        "generate-function": "^2.3.1",
+        "iconv-lite": "^0.6.3",
+        "long": "^4.0.0",
+        "lru-cache": "^6.0.0",
+        "named-placeholders": "^1.1.2",
+        "seq-queue": "^0.0.5",
+        "sqlstring": "^2.3.2"
+      },
+      "engines": {
+        "node": ">= 8.0"
+      }
+    },
+    "node_modules/mysql2/node_modules/sqlstring": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
+      "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/named-placeholders": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
+      "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
+      "dependencies": {
+        "lru-cache": "^7.14.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/named-placeholders/node_modules/lru-cache": {
+      "version": "7.18.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+      "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true
+    },
+    "node_modules/natural-compare-lite": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
+      "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
+      "dev": true
+    },
+    "node_modules/needle": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz",
+      "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==",
+      "dependencies": {
+        "debug": "^3.2.6",
+        "iconv-lite": "^0.4.4",
+        "sax": "^1.2.4"
+      },
+      "bin": {
+        "needle": "bin/needle"
+      },
+      "engines": {
+        "node": ">= 4.4.x"
+      }
+    },
+    "node_modules/needle/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "dependencies": {
+        "ms": "^2.1.1"
+      }
+    },
+    "node_modules/needle/node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/needle/node_modules/sax": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+    },
+    "node_modules/negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/neo-async": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+      "dev": true
+    },
+    "node_modules/nest-aws-sdk": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/nest-aws-sdk/-/nest-aws-sdk-3.0.1.tgz",
+      "integrity": "sha512-RtAteM1HpAMTeT4u7KladRlvba4QHYStNB7cNIwBd0TneahkhTxOsdY+s46CaONq6JimBIRM+wsKZ3hvx/RLkA==",
+      "engines": {
+        "node": ">= 10.13.0",
+        "npm": ">= 6.11.0"
+      },
+      "peerDependencies": {
+        "@nestjs/common": ">= 7.0.0",
+        "@nestjs/core": ">= 7.0.0",
+        "aws-sdk": "^2.718.0",
+        "reflect-metadata": "^0.1.13",
+        "rxjs": ">= 6.5.4"
+      }
+    },
+    "node_modules/nest-raven": {
+      "version": "9.2.0",
+      "resolved": "https://registry.npmjs.org/nest-raven/-/nest-raven-9.2.0.tgz",
+      "integrity": "sha512-pI7dBi5Jr7BE5PyuzVTzKYr4R0iJUcPQi1/c/QtHpSwuVMYUEqfvnOY3zYSW5XKvnXmsh+8a7bd0A8QOBooJFw==",
+      "optionalDependencies": {
+        "@nestjs/graphql": "^11.0.0"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^9.0.0",
+        "@sentry/node": "*",
+        "reflect-metadata": "*",
+        "rxjs": "*"
+      }
+    },
+    "node_modules/netmask": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
+      "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/node-abort-controller": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
+      "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
+      "dev": true
+    },
+    "node_modules/node-addon-api": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
+      "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
+    },
+    "node_modules/node-cache": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz",
+      "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==",
+      "dependencies": {
+        "clone": "2.x"
+      },
+      "engines": {
+        "node": ">= 8.0.0"
+      }
+    },
+    "node_modules/node-cache/node_modules/clone": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+      "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/node-cron": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.2.tgz",
+      "integrity": "sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==",
+      "dependencies": {
+        "uuid": "8.3.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/node-cron/node_modules/uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/node-emoji": {
+      "version": "1.11.0",
+      "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz",
+      "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==",
+      "dev": true,
+      "dependencies": {
+        "lodash": "^4.17.21"
+      }
+    },
+    "node_modules/node-fetch": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
+      "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/node-forge": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
+      "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
+      "engines": {
+        "node": ">= 6.13.0"
+      }
+    },
+    "node_modules/node-int64": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+      "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+      "dev": true
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.10",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
+      "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
+      "dev": true
+    },
+    "node_modules/node-sens": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/node-sens/-/node-sens-1.3.0.tgz",
+      "integrity": "sha512-d89Z5mDBLaMfmQYcrIsHcPbKf568cX+ajpOjDywQVyVDhgRxzz5dyGAuEI/1HCB5Fi71eIALsylkxSJ17NTlBA==",
+      "dependencies": {
+        "axios": "^0.19.2",
+        "crypto": "^1.0.1"
+      }
+    },
+    "node_modules/node-sens/node_modules/axios": {
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
+      "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
+      "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410",
+      "dependencies": {
+        "follow-redirects": "1.5.10"
+      }
+    },
+    "node_modules/node-sens/node_modules/debug": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+      "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/node-sens/node_modules/follow-redirects": {
+      "version": "1.5.10",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+      "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+      "dependencies": {
+        "debug": "=3.1.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/node-sens/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/nodemailer": {
+      "version": "6.9.1",
+      "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.1.tgz",
+      "integrity": "sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA==",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/nodemon": {
+      "version": "2.0.21",
+      "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.21.tgz",
+      "integrity": "sha512-djN/n2549DUtY33S7o1djRCd7dEm0kBnj9c7S9XVXqRUbuggN1MZH/Nqa+5RFQr63Fbefq37nFXAE9VU86yL1A==",
+      "dev": true,
+      "dependencies": {
+        "chokidar": "^3.5.2",
+        "debug": "^3.2.7",
+        "ignore-by-default": "^1.0.1",
+        "minimatch": "^3.1.2",
+        "pstree.remy": "^1.1.8",
+        "semver": "^5.7.1",
+        "simple-update-notifier": "^1.0.7",
+        "supports-color": "^5.5.0",
+        "touch": "^3.1.0",
+        "undefsafe": "^2.0.5"
+      },
+      "bin": {
+        "nodemon": "bin/nodemon.js"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/nodemon"
+      }
+    },
+    "node_modules/nodemon/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "dev": true,
+      "dependencies": {
+        "ms": "^2.1.1"
+      }
+    },
+    "node_modules/nodemon/node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/nodemon/node_modules/semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/nodemon/node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/nopt": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+      "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+      "dependencies": {
+        "abbrev": "1"
+      },
+      "bin": {
+        "nopt": "bin/nopt.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npm-run-path": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+      "dev": true,
+      "dependencies": {
+        "path-key": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/npmlog": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
+      "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+      "dependencies": {
+        "are-we-there-yet": "^2.0.0",
+        "console-control-strings": "^1.1.0",
+        "gauge": "^3.0.0",
+        "set-blocking": "^2.0.0"
+      }
+    },
+    "node_modules/nssocket": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.6.0.tgz",
+      "integrity": "sha512-a9GSOIql5IqgWJR3F/JXG4KpJTA3Z53Cj0MeMvGpglytB1nxE4PdFNC0jINe27CS7cGivoynwc054EzCcT3M3w==",
+      "dependencies": {
+        "eventemitter2": "~0.4.14",
+        "lazy": "~1.0.11"
+      },
+      "engines": {
+        "node": ">= 0.10.x"
+      }
+    },
+    "node_modules/nssocket/node_modules/eventemitter2": {
+      "version": "0.4.14",
+      "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
+      "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ=="
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
+    "node_modules/oauth": {
+      "version": "0.9.15",
+      "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
+      "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-hash": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
+      "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.12.3",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
+      "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/on-headers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+      "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/one-time": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
+      "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+      "dependencies": {
+        "fn.name": "1.x.x"
+      }
+    },
+    "node_modules/onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dev": true,
+      "dependencies": {
+        "mimic-fn": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/openapi-types": {
+      "version": "12.1.0",
+      "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz",
+      "integrity": "sha512-XpeCy01X6L5EpP+6Hc3jWN7rMZJ+/k1lwki/kTmWzbVhdPie3jd5O2ZtedEx8Yp58icJ0osVldLMrTB/zslQXA==",
+      "dev": true,
+      "peer": true
+    },
+    "node_modules/optionator": {
+      "version": "0.9.1",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+      "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+      "dev": true,
+      "dependencies": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.3"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/ora": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+      "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+      "dev": true,
+      "dependencies": {
+        "bl": "^4.1.0",
+        "chalk": "^4.1.0",
+        "cli-cursor": "^3.1.0",
+        "cli-spinners": "^2.5.0",
+        "is-interactive": "^1.0.0",
+        "is-unicode-supported": "^0.1.0",
+        "log-symbols": "^4.1.0",
+        "strip-ansi": "^6.0.0",
+        "wcwidth": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/os-name": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz",
+      "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==",
+      "dev": true,
+      "dependencies": {
+        "macos-release": "^2.5.0",
+        "windows-release": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/os-tmpdir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/pac-proxy-agent": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz",
+      "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==",
+      "dependencies": {
+        "@tootallnate/once": "1",
+        "agent-base": "6",
+        "debug": "4",
+        "get-uri": "3",
+        "http-proxy-agent": "^4.0.1",
+        "https-proxy-agent": "5",
+        "pac-resolver": "^5.0.0",
+        "raw-body": "^2.2.0",
+        "socks-proxy-agent": "5"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/pac-resolver": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.1.tgz",
+      "integrity": "sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==",
+      "dependencies": {
+        "degenerator": "^3.0.2",
+        "ip": "^1.1.5",
+        "netmask": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse-json": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/parse5": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+      "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
+    },
+    "node_modules/parse5-htmlparser2-tree-adapter": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+      "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+      "dependencies": {
+        "parse5": "^6.0.1"
+      }
+    },
+    "node_modules/parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/passport": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
+      "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
+      "dependencies": {
+        "passport-strategy": "1.x.x",
+        "pause": "0.0.1",
+        "utils-merge": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/jaredhanson"
+      }
+    },
+    "node_modules/passport-jwt": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz",
+      "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==",
+      "dependencies": {
+        "jsonwebtoken": "^9.0.0",
+        "passport-strategy": "^1.0.0"
+      }
+    },
+    "node_modules/passport-kakao": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz",
+      "integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==",
+      "dependencies": {
+        "passport-oauth2": "~1.1.2",
+        "pkginfo": "~0.3.0"
+      }
+    },
+    "node_modules/passport-local": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
+      "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==",
+      "dependencies": {
+        "passport-strategy": "1.x.x"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/passport-oauth2": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz",
+      "integrity": "sha512-wpsGtJDHHQUjyc9WcV9FFB0bphFExpmKtzkQrxpH1vnSr6RcWa3ZEGHx/zGKAh2PN7Po9TKYB1fJeOiIBspNPA==",
+      "dependencies": {
+        "oauth": "0.9.x",
+        "passport-strategy": "1.x.x",
+        "uid2": "0.0.x"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/passport-strategy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
+      "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+    },
+    "node_modules/path-scurry": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.6.1.tgz",
+      "integrity": "sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA==",
+      "dev": true,
+      "dependencies": {
+        "lru-cache": "^7.14.1",
+        "minipass": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/path-scurry/node_modules/lru-cache": {
+      "version": "7.18.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+      "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/path-to-regexp": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz",
+      "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA=="
+    },
+    "node_modules/path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pause": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+      "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
+    },
+    "node_modules/picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+      "dev": true
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/pidusage": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-3.0.2.tgz",
+      "integrity": "sha512-g0VU+y08pKw5M8EZ2rIGiEBaB8wrQMjYGFfW2QVIfyT8V+fq8YFLkvlz4bz5ljvFDJYNFCWT3PWqcRr2FKO81w==",
+      "dependencies": {
+        "safe-buffer": "^5.2.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/pirates": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
+      "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/pkg-dir": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+      "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+      "dev": true,
+      "dependencies": {
+        "find-up": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pkg-dir/node_modules/find-up": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "^5.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pkg-dir/node_modules/locate-path": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pkg-dir/node_modules/p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "dev": true,
+      "dependencies": {
+        "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/pkg-dir/node_modules/p-locate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pkginfo": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz",
+      "integrity": "sha512-yO5feByMzAp96LtP58wvPKSbaKAi/1C4kV9XpTctr6EepnP6F33RBNOiVrdz9BrPA98U2BMFsTNHo44TWcbQ2A==",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/pluralize": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
+      "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/pm2": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/pm2/-/pm2-5.3.0.tgz",
+      "integrity": "sha512-xscmQiAAf6ArVmKhjKTeeN8+Td7ZKnuZFFPw1DGkdFPR/0Iyx+m+1+OpCdf9+HQopX3VPc9/wqPQHqVOfHum9w==",
+      "dependencies": {
+        "@pm2/agent": "~2.0.0",
+        "@pm2/io": "~5.0.0",
+        "@pm2/js-api": "~0.6.7",
+        "@pm2/pm2-version-check": "latest",
+        "async": "~3.2.0",
+        "blessed": "0.1.81",
+        "chalk": "3.0.0",
+        "chokidar": "^3.5.3",
+        "cli-tableau": "^2.0.0",
+        "commander": "2.15.1",
+        "croner": "~4.1.92",
+        "dayjs": "~1.11.5",
+        "debug": "^4.3.1",
+        "enquirer": "2.3.6",
+        "eventemitter2": "5.0.1",
+        "fclone": "1.0.11",
+        "mkdirp": "1.0.4",
+        "needle": "2.4.0",
+        "pidusage": "~3.0",
+        "pm2-axon": "~4.0.1",
+        "pm2-axon-rpc": "~0.7.1",
+        "pm2-deploy": "~1.0.2",
+        "pm2-multimeter": "^0.1.2",
+        "promptly": "^2",
+        "semver": "^7.2",
+        "source-map-support": "0.5.21",
+        "sprintf-js": "1.1.2",
+        "vizion": "~2.2.1",
+        "yamljs": "0.3.0"
+      },
+      "bin": {
+        "pm2": "bin/pm2",
+        "pm2-dev": "bin/pm2-dev",
+        "pm2-docker": "bin/pm2-docker",
+        "pm2-runtime": "bin/pm2-runtime"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "optionalDependencies": {
+        "pm2-sysmonit": "^1.2.8"
+      }
+    },
+    "node_modules/pm2-axon": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pm2-axon/-/pm2-axon-4.0.1.tgz",
+      "integrity": "sha512-kES/PeSLS8orT8dR5jMlNl+Yu4Ty3nbvZRmaAtROuVm9nYYGiaoXqqKQqQYzWQzMYWUKHMQTvBlirjE5GIIxqg==",
+      "dependencies": {
+        "amp": "~0.3.1",
+        "amp-message": "~0.1.1",
+        "debug": "^4.3.1",
+        "escape-string-regexp": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=5"
+      }
+    },
+    "node_modules/pm2-axon-rpc": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/pm2-axon-rpc/-/pm2-axon-rpc-0.7.1.tgz",
+      "integrity": "sha512-FbLvW60w+vEyvMjP/xom2UPhUN/2bVpdtLfKJeYM3gwzYhoTEEChCOICfFzxkxuoEleOlnpjie+n1nue91bDQw==",
+      "dependencies": {
+        "debug": "^4.3.1"
+      },
+      "engines": {
+        "node": ">=5"
+      }
+    },
+    "node_modules/pm2-deploy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/pm2-deploy/-/pm2-deploy-1.0.2.tgz",
+      "integrity": "sha512-YJx6RXKrVrWaphEYf++EdOOx9EH18vM8RSZN/P1Y+NokTKqYAca/ejXwVLyiEpNju4HPZEk3Y2uZouwMqUlcgg==",
+      "dependencies": {
+        "run-series": "^1.1.8",
+        "tv4": "^1.3.0"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/pm2-multimeter": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz",
+      "integrity": "sha512-S+wT6XfyKfd7SJIBqRgOctGxaBzUOmVQzTAS+cg04TsEUObJVreha7lvCfX8zzGVr871XwCSnHUU7DQQ5xEsfA==",
+      "dependencies": {
+        "charm": "~0.1.1"
+      }
+    },
+    "node_modules/pm2-sysmonit": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/pm2-sysmonit/-/pm2-sysmonit-1.2.8.tgz",
+      "integrity": "sha512-ACOhlONEXdCTVwKieBIQLSi2tQZ8eKinhcr9JpZSUAL8Qy0ajIgRtsLxG/lwPOW3JEKqPyw/UaHmTWhUzpP4kA==",
+      "optional": true,
+      "dependencies": {
+        "async": "^3.2.0",
+        "debug": "^4.3.1",
+        "pidusage": "^2.0.21",
+        "systeminformation": "^5.7",
+        "tx2": "~1.0.4"
+      }
+    },
+    "node_modules/pm2-sysmonit/node_modules/pidusage": {
+      "version": "2.0.21",
+      "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz",
+      "integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==",
+      "optional": true,
+      "dependencies": {
+        "safe-buffer": "^5.2.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pm2/node_modules/chalk": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+      "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pm2/node_modules/commander": {
+      "version": "2.15.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+      "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
+    },
+    "node_modules/pm2/node_modules/mkdirp": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/pm2/node_modules/sprintf-js": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
+      "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/prettier": {
+      "version": "2.8.5",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.5.tgz",
+      "integrity": "sha512-3gzuxrHbKUePRBB4ZeU08VNkUcqEHaUaouNt0m7LGP4Hti/NuB07C7PPTM/LkWqXoJYJn2McEo5+kxPNrtQkLQ==",
+      "dev": true,
+      "bin": {
+        "prettier": "bin-prettier.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/prettier-linter-helpers": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+      "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+      "dev": true,
+      "dependencies": {
+        "fast-diff": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/pretty-format": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
+      "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
+      "dev": true,
+      "dependencies": {
+        "@jest/schemas": "^29.4.3",
+        "ansi-styles": "^5.0.0",
+        "react-is": "^18.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/pretty-format/node_modules/ansi-styles": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/prisma": {
+      "version": "4.12.0",
+      "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.12.0.tgz",
+      "integrity": "sha512-xqVper4mbwl32BWzLpdznHAYvYDWQQWK2tBfXjdUD397XaveRyAP7SkBZ6kFlIg8kKayF4hvuaVtYwXd9BodAg==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "@prisma/engines": "4.12.0"
+      },
+      "bin": {
+        "prisma": "build/index.js",
+        "prisma2": "build/index.js"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
+    "node_modules/promptly": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/promptly/-/promptly-2.2.0.tgz",
+      "integrity": "sha512-aC9j+BZsRSSzEsXBNBwDnAxujdx19HycZoKgRgzWnS8eOHg1asuf9heuLprfbe739zY3IdUQx+Egv6Jn135WHA==",
+      "dependencies": {
+        "read": "^1.0.4"
+      }
+    },
+    "node_modules/prompts": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+      "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+      "dev": true,
+      "dependencies": {
+        "kleur": "^3.0.3",
+        "sisteransi": "^1.0.5"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "dependencies": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/proxy-agent": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz",
+      "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==",
+      "dependencies": {
+        "agent-base": "^6.0.0",
+        "debug": "4",
+        "http-proxy-agent": "^4.0.0",
+        "https-proxy-agent": "^5.0.0",
+        "lru-cache": "^5.1.1",
+        "pac-proxy-agent": "^5.0.0",
+        "proxy-from-env": "^1.0.0",
+        "socks-proxy-agent": "^5.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/proxy-agent/node_modules/lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "dependencies": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "node_modules/proxy-agent/node_modules/yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "node_modules/pstree.remy": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+      "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+      "dev": true
+    },
+    "node_modules/pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "dev": true,
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+      "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/pure-rand": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz",
+      "integrity": "sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/dubzzz"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fast-check"
+        }
+      ]
+    },
+    "node_modules/qs": {
+      "version": "6.11.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+      "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+      "dependencies": {
+        "side-channel": "^1.0.4"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/querystring": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+      "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
+      "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
+      "engines": {
+        "node": ">=0.4.x"
+      }
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/random-bytes": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
+      "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "dependencies": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/raw-body": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+      "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/raw-body/node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/react-is": {
+      "version": "18.2.0",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+      "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
+      "dev": true
+    },
+    "node_modules/read": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
+      "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==",
+      "dependencies": {
+        "mute-stream": "~0.0.4"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/readable-stream/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "node_modules/readdir-glob": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.2.tgz",
+      "integrity": "sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==",
+      "dependencies": {
+        "minimatch": "^5.1.0"
+      }
+    },
+    "node_modules/readdir-glob/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/readdir-glob/node_modules/minimatch": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/readline": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz",
+      "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg=="
+    },
+    "node_modules/rechoir": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+      "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
+      "dev": true,
+      "dependencies": {
+        "resolve": "^1.1.6"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/redis": {
+      "version": "4.6.5",
+      "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.5.tgz",
+      "integrity": "sha512-O0OWA36gDQbswOdUuAhRL6mTZpHFN525HlgZgDaVNgCJIAZR3ya06NTESb0R+TUZ+BFaDpz6NnnVvoMx9meUFg==",
+      "dependencies": {
+        "@redis/bloom": "1.2.0",
+        "@redis/client": "1.5.6",
+        "@redis/graph": "1.1.0",
+        "@redis/json": "1.0.4",
+        "@redis/search": "1.1.2",
+        "@redis/time-series": "1.0.4"
+      }
+    },
+    "node_modules/reflect-metadata": {
+      "version": "0.1.13",
+      "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
+      "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
+    },
+    "node_modules/regex-email": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/regex-email/-/regex-email-1.0.2.tgz",
+      "integrity": "sha512-xW1jrpke+llgHpKaSlxesiLg1KklZ0V72KnUdKmdWMjoHqz40+cXIaWAv1CYKm96RhXVEFXgGeyb/YbslS0NwA=="
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/require-in-the-middle": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz",
+      "integrity": "sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==",
+      "dependencies": {
+        "debug": "^4.1.1",
+        "module-details-from-path": "^1.0.3",
+        "resolve": "^1.22.1"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/resolve": {
+      "version": "1.22.1",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+      "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+      "dependencies": {
+        "is-core-module": "^2.9.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/resolve-cwd": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+      "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+      "dev": true,
+      "dependencies": {
+        "resolve-from": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/resolve-cwd/node_modules/resolve-from": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+      "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/resolve.exports": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.1.tgz",
+      "integrity": "sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/restore-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+      "dev": true,
+      "dependencies": {
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+      "devOptional": true,
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rimraf": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.0.tgz",
+      "integrity": "sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ==",
+      "dev": true,
+      "dependencies": {
+        "glob": "^9.2.0"
+      },
+      "bin": {
+        "rimraf": "dist/cjs/src/bin.js"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/rimraf/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/rimraf/node_modules/glob": {
+      "version": "9.3.1",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.1.tgz",
+      "integrity": "sha512-qERvJb7IGsnkx6YYmaaGvDpf77c951hICMdWaFXyH3PlVob8sbPJJyJX0kWkiCWyXUzoy9UOTNjGg0RbD8bYIw==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "minimatch": "^7.4.1",
+        "minipass": "^4.2.4",
+        "path-scurry": "^1.6.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/rimraf/node_modules/minimatch": {
+      "version": "7.4.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.2.tgz",
+      "integrity": "sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/run-async": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+      "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "node_modules/run-series": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/run-series/-/run-series-1.1.9.tgz",
+      "integrity": "sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/rxjs": {
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz",
+      "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/safe-stable-stringify": {
+      "version": "2.4.3",
+      "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz",
+      "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "node_modules/sax": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
+      "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="
+    },
+    "node_modules/saxes": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
+      "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
+      "dependencies": {
+        "xmlchars": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/schema-utils": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+      "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+      "dev": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.8",
+        "ajv": "^6.12.5",
+        "ajv-keywords": "^3.5.2"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/schema-utils/node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/schema-utils/node_modules/ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+      "dev": true,
+      "peerDependencies": {
+        "ajv": "^6.9.1"
+      }
+    },
+    "node_modules/schema-utils/node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "node_modules/semver": {
+      "version": "7.3.8",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+      "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+      "dependencies": {
+        "lru-cache": "^6.0.0"
+      },
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/send": {
+      "version": "0.18.0",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+      "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+      "dependencies": {
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "mime": "1.6.0",
+        "ms": "2.1.3",
+        "on-finished": "2.4.1",
+        "range-parser": "~1.2.1",
+        "statuses": "2.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/send/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/send/node_modules/debug/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/send/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+    },
+    "node_modules/seq-queue": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+      "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
+    },
+    "node_modules/serialize-javascript": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
+      "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
+      "dev": true,
+      "dependencies": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "node_modules/serve-static": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+      "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+      "dependencies": {
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.18.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
+    },
+    "node_modules/setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shelljs": {
+      "version": "0.8.5",
+      "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
+      "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
+      "dev": true,
+      "dependencies": {
+        "glob": "^7.0.0",
+        "interpret": "^1.0.0",
+        "rechoir": "^0.6.2"
+      },
+      "bin": {
+        "shjs": "bin/shjs"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/shimmer": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
+      "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="
+    },
+    "node_modules/should": {
+      "version": "13.2.3",
+      "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz",
+      "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==",
+      "dev": true,
+      "dependencies": {
+        "should-equal": "^2.0.0",
+        "should-format": "^3.0.3",
+        "should-type": "^1.4.0",
+        "should-type-adaptors": "^1.0.1",
+        "should-util": "^1.0.0"
+      }
+    },
+    "node_modules/should-equal": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz",
+      "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==",
+      "dev": true,
+      "dependencies": {
+        "should-type": "^1.4.0"
+      }
+    },
+    "node_modules/should-format": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz",
+      "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==",
+      "dev": true,
+      "dependencies": {
+        "should-type": "^1.3.0",
+        "should-type-adaptors": "^1.0.1"
+      }
+    },
+    "node_modules/should-http": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/should-http/-/should-http-0.1.1.tgz",
+      "integrity": "sha512-KIMXUn2Anf5WALjcr2d5DRyWHsu7VoiXIbU6sojG5UkaLNBJRtA/T10t/3T8CnykuY7kOZj7zn39iQ+dhm2LQw==",
+      "dev": true,
+      "dependencies": {
+        "content-type": "^1.0.2"
+      },
+      "peerDependencies": {
+        "should": ">= 4.x"
+      }
+    },
+    "node_modules/should-type": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz",
+      "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==",
+      "dev": true
+    },
+    "node_modules/should-type-adaptors": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz",
+      "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==",
+      "dev": true,
+      "dependencies": {
+        "should-type": "^1.3.0",
+        "should-util": "^1.0.0"
+      }
+    },
+    "node_modules/should-util": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz",
+      "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==",
+      "dev": true
+    },
+    "node_modules/side-channel": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+      "dependencies": {
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+    },
+    "node_modules/simple-swizzle": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+      "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+      "dependencies": {
+        "is-arrayish": "^0.3.1"
+      }
+    },
+    "node_modules/simple-swizzle/node_modules/is-arrayish": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+      "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+    },
+    "node_modules/simple-update-notifier": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz",
+      "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==",
+      "dev": true,
+      "dependencies": {
+        "semver": "~7.0.0"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/simple-update-notifier/node_modules/semver": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
+      "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/sisteransi": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+      "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+      "dev": true
+    },
+    "node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/smart-buffer": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+      "engines": {
+        "node": ">= 6.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/socks": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
+      "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
+      "dependencies": {
+        "ip": "^2.0.0",
+        "smart-buffer": "^4.2.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/socks-proxy-agent": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz",
+      "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==",
+      "dependencies": {
+        "agent-base": "^6.0.2",
+        "debug": "4",
+        "socks": "^2.3.3"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/socks/node_modules/ip": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
+      "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
+    },
+    "node_modules/source-map": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+      "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/source-map-support/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/sourcemap-codec": {
+      "version": "1.4.8",
+      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+      "deprecated": "Please use @jridgewell/sourcemap-codec instead",
+      "dev": true
+    },
+    "node_modules/spawn-command": {
+      "version": "0.0.2-1",
+      "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
+      "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg=="
+    },
+    "node_modules/sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
+    },
+    "node_modules/sqlstring": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
+      "integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/stack-trace": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+      "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/stack-utils": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
+      "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+      "dev": true,
+      "dependencies": {
+        "escape-string-regexp": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/stack-utils/node_modules/escape-string-regexp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+      "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/statuses": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+      "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/streamsearch": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+      "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==",
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "node_modules/string_decoder/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "node_modules/string-length": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
+      "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+      "dev": true,
+      "dependencies": {
+        "char-regex": "^1.0.2",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-bom": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
+      "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-final-newline": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/strnum": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
+      "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
+    },
+    "node_modules/subscriptions-transport-ws": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz",
+      "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==",
+      "deprecated": "The `subscriptions-transport-ws` package is no longer maintained. We recommend you use `graphql-ws` instead. For help migrating Apollo software to `graphql-ws`, see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws    For general help using `graphql-ws`, see https://github.com/enisdenjo/graphql-ws/blob/master/README.md",
+      "optional": true,
+      "dependencies": {
+        "backo2": "^1.0.2",
+        "eventemitter3": "^3.1.0",
+        "iterall": "^1.2.1",
+        "symbol-observable": "^1.0.4",
+        "ws": "^5.2.0 || ^6.0.0 || ^7.0.0"
+      },
+      "peerDependencies": {
+        "graphql": "^15.7.2 || ^16.0.0"
+      }
+    },
+    "node_modules/subscriptions-transport-ws/node_modules/symbol-observable": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+      "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
+      "optional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/superagent": {
+      "version": "8.0.9",
+      "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz",
+      "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==",
+      "dev": true,
+      "dependencies": {
+        "component-emitter": "^1.3.0",
+        "cookiejar": "^2.1.4",
+        "debug": "^4.3.4",
+        "fast-safe-stringify": "^2.1.1",
+        "form-data": "^4.0.0",
+        "formidable": "^2.1.2",
+        "methods": "^1.1.2",
+        "mime": "2.6.0",
+        "qs": "^6.11.0",
+        "semver": "^7.3.8"
+      },
+      "engines": {
+        "node": ">=6.4.0 <13 || >=14"
+      }
+    },
+    "node_modules/superagent/node_modules/mime": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+      "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+      "dev": true,
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/supertest": {
+      "version": "6.3.3",
+      "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz",
+      "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==",
+      "dev": true,
+      "dependencies": {
+        "methods": "^1.1.2",
+        "superagent": "^8.0.5"
+      },
+      "engines": {
+        "node": ">=6.4.0"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/swagger-jsdoc": {
+      "version": "6.2.8",
+      "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz",
+      "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==",
+      "dev": true,
+      "dependencies": {
+        "commander": "6.2.0",
+        "doctrine": "3.0.0",
+        "glob": "7.1.6",
+        "lodash.mergewith": "^4.6.2",
+        "swagger-parser": "^10.0.3",
+        "yaml": "2.0.0-1"
+      },
+      "bin": {
+        "swagger-jsdoc": "bin/swagger-jsdoc.js"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/swagger-jsdoc/node_modules/commander": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz",
+      "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/swagger-jsdoc/node_modules/glob": {
+      "version": "7.1.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/swagger-jsdoc/node_modules/yaml": {
+      "version": "2.0.0-1",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz",
+      "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/swagger-parser": {
+      "version": "10.0.3",
+      "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz",
+      "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==",
+      "dev": true,
+      "dependencies": {
+        "@apidevtools/swagger-parser": "10.0.3"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/swagger-ui-dist": {
+      "version": "4.18.2",
+      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.18.2.tgz",
+      "integrity": "sha512-oVBoBl9Dg+VJw8uRWDxlyUyHoNEDC0c1ysT6+Boy6CTgr2rUcLcfPon4RvxgS2/taNW6O0+US+Z/dlAsWFjOAQ=="
+    },
+    "node_modules/swagger-ui-express": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.2.tgz",
+      "integrity": "sha512-MHIOaq9JrTTB3ygUJD+08PbjM5Tt/q7x80yz9VTFIatw8j5uIWKcr90S0h5NLMzFEDC6+eVprtoeA5MDZXCUKQ==",
+      "dependencies": {
+        "swagger-ui-dist": ">=4.11.0"
+      },
+      "engines": {
+        "node": ">= v0.10.32"
+      },
+      "peerDependencies": {
+        "express": ">=4.0.0"
+      }
+    },
+    "node_modules/symbol-observable": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
+      "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/systeminformation": {
+      "version": "5.17.12",
+      "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.17.12.tgz",
+      "integrity": "sha512-I3pfMW2vue53u+X08BNxaJieaHkRoMMKjWetY9lbYJeWFaeWPO6P4FkNc4XOCX8F9vbQ0HqQ25RJoz3U/B7liw==",
+      "optional": true,
+      "os": [
+        "darwin",
+        "linux",
+        "win32",
+        "freebsd",
+        "openbsd",
+        "netbsd",
+        "sunos",
+        "android"
+      ],
+      "bin": {
+        "systeminformation": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      },
+      "funding": {
+        "type": "Buy me a coffee",
+        "url": "https://www.buymeacoffee.com/systeminfo"
+      }
+    },
+    "node_modules/tapable": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+      "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/tar": {
+      "version": "6.1.13",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz",
+      "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==",
+      "dependencies": {
+        "chownr": "^2.0.0",
+        "fs-minipass": "^2.0.0",
+        "minipass": "^4.0.0",
+        "minizlib": "^2.1.1",
+        "mkdirp": "^1.0.3",
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/tar-stream": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+      "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+      "dependencies": {
+        "bl": "^4.0.3",
+        "end-of-stream": "^1.4.1",
+        "fs-constants": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^3.1.1"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/tar-stream/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/tar/node_modules/mkdirp": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/terser": {
+      "version": "5.16.6",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.6.tgz",
+      "integrity": "sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/source-map": "^0.3.2",
+        "acorn": "^8.5.0",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      },
+      "bin": {
+        "terser": "bin/terser"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/terser-webpack-plugin": {
+      "version": "5.3.7",
+      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz",
+      "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.17",
+        "jest-worker": "^27.4.5",
+        "schema-utils": "^3.1.1",
+        "serialize-javascript": "^6.0.1",
+        "terser": "^5.16.5"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.1.0"
+      },
+      "peerDependenciesMeta": {
+        "@swc/core": {
+          "optional": true
+        },
+        "esbuild": {
+          "optional": true
+        },
+        "uglify-js": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/terser-webpack-plugin/node_modules/jest-worker": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+      "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*",
+        "merge-stream": "^2.0.0",
+        "supports-color": "^8.0.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      }
+    },
+    "node_modules/terser-webpack-plugin/node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/terser/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true
+    },
+    "node_modules/test-exclude": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+      "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+      "dev": true,
+      "dependencies": {
+        "@istanbuljs/schema": "^0.1.2",
+        "glob": "^7.1.4",
+        "minimatch": "^3.0.4"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/text-hex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+      "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
+    },
+    "node_modules/text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+      "dev": true
+    },
+    "node_modules/through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+      "dev": true
+    },
+    "node_modules/tmp": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+      "dev": true,
+      "dependencies": {
+        "os-tmpdir": "~1.0.2"
+      },
+      "engines": {
+        "node": ">=0.6.0"
+      }
+    },
+    "node_modules/tmpl": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
+      "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
+      "dev": true
+    },
+    "node_modules/to-fast-properties": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/touch": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
+      "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
+      "dev": true,
+      "dependencies": {
+        "nopt": "~1.0.10"
+      },
+      "bin": {
+        "nodetouch": "bin/nodetouch.js"
+      }
+    },
+    "node_modules/touch/node_modules/nopt": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+      "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
+      "dev": true,
+      "dependencies": {
+        "abbrev": "1"
+      },
+      "bin": {
+        "nopt": "bin/nopt.js"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+    },
+    "node_modules/traverse": {
+      "version": "0.3.9",
+      "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
+      "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/tree-kill": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+      "bin": {
+        "tree-kill": "cli.js"
+      }
+    },
+    "node_modules/triple-beam": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
+      "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
+    },
+    "node_modules/ts-jest": {
+      "version": "29.0.3",
+      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.3.tgz",
+      "integrity": "sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==",
+      "dev": true,
+      "dependencies": {
+        "bs-logger": "0.x",
+        "fast-json-stable-stringify": "2.x",
+        "jest-util": "^29.0.0",
+        "json5": "^2.2.1",
+        "lodash.memoize": "4.x",
+        "make-error": "1.x",
+        "semver": "7.x",
+        "yargs-parser": "^21.0.1"
+      },
+      "bin": {
+        "ts-jest": "cli.js"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "@babel/core": ">=7.0.0-beta.0 <8",
+        "@jest/types": "^29.0.0",
+        "babel-jest": "^29.0.0",
+        "jest": "^29.0.0",
+        "typescript": ">=4.3"
+      },
+      "peerDependenciesMeta": {
+        "@babel/core": {
+          "optional": true
+        },
+        "@jest/types": {
+          "optional": true
+        },
+        "babel-jest": {
+          "optional": true
+        },
+        "esbuild": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/ts-loader": {
+      "version": "9.4.2",
+      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz",
+      "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "enhanced-resolve": "^5.0.0",
+        "micromatch": "^4.0.0",
+        "semver": "^7.3.4"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "typescript": "*",
+        "webpack": "^5.0.0"
+      }
+    },
+    "node_modules/ts-node": {
+      "version": "10.9.1",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
+      "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+      "dev": true,
+      "dependencies": {
+        "@cspotcode/source-map-support": "^0.8.0",
+        "@tsconfig/node10": "^1.0.7",
+        "@tsconfig/node12": "^1.0.7",
+        "@tsconfig/node14": "^1.0.0",
+        "@tsconfig/node16": "^1.0.2",
+        "acorn": "^8.4.1",
+        "acorn-walk": "^8.1.1",
+        "arg": "^4.1.0",
+        "create-require": "^1.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "v8-compile-cache-lib": "^3.0.1",
+        "yn": "3.1.1"
+      },
+      "bin": {
+        "ts-node": "dist/bin.js",
+        "ts-node-cwd": "dist/bin-cwd.js",
+        "ts-node-esm": "dist/bin-esm.js",
+        "ts-node-script": "dist/bin-script.js",
+        "ts-node-transpile-only": "dist/bin-transpile.js",
+        "ts-script": "dist/bin-script-deprecated.js"
+      },
+      "peerDependencies": {
+        "@swc/core": ">=1.2.50",
+        "@swc/wasm": ">=1.2.50",
+        "@types/node": "*",
+        "typescript": ">=2.7"
+      },
+      "peerDependenciesMeta": {
+        "@swc/core": {
+          "optional": true
+        },
+        "@swc/wasm": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/tsconfig-paths": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.1.tgz",
+      "integrity": "sha512-VgPrtLKpRgEAJsMj5Q/I/mXouC6A/7eJ/X4Nuk6o0cRPwBtznYxTCU4FodbexbzH9somBPEXYi0ZkUViUpJ21Q==",
+      "dev": true,
+      "dependencies": {
+        "json5": "^2.2.1",
+        "minimist": "^1.2.6",
+        "strip-bom": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/tsconfig-paths-webpack-plugin": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.1.tgz",
+      "integrity": "sha512-m5//KzLoKmqu2MVix+dgLKq70MnFi8YL8sdzQZ6DblmCdfuq/y3OqvJd5vMndg2KEVCOeNz8Es4WVZhYInteLw==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "enhanced-resolve": "^5.7.0",
+        "tsconfig-paths": "^4.1.2"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/tsconfig-paths-webpack-plugin/node_modules/strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/tsconfig-paths-webpack-plugin/node_modules/tsconfig-paths": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz",
+      "integrity": "sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw==",
+      "dev": true,
+      "dependencies": {
+        "json5": "^2.2.2",
+        "minimist": "^1.2.6",
+        "strip-bom": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/tsconfig-paths/node_modules/strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
+      "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
+    },
+    "node_modules/tsutils": {
+      "version": "3.21.0",
+      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+      "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "^1.8.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      },
+      "peerDependencies": {
+        "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+      }
+    },
+    "node_modules/tsutils/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+      "dev": true
+    },
+    "node_modules/tv4": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz",
+      "integrity": "sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw==",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/tx2": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/tx2/-/tx2-1.0.5.tgz",
+      "integrity": "sha512-sJ24w0y03Md/bxzK4FU8J8JveYYUbSs2FViLJ2D/8bytSiyPRbuE3DyL/9UKYXTZlV3yXq0L8GLlhobTnekCVg==",
+      "optional": true,
+      "dependencies": {
+        "json-stringify-safe": "^5.0.1"
+      }
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/type-detect": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+      "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/type-fest": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "dependencies": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
+    },
+    "node_modules/typescript": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+      "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+      "dev": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=4.2.0"
+      }
+    },
+    "node_modules/uid": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.1.tgz",
+      "integrity": "sha512-PF+1AnZgycpAIEmNtjxGBVmKbZAQguaa4pBUq6KNaGEcpzZ2klCNZLM34tsjp76maN00TttiiUf6zkIBpJQm2A==",
+      "dependencies": {
+        "@lukeed/csprng": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/uid-safe": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
+      "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
+      "dependencies": {
+        "random-bytes": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/uid2": {
+      "version": "0.0.4",
+      "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz",
+      "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA=="
+    },
+    "node_modules/undefsafe": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+      "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+      "dev": true
+    },
+    "node_modules/underscore": {
+      "version": "1.13.3",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.3.tgz",
+      "integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA=="
+    },
+    "node_modules/universalify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+      "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/unzipper": {
+      "version": "0.10.11",
+      "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz",
+      "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==",
+      "dependencies": {
+        "big-integer": "^1.6.17",
+        "binary": "~0.3.0",
+        "bluebird": "~3.4.1",
+        "buffer-indexof-polyfill": "~1.0.0",
+        "duplexer2": "~0.1.4",
+        "fstream": "^1.0.12",
+        "graceful-fs": "^4.2.2",
+        "listenercount": "~1.0.1",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "~1.0.4"
+      }
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+      "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        }
+      ],
+      "dependencies": {
+        "escalade": "^3.1.1",
+        "picocolors": "^1.0.0"
+      },
+      "bin": {
+        "browserslist-lint": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/url": {
+      "version": "0.10.3",
+      "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
+      "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==",
+      "dependencies": {
+        "punycode": "1.3.2",
+        "querystring": "0.2.0"
+      }
+    },
+    "node_modules/url-template": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
+      "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="
+    },
+    "node_modules/url/node_modules/punycode": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+      "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw=="
+    },
+    "node_modules/urlsafe-base64": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz",
+      "integrity": "sha512-RtuPeMy7c1UrHwproMZN9gN6kiZ0SvJwRaEzwZY0j9MypEkFqyBaKv176jvlPtg58Zh36bOkS0NFABXMHvvGCA=="
+    },
+    "node_modules/util": {
+      "version": "0.12.5",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+      "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "is-arguments": "^1.0.4",
+        "is-generator-function": "^1.0.7",
+        "is-typed-array": "^1.1.3",
+        "which-typed-array": "^1.1.2"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+    },
+    "node_modules/utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/uuid": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz",
+      "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/v8-compile-cache-lib": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+      "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+      "dev": true
+    },
+    "node_modules/v8-to-istanbul": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz",
+      "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.12",
+        "@types/istanbul-lib-coverage": "^2.0.1",
+        "convert-source-map": "^1.6.0"
+      },
+      "engines": {
+        "node": ">=10.12.0"
+      }
+    },
+    "node_modules/v8-to-istanbul/node_modules/convert-source-map": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+      "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+      "dev": true
+    },
+    "node_modules/validator": {
+      "version": "13.9.0",
+      "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz",
+      "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/value-or-promise": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz",
+      "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==",
+      "optional": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/vizion": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/vizion/-/vizion-2.2.1.tgz",
+      "integrity": "sha512-sfAcO2yeSU0CSPFI/DmZp3FsFE9T+8913nv1xWBOyzODv13fwkn6Vl7HqxGpkr9F608M+8SuFId3s+BlZqfXww==",
+      "dependencies": {
+        "async": "^2.6.3",
+        "git-node-fs": "^1.0.0",
+        "ini": "^1.3.5",
+        "js-git": "^0.7.8"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/vizion/node_modules/async": {
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+      "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+      "dependencies": {
+        "lodash": "^4.17.14"
+      }
+    },
+    "node_modules/vm2": {
+      "version": "3.9.14",
+      "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.14.tgz",
+      "integrity": "sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==",
+      "dependencies": {
+        "acorn": "^8.7.0",
+        "acorn-walk": "^8.2.0"
+      },
+      "bin": {
+        "vm2": "bin/vm2"
+      },
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/walker": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
+      "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
+      "dev": true,
+      "dependencies": {
+        "makeerror": "1.0.12"
+      }
+    },
+    "node_modules/watchpack": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
+      "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
+      "dev": true,
+      "dependencies": {
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.1.2"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/wcwidth": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+      "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+      "dev": true,
+      "dependencies": {
+        "defaults": "^1.0.3"
+      }
+    },
+    "node_modules/web-push": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/web-push/-/web-push-3.5.0.tgz",
+      "integrity": "sha512-JC0V9hzKTqlDYJ+LTZUXtW7B175qwwaqzbbMSWDxHWxZvd3xY0C2rcotMGDavub2nAAFw+sXTsqR65/KY2A5AQ==",
+      "dependencies": {
+        "asn1.js": "^5.3.0",
+        "http_ece": "1.1.0",
+        "https-proxy-agent": "^5.0.0",
+        "jws": "^4.0.0",
+        "minimist": "^1.2.5",
+        "urlsafe-base64": "^1.0.0"
+      },
+      "bin": {
+        "web-push": "src/cli.js"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+    },
+    "node_modules/webpack": {
+      "version": "5.76.2",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.2.tgz",
+      "integrity": "sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==",
+      "dev": true,
+      "dependencies": {
+        "@types/eslint-scope": "^3.7.3",
+        "@types/estree": "^0.0.51",
+        "@webassemblyjs/ast": "1.11.1",
+        "@webassemblyjs/wasm-edit": "1.11.1",
+        "@webassemblyjs/wasm-parser": "1.11.1",
+        "acorn": "^8.7.1",
+        "acorn-import-assertions": "^1.7.6",
+        "browserslist": "^4.14.5",
+        "chrome-trace-event": "^1.0.2",
+        "enhanced-resolve": "^5.10.0",
+        "es-module-lexer": "^0.9.0",
+        "eslint-scope": "5.1.1",
+        "events": "^3.2.0",
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.2.9",
+        "json-parse-even-better-errors": "^2.3.1",
+        "loader-runner": "^4.2.0",
+        "mime-types": "^2.1.27",
+        "neo-async": "^2.6.2",
+        "schema-utils": "^3.1.0",
+        "tapable": "^2.1.1",
+        "terser-webpack-plugin": "^5.1.3",
+        "watchpack": "^2.4.0",
+        "webpack-sources": "^3.2.3"
+      },
+      "bin": {
+        "webpack": "bin/webpack.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependenciesMeta": {
+        "webpack-cli": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webpack-node-externals": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz",
+      "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/webpack-sources": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+      "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+      "dev": true,
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/which-typed-array": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
+      "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
+      "dependencies": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "gopd": "^1.0.1",
+        "has-tostringtag": "^1.0.0",
+        "is-typed-array": "^1.1.10"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/wide-align": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
+      "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
+      "dependencies": {
+        "string-width": "^1.0.2 || 2 || 3 || 4"
+      }
+    },
+    "node_modules/windows-release": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz",
+      "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==",
+      "dev": true,
+      "dependencies": {
+        "execa": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/windows-release/node_modules/execa": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+      "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+      "dev": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.0",
+        "get-stream": "^5.0.0",
+        "human-signals": "^1.1.1",
+        "is-stream": "^2.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^4.0.0",
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2",
+        "strip-final-newline": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/windows-release/node_modules/get-stream": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+      "dev": true,
+      "dependencies": {
+        "pump": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/windows-release/node_modules/human-signals": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
+      "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.12.0"
+      }
+    },
+    "node_modules/winston": {
+      "version": "3.8.2",
+      "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz",
+      "integrity": "sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==",
+      "dependencies": {
+        "@colors/colors": "1.5.0",
+        "@dabh/diagnostics": "^2.0.2",
+        "async": "^3.2.3",
+        "is-stream": "^2.0.0",
+        "logform": "^2.4.0",
+        "one-time": "^1.0.0",
+        "readable-stream": "^3.4.0",
+        "safe-stable-stringify": "^2.3.1",
+        "stack-trace": "0.0.x",
+        "triple-beam": "^1.3.0",
+        "winston-transport": "^4.5.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/winston-daily-rotate-file": {
+      "version": "4.7.1",
+      "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.7.1.tgz",
+      "integrity": "sha512-7LGPiYGBPNyGHLn9z33i96zx/bd71pjBn9tqQzO3I4Tayv94WPmBNwKC7CO1wPHdP9uvu+Md/1nr6VSH9h0iaA==",
+      "dependencies": {
+        "file-stream-rotator": "^0.6.1",
+        "object-hash": "^2.0.1",
+        "triple-beam": "^1.3.0",
+        "winston-transport": "^4.4.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "peerDependencies": {
+        "winston": "^3"
+      }
+    },
+    "node_modules/winston-transport": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz",
+      "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==",
+      "dependencies": {
+        "logform": "^2.3.2",
+        "readable-stream": "^3.6.0",
+        "triple-beam": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 6.4.0"
+      }
+    },
+    "node_modules/winston-transport/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/winston/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+    },
+    "node_modules/write-file-atomic": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
+      "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
+      "dev": true,
+      "dependencies": {
+        "imurmurhash": "^0.1.4",
+        "signal-exit": "^3.0.7"
+      },
+      "engines": {
+        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+      }
+    },
+    "node_modules/ws": {
+      "version": "7.4.6",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+      "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
+      "engines": {
+        "node": ">=8.3.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/xml2js": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
+      "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
+      "dependencies": {
+        "sax": ">=0.6.0",
+        "xmlbuilder": "~11.0.0"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/xmlbuilder": {
+      "version": "11.0.1",
+      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+      "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/xmlchars": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+      "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
+    },
+    "node_modules/xregexp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz",
+      "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+    },
+    "node_modules/yaml": {
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+      "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/yamljs": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz",
+      "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==",
+      "dependencies": {
+        "argparse": "^1.0.7",
+        "glob": "^7.0.5"
+      },
+      "bin": {
+        "json2yaml": "bin/json2yaml",
+        "yaml2json": "bin/yaml2json"
+      }
+    },
+    "node_modules/yamljs/node_modules/argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dependencies": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "node_modules/yargs": {
+      "version": "17.7.1",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
+      "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
+      "dev": true,
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/z-schema": {
+      "version": "5.0.5",
+      "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz",
+      "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==",
+      "dev": true,
+      "dependencies": {
+        "lodash.get": "^4.4.2",
+        "lodash.isequal": "^4.5.0",
+        "validator": "^13.7.0"
+      },
+      "bin": {
+        "z-schema": "bin/z-schema"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "commander": "^9.4.1"
+      }
+    },
+    "node_modules/z-schema/node_modules/commander": {
+      "version": "9.5.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
+      "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+      "dev": true,
+      "optional": true,
+      "engines": {
+        "node": "^12.20.0 || >=14"
+      }
+    },
+    "node_modules/zip-stream": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz",
+      "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==",
+      "dependencies": {
+        "archiver-utils": "^2.1.0",
+        "compress-commons": "^4.1.0",
+        "readable-stream": "^3.6.0"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/zip-stream/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..abd79e2
--- /dev/null
+++ b/package.json
@@ -0,0 +1,136 @@
+{
+  "name": "engineeo-server",
+  "version": "0.0.1",
+  "description": "",
+  "author": "",
+  "private": true,
+  "license": "UNLICENSED",
+  "scripts": {
+    "build": "nest build",
+    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
+    "start": "nest start",
+    "start:dev": "cross-env NODE_ENV=dev nest start --watch ",
+    "start:debug": "cross-env NODE_ENV=dev nest start --debug --watch",
+    "start:prod": "pm2-runtime -i max dist/src/main.js",
+    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
+    "test": "jest",
+    "test:watch": "jest --watch",
+    "test:cov": "jest --coverage",
+    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
+    "test:e2e": "jest --config ./test/jest-e2e.json"
+  },
+  "dependencies": {
+    "@aws-sdk/client-s3": "^3.309.0",
+    "@aws-sdk/types": "^3.310.0",
+    "@nestjs/common": "^9.0.0",
+    "@nestjs/config": "^2.3.1",
+    "@nestjs/core": "^9.4.0",
+    "@nestjs/jwt": "^10.0.2",
+    "@nestjs/passport": "^9.0.3",
+    "@nestjs/platform-express": "^9.0.0",
+    "@nestjs/swagger": "^6.3.0",
+    "@prisma/client": "^4.13.0",
+    "@sentry/node": "^7.47.0",
+    "@sentry/tracing": "^7.47.0",
+    "@types/aws-sdk": "^2.7.0",
+    "aws-sdk": "^2.1369.0",
+    "axios": "^0.21.4",
+    "bcrypt": "^5.0.1",
+    "body-parser": "^1.19.0",
+    "cheerio": "1.0.0-rc.10",
+    "class-transformer": "^0.5.1",
+    "class-validator": "^0.14.0",
+    "compression": "^1.7.4",
+    "concurrently": "^6.0.2",
+    "connect-redis": "^6.1.3",
+    "cookie-parser": "^1.4.6",
+    "cors": "^2.8.5",
+    "cross-env": "^7.0.3",
+    "crypto": "^1.0.1",
+    "crypto-js": "^4.0.0",
+    "dotenv": "^15.0.1",
+    "dotenv-cli": "^7.1.0",
+    "exceljs": "^4.3.0",
+    "express": "^4.17.1",
+    "express-mysql-session": "^2.1.8",
+    "express-session": "^1.17.3",
+    "form-data": "^4.0.0",
+    "google-auth-library": "^7.3.0",
+    "googleapis": "^89.0.0",
+    "iconv-lite": "^0.6.3",
+    "joi": "^17.9.1",
+    "jsonwebtoken": "^9.0.0",
+    "lru-cache": "^6.0.0",
+    "method-override": "^3.0.0",
+    "multer": "^1.4.3",
+    "multer-s3": "^2.10.0",
+    "mysql2": "^2.2.5",
+    "nest-aws-sdk": "^3.0.1",
+    "nest-raven": "^9.2.0",
+    "node-cache": "^5.1.2",
+    "node-cron": "^3.0.0",
+    "node-sens": "^1.0.1",
+    "nodemailer": "^6.6.2",
+    "passport": "^0.6.0",
+    "passport-jwt": "^4.0.1",
+    "passport-kakao": "^1.0.1",
+    "passport-local": "^1.0.0",
+    "pm2": "^5.1.0",
+    "prisma": "^4.12.0",
+    "readline": "^1.3.0",
+    "redis": "^4.3.1",
+    "reflect-metadata": "^0.1.13",
+    "regex-email": "^1.0.2",
+    "rxjs": "^7.2.0",
+    "swagger-ui-express": "^4.6.2",
+    "web-push": "^3.4.5",
+    "winston": "^3.3.3",
+    "winston-daily-rotate-file": "^4.5.5"
+  },
+  "devDependencies": {
+    "@nestjs/cli": "^9.0.0",
+    "@nestjs/schematics": "^9.0.0",
+    "@nestjs/testing": "^9.4.0",
+    "@types/express": "^4.17.17",
+    "@types/jest": "29.2.4",
+    "@types/node": "18.11.18",
+    "@types/passport-jwt": "^3.0.8",
+    "@types/passport-local": "^1.0.35",
+    "@types/supertest": "^2.0.11",
+    "@typescript-eslint/eslint-plugin": "^5.0.0",
+    "@typescript-eslint/parser": "^5.0.0",
+    "eslint": "^8.0.1",
+    "eslint-config-prettier": "^8.3.0",
+    "eslint-plugin-prettier": "^4.0.0",
+    "jest": "29.3.1",
+    "nodemon": "^2.0.20",
+    "prettier": "^2.3.2",
+    "should": "^13.2.3",
+    "should-http": "^0.1.1",
+    "source-map-support": "^0.5.20",
+    "supertest": "^6.1.6",
+    "swagger-jsdoc": "^6.1.0",
+    "ts-jest": "29.0.3",
+    "ts-loader": "^9.2.3",
+    "ts-node": "^10.0.0",
+    "tsconfig-paths": "4.1.1",
+    "typescript": "^4.7.4"
+  },
+  "jest": {
+    "moduleFileExtensions": [
+      "js",
+      "json",
+      "ts"
+    ],
+    "rootDir": "src",
+    "testRegex": ".*\\.spec\\.ts$",
+    "transform": {
+      "^.+\\.(t|j)s$": "ts-jest"
+    },
+    "collectCoverageFrom": [
+      "**/*.(t|j)s"
+    ],
+    "coverageDirectory": "../coverage",
+    "testEnvironment": "node"
+  }
+}
diff --git a/prisma/migrations/0_init/migration.sql b/prisma/migrations/0_init/migration.sql
new file mode 100644
index 0000000..1bc95a9
--- /dev/null
+++ b/prisma/migrations/0_init/migration.sql
@@ -0,0 +1,978 @@
+-- CreateTable
+CREATE TABLE `AuthDetail` (
+    `authDetailIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userAuthIdx` INTEGER NOT NULL,
+    `examDetailIdx` INTEGER NOT NULL,
+    `isUse` INTEGER NOT NULL DEFAULT 0,
+
+    INDEX `fk_AuthDetail_examDetailIdx`(`examDetailIdx`),
+    INDEX `fk_AuthDetail_userAuthIdx`(`userAuthIdx`),
+    PRIMARY KEY (`authDetailIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `AuthorizationLog` (
+    `authorizationLogIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `adminIdx` INTEGER NOT NULL,
+    `userAuthIdx` INTEGER NOT NULL,
+
+    INDEX `AuthorizationLog_fk`(`userAuthIdx`),
+    PRIMARY KEY (`authorizationLogIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `Board` (
+    `boardIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `boardSort` VARCHAR(45) NOT NULL,
+    `boardTitle` VARCHAR(80) NOT NULL,
+    `boardContent` TEXT NOT NULL,
+    `readCount` INTEGER NOT NULL DEFAULT 1,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    INDEX `fk_Board_userIdx`(`userIdx`),
+    PRIMARY KEY (`boardIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `BoardComment` (
+    `boardCommentIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `boardIdx` INTEGER NOT NULL,
+    `userIdx` INTEGER NOT NULL,
+    `content` VARCHAR(500) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    INDEX `fk_BoardComment_boardIdx`(`boardIdx`),
+    INDEX `fk_BoardComment_userIdx`(`userIdx`),
+    PRIMARY KEY (`boardCommentIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `BoardImage` (
+    `boardImageIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `boardIdx` INTEGER NOT NULL,
+    `boardImageUrl` VARCHAR(500) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    INDEX `fk_BoardImage_boardIdx`(`boardIdx`),
+    PRIMARY KEY (`boardImageIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `BoardLike` (
+    `boardLikeIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `boardIdx` INTEGER NOT NULL,
+    `userIdx` INTEGER NOT NULL,
+    `isLike` TINYINT NOT NULL DEFAULT 1,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    INDEX `fk_BoardLike_boardIdx`(`boardIdx`),
+    INDEX `fk_BoardLike_userIdx`(`userIdx`),
+    PRIMARY KEY (`boardLikeIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `Category` (
+    `categoryIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `categoryRef` INTEGER NULL,
+    `categoryName` VARCHAR(45) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `categoryType` CHAR(2) NOT NULL,
+
+    INDEX `fk_Category_categoryRef`(`categoryRef`),
+    PRIMARY KEY (`categoryIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `CompanyExam` (
+    `companyExamIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `examIdx` INTEGER NOT NULL,
+    `domain` VARCHAR(45) NOT NULL,
+    `education` VARCHAR(45) NOT NULL,
+    `examSortIdx_S` INTEGER NOT NULL,
+    `companyName` VARCHAR(45) NOT NULL,
+    `category` VARCHAR(45) NULL,
+    `companyUrl` VARCHAR(200) NULL,
+    `prio` INTEGER NOT NULL DEFAULT 0,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+
+    PRIMARY KEY (`companyExamIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `CompanyExam_old` (
+    `companyExamIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `examIdx` INTEGER NOT NULL,
+    `position` VARCHAR(45) NOT NULL,
+    `education` VARCHAR(45) NOT NULL,
+
+    INDEX `fk_CompanyExam_examIdx`(`examIdx`),
+    PRIMARY KEY (`companyExamIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `CompanyInfo` (
+    `examSortIdx` INTEGER NOT NULL,
+    `companyName` VARCHAR(45) NOT NULL,
+    `companySortLabel` INTEGER NOT NULL DEFAULT 0,
+    `prio` INTEGER NULL DEFAULT 0,
+
+    UNIQUE INDEX `examSortIdx_UNIQUE`(`examSortIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `CorrectAnswerRate` (
+    `correctAnswerRateIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `multipleProblemIdx` INTEGER NOT NULL,
+    `userIdx` INTEGER NOT NULL,
+    `isCorrect` CHAR(1) NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+
+    INDEX `fk_CorrectAnswerRate_multipleProblemIdx`(`multipleProblemIdx`),
+    INDEX `fk_CorrectAnswerRate_userIdx`(`userIdx`),
+    PRIMARY KEY (`correctAnswerRateIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `Coupon` (
+    `couponIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `couponName` VARCHAR(45) NOT NULL,
+    `duration` INTEGER NULL,
+    `couponPrice` VARCHAR(45) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `couponCode` VARCHAR(45) NULL,
+
+    PRIMARY KEY (`couponIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `CouponProduct` (
+    `couponIdx` INTEGER NOT NULL,
+    `productIdx` INTEGER NOT NULL,
+
+    INDEX `fk_CouponProduct_productIdx`(`productIdx`),
+    PRIMARY KEY (`couponIdx`, `productIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `CustomProblem` (
+    `customProblemIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `restoreExamDetailIdx` INTEGER NOT NULL,
+    `problem` VARCHAR(500) NULL,
+    `comment` VARCHAR(200) NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `problemNum` INTEGER NULL,
+
+    INDEX `fk_CustomProblem_restoreExamDetailIdx`(`restoreExamDetailIdx`),
+    INDEX `fk_CustomProblem_userIdx`(`userIdx`),
+    PRIMARY KEY (`customProblemIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `CustomProblemImage` (
+    `customProblemImageIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `customProblemIdx` INTEGER NOT NULL,
+    `imageUrl` VARCHAR(500) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    INDEX `fk_CustomProblemImage_customProblemIdx`(`customProblemIdx`),
+    PRIMARY KEY (`customProblemImageIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `CustomQuestion` (
+    `customQuestionIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `customProblemIdx` INTEGER NOT NULL,
+    `questionNum` TINYINT NOT NULL,
+    `question` VARCHAR(100) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    INDEX `fk_CustomQuestion_customProblemIdx`(`customProblemIdx`),
+    PRIMARY KEY (`customQuestionIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `Enterprise` (
+    `enterpriseIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `enterpriseName` CHAR(20) NOT NULL,
+    `recruitment` VARCHAR(1500) NOT NULL,
+    `link` VARCHAR(1500) NOT NULL,
+    `content` VARCHAR(1000) NOT NULL,
+    `startDate` DATE NOT NULL,
+    `endDate` DATE NOT NULL,
+    `examDate` VARCHAR(200) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `image` VARCHAR(500) NULL,
+    `essentialElement` VARCHAR(500) NULL,
+    `extraElement` VARCHAR(500) NULL,
+    `etcElement` VARCHAR(500) NULL,
+    `interviewDate` VARCHAR(200) NULL,
+    `thirdLanguage` VARCHAR(45) NULL,
+
+    PRIMARY KEY (`enterpriseIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `Exam` (
+    `examIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `examName` VARCHAR(45) NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `passScore` INTEGER NOT NULL,
+    `examSortIdx` INTEGER NULL,
+    `timeLimit` INTEGER NOT NULL,
+    `problemCount` INTEGER NOT NULL,
+    `accessLevel` TINYINT NOT NULL DEFAULT 0,
+    `examUrl` VARCHAR(500) NULL,
+    `questionType` VARCHAR(10) NULL,
+    `subjectiveLabel` TINYINT NOT NULL DEFAULT 0,
+    `isDelete` TINYINT NOT NULL DEFAULT 0,
+    `isBody` TINYINT NULL,
+    `prio` INTEGER NULL DEFAULT 0,
+
+    PRIMARY KEY (`examIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ExamAdmissionTicket` (
+    `examAdmissionTicketIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `restoreExamDetailIdx` INTEGER NOT NULL,
+    `examAdmissionTicketImage` VARCHAR(500) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `isAuthentication` TINYINT NOT NULL DEFAULT 1,
+
+    INDEX `fk_ExamAdmissionTicke_restoreExamDetailIdx`(`restoreExamDetailIdx`),
+    INDEX `fk_ExamAdmissionTicket_userIdx`(`userIdx`),
+    PRIMARY KEY (`examAdmissionTicketIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ExamDetail` (
+    `examDetailIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `examIdx` INTEGER NOT NULL,
+    `examDate` DATE NOT NULL,
+    `examRound` VARCHAR(45) NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `isPublic` TINYINT NOT NULL DEFAULT 0,
+    `publicLevel` TINYINT NOT NULL DEFAULT 2,
+    `examDetailUrl` VARCHAR(500) NULL,
+
+    INDEX `fk_ExamDetail_examIdx`(`examIdx`),
+    PRIMARY KEY (`examDetailIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ExamSort` (
+    `examSortIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `examSortRef` INTEGER NULL,
+    `examSortName` VARCHAR(45) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `examSortType` CHAR(2) NULL,
+
+    PRIMARY KEY (`examSortIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ExamSortImage` (
+    `examSortImageIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `examSortIdx` INTEGER NOT NULL,
+    `examSortUrl` VARCHAR(500) NOT NULL,
+
+    UNIQUE INDEX `examSortIdx_UNIQUE`(`examSortIdx`),
+    PRIMARY KEY (`examSortImageIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ExamSort_old` (
+    `examSortIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `examSortRef` INTEGER NULL,
+    `examSortName` VARCHAR(45) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `examSortType` CHAR(2) NULL,
+
+    INDEX `fk_ExamSort_examSortRef`(`examSortRef`),
+    PRIMARY KEY (`examSortIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ExamSubject` (
+    `examSubjectIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `examSubjectName` VARCHAR(45) NOT NULL,
+    `passScore` INTEGER NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `problemCount` TINYINT NULL,
+
+    PRIMARY KEY (`examSubjectIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ExamSubjectMulti` (
+    `examIdx` INTEGER NOT NULL,
+    `examSubjectIdx` VARCHAR(45) NOT NULL,
+    `examSubjectNum` INTEGER NOT NULL,
+
+    INDEX `fk_examIdx`(`examIdx`),
+    INDEX `fk_examSubjectIdx`(`examSubjectIdx`),
+    PRIMARY KEY (`examIdx`, `examSubjectIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `Exam_old` (
+    `examIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `examName` VARCHAR(45) NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `passScore` INTEGER NOT NULL,
+    `examSortIdx` INTEGER NOT NULL,
+    `timeLimit` INTEGER NOT NULL,
+    `problemCount` INTEGER NOT NULL,
+    `accessLevel` TINYINT NOT NULL DEFAULT 0,
+    `examUrl` VARCHAR(500) NULL,
+    `questionType` VARCHAR(10) NULL,
+    `subjectiveLabel` TINYINT NOT NULL DEFAULT 0,
+    `isDelete` TINYINT NOT NULL DEFAULT 0,
+    `isNcs` TINYINT NOT NULL DEFAULT 0,
+    `prio` INTEGER NULL DEFAULT 0,
+
+    INDEX `fk_Exam_examSort`(`examSortIdx`),
+    PRIMARY KEY (`examIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `MultipleProblem` (
+    `multipleProblemIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `problem` TEXT NOT NULL,
+    `provisionNum` VARCHAR(45) NULL,
+    `problemImage` VARCHAR(1000) NULL,
+    `solution` TEXT NOT NULL,
+    `isKatex` TINYINT NOT NULL DEFAULT 1,
+    `problemScore` TINYINT NULL DEFAULT 5,
+    `lectureUrl` VARCHAR(1000) NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `isDelete` TINYINT NOT NULL DEFAULT 0,
+    `examHistory` VARCHAR(500) NULL,
+    `problemUrl` VARCHAR(1000) NULL,
+
+    FULLTEXT INDEX `multipleProblem_fulltext_index`(`problem`),
+    FULLTEXT INDEX `multipleProblem_fultext_solution`(`solution`),
+    PRIMARY KEY (`multipleProblemIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `Payment` (
+    `paymentIdx` VARCHAR(45) NOT NULL,
+    `userIdx` INTEGER NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `level` CHAR(30) NOT NULL DEFAULT 'unverified',
+    `pay_method` VARCHAR(45) NULL,
+    `receipt_url` VARCHAR(300) NULL,
+    `imp_uid` VARCHAR(45) NULL,
+    `status` CHAR(1) NULL DEFAULT 'N',
+    `totalPrice` INTEGER NOT NULL,
+    `paymentName` VARCHAR(45) NOT NULL,
+    `totalPoint` INTEGER NOT NULL DEFAULT 0,
+    `checkSum` INTEGER NOT NULL,
+    `paidAt` TIMESTAMP(0) NULL,
+    `refundAt` TIMESTAMP(0) NULL,
+    `totalCoupon` INTEGER NULL DEFAULT 0,
+    `pointConsumed` INTEGER NULL DEFAULT 0,
+
+    INDEX `fk_Payment_userIdx`(`userIdx`),
+    PRIMARY KEY (`paymentIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `PaymentAddress` (
+    `paymentAddressIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `paymentIdx` VARCHAR(45) NOT NULL,
+    `recipient` VARCHAR(45) NOT NULL,
+    `phoneNum` INTEGER NOT NULL,
+    `postcode` VARCHAR(6) NOT NULL,
+    `address` VARCHAR(300) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    PRIMARY KEY (`paymentAddressIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `PaymentProduct` (
+    `paymentProductIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `productIdx` INTEGER NOT NULL,
+    `paymentIdx` VARCHAR(45) NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `usePoint` INTEGER NOT NULL DEFAULT 0,
+    `useCoupon` INTEGER NULL,
+    `finalPrice` INTEGER NOT NULL,
+    `refundPrice` INTEGER NULL,
+    `refundReason` VARCHAR(45) NULL,
+    `refundAt` TIMESTAMP(0) NULL,
+    `pg_tid` VARCHAR(45) NULL,
+    `receipt_url` VARCHAR(300) NULL,
+    `isRefund` TINYINT NOT NULL DEFAULT 0,
+
+    INDEX `fk_PaymentProduct_paymentIdx`(`paymentIdx`),
+    INDEX `fk_PaymentProduct_productIdx`(`productIdx`),
+    PRIMARY KEY (`paymentProductIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `Point` (
+    `pointIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `pointName` VARCHAR(45) NOT NULL,
+    `point` INTEGER NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    PRIMARY KEY (`pointIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `PointLog` (
+    `pointLogIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `content` VARCHAR(45) NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `point` INTEGER NOT NULL,
+    `pointType` CHAR(1) NOT NULL DEFAULT 'S',
+
+    INDEX `fk_PointLog_userIdx`(`userIdx`),
+    PRIMARY KEY (`pointLogIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ProblemCategory` (
+    `multipleProblemIdx` INTEGER NOT NULL,
+    `categoryIdx` INTEGER NOT NULL,
+
+    INDEX `fk_categoryIdx`(`categoryIdx`),
+    INDEX `fk_multipleProblemIdx`(`multipleProblemIdx`),
+    PRIMARY KEY (`multipleProblemIdx`, `categoryIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ProblemError` (
+    `problemErrorIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `multipleProblemIdx` INTEGER NOT NULL,
+    `title` VARCHAR(45) NOT NULL,
+    `content` VARCHAR(500) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `solution` VARCHAR(2000) NULL,
+    `type` INTEGER NULL DEFAULT 0,
+    `subjectiveLabel` BOOLEAN NULL DEFAULT false,
+
+    INDEX `fk_ProblemError_multipleProblemIdx`(`multipleProblemIdx`),
+    INDEX `fk_ProblemError_userIdx`(`userIdx`),
+    PRIMARY KEY (`problemErrorIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ProblemExam` (
+    `examDetailIdx` INTEGER NOT NULL,
+    `multipleProblemIdx` INTEGER NOT NULL,
+    `problemNum` INTEGER NULL,
+
+    INDEX `fk_ProblemExam_examDetailIdx`(`examDetailIdx`),
+    INDEX `fk_ProblemExam_multipleProblemIdx`(`multipleProblemIdx`),
+    PRIMARY KEY (`examDetailIdx`, `multipleProblemIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ProblemSubject` (
+    `multipleProblemIdx` INTEGER NOT NULL,
+    `examSubjectIdx` INTEGER NOT NULL,
+
+    INDEX `fk_examSubjectIdx`(`examSubjectIdx`),
+    INDEX `fk_multipleProblemIdx`(`multipleProblemIdx`),
+    PRIMARY KEY (`multipleProblemIdx`, `examSubjectIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `Product` (
+    `productIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `productName` VARCHAR(100) NULL,
+    `productThumbnail` VARCHAR(450) NULL,
+    `price` INTEGER NULL,
+    `discountPrice` INTEGER NULL,
+    `shortDescription` VARCHAR(500) NULL,
+    `generalDescription` TEXT NULL,
+    `detailDescription` TEXT NULL,
+    `duration` INTEGER NULL,
+    `depth` INTEGER NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `prio` INTEGER NULL DEFAULT 0,
+
+    PRIMARY KEY (`productIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ProductExamDetail` (
+    `productIdx` INTEGER NOT NULL,
+    `examDetailIdx` INTEGER NOT NULL,
+
+    INDEX `PED.fk_examDetailIdx_idx`(`examDetailIdx`),
+    INDEX `PED.fk_productIdx_idx`(`productIdx`),
+    PRIMARY KEY (`productIdx`, `examDetailIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ProductImage` (
+    `productImageIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `productIdx` INTEGER NOT NULL,
+    `productImage` VARCHAR(500) NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    INDEX `fk_ProductImage_productIdx`(`productIdx`),
+    PRIMARY KEY (`productImageIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ProductRelation` (
+    `productIdx` INTEGER NOT NULL,
+    `parentIdx` INTEGER NOT NULL,
+    `productTypeIdx` INTEGER NOT NULL,
+
+    PRIMARY KEY (`productIdx`, `parentIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ProductType` (
+    `productTypeIdx` INTEGER NOT NULL,
+    `examField` VARCHAR(45) NOT NULL,
+
+    PRIMARY KEY (`productTypeIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ProductUrl` (
+    `productIdx` INTEGER NOT NULL,
+    `urlHeader` VARCHAR(45) NOT NULL,
+    `urlOrder` INTEGER NOT NULL,
+    `productUrl` VARCHAR(500) NOT NULL,
+    `productUrlSort` VARCHAR(45) NOT NULL,
+    `status` VARCHAR(45) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    PRIMARY KEY (`productIdx`, `urlHeader`, `urlOrder`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `Question` (
+    `questionIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `multipleProblemIdx` INTEGER NOT NULL,
+    `question` VARCHAR(1000) NOT NULL,
+    `questionImage` VARCHAR(500) NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `isAnswer` TINYINT NULL DEFAULT 0,
+    `questionNum` INTEGER NULL,
+
+    INDEX `fk_Question_multipleProblemIdx`(`multipleProblemIdx`),
+    FULLTEXT INDEX `question_fulltext_index`(`question`),
+    PRIMARY KEY (`questionIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `Restoration` (
+    `restorationIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `restoreExamDetailIdx` INTEGER NOT NULL,
+    `multipleProblemIdx` INTEGER NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `isPublic` TINYINT NOT NULL DEFAULT 0,
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    INDEX `fk_Restoration_multipleProblemIdx`(`multipleProblemIdx`),
+    INDEX `fk_Restoration_restoreExamDetailIdx`(`restoreExamDetailIdx`),
+    PRIMARY KEY (`restorationIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `RestorationComment` (
+    `restorationCommentIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `restorationIdx` INTEGER NOT NULL,
+    `comment` VARCHAR(200) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `isPublic` TINYINT NULL DEFAULT 0,
+
+    INDEX `fk_RestorationComment_restorationIdx`(`restorationIdx`),
+    INDEX `fk_RestorationComment_userIdx`(`userIdx`),
+    PRIMARY KEY (`restorationCommentIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `RestorationCommentLike` (
+    `restorationCommentLikeIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `restorationCommentIdx` INTEGER NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `isLike` TINYINT NOT NULL DEFAULT 1,
+
+    INDEX `fk_RestorationCommentLike_restorationCommentIdx`(`restorationCommentIdx`),
+    INDEX `fk_RestorationCommentLike_userIdx`(`userIdx`),
+    PRIMARY KEY (`restorationCommentLikeIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `RestorationLike` (
+    `restorationLikeIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `restorationIdx` INTEGER NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `isLike` TINYINT NOT NULL DEFAULT 1,
+
+    INDEX `fk_RestorationLike_restorationIdx`(`restorationIdx`),
+    INDEX `fk_RestorationLike_userIdx`(`userIdx`),
+    PRIMARY KEY (`restorationLikeIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `RestorationSearch` (
+    `multipleProblemIdx` INTEGER NOT NULL,
+    `content` TEXT NULL,
+
+    UNIQUE INDEX `multipleProblemIdx_UNIQUE`(`multipleProblemIdx`),
+    FULLTEXT INDEX `fulltext_content`(`content`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `RestoreExamDetail` (
+    `restoreExamDetailIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `examIdx` INTEGER NOT NULL,
+    `restoreExamDate` DATE NOT NULL,
+    `restoreExamRound` VARCHAR(10) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `thumbnail` VARCHAR(500) NULL,
+    `isPublic` TINYINT NOT NULL DEFAULT 0,
+
+    INDEX `fk_RestoreExamDetail_examIdx`(`examIdx`),
+    PRIMARY KEY (`restoreExamDetailIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `ReviewNote` (
+    `reviewNoteIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `multipleProblemIdx` INTEGER NULL,
+    `subjectiveProblemIdx` INTEGER NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `isRandom` TINYINT NULL DEFAULT 0,
+    `bookMark` TINYINT NOT NULL DEFAULT 0,
+    `memo` VARCHAR(100) NULL,
+
+    INDEX `fk_ReviewNote`(`multipleProblemIdx`, `userIdx`),
+    INDEX `fk_ReviewNote_multipleProblemIdx`(`multipleProblemIdx`),
+    INDEX `fk_ReviewNote_userIdx`(`userIdx`),
+    UNIQUE INDEX `uk_ReviewNote`(`userIdx`, `multipleProblemIdx`),
+    PRIMARY KEY (`reviewNoteIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `SocialAccount` (
+    `userIdx` INTEGER NOT NULL,
+    `snsName` VARCHAR(10) NOT NULL,
+    `snsToken` VARCHAR(150) NOT NULL,
+
+    INDEX `fk_SocialAccount_userIdx`(`userIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `SubjectiveProblem` (
+    `subjectiveProblemIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `problem` LONGTEXT NOT NULL,
+    `problemImage` VARCHAR(255) NULL,
+    `solution` LONGTEXT NOT NULL,
+    `solutionImage` VARCHAR(255) NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `isKatex` INTEGER NOT NULL,
+    `problemScore` INTEGER NOT NULL,
+    `isDelete` TINYINT NOT NULL DEFAULT 0,
+    `lectureUrl` VARCHAR(500) NULL,
+    `examHistory` VARCHAR(500) NULL,
+    `problemUrl` VARCHAR(1000) NULL,
+
+    PRIMARY KEY (`subjectiveProblemIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `SubjectiveProblemCategory` (
+    `subjectiveProblemIdx` INTEGER NOT NULL,
+    `categoryIdx` INTEGER NOT NULL,
+
+    INDEX `fk_ProblemCategory_multipleProblemIdx`(`subjectiveProblemIdx`),
+    PRIMARY KEY (`categoryIdx`, `subjectiveProblemIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `SubjectiveProblemExam` (
+    `examDetailIdx` INTEGER NOT NULL,
+    `subjectiveProblemIdx` INTEGER NOT NULL,
+    `problemNum` INTEGER NULL,
+
+    INDEX `fk_SubjectiveProblemExam_examDetailIdx`(`examDetailIdx`),
+    INDEX `fk_SubjectiveProblemExam_subjectiveProblemIdx`(`subjectiveProblemIdx`),
+    PRIMARY KEY (`examDetailIdx`, `subjectiveProblemIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `SubjectiveProblemSubject` (
+    `subjectiveProblemIdx` INTEGER NOT NULL,
+    `examSubjectIdx` INTEGER NOT NULL,
+
+    INDEX `fk_SubjectiveProblemSubject_subjectiveProblemIdx`(`subjectiveProblemIdx`),
+    INDEX `fk_examSubjectIdx`(`examSubjectIdx`),
+    PRIMARY KEY (`subjectiveProblemIdx`, `examSubjectIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `SubjectiveProblem_old` (
+    `subjectiveProblemIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `problem` LONGTEXT NOT NULL,
+    `problemImage` VARCHAR(255) NULL,
+    `solution` LONGTEXT NOT NULL,
+    `solutionImage` VARCHAR(255) NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `isKatex` INTEGER NOT NULL,
+    `problemScore` INTEGER NOT NULL,
+    `isDelete` TINYINT NOT NULL DEFAULT 0,
+    `lectureUrl` VARCHAR(500) NULL,
+    `examHistory` VARCHAR(500) NULL,
+
+    PRIMARY KEY (`subjectiveProblemIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `User` (
+    `userIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userEmail` VARCHAR(45) NOT NULL,
+    `userPassword` VARCHAR(150) NULL,
+    `userPhoneNum` INTEGER NOT NULL,
+    `userName` VARCHAR(45) NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `dateOfBirth` INTEGER NULL DEFAULT 0,
+    `nickname` VARCHAR(10) NOT NULL,
+    `recommendCount` INTEGER NOT NULL DEFAULT 0,
+    `accessLevel` TINYINT NOT NULL DEFAULT 0,
+    `point` INTEGER NOT NULL DEFAULT 0,
+    `provider` VARCHAR(40) NOT NULL DEFAULT 'engineeo',
+
+    PRIMARY KEY (`userIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `UserAuth` (
+    `userAuthIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `productIdx` INTEGER NOT NULL,
+    `expireAt` TIMESTAMP(0) NOT NULL,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `isUse` INTEGER NOT NULL DEFAULT 0,
+
+    INDEX `fk_UserAuth_productIdx`(`productIdx`),
+    INDEX `fk_UserAuth_userIdx`(`userIdx`),
+    PRIMARY KEY (`userAuthIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `UserAuthLog` (
+    `userAuthLogIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `examDetailIdx` INTEGER NULL,
+    `examIdx` INTEGER NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `type` INTEGER NULL,
+
+    PRIMARY KEY (`userAuthLogIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `UserCart` (
+    `userCartIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `productIdx` INTEGER NOT NULL,
+
+    PRIMARY KEY (`userCartIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `UserCoupon` (
+    `userCouponIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `couponIdx` INTEGER NOT NULL,
+    `expiratonDate` DATETIME(0) NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+
+    PRIMARY KEY (`userCouponIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `UserExamDetailRecord` (
+    `userExamDetailRecordIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userExamRecordIdx` INTEGER NOT NULL,
+    `multipleProblemIdx` INTEGER NOT NULL,
+    `questionIdx` INTEGER NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+
+    INDEX `fk_UserExamDetailRecord_multipleProblemIdx`(`multipleProblemIdx`),
+    INDEX `fk_UserExamDetailRecord_questionIdx`(`questionIdx`),
+    INDEX `fk_UserExamDetailRecord_userExamRecordIdx`(`userExamRecordIdx`),
+    PRIMARY KEY (`userExamDetailRecordIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `UserExamRecord` (
+    `userExamRecordIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `examDetailIdx` INTEGER NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `score` INTEGER NOT NULL,
+    `isPass` TINYINT NULL,
+
+    INDEX `fk_examDetailIdx`(`examDetailIdx`),
+    INDEX `fk_userIdx`(`userIdx`),
+    PRIMARY KEY (`userExamRecordIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `UserLog` (
+    `userLogIdx` INTEGER NOT NULL,
+    `userIdx` INTEGER NOT NULL,
+    `action` CHAR(15) NULL,
+    `createdAt` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    INDEX `fk_UserLog_userIdx`(`userIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `UserPoint` (
+    `userPointIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `pointIdx` INTEGER NOT NULL,
+    `adminIdx` INTEGER NOT NULL DEFAULT 4,
+    `status` CHAR(1) NOT NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `reason` VARCHAR(100) NULL,
+
+    INDEX `UserPoint_fk_pointIdx`(`pointIdx`),
+    INDEX `UserPoint_fk_userIdx`(`userIdx`),
+    PRIMARY KEY (`userPointIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `UserPointLog` (
+    `userPointLogIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `sort` CHAR(1) NOT NULL,
+    `content` VARCHAR(45) NOT NULL,
+    `point` INTEGER NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    INDEX `userPointLog_fk_userIdx_idx`(`userIdx`),
+    PRIMARY KEY (`userPointLogIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `UserRestoration` (
+    `userRestorationIdx` INTEGER NOT NULL AUTO_INCREMENT,
+    `userIdx` INTEGER NOT NULL,
+    `restorationIdx` INTEGER NOT NULL,
+    `status` CHAR(1) NULL DEFAULT 'N',
+    `createdAt` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    INDEX `fk_UserRestoration_restorationIdx`(`restorationIdx`),
+    INDEX `fk_UserRestoration_userIdx`(`userIdx`),
+    PRIMARY KEY (`userRestorationIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `new_table` (
+    `problemIdx` INTEGER NOT NULL,
+    `subjectiveLabel` INTEGER NULL,
+    `history` VARCHAR(500) NULL,
+
+    PRIMARY KEY (`problemIdx`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- AddForeignKey
+ALTER TABLE `AuthorizationLog` ADD CONSTRAINT `AuthorizationLog_fk` FOREIGN KEY (`userAuthIdx`) REFERENCES `UserAuth`(`userAuthIdx`) ON DELETE NO ACTION ON UPDATE NO ACTION;
+
+-- AddForeignKey
+ALTER TABLE `ProductExamDetail` ADD CONSTRAINT `PED.fk_examDetailIdx` FOREIGN KEY (`examDetailIdx`) REFERENCES `ExamDetail`(`examDetailIdx`) ON DELETE NO ACTION ON UPDATE NO ACTION;
+
+-- AddForeignKey
+ALTER TABLE `ProductExamDetail` ADD CONSTRAINT `PED.fk_productIdx` FOREIGN KEY (`productIdx`) REFERENCES `Product`(`productIdx`) ON DELETE NO ACTION ON UPDATE NO ACTION;
+
diff --git a/prisma/migrations/20230424071857_remove_deprecated_table/migration.sql b/prisma/migrations/20230424071857_remove_deprecated_table/migration.sql
new file mode 100644
index 0000000..8051ce7
--- /dev/null
+++ b/prisma/migrations/20230424071857_remove_deprecated_table/migration.sql
@@ -0,0 +1,128 @@
+/*
+  Warnings:
+
+  - The primary key for the `ExamSubjectMulti` table will be changed. If it partially fails, the table could be left without primary key constraint.
+  - You are about to alter the column `examSubjectIdx` on the `ExamSubjectMulti` table. The data in that column could be lost. The data in that column will be cast from `VarChar(45)` to `Int`.
+  - You are about to drop the `AuthDetail` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `BoardComment` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `BoardImage` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `BoardLike` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `CompanyExam_old` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `Coupon` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `CouponProduct` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `CustomProblem` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `CustomProblemImage` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `CustomQuestion` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `ExamAdmissionTicket` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `ExamSort_old` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `Exam_old` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `PaymentAddress` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `ProductImage` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `ProductUrl` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `Restoration` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `RestorationComment` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `RestorationCommentLike` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `RestorationLike` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `RestorationSearch` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `RestoreExamDetail` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `SocialAccount` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `SubjectiveProblem_old` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `UserCoupon` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `UserRestoration` table. If the table is not empty, all the data it contains will be lost.
+  - You are about to drop the `new_table` table. If the table is not empty, all the data it contains will be lost.
+
+*/
+-- DropForeignKey
+ALTER TABLE `AuthorizationLog` DROP FOREIGN KEY `AuthorizationLog_fk`;
+
+-- DropForeignKey
+ALTER TABLE `ProductExamDetail` DROP FOREIGN KEY `PED.fk_examDetailIdx`;
+
+-- DropForeignKey
+ALTER TABLE `ProductExamDetail` DROP FOREIGN KEY `PED.fk_productIdx`;
+
+-- AlterTable
+ALTER TABLE `ExamSubjectMulti` DROP PRIMARY KEY,
+    MODIFY `examSubjectIdx` INTEGER NOT NULL,
+    ADD PRIMARY KEY (`examIdx`, `examSubjectIdx`);
+
+-- DropTable
+DROP TABLE `AuthDetail`;
+
+-- DropTable
+DROP TABLE `BoardComment`;
+
+-- DropTable
+DROP TABLE `BoardImage`;
+
+-- DropTable
+DROP TABLE `BoardLike`;
+
+-- DropTable
+DROP TABLE `CompanyExam_old`;
+
+-- DropTable
+DROP TABLE `Coupon`;
+
+-- DropTable
+DROP TABLE `CouponProduct`;
+
+-- DropTable
+DROP TABLE `CustomProblem`;
+
+-- DropTable
+DROP TABLE `CustomProblemImage`;
+
+-- DropTable
+DROP TABLE `CustomQuestion`;
+
+-- DropTable
+DROP TABLE `ExamAdmissionTicket`;
+
+-- DropTable
+DROP TABLE `ExamSort_old`;
+
+-- DropTable
+DROP TABLE `Exam_old`;
+
+-- DropTable
+DROP TABLE `PaymentAddress`;
+
+-- DropTable
+DROP TABLE `ProductImage`;
+
+-- DropTable
+DROP TABLE `ProductUrl`;
+
+-- DropTable
+DROP TABLE `Restoration`;
+
+-- DropTable
+DROP TABLE `RestorationComment`;
+
+-- DropTable
+DROP TABLE `RestorationCommentLike`;
+
+-- DropTable
+DROP TABLE `RestorationLike`;
+
+-- DropTable
+DROP TABLE `RestorationSearch`;
+
+-- DropTable
+DROP TABLE `RestoreExamDetail`;
+
+-- DropTable
+DROP TABLE `SocialAccount`;
+
+-- DropTable
+DROP TABLE `SubjectiveProblem_old`;
+
+-- DropTable
+DROP TABLE `UserCoupon`;
+
+-- DropTable
+DROP TABLE `UserRestoration`;
+
+-- DropTable
+DROP TABLE `new_table`;
diff --git a/prisma/migrations/20230424075555_add_restore_models/migration.sql b/prisma/migrations/20230424075555_add_restore_models/migration.sql
new file mode 100644
index 0000000..266e561
--- /dev/null
+++ b/prisma/migrations/20230424075555_add_restore_models/migration.sql
@@ -0,0 +1,78 @@
+
+-- AlterTable
+ALTER TABLE `Payment` MODIFY `status` char(1) NOT NULL DEFAULT 'N';
+
+-- AlterTable
+ALTER TABLE `Product` MODIFY `productName` varchar(100) NOT NULL,
+                      MODIFY `prio` int NOT NULL DEFAULT 0;
+
+
+-- CreateTable
+CREATE TABLE `RestoreComment` (
+                                  `id` INTEGER NOT NULL AUTO_INCREMENT,
+                                  `authorId` INTEGER NOT NULL,
+                                  `restorePostId` INTEGER NOT NULL,
+                                  `parentCommentId` INTEGER NULL,
+                                  `content` VARCHAR(191) NOT NULL,
+                                  `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+                                  `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+                                  `deletedAt` TIMESTAMP(0) NULL,
+
+                                  INDEX `RestoreComment_parentCommentId_idx`(`parentCommentId` ASC),
+                                  INDEX `fk_RestoreComment_authorId`(`authorId` ASC),
+                                  INDEX `fk_RestoreComment_restorePostId`(`restorePostId` ASC),
+                                  PRIMARY KEY (`id` ASC)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `RestorePost` (
+                               `id` INTEGER NOT NULL AUTO_INCREMENT,
+                               `authorId` INTEGER NOT NULL,
+                               `examDetailIdx` INTEGER NOT NULL,
+                               `title` VARCHAR(191) NOT NULL,
+                               `content` VARCHAR(191) NOT NULL,
+                               `expectedAnswer` VARCHAR(191) NULL,
+                               `opinion` VARCHAR(191) NULL,
+                               `recommendCount` INTEGER NOT NULL DEFAULT 0,
+                               `commentCount` INTEGER NOT NULL DEFAULT 0,
+                               `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+                               `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+                               INDEX `fk_RestorePost_authorId`(`authorId` ASC),
+                               INDEX `fk_RestorePost_examDetailIdx`(`examDetailIdx` ASC),
+                               PRIMARY KEY (`id` ASC)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `RestoreQuestion` (
+                                   `id` INTEGER NOT NULL AUTO_INCREMENT,
+                                   `restorePostId` INTEGER NOT NULL,
+                                   `content` VARCHAR(191) NOT NULL,
+                                   `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+                                   `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+                                   INDEX `fk_RestoreQuestion_restorePostId`(`restorePostId` ASC),
+                                   PRIMARY KEY (`id` ASC)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateTable
+CREATE TABLE `RestoreRecommendLog` (
+                                       `id` INTEGER NOT NULL AUTO_INCREMENT,
+                                       `userId` INTEGER NOT NULL,
+                                       `restorePostId` INTEGER NOT NULL,
+                                       `amount` INTEGER NOT NULL DEFAULT 1,
+                                       `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+                                       INDEX `fk_RestoreRecommendLog_restorePostId`(`restorePostId` ASC),
+                                       INDEX `fk_RestoreRecommendLog_userIdx`(`userId` ASC),
+                                       PRIMARY KEY (`id` ASC)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- CreateIndex
+CREATE INDEX `fk_Exam_examSortIdx` ON `Exam`(`examSortIdx` ASC);
+
+-- CreateIndex
+CREATE INDEX `productIdx_idx` ON `UserCart`(`productIdx` ASC);
+
+-- CreateIndex
+CREATE INDEX `userIdx_idx` ON `UserCart`(`userIdx` ASC);
diff --git a/prisma/migrations/20230425021150_change_restore_model_string_type_text/migration.sql b/prisma/migrations/20230425021150_change_restore_model_string_type_text/migration.sql
new file mode 100644
index 0000000..9fd82d4
--- /dev/null
+++ b/prisma/migrations/20230425021150_change_restore_model_string_type_text/migration.sql
@@ -0,0 +1,11 @@
+-- AlterTable
+ALTER TABLE `RestoreComment` MODIFY `content` TEXT NOT NULL;
+
+-- AlterTable
+ALTER TABLE `RestorePost` MODIFY `title` TEXT NOT NULL,
+    MODIFY `content` TEXT NOT NULL,
+    MODIFY `expectedAnswer` TEXT NULL,
+    MODIFY `opinion` TEXT NULL;
+
+-- AlterTable
+ALTER TABLE `RestoreQuestion` MODIFY `content` TEXT NOT NULL;
diff --git a/prisma/migrations/20230425073348_add_attachment_model/migration.sql b/prisma/migrations/20230425073348_add_attachment_model/migration.sql
new file mode 100644
index 0000000..f48c41c
--- /dev/null
+++ b/prisma/migrations/20230425073348_add_attachment_model/migration.sql
@@ -0,0 +1,14 @@
+-- CreateTable
+CREATE TABLE `Attachment` (
+    `id` INTEGER NOT NULL AUTO_INCREMENT,
+    `state` CHAR(10) NULL,
+    `bucket` VARCHAR(32) NOT NULL,
+    `path` VARCHAR(64) NULL,
+    `name` VARCHAR(64) NOT NULL,
+    `type` VARCHAR(128) NULL,
+    `size` INTEGER NOT NULL,
+    `createdAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+    `updatedAt` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+    PRIMARY KEY (`id`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
diff --git a/prisma/migrations/20230426061654_add_restore_post_approved_at_column/migration.sql b/prisma/migrations/20230426061654_add_restore_post_approved_at_column/migration.sql
new file mode 100644
index 0000000..0ad4a28
--- /dev/null
+++ b/prisma/migrations/20230426061654_add_restore_post_approved_at_column/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE `RestorePost` ADD COLUMN `approvedAt` DATETIME(3) NULL;
diff --git a/prisma/migrations/20230426083751_remove_attachment_path/migration.sql b/prisma/migrations/20230426083751_remove_attachment_path/migration.sql
new file mode 100644
index 0000000..991d384
--- /dev/null
+++ b/prisma/migrations/20230426083751_remove_attachment_path/migration.sql
@@ -0,0 +1,8 @@
+/*
+  Warnings:
+
+  - You are about to drop the column `path` on the `Attachment` table. All the data in the column will be lost.
+
+*/
+-- AlterTable
+ALTER TABLE `Attachment` DROP COLUMN `path`;
diff --git a/prisma/migrations/20230427054536_add_restore_post_attachment_relation/migration.sql b/prisma/migrations/20230427054536_add_restore_post_attachment_relation/migration.sql
new file mode 100644
index 0000000..c49305c
--- /dev/null
+++ b/prisma/migrations/20230427054536_add_restore_post_attachment_relation/migration.sql
@@ -0,0 +1,8 @@
+-- CreateTable
+CREATE TABLE `_AttachmentToRestorePost` (
+    `A` INTEGER NOT NULL,
+    `B` INTEGER NOT NULL,
+
+    UNIQUE INDEX `_AttachmentToRestorePost_AB_unique`(`A`, `B`),
+    INDEX `_AttachmentToRestorePost_B_index`(`B`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
diff --git a/prisma/migrations/20230508070151_add_attachment_pivot_model/migration.sql b/prisma/migrations/20230508070151_add_attachment_pivot_model/migration.sql
new file mode 100644
index 0000000..c0e2f51
--- /dev/null
+++ b/prisma/migrations/20230508070151_add_attachment_pivot_model/migration.sql
@@ -0,0 +1,18 @@
+/*
+  Warnings:
+
+  - You are about to drop the `_AttachmentToRestorePost` table. If the table is not empty, all the data it contains will be lost.
+
+*/
+-- DropTable
+DROP TABLE `_AttachmentToRestorePost`;
+
+-- CreateTable
+CREATE TABLE `Attached_Attachment` (
+    `id` INTEGER NOT NULL AUTO_INCREMENT,
+    `attached` VARCHAR(191) NOT NULL,
+    `attachmentId` INTEGER NOT NULL,
+
+    UNIQUE INDEX `Attached_Attachment_attachmentId_key`(`attachmentId`),
+    PRIMARY KEY (`id`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
diff --git a/prisma/migrations/20230510092422_add_restore_post_deleted_at_column/migration.sql b/prisma/migrations/20230510092422_add_restore_post_deleted_at_column/migration.sql
new file mode 100644
index 0000000..5193d9f
--- /dev/null
+++ b/prisma/migrations/20230510092422_add_restore_post_deleted_at_column/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE `RestorePost` ADD COLUMN `deletedAt` DATETIME(3) NULL;
diff --git a/prisma/migrations/20230511044552_add_index_attached_attachment_model_attached_column/migration.sql b/prisma/migrations/20230511044552_add_index_attached_attachment_model_attached_column/migration.sql
new file mode 100644
index 0000000..eb5da32
--- /dev/null
+++ b/prisma/migrations/20230511044552_add_index_attached_attachment_model_attached_column/migration.sql
@@ -0,0 +1,2 @@
+-- CreateIndex
+CREATE INDEX `Attached_Attachment_attached_idx` ON `Attached_Attachment`(`attached`);
diff --git a/prisma/migrations/20230515061438_add_foreign_key_attachment_column_in_attached_attachment/migration.sql b/prisma/migrations/20230515061438_add_foreign_key_attachment_column_in_attached_attachment/migration.sql
new file mode 100644
index 0000000..7b464c0
--- /dev/null
+++ b/prisma/migrations/20230515061438_add_foreign_key_attachment_column_in_attached_attachment/migration.sql
@@ -0,0 +1,2 @@
+-- DropIndex
+DROP INDEX `Attached_Attachment_attachmentId_key` ON `Attached_Attachment`;
diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml
new file mode 100644
index 0000000..e5a788a
--- /dev/null
+++ b/prisma/migrations/migration_lock.toml
@@ -0,0 +1,3 @@
+# Please do not edit this file manually
+# It should be added in your version-control system (i.e. Git)
+provider = "mysql"
\ No newline at end of file
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
new file mode 100644
index 0000000..70b5aec
--- /dev/null
+++ b/prisma/schema.prisma
@@ -0,0 +1,660 @@
+generator client {
+  provider        = "prisma-client-js"
+  previewFeatures = ["fullTextIndex"]
+}
+
+datasource db {
+  provider     = "mysql"
+  url          = env("DATABASE_URL")
+  relationMode = "prisma"
+}
+
+model AuthorizationLog {
+  authorizationLogIdx Int @id @default(autoincrement())
+  adminIdx            Int
+  userAuthIdx         Int
+
+  @@index([userAuthIdx], map: "AuthorizationLog_fk")
+}
+
+model Board {
+  boardIdx     Int      @id @default(autoincrement())
+  userIdx      Int
+  boardSort    String   @db.VarChar(45)
+  boardTitle   String   @db.VarChar(80)
+  boardContent String   @db.Text
+  readCount    Int      @default(1)
+  status       String   @default("N") @db.Char(1)
+  createdAt    DateTime @default(now()) @db.Timestamp(0)
+  updatedAt    DateTime @default(now()) @db.Timestamp(0)
+
+  @@index([userIdx], map: "fk_Board_userIdx")
+}
+
+model Category {
+  categoryIdx  Int      @id @default(autoincrement())
+  categoryRef  Int?
+  categoryName String   @db.VarChar(45)
+  status       String   @default("N") @db.Char(1)
+  createdAt    DateTime @default(now()) @db.Timestamp(0)
+  updatedAt    DateTime @default(now()) @db.Timestamp(0)
+  categoryType String   @db.Char(2)
+
+  @@index([categoryRef], map: "fk_Category_categoryRef")
+}
+
+model CompanyExam {
+  companyExamIdx Int     @id @default(autoincrement())
+  examIdx        Int
+  domain         String  @db.VarChar(45)
+  education      String  @db.VarChar(45)
+  examSortIdx_S  Int
+  companyName    String  @db.VarChar(45)
+  category       String? @db.VarChar(45)
+  companyUrl     String? @db.VarChar(200)
+  prio           Int     @default(0)
+  status         String  @default("N") @db.Char(1)
+}
+
+model CompanyInfo {
+  examSortIdx      Int    @unique(map: "examSortIdx_UNIQUE")
+  companyName      String @db.VarChar(45)
+  companySortLabel Int    @default(0)
+  prio             Int?   @default(0)
+}
+
+model CorrectAnswerRate {
+  correctAnswerRateIdx Int      @id @default(autoincrement())
+  multipleProblemIdx   Int
+  userIdx              Int
+  isCorrect            String   @db.Char(1)
+  createdAt            DateTime @default(now()) @db.Timestamp(0)
+  updatedAt            DateTime @default(now()) @db.Timestamp(0)
+  status               String   @default("N") @db.Char(1)
+
+  @@index([multipleProblemIdx], map: "fk_CorrectAnswerRate_multipleProblemIdx")
+  @@index([userIdx], map: "fk_CorrectAnswerRate_userIdx")
+}
+
+model Enterprise {
+  enterpriseIdx    Int      @id @default(autoincrement())
+  enterpriseName   String   @db.Char(20)
+  recruitment      String   @db.VarChar(1500)
+  link             String   @db.VarChar(1500)
+  content          String   @db.VarChar(1000)
+  startDate        DateTime @db.Date
+  endDate          DateTime @db.Date
+  examDate         String   @db.VarChar(200)
+  status           String   @default("N") @db.Char(1)
+  createdAt        DateTime @default(now()) @db.Timestamp(0)
+  updatedAt        DateTime @default(now()) @db.Timestamp(0)
+  image            String?  @db.VarChar(500)
+  essentialElement String?  @db.VarChar(500)
+  extraElement     String?  @db.VarChar(500)
+  etcElement       String?  @db.VarChar(500)
+  interviewDate    String?  @db.VarChar(200)
+  thirdLanguage    String?  @db.VarChar(45)
+}
+
+model Exam {
+  examIdx            Int                @id @default(autoincrement())
+  examName           String             @db.VarChar(45)
+  createdAt          DateTime           @default(now()) @db.Timestamp(0)
+  updatedAt          DateTime           @default(now()) @db.Timestamp(0)
+  status             String             @default("N") @db.Char(1)
+  passScore          Int
+  examRelationIdx    Int?               @map("examSortIdx")
+  timeLimit          Int
+  problemCount       Int
+  accessLevel        Int                @default(0) @db.TinyInt
+  examUrl            String?            @db.VarChar(500)
+  questionType       String?            @db.VarChar(10)
+  subjectiveLabel    Int                @default(0) @db.TinyInt
+  isDelete           Int                @default(0) @db.TinyInt
+  isBody             Int?               @db.TinyInt
+  prio               Int?               @default(0)
+  examSubjectMulties ExamSubjectMulti[]
+  examDetails        ExamDetail[]
+  examRelation       ExamRelation?      @relation(fields: [examRelationIdx], references: [examRelationIdx])
+
+  @@index([examRelationIdx], map: "fk_Exam_examSortIdx")
+}
+
+model ExamDetail {
+  examDetailIdx      Int                 @id @default(autoincrement())
+  examIdx            Int
+  examDate           DateTime            @db.Date
+  examRound          String              @db.VarChar(45)
+  createdAt          DateTime            @default(now()) @db.Timestamp(0)
+  updatedAt          DateTime            @default(now()) @db.Timestamp(0)
+  status             String              @default("N") @db.Char(1)
+  isPublic           Int                 @default(0) @db.TinyInt
+  publicLevel        Int                 @default(2) @db.TinyInt
+  examDetailUrl      String?             @db.VarChar(500)
+  productExamDetails ProductExamDetail[]
+  exam               Exam                @relation(fields: [examIdx], references: [examIdx])
+  restorePosts       RestorePost[]
+
+  @@index([examIdx], map: "fk_ExamDetail_examIdx")
+}
+
+model ExamRelation {
+  examRelationIdx    Int            @id @default(autoincrement()) @map("examSortIdx")
+  referencedExamId   Int?           @map("examSortRef")
+  examType           String         @map("examSortName") @db.VarChar(45)
+  status             String         @default("N") @db.Char(1)
+  createdAt          DateTime       @default(now()) @db.Timestamp(0)
+  updatedAt          DateTime       @default(now()) @db.Timestamp(0)
+  referenceLevel     String?        @map("examSortType") @db.Char(2)
+  exams              Exam[]
+  parentExamRelation ExamRelation?  @relation("ExamSelfRelation", fields: [referencedExamId], references: [examRelationIdx], onDelete: NoAction, onUpdate: NoAction)
+  childExamRelation  ExamRelation[] @relation("ExamSelfRelation")
+
+  @@map("ExamSort")
+}
+
+model ExamSortImage {
+  examSortImageIdx Int    @id @default(autoincrement())
+  examSortIdx      Int    @unique(map: "examSortIdx_UNIQUE")
+  examSortUrl      String @db.VarChar(500)
+}
+
+model ExamSubject {
+  examSubjectIdx     Int                @id @default(autoincrement())
+  examSubjectName    String             @db.VarChar(45)
+  passScore          Int
+  status             String             @default("N") @db.Char(1)
+  createdAt          DateTime           @default(now()) @db.Timestamp(0)
+  updatedAt          DateTime           @default(now()) @db.Timestamp(0)
+  problemCount       Int?               @db.TinyInt
+  examSubjectMulties ExamSubjectMulti[]
+}
+
+model ExamSubjectMulti {
+  examIdx        Int
+  examSubjectIdx Int
+  examSubjectNum Int
+  examSubject    ExamSubject @relation(fields: [examSubjectIdx], references: [examSubjectIdx])
+  exam           Exam?       @relation(fields: [examIdx], references: [examIdx])
+
+  @@id([examIdx, examSubjectIdx])
+  @@index([examIdx], map: "fk_examIdx")
+  @@index([examSubjectIdx], map: "fk_examSubjectIdx")
+}
+
+model MultipleProblem {
+  multipleProblemIdx Int      @id @default(autoincrement())
+  problem            String   @db.Text
+  provisionNum       String?  @db.VarChar(45)
+  problemImage       String?  @db.VarChar(1000)
+  solution           String   @db.Text
+  isKatex            Int      @default(1) @db.TinyInt
+  problemScore       Int?     @default(5) @db.TinyInt
+  lectureUrl         String?  @db.VarChar(1000)
+  status             String   @default("N") @db.Char(1)
+  createdAt          DateTime @default(now()) @db.Timestamp(0)
+  updatedAt          DateTime @default(now()) @db.Timestamp(0)
+  isDelete           Int      @default(0) @db.TinyInt
+  examHistory        String?  @db.VarChar(500)
+  problemUrl         String?  @db.VarChar(1000)
+
+  @@fulltext([problem], map: "multipleProblem_fulltext_index")
+  @@fulltext([solution], map: "multipleProblem_fultext_solution")
+}
+
+model Payment {
+  paymentIdx      String           @id @db.VarChar(45)
+  userIdx         Int
+  createdAt       DateTime         @default(now()) @db.Timestamp(0)
+  updatedAt       DateTime         @default(now()) @db.Timestamp(0)
+  level           String           @default("unverified") @db.Char(30)
+  pay_method      String?          @db.VarChar(45)
+  receipt_url     String?          @db.VarChar(300)
+  impUid          String?          @map("imp_uid") @db.VarChar(45)
+  status          String           @default("N") @db.Char(1)
+  totalPrice      Int
+  paymentName     String           @db.VarChar(45)
+  totalPoint      Int              @default(0)
+  checkSum        Int
+  paidAt          DateTime?        @db.Timestamp(0)
+  refundAt        DateTime?        @db.Timestamp(0)
+  totalCoupon     Int?             @default(0)
+  pointConsumed   Int?             @default(0)
+  user            User             @relation(fields: [userIdx], references: [userIdx])
+  paymentProducts PaymentProduct[]
+
+  @@index([userIdx], map: "fk_Payment_userIdx")
+}
+
+model PaymentProduct {
+  paymentProductIdx Int       @id @default(autoincrement())
+  productIdx        Int
+  paymentIdx        String    @db.VarChar(45)
+  createdAt         DateTime  @default(now()) @db.Timestamp(0)
+  updatedAt         DateTime  @default(now()) @db.Timestamp(0)
+  status            String    @default("N") @db.Char(1)
+  usePoint          Int       @default(0)
+  useCoupon         Int?
+  finalPrice        Int
+  refundPrice       Int?
+  refundReason      String?   @db.VarChar(45)
+  refundAt          DateTime? @db.Timestamp(0)
+  pg_tid            String?   @db.VarChar(45)
+  receipt_url       String?   @db.VarChar(300)
+  isRefund          Int       @default(0) @db.TinyInt
+  payment           Payment   @relation(fields: [paymentIdx], references: [paymentIdx])
+  product           Product   @relation(fields: [productIdx], references: [productIdx])
+
+  @@index([paymentIdx], map: "fk_PaymentProduct_paymentIdx")
+  @@index([productIdx], map: "fk_PaymentProduct_productIdx")
+}
+
+model Point {
+  pointIdx  Int      @id @default(autoincrement())
+  pointName String   @db.VarChar(45)
+  point     Int
+  status    String   @default("N") @db.Char(1)
+  createdAt DateTime @default(now()) @db.Timestamp(0)
+  updatedAt DateTime @default(now()) @db.Timestamp(0)
+}
+
+model PointLog {
+  pointLogIdx Int      @id @default(autoincrement())
+  userIdx     Int
+  content     String?  @db.VarChar(45)
+  status      String   @default("N") @db.Char(1)
+  createdAt   DateTime @default(now()) @db.Timestamp(0)
+  updatedAt   DateTime @default(now()) @db.Timestamp(0)
+  point       Int
+  pointType   String   @default("S") @db.Char(1)
+
+  @@index([userIdx], map: "fk_PointLog_userIdx")
+}
+
+model ProblemCategory {
+  multipleProblemIdx Int
+  categoryIdx        Int
+
+  @@id([multipleProblemIdx, categoryIdx])
+  @@index([categoryIdx], map: "fk_categoryIdx")
+  @@index([multipleProblemIdx], map: "fk_multipleProblemIdx")
+}
+
+model ProblemError {
+  problemErrorIdx    Int      @id @default(autoincrement())
+  userIdx            Int
+  multipleProblemIdx Int
+  title              String   @db.VarChar(45)
+  content            String   @db.VarChar(500)
+  status             String   @default("N") @db.Char(1)
+  createdAt          DateTime @default(now()) @db.Timestamp(0)
+  updatedAt          DateTime @default(now()) @db.Timestamp(0)
+  solution           String?  @db.VarChar(2000)
+  type               Int?     @default(0)
+  subjectiveLabel    Boolean? @default(false)
+
+  @@index([multipleProblemIdx], map: "fk_ProblemError_multipleProblemIdx")
+  @@index([userIdx], map: "fk_ProblemError_userIdx")
+}
+
+model ProblemExam {
+  examDetailIdx      Int
+  multipleProblemIdx Int
+  problemNum         Int?
+
+  @@id([examDetailIdx, multipleProblemIdx])
+  @@index([examDetailIdx], map: "fk_ProblemExam_examDetailIdx")
+  @@index([multipleProblemIdx], map: "fk_ProblemExam_multipleProblemIdx")
+}
+
+model ProblemSubject {
+  multipleProblemIdx Int
+  examSubjectIdx     Int
+
+  @@id([multipleProblemIdx, examSubjectIdx])
+  @@index([examSubjectIdx], map: "fk_examSubjectIdx")
+  @@index([multipleProblemIdx], map: "fk_multipleProblemIdx")
+}
+
+model Product {
+  productIdx         Int                 @id @default(autoincrement())
+  productName        String              @db.VarChar(100)
+  productThumbnail   String?             @db.VarChar(450)
+  price              Int?
+  discountPrice      Int?
+  shortDescription   String?             @db.VarChar(500)
+  generalDescription String?             @db.Text
+  detailDescription  String?             @db.Text
+  duration           Int?
+  depth              Int
+  status             String              @default("N") @db.Char(1)
+  createdAt          DateTime            @default(now()) @db.Timestamp(0)
+  updatedAt          DateTime            @default(now()) @db.Timestamp(0)
+  prio               Int                 @default(0)
+  productExamDetails ProductExamDetail[]
+  userCarts          UserCart[]
+  paymentProducts    PaymentProduct[]
+  userAuths          UserAuth[]
+}
+
+model ProductExamDetail {
+  productIdx    Int
+  examDetailIdx Int
+  ExamDetail    ExamDetail @relation(fields: [examDetailIdx], references: [examDetailIdx], onDelete: NoAction, onUpdate: NoAction, map: "PED.fk_examDetailIdx")
+  Product       Product    @relation(fields: [productIdx], references: [productIdx], onDelete: NoAction, onUpdate: NoAction, map: "PED.fk_productIdx")
+
+  @@id([productIdx, examDetailIdx])
+  @@index([examDetailIdx], map: "PED.fk_examDetailIdx_idx")
+  @@index([productIdx], map: "PED.fk_productIdx_idx")
+}
+
+model ProductRelation {
+  productIdx     Int
+  parentIdx      Int
+  productTypeIdx Int
+
+  @@id([productIdx, parentIdx])
+}
+
+model ProductType {
+  productTypeIdx Int    @id
+  examField      String @db.VarChar(45)
+}
+
+model Question {
+  questionIdx        Int      @id @default(autoincrement())
+  multipleProblemIdx Int
+  question           String   @db.VarChar(1000)
+  questionImage      String?  @db.VarChar(500)
+  status             String   @default("N") @db.Char(1)
+  createdAt          DateTime @default(now()) @db.Timestamp(0)
+  updatedAt          DateTime @default(now()) @db.Timestamp(0)
+  isAnswer           Int?     @default(0) @db.TinyInt
+  questionNum        Int?
+
+  @@index([multipleProblemIdx], map: "fk_Question_multipleProblemIdx")
+  @@fulltext([question], map: "question_fulltext_index")
+}
+
+model ReviewNote {
+  reviewNoteIdx        Int      @id @default(autoincrement())
+  userIdx              Int
+  multipleProblemIdx   Int?
+  subjectiveProblemIdx Int?
+  status               String   @default("N") @db.Char(1)
+  createdAt            DateTime @default(now()) @db.Timestamp(0)
+  updatedAt            DateTime @default(now()) @db.Timestamp(0)
+  isRandom             Int?     @default(0) @db.TinyInt
+  bookMark             Int      @default(0) @db.TinyInt
+  memo                 String?  @db.VarChar(100)
+
+  @@unique([userIdx, multipleProblemIdx], map: "uk_ReviewNote")
+  @@index([multipleProblemIdx, userIdx], map: "fk_ReviewNote")
+  @@index([multipleProblemIdx], map: "fk_ReviewNote_multipleProblemIdx")
+  @@index([userIdx], map: "fk_ReviewNote_userIdx")
+}
+
+model SubjectiveProblem {
+  subjectiveProblemIdx Int      @id @default(autoincrement())
+  problem              String   @db.LongText
+  problemImage         String?  @db.VarChar(255)
+  solution             String   @db.LongText
+  solutionImage        String?  @db.VarChar(255)
+  status               String   @default("N") @db.Char(1)
+  createdAt            DateTime @default(now()) @db.Timestamp(0)
+  updatedAt            DateTime @default(now()) @db.Timestamp(0)
+  isKatex              Int
+  problemScore         Int
+  isDelete             Int      @default(0) @db.TinyInt
+  lectureUrl           String?  @db.VarChar(500)
+  examHistory          String?  @db.VarChar(500)
+  problemUrl           String?  @db.VarChar(1000)
+}
+
+model SubjectiveProblemCategory {
+  subjectiveProblemIdx Int
+  categoryIdx          Int
+
+  @@id([categoryIdx, subjectiveProblemIdx])
+  @@index([subjectiveProblemIdx], map: "fk_ProblemCategory_multipleProblemIdx")
+}
+
+model SubjectiveProblemExam {
+  examDetailIdx        Int
+  subjectiveProblemIdx Int
+  problemNum           Int?
+
+  @@id([examDetailIdx, subjectiveProblemIdx])
+  @@index([examDetailIdx], map: "fk_SubjectiveProblemExam_examDetailIdx")
+  @@index([subjectiveProblemIdx], map: "fk_SubjectiveProblemExam_subjectiveProblemIdx")
+}
+
+model SubjectiveProblemSubject {
+  subjectiveProblemIdx Int
+  examSubjectIdx       Int
+
+  @@id([subjectiveProblemIdx, examSubjectIdx])
+  @@index([subjectiveProblemIdx], map: "fk_SubjectiveProblemSubject_subjectiveProblemIdx")
+  @@index([examSubjectIdx], map: "fk_examSubjectIdx")
+}
+
+model User {
+  userIdx          Int      @id @default(autoincrement())
+  userEmail        String   @db.VarChar(45)
+  userPassword     String?  @db.VarChar(150)
+  userPhoneNumber  Int      @map("userPhoneNum")
+  userName         String   @db.VarChar(45)
+  createdAt        DateTime @default(now()) @db.Timestamp(0)
+  updatedAt        DateTime @default(now()) @db.Timestamp(0)
+  status           String   @default("N") @db.Char(1)
+  dateOfBirth      Int?     @default(0)
+  nickname         String   @db.VarChar(10)
+  recommendCount   Int      @default(0)
+  accessLevel      Int      @default(0) @db.TinyInt
+  point            Int      @default(0)
+  provider         String   @default("engineeo") @db.VarChar(40)
+  recommendCode    String   @db.VarChar(10)
+  recommendedCount Int      @default(0)
+
+  payments        Payment[]
+  userCarts       UserCart[]
+  restorePosts    RestorePost[]
+  restoreComments RestoreComment[]
+  recommendedLogs UserRecommendLog[]
+}
+
+model UserAuth {
+  userAuthIdx Int      @id @default(autoincrement())
+  userIdx     Int
+  productIdx  Int
+  expireAt    DateTime @db.Timestamp(0)
+  status      String   @default("N") @db.Char(1)
+  createdAt   DateTime @default(now()) @db.Timestamp(0)
+  updatedAt   DateTime @default(now()) @db.Timestamp(0)
+  isUse       Int      @default(0)
+  product     Product  @relation(fields: [productIdx], references: [productIdx])
+
+  @@index([productIdx], map: "fk_UserAuth_productIdx")
+  @@index([userIdx], map: "fk_UserAuth_userIdx")
+}
+
+model UserAuthLog {
+  userAuthLogIdx Int      @id @default(autoincrement())
+  userIdx        Int
+  examDetailIdx  Int?
+  examIdx        Int?
+  createdAt      DateTime @default(now()) @db.Timestamp(0)
+  type           Int?
+}
+
+model UserCart {
+  userCartIdx Int     @id @default(autoincrement())
+  userIdx     Int
+  productIdx  Int
+  user        User    @relation(fields: [userIdx], references: [userIdx])
+  product     Product @relation(fields: [productIdx], references: [productIdx])
+
+  @@index([userIdx], map: "userIdx_idx")
+  @@index([productIdx], map: "productIdx_idx")
+}
+
+model UserExamDetailRecord {
+  userExamDetailRecordIdx Int      @id @default(autoincrement())
+  userExamRecordIdx       Int
+  multipleProblemIdx      Int
+  questionIdx             Int?
+  createdAt               DateTime @default(now()) @db.Timestamp(0)
+  updatedAt               DateTime @default(now()) @db.Timestamp(0)
+  status                  String   @default("N") @db.Char(1)
+
+  @@index([multipleProblemIdx], map: "fk_UserExamDetailRecord_multipleProblemIdx")
+  @@index([questionIdx], map: "fk_UserExamDetailRecord_questionIdx")
+  @@index([userExamRecordIdx], map: "fk_UserExamDetailRecord_userExamRecordIdx")
+}
+
+model UserExamRecord {
+  userExamRecordIdx Int      @id @default(autoincrement())
+  userIdx           Int
+  examDetailIdx     Int
+  createdAt         DateTime @default(now()) @db.Timestamp(0)
+  updatedAt         DateTime @default(now()) @db.Timestamp(0)
+  status            String   @default("N") @db.Char(1)
+  score             Int
+  isPass            Int?     @db.TinyInt
+
+  @@index([examDetailIdx], map: "fk_examDetailIdx")
+  @@index([userIdx], map: "fk_userIdx")
+}
+
+/// The underlying table does not contain a valid unique identifier and can therefore currently not be handled by the Prisma Client.
+model UserLog {
+  userLogIdx Int
+  userIdx    Int
+  action     String?   @db.Char(15)
+  createdAt  DateTime? @default(now()) @db.Timestamp(0)
+
+  @@index([userIdx], map: "fk_UserLog_userIdx")
+  @@ignore
+}
+
+model UserPoint {
+  userPointIdx Int      @id @default(autoincrement())
+  userIdx      Int
+  pointIdx     Int
+  adminIdx     Int      @default(4)
+  status       String   @default("N") @db.Char(1)
+  createdAt    DateTime @default(now()) @db.Timestamp(0)
+  updatedAt    DateTime @default(now()) @db.Timestamp(0)
+  reason       String?  @db.VarChar(100)
+
+  @@index([pointIdx], map: "UserPoint_fk_pointIdx")
+  @@index([userIdx], map: "UserPoint_fk_userIdx")
+}
+
+model UserPointLog {
+  userPointLogIdx Int      @id @default(autoincrement())
+  userIdx         Int
+  sort            String   @db.Char(1)
+  content         String   @db.VarChar(45)
+  point           Int
+  createdAt       DateTime @default(now()) @db.Timestamp(0)
+
+  @@index([userIdx], map: "userPointLog_fk_userIdx_idx")
+}
+
+model RestorePost {
+  id               Int               @id @default(autoincrement())
+  authorId         Int
+  author           User              @relation(fields: [authorId], references: [userIdx])
+  examDetailIdx    Int
+  examDetail       ExamDetail        @relation(fields: [examDetailIdx], references: [examDetailIdx])
+  title            String            @db.Text
+  content          String            @db.Text
+  expectedAnswer   String?           @db.Text
+  opinion          String?           @db.Text
+  recommendCount   Int               @default(0)
+  commentCount     Int               @default(0)
+  approvedAt       DateTime?
+  createdAt        DateTime          @default(now()) @db.Timestamp(0)
+  updatedAt        DateTime          @default(now()) @db.Timestamp(0)
+  deletedAt        DateTime?
+  restoreComments  RestoreComment[]
+  restoreQuestions RestoreQuestion[]
+
+  @@index([authorId], map: "fk_RestorePost_authorId")
+  @@index([examDetailIdx], map: "fk_RestorePost_examDetailIdx")
+}
+
+model RestoreQuestion {
+  id            Int         @id @default(autoincrement())
+  restorePostId Int
+  content       String      @db.Text
+  createdAt     DateTime    @default(now()) @db.Timestamp(0)
+  updatedAt     DateTime    @default(now()) @db.Timestamp(0)
+  restorePost   RestorePost @relation(fields: [restorePostId], references: [id])
+
+  @@index([restorePostId], map: "fk_RestoreQuestion_restorePostId")
+}
+
+model RestoreComment {
+  id              Int              @id @default(autoincrement())
+  authorId        Int
+  author          User             @relation(fields: [authorId], references: [userIdx])
+  restorePostId   Int
+  restorePost     RestorePost      @relation(fields: [restorePostId], references: [id])
+  parentCommentId Int?
+  content         String           @db.Text
+  createdAt       DateTime         @default(now()) @db.Timestamp(0)
+  updatedAt       DateTime         @default(now()) @db.Timestamp(0)
+  deletedAt       DateTime?        @db.Timestamp(0)
+  parentComment   RestoreComment?  @relation("CommentRelations", fields: [parentCommentId], references: [id], onDelete: NoAction, onUpdate: NoAction)
+  childComments   RestoreComment[] @relation("CommentRelations")
+
+  @@index([parentCommentId])
+  @@index([authorId], map: "fk_RestoreComment_authorId")
+  @@index([restorePostId], map: "fk_RestoreComment_restorePostId")
+}
+
+model RestoreRecommendLog {
+  id            Int      @id @default(autoincrement())
+  userId        Int
+  restorePostId Int
+  amount        Int      @default(1)
+  createdAt     DateTime @default(now()) @db.Timestamp(0)
+
+  @@index([userId], map: "fk_RestoreRecommendLog_userIdx")
+  @@index([restorePostId], map: "fk_RestoreRecommendLog_restorePostId")
+}
+
+model Attachment {
+  id        Int      @id @default(autoincrement())
+  state     String?  @db.Char(10)
+  bucket    String   @db.VarChar(32)
+  name      String   @db.VarChar(64)
+  type      String?  @db.VarChar(128)
+  size      Int
+  createdAt DateTime @default(now()) @db.Timestamp(0)
+  updatedAt DateTime @default(now()) @db.Timestamp(0)
+
+  attachedAttachment Attached_Attachment[]
+}
+
+model Attached_Attachment {
+  id           Int        @id @default(autoincrement())
+  attached     String
+  attachmentId Int
+  attachment   Attachment @relation(fields: [attachmentId], references: [id])
+
+  @@index(fields: [attached])
+}
+
+model UserRecommendLog {
+  id         Int      @id @default(autoincrement())
+  referralId Int
+  referredId Int
+  createdAt  DateTime @default(now()) @db.Timestamp(0)
+
+  referredUser User @relation(fields: [referredId], references: [userIdx])
+
+  @@index([referredId])
+}
diff --git a/src/admin/admin.controller.ts b/src/admin/admin.controller.ts
new file mode 100644
index 0000000..296a3bf
--- /dev/null
+++ b/src/admin/admin.controller.ts
@@ -0,0 +1,84 @@
+import {
+  Controller,
+  Get,
+  Param,
+  ParseIntPipe,
+  Patch,
+  Post,
+  UseGuards,
+} from '@nestjs/common';
+import { AdminService } from './admin.service';
+import {
+  ApiHeader,
+  ApiOperation,
+  ApiParam,
+  ApiResponse,
+  ApiTags,
+} from '@nestjs/swagger';
+import { AdminGuard } from '../common/guards/admin.guard';
+import { CurrentUser } from '../user/decorators/current-user.decorator';
+import { UserEntity } from '../user/entities/user.entity';
+import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
+import { Serialize } from 'src/common/interceptors/serialize.interceptor';
+import { ExamService } from 'src/exam/exam.service';
+import { RestorePostService } from 'src/restorePost/restore-post.service';
+import { RestorePostDto } from 'src/restorePost/dtos/res/restore-post.dto';
+
+@Controller('admin')
+@ApiTags('admin API')
+export class AdminController {
+  constructor(
+    private readonly adminService: AdminService,
+    private readonly examService: ExamService,
+    private readonly restorePostService: RestorePostService,
+  ) {}
+
+  @ApiOperation({
+    summary: '공지사항 작성',
+    description: '공지사항을 작성',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiResponse({
+    status: 201,
+    description: '공지사항 작성 성공',
+    type: String,
+  })
+  @UseGuards(AdminGuard)
+  @UseGuards(JwtAuthGuard)
+  @Post('notice')
+  async createNotice(@CurrentUser() user: UserEntity) {
+    return true;
+  }
+
+  @ApiOperation({
+    summary: '복원 게시글 승인',
+    description: '복원 게시글 승인',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiParam({
+    name: 'id',
+    required: true,
+    description: '게시글 인덱스 번호',
+  })
+  @ApiResponse({
+    status: 200,
+    description: '복원 게시글 승인 성공',
+    type: RestorePostDto,
+  })
+  @Serialize(RestorePostDto)
+  @UseGuards(JwtAuthGuard, AdminGuard)
+  @Patch('restore-posts/:id')
+  async approveRestorePost(
+    @Param('id', ParseIntPipe) id: number,
+  ): Promise<RestorePostDto | null> {
+    return this.restorePostService.approvePost(id);
+  }
+}
diff --git a/src/admin/admin.module.ts b/src/admin/admin.module.ts
new file mode 100644
index 0000000..95ac566
--- /dev/null
+++ b/src/admin/admin.module.ts
@@ -0,0 +1,13 @@
+import { RestorePostModule } from './../restorePost/restore-post.module';
+import { Module } from '@nestjs/common';
+import { ExamModule } from 'src/exam/exam.module';
+import { AdminController } from './admin.controller';
+import { AdminService } from './admin.service';
+import { RestoreRecommendLogModule } from 'src/restoreRecommendLog/restore-recommend-log.module';
+
+@Module({
+  imports: [ExamModule, RestorePostModule],
+  controllers: [AdminController],
+  providers: [AdminService],
+})
+export class AdminModule {}
diff --git a/src/admin/admin.service.ts b/src/admin/admin.service.ts
new file mode 100644
index 0000000..09f9771
--- /dev/null
+++ b/src/admin/admin.service.ts
@@ -0,0 +1,26 @@
+import { ExamEntity } from 'src/exam/entities/exam.entity';
+import { Injectable } from '@nestjs/common';
+import { PrismaService } from 'src/prisma/prisma.service';
+import { RestoreExamDetailsQueryDto } from './dtos/query/restore-exam-details.dto';
+import { ExamService } from 'src/exam/exam.service';
+
+@Injectable()
+export class AdminService {
+  constructor(
+    private readonly prismaService: PrismaService,
+    private readonly examService: ExamService,
+  ) {}
+
+  /**
+   * @desc 복원 시험들 회차 조회
+   * @param RestoreExamDetailsQueryDto
+   * @returns ExamEntity[] | null
+   * @author YuuuJeong
+   */
+  // async getRestoreExamDetails(
+  //   restoreExamDetailsQueryDto: RestoreExamDetailsQueryDto,
+  // ): Promise<ExamEntity[] | null> {
+  //   const { examName } = restoreExamDetailsQueryDto;
+  //   return await this.examService.getRestoreExamDetails(examName);
+  // }
+}
diff --git a/src/admin/dtos/query/restore-exam-details.dto.ts b/src/admin/dtos/query/restore-exam-details.dto.ts
new file mode 100644
index 0000000..7e2ba43
--- /dev/null
+++ b/src/admin/dtos/query/restore-exam-details.dto.ts
@@ -0,0 +1,10 @@
+import { ApiPropertyOptional } from '@nestjs/swagger';
+
+export class RestoreExamDetailsQueryDto {
+  @ApiPropertyOptional({
+    example: '전기기사',
+    description:
+      '시험 이름/ 만약 시험이름을 입력하지 않으면 전체 시험에 대한 회차조회',
+  })
+  examName?: string;
+}
diff --git a/src/app.controller.spec.ts b/src/app.controller.spec.ts
new file mode 100644
index 0000000..d22f389
--- /dev/null
+++ b/src/app.controller.spec.ts
@@ -0,0 +1,22 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { AppController } from './app.controller';
+import { AppService } from './app.service';
+
+describe('AppController', () => {
+  let appController: AppController;
+
+  beforeEach(async () => {
+    const app: TestingModule = await Test.createTestingModule({
+      controllers: [AppController],
+      providers: [AppService],
+    }).compile();
+
+    appController = app.get<AppController>(AppController);
+  });
+
+  describe('root', () => {
+    it('should return "Hello World!"', () => {
+      expect(appController.getHello()).toBe('Hello World!');
+    });
+  });
+});
diff --git a/src/app.controller.ts b/src/app.controller.ts
new file mode 100644
index 0000000..cce879e
--- /dev/null
+++ b/src/app.controller.ts
@@ -0,0 +1,12 @@
+import { Controller, Get } from '@nestjs/common';
+import { AppService } from './app.service';
+
+@Controller()
+export class AppController {
+  constructor(private readonly appService: AppService) {}
+
+  @Get()
+  getHello(): string {
+    return this.appService.getHello();
+  }
+}
diff --git a/src/app.module.ts b/src/app.module.ts
new file mode 100644
index 0000000..c41a960
--- /dev/null
+++ b/src/app.module.ts
@@ -0,0 +1,102 @@
+import { RestorePostModule } from './restorePost/restore-post.module';
+import {
+  HttpException,
+  MiddlewareConsumer,
+  Module,
+  RequestMethod,
+} from '@nestjs/common';
+import { AppController } from './app.controller';
+import { AppService } from './app.service';
+import { UserModule } from './user/user.module';
+import { ConfigModule, ConfigService } from '@nestjs/config';
+import { PrismaModule } from './prisma/prisma.module';
+import { ConfigValidationSchema } from './config/config.schema';
+import databaseConfig from './config/database.config';
+import jwtConfig from './config/jwt.config';
+import { AuthModule } from './auth/auth.module';
+import ncpConfig from './config/ncp.config';
+import { AdminModule } from './admin/admin.module';
+import * as Sentry from '@sentry/node';
+import { RavenInterceptor, RavenModule } from 'nest-raven';
+import { APP_INTERCEPTOR } from '@nestjs/core';
+import { ExamModule } from './exam/exam.module';
+import { ProductModule } from './product/product.module';
+import { RestoreRecommendLogModule } from './restoreRecommendLog/restore-recommend-log.module';
+import { RestoreModule } from './restore/restore.module';
+import { AttachmentModule } from './attachment/attachment.module';
+import s3Config from './config/s3.config';
+import { AwsSdkModule } from 'nest-aws-sdk';
+import { S3 } from 'aws-sdk';
+import { S3ManagerModule } from './s3-manager/s3-manager.module';
+import awsConfig from './config/aws.config';
+import { ExamDetailModule } from './examDetail/exam-detail.module';
+import { RestoreCommentModule } from './restoreComment/restore-comment.module';
+import { AttachedAttachmentModule } from './attachedAttachment/attached-attachment.module';
+import { UserRecommendModule } from './userRecommend/user-recommend.module';
+
+@Module({
+  imports: [
+    AuthModule,
+    AdminModule,
+    UserModule,
+    ExamModule,
+    ExamDetailModule,
+    ProductModule,
+    RestoreModule,
+    AttachmentModule,
+    PrismaModule,
+    RestorePostModule,
+    RestoreCommentModule,
+    AttachedAttachmentModule,
+    ConfigModule.forRoot({
+      load: [databaseConfig, jwtConfig, ncpConfig, s3Config, awsConfig],
+      isGlobal: true,
+      expandVariables: true,
+      envFilePath: `.env.${process.env.NODE_ENV}`,
+      validationSchema: ConfigValidationSchema,
+    }),
+    S3ManagerModule,
+    AwsSdkModule.forRootAsync({
+      defaultServiceOptions: {
+        useFactory: (cs: ConfigService) => {
+          return {
+            region: cs.get('s3.AWS_S3_REGION'),
+            credentials: {
+              accessKeyId: cs.get('aws.AWS_ACCESS_KEY')!,
+              secretAccessKey: cs.get('aws.AWS_SECRET_ACCESS_KEY')!,
+            },
+          };
+        },
+        imports: [ConfigModule],
+        inject: [ConfigService],
+      },
+      services: [S3],
+    }),
+    RestoreRecommendLogModule,
+    RavenModule,
+    UserRecommendModule,
+  ],
+  controllers: [AppController],
+  providers: [
+    AppService,
+    {
+      provide: APP_INTERCEPTOR,
+      useValue: new RavenInterceptor({
+        filters: [
+          {
+            type: HttpException,
+            filter: (exception: HttpException) => 500 > exception.getStatus(),
+          },
+        ],
+      }),
+    },
+  ],
+})
+export class AppModule {
+  configure(consumer: MiddlewareConsumer) {
+    consumer.apply(Sentry.Handlers.requestHandler()).forRoutes({
+      path: '*',
+      method: RequestMethod.ALL,
+    });
+  }
+}
diff --git a/src/app.service.ts b/src/app.service.ts
new file mode 100644
index 0000000..927d7cc
--- /dev/null
+++ b/src/app.service.ts
@@ -0,0 +1,8 @@
+import { Injectable } from '@nestjs/common';
+
+@Injectable()
+export class AppService {
+  getHello(): string {
+    return 'Hello World!';
+  }
+}
diff --git a/src/attachedAttachment/attached-attachment.module.ts b/src/attachedAttachment/attached-attachment.module.ts
new file mode 100644
index 0000000..27b0028
--- /dev/null
+++ b/src/attachedAttachment/attached-attachment.module.ts
@@ -0,0 +1,8 @@
+import { Module } from '@nestjs/common';
+import { AttachedAttachmentService } from './attached-attachment.service';
+
+@Module({
+  providers: [AttachedAttachmentService],
+  exports: [AttachedAttachmentService],
+})
+export class AttachedAttachmentModule {}
diff --git a/src/attachedAttachment/attached-attachment.service.ts b/src/attachedAttachment/attached-attachment.service.ts
new file mode 100644
index 0000000..52ee36f
--- /dev/null
+++ b/src/attachedAttachment/attached-attachment.service.ts
@@ -0,0 +1,14 @@
+import { Injectable } from '@nestjs/common';
+import { PrismaService } from '../prisma/prisma.service';
+
+@Injectable()
+export class AttachedAttachmentService {
+  constructor(private readonly prismaService: PrismaService) {}
+  async deleteAttachedFiles(model: string, targetId: number) {
+    return await this.prismaService.attached_Attachment.deleteMany({
+      where: {
+        attached: `${model}_${targetId}`,
+      },
+    });
+  }
+}
diff --git a/src/attachedAttachment/dtos/attached-attachment.dto.ts b/src/attachedAttachment/dtos/attached-attachment.dto.ts
new file mode 100644
index 0000000..ef3b85c
--- /dev/null
+++ b/src/attachedAttachment/dtos/attached-attachment.dto.ts
@@ -0,0 +1,8 @@
+import { AttachmentDto } from 'src/attachment/dtos/res/attachment.dto';
+
+export class AttachedAttachmentDto {
+  id: number;
+  attached: string;
+  attachmentId: number;
+  attachment: Partial<AttachmentDto>;
+}
diff --git a/src/attachedAttachment/entities/attached-attachment.entity.ts b/src/attachedAttachment/entities/attached-attachment.entity.ts
new file mode 100644
index 0000000..e11d0c6
--- /dev/null
+++ b/src/attachedAttachment/entities/attached-attachment.entity.ts
@@ -0,0 +1,9 @@
+import { Attached_Attachment } from '@prisma/client';
+import { AttachmentEntity } from 'src/attachment/entities/attachment.entity';
+
+export class AttachedAttachmentEntity implements Attached_Attachment {
+  id: number;
+  attached: string;
+  attachmentId: number;
+  attachment: Partial<AttachmentEntity>;
+}
diff --git a/src/attachment/attachment.controller.ts b/src/attachment/attachment.controller.ts
new file mode 100644
index 0000000..a624953
--- /dev/null
+++ b/src/attachment/attachment.controller.ts
@@ -0,0 +1,176 @@
+import {
+  BadRequestException,
+  Body,
+  Controller,
+  Get,
+  Param,
+  ParseIntPipe,
+  Post,
+  Query,
+  Redirect,
+} from '@nestjs/common';
+import { AttachmentService } from './attachment.service';
+import { UploadState } from './enums/upload-state.enum';
+import { CreateAttachmentDto } from './dtos/req/create-attachment.dto';
+import {
+  ApiBody,
+  ApiOperation,
+  ApiParam,
+  ApiQuery,
+  ApiResponse,
+  ApiTags,
+} from '@nestjs/swagger';
+import {
+  FILE_NOT_UPLOADED,
+  INVALID_ATTACHMENT_ID,
+} from '../common/constants/attachment.constant';
+import { AttachmentDto } from './dtos/res/attachment.dto';
+import { Serialize } from '../common/interceptors/serialize.interceptor';
+import { PrepareUploadResponseDto } from './dtos/res/prepare-upload-response.dto';
+import { AttachmentEntity } from './entities/attachment.entity';
+import { PresignedUrlResponseDto } from './dtos/res/presigned-url-response.dto';
+import { PresignedUrlRedirectDto } from './dtos/res/presigned-url-redirect.dto';
+
+@ApiTags('attachment API')
+@Controller()
+export class AttachmentController {
+  constructor(private readonly attachmentService: AttachmentService) {}
+
+  @ApiOperation({
+    summary: '첨부파일 업로드 준비',
+    description:
+      '첨부파일 업로드 시 attachment 생성 및 업로드 presignedURL 생성 @author neo',
+  })
+  @ApiBody({
+    type: CreateAttachmentDto,
+  })
+  @ApiResponse({
+    type: PrepareUploadResponseDto,
+    status: 200,
+  })
+  @Serialize(PrepareUploadResponseDto)
+  @Post('attachment/prepare-upload')
+  async attachmentPrepareUpload(
+    @Body() createAttachmentDto: CreateAttachmentDto,
+  ): Promise<PrepareUploadResponseDto> {
+    const { type } = createAttachmentDto;
+    const attachment = await this.attachmentService.createAttachment(
+      createAttachmentDto,
+    );
+    const key = await this.attachmentService.getKey(attachment.id);
+    const signedURL = await this.attachmentService.generatePutSignedURL(
+      key,
+      type,
+    );
+    return {
+      attachment,
+      signedURL,
+    };
+  }
+
+  @ApiOperation({
+    summary: '첨부파일 업로드 완료',
+    description:
+      '첨부파일 업로드 완료시 S3 에서 업로드 확인 후 attachment state 변경 @author neo',
+  })
+  @ApiQuery({
+    name: 'attachmentID',
+    type: Number,
+    example: 1,
+  })
+  @ApiResponse({
+    type: AttachmentDto,
+    status: 200,
+  })
+  @Serialize(AttachmentDto)
+  @Post('attachment/complete-upload')
+  async attachmentCompleteUpload(
+    @Query('attachmentID') attachmentID: number,
+  ): Promise<AttachmentEntity> {
+    const attachment = await this.attachmentService.getAttachment(attachmentID);
+
+    if (!attachment) {
+      throw new BadRequestException(INVALID_ATTACHMENT_ID);
+    }
+
+    if (attachment.state === UploadState.READY) return attachment;
+
+    const key = await this.attachmentService.getKey(attachment.id);
+
+    if (
+      !(await this.attachmentService.isAvailableFile(key, attachment.bucket))
+    ) {
+      throw new BadRequestException(FILE_NOT_UPLOADED);
+    }
+
+    return this.attachmentService.completeUploadAttachment(attachment.id, {
+      state: UploadState.READY,
+    });
+  }
+
+  @ApiOperation({
+    summary: '첨부파일 presigned URL 요청',
+    description: '첨부파일 get presigned URL 요청 @author neo',
+  })
+  @ApiParam({
+    name: 'id',
+    type: Number,
+    example: 1,
+  })
+  @ApiResponse({
+    type: PresignedUrlResponseDto,
+    status: 200,
+  })
+  @Get('attachment/:id')
+  async attachmentGetPresignedURL(
+    @Param('id', ParseIntPipe) id: number,
+  ): Promise<PresignedUrlResponseDto> {
+    const attachment = await this.attachmentService.getAttachment(id);
+
+    if (!attachment || attachment.state !== UploadState.READY) {
+      throw new BadRequestException(INVALID_ATTACHMENT_ID);
+    }
+
+    const key = await this.attachmentService.getKey(id);
+
+    const signedURL = await this.attachmentService.generateGetSignedURL(key);
+
+    return {
+      signedURL,
+    };
+  }
+
+  @ApiOperation({
+    summary: '첨부파일 presigned URL로 리다이렉트',
+    description: '첨부파일 get presigned URL로 리다이렉트 @author YuuuJeong',
+  })
+  @ApiParam({
+    name: 'id',
+    type: Number,
+    example: 1,
+  })
+  @ApiResponse({
+    type: PresignedUrlResponseDto,
+    status: 200,
+  })
+  @Get('attachments/:id')
+  @Redirect()
+  async attachmentRedirectPresignedURL(
+    @Param('id', ParseIntPipe) id: number,
+  ): Promise<PresignedUrlRedirectDto> {
+    const attachment = await this.attachmentService.getAttachment(id);
+
+    if (!attachment || attachment.state !== UploadState.READY) {
+      throw new BadRequestException(INVALID_ATTACHMENT_ID);
+    }
+
+    const key = await this.attachmentService.getKey(id);
+
+    const signedURL = await this.attachmentService.generateGetSignedURL(key);
+
+    return {
+      url: signedURL,
+      statusCode: 302,
+    };
+  }
+}
diff --git a/src/attachment/attachment.module.ts b/src/attachment/attachment.module.ts
new file mode 100644
index 0000000..c8d6968
--- /dev/null
+++ b/src/attachment/attachment.module.ts
@@ -0,0 +1,12 @@
+import { Module } from '@nestjs/common';
+import { AttachmentController } from './attachment.controller';
+import { AttachmentService } from './attachment.service';
+import { S3ManagerModule } from '../s3-manager/s3-manager.module';
+
+@Module({
+  imports: [S3ManagerModule],
+  controllers: [AttachmentController],
+  providers: [AttachmentService],
+  exports: [AttachmentService],
+})
+export class AttachmentModule {}
diff --git a/src/attachment/attachment.service.ts b/src/attachment/attachment.service.ts
new file mode 100644
index 0000000..73dd2e0
--- /dev/null
+++ b/src/attachment/attachment.service.ts
@@ -0,0 +1,180 @@
+import { BadRequestException, Injectable } from '@nestjs/common';
+import { PrismaService } from '../prisma/prisma.service';
+import { ConfigService } from '@nestjs/config';
+import { UploadState } from './enums/upload-state.enum';
+import { CreateAttachmentDto } from './dtos/req/create-attachment.dto';
+import { S3ManagerService } from '../s3-manager/s3-manager.service';
+import { AttachmentEntity } from './entities/attachment.entity';
+import { INVALID_ATTACHMENT_ID } from '../common/constants/attachment.constant';
+
+@Injectable()
+export class AttachmentService {
+  constructor(
+    private readonly prismaService: PrismaService,
+    private readonly configService: ConfigService,
+    private readonly s3ManagerService: S3ManagerService,
+  ) {}
+
+  /**
+   * S3 업로드 attachment 생성
+   * @param input CreateAttachmentDto
+   *
+   * @returns AttachmentEntity
+   *
+   * @author neo
+   */
+  public createAttachment(
+    input: CreateAttachmentDto,
+  ): Promise<AttachmentEntity> {
+    const { name, size, type } = input;
+
+    return this.prismaService.attachment.create({
+      data: {
+        name,
+        size,
+        type,
+        bucket: this.configService.get<string>('s3.AWS_S3_BUCKET')!,
+        state: UploadState.UPLOADING,
+      },
+    });
+  }
+
+  /**
+   * Put presignedURL 생성
+   * @param key
+   * @param type
+   *
+   * @returns string
+   *
+   * @author neo
+   */
+  public generatePutSignedURL(key: string, type: string): Promise<string> {
+    return this.s3ManagerService.getSignedUrlPromise('putObject', {
+      Bucket: this.configService.get<string>('s3.AWS_S3_BUCKET'),
+      Key: key,
+      ContentType: type,
+      ACL: 'public-read',
+      Expires: 60 * 60 * 24 * 7,
+    });
+  }
+
+  /**
+   * Get presignedURL 생성
+   * @param key
+   *
+   * @returns string
+   * @author neo
+   */
+  public generateGetSignedURL(key: string): Promise<string> {
+    return this.s3ManagerService.getSignedUrlPromise('getObject', {
+      Bucket: this.configService.get<string>('s3.AWS_S3_BUCKET'),
+      Key: key,
+      Expires: 60 * 60 * 24 * 7,
+    });
+  }
+
+  /**
+   * 첨부파일 조회
+   * @param id
+   *
+   * @returns AttachmentEntity | null
+   * @throws BadRequestException
+   *
+   * @author neo
+   */
+  public getAttachment(id: number): Promise<AttachmentEntity | null> {
+    return this.prismaService.attachment.findUnique({
+      where: {
+        id,
+      },
+    });
+  }
+
+  /**
+   * attachmet 의 S3 key 생성
+   * @param id
+   * @param useFileName
+   *
+   * @returns string
+   *
+   * @throws BadRequestException
+   *
+   * @author neo
+   */
+  public async getKey(id: number, useFileName?: boolean): Promise<string> {
+    const attachment = await this.getAttachment(id);
+
+    if (!attachment) {
+      throw new BadRequestException(INVALID_ATTACHMENT_ID);
+    }
+
+    const fileName = useFileName ? attachment.name : attachment.id.toString();
+    const key = `attachment/${fileName}`;
+
+    return key;
+  }
+
+  /**
+   * S3에 파일이 존재하는지 확인
+   * @param key
+   * @param bucket
+   *
+   * @returns boolean
+   *
+   * @author neo
+   */
+  public async isAvailableFile(key: string, bucket: string): Promise<boolean> {
+    try {
+      const file = await this.s3ManagerService
+        .headObject({
+          Bucket: bucket,
+          Key: key,
+        })
+        .promise();
+      if (file) {
+        return true;
+      }
+      return false;
+    } catch (error) {
+      return false;
+    }
+  }
+
+  /**
+   * 첨부파일 업로드 완료시 상태 변경
+   * @param id
+   * @param input
+   *
+   * @returns AttachmentEntity
+   *
+   * @throws BadRequestException
+   *
+   * @auhtor neo
+   */
+  public async completeUploadAttachment(
+    id: number,
+    input: { state: string },
+  ): Promise<AttachmentEntity> {
+    const attachment = await this.getAttachment(id);
+    if (!attachment) {
+      throw new BadRequestException(INVALID_ATTACHMENT_ID);
+    }
+
+    return this.prismaService.attachment.update({
+      data: input,
+      where: {
+        id,
+      },
+    });
+  }
+
+  // public async getAttachmentIds(attachedId: string) {
+  //   const attaches = await this.prismaService.attachment.findMany({
+  //     where: {
+  //       attachedId,
+  //     },
+  //   });
+  //
+  //   return attaches.map((attach) => attach.attachmentId);
+  // }
+}
diff --git a/src/attachment/dtos/req/create-attachment.dto.ts b/src/attachment/dtos/req/create-attachment.dto.ts
new file mode 100644
index 0000000..3f0bf09
--- /dev/null
+++ b/src/attachment/dtos/req/create-attachment.dto.ts
@@ -0,0 +1,21 @@
+import { ApiProperty } from '@nestjs/swagger';
+
+export class CreateAttachmentDto {
+  @ApiProperty({
+    description: '파일 이름',
+    example: 'test.jpg',
+  })
+  name: string;
+
+  @ApiProperty({
+    description: '파일 크기',
+    example: 123456,
+  })
+  size: number;
+
+  @ApiProperty({
+    description: '파일 타입',
+    example: 'image/jpeg',
+  })
+  type: string;
+}
diff --git a/src/attachment/dtos/res/attachment.dto.ts b/src/attachment/dtos/res/attachment.dto.ts
new file mode 100644
index 0000000..1d2f70f
--- /dev/null
+++ b/src/attachment/dtos/res/attachment.dto.ts
@@ -0,0 +1,62 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { Expose } from 'class-transformer';
+import { UploadState } from '../../enums/upload-state.enum';
+
+export class AttachmentDto {
+  @ApiProperty({
+    example: 1,
+    description: '첨부파일 id',
+  })
+  @Expose()
+  id: number;
+
+  @ApiProperty({
+    example: 'engineeoimage',
+    description: 's3 버킷 이름',
+  })
+  @Expose()
+  bucket: string;
+
+  @ApiProperty({
+    example: 'image.png',
+    description: '첨부파일 이름',
+  })
+  @Expose()
+  name: string;
+
+  @ApiProperty({
+    example: 1000,
+    description: '첨부파일 크기',
+  })
+  @Expose()
+  size: number;
+
+  @ApiProperty({
+    example: UploadState.READY,
+    description: '첨부파일 상태',
+    enum: UploadState,
+  })
+  @Expose()
+  state: string | null;
+
+  @ApiProperty({
+    example: 'image/png',
+    description: '첨부파일 타입',
+  })
+  @Expose()
+  type: string | null;
+
+  @ApiProperty({
+    example: '2021-08-31T07:50:00.000Z',
+    description: '첨부파일 생성일',
+  })
+  @Expose()
+  createdAt: Date;
+
+  @ApiProperty({
+    example: '2021-08-31T07:50:00.000Z',
+    description: '첨부파일 수정일',
+  })
+  @Expose()
+  updatedAt: Date;
+}
diff --git a/src/attachment/dtos/res/prepare-upload-response.dto.ts b/src/attachment/dtos/res/prepare-upload-response.dto.ts
new file mode 100644
index 0000000..e56d7ed
--- /dev/null
+++ b/src/attachment/dtos/res/prepare-upload-response.dto.ts
@@ -0,0 +1,21 @@
+import { AttachmentDto } from './attachment.dto';
+import { ApiProperty } from '@nestjs/swagger';
+import { Expose, Type } from 'class-transformer';
+import { PresignedUrlResponseDto } from './presigned-url-response.dto';
+
+export class PrepareUploadResponseDto {
+  @ApiProperty({
+    type: AttachmentDto,
+  })
+  @Type(() => AttachmentDto)
+  @Expose()
+  attachment: AttachmentDto;
+
+  @ApiProperty({
+    type: PresignedUrlResponseDto,
+    description: 'put presigned url',
+  })
+  @Type(() => PresignedUrlResponseDto)
+  @Expose()
+  signedURL: string;
+}
diff --git a/src/attachment/dtos/res/presigned-url-redirect.dto.ts b/src/attachment/dtos/res/presigned-url-redirect.dto.ts
new file mode 100644
index 0000000..366c4fb
--- /dev/null
+++ b/src/attachment/dtos/res/presigned-url-redirect.dto.ts
@@ -0,0 +1,18 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { Expose } from 'class-transformer';
+
+export class PresignedUrlRedirectDto {
+  @ApiProperty({
+    example: 'https://s3.ap-northeast-2.amazonaws.com/...',
+    description: 'presigned url',
+  })
+  @Expose()
+  url: string;
+
+  @ApiProperty({
+    example: '200',
+    description: 'statusCode',
+  })
+  @Expose()
+  statusCode: number;
+}
diff --git a/src/attachment/dtos/res/presigned-url-response.dto.ts b/src/attachment/dtos/res/presigned-url-response.dto.ts
new file mode 100644
index 0000000..dc57f85
--- /dev/null
+++ b/src/attachment/dtos/res/presigned-url-response.dto.ts
@@ -0,0 +1,11 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { Expose } from 'class-transformer';
+
+export class PresignedUrlResponseDto {
+  @ApiProperty({
+    example: 'https://s3.ap-northeast-2.amazonaws.com/...',
+    description: 'presigned url',
+  })
+  @Expose()
+  signedURL: string;
+}
diff --git a/src/attachment/entities/attachment.entity.ts b/src/attachment/entities/attachment.entity.ts
new file mode 100644
index 0000000..4360850
--- /dev/null
+++ b/src/attachment/entities/attachment.entity.ts
@@ -0,0 +1,14 @@
+import { Attachment } from '@prisma/client';
+import { AttachedAttachmentEntity } from 'src/attachedAttachment/entities/attached-attachment.entity';
+
+export class AttachmentEntity implements Attachment {
+  bucket: string;
+  createdAt: Date;
+  id: number;
+  name: string;
+  size: number;
+  state: string | null;
+  type: string | null;
+  updatedAt: Date;
+  attachedAttachment?: Partial<AttachedAttachmentEntity>;
+}
diff --git a/src/attachment/enums/attachment-model.enum.ts b/src/attachment/enums/attachment-model.enum.ts
new file mode 100644
index 0000000..43dd4f7
--- /dev/null
+++ b/src/attachment/enums/attachment-model.enum.ts
@@ -0,0 +1,4 @@
+export enum AttachmentModel {
+  RESTORE_POST = 'RestorePost',
+  RESTORE_COMMENT = 'RestoreComment',
+}
diff --git a/src/attachment/enums/upload-state.enum.ts b/src/attachment/enums/upload-state.enum.ts
new file mode 100644
index 0000000..ce8cca7
--- /dev/null
+++ b/src/attachment/enums/upload-state.enum.ts
@@ -0,0 +1,5 @@
+export enum UploadState {
+  READY = 'READY',
+  UPLOADING = 'UPLOADING',
+  DELETED = 'DELETED',
+}
diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts
new file mode 100644
index 0000000..05cff6d
--- /dev/null
+++ b/src/auth/auth.controller.ts
@@ -0,0 +1,52 @@
+import { Controller, Get, Post, UseGuards } from '@nestjs/common';
+import { AuthService } from './auth.service';
+import { CurrentUser } from '../user/decorators/current-user.decorator';
+import { LocalAuthGuard } from './guards/local-auth.guard';
+import { JwtAuthGuard } from './guards/jwt-auth.guard';
+import { UserEntity } from '../user/entities/user.entity';
+import { LogInResponseDto } from './dtos/login-response.dto';
+import { ApiBody, ApiHeader, ApiOperation, ApiResponse } from '@nestjs/swagger';
+import { UpdateUserCartDto } from '../user/dtos/req/update-user-cart.dto';
+import { LoginDto } from './dtos/login.dto';
+
+@Controller('auth')
+@Controller()
+export class AuthController {
+  constructor(private readonly authService: AuthService) {}
+
+  @Get('test')
+  test() {
+    return 'test';
+  }
+
+  @ApiOperation({
+    summary: 'local user login',
+    description: 'local user login @author neo',
+  })
+  @ApiBody({
+    type: LoginDto,
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: 'return jwt access_token',
+    type: LogInResponseDto,
+  })
+  @Post('login')
+  @UseGuards(LocalAuthGuard)
+  login(@CurrentUser() user: UserEntity): LogInResponseDto {
+    return {
+      access_token: this.authService.login(user),
+    };
+  }
+
+  @Get('profile')
+  @ApiHeader({
+    name: 'x-access-token',
+    required: true,
+  })
+  @UseGuards(JwtAuthGuard)
+  getProfile(@CurrentUser() user: UserEntity) {
+    return user;
+  }
+}
diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts
new file mode 100644
index 0000000..0e4f270
--- /dev/null
+++ b/src/auth/auth.module.ts
@@ -0,0 +1,27 @@
+import { Module } from '@nestjs/common';
+import { AuthService } from './auth.service';
+import { UserModule } from '../user/user.module';
+import { JwtStrategy } from './strategies/jwt.strategy';
+import { LocalStrategy } from './strategies/local.strategy';
+import { AuthController } from './auth.controller';
+import { JwtModule } from '@nestjs/jwt';
+import { ConfigService } from '@nestjs/config';
+
+@Module({
+  imports: [
+    UserModule,
+    JwtModule.registerAsync({
+      useFactory: (config: ConfigService) => ({
+        secret: config.get<string>('jwt.JWT_SECRET'),
+        signOptions: {
+          expiresIn: config.get<string | number>('jwt.JWT_EXPIRES_IN'),
+        },
+      }),
+      inject: [ConfigService],
+    }),
+  ],
+  controllers: [AuthController],
+  providers: [AuthService, LocalStrategy, JwtStrategy],
+  exports: [AuthService],
+})
+export class AuthModule {}
diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts
new file mode 100644
index 0000000..2071f66
--- /dev/null
+++ b/src/auth/auth.service.ts
@@ -0,0 +1,52 @@
+import { Injectable } from '@nestjs/common';
+import { JwtService } from '@nestjs/jwt';
+import { LoginDto } from './dtos/login.dto';
+import { plainToInstance } from 'class-transformer';
+import { UserEntity } from '../user/entities/user.entity';
+import { hashPassword } from '../common/utils/hash-password.util';
+import { PrismaService } from '../prisma/prisma.service';
+import { UserProvider } from '../user/enums/user-provider.enum';
+import { UserDto } from '../user/dtos/res/user.dto';
+
+@Injectable()
+export class AuthService {
+  constructor(
+    private readonly prismaService: PrismaService,
+    private readonly jwtService: JwtService,
+  ) {}
+
+  /**
+   * @desc validate user - local provider only
+   * @param signInDto
+   * @returns UserEntity | null
+   * @author neo
+   */
+  async validateUser(signInDto: LoginDto): Promise<UserEntity | null> {
+    const { userEmail, userPassword } = signInDto;
+
+    const user = await this.prismaService.user.findFirst({
+      where: {
+        userEmail,
+        provider: UserProvider.ENGINEEO,
+      },
+    });
+
+    const hashedPassword = hashPassword(userPassword);
+
+    if (user && user.userPassword === hashedPassword) {
+      return plainToInstance(UserDto, user);
+    }
+    return null;
+  }
+
+  /**
+   * @desc generate jwt token when login
+   * @param user
+   * @returns string - jwt token
+   * @author neo
+   */
+  public login(user: UserEntity): string {
+    const payload = { ...user };
+    return this.jwtService.sign(payload);
+  }
+}
diff --git a/src/auth/dtos/login-response.dto.ts b/src/auth/dtos/login-response.dto.ts
new file mode 100644
index 0000000..68e5d5a
--- /dev/null
+++ b/src/auth/dtos/login-response.dto.ts
@@ -0,0 +1,9 @@
+import { ApiProperty } from '@nestjs/swagger';
+
+export class LogInResponseDto {
+  @ApiProperty({
+    example:
+      'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWR4IjoyMzQ3MywidXNlckVtYWlsIjoidGVzdDIyQHRlc3QuY29tIiwidXNlclBob25lTnVtIjoxMDkyNDc4NjAxLCJ1c2VyTmFtZSI6Iu2FjOyKpO2KuCIsImNyZWF0ZWRBdCI6IjIwMjMtMDEtMDJUMDE6Mzc6MjEuMDAwWiIsInVwZGF0ZWRBdCI6IjIwMjMtMDMtMzFUMDU6MjY6NDkuMDAwWiIsInN0YXR1cyI6Ik4iLCJkYXRlT2ZCaXJ0aCI6MTk5OTAyMjIsIm5pY2tuYW1lIjoi7YWM7Iqk7Yq4MjIzMjExMiIsImlhdCI6MTY4MDQzODk0NCwiZXhwIjoxNjgxMzAyOTQ0fQ.tgAValf3wep4ELVohhr-Hd6zNRkIFuHwT2PhdVrB2Rc',
+  })
+  access_token: string;
+}
diff --git a/src/auth/dtos/login.dto.ts b/src/auth/dtos/login.dto.ts
new file mode 100644
index 0000000..dad62c0
--- /dev/null
+++ b/src/auth/dtos/login.dto.ts
@@ -0,0 +1,17 @@
+import { IsEmail, IsNotEmpty } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class LoginDto {
+  @IsEmail()
+  @IsNotEmpty()
+  @ApiProperty({
+    example: 'test22@test.com',
+  })
+  userEmail: string;
+
+  @IsNotEmpty()
+  @ApiProperty({
+    example: 'tRwxh8',
+  })
+  userPassword: string;
+}
diff --git a/src/auth/guards/jwt-auth.guard.ts b/src/auth/guards/jwt-auth.guard.ts
new file mode 100644
index 0000000..2155290
--- /dev/null
+++ b/src/auth/guards/jwt-auth.guard.ts
@@ -0,0 +1,5 @@
+import { Injectable } from '@nestjs/common';
+import { AuthGuard } from '@nestjs/passport';
+
+@Injectable()
+export class JwtAuthGuard extends AuthGuard('jwt') {}
diff --git a/src/auth/guards/local-auth.guard.ts b/src/auth/guards/local-auth.guard.ts
new file mode 100644
index 0000000..ccf962b
--- /dev/null
+++ b/src/auth/guards/local-auth.guard.ts
@@ -0,0 +1,5 @@
+import { Injectable } from '@nestjs/common';
+import { AuthGuard } from '@nestjs/passport';
+
+@Injectable()
+export class LocalAuthGuard extends AuthGuard('local') {}
diff --git a/src/auth/strategies/jwt.strategy.ts b/src/auth/strategies/jwt.strategy.ts
new file mode 100644
index 0000000..4758f0b
--- /dev/null
+++ b/src/auth/strategies/jwt.strategy.ts
@@ -0,0 +1,24 @@
+import { Strategy, ExtractJwt } from 'passport-jwt';
+import { PassportStrategy } from '@nestjs/passport';
+import { Injectable, UnauthorizedException } from '@nestjs/common';
+import { AuthService } from '../auth.service';
+import { ConfigService } from '@nestjs/config';
+import { UserEntity } from '../../user/entities/user.entity';
+
+@Injectable()
+export class JwtStrategy extends PassportStrategy(Strategy) {
+  constructor(
+    private authService: AuthService,
+    private readonly configService: ConfigService,
+  ) {
+    super({
+      jwtFromRequest: ExtractJwt.fromHeader('x-access-token'),
+      ignoreExpiration: false,
+      secretOrKey: configService.get('JWT_SECRET'),
+    });
+  }
+
+  async validate(payload: any) {
+    return payload;
+  }
+}
diff --git a/src/auth/strategies/local.strategy.ts b/src/auth/strategies/local.strategy.ts
new file mode 100644
index 0000000..2001bac
--- /dev/null
+++ b/src/auth/strategies/local.strategy.ts
@@ -0,0 +1,40 @@
+import { Strategy } from 'passport-local';
+import { PassportStrategy } from '@nestjs/passport';
+import { BadRequestException, Injectable } from '@nestjs/common';
+import { AuthService } from '../auth.service';
+import { UserStatus } from '../../user/enums/user-status.enum';
+import {
+  SIGN_IN_FAILED,
+  USER_BANNED,
+  USER_QUIT,
+} from '../../common/constants/user.constant';
+
+@Injectable()
+export class LocalStrategy extends PassportStrategy(Strategy) {
+  constructor(private authService: AuthService) {
+    super({
+      usernameField: 'userEmail',
+      passwordField: 'userPassword',
+    });
+  }
+
+  async validate(userEmail: string, userPassword: string): Promise<any> {
+    const user = await this.authService.validateUser({
+      userEmail,
+      userPassword,
+    });
+    if (!user) {
+      throw new BadRequestException(SIGN_IN_FAILED);
+    }
+
+    if (user.status === UserStatus.BANNED) {
+      throw new BadRequestException(USER_BANNED);
+    }
+
+    if (user.status === UserStatus.QUIT) {
+      throw new BadRequestException(USER_QUIT);
+    }
+
+    return user;
+  }
+}
diff --git a/src/common/constants/attachment.constant.ts b/src/common/constants/attachment.constant.ts
new file mode 100644
index 0000000..722748b
--- /dev/null
+++ b/src/common/constants/attachment.constant.ts
@@ -0,0 +1,2 @@
+export const INVALID_ATTACHMENT_ID = 'Invalid attachmentID';
+export const FILE_NOT_UPLOADED = 'File not uploaded';
diff --git a/src/common/constants/comment.constant.ts b/src/common/constants/comment.constant.ts
new file mode 100644
index 0000000..f2bac7c
--- /dev/null
+++ b/src/common/constants/comment.constant.ts
@@ -0,0 +1,3 @@
+export const COMMENT_NOT_FOUND = '댓글을 찾을 수 없습니다.';
+export const USER_NOT_AUTHOR =
+  '작성자가 아니므로 댓글을 수정 및 삭제할 수 없습니다.';
diff --git a/src/common/constants/exam.constant.ts b/src/common/constants/exam.constant.ts
new file mode 100644
index 0000000..2e85eb1
--- /dev/null
+++ b/src/common/constants/exam.constant.ts
@@ -0,0 +1 @@
+export const EXAM_NOT_FOUND = '해당 시험을 찾을 수 없습니다.';
diff --git a/src/common/constants/http.constant.ts b/src/common/constants/http.constant.ts
new file mode 100644
index 0000000..e2bfb50
--- /dev/null
+++ b/src/common/constants/http.constant.ts
@@ -0,0 +1,9 @@
+export const HTTP_ERROR_SUFFIX = 'failed';
+export const HTTP_SUCCESS_SUFFIX = 'succeed';
+export const HTTP_DEFAULT_TEXT = 'request';
+export const HTTP_DEFAULT_ERROR_TEXT = `${HTTP_DEFAULT_TEXT} ${HTTP_ERROR_SUFFIX}`;
+export const HTTP_DEFAULT_SUCCESS_TEXT = `${HTTP_DEFAULT_TEXT} ${HTTP_SUCCESS_SUFFIX}`;
+
+export const HTTP_UNAUTHORIZED_TEXT_DEFAULT = 'Unauthorized';
+export const HTTP_PARAMS_PERMISSION_ERROR_DEFAULT = 'Permission denied';
+export const HTTP_BAD_REQUEST_TEXT_DEFAULT = 'Unknown error';
diff --git a/src/common/constants/post.constant.ts b/src/common/constants/post.constant.ts
new file mode 100644
index 0000000..7d3570d
--- /dev/null
+++ b/src/common/constants/post.constant.ts
@@ -0,0 +1 @@
+export const POST_NOT_FOUND = '게시글을 찾을 수 없습니다.';
diff --git a/src/common/constants/user.constant.ts b/src/common/constants/user.constant.ts
new file mode 100644
index 0000000..1579381
--- /dev/null
+++ b/src/common/constants/user.constant.ts
@@ -0,0 +1,9 @@
+export const USER_NOT_FOUND = '존재하지 않는 사용자입니다.';
+export const SIGN_IN_FAILED = '이메일 또는 비밀번호가 일치하지 않습니다.';
+
+export const USER_QUIT = '탈퇴한 사용자입니다.';
+export const USER_BANNED = '정지된 사용자입니다.';
+
+export const USER_RESET_PASSWORD_FAIL = '비밀번호 재설정에 실패했습니다.';
+
+export const DUPLICATE_NICKNAME = '중복된 닉네임입니다.';
diff --git a/src/common/constants/validation.constant.ts b/src/common/constants/validation.constant.ts
new file mode 100644
index 0000000..15e498f
--- /dev/null
+++ b/src/common/constants/validation.constant.ts
@@ -0,0 +1 @@
+export const VALIDATION_ERROR_DEFAULT = 'Invalid params';
diff --git a/src/common/decorators/self.decorator.ts b/src/common/decorators/self.decorator.ts
new file mode 100644
index 0000000..7b89384
--- /dev/null
+++ b/src/common/decorators/self.decorator.ts
@@ -0,0 +1,8 @@
+import { SetMetadata } from '@nestjs/common';
+
+export interface AuthorDecoratorParams {
+  entity: 'RestorePost' | 'RestoreComment';
+}
+
+export const Author = (entity: AuthorDecoratorParams | string) =>
+  SetMetadata('authorParams', typeof entity == 'string' ? { entity } : null);
diff --git a/src/common/entities/base.entity.ts b/src/common/entities/base.entity.ts
new file mode 100644
index 0000000..e65aa87
--- /dev/null
+++ b/src/common/entities/base.entity.ts
@@ -0,0 +1,15 @@
+import { Expose } from 'class-transformer';
+import { IsOptional } from 'class-validator';
+import { UserStatus } from '../../user/enums/user-status.enum';
+import { Status } from '../enums/status.enum';
+
+export class BaseEntity {
+  @Expose()
+  status: string | UserStatus | Status;
+
+  @Expose()
+  createdAt: Date;
+
+  @Expose()
+  updatedAt: Date;
+}
diff --git a/src/common/enums/exam-relation-type.enum.ts b/src/common/enums/exam-relation-type.enum.ts
new file mode 100644
index 0000000..74e0aee
--- /dev/null
+++ b/src/common/enums/exam-relation-type.enum.ts
@@ -0,0 +1,5 @@
+export const enum ExamRelationType {
+  LARGE = 'L',
+  MIDDLE = 'M',
+  SMALL = 'S',
+}
diff --git a/src/common/enums/exam-type.enum.ts b/src/common/enums/exam-type.enum.ts
new file mode 100644
index 0000000..d2d2e5c
--- /dev/null
+++ b/src/common/enums/exam-type.enum.ts
@@ -0,0 +1,4 @@
+export const enum ExamType {
+  TOTAL = '전체',
+  ELECTRIC = '전기',
+}
diff --git a/src/common/enums/post-recommend-amount.enum.ts b/src/common/enums/post-recommend-amount.enum.ts
new file mode 100644
index 0000000..1c3c892
--- /dev/null
+++ b/src/common/enums/post-recommend-amount.enum.ts
@@ -0,0 +1,4 @@
+export const enum PostRecommendAmount {
+  RECOMMEND = 1,
+  DISRECOMMEND = -1,
+}
diff --git a/src/common/enums/public-status.enum.ts b/src/common/enums/public-status.enum.ts
new file mode 100644
index 0000000..70ff2f7
--- /dev/null
+++ b/src/common/enums/public-status.enum.ts
@@ -0,0 +1,4 @@
+export const enum PublicStatus {
+  PUBLIC = 1,
+  PRIVATE = 0,
+}
diff --git a/src/common/enums/status.enum.ts b/src/common/enums/status.enum.ts
new file mode 100644
index 0000000..9791461
--- /dev/null
+++ b/src/common/enums/status.enum.ts
@@ -0,0 +1,4 @@
+export const enum Status {
+  ACTIVE = 'N',
+  DELETED = 'Y',
+}
diff --git a/src/common/errors/validation-http-error.ts b/src/common/errors/validation-http-error.ts
new file mode 100644
index 0000000..5ec8b69
--- /dev/null
+++ b/src/common/errors/validation-http-error.ts
@@ -0,0 +1,24 @@
+import { HttpException, HttpStatus } from '@nestjs/common';
+import { ValidationError } from 'class-validator';
+import { VALIDATION_ERROR_DEFAULT } from '../constants/validation.constant';
+
+/**
+ * @class ValidationHttpError
+ * @classdesc 400 -> bad request
+ *
+ * class-validator를 사용한 validation error는 ValidationHttpError를 throw
+ * HttpException의 getResponse()를 override
+ *
+ * @example new ValidationHttpError(ValidationError()[])
+ *
+ * @author neo
+ */
+export class ValidationHttpError extends HttpException {
+  constructor(error?: any) {
+    super(error || VALIDATION_ERROR_DEFAULT, HttpStatus.BAD_REQUEST);
+  }
+
+  getResponse(): ValidationError[] {
+    return super.getResponse() as ValidationError[];
+  }
+}
diff --git a/src/common/filters/http-exception.filter.ts b/src/common/filters/http-exception.filter.ts
new file mode 100644
index 0000000..f2e0a7c
--- /dev/null
+++ b/src/common/filters/http-exception.filter.ts
@@ -0,0 +1,59 @@
+import {
+  ArgumentsHost,
+  Catch,
+  ExceptionFilter,
+  HttpException,
+} from '@nestjs/common';
+import { ValidationHttpError } from '../errors/validation-http-error';
+
+/**
+ * HttpExceptionFilter
+ * 모든 HttpException을 처리하는 필터
+ *
+ * class-validator를 사용한 validation error는 ValidationHttpError를 throw
+ * 이 클래스에서 클라이언트에 전달할 response를 만듬
+ *
+ * @example validationError:[{
+ * property: 'userEmail',
+ * constraints: {
+ *  isEmail: 'userEmail must be an email',
+ *  },
+ * }]
+ *
+ * @author neo
+ */
+@Catch(HttpException)
+export class HttpExceptionFilter implements ExceptionFilter {
+  catch(exception: HttpException, host: ArgumentsHost) {
+    const ctx = host.switchToHttp();
+    const response = ctx.getResponse();
+    const request = ctx.getRequest();
+    const status = exception.getStatus();
+    const message = exception.message;
+
+    if (exception instanceof ValidationHttpError) {
+      const error = exception.getResponse();
+      const validationError = error.map((e) => {
+        return {
+          property: e.property,
+          constraints: e.constraints,
+        };
+      });
+      return response.status(status).json({
+        message: message,
+        timestamp: new Date().toISOString(),
+        path: request.url,
+        user: request.user?.userEmail,
+        validationError: validationError,
+      });
+    }
+
+    response.status(status).json({
+      statusCode: status,
+      message: message,
+      timestamp: new Date().toISOString(),
+      path: request.url,
+      user: request.user?.userEmail,
+    });
+  }
+}
diff --git a/src/common/filters/unhandled-exception.filter.ts b/src/common/filters/unhandled-exception.filter.ts
new file mode 100644
index 0000000..01ba5de
--- /dev/null
+++ b/src/common/filters/unhandled-exception.filter.ts
@@ -0,0 +1,29 @@
+import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';
+
+/**
+ * UnhandledExceptionFilter
+ * HttpExceptionFilter를 제외한 모든 예외를 처리하는 필터
+ *
+ * handling되지 않은 예외를 처리
+ *
+ * @author neo
+ */
+@Catch()
+export class UnhandledExceptionFilter implements ExceptionFilter {
+  catch(exception: Error, host: ArgumentsHost) {
+    const ctx = host.switchToHttp();
+    const response = ctx.getResponse();
+    const request = ctx.getRequest();
+    const message = exception.message;
+
+    // TODO : logger
+    console.log('exception', exception);
+
+    response.status(500).json({
+      message: message,
+      timestamp: new Date().toISOString(),
+      path: request.url,
+      user: request.user?.userEmail,
+    });
+  }
+}
diff --git a/src/common/guards/admin.guard.ts b/src/common/guards/admin.guard.ts
new file mode 100644
index 0000000..d4b6ece
--- /dev/null
+++ b/src/common/guards/admin.guard.ts
@@ -0,0 +1,30 @@
+import {
+  CanActivate,
+  ExecutionContext,
+  HttpException,
+  HttpStatus,
+  Injectable,
+} from '@nestjs/common';
+import { Observable } from 'rxjs';
+import { UserStatus } from '../../user/enums/user-status.enum';
+
+/**
+ * @class AdminGuard
+ * 관리자 권한을 가진 유저만 접근 가능한 endpoint 사용
+ *
+ * @author neo
+ */
+@Injectable()
+export class AdminGuard implements CanActivate {
+  canActivate(
+    context: ExecutionContext,
+  ): boolean | Promise<boolean> | Observable<boolean> {
+    const request = context.switchToHttp().getRequest();
+
+    if (request?.user.status === (UserStatus.ADMIN || UserStatus.KING)) {
+      return true;
+    }
+
+    throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED);
+  }
+}
diff --git a/src/common/guards/author.guard.ts b/src/common/guards/author.guard.ts
new file mode 100644
index 0000000..2e90ede
--- /dev/null
+++ b/src/common/guards/author.guard.ts
@@ -0,0 +1,56 @@
+import {
+  CanActivate,
+  ExecutionContext,
+  HttpException,
+  HttpStatus,
+  Injectable,
+} from '@nestjs/common';
+import { Reflector } from '@nestjs/core';
+import { PrismaService } from 'src/prisma/prisma.service';
+import { UserStatus } from '../../user/enums/user-status.enum';
+import { AuthorDecoratorParams } from '../decorators/self.decorator';
+
+/**
+ * @class AuthorGuard
+ * 댓글 혹은 게시글에 대한 권한을 가진 사람만 접근할 수 있는 guard
+ *
+ * @author YuuuJeong
+ */
+@Injectable()
+export class AuthorGuard implements CanActivate {
+  constructor(
+    private readonly reflector: Reflector,
+    private readonly prismaService: PrismaService,
+  ) {}
+
+  async canActivate(context: ExecutionContext): Promise<boolean> {
+    const request = context.switchToHttp().getRequest();
+    const userStatus = request?.user.status;
+    if (userStatus === (UserStatus.ADMIN || UserStatus.KING)) {
+      return true;
+    }
+
+    const authorParams = this.reflector.get<AuthorDecoratorParams>(
+      'authorParams',
+      context.getHandler() ?? context.getClass(),
+    );
+
+    if (!authorParams) return true;
+
+    const entity = authorParams.entity;
+    const id = request?.params.id;
+
+    const isAuthor = await this.prismaService[entity].findUnique({
+      where: {
+        id: parseInt(id),
+      },
+      select: { authorId: true },
+    });
+
+    if (isAuthor.authorId === request?.user.userIdx) {
+      return true;
+    }
+
+    throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED);
+  }
+}
diff --git a/src/common/interceptors/serialize.interceptor.ts b/src/common/interceptors/serialize.interceptor.ts
new file mode 100644
index 0000000..dbee9d2
--- /dev/null
+++ b/src/common/interceptors/serialize.interceptor.ts
@@ -0,0 +1,51 @@
+import {
+  CallHandler,
+  ExecutionContext,
+  NestInterceptor,
+  UseInterceptors,
+} from '@nestjs/common';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { plainToInstance } from 'class-transformer';
+
+interface ClassConstructor {
+  new (...args: any[]): {};
+}
+
+export function Serialize(dto: ClassConstructor) {
+  return UseInterceptors(new SerializeInterceptor(dto));
+}
+
+/**
+ * SerializeInterceptor
+ * 컨트롤러에서 클라이언트에 리턴하는 데이터를 dto 형식에 맞게 변환
+ * @author neo
+ */
+export class SerializeInterceptor implements NestInterceptor {
+  constructor(private dto: any) {}
+
+  intercept(
+    context: ExecutionContext,
+    next: CallHandler<any>,
+  ): Observable<any> | Promise<Observable<any>> {
+    // if (Array.isArray(this.dto)) {
+    //   console.log('adssdsdsdsdsds');
+    //   return next.handle().pipe(
+    //     map((data: any) => {
+    //       return data.map((item: any) =>
+    //         plainToInstance(this.dto[0], item, {
+    //           excludeExtraneousValues: true,
+    //         }),
+    //       );
+    //     }),
+    //   );
+    // }
+    return next.handle().pipe(
+      map((data: any) => {
+        return plainToInstance(this.dto, data, {
+          excludeExtraneousValues: true,
+        });
+      }),
+    );
+  }
+}
diff --git a/src/common/paginate/page-meta.dto.ts b/src/common/paginate/page-meta.dto.ts
new file mode 100644
index 0000000..1da6bee
--- /dev/null
+++ b/src/common/paginate/page-meta.dto.ts
@@ -0,0 +1,31 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { PaginateQueryDto } from './paginate-query.dto';
+
+export class PageMetaDto {
+  @ApiProperty()
+  readonly page: number;
+
+  @ApiProperty()
+  readonly pageSize: number;
+
+  @ApiProperty()
+  readonly totalCount: number;
+
+  @ApiProperty()
+  readonly pageCount: number;
+
+  @ApiProperty()
+  readonly hasPreviousPage: boolean;
+
+  @ApiProperty()
+  readonly hasNextPage: boolean;
+
+  constructor(paginateQueryDto: PaginateQueryDto, totalCount: number) {
+    this.page = paginateQueryDto.page!;
+    this.pageSize = paginateQueryDto.pageSize!;
+    this.totalCount = totalCount;
+    this.pageCount = Math.ceil(this.totalCount / this.pageSize);
+    this.hasPreviousPage = this.page > 1;
+    this.hasNextPage = this.page < this.pageCount;
+  }
+}
diff --git a/src/common/paginate/paginate-query.dto.ts b/src/common/paginate/paginate-query.dto.ts
new file mode 100644
index 0000000..35cc48f
--- /dev/null
+++ b/src/common/paginate/paginate-query.dto.ts
@@ -0,0 +1,20 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { Transform } from 'class-transformer';
+
+export class PaginateQueryDto {
+  @ApiProperty({
+    example: 1,
+    description: '페이지 번호',
+    required: false,
+  })
+  @Transform(({ value }) => Number.parseInt(value))
+  page?: number;
+
+  @ApiProperty({
+    example: 15,
+    description: '페이지 사이즈',
+    required: false,
+  })
+  @Transform(({ value }) => Number.parseInt(value))
+  pageSize?: number;
+}
diff --git a/src/common/paginate/paginated.dto.ts b/src/common/paginate/paginated.dto.ts
new file mode 100644
index 0000000..a4ce11e
--- /dev/null
+++ b/src/common/paginate/paginated.dto.ts
@@ -0,0 +1,19 @@
+import { Expose } from 'class-transformer';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class PaginatedDto<T> {
+  constructor(nodes: T[], totalCount: number) {
+    this.nodes = nodes;
+    this.totalCount = totalCount;
+  }
+
+  @Expose()
+  @ApiProperty({
+    type: () => [Object],
+    description: '페이지에 해당하는 데이터',
+  })
+  public nodes: T[];
+
+  @ApiProperty()
+  readonly totalCount: number;
+}
diff --git a/src/common/pipes/custom-validation.pipe.ts b/src/common/pipes/custom-validation.pipe.ts
new file mode 100644
index 0000000..1caddfb
--- /dev/null
+++ b/src/common/pipes/custom-validation.pipe.ts
@@ -0,0 +1,26 @@
+import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
+import { validate } from 'class-validator';
+import { plainToInstance } from 'class-transformer';
+import { ValidationHttpError } from '../errors/validation-http-error';
+
+@Injectable()
+export class CustomValidationPipe implements PipeTransform<any> {
+  async transform(value: any, { metatype }: ArgumentMetadata) {
+    if (!metatype || !this.toValidate(metatype)) {
+      return value;
+    }
+
+    const object = plainToInstance(metatype, value);
+    const errors = await validate(object);
+
+    if (errors.length > 0) {
+      throw new ValidationHttpError(errors);
+    }
+    return value;
+  }
+
+  private toValidate(metatype: Function): boolean {
+    const types: Function[] = [String, Boolean, Number, Array, Object];
+    return !types.includes(metatype);
+  }
+}
diff --git a/src/common/utils/hash-password.util.ts b/src/common/utils/hash-password.util.ts
new file mode 100644
index 0000000..c133dbf
--- /dev/null
+++ b/src/common/utils/hash-password.util.ts
@@ -0,0 +1,5 @@
+import crypto from 'crypto';
+
+export function hashPassword(password: string): string {
+  return crypto.createHash('sha512').update(password).digest('hex');
+}
diff --git a/src/common/utils/multer.util.ts b/src/common/utils/multer.util.ts
new file mode 100644
index 0000000..575ba05
--- /dev/null
+++ b/src/common/utils/multer.util.ts
@@ -0,0 +1,29 @@
+// import { ConfigService } from '@nestjs/config';
+// import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
+// import multerS3 from 'multer-s3';
+// // import { S3Client } from '@aws-sdk/client-s3';
+// import path from 'path';
+
+// export const multerOptionsFactory = (
+//   configService: ConfigService,
+// ): MulterOptions => {
+//   const s3 = new S3Client({
+//     region: configService.get('AWS_CONFIG_REGION'),
+//     credentials: {
+//       accessKeyId: configService.get('IMAGE_ACCESS_KEY') ?? '',
+//       secretAccessKey: configService.get('IMAGE_SECRET_KEY') ?? '',
+//     },
+//   });
+//   return {
+//     storage: multerS3({
+//       s3,
+//       bucket: configService.get('BUCKET'),
+//       key(_req, file, done) {
+//         const ext = path.extname(file.originalname);
+//         const basename = path.basename(file.originalname, ext);
+//         done(null, `${basename}_${Date.now()}${ext}`);
+//       },
+//     }),
+//     limits: { fileSize: 10 * 1024 * 1024 },
+//   };
+// };
diff --git a/src/common/utils/regex.util.ts b/src/common/utils/regex.util.ts
new file mode 100644
index 0000000..fa5e273
--- /dev/null
+++ b/src/common/utils/regex.util.ts
@@ -0,0 +1,3 @@
+export const nicknameRegex = RegExp(/^[a-zA-Z0-9가-힣]*$/);
+export const passwordRegex =
+  /^(?=.*[a-zA-Z])(?=.*[`~!@#$%^&*-_+=\\(\\)\])(?=.*[0-9])/;
diff --git a/src/common/utils/validate-result.util.ts b/src/common/utils/validate-result.util.ts
new file mode 100644
index 0000000..fdcc3a6
--- /dev/null
+++ b/src/common/utils/validate-result.util.ts
@@ -0,0 +1,7 @@
+import crypto from 'crypto';
+import { Status } from '../enums/status.enum';
+
+export function validateResult(array: any): boolean {
+  if (!array || array.status === Status.DELETED) return false;
+  return true;
+}
diff --git a/src/company/company.module.ts b/src/company/company.module.ts
new file mode 100644
index 0000000..9b23283
--- /dev/null
+++ b/src/company/company.module.ts
@@ -0,0 +1,4 @@
+import { Module } from '@nestjs/common';
+
+@Module({})
+export class CompanyModule {}
diff --git a/src/company/company.service.ts b/src/company/company.service.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/config/aws.config.ts b/src/config/aws.config.ts
new file mode 100644
index 0000000..c285a11
--- /dev/null
+++ b/src/config/aws.config.ts
@@ -0,0 +1,6 @@
+import { registerAs } from '@nestjs/config';
+
+export default registerAs('aws', () => ({
+  AWS_ACCESS_KEY: process.env.AWS_ACCESS_KEY,
+  AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
+}));
diff --git a/src/config/config.schema.ts b/src/config/config.schema.ts
new file mode 100644
index 0000000..f432c54
--- /dev/null
+++ b/src/config/config.schema.ts
@@ -0,0 +1,38 @@
+import * as Joi from 'joi';
+
+/**
+ * Define config validation schema
+ * @see https://docs.nestjs.com/techniques/configuration#validation
+ *
+ * 새로운 환경변수가 추가되면 여기에 추가해야 합니다.
+ *
+ * @author neo
+ */
+export const ConfigValidationSchema = Joi.object({
+  PORT: Joi.number().default(3000),
+  NODE_ENV: Joi.string().valid('dev', 'production', 'test', 'provision'),
+
+  SENTRY_DSN: Joi.string().required(),
+
+  DATABASE_PROVIDER: Joi.string().required(),
+  DATABASE_HOST: Joi.string().required(),
+  DATABASE_PORT: Joi.string().required(),
+  DATABASE_NAME: Joi.string().required(),
+  DATABASE_USER: Joi.string().required(),
+  DATABASE_PASSWORD: Joi.string().required(),
+  DATABASE_URL: Joi.string().required(),
+
+  JWT_SECRET: Joi.string().required(),
+  JWT_EXPIRES_IN: Joi.string().required(),
+
+  NCP_ACCESS_KEY: Joi.string().required(),
+  NCP_SECRET_KEY: Joi.string().required(),
+  NCP_SERVICE_ID: Joi.string().required(),
+  NCP_OFFICE_NUMBER: Joi.string().required(),
+
+  AWS_ACCESS_KEY: Joi.string().required(),
+  AWS_SECRET_ACCESS_KEY: Joi.string().required(),
+
+  AWS_S3_BUCKET: Joi.string().required(),
+  AWS_S3_REGION: Joi.string().required(),
+});
diff --git a/src/config/database.config.ts b/src/config/database.config.ts
new file mode 100644
index 0000000..cd01293
--- /dev/null
+++ b/src/config/database.config.ts
@@ -0,0 +1,11 @@
+import { registerAs } from '@nestjs/config';
+import * as process from 'process';
+
+export default registerAs('database', () => ({
+  DATABASE_PROVIDER: process.env.DATABASE_PROVIDER,
+  DATABASE_HOST: process.env.DATABASE_HOST,
+  DATABASE_PORT: process.env.DATABASE_PORT,
+  DATABASE_NAME: process.env.DATABASE_NAME,
+  DATABASE_USERNAME: process.env.DATABASE_USERNAME,
+  DATABASE_PASSWORD: process.env.DATABASE_PASSWORD,
+}));
diff --git a/src/config/jwt.config.ts b/src/config/jwt.config.ts
new file mode 100644
index 0000000..693c9ff
--- /dev/null
+++ b/src/config/jwt.config.ts
@@ -0,0 +1,6 @@
+import { registerAs } from '@nestjs/config';
+
+export default registerAs('jwt', () => ({
+  JWT_SECRET: process.env.JWT_SECRET,
+  JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN,
+}));
diff --git a/src/config/ncp.config.ts b/src/config/ncp.config.ts
new file mode 100644
index 0000000..299263f
--- /dev/null
+++ b/src/config/ncp.config.ts
@@ -0,0 +1,8 @@
+import { registerAs } from '@nestjs/config';
+
+export default registerAs('ncp', () => ({
+  NCP_ACCESS_KEY: process.env.NCP_ACCESS_KEY,
+  NCP_SECRET_KEY: process.env.NCP_SECRET_KEY,
+  NCP_SERVICE_ID: process.env.NCP_SERVICE_ID,
+  NCP_OFFICE_NUMBER: process.env.NCP_OFFICE_NUMBER,
+}));
diff --git a/src/config/s3.config.ts b/src/config/s3.config.ts
new file mode 100644
index 0000000..9e072e8
--- /dev/null
+++ b/src/config/s3.config.ts
@@ -0,0 +1,6 @@
+import { registerAs } from '@nestjs/config';
+
+export default registerAs('s3', () => ({
+  AWS_S3_BUCKET: process.env.AWS_S3_BUCKET,
+  AWS_S3_REGION: process.env.AWS_S3_REGION,
+}));
diff --git a/src/exam/dtos/req/get-exam-field.dto.ts b/src/exam/dtos/req/get-exam-field.dto.ts
new file mode 100644
index 0000000..73f0342
--- /dev/null
+++ b/src/exam/dtos/req/get-exam-field.dto.ts
@@ -0,0 +1,8 @@
+import { IsString } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class GetExamFieldDto {
+  @ApiProperty({ example: '자격증', description: '이름', default: '자격증' })
+  @IsString()
+  readonly category: string = '자격증';
+}
diff --git a/src/exam/dtos/req/get-exam.dto.ts b/src/exam/dtos/req/get-exam.dto.ts
new file mode 100644
index 0000000..567729e
--- /dev/null
+++ b/src/exam/dtos/req/get-exam.dto.ts
@@ -0,0 +1,12 @@
+import { IsString } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class GetExamDto {
+  @ApiProperty({ example: '자격증', description: '이름', default: '자격증' })
+  @IsString()
+  readonly category: string = '자격증';
+
+  @ApiProperty({ example: '전기', description: '시험 분류', default: '전기' })
+  @IsString()
+  readonly subCategory: string = '전기';
+}
diff --git a/src/exam/dtos/res/exam.dto.ts b/src/exam/dtos/res/exam.dto.ts
new file mode 100644
index 0000000..037d412
--- /dev/null
+++ b/src/exam/dtos/res/exam.dto.ts
@@ -0,0 +1,126 @@
+import { Expose, Type } from 'class-transformer';
+import { ApiProperty } from '@nestjs/swagger';
+
+import { ExamDetailDto } from 'src/examDetail/dtos/res/exam-detail.dto';
+import { ExamSubjectMultiDto } from 'src/examsubjectmulti/dtos/exam-subject-multi.dto';
+import { ExamRelationDto } from 'src/examRelation/dtos/res/exam-relation.dto';
+
+export class ExamDto {
+  @ApiProperty({
+    example: 1,
+    description: '시험 아이디',
+  })
+  @Expose()
+  examIdx: number;
+
+  @ApiProperty({
+    example: 0,
+    description: '시험 권한 수준',
+  })
+  @Expose()
+  accessLevel: number;
+
+  @ApiProperty({
+    example: '전기 기사',
+    description: '시험 이름',
+  })
+  @Expose()
+  examName: string;
+
+  @ApiProperty({
+    example: 1,
+    description: '시험 종류 인덱스',
+  })
+  @Expose()
+  examRelationIdx?: number | null;
+
+  @ApiProperty({
+    example: '',
+    description: '시험 url',
+  })
+  @Expose()
+  examUrl?: string | null;
+
+  @ApiProperty({
+    example: 1,
+    description: '시험 본문 여부',
+  })
+  @Expose()
+  isBody: number | null;
+
+  @ApiProperty({
+    example: 60,
+    description: '시험 합격 점수',
+  })
+  @Expose()
+  passScore: number;
+
+  @ApiProperty({
+    example: 1,
+    description: '정렬 순서',
+  })
+  @Expose()
+  prio?: number | null;
+
+  @ApiProperty({
+    example: 40,
+    description: '문제수',
+  })
+  @Expose()
+  problemCount: number;
+
+  @ApiProperty({
+    example: '객관식 5지선다',
+    description: '출제 유형',
+  })
+  @Expose()
+  questionType?: string | null;
+
+  @ApiProperty({
+    example: 1,
+    description: '주관식 여부',
+  })
+  @Expose()
+  subjectiveLabel: number;
+
+  @ApiProperty({
+    example: 60,
+    description: '제한 시간',
+  })
+  @Expose()
+  timeLimit: number;
+
+  @Expose()
+  status: string;
+  @Expose()
+  createdAt: Date;
+  @Expose()
+  updatedAt: Date;
+
+  @Expose()
+  isDelete: number;
+
+  @ApiProperty({
+    type: ExamDetailDto,
+    isArray: true,
+  })
+  @Expose()
+  @Type(() => ExamDetailDto)
+  examDetails?: Partial<ExamDetailDto>[];
+
+  @ApiProperty({
+    type: ExamSubjectMultiDto,
+    isArray: true,
+  })
+  @Expose()
+  @Type(() => ExamSubjectMultiDto)
+  examSubjectMulties?: Partial<ExamSubjectMultiDto>[];
+
+  @ApiProperty({
+    type: ExamRelationDto,
+    isArray: true,
+  })
+  @Expose()
+  @Type(() => ExamRelationDto)
+  examRelation?: Partial<ExamRelationDto> | null;
+}
diff --git a/src/exam/dtos/res/get-exam-info-response.dto.ts b/src/exam/dtos/res/get-exam-info-response.dto.ts
new file mode 100644
index 0000000..e76523b
--- /dev/null
+++ b/src/exam/dtos/res/get-exam-info-response.dto.ts
@@ -0,0 +1,27 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { Expose } from 'class-transformer';
+import { ExamSubjectMultiEntity } from 'src/examsubjectmulti/entities/exam-subject-multi.entity';
+
+export class GetExamInfoResponseDto {
+  @Expose()
+  readonly examIdx: number;
+  @Expose()
+  readonly examName: string;
+  @Expose()
+  readonly passScore: number;
+  @Expose()
+  readonly timeLimit: number;
+  @Expose()
+  readonly problemCount: number;
+  @Expose()
+  readonly examUrl: string;
+  @Expose()
+  readonly subjectiveLabel: number;
+
+  @ApiProperty({
+    type: ExamSubjectMultiEntity,
+    isArray: true,
+  })
+  @Expose()
+  examSubjectMulties: Partial<ExamSubjectMultiEntity>[];
+}
diff --git a/src/exam/dtos/res/get-exam-sub-category-response.dto.ts b/src/exam/dtos/res/get-exam-sub-category-response.dto.ts
new file mode 100644
index 0000000..f8ac7ac
--- /dev/null
+++ b/src/exam/dtos/res/get-exam-sub-category-response.dto.ts
@@ -0,0 +1,10 @@
+import { Expose, Type } from 'class-transformer';
+
+export class GetExamSubCategoryResponseDto {
+  @Expose()
+  readonly examRelationIdx: number;
+  @Expose()
+  readonly examType: string;
+  @Expose()
+  readonly referenceLevel: string;
+}
diff --git a/src/exam/entities/exam.entity.ts b/src/exam/entities/exam.entity.ts
new file mode 100644
index 0000000..ef379ce
--- /dev/null
+++ b/src/exam/entities/exam.entity.ts
@@ -0,0 +1,26 @@
+import { BaseEntity } from '../../common/entities/base.entity';
+import { Exam } from '@prisma/client';
+import { ExamDetailEntity } from '../../examDetail/entities/exam-detail.entity';
+import { ExamSubjectMultiEntity } from 'src/examsubjectmulti/entities/exam-subject-multi.entity';
+import { ExamRelationEntity } from 'src/examRelation/entities/exam-relation.entity';
+
+export class ExamEntity extends BaseEntity implements Exam {
+  examDetail?: Partial<ExamDetailEntity[]>;
+  accessLevel: number;
+  examIdx: number;
+  examName: string;
+  examRelationIdx: number | null;
+  examUrl: string | null;
+  isBody: number | null;
+  isDelete: number;
+  passScore: number;
+  prio: number | null;
+  problemCount: number;
+  questionType: string | null;
+  subjectiveLabel: number;
+  timeLimit: number;
+
+  examSubjectMulties?: Partial<ExamSubjectMultiEntity>[];
+  examRelation?: Partial<ExamRelationEntity> | null;
+  examDetails?: Partial<ExamDetailEntity>[];
+}
diff --git a/src/exam/exam.controller.ts b/src/exam/exam.controller.ts
new file mode 100644
index 0000000..0e3b9a0
--- /dev/null
+++ b/src/exam/exam.controller.ts
@@ -0,0 +1,94 @@
+import { GetExamDto } from './dtos/req/get-exam.dto';
+import { Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common';
+import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
+import { Serialize } from '../common/interceptors/serialize.interceptor';
+import { ExamDetailDto } from '../examDetail/dtos/res/exam-detail.dto';
+import { ExamService } from './exam.service';
+
+import { GetExamFieldDto } from './dtos/req/get-exam-field.dto';
+import { ExamDto } from './dtos/res/exam.dto';
+import { ExamRelationDto } from 'src/examRelation/dtos/res/exam-relation.dto';
+
+@ApiTags('exam API')
+@Controller()
+export class ExamController {
+  constructor(private readonly examService: ExamService) {}
+
+  @ApiOperation({
+    summary: '시험 조회 ',
+    description: '시험 조회 @author YuuuJeong',
+  })
+  @ApiResponse({
+    status: 200,
+    description: '시험 조회 성공',
+  })
+  @Get('exams')
+  @Serialize(ExamDto)
+  async getExams(@Query() getExamDto: GetExamDto): Promise<ExamDto[] | null> {
+    return await this.examService.getExamsByCategories(getExamDto);
+  }
+
+  //TODO: examRelation 모듈로 이동?
+  @ApiOperation({
+    summary: '시험 타입 조회 ',
+    description: '시험 타입 조회 @author YuuuJeong',
+  })
+  @ApiResponse({
+    status: 200,
+    description: '시험 타입 조회 성공',
+  })
+  @Get('exams/sub-category')
+  @Serialize(ExamRelationDto)
+  async getExamSubCategory(
+    @Query() getExamFieldDto: GetExamFieldDto,
+  ): Promise<ExamRelationDto[] | null> {
+    return await this.examService.getExamSubCategory(getExamFieldDto);
+  }
+
+  @ApiOperation({
+    summary: '시험 정보 조회 ',
+    description: '시험 정보 조회 @author YuuuJeong',
+  })
+  @ApiParam({
+    name: 'examIdx',
+    description: '시험 인덱스',
+    required: true,
+    example: 1,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '시험 정보 조회 성공',
+  })
+  @Serialize(ExamDto)
+  @Get('exam/:examIdx')
+  async getExam(
+    @Param('examIdx', ParseIntPipe) examIdx: number,
+  ): Promise<ExamDto> {
+    return await this.examService.getExam(examIdx);
+  }
+
+  //TODO: examDetail 모듈로 이동?
+  @ApiOperation({
+    summary: '시험의 회차들 조회',
+    description: '시험의 회차들 조회 @author neo',
+  })
+  @ApiParam({
+    name: 'examIdx',
+    description: '시험 인덱스',
+    required: true,
+    example: 1,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '시험 회차 조회 성공',
+    isArray: true,
+    type: ExamDetailDto,
+  })
+  @Serialize(ExamDetailDto)
+  @Get('exam/:examIdx/exam-details')
+  async getExamDetails(
+    @Param('examIdx', ParseIntPipe) examIdx: number,
+  ): Promise<ExamDetailDto[] | null> {
+    return this.examService.getExamDetailsByExamIdx(examIdx);
+  }
+}
diff --git a/src/exam/exam.module.ts b/src/exam/exam.module.ts
new file mode 100644
index 0000000..ae92fa4
--- /dev/null
+++ b/src/exam/exam.module.ts
@@ -0,0 +1,15 @@
+import { ExamRelationService } from '../examRelation/exam-relation.service';
+import { Module } from '@nestjs/common';
+import { ExamController } from './exam.controller';
+import { ExamService } from './exam.service';
+import { UserAuthModule } from 'src/userAuth/user-auth.module';
+import { ProductModule } from 'src/product/product.module';
+import { ExamSubjectService } from 'src/examsubject/exam-subject.service';
+
+@Module({
+  controllers: [ExamController],
+  providers: [ExamService, ExamSubjectService, ExamRelationService],
+  imports: [UserAuthModule, ProductModule],
+  exports: [ExamService],
+})
+export class ExamModule {}
diff --git a/src/exam/exam.service.ts b/src/exam/exam.service.ts
new file mode 100644
index 0000000..cb8fa17
--- /dev/null
+++ b/src/exam/exam.service.ts
@@ -0,0 +1,219 @@
+import { GetExamFieldDto } from './dtos/req/get-exam-field.dto';
+import { GetExamDto } from './dtos/req/get-exam.dto';
+import { ExamRelationService } from '../examRelation/exam-relation.service';
+import { ExamEntity } from './entities/exam.entity';
+import { Injectable, NotFoundException } from '@nestjs/common';
+import { PrismaService } from 'src/prisma/prisma.service';
+import { Status } from 'src/common/enums/status.enum';
+import { ExamType } from 'src/common/enums/exam-type.enum';
+import { EXAM_NOT_FOUND } from 'src/common/constants/exam.constant';
+import { PublicStatus } from 'src/common/enums/public-status.enum';
+import { UserAuthService } from '../userAuth/user-auth.service';
+import { ProductService } from '../product/product.service';
+import { MyExamsResponseDto } from '../user/dtos/res/my-exams-response.dto';
+import { ExamDetailEntity } from '../examDetail/entities/exam-detail.entity';
+import { ExamRelationEntity } from 'src/examRelation/entities/exam-relation.entity';
+import { RestoreExamDetailsQueryDto } from 'src/admin/dtos/query/restore-exam-details.dto';
+
+@Injectable()
+export class ExamService {
+  constructor(
+    private readonly prismaService: PrismaService,
+    private readonly examRelationService: ExamRelationService,
+    private readonly userAuthService: UserAuthService,
+    private readonly productService: ProductService,
+  ) {}
+
+  async getExamByIdx(examIdx: number): Promise<ExamEntity | null> {
+    return await this.prismaService.exam.findFirst({
+      where: {
+        examIdx,
+        status: { in: Status.ACTIVE },
+      },
+    });
+  }
+
+  /**
+   * @desc 복원 시험들 회차 조회, 시험회차의 isPublicㅇ
+   * @param RestoreExamDetailsQueryDto
+   * @returns ExamEntity[] | null
+   * @author YuuuJeong
+   */
+  async getRestoreExamDetails(
+    restoreExamDetailsQueryDto: RestoreExamDetailsQueryDto,
+  ): Promise<ExamEntity[]> {
+    return await this.prismaService.exam.findMany({
+      where: {
+        examName: {
+          contains: restoreExamDetailsQueryDto.examName,
+        },
+        status: Status.ACTIVE,
+        examRelation: {
+          parentExamRelation: {
+            referencedExamId: 1,
+          },
+        },
+      },
+      include: {
+        examDetails: {
+          orderBy: {
+            examDate: 'desc',
+          },
+          where: {
+            isPublic: PublicStatus.PRIVATE,
+          },
+          take: 1,
+        },
+      },
+    });
+  }
+
+  /**
+   * @desc 시험 중분류 조회(ex: 전기, 기계, 소방 등등 )
+   * @param category
+   * @returns examSortEntity
+   * @author YuuuJeong
+   */
+
+  async getExamSubCategory(
+    getExamFieldDto: GetExamFieldDto,
+  ): Promise<ExamRelationEntity[] | null> {
+    const { category } = getExamFieldDto;
+    const examSortResult =
+      await this.examRelationService.getExamRelationByExamType(category);
+
+    const examTypeId = examSortResult?.examRelationIdx;
+
+    const examFieldList = await this.examRelationService.getChildExamRelation(
+      examTypeId,
+    );
+
+    return examFieldList;
+  }
+
+  /**
+   * @desc 소분류 시험 조회
+   * @param category
+   * @param subCategory
+   * @returns ExamEntity
+   * @author YuuuJeong
+   */
+  async getExamsByCategories(
+    getExamDto: GetExamDto,
+  ): Promise<ExamEntity[] | null> {
+    const { category, subCategory } = getExamDto;
+
+    if (category === ExamType.TOTAL) {
+      return await this.prismaService.exam.findMany({});
+    }
+
+    const examRelation =
+      await this.examRelationService.getExamRelationByExamType(category);
+
+    if (!examRelation) {
+      throw new NotFoundException(EXAM_NOT_FOUND);
+    }
+
+    const examRelationIdx: number = examRelation.examRelationIdx;
+
+    const middleLevelExam =
+      await this.examRelationService.getChildExamRelationByExamType(
+        examRelationIdx,
+        subCategory,
+      );
+
+    if (!middleLevelExam) throw new NotFoundException(EXAM_NOT_FOUND);
+
+    const smallLevelExamList =
+      await this.examRelationService.getChildExamRelation(
+        middleLevelExam.examRelationIdx,
+      );
+
+    if (!smallLevelExamList) throw new NotFoundException(EXAM_NOT_FOUND);
+
+    const examRelationIdxList = smallLevelExamList.map(
+      (exam) => exam.examRelationIdx,
+    );
+
+    return await this.prismaService.exam.findMany({
+      where: {
+        status: { in: Status.ACTIVE },
+        examRelationIdx: {
+          in: examRelationIdxList,
+        },
+        examDetails: {
+          some: {
+            isPublic: PublicStatus.PUBLIC,
+          },
+        },
+      },
+    });
+  }
+
+  /**
+   * @desc 특정(examIdx에 매치되는) 시험에 대한 정보 조회
+   * @param examIdx
+   * @returns ExamEntity
+   * @author YuuuJeong
+   */
+  async getExam(examIdx: number): Promise<ExamEntity> {
+    const exam = await this.prismaService.exam.findFirst({
+      where: {
+        examIdx,
+        status: { in: Status.ACTIVE },
+      },
+      include: {
+        examSubjectMulties: {
+          select: {
+            examSubject: true,
+          },
+        },
+      },
+    });
+    if (!exam) {
+      throw new NotFoundException(EXAM_NOT_FOUND);
+    }
+    return exam;
+  }
+
+  /**
+   * 내 시험 목록 조회
+   * @param userIdx:number
+   * @returns Promise<MyExamsResponseDto[]>
+   * @author neo
+   */
+  async getMyExams(userIdx: number): Promise<MyExamsResponseDto[]> {
+    const userAuthList =
+      await this.userAuthService.getUserAuthsWithProductByUserIdx(userIdx);
+
+    const myExamList: MyExamsResponseDto[] = [];
+
+    for (const userAuth of userAuthList) {
+      const productTopCategory =
+        await this.productService.getProductTopCategory(userAuth.productIdx);
+
+      if (!productTopCategory) {
+        continue;
+      }
+
+      myExamList.push({ productTopCategory, ...userAuth });
+    }
+
+    return myExamList;
+  }
+
+  /**
+   * @desc 시험 회차 상세 조회
+   * @param examIdx
+   * @returns Promise<ExamDetailEntity[]>
+   * @author neo
+   */
+  async getExamDetailsByExamIdx(examIdx: number): Promise<ExamDetailEntity[]> {
+    return this.prismaService.examDetail.findMany({
+      where: {
+        examIdx,
+        status: Status.ACTIVE,
+      },
+    });
+  }
+}
diff --git a/src/examDetail/dtos/res/exam-detail.dto.ts b/src/examDetail/dtos/res/exam-detail.dto.ts
new file mode 100644
index 0000000..c8b610f
--- /dev/null
+++ b/src/examDetail/dtos/res/exam-detail.dto.ts
@@ -0,0 +1,54 @@
+import { Expose, Type } from 'class-transformer';
+import { ApiProperty } from '@nestjs/swagger';
+import { ExamDto } from 'src/exam/dtos/res/exam.dto';
+
+export class ExamDetailDto {
+  createdAt: Date;
+  updatedAt: Date;
+  status: string;
+
+  examDetailUrl: string | null;
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  examDetailIdx: number;
+
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  examIdx: number;
+
+  @ApiProperty({
+    example: '2021-01-01',
+  })
+  @Expose()
+  examDate: Date;
+
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  examRound: string;
+
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  isPublic: number;
+
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  publicLevel: number;
+
+  @ApiProperty({
+    type: ExamDto,
+    description: '시험 정보',
+  })
+  @Expose()
+  @Type(() => ExamDto)
+  exam?: Partial<ExamDto>;
+}
diff --git a/src/examDetail/entities/exam-detail.entity.ts b/src/examDetail/entities/exam-detail.entity.ts
new file mode 100644
index 0000000..3ada020
--- /dev/null
+++ b/src/examDetail/entities/exam-detail.entity.ts
@@ -0,0 +1,15 @@
+import { BaseEntity } from '../../common/entities/base.entity';
+import { ExamDetail } from '@prisma/client';
+import { ExamEntity } from '../../exam/entities/exam.entity';
+
+export class ExamDetailEntity extends BaseEntity implements ExamDetail {
+  examDate: Date;
+  examDetailIdx: number;
+  examDetailUrl: string | null;
+  examIdx: number;
+  examRound: string;
+  isPublic: number;
+  publicLevel: number;
+
+  exam?: Partial<ExamEntity>;
+}
diff --git a/src/examDetail/exam-detail.controller.ts b/src/examDetail/exam-detail.controller.ts
new file mode 100644
index 0000000..9cf8462
--- /dev/null
+++ b/src/examDetail/exam-detail.controller.ts
@@ -0,0 +1,93 @@
+import { ExamService } from 'src/exam/exam.service';
+import { ExamDetailService } from './exam-detail.service';
+import {
+  ApiHeader,
+  ApiOperation,
+  ApiParam,
+  ApiResponse,
+  ApiTags,
+} from '@nestjs/swagger';
+import { ExamDetailDto } from './dtos/res/exam-detail.dto';
+import {
+  BadRequestException,
+  Controller,
+  Get,
+  Param,
+  ParseIntPipe,
+  Query,
+  UseGuards,
+} from '@nestjs/common';
+import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
+import { Serialize } from '../common/interceptors/serialize.interceptor';
+import { ExamDto } from 'src/exam/dtos/res/exam.dto';
+
+import { RestoreExamDetailsQueryDto } from 'src/admin/dtos/query/restore-exam-details.dto';
+
+@Controller()
+@ApiTags('exam-detail API')
+export class ExamDetailController {
+  constructor(
+    private readonly examDetailService: ExamDetailService,
+    private readonly examService: ExamService,
+  ) {}
+
+  @ApiOperation({
+    summary: '시험 회차 정보 조회',
+    description: '시험 회차 정보 조회 @author neo',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiParam({
+    name: 'examDetailIdx',
+    description: '시험 회차의 인덱스',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    type: ExamDetailDto,
+  })
+  @UseGuards(JwtAuthGuard)
+  @Serialize(ExamDetailDto)
+  @Get('exam-details/:examDetailIdx')
+  async getExamDetail(
+    @Param('examDetailIdx', ParseIntPipe) examDetailIdx: number,
+  ) {
+    const examDetail = await this.examDetailService.getExamDetailWithExam(
+      examDetailIdx,
+    );
+
+    if (!examDetail) {
+      throw new BadRequestException('존재하지 않는 시험 회차입니다.');
+    }
+
+    return examDetail;
+  }
+
+  @ApiOperation({
+    summary: '복원 시험회차 조회',
+    description: '복원 시험회차 조회',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '복원 시험회차 조회 성공',
+    type: ExamDto,
+  })
+  @Serialize(ExamDto)
+  @UseGuards(JwtAuthGuard)
+  @Get('restore-exam-details')
+  async getRestoreExamDetails(
+    @Query() restoreExamDetailsQueryDto: RestoreExamDetailsQueryDto,
+  ): Promise<any> {
+    return await this.examService.getRestoreExamDetails(
+      restoreExamDetailsQueryDto,
+    );
+  }
+}
diff --git a/src/examDetail/exam-detail.module.ts b/src/examDetail/exam-detail.module.ts
new file mode 100644
index 0000000..2e5b42e
--- /dev/null
+++ b/src/examDetail/exam-detail.module.ts
@@ -0,0 +1,12 @@
+import { ExamModule } from './../exam/exam.module';
+import { Module } from '@nestjs/common';
+import { ExamDetailController } from './exam-detail.controller';
+import { ExamDetailService } from './exam-detail.service';
+
+@Module({
+  imports: [ExamModule],
+  controllers: [ExamDetailController],
+  providers: [ExamDetailService],
+  exports: [ExamDetailService],
+})
+export class ExamDetailModule {}
diff --git a/src/examDetail/exam-detail.service.ts b/src/examDetail/exam-detail.service.ts
new file mode 100644
index 0000000..ed40788
--- /dev/null
+++ b/src/examDetail/exam-detail.service.ts
@@ -0,0 +1,23 @@
+import { PublicStatus } from 'src/common/enums/public-status.enum';
+import { Injectable } from '@nestjs/common';
+import { PrismaService } from '../prisma/prisma.service';
+import { ExamDetailEntity } from './entities/exam-detail.entity';
+import { Status } from 'src/common/enums/status.enum';
+
+@Injectable()
+export class ExamDetailService {
+  constructor(private readonly prismaService: PrismaService) {}
+
+  getExamDetailWithExam(
+    examDetailIdx: number,
+  ): Promise<ExamDetailEntity | null> {
+    return this.prismaService.examDetail.findUnique({
+      where: {
+        examDetailIdx,
+      },
+      include: {
+        exam: true,
+      },
+    });
+  }
+}
diff --git a/src/examRelation/dtos/res/exam-relation.dto.ts b/src/examRelation/dtos/res/exam-relation.dto.ts
new file mode 100644
index 0000000..a7d4c6d
--- /dev/null
+++ b/src/examRelation/dtos/res/exam-relation.dto.ts
@@ -0,0 +1,58 @@
+import { Expose, Type } from 'class-transformer';
+import { ApiProperty } from '@nestjs/swagger';
+import { ExamDto } from 'src/exam/dtos/res/exam.dto';
+
+export class ExamRelationDto {
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  examRelationIdx: number;
+
+  @ApiProperty({
+    example: 1,
+    description: '부모의 examRelationIdx',
+  })
+  @Expose()
+  referencedExamId: number | null;
+
+  @ApiProperty({
+    example: '자격증',
+    description: '자격증(L레벨), 전기(M레벨), 전기기사(S레벨)',
+  })
+  @Expose()
+  examType: string;
+
+  @ApiProperty({
+    example: 'N',
+  })
+  @Expose()
+  status: string;
+
+  @ApiProperty({
+    example: '2023-04-20 23:59:59',
+  })
+  @Expose()
+  createdAt: Date;
+
+  @ApiProperty({
+    example: '2023-04-20 23:59:59',
+  })
+  @Expose()
+  updatedAt: Date;
+
+  @ApiProperty({
+    example: 'L',
+    description: '레벨',
+  })
+  @Expose()
+  referenceLevel: string | null;
+
+  @ApiProperty({
+    type: ExamDto,
+    description: '시험 정보',
+  })
+  @Expose()
+  @Type(() => ExamDto)
+  exam?: Partial<ExamDto>;
+}
diff --git a/src/examRelation/entities/exam-relation.entity.ts b/src/examRelation/entities/exam-relation.entity.ts
new file mode 100644
index 0000000..d706569
--- /dev/null
+++ b/src/examRelation/entities/exam-relation.entity.ts
@@ -0,0 +1,12 @@
+import { ExamRelation } from '@prisma/client';
+import { BaseEntity } from '../../common/entities/base.entity';
+
+export class ExamRelationEntity extends BaseEntity implements ExamRelation {
+  examRelationIdx: number;
+  referencedExamId: number | null;
+  examType: string;
+  status: string;
+  createdAt: Date;
+  updatedAt: Date;
+  referenceLevel: string | null;
+}
diff --git a/src/examRelation/exam-relation.module.ts b/src/examRelation/exam-relation.module.ts
new file mode 100644
index 0000000..de12e35
--- /dev/null
+++ b/src/examRelation/exam-relation.module.ts
@@ -0,0 +1,8 @@
+import { Module } from '@nestjs/common';
+import { ExamRelationService } from './exam-relation.service';
+
+@Module({
+  providers: [ExamRelationService],
+  exports: [ExamRelationService],
+})
+export class ExamRelationModule {}
diff --git a/src/examRelation/exam-relation.service.ts b/src/examRelation/exam-relation.service.ts
new file mode 100644
index 0000000..f3f1ca3
--- /dev/null
+++ b/src/examRelation/exam-relation.service.ts
@@ -0,0 +1,55 @@
+import { Injectable } from '@nestjs/common';
+import { ExamRelationType } from 'src/common/enums/exam-relation-type.enum';
+import { Status } from 'src/common/enums/status.enum';
+import { PrismaService } from 'src/prisma/prisma.service';
+import { ExamRelationEntity } from './entities/exam-relation.entity';
+
+@Injectable()
+export class ExamRelationService {
+  constructor(private readonly prismaService: PrismaService) {}
+
+  async getExamRelationByExamType(
+    examType,
+  ): Promise<ExamRelationEntity | null> {
+    return await this.prismaService.examRelation.findFirst({
+      where: {
+        examType,
+      },
+    });
+  }
+
+  async getgetMiddleLevelExamRelations(
+    referencedExamId,
+  ): Promise<ExamRelationEntity[] | null> {
+    return await this.prismaService.examRelation.findMany({
+      where: {
+        referencedExamId,
+        referenceLevel: { in: ExamRelationType.MIDDLE },
+        status: { in: Status.ACTIVE },
+      },
+    });
+  }
+
+  async getChildExamRelationByExamType(
+    referencedExamId,
+    examType,
+  ): Promise<ExamRelationEntity | null> {
+    const result = await this.prismaService.examRelation.findFirst({
+      where: {
+        referencedExamId,
+        examType,
+      },
+    });
+    return result;
+  }
+
+  async getChildExamRelation(
+    referencedExamId,
+  ): Promise<ExamRelationEntity[] | null> {
+    return await this.prismaService.examRelation.findMany({
+      where: {
+        referencedExamId,
+      },
+    });
+  }
+}
diff --git a/src/examsubject/dtos/exam-subject.dto.ts b/src/examsubject/dtos/exam-subject.dto.ts
new file mode 100644
index 0000000..d36a48a
--- /dev/null
+++ b/src/examsubject/dtos/exam-subject.dto.ts
@@ -0,0 +1,57 @@
+import { ExamSubjectMultiDto } from './../../examsubjectmulti/dtos/exam-subject-multi.dto';
+import { Expose, Type } from 'class-transformer';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class ExamSubjectDto {
+  @ApiProperty({
+    example: 1,
+  })
+  examSubjectIdx: number;
+
+  @ApiProperty({
+    example: '전자기학',
+    description: '과목 이름',
+  })
+  @Expose()
+  examSubjectName: string;
+
+  @ApiProperty({
+    example: 60,
+    description: '합격 점수 기준',
+  })
+  @Expose()
+  passScore: number;
+
+  @ApiProperty({
+    example: 'N',
+  })
+  @Expose()
+  status: string;
+
+  @ApiProperty({
+    example: '2023-04-20 23:59:59',
+  })
+  @Expose()
+  createdAt: Date;
+
+  @ApiProperty({
+    example: '2023-04-20 23:59:59',
+  })
+  @Expose()
+  updatedAt: Date;
+
+  @ApiProperty({
+    example: 20,
+    description: '문제 수',
+  })
+  @Expose()
+  problemCount: number | null;
+
+  @ApiProperty({
+    type: ExamSubjectMultiDto,
+    description: '시험 과목 정보',
+  })
+  @Expose()
+  @Type(() => ExamSubjectMultiDto)
+  examSubjectMulties?: Partial<ExamSubjectMultiDto>[];
+}
diff --git a/src/examsubject/entities/exam-subject.entity.ts b/src/examsubject/entities/exam-subject.entity.ts
new file mode 100644
index 0000000..6fe50b1
--- /dev/null
+++ b/src/examsubject/entities/exam-subject.entity.ts
@@ -0,0 +1,9 @@
+import { ExamSubject } from '@prisma/client';
+import { BaseEntity } from '../../common/entities/base.entity';
+
+export class ExamSubjectEntity extends BaseEntity implements ExamSubject {
+  examSubjectIdx: number;
+  examSubjectName: string;
+  passScore: number;
+  problemCount: number | null;
+}
diff --git a/src/examsubject/exam-subject.module.ts b/src/examsubject/exam-subject.module.ts
new file mode 100644
index 0000000..6aa4b14
--- /dev/null
+++ b/src/examsubject/exam-subject.module.ts
@@ -0,0 +1,8 @@
+import { Module } from '@nestjs/common';
+import { ExamSubjectService } from './exam-subject.service';
+
+@Module({
+  providers: [ExamSubjectService],
+  exports: [ExamSubjectService],
+})
+export class ExamSubjectModule {}
diff --git a/src/examsubject/exam-subject.service.ts b/src/examsubject/exam-subject.service.ts
new file mode 100644
index 0000000..2583de6
--- /dev/null
+++ b/src/examsubject/exam-subject.service.ts
@@ -0,0 +1,25 @@
+import { Injectable } from '@nestjs/common';
+import { Status } from 'src/common/enums/status.enum';
+import { PrismaService } from 'src/prisma/prisma.service';
+
+@Injectable()
+export class ExamSubjectService {
+  constructor(private readonly prismaService: PrismaService) {}
+  async getExamSubject(examIdx) {
+    // return await this.prismaService.examSubject.findMany({
+    //   where: {
+    //     status: { in: Status.ACTIVE },
+    //     ExamSubjectMulti: {
+    //       examIdx,
+    //     },
+    //   },
+    //   include: {
+    //     ExamSubjectMulti: {
+    //       where: {
+    //         examIdx,
+    //       },
+    //     },
+    //   },
+    // });
+  }
+}
diff --git a/src/examsubjectmulti/dtos/exam-subject-multi.dto.ts b/src/examsubjectmulti/dtos/exam-subject-multi.dto.ts
new file mode 100644
index 0000000..e19c7d3
--- /dev/null
+++ b/src/examsubjectmulti/dtos/exam-subject-multi.dto.ts
@@ -0,0 +1,62 @@
+import { Expose, Type } from 'class-transformer';
+import { ApiProperty } from '@nestjs/swagger';
+
+import { ExamSubjectDto } from 'src/examsubject/dtos/exam-subject.dto';
+import { ExamDto } from 'src/exam/dtos/res/exam.dto';
+
+export class ExamSubjectMultiDto {
+  @ApiProperty({
+    example: 1,
+    description: '시험 인덱스',
+  })
+  @Expose()
+  examIdx: number;
+
+  @ApiProperty({
+    example: 1,
+    description: '시험 과목 인덱스',
+  })
+  @Expose()
+  examSubjectIdx: number;
+
+  @ApiProperty({
+    example: 1,
+    description: '시험 과목 번호',
+  })
+  @Expose()
+  examSubjectNum: number;
+
+  @ApiProperty({
+    example: '2023-04-20 23:59:59',
+  })
+  @Expose()
+  createdAt: Date;
+
+  @ApiProperty({
+    example: '2023-04-20 23:59:59',
+  })
+  @Expose()
+  updatedAt: Date;
+
+  @ApiProperty({
+    example: 'N',
+  })
+  @Expose()
+  status: string;
+
+  @ApiProperty({
+    type: ExamSubjectDto,
+    description: '시험 과목 정보',
+  })
+  @Expose()
+  @Type(() => ExamSubjectDto)
+  examSubject?: Partial<ExamSubjectDto>;
+
+  @ApiProperty({
+    type: ExamDto,
+    description: '시험 정보',
+  })
+  @Expose()
+  @Type(() => ExamDto)
+  exam?: Partial<ExamDto>;
+}
diff --git a/src/examsubjectmulti/entities/exam-subject-multi.entity.ts b/src/examsubjectmulti/entities/exam-subject-multi.entity.ts
new file mode 100644
index 0000000..6191889
--- /dev/null
+++ b/src/examsubjectmulti/entities/exam-subject-multi.entity.ts
@@ -0,0 +1,14 @@
+import { ExamEntity } from 'src/exam/entities/exam.entity';
+import { ExamSubjectEntity } from 'src/examsubject/entities/exam-subject.entity';
+import { BaseEntity } from '../../common/entities/base.entity';
+
+export class ExamSubjectMultiEntity
+  extends BaseEntity
+  implements ExamSubjectMultiEntity
+{
+  examIdx: number;
+  examSubjectIdx: number;
+  examSubjectNum: number;
+  examSubject: ExamSubjectEntity;
+  exam?: Partial<ExamEntity>;
+}
diff --git a/src/file/file.controller.ts b/src/file/file.controller.ts
new file mode 100644
index 0000000..f4ee2c9
--- /dev/null
+++ b/src/file/file.controller.ts
@@ -0,0 +1,21 @@
+// src/file/file.controller.ts
+
+import {
+  Controller,
+  Post,
+  UploadedFile,
+  UseInterceptors,
+} from '@nestjs/common';
+import { FileInterceptor } from '@nestjs/platform-express';
+import { FileService } from './file.service';
+
+@Controller('file')
+export class FileController {
+  constructor(private readonly fileService: FileService) {}
+
+  @Post('upload')
+  @UseInterceptors(FileInterceptor('file'))
+  uploadFile(@UploadedFile() file: Express.MulterS3.File) {
+    return this.fileService.uploadFile(file);
+  }
+}
diff --git a/src/file/file.module.ts b/src/file/file.module.ts
new file mode 100644
index 0000000..b719957
--- /dev/null
+++ b/src/file/file.module.ts
@@ -0,0 +1,19 @@
+// import { Module } from '@nestjs/common';
+// import { ConfigModule, ConfigService } from '@nestjs/config';
+// import { MulterModule } from '@nestjs/platform-express';
+// import { multerOptionsFactory } from 'src/common/utils/multer.util';
+// import { FileController } from './file.controller';
+// import { FileService } from './file.service';
+
+// @Module({
+//   imports: [
+//     MulterModule.registerAsync({
+//       imports: [ConfigModule],
+//       useFactory: multerOptionsFactory,
+//       inject: [ConfigService],
+//     }),
+//   ],
+//   controllers: [FileController],
+//   providers: [FileService],
+// })
+// export class FileModule {}
diff --git a/src/file/file.service.ts b/src/file/file.service.ts
new file mode 100644
index 0000000..38ee9cc
--- /dev/null
+++ b/src/file/file.service.ts
@@ -0,0 +1,12 @@
+import { BadRequestException, Injectable } from '@nestjs/common';
+
+@Injectable()
+export class FileService {
+  uploadFile(file: Express.MulterS3.File) {
+    if (!file) {
+      throw new BadRequestException('파일이 존재하지 않습니다.');
+    }
+
+    return { filePath: file.location };
+  }
+}
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..7727bf5
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,58 @@
+import { NestFactory } from '@nestjs/core';
+import { AppModule } from './app.module';
+import run from '../_old/config/express';
+import { HttpExceptionFilter } from './common/filters/http-exception.filter';
+import { UnhandledExceptionFilter } from './common/filters/unhandled-exception.filter';
+import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
+import { ValidationPipe } from '@nestjs/common';
+import { ValidationHttpError } from './common/errors/validation-http-error';
+import { ConfigService } from '@nestjs/config';
+import * as Sentry from '@sentry/node';
+
+async function bootstrap() {
+  const app = await NestFactory.create(AppModule);
+  const oldApp = run();
+
+  Sentry.init({
+    dsn: app.get<ConfigService>(ConfigService).get('SENTRY_DSN'),
+    tracesSampleRate: 1.0,
+  });
+
+  const port = app.get<ConfigService>(ConfigService).get('PORT');
+  app.use(oldApp);
+  app.setGlobalPrefix('apiV2', {
+    exclude: ['/'],
+  });
+  app.useGlobalFilters(
+    new UnhandledExceptionFilter(),
+    new HttpExceptionFilter(),
+  );
+  app.useGlobalPipes(
+    new ValidationPipe({
+      transform: true,
+      exceptionFactory: (errors) => {
+        return new ValidationHttpError(errors);
+      },
+    }),
+  );
+
+
+  if (process.env.NODE_ENV !== 'production') {
+    SwaggerModule.setup(
+      'apiV2/docs',
+      app,
+      SwaggerModule.createDocument(
+        app,
+        new DocumentBuilder()
+          .setTitle('Engineeo API')
+          .setDescription('Engineeo API description')
+          .setVersion('2.0')
+          .build(),
+      ),
+    );
+  }
+
+  await app.listen(port);
+
+}
+bootstrap();
diff --git a/src/payment/entities/payment.entity.ts b/src/payment/entities/payment.entity.ts
new file mode 100644
index 0000000..f9ae83b
--- /dev/null
+++ b/src/payment/entities/payment.entity.ts
@@ -0,0 +1,21 @@
+import { Payment } from '@prisma/client';
+import { BaseEntity } from '../../common/entities/base.entity';
+import { PaymentProductEntity } from '../../paymentProduct/entities/payment-product.entity';
+
+export class PaymentEntity extends BaseEntity implements Payment {
+  paymentProducts?: Partial<PaymentProductEntity>[];
+  checkSum: number;
+  impUid: string | null;
+  level: string;
+  paidAt: Date | null;
+  pay_method: string | null;
+  paymentIdx: string;
+  paymentName: string;
+  pointConsumed: number | null;
+  receipt_url: string | null;
+  refundAt: Date | null;
+  totalCoupon: number | null;
+  totalPoint: number;
+  totalPrice: number;
+  userIdx: number;
+}
diff --git a/src/payment/enums/payment-level.enum.ts b/src/payment/enums/payment-level.enum.ts
new file mode 100644
index 0000000..ded6c4d
--- /dev/null
+++ b/src/payment/enums/payment-level.enum.ts
@@ -0,0 +1,5 @@
+export const enum PaymentLevel {
+  PAID = 'paid',
+  CANCELLED = 'cancelled',
+  POINT_PAID = 'pointPaid',
+}
diff --git a/src/payment/payment.module.ts b/src/payment/payment.module.ts
new file mode 100644
index 0000000..0e757d2
--- /dev/null
+++ b/src/payment/payment.module.ts
@@ -0,0 +1,10 @@
+import { Module } from '@nestjs/common';
+import { PaymentService } from './payment.service';
+
+@Module({
+  imports: [],
+  controllers: [],
+  providers: [PaymentService],
+  exports: [PaymentService],
+})
+export class PaymentModule {}
diff --git a/src/payment/payment.service.ts b/src/payment/payment.service.ts
new file mode 100644
index 0000000..968c0f6
--- /dev/null
+++ b/src/payment/payment.service.ts
@@ -0,0 +1,66 @@
+import { Injectable } from '@nestjs/common';
+import { PrismaService } from '../prisma/prisma.service';
+import { UserPurchaseHistoryQueryDto } from '../user/dtos/query/user-purchase-history-query.dto';
+import { PaginatedDto } from '../common/paginate/paginated.dto';
+import { Status } from '../common/enums/status.enum';
+import { PageMetaDto } from '../common/paginate/page-meta.dto';
+import { PaymentEntity } from './entities/payment.entity';
+
+@Injectable()
+export class PaymentService {
+  constructor(private readonly prismaService: PrismaService) {}
+
+  /**
+   * @desc 구매내역 조회
+   * @param userIdx
+   * @param query UserPurchaseHistoryQueryDto
+   * @returns Promise<>
+   * @author neo
+   */
+  async getPurchaseHistory(
+    userIdx: number,
+    query: UserPurchaseHistoryQueryDto,
+  ): Promise<PaginatedDto<PaymentEntity>> {
+    const totalCount = await this.prismaService.payment.count({
+      where: {
+        userIdx,
+        status: Status.ACTIVE,
+        level: {
+          in: query.status,
+        },
+      },
+    });
+
+    const result = await this.prismaService.payment.findMany({
+      include: {
+        paymentProducts: {
+          select: {
+            productIdx: true,
+            isRefund: true,
+            refundPrice: true,
+          },
+        },
+      },
+      where: {
+        userIdx,
+        status: Status.ACTIVE,
+        level: {
+          in: query.status,
+        },
+      },
+      orderBy: {
+        paidAt: 'desc',
+      },
+      ...(query.pageSize && { take: query.pageSize }),
+      skip:
+        query.page && query.pageSize
+          ? query.pageSize * (query.page - 1)
+          : undefined,
+    });
+
+    //TODO
+    const pageMetaDto = new PageMetaDto(query, totalCount);
+
+    return new PaginatedDto<PaymentEntity>(result, totalCount);
+  }
+}
diff --git a/src/paymentProduct/entities/payment-product.entity.ts b/src/paymentProduct/entities/payment-product.entity.ts
new file mode 100644
index 0000000..2c20601
--- /dev/null
+++ b/src/paymentProduct/entities/payment-product.entity.ts
@@ -0,0 +1,17 @@
+import { PaymentProduct } from '@prisma/client';
+import { BaseEntity } from '../../common/entities/base.entity';
+
+export class PaymentProductEntity extends BaseEntity implements PaymentProduct {
+  finalPrice: number;
+  isRefund: number;
+  paymentIdx: string;
+  paymentProductIdx: number;
+  pg_tid: string | null;
+  productIdx: number;
+  receipt_url: string | null;
+  refundAt: Date | null;
+  refundPrice: number | null;
+  refundReason: string | null;
+  useCoupon: number | null;
+  usePoint: number;
+}
diff --git a/src/paymentProduct/payment-product.module.ts b/src/paymentProduct/payment-product.module.ts
new file mode 100644
index 0000000..c18783e
--- /dev/null
+++ b/src/paymentProduct/payment-product.module.ts
@@ -0,0 +1,9 @@
+import { Module } from '@nestjs/common';
+
+@Module({
+  imports: [],
+  controllers: [],
+  providers: [],
+  exports: [],
+})
+export class PaymentModule {}
diff --git a/src/prisma/prisma.module.ts b/src/prisma/prisma.module.ts
new file mode 100644
index 0000000..7207426
--- /dev/null
+++ b/src/prisma/prisma.module.ts
@@ -0,0 +1,9 @@
+import { Global, Module } from '@nestjs/common';
+import { PrismaService } from './prisma.service';
+
+@Global()
+@Module({
+  providers: [PrismaService],
+  exports: [PrismaService],
+})
+export class PrismaModule {}
diff --git a/src/prisma/prisma.service.ts b/src/prisma/prisma.service.ts
new file mode 100644
index 0000000..0728ce7
--- /dev/null
+++ b/src/prisma/prisma.service.ts
@@ -0,0 +1,11 @@
+import { INestApplication, Injectable } from '@nestjs/common';
+import { PrismaClient } from '@prisma/client';
+
+@Injectable()
+export class PrismaService extends PrismaClient {
+  async enableShutdownHooks(app: INestApplication) {
+    this.$on('beforeExit', async () => {
+      await app.close();
+    });
+  }
+}
diff --git a/src/problem/problem.module.ts b/src/problem/problem.module.ts
new file mode 100644
index 0000000..c9141c9
--- /dev/null
+++ b/src/problem/problem.module.ts
@@ -0,0 +1,4 @@
+import { Module } from '@nestjs/common';
+
+@Module({})
+export class ProblemModule {}
diff --git a/src/product/dtos/res/product.dto.ts b/src/product/dtos/res/product.dto.ts
new file mode 100644
index 0000000..8353b9a
--- /dev/null
+++ b/src/product/dtos/res/product.dto.ts
@@ -0,0 +1,99 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { Expose } from 'class-transformer';
+
+export class ProductDto {
+  @ApiProperty({
+    example: 1,
+    description: '상품 인덱스',
+  })
+  @Expose()
+  productIdx: number;
+
+  @ApiProperty({
+    example: '전기기사',
+    description: '상품 이름',
+  })
+  @Expose()
+  productName: string;
+
+  @ApiProperty({
+    example: 'https://www.google.com',
+    description: '상품 썸네일',
+  })
+  @Expose()
+  productThumbnail: string | null;
+
+  @ApiProperty({
+    example: 'generalDescription',
+    description: '상품 설명',
+  })
+  @Expose()
+  generalDescription: string | null;
+
+  @ApiProperty({
+    example: 'shortDescription',
+    description: '상품 간략 설명',
+  })
+  @Expose()
+  shortDescription: string | null;
+
+  @ApiProperty({
+    example: 'detailDescription',
+    description: '상품 회차 설명',
+  })
+  @Expose()
+  detailDescription: string | null;
+
+  @ApiProperty({
+    example: 10000,
+    description: '상품 가격',
+  })
+  @Expose()
+  price: number | null;
+
+  @ApiProperty({
+    example: 7000,
+    description: '상품 할인 가격',
+  })
+  @Expose()
+  discountPrice: number | null;
+
+  @ApiProperty({
+    example: 50,
+    description: '상품 구독 기간',
+  })
+  @Expose()
+  duration: number | null;
+
+  @ApiProperty({
+    example: 2,
+    description: '상품 depth',
+  })
+  @Expose()
+  depth: number;
+
+  @ApiProperty({
+    example: 1,
+    description: '스토어 상품 정렬 우선순위',
+  })
+  @Expose()
+  prio: number;
+
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  status: string;
+
+  @ApiProperty({
+    example: '2021-01-01T00:00:00.000Z',
+  })
+  @Expose()
+  createdAt: Date;
+
+  @ApiProperty({
+    example: '2021-01-01T00:00:00.000Z',
+  })
+  @Expose()
+  updatedAt: Date;
+}
diff --git a/src/product/entities/product.entity.ts b/src/product/entities/product.entity.ts
new file mode 100644
index 0000000..a553a3f
--- /dev/null
+++ b/src/product/entities/product.entity.ts
@@ -0,0 +1,16 @@
+import { Product } from '@prisma/client';
+import { BaseEntity } from '../../common/entities/base.entity';
+
+export class ProductEntity extends BaseEntity implements Product {
+  depth: number;
+  detailDescription: string | null;
+  discountPrice: number | null;
+  duration: number | null;
+  generalDescription: string | null;
+  price: number | null;
+  prio: number;
+  productIdx: number;
+  productName: string;
+  productThumbnail: string | null;
+  shortDescription: string | null;
+}
diff --git a/src/product/enums/product.enum.ts b/src/product/enums/product.enum.ts
new file mode 100644
index 0000000..c3ae431
--- /dev/null
+++ b/src/product/enums/product.enum.ts
@@ -0,0 +1,4 @@
+export const enum ProductType {
+  'TOP_CATEGORY_PARENT_IDX' = 0,
+  'FREE_PRODUCT_IDX' = 99,
+}
diff --git a/src/product/product.controller.ts b/src/product/product.controller.ts
new file mode 100644
index 0000000..04e0935
--- /dev/null
+++ b/src/product/product.controller.ts
@@ -0,0 +1,45 @@
+import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
+import { ProductService } from './product.service';
+import {
+  ApiHeader,
+  ApiOperation,
+  ApiParam,
+  ApiResponse,
+} from '@nestjs/swagger';
+import { ExamDetailEntity } from '../examDetail/entities/exam-detail.entity';
+import { Serialize } from '../common/interceptors/serialize.interceptor';
+import { ExamDetailDto } from '../examDetail/dtos/res/exam-detail.dto';
+
+@Controller('product')
+export class ProductController {
+  constructor(private readonly productService: ProductService) {}
+
+  @ApiOperation({
+    summary: '상품의 시험 회차들 조회',
+    description: '상품에 권한이 연결된 시험 회차들 조회 @author neo',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiParam({
+    name: 'productIdx',
+    description: '상품 인덱스',
+    required: true,
+    example: 19,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '상품의 시험 회차 조회 성공',
+    isArray: true,
+    type: ExamDetailDto,
+  })
+  @Get(':productIdx/exam-detail')
+  @Serialize(ExamDetailDto)
+  async getExamDetails(
+    @Param('productIdx', ParseIntPipe) productIdx: number,
+  ): Promise<ExamDetailEntity[]> {
+    return this.productService.getProductExamDetailList(productIdx);
+  }
+}
diff --git a/src/product/product.module.ts b/src/product/product.module.ts
new file mode 100644
index 0000000..670240a
--- /dev/null
+++ b/src/product/product.module.ts
@@ -0,0 +1,11 @@
+import { Module } from '@nestjs/common';
+import { ProductService } from './product.service';
+import { ProductController } from './product.controller';
+
+@Module({
+  imports: [],
+  controllers: [ProductController],
+  providers: [ProductService],
+  exports: [ProductService],
+})
+export class ProductModule {}
diff --git a/src/product/product.service.ts b/src/product/product.service.ts
new file mode 100644
index 0000000..6588453
--- /dev/null
+++ b/src/product/product.service.ts
@@ -0,0 +1,105 @@
+import { Injectable } from '@nestjs/common';
+import { PrismaService } from '../prisma/prisma.service';
+import { ProductType } from './enums/product.enum';
+import { ProductEntity } from './entities/product.entity';
+import { Status } from '../common/enums/status.enum';
+import { ExamDetailEntity } from '../examDetail/entities/exam-detail.entity';
+
+@Injectable()
+export class ProductService {
+  constructor(private readonly prismaService: PrismaService) {}
+
+  /**
+   * @desc 상품 정보 조회
+   * @param productIdx
+   * @returns Promise<ProductEntity | null>
+   * @author neo
+   */
+  getProductByIdx(productIdx: number): Promise<ProductEntity | null> {
+    return this.prismaService.product.findFirst({
+      where: {
+        productIdx,
+        status: Status.ACTIVE,
+      },
+    });
+  }
+  /**
+   * @desc 상품의 최상위 카테고리 조회하기 위한 재귀
+   * @param productIdx
+   * @private
+   * @returns Promise<string | undefined>
+   * @author neo
+   */
+  async getProductTopCategory(productIdx: number): Promise<string | undefined> {
+    const productRelation = await this.prismaService.productRelation.findFirst({
+      select: {
+        parentIdx: true,
+      },
+      where: {
+        productIdx,
+      },
+    });
+
+    if (!productRelation) {
+      return;
+    }
+
+    if (productRelation.parentIdx === ProductType.TOP_CATEGORY_PARENT_IDX) {
+      return (
+        await this.prismaService.product.findUnique({
+          select: {
+            productName: true,
+          },
+          where: {
+            productIdx,
+          },
+        })
+      )?.productName;
+    }
+
+    return this.getProductTopCategory(productRelation.parentIdx);
+  }
+
+  //TODO : generated column 사용해서 수정하기
+  /**
+   * @desc 상품에 포함된 시험 상세 목록 조회
+   * @param productIdx
+   * @author neo
+   */
+  async getProductExamDetailList(
+    productIdx: number,
+  ): Promise<ExamDetailEntity[]> {
+    const productExamDetailList = await this.prismaService.examDetail.findMany({
+      where: {
+        productExamDetails: {
+          some: {
+            productIdx,
+          },
+        },
+        status: Status.ACTIVE,
+      },
+    });
+
+    console.log(productExamDetailList);
+    return productExamDetailList;
+
+    // const productExamDetailList = await this.prismaService.$queryRaw<
+    //   ExamDetailEntity[]
+    // >`SELECT e.examIdx,e.subjectiveLabel, ed.examDetailIdx, examName, YEAR(examDate) examYear, date_format(examDate, '%Y.%m.%d') as 'examDate', examRound, e.isNcs
+    //                                    FROM Product p
+    //                                           LEFT JOIN ProductExamDetail ped on ped.productIdx = p.productIdx
+    //                                           LEFT JOIN ExamDetail ed on ed.examDetailIdx = ped.examDetailIdx and ed.status = 'N'
+    //                                           LEFT JOIN Exam e on e.examIdx = ed.examIdx and e.status = 'N'
+    //                                    WHERE p.productIdx = ${productIdx} and p.status = 'N' and ed.isPublic = 1
+    //                                    ORDER BY examDate DESC;`;
+    //
+    // console.log(productExamDetailList);
+    //     const temp = await this.prismaService.$queryRaw<
+    //       ExamDetailEntity[]
+    //     >`SELECT ed.* FROM Product p LEFT JOIN ProductExamDetail ped on ped.productIdx = p.productIdx
+    // LEFT JOIN ExamDetail ed on ed.examDetailIdx = ped.examDetailIdx and ed.status = 'N'
+    // WHERE p.productIdx = ${productIdx} and p.status = 'N' and ed.isPublic = 1`;
+    //
+    //     console.log(temp);
+  }
+}
diff --git a/src/restore/restore.controller.ts b/src/restore/restore.controller.ts
new file mode 100644
index 0000000..ad0457b
--- /dev/null
+++ b/src/restore/restore.controller.ts
@@ -0,0 +1,13 @@
+import { Controller } from '@nestjs/common';
+import { ApiTags } from '@nestjs/swagger';
+import { RestorePostService } from '../restorePost/restore-post.service';
+import { ExamDetailService } from '../examDetail/exam-detail.service';
+
+@ApiTags('restore API')
+@Controller()
+export class RestoreController {
+  constructor(
+    private readonly restorePostService: RestorePostService,
+    private readonly examDetailService: ExamDetailService,
+  ) {}
+}
diff --git a/src/restore/restore.module.ts b/src/restore/restore.module.ts
new file mode 100644
index 0000000..af6c30f
--- /dev/null
+++ b/src/restore/restore.module.ts
@@ -0,0 +1,12 @@
+import { Module } from '@nestjs/common';
+import { RestoreController } from './restore.controller';
+import { RestorePostModule } from '../restorePost/restore-post.module';
+import { ExamDetailModule } from '../examDetail/exam-detail.module';
+
+@Module({
+  imports: [RestorePostModule, ExamDetailModule],
+  controllers: [RestoreController],
+  providers: [],
+  exports: [],
+})
+export class RestoreModule {}
diff --git a/src/restoreComment/dtos/req/create-comment.dto.ts b/src/restoreComment/dtos/req/create-comment.dto.ts
new file mode 100644
index 0000000..e408814
--- /dev/null
+++ b/src/restoreComment/dtos/req/create-comment.dto.ts
@@ -0,0 +1,16 @@
+import { IsNumber, IsOptional, IsString } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class CreateCommentDto {
+  @ApiProperty({
+    example: '제 생각에는 답이 4번같은데요?',
+    description: '댓글 본문',
+  })
+  @IsString()
+  readonly content: string;
+
+  @ApiProperty({ example: 1, description: '부모 댓글의 ID' })
+  @IsOptional()
+  @IsNumber()
+  readonly parentCommentId: number | null;
+}
diff --git a/src/restoreComment/dtos/req/delete-comment.dto.ts b/src/restoreComment/dtos/req/delete-comment.dto.ts
new file mode 100644
index 0000000..3071152
--- /dev/null
+++ b/src/restoreComment/dtos/req/delete-comment.dto.ts
@@ -0,0 +1,12 @@
+import { ArrayNotEmpty, IsArray, ValidateIf } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class DeleteCommentDto {
+  @ApiProperty({
+    example: [1, 2, 3],
+    description: '댓글 id 배열',
+  })
+  @IsArray()
+  @ArrayNotEmpty()
+  readonly commentIds: number[];
+}
diff --git a/src/restoreComment/dtos/req/update-comment.dto.ts b/src/restoreComment/dtos/req/update-comment.dto.ts
new file mode 100644
index 0000000..3444192
--- /dev/null
+++ b/src/restoreComment/dtos/req/update-comment.dto.ts
@@ -0,0 +1,12 @@
+import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class UpdateCommentDto {
+  @ApiProperty({
+    example: '제 생각에는 답이 4번같은데요?',
+    description: '댓글 본문',
+  })
+  @IsNotEmpty()
+  @IsString()
+  readonly content: string;
+}
diff --git a/src/restoreComment/dtos/res/restore-comment.dto.ts b/src/restoreComment/dtos/res/restore-comment.dto.ts
new file mode 100644
index 0000000..fbf9e7e
--- /dev/null
+++ b/src/restoreComment/dtos/res/restore-comment.dto.ts
@@ -0,0 +1,102 @@
+import { ApiProperty, PickType } from '@nestjs/swagger';
+import { Expose, Transform, Type } from 'class-transformer';
+import { IsOptional } from 'class-validator';
+
+import { UserDto } from 'src/user/dtos/res/user.dto';
+export class RestoreCommentDto {
+  @ApiProperty({
+    example: 1,
+    description: '댓글 id',
+  })
+  @Expose()
+  id: number;
+
+  @ApiProperty({
+    example: 4,
+    description: '작성자의 userId',
+  })
+  @Expose()
+  authorId: number;
+
+  @ApiProperty({
+    example: 1,
+    description: '복원 게시글 Id',
+  })
+  @Expose()
+  restorePostId: number;
+
+  @ApiProperty({
+    example: 23,
+    description: '부모 댓글 Id, Null이라면 대댓글이 아닌 일반 댓글',
+  })
+  @Expose()
+  parentCommentId: number | null;
+
+  @ApiProperty({
+    example: '제 생각에는 예상 답변이 잘못된 것 같아요.',
+    description: '댓글 내용',
+  })
+  @Expose()
+  @Transform(({ value, obj }) => (obj.deletedAt ? '삭제된 댓글입니다.' : value))
+  content: string;
+
+  @ApiProperty({
+    example: '2023-04-19 05:43:12',
+    description: '댓글 작성한 시간',
+  })
+  @Expose()
+  createdAt: Date;
+
+  @ApiProperty({
+    example: '2023-04-19 05:43:12',
+    description: '댓글 수정한 시간',
+  })
+  @Expose()
+  updatedAt: Date;
+
+  @ApiProperty({
+    example: '2023-04-19 05:43:12',
+    description: '댓글 삭제한 시간',
+  })
+  @Expose()
+  deletedAt?: Date | null;
+
+  @ApiProperty({
+    example: {
+      id: 3,
+      authorId: 4,
+      restorePostId: 1,
+      parentCommentId: null,
+      content: '자식없어요',
+      createdAt: '2023-04-19T09:00:04.000Z',
+      updatedAt: '2023-04-19T09:00:04.000Z',
+    },
+    description: '자식 댓글들 정보',
+  })
+  @Expose()
+  @IsOptional()
+  @Type(() => RestoreCommentDto)
+  readonly childComments?: RestoreCommentDto[] | null; //naming replies가 나을까요?
+
+  @Expose()
+  @IsOptional()
+  @Type(() => RestoreCommentDto)
+  parentComment?: Partial<RestoreCommentDto> | null;
+
+  @ApiProperty({
+    type: PickType(UserDto, ['nickname']),
+    description: '댓글 작성자 닉네임',
+  })
+  @Expose()
+  @IsOptional()
+  @Type(() => UserDto)
+  readonly author?: Partial<UserDto>;
+
+  @ApiProperty({
+    type: Number,
+    description: '대댓글 개수',
+  })
+  @Expose()
+  @IsOptional()
+  readonly childCommentCount?: number | null;
+}
diff --git a/src/restoreComment/entities/restore-comment.entity.ts b/src/restoreComment/entities/restore-comment.entity.ts
new file mode 100644
index 0000000..8b0256e
--- /dev/null
+++ b/src/restoreComment/entities/restore-comment.entity.ts
@@ -0,0 +1,19 @@
+import { RestoreComment } from '@prisma/client';
+import { RestorePostEntity } from 'src/restorePost/entities/restore-post.entity';
+import { UserEntity } from 'src/user/entities/user.entity';
+import { RestoreCommentDto } from '../dtos/res/restore-comment.dto';
+
+export class RestoreCommentEntity implements RestoreComment {
+  id: number;
+  authorId: number;
+  restorePostId: number;
+  restorePost?: Partial<RestorePostEntity>;
+  parentCommentId: number | null;
+  content: string;
+  createdAt: Date;
+  updatedAt: Date;
+  deletedAt: Date | null;
+  parentComment?: Partial<RestoreCommentEntity> | null;
+  childComments?: RestoreCommentDto[] | null;
+  author?: Partial<UserEntity>;
+}
diff --git a/src/restoreComment/restore-comment-controller.ts b/src/restoreComment/restore-comment-controller.ts
new file mode 100644
index 0000000..22f294d
--- /dev/null
+++ b/src/restoreComment/restore-comment-controller.ts
@@ -0,0 +1,200 @@
+import { AdminGuard } from './../common/guards/admin.guard';
+import { AuthorGuard } from './../common/guards/author.guard';
+import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
+import { CreateCommentDto } from './dtos/req/create-comment.dto';
+import {
+  Body,
+  Controller,
+  Delete,
+  Get,
+  Param,
+  ParseIntPipe,
+  Post,
+  Put,
+  UseGuards,
+} from '@nestjs/common';
+import {
+  ApiBody,
+  ApiHeader,
+  ApiOperation,
+  ApiParam,
+  ApiResponse,
+  ApiTags,
+} from '@nestjs/swagger';
+import { RestoreCommentService } from './restore-comment-service';
+import { Serialize } from 'src/common/interceptors/serialize.interceptor';
+import { CurrentUser } from 'src/user/decorators/current-user.decorator';
+import { UserEntity } from 'src/user/entities/user.entity';
+import { UpdateCommentDto } from './dtos/req/update-comment.dto';
+import { RestoreCommentDto } from './dtos/res/restore-comment.dto';
+import { Author } from 'src/common/decorators/self.decorator';
+import { DeleteCommentDto } from './dtos/req/delete-comment.dto';
+//TODO: 공통되는 라우터 URL 작성
+@ApiTags('restore API')
+@Controller()
+export class RestoreCommentController {
+  constructor(private readonly restoreCommentService: RestoreCommentService) {}
+
+  @ApiOperation({
+    summary: '복원 댓글 조회',
+    description: '게시글 댓글 조회 @author YuuuJeong',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiParam({
+    name: 'id',
+    description: '게시물 id',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '게시글 댓글 조회 성공',
+    type: RestoreCommentDto,
+  })
+  @Get('restore-posts/:id/comments')
+  @UseGuards(JwtAuthGuard)
+  @Serialize(RestoreCommentDto)
+  async getComments(
+    @Param('id', ParseIntPipe) id: number,
+  ): Promise<RestoreCommentDto[]> {
+    const comments = await this.restoreCommentService.getComments(id);
+    return comments;
+  }
+
+  @ApiOperation({
+    summary: '복원 댓글 작성',
+    description: '댓글 달기 @author YuuuJeong',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiBody({
+    type: CreateCommentDto,
+    required: true,
+  })
+  @ApiParam({
+    name: 'id',
+    description: '게시물 id',
+    required: true,
+  })
+  @ApiResponse({
+    status: 201,
+    description: '댓글 작성 성공',
+    type: RestoreCommentDto,
+  })
+  @Post('restore-posts/:id/comment')
+  @UseGuards(JwtAuthGuard)
+  @Serialize(RestoreCommentDto)
+  async createComment(
+    @CurrentUser() user: UserEntity,
+    @Body() createCommentDto: CreateCommentDto,
+    @Param('id', ParseIntPipe) id: number,
+  ): Promise<RestoreCommentDto> {
+    return await this.restoreCommentService.createComment(
+      user.userIdx,
+      id,
+      createCommentDto,
+    );
+  }
+
+  @ApiOperation({
+    summary: '복원 댓글 수정',
+    description: '댓글 수정 @author YuuuJeong',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiBody({
+    type: UpdateCommentDto,
+    required: true,
+  })
+  @ApiParam({
+    name: 'id',
+    description: '댓글 id',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '댓글 수정 성공',
+    type: RestoreCommentDto,
+  })
+  @Put('restore-comments/:id')
+  @UseGuards(JwtAuthGuard)
+  @Serialize(RestoreCommentDto)
+  async updateComment(
+    @CurrentUser() user: UserEntity,
+    @Body() updateCommentDto: UpdateCommentDto,
+    @Param('id', ParseIntPipe) id: number,
+  ): Promise<RestoreCommentDto> {
+    return await this.restoreCommentService.updateComment(
+      user.userIdx,
+      id,
+      updateCommentDto.content,
+    );
+  }
+
+  @ApiOperation({
+    summary: '복원 댓글 삭제',
+    description: '댓글 삭제 , @author YuuuJeong',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '댓글 삭제 성공',
+  })
+  @ApiParam({
+    name: 'id',
+    description: '댓글 id',
+    required: true,
+  })
+  @Delete('restore-comments/:id')
+  @UseGuards(JwtAuthGuard)
+  @Serialize(RestoreCommentDto)
+  async deleteComment(
+    @Param('id', ParseIntPipe) id: number,
+    @CurrentUser() user: UserEntity,
+  ): Promise<any> {
+    return await this.restoreCommentService.deleteComment(user.userIdx, id);
+  }
+
+  //TODO: 기존 댓글 삭제 API와 분리하는 것이 맞을까? 합치는게 맞을까?
+  @ApiOperation({
+    summary: '복원 댓글들 삭제',
+    description: '댓글들 삭제 , @author YuuuJeong',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '댓글 삭제 성공',
+  })
+  @ApiBody({
+    type: DeleteCommentDto,
+    required: true,
+  })
+  @Delete('restore-comments')
+  @UseGuards(AdminGuard)
+  @UseGuards(JwtAuthGuard)
+  @Serialize(DeleteCommentDto)
+  async deleteCommentsByAdmin(
+    @Body() deleteCommentDto: DeleteCommentDto,
+  ): Promise<string> {
+    const deletedCommentCount =
+      await this.restoreCommentService.deleteCommentsByAdmin(deleteCommentDto);
+    return `${deletedCommentCount}개의 댓글이 삭제되었습니다.`;
+  }
+}
diff --git a/src/restoreComment/restore-comment-service.ts b/src/restoreComment/restore-comment-service.ts
new file mode 100644
index 0000000..4e45527
--- /dev/null
+++ b/src/restoreComment/restore-comment-service.ts
@@ -0,0 +1,212 @@
+import { DeleteCommentDto } from './dtos/req/delete-comment.dto';
+import { CreateCommentDto } from './dtos/req/create-comment.dto';
+import { Injectable, BadRequestException } from '@nestjs/common';
+import { PrismaService } from 'src/prisma/prisma.service';
+import { RestorePostService } from 'src/restorePost/restore-post.service';
+import { RestoreCommentEntity } from './entities/restore-comment.entity';
+import { UserService } from 'src/user/user.service';
+import {
+  COMMENT_NOT_FOUND,
+  USER_NOT_AUTHOR,
+} from 'src/common/constants/comment.constant';
+import { UserStatus } from 'src/user/enums/user-status.enum';
+
+@Injectable()
+export class RestoreCommentService {
+  constructor(
+    private readonly prismaService: PrismaService,
+    private readonly restorePostService: RestorePostService,
+    private readonly userService: UserService,
+  ) {}
+
+  public async getCommentById(id) {
+    return await this.prismaService.restoreComment.findUnique({
+      where: {
+        id,
+      },
+    });
+  }
+
+  /**
+   * @desc 복원 댓글들 삭제 - 관리자
+   * @param deleteCommentDto
+   * @returns number
+   * @author YuuuJeong
+   */
+  public async deleteCommentsByAdmin(
+    deleteCommentDto: DeleteCommentDto,
+  ): Promise<number> {
+    const deletedComments = await this.prismaService.restoreComment.updateMany({
+      data: {
+        deletedAt: new Date(),
+      },
+      where: {
+        id: {
+          in: deleteCommentDto.commentIds,
+        },
+      },
+    });
+
+    return deletedComments.count;
+  }
+
+  /**
+   * @desc 복원 댓글 조회
+   * @param restorePostId
+   * @returns GetCommentsResponseDto[]
+   * @author YuuuJeong
+   */
+  public async getComments(
+    restorePostId: number,
+  ): Promise<RestoreCommentEntity[]> {
+    await this.restorePostService.getRestorePost(restorePostId);
+
+    const comments = await this.prismaService.restoreComment.findMany({
+      where: {
+        parentCommentId: null,
+        restorePostId,
+      },
+      include: {
+        childComments: {
+          orderBy: {
+            createdAt: 'asc',
+          },
+          include: {
+            author: {
+              select: {
+                nickname: true,
+              },
+            },
+          },
+        },
+        author: {
+          select: {
+            nickname: true,
+          },
+        },
+      },
+    });
+
+    return comments;
+  }
+
+  /**
+   * @desc 복원 댓글 작성
+   * @param userIdx
+   * @param restorePostId
+   * @param createCommentDto
+   * @returns RestoreCommentEntity
+   * @author YuuuJeong
+   */
+  public async createComment(
+    userIdx: number,
+    restorePostId: number,
+    createCommentDto: CreateCommentDto,
+  ): Promise<RestoreCommentEntity> {
+    await this.restorePostService.getRestorePost(restorePostId);
+    const { content, parentCommentId } = createCommentDto;
+    const comment = await this.prismaService.restoreComment.create({
+      data: {
+        content,
+        parentCommentId,
+        restorePostId,
+        authorId: userIdx,
+      },
+      include: {
+        author: {
+          select: {
+            nickname: true,
+          },
+        },
+      },
+    });
+
+    await this.restorePostService.updatePostCommentCount(restorePostId, 1);
+
+    return comment;
+  }
+
+  /**
+   * @desc 복원 댓글 수정
+   * @param userIdx
+   * @param restorePostId
+   * @param createCommentDto
+   * @returns RestoreCommentEntity
+   * @author YuuuJeong
+   */
+  public async updateComment(
+    userIdx: number,
+    id: number,
+    content: string,
+  ): Promise<RestoreCommentEntity> {
+    const comment = await this.getCommentById(id);
+
+    if (!comment) {
+      throw new BadRequestException(COMMENT_NOT_FOUND);
+    }
+
+    const user = await this.userService.getUserByIdx(userIdx, [
+      UserStatus.USER,
+    ]);
+
+    if (user?.status === UserStatus.USER && userIdx !== comment.authorId) {
+      throw new BadRequestException(USER_NOT_AUTHOR);
+    }
+
+    return this.prismaService.restoreComment.update({
+      where: {
+        id,
+      },
+      data: {
+        content,
+      },
+      include: {
+        author: {
+          select: {
+            nickname: true,
+          },
+        },
+      },
+    });
+  }
+
+  /**
+   * @desc 복원 댓글 삭제
+   * @param userIdx
+   * @param id
+   * @returns RestoreCommentEntity
+   * @author YuuuJeong
+   */
+  public async deleteComment(
+    userIdx: number,
+    id: number,
+  ): Promise<RestoreCommentEntity> {
+    const comment = await this.getCommentById(id);
+
+    if (!comment) {
+      throw new BadRequestException(COMMENT_NOT_FOUND);
+    }
+
+    const user = await this.userService.getUserByIdx(userIdx, [
+      UserStatus.USER,
+    ]);
+
+    if (user?.status === UserStatus.USER && userIdx !== comment.authorId) {
+      throw new BadRequestException(USER_NOT_AUTHOR);
+    }
+    const restorePostId = comment.restorePostId;
+
+    const deletedComment = await this.prismaService.restoreComment.update({
+      where: {
+        id,
+      },
+      data: {
+        deletedAt: new Date(),
+      },
+    });
+
+    await this.restorePostService.updatePostCommentCount(restorePostId, -1);
+
+    return deletedComment;
+  }
+}
diff --git a/src/restoreComment/restore-comment.module.ts b/src/restoreComment/restore-comment.module.ts
new file mode 100644
index 0000000..0ece881
--- /dev/null
+++ b/src/restoreComment/restore-comment.module.ts
@@ -0,0 +1,13 @@
+import { Module } from '@nestjs/common';
+import { RestorePostModule } from 'src/restorePost/restore-post.module';
+import { RestoreCommentController } from './restore-comment-controller';
+import { RestoreCommentService } from './restore-comment-service';
+import { UserModule } from '../user/user.module';
+
+@Module({
+  imports: [RestorePostModule, UserModule],
+  controllers: [RestoreCommentController],
+  providers: [RestoreCommentService],
+  exports: [RestoreCommentService],
+})
+export class RestoreCommentModule {}
diff --git a/src/restorePost/dtos/query/get-restore-posts-query.dto.ts b/src/restorePost/dtos/query/get-restore-posts-query.dto.ts
new file mode 100644
index 0000000..8380e18
--- /dev/null
+++ b/src/restorePost/dtos/query/get-restore-posts-query.dto.ts
@@ -0,0 +1,18 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { PaginateQueryDto } from '../../../common/paginate/paginate-query.dto';
+
+export class GetRestorePostsQueryDto extends PaginateQueryDto {
+  @ApiProperty({
+    example: 'createdAt',
+    enum: ['createdAt', 'recommendCount', 'commentCount'],
+    description: '정렬 기준',
+  })
+  orderField: string = 'createdAt';
+
+  @ApiProperty({
+    example: 'desc',
+    enum: ['asc', 'desc'],
+    description: '정렬 방식',
+  })
+  orderDirection: string = 'desc';
+}
diff --git a/src/restorePost/dtos/req/create-restore-post.dto.ts b/src/restorePost/dtos/req/create-restore-post.dto.ts
new file mode 100644
index 0000000..31d5736
--- /dev/null
+++ b/src/restorePost/dtos/req/create-restore-post.dto.ts
@@ -0,0 +1,41 @@
+import { ApiProperty } from '@nestjs/swagger';
+
+export class CreateRestorePostDto {
+  @ApiProperty({
+    description: '복원 게시글 문제',
+    example: '예시 문제 입니다',
+  })
+  title: string;
+
+  @ApiProperty({
+    description: 'html 태그 붙힌 복원 게시글 문제',
+    example: 'html 태그 예시 문제 입니다',
+  })
+  content: string;
+
+  @ApiProperty({
+    description: '게시글의 의견',
+    example: '복원 게시글의 의견',
+  })
+  opinion: string;
+
+  @ApiProperty({
+    description: '게시글의 예상 답',
+    example: '복원 게시글의 예상 답',
+  })
+  expectedAnswer: string;
+
+  @ApiProperty({
+    description: '게시글의 선지들',
+    isArray: true,
+    example: [{ content: '1번 선지' }, { content: '2번 선지' }],
+  })
+  questions: { content: string }[];
+
+  @ApiProperty({
+    description: '게시글의 첨부파일 id',
+    isArray: true,
+    example: [1],
+  })
+  attachmentIds: number[];
+}
diff --git a/src/restorePost/dtos/res/restore-post.dto.ts b/src/restorePost/dtos/res/restore-post.dto.ts
new file mode 100644
index 0000000..af7f9d4
--- /dev/null
+++ b/src/restorePost/dtos/res/restore-post.dto.ts
@@ -0,0 +1,139 @@
+import { Expose, Type } from 'class-transformer';
+import { ApiProperty } from '@nestjs/swagger';
+import { UserDto } from '../../../user/dtos/res/user.dto';
+import { RestoreQuestionDto } from '../../../restoreQuestion/dtos/res/restore-question.dto';
+import { RestorePostEntity } from '../../entities/restore-post.entity';
+import { AttachmentDto } from '../../../attachment/dtos/res/attachment.dto';
+import { RestoreQuestionEntity } from '../../../restoreQuestion/entities/restore-question.entity';
+import { ExamDetailEntity } from '../../../examDetail/entities/exam-detail.entity';
+import { AttachmentEntity } from '../../../attachment/entities/attachment.entity';
+import { UserEntity } from '../../../user/entities/user.entity';
+import { RestoreCommentEntity } from '../../../restoreComment/entities/restore-comment.entity';
+import { ExamDetailDto } from '../../../examDetail/dtos/res/exam-detail.dto';
+import { RestoreCommentDto } from '../../../restoreComment/dtos/res/restore-comment.dto';
+
+export class RestorePostDto implements Partial<RestorePostEntity> {
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  id: number;
+
+  @ApiProperty({
+    example: 'title',
+  })
+  @Expose()
+  title: string;
+
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  authorId: number;
+
+  @ApiProperty({
+    example: 'content',
+  })
+  @Expose()
+  content: string;
+
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  examDetailIdx: number;
+
+  @ApiProperty({
+    example: 'expectedAnswer',
+  })
+  @Expose()
+  expectedAnswer: string | null;
+
+  @ApiProperty({
+    example: 'opinion',
+  })
+  @Expose()
+  opinion: string | null;
+
+  @ApiProperty({
+    example: 1,
+    description: '추천수',
+  })
+  @Expose()
+  recommendCount: number;
+
+  @ApiProperty({
+    example: 1,
+    description: '댓글수',
+  })
+  @Expose()
+  commentCount: number;
+
+  @ApiProperty({
+    example: '2023-04-19T09:20:28.000Z',
+  })
+  @Expose()
+  createdAt: Date;
+
+  @ApiProperty({
+    example: '2023-04-19T09:20:28.000Z',
+  })
+  @Expose()
+  updatedAt: Date;
+
+  // @ApiProperty({
+  //   example: '2023-04-19T09:20:28.000Z',
+  //   description: '승인시간',
+  // })
+  // @Expose()
+  // approvedAt: Date | null;
+
+  @ApiProperty({
+    type: UserDto,
+  })
+  @Expose()
+  @Type(() => UserDto)
+  author?: Partial<UserEntity>;
+
+  @ApiProperty({
+    type: ExamDetailDto,
+  })
+  @Expose()
+  @Type(() => ExamDetailDto)
+  examDetail?: Partial<ExamDetailEntity>;
+
+  @ApiProperty({
+    type: RestoreCommentDto,
+  })
+  @Expose()
+  @Type(() => RestoreCommentDto)
+  restoreComments?: Partial<RestoreCommentEntity>[];
+
+  @ApiProperty({
+    type: RestoreQuestionDto,
+    isArray: true,
+  })
+  @Expose()
+  @Type(() => RestoreQuestionDto)
+  restoreQuestions?: Partial<RestoreQuestionEntity>[];
+
+  @ApiProperty({
+    type: AttachmentDto,
+    isArray: true,
+  })
+  @Expose()
+  @Type(() => AttachmentDto)
+  attachments?: Partial<AttachmentEntity>[];
+
+  @ApiProperty({
+    type: Boolean,
+  })
+  @Expose()
+  isRecommend?: boolean | null;
+
+  @ApiProperty({
+    type: Boolean,
+  })
+  @Expose()
+  viewerHasLike?: boolean | null;
+}
diff --git a/src/restorePost/dtos/res/restore-posts-response.dto.ts b/src/restorePost/dtos/res/restore-posts-response.dto.ts
new file mode 100644
index 0000000..4a616ba
--- /dev/null
+++ b/src/restorePost/dtos/res/restore-posts-response.dto.ts
@@ -0,0 +1,20 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { Expose, Type } from 'class-transformer';
+import { RestorePostDto } from './restore-post.dto';
+
+export class RestorePostsResponseDto {
+  @ApiProperty({
+    type: RestorePostDto,
+    isArray: true,
+  })
+  @Type(() => RestorePostDto)
+  @Expose()
+  nodes: Partial<RestorePostDto>[];
+
+  @ApiProperty({
+    example: 30,
+    description: '총 복원 게시글 수',
+  })
+  @Expose()
+  totalCount: number;
+}
diff --git a/src/restorePost/entities/restore-post.entity.ts b/src/restorePost/entities/restore-post.entity.ts
new file mode 100644
index 0000000..671ead0
--- /dev/null
+++ b/src/restorePost/entities/restore-post.entity.ts
@@ -0,0 +1,27 @@
+import { RestorePost } from '@prisma/client';
+import { RestoreQuestionEntity } from '../../restoreQuestion/entities/restore-question.entity';
+import { UserEntity } from '../../user/entities/user.entity';
+import { ExamDetailEntity } from '../../examDetail/entities/exam-detail.entity';
+import { AttachmentEntity } from '../../attachment/entities/attachment.entity';
+import { RestoreCommentEntity } from 'src/restoreComment/entities/restore-comment.entity';
+
+export class RestorePostEntity implements RestorePost {
+  approvedAt: Date | null;
+  authorId: number;
+  commentCount: number;
+  content: string;
+  createdAt: Date;
+  deletedAt: Date | null;
+  examDetailIdx: number;
+  expectedAnswer: string | null;
+  id: number;
+  opinion: string | null;
+  recommendCount: number;
+  title: string;
+  updatedAt: Date;
+  author?: Partial<UserEntity>;
+  examDetail?: Partial<ExamDetailEntity>;
+
+  restoreComments?: Partial<RestoreCommentEntity>[];
+  restoreQuestions?: Partial<RestoreQuestionEntity>[];
+}
diff --git a/src/restorePost/restore-post.controller.ts b/src/restorePost/restore-post.controller.ts
new file mode 100644
index 0000000..82a265d
--- /dev/null
+++ b/src/restorePost/restore-post.controller.ts
@@ -0,0 +1,222 @@
+import { RestorePostService } from './restore-post.service';
+import {
+  ApiBody,
+  ApiHeader,
+  ApiOperation,
+  ApiParam,
+  ApiQuery,
+  ApiResponse,
+  ApiTags,
+} from '@nestjs/swagger';
+import { CreateRestorePostDto } from './dtos/req/create-restore-post.dto';
+import { RestorePostDto } from './dtos/res/restore-post.dto';
+import {
+  Body,
+  Controller,
+  Delete,
+  Get,
+  Param,
+  ParseIntPipe,
+  Post,
+  Put,
+  Query,
+  UseGuards,
+} from '@nestjs/common';
+import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
+import { Serialize } from '../common/interceptors/serialize.interceptor';
+import { CurrentUser } from '../user/decorators/current-user.decorator';
+import { UserEntity } from '../user/entities/user.entity';
+import { RestorePostsResponseDto } from './dtos/res/restore-posts-response.dto';
+import { GetRestorePostsQueryDto } from './dtos/query/get-restore-posts-query.dto';
+import { RestoreRecommendLogService } from 'src/restoreRecommendLog/restore-recommend-log.service';
+import { RestoreRecommendLogEntity } from 'src/restoreRecommendLog/entities/restore-recommend-log.entity';
+import { PaginateQueryDto } from 'src/common/paginate/paginate-query.dto';
+
+@ApiTags('restore-post API')
+@Controller()
+export class RestorePostController {
+  constructor(
+    private readonly restorePostService: RestorePostService,
+    private readonly restoreRecommendLogService: RestoreRecommendLogService,
+  ) {}
+
+  @ApiOperation({
+    summary: '복원 게시글 리스트 조회',
+    description: '복원 게시글 리스트 조회 @author neo',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiParam({
+    name: 'examDetailIdx',
+    description: '시험 회차의 인덱스',
+    required: true,
+  })
+  @ApiQuery({
+    type: PaginateQueryDto,
+    description: '페이지 네이션 메타데이터',
+    required: false,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '복원 게시글 리스트 조회 성공',
+    type: RestorePostsResponseDto,
+  })
+  @UseGuards(JwtAuthGuard)
+  @Serialize(RestorePostsResponseDto)
+  @Get('exam-details/:examDetailIdx/restore-posts')
+  async getRestorePosts(
+    @CurrentUser() user: UserEntity,
+    @Param('examDetailIdx') examDetailIdx: number,
+    @Query() query: GetRestorePostsQueryDto,
+  ): Promise<RestorePostsResponseDto> {
+    return this.restorePostService.getRestorePosts(examDetailIdx, query);
+  }
+
+  @ApiOperation({
+    summary: '복원 게시글 조회',
+    description: '복원 게시글 조회 @author neo',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiParam({
+    name: 'id',
+    description: '복원 게시글 인덱스',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '복원 게시글 조회 성공',
+    type: RestorePostDto,
+  })
+  @UseGuards(JwtAuthGuard)
+  @Get('restore-posts/:id')
+  async getRestorePost(
+    @CurrentUser() user: UserEntity,
+    @Param('id', ParseIntPipe) id: number,
+  ): Promise<RestorePostDto> {
+    const post = await this.restorePostService.getRestorePost(id);
+
+    const userPostFeedback: RestoreRecommendLogEntity | null =
+      await this.restoreRecommendLogService.getUserRecommendationLog(
+        user.userIdx,
+        id,
+      );
+
+    post['isRecommend'] =
+      userPostFeedback && userPostFeedback.amount > 0 ? true : false;
+
+    post['viewerHasLike'] =
+      userPostFeedback && userPostFeedback.amount > 0 ? true : false;
+    return post;
+  }
+
+  @ApiOperation({
+    summary: '복원 게시글 등록',
+    description: '복원 게시글 등록 @author neo',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiBody({
+    type: CreateRestorePostDto,
+    required: true,
+  })
+  @ApiParam({
+    name: 'examDetailIdx',
+    description: '복원 게시글을 등록할 시험 회차 인덱스',
+    required: true,
+  })
+  @ApiResponse({
+    status: 201,
+    description: '복원 게시글 등록 성공',
+    type: RestorePostDto,
+  })
+  @UseGuards(JwtAuthGuard)
+  @Post('exam-details/:examDetailIdx/restore-post')
+  async createRestorePost(
+    @CurrentUser() user: UserEntity,
+    @Param('examDetailIdx') examDetailIdx: number,
+    @Body() createRestorePostDto: CreateRestorePostDto,
+  ): Promise<RestorePostDto> {
+    return this.restorePostService.createRestorePost(
+      examDetailIdx,
+      user.userIdx,
+      createRestorePostDto,
+    );
+  }
+
+  @ApiOperation({
+    summary: '복원 게시글 수정',
+    description: '복원 게시글 수정 @author neo',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiBody({
+    type: CreateRestorePostDto,
+    required: true,
+  })
+  @ApiParam({
+    name: 'id',
+    description: '복원 게시글 인덱스',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '복원 게시글 수정 성공',
+    type: RestorePostDto,
+  })
+  @Serialize(RestorePostDto)
+  @UseGuards(JwtAuthGuard)
+  @Put('restore-posts/:id')
+  async updateRestorePost(
+    @CurrentUser() user: UserEntity,
+    @Param('id') id: number,
+    @Body() createRestorePostDto: CreateRestorePostDto,
+  ): Promise<RestorePostDto> {
+    return this.restorePostService.updateRestorePost(
+      user.userIdx,
+      id,
+      createRestorePostDto,
+    );
+  }
+
+  @ApiOperation({
+    summary: '복원 게시글 삭제',
+    description: '복원 게시글 삭제 @author neo',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiParam({
+    name: 'id',
+    description: '복원 게시글 인덱스',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '복원 게시글 삭제 성공',
+    type: RestorePostDto,
+  })
+  @UseGuards(JwtAuthGuard)
+  @Serialize(RestorePostDto)
+  @Delete('restore-posts/:id')
+  async deleteRestorePost(
+    @CurrentUser() user: UserEntity,
+    @Param('id', ParseIntPipe) id: number,
+  ): Promise<RestorePostDto> {
+    return this.restorePostService.deleteRestorePost(user.userIdx, id);
+  }
+}
diff --git a/src/restorePost/restore-post.module.ts b/src/restorePost/restore-post.module.ts
new file mode 100644
index 0000000..9f27715
--- /dev/null
+++ b/src/restorePost/restore-post.module.ts
@@ -0,0 +1,18 @@
+import { AttachedAttachmentModule } from './../attachedAttachment/attached-attachment.module';
+import { Module, forwardRef } from '@nestjs/common';
+import { RestorePostService } from './restore-post.service';
+import { RestorePostController } from './restore-post.controller';
+import { RestoreRecommendLogModule } from 'src/restoreRecommendLog/restore-recommend-log.module';
+import { UserModule } from '../user/user.module';
+
+@Module({
+  imports: [
+    forwardRef(() => RestoreRecommendLogModule),
+    AttachedAttachmentModule,
+    UserModule,
+  ],
+  controllers: [RestorePostController],
+  providers: [RestorePostService],
+  exports: [RestorePostService],
+})
+export class RestorePostModule {}
diff --git a/src/restorePost/restore-post.service.ts b/src/restorePost/restore-post.service.ts
new file mode 100644
index 0000000..32bb5eb
--- /dev/null
+++ b/src/restorePost/restore-post.service.ts
@@ -0,0 +1,340 @@
+import { AttachedAttachmentService } from './../attachedAttachment/attached-attachment.service';
+import { BadRequestException, Injectable } from '@nestjs/common';
+import { PrismaService } from '../prisma/prisma.service';
+import { CreateRestorePostDto } from './dtos/req/create-restore-post.dto';
+import { RestorePostEntity } from './entities/restore-post.entity';
+import { GetRestorePostsQueryDto } from './dtos/query/get-restore-posts-query.dto';
+import { RestorePostDto } from './dtos/res/restore-post.dto';
+import { AttachmentModel } from 'src/attachment/enums/attachment-model.enum';
+import { UserService } from 'src/user/user.service';
+import { UserStatus } from 'src/user/enums/user-status.enum';
+
+@Injectable()
+export class RestorePostService {
+  constructor(
+    private readonly prismaService: PrismaService,
+    private readonly attachedAttachmentService: AttachedAttachmentService,
+    private readonly userService: UserService,
+  ) {}
+
+  async updatePostRecommendCount(id: number, amount: number) {
+    return await this.prismaService.restorePost.update({
+      where: {
+        id,
+      },
+      data: {
+        recommendCount: {
+          increment: amount,
+        },
+      },
+    });
+  }
+
+  async updatePostCommentCount(id: number, amount: number) {
+    return await this.prismaService.restorePost.update({
+      where: {
+        id,
+      },
+      data: {
+        commentCount: {
+          increment: amount,
+        },
+      },
+    });
+  }
+
+  /**
+   * 복원 게시글 id 로 조회
+   * 선지와 함께 조회
+   * @param id
+   * @returns Promise<RestorePostEntity>
+   *
+   * @throws 존재하지 않는 복원 게시글입니다
+   * @author neo
+   */
+
+  async getRestorePost(id: number): Promise<RestorePostDto> {
+    const restorePost = await this.prismaService.restorePost.findUnique({
+      where: {
+        id,
+      },
+      include: {
+        restoreQuestions: true,
+        author: {
+          select: {
+            nickname: true,
+          },
+        },
+      },
+    });
+
+    if (!restorePost) {
+      throw new BadRequestException('존재하지 않는 복원 게시글입니다');
+    }
+
+    const postAttachments =
+      await this.prismaService.attached_Attachment.findMany({
+        where: {
+          attached: `${AttachmentModel.RESTORE_POST}_${restorePost.id}`,
+        },
+        select: {
+          attachmentId: true,
+        },
+      });
+
+    const attachmentIds = postAttachments.map(
+      (postAttachment) => postAttachment.attachmentId,
+    );
+
+    const attachments = await this.prismaService.attachment.findMany({
+      where: {
+        id: {
+          in: attachmentIds,
+        },
+      },
+    });
+
+    restorePost['attachments'] = attachments;
+
+    return restorePost;
+  }
+
+  /**
+   * 복원 게시글 목록 조회
+   * 복원 선지와 함께 조회
+   *
+   * @param examDetailIdx
+   * @param query GetRestorePostsQueryDto
+   * @returns Promise<RestorePostEntity[]>
+   *
+   * @author neo
+   */
+  async getRestorePosts(
+    examDetailIdx: number,
+    query: GetRestorePostsQueryDto,
+  ): Promise<{ nodes: RestorePostEntity[]; totalCount: number }> {
+    const totalCount = await this.prismaService.restorePost.count({
+      where: {
+        examDetailIdx,
+        deletedAt: null,
+      },
+    });
+
+    const nodes = await this.prismaService.restorePost.findMany({
+      where: {
+        examDetailIdx,
+        deletedAt: null,
+      },
+      include: {
+        author: {
+          select: {
+            nickname: true,
+          },
+        },
+      },
+      orderBy: {
+        [query.orderField]: query.orderDirection,
+      },
+      skip:
+        query.page && query.pageSize
+          ? query.pageSize * (query.page - 1)
+          : undefined,
+      ...(query.pageSize && { take: query.pageSize }),
+    });
+
+    return { nodes, totalCount };
+  }
+
+  /**
+   * 복원 게시글 등록
+   * @param examDetailIdx
+   * @param authorId
+   * @param createRestorePostDto
+   *
+   * @returns Promise<RestorePost>
+   * @author neo
+   */
+  async createRestorePost(
+    examDetailIdx: number,
+    authorId: number,
+    createRestorePostDto: CreateRestorePostDto,
+  ): Promise<RestorePostDto> {
+    const restorePost = await this.prismaService.restorePost.create({
+      data: {
+        examDetailIdx,
+        authorId,
+        title: createRestorePostDto.title,
+        content: createRestorePostDto.content,
+        opinion: createRestorePostDto.opinion,
+        expectedAnswer: createRestorePostDto.expectedAnswer,
+        restoreQuestions: {
+          createMany: {
+            data: createRestorePostDto.questions.map((question) => ({
+              content: question.content,
+            })),
+          },
+        },
+      },
+      include: {
+        restoreQuestions: true,
+      },
+    });
+
+    const attachmentData = createRestorePostDto.attachmentIds.map(
+      (attachmentId) => ({
+        attached: `${AttachmentModel.RESTORE_POST}_${restorePost.id}`,
+        attachmentId,
+      }),
+    );
+
+    await this.prismaService.attached_Attachment.createMany({
+      data: attachmentData,
+    });
+
+    const attachments = await this.prismaService.attachment.findMany({
+      where: {
+        id: {
+          in: createRestorePostDto.attachmentIds,
+        },
+      },
+    });
+
+    restorePost['attachments'] = attachments;
+
+    return restorePost;
+  }
+
+  /**
+   * 복원 게시글 수정
+   * 연결된 선지 다 지우고 다시 생성
+   * 이미지 연결 다시 생성
+   * @param authorId
+   * @param id
+   * @param createRestorePostDto
+   *
+   * @returns Promise<RestorePost>
+   *
+   * @throws 게시글 작성자만 수정할 수 있습니다.
+   *
+   * @author neo
+   */
+  async updateRestorePost(
+    authorId: number,
+    id: number,
+    createRestorePostDto: CreateRestorePostDto,
+  ): Promise<RestorePostDto> {
+    const restorePost = await this.getRestorePost(id);
+
+    const user = await this.userService.getUserByIdx(authorId, [
+      UserStatus.USER,
+    ]);
+
+    if (user?.status === UserStatus.USER && restorePost.authorId !== authorId) {
+      throw new BadRequestException('게시글 작성자만 수정할 수 있습니다.');
+    }
+
+    await this.attachedAttachmentService.deleteAttachedFiles(
+      AttachmentModel.RESTORE_POST,
+      id,
+    );
+
+    const attachmentData = createRestorePostDto.attachmentIds.map(
+      (attachmentId) => ({
+        attached: `${AttachmentModel.RESTORE_POST}_${restorePost.id}`,
+        attachmentId,
+      }),
+    );
+
+    await this.prismaService.attached_Attachment.createMany({
+      data: attachmentData,
+    });
+
+    const post = await this.prismaService.restorePost.update({
+      where: {
+        id,
+      },
+      data: {
+        title: createRestorePostDto.title,
+        content: createRestorePostDto.content,
+        opinion: createRestorePostDto.opinion,
+        expectedAnswer: createRestorePostDto.expectedAnswer,
+        restoreQuestions: {
+          deleteMany: {
+            restorePostId: id,
+          },
+          createMany: {
+            data: createRestorePostDto.questions.map((question) => ({
+              content: question.content,
+            })),
+          },
+        },
+      },
+      include: {
+        restoreQuestions: true,
+      },
+    });
+
+    const attachments = await this.prismaService.attachment.findMany({
+      where: {
+        id: {
+          in: createRestorePostDto.attachmentIds,
+        },
+      },
+    });
+
+    restorePost['attachments'] = attachments;
+
+    return post;
+  }
+
+  /**
+   * 복원 게시글 삭제
+   * @param authorId
+   * @param id
+   *
+   * @returns Promise<RestorePost>
+   *
+   * @throws 게시글 작성자만 삭제할 수 있습니다.
+   * @author neo
+   */
+  async deleteRestorePost(
+    authorId: number,
+    id: number,
+  ): Promise<RestorePostEntity> {
+    const restorePost = await this.getRestorePost(id);
+
+    const user = await this.userService.getUserByIdx(authorId, [
+      UserStatus.USER,
+    ]);
+
+    if (user?.status === UserStatus.USER && restorePost.authorId !== authorId) {
+      throw new BadRequestException('게시글 작성자만 삭제할 수 있습니다.');
+    }
+
+    await this.prismaService.restoreQuestion.deleteMany({
+      where: {
+        restorePostId: id,
+      },
+    });
+
+    return await this.prismaService.restorePost.update({
+      where: {
+        id,
+      },
+      data: {
+        deletedAt: new Date(),
+      },
+    });
+  }
+
+  async approvePost(id): Promise<RestorePostEntity> {
+    return await this.prismaService.restorePost.update({
+      where: {
+        id,
+      },
+      data: {
+        approvedAt: new Date(),
+      },
+    });
+  }
+}
diff --git a/src/restoreQuestion/dtos/res/restore-question.dto.ts b/src/restoreQuestion/dtos/res/restore-question.dto.ts
new file mode 100644
index 0000000..c4e4603
--- /dev/null
+++ b/src/restoreQuestion/dtos/res/restore-question.dto.ts
@@ -0,0 +1,49 @@
+import { Expose, Type } from 'class-transformer';
+import { RestorePostDto } from '../../../restorePost/dtos/res/restore-post.dto';
+import { ApiProperty } from '@nestjs/swagger';
+import { RestoreQuestionEntity } from '../../entities/restore-question.entity';
+import { RestorePostEntity } from '../../../restorePost/entities/restore-post.entity';
+
+export class RestoreQuestionDto implements Partial<RestoreQuestionEntity> {
+  @ApiProperty({
+    example: 1,
+    description: '선지 인덱스',
+  })
+  @Expose()
+  id: number;
+
+  @ApiProperty({
+    example: 1,
+    description: '복원 게시글 인덱스',
+  })
+  @Expose()
+  restorePostId: number;
+
+  @ApiProperty({
+    example: '1번 선지',
+    description: '선지 내용',
+  })
+  @Expose()
+  content: string;
+
+  @ApiProperty({
+    example: '2023-04-19T09:20:28.000Z',
+    description: '생성일',
+  })
+  @Expose()
+  createdAt: Date;
+
+  @ApiProperty({
+    example: '2023-04-19T09:20:28.000Z',
+    description: '수정일',
+  })
+  @Expose()
+  updatedAt: Date;
+
+  @ApiProperty({
+    type: RestorePostDto,
+  })
+  @Type(() => RestorePostDto)
+  @Expose()
+  restorePost?: Partial<RestorePostEntity>;
+}
diff --git a/src/restoreQuestion/entities/restore-question.entity.ts b/src/restoreQuestion/entities/restore-question.entity.ts
new file mode 100644
index 0000000..3800756
--- /dev/null
+++ b/src/restoreQuestion/entities/restore-question.entity.ts
@@ -0,0 +1,12 @@
+import { RestoreQuestion } from '@prisma/client';
+import { RestorePostEntity } from '../../restorePost/entities/restore-post.entity';
+
+export class RestoreQuestionEntity implements RestoreQuestion {
+  id: number;
+  restorePostId: number;
+  content: string;
+  createdAt: Date;
+  updatedAt: Date;
+
+  restorePost?: Partial<RestorePostEntity>;
+}
diff --git a/src/restoreRecommendLog/entities/restore-recommend-log.entity.ts b/src/restoreRecommendLog/entities/restore-recommend-log.entity.ts
new file mode 100644
index 0000000..303355a
--- /dev/null
+++ b/src/restoreRecommendLog/entities/restore-recommend-log.entity.ts
@@ -0,0 +1,9 @@
+import { RestoreRecommendLog } from '@prisma/client';
+
+export class RestoreRecommendLogEntity implements RestoreRecommendLog {
+  id: number;
+  userId: number;
+  restorePostId: number;
+  amount: number;
+  createdAt: Date;
+}
diff --git a/src/restoreRecommendLog/restore-recommend-log.controller.ts b/src/restoreRecommendLog/restore-recommend-log.controller.ts
new file mode 100644
index 0000000..bff513b
--- /dev/null
+++ b/src/restoreRecommendLog/restore-recommend-log.controller.ts
@@ -0,0 +1,73 @@
+import { RestoreRecommendLogEntity } from './entities/restore-recommend-log.entity';
+import { RestoreRecommendLogService } from './restore-recommend-log.service';
+import {
+  Controller,
+  Delete,
+  Param,
+  ParseIntPipe,
+  Post,
+  UseGuards,
+} from '@nestjs/common';
+import { ApiHeader, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
+import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
+import { CurrentUser } from 'src/user/decorators/current-user.decorator';
+import { UserEntity } from 'src/user/entities/user.entity';
+
+@Controller()
+@ApiTags('restore-recommend API')
+export class RestoreRecommendLogController {
+  constructor(
+    private readonly restoreRecommendLogService: RestoreRecommendLogService,
+  ) {}
+  @ApiOperation({
+    summary: '게시물 추천',
+    description: '게시물 추천 @author YuuuJeong',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiResponse({
+    status: 201,
+    description: '유저 게시물 좋아요 성공',
+    type: RestoreRecommendLogEntity,
+  })
+  @Post('restore-posts/:id/recommend')
+  @UseGuards(JwtAuthGuard)
+  async recommendPost(
+    @CurrentUser() user: UserEntity,
+    @Param('id', ParseIntPipe) id: number,
+  ): Promise<RestoreRecommendLogEntity | null> {
+    return await this.restoreRecommendLogService.recommendPost(
+      user.userIdx,
+      id,
+    );
+  }
+
+  @ApiOperation({
+    summary: '게시물 비추천',
+    description: '게시물 비추천 @author YuuuJeong',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '유저 게시물 싫어요',
+    type: RestoreRecommendLogEntity,
+  })
+  @Post('restore-posts/:id/dis-recommend')
+  @UseGuards(JwtAuthGuard)
+  async disRecommendPost(
+    @CurrentUser() user: UserEntity,
+    @Param('id', ParseIntPipe) id: number,
+  ): Promise<RestoreRecommendLogEntity> {
+    return await this.restoreRecommendLogService.disRecommendPost(
+      user.userIdx,
+      id,
+    );
+  }
+}
diff --git a/src/restoreRecommendLog/restore-recommend-log.module.ts b/src/restoreRecommendLog/restore-recommend-log.module.ts
new file mode 100644
index 0000000..8ba9de4
--- /dev/null
+++ b/src/restoreRecommendLog/restore-recommend-log.module.ts
@@ -0,0 +1,12 @@
+import { Module, forwardRef } from '@nestjs/common';
+import { RestorePostModule } from 'src/restorePost/restore-post.module';
+import { RestoreRecommendLogController } from './restore-recommend-log.controller';
+import { RestoreRecommendLogService } from './restore-recommend-log.service';
+
+@Module({
+  imports: [forwardRef(() => RestorePostModule)],
+  providers: [RestoreRecommendLogService],
+  exports: [RestoreRecommendLogService],
+  controllers: [RestoreRecommendLogController],
+})
+export class RestoreRecommendLogModule {}
diff --git a/src/restoreRecommendLog/restore-recommend-log.service.ts b/src/restoreRecommendLog/restore-recommend-log.service.ts
new file mode 100644
index 0000000..ecf4850
--- /dev/null
+++ b/src/restoreRecommendLog/restore-recommend-log.service.ts
@@ -0,0 +1,126 @@
+import { Injectable } from '@nestjs/common';
+import { PostRecommendAmount } from 'src/common/enums/post-recommend-amount.enum';
+import { PrismaService } from 'src/prisma/prisma.service';
+import { RestorePostService } from 'src/restorePost/restore-post.service';
+import { RestoreRecommendLogEntity } from './entities/restore-recommend-log.entity';
+
+@Injectable()
+export class RestoreRecommendLogService {
+  constructor(
+    private readonly prismaService: PrismaService,
+    private readonly restorePostService: RestorePostService,
+  ) {}
+
+  public async getUserRecommendationLog(
+    userId: number,
+    restorePostId: number,
+  ): Promise<RestoreRecommendLogEntity | null> {
+    return await this.prismaService.restoreRecommendLog.findFirst({
+      where: {
+        userId,
+        restorePostId,
+      },
+      orderBy: {
+        createdAt: 'desc',
+      },
+    });
+  }
+
+  public createRecommendLog(
+    userId: number,
+    restorePostId: number,
+    recommendAmount: number,
+  ): Promise<RestoreRecommendLogEntity> {
+    return this.prismaService.restoreRecommendLog.create({
+      data: {
+        userId,
+        restorePostId,
+        amount: recommendAmount ?? PostRecommendAmount.RECOMMEND,
+      },
+    });
+  }
+  /**
+   * 게시글 추천
+   *
+   * @param userId
+   * @param restorePostId
+   *
+   * @returns : Promise<RestoreRecommendLogEntity>
+   *
+   * @author YuuuJeong
+   */
+  public async recommendPost(
+    userId: number,
+    restorePostId: number,
+  ): Promise<RestoreRecommendLogEntity | null> {
+    const userPostRecommendLatestLog = await this.getUserRecommendationLog(
+      userId,
+      restorePostId,
+    );
+
+    let recommendAmount;
+
+    if (userPostRecommendLatestLog) {
+      if (
+        Date.now() - new Date(userPostRecommendLatestLog.createdAt).getTime() <
+        1500
+      ) {
+        return null;
+      }
+
+      recommendAmount =
+        userPostRecommendLatestLog.amount > 0
+          ? PostRecommendAmount.DISRECOMMEND
+          : PostRecommendAmount.RECOMMEND;
+    }
+
+    await this.restorePostService.updatePostRecommendCount(
+      restorePostId,
+      recommendAmount ?? PostRecommendAmount.RECOMMEND,
+    );
+
+    return await this.createRecommendLog(
+      userId,
+      restorePostId,
+      recommendAmount ?? PostRecommendAmount.RECOMMEND,
+    );
+  }
+
+  /**
+   * 게시글 비추천(당장은 사용x)
+   *
+   * @param userId
+   * @param restorePostId
+   *
+   * @returns : Promise<RestoreRecommendLogEntity>
+   *
+   * @author YuuuJeong
+   */
+  public async disRecommendPost(
+    userId: number,
+    restorePostId: number,
+  ): Promise<RestoreRecommendLogEntity> {
+    const userPostDisRecommendLogs =
+      await this.prismaService.restoreRecommendLog.findMany({
+        where: {
+          userId,
+          amount: PostRecommendAmount.RECOMMEND,
+        },
+      });
+
+    if (userPostDisRecommendLogs.length === 0) {
+      await this.restorePostService.updatePostRecommendCount(
+        restorePostId,
+        PostRecommendAmount.DISRECOMMEND,
+      );
+    }
+
+    return await this.prismaService.restoreRecommendLog.create({
+      data: {
+        userId,
+        restorePostId,
+        amount: PostRecommendAmount.DISRECOMMEND,
+      },
+    });
+  }
+}
diff --git a/src/s3-manager/s3-manager.module.ts b/src/s3-manager/s3-manager.module.ts
new file mode 100644
index 0000000..a286d73
--- /dev/null
+++ b/src/s3-manager/s3-manager.module.ts
@@ -0,0 +1,10 @@
+// s3-manager.module.ts
+import { Module } from '@nestjs/common';
+import { S3ManagerService } from './s3-manager.service';
+
+@Module({
+  imports: [],
+  providers: [S3ManagerService],
+  exports: [S3ManagerService],
+})
+export class S3ManagerModule {}
diff --git a/src/s3-manager/s3-manager.service.ts b/src/s3-manager/s3-manager.service.ts
new file mode 100644
index 0000000..808dadc
--- /dev/null
+++ b/src/s3-manager/s3-manager.service.ts
@@ -0,0 +1,30 @@
+// s3-manager.service.ts
+import { Injectable } from '@nestjs/common';
+import { InjectAwsService } from 'nest-aws-sdk';
+import { S3 } from 'aws-sdk';
+
+@Injectable()
+export class S3ManagerService {
+  constructor(@InjectAwsService(S3) private readonly s3: S3) {}
+
+  /**
+   * generate presigned url based on operation and params
+   * @param operation
+   * @param params
+   *
+   * @author neo
+   */
+  public getSignedUrlPromise(operation: string, params: any) {
+    return this.s3.getSignedUrlPromise(operation, params);
+  }
+
+  /**
+   * check if object exists
+   * @param params
+   *
+   * @author neo
+   */
+  public headObject(params: any) {
+    return this.s3.headObject(params);
+  }
+}
diff --git a/src/store/store.module.ts b/src/store/store.module.ts
new file mode 100644
index 0000000..11f13e7
--- /dev/null
+++ b/src/store/store.module.ts
@@ -0,0 +1,4 @@
+import { Module } from '@nestjs/common';
+
+@Module({})
+export class StoreModule {}
diff --git a/src/user/decorators/current-user.decorator.ts b/src/user/decorators/current-user.decorator.ts
new file mode 100644
index 0000000..2afdfc0
--- /dev/null
+++ b/src/user/decorators/current-user.decorator.ts
@@ -0,0 +1,15 @@
+import { createParamDecorator, ExecutionContext } from '@nestjs/common';
+
+export const CurrentUser = createParamDecorator(
+  (data: any, ctx: ExecutionContext) => {
+    const request = ctx.switchToHttp().getRequest();
+    if (!request.user) {
+      return null;
+    }
+
+    if (data) {
+      return request.user[data];
+    }
+    return request.user;
+  },
+);
diff --git a/src/user/dtos/query/user-purchase-history-query.dto.ts b/src/user/dtos/query/user-purchase-history-query.dto.ts
new file mode 100644
index 0000000..50e4af0
--- /dev/null
+++ b/src/user/dtos/query/user-purchase-history-query.dto.ts
@@ -0,0 +1,18 @@
+import { PaymentLevel } from '../../../payment/enums/payment-level.enum';
+import { ApiProperty } from '@nestjs/swagger';
+import { PaginateQueryDto } from '../../../common/paginate/paginate-query.dto';
+
+export class UserPurchaseHistoryQueryDto extends PaginateQueryDto {
+  @ApiProperty({
+    example: [
+      PaymentLevel.PAID,
+      PaymentLevel.POINT_PAID,
+      PaymentLevel.CANCELLED,
+    ],
+  })
+  status?: PaymentLevel[] = [
+    PaymentLevel.PAID,
+    PaymentLevel.POINT_PAID,
+    PaymentLevel.CANCELLED,
+  ];
+}
diff --git a/src/user/dtos/req/email-recovery.dto.ts b/src/user/dtos/req/email-recovery.dto.ts
new file mode 100644
index 0000000..1c94383
--- /dev/null
+++ b/src/user/dtos/req/email-recovery.dto.ts
@@ -0,0 +1,16 @@
+import { IsNotEmpty, IsString, Matches } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class EmailRecoveryDto {
+  @IsNotEmpty()
+  @ApiProperty({ example: '홍길동', description: '이름' })
+  @IsString()
+  readonly userName: string;
+
+  @IsNotEmpty()
+  @ApiProperty({ example: '01012345678', description: '전화번호' })
+  @Matches(/^[0-9]{10,11}$/, {
+    message: '알맞은 형식의 휴대폰 번호를 입력해주세요.',
+  })
+  readonly userPhoneNumber: string;
+}
diff --git a/src/user/dtos/req/forgot-password.dto.ts b/src/user/dtos/req/forgot-password.dto.ts
new file mode 100644
index 0000000..ee73e5a
--- /dev/null
+++ b/src/user/dtos/req/forgot-password.dto.ts
@@ -0,0 +1,19 @@
+import { IsEmail, IsNotEmpty, Matches } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class ForgotPasswordDto {
+  @ApiProperty({
+    example: 'test@test.com',
+    description: '이메일',
+  })
+  @IsNotEmpty()
+  @IsEmail({}, { message: '이메일 형식이 아닙니다.' })
+  readonly userEmail: string;
+
+  @IsNotEmpty()
+  @ApiProperty({ example: '01012345678', description: '전화번호' })
+  @Matches(/^[0-9]{10,11}$/, {
+    message: '알맞은 형식의 휴대폰 번호를 입력해주세요.',
+  })
+  readonly userPhoneNumber: string;
+}
diff --git a/src/user/dtos/req/update-user-cart.dto.ts b/src/user/dtos/req/update-user-cart.dto.ts
new file mode 100644
index 0000000..22187bf
--- /dev/null
+++ b/src/user/dtos/req/update-user-cart.dto.ts
@@ -0,0 +1,13 @@
+import { IsArray, IsNotEmpty, IsNumber } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class UpdateUserCartDto {
+  @ApiProperty({
+    example: [1, 2, 3],
+    description: '상품 인덱스 리스트',
+  })
+  @IsNotEmpty()
+  @IsArray()
+  @IsNumber({}, { each: true })
+  readonly productIdxList: number[];
+}
diff --git a/src/user/dtos/res/my-exams-response.dto.ts b/src/user/dtos/res/my-exams-response.dto.ts
new file mode 100644
index 0000000..5525e33
--- /dev/null
+++ b/src/user/dtos/res/my-exams-response.dto.ts
@@ -0,0 +1,74 @@
+import { Expose, Type } from 'class-transformer';
+import { ApiProperty, PickType } from '@nestjs/swagger';
+import { ProductEntity } from '../../../product/entities/product.entity';
+import { ProductDto } from '../../../product/dtos/res/product.dto';
+
+export class MyExamsResponseDto {
+  // @ApiProperty({
+  //   example: {
+  //     userAuthIdx: 1,
+  //     userIdx: 1,
+  //     productIdx: 1,
+  //     expireAt: '2021-01-01T00:00:00.000Z',
+  //     isUse: 0,
+  //     createdAt: '2021-01-01T00:00:00.000Z',
+  //   },
+  // })
+  // @Expose()
+  // userAuth: UserAuthEntity;
+
+  @ApiProperty({
+    example: '2021-01-01T00:00:00.000Z',
+  })
+  @Expose()
+  expireAt: Date;
+
+  @ApiProperty({
+    example: '2021-01-01T00:00:00.000Z',
+  })
+  @Expose()
+  createdAt: Date;
+
+  @ApiProperty({
+    example: 0,
+  })
+  @Expose()
+  isUse: number;
+
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  productIdx: number;
+
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  userAuthIdx: number;
+
+  @ApiProperty({
+    example: 1,
+  })
+  @Expose()
+  userIdx: number;
+
+  @ApiProperty({
+    type: PickType(ProductDto, [
+      'productIdx',
+      'productName',
+      'productThumbnail',
+    ]),
+    description: '상품 정보',
+  })
+  @Type(() => ProductDto)
+  @Expose()
+  product?: Partial<ProductDto>;
+
+  @ApiProperty({
+    example: '자격증',
+    description: '시험 카테고리',
+  })
+  @Expose()
+  productTopCategory: string;
+}
diff --git a/src/user/dtos/res/user-cart-response.dto.ts b/src/user/dtos/res/user-cart-response.dto.ts
new file mode 100644
index 0000000..c72328c
--- /dev/null
+++ b/src/user/dtos/res/user-cart-response.dto.ts
@@ -0,0 +1,28 @@
+import { Expose, Type } from 'class-transformer';
+import { ApiProperty, PickType } from '@nestjs/swagger';
+import { ProductDto } from '../../../product/dtos/res/product.dto';
+
+export class UserCartResponseDto {
+  @ApiProperty({
+    example: 1,
+    description: '장바구니 인덱스',
+  })
+  @Expose()
+  readonly userCartIdx: number;
+
+  @ApiProperty({
+    type: PickType(ProductDto, [
+      'productIdx',
+      'productName',
+      'productThumbnail',
+      'shortDescription',
+      'price',
+      'discountPrice',
+      'duration',
+    ]),
+    description: '상품 정보',
+  })
+  @Expose()
+  @Type(() => ProductDto)
+  readonly product: Partial<ProductDto>;
+}
diff --git a/src/user/dtos/res/user-purchase-history-response.dto.ts b/src/user/dtos/res/user-purchase-history-response.dto.ts
new file mode 100644
index 0000000..70be7aa
--- /dev/null
+++ b/src/user/dtos/res/user-purchase-history-response.dto.ts
@@ -0,0 +1,16 @@
+import { Expose, Type } from 'class-transformer';
+import { Payment } from '@prisma/client';
+import { PaymentEntity } from '../../../payment/entities/payment.entity';
+
+export class UserPurchaseHistoryResponseDto {
+  @Expose()
+  @Type(() => PaymentEntity)
+  payment: Payment;
+
+  @Expose()
+  paymentProducts: {
+    productIdx: number;
+    isRefund: number;
+    refundPrice: number | null;
+  };
+}
diff --git a/src/user/dtos/res/user.dto.ts b/src/user/dtos/res/user.dto.ts
new file mode 100644
index 0000000..e376681
--- /dev/null
+++ b/src/user/dtos/res/user.dto.ts
@@ -0,0 +1,98 @@
+import { Exclude, Expose } from 'class-transformer';
+import { UserStatus } from '../../enums/user-status.enum';
+import { Status } from '../../../common/enums/status.enum';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class UserDto {
+  @ApiProperty({
+    example: 1,
+    description: '유저 인덱스',
+  })
+  @Expose()
+  userIdx: number;
+
+  @ApiProperty({
+    example: 'user_name',
+    description: '사용자 이름',
+  })
+  @Expose()
+  userName: string;
+
+  @ApiProperty({
+    example: 'test22@test.com',
+    description: '사용자 이메일',
+  })
+  @Expose()
+  userEmail: string;
+
+  @ApiProperty({
+    example: '010-1234-5678',
+    description: '사용자 전화번호',
+  })
+  @Expose()
+  userPhoneNumber: number;
+
+  @ApiProperty({
+    example: 'nickname',
+    description: '사용자 닉네임',
+  })
+  @Expose()
+  nickname: string;
+
+  @ApiProperty({
+    example: '19990222',
+    description: '사용자 생년월일',
+  })
+  @Expose()
+  dateOfBirth: number | null;
+
+  @Exclude()
+  userPassword: string | null;
+
+  @ApiProperty({
+    example: 5000,
+    description: '사용자 포인트',
+  })
+  @Expose()
+  point: number;
+
+  @Exclude()
+  recommendCount: number;
+
+  @Exclude()
+  accessLevel: number;
+
+  @ApiProperty({
+    example: 'engineeo',
+    description: '사용자 계정 제공자',
+  })
+  @Expose()
+  provider: string;
+
+  @ApiProperty({
+    example: 'sadfwe22aa',
+    description: '추천인 코드',
+  })
+  @Expose()
+  recommendCode: string;
+
+  @ApiProperty({
+    example: 3,
+    description: '추천된 횟수',
+  })
+  @Expose()
+  recommendedCount: number;
+
+  @Expose()
+  status: string | UserStatus | Status;
+
+  @Expose()
+  createdAt: Date;
+
+  @Expose()
+  updatedAt: Date;
+
+  // userCarts?: Partial<UserCartEntity[]>;
+  // UserCoupon   :  UserCoupon[]
+  // payments?: Partial<PaymentEntity[]>;
+}
diff --git a/src/user/dtos/update-user.dto.ts b/src/user/dtos/update-user.dto.ts
new file mode 100644
index 0000000..689b450
--- /dev/null
+++ b/src/user/dtos/update-user.dto.ts
@@ -0,0 +1,36 @@
+import { passwordRegex, nicknameRegex } from './../../common/utils/regex.util';
+import { ApiProperty, PickType } from '@nestjs/swagger';
+import {
+  IsString,
+  Matches,
+  MaxLength,
+  MinLength,
+  ValidateIf,
+} from 'class-validator';
+import { UserEntity } from '../entities/user.entity';
+import { string } from 'joi';
+
+export class UpdateUserDto {
+  @ApiProperty({
+    type: string,
+    example: 'qwer1234!',
+    description: '유저 비밀번호',
+  })
+  @Matches(passwordRegex, {
+    message: '비밀번호 형식에 맞춰서 입력해주세요.',
+  })
+  @ValidateIf((o) => !o.nickname || o.userPassword)
+  readonly userPassword?: string;
+
+  @ApiProperty({
+    type: string,
+    example: '유정호호',
+    description: '유저 닉네임',
+  })
+  @Matches(nicknameRegex, {
+    message: '닉네임 양식에 맞춰서 입력해주세요.',
+  })
+  @ValidateIf((o) => !o.userPassword || o.nickname)
+  @IsString()
+  readonly nickname?: string;
+}
diff --git a/src/user/entities/user.entity.ts b/src/user/entities/user.entity.ts
new file mode 100644
index 0000000..b718a1e
--- /dev/null
+++ b/src/user/entities/user.entity.ts
@@ -0,0 +1,23 @@
+import { User } from '@prisma/client';
+import { BaseEntity } from '../../common/entities/base.entity';
+import { UserCartEntity } from '../../userCart/entities/user-cart.entity';
+import { PaymentEntity } from '../../payment/entities/payment.entity';
+
+export class UserEntity extends BaseEntity implements User {
+  accessLevel: number;
+  dateOfBirth: number | null;
+  nickname: string;
+  point: number;
+  provider: string;
+  recommendCount: number;
+  userEmail: string;
+  userIdx: number;
+  userName: string;
+  userPassword: string | null;
+  userPhoneNumber: number;
+  recommendCode: string;
+  recommendedCount: number;
+
+  userCarts?: Partial<UserCartEntity[]>;
+  payments?: Partial<PaymentEntity[]>;
+}
diff --git a/src/user/enums/user-provider.enum.ts b/src/user/enums/user-provider.enum.ts
new file mode 100644
index 0000000..846fc32
--- /dev/null
+++ b/src/user/enums/user-provider.enum.ts
@@ -0,0 +1,4 @@
+export const enum UserProvider {
+  ENGINEEO = 'engineeo',
+  KAKAO = 'kakao',
+}
diff --git a/src/user/enums/user-status.enum.ts b/src/user/enums/user-status.enum.ts
new file mode 100644
index 0000000..672c785
--- /dev/null
+++ b/src/user/enums/user-status.enum.ts
@@ -0,0 +1,7 @@
+export const enum UserStatus {
+  USER = 'N',
+  ADMIN = 'M',
+  KING = 'K',
+  BANNED = 'F',
+  QUIT = 'Y',
+}
diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts
new file mode 100644
index 0000000..baafa4f
--- /dev/null
+++ b/src/user/user.controller.ts
@@ -0,0 +1,343 @@
+import {
+  BadRequestException,
+  Body,
+  Controller,
+  Delete,
+  Get,
+  HttpCode,
+  NotFoundException,
+  Patch,
+  Post,
+  UseGuards,
+} from '@nestjs/common';
+import { UserService } from './user.service';
+import { CurrentUser } from './decorators/current-user.decorator';
+import { UserEntity } from './entities/user.entity';
+import { Serialize } from '../common/interceptors/serialize.interceptor';
+import { EmailRecoveryDto } from './dtos/req/email-recovery.dto';
+import {
+  ApiBody,
+  ApiHeader,
+  ApiOperation,
+  ApiQuery,
+  ApiResponse,
+  ApiTags,
+} from '@nestjs/swagger';
+import { ForgotPasswordDto } from './dtos/req/forgot-password.dto';
+import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
+import { UserStatus } from './enums/user-status.enum';
+import { UserCartResponseDto } from './dtos/res/user-cart-response.dto';
+import { UpdateUserCartDto } from './dtos/req/update-user-cart.dto';
+import { UserCartEntity } from '../userCart/entities/user-cart.entity';
+import { UserCartService } from '../userCart/user-cart.service';
+import { PaymentService } from '../payment/payment.service';
+
+import { UpdateUserDto } from './dtos/update-user.dto';
+import {
+  USER_BANNED,
+  USER_NOT_FOUND,
+  USER_QUIT,
+  USER_RESET_PASSWORD_FAIL,
+} from '../common/constants/user.constant';
+import { ExamService } from '../exam/exam.service';
+import { MyExamsResponseDto } from './dtos/res/my-exams-response.dto';
+
+@Controller('user')
+@ApiTags('user API')
+export class UserController {
+  constructor(
+    private readonly userService: UserService,
+    private readonly userCartService: UserCartService,
+    private readonly examService: ExamService,
+    private readonly paymentService: PaymentService,
+  ) {}
+
+  // 기존 엔드포인트에서 userIdx param 빠짐.
+  @ApiOperation({
+    summary: '유저정보 업데이트',
+    description: '유저정보 업데이트',
+  })
+  @ApiResponse({
+    status: 200,
+    description: '유저 정보 수정 완료',
+    type: UserEntity,
+  })
+  @Patch('profile')
+  @HttpCode(200)
+  @UseGuards(JwtAuthGuard)
+  @Serialize(UserEntity)
+  async updateUserInfo(
+    @CurrentUser() user: UserEntity,
+    @Body() updateUserDto: UpdateUserDto,
+  ) {
+    return await this.userService.updateUserInfo(user.userIdx, updateUserDto);
+  }
+
+  @ApiOperation({ summary: '닉네임 중복확인', description: '닉네임 중복확인' })
+  @ApiBody({
+    schema: {
+      properties: {
+        nickname: { type: 'string', example: '테스트22' },
+      },
+    },
+  })
+  @ApiResponse({
+    status: 200,
+    description: '닉네임 중복확인 완료',
+    type: Boolean,
+  })
+  @Post('check-nickname')
+  @HttpCode(200)
+  @Serialize(UserEntity)
+  async checkNicknameValid(@Body('nickname') nickname: string) {
+    if (!nickname) throw new BadRequestException('닉네임이 누락되었습니다.');
+    return await this.userService.checkNicknameValid(nickname);
+  }
+
+  // @Get('test')
+  // async test() {
+  //   return this.userService.test2();
+  // }
+
+  @ApiOperation({
+    summary: '이메일 찾기',
+    description: '이메일 찾기 @author neo',
+  })
+  @ApiBody({
+    type: EmailRecoveryDto,
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '이메일 찾기 성공',
+    type: String,
+  })
+  @Post('email-recovery')
+  async emailRecovery(
+    @Body() emailRecoveryDto: EmailRecoveryDto,
+  ): Promise<string | null> {
+    const user = await this.userService.recoverEmail(emailRecoveryDto);
+
+    if (!user) {
+      throw new NotFoundException(USER_NOT_FOUND);
+    }
+    if (user.status === UserStatus.QUIT) {
+      throw new BadRequestException(USER_QUIT);
+    }
+    if (user.status === UserStatus.BANNED) {
+      throw new BadRequestException(USER_BANNED);
+    }
+
+    return user.userEmail;
+  }
+
+  @ApiOperation({
+    summary: '비밀번호 찾기',
+    description: '비밀번호 찾기 @author neo',
+  })
+  @ApiBody({
+    type: ForgotPasswordDto,
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: 'success message from NCP',
+    type: String,
+  })
+  @Post('forgot-password')
+  @HttpCode(200)
+  async forgotPassword(
+    @Body() forgotPasswordDto: ForgotPasswordDto,
+  ): Promise<string> {
+    const sendSMSReturnType = await this.userService.forgotPassword(
+      forgotPasswordDto,
+    );
+
+    if (sendSMSReturnType.status !== 202) {
+      throw new BadRequestException(USER_RESET_PASSWORD_FAIL);
+    }
+
+    return sendSMSReturnType.msg;
+  }
+
+  @ApiOperation({
+    summary: '리뷰 노트 조회',
+    description: '로그인한 유저의 리뷰 노트 조회',
+  })
+  @ApiQuery({})
+  @ApiResponse({
+    status: 200,
+    description: '리뷰 노트 조회 성공',
+    //TODO
+    type: String,
+  })
+  @Get('review-note')
+  @UseGuards(JwtAuthGuard)
+  async getReviewNote() {
+    // TODO
+    // await this.userService.getReviewNote();
+
+    return 'getReviewNote';
+  }
+
+  @ApiOperation({
+    summary: '유저 장바구니 조회',
+    description: '유저 장바구니 조회 @author neo',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '유저 장바구니 조회 성공',
+    isArray: true,
+    type: UserCartResponseDto,
+  })
+  @Get('cart')
+  @UseGuards(JwtAuthGuard)
+  @Serialize(UserCartResponseDto)
+  getCart(@CurrentUser() user: UserEntity): Promise<UserCartEntity[]> {
+    return this.userCartService.getUserCarts(user.userIdx);
+  }
+
+  @ApiOperation({
+    summary: '유저 장바구니 추가',
+    description: '유저 장바구니 추가 @author neo',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiBody({
+    type: UpdateUserCartDto,
+    required: true,
+    description: '장바구니에 담을 상품 인덱스 리스트',
+  })
+  @ApiResponse({
+    status: 201,
+    description: '유저 장바구니 추가 성공',
+    type: String,
+  })
+  @Post('cart')
+  @UseGuards(JwtAuthGuard)
+  async addCart(
+    @CurrentUser() user: UserEntity,
+    @Body() createUserCartDto: UpdateUserCartDto,
+  ): Promise<string> {
+    const createResult = await this.userCartService.createUserCarts(
+      user.userIdx,
+      createUserCartDto,
+    );
+
+    return `장바구니에 ${createResult.count}개의 상품이 추가되었습니다.`;
+  }
+
+  @ApiOperation({
+    summary: '유저 장바구니 삭제',
+    description: '유저 장바구니 삭제 @author neo',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+  })
+  @ApiBody({
+    type: UpdateUserCartDto,
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '유저 장바구니 삭제 성공',
+    type: String,
+  })
+  @Delete('cart')
+  @UseGuards(JwtAuthGuard)
+  async deleteCart(
+    @CurrentUser() user: UserEntity,
+    @Body() deleteUserCartDto: UpdateUserCartDto,
+  ): Promise<string> {
+    const deleteResult = await this.userCartService.deleteUserCarts(
+      user.userIdx,
+      deleteUserCartDto,
+    );
+
+    return `장바구니에서 ${deleteResult.count}개의 상품이 삭제되었습니다.`;
+  }
+
+  @ApiOperation({
+    summary: '비밀번호 확인',
+    description: '유저 정보 수정시 비밀번호 확인',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+  })
+  @ApiBody({
+    schema: {
+      properties: {
+        password: { type: 'string', example: 'qwer1234!' },
+      },
+    },
+  })
+  @ApiResponse({
+    status: 200,
+    description: '비밀번호 일치',
+    type: Boolean,
+  })
+  @Post('/passwordAuth')
+  @UseGuards(JwtAuthGuard)
+  async checkPassword(
+    @CurrentUser() user: UserEntity,
+    @Body('password') password: string,
+  ) {
+    if (!password) {
+      throw new BadRequestException('비밀번호가 누락되었습니다.');
+    }
+    return this.userService.checkPassword(user.userIdx, password);
+  }
+
+  @ApiOperation({
+    summary: '회원 탈퇴',
+    description: '회원 탈퇴',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+  })
+  @ApiResponse({
+    status: 200,
+    description: '회원 탈퇴 완료',
+    type: Boolean,
+  })
+  @Patch('withdrawl')
+  @UseGuards(JwtAuthGuard)
+  async dropUser(@CurrentUser() user: UserEntity) {
+    return await this.userService.dropUser(user.userIdx);
+  }
+
+  @ApiOperation({
+    summary: '나의 시험 조회',
+    description: '로그인한 유저의 나의 시험 조회 @author neo',
+  })
+  @ApiHeader({
+    name: 'x-access-token',
+    description: 'JWT token',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '나의 시험 조회 성공',
+    isArray: true,
+    type: MyExamsResponseDto,
+  })
+  @Get('my-exam')
+  @UseGuards(JwtAuthGuard)
+  @Serialize(MyExamsResponseDto)
+  async getMyExam(
+    @CurrentUser() user: UserEntity,
+  ): Promise<MyExamsResponseDto[]> {
+    return await this.examService.getMyExams(user.userIdx);
+  }
+}
diff --git a/src/user/user.module.ts b/src/user/user.module.ts
new file mode 100644
index 0000000..a459e29
--- /dev/null
+++ b/src/user/user.module.ts
@@ -0,0 +1,14 @@
+import { Module } from '@nestjs/common';
+import { UserController } from './user.controller';
+import { UserService } from './user.service';
+import { UserCartModule } from '../userCart/user-cart.module';
+import { PaymentModule } from '../payment/payment.module';
+import { ExamModule } from '../exam/exam.module';
+
+@Module({
+  imports: [UserCartModule, PaymentModule, ExamModule],
+  controllers: [UserController],
+  providers: [UserService],
+  exports: [UserService],
+})
+export class UserModule {}
diff --git a/src/user/user.service.spec.ts b/src/user/user.service.spec.ts
new file mode 100644
index 0000000..355170f
--- /dev/null
+++ b/src/user/user.service.spec.ts
@@ -0,0 +1,156 @@
+import { DUPLICATE_NICKNAME } from './../common/constants/text.constant';
+import { BadRequestException } from '@nestjs/common';
+import { PrismaService } from './../prisma/prisma.service';
+import { Test, TestingModule } from '@nestjs/testing';
+import { UserService } from './user.service';
+import { PrismaModule } from '../prisma/prisma.module';
+
+const nickname = '가나다라';
+const userPassword = 'qwer1234!@';
+const userArray = [
+  {
+    userEmail: 'test23@test.com',
+    userPhoneNum: 1055427885,
+    userName: '유정후',
+    dateOfBirth: 19990506,
+    nickname: nickname,
+    userIdx: 1,
+    userPassword,
+    createdAt: new Date('2023-03-31T06:09:12.125Z'),
+    updatedAt: new Date('2023-03-31T06:09:12.125Z'),
+    status: 'N',
+    recommendCount: 0,
+    accessLevel: 0,
+    point: 1231,
+    provider: 'engineeo',
+  },
+  {
+    userEmail: 'test24@test.com',
+    userPhoneNum: 1055427886,
+    userName: '유정하',
+    dateOfBirth: 19990506,
+    nickname: '테스트2',
+    userIdx: 2,
+    userPassword: 'qwer5678',
+    createdAt: new Date('2023-03-31T06:09:12.125Z'),
+    updatedAt: new Date('2023-03-31T06:09:12.125Z'),
+    status: 'N',
+    recommendCount: 0,
+    accessLevel: 0,
+    point: 1231,
+    provider: 'engineeo',
+  },
+  {
+    userEmail: 'test25@test.com',
+    userPhoneNum: 1055427887,
+    userName: '유정현',
+    dateOfBirth: 19990506,
+    nickname: '테스트3',
+    userIdx: 3,
+    userPassword: 'asdf123!',
+    createdAt: new Date('2023-03-31T06:09:12.125Z'),
+    updatedAt: new Date('2023-03-31T06:09:12.125Z'),
+    status: 'N',
+    recommendCount: 0,
+    accessLevel: 0,
+    point: 1231,
+    provider: 'engineeo',
+  },
+  {
+    userEmail: 'test23@test.com',
+    userPhoneNum: 1055427885,
+    userName: '유정후',
+    dateOfBirth: 19990506,
+    nickname: '마바사아',
+    userIdx: 1,
+    userPassword: 'abcd0103!',
+    createdAt: new Date('2023-03-31T06:09:12.125Z'),
+    updatedAt: new Date('2023-03-31T06:09:12.125Z'),
+    status: 'N',
+    recommendCount: 0,
+    accessLevel: 0,
+    point: 1231,
+    provider: 'engineeo',
+  },
+];
+const user = userArray[0];
+const db = {
+  user: {
+    findMany: jest.fn().mockResolvedValue(userArray),
+    findUnique: jest.fn().mockResolvedValue(user),
+    create: jest.fn().mockResolvedValue(user),
+    save: jest.fn(),
+    update: jest.fn().mockResolvedValue(user),
+    delete: jest.fn().mockResolvedValue(user),
+  },
+};
+
+describe('UserService', () => {
+  let userService: UserService;
+  let prisma: PrismaService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      providers: [
+        UserService,
+        {
+          provide: PrismaService,
+          useValue: db,
+        },
+      ],
+      imports: [PrismaModule],
+    }).compile();
+    userService = module.get<UserService>(UserService);
+    prisma = module.get<PrismaService>(PrismaService);
+  });
+
+  it('should be defined', () => {
+    expect(prisma).toBeDefined();
+    expect(userService).toBeDefined();
+  });
+
+  describe('check nickname valid', () => {
+    it('should check nickname duplicated', async () => {
+      db.user['findFirst'] = jest.fn().mockResolvedValue(null);
+      const nickname = await userService.checkNicknameValid('테스트에요');
+      expect(nickname).toEqual(true);
+    });
+  });
+
+  describe('check nickname valid', () => {
+    it('should check nickname duplicated', async () => {
+      db.user['findFirst'] = jest.fn().mockResolvedValue(user);
+      const nickname = await userService.checkNicknameValid('가나다라');
+      expect(nickname).toEqual(new BadRequestException(DUPLICATE_NICKNAME));
+    });
+  });
+
+  describe('update user', () => {
+    it('should check nickname duplicated', async () => {
+      db.user['update'] = jest.fn().mockResolvedValue(userArray[3]);
+      const updateInfo = {
+        nickname: '마바사아',
+        userPassword: 'abcd0103!',
+      };
+
+      const updateResult = await userService.updateUserInfo(1, updateInfo);
+      expect(updateResult.userPassword).toEqual(updateInfo.userPassword);
+      expect(updateResult.nickname).toEqual(updateInfo.nickname);
+    });
+  });
+
+  describe('can not update user', () => {
+    it('when user nickname is duplicated', async () => {
+      db.user['update'] = jest
+        .fn()
+        .mockResolvedValue(new BadRequestException(DUPLICATE_NICKNAME));
+      db.user['findFirst'] = jest.fn().mockResolvedValue(user);
+      const updateResult = await userService.updateUserInfo(2, {
+        nickname: '유니크한',
+        userPassword: 'skaks!@',
+      });
+
+      expect(updateResult).toEqual(new BadRequestException(DUPLICATE_NICKNAME));
+    });
+  });
+});
diff --git a/src/user/user.service.ts b/src/user/user.service.ts
new file mode 100644
index 0000000..11c94cd
--- /dev/null
+++ b/src/user/user.service.ts
@@ -0,0 +1,254 @@
+import { User } from '@prisma/client';
+import { UpdateUserDto } from './dtos/update-user.dto';
+import {
+  Injectable,
+  BadRequestException,
+  NotFoundException,
+} from '@nestjs/common';
+import { PrismaService } from '../prisma/prisma.service';
+import { UserEntity } from './entities/user.entity';
+
+import { hashPassword } from '../common/utils/hash-password.util';
+import { UserProvider } from './enums/user-provider.enum';
+import { NCPClient } from 'node-sens';
+import { ConfigService } from '@nestjs/config';
+import { sendSMSReturnType } from 'node-sens/dist/ncp_client';
+import {
+  DUPLICATE_NICKNAME,
+  USER_BANNED,
+  USER_NOT_FOUND,
+  USER_QUIT,
+} from '../common/constants/user.constant';
+import { UserStatus } from './enums/user-status.enum';
+import { ForgotPasswordDto } from './dtos/req/forgot-password.dto';
+import { EmailRecoveryDto } from './dtos/req/email-recovery.dto';
+
+@Injectable()
+export class UserService {
+  constructor(
+    private readonly prismaService: PrismaService,
+    private readonly configService: ConfigService,
+  ) {}
+
+  getUserByIdx(userIdx: number, status: UserStatus[]) {
+    return this.prismaService.user.findFirst({
+      where: {
+        status: { in: status },
+        userIdx,
+      },
+    });
+  }
+
+  getActiveUserByIdx(userEmail: string): Promise<UserEntity | null> {
+    return this.getUserByEmail(userEmail, [UserStatus.USER, UserStatus.KING]);
+  }
+
+  getBannedUserByIdx(userEmail: string): Promise<UserEntity | null> {
+    return this.getUserByEmail(userEmail, [UserStatus.BANNED]);
+  }
+
+  getQuitUserByIdx(userEmail: string): Promise<UserEntity | null> {
+    return this.getUserByEmail(userEmail, [UserStatus.QUIT]);
+  }
+
+  private getUserByEmail(
+    userEmail: string,
+    status: UserStatus[],
+  ): Promise<User | null> {
+    return this.prismaService.user.findFirst({
+      where: {
+        status: { in: status },
+        userEmail,
+      },
+    });
+  }
+
+  getActiveUserByEmail(userEmail: string): Promise<UserEntity | null> {
+    return this.getUserByEmail(userEmail, [
+      UserStatus.USER,
+      UserStatus.ADMIN,
+      UserStatus.KING,
+    ]);
+  }
+
+  getBannedUserByEmail(userEmail: string): Promise<UserEntity | null> {
+    return this.getUserByEmail(userEmail, [UserStatus.BANNED]);
+  }
+
+  getQuitUserByEmail(userEmail: string): Promise<UserEntity | null> {
+    return this.getUserByEmail(userEmail, [UserStatus.QUIT]);
+  }
+
+  /**
+   * @desc 이메일 찾기 - local provider only
+   * @param emailRecoveryDto
+   * @returns UserEntity
+   * @author neo
+   */
+  public recoverEmail(
+    emailRecoveryDto: EmailRecoveryDto,
+  ): Promise<UserEntity | null> {
+    const { userName, userPhoneNumber } = emailRecoveryDto;
+
+    return this.prismaService.user.findFirst({
+      where: {
+        userName,
+        userPhoneNumber: parseInt(userPhoneNumber),
+        provider: UserProvider.ENGINEEO,
+      },
+    });
+  }
+
+  /**
+   * @desc 랜덤 비밀번호 재설정 및 SMS 전송 - local provider only
+   * @param forgotPasswordDto
+   * @returns Promise<sendSMSReturnType>
+   * @throws NotFoundException
+   * @throws BadRequestException
+   * @author neo
+   */
+  public async forgotPassword(
+    forgotPasswordDto: ForgotPasswordDto,
+  ): Promise<sendSMSReturnType> {
+    const { userEmail, userPhoneNumber } = forgotPasswordDto;
+
+    const user = await this.prismaService.user.findFirst({
+      where: {
+        userEmail,
+        userPhoneNumber: parseInt(userPhoneNumber),
+        provider: UserProvider.ENGINEEO,
+      },
+    });
+
+    if (!user) {
+      throw new NotFoundException(USER_NOT_FOUND);
+    }
+
+    if (user.status === UserStatus.BANNED) {
+      throw new BadRequestException(USER_BANNED);
+    }
+
+    if (user.status === UserStatus.QUIT) {
+      throw new BadRequestException(USER_QUIT);
+    }
+
+    let randomGeneratedPassword = '';
+    const arr =
+      '0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z'.split(
+        ',',
+      );
+    for (let j = 0; j < 6; j++) {
+      randomGeneratedPassword += arr[Math.floor(Math.random() * arr.length)];
+    }
+    const hashedPassword = hashPassword(randomGeneratedPassword);
+
+    return await this.prismaService.$transaction(async (tx) => {
+      await tx.user.update({
+        where: { userIdx: user.userIdx },
+        data: {
+          userPassword: hashedPassword,
+        },
+      });
+
+      const ncpClient = new NCPClient({
+        phoneNumber: this.configService.get('ncp.NCP_OFFICE_NUMBER')!,
+        serviceId: this.configService.get('ncp.NCP_SERVICE_ID')!,
+        secretKey: this.configService.get('ncp.NCP_SECRET_KEY')!,
+        accessKey: this.configService.get('ncp.NCP_ACCESS_KEY')!,
+      });
+
+      return await ncpClient.sendSMS({
+        to: userPhoneNumber,
+        content: `Engineeo 임시 비밀번호는 ${randomGeneratedPassword} 입니다.`,
+      });
+    });
+  }
+
+  /**
+   * @desc 유저 정보 업데이트
+   * @param nickname
+   * @param userPassword
+   * @returns UserEntity
+   * @author YuuuJeong
+   */
+  async updateUserInfo(
+    userIdx: number,
+    updateUserDto: UpdateUserDto,
+  ): Promise<User | null> {
+    const { nickname, userPassword } = updateUserDto;
+
+    if (nickname) {
+      await this.checkNicknameValid(nickname);
+    }
+
+    const password = userPassword ? hashPassword(userPassword) : undefined;
+
+    return await this.prismaService.user.update({
+      where: {
+        userIdx,
+      },
+      data: {
+        nickname: nickname,
+        userPassword: password,
+      },
+    });
+  }
+
+  /**
+   * @desc 닉네임 중복 확인
+   * @param nickname
+   * @returns UserEntity
+   * @author YuuuJeong
+   */
+  async checkNicknameValid(nickname: string): Promise<boolean | null> {
+    const isNickname = await this.prismaService.user.findFirst({
+      where: {
+        nickname,
+      },
+    });
+    if (isNickname) {
+      throw new BadRequestException(DUPLICATE_NICKNAME);
+    }
+    return true;
+  }
+
+  /**
+   * @desc 비밀번호 확인
+   * @param userIdx
+   * @param password
+   * @returns boolean
+   * @author YuuuJeong
+   */
+  async checkPassword(
+    userIdx: number,
+    password: string,
+  ): Promise<boolean | null> {
+    const cryptedPassword = hashPassword(password);
+    const user = await this.prismaService.user.findUnique({
+      where: {
+        userIdx,
+      },
+    });
+    if (user?.userPassword === cryptedPassword) return true;
+    return false;
+  }
+
+  /**
+   * @desc 회원 탈퇴
+   * @param userIdx
+   * @returns boolean
+   * @author YuuuJeong
+   */
+  async dropUser(userIdx: number): Promise<boolean | null> {
+    await this.prismaService.user.update({
+      where: {
+        userIdx,
+      },
+      data: {
+        status: UserStatus.QUIT,
+      },
+    });
+
+    return true; //TODO: response 형식
+  }
+}
diff --git a/src/userAuth/entities/user-auth.entity.ts b/src/userAuth/entities/user-auth.entity.ts
new file mode 100644
index 0000000..e4897f5
--- /dev/null
+++ b/src/userAuth/entities/user-auth.entity.ts
@@ -0,0 +1,13 @@
+import { BaseEntity } from '../../common/entities/base.entity';
+import { UserAuth } from '@prisma/client';
+import { ProductEntity } from '../../product/entities/product.entity';
+
+export class UserAuthEntity extends BaseEntity implements UserAuth {
+  expireAt: Date;
+  isUse: number;
+  productIdx: number;
+  userAuthIdx: number;
+  userIdx: number;
+
+  product?: Partial<ProductEntity>;
+}
diff --git a/src/userAuth/user-auth.module.ts b/src/userAuth/user-auth.module.ts
new file mode 100644
index 0000000..f4e4ccb
--- /dev/null
+++ b/src/userAuth/user-auth.module.ts
@@ -0,0 +1,10 @@
+import { Module } from '@nestjs/common';
+import { UserAuthService } from './user-auth.service';
+
+@Module({
+  imports: [],
+  controllers: [],
+  providers: [UserAuthService],
+  exports: [UserAuthService],
+})
+export class UserAuthModule {}
diff --git a/src/userAuth/user-auth.service.ts b/src/userAuth/user-auth.service.ts
new file mode 100644
index 0000000..6214773
--- /dev/null
+++ b/src/userAuth/user-auth.service.ts
@@ -0,0 +1,55 @@
+import { Injectable } from '@nestjs/common';
+import { Status } from '../common/enums/status.enum';
+import { PrismaService } from '../prisma/prisma.service';
+import { UserAuthEntity } from './entities/user-auth.entity';
+import { ProductType } from '../product/enums/product.enum';
+
+@Injectable()
+export class UserAuthService {
+  constructor(private readonly prismaService: PrismaService) {}
+
+  /**
+   * @desc 유저 상품 권한 조회
+   * @param userIdx
+   * @returns Promise<UserAuthEntity[]>
+   * @author neo
+   */
+  getUserAuthsByUserIdx(userIdx: number): Promise<UserAuthEntity[]> {
+    return this.prismaService.userAuth.findMany({
+      where: {
+        userIdx: userIdx,
+        status: Status.ACTIVE,
+        NOT: {
+          productIdx: ProductType.FREE_PRODUCT_IDX,
+        },
+      },
+    });
+  }
+
+  /**
+   * @desc 유저 상품 권한 상품과 함께 조회
+   * @param userIdx
+   * @returns Promise<UserAuthEntity[]>
+   * @author neo
+   */
+  getUserAuthsWithProductByUserIdx(userIdx: number): Promise<UserAuthEntity[]> {
+    return this.prismaService.userAuth.findMany({
+      include: {
+        product: {
+          select: {
+            productIdx: true,
+            productName: true,
+            productThumbnail: true,
+          },
+        },
+      },
+      where: {
+        userIdx: userIdx,
+        status: Status.ACTIVE,
+        NOT: {
+          productIdx: ProductType.FREE_PRODUCT_IDX,
+        },
+      },
+    });
+  }
+}
diff --git a/src/userCart/entities/user-cart.entity.ts b/src/userCart/entities/user-cart.entity.ts
new file mode 100644
index 0000000..dd194b4
--- /dev/null
+++ b/src/userCart/entities/user-cart.entity.ts
@@ -0,0 +1,12 @@
+import { UserCart } from '@prisma/client';
+import { ProductEntity } from '../../product/entities/product.entity';
+import { UserEntity } from '../../user/entities/user.entity';
+
+export class UserCartEntity implements UserCart {
+  productIdx: number;
+  userCartIdx: number;
+  userIdx: number;
+
+  user?: Partial<UserEntity>;
+  product?: Partial<ProductEntity>;
+}
diff --git a/src/userCart/user-cart.module.ts b/src/userCart/user-cart.module.ts
new file mode 100644
index 0000000..d9a4b70
--- /dev/null
+++ b/src/userCart/user-cart.module.ts
@@ -0,0 +1,10 @@
+import { Module } from '@nestjs/common';
+import { UserCartService } from './user-cart.service';
+
+@Module({
+  imports: [],
+  controllers: [],
+  providers: [UserCartService],
+  exports: [UserCartService],
+})
+export class UserCartModule {}
diff --git a/src/userCart/user-cart.service.ts b/src/userCart/user-cart.service.ts
new file mode 100644
index 0000000..2a8916f
--- /dev/null
+++ b/src/userCart/user-cart.service.ts
@@ -0,0 +1,91 @@
+import { Injectable } from '@nestjs/common';
+import { PrismaService } from '../prisma/prisma.service';
+import { UserCartEntity } from './entities/user-cart.entity';
+import { Status } from '../common/enums/status.enum';
+import { UpdateUserCartDto } from '../user/dtos/req/update-user-cart.dto';
+
+@Injectable()
+export class UserCartService {
+  constructor(private readonly prismaService: PrismaService) {}
+
+  /**
+   * @desc 장바구니 조회
+   * @param userIdx
+   * @returns Promise<UserCartEntity[]>
+   * @author neo
+   */
+  getUserCarts(userIdx: number): Promise<UserCartEntity[]> {
+    return this.prismaService.userCart.findMany({
+      where: {
+        userIdx,
+        product: {
+          status: Status.ACTIVE,
+        },
+      },
+      include: {
+        product: {
+          select: {
+            productIdx: true,
+            productName: true,
+            //TODO : 할인 끝나면 ?
+            discountPrice: true,
+            price: true,
+            shortDescription: true,
+            duration: true,
+            productThumbnail: true,
+          },
+        },
+      },
+    });
+  }
+
+  /**
+   * @desc 장바구니 추가 - 중복 제거
+   * @param userIdx
+   * @param createUserCartDto
+   * @returns Promise<Prisma.BatchPayload>
+   * @author neo
+   */
+  public async createUserCarts(
+    userIdx: number,
+    createUserCartDto: UpdateUserCartDto,
+  ) {
+    const userCartList = await this.prismaService.userCart.findMany({
+      where: {
+        userIdx,
+      },
+      select: {
+        productIdx: true,
+      },
+    });
+
+    const productIdxList = createUserCartDto.productIdxList.filter((idx) => {
+      return !userCartList.find((cart) => cart.productIdx === idx);
+    });
+
+    return this.prismaService.userCart.createMany({
+      data: productIdxList.map((productIdx) => ({
+        userIdx,
+        productIdx,
+      })),
+    });
+  }
+
+  /**
+   * @desc 장바구니 삭제
+   * @param userIdx
+   * @param deleteUserCartDto
+   * @returns Promise<Prisma.BatchPayload>
+   * @author neo
+   */
+  async deleteUserCarts(userIdx: number, deleteUserCartDto: UpdateUserCartDto) {
+    return this.prismaService.userCart.deleteMany({
+      where: {
+        userIdx,
+        productIdx: {
+          in: deleteUserCartDto.productIdxList,
+        },
+      },
+    });
+  }
+}
diff --git a/src/userRecommend/constants/user-recommend-code.constant.ts b/src/userRecommend/constants/user-recommend-code.constant.ts
new file mode 100644
index 0000000..2c08c95
--- /dev/null
+++ b/src/userRecommend/constants/user-recommend-code.constant.ts
@@ -0,0 +1,2 @@
+export const USER_RECOMMEND_CODE_USAGE_LIMIT = 10;
+export const USER_RECOMMEND_CODE_PAY_POINT = 10000;
diff --git a/src/userRecommend/dtos/user-recommend-code-response.dto.ts b/src/userRecommend/dtos/user-recommend-code-response.dto.ts
new file mode 100644
index 0000000..4e16453
--- /dev/null
+++ b/src/userRecommend/dtos/user-recommend-code-response.dto.ts
@@ -0,0 +1,22 @@
+import { Expose, Type } from 'class-transformer';
+import { ApiProperty, PickType } from '@nestjs/swagger';
+
+export class UserRecommendCodeResponseDto {
+  @ApiProperty({
+    example: 4,
+  })
+  @Expose()
+  userIdx: Number;
+
+  @ApiProperty({
+    example: '4b227777d4',
+  })
+  @Expose()
+  recommendCode: string;
+
+  @ApiProperty({
+    example: 0,
+  })
+  @Expose()
+  recommendedCount: Number;
+}
diff --git a/src/userRecommend/dtos/user-recommend-code-use-response.dto.ts b/src/userRecommend/dtos/user-recommend-code-use-response.dto.ts
new file mode 100644
index 0000000..277c7dc
--- /dev/null
+++ b/src/userRecommend/dtos/user-recommend-code-use-response.dto.ts
@@ -0,0 +1,28 @@
+import { Expose } from 'class-transformer';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class UserRecommendCodeUseResponseDto {
+  @ApiProperty({
+    example: 45,
+  })
+  @Expose()
+  referralId: Number;
+
+  @ApiProperty({
+    example: 4,
+  })
+  @Expose()
+  referredId: Number;
+
+  @ApiProperty({
+    example: 10000,
+  })
+  @Expose()
+  paidPoint: Number;
+
+  @ApiProperty({
+    example: '2021-01-01T00:00:00.000Z',
+  })
+  @Expose()
+  updatedAt: Date;
+}
diff --git a/src/userRecommend/entities/user-recommend-log.entity.ts b/src/userRecommend/entities/user-recommend-log.entity.ts
new file mode 100644
index 0000000..78111c9
--- /dev/null
+++ b/src/userRecommend/entities/user-recommend-log.entity.ts
@@ -0,0 +1,13 @@
+import { User, UserRecommendLog } from '@prisma/client';
+import { BaseEntity } from '../../common/entities/base.entity';
+import { UserCartEntity } from '../../userCart/entities/user-cart.entity';
+import { PaymentEntity } from '../../payment/entities/payment.entity';
+
+export class UserRecommendLogEntity
+  extends BaseEntity
+  implements UserRecommendLog
+{
+  id: number;
+  referralId: number;
+  referredId: number;
+}
diff --git a/src/userRecommend/enums/user-point-type.enum.ts b/src/userRecommend/enums/user-point-type.enum.ts
new file mode 100644
index 0000000..e3b30be
--- /dev/null
+++ b/src/userRecommend/enums/user-point-type.enum.ts
@@ -0,0 +1,4 @@
+export const enum UserPointType {
+  ADD = 'A',
+  USE = 'U',
+}
diff --git a/src/userRecommend/user-recommend.controller.spec.ts b/src/userRecommend/user-recommend.controller.spec.ts
new file mode 100644
index 0000000..336bd35
--- /dev/null
+++ b/src/userRecommend/user-recommend.controller.spec.ts
@@ -0,0 +1,18 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { UserRecommendController } from './user-recommend.controller';
+
+describe('UserRecommendController', () => {
+  let controller: UserRecommendController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      controllers: [UserRecommendController],
+    }).compile();
+
+    controller = module.get<UserRecommendController>(UserRecommendController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+});
diff --git a/src/userRecommend/user-recommend.controller.ts b/src/userRecommend/user-recommend.controller.ts
new file mode 100644
index 0000000..8eee09b
--- /dev/null
+++ b/src/userRecommend/user-recommend.controller.ts
@@ -0,0 +1,89 @@
+import { Body, Controller, HttpCode, Post, UseGuards } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { ApiBody, ApiOperation, ApiResponse } from '@nestjs/swagger';
+import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
+import { Serialize } from 'src/common/interceptors/serialize.interceptor';
+import { CurrentUser } from 'src/user/decorators/current-user.decorator';
+import { UserEntity } from 'src/user/entities/user.entity';
+import { UserRecommendCodeResponseDto } from './dtos/user-recommend-code-response.dto';
+import { UserRecommendCodeUseResponseDto } from './dtos/user-recommend-code-use-response.dto';
+import { UserRecommendService } from './user-recommend.service';
+
+@Controller('recommend-code')
+export class UserRecommendController {
+  constructor(private readonly userRecommendService: UserRecommendService) {}
+
+  @ApiOperation({
+    summary: '추천인 코드 사용하기',
+    description: '추천인 코드 사용하기',
+  })
+  @ApiBody({
+    description: '추천인 코드',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '추천인 코드 사용 완료',
+    type: UserRecommendCodeUseResponseDto,
+  })
+  @Post('use')
+  @HttpCode(200)
+  @UseGuards(JwtAuthGuard)
+  @Serialize(UserRecommendCodeUseResponseDto)
+  async useRecommendCode(
+    @CurrentUser() referralUser: UserEntity,
+    @Body('recommendCode') recommendCode: string,
+  ): Promise<UserRecommendCodeUseResponseDto | null> {
+    return await this.userRecommendService.useRecommendCode(
+      referralUser,
+      recommendCode,
+    );
+  }
+
+  @ApiOperation({
+    summary: '추천인 코드 사용가능 확인',
+    description: '추천인 코드 사용가능 확인',
+  })
+  @ApiBody({
+    description: '추천인 코드',
+    required: true,
+  })
+  @ApiResponse({
+    status: 200,
+    description: '추천인 코드 사용가능 확인 완료',
+    type: UserRecommendCodeResponseDto,
+  })
+  @Post('validate')
+  @HttpCode(200)
+  @UseGuards(JwtAuthGuard)
+  @Serialize(UserRecommendCodeResponseDto)
+  async checkRecommendCodeUsage(
+    @Body('recommendCode') recommendCode: string,
+  ): Promise<UserRecommendCodeResponseDto | null> {
+    return await this.userRecommendService.checkRecommendCodeUsage(
+      recommendCode,
+    );
+  }
+
+  @ApiOperation({
+    summary: '추천인 코드 발급',
+    description: '추천인 코드 발급',
+  })
+  @ApiResponse({
+    status: 200,
+    description: '코드 발급 완료',
+  })
+  @Post()
+  @HttpCode(200)
+  @UseGuards(JwtAuthGuard)
+  @Serialize(UserRecommendCodeResponseDto)
+  async createRecommendCode(
+    @CurrentUser() user: UserEntity,
+  ): Promise<UserRecommendCodeResponseDto | null> {
+    const result = await this.userRecommendService.updateRecommendCode(
+      user.userIdx,
+    );
+
+    return result;
+  }
+}
diff --git a/src/userRecommend/user-recommend.module.ts b/src/userRecommend/user-recommend.module.ts
new file mode 100644
index 0000000..b53f44f
--- /dev/null
+++ b/src/userRecommend/user-recommend.module.ts
@@ -0,0 +1,20 @@
+import { Module } from '@nestjs/common';
+
+import {
+  ApiBody,
+  ApiHeader,
+  ApiOperation,
+  ApiQuery,
+  ApiResponse,
+  ApiTags,
+} from '@nestjs/swagger';
+import { UserRecommendController } from './user-recommend.controller';
+import { UserRecommendService } from './user-recommend.service';
+
+@Module({
+  imports: [],
+  controllers: [UserRecommendController],
+  providers: [UserRecommendService],
+  exports: [UserRecommendService],
+})
+export class UserRecommendModule {}
diff --git a/src/userRecommend/user-recommend.service.spec.ts b/src/userRecommend/user-recommend.service.spec.ts
new file mode 100644
index 0000000..7dfa530
--- /dev/null
+++ b/src/userRecommend/user-recommend.service.spec.ts
@@ -0,0 +1,18 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { UserRecommendService } from './user-recommend.service';
+
+describe('UserRecommendService', () => {
+  let service: UserRecommendService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      providers: [UserRecommendService],
+    }).compile();
+
+    service = module.get<UserRecommendService>(UserRecommendService);
+  });
+
+  it('should be defined', () => {
+    expect(service).toBeDefined();
+  });
+});
diff --git a/src/userRecommend/user-recommend.service.ts b/src/userRecommend/user-recommend.service.ts
new file mode 100644
index 0000000..311988b
--- /dev/null
+++ b/src/userRecommend/user-recommend.service.ts
@@ -0,0 +1,164 @@
+import { BadRequestException, Injectable } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { int } from 'aws-sdk/clients/datapipeline';
+import { data } from 'cheerio/lib/api/attributes';
+import { createHash } from 'crypto';
+import { PrismaService } from 'src/prisma/prisma.service';
+import { UserEntity } from 'src/user/entities/user.entity';
+import {
+  USER_RECOMMEND_CODE_PAY_POINT,
+  USER_RECOMMEND_CODE_USAGE_LIMIT,
+} from './constants/user-recommend-code.constant';
+import { UserRecommendCodeResponseDto } from './dtos/user-recommend-code-response.dto';
+import { UserRecommendCodeUseResponseDto } from './dtos/user-recommend-code-use-response.dto';
+import { UserPointType } from './enums/user-point-type.enum';
+
+@Injectable()
+export class UserRecommendService {
+  constructor(private readonly prismaService: PrismaService) {}
+
+  async updateRecommendCode(
+    userIdx: number,
+  ): Promise<UserRecommendCodeResponseDto | null> {
+    const userRecommendCodeExists = await this.prismaService.user.findFirst({
+      where: {
+        userIdx,
+      },
+    });
+    if (userRecommendCodeExists!.recommendCode) {
+      // 이미 존재한다면 더이상 발급하지 아니한다.
+      throw new BadRequestException(
+        '쿠폰을 이미 발급하신 회원 분은 더이상 발급하실 수 없습니다.',
+      );
+    }
+
+    // 먼저 해당추천인 코드를 발급하도록 한다.
+    let hashCode: string, recommendCode: string;
+    let condition = false;
+
+    do {
+      //userIdx와 현재시간을 조합한 로직
+      const uniqueString = userIdx.toString() + Date.now().toString();
+      hashCode = createHash('sha256').update(uniqueString).digest('hex');
+      recommendCode = hashCode.slice(0, 10); // 10개의 코드로 자른다.
+
+      // 중복된 코드인지 검사
+      const exist = await this.prismaService.user.findFirst({
+        where: {
+          recommendCode,
+        },
+      });
+
+      if (!exist) {
+        // 존재하지 않는다면 OK
+        condition = true;
+      }
+    } while (!condition);
+
+    const recommendCodeResult: UserRecommendCodeResponseDto =
+      await this.prismaService.user.update({
+        where: { userIdx },
+        data: {
+          recommendCode,
+        },
+      });
+
+    return recommendCodeResult;
+  }
+
+  async checkRecommendCodeUsage(
+    recommendCode: string,
+  ): Promise<UserRecommendCodeResponseDto | null> {
+    const recommendCodeInfo = await this.prismaService.user.findFirst({
+      where: { recommendCode },
+    });
+
+    if (!recommendCodeInfo) {
+      throw new BadRequestException('잘못된 쿠폰 코드를 입력하셨습니다. ');
+    }
+    if (recommendCodeInfo.recommendedCount > USER_RECOMMEND_CODE_USAGE_LIMIT) {
+      throw new BadRequestException(
+        '해당 쿠폰은 이미 10회 이상 사용하셨으므로 사용이 불가능합니다. ',
+      );
+    }
+
+    return recommendCodeInfo;
+  }
+
+  async useRecommendCode(
+    referralUser: UserEntity,
+    recommendCode: string,
+  ): Promise<UserRecommendCodeUseResponseDto | null> {
+    const recommendCodeInfo = await this.prismaService.user.findFirst({
+      where: {
+        recommendCode,
+        recommendedCount: {
+          lt: USER_RECOMMEND_CODE_USAGE_LIMIT,
+        },
+      },
+    });
+    if (!recommendCodeInfo) {
+      throw new BadRequestException(
+        '해당 쿠폰은 사용이 불가능하므로 쿠폰의 사용 가능성을 다시 한번 조회해 보시기 바랍니다.',
+      );
+    }
+
+    await this.prismaService.user.update({
+      where: { userIdx: recommendCodeInfo.userIdx },
+      data: { recommendedCount: { increment: 1 } },
+    });
+
+    let { recommendedCount, userIdx: referredId } = recommendCodeInfo;
+    recommendedCount += 1;
+
+    // log 생성.
+    await this.prismaService.userRecommendLog.create({
+      data: {
+        referralId: referralUser.userIdx,
+        referredId,
+      },
+    });
+
+    const { userIdx: referralId } = referralUser;
+
+    const codePayPoint = USER_RECOMMEND_CODE_PAY_POINT;
+
+    // 포인트 지급.
+    await this.prismaService.user.updateMany({
+      where: {
+        OR: [{ userIdx: referralId }, { userIdx: referredId }],
+      },
+      data: {
+        point: {
+          increment: codePayPoint,
+        },
+      },
+    });
+
+    //userPointLog에도 해당 포인트 로그에 대한 내용 추가 입력하기
+    await this.prismaService.userPointLog.createMany({
+      data: [
+        {
+          userIdx: referralId,
+          sort: UserPointType.ADD,
+          content: '추천인 코드',
+          point: codePayPoint,
+        },
+        {
+          userIdx: referredId,
+          sort: UserPointType.ADD,
+          content: '추천인 코드',
+          point: codePayPoint,
+        },
+      ],
+    });
+
+    let result: UserRecommendCodeUseResponseDto = {
+      ...recommendCodeInfo,
+      referralId,
+      referredId,
+      paidPoint: codePayPoint,
+    };
+    return result;
+  }
+}
diff --git a/test/app.e2e-spec.ts b/test/app.e2e-spec.ts
new file mode 100644
index 0000000..6c1e1d6
--- /dev/null
+++ b/test/app.e2e-spec.ts
@@ -0,0 +1,24 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { INestApplication } from '@nestjs/common';
+import request = require('supertest');
+import { AppModule } from './../src/app.module';
+
+describe('AppController (e2e)', () => {
+  let app: INestApplication;
+
+  beforeEach(async () => {
+    const moduleFixture: TestingModule = await Test.createTestingModule({
+      imports: [AppModule],
+    }).compile();
+
+    app = moduleFixture.createNestApplication();
+    await app.init();
+  });
+
+  it('/ (GET)', () => {
+    return request(app.getHttpServer())
+      .get('/')
+      .expect(200)
+      .expect('Hello World!');
+  });
+});
diff --git a/test/jest-e2e.json b/test/jest-e2e.json
new file mode 100644
index 0000000..e9d912f
--- /dev/null
+++ b/test/jest-e2e.json
@@ -0,0 +1,9 @@
+{
+  "moduleFileExtensions": ["js", "json", "ts"],
+  "rootDir": ".",
+  "testEnvironment": "node",
+  "testRegex": ".e2e-spec.ts$",
+  "transform": {
+    "^.+\\.(t|j)s$": "ts-jest"
+  }
+}
diff --git a/tsconfig.build.json b/tsconfig.build.json
new file mode 100644
index 0000000..64f86c6
--- /dev/null
+++ b/tsconfig.build.json
@@ -0,0 +1,4 @@
+{
+  "extends": "./tsconfig.json",
+  "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..1891707
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,24 @@
+{
+  "compilerOptions": {
+    "module": "commonjs",
+    "declaration": true,
+    "removeComments": true,
+    "emitDecoratorMetadata": true,
+    "experimentalDecorators": true,
+    "allowSyntheticDefaultImports": true,
+    "target": "es2017",
+    "sourceMap": true,
+    "outDir": "./dist",
+    "baseUrl": "./",
+    "incremental": true,
+    "skipLibCheck": true,
+    "strictNullChecks": true,
+    "noImplicitAny": false,
+    "strictBindCallApply": false,
+    "forceConsistentCasingInFileNames": false,
+    "noFallthroughCasesInSwitch": false,
+    "resolveJsonModule": true,
+    "esModuleInterop": true,
+    "allowJs": true
+  },
+}
-- 
GitLab