From d680ff9fa4298ad3c0cd12f5f9d87f6c51110480 Mon Sep 17 00:00:00 2001
From: jhjang <jhjang@maprex.co.kr>
Date: 목, 14 10월 2021 17:49:08 +0900
Subject: [PATCH] owl-local

---
 src/main/java/kr/wisestone/owl/service/impl/PaymentHistoryServiceImpl.java                            |   78 
 src/main/java/kr/wisestone/owl/web/condition/NoticeCondition.java                                     |   62 
 src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java                                      |  160 
 src/main/java/kr/wisestone/owl/domain/Event.java                                                      |   77 
 src/main/java/kr/wisestone/owl/service/impl/UserInviteServiceImpl.java                                |  322 
 src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java                                     | 2336 ++
 src/main/java/kr/wisestone/owl/service/impl/PermissionServiceImpl.java                                |   75 
 src/main/java/kr/wisestone/owl/web/controller/PriorityController.java                                 |   34 
 src/main/java/kr/wisestone/owl/service/impl/UserWithDrawServiceImpl.java                              |   49 
 src/main/java/kr/wisestone/owl/service/impl/QnaServiceImpl.java                                       |  133 
 src/main/java/kr/wisestone/owl/service/ProjectClosureService.java                                     |    8 
 src/main/java/kr/wisestone/owl/domain/IssueTableConfig.java                                           |   59 
 src/main/java/kr/wisestone/owl/config/kafka/KafkaSender.java                                          |   22 
 src/main/java/kr/wisestone/owl/domain/CustomFieldValue.java                                           |   52 
 src/main/java/kr/wisestone/owl/service/PermissionService.java                                         |   13 
 src/main/java/kr/wisestone/owl/mapper/EventMapper.java                                                |   14 
 src/main/java/kr/wisestone/owl/domain/enumType/AttachedType.java                                      |   10 
 src/main/resources/migration/V1_9__Alter_Table.sql                                                    |   70 
 src/main/java/kr/wisestone/owl/mapper/IssueTypeMapper.java                                            |   17 
 src/main/java/kr/wisestone/owl/web/controller/ManageUserController.java                               |   56 
 src/main/java/kr/wisestone/owl/web/form/WorkflowStatusForm.java                                       |   87 
 src/main/java/kr/wisestone/owl/service/IssueTypeService.java                                          |   39 
 src/main/java/kr/wisestone/owl/service/IssueSearchService.java                                        |   14 
 src/main/java/kr/wisestone/owl/vo/UserVo.java                                                         |  192 
 src/main/java/kr/wisestone/owl/web/condition/ProjectCondition.java                                    |  168 
 src/main/resources/mybatis/query-template/guide-template.xml                                          |   37 
 src/main/resources/mails/projectDefaultExcludeEmail.html                                              |   83 
 src/main/java/kr/wisestone/owl/repository/IssueNumberGeneratorRepository.java                         |    9 
 src/main/java/kr/wisestone/owl/web/controller/ReservationDisableUserController.java                   |   35 
 src/main/resources/mybatis/query-template/userWorkspace-template.xml                                  |   71 
 src/main/java/kr/wisestone/owl/service/FaqService.java                                                |   27 
 src/main/java/kr/wisestone/owl/repository/CustomFieldRepository.java                                  |   16 
 src/main/java/kr/wisestone/owl/web/controller/SeverityController.java                                 |   34 
 src/main/java/kr/wisestone/owl/repository/FaqRepository.java                                          |    7 
 src/main/java/kr/wisestone/owl/repository/WorkflowRepository.java                                     |   21 
 src/main/java/kr/wisestone/owl/vo/IssueCommentVo.java                                                 |   68 
 src/main/resources/system_design.properties                                                           |   76 
 src/main/java/kr/wisestone/owl/service/impl/IssueCustomFieldValueServiceImpl.java                     |  355 
 src/main/java/kr/wisestone/owl/mapper/AttachedFileMapper.java                                         |   25 
 src/main/java/kr/wisestone/owl/vo/UserWorkspaceVo.java                                                |   54 
 src/main/resources/mybatis/query-template/qna-template.xml                                            |   36 
 src/main/java/kr/wisestone/owl/annotation/Viewer.java                                                 |   13 
 src/main/java/kr/wisestone/owl/vo/IssueVo.java                                                        |  373 
 src/main/java/kr/wisestone/owl/web/controller/EventController.java                                    |   88 
 src/main/java/kr/wisestone/owl/web/controller/UserWorkspaceController.java                            |   53 
 src/main/java/kr/wisestone/owl/service/IssueRiskService.java                                          |   13 
 src/main/java/kr/wisestone/owl/search/ElasticSearch.java                                              |   50 
 src/main/java/kr/wisestone/owl/domain/Payment.java                                                    |   83 
 src/main/java/kr/wisestone/owl/web/condition/QnaCondition.java                                        |   62 
 src/main/java/kr/wisestone/owl/repository/SystemEmailRepository.java                                  |   11 
 src/main/java/kr/wisestone/owl/service/impl/WorkspaceServiceImpl.java                                 |  630 
 src/main/java/kr/wisestone/owl/service/SystemRoleService.java                                         |    8 
 src/main/java/kr/wisestone/owl/web/controller/IssueSearchController.java                              |   47 
 src/main/java/kr/wisestone/owl/domain/IssueCustomFieldValue.java                                      |   78 
 src/main/java/kr/wisestone/owl/web/condition/IssueReservationCondition.java                           |   35 
 src/main/java/kr/wisestone/owl/vo/BaseVo.java                                                         |   55 
 src/main/java/kr/wisestone/owl/repository/IssueRiskRepository.java                                    |    7 
 src/main/java/kr/wisestone/owl/domain/WorkflowStatus.java                                             |  119 
 src/main/java/kr/wisestone/owl/config/security/handler/AjaxAuthenticationSuccessHandler.java          |   60 
 src/main/java/kr/wisestone/owl/repository/UserWithDrawRepository.java                                 |   11 
 src/main/java/kr/wisestone/owl/service/impl/IssueVersionServiceImpl.java                              |  146 
 src/main/java/kr/wisestone/owl/service/UserInviteProjectService.java                                  |   11 
 src/main/java/kr/wisestone/owl/service/impl/UserHistoryServiceImpl.java                               |   41 
 src/main/java/kr/wisestone/owl/domain/SystemRoleUser.java                                             |   54 
 src/main/java/kr/wisestone/owl/repository/UserWorkspaceRepository.java                                |   27 
 src/main/java/kr/wisestone/owl/service/impl/UserInviteProjectServiceImpl.java                         |   55 
 src/main/java/kr/wisestone/owl/service/impl/IssueStatusServiceImpl.java                               |  570 
 src/main/java/kr/wisestone/owl/domain/Guide.java                                                      |   59 
 src/main/java/kr/wisestone/owl/domain/SystemRole.java                                                 |  222 
 src/main/java/kr/wisestone/owl/repository/WorkspaceRepository.java                                    |   19 
 src/main/java/kr/wisestone/owl/service/impl/GuideServiceImpl.java                                     |  161 
 src/main/java/kr/wisestone/owl/web/controller/BaseController.java                                     |   66 
 src/main/java/kr/wisestone/owl/web/controller/WorkflowStatusController.java                           |   66 
 src/main/resources/mails/projectDefaultExcludeAndManagerIncludeEmail.html                             |   81 
 src/main/java/kr/wisestone/owl/service/impl/ManageUserServiceImpl.java                                |  113 
 src/main/java/kr/wisestone/owl/vo/IssueVersionVo.java                                                 |   53 
 src/main/java/kr/wisestone/owl/repository/IssueTypeCustomFieldRepository.java                         |   15 
 src/main/java/kr/wisestone/owl/service/impl/IssueSearchServiceImpl.java                               |   88 
 src/main/java/kr/wisestone/owl/config/AsyncConfiguration.java                                         |   82 
 src/main/java/kr/wisestone/owl/service/SeverityService.java                                           |   22 
 src/main/java/kr/wisestone/owl/service/IssueUserService.java                                          |   20 
 src/main/java/kr/wisestone/owl/domain/IssueNumberGenerator.java                                       |   52 
 src/main/java/kr/wisestone/owl/web/form/FaqForm.java                                                  |   93 
 src/main/java/kr/wisestone/owl/exception/OwlRuntimeException.java                                     |   52 
 src/main/java/kr/wisestone/owl/web/condition/WidgetCondition.java                                     |  158 
 src/main/java/kr/wisestone/owl/vo/FaqVo.java                                                          |   64 
 src/main/java/kr/wisestone/owl/vo/QnaVo.java                                                          |   45 
 src/main/java/kr/wisestone/owl/vo/CustomFieldVo.java                                                  |   78 
 src/main/java/kr/wisestone/owl/web/controller/FaqController.java                                      |   89 
 src/main/java/kr/wisestone/owl/domain/strategy/PrefixNamingStrategy.java                              |   47 
 src/main/java/kr/wisestone/owl/vo/IssueCustomFieldValueVo.java                                        |   36 
 src/main/java/kr/wisestone/owl/domain/Workspace.java                                                  |  343 
 src/main/java/kr/wisestone/owl/web/controller/ProjectController.java                                  |  161 
 src/main/java/kr/wisestone/owl/vo/SeverityVo.java                                                     |   50 
 src/main/resources/log4j2.xml                                                                         |   70 
 src/main/java/kr/wisestone/owl/monitor/Email.java                                                     |   76 
 src/main/java/kr/wisestone/owl/service/impl/IssueUserServiceImpl.java                                 |  128 
 src/main/java/kr/wisestone/owl/vo/PermissionVo.java                                                   |   54 
 src/main/java/kr/wisestone/owl/web/controller/PaymentController.java                                  |   79 
 src/main/java/kr/wisestone/owl/service/ProjectRolePermissionService.java                              |   10 
 src/main/java/kr/wisestone/owl/service/AttachedFileService.java                                       |   45 
 src/main/java/kr/wisestone/owl/domain/IssueTypeCustomField.java                                       |  103 
 src/main/java/kr/wisestone/owl/domain/enumType/ProjectType.java                                       |   10 
 src/main/java/kr/wisestone/owl/config/security/strategy/SecuritySessionExpiredStrategy.java           |   22 
 src/main/java/kr/wisestone/owl/repository/ReservationDisableUserRepository.java                       |    7 
 src/main/java/kr/wisestone/owl/web/controller/PermissionController.java                               |   33 
 src/main/java/kr/wisestone/owl/repository/PriorityRepository.java                                     |   12 
 src/main/java/kr/wisestone/owl/config/security/exception/LoginProcessingException.java                |   14 
 src/main/java/kr/wisestone/owl/mapper/IssueHistoryMapper.java                                         |   17 
 src/main/java/kr/wisestone/owl/vo/WorkspaceVo.java                                                    |  145 
 src/main/java/kr/wisestone/owl/web/controller/WorkspaceController.java                                |   87 
 src/main/java/kr/wisestone/owl/service/ProjectRoleUserService.java                                    |   22 
 src/main/java/kr/wisestone/owl/service/GanttService.java                                              |   36 
 src/main/resources/mybatis/query-template/attachedFile-template.xml                                   |   61 
 src/main/java/kr/wisestone/owl/web/controller/LanguageController.java                                 |   35 
 src/main/resources/mybatis/query-template/project-template.xml                                        |  426 
 src/main/resources/migration/V1_3__Alter_Table.sql                                                    |    2 
 src/main/java/kr/wisestone/owl/repository/UserInviteProjectRepository.java                            |   10 
 src/main/java/kr/wisestone/owl/mapper/CustomFieldMapper.java                                          |   17 
 src/main/java/kr/wisestone/owl/web/controller/GanttController.java                                    |  115 
 src/main/java/kr/wisestone/owl/domain/IssueRelation.java                                              |   56 
 src/main/java/kr/wisestone/owl/service/impl/GanttServiceImpl.java                                     |  115 
 src/main/java/kr/wisestone/owl/service/IssueNumberGeneratorService.java                               |   13 
 src/main/resources/mails/workspaceInviteSystemUserEmail.html                                          |   65 
 src/main/java/kr/wisestone/owl/vo/UserInviteVo.java                                                   |   45 
 src/main/java/kr/wisestone/owl/web/condition/FaqCondition.java                                        |   71 
 src/main/java/kr/wisestone/owl/web/view/FileDownloadView.java                                         |   66 
 src/main/resources/system_test.properties                                                             |   90 
 src/main/java/kr/wisestone/owl/monitor/EmailAttachment.java                                           |   36 
 src/main/java/kr/wisestone/owl/domain/ProjectRole.java                                                |  111 
 src/main/java/kr/wisestone/owl/service/UserHistoryService.java                                        |   11 
 src/main/java/kr/wisestone/owl/vo/ExportExcelAttrVo.java                                              |  156 
 src/main/java/kr/wisestone/owl/domain/IssueComment.java                                               |   63 
 src/main/java/kr/wisestone/owl/config/SessionConfiguration.java                                       |   73 
 src/main/java/kr/wisestone/owl/domain/Faq.java                                                        |   59 
 src/main/java/kr/wisestone/owl/repository/CustomFieldValueRepository.java                             |    7 
 src/main/java/kr/wisestone/owl/web/form/GuideForm.java                                                |   93 
 src/main/java/kr/wisestone/owl/service/impl/PriorityServiceImpl.java                                  |   99 
 src/main/java/kr/wisestone/owl/repository/EventRepository.java                                        |   15 
 src/main/java/kr/wisestone/owl/service/impl/IssueTypeCustomFieldServiceImpl.java                      |  191 
 src/main/java/kr/wisestone/owl/service/WidgetService.java                                             |   45 
 src/main/java/kr/wisestone/owl/config/security/handler/AjaxLogoutSuccessHandler.java                  |   20 
 src/main/resources/mails/regularPaymentModifyEmail.html                                               |  106 
 src/main/java/kr/wisestone/owl/service/impl/IssueReservationServiceImpl.java                          |  243 
 src/main/java/kr/wisestone/owl/web/form/IssueReservationForm.java                                     |   54 
 src/main/java/kr/wisestone/owl/repository/WorkflowTransitionRepository.java                           |   15 
 src/main/java/kr/wisestone/owl/vo/PriorityVo.java                                                     |   50 
 src/main/java/kr/wisestone/owl/service/AbstractService.java                                           |   19 
 src/main/resources/mails/userWithDrawEmail.html                                                       |   45 
 src/main/java/kr/wisestone/owl/domain/Workflow.java                                                   |   92 
 src/main/java/kr/wisestone/owl/vo/IssueStatusVo.java                                                  |  135 
 src/main/java/kr/wisestone/owl/repository/IssueCommentRepository.java                                 |   14 
 src/main/java/kr/wisestone/owl/web/controller/IssueRelationController.java                            |   61 
 src/main/java/kr/wisestone/owl/web/resolver/OwlResponseEntityExceptionHandler.java                    |   87 
 src/main/java/kr/wisestone/owl/common/ExcelConditionCheck.java                                        |   69 
 src/main/java/kr/wisestone/owl/util/ApplicationContextUtil.java                                       |   48 
 src/main/resources/mails/totalStatisticsEmail.html                                                    |   77 
 src/main/java/kr/wisestone/owl/repository/IssueHistoryRepository.java                                 |   13 
 src/main/resources/META-INF/orm.xml                                                                   |  189 
 src/main/java/kr/wisestone/owl/web/controller/WebSocketSessionController.java                         |   68 
 src/main/java/kr/wisestone/owl/web/form/UserInviteForm.java                                           |   44 
 src/main/java/kr/wisestone/owl/service/PaymentHistoryService.java                                     |   15 
 src/main/java/kr/wisestone/owl/domain/Notice.java                                                     |   47 
 src/main/resources/system_prod.properties                                                             |   92 
 src/main/java/kr/wisestone/owl/constant/MngPermission.java                                            |   63 
 src/main/java/kr/wisestone/owl/repository/AttachedFileRepository.java                                 |   10 
 src/main/java/kr/wisestone/owl/service/IssueStatusService.java                                        |   51 
 src/main/java/kr/wisestone/owl/mapper/QnaMapper.java                                                  |   14 
 src/main/java/kr/wisestone/owl/service/impl/CustomFieldValueServiceImpl.java                          |  125 
 src/main/java/kr/wisestone/owl/service/PaymentService.java                                            |   29 
 src/main/java/kr/wisestone/owl/config/KafkaConfiguration.java                                         |   71 
 src/main/java/kr/wisestone/owl/web/condition/IssueHistoryCondition.java                               |   54 
 src/main/java/kr/wisestone/owl/web/controller/QnaController.java                                      |   76 
 src/main/java/kr/wisestone/owl/mapper/ProjectRoleUserMapper.java                                      |   19 
 src/main/java/kr/wisestone/owl/service/CustomFieldValueService.java                                   |   13 
 src/main/java/kr/wisestone/owl/web/controller/IssueReservationController.java                         |   46 
 src/main/java/kr/wisestone/owl/repository/IssueCustomFieldValueRepository.java                        |   13 
 src/main/java/kr/wisestone/owl/config/ElasticSearchConfiguration.java                                 |   24 
 src/main/resources/migration/V1_6__Alter_Table.sql                                                    |   59 
 src/main/java/kr/wisestone/owl/domain/enumType/IssueHistoryType.java                                  |   11 
 src/main/java/kr/wisestone/owl/util/MapUtil.java                                                      |  191 
 src/main/java/kr/wisestone/owl/repository/ProjectClosureRepository.java                               |   14 
 src/main/java/kr/wisestone/owl/repository/IssueVersionRepository.java                                 |   10 
 src/main/java/kr/wisestone/owl/service/impl/IssueRiskServiceImpl.java                                 |   84 
 src/main/java/kr/wisestone/owl/repository/IssueReservationRepository.java                             |   10 
 src/main/java/kr/wisestone/owl/service/impl/FaqServiceImpl.java                                       |  167 
 src/main/java/kr/wisestone/owl/web/form/EventForm.java                                                |  111 
 src/main/java/kr/wisestone/owl/constant/ElasticSearchConstants.java                                   |   14 
 src/main/resources/mails/regularPaymentCancelByAccountingManagerEmail.html                            |   98 
 src/main/resources/mails/issueRemoveEmail.html                                                        |  161 
 src/main/java/kr/wisestone/owl/web/controller/IssueHistoryController.java                             |   37 
 src/main/java/kr/wisestone/owl/web/condition/IssueVersionCondition.java                               |   60 
 src/main/java/kr/wisestone/owl/mapper/IssueStatusMapper.java                                          |   17 
 src/main/java/kr/wisestone/owl/domain/Priority.java                                                   |   86 
 src/main/java/kr/wisestone/owl/domain/IssueReservation.java                                           |   72 
 src/main/java/kr/wisestone/owl/util/PageUtil.java                                                     |   59 
 src/main/java/kr/wisestone/owl/domain/enumType/IssueReservationType.java                              |   11 
 src/main/java/kr/wisestone/owl/service/IssueReservationService.java                                   |   20 
 src/main/resources/mails/issueAddEmail.html                                                           |  161 
 src/main/java/kr/wisestone/owl/service/WorkspaceService.java                                          |   54 
 src/main/java/kr/wisestone/owl/initializer/AppInitializer.java                                        |   52 
 src/main/java/kr/wisestone/owl/web/form/IssueCommentForm.java                                         |   62 
 src/main/java/kr/wisestone/owl/web/condition/IssueStatusCondition.java                                |  132 
 src/main/java/kr/wisestone/owl/web/controller/IssueVersionController.java                             |   37 
 src/main/resources/mails/projectManagerExcludeAndDefaultIncludeEmail.html                             |   82 
 src/main/java/kr/wisestone/owl/config/DataBaseConfiguration.java                                      |  175 
 src/main/java/kr/wisestone/owl/repository/IssueTypeRepository.java                                    |   18 
 src/main/java/kr/wisestone/owl/service/UserInviteService.java                                         |   19 
 src/main/java/kr/wisestone/owl/web/condition/WorkflowStatusCondition.java                             |   26 
 src/main/java/kr/wisestone/owl/service/impl/WorkflowTransitionServiceImpl.java                        |  316 
 src/main/java/kr/wisestone/owl/web/form/IssueTypeForm.java                                            |   84 
 src/main/java/kr/wisestone/owl/constant/MsgConstants.java                                             |  209 
 src/main/java/kr/wisestone/owl/mapper/WorkspaceMapper.java                                            |   13 
 src/main/java/kr/wisestone/owl/config/ThymeleafConfiguration.java                                     |   33 
 src/main/java/kr/wisestone/owl/repository/UserInviteRepository.java                                   |   15 
 src/main/java/kr/wisestone/owl/web/condition/IssueTypeCustomFieldCondition.java                       |   36 
 src/main/java/kr/wisestone/owl/service/IssueService.java                                              |   69 
 src/main/java/kr/wisestone/owl/web/controller/GuideController.java                                    |   89 
 src/main/java/kr/wisestone/owl/repository/PaymentHistoryRepository.java                               |   11 
 src/main/java/kr/wisestone/owl/web/controller/IssueTypeCustomFieldController.java                     |   51 
 src/main/java/kr/wisestone/owl/vo/IssueRelationVo.java                                                |   40 
 src/main/resources/mails/issueSendEmail.html                                                          |  161 
 src/main/java/kr/wisestone/owl/config/kafka/KafkaReceiver.java                                        |   28 
 src/main/java/kr/wisestone/owl/web/condition/GuideCondition.java                                      |   71 
 src/main/java/kr/wisestone/owl/service/impl/SystemEmailServiceImpl.java                               |  436 
 src/main/java/kr/wisestone/owl/web/form/QnaForm.java                                                  |   75 
 src/main/resources/migration/V1_5__Alter_Table.sql                                                    |   10 
 src/main/resources/mybatis/query-template/issueStatus-template.xml                                    |   55 
 src/main/java/kr/wisestone/owl/repository/IssueRepository.java                                        |    7 
 src/main/java/kr/wisestone/owl/domain/enumType/CustomFieldType.java                                   |   10 
 src/main/java/kr/wisestone/owl/service/IssueTypeCustomFieldService.java                               |   21 
 src/main/java/kr/wisestone/owl/config/WebSocketStompConfiguration.java                                |   30 
 src/main/java/kr/wisestone/owl/vo/IssueTypeVo.java                                                    |   63 
 src/main/java/kr/wisestone/owl/web/controller/IssueCommentController.java                             |   58 
 src/main/java/kr/wisestone/owl/web/form/CustomFieldForm.java                                          |  106 
 src/main/java/kr/wisestone/owl/config/AppConfiguration.java                                           |   15 
 src/main/java/kr/wisestone/owl/service/IssueCustomFieldValueService.java                              |   33 
 src/main/java/kr/wisestone/owl/web/controller/IssueTypeController.java                                |   97 
 src/main/java/kr/wisestone/owl/domain/IssueVersion.java                                               |   88 
 src/main/java/kr/wisestone/owl/web/controller/SystemEmailController.java                              |   35 
 src/main/java/kr/wisestone/owl/config/websocket/WebSocketHandshakeHandler.java                        |   16 
 src/main/java/kr/wisestone/owl/repository/IssueSearchRepository.java                                  |    8 
 src/main/java/kr/wisestone/owl/repository/ProjectRoleRepository.java                                  |   15 
 src/main/java/kr/wisestone/owl/service/IssueCommentService.java                                       |   20 
 src/main/java/kr/wisestone/owl/vo/PaymentHistoryVo.java                                               |   82 
 src/main/java/kr/wisestone/owl/domain/CustomField.java                                                |  113 
 src/main/java/kr/wisestone/owl/vo/NoticeVo.java                                                       |   45 
 src/main/java/kr/wisestone/owl/service/CustomFieldService.java                                        |   35 
 src/main/java/kr/wisestone/owl/vo/CustomFieldValueVo.java                                             |   27 
 src/main/java/kr/wisestone/owl/repository/UserHistoryRepository.java                                  |    8 
 src/main/java/kr/wisestone/owl/monitor/MailMonitor.java                                               |  226 
 src/main/java/kr/wisestone/owl/util/ConvertUtil.java                                                  |  552 
 src/main/resources/mybatis/query-template/issueHistory-template.xml                                   |   26 
 src/main/java/kr/wisestone/owl/util/StringTemplateUtil.java                                           |   18 
 src/main/java/kr/wisestone/owl/service/IssueVersionService.java                                       |   16 
 src/main/java/kr/wisestone/owl/service/IssueTableConfigService.java                                   |   14 
 src/main/java/kr/wisestone/owl/domain/UserWithDraw.java                                               |   42 
 src/main/resources/system_dev.properties                                                              |  102 
 src/main/java/kr/wisestone/owl/domain/IssueType.java                                                  |   98 
 src/main/java/kr/wisestone/owl/domain/Issue.java                                                      |  277 
 src/main/java/kr/wisestone/owl/config/CommonConfiguration.java                                        |  174 
 src/main/java/kr/wisestone/owl/mapper/NoticeMapper.java                                               |   14 
 src/main/java/kr/wisestone/owl/repository/SystemRolePermissionRepository.java                         |    9 
 src/main/java/kr/wisestone/owl/domain/SystemEmail.java                                                |   67 
 src/main/java/kr/wisestone/owl/service/impl/ProjectClosureServiceImpl.java                            |   30 
 src/main/java/kr/wisestone/owl/service/WorkflowTransitionService.java                                 |   24 
 src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java                              |  774 
 src/main/resources/mails/userSearchPasswordEmail.html                                                 |   67 
 src/main/java/kr/wisestone/owl/domain/UserLikeIssue.java                                              |   62 
 src/main/java/kr/wisestone/owl/repository/GuideRepository.java                                        |   15 
 src/main/java/kr/wisestone/owl/vo/AttachedFileVo.java                                                 |   72 
 src/main/java/kr/wisestone/owl/web/condition/IssueCondition.java                                      |  396 
 src/main/java/kr/wisestone/owl/repository/PermissionRepository.java                                   |   15 
 src/main/java/kr/wisestone/owl/web/controller/UserController.java                                     |  224 
 src/main/java/kr/wisestone/owl/web/converter/FileHttpMessageConverter.java                            |   75 
 src/main/java/kr/wisestone/owl/config/persistence/routing/TransactionDefinitionRoutingDataSource.java |   39 
 src/main/java/kr/wisestone/owl/service/impl/WorkflowServiceImpl.java                                  |  343 
 src/main/java/kr/wisestone/owl/constant/MailConstants.java                                            |   61 
 src/main/java/kr/wisestone/owl/web/form/NoticeForm.java                                               |   75 
 src/main/java/kr/wisestone/owl/repository/PaymentRepository.java                                      |    7 
 src/main/java/kr/wisestone/owl/web/controller/IssueUserController.java                                |   40 
 src/main/resources/mails/workspaceMaxStorageExcessEmail.html                                          |   85 
 src/main/resources/mybatis/query-template/issueUser-template.xml                                      |   45 
 src/main/java/kr/wisestone/owl/vo/PaymentVo.java                                                      |   43 
 src/main/java/kr/wisestone/owl/web/controller/AttatchedFileController.java                            |   54 
 src/main/java/kr/wisestone/owl/config/security/handler/AjaxAuthenticationFailureHandler.java          |   57 
 src/main/java/kr/wisestone/owl/domain/UserInviteProject.java                                          |   55 
 src/main/java/kr/wisestone/owl/vo/EventVo.java                                                        |   82 
 src/main/java/kr/wisestone/owl/web/controller/UserInviteController.java                               |   36 
 src/main/resources/mybatis/config/mybatis-config.xml                                                  |    9 
 src/main/java/kr/wisestone/owl/vo/MessageVo.java                                                      |   34 
 src/main/java/kr/wisestone/owl/util/ThreadCounter.java                                                |   37 
 src/main/java/kr/wisestone/owl/web/view/AbstractExcelView.java                                        |  143 
 src/main/java/kr/wisestone/owl/vo/ResMessageVo.java                                                   |   53 
 src/main/java/kr/wisestone/owl/domain/IssueHistory.java                                               |   75 
 src/main/java/kr/wisestone/owl/service/EventService.java                                              |   25 
 src/main/java/kr/wisestone/owl/service/WorkflowStatusService.java                                     |   29 
 src/main/java/kr/wisestone/owl/vo/ProjectVo.java                                                      |  150 
 src/main/java/kr/wisestone/owl/service/impl/WorkflowStatusServiceImpl.java                            |  209 
 src/main/java/kr/wisestone/owl/web/controller/CustomFieldController.java                              |   98 
 src/main/java/kr/wisestone/owl/service/SystemEmailService.java                                        |   26 
 src/main/resources/mails/projectDefaultIncludeEmail.html                                              |   83 
 src/main/java/kr/wisestone/owl/vo/ExportExcelVo.java                                                  |   64 
 src/main/java/kr/wisestone/owl/mapper/GuideMapper.java                                                |   14 
 src/main/java/kr/wisestone/owl/domain/enumType/IssueStatusType.java                                   |   10 
 src/main/java/kr/wisestone/owl/web/form/IssueStatusForm.java                                          |  122 
 src/main/java/kr/wisestone/owl/service/PriorityService.java                                           |   21 
 src/main/java/kr/wisestone/owl/vo/WorkflowTransitionVo.java                                           |   94 
 src/main/resources/mybatis/query-template/customField-template.xml                                    |   54 
 src/main/java/kr/wisestone/owl/service/impl/IssueRelationServiceImpl.java                             |  121 
 src/main/java/kr/wisestone/owl/domain/Qna.java                                                        |   47 
 src/main/java/kr/wisestone/owl/web/controller/WidgetController.java                                   |  157 
 src/main/java/kr/wisestone/owl/vo/WorkflowVo.java                                                     |   67 
 src/main/java/kr/wisestone/owl/web/form/WorkspaceForm.java                                            |   50 
 src/main/resources/migration/V1_1__Initial_Setup.sql                                                  |  840 +
 src/main/java/kr/wisestone/owl/repository/NoticeRepository.java                                       |    7 
 src/main/java/kr/wisestone/owl/service/impl/UserWorkspaceServiceImpl.java                             |  253 
 src/main/java/kr/wisestone/owl/util/SecurityUtils.java                                                |  131 
 src/main/java/kr/wisestone/owl/web/controller/WorkflowController.java                                 |  100 
 src/main/java/kr/wisestone/owl/domain/ReservationDisableUser.java                                     |   47 
 src/main/java/kr/wisestone/owl/repository/IssueRelationRepository.java                                |   10 
 src/main/java/kr/wisestone/owl/web/form/UserWorkspaceForm.java                                        |   37 
 src/main/java/kr/wisestone/owl/domain/ManageUser.java                                                 |   80 
 src/main/resources/mails/workspaceInviteNewUserEmail.html                                             |   65 
 src/main/java/kr/wisestone/owl/mapper/IssueCustomFieldValueMapper.java                                |   24 
 src/main/java/kr/wisestone/owl/domain/IssueStatus.java                                                |  136 
 src/main/java/kr/wisestone/owl/web/condition/AttachedFileCondition.java                               |   47 
 src/main/java/kr/wisestone/owl/repository/ProjectRoleUserRepository.java                              |   14 
 src/main/java/kr/wisestone/owl/web/form/ProjectForm.java                                              |  157 
 src/main/resources/mybatis/query-template/widget-template.xml                                         |  746 
 src/main/java/kr/wisestone/owl/domain/Project.java                                                    |  200 
 src/main/resources/mails/userJoinStatisticsEmail.html                                                 |   84 
 src/main/resources/mails/regularPaymentCancelEmail.html                                               |   87 
 src/main/java/kr/wisestone/owl/domain/ProjectClosure.java                                             |   49 
 src/main/resources/migration/V1_2__Alter_Table.sql                                                    |    1 
 src/main/java/kr/wisestone/owl/service/IssueHistoryService.java                                       |   56 
 src/main/java/kr/wisestone/owl/vo/ManageUserVo.java                                                   |   87 
 src/main/resources/mybatis/query-template/workflow-template.xml                                       |   37 
 src/main/java/kr/wisestone/owl/web/form/IssueTypeCustomFieldForm.java                                 |   62 
 src/main/java/kr/wisestone/owl/repository/SystemRoleUserRepository.java                               |    8 
 src/main/java/kr/wisestone/owl/service/impl/IssueTableConfigServiceImpl.java                          |   89 
 src/main/java/kr/wisestone/owl/vo/IssueReservationVo.java                                             |   45 
 src/main/java/kr/wisestone/owl/web/form/PaymentForm.java                                              |  171 
 src/main/resources/migration/V1_8__Alter_Table.sql                                                    |   63 
 src/main/java/kr/wisestone/owl/web/condition/IssueTypeCondition.java                                  |   72 
 src/main/java/kr/wisestone/owl/util/DateUtil.java                                                     |  166 
 src/main/java/kr/wisestone/owl/vo/ResPage.java                                                        |   98 
 src/main/java/kr/wisestone/owl/service/impl/PaymentServiceImpl.java                                   |  789 +
 src/main/java/kr/wisestone/owl/web/condition/WorkflowCondition.java                                   |   73 
 src/main/java/kr/wisestone/owl/repository/IssueStatusRepository.java                                  |   18 
 src/main/java/kr/wisestone/owl/domain/enumType/IssueModifyType.java                                   |   16 
 src/main/java/kr/wisestone/owl/vo/IssueTypeCustomFieldVo.java                                         |   36 
 src/main/java/kr/wisestone/owl/vo/GuideVo.java                                                        |   64 
 src/main/java/kr/wisestone/owl/web/condition/EventCondition.java                                      |   71 
 src/main/java/kr/wisestone/owl/web/controller/IssueTableConfigController.java                         |   46 
 src/main/resources/mails/workspaceExpireAlarmEmail.html                                               |  265 
 src/main/resources/mails/workspaceJoinEmail.html                                                      |  281 
 src/main/java/kr/wisestone/owl/domain/BaseEntity.java                                                 |   76 
 src/main/java/kr/wisestone/owl/service/GuideService.java                                              |   25 
 src/main/java/kr/wisestone/owl/service/impl/ProjectRoleServiceImpl.java                               |   87 
 src/main/java/kr/wisestone/owl/vo/WorkflowStatusVo.java                                               |  132 
 src/main/java/kr/wisestone/owl/web/view/ExcelView.java                                                |  333 
 src/main/java/kr/wisestone/owl/domain/ProjectRolePermission.java                                      |   55 
 src/main/java/kr/wisestone/owl/service/impl/AbstractServiceImpl.java                                  |   93 
 src/main/java/kr/wisestone/owl/web/condition/UserCondition.java                                       |  164 
 src/main/java/kr/wisestone/owl/domain/UserWorkspace.java                                              |   91 
 src/main/java/kr/wisestone/owl/web/controller/UserHistoryController.java                              |   46 
 src/main/java/kr/wisestone/owl/domain/enumType/FileType.java                                          |   11 
 src/main/java/kr/wisestone/owl/domain/AttachedFile.java                                               |  136 
 src/main/resources/migration/V1_7__Alter_Table.sql                                                    |    1 
 src/main/java/kr/wisestone/owl/domain/PaymentHistory.java                                             |  139 
 src/main/java/kr/wisestone/owl/domain/enumType/EmailType.java                                         |   42 
 src/main/java/kr/wisestone/owl/service/impl/ProjectServiceImpl.java                                   | 1040 +
 src/main/java/kr/wisestone/owl/constant/Constants.java                                                |   16 
 src/main/java/kr/wisestone/owl/web/form/IssueStatusChangeForm.java                                    |   87 
 src/main/java/kr/wisestone/owl/domain/IssueRisk.java                                                  |   77 
 src/main/java/kr/wisestone/owl/service/UserService.java                                               |   84 
 src/main/java/kr/wisestone/owl/repository/IssueUserRepository.java                                    |    9 
 src/main/java/kr/wisestone/owl/service/ProjectService.java                                            |   53 
 src/main/java/kr/wisestone/owl/domain/interceptor/AuditLogInterceptor.java                            |   80 
 src/main/resources/mybatis/query-template/user-template.xml                                           |  174 
 src/main/java/kr/wisestone/owl/config/security/service/UserSecurityService.java                       |   36 
 src/main/java/kr/wisestone/owl/service/ProjectRoleService.java                                        |   15 
 src/main/java/kr/wisestone/owl/web/controller/NoticeController.java                                   |   89 
 src/main/java/kr/wisestone/owl/repository/ProjectRepository.java                                      |   19 
 src/main/java/kr/wisestone/owl/service/IssueRelationService.java                                      |   22 
 src/main/java/kr/wisestone/owl/web/condition/IssueRelationCondition.java                              |   54 
 src/main/resources/mybatis/query-template/issueCustomFieldValue-template.xml                          |   57 
 src/main/java/kr/wisestone/owl/service/impl/IssueNumberGeneratorServiceImpl.java                      |   77 
 src/main/java/kr/wisestone/owl/domain/User.java                                                       |  376 
 src/main/java/kr/wisestone/owl/util/CommonUtil.java                                                   |  922 +
 src/main/java/kr/wisestone/owl/web/controller/IssueController.java                                    |  146 
 src/main/java/kr/wisestone/owl/repository/UserRepository.java                                         |   32 
 src/main/java/kr/wisestone/owl/service/impl/NoticeServiceImpl.java                                    |  156 
 src/main/java/kr/wisestone/owl/service/impl/AttachedFileServiceImpl.java                              |  589 
 src/main/java/kr/wisestone/owl/service/impl/WidgetServiceImpl.java                                    | 1231 +
 src/main/java/kr/wisestone/owl/domain/UserInvite.java                                                 |   89 
 src/main/java/kr/wisestone/owl/config/persistence/aop/TransactionDefinitionInterceptor.java           |   68 
 src/main/java/kr/wisestone/owl/web/controller/IssueStatusController.java                              |  133 
 src/main/java/kr/wisestone/owl/vo/PageVo.java                                                         |   39 
 src/main/java/kr/wisestone/owl/repository/WorkflowStatusRepository.java                               |   22 
 src/main/java/kr/wisestone/owl/repository/IssueTableConfigRepository.java                             |    9 
 src/main/java/kr/wisestone/owl/domain/support/ZonedDateTimeTypeHandler.java                           |   51 
 src/main/java/kr/wisestone/owl/domain/enumType/SocialType.java                                        |   11 
 src/main/resources/mybatis/query-template/notice-template.xml                                         |   34 
 src/main/java/kr/wisestone/owl/mapper/UserMapper.java                                                 |   32 
 src/main/resources/mails/projectManagerIncludeEmail.html                                              |   82 
 src/main/java/kr/wisestone/owl/config/websocket/WebSocketService.java                                 |   64 
 src/main/java/kr/wisestone/owl/service/NoticeService.java                                             |   26 
 src/main/java/kr/wisestone/owl/mapper/ProjectMapper.java                                              |   34 
 src/main/java/kr/wisestone/owl/web/form/ManageUserForm.java                                           |  130 
 src/main/java/kr/wisestone/owl/config/security/handler/AjaxAuthenticationEntryPoint.java              |   48 
 src/main/java/kr/wisestone/owl/domain/ProjectRoleUser.java                                            |   56 
 src/main/java/kr/wisestone/owl/web/form/WorkflowForm.java                                             |  144 
 src/main/java/kr/wisestone/owl/util/WebAppUtil.java                                                   |  275 
 src/main/java/kr/wisestone/owl/scheduler/Scheduler.java                                               |   98 
 src/main/resources/mybatis/query-template/issue-template.xml                                          |  426 
 src/main/java/kr/wisestone/owl/mapper/IssueUserMapper.java                                            |   21 
 src/main/resources/mybatis/query-template/projectRoleUser-template.xml                                |   46 
 src/main/java/kr/wisestone/owl/service/ReservationDisableUserService.java                             |   14 
 src/main/java/kr/wisestone/owl/web/condition/IssueCustomFieldValueCondition.java                      |  100 
 src/main/resources/mails/workspaceExpireEmail.html                                                    |  180 
 src/main/java/kr/wisestone/owl/domain/enumType/ServiceType.java                                       |    9 
 src/main/java/kr/wisestone/owl/domain/Permission.java                                                 |   80 
 src/main/java/kr/wisestone/owl/config/websocket/RedisSubscriber.java                                  |   34 
 src/main/java/kr/wisestone/owl/service/UserWithDrawService.java                                       |   11 
 src/main/java/kr/wisestone/owl/mapper/UserWorkspaceMapper.java                                        |   17 
 src/main/java/kr/wisestone/owl/service/impl/IssueTypeServiceImpl.java                                 |  353 
 src/main/java/kr/wisestone/owl/domain/IssueSearch.java                                                |   60 
 src/main/java/kr/wisestone/owl/config/WebMvcConfiguration.java                                        |   88 
 src/main/java/kr/wisestone/owl/web/condition/UserWorkspaceCondition.java                              |   80 
 src/main/resources/mybatis/query-template/issueType-template.xml                                      |   38 
 src/main/java/kr/wisestone/owl/web/condition/UserHistoryCondition.java                                |   25 
 src/main/resources/mails/regularPaymentEmail.html                                                     |  103 
 src/main/java/kr/wisestone/owl/service/impl/IssueCommentServiceImpl.java                              |  139 
 src/main/resources/mybatis/query-template/event-template.xml                                          |   39 
 src/main/java/kr/wisestone/owl/service/impl/ProjectRolePermissionServiceImpl.java                     |   48 
 src/main/java/kr/wisestone/owl/config/websocket/RedisPublisher.java                                   |   19 
 src/main/java/kr/wisestone/owl/util/ElasticSearchUtil.java                                            |  112 
 src/main/java/kr/wisestone/owl/service/WorkflowService.java                                           |   40 
 src/main/resources/mails/projectManagerExcludeEmail.html                                              |   82 
 src/main/java/kr/wisestone/owl/service/ManageUserService.java                                         |   23 
 src/main/java/kr/wisestone/owl/service/UserWorkspaceService.java                                      |   43 
 src/main/java/kr/wisestone/owl/vo/IssueHistoryVo.java                                                 |  114 
 src/main/java/kr/wisestone/owl/domain/Severity.java                                                   |   85 
 src/main/java/kr/wisestone/owl/domain/IssueUser.java                                                  |   65 
 src/main/java/kr/wisestone/owl/service/impl/EventServiceImpl.java                                     |  158 
 src/main/resources/migration/V1_4__Alter_Table.sql                                                    |   14 
 src/main/java/kr/wisestone/owl/mapper/WidgetMapper.java                                               |   82 
 src/main/java/kr/wisestone/owl/repository/ProjectRolePermissionRepository.java                        |    7 
 src/main/java/kr/wisestone/owl/web/form/UserForm.java                                                 |  162 
 src/main/java/kr/wisestone/owl/config/ApplicationContextProvider.java                                 |   21 
 src/main/java/kr/wisestone/owl/repository/SeverityRepository.java                                     |   12 
 src/main/java/kr/wisestone/owl/service/impl/SeverityServiceImpl.java                                  |   99 
 src/main/java/kr/wisestone/owl/domain/SystemRolePermission.java                                       |   92 
 src/main/java/kr/wisestone/owl/repository/QnaRepository.java                                          |    7 
 src/main/java/kr/wisestone/owl/service/impl/ReservationDisableUserServiceImpl.java                    |   81 
 src/main/java/kr/wisestone/owl/domain/UserHistory.java                                                |   30 
 src/main/java/kr/wisestone/owl/web/form/IssueForm.java                                                |  260 
 src/main/java/kr/wisestone/owl/service/QnaService.java                                                |   24 
 src/main/java/kr/wisestone/owl/service/impl/CustomFieldServiceImpl.java                               |  408 
 src/main/java/kr/wisestone/owl/common/MessageAccessor.java                                            |   45 
 src/main/java/kr/wisestone/owl/config/security/filter/AjaxSessionExpiredFilter.java                   |   94 
 src/main/java/kr/wisestone/owl/mapper/WorkflowMapper.java                                             |   17 
 src/main/java/kr/wisestone/owl/domain/WorkflowTransition.java                                         |  154 
 src/main/resources/mybatis/query-template/workspace-template.xml                                      |  160 
 src/main/resources/mybatis/query-template/faq-template.xml                                            |   37 
 src/main/java/kr/wisestone/owl/service/impl/SystemRoleServiceImpl.java                                |   29 
 src/main/java/kr/wisestone/owl/mapper/IssueMapper.java                                                |   41 
 src/main/java/kr/wisestone/owl/type/LikeType.java                                                     |    7 
 src/main/java/kr/wisestone/owl/service/impl/UserServiceImpl.java                                      | 1427 +
 src/main/java/kr/wisestone/owl/web/condition/CustomFieldCondition.java                                |  101 
 src/main/java/kr/wisestone/owl/service/impl/ProjectRoleUserServiceImpl.java                           |  140 
 src/main/java/kr/wisestone/owl/mapper/FaqMapper.java                                                  |   14 
 src/main/java/kr/wisestone/owl/repository/SystemRoleRepository.java                                   |   10 
 src/main/resources/mails/workspaceMaxUserExcessEmail.html                                             |   73 
 476 files changed, 46,855 insertions(+), 0 deletions(-)

diff --git a/src/main/java/kr/wisestone/owl/annotation/Viewer.java b/src/main/java/kr/wisestone/owl/annotation/Viewer.java
new file mode 100644
index 0000000..5e8b174
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/annotation/Viewer.java
@@ -0,0 +1,13 @@
+package kr.wisestone.owl.annotation;
+
+import org.springframework.stereotype.Component;
+
+import java.lang.annotation.*;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Component
+public @interface Viewer {
+    String value() default "";
+}
diff --git a/src/main/java/kr/wisestone/owl/common/ExcelConditionCheck.java b/src/main/java/kr/wisestone/owl/common/ExcelConditionCheck.java
new file mode 100644
index 0000000..efa8dad
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/common/ExcelConditionCheck.java
@@ -0,0 +1,69 @@
+package kr.wisestone.owl.common;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.WebAppUtil;
+import kr.wisestone.owl.vo.ExportExcelAttrVo;
+import kr.wisestone.owl.vo.ExportExcelVo;
+import kr.wisestone.owl.web.view.ExcelView;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-12-18.
+ */
+@Component
+public class ExcelConditionCheck {
+
+    @Autowired
+    private SimpMessagingTemplate simpMessagingTemplate;
+
+    @Autowired
+    private WebAppUtil webAppUtil;
+
+    @Autowired
+    private MessageAccessor messageAccessor;
+
+    @Autowired
+    private ExcelView excelView;
+
+    public ModelAndView checkCondition(Map<String, Object> conditions, HttpServletRequest request, Model model) {
+        Map<String, Object> results;
+
+        try {
+            results = CommonUtil.getSearchConditions(request);
+            Iterator iterator = results.keySet().iterator();
+
+            while(iterator.hasNext()) {
+                String key = (String)iterator.next();
+                Object value = results.get(key);
+                conditions.put(key, value);
+            }
+        }
+        catch(IOException e) {
+            ExportExcelVo excelInfo = this.makeEmptyDownloadExcel();
+            excelInfo.setFileName("寃��깋 議곌굔 �삤瑜� - ���씠利덉뒪�넠 �떞�떦�옄�뿉寃� 臾몄쓽�븯�꽭�슂.");
+            this.simpMessagingTemplate.convertAndSendToUser(this.webAppUtil.getLoginUser().getAccount(), "/notification/system-alert", this.messageAccessor.getMessage(MsgConstants.EXCEL_CONDITIONS_NOT_EXIST));
+            model.addAttribute(Constants.EXCEL, excelInfo);
+            return new ModelAndView(this.excelView);
+        }
+
+        return null;
+    }
+
+    public ExportExcelVo makeEmptyDownloadExcel() {
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("name", "", 120, ExportExcelAttrVo.ALIGN_LEFT));
+        return excelInfo;
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/common/MessageAccessor.java b/src/main/java/kr/wisestone/owl/common/MessageAccessor.java
new file mode 100644
index 0000000..26e2acd
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/common/MessageAccessor.java
@@ -0,0 +1,45 @@
+package kr.wisestone.owl.common;
+
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.vo.MessageVo;
+import kr.wisestone.owl.vo.ResMessageVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.context.support.MessageSourceAccessor;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.LocaleResolver;
+
+import java.util.Locale;
+
+@Component
+public class MessageAccessor {
+    @Autowired
+    private MessageSourceAccessor messageSourceAccessor;
+
+    @Autowired
+    private LocaleResolver localeResolver;
+
+    public MessageSourceAccessor getMessageSourceAccessor() {
+        return messageSourceAccessor;
+    }
+
+    public String message(String code, Locale locale) {
+        return this.messageSourceAccessor.getMessage(code, locale);
+    }
+
+    public String message(String code, Object... args) {
+        return this.messageSourceAccessor.getMessage(code, args, LocaleContextHolder.getLocale());
+    }
+
+    public MessageVo getMessage(String code, Object... args) {
+        return new MessageVo(code, this.messageSourceAccessor.getMessage(code, args, LocaleContextHolder.getLocale()));
+    }
+
+    public ResMessageVo getResMessage(String code, String status, Object... args) {
+        return new ResMessageVo(this.messageSourceAccessor.getMessage(code, args, LocaleContextHolder.getLocale()), code, status);
+    }
+
+    public Object getResMessage(OwlRuntimeException ex, String status) {
+        return new ResMessageVo(ex.getMessage(), ex.getCode(), status);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/AppConfiguration.java b/src/main/java/kr/wisestone/owl/config/AppConfiguration.java
new file mode 100644
index 0000000..350bede
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/AppConfiguration.java
@@ -0,0 +1,15 @@
+package kr.wisestone.owl.config;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Created by jeong on 2017-08-01.
+ */
+@Configuration
+@ComponentScan(basePackages = "kr.wisestone.owl")
+@MapperScan(basePackages = "kr.wisestone.owl.mapper")
+public class AppConfiguration {
+
+}
diff --git a/src/main/java/kr/wisestone/owl/config/ApplicationContextProvider.java b/src/main/java/kr/wisestone/owl/config/ApplicationContextProvider.java
new file mode 100644
index 0000000..a9055a7
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/ApplicationContextProvider.java
@@ -0,0 +1,21 @@
+
+package kr.wisestone.owl.config;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+@Component("")
+public class ApplicationContextProvider implements ApplicationContextAware {
+    private static ApplicationContext context;
+
+    public static ApplicationContext getContext() {
+        return context;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
+        context = ctx;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/AsyncConfiguration.java b/src/main/java/kr/wisestone/owl/config/AsyncConfiguration.java
new file mode 100644
index 0000000..1730022
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/AsyncConfiguration.java
@@ -0,0 +1,82 @@
+package kr.wisestone.owl.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.annotation.AsyncConfigurer;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.SchedulingConfigurer;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+import org.springframework.scheduling.config.ScheduledTaskRegistrar;
+import org.springframework.util.ErrorHandler;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * Created by jeong on 2017-07-26.
+ */
+@Configuration
+@EnableScheduling
+@EnableAsync(order= Ordered.HIGHEST_PRECEDENCE)
+public class AsyncConfiguration implements AsyncConfigurer, SchedulingConfigurer {
+
+    private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
+
+    private static final String CORE_POOL_SIZE = "10";
+
+    private static final String MAX_POOL_SIZE = "100";
+
+    private static final String QUEUE_CAPACITY = "100000";
+
+    @Override
+    @Bean(name = "taskExecutor")
+    public ThreadPoolTaskExecutor getAsyncExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(Integer.parseInt(CORE_POOL_SIZE));
+        executor.setMaxPoolSize(Integer.parseInt(MAX_POOL_SIZE));
+        executor.setQueueCapacity(Integer.parseInt(QUEUE_CAPACITY));
+        executor.setThreadNamePrefix("owl-Executor-");
+        return executor;
+    }
+
+    @Bean
+    public ThreadPoolTaskScheduler taskScheduler() {
+        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
+        scheduler.setPoolSize(20);
+        scheduler.setThreadNamePrefix("task-");
+        scheduler.setAwaitTerminationSeconds(60);
+        scheduler.setWaitForTasksToCompleteOnShutdown(true);
+        scheduler.setErrorHandler(new ErrorHandler() {
+            @Override
+            public void handleError(Throwable t) {
+                log.error("task �떎�뻾�떆 �븣 �닔 �뾾�뒗 �뿉�윭媛� 諛쒖깮�뻽�뒿�땲�떎.", t);
+            }
+        });
+        scheduler.setRejectedExecutionHandler(new RejectedExecutionHandler() {
+            @Override
+            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
+                log.error("�븣 �닔 �뾾�뒗 �씠�쑀濡� task �떎�뻾�씠 嫄곗젅�릺�뿀�뒿�땲�떎.", r);
+            }
+        });
+
+        return scheduler;
+    }
+
+    @Override
+    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+        return new SimpleAsyncUncaughtExceptionHandler();
+    }
+
+    @Override
+    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
+        TaskScheduler taskScheduler = this.taskScheduler();
+        taskRegistrar.setTaskScheduler(taskScheduler);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/CommonConfiguration.java b/src/main/java/kr/wisestone/owl/config/CommonConfiguration.java
new file mode 100644
index 0000000..87d11b0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/CommonConfiguration.java
@@ -0,0 +1,174 @@
+package kr.wisestone.owl.config;
+
+import com.amazonaws.ClientConfiguration;
+import com.amazonaws.Protocol;
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.regions.Regions;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.AmazonS3ClientBuilder;
+import kr.wisestone.owl.util.PageUtil;
+import kr.wisestone.owl.util.WebAppUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.context.support.MessageSourceAccessor;
+import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
+import org.springframework.context.support.ReloadableResourceBundleMessageSource;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+import org.springframework.session.FindByIndexNameSessionRepository;
+import org.springframework.session.security.SpringSessionBackedSessionRegistry;
+
+import java.util.Properties;
+
+/**
+ * Created by jeong on 2017-08-14.
+ */
+@Configuration
+public class CommonConfiguration {
+
+    private static final String FILE_ENCODING = "UTF-8";
+    private static final String MAIL_DEBUG = "mail.debug";
+    private static final String MAIL_SMTP_STARTTLS_ENABLE = "mail.smtp.starttls.enable";
+    private static final String MAIL_SMTP_AUTH = "mail.smtp.auth";
+    private static final String MAIL_SMTP_SSL_TRUST = "mail.smtp.ssl.trust";
+
+    @Value("${aws.access.key}")
+    private String accessKey;
+
+    @Value("${aws.access.password}")
+    private String accessPassword;
+
+    @Autowired
+    private FindByIndexNameSessionRepository findByIndexNameSessionRepository;
+
+    @Value("${email.host}")
+    private String emailHost;
+    @Value("${email.port}")
+    private String emailPort;
+    @Value("${email.userName}")
+    private String emailUserName;
+    @Value("${email.password}")
+    private String emailPassword;
+    @Value("${email.transport.protocol}")
+    private String emailTransportProtocol;
+    @Value("${email.smtp.auth}")
+    private String emailSmtpAuth;
+    @Value("${email.smtp.starttle.enable}")
+    private String emailSmtpStarttleEnable;
+    @Value("${email.debug}")
+    private String emailDebug;
+    @Value("${email.sendUrl}")
+    private String emailSendUrl;
+
+    @Bean
+    public WebAppUtil webAppUtil() {
+        return new WebAppUtil();
+    }
+
+    @Bean
+    public PageUtil pageUtil() {
+        return new PageUtil();
+    }
+
+    @Profile("prod")
+    @Bean
+    public static PropertySourcesPlaceholderConfigurer prodProperties() {
+        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
+        configurer.setLocations(new ClassPathResource("system_prod.properties"));
+        configurer.setFileEncoding(FILE_ENCODING);
+
+        return configurer;
+    }
+
+    @Profile("dev")
+    @Bean
+    public static PropertySourcesPlaceholderConfigurer devProperties() {
+        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
+        configurer.setLocations(new ClassPathResource("system_dev.properties"));
+        configurer.setFileEncoding(FILE_ENCODING);
+
+        return configurer;
+    }
+
+    @Profile("test")
+    @Bean
+    public static PropertySourcesPlaceholderConfigurer testProperties() {
+        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
+        configurer.setLocations(new ClassPathResource("system_test.properties"));
+        configurer.setFileEncoding(FILE_ENCODING);
+
+        return configurer;
+    }
+
+    @Profile("design")
+    @Bean
+    public static PropertySourcesPlaceholderConfigurer designProperties() {
+        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
+        configurer.setLocations(new ClassPathResource("system_design.properties"));
+        configurer.setFileEncoding(FILE_ENCODING);
+
+        return configurer;
+    }
+
+    @Bean
+    public String getEmailSendUrl() { return this.emailSendUrl; }
+
+    @Bean
+    public MessageSourceAccessor messageSourceAccessor(ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource) {
+        return new MessageSourceAccessor(reloadableResourceBundleMessageSource);
+    }
+
+    @Bean
+    public ReloadableResourceBundleMessageSource messageSource() {
+        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
+        messageSource.setCacheSeconds(10);
+        messageSource.setDefaultEncoding(FILE_ENCODING);
+        messageSource.setBasenames("/WEB-INF/i18n/messages", "/WEB-INF/i18n/mail", "/WEB-INF/i18n/code");
+        messageSource.setUseCodeAsDefaultMessage(true);
+
+        return messageSource;
+    }
+
+    @Bean
+    public JavaMailSender javaMailSender() {
+        JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
+        javaMailSender.setHost(this.emailHost);
+        javaMailSender.setPort(Integer.valueOf(this.emailPort));
+        javaMailSender.setUsername(this.emailUserName);
+        javaMailSender.setPassword(this.emailPassword);
+
+        Properties javaMailProperties = new Properties();
+        javaMailProperties.setProperty(MAIL_SMTP_SSL_TRUST, this.emailHost);
+        javaMailProperties.setProperty(MAIL_SMTP_AUTH, this.emailSmtpAuth);
+        javaMailProperties.setProperty(MAIL_SMTP_STARTTLS_ENABLE, this.emailSmtpStarttleEnable);
+        javaMailProperties.setProperty(MAIL_DEBUG, this.emailDebug);
+        javaMailSender.setJavaMailProperties(javaMailProperties);
+
+        return javaMailSender;
+    }
+
+    @Bean
+    public AmazonS3 amazonS3() {
+        BasicAWSCredentials credentials = new BasicAWSCredentials(this.accessKey, this.accessPassword);
+        ClientConfiguration clientConfiguration = new ClientConfiguration();
+        clientConfiguration.setProtocol(Protocol.HTTP);
+
+        return AmazonS3ClientBuilder
+                .standard()
+                .withRegion(Regions.AP_NORTHEAST_2)
+                .withCredentials(new AWSStaticCredentialsProvider(credentials))
+                .withClientConfiguration(clientConfiguration).build();
+    }
+
+    @Bean
+    @SuppressWarnings("unchecked")
+    public SpringSessionBackedSessionRegistry sessionRegistry() {
+        return new SpringSessionBackedSessionRegistry(this.findByIndexNameSessionRepository);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/config/DataBaseConfiguration.java b/src/main/java/kr/wisestone/owl/config/DataBaseConfiguration.java
new file mode 100644
index 0000000..a87bec7
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/DataBaseConfiguration.java
@@ -0,0 +1,175 @@
+package kr.wisestone.owl.config;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import kr.wisestone.owl.config.persistence.aop.TransactionDefinitionInterceptor;
+import kr.wisestone.owl.config.persistence.routing.TransactionDefinitionRoutingDataSource;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.flywaydb.core.Flyway;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+import org.springframework.transaction.annotation.TransactionManagementConfigurer;
+import org.springframework.transaction.interceptor.TransactionAttributeSource;
+
+import javax.persistence.SharedCacheMode;
+import javax.persistence.ValidationMode;
+import javax.sql.DataSource;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by jeong on 2017-08-02.
+ */
+@Configuration
+@EnableTransactionManagement
+@EnableJpaRepositories(
+        basePackages = {"kr.wisestone.owl.repository"},
+        entityManagerFactoryRef = "entityManagerFactory",
+        transactionManagerRef = "jpaTransactionManager"
+)
+@EnableAspectJAutoProxy
+public class DataBaseConfiguration implements TransactionManagementConfigurer {
+
+    @Value("${db.primary.driverName}")
+    private String dbPrimaryDriverName;
+    @Value("${db.primary.url}")
+    private String dbPrimaryUrl;
+    @Value("${db.primary.userName}")
+    private String dbPrimaryUserName;
+    @Value("${db.primary.password}")
+    private String dbPrimaryPassword;
+
+    @Value("${db.replica1.url}")
+    private String dbReplica1PrimaryUrl;
+    @Value("${db.replica2.url}")
+    private String dbReplica2PrimaryUrl;
+    @Value("${db.replica3.url}")
+    private String dbReplica3PrimaryUrl;
+    @Value("${db.replica4.url}")
+    private String dbReplica4PrimaryUrl;
+    @Value("${db.replica5.url}")
+    private String dbReplica5PrimaryUrl;
+
+    @Bean
+    public TransactionDefinitionInterceptor transactionDefinitionInterceptor(TransactionAttributeSource transactionAttributeSource) {
+        return new TransactionDefinitionInterceptor(transactionAttributeSource);
+    }
+
+    @Bean
+    public DataSource dataSource() {
+        final TransactionDefinitionRoutingDataSource transactionDefinitionRoutingDataSource = new TransactionDefinitionRoutingDataSource();
+        final Map<Object, Object> targetDataSources = new HashMap<>();
+
+        this.setReplica(targetDataSources, this.dbReplica1PrimaryUrl, 1);
+        this.setReplica(targetDataSources, this.dbReplica2PrimaryUrl, 2);
+        this.setReplica(targetDataSources, this.dbReplica3PrimaryUrl, 3);
+        this.setReplica(targetDataSources, this.dbReplica4PrimaryUrl, 4);
+        this.setReplica(targetDataSources, this.dbReplica5PrimaryUrl, 5);
+
+        transactionDefinitionRoutingDataSource.setDefaultTargetDataSource(this.buildDataSource("primaryHikariPool", this.dbPrimaryUrl, this.dbPrimaryUserName, this.dbPrimaryPassword, this.dbPrimaryDriverName));    //  master db
+        transactionDefinitionRoutingDataSource.setTargetDataSources(targetDataSources);
+        return transactionDefinitionRoutingDataSource;
+    }
+
+    //  由ы뵆由ъ뭅 �꽕�젙
+    private void setReplica(Map<Object, Object> targetDataSources, String replicaUrl, int count) {
+        if (!StringUtils.isEmpty(replicaUrl)) {
+            targetDataSources.put("replica" + count, this.buildDataSource("replicaHikariPool" + count, replicaUrl, this.dbPrimaryUserName, this.dbPrimaryPassword, this.dbPrimaryDriverName));
+        }
+    }
+
+    private DataSource buildDataSource(String poolName, String jdbcUrl, String userName, String password, String driverClassName) {
+        HikariConfig hikariConfig = new HikariConfig();
+        hikariConfig.setMinimumIdle(40);
+        hikariConfig.setConnectionTimeout(10000);
+        hikariConfig.setMaxLifetime(58000);
+        hikariConfig.setValidationTimeout(10000);
+        hikariConfig.setMaximumPoolSize(40);
+        hikariConfig.setConnectionTestQuery("SELECT 1");
+        hikariConfig.setPoolName(poolName);
+        hikariConfig.setJdbcUrl(jdbcUrl);
+        hikariConfig.setUsername(userName);
+        hikariConfig.setPassword(password);
+        hikariConfig.setDriverClassName(driverClassName);
+
+        return new HikariDataSource(hikariConfig);
+    }
+
+    @Override
+    public PlatformTransactionManager annotationDrivenTransactionManager() {
+        return this.jpaTransactionManager();
+    }
+
+    @Bean
+    public PlatformTransactionManager jpaTransactionManager() {
+        JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
+        jpaTransactionManager.setEntityManagerFactory(this.entityManagerFactory().getObject());
+        return jpaTransactionManager;
+    }
+
+    @Bean
+    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
+        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
+        adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect");
+
+        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
+        factory.setJpaVendorAdapter(adapter);
+        factory.setDataSource(this.dataSource());
+        factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
+        factory.setValidationMode(ValidationMode.NONE);
+        factory.setPackagesToScan("kr.wisestone.owl.domain");
+        factory.setMappingResources("META-INF/orm.xml");
+        factory.setJpaPropertyMap(this.getJpaProperties());
+
+        return factory;
+    }
+
+    private Map<String, Object> getJpaProperties() {
+        Map<String, Object> jpaProperties = new HashMap<>();
+        jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
+        jpaProperties.put("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
+        jpaProperties.put("hibernate.physical_naming_strategy", "kr.wisestone.owl.domain.strategy.PrefixNamingStrategy");
+        jpaProperties.put("hibernate.show_sql", "false");
+        jpaProperties.put("hibernate.format_sql", "true");
+        jpaProperties.put("hibernate.use_sql_comments", "true");
+        jpaProperties.put("hibernate.session_factory.interceptor", "kr.wisestone.owl.domain.interceptor.AuditLogInterceptor");
+
+        return jpaProperties;
+    }
+
+    @Bean
+    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
+        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
+
+        sqlSessionFactoryBean.setDataSource(dataSource);
+        sqlSessionFactoryBean.setConfigLocation(
+                new PathMatchingResourcePatternResolver().getResource("classpath:/mybatis/config/mybatis-config.xml")
+        );
+        sqlSessionFactoryBean.setMapperLocations(
+                new PathMatchingResourcePatternResolver().getResources("classpath:/mybatis/query-template*//*.xml")
+        );
+
+        return sqlSessionFactoryBean.getObject();
+    }
+
+    @Bean(initMethod = "migrate")
+    public Flyway flyway(DataSource dataSource) {
+        Flyway flyway = new Flyway();
+        flyway.setBaselineOnMigrate(true);
+        flyway.setLocations("classpath:/migration");
+        flyway.setDataSource(dataSource);
+
+        return flyway;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/ElasticSearchConfiguration.java b/src/main/java/kr/wisestone/owl/config/ElasticSearchConfiguration.java
new file mode 100644
index 0000000..5a9d2b4
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/ElasticSearchConfiguration.java
@@ -0,0 +1,24 @@
+package kr.wisestone.owl.config;
+
+import org.apache.http.HttpHost;
+import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import java.util.Arrays;
+
+@Configuration
+public class ElasticSearchConfiguration {
+    @Value("${elastic.search.hosts}")
+    private String hosts;
+
+    @Bean
+    public RestHighLevelClient restHighLevelClient() {
+        String[] elasticSearchHosts = this.hosts.replaceAll("\\p{Z}", "").split(",");
+
+        return new RestHighLevelClient(
+                RestClient.builder(Arrays.stream(elasticSearchHosts).map(HttpHost::create).toArray(HttpHost[]::new))
+        );
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/KafkaConfiguration.java b/src/main/java/kr/wisestone/owl/config/KafkaConfiguration.java
new file mode 100644
index 0000000..077045e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/KafkaConfiguration.java
@@ -0,0 +1,71 @@
+package kr.wisestone.owl.config;
+
+import kr.wisestone.owl.service.impl.AttachedFileServiceImpl;
+import kr.wisestone.owl.config.kafka.KafkaReceiver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.kafka.support.serializer.JsonDeserializer;
+import org.springframework.kafka.support.serializer.JsonSerializer;
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.apache.kafka.common.serialization.StringDeserializer;
+import org.apache.kafka.common.serialization.StringSerializer;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.kafka.annotation.EnableKafka;
+import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
+import org.springframework.kafka.core.*;
+import java.util.HashMap;
+import java.util.Map;
+
+// edit by zenith : for disable kafka
+//@EnableKafka
+@Configuration
+public class KafkaConfiguration {
+    private static final Logger LOGGER = LoggerFactory.getLogger(AttachedFileServiceImpl.class);
+
+    @Value("${use.kafka}")
+    private boolean useKafka;
+
+    @Value("${kafka.bootstrap.servers}")
+    private String bootstrapServers;
+
+    @Value("${kafka.consumer.group.id}")
+    private String groupId;
+
+    @Bean
+    public ProducerFactory<String, Object> producerFactory() {
+        Map<String, Object> configProps = new HashMap<>();
+        configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, this.bootstrapServers);
+        configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
+        configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
+        return new DefaultKafkaProducerFactory<>(configProps);
+    }
+
+    @Bean
+    public KafkaTemplate<String, Object> kafkaTemplate() {
+        return new KafkaTemplate<>(this.producerFactory());
+    }
+
+    @Bean
+    public ConsumerFactory<String, Object> consumerFactory() {
+        Map<String, Object> configProps = new HashMap<>();
+        configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, this.bootstrapServers);
+        configProps.put(ConsumerConfig.GROUP_ID_CONFIG, this.groupId);
+        configProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
+        configProps.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 1000);
+        configProps.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 30000);
+        configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
+        configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
+
+        return new DefaultKafkaConsumerFactory<>(configProps);
+    }
+
+    @Bean
+    public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
+        ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
+        factory.setConsumerFactory(this.consumerFactory());
+        return factory;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java b/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java
new file mode 100644
index 0000000..20b073d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java
@@ -0,0 +1,160 @@
+package kr.wisestone.owl.config;
+
+import kr.wisestone.owl.config.security.filter.AjaxSessionExpiredFilter;
+import kr.wisestone.owl.config.security.handler.AjaxAuthenticationEntryPoint;
+import kr.wisestone.owl.config.security.handler.AjaxAuthenticationFailureHandler;
+import kr.wisestone.owl.config.security.handler.AjaxAuthenticationSuccessHandler;
+import kr.wisestone.owl.config.security.handler.AjaxLogoutSuccessHandler;
+import kr.wisestone.owl.config.security.service.UserSecurityService;
+import kr.wisestone.owl.config.security.strategy.SecuritySessionExpiredStrategy;
+import kr.wisestone.owl.constant.Constants;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.config.BeanIds;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.session.SessionRegistry;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.session.ConcurrentSessionFilter;
+import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+    private static final String REMEMBER_ME_KEY = "rememberMe";
+
+    @Autowired
+    private AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler;
+
+    @Autowired
+    private AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler;
+
+    @Autowired
+    private AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler;
+
+    @Autowired
+    private AjaxAuthenticationEntryPoint authenticationEntryPoint;
+
+    @Autowired
+    private UserSecurityService userSecurityService;
+
+    @Autowired
+    private SessionRegistry sessionRegistry;
+
+    @Autowired
+    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
+        auth.userDetailsService(this.userSecurityService)
+        .passwordEncoder(this.passwordEncoder());
+    }
+
+    @Bean
+    public DaoAuthenticationProvider authenticationProvider() {
+        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
+        authenticationProvider.setUserDetailsService(this.userSecurityService);
+        authenticationProvider.setPasswordEncoder(this.passwordEncoder());
+        return authenticationProvider;
+    }
+
+    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
+    @Override
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        return super.authenticationManagerBean();
+    }
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
+    }
+
+    @Bean
+    public AjaxSessionExpiredFilter ajaxSessionExpiredFilter() {
+        return new AjaxSessionExpiredFilter(this.sessionRegistry);
+    }
+
+    @Bean
+    public SecuritySessionExpiredStrategy securitySessionExpiredStrategy() {
+        return new SecuritySessionExpiredStrategy(Constants.SESSION_EXPIRE_REDIRECT_URL);
+    }
+
+    @Bean
+    public SpringSessionRememberMeServices springSessionRememberMeServices() {
+        SpringSessionRememberMeServices springSessionRememberMeServices =
+                new SpringSessionRememberMeServices();
+
+        springSessionRememberMeServices.setAlwaysRemember(false);
+        springSessionRememberMeServices.setRememberMeParameterName(REMEMBER_ME_KEY);
+        return springSessionRememberMeServices;
+    }
+
+    @Override
+    public void configure(WebSecurity web) {
+        web.ignoring()
+                .antMatchers("/**/*.{js,css}");
+    }
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http.formLogin()
+                .loginProcessingUrl("/security/login")
+                .successHandler(this.ajaxAuthenticationSuccessHandler)
+                .failureHandler(this.ajaxAuthenticationFailureHandler)
+                .usernameParameter("j_username")
+                .passwordParameter("j_password")
+                .permitAll();
+
+        http.logout()
+                .clearAuthentication(true)
+                .logoutUrl("/security/logout")
+                .logoutSuccessHandler(this.ajaxLogoutSuccessHandler)
+                .deleteCookies("JSESSIONID", REMEMBER_ME_KEY)
+                .invalidateHttpSession(true)
+                .permitAll();
+
+        http.headers()
+                .frameOptions()
+                .disable()
+                .and()
+                .csrf()
+                .disable()
+                .httpBasic()
+                .and()
+                .exceptionHandling()
+                .authenticationEntryPoint(this.authenticationEntryPoint);
+
+        http.authorizeRequests()
+                .antMatchers("/kakaoOAuth2CallBack").permitAll()
+                .antMatchers("/naverOAuth2CallBack").permitAll()
+                .antMatchers("/googleOAuth2CallBack").permitAll()
+                .antMatchers("/facebookOAuth2CallBack").permitAll()
+                .antMatchers("/user/addSocialLogin").permitAll()
+                .antMatchers("/user/add").permitAll()
+                .antMatchers("/user/getUserSession").permitAll()
+                .antMatchers("/user/returnEmailPassword").permitAll()
+                .antMatchers("/workspace/find").permitAll()
+                .antMatchers("/workspace/findPrimaryWorkspace").permitAll()
+                .antMatchers("/guide/detail").permitAll()
+                .antMatchers("/language/change").permitAll()
+                .antMatchers("/security/*").permitAll()
+                .antMatchers("/**/*").authenticated();
+
+
+        http.rememberMe()
+                .rememberMeServices(this.springSessionRememberMeServices());
+
+
+        http.sessionManagement()
+                .maximumSessions(1)
+                .maxSessionsPreventsLogin(false)
+                .expiredSessionStrategy(this.securitySessionExpiredStrategy())
+                .sessionRegistry(this.sessionRegistry).and().and()
+                .addFilterBefore(this.ajaxSessionExpiredFilter(), ConcurrentSessionFilter.class);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/SessionConfiguration.java b/src/main/java/kr/wisestone/owl/config/SessionConfiguration.java
new file mode 100644
index 0000000..56d136b
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/SessionConfiguration.java
@@ -0,0 +1,73 @@
+package kr.wisestone.owl.config;
+
+import kr.wisestone.owl.config.websocket.RedisSubscriber;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.listener.ChannelTopic;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import org.springframework.security.web.session.HttpSessionEventPublisher;
+import org.springframework.session.data.redis.config.ConfigureRedisAction;
+import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
+import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
+
+/**
+ * Created by jeong on 2019-02-28.
+ */
+@Configuration
+@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
+public class SessionConfiguration extends AbstractHttpSessionApplicationInitializer {
+    @Value("${redis.host}")
+    private String redisHost;
+
+    @Value("${redis.port}")
+    private int redisPort;
+
+    @Value("${redis.common.topic}")
+    private String redisCommonTopic;
+
+    @Bean
+    public RedisConnectionFactory redisConnectionFactory() {
+        return new LettuceConnectionFactory(this.redisHost, this.redisPort);
+    }
+
+    @Bean
+    public HttpSessionEventPublisher httpSessionEventPublisher() {
+        return new HttpSessionEventPublisher();
+    }
+
+    @Bean
+    public ConfigureRedisAction configureRedisAction() {
+        return ConfigureRedisAction.NO_OP;
+    }
+
+    @Bean
+    public RedisSubscriber redisSubscriber() {
+        return new RedisSubscriber();
+    }
+
+    @Bean
+    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory) {
+        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
+        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
+        redisMessageListenerContainer.addMessageListener(this.redisSubscriber(), new ChannelTopic(this.redisCommonTopic));
+        return redisMessageListenerContainer;
+    }
+
+    @Bean
+    public StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
+        stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
+        stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
+        stringRedisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(String.class));
+        return stringRedisTemplate;
+    }
+
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/config/ThymeleafConfiguration.java b/src/main/java/kr/wisestone/owl/config/ThymeleafConfiguration.java
new file mode 100644
index 0000000..5e062b0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/ThymeleafConfiguration.java
@@ -0,0 +1,33 @@
+package kr.wisestone.owl.config;
+
+import org.apache.commons.codec.CharEncoding;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.thymeleaf.spring5.SpringTemplateEngine;
+import org.thymeleaf.templatemode.TemplateMode;
+import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
+
+/**
+ * Created by wisestone on 2018-03-19.
+ */
+@Configuration
+public class ThymeleafConfiguration {
+
+    @Bean
+    public ClassLoaderTemplateResolver emailTemplateResolver() {
+        ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
+        emailTemplateResolver.setPrefix("mails/");
+        emailTemplateResolver.setSuffix(".html");
+        emailTemplateResolver.setTemplateMode(TemplateMode.HTML);
+        emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8);
+        emailTemplateResolver.setOrder(1);
+        return emailTemplateResolver;
+    }
+
+    @Bean
+    public SpringTemplateEngine templateEngine() {
+        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
+        templateEngine.addTemplateResolver(emailTemplateResolver());
+        return templateEngine;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/WebMvcConfiguration.java b/src/main/java/kr/wisestone/owl/config/WebMvcConfiguration.java
new file mode 100644
index 0000000..ce14a6f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/WebMvcConfiguration.java
@@ -0,0 +1,88 @@
+package kr.wisestone.owl.config;
+
+import kr.wisestone.owl.web.converter.FileHttpMessageConverter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.multipart.MultipartResolver;
+import org.springframework.web.multipart.commons.CommonsMultipartResolver;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.config.annotation.*;
+import org.springframework.web.servlet.i18n.CookieLocaleResolver;
+import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Created by jeong on 2017-08-01.
+ */
+@EnableWebMvc
+@Configuration
+public class WebMvcConfiguration implements WebMvcConfigurer {
+
+    @Override
+    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
+        converters.add(new StringHttpMessageConverter());
+
+        MappingJackson2HttpMessageConverter jsonConverter = this.mappingJackson2HttpMessageConverter();
+        jsonConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML));
+        converters.add(jsonConverter);
+
+        FileHttpMessageConverter fileConverter = this.fileHttpMessageConverter();
+        converters.add(fileConverter);
+    }
+
+    @Bean
+    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
+        return new MappingJackson2HttpMessageConverter();
+    }
+
+    @Bean
+    public FileHttpMessageConverter fileHttpMessageConverter() {
+        return new FileHttpMessageConverter();
+    }
+
+    @Bean
+    public MultipartResolver multipartResolver() {
+        CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
+        commonsMultipartResolver.setMaxUploadSize(314572800);
+        return commonsMultipartResolver;
+    }
+
+    @Bean
+    public LocaleChangeInterceptor localeChangeInterceptor() {
+        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
+        localeChangeInterceptor.setParamName("language");
+        return localeChangeInterceptor;
+    }
+
+    @Bean
+    public LocaleResolver localeResolver() {
+        CookieLocaleResolver localeResolver = new CookieLocaleResolver();
+        localeResolver.setCookieMaxAge(3600);
+        localeResolver.setDefaultLocale(new Locale("ko_KR"));
+        Locale locale = new Locale("ko", "KR");
+        locale.setDefault(locale);
+
+        return localeResolver;
+    }
+
+    @Override
+    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
+        configurer.enable();
+    }
+
+    @Override
+    public void addViewControllers(ViewControllerRegistry registry) {
+        registry.addViewController("/").setViewName("index.html");
+    }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(this.localeChangeInterceptor());
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/WebSocketStompConfiguration.java b/src/main/java/kr/wisestone/owl/config/WebSocketStompConfiguration.java
new file mode 100644
index 0000000..e39bc13
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/WebSocketStompConfiguration.java
@@ -0,0 +1,30 @@
+package kr.wisestone.owl.config;
+
+import kr.wisestone.owl.config.websocket.WebSocketHandshakeHandler;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.messaging.simp.config.MessageBrokerRegistry;
+import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
+import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
+import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
+import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
+
+@Configuration
+@EnableWebSocketMessageBroker
+public class WebSocketStompConfiguration implements WebSocketMessageBrokerConfigurer {
+
+    @Override
+    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
+        stompEndpointRegistry.addEndpoint("/owl-socket")
+                            .setAllowedOrigins("*")
+                            .addInterceptors(new HttpSessionHandshakeInterceptor())
+                            .setHandshakeHandler(new WebSocketHandshakeHandler())
+                            .withSockJS();
+    }
+
+    @Override
+    public void configureMessageBroker(MessageBrokerRegistry registry) {
+        registry.enableSimpleBroker("/session", "/notification", "/permission");
+        registry.setApplicationDestinationPrefixes("/app");
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/config/kafka/KafkaReceiver.java b/src/main/java/kr/wisestone/owl/config/kafka/KafkaReceiver.java
new file mode 100644
index 0000000..7aa6b54
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/kafka/KafkaReceiver.java
@@ -0,0 +1,28 @@
+package kr.wisestone.owl.config.kafka;
+
+import kr.wisestone.owl.util.CommonUtil;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+@Service
+public class KafkaReceiver {
+    private static final Logger log = LoggerFactory.getLogger(KafkaReceiver.class);
+
+    @Autowired
+    private SimpMessagingTemplate simpMessagingTemplate;
+
+    @KafkaListener(topics = "#{'${kafka.common.topic}'}")
+    public void receive(ConsumerRecord<?, ?> consumerRecord) {
+        Map<String, Object> messageMap = (Map<String, Object>) consumerRecord.value();
+
+        CommonUtil.sendWebSocketMessage(this.simpMessagingTemplate, messageMap);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/kafka/KafkaSender.java b/src/main/java/kr/wisestone/owl/config/kafka/KafkaSender.java
new file mode 100644
index 0000000..55b49d9
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/kafka/KafkaSender.java
@@ -0,0 +1,22 @@
+package kr.wisestone.owl.config.kafka;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+@Service
+public class KafkaSender {
+    private static final Logger log = LoggerFactory.getLogger(KafkaSender.class);
+
+    @Autowired
+    private KafkaTemplate<String, Object> kafkaTemplate;
+
+    public void send(String topic, Map<String, Object> message) {
+        if(kafkaTemplate != null)
+            this.kafkaTemplate.send(topic, message);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/persistence/aop/TransactionDefinitionInterceptor.java b/src/main/java/kr/wisestone/owl/config/persistence/aop/TransactionDefinitionInterceptor.java
new file mode 100644
index 0000000..5ad5d01
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/persistence/aop/TransactionDefinitionInterceptor.java
@@ -0,0 +1,68 @@
+package kr.wisestone.owl.config.persistence.aop;
+
+import java.lang.reflect.Method;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.core.NamedThreadLocal;
+import org.springframework.core.Ordered;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.interceptor.TransactionAttributeSource;
+
+
+@Aspect
+public class TransactionDefinitionInterceptor implements Ordered {
+
+    private static final ThreadLocal<TransactionDefinition> transactionDefinitionHolder = new NamedThreadLocal<>("Current aspect-driven transaction definition");
+
+    public static TransactionDefinition currentTransactionDefinition() {
+        return transactionDefinitionHolder.get();
+    }
+
+    public static boolean isCurrentTransactionReadOnly() {
+        return transactionDefinitionHolder.get() != null ? transactionDefinitionHolder.get().isReadOnly() : false;
+    }
+
+    private final TransactionAttributeSource transactionAttributeSource;
+
+    private int order = Ordered.LOWEST_PRECEDENCE - 10;
+
+    public TransactionDefinitionInterceptor(
+            TransactionAttributeSource transactionAttributeSource) {
+        this.transactionAttributeSource = transactionAttributeSource;
+    }
+
+    @Around("@annotation(transactional)")
+    public Object rememberTransactionDefinition(
+            ProceedingJoinPoint joinPoint,
+            Transactional transactional) throws Throwable {
+        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
+        Class<?> targetClass = joinPoint.getTarget().getClass();
+        TransactionDefinition transactionDefinition = transactionAttributeSource.getTransactionAttribute(method, targetClass);
+        boolean restore = false;
+        if (transactionDefinitionHolder.get() == null) {
+            transactionDefinitionHolder.set(transactionDefinition);
+            restore = true;
+        }
+        try {
+            return joinPoint.proceed();
+        } finally {
+            if (restore) {
+                transactionDefinitionHolder.set(null);
+            }
+        }
+    }
+
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+    @Override
+    public int getOrder() {
+        return order;
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/config/persistence/routing/TransactionDefinitionRoutingDataSource.java b/src/main/java/kr/wisestone/owl/config/persistence/routing/TransactionDefinitionRoutingDataSource.java
new file mode 100644
index 0000000..57d03c9
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/persistence/routing/TransactionDefinitionRoutingDataSource.java
@@ -0,0 +1,39 @@
+package kr.wisestone.owl.config.persistence.routing;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.config.persistence.aop.TransactionDefinitionInterceptor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class TransactionDefinitionRoutingDataSource extends AbstractRoutingDataSource {
+
+    private Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private List<Object> dataSourceKeys;
+
+    private final AtomicInteger currentIndex = new AtomicInteger();
+
+    @Override
+    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
+        super.setTargetDataSources(targetDataSources);
+        this.dataSourceKeys = Lists.newArrayList(targetDataSources.keySet());
+    }
+
+    @Override
+    protected Object determineCurrentLookupKey() {
+        if (TransactionDefinitionInterceptor
+                .isCurrentTransactionReadOnly()
+                && !dataSourceKeys.isEmpty()) {
+            int size = dataSourceKeys.size();
+            Object key = dataSourceKeys.get(currentIndex.getAndIncrement() % size);
+            return key;
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/config/security/exception/LoginProcessingException.java b/src/main/java/kr/wisestone/owl/config/security/exception/LoginProcessingException.java
new file mode 100644
index 0000000..a4b6e19
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/security/exception/LoginProcessingException.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.config.security.exception;
+
+import org.springframework.security.core.AuthenticationException;
+
+public class LoginProcessingException extends AuthenticationException {
+
+    public LoginProcessingException(String message) {
+        super(message);
+    }
+
+    public LoginProcessingException(String message, Throwable t) {
+        super(message, t);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/security/filter/AjaxSessionExpiredFilter.java b/src/main/java/kr/wisestone/owl/config/security/filter/AjaxSessionExpiredFilter.java
new file mode 100644
index 0000000..b537608
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/security/filter/AjaxSessionExpiredFilter.java
@@ -0,0 +1,94 @@
+package kr.wisestone.owl.config.security.filter;
+
+import kr.wisestone.owl.constant.Constants;
+import org.apache.commons.io.FilenameUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.session.SessionInformation;
+import org.springframework.security.core.session.SessionRegistry;
+import org.springframework.security.web.DefaultRedirectStrategy;
+import org.springframework.security.web.RedirectStrategy;
+import org.springframework.security.web.authentication.logout.LogoutHandler;
+import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
+import org.springframework.security.web.util.UrlUtils;
+import org.springframework.util.Assert;
+import org.springframework.web.filter.GenericFilterBean;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+
+public class AjaxSessionExpiredFilter extends GenericFilterBean {
+    private final Logger log = LoggerFactory.getLogger(AjaxSessionExpiredFilter.class);
+
+    private String ajaxHeader;
+    private SessionRegistry sessionRegistry;
+    private String expiredUrl;
+    private LogoutHandler[] handlers = new LogoutHandler[]{new SecurityContextLogoutHandler()};
+    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
+
+    public void destroy() {
+    }
+
+    public AjaxSessionExpiredFilter(SessionRegistry sessionRegistry) {
+        Assert.notNull(sessionRegistry, "SessionRegistry required");
+        this.sessionRegistry = sessionRegistry;
+    }
+
+    public AjaxSessionExpiredFilter(SessionRegistry sessionRegistry, String expiredUrl) {
+        Assert.notNull(sessionRegistry, "SessionRegistry required");
+        Assert.isTrue(expiredUrl == null || UrlUtils.isValidRedirectUrl(expiredUrl), expiredUrl + " isn\'t a valid redirect URL");
+        this.sessionRegistry = sessionRegistry;
+        this.expiredUrl = expiredUrl;
+    }
+
+    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest)req;
+        HttpServletResponse response = (HttpServletResponse)res;
+        HttpSession session = request.getSession(false);
+
+        String uri = request.getRequestURI();
+        if (FilenameUtils.isExtension(uri, "html") || uri.contains("/user/getUserSession") || uri.contains("/owl-socket")) {
+            chain.doFilter(request, response);
+            return;
+        }
+
+        if(session != null) {
+            SessionInformation info = this.sessionRegistry.getSessionInformation(session.getId());
+            if(info != null) {
+                if(info.isExpired()) {
+                    this.doLogoutAndFireErrorMessage(request, response);
+                    return;
+                }
+
+                this.sessionRegistry.refreshLastRequest(info.getSessionId());
+            }
+        }
+
+        chain.doFilter(request, response);
+    }
+
+    private void doLogoutAndFireErrorMessage(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        this.doLogout(request, response);
+        response.sendRedirect(Constants.SESSION_EXPIRE_REDIRECT_URL);
+    }
+
+    private void doLogout(HttpServletRequest request, HttpServletResponse response) {
+        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+        LogoutHandler[] var4 = this.handlers;
+        int var5 = var4.length;
+
+        for(int var6 = 0; var6 < var5; ++var6) {
+            LogoutHandler handler = var4[var6];
+            handler.logout(request, response, auth);
+        }
+
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/security/handler/AjaxAuthenticationEntryPoint.java b/src/main/java/kr/wisestone/owl/config/security/handler/AjaxAuthenticationEntryPoint.java
new file mode 100644
index 0000000..6ceae08
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/security/handler/AjaxAuthenticationEntryPoint.java
@@ -0,0 +1,48 @@
+package kr.wisestone.owl.config.security.handler;
+
+import kr.wisestone.owl.common.MessageAccessor;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.util.ConvertUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class AjaxAuthenticationEntryPoint implements AuthenticationEntryPoint {
+
+    private final Logger log = LoggerFactory.getLogger(AjaxAuthenticationEntryPoint.class);
+
+    @Autowired
+    protected MessageAccessor messageAccessor;
+
+    @Override
+    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        resJsonData.put(Constants.RES_KEY_MESSAGE,
+                this.messageAccessor.getResMessage(MsgConstants.SESSION_EXPIRED, Constants.RES_KEY_MSG_FAIL));
+
+        response.setContentType("application/json");
+        response.setCharacterEncoding("utf-8");
+        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+
+        String data = ConvertUtil.convertObjectToJson(resJsonData);
+
+        try (PrintWriter out = response.getWriter()) {
+            out.print(data);
+            out.flush();
+        } catch (IOException e) {
+            log.error(e.getMessage());
+        }
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/security/handler/AjaxAuthenticationFailureHandler.java b/src/main/java/kr/wisestone/owl/config/security/handler/AjaxAuthenticationFailureHandler.java
new file mode 100644
index 0000000..0086ae1
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/security/handler/AjaxAuthenticationFailureHandler.java
@@ -0,0 +1,57 @@
+package kr.wisestone.owl.config.security.handler;
+
+import kr.wisestone.owl.common.MessageAccessor;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.util.ConvertUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class AjaxAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
+
+    private final Logger log = LoggerFactory.getLogger(AjaxAuthenticationFailureHandler.class);
+
+    @Autowired
+    protected MessageAccessor messageAccessor;
+
+    @Override
+    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
+
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        if (exception instanceof BadCredentialsException) {
+            resJsonData.put(Constants.RES_KEY_MESSAGE, this.messageAccessor.getResMessage(MsgConstants.USER_NOT_EQUAL_PASSWORD, Constants.RES_KEY_MSG_FAIL));
+        }
+        else {
+            resJsonData.put(Constants.RES_KEY_MESSAGE, this.messageAccessor.getResMessage(exception.getMessage(), Constants.RES_KEY_MSG_FAIL));
+        }
+
+        response.setContentType("application/json");
+        response.setCharacterEncoding("utf-8");
+        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+
+        String data = ConvertUtil.convertObjectToJson(resJsonData);
+
+        try {
+            PrintWriter out = response.getWriter();
+            out.print(data);
+            out.flush();
+        } catch (IOException e) {
+            log.error(e.getMessage());
+        }
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/security/handler/AjaxAuthenticationSuccessHandler.java b/src/main/java/kr/wisestone/owl/config/security/handler/AjaxAuthenticationSuccessHandler.java
new file mode 100644
index 0000000..f1bf531
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/security/handler/AjaxAuthenticationSuccessHandler.java
@@ -0,0 +1,60 @@
+package kr.wisestone.owl.config.security.handler;
+
+import kr.wisestone.owl.common.MessageAccessor;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.ElasticSearchUtil;
+import kr.wisestone.owl.vo.UserVo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
+import org.springframework.stereotype.Component;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class AjaxAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
+
+    private final Logger log = LoggerFactory.getLogger(AjaxAuthenticationSuccessHandler.class);
+
+    @Autowired
+    protected MessageAccessor messageAccessor;
+
+    @Override
+    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse response, Authentication authentication) {
+
+        Map<String, Object> resJsonData = new HashMap<>();
+        User user = (User) authentication.getPrincipal();
+        try {
+            log.info(ElasticSearchUtil.makeUserSessionHistoryMessage(httpServletRequest, ConvertUtil.copyProperties(user, UserVo.class)));
+        }
+        catch (Exception ex) {
+            log.error(ex.getMessage());
+        }
+
+        resJsonData.put(Constants.RES_KEY_MESSAGE,
+                this.messageAccessor.getResMessage(MsgConstants.SUCCESS_REQUEST, Constants.RES_KEY_MSG_SUCCESS));
+
+        response.setContentType("application/json");
+        response.setCharacterEncoding("utf-8");
+        response.setStatus(HttpServletResponse.SC_OK);
+
+        String data = ConvertUtil.convertObjectToJson(resJsonData);
+
+        try {
+            PrintWriter out = response.getWriter();
+            out.print(data);
+            out.flush();
+        } catch (IOException e) {
+            log.error(e.getMessage());
+        }
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/security/handler/AjaxLogoutSuccessHandler.java b/src/main/java/kr/wisestone/owl/config/security/handler/AjaxLogoutSuccessHandler.java
new file mode 100644
index 0000000..31585ff
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/security/handler/AjaxLogoutSuccessHandler.java
@@ -0,0 +1,20 @@
+package kr.wisestone.owl.config.security.handler;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Component
+public class AjaxLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler {
+
+    @Override
+    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
+        response.setStatus(HttpServletResponse.SC_OK);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/security/service/UserSecurityService.java b/src/main/java/kr/wisestone/owl/config/security/service/UserSecurityService.java
new file mode 100644
index 0000000..483e6c2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/security/service/UserSecurityService.java
@@ -0,0 +1,36 @@
+package kr.wisestone.owl.config.security.service;
+
+import kr.wisestone.owl.common.MessageAccessor;
+import kr.wisestone.owl.config.security.exception.LoginProcessingException;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.stereotype.Component;
+
+@Component("userSecurityService")
+public class UserSecurityService implements UserDetailsService {
+
+    @Autowired
+    protected MessageAccessor messageAccessor;
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    public UserDetails loadUserByUsername(final String login) {
+        User user = this.userService.findByAccount(login);
+
+        if (user == null) {
+            throw new LoginProcessingException(MsgConstants.USER_NOT_EXIST);
+        }
+
+        if (!User.USER_STATUS_ACTIVE.equals(user.getStatus())) {
+            throw new LoginProcessingException(MsgConstants.USER_NOT_USE_ACTIVE_STATUS);
+        }
+
+        return user;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/security/strategy/SecuritySessionExpiredStrategy.java b/src/main/java/kr/wisestone/owl/config/security/strategy/SecuritySessionExpiredStrategy.java
new file mode 100644
index 0000000..8e78d0e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/security/strategy/SecuritySessionExpiredStrategy.java
@@ -0,0 +1,22 @@
+package kr.wisestone.owl.config.security.strategy;
+
+import org.springframework.security.web.session.SessionInformationExpiredEvent;
+import org.springframework.security.web.session.SessionInformationExpiredStrategy;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class SecuritySessionExpiredStrategy implements SessionInformationExpiredStrategy {
+
+    private String defaultUrl;
+
+    public SecuritySessionExpiredStrategy(String defaultUrl) {
+        this.defaultUrl = defaultUrl;
+    }
+
+    @Override
+    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException {
+        HttpServletResponse response = event.getResponse();
+        response.sendRedirect(this.defaultUrl);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/config/websocket/RedisPublisher.java b/src/main/java/kr/wisestone/owl/config/websocket/RedisPublisher.java
new file mode 100644
index 0000000..7128dc2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/websocket/RedisPublisher.java
@@ -0,0 +1,19 @@
+package kr.wisestone.owl.config.websocket;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.data.redis.listener.ChannelTopic;
+
+import java.util.Map;
+
+@Service
+public class RedisPublisher {
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    public void publish(ChannelTopic channelTopic, Map<String, Object> message) {
+        this.redisTemplate.convertAndSend(channelTopic.getTopic(), message);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/websocket/RedisSubscriber.java b/src/main/java/kr/wisestone/owl/config/websocket/RedisSubscriber.java
new file mode 100644
index 0000000..af04866
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/websocket/RedisSubscriber.java
@@ -0,0 +1,34 @@
+package kr.wisestone.owl.config.websocket;
+
+import com.google.gson.Gson;
+import kr.wisestone.owl.util.CommonUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.connection.MessageListener;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+
+import java.util.Map;
+
+public class RedisSubscriber implements MessageListener {
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    @Autowired
+    private SimpMessagingTemplate simpMessagingTemplate;
+
+    @Override
+    public void onMessage(Message message, byte[] pattern) {
+        try {
+            Gson gson = new Gson();
+            String publishMessage = (String) this.redisTemplate.getStringSerializer().deserialize(message.getBody());
+            Map<String, Object> messageMap = (Map<String, Object>) gson.fromJson(publishMessage, Object.class);
+
+            CommonUtil.sendWebSocketMessage(this.simpMessagingTemplate, messageMap);
+
+        } catch (Exception ex) {
+            System.out.println("ex : " + ex.getMessage());
+        }
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/websocket/WebSocketHandshakeHandler.java b/src/main/java/kr/wisestone/owl/config/websocket/WebSocketHandshakeHandler.java
new file mode 100644
index 0000000..d54043a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/websocket/WebSocketHandshakeHandler.java
@@ -0,0 +1,16 @@
+package kr.wisestone.owl.config.websocket;
+
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
+
+import java.security.Principal;
+import java.util.Map;
+
+public class WebSocketHandshakeHandler extends DefaultHandshakeHandler {
+
+    @Override
+    protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
+        return request.getPrincipal();
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/config/websocket/WebSocketService.java b/src/main/java/kr/wisestone/owl/config/websocket/WebSocketService.java
new file mode 100644
index 0000000..2ffa28e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/websocket/WebSocketService.java
@@ -0,0 +1,64 @@
+package kr.wisestone.owl.config.websocket;
+
+import com.google.gson.Gson;
+import kr.wisestone.owl.util.MapUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.listener.ChannelTopic;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+@Service
+public class WebSocketService {
+
+    @Autowired
+    private StringRedisTemplate stringRedisTemplate;
+
+    @Autowired
+    private RedisPublisher redisPublisher;
+
+    @Value("${redis.common.topic}")
+    private String redisCommonTopic;
+
+    public void checkActiveUser() {
+        Map<String, Object> totalActiveUserMap = this.checkExpireWebSocketSession();
+        Gson gson = new Gson();
+        Map<String, Object> responseMap = new HashMap<>();
+
+        responseMap.put("url", "/session/user-list");
+        responseMap.put("message", gson.toJson(totalActiveUserMap));
+        ChannelTopic channelTopic = new ChannelTopic(this.redisCommonTopic);
+
+        this.redisPublisher.publish(channelTopic, responseMap);
+    }
+
+    private Map<String, Object> checkExpireWebSocketSession() {
+        Map<String, Object> totalActiveUserMap = new HashMap<>();
+        Gson gson = new Gson();
+        String loginUsers = this.stringRedisTemplate.opsForValue().get("activeUsers");
+
+        if (!StringUtils.isEmpty(loginUsers)) {
+            totalActiveUserMap = (Map<String, Object>)gson.fromJson(loginUsers, Object.class);
+
+            Iterator<String> iterator = totalActiveUserMap.keySet().iterator();
+
+            while (iterator.hasNext()) {
+                String key = iterator.next();
+                Map<String, Object> userMap = (Map<String, Object>)totalActiveUserMap.get(key);
+
+                Long sessionActiveTime = Long.valueOf(MapUtil.getString(userMap, "sessionActiveTime"));
+
+                if (System.currentTimeMillis() - sessionActiveTime >= 60000) {
+                    iterator.remove();
+                }
+            }
+        }
+
+        return totalActiveUserMap;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/constant/Constants.java b/src/main/java/kr/wisestone/owl/constant/Constants.java
new file mode 100644
index 0000000..da9550e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/constant/Constants.java
@@ -0,0 +1,16 @@
+package kr.wisestone.owl.constant;
+
+/**
+ * Created by jeong on 2017-08-14.
+ */
+public class Constants {
+    public static final String REQ_KEY_PAGE_VO = "page";
+    public static final String REQ_KEY_CONTENT = "content";
+    public static final String RES_KEY_MESSAGE = "message";
+    public static final String RES_KEY_MSG_FAIL = "fail";
+    public static final String RES_KEY_MSG_SUCCESS = "success";
+    public static final String RES_KEY_CONTENTS = "data";
+    public static final String SESSION_ACCOUNT = "account";
+    public static final String EXCEL = "excel";
+    public static final String SESSION_EXPIRE_REDIRECT_URL = "/#/login";
+}
diff --git a/src/main/java/kr/wisestone/owl/constant/ElasticSearchConstants.java b/src/main/java/kr/wisestone/owl/constant/ElasticSearchConstants.java
new file mode 100644
index 0000000..8125dbc
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/constant/ElasticSearchConstants.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.constant;
+
+public class ElasticSearchConstants {
+    public static final String ISSUE_ADD = "ISSUE_ADD"; //  �씠�뒋 �깮�꽦
+    public static final String ISSUE_MODIFY = "ISSUE_MODIFY";   //  �씠�뒋 �닔�젙
+    public static final String ISSUE_REMOVE = "ISSUE_REMOVE";   //  �씠�뒋 �궘�젣
+    public static final String ISSUE_FIND = "ISSUE_FIND";   //  �씠�뒋 議고쉶
+    public static final String ISSUE_DETAIL = "ISSUE_DETAIL";   //  �씠�뒋 �긽�꽭 議고쉶
+    public static final String ISSUE_STATUS_CHANGE = "ISSUE_STATUS_CHANGE";   //  �씠�뒋 �긽�깭 蹂�寃�
+    public static final String ISSUE_USER_CHANGE = "ISSUE_STATUS_CHANGE";   //  �씠�뒋 �떞�떦�옄 蹂�寃�
+    public static final String ISSUE_ANOTHER_USER_SEND_EMAIL = "ISSUE_ANOTHER_USER_SEND_EMAIL";   //  �떎瑜� �궗�슜�옄�뿉寃� 硫붿씪 蹂대궡湲�
+    public static final String ISSUE_HISTORY_FIND = "ISSUE_HISTORY_FIND";   //  �씠�뒋 �씠�젰 議고쉶
+    public static final String ISSUE_RESERVATION = "ISSUE_RESERVATION"; //  �씠�뒋 �삁�빟 諛쒖깮
+}
diff --git a/src/main/java/kr/wisestone/owl/constant/MailConstants.java b/src/main/java/kr/wisestone/owl/constant/MailConstants.java
new file mode 100644
index 0000000..1a68deb
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/constant/MailConstants.java
@@ -0,0 +1,61 @@
+package kr.wisestone.owl.constant;
+
+/**
+ * Created by wisestone on 2018-03-14.
+ */
+public enum MailConstants {
+    WORKSPACE_JOIN("workspace.join.title", "workspaceJoinEmail"),    //  �쉶�썝 媛��엯
+    USER_SEARCH_PASSWORD("user.search.password.title", "userSearchPasswordEmail"),   //  �궗�슜�옄 鍮꾨�踰덊샇 李얘린
+    USER_WITH_DRAW("user.withDraw.title", "userWithDrawEmail"), //  �쉶�썝 �깉�눜
+    REGULAR_PAYMENT("regular.payment.title", "regularPaymentEmail"),   //  �젙湲� 寃곗젣 �셿猷�
+    REGULAR_PAYMENT_CANCEL("regular.payment.cancel.title", "regularPaymentCancelEmail"), //  �젙湲� 寃곗젣 痍⑥냼
+    REGULAR_PAYMENT_CANCEL_BY_ACCOUNTING_MANAGER("regular.payment.cancel.accounting.manager.title", "regularPaymentCancelByAccountingManagerEmail"), //  �젙湲� 寃곗젣 痍⑥냼
+    REGULAR_PAYMENT_MODIFY("regular.payment.modify.title", "regularPaymentModifyEmail"),    //  �젙湲� 寃곗젣 蹂�寃�
+    WORKSPACE_INVITE_NEW_USER("workspace.inviteNewUser.title", "workspaceInviteNewUserEmail"),   //  �떊洹� �궗�슜�옄 �뾽臾� 怨듦컙 珥덈�
+    WORKSPACE_MAX_USER_EXCESS("workspace.maxUserExcess.title", "workspaceMaxUserExcessEmail"),   //  �뾽臾� 怨듦컙 �솢�꽦 �궗�슜�옄 珥덇낵
+    USER_JOIN_STATISTICS("user.join.statistics.title", "userJoinStatisticsEmail"),   //  �궗�슜�옄 �쁽�솴 �젙蹂�
+    TOTAL_STATISTICS("total.statistics.title", "totalStatisticsEmail"), //  �쟾泥� �떆�뒪�뀥 �쁽�솴 �젙蹂�
+    WORKSPACE_EXPIRE("workspace.expire.title", "workspaceExpireEmail"),  //  �뾽臾� 怨듦컙 �궗�슜 湲곌컙 留뚮즺
+    WORKSPACE_EXPIRE_ALARM("workspace.expire.alarm.title", "workspaceExpireAlarmEmail"),    //  �뾽臾� 怨듦컙 �궗�슜 湲곌컙 留뚮즺 �삁�젙 �븞�궡
+
+
+    WORKSPACE_MAX_STORAGE_EXCESS("workspace.maxStorageExcess.title", "workspaceMaxStorageExcessEmail"),  //  �뾽臾� 怨듦컙 ���옣 �슜�웾 珥덇낵
+    WORKSPACE_INVITE_SYSTEM_USER("workspace.inviteSystemUser.title", "workspaceInviteSystemUserEmail"),  //  湲곗〈 �궗�슜�옄 �뾽臾� 怨듦컙 珥덈�
+    PROJECT_DEFAULT_EXCLUDE("project.default.exclude.title", "projectDefaultExcludeEmail"),  //  �봽濡쒖젥�듃 �씪諛� 李몄뿬�옄 �젣�쇅
+    PROJECT_DEFAULT_INCLUDE("project.default.include.title", "projectDefaultIncludeEmail"),  //  �봽濡쒖젥�듃 �씪諛� 李몄뿬�옄 李몄뿬
+    PROJECT_MANAGER_EXCLUDE("project.manager.exclude.title", "projectManagerExcludeEmail"),  //  �봽濡쒖젥�듃 愿�由ъ옄 �젣�쇅
+    PROJECT_MANAGER_INCLUDE("project.manager.include.title", "projectManagerIncludeEmail"),  //  �봽濡쒖젥�듃 愿�由ъ옄 李몄뿬
+    PROJECT_MANAGER_EXCLUDE_AND_PROJECT_DEFAULT_INCLUDE("project.manager.exclude.default.include.title", "projectManagerExcludeAndDefaultIncludeEmail"), //  �봽濡쒖젥�듃 愿�由ъ옄 �젣�쇅 �썑 �씪諛� �궗�슜�옄濡� 李몄뿬
+    PROJECT_DEFAULT_EXCLUDE_AND_PROJECT_MANAGER_INCLUDE("project.default.exclude.manager.include.title", "projectDefaultExcludeAndManagerIncludeEmail"), //  �씪諛� �궗�슜�옄�뿉�꽌 �젣�쇅 �썑 �봽濡쒖젥�듃 愿�由ъ옄濡� 李몄뿬
+
+    ISSUE_ADD("issue.add.title", "issueAddEmail"),  //  �씠�뒋 �깮�꽦
+    ISSUE_REMOVE("issue.remove.title", "issueRemoveEmail"), //  �씠�뒋 �궘�젣
+    ISSUE_SEND("issue.send.title", "issueSendEmail"); //  �씠�뒋 �씠硫붿씪 �쟾�떖
+    //  ISSUE_MODIFY("issue.modify.title", "issueModifyEmail"); //  �씠�뒋 �닔�젙
+
+
+
+    private String title;
+    private String mailTemplate;
+
+    MailConstants(String title, String mailTemplate) {
+        this.title = title;
+        this.mailTemplate = mailTemplate;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getMailTemplate() {
+        return mailTemplate;
+    }
+
+    public void setMailTemplate(String mailTemplate) {
+        this.mailTemplate = mailTemplate;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/constant/MngPermission.java b/src/main/java/kr/wisestone/owl/constant/MngPermission.java
new file mode 100644
index 0000000..357ccaf
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/constant/MngPermission.java
@@ -0,0 +1,63 @@
+package kr.wisestone.owl.constant;
+
+/**
+ * Created by zenith at 20200803
+ */
+public class MngPermission {
+
+    public static final int USER_PERMISSION_MNG_WORKSPACE = 4096;   //  WORK SPACE 愿�由�    1000000000000
+    public static final int USER_PERMISSION_MNG_PROJECT = 2048;   //  �봽濡쒖젥�듃 愿�由�          0100000000000
+    public static final int USER_PERMISSION_MNG_USER = 1024;        //  USER 愿�由�           0010000000000
+    public static final int USER_PERMISSION_MNG_ISSUE_STATUS = 512;  //  ISSUE SETTING 愿�由� 0001000000000
+    public static final int USER_PERMISSION_MNG_WORKFLOW = 256;     // WORK FLOW 愿�由�       0000100000000
+    public static final int USER_PERMISSION_MNG_CUSTOME_FIELD = 128; //  �궗�슜�옄�젙�쓽 �븘�뱶 愿�由�  0000010000000
+    public static final int USER_PERMISSION_MNG_ISSUE_TYPE = 64;    //  ISSUE TYPE 愿�由�     0000000100000
+    public static final int USER_PERMISSION_MNG_NOTICE = 32;        //  ISSUE TYPE 愿�由�     0000000010000
+    public static final int USER_PERMISSION_MNG_FAQ = 16;            //  FAQ 愿�由�           0000000001000
+    public static final int USER_PERMISSION_MNG_QNA = 8;           //  怨듭��궗�빆 愿�由�          0000000000100
+    public static final int USER_PERMISSION_MNG_EVENT = 4;          //  怨듭��궗�빆 愿�由�         0000000000010
+    public static final int USER_PERMISSION_MNG_GUIDE = 2;          //  �궗�슜�옄 �븣由� 愿�由�      0000000000001
+    public static final int USER_PERMISSION_MNG_NONE = 0;          //
+
+    public static final int USER_PERMISSION_MNG_ISSUE_SETTING = (USER_PERMISSION_MNG_ISSUE_STATUS | USER_PERMISSION_MNG_WORKFLOW |
+                                                                USER_PERMISSION_MNG_CUSTOME_FIELD | USER_PERMISSION_MNG_ISSUE_TYPE);
+
+    public static boolean checkMngPermission(int userPermission, int typePermission)
+    {
+        return ((userPermission & typePermission) != 0);
+    }
+
+    public static int makePermission(boolean userPermission, int typePermission)
+    {
+        if(userPermission)
+            return typePermission;
+        else
+            return USER_PERMISSION_MNG_NONE;
+    }
+
+    public static int makeAllPermission()
+    {
+        int nPermission = (USER_PERMISSION_MNG_WORKSPACE |
+                            USER_PERMISSION_MNG_PROJECT |
+                          USER_PERMISSION_MNG_USER |
+                USER_PERMISSION_MNG_NOTICE |
+                USER_PERMISSION_MNG_FAQ |
+                USER_PERMISSION_MNG_QNA |
+                USER_PERMISSION_MNG_EVENT |
+                USER_PERMISSION_MNG_GUIDE |
+                USER_PERMISSION_MNG_ISSUE_SETTING);
+
+        return nPermission;
+    }
+
+    public static int makeSubAllPermission()
+    {
+        int nPermission = (/*USER_PERMISSION_MNG_WORKSPACE |*/
+                USER_PERMISSION_MNG_USER | USER_PERMISSION_MNG_NOTICE |
+                USER_PERMISSION_MNG_FAQ | USER_PERMISSION_MNG_QNA |
+                USER_PERMISSION_MNG_EVENT | USER_PERMISSION_MNG_GUIDE |
+                        USER_PERMISSION_MNG_ISSUE_SETTING);
+
+        return nPermission;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/constant/MsgConstants.java b/src/main/java/kr/wisestone/owl/constant/MsgConstants.java
new file mode 100644
index 0000000..d01a82c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/constant/MsgConstants.java
@@ -0,0 +1,209 @@
+package kr.wisestone.owl.constant;
+
+/**
+ * Created by jeong on 2017-08-02.
+ */
+public class MsgConstants {
+
+    public static final String PROJECT_NAME_MAX_LENGTH_OUT = "PROJECT_NAME_MAX_LENGTH_OUT";   //  �봽濡쒖젥�듃 紐낆� 理쒕� 50湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+    public static final String DATE_PICKER_NOT_AVAILABLE = "DATE_PICKER_NOT_AVAILABLE"; //  �궇吏� �꽑�깮�씠 �옒紐삳릺�뿀�뒿�땲�떎.
+    public static final String PROJECT_NOT_EXIST = "PROJECT_NOT_EXIST";    //	�봽濡쒖젥�듃媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String PROJECT_NOT_MANAGER = "PROJECT_NOT_MANAGER"; //  �봽濡쒖젥�듃 愿�由ъ옄媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String PROJECT_NOT_MODIFY_PERMISSION = "PROJECT_NOT_MODIFY_PERMISSION"; //  �봽濡쒖젥�듃 愿�由� 沅뚰븳�씠 �뾾�뒿�땲�떎.
+    public static final String PROJECT_NOT_NAME = "PROJECT_NOT_NAME";   //  �봽濡쒖젥�듃 �씠由꾩씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String PROJECT_NOT_STATUS = "PROJECT_NOT_STATUS";   //  �봽濡쒖젥�듃�쓽 �긽�깭媛� �꽑�깮�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String DATE_NOT_EXIST = "DATE_NOT_EXIST";   //  �궇吏쒓� �꽑�깮�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String PROJECT_OVER_LENGTH_PROJECT_KEY = "PROJECT_OVER_LENGTH_PROJECT_KEY"; //  �봽濡쒖젥�듃 �궎�뒗 理쒕� 10湲��옄留� �엯�젰 媛��뒫�빀�땲�떎.
+    public static final String PROJECT_KEY_NOT_EXIST = "PROJECT_KEY_NOT_EXIST"; //  �봽濡쒖젥�듃 �궎媛� �엯�젰 �릺吏� �븡�븯�뒿�땲�떎.
+    public static final String PROJECT_USED_NAME = "PROJECT_USED_NAME";    //	�봽濡쒖젥�듃 �씠由꾩씠 �씠誘� �궗�슜�릺怨� �엳�뒿�땲�떎.
+    public static final String DEFAULT_PROJECT_NOT_REMOVE = "DEFAULT_PROJECT_NOT_REMOVE";   //  湲곕낯�쑝濡� �젣怨듬릺�뒗 �봽濡쒖젥�듃�뒗 �궘�젣�븷 �닔 �뾾�뒿�땲�떎.
+    public static final String PROJECT_REMOVE_NOT_SELECT = "PROJECT_REMOVE_NOT_SELECT"; //  �궘�젣�븷 �봽濡쒖젥�듃媛� �꽑�깮�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String PROJECT_NOT_INCLUDE_USER = "PROJECT_NOT_INCLUDE_USER";   //  �봽濡쒖젥�듃�뿉 李몄뿬�븯怨� �엳吏� �븡�� �궗�슜�옄 �엯�땲�떎.
+    public static final String PROJECT_USED_PROJECT_KEY = "PROJECT_USED_PROJECT_KEY";   //  �봽濡쒖젥�듃 �궎媛� �씠誘� �궗�슜�릺怨� �엳�뒿�땲�떎.
+
+    public static final String PAYMENT_NOT_EXIST = "PAYMENT_NOT_EXIST"; //  寃곗젣 �젙蹂닿� �뾾�뒿�땲�떎.
+    public static final String PAYMENT_EXECUTE_ONLY_WORKSPACE_MANAGER = "PAYMENT_EXECUTE_ONLY_WORKSPACE_MANAGER"; //  寃곗젣�뒗 �뾽臾� 怨듦컙 愿�由ъ옄留� �븷 �닔 �엳�뒿�땲�떎.
+    public static final String PAYMENT_BUY_USER_MUST_BE_GREATER_THAN_ZERO = "PAYMENT_BUY_USER_MUST_BE_GREATER_THAN_ZERO"; //  寃곗젣�븯�젮�뒗 �궗�슜�옄 �닔�뒗 0蹂대떎 而ㅼ빞 �빀�땲�떎.
+    public static final String PAYMENT_NO_TYPE = "PAYMENT_NO_TYPE";   //  寃곗젣 �쑀�삎�씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String PAYMENT_HISTORY_NOT_EXIST = "PAYMENT_HISTORY_NOT_EXIST"; //  寃곗젣 �씠�젰 �젙蹂닿� �뾾�뒿�땲�떎.
+
+    public static final String USER_WORKSPACE_MANAGER_NOT_EXIST = "USER_WORKSPACE_MANAGER_NOT_EXIST"; //  �뾽臾� 怨듦컙 愿�由ъ옄 �젙蹂닿� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String OAUTH_STATE_VALUE_NOT_EQUAL = "OAUTH_STATE_VALUE_NOT_EQUAL";  //  OAuth �씤利앹뿉 �떎�뙣�뻽�뒿�땲�떎. - �긽�깭媛� ���졇�뒗�뜲 援녹씠 �븣�젮以� �븘�슂�뒗 �뾾�떎.
+    public static final String WORKSPACE_NOT_EXIST = "WORKSPACE_NOT_EXIST"; //  �뾽臾� 怨듦컙�씠 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String WORKSPACE_STORAGE_SIZE_EXCESS = "WORKSPACE_STORAGE_SIZE_EXCESS"; //  �뾽臾� 怨듦컙 ���옣 怨듦컙�씠 珥덇낵�릺�뿀�뒿�땲�떎. 遺덊븘�슂�븳 �뙆�씪�쓣 �젙由ы븯�꽭�슂.
+    public static final String WORKSPACE_MANAGER_NOT_CHANGE_USE_YN = "WORKSPACE_MANAGER_NOT_CHANGE_USE_YN"; //  �뾽臾� 怨듦컙 愿�由ъ옄�뒗 鍮꾪솢�꽦�솕濡� 蹂�寃� �맆 �닔 �뾾�뒿�땲�떎.
+    public static final String WORKSPACE_PERIOD_REMAIN = "WORKSPACE_PERIOD_REMAIN"; //  �뾽臾� 怨듦컙 �궗�슜 湲곌컙�씠 �궓�븘 �엳�뒿�땲�떎.
+    public static final String WORKSPACE_USE_PERIOD_EXCESS = "WORKSPACE_USE_PERIOD_EXCESS"; //  �뾽臾� 怨듦컙 �궗�슜 湲곌컙�씠 醫낅즺�릺�뼱 �깮�꽦, �닔�젙, �궘�젣 湲곕뒫�쓣 �궗�슜�븷 �닔 �뾾�뒿�땲�떎.
+    public static final String WORKSPACE_INCLUDE_DISABLED = "WORKSPACE_INCLUDE_DISABLED"; //  �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 鍮꾪솢�꽦 �긽�깭�씠誘�濡� �깮�꽦, �닔�젙, �궘�젣 湲곕뒫�쓣 �궗�슜�븷 �닔 �뾾�뒿�땲�떎.
+
+    public static final String ATTACHED_FILE_NOT_EXIST = "ATTACHED_FILE_NOT_EXIST"; //  泥⑤� �뙆�씪 �젙蹂대�� 李얠쓣 �닔 �뾾�뒿�땲�떎.
+
+    public static final String START_ISSUE_STATUS_NOT_EXIST = "START_ISSUE_STATUS_NOT_EXIST";  //  �떆�옉�븯�뒗 �씠�뒋 �긽�깭媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String END_ISSUE_STATUS_NOT_EXIST = "END_ISSUE_STATUS_NOT_EXIST";  //  醫낅즺�븯�뒗 �씠�뒋 �긽�깭媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+
+    public static final String EMAIL_NOT_SEND = "EMAIL_NOT_SEND";   //  硫붿씪�쓣 �쟾�넚�븷 �닔 �뾾�뒿�땲�떎.
+    public static final String INVITE_USER_USED_WORKSPACE = "INVITE_USER_USED_WORKSPACE";   //  �빐�떦 �궗�슜�옄�뒗 �씠誘� �뾽臾� 怨듦컙�뿉 李몄뿬�븯怨� �엳�뒿�땲�떎.
+
+    public static final String PROJECT_ROLE_NOT_EXIST = "PROJECT_ROLE_NOT_EXIST";   //  �봽濡쒖젥�듃 �뿭�븷�씠 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String WORKSPACE_NOT_NAME = "WORKSPACE_NOT_NAME";   //  �뾽臾� 怨듦컙 紐낆씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String WORKSPACE_MAX_USER_EXCESS = "WORKSPACE_MAX_USER_EXCESS";   //  �뾽臾� 怨듦컙�쓽 理쒕� �궗�슜�옄 �닔媛� 珥덇낵�릺�뿀�뒿�땲�떎. 理쒕� �궗�슜�옄 �닔媛� 珥덇낵�릺硫� 珥덈�諛쏆� �궗�슜�옄媛� �빐�떦 �뾽臾� 怨듦컙瑜� �젒洹쇳븷 �닔 �뾾�뒿�땲�떎.
+    public static final String WORKSPACE_MAX_USER_EXCESS_NOT_INCLUDE = "WORKSPACE_MAX_USER_EXCESS_NOT_INCLUDE"; // �뾽臾� 怨듦컙�쓽 理쒕� �궗�슜�옄 �닔媛� 珥덇낵�릺�뼱 李몄뿬 �긽�깭濡� 蹂�寃쏀븷 �닔 �뾾�뒿�땲�떎.
+    public static final String WORKSPACE_OUT = "WORKSPACE_OUT"; //  李몄뿬�븯�뒗 �뾽臾� 怨듦컙�뿉�꽌 �젣�쇅�릺�뿀�뒿�땲�떎. (1)
+    public static final String WORKSPACE_NAME_MAX_LENGTH_OUT = "WORKSPACE_NAME_MAX_LENGTH_OUT";   //  �뾽臾� 怨듦컙 紐낆� 理쒕� 50湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+
+    public static final String DEFAULT_PROJECT_MANAGER_NOT_CHANGE = "DEFAULT_PROJECT_MANAGER_NOT_CHANGE";   //  湲곕낯 �젣怨듬릺�뒗 �봽濡쒖젥�듃�쓽 愿�由ъ옄�뒗 �뾽臾� 怨듦컙 愿�由ъ옄媛� �엳�뼱�빞 �빀�땲�떎.
+
+
+    public static final String ISSUE_STATUS_NAME_MAX_LENGTH_OUT = "ISSUE_STATUS_NAME_MAX_LENGTH_OUT";   //  �씠�뒋 �긽�깭紐낆� 理쒕� 50湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+    public static final String ISSUE_STATUS_NOT_NAME = "ISSUE_STATUS_NOT_NAME"; //  �씠�뒋 �긽�깭 紐낆씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String ISSUE_STATUS_NOT_COLOR = "ISSUE_STATUS_NOT_COLOR";   //  �씠�뒋 �긽�깭�쓽 �깋�긽�씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String ISSUE_STATUS_USED_NAME = "ISSUE_STATUS_USED_NAME";    //	�씠�뒋 �긽�깭 �씠由꾩씠 �씠誘� �궗�슜�릺怨� �엳�뒿�땲�떎.
+    public static final String ISSUE_STATUS_REMOVE_NOT_SELECT = "ISSUE_STATUS_REMOVE_NOT_SELECT";   //  �궘�젣�븷 �씠�뒋 �긽�깭媛� �꽑�깮�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String DEFAULT_ISSUE_STATUS_NOT_REMOVE = "DEFAULT_ISSUE_STATUS_NOT_REMOVE"; //  湲곕낯�쑝濡� �젣怨듬릺�뒗 �씠�뒋 �긽�깭�뒗 �궘�젣�븷 �닔 �뾾�뒿�땲�떎.
+    public static final String ISSUE_STATUS_NOT_EXIST = "ISSUE_STATUS_NOT_EXIST";  //  �씠�뒋 �긽�깭媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String READY_ISSUE_STATUS_NOT_EXIST = "READY_ISSUE_STATUS_NOT_EXIST";   //  �씠�뒋 �긽�깭 �쑀�삎�씠 ��湲곗씤 �씠�뒋 �긽�깭瑜� 李얠쓣 �닔 �뾾�뒿�땲�떎.
+    public static final String ISSUE_STATUS_CHANGE_NOT_TARGET = "ISSUE_STATUS_CHANGE_NOT_TARGET";   //  �꽑�깮�븳 �씠�뒋 �긽�깭濡� 蹂�寃쏀븷 �닔 �뾾�뒿�땲�떎. �썙�겕�뵆濡쒖슦瑜� �솗�씤�븯�꽭�슂.
+    public static final String ISSUE_STATUS_USE_ISSUES = "ISSUE_STATUS_USE_ISSUES"; //  �씠�뒋 �긽�깭瑜� �궗�슜�븯怨� �엳�뒗 �씠�뒋媛� 議댁옱�븯怨� �엳�뒿�땲�떎.
+    public static final String ISSUE_STATUS_USE_WORKFLOW = "ISSUE_STATUS_USE_WORKFLOW"; //  �씠�뒋 �긽�깭瑜� �궗�슜�븯怨� �엳�뒗 �썙�겕�뵆濡쒖슦媛� 議댁옱�븯怨� �엳�뒿�땲�떎.
+
+    public static final String WORKFLOW_NOT_EXIST = "WORKFLOW_NOT_EXIST";   //  �썙�겕�뵆濡쒖슦媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String WORKFLOW_NOT_NAME = "WORKFLOW_NOT_NAME"; //  �썙�겕�뵆濡쒖슦 紐낆씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String WORKFLOW_NAME_MAX_LENGTH_OUT = "WORKFLOW_NAME_MAX_LENGTH_OUT";   //  �썙�겕�뵆濡쒖슦 紐낆� 理쒕� 20湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+    public static final String WORKFLOW_USED_NAME = "WORKFLOW_USED_NAME";   //  �썙�겕�뵆濡쒖슦 紐낆씠 �씠誘� �궗�슜�릺怨� �엳�뒿�땲�떎.
+    public static final String WORKFLOW_TRANSITION_NOT_EXIST = "WORKFLOW_TRANSITION_NOT_EXIST"; //  �썙�겕�뵆濡쒖슦 �쟾�씠媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String WORKFLOW_USED_ISSUE_TYPE = "WORKFLOW_USED_ISSUE_TYPE";   //  �썙�겕�뵆濡쒖슦媛� �씠�뒋 ���엯�뿉�꽌 �궗�슜�릺怨� �엳�뼱�꽌 �궘�젣�븷 �닔 �뾾�뒿�땲�떎.
+    public static final String WORKFLOW_ISOLATION = "WORKFLOW_ISOLATION"; //  �썙�겕�뵆濡쒖슦�뿉 怨좊┰�맂 �씠�뒋 �긽�깭媛� 議댁옱�빀�땲�떎.
+    public static final String WORKFLOW_REMOVE_NOT_SELECT = "WORKFLOW_REMOVE_NOT_SELECT"; //  �궘�젣�븷 �썙�겕�뵆濡쒖슦媛� �꽑�깮�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String WORKFLOW_REQUIRE_ISSUE_STATUS_TYPE_TO_READY = "WORKFLOW_REQUIRE_ISSUE_STATUS_TYPE_TO_READY"; //  �썙�겕�뵆濡쒖슦�뿉�뒗 �긽�깭 �냽�꽦 '��湲�' �씤 �씠�뒋 �긽�깭媛� 1媛� �씠�긽 議댁옱�빐�빞 �빀�땲�떎.
+    public static final String WORKFLOW_REQUIRE_ISSUE_STATUS_TYPE_TO_OPEN = "WORKFLOW_REQUIRE_ISSUE_STATUS_TYPE_TO_OPEN"; //  �썙�겕�뵆濡쒖슦�뿉�뒗 �긽�깭 �냽�꽦 '吏꾪뻾' �씤 �씠�뒋 �긽�깭媛� 1媛� �씠�긽 議댁옱�빐�빞 �빀�땲�떎.
+    public static final String WORKFLOW_REQUIRE_ISSUE_STATUS_TYPE_TO_CLOSE = "WORKFLOW_REQUIRE_ISSUE_STATUS_TYPE_TO_CLOSE"; //  �썙�겕�뵆濡쒖슦�뿉�뒗 �긽�깭 �냽�꽦 '醫낅즺' �씤 �씠�뒋 �긽�깭媛� 1媛� �씠�긽 議댁옱�빐�빞 �빀�땲�떎.
+
+    public static final String CUSTOM_FIELD_NOT_EXIST = "CUSTOM_FIELD_NOT_EXIST";   //  �궗�슜�옄 �젙�쓽 �븘�뱶媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String CUSTOM_FIELD_NOT_NAME = "CUSTOM_FIELD_NOT_NAME"; //  �궗�슜�옄 �젙�쓽 �븘�뱶紐낆씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String CUSTOM_FIELD_NAME_MAX_LENGTH_OUT = "CUSTOM_FIELD_NAME_MAX_LENGTH_OUT";   //  �궗�슜�옄 �젙�쓽 �븘�뱶紐낆� 理쒕� 50湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+    public static final String CUSTOM_FIELD_USED_NAME = "CUSTOM_FIELD_USED_NAME";   //  �궗�슜�옄 �젙�쓽 �븘�뱶 紐낆씠 �씠誘� �궗�슜�릺怨� �엳�뒿�땲�떎.
+    public static final String CUSTOM_FIELD_REMOVE_NOT_SELECT = "CUSTOM_FIELD_REMOVE_NOT_SELECT";   //  �궘�젣�븷 �궗�슜�옄 �젙�쓽 �븘�뱶媛� �꽑�깮�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String CUSTOM_FIELD_OPTIONS_NOT_USE_INPUT_FIELD = "CUSTOM_FIELD_OPTIONS_NOT_USE_INPUT_FIELD";   //  臾몄옄�뿴 �븘�뱶�뒗 �샃�뀡 媛믪쓣 �궗�슜�븷 �닔 �뾾�뒿�땲�떎.
+    public static final String CUSTOM_FIELD_OPTIONS_NOT_EXIST_DEFAULT_VALUE = "CUSTOM_FIELD_OPTIONS_NOT_EXIST_DEFAULT_VALUE";   //  �궗�슜�옄 �젙�쓽 �븘�뱶 湲곕낯 媛믪씠 �샃�뀡�뿉 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String CUSTOM_FIELD_OPTIONS_USED_EXIST_DEFAULT_VALUE = "CUSTOM_FIELD_OPTIONS_USED_EXIST_DEFAULT_VALUE";   //  �궗�슜�옄 �젙�쓽 �븘�뱶 湲곕낯 媛믪뿉 以묐났�맂 媛믪씠 議댁옱�빀�땲�떎.
+    public static final String CUSTOM_FIELD_OPTIONS_NOT_USE_MULTI_DEFAULT_VALUE = "CUSTOM_FIELD_OPTIONS_NOT_USE_MULTI_DEFAULT_VALUE";   //  �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪뿉 ���븳 湲곕낯 媛믪쑝濡� 2媛� �씠�긽 吏��젙�븷 �닔 �뾾�뒿�땲�떎.
+    public static final String CUSTOM_FIELD_OPTIONS_NOT_EMPTY_VALUE = "CUSTOM_FIELD_OPTIONS_NOT_EMPTY_VALUE";   //  �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪뿉 怨듬갚�쓣 �벑濡앺븷 �닔 �뾾�뒿�땲�떎.
+    public static final String CUSTOM_FIELD_OPTIONS_NOT_VALUE = "CUSTOM_FIELD_OPTIONS_NOT_VALUE";   //  �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String CUSTOM_FIELD_TEXT_TYPE_MAX_LENGTH_OUT = "CUSTOM_FIELD_TEXT_TYPE_MAX_LENGTH_OUT"; //  �궗�슜�옄 �젙�쓽 臾몄옄�뿴 �븘�뱶�뒗 理쒕� 100湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+    public static final String CUSTOM_FIELD_DEFAULT_VALUE_MAX_LENGTH_OUT = "CUSTOM_FIELD_DEFAULT_VALUE_MAX_LENGTH_OUT"; //  �궗�슜�옄 �젙�쓽 �븘�뱶�쓽 湲곕낯 媛� �븘�뱶�뒗 理쒕� 100湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+    public static final String CUSTOM_FIELD_OPTION_VALUE_MAX_LENGTH_OUT = "CUSTOM_FIELD_OPTION_VALUE_MAX_LENGTH_OUT";   //  �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪� 理쒕� 15湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+
+    public static final String ISSUE_TYPE_NOT_EXIST = "ISSUE_TYPE_NOT_EXIST";   //  �씠�뒋 �쑀�삎�씠 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String ISSUE_TYPE_NOT_NAME = "ISSUE_TYPE_NOT_NAME"; //  �씠�뒋 �쑀�삎 紐낆씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String ISSUE_TYPE_NAME_MAX_LENGTH_OUT = "ISSUE_TYPE_NAME_MAX_LENGTH_OUT";   //  �씠�뒋 �쑀�삎�� 理쒕� 50湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+    public static final String ISSUE_TYPE_USED_NAME = "ISSUE_TYPE_USED_NAME";   //  �씠�뒋 �쑀�삎 紐낆씠 �씠誘� �궗�슜�릺怨� �엳�뒿�땲�떎.
+    public static final String ISSUE_TYPE_REMOVE_NOT_SELECT = "ISSUE_TYPE_REMOVE_NOT_SELECT";   //  �궘�젣�븷 �씠�뒋 �쑀�삎�씠 �꽑�깮�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String ISSUE_TYPE_NOT_COLOR = "ISSUE_TYPE_NOT_COLOR";   //  �씠�뒋 �쑀�삎�뿉 �깋�긽�씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String ISSUE_TYPE_USE_ISSUES = "ISSUE_TYPE_USE_ISSUES"; //  �씠�뒋 �쑀�삎�쓣 �궗�슜�븯怨� �엳�뒗 �씠�뒋媛� 議댁옱�븯怨� �엳�뒿�땲�떎.
+
+    public static final String PRIORITY_NOT_EXIST = "PRIORITY_NOT_EXIST";   //  �슦�꽑�닚�쐞媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String SEVERITY_NOT_EXIST = "SEVERITY_NOT_EXIST";   //  以묒슂�룄媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+
+    public static final String ISSUE_NOT_EXIST = "ISSUE_NOT_EXIST"; //  �씠�뒋媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String ISSUE_NUMBER_GENERATOR_NOT_EXIST = "ISSUE_NUMBER_GENERATOR_NOT_EXIST";   //  �씠�뒋 踰덊샇 �젣�꼫�젅�씠�꽣媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String ISSUE_NUMBER_NOT_EXIST = "ISSUE_NUMBER_NOT_EXIST";   //  �씠�뒋 踰덊샇媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String ISSUE_TITLE_MAX_LENGTH_OUT = "ISSUE_TITLE_MAX_LENGTH_OUT";   //  �씠�뒋 �젣紐⑹� 理쒕� 300湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+    public static final String ISSUE_NOT_MODIFY_PERMISSION = "ISSUE_NOT_MODIFY_PERMISSION"; //  �씠�뒋 �닔�젙 沅뚰븳�씠 �뾾�뒿�땲�떎.
+    public static final String ISSUE_REMOVE_NOT_SELECT = "ISSUE_REMOVE_NOT_SELECT"; //  �궘�젣�븷 �씠�뒋媛� �꽑�깮�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String ISSUE_NO_TITLE = "ISSUE_NO_TITLE";   //  �씠�뒋 �젣紐⑹씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String ISSUE_NOT_SEND_USER = "ISSUE_NOT_SEND_USER";   //  �씠�뒋 諛쒖넚 ���긽�옄瑜� �꽑�깮�븯吏� �븡�븯�뒿�땲�떎.
+
+    public static final String ISSUE_COMMENT_REMOVE_NOT_SELECT = "ISSUE_COMMENT_REMOVE_NOT_SELECT"; //  �궘�젣�븷 �뙎湲��쓣 �꽑�깮�븯吏� �븡�븯�뒿�땲�떎.
+    public static final String ISSUE_COMMENT_NOT_EXIST = "ISSUE_COMMENT_NOT_EXIST"; //  �뙎湲��씠 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String ISSUE_COMMENT_NOT_REMOVE_PERMISSION = "ISSUE_COMMENT_NOT_REMOVE_PERMISSION"; //  �뙎湲��쓣 �궘�젣�븷 �닔 �엳�뒗 沅뚰븳�씠 �뾾�뒿�땲�떎.
+    public static final String ISSUE_COMMENT_NOT_COMMENT = "ISSUE_COMMENT_NOT_COMMENT"; //  �뙎湲��씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String ISSUE_COMMENT_MAX_LENGTH_OUT = "ISSUE_COMMENT_MAX_LENGTH_OUT"; //  �뙎湲��� 理쒕� 300湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+
+    public static final String ISSUE_RESERVATION_NOT_EXIST = "ISSUE_RESERVATION_NOT_EXIST"; //  �씠�뒋 諛쒖깮 �삁�빟 �젙蹂닿� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String ISSUE_RESERVATION_VALUE_INVALID = "ISSUE_RESERVATION_VALUE_INVALID"; //  �씠�뒋 諛쒖깮 �삁�빟�씪�씠 �옒紐삳릺�뿀�뒿�땲�떎.
+
+
+    public static final String USER_WORKSPACE_NOT_EXIST = "USER_WORKSPACE_NOT_EXIST";   //  �뾽臾� 怨듦컙 �궗�슜�옄 �뿰寃� �젙蹂닿� 議댁옱�븯吏� �븡�뒿�땲�떎.
+
+    public static final String WIDGET_SEARCH_DATE_NOT_FOUND = "WIDGET_SEARCH_DATE_NOT_FOUND";   //  �쐞�젽 寃��깋 �씪�옄瑜� 李얠쓣 �닔 �뾾�뒿�땲�떎.
+
+    public static final String USER_NO_NAME = "USER_NO_NAME";   //  �씠由꾩씠 �엯�젰 �릺吏� �븡�븯�뒿�땲�떎.
+    public static final String USER_NAME_LENGTH_EXCESS = "USER_NAME_LENGTH_EXCESS"; //  �궗�슜�옄 �씠由꾩� 理쒕� 50湲��옄留� 媛��뒫�빀�땲�떎.
+    public static final String USER_NO_EMAIL = "USER_NO_EMAIL"; //  �씠硫붿씪 二쇱냼媛� �엯�젰 �릺吏� �븡�븯�뒿�땲�떎.
+    public static final String USER_INVALID_EMAIL = "INVALID_EMAIL_ADDRESS";    //  �옒紐삳맂 �씠硫붿씪 二쇱냼 �엯�땲�떎.
+    public static final String USER_USED_EMAIL = "USER_USED_EMAIL"; //  �씠誘� 媛��엯�맂 �씠硫붿씪 二쇱냼 �엯�땲�떎.
+    public static final String USER_NO_PASSWORD = "USER_NO_PASSWORD";   //  鍮꾨�踰덊샇媛� �엯�젰 �릺吏� �븡�븯�뒿�땲�떎.
+    public static final String USER_NOT_MODIFY_SELF = "USER_NOT_MODIFY_SELF";   //  �떎瑜� �궗�엺�쓽 �젙蹂대�� �닔�젙�븷 �닔 �뾾�뒿�땲�떎.
+    public static final String USER_PHONE_MAX_LENGTH_OUT = "USER_PHONE_MAX_LENGTH_OUT"; //  �뿰�씫泥섎뒗 理쒕� 20湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+    public static final String USER_PHONE_ONLY_NUMBER = "USER_PHONE_ONLY_NUMBER";   //  �뿰�씫泥섎뒗 �닽�옄留� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+    public static final String USER_PASSWORD_MAX_LENGTH_OUT = "USER_PASSWORD_MAX_LENGTH_OUT"; //  鍮꾨�踰덊샇�뒗 理쒕� 20湲��옄源뚯� �엯�젰�븷 �닔 �엳�뒿�땲�떎.
+    public static final String USER_INVALID_CURRENT_PASSWORD = "USER_INVALID_CURRENT_PASSWORD"; //  �엯�젰�븳 �쁽�옱 鍮꾨�踰덊샇媛� �옒紐삳릺�뿀�뒿�땲�떎.
+    public static final String USER_INVALID_LICENSEKEY = "USER_INVALID_LICENSEKEY"; //  �엯�젰�븳 �쁽�옱 �씪�씠�꽱�뒪媛� �옒紐삳릺�뿀�뒿�땲�떎.
+    public static final String USER_PASSWORD_SAME_NEW_PASSWORD = "USER_PASSWORD_SAME_NEW_PASSWORD"; //  �쁽�옱 �궗�슜�븯�뒗 鍮꾨�踰덊샇�� 蹂�寃쏀븯�젮�뒗 鍮꾨�踰덊샇�뒗 �떖�씪�빞 �빀�땲�떎.
+    public static final String USER_PASSWORD_NOT_SAME_CONFIRM_PASSWORD = "USER_PASSWORD_NOT_SAME_CONFIRM_PASSWORD"; //  �떊洹� 鍮꾨�踰덊샇�� �떊洹� 鍮꾨�踰덊샇 �솗�씤 �븘�뱶�뿉 �엯�젰�븳 鍮꾨�踰덊샇媛� �떎由낅땲�떎.
+    public static final String USER_WITH_DRAW_EXIST = "USER_WITH_DRAW_EXIST";   //  �빐�떦 怨꾩젙�� �쉶�썝 �깉�눜 湲곕줉�씠 �엳�뒿�땲�떎.
+    public static final String USER_PROFILE_SIZE_NOT_ALLOW = "USER_PROFILE_SIZE_NOT_ALLOW"; //  �궗�슜�옄 �봽濡쒗븘 �궗�씠利덇� �꼫臾� �겱�땲�떎. 10MB �씠�븯 �뙆�씪濡� �뾽濡쒕뱶�빐二쇱꽭�슂.
+    public static final String USER_PROFILE_UPLOAD_FILE_TYPE_NOT_ALLOW = "USER_PROFILE_UPLOAD_FILE_TYPE_NOT_ALLOW"; //  �봽濡쒗븘 �뙆�씪�� jpg, png 留� 媛��뒫�빀�땲�떎.
+    public static final String USER_NOT_EQUAL_PASSWORD = "USER_NOT_EQUAL_PASSWORD"; //  鍮꾨�踰덊샇媛� 留욎� �븡�뒿�땲�떎.
+    public static final String USER_NOT_EXIST = "USER_NOT_EXIST";   //  �궗�슜�옄媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String USER_NOT_AUTHORIZED = "USER_NOT_AUTHORIZED"; //  �궗�슜�옄 �씤利� 沅뚰븳�씠 �뾾�뒿�땲�떎.
+    public static final String USER_EXPIRED_PASSWORD = "USER_EXPIRED_PASSWORD"; //  鍮꾨�踰덊샇媛� 留뚮즺�릺�뿀�뒿�땲�떎.
+    public static final String USER_RETURN_PASSWORD_NOT_PROVIDER_SOCIAL_JOIN_USER = "USER_RETURN_PASSWORD_NOT_PROVIDER_SOCIAL_JOIN_USER";   //  鍮꾨�踰덊샇 李얘린 湲곕뒫�쓣 �냼�뀥 怨꾩젙 媛��엯 �궗�슜�옄�뒗 �궗�슜�븷 �닔 �뾾�뒿�땲�떎.
+    public static final String USER_NOT_USE_ACTIVE_STATUS = "USER_NOT_USE_ACTIVE_STATUS";   //  �궗�슜�옄�뒗 �솢�꽦 �긽�깭媛� �븘�땲硫� 濡쒓렇�씤�븷 �닔 �뾾�뒿�땲�떎.
+
+    public static final String EXCEL_NOT_EXTENSION = "EXCEL_NOT_EXTENSION"; //  �뿊�� �뙆�씪 �솗�옣�옄 (xlsx)留� �뾽濡쒕뱶媛� 媛��뒫�빀�땲�떎.
+    public static final String EXCEL_DOWNLOAD_MAX_ROWS_OVER = "EXCEL_DOWNLOAD_MAX_ROWS_OVER";   //  寃��깋�맂 �뿊�� �뻾�씠 1,000嫄댁쓣 珥덇낵�븯�뿬 �떎�슫濡쒕뱶 �븷 �닔 �뾾�뒿�땲�떎. 寃��깋 議곌굔�쓣 �궗�슜�븯�뿬 1,000 嫄� �씠�븯濡� �떎�슫濡쒕뱶瑜� 吏꾪뻾�빐�빞 �빀�땲�떎.
+    public static final String EXCEL_IMPORT_MAX_ROWS_OVER = "EXCEL_IMPORT_MAX_ROWS_OVER";   //  �뿊�� �뾽濡쒕뱶濡� �씠�뒋 �벑濡앹� 理쒕� 1,000 嫄닿퉴吏�留� 媛��뒫�빀�땲�떎.
+    public static final String EXCEL_CONDITIONS_NOT_EXIST = "EXCEL_CONDITIONS_NOT_EXIST";   //  �뿊�� �떎�슫濡쒕뱶�뿉 �븘�슂�븳 寃��깋 議곌굔�쓣 李얠쓣 �닔 �뾾�뒿�땲�떎.
+    public static final String EXCEL_EMPTY_CELL = "EXCEL_EMPTY_CELL"; //  �뿊�� �뿤�뜑 遺�遺� ���쓣 李얠쓣 �닔 �뾾�뒿�땲�떎. �뿊�� �옉�꽦 �뼇�떇�뿉 臾몄젣媛� �엳�뒿�땲�떎.
+    public static final String EXCEL_HEADER_EMPTY_CELL = "EXCEL_HEADER_EMPTY_CELL"; //  �뿊�� �뿤�뜑�뿉 鍮� ���씠 �엳�뒿�땲�떎.
+    public static final String EXCEL_IMPORT_ISSUE_TITLE_IS_NULL = "EXCEL_IMPORT_ISSUE_TITLE_IS_NULL";   //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 �씠�뒋 �젣紐⑹씠 �엯�젰吏� �븡�븯�뒿�땲�떎.
+    public static final String EXCEL_IMPORT_PROJECT_KEY_IS_NULL = "EXCEL_IMPORT_PROJECT_KEY_IS_NULL";   //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 �봽濡쒖젥�듃 �궎媛� �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String EXCEL_IMPORT_PROJECT_NOT_EXIST = "EXCEL_IMPORT_PROJECT_NOT_EXIST";   //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 �엯�젰�맂 �봽濡쒖젥�듃 �궎濡� 寃��깋�릺�뒗 �봽濡쒖젥�듃媛� �뾾�뒿�땲�떎.
+    public static final String EXCEL_IMPORT_ISSUE_TYPE_IS_NULL = "EXCEL_IMPORT_ISSUE_TYPE_IS_NULL";   //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 �씠�뒋 ���엯 紐낆씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String EXCEL_IMPORT_ISSUE_TYPE_NOT_EXIST = "EXCEL_IMPORT_ISSUE_TYPE_NOT_EXIST";   //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 �엯�젰�맂 �씠�뒋 ���엯 紐낆쑝濡� 寃��깋�릺�뒗 �씠�뒋 ���엯�씠 �뾾�뒿�땲�떎.
+    public static final String EXCEL_IMPORT_ISSUE_STATUS_READY_NOT_EXIST = "EXCEL_IMPORT_ISSUE_STATUS_READY_NOT_EXIST";   //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 �엯�젰�맂 �씠�뒋 ���엯�쓽 �썙�겕�뵆濡쒖슦�뿉�꽌 �긽�깭 �냽�꽦 '��湲�'�씤 �긽�깭媛� �뾾�뒿�땲�떎.
+    public static final String EXCEL_IMPORT_PRIORITY_IS_NULL = "EXCEL_IMPORT_PRIORITY_IS_NULL";   //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 �슦�꽑�닚�쐞 紐낆씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String EXCEL_IMPORT_PRIORITY_NOT_EXIST = "EXCEL_IMPORT_PRIORITY_NOT_EXIST";   //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 �엯�젰�맂 �슦�꽑�닚�쐞 紐낆쑝濡� 寃��깋�릺�뒗 �슦�꽑�닚�쐞媛� �뾾�뒿�땲�떎.
+    public static final String EXCEL_IMPORT_SEVERITY_IS_NULL = "EXCEL_IMPORT_SEVERITY_IS_NULL";   //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 以묒슂�룄 紐낆씠 �엯�젰�릺吏� �븡�븯�뒿�땲�떎.
+    public static final String EXCEL_IMPORT_SEVERITY_NOT_EXIST = "EXCEL_IMPORT_SEVERITY_NOT_EXIST";   //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 �엯�젰�맂 以묒슂�룄 紐낆쑝濡� 寃��깋�릺�뒗 �슦�꽑�닚�쐞媛� �뾾�뒿�땲�떎.
+    public static final String EXCEL_IMPORT_PERIOD_NOT_VALIDITY = "EXCEL_IMPORT_PERIOD_NOT_VALIDITY"; //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 �엯�젰�븳 �떆�옉�씪, 醫낅즺�씪�뿉 臾몄젣媛� �엳�뒿�땲�떎. �떆�옉�씪�� 醫낅즺�씪蹂대떎 鍮⑤씪�빞 �빀�땲�떎.
+    public static final String EXCEL_IMPORT_PERIOD_NOT_VALIDITY_EMPTY = "EXCEL_IMPORT_PERIOD_NOT_VALIDITY_EMPTY"; //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 �엯�젰�븳 �떆�옉�씪, 醫낅즺�씪�뿉 臾몄젣媛� �엳�뒿�땲�떎. 怨듬갚�씠 �룷�븿�릺�뼱 �엳�뒗吏� �솗�씤 �썑 怨듬갚�쓣 �젣嫄고븯�꽭�슂.
+    public static final String EXCEL_CUSTOM_FIELD_VALUE_NOT_VALIDITY = "EXCEL_CUSTOM_FIELD_VALUE_NOT_VALIDITY"; //  �떎�쓬 �뿊�� �씪�씤�뿉�꽌 �엯�젰�븳 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪씠 �쑀�슚�븯吏� �븡�뒿�땲�떎.
+    public static final String EXCEL_IMPORT_HEADER_CUSTOM_FIELD_NOT_EXIST = "EXCEL_IMPORT_HEADER_CUSTOM_FIELD_NOT_EXIST";   //  �벑濡앺븯�젮�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶瑜� 李얠쓣 �닔 �뾾�뒿�땲�떎. �빐�떦 �궗�슜�옄 �젙�쓽�븘�뱶�쓽 �씠由꾩씠 蹂�寃쎈릺�뿀嫄곕굹 �궘�젣�릺�뿀�뒿�땲�떎.
+
+    public static final String NOTICE_NOT_EXIST = "NOTICE_NOT_EXIST";   //  怨듭��궗�빆�씠 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String NOTICE_EMPTY_CONTENT = "NOTICE_EMPTY_CONTENT";   //  怨듭��궗�빆 �젣紐� 諛� �궡�슜 以� �엯�젰 媛믪씠 �뾾�뒗 �븘�뱶媛� �엳�뒿�땲�떎.
+
+    public static final String FAQ_NOT_EXIST = "FAQ_NOT_EXIST";   //  FAQ媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String FAQ_EMPTY_CONTENT = "FAQ_EMPTY_CONTENT";   //  FAQ �젣紐� 諛� �궡�슜 以� �엯�젰 媛믪씠 �뾾�뒗 �븘�뱶媛� �엳�뒿�땲�떎.
+
+    public static final String GUIDE_NOT_EXIST="GUIDE_NOT_EXIST";
+    public static final String GUIDE_EMPTY_CONTENT="GUIDE_EMPTY_CONTENT";
+
+    public static final String EVENT_NOT_EXIST="EVENT_NOT_EXIST";
+    public static final String EVENT_EMPTY_CONTENT="EVENT_EMPTY_CONTENT";
+
+    public static final String QNA_NOT_EXIST = "QNA_NOT_EXIST";   //  QNA 議댁옱�븯吏� �븡�뒿�땲�떎.
+    public static final String QNA_EMPTY_CONTENT = "QNA_EMPTY_CONTENT";   //  QNA �젣紐� 諛� �궡�슜 以� �엯�젰 媛믪씠 �뾾�뒗 �븘�뱶媛� �엳�뒿�땲�떎.
+
+    public static final String RESERVATION_EMAIL_TITLE = "RESERVATION_EMAIL_TITLE"; //  [OWL ITS] �뼱�젣 �씠�뒋 諛� �솢�룞 �쁽�솴�엯�땲�떎.
+    public static final String FILE_TYPE_NOT_ALLOW = "FILE_TYPE_NOT_ALLOW"; //  �뿀�슜�릺吏� �븡�� �뙆�씪 �쑀�삎�엯�땲�떎.
+
+
+    public static final String ERR_FAILED_CONVERT_OBJECT = "ERR_FAILED_CONVERT_OBJECT"; //  �뜲�씠�꽣 蹂��솚�쓣 �떎�뙣�뻽�뒿�땲�떎.
+    public static final String TARGET_OBJECT_IS_NULL = "TARGET_OBJECT_IS_NULL"; //  ��寃� 媛앹껜媛� Null �엯�땲�떎.
+    public static final String SOURCE_OBJECT_IS_NULL = "SOURCE_OBJECT_IS_NULL"; //  �냼�뒪 媛앹껜媛� Null �엯�땲�떎.
+    public static final String ERR_FAILED_CONVERT_JSON = "ERR_FAILED_CONVERT_JSON"; //  �슂泥� �뜲�씠�꽣�쓽 JSON 蹂��솚�쓣 �떎�뙣�뻽�뒿�땲�떎.
+
+    public static final String NOT_READABLE_JSON_DATA = "NOT_READABLE_JSON_DATA";   //  JSON �뜲�씠�꽣瑜� �씫�쓣 �닔 �뾾�뒿�땲�떎.
+    public static final String SUCCESS_REQUEST = "SUCCESS_REQUEST"; //  �슂泥��씠 �꽦怨듯븯���뒿�땲�떎.
+    public static final String SESSION_EXPIRED = "SESSION_EXPIRED"; //  �궗�슜�옄 �꽭�뀡�씠 留뚮즺 �릺�뿀�뒿�땲�떎.
+
+    public static final String PAGE_NOT_EXIST_INFO = "PAGE_NOT_EXIST_INFO"; //  �럹�씠吏� �젙蹂대�� 李얠쓣 �닔 �뾾�뒿�땲�떎.
+    public static final String PAGE_NEGATIVE_OR_NULL = "PAGE_NEGATIVE_OR_NULL"; //  �슂泥��븳 �럹�씠吏� �젙蹂닿� �옒紐삳릺�뿀�뒿�땲�떎.
+    public static final String PAGE_SIZE_NEGATIVE_OR_NULL = "PAGE_SIZE_NEGATIVE_OR_NULL";   //  �슂泥��븳 �럹�씠吏� �겕湲곌� �옒紐삳릺�뿀�뒿�땲�떎.
+
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/AttachedFile.java b/src/main/java/kr/wisestone/owl/domain/AttachedFile.java
new file mode 100644
index 0000000..aec0b26
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/AttachedFile.java
@@ -0,0 +1,136 @@
+package kr.wisestone.owl.domain;
+
+import kr.wisestone.owl.domain.enumType.AttachedType;
+import kr.wisestone.owl.domain.enumType.FileType;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+@Entity
+public class AttachedFile extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 847376294732544822L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private String path;
+    private Long size;
+    private String contentType;
+    private String awsKey;
+    @Enumerated(EnumType.STRING)
+    private FileType fileType;
+
+    @Enumerated(EnumType.STRING)
+    private AttachedType attachedType;
+
+    @ManyToOne(fetch= FetchType.EAGER)
+    @JoinColumn(name="issue_id")
+    private Issue issue;
+    //  �뙆�씪 �떎�슫濡쒕뱶 �삤瑜� �빐寃�
+    @ManyToOne(fetch= FetchType.EAGER)
+    @JoinColumn(name="workspace_id")
+    private Workspace workspace;
+
+    public AttachedFile() {
+    }
+
+    public AttachedFile(String name, Long size, String contentType, String path) {
+        this.name = name;
+        this.path = path;
+        this.size = size;
+        this.contentType = contentType;
+    }
+
+    public AttachedFile(String name, Long size, String contentType, String path, String awsKey, Issue issue, Workspace workspace, FileType fileType, AttachedType attachedType) {
+        this.name = name;
+        this.size = size;
+        this.contentType = contentType;
+        this.path = path;
+        this.awsKey = awsKey;
+        this.issue = issue;
+        this.workspace = workspace;
+        this.fileType = fileType;
+        this.attachedType = attachedType;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public Long getSize() {
+        return size;
+    }
+
+    public void setSize(Long size) {
+        this.size = size;
+    }
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public void setContentType(String contentType) {
+        this.contentType = contentType;
+    }
+
+    public Issue getIssue() {
+        return issue;
+    }
+
+    public void setIssue(Issue issue) {
+        this.issue = issue;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public FileType getFileType() {
+        return fileType;
+    }
+
+    public void setFileType(FileType fileType) {
+        this.fileType = fileType;
+    }
+
+    public String getAwsKey() {
+        return awsKey;
+    }
+
+    public void setAwsKey(String awsKey) {
+        this.awsKey = awsKey;
+    }
+
+    public AttachedType getAttachedType() {
+        return attachedType;
+    }
+
+    public void setAttachedType(AttachedType attachedType) {
+        this.attachedType = attachedType;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/BaseEntity.java b/src/main/java/kr/wisestone/owl/domain/BaseEntity.java
new file mode 100644
index 0000000..1308c45
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/BaseEntity.java
@@ -0,0 +1,76 @@
+/**
+ * �긽湲� �봽濡쒓렇�옩�뿉 ���븳 ���옉沅뚯쓣 �룷�븿�븳 吏��쟻�옱�궛沅뚯� WiseStone�뿉 �엳�쑝硫�,
+ * WiseStone�씠 紐낆떆�쟻�쑝濡� �뿀�슜�븯吏� �븡�� �궗�슜, 蹂듭궗, 蹂�寃�, �젣3�옄�뿉�쓽 怨듦컻,
+ * 諛고룷�뒗 �뾼寃⑺엳 湲덉��릺硫�, WiseStone�쓽 吏��쟻�옱�궛沅� 移⑦빐�뿉 �빐�떦�맗�땲�떎.
+ * (Copyright �뱬 2014 WiseStone Co., Ltd. All Rights Reserved|Confidential)
+ * -----------------------------------------------------------------------------
+ * You are strictly prohibited to copy, disclose, distribute, modify,
+ * or use this program in part or as a whole without the prior written
+ * consent of WiseStone Co., Ltd. WiseStone Co., Ltd., owns the
+ * intellectual property rights in and to this program.
+ * (Copyright �뱬 2014 WiseStone Co., Ltd. All Rights Reserved|Confidential)
+ * -----------------------------------------------------------------------------
+ */
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.util.Date;
+
+@MappedSuperclass
+public class BaseEntity {
+    @Basic
+    @Column(insertable = true, updatable = false)
+    Long registerId;
+    @Column(insertable = true, updatable = false)
+    @Temporal(TemporalType.TIMESTAMP)
+    Date registerDate;
+
+    @Basic
+    @Column(insertable = true, updatable = true)
+    Long modifyId;
+    @Column(insertable = true, updatable = true)
+    @Temporal(TemporalType.TIMESTAMP)
+    Date modifyDate;
+
+    public Long getRegisterId() {
+        return registerId;
+    }
+
+    public void setRegisterId(Long registerId) {
+        this.registerId = registerId;
+    }
+
+    public Date getRegisterDate() {
+        return registerDate;
+    }
+
+    public void setRegisterDate(Date registerDate) {
+        this.registerDate = registerDate;
+    }
+
+    public Long getModifyId() {
+        return modifyId;
+    }
+
+    public void setModifyId(Long modifyId) {
+        this.modifyId = modifyId;
+    }
+
+    public Date getModifyDate() {
+        return modifyDate;
+    }
+
+    public void setModifyDate(Date modifyDate) {
+        this.modifyDate = modifyDate;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("BaseEntity [registerId=").append(this.registerId)
+                .append(", registerDate=").append(this.registerDate)
+                .append(", modifyId=").append(this.modifyId).append(", modifyDate=")
+                .append(this.modifyDate).append("]");
+        return builder.toString();
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/CustomField.java b/src/main/java/kr/wisestone/owl/domain/CustomField.java
new file mode 100644
index 0000000..61cab1d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/CustomField.java
@@ -0,0 +1,113 @@
+package kr.wisestone.owl.domain;
+
+
+import kr.wisestone.owl.domain.enumType.CustomFieldType;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Created by wisestone on 2018-03-07.
+ */
+@Entity
+public class CustomField extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+
+    @Enumerated(EnumType.STRING)
+    private CustomFieldType customFieldType;
+    private String defaultValue;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    @OneToMany(mappedBy = "customField", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<IssueTypeCustomField> issueTypeCustomFields = new HashSet<>();
+
+    @OrderBy("id asc")
+    @OneToMany(mappedBy = "customField", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<CustomFieldValue> customFieldValues = new HashSet<>();
+
+    @OneToMany(mappedBy = "customField", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<IssueCustomFieldValue> issueCustomFieldValues = new HashSet<>();
+
+    private String useFlag;
+
+    public CustomField() {
+    }
+
+    public String getUse() { return useFlag; }
+
+    public void setUse(String useFlag) { this.useFlag = useFlag; }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public CustomFieldType getCustomFieldType() {
+        return customFieldType;
+    }
+
+    public void setCustomFieldType(CustomFieldType customFieldType) {
+        this.customFieldType = customFieldType;
+    }
+
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    public void setDefaultValue(String defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public Set<IssueTypeCustomField> getIssueTypeCustomFields() {
+        return issueTypeCustomFields;
+    }
+
+    public void setIssueTypeCustomFields(Set<IssueTypeCustomField> issueTypeCustomFields) {
+        this.issueTypeCustomFields = issueTypeCustomFields;
+    }
+
+    public Set<CustomFieldValue> getCustomFieldValues() {
+        return customFieldValues;
+    }
+
+    public void setCustomFieldValues(Set<CustomFieldValue> customFieldValues) {
+        this.customFieldValues = customFieldValues;
+    }
+
+    public Set<IssueCustomFieldValue> getIssueCustomFieldValues() {
+        return issueCustomFieldValues;
+    }
+
+    public void setIssueCustomFieldValues(Set<IssueCustomFieldValue> issueCustomFieldValues) {
+        this.issueCustomFieldValues = issueCustomFieldValues;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/CustomFieldValue.java b/src/main/java/kr/wisestone/owl/domain/CustomFieldValue.java
new file mode 100644
index 0000000..18ba892
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/CustomFieldValue.java
@@ -0,0 +1,52 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-03-07.
+ */
+@Entity
+public class CustomFieldValue extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String value;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "custom_field_id")
+    private CustomField customField;
+
+    public CustomFieldValue(){}
+
+    public CustomFieldValue(CustomField customField, String value){
+        this.customField = customField;
+        this.value = value;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public CustomField getCustomField() {
+        return customField;
+    }
+
+    public void setCustomField(CustomField customField) {
+        this.customField = customField;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Event.java b/src/main/java/kr/wisestone/owl/domain/Event.java
new file mode 100644
index 0000000..5c5de8e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Event.java
@@ -0,0 +1,77 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import java.io.Serializable;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+@Entity
+public class Event extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static final Integer ACTIVATION = 1;  //  �솢�꽦
+    public static final Integer INACTIVATION = 0;  //  鍮꾪솢�꽦
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String title;
+    private String description;
+    private Integer status;
+    private String startDate;
+    private String endDate;
+
+    public Event(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public String getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(String startDate) {
+        this.startDate = startDate;
+    }
+
+    public String getEndDate() {
+        return endDate;
+    }
+
+    public void setEndDate(String endDate) {
+        this.endDate = endDate;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Faq.java b/src/main/java/kr/wisestone/owl/domain/Faq.java
new file mode 100644
index 0000000..5280121
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Faq.java
@@ -0,0 +1,59 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import java.io.Serializable;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+@Entity
+public class Faq extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static final Integer ACTIVATION = 1;  //  �솢�꽦
+    public static final Integer INACTIVATION = 0;  //  鍮꾪솢�꽦
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String title;
+    private String description;
+    private Integer status;
+
+    public Faq(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Guide.java b/src/main/java/kr/wisestone/owl/domain/Guide.java
new file mode 100644
index 0000000..02cd67d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Guide.java
@@ -0,0 +1,59 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import java.io.Serializable;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+@Entity
+public class Guide extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static final Integer ACTIVATION = 1;  //  �솢�꽦
+    public static final Integer INACTIVATION = 0;  //  鍮꾪솢�꽦
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String title;
+    private String description;
+    private Integer status;
+
+    public Guide(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Issue.java b/src/main/java/kr/wisestone/owl/domain/Issue.java
new file mode 100644
index 0000000..6061a8a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Issue.java
@@ -0,0 +1,277 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Created by wisestone on 2018-01-03.
+ */
+@Entity
+public class Issue extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final String WORKSPACE_MANAGER = "WORKSPACE_MANAGER"; //  �뾽臾� 怨듦컙 愿�由ъ옄
+    public static final String PROJECT_MANAGER = "PROJECT_MANAGER"; //  �봽濡쒖젥�듃 愿�由ъ옄
+    public static final String REGISTER = "REGISTER";   //  �씠�뒋 �벑濡앹옄
+    public static final String ASSIGNEE = "ASSIGNEE";   //  �씠�뒋 �떞�떦�옄
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String title;
+    private String description;
+    private Long reverseIndex;
+    private Long issueNumber;
+    private String startDate;
+    private String completeDate;
+
+    @ManyToOne(fetch=FetchType.LAZY)
+    @JoinColumn(name = "project_id")
+    private Project project;
+
+    @ManyToOne(fetch=FetchType.LAZY)
+    @JoinColumn(name = "issue_status_id")
+    private IssueStatus issueStatus;
+
+    @ManyToOne(fetch=FetchType.LAZY)
+    @JoinColumn(name = "issue_type_id")
+    private IssueType issueType;
+
+    @ManyToOne(fetch=FetchType.LAZY)
+    @JoinColumn(name = "priority_id")
+    private Priority priority;
+
+    @ManyToOne(fetch=FetchType.LAZY)
+    @JoinColumn(name = "severity_id")
+    private Severity severity;
+
+    @OneToOne(mappedBy = "issue", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private IssueRisk issueRisk;
+
+    @OrderBy(value="id DESC")
+    @OneToMany(mappedBy = "issue", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<IssueUser> issueUsers = new HashSet<>();
+
+    @OrderBy(value="id DESC")
+    @OneToMany(mappedBy="issue", cascade={CascadeType.ALL}, orphanRemoval = true)
+    private Set<AttachedFile> attachedFiles = new HashSet<>();
+
+    @OneToMany(mappedBy = "issue", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<UserLikeIssue> userLikeIssues = new HashSet<>();
+
+    //  �씠�젰�� �궘�젣 湲덉�
+    @OrderBy(value="id DESC")
+    @OneToMany(mappedBy="issue", cascade={CascadeType.ALL}, orphanRemoval = true)
+    private Set<IssueHistory> issueHistory = new HashSet<>();
+
+    @OneToMany(mappedBy="issue", cascade={CascadeType.ALL}, orphanRemoval = true)
+    @OrderBy(value="id DESC")
+    private Set<IssueComment> issueComments = new HashSet<>();
+
+    @OneToMany(mappedBy = "issue", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<IssueCustomFieldValue> issueCustomFieldValues = new HashSet<>();
+
+    @OneToMany(mappedBy = "issue", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<IssueVersion> issueVersions = new HashSet<>();
+
+    @OneToMany(mappedBy = "issue", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<IssueRelation> issueRelations = new HashSet<>();
+
+    @OneToOne(mappedBy = "issue", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private IssueReservation issueReservation;
+
+    @ManyToOne(fetch=FetchType.LAZY)
+    @JoinColumn(name = "workflow_status_id")
+    private WorkflowStatus workflowStatus;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Long getReverseIndex() {
+        return reverseIndex;
+    }
+
+    public void setReverseIndex(Long reverseIndex) {
+        this.reverseIndex = reverseIndex;
+    }
+
+    public Project getProject() {
+        return project;
+    }
+
+    public void setProject(Project project) {
+        this.project = project;
+    }
+
+    public IssueStatus getIssueStatus() {
+        return issueStatus;
+    }
+
+    public void setIssueStatus(IssueStatus issueStatus) {
+        this.issueStatus = issueStatus;
+    }
+
+    public IssueType getIssueType() {
+        return issueType;
+    }
+
+    public void setIssueType(IssueType issueType) {
+        this.issueType = issueType;
+    }
+
+    public Priority getPriority() {
+        return priority;
+    }
+
+    public void setPriority(Priority priority) {
+        this.priority = priority;
+    }
+
+    public Severity getSeverity() {
+        return severity;
+    }
+
+    public void setSeverity(Severity severity) {
+        this.severity = severity;
+    }
+
+    public IssueRisk getIssueRisk() {
+        return issueRisk;
+    }
+
+    public void setIssueRisk(IssueRisk issueRisk) {
+        this.issueRisk = issueRisk;
+    }
+
+    public Long getIssueNumber() {
+        return issueNumber;
+    }
+
+    public void setIssueNumber(Long issueNumber) {
+        this.issueNumber = issueNumber;
+    }
+
+    public Set<IssueUser> getIssueUsers() {
+        return issueUsers;
+    }
+
+    public void setIssueUsers(Set<IssueUser> issueUsers) {
+        this.issueUsers = issueUsers;
+    }
+
+    public Set<AttachedFile> getAttachedFiles() {
+        return attachedFiles;
+    }
+
+    public void setAttachedFiles(Set<AttachedFile> attachedFiles) {
+        this.attachedFiles = attachedFiles;
+    }
+
+    public Set<UserLikeIssue> getUserLikeIssues() {
+        return userLikeIssues;
+    }
+
+    public void setUserLikeIssues(Set<UserLikeIssue> userLikeIssues) {
+        this.userLikeIssues = userLikeIssues;
+    }
+
+    public Set<IssueHistory> getIssueHistory() {
+        return issueHistory;
+    }
+
+    public void setIssueHistory(Set<IssueHistory> issueHistory) {
+        this.issueHistory = issueHistory;
+    }
+
+    public Set<IssueComment> getIssueComments() {
+        return issueComments;
+    }
+
+    public void setIssueComments(Set<IssueComment> issueComments) {
+        this.issueComments = issueComments;
+    }
+
+    public Set<IssueCustomFieldValue> getIssueCustomFieldValues() {
+        return issueCustomFieldValues;
+    }
+
+    public void setIssueCustomFieldValues(Set<IssueCustomFieldValue> issueCustomFieldValues) {
+        this.issueCustomFieldValues = issueCustomFieldValues;
+    }
+
+    public String getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(String startDate) {
+        this.startDate = startDate;
+    }
+
+    public String getCompleteDate() {
+        return completeDate;
+    }
+
+    public void setCompleteDate(String completeDate) {
+        this.completeDate = completeDate;
+    }
+
+    public IssueReservation getIssueReservation() {
+        return issueReservation;
+    }
+
+    public void setIssueReservation(IssueReservation issueReservation) {
+        this.issueReservation = issueReservation;
+    }
+
+    public Set<IssueVersion> getIssueVersions() {
+        return issueVersions;
+    }
+
+    public void setIssueVersions(Set<IssueVersion> issueVersions) {
+        this.issueVersions = issueVersions;
+    }
+
+    public WorkflowStatus getWorkflowStatus() {
+        return workflowStatus;
+    }
+
+    public void setWorkflowStatus(WorkflowStatus workflowStatus) {
+        this.workflowStatus = workflowStatus;
+    }
+
+    public Set<IssueRelation> getIssueRelations() {
+        return issueRelations;
+    }
+
+    public void setIssueRelations(Set<IssueRelation> issueRelations) {
+        this.issueRelations = issueRelations;
+    }
+
+    public  void clearIssueRelations() {
+        this.issueRelations.clear();
+    }
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueComment.java b/src/main/java/kr/wisestone/owl/domain/IssueComment.java
new file mode 100644
index 0000000..c69d939
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueComment.java
@@ -0,0 +1,63 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.*;
+
+
+@Entity
+public class IssueComment extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String description;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="issue_id")
+    private Issue issue;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="workspace_id")
+    private Workspace workspace;
+
+    public IssueComment() {
+    }
+
+    public IssueComment(String description) {
+        this.description = description;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Issue getIssue() {
+        return issue;
+    }
+
+    public void setIssue(Issue issue) {
+        this.issue = issue;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueCustomFieldValue.java b/src/main/java/kr/wisestone/owl/domain/IssueCustomFieldValue.java
new file mode 100644
index 0000000..28ce227
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueCustomFieldValue.java
@@ -0,0 +1,78 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-03-07.
+ */
+@Entity
+public class IssueCustomFieldValue extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String useValue;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "issue_type_custom_field_id")
+    private IssueTypeCustomField issueTypeCustomField;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "issue_id")
+    private Issue issue;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "custom_field_id")
+    private CustomField customField;
+
+    public IssueCustomFieldValue(){}
+
+    public IssueCustomFieldValue(Issue issue, CustomField customField, IssueTypeCustomField issueTypeCustomField, String userValue){
+        this.issue = issue;
+        this.customField = customField;
+        this.issueTypeCustomField = issueTypeCustomField;
+        this.useValue = userValue;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getUseValue() {
+        return useValue;
+    }
+
+    public void setUseValue(String useValue) {
+        this.useValue = useValue;
+    }
+
+    public IssueTypeCustomField getIssueTypeCustomField() {
+        return issueTypeCustomField;
+    }
+
+    public void setIssueTypeCustomField(IssueTypeCustomField issueTypeCustomField) {
+        this.issueTypeCustomField = issueTypeCustomField;
+    }
+
+    public Issue getIssue() {
+        return issue;
+    }
+
+    public void setIssue(Issue issue) {
+        this.issue = issue;
+    }
+
+    public CustomField getCustomField() {
+        return customField;
+    }
+
+    public void setCustomField(CustomField customField) {
+        this.customField = customField;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueHistory.java b/src/main/java/kr/wisestone/owl/domain/IssueHistory.java
new file mode 100644
index 0000000..25b3c47
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueHistory.java
@@ -0,0 +1,75 @@
+package kr.wisestone.owl.domain;
+
+import kr.wisestone.owl.domain.enumType.IssueHistoryType;
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-01-26.
+ */
+@Entity
+public class IssueHistory extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String description;
+
+    @Enumerated(EnumType.STRING)
+    private IssueHistoryType issueHistoryType;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="issue_id")
+    private Issue issue;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="project_id")
+    private Project project;
+
+    public IssueHistory(){}
+
+    public IssueHistory(Issue issue) {
+        this.issue = issue;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Project getProject() {
+        return project;
+    }
+
+    public void setProject(Project project) {
+        this.project = project;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public IssueHistoryType getIssueHistoryType() {
+        return issueHistoryType;
+    }
+
+    public void setIssueHistoryType(IssueHistoryType issueHistoryType) {
+        this.issueHistoryType = issueHistoryType;
+    }
+
+    public Issue getIssue() {
+        return issue;
+    }
+
+    public void setIssue(Issue issue) {
+        this.issue = issue;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueNumberGenerator.java b/src/main/java/kr/wisestone/owl/domain/IssueNumberGenerator.java
new file mode 100644
index 0000000..f2c7597
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueNumberGenerator.java
@@ -0,0 +1,52 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-03-07.
+ */
+@Entity
+public class IssueNumberGenerator extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private Long number;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "project_id")
+    private Project project;
+
+    public IssueNumberGenerator(){}
+
+    public IssueNumberGenerator(Project project, Long number) {
+        this.project = project;
+        this.number = number;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Project getProject() {
+        return project;
+    }
+
+    public void setProject(Project project) {
+        this.project = project;
+    }
+
+    public Long getNumber() {
+        return number;
+    }
+
+    public void setNumber(Long number) {
+        this.number = number;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueRelation.java b/src/main/java/kr/wisestone/owl/domain/IssueRelation.java
new file mode 100644
index 0000000..1d3a3ef
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueRelation.java
@@ -0,0 +1,56 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Create By maprex / 2021-05-13
+ */
+@Entity
+public class IssueRelation extends BaseEntity implements Serializable {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="issue_id")
+    private Issue issue;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="relation_issue_id")
+    private Issue relationIssue;
+
+    private Long relationIssueType;
+
+    public void IssueRelation(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Issue getIssue() {
+        return issue;
+    }
+
+    public void setIssue(Issue issue) {
+        this.issue = issue;
+    }
+
+    public Issue getRelationIssue() {
+        return relationIssue;
+    }
+
+    public void setRelationIssue(Issue issue) {
+        this.relationIssue = issue;
+    }
+
+    public Long getRelationIssueType() { return  relationIssueType; }
+
+    public void setRelationIssueType(Long relationType) { this.relationIssueType = relationType; }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueReservation.java b/src/main/java/kr/wisestone/owl/domain/IssueReservation.java
new file mode 100644
index 0000000..d5ef926
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueReservation.java
@@ -0,0 +1,72 @@
+package kr.wisestone.owl.domain;
+
+import kr.wisestone.owl.domain.enumType.IssueReservationType;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-07
+ */
+@Entity
+public class IssueReservation extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    @Enumerated(EnumType.STRING)
+    private IssueReservationType issueReservationType;
+
+    private String reservation;
+
+    @OneToOne
+    @JoinColumn(name = "issue_id")
+    private Issue issue;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    public IssueReservation(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public IssueReservationType getIssueReservationType() {
+        return issueReservationType;
+    }
+
+    public void setIssueReservationType(IssueReservationType issueReservationType) {
+        this.issueReservationType = issueReservationType;
+    }
+
+    public String getReservation() {
+        return reservation;
+    }
+
+    public void setReservation(String reservation) {
+        this.reservation = reservation;
+    }
+
+    public Issue getIssue() {
+        return issue;
+    }
+
+    public void setIssue(Issue issue) {
+        this.issue = issue;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueRisk.java b/src/main/java/kr/wisestone/owl/domain/IssueRisk.java
new file mode 100644
index 0000000..49e1d26
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueRisk.java
@@ -0,0 +1,77 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-11-01.
+ */
+@Entity
+public class IssueRisk extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private Long changeAssigneeCount;
+    private Long changeIssueStatusCount;
+    private String issueStatusIds;
+
+    @OneToOne
+    @JoinColumn(name = "issue_id")
+    private Issue issue;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    public IssueRisk(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getChangeAssigneeCount() {
+        return changeAssigneeCount;
+    }
+
+    public void setChangeAssigneeCount(Long changeAssigneeCount) {
+        this.changeAssigneeCount = changeAssigneeCount;
+    }
+
+    public Long getChangeIssueStatusCount() {
+        return changeIssueStatusCount;
+    }
+
+    public void setChangeIssueStatusCount(Long changeIssueStatusCount) {
+        this.changeIssueStatusCount = changeIssueStatusCount;
+    }
+
+    public String getIssueStatusIds() {
+        return issueStatusIds;
+    }
+
+    public void setIssueStatusIds(String issueStatusIds) {
+        this.issueStatusIds = issueStatusIds;
+    }
+
+    public Issue getIssue() {
+        return issue;
+    }
+
+    public void setIssue(Issue issue) {
+        this.issue = issue;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueSearch.java b/src/main/java/kr/wisestone/owl/domain/IssueSearch.java
new file mode 100644
index 0000000..314a42d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueSearch.java
@@ -0,0 +1,60 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-03-07.
+ */
+@Entity
+public class IssueSearch extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "user_id")
+    private User user;
+
+    private String conditions;
+
+    public IssueSearch(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public String getConditions() {
+        return conditions;
+    }
+
+    public void setConditions(String conditions) {
+        this.conditions = conditions;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueStatus.java b/src/main/java/kr/wisestone/owl/domain/IssueStatus.java
new file mode 100644
index 0000000..5f83c0f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueStatus.java
@@ -0,0 +1,136 @@
+package kr.wisestone.owl.domain;
+
+
+import kr.wisestone.owl.domain.enumType.IssueStatusType;
+import org.hibernate.annotations.Type;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Created by wisestone on 2018-01-03.
+ */
+@Entity
+public class IssueStatus extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private String color;
+    private Long position; //  湲곕낯 �젣怨듬릺�뒗 �씠�뒋 �긽�깭�뿉�꽌 �뿰寃곗쓣 �쐞�빐 �궗�슜
+
+    @Type(type = "yes_no")
+    private Boolean defaultYn = Boolean.FALSE;
+
+    @Enumerated(EnumType.STRING)
+    private IssueStatusType issueStatusType;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    @OneToMany(mappedBy = "issueStatus", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<Issue> issues = new HashSet<>();
+
+    @OneToMany(mappedBy="sourceIssueStatus", cascade={CascadeType.ALL}, orphanRemoval=true)
+    private Set<WorkflowTransition> sourceWorkflowTransitions = new HashSet<>();
+
+    @OneToMany(mappedBy="targetIssueStatus", cascade={CascadeType.ALL}, orphanRemoval=true)
+    private Set<WorkflowTransition> targetWorkflowTransitions = new HashSet<>();
+
+    public IssueStatus() {
+    }
+
+    public IssueStatus(Workspace workspace, String name, Boolean defaultYn, String color, IssueStatusType issueStatusType, Long position) {
+        this.workspace = workspace;
+        this.name = name;
+        this.defaultYn = defaultYn;
+        this.color = color;
+        this.issueStatusType = issueStatusType;
+        this.position = position;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public Set<Issue> getIssues() {
+        return issues;
+    }
+
+    public void setIssues(Set<Issue> issues) {
+        this.issues = issues;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+
+    public IssueStatusType getIssueStatusType() {
+        return issueStatusType;
+    }
+
+    public void setIssueStatusType(IssueStatusType issueStatusType) {
+        this.issueStatusType = issueStatusType;
+    }
+
+    public Boolean getDefaultYn() {
+        return defaultYn;
+    }
+
+    public void setDefaultYn(Boolean defaultYn) {
+        this.defaultYn = defaultYn;
+    }
+
+    public Long getPosition() {
+        return position;
+    }
+
+    public void setPosition(Long position) {
+        this.position = position;
+    }
+
+    public Set<WorkflowTransition> getSourceWorkflowTransitions() {
+        return sourceWorkflowTransitions;
+    }
+
+    public void setSourceWorkflowTransitions(Set<WorkflowTransition> sourceWorkflowTransitions) {
+        this.sourceWorkflowTransitions = sourceWorkflowTransitions;
+    }
+
+    public Set<WorkflowTransition> getTargetWorkflowTransitions() {
+        return targetWorkflowTransitions;
+    }
+
+    public void setTargetWorkflowTransitions(Set<WorkflowTransition> targetWorkflowTransitions) {
+        this.targetWorkflowTransitions = targetWorkflowTransitions;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueTableConfig.java b/src/main/java/kr/wisestone/owl/domain/IssueTableConfig.java
new file mode 100644
index 0000000..9cb73fd
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueTableConfig.java
@@ -0,0 +1,59 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2019-02-07.
+ */
+@Entity
+public class IssueTableConfig extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String issueTableConfigs;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "user_id")
+    private User user;
+
+    public IssueTableConfig(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getIssueTableConfigs() {
+        return issueTableConfigs;
+    }
+
+    public void setIssueTableConfigs(String issueTableConfigs) {
+        this.issueTableConfigs = issueTableConfigs;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueType.java b/src/main/java/kr/wisestone/owl/domain/IssueType.java
new file mode 100644
index 0000000..ac7f750
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueType.java
@@ -0,0 +1,98 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Created by wisestone on 2018-03-07.
+ */
+@Entity
+public class IssueType extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private String description;
+    private String color;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workflow_id")
+    private Workflow workflow;
+
+    @OneToMany(mappedBy = "issueType", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<IssueTypeCustomField> issueTypeCustomFields = new HashSet<>();
+
+    public IssueType(){}
+
+    public IssueType(Workspace workspace, Workflow workflow, String name, String description, String color){
+        this.workspace = workspace;
+        this.workflow = workflow;
+        this.name = name;
+        this.description = description;
+        this.color = color;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public Workflow getWorkflow() {
+        return workflow;
+    }
+
+    public void setWorkflow(Workflow workflow) {
+        this.workflow = workflow;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+
+    public Set<IssueTypeCustomField> getIssueTypeCustomFields() {
+        return issueTypeCustomFields;
+    }
+
+    public void setIssueTypeCustomFields(Set<IssueTypeCustomField> issueTypeCustomFields) {
+        this.issueTypeCustomFields = issueTypeCustomFields;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueTypeCustomField.java b/src/main/java/kr/wisestone/owl/domain/IssueTypeCustomField.java
new file mode 100644
index 0000000..6c4c55d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueTypeCustomField.java
@@ -0,0 +1,103 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Created by wisestone on 2018-03-07.
+ */
+@Entity
+public class IssueTypeCustomField extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static final String FIELD_OPTION_Y = "01";  //  �븘�닔濡� �궗�슜
+    public static final String FIELD_OPTION_N = "02";    //  �샃�뀡�쑝濡� �궗�슜
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String fieldOption;
+    private Integer position = 0;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "project_id")
+    private Project project;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "issue_type_id")
+    private IssueType issueType;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "custom_field_id")
+    private CustomField customField;
+
+    @OneToMany(mappedBy = "issueTypeCustomField", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<IssueCustomFieldValue> issueCustomFieldValues = new HashSet<>();
+
+    public IssueTypeCustomField(){}
+
+    public IssueTypeCustomField(Project project, IssueType issueType, CustomField customField, String fieldOption){
+        this.project = project;
+        this.issueType = issueType;
+        this.customField = customField;
+        this.fieldOption = fieldOption;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getFieldOption() {
+        return fieldOption;
+    }
+
+    public void setFieldOption(String fieldOption) {
+        this.fieldOption = fieldOption;
+    }
+
+    public Project getProject() {
+        return project;
+    }
+
+    public void setProject(Project project) {
+        this.project = project;
+    }
+
+    public IssueType getIssueType() {
+        return issueType;
+    }
+
+    public void setIssueType(IssueType issueType) {
+        this.issueType = issueType;
+    }
+
+    public CustomField getCustomField() {
+        return customField;
+    }
+
+    public void setCustomField(CustomField customField) {
+        this.customField = customField;
+    }
+
+    public Set<IssueCustomFieldValue> getIssueCustomFieldValues() {
+        return issueCustomFieldValues;
+    }
+
+    public void setIssueCustomFieldValues(Set<IssueCustomFieldValue> issueCustomFieldValues) {
+        this.issueCustomFieldValues = issueCustomFieldValues;
+    }
+
+    public Integer getPosition() {
+        return position;
+    }
+
+    public void setPosition(Integer position) {
+        this.position = position;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueUser.java b/src/main/java/kr/wisestone/owl/domain/IssueUser.java
new file mode 100644
index 0000000..e5876a4
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueUser.java
@@ -0,0 +1,65 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+@Entity
+public class IssueUser extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "user_id")
+    private User user;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "issue_id")
+    private Issue issue;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    public IssueUser() {
+    }
+
+    public IssueUser(Issue issue, User user) {
+        this.issue = issue;
+        this.user = user;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public Issue getIssue() {
+        return issue;
+    }
+
+    public void setIssue(Issue issue) {
+        this.issue = issue;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/IssueVersion.java b/src/main/java/kr/wisestone/owl/domain/IssueVersion.java
new file mode 100644
index 0000000..8d4f314
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/IssueVersion.java
@@ -0,0 +1,88 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2019-02-28.
+ */
+@Entity
+public class IssueVersion extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private Integer version;
+    private String content;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="issue_id")
+    private Issue issue;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="project_id")
+    private Project project;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="workspace_id")
+    private Workspace workspace;
+
+    public IssueVersion() {
+    }
+
+    public IssueVersion(Issue issue, Project project, Workspace workspace, String content) {
+        this.issue = issue;
+        this.project = project;
+        this.workspace = workspace;
+        this.content = content;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Integer getVersion() {
+        return version;
+    }
+
+    public void setVersion(Integer version) {
+        this.version = version;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public Issue getIssue() {
+        return issue;
+    }
+
+    public void setIssue(Issue issue) {
+        this.issue = issue;
+    }
+
+    public Project getProject() {
+        return project;
+    }
+
+    public void setProject(Project project) {
+        this.project = project;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/ManageUser.java b/src/main/java/kr/wisestone/owl/domain/ManageUser.java
new file mode 100644
index 0000000..ac3e61f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/ManageUser.java
@@ -0,0 +1,80 @@
+package kr.wisestone.owl.domain;
+
+import org.hibernate.annotations.Type;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-02-13.
+ */
+@Entity
+public class ManageUser extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch= FetchType.EAGER)  //  �뒪耳�伊대윭�뿉�꽌 硫붿씪 �쟾�넚�쓣 �쐞�빐 利됱떆 濡쒕뵫 �쟻�슜
+    @JoinColumn(name="user_id")
+    private User user;
+
+    @ManyToOne(fetch=FetchType.EAGER)   //  �뒪耳�伊대윭�뿉�꽌 硫붿씪 �쟾�넚�쓣 �쐞�빐 利됱떆 濡쒕뵫 �쟻�슜
+    @JoinColumn(name="workspace_id")
+    private Workspace workspace;
+
+    @Type(type = "yes_no")
+    private Boolean useYn = Boolean.FALSE;
+
+    private Long disablePosition;
+
+    public ManageUser() {}
+
+    public ManageUser(User user, Workspace workspace, Boolean managerYn, Boolean useYn, Long disablePosition) {
+        this.user = user;
+        this.workspace = workspace;
+        this.useYn = useYn;
+        this.disablePosition = disablePosition;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public Boolean getUseYn() {
+        return useYn;
+    }
+
+    public void setUseYn(Boolean useYn) {
+        this.useYn = useYn;
+    }
+
+    public Long getDisablePosition() {
+        return disablePosition;
+    }
+
+    public void setDisablePosition(Long disablePosition) {
+        this.disablePosition = disablePosition;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Notice.java b/src/main/java/kr/wisestone/owl/domain/Notice.java
new file mode 100644
index 0000000..f19e578
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Notice.java
@@ -0,0 +1,47 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import java.io.Serializable;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+@Entity
+public class Notice extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String title;
+    private String description;
+
+    public Notice(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Payment.java b/src/main/java/kr/wisestone/owl/domain/Payment.java
new file mode 100644
index 0000000..3c35415
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Payment.java
@@ -0,0 +1,83 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Created by wisestone on 2018-02-13.
+ */
+@Entity
+public class Payment extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static final String MONTH = "MONTH";
+    public static final String YEAR = "YEAR";
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String type;
+    private Integer price;
+    private Integer buyUser;
+
+    @OneToOne
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    @OneToOne(mappedBy = "payment", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private ReservationDisableUser reservationDisableUser;
+
+    public Payment() {
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public Integer getPrice() {
+        return price;
+    }
+
+    public void setPrice(Integer price) {
+        this.price = price;
+    }
+
+    public Integer getBuyUser() {
+        return buyUser;
+    }
+
+    public void setBuyUser(Integer buyUser) {
+        this.buyUser = buyUser;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public ReservationDisableUser getReservationDisableUser() {
+        return reservationDisableUser;
+    }
+
+    public void setReservationDisableUser(ReservationDisableUser reservationDisableUser) {
+        this.reservationDisableUser = reservationDisableUser;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/PaymentHistory.java b/src/main/java/kr/wisestone/owl/domain/PaymentHistory.java
new file mode 100644
index 0000000..c7612de
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/PaymentHistory.java
@@ -0,0 +1,139 @@
+package kr.wisestone.owl.domain;
+
+import kr.wisestone.owl.service.impl.PaymentServiceImpl;
+import kr.wisestone.owl.util.ConvertUtil;
+import org.apache.commons.text.StringEscapeUtils;
+import org.springframework.util.StringUtils;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-02-13.
+ */
+@Entity
+public class PaymentHistory extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static final String PAYMENT_RESULT_SUCCESS = "success";
+    public static final String PAYMENT_RESULT_FAILED = "failed";
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String type;
+    private Integer price;
+    private Integer buyUser;
+    private String customerUid;
+    private String merchantUid;
+    private String paymentResult;
+    private String paymentResponse;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    public PaymentHistory() {
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public Integer getPrice() {
+        return price;
+    }
+
+    public void setPrice(Integer price) {
+        this.price = price;
+    }
+
+    public Integer getBuyUser() {
+        return buyUser;
+    }
+
+    public void setBuyUser(Integer buyUser) {
+        this.buyUser = buyUser;
+    }
+
+    public String getCustomerUid() {
+        return customerUid;
+    }
+
+    public void setCustomerUid(String customerUid) {
+        this.customerUid = customerUid;
+    }
+
+    public String getMerchantUid() {
+        return merchantUid;
+    }
+
+    public void setMerchantUid(String merchantUid) {
+        this.merchantUid = merchantUid;
+    }
+
+    public String getPaymentResult() {
+        return paymentResult;
+    }
+
+    public void setPaymentResult(String paymentResult) {
+        this.paymentResult = paymentResult;
+    }
+
+    public String getPaymentResponse() {
+        return paymentResponse;
+    }
+
+    public void setPaymentResponse(String paymentResponse) {
+        this.paymentResponse = paymentResponse;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public PaymentHistory bindPaymentResult(PaymentServiceImpl.RestClientResultObject resultObject) {
+
+        if (resultObject.isValidResult()) {
+            this.setPaymentResult(PAYMENT_RESULT_SUCCESS);
+            this.setPaymentResponse(ConvertUtil.convertObjectToJson(resultObject.getResponse()));
+        }
+        else {
+
+            this.setPaymentResult(PAYMENT_RESULT_FAILED);
+
+            if (resultObject.isHttpRequestFailed()) {
+                this.setPaymentResponse(resultObject.getHttpStatus().getReasonPhrase());
+            }
+            else if (resultObject.isIamportResultFailed()) {
+                this.setPaymentResponse(resultObject.getMessage());
+            }
+            else if (resultObject.isIamportPaymentFailed()) {
+                if (StringUtils.hasText(resultObject.getMessage())) {
+                    this.setPaymentResponse(resultObject.getMessage());
+                }
+                else {
+                    this.setPaymentResponse(StringEscapeUtils.unescapeJava(resultObject.getIamportFailReason()));
+                }
+            }
+        }
+
+        return this;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Permission.java b/src/main/java/kr/wisestone/owl/domain/Permission.java
new file mode 100644
index 0000000..4801f61
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Permission.java
@@ -0,0 +1,80 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+@Entity
+public class Permission extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static final String ROLE_TYPE_USER = "01";   //  �씪諛� �궗�슜�옄 沅뚰븳
+    public static final String ROLE_TYPE_WORKSPACE_MANAGER = "02"; //  �썙�겕�뒪�럹�씠�뒪 愿�由ъ옄 沅뚰븳
+    public static final String ROLE_TYPE_PROJECT_JOIN = "03";   //  �봽濡쒖젥�듃 李몄뿬�옄 沅뚰븳
+    public static final String ROLE_TYPE_PROJECT_MANAGER = "04";    //  �봽濡쒖젥�듃 愿�由ъ옄 沅뚰븳
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private String action;
+    private String roleType;
+
+    @OneToMany(mappedBy="permission", cascade={CascadeType.ALL})
+    private Set<SystemRolePermission> systemRolePermissions = new HashSet<>();
+
+    @OneToMany(mappedBy="permission", cascade={CascadeType.ALL})
+    private Set<ProjectRolePermission> projectRolePermissions = new HashSet<>();
+
+    public Permission() {
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getAction() {
+        return action;
+    }
+
+    public void setAction(String action) {
+        this.action = action;
+    }
+
+    public String getRoleType() {
+        return roleType;
+    }
+
+    public void setRoleType(String roleType) {
+        this.roleType = roleType;
+    }
+
+    public Set<SystemRolePermission> getSystemRolePermissions() {
+        return systemRolePermissions;
+    }
+
+    public void setSystemRolePermissions(Set<SystemRolePermission> systemRolePermissions) {
+        this.systemRolePermissions = systemRolePermissions;
+    }
+
+    public Set<ProjectRolePermission> getProjectRolePermissions() {
+        return projectRolePermissions;
+    }
+
+    public void setProjectRolePermissions(Set<ProjectRolePermission> projectRolePermissions) {
+        this.projectRolePermissions = projectRolePermissions;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Priority.java b/src/main/java/kr/wisestone/owl/domain/Priority.java
new file mode 100644
index 0000000..5eb10d1
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Priority.java
@@ -0,0 +1,86 @@
+package kr.wisestone.owl.domain;
+
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Created by wisestone on 2018-01-03.
+ */
+@Entity
+public class Priority extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private Integer position;
+    private String color;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    @OneToMany(mappedBy = "priority", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<Issue> issues = new HashSet<>();
+
+    public Priority() {}
+
+    public Priority(String name, Integer position, String color, Workspace workspace) {
+        this.name = name;
+        this.position = position;
+        this.color = color;
+        this.workspace = workspace;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getPosition() {
+        return position;
+    }
+
+    public void setPosition(Integer position) {
+        this.position = position;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public Set<Issue> getIssues() {
+        return issues;
+    }
+
+    public void setIssues(Set<Issue> issues) {
+        this.issues = issues;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Project.java b/src/main/java/kr/wisestone/owl/domain/Project.java
new file mode 100644
index 0000000..4069de6
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Project.java
@@ -0,0 +1,200 @@
+package kr.wisestone.owl.domain;
+
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import org.hibernate.annotations.Type;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Created by jeong on 2017-12-30.
+ */
+@Entity
+public class Project extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static final String PROJECT_READY = "01";
+    public static final String PROJECT_OPEN = "02";
+    public static final String PROJECT_CLOSE = "03";
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private String projectKey;
+    private String description;
+    private String status;
+    private String startDate;
+    private String endDate;
+    @Type(type="yes_no")
+    private Boolean defaultYn = Boolean.FALSE;
+
+    @Enumerated(EnumType.STRING)
+    private ProjectType projectType;
+
+    @ManyToOne(fetch = FetchType.EAGER)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    @OneToMany(mappedBy = "project", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<Issue> issues = new HashSet<>();
+
+    @OneToMany(mappedBy = "project", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<IssueNumberGenerator> issueNumberGenerators = new HashSet<>();
+
+    @OneToMany(mappedBy = "project", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<IssueTypeCustomField> issueTypeCustomFields = new HashSet<>();
+
+    @OneToMany(mappedBy = "project", cascade={CascadeType.ALL}, orphanRemoval=true)
+    private Set<ProjectRole> projectRoles = new HashSet<>();
+
+    @OneToMany(mappedBy = "project", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<UserInviteProject> userInviteProjects = new HashSet<>();
+
+    @OneToMany(mappedBy = "project", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<ProjectClosure> projectClosures = new HashSet<>();
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getProjectKey() {
+        return projectKey;
+    }
+
+    public void setProjectKey(String projectKey) {
+        this.projectKey = projectKey;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(String startDate) {
+        this.startDate = startDate;
+    }
+
+    public String getEndDate() {
+        return endDate;
+    }
+
+    public void setEndDate(String endDate) {
+        this.endDate = endDate;
+    }
+
+    public Boolean getDefaultYn() {
+        return defaultYn;
+    }
+
+    public void setDefaultYn(Boolean defaultYn) {
+        this.defaultYn = defaultYn;
+    }
+
+    public ProjectType getProjectType() {
+        return projectType;
+    }
+
+    public void setProjectType(ProjectType projectType) {
+        this.projectType = projectType;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public Set<Issue> getIssues() {
+        return issues;
+    }
+
+    public void setIssues(Set<Issue> issues) {
+        this.issues = issues;
+    }
+
+    public Set<IssueNumberGenerator> getIssueNumberGenerators() {
+        return issueNumberGenerators;
+    }
+
+    public void setIssueNumberGenerators(Set<IssueNumberGenerator> issueNumberGenerators) {
+        this.issueNumberGenerators = issueNumberGenerators;
+    }
+
+    public Set<IssueTypeCustomField> getIssueTypeCustomFields() {
+        return issueTypeCustomFields;
+    }
+
+    public void setIssueTypeCustomFields(Set<IssueTypeCustomField> issueTypeCustomFields) {
+        this.issueTypeCustomFields = issueTypeCustomFields;
+    }
+
+    public Set<ProjectRole> getProjectRoles() {
+        return projectRoles;
+    }
+
+    public void setProjectRoles(Set<ProjectRole> projectRoles) {
+        this.projectRoles = projectRoles;
+    }
+
+    public Set<UserInviteProject> getUserInviteProjects() {
+        return userInviteProjects;
+    }
+
+    public void setUserInviteProjects(Set<UserInviteProject> userInviteProjects) {
+        this.userInviteProjects = userInviteProjects;
+    }
+
+    public Set<ProjectClosure> getProjectClosures() {
+        return projectClosures;
+    }
+
+    public  ProjectClosure getParentProjectClosure() {
+        if (this.projectClosures != null && this.projectClosures.size() > 0) {
+            Iterator<ProjectClosure> iter = this.projectClosures.iterator();
+            return  iter.next();
+        }
+        return  null;
+    }
+
+
+    public void setProjectClosures(Set<ProjectClosure> childProjects) {
+        this.projectClosures = childProjects;
+    }
+
+    public  void addParent(ProjectClosure project){
+        projectClosures.add(project);
+    }
+}
+
diff --git a/src/main/java/kr/wisestone/owl/domain/ProjectClosure.java b/src/main/java/kr/wisestone/owl/domain/ProjectClosure.java
new file mode 100644
index 0000000..18a13bd
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/ProjectClosure.java
@@ -0,0 +1,49 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+@Entity
+public class ProjectClosure extends BaseEntity implements Serializable {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="project_id")
+    private Project project;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="parent_project_id")
+    private Project parentProject;
+
+    public ProjectClosure(){}
+
+    public ProjectClosure(Project project, Project parentProject){
+        this.project = project;
+        this.parentProject = parentProject;
+    }
+
+    public  ProjectClosure(Project project) {
+        this.project = project;
+        this.parentProject = null;
+    }
+
+    public Long getId() {return this.id; }
+
+    public void setId(Long id) { this.id = id; }
+
+    public Project getProject() {
+        return project;
+    }
+
+    public void setProject(Project project) {
+        this.project = project;
+    }
+
+    public Project getParentProject() {
+        return parentProject;
+    }
+
+    public  void setParentProject(Project project) {this.parentProject = project; }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/ProjectRole.java b/src/main/java/kr/wisestone/owl/domain/ProjectRole.java
new file mode 100644
index 0000000..832baa0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/ProjectRole.java
@@ -0,0 +1,111 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Created by jeong on 2017-12-30.
+ */
+@Entity
+public class ProjectRole extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static final String TYPE_DEFAULT  = "01";    //  �씪諛� �궗�슜�옄
+    public static final String TYPE_MANAGER  = "02";    //  愿�由ъ옄
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private String roleType;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="project_id")
+    private Project project;
+
+    @OneToMany(mappedBy="projectRole", cascade={CascadeType.ALL}, orphanRemoval=true)
+    private Set<ProjectRoleUser> projectRoleUsers = new HashSet<>();
+
+    @OneToMany(mappedBy="projectRole", cascade={CascadeType.ALL}, orphanRemoval=true)
+    private Set<ProjectRolePermission> projectRolePermissions = new HashSet<>();
+
+    public ProjectRole(){}
+
+    public ProjectRole(Project project, String name, String roleType){
+        this.project = project;
+        this.name = name;
+        this.roleType = roleType;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getRoleType() {
+        return roleType;
+    }
+
+    public void setRoleType(String roleType) {
+        this.roleType = roleType;
+    }
+
+    public Project getProject() {
+        return project;
+    }
+
+    public void setProject(Project project) {
+        this.project = project;
+    }
+
+    public Set<ProjectRoleUser> getProjectRoleUsers() {
+        return projectRoleUsers;
+    }
+
+    public void setProjectRoleUsers(Set<ProjectRoleUser> projectRoleUsers) {
+        this.projectRoleUsers = projectRoleUsers;
+    }
+
+    public Set<ProjectRolePermission> getProjectRolePermissions() {
+        return projectRolePermissions;
+    }
+
+    public void setProjectRolePermissions(Set<ProjectRolePermission> projectRolePermissions) {
+        this.projectRolePermissions = projectRolePermissions;
+    }
+
+    public void addUser(User user) {
+        if (this.projectRoleUsers == null) {
+            this.projectRoleUsers = new HashSet<>();
+        }
+        ProjectRoleUser projectRoleUser = new ProjectRoleUser(this, user);
+
+        this.projectRoleUsers.add(projectRoleUser);
+    }
+
+    public void removeProjectRole(User user) {
+        Iterator<ProjectRoleUser> iterator = this.projectRoleUsers.iterator();
+
+        while (iterator.hasNext()) {
+            ProjectRoleUser projectRoleUser = iterator.next();
+            if (user.getId().equals(projectRoleUser.getUser().getId())) {
+                this.projectRoleUsers.remove(projectRoleUser);
+                break;
+            }
+        }
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/ProjectRolePermission.java b/src/main/java/kr/wisestone/owl/domain/ProjectRolePermission.java
new file mode 100644
index 0000000..4ded4be
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/ProjectRolePermission.java
@@ -0,0 +1,55 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by jeong on 2017-12-30.
+ */
+@Entity
+public class ProjectRolePermission extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="project_role_id")
+    private ProjectRole projectRole;
+
+    @ManyToOne(fetch=FetchType.LAZY)
+    @JoinColumn(name="permission_id")
+    private Permission permission;
+
+    public ProjectRolePermission(){}
+
+    public ProjectRolePermission(ProjectRole projectRole, Permission permission) {
+        this.projectRole = projectRole;
+        this.permission = permission;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public ProjectRole getProjectRole() {
+        return projectRole;
+    }
+
+    public void setProjectRole(ProjectRole projectRole) {
+        this.projectRole = projectRole;
+    }
+
+    public Permission getPermission() {
+        return permission;
+    }
+
+    public void setPermission(Permission permission) {
+        this.permission = permission;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/ProjectRoleUser.java b/src/main/java/kr/wisestone/owl/domain/ProjectRoleUser.java
new file mode 100644
index 0000000..ab69468
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/ProjectRoleUser.java
@@ -0,0 +1,56 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by jeong on 2017-12-30.
+ */
+@Entity
+public class ProjectRoleUser extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name="project_role_id")
+    private ProjectRole projectRole;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name="user_id")
+    private User user;
+
+    public ProjectRoleUser() {
+    }
+
+    public ProjectRoleUser(ProjectRole projectRole, User user) {
+        this.projectRole = projectRole;
+        this.user = user;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public ProjectRole getProjectRole() {
+        return projectRole;
+    }
+
+    public void setProjectRole(ProjectRole projectRole) {
+        this.projectRole = projectRole;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Qna.java b/src/main/java/kr/wisestone/owl/domain/Qna.java
new file mode 100644
index 0000000..4e432e9
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Qna.java
@@ -0,0 +1,47 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import java.io.Serializable;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+@Entity
+public class Qna extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String title;
+    private String description;
+
+    public Qna(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/ReservationDisableUser.java b/src/main/java/kr/wisestone/owl/domain/ReservationDisableUser.java
new file mode 100644
index 0000000..d75185c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/ReservationDisableUser.java
@@ -0,0 +1,47 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-02-19.
+ */
+@Entity
+public class ReservationDisableUser extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String userIds;
+
+    @OneToOne
+    @JoinColumn(name = "payment_id")
+    private Payment payment;
+
+    public ReservationDisableUser() {}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(String userIds) {
+        this.userIds = userIds;
+    }
+
+    public Payment getPayment() {
+        return payment;
+    }
+
+    public void setPayment(Payment payment) {
+        this.payment = payment;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Severity.java b/src/main/java/kr/wisestone/owl/domain/Severity.java
new file mode 100644
index 0000000..4a0b915
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Severity.java
@@ -0,0 +1,85 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Created by wisestone on 2018-03-15.
+ */
+@Entity
+public class Severity extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private Integer position;
+    private String color;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    @OneToMany(mappedBy = "severity", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<Issue> issues = new HashSet<>();
+
+    public Severity(){}
+
+    public Severity(String name, Integer position, String color, Workspace workspace){
+        this.name = name;
+        this.position = position;
+        this.color = color;
+        this.workspace = workspace;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getPosition() {
+        return position;
+    }
+
+    public void setPosition(Integer position) {
+        this.position = position;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+
+    public Set<Issue> getIssues() {
+        return issues;
+    }
+
+    public void setIssues(Set<Issue> issues) {
+        this.issues = issues;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/SystemEmail.java b/src/main/java/kr/wisestone/owl/domain/SystemEmail.java
new file mode 100644
index 0000000..a6e9251
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/SystemEmail.java
@@ -0,0 +1,67 @@
+package kr.wisestone.owl.domain;
+
+import kr.wisestone.owl.domain.enumType.EmailType;
+import org.hibernate.annotations.Type;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-03-14.
+ */
+@Entity
+public class SystemEmail extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String sendAddress;
+    @Enumerated(EnumType.STRING)
+    private EmailType emailType;
+    private String parameter;
+    @Type(type = "yes_no")
+    private Boolean sendYn = Boolean.FALSE; //  N : 諛쒖넚 ��湲�, Y : 諛쒖넚 �셿猷�
+
+    public SystemEmail() {}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getSendAddress() {
+        return sendAddress;
+    }
+
+    public void setSendAddress(String sendAddress) {
+        this.sendAddress = sendAddress;
+    }
+
+    public EmailType getEmailType() {
+        return emailType;
+    }
+
+    public void setEmailType(EmailType emailType) {
+        this.emailType = emailType;
+    }
+
+    public String getParameter() {
+        return parameter;
+    }
+
+    public void setParameter(String parameter) {
+        this.parameter = parameter;
+    }
+
+    public Boolean getSendYn() {
+        return sendYn;
+    }
+
+    public void setSendYn(Boolean sendYn) {
+        this.sendYn = sendYn;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/SystemRole.java b/src/main/java/kr/wisestone/owl/domain/SystemRole.java
new file mode 100644
index 0000000..5e33cfa
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/SystemRole.java
@@ -0,0 +1,222 @@
+
+package kr.wisestone.owl.domain;
+
+
+import com.google.common.collect.Lists;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.*;
+
+@Entity
+public class SystemRole extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private String roleType;
+
+    @OneToMany(mappedBy="systemRole", cascade={CascadeType.ALL}, orphanRemoval=true)
+    private Set<SystemRoleUser> systemRoleUsers = new HashSet<>();
+
+    @OneToMany(mappedBy="systemRole", cascade={CascadeType.ALL}, orphanRemoval=true)
+    private Set<SystemRolePermission> systemRolePermissions = new HashSet<>();
+
+    public SystemRole() {
+    }
+
+    public Long getId() {
+        return this.id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Set<SystemRoleUser> getSystemRoleUsers() {
+        return this.systemRoleUsers;
+    }
+
+    public List<User> getUsers() {
+        List<User> users = Lists.newArrayList();
+
+        Iterator<SystemRoleUser> iterator = this.systemRoleUsers.iterator();
+        while (iterator.hasNext()) {
+            users.add(iterator.next().getUser());
+        }
+
+        return users;
+    }
+
+    public void setSystemRoleUsers(Set<SystemRoleUser> systemRoleUsers) {
+        this.systemRoleUsers = systemRoleUsers;
+    }
+
+    public Set<SystemRolePermission> getSystemRolePermissions() {
+        return this.systemRolePermissions;
+    }
+
+    public List<Permission> getPermissions() {
+        List<Permission> permissions = new ArrayList<Permission>();
+
+        Iterator<SystemRolePermission> iterator = this.systemRolePermissions.iterator();
+        while (iterator.hasNext()) {
+            permissions.add(iterator.next().getPermission());
+        }
+
+        return permissions;
+    }
+
+    public void setSystemRolePermissions(Set<SystemRolePermission> systemRolePermissions) {
+        this.systemRolePermissions = systemRolePermissions;
+    }
+
+    public void addSystemRolePermission(SystemRolePermission systemRolePermission) {
+        if (this.systemRolePermissions == null) {
+            this.systemRolePermissions = new HashSet<>();
+        }
+
+        this.systemRolePermissions.add(systemRolePermission);
+    }
+
+    public void removeSystemRolePermission(SystemRolePermission systemRolePermission) {
+        this.systemRolePermissions.remove(systemRolePermission);
+    }
+
+    public void addSystemRoleUser(SystemRoleUser systemRoleUser) {
+        if (this.systemRoleUsers == null) {
+            this.systemRoleUsers = new HashSet<>();
+        }
+
+        this.systemRoleUsers.add(systemRoleUser);
+    }
+
+    public String getRoleType() {
+        return roleType;
+    }
+
+    public void setRoleType(String roleType) {
+        this.roleType = roleType;
+    }
+
+    public void removeSystemRoleUser(SystemRoleUser systemRoleUser) {
+        this.systemRoleUsers.remove(systemRoleUser);
+    }
+
+    public boolean contains(User user) {
+        for (SystemRoleUser systemRoleUser : this.systemRoleUsers) {
+            if (user.getId().equals(systemRoleUser.getUser().getId())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public boolean contains(Long userId) {
+        for (SystemRoleUser systemRoleUser : this.systemRoleUsers) {
+            if (userId.equals(systemRoleUser.getUser().getId())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (this.id == null ? 0 : this.id.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (this.getClass() != obj.getClass()) {
+            return false;
+        }
+        SystemRole other = (SystemRole) obj;
+        if (this.id == null) {
+            if (other.id != null) {
+                return false;
+            }
+        } else if (!this.id.equals(other.id)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("SystemRole [");
+        if (this.id != null) {
+            builder.append("id=");
+            builder.append(this.id);
+            builder.append(", ");
+        }
+        if (this.name != null) {
+            builder.append("name=");
+            builder.append(this.name);
+            builder.append(", ");
+        }
+
+        if (this.systemRoleUsers != null) {
+            builder.append("systemRoleUsers=");
+            builder.append(this.systemRoleUsers);
+            builder.append(", ");
+        }
+        if (this.systemRolePermissions != null) {
+            builder.append("systemRolePermissions=");
+            builder.append(this.systemRolePermissions);
+        }
+        builder.append("]");
+        return builder.toString();
+    }
+
+    public void addUser(User user) {
+        if (this.systemRoleUsers == null) {
+            this.systemRoleUsers = new HashSet<>();
+        }
+
+        SystemRoleUser systemRoleUser = new SystemRoleUser();
+        systemRoleUser.setSystemRole(this);
+        systemRoleUser.setUser(user);
+
+        this.systemRoleUsers.add(systemRoleUser);
+    }
+
+    public void removeUser(User user) {
+        for (SystemRoleUser systemRoleUser : this.systemRoleUsers) {
+            if (user.getId().equals(systemRoleUser.getUser().getId())) {
+                this.systemRoleUsers.remove(systemRoleUser);
+                break;
+            }
+        }
+    }
+
+    public void addPermission(Permission permission) {
+        SystemRolePermission systemRolePermission = new SystemRolePermission();
+        systemRolePermission.setSystemRole(this);
+        systemRolePermission.setPermission(permission);
+
+        systemRolePermissions.add(systemRolePermission);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/SystemRolePermission.java b/src/main/java/kr/wisestone/owl/domain/SystemRolePermission.java
new file mode 100644
index 0000000..3c1c16d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/SystemRolePermission.java
@@ -0,0 +1,92 @@
+
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+@Entity
+public class SystemRolePermission extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch=FetchType.LAZY)
+    @JoinColumn(name="system_role_id")
+    private SystemRole systemRole;
+
+    @ManyToOne(fetch=FetchType.LAZY)
+    @JoinColumn(name="permission_id")
+    private Permission permission;
+
+    public SystemRolePermission() {
+    }
+
+    public SystemRolePermission(SystemRole systemRole, Permission permission) {
+        this.systemRole = systemRole;
+        this.permission = permission;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * @return systemRole
+     */
+    public SystemRole getSystemRole() {
+        return this.systemRole;
+    }
+
+    /**
+     * @param systemRole
+     */
+    public void setSystemRole(SystemRole systemRole) {
+        this.systemRole = systemRole;
+    }
+
+    /**
+     * @return permission
+     */
+    public Permission getPermission() {
+        return this.permission;
+    }
+
+    /**
+     * @param permission
+     */
+    public void setPermission(Permission permission) {
+        this.permission = permission;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((id == null) ? 0 : id.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        SystemRolePermission other = (SystemRolePermission) obj;
+        if (id == null) {
+            if (other.id != null)
+                return false;
+        } else if (!id.equals(other.id))
+            return false;
+        return true;
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/SystemRoleUser.java b/src/main/java/kr/wisestone/owl/domain/SystemRoleUser.java
new file mode 100644
index 0000000..46cd9da
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/SystemRoleUser.java
@@ -0,0 +1,54 @@
+package kr.wisestone.owl.domain;
+
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+@Entity
+public class SystemRoleUser extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "system_role_id")
+    private SystemRole systemRole;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "user_id")
+    private User user;
+
+    public SystemRoleUser() {
+    }
+
+    public SystemRoleUser(SystemRole systemRole, User user) {
+        this.systemRole = systemRole;
+        this.user = user;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public SystemRole getSystemRole() {
+        return systemRole;
+    }
+
+    public void setSystemRole(SystemRole systemRole) {
+        this.systemRole = systemRole;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/User.java b/src/main/java/kr/wisestone/owl/domain/User.java
new file mode 100644
index 0000000..a0e43bb
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/User.java
@@ -0,0 +1,376 @@
+package kr.wisestone.owl.domain;
+
+import kr.wisestone.owl.domain.enumType.SocialType;
+import kr.wisestone.owl.util.CommonUtil;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.*;
+
+@Entity
+public class User extends BaseEntity implements UserDetails, Principal, Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final String USER_STATUS_ACTIVE = "01";  //  �궗�슜�옄 �솢�꽦
+    public static final String USER_STATUS_DEL = "02";    //  �궗�슜�옄 �깉�눜
+    public static final String DEFAULT_PROFILE = "assets/images/default_profile.png";    //  湲곕낯 �봽濡쒗븘
+    public static final String DEFAULT_RESERVATION_NOTIFY_TIME = "09:00";    //  湲곕낯 �씠硫붿씪 �븣由� �삁�젙 �떆媛�
+    public static final String DEFAULT_LANGUAGE = "ko"; //  湲곕낯 �뼵�뼱
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private String account;
+    private String password;
+    private String status;
+    private String phone;
+    private String profile;
+    private String awsKey;
+    @Enumerated(EnumType.STRING)
+    private SocialType socialType;
+    private Long lastWorkspaceId;
+    private Long lastProjectId;
+    private Date lastLoginDate;
+    private String reservationNotifyTime;   //  �씠硫붿씪 �븣由� �떆媛� �삁�젙
+    private String language;
+    private Integer permission;
+    private String licensekey;
+
+    @OneToMany(mappedBy = "user", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<SystemRoleUser> systemRoleUsers = new HashSet<>();
+
+    @OneToMany(mappedBy = "user", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<ProjectRoleUser> projectRoleUsers = new HashSet<>();
+
+    @OneToMany(mappedBy = "user", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<IssueUser> issueUsers = new HashSet<>();
+
+    @OneToMany(mappedBy = "user", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<UserLikeIssue> userLikeIssues = new HashSet<>();
+
+    @OneToMany(mappedBy = "user", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<UserWorkspace> userWorkspaces = new HashSet<>();
+
+    @OneToMany(mappedBy = "user", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<IssueSearch> issueSearches = new HashSet<>();
+
+    @OneToMany(mappedBy = "user", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<IssueTableConfig> issueTableConfigs = new HashSet<>();
+
+    public User() {
+    }
+
+    public User(Long id, String name, String account) {
+        this.id = id;
+        this.name = name;
+        this.account = account;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getProfile() {
+        return profile;
+    }
+
+    public void setProfile(String profile) {
+        this.profile = profile;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public SocialType getSocialType() {
+        return socialType;
+    }
+
+    public void setSocialType(SocialType socialType) {
+        this.socialType = socialType;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public Long getLastWorkspaceId() {
+        return lastWorkspaceId;
+    }
+
+    public Long getLastProjectId() {return lastProjectId; }
+
+    public void setLastProjectId(Long lastProjectId) {
+        this.lastProjectId = lastProjectId;
+    }
+
+    public String getAwsKey() {
+        return awsKey;
+    }
+
+    public void setAwsKey(String awsKey) {
+        this.awsKey = awsKey;
+    }
+
+    public void setLastWorkspaceId(Long lastWorkspaceId) {
+        this.lastWorkspaceId = lastWorkspaceId;
+    }
+
+    public Set<SystemRoleUser> getSystemRoleUsers() {
+        return systemRoleUsers;
+    }
+
+    public Set<IssueSearch> getIssueSearches() {
+        return issueSearches;
+    }
+
+    public void setIssueSearches(Set<IssueSearch> issueSearches) {
+        this.issueSearches = issueSearches;
+    }
+
+    public void setSystemRoleUsers(Set<SystemRoleUser> systemRoleUsers) {
+        this.systemRoleUsers = systemRoleUsers;
+    }
+
+    public void addSystemRole(SystemRole systemRole) {
+        if (this.systemRoleUsers == null) {
+            this.systemRoleUsers = new HashSet<>();
+        }
+        SystemRoleUser systemRoleUser = new SystemRoleUser(systemRole, this);
+
+        this.systemRoleUsers.add(systemRoleUser);
+    }
+
+    public Set<ProjectRoleUser> getProjectRoleUsers() {
+        return projectRoleUsers;
+    }
+
+    public void setProjectRoleUsers(Set<ProjectRoleUser> projectRoleUsers) {
+        this.projectRoleUsers = projectRoleUsers;
+    }
+
+    public void addProjectRole(ProjectRole projectRole) {
+        if (this.projectRoleUsers == null) {
+            this.projectRoleUsers = new HashSet<>();
+        }
+        ProjectRoleUser projectRoleUser = new ProjectRoleUser(projectRole, this);
+
+        this.projectRoleUsers.add(projectRoleUser);
+    }
+
+    public void removeProjectRole(ProjectRole projectRole) {
+        Iterator<ProjectRoleUser> iterator = this.projectRoleUsers.iterator();
+
+        while (iterator.hasNext()) {
+            ProjectRoleUser projectRoleUser = iterator.next();
+            if (projectRole.getId().equals(projectRoleUser.getProjectRole().getId())) {
+                this.projectRoleUsers.remove(projectRoleUser);
+                break;
+            }
+        }
+    }
+
+    public Set<IssueUser> getIssueUsers() {
+        return issueUsers;
+    }
+
+    public void setIssueUsers(Set<IssueUser> issueUsers) {
+        this.issueUsers = issueUsers;
+    }
+
+    public void addIssue(Issue issue) {
+        if (this.issueUsers == null) {
+            this.issueUsers = new HashSet<>();
+        }
+        IssueUser issueUser = new IssueUser(issue, this);
+
+        this.issueUsers.add(issueUser);
+    }
+
+    public void removeIssue(Issue issue) {
+        Iterator<IssueUser> iterator = this.issueUsers.iterator();
+
+        while (iterator.hasNext()) {
+            IssueUser issueUser = iterator.next();
+            if (issue.getId().equals(issueUser.getIssue().getId())) {
+                this.issueUsers.remove(issueUser);
+                break;
+            }
+        }
+    }
+
+    public Set<UserWorkspace> getUserWorkspaces() {
+        return userWorkspaces;
+    }
+
+    public void setUserWorkspaces(Set<UserWorkspace> userWorkspaces) {
+        this.userWorkspaces = userWorkspaces;
+    }
+
+    public Set<UserLikeIssue> getUserLikeIssues() {
+        return userLikeIssues;
+    }
+
+    public void setUserLikeIssues(Set<UserLikeIssue> userLikeIssues) {
+        this.userLikeIssues = userLikeIssues;
+    }
+
+    public String getReservationNotifyTime() {
+        return reservationNotifyTime;
+    }
+
+    public void setReservationNotifyTime(String reservationNotifyTime) {
+        this.reservationNotifyTime = reservationNotifyTime;
+    }
+
+    public Set<IssueTableConfig> getIssueTableConfigs() {
+        return issueTableConfigs;
+    }
+
+    public void setIssueTableConfigs(Set<IssueTableConfig> issueTableConfigs) {
+        this.issueTableConfigs = issueTableConfigs;
+    }
+
+    public String getLanguage() {
+        return language;
+    }
+
+    public void setLanguage(String language) {
+        this.language = language;
+    }
+
+    public Integer getPermission() {
+        return permission;
+    }
+
+    public void setPermission(Integer permission) {
+        this.permission = permission;
+    }
+
+    public String getLicensekey() {
+        return licensekey;
+    }
+
+    public void setLicensekey(String licensekey) {
+        this.licensekey = licensekey;
+    }
+
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public String getUsername() {
+        return this.account;
+    }
+
+    @Override
+    public boolean isAccountNonExpired() {
+        return true;
+    }
+
+    @Override
+    public boolean isAccountNonLocked() {
+        return true;
+    }
+
+    @Override
+    public boolean isCredentialsNonExpired() {
+        return true;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((this.account == null) ? 0 : this.account.hashCode());
+        result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        User other = (User) obj;
+        if (this.account == null) {
+            if (other.account != null) {
+                return false;
+            }
+        }
+        else if (!this.account.equals(other.account)) {
+            return false;
+        }
+        if (this.id == null) {
+            if (other.id != null) {
+                return false;
+            }
+        }
+        else if (!this.id.equals(other.id)) {
+            return false;
+        }
+        return true;
+    }
+
+    public void setLastLoginDate(Date date) { this.lastLoginDate = date; }
+    public Date getLastLoginDate() { return  this.lastLoginDate; }
+
+    /*@Override
+    public String toString() {
+        return CommonUtil.decryptAES128(this.account);
+    }*/
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/UserHistory.java b/src/main/java/kr/wisestone/owl/domain/UserHistory.java
new file mode 100644
index 0000000..ca7c25a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/UserHistory.java
@@ -0,0 +1,30 @@
+package kr.wisestone.owl.domain;
+
+import kr.wisestone.owl.domain.enumType.SocialType;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.security.Principal;
+
+@Entity
+public class UserHistory extends BaseEntity implements Serializable {
+
+    public static final String HISTORY_LOGIN = "LOGIN";
+    public static final String HISTORY_LOGOUT = "LOGOUT";
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String historyType;
+
+    public Long getId(){ return  this.id; }
+
+    public void setId(Long id) { this.id = id; }
+
+    public String getHistoryType() { return  this.historyType; }
+
+    public void setHistoryType(String historyType) { this.historyType = historyType; }
+
+    public UserHistory() { }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/UserInvite.java b/src/main/java/kr/wisestone/owl/domain/UserInvite.java
new file mode 100644
index 0000000..50309f4
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/UserInvite.java
@@ -0,0 +1,89 @@
+package kr.wisestone.owl.domain;
+
+import com.google.common.collect.Lists;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Created by wisestone on 2018-03-14.
+ */
+@Entity
+public class UserInvite extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static final String WORKSPACE_JOIN_READY = "01";     //  珥덈�以�
+    public static final String WORKSPACE_JOIN_COMPLETE = "02";  //  媛��엯 �셿猷�
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String email;
+    private String status;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    @OneToMany(mappedBy="userInvite", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<UserInviteProject> userInviteProjects = new HashSet<>();
+
+    public UserInvite() {
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public Set<UserInviteProject> getUserInviteProjects() {
+        return userInviteProjects;
+    }
+
+    public void setUserInviteProjects(Set<UserInviteProject> userInviteProjects) {
+        this.userInviteProjects = userInviteProjects;
+    }
+
+    public void removeUserInviteProject() {
+        Iterator<UserInviteProject> iterator = this.userInviteProjects.iterator();
+
+        while (iterator.hasNext()) {
+            this.userInviteProjects.remove(iterator.next());
+        }
+    }
+
+    public void addUserInviteProjects(UserInviteProject userInviteProject) {
+        this.userInviteProjects.add(userInviteProject);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/UserInviteProject.java b/src/main/java/kr/wisestone/owl/domain/UserInviteProject.java
new file mode 100644
index 0000000..638120d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/UserInviteProject.java
@@ -0,0 +1,55 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-03-15.
+ */
+@Entity
+public class UserInviteProject extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="user_invite_id")
+    private UserInvite userInvite;
+
+    @ManyToOne(fetch=FetchType.LAZY)
+    @JoinColumn(name="project_id")
+    private Project project;
+
+    public UserInviteProject(){}
+
+    public UserInviteProject(UserInvite userInvite, Project project){
+        this.userInvite = userInvite;
+        this.project = project;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public UserInvite getUserInvite() {
+        return userInvite;
+    }
+
+    public void setUserInvite(UserInvite userInvite) {
+        this.userInvite = userInvite;
+    }
+
+    public Project getProject() {
+        return project;
+    }
+
+    public void setProject(Project project) {
+        this.project = project;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/UserLikeIssue.java b/src/main/java/kr/wisestone/owl/domain/UserLikeIssue.java
new file mode 100644
index 0000000..96beb3c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/UserLikeIssue.java
@@ -0,0 +1,62 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-03-07.
+ */
+@Entity
+public class UserLikeIssue extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "user_id")
+    private User user;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "issue_id")
+    private Issue issue;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    public UserLikeIssue(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public Issue getIssue() {
+        return issue;
+    }
+
+    public void setIssue(Issue issue) {
+        this.issue = issue;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/UserWithDraw.java b/src/main/java/kr/wisestone/owl/domain/UserWithDraw.java
new file mode 100644
index 0000000..0309e32
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/UserWithDraw.java
@@ -0,0 +1,42 @@
+package kr.wisestone.owl.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-11-19.
+ */
+@Entity
+public class UserWithDraw extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String account;
+
+    public UserWithDraw(){}
+
+    public UserWithDraw(String account){
+        this.account = account;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/UserWorkspace.java b/src/main/java/kr/wisestone/owl/domain/UserWorkspace.java
new file mode 100644
index 0000000..273ea6e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/UserWorkspace.java
@@ -0,0 +1,91 @@
+package kr.wisestone.owl.domain;
+
+import org.hibernate.annotations.Type;
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-02-13.
+ */
+@Entity
+public class UserWorkspace extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch= FetchType.EAGER)  //  �뒪耳�伊대윭�뿉�꽌 硫붿씪 �쟾�넚�쓣 �쐞�빐 利됱떆 濡쒕뵫 �쟻�슜
+    @JoinColumn(name="user_id")
+    private User user;
+
+    @ManyToOne(fetch=FetchType.EAGER)   //  �뒪耳�伊대윭�뿉�꽌 硫붿씪 �쟾�넚�쓣 �쐞�빐 利됱떆 濡쒕뵫 �쟻�슜
+    @JoinColumn(name="workspace_id")
+    private Workspace workspace;
+
+    @Type(type = "yes_no")
+    private Boolean managerYn = Boolean.FALSE;
+
+    @Type(type = "yes_no")
+    private Boolean useYn = Boolean.FALSE;
+
+    private Long disablePosition;
+
+    public UserWorkspace() {}
+
+    public UserWorkspace(User user, Workspace workspace, Boolean managerYn, Boolean useYn, Long disablePosition) {
+        this.user = user;
+        this.workspace = workspace;
+        this.managerYn = managerYn;
+        this.useYn = useYn;
+        this.disablePosition = disablePosition;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public Boolean getManagerYn() {
+        return managerYn;
+    }
+
+    public void setManagerYn(Boolean managerYn) {
+        this.managerYn = managerYn;
+    }
+
+    public Boolean getUseYn() {
+        return useYn;
+    }
+
+    public void setUseYn(Boolean useYn) {
+        this.useYn = useYn;
+    }
+
+    public Long getDisablePosition() {
+        return disablePosition;
+    }
+
+    public void setDisablePosition(Long disablePosition) {
+        this.disablePosition = disablePosition;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Workflow.java b/src/main/java/kr/wisestone/owl/domain/Workflow.java
new file mode 100644
index 0000000..5fb61c2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Workflow.java
@@ -0,0 +1,92 @@
+package kr.wisestone.owl.domain;
+
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Created by wisestone on 2018-03-07.
+ */
+@Entity
+public class Workflow extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private String description;
+
+    @Enumerated(EnumType.STRING)
+    private ProjectType projectType;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workspace_id")
+    private Workspace workspace;
+
+    @OneToMany(mappedBy = "workflow", cascade = {CascadeType.ALL})
+    private Set<IssueType> issueTypes = new HashSet<>();
+
+    @OneToMany(mappedBy = "workflow", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<WorkflowTransition> workflowTransitions = new HashSet<>();
+
+    public Workflow(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public void setWorkspace(Workspace workspace) {
+        this.workspace = workspace;
+    }
+
+    public Set<IssueType> getIssueTypes() {
+        return issueTypes;
+    }
+
+    public void setIssueTypes(Set<IssueType> issueTypes) {
+        this.issueTypes = issueTypes;
+    }
+
+    public Set<WorkflowTransition> getWorkflowTransitions() {
+        return workflowTransitions;
+    }
+
+    public void setWorkflowTransitions(Set<WorkflowTransition> workflowTransitions) {
+        this.workflowTransitions = workflowTransitions;
+    }
+
+    public ProjectType getProjectType() {
+        return projectType;
+    }
+
+    public void setProjectType(ProjectType projectType) {
+        this.projectType = projectType;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/WorkflowStatus.java b/src/main/java/kr/wisestone/owl/domain/WorkflowStatus.java
new file mode 100644
index 0000000..6034770
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/WorkflowStatus.java
@@ -0,0 +1,119 @@
+package kr.wisestone.owl.domain;
+
+import org.hibernate.annotations.Type;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Created by wisestone on 2018-01-03.
+ */
+@Entity
+public class WorkflowStatus extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy=GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    @Type(type="yes_no")
+    private Boolean firstYn = Boolean.FALSE;
+    @Type(type="yes_no")
+    private Boolean lastYn = Boolean.FALSE;
+    private String color;
+    private Long progress;
+    private Long position;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="project_id")
+    private Project project;
+
+    @OneToMany(mappedBy="workflowStatus", cascade={CascadeType.ALL}, orphanRemoval=true)
+    private Set<Issue> tasks = new HashSet<Issue>();
+
+    public WorkflowStatus() {}
+
+    public WorkflowStatus(Project project, String name, Boolean firstYn, Boolean lastYn, String color, Long progress, Long position) {
+        this.project = project;
+        this.name = name;
+        this.firstYn = firstYn;
+        this.lastYn = lastYn;
+        this.color = color;
+        this.progress = progress;
+        this.position = position;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Boolean getFirstYn() {
+        return firstYn;
+    }
+
+    public void setFirstYn(Boolean firstYn) {
+        this.firstYn = firstYn;
+    }
+
+    public Boolean getLastYn() {
+        return lastYn;
+    }
+
+    public void setLastYn(Boolean lastYn) {
+        this.lastYn = lastYn;
+    }
+
+    public Long getProgress() {
+        return progress;
+    }
+
+    public void setProgress(Long progress) {
+        this.progress = progress;
+    }
+
+    public Long getPosition() {
+        return position;
+    }
+
+    public void setPosition(Long position) {
+        this.position = position;
+    }
+
+    public Project getProject() {
+        return project;
+    }
+
+    public void setProject(Project project) {
+        this.project = project;
+    }
+
+    public Set<Issue> getTasks() {
+        return tasks;
+    }
+
+    public void setTasks(Set<Issue> tasks) {
+        this.tasks = tasks;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/WorkflowTransition.java b/src/main/java/kr/wisestone/owl/domain/WorkflowTransition.java
new file mode 100644
index 0000000..650d2c1
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/WorkflowTransition.java
@@ -0,0 +1,154 @@
+package kr.wisestone.owl.domain;
+
+import org.hibernate.annotations.Type;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * Created by wisestone on 2018-03-07.
+ */
+@Entity
+public class WorkflowTransition extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "workflow_id")
+    private Workflow workflow;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "source_issue_status_id")
+    private IssueStatus sourceIssueStatus;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "target_issue_status_id")
+    private IssueStatus targetIssueStatus;
+
+    private Long sourceX;
+    private Long sourceY;
+    private Long targetX;
+    private Long targetY;
+    private Long correctX;
+    private Long correctY;
+    @Type(type="yes_no")
+    private Boolean direct = Boolean.FALSE;
+
+    public WorkflowTransition(){}
+
+    public WorkflowTransition(Workflow workflow, IssueStatus sourceIssueStatus, IssueStatus targetIssueStatus, Long sourceX, Long sourceY, Long targetX, Long targetY, Long correctX, Long correctY){
+        this.workflow = workflow;
+        this.sourceIssueStatus = sourceIssueStatus;
+        this.targetIssueStatus = targetIssueStatus;
+        this.sourceX = sourceX;
+        this.sourceY = sourceY;
+        this.targetX = targetX;
+        this.targetY = targetY;
+        this.correctX = correctX;
+        this.correctY = correctY;
+    }
+
+    public WorkflowTransition(Workflow workflow, IssueStatus sourceIssueStatus, IssueStatus targetIssueStatus, Long sourceX, Long sourceY, Long targetX, Long targetY, Long correctX, Long correctY, Boolean direct){
+        this.workflow = workflow;
+        this.sourceIssueStatus = sourceIssueStatus;
+        this.targetIssueStatus = targetIssueStatus;
+        this.sourceX = sourceX;
+        this.sourceY = sourceY;
+        this.targetX = targetX;
+        this.targetY = targetY;
+        this.correctX = correctX;
+        this.correctY = correctY;
+        this.direct = direct;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Workflow getWorkflow() {
+        return workflow;
+    }
+
+    public void setWorkflow(Workflow workflow) {
+        this.workflow = workflow;
+    }
+
+    public IssueStatus getSourceIssueStatus() {
+        return sourceIssueStatus;
+    }
+
+    public void setSourceIssueStatus(IssueStatus sourceIssueStatus) {
+        this.sourceIssueStatus = sourceIssueStatus;
+    }
+
+    public IssueStatus getTargetIssueStatus() {
+        return targetIssueStatus;
+    }
+
+    public void setTargetIssueStatus(IssueStatus targetIssueStatus) {
+        this.targetIssueStatus = targetIssueStatus;
+    }
+
+    public Long getSourceX() {
+        return sourceX;
+    }
+
+    public void setSourceX(Long sourceX) {
+        this.sourceX = sourceX;
+    }
+
+    public Long getSourceY() {
+        return sourceY;
+    }
+
+    public void setSourceY(Long sourceY) {
+        this.sourceY = sourceY;
+    }
+
+    public Long getTargetX() {
+        return targetX;
+    }
+
+    public void setTargetX(Long targetX) {
+        this.targetX = targetX;
+    }
+
+    public Long getTargetY() {
+        return targetY;
+    }
+
+    public void setTargetY(Long targetY) {
+        this.targetY = targetY;
+    }
+
+    public Long getCorrectX() {
+        return correctX;
+    }
+
+    public void setCorrectX(Long correctX) {
+        this.correctX = correctX;
+    }
+
+    public Long getCorrectY() {
+        return correctY;
+    }
+
+    public void setCorrectY(Long correctY) {
+        this.correctY = correctY;
+    }
+
+    public Boolean getDirect() {
+        return direct;
+    }
+
+    public void setDirect(Boolean direct) {
+        this.direct = direct;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/Workspace.java b/src/main/java/kr/wisestone/owl/domain/Workspace.java
new file mode 100644
index 0000000..b22e045
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/Workspace.java
@@ -0,0 +1,343 @@
+package kr.wisestone.owl.domain;
+
+
+import kr.wisestone.owl.domain.enumType.ServiceType;
+import kr.wisestone.owl.util.DateUtil;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+
+/**
+ * Created by wisestone on 2018-02-13.
+ */
+@Entity
+public class Workspace extends BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+    private static final Long DEFAULT_FREE_ALLOCATE_DISK = 10737418240L;
+    private static final Long DEFAULT_USER_ALLOCATE_DISK = 3221225472L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private Integer maxUser;
+    private Date startDate;
+    private Date expireDate;
+    private Long storageSize = 0L;
+    private Long useTraffic = 0L;    //  �듃�젅�뵿 �궗�슜�웾 痢≪젙
+    @Enumerated(EnumType.STRING)
+    private ServiceType serviceType;    //  �궗�슜�옄媛� �꽌鍮꾩뒪瑜� 痍⑥냼�븷 寃쎌슦�뿉 UNUSED 濡� 媛믪씠 蹂�寃쎈맂�떎. - �씪諛섏쟻�씤 寃쎌슦�뿉�뒗 USE 媛믪씠�떎.
+
+    @OneToOne(mappedBy = "workspace", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Payment payment;
+
+    @OneToMany(mappedBy = "workspace", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<AttachedFile> attachedFiles = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<Project> projects = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<CustomField> customFields = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<IssueType> issueTypes = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<Workflow> workflows = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<IssueStatus> issueStatuses = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<UserWorkspace> userWorkspaces = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<UserInvite> userInvites = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<PaymentHistory> paymentHistories = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<IssueUser> issueUsers = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<IssueRisk> issueRisks = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<IssueComment> issueComments = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<UserLikeIssue> userLikeIssues = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = { CascadeType.ALL }, orphanRemoval = true)
+    private Set<IssueSearch> issueSearches = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<IssueTableConfig> issueTableConfigs = new HashSet<>();
+
+    @OneToMany(mappedBy = "workspace", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    private Set<Priority> priorities = new HashSet<>();
+
+
+
+    public Workspace() {
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getMaxUser() {
+        return maxUser;
+    }
+
+    public void setMaxUser(Integer maxUser) {
+        this.maxUser = maxUser;
+    }
+
+    public Date getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(Date startDate) {
+        this.startDate = startDate;
+    }
+
+    public Date getExpireDate() {
+        return expireDate;
+    }
+
+    public void setExpireDate(Date expireDate) {
+        this.expireDate = expireDate;
+    }
+
+    public Long getStorageSize() {
+        return storageSize;
+    }
+
+    public void setStorageSize(Long storageSize) {
+        this.storageSize = storageSize;
+    }
+
+    public Payment getPayment() {
+        return payment;
+    }
+
+    public void setPayment(Payment payment) {
+        this.payment = payment;
+    }
+
+    public Set<PaymentHistory> getPaymentHistories() {
+        return paymentHistories;
+    }
+
+    public void setPaymentHistories(Set<PaymentHistory> paymentHistories) {
+        this.paymentHistories = paymentHistories;
+    }
+
+    public Long calculateStorageSize() {
+        //  10紐� �씠�븯�뒗 5GB �젣怨�
+        if (this.maxUser < 11) {
+            return DEFAULT_FREE_ALLOCATE_DISK;
+        }
+
+        //  10紐� �씠�긽�� 10GB + �궗�슜�옄�닔 * 3湲곌� �젣怨�
+        return DEFAULT_FREE_ALLOCATE_DISK + (this.maxUser * DEFAULT_USER_ALLOCATE_DISK);
+    }
+
+    public Set<Project> getProjects() {
+        return projects;
+    }
+
+    public void setProjects(Set<Project> projects) {
+        this.projects = projects;
+    }
+
+    public Set<IssueStatus> getIssueStatuses() {
+        return issueStatuses;
+    }
+
+    public void setIssueStatuses(Set<IssueStatus> issueStatuses) {
+        this.issueStatuses = issueStatuses;
+    }
+
+    public Set<AttachedFile> getAttachedFiles() {
+        return attachedFiles;
+    }
+
+    public void setAttachedFiles(Set<AttachedFile> attachedFiles) {
+        this.attachedFiles = attachedFiles;
+    }
+
+    public Set<UserWorkspace> getUserWorkspaces() {
+        return userWorkspaces;
+    }
+
+    public void setUserWorkspaces(Set<UserWorkspace> userWorkspaces) {
+        this.userWorkspaces = userWorkspaces;
+    }
+
+    public Set<UserInvite> getUserInvites() {
+        return userInvites;
+    }
+
+    public void setUserInvites(Set<UserInvite> userInvites) {
+        this.userInvites = userInvites;
+    }
+
+    public Set<IssueType> getIssueTypes() {
+        return issueTypes;
+    }
+
+    public void setIssueTypes(Set<IssueType> issueTypes) {
+        this.issueTypes = issueTypes;
+    }
+
+    public Set<Workflow> getWorkflows() {
+        return workflows;
+    }
+
+    public void setWorkflows(Set<Workflow> workflows) {
+        this.workflows = workflows;
+    }
+
+    public Set<CustomField> getCustomFields() {
+        return customFields;
+    }
+
+    public void setCustomFields(Set<CustomField> customFields) {
+        this.customFields = customFields;
+    }
+
+    public ServiceType getServiceType() {
+        return serviceType;
+    }
+
+    public void setServiceType(ServiceType serviceType) {
+        this.serviceType = serviceType;
+    }
+
+    public Set<IssueUser> getIssueUsers() {
+        return issueUsers;
+    }
+
+    public void setIssueUsers(Set<IssueUser> issueUsers) {
+        this.issueUsers = issueUsers;
+    }
+
+    public Set<IssueRisk> getIssueRisks() {
+        return issueRisks;
+    }
+
+    public void setIssueRisks(Set<IssueRisk> issueRisks) {
+        this.issueRisks = issueRisks;
+    }
+
+    public Set<IssueComment> getIssueComments() {
+        return issueComments;
+    }
+
+    public void setIssueComments(Set<IssueComment> issueComments) {
+        this.issueComments = issueComments;
+    }
+
+    public Set<UserLikeIssue> getUserLikeIssues() {
+        return userLikeIssues;
+    }
+
+    public void setUserLikeIssues(Set<UserLikeIssue> userLikeIssues) {
+        this.userLikeIssues = userLikeIssues;
+    }
+
+    public Long getUseTraffic() {
+        return useTraffic;
+    }
+
+    public void setUseTraffic(Long useTraffic) {
+        this.useTraffic = useTraffic;
+    }
+
+    public Set<IssueSearch> getIssueSearches() {
+        return issueSearches;
+    }
+
+    public void setIssueSearches(Set<IssueSearch> issueSearches) {
+        this.issueSearches = issueSearches;
+    }
+
+    public Set<IssueTableConfig> getIssueTableConfigs() {
+        return issueTableConfigs;
+    }
+
+    public void setIssueTableConfigs(Set<IssueTableConfig> issueTableConfigs) {
+        this.issueTableConfigs = issueTableConfigs;
+    }
+
+    public Set<Priority> getPriorities() {
+        return priorities;
+    }
+
+    public void setPriorities(Set<Priority> priorities) {
+        this.priorities = priorities;
+    }
+
+    public String makeCustomerUid(String account) {
+        account = account.replaceAll(".", "_");
+
+        MessageDigest messageDigest = null;
+
+        try {
+            messageDigest = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+
+        if (messageDigest != null) {
+            byte[] digest = messageDigest.digest((account + this.getId()).getBytes());
+            BigInteger bigInt = new BigInteger(1, digest);
+            return bigInt.toString(16);
+        }
+        else {
+            return account + this.getId();
+        }
+    }
+
+    //  OWL ITS 寃곗젣�뒗 留ㅻ떖�씪 寃쎌슦 30�씪, 1�뀈�씪 寃쎌슦 365�씪濡� �븳�떎.
+    public Date calculateNextPaymentDate(String decisionType, Date expireDate) {
+        Date toDay = new Date();
+
+        if (Payment.MONTH.equals(decisionType)) {
+            int compare = toDay.compareTo(expireDate);
+
+            if (compare > 0) {
+                //  �삤�뒛 �궇吏쒓� �뜑 �겢 �븣 - �븳�룞�븞 �궗�슜�븞�븯�떎媛� 寃곗젣�븳 寃쎌슦
+                return DateUtil.addDays(toDay, 30);
+            }
+            else {
+                //  留뚮즺 �궇吏쒓� 媛숆굅�굹 �뜑 �겢 �븣 - 留뚮즺�씪�뿉 寃곗젣�뻽嫄곕굹 臾대즺 �궗�슜湲곌컙�뿉 寃곗젣�뻽�쓣 �븣
+                return DateUtil.addDays(expireDate, 30);
+            }
+        }
+        else {
+            //  留ㅻ뀈 寃곗젣
+            return DateUtil.addDays(expireDate, 365);
+        }
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/enumType/AttachedType.java b/src/main/java/kr/wisestone/owl/domain/enumType/AttachedType.java
new file mode 100644
index 0000000..96cab2a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/enumType/AttachedType.java
@@ -0,0 +1,10 @@
+package kr.wisestone.owl.domain.enumType;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-02
+ */
+public enum AttachedType {
+    SUMMER, //  �꽟癒몃끂�듃�뿉�꽌 �뾽濡쒕뱶
+    ISSUE_ATTACHED, //  �씠�뒋�뿉�꽌 �뾽濡쒕뱶
+    TEMP_SUMMER //  �꽟癒몃끂�듃�뿉�꽌 �뾽濡쒕뱶 �뻽�쑝�굹 �씠�뒋�뿉 �뿰寃곕릺吏� �븡�� 寃쎌슦
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/enumType/CustomFieldType.java b/src/main/java/kr/wisestone/owl/domain/enumType/CustomFieldType.java
new file mode 100644
index 0000000..dce48b7
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/enumType/CustomFieldType.java
@@ -0,0 +1,10 @@
+package kr.wisestone.owl.domain.enumType;
+
+/**
+ * Created by wisestone on 2018-05-28.
+ */
+public enum CustomFieldType {
+    INPUT,
+    MULTI_SELECT,
+    SINGLE_SELECT
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/enumType/EmailType.java b/src/main/java/kr/wisestone/owl/domain/enumType/EmailType.java
new file mode 100644
index 0000000..75d8b6b
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/enumType/EmailType.java
@@ -0,0 +1,42 @@
+package kr.wisestone.owl.domain.enumType;
+
+/**
+ * Created by wisestone on 2018-03-14.
+ */
+public enum EmailType {
+    WORKSPACE_JOIN, //  �쉶�썝 媛��엯
+    USER_SEARCH_PASSWORD, //  �궗�슜�옄 鍮꾨�踰덊샇 李얘린
+    USER_WITH_DRAW, //  �쉶�썝 �깉�눜
+    REGULAR_PAYMENT,   //  �젙湲� 寃곗젣 �셿猷�
+    REGULAR_PAYMENT_CANCEL, //  �젙湲� 寃곗젣 痍⑥냼
+    REGULAR_PAYMENT_CANCEL_BY_ACCOUNTING_MANAGER, //  �젙湲� 寃곗젣 痍⑥냼 �쉶怨� �떞�떦�옄�뿉寃� �븣由�
+    REGULAR_PAYMENT_MODIFY, //  �젙湲� 寃곗젣 蹂�寃�
+    WORKSPACE_INVITE_NEW_USER,  //  �떊洹� �궗�슜�옄 珥덈�
+    WORKSPACE_MAX_USER_EXCESS,  //  �뾽臾� 怨듦컙 �궗�슜�옄 珥덇낵
+    WORKSPACE_MAX_STORAGE_EXCESS,   //  �뾽臾� 怨듦컙 ���옣 怨듦컙 遺�議�
+    USER_JOIN_STATISTICS,   //  �궗�슜�옄 �쁽�솴 �젙蹂�
+    TOTAL_STATISTICS,   //  �쟾泥� �떆�뒪�뀥 �쁽�솴 �젙蹂�
+    WORKSPACE_EXPIRE,   //  �뾽臾� 怨듦컙 �궗�슜 湲곌컙 留뚮즺
+    WORKSPACE_EXPIRE_ALARM,   //  �뾽臾� 怨듦컙 �궗�슜 湲곌컙 留뚮즺 �삁�젙 �븣由�
+
+
+
+    WORKSPACE_INVITE_SYSTEM_USER,   //  湲곗〈 �궗�슜�옄 珥덈�
+    PROJECT_DEFAULT_EXCLUDE,    //  �봽濡쒖젥�듃�쓽 �씪諛섏궗�슜�옄�뿉�꽌 �젣�쇅
+    PROJECT_DEFAULT_INCLUDE,    //  �봽濡쒖젥�듃�뿉 �씪諛� �궗�슜�옄濡� 李몄뿬
+    PROJECT_MANAGER_EXCLUDE,    //  �봽濡쒖젥�듃�뿉�꽌 愿�由ъ옄 �젣�쇅
+    PROJECT_MANAGER_INCLUDE,    //  �봽濡쒖젥�듃�뿉 愿�由ъ옄濡� 李몄뿬
+    PROJECT_MANAGER_EXCLUDE_AND_PROJECT_DEFAULT_INCLUDE,    //  �봽濡쒖젥�듃 愿�由ъ옄 �젣�쇅 �썑 �씪諛� �궗�슜�옄濡� 李몄뿬
+    PROJECT_DEFAULT_EXCLUDE_AND_PROJECT_MANAGER_INCLUDE,    //  �씪諛� �궗�슜�옄�뿉�꽌 �젣�쇅 �썑 �봽濡쒖젥�듃 愿�由ъ옄濡� 李몄뿬
+    ISSUE_ADD,  //  �씠�뒋 �깮�꽦
+    ISSUE_MODIFY,   //  �씠�뒋 �닔�젙
+    ISSUE_REMOVE,   //  �씠�뒋 �궘�젣
+    ISSUE_SEND, //  �씠�뒋 �젙蹂� �씠硫붿씪濡� �쟾�떖
+    PROJECT_ADD,    //  �봽濡쒖젥�듃 �깮�꽦
+    PROJECT_MODIFY, //  �봽濡쒖젥�듃 �닔�젙
+    PROJECT_REMOVE, //  �봽濡쒖젥�듃 �궘�젣
+    ISSUE_ASSIGNEE_INCLUDE,  //  �씠�뒋 �떞�떦�옄 吏��젙
+    ISSUE_ASSIGNEE_EXCLUDE, //  �씠�뒋 �떞�떦�옄 �젣�쇅
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/enumType/FileType.java b/src/main/java/kr/wisestone/owl/domain/enumType/FileType.java
new file mode 100644
index 0000000..8071a85
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/enumType/FileType.java
@@ -0,0 +1,11 @@
+package kr.wisestone.owl.domain.enumType;
+
+/**
+ * Created by wisestone on 2018-10-11.
+ */
+public enum FileType {
+    IMAGE,  //  �씠誘몄�
+    MEDIA,  //  誘몃뵒�뼱
+    DOC,    //  臾몄꽌
+    ETC //  湲고�
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/enumType/IssueHistoryType.java b/src/main/java/kr/wisestone/owl/domain/enumType/IssueHistoryType.java
new file mode 100644
index 0000000..98e90b4
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/enumType/IssueHistoryType.java
@@ -0,0 +1,11 @@
+package kr.wisestone.owl.domain.enumType;
+
+/**
+ * Created by wisestone on 2018-10-08.
+ */
+public enum IssueHistoryType {
+    ADD,
+    MODIFY,
+    DELETE,
+    TOTAL
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/enumType/IssueModifyType.java b/src/main/java/kr/wisestone/owl/domain/enumType/IssueModifyType.java
new file mode 100644
index 0000000..313997d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/enumType/IssueModifyType.java
@@ -0,0 +1,16 @@
+package kr.wisestone.owl.domain.enumType;
+
+/**
+ * Created by wisestone on 2018-10-10.
+ */
+public enum IssueModifyType {
+    SEVERITY,
+    PRIORITY,
+    ISSUE_STATUS,
+    ISSUE_TYPE,
+    PERIOD,
+    ASSIGNEE,
+    CUSTOM_FIELD,
+    TITLE,
+    DESCRIPTION
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/enumType/IssueReservationType.java b/src/main/java/kr/wisestone/owl/domain/enumType/IssueReservationType.java
new file mode 100644
index 0000000..732a112
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/enumType/IssueReservationType.java
@@ -0,0 +1,11 @@
+package kr.wisestone.owl.domain.enumType;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-07
+ */
+public enum IssueReservationType {
+    DAY,
+    WEEK,
+    MONTH,
+    YEAR
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/enumType/IssueStatusType.java b/src/main/java/kr/wisestone/owl/domain/enumType/IssueStatusType.java
new file mode 100644
index 0000000..0874c30
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/enumType/IssueStatusType.java
@@ -0,0 +1,10 @@
+package kr.wisestone.owl.domain.enumType;
+
+/**
+ * Created by wisestone on 2018-03-09.
+ */
+public enum IssueStatusType {
+    READY,
+    OPEN,
+    CLOSE
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/enumType/ProjectType.java b/src/main/java/kr/wisestone/owl/domain/enumType/ProjectType.java
new file mode 100644
index 0000000..0172eb0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/enumType/ProjectType.java
@@ -0,0 +1,10 @@
+package kr.wisestone.owl.domain.enumType;
+
+/**
+ * Created by wisestone on 2018-02-23.
+ */
+public enum ProjectType {
+    RMS_PROJECT,
+    BTS_PROJECT,
+    TCM_PROJECT
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/enumType/ServiceType.java b/src/main/java/kr/wisestone/owl/domain/enumType/ServiceType.java
new file mode 100644
index 0000000..cd3f800
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/enumType/ServiceType.java
@@ -0,0 +1,9 @@
+package kr.wisestone.owl.domain.enumType;
+
+/**
+ * Created by wisestone on 2018-10-05.
+ */
+public enum ServiceType {
+    USE,    //  �꽌鍮꾩뒪 �궗�슜以�
+    UNUSED, //  �꽌鍮꾩뒪 留뚮즺
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/enumType/SocialType.java b/src/main/java/kr/wisestone/owl/domain/enumType/SocialType.java
new file mode 100644
index 0000000..cc1a7d5
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/enumType/SocialType.java
@@ -0,0 +1,11 @@
+package kr.wisestone.owl.domain.enumType;
+
+/**
+ * Created by wisestone on 2018-02-21.
+ */
+public enum SocialType {
+    GOOGLE,
+    NAVER,
+    KAKAO,
+    FACEBOOK
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/interceptor/AuditLogInterceptor.java b/src/main/java/kr/wisestone/owl/domain/interceptor/AuditLogInterceptor.java
new file mode 100644
index 0000000..a37dc7e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/interceptor/AuditLogInterceptor.java
@@ -0,0 +1,80 @@
+package kr.wisestone.owl.domain.interceptor;
+
+import kr.wisestone.owl.domain.BaseEntity;
+import kr.wisestone.owl.util.ApplicationContextUtil;
+import kr.wisestone.owl.util.WebAppUtil;
+import org.hibernate.CallbackException;
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.type.Type;
+import java.io.Serializable;
+import java.util.Date;
+
+public class AuditLogInterceptor extends EmptyInterceptor {
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState,
+            Object[] previousState, String[] propertyNames, Type[] types) throws CallbackException {
+        this.setModifyInfoToEntity(entity, currentState, propertyNames);
+
+        return true;
+    }
+    
+    @Override
+    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames,
+            Type[] types) throws CallbackException {
+
+        this.setRegisterInfo(entity, state, propertyNames);
+
+        return true;
+    }
+    
+    private void setModifyInfoToEntity(Object entity, Object[] state, String[] propertyNames) {
+        if (entity instanceof BaseEntity) {
+            Long loginId = this.getUserId();
+            if (loginId == null) {
+                loginId = Long.valueOf(1);
+            }
+
+            for (int i = 0; i < propertyNames.length; i++) {
+                if ("modifyId".equals(propertyNames[i])) {
+                    state[i] = loginId;
+                } else if ("modifyDate".equals(propertyNames[i])) {
+                    state[i] = new Date();
+                }
+            }
+        }
+    }
+    
+    private void setRegisterInfo(Object entity, Object[] state, String[] propertyNames) {
+        if (entity instanceof BaseEntity) {
+            Long loginId = this.getUserId();
+            if (loginId == null) {
+                loginId = Long.valueOf(1);
+            }
+
+            Date currentDate = new Date();
+
+            for (int i = 0; i < propertyNames.length; i++) {
+                if ("registerId".equals(propertyNames[i])) {
+                    state[i] = loginId;
+                } else if ("modifyId".equals(propertyNames[i])) {
+                    state[i] = loginId;
+                } else if ("registerDate".equals(propertyNames[i])) {
+                    state[i] = currentDate;
+                } else if ("modifyDate".equals(propertyNames[i])) {
+                    state[i] = currentDate;
+                }
+            }
+        }
+    }
+    
+    private Long getUserId() {
+        WebAppUtil webAppUtil = ApplicationContextUtil.getBean("webAppUtil", WebAppUtil.class);
+        if (webAppUtil == null) {
+            return null;
+        }
+
+        return webAppUtil.getLoginId();
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/strategy/PrefixNamingStrategy.java b/src/main/java/kr/wisestone/owl/domain/strategy/PrefixNamingStrategy.java
new file mode 100644
index 0000000..e79a303
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/strategy/PrefixNamingStrategy.java
@@ -0,0 +1,47 @@
+package kr.wisestone.owl.domain.strategy;
+
+import org.apache.commons.lang3.StringUtils;
+import org.hibernate.boot.model.naming.Identifier;
+import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
+import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
+
+public class PrefixNamingStrategy implements PhysicalNamingStrategy {
+
+    @Override
+    public Identifier toPhysicalCatalogName(Identifier identifier, JdbcEnvironment jdbcEnv) {
+        return convert(identifier);
+    }
+
+    @Override
+    public Identifier toPhysicalColumnName(Identifier identifier, JdbcEnvironment jdbcEnv) {
+        return convert(identifier);
+    }
+
+    @Override
+    public Identifier toPhysicalSchemaName(Identifier identifier, JdbcEnvironment jdbcEnv) {
+        return convert(identifier);
+    }
+
+    @Override
+    public Identifier toPhysicalSequenceName(Identifier identifier, JdbcEnvironment jdbcEnv) {
+        return convert(identifier);
+    }
+
+    @Override
+    public Identifier toPhysicalTableName(Identifier identifier, JdbcEnvironment jdbcEnv) {
+        return convert(identifier);
+    }
+
+    private Identifier convert(Identifier identifier) {
+        if (identifier == null || StringUtils.isBlank(identifier.getText())) {
+            return identifier;
+        }
+
+        String regex = "([a-z])([A-Z])";
+        String replacement = "$1_$2";
+        String newName = identifier.getText().replaceAll(regex, replacement).toLowerCase();
+        return Identifier.toIdentifier(newName);
+    }
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/domain/support/ZonedDateTimeTypeHandler.java b/src/main/java/kr/wisestone/owl/domain/support/ZonedDateTimeTypeHandler.java
new file mode 100644
index 0000000..e2d35e1
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/domain/support/ZonedDateTimeTypeHandler.java
@@ -0,0 +1,51 @@
+package kr.wisestone.owl.domain.support;
+
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedTypes;
+
+import java.sql.*;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+@MappedTypes(ZonedDateTime.class)
+public class ZonedDateTimeTypeHandler extends BaseTypeHandler<ZonedDateTime> {
+
+    @Override
+    public void setNonNullParameter(PreparedStatement ps, int i, ZonedDateTime parameter, JdbcType jdbcType) throws SQLException {
+        ps.setTimestamp(
+            i,
+            Timestamp.from(parameter.toInstant()),
+            GregorianCalendar.from(parameter)
+        );
+    }
+
+    @Override
+    public ZonedDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
+        Timestamp ts = rs.getTimestamp(columnName, Calendar.getInstance());
+        if (ts != null) {
+            return ZonedDateTime.ofInstant(ts.toInstant(), ZoneId.systemDefault());
+        }
+        return null;
+    }
+
+    @Override
+    public ZonedDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
+        Timestamp ts = rs.getTimestamp(columnIndex, Calendar.getInstance());
+        if (ts != null) {
+            return ZonedDateTime.ofInstant(ts.toInstant(), ZoneId.systemDefault());
+        }
+        return null;
+    }
+
+    @Override
+    public ZonedDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
+        Timestamp ts = cs.getTimestamp(columnIndex, Calendar.getInstance());
+        if (ts != null) {
+            return ZonedDateTime.ofInstant(ts.toInstant(), ZoneId.systemDefault());
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/exception/OwlRuntimeException.java b/src/main/java/kr/wisestone/owl/exception/OwlRuntimeException.java
new file mode 100644
index 0000000..266672f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/exception/OwlRuntimeException.java
@@ -0,0 +1,52 @@
+package kr.wisestone.owl.exception;
+
+import kr.wisestone.owl.vo.MessageVo;
+
+
+public class OwlRuntimeException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    private String code;
+
+    public OwlRuntimeException() {
+        super();
+    }
+
+    public OwlRuntimeException(String code, final String message,
+                               final Throwable cause) {
+        super(message, cause);
+        this.code = code;
+    }
+
+    public OwlRuntimeException(String code, final String message) {
+        super(message);
+        this.code = code;
+    }
+
+    public OwlRuntimeException(MessageVo message) {
+        super(message.getMessage());
+        this.code = message.getCode();
+    }
+
+    public OwlRuntimeException(MessageVo message, Throwable t) {
+        super(message.getMessage(), t);
+        this.code = message.getCode();
+    }
+
+    public OwlRuntimeException(Exception e) {
+        super(e);
+    }
+
+    public OwlRuntimeException(String code) {
+        this.code = code;
+    }
+
+    public OwlRuntimeException(String code, Exception e) {
+        super(e);
+        this.code = code;
+    }
+
+    public String getCode() {
+        return this.code;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/initializer/AppInitializer.java b/src/main/java/kr/wisestone/owl/initializer/AppInitializer.java
new file mode 100644
index 0000000..337e27f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/initializer/AppInitializer.java
@@ -0,0 +1,52 @@
+package kr.wisestone.owl.initializer;
+
+import org.springframework.security.web.session.HttpSessionEventPublisher;
+import org.springframework.web.WebApplicationInitializer;
+import org.springframework.web.context.ContextLoaderListener;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+import org.springframework.web.filter.CharacterEncodingFilter;
+import org.springframework.web.filter.DelegatingFilterProxy;
+import org.springframework.web.servlet.DispatcherServlet;
+import javax.servlet.*;
+import java.util.EnumSet;
+
+/**
+ * Created by jeong on 2017-08-01.
+ */
+public class AppInitializer implements WebApplicationInitializer {
+
+    private static final String CONFIG_LOCATION = "kr.wisestone.owl.config";
+    private static final String MAPPING_URL = "/*";
+
+    @Override
+    public void onStartup(ServletContext servletContext) {
+        this.registerDispatcherServlet(servletContext);
+        this.registerCharacterEncodingFilter(servletContext);
+        this.registerSpringSecurityFilter(servletContext);
+    }
+
+    private void registerDispatcherServlet(ServletContext servletContext) {
+        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
+        context.setConfigLocation(CONFIG_LOCATION);
+        servletContext.addListener(new ContextLoaderListener(context));
+        servletContext.addListener(new HttpSessionEventPublisher());
+
+        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(context));
+        dispatcher.setLoadOnStartup(1);
+        dispatcher.addMapping(MAPPING_URL);
+
+        context.registerShutdownHook();
+    }
+
+    private void registerCharacterEncodingFilter(ServletContext servletContext) {
+        FilterRegistration.Dynamic characterEncodingFilter = servletContext.addFilter("characterEncodingFilter", new CharacterEncodingFilter());
+        characterEncodingFilter.setInitParameter("encoding", "UTF-8");
+        characterEncodingFilter.setInitParameter("forceEncoding", "true");
+        characterEncodingFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
+    }
+
+    private void registerSpringSecurityFilter(ServletContext servletContext) {
+        FilterRegistration.Dynamic springSecurityFilter = servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy());
+        springSecurityFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/AttachedFileMapper.java b/src/main/java/kr/wisestone/owl/mapper/AttachedFileMapper.java
new file mode 100644
index 0000000..bd0305a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/AttachedFileMapper.java
@@ -0,0 +1,25 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.AttachedFileCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-02-27.
+ */
+@Repository
+public interface AttachedFileMapper {
+    Long findUseStorage(AttachedFileCondition attachedFileCondition);
+
+    List<Map<String, Object>> findByWorkspaceId(Long workspaceId);
+
+    List<Map<String, Object>> findByIssueIds(AttachedFileCondition attachedFileCondition);
+
+    void deleteAttachedFileByWorkspaceId(Long workspaceId);
+
+    void deleteAttachedFileByIssueIds(AttachedFileCondition attachedFileCondition);
+
+    void deleteAttachedFileNotId();
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/CustomFieldMapper.java b/src/main/java/kr/wisestone/owl/mapper/CustomFieldMapper.java
new file mode 100644
index 0000000..54c866b
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/CustomFieldMapper.java
@@ -0,0 +1,17 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.CustomFieldCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-28.
+ */
+@Repository
+public interface CustomFieldMapper {
+    List<Map<String, Object>> find(CustomFieldCondition customFieldCondition);
+
+    Long count(CustomFieldCondition customFieldCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/EventMapper.java b/src/main/java/kr/wisestone/owl/mapper/EventMapper.java
new file mode 100644
index 0000000..36482a4
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/EventMapper.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.EventCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+@Repository
+public interface EventMapper {
+    List<Map<String, Object>> find(EventCondition eventCondition);
+
+    Long count(EventCondition eventCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/FaqMapper.java b/src/main/java/kr/wisestone/owl/mapper/FaqMapper.java
new file mode 100644
index 0000000..4589538
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/FaqMapper.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.FaqCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+@Repository
+public interface FaqMapper {
+    List<Map<String, Object>> find(FaqCondition faqCondition);
+
+    Long count(FaqCondition faqCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/GuideMapper.java b/src/main/java/kr/wisestone/owl/mapper/GuideMapper.java
new file mode 100644
index 0000000..6aa1ef5
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/GuideMapper.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.GuideCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+@Repository
+public interface GuideMapper {
+    List<Map<String, Object>> find(GuideCondition guideCondition);
+
+    Long count(GuideCondition guideCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/IssueCustomFieldValueMapper.java b/src/main/java/kr/wisestone/owl/mapper/IssueCustomFieldValueMapper.java
new file mode 100644
index 0000000..3511c1d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/IssueCustomFieldValueMapper.java
@@ -0,0 +1,24 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.IssueCustomFieldValueCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-06-07.
+ */
+@Repository
+public interface IssueCustomFieldValueMapper {
+    Map<String, Object> findLikeUseValue(IssueCustomFieldValueCondition issueCustomFieldValueCondition);
+
+    Map<String, Object> findByUseValue(IssueCustomFieldValueCondition issueCustomFieldValueCondition);
+
+    void deleteIssueCustomFieldValue(Long issueTypeCustomFieldId);
+
+    List<Map<String, Object>> findInIssueIds(IssueCondition issueCondition);
+
+    void deleteByIssueCustomFieldValueId(List<Long> ids);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/IssueHistoryMapper.java b/src/main/java/kr/wisestone/owl/mapper/IssueHistoryMapper.java
new file mode 100644
index 0000000..1081cfc
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/IssueHistoryMapper.java
@@ -0,0 +1,17 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.IssueHistoryCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-10-17.
+ */
+@Repository
+public interface IssueHistoryMapper {
+    List<Map<String, Object>> find(IssueHistoryCondition issueHistoryCondition);
+
+    Long count(IssueHistoryCondition issueHistoryCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/IssueMapper.java b/src/main/java/kr/wisestone/owl/mapper/IssueMapper.java
new file mode 100644
index 0000000..060e7dd
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/IssueMapper.java
@@ -0,0 +1,41 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.form.IssueForm;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-17.
+ */
+@Repository
+public interface IssueMapper {
+    List<Map<String, Object>> find(IssueCondition issueCondition);
+
+    Long count(IssueCondition issueCondition);
+
+    void insertBatch(@Param("issueForms") List<IssueForm> issueForms);
+
+    void updateBatch(@Param("issueForms") List<IssueForm> issueForms);
+
+    void insertHistoryBatch(List<Map<String, Object>> issueHistoryMaps);
+
+    void insertIssueRiskBatch(List<Map<String, Long>> issueRiskMaps);
+
+    void insertIssueCustomFieldValueBatch(List<Map<String, Object>> issueCustomFieldValueMaps);
+
+    List<Map<String, Object>> findIssueUser(IssueCondition issueCondition);
+
+    Long countByIssueTypeId(Long issueTypeId);
+
+    Long countByIssueStatusId(Long issueStatusId);
+
+    List<Map<String, Object>> findByIssueTypeId(Long issueTypeId);
+
+    List<Map<String, Object>> findByProjectId(Long projectId);
+
+    List<Map<String, Object>> getAllTaskUser(IssueCondition taskCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/IssueStatusMapper.java b/src/main/java/kr/wisestone/owl/mapper/IssueStatusMapper.java
new file mode 100644
index 0000000..72c8919
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/IssueStatusMapper.java
@@ -0,0 +1,17 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.IssueStatusCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-09.
+ */
+@Repository
+public interface IssueStatusMapper {
+    List<Map<String, Object>> find(IssueStatusCondition issueStatusCondition);
+
+    Long count(IssueStatusCondition issueStatusCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/IssueTypeMapper.java b/src/main/java/kr/wisestone/owl/mapper/IssueTypeMapper.java
new file mode 100644
index 0000000..bdc5d2b
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/IssueTypeMapper.java
@@ -0,0 +1,17 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.IssueTypeCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-29.
+ */
+@Repository
+public interface IssueTypeMapper {
+    List<Map<String, Object>> find(IssueTypeCondition issueTypeCondition);
+
+    Long count(IssueTypeCondition issueTypeCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/IssueUserMapper.java b/src/main/java/kr/wisestone/owl/mapper/IssueUserMapper.java
new file mode 100644
index 0000000..59b0066
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/IssueUserMapper.java
@@ -0,0 +1,21 @@
+package kr.wisestone.owl.mapper;
+
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-11-27.
+ */
+@Repository
+public interface IssueUserMapper {
+    void insertIssueUser(List<Map<String, Long>> issueRoleUserMaps);
+
+    void deleteIssueUserByIssueIdAndMultiUserId(Map<String, Object> removeIssueAssigneeMap);
+
+    void deleteIssueUserByUserIdAndMultiIssueId(Map<String, Object> removeIssueAssigneeMap);
+
+    List<Map<String, Object>> findByUserIdAndProjectId(Map<String, Object> issueUserMap);
+
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/NoticeMapper.java b/src/main/java/kr/wisestone/owl/mapper/NoticeMapper.java
new file mode 100644
index 0000000..da2cf00
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/NoticeMapper.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.NoticeCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+@Repository
+public interface NoticeMapper {
+    List<Map<String, Object>> find(NoticeCondition noticeCondition);
+
+    Long count(NoticeCondition noticeCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/ProjectMapper.java b/src/main/java/kr/wisestone/owl/mapper/ProjectMapper.java
new file mode 100644
index 0000000..118fe38
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/ProjectMapper.java
@@ -0,0 +1,34 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.ProjectCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-02-23.
+ */
+@Repository
+public interface ProjectMapper {
+
+    List<Map<String, Object>> find(ProjectCondition projectCondition);
+
+    Long count(ProjectCondition projectCondition);
+
+    List<Map<String, Object>> findByWorkspaceManager(ProjectCondition projectCondition);
+
+    List<Map<String, Object>> findByWorkspaceManagerAll(ProjectCondition projectCondition);
+
+    Long countByWorkspaceManager(ProjectCondition projectCondition);
+
+    List<Map<String, Object>> findByWorkspaceIdAndIncludeProject(ProjectCondition projectCondition);
+
+    List<Map<String, Object>> findByWorkspaceIdAndIncludeProjectAll(ProjectCondition projectCondition);
+
+    void deleteProject(Map<String, Object> deleteProjectMap);
+
+    List<Map<String, Object>> checkIncludeProject(ProjectCondition projectCondition);
+
+    List<Map<String, Object>> findChildrenProject(Long parentProjectId);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/ProjectRoleUserMapper.java b/src/main/java/kr/wisestone/owl/mapper/ProjectRoleUserMapper.java
new file mode 100644
index 0000000..e0e08f0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/ProjectRoleUserMapper.java
@@ -0,0 +1,19 @@
+package kr.wisestone.owl.mapper;
+
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-11-27.
+ */
+@Repository
+public interface ProjectRoleUserMapper {
+    void insertProjectRoleUser(List<Map<String, Long>> projectRoleUserMaps);
+
+    void deleteProjectRoleUser(Map<String, Long> projectRoleUserMap);
+
+    List<Map<String, Object>> findProjectRoleUser(Map<String, Object> projectRoleUserMap);
+
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/QnaMapper.java b/src/main/java/kr/wisestone/owl/mapper/QnaMapper.java
new file mode 100644
index 0000000..ad1429e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/QnaMapper.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.QnaCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+@Repository
+public interface QnaMapper {
+    List<Map<String, Object>> find(QnaCondition qnaCondition);
+
+    Long count(QnaCondition qnaCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/UserMapper.java b/src/main/java/kr/wisestone/owl/mapper/UserMapper.java
new file mode 100644
index 0000000..b1522b8
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/UserMapper.java
@@ -0,0 +1,32 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.UserCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-02-26.
+ */
+@Repository
+public interface UserMapper {
+    List<Map<String, Object>> find(UserCondition userCondition);
+
+    List<Map<String, Object>> findAdmin();
+
+    List<Map<String, Object>> findProjectMember(UserCondition userCondition);
+
+    Long count(UserCondition userCondition);
+
+    void deleteCascadeUser(UserCondition userCondition);
+
+    List<Map<String, Object>> findByReservationNotifyTime(Map<String, Object> conditions);
+
+    List<Map<String, Object>> findByAllWorkspace(UserCondition userCondition);
+
+    Long countByAllWorkspace(UserCondition userCondition);
+
+    List<Map<String, Object>> findEvent();
+
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/UserWorkspaceMapper.java b/src/main/java/kr/wisestone/owl/mapper/UserWorkspaceMapper.java
new file mode 100644
index 0000000..a8b9941
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/UserWorkspaceMapper.java
@@ -0,0 +1,17 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.UserWorkspaceCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-10-02.
+ */
+@Repository
+public interface UserWorkspaceMapper {
+    List<Map<String, Object>> find(UserWorkspaceCondition userWorkspaceCondition);
+
+    Long count(UserWorkspaceCondition userWorkspaceCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/WidgetMapper.java b/src/main/java/kr/wisestone/owl/mapper/WidgetMapper.java
new file mode 100644
index 0000000..86575ca
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/WidgetMapper.java
@@ -0,0 +1,82 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.WidgetCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-11.
+ */
+@Repository
+public interface WidgetMapper {
+
+    // �셿猷뚮맂 �씠�뒋
+    Long countCompleteIssue(WidgetCondition widgetCondition);
+
+    //  誘명븷�떦 �씠�뒋
+    Long countNoAssigneeIssue(WidgetCondition widgetCondition);
+
+    //  �궡媛� �벑濡앺븳 �씠�뒋
+    List<Map<String, Object>> findRegisterIssue(WidgetCondition widgetCondition);
+    //  �궡媛� �삤�뒛 �벑濡앺븳 �씠�뒋 媛��닔
+    Long countTodayRegisterIssue(WidgetCondition widgetCondition);
+    Long countRegisterIssue(WidgetCondition widgetCondition);
+
+    //  �븷�떦�맂 �씠�뒋
+    Long countAssigneeIssue(WidgetCondition widgetCondition);
+
+    //  吏��뿰�맂 �씠�뒋
+    List<Map<String, Object>> findDelayIssue(WidgetCondition widgetCondition);
+    Long countDelayIssue(WidgetCondition widgetCondition);
+    Long countTodayDelayIssue(WidgetCondition widgetCondition);
+
+    //  �옍�뿬 �씠�뒋
+    Long countRemainIssue(WidgetCondition widgetCondition);
+
+    //  吏꾪뻾以묒씤 �봽濡쒖젥�듃 �쁽�솴(李몄뿬以묒씤 �봽濡쒖젥�듃)
+    List<Map<String, Object>> findProjectProgress(WidgetCondition widgetCondition);
+
+    //  吏꾪뻾以묒씤 �봽濡쒖젥�듃 �쁽�솴(�쟾泥�)
+    List<Map<String, Object>> findProjectProgressAll(WidgetCondition widgetCondition);
+
+    //  �굹�뿉寃� �븷�떦�맂 �씠�뒋
+    List<Map<String, Object>> findMyAssigneeIssue(WidgetCondition widgetCondition);
+    //  �궡媛� �삤�뒛 �븷�떦諛쏆� �씠�뒋 媛��닔
+    Long countTodayMyAssigneeIssue(WidgetCondition widgetCondition);
+    Long countMyAssigneeIssue(WidgetCondition widgetCondition);
+
+    //  硫ㅻ쾭蹂� 吏꾪뻾瑜�
+    List<Map<String, Object>> findProjectMemberIssue(WidgetCondition widgetCondition);
+
+    //  �벑濡앺븳 �씠�뒋 以� �셿猷� 媛��닔
+    List<Map<String, Object>> findMyRegisterCompleteIssue(WidgetCondition widgetCondition);
+    //  �벑濡앺븳 �씠�뒋 以� 吏꾪뻾 媛��닔
+    List<Map<String, Object>> findMyRegisterRemainIssue(WidgetCondition widgetCondition);
+    //  �떞�떦�븳 �씠�뒋 以� �셿猷� 媛��닔
+    List<Map<String, Object>> findMyAssigneeCompleteIssue(WidgetCondition widgetCondition);
+    //  �떞�떦�븳 �씠�뒋 以� 吏꾪뻾 媛��닔
+    List<Map<String, Object>> findMyAssigneeRemainIssue(WidgetCondition widgetCondition);
+
+    //  �쐞�뿕 愿�由�
+    List<Map<String, Object>> findRiskIssue(WidgetCondition widgetCondition);
+    Map<String, Object> countChangeStatusAndAssigneeIssue(WidgetCondition widgetCondition);
+    Long countRiskIssue(WidgetCondition widgetCondition);
+
+    //  �쟾泥� �씠�뒋 泥섎━ �쁽�솴
+    List<Map<String, Object>> findIssueComplete(WidgetCondition widgetCondition);
+    Long countTotalIssue(WidgetCondition widgetCondition);
+
+    //  �긽�깭蹂� �씠�뒋 �쁽�솴
+    List<Map<String, Object>> findByStandIssueStatus(WidgetCondition widgetCondition);
+
+    List<Map<String, Object>> findByStandIssueType(WidgetCondition widgetCondition);
+
+    //以묒슂�룄 蹂� �씠�뒋 媛��닔
+    List<Map<String, Object>> countSeverityIssue(WidgetCondition widgetCondition);
+    //以묒슂�룄 �씠�뒋 紐⑸줉
+    List<Map<String, Object>> findSeverityIssues(WidgetCondition widgetCondition);
+    //以묒슂�룄 �뱶濡��떎�슫 蹂� �빆紐� 媛��닔
+    Long countSearchIssue(WidgetCondition widgetCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/WorkflowMapper.java b/src/main/java/kr/wisestone/owl/mapper/WorkflowMapper.java
new file mode 100644
index 0000000..ded91c8
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/WorkflowMapper.java
@@ -0,0 +1,17 @@
+package kr.wisestone.owl.mapper;
+
+import kr.wisestone.owl.web.condition.WorkflowCondition;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-10.
+ */
+@Repository
+public interface WorkflowMapper {
+    List<Map<String, Object>> find(WorkflowCondition workflowCondition);
+
+    Long count(WorkflowCondition workflowCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/mapper/WorkspaceMapper.java b/src/main/java/kr/wisestone/owl/mapper/WorkspaceMapper.java
new file mode 100644
index 0000000..446e458
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/mapper/WorkspaceMapper.java
@@ -0,0 +1,13 @@
+package kr.wisestone.owl.mapper;
+
+import org.springframework.stereotype.Repository;
+
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-11-29.
+ */
+@Repository
+public interface WorkspaceMapper {
+    void deleteWorkspace(Map<String, Object> deleteWorkspaceMap);
+}
diff --git a/src/main/java/kr/wisestone/owl/monitor/Email.java b/src/main/java/kr/wisestone/owl/monitor/Email.java
new file mode 100644
index 0000000..41db5d2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/monitor/Email.java
@@ -0,0 +1,76 @@
+package kr.wisestone.owl.monitor;
+
+import com.google.common.collect.Lists;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Created by wisestone on 2018-02-09.
+ */
+public class Email {
+    public Date received;
+    public String from;
+    public List<String> to = Lists.newArrayList();
+    public List<String> cc = Lists.newArrayList();
+    public String subject;
+    public String body;
+    public List<EmailAttachment> attachments = Lists.newArrayList();
+
+    public Email(){}
+
+    public Date getReceived() {
+        return received;
+    }
+
+    public void setReceived(Date received) {
+        this.received = received;
+    }
+
+    public String getFrom() {
+        return from;
+    }
+
+    public void setFrom(String from) {
+        this.from = from;
+    }
+
+    public List<String> getTo() {
+        return to;
+    }
+
+    public void setTo(List<String> to) {
+        this.to = to;
+    }
+
+    public List<String> getCc() {
+        return cc;
+    }
+
+    public void setCc(List<String> cc) {
+        this.cc = cc;
+    }
+
+    public String getSubject() {
+        return subject;
+    }
+
+    public void setSubject(String subject) {
+        this.subject = subject;
+    }
+
+    public String getBody() {
+        return body;
+    }
+
+    public void setBody(String body) {
+        this.body = body;
+    }
+
+    public List<EmailAttachment> getAttachments() {
+        return attachments;
+    }
+
+    public void setAttachments(List<EmailAttachment> attachments) {
+        this.attachments = attachments;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/monitor/EmailAttachment.java b/src/main/java/kr/wisestone/owl/monitor/EmailAttachment.java
new file mode 100644
index 0000000..3483b39
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/monitor/EmailAttachment.java
@@ -0,0 +1,36 @@
+package kr.wisestone.owl.monitor;
+
+/**
+ * Created by wisestone on 2018-02-09.
+ */
+public class EmailAttachment {
+    public String name;
+    public String path;
+    public Long size;
+
+    public EmailAttachment(){}
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public Long getSize() {
+        return size;
+    }
+
+    public void setSize(Long size) {
+        this.size = size;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/monitor/MailMonitor.java b/src/main/java/kr/wisestone/owl/monitor/MailMonitor.java
new file mode 100644
index 0000000..d7ac168
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/monitor/MailMonitor.java
@@ -0,0 +1,226 @@
+package kr.wisestone.owl.monitor;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.WebAppUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+
+import javax.mail.*;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeUtility;
+import javax.mail.search.FlagTerm;
+import java.io.*;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Created by jeong on 2018-02-08.
+ */
+public class MailMonitor {
+    private static final Logger log = LoggerFactory.getLogger(MailMonitor.class);
+
+    private volatile static MailMonitor uniqueInstance;
+    private Store store;
+    private String downloadDirectory = "";
+    private String userName = "";
+    private String password = "";
+
+    private MailMonitor() {
+        //  �떆�뒪�뀥 �봽濡쒗띁�떚瑜� �씫�뼱 �떎�슫濡쒕뱶 寃쎈줈瑜� 吏��젙
+        this.initMailProperties();
+        this.connectEmail();
+    }
+
+    public static MailMonitor getInstance() {
+        if (uniqueInstance == null) {
+            synchronized (MailMonitor.class) {
+                if (uniqueInstance == null) {
+                    uniqueInstance = new MailMonitor();
+                }
+            }
+        }
+
+        return uniqueInstance;
+    }
+
+    private void initMailProperties() {
+        Properties properties = new Properties();
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        InputStream inputStream = classLoader.getResourceAsStream("system.properties");
+
+        try {
+            properties.load(inputStream);
+            this.downloadDirectory = properties.getProperty("mail.file.path");
+            this.userName = properties.getProperty("mail.account");
+            this.password = properties.getProperty("mail.password");
+        } catch (IOException e) {
+            log.debug("initMailProperties() : " + e.getMessage());
+        }
+    }
+
+    private void connectEmail() {
+        Properties properties = new Properties();
+        properties.setProperty("mail.host", "imap.gmail.com");
+        properties.setProperty("mail.port", "995");
+        properties.setProperty("mail.transport.protocol", "imaps");
+
+        Session session = Session.getInstance(properties,
+                new javax.mail.Authenticator() {
+                    protected PasswordAuthentication getPasswordAuthentication() {
+                        return new PasswordAuthentication(userName, password);
+                    }
+                });
+
+        try {
+            this.store = session.getStore("imaps");
+            this.store.connect();
+        } catch (MessagingException e) {
+            log.debug("connectEmail() : " + e.getMessage());
+        }
+    }
+
+    //  TODO - �씠硫붿씪 �뿰�룞 �떆�뒪�뀥�뿉�꽌 異붽��쟻�쑝濡� �궗�슜�옄媛� �씠硫붿씪�쓣 蹂대궡怨� �떢�� �봽濡쒖젥�듃瑜� �꽑�깮�븯�뒗 �꽕�젙 �솕硫� 媛쒕컻�씠 �븘�슂�븯�떎.
+    public List<Email> readMails() {
+        Folder inbox = null;
+        List<Email> emails = Lists.newArrayList();
+
+        try {
+            inbox = store.getFolder("INBOX");
+            inbox.open(Folder.READ_WRITE);
+
+            Message messages[] = inbox.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
+
+            System.out.println("Number of mails = " + messages.length);
+
+            for (Message message : messages) {
+                Email email = new Email();
+                Address[] from = message.getFrom();
+
+                /*System.out.println("-------------------------------");
+                System.out.println("Date : " + message.getSentDate());
+                System.out.println("From : " + MimeUtility.decodeText(from[0].toString()));
+                System.out.println("Subject: " + MimeUtility.decodeText(message.getSubject()));
+                System.out.println("Content :");*/
+
+                String contentType = message.getContentType();
+                String messageContent = "";
+                List<EmailAttachment> emailAttachments = Lists.newArrayList();
+
+                if (contentType.contains("multipart")) {
+                    Multipart multiPart = (Multipart) message.getContent();
+                    int numberOfParts = multiPart.getCount();
+
+                    for (int partCount = 0; partCount < numberOfParts; partCount++) {
+                        MimeBodyPart part = (MimeBodyPart) multiPart.getBodyPart(partCount);
+                        if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
+                            // 泥⑤� �뙆�씪
+                            String fileName = MimeUtility.decodeText(part.getFileName());
+                            EmailAttachment emailAttachment = new EmailAttachment();
+                            emailAttachment.setName(fileName);
+                            emailAttachment.setPath(this.downloadDirectory + fileName);
+                            emailAttachments.add(emailAttachment);
+
+                            part.saveFile(this.downloadDirectory + fileName);
+                        }
+                        else {
+                            // 蹂몃Ц �뀓�뒪�듃
+                            messageContent = this.getText(part);
+                        }
+                    }
+                }
+                else if (contentType.contains("text/plain") || contentType.contains("text/html")) {
+                    Object content = message.getContent();
+                    if (content != null) {
+                        // 蹂몃Ц �뀓�뒪�듃
+                        messageContent = content.toString();
+                    }
+                }
+
+                /*System.out.println("\t Message: " + messageContent);
+                System.out.println("--------------------------------");*/
+
+                email.setSubject(message.getSubject());
+                email.setFrom(MimeUtility.decodeText(from[0].toString()));
+                email.setBody(messageContent);
+                email.setAttachments(emailAttachments);
+                emails.add(email);
+            }
+
+            //inbox.setFlags(messages, new Flags(Flags.Flag.SEEN), true);
+
+            /*inbox.close(true);
+            this.store.close();*/
+        } catch (NoSuchProviderException e) {
+            log.debug("readMails() : "+ e.getMessage());
+            e.printStackTrace();
+        } catch (MessagingException e) {
+            log.debug("readMails() : "+ e.getMessage());
+            e.printStackTrace();
+        } catch (UnsupportedEncodingException e) {
+            log.debug("readMails() : "+ e.getMessage());
+            e.printStackTrace();
+        } catch (IOException e) {
+            log.debug("readMails() : "+ e.getMessage());
+            e.printStackTrace();
+        }
+        finally{
+            this.closeFolder(inbox);
+        }
+
+        return emails;
+    }
+
+    private String getText(Part part) throws MessagingException, IOException {
+        if (part.isMimeType("text/*")) {
+            return (String) part.getContent();
+        }
+
+        if (part.isMimeType("multipart/alternative")) {
+            // prefer html text over plain text
+            Multipart mp = (Multipart) part.getContent();
+            String text = null;
+            for (int i = 0; i < mp.getCount(); i++) {
+                Part bp = mp.getBodyPart(i);
+                if (bp.isMimeType("text/plain")) {
+                    if (text == null) {
+                        text = this.getText(bp);
+                    }
+                    continue;
+                }
+                else if (bp.isMimeType("text/html")) {
+                    String content = this.getText(bp);
+                    if (content != null) {
+                        return content;
+                    }
+                }
+                else {
+                    return this.getText(bp);
+                }
+            }
+            return text;
+        }
+        else if (part.isMimeType("multipart/*")) {
+            Multipart mp = (Multipart) part.getContent();
+            for (int i = 0; i < mp.getCount(); i++) {
+                String content = this.getText(mp.getBodyPart(i));
+                if (content != null) {
+                    return content;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private void closeFolder(Folder inbox) {
+        try {
+            if (inbox != null) {
+                inbox.close(false);
+            }
+
+        } catch (MessagingException e) {
+            log.debug("closeFolder() : "+ e.getMessage());
+        }
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/AttachedFileRepository.java b/src/main/java/kr/wisestone/owl/repository/AttachedFileRepository.java
new file mode 100644
index 0000000..e1f3d80
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/AttachedFileRepository.java
@@ -0,0 +1,10 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.AttachedFile;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface AttachedFileRepository extends JpaRepository<AttachedFile, Long> {
+    List<AttachedFile> findByIssueId(@Param("issueId") Long issueId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/CustomFieldRepository.java b/src/main/java/kr/wisestone/owl/repository/CustomFieldRepository.java
new file mode 100644
index 0000000..33ff470
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/CustomFieldRepository.java
@@ -0,0 +1,16 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.CustomField;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface CustomFieldRepository extends JpaRepository<CustomField, Long> {
+
+    CustomField findByNameAndWorkspaceId(@Param("name") String name, @Param("workspaceId") Long workspaceId);
+
+    CustomField findByNameAndWorkspaceIdAndIdNot(@Param("name") String name, @Param("workspaceId") Long workspaceId, @Param("id") Long id);
+
+    List<CustomField> findByWorkspaceId(@Param("workspaceId") Long workspaceId);
+    
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/CustomFieldValueRepository.java b/src/main/java/kr/wisestone/owl/repository/CustomFieldValueRepository.java
new file mode 100644
index 0000000..8dd3592
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/CustomFieldValueRepository.java
@@ -0,0 +1,7 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.CustomFieldValue;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface CustomFieldValueRepository extends JpaRepository<CustomFieldValue, Long> {
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/EventRepository.java b/src/main/java/kr/wisestone/owl/repository/EventRepository.java
new file mode 100644
index 0000000..d96cfe3
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/EventRepository.java
@@ -0,0 +1,15 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Event;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.transaction.annotation.Transactional;
+
+public interface EventRepository extends JpaRepository<Event, Long> {
+    @Modifying
+    @Transactional
+    @Query(name = "Event.updateInActivation")
+    void updateInActivation(@Param("eventId") Long eventId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/FaqRepository.java b/src/main/java/kr/wisestone/owl/repository/FaqRepository.java
new file mode 100644
index 0000000..a996efd
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/FaqRepository.java
@@ -0,0 +1,7 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Faq;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface FaqRepository extends JpaRepository<Faq, Long> {
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/GuideRepository.java b/src/main/java/kr/wisestone/owl/repository/GuideRepository.java
new file mode 100644
index 0000000..f5a838c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/GuideRepository.java
@@ -0,0 +1,15 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Guide;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.transaction.annotation.Transactional;
+
+public interface GuideRepository extends JpaRepository<Guide, Long> {
+    @Modifying
+    @Transactional
+    @Query(name = "Guide.updateInActivation")
+    void updateInActivation(@Param("guideId") Long guideId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueCommentRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueCommentRepository.java
new file mode 100644
index 0000000..fb5dce7
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueCommentRepository.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueComment;
+import kr.wisestone.owl.vo.IssueCommentVo;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface IssueCommentRepository extends JpaRepository<IssueComment, Long> {
+
+    @Query(name="IssueComment.findByIssueId")
+    List<IssueCommentVo> findByIssueId(@Param("issueId") Long issueId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueCustomFieldValueRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueCustomFieldValueRepository.java
new file mode 100644
index 0000000..6437ee9
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueCustomFieldValueRepository.java
@@ -0,0 +1,13 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueCustomFieldValue;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface IssueCustomFieldValueRepository extends JpaRepository<IssueCustomFieldValue, Long> {
+
+    List<IssueCustomFieldValue> findByIssueId(@Param("issueId") Long issueId);
+
+    List<IssueCustomFieldValue> findByCustomFieldId(@Param("customFieldId") Long customFieldId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueHistoryRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueHistoryRepository.java
new file mode 100644
index 0000000..b706280
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueHistoryRepository.java
@@ -0,0 +1,13 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueHistory;
+import kr.wisestone.owl.vo.IssueHistoryVo;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface IssueHistoryRepository extends JpaRepository<IssueHistory, Long> {
+    @Query(name="IssueHistory.findByIssueId")
+    List<IssueHistoryVo> findByIssueId(@Param("issueId") Long issueId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueNumberGeneratorRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueNumberGeneratorRepository.java
new file mode 100644
index 0000000..11181f4
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueNumberGeneratorRepository.java
@@ -0,0 +1,9 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueNumberGenerator;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+
+public interface IssueNumberGeneratorRepository extends JpaRepository<IssueNumberGenerator, Long> {
+    IssueNumberGenerator findByProjectId(@Param("projectId") Long projectId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueRelationRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueRelationRepository.java
new file mode 100644
index 0000000..6b14f74
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueRelationRepository.java
@@ -0,0 +1,10 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueRelation;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface IssueRelationRepository extends JpaRepository<IssueRelation, Long> {
+    List<IssueRelation> findAllByIssueId(Long issueId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueRepository.java
new file mode 100644
index 0000000..300b1b2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueRepository.java
@@ -0,0 +1,7 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Issue;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface IssueRepository extends JpaRepository<Issue, Long> {
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueReservationRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueReservationRepository.java
new file mode 100644
index 0000000..1667ba4
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueReservationRepository.java
@@ -0,0 +1,10 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueReservation;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface IssueReservationRepository extends JpaRepository<IssueReservation, Long> {
+    List<IssueReservation> findByIssueReservationTypeNotNull();
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueRiskRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueRiskRepository.java
new file mode 100644
index 0000000..f4fba1c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueRiskRepository.java
@@ -0,0 +1,7 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueRisk;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface IssueRiskRepository extends JpaRepository<IssueRisk, Long> {
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueSearchRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueSearchRepository.java
new file mode 100644
index 0000000..6508919
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueSearchRepository.java
@@ -0,0 +1,8 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueSearch;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface IssueSearchRepository extends JpaRepository<IssueSearch, Long> {
+    IssueSearch findByUserIdAndWorkspaceId(Long userId, Long workspaceId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueStatusRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueStatusRepository.java
new file mode 100644
index 0000000..2600051
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueStatusRepository.java
@@ -0,0 +1,18 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueStatus;
+import kr.wisestone.owl.domain.enumType.IssueStatusType;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface IssueStatusRepository extends JpaRepository<IssueStatus, Long> {
+    List<IssueStatus> findByWorkspaceId(@Param("workspaceId") Long workspaceId);
+
+    IssueStatus findByNameAndWorkspaceId(@Param("name") String name, @Param("workspaceId") Long workspaceId);
+
+    IssueStatus findByNameAndWorkspaceIdAndIdNot(@Param("name") String name, @Param("workspaceId") Long workspaceId, @Param("id") Long id);
+
+    IssueStatus findByWorkspaceIdAndIssueStatusType(@Param("workspaceId") Long workspaceId, @Param("issueStatusType") IssueStatusType issueStatusType);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueTableConfigRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueTableConfigRepository.java
new file mode 100644
index 0000000..07303d3
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueTableConfigRepository.java
@@ -0,0 +1,9 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueTableConfig;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+
+public interface IssueTableConfigRepository extends JpaRepository<IssueTableConfig, Long> {
+    IssueTableConfig findByUserIdAndWorkspaceId(@Param("userId") Long userId, @Param("workspaceId") Long workspaceId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueTypeCustomFieldRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueTypeCustomFieldRepository.java
new file mode 100644
index 0000000..4ca81d6
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueTypeCustomFieldRepository.java
@@ -0,0 +1,15 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueTypeCustomField;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface IssueTypeCustomFieldRepository extends JpaRepository<IssueTypeCustomField, Long> {
+    List<IssueTypeCustomField> findByProjectIdAndIssueTypeId(@Param("projectId") Long projectId, @Param("issueTypeId") Long issueTypeId);
+
+    List<IssueTypeCustomField> findByProjectIdAndIssueTypeIdOrderByPosition(@Param("projectId") Long projectId, @Param("issueTypeId") Long issueTypeId);
+
+    IssueTypeCustomField findByProjectIdAndIssueTypeIdAndCustomFieldId(@Param("projectId") Long projectId, @Param("issueTypeId") Long issueTypeId, @Param("customFieldId") Long customFieldId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueTypeRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueTypeRepository.java
new file mode 100644
index 0000000..c0a8a75
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueTypeRepository.java
@@ -0,0 +1,18 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueType;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface IssueTypeRepository extends JpaRepository<IssueType, Long> {
+
+    IssueType findByNameAndWorkspaceId(@Param("name") String name, @Param("workspaceId") Long workspaceId);
+
+    IssueType findByNameAndWorkspaceIdAndIdNot(@Param("name") String name, @Param("workspaceId") Long workspaceId, @Param("id") Long id);
+
+    IssueType findByName(@Param("name") String name);
+
+    List<IssueType> findByWorkspaceId(@Param("workspaceId") Long workspaceId);
+
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueUserRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueUserRepository.java
new file mode 100644
index 0000000..310f026
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueUserRepository.java
@@ -0,0 +1,9 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueUser;
+import org.springframework.data.jpa.repository.JpaRepository;
+import java.util.List;
+
+public interface IssueUserRepository extends JpaRepository<IssueUser, Long> {
+    List<IssueUser> findByIssueId(Long issueId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/IssueVersionRepository.java b/src/main/java/kr/wisestone/owl/repository/IssueVersionRepository.java
new file mode 100644
index 0000000..e4aed32
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/IssueVersionRepository.java
@@ -0,0 +1,10 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.IssueVersion;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface IssueVersionRepository extends JpaRepository<IssueVersion, Long> {
+    List<IssueVersion> findByIssueId(@Param("issueId") Long issueId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/NoticeRepository.java b/src/main/java/kr/wisestone/owl/repository/NoticeRepository.java
new file mode 100644
index 0000000..400d2db
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/NoticeRepository.java
@@ -0,0 +1,7 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Notice;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface NoticeRepository extends JpaRepository<Notice, Long> {
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/PaymentHistoryRepository.java b/src/main/java/kr/wisestone/owl/repository/PaymentHistoryRepository.java
new file mode 100644
index 0000000..8724485
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/PaymentHistoryRepository.java
@@ -0,0 +1,11 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.PaymentHistory;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface PaymentHistoryRepository extends JpaRepository<PaymentHistory, Long> {
+
+    List<PaymentHistory> findByWorkspaceId(@Param("workspaceId") Long workspaceId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/PaymentRepository.java b/src/main/java/kr/wisestone/owl/repository/PaymentRepository.java
new file mode 100644
index 0000000..80ee89c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/PaymentRepository.java
@@ -0,0 +1,7 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Payment;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface PaymentRepository extends JpaRepository<Payment, Long> {
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/PermissionRepository.java b/src/main/java/kr/wisestone/owl/repository/PermissionRepository.java
new file mode 100644
index 0000000..8de8b24
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/PermissionRepository.java
@@ -0,0 +1,15 @@
+
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Permission;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface PermissionRepository extends JpaRepository<Permission, Long> {
+    @Query(name="Permission.findByUserId")
+    List<Permission> findByUserId(@Param("userId") Long userId);
+
+    List<Permission> findByRoleType(@Param("roleType") String roleType);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/PriorityRepository.java b/src/main/java/kr/wisestone/owl/repository/PriorityRepository.java
new file mode 100644
index 0000000..bda172d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/PriorityRepository.java
@@ -0,0 +1,12 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Priority;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface PriorityRepository extends JpaRepository<Priority, Long> {
+    List<Priority> findByWorkspaceIdOrderByPosition(@Param("workspaceId") Long workspaceId);
+
+    List<Priority> findByWorkspaceId(@Param("workspaceId") Long workspaceId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/ProjectClosureRepository.java b/src/main/java/kr/wisestone/owl/repository/ProjectClosureRepository.java
new file mode 100644
index 0000000..9c0d0f8
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/ProjectClosureRepository.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Priority;
+import kr.wisestone.owl.domain.ProjectClosure;
+import kr.wisestone.owl.domain.ProjectRole;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface ProjectClosureRepository extends JpaRepository<ProjectClosure, Long> {
+    ProjectClosure findByProjectId(@Param("project_Id") Long projectId);
+    List<ProjectClosure> findByParentProjectId(@Param("parent_Project_Id") Long parentProjectId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/ProjectRepository.java b/src/main/java/kr/wisestone/owl/repository/ProjectRepository.java
new file mode 100644
index 0000000..da5fa99
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/ProjectRepository.java
@@ -0,0 +1,19 @@
+package kr.wisestone.owl.repository;
+
+import com.amazonaws.services.route53.model.transform.ListGeoLocationsResultStaxUnmarshaller;
+import kr.wisestone.owl.domain.Project;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface ProjectRepository extends JpaRepository<Project, Long> {
+    Project findByProjectKeyAndWorkspaceId(@Param("projectKey") String projectKey, @Param("workspaceId") Long workspaceId);
+
+    Project findByNameAndWorkspaceId(@Param("name") String name, @Param("workspaceId") Long workspaceId);
+
+    Project findByNameAndWorkspaceIdAndIdNot(@Param("name") String name, @Param("workspaceId") Long workspaceId, @Param("id") Long id);
+
+    List<Project> findByWorkspaceId(@Param("workspaceId") Long workspaceId);
+
+    Project findByWorkspaceIdAndDefaultYn(@Param("workspaceId") Long workspaceId, @Param("defaultYn") Boolean defaultYn);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/ProjectRolePermissionRepository.java b/src/main/java/kr/wisestone/owl/repository/ProjectRolePermissionRepository.java
new file mode 100644
index 0000000..fc9adcf
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/ProjectRolePermissionRepository.java
@@ -0,0 +1,7 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.ProjectRolePermission;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface ProjectRolePermissionRepository extends JpaRepository<ProjectRolePermission, Long> {
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/ProjectRoleRepository.java b/src/main/java/kr/wisestone/owl/repository/ProjectRoleRepository.java
new file mode 100644
index 0000000..6263a82
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/ProjectRoleRepository.java
@@ -0,0 +1,15 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.ProjectRole;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+public interface ProjectRoleRepository extends JpaRepository<ProjectRole, Long> {
+    ProjectRole findByProjectId(@Param("projectId") Long projectId);
+
+    ProjectRole findByProjectIdAndRoleType(@Param("projectId") Long projectId, @Param("roleType") String roleType);
+
+    @Query(name="ProjectRole.findByUserIdAndProjectId")
+    ProjectRole findByUserIdAndProjectId(@Param("userId") Long userId, @Param("projectId") Long projectId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/ProjectRoleUserRepository.java b/src/main/java/kr/wisestone/owl/repository/ProjectRoleUserRepository.java
new file mode 100644
index 0000000..661e098
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/ProjectRoleUserRepository.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.ProjectRoleUser;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface ProjectRoleUserRepository extends JpaRepository<ProjectRoleUser, Long> {
+    List<ProjectRoleUser> findByProjectRoleId(@Param("projectRoleId") Long projectRoleId);
+
+    ProjectRoleUser findByProjectRoleIdAndUserId(@Param("projectRoleId") Long projectRoleId, @Param("userId") Long userId);
+
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/QnaRepository.java b/src/main/java/kr/wisestone/owl/repository/QnaRepository.java
new file mode 100644
index 0000000..0803632
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/QnaRepository.java
@@ -0,0 +1,7 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Qna;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface QnaRepository extends JpaRepository<Qna, Long> {
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/ReservationDisableUserRepository.java b/src/main/java/kr/wisestone/owl/repository/ReservationDisableUserRepository.java
new file mode 100644
index 0000000..dc2dcd7
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/ReservationDisableUserRepository.java
@@ -0,0 +1,7 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.ReservationDisableUser;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface ReservationDisableUserRepository extends JpaRepository<ReservationDisableUser, Long> {
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/SeverityRepository.java b/src/main/java/kr/wisestone/owl/repository/SeverityRepository.java
new file mode 100644
index 0000000..b622a2a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/SeverityRepository.java
@@ -0,0 +1,12 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Severity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface SeverityRepository extends JpaRepository<Severity, Long> {
+    List<Severity> findByWorkspaceIdOrderByPosition(@Param("workspaceId") Long workspaceId);
+
+    List<Severity> findByWorkspaceId(@Param("workspaceId") Long workspaceId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/SystemEmailRepository.java b/src/main/java/kr/wisestone/owl/repository/SystemEmailRepository.java
new file mode 100644
index 0000000..84d79e9
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/SystemEmailRepository.java
@@ -0,0 +1,11 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.SystemEmail;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface SystemEmailRepository extends JpaRepository<SystemEmail, Long> {
+    List<SystemEmail> findBySendAddressAndSendYn(@Param("sendAddress") String sendAddress, @Param("sendYn") Boolean sendYn);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/SystemRolePermissionRepository.java b/src/main/java/kr/wisestone/owl/repository/SystemRolePermissionRepository.java
new file mode 100644
index 0000000..bfe537e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/SystemRolePermissionRepository.java
@@ -0,0 +1,9 @@
+
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.SystemRolePermission;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface SystemRolePermissionRepository extends JpaRepository<SystemRolePermission, Long> {
+
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/SystemRoleRepository.java b/src/main/java/kr/wisestone/owl/repository/SystemRoleRepository.java
new file mode 100644
index 0000000..c334bf9
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/SystemRoleRepository.java
@@ -0,0 +1,10 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.SystemRole;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+
+public interface SystemRoleRepository extends JpaRepository<SystemRole, Long> {
+    SystemRole findByRoleType(@Param("roleType") String roleType);
+
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/SystemRoleUserRepository.java b/src/main/java/kr/wisestone/owl/repository/SystemRoleUserRepository.java
new file mode 100644
index 0000000..57e3d99
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/SystemRoleUserRepository.java
@@ -0,0 +1,8 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.SystemRoleUser;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface SystemRoleUserRepository extends JpaRepository<SystemRoleUser, Long> {
+
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/UserHistoryRepository.java b/src/main/java/kr/wisestone/owl/repository/UserHistoryRepository.java
new file mode 100644
index 0000000..19f5dfd
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/UserHistoryRepository.java
@@ -0,0 +1,8 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.SystemRoleUser;
+import kr.wisestone.owl.domain.UserHistory;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface UserHistoryRepository extends JpaRepository<UserHistory, Long> {
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/UserInviteProjectRepository.java b/src/main/java/kr/wisestone/owl/repository/UserInviteProjectRepository.java
new file mode 100644
index 0000000..a06b90e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/UserInviteProjectRepository.java
@@ -0,0 +1,10 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.UserInviteProject;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface UserInviteProjectRepository extends JpaRepository<UserInviteProject, Long> {
+    List<UserInviteProject> findByUserInviteIdAndProjectId(@Param("userInviteId") Long userInviteId, @Param("projectId") Long projectId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/UserInviteRepository.java b/src/main/java/kr/wisestone/owl/repository/UserInviteRepository.java
new file mode 100644
index 0000000..9773ead
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/UserInviteRepository.java
@@ -0,0 +1,15 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.UserInvite;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface UserInviteRepository extends JpaRepository<UserInvite, Long> {
+
+    List<UserInvite> findByEmailAndStatus(@Param("email") String email, @Param("status") String status);
+
+    UserInvite findByEmailAndWorkspaceId(@Param("email") String email, @Param("workspaceId") Long workspaceId);
+
+    List<UserInvite> findByWorkspaceIdAndEmailIn(@Param("workspaceId") Long workspaceId, @Param("emails") List<String> emails);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/UserRepository.java b/src/main/java/kr/wisestone/owl/repository/UserRepository.java
new file mode 100644
index 0000000..2176ccc
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/UserRepository.java
@@ -0,0 +1,32 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.vo.UserVo;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+public interface UserRepository extends JpaRepository<User, Long> {
+
+    User findByAccount(String account);
+
+    User findByIdNotAndAccount(@Param("id") Long id, @Param("account") String account);
+
+    List<User> findByStatus(@Param("status") String status);
+
+    @Query(name = "User.findByWorkspaceIdAndManagerYn")
+    User findByWorkspaceIdAndManagerYn(@Param("workspaceId") Long workspaceId, @Param("managerYn") Boolean managerYn);
+
+    List<User> findByIdIn(@Param("userIds") List<Long> userIds);
+
+    @Query(name = "User.findJoinDay")
+    List<User> findJoinDay(@Param("todayFrom") Date todayFrom, @Param("todayTo") Date todayTo);
+
+    @Query(name = "User.findAdmin")
+    List<User> findAdmin();
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/UserWithDrawRepository.java b/src/main/java/kr/wisestone/owl/repository/UserWithDrawRepository.java
new file mode 100644
index 0000000..ab46c5f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/UserWithDrawRepository.java
@@ -0,0 +1,11 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.UserWithDraw;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+
+public interface UserWithDrawRepository extends JpaRepository<UserWithDraw, Long> {
+
+    UserWithDraw findByAccount(@Param("account") String account);
+
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/UserWorkspaceRepository.java b/src/main/java/kr/wisestone/owl/repository/UserWorkspaceRepository.java
new file mode 100644
index 0000000..0428861
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/UserWorkspaceRepository.java
@@ -0,0 +1,27 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.UserWorkspace;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface UserWorkspaceRepository extends JpaRepository<UserWorkspace, Long> {
+
+    UserWorkspace findByUserIdAndWorkspaceId(@Param("userId") Long userId, @Param("workspaceId") Long workspaceId);
+
+    List<UserWorkspace> findByWorkspaceIdAndUseYn(@Param("workspaceId") Long workspaceId, @Param("useYn") Boolean useYn);
+
+    @Query(name = "UserWorkspace.maxDisablePosition")
+    Long maxDisablePosition(@Param("workspaceId") Long workspaceId);
+
+    List<UserWorkspace> findByWorkspaceIdAndManagerYn(@Param("workspaceId") Long workspaceId, @Param("managerYn") Boolean managerYn);
+
+    UserWorkspace findByUserIdAndManagerYn(@Param("userId") Long userId, @Param("managerYn") Boolean managerYn);
+
+    List<UserWorkspace> findByWorkspaceId(@Param("workspaceId") Long workspaceId);
+
+    List<UserWorkspace> findByWorkspaceIdAndUserIdIn(@Param("workspaceId") Long workspaceId, @Param("userIds") List<Long> userIds);
+
+}
+
diff --git a/src/main/java/kr/wisestone/owl/repository/WorkflowRepository.java b/src/main/java/kr/wisestone/owl/repository/WorkflowRepository.java
new file mode 100644
index 0000000..7e78765
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/WorkflowRepository.java
@@ -0,0 +1,21 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Workflow;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+
+public interface WorkflowRepository extends JpaRepository<Workflow, Long> {
+    Workflow findByWorkspaceIdAndProjectType(@Param("workspaceId") Long workspaceId, @Param("projectType") ProjectType projectType);
+
+    Workflow findByNameAndWorkspaceId(@Param("name") String name, @Param("workspaceId") Long workspaceId);
+
+    Workflow findByNameAndWorkspaceIdAndIdNot(@Param("name") String name, @Param("workspaceId") Long workspaceId, @Param("id") Long id);
+
+    List<Workflow> findByWorkspaceId(@Param("workspaceId") Long workspaceId);
+
+}
+
+
diff --git a/src/main/java/kr/wisestone/owl/repository/WorkflowStatusRepository.java b/src/main/java/kr/wisestone/owl/repository/WorkflowStatusRepository.java
new file mode 100644
index 0000000..2a77e07
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/WorkflowStatusRepository.java
@@ -0,0 +1,22 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.WorkflowStatus;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+/**
+ * Created by wisestone on 2018-01-03.
+ */
+public interface WorkflowStatusRepository extends JpaRepository<WorkflowStatus, Long> {
+    List<WorkflowStatus> findByProjectId(@Param("projectId") Long projectId);
+//
+//    WorkflowStatus findByIdNotAndNameAndProjectId(@Param("id") Long id, @Param("name") String name, @Param("projectId") Long projectId);
+//
+//    WorkflowStatus findByNameAndProjectId(@Param("name") String name, @Param("projectId") Long projectId);
+//
+//    @Query(name="WorkflowStatus.findMaxPosition")
+//    Long findMaxPosition(@Param("projectId") Long projectId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/WorkflowTransitionRepository.java b/src/main/java/kr/wisestone/owl/repository/WorkflowTransitionRepository.java
new file mode 100644
index 0000000..0331a91
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/WorkflowTransitionRepository.java
@@ -0,0 +1,15 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.WorkflowTransition;
+import kr.wisestone.owl.vo.WorkflowTransitionVo;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import java.util.List;
+
+public interface WorkflowTransitionRepository extends JpaRepository<WorkflowTransition, Long> {
+    List<WorkflowTransition> findByWorkflowId(@Param("workflowId") Long workflowId);
+
+    @Query(name="WorkflowTransition.findBySourceIssueStatusIdAndWorkflowId")
+    List<WorkflowTransitionVo> findBySourceIssueStatusIdAndWorkflowId(@Param("sourceIssueStatusId") Long sourceIssueStatusId, @Param("workflowId") Long workflowId);
+}
diff --git a/src/main/java/kr/wisestone/owl/repository/WorkspaceRepository.java b/src/main/java/kr/wisestone/owl/repository/WorkspaceRepository.java
new file mode 100644
index 0000000..5802735
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/WorkspaceRepository.java
@@ -0,0 +1,19 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.Workspace;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import java.util.Date;
+import java.util.List;
+
+public interface WorkspaceRepository extends JpaRepository<Workspace, Long> {
+    @Query(name="Workspace.findSubscribeImmediateExpireDate")
+    List<Workspace> findSubscribeImmediateExpireDate(@Param("todayFrom") Date todayFrom, @Param("todayTo") Date todayTo);
+
+    @Query(name="Workspace.findExpireDate")
+    List<Workspace> findExpireDate(@Param("todayFrom") Date todayFrom, @Param("todayTo") Date todayTo);
+
+    @Query(name = "Workspace.findByUserId")
+    List<Workspace> findByUserId(@Param("userId") Long userId);
+}
diff --git a/src/main/java/kr/wisestone/owl/scheduler/Scheduler.java b/src/main/java/kr/wisestone/owl/scheduler/Scheduler.java
new file mode 100644
index 0000000..e4306e1
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/scheduler/Scheduler.java
@@ -0,0 +1,98 @@
+package kr.wisestone.owl.scheduler;
+
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.config.websocket.WebSocketService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import java.util.List;
+
+
+@Component
+public class Scheduler {
+
+    /*private static final Logger log = LoggerFactory.getLogger(Scheduler.class);
+
+    @Autowired
+    private PaymentService paymentService;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private SystemEmailService systemEmailService;
+
+    @Autowired
+    private AttachedFileService attachedFileService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private IssueService issueService;
+
+    @Autowired
+    private WebSocketService webSocketService;
+
+    //  OWL ITS 愿��젴�옄�뱾�뿉寃� �쉶�썝 媛��엯 �젙蹂대�� �쟾�떖�븳�떎.
+    @Scheduled(cron = "0 0 17 * * *")
+    public void sendOwlManager() {
+        //  OWL ITS 愿��젴�옄�뱾�뿉寃� �쉶�썝 媛��엯 �젙蹂대�� �쟾�떖�븳�떎.
+        this.userService.sendUserJoinStatisticsEmail();
+
+        //  OWL ITS 愿��젴�옄�뱾�뿉寃� �떆�뒪�뀥 �쁽�솴 �젙蹂대�� �쟾�떖�븳�떎.
+        this.userService.sendTotalStatisticsEmail();
+    }
+
+    @Scheduled(cron = "0 50 23 * * *")    //  留� �떆媛� (珥�, 遺�, �떆, �씪, �썡, �뀈)
+    public void updateExchangeRatePayment() {
+        //  蹂�寃쎈맂 �솚�쑉 �젙蹂대�� 寃곗젣 湲덉븸�뿉 �뾽�뜲�씠�듃�븳�떎.
+        this.paymentService.updateExchangeRatePayment();
+    }
+
+    //   �옄�룞 寃곗옱 - �뼱�젣媛� 留뚮즺�씪�씤 �뾽臾� 怨듦컙�쓣 李얠븘�꽌 寃곗젣�븳�떎. �깉踰� 1�떆�뿉 �떎�뻾
+    @Scheduled(cron = "0 0 01 * * *")    //  留� �떆媛� (珥�, 遺�, �떆, �씪, �썡, �뀈)
+    public void subscribePayment() {
+        List<Workspace> workspaces = this.workspaceService.findSubscribeImmediateExpireDate();
+
+        for (Workspace workspace : workspaces) {
+            this.paymentService.subscribeImmediate(workspace);
+        }
+    }
+
+    //  �씠�뒋�� �뿰寃곕릺吏� �븡�� 泥⑤��뙆�씪 �궘�젣 - �씠�뒋 �깮�꽦, �닔�젙�뿉�꽌 �뿉�뵒�듃 李쎌뿉 泥⑤��뻽�떎媛� ���옣�븯吏� �븡�� �뙆�씪�뱾... �깉踰� 1�떆 30遺꾩뿉 �떎�뻾
+    @Scheduled(cron = "0 30 01 * * *")
+    public void deleteAttachedFileNotIdAndReservationIssue() {
+        //  �씠�뒋�� �뿰寃곕릺吏� �븡�� 泥⑤��뙆�씪 �궘�젣
+        this.attachedFileService.deleteAttachedFileNotId();
+        //  �씠�뒋 �삁�빟 諛쒖깮�븳 �빆紐⑹쓣 李얠븘 �씠�뒋瑜� �떎�떆 �깮�꽦 �긽�깭濡� 蹂�寃쏀븳�떎.
+        this.issueService.reservationIssue();
+    }
+
+    //  �뾽臾� 怨듦컙 �옄�룞 珥덇린�솕 - �뼱�젣媛� 留뚮즺�씪�씤 �뾽臾� 怨듦컙�쓣 李얠븘�꽌 �뾽臾� 怨듦컙 �궗�슜�옄, �궗�슜 �슜�웾�쓣 珥덇린�솕�븳�떎. �깉踰� 2�떆�뿉 �떎�뻾
+    @Scheduled(cron = "0 0 02 * * *")
+    public void expireWorkspace() {
+        //  �궗�슜 湲곌컙�씠 留뚮즺�맂 �뾽臾� 怨듦컙�쓣 李얠븘 �슜�웾, 理쒕� �궗�슜�옄, �꽌鍮꾩뒪 �쑀�삎�쓣 蹂�寃쏀븳�떎.
+        this.workspaceService.expireWorkspace();
+        //  �뾽臾� 怨듦컙 留뚮즺 �삁�젙 �븞�궡
+        this.workspaceService.expireAlarmWorkspace();
+    }
+
+    //  �씠硫붿씪 �삁�빟 諛쒖넚 - �궗�슜�옄媛� �꽕�젙�븳 �븣由� �떆媛꾩뿉 �떆�뒪�뀥�뿉�꽌 �씪�뼱�궃 �씠踰ㅽ듃瑜� �씠硫붿씪濡� 諛쒖넚�븳�떎. - 留ㅼ떆媛� 30遺꾩뿉 �떎�뻾
+    @Scheduled(cron = "0 0/30 * * * *")
+    public void smartEmailSystem() {
+        //  �삁�빟�맂 �씠硫붿씪 諛쒖넚
+        this.systemEmailService.reservationSendEmail();
+    }
+
+    //  15珥� 留덈떎 �젒�냽�옄 �솗�씤
+    @Scheduled(fixedDelay = 15000)
+    public void pingUsers() {
+        //  �젒�냽 �궗�슜�옄瑜� �솗�씤�븳�떎.
+        this.webSocketService.checkActiveUser();
+    }*/
+}
diff --git a/src/main/java/kr/wisestone/owl/search/ElasticSearch.java b/src/main/java/kr/wisestone/owl/search/ElasticSearch.java
new file mode 100644
index 0000000..2f039cd
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/search/ElasticSearch.java
@@ -0,0 +1,50 @@
+package kr.wisestone.owl.search;
+
+import kr.wisestone.owl.constant.ElasticSearchConstants;
+import kr.wisestone.owl.util.ElasticSearchUtil;
+import kr.wisestone.owl.vo.UserVo;
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.action.index.IndexRequest;
+import org.elasticsearch.action.index.IndexResponse;
+import org.elasticsearch.action.support.WriteRequest;
+import org.elasticsearch.client.RequestOptions;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.common.xcontent.XContentType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import javax.servlet.http.HttpServletRequest;
+
+@Component
+public class ElasticSearch {
+    private static final Logger log = LoggerFactory.getLogger(ElasticSearch.class);
+
+    @Autowired
+    private RestHighLevelClient restHighLevelClient;
+
+    /*//  �궗�슜�옄 �떆�뒪�뀥 �궗�슜 湲곕줉�쓣 ���옣�븳�떎.
+    public void updateUserActiveHistory(HttpServletRequest httpServletRequest, UserVo userVo) {
+        IndexRequest request = new IndexRequest(ElasticSearchConstants.USER_SESSION_HISTORY_INDEX);
+
+        request.source(ElasticSearchUtil.makeUserActiveHistoryDocument(httpServletRequest, userVo), XContentType.JSON);
+        request.timeout("5s");
+        request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
+
+        try {
+            this.restHighLevelClient.indexAsync(request, RequestOptions.DEFAULT, new ActionListener<IndexResponse>() {
+                @Override
+                public void onResponse(IndexResponse indexResponse) {
+                    log.debug("�궗�슜�옄 �떆�뒪�뀥 �궗�슜 湲곕줉 �씤�뜳�뒪 �뾽�뜲�씠�듃 �꽦怨�");
+                }
+
+                @Override
+                public void onFailure(Exception ex) {
+                    log.error("�궗�슜�옄 �떆�뒪�뀥 �궗�슜 湲곕줉 �씤�뜳�뒪 �뾽�뜲�씠�듃 �삤瑜� : " + ex.getMessage());
+                }
+            });
+        } catch (Exception ex) {
+            log.error("�궗�슜�옄 �떆�뒪�뀥 �궗�슜 湲곕줉 �씤�뜳�뒪 �뾽�뜲�씠�듃 �삤瑜� : " + ex.getMessage());
+        }
+    }*/
+}
diff --git a/src/main/java/kr/wisestone/owl/service/AbstractService.java b/src/main/java/kr/wisestone/owl/service/AbstractService.java
new file mode 100644
index 0000000..55ab4ae
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/AbstractService.java
@@ -0,0 +1,19 @@
+
+package kr.wisestone.owl.service;
+import org.springframework.data.jpa.repository.JpaRepository;
+import java.io.Serializable;
+import java.util.List;
+
+public interface AbstractService<T, ID extends Serializable, R extends JpaRepository<T, ID>> {
+    long count();
+
+    void clear();
+
+    void detach(Object entity);
+
+    void bulkInsert(List<T> entities);
+
+    T findOne(ID id);
+
+    List<T> findAll(List<ID> ids);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/AttachedFileService.java b/src/main/java/kr/wisestone/owl/service/AttachedFileService.java
new file mode 100644
index 0000000..f75d388
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/AttachedFileService.java
@@ -0,0 +1,45 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.AttachedFile;
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.vo.AttachedFileVo;
+import kr.wisestone.owl.web.condition.AttachedFileCondition;
+import kr.wisestone.owl.web.form.IssueForm;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.ui.Model;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.ModelAndView;
+import java.util.List;
+import java.util.Map;
+
+public interface AttachedFileService extends AbstractService<AttachedFile, Long, JpaRepository<AttachedFile, Long>> {
+
+    void addAttachedFile(List<Map<String, Object>> convertFileMaps, Issue issue, String userAccount);
+
+    List<AttachedFile> addAttachedFile(List<MultipartFile> multipartFiles, Map<String, Object> content);
+
+    List<AttachedFileVo> findAttachedFile(Map<String, Object> resJsonData, AttachedFileCondition condition);
+
+    List<AttachedFile> findByIssueId(Long issueId);
+
+    void connectIssueIdAttachedFile(Issue issue, IssueForm issueForm);
+
+    AttachedFile getAttachedFile(Long attachedFileId);
+
+    void removeAttachedFiles(List<Long> removeIds);
+
+    void deleteWorkspaceCascadeAttachedFile(Workspace workspace);
+
+    void deleteIssueCascadeAttachedFile(List<Long> issueIds, Workspace workspace);
+
+    Long findUseStorage(Workspace workspace);
+
+    ModelAndView checkUseWorkspaceTraffic(Long id, Model model);
+
+    void deleteAttachedFileNotId();
+
+    String uploadFile(Map<String, Object> convertFileMap, String awsUploadFolder, String userAccount, int totalFileCount, int uploadFileCount);
+
+    void removeFile(String key, Long workspaceId);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/CustomFieldService.java b/src/main/java/kr/wisestone/owl/service/CustomFieldService.java
new file mode 100644
index 0000000..7fe8cd2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/CustomFieldService.java
@@ -0,0 +1,35 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.CustomField;
+import kr.wisestone.owl.vo.CustomFieldVo;
+import kr.wisestone.owl.web.condition.CustomFieldCondition;
+import kr.wisestone.owl.web.form.CustomFieldForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+public interface CustomFieldService extends AbstractService<CustomField, Long, JpaRepository<CustomField, Long>>{
+
+    CustomField addCustomField(CustomFieldForm customFieldForm);
+
+    List<CustomFieldVo> findCustomField(Map<String, Object> resJsonData,
+                                    CustomFieldCondition condition, Pageable pageable);
+
+    void detailCustomField(Map<String, Object> resJsonData, CustomFieldCondition customFieldCondition);
+
+    CustomField modifyCustomField(CustomFieldForm customFieldForm);
+
+    CustomField getCustomField(Long id);
+
+    void removeCustomFields(CustomFieldForm customFieldForm);
+
+    CustomField findByName(String name);
+
+    ModelAndView downloadExcel(HttpServletRequest request, Model model);
+
+    List<CustomField> findByWorkspaceId();
+}
diff --git a/src/main/java/kr/wisestone/owl/service/CustomFieldValueService.java b/src/main/java/kr/wisestone/owl/service/CustomFieldValueService.java
new file mode 100644
index 0000000..8f332b6
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/CustomFieldValueService.java
@@ -0,0 +1,13 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.CustomField;
+import kr.wisestone.owl.domain.CustomFieldValue;
+import kr.wisestone.owl.domain.enumType.CustomFieldType;
+import org.springframework.data.jpa.repository.JpaRepository;
+import java.util.List;
+
+public interface CustomFieldValueService extends AbstractService<CustomFieldValue, Long, JpaRepository<CustomFieldValue, Long>>{
+
+    void addCustomFieldValues(CustomField customField, List<String> values, CustomFieldType oldCustomFieldType);
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/EventService.java b/src/main/java/kr/wisestone/owl/service/EventService.java
new file mode 100644
index 0000000..7d21db2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/EventService.java
@@ -0,0 +1,25 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Event;
+import kr.wisestone.owl.vo.EventVo;
+import kr.wisestone.owl.web.condition.EventCondition;
+import kr.wisestone.owl.web.form.EventForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface EventService extends AbstractService<Event, Long, JpaRepository<Event, Long>> {
+    Event addEvent(EventForm eventForm);
+
+    List<EventVo> findEvent(Map<String, Object> resJsonData,
+                            EventCondition eventCondition, Pageable pageable);
+
+    Event getEvent(Long id);
+
+    Event modifyEvent(EventForm eventForm);
+    Event activeEvent(EventForm eventForm);
+
+    void detailEvent(Map<String, Object> resJsonData, EventCondition eventCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/FaqService.java b/src/main/java/kr/wisestone/owl/service/FaqService.java
new file mode 100644
index 0000000..f969424
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/FaqService.java
@@ -0,0 +1,27 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Faq;
+import kr.wisestone.owl.domain.Guide;
+import kr.wisestone.owl.vo.FaqVo;
+import kr.wisestone.owl.web.condition.FaqCondition;
+import kr.wisestone.owl.web.form.FaqForm;
+import kr.wisestone.owl.web.form.GuideForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface FaqService extends AbstractService<Faq, Long, JpaRepository<Faq, Long>> {
+    Faq addFaq(FaqForm faqForm);
+
+    List<FaqVo> findFaq(Map<String, Object> resJsonData,
+                              FaqCondition faqCondition, Pageable pageable);
+
+    Faq getFaq(Long id);
+
+    Faq modifyFaq(FaqForm faqForm);
+    Faq activeFaq(FaqForm faqForm);
+
+    void detailFaq(Map<String, Object> resJsonData, FaqCondition faqCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/GanttService.java b/src/main/java/kr/wisestone/owl/service/GanttService.java
new file mode 100644
index 0000000..ae3e0f4
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/GanttService.java
@@ -0,0 +1,36 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.IssueType;
+import kr.wisestone.owl.domain.Workflow;
+import kr.wisestone.owl.vo.IssueVo;
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.form.IssueForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.ui.Model;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.ModelAndView;
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+public interface GanttService extends AbstractService<Issue, Long, JpaRepository<Issue, Long>>{
+
+    void addIssueVersion(Long id);
+
+    Issue addIssue(IssueForm issueForm, List<MultipartFile> files);
+
+    List<IssueVo> findIssue(Map<String, Object> resJsonData,
+                            IssueCondition condition, Pageable pageable);
+
+    List<IssueVo> findIssue(Map<String, Object> resJsonData,
+                            ProjectCondition projectCondition, Pageable pageable);
+
+    void detailIssue(Map<String, Object> resJsonData, IssueCondition issueCondition);
+
+    Issue modifyIssue(IssueForm issueForm, List<MultipartFile> files);
+
+    void removeIssues(IssueForm issueForm);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/GuideService.java b/src/main/java/kr/wisestone/owl/service/GuideService.java
new file mode 100644
index 0000000..2f7db78
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/GuideService.java
@@ -0,0 +1,25 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Guide;
+import kr.wisestone.owl.vo.GuideVo;
+import kr.wisestone.owl.web.condition.GuideCondition;
+import kr.wisestone.owl.web.form.GuideForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface GuideService extends AbstractService<Guide, Long, JpaRepository<Guide, Long>> {
+    Guide addGuide(GuideForm guideForm);
+
+    List<GuideVo> findGuide(Map<String, Object> resJsonData,
+                             GuideCondition guideCondition, Pageable pageable);
+
+    Guide getGuide(Long id);
+
+    Guide modifyGuide(GuideForm guideForm);
+    Guide activeGuide(GuideForm guideForm);
+
+    void detailGuide(Map<String, Object> resJsonData, GuideCondition guideCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueCommentService.java b/src/main/java/kr/wisestone/owl/service/IssueCommentService.java
new file mode 100644
index 0000000..da38dc5
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueCommentService.java
@@ -0,0 +1,20 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.IssueComment;
+import kr.wisestone.owl.vo.IssueCommentVo;
+import kr.wisestone.owl.web.form.IssueCommentForm;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface IssueCommentService extends AbstractService<IssueComment, Long, JpaRepository<IssueComment, Long>> {
+
+    IssueComment addIssueComment(IssueCommentForm issueCommentForm);
+
+    void removeIssueComments(IssueCommentForm issueCommentForm);
+
+    IssueComment getIssueComment(Long id );
+
+    List<IssueCommentVo> findIssueComment(Long issueId);
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueCustomFieldValueService.java b/src/main/java/kr/wisestone/owl/service/IssueCustomFieldValueService.java
new file mode 100644
index 0000000..28c6e08
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueCustomFieldValueService.java
@@ -0,0 +1,33 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.CustomField;
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.IssueCustomFieldValue;
+import kr.wisestone.owl.domain.enumType.CustomFieldType;
+import kr.wisestone.owl.vo.IssueCustomFieldValueVo;
+import kr.wisestone.owl.web.condition.IssueCondition;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public interface IssueCustomFieldValueService extends AbstractService<IssueCustomFieldValue, Long, JpaRepository<IssueCustomFieldValue, Long>>{
+    void modifyIssueCustomFieldValue(Issue issue, List<Map<String, Object>> issueCustomFields);
+
+    void getCustomFieldAndIssueTypeCustomField(Map<String, Object> map, Issue issue, Map<String, Object> result);
+
+    List<IssueCustomFieldValueVo> findByIssueId(Long issueId);
+
+    void checkExistIssueCustomFieldValue(CustomField customField, List<String> values, CustomFieldType oldCustomFieldType);
+
+    List<IssueCustomFieldValue> findByCustomFieldId(CustomField customField);
+
+    void removeIssueCustomFieldValue(Long issueTypeCustomFieldId);
+
+    boolean find(IssueCondition condition, Set<String> issueIds);
+
+    List<Map<String, Object>> findInIssueIds(IssueCondition issueCondition);
+
+    void removeIssueCustomFieldValuesByCustomFieldId(CustomField customField);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueHistoryService.java b/src/main/java/kr/wisestone/owl/service/IssueHistoryService.java
new file mode 100644
index 0000000..47bf34f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueHistoryService.java
@@ -0,0 +1,56 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.IssueHistoryType;
+import kr.wisestone.owl.service.impl.IssueHistoryServiceImpl;
+import kr.wisestone.owl.vo.IssueHistoryVo;
+import kr.wisestone.owl.web.condition.IssueHistoryCondition;
+import kr.wisestone.owl.web.form.IssueForm;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.web.multipart.MultipartFile;
+import java.util.List;
+import java.util.Map;
+
+
+
+public interface IssueHistoryService extends AbstractService<IssueHistory, Long, JpaRepository<IssueHistory, Long>>{
+
+    void addIssueHistory(Issue issue, IssueHistoryType issueHistoryType, String issueChangeDescription);
+
+    void makeDescription(StringBuilder description, IssueHistoryType issueHistoryType, String issueChangeDescription);
+
+    void findIssueHistory(Map<String, Object> resJsonData, IssueHistoryCondition issueHistoryCondition);
+
+    List<IssueHistoryVo> findIssueHistory(Long issueId);
+
+    StringBuilder detectIssueChange(Issue issue, IssueForm issueForm, Project project, IssueStatus issueStatus, IssueType issueType, Priority priority, Severity severity, List<MultipartFile> files);
+
+    void detectProject(Issue issue, IssueForm issueForm, StringBuilder description, Project project);
+
+    void detectIssueSeverity(Issue issue, IssueForm issueForm, StringBuilder description, Severity severity);
+
+    void detectIssuePriority(Issue issue, IssueForm issueForm, StringBuilder description, Priority priority);
+
+    void detectIssueStatus(Issue issue, IssueForm issueForm, StringBuilder description, IssueStatus issueStatus);
+
+    void detectReservationIssueStatus(Issue issue, StringBuilder description, IssueStatus issueStatus);
+
+    void recordRemoveWorkflowToIssueStatus(String oldIssueStatusName, String newIssueStatusName, StringBuilder description);
+
+    void detectIssueType(Issue issue, IssueForm issueForm, StringBuilder description, IssueType issueType);
+
+    void detectIssuePeriod(Issue issue, IssueForm issueForm, StringBuilder description);
+
+    void detectIssueManager(Issue issue, IssueForm issueForm, StringBuilder description);
+
+    void detectAttachedFile(IssueForm issueForm, StringBuilder description, List<MultipartFile> files);
+
+    void detectCustomField(Issue issue, IssueForm issueForm, StringBuilder description);
+
+    void detectRelationIssue(IssueHistoryType type, IssueRelation issueRelation, StringBuilder description);
+
+    void recodeRemoveCustomFieldOptionValue(CustomField customField, String oldValue, String newValue, StringBuilder description);
+
+    void recodeChangeCustomFieldType(CustomField customField, String oldValue, StringBuilder description);
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueNumberGeneratorService.java b/src/main/java/kr/wisestone/owl/service/IssueNumberGeneratorService.java
new file mode 100644
index 0000000..6f4baa0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueNumberGeneratorService.java
@@ -0,0 +1,13 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.IssueNumberGenerator;
+import kr.wisestone.owl.domain.Project;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Map;
+
+public interface IssueNumberGeneratorService extends AbstractService<IssueNumberGenerator, Long, JpaRepository<IssueNumberGenerator, Long>>{
+    Long generateIssueNumber(Project project);
+
+    void updateIssueNumber(Map<Long, Long> issueNumberMaps);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueRelationService.java b/src/main/java/kr/wisestone/owl/service/IssueRelationService.java
new file mode 100644
index 0000000..f7ab0df
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueRelationService.java
@@ -0,0 +1,22 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.IssueRelation;
+import kr.wisestone.owl.vo.IssueRelationVo;
+import kr.wisestone.owl.vo.IssueVo;
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.IssueRelationCondition;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface IssueRelationService extends AbstractService<IssueRelation, Long, JpaRepository<IssueRelation, Long>>{
+    void addRelationIssue(Map<String, Object> resJsonData, IssueRelationCondition condition);
+    List<IssueVo> findRelationIssue(Map<String, Object> resJsonData,
+                                    IssueRelationCondition condition, Pageable pageable);
+
+    List<IssueVo> findRelationIssue(Long issueId);
+
+    boolean removeRelationIssue(Map<String, Object> resJsonData, IssueRelationCondition condition);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueReservationService.java b/src/main/java/kr/wisestone/owl/service/IssueReservationService.java
new file mode 100644
index 0000000..f98b938
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueReservationService.java
@@ -0,0 +1,20 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.IssueReservation;
+import kr.wisestone.owl.web.condition.IssueReservationCondition;
+import kr.wisestone.owl.web.form.IssueReservationForm;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface IssueReservationService extends AbstractService<IssueReservation, Long, JpaRepository<IssueReservation, Long>>{
+
+    void detailIssueReservation(Map<String, Object> resJsonData, IssueReservationCondition issueReservationCondition);
+
+    IssueReservation getIssueReservation(Long id);
+
+    void modifyIssueReservation(Map<String, Object> resJsonData, IssueReservationForm issueReservationForm);
+
+    List<IssueReservation> findByIssueReservationTypeNotNull();
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueRiskService.java b/src/main/java/kr/wisestone/owl/service/IssueRiskService.java
new file mode 100644
index 0000000..342ace2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueRiskService.java
@@ -0,0 +1,13 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.IssueRisk;
+import kr.wisestone.owl.domain.Workspace;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface IssueRiskService extends AbstractService<IssueRisk, Long, JpaRepository<IssueRisk, Long>>{
+
+    IssueRisk addIssueRisk(Issue issue, Workspace workspace);
+
+    void modifyIssueRisk(Issue issue, Boolean changeIssueStatus, Boolean changeAssignee, Long issueStatusId);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueSearchService.java b/src/main/java/kr/wisestone/owl/service/IssueSearchService.java
new file mode 100644
index 0000000..2a8ebc0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueSearchService.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.IssueSearch;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Map;
+
+public interface IssueSearchService extends AbstractService<IssueSearch, Long, JpaRepository<IssueSearch, Long>>{
+    IssueSearch addIssueSearch(Map<String, Object> params);
+
+    IssueSearch findByUserIdAndWorkspaceId();
+
+    void detailIssueSearch(Map<String, Object> resJsonData);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueService.java b/src/main/java/kr/wisestone/owl/service/IssueService.java
new file mode 100644
index 0000000..59e6ba4
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueService.java
@@ -0,0 +1,69 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.IssueType;
+import kr.wisestone.owl.domain.Workflow;
+import kr.wisestone.owl.vo.IssueVo;
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.form.IssueForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.ui.Model;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.ModelAndView;
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+public interface IssueService extends AbstractService<Issue, Long, JpaRepository<Issue, Long>>{
+
+    void addIssueVersion(Long id);
+
+    Issue addIssue(IssueForm issueForm, List<MultipartFile> files);
+
+    List<IssueVo> findIssue(Map<String, Object> resJsonData,
+                            IssueCondition condition, Pageable pageable);
+
+    List<IssueVo> findChartIssue(Map<String, Object> resJsonData,
+                            IssueCondition condition, Pageable pageable);
+
+    List<IssueVo> findChartIssue(Map<String, Object> resJsonData,
+                                 ProjectCondition condition, Pageable pageable);
+
+    void detailIssue(Map<String, Object> resJsonData, IssueCondition issueCondition);
+
+    Issue modifyIssue(IssueForm issueForm, List<MultipartFile> files);
+
+    void removeIssues(IssueForm issueForm);
+
+    void modifyIssueStatus(IssueForm issueForm);
+
+    Issue getIssue(Long taskId);
+
+    long countByIssueTypeId(Long issueTypeId);
+
+    long countByIssueStatus(Long issueStatusId);
+
+    void changeWorkflows(Workflow workflow, IssueType issueType);
+
+    ModelAndView downloadExcel(HttpServletRequest request, Model model);
+
+    void modifyMultiIssueStatus(IssueForm issueForm);
+
+    void modifyIssueUser(IssueForm issueForm);
+
+    ModelAndView downloadExcelTemplate(HttpServletRequest request, Model model);
+
+    void importExcel(MultipartFile multipartFile) throws Exception;
+
+    List<Long> findByProjectId(Long projectId);
+
+    void setIssueDetail(IssueVo issueVo, Issue issue);
+
+    void sendIssueEmail(IssueForm issueForm);
+
+    void reservationIssue();
+
+    Map<String, Object> findTask(IssueCondition taskCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueStatusService.java b/src/main/java/kr/wisestone/owl/service/IssueStatusService.java
new file mode 100644
index 0000000..b972cf5
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueStatusService.java
@@ -0,0 +1,51 @@
+package kr.wisestone.owl.service;
+
+
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.IssueStatus;
+import kr.wisestone.owl.domain.Workflow;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.vo.IssueStatusVo;
+import kr.wisestone.owl.web.condition.IssueStatusCondition;
+import kr.wisestone.owl.web.form.IssueStatusForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+public interface IssueStatusService extends AbstractService<IssueStatus, Long, JpaRepository<IssueStatus, Long>>{
+
+    List<IssueStatus> addDefaultIssueStatus(Workspace workspace);
+
+    List<IssueStatus> findByWorkspaceId(Long workspaceId);
+
+    IssueStatus addIssueStatus(IssueStatusForm issueStatusForm);
+
+    List<IssueStatusVo> findIssueStatus(Map<String, Object> resJsonData,
+                                        IssueStatusCondition issueStatusCondition, Pageable pageable);
+
+    List<IssueStatusVo> findIssueStatus(Map<String, Object> resJsonData, IssueStatusCondition condition);
+
+    void findNextIssueStatus(Map<String, Object> resJsonData, IssueStatusCondition condition);
+
+    void detailIssueStatus(Map<String, Object> resJsonData, IssueStatusCondition issueStatusCondition);
+
+    IssueStatus modifyIssueStatus(IssueStatusForm issueStatusForm);
+
+    IssueStatus getIssueStatus(Long id);
+
+    void removeIssueStatus(IssueStatusForm issueStatusForm);
+
+    List<IssueStatusVo> findByWorkflowId(Long workflowId);
+
+    IssueStatus findByIssueStatusTypeIsReady(Workflow workflow);
+
+    void checkNextIssueStatus(Issue issue, IssueStatus nextIssueStatus);
+
+    ModelAndView downloadExcel(HttpServletRequest request, Model model);
+
+    void findNextMultiIssueStatus(Map<String, Object> resJsonData, IssueStatusCondition condition);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueTableConfigService.java b/src/main/java/kr/wisestone/owl/service/IssueTableConfigService.java
new file mode 100644
index 0000000..64d643f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueTableConfigService.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.IssueTableConfig;
+import org.springframework.data.jpa.repository.JpaRepository;
+import java.util.Map;
+
+public interface IssueTableConfigService extends AbstractService<IssueTableConfig, Long, JpaRepository<IssueTableConfig, Long>> {
+
+    IssueTableConfig addIssueTableConfig(Map<String, Object> params);
+
+    IssueTableConfig findByUserIdAndWorkspaceId();
+
+    void detailIssueTableConfig(Map<String, Object> resJsonData);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueTypeCustomFieldService.java b/src/main/java/kr/wisestone/owl/service/IssueTypeCustomFieldService.java
new file mode 100644
index 0000000..d246363
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueTypeCustomFieldService.java
@@ -0,0 +1,21 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.IssueTypeCustomField;
+import kr.wisestone.owl.vo.IssueTypeCustomFieldVo;
+import kr.wisestone.owl.web.condition.IssueTypeCustomFieldCondition;
+import kr.wisestone.owl.web.form.IssueTypeCustomFieldForm;
+import org.springframework.data.jpa.repository.JpaRepository;
+import java.util.List;
+import java.util.Map;
+
+public interface IssueTypeCustomFieldService extends AbstractService<IssueTypeCustomField, Long, JpaRepository<IssueTypeCustomField, Long>>{
+
+    void modifyIssueTypeCustomFields(IssueTypeCustomFieldForm issueTypeCustomFieldForm);
+
+    List<IssueTypeCustomFieldVo> findIssueTypeCustomField(Map<String, Object> resJsonData, IssueTypeCustomFieldCondition condition);
+
+    List<IssueTypeCustomField> findByProjectIdAndIssueTypeId(Long projectId, Long issueTypeId);
+
+    IssueTypeCustomField findByProjectIdAndIssueTypeIdAndCustomFieldId(Long projectId, Long issueTypeId, Long customFieldId);
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueTypeService.java b/src/main/java/kr/wisestone/owl/service/IssueTypeService.java
new file mode 100644
index 0000000..d7517ea
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueTypeService.java
@@ -0,0 +1,39 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.IssueType;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import kr.wisestone.owl.vo.IssueTypeVo;
+import kr.wisestone.owl.web.condition.IssueTypeCondition;
+import kr.wisestone.owl.web.form.IssueTypeForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+
+public interface IssueTypeService extends AbstractService<IssueType, Long, JpaRepository<IssueType, Long>>{
+
+    void addDefaultIssueType(Workspace workspace, List<ProjectType> projectTypes);
+
+    IssueType addIssueType(IssueTypeForm issueTypeForm);
+
+    List<IssueTypeVo> findIssueType(Map<String, Object> resJsonData,
+                                    IssueTypeCondition condition, Pageable pageable);
+
+    void detailIssueType(Map<String, Object> resJsonData, IssueTypeCondition issueTypeCondition);
+
+    IssueType modifyIssueType(IssueTypeForm issueTypeForm);
+
+    IssueType getIssueType(Long id);
+
+    void removeIssueTypes(IssueTypeForm issueTypeForm);
+
+    List<IssueType> findByWorkspaceId();
+
+    ModelAndView downloadExcel(HttpServletRequest request, Model model);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueUserService.java b/src/main/java/kr/wisestone/owl/service/IssueUserService.java
new file mode 100644
index 0000000..ea6f8d0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueUserService.java
@@ -0,0 +1,20 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.IssueUser;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.web.form.IssueForm;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface IssueUserService extends AbstractService<IssueUser, Long, JpaRepository<IssueUser, Long>>{
+    void modifyIssueUser(Issue issue, Workspace workspace, List<Long> userIds);
+
+    void insertIssueUser(List<Map<String, Long>> issueAssigneeMaps);
+
+    void removeIssueUser(Long projectId, List<Long> excludeUserIds);
+
+    List<IssueUser> find(Issue issue);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/IssueVersionService.java b/src/main/java/kr/wisestone/owl/service/IssueVersionService.java
new file mode 100644
index 0000000..2c8728b
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/IssueVersionService.java
@@ -0,0 +1,16 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.IssueVersion;
+import kr.wisestone.owl.web.condition.IssueVersionCondition;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Map;
+
+
+public interface IssueVersionService extends AbstractService<IssueVersion, Long, JpaRepository<IssueVersion, Long>>{
+
+    void addIssueVersion(Issue issue);
+
+    void find(Map<String, Object> resJsonData, IssueVersionCondition issueVersionCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/ManageUserService.java b/src/main/java/kr/wisestone/owl/service/ManageUserService.java
new file mode 100644
index 0000000..5c42b35
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/ManageUserService.java
@@ -0,0 +1,23 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.UserWorkspace;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.vo.ManageUserVo;
+import kr.wisestone.owl.vo.UserWorkspaceVo;
+import kr.wisestone.owl.web.condition.UserWorkspaceCondition;
+import kr.wisestone.owl.web.form.ManageUserForm;
+import kr.wisestone.owl.web.form.UserWorkspaceForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface ManageUserService extends AbstractService<UserWorkspace, Long, JpaRepository<UserWorkspace, Long>> {
+
+    List<ManageUserVo> findUserPermission(Map<String, Object> resJsonData,
+                                         UserWorkspaceCondition condition, Pageable pageable);
+
+    void modifyUserPermission(ManageUserForm manageUserForm);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/NoticeService.java b/src/main/java/kr/wisestone/owl/service/NoticeService.java
new file mode 100644
index 0000000..c56192f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/NoticeService.java
@@ -0,0 +1,26 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Notice;
+import kr.wisestone.owl.vo.NoticeVo;
+import kr.wisestone.owl.web.condition.NoticeCondition;
+import kr.wisestone.owl.web.form.NoticeForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface NoticeService extends AbstractService<Notice, Long, JpaRepository<Notice, Long>> {
+    Notice addNotice(NoticeForm noticeForm);
+
+    List<NoticeVo> findNotice(Map<String, Object> resJsonData,
+                              NoticeCondition noticeCondition, Pageable pageable);
+
+    Notice getNotice(Long id);
+
+    Notice modifyNotice(NoticeForm noticeForm);
+
+    void detailNotice(Map<String, Object> resJsonData, NoticeCondition noticeCondition);
+
+    void sendNotice(NoticeForm noticeForm);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/PaymentHistoryService.java b/src/main/java/kr/wisestone/owl/service/PaymentHistoryService.java
new file mode 100644
index 0000000..b2c9bc0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/PaymentHistoryService.java
@@ -0,0 +1,15 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.PaymentHistory;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.service.impl.PaymentServiceImpl;
+import kr.wisestone.owl.web.form.PaymentForm;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface PaymentHistoryService extends AbstractService<PaymentHistory, Long, JpaRepository<PaymentHistory, Long>>{
+    PaymentHistory addPaymentHistory(PaymentServiceImpl.RestClientResultObject resultObject, PaymentForm paymentForm, Workspace workspace);
+
+    PaymentHistory cancelPaymentHistory(PaymentForm paymentForm, Workspace workspace, String reason);
+
+    PaymentHistory findByWorkspaceLastPaymentHistory(Workspace workspace);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/PaymentService.java b/src/main/java/kr/wisestone/owl/service/PaymentService.java
new file mode 100644
index 0000000..956b1b2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/PaymentService.java
@@ -0,0 +1,29 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Payment;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.vo.PaymentVo;
+import kr.wisestone.owl.web.form.PaymentForm;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Map;
+
+public interface PaymentService extends AbstractService<Payment, Long, JpaRepository<Payment, Long>>{
+    String getAccessToken();
+
+    void immediateAddUser(PaymentForm paymentForm);
+
+    void paymentOneTime(PaymentForm paymentForm);
+
+    void cancelNextPayment(PaymentForm paymentForm);
+
+    void detailPayment(Map<String, Object> resJsonData, PaymentForm paymentForm);
+
+    PaymentVo modifyPayment(PaymentForm paymentForm);
+
+    void subscribeImmediate(Workspace workspace);
+
+    Payment getPayment(Long id);
+
+    void updateExchangeRatePayment();
+}
diff --git a/src/main/java/kr/wisestone/owl/service/PermissionService.java b/src/main/java/kr/wisestone/owl/service/PermissionService.java
new file mode 100644
index 0000000..55d4937
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/PermissionService.java
@@ -0,0 +1,13 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Permission;
+import kr.wisestone.owl.vo.PermissionVo;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface PermissionService extends AbstractService<Permission, Long, JpaRepository<Permission, Long>>{
+    List<PermissionVo> findByUserId();
+
+    List<Permission> findByRoleType(String roleType);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/PriorityService.java b/src/main/java/kr/wisestone/owl/service/PriorityService.java
new file mode 100644
index 0000000..ab14c86
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/PriorityService.java
@@ -0,0 +1,21 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Priority;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.vo.PriorityVo;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface PriorityService extends AbstractService<Priority, Long, JpaRepository<Priority, Long>>{
+    List<Priority> findByWorkspaceIdOrderByPosition();
+
+    Priority getPriority(Long id);
+
+    List<PriorityVo> findPriority(Map<String, Object> resJsonData);
+
+    void addDefaultPriority(Workspace workspace);
+
+    List<Priority> findByWorkspaceId();
+}
diff --git a/src/main/java/kr/wisestone/owl/service/ProjectClosureService.java b/src/main/java/kr/wisestone/owl/service/ProjectClosureService.java
new file mode 100644
index 0000000..e9371fe
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/ProjectClosureService.java
@@ -0,0 +1,8 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.ProjectClosure;
+import kr.wisestone.owl.domain.ProjectRole;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface ProjectClosureService extends AbstractService<ProjectClosure, Long, JpaRepository<ProjectClosure, Long>>{
+}
diff --git a/src/main/java/kr/wisestone/owl/service/ProjectRolePermissionService.java b/src/main/java/kr/wisestone/owl/service/ProjectRolePermissionService.java
new file mode 100644
index 0000000..30811b9
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/ProjectRolePermissionService.java
@@ -0,0 +1,10 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.ProjectRole;
+import kr.wisestone.owl.domain.ProjectRolePermission;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface ProjectRolePermissionService extends AbstractService<ProjectRolePermission, Long, JpaRepository<ProjectRolePermission, Long>>{
+
+    void addDefaultProjectRoleAssociatedPermissions(ProjectRole projectRole, String roleType);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/ProjectRoleService.java b/src/main/java/kr/wisestone/owl/service/ProjectRoleService.java
new file mode 100644
index 0000000..7940a56
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/ProjectRoleService.java
@@ -0,0 +1,15 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Project;
+import kr.wisestone.owl.domain.ProjectRole;
+import kr.wisestone.owl.domain.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface ProjectRoleService extends AbstractService<ProjectRole, Long, JpaRepository<ProjectRole, Long>>{
+
+    void addDefaultProjectRole(Project project, List<User> managers, List<User> users);
+
+    ProjectRole findByProjectIdAndRoleType(Long projectId, String roleType);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/ProjectRoleUserService.java b/src/main/java/kr/wisestone/owl/service/ProjectRoleUserService.java
new file mode 100644
index 0000000..7c58a23
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/ProjectRoleUserService.java
@@ -0,0 +1,22 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Project;
+import kr.wisestone.owl.domain.ProjectRoleUser;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.Workspace;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface ProjectRoleUserService extends AbstractService<ProjectRoleUser, Long, JpaRepository<ProjectRoleUser, Long>>{
+    List<ProjectRoleUser> findByProjectRoleId(Long projectRoleId);
+
+    ProjectRoleUser findByProjectRoleIdAndUserId(Long projectRoleId, Long userId);
+
+    void withDrawWorkspaceManagerModifyProjectRole(Workspace workspace, User user);
+
+    List<Map<String, Object>> findProjectRoleUser(Map<String, Object> projectRoleUserMap);
+
+    boolean checkProjectManager(Project project);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/ProjectService.java b/src/main/java/kr/wisestone/owl/service/ProjectService.java
new file mode 100644
index 0000000..1dec201
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/ProjectService.java
@@ -0,0 +1,53 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Project;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.vo.ProjectVo;
+import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.form.ProjectForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+public interface ProjectService extends AbstractService<Project, Long, JpaRepository<Project, Long>>{
+
+    Project addDefaultProject(User user, Workspace workspace);
+
+    Project addProject(ProjectForm projectForm);
+
+    List<ProjectVo> findProject(Map<String, Object> resJsonData,
+                                ProjectCondition condition, Pageable pageable);
+
+    void detailProject(Map<String, Object> resJsonData, ProjectCondition projectCondition);
+
+    Project findByProjectKey(String projectKey);
+
+    Project modifyProject(ProjectForm projectForm);
+
+    Project getProject(Long id);
+
+    void removeProjects(ProjectForm projectForm);
+
+    List<Project> findByWorkspaceId();
+
+    List<Map<String, Object>> findByWorkspaceIdAndIncludeProject(List<String> statuses, String projectType);
+    List<Map<String, Object>> findByWorkspaceIdAndIncludeProject(ProjectCondition projectCondition);
+
+    List<Map<String, Object>> findByWorkspaceIdAndIncludeProjectAll(List<String> statuses, String projectType);
+
+    List<Map<String, Object>> findByWorkspaceManagerAll();
+
+    List<Map<String, Object>> findByWorkspaceIdAndIncludeProjectAll(ProjectCondition projectCondition);
+
+    List<ProjectVo> findByIncludeProject(List<String> statuses, String projectType);
+
+    ModelAndView downloadExcel(HttpServletRequest request, Model model);
+
+    void findLastUseProject(Map<String, Object> resJsonData);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/QnaService.java b/src/main/java/kr/wisestone/owl/service/QnaService.java
new file mode 100644
index 0000000..8a4302c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/QnaService.java
@@ -0,0 +1,24 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Qna;
+import kr.wisestone.owl.vo.QnaVo;
+import kr.wisestone.owl.web.condition.QnaCondition;
+import kr.wisestone.owl.web.form.QnaForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface QnaService extends AbstractService<Qna, Long, JpaRepository<Qna, Long>> {
+    Qna addQna(QnaForm qnaForm);
+
+    List<QnaVo> findQna(Map<String, Object> resJsonData,
+                              QnaCondition qnaCondition, Pageable pageable);
+
+    Qna getQna(Long id);
+
+    Qna modifyQna(QnaForm qnaForm);
+
+    void detailQna(Map<String, Object> resJsonData, QnaCondition qnaCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/ReservationDisableUserService.java b/src/main/java/kr/wisestone/owl/service/ReservationDisableUserService.java
new file mode 100644
index 0000000..5c2d462
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/ReservationDisableUserService.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Payment;
+import kr.wisestone.owl.domain.ReservationDisableUser;
+import kr.wisestone.owl.web.form.PaymentForm;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Map;
+
+public interface ReservationDisableUserService extends AbstractService<ReservationDisableUser, Long, JpaRepository<ReservationDisableUser, Long>>{
+    void add(PaymentForm paymentForm, Payment payment);
+
+    void findReservationDisableUser(Map<String, Object> params, Map<String, Object> resJsonData);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/SeverityService.java b/src/main/java/kr/wisestone/owl/service/SeverityService.java
new file mode 100644
index 0000000..d973ed1
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/SeverityService.java
@@ -0,0 +1,22 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Severity;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.vo.SeverityVo;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface SeverityService extends AbstractService<Severity, Long, JpaRepository<Severity, Long>>{
+    List<Severity> findByWorkspaceIdOrderByPosition();
+
+    Severity getSeverity(Long id);
+
+    List<SeverityVo> findSeverity(Map<String, Object> resJsonData);
+
+    void addDefaultSeverity(Workspace workspace);
+
+    List<Severity> findByWorkspaceId();
+}
+
diff --git a/src/main/java/kr/wisestone/owl/service/SystemEmailService.java b/src/main/java/kr/wisestone/owl/service/SystemEmailService.java
new file mode 100644
index 0000000..cd86e74
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/SystemEmailService.java
@@ -0,0 +1,26 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.SystemEmail;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.enumType.EmailType;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface SystemEmailService extends AbstractService<SystemEmail, Long, JpaRepository<SystemEmail, Long>>{
+
+    void directEmail(String[] sendUsers, EmailType emailType, Map<String, Object> content, String toUser);
+
+    void sendEmail(String subject, String content, String[] to, String[] filePaths);
+
+    List<String> notificationUserChange(List<User> totalUsers, List<User> targetUsers);
+
+    void reservationEmail(String[] sendUsers, EmailType emailType, Map<String, Object> params);
+
+    List<SystemEmail> findBySendAddressAndSendYn(String sendAddress);
+
+    void reservationSendEmail();
+
+    void information(Map<String, Object> params);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/SystemRoleService.java b/src/main/java/kr/wisestone/owl/service/SystemRoleService.java
new file mode 100644
index 0000000..1835b5d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/SystemRoleService.java
@@ -0,0 +1,8 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.SystemRole;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface SystemRoleService extends AbstractService<SystemRole, Long, JpaRepository<SystemRole, Long>>{
+    SystemRole findByRoleType(String roleType);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/UserHistoryService.java b/src/main/java/kr/wisestone/owl/service/UserHistoryService.java
new file mode 100644
index 0000000..0a56d35
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/UserHistoryService.java
@@ -0,0 +1,11 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.UserHistory;
+import kr.wisestone.owl.web.condition.UserHistoryCondition;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Map;
+
+public interface UserHistoryService extends AbstractService<UserHistory, Long, JpaRepository<UserHistory, Long>>{
+    void addUserHistory(Map<String, Object> resJsonData, UserHistoryCondition userHistoryCondition);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/UserInviteProjectService.java b/src/main/java/kr/wisestone/owl/service/UserInviteProjectService.java
new file mode 100644
index 0000000..4bb71c8
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/UserInviteProjectService.java
@@ -0,0 +1,11 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.UserInvite;
+import kr.wisestone.owl.domain.UserInviteProject;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface UserInviteProjectService extends AbstractService<UserInviteProject, Long, JpaRepository<UserInviteProject, Long>> {
+    List<UserInviteProject> addUserInviteProject(List<Long> projectIds, UserInvite userInvite);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/UserInviteService.java b/src/main/java/kr/wisestone/owl/service/UserInviteService.java
new file mode 100644
index 0000000..fabc1f8
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/UserInviteService.java
@@ -0,0 +1,19 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.UserInvite;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.web.form.UserInviteForm;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface UserInviteService extends AbstractService<UserInvite, Long, JpaRepository<UserInvite, Long>> {
+    void inviteWorkspace(UserInviteForm userInviteForm);
+
+    void checkInviteUser(User user);
+
+    void deleteUserInvite(Long workspaceId, List<String> email);
+
+    void includePrimaryWorkspace(User user, Workspace workspace);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/UserService.java b/src/main/java/kr/wisestone/owl/service/UserService.java
new file mode 100644
index 0000000..70af5fa
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/UserService.java
@@ -0,0 +1,84 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Project;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.domain.enumType.SocialType;
+import kr.wisestone.owl.vo.UserVo;
+import kr.wisestone.owl.web.condition.UserCondition;
+import kr.wisestone.owl.web.form.UserForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.ui.Model;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+public interface UserService extends AbstractService<User, Long, JpaRepository<User, Long>> {
+
+    User findByAccount(String account);
+
+    User addUser(UserForm userForm, MultipartFile profile);
+
+    List<UserVo> findUser(Map<String, Object> resJsonData,
+                          UserCondition condition, Pageable pageable);
+
+    List<User> findAdmin();
+
+    User getUser(Long userId);
+
+    void detailUser(Map<String, Object> resJsonData, UserCondition userCondition);
+
+    void modifyUser(UserForm userForm, MultipartFile profile);
+
+    void modifyPassword(UserForm userForm);
+
+    void returnEmailPassword(UserForm userForm);
+
+    ModelAndView getOAuthToken(String code, String state, SocialType socialType, HttpServletRequest request);
+
+    List<User> findByIdIn(List<Long> userIds);
+
+    void updateLastDefaultWorkspace(Workspace workspace, User user);
+
+    User findByWorkspaceIdAndManagerYn(Workspace workspace);
+
+    void updateLastWorkspace(Map<String, Object> resJsonData, UserForm userForm);
+
+    void updateLastProject(Map<String, Object> resJsonData, UserForm userForm);
+
+    void updateLastMyWorkspace(User user);
+
+    void findProjectMember(Map<String, Object> resJsonData, UserCondition userCondition);
+
+    void withDrawUser();
+
+    void autoLogin(String email, HttpServletRequest request);
+
+    void updateUserSession();
+
+    User getUserSession(Map<String, Object> resJsonData, HttpServletRequest httpServletRequest);
+
+    List<String> findByReservationNotifyTime();
+
+    List<Map<String, Object>> findProjectMember(Project project);
+
+    void updateLanguage(String language);
+
+    void sendUserJoinStatisticsEmail();
+
+    void sendTotalStatisticsEmail();
+
+    List<UserVo> findByAllWorkspace(Map<String, Object> resJsonData, UserCondition condition, Pageable pageable);
+
+    ModelAndView downloadExcel(Model model);
+
+    UserVo removeSensitiveUser(Long userId);
+
+    void updateLastLogin();
+
+    //ModelAndView downloadExcelEvent(Model model);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/UserWithDrawService.java b/src/main/java/kr/wisestone/owl/service/UserWithDrawService.java
new file mode 100644
index 0000000..e2fcf94
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/UserWithDrawService.java
@@ -0,0 +1,11 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.UserWithDraw;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface UserWithDrawService extends AbstractService<UserWithDraw, Long, JpaRepository<UserWithDraw, Long>> {
+    void addUserWithDraw(User user);
+
+    UserWithDraw findByAccount(String account);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/UserWorkspaceService.java b/src/main/java/kr/wisestone/owl/service/UserWorkspaceService.java
new file mode 100644
index 0000000..dbe05f2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/UserWorkspaceService.java
@@ -0,0 +1,43 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.UserWorkspace;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.vo.UserWorkspaceVo;
+import kr.wisestone.owl.web.condition.UserWorkspaceCondition;
+import kr.wisestone.owl.web.form.UserWorkspaceForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public interface UserWorkspaceService extends AbstractService<UserWorkspace, Long, JpaRepository<UserWorkspace, Long>> {
+
+    UserWorkspace addUserWorkspace(User user, Workspace workspace, Boolean managerYn, Boolean useYn);
+
+    List<UserWorkspaceVo> findUserWorkspace(Map<String, Object> resJsonData,
+                                            UserWorkspaceCondition condition, Pageable pageable);
+
+    void modifyUserWorkspace(UserWorkspaceForm userWorkspaceForm);
+
+    UserWorkspace findByUserIdAndWorkspaceId(Long userId, Long workspaceId);
+
+    Integer countByWorkspaceIdAndUseYn(Long workspaceId, Boolean useYn);
+
+    List<UserWorkspace> findByWorkspaceIdAndUseYn(Long workspaceId, Boolean useYn);
+
+    List<UserWorkspace> findByWorkspaceIdAndManagerYn(Long workspaceId, Boolean managerYn);
+
+    UserWorkspace findMyWorkspace(Long userId);
+
+    void disabledUserWorkspace(User user, Workspace workspace);
+
+    UserWorkspace getUserWorkspace(Long id);
+
+    boolean checkWorkspaceManager();
+
+    List<UserWorkspace> findByWorkspaceId(Long workspaceId);
+
+    void limitExpireUserWorkspace(Workspace workspace);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/WidgetService.java b/src/main/java/kr/wisestone/owl/service/WidgetService.java
new file mode 100644
index 0000000..40fb26a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/WidgetService.java
@@ -0,0 +1,45 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.condition.WidgetCondition;
+import org.springframework.data.domain.Pageable;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+public interface WidgetService {
+
+    WidgetCondition makeWidgetCondition();
+
+    void findAllWidget(Map<String, Object> resJsonData);
+
+    void findStatisticsIssue(Map<String, Object> resJsonData, WidgetCondition widgetCondition);
+
+    void findProjectProgress(Map<String, Object> resJsonData, WidgetCondition widgetCondition);
+
+    void findMyAssigneeIssue(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Pageable pageable);
+
+    void findDelayIssue(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Pageable pageable);
+
+    void findRegisterIssue(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Pageable pageable);
+
+    void findMemberProgress(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Boolean getWidgetCondition);
+
+    void findMyIssueDetail(Map<String, Object> resJsonData, WidgetCondition widgetCondition);
+
+    void findRiskIssue(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Pageable pageable);
+
+    void findIssueComplete(Map<String, Object> resJsonData, WidgetCondition widgetCondition, String searchPeriod);
+
+    void findByStandIssueStatus(Map<String, Object> resJsonData, WidgetCondition widgetCondition);
+
+    void findByStandIssueType(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Boolean getWidgetCondition);
+
+    void findSeverityIssueWidget(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Map<String, Object> parameter, Pageable pageable);
+
+    ModelAndView downloadExcel(HttpServletRequest request, Model model);
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/WorkflowService.java b/src/main/java/kr/wisestone/owl/service/WorkflowService.java
new file mode 100644
index 0000000..2811bec
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/WorkflowService.java
@@ -0,0 +1,40 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.Workflow;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import kr.wisestone.owl.vo.WorkflowVo;
+import kr.wisestone.owl.web.condition.WorkflowCondition;
+import kr.wisestone.owl.web.form.WorkflowForm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+public interface WorkflowService extends AbstractService<Workflow, Long, JpaRepository<Workflow, Long>> {
+
+    void addDefaultWorkflow(Workspace workspace, List<ProjectType> projectTypes);
+
+    Workflow addWorkflow(WorkflowForm form);
+
+    List<WorkflowVo> findWorkflow(Map<String, Object> resJsonData,
+                                  WorkflowCondition condition, Pageable pageable);
+
+    void detailWorkflow(Map<String, Object> resJsonData, WorkflowCondition workflowCondition);
+
+    Workflow modifyWorkflow(WorkflowForm form);
+
+    Workflow getWorkflow(Long id);
+
+    void removeWorkflows(WorkflowForm workflowForm);
+
+    Workflow findByWorkspaceIdAndProjectType(Long workspaceId, ProjectType projectType);
+
+    List<Workflow> findByWorkspaceId(Long workspaceId);
+
+    ModelAndView downloadExcel(HttpServletRequest request, Model model);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/WorkflowStatusService.java b/src/main/java/kr/wisestone/owl/service/WorkflowStatusService.java
new file mode 100644
index 0000000..b0b8e8c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/WorkflowStatusService.java
@@ -0,0 +1,29 @@
+package kr.wisestone.owl.service;
+
+
+import kr.wisestone.owl.domain.Project;
+import kr.wisestone.owl.domain.WorkflowStatus;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import org.springframework.data.jpa.repository.JpaRepository;
+import kr.wisestone.owl.vo.WorkflowStatusVo;
+import kr.wisestone.owl.web.condition.WorkflowStatusCondition;
+//import kr.wisestone.owl.web.form.WorkflowStatusForm;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-03.
+ */
+public interface WorkflowStatusService extends AbstractService<WorkflowStatus, Long, JpaRepository<WorkflowStatus, Long>>{
+    List<WorkflowStatus> addDefaultWorkflowStatus(Project project, ProjectType projectType);
+
+    List<WorkflowStatusVo> findWorkflowStatus(Map<String, Object> resJsonData,
+                                              WorkflowStatusCondition condition);
+
+//    WorkflowStatus addWorkflowStatus(WorkflowStatusForm workflowStatusForm);
+//
+//    WorkflowStatusVo detailWorkflowStatus(Map<String, Object> resJsonData, WorkflowStatusForm workflowStatusForm);
+//
+//    WorkflowStatus modifyWorkflowStatus(WorkflowStatusForm workflowStatusForm);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/WorkflowTransitionService.java b/src/main/java/kr/wisestone/owl/service/WorkflowTransitionService.java
new file mode 100644
index 0000000..7be65a3
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/WorkflowTransitionService.java
@@ -0,0 +1,24 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.IssueStatus;
+import kr.wisestone.owl.domain.Workflow;
+import kr.wisestone.owl.domain.WorkflowTransition;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import kr.wisestone.owl.vo.IssueStatusVo;
+import kr.wisestone.owl.vo.WorkflowTransitionVo;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface WorkflowTransitionService extends AbstractService<WorkflowTransition, Long, JpaRepository<WorkflowTransition, Long>>{
+
+    void addDefaultWorkflowTransition(Workflow workflow, List<IssueStatus> issueStatuses, ProjectType projectType);
+
+    List<WorkflowTransition> findByWorkflowId(Long workflowId);
+
+    List<WorkflowTransitionVo> findBySourceIssueStatusIdAndWorkflowId(Long sourceIssueStatusId, Long workflowId);
+
+    void modify(Workflow workflow, List<IssueStatusVo> issueStatusVos);
+
+    WorkflowTransition getWorkflowTransition(Long id);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/WorkspaceService.java b/src/main/java/kr/wisestone/owl/service/WorkspaceService.java
new file mode 100644
index 0000000..e29596d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/WorkspaceService.java
@@ -0,0 +1,54 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.web.form.PaymentForm;
+import kr.wisestone.owl.web.form.WorkspaceForm;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+
+import java.util.List;
+import java.util.Map;
+
+public interface WorkspaceService extends AbstractService<Workspace, Long, JpaRepository<Workspace, Long>>{
+    Workspace addWorkspace(String workspaceName);
+
+    Workspace updateWorkspace(Workspace workspace, PaymentForm paymentForm);
+
+    void expireAlarmWorkspace();
+
+    List<Workspace> findSubscribeImmediateExpireDate();
+
+    void initMaxUserAndStorageSize(Workspace workspace);
+
+    void cancelWorkspacePayment(Workspace workspace);
+
+    Workspace getWorkspace(Long workspaceId);
+
+    void find(Map<String, Object> resJsonData);
+
+    void out(WorkspaceForm workspaceForm);
+
+    void findPrimaryWorkspace(Map<String, Object> resJsonData);
+
+    void findMyWorkspace(Map<String, Object> resJsonData);
+
+    void removeWorkspace(Workspace workspace, User user);
+
+    void modifyWorkspace(WorkspaceForm workspaceForm);
+
+    void expireWorkspace();
+
+    void checkUseWorkspace();
+
+    ModelAndView checkUseExcelDownload(Model model);
+
+    boolean checkUseTraffic(Long fileSize);
+
+    Workspace updateWorkspaceByImmediatePayment(Workspace workspace, int buyUser);
+
+    Map<String, Object> getWorkspaceExpireDay();
+
+    Workspace getPrimaryWorkspace();
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/AbstractServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/AbstractServiceImpl.java
new file mode 100644
index 0000000..4f09b26
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/AbstractServiceImpl.java
@@ -0,0 +1,93 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.common.MessageAccessor;
+import kr.wisestone.owl.service.AbstractService;
+import kr.wisestone.owl.util.WebAppUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public abstract class AbstractServiceImpl<T, ID extends Serializable, R extends JpaRepository<T, ID>>
+        implements AbstractService<T, ID, R> {
+
+    protected abstract JpaRepository<T, ID> getRepository();
+
+    @PersistenceContext
+    protected EntityManager entityManager;
+
+    @Autowired
+    protected MessageAccessor messageAccessor;
+
+    @Autowired
+    protected WebAppUtil webAppUtil;
+
+    private static final int BATCH_COUNT = 200;    //  諛곗튂 �궗�씠利�
+
+    @Override
+    public long count() {
+        return this.getRepository().count();
+    }
+
+    @Override
+    public void clear() {
+        this.entityManager.clear();
+    }
+
+    @Override
+    public void detach(Object entity) {
+        this.entityManager.detach(entity);
+    }
+
+    @Override
+    @Transactional
+    public void bulkInsert(List<T> entities) {
+        AtomicInteger adGroupInsightIndex = new AtomicInteger(1);
+
+        entities.forEach(entity -> {
+            entity = this.entityManager.merge(entity);
+
+            if (adGroupInsightIndex.get() > 0 && adGroupInsightIndex.get() % BATCH_COUNT == 0) {
+                this.entityManager.flush();
+                this.entityManager.clear();
+            }
+            adGroupInsightIndex.getAndIncrement();
+
+        });
+
+        entityManager.flush();
+        entityManager.clear();
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public T findOne(ID id) {
+        Optional<T> entity = this.getRepository().findById(id);
+
+        if (entity.isPresent()) {
+            return entity.get();
+        }
+
+        return null;
+    }
+
+    /*@Override
+    @Transactional(readOnly = true)
+    public T findOne(ID id) {
+        return this.getRepository().getOne(id);
+    }*/
+
+
+    @Override
+    @Transactional(readOnly = true)
+    public List<T> findAll(List<ID> ids) {
+        return this.getRepository().findAllById(ids);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/AttachedFileServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/AttachedFileServiceImpl.java
new file mode 100644
index 0000000..8057f64
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/AttachedFileServiceImpl.java
@@ -0,0 +1,589 @@
+package kr.wisestone.owl.service.impl;
+
+
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.model.*;
+import com.amazonaws.services.s3.transfer.*;
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.AttachedFile;
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.domain.enumType.AttachedType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.AttachedFileMapper;
+import kr.wisestone.owl.repository.AttachedFileRepository;
+import kr.wisestone.owl.service.AttachedFileService;
+import kr.wisestone.owl.service.IssueService;
+import kr.wisestone.owl.service.WorkspaceService;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.util.WebAppUtil;
+import kr.wisestone.owl.vo.AttachedFileVo;
+import kr.wisestone.owl.vo.ExportExcelAttrVo;
+import kr.wisestone.owl.vo.ExportExcelVo;
+import kr.wisestone.owl.web.condition.AttachedFileCondition;
+import kr.wisestone.owl.web.form.IssueForm;
+import kr.wisestone.owl.web.view.ExcelView;
+import kr.wisestone.owl.web.view.FileDownloadView;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.StopWatch;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.ui.Model;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.ModelAndView;
+import java.io.File;
+import java.io.InputStream;
+import java.text.DecimalFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class AttachedFileServiceImpl extends AbstractServiceImpl<AttachedFile, Long, JpaRepository<AttachedFile, Long>>
+        implements AttachedFileService {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(AttachedFileServiceImpl.class);
+
+    @Autowired
+    private AttachedFileRepository attachedFileRepository;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private IssueService issueService;
+
+    @Autowired
+    private AttachedFileMapper attachedFileMapper;
+
+    @Autowired
+    private SimpMessagingTemplate simpMessagingTemplate;
+
+    @Value("${use.aws}")
+    private boolean bUseAWS;
+
+    @Value("${attached.file.path}")
+    private String uploadFolder;
+
+    @Value("${aws.bucket.name}")
+    private String bucketName;
+
+    @Value("${aws.s3.url}")
+    private String awsS3Url;
+
+    @Autowired
+    private FileDownloadView fileDownloadView;
+
+    @Autowired
+    private AmazonS3 amazonS3;
+
+    @Autowired
+    private ExcelView excelView;
+
+
+    @Override
+    protected JpaRepository<AttachedFile, Long> getRepository() {
+        return this.attachedFileRepository;
+    }
+
+    //  泥⑤� �뙆�씪�쓣 �벑濡앺븳�떎. - �씠�뒋 �꽟癒� �끂�듃�뿉�꽌 �궗�슜
+    @Override
+    @Transactional
+    public List<AttachedFile> addAttachedFile(List<MultipartFile> multipartFiles, Map<String, Object> content) {
+        Long workspaceId = MapUtil.getLong(content, "workspaceId");
+        Long issueId = MapUtil.getLong(content, "issueId");
+        Workspace workspace = this.workspaceService.getWorkspace(workspaceId);
+        List<Map<String, Object>> convertFileMaps = Lists.newArrayList();
+
+        for (MultipartFile multipartFile : multipartFiles) {
+            convertFileMaps.add(CommonUtil.makeFileMap(multipartFile));
+        }
+
+        if (issueId != null) {
+            Issue issue = this.issueService.getIssue(issueId);
+            return this.addAttachedFiles(workspace, convertFileMaps, issue, null, AttachedType.SUMMER);
+        }
+        else {
+            return this.addAttachedFiles(workspace, convertFileMaps, null, null, AttachedType.TEMP_SUMMER);
+        }
+    }
+
+    //  泥⑤� �뙆�씪�쓣 �벑濡앺븳�떎. - �씠�뒋 �깮�꽦, �닔�젙�뿉�꽌 �궗�슜
+    @Override
+    @Transactional
+    public void addAttachedFile(List<Map<String, Object>> convertFileMaps, Issue issue, String userAccount) {
+        Workspace workspace = issue.getIssueStatus().getWorkspace();
+
+        if (workspace == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKSPACE_NOT_EXIST));
+        }
+
+        this.addAttachedFiles(workspace, convertFileMaps, issue, userAccount, AttachedType.ISSUE_ATTACHED);
+    }
+
+    private List<AttachedFile> addAttachedFiles(Workspace workspace, List<Map<String, Object>> convertFileMaps, Issue issue, String userAccount, AttachedType attachedType) {
+        List<AttachedFile> attachedFiles = Lists.newArrayList();
+        AttachedFileCondition attachedFileCondition = new AttachedFileCondition();
+        attachedFileCondition.setWorkspaceId(workspace.getId());
+
+        Long useStorageSize = this.attachedFileMapper.findUseStorage(attachedFileCondition);
+
+        if (useStorageSize == null) {
+            useStorageSize = 0L;
+        }
+
+        //  �슜�웾 諛� �뙆�씪 �솗�옣�옄 �뿀�슜 �뿬遺� 泥댄겕
+        this.checkStorageSizeAndFileType(convertFileMaps, workspace.getStorageSize(), useStorageSize);
+
+        int totalFileCount = convertFileMaps.size();    //  �쟾泥� �뾽濡쒕뱶 �뙆�씪 媛쒖닔
+        int uploadFileCount = 1;    //  �쁽�옱 �뾽濡쒕뱶 �뙆�씪 �닚�꽌
+
+        for (Map<String, Object> convertFileMap : convertFileMaps) {
+            //  �뙆�씪 �뾽濡쒕뱶 �썑 awsKey(�뙆�씪 紐�)�쓣 媛��졇�삩�떎.
+            String awsKey = this.uploadFile(convertFileMap, this.uploadFolder + workspace.getId(), userAccount, totalFileCount, uploadFileCount);
+
+            attachedFiles.add(new AttachedFile(MapUtil.getString(convertFileMap, "fileName"), MapUtil.getLong(convertFileMap, "fileSize"), MapUtil.getString(convertFileMap, "contentType"),
+                    this.setMakeFilePath(awsKey, workspace), awsKey, issue, workspace, CommonUtil.getFileType(MapUtil.getString(convertFileMap, "fileName")), attachedType));
+
+            uploadFileCount++;
+        }
+
+        if (attachedFiles.size() > 0) {
+            this.attachedFileRepository.saveAll(attachedFiles);
+        }
+
+        return attachedFiles;
+    }
+
+    //  �슜�웾 諛� �뙆�씪 �솗�옣�옄 �뿀�슜 �뿬遺� 泥댄겕
+    private void checkStorageSizeAndFileType(List<Map<String, Object>> convertFileMaps, Long totalStorageSize, Long useStorageSize) {
+        for (Map<String, Object> convertFileMap : convertFileMaps) {
+            Long fileSize = MapUtil.getLong(convertFileMap, "fileSize");
+            String fileName = MapUtil.getString(convertFileMap, "fileName");
+
+            if (fileSize == null) {
+                fileSize = 0L;
+            }
+
+            //  �슜�웾 珥덇낵 泥댄겕
+            if (totalStorageSize < (useStorageSize + fileSize)) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.WORKSPACE_STORAGE_SIZE_EXCESS));
+            }
+
+            //  �뙆�씪 �솗�옣�옄 泥댄겕
+            if (!CommonUtil.checkFileType(fileName)) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.FILE_TYPE_NOT_ALLOW));
+            }
+        }
+    }
+
+    //  �씠�뒋 �깮�꽦, �닔�젙�뿉�꽌 �꽟癒� �끂�듃濡� �뾽濡쒕뱶�븳 �씠誘몄��� �씠�뒋瑜� �뿰寃곗떆�궓�떎.
+    @Override
+    @Transactional
+    public void connectIssueIdAttachedFile(Issue issue, IssueForm issueForm) {
+        for (Long attachedFileId : issueForm.getAttachedFileIds()) {
+            AttachedFile attachedFile = this.getAttachedFile(attachedFileId);
+            attachedFile.setIssue(issue);
+            attachedFile.setAttachedType(AttachedType.SUMMER);
+            this.attachedFileRepository.save(attachedFile);
+        }
+
+        this.attachedFileRepository.flush();
+    }
+
+    //  �씠�뒋 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<AttachedFileVo> findAttachedFile(Map<String, Object> resJsonData, AttachedFileCondition condition) {
+        List<AttachedFileVo> attachedFileVos = Lists.newArrayList();
+
+        for (AttachedFile attachedFile : this.findByIssueId(condition.getIssueId())) {
+            AttachedFileVo attachedFileVo = ConvertUtil.copyProperties(attachedFile, AttachedFileVo.class, "fileType");
+            attachedFileVo.setFileType(attachedFile.getFileType().toString());
+            attachedFileVos.add(attachedFileVo);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, attachedFileVos);
+
+        return attachedFileVos;
+    }
+
+    //  �씠�뒋 �븘�씠�뵒濡� 泥⑤� �뙆�씪�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<AttachedFile> findByIssueId(Long issueId) {
+        return this.attachedFileRepository.findByIssueId(issueId);
+    }
+
+    //  泥⑤� �뙆�씪 �븘�씠�뵒濡� 泥⑤� �뙆�씪�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public AttachedFile getAttachedFile(Long attachedFileId) {
+        AttachedFile attachedFile = this.findOne(attachedFileId);
+
+        if (attachedFile == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ATTACHED_FILE_NOT_EXIST));
+        }
+
+        return attachedFile;
+    }
+
+    //  泥⑤��뙆�씪 �궘�젣
+    @Override
+    @Transactional
+    public void removeAttachedFiles(List<Long> removeIds) {
+        for (Long attachedId : removeIds) {
+            AttachedFile attachedFile = this.getAttachedFile(attachedId);
+
+            switch (attachedFile.getAttachedType()) {
+                case SUMMER:
+                    //  �씠�뒋�� 泥⑤� �뙆�씪 �뿰寃곗쓣 �빐�젣�븳�떎.
+                    attachedFile.setIssue(null);
+                    break;
+                case ISSUE_ATTACHED:
+                    //  泥⑤� �뙆�씪�쓣 �궘�젣�븳�떎.
+                    this.removeAttachedFiles(attachedFile);
+                    break;
+            }
+        }
+    }
+
+    //  泥⑤� �뙆�씪�쓣 �궘�젣�븳�떎.
+    private void removeAttachedFiles(AttachedFile attachedFile) {
+        //  �뙆�씪�쓣 �궘�젣�븳�떎.
+        this.removeFile(attachedFile.getAwsKey(), attachedFile.getWorkspace().getId());
+
+        this.attachedFileRepository.delete(attachedFile);
+    }
+
+    //  �뾽臾� 怨듦컙 �궘�젣�떆 �씠�뒋�뿉 泥⑤��맂 �뙆�씪�쓣 �떆�뒪�뀥�뿉�꽌 �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void deleteWorkspaceCascadeAttachedFile(Workspace workspace) {
+        List<Map<String, Object>> attachedFiles = this.attachedFileMapper.findByWorkspaceId(workspace.getId());
+
+        for (Map<String, Object> attachedFile : attachedFiles) {
+            //  �뙆�씪�쓣 �궘�젣�븳�떎.
+            this.removeFile(MapUtil.getString(attachedFile, "awsKey"), MapUtil.getLong(attachedFile, "workspaceId"));
+        }
+
+        //  泥⑤� �뙆�씪 �궘�젣
+        this.attachedFileMapper.deleteAttachedFileByWorkspaceId(workspace.getId());
+    }
+
+    //  �봽濡쒖젥�듃 �궘�젣�떆 �씠�뒋�뿉 泥⑤��맂 �뙆�씪�쓣 �떆�뒪�뀥�뿉�꽌 �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void deleteIssueCascadeAttachedFile(List<Long> issueIds, Workspace workspace) {
+        //  �씠�뒋媛� �뾾�쓣 寃쎌슦�뿉�뒗 �븘�옒 濡쒖쭅�쓣 ��吏� �븡�뒗�떎. -> 紐⑤뱺 �뾽臾닿났媛꾩뿉 泥⑤��뙆�씪�씠 �궘�젣�맆 �쐞�뿕�씠 �엳�쓬.
+        if (issueIds.size() < 1) {
+            return;
+        }
+
+        AttachedFileCondition attachedFileCondition = new AttachedFileCondition();
+        attachedFileCondition.setIssueIds(issueIds);
+        attachedFileCondition.setWorkspaceId(workspace.getId());
+
+        List<Map<String, Object>> attachedFiles = this.attachedFileMapper.findByIssueIds(attachedFileCondition);
+
+        for (Map<String, Object> attachedFile : attachedFiles) {
+            //  �뙆�씪�쓣 �궘�젣�븳�떎.
+            this.removeFile(MapUtil.getString(attachedFile, "awsKey"), MapUtil.getLong(attachedFile, "workspaceId"));
+        }
+
+        //  泥⑤� �뙆�씪 �궘�젣
+        this.attachedFileMapper.deleteAttachedFileByIssueIds(attachedFileCondition);
+    }
+
+
+    //  �뾽臾� 怨듦컙�뿉�꽌 �궗�슜以묒씤 ���옣 �슜�웾�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Long findUseStorage(Workspace workspace) {
+        AttachedFileCondition attachedFileCondition = new AttachedFileCondition();
+        attachedFileCondition.setWorkspaceId(workspace.getId());
+        return this.attachedFileMapper.findUseStorage(attachedFileCondition);
+    }
+
+    //  �뾽臾� 怨듦컙�뿉�꽌 �궗�슜 �듃�옒�뵿�쓣 泥댄겕�븯怨� �듃�옒�뵿 珥덇낵�떆 �떎�슫濡쒕뱶瑜� 留됰뒗�떎.
+    @Override
+    @Transactional
+    public ModelAndView checkUseWorkspaceTraffic(Long id, Model model) {
+        AttachedFile attachedFile = this.getAttachedFile(id);
+        //  �듃�옒�뵿 �궗�슜�웾�쓣 ���옣�븯怨� 珥덇낵�븷 寃쎌슦�뿉�뒗 �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 �떎�슫濡쒕뱶瑜� �씪�떆�쟻�쑝濡� 湲덉��븳�떎.
+        if (!this.workspaceService.checkUseTraffic(attachedFile.getSize())) {
+            ExportExcelVo excelInfo = new ExportExcelVo();
+            excelInfo.setFileName("�빐�떦 �뾽臾� 怨듦컙�뿉�꽌 �궗�슜�븷 �닔 �엳�뒗 �듃�옒�뵿�씠 珥덇낵�릺�뿀�뒿�땲�떎. �듃�옒�뵿�쓣 異붽��븯�젮硫� ���씠利덉뒪�넠 �떞�떦�옄�뿉寃� 臾몄쓽�븯�꽭�슂. - supportowl@wisestone.kr");
+            excelInfo.addAttrInfos(new ExportExcelAttrVo("name", "", 120, ExportExcelAttrVo.ALIGN_LEFT));
+            model.addAttribute(Constants.EXCEL, excelInfo);
+            return new ModelAndView(this.excelView);
+        }
+
+        ModelAndView objModelView = null;
+
+        if( this.bUseAWS )
+        {
+            objModelView = downloadFileFromAWS(id, model);
+        }
+        else
+        {
+            objModelView = downloadFileFromLocal(id, model);
+        }
+
+        return objModelView;
+    }
+
+    private ModelAndView downloadFileFromLocal(Long id, Model model) {
+        AttachedFile attachedFile = this.getAttachedFile(id);
+
+        InputStream objectInputStream = CommonUtil.getFileInputStream(this.bucketName + this.uploadFolder + attachedFile.getWorkspace().getId(), attachedFile.getAwsKey());
+
+        try {
+            byte[] bytes = IOUtils.toByteArray(objectInputStream);
+            AttachedFileVo attachedFileVo = ConvertUtil.copyProperties(attachedFile, AttachedFileVo.class);
+            attachedFileVo.setBytes(bytes);
+            model.addAttribute("fileDownloadTarget", attachedFileVo);
+        } catch (Exception e) {
+            LOGGER.error("泥⑤� �뙆�씪 �떎�슫濡쒕뱶�뿉 �떎�뙣�븯���뒿�땲�떎.");
+        }
+
+        return new ModelAndView(this.fileDownloadView);
+    }
+
+    private ModelAndView downloadFileFromAWS(Long id, Model model) {
+        AttachedFile attachedFile = this.getAttachedFile(id);
+
+        GetObjectRequest getObjectRequest = new GetObjectRequest(this.bucketName + this.uploadFolder + attachedFile.getWorkspace().getId(), attachedFile.getAwsKey());
+        S3Object s3Object = this.amazonS3.getObject(getObjectRequest);
+        S3ObjectInputStream objectInputStream = s3Object.getObjectContent();
+
+        try {
+            byte[] bytes = IOUtils.toByteArray(objectInputStream);
+            AttachedFileVo attachedFileVo = ConvertUtil.copyProperties(attachedFile, AttachedFileVo.class);
+            attachedFileVo.setBytes(bytes);
+            model.addAttribute("fileDownloadTarget", attachedFileVo);
+        } catch (Exception e) {
+            LOGGER.error("�븘留덉〈 �겢�씪�슦�뱶�뿉�꽌 泥⑤� �뙆�씪 �떎�슫濡쒕뱶�뿉 �떎�뙣�븯���뒿�땲�떎.");
+        }
+
+        return new ModelAndView(this.fileDownloadView);
+    }
+
+    //  �씠�뒋�� �뿰寃곕릺吏� �븡�� 泥⑤��뙆�씪 �궘�젣
+    @Override
+    @Transactional
+    public void deleteAttachedFileNotId() {
+        this.attachedFileMapper.deleteAttachedFileNotId();
+    }
+
+    // �뙆�씪�쓣 �뾽濡쒕뱶 �븳�떎.
+    @Override
+    @Transactional
+    public String uploadFile(Map<String, Object> convertFileMap, String awsUploadFolder, String userAccount, int totalFileCount, int uploadFileCount) {
+        String strKeyName = "";
+
+        if( this.bUseAWS )
+        {
+            strKeyName = uploadFileToAws(convertFileMap, awsUploadFolder, userAccount, totalFileCount, uploadFileCount);
+        }
+        else
+        {
+            strKeyName = uploadFileToLocal(convertFileMap, awsUploadFolder, userAccount, totalFileCount, uploadFileCount);
+        }
+
+        return strKeyName;
+    }
+
+    // local storage濡� �씠�룞�븳�떎.
+    private String uploadFileToLocal(Map<String, Object> convertFileMap, String awsUploadFolder, String userAccount, int totalFileCount, int uploadFileCount) {
+        String awsKeyName = CommonUtil.getFileNameByUUID(MapUtil.getString(convertFileMap, "fileName"));
+        StopWatch serviceStart = new StopWatch();
+        serviceStart.start();
+        File file = (File) convertFileMap.get("file");
+
+        try {
+            //  �씠�뒋 �깮�꽦, �닔�젙�뿉�꽌 �뙆�씪�쓣 �뾽濡쒕뱶�븷�븣�뒗 鍮꾨룞湲곕줈 �삱由щʼn �뾽濡쒕뱶 吏꾪뻾瑜좎쓣 �몴�떆�빐以��떎.
+            if (!StringUtils.isEmpty(userAccount)) {
+            }
+
+            CommonUtil.moveToSaveStorage(this.bucketName + awsUploadFolder, awsKeyName, file);
+
+            if (file.exists()) {
+                file.delete();
+            }
+        } catch (Exception e) {
+            LOGGER.error("�뙆�씪 �뾽濡쒕뱶 �뿉�윭 :" + e.getMessage());
+        }
+
+        serviceStart.stop();
+
+        return awsKeyName;
+    }
+
+    //  �븘留덉〈 �겢�씪�슦�뱶�뿉 �뙆�씪�쓣 �뾽濡쒕뱶�븳�떎.
+    private String uploadFileToAws(Map<String, Object> convertFileMap, String awsUploadFolder, String userAccount, int totalFileCount, int uploadFileCount) {
+        String awsKeyName = CommonUtil.getFileNameByUUID(MapUtil.getString(convertFileMap, "fileName"));
+        StopWatch serviceStart = new StopWatch();
+        serviceStart.start();
+        File file = (File) convertFileMap.get("file");
+
+        TransferManager transferManager = TransferManagerBuilder
+                .standard()
+                .withS3Client(this.amazonS3)
+                /*.withMultipartUploadThreshold((long) 5*1024*1024)*/
+                /*.withExecutorFactory(() -> Executors.newFixedThreadPool(20))*/
+                .build();
+
+        /*TransferManager transferManager = TransferManagerBuilder
+                .standard()
+                .withS3Client(this.amazonS3)
+                .withDisableParallelDownloads(false)
+                .withMinimumUploadPartSize((long)(5 * MB))
+                .withMultipartUploadThreshold((long)(16 * MB))
+                .withMultipartCopyPartSize((long)(5 * MB))
+                .withMultipartCopyThreshold((long)(100 * MB))
+                .withExecutorFactory(() -> Executors.newFixedThreadPool(20))
+                .build();*/
+
+        try {
+            PutObjectRequest putObjectRequest =
+                    new PutObjectRequest(this.bucketName + awsUploadFolder, awsKeyName, file);
+            putObjectRequest.setCannedAcl(CannedAccessControlList.PublicRead); // file permission
+            //this.amazonS3.putObject(putObjectRequest); // upload file
+
+            long fileSize = MapUtil.getLong(convertFileMap, "fileSize");
+            String fileName = MapUtil.getString(convertFileMap, "fileName");
+
+            //  �씠�뒋 �깮�꽦, �닔�젙�뿉�꽌 �뙆�씪�쓣 �뾽濡쒕뱶�븷�븣�뒗 鍮꾨룞湲곕줈 �삱由щʼn �뾽濡쒕뱶 吏꾪뻾瑜좎쓣 �몴�떆�빐以��떎.
+            if (!StringUtils.isEmpty(userAccount)) {
+                com.amazonaws.event.ProgressListener progressListener = new com.amazonaws.event.ProgressListener() {
+                    long bytesUploaded = 0;
+
+                    SimpMessagingTemplate webSocket = simpMessagingTemplate;
+
+                    @Override
+                    public void progressChanged(com.amazonaws.event.ProgressEvent progressEvent) {
+                        this.bytesUploaded += progressEvent.getBytesTransferred();// add counter
+                        double uploadProcess = this.bytesUploaded * 100.0 / fileSize;
+                        String percent = new DecimalFormat("###").format(uploadProcess);
+                        Map<String, Object> fileMap = new HashMap<>();
+                        fileMap.put("display", (uploadProcess < 100));
+                        fileMap.put("serverFileName", fileName);
+                        fileMap.put("serverProgress", percent + "%");
+                        fileMap.put("totalFileCount", totalFileCount);
+                        fileMap.put("uploadFileCount", uploadFileCount);
+
+                        this.webSocket.convertAndSendToUser(userAccount, "/notification/file-upload-process", fileMap);
+                    }
+                };
+
+                putObjectRequest.setGeneralProgressListener(progressListener);
+            }
+
+            Upload upload = transferManager.upload(putObjectRequest);
+            upload.waitForCompletion();
+            transferManager.shutdownNow(false);
+
+            if (file.exists()) {
+                file.delete();
+            }
+        } catch (Exception e) {
+            LOGGER.error("�뙆�씪 �뾽濡쒕뱶 �뿉�윭 :" + e.getMessage());
+        }
+
+        serviceStart.stop();
+
+        return awsKeyName;
+    }
+
+    //  �뙆�씪�쓣 �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void removeFile(String key, Long workspaceId) {
+        try {
+            if( this.bUseAWS )
+            {
+                removeFileToAws(key, workspaceId);
+            }
+            else
+            {
+                removeFileToLocal(key, workspaceId);
+            }
+        } catch (Exception e) {
+            LOGGER.error("�뙆�씪 �궘�젣 �뿉�윭 :" + e.getMessage());
+        }
+    }
+
+    //  �뙆�씪�쓣 �궘�젣�븳�떎.
+    private void removeFileToLocal(String key, Long workspaceId) {
+        try {
+            if(workspaceId > 0 )
+            {
+                CommonUtil.deleteToSaveStorage(this.bucketName + this.uploadFolder, key);
+            }
+            else
+            {
+                CommonUtil.deleteToSaveStorage(this.bucketName + this.uploadFolder + workspaceId, key);
+            }
+        } catch (Exception e) {
+            LOGGER.error("�뙆�씪 �궘�젣 �뿉�윭 :" + e.getMessage());
+        }
+    }
+
+    //  �븘留덉〈�겢�씪�슦�뱶�뿉�꽌 �뙆�씪�쓣 �궘�젣�븳�떎.
+    private void removeFileToAws(String key, Long workspaceId) {
+        try {
+            if(workspaceId > 0 )
+            {
+                this.amazonS3.deleteObject(this.bucketName + this.uploadFolder, key);
+            }
+            else
+            {
+                this.amazonS3.deleteObject(this.bucketName + this.uploadFolder + workspaceId, key);
+            }
+        } catch (Exception e) {
+            LOGGER.error("�뙆�씪 �궘�젣 �뿉�윭 :" + e.getMessage());
+        }
+    }
+
+    //  �뾽濡쒕뱶�릺�뒗 �쟾泥� 寃쎈줈瑜� 媛��졇�삩�떎.
+    private String setMakeFilePath(String path, Workspace workspace) {
+        String strFilePath = "";
+
+        if( this.bUseAWS )
+        {
+            strFilePath = setMakeAwsFilePath(path, workspace);
+        }
+        else
+        {
+            strFilePath = setMakeLocalFilePath(path, workspace);
+        }
+
+        return strFilePath;
+    }
+
+    //  �뾽濡쒕뱶�릺�뒗 �쟾泥� 寃쎈줈瑜� 媛��졇�삩�떎.
+    private String setMakeLocalFilePath(String path, Workspace workspace) {
+        return this.awsS3Url + this.bucketName + this.uploadFolder + workspace.getId() + "/" + path;
+    }
+
+    //  �븘留덉〈 �겢�씪�슦�뱶�뿉 �뾽濡쒕뱶�릺�뒗 �쟾泥� 寃쎈줈瑜� 媛��졇�삩�떎.
+    private String setMakeAwsFilePath(String path, Workspace workspace) {
+        return this.awsS3Url + this.bucketName + this.uploadFolder + workspace.getId() + "/" + path;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/CustomFieldServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/CustomFieldServiceImpl.java
new file mode 100644
index 0000000..391ffcf
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/CustomFieldServiceImpl.java
@@ -0,0 +1,408 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.common.ExcelConditionCheck;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.CustomField;
+import kr.wisestone.owl.domain.IssueCustomFieldValue;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.domain.enumType.CustomFieldType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.CustomFieldMapper;
+import kr.wisestone.owl.repository.CustomFieldRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.vo.*;
+import kr.wisestone.owl.web.condition.CustomFieldCondition;
+import kr.wisestone.owl.web.form.CustomFieldForm;
+import kr.wisestone.owl.web.view.ExcelView;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class CustomFieldServiceImpl extends AbstractServiceImpl<CustomField, Long, JpaRepository<CustomField, Long>> implements CustomFieldService {
+
+    private static final Logger log = LoggerFactory.getLogger(CustomFieldServiceImpl.class);
+
+    public enum UseType {
+        Y,
+        N,
+    }
+
+    @Autowired
+    private CustomFieldRepository customFieldRepository;
+
+    @Autowired
+    private CustomFieldMapper customFieldMapper;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private CustomFieldValueService customFieldValueService;
+
+    @Autowired
+    private IssueCustomFieldValueService issueCustomFieldValueService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private ExcelView excelView;
+
+    @Autowired
+    private ExcelConditionCheck excelConditionCheck;
+
+    @Override
+    protected JpaRepository<CustomField, Long> getRepository() {
+        return this.customFieldRepository;
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �깮�꽦�븳�떎.
+    @Override
+    @Transactional
+    public CustomField addCustomField(CustomFieldForm customFieldForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+        //  �씠由� �쑀�슚�꽦 泥댄겕
+        this.verifyName(customFieldForm.getName(), null);
+        //  �샃�뀡 媛� �쑀�슚 泥댄겕
+        this.verifyOptions(customFieldForm);
+
+        CustomField customField = ConvertUtil.copyProperties(customFieldForm, CustomField.class, "customFieldType");
+        customField.setUse("Y");
+        Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        customField.setWorkspace(workspace);
+        customField.setCustomFieldType(CustomFieldType.valueOf(customFieldForm.getCustomFieldType()));   // �궗�슜�옄 �젙�쓽 �븘�뱶 �쑀�삎�쓣 �뀑�똿�븳�떎.
+
+        this.customFieldRepository.saveAndFlush(customField);
+
+        this.customFieldValueService.addCustomFieldValues(customField, customFieldForm.getOptions(), null);   //  �궗�슜�옄 �젙�쓽 �븘�뱶 媛� 異붽�
+
+        return customField;
+    }
+
+    //  �씠由� �쑀�슚�꽦 泥댄겕
+    private void verifyName(String name, Long id) {
+        if (StringUtils.isEmpty(name)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_NOT_NAME));
+        }
+
+        if (name.length() > 15) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_NAME_MAX_LENGTH_OUT));
+        }
+
+        CustomField customField;
+        Long workspaceId = this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId();
+
+        if (id == null) {
+            customField = this.customFieldRepository.findByNameAndWorkspaceId(name, workspaceId);
+        }
+        else {
+            customField = this.customFieldRepository.findByNameAndWorkspaceIdAndIdNot(name, workspaceId, id);
+        }
+
+        if (customField != null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_USED_NAME));
+        }
+    }
+
+    //  �샃�뀡 媛� �쑀�슚 泥댄겕
+    private void verifyOptions(CustomFieldForm customFieldForm) {
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 �쑀�삎�씠 �뀓�뒪�듃 �씪�븣�뒗 �샃�뀡 媛믪씠 議댁옱�븯硫� �븞�맂�떎.
+        if (CustomFieldType.valueOf(customFieldForm.getCustomFieldType()).equals(CustomFieldType.INPUT)) {
+            if (customFieldForm.getOptions().size() > 0) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_OPTIONS_NOT_USE_INPUT_FIELD));
+            }
+
+            if(customFieldForm.getDefaultValue().length() > 100) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_DEFAULT_VALUE_MAX_LENGTH_OUT));
+            }
+        }
+
+        if (customFieldForm.getOptions().size() > 0) {
+            //  �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪씠 null �씠嫄곕굹 怨듬갚�씤 寃쎌슦瑜� 李얜뒗�떎.
+            for (String option : customFieldForm.getOptions()) {
+                if (StringUtils.isEmpty(option)) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_OPTIONS_NOT_EMPTY_VALUE));
+                }
+                //  �샃�뀡 媛� 理쒕� 湲몄씠 泥댄겕
+                if (option.length() > 15) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_OPTION_VALUE_MAX_LENGTH_OUT));
+                }
+            }
+
+            //  �궗�슜�옄 �젙�쓽 �븘�뱶 湲곕낯 媛믪씠 �샃�뀡�뿉 議댁옱�븯吏� �븡�쓣 寃쎌슦瑜� 李얜뒗�떎.
+            if (!StringUtils.isEmpty(customFieldForm.getDefaultValue())) {
+                switch (CustomFieldType.valueOf(customFieldForm.getCustomFieldType())) {
+                    case SINGLE_SELECT:
+                        if (customFieldForm.getDefaultValue().split("#").length > 2) {
+                            throw new OwlRuntimeException(
+                                    this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_OPTIONS_NOT_USE_MULTI_DEFAULT_VALUE));
+                        }
+                        //  湲곕낯 媛믪쑝濡� 吏��젙�븳 媛믪씠 �샃�뀡�뿉 �엳�뒗吏� �솗�씤�븳�떎.
+                        this.verifyOptionsDefaultValue(customFieldForm);
+
+                        break;
+                    case MULTI_SELECT:
+                        //  湲곕낯 媛믪쑝濡� 吏��젙�븳 媛믪씠 �샃�뀡�뿉 �엳�뒗吏� �솗�씤�븳�떎.
+                        this.verifyOptionsDefaultValue(customFieldForm);
+                        break;
+                }
+            }
+        }
+    }
+
+    //  湲곕낯 媛믪쑝濡� 吏��젙�븳 媛믪씠 �샃�뀡�뿉 �엳�뒗吏� �솗�씤�븳�떎.
+    private void verifyOptionsDefaultValue(CustomFieldForm customFieldForm) {
+        //  以묐났�맂 �샃�뀡 媛� 李얘린
+        Map<String, Object> optionValueDictionary = new HashMap<>();
+
+        for (String defaultValue : customFieldForm.getDefaultValue().split("#")) {
+            if (!StringUtils.isEmpty(defaultValue)) {
+                if (!customFieldForm.getOptions().contains(defaultValue)) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_OPTIONS_NOT_EXIST_DEFAULT_VALUE));
+                }
+
+                if (MapUtil.getBoolean(optionValueDictionary, defaultValue) == null) {
+                    optionValueDictionary.put(defaultValue, true);
+                }
+                else {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_OPTIONS_USED_EXIST_DEFAULT_VALUE));
+                }
+            }
+        }
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<CustomFieldVo> findCustomField(Map<String, Object> resJsonData,
+                                               CustomFieldCondition condition, Pageable pageable) {
+
+        condition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        condition.setPageSize(pageable.getPageSize());
+        condition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        List<Map<String, Object>> results = this.customFieldMapper.find(condition);
+        Long totalCount = this.customFieldMapper.count(condition);
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 議고쉶 寃곌낵瑜� CustomFieldVo 濡� 蹂��솚�븳�떎.
+        List<CustomFieldVo> customFieldVos = this.makeCustomFieldVos(results);
+
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, customFieldVos);
+
+        return customFieldVos;
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 議고쉶 寃곌낵瑜� CustomFieldVo 濡� 蹂��솚�븳�떎.
+    private List<CustomFieldVo> makeCustomFieldVos(List<Map<String, Object>> results) {
+        List<CustomFieldVo> customFieldVos = Lists.newArrayList();
+
+        for (Map<String, Object> result : results) {
+            CustomFieldVo customFieldVo = ConvertUtil.convertMapToClass(result, CustomFieldVo.class);
+            CustomField customField = this.getCustomField(customFieldVo.getId());
+            customFieldVo.setCustomFieldValueVos(ConvertUtil.convertObjectsToClasses(customField.getCustomFieldValues(), CustomFieldValueVo.class));
+            customFieldVos.add(customFieldVo);
+        }
+
+        return customFieldVos;
+    }
+
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailCustomField(Map<String, Object> resJsonData, CustomFieldCondition customFieldCondition) {
+        CustomFieldVo customFieldVo = new CustomFieldVo();
+
+        if (customFieldCondition.getId() != null) {
+            CustomField customField = this.getCustomField(customFieldCondition.getId());
+            customFieldVo = ConvertUtil.copyProperties(customField, CustomFieldVo.class);
+            customFieldVo.setCustomFieldType(customField.getCustomFieldType().toString());
+
+            switch (customFieldCondition.getDeep()) {
+                case "01": //  �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪쓣 媛��졇�삩�떎.
+                    customFieldVo.setCustomFieldValueVos(ConvertUtil.convertObjectsToClasses(customField.getCustomFieldValues(), CustomFieldValueVo.class));
+                    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪씠 �씠�뒋�뿉�꽌 �궗�슜�릺怨� �엳�뒗吏� �솗�씤�븳 �썑 �뵆�옒洹� 媛믪쓣 �꽕�젙�븳�떎.
+                    List<IssueCustomFieldValue> issueCustomFieldValues = this.issueCustomFieldValueService.findByCustomFieldId(customField);
+                    if (issueCustomFieldValues.size() > 0) {
+                        customFieldVo.setUseCustomFieldValue(true);
+                    }
+                    break;
+            }
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, customFieldVo);
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �닔�젙�븳�떎.
+    @Override
+    @Transactional
+    public CustomField modifyCustomField(CustomFieldForm customFieldForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+        CustomField customField = this.getCustomField(customFieldForm.getId());
+        CustomFieldType oldCustomFieldType = customField.getCustomFieldType();
+        //  �씠由� �쑀�슚�꽦 泥댄겕
+        this.verifyName(customFieldForm.getName(), customFieldForm.getId());
+        //  �샃�뀡 媛� �쑀�슚 泥댄겕
+        this.verifyOptions(customFieldForm);
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 �쑀�삎�씠 �떒�씪, �떎以� �꽑�깮�뿉�꽌 臾몄옄�뿴濡� 蹂�寃쎈맆 寃쎌슦 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 珥덇린�솕�븳�떎.
+        this.checkChangeCustomFieldType(customFieldForm, customField);
+
+        ConvertUtil.copyProperties(customFieldForm, customField, "id", "customFieldType");
+        customField.setCustomFieldType(CustomFieldType.valueOf(customFieldForm.getCustomFieldType()));
+
+        this.customFieldRepository.saveAndFlush(customField);
+        this.customFieldValueService.addCustomFieldValues(customField, customFieldForm.getOptions(), oldCustomFieldType);   //  �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛� 異붽�
+
+        return customField;
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �쑀�삎�씠 �떒�씪, �떎以� �꽑�깮�뿉�꽌 臾몄옄�뿴濡� 蹂�寃쎈맆 寃쎌슦 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 珥덇린�솕�븳�떎.
+    private void checkChangeCustomFieldType(CustomFieldForm customFieldForm, CustomField customField) {
+        if (!customField.getCustomFieldType().equals(CustomFieldType.INPUT)) {
+            //  �떒�씪, �떎以� �꽑�깮�뿉�꽌 臾몄옄�뿴 �븘�뱶濡� 蹂�寃쎈맂 寃쎌슦
+            if (CustomFieldType.valueOf(customFieldForm.getCustomFieldType()).equals(CustomFieldType.INPUT)) {
+                customField.getCustomFieldValues().clear();
+            }
+        }
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public CustomField getCustomField(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_NOT_EXIST));
+        }
+
+
+        CustomField customField = this.findOne(id);
+
+        if (customField == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_NOT_EXIST));
+        }
+
+        return customField;
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void removeCustomFields(CustomFieldForm customFieldForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        if (customFieldForm.getRemoveIds().size() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_REMOVE_NOT_SELECT));
+        }
+
+        for (Long projectId : customFieldForm.getRemoveIds()) {
+            this.removeCustomFields(projectId);
+        }
+
+//        this.customFieldRepository.flush();
+    }
+
+    private void removeCustomFields(Long customFieldId) {
+        CustomField customField = this.getCustomField(customFieldId);
+        customField.setUse(UseType.N.toString());
+        this.customFieldRepository.saveAndFlush(customField);
+//        this.customFieldRepository.delete(customField);
+    }
+
+    //  �씠�뒋 �뿊�� import �뿉�꽌 �궗�슜�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public CustomField findByName(String name) {
+        return this.customFieldRepository.findByNameAndWorkspaceId(name, this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 紐⑸줉�쓣 �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    @Override
+    @Transactional
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        //  �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븯怨� 鍮꾪솢�꽦�씪 寃쎌슦 �뿊�� �떎�슫濡쒕뱶瑜� 湲덉��븳�떎.
+        ModelAndView modelAndView = this.workspaceService.checkUseExcelDownload(model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        Map<String, Object> conditions = new HashMap<>();
+        //  �뿊�� �떎�슫濡쒕뱶�뿉 �븘�슂�븳 寃��깋 議곌굔 �젙蹂대�� 異붿텧�븯怨� 寃��깋 議곌굔 異붿텧�뿉 �삤瑜섍� 諛쒖깮�븯硫� 寃쎄퀬瑜� �몴�떆�빐以��떎.
+        modelAndView = this.excelConditionCheck.checkCondition(conditions, request, model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        CustomFieldCondition customFieldCondition = CustomFieldCondition.make(conditions);
+        customFieldCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        List<Map<String, Object>> results = this.customFieldMapper.find(customFieldCondition);
+
+        for (Map<String, Object> result : results) {
+            String customFieldType = MapUtil.getString(result, "customFieldType");
+            String customFieldTypeName = "";
+
+            if (customFieldType != null) {
+                switch(customFieldType) {
+                    case "INPUT" :
+                        customFieldTypeName = this.messageAccessor.message("common.stringField"); // 臾몄옄�뿴 �븘�뱶
+                        break;
+                    case "SINGLE_SELECT" :
+                        customFieldTypeName = this.messageAccessor.message("common.singleSelectionField"); // �떒�씪 �꽑�깮 �븘�뱶
+                        break;
+                    case "MULTI_SELECT" :
+                        customFieldTypeName = this.messageAccessor.message("common.multipleSelectionField"); // �떎以� �꽑�깮 �븘�뱶
+                        break;
+                }
+            }
+
+            result.put("customFieldTypeName", customFieldTypeName);
+        }
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.customFieldList")); // �궗�슜�옄 �젙�쓽 �븘�뱶 紐⑸줉
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("name", this.messageAccessor.message("common.customField"), 15, ExportExcelAttrVo.ALIGN_LEFT)); // �궗�슜�옄 �젙�쓽 �븘�뱶
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("customFieldTypeName", this.messageAccessor.message("common.fieldType"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �븘�뱶 �쑀�삎
+        //  �뿊���뿉 �꽔�쓣 �뜲�씠�꽣
+        excelInfo.setDatas(results);
+        model.addAttribute(Constants.EXCEL, excelInfo);
+        return new ModelAndView(this.excelView);
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 �엳�뒗 紐⑤뱺 �궗�슜�옄 �젙�쓽 �븘�뱶�쓣 議고쉶�븳�떎. �씠�뒋 �뿊�� import �뿉�꽌 �궗�슜
+    @Override
+    @Transactional(readOnly = true)
+    public List<CustomField> findByWorkspaceId() {
+        return this.customFieldRepository.findByWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/CustomFieldValueServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/CustomFieldValueServiceImpl.java
new file mode 100644
index 0000000..0eca579
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/CustomFieldValueServiceImpl.java
@@ -0,0 +1,125 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.CustomField;
+import kr.wisestone.owl.domain.CustomFieldValue;
+import kr.wisestone.owl.domain.enumType.CustomFieldType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.CustomFieldValueRepository;
+import kr.wisestone.owl.service.CustomFieldValueService;
+import kr.wisestone.owl.service.IssueCustomFieldValueService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+
+@Service
+public class CustomFieldValueServiceImpl extends AbstractServiceImpl<CustomFieldValue, Long, JpaRepository<CustomFieldValue, Long>> implements CustomFieldValueService {
+
+    private static final Logger log = LoggerFactory.getLogger(CustomFieldValueServiceImpl.class);
+
+    @Autowired
+    private CustomFieldValueRepository customFieldValueRepository;
+
+    @Autowired
+    private IssueCustomFieldValueService issueCustomFieldValueService;
+
+    @Override
+    protected JpaRepository<CustomFieldValue, Long> getRepository() {
+        return this.customFieldValueRepository;
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 異붽��븳�떎.
+    @Override
+    @Transactional
+    public void addCustomFieldValues(CustomField customField, List<String> values, CustomFieldType oldCustomFieldType) {
+        if (oldCustomFieldType != null) {
+            //  �뀓�뒪�듃 �엯�젰 �븘�뱶�씪 �븣�뒗 �씠�뒋�뿉�꽌 �궗�슜�릺怨� �엳�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶 媛� �솗�씤 �썑 �궘�젣 泥섎━
+            if (customField.getCustomFieldType().equals(CustomFieldType.INPUT)) {
+                //  �씠�쟾�뿉�룄 �뀓�뒪�듃 �븘�뱶���떎硫� �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 �궘�젣�븯吏� �븡�뒗�떎.
+                if (CustomFieldType.INPUT.equals(oldCustomFieldType)) {
+                    return;
+                }
+                //  �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪씠 蹂�寃쎈릺�뿀�쓣 �븣 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 �궗�슜�븯�뒗 �씠�뒋�뿉�꽌 �빐�떦 媛믪씠 議댁옱�븯�뒗吏� �솗�씤�븯怨� �뾾�뼱議뚯쑝硫� �궘�젣�빐以��떎.
+                this.issueCustomFieldValueService.checkExistIssueCustomFieldValue(customField, values, oldCustomFieldType);
+                return;
+            }
+
+            //  �떎以묒꽑�깮 �븘�뱶�뿉�꽌 �떒�씪 �꽑�깮 �븘�뱶濡� 蹂�寃쏀븷 寃쎌슦 紐⑤뱺 媛믪쓣 �궘�젣泥섎━�븳�떎.
+            if (oldCustomFieldType.equals(CustomFieldType.MULTI_SELECT) && customField.getCustomFieldType().equals(CustomFieldType.SINGLE_SELECT)) {
+                //  �씠�뒋�뿉�꽌 ���옣�맂 �빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 紐⑤몢 �궘�젣�븳�떎.
+                this.issueCustomFieldValueService.removeIssueCustomFieldValuesByCustomFieldId(customField);
+            }
+        }
+        else {
+            //  �뀓�뒪�듃 �엯�젰 �븘�뱶�씪 �븣�뒗 諛붾줈 醫낅즺
+            if (customField.getCustomFieldType().equals(CustomFieldType.INPUT)) {
+                return;
+            }
+        }
+
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪씠 0媛� �씪 寃쎌슦�뿉�뒗 諛붾줈 醫낅즺
+        if (values.size() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_OPTIONS_NOT_VALUE));
+        }
+
+        List<CustomFieldValue> customFieldValues = Lists.newArrayList();
+        List<String> addValues = Lists.newArrayList();
+        List<CustomFieldValue> removeValues = Lists.newArrayList();
+
+
+        //  �궘�젣�빐�빞�븷 ���긽�쓣 異붿텧�븳�떎.
+        for (CustomFieldValue customFieldValue : customField.getCustomFieldValues()) {
+            Boolean exist = false;
+
+            for (String value : values) {
+                if (value.equals(customFieldValue.getValue())) {
+                    exist = true;
+                    break;
+                }
+            }
+
+            //  �깉濡� �삱�씪�삩 媛믪뿉 湲곗〈 媛믪씠 �뾾�쑝硫� �궘�젣���긽
+            if (!exist) {
+                removeValues.add(customFieldValue);
+            }
+        }
+
+        //  異붽��빐�빞�븷 媛믪쓣 異붿텧�븳�떎.
+        for (String value : values) {
+            Boolean exist = false;
+
+            for (CustomFieldValue customFieldValue : customField.getCustomFieldValues()) {
+                if (customFieldValue.getValue().equals(value)) {
+                    exist = true;
+                    break;
+                }
+            }
+
+            //  �깉濡� �삱�씪�삩 媛믪씠 湲곗〈�뿉 �뾾�떎硫� 異붽� 媛�
+            if (!exist) {
+                addValues.add(value);
+            }
+        }
+
+        //  �궘�젣 ���긽 �궘�젣
+        if (removeValues.size() > 0) {
+            this.customFieldValueRepository.deleteInBatch(removeValues);
+        }
+
+        //  異붽� ���긽 ���옣
+        for (String value : addValues) {
+            CustomFieldValue customFieldValue = new CustomFieldValue(customField, value);
+            customFieldValues.add(customFieldValue);
+        }
+
+        this.customFieldValueRepository.saveAll(customFieldValues);
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪씠 蹂�寃쎈릺�뿀�쓣 �븣 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 �궗�슜�븯�뒗 �씠�뒋�뿉�꽌 �빐�떦 媛믪씠 議댁옱�븯�뒗吏� �솗�씤�븯怨� �뾾�뼱議뚯쑝硫� �궘�젣�빐以��떎.
+        this.issueCustomFieldValueService.checkExistIssueCustomFieldValue(customField, values, oldCustomFieldType);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/EventServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/EventServiceImpl.java
new file mode 100644
index 0000000..04ba05c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/EventServiceImpl.java
@@ -0,0 +1,158 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.Event;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.EventMapper;
+import kr.wisestone.owl.repository.EventRepository;
+import kr.wisestone.owl.service.EventService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.EventVo;
+import kr.wisestone.owl.vo.ResPage;
+import kr.wisestone.owl.web.condition.EventCondition;
+import kr.wisestone.owl.web.form.EventForm;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class EventServiceImpl extends AbstractServiceImpl<Event, Long, JpaRepository<Event, Long>> implements EventService {
+
+    @Autowired
+    private EventRepository eventRepository;
+
+    @Autowired
+    private EventMapper eventMapper;
+
+    @Override
+    protected JpaRepository<Event, Long> getRepository() {
+        return this.eventRepository;
+    }
+
+    //  Event �벑濡�
+    @Override
+    @Transactional
+    public Event addEvent(EventForm eventForm) {
+        //  Event �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+        this.verifyTitleAndDescription(eventForm.getTitle(), eventForm.getDescription());
+
+        eventForm.setStatus(Event.INACTIVATION);
+        Event event = ConvertUtil.copyProperties(eventForm, Event.class);
+
+        return this.eventRepository.saveAndFlush(event);
+    }
+
+    //  Event �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+    private void verifyTitleAndDescription(String title, String description) {
+        if (StringUtils.isEmpty(title) || StringUtils.isEmpty(description)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EVENT_EMPTY_CONTENT));
+        }
+    }
+
+    //  Event 議고쉶
+    @Override
+    @Transactional(readOnly = true)
+    public List<EventVo> findEvent(Map<String, Object> resJsonData,
+                                   EventCondition eventCondition, Pageable pageable) {
+
+        eventCondition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        eventCondition.setPageSize(pageable.getPageSize());
+        eventCondition.setTitle(eventCondition.getTitle());
+
+        List<Map<String, Object>> results = this.eventMapper.find(eventCondition);
+        Long totalCount = this.eventMapper.count(eventCondition);
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        List<EventVo> eventVos = ConvertUtil.convertListToListClass(results, EventVo.class);
+
+        for (EventVo eventVo : eventVos) {
+            Boolean bActivation = false;
+
+            if(eventVo.getStatus().equals(Event.ACTIVATION)) {
+                bActivation = true;
+            }
+            eventVo.activation =  bActivation;
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, eventVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        return eventVos;
+    }
+
+    //  Event �닔�젙
+    @Override
+    @Transactional
+    public Event modifyEvent(EventForm eventForm) {
+        //  Event �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+        this.verifyTitleAndDescription(eventForm.getTitle(), eventForm.getDescription());
+
+        Event event = this.getEvent(eventForm.getId());
+        ConvertUtil.copyProperties(eventForm, event, "id");
+
+        return this.eventRepository.saveAndFlush(event);
+    }
+
+    //  Event �닔�젙
+    @Override
+    @Transactional
+    public Event activeEvent(EventForm eventForm) {
+
+        boolean bActivation = eventForm.getActivation();
+
+        if(bActivation) {
+            eventForm.setStatus(Event.ACTIVATION);
+        } else {
+            eventForm.setStatus(Event.INACTIVATION);
+        }
+
+        Event event = this.getEvent(eventForm.getId());
+        ConvertUtil.copyProperties(eventForm, event, "id");
+
+        if(bActivation) {
+            this.eventRepository.updateInActivation(eventForm.getId());
+        }
+
+        return this.eventRepository.saveAndFlush(event);
+    }
+
+    //  Event�쓣 id 濡� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Event getEvent(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.EVENT_NOT_EXIST));
+        }
+
+        Event event = this.findOne(id);
+
+        if (event == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.EVENT_NOT_EXIST));
+        }
+
+        return event;
+    }
+
+    //  Event �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailEvent(Map<String, Object> resJsonData, EventCondition eventCondition) {
+        EventVo eventVo = new EventVo();
+
+        if (eventCondition.getId() != null) {
+            Event event = this.getEvent(eventCondition.getId());
+            eventVo = ConvertUtil.copyProperties(event, EventVo.class);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, eventVo);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/FaqServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/FaqServiceImpl.java
new file mode 100644
index 0000000..d796c94
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/FaqServiceImpl.java
@@ -0,0 +1,167 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.config.kafka.KafkaSender;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.Faq;
+import kr.wisestone.owl.domain.Guide;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.FaqMapper;
+import kr.wisestone.owl.repository.FaqRepository;
+import kr.wisestone.owl.service.FaqService;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.FaqVo;
+import kr.wisestone.owl.vo.ResPage;
+import kr.wisestone.owl.web.condition.FaqCondition;
+import kr.wisestone.owl.web.form.FaqForm;
+import kr.wisestone.owl.web.form.GuideForm;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class FaqServiceImpl extends AbstractServiceImpl<Faq, Long, JpaRepository<Faq, Long>> implements FaqService {
+
+    @Autowired
+    private FaqRepository faqRepository;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private KafkaSender kafkaSender;
+
+    @Autowired
+    private FaqMapper faqMapper;
+
+    @Override
+    protected JpaRepository<Faq, Long> getRepository() {
+        return this.faqRepository;
+    }
+
+    //  怨듭� �궗�빆 �벑濡�
+    @Override
+    @Transactional
+    public Faq addFaq(FaqForm faqForm) {
+        //  faq �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+        this.verifyTitleAndDescription(faqForm.getTitle(), faqForm.getDescription());
+
+        faqForm.setStatus(Faq.INACTIVATION);
+        Faq faq = ConvertUtil.copyProperties(faqForm, Faq.class);
+
+        return this.faqRepository.saveAndFlush(faq);
+    }
+
+    //  faq �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+    private void verifyTitleAndDescription(String title, String description) {
+        if (StringUtils.isEmpty(title) || StringUtils.isEmpty(description)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.FAQ_EMPTY_CONTENT));
+        }
+    }
+
+    //  faq 議고쉶
+    @Override
+    @Transactional(readOnly = true)
+    public List<FaqVo> findFaq(Map<String, Object> resJsonData,
+                               FaqCondition faqCondition, Pageable pageable) {
+
+        faqCondition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        faqCondition.setPageSize(pageable.getPageSize());
+        faqCondition.setTitle(faqCondition.getTitle());
+
+        List<Map<String, Object>> results = this.faqMapper.find(faqCondition);
+        Long totalCount = this.faqMapper.count(faqCondition);
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        List<FaqVo> faqVos = ConvertUtil.convertListToListClass(results, FaqVo.class);
+
+        //  faq �븘�씠�뵒 1 �� 愿�由ъ옄 - 愿�由ъ옄留� �닔�젙 媛��뒫
+        for (FaqVo faqVo : faqVos) {
+            Boolean bActivation = false;
+
+            if(faqVo.getStatus().equals(Faq.ACTIVATION)) {
+                bActivation = true;
+            }
+            faqVo.activation =  bActivation;
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, faqVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        return faqVos;
+    }
+
+    //  faq �닔�젙
+    @Override
+    @Transactional
+    public Faq modifyFaq(FaqForm faqForm) {
+        //  怨듭��궗�빆 �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+        this.verifyTitleAndDescription(faqForm.getTitle(), faqForm.getDescription());
+
+        Faq faq = this.getFaq(faqForm.getId());
+        ConvertUtil.copyProperties(faqForm, faq, "id");
+
+        return this.faqRepository.saveAndFlush(faq);
+    }
+
+    //  faq �닔�젙
+    @Override
+    @Transactional
+    public Faq activeFaq(FaqForm faqForm) {
+
+        boolean bActivation = faqForm.getActivation();
+
+        if(bActivation) {
+            faqForm.setStatus(Faq.ACTIVATION);
+        } else {
+            faqForm.setStatus(Faq.INACTIVATION);
+        }
+
+        Faq faq = this.getFaq(faqForm.getId());
+        ConvertUtil.copyProperties(faqForm, faq, "id");
+
+        return this.faqRepository.saveAndFlush(faq);
+    }
+
+    //  faq id 濡� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Faq getFaq(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.FAQ_NOT_EXIST));
+        }
+
+        Faq faq = this.findOne(id);
+
+        if (faq == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.FAQ_NOT_EXIST));
+        }
+
+        return faq;
+    }
+
+    //  faq �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailFaq(Map<String, Object> resJsonData, FaqCondition faqCondition) {
+        FaqVo faqVo = new FaqVo();
+
+        if (faqCondition.getId() != null) {
+            Faq faq = this.getFaq(faqCondition.getId());
+            faqVo = ConvertUtil.copyProperties(faq, FaqVo.class);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, faqVo);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/GanttServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/GanttServiceImpl.java
new file mode 100644
index 0000000..5abe8c5
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/GanttServiceImpl.java
@@ -0,0 +1,115 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.common.ExcelConditionCheck;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.ElasticSearchConstants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.CustomFieldType;
+import kr.wisestone.owl.domain.enumType.EmailType;
+import kr.wisestone.owl.domain.enumType.IssueHistoryType;
+import kr.wisestone.owl.domain.enumType.IssueStatusType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.IssueMapper;
+import kr.wisestone.owl.mapper.ProjectMapper;
+import kr.wisestone.owl.repository.IssueRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.*;
+import kr.wisestone.owl.util.DateUtil;
+import kr.wisestone.owl.vo.*;
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.IssueTypeCustomFieldCondition;
+import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.form.IssueCommentForm;
+import kr.wisestone.owl.web.form.IssueForm;
+import kr.wisestone.owl.web.view.ExcelView;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.StopWatch;
+import org.apache.poi.ss.usermodel.*;
+import org.jsoup.Jsoup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.ui.Model;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.*;
+
+@Service
+public class GanttServiceImpl extends AbstractServiceImpl<Issue, Long, JpaRepository<Issue, Long>> implements GanttService {
+
+    private static final Logger log = LoggerFactory.getLogger(GanttServiceImpl.class);
+
+    @Autowired
+    private IssueRepository issueRepository;
+
+    @Autowired
+    private IssueService issueService;
+
+    @Override
+    protected JpaRepository<Issue, Long> getRepository() {
+        return this.issueRepository;
+    }
+
+    @Override
+    @Transactional
+    public void addIssueVersion(Long id) {
+        issueService.addIssueVersion(id);
+    }
+
+
+    //  �씠�뒋瑜� �깮�꽦�븳�떎.
+    @Override
+    @Transactional
+    public Issue addIssue(IssueForm issueForm, List<MultipartFile> multipartFiles) {
+        return issueService.addIssue(issueForm, multipartFiles);
+    }
+
+    //  �씠�뒋 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueVo> findIssue(Map<String, Object> resJsonData,
+                                   IssueCondition issueCondition, Pageable pageable) {
+
+        return issueService.findChartIssue(resJsonData, issueCondition, pageable);
+    }
+
+    //  �씠�뒋 紐⑸줉�쓣 議고쉶�븳�떎(�봽濡쒖젥�듃�슜)
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueVo> findIssue(Map<String, Object> resJsonData,
+                                   ProjectCondition projectCondition, Pageable pageable) {
+
+        return issueService.findChartIssue(resJsonData, projectCondition, pageable);
+    }
+
+    //  �씠�뒋 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailIssue(Map<String, Object> resJsonData, IssueCondition issueCondition) {
+        issueService.detailIssue(resJsonData, issueCondition);
+    }
+
+    //  �씠�뒋瑜� �닔�젙�븳�떎.
+    @Override
+    @Transactional
+    public Issue modifyIssue(IssueForm issueForm, List<MultipartFile> multipartFiles) {
+        return  issueService.modifyIssue(issueForm, multipartFiles);
+    }
+
+    //  �씠�뒋瑜� �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void removeIssues(IssueForm issueForm) {
+        issueService.removeIssues(issueForm);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/GuideServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/GuideServiceImpl.java
new file mode 100644
index 0000000..4cb9a19
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/GuideServiceImpl.java
@@ -0,0 +1,161 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.Guide;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.GuideMapper;
+import kr.wisestone.owl.repository.GuideRepository;
+import kr.wisestone.owl.service.GuideService;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.GuideVo;
+import kr.wisestone.owl.vo.ManageUserVo;
+import kr.wisestone.owl.vo.ResPage;
+import kr.wisestone.owl.web.condition.GuideCondition;
+import kr.wisestone.owl.web.form.GuideForm;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class GuideServiceImpl extends AbstractServiceImpl<Guide, Long, JpaRepository<Guide, Long>> implements GuideService {
+
+    @Autowired
+    private GuideRepository guideRepository;
+
+    @Autowired
+    private GuideMapper guideMapper;
+
+    @Override
+    protected JpaRepository<Guide, Long> getRepository() {
+        return this.guideRepository;
+    }
+
+    //  guide �벑濡�
+    @Override
+    @Transactional
+    public Guide addGuide(GuideForm guideForm) {
+        //  guide �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+        this.verifyTitleAndDescription(guideForm.getTitle(), guideForm.getDescription());
+
+        guideForm.setStatus(Guide.INACTIVATION);
+        Guide guide = ConvertUtil.copyProperties(guideForm, Guide.class);
+
+        return this.guideRepository.saveAndFlush(guide);
+    }
+
+    //  guide �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+    private void verifyTitleAndDescription(String title, String description) {
+        if (StringUtils.isEmpty(title) || StringUtils.isEmpty(description)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.GUIDE_EMPTY_CONTENT));
+        }
+    }
+
+    //  guide 議고쉶
+    @Override
+    @Transactional(readOnly = true)
+    public List<GuideVo> findGuide(Map<String, Object> resJsonData,
+                                   GuideCondition guideCondition, Pageable pageable) {
+
+        guideCondition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        guideCondition.setPageSize(pageable.getPageSize());
+        guideCondition.setTitle(guideCondition.getTitle());
+
+        List<Map<String, Object>> results = this.guideMapper.find(guideCondition);
+        Long totalCount = this.guideMapper.count(guideCondition);
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        List<GuideVo> guideVos = ConvertUtil.convertListToListClass(results, GuideVo.class);
+
+        for (GuideVo guideVo : guideVos) {
+            Boolean bActivation = false;
+
+            if(guideVo.getStatus().equals(Guide.ACTIVATION)) {
+                bActivation = true;
+            }
+            guideVo.activation =  bActivation;
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, guideVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        return guideVos;
+    }
+
+    //  guide �닔�젙
+    @Override
+    @Transactional
+    public Guide modifyGuide(GuideForm guideForm) {
+        //  guide �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+        this.verifyTitleAndDescription(guideForm.getTitle(), guideForm.getDescription());
+
+        Guide guide = this.getGuide(guideForm.getId());
+        ConvertUtil.copyProperties(guideForm, guide, "id");
+
+        return this.guideRepository.saveAndFlush(guide);
+    }
+
+    //  guide �닔�젙
+    @Override
+    @Transactional
+    public Guide activeGuide(GuideForm guideForm) {
+
+        boolean bActivation = guideForm.getActivation();
+
+        if(bActivation) {
+            guideForm.setStatus(Guide.ACTIVATION);
+        } else {
+            guideForm.setStatus(Guide.INACTIVATION);
+        }
+
+        Guide guide = this.getGuide(guideForm.getId());
+        ConvertUtil.copyProperties(guideForm, guide, "id");
+
+        if(bActivation) {
+            this.guideRepository.updateInActivation(guideForm.getId());
+        }
+
+        return this.guideRepository.saveAndFlush(guide);
+    }
+
+    //  guide�쓣 id 濡� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Guide getGuide(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.GUIDE_NOT_EXIST));
+        }
+
+        Guide guide = this.findOne(id);
+
+        if (guide == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.GUIDE_NOT_EXIST));
+        }
+
+        return guide;
+    }
+
+    //  guide �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailGuide(Map<String, Object> resJsonData, GuideCondition guideCondition) {
+        GuideVo guideVo = new GuideVo();
+
+        if (guideCondition.getId() != null) {
+            Guide guide = this.getGuide(guideCondition.getId());
+            guideVo = ConvertUtil.copyProperties(guide, GuideVo.class);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, guideVo);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueCommentServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueCommentServiceImpl.java
new file mode 100644
index 0000000..832585a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueCommentServiceImpl.java
@@ -0,0 +1,139 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.IssueComment;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.IssueCommentRepository;
+import kr.wisestone.owl.service.IssueCommentService;
+import kr.wisestone.owl.service.IssueService;
+import kr.wisestone.owl.service.WorkspaceService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.IssueCommentVo;
+import kr.wisestone.owl.vo.UserVo;
+import kr.wisestone.owl.web.form.IssueCommentForm;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+
+@Service
+public class IssueCommentServiceImpl extends AbstractServiceImpl<IssueComment, Long, JpaRepository<IssueComment, Long>>
+        implements IssueCommentService {
+
+    static final Logger log = LoggerFactory.getLogger(IssueCommentServiceImpl.class);
+
+    @Autowired
+    private IssueCommentRepository issueCommentRepository;
+
+    @Autowired
+    private IssueService issueService;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Override
+    protected JpaRepository<IssueComment, Long> getRepository() {
+        return this.issueCommentRepository;
+    }
+
+    //  �뙎湲��쓣 �벑濡앺븳�떎.
+    @Override
+    @Transactional
+    public IssueComment addIssueComment(IssueCommentForm issueCommentForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        IssueComment issueComment = ConvertUtil.copyProperties(issueCommentForm, IssueComment.class);
+
+        this.verifyComment(issueCommentForm.getDescription());
+
+        Issue issue = this.issueService.getIssue(issueCommentForm.getIssueId());
+        issueComment.setIssue(issue);
+        issueComment.setWorkspace(issue.getProject().getWorkspace());
+
+        this.issueCommentRepository.saveAndFlush(issueComment);
+
+        return issueComment;
+    }
+
+    private void verifyComment(String comment) {
+        if (StringUtils.isEmpty(comment)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_COMMENT_NOT_COMMENT));
+        }
+
+        if (comment.length() > 300) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_COMMENT_MAX_LENGTH_OUT));
+        }
+    }
+
+    //  �뙎湲��쓣 �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void removeIssueComments(IssueCommentForm issueCommentForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        if (issueCommentForm.getRemoveIds().size() < 1) {
+            throw new OwlRuntimeException(MsgConstants.ISSUE_COMMENT_REMOVE_NOT_SELECT);
+        }
+
+        for (Long issueCommentId : issueCommentForm.getRemoveIds()) {
+            this.removeIssueComments(issueCommentId);
+        }
+
+        this.issueCommentRepository.flush();
+    }
+
+    //  �뙎湲��쓣 �궘�젣�븳�떎.
+    private void removeIssueComments(Long issueCommentId) {
+        IssueComment issueComment = this.getIssueComment(issueCommentId);
+        //  �뙎湲� �궘�젣 沅뚰븳 泥댄겕
+        this.checkRemovePermission(issueComment);
+
+        this.issueCommentRepository.delete(issueComment);
+    }
+
+    //  �뙎湲� �궘�젣 沅뚰븳�씠 �엳�뒗吏� 泥댄겕�븳�떎.
+    private void checkRemovePermission(IssueComment issueComment) {
+        UserVo userVo = this.webAppUtil.getLoginUser();
+
+        if (!issueComment.getRegisterId().equals(userVo.getId())) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_COMMENT_NOT_REMOVE_PERMISSION));
+        }
+    }
+
+    //  �뙎湲� �븘�씠�뵒濡� �뙎湲��쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public IssueComment getIssueComment(Long id ) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_COMMENT_NOT_EXIST));
+        }
+
+        IssueComment issueComment = this.findOne(id);
+
+        if (issueComment == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_COMMENT_NOT_EXIST));
+        }
+
+        return issueComment;
+    }
+
+    //  �씠�뒋�뿉 �벑濡앸맂 �뙎湲� 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueCommentVo> findIssueComment(Long issueId) {
+        return this.issueCommentRepository.findByIssueId(issueId);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueCustomFieldValueServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueCustomFieldValueServiceImpl.java
new file mode 100644
index 0000000..c35f695
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueCustomFieldValueServiceImpl.java
@@ -0,0 +1,355 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.CustomFieldType;
+import kr.wisestone.owl.domain.enumType.IssueHistoryType;
+import kr.wisestone.owl.mapper.IssueCustomFieldValueMapper;
+import kr.wisestone.owl.repository.IssueCustomFieldValueRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.vo.CustomFieldVo;
+import kr.wisestone.owl.vo.IssueCustomFieldValueVo;
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.IssueCustomFieldValueCondition;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.*;
+
+@Service
+public class IssueCustomFieldValueServiceImpl extends AbstractServiceImpl<IssueCustomFieldValue, Long, JpaRepository<IssueCustomFieldValue, Long>> implements IssueCustomFieldValueService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueCustomFieldValueServiceImpl.class);
+
+    @Autowired
+    private IssueCustomFieldValueRepository issueCustomFieldValueRepository;
+
+    @Autowired
+    private CustomFieldService customFieldService;
+
+    @Autowired
+    private IssueHistoryService issueHistoryService;
+
+    @Autowired
+    private IssueCustomFieldValueMapper issueCustomFieldValueMapper;
+
+    @Autowired
+    private IssueTypeCustomFieldService issueTypeCustomFieldService;
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    protected JpaRepository<IssueCustomFieldValue, Long> getRepository() {
+        return this.issueCustomFieldValueRepository;
+    }
+
+    //  �씠�뒋�뿉�꽌 �궗�슜�릺�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 �뾽�뜲�씠�듃�븳�떎.
+    @Override
+    @Transactional
+    public void modifyIssueCustomFieldValue(Issue issue, List<Map<String, Object>> issueCustomFields) {
+        //  �쟾泥� �씠�뒋�뿉 �궗�슜�릺�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶 媛� �궘�젣
+        issue.getIssueCustomFieldValues().clear();
+        List<IssueCustomFieldValue> issueCustomFieldValues = Lists.newArrayList();
+
+        for (Map<String, Object> map : issueCustomFields) {
+            Map<String, Object> result = new HashMap<>();
+            //  customFieldVo �뿉�꽌 �궗�슜�옄 �젙�쓽 �븘�뱶�� �씠�뒋 �쑀�삎�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂대�� 媛��졇�삩�떎.
+            this.getCustomFieldAndIssueTypeCustomField(map, issue, result);
+            List<String> useValues = MapUtil.getStrings(map, "useValues");
+
+            if (useValues != null) {
+                for (String useValue : useValues) {
+                    if (!StringUtils.isEmpty(useValue)) {
+                        //  Xss 怨듦꺽 諛⑹뼱瑜� �쐞�빐 script 怨듬갚�쑝濡� 移섑솚
+                        IssueCustomFieldValue issueCustomFieldValue = new IssueCustomFieldValue(issue, (CustomField)result.get("customField"), (IssueTypeCustomField)result.get("issueTypeCustomField"), useValue);
+                        issueCustomFieldValues.add(issueCustomFieldValue);
+                    }
+                }
+            }
+        }
+
+        if (issueCustomFieldValues.size() > 0) {
+            this.issueCustomFieldValueRepository.saveAll(issueCustomFieldValues);
+        }
+    }
+
+    //  customFieldVo �뿉�꽌 �궗�슜�옄 �젙�쓽 �븘�뱶�� �씠�뒋 �쑀�삎�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂대�� 媛��졇�삩�떎.
+    @Override
+    public void getCustomFieldAndIssueTypeCustomField(Map<String, Object> map, Issue issue, Map<String, Object> result) {
+        Map<String, Object> customFieldMap = (Map<String, Object>) MapUtil.getObject(map, "customFieldVo");
+        CustomField customField = this.customFieldService.getCustomField(MapUtil.getLong(customFieldMap, "id"));
+        IssueTypeCustomField issueTypeCustomField = this.issueTypeCustomFieldService.findByProjectIdAndIssueTypeIdAndCustomFieldId(issue.getProject().getId(), issue.getIssueType().getId(), customField.getId());
+
+        result.put("customField", customField);
+        result.put("issueTypeCustomField", issueTypeCustomField);
+    }
+
+    //  �씠�뒋�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueCustomFieldValueVo> findByIssueId(Long issueId) {
+        List<IssueCustomFieldValueVo> issueCustomFieldValueVos = Lists.newArrayList();
+        List<IssueCustomFieldValue> issueCustomFieldValues = this.issueCustomFieldValueRepository.findByIssueId(issueId);
+
+        for (IssueCustomFieldValue issueCustomFieldValue : issueCustomFieldValues) {
+            IssueCustomFieldValueVo issueCustomFieldValueVo = ConvertUtil.copyProperties(issueCustomFieldValue, IssueCustomFieldValueVo.class);
+            CustomFieldVo customFieldVo = ConvertUtil.copyProperties(issueCustomFieldValue.getCustomField(), CustomFieldVo.class);
+            customFieldVo.setCustomFieldType(issueCustomFieldValue.getCustomField().getCustomFieldType().toString());
+            issueCustomFieldValueVo.setCustomFieldVo(customFieldVo);
+            issueCustomFieldValueVos.add(issueCustomFieldValueVo);
+        }
+
+        return issueCustomFieldValueVos;
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪씠 蹂�寃쎈릺�뿀�쓣 �븣 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 �궗�슜�븯�뒗 �씠�뒋�뿉�꽌 �빐�떦 媛믪씠 議댁옱�븯�뒗吏� �솗�씤�븯怨� �뾾�뼱議뚯쑝硫� �궘�젣�빐以��떎.
+    @Override
+    @Transactional
+    public void checkExistIssueCustomFieldValue(CustomField customField, List<String> values, CustomFieldType oldCustomFieldType) {
+
+        List<IssueCustomFieldValue> removeIssueCustomFieldValues = Lists.newArrayList();
+        Map<String, Object> issueMaps = new HashMap<>(); //  �씠�젰�쓣 �궓湲� �씠�뒋 異붿텧
+
+        //  �씠�뒋�뿉�꽌 �궗�슜�릺�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶 媛� �젙蹂대�� 異붿텧�븳�떎.
+        this.saveOriginalIssueCustomFieldValues(customField, values, issueMaps, removeIssueCustomFieldValues);
+
+        //  �궘�젣�븷 媛� �궘�젣
+        if (removeIssueCustomFieldValues.size() > 0) {
+            this.issueCustomFieldValueRepository.deleteInBatch(removeIssueCustomFieldValues);
+            //  �궘�젣 �썑 �궓�� �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶 媛� �젙蹂대�� issueMaps �뿉 ���옣�븳�떎.
+            this.saveNewIssueCustomFieldValues(customField, issueMaps);
+            //  蹂�寃쎈맂 �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶 媛� �젙蹂대�� �엳�뒪�넗由щ줈 �궓湲대떎.
+            this.saveIssueCustomFieldValueHistory(issueMaps, customField, oldCustomFieldType);
+        }
+    }
+
+    //  �씠�뒋�뿉�꽌 �궗�슜�릺�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶 媛� �젙蹂대�� 異붿텧�븳�떎.
+    private void saveOriginalIssueCustomFieldValues(CustomField customField, List<String> values, Map<String, Object> issueMaps, List<IssueCustomFieldValue> removeIssueCustomFieldValues) {
+        //  �빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �궗�슜�븯怨� �엳�뒗 �씠�뒋 �젙蹂대�� 異붿텧�븳�떎.
+        List<IssueCustomFieldValue> issueCustomFieldValues = this.findByCustomFieldId(customField);
+
+        //  媛� �씠�뒋�쓽 �씠�쟾 媛� 異붿텧
+        for (IssueCustomFieldValue issueCustomFieldValue : issueCustomFieldValues) {
+            Boolean existValue = false;
+
+            for (String value : values) {
+                if (issueCustomFieldValue.getUseValue().equals(value)) {
+                    existValue = true;
+                    break;
+                }
+            }
+
+            if (!existValue) {
+                //  �궘�젣�븷 媛�
+                removeIssueCustomFieldValues.add(issueCustomFieldValue);
+            }
+
+            String issueId = issueCustomFieldValue.getIssue().getId().toString();
+
+            if (MapUtil.getObject(issueMaps, issueId) == null) {
+                Map<String, Object> issue = new HashMap<>();
+                issue.put("issue", issueCustomFieldValue.getIssue());
+                issue.put("oldValues", Lists.newArrayList(issueCustomFieldValue.getUseValue()));
+                issue.put("newValues", Lists.newArrayList());
+
+                issueMaps.put(issueId, issue);
+            }
+            else {
+                Map<String, Object> issue = (Map<String, Object>) issueMaps.get(issueId);
+                List<String> oldValues = (List<String>) MapUtil.getObject(issue, "oldValues");
+                oldValues.add(issueCustomFieldValue.getUseValue());
+                issue.put("oldValues", oldValues);
+            }
+        }
+    }
+
+    //  �궘�젣 �썑 �궓�� �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶 媛� �젙蹂대�� issueMaps �뿉 ���옣�븳�떎.
+    private void saveNewIssueCustomFieldValues(CustomField customField, Map<String, Object> issueMaps) {
+        //  �궘�젣 �썑 �떎�떆 �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 議고쉶�븳�떎.
+        List<IssueCustomFieldValue> newIssueCustomFieldValues = this.findByCustomFieldId(customField);
+
+        for (IssueCustomFieldValue newIssueCustomFieldValue : newIssueCustomFieldValues) {
+            String issueId = newIssueCustomFieldValue.getIssue().getId().toString();
+
+            if (MapUtil.getObject(issueMaps, issueId) != null) {
+                Map<String, Object> issue = (Map<String, Object>) issueMaps.get(issueId);
+                List<String> newValues = (List<String>) MapUtil.getObject(issue, "newValues");
+                newValues.add(newIssueCustomFieldValue.getUseValue());
+                issue.put("newValues", newValues);
+            }
+        }
+    }
+
+    //  蹂�寃쎈맂 �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶 媛� �젙蹂대�� �엳�뒪�넗由щ줈 �궓湲대떎.
+    private void saveIssueCustomFieldValueHistory(Map<String, Object> issueMaps, CustomField customField, CustomFieldType oldCustomFieldType) {
+        Iterator<String> iterator = issueMaps.keySet().iterator();
+
+        while (iterator.hasNext()) {
+            StringBuilder issueChangeDescription = new StringBuilder();
+            Map<String, Object> issueMap = (Map<String, Object>) issueMaps.get(iterator.next());
+            Issue issue = (Issue) MapUtil.getObject(issueMap, "issue");
+            List<String> oldValues = (List<String>) MapUtil.getObject(issueMap, "oldValues");
+            List<String> newValues = (List<String>) MapUtil.getObject(issueMap, "newValues");
+
+            //  諛곗뿴�뿉 �엳�뒗 媛믪쓣 蹂닿린 醫뗪쾶 異붿텧�븳�떎.
+            StringBuilder oldValueString = this.setValueString(oldValues);
+            StringBuilder newValueString = this.setValueString(newValues);
+
+            //  �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪씠 蹂�寃쎈릺�뿀�뒗吏� �솗�씤�븳�떎.
+            if (this.checkChangeIssueCustomFieldValues(oldValueString, newValueString)) {
+                continue;
+            }
+
+            //  媛믪씠 鍮꾩뿀�쓣 �븣 媛믪씠 �뾾�떎�뒗 湲��옄瑜� �궓寃⑥��떎.
+            this.setEmptyString(oldValueString, oldCustomFieldType);
+            //  媛믪씠 鍮꾩뿀�쓣 �븣 媛믪씠 �뾾�떎�뒗 湲��옄瑜� �궓寃⑥��떎.
+            this.setEmptyString(newValueString, oldCustomFieldType);
+
+            //  �씠�쟾�뿉 臾몄옄�뿴�븘�뱶���떎媛� �떎瑜� �븘�뱶濡� 蹂�寃쏀븳 寃쎌슦�굹 �씠�쟾�뿉 臾몄옄�뿴 �븘�뱶媛� �븘�땲�뿀�떎媛� 臾몄옄�뿴 �븘�뱶濡� 諛붾�� 寃쎌슦
+            if (CustomFieldType.INPUT.equals(oldCustomFieldType) && !customField.getCustomFieldType().equals(CustomFieldType.INPUT) ||
+                    !CustomFieldType.INPUT.equals(oldCustomFieldType) && customField.getCustomFieldType().equals(CustomFieldType.INPUT)) {
+                //  �궗�슜�옄 �젙�쓽 �븘�뱶 �쑀�삎�씠 蹂�寃쎈릺�뼱 �씠�뒋�뿉�꽌 �궗�슜�맂 �샃�뀡 媛믪씠 �궘�젣�릺�뿀�떎怨� ���옣�븳�떎.
+                this.issueHistoryService.recodeChangeCustomFieldType(customField, oldValueString.toString(), issueChangeDescription);
+            }
+            else {
+                //  �궗�슜�옄 �젙�쓽 �븘�뱶�쓽 �샃�뀡 媛믪씠 �궘�젣�릺�뼱 �씠�뒋�뿉�꽌 �궗�슜�맂 媛믪씠 �궘�젣�맆 寃쎌슦 蹂�寃� �젙蹂대�� ���옣�븳�떎.
+                this.issueHistoryService.recodeRemoveCustomFieldOptionValue(customField, oldValueString.toString(), newValueString.toString(), issueChangeDescription);
+            }
+
+            this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, issueChangeDescription.toString());
+        }
+    }
+
+    //  諛곗뿴�뿉 �엳�뒗 媛믪쓣 蹂닿린 醫뗪쾶 異붿텧�븳�떎.
+    private StringBuilder setValueString(List<String> values) {
+        StringBuilder stringBuilder = new StringBuilder();
+        for (String value : values) {
+            stringBuilder.append(value);
+            stringBuilder.append(", ");
+        }
+
+        return stringBuilder;
+    }
+
+    //  媛믪씠 鍮꾩뿀�쓣 �븣 媛믪씠 �뾾�떎�뒗 湲��옄瑜� �궓寃⑥��떎.
+    private void setEmptyString(StringBuilder stringBuilder, CustomFieldType customFieldType) {
+        if (stringBuilder.toString().equals("")) {
+            if (CustomFieldType.INPUT.equals(customFieldType)) {
+                stringBuilder.append("<span translate=\"common.noValueEntered\">�엯�젰�븳 媛믪씠 �뾾�뒿�땲�떎.</span>");
+            }
+            else {
+                stringBuilder.append("<span translate=\"common.noValueSelected\">�꽑�깮�븳 媛믪씠 �뾾�뒿�땲�떎.</span>");
+            }
+        }
+    }
+
+    //  �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪씠 蹂�寃쎈릺�뿀�뒗吏� �솗�씤�븳�떎.
+    private boolean checkChangeIssueCustomFieldValues(StringBuilder oldValueString, StringBuilder newValueString) {
+        if (oldValueString.toString().equals(newValueString.toString())) {
+            return true;
+        }
+
+        return false;
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �궗�슜�븯怨� �엳�뒗 媛� �젙蹂대�� 異붿텧�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueCustomFieldValue> findByCustomFieldId(CustomField customField) {
+        return this.issueCustomFieldValueRepository.findByCustomFieldId(customField.getId());
+    }
+
+    //  �봽濡쒖젥�듃�뿉 �궗�슜�옄 �젙�쓽 �븘�뱶 �뿰寃곗씠 �빐�젣�맆 寃쎌슦 �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void removeIssueCustomFieldValue(Long issueTypeCustomFieldId) {
+        this.issueCustomFieldValueMapper.deleteIssueCustomFieldValue(issueTypeCustomFieldId);
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 ���옣�븳 �씠�뒋瑜� 李얜뒗�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public boolean find(IssueCondition condition, Set<String> issueIds) {
+        boolean customFieldSearch = false;
+        int firstCount = 0;
+
+        Set<String> tempIssueIds = new HashSet<>();
+
+        for (Map<String, Object> customField : condition.getIssueCustomFields()) {
+            IssueCustomFieldValueCondition issueCustomFieldValueCondition = IssueCustomFieldValueCondition.make(customField);
+
+            if (issueCustomFieldValueCondition.getUseValues().size() > 0 || !StringUtils.isEmpty(issueCustomFieldValueCondition.getUseValue())) {
+                issueCustomFieldValueCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+                issueCustomFieldValueCondition.setCustomFieldId(MapUtil.getLong(customField, "id"));
+
+                //  �궗�슜�옄 �젙�쓽 �븘�뱶 媛� 寃��깋 �떆�옉
+                customFieldSearch = true;
+                Map<String, Object> result = new HashMap<>();
+
+                switch (CustomFieldType.valueOf(issueCustomFieldValueCondition.getCustomFieldType())) {
+                    case INPUT:
+                        result = this.issueCustomFieldValueMapper.findLikeUseValue(issueCustomFieldValueCondition);
+                        break;
+                    case MULTI_SELECT:
+                    case SINGLE_SELECT:
+                        result = this.issueCustomFieldValueMapper.findByUseValue(issueCustomFieldValueCondition);
+                        break;
+                }
+
+                String issueIdList = MapUtil.getString(result, "issueIds");
+
+                if (!StringUtils.isEmpty(issueIdList)) {
+                    Set<String> results = new HashSet<>(Arrays.asList(issueIdList.split(",")));
+
+                    if (firstCount > 0) {
+                        Set<String> commonKeys = new HashSet<>(tempIssueIds);
+                        commonKeys.retainAll(results);
+                        tempIssueIds = commonKeys;
+                    }
+                    else {
+                        tempIssueIds = results;
+                    }
+                }
+
+                firstCount++;
+            }
+        }
+
+        issueIds.addAll(tempIssueIds);
+
+        return customFieldSearch;
+    }
+
+    //  �씠�뒋�뿉�꽌 ���옣�븳 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<Map<String, Object>> findInIssueIds(IssueCondition issueCondition) {
+        return this.issueCustomFieldValueMapper.findInIssueIds(issueCondition);
+    }
+
+    //  �씠�뒋�뿉�꽌 ���옣�맂 �빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 紐⑤몢 �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void removeIssueCustomFieldValuesByCustomFieldId(CustomField customField) {
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �궗�슜�븯怨� �엳�뒗 媛� �젙蹂대�� 異붿텧�븳�떎.
+        List<IssueCustomFieldValue> issueCustomFieldValues = this.findByCustomFieldId(customField);
+        List<Long> ids = Lists.newArrayList();
+
+        for (IssueCustomFieldValue issueCustomFieldValue : issueCustomFieldValues) {
+            ids.add(issueCustomFieldValue.getId());
+        }
+
+        if (ids.size() > 0) {
+            this.issueCustomFieldValueMapper.deleteByIssueCustomFieldValueId(ids);
+        }
+    }
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java
new file mode 100644
index 0000000..ecd9ce8
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java
@@ -0,0 +1,774 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.ElasticSearchConstants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.CustomFieldType;
+import kr.wisestone.owl.domain.enumType.IssueHistoryType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.IssueHistoryMapper;
+import kr.wisestone.owl.repository.IssueHistoryRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.*;
+import kr.wisestone.owl.vo.IssueHistoryVo;
+import kr.wisestone.owl.web.condition.IssueHistoryCondition;
+import kr.wisestone.owl.web.form.IssueForm;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.*;
+
+@Service
+public class IssueHistoryServiceImpl extends AbstractServiceImpl<IssueHistory, Long, JpaRepository<IssueHistory, Long>> implements IssueHistoryService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueHistoryServiceImpl.class);
+
+    @Autowired
+    private IssueHistoryRepository issueHistoryRepository;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private IssueHistoryMapper issueHistoryMapper;
+
+    @Autowired
+    private IssueRiskService issueRiskService;
+
+    @Autowired
+    private IssueCustomFieldValueService issueCustomFieldValueService;
+
+    @Value("${email.userName}")
+    private String systemEmail;
+
+    @Override
+    protected JpaRepository<IssueHistory, Long> getRepository() {
+        return this.issueHistoryRepository;
+    }
+
+    //  �씠�젰 �깮�꽦
+    @Override
+    @Transactional
+    public void addIssueHistory(Issue issue, IssueHistoryType issueHistoryType, String issueChangeDescription) {
+        IssueHistory issueHistory = new IssueHistory();
+        issueHistory.setIssue(issue);
+        issueHistory.setProject(issue.getProject());
+        issueHistory.setIssueHistoryType(issueHistoryType);
+        StringBuilder description = new StringBuilder();
+        //  �씠�젰 �젙蹂대�� 留뚮뱾�뼱 �궦�떎.
+        this.makeDescription(description, issueHistoryType, issueChangeDescription);
+        issueHistory.setDescription(description.toString());
+
+        this.issueHistoryRepository.saveAndFlush(issueHistory);
+
+        //  鍮꾩듂�븳 �떆媛꾩뿉 �뿰�냽�쟻�쑝濡� 湲곕줉�쓣 �궓寃쇰뒗吏� �솗�씤�븯怨� 諛쒓껄�맂 湲곕줉�쓣 臾띠뼱�꽌 ���옣�븳�떎.
+        this.detectSameTimeIssueHistory(issue);
+    }
+
+    //  �씠�젰 �젙蹂대�� 留뚮뱾�뼱 �궦�떎.
+    @Override
+    public void makeDescription(StringBuilder description, IssueHistoryType issueHistoryType, String issueChangeDescription) {
+        description.append("<div class=\"activity-text\">");
+
+        //  �깮�꽦, �닔�젙, �궘�젣�뿉 ���빐 湲곕줉�쓣 �궓湲대떎.
+        switch (issueHistoryType) {
+            case ADD:
+                description.append("<h6 class=\"change\"><span class=\"dot\"></span><span translate=\"common.createIssue\">�씠�뒋 �깮�꽦</span>");
+                description.append("<span class='activity-timestamp'>");
+                description.append(DateUtil.convertDateToStr(new Date()));
+                description.append(" (");
+                description.append(this.webAppUtil.getLoginUser().getName());
+                description.append(" - ");
+                description.append(CommonUtil.decryptAES128(this.webAppUtil.getLoginUser().getAccount()));
+                description.append(")");
+                description.append("</span></h6>");
+                break;
+
+            case MODIFY:
+                description.append("<h6 class=\"creat\"><span class=\"dot\"></span><span translate=\"common.updateIssue\">�씠�뒋 蹂�寃�</span>");
+                description.append("<span class=\"activity-timestamp\">");
+                description.append(DateUtil.convertDateToStr(new Date()));
+                description.append(" (");
+
+                if (this.webAppUtil.getLoginUser() != null) {
+                    description.append(this.webAppUtil.getLoginUser().getName());
+                    description.append(" - ");
+                    description.append(CommonUtil.decryptAES128(this.webAppUtil.getLoginUser().getAccount()));
+                }
+                else {
+                    description.append("OWL-ITS-SYSTEM");
+                    description.append(" - ");
+                    description.append(this.systemEmail);
+                }
+
+                description.append(")");
+                description.append("</span></h6>");
+                description.append(issueChangeDescription);
+                break;
+
+            case DELETE:
+                description.append("<h6 class=\"delete\"><span class=\"dot\"></span><span translate=\"common.deleteIssue\">�씠�뒋 �궘�젣</span>");
+                description.append("<span class=\"activity-timestamp\">");
+                description.append(DateUtil.convertDateToStr(new Date()));
+                description.append(" (");
+                description.append(this.webAppUtil.getLoginUser().getName());
+                description.append(" - ");
+                description.append(CommonUtil.decryptAES128(this.webAppUtil.getLoginUser().getAccount()));
+                description.append(")");
+                description.append("</span></h6>");
+                break;
+        }
+
+        description.append("</div>");
+    }
+
+    //  鍮꾩듂�븳 �떆媛꾩뿉 �뿰�냽�쟻�쑝濡� 湲곕줉�쓣 �궓寃쇰뒗吏� �솗�씤�븯怨� 諛쒓껄�맂 湲곕줉�쓣 臾띠뼱�꽌 ���옣�븳�떎.
+    private void detectSameTimeIssueHistory(Issue issue) {
+        StringBuilder description = new StringBuilder();
+        List<IssueHistoryVo> issueHistoryVos = this.issueHistoryRepository.findByIssueId(issue.getId());
+        List<IssueHistoryVo> saveIssueHistoryVos = Lists.newArrayList();
+
+        for (int count = 0; count < (issueHistoryVos.size() - 1); count++) {
+            IssueHistoryVo issueHistoryVo = issueHistoryVos.get(count);
+            IssueHistoryVo nextIssueHistoryVo = issueHistoryVos.get(count + 1);
+
+            Date beginDate = DateUtil.convertStrToDate(issueHistoryVo.getRegisterDate());
+            Date endDate = DateUtil.convertStrToDate(nextIssueHistoryVo.getRegisterDate());
+
+            long diff = beginDate.getTime() - endDate.getTime();
+            //  1遺� �씠�궡�뿉 湲곕줉�맂 �씠�뒋 蹂�寃� �궡�뿭�� �빀移쒕떎.
+            if ((diff/1000) < 61) {
+                //  以묐났�맂 �씠�뒋 �씠�젰�씠 �엳�뒗吏� �솗�씤�븯怨� 以묐났�릺吏� �븡�븯�쑝硫� 諛곗뿴�뿉 異붽��븳�떎.
+                this.checkDuplicationHistoryList(saveIssueHistoryVos, issueHistoryVo);
+                //  以묐났�맂 �씠�뒋 �씠�젰�씠 �엳�뒗吏� �솗�씤�븯怨� 以묐났�릺吏� �븡�븯�쑝硫� 諛곗뿴�뿉 異붽��븳�떎.
+                this.checkDuplicationHistoryList(saveIssueHistoryVos, nextIssueHistoryVo);
+            }
+        }
+
+        List<Long> removeIds = Lists.newArrayList();
+
+        for (IssueHistoryVo issueHistoryVo : saveIssueHistoryVos) {
+            description.append(issueHistoryVo.getDescription());
+            removeIds.add(issueHistoryVo.getId());
+        }
+
+        //  �빀移� 湲곕줉 �깮�꽦
+        if (removeIds.size() > 0) {
+            Long lastUpdateIssueHistoryId = removeIds.get(removeIds.size() - 1);
+            //  留덉�留� ���긽�뿉 湲곕줉�쓣 �삷湲대떎.
+            IssueHistory issueHistory = this.findOne(lastUpdateIssueHistoryId);
+            issueHistory.setIssueHistoryType(IssueHistoryType.TOTAL);
+            issueHistory.setDescription(description.toString());
+            this.issueHistoryRepository.saveAndFlush(issueHistory);
+
+            //  �씠�쟾 湲곕줉 �궘�젣
+            for (Long removeId : removeIds) {
+                //  留덉�留� ���긽�� �궘�젣�븯吏� �븡�뒗�떎.
+                if (!lastUpdateIssueHistoryId.equals(removeId)) {
+                    this.issueHistoryRepository.deleteById(removeId);
+                }
+            }
+        }
+    }
+
+    //  以묐났�맂 �씠�뒋 �씠�젰�씠 �엳�뒗吏� �솗�씤�븯怨� 以묐났�릺吏� �븡�븯�쑝硫� 諛곗뿴�뿉 異붽��븳�떎.
+    private void checkDuplicationHistoryList(List<IssueHistoryVo> issueHistoryVos, IssueHistoryVo issueHistoryVo) {
+        boolean duplicate = false;
+
+        for (IssueHistoryVo historyVo : issueHistoryVos) {
+            if (historyVo.getId().equals(issueHistoryVo.getId())) {
+                duplicate = true;
+                break;
+            }
+        }
+
+        if (!duplicate) {
+            issueHistoryVos.add(issueHistoryVo);
+        }
+    }
+
+    //  �빐�떦 �궗�슜�옄�쓽 �씠�뒋 湲곕줉 �젙蹂대�� 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findIssueHistory(Map<String, Object> resJsonData, IssueHistoryCondition issueHistoryCondition) {
+        //  議고쉶�븯�뒗 �궗�슜�옄 �젙蹂�
+        issueHistoryCondition.setUserId(this.webAppUtil.getLoginId());
+
+        if (StringUtils.isEmpty(issueHistoryCondition.getSearchPeriod())) {
+            issueHistoryCondition.setSearchPeriod(DateUtil.LAST_SEVEN_DAYS);
+        }
+
+        //  寃��깋 議곌굔 吏곸젒 �엯�젰�씠 �븘�땺 寃쎌슦
+        if (!issueHistoryCondition.getSearchPeriod().equals(DateUtil.CUSTOM_INPUT)) {
+            //  寃��깋 �씪�옄瑜� 援ы븳�떎.
+            List<Date> searchDates = CommonUtil.findSearchPeriod(issueHistoryCondition.getSearchPeriod());
+
+            //  �궇吏쒓� 寃��깋�릺吏� �븡�븯�쑝硫� �삤瑜�
+            if (searchDates.size() < 1) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.WIDGET_SEARCH_DATE_NOT_FOUND));
+            }
+
+            //  寃��깋 議곌굔�뿉 �떆�옉�씪, 醫낅즺�씪�쓣 �꽕�젙�븳�떎.
+            issueHistoryCondition.setSearchStartDate(DateUtil.convertDateToYYYYMMDD(searchDates.get(0)));
+            issueHistoryCondition.setSearchEndDate(DateUtil.convertDateToYYYYMMDD(DateUtil.addDays(searchDates.get(searchDates.size() - 1), 1)));
+        }
+        else {
+            issueHistoryCondition.setSearchStartDate(DateUtil.convertDateToStr(DateUtil.convertStrToDate(issueHistoryCondition.getSearchStartDate(), "yyyy-MM-dd")));
+            issueHistoryCondition.setSearchEndDate(DateUtil.convertDateToStr(DateUtil.addDays(DateUtil.convertStrToDate(issueHistoryCondition.getSearchEndDate(), "yyyy-MM-dd"), 1)));
+        }
+
+        List<Map<String, Object>> results = this.issueHistoryMapper.find(issueHistoryCondition);
+
+        Map<String, Object> issueHistoryMaps = new HashMap<>();
+        Map<String, Object> issueHistoryGroups = new HashMap<>();
+        issueHistoryMaps.put("searchStartDate", issueHistoryCondition.getSearchStartDate());
+        issueHistoryMaps.put("searchEndDate", issueHistoryCondition.getSearchEndDate());
+
+        for (Map<String, Object> result : results) {
+            IssueHistoryVo issueHistoryVo = ConvertUtil.convertMapToClass(result, IssueHistoryVo.class);
+            List<IssueHistoryVo> issueHistoryVos = Lists.newArrayList();
+
+            String recodeDate = issueHistoryVo.getRegisterDate();
+
+            //  �빐�떦 �슂�씪�뿉 �젙蹂닿� �뾾�쓣 寃쎌슦 諛곗뿴�쓣 留뚮뱾�뼱 �씠�뒋 湲곕줉 �젙蹂대�� �떞�뒗�떎.
+            if (MapUtil.getObject(issueHistoryGroups, recodeDate) == null) {
+                issueHistoryVos.add(issueHistoryVo);
+                issueHistoryGroups.put(recodeDate, issueHistoryVos);
+            }
+            else {
+                //  �엳�쓣 寃쎌슦�뿉�뒗 �빐�떦 諛곗뿴�쓣 媛��졇�� �씠�뒋 湲곕줉 �젙蹂대�� 異붽��븳�떎.
+                issueHistoryVos =  (List<IssueHistoryVo>)MapUtil.getObject(issueHistoryGroups, recodeDate);
+                issueHistoryVos.add(issueHistoryVo);
+            }
+
+            if (issueHistoryVos.size() > 0) {
+                issueHistoryGroups.put(recodeDate, issueHistoryVos);
+            }
+        }
+
+        issueHistoryMaps.put("issueHistoryGroups", issueHistoryGroups);
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueHistoryMaps);
+
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_HISTORY_FIND));
+    }
+
+
+    //  �씠�뒋 �븘�씠�뵒�뿉 �빐�떦�븯�뒗 湲곕줉 �젙蹂대�� 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueHistoryVo> findIssueHistory(Long issueId) {
+        return this.issueHistoryRepository.findByIssueId(issueId);
+    }
+
+    //  �씠�뒋 蹂�寃� �궡�뿭�쓣 異붿텧�븳�떎.
+    @Override
+    public StringBuilder detectIssueChange(Issue issue, IssueForm issueForm, Project project, IssueStatus issueStatus, IssueType issueType, Priority priority, Severity severity, List<MultipartFile> files) {
+        StringBuilder description = new StringBuilder();
+
+        //  �씠�뒋 �봽濡쒖젥�듃 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+        this.detectProject(issue, issueForm, description, project);
+        //  �씠�뒋 以묒슂�룄 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+        this.detectIssueSeverity(issue, issueForm, description, severity);
+        //  �씠�뒋 �슦�꽑�닚�쐞 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+        this.detectIssuePriority(issue, issueForm, description, priority);
+        //  �씠�뒋 �긽�깭 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+        this.detectIssueStatus(issue, issueForm, description, issueStatus);
+        //  �씠�뒋 ���엯 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+        this.detectIssueType(issue, issueForm, description, issueType);
+        //  �씠�뒋�뿉 泥⑤��맂 �뙆�씪�뿉 ���빐 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+        this.detectAttachedFile(issueForm, description, files);
+        //  �씠�뒋 湲곌컙 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+        this.detectIssuePeriod(issue, issueForm, description);
+        //  �떞�떦�옄 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+        this.detectIssueManager(issue, issueForm, description);
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+        this.detectCustomField(issue, issueForm, description);
+
+        //  �씠�뒋 �젣紐� 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+        if (!issue.getTitle().equals(issueForm.getTitle())) {
+            description.append("<li><span translate=\"common.updateTitle\">�젣紐⑹씠 蹂�寃쎈릺�뿀�뒿�땲�떎.</span> </li>");
+        }
+
+        //  �씠�뒋 �궡�슜 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+        if (!issue.getDescription().equals(issueForm.getDescription())) {
+            description.append("<li><span translate=\"common.updateContent\">�궡�슜�씠 蹂�寃쎈릺�뿀�뒿�땲�떎.</span> </li>");
+        }
+
+        if (description.toString().length() > 1){
+            StringBuilder firstEnd = new StringBuilder();
+            firstEnd.append("<ul class=\"activity-list\">");
+            firstEnd.append(description.toString());
+            firstEnd.append("</ul>");
+            return firstEnd;
+        }
+
+        return description;
+    }
+
+    //  �씠�뒋 �봽濡쒖젥�듃 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+    @Override
+    public void detectProject(Issue issue, IssueForm issueForm, StringBuilder description, Project project) {
+        if (!issue.getProject().getId().equals(issueForm.getProjectId())) {
+            String title = "<span translate=\"common.updateProject\">�봽濡쒖젥�듃媛� 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>";
+            //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+            this.makeIssueHistoryHtml(description, title, issue.getProject().getName(), project.getName());
+        }
+    }
+
+    //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+    private void makeIssueHistoryHtml(StringBuilder description, String title, String beforeValue, String afterValue) {
+        description.append("<li>");
+        description.append(title);
+        description.append("&nbsp;<span class=\"fc-purple\">");
+        description.append(beforeValue);
+        description.append("</span>");
+        description.append("<i class=\"os-icon os-icon-arrow-right fc-grey\"></i>");
+        description.append("<span class=\"text-primary bold\">");
+        description.append(afterValue);
+        description.append("</span>");
+        description.append("</li>");
+    }
+
+    //  �씠�뒋 以묒슂�룄 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+    @Override
+    public void detectIssueSeverity(Issue issue, IssueForm issueForm, StringBuilder description, Severity severity) {
+        if (!issue.getSeverity().getId().equals(issueForm.getSeverityId())) {
+            String title = "<span translate=\"common.updateSeverity\">以묒슂�룄媛� 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>";
+            //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+            this.makeIssueHistoryHtml(description, title, issue.getSeverity().getName(), severity.getName());
+        }
+    }
+
+    //  �씠�뒋 �슦�꽑�닚�쐞 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+    @Override
+    public void detectIssuePriority(Issue issue, IssueForm issueForm, StringBuilder description, Priority priority) {
+        if (!issue.getPriority().getId().equals(issueForm.getPriorityId())) {
+            String title = "<span translate=\"common.updatePriority\">�슦�꽑�닚�쐞媛� 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>";
+            //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+            this.makeIssueHistoryHtml(description, title, issue.getPriority().getName(), priority.getName());
+        }
+    }
+
+    //  �씠�뒋 �긽�깭 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+    @Override
+    public void detectIssueStatus(Issue issue, IssueForm issueForm, StringBuilder description, IssueStatus issueStatus) {
+        if (!issue.getIssueStatus().getId().equals(issueForm.getIssueStatusId())) {
+            String title = "<span translate=\"common.updateHasStatus\">�긽�깭媛� 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>";
+            //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+            this.makeIssueHistoryHtml(description, title, issue.getIssueStatus().getName(), issueStatus.getName());
+
+            //  �씠�뒋 �쐞�뿕 愿�由ъ뿉 �긽�깭 蹂�寃� �젙蹂대�� �뾽�뜲�씠�듃�븳�떎. - �떞�떦�옄 蹂�寃�
+            this.issueRiskService.modifyIssueRisk(issue, true, false, issueForm.getIssueStatusId());
+        }
+    }
+
+    //  �삁�빟 諛쒖깮�쑝濡� �씠�뒋 �긽�깭 蹂�寃쎈맂 �젙蹂대�� 湲곕줉�븳�떎.
+    @Override
+    public void detectReservationIssueStatus(Issue issue, StringBuilder description, IssueStatus issueStatus) {
+        description.append("<ul class=\"activity-list\">");
+
+        String title = "<span translate=\"common.updateHasStatusReservation\">�씠�뒋 諛쒖깮 �삁�빟�씪�씠 �릺�뼱 �긽�깭媛� 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>";
+        //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+        this.makeIssueHistoryHtml(description, title, issue.getIssueStatus().getName(), issueStatus.getName());
+
+        description.append("</ul>");
+    }
+
+    //  �썙�겕�뵆濡쒖슦�뿉�꽌 �씠�뒋 �긽�깭媛� �궘�젣�릺�뼱 �긽�깭媛� 蹂�寃쎈맂 �젙蹂대�� 湲곕줉�븳�떎.
+    @Override
+    public void recordRemoveWorkflowToIssueStatus(String oldIssueStatusName, String newIssueStatusName, StringBuilder description) {
+        description.append("<ul class=\"activity-list\">");
+
+        String title = "<span translate=\"common.upddetectReservationIssueStatusateWorkflowNotExist\">蹂�寃쎈맂 �썙�겕�뵆濡쒖슦�뿉�꽌 �긽�깭媛� 議댁옱�븯吏� �븡�븘 �씠�뒋�쓽 �긽�깭媛� 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>";
+        //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+        this.makeIssueHistoryHtml(description, title, oldIssueStatusName, newIssueStatusName);
+
+        description.append("</ul>");
+    }
+
+    //  �씠�뒋 ���엯 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+    @Override
+    public void detectIssueType(Issue issue, IssueForm issueForm, StringBuilder description, IssueType issueType) {
+        if (!issue.getIssueType().getId().equals(issueForm.getIssueTypeId())) {
+            String title = "<span translate=\"common.updateIssueType\">�씠�뒋 ���엯�씠 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>";
+            //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+            this.makeIssueHistoryHtml(description, title, issue.getIssueType().getName(), issueType.getName());
+        }
+    }
+
+    //  �씠�뒋 湲곌컙 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+    @Override
+    public void detectIssuePeriod(Issue issue, IssueForm issueForm, StringBuilder description) {
+        //  湲곌컙 �떊洹� �꽕�젙 �삉�뒗 湲곌컙 �궘�젣�떆
+        if ((issue.getStartDate() == null && issueForm.getStartDate() != null) || (issue.getStartDate() != null && issueForm.getStartDate() == null)) {
+            //  湲곌컙 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+            this.recodeChangeIssuePeriod(issue, issueForm, description);
+        }
+
+        if (issue.getStartDate() != null && issueForm.getStartDate() != null) {
+            if (!issue.getStartDate().equals(issueForm.getStartDate()) || !issue.getCompleteDate().equals(issueForm.getCompleteDate())) {
+                //  湲곌컙 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+                this.recodeChangeIssuePeriod(issue, issueForm, description);
+            }
+        }
+    }
+
+    //  湲곌컙 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+    private void recodeChangeIssuePeriod(Issue issue, IssueForm issueForm, StringBuilder description) {
+        String title = "<span translate=\"common.updatePeriod\">湲곌컙�씠 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>";
+        //  湲곌컙 �젙蹂대�� 湲곕줉�븳�떎.
+        String beforePeriod = this.recodeIssuePeriod(issue.getStartDate(), issue.getCompleteDate());
+        String afterPeriod = this.recodeIssuePeriod(issueForm.getStartDate(), issueForm.getCompleteDate());
+        //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+        this.makeIssueHistoryHtml(description, title, beforePeriod, afterPeriod);
+    }
+
+    //  湲곌컙 �젙蹂대�� 湲곕줉�븳�떎.
+    private String recodeIssuePeriod(String startDate, String completeDate) {
+        StringBuilder recodeIssuePeriod = new StringBuilder();
+
+        if (startDate != null) {
+            recodeIssuePeriod.append(startDate);
+            recodeIssuePeriod.append(" ~ ");
+            recodeIssuePeriod.append(completeDate);
+        }
+        else {
+            recodeIssuePeriod.append("<span translate=\"common.unspecified\">誘몄��젙</span>");
+        }
+
+        return recodeIssuePeriod.toString();
+    }
+
+
+    // �뿰愿� �씪媛� 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+    @Override
+    public void detectRelationIssue(IssueHistoryType type, IssueRelation issueRelation, StringBuilder description) {
+       if (type == IssueHistoryType.ADD) {
+           description.append("<span translate=\"issue.relationIssueAddHistory\">�뿰愿� �씪媛먯씠 異붽��릺�뿀�뒿�땲�떎. </span>");
+           description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issueRelation.getRelationIssue().getTitle() + "</span>");
+       } else {
+           description.append("<span translate=\"issue.relationIssueRemoveHistory\">�뿰愿� �씪媛먯씠 �궘�젣�릺�뿀�뒿�땲�떎. " + issueRelation.getRelationIssue().getTitle() + "</span>");
+           description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issueRelation.getRelationIssue().getTitle() + "</span>");
+       }
+    }
+
+    //  �떞�떦�옄 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+    @Override
+    public void detectIssueManager(Issue issue, IssueForm issueForm, StringBuilder description) {
+        boolean saveIssueRisk = false;  //  �씠�뒋 �쐞�뿕 愿�由ъ뿉 �씠以� ���옣�릺�뒗 寃껋쓣 諛⑹��븯湲� �쐞�븳 援щ텇 媛�
+
+        //  �떞�떦�옄 �닔媛� �떖�젮�젘�쓣 寃쎌슦
+        if (issue.getIssueUsers().size() != issueForm.getUserIds().size()) {
+            this.recodeIssueManager(issue, issueForm, description);
+            //  �씠�뒋 �쐞�뿕 愿�由ъ뿉 �떞�떦�옄 蹂�寃� �젙蹂대�� �뾽�뜲�씠�듃�븳�떎. - �떞�떦�옄 蹂�寃�
+            this.issueRiskService.modifyIssueRisk(issue, false, true, null);
+            saveIssueRisk = true;
+        }
+
+        //  �떞�떦�옄 �닔�뒗 媛숈쑝�굹 �궗�슜�옄媛� �떖�씪議뚯쓣 寃쎌슦
+        if (issue.getIssueUsers().size() > 0 && issueForm.getUserIds().size() > 0) {
+            //  �씠�쟾 �떞�떦�옄 �몴�떆
+            for (IssueUser issueUser : issue.getIssueUsers()) {
+                boolean change = true;
+                User user = issueUser.getUser();
+
+                for (Long userId : issueForm.getUserIds()) {
+                    if (user.getId().equals(userId)) {
+                        change = false;
+                        break;
+                    }
+                }
+
+                if (change) {
+                    //  �떞�떦�옄 蹂�寃� �젙蹂� 湲곕줉
+                    this.recodeIssueManager(issue, issueForm, description);
+                    //  �떞�떦�옄�닔媛� �떖�씪議뚯쓣 寃쎌슦�뿉 ���옣�릺吏� �븡�븯�떎硫� �뿬湲곗꽌 �씠�뒋 �쐞�뿕 愿�由� ���옣�쓣 �븳�떎.
+                    if (!saveIssueRisk) {
+                        //  �씠�뒋 �쐞�뿕 愿�由ъ뿉 �떞�떦�옄 蹂�寃� �젙蹂대�� �뾽�뜲�씠�듃�븳�떎. - �떞�떦�옄 蹂�寃�
+                        this.issueRiskService.modifyIssueRisk(issue, false, true, null);
+                    }
+
+                    break;
+                }
+            }
+        }
+    }
+
+    //  �떞�떦�옄 蹂�寃� �젙蹂� 湲곕줉
+    private void recodeIssueManager(Issue issue, IssueForm issueForm, StringBuilder description) {
+        String title = "<span translate=\"common.updateAssignee\">�떞�떦�옄媛� 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>";
+        StringBuilder beforeUser = new StringBuilder();
+
+        //  �씠�쟾 �떞�떦�옄 �몴�떆
+        for (IssueUser issueUser : issue.getIssueUsers()) {
+            beforeUser.append(issueUser.getUser().getName());
+            beforeUser.append(", ");
+        }
+        //  �떞�떦�옄媛� �뾾�뿀�쑝硫� �뾾�쓬�쑝濡� �몴�떆
+        if (issue.getIssueUsers().size() < 1) {
+            beforeUser.append("<span translate=\"common.none\">�뾾�쓬</span>");
+        }
+
+        StringBuilder afterUser = new StringBuilder();
+        for (Long userId : issueForm.getUserIds()) {
+            User user = this.userService.getUser(userId);
+            afterUser.append(user.getName());
+            afterUser.append(", ");
+        }
+
+        //  �떞�떦�옄媛� �뾾�뿀�쑝硫� �뾾�쓬�쑝濡� �몴�떆
+        if (issueForm.getUserIds().size() < 1) {
+            afterUser.append("<span translate=\"common.none\">�뾾�쓬</span>");
+        }
+
+        //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+        this.makeIssueHistoryHtml(description, title, beforeUser.toString(), afterUser.toString());
+    }
+
+    //  �씠�뒋�뿉 泥⑤��맂 �뙆�씪�뿉 ���빐 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+    @Override
+    public void detectAttachedFile(IssueForm issueForm, StringBuilder description, List<MultipartFile> files) {
+        //  �궘�젣 ���긽�씠 �엳嫄곕굹 �깉濡� �뾽濡쒕뱶 �맂 �뙆�씪�씠 議댁옱�븷 寃쎌슦 蹂�寃� �젙蹂� 湲곕줉
+        if (issueForm.getRemoveFiles().size() > 0 && files.size() > 0) {
+            description.append("<li><span translate=\"common.updateAttachment\">泥⑤� �뙆�씪�씠 蹂�寃쎈릺�뿀�뒿�땲�떎.</span> </li>");
+        }
+
+        //  �궘�젣 ���긽�씠 �엳嫄곕굹 �깉濡� �뾽濡쒕뱶 �맂 �뙆�씪�씠 議댁옱�븷 寃쎌슦 蹂�寃� �젙蹂� 湲곕줉
+        if (issueForm.getRemoveFiles().size() > 0 && files.size() < 1) {
+            description.append("<li><span translate=\"common.deleteAttachment\">泥⑤� �뙆�씪�씠 �궘�젣�릺�뿀�뒿�땲�떎.</span> </li>");
+        }
+
+        //  �궘�젣 ���긽�씠 �엳嫄곕굹 �깉濡� �뾽濡쒕뱶 �맂 �뙆�씪�씠 議댁옱�븷 寃쎌슦 蹂�寃� �젙蹂� 湲곕줉
+        if (issueForm.getRemoveFiles().size() < 1 && files.size() > 0) {
+            description.append("<li><span translate=\"common.registerAttachment\">泥⑤� �뙆�씪�씠 �벑濡앸릺�뿀�뒿�땲�떎.</span> </li>");
+        }
+    }
+
+    //  �씠�뒋�뿉�꽌 �궗�슜�릺�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶 蹂�寃� �젙蹂대�� 湲곕줉�븳�떎.
+    @Override
+    public void detectCustomField(Issue issue, IssueForm issueForm, StringBuilder description) {
+        //  �씠�뒋 �뤌�뿉�꽌 �삱�씪�삩 �궗�슜�옄 �젙�쓽 �븘�뱶 蹂�寃� �젙蹂대�� 異붿텧�빐�꽌 媛앹껜濡� 留뚮뱺�떎.
+        List<IssueCustomFieldValue> formIssueCustomFieldValues = this.getIssueCustomFieldValuesByIssueForm(issue, issueForm);
+        List<Long> passMultiSelectCustomFields = Lists.newArrayList();
+
+        for (IssueCustomFieldValue issueCustomFieldValue : formIssueCustomFieldValues) {
+            CustomField customField = issueCustomFieldValue.getCustomField();
+
+            switch (customField.getCustomFieldType()) {
+                case INPUT :
+                case SINGLE_SELECT :
+                    boolean existIssueCustomFieldValue = false;
+
+                    for (IssueCustomFieldValue savedIssueCustomFieldValue : issue.getIssueCustomFieldValues()) {
+                        if (customField.getId().equals(savedIssueCustomFieldValue.getCustomField().getId())) {
+                            existIssueCustomFieldValue = true;
+
+                            if (!issueCustomFieldValue.getUseValue().equals(savedIssueCustomFieldValue.getUseValue())) {
+                                //  湲곗〈�뿉 �엳�뜕 媛믪쓣 蹂�寃쏀븳 寃쎌슦
+                                this.recodeCustomFieldValue(savedIssueCustomFieldValue.getUseValue(), issueCustomFieldValue.getUseValue(), customField.getName(), description, customField.getCustomFieldType());
+                            }
+
+                            break;
+                        }
+                    }
+
+                    if (!existIssueCustomFieldValue) {
+                        //  湲곗〈�뿉 ���옣�맂 �궡�뿭�씠 �뾾�쑝硫댁꽌 �떊洹쒕줈 ���옣�븳 媛믪씠 怨듬갚�씪 寃쎌슦�뒗 ���옣�븯吏� �븡�뒗�떎.
+                        if (!StringUtils.isEmpty(issueCustomFieldValue.getUseValue())) {
+                            //  �떊洹쒕줈 媛믪쓣 ���옣�븳 寃쎌슦
+                            this.recodeCustomFieldValue("", issueCustomFieldValue.getUseValue(), customField.getName(), description, customField.getCustomFieldType());
+                        }
+                    }
+
+                    break;
+                case MULTI_SELECT :
+                    this.recodeCaseMultiSelect(customField, issue, formIssueCustomFieldValues, passMultiSelectCustomFields, description);
+                    break;
+            }
+        }
+    }
+
+    //  �씠�뒋 �뤌�뿉�꽌 �삱�씪�삩 �궗�슜�옄 �젙�쓽 �븘�뱶 蹂�寃� �젙蹂대�� 異붿텧�빐�꽌 媛앹껜濡� 留뚮뱺�떎.
+    private List<IssueCustomFieldValue> getIssueCustomFieldValuesByIssueForm(Issue issue, IssueForm issueForm) {
+        List<IssueCustomFieldValue> formIssueCustomFieldValues = Lists.newArrayList();
+
+        //  蹂�寃쏀븯�젮�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 IssueCustomFieldValue 媛앹껜濡� 留뚮뱺�떎.
+        for (Map<String, Object> map : issueForm.getIssueCustomFields()) {
+            Map<String, Object> result = new HashMap<>();
+            //  customFieldVo �뿉�꽌 �궗�슜�옄 �젙�쓽 �븘�뱶�� �씠�뒋 �쑀�삎�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂대�� 媛��졇�삩�떎.
+            this.issueCustomFieldValueService.getCustomFieldAndIssueTypeCustomField(map, issue, result);
+            CustomField customField = (CustomField)result.get("customField");
+            IssueTypeCustomField issueTypeCustomField = (IssueTypeCustomField)result.get("issueTypeCustomField");
+            List<String> useValues = MapUtil.getStrings(map, "useValues");
+
+            if (useValues != null) {
+                for (String useValue : useValues) {
+                    IssueCustomFieldValue issueCustomFieldValue = new IssueCustomFieldValue(issue, customField, issueTypeCustomField, useValue);
+                    formIssueCustomFieldValues.add(issueCustomFieldValue);
+                }
+
+                //  �씠�뒋 �닔�젙�뿉�꽌 �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �꽑�깮�븯吏� �븡�븯�쓣 寃쎌슦�뿉�뒗 useValue 媛믪쓣 怨듬갚�쑝濡� �빐�꽌 媛앹껜瑜� �깮�꽦�븳�떎.
+                if (useValues.size() < 1) {
+                    IssueCustomFieldValue issueCustomFieldValue = new IssueCustomFieldValue(issue, customField, issueTypeCustomField, "");
+                    formIssueCustomFieldValues.add(issueCustomFieldValue);
+                }
+            }
+        }
+
+        return formIssueCustomFieldValues;
+    }
+
+    //  硫��떚 ���젆�듃 �쑀�삎�뿉 ���빐 蹂�寃� �궡�뿭�쓣 �솗�씤�븯怨� 湲곕줉�븳�떎.
+    private void recodeCaseMultiSelect(CustomField customField, Issue issue, List<IssueCustomFieldValue> formIssueCustomFieldValues, List<Long> passMultiSelectCustomFields, StringBuilder description) {
+        boolean passMultiSelect = false;
+        //  �븳踰� 泥댄겕�븳 硫��떚 ���젆�듃�뒗 �뜑 �씠�긽 蹂�寃� �궡�뿭�쓣 ���옣�븯吏� �븡�뒗�떎. (issue custom field value �뿉�뒗 硫��떚���젆�듃 媛� 留덈떎 ���옣�릺�뼱�엳�뼱�꽌...)
+        for (Long passMultiSelectCustomFieldId : passMultiSelectCustomFields) {
+            if (customField.getId().equals(passMultiSelectCustomFieldId)) {
+                passMultiSelect = true;
+                break;
+            }
+        }
+
+        if (passMultiSelect) {
+            return;
+        }
+        else {
+            passMultiSelectCustomFields.add(customField.getId());
+        }
+
+        //  �뤌�뿉 �엳�뒗 硫��떚 ���젆�듃 媛� 異붿텧 -  �빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뿉�꽌 �궗�슜�맂 媛�
+        List<String> formCustomFieldValues = this.getMultiSelectCustomFieldValues(customField, formIssueCustomFieldValues);
+        //  ���옣�릺�뼱 �엳�뒗 硫��떚 ���젆�듃 媛믪쓣 異붿텧
+        List<String> savedCustomFieldValues = this.getMultiSelectCustomFieldValues(customField, Lists.newArrayList(issue.getIssueCustomFieldValues()));
+
+        //  蹂�寃� �젙蹂� ���옣
+        if (formCustomFieldValues.size() != savedCustomFieldValues.size()) {
+            this.recodeMultiSelectCustomFieldValue(savedCustomFieldValues, formCustomFieldValues, customField.getName(), description);
+        }
+        else {
+            for (String formCustomFieldValue : formCustomFieldValues) {
+                boolean checkValue = false;
+
+                for (String savedCustomFieldValue : savedCustomFieldValues) {
+                    if (formCustomFieldValue.equals(savedCustomFieldValue)) {
+                        checkValue = true;
+                        break;
+                    }
+                }
+
+                if (!checkValue) {
+                    //  蹂�寃� �젙蹂� ���옣
+                    this.recodeMultiSelectCustomFieldValue(savedCustomFieldValues, formCustomFieldValues, customField.getName(), description);
+                    break;
+                }
+            }
+        }
+    }
+
+    //  硫��떚 ���젆�듃�뿉 �엳�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 異붿텧�븳�떎.
+    private List<String> getMultiSelectCustomFieldValues(CustomField customField, List<IssueCustomFieldValue> issueCustomFieldValues) {
+        List<String> customFieldValues = Lists.newArrayList();
+
+        //  �뤌�뿉 �엳�뒗 硫��떚 ���젆�듃 媛� 異붿텧
+        for (IssueCustomFieldValue issueCustomFieldValue : issueCustomFieldValues) {
+            if (issueCustomFieldValue.getCustomField().getCustomFieldType().equals(CustomFieldType.MULTI_SELECT) && customField.getId().equals(issueCustomFieldValue.getCustomField().getId())) {
+                if (!StringUtils.isEmpty(issueCustomFieldValue.getUseValue())) {
+                    customFieldValues.add(issueCustomFieldValue.getUseValue());
+                }
+            }
+        }
+
+        return customFieldValues;
+    }
+
+    //  硫��떚 ���젆�듃�쓽 蹂�寃쎈맂 媛믪쓣 湲곕줉�븳�떎.
+    private void recodeMultiSelectCustomFieldValue(List<String> oldValues, List<String> newValues, String customFieldName, StringBuilder description) {
+        StringBuilder oldValueBuilder = new StringBuilder();
+        StringBuilder newValueBuilder = new StringBuilder();
+        //  諛곗뿴�뿉�꽌 媛믪쓣 異붿텧�븳�떎.
+        this.setListValue(oldValues, oldValueBuilder);
+        //  諛곗뿴�뿉�꽌 媛믪쓣 異붿텧�븳�떎.
+        this.setListValue(newValues, newValueBuilder);
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 蹂�寃� �젙蹂대�� ���옣�븳�떎.
+        this.recodeCustomFieldValue(oldValueBuilder.toString(), newValueBuilder.toString(), customFieldName, description, CustomFieldType.MULTI_SELECT);
+    }
+
+    //  諛곗뿴�뿉�꽌 媛믪쓣 異붿텧�븳�떎.
+    private void setListValue(List<String> values, StringBuilder stringBuilder) {
+        for (String value : values) {
+            stringBuilder.append(value);
+            stringBuilder.append(", ");
+        }
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 蹂�寃� �젙蹂대�� ���옣�븳�떎.
+    private void recodeCustomFieldValue(String oldValue, String newValue, String customFieldName, StringBuilder description, CustomFieldType customFieldType) {
+        String title = "(" + customFieldName + ")<span translate=\"common.updateCustomField\">�궗�슜�옄 �젙�쓽 �븘�뱶媛� 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>";
+        //  湲곕줉�뿉 �궓湲곕뒗 �궗�슜�옄 �젙�쓽 �븘�뱶媛� 怨듬갚 媛믪씠硫� �븣�븘蹂� �닔 �엳寃� �븣由쇱쓣 �꽔�뼱以��떎.
+        String beforeValue = this.checkEmptyCustomFieldValue(oldValue, customFieldType);
+        String afterValue = this.checkEmptyCustomFieldValue(newValue,  customFieldType);
+        //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+        this.makeIssueHistoryHtml(description, title, beforeValue, afterValue);
+    }
+
+    //  湲곕줉�뿉 �궓湲곕뒗 �궗�슜�옄 �젙�쓽 �븘�뱶媛� 怨듬갚 媛믪씠硫� �븣�븘蹂� �닔 �엳寃� �븣由쇱쓣 �꽔�뼱以��떎.
+    private String checkEmptyCustomFieldValue(String value, CustomFieldType customFieldType) {
+        String result = "";
+
+        if (StringUtils.isEmpty(value)) {
+            switch(customFieldType) {
+                case INPUT:
+                    result = "<span translate=\"common.noValueEntered\">�엯�젰�븳 媛믪씠 �뾾�뒿�땲�떎.</span>";
+                    break;
+                case SINGLE_SELECT:
+                case MULTI_SELECT:
+                    result = "<span translate=\"common.noValueSelected\">�꽑�깮�븳 媛믪씠 �뾾�뒿�땲�떎.</span>";
+                    break;
+            }
+        }
+        else {
+            result = value;
+        }
+
+        return result;
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶�쓽 �샃�뀡 媛믪씠 �궘�젣�릺�뼱 �씠�뒋�뿉�꽌 �궗�슜�맂 媛믪씠 �궘�젣�맆 寃쎌슦 蹂�寃� �젙蹂대�� ���옣�븳�떎.
+    @Override
+    @Transactional
+    public void recodeRemoveCustomFieldOptionValue(CustomField customField, String oldValue, String newValue, StringBuilder description) {
+        description.append("<ul class=\"activity-list\">");
+        String title = "(" + customField.getName() + ")<span translate=\"common.updateIssueCustomField\">�궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪씠 蹂�寃쎈릺�뼱 �씠�뒋�쓽 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪씠 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>";
+        //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+        this.makeIssueHistoryHtml(description, title, oldValue, newValue);
+
+        description.append("</ul>");
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �쑀�삎�씠 蹂�寃쎈릺�뼱 �씠�뒋�뿉�꽌 �궗�슜�맂 �샃�뀡 媛믪씠 �궘�젣�릺�뿀�떎怨� ���옣�븳�떎.
+    @Override
+    @Transactional
+    public void recodeChangeCustomFieldType(CustomField customField, String oldValue, StringBuilder description) {
+        description.append("<ul class=\"activity-list\">");
+
+        String title = "(" + customField.getName() + ")<span translate=\"common.updateIssueCustomFieldType\">�궗�슜�옄 �젙�쓽 �븘�뱶 �쑀�삎�씠 蹂�寃쎈릺�뼱 �씠�뒋�쓽 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪씠 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>";
+        String newValue = "<span translate=\"common.noValueEntered\">�엯�젰�븳 媛믪씠 �뾾�뒿�땲�떎.</span>";
+        //  �씠�젰 �젙蹂대�� html �깭洹몃줈 留뚮뱾�뼱 以��떎.
+        this.makeIssueHistoryHtml(description, title, oldValue, newValue);
+
+        description.append("</ul>");
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueNumberGeneratorServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueNumberGeneratorServiceImpl.java
new file mode 100644
index 0000000..ddadcbd
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueNumberGeneratorServiceImpl.java
@@ -0,0 +1,77 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.IssueNumberGenerator;
+import kr.wisestone.owl.domain.Project;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.IssueNumberGeneratorRepository;
+import kr.wisestone.owl.service.IssueNumberGeneratorService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Iterator;
+import java.util.Map;
+
+@Service
+public class IssueNumberGeneratorServiceImpl extends AbstractServiceImpl<IssueNumberGenerator, Long, JpaRepository<IssueNumberGenerator, Long>> implements IssueNumberGeneratorService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueNumberGeneratorServiceImpl.class);
+
+    @Autowired
+    private IssueNumberGeneratorRepository issueNumberGeneratorRepository;
+
+    @Override
+    protected JpaRepository<IssueNumberGenerator, Long> getRepository() {
+        return this.issueNumberGeneratorRepository;
+    }
+
+    //  媛� �봽濡쒖젥�듃�쓽 �씠�뒋 踰덊샇瑜� �옄�룞�쑝濡� �깮�꽦�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Long generateIssueNumber(Project project) {
+        IssueNumberGenerator issueNumberGenerator = this.issueNumberGeneratorRepository.findByProjectId(project.getId());
+
+        if (issueNumberGenerator == null) {
+            issueNumberGenerator = new IssueNumberGenerator(project, 0L);
+        }
+        else {
+            issueNumberGenerator.setNumber(issueNumberGenerator.getNumber() + 1);
+        }
+
+        this.issueNumberGeneratorRepository.saveAndFlush(issueNumberGenerator);
+
+        return issueNumberGenerator.getNumber();
+    }
+
+    //  �씠�뒋 �뿊�� import 媛� �셿猷뚮맂 �썑 �궗�슜�맂 issue number 瑜� �뾽�뜲�씠�듃�빐以��떎.
+    @Override
+    @Transactional
+    public void updateIssueNumber(Map<Long, Long> issueNumberMaps) {
+        Iterator iterator = issueNumberMaps.keySet().iterator();
+
+        while(iterator.hasNext()) {
+            Long projectId = (Long)iterator.next();
+            IssueNumberGenerator issueNumberGenerator = this.issueNumberGeneratorRepository.findByProjectId(projectId);
+
+            if (issueNumberGenerator == null) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.ISSUE_NUMBER_GENERATOR_NOT_EXIST));
+            }
+
+            Long issueNumber = issueNumberMaps.get(projectId);
+
+            if (issueNumber == null) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.ISSUE_NUMBER_NOT_EXIST));
+            }
+
+            issueNumberGenerator.setNumber(--issueNumber);
+            this.issueNumberGeneratorRepository.saveAndFlush(issueNumberGenerator);
+        }
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueRelationServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueRelationServiceImpl.java
new file mode 100644
index 0000000..c613a5d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueRelationServiceImpl.java
@@ -0,0 +1,121 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.IssueHistory;
+import kr.wisestone.owl.domain.IssueRelation;
+import kr.wisestone.owl.domain.enumType.IssueHistoryType;
+import kr.wisestone.owl.repository.IssueRelationRepository;
+import kr.wisestone.owl.service.IssueHistoryService;
+import kr.wisestone.owl.service.IssueRelationService;
+import kr.wisestone.owl.service.IssueService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.vo.IssueRelationVo;
+import kr.wisestone.owl.vo.IssueVo;
+import kr.wisestone.owl.vo.ResPage;
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.IssueRelationCondition;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+
+import javax.transaction.Transactional;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class IssueRelationServiceImpl extends AbstractServiceImpl<IssueRelation, Long, JpaRepository<IssueRelation, Long>> implements IssueRelationService {
+
+    @Autowired
+    private IssueRelationRepository issueRelationRepository;
+
+    @Autowired
+    private IssueService issueService;
+
+    @Autowired
+    private IssueHistoryService issueHistoryService;
+
+    @Override
+    protected IssueRelationRepository getRepository() {
+        return this.issueRelationRepository;
+    }
+
+    // �뿰愿� �씪媛� 異붽�
+    @Override
+    public void addRelationIssue(Map<String, Object> resJsonData, IssueRelationCondition condition) {
+        if (condition != null) {
+            Issue issue = this.issueService.getIssue(condition.getIssueId());
+
+            IssueRelation issueRelation = new IssueRelation();
+            issueRelation.setIssue(issue);
+            issueRelation.setRelationIssue(issueService.getIssue(condition.getRelationIssueId()));
+            issueRelation.setRelationIssueType(condition.getRelationIssueType());
+
+            issueRelationRepository.saveAndFlush(issueRelation);
+
+            StringBuilder sb = new StringBuilder();
+            issueHistoryService.detectRelationIssue(IssueHistoryType.ADD, issueRelation, sb);
+            issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, sb.toString());
+        }
+    }
+
+    // �뿰愿� �씪媛� 媛��졇�삤湲�
+    @Override
+    public List<IssueVo> findRelationIssue(Map<String, Object> resJsonData, IssueRelationCondition condition, Pageable pageable) {
+        List<IssueRelation> issueRelations = issueRelationRepository.findAllByIssueId(condition.getIssueId());
+        List<IssueVo> issueVos = new ArrayList<>();
+
+        if (issueRelations != null) {
+            for (IssueRelation issueRelation : issueRelations) {
+                issueVos.add(ConvertUtil.copyProperties(issueRelation.getRelationIssue(), IssueVo.class));
+            }
+
+            int totalCount = issueVos.size();
+
+            resJsonData.put(Constants.RES_KEY_CONTENTS, issueVos);
+            resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                1, totalCount));
+        }
+
+        return  issueVos;
+    }
+
+    // �뿰愿� �씪媛� 媛��졇�삤湲�
+    @Override
+    public List<IssueVo> findRelationIssue(Long issueId) {
+        List<IssueRelation> issueRelations = issueRelationRepository.findAllByIssueId(issueId);
+        List<IssueVo> issueVos = new ArrayList<>();
+
+        if (issueRelations != null) {
+            for (IssueRelation issueRelation : issueRelations) {
+                issueVos.add(ConvertUtil.copyProperties(issueRelation.getRelationIssue(), IssueVo.class));
+            }
+        }
+
+        return issueVos;
+    }
+
+    // �뿰愿� �씪媛� �궘�젣
+    @Override
+    @Transactional
+    public boolean removeRelationIssue(Map<String, Object> resJsonData, IssueRelationCondition condition) {
+        Long id = condition.getId();
+        if (id != null) {
+            IssueRelation issueRelation = findOne(id);
+            if (issueRelation != null) {
+                StringBuilder sb = new StringBuilder();
+                issueHistoryService.detectRelationIssue(IssueHistoryType.DELETE, issueRelation, sb);
+                issueHistoryService.addIssueHistory(issueRelation.getIssue(), IssueHistoryType.MODIFY, sb.toString());
+
+                this.issueRelationRepository.deleteById(id);
+
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueReservationServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueReservationServiceImpl.java
new file mode 100644
index 0000000..52fbc1b
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueReservationServiceImpl.java
@@ -0,0 +1,243 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.ElasticSearchConstants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.IssueReservation;
+import kr.wisestone.owl.domain.enumType.IssueReservationType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.IssueReservationRepository;
+import kr.wisestone.owl.service.IssueReservationService;
+import kr.wisestone.owl.service.IssueService;
+import kr.wisestone.owl.service.WorkspaceService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.ElasticSearchUtil;
+import kr.wisestone.owl.vo.IssueReservationVo;
+import kr.wisestone.owl.web.condition.IssueReservationCondition;
+import kr.wisestone.owl.web.form.IssueReservationForm;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class IssueReservationServiceImpl extends AbstractServiceImpl<IssueReservation, Long, JpaRepository<IssueReservation, Long>> implements IssueReservationService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueReservationServiceImpl.class);
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private IssueService issueService;
+
+    @Autowired
+    private IssueReservationRepository issueReservationRepository;
+
+    @Override
+    protected JpaRepository<IssueReservation, Long> getRepository() {
+        return this.issueReservationRepository;
+    }
+
+    //  �씠�뒋 諛쒖깮 �삁�빟 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailIssueReservation(Map<String, Object> resJsonData, IssueReservationCondition issueReservationCondition) {
+        IssueReservationVo issueReservationVo = new IssueReservationVo();
+
+        if (issueReservationCondition.getIssueId() != null) {
+            Issue issue = this.issueService.getIssue(issueReservationCondition.getIssueId());
+            IssueReservation issueReservation = issue.getIssueReservation();
+
+            if (issueReservation != null) {
+                issueReservationVo = ConvertUtil.copyProperties(issueReservation, IssueReservationVo.class, "issueReservationType");
+
+                if (issueReservation.getIssueReservationType() != null) {
+                    issueReservationVo.setIssueReservationType(issueReservation.getIssueReservationType().toString());
+                }
+            }
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueReservationVo);
+    }
+
+    //  �씠�뒋 諛쒖깮 �씠�젰 �븘�씠�뵒濡� �씠�뒋 諛쒖깮 �씠�젰�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public IssueReservation getIssueReservation(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_NOT_EXIST));
+        }
+
+        IssueReservation issueReservation = this.findOne(id);
+
+        if (issueReservation == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_NOT_EXIST));
+        }
+
+        return issueReservation;
+    }
+
+    //  �씠�뒋 諛쒖깮 �씠�젰 �젙蹂대�� �닔�젙�븳�떎.
+    @Override
+    @Transactional
+    public void modifyIssueReservation(Map<String, Object> resJsonData, IssueReservationForm issueReservationForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        IssueReservation issueReservation;
+
+        //  �닔�젙
+        if (issueReservationForm.getId() != null) {
+            issueReservation = this.getIssueReservation(issueReservationForm.getId());
+            ConvertUtil.copyProperties(issueReservationForm, issueReservation, "id", "issueReservationType");
+
+            if (!StringUtils.isEmpty(issueReservationForm.getIssueReservationType())) {
+                issueReservation.setIssueReservationType(IssueReservationType.valueOf(issueReservationForm.getIssueReservationType()));
+                //  �씠�뒋 諛쒖깮 �삁�빟�씪 泥댄겕
+                this.verifyReservation(issueReservation.getIssueReservationType(), issueReservation.getReservation());
+            }
+            else {
+                issueReservation.setIssueReservationType(null);
+                issueReservation.setReservation(null);
+            }
+        }
+        else {
+            Issue issue = this.issueService.getIssue(issueReservationForm.getIssueId());
+            //  �깮�꽦
+            issueReservation = ConvertUtil.copyProperties(issueReservationForm, IssueReservation.class, "issueReservationType");
+            issueReservation.setIssue(issue);
+            issueReservation.setWorkspace(issue.getProject().getWorkspace());
+
+            if (!StringUtils.isEmpty(issueReservationForm.getIssueReservationType())) {
+                issueReservation.setIssueReservationType(IssueReservationType.valueOf(issueReservationForm.getIssueReservationType()));
+            }
+        }
+
+        this.issueReservationRepository.saveAndFlush(issueReservation);
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, ConvertUtil.copyProperties(issueReservation, IssueReservationVo.class));
+
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_RESERVATION));
+    }
+
+    //  �씠�뒋 諛쒖깮 �삁�빟�씪 泥댄겕
+    private void verifyReservation(IssueReservationType issueReservationType, String reservation) {
+        switch (issueReservationType) {
+            case DAY:
+                if (!StringUtils.isEmpty(reservation)) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                }
+
+                break;
+            case WEEK:
+                if (StringUtils.isEmpty(reservation)) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                }
+
+                boolean pass = false;
+
+                //  �씪 �떒�쐞 �삁�빟
+                switch (reservation) {
+                    case "1":
+                    case "2":
+                    case "3":
+                    case "4":
+                    case "5":
+                    case "6":
+                    case "7":
+                        pass = true;
+                        break;
+                }
+
+                if (!pass) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                }
+
+                break;
+            case MONTH:
+                if (StringUtils.isEmpty(reservation)) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                }
+
+                if (!reservation.matches("^[0-9]*$")) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                }
+
+                if (Integer.parseInt(reservation) > 31) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                }
+
+                break;
+            case YEAR:
+                if (StringUtils.isEmpty(reservation)) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                }
+
+                if (!reservation.matches("^[0-9]+-[0-9]+$")) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                }
+
+                String[] date = reservation.split("-");
+
+                if (date.length < 2) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                }
+
+                if (Integer.parseInt(date[0]) > 12 || Integer.parseInt(date[0]) < 1) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                }
+
+                if (Integer.parseInt(date[1]) > 31 || Integer.parseInt(date[1]) < 1) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                }
+
+                switch (Integer.parseInt(date[0])) {
+                    case 2:
+                        if (Integer.parseInt(date[1]) > 29) {
+                            throw new OwlRuntimeException(
+                                    this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                        }
+                        break;
+                    case 4:
+                    case 6:
+                    case 9:
+                    case 11:
+                        if (Integer.parseInt(date[1]) > 30) {
+                            throw new OwlRuntimeException(
+                                    this.messageAccessor.getMessage(MsgConstants.ISSUE_RESERVATION_VALUE_INVALID));
+                        }
+                        break;
+                }
+
+                break;
+        }
+    }
+
+    //  �씠�뒋 諛쒖깮 �씠�젰 �젙蹂닿� ���옣�릺�뼱 �엳�뒗 �빆紐⑹쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueReservation> findByIssueReservationTypeNotNull() {
+        return this.issueReservationRepository.findByIssueReservationTypeNotNull();
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueRiskServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueRiskServiceImpl.java
new file mode 100644
index 0000000..fcfd3e6
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueRiskServiceImpl.java
@@ -0,0 +1,84 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.IssueRisk;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.repository.IssueRiskRepository;
+import kr.wisestone.owl.service.IssueRiskService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+public class IssueRiskServiceImpl extends AbstractServiceImpl<IssueRisk, Long, JpaRepository<IssueRisk, Long>> implements IssueRiskService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueRiskServiceImpl.class);
+
+    @Autowired
+    private IssueRiskRepository issueRiskRepository;
+
+    @Override
+    protected JpaRepository<IssueRisk, Long> getRepository() {
+        return this.issueRiskRepository;
+    }
+
+    //  �씠�뒋媛� �깮�꽦�맆 �븣 �씠�뒋 �쐞�뿕 愿�由щ룄 媛숈씠 �깮�꽦�맂�떎.
+    @Override
+    @Transactional
+    public IssueRisk addIssueRisk(Issue issue, Workspace workspace) {
+        IssueRisk issueRisk = new IssueRisk();
+        issueRisk.setChangeIssueStatusCount(0L);
+        issueRisk.setChangeAssigneeCount(0L);
+        issueRisk.setIssue(issue);
+        issueRisk.setWorkspace(workspace);
+        issueRisk.setIssueStatusIds(issue.getIssueStatus().getId().toString());
+        return this.issueRiskRepository.saveAndFlush(issueRisk);
+    }
+
+    //  �씠�뒋�뿉�꽌 �떞�떦�옄�굹 �긽�깭媛� 蹂�寃쎈맆 寃쎌슦 �씠�뒋 �쐞�뿕 愿�由� �젙蹂대�� �뾽�뜲�씠�듃�븳�떎.
+    @Override
+    @Transactional
+    public void modifyIssueRisk(Issue issue, Boolean changeIssueStatus, Boolean changeAssignee, Long issueStatusId) {
+        IssueRisk issueRisk = issue.getIssueRisk();
+
+        //  �긽�깭 蹂�寃�
+        if (changeIssueStatus) {
+            String issueStatusIds = issueRisk.getIssueStatusIds();
+            issueStatusIds += "&" + issueStatusId;
+
+            Map<String, Integer> issueStatusIdMaps = new HashMap<>();
+
+            for (String key : issueStatusIds.split("&")) {
+                if (!issueStatusIdMaps.containsKey(key)) {
+                    issueStatusIdMaps.put(key, 1);
+                }
+                else {
+                    issueStatusIdMaps.put(key, issueStatusIdMaps.get(key) + 1);
+                }
+            }
+
+            Integer saveCount = 0;
+
+            for (Integer value : issueStatusIdMaps.values()) {
+                if (value > saveCount) {
+                    saveCount = value;
+                }
+            }
+
+            issueRisk.setChangeIssueStatusCount(saveCount.longValue());
+            issueRisk.setIssueStatusIds(issueStatusIds);
+        }
+
+        //  �떞�떦�옄 蹂�寃�
+        if (changeAssignee) {
+            issueRisk.setChangeAssigneeCount(issueRisk.getChangeAssigneeCount() + 1);
+        }
+
+        this.issueRiskRepository.saveAndFlush(issueRisk);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueSearchServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueSearchServiceImpl.java
new file mode 100644
index 0000000..7fd0bd6
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueSearchServiceImpl.java
@@ -0,0 +1,88 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.IssueSearch;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.repository.IssueSearchRepository;
+import kr.wisestone.owl.service.IssueSearchService;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.service.WorkspaceService;
+import kr.wisestone.owl.util.MapUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Map;
+
+@Service
+public class IssueSearchServiceImpl extends AbstractServiceImpl<IssueSearch, Long, JpaRepository<IssueSearch, Long>> implements IssueSearchService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueSearchServiceImpl.class);
+
+    @Autowired
+    private IssueSearchRepository issueSearchRepository;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    protected JpaRepository<IssueSearch, Long> getRepository() {
+        return this.issueSearchRepository;
+    }
+
+    //  �씠�뒋 寃��깋 議곌굔�쓣 ���옣�븳�떎. - �씠�뒋 踰덊샇留� ���옣�븳�떎.
+    @Override
+    @Transactional
+    public IssueSearch addIssueSearch(Map<String, Object> params) {
+        String conditions = MapUtil.getString(params, "conditions");
+        //  �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 �궗�슜�옄�쓽 �씠�뒋 寃��깋 議곌굔�쓣 議고쉶�븳�떎.
+        IssueSearch saveIssueSearch = this.findByUserIdAndWorkspaceId();
+
+        if (saveIssueSearch == null) {
+            IssueSearch issueSearch = new IssueSearch();
+            Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+            User user = this.userService.getUser(this.webAppUtil.getLoginId());
+            issueSearch.setWorkspace(workspace);
+            issueSearch.setUser(user);
+            issueSearch.setConditions(conditions);
+            return this.issueSearchRepository.saveAndFlush(issueSearch);
+        }
+        else {
+            saveIssueSearch.setConditions(conditions);
+            return this.issueSearchRepository.saveAndFlush(saveIssueSearch);
+        }
+    }
+
+    //  �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 �궗�슜�옄�쓽 �씠�뒋 寃��깋 議곌굔�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public IssueSearch findByUserIdAndWorkspaceId() {
+        return this.issueSearchRepository.findByUserIdAndWorkspaceId(this.webAppUtil.getLoginId(),
+                this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+    }
+
+    //  ���옣�맂 �씠�뒋 寃��깋 議곌굔�쓣 議고쉶 �썑 利됱떆 �궘�젣�븳�떎 - �씠�뒋 踰덊샇留� 寃��깋 議곌굔 ���옣�빐�꽌 �궗�슜
+    @Override
+    @Transactional
+    public void detailIssueSearch(Map<String, Object> resJsonData) {
+        //  �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 �궗�슜�옄�쓽 �씠�뒋 寃��깋 議곌굔�쓣 議고쉶�븳�떎.
+        IssueSearch issueSearch = this.findByUserIdAndWorkspaceId();
+
+        if (issueSearch != null) {
+            resJsonData.put(Constants.RES_KEY_CONTENTS, issueSearch.getConditions());
+            issueSearch.setConditions(null);
+            this.issueSearchRepository.saveAndFlush(issueSearch);
+        }
+        else {
+            resJsonData.put(Constants.RES_KEY_CONTENTS, "");
+        }
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
new file mode 100644
index 0000000..8fec89e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
@@ -0,0 +1,2336 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.common.ExcelConditionCheck;
+import kr.wisestone.owl.config.CommonConfiguration;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.ElasticSearchConstants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.CustomFieldType;
+import kr.wisestone.owl.domain.enumType.EmailType;
+import kr.wisestone.owl.domain.enumType.IssueHistoryType;
+import kr.wisestone.owl.domain.enumType.IssueStatusType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.IssueMapper;
+import kr.wisestone.owl.mapper.ProjectMapper;
+import kr.wisestone.owl.repository.IssueRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.*;
+import kr.wisestone.owl.util.DateUtil;
+import kr.wisestone.owl.vo.*;
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.IssueTypeCustomFieldCondition;
+import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.form.IssueCommentForm;
+import kr.wisestone.owl.web.form.IssueForm;
+import kr.wisestone.owl.web.view.ExcelView;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.StopWatch;
+import org.apache.poi.ss.usermodel.*;
+import org.jsoup.Jsoup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.ui.Model;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.locks.Condition;
+
+@Service
+public class IssueServiceImpl extends AbstractServiceImpl<Issue, Long, JpaRepository<Issue, Long>> implements IssueService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueServiceImpl.class);
+
+    @Autowired
+    private IssueRepository issueRepository;
+
+    @Autowired
+    private ProjectService projectService;
+
+    @Autowired
+    private IssueStatusService issueStatusService;
+
+    @Autowired
+    private IssueTypeService issueTypeService;
+
+    @Autowired
+    private PriorityService priorityService;
+
+    @Autowired
+    private SeverityService severityService;
+
+    @Autowired
+    private CommonConfiguration configuration;
+
+    @Autowired
+    private IssueNumberGeneratorService issueNumberGeneratorService;
+
+    @Autowired
+    private AttachedFileService attachedFileService;
+
+    @Autowired
+    private IssueCustomFieldValueService issueCustomFieldValueService;
+
+    @Autowired
+    private IssueUserService issueUserService;
+
+    @Autowired
+    private CustomFieldService customFieldService;
+
+    @Autowired
+    private IssueTypeCustomFieldService issueTypeCustomFieldService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private IssueCommentService issueCommentService;
+
+    @Autowired
+    private IssueHistoryService issueHistoryService;
+
+    @Autowired
+    private ProjectRoleUserService projectRoleUserService;
+
+    @Autowired
+    private IssueRiskService issueRiskService;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private SystemEmailService systemEmailService;
+
+    @Autowired
+    private IssueVersionService issueVersionService;
+
+    @Autowired
+    private IssueReservationService issueReservationService;
+
+    @Autowired
+    private UserWorkspaceService userWorkspaceService;
+
+    @Autowired
+    private IssueRelationService issueRelationService;
+
+    @Autowired
+    private ExcelView excelView;
+
+    @Autowired
+    private IssueMapper issueMapper;
+
+    @Autowired
+    private ExcelConditionCheck excelConditionCheck;
+
+    @Autowired
+    private SimpMessagingTemplate simpMessagingTemplate;
+
+    @Override
+    protected JpaRepository<Issue, Long> getRepository() {
+        return this.issueRepository;
+    }
+
+    private static final int EXCEL_DOWNLOAD_MAX_ROWS = 10000;   //  excel download �젣�븳
+    private static final int EXCEL_IMPORT_MAX_ROWS = 10000; //  excel import �젣�븳
+
+    @Override
+    @Transactional
+    public void addIssueVersion(Long id) {
+        Issue issue = this.getIssue(id);
+        //  �씠�뒋 踰꾩쟾 �깮�꽦
+        this.issueVersionService.addIssueVersion(issue);
+    }
+
+
+    //  �씠�뒋瑜� �깮�꽦�븳�떎.
+    @Override
+    @Transactional
+    public Issue addIssue(IssueForm issueForm, List<MultipartFile> multipartFiles) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+        //  �봽濡쒖젥�듃 �쑀�슚�꽦 泥댄겕
+        Project project = this.projectService.getProject(issueForm.getProjectId());
+        //  �씠�뒋 �쑀�삎 �쑀�슚�꽦 泥댄겕
+        IssueType issueType = this.issueTypeService.getIssueType(issueForm.getIssueTypeId());
+        //  �슦�꽑�닚�쐞 �쑀�슚�꽦 泥댄겕
+        Priority priority = this.priorityService.getPriority(issueForm.getPriorityId());
+        //  以묒슂�룄 �쑀�슚�꽦 泥댄겕
+        Severity severity = this.severityService.getSeverity(issueForm.getSeverityId());
+
+        //  �젣紐� �쑀�슚�꽦 泥댄겕
+        this.verifyTitle(issueForm.getTitle());
+        //  �궇吏� �쑀�슚�꽦 泥댄겕
+        this.checkStartCompleteDate(issueForm.getStartDate(), issueForm.getCompleteDate());
+
+        //  �씠�뒋 �긽�깭 �쑀�삎�씠 '��湲�' �씤 �씠�뒋 �긽�깭 媛��졇�삤湲�
+        IssueStatus issueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(issueType.getWorkflow());
+
+        Issue issue = ConvertUtil.copyProperties(issueForm, Issue.class);
+        issue.setProject(project);
+        issue.setIssueStatus(issueStatus);
+        issue.setIssueType(issueType);
+        issue.setPriority(priority);
+        issue.setSeverity(severity);
+        issue.setIssueNumber(this.issueNumberGeneratorService.generateIssueNumber(project));    //  媛� �봽濡쒖젥�듃�쓽 怨좎쑀 �씠�뒋 踰덊샇 �깮�꽦
+
+        this.issueRepository.saveAndFlush(issue);
+
+        issue.setReverseIndex(issue.getId() * -1);  //  荑쇰━ �냽�룄 媛쒖꽑�쓣 �쐞�빐 由щ쾭�뒪 �씤�뜳�뒪 �깮�꽦
+        //  �떞�떦�옄 吏��젙
+        this.issueUserService.modifyIssueUser(issue, project.getWorkspace(), issueForm.getUserIds());
+
+        //  multipartFile �쓣 file Map List 媛앹껜濡� 蹂�寃쏀븳�떎.
+        List<Map<String, Object>> convertFileMaps = this.convertMultipartFileToFile(multipartFiles);
+        //  泥⑤� �뙆�씪 ���옣
+        this.attachedFileService.addAttachedFile(convertFileMaps, issue, this.webAppUtil.getLoginUser().getAccount());
+        //  �뀓�뒪�듃 �뿉�뵒�꽣�뿉 泥⑤��븳 �뙆�씪�쓣 �씠�뒋�� �뿰寃�
+        this.checkNotHaveIssueIdAttachedFile(issue, issueForm);
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 ���옣
+        this.issueCustomFieldValueService.modifyIssueCustomFieldValue(issue, issueForm.getIssueCustomFields());
+        //  �씠�뒋 �씠�젰 �깮�꽦
+        this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.ADD, null);
+        //  �씠�뒋 �쐞�뿕 愿�由� �깮�꽦
+        this.issueRiskService.addIssueRisk(issue, project.getWorkspace());
+        //  �쁺�냽�꽦 而⑦뀓�뒪�듃 鍮꾩슦湲�
+        this.clear();
+        //  �씠�뒋 �깮�꽦, �궘�젣�떆 �삁�빟 �씠硫붿씪�뿉 �벑濡앺빐�넃�뒗�떎.
+        this.reservationIssueEmail(issue.getId(), EmailType.ISSUE_ADD);
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_ADD));
+
+        return issue;
+    }
+
+    //  �씠�뒋 �깮�꽦, �궘�젣�떆 �삁�빟 �씠硫붿씪�뿉 �벑濡앺빐�넃�뒗�떎.
+    private void reservationIssueEmail(Long id, EmailType emailType) {
+        Issue issue = this.getIssue(id);
+
+        Map<String, Object> issueMap = new HashMap<>();
+        //  �씠�뒋 �젙蹂대�� �씠硫붿씪 �쟾�넚�뿉 �궗�슜�븯湲� �쐞�빐 Map �삎�깭濡� 蹂��솚�븳�떎.
+        this.makeIssueMapToIssue(issue, issueMap);
+
+        Map<String, Object> projectRoleUserMap = new HashMap<>();
+        projectRoleUserMap.put("id", issue.getProject().getId());
+        projectRoleUserMap.put("statuses", Lists.newArrayList("02"));   //  愿�由ъ옄 議고쉶
+        //  愿�由ъ옄 �젙蹂� �뀑�똿
+        List<Map<String, Object>> projectRoleUsers = this.projectRoleUserService.findProjectRoleUser(projectRoleUserMap);
+
+        if (projectRoleUsers != null && !projectRoleUsers.isEmpty()) {
+            for (Map<String, Object> projectRoleUser : projectRoleUsers) {
+                UserVo userVo = ConvertUtil.convertMapToClass(projectRoleUser, UserVo.class);
+
+                //  �씠�뒋 �깮�꽦 �븣由� 硫붿씪 �쟾�넚
+                this.systemEmailService.reservationEmail(new String[]{userVo.getAccount()}, emailType, issueMap);
+            }
+        }
+    }
+
+    //  �씠�뒋 �젙蹂대�� �씠硫붿씪 �쟾�넚�뿉 �궗�슜�븯湲� �쐞�빐 Map �삎�깭濡� 蹂��솚�븳�떎.
+    private void makeIssueMapToIssue(Issue issue, Map<String, Object> issueMap) {
+        issueMap.put("title", issue.getTitle());
+        issueMap.put("issueNumber", issue.getIssueNumber());
+        issueMap.put("issueTypeName", issue.getIssueType().getName());
+        issueMap.put("issueStatusName", issue.getIssueStatus().getName());
+
+        //  �떞�떦�옄
+        StringBuilder assigneeBuilder = new StringBuilder();
+        for (IssueUser issueUser : issue.getIssueUsers()) {
+            assigneeBuilder.append(issueUser.getUser().getName());
+            assigneeBuilder.append("(");
+            assigneeBuilder.append(CommonUtil.decryptAES128(issueUser.getUser().getAccount()));
+            assigneeBuilder.append(")");
+            assigneeBuilder.append("\n");
+        }
+
+        issueMap.put("assignees", assigneeBuilder.toString());
+        //  湲곌컙
+        if (!StringUtils.isEmpty(issue.getStartDate())) {
+            issueMap.put("period", issue.getStartDate() + " ~ " + issue.getCompleteDate());
+        }
+
+        issueMap.put("severityName", issue.getSeverity().getName());
+        issueMap.put("priorityName", issue.getPriority().getName());
+        issueMap.put("projectName", issue.getProject().getName());
+        issueMap.put("projectKey", issue.getProject().getProjectKey());
+
+        User user = this.userService.getUser(issue.getRegisterId());
+        StringBuilder registerBuilder = new StringBuilder();
+        registerBuilder.append(user.getName());
+        registerBuilder.append("(");
+        registerBuilder.append(CommonUtil.decryptAES128(user.getAccount()));
+        registerBuilder.append(")");
+        issueMap.put("register", registerBuilder.toString());
+
+        Map<String, Object> customField = new HashMap<>();
+
+        List<IssueCustomFieldValueVo> issueCustomFieldValueVos = this.issueCustomFieldValueService.findByIssueId(issue.getId());
+
+        for (IssueCustomFieldValueVo issueCustomFieldValueVo : issueCustomFieldValueVos) {
+            //  �씠誘� �뜲�씠�꽣媛� 議댁옱
+            if (customField.get(issueCustomFieldValueVo.getCustomFieldVo().getName()) != null) {
+                List<String> useValues = (List<String>) customField.get(issueCustomFieldValueVo.getCustomFieldVo().getName());
+                useValues.add(issueCustomFieldValueVo.getUseValue());
+                customField.put(issueCustomFieldValueVo.getCustomFieldVo().getName(), useValues);
+            } else {
+                if (issueCustomFieldValueVo.getCustomFieldVo().getCustomFieldType().equals(CustomFieldType.INPUT.toString())) {
+                    customField.put(issueCustomFieldValueVo.getCustomFieldVo().getName(), issueCustomFieldValueVo.getUseValue());
+                } else {
+                    customField.put(issueCustomFieldValueVo.getCustomFieldVo().getName(), Lists.newArrayList(issueCustomFieldValueVo.getUseValue()));
+                }
+            }
+        }
+
+        List<Map<String, Object>> customFields = Lists.newArrayList();
+
+        Iterator<String> iterator = customField.keySet().iterator();
+        while (iterator.hasNext()) {
+            String key = iterator.next();
+            Map<String, Object> result = new HashMap<>();
+            result.put("name", key);
+            result.put("useValue", customField.get(key));
+            customFields.add(result);
+        }
+
+        issueMap.put("customFields", customFields);
+        issueMap.put("description", issue.getDescription());
+
+        StringBuilder attachedFileBuilder = new StringBuilder();
+
+        List<AttachedFile> attachedFiles = this.attachedFileService.findByIssueId(issue.getId());
+
+        for (AttachedFile attachedFile : attachedFiles) {
+            attachedFileBuilder.append("<a href='");
+            attachedFileBuilder.append(attachedFile.getPath());
+            attachedFileBuilder.append("'>");
+            attachedFileBuilder.append(attachedFile.getName());
+            attachedFileBuilder.append("</a>");
+            attachedFileBuilder.append("\n");
+        }
+
+        issueMap.put("attachedFiles", attachedFileBuilder.toString());
+    }
+
+    //  �젣紐� �쑀�슚�꽦 泥댄겕
+    private void verifyTitle(String title) {
+        if (StringUtils.isEmpty(title)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_NO_TITLE));
+        }
+
+        if (title.length() > 300) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_TITLE_MAX_LENGTH_OUT));
+        }
+    }
+
+    //  �궇吏� �쑀�슚�꽦 泥댄겕
+    private void checkStartCompleteDate(String startDate, String completeDate) {
+        if (!StringUtils.isEmpty(startDate) && !StringUtils.isEmpty(completeDate)) {
+            Date start = DateUtil.convertStrToDate(startDate, "yy-MM-dd");
+            Date end = DateUtil.convertStrToDate(completeDate, "yy-MM-dd");
+            if (start.getTime() > end.getTime()) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.DATE_PICKER_NOT_AVAILABLE));
+            }
+        }
+    }
+
+    //  �뀓�뒪�듃 �뿉�뵒�꽣�뿉 泥⑤��븳 �뙆�씪�쓣 �씠�뒋�� �뿰寃�
+    private void checkNotHaveIssueIdAttachedFile(Issue issue, IssueForm issueForm) {
+        if (!issueForm.getAttachedFileIds().isEmpty()) {
+            this.attachedFileService.connectIssueIdAttachedFile(issue, issueForm);
+        }
+    }
+
+    //  �씠�뒋 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueVo> findIssue(Map<String, Object> resJsonData,
+                                   IssueCondition issueCondition, Pageable pageable) {
+
+        //  寃��깋 議곌굔�쓣 留뚮뱺�떎
+        if (!this.makeIssueSearchCondition(issueCondition, Lists.newArrayList("01", "02", "03"), pageable)) {
+            //  �씠�뒋 紐⑸줉�쓣 李얠� 紐삵븷 寃쎌슦 湲곕낯 �젙蹂대줈 由ы꽩�븳�떎.
+            this.notFoundIssueList(resJsonData, pageable);
+            return Lists.newArrayList();
+        }
+
+        Set<String> issueIds = new HashSet<>(); //  �궗�슜�옄 �젙�쓽 �븘�뱶 寃��깋�떆 �굹�삤�뒗 �씠�뒋 �븘�씠�뵒 ���옣 而щ젆�뀡
+
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �궗�슜�븳 �씠�뒋瑜� 李얜뒗�떎. 留뚯빟 �씠�뒋媛� �뾾�떎硫� �뿬湲곗꽌 �씠�뒋 議고쉶媛� �걹�궃�떎.
+        if (!this.searchUseCustomFields(issueCondition, issueIds, resJsonData, pageable)) {
+            //  �씠�뒋 紐⑸줉�쓣 李얠� 紐삵븷 寃쎌슦 湲곕낯 �젙蹂대줈 由ы꽩�븳�떎.
+            this.notFoundIssueList(resJsonData, pageable);
+            return Lists.newArrayList();
+        }
+
+        //  �뒠�떇 �쟾 - 1.3 / 1.2 / 1.1
+        //  �뒠�떇 �썑 (�떒�씪/�떎以� 寃��깋 議곌굔 3媛� 湲곗�) - 0.49 / 0.41 / 0.47 / 0.41
+
+        List<IssueVo> issueVos = Lists.newArrayList();  //  �씠�뒋 紐⑸줉 �뜲�씠�꽣 ���옣 而щ젆�뀡
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶濡� 寃��깋�븳 �씠�뒋 �븘�씠�뵒 媛�
+        List<String> issueKeys = Lists.newArrayList(issueIds);
+        issueCondition.setIssueIds(issueKeys);
+
+        List<Map<String, Object>> results = this.issueMapper.find(issueCondition);
+        //  �뒠�떇 �쟾 - 0.8, 0.9, 0.9, 0.9, 0.9
+        StopWatch serviceStart = new StopWatch();
+        serviceStart.start();
+        Long totalCount = this.issueMapper.count(issueCondition);
+        //  �뒠�떇 �쟾 - 1.1, 1.1, 1.3, 1.2
+
+        serviceStart.stop();
+        log.debug("serviceENd1 : " + serviceStart.getTime());
+
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        //  �씠�뒋 �븘�씠�뵒 珥덇린�솕
+        issueCondition.setIssueIds(Lists.newArrayList());
+        //  Map �뿉 �엳�뒗 �뜲�씠�꽣瑜� IssueVo �뜲�씠�꽣濡� 蹂��솚�븳�떎.
+        this.setMapToIssueVo(results, issueVos, issueCondition);
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_FIND));
+        return issueVos;
+    }
+
+    //  �씠�뒋 紐⑸줉�쓣 議고쉶�븳�떎(李⑦듃�슜 - �뿰愿��씪媛먰룷�븿)
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueVo> findChartIssue(Map<String, Object> resJsonData,
+                                   IssueCondition issueCondition, Pageable pageable) {
+
+        //  寃��깋 議곌굔�쓣 留뚮뱺�떎
+        if (!this.makeIssueSearchCondition(issueCondition,Lists.newArrayList("01", "02", "03"), pageable)) {
+            //  �씠�뒋 紐⑸줉�쓣 李얠� 紐삵븷 寃쎌슦 湲곕낯 �젙蹂대줈 由ы꽩�븳�떎.
+            this.notFoundIssueList(resJsonData, pageable);
+            return Lists.newArrayList();
+        }
+
+        Set<String> issueIds = new HashSet<>(); //  �궗�슜�옄 �젙�쓽 �븘�뱶 寃��깋�떆 �굹�삤�뒗 �씠�뒋 �븘�씠�뵒 ���옣 而щ젆�뀡
+
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �궗�슜�븳 �씠�뒋瑜� 李얜뒗�떎. 留뚯빟 �씠�뒋媛� �뾾�떎硫� �뿬湲곗꽌 �씠�뒋 議고쉶媛� �걹�궃�떎.
+        if (!this.searchUseCustomFields(issueCondition, issueIds, resJsonData, pageable)) {
+            //  �씠�뒋 紐⑸줉�쓣 李얠� 紐삵븷 寃쎌슦 湲곕낯 �젙蹂대줈 由ы꽩�븳�떎.
+            this.notFoundIssueList(resJsonData, pageable);
+            return Lists.newArrayList();
+        }
+
+        //  �뒠�떇 �쟾 - 1.3 / 1.2 / 1.1
+        //  �뒠�떇 �썑 (�떒�씪/�떎以� 寃��깋 議곌굔 3媛� 湲곗�) - 0.49 / 0.41 / 0.47 / 0.41
+        List<IssueVo> issueVos = Lists.newArrayList();  //  �씠�뒋 紐⑸줉 �뜲�씠�꽣 ���옣 而щ젆�뀡
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶濡� 寃��깋�븳 �씠�뒋 �븘�씠�뵒 媛�
+        List<String> issueKeys = Lists.newArrayList(issueIds);
+        issueCondition.setIssueIds(issueKeys);
+
+        List<Map<String, Object>> results = this.issueMapper.find(issueCondition);
+        int totalCount = results.size();
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+
+        //  �씠�뒋 �븘�씠�뵒 珥덇린�솕
+        issueCondition.setIssueIds(Lists.newArrayList());
+        //  Map �뿉 �엳�뒗 �뜲�씠�꽣瑜� IssueVo �뜲�씠�꽣濡� 蹂��솚�븳�떎.
+        this.setMapToIssueVoForChart(results, issueVos, issueCondition);
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_FIND));
+        return issueVos;
+    }
+
+    //  �씠�뒋 紐⑸줉�쓣 議고쉶�븳�떎(李⑦듃�슜 - �뿰愿��씪媛�)
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueVo> findChartIssue(Map<String, Object> resJsonData,
+                                        ProjectCondition projectCondition, Pageable pageable) {
+
+        IssueCondition issueCondition = new IssueCondition();
+        //  寃��깋 議곌굔�쓣 留뚮뱺�떎
+        if (!this.makeIssueSearchCondition(issueCondition, projectCondition, pageable)) {
+            //  �씠�뒋 紐⑸줉�쓣 李얠� 紐삵븷 寃쎌슦 湲곕낯 �젙蹂대줈 由ы꽩�븳�떎.
+            this.notFoundIssueList(resJsonData, pageable);
+            return Lists.newArrayList();
+        }
+
+        Set<String> issueIds = new HashSet<>(); //  �궗�슜�옄 �젙�쓽 �븘�뱶 寃��깋�떆 �굹�삤�뒗 �씠�뒋 �븘�씠�뵒 ���옣 而щ젆�뀡
+        List<IssueVo> issueVos = Lists.newArrayList();  //  �씠�뒋 紐⑸줉 �뜲�씠�꽣 ���옣 而щ젆�뀡
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶濡� 寃��깋�븳 �씠�뒋 �븘�씠�뵒 媛�
+        List<String> issueKeys = Lists.newArrayList(issueIds);
+        issueCondition.setIssueIds(issueKeys);
+
+        List<Map<String, Object>> results = this.issueMapper.find(issueCondition);
+        int totalCount = results.size();
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+
+        //  �씠�뒋 �븘�씠�뵒 珥덇린�솕
+        issueCondition.setIssueIds(Lists.newArrayList());
+        //  Map �뿉 �엳�뒗 �뜲�씠�꽣瑜� IssueVo �뜲�씠�꽣濡� 蹂��솚�븳�떎.
+        this.setMapToIssueVoForChart(results, issueVos, issueCondition);
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_FIND));
+        return issueVos;
+    }
+
+    // Map �뿉 �엳�뒗 �뜲�씠�꽣瑜� IssueVo �뜲�씠�꽣濡� 蹂��솚�븳�떎. 李⑦듃�슜
+    private void setMapToIssueVoForChart(List<Map<String, Object>> results, List<IssueVo> issueVos, IssueCondition issueCondition) {
+        for (Map<String, Object> result : results) {
+            IssueVo issueVo = ConvertUtil.convertMapToClass(result, IssueVo.class);
+            issueVos.add(issueVo);
+            issueCondition.addIssueIds(String.valueOf(issueVo.getId()));
+        }
+
+        for (IssueVo issueVo : issueVos) {
+            this.setRelationIssue(issueVo, issueVo.getId());
+        }
+    }
+
+    //  Map �뿉 �엳�뒗 �뜲�씠�꽣瑜� IssueVo �뜲�씠�꽣濡� 蹂��솚�븳�떎.
+    private void setMapToIssueVo(List<Map<String, Object>> results, List<IssueVo> issueVos, IssueCondition issueCondition) {
+        for (Map<String, Object> result : results) {
+            IssueVo issueVo = ConvertUtil.convertMapToClass(result, IssueVo.class);
+            issueVos.add(issueVo);
+            issueCondition.addIssueIds(String.valueOf(issueVo.getId()));
+        }
+
+        //  �씠�뒋 �궗�슜�옄 �젙蹂� 異붽�
+        this.setIssueUserList(issueVos, issueCondition);
+        //  �벑濡앹옄 �젙蹂� 異붽�
+        this.setRegister(issueVos);  //  �떞�떦�옄 �젙蹂� �뀑�똿
+
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂� 異붽�
+        this.setIssueCustomFieldValue(issueVos, issueCondition);
+    }
+
+        //  寃��깋 議곌굔�쓣 留뚮뱺�떎
+    private boolean makeIssueSearchCondition(IssueCondition condition, List<String> projectStatues, Pageable pageable) {
+        if (pageable != null) {
+            condition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+            condition.setPageSize(pageable.getPageSize());
+        }
+
+        condition.setLoginUserId(this.webAppUtil.getLoginId());
+        condition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+
+        //  �봽濡쒖젥�듃 �궎媛� 議댁옱�븷 寃쎌슦 �봽濡쒖젥�듃 �궎�뿉 �빐�떦�븯�뒗 �봽濡쒖젥�듃瑜� 議고쉶�븯怨� 寃��깋 議곌굔�뿉 �뀑�똿�븳�떎.
+        if (!this.getProjectByProjectKey(condition.getProjectKey(), condition)) {
+            return false;
+        }
+
+        //  �봽濡쒖젥�듃瑜� �꽑�깮�븯吏� �븡�븯�쑝硫� �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 李몄뿬�븯怨� �엳�뒗 �봽濡쒖젥�듃瑜� 李얜뒗�떎.
+        if (condition.getProjectIds().size() < 1) {
+            List<Map<String, Object>> projects = this.projectService.findByWorkspaceIdAndIncludeProjectAll(projectStatues, condition.getProjectType());
+            List<Long> projectIds = Lists.newArrayList();
+
+            for (Map<String, Object> result : projects) {
+                Long projectId = MapUtil.getLong(result, "id");
+
+                if (projectId != null) {
+                    projectIds.add(projectId);
+                }
+            }
+
+            condition.setProjectIds(projectIds);
+
+            if (projectIds.size() < 1) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    //  寃��깋 議곌굔�쓣 留뚮뱺�떎
+    private boolean makeIssueSearchCondition(IssueCondition condition, ProjectCondition projectCondition, Pageable pageable) {
+        if (pageable != null) {
+            condition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+            condition.setPageSize(pageable.getPageSize());
+        }
+
+        condition.setLoginUserId(this.webAppUtil.getLoginId());
+        condition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        projectCondition.setWorkspaceId(condition.getWorkspaceId());
+
+        //  �봽濡쒖젥�듃 �궎媛� 議댁옱�븷 寃쎌슦 �봽濡쒖젥�듃 �궎�뿉 �빐�떦�븯�뒗 �봽濡쒖젥�듃瑜� 議고쉶�븯怨� 寃��깋 議곌굔�뿉 �뀑�똿�븳�떎.
+        if (!this.getProjectByProjectKey(condition.getProjectKey(), condition)) {
+            return false;
+        }
+
+        //  �봽濡쒖젥�듃瑜� �꽑�깮�븯吏� �븡�븯�쑝硫� �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 李몄뿬�븯怨� �엳�뒗 �봽濡쒖젥�듃瑜� 李얜뒗�떎.
+        if (condition.getProjectIds().size() < 1) {
+            List<Map<String, Object>> projects = null;
+            if (this.userWorkspaceService.checkWorkspaceManager()) {
+                projects = this.projectMapper.findByWorkspaceManagerAll(projectCondition);
+            } else  {
+                projects = this.projectService.findByWorkspaceIdAndIncludeProjectAll(projectCondition);
+            }
+            List<Long> projectIds = Lists.newArrayList();
+
+            for (Map<String, Object> result : projects) {
+                Long projectId = MapUtil.getLong(result, "id");
+
+                if (projectId != null) {
+                    projectIds.add(projectId);
+                }
+            }
+
+            condition.setProjectIds(projectIds);
+
+            if (projectIds.size() < 1) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    //  �봽濡쒖젥�듃 �궎媛� 議댁옱�븷 寃쎌슦 �봽濡쒖젥�듃 �궎�뿉 �빐�떦�븯�뒗 �봽濡쒖젥�듃瑜� 議고쉶�븯怨� 寃��깋 議곌굔�뿉 �뀑�똿�븳�떎.
+    private boolean getProjectByProjectKey(String projectKey, IssueCondition condition) {
+        if (!StringUtils.isEmpty(projectKey)) {
+            Project project = this.projectService.findByProjectKey(projectKey);
+
+            if (project != null) {
+                //  �씠誘� �봽濡쒖젥�듃瑜� �꽑�깮�뻽�쓣 寃쎌슦�뿉 �봽濡쒖젥�듃�궎濡� 寃��깋�븳 �봽濡쒖젥�듃媛� �룷�븿�릺�뼱 �엳吏� �븡�쑝硫� false
+                if (condition.getProjectIds().size() > 0) {
+                    if (condition.getProjectIds().contains(project.getId())) {
+                        condition.setProjectIds(Lists.newArrayList());
+                    } else {
+                        return false;
+                    }
+                }
+
+                condition.addProjectIds(project.getId());
+            } else {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    //  �씠�뒋 紐⑸줉�쓣 李얠� 紐삵븷 寃쎌슦 湲곕낯 �젙蹂대줈 由ы꽩�븳�떎.
+    private void notFoundIssueList(Map<String, Object> resJsonData, Pageable pageable) {
+        resJsonData.put(Constants.RES_KEY_CONTENTS, Lists.newArrayList());
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                0, 0));
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �궗�슜�븳 �씠�뒋瑜� 李얜뒗�떎. 留뚯빟 �씠�뒋媛� �뾾�떎硫� �뿬湲곗꽌 �씠�뒋 議고쉶媛� �걹�궃�떎.
+    private boolean searchUseCustomFields(IssueCondition condition, Set<String> issueIds, Map<String, Object> resJsonData, Pageable pageable) {
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪씠 寃��깋 �샃�뀡�쑝濡� �엳�뿀�쓣 �븣 議고쉶�맂 �씠�뒋媛� �뾾�쑝硫� 議고쉶瑜� �뜑 �씠�긽 �븷 �븘�슂媛� �뾾�떎.
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �궗�슜�븳 �씠�뒋瑜� 李얜뒗�떎.
+        boolean customFieldSearch = this.issueCustomFieldValueService.find(condition, issueIds);
+
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪씠 議댁옱�븯�뿬 寃��깋�쓣 �뻽�쓣 �븣 寃��깋�맂 �씠�뒋媛� �뾾�쑝硫� �뿬湲곗꽌 醫낅즺�븳�떎.
+        if (customFieldSearch && issueIds.size() < 1) {
+            resJsonData.put(Constants.RES_KEY_CONTENTS, Lists.newArrayList());
+
+            if (pageable != null) {
+                resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                        0, 0));
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    //  �씠�뒋 �떞�떦�옄 �젙蹂대�� �뀑�똿�븳�떎.
+    private void setIssueUserList(List<IssueVo> issueVos, IssueCondition issueCondition) {
+        if (issueVos.size() < 1) {
+            return;
+        }
+
+        List<Map<String, Object>> issueUsers = this.issueMapper.findIssueUser(issueCondition);
+        Map<String, Object> issueConverterUsers = new HashMap<>();
+
+        //  �씠�뒋�뿉 �빐�떦�븯�뒗 �씠�뒋 �떞�떦�옄 �젙蹂� �뀑�똿
+        for (Map<String, Object> issueUser : issueUsers) {
+            String issueId = MapUtil.getString(issueUser, "issueId");
+
+            if (MapUtil.getObject(issueConverterUsers, issueId) != null) {
+                List<UserVo> users = (List) MapUtil.getObject(issueConverterUsers, issueId);
+                users.add(new UserVo(MapUtil.getLong(issueUser, "id"), MapUtil.getString(issueUser, "name"), CommonUtil.decryptAES128(MapUtil.getString(issueUser, "account")), MapUtil.getString(issueUser, "profile")));
+            } else {
+                List<UserVo> users = Lists.newArrayList(new UserVo(MapUtil.getLong(issueUser, "id"), MapUtil.getString(issueUser, "name"), CommonUtil.decryptAES128(MapUtil.getString(issueUser, "account")),
+                        MapUtil.getString(issueUser, "profile")));
+                issueConverterUsers.put(issueId, users);
+            }
+        }
+
+        //  �씠�뒋Vo�뿉 �떞�떦�옄 �젙蹂대�� �뀑�똿
+        for (IssueVo issueVo : issueVos) {
+            if (MapUtil.getObject(issueConverterUsers, String.valueOf(issueVo.getId())) != null) {
+                List<UserVo> userVos = (List) MapUtil.getObject(issueConverterUsers, String.valueOf(issueVo.getId()));
+
+                issueVo.setUserVos(userVos);
+            }
+
+            //  �씠�뒋 �닔�젙 沅뚰븳�쓣 媛뽮퀬 �엳�뒗吏� �솗�씤
+            if (this.checkHasPermission(issueVo, issueVo.getUserVos())) {
+                issueVo.setModifyPermissionCheck(Boolean.TRUE);
+            }
+        }
+    }
+
+    //  �씠�뒋 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailIssue(Map<String, Object> resJsonData, IssueCondition issueCondition) {
+        IssueVo issueVo = new IssueVo();
+
+        if (issueCondition.getId() != null) {
+            Issue issue = this.getIssue(issueCondition.getId());
+            issueVo = ConvertUtil.copyProperties(issue, IssueVo.class);
+
+            switch (issueCondition.getDeep()) {
+                case "01": //  �봽濡쒖젥�듃, �씠�뒋 �쑀�삎, �씠�뒋 �긽�깭,  �슦�꽑�닚�쐞, 以묒슂�룄, �떞�떦�옄, 泥⑤��뙆�씪, �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂대�� �뀑�똿�븳�떎.
+                    issueVo.setProjectVo(ConvertUtil.copyProperties(issue.getProject(), ProjectVo.class));
+                    issueVo.setIssueTypeVo(ConvertUtil.copyProperties(issue.getIssueType(), IssueTypeVo.class));
+                    issueVo.setIssueStatusVo(ConvertUtil.copyProperties(issue.getIssueStatus(), IssueStatusVo.class));
+                    issueVo.setPriorityVo(ConvertUtil.copyProperties(issue.getPriority(), PriorityVo.class));
+                    issueVo.setSeverityVo(ConvertUtil.copyProperties(issue.getSeverity(), SeverityVo.class));
+
+                    this.setRegister(issue, issueVo);   //  �벑濡앹옄 �젙蹂� �뀑�똿
+                    this.setIssueUser(issue, issueVo);  //  �떞�떦�옄 �젙蹂� �뀑�똿
+                    this.setAttachedFiles(issue, issueVo);  //  泥⑤� �뙆�씪 �젙蹂� �뀑�똿
+                    this.setIssueCustomFields(issue, issueVo);  //  �궗�슜�옄 �젙�쓽 �븘�뱶 媛� �젙蹂� �뀑�똿
+                    this.setRelationIssue(issue, issueVo);        //�뿰愿� �씪媛� �뀑�똿
+                    break;
+
+                case "02": //  �봽濡쒖젥�듃, �씠�뒋 �쑀�삎, �씠�뒋 �긽�깭,  �슦�꽑�닚�쐞, 以묒슂�룄, �떞�떦�옄, 泥⑤��뙆�씪, �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂�, �뙎湲�, 湲곕줉�쓣 �뀑�똿�븳�떎.
+                    this.setIssueDetail(issueVo, issue);    //  �씠�뒋 �긽�꽭 �젙蹂대�� �뀑�똿�븳�떎.
+                    break;
+            }
+        }
+
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_DETAIL));
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueVo);
+    }
+
+    //  �씠�뒋 �긽�꽭 �젙蹂대�� �뀑�똿�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void setIssueDetail(IssueVo issueVo, Issue issue) {
+        issueVo.setProjectVo(ConvertUtil.copyProperties(issue.getProject(), ProjectVo.class));
+        issueVo.setIssueTypeVo(ConvertUtil.copyProperties(issue.getIssueType(), IssueTypeVo.class));
+        IssueStatusVo issueStatusVo = ConvertUtil.copyProperties(issue.getIssueStatus(), IssueStatusVo.class, "issueStatusType");
+        issueStatusVo.setIssueStatusType(issue.getIssueStatus().getIssueStatusType().toString());
+        issueVo.setIssueStatusVo(issueStatusVo);
+        issueVo.setPriorityVo(ConvertUtil.copyProperties(issue.getPriority(), PriorityVo.class));
+        issueVo.setSeverityVo(ConvertUtil.copyProperties(issue.getSeverity(), SeverityVo.class));
+        this.setRegister(issue, issueVo);   //  �벑濡앹옄 �젙蹂� �뀑�똿
+        this.setIssueUser(issue, issueVo);  //  �떞�떦�옄 �젙蹂� �뀑�똿
+        this.setAttachedFiles(issue, issueVo);  //  泥⑤� �뙆�씪 �젙蹂� �뀑�똿
+        this.setIssueCustomFields(issue, issueVo);  //  �궗�슜�옄 �젙�쓽 �븘�뱶 媛� �젙蹂� �뀑�똿
+        this.setIssueComments(issue, issueVo);  //  �뙎湲� �젙蹂� �뀑�똿
+        this.setIssueHistory(issue, issueVo);   //  �씠�뒋 湲곕줉 �젙蹂� �뀑�똿
+        this.setRelationIssue(issue, issueVo);        //�뿰愿� �씪媛� �뀑�똿
+    }
+
+    //  �벑濡앹옄 �젙蹂� 異붽�
+    private void setRegister(List<IssueVo> issueVos) {
+        for (IssueVo issueVo : issueVos) {
+            //  �궗�슜�옄 �뙣�뒪�썙�뱶瑜� �궘�젣�븯怨� �궗�슜�옄 怨꾩젙�쓣 蹂듯샇�솕�븳�떎.
+            issueVo.setRegisterVo(this.userService.removeSensitiveUser(issueVo.getRegisterId()));
+        }
+    }
+
+    //  �씠�뒋 �벑濡앹옄 �젙蹂대�� �뀑�똿�븳�떎.
+    private void setRegister(Issue issue, IssueVo issueVo) {
+        UserVo userVo = this.userService.removeSensitiveUser(issue.getRegisterId());
+        issueVo.setRegisterVo(userVo);
+
+        //  �벑濡앹옄�뒗 �빆�긽 �닔�젙 媛��뒫.
+        if (userVo.getId().equals(this.webAppUtil.getLoginId())) {
+            issueVo.setModifyPermissionCheck(Boolean.TRUE);
+        }
+    }
+
+    // �뿰愿� �씠�뒋 �젙蹂대�� �뀑�똿�븳�떎
+    private void setRelationIssue(Issue issue, IssueVo issueVo) {
+        Set<IssueRelation> issueRelations = issue.getIssueRelations();
+        if (issue != null && issueVo != null && issueRelations.size() > 0) {
+            for (IssueRelation issueRelation : issueRelations) {
+                IssueRelationVo issueRelationVo = ConvertUtil.copyProperties(issueRelation, IssueRelationVo.class);
+
+                Issue relationIssue = issueRelation.getRelationIssue();
+
+                IssueVo relIssueVo = ConvertUtil.copyProperties(relationIssue, IssueVo.class);
+                Project project = this.projectService.getProject(relationIssue.getProject().getId());
+                relIssueVo.setProjectId(project.getId());
+                relIssueVo.setProjectKey(project.getProjectKey());
+                relIssueVo.setIssueNumber(relationIssue.getIssueNumber());
+
+                issueRelationVo.setIssueRelation(relIssueVo);
+                issueRelationVo.setTitle(relationIssue.getTitle());
+                issueVo.addIssueRelationVo(issueRelationVo);
+            }
+        } else {
+            issue.clearIssueRelations();
+        }
+    }
+
+    //  �씠�뒋 �떞�떦�옄 �젙蹂대�� �뀑�똿�븳�떎.
+    private void setIssueUser(Issue issue, IssueVo issueVo) {
+        List<UserVo> userVos = Lists.newArrayList();
+
+        for (IssueUser issueUser : issue.getIssueUsers()) {
+            UserVo userVo = ConvertUtil.copyProperties(issueUser.getUser(), UserVo.class, "password");
+            userVo.setByName(userVo.getName() + "(" + CommonUtil.decryptAES128(userVo.getAccount()) + ")");
+            userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount()));
+            userVos.add(userVo);
+            //  �떞�떦�옄媛� �엳�쓣 寃쎌슦 �떞�떦�옄留� �닔�젙 媛��뒫.
+            if (userVo.getId().equals(this.webAppUtil.getLoginId())) {
+                issueVo.setModifyPermissionCheck(Boolean.TRUE);
+            }
+        }
+
+        //  �뒪耳�伊대윭�뿉�꽌 �떎�뻾�맆 寃쎌슦 �삤瑜� 諛쒖깮�븯誘�濡� 沅뚰븳 泥댄겕�븯吏� �븡�뒗�떎.
+        if (this.webAppUtil.getLoginId() != null) {
+            //  �뾽臾� 怨듦컙 愿�由ъ옄�씪 寃쎌슦 �닔�젙 沅뚰븳�쓣 媛뽯뒗�떎.
+            if (this.userWorkspaceService.checkWorkspaceManager()) {
+                issueVo.setModifyPermissionCheck(Boolean.TRUE);
+            }
+
+            //  �봽濡쒖젥�듃 愿�由ъ옄�씪 寃쎌슦 �빐�떦 �봽濡쒖젥�듃�뿉 �벑濡앸맂 �씠�뒋�뒗 �닔�젙 沅뚰븳�쓣 媛뽯뒗�떎.
+            if (this.projectRoleUserService.checkProjectManager(issue.getProject())) {
+                issueVo.setModifyPermissionCheck(Boolean.TRUE);
+            }
+        }
+
+        //  �떞�떦�옄媛� �뾾�쑝硫� 紐⑤뱺 �궗�슜�옄媛� �닔�젙 媛��뒫.
+        if (issue.getIssueUsers().size() < 1) {
+            issueVo.setModifyPermissionCheck(Boolean.TRUE);
+        }
+
+        issueVo.setUserVos(userVos);
+    }
+
+    //  �씠�뒋 泥⑤��뙆�씪 �젙蹂대�� �뀑�똿�븳�떎.
+    private void setAttachedFiles(Issue issue, IssueVo issueVo) {
+        List<AttachedFileVo> attachedFileVos = Lists.newArrayList();
+
+        for (AttachedFile attachedFile : issue.getAttachedFiles()) {
+            AttachedFileVo attachedFileVo = ConvertUtil.copyProperties(attachedFile, AttachedFileVo.class, "fileType");
+            attachedFileVo.setFileType(attachedFile.getFileType().toString());
+            attachedFileVos.add(attachedFileVo);
+        }
+
+        issueVo.setAttachedFileVos(attachedFileVos);
+    }
+
+    //  �씠�뒋(�씠�뒋 �쑀�삎)�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂대�� �뀑�똿�븳�떎.
+    private void setIssueCustomFields(Issue issue, IssueVo issueVo) {
+
+        //  �빐�떦 �봽濡쒖젥�듃�쓽 �씠�뒋 �쑀�삎�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂대�� 媛��졇�삩�떎.
+        IssueTypeCustomFieldCondition issueTypeCustomFieldCondition = new IssueTypeCustomFieldCondition();
+        issueTypeCustomFieldCondition.setProjectId(issue.getProject().getId());
+        issueTypeCustomFieldCondition.setIssueTypeId(issue.getIssueType().getId());
+        List<IssueTypeCustomFieldVo> issueTypeCustomFieldVos = this.issueTypeCustomFieldService.findIssueTypeCustomField(new HashMap<>(), issueTypeCustomFieldCondition);
+        issueVo.setIssueTypeCustomFieldVos(issueTypeCustomFieldVos);
+
+        //  �씠�뒋�뿉�꽌 �궗�슜�맂 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 媛��졇�삩�떎.
+        List<IssueCustomFieldValueVo> issueCustomFieldValueVos = this.issueCustomFieldValueService.findByIssueId(issue.getId());
+        issueVo.setIssueCustomFieldValueVos(issueCustomFieldValueVos);
+    }
+
+    //  �씠�뒋�뿉 �벑濡앸맂 �뙎湲� �젙蹂대�� �뀑�똿�븳�떎.
+    private void setIssueComments(Issue issue, IssueVo issueVo) {
+        issueVo.setIssueCommentVos(this.issueCommentService.findIssueComment(issue.getId()));
+    }
+
+    //  �씠�뒋 湲곕줉 �젙蹂대�� �뀑�똿�븳�떎.
+    private void setIssueHistory(Issue issue, IssueVo issueVo) {
+        issueVo.setIssueHistoryVos(this.issueHistoryService.findIssueHistory(issue.getId()));
+    }
+
+    //  �씠�뒋瑜� �닔�젙�븳�떎.
+    @Override
+    @Transactional
+    public Issue modifyIssue(IssueForm issueForm, List<MultipartFile> multipartFiles) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+        //  �씠�뒋 �닔�젙 沅뚰븳 泥댄겕
+        this.verifyIssueModifyPermission(issueForm.getId());
+        //  �봽濡쒖젥�듃 �쑀�슚�꽦 泥댄겕
+        Project project = this.projectService.getProject(issueForm.getProjectId());
+        //  �씠�뒋 �긽�깭 �쑀�슚�꽦 泥댄겕
+        IssueStatus issueStatus = this.issueStatusService.getIssueStatus(issueForm.getIssueStatusId());
+        //  �씠�뒋 �쑀�삎 �쑀�슚�꽦 泥댄겕
+        IssueType issueType = this.issueTypeService.getIssueType(issueForm.getIssueTypeId());
+        //  �슦�꽑�닚�쐞 �쑀�슚�꽦 泥댄겕
+        Priority priority = this.priorityService.getPriority(issueForm.getPriorityId());
+        //  以묒슂�룄 �쑀�슚�꽦 泥댄겕
+        Severity severity = this.severityService.getSeverity(issueForm.getSeverityId());
+        //  �젣紐� �쑀�슚�꽦 泥댄겕
+        this.verifyTitle(issueForm.getTitle());
+        //  �궇吏� �쑀�슚�꽦 泥댄겕
+        this.checkStartCompleteDate(issueForm.getStartDate(), issueForm.getCompleteDate());
+
+        //  �떞�떦�옄 �쑀�슚�꽦 泥댄겕
+        this.verifyIssueAssignee(project, issueForm);
+
+        Issue issue = this.getIssue(issueForm.getId());
+
+        //  蹂�寃� �씠�젰 �젙蹂� 異붿텧
+        StringBuilder detectIssueChange = this.issueHistoryService.detectIssueChange(issue, issueForm, project, issueStatus, issueType, priority, severity, multipartFiles);
+
+        //  �봽濡쒖젥�듃媛� 蹂�寃쎈릺硫� �씠�뒋 �꽆踰꾨�� �깉濡� �뵲�빞 �븳�떎.
+        this.checkChangeProject(project, issue);
+
+        //  �씠�뒋 �쑀�삎�씠 蹂�寃쎈릺�뿀�뒗吏� �솗�씤�븯怨� 蹂�寃쎈릺�뿀�떎硫� �씠�뒋 �긽�깭 �냽�꽦�씠 '��湲�' �씤 �씠�뒋 �긽�깭濡� 援먯껜�븳�떎.
+        if (this.checkChangeIssueType(issueType, issueStatus, issue)) {
+            issueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(issueType.getWorkflow());
+            //  �씠�뒋 �긽�깭 蹂�寃� �씠�젰 �궓湲곌린 - �씠�젰�쓣 �궓湲곌린 �쐞�빐 issueForm �뿉 issueStatus Id 媛믪쓣 ���옣.
+            issueForm.setIssueStatusId(issueStatus.getId());
+            this.issueHistoryService.detectIssueStatus(issue, issueForm, detectIssueChange, issueStatus);
+        }
+
+        ConvertUtil.copyProperties(issueForm, issue, "id");
+        issue.setProject(project);
+        issue.setIssueStatus(issueStatus);
+        issue.setIssueType(issueType);
+        issue.setPriority(priority);
+        issue.setSeverity(severity);
+        issue.setStartDate(issueForm.getStartDate());
+        issue.setCompleteDate(issueForm.getCompleteDate());
+
+        this.issueRepository.saveAndFlush(issue);
+        //  �떞�떦�옄 吏��젙
+        this.issueUserService.modifyIssueUser(issue, project.getWorkspace(), issueForm.getUserIds());
+
+        //  multipartFile �쓣 file Map List 媛앹껜濡� 蹂�寃쏀븳�떎.
+        List<Map<String, Object>> convertFileMaps = this.convertMultipartFileToFile(multipartFiles);
+
+        //  泥⑤� �뙆�씪 ���옣 - 鍮꾨룞湲곕줈 �옉�룞
+        this.attachedFileService.addAttachedFile(convertFileMaps, issue, this.webAppUtil.getLoginUser().getAccount());
+        //  �궘�젣�맂 泥⑤��뙆�씪 泥섎━
+        this.attachedFileService.removeAttachedFiles(issueForm.getRemoveFiles());
+        //  �뀓�뒪�듃 �뿉�뵒�꽣�뿉 泥⑤��븳 �뙆�씪�쓣 �씠�뒋�� �뿰寃�
+        this.checkNotHaveIssueIdAttachedFile(issue, issueForm);
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 ���옣
+        this.issueCustomFieldValueService.modifyIssueCustomFieldValue(issue, issueForm.getIssueCustomFields());
+        //  �씠�뒋 �씠�젰 �벑濡�
+        if (!StringUtils.isEmpty(detectIssueChange.toString())) {
+            this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, detectIssueChange.toString());
+        }
+
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_MODIFY));
+
+        return issue;
+    }
+
+    //  multipartFile �쓣 file Map 媛앹껜濡� 蹂�寃쏀븳�떎.
+    private List<Map<String, Object>> convertMultipartFileToFile(List<MultipartFile> multipartFiles) {
+        List<Map<String, Object>> convertFileMaps = Lists.newArrayList();
+
+        for (MultipartFile multipartFile : multipartFiles) {
+            try {
+                Map<String, Object> fileMap = CommonUtil.makeFileMap(multipartFile);
+                convertFileMaps.add(fileMap);
+            } catch (Exception e) {
+                log.debug("multipartFile -> file 蹂��솚 �삤瑜�" + e.getMessage());
+            }
+        }
+
+        return convertFileMaps;
+    }
+
+    //  �봽濡쒖젥�듃媛� 蹂�寃쎈릺�뿀�뒗吏� �솗�씤�븳�떎.
+    private void checkChangeProject(Project newProject, Issue issue) {
+        if (!issue.getProject().getId().equals(newProject.getId())) {
+            //  媛� �봽濡쒖젥�듃�쓽 怨좎쑀 �씠�뒋 踰덊샇 �깮�꽦
+            issue.setIssueNumber(this.issueNumberGeneratorService.generateIssueNumber(newProject));
+        }
+    }
+
+    //  �씠�뒋 �쑀�삎�씠 蹂�寃쎈릺�뿀�뒗吏� �솗�씤�븯怨� 蹂�寃쎈릺�뿀�쑝硫� �씠�뒋 �긽�깭瑜� ��湲� �냽�꽦�씤 �씠�뒋 �긽�깭濡� �뀑�똿�븳�떎.
+    private Boolean checkChangeIssueType(IssueType newIssueType, IssueStatus issueStatus, Issue issue) {
+        if (!issue.getIssueType().getId().equals(newIssueType.getId())) {
+            //  �씠�뒋 �긽�깭瑜� �꽑�깮�븯吏� �븡�븯�쓣 �븣
+            if (issueStatus == null) {
+                return true;
+            }
+
+            //  �씠�뒋 �긽�깭�쓽 �냽�꽦�씠 '��湲�' 媛� �븘�땶 寃쎌슦
+            if (!issueStatus.getIssueStatusType().equals(IssueStatusType.READY)) {
+                return true;
+            } else {
+                //  蹂�寃쏀븯�뒗 �씠�뒋 �쑀�삎�쓽 �썙�겕�뵆濡쒖슦�뿉 議댁옱�븯�뒗 �긽�깭 �냽�꽦 '��湲�'�� �룞�씪�븳 �긽�깭�씤吏� �솗�씤
+                IssueStatus newReadyIssueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(newIssueType.getWorkflow());
+
+                if (!newReadyIssueStatus.getId().equals(issueStatus.getId())) {
+                    return true;
+                }
+
+            }
+        }
+
+        return false;
+    }
+
+    //  �씠�뒋 �떞�떦�옄濡� 吏��젙�맆 �궗�슜�옄媛� �빐�떦 �봽濡쒖젥�듃�뿉 李몄뿬 �븯怨� �엳�뒗 �궗�슜�옄 �씤吏� �솗�씤
+    private void verifyIssueAssignee(Project project, IssueForm issueForm) {
+        if (issueForm.getUserIds().size() > 0) {
+            List<Long> trustUserIds = Lists.newArrayList(); //  李몄뿬 �솗�씤�맂 �궗�슜�옄
+
+            for (Long userId : issueForm.getUserIds()) {
+                boolean includeProject = false;
+
+                for (ProjectRole projectRole : project.getProjectRoles()) {
+                    ProjectRoleUser projectRoleUser = this.projectRoleUserService.findByProjectRoleIdAndUserId(projectRole.getId(), userId);
+
+                    if (projectRoleUser != null) {
+                        includeProject = true;
+                        trustUserIds.add(userId);
+                        break;
+                    }
+                }
+
+                //  �뜲�씠�꽣 蹂댁젙 �옉�뾽 - �봽濡쒖젥�듃�뿉�꽌 �젣�쇅�맂 �궗�슜�옄�뒗 �떞�떦�옄�뿉�꽌 �젣�쇅 �맆 �닔 �엳�룄濡� 泥섎━
+                /*if (!includeProject) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.PROJECT_NOT_INCLUDE_USER));
+                }*/
+            }
+            //  李몄뿬 �솗�씤�맂 �궗�슜�옄濡� �떞�떦�옄 蹂�寃�
+            issueForm.setUserIds(trustUserIds);
+        }
+    }
+
+    //  �씠�뒋 �닔�젙 沅뚰븳 泥댄겕
+    private void verifyIssueModifyPermission(Long issueId) {
+        Issue issue = this.getIssue(issueId);
+
+        //  �씠�뒋 �닔�젙 沅뚰븳�쓣 媛뽮퀬 �엳�뒗吏� �솗�씤
+        if (!this.checkHasPermission(ConvertUtil.copyProperties(issue, IssueVo.class), this.getIssueUserVos(issue))) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_MODIFY_PERMISSION));
+        }
+    }
+
+    //  �씠�뒋�뿉�꽌 �떞�떦�옄 �젙蹂대�� 異붿텧�븳�떎.
+    private List<UserVo> getIssueUserVos(Issue issue) {
+        List<UserVo> userVos = Lists.newArrayList();
+
+        for (IssueUser issueUser : issue.getIssueUsers()) {
+            UserVo userVo = ConvertUtil.copyProperties(issueUser.getUser(), UserVo.class, "password");
+            userVos.add(userVo);
+        }
+
+        return userVos;
+    }
+
+    //  �씠�뒋 �닔�젙 沅뚰븳�쓣 媛뽮퀬 �엳�뒗吏� �솗�씤
+    private boolean checkHasPermission(IssueVo issueVo, List<UserVo> issueUserVos) {
+        boolean hasPermission = false;
+
+        //  �뾽臾� 怨듦컙 愿�由ъ옄�씪 寃쎌슦 �닔�젙 沅뚰븳�쓣 媛뽯뒗�떎.
+        hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.WORKSPACE_MANAGER, issueVo, null);
+        //  �봽濡쒖젥�듃 愿�由ъ옄�씪 寃쎌슦 �빐�떦 �봽濡쒖젥�듃�뿉 �벑濡앸맂 �씠�뒋�뒗 �닔�젙 沅뚰븳�쓣 媛뽯뒗�떎.
+        hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.PROJECT_MANAGER, issueVo, null);
+        //   �씠�뒋 �벑濡앹옄�씪 寃쎌슦 �닔�젙 沅뚰븳�쓣 媛뽯뒗�떎.
+        hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.REGISTER, issueVo, null);
+        //  �씠�뒋 �떞�떦�옄�씪 寃쎌슦 �닔�젙 沅뚰븳�쓣 媛뽯뒗�떎.
+        hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.ASSIGNEE, issueVo, issueUserVos);
+        //  �떞�떦�옄媛� �뾾�쑝硫� 紐⑤뱺 �궗�슜�옄媛� �닔�젙 沅뚰븳�쓣 媛뽯뒗�떎.
+
+        return hasPermission;
+    }
+
+    //  �씠�뒋 �닔�젙 沅뚰븳�쓣 �솗�씤�븳�떎.
+    private boolean checkIssueModifyPermission(Boolean hasPermission, String checkType, IssueVo issueVo, List<UserVo> issueUserVos) {
+        if (!hasPermission) {
+            switch (checkType) {
+                case Issue.WORKSPACE_MANAGER:  //  �뾽臾� 怨듦컙 愿�由ъ옄
+                    //  �뾽臾� 怨듦컙 愿�由ъ옄�씪 寃쎌슦 �닔�젙 沅뚰븳�쓣 媛뽯뒗�떎.
+                    hasPermission = this.userWorkspaceService.checkWorkspaceManager();
+                    break;
+
+                case Issue.PROJECT_MANAGER:    //  �봽濡쒖젥�듃 愿�由ъ옄
+                    Issue issue = this.getIssue(issueVo.getId());
+                    //  �봽濡쒖젥�듃 愿�由ъ옄�씪 寃쎌슦 �빐�떦 �봽濡쒖젥�듃�뿉 �벑濡앸맂 �씠�뒋�뒗 �닔�젙 沅뚰븳�쓣 媛뽯뒗�떎.
+                    hasPermission = this.projectRoleUserService.checkProjectManager(issue.getProject());
+                    break;
+
+                case Issue.REGISTER:   //  �씠�뒋 �벑濡앹옄
+                    hasPermission = issueVo.getRegisterId().equals(this.webAppUtil.getLoginId());
+                    break;
+
+                case Issue.ASSIGNEE:
+                    //  �떞�떦�옄媛� �뾾�쑝硫� 紐⑤뱺 �궗�슜�옄媛� �닔�젙 沅뚰븳�쓣 媛뽯뒗�떎.
+                    if (issueUserVos.size() < 1) {
+                        hasPermission = true;
+                        break;
+                    }
+
+                    //   �씠�뒋 �떞�떦�옄 �뿬遺� �솗�씤
+                    for (UserVo issueUserVo : issueUserVos) {
+                        if (issueUserVo.getId().equals(this.webAppUtil.getLoginId())) {
+                            hasPermission = true;
+                            break;
+                        }
+                    }
+
+                    break;
+            }
+        }
+
+        return hasPermission;
+    }
+
+    //  �씠�뒋 �긽�깭 蹂�寃�
+    @Override
+    @Transactional
+    public void modifyIssueStatus(IssueForm issueForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+        //  蹂�寃� �씠�젰 �젙蹂� 異붿텧
+        StringBuilder detectIssueChange = new StringBuilder();
+        //  �씠�뒋 �닔�젙 沅뚰븳 泥댄겕
+        this.verifyIssueModifyPermission(issueForm.getId());
+        Issue issue = this.getIssue(issueForm.getId());
+
+        IssueStatus issueStatus = this.issueStatusService.getIssueStatus(issueForm.getIssueStatusId());
+        //  �씠�뒋 �긽�깭瑜� 蹂�寃쏀븷 �븣 �꽑�깮�븳 �씠�뒋 �긽�깭濡� 蹂�寃쏀븷 �닔 �엳�뒗吏� �솗�씤�븳�떎.
+        this.issueStatusService.checkNextIssueStatus(issue, issueStatus);
+        //  蹂�寃� �씠�젰 �젙蹂� 異붿텧
+        this.issueHistoryService.detectIssueStatus(issue, issueForm, detectIssueChange, issueStatus);
+
+        issue.setIssueStatus(issueStatus);
+        this.issueRepository.saveAndFlush(issue);
+
+        //  肄붾찘�듃 �벑濡�
+        if (!StringUtils.isEmpty(issueForm.getComment())) {
+            IssueCommentForm issueCommentForm = new IssueCommentForm();
+            issueCommentForm.setIssueId(issue.getId());
+            issueCommentForm.setDescription(issueForm.getComment());
+            this.issueCommentService.addIssueComment(issueCommentForm);
+        }
+
+        //  �씠�뒋 �씠�젰 �벑濡�
+        if (!StringUtils.isEmpty(detectIssueChange.toString())) {
+            StringBuilder stringBuilder = new StringBuilder();
+            stringBuilder.append("<ul class=\"activity-list\">");
+            stringBuilder.append(detectIssueChange.toString());
+            stringBuilder.append("</ul>");
+
+            this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, stringBuilder.toString());
+        }
+
+        //  �씠�뒋 踰꾩쟾 �깮�꽦
+        this.issueVersionService.addIssueVersion(issue);
+
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_STATUS_CHANGE));
+    }
+
+    //  �씠�뒋 �떞�떦�옄 蹂�寃�
+    @Override
+    @Transactional
+    public void modifyIssueUser(IssueForm issueForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+        //  蹂�寃� �씠�젰 �젙蹂� 異붿텧
+        StringBuilder detectIssueChange = new StringBuilder();
+        //  �씠�뒋 �닔�젙 沅뚰븳 泥댄겕
+        this.verifyIssueModifyPermission(issueForm.getId());
+        Issue issue = this.getIssue(issueForm.getId());
+        issue.setProject(this.projectService.getProject(issueForm.getProjectId()));
+
+        //  蹂�寃� �씠�젰 �젙蹂� 異붿텧
+        this.issueHistoryService.detectIssueManager(issue, issueForm, detectIssueChange);
+
+        this.issueUserService.modifyIssueUser(issue, issue.getProject().getWorkspace(), issueForm.getUserIds());
+        this.issueRepository.saveAndFlush(issue);
+
+        //  �씠�뒋 �씠�젰 �벑濡�
+        if (!StringUtils.isEmpty(detectIssueChange.toString())) {
+            StringBuilder stringBuilder = new StringBuilder();
+            stringBuilder.append("<ul class=\"activity-list\">");
+            stringBuilder.append(detectIssueChange.toString());
+            stringBuilder.append("</ul>");
+
+            this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, stringBuilder.toString());
+        }
+
+        //  �씠�뒋 踰꾩쟾 �깮�꽦
+        this.issueVersionService.addIssueVersion(issue);
+
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_USER_CHANGE));
+    }
+
+    //  �씠�뒋瑜� �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void removeIssues(IssueForm issueForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        if (issueForm.getRemoveIds().size() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_REMOVE_NOT_SELECT));
+        }
+
+        List<Issue> removeIssues = Lists.newArrayList();
+
+        for (Long issueId : issueForm.getRemoveIds()) {
+            Issue issue = this.issueRemoves(issueId);
+            removeIssues.add(issue);
+        }
+
+        if (removeIssues.size() > 0) {
+            //this.issueRepository.deleteAll(removeIssues);
+        }
+
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_REMOVE));
+    }
+
+    private Issue issueRemoves(Long issueId) {
+        Issue issue = this.getIssue(issueId);
+        //  �씠�뒋 �닔�젙 沅뚰븳�쓣 媛뽮퀬 �엳�뒗吏� �솗�씤
+        this.verifyIssueModifyPermission(issueId);
+
+        //  �씠�뒋 泥⑤� �뙆�씪�쓣 �궘�젣�븳�떎.
+        if (issue.getAttachedFiles().size() > 0) {
+            List<Long> attachedFileIds = Lists.newArrayList();
+
+            for (AttachedFile attachedFile : issue.getAttachedFiles()) {
+                attachedFileIds.add(attachedFile.getId());
+            }
+            //  泥⑤��뙆�씪 �궘�젣
+            this.attachedFileService.removeAttachedFiles(attachedFileIds);
+        }
+
+        //  �씠�뒋 �깮�꽦, �궘�젣�떆 �삁�빟 �씠硫붿씪�뿉 �벑濡앺빐�넃�뒗�떎.
+        this.reservationIssueEmail(issue.getId(), EmailType.ISSUE_REMOVE);
+        //  �씠�뒋 �궘�젣
+        this.issueRepository.delete(issue);
+
+        return issue;
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public Issue getIssue(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_EXIST));
+        }
+
+        Issue issue = this.findOne(id);
+
+        if (issue == null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_EXIST));
+        }
+
+        return issue;
+    }
+
+    //  �씠�뒋 �쑀�삎�쓣 �궗�슜�븯�뒗 �씠�뒋 媛��닔瑜� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public long countByIssueTypeId(Long issueTypeId) {
+        return this.issueMapper.countByIssueTypeId(issueTypeId);
+    }
+
+    //  �씠�뒋 �긽�깭瑜� �궗�슜�븯�뒗 �씠�뒋 媛��닔瑜� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public long countByIssueStatus(Long issueStatusId) {
+        return this.issueMapper.countByIssueStatusId(issueStatusId);
+    }
+
+    //  �씠�뒋 �쑀�삎�뿉�꽌 �썙�겕�뵆濡쒖슦媛� 蹂�寃쎈릺�뿀�쓣 �븣 �빐�떦 �썙�겕�뵆濡쒖슦�뿉 �쁽�옱 �씠�뒋 �긽�깭媛� 議댁옱�븯吏� �븡�쑝硫� ��湲�(�깮�꽦) 濡� 蹂�寃쏀븳�떎.
+    @Override
+    @Transactional
+    public void changeWorkflows(Workflow workflow, IssueType issueType) {
+        List<Map<String, Object>> issueMaps = this.issueMapper.findByIssueTypeId(issueType.getId());
+        List<IssueStatusVo> issueStatusVos = this.issueStatusService.findByWorkflowId(workflow.getId());
+        IssueStatus readyIssueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(workflow);
+        List<Issue> updateIssues = Lists.newArrayList();
+
+        for (Map<String, Object> issueMap : issueMaps) {
+            boolean exist = false;
+
+            for (IssueStatusVo issueStatusVo : issueStatusVos) {
+                Long issueStatusId = MapUtil.getLong(issueMap, "issueStatusId");
+                if (issueStatusId == null) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_NOT_EXIST));
+                }
+
+                if (issueStatusId.equals(issueStatusVo.getId())) {
+                    exist = true;
+                    break;
+                }
+            }
+            //  議댁옱�븯吏� �븡�뒗 ���긽��
+            if (!exist) {
+                StringBuilder stringBuilder = new StringBuilder();
+                String issueStatusName = MapUtil.getString(issueMap, "issueStatusName");
+                if (issueStatusName == null) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_NOT_EXIST));
+                }
+
+                Issue issue = this.getIssue(MapUtil.getLong(issueMap, "issueId"));
+
+                //  �썙�겕�뵆濡쒖슦�뿉�꽌 �씠�뒋 �긽�깭媛� �궘�젣�릺�뼱 �긽�깭媛� 蹂�寃쎈맂 �젙蹂대�� 湲곕줉�븳�떎.
+                this.issueHistoryService.recordRemoveWorkflowToIssueStatus(issueStatusName, readyIssueStatus.getName(), stringBuilder);
+                this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, stringBuilder.toString());
+
+                issue.setIssueStatus(readyIssueStatus);
+                updateIssues.add(issue);
+            }
+        }
+
+        if (updateIssues.size() > 0) {
+            this.issueRepository.saveAll(updateIssues);
+
+            for (Issue issue : updateIssues) {
+                //  �씠�뒋 踰꾩쟾 �깮�꽦
+                this.issueVersionService.addIssueVersion(issue);
+            }
+        }
+    }
+
+    //  �씠�뒋 紐⑸줉�쓣 �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    @Override
+    @Transactional
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+
+        //  �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븯怨� 鍮꾪솢�꽦�씪 寃쎌슦 �뿊�� �떎�슫濡쒕뱶瑜� 湲덉��븳�떎.
+        ModelAndView modelAndView = this.workspaceService.checkUseExcelDownload(model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        Map<String, Object> conditions = new HashMap<>();
+        //  �뿊�� �떎�슫濡쒕뱶�뿉 �븘�슂�븳 寃��깋 議곌굔 �젙蹂대�� 異붿텧�븯怨� 寃��깋 議곌굔 異붿텧�뿉 �삤瑜섍� 諛쒖깮�븯硫� 寃쎄퀬瑜� �몴�떆�빐以��떎.
+        modelAndView = this.excelConditionCheck.checkCondition(conditions, request, model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        IssueCondition issueCondition = IssueCondition.make(conditions);
+        //  寃��깋 議곌굔�쓣 留뚮뱺�떎
+        this.makeIssueSearchCondition(issueCondition, Lists.newArrayList("01", "02", "03"), null);
+
+        Set<String> issueIds = new HashSet<>(); //  �궗�슜�옄 �젙�쓽 �븘�뱶 寃��깋�떆 �굹�삤�뒗 �씠�뒋 �븘�씠�뵒 ���옣 而щ젆�뀡
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.issueList")); // �씠�뒋 紐⑸줉
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueStatusName", this.messageAccessor.message("common.status"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �긽�깭
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueKey", this.messageAccessor.message("common.issueKey"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �씠�뒋 踰덊샇
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("title", this.messageAccessor.message("common.issueTitle"), 40, ExportExcelAttrVo.ALIGN_LEFT)); // �씠�뒋 �젣紐�
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("description", this.messageAccessor.message("common.content"), 60, ExportExcelAttrVo.ALIGN_LEFT)); // �궡�슜
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueTypeName", this.messageAccessor.message("common.issueType"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // �씠�뒋 ���엯
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("assignees", this.messageAccessor.message("common.assignee"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // �떞�떦�옄
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("priorityName", this.messageAccessor.message("common.priority"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �슦�꽑�닚�쐞
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("severityName", this.messageAccessor.message("common.importance"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // 以묒슂�룄
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("register", this.messageAccessor.message("common.register"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // �벑濡앹옄
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("period", this.messageAccessor.message("common.period"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 湲곌컙
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("modifyDate", this.messageAccessor.message("common.modifyDate"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 理쒖쥌 蹂�寃쎌씪
+
+
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �궗�슜�븳 �씠�뒋瑜� 李얜뒗�떎. 留뚯빟 �씠�뒋媛� �뾾�떎硫� �뿬湲곗꽌 �씠�뒋 議고쉶媛� �걹�궃�떎.
+        if (!this.searchUseCustomFields(issueCondition, issueIds, resJsonData, null)) {
+            model.addAttribute(Constants.EXCEL, excelInfo);
+            return new ModelAndView(this.excelView);
+        }
+
+        List<IssueVo> issueVos = Lists.newArrayList();  //  �씠�뒋 紐⑸줉 �뜲�씠�꽣 ���옣 而щ젆�뀡
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶濡� 寃��깋�븳 �씠�뒋 �븘�씠�뵒 媛�
+        List<String> issueKeys = Lists.newArrayList(issueIds);
+        issueCondition.setIssueIds(issueKeys);
+
+        List<Map<String, Object>> results = this.issueMapper.find(issueCondition);
+        //  �뿊�� �떎�슫濡쒕뱶瑜� 吏꾪뻾�븷 �븣 �쟾泥� �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �몴�떆�븳�떎.
+        this.makeIssueExcelDownloadCustomFields(excelInfo);
+
+        //  �씠�뒋 �븘�씠�뵒 珥덇린�솕
+        issueCondition.setIssueIds(Lists.newArrayList());
+
+        //  Map �뿉 �엳�뒗 �뜲�씠�꽣瑜� IssueVo �뜲�씠�꽣濡� 蹂��솚�븳�떎.
+        this.setMapToIssueVo(results, issueVos, issueCondition);
+
+        //  IssueVos �뜲�씠�꽣瑜� �뿊���뿉�꽌 �몴�떆�븷 �닔 �엳�뒗 �뜲�씠�꽣濡� 蹂�寃쏀븳�떎.
+        List<Map<String, String>> convertExcelViewToIssueMaps = this.convertExcelViewToIssueVos(issueVos);
+
+        if (results.size() > EXCEL_DOWNLOAD_MAX_ROWS) {
+            //  �뿊�� 1留뚭굔 珥덇낵 �븣由�
+            this.simpMessagingTemplate.convertAndSendToUser(this.webAppUtil.getLoginUser().getAccount(), "/notification/system-alert", this.messageAccessor.getMessage(MsgConstants.EXCEL_DOWNLOAD_MAX_ROWS_OVER));
+
+            //  1留� 嫄대쭔 異쒕젰�빐以��떎.
+            excelInfo.setDatas(convertExcelViewToIssueMaps.subList(0, EXCEL_DOWNLOAD_MAX_ROWS));
+            model.addAttribute(Constants.EXCEL, excelInfo);
+            return new ModelAndView(this.excelView);
+        }
+
+        //  �뿊���뿉 �꽔�쓣 �뜲�씠�꽣 - IssueVos �뜲�씠�꽣瑜� �뿊���뿉�꽌 �몴�떆�븷 �닔 �엳�뒗 �뜲�씠�꽣濡� 蹂�寃쏀븳�떎.
+        excelInfo.setDatas(convertExcelViewToIssueMaps);
+
+        model.addAttribute(Constants.EXCEL, excelInfo);
+        return new ModelAndView(this.excelView);
+    }
+
+    //  �뿊�� �떎�슫濡쒕뱶瑜� 吏꾪뻾�븷 �븣 �쟾泥� �궗�슜�옄 �젙�쓽 �븘�뱶瑜� �몴�떆�븳�떎.
+    private void makeIssueExcelDownloadCustomFields(ExportExcelVo excelInfo) {
+        List<CustomField> customFields = this.customFieldService.findByWorkspaceId();
+
+        for (CustomField customField : customFields) {
+            excelInfo.addAttrInfos(new ExportExcelAttrVo("customField_" + customField.getId(), customField.getName(), 10, ExportExcelAttrVo.ALIGN_CENTER));
+        }
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂� 異붽�
+    private void setIssueCustomFieldValue(List<IssueVo> issueVos, IssueCondition issueCondition) {
+        //  �씠�뒋�뿉�꽌 ���옣�븳 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 議고쉶�븳�떎.
+        List<Map<String, Object>> issueCustomFieldValues = this.issueCustomFieldValueService.findInIssueIds(issueCondition);
+
+        for (IssueVo issueVo : issueVos) {
+            for (Map<String, Object> issueCustomFieldValue : issueCustomFieldValues) {
+                if (issueVo.getId().equals(MapUtil.getLong(issueCustomFieldValue, "issueId"))) {
+                    IssueCustomFieldValueVo issueCustomFieldValueVo = new IssueCustomFieldValueVo();
+                    issueCustomFieldValueVo.setUseValue(MapUtil.getString(issueCustomFieldValue, "useValue"));
+
+                    CustomFieldVo customFieldVo = new CustomFieldVo();
+                    customFieldVo.setId(MapUtil.getLong(issueCustomFieldValue, "customFieldId"));
+                    issueCustomFieldValueVo.setCustomFieldVo(customFieldVo);
+                    issueVo.addIssueCustomFieldValueVo(issueCustomFieldValueVo);
+                }
+            }
+        }
+    }
+
+    //  �뿰愿��씪媛� �젙蹂� 異붽�
+    private void setRelationIssue(IssueVo issueVo, Long issueId) {
+        List<IssueVo> relationIssues = this.issueRelationService.findRelationIssue(issueId);
+        issueVo.setIssueRelationIssueVos(relationIssues);
+    }
+
+    //  IssueVos �뜲�씠�꽣瑜� �뿊���뿉�꽌 �몴�떆�븷 �닔 �엳�뒗 �뜲�씠�꽣濡� 蹂�寃쏀븳�떎.
+    private List<Map<String, String>> convertExcelViewToIssueVos(List<IssueVo> issueVos) {
+        List<Map<String, String>> results = Lists.newArrayList();
+
+        for (IssueVo issueVo : issueVos) {
+            try {
+                Map<String, String> result = new HashMap<>();
+                result.put("issueStatusName", issueVo.getIssueStatusName());
+                result.put("issueKey", issueVo.getProjectKey() + "-" + issueVo.getIssueNumber());
+                result.put("title", issueVo.getTitle());
+
+                String description = "";
+
+                if (issueVo.getDescription() != null) {
+                    description = Jsoup.parse(issueVo.getDescription()).text();   //  HTML �깭洹� �젣嫄�
+                    description = description.replaceAll("\\<.*?>", "");    //  怨듬갚 �젣嫄�
+                }
+
+                result.put("description", description);
+                result.put("issueTypeName", issueVo.getIssueTypeName());
+                result.put("assignees", CommonUtil.convertUserVosToString(issueVo.getUserVos()));
+                result.put("priorityName", issueVo.getPriorityName());
+                result.put("severityName", issueVo.getSeverityName());
+
+                UserVo register = this.userService.removeSensitiveUser(issueVo.getRegisterId());
+                //  �벑濡앹옄
+                result.put("register", register.getByName());
+                //  理쒖쥌 蹂�寃쎌씪
+                result.put("modifyDate", issueVo.getModifyDate());
+
+                if (StringUtils.isEmpty(issueVo.getStartDate())) {
+                    result.put("period", "");
+                } else {
+                    result.put("period", issueVo.getStartDate() + " ~ " + issueVo.getCompleteDate());
+                }
+
+                for (IssueCustomFieldValueVo issueCustomFieldValueVo : issueVo.getIssueCustomFieldValueVos()) {
+                    //  �궗�슜�옄 �젙�쓽 �븘�뱶 媛� ���옣�씠 �븘吏� �븞�릺�뼱 �엳�뒗 寃쎌슦
+                    if (result.get("customField_" + issueCustomFieldValueVo.getCustomFieldVo().getId()) == null) {
+                        result.put("customField_" + issueCustomFieldValueVo.getCustomFieldVo().getId().toString(), issueCustomFieldValueVo.getUseValue());
+                    } else {
+                        //  �씠誘� ���옣�릺�뼱 �엳�쑝硫� �떎以� �꽑�깮 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪씠�떎.
+                        String useValue = result.get("customField_" + issueCustomFieldValueVo.getCustomFieldVo().getId());
+                        result.put("customField_" + issueCustomFieldValueVo.getCustomFieldVo().getId().toString(), useValue + ", " + issueCustomFieldValueVo.getUseValue());
+                    }
+                }
+
+                results.add(result);
+            } catch (Exception e) {
+                log.error("�뿊�� �떎�슫濡쒕뱶 �삤瑜� 諛쒖깮");
+            }
+        }
+
+        return results;
+    }
+
+    //  �씠�뒋 �떎以� �긽�깭 蹂�寃�
+    @Override
+    @Transactional
+    public void modifyMultiIssueStatus(IssueForm issueForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        for (Long issueId : issueForm.getIds()) {
+            issueForm.setId(issueId);
+            //  �씠�뒋 �긽�깭 蹂�寃�
+            this.modifyIssueStatus(issueForm);
+        }
+    }
+
+    //  �씠�뒋 Import �슜 �뿊�� �뀥�뵆由� �떎�슫濡쒕뱶
+    @Override
+    @Transactional
+    public ModelAndView downloadExcelTemplate(HttpServletRequest request, Model model) {
+        Map<String, Object> conditions;
+
+        try {
+            //  �뿊�� �떎�슫濡쒕뱶�뿉 �븘�슂�븳 寃��깋 議곌굔 �젙蹂대�� 異붿텧�븳�떎.
+            conditions = CommonUtil.getSearchConditions(request);
+        } catch (IOException e) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EXCEL_CONDITIONS_NOT_EXIST));
+        }
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setHideCount(true);
+        excelInfo.setFileName(this.messageAccessor.message("common.registerExcelIssue")); // �뿊��濡� �씠�뒋 �벑濡앺븯湲�
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.title"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // �젣紐�
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.content"), 40, ExportExcelAttrVo.ALIGN_CENTER)); // �궡�슜
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.projectKey"), 10, ExportExcelAttrVo.ALIGN_LEFT)); // �봽濡쒖젥�듃 �궎
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.issueType"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // �씠�뒋 ���엯
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.priority"), 5, ExportExcelAttrVo.ALIGN_CENTER)); // �슦�꽑�닚�쐞
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.importance"), 5, ExportExcelAttrVo.ALIGN_CENTER)); // 以묒슂�룄
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.assignee"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // �떞�떦�옄
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.startDate"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // �떆�옉�씪
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.endDate"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // 醫낅즺�씪
+        //  �봽濡쒖젥�듃�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂대�� 異붿텧�븯�뿬 �뿊�� download �뀥�뵆由우쓣 留뚮뱺�떎.
+        this.makeIssueExcelTemplateCustomFields(excelInfo, conditions);
+        //  �뿊���뿉 �꽔�쓣 �뜲�씠�꽣 - IssueVos �뜲�씠�꽣瑜� �뿊���뿉�꽌 �몴�떆�븷 �닔 �엳�뒗 �뜲�씠�꽣濡� 蹂�寃쏀븳�떎.
+        excelInfo.setDatas(Lists.newArrayList(new IssueVo()));
+
+        model.addAttribute(Constants.EXCEL, excelInfo);
+        return new ModelAndView(this.excelView);
+    }
+
+    //  �봽濡쒖젥�듃�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂대�� 異붿텧�븯�뿬 �뿊�� download �뀥�뵆由우쓣 留뚮뱺�떎.
+    private void makeIssueExcelTemplateCustomFields(ExportExcelVo excelInfo, Map<String, Object> conditions) {
+        List<IssueTypeCustomField> issueTypeCustomFields = this.issueTypeCustomFieldService.findByProjectIdAndIssueTypeId(MapUtil.getLong(conditions, "projectId"), MapUtil.getLong(conditions, "issueTypeId"));
+
+        for (IssueTypeCustomField issueTypeCustomField : issueTypeCustomFields) {
+            excelInfo.addAttrInfos(new ExportExcelAttrVo("id", issueTypeCustomField.getCustomField().getName(), 10, ExportExcelAttrVo.ALIGN_CENTER));
+        }
+    }
+
+    //  �뿊�� import 濡� �씠�뒋瑜� �벑濡앺븳�떎.
+    @Override
+    @Transactional
+    public void importExcel(MultipartFile multipartFile) throws Exception {
+        /*StopWatch serviceStart = new StopWatch();
+        serviceStart.start();*/
+
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        if (multipartFile != null) {
+            //  �뾽濡쒕뱶 �뙆�씪 �솗�옣�옄 泥댄겕
+            this.verifyMultipartFileExtension(multipartFile);
+
+            Map<String, Project> projectMaps = new HashMap<>(); //  �봽濡쒖젥�듃 紐⑥쓬
+            Map<String, IssueType> issueTypeMaps = new HashMap<>(); //  �씠�뒋 ���엯 紐⑥쓬
+            Map<String, Priority> priorityMaps = new HashMap<>();   //  �슦�꽑 �닚�쐞 紐⑥쓬
+            Map<String, Severity> severityMaps = new HashMap<>();   //  以묒슂�룄 紐⑥쓬
+            Map<String, Object> userMaps = new HashMap<>(); //  �궗�슜�옄 紐⑥쓬
+            Map<String, CustomField> customFieldMaps = new HashMap<>();
+            Map<String, IssueStatus> issueStatusReadyMaps = new HashMap<>();   //  �긽�깭 �냽�꽦 '��湲�'�씤 �씠�뒋 �긽�깭
+            Map<Long, Long> issueNumberMaps = new HashMap<>();  //  �씠�뒋 踰덊샇 紐⑥쓬
+            Map<String, Long> issueTypeCustomFieldMaps = new HashMap<>(); //  �씠�뒋 ���엯 + �궗�슜�옄 �젙�쓽 �븘�뱶 �뿰寃� �젙蹂�
+            Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());  //  �씠�뒋瑜� �꽔�쑝�젮�뒗 �뾽臾� 怨듦컙
+            //  �씠�뒋�쓽 二쇱슂 �냽�꽦�쓣 map �뿉 ���옣�븯�뿬 �뿊�� import �뿉�꽌 吏��젙�븳 ���긽(�씠�뒋 �냽�꽦)�쓣 鍮좊Ⅴ寃� 李얠쓣 �닔 �엳寃� �븳�떎.
+            this.IssueAttributeMapToList(projectMaps, issueTypeMaps, priorityMaps, severityMaps, userMaps, customFieldMaps, issueNumberMaps, issueTypeCustomFieldMaps, issueStatusReadyMaps);
+            //  0.237 - 0.230
+
+            List<IssueForm> issueForms = Lists.newArrayList();
+            List<String> headers = Lists.newArrayList();
+
+            Workbook workbook;
+
+            workbook = WorkbookFactory.create(multipartFile.getInputStream());
+            Sheet sheet = workbook.getSheetAt(0);
+            int lastRowNum = sheet.getLastRowNum() + 1;
+
+            //  2嫄� - �젣紐�, �뿤�뜑 - �꽦�뒫�쓣 �쐞�빐 理쒕� 1留뚭굔�쑝濡� �젣�븳
+            if (lastRowNum > (EXCEL_IMPORT_MAX_ROWS + 2)) {
+                throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_MAX_ROWS_OVER));
+            }
+
+            for (int rowIndex = 0; rowIndex < lastRowNum; rowIndex++) {
+                //  0踰덉� �뿤�뜑�뒗 臾댁떆�븳�떎.
+                Row row = sheet.getRow(rowIndex);
+                //  �뿤�뜑 �젙蹂대�� 異붿텧�븳�떎 - �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂대�� 媛��졇�삤湲� �쐞�빐
+                if (rowIndex == 1) {
+                    for (int cellIndex = 0; cellIndex < row.getLastCellNum(); cellIndex++) {
+                        Cell cell = row.getCell(cellIndex);
+
+                        if (cell == null) {
+                            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EXCEL_EMPTY_CELL));
+                        }
+
+                        //  �뿊�� import �뜲�씠�꽣�뿉�꽌 cell 媛믪쓣 臾몄옄�뿴濡� 蹂��솚�븳�떎.
+                        String cellValue = CommonUtil.convertExcelStringToCell(cell);
+
+                        if (StringUtils.isEmpty(cellValue)) {
+                            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EXCEL_HEADER_EMPTY_CELL));
+                        }
+
+                        headers.add(cellValue);
+                    }
+                }
+
+                //  1踰� �뿤�뜑遺��꽣 �뜲�씠�꽣 �쁺�뿭
+                if (rowIndex > 1) {
+                    //  �씠�뒋濡� �벑濡앺븯湲� �쐞�빐 IssueForm �뿉 �뜲�씠�꽣瑜� �뀑�똿�븳�떎.
+                    issueForms.add(this.setIssueFormToExcelField(row, (rowIndex + 1), issueStatusReadyMaps, projectMaps, issueTypeMaps, priorityMaps, severityMaps, userMaps, customFieldMaps, issueNumberMaps, headers));
+                }
+            }
+
+            if (issueForms.size() < 1) {
+                return;
+            }
+            //  1.176
+
+
+            //  �씠�뒋 �벑濡�
+            this.issueMapper.insertBatch(issueForms);
+            //  0.416 - 0.439
+
+            //  1.373 ~ 1.394
+
+            //  TODO - �씠�뒋 �씠�젰 踰뚰겕 �벑濡앹쓣 �븷 寃쎌슦 �봽濡쒗븘 �씠�젰 議고쉶�뿉�꽌 遺��븯媛� �떖�빐�꽌 �꽔�쓣吏� 怨좊�쇳빐�빞�븿.
+            //  this.bulkInsertIssueHistory(issueForms);
+
+            //  �씠�뒋 �떞�떦�옄 踰뚰겕 �벑濡�
+            this.bulkInsertIssueAssignee(issueForms, workspace);
+            //  0.361 - 0.705
+
+            //  1.816
+            /*StopWatch serviceStart = new StopWatch();
+            serviceStart.start();*/
+            //  �씠�뒋 �궗�슜�옄 �젙�쓽 媛� �븘�뱶 踰뚰겕 �벑濡�
+            this.bulkInsertIssueCustomFieldValue(issueForms, issueTypeCustomFieldMaps);
+            //  3.628 - 3.445
+
+            /*serviceStart.stop();
+            log.debug("2李� ���옣 �떆媛� : " + serviceStart.getTime());*/
+
+            //  �씠�뒋 由ъ뒪�겕 踰뚰겕 �벑濡�
+            this.bulkInsertIssueRisk(issueForms, workspace);
+
+            //  reverse index �뾽�뜲�씠�듃
+            this.issueMapper.updateBatch(issueForms);
+            //  利앷��맂 �씠�뒋 踰덊샇瑜� �뾽�뜲�씠�듃 �븳�떎.
+            this.issueNumberGeneratorService.updateIssueNumber(issueNumberMaps);
+        }
+    }
+
+    //  �뾽濡쒕뱶 �뙆�씪 �솗�옣�옄 泥댄겕
+    private void verifyMultipartFileExtension(MultipartFile multipartFile) {
+        multipartFile.getOriginalFilename();
+
+        int pos = multipartFile.getOriginalFilename().lastIndexOf(".");
+        String ext = multipartFile.getOriginalFilename().substring(pos + 1);
+
+        if (!ext.equals("xlsx")) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EXCEL_NOT_EXTENSION));
+        }
+    }
+
+    //  �씠�뒋 �씠�젰 踰뚰겕 �벑濡�
+    private void bulkInsertIssueHistory(List<IssueForm> issueForms) {
+        List<Map<String, Object>> issueHistoryMaps = Lists.newArrayList();
+
+        for (IssueForm issueForm : issueForms) {
+            Map<String, Object> issueHistoryMap = new HashMap<>();
+            issueHistoryMap.put("issueId", issueForm.getId());
+            issueHistoryMap.put("projectId", issueForm.getProjectId());
+            issueHistoryMap.put("registerId", this.webAppUtil.getLoginId());
+            issueHistoryMap.put("issueHistoryType", IssueHistoryType.ADD.toString());
+
+            StringBuilder description = new StringBuilder();
+            //  �씠�젰 �뀒�뒪�듃 異붿텧
+            this.issueHistoryService.makeDescription(description, IssueHistoryType.ADD, null);
+            issueHistoryMap.put("description", description.toString());
+
+            issueHistoryMaps.add(issueHistoryMap);
+        }
+
+        //  �씠�뒋 �씠�젰 踰뚰겕 �벑濡�
+        this.issueMapper.insertHistoryBatch(issueHistoryMaps);
+    }
+
+    //  �씠�뒋 �떞�떦�옄 踰뚰겕 �벑濡�
+    private void bulkInsertIssueAssignee(List<IssueForm> issueForms, Workspace workspace) {
+        List<Map<String, Long>> issueAssigneeMaps = Lists.newArrayList();
+
+        for (IssueForm issueForm : issueForms) {
+            for (Long userId : issueForm.getUserIds()) {
+                Map<String, Long> issueAssigneeMap = new HashMap<>();
+                issueAssigneeMap.put("issueId", issueForm.getId());
+                issueAssigneeMap.put("userId", userId);
+                issueAssigneeMap.put("workspaceId", workspace.getId());
+                issueAssigneeMap.put("registerId", this.webAppUtil.getLoginId());
+                issueAssigneeMaps.add(issueAssigneeMap);
+            }
+        }
+
+        if (issueAssigneeMaps.size() > 0) {
+            //  �씠�뒋 �떞�떦�옄 踰뚰겕 �벑濡�
+            this.issueUserService.insertIssueUser(issueAssigneeMaps);
+        }
+    }
+
+    //  �씠�뒋 由ъ뒪�겕 踰뚰겕 �벑濡�
+    private void bulkInsertIssueRisk(List<IssueForm> issueForms, Workspace workspace) {
+        List<Map<String, Long>> issueRiskMaps = Lists.newArrayList();
+        for (IssueForm issueForm : issueForms) {
+            Map<String, Long> issueRiskMap = new HashMap<>();
+            issueRiskMap.put("issueId", issueForm.getId());
+            issueRiskMap.put("changeAssigneeCount", 0L);
+            issueRiskMap.put("changeIssueStatusCount", 0L);
+            issueRiskMap.put("workspaceId", workspace.getId());
+            issueRiskMap.put("issueStatusIds", issueForm.getIssueStatusId());
+            issueRiskMap.put("registerId", this.webAppUtil.getLoginId());
+            issueRiskMaps.add(issueRiskMap);
+        }
+
+        if (issueRiskMaps.size() > 0) {
+            //  �씠�뒋 由ъ뒪�겕 踰뚰겕 �벑濡�
+            this.issueMapper.insertIssueRiskBatch(issueRiskMaps);
+        }
+    }
+
+    //  �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶 �꽑�깮 媛� 踰뚰겕 �벑濡�
+    private void bulkInsertIssueCustomFieldValue(List<IssueForm> issueForms, Map<String, Long> issueTypeCustomFieldMaps) {
+        List<Map<String, Object>> issueCustomFieldValueMaps = Lists.newArrayList();
+
+        for (IssueForm issueForm : issueForms) {
+            for (Map<String, Object> issueCustomField : issueForm.getIssueCustomFields()) {
+                String findKey = issueForm.getIssueTypeId().toString() + MapUtil.getString(issueCustomField, "customFieldId");
+
+                //  �씠�뒋 ���엯 + �궗�슜�옄 �젙�쓽 �븘�뱶 �뿰寃곗젙蹂닿� �뾾�떎硫� ���옣�븯吏� �븡�뒗�떎.
+                if (issueTypeCustomFieldMaps.get(findKey) == null) {
+                    continue;
+                }
+
+                issueCustomField.put("issueTypeCustomFieldId", issueTypeCustomFieldMaps.get(findKey));
+                issueCustomField.put("issueId", issueForm.getId());
+                issueCustomField.put("registerId", this.webAppUtil.getLoginId());
+                issueCustomFieldValueMaps.add(issueCustomField);
+            }
+        }
+
+        if (issueCustomFieldValueMaps.size() > 0) {
+            this.issueMapper.insertIssueCustomFieldValueBatch(issueCustomFieldValueMaps);
+        }
+    }
+
+    //  �씠�뒋�쓽 二쇱슂 �냽�꽦�쓣 map �뿉 ���옣�븯�뿬 �뿊�� import �뿉�꽌 吏��젙�븳 ���긽(�씠�뒋 �냽�꽦)�쓣 鍮좊Ⅴ寃� 李얠쓣 �닔 �엳寃� �븳�떎.
+    private void IssueAttributeMapToList(Map<String, Project> projectMaps, Map<String, IssueType> issueTypeMaps, Map<String, Priority> priorityMaps, Map<String, Severity> severityMaps,
+                                         Map<String, Object> userMaps, Map<String, CustomField> customFieldMaps, Map<Long, Long> issueNumberMaps, Map<String, Long> issueTypeCustomFieldMaps, Map<String, IssueStatus> issueStatusReadyMaps) {
+        //  �봽濡쒖젥�듃 �궎濡� 諛붾줈 李얠쓣 �닔 �엳寃� 以�鍮�
+        List<Project> projects = this.projectService.findByWorkspaceId();
+        List<Long> projectIds = Lists.newArrayList();
+
+        for (Project project : projects) {
+            projectIds.add(project.getId());
+            //  �빐�떦 �봽濡쒖젥�듃�뿉�꽌 �깮�꽦�릺�뒗 �떎�쓬 �씠�뒋 踰덊샇瑜� �깮�꽦�빐�삩�떎.
+            issueNumberMaps.put(project.getId(), this.issueNumberGeneratorService.generateIssueNumber(project));
+            projectMaps.put(project.getProjectKey(), project);
+
+            for (IssueTypeCustomField issueTypeCustomField : project.getIssueTypeCustomFields()) {
+                //  鍮좊Ⅴ寃� 李얘린 �쐞�빐 �씠�뒋 ���엯 �븘�씠�뵒 + �궗�슜�옄 �젙�쓽 �븘�뱶 �븘�씠�뵒瑜� �궎濡� �븳�떎.
+                String makeKey = issueTypeCustomField.getIssueType().getId().toString() + issueTypeCustomField.getCustomField().getId().toString();
+                issueTypeCustomFieldMaps.put(makeKey, issueTypeCustomField.getId());
+            }
+
+            //  �봽濡쒖젥�듃�뿉 李몄뿬�븯�뒗 �궗�슜�옄 �젙蹂�
+            List<Map<String, Object>> users = this.userService.findProjectMember(project);
+            Map<String, Object> userMap = new HashMap<>();
+            //  �궗�슜�옄 �젙蹂대�� Map �뿉 ���옣
+            for (Map<String, Object> user : users) {
+                userMap.put(CommonUtil.decryptAES128(MapUtil.getString(user, "account")), MapUtil.getLong(user, "userId"));
+            }
+
+            userMaps.put(project.getProjectKey(), userMap);
+        }
+
+        //  �씠�뒋 �쑀�삎�쓣 諛붾줈 李얠쓣 �닔 �엳寃� 以�鍮�
+        List<IssueType> issueTypes = this.issueTypeService.findByWorkspaceId();
+        for (IssueType issueType : issueTypes) {
+            issueTypeMaps.put(issueType.getName(), issueType);
+
+            IssueStatus issueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(issueType.getWorkflow());
+            issueStatusReadyMaps.put(issueType.getId().toString(), issueStatus);
+        }
+
+        //  �슦�꽑�닚�쐞瑜� 諛붾줈 李얠쓣 �닔 �엳寃� 以�鍮�
+        List<Priority> priorities = this.priorityService.findByWorkspaceId();
+        for (Priority priority : priorities) {
+            priorityMaps.put(priority.getName(), priority);
+        }
+
+        //  以묒슂�룄瑜� 諛붾줈 李얠쓣 �닔 �엳寃� 以�鍮�
+        List<Severity> severities = this.severityService.findByWorkspaceId();
+        for (Severity severity : severities) {
+            severityMaps.put(severity.getName(), severity);
+        }
+
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶瑜� 諛붾줈 李얠쓣 �닔 �엳寃� 以�鍮�
+        List<CustomField> customFields = this.customFieldService.findByWorkspaceId();
+        for (CustomField customField : customFields) {
+            customFieldMaps.put(customField.getName(), customField);
+        }
+    }
+
+    //  �뿊�� �븘�뱶�뿉 �엳�뒗 �젙蹂대�� �씠�뒋 form �쑝濡� �삷湲대떎.
+    private IssueForm setIssueFormToExcelField(Row row, int rowIndex, Map<String, IssueStatus> issueStatusReadyMaps, Map<String, Project> projectMaps, Map<String, IssueType> issueTypeMaps, Map<String,
+            Priority> priorityMaps, Map<String, Severity> severityMaps, Map<String, Object> userMaps, Map<String, CustomField> customFieldMaps, Map<Long, Long> issueNumberMaps, List<String> headers) {
+        IssueForm issueForm = new IssueForm();
+        issueForm.setRegisterId(this.webAppUtil.getLoginId());
+        Project project = null;
+
+        //  �젣紐�, �궡�슜, �봽濡쒖젥�듃 �궎, �씠�뒋 ���엯, �슦�꽑�닚�쐞, 以묒슂�룄, �떞�떦�옄, �떆�옉�씪, 醫낅즺�씪, �궗�슜�옄 �젙�쓽 �븘�뱶
+        for (int cellIndex = 0; cellIndex < headers.size(); cellIndex++) {
+            Cell cell = row.getCell(cellIndex);
+            switch (cellIndex) {
+                case 0:
+                    //  �씠�뒋 �젣紐⑹쓣 IssueForm �뿉 ���옣�븳�떎.
+                    this.setIssueFormTitle(cell, issueForm, rowIndex);
+                    break;
+
+                case 1:    //  �궡�슜
+                    if (cell != null) {
+                        issueForm.setDescription(CommonUtil.convertExcelStringToCell(cell));
+                    } else {
+                        //  null �엯�젰 諛⑹�
+                        issueForm.setDescription("");
+                    }
+
+                    break;
+
+                case 2:    //  �봽濡쒖젥�듃 �궎�� �씠�뒋 踰덊샇
+                    project = this.setIssueFormProjectKeyAndIssueNumber(cell, issueForm, projectMaps, issueNumberMaps, rowIndex);
+                    break;
+
+                case 3:
+                    //  �씠�뒋 ���엯�쓣 IssueForm �뿉 ���옣�븳�떎.
+                    this.setIssueFormIssueType(cell, issueTypeMaps, issueForm, rowIndex);
+                    //  �씠�뒋 ���엯�뿉 �뿰寃곕맂 �썙�겕�뵆濡쒖슦�쓽 �긽�깭 �냽�꽦 '��湲�' �씤 �긽�깭瑜� issueForm �뿉 ���옣�븳�떎.
+                    this.setIssueFormIssueStatus(issueStatusReadyMaps, issueForm, rowIndex);
+                    break;
+
+                case 4:
+                    //  �슦�꽑�닚�쐞瑜� IssueForm �뿉 ���옣�븳�떎.
+                    this.setIssueFormPriority(cell, priorityMaps, issueForm, rowIndex);
+                    break;
+
+                case 5:
+                    //  以묒슂�룄瑜� IssueForm �뿉 ���옣�븳�떎.
+                    this.setIssueFormSeverity(cell, severityMaps, issueForm, rowIndex);
+                    break;
+                case 6:
+                    //  �떞�떦�옄瑜� IssueForm �뿉 ���옣�븳�떎.
+                    this.setIssueFormAssignee(cell, userMaps, issueForm, project);
+                    break;
+                case 7:
+                    //  �떆�옉�씪�쓣 IssueForm �뿉 ���옣�븳�떎.
+                    this.setIssueFormPeriod(cell, issueForm, true, rowIndex);
+                    break;
+                case 8:
+                    //  醫낅즺�씪�쓣 IssueForm �뿉 ���옣�븳�떎.
+                    this.setIssueFormPeriod(cell, issueForm, false, rowIndex);
+                    break;
+                default:
+                    //  8踰� �씠�긽遺��꽣�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶. �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂대�� IssueForm �뿉 ���옣�븳�떎.
+                    this.setIssueFormCustomFieldValue(cell, customFieldMaps, issueForm, headers.get(cellIndex), rowIndex);
+            }
+        }
+
+        return issueForm;
+    }
+
+    //  �씠�뒋 �젣紐⑹쓣 IssueForm �뿉 ���옣�븳�떎.
+    private void setIssueFormTitle(Cell cell, IssueForm issueForm, int rowIndex) {
+        if (cell == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_ISSUE_TITLE_IS_NULL, rowIndex));
+        }
+
+        String title = CommonUtil.convertExcelStringToCell(cell);
+
+        //  �젣紐� �쑀�슚�꽦 泥댄겕
+        this.verifyTitle(title);
+        issueForm.setTitle(title);
+    }
+
+    //  �봽濡쒖젥�듃 �궎, �씠�뒋 怨좎쑀 踰덊샇, �떞�떦�옄瑜� IssueForm �뿉 ���옣�븳�떎.
+    private Project setIssueFormProjectKeyAndIssueNumber(Cell cell, IssueForm issueForm, Map<String, Project> projectMaps, Map<Long, Long> issueNumberMaps, int rowIndex) {
+        //  �봽濡쒖젥�듃 �븘�씠�뵒瑜� IssueForm �뿉 ���옣�븳�떎.
+        Project project = this.setIssueFormProject(cell, projectMaps, issueForm, rowIndex);
+
+        //  �씠�뒋 怨좎쑀 踰덊샇瑜� IssueForm �뿉 ���옣�븳�떎.
+        this.setIssueFormIssueNumber(issueForm, issueNumberMaps, project, rowIndex);
+
+        return project;
+    }
+
+    //  �봽濡쒖젥�듃 �븘�씠�뵒瑜� IssueForm �뿉 ���옣�븳�떎.
+    private Project setIssueFormProject(Cell cell, Map<String, Project> projectMaps, IssueForm issueForm, int rowIndex) {
+        if (cell == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PROJECT_KEY_IS_NULL, rowIndex));
+        }
+
+        Project project = projectMaps.get(CommonUtil.convertExcelStringToCell(cell).toUpperCase());
+
+        if (project == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PROJECT_NOT_EXIST, rowIndex));
+        }
+
+        issueForm.setProjectId(project.getId());
+
+        return project;
+    }
+
+    //  �씠�뒋 怨좎쑀 踰덊샇瑜� IssueForm �뿉 ���옣�븳�떎.
+    private void setIssueFormIssueNumber(IssueForm issueForm, Map<Long, Long> issueNumberMaps, Project project, int rowIndex) {
+        //  �씠�뒋 怨좎쑀 踰덊샇
+        Long issueNumber = issueNumberMaps.get(project.getId());
+
+        if (issueNumber == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_NUMBER_NOT_EXIST, rowIndex));
+        }
+
+        issueForm.setIssueNumber(issueNumber);
+        issueNumberMaps.put(project.getId(), ++issueNumber);  //  �씠�뒋 踰덊샇瑜� 1�뵫 利앷� �떆�궓�떎.
+    }
+
+    //  �씠�뒋 ���엯�쓣 IssueForm �뿉 ���옣�븳�떎.
+    private void setIssueFormIssueType(Cell cell, Map<String, IssueType> issueTypeMaps, IssueForm issueForm, int rowIndex) {
+        if (cell == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_ISSUE_TYPE_IS_NULL, rowIndex));
+        }
+
+        IssueType issueType = issueTypeMaps.get(CommonUtil.convertExcelStringToCell(cell));
+
+        if (issueType == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_ISSUE_TYPE_NOT_EXIST, rowIndex));
+        }
+
+        issueForm.setIssueTypeId(issueType.getId());
+    }
+
+    //  �씠�뒋 ���엯�뿉 �뿰寃곕맂 �썙�겕�뵆濡쒖슦�쓽 �긽�깭 �냽�꽦 '��湲�' �씤 �긽�깭瑜� issueForm �뿉 ���옣�븳�떎.
+    private void setIssueFormIssueStatus(Map<String, IssueStatus> issueStatusReadyMaps, IssueForm issueForm, int rowIndex) {
+        IssueStatus issueStatus = issueStatusReadyMaps.get(issueForm.getIssueTypeId().toString());
+
+        if (issueStatus == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_ISSUE_STATUS_READY_NOT_EXIST, rowIndex));
+        }
+
+        issueForm.setIssueStatusId(issueStatus.getId());
+    }
+
+    //  �슦�꽑�닚�쐞瑜� IssueForm �뿉 ���옣�븳�떎.
+    private void setIssueFormPriority(Cell cell, Map<String, Priority> priorityMaps, IssueForm issueForm, int rowIndex) {
+        if (cell == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PRIORITY_IS_NULL, rowIndex));
+        }
+
+        Priority priority = priorityMaps.get(CommonUtil.convertExcelStringToCell(cell));
+
+        if (priority == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PRIORITY_NOT_EXIST, rowIndex));
+        }
+
+        issueForm.setPriorityId(priority.getId());
+    }
+
+    //  以묒슂�룄瑜� IssueForm �뿉 ���옣�븳�떎.
+    private void setIssueFormSeverity(Cell cell, Map<String, Severity> severityMaps, IssueForm issueForm, int rowIndex) {
+        if (cell == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_SEVERITY_IS_NULL, rowIndex));
+        }
+
+        Severity severity = severityMaps.get(CommonUtil.convertExcelStringToCell(cell));
+
+        if (severity == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_SEVERITY_NOT_EXIST, rowIndex));
+        }
+
+        issueForm.setSeverityId(severity.getId());
+    }
+
+    //  �떞�떦�옄瑜� IssueForm �뿉 ���옣�븳�떎.
+    private void setIssueFormAssignee(Cell cell, Map<String, Object> userMaps, IssueForm issueForm, Project project) {
+        if (cell != null) {
+            String[] splitAssignee = CommonUtil.convertExcelStringToCell(cell).split("#");
+            Map<String, Object> userMap = (Map<String, Object>) MapUtil.getObject(userMaps, project.getProjectKey());
+
+            List<Long> userIds = Lists.newArrayList();
+
+            for (String account : splitAssignee) {
+                if (MapUtil.getLong(userMap, account) != null) {
+                    userIds.add(MapUtil.getLong(userMap, account));
+                }
+            }
+
+            issueForm.setUserIds(userIds);
+        }
+    }
+
+    //  �떆�옉�씪, 醫낅즺�씪�쓣 IssueForm �뿉 ���옣�븳�떎.
+    private void setIssueFormPeriod(Cell cell, IssueForm issueForm, Boolean checkStartDate, int rowIndex) {
+        if (cell != null && !cell.toString().equals("")) {
+
+            Date startDate;
+
+            try {
+                startDate = cell.getDateCellValue();
+            } catch (Exception e) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PERIOD_NOT_VALIDITY_EMPTY, rowIndex));
+            }
+
+            if (checkStartDate) {
+                issueForm.setStartDate(DateUtil.convertDateToStr(startDate, "yyyy-MM-dd"));
+            } else {
+                issueForm.setCompleteDate(DateUtil.convertDateToStr(startDate, "yyyy-MM-dd"));
+
+                try {
+                    //  �궇吏� �쑀�슚�꽦 泥댄겕
+                    this.checkStartCompleteDate(issueForm.getStartDate(), issueForm.getCompleteDate());
+                } catch (OwlRuntimeException e) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PERIOD_NOT_VALIDITY, rowIndex));
+                }
+            }
+        }
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂대�� IssueForm �뿉 ���옣�븳�떎.
+    private void setIssueFormCustomFieldValue(Cell cell, Map<String, CustomField> customFieldMaps, IssueForm issueForm, String customFieldName, int rowIndex) {
+        if (cell != null) {
+            String cellValue = CommonUtil.convertExcelStringToCell(cell);
+            Map<String, Object> issueCustomFieldMap = new HashMap<>();
+            CustomField customField = customFieldMaps.get(customFieldName);
+
+            if (customField == null) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_HEADER_CUSTOM_FIELD_NOT_EXIST, rowIndex));
+            }
+            //  �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪씠 怨듬갚�씠硫� 以묒�
+            if (StringUtils.isEmpty(cellValue)) {
+                return;
+            }
+
+            boolean validity = false;
+
+            switch (customField.getCustomFieldType()) {
+                case INPUT:
+                    if (cellValue.length() > 100) {
+                        throw new OwlRuntimeException(
+                                this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_TEXT_TYPE_MAX_LENGTH_OUT));
+                    }
+
+                    issueCustomFieldMap.put("customFieldId", customField.getId());
+                    issueCustomFieldMap.put("useValue", cellValue);
+                    issueForm.addIssueCustomFields(issueCustomFieldMap);
+                    break;
+                case SINGLE_SELECT:
+                    //  媛� �쑀�슚�꽦 泥댄겕
+                    for (CustomFieldValue customFieldValue : customField.getCustomFieldValues()) {
+                        if (customFieldValue.getValue().equals(cellValue)) {
+                            validity = true;
+                            break;
+                        }
+                    }
+
+                    if (!validity) {
+                        throw new OwlRuntimeException(
+                                this.messageAccessor.getMessage(MsgConstants.EXCEL_CUSTOM_FIELD_VALUE_NOT_VALIDITY, rowIndex));
+                    }
+
+                    issueCustomFieldMap.put("customFieldId", customField.getId());
+                    issueCustomFieldMap.put("useValue", cellValue);
+                    issueForm.addIssueCustomFields(issueCustomFieldMap);
+
+                    break;
+                case MULTI_SELECT:
+                    //  媛� �쑀�슚�꽦 泥댄겕
+                    String[] useValues = cellValue.split("#");
+                    //  �빐, �떖
+                    for (String useValue : useValues) {
+                        for (CustomFieldValue customFieldValue : customField.getCustomFieldValues()) {
+
+                            if (customFieldValue.getValue().equals(useValue)) {
+                                validity = true;
+                                Map<String, Object> multiValueMap = new HashMap<>();
+                                multiValueMap.put("customFieldId", customField.getId());
+                                multiValueMap.put("useValue", useValue);
+                                issueForm.addIssueCustomFields(multiValueMap);
+                            }
+
+                        }
+                    }
+
+                    if (!validity) {
+                        throw new OwlRuntimeException(
+                                this.messageAccessor.getMessage(MsgConstants.EXCEL_CUSTOM_FIELD_VALUE_NOT_VALIDITY, rowIndex));
+                    }
+
+                    break;
+            }
+        }
+    }
+
+    //  �봽濡쒖젥�듃�뿉 �엳�뒗 紐⑤뱺 �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<Long> findByProjectId(Long projectId) {
+        List<Map<String, Object>> issueMaps = this.issueMapper.findByProjectId(projectId);
+        List<Long> issueIds = Lists.newArrayList();
+
+        for (Map<String, Object> issueMap : issueMaps) {
+            Long issueId = MapUtil.getLong(issueMap, "id");
+            if (issueId != null) {
+                issueIds.add(issueId);
+            }
+        }
+
+        return issueIds;
+    }
+
+    //  �씠�뒋瑜� ���긽�옄�뱾�뿉寃� 硫붿씪濡� 諛쒖넚�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void sendIssueEmail(IssueForm issueForm) {
+        if (issueForm.getSendEmails().size() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_SEND_USER));
+        }
+
+        Issue issue = this.getIssue(issueForm.getId());
+
+        Map<String, Object> issueMap = new HashMap<>();
+        //  �씠�뒋 �젙蹂대�� �씠硫붿씪 �쟾�넚�뿉 �궗�슜�븯湲� �쐞�빐 Map �삎�깭濡� 蹂��솚�븳�떎.
+        this.makeIssueMapToIssue(issue, issueMap);
+        //  諛쒖떊�옄 �몴�떆
+        UserVo toUser = this.webAppUtil.getLoginUser();
+        issueMap.put("toUser", toUser.getName() + "(" + CommonUtil.decryptAES128(toUser.getAccount()) + ")");
+
+        // �씠�뒋 留곹겕
+        String projectKey = issue.getProject().getProjectKey();
+        Long IssueNumber = issue.getIssueNumber();
+        String link = this.configuration.getEmailSendUrl() + "/#/issues/issueList?projectKey=" + projectKey + "&issueNumber=" + IssueNumber.toString();
+
+        issueMap.put("issueLink", link);
+        issueMap.put("projectLink", link);
+
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_ANOTHER_USER_SEND_EMAIL));
+
+        this.systemEmailService.directEmail(issueForm.getSendEmails().toArray(new String[issueForm.getSendEmails().size()]), EmailType.ISSUE_SEND, issueMap, null);
+    }
+
+    //  �삁�빟 諛쒖깮 �씠�뒋瑜� �떎�뻾�븳�떎
+    @Override
+    @Transactional
+    public void reservationIssue() {
+        List<IssueReservation> issueReservations = this.issueReservationService.findByIssueReservationTypeNotNull();
+
+        Calendar calendar = Calendar.getInstance();
+        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
+        int month = calendar.get(Calendar.MONTH) + 1;
+        int date = calendar.get(Calendar.DATE);
+
+        for (IssueReservation issueReservation : issueReservations) {
+            switch (issueReservation.getIssueReservationType()) {
+                case DAY:
+                    //  �씠�뒋瑜� �떎�떆 �깮�꽦 �긽�깭濡� 蹂�寃쎌떆�궓�떎.
+                    this.occurReservationIssue(issueReservation.getIssue());
+                    break;
+                case WEEK:
+                    if (dayOfWeek == Integer.parseInt(issueReservation.getReservation())) {
+                        //  �씠�뒋瑜� �떎�떆 �깮�꽦 �긽�깭濡� 蹂�寃쎌떆�궓�떎.
+                        this.occurReservationIssue(issueReservation.getIssue());
+                    }
+
+                    break;
+                case MONTH:
+                    if (date == Integer.parseInt(issueReservation.getReservation())) {
+                        //  �씠�뒋瑜� �떎�떆 �깮�꽦 �긽�깭濡� 蹂�寃쎌떆�궓�떎.
+                        this.occurReservationIssue(issueReservation.getIssue());
+                    }
+
+                    break;
+                case YEAR:
+                    String[] reservations = issueReservation.getReservation().split("-");
+
+                    if (reservations.length > 1) {
+                        if ((Integer.parseInt(reservations[0]) == month) && Integer.parseInt(reservations[1]) == date) {
+                            //  �씠�뒋瑜� �떎�떆 �깮�꽦 �긽�깭濡� 蹂�寃쎌떆�궓�떎.
+                            this.occurReservationIssue(issueReservation.getIssue());
+                        }
+                    }
+
+                    break;
+            }
+        }
+    }
+
+    //  �씠�뒋瑜� �떎�떆 �깮�꽦 �긽�깭濡� 蹂�寃쎌떆�궓�떎.
+    private void occurReservationIssue(Issue issue) {
+        if (!issue.getIssueStatus().getIssueStatusType().equals(IssueStatusType.READY)) {
+            //  �씠�뒋瑜� �깮�꽦 �긽�깭濡� 蹂�寃쎌떆�궓�떎.
+            IssueStatus issueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(issue.getIssueType().getWorkflow());
+
+            if (issueStatus != null) {
+                StringBuilder detectIssueChange = new StringBuilder();
+                //  �삁�빟 諛쒖깮�쑝濡� �씠�뒋 �긽�깭 蹂�寃쎈맂 �젙蹂대�� 湲곕줉�븳�떎.
+                this.issueHistoryService.detectReservationIssueStatus(issue, detectIssueChange, issueStatus);
+                issue.setIssueStatus(issueStatus);
+                this.issueRepository.saveAndFlush(issue);
+                //  �씠�뒋 �긽�깭 蹂�寃� �젙蹂대�� �씠�젰�쑝濡� �궓湲대떎.
+                this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, detectIssueChange.toString());
+                //  �씠�뒋 踰꾩쟾 �깮�꽦
+                this.issueVersionService.addIssueVersion(issue);
+            }
+        }
+    }
+
+    @Autowired
+    private ProjectMapper projectMapper;
+
+    @Override
+    @Transactional(readOnly = true)
+    public Map<String, Object> findTask(IssueCondition taskCondition) {
+
+        StopWatch serviceStart = new StopWatch();
+        serviceStart.start();
+        ProjectCondition projectCondition = new ProjectCondition(this.webAppUtil.getLoginUser().getLastProjectId(), this.webAppUtil.getLoginId());
+        List<Map<String, Object>> checkProjectRole = this.projectMapper.checkIncludeProject(projectCondition);
+
+        if (checkProjectRole.size() < 1) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.PROJECT_NOT_INCLUDE_USER));
+        }
+
+        //  �씠以묒쑝濡� �뿭�븷 泥댄겕瑜� �빐�꽌 李몄뿬�븯吏� �븡�� �궗�슜�옄媛� �빐�떦 �봽濡쒖젥�듃�뿉 �젒洹� 紐삵븯�룄濡� �븳�떎.
+        taskCondition.setLoginUserId(this.webAppUtil.getLoginId());
+//        List<Map<String, Object>> results = this.taskMapper.find(taskCondition);
+        List<Map<String, Object>> results = this.issueMapper.find(taskCondition);
+
+        serviceStart.stop();
+        long executeTime = serviceStart.getTime();
+
+        Map<String, Object> tasks = new HashMap<>();
+        //  �썙�겕�뵆濡쒖슦 �긽�깭 id 瑜� �궎濡� 留뚮뱺�떎.
+        for (Long workflowStatusId : taskCondition.getStatusIds()) {
+            tasks.put(workflowStatusId.toString(), Lists.newArrayList());
+        }
+
+        Map<String, Object> taskUserSave = new HashMap<>();
+        //  �쟾泥� task_id瑜� 紐⑥��떎.
+        for (Map<String, Object> result : results) {
+            Long taskId = MapUtil.getLong(result, "id");
+            taskCondition.addIssueIds(taskId.toString());
+            //  媛� task_id 蹂꾨줈 �궗�슜�옄 �젙蹂대�� ���옣�븷 怨듦컙�쓣 誘몃━ �솗蹂댄븳�떎.
+            taskUserSave.put(taskId.toString(), Lists.newArrayList());
+        }
+
+        List<Map<String, Object>> taskUsers = Lists.newArrayList();
+        //  task 媛� �븯�굹�룄 �뾾�쓣 寃쎌슦�뿉�뒗 議고쉶瑜� �븯吏� �븡�뒗�떎.
+        if (!taskCondition.getIssueIds().isEmpty()) {
+            taskUsers = this.issueMapper.getAllTaskUser(taskCondition);
+        }
+
+        //  task_id �뿉 留ㅼ묶�릺�뒗 �떞�떦�옄 �젙蹂대�� 以�鍮꾪븳�떎.
+        for (Map<String, Object> taskUser : taskUsers) {
+            Long taskId = MapUtil.getLong(taskUser, "taskId");
+            List<UserVo> userVos = (List<UserVo>)taskUserSave.get(taskId.toString());
+            userVos.add(ConvertUtil.convertMapToClass(taskUser, UserVo.class));
+        }
+
+        for (Map<String, Object> result : results) {
+            IssueVo taskVo = ConvertUtil.convertMapToClass(result, IssueVo.class);
+
+            //  以묒슂�룄 �뀑�똿
+            if (MapUtil.getLong(result, "priorityId") != null) {
+                PriorityVo priorityVo = new PriorityVo();
+                priorityVo.setId(MapUtil.getLong(result, "priorityId"));
+                priorityVo.setName(MapUtil.getString(result, "priorityName"));
+                priorityVo.setColor(MapUtil.getString(result, "priorityColor"));
+                taskVo.setPriorityVo(priorityVo);
+            }
+
+            //  �썙�겕�뵆濡쒖슦 �긽�깭 �뀑�똿
+            if (MapUtil.getLong(result, "workflowStatusId") != null) {
+                WorkflowStatusVo workflowStatusVo = new WorkflowStatusVo();
+                workflowStatusVo.setId(MapUtil.getLong(result, "workflowStatusId"));
+                workflowStatusVo.setName(MapUtil.getString(result, "workflowStatusName"));
+                workflowStatusVo.setColor(MapUtil.getString(result, "workflowStatusColor"));
+                taskVo.setWorkflowStatusVo(workflowStatusVo);
+            }
+
+            //  �떞�떦�옄 �뀑�똿
+            List<UserVo> userVos =  (List<UserVo>)taskUserSave.get(taskVo.getId().toString());
+            taskVo.setUserVos(userVos);
+
+            List<IssueVo> taskVos = (List<IssueVo>)tasks.get(MapUtil.getString(result, "workflowStatusId"));
+            taskVos.add(taskVo);
+            tasks.put(MapUtil.getString(result, "workflowStatusId"), taskVos);
+        }
+
+        /*serviceStart.stop();
+        long executeTime = serviceStart.getTime();*/
+
+        return tasks;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueStatusServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueStatusServiceImpl.java
new file mode 100644
index 0000000..0b36fc8
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueStatusServiceImpl.java
@@ -0,0 +1,570 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.common.ExcelConditionCheck;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.IssueStatusType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.IssueStatusMapper;
+import kr.wisestone.owl.repository.IssueStatusRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.vo.*;
+import kr.wisestone.owl.web.condition.IssueStatusCondition;
+import kr.wisestone.owl.web.form.IssueStatusForm;
+import kr.wisestone.owl.web.view.ExcelView;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.*;
+
+@Service
+public class IssueStatusServiceImpl extends AbstractServiceImpl<IssueStatus, Long, JpaRepository<IssueStatus, Long>> implements IssueStatusService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueStatusServiceImpl.class);
+
+    @Autowired
+    private IssueStatusRepository issueStatusRepository;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private WorkflowTransitionService workflowTransitionService;
+
+    @Autowired
+    private IssueTypeService issueTypeService;
+
+    @Autowired
+    private IssueStatusMapper issueStatusMapper;
+
+    @Autowired
+    private IssueService issueService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private ExcelView excelView;
+
+    @Autowired
+    private ExcelConditionCheck excelConditionCheck;
+
+    @Override
+    protected JpaRepository<IssueStatus, Long> getRepository() {
+        return this.issueStatusRepository;
+    }
+
+    //  �썙�겕�뒪�럹�씠�뒪 �깮�꽦�떆 �깮�꽦�릺�뒗 �씠�뒋 �긽�깭 - �봽濡쒖젥�듃 �쑀�삎�뿉 �뵲�씪 �깮�꽦�릺�뒗 �씠�뒋 �긽�깭媛� �떖�씪吏꾨떎.
+    @Override
+    @Transactional
+    public List<IssueStatus> addDefaultIssueStatus(Workspace workspace) {
+        List<IssueStatus> issueStatuses = Lists.newArrayList();
+        issueStatuses.add(new IssueStatus(workspace, this.messageAccessor.message("common.create"), Boolean.TRUE, "#665fff", IssueStatusType.READY, 1L)); // �깮�꽦
+        issueStatuses.add(new IssueStatus(workspace, this.messageAccessor.message("common.progress"), Boolean.TRUE, "#98c220", IssueStatusType.OPEN, 2L)); // 吏꾪뻾
+        issueStatuses.add(new IssueStatus(workspace, this.messageAccessor.message("common.reProgress"), Boolean.TRUE, "#c940ea", IssueStatusType.OPEN, 3L)); // �옱吏꾪뻾
+        issueStatuses.add(new IssueStatus(workspace, this.messageAccessor.message("common.check"), Boolean.TRUE, "#febd35", IssueStatusType.OPEN, 4L)); // �솗�씤
+        issueStatuses.add(new IssueStatus(workspace, this.messageAccessor.message("common.end"), Boolean.TRUE, "#888888", IssueStatusType.CLOSE, 5L)); // 醫낅즺
+        issueStatuses.add(new IssueStatus(workspace, this.messageAccessor.message("common.noApproval"), Boolean.TRUE, "#ff5f99", IssueStatusType.CLOSE, 6L)); // �듅�씤 遺덇�
+        issueStatuses.add(new IssueStatus(workspace, this.messageAccessor.message("common.approval"), Boolean.TRUE, "#3598fe", IssueStatusType.CLOSE, 7L)); // �듅�씤
+
+        return this.issueStatusRepository.saveAll(issueStatuses);
+    }
+
+    //  �썙�겕�뒪�럹�씠�뒪�뿉 議댁옱�븯�뒗 �씠�뒋 �긽�깭 議고쉶
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueStatus> findByWorkspaceId(Long workspaceId) {
+        return this.issueStatusRepository.findByWorkspaceId(workspaceId);
+    }
+
+    //  �씠�뒋 �긽�깭瑜� �깮�꽦�븳�떎.
+    @Override
+    @Transactional
+    public IssueStatus addIssueStatus(IssueStatusForm issueStatusForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        //  �씠由� �쑀�슚�꽦 泥댄겕
+        this.verifyName(issueStatusForm.getName(), null);
+        //  �깋�긽 泥댄겕
+        this.verifyColor(issueStatusForm.getColor());
+
+        IssueStatus issueStatus = ConvertUtil.copyProperties(issueStatusForm, IssueStatus.class, "issueStatusType");
+        issueStatus.setIssueStatusType(IssueStatusType.valueOf(issueStatusForm.getIssueStatusType()));
+        issueStatus.setWorkspace(this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId()));
+        issueStatus.setPosition(0L);
+
+        return this.issueStatusRepository.saveAndFlush(issueStatus);
+    }
+
+    //  �씠由� �쑀�슚�꽦 泥댄겕
+    private void verifyName(String name, Long id) {
+        if (StringUtils.isEmpty(name)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_NOT_NAME));
+        }
+
+        if (name.length() > 20) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_NAME_MAX_LENGTH_OUT));
+        }
+
+        IssueStatus issueStatus;
+        Long workspaceId = this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId();
+
+        if (id == null) {
+            issueStatus = this.issueStatusRepository.findByNameAndWorkspaceId(name, workspaceId);
+        }
+        else {
+            issueStatus = this.issueStatusRepository.findByNameAndWorkspaceIdAndIdNot(name, workspaceId, id);
+        }
+
+        if (issueStatus != null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_USED_NAME));
+        }
+    }
+
+    //  �깋�긽 泥댄겕
+    private void verifyColor(String color) {
+        if (StringUtils.isEmpty(color)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_NOT_COLOR));
+        }
+    }
+
+    //  �씠�뒋 �긽�깭 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueStatusVo> findIssueStatus(Map<String, Object> resJsonData,
+                                               IssueStatusCondition condition, Pageable pageable) {
+
+        condition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        condition.setPageSize(pageable.getPageSize());
+        condition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+
+        List<Map<String, Object>> results = this.issueStatusMapper.find(condition);
+        Long totalCount = this.issueStatusMapper.count(condition);
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        List<IssueStatusVo> issueStatusVos = ConvertUtil.convertListToListClass(results, IssueStatusVo.class);
+
+        //  �씠�뒋 �긽�깭媛� �썙�겕�뵆濡쒖슦�뿉 �궗�슜�릺怨� �엳�뒗吏� �뿬遺�瑜� 泥댄겕�븯怨� �씠�뒋 �긽�깭 �븞�뿉 �궗�슜�릺�뒗 �썙�겕�뵆濡쒖슦 �젙蹂대�� �뀑�똿�븳�떎.
+        if (condition.getDeep() != null) {
+            this.setUseIssueStatusByWorkflow(issueStatusVos);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueStatusVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        return issueStatusVos;
+    }
+
+    //  �씠�뒋 �긽�깭 紐⑸줉�쓣 議고쉶�븳�떎 - �럹�씠吏� �궗�슜x �썙�겕�뵆濡쒖슦 �깮�꽦, �닔�젙�뿉�꽌 �궗�슜
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueStatusVo> findIssueStatus(Map<String, Object> resJsonData, IssueStatusCondition condition) {
+        condition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        List<Map<String, Object>> results = this.issueStatusMapper.find(condition);
+        List<IssueStatusVo> issueStatusVos = ConvertUtil.convertListToListClass(results, IssueStatusVo.class);
+        //  issueStatusType 蹂꾨줈 �젙�젹
+        Collections.sort(issueStatusVos);
+        //  ready �긽�깭媛� �븵�쑝濡� �삤�룄濡� �떎�떆 �젙�젹
+        Collections.reverse(issueStatusVos);
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueStatusVos);
+        return issueStatusVos;
+    }
+
+    //  �씠�뒋 �긽�깭瑜� �궗�슜�븯怨� �엳�뒗 �썙�겕�뵆濡쒖슦 �젙蹂대�� �뀑�똿�븳�떎.
+    private void setUseIssueStatusByWorkflow(List<IssueStatusVo> issueStatusVos) {
+        for (IssueStatusVo issueStatusVo : issueStatusVos) {
+            Map<String, Object> workflows = new HashMap<>();
+            IssueStatus issueStatus = this.getIssueStatus(issueStatusVo.getId());
+            this.findUseIssueStatusByWorkflow(issueStatus.getSourceWorkflowTransitions().iterator(), workflows);
+            this.findUseIssueStatusByWorkflow(issueStatus.getTargetWorkflowTransitions().iterator(), workflows);
+
+            for (String key : workflows.keySet()) {
+                issueStatusVo.addWorkflowVos((WorkflowVo) workflows.get(key));
+            }
+        }
+    }
+
+    //  �씠�뒋 �긽�깭瑜� �궗�슜�븯怨� �엳�뒗 �썙�겕�뵆濡쒖슦瑜� 李얜뒗�떎.
+    private void findUseIssueStatusByWorkflow(Iterator<WorkflowTransition> iterator, Map<String, Object> workflows) {
+        while (iterator.hasNext()) {
+            WorkflowTransition workflowTransition = iterator.next();
+            workflows.put(workflowTransition.getWorkflow().getId().toString(), ConvertUtil.copyProperties(workflowTransition.getWorkflow(), WorkflowVo.class));
+        }
+    }
+
+    //  �씠�뒋�쓽 �쁽�옱 �긽�깭�뿉�꽌 �씠�룞 媛��뒫�븳 �떎�쓬 �긽�깭 �젙蹂� 紐⑸줉�쓣 李얜뒗�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findNextIssueStatus(Map<String, Object> resJsonData, IssueStatusCondition condition) {
+        IssueType issueType = this.issueTypeService.getIssueType(condition.getIssueTypeId());
+        IssueStatus issueStatus = this.getIssueStatus(condition.getId());
+        Workflow workflow = issueType.getWorkflow();
+
+        List<WorkflowTransitionVo> workflowTransitionVos = this.workflowTransitionService.findBySourceIssueStatusIdAndWorkflowId(issueStatus.getId(), workflow.getId());
+        List<IssueStatusVo> issueStatusVos = Lists.newArrayList();
+
+        for (WorkflowTransitionVo workflowTransitionVo : workflowTransitionVos) {
+            IssueStatusVo issueStatusVo = new IssueStatusVo();
+            issueStatusVo.setId(workflowTransitionVo.getTargetStatusId());
+            issueStatusVo.setName(workflowTransitionVo.getTargetStatusName());
+            issueStatusVos.add(issueStatusVo);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueStatusVos);
+    }
+
+
+    //  �씠�뒋 �긽�깭 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailIssueStatus(Map<String, Object> resJsonData, IssueStatusCondition issueStatusCondition) {
+        IssueStatusVo issueStatusVo = new IssueStatusVo();
+
+        if (issueStatusCondition.getId() != null) {
+            IssueStatus issueStatus = this.getIssueStatus(issueStatusCondition.getId());
+            issueStatusVo = ConvertUtil.copyProperties(issueStatus, IssueStatusVo.class);
+            issueStatusVo.setIssueStatusType(issueStatus.getIssueStatusType().toString());
+
+            if (issueStatusCondition.getDeep().equals("01")) {
+                int useCount = issueStatus.getSourceWorkflowTransitions().size() + issueStatus.getTargetWorkflowTransitions().size();
+
+                if (useCount > 0) {
+                    issueStatusVo.setUseYn(true);
+                }
+            }
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueStatusVo);
+    }
+
+    //  �씠�뒋 �긽�깭 �젙蹂대�� �닔�젙�븳�떎.
+    @Override
+    @Transactional
+    public IssueStatus modifyIssueStatus(IssueStatusForm issueStatusForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+        //  �씠由� �쑀�슚�꽦 泥댄겕
+        this.verifyName(issueStatusForm.getName(), issueStatusForm.getId());
+        //  �깋�긽 泥댄겕
+        this.verifyColor(issueStatusForm.getColor());
+
+        IssueStatus issueStatus = this.getIssueStatus(issueStatusForm.getId());
+        ConvertUtil.copyProperties(issueStatusForm, issueStatus, "id", "issueStatusType");
+        issueStatus.setIssueStatusType(IssueStatusType.valueOf(issueStatusForm.getIssueStatusType()));
+
+        return this.issueStatusRepository.saveAndFlush(issueStatus);
+    }
+
+    //  �씠�뒋 �긽�깭 �븘�씠�뵒濡� �씠�뒋 �긽�깭瑜� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public IssueStatus getIssueStatus(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_NOT_EXIST));
+        }
+
+        IssueStatus issueStatus = this.findOne(id);
+
+        if (issueStatus == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_NOT_EXIST));
+        }
+
+        return issueStatus;
+    }
+
+    //  �씠�뒋 �긽�깭瑜� �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void removeIssueStatus(IssueStatusForm issueStatusForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        if (issueStatusForm.getRemoveIds().size() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_REMOVE_NOT_SELECT));
+        }
+
+        for (Long issueStatusId : issueStatusForm.getRemoveIds()) {
+            this.removeIssueStatuses(issueStatusId);
+        }
+
+        this.issueStatusRepository.flush();
+    }
+
+    private void removeIssueStatuses(Long issueStatusId) {
+        IssueStatus issueStatus = this.getIssueStatus(issueStatusId);
+
+        //  �궘�젣�븷 �씠�뒋 �긽�깭媛� �씠�뒋 �뿉�꽌 �궗�슜�릺怨� �엳�뒗吏� �솗�씤�븳�떎.
+        this.checkUseIssueStatus(issueStatus);
+        //  �궘�젣�븷 �씠�뒋 �긽�깭媛� �썙�겕�뵆濡쒖슦 �뿉�꽌 �궗�슜�릺怨� �엳�뒗吏� �솗�씤�븳�떎.
+        this.checkUseWorkflow(issueStatus);
+
+        //  湲곕낯�쑝濡� �젣怨듬릺�뒗 �씠�뒋 �긽�깭�뒗 �궘�젣 湲덉�
+        if (issueStatus.getDefaultYn()) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.DEFAULT_ISSUE_STATUS_NOT_REMOVE));
+        }
+
+        this.issueStatusRepository.delete(issueStatus);
+    }
+
+    //  �궘�젣�븷 �씠�뒋 �긽�깭媛� �씠�뒋 �뿉�꽌 �궗�슜�릺怨� �엳�뒗吏� �솗�씤�븳�떎.
+    private void checkUseIssueStatus(IssueStatus issueStatus) {
+        //  �씠�뒋 �쑀�삎�쓣 �궗�슜�븯�뒗 �씠�뒋 媛��닔瑜� 議고쉶�븳�떎.
+        long issueCount = this.issueService.countByIssueStatus(issueStatus.getId());
+
+        if (issueCount > 0) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_USE_ISSUES));
+        }
+    }
+
+    //  �궘�젣�븷 �씠�뒋 �긽�깭媛� �썙�겕�뵆濡쒖슦 �뿉�꽌 �궗�슜�릺怨� �엳�뒗吏� �솗�씤�븳�떎.
+    private void checkUseWorkflow(IssueStatus issueStatus) {
+        List<IssueStatusVo> issueStatusVos = Lists.newArrayList(ConvertUtil.copyProperties(issueStatus, IssueStatusVo.class));
+        //  �씠�뒋 �긽�깭瑜� �궗�슜�븯怨� �엳�뒗 �썙�겕�뵆濡쒖슦 �젙蹂대�� �뀑�똿�븳�떎.
+        this.setUseIssueStatusByWorkflow(issueStatusVos);
+
+        for (IssueStatusVo issueStatusVo : issueStatusVos) {
+            if (issueStatusVo.getWorkflowVos().size() > 0) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_USE_WORKFLOW));
+            }
+        }
+    }
+
+    //  �썙�겕�뵆濡쒖슦�뿉 �엳�뒗 �씠�뒋 �긽�깭瑜� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueStatusVo> findByWorkflowId(Long workflowId) {
+        List<WorkflowTransition> workflowTransitions = this.workflowTransitionService.findByWorkflowId(workflowId);
+
+        Map<String, Object> issueStatuses = new HashMap<>();
+        List<IssueStatusVo> issueStatusVos = Lists.newArrayList();
+
+        for (WorkflowTransition workflowTransition : workflowTransitions) {
+            IssueStatus source = workflowTransition.getSourceIssueStatus();
+            IssueStatus target = workflowTransition.getTargetIssueStatus();
+
+            //  ���긽 �씠�뒋 �긽�깭媛� �씠誘� ���옣�릺�뼱 �엳�뒗吏� �솗�씤�븳�떎. - source �빆紐�
+            if (source != null) {
+                this.setIssueStatusLocation(issueStatuses, source, workflowTransition.getSourceX(), workflowTransition.getSourceY());
+            }
+
+            //  ���긽 �씠�뒋 �긽�깭媛� �씠誘� ���옣�릺�뼱 �엳�뒗吏� �솗�씤�븳�떎. - target �빆紐�
+            if (target != null) {
+                this.setIssueStatusLocation(issueStatuses, target, workflowTransition.getTargetX(), workflowTransition.getTargetY());
+            }
+        }
+
+        Iterator<String> iterator = issueStatuses.keySet().iterator();
+
+        while (iterator.hasNext()) {
+            IssueStatusVo issueStatusVo = (IssueStatusVo) issueStatuses.get(iterator.next());
+            issueStatusVo.setWorkflowTransitionVos(this.workflowTransitionService.findBySourceIssueStatusIdAndWorkflowId(issueStatusVo.getId(), workflowId));
+            issueStatusVos.add(issueStatusVo);
+        }
+
+        return issueStatusVos;
+    }
+
+
+    private void setIssueStatusLocation(Map<String, Object> issueStatuses, IssueStatus issueStatus, Long xLocation, Long yLocation) {
+        Object targetIssueStatus = MapUtil.getObject(issueStatuses, issueStatus.getId().toString());
+
+        if (targetIssueStatus == null) {
+            IssueStatusVo issueStatusVo = ConvertUtil.copyProperties(issueStatus, IssueStatusVo.class);
+            issueStatusVo.setxLocation(xLocation);
+            issueStatusVo.setyLocation(yLocation);
+            issueStatusVo.setIssueStatusType(issueStatus.getIssueStatusType().toString());
+
+            issueStatuses.put(issueStatus.getId().toString(), issueStatusVo);
+        }
+    }
+
+    //  �씠�뒋 �긽�깭 �냽�꽦�씠 ��湲곗씤 �씠�뒋 �긽�깭瑜� 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public IssueStatus findByIssueStatusTypeIsReady(Workflow workflow) {
+        List<IssueStatusVo> issueStatusVos = this.findByWorkflowId(workflow.getId());
+        IssueStatus issueStatus = null;
+
+        for (IssueStatusVo issueStatusVo : issueStatusVos) {
+            if (IssueStatusType.READY.equals(IssueStatusType.valueOf(issueStatusVo.getIssueStatusType()))) {
+                issueStatus = this.getIssueStatus(issueStatusVo.getId());
+                break;
+            }
+        }
+
+        if (issueStatus == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.READY_ISSUE_STATUS_NOT_EXIST));
+        }
+
+        return issueStatus;
+    }
+
+    //  �씠�뒋 �긽�깭瑜� 蹂�寃쏀븷 �븣 �꽑�깮�븳 �씠�뒋 �긽�깭濡� 蹂�寃쏀븷 �닔 �엳�뒗吏� �솗�씤�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void checkNextIssueStatus(Issue issue, IssueStatus nextIssueStatus) {
+        Workflow workflow = issue.getIssueType().getWorkflow();
+        boolean passNextIssueStatus = false;
+
+        List<WorkflowTransitionVo> workflowTransitionVos = this.workflowTransitionService.findBySourceIssueStatusIdAndWorkflowId(issue.getIssueStatus().getId(), workflow.getId());
+
+        for (WorkflowTransitionVo workflowTransitionVo : workflowTransitionVos) {
+            if (workflowTransitionVo.getTargetStatusId().equals(nextIssueStatus.getId())) {
+                passNextIssueStatus = true;
+                break;
+            }
+        }
+
+        if (!passNextIssueStatus) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_CHANGE_NOT_TARGET));
+        }
+    }
+
+    //  �씠�뒋 �긽�깭 紐⑸줉�쓣 �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    @Override
+    @Transactional
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        //  �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븯怨� 鍮꾪솢�꽦�씪 寃쎌슦 �뿊�� �떎�슫濡쒕뱶瑜� 湲덉��븳�떎.
+        ModelAndView modelAndView = this.workspaceService.checkUseExcelDownload(model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        Map<String, Object> conditions = new HashMap<>();
+        //  �뿊�� �떎�슫濡쒕뱶�뿉 �븘�슂�븳 寃��깋 議곌굔 �젙蹂대�� 異붿텧�븯怨� 寃��깋 議곌굔 異붿텧�뿉 �삤瑜섍� 諛쒖깮�븯硫� 寃쎄퀬瑜� �몴�떆�빐以��떎.
+        modelAndView = this.excelConditionCheck.checkCondition(conditions, request, model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        IssueStatusCondition issueStatusCondition = IssueStatusCondition.make(conditions);
+        issueStatusCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        List<Map<String, Object>> results = this.issueStatusMapper.find(issueStatusCondition);
+        List<IssueStatusVo> issueStatusVos = ConvertUtil.convertListToListClass(results, IssueStatusVo.class);
+
+        //  �씠�뒋 �긽�깭媛� �썙�겕�뵆濡쒖슦�뿉 �궗�슜�릺怨� �엳�뒗吏� �뿬遺�瑜� 泥댄겕�븯怨� �씠�뒋 �긽�깭 �븞�뿉 �궗�슜�릺�뒗 �썙�겕�뵆濡쒖슦 �젙蹂대�� �뀑�똿�븳�떎.
+        if (issueStatusCondition.getDeep() != null) {
+            this.setUseIssueStatusByWorkflow(issueStatusVos);
+        }
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.issueStatusList")); // �씠�뒋 �긽�깭 紐⑸줉
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("name", this.messageAccessor.message("common.issueStatus"), 10, ExportExcelAttrVo.ALIGN_LEFT)); // �씠�뒋 �긽�깭
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueStatusType", this.messageAccessor.message("common.statusProperties"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �긽�깭 �냽�꽦
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("color", this.messageAccessor.message("common.color"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �깋�긽
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("workflowNames", this.messageAccessor.message("common.workflow"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // �썙�겕�뵆濡쒖슦
+        //  �뿊���뿉 �꽔�쓣 �뜲�씠�꽣 - issueStatusVos �뜲�씠�꽣瑜� �뿊���뿉�꽌 �몴�떆�븷 �닔 �엳�뒗 �뜲�씠�꽣濡� 蹂�寃쏀븳�떎.
+        excelInfo.setDatas(this.convertExcelViewToIssueStatusVos(issueStatusVos));
+        model.addAttribute(Constants.EXCEL, excelInfo);
+        return new ModelAndView(this.excelView);
+    }
+
+    //  IssueStatusVo �뜲�씠�꽣瑜� �뿊���뿉�꽌 �몴�떆�븷 �닔 �엳�뒗 �뜲�씠�꽣濡� 蹂�寃쏀븳�떎.
+    private List<Map<String, String>> convertExcelViewToIssueStatusVos(List<IssueStatusVo> issueStatusVos) {
+        List<Map<String, String>> results = Lists.newArrayList();
+
+        for (IssueStatusVo issueStatusVo : issueStatusVos) {
+            Map<String, String> result = new HashMap<>();
+
+            result.put("name", issueStatusVo.getName());
+
+            String issueStatusType = "";
+            switch (issueStatusVo.getIssueStatusType()) {
+                case "READY":
+                    issueStatusType = this.messageAccessor.message("common.wait"); // ��湲�
+                    break;
+                case "OPEN":
+                    issueStatusType = this.messageAccessor.message("common.progress"); // 吏꾪뻾
+                    break;
+                case "CLOSE":
+                    issueStatusType = this.messageAccessor.message("common.end"); // 醫낅즺
+                    break;
+            }
+
+            result.put("issueStatusType", issueStatusType);
+            result.put("color", issueStatusVo.getColor());
+
+            StringBuilder stringBuilder = new StringBuilder();
+
+            for (WorkflowVo workflowVo : issueStatusVo.getWorkflowVos()) {
+                stringBuilder.append(workflowVo.getName());
+                stringBuilder.append("\n");
+            }
+
+            result.put("workflowNames", stringBuilder.toString());
+            results.add(result);
+        }
+
+        return results;
+    }
+
+    //  �뿬�윭嫄댁쓽 �씠�뒋�쓽 �쁽�옱 �긽�깭�뿉�꽌 �씠�룞 媛��뒫�븳 �떎�쓬 �긽�깭 �젙蹂� 紐⑸줉�쓣 李얜뒗�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findNextMultiIssueStatus(Map<String, Object> resJsonData, IssueStatusCondition issueStatusCondition) {
+        List<IssueStatusVo> issueStatusVos = Lists.newArrayList();
+        int count = 0;
+
+        //  �썙�겕�뵆濡쒖슦�뿉�꽌 �씠�룞 媛��뒫�븳 �긽�깭 異붿텧
+        for (Long issueId : issueStatusCondition.getIssueIds()) {
+            Issue issue = this.issueService.getIssue(issueId);
+            Workflow workflow = issue.getIssueType().getWorkflow();
+            List<WorkflowTransitionVo> workflowTransitionVos = this.workflowTransitionService.findBySourceIssueStatusIdAndWorkflowId(issue.getIssueStatus().getId(), workflow.getId());
+
+            List<IssueStatusVo> tempIssueStatusVos = Lists.newArrayList();
+
+            for (WorkflowTransitionVo workflowTransitionVo : workflowTransitionVos) {
+                //  泥ル쾲吏� �씠�뒋�뿉�꽌 �씠�룞 媛��뒫�븳 �긽�깭 �젙蹂대�� ���옣�븳�떎.
+                if (count < 1) {
+                    IssueStatusVo issueStatusVo = new IssueStatusVo(workflowTransitionVo.getTargetStatusId(), workflowTransitionVo.getTargetStatusName());
+                    issueStatusVos.add(issueStatusVo);
+                }
+                else {
+                    //  �몢踰덉㎏ �씠�뒋遺��꽣 泥ル쾲吏� �씠�뒋�뿉�꽌 �씠�룞 媛��뒫�뻽�뜕 �긽�깭 以� �뾾�뒗 ���긽�쓣 李얜뒗�떎.
+                    for (IssueStatusVo issueStatusVo : issueStatusVos) {
+                        if (issueStatusVo.getId().equals(workflowTransitionVo.getTargetStatusId())) {
+                            tempIssueStatusVos.add(issueStatusVo);
+                        }
+                    }
+                }
+            }
+
+            if (count > 0) {
+                //  鍮꾧탳�븳 �썑 議댁옱�븯�뒗 �씠�뒋 �긽�깭留� ���옣
+                issueStatusVos = tempIssueStatusVos;
+            }
+
+            count++;
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueStatusVos);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueTableConfigServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueTableConfigServiceImpl.java
new file mode 100644
index 0000000..a404a90
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueTableConfigServiceImpl.java
@@ -0,0 +1,89 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.IssueTableConfig;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.repository.IssueTableConfigRepository;
+import kr.wisestone.owl.service.IssueTableConfigService;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.service.WorkspaceService;
+import kr.wisestone.owl.util.MapUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Map;
+
+@Service
+public class IssueTableConfigServiceImpl extends AbstractServiceImpl<IssueTableConfig, Long, JpaRepository<IssueTableConfig, Long>> implements IssueTableConfigService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueTableConfigServiceImpl.class);
+
+    @Autowired
+    private IssueTableConfigRepository issueTableConfigRepository;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    protected JpaRepository<IssueTableConfig, Long> getRepository() {
+        return this.issueTableConfigRepository;
+    }
+
+    //  �씠�뒋 �뀒�씠釉� 而щ읆 �꽕�젙 �젙蹂대�� ���옣�븳�떎.
+    @Override
+    @Transactional
+    public IssueTableConfig addIssueTableConfig(Map<String, Object> params) {
+        String issueTableConfigs = MapUtil.getString(params, "issueTableConfigs");
+        //  �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 �궗�슜�옄�쓽 �씠�뒋 紐⑸줉 �뀒�씠釉� 而щ읆 �꽕�젙�쓣 議고쉶�븳�떎.
+        IssueTableConfig saveIssueTableConfig = this.findByUserIdAndWorkspaceId();
+
+        //  �븘吏� �뀒�씠釉� 而щ읆 �꽕�젙�쓣 �븯吏� �븡�븯�쓣 寃쎌슦
+        if (saveIssueTableConfig == null) {
+            IssueTableConfig issueTableConfig = new IssueTableConfig();
+            Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+            User user = this.userService.getUser(this.webAppUtil.getLoginId());
+            issueTableConfig.setWorkspace(workspace);
+            issueTableConfig.setUser(user);
+            issueTableConfig.setIssueTableConfigs(issueTableConfigs);
+            return this.issueTableConfigRepository.saveAndFlush(issueTableConfig);
+        }
+        else {
+            saveIssueTableConfig.setIssueTableConfigs(issueTableConfigs);
+            return this.issueTableConfigRepository.saveAndFlush(saveIssueTableConfig);
+        }
+    }
+
+    //  �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 �궗�슜�옄�쓽 �씠�뒋 �뀒�씠釉� �꽕�젙�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public IssueTableConfig findByUserIdAndWorkspaceId() {
+        return this.issueTableConfigRepository.findByUserIdAndWorkspaceId(this.webAppUtil.getLoginId(),
+                this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+    }
+
+    //  ���옣�맂 �씠�뒋 �뀒�씠釉� �꽕�젙�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailIssueTableConfig(Map<String, Object> resJsonData) {
+        //  �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 �궗�슜�옄�쓽 �씠�뒋 寃��깋 議곌굔�쓣 議고쉶�븳�떎.
+        IssueTableConfig issueTableConfig = this.findByUserIdAndWorkspaceId();
+
+        if (issueTableConfig != null) {
+            resJsonData.put(Constants.RES_KEY_CONTENTS, issueTableConfig.getIssueTableConfigs());
+        }
+        else {
+            resJsonData.put(Constants.RES_KEY_CONTENTS, "");
+        }
+    }
+
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueTypeCustomFieldServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueTypeCustomFieldServiceImpl.java
new file mode 100644
index 0000000..ce67e51
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueTypeCustomFieldServiceImpl.java
@@ -0,0 +1,191 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.repository.IssueTypeCustomFieldRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.vo.CustomFieldValueVo;
+import kr.wisestone.owl.vo.CustomFieldVo;
+import kr.wisestone.owl.vo.IssueTypeCustomFieldVo;
+import kr.wisestone.owl.web.condition.IssueTypeCustomFieldCondition;
+import kr.wisestone.owl.web.form.IssueTypeCustomFieldForm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class IssueTypeCustomFieldServiceImpl extends AbstractServiceImpl<IssueTypeCustomField, Long, JpaRepository<IssueTypeCustomField, Long>> implements IssueTypeCustomFieldService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueTypeCustomFieldServiceImpl.class);
+
+    @Autowired
+    private IssueTypeCustomFieldRepository issueTypeCustomFieldRepository;
+
+    @Autowired
+    private ProjectService projectService;
+
+    @Autowired
+    private IssueTypeService issueTypeService;
+
+    @Autowired
+    private CustomFieldService customFieldService;
+
+    @Autowired
+    private IssueCustomFieldValueService issueCustomFieldValueService;
+
+    @Override
+    protected JpaRepository<IssueTypeCustomField, Long> getRepository() {
+        return this.issueTypeCustomFieldRepository;
+    }
+
+    //  �봽濡쒖젥�듃�뿉�꽌 �궗�슜�븯�뒗 �븘�뱶 �젙蹂대�� �뾽�뜲�씠�듃�븳�떎.
+    @Override
+    @Transactional
+    public void modifyIssueTypeCustomFields(IssueTypeCustomFieldForm issueTypeCustomFieldForm) {
+        List<IssueTypeCustomField> issueTypeCustomFields = Lists.newArrayList();
+        Project project = this.projectService.getProject(issueTypeCustomFieldForm.getProjectId());
+        IssueType issueType = this.issueTypeService.getIssueType(issueTypeCustomFieldForm.getIssueTypeId());
+        List<IssueTypeCustomField> saveIssueTypeCustomFields = this.issueTypeCustomFieldRepository.findByProjectIdAndIssueTypeId(project.getId(), issueType.getId());
+
+        List<Long> addIssueTypeCustomFields = Lists.newArrayList();
+        List<IssueTypeCustomField> removeIssueTypeCustomFields = Lists.newArrayList();
+
+        //  ���옣�빐�빞�븷 ���긽 異붿텧
+        for (Map<String, Object> map : issueTypeCustomFieldForm.getRelationCustomFields()) {
+            Long customFieldId = MapUtil.getLong(map, "id");
+            boolean exist = false;
+
+            for (IssueTypeCustomField issueTypeCustomField : saveIssueTypeCustomFields) {
+                if (issueTypeCustomField.getCustomField().getId().equals(customFieldId)) {
+                    exist = true;
+                    break;
+                }
+            }
+
+            if (!exist) {
+                addIssueTypeCustomFields.add(customFieldId);
+            }
+        }
+
+        //  �궘�젣 ���긽 異붿텧
+        for (IssueTypeCustomField issueTypeCustomField : saveIssueTypeCustomFields) {
+            boolean exist = false;
+
+            for (Map<String, Object> map : issueTypeCustomFieldForm.getRelationCustomFields()) {
+                Long customFieldId = MapUtil.getLong(map, "id");
+
+                //  �씠誘� ���옣�릺�뼱 �엳�뒗 ���긽�� �뵲濡� ���옣
+                if (issueTypeCustomField.getCustomField().getId().equals(customFieldId)) {
+                    exist = true;
+                    break;
+                }
+            }
+
+            if (!exist) {
+                removeIssueTypeCustomFields.add(issueTypeCustomField);
+            }
+        }
+
+        //  �씠�뒋 ���엯 - �궗�슜�옄 �젙�쓽 �븘�뱶 �궘�젣
+        if (removeIssueTypeCustomFields.size() > 0) {
+            for (IssueTypeCustomField issueTypeCustomField : removeIssueTypeCustomFields) {
+                //  �씠�뒋 - �궗�슜�옄 �젙�쓽 �븘�뱶 媛� �궘�젣
+                this.issueCustomFieldValueService.removeIssueCustomFieldValue(issueTypeCustomField.getId());
+            }
+
+            this.issueTypeCustomFieldRepository.deleteAll(removeIssueTypeCustomFields);
+        }
+
+        for (Long customFieldId : addIssueTypeCustomFields) {
+            CustomField customField = this.customFieldService.getCustomField(customFieldId);
+            IssueTypeCustomField issueTypeCustomField = new IssueTypeCustomField(project, issueType, customField, IssueTypeCustomField.FIELD_OPTION_N);
+            issueTypeCustomFields.add(issueTypeCustomField);
+        }
+
+        //  �씠�뒋 ���엯 - �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂� �뿰寃�
+        if (issueTypeCustomFields.size() > 0) {
+            this.issueTypeCustomFieldRepository.saveAll(issueTypeCustomFields);
+            this.issueTypeCustomFieldRepository.flush();
+        }
+
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶媛� �씠�뒋�뿉�꽌 �몴�떆�릺�뒗 �닚�꽌瑜� �꽕�젙�븳�떎.
+        this.setIssueTypeCustomFieldPosition(project.getId(), issueType.getId(), issueTypeCustomFieldForm.getRelationCustomFields());
+
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶媛� �씠�뒋�뿉�꽌 �몴�떆�릺�뒗 �닚�꽌瑜� �꽕�젙�븳�떎.
+    private void setIssueTypeCustomFieldPosition(Long projectId, Long issueTypeId, List<Map<String, Object>> relationCustomFields) {
+
+        List<IssueTypeCustomField> saveIssueTypeCustomFields = this.issueTypeCustomFieldRepository.findByProjectIdAndIssueTypeId(projectId, issueTypeId);
+
+        int count = 1;
+
+        for (Map<String, Object> relationCustomField : relationCustomFields) {
+            Long customFieldId = MapUtil.getLong(relationCustomField, "id");
+
+            if (customFieldId != null) {
+                for (IssueTypeCustomField issueTypeCustomField : saveIssueTypeCustomFields) {
+                    if (issueTypeCustomField.getCustomField().getId().equals(customFieldId)) {
+                        issueTypeCustomField.setPosition(count);
+                        break;
+                    }
+                }
+            }
+            count++;
+        }
+
+        if (saveIssueTypeCustomFields.size() > 0) {
+            this.issueTypeCustomFieldRepository.saveAll(saveIssueTypeCustomFields);
+            this.issueTypeCustomFieldRepository.flush();
+        }
+    }
+
+    //  �봽濡쒖젥�듃�뿉�꽌 �궗�슜�븯�뒗 �쟾泥� �븘�뱶 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueTypeCustomFieldVo> findIssueTypeCustomField(Map<String, Object> resJsonData, IssueTypeCustomFieldCondition condition) {
+        Project project = this.projectService.getProject(condition.getProjectId());
+        IssueType issueType = this.issueTypeService.getIssueType(condition.getIssueTypeId());
+
+        List<IssueTypeCustomField> issueTypeCustomFields = this.issueTypeCustomFieldRepository.findByProjectIdAndIssueTypeIdOrderByPosition(project.getId(), issueType.getId());
+        List<IssueTypeCustomFieldVo> issueTypeCustomFieldVos = Lists.newArrayList();
+
+        for (IssueTypeCustomField issueTypeCustomField : issueTypeCustomFields) {
+            IssueTypeCustomFieldVo issueTypeCustomFieldVo = new IssueTypeCustomFieldVo();
+            CustomFieldVo customFieldVo = ConvertUtil.copyProperties(issueTypeCustomField.getCustomField(), CustomFieldVo.class);
+            customFieldVo.setCustomFieldValueVos(ConvertUtil.convertObjectsToClasses(issueTypeCustomField.getCustomField().getCustomFieldValues(), CustomFieldValueVo.class));
+            customFieldVo.setCustomFieldType(issueTypeCustomField.getCustomField().getCustomFieldType().toString());
+            issueTypeCustomFieldVo.setCustomFieldVo(customFieldVo);
+            issueTypeCustomFieldVo.setFieldOption(issueTypeCustomField.getFieldOption());
+            issueTypeCustomFieldVo.setChecked(issueTypeCustomField.getFieldOption().equals(IssueTypeCustomField.FIELD_OPTION_Y));   //  �솕硫댁뿉�꽌 �샃�뀡 泥댄겕�릺�룄濡� checked �뿉 媛� �뀑�똿
+            issueTypeCustomFieldVos.add(issueTypeCustomFieldVo);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueTypeCustomFieldVos);
+
+        return issueTypeCustomFieldVos;
+    }
+
+    //  �씠�뒋 excel template download �뿉�꽌 �궗�슜
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueTypeCustomField> findByProjectIdAndIssueTypeId(Long projectId, Long issueTypeId) {
+        return this.issueTypeCustomFieldRepository.findByProjectIdAndIssueTypeId(projectId, issueTypeId);
+    }
+
+    //  �씠�뒋 add / modify �뿉�꽌 �궗�슜
+    @Override
+    @Transactional(readOnly = true)
+    public IssueTypeCustomField findByProjectIdAndIssueTypeIdAndCustomFieldId(Long projectId, Long issueTypeId, Long customFieldId) {
+        return this.issueTypeCustomFieldRepository.findByProjectIdAndIssueTypeIdAndCustomFieldId(projectId, issueTypeId, customFieldId);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueTypeServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueTypeServiceImpl.java
new file mode 100644
index 0000000..7b14f1d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueTypeServiceImpl.java
@@ -0,0 +1,353 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.common.ExcelConditionCheck;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.IssueTypeMapper;
+import kr.wisestone.owl.repository.IssueTypeRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.*;
+import kr.wisestone.owl.web.condition.IssueTypeCondition;
+import kr.wisestone.owl.web.form.IssueTypeForm;
+import kr.wisestone.owl.web.view.ExcelView;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class IssueTypeServiceImpl extends AbstractServiceImpl<IssueType, Long, JpaRepository<IssueType, Long>> implements IssueTypeService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueTypeServiceImpl.class);
+
+    @Autowired
+    private IssueTypeRepository issueTypeRepository;
+
+    @Autowired
+    private WorkflowService workflowService;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private IssueTypeMapper issueTypeMapper;
+
+    @Autowired
+    private IssueService issueService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private ExcelView excelView;
+
+    @Autowired
+    private ExcelConditionCheck excelConditionCheck;
+
+
+    @Override
+    protected JpaRepository<IssueType, Long> getRepository() {
+        return this.issueTypeRepository;
+    }
+
+    @Override
+    @Transactional
+    public void addDefaultIssueType(Workspace workspace, List<ProjectType> projectTypes) {
+        for (ProjectType projectType : projectTypes) {
+            List<IssueType> issueTypes = Lists.newArrayList();
+            Workflow workflow = this.workflowService.findByWorkspaceIdAndProjectType(workspace.getId(), projectType);
+
+            switch (projectType) {
+                case BTS_PROJECT:
+                    issueTypes.add(new IssueType(workspace, workflow, this.messageAccessor.message("common.bug"), "", "#ff5f99")); // 踰꾧렇
+                    issueTypes.add(new IssueType(workspace, workflow, this.messageAccessor.message("common.improvement"), "", "#3598fe")); // 媛쒖꽑
+                    break;
+
+                case RMS_PROJECT:
+                    issueTypes.add(new IssueType(workspace, workflow, this.messageAccessor.message("common.requirement"), "", "#3bcde2")); // �슂援� �궗�빆
+                    break;
+
+                case TCM_PROJECT:
+                    issueTypes.add(new IssueType(workspace, workflow, this.messageAccessor.message("common.testcase"), "", "#008ca7")); // �뀒�뒪�듃 耳��씠�뒪, �떎�뻾 �닚�꽌, �쟾�젣 議곌굔, 湲곕� 寃곌낵
+                    break;
+            }
+
+            this.issueTypeRepository.saveAll(issueTypes);
+        }
+    }
+
+    //  �씠�뒋 �쑀�삎�쓣 �깮�꽦�븳�떎.
+    @Override
+    @Transactional
+    public IssueType addIssueType(IssueTypeForm issueTypeForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+        //  �씠由� �쑀�슚�꽦 泥댄겕
+        this.verifyName(issueTypeForm.getName(), null);
+        //  �깋�긽 泥댄겕
+        this.verifyColor(issueTypeForm.getColor());
+
+        IssueType issueType = ConvertUtil.copyProperties(issueTypeForm, IssueType.class);
+        Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        issueType.setWorkspace(workspace);
+        Workflow workflow = this.workflowService.getWorkflow(issueTypeForm.getWorkflowId());
+        issueType.setWorkflow(workflow);
+
+        return this.issueTypeRepository.saveAndFlush(issueType);
+    }
+
+    //  �씠由� �쑀�슚�꽦 泥댄겕
+    private void verifyName(String name, Long id) {
+        if (StringUtils.isEmpty(name)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_TYPE_NOT_NAME));
+        }
+
+        if (name.length() > 15) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_TYPE_NAME_MAX_LENGTH_OUT));
+        }
+
+        IssueType issueType;
+        Long workspaceId = this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId();
+
+        if (id == null) {
+            issueType = this.issueTypeRepository.findByNameAndWorkspaceId(name, workspaceId);
+        } else {
+            issueType = this.issueTypeRepository.findByNameAndWorkspaceIdAndIdNot(name, workspaceId, id);
+        }
+
+        if (issueType != null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_TYPE_USED_NAME));
+        }
+    }
+
+    //  �깋�긽 泥댄겕
+    private void verifyColor(String color) {
+        if (StringUtils.isEmpty(color)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_TYPE_NOT_COLOR));
+        }
+    }
+
+    //  �씠�뒋 �쑀�삎 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueTypeVo> findIssueType(Map<String, Object> resJsonData,
+                                           IssueTypeCondition condition, Pageable pageable) {
+
+        condition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        condition.setPageSize(pageable.getPageSize());
+        condition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        List<Map<String, Object>> results = this.issueTypeMapper.find(condition);
+        Long totalCount = this.issueTypeMapper.count(condition);
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        List<IssueTypeVo> issueTypeVos = ConvertUtil.convertListToListClass(results, IssueTypeVo.class);
+
+        //  �씠�뒋 �쑀�삎�뿉 �뿰寃곕맂 �썙�겕�뵆濡쒖슦 �젙蹂대�� �뀑�똿�븳�떎.
+        if (condition.getDeep() != null) {
+            this.setUseIssueTypeByWorkflow(issueTypeVos);
+        }
+
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueTypeVos);
+
+        return issueTypeVos;
+    }
+
+    //  �씠�뒋 �쑀�삎�뿉 �뿰寃곕맂 �썙�겕�뵆濡쒖슦 �젙蹂대�� �뀑�똿�븳�떎.
+    private void setUseIssueTypeByWorkflow(List<IssueTypeVo> issueTypeVos) {
+        for (IssueTypeVo issueTypeVo : issueTypeVos) {
+            IssueType issueType = this.getIssueType(issueTypeVo.getId());
+            issueTypeVo.setWorkflowVo(ConvertUtil.copyProperties(issueType.getWorkflow(), WorkflowVo.class));
+        }
+    }
+
+    //  �씠�뒋 �쑀�삎 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailIssueType(Map<String, Object> resJsonData, IssueTypeCondition issueTypeCondition) {
+        IssueTypeVo issueTypeVo = new IssueTypeVo();
+
+        if (issueTypeCondition.getId() != null) {
+            IssueType issueType = this.getIssueType(issueTypeCondition.getId());
+            issueTypeVo = ConvertUtil.copyProperties(issueType, IssueTypeVo.class);
+
+            switch (issueTypeCondition.getDeep()) {
+                case "01": //  �썙�겕�뵆濡쒖슦 �젙蹂대�� 媛��졇�삩�떎.
+                    issueTypeVo.setWorkflowVo(ConvertUtil.copyProperties(issueType.getWorkflow(), WorkflowVo.class));
+                    break;
+            }
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueTypeVo);
+    }
+
+    //  �씠�뒋 �쑀�삎�쓣 �닔�젙�븳�떎.
+    @Override
+    @Transactional
+    public IssueType modifyIssueType(IssueTypeForm issueTypeForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        IssueType issueType = this.getIssueType(issueTypeForm.getId());
+        //  �씠由� �쑀�슚�꽦 泥댄겕
+        this.verifyName(issueTypeForm.getName(), issueTypeForm.getId());
+        //  �깋�긽 泥댄겕
+        this.verifyColor(issueTypeForm.getColor());
+
+        Workflow workflow = this.workflowService.getWorkflow(issueTypeForm.getWorkflowId());
+        //  �썙�겕�뵆濡쒖슦 蹂�寃� 泥댄겕
+        this.checkWorkflowChange(issueType.getWorkflow(), workflow, issueType);
+
+        ConvertUtil.copyProperties(issueTypeForm, issueType, "id", "issueTypeType");
+        issueType.setWorkflow(workflow);
+
+        this.issueTypeRepository.saveAndFlush(issueType);
+
+        return issueType;
+    }
+
+    //  �썙�겕�뵆濡쒖슦媛� 蹂�寃쎈릺�뿀�뒗吏� �솗�씤�븯怨� 蹂�寃쎈릺�뿀�쓣 寃쎌슦 �씠�뒋 �긽�깭媛� �뾾�뒗 �씠�뒋�뒗 '�깮�꽦' �씤 �씠�뒋 �긽�깭濡� �씠�룞�븳�떎.
+    private void checkWorkflowChange(Workflow oldWorkflow, Workflow newWorkflow, IssueType issueType) {
+        if (!oldWorkflow.getId().equals(newWorkflow.getId())) {
+            //  �씠�뒋 �쑀�삎�뿉�꽌 �썙�겕�뵆濡쒖슦媛� 蹂�寃쎈릺�뿀�쓣 �븣 �빐�떦 �썙�겕�뵆濡쒖슦�뿉 �쁽�옱 �씠�뒋 �긽�깭媛� 議댁옱�븯吏� �븡�쑝硫� ��湲�(�깮�꽦) 濡� 蹂�寃쏀븳�떎.
+            this.issueService.changeWorkflows(newWorkflow, issueType);
+        }
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public IssueType getIssueType(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_TYPE_NOT_EXIST));
+        }
+
+        IssueType issueType = this.findOne(id);
+
+        if (issueType == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_TYPE_NOT_EXIST));
+        }
+
+        return issueType;
+    }
+
+    //  �씠�뒋 �쑀�삎�쓣 �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void removeIssueTypes(IssueTypeForm issueTypeForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        if (issueTypeForm.getRemoveIds().size() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_TYPE_REMOVE_NOT_SELECT));
+        }
+
+        for (Long projectId : issueTypeForm.getRemoveIds()) {
+            this.removeIssueTypes(projectId);
+        }
+
+        this.issueTypeRepository.flush();
+    }
+
+    private void removeIssueTypes(Long issueTypeId) {
+        IssueType issueType = this.getIssueType(issueTypeId);
+        //  �궘�젣�븷 �씠�뒋 �쑀�삎�씠 �궗�슜�릺怨� �엳�뒗吏� �솗�씤�븳�떎.
+        this.checkUseIssueType(issueType);
+        this.issueTypeRepository.delete(issueType);
+    }
+
+    //  �궘�젣�븷 �씠�뒋 �쑀�삎�씠 �궗�슜�릺怨� �엳�뒗吏� �솗�씤�븳�떎.
+    private void checkUseIssueType(IssueType issueType) {
+        //  �씠�뒋 �쑀�삎�쓣 �궗�슜�븯�뒗 �씠�뒋 媛��닔瑜� 議고쉶�븳�떎.
+        Long issueCount = this.issueService.countByIssueTypeId(issueType.getId());
+
+        if (issueCount > 0) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_TYPE_USE_ISSUES));
+        }
+    }
+
+    //  �썙�겕�뒪�럹�씠�뒪�뿉 �엳�뒗 紐⑤뱺 �씠�뒋 �쑀�삎�쓣 議고쉶�븳�떎. �씠�뒋 �뿊�� import �뿉�꽌 �궗�슜
+    @Override
+    @Transactional(readOnly = true)
+    public List<IssueType> findByWorkspaceId() {
+        return this.issueTypeRepository.findByWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+    }
+
+    //  �씠�뒋 ���엯 �븘�뱶 紐⑸줉�쓣 �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    @Override
+    @Transactional
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        //  �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븯怨� 鍮꾪솢�꽦�씪 寃쎌슦 �뿊�� �떎�슫濡쒕뱶瑜� 湲덉��븳�떎.
+        ModelAndView modelAndView = this.workspaceService.checkUseExcelDownload(model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        Map<String, Object> conditions = new HashMap<>();
+        //  �뿊�� �떎�슫濡쒕뱶�뿉 �븘�슂�븳 寃��깋 議곌굔 �젙蹂대�� 異붿텧�븯怨� 寃��깋 議곌굔 異붿텧�뿉 �삤瑜섍� 諛쒖깮�븯硫� 寃쎄퀬瑜� �몴�떆�빐以��떎.
+        modelAndView = this.excelConditionCheck.checkCondition(conditions, request, model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        IssueTypeCondition issueTypeCondition = IssueTypeCondition.make(conditions);
+        issueTypeCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        List<Map<String, Object>> results = this.issueTypeMapper.find(issueTypeCondition);
+        List<IssueTypeVo> issueTypeVos = ConvertUtil.convertListToListClass(results, IssueTypeVo.class);
+
+        //  �씠�뒋 �쑀�삎�뿉 �뿰寃곕맂 �썙�겕�뵆濡쒖슦 �젙蹂대�� �뀑�똿�븳�떎.
+        if (issueTypeCondition.getDeep() != null) {
+            this.setUseIssueTypeByWorkflow(issueTypeVos);
+        }
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.issueTypeList")); // �씠�뒋 ���엯 紐⑸줉
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("name", this.messageAccessor.message("common.issueType"), 15, ExportExcelAttrVo.ALIGN_LEFT)); // �씠�뒋 ���엯
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("color", this.messageAccessor.message("common.color"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �깋�긽
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("workflowName", this.messageAccessor.message("common.workflow"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // �썙�겕�뵆濡쒖슦
+        //  �뿊���뿉 �꽔�쓣 �뜲�씠�꽣
+        excelInfo.setDatas(this.convertExcelViewToIssueTypeVos(issueTypeVos));
+        model.addAttribute(Constants.EXCEL, excelInfo);
+        return new ModelAndView(this.excelView);
+    }
+
+    //  IssueTypeVo �뜲�씠�꽣瑜� �뿊���뿉�꽌 �몴�떆�븷 �닔 �엳�뒗 �뜲�씠�꽣濡� 蹂�寃쏀븳�떎.
+    private List<Map<String, String>> convertExcelViewToIssueTypeVos(List<IssueTypeVo> issueTypeVos) {
+        List<Map<String, String>> results = Lists.newArrayList();
+
+        for (IssueTypeVo issueTypeVo : issueTypeVos) {
+            Map<String, String> result = new HashMap<>();
+            result.put("name", issueTypeVo.getName());
+            result.put("color", issueTypeVo.getColor());
+            result.put("workflowName", issueTypeVo.getWorkflowVo().getName());
+            results.add(result);
+        }
+
+        return results;
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueUserServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueUserServiceImpl.java
new file mode 100644
index 0000000..13dfa3f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueUserServiceImpl.java
@@ -0,0 +1,128 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.mapper.IssueUserMapper;
+import kr.wisestone.owl.repository.IssueUserRepository;
+import kr.wisestone.owl.service.IssueUserService;
+import kr.wisestone.owl.service.ProjectService;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.web.form.IssueForm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class IssueUserServiceImpl extends AbstractServiceImpl<IssueUser, Long, JpaRepository<IssueUser, Long>> implements IssueUserService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueUserServiceImpl.class);
+
+    @Autowired
+    private IssueUserRepository issueUserRepository;
+
+    @Autowired
+    private IssueUserMapper issueUserMapper;
+
+    @Override
+    protected JpaRepository<IssueUser, Long> getRepository() {
+        return this.issueUserRepository;
+    }
+
+    //  �씠�뒋 �떞�떦�옄瑜� 蹂�寃쏀븳�떎.
+    @Override
+    @Transactional
+    public void modifyIssueUser(Issue issue, Workspace workspace, List<Long> userIds) {
+        List<Long> oldUserIds = Lists.newArrayList();
+
+        //  �씠�쟾 �떞�떦�옄
+        for (IssueUser issueUser : issue.getIssueUsers()) {
+            oldUserIds.add(issueUser.getUser().getId());
+        }
+
+        List<Long> newUserIds = CommonUtil.searchChangeList(oldUserIds, userIds); //  異붽��빐�빞�븷 �궗�슜�옄瑜� 李얜뒗�떎.
+        List<Long> removeUserIds = CommonUtil.searchChangeList(userIds, oldUserIds); //  �궘�젣�빐�빞�븷 �궗�슜�옄瑜� 李얜뒗�떎.
+
+        if (removeUserIds.size() > 0) {
+            Map<String, Object> removeIssueAssigneeMap = new HashMap<>();
+            removeIssueAssigneeMap.put("issueId", issue.getId());
+            removeIssueAssigneeMap.put("userIds", removeUserIds);
+
+            //  �떞�떦�옄 �궘�젣
+            this.issueUserMapper.deleteIssueUserByIssueIdAndMultiUserId(removeIssueAssigneeMap);
+        }
+
+        if (newUserIds.size() > 0) {
+            List<Map<String, Long>> addIssueAssigneeMaps = Lists.newArrayList();
+
+            for (Long userId : newUserIds) {
+                Map<String, Long> issueAssigneeMap = new HashMap<>();
+                issueAssigneeMap.put("userId", userId);
+                issueAssigneeMap.put("issueId", issue.getId());
+                issueAssigneeMap.put("workspaceId", workspace.getId());
+                issueAssigneeMap.put("registerId", this.webAppUtil.getLoginId());
+                addIssueAssigneeMaps.add(issueAssigneeMap);
+            }
+
+            //  �떞�떦�옄 異붽�
+            this.issueUserMapper.insertIssueUser(addIssueAssigneeMaps);
+        }
+    }
+
+
+
+    //  �씠�뒋 �떞�떦�옄 李얘린
+    @Override
+    @Transactional
+    public List<IssueUser> find(Issue issue) {
+        return this.issueUserRepository.findByIssueId(issue.getId());
+    }
+
+    //  �씠�뒋 �떞�떦�옄 踰뚰겕 �벑濡�
+    @Override
+    @Transactional
+    public void insertIssueUser(List<Map<String, Long>> issueAssigneeMaps) {
+        //  �씠�뒋 �떞�떦�옄 踰뚰겕 �벑濡�
+        this.issueUserMapper.insertIssueUser(issueAssigneeMaps);
+    }
+
+    //  �씠�뒋 �떞�떦�옄�뿉�꽌 �젣�쇅�븳�떎.
+    @Override
+    @Transactional
+    public void removeIssueUser(Long projectId, List<Long> excludeUserIds) {
+
+        for (Long userId : excludeUserIds) {
+            Map<String, Object> issueUserMap = new HashMap<>();
+            issueUserMap.put("userId", userId);
+            issueUserMap.put("projectId", projectId);
+
+            List<Map<String, Object>> results = this.issueUserMapper.findByUserIdAndProjectId(issueUserMap);
+
+            if (results.size() > 0) {
+                List<Long> issueIds = Lists.newArrayList();
+
+                for (Map<String, Object> result : results) {
+                    Long id = MapUtil.getLong(result, "id");
+
+                    if (id != null) {
+                        issueIds.add(id);
+                    }
+                }
+
+                if (issueIds.size() > 0) {
+                    Map<String, Object> removeIssueAssigneeMap = new HashMap<>();
+                    removeIssueAssigneeMap.put("userId", userId);
+                    removeIssueAssigneeMap.put("issueIds", issueIds);
+                    this.issueUserMapper.deleteIssueUserByUserIdAndMultiIssueId(removeIssueAssigneeMap);
+                }
+            }
+        }
+
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueVersionServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueVersionServiceImpl.java
new file mode 100644
index 0000000..47c339a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueVersionServiceImpl.java
@@ -0,0 +1,146 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import com.google.gson.Gson;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.domain.IssueVersion;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.repository.IssueVersionRepository;
+import kr.wisestone.owl.service.IssueService;
+import kr.wisestone.owl.service.IssueVersionService;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.IssueVersionVo;
+import kr.wisestone.owl.vo.IssueVo;
+import kr.wisestone.owl.web.condition.IssueVersionCondition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class IssueVersionServiceImpl extends AbstractServiceImpl<IssueVersion, Long, JpaRepository<IssueVersion, Long>> implements IssueVersionService {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueVersionServiceImpl.class);
+
+    @Autowired
+    private IssueVersionRepository issueVersionRepository;
+
+    @Autowired
+    private IssueService issueService;
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    protected JpaRepository<IssueVersion, Long> getRepository() {
+        return this.issueVersionRepository;
+    }
+
+    //  �씠�뒋 踰꾩쟾 �깮�꽦
+    @Override
+    @Transactional
+    public void addIssueVersion(Issue issue) {
+        IssueVo issueVo = ConvertUtil.copyProperties(issue, IssueVo.class);
+        this.issueService.setIssueDetail(issueVo, issue);    //  �씠�뒋 �긽�꽭 �젙蹂대�� �뀑�똿�븳�떎.
+
+        List<IssueVersion> issueVersions = this.issueVersionRepository.findByIssueId(issueVo.getId());
+
+        Gson gson = new Gson();
+        String content = gson.toJson(issueVo);
+        IssueVersion issueVersion = new IssueVersion(issue, issue.getProject(), issue.getProject().getWorkspace(), content);
+
+        if (issueVersions.size() > 0) {
+            IssueVersion prevIssueVersion = issueVersions.get(issueVersions.size() - 1);
+            issueVersion.setVersion(prevIssueVersion.getVersion() + 1);
+        }
+        else {
+            issueVersion.setVersion(1);
+        }
+
+        this.issueVersionRepository.saveAndFlush(issueVersion);
+    }
+
+    //  �씠�뒋 踰꾩쟾 �젙蹂대�� 留뚮뱾�뼱�꽌 Vo 濡� 蹂��솚�븳�떎.
+    private IssueVersionVo makeIssueVersionVo(IssueVersion issueVersion) {
+        IssueVersionVo issueVersionVo = ConvertUtil.copyProperties(issueVersion, IssueVersionVo.class); //  �씠�쟾 踰꾩쟾
+        Gson gson = new Gson();
+        Map<String, Object> targetIssueVersionContent = (Map<String, Object>) gson.fromJson(issueVersionVo.getContent(), Object.class);
+        issueVersionVo.setContent(null);
+        issueVersionVo.setVersionHistory(targetIssueVersionContent);
+        User beforeModifyUser = this.userService.getUser(issueVersionVo.getModifyId());
+        issueVersionVo.setModifyByName(beforeModifyUser.getName() + "(" + CommonUtil.decryptAES128(beforeModifyUser.getAccount()) + ")");
+
+        return issueVersionVo;
+    }
+
+    //  �꽑�깮�븳 踰꾩쟾怨� 洹� �떎�쓬 踰꾩쟾 �젙蹂대�� Vo濡� 留뚮뱾�뼱 由ы꽩�븳�떎.
+    private void makeTargetAndNextIssueVersion(IssueVersion targetIssueVersion, IssueVersion nextIssueVersion, List<IssueVersion> issueVersions, Map<String, Object> results) {
+        //  �씠�뒋 踰꾩쟾 �젙蹂대�� 留뚮뱾�뼱�꽌 Vo 濡� 蹂��솚�븳�떎.
+        IssueVersionVo targetIssueVersionVo = this.makeIssueVersionVo(targetIssueVersion);
+        //  �씠�뒋 踰꾩쟾 �젙蹂대�� 留뚮뱾�뼱�꽌 Vo 濡� 蹂��솚�븳�떎.
+        IssueVersionVo nextIssueVersionVo = this.makeIssueVersionVo(nextIssueVersion);
+
+        results.put("targetIssueVersionVo", targetIssueVersionVo);
+        results.put("nextIssueVersionVo", nextIssueVersionVo);
+
+        List<IssueVersionVo> issueVersionVos = ConvertUtil.convertObjectsToClasses(issueVersions, IssueVersionVo.class);
+        issueVersionVos.remove(issueVersionVos.size() - 1);
+        results.put("issueVersionList", issueVersionVos);
+    }
+
+
+    //  �씠�뒋 踰꾩쟾 �젙蹂� 議고쉶
+    @Override
+    @Transactional(readOnly = true)
+    public void find(Map<String, Object> resJsonData, IssueVersionCondition issueVersionCondition) {
+        List<IssueVersion> issueVersions = this.issueVersionRepository.findByIssueId(issueVersionCondition.getIssueId());
+
+        Map<String, Object> results = new HashMap<>();
+        results.put("targetIssueVersionVo", null);
+        results.put("nextIssueVersionVo", null);
+        results.put("issueVersionList", Lists.newArrayList());
+
+        if (issueVersionCondition.getId() == null) {
+            if (issueVersions.size() > 1) {
+                IssueVersion targetIssueVersion = issueVersions.get(issueVersions.size() - 2);
+                IssueVersion nextIssueVersion = issueVersions.get(issueVersions.size() - 1);
+                //  �꽑�깮�븳 踰꾩쟾怨� 洹� �떎�쓬 踰꾩쟾 �젙蹂대�� Vo 濡� 留뚮뱾�뼱 由ы꽩�븳�떎.
+                this.makeTargetAndNextIssueVersion(targetIssueVersion, nextIssueVersion, issueVersions, results);
+            }
+        }
+        else {
+            IssueVersion targetIssueVersion = null; //  �꽑�깮�븳 踰꾩쟾
+            IssueVersion nextIssueVersion = null;   //  �꽑�깮�븳 踰꾩쟾�쓽 �떎�쓬 踰꾩쟾
+
+            for (IssueVersion issueVersion : issueVersions) {
+                //  �꽑�깮�븳 踰꾩쟾�쓣 李얠븯�쑝硫� 洹� �떎�쓬 踰꾩쟾�쓣 李얜뒗�떎.
+                if (targetIssueVersion != null) {
+                    nextIssueVersion = issueVersion;
+                    break;
+                }
+
+                //  �꽑�깮�븳 踰꾩쟾�쓣 李얜뒗�떎.
+                if (issueVersion.getId().equals(issueVersionCondition.getId())) {
+                    targetIssueVersion = issueVersion;
+                }
+            }
+
+            //  �몮�떎 議댁옱�븷 �븣留� 踰꾩쟾 鍮꾧탳媛� 媛��뒫�븯�떎.
+            if (targetIssueVersion != null && nextIssueVersion != null) {
+                //  �꽑�깮�븳 踰꾩쟾怨� 洹� �떎�쓬 踰꾩쟾 �젙蹂대�� Vo 濡� 留뚮뱾�뼱 由ы꽩�븳�떎.
+                this.makeTargetAndNextIssueVersion(targetIssueVersion, nextIssueVersion, issueVersions, results);
+            }
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, results);
+
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/ManageUserServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/ManageUserServiceImpl.java
new file mode 100644
index 0000000..905bf06
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/ManageUserServiceImpl.java
@@ -0,0 +1,113 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MngPermission;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.UserWorkspace;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.UserWorkspaceMapper;
+import kr.wisestone.owl.repository.UserRepository;
+import kr.wisestone.owl.repository.UserWorkspaceRepository;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.service.ManageUserService;
+import kr.wisestone.owl.service.WorkspaceService;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.SecurityUtils;
+import kr.wisestone.owl.vo.ResPage;
+import kr.wisestone.owl.vo.ManageUserVo;
+import kr.wisestone.owl.web.condition.UserWorkspaceCondition;
+import kr.wisestone.owl.web.form.ManageUserForm;
+import kr.wisestone.owl.web.form.UserWorkspaceForm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class ManageUserServiceImpl extends AbstractServiceImpl<UserWorkspace, Long, JpaRepository<UserWorkspace, Long>>  implements ManageUserService {
+
+    private static final Logger log = LoggerFactory.getLogger(ManageUserServiceImpl.class);
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private UserWorkspaceRepository userWorkspaceRepository;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private UserWorkspaceMapper userWorkspaceMapper;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Override
+    protected JpaRepository<UserWorkspace, Long> getRepository() {
+        return this.userWorkspaceRepository;
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 李몄뿬/李몄뿬��湲� �븯�뒗 �쟾泥� �궗�슜�옄 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<ManageUserVo> findUserPermission(Map<String, Object> resJsonData,
+                                                   UserWorkspaceCondition condition, Pageable pageable) {
+
+        //  �뾽臾� 怨듦컙瑜� 媛��졇�삩�떎.
+        Workspace primaryWorkspace = this.workspaceService.getPrimaryWorkspace();
+        condition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        condition.setPageSize(pageable.getPageSize());
+        condition.setWorkspaceId(primaryWorkspace.getId());
+        condition.setAccount(CommonUtil.encryptAES128(condition.getAccount()));
+
+        List<Map<String, Object>> results = this.userWorkspaceMapper.find(condition);
+        Long totalCount = this.userWorkspaceMapper.count(condition);
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        List<ManageUserVo> manageUserVos = ConvertUtil.convertListToListClass(results, ManageUserVo.class);
+
+        for (ManageUserVo manageUserVo : manageUserVos) {
+            manageUserVo.setAccount(CommonUtil.decryptAES128(manageUserVo.getAccount()));
+            manageUserVo.setPermission(manageUserVo.getPermission());
+        }
+
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+        resJsonData.put(Constants.RES_KEY_CONTENTS, manageUserVos);
+
+        return manageUserVos;
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 �뿰寃곕맂 �궗�슜�옄�쓽 李몄뿬 �긽�깭瑜� 蹂�寃쏀븳�떎.
+    @Override
+    @Transactional
+    public void modifyUserPermission(ManageUserForm manageUserForm) {
+        int newPermission = MngPermission.USER_PERMISSION_MNG_NONE;
+
+        newPermission |= MngPermission.makePermission(manageUserForm.getPermWorkSpace(), MngPermission.USER_PERMISSION_MNG_WORKSPACE);
+        newPermission |= MngPermission.makePermission(manageUserForm.getPermProjectSetting(), MngPermission.USER_PERMISSION_MNG_PROJECT);
+        newPermission |= MngPermission.makePermission(manageUserForm.getPermIssueSetting(), MngPermission.USER_PERMISSION_MNG_ISSUE_SETTING);
+        newPermission |= MngPermission.makePermission(manageUserForm.getPermUser(), MngPermission.USER_PERMISSION_MNG_USER);
+        newPermission |= MngPermission.makePermission(manageUserForm.getPermNotice(), MngPermission.USER_PERMISSION_MNG_NOTICE);
+        newPermission |= MngPermission.makePermission(manageUserForm.getPermFAQ(), MngPermission.USER_PERMISSION_MNG_FAQ);
+        newPermission |= MngPermission.makePermission(manageUserForm.getPermQnA(), MngPermission.USER_PERMISSION_MNG_QNA);
+        newPermission |= MngPermission.makePermission(manageUserForm.getPermEvent(), MngPermission.USER_PERMISSION_MNG_EVENT);
+        newPermission |= MngPermission.makePermission(manageUserForm.getPermGuide(), MngPermission.USER_PERMISSION_MNG_GUIDE);
+
+        User user = userService.getUser(manageUserForm.getUserId());
+        user.setPermission(newPermission);
+
+        this.userRepository.saveAndFlush(user);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/NoticeServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/NoticeServiceImpl.java
new file mode 100644
index 0000000..4d30df3
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/NoticeServiceImpl.java
@@ -0,0 +1,156 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.config.kafka.KafkaSender;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.Notice;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.NoticeMapper;
+import kr.wisestone.owl.repository.NoticeRepository;
+import kr.wisestone.owl.service.NoticeService;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.NoticeVo;
+import kr.wisestone.owl.vo.ResPage;
+import kr.wisestone.owl.web.condition.NoticeCondition;
+import kr.wisestone.owl.web.form.NoticeForm;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class NoticeServiceImpl extends AbstractServiceImpl<Notice, Long, JpaRepository<Notice, Long>> implements NoticeService {
+
+    @Autowired
+    private NoticeRepository noticeRepository;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private KafkaSender kafkaSender;
+
+    @Autowired
+    private NoticeMapper noticeMapper;
+
+    @Override
+    protected JpaRepository<Notice, Long> getRepository() {
+        return this.noticeRepository;
+    }
+
+    //  怨듭� �궗�빆 �벑濡�
+    @Override
+    @Transactional
+    public Notice addNotice(NoticeForm noticeForm) {
+        //  怨듭��궗�빆 �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+        this.verifyTitleAndDescription(noticeForm.getTitle(), noticeForm.getDescription());
+
+        Notice notice = ConvertUtil.copyProperties(noticeForm, Notice.class);
+
+        return this.noticeRepository.saveAndFlush(notice);
+    }
+
+    //  怨듭��궗�빆 �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+    private void verifyTitleAndDescription(String title, String description) {
+        if (StringUtils.isEmpty(title) || StringUtils.isEmpty(description)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.NOTICE_EMPTY_CONTENT));
+        }
+    }
+
+    //  怨듭��궗�빆 議고쉶
+    @Override
+    @Transactional(readOnly = true)
+    public List<NoticeVo> findNotice(Map<String, Object> resJsonData,
+                                     NoticeCondition noticeCondition, Pageable pageable) {
+
+        noticeCondition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        noticeCondition.setPageSize(pageable.getPageSize());
+        noticeCondition.setTitle(noticeCondition.getTitle());
+
+        List<Map<String, Object>> results = this.noticeMapper.find(noticeCondition);
+        Long totalCount = this.noticeMapper.count(noticeCondition);
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        List<NoticeVo> noticeVos = ConvertUtil.convertListToListClass(results, NoticeVo.class);
+
+        //  濡쒓렇�씤 �븘�씠�뵒 1 �� 愿�由ъ옄 - 愿�由ъ옄留� �닔�젙 媛��뒫
+        for (NoticeVo noticeVo : noticeVos) {
+            if (this.webAppUtil.getLoginId().equals(1L)) {
+                noticeVo.setModifyPermissionCheck(true);
+            }
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, noticeVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        return noticeVos;
+    }
+
+    //  怨듭��궗�빆 �닔�젙
+    @Override
+    @Transactional
+    public Notice modifyNotice(NoticeForm noticeForm) {
+        //  怨듭��궗�빆 �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+        this.verifyTitleAndDescription(noticeForm.getTitle(), noticeForm.getDescription());
+
+        Notice notice = this.getNotice(noticeForm.getId());
+        ConvertUtil.copyProperties(noticeForm, notice, "id");
+
+        return this.noticeRepository.saveAndFlush(notice);
+    }
+
+    //  怨듭��궗�빆�쓣 id 濡� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Notice getNotice(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.NOTICE_NOT_EXIST));
+        }
+
+        Notice notice = this.findOne(id);
+
+        if (notice == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.NOTICE_NOT_EXIST));
+        }
+
+        return notice;
+    }
+
+    //  怨듭��궗�빆 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailNotice(Map<String, Object> resJsonData, NoticeCondition noticeCondition) {
+        NoticeVo noticeVo = new NoticeVo();
+
+        if (noticeCondition.getId() != null) {
+            Notice notice = this.getNotice(noticeCondition.getId());
+            noticeVo = ConvertUtil.copyProperties(notice, NoticeVo.class);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, noticeVo);
+    }
+
+    //  硫붿꽭吏� 蹂대궡湲�
+    @Override
+    @Transactional
+    public void sendNotice(NoticeForm noticeForm) {
+        for (Long userId : noticeForm.getUserIds()) {
+            User user = this.userService.getUser(userId);
+
+            Map<String, Object> message = new HashMap<>();
+            message.put("url", "/notification/message");
+            message.put("message", noticeForm.getDescription());
+            message.put("account", user.getAccount());
+            this.kafkaSender.send("common-topic", message);
+        }
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/PaymentHistoryServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/PaymentHistoryServiceImpl.java
new file mode 100644
index 0000000..06d801a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/PaymentHistoryServiceImpl.java
@@ -0,0 +1,78 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.PaymentHistory;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.PaymentHistoryRepository;
+import kr.wisestone.owl.service.PaymentHistoryService;
+import kr.wisestone.owl.web.form.PaymentForm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+public class PaymentHistoryServiceImpl extends AbstractServiceImpl<PaymentHistory, Long, JpaRepository<PaymentHistory, Long>> implements PaymentHistoryService {
+
+    private static final Logger log = LoggerFactory.getLogger(PaymentHistoryServiceImpl.class);
+
+    @Autowired
+    private PaymentHistoryRepository paymentHistoryRepository;
+
+    @Override
+    protected JpaRepository<PaymentHistory, Long> getRepository() {
+        return this.paymentHistoryRepository;
+    }
+
+    @Override
+    @Transactional
+    public PaymentHistory addPaymentHistory(PaymentServiceImpl.RestClientResultObject resultObject, PaymentForm paymentForm, Workspace workspace) {
+        PaymentHistory paymentHistory = new PaymentHistory();
+        paymentHistory.bindPaymentResult(resultObject);
+        paymentHistory.setPrice(paymentForm.getPaymentAmount());
+        paymentHistory.setBuyUser(paymentForm.getBuyUser());
+        paymentHistory.setType(paymentForm.getType());
+        paymentHistory.setCustomerUid(paymentForm.getCustomerUid());
+        paymentHistory.setMerchantUid(paymentForm.getMerchantUid());
+        paymentHistory.setWorkspace(workspace);
+
+        return this.paymentHistoryRepository.saveAndFlush(paymentHistory);
+    }
+
+    //  寃곗젣 �떆 �삤瑜섍� 諛쒖깮�뻽�쓣 �븣 痍⑥냼 �젙蹂대�� ���옣�븳�떎.
+    @Override
+    @Transactional
+    public PaymentHistory cancelPaymentHistory(PaymentForm paymentForm, Workspace workspace, String reason) {
+        PaymentHistory paymentHistory = new PaymentHistory();
+        paymentHistory.setPrice(paymentForm.getPaymentAmount());
+        paymentHistory.setBuyUser(paymentForm.getBuyUser());
+        paymentHistory.setType(paymentForm.getType());
+        paymentHistory.setCustomerUid(paymentForm.getCustomerUid());
+        paymentHistory.setMerchantUid(paymentForm.getMerchantUid());
+        paymentHistory.setWorkspace(workspace);
+        paymentHistory.setPaymentResult(PaymentHistory.PAYMENT_RESULT_FAILED);
+        paymentHistory.setPaymentResponse(reason);
+
+        return this.paymentHistoryRepository.saveAndFlush(paymentHistory);
+    }
+
+    //  �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 留덉�留됱쑝濡� 寃곗젣�븳 �젙蹂대�� 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public PaymentHistory findByWorkspaceLastPaymentHistory(Workspace workspace) {
+        List<PaymentHistory> paymentHistoryList = this.paymentHistoryRepository.findByWorkspaceId(workspace.getId());
+
+        if (paymentHistoryList.size() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PAYMENT_NOT_EXIST));
+        }
+
+        return paymentHistoryList.get(paymentHistoryList.size() - 1);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/PaymentServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/PaymentServiceImpl.java
new file mode 100644
index 0000000..06bbdad
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/PaymentServiceImpl.java
@@ -0,0 +1,789 @@
+package kr.wisestone.owl.service.impl;
+
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.EmailType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.PaymentRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.DateUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.vo.PaymentHistoryVo;
+import kr.wisestone.owl.vo.PaymentVo;
+import kr.wisestone.owl.vo.UserVo;
+import kr.wisestone.owl.web.form.PaymentForm;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.UriComponentsBuilder;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class PaymentServiceImpl extends AbstractServiceImpl<Payment, Long, JpaRepository<Payment, Long>> implements PaymentService {
+
+    private static final Logger log = LoggerFactory.getLogger(PaymentServiceImpl.class);
+
+    @Autowired
+    private PaymentRepository paymentRepository;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private UserWorkspaceService userWorkspaceService;
+
+    @Autowired
+    private ReservationDisableUserService reservationDisableUserService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private SystemEmailService systemEmailService;
+
+    @Autowired
+    private PaymentHistoryService paymentHistoryService;
+
+    @Value("${saas.usdkrw}")
+    private Integer usdKrw;
+
+    @Value("${payment.cancel.manager.email}")
+    private String paymentCancelManagerEmail;
+
+    private static final String IAMPORT_SERVER_URL = "https://api.iamport.kr";
+    private static final String ACCESS_TOKEN_REQUEST_URL = "/users/getToken";
+    private static final String IMP_KEY = "4101736461182334";
+    private static final String IMP_SECRET = "HjTiwAyYJD8r0NRDY0wcmQG989bsOTGvczOJamIT0Rl0FdEDdaQz1wJ9GUCuNj00TOK3btWclic0vREI";
+    private static final String SUBSCRIBE_PAYMENTS_ONETIME = "/subscribe/payments/onetime";  //  理쒖큹 寃곗젣�븷�븣
+    private static final String SUBSCRIBE_PAYMENTS_AGAIN = "/subscribe/payments/again";  //  �젙湲� 寃곗젣�븷�븣
+    private static final String PAYMENTS_CANCEL_URL = "/payments/cancel";    //  痍⑥냼�븷�븣
+    private static final int DEFAULT_BILLING_AMOUNT = 9;
+    private static final int BILLING_AMOUNT = 6;
+
+    @Override
+    protected JpaRepository<Payment, Long> getRepository() {
+        return this.paymentRepository;
+    }
+
+    //  利됱떆 寃곗젣
+    @Override
+    @Transactional
+    public void immediateAddUser(PaymentForm paymentForm) {
+        Workspace workspace = this.workspaceService.findOne(paymentForm.getWorkspaceId());
+        //  �꽌踰꾩뿉�꽌 寃곗옱 湲덉븸�쓣 �떎�떆 怨꾩궛�븳�떎.
+        int definiteAmount = this.calculateDailyAmount(paymentForm.getBuyUser(), workspace.getExpireDate());
+        paymentForm.setPaymentAmount(definiteAmount);
+
+        //  寃곗옱 �떎�뻾
+        RestClientResultObject resultObject = this.executePayment(paymentForm, workspace);
+        //  �엳�뒪�넗由� �깮�꽦
+        PaymentHistory paymentHistory = this.paymentHistoryService.addPaymentHistory(resultObject, paymentForm, workspace);
+        //  寃곗옱 寃곌낵 泥섎━
+        this.paymentResultProcess(paymentHistory, workspace, paymentForm, true);
+    }
+
+    private int calculateDailyAmount(int buyUser, Date expireDate) {
+
+        int expireDateTerm = DateUtil.getDateDiff(new Date(), expireDate) + 1;
+
+        //  �궗�슜�옄媛� 1紐낆씠 �븞�맆 �븣�뒗 �삤瑜� 諛쒖깮
+        if (buyUser < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PAYMENT_BUY_USER_MUST_BE_GREATER_THAN_ZERO));
+        }
+
+        if (expireDateTerm > 29) {
+            //  湲곕낯 �궗�슜�옄(10紐�)�쓣 珥덇낵�븳 寃곗젣瑜� 吏꾪뻾�뻽�쓣 �븣 寃곗젣 湲덉븸�쓣 怨꾩궛�븳�떎.
+            return this.calculateAmountDefaultUserOver(buyUser);
+        }
+        else {
+            int totalAmount = (BILLING_AMOUNT * this.usdKrw * buyUser);
+            int dayAmount = (totalAmount / 30); //  30�씪 湲곗��쑝濡� 媛�寃⑹쓣 �굹�늿�떎.
+            double sale = buyUser * 0.01;
+            double discount = 0;
+
+            //  100紐� �씠�긽 寃곗젣�떆 �븷�씤 �쟻�슜
+            if (buyUser > 99) {
+                discount = (totalAmount * (sale / 100));
+            }
+
+            return (int) Math.round((dayAmount * expireDateTerm) - discount);
+        }
+    }
+
+    //  湲곕낯 �궗�슜�옄(10紐�)�쓣 珥덇낵�븳 寃곗젣瑜� 吏꾪뻾�뻽�쓣 �븣 寃곗젣 湲덉븸�쓣 怨꾩궛�븳�떎.
+    private int calculateAmountDefaultUserOver(int buyUser) {
+        //   100�봽濡�
+        int totalAmount = (BILLING_AMOUNT * this.usdKrw * buyUser);
+        double sale = buyUser * 0.01;
+        double discount = 0;
+
+        //  100紐� �씠�긽 寃곗젣�떆 �븷�씤 �쟻�슜
+        if (buyUser > 99) {
+            discount = (totalAmount * (sale / 100));
+        }
+
+        return (int) Math.floor((totalAmount - discount));
+    }
+
+    //  �젙湲� 寃곗젣
+    @Override
+    @Transactional
+    public void paymentOneTime(PaymentForm paymentForm) {
+        Workspace workspace = this.workspaceService.getWorkspace(paymentForm.getWorkspaceId());
+        //  寃곗젣�븯�젮�뒗 �궗�슜�옄媛� �뾽臾� 怨듦컙�쓽 愿�由ъ옄�씤吏� �솗�씤�븳�떎.
+        this.checkWorkspaceManager(workspace);
+        //  援щℓ �궗�슜�옄 �닔 泥댄겕
+        this.verifyBuyUser(paymentForm);
+        //  寃곗젣 �쑀�삎 泥댄겕
+        this.verifyPaymentType(paymentForm.getType());
+
+        //  �꽌踰꾩뿉�꽌 寃곗젣 湲덉븸�쓣 �떎�떆 怨꾩궛�븳�떎.
+        int definiteAmount = this.calculateAmount(paymentForm.getBuyUser());
+        paymentForm.setPaymentAmount(definiteAmount);
+        //  寃곗젣 �떎�뻾
+        RestClientResultObject resultObject = this.executePayment(paymentForm, workspace);
+        //  �엳�뒪�넗由� �깮�꽦
+        PaymentHistory paymentHistory = this.paymentHistoryService.addPaymentHistory(resultObject, paymentForm, workspace);
+        //  寃곗젣 寃곌낵 泥섎━
+        this.paymentResultProcess(paymentHistory, workspace, paymentForm, false);
+
+        //  寃곗젣 �젙蹂대�� map �쑝濡� 留뚮뱾�뼱以��떎. - �씠硫붿씪 諛쒖넚�뿉 �궗�슜
+        Map<String, Object> paymentMap = this.makePaymentMap(workspace, this.webAppUtil.getLoginUser().getName(), paymentForm.getBuyUser(), definiteAmount, null);
+
+        //  寃곗젣 �꽦怨� 硫붿씪 諛쒖넚
+        this.systemEmailService.directEmail(new String[]{this.webAppUtil.getLoginUser().getAccount()}, EmailType.REGULAR_PAYMENT, paymentMap, this.webAppUtil.getLoginUser().getAccount());
+    }
+
+    //  寃곗젣 �젙蹂대�� map �쑝濡� 留뚮뱾�뼱以��떎. - �씠硫붿씪 諛쒖넚�뿉 �궗�슜
+    private Map<String, Object> makePaymentMap(Workspace workspace, String userName, Integer buyUser, Integer price, String email) {
+        Map<String, Object> paymentMap = new HashMap<>();
+        paymentMap.put("userName", userName);
+        paymentMap.put("workspaceName", workspace.getName());
+        paymentMap.put("storageSize", workspace.getStorageSize() / 1024 / 1024 / 1024);
+        paymentMap.put("buyUser", buyUser);
+        paymentMap.put("price", CommonUtil.getDecimalFormat(price));
+        paymentMap.put("registerDate", DateUtil.convertDateToStr(new Date()));
+        paymentMap.put("nextPaymentDay", DateUtil.convertDateToYYYYMMDD(DateUtil.addDays(workspace.getExpireDate(), 1)));
+        paymentMap.put("startDate", DateUtil.convertDateToYYYYMMDD(workspace.getStartDate()));
+        paymentMap.put("expireDate", DateUtil.convertDateToYYYYMMDD(workspace.getExpireDate()));
+        paymentMap.put("email", email);
+
+        return paymentMap;
+    }
+
+    //  寃곗젣�븯�젮�뒗 �궗�슜�옄媛� �뾽臾� 怨듦컙�쓽 愿�由ъ옄�씤吏� �솗�씤�븳�떎.
+    private void checkWorkspaceManager(Workspace workspace) {
+        UserWorkspace userWorkspace = this.userWorkspaceService.findByUserIdAndWorkspaceId(this.webAppUtil.getLoginId(), workspace.getId());
+
+        if (!userWorkspace.getManagerYn()) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PAYMENT_EXECUTE_ONLY_WORKSPACE_MANAGER));
+        }
+    }
+
+    //  �뒪耳�伊대윭濡� �븳踰� 寃곗젣�븳 �젙蹂대�� �넗��濡� �옄�룞 寃곗젣瑜� �떎�뻾�븳�떎.
+    @Override
+    @Transactional
+    public void subscribeImmediate(Workspace workspace) {
+        Payment payment = workspace.getPayment();
+
+        if (payment == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PAYMENT_NOT_EXIST));
+        }
+
+        User workspaceManager = this.userService.findByWorkspaceIdAndManagerYn(workspace);
+
+        if (workspaceManager == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.USER_WORKSPACE_MANAGER_NOT_EXIST));
+        }
+
+        PaymentForm paymentForm = ConvertUtil.copyProperties(payment, PaymentForm.class);
+        paymentForm.setCustomerUid(workspace.makeCustomerUid(workspaceManager.getAccount()));
+        paymentForm.setMerchantUid(workspaceManager.getAccount() + new Date().getTime());
+        paymentForm.setPaymentAmount(payment.getPrice());   //  �엳�뒪�넗由� �깮�꽦�븷 �븣 �궗�슜
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.add("Authorization", this.getAccessToken());
+
+        RestTemplate restTemplate = new RestTemplate();
+        restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
+
+        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
+        parameters.add("customer_uid", paymentForm.getCustomerUid());
+        parameters.add("merchant_uid", paymentForm.getMerchantUid());
+        parameters.add("amount", paymentForm.getPaymentAmount().toString());
+
+        RestClientResultObject resultObject = this.postRequest(this.makeIamportRequestUrl(SUBSCRIBE_PAYMENTS_AGAIN), new HttpEntity<>(parameters, headers));
+        //  �엳�뒪�넗由� �깮�꽦
+        PaymentHistory paymentHistory = this.paymentHistoryService.addPaymentHistory(resultObject, paymentForm, workspace);
+        //  寃곗젣 寃곌낵 泥섎━
+        this.paymentResultProcess(paymentHistory, workspace, paymentForm, false);
+        //  鍮꾪솢�꽦�솕 �궗�슜�옄 泥섎━
+        this.checkPaymentByUserCount(paymentHistory, payment, paymentForm, workspace);
+
+        List<UserWorkspace> userWorkspaces = this.userWorkspaceService.findByWorkspaceIdAndManagerYn(workspace.getId(), true);
+        //  �뾽臾� 怨듦컙 �떞�떦�옄 �씠由�
+        String workspaceManagerName = "";
+
+        if (userWorkspaces.size() > 0) {
+            workspaceManagerName = userWorkspaces.get(0).getUser().getName();
+        }
+
+        //  寃곗젣 �젙蹂대�� map �쑝濡� 留뚮뱾�뼱以��떎. - �씠硫붿씪 諛쒖넚�뿉 �궗�슜
+        Map<String, Object> paymentMap = this.makePaymentMap(workspace, workspaceManagerName, payment.getBuyUser(), payment.getPrice(), null);
+
+        //  寃곗젣 �꽦怨� 硫붿씪 諛쒖넚
+        this.systemEmailService.directEmail(new String[]{workspaceManager.getAccount()}, EmailType.REGULAR_PAYMENT, paymentMap, null);
+    }
+
+    //  寃곗젣�맂 �궗�슜�옄 �닔瑜� 泥댄겕�븳�떎.
+    private void checkPaymentByUserCount(PaymentHistory paymentHistory, Payment payment, PaymentForm paymentForm, Workspace workspace) {
+        try {
+            //  寃곗젣媛� �꽦怨듯뻽�쓣 �븣 李몄뿬 �궗�슜�옄�� 寃곗젣�맂 �궗�슜�옄 �닔瑜� 鍮꾧탳�븳�떎.
+            if (paymentHistory.getPaymentResult().equals(PaymentHistory.PAYMENT_RESULT_SUCCESS)) {
+                Integer activeUserCount = this.userWorkspaceService.countByWorkspaceIdAndUseYn(workspace.getId(), true);
+
+                //  寃곗젣�븳 �궗�슜�옄蹂대떎 李몄뿬以묒씤 �궗�슜�옄媛� 留롮쓣 寃쎌슦
+                if (workspace.getMaxUser() < activeUserCount) {
+                    this.executeWorkspaceStandbyUser(workspace, payment, activeUserCount);
+                }
+            }
+        } catch (Exception e) {
+            this.cancelPayment(paymentForm.getMerchantUid(), "");
+            this.paymentHistoryService.cancelPaymentHistory(paymentForm, workspace, "寃곗젣 �셿猷뚰썑 鍮꾪솢�꽦�솕濡� �삁�젙�맂 �궗�슜�옄 泥섎━ 怨쇱젙�뿉�꽌 �삤瑜섍� 諛쒖깮�븯���뒿�땲�떎.");
+        }
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 李몄뿬�븯�뒗 �궗�슜�옄 李몄뿬 ��湲� �긽�깭濡� 蹂�寃쏀븯湲� - 寃곗젣 �씤�썝 �닔瑜� �젣�쇅�븳 �씤�썝�뱾
+    private void executeWorkspaceStandbyUser(Workspace workspace, Payment payment, Integer activeUserCount) {
+        int disableUserCount = activeUserCount - workspace.getMaxUser();
+        //  李몄뿬 ��湲곕줈 �삁�빟�맂 �궗�슜�옄遺��꽣 李몄뿬 ��湲곕줈 �긽�깭瑜� 蹂�寃�
+        ReservationDisableUser reservationDisableUser = payment.getReservationDisableUser();
+
+        if (reservationDisableUser != null) {
+            String[] userIds = reservationDisableUser.getUserIds().split(",");
+
+            for (String userId : userIds) {
+                if (!StringUtils.isEmpty(userId)) {
+                    if (disableUserCount < 1) {
+                        break;
+                    }
+
+                    User user = this.userService.getUser(Long.valueOf(userId));
+                    //  �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 �궗�슜�옄瑜� 鍮꾪솢�꽦�솕�븳�떎.
+                    this.userWorkspaceService.disabledUserWorkspace(user, workspace);
+                    //  �궗�슜�옄瑜� �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 李몄뿬 ��湲� �긽�깭濡� 蹂�寃쏀뻽�쑝硫� 移댁슫�꽣瑜� �궡由곕떎.
+                    disableUserCount--;
+                }
+            }
+
+            payment.setReservationDisableUser(null);
+            this.paymentRepository.saveAndFlush(payment);
+        }
+
+        //  �빐�떦 �뾽臾� 怨듦컙�뿉 李몄뿬�븯�뒗 �궗�슜�옄 以� 踰덊샇媛� �넂�� �닚�꽌��濡� 李몄뿬 ��湲� �긽�깭濡� 蹂�寃쏀븳�떎.
+        List<UserWorkspace> userWorkspaces = this.userWorkspaceService.findByWorkspaceIdAndUseYn(workspace.getId(), true);
+
+        for (UserWorkspace userWorkspace : userWorkspaces) {
+            //  愿�由ъ옄�뒗 �빆�긽 李몄뿬 �긽�깭�뿬�빞 �븳�떎.
+            if (userWorkspace.getManagerYn()) {
+                continue;
+            }
+
+            if (disableUserCount < 1) {
+                break;
+            }
+
+            //  �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 �궗�슜�옄瑜� 鍮꾪솢�꽦�솕�븳�떎.
+            this.userWorkspaceService.disabledUserWorkspace(userWorkspace.getUser(), workspace);
+            //  �궗�슜�옄瑜� �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 李몄뿬 ��湲� �긽�깭濡� 蹂�寃쏀뻽�쑝硫� 移댁슫�꽣瑜� �궡由곕떎.
+            disableUserCount--;
+        }
+    }
+
+    //  �꽌踰꾩뿉�꽌 寃곗젣 湲덉븸�쓣 �떎�떆 怨꾩궛�븳�떎.
+    private int calculateAmount(int buyUser) {
+        //  �궗�슜�옄媛� 1紐낆씠 �븞�맆 �븣�뒗 �삤瑜� 諛쒖깮
+        if (buyUser < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PAYMENT_BUY_USER_MUST_BE_GREATER_THAN_ZERO));
+        }
+
+        if (buyUser < 11) {
+            return (DEFAULT_BILLING_AMOUNT * this.usdKrw);
+        }
+        else {
+            //  湲곕낯 �궗�슜�옄(10紐�)�쓣 珥덇낵�븳 寃곗젣瑜� 吏꾪뻾�뻽�쓣 �븣 寃곗젣 湲덉븸�쓣 怨꾩궛�븳�떎.
+            return this.calculateAmountDefaultUserOver(buyUser);
+        }
+    }
+
+    //  寃곗젣瑜� �떎�뻾�븳�떎.
+    private RestClientResultObject executePayment(PaymentForm paymentForm, Workspace workspace) {
+        UserVo workspaceManager = this.webAppUtil.getLoginUser();
+        paymentForm.setCustomerUid(workspace.makeCustomerUid(workspaceManager.getAccount()));
+        paymentForm.setMerchantUid(workspaceManager.getAccount() + new Date().getTime());
+        //  HttpHeader 瑜� �깮�꽦�븳�떎.
+        HttpHeaders headers = this.makeHttpHeader();
+
+        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
+        parameters.add("merchant_uid", paymentForm.getMerchantUid());
+        parameters.add("amount", paymentForm.getPaymentAmount().toString());
+        parameters.add("card_number", CommonUtil.decryptAES128(paymentForm.getCardNumber1()) + "-" + CommonUtil.decryptAES128(paymentForm.getCardNumber2())
+                + "-" + CommonUtil.decryptAES128(paymentForm.getCardNumber3()) + "-" + CommonUtil.decryptAES128(paymentForm.getCardNumber4()));
+        parameters.add("expiry", CommonUtil.decryptAES128(paymentForm.getExpireYear()) + "-" + CommonUtil.decryptAES128(paymentForm.getExpireMonth()));
+        parameters.add("birth", CommonUtil.decryptAES128(paymentForm.getBirth()));
+        parameters.add("pwd_2digit", CommonUtil.decryptAES128(paymentForm.getCardPwd()));
+        parameters.add("customer_uid", paymentForm.getCustomerUid());
+        parameters.add("buyer_name", workspaceManager.getName());
+        parameters.add("buyer_email", workspaceManager.getAccount());
+
+        return this.postRequest(this.makeIamportRequestUrl(SUBSCRIBE_PAYMENTS_ONETIME), new HttpEntity<>(parameters, headers));
+    }
+
+    //  寃곗젣 寃곌낵瑜� 泥섎━�븳�떎.
+    private PaymentHistoryVo paymentResultProcess(PaymentHistory paymentHistory, Workspace workspace, PaymentForm paymentForm, Boolean immediate) {
+        if (paymentHistory.getPaymentResult().equals(PaymentHistory.PAYMENT_RESULT_SUCCESS)) {
+            try {
+                //  異붽� 寃곗젣
+                if (immediate) {
+                    Payment payment = workspace.getPayment();
+                    //  �씤�썝 異붽�
+                    payment.setBuyUser(payment.getBuyUser() + paymentForm.getBuyUser());
+                    //  異붽� 寃곗젣濡� �씤�븳 �궗�슜湲덉븸 蹂�寃�
+                    payment.setPrice(this.calculateAmountDefaultUserOver(payment.getBuyUser()));
+                    this.paymentRepository.saveAndFlush(payment);
+                    //  異붽� 寃곗젣 �셿猷� �썑 理쒕� �궗�슜�옄 �닔, ���옣 怨듦컙�쓣 �뾽�뜲�씠�듃�븳�떎.
+                    this.workspaceService.updateWorkspaceByImmediatePayment(workspace, payment.getBuyUser());
+                }
+                else {
+                    //  �젙湲� 寃곗젣濡� 寃곗젣瑜� 吏꾪뻾�븳 寃쎌슦
+                    Payment payment = this.executePaymentOneTime(workspace, paymentForm);
+
+                    if (payment != null) {
+                        //  鍮꾪솢�꽦�솕 �궗�슜�옄 泥섎━
+                        this.checkPaymentByUserCount(paymentHistory, payment, paymentForm, workspace);
+                    }
+                }
+            } catch (Exception e) {
+                this.cancelPayment(paymentForm.getMerchantUid(), "");
+                PaymentHistory cancelPaymentHistory = this.paymentHistoryService.cancelPaymentHistory(paymentForm, workspace, "寃곗젣 怨쇱젙�뿉�꽌 �삤瑜섍� 諛쒖깮�븯���뒿�땲�떎. �떎�떆 �떆�룄�빐 二쇱꽭�슂.");
+                return ConvertUtil.copyProperties(cancelPaymentHistory, PaymentHistoryVo.class);
+            }
+        }
+        else {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(paymentHistory.getPaymentResponse()));
+        }
+
+        return ConvertUtil.copyProperties(paymentHistory, PaymentHistoryVo.class);
+    }
+
+    //  �젙湲� 寃곗젣濡� 寃곗젣瑜� 吏꾪뻾�븳 寃쎌슦
+    private Payment executePaymentOneTime(Workspace workspace, PaymentForm paymentForm) {
+        //  �떎�쓬 寃곗젣�씪 吏��젙 諛� �뾽臾� 怨듦컙�뿉 寃곗젣�븳 �떊洹� �뜲�씠�꽣 �쟻�슜
+        this.workspaceService.updateWorkspace(workspace, paymentForm);
+
+        if (workspace.getPayment() == null) {
+            //  寃곗젣媛� �셿猷뚮릺怨� 寃곗젣 �젙蹂닿� �뾽臾� 怨듦컙�뿉 �뾽�뜲�씠�듃媛� �릺硫� �젙湲� 寃곗젣瑜� �쐞�빐 寃곗젣 �젙蹂대�� �깮�꽦�븳�떎.
+            Payment payment = ConvertUtil.copyProperties(paymentForm, Payment.class);
+            payment.setPrice(paymentForm.getPaymentAmount());
+            payment.setWorkspace(workspace);
+            return this.paymentRepository.saveAndFlush(payment);
+        }
+
+        return null;
+    }
+
+    //  寃곗젣 吏꾪뻾以� 諛쒖깮�븳 �삤瑜섎줈 �씤�븳 寃곗젣 痍⑥냼 �슂泥�
+    private String cancelPayment(String merchantUid, String reason) {
+        //  HttpHeader 瑜� �깮�꽦�븳�떎.
+        HttpHeaders headers = this.makeHttpHeader();
+        //  RestTemplate 瑜� �깮�꽦�븳�떎.
+        RestTemplate restTemplate = this.makeRestTemplate();
+
+        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
+        parameters.add("merchant_uid", merchantUid);
+        parameters.add("reason", reason);
+
+        URI targetUrl = UriComponentsBuilder.fromUriString(IAMPORT_SERVER_URL)
+                .path(PAYMENTS_CANCEL_URL)
+                .build()
+                .toUri();
+
+        String url = null;
+
+        try {
+            url = URLDecoder.decode(targetUrl.toString(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+
+        ResponseEntity<String> result = restTemplate.postForEntity(url, new HttpEntity<>(parameters, headers), String.class);
+        Map<String, Object> resultMap = ConvertUtil.convertJsonToMap(result.getBody());
+        return StringEscapeUtils.unescapeJava(MapUtil.getString(resultMap, "response"));
+    }
+
+    //  HttpHeader 瑜� �깮�꽦�븳�떎.
+    private HttpHeaders makeHttpHeader() {
+        HttpHeaders headers = new HttpHeaders();
+        headers.add("Authorization", this.getAccessToken());
+
+        return headers;
+    }
+
+    //  RestTemplate 瑜� �깮�꽦�븳�떎.
+    private RestTemplate makeRestTemplate() {
+        RestTemplate restTemplate = new RestTemplate();
+        restTemplate.getMessageConverters()
+                .add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
+
+        return restTemplate;
+    }
+
+    //  �젙湲� 寃곗젣 痍⑥냼�븯湲�
+    @Override
+    @Transactional
+    public void cancelNextPayment(PaymentForm paymentForm) {
+        Workspace workspace = this.workspaceService.getWorkspace(paymentForm.getWorkspaceId());
+        //  寃곗젣 痍⑥냼�븯�젮�뒗 �궗�슜�옄媛� �뾽臾� 怨듦컙�쓽 愿�由ъ옄�씤吏� �솗�씤�븳�떎.
+        this.checkWorkspaceManager(workspace);
+
+        Payment payment = workspace.getPayment();
+
+        if (payment == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PAYMENT_NOT_EXIST));
+        }
+        //  �뾽臾� 怨듦컙 �젙湲� 寃곗젣 痍⑥냼 - �젙湲� 寃곗젣 �젙蹂� �궘�젣
+        this.workspaceService.cancelWorkspacePayment(workspace);
+
+        //  寃곗젣 �젙蹂대�� map �쑝濡� 留뚮뱾�뼱以��떎. - �씠硫붿씪 諛쒖넚�뿉 �궗�슜
+        Map<String, Object> paymentMap = this.makePaymentMap(workspace, this.webAppUtil.getLoginUser().getName(), 0, 0, CommonUtil.decryptAES128(this.webAppUtil.getLoginUser().getAccount()));
+        paymentMap.put("maxUser", workspace.getMaxUser());
+
+        try {
+            //  �삤�뒛 寃곗젣�븳 �뾽臾� 怨듦컙�쓣 �궗�슜�옄媛� 痍⑥냼�뻽�뒗吏� �솗�씤�븳�떎.
+            PaymentHistory paymentHistory = this.paymentHistoryService.findByWorkspaceLastPaymentHistory(workspace);
+
+            //  �떦�씪 寃곗젣 痍⑥냼�씪 寃쎌슦
+            if (paymentHistory.getRegisterDate().compareTo(new Date()) == 0) {
+                paymentMap.put("refundPrice", CommonUtil.getDecimalFormat(paymentHistory.getPrice())); //  �솚遺� 湲덉븸
+                //  �쉶怨� �떞�떦�옄 �씠硫붿씪 二쇱냼 �븫�샇�솕
+                String encryptAccountingManager = CommonUtil.encryptAES128(this.paymentCancelManagerEmail);
+                //  �쉶怨� �떞�떦�옄�뿉寃� 寃곗젣 痍⑥냼 �븣由� 硫붿씪
+                this.systemEmailService.directEmail(new String[]{encryptAccountingManager}, EmailType.REGULAR_PAYMENT_CANCEL_BY_ACCOUNTING_MANAGER, paymentMap, encryptAccountingManager);
+            }
+
+        } catch (Exception e) {
+            log.debug("�솚遺� �븣由� 硫붿씪 �삤瑜� 諛쒖깮" + e.getMessage());
+        }
+
+        //  �젙湲� 寃곗젣 痍⑥냼 �븣由� 硫붿씪
+        this.systemEmailService.directEmail(new String[]{this.webAppUtil.getLoginUser().getAccount()}, EmailType.REGULAR_PAYMENT_CANCEL, paymentMap, this.webAppUtil.getLoginUser().getAccount());
+    }
+
+    @Override
+    public String getAccessToken() {
+        RestTemplate restTemplate = new RestTemplate();
+        restTemplate.getMessageConverters()
+                .add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
+
+        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
+        parameters.add("imp_key", IMP_KEY);
+        parameters.add("imp_secret", IMP_SECRET);
+
+        URI targetUrl = UriComponentsBuilder.fromUriString(IAMPORT_SERVER_URL)
+                .path(ACCESS_TOKEN_REQUEST_URL)
+                .build()
+                .toUri();
+
+        String url = "";
+
+        try {
+            url = URLDecoder.decode(targetUrl.toString(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+
+        String result = restTemplate.postForObject(url, parameters, String.class);
+        Map<String, Object> resultMap = ConvertUtil.convertJsonToMap(result);
+        Map<String, Object> tokenMap = (Map<String, Object>) resultMap.get("response");
+        return MapUtil.getString(tokenMap, "access_token");
+    }
+
+    //  寃곗젣 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailPayment(Map<String, Object> resJsonData, PaymentForm paymentForm) {
+        Workspace workspace = this.workspaceService.findOne(paymentForm.getWorkspaceId());
+        Payment payment = workspace.getPayment();
+
+        if (payment != null) {
+            resJsonData.put(Constants.RES_KEY_CONTENTS, ConvertUtil.copyProperties(payment, PaymentVo.class));
+        }
+    }
+
+    //  寃곗젣 �젙蹂대�� �닔�젙�븳�떎.
+    @Override
+    @Transactional
+    public PaymentVo modifyPayment(PaymentForm paymentForm) {
+        Workspace workspace = this.workspaceService.findOne(paymentForm.getWorkspaceId());
+        //  寃곗젣�븯�젮�뒗 �궗�슜�옄媛� �뾽臾� 怨듦컙�쓽 愿�由ъ옄�씤吏� �솗�씤�븳�떎.
+        this.checkWorkspaceManager(workspace);
+
+        Payment payment = workspace.getPayment();
+
+        Map<String, Object> paymentMap = new HashMap<>();
+        paymentMap.put("beforeMaxUser", payment.getBuyUser());    //  �씠�쟾 理쒕� �궗�슜�옄 �닔
+        paymentMap.put("beforePrice", CommonUtil.getDecimalFormat(payment.getPrice()));  //  �씠�쟾 媛�寃�
+
+        //  寃곗젣 �궗�슜�옄 �닔 泥댄겕
+        this.verifyBuyUser(paymentForm);
+
+        //  寃곗젣 �쑀�삎 泥댄겕
+        this.verifyPaymentType(paymentForm.getType());
+        payment.setBuyUser(paymentForm.getBuyUser());
+        payment.setType(paymentForm.getType());
+
+        //  �꽌踰꾩뿉�꽌 寃곗젣 湲덉븸�쓣 �떎�떆 怨꾩궛�븳�떎.
+        int definiteAmount = this.calculateAmount(paymentForm.getBuyUser());
+        payment.setPrice(definiteAmount);
+
+        if (payment.getReservationDisableUser() != null) {
+            payment.setReservationDisableUser(null);
+        }
+
+        this.paymentRepository.saveAndFlush(payment);
+        this.reservationDisableUserService.add(paymentForm, payment);
+
+        paymentMap.put("userName", this.webAppUtil.getLoginUser().getName());
+        paymentMap.put("workspaceName", workspace.getName());
+        paymentMap.put("maxUser", paymentForm.getBuyUser());
+        paymentMap.put("price", CommonUtil.getDecimalFormat(definiteAmount));
+        paymentMap.put("nextPaymentDay", DateUtil.convertDateToYYYYMMDD(DateUtil.addDays(workspace.getExpireDate(), 1)));
+
+        //  �젙湲� 寃곗젣 蹂�寃� �븣由� 硫붿씪
+        this.systemEmailService.directEmail(new String[]{this.webAppUtil.getLoginUser().getAccount()}, EmailType.REGULAR_PAYMENT_MODIFY, paymentMap, this.webAppUtil.getLoginUser().getAccount());
+
+        return ConvertUtil.copyProperties(payment, PaymentVo.class);
+    }
+
+    //  寃곗젣 �궗�슜�옄 �닔 泥댄겕
+    private void verifyBuyUser(PaymentForm paymentForm) {
+        if (paymentForm.getBuyUser() == null || paymentForm.getBuyUser() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PAYMENT_BUY_USER_MUST_BE_GREATER_THAN_ZERO));
+        }
+
+        //  1紐낆쑝濡� 援ъ엯�빐�룄 10�씤源뚯� �궗�슜 媛��뒫�븯�룄濡� 蹂�寃�
+        if (paymentForm.getBuyUser() < 10) {
+            paymentForm.setBuyUser(10);
+        }
+    }
+
+    //  寃곗젣 �쑀�삎 泥댄겕
+    private void verifyPaymentType(String type) {
+        if (StringUtils.isEmpty(type)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PAYMENT_NO_TYPE));
+        }
+    }
+
+    //  寃곗젣 �븘�씠�뵒濡� 寃곗젣瑜� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Payment getPayment(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PAYMENT_NOT_EXIST));
+        }
+
+        Payment payment = this.findOne(id);
+
+        if (payment == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PAYMENT_NOT_EXIST));
+        }
+
+        return payment;
+    }
+
+    //  寃곗젣 湲덉븸�뿉 �솚�쑉 �젙蹂대�� �뾽�뜲�씠�듃�븳�떎.
+    @Override
+    @Transactional
+    public void updateExchangeRatePayment() {
+        List<Payment> payments = this.paymentRepository.findAll();
+
+        for (Payment payment : payments) {
+            //  �꽌踰꾩뿉�꽌 寃곗젣 湲덉븸�쓣 �떎�떆 怨꾩궛�븳�떎.
+            int definiteAmount = this.calculateAmount(payment.getBuyUser());
+            payment.setPrice(definiteAmount);
+        }
+
+        this.paymentRepository.saveAll(payments);
+    }
+
+    private URI makeIamportRequestUrl(String requestPath) {
+        return UriComponentsBuilder.fromUriString(IAMPORT_SERVER_URL)
+                .path(requestPath)
+                .build()
+                .toUri();
+    }
+
+    private RestClientResultObject postRequest(URI targetUrl, HttpEntity<MultiValueMap<String, String>> request) {
+        RestClientResultObject restResult = new RestClientResultObject();
+        RestTemplate restTemplate = new RestTemplate();
+        restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
+
+        try {
+            String url = URLDecoder.decode(targetUrl.toString(), "UTF-8");
+            ResponseEntity<String> result = restTemplate.postForEntity(url, request, String.class);
+            Map<String, Object> resultMap = ConvertUtil.convertJsonToMap(result.getBody());
+
+            restResult.setHttpStatus(result.getStatusCode());
+            restResult.setCode(MapUtil.getString(resultMap, "code"));
+
+            if (resultMap.containsKey("message")) {
+                restResult.setMessage(StringEscapeUtils.unescapeJava(MapUtil.getString(resultMap, "message")));
+            }
+
+            if (resultMap.containsKey("response")) {
+                Map<String, Object> response = (Map<String, Object>) resultMap.get("response");
+                restResult.setResponse(response);
+
+                String responseStatus = MapUtil.getString(response, "status");
+                String failReason = MapUtil.getString(response, "fail_reason");
+
+                restResult.setIamportStatus(responseStatus);
+
+                Boolean isNotPaidResult = !"paid".equals(responseStatus);
+
+                if (isNotPaidResult) {
+                    restResult.setIamportFailReason(failReason);
+                }
+            }
+
+        } catch (HttpClientErrorException e) {
+
+            restResult.setMessage(e.getMessage());
+            restResult.setCode(e.getStatusCode().toString());
+            restResult.setHttpStatus(e.getStatusCode());
+
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+
+        return restResult;
+    }
+
+    public static class RestClientResultObject {
+        private static final String CODE_RESULT_SUCCESS = "0";
+        private static final String CODE_RESULT_FAILED = "-1";
+        private static final String IAMPORT_STATUS_PAID = "paid";
+
+
+        private HttpStatus httpStatus;
+        private String code;
+        private String message;
+        private Map<String, Object> response;
+        private String iamportStatus;
+        private String iamportFailReason;
+
+        public Boolean isValidResult() {
+            if (isHttpRequestFailed() || isIamportResultFailed() || isIamportPaymentFailed()) {
+                return Boolean.FALSE;
+            }
+
+            return Boolean.TRUE;
+        }
+
+        public Boolean isHttpRequestFailed() {
+            return !HttpStatus.OK.equals(this.getHttpStatus());
+        }
+
+        public Boolean isIamportResultFailed() {
+            return CODE_RESULT_FAILED.equals(this.getCode());
+        }
+
+        public Boolean isIamportPaymentFailed() {
+            return !IAMPORT_STATUS_PAID.equals(this.getIamportStatus());
+        }
+
+        public HttpStatus getHttpStatus() {
+            return httpStatus;
+        }
+
+        public void setHttpStatus(HttpStatus httpStatus) {
+            this.httpStatus = httpStatus;
+        }
+
+        public String getCode() {
+            return code;
+        }
+
+        public void setCode(String code) {
+            this.code = code;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        public void setMessage(String message) {
+            this.message = message;
+        }
+
+        public Map<String, Object> getResponse() {
+            return response;
+        }
+
+        public void setResponse(Map<String, Object> response) {
+            this.response = response;
+        }
+
+        public String getIamportStatus() {
+            return iamportStatus;
+        }
+
+        public void setIamportStatus(String iamportStatus) {
+            this.iamportStatus = iamportStatus;
+        }
+
+        public String getIamportFailReason() {
+            return iamportFailReason;
+        }
+
+        public void setIamportFailReason(String iamportFailReason) {
+            this.iamportFailReason = iamportFailReason;
+        }
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/PermissionServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/PermissionServiceImpl.java
new file mode 100644
index 0000000..0817af0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/PermissionServiceImpl.java
@@ -0,0 +1,75 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.Permission;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.repository.PermissionRepository;
+import kr.wisestone.owl.service.PermissionService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.PermissionVo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+
+@Service
+public class PermissionServiceImpl extends AbstractServiceImpl<Permission, Long, JpaRepository<Permission, Long>> implements PermissionService {
+
+    private static final Logger log = LoggerFactory.getLogger(PermissionServiceImpl.class);
+
+    @Autowired
+    private PermissionRepository permissionRepository;
+
+    @Override
+    protected JpaRepository<Permission, Long> getRepository() {
+        return this.permissionRepository;
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public List<PermissionVo> findByUserId() {
+
+        User user = this.webAppUtil.getLoginUserObject();
+
+        List<PermissionVo> allPermission = ConvertUtil.convertObjectsToClasses(this.permissionRepository.findAll(), PermissionVo.class);
+        List<PermissionVo> userPermissions = Lists.newArrayList();
+
+        if (user != null) {
+            userPermissions.addAll(ConvertUtil.convertObjectsToClasses(this.permissionRepository.findByUserId(user.getId()), PermissionVo.class));
+        }
+
+        this.setPermissionActiveYn(allPermission, userPermissions);
+
+        return allPermission;
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public List<Permission> findByRoleType(String roleType) {
+        return this.permissionRepository.findByRoleType(roleType);
+    }
+
+    private void setPermissionActiveYn(List<PermissionVo> allPermissionVos, List<PermissionVo> userPermissionVos) {
+        for (PermissionVo permissionVo : allPermissionVos) {
+
+            final Long permissionId = permissionVo.getId();
+
+            PermissionVo userPermission = Iterables.find(userPermissionVos, new Predicate<PermissionVo>() {
+                @Override
+                public boolean apply(PermissionVo input) {
+                    return input.getId().equals(permissionId);
+                }
+            }, null);
+
+            if (userPermission != null) {
+                permissionVo.setActiveYn(Boolean.TRUE);
+            }
+
+        }
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/PriorityServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/PriorityServiceImpl.java
new file mode 100644
index 0000000..8dac7b1
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/PriorityServiceImpl.java
@@ -0,0 +1,99 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.Priority;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.PriorityRepository;
+import kr.wisestone.owl.service.PriorityService;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.service.WorkspaceService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.PriorityVo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class PriorityServiceImpl extends AbstractServiceImpl<Priority, Long, JpaRepository<Priority, Long>> implements PriorityService {
+
+    private static final Logger log = LoggerFactory.getLogger(PriorityServiceImpl.class);
+
+    @Autowired
+    private PriorityRepository priorityRepository;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    protected JpaRepository<Priority, Long> getRepository() {
+        return this.priorityRepository;
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public List<Priority> findByWorkspaceIdOrderByPosition() {
+        Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        return this.priorityRepository.findByWorkspaceIdOrderByPosition(workspace.getId());
+    }
+
+    //  �슦�꽑�닚�쐞 �븘�씠�뵒濡� �슦�꽑�닚�쐞瑜� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Priority getPriority(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PRIORITY_NOT_EXIST));
+        }
+
+        Priority priority = this.findOne(id);
+
+        if (priority == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PRIORITY_NOT_EXIST));
+        }
+
+        return priority;
+    }
+
+    //  �슦�꽑�닚�쐞 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<PriorityVo> findPriority(Map<String, Object> resJsonData) {
+        List<PriorityVo> priorityVos = ConvertUtil.convertObjectsToClasses(this.findByWorkspaceIdOrderByPosition(), PriorityVo.class);
+        resJsonData.put(Constants.RES_KEY_CONTENTS, priorityVos);
+
+        return priorityVos;
+    }
+
+
+    //  湲곕낯�쟻�쑝濡� �젣怨듬릺�뒗 �슦�꽑�닚�쐞 紐⑸줉
+    @Override
+    @Transactional
+    public void addDefaultPriority(Workspace workspace) {
+        List<Priority> priorities = Lists.newArrayList();
+        priorities.add(new Priority(this.messageAccessor.message("common.urgent"), 1, "#ed5565", workspace));
+        priorities.add(new Priority(this.messageAccessor.message("common.high"), 2, "#f8ac59", workspace));
+        priorities.add(new Priority(this.messageAccessor.message("common.medium"), 3, "#1c84c6", workspace));
+        priorities.add(new Priority(this.messageAccessor.message("common.low"), 4, "#23c6c8", workspace));
+
+        this.priorityRepository.saveAll(priorities);
+    }
+
+    //  �뾽臾닿났媛꾩뿉 �엳�뒗 �슦�꽑 �닚�쐞 紐⑸줉�쓣 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<Priority> findByWorkspaceId() {
+        return this.priorityRepository.findByWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/ProjectClosureServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/ProjectClosureServiceImpl.java
new file mode 100644
index 0000000..48dfa1d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/ProjectClosureServiceImpl.java
@@ -0,0 +1,30 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.domain.Priority;
+import kr.wisestone.owl.domain.Project;
+import kr.wisestone.owl.domain.ProjectClosure;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.repository.ProjectClosureRepository;
+import kr.wisestone.owl.service.ProjectClosureService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Set;
+
+@Service
+public class ProjectClosureServiceImpl extends AbstractServiceImpl<ProjectClosure, Long, JpaRepository<ProjectClosure, Long>> implements ProjectClosureService {
+    private static final Logger log = LoggerFactory.getLogger(ProjectClosureServiceImpl.class);
+
+    @Autowired
+    private ProjectClosureRepository projectClosureRepository;
+
+    @Override
+    protected JpaRepository<ProjectClosure, Long> getRepository() {
+        return this.projectClosureRepository;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/ProjectRolePermissionServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/ProjectRolePermissionServiceImpl.java
new file mode 100644
index 0000000..d511ce9
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/ProjectRolePermissionServiceImpl.java
@@ -0,0 +1,48 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.Permission;
+import kr.wisestone.owl.domain.ProjectRole;
+import kr.wisestone.owl.domain.ProjectRolePermission;
+import kr.wisestone.owl.repository.ProjectRolePermissionRepository;
+import kr.wisestone.owl.service.PermissionService;
+import kr.wisestone.owl.service.ProjectRolePermissionService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+
+@Service
+public class ProjectRolePermissionServiceImpl extends AbstractServiceImpl<ProjectRolePermission, Long, JpaRepository<ProjectRolePermission, Long>> implements ProjectRolePermissionService {
+
+    private static final Logger log = LoggerFactory.getLogger(ProjectRolePermissionServiceImpl.class);
+
+    @Autowired
+    private ProjectRolePermissionRepository projectRolePermissionRepository;
+
+    @Autowired
+    private PermissionService permissionService;
+
+    @Override
+    protected JpaRepository<ProjectRolePermission, Long> getRepository() {
+        return this.projectRolePermissionRepository;
+    }
+
+    //  �빐�떦 �봽濡쒖젥�듃 �뿭�븷怨� 沅뚰븳�쓣 �뿰寃곗떆�궓�떎.
+    @Override
+    @Transactional
+    public void addDefaultProjectRoleAssociatedPermissions(ProjectRole projectRole, String roleType) {
+        List<Permission> permissions = this.permissionService.findByRoleType(roleType);
+        List<ProjectRolePermission> projectRolePermissions = Lists.newArrayList();
+
+        permissions.parallelStream().forEach(permission -> {
+            ProjectRolePermission projectRolePermission = new ProjectRolePermission(projectRole, permission);
+            projectRolePermissions.add(projectRolePermission);
+        });
+
+        this.projectRolePermissionRepository.saveAll(projectRolePermissions);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/ProjectRoleServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/ProjectRoleServiceImpl.java
new file mode 100644
index 0000000..2795517
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/ProjectRoleServiceImpl.java
@@ -0,0 +1,87 @@
+package kr.wisestone.owl.service.impl;
+
+
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.ProjectRoleRepository;
+import kr.wisestone.owl.service.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+
+@Service
+public class ProjectRoleServiceImpl extends AbstractServiceImpl<ProjectRole, Long, JpaRepository<ProjectRole, Long>> implements ProjectRoleService {
+
+    private static final Logger log = LoggerFactory.getLogger(ProjectRoleServiceImpl.class);
+
+    @Autowired
+    private ProjectRoleRepository projectRoleRepository;
+
+    @Autowired
+    private ProjectRoleUserService projectRoleUserService;
+
+    @Autowired
+    private ProjectRolePermissionService projectRolePermissionService;
+
+    @Override
+    protected JpaRepository<ProjectRole, Long> getRepository() {
+        return this.projectRoleRepository;
+    }
+
+    //  湲곕낯, 愿�由ъ옄 �봽濡쒖젥�듃 �뿭�븷�쓣 �깮�꽦�븳�떎.
+    @Override
+    @Transactional
+    public void addDefaultProjectRole(Project project, List<User> managers, List<User> users) {
+        ProjectRole projectRole = this.addProjectRole(project, "湲곕낯 �봽濡쒖젥�듃 �뿭�븷", ProjectRole.TYPE_DEFAULT, Permission.ROLE_TYPE_PROJECT_JOIN);
+        ProjectRole managerProjectRole = this.addProjectRole(project, "�봽濡쒖젥�듃 愿�由ъ옄 �뿭�븷", ProjectRole.TYPE_MANAGER, Permission.ROLE_TYPE_PROJECT_MANAGER);
+        //  �봽濡쒖젥�듃 愿�由ъ옄 ���옣
+        this.projectRoleAssociatedUser(managers, managerProjectRole);
+        //  �봽濡쒖젥�듃 �씪諛� �궗�슜�옄 ���옣
+        this.projectRoleAssociatedUser(users, projectRole);
+
+        this.projectRoleRepository.flush();
+    }
+
+    //  �봽濡쒖젥�듃 �뿭�븷媛� �궗�슜�옄 �뿰寃�
+    private void projectRoleAssociatedUser(List<User> users, ProjectRole projectRole) {
+        for (User user : users) {
+            this.addDefaultProjectRoleAssociatedUser(projectRole, user);
+        }
+    }
+
+    //  �빐�떦 �뿭�븷怨� �궗�슜�옄瑜� �뿰寃곗떆�궓�떎.
+    private void addDefaultProjectRoleAssociatedUser(ProjectRole projectRole, User user) {
+        ProjectRoleUser projectRoleUser = this.projectRoleUserService.findByProjectRoleIdAndUserId(projectRole.getId(), user.getId());
+
+        if (projectRoleUser == null) {
+            //  �봽濡쒖젥�듃 愿�由ъ옄 - 湲곕낯 �봽濡쒖젥�듃 �뿭�븷�뿉 異붽�.
+            user.addProjectRole(projectRole);
+        }
+    }
+
+    //  �봽濡쒖젥�듃 �뿭�븷�쓣 �깮�꽦�븳�떎.
+    private ProjectRole addProjectRole(Project project, String projectRoleName, String projectRoleType, String permissionType) {
+        ProjectRole projectRole = new ProjectRole(project, projectRoleName, projectRoleType);
+        this.projectRoleRepository.saveAndFlush(projectRole);
+        this.projectRolePermissionService.addDefaultProjectRoleAssociatedPermissions(projectRole, permissionType);
+        return projectRole;
+    }
+
+    //  �빐�떦 �봽濡쒖젥�듃 �뿭�븷�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public ProjectRole findByProjectIdAndRoleType(Long projectId, String roleType) {
+        ProjectRole projectRole = this.projectRoleRepository.findByProjectIdAndRoleType(projectId, roleType);
+
+        if (projectRole == null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.PROJECT_ROLE_NOT_EXIST));
+        }
+
+        return projectRole;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/ProjectRoleUserServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/ProjectRoleUserServiceImpl.java
new file mode 100644
index 0000000..f158fe0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/ProjectRoleUserServiceImpl.java
@@ -0,0 +1,140 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.ProjectRoleUserMapper;
+import kr.wisestone.owl.repository.ProjectRoleUserRepository;
+import kr.wisestone.owl.service.ProjectRoleService;
+import kr.wisestone.owl.service.ProjectRoleUserService;
+import kr.wisestone.owl.service.UserWorkspaceService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class ProjectRoleUserServiceImpl extends AbstractServiceImpl<ProjectRoleUser, Long, JpaRepository<ProjectRoleUser, Long>> implements ProjectRoleUserService {
+
+    private static final Logger log = LoggerFactory.getLogger(ProjectRoleUserServiceImpl.class);
+
+    @Autowired
+    private ProjectRoleUserRepository projectRoleUserRepository;
+
+    @Autowired
+    private ProjectRoleService projectRoleService;
+
+    @Autowired
+    private UserWorkspaceService userWorkspaceService;
+
+    @Autowired
+    private ProjectRoleUserMapper projectRoleUserMapper;
+
+    @Override
+    protected JpaRepository<ProjectRoleUser, Long> getRepository() {
+        return this.projectRoleUserRepository;
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public List<ProjectRoleUser> findByProjectRoleId(Long projectRoleId) {
+        return this.projectRoleUserRepository.findByProjectRoleId(projectRoleId);
+    }
+
+    //  �빐�떦 �궗�슜�옄媛� �듅�젙 �뿭�븷�뿉 �냼�냽�릺�뼱 �엳�뒗吏� �솗�씤�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public ProjectRoleUser findByProjectRoleIdAndUserId(Long projectRoleId, Long userId) {
+        return this.projectRoleUserRepository.findByProjectRoleIdAndUserId(projectRoleId, userId);
+    }
+
+    //  �뾽臾� 怨듦컙�쓣 �깉�눜�븳 �궗�슜�옄媛� �떎瑜� �뾽臾� 怨듦컙�쓽 �봽濡쒖젥�듃 愿�由ъ옄, �씪諛� �궗�슜�옄濡� �엳�쓣 寃쎌슦 �젣嫄고븳�떎.
+    @Override
+    @Transactional
+    public void withDrawWorkspaceManagerModifyProjectRole(Workspace workspace, User user) {
+        List<UserWorkspace> userWorkspaces = this.userWorkspaceService.findByWorkspaceIdAndManagerYn(workspace.getId(), true);
+
+        if (userWorkspaces.size() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.USER_WORKSPACE_MANAGER_NOT_EXIST));
+        }
+
+        User workspaceManager = userWorkspaces.get(0).getUser();
+
+        for (Project project : workspace.getProjects()) {
+            //  �씪諛� 李몄뿬 �뿭�븷 �궘�젣
+            this.removeProjectRoleUser(project, ProjectRole.TYPE_DEFAULT, user, workspaceManager);
+            //  �봽濡쒖젥�듃 愿�由ъ옄 �뿭�븷 �궘�젣
+            this.removeProjectRoleUser(project, ProjectRole.TYPE_MANAGER, user, workspaceManager);
+        }
+    }
+
+    //  �봽濡쒖젥�듃 �뿭�븷 �뿰寃� �젙蹂댁뿉�꽌 �깉�눜�븯�뒗 �궗�슜�옄瑜� �젣嫄고븳�떎.
+    private void removeProjectRoleUser(Project project, String roleType, User user, User workspaceManager) {
+        ProjectRole projectRole = this.projectRoleService.findByProjectIdAndRoleType(project.getId(), roleType);
+
+        //  �봽濡쒖젥�듃 愿�由ъ옄 �뿭�븷�씪 �븣
+        if (roleType.equals(ProjectRole.TYPE_MANAGER)) {
+            //  �봽濡쒖젥�듃 愿�由ъ옄 �뿰寃� �젙蹂�
+            List<ProjectRoleUser> projectRoleUsers = this.findByProjectRoleId(projectRole.getId());
+            boolean existManager = false;
+
+            //  �깉�눜�븳 �궗�슜�옄媛� �봽濡쒖젥�듃 愿�由ъ옄 �뿬遺� �솗�씤
+            for (ProjectRoleUser projectRoleUser : projectRoleUsers) {
+                if (projectRoleUser.getUser().getId().equals(user.getId())) {
+                    existManager = true;
+                    break;
+                }
+            }
+
+            if (existManager) {
+                //  �뾽臾� 怨듦컙 愿�由ъ옄媛� �씪諛� �궗�슜�옄 �뿭�븷�뿉 議댁옱�븷 �닔 �엳�쑝誘�濡� �젣嫄� 濡쒖쭅 �닔�뻾
+                this.removeProjectRoleUser(project, ProjectRole.TYPE_DEFAULT, workspaceManager, null);
+                //  �뾽臾� 怨듦컙 愿�由ъ옄�뿉寃� �봽濡쒖젥�듃 愿�由ъ옄 �뿭�븷 遺��뿬
+                List<Map<String, Long>> projectRoleUserMaps = Lists.newArrayList();
+                Map<String, Long> projectRoleUserMap = new HashMap<>();
+                projectRoleUserMap.put("projectRoleId", projectRole.getId());
+                projectRoleUserMap.put("userId", workspaceManager.getId());
+                projectRoleUserMap.put("registerId", this.webAppUtil.getLoginId());
+                projectRoleUserMaps.add(projectRoleUserMap);
+                //  �뾽臾� 怨듦컙 愿�由ъ옄瑜� �봽濡쒖젥�듃 愿�由ъ옄濡� 吏��젙
+                this.projectRoleUserMapper.insertProjectRoleUser(projectRoleUserMaps);
+
+                /*workspaceManager.addProjectRole(projectRole);*/
+            }
+        }
+
+        Map<String, Long> deleteProjectRoleUserMap = new HashMap<>();
+        deleteProjectRoleUserMap.put("projectRoleId", projectRole.getId());
+        deleteProjectRoleUserMap.put("userId", user.getId());
+        //  �깉�눜�븳 �궗�슜�옄�뒗 �봽濡쒖젥�듃 �뿭�븷 �뿰寃� �젙蹂� �궘�젣
+        this.projectRoleUserMapper.deleteProjectRoleUser(deleteProjectRoleUserMap);
+
+        //  �깉�눜�븳 �궗�슜�옄�뒗 �봽濡쒖젥�듃 �뿭�븷 �뿰寃� �젙蹂� �궘�젣
+        //  user.removeProjectRole(projectRole);
+    }
+
+    //  �봽濡쒖젥�듃�뿉 李몄뿬�븯�뒗 �궗�슜�옄, �봽濡쒖젥�듃 愿�由ъ옄瑜� �뙆�씪誘명꽣�뿉 �뵲�씪 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<Map<String, Object>> findProjectRoleUser(Map<String, Object> projectRoleUserMap) {
+        return this.projectRoleUserMapper.findProjectRoleUser(projectRoleUserMap);
+    }
+
+    //  �봽濡쒖젥�듃 愿�由ъ옄 �뿬遺�瑜� �솗�씤�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public boolean checkProjectManager(Project project) {
+        ProjectRole projectManagerRole = this.projectRoleService.findByProjectIdAndRoleType(project.getId(), ProjectRole.TYPE_MANAGER);
+        //  �빐�떦 �궗�슜�옄媛� �듅�젙 �뿭�븷�뿉 �냼�냽�릺�뼱 �엳�뒗吏� �솗�씤�븳�떎.
+        ProjectRoleUser projectRoleUser = this.findByProjectRoleIdAndUserId(projectManagerRole.getId(), this.webAppUtil.getLoginId());
+
+        return projectRoleUser != null;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/ProjectServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/ProjectServiceImpl.java
new file mode 100644
index 0000000..e6ace4a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/ProjectServiceImpl.java
@@ -0,0 +1,1040 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.common.ExcelConditionCheck;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.EmailType;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.ProjectMapper;
+import kr.wisestone.owl.repository.ProjectClosureRepository;
+import kr.wisestone.owl.repository.ProjectRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.DateUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.vo.*;
+import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.form.ProjectForm;
+import kr.wisestone.owl.web.view.ExcelView;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.*;
+
+@Service
+public class ProjectServiceImpl extends AbstractServiceImpl<Project, Long, JpaRepository<Project, Long>> implements ProjectService {
+
+    private static final Logger log = LoggerFactory.getLogger(ProjectServiceImpl.class);
+
+    @Autowired
+    private ProjectRepository projectRepository;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private ProjectRoleService projectRoleService;
+
+    @Autowired
+    private WorkflowStatusService workflowStatusService;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private UserWorkspaceService userWorkspaceService;
+
+    @Autowired
+    private SystemEmailService systemEmailService;
+
+    @Autowired
+    private ProjectRoleUserService projectRoleUserService;
+
+    @Autowired
+    private AttachedFileService attachedFileService;
+
+    @Autowired
+    private IssueService issueService;
+
+    @Autowired
+    private IssueUserService issueUserService;
+
+    @Autowired
+    private IssueNumberGeneratorService issueNumberGeneratorService;
+
+    @Autowired
+    private ProjectClosureRepository projectClosureRepository;
+
+    @Autowired
+    private ProjectMapper projectMapper;
+
+    @Autowired
+    private ExcelView excelView;
+
+    @Autowired
+    private ExcelConditionCheck excelConditionCheck;
+
+    @Override
+    protected JpaRepository<Project, Long> getRepository() {
+        return this.projectRepository;
+    }
+
+    //  湲곕낯�쑝濡� �젣怨듬릺�뒗 �봽濡쒖젥�듃瑜� �깮�꽦�븳�떎.
+    @Override
+    @Transactional
+    public Project addDefaultProject(User user, Workspace workspace) {
+        Project project = new Project();
+        project.setName(this.messageAccessor.message("common.issueManagementProject")); // �씠�뒋 愿�由� �봽濡쒖젥�듃
+        project.setProjectKey("BTS");
+        project.setStatus(Project.PROJECT_OPEN);
+        project.setDescription(this.messageAccessor.message("common.intoTheSystemIssueManagementProject")); // �떆�뒪�뀥�뿉�꽌 湲곕낯�쑝濡� �젣怨듬릺�뒗 �씠�뒋 愿�由� �봽濡쒖젥�듃�엯�땲�떎.
+        project.setStartDate(DateUtil.convertDateToYYYYMMDD(new Date()));
+        project.setEndDate(DateUtil.convertDateToYYYYMMDD(DateUtil.addDays(new Date(), 3650)));
+        //  湲곕낯�쑝濡� �깮�꽦�릺�뒗 �봽濡쒖젥�듃�쓽 �쑀�삎�� BTS
+        project.setProjectType(ProjectType.BTS_PROJECT);
+        project.setWorkspace(workspace);
+        project.setDefaultYn(true);
+        this.projectRepository.saveAndFlush(project);
+        //  �봽濡쒖젥�듃 湲곕낯 �뿭�븷怨� 愿�由ъ옄 �뿭�븷�쓣 �깮�꽦�븳�떎. 愿�由ъ옄�뒗 �깮�꽦�븳 �궗�슜�옄
+        this.projectRoleService.addDefaultProjectRole(project, Lists.newArrayList(user), Lists.newArrayList());
+        //  媛� �봽濡쒖젥�듃�쓽 �씠�뒋 踰덊샇瑜� �옄�룞�쑝濡� �깮�꽦�븳�떎.
+        this.issueNumberGeneratorService.generateIssueNumber(project);
+
+        //  湲곕낯�쑝濡� �깮�꽦�릺�뒗 �봽濡쒖젥�듃�쓽 �쑀�삎�� 媛쒕컻 �봽濡쒖젥�듃
+        this.workflowStatusService.addDefaultWorkflowStatus(project, ProjectType.RMS_PROJECT);
+
+        return project;
+    }
+
+    //  �봽濡쒖젥�듃瑜� �궗�슜�옄媛� �깮�꽦�븳�떎.
+    @Transactional
+    @Override
+    public Project addProject(ProjectForm projectForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+        //  �씠由� �쑀�슚�꽦 泥댄겕
+        this.verifyName(projectForm.getName(), null);
+        //  �궎 �쑀�슚�꽦 泥댄겕
+        this.checkDuplicateProjectKey(projectForm.getProjectKey());
+        //  �긽�깭 泥댄겕
+        this.verifyProjectStatus(projectForm.getStatus());
+        //  �궇吏� �쑀�슚�꽦 泥댄겕
+        this.checkStartEndDate(projectForm.getStartDate(), projectForm.getEndDate());
+        //  愿�由ъ옄 �쑀�슚�꽦 泥댄겕
+        this.verifyManager(projectForm.getManagerIds());
+
+        Project project = ConvertUtil.copyProperties(projectForm, Project.class, "projectType");
+        project.setProjectType(ProjectType.valueOf(projectForm.getProjectType()));
+        Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        project.setWorkspace(workspace);
+
+        this.projectRepository.saveAndFlush(project);
+        // �긽�쐞�봽濡쒖젥�듃 �꽔湲�
+        SetParentProject(projectForm.getParentProjectId(), project);
+
+        //  媛� �봽濡쒖젥�듃�쓽 �씠�뒋 踰덊샇瑜� �옄�룞�쑝濡� �깮�꽦�븳�떎.
+        this.issueNumberGeneratorService.generateIssueNumber(project);
+        //  �씪諛� �궗�슜�옄 諛� 愿�由ъ옄瑜� �벑濡앺븯怨� �궗�슜�옄�뱾�뿉寃� �빐�떦 �뿭�븷�쓣 諛곗젙�븳�떎.
+        this.registerManagerAndUser(projectForm, project);
+
+        //  �봽濡쒖젥�듃 �쑀�삎�뿉 �뵲瑜� �썙�겕�뵆濡쒖슦 �깮�꽦
+        this.workflowStatusService.addDefaultWorkflowStatus(project, ProjectType.valueOf(projectForm.getProjectType()));
+
+        return project;
+    }
+
+    //  �씪諛� �궗�슜�옄 諛� 愿�由ъ옄瑜� �벑濡앺븯怨� �궗�슜�옄�뱾�뿉寃� �빐�떦 �뿭�븷�쓣 諛곗젙�븳�떎.
+    private void registerManagerAndUser(ProjectForm projectForm, Project project) {
+        List<User> managers = Lists.newArrayList();
+        //  愿�由ъ옄 �벑濡�
+        for (Long managerId : projectForm.getManagerIds()) {
+            User user = this.userService.getUser(managerId);
+            managers.add(user);
+        }
+        List<User> users = Lists.newArrayList();
+        List<String> sendEmails = Lists.newArrayList(); //  硫붿씪 ���긽�옄
+
+        //  �씪諛� �궗�슜�옄 �벑濡�
+        for (Long userId : projectForm.getUserIds()) {
+            User user = this.userService.getUser(userId);
+            users.add(user);
+            sendEmails.add(user.getAccount());
+        }
+
+        //  湲곕낯, 愿�由ъ옄 �봽濡쒖젥�듃 �뿭�븷�쓣 �깮�꽦�븯怨� �궗�슜�옄瑜� �빐�떦 �뿭�븷�뿉 諛곗젙�븳�떎.
+        this.projectRoleService.addDefaultProjectRole(project, managers, users);
+
+        //  �봽濡쒖젥�듃 李몄뿬�옄�뱾�뿉寃� �씠硫붿씪 諛쒖넚�쓣 �삁�빟�븳�떎.
+        Map<String, Object> projectMap = new HashMap<>();
+        projectMap.put("workspaceName", project.getWorkspace().getName());
+        projectMap.put("projectName", project.getName());
+        projectMap.put("registerDate", DateUtil.convertDateToStr(new Date()));
+
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append(managers.get(0).getName());
+        stringBuilder.append("(");
+        stringBuilder.append(CommonUtil.decryptAES128(managers.get(0).getAccount()));
+        stringBuilder.append(")");
+
+        projectMap.put("projectManagerName", stringBuilder.toString());
+        //  �봽濡쒖젥�듃 �씪諛� 李몄뿬 硫붿씪 諛쒖넚 �삁�빟
+        this.systemEmailService.reservationEmail(sendEmails.toArray(new String[sendEmails.size()]), EmailType.PROJECT_DEFAULT_INCLUDE, projectMap);
+    }
+
+
+    //  �씠由� �쑀�슚�꽦 泥댄겕
+    private void verifyName(String name, Long id) {
+        if (StringUtils.isEmpty(name)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PROJECT_NOT_NAME));
+        }
+
+        if (name.length() > 50) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PROJECT_NAME_MAX_LENGTH_OUT));
+        }
+
+        Project project;
+
+        Long workspaceId = this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId();
+
+        if (id == null) {
+            project = this.projectRepository.findByNameAndWorkspaceId(name, workspaceId);
+        }
+        else {
+            project = this.projectRepository.findByNameAndWorkspaceIdAndIdNot(name, workspaceId, id);
+        }
+
+        if (project != null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PROJECT_USED_NAME));
+        }
+    }
+
+    //  �궎 �쑀�슚�꽦 泥댄겕
+    private void checkDuplicateProjectKey(String projectKey) {
+        if (StringUtils.isEmpty(projectKey)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.PROJECT_KEY_NOT_EXIST));
+        }
+
+
+        if (projectKey.length() > 10) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.PROJECT_OVER_LENGTH_PROJECT_KEY));
+        }
+
+        Project project = this.findByProjectKey(projectKey);
+
+        if (project != null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.PROJECT_USED_PROJECT_KEY));
+        }
+    }
+
+    //  �궇吏� �쑀�슚�꽦 泥댄겕
+    private void checkStartEndDate(String startDate, String endDate) {
+        if (StringUtils.isEmpty(startDate) || StringUtils.isEmpty(endDate)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.DATE_NOT_EXIST));
+        }
+
+        Date start = DateUtil.convertStrToDate(startDate, "yyyy-MM-dd");
+        Date end = DateUtil.convertStrToDate(endDate, "yyyy-MM-dd");
+        if (start.getTime() > end.getTime()) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.DATE_PICKER_NOT_AVAILABLE));
+        }
+    }
+
+    //  愿�由ъ옄 �쑀�슚�꽦 泥댄겕
+    private void verifyManager(List<Long> managerIds) {
+        if (managerIds.isEmpty()) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PROJECT_NOT_MANAGER));
+        }
+    }
+
+    //  �긽�깭 泥댄겕
+    private void verifyProjectStatus(String projectStatus) {
+        if (StringUtils.isEmpty(projectStatus)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PROJECT_NOT_STATUS));
+        }
+    }
+
+    //  �봽濡쒖젥�듃 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<ProjectVo> findProject(Map<String, Object> resJsonData,
+                                       ProjectCondition condition, Pageable pageable) {
+
+        condition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        condition.setPageSize(pageable.getPageSize());
+        condition.setLoginUserId(this.webAppUtil.getLoginId());
+        condition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+
+        List<Map<String, Object>> results;
+        Long totalCount;
+
+        if (condition.getWorkspaceManager()) {
+            //  �뾽臾닿났媛� 愿�由ъ옄�씪 寃쎌슦 紐⑤뱺 �봽濡쒖젥�듃媛� �몴�떆�릺�뼱�빞 �븳�떎.
+            //  愿�由ъ옄�씪 �븣
+            if (this.userWorkspaceService.checkWorkspaceManager()) {
+                results = this.projectMapper.findByWorkspaceManager(condition);
+                totalCount = this.projectMapper.countByWorkspaceManager(condition);
+            }
+            else {
+                results = this.projectMapper.find(condition);
+                totalCount = this.projectMapper.count(condition);
+            }
+        }
+        else {
+            results = this.projectMapper.find(condition);
+            totalCount = this.projectMapper.count(condition);
+        }
+
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        //  �봽濡쒖젥�듃 議고쉶 寃곌낵瑜� ProjectVos 濡� 蹂��솚�븳�떎. - 愿�由ъ옄, �씪諛� �궗�슜�옄 �젙蹂� 異붽�
+        List<ProjectVo> projectVos = this.makeProjectVos(results);
+        this.setChildrenProject(projectVos);
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, projectVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        return projectVos;
+    }
+
+    void setChildrenProject(List<ProjectVo> projectVos) {
+        int projectCount = projectVos.size();
+        for (int i=0; i< projectCount; i++) {
+            ProjectVo projectVo = projectVos.get(i);
+            List<Map<String, Object>> children = this.projectMapper.findChildrenProject(projectVo.getId());
+
+            if (children != null && children.size() > 0) {
+                List<ProjectVo> childrenVo = this.makeProjectVos(children);
+                projectVo.setChildProjects(childrenVo);
+                setChildrenProject(childrenVo);
+            }
+        }
+    }
+
+
+    //  �봽濡쒖젥�듃 議고쉶 寃곌낵瑜� ProjectVos 濡� 蹂��솚�븳�떎.
+    private List<ProjectVo> makeProjectByVos(List<Map<String, Object>> results) {
+        List<ProjectVo> projectVos = Lists.newArrayList();
+
+        for (Map<String, Object> result : results) {
+            ProjectVo projectVo = ConvertUtil.convertMapToClass(result, ProjectVo.class);
+            projectVos.add(projectVo);
+        }
+
+        return projectVos;
+    }
+
+
+    //  �봽濡쒖젥�듃 議고쉶 寃곌낵瑜� ProjectVos 濡� 蹂��솚�븳�떎. - 愿�由ъ옄, �씪諛� �궗�슜�옄 �젙蹂� 異붽�
+    private List<ProjectVo> makeProjectVos(List<Map<String, Object>> results) {
+        List<ProjectVo> projectVos = Lists.newArrayList();
+
+        for (Map<String, Object> result : results) {
+            ProjectVo projectVo = ConvertUtil.convertMapToClass(result, ProjectVo.class);
+            //  �봽濡쒖젥�듃�뿉 李몄뿬�븯�뒗 �궗�슜�옄瑜� �뀑�똿�븳�떎. - 愿�由ъ옄 / �씪諛� �궗�슜�옄
+            this.setProjectUser(projectVo, true);
+            this.setProjectUser(projectVo, false);
+            //  �뾽臾닿났媛� �떞�떦�옄�뒗 紐⑤뱺 �봽濡쒖젥�듃瑜� �닔�젙/�궘�젣�븷 �닔 �엳�뼱�빞 �븳�떎.
+            if (this.userWorkspaceService.checkWorkspaceManager()) {
+                projectVo.setModifyPermissionCheck(true);
+            }
+            projectVos.add(projectVo);
+        }
+
+        return projectVos;
+    }
+
+    //  愿�由ъ옄, �씪諛� �궗�슜�옄瑜� 議곌굔�뿉 �뵲�씪 李얠븘以��떎.
+    private void setProjectUser(ProjectVo projectVo, Boolean findProjectManager) {
+        Map<String, Object> projectRoleUserMap = new HashMap<>();
+        projectRoleUserMap.put("id", projectVo.getId());
+
+        if (findProjectManager) {
+            projectRoleUserMap.put("statuses", Lists.newArrayList("02"));   //  愿�由ъ옄 �뀑�똿
+        }
+        else {
+            projectRoleUserMap.put("statuses", Lists.newArrayList("01"));   //  �씪諛� �궗�슜�옄 �뀑�똿
+        }
+
+        //  �궗�슜�옄 �젙蹂� �뀑�똿
+        List<Map<String, Object>> projectRoleUsers = this.projectRoleUserService.findProjectRoleUser(projectRoleUserMap);
+
+        if (projectRoleUsers != null && !projectRoleUsers.isEmpty()) {
+            List<UserVo> userVos = Lists.newArrayList();
+
+            for (Map<String, Object> projectRoleUser : projectRoleUsers) {
+                UserVo userVo = ConvertUtil.convertMapToClass(projectRoleUser, UserVo.class);
+                userVo.setByName(userVo.getName() + "(" + CommonUtil.decryptAES128(userVo.getAccount()) + ")");
+                userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount()));
+                //  �쁽�옱 濡쒓렇�씤�븳 �궗�슜�옄媛� �떞�떦�옄�씪 寃쎌슦 �닔�젙 沅뚰븳�쓣 以��떎.
+                if (userVo.getId().equals(this.webAppUtil.getLoginId()) && findProjectManager) {
+                    projectVo.setModifyPermissionCheck(Boolean.TRUE);
+                }
+                userVos.add(userVo);
+            }
+
+            if (findProjectManager) {
+                projectVo.setProjectManagerVos(userVos);
+            }
+            else {
+                projectVo.setProjectUserVos(userVos);
+            }
+        }
+    }
+
+
+    //  �봽濡쒖젥�듃 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailProject(Map<String, Object> resJsonData, ProjectCondition projectCondition) {
+        ProjectVo projectVo = new ProjectVo();
+
+        if (projectCondition.getId() != null) {
+            Project project = this.getProject(projectCondition.getId());
+            projectVo = ConvertUtil.copyProperties(project, ProjectVo.class);
+            projectVo.setProjectType(project.getProjectType().toString());
+
+            // �긽�쐞 �봽濡쒖젥�듃 �젙蹂� 媛��졇�삤湲�
+            ProjectClosure closure = project.getParentProjectClosure();
+            if (closure != null) {
+                ProjectVo parentProjectVo = ConvertUtil.copyProperties(closure.getParentProject(), ProjectVo.class);
+                projectVo.setParentProjectVo(parentProjectVo);
+            }
+
+            switch (projectCondition.getDeep()) {
+                case "01": //  �봽濡쒖젥�듃�뿉 李몄뿬�븯�뒗 �궗�슜�옄, 愿�由ъ옄 �젙蹂대�� �뀑�똿�븳�떎.
+                    this.setProjectUser(projectVo, true);
+                    this.setProjectUser(projectVo, false);
+                    break;
+            }
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, projectVo);
+    }
+
+    //  �봽濡쒖젥�듃 �젙蹂대�� �닔�젙�븳�떎.
+    @Transactional
+    @Override
+    public Project modifyProject(ProjectForm projectForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+        //  �씠由� �쑀�슚�꽦 泥댄겕
+        this.verifyName(projectForm.getName(), projectForm.getId());
+        //  �긽�깭 泥댄겕
+        this.verifyProjectStatus(projectForm.getStatus());
+        //  �궇吏� �쑀�슚�꽦 泥댄겕
+        this.checkStartEndDate(projectForm.getStartDate(), projectForm.getEndDate());
+        //  愿�由ъ옄 �쑀�슚�꽦 泥댄겕
+        this.verifyManager(projectForm.getManagerIds());
+
+        Project project = this.getProject(projectForm.getId());
+        //  �봽濡쒖젥�듃 李몄뿬 �궗�슜�옄
+        List<Long> existUserIds = this.getIncludeProjectUser(project);
+
+        //  �썙�겕�뒪�럹�씠�뒪�뿉�꽌 湲곕낯�쑝濡� �젣怨듬릺�뒗 �봽濡쒖젥�듃�뿉 ���븳 泥댄겕
+        this.checkDefaultProject(project, projectForm);
+        //  �닔�젙 沅뚰븳 泥댄겕
+        this.checkModifyPermission(project.getId());
+        //  愿�由ъ옄 蹂�寃�
+        Map<String, Object> changeProjectManagerNotifications = this.modifyProjectManagers(project, projectForm, ProjectRole.TYPE_MANAGER);
+        //  �씪諛� �궗�슜�옄 蹂�寃�
+        Map<String, Object> changeProjectUserNotifications = this.modifyProjectManagers(project, projectForm, ProjectRole.TYPE_DEFAULT);
+        ConvertUtil.copyProperties(projectForm, project, "id", "projectType");
+
+        this.projectRepository.saveAndFlush(project);
+
+        // �긽�쐞 �봽濡쒖젥�듃 �젙蹂� ���옣
+        SetParentProject(projectForm.getParentProjectId(), project);
+
+        //  �봽濡쒖젥�듃�뿉�꽌 李몄뿬媛� �젣�쇅�맂 �궗�슜�옄�뒗 �씠�뒋 �떞�떦�옄�뿉�꽌 �젣�쇅�븳�떎.
+        //  �빐�떦 �봽濡쒖젥�듃�뿉 李몄뿬�븯�뒗 紐⑤뱺 �궗�슜�옄 議고쉶
+        //  鍮좎쭊 �궗�엺�씠 愿�由ы븯�뒗 �씠�뒋 �쟾泥� 議고쉶 �썑 �뜲�씠�꽣 �궘�젣
+        List<Long> changeUserIds = this.getIncludeProjectUser(project);
+
+        //  李몄뿬�뿉�꽌 �젣�쇅�맂 �궗�슜�옄瑜� 李얘퀬 �떞�떦�븯怨� �엳�뜕 �씠�뒋�뿉�꽌 �젣�쇅�븳�떎.
+        this.checkExcludeUserAndRemoveIssueAssignee(project, existUserIds, changeUserIds);
+
+        //  愿�由ъ옄/�씪諛� �궗�슜�옄 蹂�寃� �궡�뿭�쓣 �넻吏��븳�떎.
+        this.notificationProjectRoleUser(changeProjectManagerNotifications, changeProjectUserNotifications, project);
+
+        return project;
+    }
+
+    void SetParentProject(Long parentProjectId, Project project) {
+        ProjectClosure projectClosure = this.projectClosureRepository.findByProjectId(project.getId());
+        if (parentProjectId != null && parentProjectId > -1) {
+            Project parentProject = this.getProject(parentProjectId);
+            if (projectClosure != null) {
+                projectClosure.setParentProject(parentProject);
+            } else {
+                projectClosure = new ProjectClosure(project, parentProject);
+            }
+            this.projectClosureRepository.saveAndFlush(projectClosure);
+        } else {
+            if (projectClosure != null) {
+                this.projectClosureRepository.delete(projectClosure);
+            }
+        }
+    }
+
+
+    //  �봽濡쒖젥�듃 李몄뿬 �궗�슜�옄
+    private List<Long> getIncludeProjectUser(Project project) {
+        Set<Long> includeUserIds = new HashSet<>();
+
+        for (ProjectRole projectRole : project.getProjectRoles()) {
+            List<ProjectRoleUser> projectRoleUsers =  this.projectRoleUserService.findByProjectRoleId(projectRole.getId());
+
+            for (ProjectRoleUser projectRoleUser : projectRoleUsers) {
+                includeUserIds.add(projectRoleUser.getUser().getId());
+            }
+        }
+
+        return Lists.newArrayList(includeUserIds);
+    }
+
+    //  李몄뿬�뿉�꽌 �젣�쇅�맂 �궗�슜�옄瑜� 李얘퀬 �떞�떦�븯怨� �엳�뜕 �씠�뒋�뿉�꽌 �젣�쇅�븳�떎.
+    private void checkExcludeUserAndRemoveIssueAssignee(Project project, List<Long> existUserIds, List<Long> changeUserIds) {
+        List<Long> excludeUserIds = CommonUtil.searchChangeList(changeUserIds, existUserIds);
+
+        if (excludeUserIds.size() > 0) {
+            this.issueUserService.removeIssueUser(project.getId(), excludeUserIds);
+        }
+    }
+
+
+
+    //  湲곕낯 �젣怨듬릺�뒗 �봽濡쒖젥�듃�쓽 愿�由ъ옄�뒗 �썙�겕�뒪�럹�씠�뒪 愿�由ъ옄媛� �룷�븿�릺�뼱 �엳�뼱�빞 �븳�떎.
+    private void checkDefaultProject(Project project, ProjectForm projectForm) {
+        if (project.getDefaultYn()) {
+            //  �빐�떦 �봽濡쒖젥�듃�쓽 �썙�겕�뒪�럹�씠�뒪�쓽 愿�由ъ옄瑜� 李얜뒗�떎.
+            //  湲곕낯 �봽濡쒖젥�듃�뒗 �썙�겕�뒪�럹�씠�뒪 愿�由ъ옄媛� 臾댁“嫄� �봽濡쒖젥�듃 愿�由ъ옄濡� �뱾�뼱媛� �엳�뼱�빞 �븳�떎.
+            Workspace workspace = project.getWorkspace();
+            List<UserWorkspace> userWorkspaces = this.userWorkspaceService.findByWorkspaceIdAndManagerYn(workspace.getId(), true);
+
+            for (UserWorkspace userWorkspace : userWorkspaces) {
+                User workspaceManager = userWorkspace.getUser();
+                boolean checkDefaultManager = false;
+
+                for (Long managerId : projectForm.getManagerIds()) {
+                    if (managerId.equals(workspaceManager.getId())) {
+                        checkDefaultManager = true;
+                        break;
+                    }
+                }
+
+                if (!checkDefaultManager) {
+                    throw new OwlRuntimeException(
+                            this.messageAccessor.getMessage(MsgConstants.DEFAULT_PROJECT_MANAGER_NOT_CHANGE));
+                }
+            }
+        }
+    }
+
+    //  濡쒓렇�씤�븳 �궗�슜�옄媛� 愿�由ъ옄 �뿭�븷�뿉 �냼�냽�릺�뼱 �엳�뒗吏� �솗�씤�븳�떎.
+    private void checkModifyPermission(Long projectId) {
+        Boolean hasPermission = Boolean.FALSE;
+
+        //  �빐�떦 �뾽臾� 怨듦컙�쓽 愿�由ъ옄�씪 寃쎌슦 沅뚰븳 泥댄겕瑜� �븯吏� �븡�뒗�떎.
+        if (this.userWorkspaceService.checkWorkspaceManager()) {
+            return;
+        }
+
+        //  愿�由ъ옄 �뿭�븷 議고쉶
+        Map<String, Object> projectRoleUserMap = new HashMap<>();
+        projectRoleUserMap.put("id", projectId);
+        projectRoleUserMap.put("statuses", Lists.newArrayList("02"));   //  愿�由ъ옄 議고쉶
+
+        List<Map<String, Object>> projectUsers = this.projectRoleUserService.findProjectRoleUser(projectRoleUserMap);
+        //  �쁽�옱 濡쒓렇�씤 �궗�슜�옄媛� 愿�由ъ옄�씤吏� �솗�씤
+        if (projectUsers != null && !projectUsers.isEmpty()) {
+            for (Map<String, Object> projectUser : projectUsers) {
+                UserVo userVo = ConvertUtil.convertMapToClass(projectUser, UserVo.class);
+                if (userVo.getId().equals(this.webAppUtil.getLoginId())) {
+                    hasPermission = true;
+                    break;
+                }
+            }
+        }
+
+        if (!hasPermission) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PROJECT_NOT_MODIFY_PERMISSION));
+        }
+    }
+
+    //  愿�由ъ옄瑜� 蹂�寃쏀븳�떎.
+    private Map<String, Object> modifyProjectManagers(Project project, ProjectForm projectForm, String roleType) {
+        ProjectRole projectRole = this.projectRoleService.findByProjectIdAndRoleType(project.getId(), roleType);
+        List<User> oldManager = Lists.newArrayList();
+        List<User> newManager = Lists.newArrayList();
+        Map<String, Object> results = new HashMap<>();
+
+        for (ProjectRoleUser projectRoleUser : projectRole.getProjectRoleUsers()) {
+            User user = projectRoleUser.getUser();
+            oldManager.add(user);
+            user.removeProjectRole(projectRole);
+        }
+
+        projectRole.getProjectRoleUsers().clear();
+
+        //  愿�由ъ옄�씪 寃쎌슦
+        if (roleType.equals(ProjectRole.TYPE_MANAGER)) {
+            for (Long managerId : projectForm.getManagerIds()) {
+                User user = this.userService.getUser(managerId);
+                newManager.add(user);
+                user.addProjectRole(projectRole);
+            }
+        }
+        else if (roleType.equals(ProjectRole.TYPE_DEFAULT)) {
+            //  �씪諛� �궗�슜�옄�씪 寃쎌슦
+            for (Long userId : projectForm.getUserIds()) {
+                User user = this.userService.getUser(userId);
+                newManager.add(user);
+                user.addProjectRole(projectRole);
+            }
+        }
+
+        //  �젣�쇅 ���긽�옄 李얘린, oldManager �뿉�뒗 �엳�뒗�뜲 newManager �뿉 �뾾�쑝硫� �젣�쇅 ���긽
+        List<String> excludeUsers = this.systemEmailService.notificationUserChange(oldManager, newManager);
+        //  李몄뿬 ���긽�옄 李얘린, newManager �뿉�뒗 �엳�뒗�뜲 oldManager �뿉 �뾾�쑝硫� 珥덈�諛쏆� ���긽
+        List<String> includeUsers = this.systemEmailService.notificationUserChange(newManager, oldManager);
+
+        results.put("excludeUsers", excludeUsers);
+        results.put("includeUsers", includeUsers);
+
+        return results;
+
+    }
+
+    //  �봽濡쒖젥�듃 李몄뿬, �젣�쇅 �넻吏� �젙蹂대�� 以묐났�쑝濡� �굹媛�吏� �븡�룄濡� 泥댄겕�븳�떎.
+    private void notificationProjectRoleUser(Map<String, Object> changeProjectManagerNotifications, Map<String, Object> changeProjectUserNotifications, Project project) {
+        List<String> projectManagerExcludeUsers = (List<String>) changeProjectManagerNotifications.get("excludeUsers");  //  愿�由ъ옄 �젣�쇅 �궗�슜�옄
+        List<String> projectManagerIncludeUsers = (List<String>) changeProjectManagerNotifications.get("includeUsers");  //  愿�由ъ옄 李몄뿬 �궗�슜�옄
+        List<String> projectUserExcludeUsers = (List<String>) changeProjectUserNotifications.get("excludeUsers");    //  �젣�쇅�맂 �씪諛� �궗�슜�옄
+        List<String> projectUserIncludeUsers = (List<String>) changeProjectUserNotifications.get("includeUsers");    //  李몄뿬�맂 �씪諛� �궗�슜�옄
+        Map<String, Object> projectMap = new HashMap<>();
+        projectMap.put("workspaceName", project.getWorkspace().getName());
+        projectMap.put("projectName", project.getName());
+        projectMap.put("registerDate", DateUtil.convertDateToStr(new Date()));
+
+        Map<String, Object> projectRoleUserMap = new HashMap<>();
+        projectRoleUserMap.put("id", project.getId());
+        projectRoleUserMap.put("statuses", Lists.newArrayList("02"));   //  愿�由ъ옄 議고쉶
+
+        //  愿�由ъ옄 �젙蹂� �뀑�똿
+        List<Map<String, Object>> projectRoleUsers = this.projectRoleUserService.findProjectRoleUser(projectRoleUserMap);
+
+        if (projectRoleUsers != null && !projectRoleUsers.isEmpty()) {
+            for (Map<String, Object> projectRoleUser : projectRoleUsers) {
+                UserVo userVo = ConvertUtil.convertMapToClass(projectRoleUser, UserVo.class);
+                StringBuilder stringBuilder = new StringBuilder();
+                stringBuilder.append(userVo.getName());
+                stringBuilder.append("(");
+                stringBuilder.append(CommonUtil.decryptAES128(userVo.getAccount()));
+                stringBuilder.append(")");
+
+                projectMap.put("projectManagerName", stringBuilder.toString());
+            }
+        }
+
+        // �봽濡쒖젥�듃 愿�由ъ옄�뿉�꽌 �젣�쇅�릺�뿀�쑝硫댁꽌 �봽濡쒖젥�듃 �씪諛� �궗�슜�옄濡� �뱾�뼱媛� 寃쎌슦
+        List<String> excludeManagerAndIncludeUser = Lists.newArrayList();
+
+        for (String projectManagerExcludeUserEmail : projectManagerExcludeUsers) {
+            for (String projectUserIncludeUserEmail : projectUserIncludeUsers) {
+                if (projectManagerExcludeUserEmail.equals(projectUserIncludeUserEmail)) {
+                    excludeManagerAndIncludeUser.add(projectManagerExcludeUserEmail);
+                }
+            }
+        }
+
+        //  �씪諛� �궗�슜�옄�뿉�꽌 �젣�쇅�릺�뿀�쑝硫댁꽌 �봽濡쒖젥�듃 愿�由ъ옄濡� �뱾�뼱媛� 寃쎌슦
+        List<String> excludeUserAndIncludeManager = Lists.newArrayList();
+
+        for (String projectUserExcludeUserEmail : projectUserExcludeUsers) {
+            for (String projectManagerIncludeUserEmail : projectManagerIncludeUsers) {
+                if (projectUserExcludeUserEmail.equals(projectManagerIncludeUserEmail)) {
+                    excludeUserAndIncludeManager.add(projectManagerIncludeUserEmail);
+                }
+            }
+        }
+
+        // �봽濡쒖젥�듃 愿�由ъ옄�뿉�꽌 �젣�쇅�릺�뿀�쑝硫댁꽌 �봽濡쒖젥�듃 �씪諛� �궗�슜�옄濡� �뱾�뼱媛� 寃쎌슦
+        this.sendEmailProjectRoleChange(excludeManagerAndIncludeUser, EmailType.PROJECT_MANAGER_EXCLUDE_AND_PROJECT_DEFAULT_INCLUDE, projectMap);
+        //  �씪諛� �궗�슜�옄�뿉�꽌 �젣�쇅�릺�뿀�쑝硫댁꽌 �봽濡쒖젥�듃 愿�由ъ옄濡� �뱾�뼱媛� 寃쎌슦
+        this.sendEmailProjectRoleChange(excludeUserAndIncludeManager, EmailType.PROJECT_DEFAULT_EXCLUDE_AND_PROJECT_MANAGER_INCLUDE, projectMap);
+        //  愿�由ъ옄 �젣�쇅 硫붿씪 理쒖쥌
+        this.sendEmailProjectRoleChange(this.checkDuplicationEmails(projectManagerExcludeUsers, excludeManagerAndIncludeUser, excludeUserAndIncludeManager), EmailType.PROJECT_MANAGER_EXCLUDE, projectMap);
+        //  愿�由ъ감 李몄뿬 硫붿씪 理쒖쥌
+        this.sendEmailProjectRoleChange(this.checkDuplicationEmails(projectManagerIncludeUsers, excludeManagerAndIncludeUser, excludeUserAndIncludeManager), EmailType.PROJECT_MANAGER_INCLUDE, projectMap);
+        //  �씪諛� �궗�슜�옄 �젣�쇅 硫붿씪 理쒖쥌
+        this.sendEmailProjectRoleChange(this.checkDuplicationEmails(projectUserExcludeUsers, excludeManagerAndIncludeUser, excludeUserAndIncludeManager), EmailType.PROJECT_DEFAULT_EXCLUDE, projectMap);
+        //  �씪諛� �궗�슜�옄 李몄뿬 硫붿씪 理쒖쥌
+        this.sendEmailProjectRoleChange(this.checkDuplicationEmails(projectUserIncludeUsers, excludeManagerAndIncludeUser, excludeUserAndIncludeManager), EmailType.PROJECT_DEFAULT_INCLUDE, projectMap);
+    }
+
+    //  以묐났�쑝濡� �굹媛��뒗 硫붿씪�씠 �엳�뒗吏� 泥댄겕�븳�떎.
+    private List<String> checkDuplicationEmails(List<String> checkEmails, List<String> excludeManagerAndIncludeUser, List<String> excludeUserAndIncludeManager) {
+        List<String> sendProjectManagerExcludeUserEmails = Lists.newArrayList();
+
+        for (String projectManagerExcludeUserEmail : checkEmails) {
+            boolean sendEmail = true;
+            //  愿�由ъ옄 �젣�쇅 硫붿씪�쓣 蹂대궡�젮�뒗�뜲 以묐났�릺�뒗 硫붿씪�씠 �굹媛덉� �뙋�떒�븳�떎.
+            for (String excludeManagerAndIncludeUserEmail : excludeManagerAndIncludeUser) {
+                if (excludeManagerAndIncludeUserEmail.equals(projectManagerExcludeUserEmail)) {
+                    sendEmail = false;
+                    break;
+                }
+            }
+
+            //  愿�由ъ옄 �젣�쇅 硫붿씪�쓣 蹂대궡�젮�뒗�뜲 以묐났�릺�뒗 硫붿씪�씠 �굹媛덉� �뙋�떒�븳�떎.
+            for (String excludeUserAndIncludeManagerEmail : excludeUserAndIncludeManager) {
+                if (excludeUserAndIncludeManagerEmail.equals(projectManagerExcludeUserEmail)) {
+                    sendEmail = false;
+                    break;
+                }
+            }
+
+            if (sendEmail) {
+                sendProjectManagerExcludeUserEmails.add(projectManagerExcludeUserEmail);
+            }
+        }
+
+        return sendProjectManagerExcludeUserEmails;
+    }
+
+    //  �봽濡쒖젥�듃 李몄뿬媛� 蹂�寃쎈맂 ���긽�옄�뿉寃� �씠硫붿씪�쓣 蹂대궦�떎.
+    private void sendEmailProjectRoleChange(List<String> sendEmails, EmailType emailType, Map<String, Object> params) {
+        String[] sendUsers = sendEmails.toArray(new String[sendEmails.size()]);
+
+        if (sendUsers.length > 0) {
+            //  留덉�留� �젒洹� �봽濡쒖젥�듃�뿉�꽌 �젣�쇅�릺�뿀�쓣 �븣 �솕硫� �깉濡쒓퀬移⑥쓣 �븳�떎.
+            switch (emailType) {
+                case PROJECT_DEFAULT_EXCLUDE:
+                case PROJECT_MANAGER_EXCLUDE:
+                    this.updateProjectExcludeUserLastWorkspaceId(sendUsers, params);
+                    break;
+            }
+
+            this.systemEmailService.reservationEmail(sendUsers, emailType, params);
+        }
+    }
+
+    //  �젒�냽以묒씤 �궗�슜�옄以� �젣�쇅�떦�븳 �궗�슜�옄媛� �빐�떦 �봽濡쒖젥�듃瑜� 蹂닿퀬�엳�쑝硫� 寃쎄퀬李쎌쓣 �몴�떆�븯怨� �솕硫댁쓣 �깉濡쒓퀬移⑦빐以��떎.
+    private void updateProjectExcludeUserLastWorkspaceId(String[] sendUsers, Map<String, Object> params) {
+       /* List<UserVo> activeLoginUserVos = this.webSocketSessionService.getActiveUserVos();
+        Long projectId = MapUtil.getLong(params, "projectId");*/
+
+        /*for (String email : sendUsers) {
+            //  �젒�냽以묒씠 �븘�땲�뼱�룄 留덉�留� �젙蹂대뒗 �뾽�뜲�씠�듃�븳�떎.
+            User user = this.userService.findByAccount(email);
+
+            if (user != null && projectId != null) {
+                //  �빐�떦 �궗�슜�옄媛� 留덉�留됱쑝濡� �젒洹쇳븳 �봽濡쒖젥�듃媛� �젣�쇅�떦�븳 �봽濡쒖젥�듃�씪硫� �썙�겕�뒪�럹�씠�뒪 �젙蹂대�� 珥덇린�솕�븳�떎.
+                if (projectId.equals(user.getLastProjectId())) {
+                    //  �옄�떊�쓽 留덉�留� �젒洹� workspace & project �젙蹂대�� 蹂몄씤�씠 愿�由ы븯�뒗 �썙�겕�뒪�럹�씠�뒪�쓽 湲곕낯 �봽濡쒖젥�듃濡� 珥덇린�솕�븳�떎.
+                    this.userService.initLastWorkspaceIdAndLastProjectId(user);
+
+                    for (UserVo userVo : activeLoginUserVos) {
+                        //  �젒�냽以묒씤 �궗�슜�옄�뿉寃뚮쭔 �쎒 �냼耳� �뾽�뜲�씠�듃 �떆�옉.
+                        if (userVo.getAccount().equals(email)) {
+                            this.simpMessagingTemplate.convertAndSendToUser(email, "/notification/project-exclude", this.messageAccessor.getMessage(MsgConstants.PROJECT_EXCLUDE), params);
+                        }
+                    }
+                }
+            }
+        }*/
+    }
+
+    //  �봽濡쒖젥�듃 �궎濡� �봽濡쒖젥�듃瑜� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Project findByProjectKey(String projectKey) {
+        return this.projectRepository.findByProjectKeyAndWorkspaceId(projectKey, this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+    }
+
+    //  �봽濡쒖젥�듃 �븘�씠�뵒濡� �봽濡쒖젥�듃瑜� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Project getProject(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PROJECT_NOT_EXIST));
+        }
+
+        Project project = this.findOne(id);
+
+        if (project == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PROJECT_NOT_EXIST));
+        }
+
+        return project;
+    }
+
+    //  �봽濡쒖젥�듃瑜� �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void removeProjects(ProjectForm projectForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        if (projectForm.getRemoveIds().size() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PROJECT_REMOVE_NOT_SELECT));
+        }
+
+        for (Long projectId : projectForm.getRemoveIds()) {
+            this.removeProjects(projectId);
+        }
+
+        this.projectRepository.flush();
+    }
+
+    private void removeProjects(Long projectId) {
+        Project project = this.getProject(projectId);
+        //  湲곕낯 �봽濡쒖젥�듃�뒗 �궘�젣 湲덉�
+        if (project.getDefaultYn()) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.DEFAULT_PROJECT_NOT_REMOVE));
+        }
+
+        //  濡쒓렇�씤�븳 �궗�슜�옄媛� 愿�由ъ옄 �뿭�븷�뿉 �냼�냽�릺�뼱 �엳�뒗吏� �솗�씤�븳�떎.
+        this.checkModifyPermission(project.getId());
+
+        List<String> sendEmails = Lists.newArrayList();
+        Map<String, Object> params = new HashMap<>();
+        params.put("projectId", projectId);
+
+        //  �빐�떦 �봽濡쒖젥�듃�뿉 李몄뿬�븯怨� �엳�뒗 �궗�슜�옄�뒗 紐⑤몢 �봽濡쒖젥�듃 �젣�쇅 �븣由� 硫붿씪�쓣 �빐以��떎.
+        for (ProjectRole projectRole : project.getProjectRoles()) {
+            for (ProjectRoleUser projectRoleUser : projectRole.getProjectRoleUsers()) {
+                sendEmails.add(projectRoleUser.getUser().getAccount());
+            }
+        }
+
+        String[] sendUsers = sendEmails.toArray(new String[sendEmails.size()]);
+        this.updateProjectExcludeUserLastWorkspaceId(sendUsers, params);
+
+        //  �봽濡쒖젥�듃�뿉 �엳�뒗 紐⑤뱺 �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+        List<Long> issueIds = this.issueService.findByProjectId(projectId);
+        List<Long> projectRoleIds = Lists.newArrayList();
+        for (ProjectRole projectRole : project.getProjectRoles()) {
+            projectRoleIds.add(projectRole.getId());
+        }
+
+        params.put("issueIds", issueIds);
+        params.put("projectRoleIds", projectRoleIds);
+
+        //  �봽濡쒖젥�듃�뿉 �엳�뒗 紐⑤뱺 �젙蹂대�� �궘�젣�븳�떎.
+        this.projectMapper.deleteProject(params);
+
+        //  �봽濡쒖젥�듃 �궘�젣�떆 �씠�뒋�뿉 泥⑤��맂 �뙆�씪�쓣 �떆�뒪�뀥�뿉�꽌 �궘�젣�븳�떎.
+        this.attachedFileService.deleteIssueCascadeAttachedFile(issueIds, project.getWorkspace());
+        this.projectRepository.flush();
+
+        //  this.projectRepository.delete(project.getId());
+    }
+
+    //  �썙�겕�뒪�럹�씠�뒪�뿉 �엳�뒗 紐⑤뱺 �봽濡쒖젥�듃瑜� 議고쉶�븳�떎. �씠�뒋 �뿊�� import �뿉�꽌 �궗�슜
+    @Override
+    @Transactional(readOnly = true)
+    public List<Project> findByWorkspaceId() {
+        return this.projectRepository.findByWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+    }
+
+    //  �쁽�옱 �젒洹쇳븳 �뾽臾닿났媛꾩뿉�꽌 李몄뿬�븯怨� �엳�뒗 �봽濡쒖젥�듃瑜� 議고쉶�븳�떎. - ���떆蹂대뱶, �씠�뒋 紐⑸줉�뿉�꽌 �궗�슜
+    @Override
+    @Transactional(readOnly = true)
+    public List<Map<String, Object>> findByWorkspaceIdAndIncludeProject(List<String> statuses, String projectType) {
+        ProjectCondition projectCondition = new ProjectCondition();
+        projectCondition.setLoginUserId(this.webAppUtil.getLoginId());
+        projectCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        projectCondition.setProjectType(projectType);
+        projectCondition.setStatuses(statuses);
+
+        return this.projectMapper.findByWorkspaceIdAndIncludeProject(projectCondition);
+    }
+
+    // �쁽�옱 �젒洹쇳븳 �뾽援ш났媛꾩뿉�꽌 李몄뿬�븯怨� �엳�뒗 �봽濡쒖젥�듃瑜� 議고쉶�븳�떎(�븯�쐞�봽濡쒖젥�듃 誘명룷�븿)
+    @Override
+    public List<Map<String, Object>> findByWorkspaceIdAndIncludeProject(ProjectCondition projectCondition) {
+        projectCondition.setLoginUserId(this.webAppUtil.getLoginId());
+        projectCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        return this.projectMapper.findByWorkspaceIdAndIncludeProject(projectCondition);
+    }
+
+    @Override
+    public List<Map<String, Object>> findByWorkspaceIdAndIncludeProjectAll(List<String> statuses, String projectType) {
+        ProjectCondition projectCondition = new ProjectCondition();
+        projectCondition.setLoginUserId(this.webAppUtil.getLoginId());
+        projectCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        projectCondition.setProjectType(projectType);
+        projectCondition.setStatuses(statuses);
+
+        return this.projectMapper.findByWorkspaceIdAndIncludeProjectAll(projectCondition);
+    }
+
+    @Override
+    public List<Map<String, Object>> findByWorkspaceManagerAll() {
+        ProjectCondition projectCondition = new ProjectCondition();
+        projectCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        return this.projectMapper.findByWorkspaceManagerAll(projectCondition);
+    }
+
+    // �쁽�옱 �젒洹쇳븳 �뾽援ш났媛꾩뿉�꽌 李몄뿬�븯怨� �엳�뒗 �봽濡쒖젥�듃瑜� 議고쉶�븳�떎(�븯�쐞�봽濡쒖젥�듃 �룷�븿)
+    @Override
+    public List<Map<String, Object>> findByWorkspaceIdAndIncludeProjectAll(ProjectCondition projectCondition) {
+        projectCondition.setLoginUserId(this.webAppUtil.getLoginId());
+        projectCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        return this.projectMapper.findByWorkspaceIdAndIncludeProjectAll(projectCondition);
+    }
+
+    //  �쁽�옱 �젒洹쇳븳 �뾽臾닿났媛꾩뿉�꽌 李몄뿬�븯怨� �엳�뒗 �봽濡쒖젥�듃瑜� 議고쉶�븳�떎. - �긽�떒 �봽濡쒖젥�듃 紐⑸줉�뿉�꽌 �궗�슜
+    @Override
+    @Transactional(readOnly = true)
+    public List<ProjectVo> findByIncludeProject(List<String> statuses, String projectType) {
+        ProjectCondition projectCondition = new ProjectCondition();
+        projectCondition.setLoginUserId(this.webAppUtil.getLoginId());
+        projectCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        projectCondition.setProjectType(projectType);
+        projectCondition.setStatuses(statuses);
+
+        List<Map<String, Object>> results;
+
+        if (this.userWorkspaceService.checkWorkspaceManager()) {
+            results = this.projectMapper.findByWorkspaceManager(projectCondition);
+        } else {
+            results = this.projectMapper.findByWorkspaceIdAndIncludeProject(projectCondition);
+        }
+        List<ProjectVo> projectVos = this.makeProjectByVos(results);
+        this.setChildrenProject(projectVos);
+
+        return  projectVos;
+    }
+
+    //  �봽濡쒖젥�듃 紐⑸줉�쓣 �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    @Override
+    @Transactional
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        //  �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븯怨� 鍮꾪솢�꽦�씪 寃쎌슦 �뿊�� �떎�슫濡쒕뱶瑜� 湲덉��븳�떎.
+        ModelAndView modelAndView = this.workspaceService.checkUseExcelDownload(model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        Map<String, Object> conditions = new HashMap<>();
+        //  �뿊�� �떎�슫濡쒕뱶�뿉 �븘�슂�븳 寃��깋 議곌굔 �젙蹂대�� 異붿텧�븯怨� 寃��깋 議곌굔 異붿텧�뿉 �삤瑜섍� 諛쒖깮�븯硫� 寃쎄퀬瑜� �몴�떆�빐以��떎.
+        modelAndView = this.excelConditionCheck.checkCondition(conditions, request, model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        ProjectCondition projectCondition = ProjectCondition.make(conditions);
+        projectCondition.setLoginUserId(this.webAppUtil.getLoginId());
+        projectCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        List<Map<String, Object>> results = this.projectMapper.find(projectCondition);
+        //  �봽濡쒖젥�듃 議고쉶 寃곌낵瑜� ProjectVos 濡� 蹂��솚�븳�떎. - 愿�由ъ옄, �씪諛� �궗�슜�옄 �젙蹂� 異붽�
+        List<ProjectVo> projectVos = this.makeProjectVos(results);
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.projectList")); // �봽濡쒖젥�듃 紐⑸줉
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("statusName", this.messageAccessor.message("common.status"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �긽�깭
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("name", this.messageAccessor.message("common.project"), 40, ExportExcelAttrVo.ALIGN_LEFT)); // �봽濡쒖젥�듃
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("manager", this.messageAccessor.message("common.admin"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 愿�由ъ옄
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("members", this.messageAccessor.message("common.teamMember"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // ���썝
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("period", this.messageAccessor.message("common.period"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 湲곌컙
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("projectKey", this.messageAccessor.message("common.projectKey"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �봽濡쒖젥�듃 �궎
+        //  �뿊���뿉 �꽔�쓣 �뜲�씠�꽣 - ProjectVos �뜲�씠�꽣瑜� �뿊���뿉�꽌 �몴�떆�븷 �닔 �엳�뒗 �뜲�씠�꽣濡� 蹂�寃쏀븳�떎.
+        excelInfo.setDatas(this.convertExcelViewToProjectVos(projectVos));
+
+        model.addAttribute(Constants.EXCEL, excelInfo);
+        return new ModelAndView(this.excelView);
+    }
+
+    //  ProjectVo �뜲�씠�꽣瑜� �뿊���뿉�꽌 �몴�떆�븷 �닔 �엳�뒗 �뜲�씠�꽣濡� 蹂�寃쏀븳�떎.
+    private List<Map<String, String>> convertExcelViewToProjectVos(List<ProjectVo> projectVos) {
+        List<Map<String, String>> results = Lists.newArrayList();
+
+        for (ProjectVo projectVo : projectVos) {
+            Map<String, String> result = new HashMap<>();
+
+            String projectStatusName = "";
+
+            switch (projectVo.getStatus()) {
+                case Project.PROJECT_READY:
+                    projectStatusName = this.messageAccessor.message("common.wait"); // ��湲�
+                    break;
+                case Project.PROJECT_OPEN:
+                    projectStatusName = this.messageAccessor.message("common.progress"); // 吏꾪뻾
+                    break;
+                case Project.PROJECT_CLOSE:
+                    projectStatusName = this.messageAccessor.message("common.end"); // 醫낅즺
+                    break;
+            }
+
+            result.put("statusName", projectStatusName);
+            result.put("name", projectVo.getName());
+
+            StringBuilder stringBuilderManager = new StringBuilder();
+
+            if (projectVo.getProjectManagerVos().size() > 0) {
+                stringBuilderManager.append(projectVo.getProjectManagerVos().get(0).getName());
+                stringBuilderManager.append("(");
+                stringBuilderManager.append(projectVo.getProjectManagerVos().get(0).getAccount());
+                stringBuilderManager.append(")");
+            }
+
+            result.put("manager", stringBuilderManager.toString());
+            result.put("members", CommonUtil.convertUserVosToString(projectVo.getProjectUserVos()));
+            result.put("projectKey", projectVo.getProjectKey());
+            result.put("period", projectVo.getStartDate() + " - " + projectVo.getEndDate());
+            results.add(result);
+        }
+
+        return results;
+    }
+
+
+    @Override
+    @Transactional
+    public void findLastUseProject(Map<String, Object> resJsonData) {
+        UserVo loginUser = this.webAppUtil.getLoginUser();
+        Project project = null;
+
+        if (loginUser.getLastProjectId() != null) {
+            project = this.projectRepository.getOne(loginUser.getLastProjectId());
+        }
+
+        if (project == null) {
+            project = this.projectRepository.findByWorkspaceIdAndDefaultYn(loginUser.getLastWorkspaceId(), true);
+        }
+
+        if (project == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.PROJECT_NOT_EXIST));
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, ConvertUtil.copyProperties(project, ProjectVo.class));
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/QnaServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/QnaServiceImpl.java
new file mode 100644
index 0000000..92b538a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/QnaServiceImpl.java
@@ -0,0 +1,133 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.config.kafka.KafkaSender;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.Qna;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.QnaMapper;
+import kr.wisestone.owl.repository.QnaRepository;
+import kr.wisestone.owl.service.QnaService;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.QnaVo;
+import kr.wisestone.owl.vo.ResPage;
+import kr.wisestone.owl.web.condition.QnaCondition;
+import kr.wisestone.owl.web.form.QnaForm;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class QnaServiceImpl extends AbstractServiceImpl<Qna, Long, JpaRepository<Qna, Long>> implements QnaService {
+
+    @Autowired
+    private QnaRepository qnaRepository;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private KafkaSender kafkaSender;
+
+    @Autowired
+    private QnaMapper qnaMapper;
+
+    @Override
+    protected JpaRepository<Qna, Long> getRepository() {
+        return this.qnaRepository;
+    }
+
+    //  怨듭� �궗�빆 �벑濡�
+    @Override
+    @Transactional
+    public Qna addQna(QnaForm qnaForm) {
+        //  Qna �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+        this.verifyTitleAndDescription(qnaForm.getTitle(), qnaForm.getDescription());
+
+        Qna qna = ConvertUtil.copyProperties(qnaForm, Qna.class);
+
+        return this.qnaRepository.saveAndFlush(qna);
+    }
+
+    //  Qna �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+    private void verifyTitleAndDescription(String title, String description) {
+        if (StringUtils.isEmpty(title) || StringUtils.isEmpty(description)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.QNA_EMPTY_CONTENT));
+        }
+    }
+
+    //  Qna 議고쉶
+    @Override
+    @Transactional(readOnly = true)
+    public List<QnaVo> findQna(Map<String, Object> resJsonData,
+                               QnaCondition qnaCondition, Pageable pageable) {
+
+        qnaCondition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        qnaCondition.setPageSize(pageable.getPageSize());
+        qnaCondition.setTitle(qnaCondition.getTitle());
+
+        List<Map<String, Object>> results = this.qnaMapper.find(qnaCondition);
+        Long totalCount = this.qnaMapper.count(qnaCondition);
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        List<QnaVo> qnaVos = ConvertUtil.convertListToListClass(results, QnaVo.class);
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, qnaVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        return qnaVos;
+    }
+
+    //  Qna �닔�젙
+    @Override
+    @Transactional
+    public Qna modifyQna(QnaForm qnaForm) {
+        //  怨듭��궗�빆 �젣紐� 諛� �궡�슜 怨듬갚 泥댄겕
+        this.verifyTitleAndDescription(qnaForm.getTitle(), qnaForm.getDescription());
+
+        Qna qna = this.getQna(qnaForm.getId());
+        ConvertUtil.copyProperties(qnaForm, qna, "id");
+
+        return this.qnaRepository.saveAndFlush(qna);
+    }
+
+    //  Qna id 濡� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Qna getQna(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.QNA_NOT_EXIST));
+        }
+
+        Qna qna = this.findOne(id);
+
+        if (qna == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.QNA_NOT_EXIST));
+        }
+
+        return qna;
+    }
+
+    //  Qna �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailQna(Map<String, Object> resJsonData, QnaCondition qnaCondition) {
+        QnaVo qnaVo = new QnaVo();
+
+        if (qnaCondition.getId() != null) {
+            Qna qna = this.getQna(qnaCondition.getId());
+            qnaVo = ConvertUtil.copyProperties(qna, QnaVo.class);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, qnaVo);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/ReservationDisableUserServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/ReservationDisableUserServiceImpl.java
new file mode 100644
index 0000000..fbbb903
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/ReservationDisableUserServiceImpl.java
@@ -0,0 +1,81 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.Payment;
+import kr.wisestone.owl.domain.ReservationDisableUser;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.repository.ReservationDisableUserRepository;
+import kr.wisestone.owl.service.PaymentService;
+import kr.wisestone.owl.service.ReservationDisableUserService;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.vo.UserVo;
+import kr.wisestone.owl.web.form.PaymentForm;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class ReservationDisableUserServiceImpl extends AbstractServiceImpl<ReservationDisableUser, Long, JpaRepository<ReservationDisableUser, Long>> implements ReservationDisableUserService {
+
+    private static final Logger log = LoggerFactory.getLogger(ReservationDisableUserServiceImpl.class);
+
+    @Autowired
+    private ReservationDisableUserRepository reservationDisableUserRepository;
+
+    @Autowired
+    private PaymentService paymentService;
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    protected JpaRepository<ReservationDisableUser, Long> getRepository() {
+        return this.reservationDisableUserRepository;
+    }
+
+    //  李몄뿬 ��湲� �궗�슜�옄 �삁�빟
+    @Override
+    @Transactional
+    public void add(PaymentForm paymentForm, Payment payment) {
+        ReservationDisableUser reservationDisableUser = new ReservationDisableUser();
+        reservationDisableUser.setPayment(payment);
+        reservationDisableUser.setUserIds(paymentForm.getReservationDisableUserIds());
+
+        this.reservationDisableUserRepository.saveAndFlush(reservationDisableUser);
+    }
+
+    //  李몄뿬 ��湲� �궗�슜�옄 紐⑸줉 議고쉶
+    @Override
+    @Transactional(readOnly = true)
+    public void findReservationDisableUser(Map<String, Object> params, Map<String, Object> resJsonData) {
+        Payment payment = this.paymentService.getPayment(MapUtil.getLong(params, "id"));
+        ReservationDisableUser reservationDisableUser = payment.getReservationDisableUser();
+        List<UserVo> userVos = Lists.newArrayList();
+
+        if (reservationDisableUser != null) {
+            String [] userIds = reservationDisableUser.getUserIds().split(",");
+
+            for (String userId : userIds) {
+                if (!StringUtils.isEmpty(userId)) {
+                    User user = this.userService.getUser(Long.valueOf(userId));
+                    UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class);
+                    userVo.setByName(userVo.getName() + "(" + CommonUtil.decryptAES128(userVo.getAccount()) + ")");
+                    userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount()));
+                    userVos.add(userVo);
+                }
+            }
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, userVos);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/SeverityServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/SeverityServiceImpl.java
new file mode 100644
index 0000000..00ba8f7
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/SeverityServiceImpl.java
@@ -0,0 +1,99 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.Severity;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.SeverityRepository;
+import kr.wisestone.owl.service.SeverityService;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.service.WorkspaceService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.SeverityVo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class SeverityServiceImpl extends AbstractServiceImpl<Severity, Long, JpaRepository<Severity, Long>> implements SeverityService {
+
+    private static final Logger log = LoggerFactory.getLogger(SeverityServiceImpl.class);
+
+    @Autowired
+    private SeverityRepository severityRepository;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    protected JpaRepository<Severity, Long> getRepository() {
+        return this.severityRepository;
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public List<Severity> findByWorkspaceIdOrderByPosition() {
+        Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        return this.severityRepository.findByWorkspaceIdOrderByPosition(workspace.getId());
+    }
+
+    //  以묒슂�룄 �븘�씠�뵒濡� �슦�꽑�닚�쐞瑜� 議고쉶�븳�떎.
+    @Transactional(readOnly = true)
+    @Override
+    public Severity getSeverity(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.SEVERITY_NOT_EXIST));
+        }
+
+        Severity severity = this.findOne(id);
+
+        if (severity == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.SEVERITY_NOT_EXIST));
+        }
+
+        return severity;
+    }
+
+    //  以묒슂�룄 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<SeverityVo> findSeverity(Map<String, Object> resJsonData) {
+        List<SeverityVo> severityVos = ConvertUtil.convertObjectsToClasses(this.findByWorkspaceIdOrderByPosition(), SeverityVo.class);
+        resJsonData.put(Constants.RES_KEY_CONTENTS, severityVos);
+
+        return severityVos;
+    }
+
+    //  湲곕낯�쟻�쑝濡� �젣怨듬릺�뒗 �슦�꽑�닚�쐞 紐⑸줉濡�
+    @Override
+    @Transactional
+    public void addDefaultSeverity(Workspace workspace) {
+        List<Severity> severities = Lists.newArrayList();
+        severities.add(new Severity(this.messageAccessor.message("common.critical"), 1, "#ed5565", workspace));
+        severities.add(new Severity(this.messageAccessor.message("common.major"), 2, "#f8ac59", workspace));
+        severities.add(new Severity(this.messageAccessor.message("common.minor"), 3, "#1c84c6", workspace));
+        severities.add(new Severity(this.messageAccessor.message("common.trivial"), 4, "#23c6c8", workspace));
+
+        this.severityRepository.saveAll(severities);
+    }
+
+    //  �뾽臾닿났媛꾩뿉 �엳�뒗 以묒슂�룄 紐⑸줉�쓣 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<Severity> findByWorkspaceId() {
+        return this.severityRepository.findByWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/SystemEmailServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/SystemEmailServiceImpl.java
new file mode 100644
index 0000000..d6f06f8
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/SystemEmailServiceImpl.java
@@ -0,0 +1,436 @@
+package kr.wisestone.owl.service.impl;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.Lists;
+import com.sun.mail.smtp.SMTPAddressFailedException;
+import kr.wisestone.owl.config.CommonConfiguration;
+import kr.wisestone.owl.constant.MailConstants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.SystemEmail;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.enumType.EmailType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.SystemEmailRepository;
+import kr.wisestone.owl.service.SystemEmailService;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.DateUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.util.StringTemplateUtil;
+import org.apache.commons.io.FilenameUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.mail.MailSendException;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import javax.activation.DataSource;
+import javax.activation.FileDataSource;
+import javax.mail.SendFailedException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeUtility;
+import java.io.IOException;
+import java.util.*;
+
+import org.thymeleaf.context.Context;
+import org.thymeleaf.spring5.SpringTemplateEngine;
+
+@Service
+public class SystemEmailServiceImpl extends AbstractServiceImpl<SystemEmail, Long, JpaRepository<SystemEmail, Long>> implements SystemEmailService {
+
+    private static final Logger log = LoggerFactory.getLogger(SystemEmailServiceImpl.class);
+
+    @Autowired
+    private SystemEmailRepository systemEmailRepository;
+
+    @Autowired
+    private JavaMailSender javaMailSender;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private SpringTemplateEngine springTemplateEngine;
+
+    @Override
+    protected JpaRepository<SystemEmail, Long> getRepository() {
+        return this.systemEmailRepository;
+    }
+
+    @Autowired
+    protected CommonConfiguration commonConfiguration;
+
+    @Value("${email.userName}")
+    private String emailUserName;
+
+    //  �씠硫붿씪 利됱떆 �쟾�넚
+    @Async
+    @Override
+    @Transactional(readOnly = true)
+    public void directEmail(String[] sendUsers, EmailType emailType, Map<String, Object> params, String toUser) {
+        for (String sendUser : sendUsers) {
+            User user = this.userService.findByAccount(sendUser);
+            if (user != null) {
+                Locale locale = CommonUtil.getUserLanguage(user.getLanguage());    //  硫붿씪�쓣 諛쏅뒗 �궗�슜�옄媛� �궗�슜�븯怨� �엳�뒗 �뼵�뼱 �젙蹂대�� 媛��졇�삩�떎.
+                //  諛쏅뒗 �궗�엺�쓽 �뼵�뼱濡� 蹂�寃쏀븯�뿬 利됱떆 �씠硫붿씪�쓣 諛쒖넚�븳�떎.
+                this.makeDirectContextEmail(emailType, params, locale, new String[]{CommonUtil.decryptAES128(user.getAccount())});
+            } else {
+                Locale locale = Locale.getDefault();
+                //  �떊洹� �궗�슜�옄 珥덈�媛숈씠 �떆�뒪�뀥�뿉 �벑濡앸릺吏� �븡�� �궗�슜�옄�씪 �븣�뒗 濡쒓렇�씤�븳 �궗�슜�옄�쓽 �뼵�뼱濡� �쟾�떖�븳�떎.
+                //  濡쒓렇�씤 �젙蹂닿� �뾾�쓣 寃쎌슦�뿉�뒗 �떆�뒪�뀥 湲곕낯 濡쒖��씪濡� 諛쒖넚�븳�떎. - (�뒪耳�伊대윭�굹 鍮꾨룞湲�)
+                if (!StringUtils.isEmpty(toUser)) {
+                    User loginUser = this.userService.findByAccount(toUser);
+
+                    if (loginUser != null) {
+                        locale = CommonUtil.getUserLanguage(loginUser.getLanguage());
+                    }
+                }
+
+                this.makeDirectContextEmail(emailType, params, locale, new String[]{CommonUtil.decryptAES128(sendUser)});
+            }
+        }
+    }
+
+    //  諛쏅뒗 �궗�엺�쓽 �뼵�뼱濡� 蹂�寃쏀븯�뿬 利됱떆 �씠硫붿씪�쓣 諛쒖넚�븳�떎.
+    private void makeDirectContextEmail(EmailType emailType, Map<String, Object> params, Locale locale, String[] sendUsers) {
+        MailConstants mailConstants = null;
+        Context context;
+        String content = null;
+        String[] filePaths = null;
+
+        params.put("url", this.commonConfiguration.getEmailSendUrl());
+
+        switch (emailType) {
+            case WORKSPACE_JOIN:    //  �쉶�썝 媛��엯
+                mailConstants = MailConstants.WORKSPACE_JOIN;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case USER_SEARCH_PASSWORD:    //  鍮꾨�踰덊샇 李얘린
+                mailConstants = MailConstants.USER_SEARCH_PASSWORD;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case USER_WITH_DRAW:    //  �쉶�썝 �깉�눜
+                mailConstants = MailConstants.USER_WITH_DRAW;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case REGULAR_PAYMENT:  //  �젙湲� 寃곗젣 �셿猷�
+                mailConstants = MailConstants.REGULAR_PAYMENT;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case REGULAR_PAYMENT_CANCEL:    //  �젙湲� 寃곗젣 痍⑥냼
+                mailConstants = MailConstants.REGULAR_PAYMENT_CANCEL;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case REGULAR_PAYMENT_CANCEL_BY_ACCOUNTING_MANAGER:  //  �젙湲� 寃곗젣 痍⑥냼 �젙蹂대�� �쉶怨� �떞�떦�옄�뿉寃� �쟾�떖�븳�떎.
+                mailConstants = MailConstants.REGULAR_PAYMENT_CANCEL_BY_ACCOUNTING_MANAGER;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case REGULAR_PAYMENT_MODIFY:    //  �젙湲� 寃곗젣 蹂�寃�
+                mailConstants = MailConstants.REGULAR_PAYMENT_MODIFY;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case WORKSPACE_INVITE_NEW_USER:    //  �떊洹� �궗�슜�옄 珥덈� 硫붿씪
+                mailConstants = MailConstants.WORKSPACE_INVITE_NEW_USER;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case WORKSPACE_INVITE_SYSTEM_USER: //  湲곗〈 �궗�슜�옄 �뾽臾� 怨듦컙 珥덈�
+                mailConstants = MailConstants.WORKSPACE_INVITE_SYSTEM_USER;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case WORKSPACE_MAX_USER_EXCESS:    //  �뾽臾� 怨듦컙 �솢�꽦 �궗�슜�옄 珥덇낵 �븣由� 硫붿씪 �쟾�넚
+                mailConstants = MailConstants.WORKSPACE_MAX_USER_EXCESS;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case ISSUE_SEND:    //  �씠�뒋 �씠硫붿씪濡� ���긽�옄�뿉寃� 諛쒖넚
+                mailConstants = MailConstants.ISSUE_SEND;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case USER_JOIN_STATISTICS:  //  �씪�씪 �궗�슜�옄 媛��엯 �젙蹂� 諛쒖넚
+                mailConstants = MailConstants.USER_JOIN_STATISTICS;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case TOTAL_STATISTICS:
+                mailConstants = MailConstants.TOTAL_STATISTICS;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case WORKSPACE_EXPIRE: //  �뾽臾� 怨듦컙 �궗�슜 湲곌컙 留뚮즺
+                mailConstants = MailConstants.WORKSPACE_EXPIRE;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case WORKSPACE_EXPIRE_ALARM: //  �뾽臾� 怨듦컙 �궗�슜 湲곌컙 留뚮즺 �삁�빟 �븞�궡
+                mailConstants = MailConstants.WORKSPACE_EXPIRE_ALARM;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+        }
+
+        this.sendEmail(this.messageAccessor.message(mailConstants.getTitle(), locale), content, sendUsers, filePaths);
+    }
+
+    //  �씠硫붿씪�쓣 諛쒖넚�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void sendEmail(String subject, String content, String[] to, String[] filePaths) {
+        MimeMessage message = this.javaMailSender.createMimeMessage();
+
+        try {
+            InternetAddress from = new InternetAddress(this.emailUserName, "OWL-ITS");
+            MimeMessageHelper messageHelper = new MimeMessageHelper(message, true, "utf-8");
+            messageHelper.setSubject(subject);
+            messageHelper.setText(content, true);
+            messageHelper.setFrom(from);
+            messageHelper.setTo(to);
+
+            if (filePaths != null && filePaths.length > 0) {
+                for (String filePath : filePaths) {
+                    if (!StringUtils.isEmpty(filePath)) {
+                        DataSource dataSource = new FileDataSource(filePath);
+                        messageHelper.addAttachment(MimeUtility.encodeText(
+                                FilenameUtils.getBaseName(filePath), "utf-8", "B"), dataSource);
+                    }
+                }
+            }
+
+            this.javaMailSender.send(message);
+        } catch (MailSendException e) {
+            log.error(e.getMessage());
+            Exception[] exceptions = e.getMessageExceptions();
+            for (Exception exception : exceptions) {
+                if (exception instanceof SendFailedException) {
+                    Exception nextException = ((SendFailedException) exception).getNextException();
+                    if (nextException instanceof SMTPAddressFailedException) {
+                        throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_EMAIL), e);
+                    }
+                }
+
+                throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EMAIL_NOT_SEND));
+            }
+        } catch (Exception e) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EMAIL_NOT_SEND));
+        }
+    }
+
+    //  �씠硫붿씪 �삁�빟 �쟾�넚
+    @Override
+    @Transactional
+    public void reservationEmail(String[] sendUsers, EmailType emailType, Map<String, Object> params) {
+
+        ObjectMapper mapper = new ObjectMapper();
+        String parameter = null;
+        try {
+            params.put("eventOccurDate", DateUtil.convertDateToStr(new Date()));
+            parameter = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(params);
+        } catch (Exception e) {
+            log.error("systemEmailServiceImpl -> reservationEmail Exception");
+        }
+
+        for (String account : sendUsers) {
+            SystemEmail systemEmail = new SystemEmail();
+            systemEmail.setEmailType(emailType);
+            systemEmail.setSendAddress(account);
+            systemEmail.setParameter(parameter);
+            systemEmail.setSendYn(false);
+
+            this.systemEmailRepository.saveAndFlush(systemEmail);
+        }
+    }
+
+    //  �봽濡쒖젥�듃�뿉�꽌 蹂�寃쎈맂 �씪諛� �궗�슜�옄, �봽濡쒖젥�듃 愿�由ъ옄瑜� 李얘린 �쐞�빐 �궗�슜
+    @Override
+    @Transactional(readOnly = true)
+    public List<String> notificationUserChange(List<User> totalUsers, List<User> targetUsers) {
+        List<String> results = Lists.newArrayList();
+
+        //  �젣�쇅 ���긽�옄 李얘린
+        for (User user : totalUsers) {
+            boolean excludeCheck = true;
+
+            for (User newUser : targetUsers) {
+                if (user.getId().equals(newUser.getId())) {
+                    excludeCheck = false;
+                    break;
+                }
+            }
+
+            if (excludeCheck) {
+                results.add(user.getAccount());
+            }
+        }
+        return results;
+    }
+
+    //  �븘吏� 諛쒖넚�릺吏� �븡�� �씠硫붿씪�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<SystemEmail> findBySendAddressAndSendYn(String sendAddress) {
+        return this.systemEmailRepository.findBySendAddressAndSendYn(sendAddress, false);
+    }
+
+    //  �궗�슜�옄媛� 吏��젙�븳 �떆媛꾩뿉 �씠硫붿씪 諛쒖넚
+    @Override
+    @Transactional
+    public void reservationSendEmail() {
+        //  30遺� 留덈떎 �빐�떦 �떆媛꾩쓣 �씠硫붿씪 �븣�엺 �떆媛꾩쑝濡� �븳 ���긽�옄瑜� 李얜뒗�떎.
+        List<String> sendUsers = this.userService.findByReservationNotifyTime();
+
+        for (String sendUser : sendUsers) {
+            //  �궗�슜�옄 蹂꾨줈 �븘吏� 諛쒖넚�릺吏� �븡�� �씠硫붿씪�쓣 議고쉶�븳�떎.
+            List<SystemEmail> systemEmails = this.findBySendAddressAndSendYn(sendUser);
+            StringBuilder emailBuilder = new StringBuilder();
+            User user = this.userService.findByAccount(sendUser);
+
+            if (user != null) {
+                Locale locale = CommonUtil.getUserLanguage(user.getLanguage());    //  硫붿씪�쓣 諛쏅뒗 �궗�슜�옄媛� �궗�슜�븯怨� �엳�뒗 �뼵�뼱 �젙蹂대�� 媛��졇�삩�떎.
+
+                for (SystemEmail systemEmail : systemEmails) {
+                    //  諛쏅뒗 �궗�엺�쓽 �뼵�뼱濡� 蹂�寃쏀븯�뿬 �삁�빟�맂 �씠硫붿씪�쓣 諛쒖넚�븳�떎.
+                    this.makeReservationContextEmail(emailBuilder, systemEmail, locale);
+                    systemEmail.setSendYn(true);
+                }
+                //  �궡�슜�씠 �엳�쑝硫� 諛쒖넚
+                if (!StringUtils.isEmpty(emailBuilder.toString())) {
+                    //  �씠硫붿씪�쓣 諛쒖넚�븳�떎.
+                    this.sendEmail(this.messageAccessor.message(MsgConstants.RESERVATION_EMAIL_TITLE, locale), emailBuilder.toString(), new String[]{CommonUtil.decryptAES128(sendUser)}, null);
+                    this.systemEmailRepository.saveAll(systemEmails);
+                }
+            }
+        }
+    }
+
+    //  諛쏅뒗 �궗�엺�쓽 �뼵�뼱濡� 蹂�寃쏀븯�뿬 �삁�빟�맂 �씠硫붿씪�쓣 諛쒖넚�븳�떎.
+    private void makeReservationContextEmail(StringBuilder emailBuilder, SystemEmail systemEmail, Locale locale) {
+        MailConstants mailConstants;
+        Context context;
+        String content = null;
+        Map<String, Object> params = new HashMap<>();
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            params = mapper.readValue(systemEmail.getParameter(), new TypeReference<Map<String, Object>>() {
+            });
+        } catch (JsonGenerationException e) {
+            log.error("SystemEmailServiceImpl -> makeTitleAndContent JsonGenerationException");
+        } catch (JsonMappingException e) {
+            log.error("SystemEmailServiceImpl -> makeTitleAndContent JsonMappingException");
+        } catch (IOException e) {
+            log.error("SystemEmailServiceImpl -> makeTitleAndContent IOException");
+        }
+
+        switch (systemEmail.getEmailType()) {
+            case PROJECT_DEFAULT_EXCLUDE:   //  �봽濡쒖젥�듃 �씪諛� �궗�슜�옄 �젣�쇅 �븞�궡
+                mailConstants = MailConstants.PROJECT_DEFAULT_EXCLUDE;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case PROJECT_MANAGER_EXCLUDE:   //  �봽濡쒖젥�듃 愿�由ъ옄 �젣�쇅 �븞�궡
+                mailConstants = MailConstants.PROJECT_MANAGER_EXCLUDE;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case PROJECT_DEFAULT_INCLUDE:   //  �봽濡쒖젥�듃 �씪諛� �궗�슜�옄 李몄뿬 �븞�궡
+                mailConstants = MailConstants.PROJECT_DEFAULT_INCLUDE;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case PROJECT_MANAGER_INCLUDE:   //  �봽濡쒖젥�듃 愿�由ъ옄 李몄뿬 �븞�궡
+                mailConstants = MailConstants.PROJECT_MANAGER_INCLUDE;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case PROJECT_MANAGER_EXCLUDE_AND_PROJECT_DEFAULT_INCLUDE:   //  �봽濡쒖젥�듃 愿�由ъ옄 �젣�쇅 �썑 �씪諛� �궗�슜�옄濡� 李몄뿬 �븞�궡
+                mailConstants = MailConstants.PROJECT_MANAGER_EXCLUDE_AND_PROJECT_DEFAULT_INCLUDE;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case PROJECT_DEFAULT_EXCLUDE_AND_PROJECT_MANAGER_INCLUDE:   //  �씪諛� �궗�슜�옄�뿉�꽌 �젣�쇅 �썑 �봽濡쒖젥�듃 愿�由ъ옄濡� 李몄뿬 �븞�궡
+                mailConstants = MailConstants.PROJECT_DEFAULT_EXCLUDE_AND_PROJECT_MANAGER_INCLUDE;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case ISSUE_ADD: //  �씠�뒋 �깮�꽦
+                mailConstants = MailConstants.ISSUE_ADD;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+
+            case ISSUE_REMOVE: //  �씠�뒋 �궘�젣
+                mailConstants = MailConstants.ISSUE_REMOVE;
+                context = StringTemplateUtil.makeContext(params, locale);
+                content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context);
+                break;
+        }
+
+        emailBuilder.append(content);
+    }
+
+    @Override
+    public void information(Map<String, Object> params) {
+        String email = MapUtil.getString(params, "email");
+        /*Optional<String> description = Optional.of(MapUtil.getString(params, "description"));
+        description.orElseThrow(()-> new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_EMAIL)));*/
+
+        if (email == null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_EMAIL));
+        }
+
+        MimeMessage message = this.javaMailSender.createMimeMessage();
+
+        try {
+            InternetAddress from = new InternetAddress(email);
+            MimeMessageHelper messageHelper = new MimeMessageHelper(message, true, "utf-8");
+            messageHelper.setSubject("OWL ITS 臾몄쓽");
+            messageHelper.setText(MapUtil.getString(params, "description"), false);
+            messageHelper.setFrom(from);
+            messageHelper.setTo(this.emailUserName);
+
+            this.javaMailSender.send(message);
+        } catch (Exception e) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EMAIL_NOT_SEND));
+        }
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/SystemRoleServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/SystemRoleServiceImpl.java
new file mode 100644
index 0000000..98efa5a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/SystemRoleServiceImpl.java
@@ -0,0 +1,29 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.domain.SystemRole;
+import kr.wisestone.owl.repository.SystemRoleRepository;
+import kr.wisestone.owl.service.SystemRoleService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SystemRoleServiceImpl extends AbstractServiceImpl<SystemRole, Long, JpaRepository<SystemRole, Long>> implements SystemRoleService {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(SystemRoleServiceImpl.class);
+
+    @Autowired
+    private SystemRoleRepository systemRoleRepository;
+
+    @Override
+    protected JpaRepository<SystemRole, Long> getRepository() {
+        return this.systemRoleRepository;
+    }
+
+    @Override
+    public SystemRole findByRoleType(String roleType) {
+        return this.systemRoleRepository.findByRoleType(roleType);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/UserHistoryServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/UserHistoryServiceImpl.java
new file mode 100644
index 0000000..ee5bb60
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/UserHistoryServiceImpl.java
@@ -0,0 +1,41 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.domain.UserHistory;
+import kr.wisestone.owl.repository.UserHistoryRepository;
+import kr.wisestone.owl.service.UserHistoryService;
+import kr.wisestone.owl.util.DateUtil;
+import kr.wisestone.owl.vo.UserVo;
+import kr.wisestone.owl.web.condition.UserHistoryCondition;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Map;
+
+@Service
+public class UserHistoryServiceImpl extends AbstractServiceImpl<UserHistory, Long, JpaRepository<UserHistory, Long>> implements UserHistoryService {
+
+    @Autowired
+    private UserHistoryRepository userHistoryRepository;
+
+    @Override
+    protected JpaRepository<UserHistory, Long> getRepository() {
+        return this.userHistoryRepository;
+    }
+
+    @Transactional
+    public void addUserHistory(Map<String, Object> resJsonData, UserHistoryCondition userHistoryCondition) {
+
+        UserVo userVo = this.webAppUtil.getLoginUser();
+
+        String historyType = userHistoryCondition.getHistoryType();
+        UserHistory history = new UserHistory();
+        history.setHistoryType(historyType);
+
+        userHistoryRepository.saveAndFlush(history);
+
+        resJsonData.put("name", userVo.getName());
+        resJsonData.put("registerDate", DateUtil.convertDateToYYYYMMDD(history.getRegisterDate()));
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/UserInviteProjectServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/UserInviteProjectServiceImpl.java
new file mode 100644
index 0000000..282d1eb
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/UserInviteProjectServiceImpl.java
@@ -0,0 +1,55 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.Project;
+import kr.wisestone.owl.domain.UserInvite;
+import kr.wisestone.owl.domain.UserInviteProject;
+import kr.wisestone.owl.repository.UserInviteProjectRepository;
+import kr.wisestone.owl.service.ProjectService;
+import kr.wisestone.owl.service.UserInviteProjectService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+
+@Service
+public class UserInviteProjectServiceImpl extends AbstractServiceImpl<UserInviteProject, Long, JpaRepository<UserInviteProject, Long>> implements UserInviteProjectService {
+
+    private static final Logger log = LoggerFactory.getLogger(UserInviteProjectServiceImpl.class);
+
+    @Autowired
+    private UserInviteProjectRepository userInviteProjectRepository;
+
+    @Autowired
+    private ProjectService projectService;
+
+    @Override
+    protected JpaRepository<UserInviteProject, Long> getRepository() {
+        return this.userInviteProjectRepository;
+    }
+
+    @Override
+    @Transactional
+    public List<UserInviteProject> addUserInviteProject(List<Long> projectIds, UserInvite userInvite) {
+        List<UserInviteProject> userInviteProjects = Lists.newArrayList();
+
+        for (Long projectId : projectIds) {
+            Project project = this.projectService.getProject(projectId);
+
+            if (project != null) {
+                //  �씠誘� �빐�떦 �썙�겕�뒪�럹�씠�뒪�뿉�꽌 �빐�떦 �봽濡쒖젥�듃瑜� 珥덈��븳 �젙蹂닿� �엳�쑝硫� �뜑�씠�긽 異붽��븯吏� �븡�뒗�떎.
+                List<UserInviteProject> userInviteProjectList = this.userInviteProjectRepository.findByUserInviteIdAndProjectId(userInvite.getId(), project.getId());
+
+                if (userInviteProjectList.isEmpty()) {
+                    UserInviteProject userInviteProject = new UserInviteProject(userInvite, project);
+                    userInviteProjects.add(userInviteProject);
+                }
+            }
+        }
+
+        return userInviteProjects;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/UserInviteServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/UserInviteServiceImpl.java
new file mode 100644
index 0000000..dc078ef
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/UserInviteServiceImpl.java
@@ -0,0 +1,322 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.EmailType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.UserInviteRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.web.form.UserInviteForm;
+import org.apache.commons.validator.routines.EmailValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class UserInviteServiceImpl extends AbstractServiceImpl<UserInvite, Long, JpaRepository<UserInvite, Long>> implements UserInviteService {
+
+    private static final Logger log = LoggerFactory.getLogger(UserInviteServiceImpl.class);
+
+    @Autowired
+    private UserInviteRepository userInviteRepository;
+
+    @Autowired
+    private UserInviteProjectService userInviteProjectService;
+
+    @Autowired
+    private SystemEmailService systemEmailService;
+
+    @Autowired
+    private ProjectRoleService projectRoleService;
+
+    @Autowired
+    private ProjectRoleUserService projectRoleUserService;
+
+    @Autowired
+    private UserWorkspaceService userWorkspaceService;
+
+    @Autowired
+    private ProjectService projectService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private SimpMessagingTemplate simpMessagingTemplate;
+
+    @Override
+    protected JpaRepository<UserInvite, Long> getRepository() {
+        return this.userInviteRepository;
+    }
+
+    //  �빐�떦 �뾽臾� 怨듦컙�뿉 李몄뿬�븯怨� �엳�뒗吏� �솗�씤 �썑 �뾽臾� 怨듦컙�뿉 李몄뿬�떆�궓�떎. �삉�븳 �봽濡쒖젥�듃�뿉 李몄뿬�릺�뼱 �엳�뒗吏� �솗�씤 �썑 �봽濡쒖젥�듃�뿉�룄 李몄뿬�떆�궓�떎.
+    @Override
+    @Transactional
+    public void checkInviteUser(User user) {
+        List<UserInvite> userInvites = this.userInviteRepository.findByEmailAndStatus(user.getAccount(), UserInvite.WORKSPACE_JOIN_READY);
+
+        for (UserInvite userInvite : userInvites) {
+            Workspace workspace = userInvite.getWorkspace();
+            //  �뾽臾� 怨듦컙 李몄뿬
+            this.includeWorkspace(user, workspace);
+            //  �봽濡쒖젥�듃�뿉 李몄뿬
+            this.includeProject(user, userInvite);
+            //  �뾽臾� 怨듦컙 珥덈� �긽�깭瑜� �셿猷뚮줈 �뾽�뜲�씠�듃
+            userInvite.setStatus(UserInvite.WORKSPACE_JOIN_COMPLETE);
+        }
+
+        if (userInvites.size() > 0) {
+            //  �봽濡쒖젥�듃 �뿭�븷怨� �궗�슜�옄 �뿰寃� �뾽�뜲�씠�듃
+            //this.userService.saveAndFlush(user);
+            //  珥덈� �긽�깭 �뾽�뜲�씠�듃
+            this.userInviteRepository.saveAll(userInvites);
+        }
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 李몄뿬
+    private void includeWorkspace(User user, Workspace workspace) {
+        UserWorkspace userWorkspace = this.userWorkspaceService.findByUserIdAndWorkspaceId(user.getId(), workspace.getId());
+
+        if (userWorkspace != null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.INVITE_USER_USED_WORKSPACE));
+        }
+
+        //  �빐�떦 �뾽臾� 怨듦컙�쓽 �솢�꽦�씤 �궗�슜�옄
+        Integer activeUserCount = this.userWorkspaceService.countByWorkspaceIdAndUseYn(workspace.getId(), true);
+        //  �뾽臾� 怨듦컙�뿉 �뜑�씠�긽 �궗�슜�옄瑜� 諛쏆쓣 怨듦컙�씠 �뾾�쑝硫� 鍮꾪솢�꽦 �긽�깭濡� �뾽臾� 怨듦컙�� �궗�슜�옄瑜� �뿰寃고븳�떎.
+        if (workspace.getMaxUser() > activeUserCount) {
+            this.userWorkspaceService.addUserWorkspace(user, workspace, false, true);
+        }
+        else {
+            this.userWorkspaceService.addUserWorkspace(user, workspace, false, false);
+            //  �쎒 �냼耳볦쑝濡� �뾽臾� 怨듦컙 愿�由ъ옄�뿉寃� �씤�썝�닔 珥덇낵瑜� �븣�젮以��떎.
+            List<UserWorkspace> userWorkspaces = this.userWorkspaceService.findByWorkspaceIdAndManagerYn(workspace.getId(), true);
+            //  愿�由ъ옄�뿉寃� �븣由� �뙘�뾽 �몴�떆 && 硫붿씪 �쟾�넚
+            for (UserWorkspace managerUserWorkspace : userWorkspaces) {
+                User sendUser = managerUserWorkspace.getUser();
+                this.simpMessagingTemplate.convertAndSendToUser(sendUser.getAccount(), "/notification/workspace-max-user-excess", this.messageAccessor.getMessage(MsgConstants.WORKSPACE_MAX_USER_EXCESS));
+
+                Map<String, Object> workspaceMap = new HashMap<>();
+                workspaceMap.put("workspaceName", workspace.getName());
+                workspaceMap.put("activeUser", activeUserCount);
+                workspaceMap.put("disableUser", this.userWorkspaceService.countByWorkspaceIdAndUseYn(workspace.getId(), false));
+                //  �뾽臾� 怨듦컙 �솢�꽦 �궗�슜�옄 珥덇낵 �븣由� 硫붿씪 �쟾�넚
+                this.systemEmailService.directEmail(new String[]{sendUser.getAccount()}, EmailType.WORKSPACE_MAX_USER_EXCESS, workspaceMap, null);
+            }
+        }
+    }
+
+    //  湲곕낯 �뾽臾� 怨듦컙�뿉 李몄뿬
+    public void includePrimaryWorkspace(User user, Workspace workspace) {
+        //  �빐�떦 �뾽臾� 怨듦컙�쓽 �솢�꽦�씤 �궗�슜�옄
+        Integer activeUserCount = this.userWorkspaceService.countByWorkspaceIdAndUseYn(workspace.getId(), true);
+        //  �뾽臾� 怨듦컙�뿉 �뜑�씠�긽 �궗�슜�옄瑜� 諛쏆쓣 怨듦컙�씠 �뾾�쑝硫� 鍮꾪솢�꽦 �긽�깭濡� �뾽臾� 怨듦컙�� �궗�슜�옄瑜� �뿰寃고븳�떎.
+        if (workspace.getMaxUser() > activeUserCount) {
+            this.userWorkspaceService.addUserWorkspace(user, workspace, false, true);
+        }
+        else {
+            this.userWorkspaceService.addUserWorkspace(user, workspace, false, false);
+            //  �쎒 �냼耳볦쑝濡� �뾽臾� 怨듦컙 愿�由ъ옄�뿉寃� �씤�썝�닔 珥덇낵瑜� �븣�젮以��떎.
+            List<UserWorkspace> userWorkspaces = this.userWorkspaceService.findByWorkspaceIdAndManagerYn(workspace.getId(), true);
+            //  愿�由ъ옄�뿉寃� �븣由� �뙘�뾽 �몴�떆 && 硫붿씪 �쟾�넚
+            for (UserWorkspace managerUserWorkspace : userWorkspaces) {
+                User sendUser = managerUserWorkspace.getUser();
+                this.simpMessagingTemplate.convertAndSendToUser(sendUser.getAccount(), "/notification/workspace-max-user-excess", this.messageAccessor.getMessage(MsgConstants.WORKSPACE_MAX_USER_EXCESS));
+
+                Map<String, Object> workspaceMap = new HashMap<>();
+                workspaceMap.put("workspaceName", workspace.getName());
+                workspaceMap.put("activeUser", activeUserCount);
+                workspaceMap.put("disableUser", this.userWorkspaceService.countByWorkspaceIdAndUseYn(workspace.getId(), false));
+                //  �뾽臾� 怨듦컙 �솢�꽦 �궗�슜�옄 珥덇낵 �븣由� 硫붿씪 �쟾�넚
+                this.systemEmailService.directEmail(new String[]{sendUser.getAccount()}, EmailType.WORKSPACE_MAX_USER_EXCESS, workspaceMap, null);
+            }
+        }
+    }
+    //  �봽濡쒖젥�듃�뿉 李몄뿬
+    private void includeProject(User user, UserInvite userInvite) {
+        for (UserInviteProject userInviteProject : userInvite.getUserInviteProjects()) {
+            Project project = userInviteProject.getProject();
+            //  湲곕낯 �뿭�븷 �솗�씤
+            ProjectRole defaultProjectRole = this.projectRoleService.findByProjectIdAndRoleType(project.getId(), ProjectRole.TYPE_DEFAULT);
+            //  愿�由ъ옄 �뿭�븷 �솗�씤
+            ProjectRole managerProjectRole = this.projectRoleService.findByProjectIdAndRoleType(project.getId(), ProjectRole.TYPE_MANAGER);
+
+            //  �봽濡쒖젥�듃 愿�由ъ옄 �뿭�븷�뿉 �씠誘� �뿰寃곕릺�뼱 �엳�쑝硫� �빐�떦 �봽濡쒖젥�듃�뿉 紐삳뱾�뼱媛꾨떎.
+            ProjectRoleUser managerProjectRoleUser = this.projectRoleUserService.findByProjectRoleIdAndUserId(managerProjectRole.getId(), user.getId());
+            if (managerProjectRoleUser != null) {
+                continue;
+            }
+
+            ProjectRoleUser defaultProjectRoleUser = this.projectRoleUserService.findByProjectRoleIdAndUserId(defaultProjectRole.getId(), user.getId());
+            //  �빐�떦 �뿭�븷�뿉 �궗�슜�옄 �뿰寃곗씠 �븞�릺�뼱 �엳�쓣 寃쎌슦�뿉留� �뿰寃�
+            if (defaultProjectRoleUser == null) {
+                user.addProjectRole(defaultProjectRole);
+            }
+        }
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 珥덈��븳 �젙蹂대�� ���옣�븯怨� �씠誘� 媛��엯�릺�뼱 �엳�뒗 �궗�슜�옄�뒗 利됱떆 �빐�떦 �뾽臾� 怨듦컙, �봽濡쒖젥�듃�뿉 李몄뿬�떆�궓�떎.
+    @Override
+    @Transactional
+    public void inviteWorkspace(UserInviteForm userInviteForm) {
+
+        for (String email : userInviteForm.getEmails()) {
+            //  �씠硫붿씪 �쑀�슚�꽦 寃�利�
+            this.verifyEmail(CommonUtil.decryptAES128(email));
+
+            User user = this.userService.findByAccount(email);
+            Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+
+            //  �궗�슜�옄媛� �씠誘� 議댁옱�븷 寃쎌슦 �빐�떦 �뾽臾� 怨듦컙�뿉 珥덈��븳�떎.
+            //  �궗�슜�옄媛� �뾾�쓣 寃쎌슦 珥덈� 硫붿씪�쓣 �쟾�떖�븳�떎.
+            //  珥덈�諛쏆� �궗�슜�옄�뒗 �쉶�썝 媛��엯�떆 �솗�씤�쓣 嫄곗퀜 �빐�떦 �뾽臾� 怨듦컙濡� 媛��엯�떆�궓�떎.
+            if (user != null) {
+                //  �씠 珥덈�瑜� 蹂대궦 �궗�슜�옄�뒗 留덉�留됱쑝濡� �꽑�깮�븳 �뾽臾� 怨듦컙�뿉�꽌 珥덈�瑜� 蹂대궦 寃껋씠�떎. - 愿�由ъ옄肉먮쭔 �븘�땲�씪 �빐�떦 �뾽臾� 怨듦컙�쓽 紐⑤뱺 �궗�슜�옄�뒗 �궗�엺�뱾�쓣 珥덈��븷 �닔 �엳�떎.
+                UserWorkspace userWorkspace = this.userWorkspaceService.findByUserIdAndWorkspaceId(user.getId(), workspace.getId());
+
+                if (userWorkspace != null) {
+                    throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.INVITE_USER_USED_WORKSPACE));
+                }
+                else {
+                    //  �뾽臾� 怨듦컙 湲곗〈 �궗�슜�옄�뿉寃� 珥덈� 硫붿씪 諛쒖넚 - �옄�룞�쑝濡� �빐�떦 �뾽臾� 怨듦컙�뿉 李몄뿬�맖
+                    this.addInvite(userInviteForm, workspace, email, EmailType.WORKSPACE_INVITE_SYSTEM_USER);
+                    //  �빐�떦 �뾽臾� 怨듦컙�뿉 媛��엯
+                    this.checkInviteUser(user);
+                }
+            }
+            else {
+                //  �뾽臾� 怨듦컙 珥덈� 硫붿씪 諛쒖넚 - �궗�슜�옄媛� 吏곸젒 媛��엯�빐�빞�븿.
+                this.addInvite(userInviteForm, workspace, email, EmailType.WORKSPACE_INVITE_NEW_USER);
+            }
+        }
+    }
+
+    //  �씠硫붿씪 �쑀�슚�꽦 寃�利�
+    private void verifyEmail(String email) {
+        if (StringUtils.isEmpty(email)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_EMAIL));
+        }
+        else {
+            if (!EmailValidator.getInstance().isValid(email)) {
+                throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_EMAIL));
+            }
+        }
+    }
+
+    //  珥덈� �젙蹂� ���옣 諛� 硫붿씪 諛쒖넚
+    private void addInvite(UserInviteForm userInviteForm, Workspace workspace, String email, EmailType emailType) {
+        //  user invite �젙蹂대�� �깮�꽦.
+        //  李몄뿬�릺�뒗 �봽濡쒖젥�듃 �뀑�똿
+        //  �깮�꽦 醫낅즺 諛� �씠硫붿씪 諛쒖넚
+        UserInvite userInvite = this.userInviteRepository.findByEmailAndWorkspaceId(email, workspace.getId());
+
+        //  �씠誘� 珥덈��옣�쓣 蹂대궦�쟻�씠 �엳�쑝硫� 異붽��빐�빞 �븷 �봽濡쒖젥�듃 �젙蹂대�� 異붿텧�븳�떎.
+        if (userInvite != null) {
+            for (UserInviteProject userInviteProject : userInvite.getUserInviteProjects()) {
+                Project project = userInviteProject.getProject();
+
+                for (int count = 0; count < userInviteForm.getProjectIds().size(); count++) {
+                    if (userInviteForm.getProjectIds().get(count).equals(project.getId())) {
+                        userInviteForm.getProjectIds().remove(count);
+                        break;
+                    }
+                }
+            }
+        }
+        else {
+            List<Long> addProjectIds = this.checkHasProjectRole(email, userInviteForm.getProjectIds());
+            userInvite = new UserInvite();
+            userInvite.setEmail(email);
+            userInvite.setStatus(UserInvite.WORKSPACE_JOIN_READY);
+            userInvite.setWorkspace(workspace);
+
+            //  留뚯빟 異붽��빐�빞�븷 �봽濡쒖젥�듃媛� �뾾�쓣 寃쎌슦(�씠誘� �빐�떦 �봽濡쒖젥�듃�뿉 �냼�냽�릺�뼱�엳�쓣 寃쎌슦) �뿬湲곗꽌 以묐떒�븳�떎.
+            if (addProjectIds.isEmpty()) {
+                this.userInviteRepository.saveAndFlush(userInvite);
+                //  珥덈� 硫붿씪�쓣 諛쒖넚�븳�떎.
+                this.sendInviteEmail(workspace, emailType, email);
+                return;
+            }
+        }
+
+        this.userInviteRepository.saveAndFlush(userInvite);
+
+        //  李몄뿬 �봽濡쒖젥�듃 �뀑�똿
+        List<UserInviteProject> userInviteProjects = this.userInviteProjectService.addUserInviteProject(userInviteForm.getProjectIds(), userInvite);
+
+        if (!userInviteProjects.isEmpty()) {
+            for (UserInviteProject userInviteProject : userInviteProjects) {
+                userInvite.addUserInviteProjects(userInviteProject);
+            }
+
+            this.userInviteRepository.saveAndFlush(userInvite);
+        }
+
+        //  珥덈� 硫붿씪�쓣 諛쒖넚�븳�떎.
+        this.sendInviteEmail(workspace, emailType, email);
+    }
+
+    //  珥덈� 硫붿씪�쓣 諛쒖넚�븳�떎.
+    private void sendInviteEmail(Workspace workspace, EmailType emailType, String sendEmail) {
+        Map<String, Object> userInviteMap = new HashMap<>();
+        userInviteMap.put("inviteUserName", this.webAppUtil.getLoginUser().getName());
+        userInviteMap.put("workspaceName", workspace.getName());
+
+        //  �뾽臾� 怨듦컙 珥덈� 硫붿씪 諛쒖넚
+        this.systemEmailService.directEmail(new String[]{sendEmail}, emailType, userInviteMap, this.webAppUtil.getLoginUser().getAccount());
+    }
+
+    //  �빐�떦 �봽濡쒖젥�듃�뿭�븷�뿉 �궗�슜�옄媛� �씠誘� �냼�냽�릺�뼱 �엳�뒗吏� �솗�씤�븳�떎.
+    private List<Long> checkHasProjectRole(String email, List<Long> projectIds) {
+        List<Long> addProjectIds = Lists.newArrayList();
+        boolean projectHasRole = false;
+        User user = this.userService.findByAccount(email);
+        //  媛��엯�릺�뼱�엳吏� �븡�� �궗�슜�옄�뒗 泥댄겕�븯吏� �븡�뒗�떎.
+        if (user == null) {
+            return projectIds;
+        }
+
+        for (Long projectId : projectIds) {
+            Project project = this.projectService.getProject(projectId);
+
+            if (project != null) {
+                //  �봽濡쒖젥�듃 李몄뿬 �뿬遺� �솗�씤
+                for (ProjectRole projectRole : project.getProjectRoles()) {
+                    ProjectRoleUser projectRoleUser = this.projectRoleUserService.findByProjectRoleIdAndUserId(projectRole.getId(), user.getId());
+                    if (projectRoleUser != null) {
+                        projectHasRole = true;
+                        break;
+                    }
+                }
+                //  留뚯빟 �봽濡쒖젥�듃 �뿭�븷�뿉 �냼�냽�릺�뼱 �엳吏� �븡�쑝硫� 珥덈�瑜� �븯�옄.
+                if (!projectHasRole) {
+                    addProjectIds.add(project.getId());
+                }
+            }
+        }
+
+        return addProjectIds;
+    }
+
+    //  �씠�쟾�뿉 蹂대궦 珥덈� �젙蹂� �궘�젣
+    @Override
+    @Transactional
+    public void deleteUserInvite(Long workspaceId, List<String> email) {
+        List<UserInvite> userInvites = this.userInviteRepository.findByWorkspaceIdAndEmailIn(workspaceId, email);
+        this.userInviteRepository.deleteAll(userInvites);
+        this.userInviteRepository.flush();
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/UserServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/UserServiceImpl.java
new file mode 100644
index 0000000..bf2204e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/UserServiceImpl.java
@@ -0,0 +1,1427 @@
+package kr.wisestone.owl.service.impl;
+
+import com.amazonaws.services.s3.AmazonS3;
+import com.google.common.collect.Lists;
+import com.google.gson.Gson;
+import kr.wisestone.owl.config.security.service.UserSecurityService;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MngPermission;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.EmailType;
+import kr.wisestone.owl.domain.enumType.SocialType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.UserMapper;
+import kr.wisestone.owl.repository.UserRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.*;
+import kr.wisestone.owl.vo.*;
+import kr.wisestone.owl.web.condition.UserCondition;
+import kr.wisestone.owl.web.form.UserForm;
+import kr.wisestone.owl.web.view.ExcelView;
+import org.apache.commons.validator.routines.EmailValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.security.core.session.SessionInformation;
+import org.springframework.security.core.session.SessionRegistry;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.ui.Model;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.*;
+import java.util.regex.Pattern;
+
+@Service
+public class UserServiceImpl extends AbstractServiceImpl<User, Long, JpaRepository<User, Long>> implements UserService {
+
+    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private SystemRoleService systemRoleService;
+
+    @Autowired
+    private SystemEmailService systemEmailService;
+
+    @Autowired
+    private SimpMessagingTemplate simpMessagingTemplate;
+
+    @Autowired
+    private UserMapper userMapper;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private UserWorkspaceService userWorkspaceService;
+
+    @Autowired
+    private ProjectRoleUserService projectRoleUserService;
+
+    @Autowired
+    private ProjectRoleService projectRoleService;
+
+    @Autowired
+    private ProjectService projectService;
+
+    @Autowired
+    private UserSecurityService userSecurityService;
+
+    @Autowired
+    private UserInviteService userInviteService;
+
+    @Autowired
+    private UserWithDrawService userWithDrawService;
+
+    @Autowired
+    private AttachedFileService attachedFileService;
+
+    @Autowired
+    private IssueService issueService;
+
+    @Autowired
+    private SessionRegistry sessionRegistry;
+
+    @Value("${OAuth.google.clientId}")
+    private String googleClientId;
+
+    @Value("${OAuth.google.clientSecret}")
+    private String googleClientSecret;
+
+    @Value("${OAuth.google.redirectUri}")
+    private String googleClientRedirectUri;
+
+    @Value("${OAuth.naver.clientId}")
+    private String naverClientId;
+
+    @Value("${OAuth.naver.clientSecret}")
+    private String naverClientSecret;
+
+    @Value("${OAuth.kakao.clientId}")
+    private String kakaoClientId;
+
+    @Value("${OAuth.kakao.clientSecret}")
+    private String kakaoClientSecret;
+
+    @Value("${OAuth.facebook.clientId}")
+    private String facebookClientId;
+
+    @Value("${OAuth.facebook.clientSecret}")
+    private String facebookClientSecret;
+
+    @Value("${OAuth.facebook.redirectUri}")
+    private String facebookClientRedirectUri;
+
+    @Value("${OAuth.common.state}")
+    private String oAuthState;
+
+    @Value("${use.aws}")
+    private boolean bUseAWS;
+
+    @Value("${owl.license.key}")
+    private String owlLicenseKey;
+
+    @Autowired
+    private AmazonS3 amazonS3;
+
+    @Value("${aws.bucket.name}")
+    private String bucketName;
+
+    @Value("${aws.s3.url}")
+    private String awsS3Url;
+
+    @Value("${profile.file.path}")
+    private String profileFolder;
+
+    @Value("${user.join.statistics.email}")
+    private String userJoinStatisticsEmail; //  �궗�슜�옄 媛��엯 �젙蹂� �떞�떦�옄�뿉寃� �븣由�
+
+    @Value("${total.statistics.email}")
+    private String totalStatisticsEmail; //  �봽濡쒖젥�듃, �씠�뒋, �궗�슜�옄�닔 �븣由�
+
+    @Autowired
+    private ExcelView excelView;
+
+    @Autowired
+    private PasswordEncoder passwordEncoder;
+
+    @Override
+    protected JpaRepository<User, Long> getRepository() {
+        return this.userRepository;
+    }
+
+    //  �궗�슜�옄 媛��엯 �썑 �뾽臾� 怨듦컙瑜� �깮�꽦�븳�떎.
+    @Override
+    @Transactional
+    public User addUser(UserForm userForm, MultipartFile profile) {
+        User user = ConvertUtil.copyProperties(userForm, User.class, "socialType");
+
+        // edit by zenith at 20200722
+        // form �쓽 license瑜� �솗�씤�븯怨� �젙�긽�쟻�씠硫�, 愿�由ъ옄濡� �엯�젰�븳�떎.
+        Integer validAdmin = this.verifyLicenseKey(userForm.getLicensekey());
+
+        //  �냼�뀥 �쉶�썝 媛��엯�씪 �븣 �냼�뀥 �젙蹂대�� ���옣
+        if (userForm.getSocialType() != null) {
+            user.setSocialType(SocialType.valueOf(userForm.getSocialType()));
+        }
+
+        //  �씠由� �쑀�슚�꽦 寃��궗
+        this.verifyUserName(user.getName());
+        //  怨꾩젙 �쑀�슚�꽦 寃��궗
+        this.verifyAccount(user.getId(), user.getAccount());
+        //  鍮꾨�踰덊샇 �쑀�슚�꽦 寃��궗
+        this.verifyPassword(user.getPassword());
+        //  �뿰�씫泥� �쑀�슚�꽦 寃��궗
+        this.verifyPhone(user.getPhone());
+
+        user.addSystemRole(this.systemRoleService.findByRoleType(Permission.ROLE_TYPE_WORKSPACE_MANAGER));
+
+        //  �씠硫붿씪 �젙蹂� �븫�샇�솕
+        user.setAccount(CommonUtil.encryptAES128(userForm.getAccount()));
+        user.setPassword(this.passwordEncoder.encode(user.getPassword()));
+
+        this.userRepository.saveAndFlush(user);
+
+        //  �봽濡쒗븘 �뙆�씪�씠 �쑀�슚�븳吏� 泥댄겕�븳�떎.
+        this.verifyProfile(profile);
+        //  �봽濡쒗븘�쓣 �꽑�깮�븯吏� �븡�븯�쑝硫� �떆�뒪�뀥 �봽濡쒗븘�씠 湲곕낯 �꽑�깮�맂�떎.
+        this.changeProfile(user, profile);
+        //  �궗�슜�옄 湲곕낯 �뼵�뼱瑜� �꽌踰� �뼵�뼱濡� �꽕�젙�븳�떎.
+        LocaleContextHolder.setLocale(CommonUtil.getUserLanguage(user.getLanguage()));
+        //  �뾽臾� 怨듦컙 紐낆씠 鍮꾩뼱�엳�쓣 寃쎌슦 �떆�뒪�뀥�뿉�꽌 留뚮뱾�뼱以��떎.
+        if (StringUtils.isEmpty(userForm.getWorkspaceName())) {
+            userForm.setWorkspaceName(user.getName() + this.messageAccessor.message("common.sWorkspace"));
+        }
+
+        if(validAdmin == 0) {   // �씪�씠�꽱�뒪 誘몄엯�젰 �씪諛� �궗�슜�옄
+            //  �뾽臾� 怨듦컙瑜� 媛��졇�삩�떎.
+            Workspace primaryWorkspace = this.workspaceService.getPrimaryWorkspace();
+            user.setLastWorkspaceId(primaryWorkspace.getId());
+
+            this.userInviteService.includePrimaryWorkspace(user, primaryWorkspace);
+
+            // edit by zenith for permission at 20200803
+            user.setPermission(MngPermission.USER_PERMISSION_MNG_NONE);
+
+        } else if(validAdmin == 1) {    // �씪�씠�꽱�뒪 �엯�젰 愿�由ъ옄
+            Workspace primaryWorkspace = this.workspaceService.getPrimaryWorkspace();
+
+            if(primaryWorkspace == null || primaryWorkspace.getName() != userForm.getWorkspaceName()) {
+                //  �뾽臾� 怨듦컙瑜� �깮�꽦�븳�떎. 媛��엯�븳 �궗�슜�옄�뒗 �뾽臾� 怨듦컙�쓽 二쇱씤�씠�떎.
+                Workspace workspace = this.workspaceService.addWorkspace(userForm.getWorkspaceName());
+                this.userWorkspaceService.addUserWorkspace(user, workspace, true, true);
+
+                //  湲곕낯�쑝濡� �젣怨듬릺�뒗 �봽濡쒖젥�듃瑜� �깮�꽦�븳�떎.
+//                this.projectService.addDefaultProject(user, workspace);
+
+                user.setLastWorkspaceId(workspace.getId());
+
+                // edit by zenith for permission at 20200803
+                user.setPermission(MngPermission.makeAllPermission());
+            } else  {
+                this.userInviteService.includePrimaryWorkspace(user, primaryWorkspace);
+                user.setLastWorkspaceId(primaryWorkspace.getId());
+
+                user.setPermission(MngPermission.makeSubAllPermission());
+            }
+        }
+
+        //  �씠硫붿씪 �븣由� �삁�젙 �떆媛꾩씠 怨듬갚�씠硫� �뵒�뤃�듃 �씠硫붿씪 �븣由� �삁�젙 �떆媛꾩쑝濡� �꽕�젙�븳�떎.
+        user.setReservationNotifyTime(User.DEFAULT_RESERVATION_NOTIFY_TIME);
+        this.userRepository.saveAndFlush(user);
+
+        //  珥덈�諛쏆� 硫붿씪�씠 �엳�뒗 �궗�슜�옄媛� 媛��엯�뻽�쑝硫� 珥덈��뻽�쓣 �븣 �꽑�깮�븳 �봽濡쒖젥�듃�뿉 李몄뿬 �떆�궓�떎.
+        this.userInviteService.checkInviteUser(user);
+
+        Map<String, Object> userMap = new HashMap<>();
+        userMap.put("name", user.getName());
+        userMap.put("account", CommonUtil.decryptAES128(user.getAccount()));
+        userMap.put("registerDate", DateUtil.convertDateToYYYYMMDD(user.getRegisterDate()));
+
+        //  �쉶�썝 媛��엯 �븣由� 硫붿씪 �쟾�넚
+        this.systemEmailService.directEmail(new String[]{user.getAccount()}, EmailType.WORKSPACE_JOIN, userMap, null);
+
+        return user;
+    }
+
+    //  �씠由� �쑀�슚�꽦 寃��궗
+    private void verifyUserName(String userName) {
+        if (StringUtils.isEmpty(userName)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NO_NAME));
+        }
+
+        if (userName.length() > 50) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NAME_LENGTH_EXCESS));
+        }
+    }
+
+    //  怨꾩젙 �쑀�슚�꽦 寃��궗
+    private void verifyAccount(Long id, String account) {
+        if (StringUtils.isEmpty(account)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NO_EMAIL));
+        } else {
+            if (!EmailValidator.getInstance().isValid(account)) {
+                throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_EMAIL));
+            }
+
+            //  以묐났 �뿬遺� 泥댄겕
+            if (id == null) {
+                if (this.findByAccount(CommonUtil.encryptAES128(account)) != null) {
+                    throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_USED_EMAIL));
+                }
+            } else {
+                if (this.userRepository.findByIdNotAndAccount(id, CommonUtil.encryptAES128(account)) != null) {
+                    throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_USED_EMAIL));
+                }
+            }
+
+            //  �쉶�썝 �깉�눜 湲곕줉 議고쉶
+            this.checkWithDrawAccount(CommonUtil.encryptAES128(account));
+        }
+    }
+
+    //  �쉶�썝 �깉�눜 湲곕줉 議고쉶
+    private void checkWithDrawAccount(String account) {
+        if (this.userWithDrawService.findByAccount(account) != null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_WITH_DRAW_EXIST));
+        }
+    }
+
+    //  鍮꾨�踰덊샇 �쑀�슚�꽦 寃��궗
+    private void verifyPassword(String password) {
+        if (StringUtils.isEmpty(password)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NO_PASSWORD));
+        }
+
+        //  �궗�슜�옄媛� �엯�젰�븷 �닔 �엳�뒗 鍮꾨�踰덊샇�뒗 20湲��옄�씠吏�留� MD5 �븫�샇�솕瑜� 嫄곗튂湲� �븣臾몄뿉 Table 而щ읆 湲몄씠 100�쑝濡� �븳�떎.
+        if (password.length() > 200) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PASSWORD_MAX_LENGTH_OUT));
+        }
+    }
+
+    //  �뿰�씫泥� �쑀�슚�꽦 寃��궗
+    private void verifyPhone(String phone) {
+        if (!StringUtils.isEmpty(phone)) {
+            if (phone.length() > 20) {
+                throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PHONE_MAX_LENGTH_OUT));
+            }
+
+            if (!Pattern.matches("^[0-9]*$", phone)) {
+                throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PHONE_ONLY_NUMBER));
+            }
+        }
+    }
+
+    //  �씪�씠�꽱�뒪�궎 �쑀�슚�꽦 寃��궗
+    private Integer verifyLicenseKey(String licensekey) {
+        Integer validAdminUser = 0; // 0 : �씪諛섏궗�슜�옄, 1: 愿�由ъ옄, -1 : 鍮꾩젙�긽 �씪�씠�꽱�뒪�엯�젰
+
+        if (StringUtils.isEmpty(licensekey)) {
+            return validAdminUser;
+        }
+
+        String inputLicenseKey = StringUtils.trimWhitespace(licensekey);
+
+        if(owlLicenseKey.compareTo(inputLicenseKey) == 0) {
+            validAdminUser = 1;
+        } else {
+            validAdminUser = -1;
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_LICENSEKEY));
+        }
+
+        return validAdminUser;
+    }
+
+    //  �봽濡쒗븘 寃��궗
+    private void verifyProfile(MultipartFile profile) {
+        if (profile == null) {
+            return;
+        }
+        //  �뾽濡쒕뱶 �봽濡쒗븘 �솗�옣�옄 泥댄겕
+        this.checkAcceptFile(profile);
+        //  �뾽濡쒕뱶 �봽濡쒗븘 �궗吏� �슜�웾 泥댄겕
+        this.checkProfileSize(profile);
+    }
+
+    //  �봽濡쒗븘 �뙆�씪 �슜�웾 泥댄겕
+    private void checkProfileSize(MultipartFile profile) {
+        if (profile.getSize() > 10485760) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PROFILE_SIZE_NOT_ALLOW));
+        }
+    }
+
+    //  �봽濡쒗븘 �뙆�씪 �솗�옣�옄 泥댄겕
+    private void checkAcceptFile(MultipartFile profile) {
+        boolean result = false;
+        String check = profile.getOriginalFilename().substring(profile.getOriginalFilename().lastIndexOf("."));
+
+        if (check.equalsIgnoreCase(".jpg") || check.equalsIgnoreCase(".png")) {
+            result = true;
+        }
+
+        if (!result) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PROFILE_UPLOAD_FILE_TYPE_NOT_ALLOW));
+        }
+    }
+
+    //  �봽濡쒗븘 �뙆�씪�� 10M �씠�븯留� �뿀�슜�븳�떎.
+    private void changeProfile(User user, MultipartFile profile) {
+        //  �봽濡쒗븘�쓣 �꽑�깮�븯吏� �븡�븯�쑝硫� �떆�뒪�뀥 �봽濡쒗븘�씠 湲곕낯 �꽑�깮�맂�떎.
+        String filePath = User.DEFAULT_PROFILE;
+
+        if (profile != null) {
+            //  10MB �젣�븳
+            if (profile.getSize() > 10 * 1024 * 1024) {
+                throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PROFILE_SIZE_NOT_ALLOW));
+            }
+
+            try {
+                Map<String, Object> fileMap = CommonUtil.makeFileMap(profile);
+
+                //  �봽濡쒗븘 �뾽濡쒕뱶
+                String awsKey = this.attachedFileService.uploadFile(fileMap, this.profileFolder, null, 1, 1);
+                user.setAwsKey(awsKey); //  owlKey ���옣
+                filePath = this.awsS3Url + this.bucketName + this.profileFolder + "/" + awsKey;
+            } catch (Exception e) {
+                log.debug(e.getMessage());
+            }
+        }
+
+        //  oAuth2.0�쑝濡� �뿰�룞�뻽�쓣 �븣 媛��졇�삩 �봽濡쒗븘 �젙蹂대�� 湲곕낯 �봽濡쒗븘濡� ���옣�븳�떎.
+        if (user.getProfile() == null) {
+            user.setProfile(filePath);
+        }
+
+        this.userRepository.saveAndFlush(user);
+    }
+
+    //  �궗�슜�옄 �젙蹂대�� �닔�젙�븳�떎.
+    @Override
+    @Transactional
+    public void modifyUser(UserForm userForm, MultipartFile profile) {
+        //  濡쒓렇�씤�븳 �궗�슜�옄�� �닔�젙�븯�젮�뒗 �궗�슜�옄 �젙蹂닿� �씪移섑븯�뒗吏� �솗�씤
+        this.verifyUserId(userForm.getId());
+        //  �씠由� �쑀�슚�꽦 寃��궗
+        this.verifyUserName(userForm.getName());
+        //  �뿰�씫泥� �쑀�슚�꽦 寃��궗
+        this.verifyPhone(userForm.getPhone());
+        //  �봽濡쒗븘 �뙆�씪�씠 �쑀�슚�븳吏� 泥댄겕�븳�떎.
+        this.verifyProfile(profile);
+
+        User user = this.getUser(userForm.getId());
+
+        //  �뤌 �젙蹂대�� user 媛앹껜�뿉 蹂듭궗�븳�떎.
+        ConvertUtil.copyProperties(userForm, user, "id", "profile", "account", "password");
+
+
+        //  �씠硫붿씪 �븣由� �삁�젙 �떆媛꾩씠 怨듬갚�씠硫� �뵒�뤃�듃 �씠硫붿씪 �븣由� �삁�젙 �떆媛꾩쑝濡� �꽕�젙�븳�떎.
+        if (StringUtils.isEmpty(user.getReservationNotifyTime())) {
+            user.setReservationNotifyTime(User.DEFAULT_RESERVATION_NOTIFY_TIME);
+        } else {
+            user.setReservationNotifyTime(userForm.getReservationNotifyTime());
+        }
+
+        //  �봽濡쒗븘�쓣 蹂�寃쏀븯吏� �븡�� 寃쎌슦 - profile �씠 �삱�씪�삤吏� �븡�뒗�떎.
+        //  �봽濡쒗븘�쓣 蹂�寃쏀븳 寃쎌슦 - profile �씠 �삱�씪�삩�떎.
+        if (profile != null) {
+            //  湲곗〈 �봽濡쒗븘 �궘�젣. �떒, 湲곕낯 �젣怨듬릺�뒗 �봽濡쒗븘�씪�븣�뒗 �궘�젣�븯吏� �븡�뒗�떎.
+            if (!user.getProfile().contains(User.DEFAULT_PROFILE)) {
+                //  �뾽濡쒕뱶�븳 �봽濡쒗븘�씪 寃쎌슦
+                if (!StringUtils.isEmpty(user.getAwsKey())) {
+                    this.removeProfile(user.getAwsKey());
+                }
+            }
+            //  �봽濡쒗븘 �벑濡�
+            user.setProfile(null);
+            this.changeProfile(user, profile);
+        }
+
+        this.userRepository.saveAndFlush(user);
+        SecurityUtils.setUserToSession(user);
+    }
+
+    //  �벑濡앺븳 �봽濡쒗븘 �뙆�씪�쓣 �궘�젣�븳�떎.
+    private void removeProfile(String awsKey) {
+        try {
+            this.attachedFileService.removeFile(awsKey, -1L);
+        } catch (Exception e) {
+            log.debug("�봽濡쒗븘 �궘�젣 �뿉�윭 :" + e.getMessage());
+        }
+    }
+
+    //  鍮꾨�踰덊샇瑜� �닔�젙�븳�떎.
+    @Override
+    @Transactional
+    public void modifyPassword(UserForm userForm) {
+        //  濡쒓렇�씤�븳 �궗�슜�옄�� �닔�젙�븯�젮�뒗 �궗�슜�옄 �젙蹂닿� �씪移섑븯�뒗吏� �솗�씤
+        this.verifyUserId(userForm.getId());
+        User user = this.getUser(userForm.getId());
+        //  鍮꾨�踰덊샇 �쑀�슚�꽦 泥댄겕
+        this.verifyPassword(userForm.getPassword());
+
+        //  鍮꾨�踰덊샇 蹂�寃� �쑀�슚�꽦 泥댄겕
+        this.verifyModifyPassword(userForm.getCurrentPassword(), user.getPassword(), userForm.getPassword(), userForm.getPasswordConfirm());
+
+        user.setPassword(this.passwordEncoder.encode(userForm.getPassword()));
+
+        this.userRepository.saveAndFlush(user);
+    }
+
+    //  鍮꾨�踰덊샇 蹂�寃� �쑀�슚�꽦 泥댄겕
+    private void verifyModifyPassword(String currentPassword, String oldPassword, String newPassword, String newPasswordConfirm) {
+        //  �쁽�옱 鍮꾨�踰덊샇 �솗�씤
+        if (!this.passwordEncoder.matches(currentPassword, oldPassword)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_CURRENT_PASSWORD));
+        }
+
+        //  蹂�寃쏀븯�젮�뒗 鍮꾨�踰덊샇媛� �쁽�옱 鍮꾨�踰덊샇�� 媛숈쑝硫� �븞�맖
+        if (newPassword.equals(currentPassword)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PASSWORD_SAME_NEW_PASSWORD));
+        }
+
+        if (!newPassword.equals(newPasswordConfirm)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_PASSWORD_NOT_SAME_CONFIRM_PASSWORD));
+        }
+    }
+
+    //  �궗�슜�옄 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailUser(Map<String, Object> resJsonData, UserCondition userCondition) {
+        UserVo userVo = new UserVo();
+
+        if (userCondition.getId() != null) {
+            //  濡쒓렇�씤�븳 �궗�슜�옄�� �닔�젙�븯�젮�뒗 �궗�슜�옄 �젙蹂닿� �씪移섑븯�뒗吏� �솗�씤
+            this.verifyUserId(userCondition.getId());
+            User user = this.getUser(userCondition.getId());
+            userVo = ConvertUtil.copyProperties(user, UserVo.class, "password");
+            userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount()));
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, userVo);
+    }
+
+    //  濡쒓렇�씤�븳 �궗�슜�옄�� �닔�젙�븯�젮�뒗 �궗�슜�옄 �젙蹂닿� �씪移섑븯�뒗吏� �솗�씤
+    private void verifyUserId(Long id) {
+        if (!this.webAppUtil.getLoginId().equals(id)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NOT_MODIFY_SELF));
+        }
+    }
+
+    //  怨꾩젙紐낆쑝濡� �궗�슜�옄瑜� 李얜뒗�떎 - security, �냼�뀥 �뿰�룞 �뿬遺� �솗�씤�뿉�꽌 �궗�슜
+    @Override
+    @Transactional(readOnly = true)
+    public User findByAccount(String account) {
+        return this.userRepository.findByAccount(account);
+    }
+
+    //  �뾽臾� 怨듦컙�쓽 愿�由ъ옄瑜� 李얜뒗�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public User findByWorkspaceIdAndManagerYn(Workspace workspace) {
+        return this.userRepository.findByWorkspaceIdAndManagerYn(workspace.getId(), true);
+    }
+
+    //  OAuth �씤利�
+    @Override
+    @Transactional
+    public ModelAndView getOAuthToken(String code, String state, SocialType socialType, HttpServletRequest request) {
+        //  �쓳�떟諛쏆� state 媛믪씠 �궡媛� �꽕�젙�븳 媛믨낵 媛숈�吏� �솗�씤
+        this.checkOAuthState(state);
+        //  �냼�뀥 �쑀�삎�뿉 �뵲�씪 token url �젙蹂대�� 媛��졇�삩�떎.
+        String targetUrl = this.getOAuthTokenUrl(socialType);
+        HttpURLConnection conn = null;
+        BufferedWriter writer = null;
+        String response = "";
+
+        try {
+            URL url = new URL(targetUrl);
+            conn = (HttpURLConnection) url.openConnection();
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            conn.setRequestMethod("POST");
+            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+
+            //  �냼�뀥 �쑀�삎�뿉 �뵲�씪 token parameter 瑜� �꽕�젙�븳�떎.
+            Map<String, String> postDataParams = new HashMap<>();
+            this.setOAuthTokenClientAuthorizationCode(postDataParams, socialType, code);
+
+            writer = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(), "UTF-8"));
+            writer.write(CommonUtil.getPostDataString(postDataParams));
+            writer.flush();
+
+            response = CommonUtil.getStringToInputStream(conn.getResponseCode(), conn.getInputStream());
+
+        } catch (IOException e) {
+            log.error("[UserServiceImpl.getOAuthToken()] => " + e.getMessage());
+        } finally {
+            this.deAllocationMemory(writer, conn);
+        }
+
+        if (!StringUtils.isEmpty(response)) {
+            Gson gson = new Gson();
+            Map<String, Object> token = (Map<String, Object>) gson.fromJson(response, Object.class);
+            String accessToken = MapUtil.getString(token, "access_token");
+            return this.getProfileAndUserSocialConfirm(socialType, accessToken, request);
+        }
+
+        return new ModelAndView("/views/login/socialFail.html");
+    }
+
+    //  �쓳�떟諛쏆� state 媛믪씠 �궡媛� �꽕�젙�븳 媛믨낵 媛숈�吏� �솗�씤
+    private void checkOAuthState(String responseState) {
+        if (!this.oAuthState.equals(responseState)) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.OAUTH_STATE_VALUE_NOT_EQUAL));
+        }
+    }
+
+    //  �냼�뀥 �쑀�삎�뿉 �뵲�씪 token url �젙蹂대�� 媛��졇�삩�떎.
+    private String getOAuthTokenUrl(SocialType socialType) {
+        String targetUrl = "";
+
+        switch (socialType) {
+            case GOOGLE:
+                targetUrl = "https://www.googleapis.com/oauth2/v4/token";
+                break;
+            case NAVER:
+                targetUrl = "https://nid.naver.com/oauth2.0/token";
+                break;
+            case KAKAO:
+                targetUrl = "https://kauth.kakao.com/oauth/token";
+                break;
+            case FACEBOOK:
+                targetUrl = "https://graph.facebook.com/v3.1/oauth/access_token";
+                break;
+        }
+
+        return targetUrl;
+    }
+
+    //  �냼�뀥 �쑀�삎�뿉 �뵲�씪 token parameter 瑜� �꽕�젙�븳�떎.
+    private void setOAuthTokenClientAuthorizationCode(Map<String, String> postDataParams, SocialType socialType, String code) {
+
+        switch (socialType) {
+            case GOOGLE:
+                postDataParams.put("client_id", this.googleClientId);
+                postDataParams.put("client_secret", this.googleClientSecret);
+                postDataParams.put("redirect_uri", this.googleClientRedirectUri);
+                break;
+            case NAVER:
+                postDataParams.put("client_id", this.naverClientId);
+                postDataParams.put("client_secret", this.naverClientSecret);
+                break;
+            case KAKAO:
+                postDataParams.put("client_id", this.kakaoClientId);
+                postDataParams.put("client_secret", this.kakaoClientSecret);
+                break;
+            case FACEBOOK:
+                postDataParams.put("client_id", this.facebookClientId);
+                postDataParams.put("client_secret", this.facebookClientSecret);
+                postDataParams.put("redirect_uri", this.facebookClientRedirectUri);
+                break;
+        }
+
+        postDataParams.put("code", code);
+        postDataParams.put("grant_type", "authorization_code");
+        postDataParams.put("state", this.oAuthState);
+    }
+
+    //  硫붾え由� �빐�젣
+    private void deAllocationMemory(BufferedWriter writer, HttpURLConnection conn) {
+        try {
+            if (writer != null) {
+                writer.close();
+            }
+        } catch (IOException e) {
+            log.error("[UserServiceImpl.getOAuthToken().writer.close()] => " + e.getMessage());
+        }
+
+        try {
+            if (conn != null) {
+                conn.disconnect();
+            }
+        } catch (Exception e) {
+            log.error("[UserServiceImpl.getOAuthToken().conn.disconnect()] => " + e.getMessage());
+        }
+    }
+
+    //  �봽濡쒗븘 �젙蹂대�� �뼸怨� �빐�떦 �궗�슜�옄媛� �씠誘� �냼�뀥 �뿰�룞�씠 �릺�뿀�뒗吏� �솗�씤 �썑 �뿰�룞�씠 �븞�릺�뼱 �엳�쑝硫� �궗�슜�옄瑜� 異붽��븳�떎.
+    private ModelAndView getProfileAndUserSocialConfirm(SocialType socialType, String accessToken, HttpServletRequest request) {
+        HttpURLConnection conn = null;
+        String apiUrl = this.getOAuthUserProfileUrl(socialType);
+        String response = "";
+        String modelViewName;
+        //  �럹�씠�뒪遺곷쭔 �씠�젃寃�...
+        if (socialType.equals(SocialType.FACEBOOK)) {
+            apiUrl += "?fields=id%2Cname%2Cemail%2Cpicture&access_token=" + accessToken;
+        }
+
+        try {
+            URL url = new URL(apiUrl);
+            conn = (HttpURLConnection) url.openConnection();
+            conn.setRequestProperty("Accept", "application/json");
+            conn.setRequestProperty("Authorization", "Bearer " + accessToken);
+            conn.setRequestMethod("GET");
+            conn.connect();
+
+            response = CommonUtil.getStringToInputStream(conn.getResponseCode(), conn.getInputStream());
+        } catch (IOException e) {
+            log.error("[UserServiceImpl.getProfileAndUserSocialConfirm()] => " + e.getMessage());
+        } finally {
+            if (conn != null) {
+                conn.disconnect();
+            }
+        }
+
+        if (StringUtils.isEmpty(response)) {
+            modelViewName = "/views/login/socialFail.html";
+        } else {
+            Map<String, Object> profile = new HashMap<>();
+            Gson gson = new Gson();
+            Object socialProfile = gson.fromJson(response, Object.class);
+            //  �빐�떦 �냼�뀥�뿉�꽌 諛쏆� �궗�슜�옄 �젙蹂대�� 遺꾩꽍�븳�떎.
+            this.parserUserProfile(socialType, profile, (Map<String, Object>) socialProfile, accessToken);
+
+            //  �궗�슜�옄媛� �씠誘� �냼�뀥�뿰�룞�씠 �릺�뼱�엳�뒗吏� �솗�씤�븯怨� �젙蹂닿� �뾾�쓣 寃쎌슦 �궗�슜�옄瑜� 異붽��븳�떎.
+            modelViewName = this.checkUserSocialConnect(profile, request, socialType);
+        }
+
+        return new ModelAndView(modelViewName);
+    }
+
+    //  移댁뭅�삤�넚 濡쒓렇�씤�뿉�꽌 �떎�뙣�븷 寃쎌슦 �빋 �뿰�룞 �젙蹂대�� �궘�젣 �슂泥��븳�떎.
+    private void unlinkKaKao(String accessToken) {
+        try {
+            URL url = new URL("https://kapi.kakao.com/v1/user/unlink");
+            HttpURLConnection unlinkConnect = (HttpURLConnection) url.openConnection();
+            unlinkConnect.setRequestProperty("Accept", "application/json");
+            unlinkConnect.setRequestProperty("Authorization", "Bearer " + accessToken);
+            unlinkConnect.setRequestMethod("POST");
+            unlinkConnect.connect();
+            CommonUtil.getStringToInputStream(unlinkConnect.getResponseCode(), unlinkConnect.getInputStream());
+
+        } catch (IOException e) {
+            log.error("[UserServiceImpl.unlinkSocial()] => " + e.getMessage());
+        }
+    }
+
+    //  �냼�뀥�뿉�꽌 媛쒖씤 �젙蹂대�� 媛��졇�삤湲� �쐞�븳 url �젙蹂�
+    private String getOAuthUserProfileUrl(SocialType socialType) {
+        String apiUrl = "";
+        switch (socialType) {
+            case GOOGLE:
+                apiUrl = "https://www.googleapis.com/plus/v1/people/me";
+                break;
+            case NAVER:
+                apiUrl = "https://openapi.naver.com/v1/nid/me";
+                break;
+            case KAKAO:
+                apiUrl = "https://kapi.kakao.com/v2/user/me";
+                break;
+            case FACEBOOK:
+                apiUrl = "https://graph.facebook.com/v3.1/me";
+                break;
+        }
+
+        return apiUrl;
+    }
+
+    //  �빐�떦 �냼�뀥�뿉�꽌 諛쏆� �궗�슜�옄 �젙蹂대�� 遺꾩꽍�븳�떎.
+    private void parserUserProfile(SocialType socialType, Map<String, Object> profile, Map<String, Object> socialProfile, String accessToken) {
+        String email = "";
+        String name = "";
+        String profilePath = "";
+        Map<String, Object> response;
+
+        switch (socialType) {
+            case GOOGLE:
+                List<Map<String, Object>> emails = (List<Map<String, Object>>) socialProfile.get("emails");
+                Map<String, Object> emailInfo = emails.get(0);
+                email = MapUtil.getString(emailInfo, "value");
+                name = MapUtil.getString(socialProfile, "displayName");
+                profile.put("language", MapUtil.getString(socialProfile, "language"));
+                break;
+            case NAVER:
+                response = (Map<String, Object>) socialProfile.get("response");
+                email = MapUtil.getString(response, "email");
+                name = MapUtil.getString(response, "name");
+                break;
+
+            case KAKAO:
+                response = (Map<String, Object>) socialProfile.get("properties");
+                name = MapUtil.getString(response, "nickname");
+                Map<String, Object> emailMap = (Map<String, Object>) socialProfile.get("kakao_account");
+
+                if (MapUtil.getBoolean(emailMap, "has_email")) {
+                    email = MapUtil.getString(emailMap, "email");
+                }
+
+                if (StringUtils.isEmpty(email)) {
+                    //  移댁뭅�삤�넚 濡쒓렇�씤�뿉�꽌 �떎�뙣�븷 寃쎌슦 �빋 �뿰�룞 �젙蹂대�� �궘�젣 �슂泥��븳�떎.
+                    this.unlinkKaKao(accessToken);
+                }
+
+                break;
+
+            case FACEBOOK:
+                email = MapUtil.getString(socialProfile, "email");
+                name = MapUtil.getString(socialProfile, "name");
+                break;
+        }
+
+        profile.put("email", email);
+        profile.put("name", name);
+        profile.put("profile", profilePath);
+    }
+
+    //  �궗�슜�옄媛� �씠誘� �냼�뀥�뿰�룞�씠 �릺�뼱�엳�뒗吏� �솗�씤�븯怨� �젙蹂닿� �뾾�쓣 寃쎌슦 �궗�슜�옄瑜� 異붽��븳�떎.
+    private String checkUserSocialConnect(Map<String, Object> profile, HttpServletRequest request, SocialType socialType) {
+        String email = MapUtil.getString(profile, "email");
+        User user = this.findByAccount(CommonUtil.encryptAES128(email));
+        String modelViewName;
+
+        //  移댁뭅�삤, �럹�씠�뒪遺�, �꽕�씠踰�, 援ш��뿉�꽌 怨듯넻�쟻�쑝濡� �씠硫붿씪�쓣 諛쏆� 紐삵븷 寃쎌슦 �뿬湲곗꽌 �뿉�윭 泥섎━�빐以��떎.
+        if (StringUtils.isEmpty(email)) {
+            modelViewName = "/views/login/socialFailNotEmail.html";
+            return modelViewName;
+        }
+
+        try {
+            //  �쉶�썝 �깉�눜 湲곕줉 議고쉶
+            this.checkWithDrawAccount(CommonUtil.encryptAES128(email));
+        } catch (Exception e) {
+            modelViewName = "/views/login/withDrawSocialAccount.html";
+            return modelViewName;
+        }
+
+        if (user == null) {
+            String name = MapUtil.getString(profile, "name");
+            String language = MapUtil.getString(profile, "language");
+            String profilePath = MapUtil.getString(profile, "profile");
+
+            UserForm userForm = new UserForm();
+            userForm.setName(name);
+            userForm.setAccount(email);
+
+            if (StringUtils.isEmpty(language)) {
+                userForm.setLanguage(User.DEFAULT_LANGUAGE);
+            } else {
+                switch (language) {
+                    case "ko":
+                        userForm.setLanguage(language);
+                        break;
+                    case "en":
+                        userForm.setLanguage(language);
+                        break;
+                    case "ja":
+                        userForm.setLanguage(language);
+                        break;
+                    case "vi":
+                        userForm.setLanguage(language);
+                        break;
+                    default:
+                        userForm.setLanguage(User.DEFAULT_LANGUAGE);
+                }
+            }
+
+            userForm.setStatus(User.USER_STATUS_ACTIVE);
+            String tempPassword = CommonUtil.randomStringMaker();
+            userForm.setPassword(CommonUtil.encryptionSha512(tempPassword));   //  鍮꾨�踰덊샇瑜� �옖�뜡�쑝濡� 留뚮뱾�뼱以��떎.
+            userForm.setWorkspaceName(name + this.messageAccessor.message("common.sWorkspace")); // �쓽 �뾽臾닿났媛�
+            //  �냼�뀥 �젙蹂� �뀑�똿
+            if (socialType != null) {
+                userForm.setSocialType(socialType.toString());
+            }
+
+            if (!StringUtils.isEmpty(profilePath)) {
+                userForm.setProfile(profilePath);
+            }
+            //  �궗�슜�옄 �깮�꽦
+            this.addUser(userForm, null);
+            //  �옄�룞 濡쒓렇�씤
+            this.autoLogin(CommonUtil.encryptAES128(userForm.getAccount()), request);
+            modelViewName = "redirect:/#/dashboards/dashboard";
+        } else {
+            //  �씠誘� 議댁옱�븯硫댁꽌 �뿰�룞 �젙蹂닿� �엳�쓣 寃쎌슦 �옄�룞 濡쒓렇�씤
+            if (user.getSocialType() != null) {
+                if (user.getSocialType().equals(socialType)) {
+                    this.autoLogin(user.getAccount(), request);
+                    modelViewName = "redirect:/#/dashboards/dashboard";
+                } else {
+                    //  �씠誘� 議댁옱�븯硫댁꽌 �뿰�룞 �젙蹂닿� �뾾�쓣 寃쎌슦
+                    modelViewName = "/views/login/socialConnect.html";
+                }
+            } else {
+                //  �씠誘� 議댁옱�븯硫댁꽌 �뿰�룞 �젙蹂닿� �뾾�쓣 寃쎌슦
+                modelViewName = "/views/login/socialConnect.html";
+            }
+        }
+
+        return modelViewName;
+    }
+
+    //  �옄�룞 濡쒓렇�씤
+    @Override
+    @Transactional(readOnly = true)
+    public void autoLogin(String email, HttpServletRequest request) {
+        UserDetails userDetails = this.userSecurityService.loadUserByUsername(email);
+        List<SessionInformation> sessionInformationList = this.sessionRegistry.getAllSessions(userDetails, false);
+
+        for (SessionInformation sessionInformation : sessionInformationList) {
+            String targetAccount = (String) sessionInformation.getPrincipal();
+
+            if (targetAccount.equals(userDetails.getUsername())) {
+                sessionInformation.expireNow();
+                break;
+            }
+        }
+
+        try {
+            log.warn("�옄�룞 濡쒓렇�씤 �닔�뻾 �쟾");
+            SecurityUtils.autoLogin(userDetails);
+            log.warn("�옄�룞 濡쒓렇�씤 �닔�뻾 �썑");
+        } catch (Exception ex) {
+            log.error(ex.getMessage());
+        }
+    }
+
+    //  �궗�슜�옄 紐⑸줉�쓣 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<UserVo> findUser(Map<String, Object> resJsonData,
+                                 UserCondition condition, Pageable pageable) {
+
+        Long loginId = this.webAppUtil.getLoginId();
+
+        condition.setWorkspaceId(this.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        condition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        condition.setPageSize(pageable.getPageSize());
+        condition.setLoginUserId(loginId);
+
+        List<Map<String, Object>> results = this.userMapper.find(condition);
+        Long totalUsersCount = this.userMapper.count(condition);
+
+        return this.convertUserVoToMap(results, totalUsersCount, pageable, resJsonData);
+    }
+
+    //  �궗�슜�옄 �븘�씠�뵒濡� �궗�슜�옄瑜� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public User getUser(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NOT_EXIST));
+        }
+
+        User user = this.findOne(id);
+
+        if (user == null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NOT_EXIST));
+        }
+
+        return user;
+    }
+
+    //  �궗�슜�옄 �씠硫붿씪濡� �쁽�옱 鍮꾨�踰덊샇瑜� �븣�젮以��떎.
+    @Override
+    @Transactional
+    public void returnEmailPassword(UserForm userForm) {
+        User user = this.userRepository.findByAccount(userForm.getAccount());
+
+        if (user == null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_NOT_EXIST));
+        }
+
+        //  �냼�뀥 怨꾩젙�쑝濡� �쉶�썝 媛��엯�븳 �궗�슜�옄�뒗 鍮꾨�踰덊샇 李얘린 湲곕뒫�쓣 �젣怨듯븯吏� �븡�뒗�떎.
+        this.checkOAuthJoinUser(user);
+
+        String tempPassword = CommonUtil.randomStringMaker();
+        user.setPassword(this.passwordEncoder.encode(CommonUtil.encryptionSha512(tempPassword)));   //  鍮꾨�踰덊샇瑜� �옖�뜡�쑝濡� 留뚮뱾�뼱以��떎.
+        this.userRepository.saveAndFlush(user);
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("name", user.getName());
+        params.put("account", CommonUtil.decryptAES128(user.getAccount()));
+        params.put("password", tempPassword);
+        //  鍮꾨�踰덊샇 李얘린 �씠硫붿씪 �쟾�넚
+        this.systemEmailService.directEmail(new String[]{user.getAccount()}, EmailType.USER_SEARCH_PASSWORD, params, null);
+    }
+
+    //  �냼�뀥 怨꾩젙�쑝濡� �쉶�썝 媛��엯�븳 �궗�슜�옄�뒗 鍮꾨�踰덊샇 李얘린 湲곕뒫�쓣 �젣怨듯븯吏� �븡�뒗�떎.
+    private void checkOAuthJoinUser(User user) {
+        if (user.getSocialType() != null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_RETURN_PASSWORD_NOT_PROVIDER_SOCIAL_JOIN_USER));
+        }
+    }
+
+    //  留덉�留됱쑝濡� �꽑�깮�븳 �뾽臾� 怨듦컙 �젙蹂대�� ���옣�븳�떎.
+    @Override
+    @Transactional
+    public void updateLastWorkspace(Map<String, Object> resJsonData, UserForm userForm) {
+        User user = this.getUser(this.webAppUtil.getLoginId());
+
+        if (userForm.getLastWorkspaceId() != null) {
+            user.setLastWorkspaceId(userForm.getLastWorkspaceId());
+        }
+
+        this.userRepository.saveAndFlush(user);
+        //  �꽭�뀡 �뾽�뜲�씠�듃
+        SecurityUtils.setUserToSession(user);
+        //  �겢�씪�씠�뼵�듃�쓽 �궗�슜�옄 �젙蹂� �뾽�뜲�씠�듃
+        UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class, "password");
+
+        if (user.getSocialType() != null) {
+            userVo.setSocialType(user.getSocialType().toString());
+        }
+
+        userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount()));
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, userVo);
+    }
+
+    //  留덉�留됱쑝濡� �꽑�깮�븳 濡쒓렇�씤 �젙蹂대�� ���옣�븳�떎.
+    @Override
+    @Transactional
+    public void updateLastLogin() {
+        User user = this.getUser(this.webAppUtil.getLoginId());
+        user.setLastLoginDate(new Date());
+
+        this.userRepository.saveAndFlush(user);
+    }
+
+        //  留덉�留됱쑝濡� �꽑�깮�븳 �봽濡쒖젥�듃 �젙蹂대�� ���옣�븳�떎.
+    @Override
+    @Transactional
+    public void updateLastProject(Map<String, Object> resJsonData, UserForm userForm) {
+        User user = this.getUser(this.webAppUtil.getLoginId());
+
+        if (userForm.getLastProjectId() != null) {
+            user.setLastProjectId(userForm.getLastProjectId());
+        }
+
+        this.userRepository.saveAndFlush(user);
+        //  �꽭�뀡 �뾽�뜲�씠�듃
+        SecurityUtils.setUserToSession(user);
+        //  �겢�씪�씠�뼵�듃�쓽 �궗�슜�옄 �젙蹂� �뾽�뜲�씠�듃
+        UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class, "password");
+
+        if (user.getSocialType() != null) {
+            userVo.setSocialType(user.getSocialType().toString());
+        }
+
+        userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount()));
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, userVo);
+    }
+
+    //  �듅�젙 �븘�씠�뵒�뿉 �빐�떦�븯�뒗 �궗�슜�옄 紐⑸줉�쓣 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<User> findByIdIn(List<Long> userIds) {
+        return this.userRepository.findByIdIn(userIds);
+    }
+
+    //  愿�由ъ옄 紐⑸줉�쓣 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<User> findAdmin() {
+        return this.userRepository.findAdmin();
+    }
+
+    //  �빐�떦 �궗�슜�옄媛� 愿�由ы븯�뒗 �뾽臾� 怨듦컙�쓣 留덉�留됱쑝濡� �젒洹쇳븳 �뾽臾� 怨듦컙 �젙蹂대줈 �뾽�뜲�씠�듃�븳�떎.
+    @Override
+    @Transactional
+    public void updateLastDefaultWorkspace(Workspace workspace, User user) {
+        if (user.getLastWorkspaceId().equals(workspace.getId())) {
+            //  �빐�떦 �궗�슜�옄媛� 愿�由ы븯�뒗 �뾽臾� 怨듦컙�쓣 留덉�留� �젒洹� �뾽臾� 怨듦컙濡� 蹂�寃쏀븳�떎.
+            this.updateLastMyWorkspace(user);
+        } else {
+            //  �븣由쇱� �몴�떆�븯吏� �븡怨� �솕硫댁긽�뿉�꽌 �빐�떦 �뾽臾� 怨듦컙媛� �븞蹂댁씠寃� �븳�떎.
+            this.simpMessagingTemplate.convertAndSendToUser(user.getAccount(), "/notification/workspace-update", this.messageAccessor.getMessage(MsgConstants.WORKSPACE_OUT, workspace.getName()));
+        }
+    }
+
+    //  �빐�떦 �궗�슜�옄媛� 愿�由ы븯�뒗 �뾽臾� 怨듦컙瑜� 留덉�留� �젒洹� �뾽臾� 怨듦컙濡� 蹂�寃쏀븳�떎.
+    @Override
+    @Transactional
+    public void updateLastMyWorkspace(User user) {
+        UserWorkspace userManagerWorkspaceConnect = this.userWorkspaceService.findMyWorkspace(user.getId());
+
+        if(userManagerWorkspaceConnect != null) {
+            Workspace userManagerWorkspace = userManagerWorkspaceConnect.getWorkspace();
+            user.setLastWorkspaceId(userManagerWorkspace.getId());
+            this.userRepository.saveAndFlush(user);
+        }
+    }
+
+    //  �봽濡쒖젥�듃�뿉 李몄뿬�븯�뒗 �씪諛� �궗�슜�옄 紐⑸줉�쓣 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findProjectMember(Map<String, Object> resJsonData, UserCondition userCondition) {
+        ProjectRole projectRole = this.projectRoleService.findByProjectIdAndRoleType(userCondition.getProjectId(), ProjectRole.TYPE_DEFAULT);
+        List<ProjectRoleUser> projectRoleUsers = this.projectRoleUserService.findByProjectRoleId(projectRole.getId());
+        List<UserVo> userVos = Lists.newArrayList();
+
+        for (ProjectRoleUser projectRoleUser : projectRoleUsers) {
+            UserVo userVo = ConvertUtil.copyProperties(projectRoleUser.getUser(), UserVo.class, "password");
+            userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount()));
+            userVos.add(userVo);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, userVos);
+    }
+
+    //  �쉶�썝 �깉�눜
+    @Override
+    @Transactional
+    public void withDrawUser() {
+        User user = this.getUser(this.webAppUtil.getLoginId());
+        //  �씠硫붿씪 諛쒖넚�뿉 �궗�슜�븷 �젙蹂� 誘몃━ 異붿텧
+        Map<String, Object> userMap = new HashMap<>();
+        userMap.put("name", user.getName());
+        userMap.put("account", user.getAccount());
+
+        UserWorkspace userWorkspace = this.userWorkspaceService.findMyWorkspace(user.getId());
+
+        if (userWorkspace != null) {
+            Workspace myWorkspace = userWorkspace.getWorkspace();
+            //  �궗�슜�옄媛� 愿�由ы븯�뒗 �뾽臾� 怨듦컙�뿉 �엳�뒗 �젙蹂대�� �궘�젣�븳�떎.
+            this.workspaceService.removeWorkspace(myWorkspace, user);
+        }
+
+        //  李몄뿬�븯怨� �엳�뒗 �떎瑜� �뾽臾� 怨듦컙�뿉�꽌 �떞�떦�옄 �젙蹂대�� �궘�젣�븳�떎.
+        this.removeOtherWorkspaceAssignee(user);
+        //  �쉶�썝 �깉�눜 �젙蹂대�� ���옣�븳�떎.
+        this.userWithDrawService.addUserWithDraw(user);
+        //  �궗�슜�옄 媛쒖씤 �젙蹂대�� 留덉뒪�궧/�븫�샇�솕 泥섎━�븳�떎.
+        this.initPrivateUser(user);
+
+
+        UserCondition userCondition = new UserCondition();
+        userCondition.setId(user.getId());
+        userCondition.setAccount(user.getAccount());
+
+        //  �쉶�썝 �깉�눜瑜� 吏꾪뻾�븯硫� �뿰愿� �뀒�씠釉� �젙蹂대�� �궘�젣�븳�떎.
+        this.userMapper.deleteCascadeUser(userCondition);
+
+        //  �쉶�썝 �깉�눜 �븣由� 硫붿씪 �쟾�넚
+        this.systemEmailService.directEmail(new String[]{MapUtil.getString(userMap, "account")}, EmailType.USER_WITH_DRAW, userMap, null);
+    }
+
+    //  �궗�슜�옄 媛쒖씤 �젙蹂대�� 留덉뒪�궧 泥섎━�븳�떎.
+    private void initPrivateUser(User user) {
+        //  �봽濡쒗븘 珥덇린�솕
+        user.setProfile(User.DEFAULT_PROFILE);
+        //  �봽濡쒗븘�씠 �삱�씪媛� �엳�쓣 寃쎌슦 �궘�젣�빐以��떎.
+        if (!StringUtils.isEmpty(user.getAwsKey())) {
+            this.removeProfile(user.getAwsKey());
+        }
+
+        user.setName(CommonUtil.maskingName(user.getName()));
+        user.setStatus(User.USER_STATUS_DEL);
+        this.userRepository.saveAndFlush(user);
+    }
+
+    //  李몄뿬�븯怨� �엳�뒗 �뾽臾� 怨듦컙�뿉�꽌 �봽濡쒖젥�듃/�씠�뒋 �떞�떦�옄 �젙蹂대�� �궘�젣�븳�떎.
+    private void removeOtherWorkspaceAssignee(User user) {
+        for (UserWorkspace userWorkspace : user.getUserWorkspaces()) {
+            //  李몄뿬�븯怨� �엳�뒗 紐⑤뱺 �뾽臾� 怨듦컙 - �옄�떊�씠 愿�由ы븯�뒗 �뾽臾� 怨듦컙�� �젣�쇅
+            if (!userWorkspace.getManagerYn()) {
+                Workspace workspace = userWorkspace.getWorkspace();
+                //  �빐�떦 �뾽臾� 怨듦컙�뿉 議댁옱�븯�뒗 紐⑤뱺 �봽濡쒖젥�듃�뿉�꽌 �젣�쇅
+                this.projectRoleUserService.withDrawWorkspaceManagerModifyProjectRole(workspace, user);
+            }
+        }
+    }
+
+    //  �뾽臾� 怨듦컙�뿉�꽌 �젣�쇅�릺嫄곕굹 �뾽臾� 怨듦컙 �궘�젣�떆 李몄뿬 �궗�슜�옄�뱾�쓽 留덉�留� �젒洹� �뾽臾� 怨듦컙 �젙蹂대줈 �꽭�뀡 �뾽�뜲�씠�듃�븯湲� �쐞�빐 �궗�슜
+    @Override
+    @Transactional(readOnly = true)
+    public void updateUserSession() {
+        User user = this.getUser(this.webAppUtil.getLoginId());
+        //  �꽭�뀡 �뾽�뜲�씠�듃
+        SecurityUtils.setUserToSession(user);
+    }
+
+    //  �궗�슜�옄�쓽 �쁽�옱 �꽭�뀡 �젙蹂대�� 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public User getUserSession(Map<String, Object> resJsonData, HttpServletRequest httpServletRequest) {
+        User user = this.getUser(this.webAppUtil.getLoginId());
+        UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class, "password");
+        userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount()));
+        resJsonData.put(Constants.RES_KEY_CONTENTS, userVo);
+        //  �궗�슜�옄 �꽭�뀡 �젙蹂대�� 遺꾩꽍�빐�꽌 濡쒓렇�뿉 �궓湲대떎.
+        log.info(ElasticSearchUtil.makeUserSessionHistoryMessage(httpServletRequest, userVo));
+
+        return user;
+    }
+
+    //  �빐�떦 �떆媛꾩뿉 �씠硫붿씪 �븣由� �삁�젙 �떆媛꾩쑝濡� �꽕�젙�븳 �궗�슜�옄瑜� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<String> findByReservationNotifyTime() {
+        Map<String, Object> conditions = new HashMap<>();
+
+        Calendar startCal = Calendar.getInstance();
+        startCal.setTime(new Date());
+        startCal.add(Calendar.MINUTE, -3);   //  3遺꾩쟾�쑝濡�..
+
+        Calendar endCal = Calendar.getInstance();
+        endCal.setTime(new Date());
+        endCal.add(Calendar.MINUTE, 27);   // 27遺� �뮘濡�..
+
+        conditions.put("startTime", DateUtil.convertDateToStr(startCal.getTime(), "HH:mm"));
+        conditions.put("endTime", DateUtil.convertDateToStr(endCal.getTime(), "HH:mm"));
+
+        List<Map<String, Object>> users = this.userMapper.findByReservationNotifyTime(conditions);
+        List<String> results = Lists.newArrayList();
+
+        for (Map<String, Object> user : users) {
+            results.add(MapUtil.getString(user, "account"));
+        }
+
+        return results;
+    }
+
+    //  �봽濡쒖젥�듃�뿉 李몄뿬�븯�뒗 �궗�슜�옄 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<Map<String, Object>> findProjectMember(Project project) {
+        UserCondition userCondition = new UserCondition();
+        userCondition.setProjectId(project.getId());
+        return this.userMapper.findProjectMember(userCondition);
+    }
+
+    //  �궗�슜�옄媛� �꽑�깮�븳 �뼵�뼱瑜� ���옣�븳�떎.
+    @Override
+    @Transactional
+    public void updateLanguage(String language) {
+        //  濡쒓렇�씤 �쟾�씪 �븣�뒗 �뾽�뜲�씠�듃 �븯吏� �븡�뒗�떎. - �쉶�썝 媛��엯
+        if (this.webAppUtil.getLoginId() == null) {
+            return;
+        }
+
+        User user = this.getUser(this.webAppUtil.getLoginId());
+
+        switch (language) {
+            case "ko_KR":
+                user.setLanguage("ko");
+                break;
+            case "en_US":
+                user.setLanguage("en");
+                break;
+            case "ja_JP":
+                user.setLanguage("ja");
+                break;
+            case "vi_VN":
+                user.setLanguage("vi");
+                break;
+            default:
+                user.setLanguage("ko");
+        }
+
+        this.userRepository.saveAndFlush(user);
+        this.updateUserSession();
+    }
+
+    //  �궗�슜�옄 �쉶�썝 媛��엯 �젙蹂대�� OWL ITS 愿��젴�옄�뱾�뿉寃� 硫붿씪濡� �넻吏��빐以��떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void sendUserJoinStatisticsEmail() {
+        String today = DateUtil.convertDateToYYYYMMDD(new Date());
+        Date yesterdayFrom = DateUtil.convertStrToDate(DateUtil.convertDateToYYYYMMDD(DateUtil.addDays(new Date(), -1)) + " 17:00:00");
+        Date todayTo = DateUtil.convertStrToDate(today + " 23:59:59");
+
+        List<User> joinUsers = this.userRepository.findJoinDay(yesterdayFrom, todayTo);
+        List<UserVo> userVos = Lists.newArrayList();
+
+        for (User user : joinUsers) {
+            UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class, "password");
+            userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount()));
+            userVos.add(userVo);
+        }
+
+        List<User> activeUsers = this.userRepository.findByStatus(User.USER_STATUS_ACTIVE);
+
+        Map<String, Object> userMap = new HashMap<>();
+        userMap.put("today", DateUtil.convertDateToYYYYMMDD(new Date()));
+        userMap.put("totalUserCount", activeUsers.size());
+        userMap.put("newUserCount", joinUsers.size());
+        userMap.put("joinUsers", userVos);
+
+        String[] sendEmails = this.userJoinStatisticsEmail.replaceAll("\\p{Z}", "").split(",");
+        List<String> encryptSendEmail = Lists.newArrayList();
+
+        for (String sendEmail : sendEmails) {
+            encryptSendEmail.add(CommonUtil.encryptAES128(sendEmail));
+        }
+
+        //  寃곗젣 �꽦怨� 硫붿씪 諛쒖넚
+        this.systemEmailService.directEmail(encryptSendEmail.toArray(new String[encryptSendEmail.size()]), EmailType.USER_JOIN_STATISTICS, userMap, null);
+    }
+
+    //  �쟾泥� �궗�슜�옄, �봽濡쒖젥�듃 �닔, �씠�뒋 �닔瑜� �씠硫붿씪濡� 蹂대궦�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void sendTotalStatisticsEmail() {
+        String today = DateUtil.convertDateToYYYYMMDD(new Date());
+        long issueCount = this.issueService.count();
+        long projectCount = this.projectService.count();
+        List<User> activeUsers = this.userRepository.findByStatus(User.USER_STATUS_ACTIVE);
+
+        Map<String, Object> userMap = new HashMap<>();
+        userMap.put("today", DateUtil.convertDateToYYYYMMDD(new Date()));
+        userMap.put("issueCount", CommonUtil.getDecimalFormat(issueCount));
+        userMap.put("projectCount", CommonUtil.getDecimalFormat(projectCount));
+        userMap.put("userCount", CommonUtil.getDecimalFormat(activeUsers.size()));
+
+        String[] sendEmails = this.totalStatisticsEmail.replaceAll("\\p{Z}", "").split(",");
+        List<String> encryptSendEmail = Lists.newArrayList();
+
+        for (String sendEmail : sendEmails) {
+            encryptSendEmail.add(CommonUtil.encryptAES128(sendEmail));
+        }
+
+        //  寃곗젣 �꽦怨� 硫붿씪 諛쒖넚
+        this.systemEmailService.directEmail(encryptSendEmail.toArray(new String[encryptSendEmail.size()]), EmailType.TOTAL_STATISTICS, userMap, null);
+    }
+
+
+    //  紐⑤뱺 �뾽臾� 怨듦컙�쓽 �궗�슜�옄 紐⑸줉�쓣 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<UserVo> findByAllWorkspace(Map<String, Object> resJsonData, UserCondition condition, Pageable pageable) {
+
+        condition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        condition.setPageSize(pageable.getPageSize());
+
+        //  �빐�떦 �뾽臾닿났媛꾩뿉 李몄뿬�븯怨� �엳�뒗 �궗�슜�옄�뒗 �젣�쇅�븳�떎.
+        List<UserWorkspace> userWorkspaces = this.userWorkspaceService.findByWorkspaceId(this.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+
+        for (UserWorkspace userWorkspace : userWorkspaces) {
+            condition.addExcludeIds(userWorkspace.getUser().getId());
+        }
+
+        List<Map<String, Object>> results = this.userMapper.findByAllWorkspace(condition);
+        Long totalUsersCount = this.userMapper.countByAllWorkspace(condition);
+
+        //  寃��깋 寃곌낵瑜� UserVo 濡� 蹂��솚�븳�떎.
+        return this.convertUserVoToMap(results, totalUsersCount, pageable, resJsonData);
+    }
+
+    //  寃��깋 寃곌낵瑜� UserVo 濡� 蹂��솚�븳�떎.
+    private List<UserVo> convertUserVoToMap(List<Map<String, Object>> results, Long totalUsersCount, Pageable pageable, Map<String, Object> resJsonData) {
+        List<UserVo> userVos = Lists.newArrayList();
+
+        for (Map<String, Object> result : results) {
+            UserVo userVo = ConvertUtil.convertMapToClass(result, UserVo.class);
+            userVo.setByName(userVo.getName() + "(" + CommonUtil.decryptAES128(userVo.getAccount()) + ")");
+            userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount()));
+            userVos.add(userVo);
+        }
+
+        int totalPage = (int) Math.ceil((totalUsersCount - 1) / pageable.getPageSize()) + 1;
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, userVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalUsersCount));
+
+        return userVos;
+    }
+
+    //  �궗�슜�옄 紐⑸줉�쓣 �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    @Override
+    @Transactional
+    public ModelAndView downloadExcel(Model model) {
+        List<User> users = this.userRepository.findByStatus(User.USER_STATUS_ACTIVE);
+        List<UserVo> userVos = Lists.newArrayList();
+
+        for (User user : users) {
+            UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class);
+            userVo.setAccount(CommonUtil.decryptAES128(user.getAccount()));
+            userVos.add(userVo);
+        }
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("�궗�슜�옄 紐⑸줉"));
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("id", "怨좎쑀踰덊샇", 6, ExportExcelAttrVo.ALIGN_CENTER));
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("account", "怨꾩젙", 6, ExportExcelAttrVo.ALIGN_CENTER));
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("name", "�씠由�", 6, ExportExcelAttrVo.ALIGN_LEFT));
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("phone", "�쟾�솕踰덊샇", 20, ExportExcelAttrVo.ALIGN_CENTER)); // 愿�由ъ옄
+        excelInfo.setDatas(userVos);
+
+        model.addAttribute(Constants.EXCEL, excelInfo);
+        return new ModelAndView(this.excelView);
+    }
+
+    //  �궗�슜�옄 �뙣�뒪�썙�뱶瑜� �궘�젣�븯怨� �궗�슜�옄 怨꾩젙�쓣 蹂듯샇�솕�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public UserVo removeSensitiveUser(Long userId) {
+        User register = this.getUser(userId);
+        UserVo userVo = ConvertUtil.copyProperties(register, UserVo.class, "password");
+        userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount()));
+        userVo.setByName(userVo.getName() + "(" + userVo.getAccount() + ")");
+
+        return userVo;
+    }
+
+    /*//  �쟾泥� �뙣�뒪�썙�뱶 �뾽�뜲�씠�듃 吏꾪뻾
+    @Override
+    @Transactional
+    public void updatePassword() {
+        List<User> users = this.userRepository.findByStatus(User.USER_STATUS_ACTIVE);
+
+        users.parallelStream().forEach(user -> {
+            if (this.passwordEncoder.upgradeEncoding(user.getPassword())) {
+                user.setPassword(this.passwordEncoder.encode(user.getPassword()));
+            }
+        });
+
+        this.redisConnectionFactory.getConnection().flushAll();
+
+        this.userRepository.saveAll(users);
+    }*/
+
+
+    /*//  �씠踰ㅽ듃 �떦泥⑥옄 紐⑸줉�쓣 �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    @Override
+    @Transactional
+    public ModelAndView downloadExcelEvent(Model model) {
+        List<Map<String, Object>> userMaps = this.userMapper.findEvent();
+        List<UserVo> userVos = Lists.newArrayList();
+
+        for (Map<String, Object> userMap : userMaps) {
+            UserVo userVo = ConvertUtil.convertMapToClass(userMap, UserVo.class);
+            userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount()));
+            userVos.add(userVo);
+        }
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("�씠踰ㅽ듃 �떦泥⑥옄 紐⑸줉"));
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("account", "怨꾩젙", 6, ExportExcelAttrVo.ALIGN_CENTER));
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("name", "�씠由�", 6, ExportExcelAttrVo.ALIGN_LEFT));
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("phone", "�쟾�솕踰덊샇", 20, ExportExcelAttrVo.ALIGN_CENTER));
+        excelInfo.setDatas(userVos);
+
+        model.addAttribute(Constants.EXCEL, excelInfo);
+        return new ModelAndView(this.excelView);
+    }*/
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/UserWithDrawServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/UserWithDrawServiceImpl.java
new file mode 100644
index 0000000..e868a1f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/UserWithDrawServiceImpl.java
@@ -0,0 +1,49 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.UserWithDraw;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.UserWithDrawRepository;
+import kr.wisestone.owl.service.UserWithDrawService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class UserWithDrawServiceImpl extends AbstractServiceImpl<UserWithDraw, Long, JpaRepository<UserWithDraw, Long>> implements UserWithDrawService {
+
+    private static final Logger log = LoggerFactory.getLogger(UserWithDrawServiceImpl.class);
+
+    @Autowired
+    private UserWithDrawRepository userWithDrawRepository;
+
+    @Override
+    protected JpaRepository<UserWithDraw, Long> getRepository() {
+        return this.userWithDrawRepository;
+    }
+
+    //  �쉶�썝 �깉�눜媛� �삁�젙�릺�뼱 �엳�뒗吏� �솗�씤�븯怨� �뾾�쑝硫� �쉶�썝 �깉�눜 �삁�젙�쑝濡� �벑濡앺븳�떎.
+    @Override
+    @Transactional
+    public void addUserWithDraw(User user) {
+        UserWithDraw userWithDraw = this.findByAccount(user.getAccount());
+
+        if (userWithDraw != null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_WITH_DRAW_EXIST));
+        }
+
+        this.userWithDrawRepository.saveAndFlush(new UserWithDraw(user.getAccount()));
+    }
+
+    //  �븫�샇�솕�맂 �씠硫붿씪 二쇱냼濡� �쉶�썝 �깉�눜 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public UserWithDraw findByAccount(String account) {
+        return this.userWithDrawRepository.findByAccount(account);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/UserWorkspaceServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/UserWorkspaceServiceImpl.java
new file mode 100644
index 0000000..d41c05c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/UserWorkspaceServiceImpl.java
@@ -0,0 +1,253 @@
+package kr.wisestone.owl.service.impl;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.UserWorkspace;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.UserWorkspaceMapper;
+import kr.wisestone.owl.repository.UserWorkspaceRepository;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.service.UserWorkspaceService;
+import kr.wisestone.owl.service.WorkspaceService;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.ResPage;
+import kr.wisestone.owl.vo.UserWorkspaceVo;
+import kr.wisestone.owl.web.condition.UserWorkspaceCondition;
+import kr.wisestone.owl.web.form.UserWorkspaceForm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class UserWorkspaceServiceImpl extends AbstractServiceImpl<UserWorkspace, Long, JpaRepository<UserWorkspace, Long>>
+        implements UserWorkspaceService {
+
+    private static final Logger log = LoggerFactory.getLogger(UserWorkspaceServiceImpl.class);
+
+    @Autowired
+    private UserWorkspaceRepository userWorkspaceRepository;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private UserWorkspaceMapper userWorkspaceMapper;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private SimpMessagingTemplate simpMessagingTemplate;
+
+    @Override
+    protected JpaRepository<UserWorkspace, Long> getRepository() {
+        return this.userWorkspaceRepository;
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 �궗�슜�옄媛� 媛��엯�맂�떎
+    @Override
+    @Transactional
+    public UserWorkspace addUserWorkspace(User user, Workspace workspace, Boolean managerYn, Boolean useYn) {
+        Long disableCount = this.userWorkspaceRepository.maxDisablePosition(workspace.getId());
+
+        if (disableCount == null) {
+            disableCount = 1L;
+        }
+
+        UserWorkspace userWorkspace = new UserWorkspace(user, workspace, managerYn, useYn, disableCount);
+
+        return this.userWorkspaceRepository.saveAndFlush(userWorkspace);
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 李몄뿬/李몄뿬��湲� �븯�뒗 �쟾泥� �궗�슜�옄 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<UserWorkspaceVo> findUserWorkspace(Map<String, Object> resJsonData,
+                                                   UserWorkspaceCondition condition, Pageable pageable) {
+
+        UserWorkspace userWorkspace = this.findMyWorkspace(this.webAppUtil.getLoginId());
+        Workspace myWorkspace = userWorkspace.getWorkspace();
+        condition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        condition.setPageSize(pageable.getPageSize());
+        condition.setWorkspaceId(myWorkspace.getId());
+        condition.setAccount(CommonUtil.encryptAES128(condition.getAccount()));
+
+        List<Map<String, Object>> results = this.userWorkspaceMapper.find(condition);
+        Long totalCount = this.userWorkspaceMapper.count(condition);
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        List<UserWorkspaceVo> userWorkspaceVos = ConvertUtil.convertListToListClass(results, UserWorkspaceVo.class);
+
+        for (UserWorkspaceVo userWorkspaceVo : userWorkspaceVos) {
+            userWorkspaceVo.setAccount(CommonUtil.decryptAES128(userWorkspaceVo.getAccount()));
+        }
+
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+        resJsonData.put(Constants.RES_KEY_CONTENTS, userWorkspaceVos);
+
+        return userWorkspaceVos;
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 �뿰寃곕맂 �궗�슜�옄�쓽 李몄뿬 �긽�깭瑜� 蹂�寃쏀븳�떎.
+    @Override
+    @Transactional
+    public void modifyUserWorkspace(UserWorkspaceForm userWorkspaceForm) {
+        UserWorkspace userWorkspace = this.getUserWorkspace(userWorkspaceForm.getId());
+
+        //  李몄뿬濡� �긽�깭瑜� 蹂�寃쏀븯�젮怨� �븷�븣
+        if (!userWorkspace.getUseYn()) {
+            Integer maxUserCount = userWorkspace.getWorkspace().getMaxUser();  //  理쒕� �궗�슜�옄
+            Integer activeUserCount = this.countByWorkspaceIdAndUseYn(userWorkspace.getWorkspace().getId(), true);
+
+            //  理쒕� �궗�슜�옄 - �쁽�옱 李몄뿬 �궗�슜�옄媛� 0蹂대떎 �겕�떎硫� 李몄뿬 �긽�깭濡� 蹂�寃�
+            if ((maxUserCount - activeUserCount) < 1) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.WORKSPACE_MAX_USER_EXCESS_NOT_INCLUDE));
+            }
+        }
+        else {
+            User user = userWorkspace.getUser();
+            //  李몄뿬 ��湲� �궗�슜�옄媛� �쁽�옱 �빐�떦 �뾽臾� 怨듦컙�쓣 �궗�슜�븯怨� �엳�쓣 寃쎌슦 利됱떆 �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 �뒘湲곌쾶 �븳�떎.
+            if (user.getLastWorkspaceId().equals(userWorkspace.getWorkspace().getId())){
+                //  �뾽臾� 怨듦컙�뿉 李몄뿬�븯�뜕 �궗�슜�옄�뿉寃� �젣�쇅 �븣由� 諛� �솕硫� �깉濡쒓퀬移�
+                this.simpMessagingTemplate.convertAndSendToUser(user.getAccount(), "/notification/workspace-disabled", this.messageAccessor.getMessage(MsgConstants.WORKSPACE_OUT, userWorkspace.getWorkspace().getName()));
+            }
+
+            //  李몄뿬 ��湲� �긽�깭濡� 蹂�寃쏀븯硫� �빐�떦 �궗�슜�옄�쓽 留덉�留� �젒洹� �뾽臾� 怨듦컙�뒗 �옄�떊�씠 愿�由ы븯�뒗 �뾽臾� 怨듦컙濡� 蹂�寃쏀븳�떎.
+            this.userService.updateLastMyWorkspace(user);
+        }
+
+        userWorkspace.setUseYn(!userWorkspace.getUseYn());
+        this.userWorkspaceRepository.saveAndFlush(userWorkspace);
+
+    }
+
+
+    //  �빐�떦 �궗�슜�옄瑜� �뾽臾� 怨듦컙�뿉�꽌 鍮꾪솢�꽦�솕 �븳�떎.
+    @Override
+    @Transactional
+    public void disabledUserWorkspace(User user, Workspace workspace) {
+        UserWorkspace userWorkspace = this.userWorkspaceRepository.findByUserIdAndWorkspaceId(user.getId(), workspace.getId());
+
+        if (userWorkspace.getManagerYn()) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKSPACE_MANAGER_NOT_CHANGE_USE_YN));
+        }
+
+        userWorkspace.setUseYn(false);
+        this.userWorkspaceRepository.saveAndFlush(userWorkspace);
+    }
+    //  �빐�떦 �궗�슜�옄媛� �뾽臾� 怨듦컙�뿉 李몄뿬 �젙蹂닿� �엳�뒗吏� �솗�씤�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public UserWorkspace findByUserIdAndWorkspaceId(Long userId, Long workspaceId) {
+        return this.userWorkspaceRepository.findByUserIdAndWorkspaceId(userId, workspaceId);
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 李몄뿬/李몄뿬��湲� �궗�슜�옄 �닔瑜� �꽑�깮�쟻�쑝濡� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Integer countByWorkspaceIdAndUseYn(Long workspaceId, Boolean useYn) {
+        List<UserWorkspace> userWorkspaces = this.findByWorkspaceIdAndUseYn(workspaceId, useYn);
+        return userWorkspaces == null ? 0 : userWorkspaces.size();
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 李몄뿬/李몄뿬��湲� �궗�슜�옄 �젙蹂대�� �꽑�깮�쟻�쑝濡� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<UserWorkspace> findByWorkspaceIdAndUseYn(Long workspaceId, Boolean useYn) {
+        return this.userWorkspaceRepository.findByWorkspaceIdAndUseYn(workspaceId, useYn);
+    }
+
+    //  �뾽臾� 怨듦컙�쓽 愿�由ъ옄/�씪諛� �궗�슜�옄 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<UserWorkspace> findByWorkspaceIdAndManagerYn(Long workspaceId, Boolean managerYn) {
+        return this.userWorkspaceRepository.findByWorkspaceIdAndManagerYn(workspaceId, managerYn);
+    }
+
+    //  濡쒓렇�씤�븳 �궗�슜�옄媛� 愿�由ы븯�뒗 �뾽臾� 怨듦컙 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public UserWorkspace findMyWorkspace(Long userId) {
+        return this.userWorkspaceRepository.findByUserIdAndManagerYn(userId, true);
+    }
+
+    //  �뾽臾� 怨듦컙 �궗�슜�옄 �뿰寃� �븘�씠�뵒濡� �뾽臾� 怨듦컙 �궗�슜�옄 �뿰寃� �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public UserWorkspace getUserWorkspace(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.USER_WORKSPACE_NOT_EXIST));
+        }
+
+        UserWorkspace userWorkspace = this.findOne(id);
+
+        if (userWorkspace == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.USER_WORKSPACE_NOT_EXIST));
+        }
+
+        return userWorkspace;
+    }
+
+    //  �뾽臾� 怨듦컙 �떞�떦�옄 �뿬遺�瑜� �솗�씤�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public boolean checkWorkspaceManager() {
+        boolean bIsManager = false;
+        User loginUser = this.userService.getUser(this.webAppUtil.getLoginId());
+
+        try
+        {
+            Workspace workspace = this.workspaceService.getWorkspace(loginUser.getLastWorkspaceId());   //  �쁽�옱 �젒�냽�븳 �뾽臾� 怨듦컙
+            //  濡쒓렇�씤�븳 �궗�슜�옄媛� 愿�由ы븯�뒗 �뾽臾� 怨듦컙�쓣 李얜뒗�떎.
+            UserWorkspace userWorkspace = this.findMyWorkspace(this.webAppUtil.getLoginId());
+
+            bIsManager = workspace.getId().equals(userWorkspace.getWorkspace().getId());
+        }
+        catch (Exception e)
+        {
+            // exception.
+        }
+
+        //  愿�由ы븯�뒗 �뾽臾� 怨듦컙�씠 �떎瑜� 寃쎌슦
+        return bIsManager;
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 紐⑤뱺 �궗�슜�옄 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<UserWorkspace> findByWorkspaceId(Long workspaceId) {
+        return this.userWorkspaceRepository.findByWorkspaceId(workspaceId);
+    }
+
+    //  �궗�슜湲곌컙�씠 留뚮즺�맂 �뾽臾� 怨듦컙�쓽 愿�由ъ옄媛� �븘�땶 �궗�슜�옄瑜� 異붿텧�븯�뿬 留덉�留� �젒洹쇳븳 �뾽臾� 怨듦컙 �젙蹂대�� 蹂�寃쏀븳�떎.
+    @Override
+    @Transactional
+    public void limitExpireUserWorkspace(Workspace workspace) {
+        //  愿�由ъ옄媛� �븘�땶 �궗�슜�옄 異붿텧
+        List<UserWorkspace> userWorkspaces = this.findByWorkspaceIdAndManagerYn(workspace.getId(), false);
+        //  �궗�슜 湲덉� �꽕�젙 & 留덉�留� �젒洹� �뾽臾� 怨듦컙 �젙蹂� 蹂�寃�
+        for (UserWorkspace userWorkspace : userWorkspaces) {
+            User user = userWorkspace.getUser();
+            this.userService.updateLastDefaultWorkspace(workspace, user);
+            userWorkspace.setUseYn(false);
+        }
+
+        if (userWorkspaces.size() > 0) {
+            this.userWorkspaceRepository.saveAll(userWorkspaces);
+        }
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/WidgetServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/WidgetServiceImpl.java
new file mode 100644
index 0000000..799ed53
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/WidgetServiceImpl.java
@@ -0,0 +1,1231 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.common.ExcelConditionCheck;
+import kr.wisestone.owl.common.MessageAccessor;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.IssueStatus;
+import kr.wisestone.owl.domain.Project;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.IssueMapper;
+import kr.wisestone.owl.mapper.WidgetMapper;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.*;
+import kr.wisestone.owl.vo.*;
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.condition.WidgetCondition;
+import kr.wisestone.owl.web.view.ExcelView;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.StopWatch;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.*;
+
+@Service
+public class WidgetServiceImpl implements WidgetService {
+
+    private static final Logger log = LoggerFactory.getLogger(WidgetServiceImpl.class);
+
+    @Autowired
+    private WidgetMapper widgetMapper;
+
+    @Autowired
+    private IssueMapper issueMapper;
+
+    @Autowired
+    protected WebAppUtil webAppUtil;
+
+    @Autowired
+    protected PageUtil pageUtil;
+
+    @Autowired
+    private ProjectService projectService;
+
+    @Autowired
+    private IssueStatusService issueStatusService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    protected MessageAccessor messageAccessor;
+
+    @Autowired
+    private ExcelView excelView;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private ExcelConditionCheck excelConditionCheck;
+
+    @Autowired
+    private UserWorkspaceService userWorkspaceService;
+
+
+    //  �쟾泥� �쐞�젽�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findAllWidget(Map<String, Object> resJsonData) {
+        Long lastProjectId = userService.getUser(this.webAppUtil.getLoginId()).getLastProjectId();
+
+        //  �쐞�젽�쓽 湲곕낯 寃��깋 議곌굔�쓣 �꽕�젙�븳�떎.
+        WidgetCondition widgetCondition = this.makeWidgetCondition();
+        if (lastProjectId != null && lastProjectId > -1) {
+            widgetCondition.setProjectIds(lastProjectId);
+            resJsonData.put("dashboardType", 1);
+        } else {
+            resJsonData.put("dashboardType", 0);
+        }
+
+        //  �럹�씠吏� �꽕�젙 - 紐⑤뱺 �쐞�젽�� �뜲�씠�꽣瑜� 5媛쒕쭔 媛��졇�삩�떎.
+        PageVo pageVo = this.pageUtil.getDefaultPageVo();
+        pageVo.setPageSize(5);
+        Pageable pageable = this.pageUtil.convertPageable(pageVo);
+
+        //  �쟾泥� �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+        this.findStatisticsIssue(resJsonData, widgetCondition);
+        //  1.129 - 1.06 �뒠�떇 寃곌낵 : 0.795 - 0.686
+
+        //  �쟾泥� �씠�뒋 泥섎━�쁽�솴�쓣 議고쉶�븳�떎.
+        this.findIssueComplete(resJsonData, widgetCondition, null);
+        //  0.169 - 0.168
+
+        //  吏꾪뻾以묒씤 �봽濡쒖젥�듃 �쁽�솴 �젙蹂대�� 議고쉶�븳�떎.
+        this.findProjectProgress(resJsonData, widgetCondition);
+        //  0.1 - 0.1
+
+        //  �굹�쓽 �씠�뒋 �쁽�솴 �젙蹂대�� 議고쉶�븳�떎. - �궡媛� �떞�떦/�븷�떦�븯�뒗 �씠�뒋 �쁽�솴 �젙蹂대룄 �뿬湲곗꽌 異붿텧�븳�떎.
+        this.findMyIssueDetail(resJsonData, widgetCondition);
+        // 5.053 - 6.933 �뒠�떇 寃곌낵 : 0.36 - 0.33
+
+        //  �굹�뿉寃� �븷�떦�맂 �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+        this.findMyAssigneeIssue(resJsonData, widgetCondition, pageable);
+        //  0.442 - 0.399 �뒠�떇 寃곌낵 : 0.266 - 0.273
+
+        //  �쐞�뿕 愿�由� �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+        this.findRiskIssue(resJsonData, widgetCondition, pageable);
+        // 1.153 - 1.15 - �뒠�떇 寃곌낵 : 0.64 - 0.63
+
+        //  �궡媛� �벑濡앺븳 �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+        this.findRegisterIssue(resJsonData, widgetCondition, pageable);
+        // 0.263 - 0.270 - �뒠�떇 寃곌낵 : 0.145 - 0.149
+        StopWatch serviceStart = new StopWatch();
+        serviceStart.start();
+        //  吏��뿰以묒씤 �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+        this.findDelayIssue(resJsonData, widgetCondition, pageable);
+        //  0.044 - 0.055
+        serviceStart.stop();
+        //  �긽�깭蹂� �씠�뒋 �쁽�솴�쓣 議고쉶�븳�떎.
+        this.findByStandIssueStatus(resJsonData, widgetCondition);
+
+        //  �봽濡쒖젥�듃 蹂� 硫ㅻ쾭 吏꾪뻾瑜� �젙蹂대�� 議고쉶�븳�떎.
+        this.findMemberProgress(resJsonData, widgetCondition, false);
+        // 0.271 - 0.271
+
+        //  �씠�뒋 ���엯 蹂� �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+        this.findByStandIssueType(resJsonData, widgetCondition, false);
+        //  0.025
+
+        // 以묒슂�룄 蹂� �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+        this.findSeverityIssueWidget(resJsonData, widgetCondition, null, pageable);
+
+
+        log.debug("serviceEnd : " + serviceStart.getTime());
+
+        log.debug("�셿猷�");
+    }
+
+    //  �쐞�젽�쓽 湲곕낯 寃��깋 議곌굔�쓣 �꽕�젙�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public WidgetCondition makeWidgetCondition() {
+        //  �빐�떦 �썙�겕�뒪�럹�씠�뒪�뿉�꽌 李몄뿬�븯怨� �엳�뒗 �봽濡쒖젥�듃 以� �긽�깭媛� �삤�뵂�씤 �봽濡쒖젥�듃
+        List<Map<String, Object>> projects = null;
+        if (this.userWorkspaceService.checkWorkspaceManager()) {
+            projects = this.projectService.findByWorkspaceManagerAll();
+        } else  {
+            projects = this.projectService.findByWorkspaceIdAndIncludeProjectAll(Lists.newArrayList("02"), ProjectType.BTS_PROJECT.toString());
+        }
+
+        List<Long> projectIds = Lists.newArrayList();
+
+        for (Map<String, Object> result : projects) {
+            Long projectId = MapUtil.getLong(result, "id");
+
+            if (projectId != null) {
+                projectIds.add(projectId);
+            }
+        }
+
+        WidgetCondition widgetCondition = new WidgetCondition();
+        widgetCondition.setProjectIds(projectIds);
+        widgetCondition.setProjects(projects);
+        widgetCondition.setLoginUserId(this.webAppUtil.getLoginId());
+        widgetCondition.setCompleteDate(DateUtil.convertDateToStr(new Date()));
+        widgetCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+
+        return widgetCondition;
+    }
+
+    //  �쟾泥� �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findStatisticsIssue(Map<String, Object> resJsonData, WidgetCondition widgetCondition) {
+        Long noAssigneeIssue = 0L;  //  誘명븷�떦 �씠�뒋
+        Long remainIssue = 0L;  //  �옍�뿬 �씠�뒋
+        Long assigneeIssue = 0L;    //  �븷�떦�맂 �씠�뒋
+        Long registerIssue = 0L; //  �벑濡앺븳 �씠�뒋
+        Long delayIssue = 0L; //  吏��뿰�맂 �씠�뒋
+        Long completeIssue = 0L; // �셿猷뚮맂 �씠�뒋
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            remainIssue = this.widgetMapper.countRemainIssue(widgetCondition);   //  �옍�뿬 �씠�뒋
+            delayIssue = this.widgetMapper.countTodayDelayIssue(widgetCondition); //  吏��뿰�맂 �씠�뒋
+            assigneeIssue = this.widgetMapper.countAssigneeIssue(widgetCondition);   //  �븷�떦�맂 �씠�뒋
+            registerIssue = this.widgetMapper.countTodayRegisterIssue(widgetCondition);    //  �벑濡앺븳 �씠�뒋
+            noAssigneeIssue = this.widgetMapper.countNoAssigneeIssue(widgetCondition);   //  誘명븷�떦 �씠�뒋
+            completeIssue = this.widgetMapper.countCompleteIssue(widgetCondition); // �셿猷뚮맂 �씠�뒋
+        }
+
+        Map<String, Object> results = new HashMap<>();
+        results.put("today", DateUtil.convertDateToStr(new Date(), "yyyy.MM.dd"));
+        results.put("projectIds", widgetCondition.getProjectIds());
+        results.put("noAssigneeIssue", noAssigneeIssue);
+        results.put("registerIssue", registerIssue);
+        results.put("assigneeIssue", assigneeIssue);
+        results.put("delayIssue", delayIssue);
+        results.put("remainIssue", remainIssue);
+        results.put("completeIssue", completeIssue);
+        //  �뾽臾� 怨듦컙 留뚮즺媛� 7�씪�쟾�씪 寃쎌슦 �몴�떆
+        results.put("workspace", this.workspaceService.getWorkspaceExpireDay());
+
+        resJsonData.put("issueStatisticsWidget", results);
+    }
+
+    //  吏꾪뻾以묒씤 �봽濡쒖젥�듃 �쁽�솴 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findProjectProgress(Map<String, Object> resJsonData, WidgetCondition widgetCondition) {
+        List<Map<String, Object>> progressingProjectDetails = Lists.newArrayList();
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            if (this.userWorkspaceService.checkWorkspaceManager()) {
+                progressingProjectDetails = this.widgetMapper.findProjectProgressAll(widgetCondition);
+            } else {
+                progressingProjectDetails = this.widgetMapper.findProjectProgress(widgetCondition);
+            }
+        }
+
+        Long completeIssueCount = 0L;
+        Long remainIssueCount = 0L;
+
+        for (Map<String, Object> progressingProjectDetail : progressingProjectDetails) {
+            Long completeCount = MapUtil.getLong(progressingProjectDetail, "close");    //  �셿猷� �씠�뒋
+            Long remainCount = MapUtil.getLong(progressingProjectDetail, "remain"); //  �옍�뿬 �씠�뒋
+            //  愿�由ъ옄 �젙蹂대�� �뙆�떛�븯�뿬 愿�由ъ옄 紐�, �씠硫붿씪, �봽濡쒗븘 �젙蹂대�� 異붿텧�븳�떎.
+            this.parseManagerInfo(MapUtil.getString(progressingProjectDetail, "managerInfo"), progressingProjectDetail);
+            //  �봽濡쒖젥�듃 吏꾪뻾瑜� �젙蹂대�� 援ы븳�떎.
+            this.statisticsProject(completeCount, remainCount, progressingProjectDetail, "projectProgressPercent");
+
+            if (completeCount != null) {
+                completeIssueCount += completeCount;
+            }
+
+            if (remainCount != null) {
+                remainIssueCount += remainCount;
+            }
+        }
+        //  �봽濡쒖젥�듃 吏꾪뻾瑜�
+        Double progressPercent = ((double) completeIssueCount / ((double) completeIssueCount + (double) remainIssueCount)) * 100.0;
+
+        if (progressPercent.isNaN()) {
+            progressPercent = 0.0;
+        }
+
+        Map<String, Object> results = new HashMap<>();
+        results.put("totalIssueCount", completeIssueCount + remainIssueCount);
+        results.put("completeIssueCount", completeIssueCount);
+        results.put("remainIssueCount", remainIssueCount);
+        results.put("progressPercent", progressPercent);
+        results.put("projects", progressingProjectDetails);
+
+        resJsonData.put("projectProgressWidget", results);
+    }
+
+    //  愿�由ъ옄 �젙蹂대�� �뙆�떛�븯�뿬 愿�由ъ옄 紐�, �씠硫붿씪, �봽濡쒗븘 �젙蹂대�� 異붿텧�븳�떎.
+    private void parseManagerInfo(String managerInfo, Map<String, Object> progressingProjectDetail) {
+        if (managerInfo != null) {
+            int count = 0;
+
+            for (String text : managerInfo.split("%")) {
+                if (!StringUtils.isEmpty(text)) {
+
+                    if (count == 0) {
+                        progressingProjectDetail.put("managerName", text);
+                    }
+
+                    if (count == 1) {
+                        progressingProjectDetail.put("managerEmail", CommonUtil.decryptAES128(text));
+                    }
+
+                    if (count == 2) {
+                        progressingProjectDetail.put("managerProfile", text);
+                    }
+                }
+                count++;
+            }
+        }
+    }
+
+    //  �봽濡쒖젥�듃 吏꾪뻾瑜� �젙蹂대�� 援ы븳�떎.
+    private void statisticsProject(Long completeCount, Long remainCount, Map<String, Object> progressingProjectDetail, String mapKey) {
+        Double projectProgressPercent = ((double) completeCount / ((double) completeCount + (double) remainCount)) * 100.0;
+
+        if (projectProgressPercent.isNaN()) {
+            projectProgressPercent = 0.0;
+        }
+
+        progressingProjectDetail.put(mapKey, projectProgressPercent);
+    }
+
+    //  �굹�뿉寃� �븷�떦�맂 �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findMyAssigneeIssue(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Pageable pageable) {
+        widgetCondition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        widgetCondition.setPageSize(pageable.getPageSize());
+
+        //  �븷�떦�맂 �옍�뿬 �씠�뒋
+        Map<String, Object> myIssueWidget = (Map<String, Object>) resJsonData.get("myIssueWidget");
+        Long totalAssigneeRemainIssueCount = MapUtil.getLong(myIssueWidget, "totalAssigneeRemainIssueCount");
+        if (totalAssigneeRemainIssueCount == null) {
+            totalAssigneeRemainIssueCount = 0L;
+        }
+
+        //  �삤�뒛 �븷�떦�맂 �씠�뒋
+        Long todayCount = 0L;
+        List<Map<String, Object>> assigneeIssues = Lists.newArrayList();
+        Long totalCount = 0L;
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            todayCount = this.widgetMapper.countTodayMyAssigneeIssue(widgetCondition);
+            assigneeIssues = this.widgetMapper.findMyAssigneeIssue(widgetCondition);
+            totalCount = this.widgetMapper.countMyAssigneeIssue(widgetCondition);
+        }
+
+
+        //  0.156 - 0.166
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+
+        Map<String, Object> results = new HashMap<>();
+        results.put("todayCount", todayCount);
+        results.put("remainCount", totalAssigneeRemainIssueCount);
+        results.put("issues", assigneeIssues);
+        results.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        resJsonData.put("myAssigneeIssueWidget", results);
+    }
+
+    //  吏��뿰以묒씤 �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findDelayIssue(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Pageable pageable) {
+        widgetCondition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        widgetCondition.setPageSize(pageable.getPageSize());
+
+        List<Map<String, Object>> delayIssues = Lists.newArrayList();
+        Long totalCount = 0L;
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            delayIssues = this.widgetMapper.findDelayIssue(widgetCondition);
+            totalCount = this.widgetMapper.countDelayIssue(widgetCondition);
+        }
+
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        //  �삤�뒛 �궇吏쒕�� 湲곗��쑝濡� 吏��뿰�씪�쓣 援ы븳�떎.
+        this.setDelayDay(delayIssues);
+
+        Map<String, Object> results = new HashMap<>();
+        results.put("delayCount", totalCount);
+        results.put("issues", delayIssues);
+        results.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+        resJsonData.put("delayIssueWidget", results);
+    }
+
+    //  �삤�뒛 �궇吏쒕�� 湲곗��쑝濡� 吏��뿰�씪�쓣 援ы븳�떎.
+    private void setDelayDay(List<Map<String, Object>> delayIssues) {
+        for (Map<String, Object> delayIssue : delayIssues) {
+            String completeDate = MapUtil.getString(delayIssue, "completeDate");
+            Date convertCompleteDate = DateUtil.convertStrToDate(completeDate, "yyyy.MM.dd");
+            Integer diff = DateUtil.getDateDiff(convertCompleteDate, new Date());
+            delayIssue.put("delayDay", diff);
+        }
+    }
+
+    //  �궡媛� �벑濡앺븳 �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findRegisterIssue(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Pageable pageable) {
+        widgetCondition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        widgetCondition.setPageSize(pageable.getPageSize());
+
+        //  �벑濡앺븳 �옍�뿬 �씠�뒋
+        Map<String, Object> myIssueWidget = (Map<String, Object>) resJsonData.get("myIssueWidget");
+        Long totalRegisterRemainIssueCount = MapUtil.getLong(myIssueWidget, "totalRegisterRemainIssueCount");
+        if (totalRegisterRemainIssueCount == null) {
+            totalRegisterRemainIssueCount = 0L;
+        }
+
+        //  �삤�뒛 �벑濡앺븳 �씠�뒋
+        Long todayCount = 0L;
+        List<Map<String, Object>> registerIssues = Lists.newArrayList();
+        Long totalCount = 0L;
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            todayCount = this.widgetMapper.countTodayRegisterIssue(widgetCondition);
+            registerIssues = this.widgetMapper.findRegisterIssue(widgetCondition);
+            totalCount = this.widgetMapper.countRegisterIssue(widgetCondition);
+        }
+
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        Map<String, Object> results = new HashMap<>();
+
+        results.put("todayCount", todayCount);
+        results.put("remainCount", totalRegisterRemainIssueCount);
+        results.put("issues", registerIssues);
+        results.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+        resJsonData.put("registerIssueWidget", results);
+    }
+
+    //  �봽濡쒖젥�듃 蹂� 硫ㅻ쾭 吏꾪뻾瑜� �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findMemberProgress(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Boolean getWidgetCondition) {
+        //  �쐞�젽 寃��깋 議곌굔�쓣 留뚮뱾怨� �쟾泥� �봽濡쒖젥�듃 �젙蹂대�� 由ы꽩�븳�떎.
+        Map<String, Object> results = this.makeWidgetConditionAllProject(widgetCondition, getWidgetCondition);
+
+        if (widgetCondition.getProjectId() != null) {
+            List<Map<String, Object>> projectMemberIssues = this.widgetMapper.findProjectMemberIssue(widgetCondition);
+
+            for (Map<String, Object> projectMemberIssue : projectMemberIssues) {
+                Long completeCount = MapUtil.getLong(projectMemberIssue, "completeCount");
+                Long remainCount = MapUtil.getLong(projectMemberIssue, "remainCount");
+                projectMemberIssue.put("account", CommonUtil.decryptAES128(MapUtil.getString(projectMemberIssue, "account")));
+
+                //  �봽濡쒖젥�듃 吏꾪뻾瑜� �젙蹂대�� 援ы븳�떎.
+                this.statisticsProject(completeCount, remainCount, projectMemberIssue, "projectProgressPercent");
+            }
+            //  硫ㅻ쾭蹂� �젙蹂�
+            results.put("members", projectMemberIssues);
+        } else {
+            results.put("members", Lists.newArrayList());
+        }
+
+        resJsonData.put("memberProgressWidget", results);
+    }
+
+    //  �굹�쓽 �씠�뒋 �쁽�솴 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findMyIssueDetail(Map<String, Object> resJsonData, WidgetCondition widgetCondition) {
+        List<Map<String, Object>> registerCompleteIssueMaps = Lists.newArrayList();
+        List<Map<String, Object>> registerRemainIssueMaps = Lists.newArrayList();
+        List<Map<String, Object>> assigneeCompleteIssueMaps = Lists.newArrayList();
+        List<Map<String, Object>> assigneeRemainIssueMaps = Lists.newArrayList();
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            registerCompleteIssueMaps = this.widgetMapper.findMyRegisterCompleteIssue(widgetCondition);
+            registerRemainIssueMaps = this.widgetMapper.findMyRegisterRemainIssue(widgetCondition);
+            assigneeCompleteIssueMaps = this.widgetMapper.findMyAssigneeCompleteIssue(widgetCondition);
+            assigneeRemainIssueMaps = this.widgetMapper.findMyAssigneeRemainIssue(widgetCondition);
+        }
+
+        Map<String, Object> result = new HashMap<>();
+        Long totalRegisterRemainIssueCount = 0L;    //  �벑濡앺븳 �씠�뒋 以� �옍�뿬 �씠�뒋 珥� 媛��닔
+        Long totalAssigneeRemainIssueCount = 0L;    //  �븷�떦�맂 �씠�뒋 以� �옍�뿬 �씠�뒋 珥� 媛��닔
+
+        for (Map<String, Object> project : widgetCondition.getProjects()) {
+            Long projectId = MapUtil.getLong(project, "id");
+            if (projectId != null) {
+                //  Map �뿉�꽌 �벑濡�/�떞�떦�븯�뒗 �씠�뒋 媛��닔瑜� 異붿텧�븳�떎.
+                Long registerCompleteIssueCount = this.getMyIssueCount(projectId, registerCompleteIssueMaps);
+                Long registerRemainIssueCount = this.getMyIssueCount(projectId, registerRemainIssueMaps);
+                Long assigneeCompleteIssueCount = this.getMyIssueCount(projectId, assigneeCompleteIssueMaps);
+                Long assigneeRemainIssueCount = this.getMyIssueCount(projectId, assigneeRemainIssueMaps);
+                project.put("registerCompleteIssueCount", registerCompleteIssueCount);
+                project.put("registerRemainIssueCount", registerRemainIssueCount);
+                project.put("totalRegisterIssueCount", (registerCompleteIssueCount + registerRemainIssueCount));
+                project.put("assigneeCompleteIssueCount", assigneeCompleteIssueCount);
+                project.put("assigneeRemainIssueCount", assigneeRemainIssueCount);
+                project.put("totalAssigneeIssueCount", (assigneeCompleteIssueCount + assigneeRemainIssueCount));
+                this.statisticsProject(registerCompleteIssueCount, registerRemainIssueCount, project, "registerIssueProgressPercent");
+                this.statisticsProject(assigneeCompleteIssueCount, assigneeRemainIssueCount, project, "assigneeIssueProgressPercent");
+                totalRegisterRemainIssueCount += registerRemainIssueCount;
+                totalAssigneeRemainIssueCount += assigneeRemainIssueCount;
+            }
+        }
+
+        result.put("issues", widgetCondition.getProjects());
+        result.put("totalRegisterRemainIssueCount", totalRegisterRemainIssueCount);
+        result.put("totalAssigneeRemainIssueCount", totalAssigneeRemainIssueCount);
+        resJsonData.put("myIssueWidget", result);
+    }
+
+    //  Map �뿉�꽌 �벑濡�/�떞�떦�븯�뒗 �씠�뒋 媛��닔瑜� 異붿텧�븳�떎.
+    private long getMyIssueCount(Long projectId, List<Map<String, Object>> maps) {
+        long issueCount = 0L;
+
+        for (Map<String, Object> map : maps) {
+            Long issueProjectId = MapUtil.getLong(map, "projectId");
+            if (issueProjectId != null) {
+                if (projectId.equals(issueProjectId)) {
+                    Long count = MapUtil.getLong(map, "issueCount");
+                    if (count != null) {
+                        issueCount = count;
+                    }
+                }
+            }
+        }
+
+        return issueCount;
+    }
+
+    //  �쐞�뿕 愿�由� �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findRiskIssue(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Pageable pageable) {
+        widgetCondition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        widgetCondition.setPageSize(pageable.getPageSize());
+
+        Map<String, Object> countChangeStatusAndAssigneeIssue = new HashMap<>();
+        countChangeStatusAndAssigneeIssue.put("changeAssigneeCount", 0L);
+        countChangeStatusAndAssigneeIssue.put("changeIssueStatusCount", 0L);
+        //  0.212 - 0.213
+        List<Map<String, Object>> riskIssues = Lists.newArrayList();
+        //  0.244 - 0.248
+
+        Long totalCount = 0L;
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            countChangeStatusAndAssigneeIssue = this.widgetMapper.countChangeStatusAndAssigneeIssue(widgetCondition);
+            riskIssues = this.widgetMapper.findRiskIssue(widgetCondition);
+            totalCount = this.widgetMapper.countRiskIssue(widgetCondition);
+        }
+
+        //  �씠�뒋�쓽 �떞�떦�옄 �젙蹂대�� 媛��졇�삩�떎.
+        if (riskIssues.size() > 0) {
+            this.setIssueUsers(riskIssues);
+        }
+
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        Map<String, Object> results = new HashMap<>();
+
+        results.put("changeAssigneeCount", MapUtil.getLong(countChangeStatusAndAssigneeIssue, "changeAssigneeCount"));
+        results.put("changeIssueStatusCount", MapUtil.getLong(countChangeStatusAndAssigneeIssue, "changeIssueStatusCount"));
+        results.put("issues", riskIssues);
+        results.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+        resJsonData.put("riskIssueWidget", results);
+    }
+
+    //  �씠�뒋�쓽 �떞�떦�옄 �젙蹂대�� 媛��졇�삩�떎.
+    private void setIssueUsers(List<Map<String, Object>> riskIssues) {
+        List<String> issueIds = Lists.newArrayList();
+
+        for (Map<String, Object> riskIssue : riskIssues) {
+            issueIds.add(MapUtil.getString(riskIssue, "id"));
+        }
+
+        List<Map<String, Object>> issueUsers = this.issueMapper.findIssueUser(new IssueCondition(issueIds));
+        Map<String, Object> mapIssueUsers = new HashMap<>();    //  �씠�뒋 踰덊샇瑜� �궎濡쒗빐�꽌 �씠�뒋 �떞�떦�옄 �젙蹂대�� ���옣�븳�떎.
+
+        for (Map<String, Object> issueUser : issueUsers) {
+            String issueId = MapUtil.getString(issueUser, "issueId");
+            issueUser.put("account", CommonUtil.decryptAES128(MapUtil.getString(issueUser, "account")));
+
+            //  議댁옱�븯吏� �븡�쓣 寃쎌슦
+            if (mapIssueUsers.get(issueId) == null) {
+                List<Map<String, Object>> users = Lists.newArrayList();
+                users.add(issueUser);
+                mapIssueUsers.put(issueId, users);
+            } else {
+                //  �씠誘� 議댁옱�븷 寃쎌슦
+                List<Map<String, Object>> users = (List<Map<String, Object>>) mapIssueUsers.get(issueId);
+                users.add(issueUser);
+                mapIssueUsers.put(issueId, users);
+            }
+        }
+
+        for (Map<String, Object> riskIssue : riskIssues) {
+            String issueId = MapUtil.getString(riskIssue, "id");
+            if (mapIssueUsers.get(issueId) == null) {
+                riskIssue.put("issueUsers", Lists.newArrayList());
+            } else {
+                riskIssue.put("issueUsers", mapIssueUsers.get(issueId));
+            }
+        }
+    }
+
+    //  �쟾泥� �씠�뒋 泥섎━ �쁽�솴
+    @Override
+    @Transactional(readOnly = true)
+    public void findIssueComplete(Map<String, Object> resJsonData, WidgetCondition widgetCondition, String searchPeriod) {
+        //  寃��깋 議곌굔�씠 湲곕낯 �꽕�젙�씪 �븣 - 理쒓렐 7�씪
+        if (searchPeriod == null) {
+            widgetCondition.setSearchPeriod(DateUtil.LAST_SEVEN_DAYS);
+        } else {
+            widgetCondition.setSearchPeriod(searchPeriod);
+        }
+
+        //  寃��깋 �씪�옄瑜� 援ы븳�떎.
+        List<Date> searchDates = CommonUtil.findSearchPeriod(widgetCondition.getSearchPeriod());
+
+        //  �궇吏쒓� 寃��깋�릺吏� �븡�븯�쑝硫� �삤瑜�
+        if (searchDates.size() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WIDGET_SEARCH_DATE_NOT_FOUND));
+        }
+
+        //  �쐞�젽 寃��깋 議곌굔�뿉 �떆�옉�씪, 醫낅즺�씪�쓣 �꽕�젙�븳�떎.
+        widgetCondition.setSearchStartDate(DateUtil.convertDateToYYYYMMDD(searchDates.get(0)));
+        widgetCondition.setSearchEndDate(DateUtil.convertDateToYYYYMMDD(DateUtil.addDays(searchDates.get(searchDates.size() - 1), 1)));
+
+        //  �씠�뒋
+        List<Map<String, Object>> dateCounts = Lists.newArrayList();
+        Long totalIssueCount = 0L;
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            dateCounts = this.widgetMapper.findIssueComplete(widgetCondition);
+            totalIssueCount = this.widgetMapper.countTotalIssue(widgetCondition);
+        }
+
+        Map<String, Object> results = new HashMap<>();
+        //  �쟾泥� �씠�뒋 泥섎━ �쁽�솴�쓣 �궇吏쒕퀎濡� 援щ텇�빐�꽌 ���옣�븳�떎.
+        this.setIssueCompleteCountList(searchDates, dateCounts, totalIssueCount, results);
+
+        resJsonData.put("issueCompleteWidget", results);
+    }
+
+    //  �쟾泥� �씠�뒋 泥섎━ �쁽�솴�쓣 �궇吏쒕퀎濡� 援щ텇�빐�꽌 ���옣�븳�떎.
+    private void setIssueCompleteCountList(List<Date> searchDates, List<Map<String, Object>> dateCounts, Long totalIssueCount, Map<String, Object> results) {
+        List<Map<String, Object>> issueCompleteCountList = Lists.newArrayList();
+        Integer totalCompleteCount = 0;
+
+        //  �씪�옄 蹂꾨줈 湲곕낯 媛� �뀑�똿
+        for (Date searchDate : searchDates) {
+            Map<String, Object> result = new HashMap<>();
+            result.put("modifyDate", DateUtil.convertDateToYYYYMMDD(searchDate));
+            result.put("issueCount", 0);
+
+            //  �닔�젙�씪
+            for (Map<String, Object> dateCount : dateCounts) {
+                String targetDate = MapUtil.getString(dateCount, "modifyDate");
+
+                if (DateUtil.convertDateToYYYYMMDD(searchDate).equals(targetDate)) {
+                    Integer issueCount = MapUtil.getInteger(dateCount, "issueCount");
+                    totalCompleteCount += issueCount;
+                    result.put("issueCount", issueCount);
+                    break;
+                }
+            }
+
+            issueCompleteCountList.add(result);
+        }
+
+        for (Map<String, Object> issueCompleteCount : issueCompleteCountList) {
+            String modifyDate = MapUtil.getString(issueCompleteCount, "modifyDate");
+            Date convertModifyDate = DateUtil.convertStrToDate(modifyDate, "yyyy-MM-dd");
+            issueCompleteCount.put("modifyDate", DateUtil.convertDateToStr(convertModifyDate, "MM.dd"));
+        }
+
+        results.put("issueCompleteCountList", issueCompleteCountList);
+        results.put("completeCount", totalCompleteCount);
+        results.put("totalIssueCount", totalIssueCount);
+
+        //  泥섎━�쑉
+        Double issueCompleteWidget = ((double) totalCompleteCount / (double) totalIssueCount) * 100.0;
+
+        if (issueCompleteWidget.isNaN()) {
+            issueCompleteWidget = 0.0;
+        }
+
+        Double dayIssueCompleteAvg = ((double) totalCompleteCount / searchDates.size());
+
+        results.put("dayIssueCompleteAvg", dayIssueCompleteAvg);
+        results.put("issueProgressPercent", issueCompleteWidget);
+    }
+
+    //  �긽�깭蹂� �씠�뒋 �쁽�솴
+    @Override
+    @Transactional(readOnly = true)
+    public void findByStandIssueStatus(Map<String, Object> resJsonData, WidgetCondition widgetCondition) {
+        List<IssueStatus> issueStatuses = this.issueStatusService.findByWorkspaceId(widgetCondition.getWorkspaceId());
+        widgetCondition.setIssueStatuses(issueStatuses);
+
+        List<Map<String, Object>> issueStatusMaps = Lists.newArrayList();
+
+        for (IssueStatus issueStatus : issueStatuses) {
+            Map<String, Object> issueStatusMap = new HashMap<>();
+            issueStatusMap.put("key", issueStatus.getName());
+            issueStatusMap.put("color", issueStatus.getColor());
+            issueStatusMap.put("values", Lists.newArrayList());
+            issueStatusMaps.add(issueStatusMap);
+        }
+
+        List<Map<String, Object>> results = Lists.newArrayList();
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            results = this.widgetMapper.findByStandIssueStatus(widgetCondition);
+        }
+
+        for (Map<String, Object> result : results) {
+            for (Map<String, Object> issueStatusMap : issueStatusMaps) {
+                String key = MapUtil.getString(issueStatusMap, "key");
+                Long value = MapUtil.getLong(result, key);
+                List<Map<String, Object>> values = (List<Map<String, Object>>) issueStatusMap.get("values");
+                Map<String, Object> map = new HashMap<>();
+                map.put("x", MapUtil.getString(result, "projectName"));
+                map.put("y", value);
+                values.add(map);
+            }
+        }
+
+        resJsonData.put("issueStatusWidget", issueStatusMaps);
+    }
+
+    //  �씠�뒋 ���엯 蹂� �씠�뒋 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findByStandIssueType(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Boolean getWidgetCondition) {
+        //  �쐞�젽 寃��깋 議곌굔�쓣 �뼸�뼱�빞 �븷 �긽�솴�씪 �븣 - �솕硫댁뿉�꽌 �꺆�쓣 �닃�윭 �뜲�씠�꽣瑜� �옱�슂泥��뻽�쓣 �븣 �쐞�젽 寃��깋 議곌굔�쓣 留뚮뱾怨� �쟾泥� �봽濡쒖젥�듃 �젙蹂대�� 由ы꽩�븳�떎.
+        Map<String, Object> results = this.makeWidgetConditionAllProject(widgetCondition, getWidgetCondition);
+
+        if (widgetCondition.getProjectId() != null) {
+            List<Map<String, Object>> issueTypeIssues = this.widgetMapper.findByStandIssueType(widgetCondition);
+            //  �씠�뒋 �젙蹂�
+            results.put("issues", issueTypeIssues);
+        } else {
+            results.put("issues", Lists.newArrayList());
+        }
+
+        resJsonData.put("issueTypeWidget", results);
+    }
+
+    //  �쐞�젽 寃��깋 議곌굔�쓣 留뚮뱾怨� �쟾泥� �봽濡쒖젥�듃 �젙蹂대�� 由ы꽩�븳�떎.
+    private Map<String, Object> makeWidgetConditionAllProject(WidgetCondition widgetCondition, boolean getWidgetCondition) {
+        Map<String, Object> results = new HashMap<>();
+
+        //  �쐞�젽 寃��깋 議곌굔�쓣 �뼸�뼱�빞 �븷 �긽�솴�씪 �븣 - �솕硫댁뿉�꽌 �꺆�쓣 �닃�윭 �뜲�씠�꽣瑜� �옱�슂泥��뻽�쓣 �븣
+        if (getWidgetCondition) {
+            WidgetCondition condition = this.makeWidgetCondition();
+            widgetCondition.setProjectIds(condition.getProjectIds());
+        }
+
+        //  �쟾泥� �쐞�젽 寃��깋 �쑝濡� �뱾�뼱�솕�쓣 寃쎌슦 - findAllWidget()
+        if (widgetCondition.getProjectId() == null && (widgetCondition.getProjectIds().size() > 0) && !getWidgetCondition) {
+            widgetCondition.setProjectId(widgetCondition.getProjectIds().get(0));
+        }
+
+        if (widgetCondition.getProjectId() != null) {
+            List<Project> projects = this.projectService.findAll(widgetCondition.getProjectIds());  //  �쟾泥� �봽濡쒖젥�듃
+            Project project = this.projectService.findOne(widgetCondition.getProjectId());
+            results.put("projectVos", ConvertUtil.convertObjectsToClasses(projects, ProjectVo.class));
+
+            if (project != null) {
+                results.put("projectName", project.getName());
+            } else  {
+                results.put("projectName", "");
+            }
+        } else {
+            results.put("projectVos", Lists.newArrayList());
+            results.put("projectName", "");
+        }
+
+        return results;
+    }
+
+
+    //以묒슂�룄 蹂� �씠�뒋 �쁽�솴 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findSeverityIssueWidget(Map<String, Object> resJsonData, WidgetCondition widgetCondition, Map<String, Object> parameter, Pageable pageable) {
+        widgetCondition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        widgetCondition.setPageSize(pageable.getPageSize());
+
+        Long severityId = 1L;
+        Long projectId = null;
+        if (parameter != null) {
+            severityId = MapUtil.getLong(parameter,"severityId");
+            projectId = MapUtil.getLong(parameter,"projectId");
+        }
+
+        //  寃��깋議곌굔 湲곕낯 �꽕�젙 媛� - 以묒슂�룄: �떖媛�
+        if (severityId != null) {
+            widgetCondition.setSeverityId(severityId);
+        }
+
+        if (projectId != null) {
+            widgetCondition.setProjectIds(projectId);
+            widgetCondition.setProjectId(projectId);
+        }
+
+        //以묒슂�룄 蹂� �씠�뒋
+        List<Map<String, Object>> severityCounts = Lists.newArrayList();
+        List<Map<String, Object>> severityIssues = Lists.newArrayList();
+        Long totalCount = 0L;
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            severityCounts = this.widgetMapper.countSeverityIssue(widgetCondition);
+            severityIssues = this.widgetMapper.findSeverityIssues(widgetCondition);
+            totalCount = this.widgetMapper.countSearchIssue(widgetCondition);
+        }
+
+        Long criticalIssueCount = 0L, majorIssueCount = 0L, minorIssueCount = 0L, trivialIssueCount = 0L;
+
+        for (Map<String, Object> severityCount : severityCounts) {
+            Long criticalCount = MapUtil.getLong(severityCount, "critical");    // 以묒슂�룄: �떖媛� 媛��닔
+            Long majorCount = MapUtil.getLong(severityCount, "major");  // 以묒슂�룄: �넂�쓬 媛��닔
+            Long minorCount = MapUtil.getLong(severityCount, "minor");  // 以묒슂�룄: 蹂댄넻 媛��닔
+            Long trivialCount = MapUtil.getLong(severityCount, "trivial");  // 以묒슂�룄: �궙�쓬 媛��닔
+
+            if (criticalCount != null) {
+                criticalIssueCount += criticalCount;
+            }
+
+            if (majorCount != null) {
+                majorIssueCount += majorCount;
+            }
+
+            if (minorCount != null) {
+                minorIssueCount += minorCount;
+            }
+
+            if (trivialCount != null) {
+                trivialIssueCount += trivialCount;
+            }
+        }
+
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+
+        Map<String, Object> results = new HashMap<>();
+        results.put("criticalIssueCount", criticalIssueCount);
+        results.put("majorIssueCount", majorIssueCount);
+        results.put("minorIssueCount", minorIssueCount);
+        results.put("trivialIssueCount", trivialIssueCount);
+        results.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+        results.put("issues", severityIssues);
+        resJsonData.put("severityIssueWidget", results);
+    }
+
+
+    //  �뿊�� �떎�슫濡쒕뱶
+    @Override
+    @Transactional
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        //  �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븯怨� 鍮꾪솢�꽦�씪 寃쎌슦 �뿊�� �떎�슫濡쒕뱶瑜� 湲덉��븳�떎.
+        ModelAndView modelAndView = this.workspaceService.checkUseExcelDownload(model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        Map<String, Object> params = new HashMap<>();
+
+        Enumeration e = request.getParameterNames();
+        while (e.hasMoreElements()) {
+            String name = (String) e.nextElement();
+            if (!org.springframework.util.StringUtils.isEmpty(request.getParameter(name))) {
+                params.put(name, request.getParameter(name));
+            }
+        }
+
+        //  �떎�슫濡쒕뱶 �쐞�젽 �쑀�삎�뿉 �삤瑜섍� �엳�쓣 寃쎌슦 �떎�슫濡쒕뱶�맂�떎. - 湲곕낯�쟻�쑝濡� Exception 諛쒖깮�븷 �븣 �궗�슜�맆 �뿊�� �삎�떇�쓣 ���옣�븳�떎.
+        ExportExcelVo excelInfo = this.excelConditionCheck.makeEmptyDownloadExcel();
+
+        String downloadWidgetType = MapUtil.getString(params, "downloadWidgetType");
+        if (downloadWidgetType == null) {
+            downloadWidgetType = "";
+            excelInfo.setFileName(this.messageAccessor.message("common.typeErrorDownloadWidget")); // �떎�슫濡쒕뱶 �쐞�젽 �쑀�삎 �삤瑜�
+        }
+
+        switch (downloadWidgetType) {
+            case "PROJECT_PROGRESS":
+                //  吏꾪뻾以묒씤 �봽濡쒖젥�듃 �쁽�솴 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+                excelInfo = this.downloadExcelProjectProgress();
+                break;
+            case "MY_ASSIGNEE_ISSUE":
+                //  �굹�뿉寃� �븷�떦�맂 �씠�뒋 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+                excelInfo = this.downloadExcelMyAssigneeIssue();
+                break;
+            case "RISK_ISSUE":
+                //  �쐞�뿕 愿�由� �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+                excelInfo = this.downloadExcelRiskIssue();
+                break;
+            case "REGISTER_ISSUE":
+                //  �궡媛� �벑濡앺븳 �씠�뒋 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+                excelInfo = this.downloadExcelRegisterIssue();
+                break;
+            case "DELAY_ISSUE":
+                //  吏��뿰 以묒씤 �씠�뒋 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+                excelInfo = this.downloadExcelDelayIssue();
+                break;
+            case "MEMBER_PROGRESS":
+                //  �봽濡쒖젥�듃 蹂� 硫ㅻ쾭 吏꾪뻾瑜� �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+                excelInfo = this.downloadExcelMemberProgress(MapUtil.getLong(params, "projectId"));
+                break;
+            case "MY_ISSUE":
+                //  �굹�쓽 �씠�뒋 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+                excelInfo = this.downloadExcelMyIssue();
+                break;
+            case "SEVERITY_ISSUE":
+                //  以묒슂�룄 蹂� �씠�뒋 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+                excelInfo = this.downloadExcelSeverityIssue(MapUtil.getLong(params, "severityId"));
+                break;
+        }
+
+        model.addAttribute(Constants.EXCEL, excelInfo);
+        return new ModelAndView(this.excelView);
+    }
+
+
+    //  吏꾪뻾以묒씤 �봽濡쒖젥�듃 �쁽�솴 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    private ExportExcelVo downloadExcelProjectProgress() {
+        Map<String, Object> resJsonData = new HashMap<>();
+        //  吏꾪뻾 以묒씤 �봽濡쒖젥�듃 �쁽�솴
+        this.findProjectProgress(resJsonData, this.makeWidgetCondition());
+
+        Map<String, Object> projectProgressWidget = (Map<String, Object>) MapUtil.getObject(resJsonData, "projectProgressWidget");
+        List<Map<String, Object>> projects = (List) MapUtil.getObject(projectProgressWidget, "projects");
+
+
+        for (Map<String, Object> project : projects) {
+            //  留ㅻ땲�� �젙蹂� 蹂��솚
+            project.put("manager", MapUtil.getString(project, "managerName") + "(" + MapUtil.getString(project, "managerEmail") + ")");
+            //  湲곌컙 �젙蹂� 蹂��솚
+            project.put("period", MapUtil.getString(project, "startDate") + " - " + MapUtil.getString(project, "endDate"));
+            //  吏꾪뻾瑜� �옄由ъ닔 �젣�븳
+            Double projectProgressPercent = MapUtil.getDouble(project, "projectProgressPercent");
+            project.put("projectProgressPercent", String.format("%.1f", projectProgressPercent) + "%");
+
+            //  �씠�뒋 �궎�슫�듃 �젙蹂� 蹂��솚
+            int closeCount = MapUtil.getInteger(project, "close");
+            int remainCount = MapUtil.getInteger(project, "remain");
+
+            try {
+                int totalCount = closeCount + remainCount;
+                project.put("issueCount", CommonUtil.getDecimalFormat(MapUtil.getInteger(project, "close")) + " / " + CommonUtil.getDecimalFormat(totalCount));
+            } catch (Exception e) {
+                log.debug("downloadExcelProjectProgress Null Exception");
+            }
+        }
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.inProgressProjectStatus")); // 吏꾪뻾 以묒씤 �봽濡쒖젥�듃 �쁽�솴
+        excelInfo.setDatas((List) MapUtil.getObject(projectProgressWidget, "projects"));
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("name", this.messageAccessor.message("common.project"), 40, ExportExcelAttrVo.ALIGN_LEFT)); // �봽濡쒖젥�듃
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("manager", this.messageAccessor.message("common.admin"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 愿�由ъ옄
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("projectProgressPercent", this.messageAccessor.message("common.progressPercent"), 6, ExportExcelAttrVo.ALIGN_CENTER)); //吏꾪뻾瑜�
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueCount", this.messageAccessor.message("common.issue"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �씠�뒋
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("teamCount", this.messageAccessor.message("common.teamMember"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // ���썝
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("period", this.messageAccessor.message("common.period"), 15, ExportExcelAttrVo.ALIGN_CENTER)); // 湲곌컙
+
+        return excelInfo;
+    }
+
+    //  �굹�뿉寃� �븷�떦�맂 �씠�뒋 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    private ExportExcelVo downloadExcelMyAssigneeIssue() {
+        WidgetCondition widgetCondition = this.makeWidgetCondition();
+        //  �굹�뿉寃� �븷�떦�맂 �씠�뒋
+        List<Map<String, Object>> assigneeIssues = Lists.newArrayList();
+        if (widgetCondition.getProjectIds().size() > 0) {
+            assigneeIssues = this.widgetMapper.findMyAssigneeIssue(this.makeWidgetCondition());
+        }
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.assignedToMeIssue")); // �굹�뿉寃� �븷�떦�맂 �씠�뒋
+        excelInfo.setDatas(assigneeIssues);
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueKey", this.messageAccessor.message("common.issueKey"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �씠�뒋 踰덊샇
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("registerDate", this.messageAccessor.message("common.registerDate"), 8, ExportExcelAttrVo.ALIGN_CENTER)); // �벑濡앹씪
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("title", this.messageAccessor.message("common.issueTitle"), 40, ExportExcelAttrVo.ALIGN_LEFT)); // �씠�뒋
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueStatusName", this.messageAccessor.message("common.status"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �긽�깭
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("completeDate", this.messageAccessor.message("common.endDate"), 15, ExportExcelAttrVo.ALIGN_CENTER)); // 醫낅즺�씪
+        return excelInfo;
+    }
+
+    //  �쐞�뿕 愿�由� �씠�뒋 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    private ExportExcelVo downloadExcelRiskIssue() {
+        WidgetCondition widgetCondition = this.makeWidgetCondition();
+        //  �쐞�뿕 愿�由� �씠�뒋
+        List<Map<String, Object>> riskIssues = Lists.newArrayList();
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            riskIssues = this.widgetMapper.findRiskIssue(this.makeWidgetCondition());
+        }
+
+        //  �씠�뒋�쓽 �떞�떦�옄 �젙蹂대�� 媛��졇�삩�떎.
+        this.setIssueUsers(riskIssues);
+
+        for (Map<String, Object> risk : riskIssues) {
+            String riskType = "";
+            String changeAssigneeType = MapUtil.getString(risk, "changeAssigneeType");
+            String changeIssueStatusType = MapUtil.getString(risk, "changeIssueStatusType");
+
+            if (changeAssigneeType != null) {
+                if (changeAssigneeType.equals("1")) {
+                    riskType = this.messageAccessor.message("common.reversalAssigneeUpdate"); // 鍮덈쾲�븳 �떞�떦�옄 蹂�寃�
+                }
+            }
+
+            if (changeIssueStatusType != null) {
+                if (changeIssueStatusType.equals("1")) {
+                    riskType = this.messageAccessor.message("common.reversalStatusUpdate"); // 踰덈났�릺�뒗 �긽�깭 蹂�寃�
+                }
+            }
+
+            if (changeAssigneeType != null && changeIssueStatusType != null) {
+                if (changeAssigneeType.equals("1") && changeIssueStatusType.equals("1")) {
+                    riskType = this.messageAccessor.message("common.reversalAssigneeUpdate") + '\n' + this.messageAccessor.message("common.reversalStatusUpdate"); // 鍮덈쾲�븳 �떞�떦�옄 蹂�寃�, 踰덈났�릺�뒗 �긽�깭 蹂�寃�
+                }
+            }
+
+            risk.put("riskType", riskType);
+
+            List<Map<String, Object>> issueUsers = (List<Map<String, Object>>) MapUtil.getObject(risk, "issueUsers");
+
+            StringBuilder stringBuilder = new StringBuilder();
+
+            for (Map<String, Object> issueUser : issueUsers) {
+                if (!StringUtils.isEmpty(stringBuilder.toString())) {
+                    stringBuilder.append("\n");
+                }
+
+                stringBuilder.append(MapUtil.getString(issueUser, "name"));
+                stringBuilder.append("(");
+                stringBuilder.append(MapUtil.getString(issueUser, "account"));
+                stringBuilder.append(") ");
+            }
+
+            risk.put("issueUsers", stringBuilder.toString());
+        }
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.managementRisk")); // �쐞�뿕 愿�由�
+        excelInfo.setDatas(riskIssues);
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueKey", this.messageAccessor.message("common.issueKey"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �씠�뒋 踰덊샇
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("riskType", this.messageAccessor.message("common.division"), 15, ExportExcelAttrVo.ALIGN_CENTER)); // 援щ텇
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("title", this.messageAccessor.message("common.issueTitle"), 40, ExportExcelAttrVo.ALIGN_LEFT)); // �씠�뒋
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueStatusName", this.messageAccessor.message("common.status"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �긽�깭
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueUsers", this.messageAccessor.message("common.assignee"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // �떞�떦�옄
+        return excelInfo;
+    }
+
+    //  �궡媛� �벑濡앺븳 �씠�뒋 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    private ExportExcelVo downloadExcelRegisterIssue() {
+        WidgetCondition widgetCondition = this.makeWidgetCondition();
+        List<Map<String, Object>> registerIssues = Lists.newArrayList();
+        Long totalCount = 0L;
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            totalCount = this.widgetMapper.countRegisterIssue(widgetCondition);
+        }
+
+        widgetCondition.setPage(0);
+        widgetCondition.setPageSize(totalCount.intValue());
+
+        //  �궡媛� �벑濡앺븳 �씠�뒋
+        if (widgetCondition.getProjectIds().size() > 0) {
+            registerIssues = this.widgetMapper.findRegisterIssue(widgetCondition);
+        }
+
+        //  湲곌컙 �젙蹂대�� �뀑�똿�븳�떎.
+        this.setPeriod(registerIssues);
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.registeredByMeIssue")); // �궡媛� �벑濡앺븳 �씠�뒋 �쁽�솴
+        excelInfo.setDatas(registerIssues);
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueKey", this.messageAccessor.message("common.issueKey"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �씠�뒋 踰덊샇
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueStatusName", this.messageAccessor.message("common.status"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �긽�깭
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("title", this.messageAccessor.message("common.issueTitle"), 40, ExportExcelAttrVo.ALIGN_LEFT)); // �씠�뒋
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("period", this.messageAccessor.message("common.period"), 15, ExportExcelAttrVo.ALIGN_CENTER)); // 湲곌컙
+        return excelInfo;
+    }
+
+    //  湲곌컙 �젙蹂대�� �뀑�똿�븳�떎.
+    private void setPeriod(List<Map<String, Object>> issues) {
+        for (Map<String, Object> issue : issues) {
+            if (MapUtil.getString(issue, "startDate") == null) {
+                issue.put("period", " - ");
+            } else {
+                issue.put("period", MapUtil.getString(issue, "startDate") + " - " + MapUtil.getString(issue, "completeDate"));
+            }
+        }
+    }
+
+    //  吏��뿰 以묒씤 �씠�뒋 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    private ExportExcelVo downloadExcelDelayIssue() {
+        WidgetCondition widgetCondition = this.makeWidgetCondition();
+        List<Map<String, Object>> delayIssues = Lists.newArrayList();
+        Long totalCount = 0L;
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            totalCount = this.widgetMapper.countDelayIssue(widgetCondition);
+        }
+
+        widgetCondition.setPage(0);
+        widgetCondition.setPageSize(totalCount.intValue());
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            delayIssues = this.widgetMapper.findDelayIssue(widgetCondition);
+        }
+
+        //  �삤�뒛 �궇吏쒕�� 湲곗��쑝濡� 吏��뿰�씪�쓣 援ы븳�떎.
+        this.setDelayDay(delayIssues);
+
+        //  湲곌컙 �젙蹂대�� �뀑�똿�븳�떎.
+        this.setPeriod(delayIssues);
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.delayingIssue")); // 吏��뿰 以묒씤 �씠�뒋
+        excelInfo.setDatas(delayIssues);
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueKey", this.messageAccessor.message("common.issueKey"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �씠�뒋 踰덊샇
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("delayDay", this.messageAccessor.message("common.delayDate"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // 吏��뿰�씪
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("title", this.messageAccessor.message("common.issueTitle"), 40, ExportExcelAttrVo.ALIGN_LEFT)); // �씠�뒋
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueStatusName", this.messageAccessor.message("common.status"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �긽�깭
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("period", this.messageAccessor.message("common.period"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 湲곌컙
+        return excelInfo;
+    }
+
+    //  �봽濡쒖젥�듃蹂� 硫ㅻ쾭 吏꾪뻾瑜� �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    private ExportExcelVo downloadExcelMemberProgress(Long projectId) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        WidgetCondition widgetCondition = this.makeWidgetCondition();
+        widgetCondition.setProjectIds(Lists.newArrayList(projectId));
+
+        //  �봽濡쒖젥�듃 蹂� 硫ㅻ쾭 吏꾪뻾瑜� �젙蹂대�� 議고쉶�븳�떎.
+        this.findMemberProgress(resJsonData, widgetCondition, false);
+
+        Map<String, Object> memberProgressWidget = (Map<String, Object>) MapUtil.getObject(resJsonData, "memberProgressWidget");
+        List<Map<String, Object>> members = (List<Map<String, Object>>) MapUtil.getObject(memberProgressWidget, "members");
+
+        for (Map<String, Object> member : members) {
+            StringBuilder stringBuilder = new StringBuilder();
+            stringBuilder.append(MapUtil.getString(member, "name"));
+            stringBuilder.append("(");
+            stringBuilder.append(MapUtil.getString(member, "account"));
+            stringBuilder.append(") ");
+            member.put("byName", stringBuilder.toString());
+            //  吏꾪뻾瑜� �옄由ъ닔 �젣�븳
+            Double projectProgressPercent = MapUtil.getDouble(member, "projectProgressPercent");
+            member.put("projectProgressPercent", String.format("%.1f", projectProgressPercent) + "%");
+
+            int completeCount = MapUtil.getInteger(member, "completeCount");
+            int remainCount = MapUtil.getInteger(member, "remainCount");
+            int totalCount = completeCount + remainCount;
+            //  �떞�떦 �씠�뒋 移댁슫�듃
+            member.put("issueCount", CommonUtil.getDecimalFormat(completeCount) + " / " + CommonUtil.getDecimalFormat(totalCount));
+        }
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.progressByMember")); // 硫ㅻ쾭蹂� 吏꾪뻾瑜�
+        excelInfo.setDatas(members);
+
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("byName", this.messageAccessor.message("common.teamMember"), 15, ExportExcelAttrVo.ALIGN_LEFT)); // ���썝
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("projectProgressPercent", this.messageAccessor.message("common.progressPercent"), 6, ExportExcelAttrVo.ALIGN_LEFT)); // 吏꾪뻾瑜�
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueCount", this.messageAccessor.message("common.assignedIssue"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // �떞�떦 �씠�뒋
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("delayCount", this.messageAccessor.message("common.delaying"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // 吏��뿰以�
+        return excelInfo;
+    }
+
+    //  �굹�쓽 �씠�뒋 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    private ExportExcelVo downloadExcelMyIssue() {
+        Map<String, Object> resJsonData = new HashMap<>();
+        //  �굹�쓽 �씠�뒋 �쁽�솴 �젙蹂대�� 議고쉶�븳�떎.
+        this.findMyIssueDetail(resJsonData, this.makeWidgetCondition());
+
+        Map<String, Object> myIssueWidget = (Map<String, Object>) MapUtil.getObject(resJsonData, "myIssueWidget");
+        List<Map<String, Object>> issues = (List<Map<String, Object>>) MapUtil.getObject(myIssueWidget, "issues");
+
+        List<Map<String, Object>> newIssues = Lists.newArrayList();
+
+        for (Map<String, Object> issue : issues) {
+            Map<String, Object> registerMaps = new HashMap<>();
+            Map<String, Object> assigneeMaps = new HashMap<>();
+
+            registerMaps.put("name", MapUtil.getString(issue, "name"));
+            registerMaps.put("division", this.messageAccessor.message("common.registration")); // �벑濡�
+            registerMaps.put("totalRegisterIssueCount", CommonUtil.getDecimalFormat(MapUtil.getInteger(issue, "totalRegisterIssueCount")));
+            registerMaps.put("registerCompleteIssueCount", CommonUtil.getDecimalFormat(MapUtil.getInteger(issue, "registerCompleteIssueCount")));
+            Double registerIssueProgressPercent = MapUtil.getDouble(issue, "registerIssueProgressPercent");
+            registerMaps.put("registerIssueProgressPercent", String.format("%.1f", registerIssueProgressPercent) + "%");
+            registerMaps.put("registerRemainIssueCount", CommonUtil.getDecimalFormat(MapUtil.getInteger(issue, "registerRemainIssueCount")));
+
+            assigneeMaps.put("division", this.messageAccessor.message("common.assigned")); // �떞�떦
+            assigneeMaps.put("totalRegisterIssueCount", CommonUtil.getDecimalFormat(MapUtil.getInteger(issue, "totalAssigneeIssueCount")));
+            assigneeMaps.put("registerCompleteIssueCount", CommonUtil.getDecimalFormat(MapUtil.getInteger(issue, "assigneeCompleteIssueCount")));
+            Double assigneeIssueProgressPercent = MapUtil.getDouble(issue, "assigneeIssueProgressPercent");
+            assigneeMaps.put("registerIssueProgressPercent", String.format("%.1f", assigneeIssueProgressPercent) + "%");
+            assigneeMaps.put("registerRemainIssueCount", CommonUtil.getDecimalFormat(MapUtil.getInteger(issue, "assigneeRemainIssueCount")));
+
+            newIssues.add(registerMaps);
+            newIssues.add(assigneeMaps);
+        }
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.myIssueStatus")); // �굹�쓽 �씠�뒋 �쁽�솴
+        excelInfo.setDatas(newIssues);
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("name", this.messageAccessor.message("common.project"), 15, ExportExcelAttrVo.ALIGN_CENTER, 1)); // �봽濡쒖젥�듃
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("division", this.messageAccessor.message("common.division"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // 援щ텇
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("totalRegisterIssueCount", this.messageAccessor.message("common.issue"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // �씠�뒋
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("registerCompleteIssueCount", this.messageAccessor.message("common.complete"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �셿猷�
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("registerIssueProgressPercent", this.messageAccessor.message("common.progressPercent"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // 吏꾪뻾瑜�
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("registerRemainIssueCount", this.messageAccessor.message("common.remainIssue"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // �옍�뿬 �씠�뒋
+
+        return excelInfo;
+    }
+
+    //  以묒슂�룄 蹂� �씠�뒋 �젙蹂대�� �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    private ExportExcelVo downloadExcelSeverityIssue(Long severityId) {
+
+        WidgetCondition widgetCondition = this.makeWidgetCondition();
+
+        if (widgetCondition.getSeverityId() == null) {
+            widgetCondition.setSeverityId(severityId);
+        }
+
+        List<Map<String, Object>> severityIssue = Lists.newArrayList();
+
+        if (widgetCondition.getProjectIds().size() > 0) {
+            severityIssue = this.widgetMapper.findSeverityIssues(widgetCondition);
+        }
+
+        //湲곌컙 �젙蹂� �뀑�똿
+        this.setPeriod(severityIssue);
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.severityIssue")); // 以묒슂�룄 蹂� �씠�뒋�쁽�솴
+        excelInfo.setDatas(severityIssue);
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueKey", this.messageAccessor.message("common.issueKey"), 8, ExportExcelAttrVo.ALIGN_CENTER));    //�씠�뒋 踰덊샇
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("severityName", this.messageAccessor.message("common.importance"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // 以묒슂�룄
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("title", this.messageAccessor.message("common.issueTitle"), 40, ExportExcelAttrVo.ALIGN_CENTER)); // �씠�뒋�젣紐�
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("issueStatusName", this.messageAccessor.message("common.status"), 6, ExportExcelAttrVo.ALIGN_CENTER));  //�씠�뒋 �긽�깭
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("period", this.messageAccessor.message("common.period"), 15, ExportExcelAttrVo.ALIGN_CENTER)); // 湲곌컙
+
+        return excelInfo;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/WorkflowServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/WorkflowServiceImpl.java
new file mode 100644
index 0000000..3ccc637
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/WorkflowServiceImpl.java
@@ -0,0 +1,343 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.common.ExcelConditionCheck;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.WorkflowMapper;
+import kr.wisestone.owl.repository.WorkflowRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.*;
+import kr.wisestone.owl.web.condition.WorkflowCondition;
+import kr.wisestone.owl.web.form.WorkflowForm;
+import kr.wisestone.owl.web.view.ExcelView;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class WorkflowServiceImpl extends AbstractServiceImpl<Workflow, Long, JpaRepository<Workflow, Long>>
+        implements WorkflowService {
+
+    private static final Logger log = LoggerFactory.getLogger(WorkflowServiceImpl.class);
+
+    @Autowired
+    private WorkflowRepository workflowRepository;
+
+    @Autowired
+    private IssueStatusService issueStatusService;
+
+    @Autowired
+    private WorkflowTransitionService workflowTransitionService;
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    @Autowired
+    private IssueService issueService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private WorkflowMapper workflowMapper;
+
+    @Autowired
+    private ExcelView excelView;
+
+    @Autowired
+    private ExcelConditionCheck excelConditionCheck;
+
+    @Override
+    protected JpaRepository<Workflow, Long> getRepository() {
+        return this.workflowRepository;
+    }
+
+    //  湲곕낯 �봽濡쒖젥�듃�뿉�꽌 �궗�슜�릺�뒗 湲곕낯 �썙�겕�뵆濡쒖슦
+    @Override
+    @Transactional
+    public void addDefaultWorkflow(Workspace workspace, List<ProjectType> projectTypes) {
+
+        for (ProjectType projectType : projectTypes) {
+            List<IssueStatus> issueStatuses = Lists.newArrayList();
+            //  �썙�겕�뵆濡쒖슦 �깮�꽦
+            Workflow workflow = new Workflow();
+            workflow.setWorkspace(workspace);
+            workflow.setProjectType(projectType);   //  project �깮�꽦�떆 project type �뿉 留욌뒗 �썙�겕�뵆濡쒖슦/�씠�뒋 �쑀�삎�쓣 �옄�룞 �깮�꽦�븷 �닔 �엳寃� �빐以��떎. - �봽濡쒖젥�듃 �뀥�뵆由� �쑀�삎 蹂꾨줈 �빐�떦 project type �뿉 留욌뒗 �썙�겕�뵆濡쒖슦濡� �깮�꽦�빐以��떎.
+
+            switch(projectType) {
+                case BTS_PROJECT:
+                    workflow.setName(this.messageAccessor.message("common.btsWorkflow")); // BTS �썙�겕�뵆濡쒖슦
+                    workflow.setDescription(this.messageAccessor.message("common.usedToManageThisWorkflow")); // 踰꾧렇瑜� 愿�由ы븯�뒗�뜲 �궗�슜�븯�뒗 �썙�겕�뵆濡쒖슦 �엯�땲�떎.
+                    issueStatuses = this.issueStatusService.findByWorkspaceId(workspace.getId());
+                    break;
+                case RMS_PROJECT:
+                    workflow.setName(this.messageAccessor.message("common.rmsWorkflow")); // RMS �썙�겕�뵆濡쒖슦
+                    workflow.setDescription(this.messageAccessor.message("common.userToManageRequirementsThisWorkflow")); // �슂援� �궗�빆�쓣 愿�由ы븯�뒗�뜲 �궗�슜�븯�뒗 �썙�겕�뵆濡쒖슦 �엯�땲�떎.
+                    issueStatuses = this.issueStatusService.findByWorkspaceId(workspace.getId());
+                    break;
+                case TCM_PROJECT:
+                    workflow.setName(this.messageAccessor.message("common.tcmWorkflow")); // TCM �썙�겕�뵆濡쒖슦
+                    workflow.setDescription(this.messageAccessor.message("common.userToManageTestCasesThisWorkflow")); // �뀒�뒪�듃 耳��씠�뒪瑜� 愿�由ы븯�뒗�뜲 �궗�슜�븯�뒗 �썙�겕�뵆濡쒖슦 �엯�땲�떎.
+                    issueStatuses = this.issueStatusService.findByWorkspaceId(workspace.getId());
+                    break;
+            }
+
+            //  �썙�겕�뒪�럹�씠�뒪�뿉 湲곕낯 �젣怨듬릺�뒗 �씠�뒋 �긽�깭媛� �뾾�쓣 寃쎌슦�뿉�뒗 �삤瑜�
+            if (issueStatuses.isEmpty()) {
+                throw new OwlRuntimeException(
+                        this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_NOT_EXIST));
+            }
+
+            this.workflowRepository.saveAndFlush(workflow);
+            //  �썙�겕�뵆濡쒖슦 �쟾�씠 �뿰寃�.
+            this.workflowTransitionService.addDefaultWorkflowTransition(workflow, issueStatuses, projectType);
+        }
+    }
+
+    //  �썙�겕�뵆濡쒖슦瑜� �깮�꽦�븳�떎.
+    @Override
+    @Transactional
+    public Workflow addWorkflow(WorkflowForm workflowForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        //  �씠由� �쑀�슚�꽦 泥댄겕
+        this.verifyName(workflowForm.getName(), null);
+        Workflow workflow = ConvertUtil.copyProperties(workflowForm, Workflow.class, "projectType");
+        Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        workflow.setWorkspace(workspace);
+
+        this.workflowRepository.saveAndFlush(workflow);
+
+        this.workflowTransitionService.modify(workflow, workflowForm.getIssueStatusVos());
+
+        return workflow;
+    }
+
+    //  �씠由� �쑀�슚�꽦 泥댄겕
+    private void verifyName(String name, Long id) {
+        if (StringUtils.isEmpty(name)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_NOT_NAME));
+        }
+
+        if (name.length() > 20) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_NAME_MAX_LENGTH_OUT));
+        }
+
+        Workflow workflow;
+        Long workspaceId = this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId();
+
+        if (id == null) {
+            workflow = this.workflowRepository.findByNameAndWorkspaceId(name, workspaceId);
+        }
+        else {
+            workflow = this.workflowRepository.findByNameAndWorkspaceIdAndIdNot(name, workspaceId, id);
+        }
+
+        if (workflow != null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_USED_NAME));
+        }
+    }
+
+    //  �썙�겕�뵆濡쒖슦 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<WorkflowVo> findWorkflow(Map<String, Object> resJsonData,
+                                  WorkflowCondition condition, Pageable pageable) {
+
+        condition.setPage(pageable.getPageNumber() * pageable.getPageSize());
+        condition.setPageSize(pageable.getPageSize());
+        condition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        List<Map<String, Object>> results = this.workflowMapper.find(condition);
+        Long totalCount = this.workflowMapper.count(condition);
+        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
+        List<WorkflowVo> workflowVos = ConvertUtil.convertListToListClass(results, WorkflowVo.class);
+        //  �썙�겕�뵆濡쒖슦瑜� �궗�슜�븯�뒗 �씠�뒋 �쑀�삎 �젙蹂대�� 異붽��븳�떎.
+        this.setIssueTypeVos(workflowVos);
+
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
+                totalPage, totalCount));
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, workflowVos);
+
+        return workflowVos;
+    }
+
+    //  �썙�겕�뵆濡쒖슦瑜� �궗�슜�븯�뒗 �씠�뒋 �쑀�삎 �젙蹂대�� 異붽��븳�떎.
+    private void setIssueTypeVos(List<WorkflowVo> workflowVos) {
+        for (WorkflowVo workflowVo : workflowVos) {
+            Workflow workflow = this.getWorkflow(workflowVo.getId());
+            List<IssueTypeVo> issueTypeVos = ConvertUtil.convertObjectsToClasses(workflow.getIssueTypes(), IssueTypeVo.class);
+            workflowVo.setIssueTypeVos(issueTypeVos);
+        }
+    }
+
+    //  �썙�겕�뵆濡쒖슦 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void detailWorkflow(Map<String, Object> resJsonData, WorkflowCondition workflowCondition) {
+        WorkflowVo workflowVo = new WorkflowVo();
+
+        if (workflowCondition.getId() != null) {
+            Workflow workflow = this.getWorkflow(workflowCondition.getId());
+            workflowVo = ConvertUtil.copyProperties(workflow, WorkflowVo.class);
+
+            switch (workflowCondition.getDeep()) {
+                case "01" : //  �뿰愿��맂 �씠�뒋 �긽�깭�� �쟾�씠�꽑 �젙蹂대�� 媛��졇�삩�떎.
+                    workflowVo.setIssueStatusVos(this.issueStatusService.findByWorkflowId(workflowCondition.getId()));
+                    break;
+            }
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, workflowVo);
+    }
+
+    //  �썙�겕�뵆濡쒖슦瑜� �닔�젙�븳�떎.
+    @Override
+    @Transactional
+    public Workflow modifyWorkflow(WorkflowForm workflowForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        Workflow workflow = this.getWorkflow(workflowForm.getId());
+        //  �씠由� �쑀�슚�꽦 泥댄겕
+        this.verifyName(workflowForm.getName(), workflowForm.getId());
+        workflow.setName(workflowForm.getName());
+
+        this.workflowTransitionService.modify(workflow, workflowForm.getIssueStatusVos());
+        //  �썙�겕�뵆濡쒖슦媛� 蹂�寃쎈릺�뿀�뒗吏� �솗�씤�븯怨� 蹂�寃쎈릺�뿀�쓣 寃쎌슦 �씠�뒋 �긽�깭媛� �뾾�뒗 �씠�뒋�뒗 '�깮�꽦' �씤 �씠�뒋 �긽�깭濡� �씠�룞�븳�떎.
+        this.checkWorkflowChange(workflow);
+
+        return workflow;
+    }
+
+    //  �썙�겕�뵆濡쒖슦媛� 蹂�寃쎈릺�뿀�뒗吏� �솗�씤�븯怨� 蹂�寃쎈릺�뿀�쓣 寃쎌슦 �씠�뒋 �긽�깭媛� �뾾�뒗 �씠�뒋�뒗 '�깮�꽦' �씤 �씠�뒋 �긽�깭濡� �씠�룞�븳�떎.
+    private void checkWorkflowChange(Workflow workflow) {
+        for (IssueType issueType : workflow.getIssueTypes()) {
+            this.issueService.changeWorkflows(workflow, issueType);
+        }
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public Workflow getWorkflow(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_NOT_EXIST));
+        }
+
+        Workflow workflow = this.findOne(id);
+
+        if (workflow == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_NOT_EXIST));
+        }
+
+        return workflow;
+    }
+
+    //  �썙�겕�뵆濡쒖슦瑜� �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void removeWorkflows(WorkflowForm workflowForm) {
+        //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+        this.workspaceService.checkUseWorkspace();
+
+        if (workflowForm.getRemoveIds().size() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_REMOVE_NOT_SELECT));
+        }
+
+        for (Long projectId : workflowForm.getRemoveIds()) {
+            this.removeWorkflow(projectId);
+        }
+
+        this.workflowRepository.flush();
+    }
+
+    private void removeWorkflow(Long workflowId) {
+        Workflow workflow = this.getWorkflow(workflowId);
+        //  �썙�겕�뵆濡쒖슦瑜� �씠�뒋 ���엯�뿉�꽌 �궗�슜�븯怨� �엳�뒗吏� �솗�씤
+        this.checkIssueTypeWorkflow(workflow);
+        this.workflowRepository.delete(workflow);
+    }
+
+    //  �썙�겕�뵆濡쒖슦瑜� �씠�뒋 ���엯�뿉�꽌 �궗�슜�븯怨� �엳�뒗吏� �솗�씤
+    private void checkIssueTypeWorkflow(Workflow workflow) {
+        if (workflow.getIssueTypes().size() > 0) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_USED_ISSUE_TYPE));
+        }
+    }
+
+    //  �썙�겕�뒪�럹�씠�뒪�쓽 �빐�떦 �봽濡쒖젥�듃 �쑀�삎�뿉�꽌 �궗�슜�릺�뒗 �썙�겕�뵆濡쒖슦瑜� 李얠븘�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Workflow findByWorkspaceIdAndProjectType(Long workspaceId, ProjectType projectType) {
+        Workflow workflow = this.workflowRepository.findByWorkspaceIdAndProjectType(workspaceId, projectType);
+
+        if (workflow == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_NOT_EXIST));
+        }
+
+        return workflow;
+    }
+
+    //  �썙�겕�뒪�럹�씠�뒪�뿉 �엳�뒗 紐⑤뱺 �썙�겕�뵆濡쒖슦 紐⑸줉�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<Workflow> findByWorkspaceId(Long workspaceId) {
+        return this.workflowRepository.findByWorkspaceId(workspaceId);
+    }
+
+    //  �썙�겕�뵆濡쒖슦 紐⑸줉�쓣 �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
+    @Override
+    @Transactional
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        //  �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븯怨� 鍮꾪솢�꽦�씪 寃쎌슦 �뿊�� �떎�슫濡쒕뱶瑜� 湲덉��븳�떎.
+        ModelAndView modelAndView = this.workspaceService.checkUseExcelDownload(model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        Map<String, Object> conditions = new HashMap<>();
+        //  �뿊�� �떎�슫濡쒕뱶�뿉 �븘�슂�븳 寃��깋 議곌굔 �젙蹂대�� 異붿텧�븯怨� 寃��깋 議곌굔 異붿텧�뿉 �삤瑜섍� 諛쒖깮�븯硫� 寃쎄퀬瑜� �몴�떆�빐以��떎.
+        modelAndView = this.excelConditionCheck.checkCondition(conditions, request, model);
+        if (modelAndView != null) {
+            return modelAndView;
+        }
+
+        WorkflowCondition workflowCondition = WorkflowCondition.make(conditions);
+        workflowCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        List<Map<String, Object>> results = this.workflowMapper.find(workflowCondition);
+        List<WorkflowVo> workflowVos = ConvertUtil.convertListToListClass(results, WorkflowVo.class);
+
+        ExportExcelVo excelInfo = new ExportExcelVo();
+        excelInfo.setFileName(this.messageAccessor.message("common.workflowList")); // �썙�겕�뵆濡쒖슦 紐⑸줉
+        excelInfo.addAttrInfos(new ExportExcelAttrVo("name", this.messageAccessor.message("common.workflow"), 15, ExportExcelAttrVo.ALIGN_LEFT)); // �썙�겕�뵆濡쒖슦
+        //  �뿊���뿉 �꽔�쓣 �뜲�씠�꽣
+        excelInfo.setDatas(workflowVos);
+        model.addAttribute(Constants.EXCEL, excelInfo);
+        return new ModelAndView(this.excelView);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/WorkflowStatusServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/WorkflowStatusServiceImpl.java
new file mode 100644
index 0000000..9fe2d8e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/WorkflowStatusServiceImpl.java
@@ -0,0 +1,209 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.Project;
+import kr.wisestone.owl.domain.WorkflowStatus;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.WorkflowStatusRepository;
+import kr.wisestone.owl.service.IssueService;
+import kr.wisestone.owl.service.ProjectService;
+import kr.wisestone.owl.service.WorkflowStatusService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.IssueVo;
+import kr.wisestone.owl.vo.UserVo;
+import kr.wisestone.owl.vo.WorkflowStatusVo;
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.WorkflowStatusCondition;
+//import kr.wisestone.owl.web.form.WorkflowStatusForm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-03.
+ */
+@Service
+public class WorkflowStatusServiceImpl extends AbstractServiceImpl<WorkflowStatus, Long, JpaRepository<WorkflowStatus, Long>> implements WorkflowStatusService {
+
+    private static final Logger log = LoggerFactory.getLogger(WorkflowStatusServiceImpl.class);
+
+    @Autowired
+    private WorkflowStatusRepository workflowStatusRepository;
+
+    @Autowired
+    private IssueService taskService;
+
+    @Autowired
+    private ProjectService projectService;
+
+    @Override
+    protected JpaRepository<WorkflowStatus, Long> getRepository() {
+        return this.workflowStatusRepository;
+    }
+
+    @Override
+    @Transactional
+
+    public List<WorkflowStatus> addDefaultWorkflowStatus(Project project, ProjectType projectType) {
+        List<WorkflowStatus> workflowStatuses = Lists.newArrayList();
+        switch(projectType) {
+            case RMS_PROJECT:
+                workflowStatuses.add(new WorkflowStatus(project, "�븷 �씪", Boolean.TRUE, Boolean.FALSE, "#047bf8", 0L, 0L));
+                workflowStatuses.add(new WorkflowStatus(project, "吏꾪뻾 以�", Boolean.FALSE, Boolean.FALSE, "#e65252", 50L, 1L));
+                workflowStatuses.add(new WorkflowStatus(project, "�셿猷�", Boolean.FALSE, Boolean.TRUE, "#5bc0de", 100L, 2L));
+                workflowStatuses.add(new WorkflowStatus(project, "蹂대쪟", Boolean.FALSE, Boolean.FALSE, "#5eb314", 0L, 3L));
+                workflowStatuses.add(new WorkflowStatus(project, "痍⑥냼", Boolean.FALSE, Boolean.FALSE, "#b17247", 0L, 4L));
+                break;
+
+            case BTS_PROJECT:
+                workflowStatuses.add(new WorkflowStatus(project, "�삤�뵂", Boolean.TRUE, Boolean.FALSE, "#047bf8", 0L, 0L));
+                workflowStatuses.add(new WorkflowStatus(project, "�븷�떦", Boolean.FALSE, Boolean.FALSE, "#e65252", 20L, 1L));
+                workflowStatuses.add(new WorkflowStatus(project, "�빐寃�", Boolean.FALSE, Boolean.FALSE, "#5bc0de", 50L, 2L));
+                workflowStatuses.add(new WorkflowStatus(project, "�솗�씤", Boolean.FALSE, Boolean.FALSE, "#5eb314", 70L, 3L));
+                workflowStatuses.add(new WorkflowStatus(project, "醫낅즺", Boolean.FALSE, Boolean.TRUE, "#b17247", 100L, 4L));
+                break;
+
+            case TCM_PROJECT :
+                workflowStatuses.add(new WorkflowStatus(project, "��湲�", Boolean.TRUE, Boolean.FALSE, "#047bf8", 0L, 0L));
+                workflowStatuses.add(new WorkflowStatus(project, "吏꾪뻾 以�", Boolean.FALSE, Boolean.FALSE, "#e65252", 20L, 1L));
+                workflowStatuses.add(new WorkflowStatus(project, "寃��닔", Boolean.FALSE, Boolean.FALSE, "#5bc0de", 50L, 2L));
+                workflowStatuses.add(new WorkflowStatus(project, "�셿猷�", Boolean.FALSE, Boolean.TRUE, "#b17247", 100L, 4L));
+                workflowStatuses.add(new WorkflowStatus(project, "痍⑥냼", Boolean.FALSE, Boolean.FALSE, "#6156c5", 0L, 5L));
+                break;
+        }
+
+        return this.workflowStatusRepository.saveAll(workflowStatuses);
+    }
+
+
+    // �썙�겕�뵆濡쒖슦 �쁽�옱 �긽�깭 李얘린
+    @Override
+    @Transactional(readOnly = true)
+    public List<WorkflowStatusVo> findWorkflowStatus(Map<String, Object> resJsonData,
+                                                     WorkflowStatusCondition condition) {
+
+        List<WorkflowStatus> workflowStatuses = this.workflowStatusRepository.findByProjectId(condition.getProjectId());
+
+        List<WorkflowStatusVo> workflowStatusVos = Lists.newArrayList();
+
+        IssueCondition taskCondition = new IssueCondition();
+
+        for (WorkflowStatus workflowStatus : workflowStatuses) {
+            taskCondition.addStatusId(workflowStatus.getId());
+        }
+
+        Map<String, Object> tasks = this.taskService.findTask(taskCondition);
+
+        for (WorkflowStatus workflowStatus : workflowStatuses) {
+            WorkflowStatusVo workflowStatusVo = ConvertUtil.copyProperties(workflowStatus, WorkflowStatusVo.class);
+            List<IssueVo> taskVos = (List<IssueVo>)tasks.get(workflowStatus.getId().toString());
+            workflowStatusVo.setTaskVos(taskVos);
+            workflowStatusVos.add(workflowStatusVo);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, workflowStatusVos);
+
+        return workflowStatusVos;
+    }
+
+//    @Override
+//    @Transactional
+//    public WorkflowStatus addWorkflowStatus(WorkflowStatusForm workflowStatusForm) {
+//        WorkflowStatus workflowStatus = ConvertUtil.copyProperties(workflowStatusForm, WorkflowStatus.class);
+//        Project project = this.projectService.getProject(workflowStatusForm.getProjectId());
+//
+//        //  �씠由� �쑀�슚�꽦 寃��궗
+//        this.verifyName(project.getId(), null, workflowStatus.getName());
+//        //  TODO - 吏꾪뻾瑜� (���떆蹂대뱶 �궗�슜�븯寃� �맆 �븣 �븘�슂)
+//        workflowStatus.setProgress(0L);
+//        workflowStatus.setProject(project);
+//
+//        this.workflowStatusRepository.saveAndFlush(workflowStatus);
+//        //  �쐞移� �젙�쓽
+//        this.positionRegulate(project, workflowStatus);
+//
+//        return workflowStatus;
+//    }
+//
+//    //  �씠由� �쑀�슚�꽦 寃��궗
+//    private void verifyName(Long projectId, Long id, String name) {
+//        if (StringUtils.isEmpty(name)) {
+//            throw new OwlRuntimeException(
+//                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_STATUS_NOT_NAME));
+//        }
+//
+//        WorkflowStatus workflowStatus;
+//
+//        if (id != null) {
+//            workflowStatus = this.workflowStatusRepository.findByIdNotAndNameAndProjectId(id, name, projectId);
+//        }
+//        else {
+//            workflowStatus = this.workflowStatusRepository.findByNameAndProjectId(name, projectId);
+//        }
+//
+//        if (workflowStatus != null) {
+//            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.WORKFLOW_STATUS_USED_NAME));
+//        }
+//    }
+//
+//    //  �쐞移� �젙�쓽
+//    private void positionRegulate(Project project, WorkflowStatus workflowStatus) {
+//        Long maxPosition = this.workflowStatusRepository.findMaxPosition(project.getId());
+//        maxPosition = maxPosition + 1;
+//        workflowStatus.setPosition(maxPosition);
+//
+//        this.workflowStatusRepository.saveAndFlush(workflowStatus);
+//    }
+//
+//    //  �썙�겕�뵆濡쒖슦 �긽�깭 �긽�꽭 議고쉶
+//    @Override
+//    @Transactional
+//    public WorkflowStatusVo detailWorkflowStatus(Map<String, Object> resJsonData, WorkflowStatusForm workflowStatusForm) {
+//        WorkflowStatusVo workflowStatusVo;
+//
+//        if (workflowStatusForm.getId() != null) {
+//            workflowStatusVo = ConvertUtil.copyProperties(this.workflowStatusRepository.findOne(workflowStatusForm.getId()), WorkflowStatusVo.class);
+//        }
+//        else {
+//            workflowStatusVo = new WorkflowStatusVo();
+//        }
+//
+//        resJsonData.put(Constants.RES_KEY_CONTENTS, workflowStatusVo);
+//
+//        return workflowStatusVo;
+//    }
+//
+//    //  �썙�겕�뵆濡쒖슦 �긽�깭 �젙蹂대�� �닔�젙�븳�떎.
+//    @Override
+//    @Transactional
+//    public WorkflowStatus modifyWorkflowStatus(WorkflowStatusForm workflowStatusForm) {
+//        WorkflowStatus workflowStatus = this.workflowStatusRepository.findOne(workflowStatusForm.getId());
+//
+//        //  �씠由� �쑀�슚�꽦 泥댄겕
+//        this.verifyName(workflowStatus.getProject().getId(), workflowStatusForm.getId(), workflowStatusForm.getName());
+//        //  �깋�긽 泥댄겕
+//        this.verifyColor(workflowStatusForm.getColor());
+//
+//        ConvertUtil.copyProperties(workflowStatusForm, workflowStatus, "id");
+//
+//        return this.workflowStatusRepository.saveAndFlush(workflowStatus);
+//    }
+//
+//    //  �깋�긽 泥댄겕
+//    private void verifyColor(String color) {
+//        if (org.apache.commons.lang.StringUtils.isEmpty(color)) {
+//            throw new OwlRuntimeException(
+//                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_STATUS_NOT_COLOR));
+//        }
+//    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/WorkflowTransitionServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/WorkflowTransitionServiceImpl.java
new file mode 100644
index 0000000..c2271c6
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/WorkflowTransitionServiceImpl.java
@@ -0,0 +1,316 @@
+package kr.wisestone.owl.service.impl;
+
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.IssueStatus;
+import kr.wisestone.owl.domain.Workflow;
+import kr.wisestone.owl.domain.WorkflowTransition;
+import kr.wisestone.owl.domain.enumType.IssueStatusType;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.repository.WorkflowTransitionRepository;
+import kr.wisestone.owl.service.IssueStatusService;
+import kr.wisestone.owl.service.WorkflowTransitionService;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.vo.IssueStatusVo;
+import kr.wisestone.owl.vo.WorkflowTransitionVo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+@Service
+public class WorkflowTransitionServiceImpl extends AbstractServiceImpl<WorkflowTransition, Long, JpaRepository<WorkflowTransition, Long>> implements WorkflowTransitionService {
+
+    private static final Logger log = LoggerFactory.getLogger(WorkflowTransitionServiceImpl.class);
+
+    @Autowired
+    private WorkflowTransitionRepository workflowTransitionRepository;
+
+    @Autowired
+    private IssueStatusService issueStatusService;
+
+    @Override
+    protected JpaRepository<WorkflowTransition, Long> getRepository() {
+        return this.workflowTransitionRepository;
+    }
+
+    //  �썙�겕�뒪�럹�씠�뒪 �깮�꽦�떆 湲곕낯 �젣怨듬릺�뒗 �씠�뒋 �긽�깭瑜� �뿰寃고븯�뿬 湲곕낯 �젣怨듬릺�뒗 �썙�겕�뵆濡쒖슦�쓣 �깮�꽦�븳�떎.
+    @Override
+    @Transactional
+    public void addDefaultWorkflowTransition(Workflow workflow, List<IssueStatus> issueStatuses, ProjectType projectType) {
+
+        switch (projectType) {
+            case BTS_PROJECT:
+                //  �깮�꽦 -> 吏꾪뻾 (1 -> 2)
+                //  吏꾪뻾 -> �솗�씤 (2 -> 4)
+                //  �솗�씤 -> �옱吏꾪뻾 (4 -> 3)
+                //  �솗�씤 -> 醫낅즺 (4 -> 5)
+                //  �옱吏꾪뻾 -> 吏꾪뻾 (3 -> 2)
+                //  �옱吏꾪뻾 -> �솗�씤 (3 -> 4)
+                //  醫낅즺 -> �옱吏꾪뻾 (5 -> 3)
+                this.makeWorkflowTransition(1L, 2L, issueStatuses, workflow, 47L, 186L, 173L, 186L, 77L, 183L);
+                this.makeWorkflowTransition(2L, 4L, issueStatuses, workflow, 173L, 186L, 313L, 185L, 139L, 186L);
+                this.makeWorkflowTransition(4L, 3L, issueStatuses, workflow, 313L, 185L, 312L, 34L, 219L, 89L);
+                this.makeWorkflowTransition(4L, 5L, issueStatuses, workflow, 313L, 185L, 493L, 185L, 380L, 186L);
+                this.makeWorkflowTransition(3L, 2L, issueStatuses, workflow, 312L, 34L, 173L, 186L, 78L, 43L);
+                this.makeWorkflowTransition(5L, 3L, issueStatuses, workflow, 493L, 185L, 312L, 34L, 490L, 40L);
+
+                break;
+            case RMS_PROJECT:
+                //  �깮�꽦 -> 吏꾪뻾 (1 -> 2)
+                //  吏꾪뻾 -> �솗�씤 (2 -> 4)
+                //  �솗�씤 -> �듅�씤 遺덇� (4 -> 6)
+                //  �솗�씤 -> �듅�씤 (4 -> 7)
+                //  �듅�씤 遺덇� -> 吏꾪뻾 (6 -> 2)
+                //  �듅�씤 遺덇� -> 醫낅즺 (6 -> 5)
+                //  �듅�씤 -> 醫낅즺 (7 -> 5)
+                //  醫낅즺 -> 吏꾪뻾 (5 -> 2)
+                this.makeWorkflowTransition(1L, 2L, issueStatuses, workflow, 73L, 45L, 297L, 42L, 73L, 45L);
+                this.makeWorkflowTransition(2L, 4L, issueStatuses, workflow, 297L, 42L, 297L, 141L, 244L, 95L);
+                this.makeWorkflowTransition(4L, 6L, issueStatuses, workflow, 297L, 141L, 79L, 153L, 292L, 132L);
+                this.makeWorkflowTransition(4L, 7L, issueStatuses, workflow, 297L, 141L, 515L, 150L, 292L, 132L);
+                this.makeWorkflowTransition(6L, 2L, issueStatuses, workflow, 79L, 153L, 297L, 42L, 79L, 153L);
+                this.makeWorkflowTransition(6L, 5L, issueStatuses, workflow, 79L, 153L, 298L, 223L, 79L, 153L);
+                this.makeWorkflowTransition(7L, 5L, issueStatuses, workflow, 515L, 150L, 298L, 223L, 515L, 150L);
+                this.makeWorkflowTransition(5L, 2L, issueStatuses, workflow, 298L, 223L, 297L, 42L, 388L, 118L);
+                break;
+            case TCM_PROJECT:
+                //  �깮�꽦 -> �솗�씤 (1 -> 4)
+                //  �솗�씤 -> 醫낅즺 (4 -> 5)
+                //  醫낅즺 -> �옱吏꾪뻾 (5 -> 3)
+                //  �옱吏꾪뻾 -> �솗�씤(3 -> 4)
+                this.makeWorkflowTransition(1L, 4L, issueStatuses, workflow, 121L, 133L, 298L, 132L, 179L, 132L);
+                this.makeWorkflowTransition(4L, 5L, issueStatuses, workflow, 298L, 133L, 466L, 133L, 272L, 131L);
+                this.makeWorkflowTransition(5L, 3L, issueStatuses, workflow, 466L, 133L, 298L, 38L, 466L, 133L);
+                this.makeWorkflowTransition(3L, 4L, issueStatuses, workflow, 298L, 38L, 298L, 132L, 219L, 68L);
+                break;
+        }
+    }
+
+    private void makeWorkflowTransition(Long startPosition, Long endPosition, List<IssueStatus> issueStatuses, Workflow workflow, Long sourceX, Long sourceY, Long targetX, Long targetY, Long correctX, Long correctY) {
+        IssueStatus start = null;
+        IssueStatus end = null;
+
+        for (IssueStatus issueStatus : issueStatuses) {
+            if (startPosition.equals(issueStatus.getPosition())) {
+                start = issueStatus;
+            }
+
+            if (endPosition.equals(issueStatus.getPosition())) {
+                end = issueStatus;
+            }
+        }
+
+        if (start == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.START_ISSUE_STATUS_NOT_EXIST));
+        }
+
+        if (end == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.END_ISSUE_STATUS_NOT_EXIST));
+        }
+
+        WorkflowTransition workflowTransition = new WorkflowTransition(workflow, start, end, sourceX, sourceY, targetX, targetY, correctX, correctY);
+        this.workflowTransitionRepository.saveAndFlush(workflowTransition);
+    }
+
+
+    //  �썙�겕�뵆濡쒖슦�뿉�꽌 �궗�슜�릺�뒗 �쟾泥� �쟾�씠�꽑�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<WorkflowTransition> findByWorkflowId(Long workflowId) {
+        return this.workflowTransitionRepository.findByWorkflowId(workflowId);
+    }
+
+    //  �씠�뒋 �긽�깭�� �뿰寃곕맂 �쟾�씠�꽑 �젙蹂대�� 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public List<WorkflowTransitionVo> findBySourceIssueStatusIdAndWorkflowId(Long sourceStatusId, Long workflowId) {
+        return this.workflowTransitionRepository.findBySourceIssueStatusIdAndWorkflowId(sourceStatusId, workflowId);
+    }
+
+    //  �쟾�씠�꽑�쓽 �젙蹂대�� �뾽�뜲�씠�듃�븳�떎. �씠誘� 議댁옱�븯�뜕 �쟾�씠�꽑�씠硫� 蹂�寃쎌쓣 �깉濡� �깮�꽦�맂 �쟾�씠�꽑�� �깮�꽦�쓣 �븳�떎.
+    @Override
+    @Transactional
+    public void modify(Workflow workflow, List<IssueStatusVo> issueStatusVos) {
+        Map<String, Object> issueStatusMaps = new HashMap<>();
+        //  �긽�깭 �냽�꽦 蹂꾨줈 1媛쒖뵫 議댁옱�븯�뒗吏� �솗�씤�븳�떎.
+        this.checkRequireIssueStatusType(issueStatusVos);
+
+        //  怨좊┰�맂 �씠�뒋 �긽�깭媛� �뾾�뒗 �썙�겕�뵆濡쒖슦�씤吏� �솗�씤�븳�떎.
+        this.checkIsolationWorkflow(issueStatusVos);
+
+        for (IssueStatusVo issueStatusVo : issueStatusVos) {
+            issueStatusMaps.put(issueStatusVo.getId().toString(), issueStatusVo);
+        }
+
+        //  �빐�떦 �썙�겕�뵆濡쒖슦�쓽 �쟾泥� �쟾�씠�꽑 �궘�젣
+        List<WorkflowTransition> removeWorkflowTransitions = this.workflowTransitionRepository.findByWorkflowId(workflow.getId());
+
+        if (removeWorkflowTransitions.size() > 0) {
+            this.removeWorkflowTransition(removeWorkflowTransitions);
+        }
+
+        for (IssueStatusVo issueStatusVo : issueStatusVos) {
+            for (WorkflowTransitionVo workflowTransitionVo : issueStatusVo.getWorkflowTransitionVos()) {
+                IssueStatus sourceIssueStatus = this.issueStatusService.getIssueStatus(workflowTransitionVo.getSourceStatusId());
+                IssueStatus targetIssueStatus = this.issueStatusService.getIssueStatus(workflowTransitionVo.getTargetStatusId());
+                IssueStatusVo sourceIssueStatusVo = (IssueStatusVo) MapUtil.getObject(issueStatusMaps, sourceIssueStatus.getId().toString());
+                IssueStatusVo targetIssueStatusVo = (IssueStatusVo) MapUtil.getObject(issueStatusMaps, targetIssueStatus.getId().toString());
+
+
+                WorkflowTransition workflowTransition = new WorkflowTransition(workflow, sourceIssueStatus, targetIssueStatus, sourceIssueStatusVo.getxLocation(), sourceIssueStatusVo.getyLocation(),
+                        targetIssueStatusVo.getxLocation(), targetIssueStatusVo.getyLocation(), workflowTransitionVo.getCorrectX(), workflowTransitionVo.getCorrectY(), workflowTransitionVo.getDirect());
+
+                this.workflowTransitionRepository.save(workflowTransition);
+            }
+        }
+    }
+
+    //  �긽�깭 �냽�꽦 蹂꾨줈 1媛쒖뵫 議댁옱�븯�뒗吏� �솗�씤�븳�떎.
+    private void checkRequireIssueStatusType(List<IssueStatusVo> issueStatusVos) {
+        boolean firstStatusExist = true;
+        boolean middleStatusExist = true;
+        boolean lastStatusExist = true;
+
+        for (IssueStatusVo issueStatusVo : issueStatusVos) {
+            switch (IssueStatusType.valueOf(issueStatusVo.getIssueStatusType())) {
+                case READY:
+                    firstStatusExist = false;
+                    break;
+                case OPEN:
+                    middleStatusExist = false;
+                    break;
+                case CLOSE:
+                    lastStatusExist = false;
+                    break;
+            }
+        }
+
+        if (firstStatusExist) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_REQUIRE_ISSUE_STATUS_TYPE_TO_READY));
+        }
+
+        if (middleStatusExist) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_REQUIRE_ISSUE_STATUS_TYPE_TO_OPEN));
+        }
+
+        if (lastStatusExist) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_REQUIRE_ISSUE_STATUS_TYPE_TO_CLOSE));
+        }
+
+    }
+
+    //  怨좊┰�맂 �씠�뒋 �긽�깭媛� �뾾�뒗 �썙�겕�뵆濡쒖슦�씤吏� �솗�씤�븳�떎.
+    private void checkIsolationWorkflow(List<IssueStatusVo> issueStatusVos) {
+        boolean firstCheck = false;
+        boolean middleCheck = false;
+        boolean lastCheck = true;
+        boolean connectCheck = false;
+        List<WorkflowTransitionVo> workflowTransitionVos = Lists.newArrayList();
+
+        //  �쟾泥� �쟾�씠�꽑 異붿텧
+        for (IssueStatusVo issueStatusVo : issueStatusVos) {
+            for (WorkflowTransitionVo workflowTransitionVo : issueStatusVo.getWorkflowTransitionVos()) {
+                workflowTransitionVos.add(workflowTransitionVo);
+            }
+        }
+
+        //  �떆�옉 �긽�깭 �젙�긽 �솗�씤
+        for (IssueStatusVo issueStatusVo : issueStatusVos) {
+            switch (IssueStatusType.valueOf(issueStatusVo.getIssueStatusType())) {
+                case READY:
+                    for (WorkflowTransitionVo workflowTransitionVo : workflowTransitionVos) {
+                        if (workflowTransitionVo.getSourceStatusId().equals(issueStatusVo.getId())) {
+                            firstCheck = true;
+                            break;
+                        }
+
+                    }
+                    break;
+
+                case OPEN:
+                    boolean normalSourceCheck = false;
+                    boolean normalTargetCheck = false;
+                    middleCheck = true;
+
+                    for (WorkflowTransitionVo workflowTransitionVo : workflowTransitionVos) {
+                        if (workflowTransitionVo.getSourceStatusId().equals(issueStatusVo.getId())) {
+                            normalSourceCheck = true;
+                        }
+
+                        if (workflowTransitionVo.getTargetStatusId().equals(issueStatusVo.getId())) {
+                            normalTargetCheck = true;
+                        }
+                    }
+
+                    if (!normalSourceCheck || !normalTargetCheck) {
+                        connectCheck = true;
+                    }
+
+                    break;
+
+                case CLOSE:
+                    boolean tempLastCheck = false;
+
+                    for (WorkflowTransitionVo workflowTransitionVo : workflowTransitionVos) {
+                        if (workflowTransitionVo.getTargetStatusId().equals(issueStatusVo.getId())) {
+                            tempLastCheck = true;
+                            break;
+                        }
+                    }
+
+                    if (!tempLastCheck) {
+                        lastCheck = false;
+                    }
+
+                    break;
+            }
+        }
+
+        if (!firstCheck || !middleCheck || !lastCheck || connectCheck) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_ISOLATION));
+        }
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public WorkflowTransition getWorkflowTransition(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_TRANSITION_NOT_EXIST));
+        }
+
+        WorkflowTransition workflowTransition = this.findOne(id);
+
+        if (workflowTransition == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKFLOW_TRANSITION_NOT_EXIST));
+        }
+
+        return workflowTransition;
+    }
+
+    private void removeWorkflowTransition(List<WorkflowTransition> workflowTransitions) {
+        for (WorkflowTransition workflowTransition : workflowTransitions) {
+            this.workflowTransitionRepository.delete(workflowTransition);
+        }
+
+        this.workflowTransitionRepository.flush();
+    }
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/WorkspaceServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/WorkspaceServiceImpl.java
new file mode 100644
index 0000000..01c08e2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/WorkspaceServiceImpl.java
@@ -0,0 +1,630 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.common.ExcelConditionCheck;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.domain.enumType.EmailType;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import kr.wisestone.owl.domain.enumType.ServiceType;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.mapper.WorkspaceMapper;
+import kr.wisestone.owl.repository.WorkspaceRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.*;
+import kr.wisestone.owl.vo.ExportExcelVo;
+import kr.wisestone.owl.vo.WorkspaceVo;
+import kr.wisestone.owl.web.form.*;
+import kr.wisestone.owl.web.view.ExcelView;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.ui.Model;
+import org.springframework.web.servlet.ModelAndView;
+
+import java.util.*;
+
+@Service
+public class WorkspaceServiceImpl extends AbstractServiceImpl<Workspace, Long, JpaRepository<Workspace, Long>> implements WorkspaceService {
+
+    private static final Logger log = LoggerFactory.getLogger(WorkspaceServiceImpl.class);
+
+    @Autowired
+    private WorkspaceRepository workspaceRepository;
+
+    @Autowired
+    private IssueStatusService issueStatusService;
+
+    @Autowired
+    private UserWorkspaceService userWorkspaceService;
+
+    @Autowired
+    private WorkflowService workflowService;
+
+    @Autowired
+    private IssueTypeService issueTypeService;
+
+    @Autowired
+    private SimpMessagingTemplate simpMessagingTemplate;
+
+    @Autowired
+    private WorkspaceMapper workspaceMapper;
+
+    @Autowired
+    private AttachedFileService attachedFileService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private SystemEmailService systemEmailService;
+
+    @Autowired
+    private PriorityService priorityService;
+
+    @Autowired
+    private SeverityService severityService;
+
+    @Autowired
+    private ExcelView excelView;
+
+    @Autowired
+    private ExcelConditionCheck excelConditionCheck;
+
+    // zenith add type
+    @Value("${saas.packagetype}")
+    private Integer packageType;
+
+    @Value("${saas.maxUser}")
+    private Integer maxUserCount;
+
+    @Value("${saas.period}")
+    private Integer usePeriod;
+
+    @Value("${saas.usdkrw}")
+    private Integer usdKrw;
+
+    @Override
+    protected JpaRepository<Workspace, Long> getRepository() {
+        return this.workspaceRepository;
+    }
+
+    //  �뾽臾� 怨듦컙 �깮�꽦
+    @Override
+    @Transactional
+    public Workspace addWorkspace(String workspaceName) {
+        Workspace workspace = new Workspace();
+        //  �뾽臾� 怨듦컙 紐� �쑀�슚�꽦 泥댄겕
+        this.verifyWorkspaceName(workspaceName);
+        workspace.setName(workspaceName);
+        workspace.setMaxUser(this.maxUserCount);
+        workspace.setStorageSize(workspace.calculateStorageSize());
+        workspace.setStartDate(new Date());
+        workspace.setExpireDate(DateUtil.addDays(new Date(), this.usePeriod));
+        workspace.setServiceType(ServiceType.USE);
+
+        this.workspaceRepository.saveAndFlush(workspace);
+        //  �떆�뒪�뀥�뿉�꽌 �궗�슜�븷 �닔 �엳�뒗 湲곕낯 �젣怨� �씠�뒋 �긽�깭瑜� �깮�꽦�븳�떎.
+        this.issueStatusService.addDefaultIssueStatus(workspace);
+        //  �떆�뒪�뀥�뿉�꽌 �궗�슜�븷 �닔 �엳�뒗 湲곕낯 �썙�겕�뵆濡쒖슦瑜� �깮�꽦�븳�떎.
+        this.workflowService.addDefaultWorkflow(workspace, Arrays.asList(ProjectType.BTS_PROJECT, ProjectType.RMS_PROJECT, ProjectType.TCM_PROJECT));
+        //  �떆�뒪�뀥�뿉�꽌 �궗�슜�븷 �닔 �엳�뒗 湲곕낯 �씠�뒋 �쑀�삎�쓣 �깮�꽦�븳�떎.
+        this.issueTypeService.addDefaultIssueType(workspace, Arrays.asList(ProjectType.BTS_PROJECT, ProjectType.RMS_PROJECT, ProjectType.TCM_PROJECT));
+        //  �떆�뒪�뀥�뿉�꽌 �궗�슜�븷 �닔 �엳�뒗 湲곕낯 �슦�꽑 �닚�쐞瑜� �깮�꽦�븳�떎.
+        this.priorityService.addDefaultPriority(workspace);
+        //  �떆�뒪�뀥�뿉�꽌 �궗�슜�븷 �닔 �엳�뒗 湲곕낯 以묒슂�룄瑜� �깮�꽦�븳�떎.
+        this.severityService.addDefaultSeverity(workspace);
+        return workspace;
+    }
+
+    //  �뾽臾� 怨듦컙 紐� �쑀�슚�꽦 泥댄겕
+    private void verifyWorkspaceName(String workspaceName) {
+        if (StringUtils.isEmpty(workspaceName)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKSPACE_NOT_NAME));
+        }
+
+        if (workspaceName.length() > 50) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKSPACE_NAME_MAX_LENGTH_OUT));
+        }
+    }
+
+    //  �뾽臾� 怨듦컙�뿉�꽌 �궗�슜�옄 �젣�쇅
+    @Override
+    @Transactional
+    public void out(WorkspaceForm workspaceForm) {
+        /*List<User> users = this.userService.findByIdIn(workspaceForm.getUserIds());
+        Workspace workspace = this.workspaceRepository.findOne(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        List<String> userEmails = Lists.newArrayList();
+
+        users.parallelStream().forEach(user -> {
+            userEmails.add(user.getAccount());
+        });
+
+        // �빐�떦 �뾽臾� 怨듦컙�뿉 議댁옱�븯�뒗 紐⑤뱺 �봽濡쒖젥�듃�뿉�꽌 �젣�쇅
+        this.projectRoleUserService.outWorkspaceModifyProjectRole(workspace, userEmails);
+        //  �씠�쟾�뿉 蹂대궦 珥덈� �젙蹂� �궘�젣
+        this.userInviteService.deleteUserInvite(workspace.getId(), userEmails);
+        //  �뾽臾� 怨듦컙 �궗�슜�옄 �뿰寃� �젙蹂� �궘�젣
+        this.userWorkspaceService.deleteUserWorkspace(workspace, workspaceForm.getUserIds());
+        //  �씠�뒋�쓽 �떞�떦�옄���쑝硫� �빐�떦 �씠�뒋�뿉�꽌 �떞�떦�옄 �젣�쇅
+        this.issueUserService.outWorkspaceModifyAssignee(workspace, userEmails);
+
+        //  �뾽臾� 怨듦컙 �젣�쇅 �씠硫붿씪 �븣由�
+        String[] sendUsers = userEmails.toArray(new String[userEmails.size()]);
+        Map<String, Object> content = new HashMap<>();
+        content.put("workspaceName", workspace.getName());
+
+        this.systemEmailService.sendEmail(sendUsers, EmailType.WORKSPACE_OUT, content);*/
+    }
+
+    //  �젙湲� 寃곗젣媛� �셿猷뚮맂 �썑 �뾽臾� 怨듦컙 留뚮즺�씪, 理쒕� �궗�슜�옄 �닔, ���옣 怨듦컙, �궗�슜 媛��뒫 �듃�옒�뵿�쓣 �뾽�뜲�씠�듃 �븳�떎.
+    @Override
+    @Transactional
+    public Workspace updateWorkspace(Workspace workspace, PaymentForm paymentForm) {
+        Date nextExpireDate = workspace.calculateNextPaymentDate(paymentForm.getType(), workspace.getExpireDate());
+        Date expireDate = DateUtil.convertStrToDate(DateUtil.convertDateToStr(nextExpireDate, "yyyy-MM-dd") + " 23:59:59");
+        workspace.setStartDate(new Date());
+        workspace.setExpireDate(expireDate);
+        workspace.setMaxUser(paymentForm.getBuyUser());
+        workspace.setStorageSize(workspace.calculateStorageSize());
+        workspace.setUseTraffic(0L);
+        workspace.setServiceType(ServiceType.USE);
+        return this.workspaceRepository.saveAndFlush(workspace);
+    }
+
+    //  異붽� 寃곗젣 �셿猷� �썑 理쒕� �궗�슜�옄 �닔, ���옣 怨듦컙�쓣 �뾽�뜲�씠�듃�븳�떎.
+    @Override
+    @Transactional
+    public Workspace updateWorkspaceByImmediatePayment(Workspace workspace, int buyUser) {
+        workspace.setMaxUser(buyUser);
+        workspace.setStorageSize(workspace.calculateStorageSize());
+        return this.workspaceRepository.saveAndFlush(workspace);
+    }
+
+    //  �젙湲� 寃곗젣�씪�씠 �맂 �뾽臾� 怨듦컙 紐⑸줉�쓣 媛��졇�삩�떎. - �뒪耳�伊대쭅�뿉�꽌 �궗�슜�븿
+    @Override
+    @Transactional(readOnly = true)
+    public List<Workspace> findSubscribeImmediateExpireDate() {
+        String today = DateUtil.convertDateToYYYYMMDD(DateUtil.addDays(new Date(), -1));
+        Date todayFrom = DateUtil.convertStrToDate(today + " 00:00:00");
+        Date todayTo = DateUtil.convertStrToDate(today + " 23:59:59");
+        return this.workspaceRepository.findSubscribeImmediateExpireDate(todayFrom, todayTo);
+    }
+
+    //  �궗�슜�옄 諛� �궗�슜 媛��뒫 �슜�웾 珥덇린�솕
+    @Override
+    @Transactional
+    public void initMaxUserAndStorageSize(Workspace workspace) {
+        workspace.setMaxUser(0);
+        workspace.setStorageSize(0L);
+        workspace.setServiceType(ServiceType.UNUSED);   //  �궗�슜 �븞�븿�쑝濡� 蹂�寃�
+        workspace.setPayment(null); //  �씠�쟾 寃곗젣 �젙蹂� �궘�젣
+        this.workspaceRepository.saveAndFlush(workspace);
+    }
+
+    //  �뾽臾� 怨듦컙 �젙湲� 寃곗젣 痍⑥냼 - �젙湲� 寃곗젣 �젙蹂� �궘�젣
+    @Override
+    @Transactional
+    public void cancelWorkspacePayment(Workspace workspace) {
+        workspace.setPayment(null);
+        this.workspaceRepository.saveAndFlush(workspace);
+    }
+
+    //  理쒖큹 �꽕移섑븳 愿�由ъ옄媛� �깮�꽦�븳 �뾽臾� 怨듦컙瑜� 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Workspace getPrimaryWorkspace() {
+        Long workspaceid = 0L;
+        List<User> users = this.userService.findAdmin();
+
+        if(users.size() > 0 )
+        {
+            User adminUser = users.get(0);
+            workspaceid = adminUser.getLastWorkspaceId();
+        }
+
+        if (workspaceid == 0) {
+            return null;
+        }
+
+        Workspace workspace = this.findOne(workspaceid);
+
+        if (workspace == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKSPACE_NOT_EXIST));
+        }
+
+        return workspace;
+    }
+
+    //  �븘�씠�뵒�뿉 �빐�떦�븯�뒗 �뾽臾� 怨듦컙瑜� 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Workspace getWorkspace(Long id) {
+        if (id == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKSPACE_NOT_EXIST));
+        }
+
+        Workspace workspace = this.findOne(id);
+
+        if (workspace == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKSPACE_NOT_EXIST));
+        }
+
+        return workspace;
+    }
+
+    //  李몄뿬�븯怨� �엳�뒗 �뾽臾� 怨듦컙 紐⑸줉�쓣 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void find(Map<String, Object> resJsonData) {
+        List<Workspace> workspaces = this.workspaceRepository.findByUserId(this.webAppUtil.getLoginId());
+        List<WorkspaceVo> workspaceVos = Lists.newArrayList();
+
+        for (Workspace workspace : workspaces) {
+            WorkspaceVo workspaceVo = ConvertUtil.copyProperties(workspace, WorkspaceVo.class);
+            workspaceVos.add(workspaceVo);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, workspaceVos);
+    }
+
+    //  �옄�떊�씠 愿�由ы븯�뒗 �뾽臾� 怨듦컙瑜� 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findPrimaryWorkspace(Map<String, Object> resJsonData) {
+
+        boolean bHavePrimaryWorkspace = false;
+
+        Workspace primaryWorkspace =  this.getPrimaryWorkspace();
+
+        if(primaryWorkspace != null) {
+            bHavePrimaryWorkspace = true;
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, bHavePrimaryWorkspace);
+    }
+
+    //  �옄�떊�씠 愿�由ы븯�뒗 �뾽臾� 怨듦컙瑜� 媛��졇�삩�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void findMyWorkspace(Map<String, Object> resJsonData) {
+        UserWorkspace userWorkspace = this.userWorkspaceService.findMyWorkspace(this.webAppUtil.getLoginId());
+        Workspace workspace = userWorkspace.getWorkspace();
+        WorkspaceVo workspaceVo = ConvertUtil.copyProperties(workspace, WorkspaceVo.class);
+        workspaceVo.setServiceType((workspace.getServiceType().toString()));
+        workspaceVo.setExpireDateTerm(DateUtil.getDateDiff(new Date(), workspace.getExpireDate())); //  異붽� 寃곗젣瑜� 吏꾪뻾�븷 �븣 湲덉븸 怨꾩궛�쓣 �쐞�빐 �궗�슜
+
+        //  zenith edit
+        //  packagetype�쓣 ���옣�븳�떎.
+        workspaceVo.setPackageType(this.packageType);
+
+        //  �뾽臾� 怨듦컙 �궗�슜 媛��뒫 湲곌컙
+        this.checkRemainPeriod(workspace, workspaceVo);
+        //  �쁽�옱 �궗�슜 �슜�웾
+        this.checkUseStorageSize(workspace, workspaceVo);
+        //  李몄뿬 �궗�슜�옄
+        this.checkActiveUserCount(workspace, workspaceVo);
+        //  李몄뿬 ��湲� �궗�슜�옄
+        this.checkStandByUserCount(workspace, workspaceVo);
+        //  �솚�쑉 �젙蹂�
+        workspaceVo.setUsdKrw(this.usdKrw);
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, workspaceVo);
+    }
+
+    // zenith edit
+    private long getRemainPeriod(Date fromDate, Date toDate) {
+        long lRemainDays = 0;
+
+        // zenith edit packageType 1 �씠�긽�씠紐�, 留뚮즺�씪�씠 �뾾�쓬. (local version)
+        if (this.packageType > 0) {
+            lRemainDays = 9999;
+        }
+        else
+        {
+            Integer totalDate = DateUtil.getDateDiff(fromDate, toDate);
+            //  �궗�슜 媛��뒫 湲곌컙�� 0蹂대떎 �쟻�쓣 寃쎌슦 0�쑝濡� �몴�떆
+            if (totalDate < 0) {
+                totalDate = 0;
+            }
+
+            lRemainDays = totalDate.longValue();
+        }
+
+        return lRemainDays;
+    }
+
+    //  �뾽臾� 怨듦컙 �궗�슜 媛��뒫 湲곌컙
+    private void checkRemainPeriod(Workspace workspace, WorkspaceVo workspaceVo) {
+
+        long lRemainDays = getRemainPeriod(new Date(), workspace.getExpireDate());
+
+        workspaceVo.setTotalDate(lRemainDays);
+    }
+
+    //  �쁽�옱 �궗�슜 �슜�웾
+    private void checkUseStorageSize(Workspace workspace, WorkspaceVo workspaceVo) {
+        //  �뾽臾� 怨듦컙�뿉�꽌 �궗�슜以묒씤 ���옣 �슜�웾�쓣 議고쉶�븳�떎.
+        Long useStorageSize = this.attachedFileService.findUseStorage(workspace);
+
+        if (useStorageSize == null) {
+            useStorageSize = 0L;
+        }
+        workspaceVo.setUseStorageSize(useStorageSize);
+    }
+
+    //  李몄뿬 �궗�슜�옄
+    private void checkActiveUserCount(Workspace workspace, WorkspaceVo workspaceVo) {
+        Integer userCount = this.userWorkspaceService.countByWorkspaceIdAndUseYn(workspace.getId(), true);
+        workspaceVo.setActiveUser(userCount);
+    }
+
+    //  李몄뿬 ��湲� �궗�슜�옄
+    private void checkStandByUserCount(Workspace workspace, WorkspaceVo workspaceVo) {
+        Integer userCount = this.userWorkspaceService.countByWorkspaceIdAndUseYn(workspace.getId(), false);
+        workspaceVo.setStandByUser(userCount);
+    }
+
+
+    //  �궗�슜�옄媛� �쉶�썝 �깉�눜瑜� 吏꾪뻾�븯硫� �뾽臾� 怨듦컙�뿉 �엳�뒗 �젙蹂대�� �궘�젣�븳�떎.
+    @Override
+    @Transactional
+    public void removeWorkspace(Workspace workspace, User user) {
+        //  �궘�젣�릺�뒗 �뾽臾� 怨듦컙�뿉 李몄뿬�븯怨� �엳�뒗 �궗�슜�옄
+        List<String> updateTargetUsers = this.findUpdateLastWorkspaceTargetUser(workspace);
+        String workspaceName = workspace.getName();
+
+        Map<String, Object> deleteWorkspaceMap = new HashMap<>();
+        deleteWorkspaceMap.put("workspaceId", workspace.getId());
+        //  �뾽臾� 怨듦컙�뿉 �뿰愿��맂 �뀒�씠釉붿쓣 �궘�젣�븯湲� �쐞�빐 id 媛믪쓣 異붿텧�븳�떎.
+        this.setWorkspaceRelationIds(workspace, deleteWorkspaceMap);
+        //  �뾽臾� 怨듦컙�뿉 �엳�뒗 紐⑤뱺 �젙蹂대�� �궘�젣�븳�떎.
+        this.workspaceMapper.deleteWorkspace(deleteWorkspaceMap);
+        //  �뾽臾� 怨듦컙 �궘�젣�떆 �씠�뒋�뿉 泥⑤��맂 �뙆�씪�쓣 �떆�뒪�뀥�뿉�꽌 �궘�젣�븳�떎.
+        this.attachedFileService.deleteWorkspaceCascadeAttachedFile(workspace);
+
+        //  �뾽臾� 怨듦컙�쓣 �궗�슜�븯吏� 紐삵븯�룄濡� �븳�떎.
+        workspace.setStorageSize(0L);   //  ���옣 媛��뒫 怨듦컙 0�쑝濡� 蹂�寃�
+        workspace.setMaxUser(0);   //  �궗�슜 媛��뒫 �씤�썝 0�쑝濡� 蹂�寃�
+        workspace.setServiceType(ServiceType.UNUSED);   //  �뾽臾� 怨듦컙 �꽌鍮꾩뒪瑜� �궗�슜 �븞�븿�쑝濡� 蹂�寃�
+        this.workspaceRepository.flush();
+
+        for (String account : updateTargetUsers) {
+            //  �뾽臾� 怨듦컙�뿉 李몄뿬�븯�뜕 �궗�슜�옄�뿉寃� �뾽臾� 怨듦컙 �궘�젣 �븣由� 諛� �솕硫� �깉濡쒓퀬移�
+            this.simpMessagingTemplate.convertAndSendToUser(account, "/notification/workspace-remove", this.messageAccessor.getMessage(MsgConstants.WORKSPACE_OUT, workspaceName));
+        }
+    }
+
+    //  �뾽臾� 怨듦컙�뿉 �뿰愿��맂 �뀒�씠釉붿쓣 �궘�젣�븯湲� �쐞�빐 id 媛믪쓣 異붿텧�븳�떎.
+    private void setWorkspaceRelationIds(Workspace workspace, Map<String, Object> deleteWorkspaceMap) {
+        List<Long> workflowIds = Lists.newArrayList();
+
+        for (Workflow workflow : workspace.getWorkflows()) {
+            workflowIds.add(workflow.getId());
+        }
+
+        deleteWorkspaceMap.put("workflowIds", workflowIds);
+
+        List<Long> userInviteIds = Lists.newArrayList();
+
+        for (UserInvite userInvite : workspace.getUserInvites()) {
+            userInviteIds.add(userInvite.getId());
+        }
+
+        deleteWorkspaceMap.put("userInviteIds", userInviteIds);
+
+        List<Long> projectIds = Lists.newArrayList();
+        List<Long> projectRoleIds = Lists.newArrayList();
+
+        for (Project project : workspace.getProjects()) {
+            projectIds.add(project.getId());
+            for (ProjectRole projectRole : project.getProjectRoles()) {
+                projectRoleIds.add(projectRole.getId());
+            }
+        }
+
+        deleteWorkspaceMap.put("projectIds", projectIds);
+        deleteWorkspaceMap.put("projectRoleIds", projectRoleIds);
+
+        List<Long> customFieldIds = Lists.newArrayList();
+
+        for (CustomField customField : workspace.getCustomFields()) {
+            customFieldIds.add(customField.getId());
+        }
+
+        deleteWorkspaceMap.put("customFieldIds", customFieldIds);
+    }
+
+    //  �궘�젣�릺�뒗 �뾽臾� 怨듦컙�뿉 李몄뿬�븯怨� �엳�뒗 �궗�슜�옄
+    private List<String> findUpdateLastWorkspaceTargetUser(Workspace workspace) {
+        List<String> updateTargetUsers = Lists.newArrayList();  //  �빐�떦 �뾽臾� 怨듦컙�쓣 留덉�留됱쑝濡� �젒洹쇳븳 �궗�슜�옄 紐⑸줉
+
+        List<UserWorkspace> userWorkspaces = this.userWorkspaceService.findByWorkspaceIdAndManagerYn(workspace.getId(), false);
+
+        for (UserWorkspace userWorkspace : userWorkspaces) {
+            User user = userWorkspace.getUser();
+
+            if (workspace.getId().equals(user.getLastWorkspaceId())) {
+                updateTargetUsers.add(user.getAccount());
+            }
+            //  �빐�떦 �궗�슜�옄媛� 愿�由ы븯�뒗 �뾽臾� 怨듦컙�쓣 留덉�留됱쑝濡� �젒洹쇳븳 �뾽臾� 怨듦컙 �젙蹂대줈 �뾽�뜲�씠�듃�븳�떎.
+            this.userService.updateLastDefaultWorkspace(workspace, user);
+        }
+
+        return updateTargetUsers;
+    }
+
+    //  �뾽臾� 怨듦컙 紐낆쓣 �닔�젙�븳�떎.
+    @Override
+    @Transactional
+    public void modifyWorkspace(WorkspaceForm workspaceForm) {
+        Workspace workspace = this.getWorkspace(workspaceForm.getWorkspaceId());
+
+        //  �뾽臾� 怨듦컙 紐� �쑀�슚�꽦 泥댄겕
+        this.verifyWorkspaceName(workspaceForm.getName());
+
+        workspace.setName(workspaceForm.getName());
+        this.workspaceRepository.saveAndFlush(workspace);
+    }
+
+    //  �궗�슜 湲곌컙�씠 留뚮즺�맂 �뾽臾� 怨듦컙�쓣 李얠븘 �슜�웾, 理쒕� �궗�슜�옄, �꽌鍮꾩뒪 �쑀�삎�쓣 蹂�寃쏀븳�떎.
+    @Override
+    @Transactional
+    public void expireWorkspace() {
+
+        // zenith edit packageType 0 �씠�긽�씠硫�, 留뚮즺�씪�씠 �뾾�쓬. (local version)
+        if(this.packageType > 0) {
+            String today = DateUtil.convertDateToYYYYMMDD(DateUtil.addDays(new Date(), -1));
+            Date todayFrom = DateUtil.convertStrToDate(today + " 00:00:00");
+            Date todayTo = DateUtil.convertStrToDate(today + " 23:59:59");
+            //  寃곗젣�씪�씠 �룄�옒�맂 �뾽臾� 怨듦컙 紐⑸줉�쓣 媛��졇�삩�떎.
+            List<Workspace> workspaces = this.workspaceRepository.findExpireDate(todayFrom, todayTo);
+
+            for (Workspace workspace : workspaces) {
+                //  �궗�슜湲곌컙�씠 留뚮즺�맂 �뾽臾� 怨듦컙�쓽 愿�由ъ옄媛� �븘�땶 �궗�슜�옄瑜� 異붿텧�븯�뿬 留덉�留� �젒洹쇳븳 �뾽臾� 怨듦컙 �젙蹂대�� 蹂�寃쏀븳�떎.
+                this.userWorkspaceService.limitExpireUserWorkspace(workspace);
+
+                List<UserWorkspace> managerUserWorkspace = this.userWorkspaceService.findByWorkspaceIdAndManagerYn(workspace.getId(), true);
+
+                for (UserWorkspace userWorkspace : managerUserWorkspace) {
+                    Map<String, Object> workspaceMap = new HashMap<>();
+                    workspaceMap.put("workspaceName", workspace.getName());
+                    workspaceMap.put("userName", userWorkspace.getUser().getName());
+                    workspaceMap.put("expireDate", DateUtil.convertDateToYYYYMMDD(workspace.getExpireDate()));
+
+                    //  �뾽臾� 怨듦컙 愿�由ъ옄�뿉寃� �궗�슜 湲곌컙 留뚮즺 �븣由�
+                    this.systemEmailService.directEmail(new String[]{userWorkspace.getUser().getAccount()}, EmailType.WORKSPACE_EXPIRE, workspaceMap, null);
+                }
+
+                //  �뾽臾� 怨듦컙 理쒕� �궗�슜�옄 諛� �궗�슜 媛��뒫 �슜�웾 珥덇린�솕
+                this.initMaxUserAndStorageSize(workspace);
+            }
+        }
+    }
+
+    //  �뾽臾� 怨듦컙 留뚮즺 �븞�궡 硫붿씪�쓣 蹂대궦�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public void expireAlarmWorkspace() {
+        //  7�씪 �썑 留뚮즺�씤 �뾽臾� 怨듦컙 �떞�떦�옄�뿉寃� 硫붿씪 蹂대궡湲�
+        this.sendExpireAlarmWorkspace(7);
+        //  3�씪 �썑 留뚮즺�씤 �뾽臾� 怨듦컙 �떞�떦�옄�뿉寃� 硫붿씪 蹂대궡湲�
+        this.sendExpireAlarmWorkspace(3);
+    }
+
+    private void sendExpireAlarmWorkspace(int dayCount) {
+        String day = DateUtil.convertDateToYYYYMMDD(DateUtil.addDays(new Date(), dayCount));
+        Date dayFrom = DateUtil.convertStrToDate(day + " 00:00:00");
+        Date dayTo = DateUtil.convertStrToDate(day + " 23:59:59");
+
+        //  留뚮즺�씪�씠 �룄�옒�맂 �뾽臾� 怨듦컙�쓣 媛��졇�삩�떎.
+        List<Workspace> workspaces = this.workspaceRepository.findExpireDate(dayFrom, dayTo);
+
+        for (Workspace workspace : workspaces) {
+            List<UserWorkspace> userWorkspaces = this.userWorkspaceService.findByWorkspaceIdAndManagerYn(workspace.getId(), true);
+
+            if (userWorkspaces.size() > 0) {
+                Map<String, Object> workspaceMap = new HashMap<>();
+                workspaceMap.put("workspaceName", workspace.getName());
+                workspaceMap.put("expireDate", DateUtil.convertDateToYYYYMMDD(workspace.getExpireDate()));
+                workspaceMap.put("dayCount", dayCount);
+                workspaceMap.put("name", userWorkspaces.get(0).getUser().getName());
+
+                //  �뾽臾� 怨듦컙 愿�由ъ옄�뿉寃� �궗�슜 湲곌컙 留뚮즺 �삁�젙�씪 �븣由�
+                this.systemEmailService.directEmail(new String[]{userWorkspaces.get(0).getUser().getAccount()}, EmailType.WORKSPACE_EXPIRE_ALARM, workspaceMap, null);
+            }
+        }
+    }
+
+
+    //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+    @Override
+    @Transactional
+    public void checkUseWorkspace() {
+        Workspace workspace = this.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+
+        if (workspace.getServiceType().equals(ServiceType.UNUSED)) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKSPACE_USE_PERIOD_EXCESS));
+        }
+
+        UserWorkspace userWorkspace = this.userWorkspaceService.findByUserIdAndWorkspaceId(this.webAppUtil.getLoginId(), workspace.getId());
+
+        if (!userWorkspace.getUseYn()) {
+            //  �빐�떦 �궗�슜�옄媛� 愿�由ы븯�뒗 �뾽臾� 怨듦컙瑜� 留덉�留� �젒洹� �뾽臾� 怨듦컙濡� 蹂�寃쏀븳�떎.
+            this.userService.updateLastMyWorkspace(userWorkspace.getUser());
+
+            //  鍮꾪솢�꽦 �궗�슜�옄�뒗 �뜑�씠�긽 �빐�떦 �뾽臾� 怨듦컙�뿉 �엳�쑝硫� �븞�맂�떎.
+            this.simpMessagingTemplate.convertAndSendToUser(this.webAppUtil.getLoginUser().getAccount(), "/notification/workspace-remove", this.messageAccessor.getMessage(MsgConstants.WORKSPACE_OUT, workspace.getName()));
+
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKSPACE_INCLUDE_DISABLED));
+        }
+    }
+
+    //  �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븯怨� 鍮꾪솢�꽦�씪 寃쎌슦 �뿊�� �떎�슫濡쒕뱶瑜� 湲덉��븳�떎.
+    @Override
+    @Transactional
+    public ModelAndView checkUseExcelDownload(Model model) {
+        try {
+            //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+            this.checkUseWorkspace();
+        }
+        catch (OwlRuntimeException e) {
+            //  �뾽臾� 怨듦컙 �솢�꽦 泥댄겕�뿉�꽌 Exception �씠 �쟾�넚�릺�뼱 �씠 遺�遺꾩뿉�꽌 Exception 硫붿꽭吏�瑜� �쟾�넚�븯吏� �븡�뒗�떎.
+            ExportExcelVo excelInfo = this.excelConditionCheck.makeEmptyDownloadExcel();
+            excelInfo.setFileName(this.messageAccessor.message("common.excelDownloadNotPossibleExcludedFromTheWorkspace")); // �뾽臾� 怨듦컙�뿉�꽌 李몄뿬媛� �젣�쇅�릺�뼱 �뿊�� �떎�슫濡쒕뱶媛� 遺덇��뒫�빀�땲�떎.
+            model.addAttribute(Constants.EXCEL, excelInfo);
+            return new ModelAndView(this.excelView);
+        }
+
+        return null;
+    }
+
+    //  �듃�옒�뵿 �궗�슜�웾�쓣 ���옣�븯怨� 珥덇낵�븷 寃쎌슦�뿉�뒗 �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 �떎�슫濡쒕뱶瑜� �씪�떆�쟻�쑝濡� 湲덉��븳�떎.
+    @Override
+    @Transactional
+    public boolean checkUseTraffic(Long fileSize) {
+
+        // zenith edit packageType 0 �씠�긽�씠硫�, �듃�옒�뵿 �젣�빟�씠 �뾾�쓬. (local version)
+        if(this.packageType > 0) {
+            Workspace workspace = this.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+
+            //  �듃�옒�뵿�� �궗�슜 怨듦컙�쓽 3諛곕줈 以��떎.
+            if (workspace.getUseTraffic() > workspace.getStorageSize() * 3) {
+                return false;
+            }
+
+            Long useTraffic = workspace.getUseTraffic() + fileSize;
+            workspace.setUseTraffic(useTraffic);
+            this.workspaceRepository.saveAndFlush(workspace);
+        }
+        
+        return true;
+    }
+
+    //  �뾽臾� 怨듦컙�쓽 �궓�� �궗�슜 湲곌컙�쓣 議고쉶�븳�떎.
+    @Override
+    @Transactional(readOnly = true)
+    public Map<String, Object> getWorkspaceExpireDay() {
+        Map<String, Object> workspaceMap = new HashMap<>();
+        Workspace workspace = this.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        workspaceMap.put("name", workspace.getName());
+        // zenith edit
+        workspaceMap.put("expireDay", getRemainPeriod(new Date(), workspace.getExpireDate()));
+
+        return workspaceMap;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/type/LikeType.java b/src/main/java/kr/wisestone/owl/type/LikeType.java
new file mode 100644
index 0000000..ba47563
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/type/LikeType.java
@@ -0,0 +1,7 @@
+package kr.wisestone.owl.type;
+
+public enum LikeType {
+    FULL,
+    LEFT,
+    RIGHT
+}
diff --git a/src/main/java/kr/wisestone/owl/util/ApplicationContextUtil.java b/src/main/java/kr/wisestone/owl/util/ApplicationContextUtil.java
new file mode 100644
index 0000000..4ec3c94
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/util/ApplicationContextUtil.java
@@ -0,0 +1,48 @@
+package kr.wisestone.owl.util;
+
+import kr.wisestone.owl.config.ApplicationContextProvider;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+public class ApplicationContextUtil {
+
+    public static ApplicationContext getApplicationContext() {
+        ApplicationContext applicationContext = null;
+
+        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        if (requestAttributes == null) {
+            return ApplicationContextProvider.getContext();
+        }
+        applicationContext = WebApplicationContextUtils.getWebApplicationContext(requestAttributes
+                .getRequest().getServletContext());
+
+        return applicationContext;
+    }
+
+    public static <T> T getBean(Class<T> clazz) {
+        ApplicationContext ac = getApplicationContext();
+        if (ac == null) {
+            return null;
+        }
+        return ac.getBean(clazz);
+    }
+
+    /**
+     * �씠由꾩쓣 �씠�슜�빐 鍮덉쓣 李얜뒗�떎. �뾾�쑝硫� null�쓣 諛섑솚�븳�떎.
+     *
+     * @param name
+     * @param clazz
+     * @return
+     */
+    public static <T> T getBean(String name, Class<T> clazz) {
+        ApplicationContext ac = getApplicationContext();
+        if (ac == null) {
+            return null;
+        }
+
+        return ac.getBean(name, clazz);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/util/CommonUtil.java b/src/main/java/kr/wisestone/owl/util/CommonUtil.java
new file mode 100644
index 0000000..0fac1dd
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/util/CommonUtil.java
@@ -0,0 +1,922 @@
+package kr.wisestone.owl.util;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.Lists;
+import com.google.gson.Gson;
+import kr.wisestone.owl.domain.enumType.FileType;
+import kr.wisestone.owl.type.LikeType;
+import kr.wisestone.owl.vo.UserVo;
+import org.apache.commons.codec.binary.*;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.Cell;
+import org.jsoup.Jsoup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.web.multipart.MultipartFile;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.crypto.*;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.HttpsURLConnection;
+import javax.servlet.http.HttpServletRequest;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.*;
+import java.math.BigInteger;
+import java.net.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.security.spec.KeySpec;
+import java.text.DecimalFormat;
+import java.util.*;
+
+public class CommonUtil {
+    private static final Logger LOGGER = LoggerFactory.getLogger(CommonUtil.class);
+
+    private static final String ENCODING_TYPE = "UTF-8";
+    private static final String SALT = "c7224df62de46817a515ce79b24d48d2";    //  AES128 �븫�샇�솕�뿉 �궗�슜
+    private static final String IV = "3c1bc71695361ca0aec4d83bb328b444";  //  AES128 �븫�샇�솕�뿉 �궗�슜
+    private static final String PASS_PHRASE = "1024";   //  AES128 �븫�샇�솕�뿉 �궗�슜
+    private static final int ITERATION_COUNT = 10000;   //  AES128 �븫�샇�솕�뿉 �궗�슜
+    private static final int KEY_SIZE = 128;    //  AES128 �븫�샇�솕�뿉 �궗�슜
+    private static final String TMP_UPLOAD_FOLDER = "/tmpUploadFolder/";    //  �씠�뒋 �깮�꽦, �닔�젙�뿉�꽌 �뙆�씪 �뾽濡쒕뱶�븷 �븣 �엫�떆 �뤃�뜑濡� �궗�슜
+
+    public static String getClinetIp() {
+        try {
+            for (Enumeration<NetworkInterface> en = NetworkInterface
+                    .getNetworkInterfaces(); en.hasMoreElements(); ) {
+                NetworkInterface intf = en.nextElement();
+                for (Enumeration<InetAddress> enumIpAddr = intf
+                        .getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
+                    InetAddress inetAddress = enumIpAddr.nextElement();
+                    if (!inetAddress.isLoopbackAddress()
+                            && !inetAddress.isLinkLocalAddress()
+                            && inetAddress.isSiteLocalAddress()) {
+                        return inetAddress.getHostAddress().toString();
+                    }
+                }
+            }
+        } catch (SocketException ex) {
+
+        }
+        return null;
+    }
+
+    /**
+     * 珥덇린 �뙣�뒪�썙�뱶瑜� �깮�꽦�븯�뿬 諛섑솚�븳�떎.
+     *
+     * @return
+     */
+
+    public static String randomStringMaker() {
+        String random = null;
+        Integer length = 10;
+        random = RandomStringUtils.random(length, true, true);
+
+        return random;
+    }
+
+    public static String randomProjectKey() {
+        String random = null;
+        Integer length = 5;
+        random = RandomStringUtils.random(length, true, true).toUpperCase();
+
+        return random;
+    }
+
+    //  sha 512 �븫�샇�솕
+    public static String encryptionSha512(String plainText) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-512");
+            byte[] messageDigest = md.digest(plainText.getBytes());
+            BigInteger no = new BigInteger(1, messageDigest);
+            String hashText = no.toString(16);
+            while (hashText.length() < 32) {
+                hashText = "0" + hashText;
+            }
+            return hashText;
+        } catch (Exception e) {
+            LOGGER.debug("encryptionSha512 error : " + e.getMessage());
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static String getServerIpAddress() {
+        try {
+            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+            while (interfaces.hasMoreElements()) {
+                NetworkInterface networkInterface = interfaces.nextElement();
+                if (networkInterface.isLoopback() || !networkInterface.isUp() || networkInterface.isVirtual() || networkInterface.isPointToPoint()) {
+                    continue;
+                }
+
+                Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
+                while (addresses.hasMoreElements()) {
+                    InetAddress inetAddress = addresses.nextElement();
+
+                    final String ip = inetAddress.getHostAddress();
+                    if (Inet4Address.class == inetAddress.getClass()) {
+                        return ip;
+                    }
+                }
+            }
+        } catch (SocketException e) {
+            throw new RuntimeException(e);
+        }
+        return null;
+    }
+
+    public static String likeStr(Map<String, Object> contents, String attr, LikeType type) {
+        if (contents == null) {
+            return "%%";
+        }
+
+        Object value = contents.get(attr);
+        if (value == null) {
+            return "%%";
+        }
+        return likeStr(value.toString(), type);
+    }
+
+
+    /**
+     * Query�뿉�꽌 �궗�슜�릺�뒗 Like 臾몄옄�뿴�쓣 留뚮뱺�떎.
+     *
+     * @param source
+     * @param type
+     * @return
+     */
+    public static String likeStr(String source, LikeType type) {
+        if (source == null) {
+            return "%%";
+        }
+        else {
+            if (type == LikeType.FULL) {
+                return "%" + source + "%";
+            }
+            else if (type == LikeType.LEFT) {
+                return "%" + source;
+            }
+            else if (type == LikeType.RIGHT) {
+                return source + "%";
+            }
+        }
+
+        return source;
+    }
+
+    public static String nvl(Object obj) {
+        if (obj == null) {
+            return "";
+        }
+        return obj.toString();
+    }
+
+    public static String nvl(String message, String defaultStr) {
+        if (message == null) {
+            return defaultStr;
+        }
+        return message;
+    }
+
+    public static String getApprovalCode() {
+        return UUID.randomUUID().toString();
+    }
+
+    public static String getOnlyClassName(Object object) {
+        String className = object.getClass().getName();
+
+        String[] tokens = className.split("\\.");
+        int lastToken = tokens.length - 1;
+        String onlyName = tokens[lastToken];
+
+        return onlyName;
+    }
+
+
+    /**
+     * �뿰寃� �떆�뒪�뀥�쓽 xml �삎�떇�쓽 String 媛믪쓣 �뙆�떛�빐�꽌 url留� �룎�젮以��떎.
+     *
+     * @param xml
+     * @return url String
+     */
+    public static String xmltoString(String checkElementName, String xml) {
+        // TODO Auto-generated method stub
+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        DocumentBuilder db = null;
+        try {
+            db = dbf.newDocumentBuilder();
+        } catch (ParserConfigurationException e1) {
+            // TODO Auto-generated catch block
+            e1.printStackTrace();
+        }
+        Document doc = null;
+        NodeList nodes = null;
+        InputSource is = new InputSource();
+        is.setCharacterStream(new StringReader(xml));
+        try {
+            doc = db.parse(is);
+        } catch (SAXException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        nodes = doc.getElementsByTagName(checkElementName);
+
+        return nodes.item(0).getTextContent();
+    }
+
+    public static long changeByteToMB(long size) {
+        return size / 1024 / 1024;
+    }
+
+    public static String getServerIp() {
+        try {
+            InetAddress address = InetAddress.getLocalHost();
+            return address.getHostAddress();
+        } catch (UnknownHostException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    /**
+     * �떎援��뼱 硫붿떆吏�瑜� 諛섑솚�븳�떎.
+     *
+     * @return
+     */
+    public static Map<String, String> getMessages() {
+        return getMessages(WebAppUtil.getHttpServletRequest().getServletContext().getRealPath("/"));
+    }
+
+    /**
+     * �떎援��뼱 硫붿떆吏�瑜� 諛섑솚�븳�떎.
+     *
+     * @param rootPath
+     * @return
+     */
+    public static Map<String, String> getMessages(String rootPath) {
+        Map<String, String> messages = new HashMap<String, String>();
+
+        String[] resources = new String[]{"messages.properties", "label.properties", "code.properties"};
+        for (String resource : resources) {
+            String resourcePath = rootPath + "/WEB-INF/i18n/" + resource;
+            LOGGER.debug("message path : resourcePath = " + resourcePath);
+
+            Properties prop = new Properties();
+            try {
+                prop.load(new FileInputStream(resourcePath));
+                Iterator iter = prop.keySet().iterator();
+                while (iter.hasNext()) {
+                    String key = iter.next().toString();
+                    messages.put(key, prop.get(key).toString());
+                }
+            } catch (FileNotFoundException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return messages;
+    }
+
+    public static String getDiagramObjectFormattedLocation(String location) {
+        if (location == null) {
+            return null;
+        }
+
+        if (location.indexOf(".") != -1) {
+            return location.substring(0, location.indexOf("."));
+        }
+        else {
+            return location;
+        }
+    }
+
+    public static String replaceBrWithNewline(String text) {
+        org.jsoup.nodes.Document document = Jsoup.parse(text);
+        document.select("br").append("\\n");
+        document.select("p").prepend("\\n\\n");
+        return document.text().replace("\\n", "\n");
+    }
+
+    //  UUID 濡� 寃뱀튂吏� �븡�뒗 �궎瑜� 留뚮뱾�뼱 �궦�떎.
+    public static String getFileNameByUUID(String originalFileName) {
+        String fileName = UUID.randomUUID().toString();
+        String[] fileType = originalFileName.split("\\.");
+
+        fileName += "." + fileType[(fileType.length - 1)];
+
+        return fileName;
+    }
+
+
+    //  multipartFile �젙蹂대�� file Map �삎�깭濡� 蹂�寃쏀븳�떎.
+    public static Map<String, Object> makeFileMap(MultipartFile multipartFile) {
+        Map<String, Object> fileMap = new HashMap<>();
+
+        try {
+            fileMap.put("fileName", multipartFile.getOriginalFilename());
+            fileMap.put("fileSize", multipartFile.getSize());
+            fileMap.put("contentType", multipartFile.getContentType());
+            fileMap.put("file", CommonUtil.multipartToFile(multipartFile));
+        } catch (Exception e) {
+            LOGGER.debug(e.getMessage());
+        }
+
+        return fileMap;
+    }
+
+    public static String getPostDataString(Map<String, String> params) throws UnsupportedEncodingException {
+        StringBuilder result = new StringBuilder();
+        boolean first = true;
+        for (Map.Entry<String, String> entry : params.entrySet()) {
+            if (first) {
+                first = false;
+            }
+            else {
+                result.append("&");
+            }
+
+            result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
+            result.append("=");
+            result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
+        }
+
+        return result.toString();
+    }
+
+    //  multipart �뙆�씪�쓣 File 濡� 蹂�寃�
+    public static File multipartToFile(MultipartFile multipart) {
+        File convertFile = new File(WebAppUtil.getContextRealPath() + TMP_UPLOAD_FOLDER + getFileNameByUUID(multipart.getOriginalFilename()));
+        if (!convertFile.exists()) {
+            convertFile.mkdirs();
+        }
+        try {
+            multipart.transferTo(convertFile);
+        } catch (IllegalStateException | IOException e) {
+            LOGGER.debug("multipart �뙆�씪 file 蹂��솚 �삤瑜�");
+        }
+
+        return convertFile;
+    }
+
+    public static InputStream getFileInputStream(String strPath, String strNewFileName){
+        InputStream objInputStream = null;
+
+        String strTargetPath = WebAppUtil.getContextRealPath() + strPath +  "/" + strNewFileName;
+
+        File originFile = new File(strTargetPath);
+
+        try {
+            objInputStream = new FileInputStream(originFile);
+        } catch (Exception e) {
+            LOGGER.debug("get Inputstream Error : " + e.getMessage());
+        }
+
+        return objInputStream;
+    }
+
+    public static boolean moveToSaveStorage(String strBasePath, String strNewFileName, File tempFile) {
+        boolean bRet = false;
+
+        String strTargetBasePath = WebAppUtil.getContextRealPath() + strBasePath;
+        strTargetBasePath = Paths.get(strTargetBasePath).toString();
+
+        String strTargetPath = strTargetBasePath +  "/" + strNewFileName;
+
+        File directory = new File(strTargetBasePath);
+        if (! directory.exists()){
+            try {
+                directory.mkdirs();
+            } catch (Exception e) {
+                LOGGER.debug("make directory error : " + e.getMessage());
+            }
+        }
+
+        bRet = tempFile.renameTo(new File(strTargetPath));
+
+        if( !bRet )
+        {
+            LOGGER.debug("file �씠�룞 �삤瑜�");
+            LOGGER.debug("original file Path : " + tempFile.getPath());
+            LOGGER.debug("target file Path : " + strTargetPath);
+        }
+
+        return bRet;
+    }
+
+    public static boolean deleteToSaveStorage(String strBasePath, String strFileName) {
+        boolean bRet = false;
+        String strTargetBasePath = WebAppUtil.getContextRealPath() + strBasePath;
+        String strTargetPath = strTargetBasePath + strFileName;
+
+        File delFile = new File(strTargetBasePath);
+        if ( delFile.exists()){
+            if(!delFile.isDirectory())
+            {
+                bRet = delFile.delete();
+            }
+        }
+
+        if( !bRet )
+        {
+            LOGGER.debug("file �궘�젣 �삤瑜�");
+            LOGGER.debug("original file Path : " + strTargetPath);
+        }
+
+        return bRet;
+    }
+
+    //  �뙆�씪 �솗�옣�옄 泥댄겕
+    //  1. 紐⑤뱺 �솗�옣�옄瑜� �젣�븳�븯怨� �뿀�슜�븯�뒗 �씪遺� �솗�옣�옄留� �뾽濡쒕뱶 �릺�룄濡� �젣�븳
+    //  2. �솗�옣�옄�뒗 �뮘�뿉�꽌遺��꽣 泥댄겕�븳�떎.
+    //  3. �듅�닔臾몄옄 泥댄겕
+    public static boolean checkFileType(String originalFileName) {
+        boolean permit = false;
+
+        if (StringUtils.isEmpty(originalFileName)) {
+            return permit;
+        }
+
+        String[] extFileTypes = {"hwp", "txt", "pptx", "ppt", "pdf", "xlsx", "xls", "docx", "doc", "jpg", "png", "gif", "jpeg", "tif", "bmp", "wmv", "avi", "mp4", "mkv", "mov", "zip"};
+        String fileName = originalFileName.toLowerCase();
+        int pos = fileName.lastIndexOf(".");
+        String ext = fileName.substring(pos + 1);
+
+        for (String extFileType : extFileTypes) {
+            if (extFileType.equals(ext)) {
+                permit = true;
+                break;
+            }
+        }
+
+        if (fileName.contains("%00") || fileName.contains("%zz") || fileName.contains(";")) {
+            permit = false;
+        }
+
+        return permit;
+    }
+
+    //  �뙆�씪 �쑀�삎�쓣 李얜뒗�떎.
+    public static FileType getFileType(String originalFileName) {
+        FileType fileType;
+
+        Map<String, Object> fileTypeMap = new HashMap<>();
+        fileTypeMap.put("hwp", FileType.DOC);
+        fileTypeMap.put("txt", FileType.DOC);
+        fileTypeMap.put("pptx", FileType.DOC);
+        fileTypeMap.put("ppt", FileType.DOC);
+        fileTypeMap.put("pdf", FileType.DOC);
+        fileTypeMap.put("xlsx", FileType.DOC);
+        fileTypeMap.put("xls", FileType.DOC);
+        fileTypeMap.put("docx", FileType.DOC);
+        fileTypeMap.put("doc", FileType.DOC);
+        //  臾몄꽌 �걹
+        fileTypeMap.put("jpg", FileType.IMAGE);
+        fileTypeMap.put("png", FileType.IMAGE);
+        fileTypeMap.put("gif", FileType.IMAGE);
+        fileTypeMap.put("jpeg", FileType.IMAGE);
+        fileTypeMap.put("tif", FileType.IMAGE);
+        fileTypeMap.put("bmp", FileType.IMAGE);
+        //  �씠誘몄� �걹
+        fileTypeMap.put("wmv", FileType.MEDIA);
+        fileTypeMap.put("avi", FileType.MEDIA);
+        fileTypeMap.put("mp4", FileType.MEDIA);
+        fileTypeMap.put("mkv", FileType.MEDIA);
+        fileTypeMap.put("mov", FileType.MEDIA);
+        //  誘몃뵒�뼱 �걹
+
+        String fileName = originalFileName.toLowerCase();
+        int pos = fileName.lastIndexOf(".");
+        String ext = fileName.substring(pos + 1);
+
+        if (fileTypeMap.get(ext) != null) {
+            fileType = (FileType) fileTypeMap.get(ext);
+        }
+        else {
+            fileType = FileType.ETC;
+        }
+
+        return fileType;
+    }
+
+    //  �씠由� 留덉뒪�궧 泥섎━(�꽦�쓣 �젣�쇅�븳 �씠由� 留덉뒪�궧 泥섎━)
+    public static String maskingName(String name) {
+        String maskedName = "";    // 留덉뒪�궧 �씠由�
+        String firstName = "";    // �꽦
+        String lastName = "";    // �씠由�
+        int lastNameStartPoint;    // �씠由� �떆�옉 �룷�씤�꽣
+
+        if (!StringUtils.isEmpty(name)) {
+            if (name.length() > 1) {
+                firstName = name.substring(0, 1);
+                lastNameStartPoint = name.indexOf(firstName);
+                lastName = name.substring(lastNameStartPoint + 1, name.length());
+
+                String makers = "";
+
+                for (int i = 0; i < lastName.length(); i++) {
+                    makers += "*";
+                }
+
+                lastName = lastName.replace(lastName, makers);
+                maskedName = firstName + lastName;
+            }
+            else {
+                maskedName = "*";
+            }
+        }
+
+        return maskedName;
+    }
+
+    //  AES 128 �븫�샇�솕
+    public static String encryptAES128(String plaintext) {
+        if (StringUtils.isEmpty(plaintext)) {
+            return null;
+        }
+
+        try {
+            SecretKey key = generateKey(SALT, PASS_PHRASE, ITERATION_COUNT, KEY_SIZE);
+            byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, IV, plaintext.getBytes("UTF-8"));
+            return encodeBase64(encrypted);
+        } catch (Exception e) {
+            LOGGER.debug("encryptAES128 error : " + e.getMessage());
+        }
+
+        return null;
+    }
+
+    private static String encodeBase64(byte[] bytes) {
+        return org.apache.commons.codec.binary.Base64.encodeBase64String(bytes);
+    }
+
+    private static SecretKey generateKey(String salt, String passPhrase, int iterationCount, int keySize) throws Exception {
+        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
+        KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), decodeHex(salt), iterationCount, keySize);
+        SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
+        return key;
+    }
+
+    private static byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) throws Exception {
+        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+        cipher.init(encryptMode, key, new IvParameterSpec(decodeHex(iv)));
+        return cipher.doFinal(bytes);
+    }
+
+    private static byte[] decodeHex(String str) throws Exception {
+        return Hex.decodeHex(str.toCharArray());
+    }
+
+    //  AES 128 蹂듯샇�솕
+    public static String decryptAES128(String text) {
+        try {
+            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
+            KeySpec spec = new PBEKeySpec(PASS_PHRASE.toCharArray(), Hex.decodeHex(SALT.toCharArray()), ITERATION_COUNT, KEY_SIZE);
+            SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
+            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(Hex.decodeHex(IV.toCharArray())));
+            byte[] decrypted = cipher.doFinal(Base64.decodeBase64(text));
+            return new String(decrypted, ENCODING_TYPE);
+        } catch (Exception e) {
+            LOGGER.debug("decryptAES128 error : " + e.getMessage());
+        }
+
+        return null;
+    }
+
+    //  AES 256 �븫�샇�솕
+    /*public static String encryptAES256(String msg, String key) throws Exception {
+        SecureRandom random = new SecureRandom();
+        byte bytes[] = new byte[20];
+        random.nextBytes(bytes);
+        byte[] saltBytes = bytes;
+        // Password-Based Key Derivation function 2
+        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
+        // 70000踰� �빐�떆�븯�뿬 256 bit 湲몄씠�쓽 �궎瑜� 留뚮뱺�떎.
+        PBEKeySpec spec = new PBEKeySpec(key.toCharArray(), saltBytes, 70000, 256);
+        SecretKey secretKey = factory.generateSecret(spec);
+        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
+        // �븣怨좊━利�/紐⑤뱶/�뙣�뵫
+        // CBC : Cipher Block Chaining Mode
+        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+        cipher.init(Cipher.ENCRYPT_MODE, secret);
+        AlgorithmParameters params = cipher.getParameters();
+        // Initial Vector(1�떒怨� �븫�샇�솕 釉붾줉�슜)
+        byte[] ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
+        byte[] encryptedTextBytes = cipher.doFinal(msg.getBytes("UTF-8"));
+        byte[] buffer = new byte[saltBytes.length + ivBytes.length + encryptedTextBytes.length];
+        System.arraycopy(saltBytes, 0, buffer, 0, saltBytes.length);
+        System.arraycopy(ivBytes, 0, buffer, saltBytes.length, ivBytes.length);
+        System.arraycopy(encryptedTextBytes, 0, buffer, saltBytes.length + ivBytes.length, encryptedTextBytes.length);
+        return Base64.getEncoder().encodeToString(buffer);
+    }
+
+    //  AES 256 蹂듯샇�솕
+    public static String decryptAES256(String msg, String key) throws Exception {
+        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+        ByteBuffer buffer = ByteBuffer.wrap(Base64.getDecoder().decode(msg));
+        byte[] saltBytes = new byte[20];
+        buffer.get(saltBytes, 0, saltBytes.length);
+        byte[] ivBytes = new byte[cipher.getBlockSize()];
+        buffer.get(ivBytes, 0, ivBytes.length);
+        byte[] encryoptedTextBytes = new byte[buffer.capacity() - saltBytes.length - ivBytes.length];
+        buffer.get(encryoptedTextBytes);
+        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
+        PBEKeySpec spec = new PBEKeySpec(key.toCharArray(), saltBytes, 70000, 256);
+        SecretKey secretKey = factory.generateSecret(spec);
+        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
+        cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));
+        byte[] decryptedTextBytes = cipher.doFinal(encryoptedTextBytes);
+        return new String(decryptedTextBytes);
+    }*/
+
+    //  �뿊�� �떎�슫濡쒕뱶�뿉 �븘�슂�븳 寃��깋 議곌굔 �젙蹂대�� 異붿텧�븳�떎.
+    public static Map<String, Object> getSearchConditions(HttpServletRequest request) throws IOException {
+        Map<String, Object> conditions = new HashMap<>();
+        Map<String, Object> params = new HashMap<>();
+
+        Enumeration enumeration = request.getParameterNames();
+        while (enumeration.hasMoreElements()) {
+            String name = (String) enumeration.nextElement();
+            if (!org.springframework.util.StringUtils.isEmpty(request.getParameter(name))) {
+                params.put(name, request.getParameter(name));
+            }
+        }
+
+        if (!StringUtils.isEmpty(MapUtil.getString(params, "conditions"))) {
+            ObjectMapper mapper = new ObjectMapper();
+            conditions = mapper.readValue(MapUtil.getString(params, "conditions"), new TypeReference<Map<String, Object>>() {
+            });
+        }
+
+        return conditions;
+    }
+
+    //  UserVos �뿉�꽌 �궗�슜�옄 �젙蹂대�� 異붿텧�빐�꽌 臾몄옄�뿴濡� 由ы꽩�빐以��떎. - 二쇰줈 �뿊�� download �뿉�꽌 �궗�슜�맂�떎.
+    public static String convertUserVosToString(List<UserVo> userVos) {
+        StringBuilder stringBuilder = new StringBuilder();
+        int count = 0;
+
+        for (UserVo userVo : userVos) {
+            if (count > 0) {
+                stringBuilder.append("\n");
+            }
+
+            stringBuilder.append(userVo.getName());
+            stringBuilder.append("(");
+            stringBuilder.append(userVo.getAccount());
+            stringBuilder.append(")");
+            count++;
+        }
+
+        return stringBuilder.toString();
+    }
+
+    //  寃��깋 �씪�옄瑜� 援ы븳�떎.
+    public static List<Date> findSearchPeriod(String searchPeriod) {
+        List<Date> searchDates = Lists.newArrayList();
+
+        switch (searchPeriod) {
+            case DateUtil.THIS_WEEK:
+                searchDates = DateUtil.getSearchDays(DateUtil.THIS_WEEK);
+                break;
+            case DateUtil.LAST_WEEK:
+                searchDates = DateUtil.getSearchDays(DateUtil.LAST_WEEK);
+                break;
+            case DateUtil.LAST_SEVEN_DAYS:
+                searchDates = DateUtil.getSearchDays(DateUtil.LAST_SEVEN_DAYS);
+                break;
+            case DateUtil.THIS_MONTH:
+                searchDates = DateUtil.getSearchDays(DateUtil.THIS_MONTH);
+                break;
+            case DateUtil.LAST_MONTH:
+                searchDates = DateUtil.getSearchDays(DateUtil.LAST_MONTH);
+                break;
+            case DateUtil.LAST_THIRTY_DAYS:
+                searchDates = DateUtil.getSearchDays(DateUtil.LAST_THIRTY_DAYS);
+                break;
+        }
+
+        return searchDates;
+    }
+
+    //  �겢�씪�씠�뼵�듃�쓽 釉뚮씪�슦�� �젙蹂대�� 李얜뒗�떎.
+    public static String getBrowser(HttpServletRequest request) {
+        String header = request.getHeader("User-Agent");
+        if (header.contains("MSIE") || header.contains("Trident")) {
+            return "MSIE";
+        }
+        else if (header.indexOf("Chrome") > -1) {
+            return "Chrome";
+        }
+        else if (header.indexOf("Opera") > -1) {
+            return "Opera";
+        }
+        return "Firefox";
+    }
+
+    //  釉뚮씪�슦�� 蹂꾨줈 �뙆�씪 怨듬갚�쓣 泥섎━�븳�떎.
+    public static String getDisposition(String filename, String browser) throws Exception {
+        String encodedFilename = null;
+
+        switch (browser) {
+            case "MSIE":
+            case "Trident":
+                encodedFilename = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
+                break;
+            case "Firefox":
+                encodedFilename = "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
+                break;
+            case "Opera":
+                encodedFilename = "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
+                break;
+            case "Chrome":
+                StringBuilder sb = new StringBuilder();
+                for (int i = 0; i < filename.length(); i++) {
+                    char c = filename.charAt(i);
+                    if (c > '~') {
+                        sb.append(URLEncoder.encode("" + c, "UTF-8"));
+                    }
+                    else {
+                        sb.append(c);
+                    }
+                }
+                encodedFilename = sb.toString();
+                break;
+        }
+
+        return encodedFilename;
+    }
+
+    //  XSS 怨듦꺽�쓣 留됯린 �쐞�빐 �뒪�겕由쏀듃 �깭洹몃�� �뾾�븻�떎. - �씠�뒋 �깮�꽦, �닔�젙, �긽�꽭�뿉�꽌 �깭洹몃�� �궡�젮�빞 �븯湲� �븣臾몄뿉 �뒪�겕由쏀듃留� 留됰뒗�떎.
+    public static String preventXss(String dirty) {
+        dirty = dirty.replaceAll("<script", "");
+        dirty = dirty.replaceAll("</script", "");
+
+        return dirty;
+    }
+
+    //  InputStream �쓣 String �쑝濡� 蹂��솚
+    public static String getStringToInputStream(int responseCode, InputStream inputStream) {
+        StringBuffer response = new StringBuffer();
+        BufferedReader br = null;
+
+        try {
+            // �젙�긽 �샇異�
+            if (responseCode == HttpsURLConnection.HTTP_OK) {
+                br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
+                String inputLine;
+                while ((inputLine = br.readLine()) != null) {
+                    response.append(inputLine);
+                }
+            }
+        } catch (IOException e) {
+            LOGGER.debug("CommonUtil.getStringToInputStream Error " + e.getMessage());
+        } finally {
+            if (br != null) {
+                try {
+                    br.close();
+                } catch (IOException e) {
+                    LOGGER.debug("CommonUtil.getStringToInputStream Error " + e.getMessage());
+                }
+            }
+        }
+
+        return response.toString();
+    }
+
+    //  �닽�옄 �씤吏� �뿬遺� �솗�씤
+    public static boolean isNumeric(String str) {
+        try {
+            Double.parseDouble(str);
+            return true;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
+    //  �몢 諛곗뿴�뿉�꽌 寃뱀튂吏� �븡�뒗 �빆紐⑹쓣 李얠븘�궦�떎.
+    public static List<Long> searchChangeList(List<Long> targetIds, List<Long> searchIds) {
+        List<Long> results = Lists.newArrayList();
+
+        for (Long searchId : searchIds) {
+            boolean exist = false;
+
+            for (Long targetId : targetIds) {
+                if (searchId.equals(targetId)) {
+                    exist = true;
+                    break;
+                }
+            }
+
+            if (!exist) {
+                results.add(searchId);
+            }
+        }
+
+        return results;
+    }
+
+    //  硫붿씪�쓣 諛쏅뒗 �궗�슜�옄媛� �궗�슜�븯怨� �엳�뒗 �뼵�뼱 �젙蹂대�� 媛��졇�삩�떎.
+    public static Locale getUserLanguage(String language) {
+        Locale locale;
+
+        switch (language) {
+            case "ko":
+                locale = new Locale("ko", "KR");
+                break;
+            case "en":
+                locale = new Locale("en", "US");
+                break;
+            case "ja":
+                locale = new Locale("ja", "JP");
+                break;
+            case "vi":
+                locale = new Locale("vi", "VN");
+                break;
+            default:
+                locale = new Locale("ko", "KR");
+        }
+
+        return locale;
+    }
+
+    //  �닽�옄�뿉 3�옄由� 留덈떎 , 瑜� 李띿뼱以��떎
+    public static String getDecimalFormat(Object object) {
+        DecimalFormat decimalFormat = new DecimalFormat("###,###");
+        return decimalFormat.format(object);
+    }
+
+    //  �뿊�� import �뜲�씠�꽣�뿉�꽌 cell 媛믪쓣 臾몄옄�뿴濡� 蹂��솚�븳�떎.
+    public static String convertExcelStringToCell(Cell cell) {
+        String cellValue = "";
+
+        switch (cell.getCellType()) {
+            case Cell.CELL_TYPE_NUMERIC :
+                double doubleValue = cell.getNumericCellValue();
+                int intValue;
+
+                if (doubleValue%1 == 0) {
+                    intValue = (int)doubleValue;
+                    cellValue = intValue + "";
+                }
+                else {
+                    cellValue = doubleValue + "";
+                }
+
+                break;
+
+            case Cell.CELL_TYPE_STRING :
+                cellValue = cell.getStringCellValue();
+                break;
+
+            case Cell.CELL_TYPE_FORMULA :
+                cellValue = cell.getCellFormula() + "";
+                break;
+
+            case Cell.CELL_TYPE_BOOLEAN :
+                cellValue = cell.getBooleanCellValue() + "";
+                break;
+        }
+
+        return cellValue;
+    }
+
+    //  �쎒 �냼耳� 硫붿꽭吏� 諛쒖넚
+    public static void sendWebSocketMessage(SimpMessagingTemplate simpMessagingTemplate, Map<String, Object> messageMap) {
+        String url = MapUtil.getString(messageMap, "url");
+        String content = MapUtil.getString(messageMap, "message");
+        String account = MapUtil.getString(messageMap, "account");
+
+        if (StringUtils.isEmpty(url) || StringUtils.isEmpty(content)) {
+            LOGGER.error("Fail WebSocket Message - url or content empty");
+            return;
+        }
+
+        if (!StringUtils.isEmpty(account)) {
+            simpMessagingTemplate.convertAndSendToUser(account, url, content);
+        }
+        else {
+            simpMessagingTemplate.convertAndSend(url, content);
+        }
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/util/ConvertUtil.java b/src/main/java/kr/wisestone/owl/util/ConvertUtil.java
new file mode 100644
index 0000000..cc3d63a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/util/ConvertUtil.java
@@ -0,0 +1,552 @@
+package kr.wisestone.owl.util;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import org.apache.commons.lang3.ArrayUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+public class ConvertUtil {
+    static final Logger LOGGER = LoggerFactory.getLogger(ConvertUtil.class);
+
+    /**
+     * 留� 紐⑸줉�쓣 二쇱뼱吏� �삤釉뚯젥�듃 紐⑸줉�쑝濡� 蹂듭궗�븳�떎.
+     *
+     * @param sources
+     * @param clazz
+     * @param ignores
+     * @return
+     */
+    public static <T> List<T> convertListToListClass(List<Map<String, Object>> sources,
+            Class<T> clazz, String... ignores) {
+        List<T> targets = new ArrayList<T>();
+
+        for (Map<String, Object> source : sources) {
+            targets.add(convertMapToClass(source, clazz, ignores));
+        }
+
+        return targets;
+    }
+
+    public static <T> List<T> convertListToListClass(List<Map<String, Object>> sources,
+            Class<T> clazz) {
+        return convertListToListClass(sources, clazz, new String[] {});
+    }
+
+    /**
+     * 留� �삤釉뚯젥�듃瑜� 二쇱뼱吏� �겢�옒�뒪 �삤釉뚯젥�듃�쓽 �븘�뱶濡� 蹂듭궗�븳�떎.
+     *
+     * @param source
+     * @param clazz
+     * @param ignores
+     *            留ㅽ븨�뿉�꽌 �젣�쇅�븷 �븘�뱶
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T convertMapToClass(Map<String, Object> source, Class<T> clazz, String... ignores) {
+        Object objClass = null;
+        try {
+            objClass = clazz.newInstance();
+        } catch (Exception e) {
+            LOGGER.error("", e);
+        }
+        convertMapToObject(source, objClass, ignores);
+
+        return (T) objClass;
+    }
+
+    /**
+     * 留� �삤釉뚯젥�듃瑜� 二쇱뼱吏� �삤釉뚯젥�듃�쓽 �븘�뱶濡� 蹂듭궗�븳�떎.
+     *
+     * @param map
+     * @param objClass
+     * @param ignores
+     * @return
+     */
+    public static Object convertMapToObject(Map<String, Object> map,
+            Object objClass, String... ignores) {
+        String keyAttribute = null;
+        Iterator<String> itr = map.keySet().iterator();
+        while (itr.hasNext()) {
+            keyAttribute = itr.next();
+
+            if (ArrayUtils.contains(ignores, keyAttribute)) {
+                continue;
+            }
+            if (map.get(keyAttribute) == null) {
+                continue;
+            }
+
+            PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(objClass.getClass(), keyAttribute);
+            try {
+                Method method = propertyDescriptor.getWriteMethod();
+
+                if (propertyDescriptor.getPropertyType() == String.class) {
+                    //  xss 諛⑹뼱瑜� �쐞�빐 script 臾몄옄 怨듬갚 移섑솚
+                    method.invoke(objClass, CommonUtil.preventXss(map.get(keyAttribute).toString()));
+                } else if (propertyDescriptor.getPropertyType() == Integer.class) {
+                    method.invoke(objClass, new Integer(map.get(keyAttribute).toString()));
+                } else if (propertyDescriptor.getPropertyType() == Long.class) {
+                    method.invoke(objClass, Long.valueOf(map.get(keyAttribute).toString()));
+                }
+                else if (propertyDescriptor.getPropertyType() == Double.class) {
+	                method.invoke(objClass, Double.valueOf(map.get(keyAttribute).toString()));
+                }
+                else if (propertyDescriptor.getPropertyType() == Boolean.class) {
+                    if ("Y".equalsIgnoreCase(map.get(keyAttribute).toString())) {
+                        method.invoke(objClass, Boolean.TRUE);
+                    } if ("true".equalsIgnoreCase(map.get(keyAttribute).toString())) {
+                        method.invoke(objClass, Boolean.TRUE);
+                    } else {
+                        method.invoke(objClass, Boolean.FALSE);
+                    }
+                }
+            } catch (Exception e) {
+//                e.printStackTrace();
+            }
+        }
+        return objClass;
+    }
+
+    /**
+     * 二쇱뼱吏� �삤釉뚯젥�듃 紐⑸줉(List)�쓣 二쇱뼱吏� �겢�옒�뒪 紐⑸줉�쑝濡� 蹂듭궗�븳�떎.
+     *
+     * @param sources
+     * @param targetClass
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <S, T> List<T> convertObjectsToClasses(
+            List<S> sources, Class<T> targetClass) {
+        List<T> targets = new ArrayList<T>();
+        Object target = null;
+
+        Iterator<S> iterator = sources.iterator();
+        while (iterator.hasNext()) {
+            S source = iterator.next();
+
+            try {
+                target = targetClass.newInstance();
+                copyProperties(source, target);
+
+                targets.add((T) target);
+            } catch (Exception e) {
+                throw new OwlRuntimeException(e);
+            }
+        }
+
+        return targets;
+    }
+
+    /**
+     * 二쇱뼱吏� �삤釉뚯젥�듃 紐⑸줉(Set)�쓣 二쇱뼱吏� �겢�옒�뒪 紐⑸줉�쑝濡� 蹂듭궗�븳�떎.
+     *
+     * @param sources
+     * @param targetClass
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <S, T> List<T> convertObjectsToClasses(
+            Set<S> sources, Class<T> targetClass) {
+        List<T> targets = new ArrayList<T>();
+        Object target = null;
+
+        Iterator<S> iterator = sources.iterator();
+        while (iterator.hasNext()) {
+            S source = iterator.next();
+
+            try {
+                target = targetClass.newInstance();
+                copyProperties(source, target);
+
+                targets.add((T) target);
+            } catch (Exception e) {
+                throw new OwlRuntimeException(e);
+            }
+        }
+
+        return targets;
+    }
+
+    /**
+     * �삤釉뚯젥�듃 �냽�꽦�쓣 留듭뿉 蹂듭궗�븯�뿬 留듭쓣 諛섑솚�븳�떎.
+     *
+     * @param source
+     * @return
+     */
+    public static Map<String, Object> convertObjectToMap(Object source) {
+        return convertObjectToMap(source, new String[] {});
+    }
+
+    /**
+     * �삤釉뚯젥�듃 �냽�꽦�뿉�꽌 ignores �븘�뱶瑜� �젣�쇅�븯怨� 留듭뿉 蹂듭궗�븯�뿬 留듭쓣 諛섑솚�븳�떎.
+     *
+     * @param source
+     * @param ignores
+     * @return
+     */
+    public static Map<String, Object> convertObjectToMap(Object source, String...ignores) {
+        Map<String, Object> target = new HashMap<String, Object>();
+
+        if (source == null) {
+            throw new OwlRuntimeException(MsgConstants.SOURCE_OBJECT_IS_NULL);
+        }
+
+        try {
+            copyAttrSourceToTarget(source, target, ignores);
+        } catch (Exception e) {
+            throw new OwlRuntimeException(MsgConstants.ERR_FAILED_CONVERT_OBJECT, e);
+        }
+
+        return target;
+    }
+
+    private static void copyAttrSourceToTarget(Object source, Object target,
+            String... ignores) {
+        copyAttrSourceToTarget(source, target, false, ignores);
+    }
+
+    /**
+     * �냼�뒪 �삤釉뚯젥�듃 �냽�꽦�뿉�꽌 ignores �븘�뱶瑜� �젣�쇅�븯怨� ��寃� �삤釉뚯젥�듃濡� �냽�꽦�쓣 蹂듭궗�븳�떎.
+     *
+     * @param source
+     * @param target
+     * @param ignores
+     */
+    @SuppressWarnings("unchecked")
+    private static void copyAttrSourceToTarget(Object source, Object target, boolean chkEqual,
+            String... ignores) {
+        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(source.getClass());
+        if (propertyDescriptors == null || propertyDescriptors.length == 0) {
+            LOGGER.debug("�냽�꽦�씠 議댁옱�븯吏� �븡�뒗�떎. : " + source.getClass().getName());
+            return;
+        }
+
+        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
+            if (ignores != null && ignores.length > 0) {
+                if (ArrayUtils.contains(ignores, propertyDescriptor.getName())) {
+                    LOGGER.debug("臾댁떆�맆 �븘�뱶 �빆紐⑹씠�떎 : " + propertyDescriptor.getName());
+                    continue;
+                }
+            }
+
+            if (propertyDescriptor.getPropertyType() == Integer.class ||
+		        propertyDescriptor.getPropertyType() == Double.class    ||
+                propertyDescriptor.getPropertyType() == Long.class    ||
+                propertyDescriptor.getPropertyType() == Boolean.class ||
+                propertyDescriptor.getPropertyType() == Date.class ||
+                propertyDescriptor.getPropertyType() == String.class) {
+                if (target instanceof Map) {
+                    if (copyAttrObjectToMap(source, propertyDescriptor.getName(), (Map<String, Object>) target) == false) {
+                        continue;
+                    }
+                } else {
+                    if (copyAttrObjectToObject(source, propertyDescriptor.getName(), target, chkEqual) == false) {
+                        continue;
+                    }
+                }
+            } else {
+//                LogUtil.debug(LOGGER, field.getName() + "(" + field.getType().getName() + ")�� 吏��썝 ���엯�씠 �븘�땲�떎.");
+            }
+        }
+    }
+
+    /**
+     * 二쇱뼱吏� ���엯�쓽 紐⑤뱺 �냽�꽦(�븘�뱶)�쓣 諛섑솚�븳�떎.
+     *
+     * @param fields
+     * @param type
+     * @return
+     */
+    public static List<Field> getAllFields(List<Field> fields, Class<?> type) {
+        for (Field field: type.getDeclaredFields()) {
+            fields.add(field);
+        }
+
+        if (type.getSuperclass() != null) {
+            fields = getAllFields(fields, type.getSuperclass());
+        }
+
+        return fields;
+    }
+
+    /**
+     * 二쇱뼱吏� �삤釉뚯젥�듃�뿉�꽌 二쇱뼱吏� �븘�뱶瑜� 留듭뿉 蹂듭궗�븳�떎.
+     *
+     * @param source
+     * @param fieldName
+     * @param target
+     *
+     * @return
+     */
+    private static boolean copyAttrObjectToMap(Object source, String fieldName, Map<String, Object> target) {
+        PropertyDescriptor sourcePropertyDescriptor = BeanUtils.getPropertyDescriptor(source.getClass(), fieldName);
+
+        Method get = null;
+        try {
+            get = sourcePropertyDescriptor.getReadMethod();
+        } catch (Exception e) {
+            return false;
+        }
+
+        Object value = null;
+        try {
+            value = get.invoke(source, new Object[] {});
+        } catch (Exception e) {
+            throw new OwlRuntimeException(MsgConstants.ERR_FAILED_CONVERT_OBJECT, e);
+        }
+        if (value != null) {
+            if (value instanceof Date) {
+                target.put(fieldName, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) value));
+            } else {
+                target.put(fieldName, value);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * �냼�뒪 �삤釉뚯젥�듃�뿉�꽌 二쇱뼱吏� �븘�뱶瑜� ��寃� �삤釉뚯젥�듃�뿉 蹂듭궗�븳�떎.
+     *
+     * @param source
+     * @param fieldName
+     * @param target
+     *
+     * @return
+     */
+    private static boolean copyAttrObjectToObject(Object source, String fieldName, Object target, boolean chkEqual) {
+        Object sourceValue = null;
+
+        PropertyDescriptor sourcePropertyDescriptor = BeanUtils.getPropertyDescriptor(source.getClass(), fieldName);
+        PropertyDescriptor targetPropertyDescriptor = BeanUtils.getPropertyDescriptor(target.getClass(), fieldName);
+
+        if (sourcePropertyDescriptor == null || targetPropertyDescriptor == null) {
+            return false;
+        }
+
+        Method set = null;
+        Method sourceGetMethod = null;
+        Method targetGetMethod = null;
+        try {
+            sourceGetMethod = sourcePropertyDescriptor.getReadMethod();
+            targetGetMethod = targetPropertyDescriptor.getReadMethod();
+
+            sourceValue = sourceGetMethod.invoke(source, new Object[] {});
+            if (chkEqual) {
+                Object targetValue = targetGetMethod.invoke(target, new Object[] {});
+
+                if (sourceValue == null && targetValue == null) {
+                    return true;
+                } else if (sourceValue != null && sourceValue.equals(targetValue)) {
+                    return true;
+                } else if (targetValue != null && targetValue.equals(sourceValue)) {
+                    return true;
+                }
+            }
+        } catch (Exception e) {
+            LOGGER.error("fieldName : " + fieldName, e);
+            return false;
+        }
+
+        try {
+            if (sourcePropertyDescriptor.getPropertyType() == Date.class) {
+                set = target.getClass().getMethod(targetPropertyDescriptor.getWriteMethod().getName(), String.class);
+            } else {
+                set = targetPropertyDescriptor.getWriteMethod();
+            }
+        } catch (Exception e) {
+            LOGGER.error("", e);
+            return false;
+        }
+
+
+        try {
+            if (sourceValue != null) {
+                LOGGER.debug("sourceValue : " + sourceValue);
+                LOGGER.debug("Set Method : " + set.toString());
+
+                if (sourcePropertyDescriptor.getPropertyType() == Date.class) {
+                    set.invoke(target, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) sourceValue));
+                } else {
+                    set.invoke(target, sourceValue);
+                }
+            }
+        } catch (Exception e) {
+            throw new OwlRuntimeException(MsgConstants.ERR_FAILED_CONVERT_OBJECT, e);
+        }
+
+        return true;
+    }
+
+    /**
+     * �냼�뒪 �삤釉뚯젥�듃�뿉�꽌 Primitive Type(ignores �젣�쇅)留� ��寃� �겢�옒�뒪 �삤釉뚯젥�듃濡� 蹂듭궗�븳�떎.
+     *
+     * @param source
+     * @param targetClass
+     * @param ignores
+     * @return
+     */
+    public static <T> T copyProperties(Object source, Class<T> targetClass, String...ignores) {
+        T target = null;
+        try {
+            target = targetClass.newInstance();
+        } catch (Exception e) {
+            throw new OwlRuntimeException(MsgConstants.ERR_FAILED_CONVERT_OBJECT, e);
+        }
+        copyProperties(source, target, true, ignores);
+
+        return target;
+    }
+
+    public static void copyProperties(Object source, Object target, boolean chkEqual, String...ignores) {
+
+        if (source == null) {
+            throw new OwlRuntimeException(MsgConstants.SOURCE_OBJECT_IS_NULL);
+        }
+        if (target == null) {
+            throw new OwlRuntimeException(MsgConstants.TARGET_OBJECT_IS_NULL);
+        }
+
+        try {
+            copyAttrSourceToTarget(source, target, chkEqual, ignores);
+        } catch (Exception e) {
+            throw new OwlRuntimeException(MsgConstants.ERR_FAILED_CONVERT_OBJECT, e);
+        }
+    }
+
+    /**
+     * �냼�뒪 �삤釉뚯젥�듃�뿉�꽌 Primitive Type(ignores �젣�쇅)留� ��寃� �삤釉뚯젥�듃濡� 蹂듭궗�븳�떎.
+     *
+     * @param source
+     * @param target
+     */
+    public static void copyProperties(Object source, Object target, String...ignores) {
+        copyProperties(source, target, true, ignores);
+    }
+
+    public static Map<String, Object> convertJsonToMap(String json) {
+        Map<String, Object> content = null;
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            content = mapper.readValue(json,
+                    new TypeReference<Map<String, Object>>() {
+                    });
+        } catch (Exception e) {
+            throw new OwlRuntimeException(MsgConstants.ERR_FAILED_CONVERT_JSON, e);
+        }
+
+        return content;
+    }
+
+    public static String convertObjectToJson(Object target) {
+        String json;
+        ObjectMapper mapper = getObjectMapper();
+
+        try {
+            json = mapper.writeValueAsString(target);
+        } catch (Exception e) {
+            throw new OwlRuntimeException(MsgConstants.ERR_FAILED_CONVERT_JSON, e);
+        }
+
+        return json;
+    }
+
+    public static <T> T convertJsonToObject(String json, Class<T> targetClass) {
+
+        T object;
+
+        ObjectMapper mapper = getObjectMapper();
+
+        try {
+            object = mapper.readValue(json, targetClass);
+        } catch (Exception e) {
+            throw new OwlRuntimeException(MsgConstants.ERR_FAILED_CONVERT_JSON, e);
+        }
+
+        return object;
+    }
+
+    public static <T> String join(List<T> objects, String fieldName, String seperate) {
+        StringBuffer sb = new StringBuffer("");
+        try {
+            for (T object : objects) {
+                PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(object.getClass(), fieldName);
+                Method method = propertyDescriptor.getReadMethod();
+                if (sb.length() == 0) {
+                        sb.append(method.invoke(object, fieldName));
+                } else {
+                    sb.append(seperate).append(" ").append(method.invoke(object, fieldName));
+                }
+            }
+        } catch (Exception e) {
+            LOGGER.error(e.getMessage(), e.getCause());
+        }
+
+        return sb.toString();
+    }
+
+    public static <T> List<String> getFieldValues(List<T> objects, String fieldName) {
+        return getFieldValues(objects, fieldName, null, null);
+    }
+
+    public static <T> List<String> getFieldValues(List<T> objects, String fieldName, String beforeAdd) {
+        return getFieldValues(objects, fieldName, beforeAdd, null);
+    }
+
+    public static <T> List<String> getFieldValues(List<T> objects, String fieldName, String beforeAdd,
+            String prefix) {
+        List<String> fieldValues = new ArrayList<String>();
+
+        if (beforeAdd != null) {
+            fieldValues.add(beforeAdd);
+        }
+
+        try {
+            for (T object : objects) {
+                PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(object.getClass(), fieldName);
+                Method method = propertyDescriptor.getReadMethod();
+                if (prefix == null) {
+                    fieldValues.add(method.invoke(object, fieldName).toString());
+                } else {
+                    fieldValues.add(prefix + method.invoke(object, fieldName).toString());
+                }
+            }
+        } catch (Exception e) {
+            LOGGER.error(e.getMessage(), e.getCause());
+        }
+
+        return fieldValues;
+    }
+
+    public static <T> String getFieldValue(T object, String fieldName) {
+        Object retVal = null;
+
+        if (object != null) {
+            PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(object.getClass(), fieldName);
+            Method method = propertyDescriptor.getReadMethod();
+            try {
+                retVal = method.invoke(object, fieldName);
+            } catch (Exception e) {
+                LOGGER.debug(e.toString());
+            }
+        }
+        return retVal == null ? "" : retVal.toString();
+    }
+
+    private static ObjectMapper getObjectMapper() {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        return mapper;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/util/DateUtil.java b/src/main/java/kr/wisestone/owl/util/DateUtil.java
new file mode 100644
index 0000000..0d67825
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/util/DateUtil.java
@@ -0,0 +1,166 @@
+package kr.wisestone.owl.util;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.time.DateUtils;
+import org.joda.time.DateTime;
+import org.joda.time.Days;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+public class DateUtil {
+
+    public static final String THIS_WEEK = "THIS_WEEK";  //  �씠踰덉<
+    public static final String LAST_WEEK = "LAST_WEEK";    //  吏��궃二�
+    public static final String LAST_SEVEN_DAYS = "LAST_SEVEN_DAYS";  //  理쒓렐 7�씪
+    public static final String THIS_MONTH = "THIS_MONTH";  //  �씠踰덈떖
+    public static final String LAST_MONTH = "LAST_MONTH";  //  吏��궃�떖
+    public static final String LAST_THIRTY_DAYS = "LAST_THIRTY_DAYS";  //  理쒓렐 30�씪
+    public static final String CUSTOM_INPUT = "CUSTOM_INPUT";   //  吏곸젒 �엯�젰
+
+
+    public static Date convertStrToDate(String source) {
+        return convertStrToDate(source, "yyyy-MM-dd HH:mm:ss");
+    }
+
+    public static Date convertStrToDate(String source, String pattern) {
+        return convertStrToDate(source, pattern, Locale.KOREA);
+    }
+
+    public static Date convertStrToDate(String source, String pattern, Locale locale) {
+        Date date = null;
+        SimpleDateFormat sdf = new SimpleDateFormat(pattern, locale);
+        try {
+            date = sdf.parse(source);
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+
+        return date;
+    }
+
+    public static String convertDateToYYYYMMDD(Date source) {
+        return convertDateToStr(source, "yyyy-MM-dd");
+    }
+
+    public static String convertDateToHHMMSS(Date source) {
+        return convertDateToStr(source, "HH:mm:ss");
+    }
+
+    public static String convertDateToStr(Date source) {
+        return convertDateToStr(source, "yyyy-MM-dd HH:mm:ss");
+    }
+
+    public static String convertDateToStr(Date source, String pattern) {
+        return convertDateToStr(source, pattern, Locale.KOREA);
+    }
+
+    public static String convertDateToStr(Date source, String pattern, Locale locale) {
+        String date = null;
+        SimpleDateFormat sdf = new SimpleDateFormat(pattern, locale);
+        date = sdf.format(source);
+
+        return date;
+    }
+
+    public static Date addDays(Date date, int amount) {
+        return DateUtils.addDays(date, amount);
+    }
+
+    public static Integer getDateDiff(Date fromDate, Date toDate) {
+
+        return Days.daysBetween(new DateTime(fromDate), new DateTime(toDate)).getDays();
+    }
+
+    public static boolean isValidDate(String date, String pattern) {
+        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+
+        if (date == null) {
+            return false;
+        }
+        String format = null;
+        try {
+            format = sdf.format(sdf.parse(date));
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        return date.equals(format);
+    }
+
+    //  寃��깋 �궇吏쒕�� 媛��졇�삩�떎.
+    public static List<Date> getSearchDays(String condition) {
+        Calendar cal = Calendar.getInstance();
+        List<Date> days = Lists.newArrayList();
+
+        switch(condition) {
+            //  理쒓렐 7�씪 �궇吏� 由ъ뒪�듃 媛��졇�삤湲�
+            case DateUtil.LAST_SEVEN_DAYS :
+                days.add(cal.getTime());
+
+                for (int count = 0; count < 6; count++) {
+                    cal.add(Calendar.DATE, -1);
+                    days.add(cal.getTime());
+                }
+                break;
+            case DateUtil.THIS_WEEK:
+                //  �씠踰덉< �궇吏� 由ъ뒪�듃 媛��졇�삤湲�
+                days.add(cal.getTime());
+
+                while (cal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
+                    cal.add(Calendar.DATE, -1);
+                    days.add(cal.getTime());
+                }
+                break;
+
+            case DateUtil.LAST_WEEK :
+                //  吏��궃二� �궇吏� 由ъ뒪�듃 媛��졇�삤湲�
+                while(cal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
+                    cal.add(Calendar.DATE, -1);
+                }
+                cal.add(Calendar.DATE, -1);
+                days.add(cal.getTime());
+
+                while(cal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
+                    cal.add(Calendar.DATE, -1);
+                    days.add(cal.getTime());
+                }
+
+                break;
+
+            case DateUtil.THIS_MONTH :
+                //  �씠踰덈떖 �궇吏� 由ъ뒪�듃 媛��졇�삤湲�
+                days.add(cal.getTime());
+                while(!"01".equals(new SimpleDateFormat("dd").format(cal.getTime()))) {
+                    cal.add(Calendar.DATE, -1);
+                    days.add(cal.getTime());
+                }
+                break;
+            case DateUtil.LAST_MONTH :
+                //  吏��궃�떖 �궇吏� 由ъ뒪�듃 媛��졇�삤湲곌린
+                while(!"01".equals(new SimpleDateFormat("dd").format(cal.getTime()))) {
+                    cal.add(Calendar.DATE, -1);
+                }
+
+                cal.add(Calendar.DATE, -1);
+                days.add(cal.getTime());
+                while(!"01".equals(new SimpleDateFormat("dd").format(cal.getTime()))) {
+                    cal.add(Calendar.DATE, -1);
+                    days.add(cal.getTime());
+                }
+
+                break;
+            case DateUtil.LAST_THIRTY_DAYS :
+                for (int count = 0; count > -30; count-- ) {
+                    days.add(DateUtil.addDays(new Date(), count));
+                }
+
+                break;
+        }
+
+        //  �뿭�닚 �젙�젹
+        Collections.reverse(days);
+
+        return days;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/util/ElasticSearchUtil.java b/src/main/java/kr/wisestone/owl/util/ElasticSearchUtil.java
new file mode 100644
index 0000000..59bad87
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/util/ElasticSearchUtil.java
@@ -0,0 +1,112 @@
+package kr.wisestone.owl.util;
+
+import kr.wisestone.owl.vo.UserVo;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ElasticSearchUtil {
+
+    //  �궗�슜�옄 �씠�젰 �젙蹂대�� 留뚮뱾�뼱 �궦�떎.
+    public static String makeUserActiveHistoryMessage(UserVo userVo, String actionType) {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("[USER_ACTIVE_HISTORY] ");
+        //  �븸�뀡 �쑀�삎
+        stringBuilder.append("[");
+        stringBuilder.append(actionType);
+        stringBuilder.append("] ");
+        //  �궗�슜�옄 �젙蹂대�� 異붿텧�빐�꽌 臾몄옄�뿴濡� 留뚮뱺�떎.
+        makeUserInfo(userVo, stringBuilder, true);
+
+        return stringBuilder.toString();
+    }
+
+    //  �궗�슜�옄 �젙蹂대�� 異붿텧�빐�꽌 臾몄옄�뿴濡� 留뚮뱺�떎.
+    private static void makeUserInfo(UserVo userVo, StringBuilder stringBuilder, boolean decrypt) {
+        //  �궗�슜�옄 �븘�씠�뵒
+        stringBuilder.append("[");
+        stringBuilder.append(userVo.getId());
+        stringBuilder.append("] ");
+        //  �궗�슜�옄 �씠由�
+        stringBuilder.append("[");
+        stringBuilder.append(userVo.getName());
+        stringBuilder.append("] ");
+        //  �궗�슜�옄 怨꾩젙
+        stringBuilder.append("[");
+        //  �븫�샇�솕 �릺�뼱�엳�쓣 寃쎌슦 蹂듯샇�솕 �븳�떎.
+        if (decrypt) {
+            stringBuilder.append(CommonUtil.decryptAES128(userVo.getAccount()));
+        }
+        else {
+            stringBuilder.append(userVo.getAccount());
+        }
+
+        stringBuilder.append("] ");
+    }
+
+    //  �궗�슜�옄�쓽 �떆�뒪�뀥 �궗�슜 �씠�젰�쓣 ���옣�븳�떎.
+    public static String makeUserSessionHistoryMessage(HttpServletRequest httpServletRequest, UserVo userVo) {
+        String agent = httpServletRequest.getHeader("User-Agent").toUpperCase();
+        String browser = null;
+        String os = null;
+
+        if (agent.contains("TRIDENT")) {
+            browser = "MSIE";
+        } else if (agent.contains("CHROME")) {
+            browser = "Chrome";
+        } else if (agent.contains("OPERA")) {
+            browser = "Opera";
+        } else if (agent.contains("SAFARI")) {
+            browser = "Safari";
+        }
+        else if (agent.contains("IPHONE") && agent.contains("MOBILE")) {
+            browser = "iPhone";
+        } else if (agent.contains("ANDROID") && agent.contains("MOBILE")) {
+            browser = "Android";
+        }
+
+        if (agent.contains("WINDOWS")) {
+            os = "Windows";
+        } else if (agent.contains("LINUX")) {
+            os = "Linux";
+        } else if (agent.contains("MACINTOSH")) {
+            os = "Macintosh";
+        } else if (agent.contains("MAC")) {
+            os = "Mac";
+        }
+
+        boolean mobile = agent.matches(".*(IPHONE|IPAD|IPOD|ANDROID|WINDOWS CE|BLACKBERRY|SYMBIAN|WINDOWS PHONE|WEBOS|OPERA MINI|" +
+                "OPERA MOBI|POLARIS|IEMOBILE|LGTELCOM|NOKIA|SONYERICSSON|LG|SAMSUNG).*");
+
+        String ip = httpServletRequest.getHeader("X-FORWARDED-FOR");
+
+        if (ip == null) {
+            ip = httpServletRequest.getRemoteAddr();
+        }
+
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("[USER_SESSION_HISTORY] ");
+        //  �젒�냽 釉뚮씪�슦�� �젙蹂�
+        stringBuilder.append("[");
+        stringBuilder.append(browser);
+        stringBuilder.append("] ");
+        //  �슫�쁺泥댁젣 �젙蹂�
+        stringBuilder.append("[");
+        stringBuilder.append(os);
+        stringBuilder.append("] ");
+        //  紐⑤컮�씪 �젒�냽 �뿬遺�
+        stringBuilder.append("[");
+        stringBuilder.append(mobile);
+        stringBuilder.append("] ");
+        //  �븘�씠�뵾 �젙蹂�
+        stringBuilder.append("[");
+        stringBuilder.append(ip);
+        stringBuilder.append("] ");
+        //  �궗�슜�옄 �젙蹂대�� 異붿텧�빐�꽌 臾몄옄�뿴濡� 留뚮뱺�떎.
+        makeUserInfo(userVo, stringBuilder, false);
+
+        return stringBuilder.toString();
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/util/MapUtil.java b/src/main/java/kr/wisestone/owl/util/MapUtil.java
new file mode 100644
index 0000000..4e2fb79
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/util/MapUtil.java
@@ -0,0 +1,191 @@
+package kr.wisestone.owl.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+
+import java.util.*;
+
+public class MapUtil {
+    static final Logger log = LoggerFactory.getLogger(MapUtil.class);
+
+    public static Long getLong(Map<String, Object> map, String attr) {
+        Object value = getObject(map, attr);
+        if (StringUtils.isEmpty(value)) {
+            return null;
+        }
+
+        return Long.valueOf(value.toString());
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static List<Long> getLongs(Map<String, Object> map, String attr) {
+        List<Long> values = new ArrayList<Long>();
+
+        Object value = getObject(map, attr);
+        if (value == null) {
+            return null;
+        }
+
+        if (!(value instanceof List)) {
+            return null;
+        }
+
+        for (Object obj : (List) value) {
+            values.add(Long.valueOf(obj.toString()));
+        }
+
+        return values;
+    }
+
+	public static Double getDouble(Map<String, Object> map, String attr) {
+		Object value = getObject(map, attr);
+		if (StringUtils.isEmpty(value)) {
+			return null;
+		}
+
+		return Double.valueOf(value.toString());
+	}
+
+	public static List<Double> getDoubles(Map<String, Object> map, String attr) {
+		List<Double> values = new ArrayList<Double>();
+
+		Object value = getObject(map, attr);
+		if (value == null) {
+			return null;
+		}
+
+		if (!(value instanceof List)) {
+			return null;
+		}
+
+		for (Object obj : (List) value) {
+			values.add(Double.valueOf(obj.toString()));
+		}
+
+		return values;
+	}
+
+    @SuppressWarnings("rawtypes")
+    public static List<String> getStrings(Map<String, Object> map, String attr) {
+        List<String> values = new ArrayList<String>();
+
+        Object value = getObject(map, attr);
+        if (value == null) {
+            return null;
+        }
+
+        if (!(value instanceof List)) {
+            return null;
+        }
+
+
+
+        for (Object obj : (List) value) {
+            values.add(obj.toString());
+        }
+
+        return values;
+    }
+
+    public static Integer getInteger(Map<String, Object> map, String attr) {
+        Object value = getObject(map, attr);
+        if (StringUtils.isEmpty(value)) {
+            return null;
+        }
+
+        return Integer.valueOf(value.toString());
+    }
+
+    /**
+     * 媛믪씠 null �삉�뒗 ""�씠硫� null�쓣 諛섑솚�븳�떎.
+     *
+     * @param map
+     * @param attr
+     * @return
+     */
+    public static String getString(Map<String, Object> map, String attr) {
+        Object value = getObject(map, attr);
+        if (StringUtils.isEmpty(value)) {
+            return null;
+        }
+
+        return value.toString();
+    }
+
+    public static String getString(Map<String, Object> map, String attr, String defaultValue) {
+        Object value = getObject(map, attr);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+
+        return value.toString();
+    }
+
+    /**
+     * @param map
+     * @param attr
+     * @return
+     */
+    public static Object getObject(Map<String, Object> map, String attr) {
+        if (map == null) {
+            return null;
+        }
+        return map.get(attr);
+    }
+
+    public static Boolean getBoolean(Map<String, Object> map, String attr) {
+        if (map != null) {
+            Object value = map.get(attr);
+            if (value != null) {
+                if (value instanceof String) {
+                    if ("".equals(value)) {
+                        return null;
+                    }
+                    if ("Y".equals(value)) {
+                        return Boolean.TRUE;
+                    } else {
+                        return Boolean.FALSE;
+                    }
+                } else {
+                    return Boolean.valueOf(value.toString());
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static Map<Long, String> getMap(Map<String, Object> map, String attr) {
+        if (map == null) {
+            return null;
+        }
+
+        Object value = map.get(attr);
+        if (value == null) {
+            return null;
+        }
+
+        if (!(value instanceof Map)) {
+            return null;
+        }
+
+        Map<Long, String> retval = new HashMap<Long, String>();
+        for (Iterator iter = ((Map) value).keySet().iterator(); iter.hasNext();) {
+            Object key = iter.next();
+
+            if (retval.containsKey(key)) {
+                log.error("留듭뿉 以묐났�맂 Key媛� 議댁옱�빀�땲�떎.");
+                return null;
+            }
+
+            retval.put(Long.valueOf(key.toString()), ((Map) value).get(key).toString());
+        }
+
+        return retval;
+    }
+
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/util/PageUtil.java b/src/main/java/kr/wisestone/owl/util/PageUtil.java
new file mode 100644
index 0000000..6d78294
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/util/PageUtil.java
@@ -0,0 +1,59 @@
+package kr.wisestone.owl.util;
+
+import kr.wisestone.owl.common.MessageAccessor;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.vo.PageVo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+
+public class PageUtil {
+    @SuppressWarnings("unused")
+    private static final Logger LOGGER = LoggerFactory.getLogger(PageUtil.class);
+
+    @Autowired
+    private MessageAccessor messageAccessor;
+
+    /**
+     * �쑀�슚�꽦�쓣 泥댄겕�븳 �썑�뿉 �쑀�슚�븯吏� �븡�쑝硫� �뿉�윭瑜� 諛쒖깮�떆�궓�떎.
+     * @param pageVo
+     */
+    public void validatePageVo(PageVo pageVo) {
+        if (pageVo == null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.PAGE_NOT_EXIST_INFO));
+        }
+
+        if (pageVo.getPage() == null || pageVo.getPage() < 0) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.PAGE_NEGATIVE_OR_NULL));
+        }
+
+        if (pageVo.getPageSize() == null || pageVo.getPageSize() < 0) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.PAGE_SIZE_NEGATIVE_OR_NULL));
+        }
+    }
+
+	public Pageable convertPageable(PageVo pageVo) {
+        return PageRequest.of(pageVo.getPage(), pageVo.getPageSize());
+	}
+
+	public Pageable getDefaultPageable() {
+		return PageRequest.of(0, 300);
+	}
+
+	public Pageable getDefaultPageable(int page, int size) {
+		return PageRequest.of(page, size);
+	}
+
+	public PageVo getDefaultPageVo() {
+		return new PageVo(0, 300);
+	}
+
+	public static Pageable applySort(Pageable page, String field, Sort.Direction direction) {
+
+		return PageRequest.of(page.getPageNumber(), page.getPageSize(), new Sort(direction, field));
+	}
+}
diff --git a/src/main/java/kr/wisestone/owl/util/SecurityUtils.java b/src/main/java/kr/wisestone/owl/util/SecurityUtils.java
new file mode 100644
index 0000000..b5c419e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/util/SecurityUtils.java
@@ -0,0 +1,131 @@
+package kr.wisestone.owl.util;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.User;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.session.SessionRegistry;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.authentication.WebAuthenticationDetails;
+import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Utility class for Spring Security.
+ */
+public final class SecurityUtils {
+
+    private SecurityUtils() {
+    }
+
+    /**
+     * Get the login of the current user.
+     */
+    public static String getCurrentUserLogin() {
+        SecurityContext securityContext = SecurityContextHolder.getContext();
+        Authentication authentication = securityContext.getAuthentication();
+        String userName = null;
+        if (authentication != null) {
+            if (authentication.getPrincipal() instanceof UserDetails) {
+                UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
+                userName = springSecurityUser.getUsername();
+            } else if (authentication.getPrincipal() instanceof String) {
+                userName = (String) authentication.getPrincipal();
+            }
+        }
+        return userName;
+    }
+
+    /**
+     * Check if a user is authenticated.
+     *
+     * @return true if the user is authenticated, false otherwise
+     */
+    public static boolean isAuthenticated() {
+        SecurityContext securityContext = SecurityContextHolder.getContext();
+        Collection<? extends GrantedAuthority> authorities = securityContext.getAuthentication().getAuthorities();
+        if (authorities != null) {
+            for (GrantedAuthority authority : authorities) {
+                if (authority.getAuthority().equals("ROLE_ANONYMOUS")) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Return the current user, or throws an exception, if the user is not
+     * authenticated yet.
+     *
+     * @return the current user
+     */
+    public static User getCurrentUser() {
+        SecurityContext securityContext = SecurityContextHolder.getContext();
+        Authentication authentication = securityContext.getAuthentication();
+        if (authentication != null) {
+            if (authentication.getPrincipal() instanceof User) {
+                return (User) authentication.getPrincipal();
+            }
+        }
+        return null;
+    }
+
+    public static void setUserToSession(User user) {
+        SecurityContext securityContext = SecurityContextHolder.getContext();
+
+        securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(user, "password", new HashSet<>()));
+    }
+
+    public static void addUserToSession(User user) {
+        SecurityContext securityContext = SecurityContextHolder.getContext();
+
+        securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(user, "password"));
+    }
+
+    /**
+     * If the current user has a specific authority (security role).
+     *
+     * <p>The name of this method comes from the isUserInRole() method in the Servlet API</p>
+     */
+    public static boolean isCurrentUserInRole(String authority) {
+        SecurityContext securityContext = SecurityContextHolder.getContext();
+        Authentication authentication = securityContext.getAuthentication();
+        if (authentication != null) {
+            if (authentication.getPrincipal() instanceof UserDetails) {
+                UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
+                return springSecurityUser.getAuthorities().contains(new SimpleGrantedAuthority(authority));
+            }
+        }
+        return false;
+    }
+
+    public static User getCurrentUserFromWebSocket(Map<String, Object> sessionAttributes) {
+        SecurityContext securityContext = (SecurityContext) sessionAttributes.get(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
+        Authentication authentication = securityContext.getAuthentication();
+        if (authentication != null) {
+            if (authentication.getPrincipal() instanceof User) {
+
+                return (User) authentication.getPrincipal();
+            }
+        }
+
+        throw new IllegalStateException("User not found!");
+    }
+
+    public static void autoLogin(UserDetails userDetails) {
+        Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+        SecurityContextHolder.getContext().setAuthentication(authentication);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/util/StringTemplateUtil.java b/src/main/java/kr/wisestone/owl/util/StringTemplateUtil.java
new file mode 100644
index 0000000..1f3a68e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/util/StringTemplateUtil.java
@@ -0,0 +1,18 @@
+package kr.wisestone.owl.util;
+
+import org.thymeleaf.context.Context;
+
+import java.util.Locale;
+import java.util.Map;
+
+public class StringTemplateUtil {
+
+    public static Context makeContext(Map<String, Object> contents, Locale locale) {
+        Context context = new Context(locale);
+
+        for(String key : contents.keySet()) {
+            context.setVariable(key, contents.get(key));
+        }
+        return context;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/util/ThreadCounter.java b/src/main/java/kr/wisestone/owl/util/ThreadCounter.java
new file mode 100644
index 0000000..e21da9a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/util/ThreadCounter.java
@@ -0,0 +1,37 @@
+package kr.wisestone.owl.util;
+
+/**
+ * Created by wisestone on 2018-02-01.
+ */
+public class ThreadCounter {
+    private volatile static ThreadCounter uniqueInstance;
+
+    private ThreadCounter() {
+    }
+
+    public static ThreadCounter getInstance() {
+        if (uniqueInstance == null) {
+            synchronized (ThreadCounter.class) {
+                if (uniqueInstance == null) {
+                    uniqueInstance = new ThreadCounter();
+                }
+            }
+        }
+
+        return uniqueInstance;
+    }
+
+    private volatile Long count = 0L;
+
+    public synchronized  Long getCount() {
+        return count;
+    }
+
+    public synchronized  void setCount(Long count) {
+        this.count = count;
+    }
+
+    public synchronized  void addCount() {
+        this.count++;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/util/WebAppUtil.java b/src/main/java/kr/wisestone/owl/util/WebAppUtil.java
new file mode 100644
index 0000000..f6c5f1f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/util/WebAppUtil.java
@@ -0,0 +1,275 @@
+/**
+ * �긽湲� �봽濡쒓렇�옩�뿉 ���븳 ���옉沅뚯쓣 �룷�븿�븳 吏��쟻�옱�궛沅뚯� WiseStone�뿉 �엳�쑝硫�,
+ * WiseStone�씠 紐낆떆�쟻�쑝濡� �뿀�슜�븯吏� �븡�� �궗�슜, 蹂듭궗, 蹂�寃�, �젣3�옄�뿉�쓽 怨듦컻,
+ * 諛고룷�뒗 �뾼寃⑺엳 湲덉��릺硫�, WiseStone�쓽 吏��쟻�옱�궛沅� 移⑦빐�뿉 �빐�떦�맗�땲�떎.
+ * (Copyright �뱬 2014 WiseStone Co., Ltd. All Rights Reserved|Confidential)
+ *-----------------------------------------------------------------------------
+ * You are strictly prohibited to copy, disclose, distribute, modify,
+ * or use this program in part or as a whole without the prior written
+ * consent of WiseStone Co., Ltd. WiseStone Co., Ltd., owns the
+ * intellectual property rights in and to this program.
+ * (Copyright �뱬 2014 WiseStone Co., Ltd. All Rights Reserved|Confidential)
+ *-----------------------------------------------------------------------------
+ */
+package kr.wisestone.owl.util;
+
+import com.google.gson.Gson;
+import kr.wisestone.owl.common.MessageAccessor;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.vo.UserVo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+
+public class WebAppUtil {
+    private static final Logger LOGGER = LoggerFactory.getLogger(WebAppUtil.class);
+
+    public static HttpServletRequest getHttpServletRequest() {
+        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+        if (requestAttributes == null) {
+            return null;
+        }
+        return ((ServletRequestAttributes) requestAttributes).getRequest();
+    }
+
+    public static void sessionInvalidate() {
+        sessionInvalidate(getHttpServletRequest());
+    }
+
+    public static void sessionInvalidate(HttpServletRequest request) {
+        HttpSession session = request.getSession(false);
+        if (session != null) {
+            session.invalidate();
+        }
+    }
+
+    public static ServletContext getServletContext() {
+        return getHttpServletRequest().getSession().getServletContext();
+    }
+
+    public static String getContextPath() {
+        return getServletContext().getContextPath();
+    }
+
+    /**
+     * �뙆�씪 �떎�젣 ���옣�븷 臾쇰━ �쐞移섎�� 媛��졇�삩�떎.
+     *
+     * @param
+     * @param
+     * @return realPath(String)
+     */
+    public static String getContextRealPath() {
+        return getServletContext().getRealPath("/");
+    }
+
+    public static Long getLoginId(HttpServletRequest request) {
+        UserVo UserVo = getLoginUser(request);
+        if (UserVo == null) {
+            return null;
+        }
+
+        return UserVo.getId();
+    }
+
+    public static UserVo getLoginUser(HttpServletRequest request) {
+        HttpSession session = request.getSession();
+        if (session == null) {
+            return null;
+        }
+
+        return (UserVo) session.getAttribute(Constants.SESSION_ACCOUNT);
+    }
+
+    public static UserVo getLoginUser(HttpSession session) {
+        return (UserVo) session.getAttribute(Constants.SESSION_ACCOUNT);
+    }
+
+    public static String getRequestUrl(HttpServletRequest request) {
+        String[] findUrltoPerm = request.getRequestURI().split(request.getServletContext().getContextPath());
+        return findUrltoPerm[1];
+    }
+
+    public static String getHost() {
+
+        HttpServletRequest request = WebAppUtil.getHttpServletRequest();
+
+        if(request == null)
+            return "";
+
+        String host = request.getServerName();
+
+        if("127.0.0.1".equals(host))
+            host = CommonUtil.getServerIpAddress();
+
+        return "http://"+host+":"+request.getServerPort();
+    }
+
+    public static String getSystemUrl() {
+        return WebAppUtil.getHost()+WebAppUtil.getContextPath();
+    }
+
+    /**
+     * �뿉�윭 �쓳�떟 �깮�꽦
+     *
+     * @param response
+     * @param errorCode MsgConstants
+     * @throws IOException
+     */
+    public static void responseException(HttpServletResponse response, String errorCode, MessageAccessor ma) throws IOException {
+        Map<String, Object> resJsonData = new HashMap<String, Object>();
+        resJsonData.put(Constants.RES_KEY_MESSAGE, ma.getResMessage(
+                errorCode,
+                Constants.RES_KEY_MSG_FAIL));
+
+        response.setContentType("application/json");
+        response.setCharacterEncoding("UTF-8");
+
+        Writer writer = null;
+        try {
+            writer = new PrintWriter(new OutputStreamWriter(response.getOutputStream(), "utf-8"));
+            writer.write(new Gson().toJson(resJsonData));
+            writer.flush();
+        } catch (Exception e) {
+            LOGGER.error("�쓳�떟 �뜲�씠�꽣 �깮�꽦 �삤瑜�", e);
+        } finally {
+            if (writer != null) {
+                writer.close();
+            }
+        }
+    }
+
+    /**
+     * Session �삉�뒗 Request�뿉 �엯�젰�맂 媛믪쓣 ���옣�븳�떎.
+     *
+     * @param name
+     * @param value
+     * @param scope
+     */
+    public static void setAttribute(String name, Object value, int scope) {
+        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+        if (requestAttributes == null) {
+            return;
+        }
+
+        requestAttributes.setAttribute(name, value, scope);
+    }
+
+    /**
+     * Session�뿉 �엯�젰�맂 媛믪쓣 ���옣�븳�떎.
+     *
+     * @param name
+     * @param value
+     */
+    public static void setSessionAttribute(String name, Object value) {
+        setAttribute(name, value, RequestAttributes.SCOPE_SESSION);
+    }
+
+    /**
+     * Session�뿉�꽌 �엯�젰�맂 �냽�꽦紐낆뿉 �빐�떦�븯�뒗 媛믪쓣 �뼸�뒗�떎.
+     * �냽�꽦紐낆뿉 �빐�떦�븯�뒗 媛믪씠 議댁옱�븯吏� �븡�쑝硫� Null�쓣 諛섑솚�븳�떎.
+     *
+     * @param name
+     * @param clazz Entity, Vo留� 吏��썝
+     * @return
+     */
+    public <T> T getAttribute(String name, Class<T> clazz) {
+        return this.getAttribute(name, RequestAttributes.SCOPE_SESSION, clazz);
+    }
+
+    /**
+     * Session �삉�뒗 Request�뿉�꽌 �엯�젰�맂 �냽�꽦紐낆뿉 �빐�떦�븯�뒗 媛믪쓣 �뼸�뒗�떎.
+     * �냽�꽦紐낆뿉 �빐�떦�븯�뒗 媛믪씠 議댁옱�븯吏� �븡�쑝硫� Null�쓣 諛섑솚�븳�떎.
+     *
+     * @param name �냽�꽦紐�
+     * @param scope RequestAttributes.SCOPE_SESSION, SCOPE_REQUEST
+     * @return
+     */
+    public Object getAttribute(String name, int scope) {
+        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+        if (requestAttributes == null) {
+            return null;
+        }
+
+        return requestAttributes.getAttribute(name, scope);
+    }
+
+    /**
+     * Session �삉�뒗 Request�뿉�꽌 �엯�젰�맂 �냽�꽦紐낆뿉 �빐�떦�븯�뒗 媛믪쓣 �뼸�뒗�떎.
+     * �냽�꽦紐낆뿉 �빐�떦�븯�뒗 媛믪씠 議댁옱�븯吏� �븡�쑝硫� Null�쓣 諛섑솚�븳�떎.
+     *
+     * @param name
+     * @param scope RequestAttributes.SCOPE_SESSION, SCOPE_REQUEST
+     * @param clazz Entity, Vo留� 吏��썝
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T getAttribute(String name, int scope, Class<T> clazz) {
+        Object object = this.getAttribute(name, scope);
+        if (object == null) {
+            return null;
+        }
+
+        return (T) object;
+    }
+
+    /**
+     * 濡쒓렇�씤�븳 �궗�슜�옄�쓽 ID瑜� 諛섑솚�븳�떎.
+     *
+     * @return
+     */
+    public Long getLoginId() {
+        UserVo userVo = this.getLoginUser();
+        if (userVo == null) {
+            return null;
+        }
+
+        return userVo.getId();
+    }
+
+    /**
+     * @return
+     */
+    public UserVo getLoginUser() {
+        User user = SecurityUtils.getCurrentUser();
+
+        if(user == null) {
+            return null;
+        }
+
+        UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class);
+
+        if (user.getSocialType() != null) {
+            userVo.setSocialType(user.getSocialType().toString());
+        }
+
+        userVo.setPassword(null);
+
+        return userVo;
+    }
+
+    public <T> T getLoginUser(Class<T> clazz) {
+        return this.getAttribute(Constants.SESSION_ACCOUNT, clazz);
+    }
+
+    public User getLoginUserObject() {
+
+        if(SecurityUtils.getCurrentUser() == null)
+            return null;
+
+        return SecurityUtils.getCurrentUser();
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/AttachedFileVo.java b/src/main/java/kr/wisestone/owl/vo/AttachedFileVo.java
new file mode 100644
index 0000000..2484273
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/AttachedFileVo.java
@@ -0,0 +1,72 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-01-05.
+ */
+public class AttachedFileVo extends BaseVo {
+    private Long id;
+    private String name;
+    private String path;
+    private Long size;
+    private String contentType;
+    private String fileType;
+    private byte[] bytes;
+
+    public AttachedFileVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public Long getSize() {
+        return size;
+    }
+
+    public void setSize(Long size) {
+        this.size = size;
+    }
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public void setContentType(String contentType) {
+        this.contentType = contentType;
+    }
+
+    public String getFileType() {
+        return fileType;
+    }
+
+    public void setFileType(String fileType) {
+        this.fileType = fileType;
+    }
+
+    public byte[] getBytes() {
+        return bytes;
+    }
+
+    public void setBytes(byte[] bytes) {
+        this.bytes = bytes;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/BaseVo.java b/src/main/java/kr/wisestone/owl/vo/BaseVo.java
new file mode 100644
index 0000000..7c0a163
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/BaseVo.java
@@ -0,0 +1,55 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by jeong on 2017-12-29.
+ */
+public class BaseVo {
+
+    Long registerId;
+    String registerDate;
+    Long modifyId;
+    String modifyDate;
+    Boolean checked = Boolean.FALSE;
+
+    public BaseVo(){}
+
+    public Long getRegisterId() {
+        return registerId;
+    }
+
+    public void setRegisterId(Long registerId) {
+        this.registerId = registerId;
+    }
+
+    public String getRegisterDate() {
+        return registerDate;
+    }
+
+    public void setRegisterDate(String registerDate) {
+        this.registerDate = registerDate;
+    }
+
+    public Long getModifyId() {
+        return modifyId;
+    }
+
+    public void setModifyId(Long modifyId) {
+        this.modifyId = modifyId;
+    }
+
+    public String getModifyDate() {
+        return modifyDate;
+    }
+
+    public void setModifyDate(String modifyDate) {
+        this.modifyDate = modifyDate;
+    }
+
+    public Boolean getChecked() {
+        return checked;
+    }
+
+    public void setChecked(Boolean checked) {
+        this.checked = checked;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/CustomFieldValueVo.java b/src/main/java/kr/wisestone/owl/vo/CustomFieldValueVo.java
new file mode 100644
index 0000000..cf5b3a7
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/CustomFieldValueVo.java
@@ -0,0 +1,27 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-05-29.
+ */
+public class CustomFieldValueVo extends BaseVo{
+    private Long id;
+    private String value;
+
+    public CustomFieldValueVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/CustomFieldVo.java b/src/main/java/kr/wisestone/owl/vo/CustomFieldVo.java
new file mode 100644
index 0000000..0a3055f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/CustomFieldVo.java
@@ -0,0 +1,78 @@
+package kr.wisestone.owl.vo;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+/**
+ * Created by wisestone on 2018-05-28.
+ */
+public class CustomFieldVo extends BaseVo{
+
+    private Long id;
+    private String name;
+    private String customFieldType;
+    private String defaultValue;
+    private List<CustomFieldValueVo> customFieldValueVos = Lists.newArrayList();
+    private Boolean modifyPermissionCheck = Boolean.TRUE;   //  �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 紐⑤뱺 �궗�엺�뱾�씠 �닔�젙, �궘�젣�븷 �닔 �엳�뼱�꽌 湲곕낯 媛믪씠 True
+    private Boolean useCustomFieldValue = Boolean.FALSE;    //  �씠�뒋�뿉�꽌 �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪쓣 �궗�슜�븯怨� �엳�쑝硫� true, �븘�땺寃쎌슦�뒗 false
+
+    public CustomFieldVo(){}
+
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCustomFieldType() {
+        return customFieldType;
+    }
+
+    public void setCustomFieldType(String customFieldType) {
+        this.customFieldType = customFieldType;
+    }
+
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    public void setDefaultValue(String defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    public List<CustomFieldValueVo> getCustomFieldValueVos() {
+        return customFieldValueVos;
+    }
+
+    public void setCustomFieldValueVos(List<CustomFieldValueVo> customFieldValueVos) {
+        this.customFieldValueVos = customFieldValueVos;
+    }
+
+    public Boolean getModifyPermissionCheck() {
+        return modifyPermissionCheck;
+    }
+
+    public void setModifyPermissionCheck(Boolean modifyPermissionCheck) {
+        this.modifyPermissionCheck = modifyPermissionCheck;
+    }
+
+    public Boolean getUseCustomFieldValue() {
+        return useCustomFieldValue;
+    }
+
+    public void setUseCustomFieldValue(Boolean useCustomFieldValue) {
+        this.useCustomFieldValue = useCustomFieldValue;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/EventVo.java b/src/main/java/kr/wisestone/owl/vo/EventVo.java
new file mode 100644
index 0000000..03f5161
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/EventVo.java
@@ -0,0 +1,82 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+public class EventVo extends BaseVo{
+    private Long id;
+    private String title;
+    private String description;
+    private Integer status;
+    private String startDate;
+    private String endDate;
+    private String writer;
+    public Boolean activation = Boolean.FALSE;
+    private Boolean modifyPermissionCheck = Boolean.FALSE;   //  媛��씠�뱶 �궗�빆 �닔�젙�� 愿�由ъ옄留� 媛��뒫
+
+    public EventVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public String getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(String startDate) {
+        this.startDate = startDate;
+    }
+
+    public String getEndDate() {
+        return endDate;
+    }
+
+    public void setEndDate(String endDate) {
+        this.endDate = endDate;
+    }
+
+    public String getWriter() {
+        return writer;
+    }
+
+    public void setWriter(String writer) {
+        this.writer = writer;
+    }
+
+    public Boolean getModifyPermissionCheck() {
+        return modifyPermissionCheck;
+    }
+
+    public void setModifyPermissionCheck(Boolean modifyPermissionCheck) {
+        this.modifyPermissionCheck = modifyPermissionCheck;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/ExportExcelAttrVo.java b/src/main/java/kr/wisestone/owl/vo/ExportExcelAttrVo.java
new file mode 100644
index 0000000..74d6384
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/ExportExcelAttrVo.java
@@ -0,0 +1,156 @@
+package kr.wisestone.owl.vo;
+
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+
+public class ExportExcelAttrVo {
+    public static final short ALIGN_LEFT = XSSFCellStyle.ALIGN_LEFT;
+    public static final short ALIGN_CENTER = XSSFCellStyle.ALIGN_CENTER;
+    public static final short ALIGN_RIGHT = XSSFCellStyle.ALIGN_RIGHT;
+
+    private String name;
+    private String desc;
+    private int width = 30;
+    private short align = ALIGN_LEFT;
+    private String i18Prefix;
+    private XSSFCellStyle cellStyle;
+    private String voAttrName;
+    private int mergeCount = 0;
+    private Boolean replaceWithNewlineYn = Boolean.FALSE;
+
+    public ExportExcelAttrVo() {
+    }
+
+    public ExportExcelAttrVo(String name) {
+        this.name = name;
+    }
+
+    public ExportExcelAttrVo(String name, String desc) {
+        this.name = name;
+        this.desc = desc;
+    }
+
+    public ExportExcelAttrVo(String name, String desc, int width) {
+        this.name = name;
+        this.desc = desc;
+        this.width = width;
+    }
+
+    public ExportExcelAttrVo(String name, String desc, int width, short align) {
+        this.name = name;
+        this.desc = desc;
+        this.width = width;
+        this.align = align;
+    }
+
+    public ExportExcelAttrVo(String name, String desc, int width, short align, Boolean replaceWithNewlineYn) {
+        this.name = name;
+        this.desc = desc;
+        this.width = width;
+        this.align = align;
+        this.replaceWithNewlineYn = replaceWithNewlineYn;
+    }
+
+    public ExportExcelAttrVo(String name, String desc, int width, short align, int mergeCount) {
+        this.name = name;
+        this.desc = desc;
+        this.width = width;
+        this.align = align;
+        this.mergeCount = mergeCount;
+    }
+
+    public ExportExcelAttrVo(String name, String desc, int width, XSSFCellStyle cellStyle, int mergeCount) {
+        this.name = name;
+        this.desc = desc;
+        this.width = width;
+        this.cellStyle = cellStyle;
+        this.mergeCount = mergeCount;
+    }
+
+    public ExportExcelAttrVo(String name, String desc, String voAttrName, int width, short align) {
+        this.name = name;
+        this.desc = desc;
+        this.voAttrName = voAttrName;
+        this.width = width;
+        this.align = align;
+    }
+
+    public ExportExcelAttrVo(String name, String desc, int width, short align, String i18Prefix) {
+        this.name = name;
+        this.desc = desc;
+        this.width = width;
+        this.align = align;
+        this.i18Prefix = i18Prefix;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDesc() {
+        return this.desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    public int getWidth() {
+        return this.width;
+    }
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public short getAlign() {
+        return this.align;
+    }
+
+    public void setAlign(short align) {
+        this.align = align;
+    }
+
+    public XSSFCellStyle getCellStyle() {
+        return this.cellStyle;
+    }
+
+    public void setCellStyle(XSSFCellStyle cellStyle) {
+        this.cellStyle = cellStyle;
+    }
+
+    public String getI18Prefix() {
+        return this.i18Prefix;
+    }
+
+    public void setI18Prefix(String i18Prefix) {
+        this.i18Prefix = i18Prefix;
+    }
+
+    public String getVoAttrName() {
+        return this.voAttrName;
+    }
+
+    public void setVoAttrName(String voAttrName) {
+        this.voAttrName = voAttrName;
+    }
+
+    public int getMergeCount() {
+        return mergeCount;
+    }
+
+    public void setMergeCount(int mergeCount) {
+        this.mergeCount = mergeCount;
+    }
+
+    public Boolean getReplaceWithNewlineYn() {
+        return replaceWithNewlineYn;
+    }
+
+    public void setReplaceWithNewlineYn(Boolean replaceWithNewlineYn) {
+        this.replaceWithNewlineYn = replaceWithNewlineYn;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/ExportExcelVo.java b/src/main/java/kr/wisestone/owl/vo/ExportExcelVo.java
new file mode 100644
index 0000000..3dbb2b9
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/ExportExcelVo.java
@@ -0,0 +1,64 @@
+
+package kr.wisestone.owl.vo;
+
+
+import com.google.common.collect.Lists;
+import java.util.List;
+
+public class ExportExcelVo {
+
+    //  �뙆�씪紐� - �젣紐�
+    private String fileName;
+    //  �뜲�씠�꽣
+    private List datas = Lists.newArrayList();
+    //  �냽�꽦 �젙蹂�
+    private List<ExportExcelAttrVo> attrInfos = Lists.newArrayList();
+    //  �뜲�씠�꽣 �냽�꽦
+    private String[] attr;
+    //  嫄댁닔 �몴�떆 �젣�뼱 - 湲곕낯 媛믪� �몴�떆濡� �뀑�똿
+    private Boolean hideCount = false;
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+
+    public List getDatas() {
+        return datas;
+    }
+
+    public void setDatas(List datas) {
+        this.datas = datas;
+    }
+
+    public List<ExportExcelAttrVo> getAttrInfos() {
+        return attrInfos;
+    }
+
+    public void setAttrInfos(List<ExportExcelAttrVo> attrInfos) {
+        this.attrInfos = attrInfos;
+    }
+
+    public void addAttrInfos(ExportExcelAttrVo attrInfo) {
+        this.attrInfos.add(attrInfo);
+    }
+
+    public String[] getAttr() {
+        return attr;
+    }
+
+    public void setAttr(String[] attr) {
+        this.attr = attr;
+    }
+
+    public Boolean getHideCount() {
+        return hideCount;
+    }
+
+    public void setHideCount(Boolean hideCount) {
+        this.hideCount = hideCount;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/FaqVo.java b/src/main/java/kr/wisestone/owl/vo/FaqVo.java
new file mode 100644
index 0000000..aab3a0f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/FaqVo.java
@@ -0,0 +1,64 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * zenith at 20200730
+ */
+public class FaqVo extends BaseVo{
+    private Long id;
+    private String title;
+    private String description;
+    private Integer status;
+    private String writer;
+    public Boolean activation = Boolean.FALSE;
+    private Boolean modifyPermissionCheck = Boolean.FALSE;   //  FAQ �궗�빆 �닔�젙�� 愿�由ъ옄留� 媛��뒫
+
+    public FaqVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public String getWriter() {
+        return writer;
+    }
+
+    public void setWriter(String writer) {
+        this.writer = writer;
+    }
+
+    public Boolean getModifyPermissionCheck() {
+        return modifyPermissionCheck;
+    }
+
+    public void setModifyPermissionCheck(Boolean modifyPermissionCheck) {
+        this.modifyPermissionCheck = modifyPermissionCheck;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/GuideVo.java b/src/main/java/kr/wisestone/owl/vo/GuideVo.java
new file mode 100644
index 0000000..d278a44
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/GuideVo.java
@@ -0,0 +1,64 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+public class GuideVo extends BaseVo{
+    private Long id;
+    private String title;
+    private String description;
+    private Integer status;
+    private String writer;
+    public Boolean activation = Boolean.FALSE;
+    private Boolean modifyPermissionCheck = Boolean.FALSE;   //  媛��씠�뱶 �궗�빆 �닔�젙�� 愿�由ъ옄留� 媛��뒫
+
+    public GuideVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public String getWriter() {
+        return writer;
+    }
+
+    public void setWriter(String writer) {
+        this.writer = writer;
+    }
+
+    public Boolean getModifyPermissionCheck() {
+        return modifyPermissionCheck;
+    }
+
+    public void setModifyPermissionCheck(Boolean modifyPermissionCheck) {
+        this.modifyPermissionCheck = modifyPermissionCheck;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/IssueCommentVo.java b/src/main/java/kr/wisestone/owl/vo/IssueCommentVo.java
new file mode 100644
index 0000000..2418595
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/IssueCommentVo.java
@@ -0,0 +1,68 @@
+package kr.wisestone.owl.vo;
+
+import kr.wisestone.owl.util.DateUtil;
+import java.util.Date;
+
+/**
+ * Created by wisestone on 2018-01-05.
+ */
+public class IssueCommentVo extends BaseVo {
+
+    private Long id;
+    private String description;
+    private IssueVo issueVo;
+    private String registerName;
+    private String profile;
+
+    public IssueCommentVo() {
+    }
+
+    public IssueCommentVo(Long id, String description, Long registerId, String registerName, String profile, Date registerDate) {
+        this.id = id;
+        this.description = description;
+        this.registerId = registerId;
+        this.registerName = registerName;
+        this.registerDate = DateUtil.convertDateToStr(registerDate);
+        this.profile = profile;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public IssueVo getIssueVo() {
+        return issueVo;
+    }
+
+    public void setIssueVo(IssueVo issueVo) {
+        this.issueVo = issueVo;
+    }
+
+    public String getRegisterName() {
+        return registerName;
+    }
+
+    public void setRegisterName(String registerName) {
+        this.registerName = registerName;
+    }
+
+    public String getProfile() {
+        return profile;
+    }
+
+    public void setProfile(String profile) {
+        this.profile = profile;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/IssueCustomFieldValueVo.java b/src/main/java/kr/wisestone/owl/vo/IssueCustomFieldValueVo.java
new file mode 100644
index 0000000..e5be303
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/IssueCustomFieldValueVo.java
@@ -0,0 +1,36 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-06-01.
+ */
+public class IssueCustomFieldValueVo extends BaseVo{
+    private Long id;
+    private String useValue;
+    private CustomFieldVo customFieldVo;
+
+    public IssueCustomFieldValueVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getUseValue() {
+        return useValue;
+    }
+
+    public void setUseValue(String useValue) {
+        this.useValue = useValue;
+    }
+
+    public CustomFieldVo getCustomFieldVo() {
+        return customFieldVo;
+    }
+
+    public void setCustomFieldVo(CustomFieldVo customFieldVo) {
+        this.customFieldVo = customFieldVo;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/IssueHistoryVo.java b/src/main/java/kr/wisestone/owl/vo/IssueHistoryVo.java
new file mode 100644
index 0000000..c15083f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/IssueHistoryVo.java
@@ -0,0 +1,114 @@
+package kr.wisestone.owl.vo;
+
+import kr.wisestone.owl.domain.enumType.IssueHistoryType;
+import kr.wisestone.owl.util.DateUtil;
+
+import java.util.Date;
+
+/**
+ * Created by wisestone on 2018-10-10.
+ */
+public class IssueHistoryVo extends BaseVo{
+    private Long id;
+    private String description;
+    private String registerName;
+    private String profile;
+    private IssueHistoryType issueHistoryType;
+    private String projectKey;
+    private String projectName;
+    private String issueNumber;
+    private String registerTime;
+    private String issueTitle;
+
+    public IssueHistoryVo(){}
+
+    public IssueHistoryVo(Long id, String description, Long registerId, String registerName, String profile, Date registerDate, IssueHistoryType issueHistoryType) {
+        this.id = id;
+        this.description = description;
+        this.registerId = registerId;
+        this.registerName = registerName;
+        this.registerDate = DateUtil.convertDateToStr(registerDate);
+        this.profile = profile;
+        this.issueHistoryType = issueHistoryType;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getRegisterName() {
+        return registerName;
+    }
+
+    public void setRegisterName(String registerName) {
+        this.registerName = registerName;
+    }
+
+    public String getProfile() {
+        return profile;
+    }
+
+    public void setProfile(String profile) {
+        this.profile = profile;
+    }
+
+    public IssueHistoryType getIssueHistoryType() {
+        return issueHistoryType;
+    }
+
+    public void setIssueHistoryType(IssueHistoryType issueHistoryType) {
+        this.issueHistoryType = issueHistoryType;
+    }
+
+    public String getProjectKey() {
+        return projectKey;
+    }
+
+    public void setProjectKey(String projectKey) {
+        this.projectKey = projectKey;
+    }
+
+    public String getProjectName() {
+        return projectName;
+    }
+
+    public void setProjectName(String projectName) {
+        this.projectName = projectName;
+    }
+
+    public String getIssueNumber() {
+        return issueNumber;
+    }
+
+    public void setIssueNumber(String issueNumber) {
+        this.issueNumber = issueNumber;
+    }
+
+    public String getRegisterTime() {
+        return registerTime;
+    }
+
+    public void setRegisterTime(String registerTime) {
+        this.registerTime = registerTime;
+    }
+
+    public String getIssueTitle() {
+        return issueTitle;
+    }
+
+    public void setIssueTitle(String issueTitle) {
+        this.issueTitle = issueTitle;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/IssueRelationVo.java b/src/main/java/kr/wisestone/owl/vo/IssueRelationVo.java
new file mode 100644
index 0000000..9e11b47
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/IssueRelationVo.java
@@ -0,0 +1,40 @@
+package kr.wisestone.owl.vo;
+
+public class IssueRelationVo extends BaseVo {
+    private Long id;
+    private Long relationIssueType;
+    private IssueVo issue;
+    private IssueVo issueRelation;
+    private String title;
+
+    public IssueRelationVo() {}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getRelationIssueType() {
+        return relationIssueType;
+    }
+
+    public void setRelationIssueType(Long relationIssueType) {
+        this.relationIssueType = relationIssueType;
+    }
+
+    public IssueVo getIssue() { return issue; }
+
+    public void setIssue(IssueVo issue) { this.issue = issue; }
+
+    public IssueVo getIssueRelation() { return issueRelation; }
+
+    public void setIssueRelation(IssueVo relationIssue) { this.issueRelation = relationIssue; }
+
+    public String getTitle() { return  this.title; }
+
+    public void setTitle(String title) { this.title = title; }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/IssueReservationVo.java b/src/main/java/kr/wisestone/owl/vo/IssueReservationVo.java
new file mode 100644
index 0000000..470b6f2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/IssueReservationVo.java
@@ -0,0 +1,45 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-08
+ */
+public class IssueReservationVo extends BaseVo{
+    private Long id;
+    private Long issueId;
+    private String issueReservationType;
+    private String reservation;
+
+    public IssueReservationVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getIssueId() {
+        return issueId;
+    }
+
+    public void setIssueId(Long issueId) {
+        this.issueId = issueId;
+    }
+
+    public String getIssueReservationType() {
+        return issueReservationType;
+    }
+
+    public void setIssueReservationType(String issueReservationType) {
+        this.issueReservationType = issueReservationType;
+    }
+
+    public String getReservation() {
+        return reservation;
+    }
+
+    public void setReservation(String reservation) {
+        this.reservation = reservation;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/IssueStatusVo.java b/src/main/java/kr/wisestone/owl/vo/IssueStatusVo.java
new file mode 100644
index 0000000..7bad8e9
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/IssueStatusVo.java
@@ -0,0 +1,135 @@
+package kr.wisestone.owl.vo;
+
+
+import com.google.common.collect.Lists;
+import java.util.List;
+
+/**
+ * Created by wisestone on 2018-01-04.
+ */
+public class IssueStatusVo extends BaseVo implements Comparable<IssueStatusVo>{
+    private Long id;
+    private String name;
+    private String color;
+    private Boolean defaultYn = Boolean.FALSE;  //  �뀒�씠釉붿뿉�꽌 泥댄겕諛뺤뒪 �꽑�깮 湲덉��븷 �닔 �엳寃� 吏��젙
+    private String issueStatusType;
+    private Long position;
+    private Boolean useYn = Boolean.FALSE;
+    private Long xLocation; //  �떎�씠�뼱洹몃옩 x異� 醫뚰몴 - workflowTransition �뿉�꽌 ���옣�븯怨� �엳�떎.
+    private Long yLocation; //  �떎�씠�뼱洹몃옩 y異� 醫뚰몴 - workflowTransition �뿉�꽌 ���옣�븯怨� �엳�떎.
+    private List<WorkflowTransitionVo> workflowTransitionVos = Lists.newArrayList();
+    private List<WorkflowVo> workflowVos = Lists.newArrayList();    //  �씠�뒋 �긽�깭 紐⑸줉�뿉�꽌 �씠�뒋 �긽�깭瑜� �궗�슜�븯怨� �엳�뒗 �썙�겕�뵆濡쒖슦 紐⑸줉 �젙蹂�
+    private Boolean modifyPermissionCheck = Boolean.TRUE;   //  �씠�뒋 �긽�깭�뒗 紐⑤뱺 �궗�엺�뱾�씠 �닔�젙, �궘�젣�븷 �닔 �엳�뼱�꽌 湲곕낯 媛믪씠 True
+
+    public IssueStatusVo(){}
+
+    public IssueStatusVo(Long id, String name){
+        this.id = id;
+        this.name = name;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+
+    public Boolean getDefaultYn() {
+        return defaultYn;
+    }
+
+    public void setDefaultYn(Boolean defaultYn) {
+        this.defaultYn = defaultYn;
+    }
+
+    public String getIssueStatusType() {
+        return issueStatusType;
+    }
+
+    public void setIssueStatusType(String issueStatusType) {
+        this.issueStatusType = issueStatusType;
+    }
+
+    public Long getPosition() {
+        return position;
+    }
+
+    public void setPosition(Long position) {
+        this.position = position;
+    }
+
+    public Boolean getUseYn() {
+        return useYn;
+    }
+
+    public void setUseYn(Boolean useYn) {
+        this.useYn = useYn;
+    }
+
+    public Long getxLocation() {
+        return xLocation;
+    }
+
+    public void setxLocation(Long xLocation) {
+        this.xLocation = xLocation;
+    }
+
+    public Long getyLocation() {
+        return yLocation;
+    }
+
+    public void setyLocation(Long yLocation) {
+        this.yLocation = yLocation;
+    }
+
+    public List<WorkflowTransitionVo> getWorkflowTransitionVos() {
+        return workflowTransitionVos;
+    }
+
+    public void setWorkflowTransitionVos(List<WorkflowTransitionVo> workflowTransitionVos) {
+        this.workflowTransitionVos = workflowTransitionVos;
+    }
+
+    public List<WorkflowVo> getWorkflowVos() {
+        return workflowVos;
+    }
+
+    public void setWorkflowVos(List<WorkflowVo> workflowVos) {
+        this.workflowVos = workflowVos;
+    }
+
+    public void addWorkflowVos(WorkflowVo workflowVo) {
+        this.workflowVos.add(workflowVo);
+    }
+
+    public Boolean getModifyPermissionCheck() {
+        return modifyPermissionCheck;
+    }
+
+    public void setModifyPermissionCheck(Boolean modifyPermissionCheck) {
+        this.modifyPermissionCheck = modifyPermissionCheck;
+    }
+
+    @Override
+    public int compareTo(IssueStatusVo issueStatusVo) {
+        return this.issueStatusType.compareTo(issueStatusVo.getIssueStatusType());
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/IssueTypeCustomFieldVo.java b/src/main/java/kr/wisestone/owl/vo/IssueTypeCustomFieldVo.java
new file mode 100644
index 0000000..7a9b3cf
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/IssueTypeCustomFieldVo.java
@@ -0,0 +1,36 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-05-30.
+ */
+public class IssueTypeCustomFieldVo extends BaseVo{
+    private Long id;
+    private String fieldOption;
+    private CustomFieldVo customFieldVo;
+
+    public IssueTypeCustomFieldVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getFieldOption() {
+        return fieldOption;
+    }
+
+    public void setFieldOption(String fieldOption) {
+        this.fieldOption = fieldOption;
+    }
+
+    public CustomFieldVo getCustomFieldVo() {
+        return customFieldVo;
+    }
+
+    public void setCustomFieldVo(CustomFieldVo customFieldVo) {
+        this.customFieldVo = customFieldVo;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/IssueTypeVo.java b/src/main/java/kr/wisestone/owl/vo/IssueTypeVo.java
new file mode 100644
index 0000000..58e173f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/IssueTypeVo.java
@@ -0,0 +1,63 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-05-29.
+ */
+public class IssueTypeVo extends BaseVo{
+    private Long id;
+    private String name;
+    private String description;
+    private String color;
+    private WorkflowVo workflowVo;
+    private Boolean modifyPermissionCheck = Boolean.TRUE;   //  �씠�뒋 �쑀�삎�� 紐⑤뱺 �궗�엺�뱾�씠 �닔�젙, �궘�젣�븷 �닔 �엳�뼱�꽌 湲곕낯 媛믪씠 True
+
+    public IssueTypeVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+
+    public WorkflowVo getWorkflowVo() {
+        return workflowVo;
+    }
+
+    public void setWorkflowVo(WorkflowVo workflowVo) {
+        this.workflowVo = workflowVo;
+    }
+
+    public Boolean getModifyPermissionCheck() {
+        return modifyPermissionCheck;
+    }
+
+    public void setModifyPermissionCheck(Boolean modifyPermissionCheck) {
+        this.modifyPermissionCheck = modifyPermissionCheck;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/IssueVersionVo.java b/src/main/java/kr/wisestone/owl/vo/IssueVersionVo.java
new file mode 100644
index 0000000..fbb1118
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/IssueVersionVo.java
@@ -0,0 +1,53 @@
+package kr.wisestone.owl.vo;
+
+import java.util.Map;
+
+public class IssueVersionVo extends BaseVo{
+    private Long id;
+    private Integer version;
+    private String content;
+    private Map<String, Object> versionHistory;
+    private String modifyByName;    //  蹂�寃쎌옄 �씠由�
+
+    public IssueVersionVo() {}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Integer getVersion() {
+        return version;
+    }
+
+    public void setVersion(Integer version) {
+        this.version = version;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public Map<String, Object> getVersionHistory() {
+        return versionHistory;
+    }
+
+    public void setVersionHistory(Map<String, Object> versionHistory) {
+        this.versionHistory = versionHistory;
+    }
+
+    public String getModifyByName() {
+        return modifyByName;
+    }
+
+    public void setModifyByName(String modifyByName) {
+        this.modifyByName = modifyByName;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/IssueVo.java b/src/main/java/kr/wisestone/owl/vo/IssueVo.java
new file mode 100644
index 0000000..ee7808a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/IssueVo.java
@@ -0,0 +1,373 @@
+package kr.wisestone.owl.vo;
+
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by wisestone on 2018-01-03.
+ */
+public class IssueVo extends BaseVo{
+    private Long id;
+    private String title;
+    private String description;
+    private Long issueNumber;
+    private String startDate;
+    private String completeDate;
+    private Long projectId;
+    private String projectName;
+    private String projectKey;
+    private Long issueStatusId;
+    private String issueStatusName;
+    private String issueStatusType;
+    private String issueStatusColor;
+    private Long issueTypeId;
+    private String issueTypeName;
+    private Long priorityId;
+    private String priorityName;
+    private String priorityColor;
+    private Long severityId;
+    private String severityName;
+    private String severityColor;
+    private ProjectVo projectVo;    //  �씠�뒋 �긽�꽭�뿉�꽌 �궗�슜
+    private IssueStatusVo issueStatusVo;    //  �씠�뒋 �긽�꽭�뿉�꽌 �궗�슜
+    private IssueTypeVo issueTypeVo;    //  �씠�뒋 �긽�꽭�뿉�꽌 �궗�슜
+    private PriorityVo priorityVo;  //  �씠�뒋 �긽�꽭�뿉�꽌 �궗�슜
+    private SeverityVo severityVo;  //  �씠�뒋 �긽�꽭�뿉�꽌 �궗�슜
+    private UserVo registerVo;  //  �씠�뒋 �긽�꽭�뿉�꽌 �궗�슜
+    private Boolean modifyPermissionCheck = Boolean.FALSE;
+    private List<UserVo> userVos = Lists.newArrayList();
+    private List<AttachedFileVo> attachedFileVos = Lists.newArrayList();
+    private List<IssueCommentVo> issueCommentVos = Lists.newArrayList();
+    private List<IssueHistoryVo> issueHistoryVos = Lists.newArrayList();
+    private List<IssueTypeCustomFieldVo> issueTypeCustomFieldVos = Lists.newArrayList();
+    private List<IssueCustomFieldValueVo> issueCustomFieldValueVos = Lists.newArrayList();
+    private List<IssueRelationVo> issueRelations = Lists.newArrayList();
+    private List<IssueVo> issueRelationVos = Lists.newArrayList();
+    private Long attachedFileCount;
+    private Long issueCommentCount;
+    private String modifyByName;    //  蹂�寃쎌옄 �젙蹂� - �씠�뒋 蹂�寃� �젙蹂� �긽�꽭 �솗�씤�뿉�꽌 �궗�슜
+    private WorkflowStatusVo workflowStatusVo;
+
+    public IssueVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Long getIssueNumber() {
+        return issueNumber;
+    }
+
+    public void setIssueNumber(Long issueNumber) {
+        this.issueNumber = issueNumber;
+    }
+
+    public String getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(String startDate) {
+        this.startDate = startDate;
+    }
+
+    public String getCompleteDate() {
+        return completeDate;
+    }
+
+    public void setCompleteDate(String completeDate) {
+        this.completeDate = completeDate;
+    }
+
+    public Long getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(Long projectId) {
+        this.projectId = projectId;
+    }
+
+    public String getProjectName() {
+        return projectName;
+    }
+
+    public void setProjectName(String projectName) {
+        this.projectName = projectName;
+    }
+
+    public String getProjectKey() {
+        return projectKey;
+    }
+
+    public void setProjectKey(String projectKey) {
+        this.projectKey = projectKey;
+    }
+
+    public Long getIssueStatusId() {
+        return issueStatusId;
+    }
+
+    public void setIssueStatusId(Long issueStatusId) {
+        this.issueStatusId = issueStatusId;
+    }
+
+    public String getIssueStatusName() {
+        return issueStatusName;
+    }
+
+    public void setIssueStatusName(String issueStatusName) {
+        this.issueStatusName = issueStatusName;
+    }
+
+    public String getIssueStatusType() { return  issueStatusType; }
+
+    public void setIssueStatusType(String issueStatusType) { this.issueStatusType = issueStatusType; }
+
+    public String getIssueStatusColor() {
+        return issueStatusColor;
+    }
+
+    public void setIssueStatusColor(String issueStatusColor) {
+        this.issueStatusColor = issueStatusColor;
+    }
+
+    public Long getIssueTypeId() {
+        return issueTypeId;
+    }
+
+    public void setIssueTypeId(Long issueTypeId) {
+        this.issueTypeId = issueTypeId;
+    }
+
+    public String getIssueTypeName() {
+        return issueTypeName;
+    }
+
+    public void setIssueTypeName(String issueTypeName) {
+        this.issueTypeName = issueTypeName;
+    }
+
+    public Long getPriorityId() {
+        return priorityId;
+    }
+
+    public void setPriorityId(Long priorityId) {
+        this.priorityId = priorityId;
+    }
+
+    public String getPriorityName() {
+        return priorityName;
+    }
+
+    public void setPriorityName(String priorityName) {
+        this.priorityName = priorityName;
+    }
+
+    public String getPriorityColor() {
+        return priorityColor;
+    }
+
+    public void setPriorityColor(String priorityColor) {
+        this.priorityColor = priorityColor;
+    }
+
+    public Long getSeverityId() {
+        return severityId;
+    }
+
+    public void setSeverityId(Long severityId) {
+        this.severityId = severityId;
+    }
+
+    public String getSeverityName() {
+        return severityName;
+    }
+
+    public void setSeverityName(String severityName) {
+        this.severityName = severityName;
+    }
+
+    public String getSeverityColor() {
+        return severityColor;
+    }
+
+    public void setSeverityColor(String severityColor) {
+        this.severityColor = severityColor;
+    }
+
+    public Boolean getModifyPermissionCheck() {
+        return modifyPermissionCheck;
+    }
+
+    public void setModifyPermissionCheck(Boolean modifyPermissionCheck) {
+        this.modifyPermissionCheck = modifyPermissionCheck;
+    }
+
+    public ProjectVo getProjectVo() {
+        return projectVo;
+    }
+
+    public void setProjectVo(ProjectVo projectVo) {
+        this.projectVo = projectVo;
+    }
+
+    public IssueStatusVo getIssueStatusVo() {
+        return issueStatusVo;
+    }
+
+    public void setIssueStatusVo(IssueStatusVo issueStatusVo) {
+        this.issueStatusVo = issueStatusVo;
+    }
+
+    public IssueTypeVo getIssueTypeVo() {
+        return issueTypeVo;
+    }
+
+    public void setIssueTypeVo(IssueTypeVo issueTypeVo) {
+        this.issueTypeVo = issueTypeVo;
+    }
+
+    public PriorityVo getPriorityVo() {
+        return priorityVo;
+    }
+
+    public void setPriorityVo(PriorityVo priorityVo) {
+        this.priorityVo = priorityVo;
+    }
+
+    public SeverityVo getSeverityVo() {
+        return severityVo;
+    }
+
+    public void setSeverityVo(SeverityVo severityVo) {
+        this.severityVo = severityVo;
+    }
+
+    public List<UserVo> getUserVos() {
+        return userVos;
+    }
+
+    public void setUserVos(List<UserVo> userVos) {
+        this.userVos = userVos;
+    }
+
+    public List<AttachedFileVo> getAttachedFileVos() {
+        return attachedFileVos;
+    }
+
+    public void setAttachedFileVos(List<AttachedFileVo> attachedFileVos) {
+        this.attachedFileVos = attachedFileVos;
+    }
+
+    public List<IssueCommentVo> getIssueCommentVos() {
+        return issueCommentVos;
+    }
+
+    public void setIssueCommentVos(List<IssueCommentVo> issueCommentVos) {
+        this.issueCommentVos = issueCommentVos;
+    }
+
+    public List<IssueTypeCustomFieldVo> getIssueTypeCustomFieldVos() {
+        return issueTypeCustomFieldVos;
+    }
+
+    public void setIssueTypeCustomFieldVos(List<IssueTypeCustomFieldVo> issueTypeCustomFieldVos) {
+        this.issueTypeCustomFieldVos = issueTypeCustomFieldVos;
+    }
+
+    public List<IssueCustomFieldValueVo> getIssueCustomFieldValueVos() {
+        return issueCustomFieldValueVos;
+    }
+
+    public void setIssueCustomFieldValueVos(List<IssueCustomFieldValueVo> issueCustomFieldValueVos) {
+        this.issueCustomFieldValueVos = issueCustomFieldValueVos;
+    }
+
+    public void addIssueCustomFieldValueVo(IssueCustomFieldValueVo issueCustomFieldValueVo) {
+        this.issueCustomFieldValueVos.add(issueCustomFieldValueVo);
+    }
+
+    public Long getAttachedFileCount() {
+        return attachedFileCount;
+    }
+
+    public void setAttachedFileCount(Long attachedFileCount) {
+        this.attachedFileCount = attachedFileCount;
+    }
+
+    public Long getIssueCommentCount() {
+        return issueCommentCount;
+    }
+
+    public void setIssueCommentCount(Long issueCommentCount) {
+        this.issueCommentCount = issueCommentCount;
+    }
+
+    public UserVo getRegisterVo() {
+        return registerVo;
+    }
+
+    public void setRegisterVo(UserVo registerVo) {
+        this.registerVo = registerVo;
+    }
+
+    public List<IssueHistoryVo> getIssueHistoryVos() {
+        return issueHistoryVos;
+    }
+
+    public void setIssueHistoryVos(List<IssueHistoryVo> issueHistoryVos) {
+        this.issueHistoryVos = issueHistoryVos;
+    }
+
+    public String getModifyByName() {
+        return modifyByName;
+    }
+
+    public void setModifyByName(String modifyByName) {
+        this.modifyByName = modifyByName;
+    }
+
+    public void setWorkflowStatusVo(WorkflowStatusVo workflowStatusVo) {
+        this.workflowStatusVo = workflowStatusVo;
+    }
+
+    public List<IssueRelationVo> getIssueRelationVos() {
+        return issueRelations;
+    }
+
+    public void setIssueRelationVos(List<IssueRelationVo> issueRelations) {
+        this.issueRelations = issueRelations;
+    }
+
+    public void addIssueRelationVo(IssueRelationVo issueRelationVo) {
+        if (this.issueRelations == null){
+            this.issueRelations = new ArrayList<>();
+        }
+
+        this.issueRelations.add(issueRelationVo);
+    }
+
+    public  List<IssueVo> getIssueRelationIssueVos() { return  this.issueRelationVos; }
+
+    public void setIssueRelationIssueVos(List<IssueVo> issueRelationVos) { this.issueRelationVos = issueRelationVos; }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/ManageUserVo.java b/src/main/java/kr/wisestone/owl/vo/ManageUserVo.java
new file mode 100644
index 0000000..157f131
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/ManageUserVo.java
@@ -0,0 +1,87 @@
+package kr.wisestone.owl.vo;
+
+import kr.wisestone.owl.constant.MngPermission;
+
+/**
+ * Created by wisestone on 2018-10-02.
+ */
+public class ManageUserVo extends BaseVo {
+    private Long id;
+    private String userName;
+    private String account;
+    private Boolean useYn;
+    private Integer permission;
+    private Long userId;
+    public Boolean permWorkSpace;
+    public Boolean permProjectSetting;
+    public Boolean permIssueSetting;
+    public Boolean permUser;
+    public Boolean permNotice;
+    public Boolean permFAQ;
+    public Boolean permQnA;
+    public Boolean permEvent;
+    public Boolean permGuide;
+
+    public ManageUserVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public Integer getPermission() {
+        return permission;
+    }
+
+    public void setPermission(Integer permission) {
+        this.permission = permission;
+        this.makePermission();
+    }
+
+    private void makePermission() {
+        this.permWorkSpace = MngPermission.checkMngPermission(this.permission, MngPermission.USER_PERMISSION_MNG_WORKSPACE);
+        this.permProjectSetting = MngPermission.checkMngPermission(this.permission, MngPermission.USER_PERMISSION_MNG_PROJECT);
+        this.permIssueSetting = MngPermission.checkMngPermission(this.permission, MngPermission.USER_PERMISSION_MNG_ISSUE_SETTING);
+        this.permUser = MngPermission.checkMngPermission(this.permission, MngPermission.USER_PERMISSION_MNG_USER);
+        this.permNotice = MngPermission.checkMngPermission(this.permission, MngPermission.USER_PERMISSION_MNG_NOTICE);
+        this.permFAQ = MngPermission.checkMngPermission(this.permission, MngPermission.USER_PERMISSION_MNG_FAQ);
+        this.permQnA = MngPermission.checkMngPermission(this.permission, MngPermission.USER_PERMISSION_MNG_QNA);
+        this.permEvent = MngPermission.checkMngPermission(this.permission, MngPermission.USER_PERMISSION_MNG_EVENT);
+        this.permGuide = MngPermission.checkMngPermission(this.permission, MngPermission.USER_PERMISSION_MNG_GUIDE);
+    }
+
+    public Boolean getUseYn() {
+        return useYn;
+    }
+
+    public void setUseYn(Boolean useYn) {
+        this.useYn = useYn;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/MessageVo.java b/src/main/java/kr/wisestone/owl/vo/MessageVo.java
new file mode 100644
index 0000000..704179a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/MessageVo.java
@@ -0,0 +1,34 @@
+package kr.wisestone.owl.vo;
+
+public class MessageVo {
+    private String code;
+    private String message;
+
+    public MessageVo(String code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+    public MessageVo() {
+    }
+
+    public String getCode() {
+        return this.code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getMessage() {
+        return this.message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+    @Override
+    public String toString() {
+        return "Message [code=" + this.code + ", message=" + this.message + "]";
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/NoticeVo.java b/src/main/java/kr/wisestone/owl/vo/NoticeVo.java
new file mode 100644
index 0000000..9bffab6
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/NoticeVo.java
@@ -0,0 +1,45 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+public class NoticeVo extends BaseVo{
+    private Long id;
+    private String title;
+    private String description;
+    private Boolean modifyPermissionCheck = Boolean.FALSE;   //  怨듭� �궗�빆 �닔�젙�� 愿�由ъ옄留� 媛��뒫
+
+    public NoticeVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Boolean getModifyPermissionCheck() {
+        return modifyPermissionCheck;
+    }
+
+    public void setModifyPermissionCheck(Boolean modifyPermissionCheck) {
+        this.modifyPermissionCheck = modifyPermissionCheck;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/PageVo.java b/src/main/java/kr/wisestone/owl/vo/PageVo.java
new file mode 100644
index 0000000..f699747
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/PageVo.java
@@ -0,0 +1,39 @@
+package kr.wisestone.owl.vo;
+
+public class PageVo {
+    private Integer page;
+    private Integer pageSize;
+    private Integer totalCount;
+
+    public PageVo() {
+    }
+
+    public PageVo(int page, int pageSize) {
+        this.page = page;
+        this.pageSize = pageSize;
+    }
+
+    public Integer getPage() {
+        return this.page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return this.pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Integer getTotalCount() {
+        return totalCount;
+    }
+
+    public void setTotalCount(Integer totalCount) {
+        this.totalCount = totalCount;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/PaymentHistoryVo.java b/src/main/java/kr/wisestone/owl/vo/PaymentHistoryVo.java
new file mode 100644
index 0000000..e8457d0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/PaymentHistoryVo.java
@@ -0,0 +1,82 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-02-14.
+ */
+public class PaymentHistoryVo extends BaseVo {
+    private Long id;
+    private String type;
+    private Integer price;
+    private Integer buyUser;
+    private String customerUid;
+    private String merchantUid;
+    private String paymentResult;
+    private String paymentResponse;
+
+    public PaymentHistoryVo() {
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public Integer getPrice() {
+        return price;
+    }
+
+    public void setPrice(Integer price) {
+        this.price = price;
+    }
+
+    public Integer getBuyUser() {
+        return buyUser;
+    }
+
+    public void setBuyUser(Integer buyUser) {
+        this.buyUser = buyUser;
+    }
+
+    public String getCustomerUid() {
+        return customerUid;
+    }
+
+    public void setCustomerUid(String customerUid) {
+        this.customerUid = customerUid;
+    }
+
+    public String getMerchantUid() {
+        return merchantUid;
+    }
+
+    public void setMerchantUid(String merchantUid) {
+        this.merchantUid = merchantUid;
+    }
+
+    public String getPaymentResult() {
+        return paymentResult;
+    }
+
+    public void setPaymentResult(String paymentResult) {
+        this.paymentResult = paymentResult;
+    }
+
+    public String getPaymentResponse() {
+        return paymentResponse;
+    }
+
+    public void setPaymentResponse(String paymentResponse) {
+        this.paymentResponse = paymentResponse;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/PaymentVo.java b/src/main/java/kr/wisestone/owl/vo/PaymentVo.java
new file mode 100644
index 0000000..26b297b
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/PaymentVo.java
@@ -0,0 +1,43 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-02-13.
+ */
+public class PaymentVo extends BaseVo {
+    private Long id;
+    private String type;
+    private Integer price;
+    private Integer buyUser;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public Integer getPrice() {
+        return price;
+    }
+
+    public void setPrice(Integer price) {
+        this.price = price;
+    }
+
+    public Integer getBuyUser() {
+        return buyUser;
+    }
+
+    public void setBuyUser(Integer buyUser) {
+        this.buyUser = buyUser;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/PermissionVo.java b/src/main/java/kr/wisestone/owl/vo/PermissionVo.java
new file mode 100644
index 0000000..21d9825
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/PermissionVo.java
@@ -0,0 +1,54 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by jeong on 2017-12-29.
+ */
+public class PermissionVo extends BaseVo{
+    private Long id;
+    private String name;
+    private String action;
+    private String roleType;
+    private Boolean activeYn = Boolean.FALSE;
+
+    public PermissionVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getAction() {
+        return action;
+    }
+
+    public void setAction(String action) {
+        this.action = action;
+    }
+
+    public String getRoleType() {
+        return roleType;
+    }
+
+    public void setRoleType(String roleType) {
+        this.roleType = roleType;
+    }
+
+    public Boolean getActiveYn() {
+        return activeYn;
+    }
+
+    public void setActiveYn(Boolean activeYn) {
+        this.activeYn = activeYn;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/PriorityVo.java b/src/main/java/kr/wisestone/owl/vo/PriorityVo.java
new file mode 100644
index 0000000..9ba9032
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/PriorityVo.java
@@ -0,0 +1,50 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-01-03.
+ */
+public class PriorityVo extends BaseVo {
+    private Long id;
+    private String name;
+    private Integer position;
+    private String color;
+
+    public PriorityVo(){}
+
+    public PriorityVo(Long id, String color){
+        this.id = id;
+        this.color = color;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getPosition() {
+        return position;
+    }
+
+    public void setPosition(Integer position) {
+        this.position = position;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/ProjectVo.java b/src/main/java/kr/wisestone/owl/vo/ProjectVo.java
new file mode 100644
index 0000000..13ad024
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/ProjectVo.java
@@ -0,0 +1,150 @@
+package kr.wisestone.owl.vo;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.Project;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by jeong on 2017-12-30.
+ */
+public class ProjectVo extends BaseVo {
+    private Long id;
+    private ProjectVo parentProjectVo;
+    private String name;
+    private String description;
+    private String status;
+    private String startDate;
+    private String endDate;
+    private String projectType;
+    private String projectKey;
+    private Long parentProjectId;
+    private Boolean defaultYn = Boolean.FALSE;  //  �뀒�씠釉붿뿉�꽌 泥댄겕諛뺤뒪 �꽑�깮 湲덉��븷 �닔 �엳寃� 吏��젙
+    private Boolean modifyPermissionCheck = Boolean.FALSE;
+    private List<UserVo> projectUserVos = Lists.newArrayList();
+    private List<UserVo> projectManagerVos = Lists.newArrayList();
+    private List<ProjectVo> childProjects = Lists.newArrayList();
+
+    public ProjectVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getParentProjectId() {
+        return parentProjectId;
+    }
+
+    public void setParentProjectId(Long id) {
+        this.parentProjectId = id;
+    }
+
+    public ProjectVo getParentProjectVo() {
+        return parentProjectVo;
+    }
+
+    public void setParentProjectVo(ProjectVo projectVo) {
+        this.parentProjectVo = projectVo;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(String startDate) {
+        this.startDate = startDate;
+    }
+
+    public String getEndDate() {
+        return endDate;
+    }
+
+    public void setEndDate(String endDate) {
+        this.endDate = endDate;
+    }
+
+    public List<UserVo> getProjectUserVos() {
+        return projectUserVos;
+    }
+
+    public void setProjectUserVos(List<UserVo> projectUserVos) {
+        this.projectUserVos = projectUserVos;
+    }
+
+    public List<UserVo> getProjectManagerVos() {
+        return projectManagerVos;
+    }
+
+    public void setProjectManagerVos(List<UserVo> projectManagerVos) {
+        this.projectManagerVos = projectManagerVos;
+    }
+
+    public List<ProjectVo> getChildProjects() {
+        return childProjects;
+    }
+
+    public void setChildProjects(List<ProjectVo> children) {
+        this.childProjects = children;
+    }
+
+    public Boolean getModifyPermissionCheck() {
+        return modifyPermissionCheck;
+    }
+
+    public void setModifyPermissionCheck(Boolean modifyPermissionCheck) {
+        this.modifyPermissionCheck = modifyPermissionCheck;
+    }
+
+    public String getProjectType() {
+        return projectType;
+    }
+
+    public void setProjectType(String projectType) {
+        this.projectType = projectType;
+    }
+
+    public String getProjectKey() {
+        return projectKey;
+    }
+
+    public void setProjectKey(String projectKey) {
+        this.projectKey = projectKey;
+    }
+
+    public Boolean getDefaultYn() {
+        return defaultYn;
+    }
+
+    public void setDefaultYn(Boolean defaultYn) {
+        this.defaultYn = defaultYn;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/QnaVo.java b/src/main/java/kr/wisestone/owl/vo/QnaVo.java
new file mode 100644
index 0000000..1611530
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/QnaVo.java
@@ -0,0 +1,45 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * zenith at 20200730
+ */
+public class QnaVo extends BaseVo{
+    private Long id;
+    private String title;
+    private String description;
+    private String writer;
+
+    public QnaVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getWriter() {
+        return writer;
+    }
+
+    public void setWriter(String writer) {
+        this.writer = writer;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/ResMessageVo.java b/src/main/java/kr/wisestone/owl/vo/ResMessageVo.java
new file mode 100644
index 0000000..f5b8c0e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/ResMessageVo.java
@@ -0,0 +1,53 @@
+package kr.wisestone.owl.vo;
+
+import java.io.Serializable;
+
+public class ResMessageVo implements Serializable{
+    private static final long serialVersionUID = 1L;
+
+    public ResMessageVo(String message, String code, String status) {
+        super();
+        this.message = message;
+        this.code = code;
+        this.status = status;
+    }
+
+    public ResMessageVo(){}
+
+    private String message;
+    private String code;
+    private String status;
+
+    public String getMessage() {
+        return this.message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public String getCode() {
+        return this.code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getStatus() {
+        return this.status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    @Override
+    public String toString() {
+        return "ResMessage [message=" + this.message + ", code=" + this.code
+                + ", status=" + this.status + "]";
+    }
+
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/ResPage.java b/src/main/java/kr/wisestone/owl/vo/ResPage.java
new file mode 100644
index 0000000..86c87ab
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/ResPage.java
@@ -0,0 +1,98 @@
+package kr.wisestone.owl.vo;
+
+public class ResPage {
+         /** �쁽�옱 �럹�씠吏� */
+        private int page;
+         /** �럹�씠吏� 濡쒖슦 */
+        private int pageSize;
+         private int totalPage;
+         /** �쟾泥� 濡쒖슦 移댁슫�듃 */
+        private long totalCount;
+
+        public ResPage() {
+        }
+
+        /**
+         * @param page
+         * @param pageSize
+         * @param totalPage
+         * @param totalCount
+         */
+        public ResPage(int page, int pageSize, int totalPage, long totalCount) {
+            this.page=page;
+            this.pageSize=pageSize;
+            this.totalPage=totalPage;
+            this.totalCount=totalCount;
+        }
+
+        public ResPage(int page, int pageSize, int totalCount) {
+            this.page=page;
+            this.pageSize=pageSize;
+            this.totalCount=totalCount;
+
+            this.totalPage = 0;
+
+            this.totalPage = totalCount / pageSize;
+            if (totalCount % pageSize > 0) {
+                this.totalPage += 1;
+            }
+        }
+
+        /**
+         * @return the page
+         */
+        public int getPage() {
+            return this.page;
+        }
+        /**
+         * @param page the page to set
+         */
+        public void setPage(int page) {
+            this.page = page;
+        }
+        /**
+         * @return the pageSize
+         */
+        public int getPageSize() {
+            return this.pageSize;
+        }
+        /**
+         * @param pageSize the pageSize to set
+         */
+        public void setPageSize(int pageSize) {
+            this.pageSize = pageSize;
+        }
+        /**
+         * @return the totalPage
+         */
+        public int getTotalPage() {
+            return this.totalPage;
+        }
+        /**
+         * @param totalPage the totalPage to set
+         */
+        public void setTotalPage(int totalPage) {
+            this.totalPage = totalPage;
+        }
+        /**
+         * @return the totalCount
+         */
+        public long getTotalCount() {
+            return this.totalCount;
+        }
+        /**
+         * @param totalCount the totalCount to set
+         */
+        public void setTotalCount(long totalCount) {
+            this.totalCount = totalCount;
+        }
+
+        @Override
+        public String toString() {
+            return "ResPage [page=" + this.page + ", pageSize=" + this.pageSize
+                    + ", totalPage=" + this.totalPage + ", totalCount=" + this.totalCount
+                    + "]";
+        }
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/SeverityVo.java b/src/main/java/kr/wisestone/owl/vo/SeverityVo.java
new file mode 100644
index 0000000..9ee8cf1
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/SeverityVo.java
@@ -0,0 +1,50 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-06-01.
+ */
+public class SeverityVo extends BaseVo {
+    private Long id;
+    private String name;
+    private Integer position;
+    private String color;
+
+    public SeverityVo(){}
+
+    public SeverityVo(Long id, String color){
+        this.id = id;
+        this.color = color;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getPosition() {
+        return position;
+    }
+
+    public void setPosition(Integer position) {
+        this.position = position;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/UserInviteVo.java b/src/main/java/kr/wisestone/owl/vo/UserInviteVo.java
new file mode 100644
index 0000000..8066bcc
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/UserInviteVo.java
@@ -0,0 +1,45 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-03-14.
+ */
+public class UserInviteVo {
+    private Long id;
+    private String email;
+    private String status;
+    private WorkspaceVo workspaceVo;
+
+    public UserInviteVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public WorkspaceVo getWorkspaceVo() {
+        return workspaceVo;
+    }
+
+    public void setWorkspaceVo(WorkspaceVo workspaceVo) {
+        this.workspaceVo = workspaceVo;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/UserVo.java b/src/main/java/kr/wisestone/owl/vo/UserVo.java
new file mode 100644
index 0000000..eb0fea6
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/UserVo.java
@@ -0,0 +1,192 @@
+package kr.wisestone.owl.vo;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+/**
+ * Created by jeong on 2017-08-02.
+ */
+public class UserVo extends BaseVo{
+
+    private Long id;
+    private String name;
+    private String account;
+    private String password;
+    private String profile;
+    private String status;
+    private String phone;
+    private String byName;  //  autocomplete �뿉�꽌 �궗�슜�븯�뒗 媛�怨듬맂 �씠由� + �씠硫붿씪 �젙蹂�
+    private String socialType;
+    private Long lastWorkspaceId;
+    private Long lastProjectId;
+    private Boolean userSocialLogin = Boolean.FALSE;
+    private List<ProjectVo> projectVos = Lists.newArrayList();
+    private String reservationNotifyTime;
+    private String language;
+    private Integer permission;
+    private String licensekey;
+    private Long sessionActiveTime; //  �겢�윭�뒪�꽣留� �솚寃쎌뿉�꽌 �젒�냽以묒씤 �궗�슜�옄 愿�由ъ뿉 �궗�슜
+
+    public UserVo() {
+    }
+
+    public UserVo(Long id, String name, String account, String profile) {
+        this.id = id;
+        this.name = name;
+        this.account = account;
+        this.profile = profile;
+    }
+
+    public UserVo(Long id, String name, String account, String profile, String status, String phone) {
+        this.id = id;
+        this.name = name;
+        this.account = account;
+        this.profile = profile;
+        this.status = status;
+        this.phone = phone;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getProfile() {
+        return profile;
+    }
+
+    public void setProfile(String profile) {
+        this.profile = profile;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public Long getLastWorkspaceId() {
+        return lastWorkspaceId;
+    }
+
+    public void setLastWorkspaceId(Long lastWorkspaceId) {
+        this.lastWorkspaceId = lastWorkspaceId;
+    }
+
+    public List<ProjectVo> getProjectVos() {
+        return projectVos;
+    }
+
+    public void setProjectVos(List<ProjectVo> projectVos) {
+        this.projectVos = projectVos;
+    }
+
+    public Boolean getUserSocialLogin() {
+        return userSocialLogin;
+    }
+
+    public void setUserSocialLogin(Boolean userSocialLogin) {
+        this.userSocialLogin = userSocialLogin;
+    }
+
+    public String getByName() {
+        return byName;
+    }
+
+    public void setByName(String byName) {
+        this.byName = byName;
+    }
+
+    public String getSocialType() {
+        return socialType;
+    }
+
+    public void setSocialType(String socialType) {
+        this.socialType = socialType;
+    }
+
+    public String getReservationNotifyTime() {
+        return reservationNotifyTime;
+    }
+
+    public void setReservationNotifyTime(String reservationNotifyTime) {
+        this.reservationNotifyTime = reservationNotifyTime;
+    }
+
+    public String getLanguage() {
+        return language;
+    }
+
+    public void setLanguage(String language) {
+        this.language = language;
+    }
+
+    public Integer getPermission() {
+        return permission;
+    }
+
+    public void setPermission(Integer permission) {
+        this.permission = permission;
+    }
+
+    public String getLicensekey() {
+        return licensekey;
+    }
+
+    public void setLicensekey(String licensekey) {
+        this.licensekey = licensekey;
+    }
+
+    public Long getSessionActiveTime() {
+        return sessionActiveTime;
+    }
+
+    public void setSessionActiveTime(Long sessionActiveTime) {
+        this.sessionActiveTime = sessionActiveTime;
+    }
+
+    public Long getLastProjectId() {
+        return lastProjectId;
+    }
+
+    public void setLastProjectId(Long id) { this.lastProjectId = id;}
+
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/UserWorkspaceVo.java b/src/main/java/kr/wisestone/owl/vo/UserWorkspaceVo.java
new file mode 100644
index 0000000..0c42a64
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/UserWorkspaceVo.java
@@ -0,0 +1,54 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-10-02.
+ */
+public class UserWorkspaceVo extends BaseVo {
+    private Long id;
+    private String userName;
+    private String account;
+    private Integer premission;
+    private Boolean useYn;
+
+    public UserWorkspaceVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public Integer getPermission() {
+        return premission;
+    }
+
+    public void setPermission(Integer premission) {
+        this.premission = premission;
+    }
+
+    public Boolean getUseYn() {
+        return useYn;
+    }
+
+    public void setUseYn(Boolean useYn) {
+        this.useYn = useYn;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/WorkflowStatusVo.java b/src/main/java/kr/wisestone/owl/vo/WorkflowStatusVo.java
new file mode 100644
index 0000000..2453195
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/WorkflowStatusVo.java
@@ -0,0 +1,132 @@
+package kr.wisestone.owl.vo;
+
+import java.util.List;
+
+/**
+ * Created by wisestone on 2018-01-04.
+ */
+public class WorkflowStatusVo extends BaseVo {
+    private Long id;
+    private String name;
+    private String description;
+    private Boolean defaultYn = Boolean.FALSE;
+    private Boolean firstYn = Boolean.FALSE;
+    private Boolean lastYn = Boolean.FALSE;
+    private String color;
+    private Long progress;
+    private Long position;
+    private ProjectVo projectVo;    //  �븷�옄�씪 蹂대뱶 �긽�깭 �닔�젙�뿉�꽌 �궗�슜
+    private List<IssueVo> taskVos;
+
+    public WorkflowStatusVo(){}
+
+    public WorkflowStatusVo(Long id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    public WorkflowStatusVo(Long id, String name, String color) {
+        this.id = id;
+        this.name = name;
+        this.color = color;
+    }
+
+    public WorkflowStatusVo(Long id, String name, String color, Boolean defaultYn, Boolean firstYn, Boolean lastYn, Long position, Long progress) {
+        this.id = id;
+        this.name = name;
+        this.color = color;
+        this.defaultYn = defaultYn;
+        this.firstYn = firstYn;
+        this.lastYn = lastYn;
+        this.position = position;
+        this.progress = progress;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Boolean getDefaultYn() {
+        return defaultYn;
+    }
+
+    public void setDefaultYn(Boolean defaultYn) {
+        this.defaultYn = defaultYn;
+    }
+
+    public Boolean getFirstYn() {
+        return firstYn;
+    }
+
+    public void setFirstYn(Boolean firstYn) {
+        this.firstYn = firstYn;
+    }
+
+    public Boolean getLastYn() {
+        return lastYn;
+    }
+
+    public void setLastYn(Boolean lastYn) {
+        this.lastYn = lastYn;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+
+    public Long getProgress() {
+        return progress;
+    }
+
+    public void setProgress(Long progress) {
+        this.progress = progress;
+    }
+
+    public Long getPosition() {
+        return position;
+    }
+
+    public void setPosition(Long position) {
+        this.position = position;
+    }
+
+    public ProjectVo getProjectVo() {
+        return projectVo;
+    }
+
+    public void setProjectVo(ProjectVo projectVo) {
+        this.projectVo = projectVo;
+    }
+
+    public List<IssueVo> getTaskVos() {
+        return taskVos;
+    }
+
+    public void setTaskVos(List<IssueVo> taskVos) {
+        this.taskVos = taskVos;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/WorkflowTransitionVo.java b/src/main/java/kr/wisestone/owl/vo/WorkflowTransitionVo.java
new file mode 100644
index 0000000..69c5059
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/WorkflowTransitionVo.java
@@ -0,0 +1,94 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-05-10.
+ */
+public class WorkflowTransitionVo extends BaseVo {
+    private Long id;
+    private Long sourceStatusId;
+    private String sourceStatusName;
+
+    private Long targetStatusId;
+    private String targetStatusName;
+
+    private Long correctX;
+    private Long correctY;
+    private Boolean direct;
+
+    public WorkflowTransitionVo(){}
+
+    public WorkflowTransitionVo(Long id, Long sourceStatusId, String sourceStatusName, Long targetStatusId, String targetStatusName, Long correctX, Long correctY, Boolean direct){
+        this.id = id;
+        this.sourceStatusId = sourceStatusId;
+        this.sourceStatusName = sourceStatusName;
+        this.targetStatusId = targetStatusId;
+        this.targetStatusName = targetStatusName;
+        this.correctX = correctX;
+        this.correctY = correctY;
+        this.direct = direct;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getSourceStatusId() {
+        return sourceStatusId;
+    }
+
+    public void setSourceStatusId(Long sourceStatusId) {
+        this.sourceStatusId = sourceStatusId;
+    }
+
+    public String getSourceStatusName() {
+        return sourceStatusName;
+    }
+
+    public void setSourceStatusName(String sourceStatusName) {
+        this.sourceStatusName = sourceStatusName;
+    }
+
+    public Long getTargetStatusId() {
+        return targetStatusId;
+    }
+
+    public void setTargetStatusId(Long targetStatusId) {
+        this.targetStatusId = targetStatusId;
+    }
+
+    public String getTargetStatusName() {
+        return targetStatusName;
+    }
+
+    public void setTargetStatusName(String targetStatusName) {
+        this.targetStatusName = targetStatusName;
+    }
+
+    public Long getCorrectX() {
+        return correctX;
+    }
+
+    public void setCorrectX(Long correctX) {
+        this.correctX = correctX;
+    }
+
+    public Long getCorrectY() {
+        return correctY;
+    }
+
+    public void setCorrectY(Long correctY) {
+        this.correctY = correctY;
+    }
+
+    public Boolean getDirect() {
+        return direct;
+    }
+
+    public void setDirect(Boolean direct) {
+        this.direct = direct;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/WorkflowVo.java b/src/main/java/kr/wisestone/owl/vo/WorkflowVo.java
new file mode 100644
index 0000000..ae316a6
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/WorkflowVo.java
@@ -0,0 +1,67 @@
+package kr.wisestone.owl.vo;
+
+import com.google.common.collect.Lists;
+import java.util.List;
+
+/**
+ * Created by wisestone on 2018-05-10.
+ */
+public class WorkflowVo extends BaseVo {
+
+    private Long id;
+    private String name;
+    private String description;
+    private List<IssueStatusVo> issueStatusVos = Lists.newArrayList();
+    private Boolean modifyPermissionCheck = Boolean.TRUE;   //  �썙�겕�뵆濡쒖슦�뒗 紐⑤뱺 �궗�엺�뱾�씠 �닔�젙, �궘�젣�븷 �닔 �엳�뼱�꽌 湲곕낯 媛믪씠 True
+    private List<IssueTypeVo> issueTypeVos = Lists.newArrayList();
+
+    public WorkflowVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public List<IssueStatusVo> getIssueStatusVos() {
+        return issueStatusVos;
+    }
+
+    public void setIssueStatusVos(List<IssueStatusVo> issueStatusVos) {
+        this.issueStatusVos = issueStatusVos;
+    }
+
+    public Boolean getModifyPermissionCheck() {
+        return modifyPermissionCheck;
+    }
+
+    public void setModifyPermissionCheck(Boolean modifyPermissionCheck) {
+        this.modifyPermissionCheck = modifyPermissionCheck;
+    }
+
+    public List<IssueTypeVo> getIssueTypeVos() {
+        return issueTypeVos;
+    }
+
+    public void setIssueTypeVos(List<IssueTypeVo> issueTypeVos) {
+        this.issueTypeVos = issueTypeVos;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/vo/WorkspaceVo.java b/src/main/java/kr/wisestone/owl/vo/WorkspaceVo.java
new file mode 100644
index 0000000..59c131c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/WorkspaceVo.java
@@ -0,0 +1,145 @@
+package kr.wisestone.owl.vo;
+
+/**
+ * Created by wisestone on 2018-02-13.
+ */
+public class WorkspaceVo extends BaseVo {
+    private Long id;
+    private String name;
+    private Integer maxUser;   //  李몄뿬 媛��뒫 �씤�썝
+    private String startDate;
+    private String expireDate;
+    private Long storageSize;
+    private Long totalDate;
+    private Long useStorageSize;
+    private Long useTraffic;    //  �궗�슜 �듃�옒�뵿
+    private Integer activeUser;    //  李몄뿬 �씤�썝
+    private Integer standByUser;  //  李몄뿬 ��湲� �씤�썝
+    private String serviceType;
+    private Integer expireDateTerm; //  異붽� 寃곗젣瑜� 吏꾪뻾�븷 �븣 �겢�씪�씠�뼵�듃�뿉�꽌 湲덉븸 怨꾩궛�쓣 �쐞�빐 �궗�슜
+    private Integer usdKrw; //  �솚�쑉
+    // zenith edit
+    private Integer packageType; // �꽕移섎맂 package�쓽 �삎�깭�뿉 �뵲�씪�꽌 workspace瑜� �젙�쓽�븳�떎.
+
+    public WorkspaceVo(){}
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getMaxUser() {
+        return maxUser;
+    }
+
+    public void setMaxUser(Integer maxUser) {
+        this.maxUser = maxUser;
+    }
+
+    public String getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(String startDate) {
+        this.startDate = startDate;
+    }
+
+    public String getExpireDate() {
+        return expireDate;
+    }
+
+    public void setExpireDate(String expireDate) {
+        this.expireDate = expireDate;
+    }
+
+    public Long getStorageSize() {
+        return storageSize;
+    }
+
+    public void setStorageSize(Long storageSize) {
+        this.storageSize = storageSize;
+    }
+
+    public Long getTotalDate() {
+        return totalDate;
+    }
+
+    public void setTotalDate(Long totalDate) {
+        this.totalDate = totalDate;
+    }
+
+    public Integer getPackageType() {
+        return packageType;
+    }
+
+    public void setPackageType(Integer packageType) {
+        this.packageType = packageType;
+    }
+
+    public Long getUseStorageSize() {
+        return useStorageSize;
+    }
+
+    public void setUseStorageSize(Long useStorageSize) {
+        this.useStorageSize = useStorageSize;
+    }
+
+    public Long getUseTraffic() {
+        return useTraffic;
+    }
+
+    public void setUseTraffic(Long useTraffic) {
+        this.useTraffic = useTraffic;
+    }
+
+    public Integer getActiveUser() {
+        return activeUser;
+    }
+
+    public void setActiveUser(Integer activeUser) {
+        this.activeUser = activeUser;
+    }
+
+    public Integer getStandByUser() {
+        return standByUser;
+    }
+
+    public void setStandByUser(Integer standByUser) {
+        this.standByUser = standByUser;
+    }
+
+    public String getServiceType() {
+        return serviceType;
+    }
+
+    public void setServiceType(String serviceType) {
+        this.serviceType = serviceType;
+    }
+
+    public Integer getExpireDateTerm() {
+        return expireDateTerm;
+    }
+
+    public void setExpireDateTerm(Integer expireDateTerm) {
+        this.expireDateTerm = expireDateTerm;
+    }
+
+    public Integer getUsdKrw() {
+        return usdKrw;
+    }
+
+    public void setUsdKrw(Integer usdKrw) {
+        this.usdKrw = usdKrw;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/AttachedFileCondition.java b/src/main/java/kr/wisestone/owl/web/condition/AttachedFileCondition.java
new file mode 100644
index 0000000..7227cfc
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/AttachedFileCondition.java
@@ -0,0 +1,47 @@
+package kr.wisestone.owl.web.condition;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-02-27.
+ */
+public class AttachedFileCondition {
+    private Long workspaceId;
+    private Long issueId;
+    private List<Long> issueIds = Lists.newArrayList();
+
+    public AttachedFileCondition(){}
+
+    public static AttachedFileCondition make(Map<String, Object> conditions) {
+        AttachedFileCondition condition = ConvertUtil.convertMapToClass(conditions, AttachedFileCondition.class);
+        return condition;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public Long getIssueId() {
+        return issueId;
+    }
+
+    public void setIssueId(Long issueId) {
+        this.issueId = issueId;
+    }
+
+    public List<Long> getIssueIds() {
+        return issueIds;
+    }
+
+    public void setIssueIds(List<Long> issueIds) {
+        this.issueIds = issueIds;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/CustomFieldCondition.java b/src/main/java/kr/wisestone/owl/web/condition/CustomFieldCondition.java
new file mode 100644
index 0000000..b979bec
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/CustomFieldCondition.java
@@ -0,0 +1,101 @@
+package kr.wisestone.owl.web.condition;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.enumType.CustomFieldType;
+import kr.wisestone.owl.domain.enumType.IssueStatusType;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-28.
+ */
+public class CustomFieldCondition {
+    private Long id;
+    private String name;
+    private Integer page;
+    private Integer pageSize;
+    private Long workspaceId;
+    private List<CustomFieldType> customFieldTypes = Lists.newArrayList();
+    private String deep;    //  �긽�꽭 議고쉶�뿉�꽌 媛��졇�삱 �뜲�씠�꽣�쓽 醫낅쪟瑜� 寃곗젙�븳�떎.
+
+    public CustomFieldCondition(){}
+
+    public static CustomFieldCondition make(Map<String, Object> conditions) {
+        CustomFieldCondition condition = ConvertUtil.convertMapToClass(conditions, CustomFieldCondition.class);
+
+        if (MapUtil.getStrings(conditions, "customFieldTypes") != null) {
+            for (String customFieldTypeString : MapUtil.getStrings(conditions, "customFieldTypes")) {
+                CustomFieldType customFieldType = CustomFieldType.valueOf(customFieldTypeString);
+
+                if (customFieldType != null) {
+                    condition.addCustomFieldTypes(customFieldType);
+                }
+            }
+        }
+
+        return condition;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public String getDeep() {
+        return deep;
+    }
+
+    public void setDeep(String deep) {
+        this.deep = deep;
+    }
+
+    public List<CustomFieldType> getCustomFieldTypes() {
+        return customFieldTypes;
+    }
+
+    public void setCustomFieldTypes(List<CustomFieldType> customFieldTypes) {
+        this.customFieldTypes = customFieldTypes;
+    }
+
+    public void addCustomFieldTypes(CustomFieldType customFieldType) {
+        this.customFieldTypes.add(customFieldType);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/EventCondition.java b/src/main/java/kr/wisestone/owl/web/condition/EventCondition.java
new file mode 100644
index 0000000..78d282d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/EventCondition.java
@@ -0,0 +1,71 @@
+package kr.wisestone.owl.web.condition;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+public class EventCondition {
+    private Long id;
+    private String title;
+    private String description;
+    private Integer status;
+    private Integer page;
+    private Integer pageSize;
+
+    public EventCondition(){}
+
+    public static EventCondition make(Map<String, Object> conditions) {
+        return ConvertUtil.convertMapToClass(conditions, EventCondition.class);
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/FaqCondition.java b/src/main/java/kr/wisestone/owl/web/condition/FaqCondition.java
new file mode 100644
index 0000000..7a2db78
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/FaqCondition.java
@@ -0,0 +1,71 @@
+package kr.wisestone.owl.web.condition;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * zenith at 20200730
+ */
+public class FaqCondition {
+    private Long id;
+    private String title;
+    private String description;
+    private Integer status;
+    private Integer page;
+    private Integer pageSize;
+
+    public FaqCondition(){}
+
+    public static FaqCondition make(Map<String, Object> conditions) {
+        return ConvertUtil.convertMapToClass(conditions, FaqCondition.class);
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/GuideCondition.java b/src/main/java/kr/wisestone/owl/web/condition/GuideCondition.java
new file mode 100644
index 0000000..88cdbbe
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/GuideCondition.java
@@ -0,0 +1,71 @@
+package kr.wisestone.owl.web.condition;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+public class GuideCondition {
+    private Long id;
+    private String title;
+    private String description;
+    private Integer status;
+    private Integer page;
+    private Integer pageSize;
+
+    public GuideCondition(){}
+
+    public static GuideCondition make(Map<String, Object> conditions) {
+        return ConvertUtil.convertMapToClass(conditions, GuideCondition.class);
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/IssueCondition.java b/src/main/java/kr/wisestone/owl/web/condition/IssueCondition.java
new file mode 100644
index 0000000..c462a95
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/IssueCondition.java
@@ -0,0 +1,396 @@
+package kr.wisestone.owl.web.condition;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.DateUtil;
+import kr.wisestone.owl.util.MapUtil;
+import org.springframework.util.StringUtils;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-04.
+ */
+public class IssueCondition {
+    private Long id;
+    private String title;
+    private String description;
+    private String combinationIssueNumber;
+    private String projectKey;
+    private String issueNumber;
+    private String beginRegisterDate;
+    private String endRegisterDate;
+    private String beginStartDate;
+    private String endStartDate;
+    private String beginCompleteDate;
+    private String endCompleteDate;
+    private Integer page;
+    private Integer pageSize;
+    private Long loginUserId;
+    private Long workspaceId;
+    private String projectType;
+    private String deep;
+    private List<Long> projectIds = Lists.newArrayList();
+    private List<Long> issueStatusIds = Lists.newArrayList();
+    private List<Long> issueTypeIds = Lists.newArrayList();
+    private List<Long> priorityIds = Lists.newArrayList();
+    private List<Long> severityIds = Lists.newArrayList();
+    private List<Long> userIds = Lists.newArrayList();
+    private List<Long> registerIds = Lists.newArrayList();
+    private List<String> issueIds = Lists.newArrayList();   //  �씠�뒋 紐⑸줉 寃��깋�뿉�꽌 �궗�슜�옄 �젙�쓽 �븘�뱶濡� 1李� 寃��깋�븳 寃곌낵瑜� �떞�쓣�븣 �궗�슜
+    private List<Map<String, Object>> issueCustomFields = Lists.newArrayList();
+    private List<Long> statusIds = Lists.newArrayList();
+    private List<Long> excludeIds = Lists.newArrayList();
+
+
+    public IssueCondition(){}
+    //  ���떆蹂대뱶 �쐞湲곌�由� �쐞�젽�뿉�꽌 �궗�슜
+    public IssueCondition(List<String> issueIds){
+        this.issueIds = issueIds;
+    }
+
+    public static IssueCondition make(Map<String, Object> conditions) {
+        IssueCondition condition = ConvertUtil.convertMapToClass(conditions, IssueCondition.class);
+
+        if (!StringUtils.isEmpty(condition.getCombinationIssueNumber())) {
+            if (condition.getCombinationIssueNumber().contains("-")) {
+                String [] combinations = condition.getCombinationIssueNumber().split("-");
+
+                if (combinations.length > 0) {
+                    condition.setProjectKey(combinations[0].trim());
+                    if (combinations.length > 1) {
+                        condition.setIssueNumber(combinations[1].trim());
+                    }
+                }
+            }
+            else {
+                if (CommonUtil.isNumeric(condition.getCombinationIssueNumber())) {
+                    condition.setIssueNumber(condition.getCombinationIssueNumber());
+                }
+                else {
+                    condition.setProjectKey(condition.getCombinationIssueNumber());
+                }
+            }
+        }
+
+        if (MapUtil.getString(conditions, "name") != null) {
+            condition.setTitle(MapUtil.getString(conditions, "name"));
+        }
+
+        if (MapUtil.getObject(conditions, "issueCustomFields") != null) {
+            condition.setIssueCustomFields((List)MapUtil.getObject(conditions, "issueCustomFields"));
+        }
+
+        if (StringUtils.hasText(MapUtil.getString(conditions, "beginRegisterDate"))) {
+            Date beginRegisterDate = DateUtil.convertStrToDate(MapUtil.getString(conditions, "beginRegisterDate"), "yy-MM-dd");
+            condition.setBeginRegisterDate(DateUtil.convertDateToStr(beginRegisterDate, "yyyy-MM-dd"));
+        }
+
+        if (StringUtils.hasText(MapUtil.getString(conditions, "endRegisterDate"))) {
+            Date endRegisterDate = DateUtil.addDays(DateUtil.convertStrToDate(MapUtil.getString(conditions, "endRegisterDate"), "yy-MM-dd"), 1);
+            condition.setEndRegisterDate(DateUtil.convertDateToStr(endRegisterDate, "yyyy-MM-dd"));
+        }
+
+        if (StringUtils.hasText(MapUtil.getString(conditions, "beginStartDate"))) {
+            Date beginStartDate = DateUtil.convertStrToDate(MapUtil.getString(conditions, "beginStartDate"), "yy-MM-dd");
+            condition.setBeginStartDate(DateUtil.convertDateToStr(beginStartDate, "yyyy-MM-dd"));
+        }
+
+        if (StringUtils.hasText(MapUtil.getString(conditions, "endStartDate"))) {
+            Date endStartDate = DateUtil.convertStrToDate(MapUtil.getString(conditions, "endStartDate"), "yy-MM-dd");
+            condition.setEndStartDate(DateUtil.convertDateToStr(endStartDate, "yyyy-MM-dd"));
+        }
+
+        if (StringUtils.hasText(MapUtil.getString(conditions, "beginCompleteDate"))) {
+            Date beginCompleteDate = DateUtil.convertStrToDate(MapUtil.getString(conditions, "beginCompleteDate"), "yy-MM-dd");
+            condition.setBeginCompleteDate(DateUtil.convertDateToStr(beginCompleteDate, "yyyy-MM-dd"));
+        }
+
+        if (StringUtils.hasText(MapUtil.getString(conditions, "endCompleteDate"))) {
+            Date endCompleteDate = DateUtil.convertStrToDate(MapUtil.getString(conditions, "endCompleteDate"), "yy-MM-dd");
+            condition.setEndCompleteDate(DateUtil.convertDateToStr(endCompleteDate, "yyyy-MM-dd"));
+        }
+
+        if (MapUtil.getLongs(conditions, "projectIds") != null) {
+            condition.setProjectIds(MapUtil.getLongs(conditions, "projectIds"));
+        }
+
+        if (MapUtil.getLongs(conditions, "issueStatusIds") != null) {
+            condition.setIssueStatusIds(MapUtil.getLongs(conditions, "issueStatusIds"));
+        }
+
+        if (MapUtil.getLongs(conditions, "issueTypeIds") != null) {
+            condition.setIssueTypeIds(MapUtil.getLongs(conditions, "issueTypeIds"));
+        }
+
+        if (MapUtil.getLongs(conditions, "priorityIds") != null) {
+            condition.setPriorityIds(MapUtil.getLongs(conditions, "priorityIds"));
+        }
+
+        if (MapUtil.getLongs(conditions, "severityIds") != null) {
+            condition.setSeverityIds(MapUtil.getLongs(conditions, "severityIds"));
+        }
+
+        if (MapUtil.getLongs(conditions, "userIds") != null) {
+            condition.setUserIds(MapUtil.getLongs(conditions, "userIds"));
+        }
+
+        if (MapUtil.getLongs(conditions, "registerIds") != null) {
+            condition.setRegisterIds(MapUtil.getLongs(conditions, "registerIds"));
+        }
+
+        if (MapUtil.getLongs(conditions, "excludeIds") != null) {
+            condition.setExcludeIds(MapUtil.getLongs(conditions, "excludeIds"));
+        }
+
+        return condition;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getCombinationIssueNumber() {
+        return combinationIssueNumber;
+    }
+
+    public void setCombinationIssueNumber(String combinationIssueNumber) {
+        this.combinationIssueNumber = combinationIssueNumber;
+    }
+
+    public String getProjectKey() {
+        return projectKey;
+    }
+
+    public void setProjectKey(String projectKey) {
+        this.projectKey = projectKey;
+    }
+
+    public String getIssueNumber() {
+        return issueNumber;
+    }
+
+    public void setIssueNumber(String issueNumber) {
+        this.issueNumber = issueNumber;
+    }
+
+    public String getBeginStartDate() {
+        return beginStartDate;
+    }
+
+    public void setBeginStartDate(String beginStartDate) {
+        this.beginStartDate = beginStartDate;
+    }
+
+    public String getEndStartDate() {
+        return endStartDate;
+    }
+
+    public void setEndStartDate(String endStartDate) {
+        this.endStartDate = endStartDate;
+    }
+
+    public String getBeginCompleteDate() {
+        return beginCompleteDate;
+    }
+
+    public void setBeginCompleteDate(String beginCompleteDate) {
+        this.beginCompleteDate = beginCompleteDate;
+    }
+
+    public String getEndCompleteDate() {
+        return endCompleteDate;
+    }
+
+    public void setEndCompleteDate(String endCompleteDate) {
+        this.endCompleteDate = endCompleteDate;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Long getLoginUserId() {
+        return loginUserId;
+    }
+
+    public void setLoginUserId(Long loginUserId) {
+        this.loginUserId = loginUserId;
+    }
+
+    public String getDeep() {
+        return deep;
+    }
+
+    public void setDeep(String deep) {
+        this.deep = deep;
+    }
+
+    public List<Long> getProjectIds() {
+        return projectIds;
+    }
+
+    public void addProjectIds(Long projectId) {
+        this.projectIds.add(projectId);
+    }
+
+    public void setProjectIds(List<Long> projectIds) {
+        this.projectIds = projectIds;
+    }
+
+    public List<Long> getIssueStatusIds() {
+        return issueStatusIds;
+    }
+
+    public void setIssueStatusIds(List<Long> issueStatusIds) {
+        this.issueStatusIds = issueStatusIds;
+    }
+
+    public List<Long> getIssueTypeIds() {
+        return issueTypeIds;
+    }
+
+    public void setIssueTypeIds(List<Long> issueTypeIds) {
+        this.issueTypeIds = issueTypeIds;
+    }
+
+    public void addIssueTypeIds(Long issueTypeId) {
+        this.issueTypeIds.add(issueTypeId);
+    }
+
+    public List<Long> getPriorityIds() {
+        return priorityIds;
+    }
+
+    public void setPriorityIds(List<Long> priorityIds) {
+        this.priorityIds = priorityIds;
+    }
+
+    public List<Long> getSeverityIds() {
+        return severityIds;
+    }
+
+    public void setSeverityIds(List<Long> severityIds) {
+        this.severityIds = severityIds;
+    }
+
+    public List<Long> getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(List<Long> userIds) {
+        this.userIds = userIds;
+    }
+
+    public List<Long> getRegisterIds() {
+        return registerIds;
+    }
+
+    public void setRegisterIds(List<Long> registerIds) {
+        this.registerIds = registerIds;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public List<Map<String, Object>> getIssueCustomFields() {
+        return issueCustomFields;
+    }
+
+    public void setIssueCustomFields(List<Map<String, Object>> issueCustomFields) {
+        this.issueCustomFields = issueCustomFields;
+    }
+
+    public List<String> getIssueIds() {
+        return issueIds;
+    }
+
+    public void setIssueIds(List<String> issueIds) {
+        this.issueIds = issueIds;
+    }
+
+    public void addIssueIds(String issueId) {
+        this.issueIds.add(issueId);
+    }
+
+    public String getBeginRegisterDate() {
+        return beginRegisterDate;
+    }
+
+    public void setBeginRegisterDate(String beginRegisterDate) {
+        this.beginRegisterDate = beginRegisterDate;
+    }
+
+    public String getEndRegisterDate() {
+        return endRegisterDate;
+    }
+
+    public void setEndRegisterDate(String endRegisterDate) {
+        this.endRegisterDate = endRegisterDate;
+    }
+
+    public String getProjectType() {
+        return projectType;
+    }
+
+    public void setProjectType(String projectType) {
+        this.projectType = projectType;
+    }
+
+    public void addStatusId(Long statusId) {
+        this.statusIds.add(statusId);
+    }
+
+    public List<Long> getStatusIds() {
+        return statusIds;
+    }
+
+    public List<Long> getExcludeIds() {
+        return excludeIds;
+    }
+
+    public void setExcludeIds(List<Long> excludeIds) {
+        this.excludeIds = excludeIds;
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/IssueCustomFieldValueCondition.java b/src/main/java/kr/wisestone/owl/web/condition/IssueCustomFieldValueCondition.java
new file mode 100644
index 0000000..61d82dd
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/IssueCustomFieldValueCondition.java
@@ -0,0 +1,100 @@
+package kr.wisestone.owl.web.condition;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.enumType.CustomFieldType;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-06-07.
+ */
+public class IssueCustomFieldValueCondition {
+    private Long workspaceId;
+    private Long customFieldId;
+    private String customFieldType;
+    private List<String> useValues = Lists.newArrayList();  //  �떒�씪, �떎以� �씪�븣 寃��깋 媛�
+    private String useValue;    //  �뀓�뒪�듃 �븘�뱶�씪 �븣 寃��깋 媛�
+
+    public IssueCustomFieldValueCondition(){}
+
+    public static IssueCustomFieldValueCondition make(Map<String, Object> conditions) {
+        IssueCustomFieldValueCondition condition = ConvertUtil.convertMapToClass(conditions, IssueCustomFieldValueCondition.class);
+
+        CustomFieldType customFieldType = CustomFieldType.valueOf(condition.getCustomFieldType());
+
+        switch(customFieldType) {
+            case INPUT:
+                if (MapUtil.getStrings(conditions, "useValues") != null) {
+                    //  怨듬갚�씠 �븘�땶 臾몄옄媛� �뱾�뼱�엳�쓣 �븣留� useValues 媛� �쑝濡� �뀑�똿�븳�떎.
+                    for (String useValue : MapUtil.getStrings(conditions, "useValues")) {
+                        //  �뀓�뒪�듃 �븘�뱶�뒗 1媛쒕컰�뿉 �븞�뱾�뼱�삩�떎.
+                        if (!StringUtils.isEmpty(useValue)) {
+                            condition.setUseValue(useValue);
+                        }
+                    }
+                }
+                break;
+            case SINGLE_SELECT:
+            case MULTI_SELECT:
+                if (MapUtil.getStrings(conditions, "useValues") != null) {
+                    //  怨듬갚�씠 �븘�땶 臾몄옄媛� �뱾�뼱�엳�쓣 �븣留� useValues 媛� �쑝濡� �뀑�똿�븳�떎.
+                    for (String useValue : MapUtil.getStrings(conditions, "useValues")) {
+                        if (!StringUtils.isEmpty(useValue)) {
+                            condition.addUseValues(useValue);
+                        }
+                    }
+                }
+                break;
+        }
+
+        return condition;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public Long getCustomFieldId() {
+        return customFieldId;
+    }
+
+    public void setCustomFieldId(Long customFieldId) {
+        this.customFieldId = customFieldId;
+    }
+
+    public List<String> getUseValues() {
+        return useValues;
+    }
+
+    public void setUseValues(List<String> useValues) {
+        this.useValues = useValues;
+    }
+
+    public void addUseValues(String useValue) {
+        this.useValues.add(useValue);
+    }
+
+    public String getUseValue() {
+        return useValue;
+    }
+
+    public void setUseValue(String useValue) {
+        this.useValue = useValue;
+    }
+
+    public String getCustomFieldType() {
+        return customFieldType;
+    }
+
+    public void setCustomFieldType(String customFieldType) {
+        this.customFieldType = customFieldType;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/IssueHistoryCondition.java b/src/main/java/kr/wisestone/owl/web/condition/IssueHistoryCondition.java
new file mode 100644
index 0000000..467d4e0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/IssueHistoryCondition.java
@@ -0,0 +1,54 @@
+package kr.wisestone.owl.web.condition;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-10-17.
+ */
+public class IssueHistoryCondition {
+    private Long userId;
+    private String searchPeriod;    //  �쟾泥� �씠�뒋 泥섎━ �쁽�솴�뿉�꽌 �궗�슜
+    private String searchStartDate; //  �쟾泥� �씠�뒋 泥섎━ �쁽�솴�뿉�꽌 寃��깋 �떆�옉�씪
+    private String searchEndDate;   //  �쟾泥� �씠�뒋 泥섎━ �쁽�솴�뿉�꽌 寃��깋 醫낅즺�씪
+
+    public IssueHistoryCondition(){}
+
+    public static IssueHistoryCondition make(Map<String, Object> conditions) {
+        IssueHistoryCondition condition = ConvertUtil.convertMapToClass(conditions, IssueHistoryCondition.class);
+        return condition;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getSearchPeriod() {
+        return searchPeriod;
+    }
+
+    public void setSearchPeriod(String searchPeriod) {
+        this.searchPeriod = searchPeriod;
+    }
+
+    public String getSearchStartDate() {
+        return searchStartDate;
+    }
+
+    public void setSearchStartDate(String searchStartDate) {
+        this.searchStartDate = searchStartDate;
+    }
+
+    public String getSearchEndDate() {
+        return searchEndDate;
+    }
+
+    public void setSearchEndDate(String searchEndDate) {
+        this.searchEndDate = searchEndDate;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/IssueRelationCondition.java b/src/main/java/kr/wisestone/owl/web/condition/IssueRelationCondition.java
new file mode 100644
index 0000000..2749904
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/IssueRelationCondition.java
@@ -0,0 +1,54 @@
+package kr.wisestone.owl.web.condition;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * Create By Maprex / 2021-05-13
+ */
+public class IssueRelationCondition {
+    private  Long id;
+    private  Long issueId;
+    private  Long relationIssueId;
+    private  Long relationIssueType;
+
+    public IssueRelationCondition() {}
+
+    public static IssueRelationCondition make(Map<String, Object> params) {
+        IssueRelationCondition condition = ConvertUtil.convertMapToClass(params, IssueRelationCondition.class);
+        return condition;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getIssueId() {
+        return issueId;
+    }
+
+    public void setIssueId(Long issueId) {
+        this.issueId = issueId;
+    }
+
+    public Long getRelationIssueId() {
+        return relationIssueId;
+    }
+
+    public void setRelationIssueId(Long issueId) {
+        this.relationIssueId = issueId;
+    }
+
+    public Long getRelationIssueType() {
+        return relationIssueType;
+    }
+
+    public void setRelationIssueType(Long type) {
+        this.relationIssueType = type;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/IssueReservationCondition.java b/src/main/java/kr/wisestone/owl/web/condition/IssueReservationCondition.java
new file mode 100644
index 0000000..1dc9ed5
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/IssueReservationCondition.java
@@ -0,0 +1,35 @@
+package kr.wisestone.owl.web.condition;
+
+import kr.wisestone.owl.util.ConvertUtil;
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-08
+ */
+public class IssueReservationCondition {
+    private Long id;
+    private Long issueId;
+
+    public IssueReservationCondition(){}
+
+    public static IssueReservationCondition make(Map<String, Object> conditions) {
+        IssueReservationCondition condition = ConvertUtil.convertMapToClass(conditions, IssueReservationCondition.class);
+        return condition;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getIssueId() {
+        return issueId;
+    }
+
+    public void setIssueId(Long issueId) {
+        this.issueId = issueId;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/IssueStatusCondition.java b/src/main/java/kr/wisestone/owl/web/condition/IssueStatusCondition.java
new file mode 100644
index 0000000..e9b3a14
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/IssueStatusCondition.java
@@ -0,0 +1,132 @@
+package kr.wisestone.owl.web.condition;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.enumType.IssueStatusType;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-08.
+ */
+public class IssueStatusCondition {
+    private Long id;
+    private String name;
+    private Integer page;
+    private Integer pageSize;
+    private Long workspaceId;
+    private Long workflowId;
+    private Long issueTypeId;   //  �씠�뒋 �긽�깭 蹂�寃쎌뿉�꽌 �궗�슜
+    private List<IssueStatusType> issueStatusTypes = Lists.newArrayList();
+    private List<Long> issueIds = Lists.newArrayList();
+    private String deep;    //  �긽�꽭 議고쉶�뿉�꽌 媛��졇�삱 �뜲�씠�꽣�쓽 醫낅쪟瑜� 寃곗젙�븳�떎.
+
+    public IssueStatusCondition() {
+    }
+
+    public static IssueStatusCondition make(Map<String, Object> conditions) {
+        IssueStatusCondition condition = ConvertUtil.convertMapToClass(conditions, IssueStatusCondition.class);
+
+        if (MapUtil.getStrings(conditions, "issueStatusTypes") != null) {
+            for (String issueStatusTypeString : MapUtil.getStrings(conditions, "issueStatusTypes")) {
+                IssueStatusType issueStatusType = IssueStatusType.valueOf(issueStatusTypeString);
+
+                if (issueStatusType != null) {
+                    condition.addIssueStatusTypes(issueStatusType);
+                }
+            }
+        }
+
+        if (MapUtil.getLongs(conditions, "issueIds") != null) {
+            condition.setIssueIds(MapUtil.getLongs(conditions, "issueIds"));
+        }
+
+        return condition;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public Long getWorkflowId() {
+        return workflowId;
+    }
+
+    public void setWorkflowId(Long workflowId) {
+        this.workflowId = workflowId;
+    }
+
+    public List<IssueStatusType> getIssueStatusTypes() {
+        return issueStatusTypes;
+    }
+
+    public void setIssueStatusTypes(List<IssueStatusType> issueStatusTypes) {
+        this.issueStatusTypes = issueStatusTypes;
+    }
+
+    public void addIssueStatusTypes(IssueStatusType issueStatusType) {
+        this.issueStatusTypes.add(issueStatusType);
+    }
+
+    public String getDeep() {
+        return deep;
+    }
+
+    public void setDeep(String deep) {
+        this.deep = deep;
+    }
+
+    public Long getIssueTypeId() {
+        return issueTypeId;
+    }
+
+    public void setIssueTypeId(Long issueTypeId) {
+        this.issueTypeId = issueTypeId;
+    }
+
+    public List<Long> getIssueIds() {
+        return issueIds;
+    }
+
+    public void setIssueIds(List<Long> issueIds) {
+        this.issueIds = issueIds;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/IssueTypeCondition.java b/src/main/java/kr/wisestone/owl/web/condition/IssueTypeCondition.java
new file mode 100644
index 0000000..7de6096
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/IssueTypeCondition.java
@@ -0,0 +1,72 @@
+package kr.wisestone.owl.web.condition;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-29.
+ */
+public class IssueTypeCondition {
+    private Long id;
+    private String name;
+    private Integer page;
+    private Integer pageSize;
+    private Long workspaceId;
+    private String deep;    //  �긽�꽭 議고쉶�뿉�꽌 媛��졇�삱 �뜲�씠�꽣�쓽 醫낅쪟瑜� 寃곗젙�븳�떎.
+
+    public IssueTypeCondition(){}
+
+    public static IssueTypeCondition make(Map<String, Object> conditions) {
+        IssueTypeCondition condition = ConvertUtil.convertMapToClass(conditions, IssueTypeCondition.class);
+        return condition;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public String getDeep() {
+        return deep;
+    }
+
+    public void setDeep(String deep) {
+        this.deep = deep;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/IssueTypeCustomFieldCondition.java b/src/main/java/kr/wisestone/owl/web/condition/IssueTypeCustomFieldCondition.java
new file mode 100644
index 0000000..90debdb
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/IssueTypeCustomFieldCondition.java
@@ -0,0 +1,36 @@
+package kr.wisestone.owl.web.condition;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-30.
+ */
+public class IssueTypeCustomFieldCondition {
+    private Long projectId;
+    private Long issueTypeId;
+
+    public IssueTypeCustomFieldCondition(){}
+
+    public static IssueTypeCustomFieldCondition make(Map<String, Object> conditions) {
+        IssueTypeCustomFieldCondition condition = ConvertUtil.convertMapToClass(conditions, IssueTypeCustomFieldCondition.class);
+        return condition;
+    }
+
+    public Long getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(Long projectId) {
+        this.projectId = projectId;
+    }
+
+    public Long getIssueTypeId() {
+        return issueTypeId;
+    }
+
+    public void setIssueTypeId(Long issueTypeId) {
+        this.issueTypeId = issueTypeId;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/IssueVersionCondition.java b/src/main/java/kr/wisestone/owl/web/condition/IssueVersionCondition.java
new file mode 100644
index 0000000..324faff
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/IssueVersionCondition.java
@@ -0,0 +1,60 @@
+package kr.wisestone.owl.web.condition;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+public class IssueVersionCondition {
+    private Long id;
+    private Long issueId;
+    private Integer version;
+    private String content;
+    private Long compareVersionId;  //  鍮꾧탳�븷 踰꾩쟾
+
+    public IssueVersionCondition() {}
+
+    public static IssueVersionCondition make(Map<String, Object> conditions) {
+        IssueVersionCondition condition = ConvertUtil.convertMapToClass(conditions, IssueVersionCondition.class);
+        return condition;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getIssueId() {
+        return issueId;
+    }
+
+    public void setIssueId(Long issueId) {
+        this.issueId = issueId;
+    }
+
+    public Integer getVersion() {
+        return version;
+    }
+
+    public void setVersion(Integer version) {
+        this.version = version;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public Long getCompareVersionId() {
+        return compareVersionId;
+    }
+
+    public void setCompareVersionId(Long compareVersionId) {
+        this.compareVersionId = compareVersionId;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/NoticeCondition.java b/src/main/java/kr/wisestone/owl/web/condition/NoticeCondition.java
new file mode 100644
index 0000000..6cc7641
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/NoticeCondition.java
@@ -0,0 +1,62 @@
+package kr.wisestone.owl.web.condition;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+public class NoticeCondition {
+    private Long id;
+    private String title;
+    private String description;
+    private Integer page;
+    private Integer pageSize;
+
+    public NoticeCondition(){}
+
+    public static NoticeCondition make(Map<String, Object> conditions) {
+        return ConvertUtil.convertMapToClass(conditions, NoticeCondition.class);
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/ProjectCondition.java b/src/main/java/kr/wisestone/owl/web/condition/ProjectCondition.java
new file mode 100644
index 0000000..18448d6
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/ProjectCondition.java
@@ -0,0 +1,168 @@
+package kr.wisestone.owl.web.condition;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-02.
+ */
+public class ProjectCondition {
+    private Long id;
+    private Long parentProjectId;
+    private String name;
+    private Integer page;
+    private Integer pageSize;
+    private Long loginUserId;
+    private Long workspaceId;
+    private String projectType;
+    private Boolean workspaceManager = Boolean.FALSE;   //  �봽濡쒖젥�듃 紐⑸줉 議고쉶�뿉�꽌 議고쉶 �슂泥��븷 寃쎌슦 �뾽臾� 怨듦컙 �떞�떦�옄�뒗 紐⑤뱺 �봽濡쒖젥�듃 紐⑸줉�씠 �몴�떆�릺�뼱�빞�븳�떎.
+    private String deep;    //  �긽�꽭 議고쉶�뿉�꽌 媛��졇�삱 �뜲�씠�꽣�쓽 醫낅쪟瑜� 寃곗젙�븳�떎.
+    private List<String> statuses = Lists.newArrayList();
+    private List<String> roleTypes = Lists.newArrayList();  //  �봽濡쒖젥�듃 愿�由ъ옄, �씪諛� 李몄뿬�옄瑜� 援щ텇�빐�꽌 媛��졇�삩�떎.
+    private List<Long> excludeIds = Lists.newArrayList();
+    private List<Long> userIds = Lists.newArrayList();
+
+    public ProjectCondition(){}
+
+    public ProjectCondition(Long id, Long loginUserId){
+        this.id = id;
+        this.loginUserId = loginUserId;
+    }
+
+    public static ProjectCondition make(Map<String, Object> conditions) {
+        ProjectCondition condition = ConvertUtil.convertMapToClass(conditions, ProjectCondition.class);
+        if (MapUtil.getStrings(conditions, "statuses") != null) {
+            condition.setStatuses(MapUtil.getStrings(conditions, "statuses"));
+        }
+
+        if (MapUtil.getStrings(conditions, "roleTypes") != null) {
+            condition.setRoleTypes(MapUtil.getStrings(conditions, "roleTypes"));
+        }
+
+        if (MapUtil.getLongs(conditions, "excludeIds") != null) {
+            condition.setExcludeIds(MapUtil.getLongs(conditions, "excludeIds"));
+        }
+
+        if (MapUtil.getLongs(conditions, "userIds") != null) {
+            condition.setUserIds(MapUtil.getLongs(conditions, "userIds"));
+        }
+
+        return condition;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getParentProjectId() {
+        return parentProjectId;
+    }
+
+    public void setParentProjectId(Long id) {
+        this.parentProjectId = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Long getLoginUserId() {
+        return loginUserId;
+    }
+
+    public void setLoginUserId(Long loginUserId) {
+        this.loginUserId = loginUserId;
+    }
+
+    public List<String> getStatuses() {
+        return statuses;
+    }
+
+    public void setStatuses(List<String> statuses) {
+        this.statuses = statuses;
+    }
+
+    public List<Long> getExcludeIds() {
+        return excludeIds;
+    }
+
+    public void setExcludeIds(List<Long> excludeIds) {
+        this.excludeIds = excludeIds;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public List<Long> getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(List<Long> userIds) {
+        this.userIds = userIds;
+    }
+
+    public String getDeep() {
+        return deep;
+    }
+
+    public void setDeep(String deep) {
+        this.deep = deep;
+    }
+
+    public List<String> getRoleTypes() {
+        return roleTypes;
+    }
+
+    public void setRoleTypes(List<String> roleTypes) {
+        this.roleTypes = roleTypes;
+    }
+
+    public String getProjectType() {
+        return projectType;
+    }
+
+    public void setProjectType(String projectType) {
+        this.projectType = projectType;
+    }
+
+    public Boolean getWorkspaceManager() {
+        return workspaceManager;
+    }
+
+    public void setWorkspaceManager(Boolean workspaceManager) {
+        this.workspaceManager = workspaceManager;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/QnaCondition.java b/src/main/java/kr/wisestone/owl/web/condition/QnaCondition.java
new file mode 100644
index 0000000..8484642
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/QnaCondition.java
@@ -0,0 +1,62 @@
+package kr.wisestone.owl.web.condition;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * zenith at 20200730
+ */
+public class QnaCondition {
+    private Long id;
+    private String title;
+    private String description;
+    private Integer page;
+    private Integer pageSize;
+
+    public QnaCondition(){}
+
+    public static QnaCondition make(Map<String, Object> conditions) {
+        return ConvertUtil.convertMapToClass(conditions, QnaCondition.class);
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/UserCondition.java b/src/main/java/kr/wisestone/owl/web/condition/UserCondition.java
new file mode 100644
index 0000000..2be9a16
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/UserCondition.java
@@ -0,0 +1,164 @@
+package kr.wisestone.owl.web.condition;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by jeong on 2018-01-01.
+ */
+public class UserCondition {
+    private Long id;
+    private String name;
+    private String account;
+    private String email;
+    private String phone;
+    private String deep;    //  �긽�꽭 議고쉶�뿉�꽌 媛��졇�삱 �뜲�씠�꽣�쓽 醫낅쪟瑜� 寃곗젙�븳�떎.
+    private List<String> statuses = Lists.newArrayList();
+    private Long projectId;
+    private List<Long> excludeIds = Lists.newArrayList();
+    private Integer page;
+    private Integer pageSize;
+    private Long loginUserId;
+    private Long workspaceId;
+    private Integer permission;
+    private String licensekey;
+
+    public static UserCondition make(Map<String, Object> conditions) {
+        UserCondition condition = ConvertUtil.convertMapToClass(conditions, UserCondition.class);
+        condition.setStatuses(MapUtil.getStrings(conditions, "statuses"));
+        condition.setExcludeIds(MapUtil.getLongs(conditions, "excludeIds"));
+
+        return condition;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public List<String> getStatuses() {
+        return statuses;
+    }
+
+    public void setStatuses(List<String> statuses) {
+        this.statuses = statuses;
+    }
+
+    public Long getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(Long projectId) {
+        this.projectId = projectId;
+    }
+
+    public List<Long> getExcludeIds() {
+        return excludeIds;
+    }
+
+    public void setExcludeIds(List<Long> excludeIds) {
+        this.excludeIds = excludeIds;
+    }
+
+    public void addExcludeIds(Long excludeId) {
+        this.excludeIds.add(excludeId);
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Long getLoginUserId() {
+        return loginUserId;
+    }
+
+    public void setLoginUserId(Long loginUserId) {
+        this.loginUserId = loginUserId;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public String getDeep() {
+        return deep;
+    }
+
+    public void setDeep(String deep) {
+        this.deep = deep;
+    }
+
+    public Integer getPermission() {
+        return permission;
+    }
+
+    public void setPermission(Integer permission) {
+        this.permission = permission;
+    }
+
+    public String getLicensekey() {
+        return licensekey;
+    }
+
+    public void setLicensekey(String licensekey) {
+        this.licensekey = licensekey;
+    }
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/UserHistoryCondition.java b/src/main/java/kr/wisestone/owl/web/condition/UserHistoryCondition.java
new file mode 100644
index 0000000..804ea73
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/UserHistoryCondition.java
@@ -0,0 +1,25 @@
+package kr.wisestone.owl.web.condition;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+public class UserHistoryCondition {
+    private Long UserId;
+    private String historyType;
+
+    public static UserHistoryCondition make(Map<String, Object> conditions) {
+        UserHistoryCondition condition = ConvertUtil.convertMapToClass(conditions, UserHistoryCondition.class);
+
+        return condition;
+    }
+
+    public  void setHistoryType(String historyType) { this.historyType = historyType; }
+
+    public String getHistoryType() {
+        return this.historyType;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/UserWorkspaceCondition.java b/src/main/java/kr/wisestone/owl/web/condition/UserWorkspaceCondition.java
new file mode 100644
index 0000000..3ee0ad1
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/UserWorkspaceCondition.java
@@ -0,0 +1,80 @@
+package kr.wisestone.owl.web.condition;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-10-02.
+ */
+public class UserWorkspaceCondition {
+    private Long workspaceId;
+    private String name;
+    private String account;
+    private List<String> statuses = Lists.newArrayList();
+    private Integer page;
+    private Integer pageSize;
+
+    public UserWorkspaceCondition(){}
+
+    public static UserWorkspaceCondition make(Map<String, Object> conditions) {
+        UserWorkspaceCondition condition = ConvertUtil.convertMapToClass(conditions, UserWorkspaceCondition.class);
+
+        if (MapUtil.getStrings(conditions, "statuses") != null) {
+            condition.setStatuses(MapUtil.getStrings(conditions, "statuses"));
+        }
+
+        return condition;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public List<String> getStatuses() {
+        return statuses;
+    }
+
+    public void setStatuses(List<String> statuses) {
+        this.statuses = statuses;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/WidgetCondition.java b/src/main/java/kr/wisestone/owl/web/condition/WidgetCondition.java
new file mode 100644
index 0000000..1b0facc
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/WidgetCondition.java
@@ -0,0 +1,158 @@
+package kr.wisestone.owl.web.condition;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.IssueStatus;
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-10-30.
+ */
+public class WidgetCondition {
+    private Long loginUserId;
+    private Long workspaceId;
+    private Long workflowId;
+    private List<Long> projectIds;
+    private String completeDate;
+    private Integer page;
+    private Integer pageSize;
+    private Long projectId; //  硫ㅻ쾭蹂� 吏꾪뻾瑜좎뿉�꽌 �궗�슜
+    private String searchPeriod;    //  �쟾泥� �씠�뒋 泥섎━ �쁽�솴�뿉�꽌 �궗�슜
+    private String searchStartDate; //  �쟾泥� �씠�뒋 泥섎━ �쁽�솴�뿉�꽌 寃��깋 �떆�옉�씪
+    private String searchEndDate;   //  �쟾泥� �씠�뒋 泥섎━ �쁽�솴�뿉�꽌 寃��깋 醫낅즺�씪
+    private List<IssueStatus> issueStatuses;  //  �긽�깭蹂� �씠�뒋 �쁽�솴�뿉�꽌 �궗�슜
+    private List<Map<String, Object>> projects = Lists.newArrayList();  //  �빐�떦 �뾽臾� 怨듦컙�뿉 李몄뿬�븯怨� �엳�뒗 吏꾪뻾以묒씤 �봽濡쒖젥�듃 �젙蹂대�� ���옣
+    private Long severityId; // 以묒슂�룄 蹂� �씠�뒋 �쁽�솴�뿉�꽌 �궗�슜
+
+    public WidgetCondition(){}
+
+    public static WidgetCondition make(Map<String, Object> conditions) {
+        WidgetCondition condition = ConvertUtil.convertMapToClass(conditions, WidgetCondition.class);
+
+        return condition;
+    }
+
+    public Long getLoginUserId() {
+        return loginUserId;
+    }
+
+    public void setLoginUserId(Long loginUserId) {
+        this.loginUserId = loginUserId;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public List<Long> getProjectIds() {
+        return this.projectIds;
+    }
+
+    public void setProjectIds(List<Long> projectIds) {
+        this.projectIds = projectIds;
+    }
+
+    public  void setProjectIds(Long projectId) {
+        if (this.projectIds != null)
+            this.projectIds.clear();
+        else
+            this.projectIds = new ArrayList<>();
+
+        this.projectIds.add(projectId);
+    }
+
+    public String getCompleteDate() {
+        return completeDate;
+    }
+
+    public void setCompleteDate(String completeDate) {
+        this.completeDate = completeDate;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Long getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(Long projectId) {
+        this.projectId = projectId;
+    }
+
+    public String getSearchPeriod() {
+        return searchPeriod;
+    }
+
+    public void setSearchPeriod(String searchPeriod) {
+        this.searchPeriod = searchPeriod;
+    }
+
+    public String getSearchStartDate() {
+        return searchStartDate;
+    }
+
+    public void setSearchStartDate(String searchStartDate) {
+        this.searchStartDate = searchStartDate;
+    }
+
+    public String getSearchEndDate() {
+        return searchEndDate;
+    }
+
+    public void setSearchEndDate(String searchEndDate) {
+        this.searchEndDate = searchEndDate;
+    }
+
+    public List<IssueStatus> getIssueStatuses() {
+        return issueStatuses;
+    }
+
+    public void setIssueStatuses(List<IssueStatus> issueStatuses) {
+        this.issueStatuses = issueStatuses;
+    }
+
+    public Long getWorkflowId() {
+        return workflowId;
+    }
+
+    public void setWorkflowId(Long workflowId) {
+        this.workflowId = workflowId;
+    }
+
+    public List<Map<String, Object>> getProjects() {
+        return projects;
+    }
+
+    public void setProjects(List<Map<String, Object>> projects) {
+        this.projects = projects;
+    }
+
+    public Long getSeverityId() {
+        return severityId;
+    }
+
+    public void setSeverityId(Long severityId) {
+        this.severityId = severityId;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/WorkflowCondition.java b/src/main/java/kr/wisestone/owl/web/condition/WorkflowCondition.java
new file mode 100644
index 0000000..88dfd85
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/WorkflowCondition.java
@@ -0,0 +1,73 @@
+package kr.wisestone.owl.web.condition;
+
+
+import kr.wisestone.owl.util.ConvertUtil;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-10.
+ */
+public class WorkflowCondition {
+    private Long id;
+    private String name;
+    private Integer page;
+    private Integer pageSize;
+    private String deep;    //  �긽�꽭 議고쉶�뿉�꽌 媛��졇�삱 �뜲�씠�꽣�쓽 醫낅쪟瑜� 寃곗젙�븳�떎.
+    private Long workspaceId;
+
+    public WorkflowCondition(){}
+
+    public static WorkflowCondition make(Map<String, Object> conditions) {
+        WorkflowCondition condition = ConvertUtil.convertMapToClass(conditions, WorkflowCondition.class);
+
+        return condition;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public String getDeep() {
+        return deep;
+    }
+
+    public void setDeep(String deep) {
+        this.deep = deep;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/condition/WorkflowStatusCondition.java b/src/main/java/kr/wisestone/owl/web/condition/WorkflowStatusCondition.java
new file mode 100644
index 0000000..bb67d35
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/condition/WorkflowStatusCondition.java
@@ -0,0 +1,26 @@
+package kr.wisestone.owl.web.condition;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-08.
+ */
+public class WorkflowStatusCondition {
+    private Long projectId;
+
+    public static WorkflowStatusCondition make(Map<String, Object> conditions) {
+        WorkflowStatusCondition condition = ConvertUtil.convertMapToClass(conditions, WorkflowStatusCondition.class);
+
+        return condition;
+    }
+
+    public Long getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(Long projectId) {
+        this.projectId = projectId;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/AttatchedFileController.java b/src/main/java/kr/wisestone/owl/web/controller/AttatchedFileController.java
new file mode 100644
index 0000000..ac48a53
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/AttatchedFileController.java
@@ -0,0 +1,54 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.AttachedFileService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.AttachedFileVo;
+import kr.wisestone.owl.web.condition.AttachedFileCondition;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+import org.springframework.web.servlet.ModelAndView;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Controller
+public class AttatchedFileController extends BaseController {
+
+    @Autowired
+    private AttachedFileService attachedFileService;
+
+    //  �뙆�씪 �벑濡�
+    @RequestMapping(value = "/attached/add", method = RequestMethod.POST)
+    public @ResponseBody
+    Map<String, Object> add(MultipartHttpServletRequest request) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Map<String, Object> content = ConvertUtil.convertJsonToMap(request.getParameter(Constants.REQ_KEY_CONTENT));
+
+        resJsonData.put("attachedFiles",
+                ConvertUtil.convertObjectsToClasses(this.attachedFileService.addAttachedFile(request.getFiles("file"), content), AttachedFileVo.class));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �뙆�씪 議고쉶
+    @RequestMapping(value = "/attached/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.attachedFileService.findAttachedFile(resJsonData, AttachedFileCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �뙆�씪 �떎�슫濡쒕뱶
+    @RequestMapping(value = "/attached/download", method = {RequestMethod.GET, RequestMethod.POST})
+    public ModelAndView downloadFile(@RequestParam(value="id") Long id, Model model) {
+        return this.attachedFileService.checkUseWorkspaceTraffic(id, model);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/BaseController.java b/src/main/java/kr/wisestone/owl/web/controller/BaseController.java
new file mode 100644
index 0000000..c8f4894
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/BaseController.java
@@ -0,0 +1,66 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.common.MessageAccessor;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.PageUtil;
+import kr.wisestone.owl.util.WebAppUtil;
+import kr.wisestone.owl.vo.PageVo;
+import kr.wisestone.owl.vo.ResMessageVo;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by jeong on 2017-08-18.
+ */
+public class BaseController {
+
+    @Autowired
+    protected WebAppUtil webAppUtil;
+
+    @Autowired
+    protected PageUtil pageUtil;
+
+    @Autowired
+    protected MessageAccessor messageAccessor;
+
+    protected Map<String, Object> setSuccessMessage(Map<String, Object> resJsonData, ResMessageVo resMessage) {
+        if (resJsonData == null) {
+            resJsonData = new HashMap<>();
+        }
+
+        if (resMessage != null) {
+            resJsonData.put(Constants.RES_KEY_MESSAGE, resMessage);
+        } else {
+            resJsonData.put(Constants.RES_KEY_MESSAGE,
+                    this.messageAccessor.getResMessage(MsgConstants.SUCCESS_REQUEST, Constants.RES_KEY_MSG_SUCCESS));
+        }
+
+        return resJsonData;
+    }
+
+    /**
+     * �꽦怨� 硫붿떆吏�瑜� �깮�꽦�븳�떎.
+     *
+     * @param retVal
+     */
+    protected Map<String, Object> setSuccessMessage(Map<String, Object> retVal) {
+        return this.setSuccessMessage(retVal, null);
+    }
+
+    protected PageVo getPageVo(Map<String, Map<String, Object>> params) {
+        PageVo pageVo;
+        if (params.get(Constants.REQ_KEY_PAGE_VO) != null) {
+            pageVo = ConvertUtil.convertMapToClass(params.get(Constants.REQ_KEY_PAGE_VO), PageVo.class);
+        } else {
+            pageVo = this.pageUtil.getDefaultPageVo();
+        }
+        this.pageUtil.validatePageVo(pageVo);
+        return pageVo;
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/CustomFieldController.java b/src/main/java/kr/wisestone/owl/web/controller/CustomFieldController.java
new file mode 100644
index 0000000..971d394
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/CustomFieldController.java
@@ -0,0 +1,98 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.CustomFieldService;
+import kr.wisestone.owl.web.condition.CustomFieldCondition;
+import kr.wisestone.owl.web.form.CustomFieldForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-29.
+ */
+@Controller
+public class CustomFieldController extends BaseController {
+
+    @Autowired
+    private CustomFieldService customFieldService;
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �깮�꽦
+    @RequestMapping(value = "/customField/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.customFieldService.addCustomField(CustomFieldForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 議고쉶
+    @RequestMapping(value = "/customField/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.customFieldService.findCustomField(resJsonData, CustomFieldCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �긽�꽭 議고쉶
+    @RequestMapping(value = "/customField/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.customFieldService.detailCustomField(resJsonData, CustomFieldCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �닔�젙
+    @RequestMapping(value = "/customField/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.customFieldService.modifyCustomField(CustomFieldForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �궘�젣
+    @SuppressWarnings("unchecked")
+    @RequestMapping(value = "/customField/remove", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> removes(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.customFieldService.removeCustomFields(CustomFieldForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �뿊�� �떎�슫濡쒕뱶
+    @RequestMapping(value = "/customField/downloadExcel", method = RequestMethod.POST)
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        return this.customFieldService.downloadExcel(request, model);
+    }
+    
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/EventController.java b/src/main/java/kr/wisestone/owl/web/controller/EventController.java
new file mode 100644
index 0000000..da1e62c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/EventController.java
@@ -0,0 +1,88 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.EventService;
+import kr.wisestone.owl.web.condition.EventCondition;
+import kr.wisestone.owl.web.form.EventForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-24
+ */
+@Controller
+public class EventController extends BaseController {
+
+    @Autowired
+    private EventService eventService;
+
+    //  event �깮�꽦
+    @RequestMapping(value = "/event/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.eventService.addEvent(EventForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  event 議고쉶
+    @RequestMapping(value = "/event/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.eventService.findEvent(resJsonData, EventCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  event �긽�꽭 議고쉶
+    @RequestMapping(value = "/event/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.eventService.detailEvent(resJsonData, EventCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  event �닔�젙
+    @RequestMapping(value = "/event/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.eventService.modifyEvent(EventForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  event �븷�꽦
+    @RequestMapping(value = "/event/activation", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> activation(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.eventService.activeEvent(EventForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
+
diff --git a/src/main/java/kr/wisestone/owl/web/controller/FaqController.java b/src/main/java/kr/wisestone/owl/web/controller/FaqController.java
new file mode 100644
index 0000000..a8de623
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/FaqController.java
@@ -0,0 +1,89 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.FaqService;
+import kr.wisestone.owl.web.condition.FaqCondition;
+import kr.wisestone.owl.web.form.EventForm;
+import kr.wisestone.owl.web.form.FaqForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * zenith at 20200730
+ */
+@Controller
+public class FaqController extends BaseController {
+
+    @Autowired
+    private FaqService faqService;
+
+    //  faq �깮�꽦
+    @RequestMapping(value = "/faq/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.faqService.addFaq(FaqForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  faq 議고쉶
+    @RequestMapping(value = "/faq/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.faqService.findFaq(resJsonData, FaqCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  faq �긽�꽭 議고쉶
+    @RequestMapping(value = "/faq/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.faqService.detailFaq(resJsonData, FaqCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  faq �닔�젙
+    @RequestMapping(value = "/faq/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.faqService.modifyFaq(FaqForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  faq �븷�꽦
+    @RequestMapping(value = "/faq/activation", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> activation(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.faqService.activeFaq(FaqForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
+
diff --git a/src/main/java/kr/wisestone/owl/web/controller/GanttController.java b/src/main/java/kr/wisestone/owl/web/controller/GanttController.java
new file mode 100644
index 0000000..f3eaaa1
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/GanttController.java
@@ -0,0 +1,115 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.service.GanttService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.form.IssueForm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by maprex on 2021-03-25.
+ */
+@Controller
+public class GanttController extends BaseController {
+
+    private static final Logger log = LoggerFactory.getLogger(GanttController.class);
+
+    @Autowired
+    private GanttService ganttServiceService;
+
+    //  �씠�뒋 �깮�꽦
+    @RequestMapping(value = "/gantt/add", method = RequestMethod.POST)
+    public
+    @ResponseBody
+    Map<String, Object> add(MultipartHttpServletRequest request) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        //  �씠�뒋 �깮�꽦
+        Issue issue = this.ganttServiceService.addIssue(IssueForm.make(ConvertUtil.convertJsonToMap(request.getParameter(Constants.REQ_KEY_CONTENT))), request.getFiles("file"));
+        //  踰꾩쟾 �깮�꽦
+        this.ganttServiceService.addIssueVersion(issue.getId());
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  ���엫�씪�씤�슜 �씠�뒋 議고쉶
+    @RequestMapping(value = "/gantt/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.ganttServiceService.findIssue(resJsonData, IssueCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  ���엫�씪�씤�슜 �씠�뒋 議고쉶(�봽濡쒖젥�듃�슜)
+    @RequestMapping(value = "/gantt/findProject", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findProjectIssues(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.ganttServiceService.findIssue(resJsonData, ProjectCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �긽�꽭 議고쉶
+    @RequestMapping(value = "/gantt/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.ganttServiceService.detailIssue(resJsonData, IssueCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �닔�젙
+    @RequestMapping(value = "/gantt/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(MultipartHttpServletRequest request) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        //  �씠�뒋 �닔�젙
+        Issue issue = this.ganttServiceService.modifyIssue(IssueForm.make(ConvertUtil.convertJsonToMap(request.getParameter(Constants.REQ_KEY_CONTENT))), request.getFiles("file"));
+        //  踰꾩쟾 �깮�꽦
+        this.ganttServiceService.addIssueVersion(issue.getId());
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �궘�젣
+    @RequestMapping(value = "/gantt/remove", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> removes(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.ganttServiceService.removeIssues(IssueForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/GuideController.java b/src/main/java/kr/wisestone/owl/web/controller/GuideController.java
new file mode 100644
index 0000000..886abc6
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/GuideController.java
@@ -0,0 +1,89 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.GuideService;
+import kr.wisestone.owl.web.condition.GuideCondition;
+import kr.wisestone.owl.web.form.GuideForm;
+import kr.wisestone.owl.web.form.GuideForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-24
+ */
+@Controller
+public class GuideController extends BaseController {
+
+    @Autowired
+    private GuideService guideService;
+
+    //  guide �깮�꽦
+    @RequestMapping(value = "/guide/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.guideService.addGuide(GuideForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  guide 議고쉶
+    @RequestMapping(value = "/guide/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.guideService.findGuide(resJsonData, GuideCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  guide �긽�꽭 議고쉶
+    @RequestMapping(value = "/guide/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.guideService.detailGuide(resJsonData, GuideCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  guide �닔�젙
+    @RequestMapping(value = "/guide/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.guideService.modifyGuide(GuideForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  guide �븷�꽦
+    @RequestMapping(value = "/guide/activation", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> activation(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.guideService.activeGuide(GuideForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
+
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueCommentController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueCommentController.java
new file mode 100644
index 0000000..488b58e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueCommentController.java
@@ -0,0 +1,58 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.IssueCommentService;
+import kr.wisestone.owl.vo.IssueCommentVo;
+import kr.wisestone.owl.web.form.IssueCommentForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Controller
+public class IssueCommentController extends BaseController {
+
+    @Autowired
+    private IssueCommentService issueCommentService;
+
+    //  �뙎湲� �벑濡�
+    @RequestMapping(value = "issueComment/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) throws ClassNotFoundException {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueCommentService.addIssueComment(IssueCommentForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �뙎湲� �궘�젣
+    @RequestMapping(value= "issueComment/remove", produces = MediaType.APPLICATION_JSON_VALUE)
+    public @ResponseBody
+    Map<String, Object> removes(@RequestBody Map<String, Map<String, Object>> params) throws ClassNotFoundException {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueCommentService.removeIssueComments(IssueCommentForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �뙎湲� 紐⑸줉 議고쉶
+    @RequestMapping(value= "issueComment/find", produces = MediaType.APPLICATION_JSON_VALUE)
+    public @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) throws ClassNotFoundException {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        IssueCommentForm issueCommentForm = IssueCommentForm.make(params.get(Constants.REQ_KEY_CONTENT));
+        List<IssueCommentVo> issueCommentVos = this.issueCommentService.findIssueComment(issueCommentForm.getIssueId());
+        resJsonData.put(Constants.RES_KEY_CONTENTS, issueCommentVos);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueController.java
new file mode 100644
index 0000000..7639f38
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueController.java
@@ -0,0 +1,146 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.service.IssueService;
+import kr.wisestone.owl.service.impl.IssueServiceImpl;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.form.IssueForm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-03.
+ */
+@Controller
+public class IssueController extends BaseController {
+
+    private static final Logger log = LoggerFactory.getLogger(IssueController.class);
+
+    @Autowired
+    private IssueService issueService;
+
+    //  �씠�뒋 �깮�꽦
+    @RequestMapping(value = "/issue/add", method = RequestMethod.POST)
+    public
+    @ResponseBody
+    Map<String, Object> add(MultipartHttpServletRequest request) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        //  �씠�뒋 �깮�꽦
+        Issue issue = this.issueService.addIssue(IssueForm.make(ConvertUtil.convertJsonToMap(request.getParameter(Constants.REQ_KEY_CONTENT))), request.getFiles("file"));
+        //  踰꾩쟾 �깮�꽦
+        this.issueService.addIssueVersion(issue.getId());
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 議고쉶
+    @RequestMapping(value = "/issue/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.issueService.findIssue(resJsonData, IssueCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �긽�꽭 議고쉶
+    @RequestMapping(value = "/issue/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.issueService.detailIssue(resJsonData, IssueCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �닔�젙
+    @RequestMapping(value = "/issue/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(MultipartHttpServletRequest request) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        //  �씠�뒋 �닔�젙
+        Issue issue = this.issueService.modifyIssue(IssueForm.make(ConvertUtil.convertJsonToMap(request.getParameter(Constants.REQ_KEY_CONTENT))), request.getFiles("file"));
+        //  踰꾩쟾 �깮�꽦
+        this.issueService.addIssueVersion(issue.getId());
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �궘�젣
+    @RequestMapping(value = "/issue/remove", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> removes(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueService.removeIssues(IssueForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �떎以� �긽�깭 蹂�寃�
+    @RequestMapping(value = "/issue/modifyMultiIssueStatus", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modifyMultiIssueStatus(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.issueService.modifyMultiIssueStatus(IssueForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �뿊�� �떎�슫濡쒕뱶
+    @RequestMapping(value = "/issue/downloadExcel", method = RequestMethod.POST)
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        return this.issueService.downloadExcel(request, model);
+    }
+
+    //  �씠�뒋 Import �슜 �뿊�� �뀥�뵆由� �떎�슫濡쒕뱶
+    @RequestMapping(value = "/issue/downloadExcelTemplate", method = RequestMethod.POST)
+    public ModelAndView downloadExcelImport(HttpServletRequest request, Model model) {
+        return this.issueService.downloadExcelTemplate(request, model);
+    }
+
+    //  �씠�뒋 �뿊�� �벑濡�
+    @RequestMapping(value = "/issue/importExcel", method = RequestMethod.POST)
+    public @ResponseBody Map<String, Object> importExcel(MultipartHttpServletRequest request) throws Exception {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueService.importExcel(request.getFile("file"));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 硫붿씪 諛쒖넚
+    @RequestMapping(value = "/issue/sendEmail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> sendEmail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.issueService.sendIssueEmail(IssueForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+        return this.setSuccessMessage(resJsonData);
+    }
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueHistoryController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueHistoryController.java
new file mode 100644
index 0000000..c8b6a6f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueHistoryController.java
@@ -0,0 +1,37 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.IssueHistoryService;
+import kr.wisestone.owl.web.condition.IssueHistoryCondition;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-10-17.
+ */
+@Controller
+public class IssueHistoryController extends BaseController {
+
+    @Autowired
+    private IssueHistoryService issueHistoryService;
+
+    //  �씠�뒋 �씠�젰 議고쉶
+    @RequestMapping(value = "/issueHistory/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueHistoryService.findIssueHistory(resJsonData, IssueHistoryCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueRelationController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueRelationController.java
new file mode 100644
index 0000000..02b09f1
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueRelationController.java
@@ -0,0 +1,61 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.service.IssueRelationService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.web.condition.IssueRelationCondition;
+import kr.wisestone.owl.web.condition.IssueReservationCondition;
+import kr.wisestone.owl.web.form.IssueForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Controller
+public class IssueRelationController extends BaseController {
+
+    @Autowired
+    public IssueRelationService issueRelationService;
+
+    //  �뿰愿� �씠�뒋 異붽�
+    @RequestMapping(value = "/issueRelation/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.issueRelationService.addRelationIssue(resJsonData, IssueRelationCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �뿰愿� �씠�뒋 議고쉶
+    @RequestMapping(value = "/issueRelation/find", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.issueRelationService.findRelationIssue(resJsonData, IssueRelationCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �뿰愿� �씠�뒋 �궘�젣
+    @RequestMapping(value = "/issueRelation/remove", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> remove(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.issueRelationService.removeRelationIssue(resJsonData, IssueRelationCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueReservationController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueReservationController.java
new file mode 100644
index 0000000..03ccab0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueReservationController.java
@@ -0,0 +1,46 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.IssueReservationService;
+import kr.wisestone.owl.web.condition.IssueReservationCondition;
+import kr.wisestone.owl.web.form.IssueReservationForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-08
+ */
+@Controller
+public class IssueReservationController extends BaseController {
+
+    @Autowired
+    private IssueReservationService issueReservationService;
+
+    //  �씠�뒋 諛쒖깮 �삁�빟 �긽�꽭 議고쉶
+    @RequestMapping(value = "/issueReservation/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.issueReservationService.detailIssueReservation(resJsonData, IssueReservationCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 諛쒖깮 �삁�빟 �닔�젙
+    @RequestMapping(value = "/issueReservation/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.issueReservationService.modifyIssueReservation(resJsonData, IssueReservationForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueSearchController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueSearchController.java
new file mode 100644
index 0000000..7334f8b
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueSearchController.java
@@ -0,0 +1,47 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.IssueSearchService;
+import kr.wisestone.owl.web.condition.WorkflowCondition;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2019-02-01.
+ */
+@Controller
+public class IssueSearchController extends BaseController {
+
+    @Autowired
+    private IssueSearchService issueSearchService;
+
+    //  �씠�뒋 寃��깋 �깮�꽦
+    @RequestMapping(value = "/issueSearch/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueSearchService.addIssueSearch(params.get(Constants.REQ_KEY_CONTENT));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 寃��깋 �긽�꽭 議고쉶
+    @RequestMapping(value = "/issueSearch/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail() {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.issueSearchService.detailIssueSearch(resJsonData);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueStatusController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueStatusController.java
new file mode 100644
index 0000000..ce21874
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueStatusController.java
@@ -0,0 +1,133 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.IssueStatusService;
+import kr.wisestone.owl.web.condition.IssueStatusCondition;
+import kr.wisestone.owl.web.form.IssueStatusForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-08.
+ */
+@Controller
+public class IssueStatusController extends BaseController {
+
+    @Autowired
+    private IssueStatusService issueStatusService;
+
+    //  �씠�뒋 �긽�깭 �깮�꽦
+    @RequestMapping(value = "/issueStatus/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueStatusService.addIssueStatus(IssueStatusForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �긽�깭 議고쉶
+    @RequestMapping(value = "/issueStatus/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.issueStatusService.findIssueStatus(resJsonData, IssueStatusCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �긽�깭 �쟾泥� 議고쉶
+    @RequestMapping(value = "/issueStatus/findAll", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findAll(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueStatusService.findIssueStatus(resJsonData, IssueStatusCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �긽�깭 �긽�꽭 議고쉶
+    @RequestMapping(value = "/issueStatus/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.issueStatusService.detailIssueStatus(resJsonData, IssueStatusCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �긽�깭 �닔�젙
+    @RequestMapping(value = "/issueStatus/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueStatusService.modifyIssueStatus(IssueStatusForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �긽�깭 �궘�젣
+    @SuppressWarnings("unchecked")
+    @RequestMapping(value = "/issueStatus/remove", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> removes(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueStatusService.removeIssueStatus(IssueStatusForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 蹂�寃� 媛��뒫 �긽�깭 議고쉶
+    @RequestMapping(value = "/issueStatus/findNextIssueStatus", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findNextIssueStatus(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueStatusService.findNextIssueStatus(resJsonData, IssueStatusCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �뿬�윭 嫄댁쓽 �씠�뒋 蹂�寃� 媛��뒫 �긽�깭 議고쉶
+    @RequestMapping(value = "/issueStatus/findNextMultiIssueStatus", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findNextMultiIssueStatus(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueStatusService.findNextMultiIssueStatus(resJsonData, IssueStatusCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �긽�깭 �뿊�� �떎�슫濡쒕뱶
+    @RequestMapping(value = "/issueStatus/downloadExcel", method = RequestMethod.POST)
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        return this.issueStatusService.downloadExcel(request, model);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueTableConfigController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueTableConfigController.java
new file mode 100644
index 0000000..7fe99d9
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueTableConfigController.java
@@ -0,0 +1,46 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.IssueTableConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2019-02-07.
+ */
+@Controller
+public class IssueTableConfigController extends BaseController {
+
+    @Autowired
+    private IssueTableConfigService issueTableConfigService;
+
+    //  �씠�뒋 �뀒�씠釉� �꽕�젙 ���옣
+    @RequestMapping(value = "/issueTableConfig/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueTableConfigService.addIssueTableConfig(params.get(Constants.REQ_KEY_CONTENT));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 �뀒�씠釉� �꽕�젙 �긽�꽭 議고쉶
+    @RequestMapping(value = "/issueTableConfig/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail() {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.issueTableConfigService.detailIssueTableConfig(resJsonData);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueTypeController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueTypeController.java
new file mode 100644
index 0000000..3dafadf
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueTypeController.java
@@ -0,0 +1,97 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.IssueTypeService;
+import kr.wisestone.owl.web.condition.IssueTypeCondition;
+import kr.wisestone.owl.web.form.IssueTypeForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-29.
+ */
+@Controller
+public class IssueTypeController extends BaseController {
+    
+    @Autowired
+    private IssueTypeService issueTypeService;
+
+    //  �씠�뒋 ���엯 �깮�꽦
+    @RequestMapping(value = "/issueType/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueTypeService.addIssueType(IssueTypeForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 ���엯 議고쉶
+    @RequestMapping(value = "/issueType/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.issueTypeService.findIssueType(resJsonData, IssueTypeCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 ���엯 �긽�꽭 議고쉶
+    @RequestMapping(value = "/issueType/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.issueTypeService.detailIssueType(resJsonData, IssueTypeCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 ���엯 �닔�젙
+    @RequestMapping(value = "/issueType/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueTypeService.modifyIssueType(IssueTypeForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 ���엯 �궘�젣
+    @SuppressWarnings("unchecked")
+    @RequestMapping(value = "/issueType/remove", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> removes(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueTypeService.removeIssueTypes(IssueTypeForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 ���엯 �뿊�� �떎�슫濡쒕뱶
+    @RequestMapping(value = "/issueType/downloadExcel", method = RequestMethod.POST)
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        return this.issueTypeService.downloadExcel(request, model);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueTypeCustomFieldController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueTypeCustomFieldController.java
new file mode 100644
index 0000000..dc19893
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueTypeCustomFieldController.java
@@ -0,0 +1,51 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.IssueTypeCustomFieldService;
+import kr.wisestone.owl.web.condition.IssueTypeCustomFieldCondition;
+import kr.wisestone.owl.web.form.IssueTypeCustomFieldForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-30.
+ */
+@Controller
+public class IssueTypeCustomFieldController extends BaseController {
+
+    @Autowired
+    private IssueTypeCustomFieldService issueTypeCustomFieldService;
+
+    //  �씠�뒋 ���엯 �궗�슜�옄 �븘�뱶 �뿰寃� �닔�젙
+    @RequestMapping(value = "/issueTypeCustomField/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueTypeCustomFieldService.modifyIssueTypeCustomFields(IssueTypeCustomFieldForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 ���엯 �궗�슜�옄 �젙�쓽 �븘�뱶 �뿰寃� 議고쉶
+    @RequestMapping(value = "/issueTypeCustomField/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueTypeCustomFieldService.findIssueTypeCustomField(resJsonData, IssueTypeCustomFieldCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueUserController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueUserController.java
new file mode 100644
index 0000000..34ea0fc
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueUserController.java
@@ -0,0 +1,40 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.IssueService;
+import kr.wisestone.owl.service.IssueUserService;
+import kr.wisestone.owl.web.form.IssueForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Controller
+public class IssueUserController extends BaseController {
+
+    @Autowired
+    private IssueUserService issueUserService;
+
+    @Autowired
+    private IssueService issueService;
+
+    @RequestMapping(value = "issueUser/modify", method = RequestMethod.POST)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        IssueForm issueForm = IssueForm.make(params.get(Constants.REQ_KEY_CONTENT));
+
+        if (issueForm != null) {
+            this.issueService.modifyIssueUser(issueForm);
+        }
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueVersionController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueVersionController.java
new file mode 100644
index 0000000..a2f9b7a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueVersionController.java
@@ -0,0 +1,37 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.IssueVersionService;
+import kr.wisestone.owl.web.condition.IssueVersionCondition;
+import kr.wisestone.owl.web.form.IssueTypeForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Controller
+public class IssueVersionController extends BaseController {
+
+    @Autowired
+    private IssueVersionService issueVersionService;
+
+    //  �씠�뒋 踰꾩쟾 議고쉶
+    @RequestMapping(value = "/issueVersion/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.issueVersionService.find(resJsonData, IssueVersionCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/LanguageController.java b/src/main/java/kr/wisestone/owl/web/controller/LanguageController.java
new file mode 100644
index 0000000..b8a63f2
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/LanguageController.java
@@ -0,0 +1,35 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-07-11.
+ */
+
+@Controller
+public class LanguageController extends BaseController {
+
+    @Autowired
+    private UserService userService;
+
+    //  �뼵�뼱 蹂�寃�
+    @RequestMapping(value="/language/change", method = RequestMethod.GET)
+    public @ResponseBody Map<String, Object> change(@RequestParam("language") String language) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.userService.updateLanguage(language);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/ManageUserController.java b/src/main/java/kr/wisestone/owl/web/controller/ManageUserController.java
new file mode 100644
index 0000000..b221136
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/ManageUserController.java
@@ -0,0 +1,56 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.ManageUserService;
+import kr.wisestone.owl.service.UserWorkspaceService;
+import kr.wisestone.owl.service.WorkspaceService;
+import kr.wisestone.owl.web.condition.UserWorkspaceCondition;
+import kr.wisestone.owl.web.form.ManageUserForm;
+import kr.wisestone.owl.web.form.UserWorkspaceForm;
+import kr.wisestone.owl.web.form.WorkspaceForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * edit by zenith at 20200804
+ */
+@Controller
+public class ManageUserController extends BaseController {
+
+    @Autowired
+    private ManageUserService manageUserService;
+
+    //  �뾽臾닿났媛꾩뿉 李몄뿬�븯�뒗 �궗�슜�옄 議고쉶
+    @RequestMapping(value = "/manageUser/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<String, Object>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.manageUserService.findUserPermission(resJsonData, UserWorkspaceCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �뾽臾닿났媛꾩뿉 李몄뿬�븯�뒗 李몄뿬�옄 �긽�깭 �닔�젙
+    @RequestMapping(value = "/manageUser/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.manageUserService.modifyUserPermission(ManageUserForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/NoticeController.java b/src/main/java/kr/wisestone/owl/web/controller/NoticeController.java
new file mode 100644
index 0000000..d53ba90
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/NoticeController.java
@@ -0,0 +1,89 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.NoticeService;
+import kr.wisestone.owl.web.condition.NoticeCondition;
+import kr.wisestone.owl.web.form.NoticeForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-24
+ */
+@Controller
+public class NoticeController extends BaseController {
+
+    @Autowired
+    private NoticeService noticeService;
+
+    //  怨듭��궗�빆 �깮�꽦
+    @RequestMapping(value = "/notice/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.noticeService.addNotice(NoticeForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  怨듭��궗�빆 議고쉶
+    @RequestMapping(value = "/notice/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.noticeService.findNotice(resJsonData, NoticeCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  怨듭��궗�빆 �긽�꽭 議고쉶
+    @RequestMapping(value = "/notice/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.noticeService.detailNotice(resJsonData, NoticeCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  怨듭��궗�빆 �닔�젙
+    @RequestMapping(value = "/notice/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.noticeService.modifyNotice(NoticeForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  硫붿꽭吏� 蹂대궡湲�
+    @RequestMapping(value = "/notice/send", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> send(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.noticeService.sendNotice(NoticeForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+}
+
diff --git a/src/main/java/kr/wisestone/owl/web/controller/PaymentController.java b/src/main/java/kr/wisestone/owl/web/controller/PaymentController.java
new file mode 100644
index 0000000..c4696f1
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/PaymentController.java
@@ -0,0 +1,79 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.PaymentService;
+import kr.wisestone.owl.web.form.PaymentForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-02-13.
+ */
+@Controller
+public class PaymentController extends BaseController {
+
+    @Autowired
+    private PaymentService paymentService;
+
+    //  �젙湲� 寃곗젣
+    @RequestMapping(value="/payment/paymentOneTime", produces = MediaType.APPLICATION_JSON_VALUE)
+    public @ResponseBody
+    Map<String, Object> paymentOneTime(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.paymentService.paymentOneTime(PaymentForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  異붽� 寃곗젣
+    @RequestMapping(value="/payment/immediateAddUser", produces = MediaType.APPLICATION_JSON_VALUE)
+    public @ResponseBody
+    Map<String, Object> immediateAddUser(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.paymentService.immediateAddUser(PaymentForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �젙湲� 寃곗젣 痍⑥냼
+    @RequestMapping(value="/payment/cancelNextPayment", produces = MediaType.APPLICATION_JSON_VALUE)
+    public @ResponseBody
+    Map<String, Object> cancelNextPayment(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.paymentService.cancelNextPayment(PaymentForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  寃곗젣 �긽�꽭 �젙蹂� 議고쉶
+    @RequestMapping(value="/payment/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.paymentService.detailPayment(resJsonData, PaymentForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  寃곗젣 �젙蹂� �닔�젙
+    @RequestMapping(value="/payment/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.paymentService.modifyPayment(PaymentForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/PermissionController.java b/src/main/java/kr/wisestone/owl/web/controller/PermissionController.java
new file mode 100644
index 0000000..fb939dc
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/PermissionController.java
@@ -0,0 +1,33 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.PermissionService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by jeong on 2017-12-29.
+ */
+@Controller
+public class PermissionController extends BaseController {
+
+    @Autowired
+    private PermissionService permissionService;
+
+    //  �궗�슜�옄 沅뚰븳 議고쉶
+    @RequestMapping(value = "/permission/findByUserId", produces = MediaType.APPLICATION_JSON_VALUE)
+    public @ResponseBody Map<String, Object> findByUserId() {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, this.permissionService.findByUserId());
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/PriorityController.java b/src/main/java/kr/wisestone/owl/web/controller/PriorityController.java
new file mode 100644
index 0000000..d5ef612
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/PriorityController.java
@@ -0,0 +1,34 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.service.PriorityService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-06-01.
+ */
+@Controller
+public class PriorityController extends BaseController {
+    @Autowired
+    private PriorityService priorityService;
+
+    //  �슦�꽑�닚�쐞 議고쉶
+    @RequestMapping(value = "/priority/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.priorityService.findPriority(resJsonData);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/ProjectController.java b/src/main/java/kr/wisestone/owl/web/controller/ProjectController.java
new file mode 100644
index 0000000..a3199cf
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/ProjectController.java
@@ -0,0 +1,161 @@
+package kr.wisestone.owl.web.controller;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.Project;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.domain.enumType.ProjectType;
+import kr.wisestone.owl.service.ProjectService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.vo.ProjectVo;
+import kr.wisestone.owl.vo.WorkspaceVo;
+import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.form.ProjectForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+import sun.rmi.runtime.Log;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by jeong on 2017-12-30.
+ */
+@Controller
+public class ProjectController extends BaseController {
+
+    @Autowired
+    private ProjectService projectService;
+
+    //  �봽濡쒖젥�듃 �깮�꽦
+    @RequestMapping(value = "/project/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        Project project = this.projectService.addProject(ProjectForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+        resJsonData.put(Constants.RES_KEY_CONTENTS, ConvertUtil.copyProperties(project, ProjectVo.class));
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �봽濡쒖젥�듃 議고쉶
+    @RequestMapping(value = "/project/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.projectService.findProject(resJsonData, ProjectCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  李몄뿬 �봽濡쒖젥�듃 議고쉶
+    @RequestMapping(value = "/project/findWork", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findWork(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        List<Map<String, Object>> projects = this.projectService.findByWorkspaceIdAndIncludeProjectAll(ProjectCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        List<ProjectVo> projectVos = Lists.newArrayList();
+
+        for (Map<String, Object> map : projects) {
+            ProjectVo projectVo = new ProjectVo();
+            projectVo.setName(MapUtil.getString(map, "name"));
+            projectVo.setId(MapUtil.getLong(map, "id"));
+
+            projectVos.add(projectVo);
+        }
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, projectVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+
+    //  李몄뿬 �봽濡쒖젥�듃 議고쉶
+    @RequestMapping(value = "/project/findListWork", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findListWork(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        List<ProjectVo> projectVos = this.projectService.findByIncludeProject(Lists.newArrayList("01", "02", "03"), ProjectType.BTS_PROJECT.toString());
+
+        resJsonData.put(Constants.RES_KEY_CONTENTS, projectVos);
+        resJsonData.put(Constants.REQ_KEY_PAGE_VO, pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �봽濡쒖젥�듃 �긽�꽭 議고쉶
+    @RequestMapping(value = "/project/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.projectService.detailProject(resJsonData, ProjectCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �봽濡쒖젥�듃 �닔�젙
+    @RequestMapping(value = "/project/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.projectService.modifyProject(ProjectForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �봽濡쒖젥�듃 �궘�젣
+    @SuppressWarnings("unchecked")
+    @RequestMapping(value = "/project/remove", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> removes(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.projectService.removeProjects(ProjectForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �봽濡쒖젥�듃 �뿊�� �떎�슫濡쒕뱶
+    @RequestMapping(value = "/project/downloadExcel", method = RequestMethod.POST)
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        return this.projectService.downloadExcel(request, model);
+    }
+
+    // 留덉�留됱쑝濡� �궗�슜�븳 �봽濡쒖젥�듃 媛��졇�삤湲�
+    @RequestMapping(value = "/project/findLastUseProject", produces = MediaType.APPLICATION_JSON_VALUE)
+    public @ResponseBody Map<String, Object> findLastUseProject(
+            @RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<String, Object>();
+        this.projectService.findLastUseProject(resJsonData);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/QnaController.java b/src/main/java/kr/wisestone/owl/web/controller/QnaController.java
new file mode 100644
index 0000000..9f63733
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/QnaController.java
@@ -0,0 +1,76 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.QnaService;
+import kr.wisestone.owl.web.condition.QnaCondition;
+import kr.wisestone.owl.web.form.QnaForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * zenith at 20200730
+ */
+@Controller
+public class QnaController extends BaseController {
+
+    @Autowired
+    private QnaService qnaService;
+
+    //  qna �깮�꽦
+    @RequestMapping(value = "/qna/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.qnaService.addQna(QnaForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  qna 議고쉶
+    @RequestMapping(value = "/qna/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.qnaService.findQna(resJsonData, QnaCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  qna �긽�꽭 議고쉶
+    @RequestMapping(value = "/qna/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.qnaService.detailQna(resJsonData, QnaCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  qna �닔�젙
+    @RequestMapping(value = "/qna/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.qnaService.modifyQna(QnaForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
+
diff --git a/src/main/java/kr/wisestone/owl/web/controller/ReservationDisableUserController.java b/src/main/java/kr/wisestone/owl/web/controller/ReservationDisableUserController.java
new file mode 100644
index 0000000..329158b
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/ReservationDisableUserController.java
@@ -0,0 +1,35 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.ReservationDisableUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2019-02-11.
+ */
+@Controller
+public class ReservationDisableUserController extends BaseController {
+
+    @Autowired
+    private ReservationDisableUserService reservationDisableUserService;
+
+    //  李몄뿬 ��湲� �궗�슜�옄 議고쉶
+    @RequestMapping(value = "/reservationDisableUser/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.reservationDisableUserService.findReservationDisableUser(params.get(Constants.REQ_KEY_CONTENT), resJsonData);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/SeverityController.java b/src/main/java/kr/wisestone/owl/web/controller/SeverityController.java
new file mode 100644
index 0000000..b4d47df
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/SeverityController.java
@@ -0,0 +1,34 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.service.SeverityService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-06-01.
+ */
+@Controller
+public class SeverityController extends BaseController {
+    @Autowired
+    private SeverityService severityService;
+
+    //  以묒슂�룄 議고쉶
+    @RequestMapping(value = "/severity/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.severityService.findSeverity(resJsonData);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/SystemEmailController.java b/src/main/java/kr/wisestone/owl/web/controller/SystemEmailController.java
new file mode 100644
index 0000000..1bc4e01
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/SystemEmailController.java
@@ -0,0 +1,35 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.SystemEmailService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-21
+ */
+@Controller
+public class SystemEmailController extends BaseController{
+
+    @Autowired
+    private SystemEmailService systemEmailService;
+
+    //  臾몄쓽�븯湲�
+    @RequestMapping(value = "/systemEmail/information", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> information(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.systemEmailService.information(params.get(Constants.REQ_KEY_CONTENT));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/UserController.java b/src/main/java/kr/wisestone/owl/web/controller/UserController.java
new file mode 100644
index 0000000..3084944
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/UserController.java
@@ -0,0 +1,224 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.enumType.SocialType;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.web.condition.UserCondition;
+import kr.wisestone.owl.web.form.UserForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by jeong on 2017-08-02.
+ */
+@Controller
+public class UserController extends BaseController {
+
+    @Autowired
+    private UserService userService;
+
+    //  濡쒓렇�씤 �꽭�뀡 議고쉶
+    @RequestMapping(value = "/user/getUserSession", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> getUserSession(HttpServletRequest httpServletRequest) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.userService.getUserSession(resJsonData, httpServletRequest);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �꽭�뀡�쓣 �뾽�뜲�씠�듃�븳�떎.
+    @RequestMapping(value = "/user/updateUserSession", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> updateUserSession() {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.userService.updateUserSession();
+        resJsonData.put(Constants.RES_KEY_CONTENTS, this.webAppUtil.getLoginUser());
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄 �깮�꽦
+    @RequestMapping(value = "/user/add", method = RequestMethod.POST)
+    public
+    @ResponseBody
+    Map<String, Object> addUser(MultipartHttpServletRequest request) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Map<String, Object> content = ConvertUtil.convertJsonToMap(request.getParameter(Constants.REQ_KEY_CONTENT));
+        this.userService.addUser(UserForm.make(content), request.getFile("file"));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  援ш� �븘�씠�뵒濡� �쉶�썝 媛��엯
+    @RequestMapping(value = "/googleOAuth2CallBack", method = RequestMethod.GET)
+    public ModelAndView googleOAuth2CallBack(@RequestParam("code") String code, @RequestParam("state") String state, HttpServletRequest request) {
+        return this.userService.getOAuthToken(code, state, SocialType.GOOGLE, request);
+    }
+
+    //  �꽕�씠踰� �븘�씠�뵒濡� �쉶�썝 媛��엯
+    @RequestMapping(value = "/naverOAuth2CallBack", method = RequestMethod.GET)
+    public ModelAndView naverOAuth2callBack(@RequestParam("code") String code, @RequestParam("state") String state, HttpServletRequest request) {
+        return this.userService.getOAuthToken(code, state, SocialType.NAVER, request);
+    }
+
+    //  移댁뭅�삤 �븘�씠�뵒濡� �쉶�썝 媛��엯
+    @RequestMapping(value = "/kakaoOAuth2CallBack", method = RequestMethod.GET)
+    public ModelAndView kakaoOAuth2CallBack(@RequestParam("code") String code, @RequestParam("state") String state, HttpServletRequest request) {
+        return this.userService.getOAuthToken(code, state, SocialType.KAKAO, request);
+    }
+
+    //  �럹�씠�뒪遺� �븘�씠�뵒濡� �쉶�썝 媛��엯
+    @RequestMapping(value = "/facebookOAuth2CallBack", method = RequestMethod.GET)
+    public ModelAndView facebookOAuth2CallBack(@RequestParam("code") String code, @RequestParam("state") String state, HttpServletRequest request) {
+        return this.userService.getOAuthToken(code, state, SocialType.FACEBOOK, request);
+    }
+
+    //  �궗�슜�옄 議고쉶
+    @RequestMapping(value = "/user/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.userService.findUser(resJsonData, UserCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �쟾泥� �뾽臾닿났媛꾩쓽 �궗�슜�옄 議고쉶
+    @RequestMapping(value = "/user/findByAllWorkspace", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findByAllWorkspace(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.userService.findByAllWorkspace(resJsonData, UserCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄 �닔�젙
+    @RequestMapping(value = "/user/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(MultipartHttpServletRequest request) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.userService.modifyUser(UserForm.make(ConvertUtil.convertJsonToMap(request.getParameter(Constants.REQ_KEY_CONTENT))), request.getFile("file"));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄 鍮꾨�踰덊샇 �닔�젙
+    @RequestMapping(value = "/user/modifyPassword", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modifyPassword(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.userService.modifyPassword(UserForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄 �긽�꽭 議고쉶
+    @RequestMapping(value = "/user/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.userService.detailUser(resJsonData, UserCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  鍮꾨�踰덊샇 李얘린
+    @RequestMapping(value = "/user/returnEmailPassword", method = RequestMethod.POST)
+    public
+    @ResponseBody
+    Map<String, Object> returnEmailPassword(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.userService.returnEmailPassword(UserForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄�쓽 留덉�留� �젒洹� �썙�겕�뒪�럹�씠�뒪 �젙蹂� �뾽�뜲�씠�듃
+    @RequestMapping(value = "/user/updateLastWorkspace", method = RequestMethod.POST)
+    public
+    @ResponseBody
+    Map<String, Object> updateLastWorkspace(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.userService.updateLastWorkspace(resJsonData, UserForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄�쓽 留덉�留� �젒洹� �봽濡쒖젥�듃 �젙蹂� �뾽�뜲�씠�듃
+    @RequestMapping(value = "/user/updateLastProject", method = RequestMethod.POST)
+    public
+    @ResponseBody
+    Map<String, Object> updateLastProject(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.userService.updateLastProject(resJsonData, UserForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �봽濡쒖젥�듃�뿉 李몄뿬�븯�뒗 �씪諛� �궗�슜�옄 紐⑸줉 議고쉶
+    @RequestMapping(value = "/user/findProjectMember", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findProjectMember(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.userService.findProjectMember(resJsonData, UserCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄 �깉�눜
+    @RequestMapping(value = "/user/withDraw", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> withDraw() {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.userService.withDrawUser();
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궗�슜�옄 �뿊�� �떎�슫濡쒕뱶
+    @RequestMapping(value = "/user/downloadExcel", method = RequestMethod.POST)
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        return this.userService.downloadExcel(model);
+    }
+
+    /*//  �씠踰ㅽ듃 �떦泥⑥옄 �뿊�� �떎�슫濡쒕뱶
+    @RequestMapping(value = "/user/downloadExcelEvent", method = RequestMethod.POST)
+    public ModelAndView downloadExcelEvent(HttpServletRequest request, Model model) {
+        return this.userService.downloadExcelEvent(model);
+    }*/
+}
+
+
+
+
+
diff --git a/src/main/java/kr/wisestone/owl/web/controller/UserHistoryController.java b/src/main/java/kr/wisestone/owl/web/controller/UserHistoryController.java
new file mode 100644
index 0000000..40d3d36
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/UserHistoryController.java
@@ -0,0 +1,46 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.domain.UserHistory;
+import kr.wisestone.owl.service.UserHistoryService;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.web.condition.UserHistoryCondition;
+import org.springframework.beans.factory.HierarchicalBeanFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by maprex on 2021-05-11
+ */
+@Controller
+public class UserHistoryController extends BaseController {
+
+    @Autowired
+    private UserHistoryService userHistoryService;
+
+    @Autowired
+    private UserService userService;
+
+    //  濡쒓렇�씤 �씠�젰 �엯�젰
+    @RequestMapping(value = "/userHistory/addLogin", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> addUserHistory() {
+        Map<String, Object> resJsonData = new HashMap<>();
+        // 媛쒕컻�뻽�쑝�굹 遺덊븘�슂
+//        UserHistoryCondition condition = new UserHistoryCondition();
+//        condition.setHistoryType(UserHistory.HISTORY_LOGIN);
+//        this.userHistoryService.addUserHistory(resJsonData, condition);
+
+        this.userService.updateLastLogin();
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/UserInviteController.java b/src/main/java/kr/wisestone/owl/web/controller/UserInviteController.java
new file mode 100644
index 0000000..9ed05dd
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/UserInviteController.java
@@ -0,0 +1,36 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.UserInviteService;
+import kr.wisestone.owl.web.form.UserInviteForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-08-31.
+ */
+@Controller
+public class UserInviteController extends BaseController {
+    @Autowired
+    private UserInviteService userInviteService;
+
+    //  �궗�슜�옄 珥덈�
+    @RequestMapping(value = "/userInvite/invite", method = RequestMethod.POST)
+    public
+    @ResponseBody
+    Map<String, Object> invite(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.userInviteService.inviteWorkspace(UserInviteForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/UserWorkspaceController.java b/src/main/java/kr/wisestone/owl/web/controller/UserWorkspaceController.java
new file mode 100644
index 0000000..fc19000
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/UserWorkspaceController.java
@@ -0,0 +1,53 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.UserWorkspaceService;
+import kr.wisestone.owl.web.condition.UserWorkspaceCondition;
+import kr.wisestone.owl.web.form.ProjectForm;
+import kr.wisestone.owl.web.form.UserWorkspaceForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-10-02.
+ */
+@Controller
+public class UserWorkspaceController extends BaseController {
+
+    @Autowired
+    private UserWorkspaceService userWorkspaceService;
+
+    //  �뾽臾닿났媛꾩뿉 李몄뿬�븯�뒗 �궗�슜�옄 議고쉶
+    @RequestMapping(value = "/userWorkspace/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<String, Object>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.userWorkspaceService.findUserWorkspace(resJsonData, UserWorkspaceCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �뾽臾닿났媛꾩뿉 李몄뿬�븯�뒗 李몄뿬�옄 �긽�깭 �닔�젙
+    @RequestMapping(value = "/userWorkspace/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.userWorkspaceService.modifyUserWorkspace(UserWorkspaceForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/WebSocketSessionController.java b/src/main/java/kr/wisestone/owl/web/controller/WebSocketSessionController.java
new file mode 100644
index 0000000..0804781
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/WebSocketSessionController.java
@@ -0,0 +1,68 @@
+package kr.wisestone.owl.web.controller;
+
+import com.google.gson.Gson;
+import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.util.SecurityUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.messaging.handler.annotation.Header;
+import org.springframework.messaging.handler.annotation.MessageMapping;
+import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
+import org.springframework.stereotype.Controller;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Controller
+public class WebSocketSessionController {
+
+    private static final Logger log = LoggerFactory.getLogger(WebSocketSessionController.class);
+
+    @Autowired
+    private StringRedisTemplate stringRedisTemplate;
+
+
+    @MessageMapping("/session/alive")
+    public void active(@Header("simpSessionAttributes") Map<String, Object> sessionAttributes, StompHeaderAccessor stompHeaderAccessor) {
+        User user = SecurityUtils.getCurrentUserFromWebSocket(sessionAttributes);
+
+        if (user != null) {
+            //  �겢�윭�뒪�꽣留� �솚寃쎌뿉�꽌 �젒�냽以묒씤 �궗�슜�옄 �젙蹂대�� ���옣�븳�떎.
+            this.updateActiveUser(user);
+        }
+    }
+
+    //  �겢�윭�뒪�꽣留� �솚寃쎌뿉�꽌 �젒�냽以묒씤 �궗�슜�옄 �젙蹂대�� ���옣�븳�떎.
+    private void updateActiveUser(User user) {
+        //  �쟾泥� �궗�슜�옄 �젙蹂�
+        Map<String, Object> totalActiveUserMap = new HashMap<>();
+        Gson gson = new Gson();
+        //  �궡�븘�엳�떎怨� �슂泥��븳 �궗�슜�옄 �젙蹂�
+        Map<String, Object> currentActiveUserMap = new HashMap<>();
+        currentActiveUserMap.put("id", user.getId());
+        currentActiveUserMap.put("account", CommonUtil.decryptAES128(user.getAccount()));
+        currentActiveUserMap.put("name", user.getName());
+        currentActiveUserMap.put("sessionActiveTime", String.valueOf(System.currentTimeMillis()));
+        currentActiveUserMap.put("lastWorkspaceId", user.getLastWorkspaceId());
+
+        //  �뀒�뒪�듃�슜 �궎 �궘�젣
+        //this.stringRedisTemplate.delete(Lists.newArrayList("activeUsers"));
+
+        String activeUsers = this.stringRedisTemplate.opsForValue().get("activeUsers");
+
+        //  redis �뿉�꽌 濡쒓렇�씤 �꽭�뀡 �젙蹂대�� 媛��졇���꽌 �뾽�뜲�씠�듃 �븳�떎.
+        if (!StringUtils.isEmpty(activeUsers)) {
+            totalActiveUserMap = (Map<String, Object>)gson.fromJson(activeUsers, Object.class);
+        }
+
+        totalActiveUserMap.put(MapUtil.getString(currentActiveUserMap, "id"), currentActiveUserMap);
+        String content = gson.toJson(totalActiveUserMap);
+
+        this.stringRedisTemplate.opsForValue().set("activeUsers", content);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/WidgetController.java b/src/main/java/kr/wisestone/owl/web/controller/WidgetController.java
new file mode 100644
index 0000000..ee07b19
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/WidgetController.java
@@ -0,0 +1,157 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.UserService;
+import kr.wisestone.owl.service.WidgetService;
+import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.condition.WidgetCondition;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-10-30.
+ */
+@Controller
+public class WidgetController extends BaseController {
+
+    @Autowired
+    private WidgetService widgetService;
+
+    //  �쐞�젽 �쟾泥� 議고쉶
+    @RequestMapping(value = "/widget/findAllWidget", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findAllWidget() {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.widgetService.findAllWidget(resJsonData);
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �굹�뿉寃� �븷�떦�맂 �씠�뒋 紐⑸줉 議고쉶
+    @RequestMapping(value = "/widget/findMyAssigneeIssue", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findMyAssigneeIssue(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.widgetService.findMyAssigneeIssue(resJsonData, this.widgetService.makeWidgetCondition(), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  吏��뿰以묒씤 �씠�뒋 紐⑸줉 議고쉶
+    @RequestMapping(value = "/widget/findDelayIssue", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findDelayIssue(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        WidgetCondition condition = WidgetCondition.make(params.get(Constants.REQ_KEY_CONTENT));
+        this.widgetService.findDelayIssue(resJsonData, this.widgetService.makeWidgetCondition(), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �궡媛� �벑濡앺븳 �씠�뒋 紐⑸줉 議고쉶
+    @RequestMapping(value = "/widget/findRegisterIssue", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findRegisterIssue(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        WidgetCondition condition = WidgetCondition.make(params.get(Constants.REQ_KEY_CONTENT));
+        this.widgetService.findRegisterIssue(resJsonData, this.widgetService.makeWidgetCondition(), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  硫ㅻ쾭蹂� 吏꾪뻾瑜� 議고쉶
+    @RequestMapping(value = "/widget/findMemberProgress", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findMemberProgress(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.widgetService.findMemberProgress(resJsonData, WidgetCondition.make(params.get(Constants.REQ_KEY_CONTENT)), true);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �쐞�뿕 愿�由� 議고쉶
+    @RequestMapping(value = "/widget/findRiskIssue", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findRiskIssue(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        WidgetCondition condition = WidgetCondition.make(params.get(Constants.REQ_KEY_CONTENT));
+        this.widgetService.findRiskIssue(resJsonData, this.widgetService.makeWidgetCondition(), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �쟾泥� �씠�뒋 泥섎━�쁽�솴 議고쉶
+    @RequestMapping(value = "/widget/findIssueComplete", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findIssueComplete(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        WidgetCondition condition = WidgetCondition.make(params.get(Constants.REQ_KEY_CONTENT));
+        this.widgetService.findIssueComplete(resJsonData, this.widgetService.makeWidgetCondition(), WidgetCondition.make(params.get(Constants.REQ_KEY_CONTENT)).getSearchPeriod());
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 ���엯 蹂� �씠�뒋 �젙蹂� 議고쉶
+    @RequestMapping(value = "/widget/findByStandIssueType", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findByStandIssueType(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.widgetService.findByStandIssueType(resJsonData, WidgetCondition.make(params.get(Constants.REQ_KEY_CONTENT)), true);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+
+
+    //以묒슂�룄 蹂� �씠�뒋 �젙蹂� 議고쉶
+    @RequestMapping(value = "/widget/findSeverityIssueWidget", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findSeverityIssueWidget(@RequestBody Map<String, Map<String, Object>> params){
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.widgetService.findSeverityIssueWidget(resJsonData, this.widgetService.makeWidgetCondition(), params.get(Constants.REQ_KEY_CONTENT), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+
+
+    //  �쐞�젽 �뀒�씠釉� �뿊��濡� �떎�슫濡쒕뱶
+    @RequestMapping(value = "/widget/downloadExcel", method = RequestMethod.POST)
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        return this.widgetService.downloadExcel(request, model);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/WorkflowController.java b/src/main/java/kr/wisestone/owl/web/controller/WorkflowController.java
new file mode 100644
index 0000000..1320cee
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/WorkflowController.java
@@ -0,0 +1,100 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.WorkflowService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.AttachedFileVo;
+import kr.wisestone.owl.web.condition.WorkflowCondition;
+import kr.wisestone.owl.web.form.WorkflowForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-10.
+ */
+@Controller
+public class WorkflowController extends BaseController {
+
+    @Autowired
+    private WorkflowService workflowService;
+
+    //  �썙�겕�뵆濡쒖슦 �깮�꽦
+    @RequestMapping(value = "/workflow/add", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.workflowService.addWorkflow(WorkflowForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �썙�겕�뵆濡쒖슦 議고쉶
+    @RequestMapping(value = "/workflow/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
+
+        this.workflowService.findWorkflow(resJsonData, WorkflowCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �썙�겕�뵆濡쒖슦 �긽�꽭 議고쉶
+    @RequestMapping(value = "/workflow/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> detail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.workflowService.detailWorkflow(resJsonData, WorkflowCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �썙�겕�뵆濡쒖슦 �닔�젙
+    @RequestMapping(value = "/workflow/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.workflowService.modifyWorkflow(WorkflowForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �썙�겕�뵆濡쒖슦 �궘�젣
+    @SuppressWarnings("unchecked")
+    @RequestMapping(value = "/workflow/remove", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> removes(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.workflowService.removeWorkflows(WorkflowForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �썙�겕�뵆濡쒖슦 �뿊�� �떎�슫濡쒕뱶
+    @RequestMapping(value = "/workflow/downloadExcel", method = RequestMethod.POST)
+    public ModelAndView downloadExcel(HttpServletRequest request, Model model) {
+        return this.workflowService.downloadExcel(request, model);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/WorkflowStatusController.java b/src/main/java/kr/wisestone/owl/web/controller/WorkflowStatusController.java
new file mode 100644
index 0000000..d866b8a
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/WorkflowStatusController.java
@@ -0,0 +1,66 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.WorkflowStatusService;
+import kr.wisestone.owl.web.condition.WorkflowStatusCondition;
+//import kr.wisestone.owl.web.form.WorkflowStatusForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import sun.rmi.runtime.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-08.
+ */
+@Controller
+public class WorkflowStatusController extends BaseController {
+
+    @Autowired
+    private WorkflowStatusService workflowStatusService;
+
+    @RequestMapping(value = "/workflowStatus/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.workflowStatusService.findWorkflowStatus(resJsonData, WorkflowStatusCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+        return this.setSuccessMessage(resJsonData);
+    }
+
+//    @RequestMapping(value= "/workflowStatus/add", produces = MediaType.APPLICATION_JSON_VALUE)
+//    public @ResponseBody Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+//        Map<String, Object> resJsonData = new HashMap<>();
+//        this.workflowStatusService.addWorkflowStatus(WorkflowStatusForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+//        return this.setSuccessMessage(resJsonData);
+//    }
+//
+//    @RequestMapping(value = "/workflowStatus/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+//    public @ResponseBody Map<String, Object> modifyData(@RequestBody Map<String, Map<String, Object>> params) {
+//        Map<String, Object> resJsonData = new HashMap<>();
+//
+//        this.workflowStatusService.detailWorkflowStatus(resJsonData, WorkflowStatusForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+//
+//        return this.setSuccessMessage(resJsonData);
+//    }
+//
+//    //  �썙�겕�뵆濡쒖슦 �긽�깭 �닔�젙
+//    @RequestMapping(value = "/workflowStatus/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+//    public
+//    @ResponseBody
+//    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+//        Map<String, Object> resJsonData = new HashMap<>();
+//
+//        this.workflowStatusService.modifyWorkflowStatus(WorkflowStatusForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+//
+//        return this.setSuccessMessage(resJsonData);
+//    }
+
+
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/WorkspaceController.java b/src/main/java/kr/wisestone/owl/web/controller/WorkspaceController.java
new file mode 100644
index 0000000..3175651
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/WorkspaceController.java
@@ -0,0 +1,87 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.service.WorkspaceService;
+import kr.wisestone.owl.web.form.UserInviteForm;
+import kr.wisestone.owl.web.form.WorkspaceForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-03-12.
+ */
+@Controller
+public class WorkspaceController extends BaseController {
+
+    @Autowired
+    private WorkspaceService workspaceService;
+
+    //  �뾽臾닿났媛� 議고쉶
+    @RequestMapping(value = "/workspace/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find() {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.workspaceService.find(resJsonData);
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �뾽臾� 怨듦컙 異붾갑
+    @RequestMapping(value = "/workspace/out", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> out(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.workspaceService.out(WorkspaceForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  Primary �뾽臾� 怨듦컙 �젙蹂�
+    @RequestMapping(value = "/workspace/findPrimaryWorkspace", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findPrimaryWorkspace() {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.workspaceService.findPrimaryWorkspace(resJsonData);
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �옄�떊�씠 愿�由ы븯�뒗 �뾽臾� 怨듦컙 �젙蹂�
+    @RequestMapping(value = "/workspace/findMyWorkspace", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findMyWorkspace() {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.workspaceService.findMyWorkspace(resJsonData);
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �뾽臾닿났媛� �씠由� 蹂�寃�
+    @RequestMapping(value = "/workspace/modify", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.workspaceService.modifyWorkspace(WorkspaceForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �뾽臾� 怨듦컙 李몄뿬 �뿬遺� �솗�씤 - ���떆蹂대뱶 �씠誘몄� �떎�슫濡쒕뱶�뿉�꽌 �궗�슜
+    @RequestMapping(value = "/workspace/checkUseWorkspace", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> checkUseWorkspace() {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.workspaceService.checkUseWorkspace();
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/converter/FileHttpMessageConverter.java b/src/main/java/kr/wisestone/owl/web/converter/FileHttpMessageConverter.java
new file mode 100644
index 0000000..515e5e5
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/converter/FileHttpMessageConverter.java
@@ -0,0 +1,75 @@
+package kr.wisestone.owl.web.converter;
+
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+import org.springframework.util.FileCopyUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FileHttpMessageConverter implements HttpMessageConverter<File> {
+
+    private List<MediaType> supportMediaTypes = new ArrayList<MediaType>();
+
+    public FileHttpMessageConverter() {
+        this.supportMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
+    }
+
+    @Override
+    public boolean canRead(Class<?> arg0, MediaType mediaType) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
+        // TODO Auto-generated method stub
+        return File.class.equals(clazz);
+    }
+
+    @Override
+    public List<MediaType> getSupportedMediaTypes() {
+        // TODO Auto-generated method stub
+        return supportMediaTypes;
+    }
+
+    @Override
+    public void write(File file, MediaType mediaType, HttpOutputMessage message)
+            throws IOException, HttpMessageNotWritableException {
+        if(mediaType == null){
+            mediaType = MediaType.APPLICATION_OCTET_STREAM;
+        }
+
+        message.getHeaders().setContentType(mediaType);
+
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(file);
+            FileCopyUtils.copy(fis, message.getBody());
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } finally {
+            if (fis != null) {
+                fis.close();
+            }
+        }
+
+    }
+
+    @Override
+    public File read(Class<? extends File> arg0, HttpInputMessage arg1)
+            throws IOException, HttpMessageNotReadableException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/kr/wisestone/owl/web/form/CustomFieldForm.java b/src/main/java/kr/wisestone/owl/web/form/CustomFieldForm.java
new file mode 100644
index 0000000..8e3935f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/CustomFieldForm.java
@@ -0,0 +1,106 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-28.
+ */
+public class CustomFieldForm {
+    private Long id;
+    private String name;
+    private String customFieldType;
+    private String defaultValue;
+    private List<String> options = Lists.newArrayList();
+    private Long workspaceId;
+    private List<Long> removeIds = Lists.newArrayList();
+    private String useFlag;
+
+    public CustomFieldForm(){}
+
+    public static CustomFieldForm make(Map<String, Object> params) {
+        CustomFieldForm form = ConvertUtil.convertMapToClass(params, CustomFieldForm.class);
+
+        if (MapUtil.getLongs(params, "removeIds") != null) {
+            form.setRemoveIds(MapUtil.getLongs(params, "removeIds"));
+        }
+
+        if (MapUtil.getStrings(params, "options") != null) {
+            form.setOptions(MapUtil.getStrings(params, "options"));
+        }
+
+        return form;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCustomFieldType() {
+        return customFieldType;
+    }
+
+    public void setCustomFieldType(String customFieldType) {
+        this.customFieldType = customFieldType;
+    }
+
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    public void setDefaultValue(String defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public List<Long> getRemoveIds() {
+        return removeIds;
+    }
+
+    public void setRemoveIds(List<Long> removeIds) {
+        this.removeIds = removeIds;
+    }
+
+    public List<String> getOptions() {
+        return options;
+    }
+
+    public void setOptions(List<String> options) {
+        this.options = options;
+    }
+
+    public void addRemoveIds(Long removeId) {
+        this.removeIds.add(removeId);
+    }
+
+    public String getUseFlag() {
+        return useFlag;
+    }
+
+    public void setUseFlag(String useFlag) {
+        this.useFlag = useFlag;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/EventForm.java b/src/main/java/kr/wisestone/owl/web/form/EventForm.java
new file mode 100644
index 0000000..fc3019f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/EventForm.java
@@ -0,0 +1,111 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+public class EventForm {
+    private Long id;
+    private String title;
+    private String description;
+    private Integer status;
+    private String startDate;
+    private String endDate;
+    private Boolean activation = Boolean.FALSE;
+    private List<Long> userIds = Lists.newArrayList();
+    private List<Long> removeIds = Lists.newArrayList();
+
+    public EventForm(){}
+
+    public static EventForm make(Map<String, Object> params) {
+        EventForm eventForm = ConvertUtil.convertMapToClass(params, EventForm.class);
+
+        if (MapUtil.getLongs(params, "userIds") != null) {
+            eventForm.setUserIds(MapUtil.getLongs(params, "userIds"));
+        }
+
+        if (MapUtil.getLongs(params, "removeIds") != null) {
+            eventForm.setRemoveIds(MapUtil.getLongs(params, "removeIds"));
+        }
+
+        return eventForm;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public String getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(String startDate) {
+        this.startDate = startDate;
+    }
+
+    public String getEndDate() {
+        return endDate;
+    }
+
+    public void setEndDate(String endDate) {
+        this.endDate = endDate;
+    }
+
+    public Boolean getActivation() {
+        return activation;
+    }
+
+    public void setActivation(Boolean activation) {
+        this.activation = activation;
+    }
+
+    public List<Long> getRemoveIds() {
+        return removeIds;
+    }
+
+    public void setRemoveIds(List<Long> removeIds) {
+        this.removeIds = removeIds;
+    }
+
+    public List<Long> getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(List<Long> userIds) {
+        this.userIds = userIds;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/FaqForm.java b/src/main/java/kr/wisestone/owl/web/form/FaqForm.java
new file mode 100644
index 0000000..83ad59e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/FaqForm.java
@@ -0,0 +1,93 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * zenith at 20200730
+ */
+public class FaqForm {
+    private Long id;
+    private String title;
+    private String description;
+    private Integer status;
+    private Boolean activation = Boolean.FALSE;
+    private List<Long> userIds = Lists.newArrayList();
+    private List<Long> removeIds = Lists.newArrayList();
+
+    public FaqForm(){}
+
+    public static FaqForm make(Map<String, Object> params) {
+        FaqForm faqForm = ConvertUtil.convertMapToClass(params, FaqForm.class);
+
+        if (MapUtil.getLongs(params, "userIds") != null) {
+            faqForm.setUserIds(MapUtil.getLongs(params, "userIds"));
+        }
+
+        if (MapUtil.getLongs(params, "removeIds") != null) {
+            faqForm.setRemoveIds(MapUtil.getLongs(params, "removeIds"));
+        }
+
+        return faqForm;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public Boolean getActivation() {
+        return activation;
+    }
+
+    public void setActivation(Boolean activation) {
+        this.activation = activation;
+    }
+
+    public List<Long> getRemoveIds() {
+        return removeIds;
+    }
+
+    public void setRemoveIds(List<Long> removeIds) {
+        this.removeIds = removeIds;
+    }
+
+    public List<Long> getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(List<Long> userIds) {
+        this.userIds = userIds;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/GuideForm.java b/src/main/java/kr/wisestone/owl/web/form/GuideForm.java
new file mode 100644
index 0000000..d796bd0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/GuideForm.java
@@ -0,0 +1,93 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+public class GuideForm {
+    private Long id;
+    private String title;
+    private String description;
+    private Integer status;
+    private Boolean activation = Boolean.FALSE;
+    private List<Long> userIds = Lists.newArrayList();
+    private List<Long> removeIds = Lists.newArrayList();
+
+    public GuideForm(){}
+
+    public static GuideForm make(Map<String, Object> params) {
+        GuideForm guideForm = ConvertUtil.convertMapToClass(params, GuideForm.class);
+
+        if (MapUtil.getLongs(params, "userIds") != null) {
+            guideForm.setUserIds(MapUtil.getLongs(params, "userIds"));
+        }
+
+        if (MapUtil.getLongs(params, "removeIds") != null) {
+            guideForm.setRemoveIds(MapUtil.getLongs(params, "removeIds"));
+        }
+
+        return guideForm;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public Boolean getActivation() {
+        return activation;
+    }
+
+    public void setActivation(Boolean activation) {
+        this.activation = activation;
+    }
+
+    public List<Long> getRemoveIds() {
+        return removeIds;
+    }
+
+    public void setRemoveIds(List<Long> removeIds) {
+        this.removeIds = removeIds;
+    }
+
+    public List<Long> getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(List<Long> userIds) {
+        this.userIds = userIds;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/IssueCommentForm.java b/src/main/java/kr/wisestone/owl/web/form/IssueCommentForm.java
new file mode 100644
index 0000000..d7cd538
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/IssueCommentForm.java
@@ -0,0 +1,62 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-08.
+ */
+public class IssueCommentForm {
+    private Long id;
+    private Long issueId;
+    private String description;
+    private List<Long> removeIds = Lists.newArrayList();
+
+    public IssueCommentForm(){}
+
+    public static IssueCommentForm make(Map<String, Object> params) {
+        IssueCommentForm form = ConvertUtil.convertMapToClass(params, IssueCommentForm.class);
+
+        if (MapUtil.getLongs(params, "removeIds") != null) {
+            form.setRemoveIds(MapUtil.getLongs(params, "removeIds"));
+        }
+
+        return form;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getIssueId() {
+        return issueId;
+    }
+
+    public void setIssueId(Long issueId) {
+        this.issueId = issueId;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public List<Long> getRemoveIds() {
+        return removeIds;
+    }
+
+    public void setRemoveIds(List<Long> removeIds) {
+        this.removeIds = removeIds;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/IssueForm.java b/src/main/java/kr/wisestone/owl/web/form/IssueForm.java
new file mode 100644
index 0000000..e36cf92
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/IssueForm.java
@@ -0,0 +1,260 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.enumType.IssueModifyType;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.DateUtil;
+import kr.wisestone.owl.util.MapUtil;
+import org.springframework.util.StringUtils;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-03.
+ */
+public class IssueForm {
+    private Long id;
+    private Long projectId;
+    private Long issueStatusId;
+    private Long issueTypeId;
+    private Long priorityId;
+    private Long severityId;
+    private String title;
+    private String description;
+    private String startDate;
+    private String completeDate;
+    private Long issueNumber;
+    private Long registerId;    //  �벑濡앹옄 �븘�씠�뵒 - issue insert batch �뿉�꽌 �궗�슜
+    private List<Long> userIds = Lists.newArrayList();
+    private List<String> sendEmails = Lists.newArrayList(); //  �씠硫붿씪 諛쒖넚 ���긽�옄
+    private List<Long> attachedFileIds = Lists.newArrayList();
+    private Long relationIssue;   // �뿰愿� �씪媛�
+    private List<Map<String, Object>> issueCustomFields = Lists.newArrayList();
+    private List<Long> removeFiles = Lists.newArrayList();
+    private List<Long> removeIds = Lists.newArrayList();
+    private List<Long> ids = Lists.newArrayList();  //  �씠�뒋 �떎以� �긽�깭 蹂�寃쎌뿉�꽌 �궗�슜
+    private String comment; //  �씠�뒋 �긽�깭 蹂�寃쎌뿉�꽌 蹂�寃� �궗�쑀瑜� �엯�젰�븷 �븣 �궗�슜
+
+    public IssueForm() {
+    }
+
+    public static IssueForm make(Map<String, Object> params) {
+        IssueForm form = ConvertUtil.convertMapToClass(params, IssueForm.class);
+
+        //  �떆�옉�씪 ~ 醫낅즺�씪
+        String startCompleteDateRange = MapUtil.getString(params, "startCompleteDateRange");
+
+        if (startCompleteDateRange != null) {
+            String[] taskPeriod = startCompleteDateRange.split("~");
+            form.setStartDate(taskPeriod[0].trim());
+            form.setCompleteDate(taskPeriod[1].trim());
+        }
+
+        //  �떞�떦�옄 �젙蹂�
+        if (MapUtil.getLongs(params, "userIds") != null) {
+            form.setUserIds(MapUtil.getLongs(params, "userIds"));
+        }
+
+        //  硫붿씪 諛쒖넚�옄 �젙蹂�
+        if (MapUtil.getStrings(params, "sendEmails") != null) {
+            form.setSendEmails(MapUtil.getStrings(params, "sendEmails"));
+        }
+
+        //  �뀓�뒪�듃 �뿉�뵒�꽣�뿉 泥⑤��맂 �뙆�씪�쓣 �씠�뒋�� �뿰寃고븯�뒗 �젙蹂�
+        if (MapUtil.getLongs(params, "attachedFileIds") != null) {
+            form.setAttachedFileIds(MapUtil.getLongs(params, "attachedFileIds"));
+        }
+
+        //  �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂�
+        if (MapUtil.getObject(params, "issueCustomFields") != null) {
+            form.setIssueCustomFields((List)MapUtil.getObject(params, "issueCustomFields"));
+        }
+
+        //  �궘�젣 泥⑤� �뙆�씪 �젙蹂�
+        if (MapUtil.getLongs(params, "removeFiles") != null) {
+            form.setRemoveFiles(MapUtil.getLongs(params, "removeFiles"));
+        }
+
+        //  �궘�젣 �씠�뒋 �젙蹂�
+        if (MapUtil.getLongs(params, "removeIds") != null) {
+            form.setRemoveIds(MapUtil.getLongs(params, "removeIds"));
+        }
+
+        //  �떎以� �긽�깭 蹂�寃쎌뿉�꽌 �씠�뒋 �븘�씠�뵒
+        if (MapUtil.getLongs(params, "ids") != null) {
+            form.setIds(MapUtil.getLongs(params, "ids"));
+        }
+
+        return form;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(Long projectId) {
+        this.projectId = projectId;
+    }
+
+    public Long getRelationIssue() { return this.relationIssue; }
+
+    public void setRelationIssue(Long issues) { this.relationIssue = issues; }
+
+    public Long getIssueStatusId() {
+        return issueStatusId;
+    }
+
+    public void setIssueStatusId(Long issueStatusId) {
+        this.issueStatusId = issueStatusId;
+    }
+
+    public Long getIssueTypeId() {
+        return issueTypeId;
+    }
+
+    public void setIssueTypeId(Long issueTypeId) {
+        this.issueTypeId = issueTypeId;
+    }
+
+    public Long getPriorityId() {
+        return priorityId;
+    }
+
+    public void setPriorityId(Long priorityId) {
+        this.priorityId = priorityId;
+    }
+
+    public Long getSeverityId() {
+        return severityId;
+    }
+
+    public void setSeverityId(Long severityId) {
+        this.severityId = severityId;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(String startDate) {
+        this.startDate = startDate;
+    }
+
+    public String getCompleteDate() {
+        return completeDate;
+    }
+
+    public void setCompleteDate(String completeDate) {
+        this.completeDate = completeDate;
+    }
+
+    public List<Long> getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(List<Long> userIds) {
+        this.userIds = userIds;
+    }
+
+    public List<Long> getRemoveFiles() {
+        return removeFiles;
+    }
+
+    public void setRemoveFiles(List<Long> removeFiles) {
+        this.removeFiles = removeFiles;
+    }
+
+    public List<Map<String, Object>> getIssueCustomFields() {
+        return issueCustomFields;
+    }
+
+    public void setIssueCustomFields(List<Map<String, Object>> issueCustomFields) {
+        this.issueCustomFields = issueCustomFields;
+    }
+
+    public void addIssueCustomFields(Map<String, Object> issueCustomField) {
+        this.issueCustomFields.add(issueCustomField);
+    }
+
+    public List<Long> getRemoveIds() {
+        return removeIds;
+    }
+
+    public void setRemoveIds(List<Long> removeIds) {
+        this.removeIds = removeIds;
+    }
+
+    public List<Long> getAttachedFileIds() {
+        return attachedFileIds;
+    }
+
+    public void setAttachedFileIds(List<Long> attachedFileIds) {
+        this.attachedFileIds = attachedFileIds;
+    }
+
+    public Long getIssueNumber() {
+        return issueNumber;
+    }
+
+    public void setIssueNumber(Long issueNumber) {
+        this.issueNumber = issueNumber;
+    }
+
+    public Long getRegisterId() {
+        return registerId;
+    }
+
+    public void setRegisterId(Long registerId) {
+        this.registerId = registerId;
+    }
+
+    public List<Long> getIds() {
+        return ids;
+    }
+
+    public void setIds(List<Long> ids) {
+        this.ids = ids;
+    }
+
+    public List<String> getSendEmails() {
+        return sendEmails;
+    }
+
+    public void setSendEmails(List<String> sendEmails) {
+        this.sendEmails = sendEmails;
+    }
+
+    public String getComment() {
+        return comment;
+    }
+
+    public void setComment(String comment) {
+        this.comment = comment;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/IssueReservationForm.java b/src/main/java/kr/wisestone/owl/web/form/IssueReservationForm.java
new file mode 100644
index 0000000..c48d03b
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/IssueReservationForm.java
@@ -0,0 +1,54 @@
+package kr.wisestone.owl.web.form;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-08
+ */
+public class IssueReservationForm {
+    private Long id;
+    private Long issueId;
+    private String reservation;
+    private String issueReservationType;
+
+    public IssueReservationForm(){}
+
+    public static IssueReservationForm make(Map<String, Object> params) {
+        IssueReservationForm form = ConvertUtil.convertMapToClass(params, IssueReservationForm.class);
+        return form;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getIssueId() {
+        return issueId;
+    }
+
+    public void setIssueId(Long issueId) {
+        this.issueId = issueId;
+    }
+
+    public String getReservation() {
+        return reservation;
+    }
+
+    public void setReservation(String reservation) {
+        this.reservation = reservation;
+    }
+
+    public String getIssueReservationType() {
+        return issueReservationType;
+    }
+
+    public void setIssueReservationType(String issueReservationType) {
+        this.issueReservationType = issueReservationType;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/IssueStatusChangeForm.java b/src/main/java/kr/wisestone/owl/web/form/IssueStatusChangeForm.java
new file mode 100644
index 0000000..756f953
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/IssueStatusChangeForm.java
@@ -0,0 +1,87 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-08.
+ */
+public class IssueStatusChangeForm {
+    private Long userId;
+    private Long targetStatusId;
+    private String comment;
+    private Long projectId;
+    private Long currentPage;
+    private List<Long> issueIds = Lists.newArrayList();
+    private List<Long> allIssues = Lists.newArrayList();
+
+    public IssueStatusChangeForm(){}
+
+    public static IssueStatusChangeForm make(Map<String, Object> params) {
+        IssueStatusChangeForm form = ConvertUtil.convertMapToClass(params, IssueStatusChangeForm.class);
+        form.setIssueIds(MapUtil.getLongs(params, "issueIds"));
+        form.setAllIssues(MapUtil.getLongs(params, "allIssues"));
+
+        return form;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public Long getTargetStatusId() {
+        return targetStatusId;
+    }
+
+    public void setTargetStatusId(Long targetStatusId) {
+        this.targetStatusId = targetStatusId;
+    }
+
+    public String getComment() {
+        return comment;
+    }
+
+    public void setComment(String comment) {
+        this.comment = comment;
+    }
+
+    public List<Long> getIssueIds() {
+        return issueIds;
+    }
+
+    public void setIssueIds(List<Long> issueIds) {
+        this.issueIds = issueIds;
+    }
+
+    public List<Long> getAllIssues() {
+        return allIssues;
+    }
+
+    public void setAllIssues(List<Long> allIssues) {
+        this.allIssues = allIssues;
+    }
+
+    public Long getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(Long projectId) {
+        this.projectId = projectId;
+    }
+
+    public Long getCurrentPage() {
+        return currentPage;
+    }
+
+    public void setCurrentPage(Long currentPage) {
+        this.currentPage = currentPage;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/IssueStatusForm.java b/src/main/java/kr/wisestone/owl/web/form/IssueStatusForm.java
new file mode 100644
index 0000000..bb86e08
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/IssueStatusForm.java
@@ -0,0 +1,122 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.enumType.IssueStatusType;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-08.
+ */
+public class IssueStatusForm {
+
+    private Long id;
+    private String name;
+    private Boolean firstYn;
+    private Boolean middleYn;
+    private Boolean lastYn;
+    private Boolean defaultYn;
+    private Long position;
+    private String color;
+    private String issueStatusType;
+    private List<Long> removeIds = Lists.newArrayList();
+
+    public IssueStatusForm(){}
+
+    public static IssueStatusForm make(Map<String, Object> params) {
+        IssueStatusForm form = ConvertUtil.convertMapToClass(params, IssueStatusForm.class);
+
+        if (MapUtil.getLongs(params, "removeIds") != null) {
+            form.setRemoveIds(MapUtil.getLongs(params, "removeIds"));
+        }
+
+        return form;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Boolean getFirstYn() {
+        return firstYn;
+    }
+
+    public void setFirstYn(Boolean firstYn) {
+        this.firstYn = firstYn;
+    }
+
+    public Boolean getMiddleYn() {
+        return middleYn;
+    }
+
+    public void setMiddleYn(Boolean middleYn) {
+        this.middleYn = middleYn;
+    }
+
+    public Boolean getLastYn() {
+        return lastYn;
+    }
+
+    public void setLastYn(Boolean lastYn) {
+        this.lastYn = lastYn;
+    }
+
+    public Boolean getDefaultYn() {
+        return defaultYn;
+    }
+
+    public void setDefaultYn(Boolean defaultYn) {
+        this.defaultYn = defaultYn;
+    }
+
+    public Long getPosition() {
+        return position;
+    }
+
+    public void setPosition(Long position) {
+        this.position = position;
+    }
+
+    public String getIssueStatusType() {
+        return issueStatusType;
+    }
+
+    public void setIssueStatusType(String issueStatusType) {
+        this.issueStatusType = issueStatusType;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+
+    public List<Long> getRemoveIds() {
+        return removeIds;
+    }
+
+    public void setRemoveIds(List<Long> removeIds) {
+        this.removeIds = removeIds;
+    }
+
+    public void addRemoveIds(Long removeId) {
+        this.removeIds.add(removeId);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/IssueTypeCustomFieldForm.java b/src/main/java/kr/wisestone/owl/web/form/IssueTypeCustomFieldForm.java
new file mode 100644
index 0000000..bf05c7b
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/IssueTypeCustomFieldForm.java
@@ -0,0 +1,62 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-30.
+ */
+public class IssueTypeCustomFieldForm {
+    private Long id;
+    private Long projectId;
+    private Long issueTypeId;
+    private List<Map<String, Object>> relationCustomFields = Lists.newArrayList();
+
+    public IssueTypeCustomFieldForm(){}
+
+    public static IssueTypeCustomFieldForm make(Map<String, Object> params) {
+        IssueTypeCustomFieldForm form = ConvertUtil.convertMapToClass(params, IssueTypeCustomFieldForm.class);
+
+        if (MapUtil.getObject(params, "relationCustomFields") != null) {
+            form.setRelationCustomFields((List)MapUtil.getObject(params, "relationCustomFields"));
+        }
+
+        return form;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(Long projectId) {
+        this.projectId = projectId;
+    }
+
+    public Long getIssueTypeId() {
+        return issueTypeId;
+    }
+
+    public void setIssueTypeId(Long issueTypeId) {
+        this.issueTypeId = issueTypeId;
+    }
+
+    public List<Map<String, Object>> getRelationCustomFields() {
+        return relationCustomFields;
+    }
+
+    public void setRelationCustomFields(List<Map<String, Object>> relationCustomFields) {
+        this.relationCustomFields = relationCustomFields;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/IssueTypeForm.java b/src/main/java/kr/wisestone/owl/web/form/IssueTypeForm.java
new file mode 100644
index 0000000..5d30cc0
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/IssueTypeForm.java
@@ -0,0 +1,84 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-05-29.
+ */
+public class IssueTypeForm {
+    private Long id;
+    private String name;
+    private String description;
+    private String color;
+    private Long workflowId;
+    private List<Long> removeIds = Lists.newArrayList();
+
+    public IssueTypeForm(){}
+
+    public static IssueTypeForm make(Map<String, Object> params) {
+        IssueTypeForm form = ConvertUtil.convertMapToClass(params, IssueTypeForm.class);
+
+        if (MapUtil.getLongs(params, "removeIds") != null) {
+            form.setRemoveIds(MapUtil.getLongs(params, "removeIds"));
+        }
+
+        return form;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+
+    public Long getWorkflowId() {
+        return workflowId;
+    }
+
+    public void setWorkflowId(Long workflowId) {
+        this.workflowId = workflowId;
+    }
+
+    public List<Long> getRemoveIds() {
+        return removeIds;
+    }
+
+    public void setRemoveIds(List<Long> removeIds) {
+        this.removeIds = removeIds;
+    }
+
+    public void addRemoveIds(Long removeId) {
+        this.removeIds.add(removeId);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/ManageUserForm.java b/src/main/java/kr/wisestone/owl/web/form/ManageUserForm.java
new file mode 100644
index 0000000..21f1cb5
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/ManageUserForm.java
@@ -0,0 +1,130 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * edit by zenith at 20200804
+ */
+public class ManageUserForm {
+    private Long id;
+    private Boolean useYn;
+    private Long userId;
+    private Boolean permWorkSpace;
+    private Boolean permProjectSetting;
+    private Boolean permIssueSetting;
+    private Boolean permUser;
+    private Boolean permNotice;
+    private Boolean permFAQ;
+    private Boolean permQnA;
+    private Boolean permEvent;
+    private Boolean permGuide;
+
+    public ManageUserForm(){}
+
+    public static ManageUserForm make(Map<String, Object> params) {
+        ManageUserForm form = ConvertUtil.convertMapToClass(params, ManageUserForm.class);
+
+        return form;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Boolean getUseYn() {
+        return useYn;
+    }
+
+    public void setUseYn(Boolean useYn) {
+        this.useYn = useYn;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public Boolean getPermWorkSpace() {
+        return permWorkSpace;
+    }
+
+    public void setPermWorkSpace(Boolean permWorkSpace) {
+        this.permWorkSpace = permWorkSpace;
+    }
+
+    public Boolean getPermProjectSetting() {
+        return permProjectSetting;
+    }
+
+    public void setPermProjectSetting(Boolean permProjectSetting) {
+        this.permProjectSetting = permProjectSetting;
+    }
+
+    public Boolean getPermIssueSetting() {
+        return permIssueSetting;
+    }
+
+    public void setPermIssueSetting(Boolean permIssueSetting) {
+        this.permIssueSetting = permIssueSetting;
+    }
+
+    public Boolean getPermUser() {
+        return permUser;
+    }
+
+    public void setPermUser(Boolean permUser) {
+        this.permUser = permUser;
+    }
+
+    public Boolean getPermNotice() {
+        return permNotice;
+    }
+
+    public void setPermNotice(Boolean permNotice) {
+        this.permNotice = permNotice;
+    }
+
+    public Boolean getPermFAQ() {
+        return permFAQ;
+    }
+
+    public void setPermFAQ(Boolean permFAQ) {
+        this.permFAQ = permFAQ;
+    }
+
+    public Boolean getPermQnA() {
+        return permQnA;
+    }
+
+    public void setPermQnA(Boolean permQnA) {
+        this.permQnA = permQnA;
+    }
+
+    public Boolean getPermEvent() {
+        return permEvent;
+    }
+
+    public void setpermEvent(Boolean permEvent) {
+        this.permEvent = permEvent;
+    }
+
+    public Boolean getPermGuide() {
+        return permGuide;
+    }
+
+    public void setPermGuide(Boolean permGuide) {
+        this.permGuide = permGuide;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/NoticeForm.java b/src/main/java/kr/wisestone/owl/web/form/NoticeForm.java
new file mode 100644
index 0000000..83fbc84
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/NoticeForm.java
@@ -0,0 +1,75 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Create By J E O N G - S U N / 2019-05-22
+ */
+public class NoticeForm {
+    private Long id;
+    private String title;
+    private String description;
+    private List<Long> userIds = Lists.newArrayList();
+    private List<Long> removeIds = Lists.newArrayList();
+
+    public NoticeForm(){}
+
+    public static NoticeForm make(Map<String, Object> params) {
+        NoticeForm noticeForm = ConvertUtil.convertMapToClass(params, NoticeForm.class);
+
+        if (MapUtil.getLongs(params, "userIds") != null) {
+            noticeForm.setUserIds(MapUtil.getLongs(params, "userIds"));
+        }
+
+        if (MapUtil.getLongs(params, "removeIds") != null) {
+            noticeForm.setRemoveIds(MapUtil.getLongs(params, "removeIds"));
+        }
+
+        return noticeForm;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public List<Long> getRemoveIds() {
+        return removeIds;
+    }
+
+    public void setRemoveIds(List<Long> removeIds) {
+        this.removeIds = removeIds;
+    }
+
+    public List<Long> getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(List<Long> userIds) {
+        this.userIds = userIds;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/PaymentForm.java b/src/main/java/kr/wisestone/owl/web/form/PaymentForm.java
new file mode 100644
index 0000000..0f01ed7
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/PaymentForm.java
@@ -0,0 +1,171 @@
+package kr.wisestone.owl.web.form;
+
+import kr.wisestone.owl.util.ConvertUtil;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-02-13.
+ */
+public class PaymentForm {
+    private Long id;
+    private Long workspaceId;
+    private Integer buyUser;
+    private String type;
+    private String cardNumber1;
+    private String cardNumber2;
+    private String cardNumber3;
+    private String cardNumber4;
+    private String expireYear;
+    private String expireMonth;
+    private String birth;
+    private String cardPwd;
+    private String merchantUid;
+    private String customerUid;
+    private String paymentMethod;
+    private Integer paymentAmount;
+
+    private String reservationDisableUserIds; //  鍮꾪솢�꽦�솕 �삁�젙 �궗�슜�옄 �븘�씠�뵒
+
+    public PaymentForm() {
+    }
+
+    public static PaymentForm make(Map<String, Object> params) {
+        return ConvertUtil.convertMapToClass(params, PaymentForm.class);
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public Integer getBuyUser() {
+        return buyUser;
+    }
+
+    public void setBuyUser(Integer buyUser) {
+        this.buyUser = buyUser;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getCardNumber1() {
+        return cardNumber1;
+    }
+
+    public void setCardNumber1(String cardNumber1) {
+        this.cardNumber1 = cardNumber1;
+    }
+
+    public String getCardNumber2() {
+        return cardNumber2;
+    }
+
+    public void setCardNumber2(String cardNumber2) {
+        this.cardNumber2 = cardNumber2;
+    }
+
+    public String getCardNumber3() {
+        return cardNumber3;
+    }
+
+    public void setCardNumber3(String cardNumber3) {
+        this.cardNumber3 = cardNumber3;
+    }
+
+    public String getCardNumber4() {
+        return cardNumber4;
+    }
+
+    public void setCardNumber4(String cardNumber4) {
+        this.cardNumber4 = cardNumber4;
+    }
+
+    public String getExpireYear() {
+        return expireYear;
+    }
+
+    public void setExpireYear(String expireYear) {
+        this.expireYear = expireYear;
+    }
+
+    public String getExpireMonth() {
+        return expireMonth;
+    }
+
+    public void setExpireMonth(String expireMonth) {
+        this.expireMonth = expireMonth;
+    }
+
+    public String getBirth() {
+        return birth;
+    }
+
+    public void setBirth(String birth) {
+        this.birth = birth;
+    }
+
+    public String getCardPwd() {
+        return cardPwd;
+    }
+
+    public void setCardPwd(String cardPwd) {
+        this.cardPwd = cardPwd;
+    }
+
+    public String getMerchantUid() {
+        return merchantUid;
+    }
+
+    public void setMerchantUid(String merchantUid) {
+        this.merchantUid = merchantUid;
+    }
+
+    public String getCustomerUid() {
+        return customerUid;
+    }
+
+    public void setCustomerUid(String customerUid) {
+        this.customerUid = customerUid;
+    }
+
+    public String getPaymentMethod() {
+        return paymentMethod;
+    }
+
+    public void setPaymentMethod(String paymentMethod) {
+        this.paymentMethod = paymentMethod;
+    }
+
+    public Integer getPaymentAmount() {
+        return paymentAmount;
+    }
+
+    public void setPaymentAmount(Integer paymentAmount) {
+        this.paymentAmount = paymentAmount;
+    }
+
+    public String getReservationDisableUserIds() {
+        return reservationDisableUserIds;
+    }
+
+    public void setReservationDisableUserIds(String reservationDisableUserIds) {
+        this.reservationDisableUserIds = reservationDisableUserIds;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/ProjectForm.java b/src/main/java/kr/wisestone/owl/web/form/ProjectForm.java
new file mode 100644
index 0000000..a8ce414
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/ProjectForm.java
@@ -0,0 +1,157 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+public class ProjectForm {
+    private Long id;
+    private Long parentProjectId;
+    private String name;
+    private String description;
+    private String status;
+    private String projectKey;
+    private String startDate;
+    private String endDate;
+    private String projectType;
+    private Long workspaceId;
+    private List<Long> managerIds = Lists.newArrayList();
+    private List<Long> removeIds = Lists.newArrayList();
+    private List<Long> userIds = Lists.newArrayList();
+
+    public ProjectForm() {
+    }
+
+    public static ProjectForm make(Map<String, Object> params) {
+        ProjectForm form =  ConvertUtil.convertMapToClass(params, ProjectForm.class);
+
+        if (MapUtil.getLongs(params, "managerIds") != null) {
+            form.setManagerIds(MapUtil.getLongs(params, "managerIds"));
+        }
+
+        if (MapUtil.getLongs(params, "userIds") != null) {
+            form.setUserIds(MapUtil.getLongs(params, "userIds"));
+        }
+
+        if (MapUtil.getLongs(params, "removeIds") != null) {
+            form.setRemoveIds(MapUtil.getLongs(params, "removeIds"));
+        }
+
+        if (form.getProjectKey() != null) {
+            form.setProjectKey(form.getProjectKey().toUpperCase());
+        }
+
+        return form;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getParentProjectId() {
+        return this.parentProjectId;
+    }
+
+    public void setParentProjectId(Long id) {
+        this.parentProjectId = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(String startDate) {
+        this.startDate = startDate;
+    }
+
+    public String getEndDate() {
+        return endDate;
+    }
+
+    public void setEndDate(String endDate) {
+        this.endDate = endDate;
+    }
+
+    public String getProjectType() {
+        return projectType;
+    }
+
+    public void setProjectType(String projectType) {
+        this.projectType = projectType;
+    }
+
+    public List<Long> getManagerIds() {
+        return managerIds;
+    }
+
+    public void setManagerIds(List<Long> managerIds) {
+        this.managerIds = managerIds;
+    }
+
+    public String getProjectKey() {
+        return projectKey;
+    }
+
+    public void setProjectKey(String projectKey) {
+        this.projectKey = projectKey;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public List<Long> getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(List<Long> userIds) {
+        this.userIds = userIds;
+    }
+
+    public List<Long> getRemoveIds() {
+        return removeIds;
+    }
+
+    public void setRemoveIds(List<Long> removeIds) {
+        this.removeIds = removeIds;
+    }
+
+    public void addRemoveIds(Long removeId) {
+        this.removeIds.add(removeId);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/QnaForm.java b/src/main/java/kr/wisestone/owl/web/form/QnaForm.java
new file mode 100644
index 0000000..f6f3637
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/QnaForm.java
@@ -0,0 +1,75 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * zenith at 20200730
+ */
+public class QnaForm {
+    private Long id;
+    private String title;
+    private String description;
+    private List<Long> userIds = Lists.newArrayList();
+    private List<Long> removeIds = Lists.newArrayList();
+
+    public QnaForm(){}
+
+    public static QnaForm make(Map<String, Object> params) {
+        QnaForm qnaForm = ConvertUtil.convertMapToClass(params, QnaForm.class);
+
+        if (MapUtil.getLongs(params, "userIds") != null) {
+            qnaForm.setUserIds(MapUtil.getLongs(params, "userIds"));
+        }
+
+        if (MapUtil.getLongs(params, "removeIds") != null) {
+            qnaForm.setRemoveIds(MapUtil.getLongs(params, "removeIds"));
+        }
+
+        return qnaForm;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public List<Long> getRemoveIds() {
+        return removeIds;
+    }
+
+    public void setRemoveIds(List<Long> removeIds) {
+        this.removeIds = removeIds;
+    }
+
+    public List<Long> getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(List<Long> userIds) {
+        this.userIds = userIds;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/UserForm.java b/src/main/java/kr/wisestone/owl/web/form/UserForm.java
new file mode 100644
index 0000000..938513c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/UserForm.java
@@ -0,0 +1,162 @@
+package kr.wisestone.owl.web.form;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * Created by jeong on 2017-12-30.
+ */
+public class UserForm {
+    private Long id;
+    private String name;
+    private String account;
+    private String password;
+    private String currentPassword;
+    private String passwordConfirm;
+    private String profile;
+    private String status;
+    private String phone;
+    private String workspaceName;
+    private String socialType;
+    private Long lastWorkspaceId;
+    private Long lastProjectId;
+    private String reservationNotifyTime;
+    private String language;
+    private String licensekey;
+
+    public UserForm(){}
+
+    public static UserForm make(Map<String, Object> params) {
+        return ConvertUtil.convertMapToClass(params, UserForm.class);
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getCurrentPassword() {
+        return currentPassword;
+    }
+
+    public void setCurrentPassword(String currentPassword) {
+        this.currentPassword = currentPassword;
+    }
+
+    public String getProfile() {
+        return profile;
+    }
+
+    public void setProfile(String profile) {
+        this.profile = profile;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getWorkspaceName() {
+        return workspaceName;
+    }
+
+    public void setWorkspaceName(String workspaceName) {
+        this.workspaceName = workspaceName;
+    }
+
+    public Long getLastWorkspaceId() {
+        return lastWorkspaceId;
+    }
+
+    public Long getLastProjectId() {
+        return this.lastProjectId;
+    }
+
+    public void setLastProjectId(Long id) {
+        this.lastProjectId = id;
+    }
+
+
+    public void setLastWorkspaceId(Long lastWorkspaceId) {
+        this.lastWorkspaceId = lastWorkspaceId;
+    }
+
+    public String getSocialType() {
+        return socialType;
+    }
+
+    public void setSocialType(String socialType) {
+        this.socialType = socialType;
+    }
+
+    public String getReservationNotifyTime() {
+        return reservationNotifyTime;
+    }
+
+    public void setReservationNotifyTime(String reservationNotifyTime) {
+        this.reservationNotifyTime = reservationNotifyTime;
+    }
+
+    public String getPasswordConfirm() {
+        return passwordConfirm;
+    }
+
+    public void setPasswordConfirm(String passwordConfirm) {
+        this.passwordConfirm = passwordConfirm;
+    }
+
+    public String getLanguage() {
+        return language;
+    }
+
+    public void setLanguage(String language) {
+        this.language = language;
+    }
+
+    public String getLicensekey() {
+        return licensekey;
+    }
+
+    public void setLicensekey(String licensekey) {
+        this.licensekey = licensekey;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/UserInviteForm.java b/src/main/java/kr/wisestone/owl/web/form/UserInviteForm.java
new file mode 100644
index 0000000..188c466
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/UserInviteForm.java
@@ -0,0 +1,44 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-03-15.
+ */
+public class UserInviteForm {
+    private List<String> emails;
+    private List<Long> projectIds = Lists.newArrayList();
+
+    public UserInviteForm() {
+    }
+
+    public static UserInviteForm make(Map<String, Object> params) {
+        UserInviteForm form = ConvertUtil.convertMapToClass(params, UserInviteForm.class);
+        form.setProjectIds(MapUtil.getLongs(params, "projectIds"));
+        form.setEmails(MapUtil.getStrings(params, "emails"));
+
+        return form;
+    }
+
+    public List<String> getEmails() {
+        return emails;
+    }
+
+    public void setEmails(List<String> emails) {
+        this.emails = emails;
+    }
+
+    public List<Long> getProjectIds() {
+        return projectIds;
+    }
+
+    public void setProjectIds(List<Long> projectIds) {
+        this.projectIds = projectIds;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/UserWorkspaceForm.java b/src/main/java/kr/wisestone/owl/web/form/UserWorkspaceForm.java
new file mode 100644
index 0000000..9654658
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/UserWorkspaceForm.java
@@ -0,0 +1,37 @@
+package kr.wisestone.owl.web.form;
+
+import kr.wisestone.owl.util.ConvertUtil;
+
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-10-02.
+ */
+public class UserWorkspaceForm {
+    private Long id;
+    private Boolean useYn;
+
+    public UserWorkspaceForm(){}
+
+    public static UserWorkspaceForm make(Map<String, Object> params) {
+        UserWorkspaceForm form = ConvertUtil.convertMapToClass(params, UserWorkspaceForm.class);
+
+        return form;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Boolean getUseYn() {
+        return useYn;
+    }
+
+    public void setUseYn(Boolean useYn) {
+        this.useYn = useYn;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/WorkflowForm.java b/src/main/java/kr/wisestone/owl/web/form/WorkflowForm.java
new file mode 100644
index 0000000..f42ace5
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/WorkflowForm.java
@@ -0,0 +1,144 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.vo.IssueStatusVo;
+import kr.wisestone.owl.vo.WorkflowTransitionVo;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-03.
+ */
+public class WorkflowForm {
+    private Long id;
+    private Long workspaceId;
+    private String name;
+    private String description;
+    private List<IssueStatusVo> issueStatusVos = Lists.newArrayList();
+    private List<Long> removeIds = Lists.newArrayList();
+
+    public WorkflowForm(){}
+
+    public static WorkflowForm make(Map<String, Object> params) {
+        WorkflowForm form = ConvertUtil.convertMapToClass(params, WorkflowForm.class);
+
+        if (MapUtil.getLongs(params, "removeIds") != null) {
+            form.setRemoveIds(MapUtil.getLongs(params, "removeIds"));
+        }
+
+        List<Map<String, Object>> tempIssueStatusVos = (List)params.get("issueStatusVos");
+        List<Map<String, Object>> nodes = (List)params.get("nodes");
+        List<Map<String, Object>> links = (List)params.get("links");
+
+        if (tempIssueStatusVos == null) {
+            return form;
+        }
+
+        for (Map<String, Object> issueStatusVo : tempIssueStatusVos) {
+            IssueStatusVo addIssueStatusVo = ConvertUtil.convertMapToClass(issueStatusVo, IssueStatusVo.class);
+            //  issueStatus x, y 醫뚰몴 媛� 蹂�寃� �젙蹂� �뀑�똿
+            for (Map<String, Object> node : nodes) {
+                Long nodeId = MapUtil.getLong(node, "id");
+
+                if (nodeId == null) {
+                    break;
+                }
+
+                if (nodeId.equals(addIssueStatusVo.getId())) {
+                    addIssueStatusVo.setxLocation((long)Double.parseDouble(MapUtil.getString(node, "x")));
+                    addIssueStatusVo.setyLocation((long)Double.parseDouble(MapUtil.getString(node, "y")));
+                    break;
+                }
+            }
+
+            List<Map<String, Object>> workflowTransitionVos = (List)issueStatusVo.get("workflowTransitionVos");
+
+            for (Map<String, Object> workflowTransitionVo : workflowTransitionVos) {
+                String workflowTransitionId = MapUtil.getString(workflowTransitionVo, "id");
+
+                if (workflowTransitionId == null) {
+                    continue;
+                }
+
+                for (Map<String, Object> link : links) {
+                    String linkId = MapUtil.getString(link, "id");
+
+                    if (linkId == null) {
+                        break;
+                    }
+                    //  �쉶�쟾 諛섍꼍 蹂댁젙 媛� �뀑�똿
+                    if (workflowTransitionId.equals(linkId)) {
+                        workflowTransitionVo.put("correctX", (long)Double.parseDouble(MapUtil.getString(link, "correctX")));
+                        workflowTransitionVo.put("correctY", (long)Double.parseDouble(MapUtil.getString(link, "correctY")));
+                        workflowTransitionVo.put("direct", MapUtil.getBoolean(link, "direct"));
+                    }
+                }
+            }
+
+
+            addIssueStatusVo.setWorkflowTransitionVos(ConvertUtil.convertListToListClass(workflowTransitionVos, WorkflowTransitionVo.class));
+            form.addIssueStatusVos(addIssueStatusVo);
+        }
+
+        return form;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<IssueStatusVo> getIssueStatusVos() {
+        return issueStatusVos;
+    }
+
+    public void setIssueStatusVos(List<IssueStatusVo> issueStatusVos) {
+        this.issueStatusVos = issueStatusVos;
+    }
+
+    public void addIssueStatusVos(IssueStatusVo issueStatusVo) {
+        this.issueStatusVos.add(issueStatusVo);
+    }
+
+    public List<Long> getRemoveIds() {
+        return removeIds;
+    }
+
+    public void setRemoveIds(List<Long> removeIds) {
+        this.removeIds = removeIds;
+    }
+
+    public void addRemoveIds(Long removeId) {
+        this.removeIds.add(removeId);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/WorkflowStatusForm.java b/src/main/java/kr/wisestone/owl/web/form/WorkflowStatusForm.java
new file mode 100644
index 0000000..896d81c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/WorkflowStatusForm.java
@@ -0,0 +1,87 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-01-09.
+ */
+public class WorkflowStatusForm {
+    private Long projectId;
+    private Long id;
+    private String name;
+    private String color;
+    private Long progress;
+    private Long position = 0L; //  湲곕낯 媛�
+
+    private List<Long> workflowStatusIds = Lists.newArrayList();
+
+    public WorkflowStatusForm(){}
+
+    public static WorkflowStatusForm make(Map<String, Object> params) {
+        WorkflowStatusForm form = ConvertUtil.convertMapToClass(params, WorkflowStatusForm.class);
+        form.setWorkflowStatusIds(MapUtil.getLongs(params, "workflowStatusIds"));
+
+        return form;
+    }
+
+    public List<Long> getWorkflowStatusIds() {
+        return workflowStatusIds;
+    }
+
+    public void setWorkflowStatusIds(List<Long> workflowStatusIds) {
+        this.workflowStatusIds = workflowStatusIds;
+    }
+
+    public Long getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(Long projectId) {
+        this.projectId = projectId;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Long getProgress() {
+        return progress;
+    }
+
+    public void setProgress(Long progress) {
+        this.progress = progress;
+    }
+
+    public Long getPosition() {
+        return position;
+    }
+
+    public void setPosition(Long position) {
+        this.position = position;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/WorkspaceForm.java b/src/main/java/kr/wisestone/owl/web/form/WorkspaceForm.java
new file mode 100644
index 0000000..e26c565
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/WorkspaceForm.java
@@ -0,0 +1,50 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by wisestone on 2018-03-16.
+ */
+public class WorkspaceForm {
+    private Long workspaceId;
+    private String name;
+    private List<Long> userIds = Lists.newArrayList();
+
+    public WorkspaceForm(){}
+
+    public static WorkspaceForm make(Map<String, Object> params) {
+        WorkspaceForm form = ConvertUtil.convertMapToClass(params, WorkspaceForm.class);
+        form.setUserIds(MapUtil.getLongs(params, "userIds"));
+
+        return form;
+    }
+
+    public Long getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public void setWorkspaceId(Long workspaceId) {
+        this.workspaceId = workspaceId;
+    }
+
+    public List<Long> getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(List<Long> userIds) {
+        this.userIds = userIds;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/resolver/OwlResponseEntityExceptionHandler.java b/src/main/java/kr/wisestone/owl/web/resolver/OwlResponseEntityExceptionHandler.java
new file mode 100644
index 0000000..560217e
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/resolver/OwlResponseEntityExceptionHandler.java
@@ -0,0 +1,87 @@
+package kr.wisestone.owl.web.resolver;
+
+import kr.wisestone.owl.common.MessageAccessor;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by jeong on 2017-08-03.
+ */
+@ControllerAdvice
+public class OwlResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
+    private static final Logger LOGGER = LoggerFactory.getLogger(OwlResponseEntityExceptionHandler.class);
+
+    @Autowired
+    private MessageAccessor messageAccessor;
+
+    public OwlResponseEntityExceptionHandler() {
+        super();
+    }
+
+    @ExceptionHandler({OwlRuntimeException.class })
+    public ResponseEntity<Object> handleBadRequest(final OwlRuntimeException ex, final WebRequest request) {
+        Map<String, Object> resJsonData = new HashMap<String, Object>();
+        resJsonData.put(Constants.RES_KEY_MESSAGE, this.messageAccessor.getResMessage(ex, Constants.RES_KEY_MSG_FAIL));
+
+        return this.handleExceptionInternal(ex, resJsonData, new HttpHeaders(), HttpStatus.OK, request);
+    }
+
+    @ExceptionHandler({ StackOverflowError.class,
+            NullPointerException.class, IllegalArgumentException.class, IllegalStateException.class })
+    public ResponseEntity<Object> handleInternal(final RuntimeException ex, final WebRequest request) {
+        Map<String, Object> resJsonData = new HashMap<String, Object>();
+        resJsonData.put(Constants.RES_KEY_MESSAGE, this.messageAccessor.getResMessage(MsgConstants.NOT_READABLE_JSON_DATA, Constants.RES_KEY_MSG_FAIL));
+
+        return this.handleExceptionInternal(ex, resJsonData, new HttpHeaders(), HttpStatus.OK, request);
+    }
+
+    @ExceptionHandler({ Exception.class })
+    public ResponseEntity<Object> handleInternal(final Exception ex, final WebRequest request) {
+        Map<String, Object> resJsonData = new HashMap<String, Object>();
+        resJsonData.put(Constants.RES_KEY_MESSAGE, this.messageAccessor.getResMessage(MsgConstants.NOT_READABLE_JSON_DATA, Constants.RES_KEY_MSG_FAIL));
+
+        return this.handleExceptionInternal(ex, resJsonData, new HttpHeaders(), HttpStatus.OK, request);
+    }
+
+    @Override
+    protected ResponseEntity<Object> handleHttpMessageNotReadable(
+            final HttpMessageNotReadableException ex, final HttpHeaders headers,
+            final HttpStatus status, final WebRequest request) {
+        Map<String, Object> resJsonData = new HashMap<String, Object>();
+        resJsonData.put(Constants.RES_KEY_MESSAGE,
+                this.messageAccessor.getResMessage(MsgConstants.NOT_READABLE_JSON_DATA, Constants.RES_KEY_MSG_FAIL));
+
+        return this.handleExceptionInternal(ex, resJsonData, headers, HttpStatus.OK, request);
+    }
+
+    @Override
+    protected ResponseEntity<Object> handleMethodArgumentNotValid(
+            final MethodArgumentNotValidException ex, final HttpHeaders headers,
+            final HttpStatus status, final WebRequest request) {
+        final String bodyOfResponse = "This should be application specific4";
+        return this.handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.OK, request);
+    }
+
+    @Override
+    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers,
+                                                             HttpStatus status, WebRequest request) {
+        LOGGER.error(ex.getMessage(), ex);
+        return super.handleExceptionInternal(ex, body, headers, status, request);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/view/AbstractExcelView.java b/src/main/java/kr/wisestone/owl/web/view/AbstractExcelView.java
new file mode 100644
index 0000000..b9df8bf
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/view/AbstractExcelView.java
@@ -0,0 +1,143 @@
+package kr.wisestone.owl.web.view;
+
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.LocalizedResourceHelper;
+import org.springframework.web.servlet.support.RequestContextUtils;
+import org.springframework.web.servlet.view.AbstractView;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Locale;
+import java.util.Map;
+
+public abstract class AbstractExcelView extends AbstractView {
+
+    /** The content type for an Excel response */
+    private static final String CONTENT_TYPE = "application/vnd.ms-excel";
+
+    /** The extension to look for existing templates */
+    private static final String EXTENSION = ".xls";
+
+
+    private String url;
+
+
+    /**
+     * Default Constructor.
+     * Sets the content type of the view to "application/vnd.ms-excel".
+     */
+    public AbstractExcelView() {
+        setContentType(CONTENT_TYPE);
+    }
+
+    /**
+     * Set the URL of the Excel workbook source, without localization part nor extension.
+     */
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+
+    @Override
+    protected boolean generatesDownloadContent() {
+        return true;
+    }
+
+    /**
+     * Renders the Excel view, given the specified model.
+     */
+    @Override
+    protected final void renderMergedOutputModel(
+            Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
+
+        XSSFWorkbook workbook;
+        if (this.url != null) {
+            workbook = getTemplateSource(this.url, request);
+        }
+        else {
+            workbook = new XSSFWorkbook();
+            logger.debug("Created Excel Workbook from scratch");
+        }
+
+        buildExcelDocument(model, workbook, request, response);
+
+        // Set the content type.
+        response.setContentType(getContentType());
+
+        // Should we set the content length here?
+        // response.setContentLength(workbook.getBytes().length);
+
+        // Flush byte array to servlet output stream.
+        ServletOutputStream out = response.getOutputStream();
+        workbook.write(out);
+        out.flush();
+    }
+
+    /**
+     * Creates the workbook from an existing XLS document.
+     * @param url the URL of the Excel template without localization part nor extension
+     * @param request current HTTP request
+     * @return the HSSFWorkbook
+     * @throws Exception in case of failure
+     */
+    protected XSSFWorkbook getTemplateSource(String url, HttpServletRequest request) throws Exception {
+        LocalizedResourceHelper helper = new LocalizedResourceHelper(getApplicationContext());
+        Locale userLocale = RequestContextUtils.getLocale(request);
+        Resource inputFile = helper.findLocalizedResource(url, EXTENSION, userLocale);
+
+        // Create the Excel document from the source.
+        if (logger.isDebugEnabled()) {
+            logger.debug("Loading Excel workbook from " + inputFile);
+        }
+        return new XSSFWorkbook(inputFile.getInputStream());
+    }
+
+    /**
+     * Subclasses must implement this method to create an Excel HSSFWorkbook document,
+     * given the model.
+     * @param model the model Map
+     * @param workbook the Excel workbook to complete
+     * @param request in case we need locale etc. Shouldn't look at attributes.
+     * @param response in case we need to set cookies. Shouldn't write to it.
+     */
+    protected abstract void buildExcelDocument(
+            Map<String, Object> model, XSSFWorkbook workbook, HttpServletRequest request, HttpServletResponse response)
+            throws Exception;
+
+
+    /**
+     * Convenient method to obtain the cell in the given sheet, row and column.
+     * <p>Creates the row and the cell if they still doesn't already exist.
+     * Thus, the column can be passed as an int, the method making the needed downcasts.
+     * @param sheet a sheet object. The first sheet is usually obtained by workbook.getSheetAt(0)
+     * @param row the row number
+     * @param col the column number
+     * @return the HSSFCell
+     */
+    protected XSSFCell getCell(XSSFSheet sheet, int row, int col) {
+        XSSFRow sheetRow = sheet.getRow(row);
+        if (sheetRow == null) {
+            sheetRow = sheet.createRow(row);
+        }
+        XSSFCell cell = sheetRow.getCell(col);
+        if (cell == null) {
+            cell = sheetRow.createCell(col);
+        }
+        return cell;
+    }
+
+    /**
+     * Convenient method to set a String as text content in a cell.
+     * @param cell the cell in which the text must be put
+     * @param text the text to put in the cell
+     */
+    protected void setText(XSSFCell cell, String text) {
+        cell.setCellType(XSSFCell.CELL_TYPE_STRING);
+        cell.setCellValue(text);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/view/ExcelView.java b/src/main/java/kr/wisestone/owl/web/view/ExcelView.java
new file mode 100644
index 0000000..c44c9df
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/view/ExcelView.java
@@ -0,0 +1,333 @@
+package kr.wisestone.owl.web.view;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import kr.wisestone.owl.annotation.Viewer;
+import kr.wisestone.owl.common.MessageAccessor;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.util.CommonUtil;
+import kr.wisestone.owl.vo.*;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+import org.apache.poi.hssf.util.HSSFColor;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.*;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+@Viewer
+public class ExcelView extends AbstractExcelView {
+    private static final Logger log = LoggerFactory.getLogger(ExcelView.class);
+
+    public static final Integer EXCEL_EXPORT_MAX_COUNT = 65000;
+
+    @Autowired
+    protected MessageAccessor messageAccessor;
+
+    @SuppressWarnings({"unused", "unchecked"})
+    @Override
+    protected void buildExcelDocument(Map<String, Object> model, XSSFWorkbook workbook, HttpServletRequest request,
+                                      HttpServletResponse response) throws Exception {
+
+        XSSFSheet sheet;
+        XSSFRow mergeRow = null;
+        XSSFRow mergeRow2 = null;
+        XSSFRow sheetRow = null;
+        XSSFRow sheetRow2 = null;
+
+        ExportExcelVo excelInfo = (ExportExcelVo) model.get(Constants.EXCEL);
+        String localeFileName = this.findLocaleToText(excelInfo.getFileName());
+
+        response.setContentType("application/vnd.ms-excel; charset=UTF-8");
+        response.setCharacterEncoding("UTF-8");
+        response.setHeader("Content-Disposition", "attachment; filename=" + CommonUtil.getDisposition(localeFileName, CommonUtil.getBrowser(request)) + ".xlsx");
+
+        // 珥� �뜲�씠�꽣�뼇�쓣 戮묒븘 �궦�떎.
+        int totalCount = excelInfo.getDatas().size();
+
+        // �떆�듃 �깮�꽦
+        sheet = workbook.createSheet(excelInfo.getFileName());
+        int currentRowNum = 0;
+
+        // �� �뒪���씪�쓣 �꽕�젙�븳�떎.
+        XSSFCellStyle sheetTitleCellStyle = this.makeSheetTitleStyle(workbook);
+        XSSFCellStyle gridPageCellStyle = this.makeGridPageStyle(workbook);
+        XSSFCellStyle gridHeaderCellStyle = this.makeGridHeaderStyle(workbook);
+
+        Boolean merge = Boolean.FALSE;  //  merge �뿬遺� �솗�씤
+
+        int colIndex = 0;
+        for (ExportExcelAttrVo attrInfo : excelInfo.getAttrInfos()) {
+            if (attrInfo.getMergeCount() == 1) {
+                merge = true;
+                //�� 蹂묓빀
+                if (totalCount >= 2) {
+                    totalCount = totalCount/  2;
+                    int rowNum3 = 3 + totalCount + (totalCount - 2);
+                    for (int rowNum1 = 3; rowNum1 <= rowNum3; rowNum1 += 2) {
+                        int rowNum2 = rowNum1 + 1;
+                        sheet.addMergedRegion(new CellRangeAddress(rowNum1, rowNum2, 0, 0));
+                    }
+                }
+            }
+            sheet.setColumnWidth(colIndex++, (attrInfo.getWidth() + 1) * 2 * 256);
+            attrInfo.setCellStyle(this.makeCellStyle(workbook, this.makeFont(workbook, XSSFFont.BOLDWEIGHT_NORMAL),
+                    attrInfo.getAlign(), XSSFCellStyle.VERTICAL_CENTER, XSSFCellStyle.BORDER_THIN));
+        }
+
+        int size = excelInfo.getDatas().size();
+        //  merge 瑜� �궗�슜�븷 寃쎌슦
+        if (merge) {
+            size = excelInfo.getDatas().size() / 2;
+        }
+
+        this.writeSheetTitle(sheet, sheetTitleCellStyle, currentRowNum++, localeFileName, excelInfo.getAttrInfos()
+                .size() - 1, (short) 1024);
+
+        //  嫄댁닔 �몴�떆 �젣�뼱
+        if (!excelInfo.getHideCount()) {
+            this.writeGridPage(sheet, gridPageCellStyle, currentRowNum++, size, excelInfo
+                    .getAttrInfos().size() - 1);
+        }
+
+        this.writeGridHeader(sheet, gridHeaderCellStyle, currentRowNum++, excelInfo.getAttrInfos());
+
+        for (int index = 0; index < excelInfo.getDatas().size(); index++) {
+            if (EXCEL_EXPORT_MAX_COUNT <= index) {
+                break;
+            }
+
+            Object data = excelInfo.getDatas().get(index);
+            XSSFRow row = sheet.createRow(currentRowNum++);
+
+            int currentColNum = 0;
+            for (ExportExcelAttrVo attrInfo : excelInfo.getAttrInfos()) {
+                this.writeData(workbook, row, currentColNum++, data, attrInfo);
+            }
+        }
+
+        ServletOutputStream out = null;
+        try {
+            out = response.getOutputStream();
+            workbook.write(out);
+        } catch (Exception e) {
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+
+    }
+
+    private XSSFCellStyle makeSheetTitleStyle(XSSFWorkbook workbook) {
+        XSSFFont font = this.makeFont(workbook, XSSFFont.BOLDWEIGHT_BOLD, (short) 20);
+        XSSFCellStyle cellStyle = this.makeCellStyle(workbook, font, XSSFCellStyle.ALIGN_CENTER,
+                XSSFCellStyle.VERTICAL_CENTER, XSSFCellStyle.BORDER_NONE);
+
+        return cellStyle;
+    }
+
+    private XSSFCellStyle makeGridPageStyle(XSSFWorkbook workbook) {
+        XSSFFont font = this.makeFont(workbook, XSSFFont.BOLDWEIGHT_BOLD);
+        XSSFCellStyle cellStyle = this.makeCellStyle(workbook, font, XSSFCellStyle.ALIGN_RIGHT,
+                XSSFCellStyle.VERTICAL_CENTER, XSSFCellStyle.BORDER_NONE);
+
+        return cellStyle;
+    }
+
+    private XSSFCellStyle makeGridHeaderStyle(XSSFWorkbook workbook) {
+        XSSFFont font = this.makeFont(workbook, XSSFFont.BOLDWEIGHT_BOLD);
+        XSSFCellStyle cellStyle = this.makeCellStyle(workbook, font, XSSFCellStyle.ALIGN_CENTER,
+                XSSFCellStyle.VERTICAL_CENTER, XSSFCellStyle.BORDER_THIN);
+
+        // ���뿉 �깋源� 梨꾩슦湲�
+        cellStyle.setFillForegroundColor(new HSSFColor.GREY_25_PERCENT().getIndex());
+        cellStyle.setFillPattern(XSSFCellStyle.SOLID_FOREGROUND);
+
+        return cellStyle;
+    }
+
+    private XSSFCellStyle makeCellStyle(XSSFWorkbook workbook, XSSFFont font, short hAlign, short vAlign, short border) {
+        XSSFCellStyle cellStyle = workbook.createCellStyle();
+
+        cellStyle.setFont(font);
+        cellStyle.setAlignment(hAlign);
+        cellStyle.setVerticalAlignment(vAlign);
+        cellStyle.setBorderTop(border);
+        cellStyle.setBorderLeft(border);
+        cellStyle.setBorderBottom(border);
+        cellStyle.setBorderRight(border);
+        cellStyle.setWrapText(true);
+
+        return cellStyle;
+    }
+
+    private XSSFFont makeFont(XSSFWorkbook workbook) {
+        XSSFFont font = workbook.createFont();
+        font.setFontName("留묒� 怨좊뵓");
+        font.setBoldweight(XSSFFont.BOLDWEIGHT_NORMAL);
+        font.setColor(HSSFColor.BLACK.index);
+
+        return font;
+    }
+
+    private XSSFFont makeFont(XSSFWorkbook workbook, short bold) {
+        XSSFFont font = workbook.createFont();
+        font.setFontName("留묒� 怨좊뵓");
+        font.setBoldweight(bold);
+        font.setColor(HSSFColor.BLACK.index);
+
+        return font;
+    }
+
+    private XSSFFont makeFont(XSSFWorkbook workbook, short bold, short size) {
+        XSSFFont font = workbook.createFont();
+        font.setFontName("留묒� 怨좊뵓");
+        font.setBoldweight(bold);
+        font.setColor(HSSFColor.BLACK.index);
+        font.setFontHeightInPoints(size);
+
+        return font;
+    }
+
+    @SuppressWarnings("unchecked")
+    private void writeData(XSSFWorkbook workbook, XSSFRow row, int colnum, Object data, ExportExcelAttrVo attrInfo) {
+        XSSFCell cell = row.createCell(colnum);
+        cell.setCellStyle(attrInfo.getCellStyle());
+
+        String cellValue = "";
+
+        if (this.getAttrValue(data, attrInfo.getName()) != null) {
+            if (attrInfo.getI18Prefix() != null) {
+                if (this.getAttrValue(data, attrInfo.getName()) instanceof List) {
+                    StringBuffer sb = new StringBuffer("");
+                    List<String> values = (List<String>) this.getAttrValue(data, attrInfo.getName());
+                    int idx = 0;
+                    for (String value : values) {
+                        if (idx != 0) {
+                            sb.append("\n");
+                        }
+                        sb.append(this.messageAccessor.message(attrInfo.getI18Prefix() + value));
+                        idx++;
+                    }
+                    cellValue = sb.toString();
+                }
+                else {
+                    cellValue = this.messageAccessor.message(attrInfo.getI18Prefix() + this.getAttrValue(data, attrInfo.getName()).toString());
+                }
+            }
+            else {
+                if (this.getAttrValue(data, attrInfo.getName()) instanceof List) {
+                    StringBuffer sb = new StringBuffer("");
+                    List values = (List) this.getAttrValue(data, attrInfo.getName());
+                    int idx = 0;
+                    for (Object value : values) {
+                        if (idx != 0) {
+                            sb.append("\n");
+                        }
+
+                        if (value instanceof BaseVo) {
+                            sb.append(this.getAttrValue(value, attrInfo.getVoAttrName()));
+                        }
+                        else {
+                            sb.append(value);
+                        }
+                        idx++;
+                    }
+                    cellValue = sb.toString();
+                }
+                else {
+                    cellValue = this.getAttrValue(data, attrInfo.getName()).toString();
+                }
+            }
+        }
+
+        if (attrInfo.getReplaceWithNewlineYn()) {
+            cellValue = CommonUtil.replaceBrWithNewline(cellValue);
+        }
+
+        cell.setCellValue(cellValue);
+    }
+
+    private void writeGridHeader(XSSFSheet sheet, XSSFCellStyle cellStyle, int rownum, List<ExportExcelAttrVo> attrInfos) {
+        XSSFRow row = sheet.createRow(rownum);
+
+        int colnum = 0;
+
+        for (ExportExcelAttrVo attrInfo : attrInfos) {
+            XSSFCell cell = row.createCell(colnum++);
+            cell.setCellStyle(cellStyle);
+            cell.setCellValue(attrInfo.getDesc());
+        }
+
+    }
+
+    private void writeSheetTitle(XSSFSheet sheet, XSSFCellStyle cellStyle, int rownum, String title, int mergeCount,
+                                 short height) {
+        XSSFRow row = sheet.createRow(rownum);
+        row.setHeight(height);
+
+        XSSFCell cell = row.createCell(0);
+        cell.setCellStyle(cellStyle);
+        cell.setCellValue(title);
+
+        sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, 0, mergeCount));
+    }
+
+    private void writeGridPage(XSSFSheet sheet, XSSFCellStyle cellStyle, int rownum, int size, int mergeCount) {
+        XSSFRow row = sheet.createRow(rownum);
+
+        XSSFCell cell = row.createCell(0);
+        cell.setCellStyle(cellStyle);
+        cell.setCellValue(size + this.messageAccessor.message("common.few"));   //  嫄댁닔
+
+        sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, 0, mergeCount));
+    }
+
+    /**
+     * 珥� 嫄댁닔瑜� �떎援��뼱 硫붿꽭吏�濡� �쟾�솚�븳�떎.
+     *
+     * @param string
+     */
+    private String findLocaleToText(String string) {
+        MessageVo message = this.messageAccessor.getMessage(string);
+        return message.getMessage();
+    }
+
+    /**
+     * �삤釉뚯젥�듃濡� 遺��꽣 attr �냽�꽦�쓽 媛믪쓣 諛섑솚�븳�떎.
+     *
+     * @param data
+     * @param attr
+     * @return
+     * @throws IllegalAccessException
+     * @throws InvocationTargetException
+     */
+    private Object getAttrValue(Object data, String attr) {
+        Object value = null;
+        if (data instanceof Map) {
+            value = ((Map) data).get(attr);
+        }
+        else {
+            PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(data.getClass(), attr);
+            Method method = descriptor.getReadMethod();
+
+            try {
+                value = method.invoke(data, null);
+            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+                value = "";
+            }
+        }
+
+        return value;
+    }
+
+}
diff --git a/src/main/java/kr/wisestone/owl/web/view/FileDownloadView.java b/src/main/java/kr/wisestone/owl/web/view/FileDownloadView.java
new file mode 100644
index 0000000..e6a3c2f
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/view/FileDownloadView.java
@@ -0,0 +1,66 @@
+package kr.wisestone.owl.web.view;
+
+
+import kr.wisestone.owl.annotation.Viewer;
+import kr.wisestone.owl.vo.AttachedFileVo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.web.servlet.view.AbstractView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URLEncoder;
+import java.util.Map;
+
+@Viewer
+public class FileDownloadView extends AbstractView {
+    private static final Logger LOGGER = LoggerFactory.getLogger(FileDownloadView.class);
+
+    public FileDownloadView() {
+        super.setContentType("application/octet-stream");
+    }
+
+    @Override
+    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
+        AttachedFileVo attachedFileVo = (AttachedFileVo) model.get("fileDownloadTarget");
+        response.setContentType(super.getContentType());
+        response.setContentLength(attachedFileVo.getSize().intValue());
+        response.setHeader("Content-Transfer-Encoding", "binary");
+        String userAgent = request.getHeader("User-Agent");
+
+        // attachment; 媛� 遺숈쑝硫� IE�쓽 寃쎌슦 臾댁“嫄� �떎�슫濡쒕뱶李쎌씠 �쑍�떎. �긽�솴�뿉 �뵲�씪 �뜥�빞�븳�떎.
+        if (userAgent != null && userAgent.indexOf("MSIE 5.5") > -1) {
+            // MS IE 5.5 �씠�븯
+            response.setHeader("Content-Disposition", String.format("filename=%s;", URLEncoder.encode(attachedFileVo.getName(), "UTF-8")));
+        } else if ((userAgent != null && userAgent.indexOf("MSIE") > -1) || userAgent.indexOf("Trident") > -1) {
+            // MS IE (蹂댄넻�� 6.x �씠�긽 媛��젙)
+            response.setHeader("Content-Disposition", String.format("attachment; filename=%s;", URLEncoder.encode(attachedFileVo.getName(), "UTF-8")));
+        } else {
+            // 紐⑥쭏�씪, �삤�럹�씪, �겕濡� �벑
+            response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\";", new String(attachedFileVo.getName().getBytes("UTF-8"), "latin1")));
+        }
+
+        OutputStream outputStream = null;
+        InputStream inputStream = null;
+
+        try {
+            outputStream = response.getOutputStream();
+            inputStream = new ByteArrayInputStream(attachedFileVo.getBytes());
+            FileCopyUtils.copy(inputStream, outputStream);
+        } catch (IOException ioe) {
+            LOGGER.error("�떎�슫濡쒕뱶 �뙆�씪 �젣�뼱 以� �삤瑜섍� 諛쒖깮�뻽�뒿�땲�떎.", ioe);
+
+            throw ioe;
+        } finally {
+            if (inputStream != null)
+                inputStream.close();
+
+            if (outputStream != null) {
+                outputStream.flush();
+                outputStream.close();
+            }
+        }
+    }
+}
diff --git a/src/main/resources/META-INF/orm.xml b/src/main/resources/META-INF/orm.xml
new file mode 100644
index 0000000..0fb4ead
--- /dev/null
+++ b/src/main/resources/META-INF/orm.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
+                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm.xsd"
+                 version="2.1">
+
+    <!-- Permission -->
+    <named-query name="Permission.findByUserId">
+        <query>
+            SELECT p
+            FROM Permission p
+            INNER JOIN p.systemRolePermissions srp
+            INNER JOIN srp.systemRole sr
+            INNER JOIN sr.systemRoleUsers sru
+            INNER JOIN sru.user u
+            WHERE u.id = ?1
+        </query>
+    </named-query>
+
+    <named-query name="User.findByWorkspaceIdAndManagerYn">
+        <query>
+            SELECT u
+            FROM User u
+            INNER JOIN u.userWorkspaces uw
+            INNER JOIN uw.workspace w
+            WHERE w.id = ?1
+            AND uw.managerYn = ?2
+        </query>
+    </named-query>
+
+    <named-query name="User.findJoinDay">
+        <query>
+            SELECT u
+            FROM User u
+            WHERE u.registerDate >= ?1
+            AND u.registerDate &lt;= ?2
+            AND u.status = '01'
+        </query>
+    </named-query>
+
+    <named-query name="User.findAdmin">
+        <query>
+            SELECT u
+            FROM User u
+            WHERE u.permission > 2047
+            AND u.status = '01'
+        </query>
+    </named-query>
+
+    <named-query name="Guide.updateInActivation">
+        <query>
+            UPDATE
+            Guide
+            SET STATUS = 0
+            WHERE id != ?1
+        </query>
+    </named-query>
+
+    <named-query name="Event.updateInActivation">
+        <query>
+            UPDATE
+            Event
+            SET STATUS = 0
+            WHERE id != ?1
+        </query>
+    </named-query>
+
+    <!--    ProjectRole -->
+    <named-query name="ProjectRole.findByUserIdAndProjectId">
+        <query>
+            SELECT pr
+            FROM ProjectRole pr
+            INNER JOIN pr.project p
+            INNER JOIN pr.projectRoleUsers pru
+            INNER JOIN pru.user u
+            WHERE u.id = ?1
+            AND p.id = ?2
+            GROUP BY u.id
+        </query>
+    </named-query>
+
+    <!--    IssueComment -->
+    <named-query name="IssueComment.findByIssueId">
+        <query>
+            SELECT new kr.wisestone.owl.vo.IssueCommentVo(
+            ic.id
+            , ic.description
+            , ic.registerId
+            , u.name
+            , u.profile
+            , ic.registerDate
+            )
+            FROM Issue i
+            INNER JOIN i.issueComments  ic, User u
+            WHERE i.id = ?1
+            AND u.id = ic.registerId
+            ORDER BY ic.id DESC
+        </query>
+    </named-query>
+
+    <!--    IssueHistory    -->
+    <named-query name="IssueHistory.findByIssueId">
+        <query>
+            SELECT new kr.wisestone.owl.vo.IssueHistoryVo(
+            ih.id
+            , ih.description
+            , ih.registerId
+            , u.name
+            , u.profile
+            , ih.registerDate
+            , ih.issueHistoryType
+            )
+            FROM Issue i
+            INNER JOIN i.issueHistory ih, User u
+            WHERE i.id = ?1
+            AND u.id = ih.registerId
+            ORDER BY ih.id DESC
+        </query>
+    </named-query>
+
+    <!--    Workspace    -->
+    <named-query name="Workspace.findSubscribeImmediateExpireDate">
+        <query>
+            SELECT w
+            FROM Workspace w
+            LEFT OUTER JOIN w.payment p
+            WHERE p.id is NOT NULL
+            AND w.expireDate >= ?1
+            AND w.expireDate &lt;= ?2
+        </query>
+    </named-query>
+
+    <named-query name="Workspace.findExpireDate">
+        <query>
+            SELECT w
+            FROM Workspace w
+            LEFT OUTER JOIN w.payment p
+            WHERE w.expireDate >= ?1
+            AND w.expireDate &lt;= ?2
+            AND p.id is NULL
+        </query>
+    </named-query>
+
+    <named-query name="Workspace.findByUserId">
+        <query>
+            SELECT w
+            FROM Workspace w
+            INNER JOIN w.userWorkspaces uw
+            INNER JOIN uw.user u
+            WHERE u.id = ?1 AND uw.useYn = true
+        </query>
+    </named-query>
+
+    <!--    UserWorkspace  -->
+    <named-query name="UserWorkspace.maxDisablePosition">
+        <query>
+            SELECT MAX(uw.disablePosition + 1)
+            FROM Workspace w
+            INNER JOIN w.userWorkspaces uw
+            WHERE w.id = ?1
+        </query>
+    </named-query>
+
+    <!-- WorkflowTransition -->
+    <named-query name="WorkflowTransition.findBySourceIssueStatusIdAndWorkflowId">
+        <query>
+            SELECT new kr.wisestone.owl.vo.WorkflowTransitionVo(
+            wt.id
+            , sis.id
+            , sis.name
+            , tis.id
+            , tis.name
+            , wt.correctX
+            , wt.correctY
+            , wt.direct
+            )
+            FROM WorkflowTransition wt
+            INNER JOIN wt.workflow w
+            INNER JOIN wt.sourceIssueStatus sis
+            INNER JOIN wt.targetIssueStatus tis
+            WHERE sis.id = ?1 AND w.id = ?2
+        </query>
+    </named-query>
+
+
+</entity-mappings>
+
+
+
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
new file mode 100644
index 0000000..e0dc65b
--- /dev/null
+++ b/src/main/resources/log4j2.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="warn" name="owl-its" monitorInterval="600">
+    <Properties>
+        <Property name="LOG_FORMAT">%d{yyyy-MM-dd HH:mm:ss} [%level] - %msg%n</Property>
+        <Property name="BASE_DIR">/owl-enterprise-logs</Property>
+    </Properties>
+
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT" follow="true">
+            <PatternLayout pattern="${LOG_FORMAT}"/>
+        </Console>
+        <RollingFile name="File"
+                      fileName="${BASE_DIR}/owl_enterprise.log"
+                      filePattern="${BASE_DIR}/owl_enterprise.%d{yyyy-MM-dd}.%i.log.zip">
+             <PatternLayout pattern="${LOG_FORMAT}"/>
+             <Policies>
+                 <SizeBasedTriggeringPolicy size="10MB"/>
+             </Policies>
+             <DefaultRolloverStrategy max="1000">
+                 <Delete basePath="${BASE_DIR}">
+                     <IfFileName glob="*.log" />
+                     <IfLastModified age="30h" />
+                 </Delete>
+             </DefaultRolloverStrategy>
+         </RollingFile>
+    </Appenders>
+
+    <Loggers>
+        <!-- Application Loggers -->
+        <Logger name="kr.wisestonw.owl" level="warn" additivity="false">
+            <AppenderRef ref="Console"/>
+            <AppenderRef ref="File"/>
+        </Logger>
+
+        <!-- 3rd party Loggers -->
+        <Logger name="org.springframework" level="warn" additivity="false">
+            <AppenderRef ref="Console"/>
+            <AppenderRef ref="File" />
+        </Logger>
+
+        <!-- Application DB SQL Loggers -->
+        <Logger name="kr.wisestone.owl.mapper" level="debug" additivity="false">
+            <AppenderRef ref="Console"/>
+            <AppenderRef ref="File"/>
+        </Logger>
+
+        <!-- Application Service Loggers -->
+        <Logger name="kr.wisestone.owl.service" level="info" additivity="false">
+            <AppenderRef ref="Console"/>
+            <AppenderRef ref="File"/>
+        </Logger>
+
+        <!-- Root Loggers -->
+        <Root level="warn">
+            <AppenderRef ref="Console"/>
+            <AppenderRef ref="File"/>
+        </Root>
+    </Loggers>
+</Configuration>
+
+<!--
+1. trace - �뵒踰꾧렇 �젅踰⑥씠 �꼫臾� 愿묐쾾�쐞�븳寃껋쓣 �빐寃고븯湲� �쐞�빐 醫��뜑 �긽�꽭�븳 �긽�깭瑜� �굹���깂
+2. debug - 媛쒕컻�떆 �뵒踰꾧렇 �슜�룄濡� �궗�슜�븳 硫붿꽭吏�瑜� �굹���깂
+3. info - 濡쒓렇�씤, �긽�깭 蹂�寃쎄낵 媛숈� �젙蹂댁꽦 硫붿떆吏�瑜� �굹���깂
+4. warn - 泥섎━ 媛��뒫�븳 臾몄젣�씠吏�留�, �뼢�썑 �떆�뒪�뀥 �뿉�윭�쓽 �썝�씤�씠 �맆 �닔 �엳�뒗 寃쎄퀬�꽦 硫붿꽭吏�瑜� �굹���깂
+5. error - �슂泥��쓣 泥섎━�븯�뒗 以� 臾몄젣媛� 諛쒖깮�븳 �긽�깭瑜� �굹���깂
+6. fatal - �븘二� �떖媛곹븳 �뿉�윭媛� 諛쒖깮�븳 �긽�깭瑜� �굹���깂
+
+* �긽�쐞 �꽕�젙�씠 �븯�쐞 �쟾泥대�� 媛뽯뒗�떎. trace �꽕�젙�떆 2~6踰� 濡쒓렇(debug, info, warn, error, fatal) �쟾泥닿� 肄섏넄怨� �뙆�씪濡� �옉�꽦�맂�떎.
+-->
diff --git a/src/main/resources/mails/issueAddEmail.html b/src/main/resources/mails/issueAddEmail.html
new file mode 100644
index 0000000..74ddd1c
--- /dev/null
+++ b/src/main/resources/mails/issueAddEmail.html
@@ -0,0 +1,161 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h3><span th:utext="${#messages.msg('issue.add.occurrence')}">諛쒖깮 �떆媛�</span> : <span th:utext="${eventOccurDate}"></span></h3>
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('issue.add.registerIssue')}">
+                OWL ITS �씠�뒋媛� �벑濡앸릺�뿀�뒿�땲�떎.
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            (<span style="color:#4B72FA;font-weight:bold;" th:utext="${projectKey}"></span>-<span style="color:#4B72FA;font-weight:bold;" th:utext="${issueNumber}"></span>)<span style="color:#000;" th:utext="${#messages.msg('issue.add.new')}">�깉濡쒖슫 �씠�뒋媛� �깮�꽦�릺�뿀�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.issueName')}">�씠�뒋 紐�</span>
+                        </div>
+                        <div style="font-weight:bold">
+                            <a href="" style="color:#4B72FA; text-decoration:underline;" target="_blank" th:utext="${title}"></a>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.content')}">�궡�슜</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${description}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width:50%">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.issueType')}">�씠�뒋 �쑀�삎</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${issueTypeName}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.issueStatus')}">�씠�뒋 �긽�깭</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${issueStatusName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width:50%">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.assignee')}">�떞�떦�옄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${assignees}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.schedule')}">�씪�젙</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${period}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width:50%">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.priority')}">以묒슂�룄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${severityName}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.severity')}">�슦�꽑�닚�쐞</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${priorityName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.project')}">�봽濡쒖젥�듃</span>
+                        </div>
+                        <div style="font-weight:bold;">
+                            <a href="" style="color:#4B72FA; text-decoration:underline;" target="_blank" th:utext="${projectName}"></a>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.projectKey')}">�봽濡쒖젥�듃 �궎</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectKey}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;border-right: none;" colspan="2">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.register')}">�벑濡앹옄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${register}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;border-right: none;" colspan="2">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.customField')}">�궗�슜�옄 �젙�쓽 �븘�뱶</span>
+                        </div>
+                        <div style="font-weight:bold;">
+                            <table>
+                                <th:block th:each="customField : ${customFields}">
+                                    <tr>
+                                        <td th:text="${customField.name}"></td>
+                                        <td th:text="${customField.useValue}"></td>
+                                    </tr>
+                                </th:block>
+                            </table>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;border-right: none;" colspan="2">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.attachedFile')}">泥⑤��뙆�씪</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${attachedFiles}"></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+          <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/resources/mails/issueRemoveEmail.html b/src/main/resources/mails/issueRemoveEmail.html
new file mode 100644
index 0000000..81db692
--- /dev/null
+++ b/src/main/resources/mails/issueRemoveEmail.html
@@ -0,0 +1,161 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h3><span th:utext="${#messages.msg('issue.add.occurrence')}">諛쒖깮 �떆媛�</span> : <span th:utext="${eventOccurDate}"></span></h3>
+            <h1 style="margin-top: 0px;color:#030962;">
+                <span th:utext="${#messages.msg('issue.add.deleteIssue')}">OWL ITS �씠�뒋媛� �궘�젣�릺�뿀�뒿�땲�떎.</span>
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            (<span style="color:#4B72FA;font-weight:bold;" th:utext="${projectKey}"></span>-<span style="color:#4B72FA;font-weight:bold;" th:utext="${issueNumber}"></span>)<span style="color:#000;" th:utext="${#messages.msg('issue.add.delete')}">�깉濡쒖슫 �씠�뒋媛� �깮�꽦�릺�뿀�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.issueName')}">�씠�뒋 紐�</span>
+                        </div>
+                        <div style="font-weight:bold">
+                            <a href="" style="color:#4B72FA; text-decoration:underline;" target="_blank" th:utext="${title}"></a>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.content')}">�궡�슜</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${description}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width:50%">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.issueType')}">�씠�뒋 �쑀�삎</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${issueTypeName}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.issueStatus')}">�씠�뒋 �긽�깭</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${issueStatusName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width:50%">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.assignee')}">�떞�떦�옄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${assignees}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.schedule')}">�씪�젙</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${period}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width:50%">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.priority')}">以묒슂�룄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${severityName}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.severity')}">�슦�꽑�닚�쐞</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${priorityName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.project')}">�봽濡쒖젥�듃</span>
+                        </div>
+                        <div style="font-weight:bold;">
+                            <a href="" style="color:#4B72FA; text-decoration:underline;" target="_blank" th:utext="${projectName}"></a>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.projectKey')}">�봽濡쒖젥�듃 �궎</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectKey}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;border-right: none;" colspan="2">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.register')}">�벑濡앹옄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${register}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;border-right: none;" colspan="2">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.customField')}">�궗�슜�옄 �젙�쓽 �븘�뱶</span>
+                        </div>
+                        <div style="font-weight:bold;">
+                            <table>
+                                <th:block th:each="customField : ${customFields}">
+                                    <tr>
+                                        <td th:text="${customField.name}"></td>
+                                        <td th:text="${customField.useValue}"></td>
+                                    </tr>
+                                </th:block>
+                            </table>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;border-right: none;" colspan="2">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.attachedFile')}">泥⑤��뙆�씪</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${attachedFiles}"></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+          <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
diff --git a/src/main/resources/mails/issueSendEmail.html b/src/main/resources/mails/issueSendEmail.html
new file mode 100644
index 0000000..c799117
--- /dev/null
+++ b/src/main/resources/mails/issueSendEmail.html
@@ -0,0 +1,161 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="" th:href="${url}" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h1 style="margin-top: 0px;color:#030962;">
+                �씠�뒋瑜� �솗�씤�빐二쇱꽭�슂.
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            (<span style="color:#4B72FA;font-weight:bold;" th:utext="${projectKey}"></span>-<span style="color:#4B72FA;font-weight:bold;" th:utext="${issueNumber}"></span>)
+            <span style="color:#000;" th:utext="${toUser}"></span>�떂�씠 �씠�뒋 �솗�씤�쓣 �슂泥��뻽�뒿�땲�떎.
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.issueName')}">�씠�뒋 紐�</span>
+                        </div>
+                        <div style="font-weight:bold">
+                            <a href="" th:href="${issueLink}" style="color:#4B72FA; text-decoration:underline;" target="_blank" th:utext="${title}"></a>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.content')}">�궡�슜</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${description}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width:50%">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.issueType')}">�씠�뒋 �쑀�삎</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${issueTypeName}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.issueStatus')}">�씠�뒋 �긽�깭</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${issueStatusName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width:50%">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.assignee')}">�떞�떦�옄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${assignees}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.schedule')}">�씪�젙</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${period}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width:50%">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.priority')}">以묒슂�룄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${severityName}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.severity')}">�슦�꽑�닚�쐞</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${priorityName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.project')}">�봽濡쒖젥�듃</span>
+                        </div>
+                        <div style="font-weight:bold;">
+                            <a href="" th:href="${projectLink}" style="color:#4B72FA; text-decoration:underline;" target="_blank" th:utext="${projectName}"></a>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.projectKey')}">�봽濡쒖젥�듃 �궎</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectKey}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;border-right: none;" colspan="2">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.register')}">�벑濡앹옄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${register}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;border-right: none;" colspan="2">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.customField')}">�궗�슜�옄 �젙�쓽 �븘�뱶</span>
+                        </div>
+                        <div style="font-weight:bold;">
+                            <table>
+                                <th:block th:each="customField : ${customFields}">
+                                    <tr>
+                                        <td th:text="${customField.name}"></td>
+                                        <td th:text="${customField.useValue}"></td>
+                                    </tr>
+                                </th:block>
+                            </table>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;border-right: none;" colspan="2">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('issue.add.attachedFile')}">泥⑤��뙆�씪</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${attachedFiles}"></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="" th:href="${url}" target="_blank" style="text-decoration: underline; color: #4B72FA;" th:utext="${url}">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+            <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/resources/mails/projectDefaultExcludeAndManagerIncludeEmail.html b/src/main/resources/mails/projectDefaultExcludeAndManagerIncludeEmail.html
new file mode 100644
index 0000000..be96f02
--- /dev/null
+++ b/src/main/resources/mails/projectDefaultExcludeAndManagerIncludeEmail.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h3><span th:utext="${#messages.msg('issue.add.occurrence')}">諛쒖깮 �떆媛�</span> : <span th:utext="${eventOccurDate}"></span></h3>
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('project.default.project.manager.change')}">
+                �씪諛� �궗�슜�옄�뿉�꽌 �봽濡쒖젥�듃 愿�由ъ옄濡� 李몄뿬 �뿭�븷�씠 蹂�寃쎈릺�뿀�뒿�땲�떎.
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            [<span style="color:#4B72FA;font-weight:bold;" th:utext="${workspaceName}"> </span>] [<span
+                style="color:#4B72FA;font-weight:bold;" th:utext="${projectName}"></span>] <span style="color:#000;" th:utext="${#messages.msg('project.default.project.user.change')}">�씪諛� �궗�슜�옄�뿉�꽌 �봽濡쒖젥�듃 愿�由ъ옄濡� 李몄뿬 �뿭�븷�씠 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.workspaceName')}">�뾽臾� 怨듦컙 紐�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${workspaceName}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.date')}">�씪�떆</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${registerDate}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.projectName')}">�봽濡쒖젥�듃 紐�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.projectAdmin')}">�봽濡쒖젥�듃 愿�由ъ옄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectManagerName}"></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+          <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
diff --git a/src/main/resources/mails/projectDefaultExcludeEmail.html b/src/main/resources/mails/projectDefaultExcludeEmail.html
new file mode 100644
index 0000000..7637f25
--- /dev/null
+++ b/src/main/resources/mails/projectDefaultExcludeEmail.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h3><span th:utext="${#messages.msg('issue.add.occurrence')}">諛쒖깮 �떆媛�</span> : <span th:utext="${eventOccurDate}"></span></h3>
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('project.default.project.delete')}">
+                �봽濡쒖젥�듃�뿉�꽌 �젣�쇅 �릺�뿀�뒿�땲�떎.
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            [<span style="color:#4B72FA;font-weight:bold;" th:utext="${workspaceName}"> </span>] [<span
+                style="color:#4B72FA;font-weight:bold;" th:utext="${projectName}"></span>]
+            <span style="color:#000;" th:utext="${#messages.msg('project.default.project.manager')}">�봽濡쒖젥�듃�뿉�꽌 �젣�쇅 �릺�뿀�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.workspaceName')}">�뾽臾� 怨듦컙 紐�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${workspaceName}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.date')}">�씪�떆</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${registerDate}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.projectName')}">�봽濡쒖젥�듃 紐�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.projectAdmin')}">�봽濡쒖젥�듃 愿�由ъ옄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectManagerName}"></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+          <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+
+</body>
+</html>
diff --git a/src/main/resources/mails/projectDefaultIncludeEmail.html b/src/main/resources/mails/projectDefaultIncludeEmail.html
new file mode 100644
index 0000000..f402520
--- /dev/null
+++ b/src/main/resources/mails/projectDefaultIncludeEmail.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h3><span th:utext="${#messages.msg('issue.add.occurrence')}">諛쒖깮 �떆媛�</span> : <span th:utext="${eventOccurDate}"></span></h3>
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('project.default.project.join')}">
+                �봽濡쒖젥�듃�뿉 李몄뿬 �릺�뿀�뒿�땲�떎.
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            [<span style="color:#4B72FA;font-weight:bold;" th:utext="${workspaceName}"> </span>] [<span
+                style="color:#4B72FA;font-weight:bold;" th:utext="${projectName}"></span>]
+            <span style="color:#000;" th:utext="${#messages.msg('project.default.project.manager.join')}">�봽濡쒖젥�듃�뿉 李몄뿬 �릺�뿀�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.workspaceName')}">�뾽臾� 怨듦컙 紐�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${workspaceName}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.date')}">�씪�떆</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${registerDate}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.projectName')}">�봽濡쒖젥�듃 紐�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.projectAdmin')}">�봽濡쒖젥�듃 愿�由ъ옄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectManagerName}"></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+          <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+
+</body>
+</html>
diff --git a/src/main/resources/mails/projectManagerExcludeAndDefaultIncludeEmail.html b/src/main/resources/mails/projectManagerExcludeAndDefaultIncludeEmail.html
new file mode 100644
index 0000000..ca47699
--- /dev/null
+++ b/src/main/resources/mails/projectManagerExcludeAndDefaultIncludeEmail.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="http://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h3><span th:utext="${#messages.msg('issue.add.occurrence')}">諛쒖깮 �떆媛�</span> : <span th:utext="${eventOccurDate}"></span></h3>
+            <h1 style="margin-top: 0px;color:#030962; line-height:1.4;" th:utext="${#messages.msg('project.default.project.manager.user')}">
+                �봽濡쒖젥�듃 愿�由ъ옄�뿉�꽌 �씪諛� �궗�슜�옄濡� 李몄뿬 �뿭�븷�씠 蹂�寃쎈릺�뿀�뒿�땲�떎.
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            [<span style="color:#4B72FA;font-weight:bold;" th:utext="${workspaceName}"> </span>] [<span
+                style="color:#4B72FA;font-weight:bold;" th:utext="${projectName}"></span>] <span style="color:#000;" th:utext="${#messages.msg('project.default.project.manager.user.change')}">�봽濡쒖젥�듃 愿�由ъ옄�뿉�꽌 �씪諛� �궗�슜�옄濡� 李몄뿬 �뿭�븷�씠 蹂�寃쎈릺�뿀�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.workspaceName')}">�뾽臾� 怨듦컙 紐�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${workspaceName}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.date')}">�씪�떆</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${registerDate}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.projectName')}">�봽濡쒖젥�듃 紐�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.projectAdmin')}">�봽濡쒖젥�듃 愿�由ъ옄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectManagerName}"></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+          <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+
+</body>
+</html>
diff --git a/src/main/resources/mails/projectManagerExcludeEmail.html b/src/main/resources/mails/projectManagerExcludeEmail.html
new file mode 100644
index 0000000..c73738d
--- /dev/null
+++ b/src/main/resources/mails/projectManagerExcludeEmail.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="http://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h3><span th:utext="${#messages.msg('issue.add.occurrence')}">諛쒖깮 �떆媛�</span> : <span th:utext="${eventOccurDate}"></span></h3>
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('project.default.project.manager.delete')}">
+                �봽濡쒖젥�듃 愿�由ъ옄�뿉�꽌 �젣�쇅�릺�뿀�뒿�땲�떎.
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            [<span style="color:#4B72FA;font-weight:bold;" th:utext="${workspaceName}"> </span>] [<span
+                style="color:#4B72FA;font-weight:bold;" th:utext="${projectName}"></span>] <span style="color:#000;" th:utext="${#messages.msg('project.default.project.manager.exclude.delete')}">�봽濡쒖젥�듃 愿�由ъ옄�뿉�꽌 �젣�쇅 �릺�뿀�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.workspaceName')}">�뾽臾� 怨듦컙 紐�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${workspaceName}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.date')}">�씪�떆</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${registerDate}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.projectName')}">�봽濡쒖젥�듃 紐�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.projectAdmin')}">�봽濡쒖젥�듃 愿�由ъ옄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectManagerName}"></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+          <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+
+</body>
+</html>
diff --git a/src/main/resources/mails/projectManagerIncludeEmail.html b/src/main/resources/mails/projectManagerIncludeEmail.html
new file mode 100644
index 0000000..85b6aeb
--- /dev/null
+++ b/src/main/resources/mails/projectManagerIncludeEmail.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="http://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h3><span th:utext="${#messages.msg('issue.add.occurrence')}">諛쒖깮 �떆媛�</span> : <span th:utext="${eventOccurDate}"></span></h3>
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('project.default.project.manager.include.manager')}">
+                �봽濡쒖젥�듃 愿�由ъ옄媛� �릺�뿀�뒿�땲�떎.
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            [<span style="color:#4B72FA;font-weight:bold;" th:utext="${workspaceName}"> </span>] [<span
+                style="color:#4B72FA;font-weight:bold;" th:utext="${projectName}"></span>] <span style="color:#000;" th:utext="${#messages.msg('project.include.manager')}">�봽濡쒖젥�듃 愿�由ъ옄媛� �릺�뿀�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.workspaceName')}">�뾽臾� 怨듦컙 紐�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${workspaceName}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.date')}">�씪�떆</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${registerDate}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.projectName')}">�봽濡쒖젥�듃 紐�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('project.default.projectAdmin')}">�봽濡쒖젥�듃 愿�由ъ옄</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${projectManagerName}"></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+          <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+
+</body>
+</html>
diff --git a/src/main/resources/mails/regularPaymentCancelByAccountingManagerEmail.html b/src/main/resources/mails/regularPaymentCancelByAccountingManagerEmail.html
new file mode 100644
index 0000000..86cfbc5
--- /dev/null
+++ b/src/main/resources/mails/regularPaymentCancelByAccountingManagerEmail.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center"
+       style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;"
+       xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS"
+                                                                  src="http://wisestone.kr/owlsolution/logo-dark.png"
+                                                                  style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h1 style="margin-top: 0px;color:#030962;">
+                �궗�슜�옄 �젙湲� 寃곗젣 �빐吏� �븞�궡
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            �떎�쓬 �궗�슜�옄�쓽 �젙湲� 寃곗젣媛� �빐吏��릺�뿀�뒿�땲�떎.
+            <span style="color:#4B72FA;font-weight:bold;" th:utext="${userName}"></span>
+            (<span style="color:#4B72FA;font-weight:bold;" th:utext="${email}"></span>)
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.user')}">�궗�슜�옄 �닔</span>
+                        </div>
+                        <div style="font-weight:bold;"><span th:utext="${maxUser}"></span><span
+                                th:utext="${#messages.msg('regular.payment.person')}">紐�</span></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.workspace')}">�꽌鍮꾩뒪 ���옣 怨듦컙</span>
+                        </div>
+                        <div style="font-weight:bold;"><span th:utext="${storageSize}"></span>G</div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;border-right: 1px solid #e7e7e7; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.service.end')}">�꽌鍮꾩뒪 醫낅즺�씪</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${expireDate}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2"
+                        style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;border-right: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span>�솚遺� 湲덉븸</span>
+                        </div>
+                        <div style="font-weight:bold;">
+                            <span th:utext="${refundPrice}"></span>�썝
+                        </div>
+                    </td>
+                </tr>
+
+                <tr>
+                    <td colspan="2"
+                        style="padding: 20px;  font-size: 13px; text-align: center; border-bottom: 1px solid #e7e7e7;">
+                        <span th:utext="${#messages.msg('regular.payment.service.service.end.date')}">�꽌鍮꾩뒪 醫낅즺�씪 �씠�썑 30�씪�씠 吏��굹硫� �뾽臾� 怨듦컙�쓽 紐⑤뱺 �뜲�씠�꽣媛� �궘�젣 �맗�땲�떎.</span>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a
+                    href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+            <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+
+</body>
+</html>
diff --git a/src/main/resources/mails/regularPaymentCancelEmail.html b/src/main/resources/mails/regularPaymentCancelEmail.html
new file mode 100644
index 0000000..e5f9ed2
--- /dev/null
+++ b/src/main/resources/mails/regularPaymentCancelEmail.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center"
+       style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;"
+       xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS"
+                                                                  src="http://wisestone.kr/owlsolution/logo-dark.png"
+                                                                  style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('regular.payment.termination.guide')}">
+                �젙湲� 寃곗젣 �빐吏� �븞�궡
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            <span style="color:#4B72FA;font-weight:bold;" th:utext="${userName}"></span><span
+                th:utext="${#messages.msg('user.hello')}">�떂, �븞�뀞�븯�꽭�슂.</span><br/>
+            [<span style="color:#4B72FA;font-weight:bold;" th:utext="${workspaceName}"></span>] <span
+                style="color:#000;" th:utext="${#messages.msg('regular.payment.termination.complete')}">�젙湲� 寃곗젣 �빐吏� �떊泥��씠 �셿猷뚮릺�뿀�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.user')}">�궗�슜�옄 �닔</span>
+                        </div>
+                        <div style="font-weight:bold;"><span th:utext="${maxUser}"></span><span
+                                th:utext="${#messages.msg('regular.payment.person')}">紐�</span></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none; width:50%;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.workspace')}">�꽌鍮꾩뒪 ���옣 怨듦컙</span>
+                        </div>
+                        <div style="font-weight:bold;"><span th:utext="${storageSize}"></span>G</div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;border-right: 1px solid #e7e7e7;" colspan="2">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.service.end')}">�꽌鍮꾩뒪 醫낅즺�씪</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${expireDate}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2"
+                        style="padding: 20px;  font-size: 13px; text-align: center; border-bottom: 1px solid #e7e7e7;">
+                        <span th:utext="${#messages.msg('regular.payment.service.service.end.date')}">�꽌鍮꾩뒪 醫낅즺�씪 �씠�썑 30�씪�씠 吏��굹硫� �뾽臾� 怨듦컙�쓽 紐⑤뱺 �뜲�씠�꽣媛� �궘�젣 �맗�땲�떎.</span>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a
+                    href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+          <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+
+</body>
+</html>
diff --git a/src/main/resources/mails/regularPaymentEmail.html b/src/main/resources/mails/regularPaymentEmail.html
new file mode 100644
index 0000000..e1bd9d1
--- /dev/null
+++ b/src/main/resources/mails/regularPaymentEmail.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('regular.payment.information')}">
+                �젙湲� 寃곗젣 �궡�뿭 �븞�궡
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            <span style="color:#4B72FA;font-weight:bold;" th:utext="${userName}"></span><span th:utext="${#messages.msg('user.hello')}">�떂, �븞�뀞�븯�꽭�슂.</span><br/>
+            <span th:utext="${#messages.msg('regular.payment.using.service')}">OWL ITS �꽌鍮꾩뒪瑜� �씠�슜�빐 二쇱뀛�꽌 媛먯궗�빀�땲�떎.</span><br/><br/>
+            <span style="color:#000;" th:utext="${#messages.msg('regular.payment.complete')}">怨좉컼�떂�씠 �떊泥��븯�떊 OWL ITS �젙湲� 寃곗젣媛� �셿猷뚮릺�뿀�뒿�땲�떎. </span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.service.information')}">�꽌鍮꾩뒪 �젙蹂�</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${workspaceName}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7; border-right:1px solid #e7e7e7; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.user')}">�궗�슜�옄 �닔</span>
+                        </div>
+                        <div style="font-weight:bold;"><span th:utext="${buyUser}"></span><span th:utext="${#messages.msg('regular.payment.person')}">紐�</span></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7; ">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.workspace')}">�꽌鍮꾩뒪 ���옣 怨듦컙</span>
+                        </div>
+                        <div style="font-weight:bold;"><span th:utext="${storageSize}"></span>GB</div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7; border-right:1px solid #e7e7e7; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.next.date')}">�떎�쓬 �젙湲� 寃곗젣�씪</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${nextPaymentDay}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.amount')}">寃곗젣 湲덉븸</span>
+                        </div>
+                        <div style="font-weight:bold;"><span th:utext="${price}"></span><span th:utext="${#messages.msg('regular.payment.won')}">�썝</span></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.date')}">寃곗젣 �씪�떆</span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${registerDate}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.service.day')}">�꽌鍮꾩뒪 湲곌컙</span>
+                        </div>
+                        <div style="font-weight:bold;"><span th:utext="${startDate}"></span>~<span th:utext="${expireDate}"></span></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+          <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
diff --git a/src/main/resources/mails/regularPaymentModifyEmail.html b/src/main/resources/mails/regularPaymentModifyEmail.html
new file mode 100644
index 0000000..5b91f8d
--- /dev/null
+++ b/src/main/resources/mails/regularPaymentModifyEmail.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('regular.payment.change.notice')}">
+                �젙湲� 寃곗젣 蹂�寃쎌궗�빆 �븞�궡
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            <span style="color:#4B72FA;font-weight:bold;" th:utext="${userName}"></span>
+            <span th:utext="${#messages.msg('user.hello')}">�떂, �븞�뀞�븯�꽭�슂.</span>
+            <br/>
+            [<span style="color:#4B72FA;font-weight:bold;" th:utext="${workspaceName}"> </span>]
+            <span style="color:#000;" th:utext="${#messages.msg('regular.payment.change.payment')}">寃곗젣 蹂�寃� �떊泥��씠 �셿猷뚮릺�뼱 �젙湲� 寃곗젣 蹂�寃쎌궗�빆�쓣 �븣�젮�뱶由쎈땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;border-top: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.user')}">�궗�슜�옄 �닔</span>
+                        </div>
+                        <div>
+                            <span style="text-decoration:line-through;" th:utext="${beforeMaxUser}"></span>
+                            <span th:utext="${#messages.msg('regular.payment.person')}">紐�</span>
+                            <img alt="OWL ITS" src="http://wisestone.kr/owlsolution/arrow.png" style="width:13px;padding:0px 5px;">
+                            <span style="font-weight:bold" th:utext="${maxUser}"></span>
+                            <span th:utext="${#messages.msg('regular.payment.person')}">紐�</span>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7; ">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.service.charge')}">�꽌鍮꾩뒪 �슂湲�</span>
+                        </div>
+                        <div>
+                            <span style="text-decoration:line-through;" th:utext="${beforePrice}"></span>
+                            <span th:utext="${#messages.msg('regular.payment.won')}">�썝</span>
+                            <img alt="OWL ITS" src="http://wisestone.kr/owlsolution/arrow.png" style="width:13px;padding:0px 5px;"><span style="font-weight:bold" th:utext="${price}"></span>
+                            <span th:utext="${#messages.msg('regular.payment.won')}">�썝</span>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.mean.payment')}">寃곗젣 �닔�떒</span>
+                        </div>
+                        <div style="font-weight:bold;">
+                            <span th:utext="${#messages.msg('regular.payment.credit.card')}">�떊�슜移대뱶</span>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('regular.payment.application.time')}">�쟻�슜�떆湲�</span>
+                        </div>
+                        <div style="font-weight:bold;">
+                            <span th:utext="${nextPaymentDay}"></span>
+                            <span th:utext="${#messages.msg('regular.payment.apply.payment')}">�젙湲� 寃곗젣遺��꽣 �쟻�슜</span>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px;  font-size: 13px; text-align: center; border-bottom: 1px solid #e7e7e7;">
+                        <span th:utext="${#messages.msg('regular.payment.automatically')}">�젙湲� 寃곗젣 �삁�젙�씪�뿉 �옄�룞�쑝濡� �꽌鍮꾩뒪媛� 寃곗젣�릺�뼱 泥�援щ맗�땲�떎. �씠�슜 以묒씤 �꽌鍮꾩뒪�뿉 蹂��룞�궗�빆�씠 �엳�떎硫� �젙湲� 寃곗젣�씪 �쟾�뿉 瑗� 蹂�寃쏀빐 二쇱꽭�슂.</span>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+            <span style="color: #A5A5A5; font-size: 10px;">
+              Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+            </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
diff --git a/src/main/resources/mails/totalStatisticsEmail.html b/src/main/resources/mails/totalStatisticsEmail.html
new file mode 100644
index 0000000..76ac8b1
--- /dev/null
+++ b/src/main/resources/mails/totalStatisticsEmail.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h3 style="margin-top: 0px;color:#030962;" align="center">
+                <span th:utext="${today}"></span>�씪 �떆�뒪�뀥 �쁽�솴 �젙蹂�
+            </h3>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span>�쟾泥� �봽濡쒖젥�듃</span>
+                        </div>
+                        <div style="font-weight:bold">
+                            <span th:utext="${projectCount}"></span> 媛�
+                        </div>
+                    </td>
+                </tr>
+
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span>�쟾泥� �씠�뒋</span>
+                        </div>
+                        <div style="font-weight:bold">
+                            <span th:utext="${issueCount}"></span> 媛�
+                        </div>
+                    </td>
+                </tr>
+
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span>�쟾泥� �궗�슜�옄</span>
+                        </div>
+                        <div style="font-weight:bold">
+                            <span th:utext="${userCount}"></span> 紐�
+                        </div>
+                    </td>
+                </tr>
+
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+            <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
diff --git a/src/main/resources/mails/userJoinStatisticsEmail.html b/src/main/resources/mails/userJoinStatisticsEmail.html
new file mode 100644
index 0000000..ccc297f
--- /dev/null
+++ b/src/main/resources/mails/userJoinStatisticsEmail.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h3 style="margin-top: 0px;color:#030962;" align="center">
+                <span th:utext="${today}"></span>�씪 �궗�슜�옄 媛��엯 �젙蹂�
+            </h3>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span>�삤�뒛 媛��엯�옄</span>
+                        </div>
+                        <div style="font-weight:bold">
+                            <span th:utext="${newUserCount}"></span>紐�
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;border-right: none;" colspan="2">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span>媛��엯�옄 �젙蹂�</span>
+                        </div>
+                        <div style="font-weight:bold;">
+                            <table>
+                                <th:block th:each="joinUser : ${joinUsers}">
+                                    <tr>
+                                        <td style="width:50%;">
+                                            <span th:text="${joinUser.name}"></span>
+                                        </td>
+                                        <td style="width:50%;">
+                                            <span th:text="${joinUser.account}"></span>
+                                        </td>
+                                    </tr>
+                                </th:block>
+                            </table>
+                        </div>
+                    </td>
+                </tr>
+
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none;" colspan="2">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span>�쟾泥� �궗�슜�옄 </span>
+                        </div>
+                        <div style="font-weight:bold;" th:utext="${totalUserCount}"></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+            <span style="color: #A5A5A5; font-size: 10px;">
+            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+          </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/resources/mails/userSearchPasswordEmail.html b/src/main/resources/mails/userSearchPasswordEmail.html
new file mode 100644
index 0000000..d1bc738
--- /dev/null
+++ b/src/main/resources/mails/userSearchPasswordEmail.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('user.password.guidanceTemporaryPassword')}">
+                �엫�떆 鍮꾨�踰덊샇 諛쒓툒 �븞�궡
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            <span style="color:#4B72FA;font-weight:bold;" th:utext="${name}"></span>
+            <span th:utext="${#messages.msg('user.hello')}">�떂, �븞�뀞�븯�꽭�슂.</span><br/>
+            <span th:utext="${#messages.msg('user.password.temporaryPassword')}"></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;border-top: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('user.password.account')}">怨꾩젙</span>
+                        </div>
+                        <div style="font-weight:bold" th:utext="${account}"></div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7; ">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('user.password.temporaryPassword')}">�엫�떆 鍮꾨�踰덊샇媛� 諛쒓툒�릺�뿀�뒿�땲�떎.</span>
+                        </div>
+                        <div style="font-weight: bold; color:#D62525" th:utext="${password}"></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+            <span style="color: #A5A5A5; font-size: 10px;">
+              Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+            </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
diff --git a/src/main/resources/mails/userWithDrawEmail.html b/src/main/resources/mails/userWithDrawEmail.html
new file mode 100644
index 0000000..a4c3812
--- /dev/null
+++ b/src/main/resources/mails/userWithDrawEmail.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('user.withDraw.guide')}">
+                �쉶�썝 �깉�눜 �븞�궡
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            <span style="color:#4B72FA;font-weight:bold;" th:utext="${name}"></span>
+            <span th:utext="${#messages.msg('user.hello')}">�떂, �븞�뀞�븯�꽭�슂.</span><br/>
+            <span th:utext="${#messages.msg('user.withDraw.success')}">�쉶�썝�떂�씠 �슂泥��븯�떊 OWL ITS �쉶�썝 �깉�눜媛� �꽦怨듭쟻�쑝濡� �셿猷뚮릺�뿀�뒿�땲�떎. 洹몃룞�븞 �씠�슜�빐 二쇱뀛�꽌 媛먯궗�뱶由쎈땲�떎. �떎�떆 諛⑸Ц�븯�떎 �닔 �엳�룄濡� �뜑�슧�뜑 �끂�젰�븯寃좎뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+            <span style="color: #A5A5A5; font-size: 10px;">
+              Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+            </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
diff --git a/src/main/resources/mails/workspaceExpireAlarmEmail.html b/src/main/resources/mails/workspaceExpireAlarmEmail.html
new file mode 100644
index 0000000..653b9bf
--- /dev/null
+++ b/src/main/resources/mails/workspaceExpireAlarmEmail.html
@@ -0,0 +1,265 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body xmlns:th="http://www.thymeleaf.org">
+<table align="center" width="600" border="0" cellpadding="0" cellspacing="0" style="background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05); font-family:'Noto Sans Korean', sans-serif, '留묒�怨좊뵓', Malgun Gothic, '�룍��', Dotum, '�굹�닎怨좊뵓', NanumGothic, Helvetica, 'Apple SD Gothic Neo'; width:600px; margin:0 auto; word-break:keep-all;color: #333; font-size: 14px; line-height:1.43;">
+    <tr>
+        <td>
+            <table align="center" border="0" cellpadding="0" cellspacing="0" style="width:100%">
+                <tbody>
+                <tr>
+                    <td colspan="3" style="height:20px"></td>
+                </tr>
+                <tr>
+                    <td style="width:210px"></td>
+                    <td style="width:180px;">
+                        <a href="http://owlsolution.co.kr" target="_blank"><img alt="OWL ITS"
+                                                                                src="http://owlsolution.co.kr/images/mail/logo-dark.png"></a>
+                    </td>
+                    <td style="width:210px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="height:20px"></td>
+                </tr>
+                </tbody>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff;">
+            <img alt="OWL ITS" src="http://owlsolution.co.kr/images/mail/em-top.jpg">
+        </td>
+    </tr>
+    <tr>
+        <td>
+            <table align="center" border="0" cellpadding="0" cellspacing="0" style="width:100%">
+                <tbody>
+                <tr>
+                    <td colspan="3" style="height:20px"></td>
+                </tr>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px;">
+                        <pre th:utext="${#messages.msg('workspace.expiredAlarm.msg1', dayCount)}"><span
+                                style="font-size:24px;font-weight:bold;">OWL ITS �뾽臾� 怨듦컙 �궗�슜 湲곌컙�씠<br/><span
+                                style="color:#ff3c00; font-weight:bold;">D-7</span>�씪 �궓�븯�뒿�땲�떎.</span></pre><br/><br/>
+                        <pre th:utext="${#messages.msg('workspace.expiredAlarm.msg2', name, workspaceName, dayCount)}"><span
+                                style="color:#4B72FA;font-weight:bold">�궗�슜�옄紐�</span>�떂, OWL ITS�쓽 <span
+                                style="color:#4B72FA;font-weight:bold">[�뾽臾닿났媛�] </span> �뾽臾닿났媛� �궗�슜 湲곌컙�씠 <span
+                                style="color:#ff3c00; font-weight:bold;">[D-DAY]</span>�씪 諛뽰뿉
+                            �궓吏� �븡�븯�뼱�슂.<br/></pre>
+                        <span th:utext="${#messages.msg('workspace.expiredAlarm.msg3')}">�젙湲� 寃곗젣瑜� �떊泥��븯�떆硫� OWL ITS�쓽 紐⑤뱺 湲곕뒫�쓣 洹몃�濡� �궗�슜�븯�떎 �닔 �엳�뒿�땲�떎.</span>
+
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="height:40px"></td>
+                </tr>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px;">
+                        <table align="center" border="0" cellpadding="0" cellspacing="0"
+                               style="width:100%;background-color: #F4F4F4;border: 1px solid #e7e7e7">
+                            <tbody>
+                            <tr>
+                                <td colspan="3" style="height:20px"></td>
+                            </tr>
+                            <tr>
+                                <td style="width:39px"></td>
+                                <td style="width:520px;">
+                                    <table style="width:100%;" align="center">
+                                        <tr>
+                                            <td>
+                                                <div th:utext="${#messages.msg('workspace.expiredAlarm.msg4', expireDate)}">
+
+                                                    <div style="color: #9a9a9a; font-size: 12px; font-weight:bold;">
+                                                        �뾽臾닿났媛� 留뚮즺 �삁�젙�씪
+                                                    </div>
+                                                    <div style="font-weight:bold">
+                                                        [�뾽臾닿났媛� 留뚮즺 �삁�젙�씪]
+                                                    </div>
+
+                                                </div>
+                                            </td>
+                                        </tr>
+                                    </table>
+                                </td>
+                                <td style="width:39px"></td>
+                            </tr>
+                            <tr>
+                                <td colspan="3" style="height:20px"></td>
+                            </tr>
+                            </tbody>
+                        </table>
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="height:40px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="text-align:center;font-size:12px;">
+                        <span th:utext="${#messages.msg('workspace.expiredAlarm,msg5')}">吏�湲� OWL ITS �젙湲곌껐�젣瑜� �떊泥��븯�떆怨� �떎�뼇�븳 �씠�뒋 愿�由� 湲곕뒫�쓣 留덉쓬猿� �궗�슜�븯�꽭�슂!</span>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="height:10px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3">
+                        <table>
+                            <tr>
+                                <td style="width:150px"></td>
+                                <td style="width:300px; height:50px; text-align:center; background:#37308d; border-radius:25px">
+                                    <a href="http://www.owlsolution.io" target="_blank"
+                                       style="display:inline-block; width:100%; height:50px; line-height:50px; font-size:15px; font-weight:bold; color:#fff; text-decoration:none"
+                                       th:utext="${#messages.msg('workspace.expiredAlarm.msg6')}">�젙湲� 寃곗젣 �떊泥��븯湲�</a>
+                                </td>
+                                <td style="width:150px"></td>
+                            </tr>
+                        </table>
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="height:40px;"></td>
+    </tr>
+    <tr>
+        <td>
+            <table align="center" border="0" cellpadding="0" cellspacing="0" style="width:100%">
+                <tbody>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px;text-align:center;">
+                        <pre th:utext="${#messages.msg('workspace.expiredAlarm.msg7')}">
+                        <span style="font-size:12px;color: #8D929D;">OWL ITS �궗�슜�뿉 �뼱�젮���씠 �엳�떎硫� <a
+                                href="mailto:supportowl@wisestone.kr"
+                                style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a>濡� �씠硫붿씪�쓣 蹂대궡二쇱꽭�슂.</span>
+                        </pre>
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="height:20px;"></td>
+    </tr>
+    <tr>
+        <td>
+            <table align="center" border="0" cellpadding="0" cellspacing="0"
+                   style="width:100%;background-color: #f1f4fd; ">
+                <tbody>
+                <tr>
+                    <td style="height:20px;"></td>
+                </tr>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px; text-align: center;color: #A5A5A5; font-size: 12px;">
+                   <pre th:utext="${#messages.msg('workspace.expiredAlarm.msg8')}">
+                        蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.<br/>
+                        <span style="color: #A5A5A5; font-size: 11px;">
+                    Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+                        </span>
+                   </pre>
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+                <tr>
+                    <td style="height:20px;"></td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
+
+
+<!--<!DOCTYPE html>-->
+<!--<html>-->
+<!--<head>-->
+<!--    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">-->
+<!--</head>-->
+<!--<body>-->
+<!--<table align="center"-->
+<!--       style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;"-->
+<!--       xmlns:th="http://www.thymeleaf.org">-->
+<!--    <tr>-->
+<!--        <td style="background-color: #fff;">-->
+<!--            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS"-->
+<!--                                                                  src="http://wisestone.kr/owlsolution/logo-dark.png"-->
+<!--                                                                  style="padding: 20px 60px"></a>-->
+<!--        </td>-->
+<!--    </tr>-->
+<!--    <tr>-->
+<!--        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">-->
+<!--            <span th:utext="${#messages.msg('workspace.expiredAlarm.msg1', dayCount)}">-->
+<!--            <h1 style="margin-top: 0px;color:#030962;">-->
+<!--                OWL ITS �뾽臾� 怨듦컙 �궗�슜 湲곌컙�씠 3/7�씪 �궓�븯�뒿�땲�떎.-->
+<!--            </h1>-->
+<!--            </span>-->
+<!--        </td>-->
+<!--    </tr>-->
+<!--    <tr>-->
+<!--        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">-->
+<!--            <pre>-->
+<!--            [<span style="color:#4B72FA;font-weight:bold;" th:utext="${workspaceName}"></span>]-->
+<!--            </pre>-->
+<!--        </td>-->
+<!--    </tr>-->
+<!--    <tr>-->
+<!--        <td style="padding: 0px 70px;font-size: 13px;">-->
+<!--            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">-->
+<!--                <tr>-->
+<!--                    <td colspan="2"-->
+<!--                        style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;border-top: 1px solid #e7e7e7;">-->
+<!--                     -->
+<!--                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">-->
+<!--                            <span th:utext="${#messages.msg('workspace.expiration.date')}">留뚮즺�씪</span>-->
+
+<!--                        </div>-->
+<!--             -->
+<!--                        <div>-->
+<!--                            <div style="font-weight:bold;color:#D62525" th:utext="${expireDate}">-->
+<!--                            </div>-->
+
+<!--                        </div>-->
+<!--                    </td>-->
+<!--                </tr>-->
+<!--                <tr>-->
+<!--                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7; ">-->
+<!--                        <pre>-->
+<!--                        <span th:utext="${#messages.msg('workspace.regular.payment.request')}">�젙湲� 寃곗젣媛� �떊泥��릺�뼱 �엳吏� �븡�� 怨좉컼�떂猿섏꽌�뒗 �젙湲� 寃곗젣瑜� �떊泥��빐二쇱떆湲� 諛붾엻�땲�떎.</span>-->
+<!--                        </pre>-->
+<!--                    </td>-->
+<!--                </tr>-->
+<!--            </table>-->
+<!--        </td>-->
+<!--    </tr>-->
+<!--    <tr>-->
+<!--        <td style="text-align:center;padding-bottom:40px;">-->
+<!--            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>-->
+<!--            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">-->
+<!--                https://owlsolution.io</a><br/><br/>-->
+<!--            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a-->
+<!--                    href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>-->
+<!--        </td>-->
+<!--    </tr>-->
+<!--    <tr>-->
+<!--        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">-->
+<!--            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>-->
+<!--            <span style="color: #A5A5A5; font-size: 10px;">-->
+<!--              Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.-->
+<!--            </span>-->
+<!--        </td>-->
+<!--    </tr>-->
+<!--</table>-->
+<!--</body>-->
+<!--</html>-->
diff --git a/src/main/resources/mails/workspaceExpireEmail.html b/src/main/resources/mails/workspaceExpireEmail.html
new file mode 100644
index 0000000..455595c
--- /dev/null
+++ b/src/main/resources/mails/workspaceExpireEmail.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body xmlns:th="http://www.thymeleaf.org">
+<table align="center" width="600" border="0" cellpadding="0" cellspacing="0"
+       style="background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05); font-family:'Noto Sans Korean', sans-serif, '留묒�怨좊뵓', Malgun Gothic, '�룍��', Dotum, '�굹�닎怨좊뵓', NanumGothic, Helvetica, 'Apple SD Gothic Neo'; width:600px; margin:0 auto; word-break:keep-all;color: #333; font-size: 14px; line-height:1.43;">
+    <tr>
+        <td>
+            <table align="center" border="0" cellpadding="0" cellspacing="0" style="width:100%">
+                <tbody>
+                <tr>
+                    <td colspan="3" style="height:20px"></td>
+                </tr>
+                <tr>
+                    <td style="width:210px"></td>
+                    <td style="width:180px;">
+                        <a href="http://owlsolution.co.kr" target="_blank"><img alt="OWL ITS"
+                                                                                 src="http://owlsolution.co.kr/images/mail/logo-dark.png"></a>
+                    </td>
+                    <td style="width:210px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="height:20px"></td>
+                </tr>
+                </tbody>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff;">
+            <img alt="OWL ITS" src="http://owlsolution.co.kr/images/mail/em-top.jpg">
+        </td>
+    </tr>
+    <tr>
+        <td>
+            <table align="center" border="0" cellpadding="0" cellspacing="0" style="width:100%">
+                <tbody>
+                <tr>
+                    <td colspan="3" style="height:20px"></td>
+                </tr>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px;">
+                        <pre th:utext="${#messages.msg('workspace.expire.msg1')}">
+                            <span style="font-size:24px;font-weight:bold;">OWL ITS �뾽臾� 怨듦컙 <br/>�궗�슜 湲곌컙�씠 留뚮즺�릺�뿀�뒿�땲�떎.</span><br/><br/>
+                        </pre>
+                        <pre th:utext="${#messages.msg('workspace.expire.msg2', userName)}">
+                            <span style="color:#4B72FA;font-weight:bold">�궗�슜�옄紐�</span>�떂, OWL ITS瑜� �씠�슜�빐 二쇱뀛�꽌 媛먯궗�빀�땲�떎.<br/>
+                        </pre>
+                        <pre th:utext="${#messages.msg('workspace.expire.msg3', workspaceName)}">
+                            <span style="color:#4B72FA;font-weight:bold">[�뾽臾닿났媛꾨챸]</span>�뾽臾� 怨듦컙 �궗�슜 湲곌컙�씠 留뚮즺�릺�뿀�뒿�땲�떎.<br/><br/>
+                        </pre>
+                        <pre th:utext="${#messages.msg('workspace.expire.msg4')}">
+                            <span>怨꾩냽 �궗�슜�쓣 �썝�븯�떊�떎硫� OWL ITS �젙湲곌껐�젣瑜� �떊泥��빐 二쇱꽭�슂. �젙湲� 寃곗젣瑜� �떊泥��븯�떆硫� OWL ITS�쓽 紐⑤뱺 湲곕뒫�쓣 洹몃�濡� �궗�슜�븯�떎 �닔 �엳�뒿�땲�떎.<br/><br/></span>
+                        </pre>
+                        <pre th:utext="${#messages.msg('workspace.expire.msg5')}">
+                            <span style="color:#ff3c00; font-size:12px;">�살쑀�쓽�궗�빆<br/>�궗�슜湲곌컙 留뚮즺 6媛쒖썡 �씠�썑�뿉�뒗 �뾽臾닿났媛꾩뿉 ���옣�맂 �뜲�씠�꽣媛� 紐⑤몢 �궘�젣�릺�땲 �쑀�쓽�빐 二쇱꽭�슂!</span>
+                        </pre>
+
+
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="height:40px"></td>
+                </tr>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px;">
+                        <table align="center" border="0" cellpadding="0" cellspacing="0"
+                               style="width:100%;background-color: #F4F4F4;border: 1px solid #e7e7e7">
+                            <tbody>
+                            <tr>
+                                <td colspan="3" style="height:20px"></td>
+                            </tr>
+                            <tr>
+                                <td style="width:39px"></td>
+                                <td style="width:520px;">
+                                    <table style="width:100%;" align="center">
+                                        <tr>
+                                            <td>
+                                                <div th:utext="${#messages.msg('workspace.expire.msg6', expireDate)}">
+                                                    <div style="color: #9a9a9a; font-size: 12px; font-weight:bold;">
+                                                        �뾽臾닿났媛� 留뚮즺�씪
+                                                    </div>
+                                                    <div style="font-weight:bold">
+                                                        2013.06.13
+                                                    </div>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                    </table>
+                                </td>
+                                <td style="width:39px"></td>
+                            </tr>
+                            <tr>
+                                <td colspan="3" style="height:20px"></td>
+                            </tr>
+                            </tbody>
+                        </table>
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="height:40px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3">
+                        <table>
+                            <tr>
+                                <td style="width:150px"></td>
+                                <td style="width:300px; height:50px; text-align:center; background:#37308d; border-radius:25px">
+                                    <a href="http://www.owlsolution.io" target="_blank"
+                                       style="display:inline-block; width:100%; height:50px; line-height:50px; font-size:15px; font-weight:bold; color:#fff; text-decoration:none"
+                                       th:utext="${#messages.msg('workspace.expire.msg7')}">�젙湲� 寃곗젣 �떊泥��븯湲�</a>
+                                </td>
+                                <td style="width:150px"></td>
+                            </tr>
+                        </table>
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="height:40px;"></td>
+    </tr>
+    <tr>
+        <td>
+            <table align="center" border="0" cellpadding="0" cellspacing="0" style="width:100%">
+                <tbody>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px;text-align:center;">
+                        <pre th:utext="${#messages.msg('workspace.expire.msg8')}">
+                            <span style="font-size:12px;color: #8D929D;">OWL ITS �궗�슜�뿉 �뼱�젮���씠 �엳�떎硫�
+                            <a href="mailto:supportowl@wisestone.kr"
+                               style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a>濡� �씠硫붿씪�쓣 蹂대궡二쇱꽭�슂.</span>
+                        </pre>
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="height:20px;"></td>
+    </tr>
+    <tr>
+        <td>
+            <table align="center" border="0" cellpadding="0" cellspacing="0"
+                   style="width:100%;background-color: #f1f4fd; ">
+                <tbody>
+                <tr>
+                    <td style="height:20px;"></td>
+                </tr>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px; text-align: center;color: #A5A5A5; font-size: 12px;">
+                        <pre th:utext="${#messages.msg('workspace.expire.msg9')}">
+                             蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.<br/>
+                            <span style="color: #A5A5A5; font-size: 11px;">
+                                Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+                            </span>
+                        </pre>
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+                <tr>
+                    <td style="height:20px;"></td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
diff --git a/src/main/resources/mails/workspaceInviteNewUserEmail.html b/src/main/resources/mails/workspaceInviteNewUserEmail.html
new file mode 100644
index 0000000..9703d26
--- /dev/null
+++ b/src/main/resources/mails/workspaceInviteNewUserEmail.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('workspace.invitation.card')}">
+                珥덈��옣
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 10px 70px;color: #636363; font-size: 13px;">
+            <span th:utext="${#messages.msg('workspace.hello')}">�븞�뀞�븯�꽭�슂.</span> <br/>
+            <span style="color:#4B72FA; font-weight:bold;" th:utext="${inviteUserName}"></span>
+            <span th:utext="${#messages.msg('workspace.asked.you')}">�떂�씠 怨좉컼�떂�쓣</span>
+            <span style="color:#4B72FA; font-weight:bold;">OWL ITS</span>
+            [<span th:utext="${workspaceName}"> </span>] <span th:utext="${#messages.msg('workspace.invited.to')}">�뿉 珥덈��븯���뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-top: 1px solid #e7e7e7;">
+                        <span th:utext="${#messages.msg('workspace.click.button')}">�븘�옒 踰꾪듉�쓣 �겢由��븯�뿬 �쉶�썝媛��엯�쓣 �븯�떆硫� 珥덈��맂 �뾽臾� 怨듦컙�뿉 李몄뿬�븷 �닔 �엳�뒿�땲�떎. OWL ITS�뒗 �씠�뒋 湲곕컲�쓽 �봽濡쒖젥�듃 愿�由� �댋濡� 紐⑤뱺 �씠�뒋瑜� 愿�由ы븯怨� �삊�뾽�븷 �닔 �엳�뒿�땲�떎.</span>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding:20px;  color: #8D929D; font-size: 13px; text-align: center;border-bottom: 1px solid #e7e7e7;">
+                        <a href="https://owlsolution.io" target="_blank" style="padding: 10px 15px; background-color: #4B72FA; color: #fff; font-weight: bolder; font-size: 14px; display: inline-block; text-decoration: none;"><span th:utext="${#messages.msg('user.password.login')}">OWL
+                    ITS 濡쒓렇�씤</span></a>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+            <span style="color: #A5A5A5; font-size: 10px;">
+              Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+            </span>
+        </td>
+    </tr>
+</table>
+
+</body>
+</html>
diff --git a/src/main/resources/mails/workspaceInviteSystemUserEmail.html b/src/main/resources/mails/workspaceInviteSystemUserEmail.html
new file mode 100644
index 0000000..45dfcef
--- /dev/null
+++ b/src/main/resources/mails/workspaceInviteSystemUserEmail.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('workspace.invitation.card')}">
+                珥덈��옣
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 10px 70px;color: #636363; font-size: 13px;">
+            <span th:utext="${#messages.msg('workspace.hello')}">�븞�뀞�븯�꽭�슂.</span> <br/>
+            <span style="color:#4B72FA; font-weight:bold;" th:utext="${inviteUserName}"></span>
+            <span th:utext="${#messages.msg('workspace.asked.you')}">�떂�씠 怨좉컼�떂�쓣</span>
+            <span  style="color:#4B72FA; font-weight:bold;">OWL ITS</span>
+            [<span th:utext="${workspaceName}"></span>] <span th:utext="${#messages.msg('workspace.invited.to')}">�뿉 珥덈��븯���뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-top: 1px solid #e7e7e7;">
+                        <span th:utext="${#messages.msg('workspace.invited.join')}">�븘�옒 踰꾪듉�쓣 �겢由��븯�뿬 珥덈��맂 �뾽臾� 怨듦컙�뿉 李몄뿬�븷 �닔 �엳�뒿�땲�떎.</span>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding:20px;  color: #8D929D; font-size: 13px; text-align: center;border-bottom: 1px solid #e7e7e7;">
+                        <a href="https://owlsolution.io" target="_blank" style="padding: 10px 15px; background-color: #4B72FA; color: #fff; font-weight: bolder; font-size: 14px; display: inline-block; text-decoration: none;">
+                            <span th:utext="${#messages.msg('user.password.login')}">OWL ITS 濡쒓렇�씤</span>
+                        </a>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+            <span style="color: #A5A5A5; font-size: 10px;">
+              Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+            </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
diff --git a/src/main/resources/mails/workspaceJoinEmail.html b/src/main/resources/mails/workspaceJoinEmail.html
new file mode 100644
index 0000000..57dc8d2
--- /dev/null
+++ b/src/main/resources/mails/workspaceJoinEmail.html
@@ -0,0 +1,281 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body xmlns:th="http://www.thymeleaf.org">
+<table align="center" width="600" border="0" cellpadding="0" cellspacing="0"
+       style="background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05); font-family:'Noto Sans Korean', sans-serif, '留묒�怨좊뵓', Malgun Gothic, '�룍��', Dotum, '�굹�닎怨좊뵓', NanumGothic, Helvetica, 'Apple SD Gothic Neo'; width:600px; margin:0 auto; word-break:keep-all;color: #333; font-size: 14px; line-height:1.43;">
+    <tr>
+        <td>
+            <table align="center" border="0" cellpadding="0" cellspacing="0" style="width:100%">
+                <tbody>
+                <tr>
+                    <td colspan="3" style="height:20px"></td>
+                </tr>
+                <tr>
+                    <td style="width:210px"></td>
+                    <td style="width:180px;">
+                        <a th:href="${url}" target="_blank"><img alt="OWL ITS"
+                                                                src="http://owlsolution.co.kr/images/mail/logo-dark.png"></a>
+                    </td>
+                    <td style="width:210px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="height:20px"></td>
+                </tr>
+                </tbody>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff;">
+            <img alt="OWL ITS" src="http://owlsolution.co.kr/images/mail/em-top.jpg">
+        </td>
+    </tr>
+    <tr>
+        <td>
+            <table align="center" border="0" cellpadding="0" cellspacing="0" style="width:100%">
+                <tbody>
+                <tr>
+                    <td colspan="3" style="height:20px"></td>
+                </tr>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px;">
+                        <pre th:utext="${#messages.msg('workspace.join.msg1', name)}">
+                            <span style="font-size:24px;font-weight:bold;"> OWL ITS 媛��엯�쓣 �솚�쁺�빀�땲�떎.</span><br/><br/>
+                            �븞�뀞�븯�꽭�슂. <span
+                                style="color:#4B72FA;font-weight:bold">�궗�슜�옄紐�</span>�떂! OWL ITS 媛��엯�쓣 吏꾩떖�쑝濡� �솚�쁺�빀�땲�떎.<br/>OWL ITS瑜� �씠�슜�븯硫� �봽濡쒖젥�듃�뿉�꽌 諛쒖깮�븯�뒗 �씠�뒋瑜� �슚�쑉�쟻�쑝濡� 愿�由ы븷 �닔 �엳�뒿�땲�떎.
+                        </pre>
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="height:40px"></td>
+                </tr>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px;">
+                        <span th:utext="${#messages.msg('workspace.join.msg2')}">
+                            媛��엯�븯�떊 �젙蹂대�� �솗�씤�빐 二쇱꽭�슂.
+                        </span>
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="height:5px"></td>
+                </tr>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px;">
+                        <table align="center" border="0" cellpadding="0" cellspacing="0"
+                               style="width:100%;background-color: #F4F4F4;border: 1px solid #e7e7e7">
+                            <tbody>
+                            <tr>
+                                <td colspan="3" style="height:20px"></td>
+                            </tr>
+                            <tr>
+                                <td style="width:39px"></td>
+                                <td style="width:520px;">
+                                    <table style="width:100%;" align="center">
+                                        <tr>
+                                            <td style="width:319px;border-right: 1px solid #e7e7e7">
+                                                <div th:utext="${#messages.msg('workspace.join.msg3', account)}">
+                                                    <div style="color: #9a9a9a; font-size: 12px; font-weight:bold;">
+                                                        �븘�씠�뵒
+                                                    </div>
+                                                    <div style="font-weight:bold">
+                                                        [�궗�슜�옄 �씠硫붿씪 二쇱냼]
+                                                    </div>
+                                                </div>
+                                            </td>
+                                            <td style="width:40px;"></td>
+                                            <td style="width:220px;">
+                                                <div th:utext="${#messages.msg('workspace.join.msg4', registerDate, account)}">
+                                                    <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold;">
+                                                        媛��엯 �씪�떆
+                                                    </div>
+                                                    <div style="font-weight: bold;">
+                                                        [媛��엯 �씪�떆]
+                                                    </div>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                    </table>
+                                </td>
+                                <td style="width:39px"></td>
+                            </tr>
+                            <tr>
+                                <td colspan="3" style="height:20px"></td>
+                            </tr>
+                            </tbody>
+                        </table>
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="height:40px"></td>
+                </tr>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px; text-align:center;">
+                        <span th:utext="${#messages.msg('workspace.join.msg5')}">
+                            吏�湲�, OWL ITS�뿉 濡쒓렇�씤�빐 �떎�뼇�븳 湲곕뒫�쓣 �궗�슜�빐 蹂댁꽭�슂!
+                        </span>
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3" style="height:10px"></td>
+                </tr>
+                <tr>
+                    <td colspan="3">
+                        <table>
+                            <tr>
+                                <td style="width:150px"></td>
+                                <td style="width:300px; height:50px; text-align:center; background:#37308d; border-radius:25px">
+                                    <a th:href="${url}" target="_blank"
+                                       style="display:inline-block; width:100%; height:50px; line-height:50px; font-size:15px; font-weight:bold; color:#fff; text-decoration:none"
+                                       th:utext="${#messages.msg('workspace.join.msg6')}">�븘�슱ITS 濡쒓렇�씤�븯湲�</a>
+                                </td>
+                                <td style="width:150px"></td>
+                            </tr>
+                        </table>
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="height:60px;"></td>
+    </tr>
+    <tr>
+        <td>
+            <table align="center" border="0" cellpadding="0" cellspacing="0" style="width:100%">
+                <tbody>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px;text-align:center;">
+                        <pre th:utext="${#messages.msg('workspace.join.msg7')}">
+                            <span style="font-size:12px;color: #8D929D;">OWL ITS �궗�슜�뿉 �뼱�젮���씠 �엳�떎硫� <a
+                                    href="mailto:supportowl@wisestone.kr"
+                                    style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a>濡� �씠硫붿씪�쓣 蹂대궡二쇱꽭�슂.</span>
+                        </pre>
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="height:20px;"></td>
+    </tr>
+    <tr>
+        <td>
+            <table align="center" border="0" cellpadding="0" cellspacing="0"
+                   style="width:100%;background-color: #f1f4fd; ">
+                <tbody>
+                <tr>
+                    <td style="height:20px;"></td>
+                </tr>
+                <tr>
+                    <td style="width:40px"></td>
+                    <td style="width:520px; text-align: center;color: #A5A5A5; font-size: 12px;">
+                        <pre th:utext="${#messages.msg('workspace.join.msg8')}">
+                            蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.<br/>
+                            <span style="color: #A5A5A5; font-size: 11px;">
+                            Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+                          </span>
+                        </pre>
+                    </td>
+                    <td style="width:40px"></td>
+                </tr>
+                <tr>
+                    <td style="height:20px;"></td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
+
+
+<!--<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center"
+       style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;"
+       xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS"
+                                                                  src="http://wisestone.kr/owlsolution/logo-dark.png"
+                                                                  style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h1 style="margin-top: 0px;color:#030962;">
+                Welcome to <span style="color:#4B72FA;">OWL ITS!</span>
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 20px 70px;color: #636363; font-size: 13px;">
+            <span style="color:#4B72FA;" th:utext="${name}"></span><span th:utext="${#messages.msg('user.hello')}">�떂, �븞�뀞�븯�꽭�슂.</span>
+            <br/>
+            <span th:utext="${#messages.msg('workspace.join.01')}">OWL ITS�뿉 媛��엯�빐二쇱뀛�꽌 媛먯궗�뱶由쎈땲�떎. OWL ITS�뿉�꽌 �젣怨듯븯�뒗 �떎�뼇�븳 湲곕뒫�뱾�쓣 �궗�슜�븯�떎 �닔 �엳�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td colspan="2"
+                        style="padding: 20px; color: #8D929D; font-size: 13px; text-align: center;border-top: 1px solid #e7e7e7;">
+                        <span th:utext="${#messages.msg('workspace.check.user.id')}">媛��엯�븯�떊 �븘�씠�뵒瑜� �솗�씤�빐二쇱꽭�슂.</span>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; width: 50%;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('workspace.user.id')}">�븘�씠�뵒</span>
+                        </div>
+                        <div style="font-weight:bold" th:utext="${account}"></div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border: 1px solid #e7e7e7; border-left: none; border-right: none;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('workspace.subscription.date')}">媛��엯 �씪�떆</span>
+                        </div>
+                        <div style="font-weight: bold;" th:utext="${registerDate}"></div>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a
+                    href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+            <span style="color: #A5A5A5; font-size: 10px;">
+          Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+        </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>-->
diff --git a/src/main/resources/mails/workspaceMaxStorageExcessEmail.html b/src/main/resources/mails/workspaceMaxStorageExcessEmail.html
new file mode 100644
index 0000000..3b737fa
--- /dev/null
+++ b/src/main/resources/mails/workspaceMaxStorageExcessEmail.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('workspace.storage.space')}">
+                OWL ITS �뾽臾� 怨듦컙�뿉 ���옣 怨듦컙�씠 遺�議깊빀�땲�떎.
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 10px 70px;color: #636363; font-size: 13px;">
+            [<span style="color:#4B72FA;font-weight:bold;" th:utext="${#messages.msg('workspace.workspace')}">�뾽臾� 怨듦컙</span>]
+            <span style="color:#000;" th:utext="${#messages.msg('workspace.storage.space.cannot.upload')}">���옣 怨듦컙�씠 遺�議깊빀�땲�떎. ���옣 怨듦컙�씠 遺�議깊븯硫� �뙆�씪 �뾽濡쒕뱶瑜� �븷 �닔 �뾾�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;border-top: 1px solid #e7e7e7;">
+                        <div style="color: #9a9a9a; font-size: 12px; font-weight:bold; margin-bottom: 3px;">
+                            <span style="color:#4B72FA;font-weight:bold;" th:utext="${#messages.msg('workspace.total.storage')}">�뒪�넗由ъ� 珥� �슜�웾</span>
+                        </div>
+                        <div style="font-weight:bold;">
+                            100GB
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;border-right: 1px solid #e7e7e7; width:50%;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span style="color:#4B72FA;font-weight:bold;" th:utext="${#messages.msg('workspace.current.usage')}">�쁽�옱 �궗�슜 �웾</span>
+                        </div>
+                        <div style="font-weight: bold; color:#4B72FA;">
+                            99.9GB
+                        </div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7; ">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span style="color:#4B72FA;font-weight:bold;" th:utext="${#messages.msg('workspace.capacity.remain')}">�궓�� �슜�웾</span>
+                        </div>
+                        <div style="font-weight: bold; color:#D62525;">
+                            99.9GB
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <span th:utext="${#messages.msg('workspace.capacity.remove')}">遺덊븘�슂�븳 �뙆�씪�쓣 �젣嫄고븯嫄곕굹 �궗�슜�옄瑜� 異붽��븯�뿬 �슜�웾�쓣 �솗蹂댄븯�꽭�슂.</span>
+                        <a href="#" style="padding: 10px 15px; background-color: #4B72FA; color: #fff; font-weight: bolder; font-size: 14px; display: inline-block; text-decoration: none;"><span th:utext="${#messages.msg('regular.payment.service.shortcut.workspace')}">�뾽臾� 怨듦컙 諛붾줈媛�湲�</span></a>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+            <span style="color: #A5A5A5; font-size: 10px;">
+              Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+            </span>
+        </td>
+    </tr>
+</table>
+
+</body>
+</html>
diff --git a/src/main/resources/mails/workspaceMaxUserExcessEmail.html b/src/main/resources/mails/workspaceMaxUserExcessEmail.html
new file mode 100644
index 0000000..6a46bda
--- /dev/null
+++ b/src/main/resources/mails/workspaceMaxUserExcessEmail.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+<table align="center" style="width: 600px; margin:0 auto; background-color: #fff; box-shadow: 0px 20px 50px rgba(0,0,0,0.05);font-size: 14px; line-height: 1.43;font-family: &quot;留묒� 怨좊뵓&quot;" xmlns:th="http://www.thymeleaf.org">
+    <tr>
+        <td style="background-color: #fff;">
+            <a href="https://owlsolution.io" target="_blank"><img alt="OWL ITS" src="http://wisestone.kr/owlsolution/logo-dark.png" style="padding: 20px 60px"></a>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 40px 70px 0 70px; border-top: 1px solid rgba(0,0,0,0.05);">
+            <h1 style="margin-top: 0px;color:#030962;" th:utext="${#messages.msg('workspace.maximum.number.user')}">
+                OWL ITS �뾽臾� 怨듦컙�뿉 理쒕� �솢�꽦 �궗�슜�옄 �닔媛� 珥덇낵�릺�뿀�뒿�땲�떎.
+            </h1>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #fff; padding: 10px 70px;color: #636363; font-size: 13px;">
+            [<span style="color:#4B72FA;font-weight:bold;" th:utext="${workspaceName}"></span>]
+            <span style="color:#000;" th:utext="${#messages.msg('workspace.maximum.number.user.invite.inactive')}">理쒕� �솢�꽦 媛��뒫�븳 �궗�슜�옄 �닔媛� 珥덇낵�릺�뼱 珥덈�諛쏆� �궗�슜�옄媛� 鍮꾪솢�꽦 �긽�깭�엯�땲�떎.</span><br/><br/>
+        </td>
+    </tr>
+    <tr>
+        <td style="padding: 0px 70px;font-size: 13px;">
+            <table style="width:450px; background-color: #F4F4F4; margin: 20px 0px 40px;" align="center">
+                <tr>
+                    <td style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;border-right: 1px solid #e7e7e7; width:50%;">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('workspace.maximum.number.active')}">理쒕� �솢�꽦 �궗�슜�옄 �닔</span>
+                        </div>
+                        <div style="font-weight: bold; color:#4B72FA;">
+                            <span th:utext="${activeUser}"></span><span th:utext="${#messages.msg('regular.payment.person')}">紐�</span>
+                        </div>
+                    </td>
+                    <td style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7; ">
+                        <div style="color: #9a9a9a; font-size: 12px;  font-weight:bold; margin-bottom: 3px;">
+                            <span th:utext="${#messages.msg('workspace.number.inactive')}">鍮꾪솢�꽦 �궗�슜�옄�닔</span>
+                        </div>
+                        <div style="font-weight: bold; color:#D62525;">
+                            <span th:utext="${disableUser}"></span><span th:utext="${#messages.msg('regular.payment.person')}">紐�</span>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" style="padding: 20px 40px; color: #111; border-bottom: 1px solid #e7e7e7;">
+                        <span th:utext="${#messages.msg('workspace.user.add.payment')}">鍮꾪솢�꽦 �궗�슜�옄瑜� �솢�꽦 �궗�슜�옄濡� 蹂�寃쏀븯湲� �쐞�빐�꽌�뒗 �궗�슜�옄 異붽� 寃곗젣媛� �븘�슂�빀�땲�떎.</span>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+    <tr>
+        <td style="text-align:center;padding-bottom:40px;">
+            <span style="color: #000; font-weight:bold; font-size:16px;">OWL ITS TEAM</span><br/>
+            <a href="https://owlsolution.io" target="_blank" style="text-decoration: underline; color: #4B72FA;">
+                https://owlsolution.io</a><br/><br/>
+            <span style="font-size:12px;color: #8D929D;">If you have any questions, contact us at <a href="mailto:supportowl@wisestone.kr" style="text-decoration: underline; color: #4B72FA;">supportowl@wisestone.kr</a></span>
+        </td>
+    </tr>
+    <tr>
+        <td style="background-color: #f1f4fd; padding: 30px; text-align: center;color: #A5A5A5; font-size: 12px; margin-bottom: 5px;">
+            <span th:utext="${#messages.msg('common.common.sendMail')}">蹂� 硫붿씪�� 諛쒖떊�쟾�슜 硫붿씪�엯�땲�떎.</span><br/>
+            <span style="color: #A5A5A5; font-size: 10px;">
+              Copyright 짤 WISESTONE CO., Ltd. All Rights Reserved.
+            </span>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
diff --git a/src/main/resources/migration/V1_1__Initial_Setup.sql b/src/main/resources/migration/V1_1__Initial_Setup.sql
new file mode 100644
index 0000000..6dc56c9
--- /dev/null
+++ b/src/main/resources/migration/V1_1__Initial_Setup.sql
@@ -0,0 +1,840 @@
+-- --------------------------------------------------------
+-- �샇�뒪�듃:                          192.168.0.64
+-- �꽌踰� 踰꾩쟾:                        10.1.13-MariaDB - Source distribution
+-- �꽌踰� OS:                        Linux
+-- HeidiSQL 踰꾩쟾:                  10.1.0.5464
+-- --------------------------------------------------------
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET NAMES utf8 */;
+/*!50503 SET NAMES utf8mb4 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+
+
+-- �뀒�씠釉� owl_en_1.5.attached_file 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `attached_file` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `issue_id` bigint(20) DEFAULT NULL,
+    `workspace_id` bigint(20) DEFAULT NULL,
+    `name` varchar(4000) DEFAULT '',
+    `path` varchar(4000) DEFAULT NULL,
+    `file_type` varchar(10) DEFAULT NULL,
+    `size` bigint(20) DEFAULT NULL,
+    `content_type` varchar(255) DEFAULT NULL,
+    `register_id` bigint(20) NOT NULL COMMENT 'register_id',
+    `register_date` timestamp NULL DEFAULT NULL COMMENT 'register_date',
+    `modify_id` bigint(20) NOT NULL COMMENT 'modify_id',
+    `modify_date` timestamp NULL DEFAULT NULL COMMENT 'modify_date',
+    `aws_key` varchar(255) DEFAULT NULL,
+    PRIMARY KEY (`id`),
+    KEY `issueIdIndex` (`issue_id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.attached_file:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `attached_file` DISABLE KEYS */;
+/*!40000 ALTER TABLE `attached_file` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.custom_field 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `custom_field` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `workspace_id` bigint(20) NOT NULL,
+    `name` varchar(15) NOT NULL,
+    `custom_field_type` varchar(50) NOT NULL,
+    `default_value` varchar(100) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.custom_field:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `custom_field` DISABLE KEYS */;
+/*!40000 ALTER TABLE `custom_field` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.custom_field_value 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `custom_field_value` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `custom_field_id` bigint(20) NOT NULL,
+    `value` varchar(100) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `customFieldIdIndex` (`custom_field_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.custom_field_value:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `custom_field_value` DISABLE KEYS */;
+/*!40000 ALTER TABLE `custom_field_value` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+    `issue_status_id` bigint(20) DEFAULT NULL,
+    `issue_type_id` bigint(20) DEFAULT NULL,
+    `project_id` bigint(20) DEFAULT NULL,
+    `priority_id` bigint(20) DEFAULT NULL,
+    `severity_id` bigint(20) DEFAULT NULL,
+    `title` varchar(300) DEFAULT NULL,
+    `description` mediumtext COMMENT 'description',
+    `reverse_index` bigint(20) DEFAULT NULL,
+    `issue_number` bigint(20) DEFAULT NULL,
+    `start_date` varchar(20) DEFAULT NULL,
+    `complete_date` varchar(20) DEFAULT NULL,
+    `register_id` bigint(20) NOT NULL COMMENT 'register_id',
+    `register_date` timestamp NULL DEFAULT NULL COMMENT 'register_date',
+    `modify_id` bigint(20) NOT NULL COMMENT 'modify_id',
+    `modify_date` timestamp NULL DEFAULT NULL COMMENT 'modify_date',
+    PRIMARY KEY (`id`),
+    KEY `projectIdIndex` (`project_id`),
+    KEY `reverseIndex` (`reverse_index`),
+    KEY `issueTypeIdIndex` (`issue_type_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue_comment 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue_comment` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+    `issue_id` bigint(20) NOT NULL COMMENT 'issue_id',
+    `workspace_id` bigint(20) NOT NULL,
+    `description` varchar(300) DEFAULT NULL COMMENT 'description',
+    `register_id` bigint(20) NOT NULL COMMENT 'register_id',
+    `register_date` timestamp NULL DEFAULT NULL COMMENT 'register_date',
+    `modify_id` bigint(20) NOT NULL COMMENT 'modify_id',
+    `modify_date` timestamp NULL DEFAULT NULL COMMENT 'modify_date',
+    PRIMARY KEY (`id`),
+    KEY `issueIdIndex` (`issue_id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='issue_comment';
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue_comment:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue_comment` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue_comment` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue_custom_field_value 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue_custom_field_value` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `issue_id` bigint(20) NOT NULL,
+    `issue_type_custom_field_id` bigint(20) NOT NULL,
+    `custom_field_id` bigint(20) NOT NULL,
+    `use_value` mediumtext NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `customFieldIdIndex` (`custom_field_id`),
+    KEY `issueTypeCustomFieldIdIndex` (`issue_type_custom_field_id`),
+    KEY `issueIdIndex` (`issue_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue_custom_field_value:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue_custom_field_value` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue_custom_field_value` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue_history 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue_history` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `issue_id` bigint(20) DEFAULT NULL,
+    `project_id` bigint(20) DEFAULT NULL,
+    `issue_history_type` varchar(10) NOT NULL,
+    `description` mediumtext,
+    `register_id` bigint(20) DEFAULT NULL,
+    `register_date` timestamp NULL DEFAULT NULL,
+    `modify_id` bigint(20) DEFAULT NULL,
+    `modify_date` timestamp NULL DEFAULT NULL,
+    PRIMARY KEY (`id`),
+    KEY `issueIdIndex` (`issue_id`),
+    KEY `projectIdIndex` (`project_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue_history:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue_history` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue_history` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue_number_generator 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue_number_generator` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `project_id` bigint(20) NOT NULL,
+    `number` bigint(20) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `projectIdIndex` (`project_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue_number_generator:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue_number_generator` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue_number_generator` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue_risk 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue_risk` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `issue_id` bigint(20) NOT NULL,
+    `workspace_id` bigint(20) NOT NULL,
+    `change_assignee_count` bigint(20) NOT NULL DEFAULT '0',
+    `change_issue_status_count` bigint(20) NOT NULL DEFAULT '0',
+    `issue_status_ids` text NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    `modify_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY (`id`),
+    KEY `issueIdIndex` (`issue_id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue_risk:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue_risk` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue_risk` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue_search 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue_search` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `workspace_id` bigint(20) NOT NULL,
+    `user_id` bigint(20) NOT NULL,
+    `conditions` mediumtext,
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `workspaceIdAndUserIdIndex` (`workspace_id`,`user_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue_search:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue_search` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue_search` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue_status 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue_status` (
+    `id` int(11) NOT NULL AUTO_INCREMENT,
+    `workspace_id` bigint(20) DEFAULT NULL,
+    `issue_status_type` varchar(50) DEFAULT NULL,
+    `name` varchar(20) NOT NULL DEFAULT '',
+    `default_yn` varchar(1) NOT NULL DEFAULT 'N',
+    `color` varchar(255) NOT NULL,
+    `position` bigint(20) NOT NULL,
+    `register_id` bigint(20) NOT NULL COMMENT 'register_id',
+    `register_date` timestamp NULL DEFAULT NULL COMMENT 'register_date',
+    `modify_id` bigint(20) NOT NULL COMMENT 'modify_id',
+    `modify_date` timestamp NULL DEFAULT NULL COMMENT 'modify_date',
+    PRIMARY KEY (`id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue_status:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue_status` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue_status` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue_table_config 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue_table_config` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `workspace_id` bigint(20) NOT NULL,
+    `user_id` bigint(20) NOT NULL,
+    `issue_table_configs` mediumtext CHARACTER SET utf8 NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `workspaceIdAndUserIdIndex` (`workspace_id`,`user_id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue_table_config:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue_table_config` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue_table_config` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue_type 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue_type` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `workspace_id` bigint(20) NOT NULL,
+    `workflow_id` bigint(20) NOT NULL,
+    `name` varchar(15) NOT NULL,
+    `description` mediumtext NOT NULL,
+    `color` varchar(255) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    PRIMARY KEY (`id`),
+    KEY `workspaceIdIndex` (`workspace_id`),
+    KEY `workflowIdIndex` (`workflow_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue_type:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue_type` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue_type` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue_type_custom_field 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue_type_custom_field` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `project_id` bigint(20) NOT NULL,
+    `issue_type_id` bigint(20) NOT NULL,
+    `custom_field_id` bigint(20) NOT NULL,
+    `field_option` varchar(10) NOT NULL,
+    `position` bigint(20) NOT NULL DEFAULT '0',
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `projectIdIndex` (`project_id`),
+    KEY `issueTypeIdIndex` (`issue_type_id`),
+    KEY `customFieldIdIndex` (`custom_field_id`),
+    KEY `projectIdAndIssueTypeIdIndex` (`project_id`,`issue_type_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue_type_custom_field:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue_type_custom_field` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue_type_custom_field` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue_user 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue_user` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `user_id` bigint(20) NOT NULL,
+    `issue_id` bigint(20) NOT NULL,
+    `workspace_id` bigint(20) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `userIdAndIssueIdIndex` (`user_id`,`issue_id`),
+    KEY `issueIdIndex` (`issue_id`),
+    KEY `workspaceIdIndex` (`workspace_id`),
+    KEY `userIdIndex` (`user_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue_user:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue_user` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue_user` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.issue_version_control 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `issue_version_control` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `workspace_id` bigint(20) DEFAULT NULL,
+    `project_id` bigint(20) DEFAULT NULL,
+    `issue_id` bigint(20) DEFAULT NULL,
+    `version` bigint(20) DEFAULT NULL,
+    `content` mediumtext,
+    `register_id` bigint(20) DEFAULT NULL,
+    `register_date` timestamp NULL DEFAULT NULL,
+    `modify_id` bigint(20) DEFAULT NULL,
+    `modify_date` timestamp NULL DEFAULT NULL,
+    PRIMARY KEY (`id`),
+    KEY `issueIdIndex` (`issue_id`),
+    KEY `workspaceIdIndex` (`workspace_id`),
+    KEY `projectIdIndex` (`project_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.issue_version_control:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `issue_version_control` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issue_version_control` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.login_history 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `login_history` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+    `user_id` bigint(20) NOT NULL COMMENT 'user_id',
+    `login_date` timestamp NULL DEFAULT NULL COMMENT 'login_date',
+    `logout_date` timestamp NULL DEFAULT NULL COMMENT 'logout_date',
+    `login_ip` varchar(20) NOT NULL COMMENT 'login_ip',
+    PRIMARY KEY (`id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='login_history';
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.login_history:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `login_history` DISABLE KEYS */;
+/*!40000 ALTER TABLE `login_history` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.payment 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `payment` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `workspace_id` bigint(20) DEFAULT NULL,
+    `type` varchar(10) DEFAULT NULL,
+    `price` bigint(20) DEFAULT NULL,
+    `buy_user` bigint(20) DEFAULT NULL,
+    `register_date` timestamp NULL DEFAULT NULL,
+    `register_id` bigint(20) DEFAULT NULL,
+    `modify_date` timestamp NULL DEFAULT NULL,
+    `modify_id` bigint(20) DEFAULT NULL,
+    PRIMARY KEY (`id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.payment:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `payment` DISABLE KEYS */;
+/*!40000 ALTER TABLE `payment` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.payment_history 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `payment_history` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `workspace_id` bigint(20) NOT NULL,
+    `type` varchar(10) DEFAULT NULL,
+    `price` bigint(20) DEFAULT NULL,
+    `buy_user` bigint(20) DEFAULT NULL,
+    `customer_uid` varchar(255) NOT NULL,
+    `merchant_uid` varchar(255) NOT NULL,
+    `payment_result` varchar(10) NOT NULL,
+    `payment_response` mediumtext NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `register_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    `modify_id` bigint(20) NOT NULL,
+    PRIMARY KEY (`id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.payment_history:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `payment_history` DISABLE KEYS */;
+/*!40000 ALTER TABLE `payment_history` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.permission 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `permission` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `name` varchar(50) NOT NULL,
+    `action` varchar(50) NOT NULL,
+    `role_type` varchar(2) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `register_date` timestamp NULL DEFAULT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NULL DEFAULT NULL,
+    PRIMARY KEY (`id`)
+    ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.permission:~12 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `permission` DISABLE KEYS */;
+INSERT INTO `permission` (`id`, `name`, `action`, `role_type`, `register_id`, `register_date`, `modify_id`, `modify_date`) VALUES
+(1, '�봽濡쒖젥�듃 議고쉶 沅뚰븳', 'PROJECT_READ', '01', 1, '2017-12-29 21:38:48', 1, '2017-12-29 21:38:51'),
+(2, '�봽濡쒖젥�듃 李몄뿬�옄 沅뚰븳', 'PROJECT_JOIN', '03', 1, '2018-01-03 10:11:42', 1, '2018-01-03 10:11:43'),
+(3, '�봽濡쒖젥�듃 愿�由ъ옄 沅뚰븳', 'PROJECT_MANAGER', '04', 1, '2018-01-03 10:11:42', 1, '2018-01-03 10:11:43'),
+(4, '�씠�뒋 �넻�빀 沅뚰븳', 'ISSUE_TOTAL', '01', 1, '2018-02-23 12:24:06', 1, '2018-02-23 12:24:07'),
+(5, '�썙�겕�뒪�럹�씠�뒪 愿�由ъ옄 沅뚰븳', 'WORKSPACE_MANAGER', '02', 1, '2018-02-23 12:24:06', 1, '2018-02-23 12:25:42'),
+(6, '���떆蹂대뱶 �넻�빀 沅뚰븳', 'DASHBOARD_TOTAL', '01', 1, '2018-05-03 15:04:44', 1, '2018-05-03 15:04:47'),
+(7, '�궗�슜�옄 議고쉶 沅뚰븳', 'USER_READ', '01', 1, '2018-05-03 15:04:44', 1, '2018-05-03 15:04:47'),
+(8, '�궗�슜�옄 �넻�빀 沅뚰븳', 'USER_TOTAL', '02', 1, '2018-05-03 15:04:44', 1, '2018-05-03 15:04:47'),
+(9, '�씠�뒋 �긽�깭 �넻�빀 沅뚰븳', 'ISSUE_STATUS_TOTAL', '01', 1, '2018-05-03 15:06:31', 1, '2018-05-03 15:06:32'),
+(10, '�썙�겕�뵆濡쒖슦 �넻�빀 沅뚰븳', 'WORKFLOW_TOTAL', '01', 1, '2018-05-03 15:07:09', 1, '2018-05-03 15:07:10'),
+(11, '�씠�뒋 �쑀�삎 �넻�빀 沅뚰븳', 'ISSUE_TYPE_TOTAL', '01', 1, '2018-05-03 15:07:35', 1, '2018-05-03 15:07:36'),
+(12, '�궗�슜�옄 �젙�쓽 �븘�뱶 �넻�빀 沅뚰븳', 'CUSTOM_FIELD_TOTAL', '01', 1, '2018-05-03 15:07:55', 1, '2018-05-03 15:07:56');
+/*!40000 ALTER TABLE `permission` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.priority 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `priority` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `workspace_id` bigint(20) NOT NULL,
+    `name` varchar(1000) NOT NULL DEFAULT '',
+    `position` int(11) DEFAULT NULL,
+    `color` varchar(255) DEFAULT NULL,
+    `register_id` bigint(20) NOT NULL COMMENT 'register_id',
+    `register_date` timestamp NULL DEFAULT NULL COMMENT 'register_date',
+    `modify_id` bigint(20) NOT NULL COMMENT 'modify_id',
+    `modify_date` timestamp NULL DEFAULT NULL COMMENT 'modify_date',
+    PRIMARY KEY (`id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.priority:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `priority` DISABLE KEYS */;
+/*!40000 ALTER TABLE `priority` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.project 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `project` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+    `workspace_id` bigint(20) NOT NULL DEFAULT '0',
+    `name` varchar(50) NOT NULL COMMENT 'name',
+    `project_key` varchar(10) NOT NULL,
+    `project_type` varchar(50) NOT NULL,
+    `description` mediumtext COMMENT 'description',
+    `status` varchar(2) NOT NULL DEFAULT '01' COMMENT '01:�솢�꽦, 02:鍮꾪솢�꽦',
+    `start_date` varchar(10) DEFAULT NULL COMMENT 'start_date',
+    `end_date` varchar(10) DEFAULT NULL COMMENT 'end_date',
+    `default_yn` varchar(2) NOT NULL DEFAULT 'N',
+    `register_id` bigint(20) NOT NULL COMMENT 'register_id',
+    `register_date` timestamp NULL DEFAULT NULL COMMENT 'register_date',
+    `modify_id` bigint(20) NOT NULL COMMENT 'modify_id',
+    `modify_date` timestamp NULL DEFAULT NULL COMMENT 'modify_date',
+    PRIMARY KEY (`id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='project';
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.project:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `project` DISABLE KEYS */;
+/*!40000 ALTER TABLE `project` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.project_role 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `project_role` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `project_id` bigint(20) NOT NULL,
+    `name` varchar(50) NOT NULL,
+    `role_type` varchar(2) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `projectIdIndex` (`project_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.project_role:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `project_role` DISABLE KEYS */;
+/*!40000 ALTER TABLE `project_role` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.project_role_permission 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `project_role_permission` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `project_role_id` bigint(20) NOT NULL,
+    `permission_id` bigint(20) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `projectRoleIdIndex` (`project_role_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.project_role_permission:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `project_role_permission` DISABLE KEYS */;
+/*!40000 ALTER TABLE `project_role_permission` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.project_role_user 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `project_role_user` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `project_role_id` bigint(20) NOT NULL,
+    `user_id` bigint(20) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `projectRoleIdAndUserIdIndex` (`project_role_id`,`user_id`),
+    KEY `userIdIndex` (`user_id`),
+    KEY `projectRoleIdIndex` (`project_role_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.project_role_user:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `project_role_user` DISABLE KEYS */;
+/*!40000 ALTER TABLE `project_role_user` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.reservation_disable_user 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `reservation_disable_user` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `payment_id` bigint(20) DEFAULT NULL,
+    `user_ids` varchar(255) DEFAULT NULL,
+    `register_id` bigint(20) DEFAULT NULL,
+    `register_date` timestamp NULL DEFAULT NULL,
+    `modify_id` bigint(20) DEFAULT NULL,
+    `modify_date` timestamp NULL DEFAULT NULL,
+    PRIMARY KEY (`id`),
+    KEY `paymentIdIndex` (`payment_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.reservation_disable_user:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `reservation_disable_user` DISABLE KEYS */;
+/*!40000 ALTER TABLE `reservation_disable_user` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.severity 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `severity` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `workspace_id` bigint(20) NOT NULL,
+    `name` varchar(50) NOT NULL,
+    `position` bigint(20) NOT NULL,
+    `color` varchar(255) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    PRIMARY KEY (`id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.severity:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `severity` DISABLE KEYS */;
+/*!40000 ALTER TABLE `severity` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.system_email 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `system_email` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `send_address` varchar(50) NOT NULL,
+    `email_type` varchar(255) DEFAULT NULL,
+    `parameter` mediumtext NOT NULL,
+    `send_yn` varchar(2) NOT NULL DEFAULT 'N',
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.system_email:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `system_email` DISABLE KEYS */;
+/*!40000 ALTER TABLE `system_email` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.system_role 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `system_role` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `name` varchar(50) NOT NULL,
+    `role_type` varchar(2) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `register_date` timestamp NULL DEFAULT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NULL DEFAULT NULL,
+    PRIMARY KEY (`id`)
+    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.system_role:~2 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `system_role` DISABLE KEYS */;
+INSERT INTO `system_role` (`id`, `name`, `role_type`, `register_id`, `register_date`, `modify_id`, `modify_date`) VALUES
+(1, '�씪諛� �궗�슜�옄 �뿭�븷', '01', 1, '2017-12-29 21:37:15', 1, '2017-12-29 21:37:16'),
+(2, '�썙�겕�뒪�럹�씠�뒪 愿�由ъ옄 �뿭�븷', '02', 1, '2017-12-29 21:37:27', 1, '2017-12-29 21:37:28');
+/*!40000 ALTER TABLE `system_role` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.system_role_permission 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `system_role_permission` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `system_role_id` bigint(20) NOT NULL,
+    `permission_id` bigint(20) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `register_date` timestamp NULL DEFAULT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NULL DEFAULT NULL,
+    PRIMARY KEY (`id`)
+    ) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.system_role_permission:~18 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `system_role_permission` DISABLE KEYS */;
+INSERT INTO `system_role_permission` (`id`, `system_role_id`, `permission_id`, `register_id`, `register_date`, `modify_id`, `modify_date`) VALUES
+(1, 1, 1, 1, '2018-05-08 15:46:30', 1, '2018-05-08 15:46:31'),
+(2, 1, 4, 1, '2018-05-08 15:46:37', 1, '2018-05-08 15:46:38'),
+(3, 1, 6, 1, '2018-05-08 15:46:47', 1, '2018-05-08 15:46:48'),
+(4, 1, 7, 1, '2018-05-08 15:46:59', 1, '2018-05-08 15:47:00'),
+(5, 1, 9, 1, '2018-05-08 15:47:06', 1, '2018-05-08 15:47:07'),
+(6, 1, 10, 1, '2018-05-08 15:47:14', 1, '2018-05-08 15:47:15'),
+(7, 1, 11, 1, '2018-05-08 15:47:21', 1, '2018-05-08 15:47:22'),
+(8, 1, 12, 1, '2018-05-08 15:47:29', 1, '2018-05-08 15:47:30'),
+(9, 2, 1, 1, '2018-05-08 15:51:30', 1, '2018-05-08 15:51:31'),
+(10, 2, 4, 1, '2018-05-08 15:51:37', 1, '2018-05-08 15:51:37'),
+(11, 2, 5, 1, '2018-05-08 15:51:43', 1, '2018-05-08 15:51:43'),
+(12, 2, 6, 1, '2018-05-08 15:51:49', 1, '2018-05-08 15:51:50'),
+(13, 2, 7, 1, '2018-05-08 15:51:57', 1, '2018-05-08 15:51:57'),
+(14, 2, 8, 1, '2018-05-08 15:52:04', 1, '2018-05-08 15:52:04'),
+(15, 2, 9, 1, '2018-05-08 15:52:13', 1, '2018-05-08 15:52:14'),
+(16, 2, 10, 1, '2018-05-08 15:52:20', 1, '2018-05-08 15:52:21'),
+(17, 2, 11, 1, '2018-05-08 15:52:28', 1, '2018-05-08 15:52:28'),
+(18, 2, 12, 1, '2018-05-08 15:52:35', 1, '2018-05-08 15:52:36');
+/*!40000 ALTER TABLE `system_role_permission` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.system_role_user 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `system_role_user` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `system_role_id` bigint(20) NOT NULL,
+    `user_id` bigint(20) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `register_date` timestamp NULL DEFAULT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NULL DEFAULT NULL,
+    PRIMARY KEY (`id`),
+    KEY `userIdIndex` (`user_id`),
+    KEY `systemRoleidIndex` (`system_role_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.system_role_user:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `system_role_user` DISABLE KEYS */;
+/*!40000 ALTER TABLE `system_role_user` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.user 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `user` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `name` varchar(50) NOT NULL,
+    `account` varchar(50) DEFAULT NULL,
+    `password` varchar(200) DEFAULT NULL,
+    `profile` varchar(200) DEFAULT NULL,
+    `status` varchar(2) NOT NULL DEFAULT '02',
+    `phone` varchar(20) DEFAULT NULL,
+    `social_type` varchar(10) DEFAULT NULL,
+    `last_workspace_id` bigint(20) DEFAULT NULL,
+    `reservation_notify_time` varchar(20) DEFAULT NULL,
+    `language` varchar(10) NOT NULL,
+    `aws_key` varchar(255) DEFAULT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `register_date` timestamp NULL DEFAULT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NULL DEFAULT NULL,
+    PRIMARY KEY (`id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.user:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `user` DISABLE KEYS */;
+/*!40000 ALTER TABLE `user` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.user_invite 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `user_invite` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `workspace_id` bigint(20) NOT NULL,
+    `email` varchar(255) NOT NULL,
+    `status` varchar(10) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.user_invite:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `user_invite` DISABLE KEYS */;
+/*!40000 ALTER TABLE `user_invite` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.user_invite_project 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `user_invite_project` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `user_invite_id` bigint(20) NOT NULL,
+    `project_id` bigint(20) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `userInviteIdIndex` (`user_invite_id`),
+    KEY `projectIdIndex` (`project_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.user_invite_project:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `user_invite_project` DISABLE KEYS */;
+/*!40000 ALTER TABLE `user_invite_project` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.user_like_issue 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `user_like_issue` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `user_id` bigint(20) NOT NULL,
+    `issue_id` bigint(20) NOT NULL,
+    `workspace_id` bigint(20) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `modify_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `userIdIndex` (`user_id`),
+    KEY `issueIdIndex` (`issue_id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.user_like_issue:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `user_like_issue` DISABLE KEYS */;
+/*!40000 ALTER TABLE `user_like_issue` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.user_with_draw 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `user_with_draw` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `account` varchar(200) NOT NULL,
+    `register_id` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `modify_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    PRIMARY KEY (`id`),
+    KEY `accountIndex` (`account`(191))
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.user_with_draw:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `user_with_draw` DISABLE KEYS */;
+/*!40000 ALTER TABLE `user_with_draw` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.user_workspace 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `user_workspace` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `user_id` bigint(20) DEFAULT NULL,
+    `workspace_id` bigint(20) DEFAULT NULL,
+    `manager_yn` varchar(2) NOT NULL DEFAULT 'N',
+    `use_yn` varchar(2) NOT NULL DEFAULT 'N',
+    `disable_position` bigint(20) NOT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `register_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    `modify_id` bigint(20) NOT NULL,
+    PRIMARY KEY (`id`),
+    KEY `userIdIndex` (`user_id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.user_workspace:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `user_workspace` DISABLE KEYS */;
+/*!40000 ALTER TABLE `user_workspace` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.workflow 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `workflow` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `workspace_id` bigint(20) NOT NULL,
+    `name` varchar(50) NOT NULL,
+    `description` mediumtext,
+    `project_type` varchar(50) DEFAULT NULL,
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `register_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    `modify_id` bigint(20) NOT NULL,
+    PRIMARY KEY (`id`),
+    KEY `workspaceIdIndex` (`workspace_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.workflow:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `workflow` DISABLE KEYS */;
+/*!40000 ALTER TABLE `workflow` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.workflow_transition 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `workflow_transition` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `workflow_id` bigint(20) NOT NULL,
+    `source_issue_status_id` bigint(20) NOT NULL,
+    `target_issue_status_id` bigint(20) NOT NULL,
+    `source_x` bigint(20) DEFAULT NULL,
+    `source_y` bigint(20) DEFAULT NULL,
+    `target_x` bigint(20) DEFAULT NULL,
+    `target_y` bigint(20) DEFAULT NULL,
+    `correct_x` bigint(20) DEFAULT NULL,
+    `correct_y` bigint(20) DEFAULT NULL,
+    `direct` varchar(2) NOT NULL DEFAULT 'N',
+    `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `register_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    `modify_id` bigint(20) NOT NULL,
+    PRIMARY KEY (`id`),
+    KEY `workflowIdIndex` (`workflow_id`),
+    KEY `sourceIssueStatusIdIndex` (`source_issue_status_id`),
+    KEY `targetIssueStatusIdIndex` (`target_issue_status_id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.workflow_transition:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `workflow_transition` DISABLE KEYS */;
+/*!40000 ALTER TABLE `workflow_transition` ENABLE KEYS */;
+
+-- �뀒�씠釉� owl_en_1.5.workspace 援ъ“ �궡蹂대궡湲�
+CREATE TABLE IF NOT EXISTS `workspace` (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT,
+    `name` varchar(255) NOT NULL,
+    `max_user` bigint(20) NOT NULL,
+    `start_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    `expire_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    `storage_size` bigint(20) NOT NULL,
+    `use_traffic` bigint(20) DEFAULT NULL,
+    `service_type` varchar(10) DEFAULT NULL,
+    `register_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    `register_id` bigint(20) NOT NULL,
+    `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+    `modify_id` bigint(20) NOT NULL,
+    PRIMARY KEY (`id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- �뀒�씠釉� �뜲�씠�꽣 owl_en_1.5.workspace:~0 rows (���왂�쟻) �궡蹂대궡湲�
+/*!40000 ALTER TABLE `workspace` DISABLE KEYS */;
+/*!40000 ALTER TABLE `workspace` ENABLE KEYS */;
+
+/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
+/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
diff --git a/src/main/resources/migration/V1_2__Alter_Table.sql b/src/main/resources/migration/V1_2__Alter_Table.sql
new file mode 100644
index 0000000..3d762ee
--- /dev/null
+++ b/src/main/resources/migration/V1_2__Alter_Table.sql
@@ -0,0 +1 @@
+ALTER TABLE issue_version_control RENAME issue_version;
\ No newline at end of file
diff --git a/src/main/resources/migration/V1_3__Alter_Table.sql b/src/main/resources/migration/V1_3__Alter_Table.sql
new file mode 100644
index 0000000..0b525bc
--- /dev/null
+++ b/src/main/resources/migration/V1_3__Alter_Table.sql
@@ -0,0 +1,2 @@
+alter table attached_file add attached_type varchar (20) not null;
+update attached_file set attached_type = 'ISSUE_ATTACHED';
\ No newline at end of file
diff --git a/src/main/resources/migration/V1_4__Alter_Table.sql b/src/main/resources/migration/V1_4__Alter_Table.sql
new file mode 100644
index 0000000..06e871e
--- /dev/null
+++ b/src/main/resources/migration/V1_4__Alter_Table.sql
@@ -0,0 +1,14 @@
+CREATE TABLE IF NOT EXISTS `issue_reservation` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `issue_id` bigint(20) DEFAULT NULL,
+  `workspace_id` bigint(20) DEFAULT NULL,
+  `reservation` varchar(10) DEFAULT NULL,
+  `issue_reservation_type` varchar(10) DEFAULT NULL,
+  `register_id` bigint(20) NOT NULL,
+  `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `modify_id` bigint(20) NOT NULL,
+  `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+  PRIMARY KEY (`id`),
+  KEY `workspaceIdIndex` (`workspace_id`),
+  KEY `issueIdIndex` (`issue_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
\ No newline at end of file
diff --git a/src/main/resources/migration/V1_5__Alter_Table.sql b/src/main/resources/migration/V1_5__Alter_Table.sql
new file mode 100644
index 0000000..01f6374
--- /dev/null
+++ b/src/main/resources/migration/V1_5__Alter_Table.sql
@@ -0,0 +1,10 @@
+CREATE TABLE IF NOT EXISTS `notice` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `title` varchar(200) NOT NULL,
+  `description` mediumtext NOT NULL,
+  `register_id` bigint(20) NOT NULL,
+  `modify_id` bigint(20) NOT NULL,
+  `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `modify_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/src/main/resources/migration/V1_6__Alter_Table.sql b/src/main/resources/migration/V1_6__Alter_Table.sql
new file mode 100644
index 0000000..0702934
--- /dev/null
+++ b/src/main/resources/migration/V1_6__Alter_Table.sql
@@ -0,0 +1,59 @@
+ALTER TABLE custom_field modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE custom_field modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE custom_field_value modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE custom_field_value modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_custom_field_value modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_custom_field_value modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_number_generator modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_number_generator modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_reservation modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_reservation modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_risk modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_risk modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_search modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_search modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_table_config modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_table_config modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_type modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_type modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_type_custom_field modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_type_custom_field modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_user modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE issue_user modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE payment_history modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE payment_history modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE project_role modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE project_role modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE project_role_permission modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE project_role_permission modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE project_role_user modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE project_role_user modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE severity modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE severity modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE system_email modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE system_email modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE user_invite modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE user_invite modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE user_invite_project modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE user_invite_project modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE user_like_issue modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE user_like_issue modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE user_with_draw modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE user_with_draw modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE user_workspace modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE user_workspace modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE workflow modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE workflow modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE workflow_transition modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE workflow_transition modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE workspace modify register_date timestamp NULL DEFAULT NULL;
+ALTER TABLE workspace modify modify_date timestamp NULL DEFAULT NULL;
+ALTER TABLE workspace modify start_date timestamp NULL DEFAULT NULL;
+ALTER TABLE workspace modify expire_date timestamp NULL DEFAULT NULL;
+
+INSERT INTO `permission` (`name`, `action`, `role_type`, `register_id`, `register_date`, `modify_id`, `modify_date`) VALUES
+	('怨듭��궗�빆 議고쉶 沅뚰븳', 'NOTICE_READ', '01', 1, '2019-06-12 12:05:23', 1, '2019-06-12 12:05:24');
+
+INSERT INTO `system_role_permission` (`system_role_id`, `permission_id`, `register_id`, `register_date`, `modify_id`, `modify_date`) VALUES
+	(1, 13, 1, '2019-06-12 12:06:35', 1, '2019-06-12 12:06:37'),
+	(2, 13, 1, '2019-06-12 12:06:47', 1, '2019-06-12 12:06:49');
diff --git a/src/main/resources/migration/V1_7__Alter_Table.sql b/src/main/resources/migration/V1_7__Alter_Table.sql
new file mode 100644
index 0000000..58aa81f
--- /dev/null
+++ b/src/main/resources/migration/V1_7__Alter_Table.sql
@@ -0,0 +1 @@
+DROP TABLE login_history;
diff --git a/src/main/resources/migration/V1_8__Alter_Table.sql b/src/main/resources/migration/V1_8__Alter_Table.sql
new file mode 100644
index 0000000..6af0d38
--- /dev/null
+++ b/src/main/resources/migration/V1_8__Alter_Table.sql
@@ -0,0 +1,63 @@
+ALTER TABLE `user` ADD COLUMN `permission` int NULL DEFAULT NULL;
+ALTER TABLE `user` ADD COLUMN `licensekey` varchar(255) NULL DEFAULT NULL;
+
+CREATE TABLE IF NOT EXISTS `faq` (
+	`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+	`title` VARCHAR(200) NOT NULL,
+	`description` MEDIUMTEXT NOT NULL,
+	`register_id` BIGINT(20) NOT NULL,
+	`modify_id` BIGINT(20) NOT NULL,
+	`register_date` TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
+	`modify_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
+    `status` INT(11) NOT NULL,
+	PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE IF NOT EXISTS `qna` (
+	`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+	`title` VARCHAR(200) NOT NULL,
+	`description` MEDIUMTEXT NOT NULL,
+	`register_id` BIGINT(20) NOT NULL,
+	`modify_id` BIGINT(20) NOT NULL,
+	`register_date` TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
+	`modify_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
+	PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE IF NOT EXISTS `qna_answer` (
+	`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+	`ask_id` BIGINT(20) NOT NULL,
+	`title` VARCHAR(200) NOT NULL,
+	`description` MEDIUMTEXT NOT NULL,
+	`register_id` BIGINT(20) NOT NULL,
+	`modify_id` BIGINT(20) NOT NULL,
+	`register_date` TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
+	`modify_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
+	PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE IF NOT EXISTS `event` (
+	`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+	`title` VARCHAR(200) NOT NULL,
+	`description` MEDIUMTEXT NOT NULL,
+	`register_id` BIGINT(20) NOT NULL,
+	`modify_id` BIGINT(20) NOT NULL,
+	`register_date` TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
+	`modify_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
+    `start_date` varchar(10) DEFAULT NULL COMMENT 'start_date',
+    `end_date` varchar(10) DEFAULT NULL COMMENT 'end_date',
+	`status` INT(11) NOT NULL,
+	PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE IF NOT EXISTS `guide` (
+	`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+	`title` VARCHAR(200) NOT NULL,
+	`description` MEDIUMTEXT NOT NULL,
+	`register_id` BIGINT(20) NOT NULL,
+	`modify_id` BIGINT(20) NOT NULL,
+	`register_date` TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
+	`modify_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
+	`status` INT(11) NOT NULL,
+	PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
\ No newline at end of file
diff --git a/src/main/resources/migration/V1_9__Alter_Table.sql b/src/main/resources/migration/V1_9__Alter_Table.sql
new file mode 100644
index 0000000..4217a77
--- /dev/null
+++ b/src/main/resources/migration/V1_9__Alter_Table.sql
@@ -0,0 +1,70 @@
+INSERT INTO `system_role` (`id`, `name`, `role_type`, `register_id`, `register_date`, `modify_id`, `modify_date`) VALUES
+(3, '�봽濡쒖젥�듃 �궗�슜�옄 �뿭�븷', '03', 1, '2017-12-29 21:37:15', 1, '2017-12-29 21:37:15'),
+(4, '�봽濡쒖젥�듃 愿�由ъ옄 �뿭�븷', '04', 1, '2017-12-29 21:37:15', 1, '2017-12-29 21:37:15');
+
+DELETE FROM `system_role_permission` WHERE id = '5';
+DELETE FROM `system_role_permission` WHERE id = '6';
+DELETE FROM `system_role_permission` WHERE id = '7';
+DELETE FROM `system_role_permission` WHERE id = '8';
+
+CREATE TABLE `workflow_status` (
+   `id` INT(11) NOT NULL AUTO_INCREMENT,
+   `project_id` BIGINT(20) NULL,
+   `name` VARCHAR(255) NOT NULL DEFAULT '' COLLATE 'utf8_general_ci',
+   `first_yn` VARCHAR(1) NOT NULL DEFAULT 'N' COLLATE 'utf8_general_ci',
+   `last_yn` VARCHAR(1) NOT NULL DEFAULT 'N' COLLATE 'utf8_general_ci',
+   `progress` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0' COMMENT '吏꾪뻾瑜�',
+   `color` VARCHAR(255) NOT NULL DEFAULT '0' COLLATE 'utf8_general_ci',
+   `position` BIGINT(20) NOT NULL DEFAULT '0',
+   `register_id` BIGINT(20) NOT NULL COMMENT 'register_id',
+   `register_date` TIMESTAMP NULL COMMENT 'register_date',
+   `modify_id` BIGINT(20) NOT NULL COMMENT 'modify_id',
+   `modify_date` TIMESTAMP NULL COMMENT 'modify_date',
+   PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+ALTER TABLE `user` ADD COLUMN `last_project_id` bigint(20) NULL DEFAULT '0';
+ALTER TABLE `user` ADD COLUMN `last_login_date` TIMESTAMP NULL;
+ALTER TABLE `issue` ADD COLUMN `workflow_status_id` bigint(20) NULL DEFAULT NULL;
+
+/* �봽濡쒖젥�듃 �겢濡쒖� �뀒�씠釉� */
+CREATE TABLE `project_closure` (
+   `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+   `project_id` BIGINT(20) NOT NULL,
+   `parent_project_id` BIGINT(20) NOT NULL DEFAULT -1,
+   `register_id` BIGINT(20) NOT NULL COMMENT 'register_id',
+   `register_date` TIMESTAMP NULL COMMENT 'register_date',
+   `modify_id` BIGINT(20) NOT NULL COMMENT 'modify_id',
+   `modify_date` TIMESTAMP NULL COMMENT 'modify_date',
+   PRIMARY KEY (`id`) USING BTREE,
+   INDEX `projectIdIndex` (`project_id`) USING BTREE,
+   INDEX `parentProjectIdIndex` (`parent_project_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+/* �뿰愿� �씠�뒋 �뀒�씠釉� */
+CREATE TABLE `issue_relation` (
+   `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+   `issue_id` BIGINT(20) NOT NULL,
+   `relation_issue_id` BIGINT(20) NOT NULL DEFAULT -1,
+   `relation_issue_type` INT(20) NOT NULL,
+   `register_id` BIGINT(20) NOT NULL COMMENT 'register_id',
+   `register_date` TIMESTAMP NULL COMMENT 'register_date',
+   `modify_id` BIGINT(20) NOT NULL COMMENT 'modify_id',
+   `modify_date` TIMESTAMP NULL COMMENT 'modify_date',
+   PRIMARY KEY (`id`) USING BTREE,
+   INDEX `issueId` (`issue_id`) USING BTREE,
+   INDEX `relationIssueId` (`relation_issue_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+/* �쑀�� 濡쒓렇�씤 �씠�젰 �뀒�씠釉� (�엳�뒪�넗由� 遺덊븘�슂) */
+-- CREATE TABLE `user_history` (
+--    `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+--    `history_type` VARCHAR (20) NOT NULL,
+--    `register_id` BIGINT(20) NOT NULL COMMENT 'register_id',
+--    `register_date` TIMESTAMP NULL COMMENT 'register_date',
+--    `modify_id` BIGINT(20) NOT NULL COMMENT 'modify_id',
+--    `modify_date` TIMESTAMP NULL COMMENT 'modify_date',
+--    PRIMARY KEY (`id`) USING BTREE
+-- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+ALTER TABLE `custom_field` ADD COLUMN `use_flag` varchar(1) NOT NULL DEFAULT 'Y';
\ No newline at end of file
diff --git a/src/main/resources/mybatis/config/mybatis-config.xml b/src/main/resources/mybatis/config/mybatis-config.xml
new file mode 100644
index 0000000..1dce3d6
--- /dev/null
+++ b/src/main/resources/mybatis/config/mybatis-config.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE configuration
+        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-config.dtd">
+<configuration>
+    <typeHandlers>
+        <typeHandler handler="kr.wisestone.owl.domain.support.ZonedDateTimeTypeHandler"/>
+    </typeHandlers>
+</configuration>
diff --git a/src/main/resources/mybatis/query-template/attachedFile-template.xml b/src/main/resources/mybatis/query-template/attachedFile-template.xml
new file mode 100644
index 0000000..a09b227
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/attachedFile-template.xml
@@ -0,0 +1,61 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.AttachedFileMapper">
+    <select id="findUseStorage" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.AttachedFileCondition">
+        SELECT SUM(af.size)
+        FROM
+        attached_file af
+        WHERE af.workspace_id = #{workspaceId}
+    </select>
+
+    <!--    �뾽臾� 怨듦컙�뿉 �엳�뒗 紐⑤뱺 �씠�뒋 泥⑤� �뙆�씪�쓣 議고쉶�븳�떎.    -->
+    <select id="findByWorkspaceId" resultType="java.util.HashMap" parameterType="java.lang.Long">
+        SELECT id, aws_key as awsKey, workspace_id as workspaceId
+        FROM
+        attached_file af
+        WHERE af.workspace_id = #{workspaceId}
+    </select>
+
+    <!--    �뾽臾� 怨듦컙�뿉 �엳�뒗 紐⑤뱺 �씠�뒋 泥⑤� �뙆�씪 �궘�젣 -->
+    <delete id="deleteAttachedFileByWorkspaceId" parameterType="java.lang.Long">
+        DELETE FROM attached_file WHERE workspace_id = #{workspaceId};
+    </delete>
+
+    <!--    �봽濡쒖젥�듃�뿉 �엳�뒗 紐⑤뱺 �씠�뒋 泥⑤� �뙆�씪�쓣 議고쉶�븳�떎.    -->
+    <select id="findByIssueIds" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.AttachedFileCondition">
+        SELECT id, aws_key as awsKey, workspace_id as workspaceId
+        FROM
+        attached_file
+        WHERE
+        <choose>
+            <when test="issueIds.size != 0">
+                issue_id IN
+                <foreach collection="issueIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND workspace_id = #{workspaceId};
+    </select>
+
+    <!--    �봽濡쒖젥�듃�뿉 �엳�뒗 紐⑤뱺 �씠�뒋 泥⑤� �뙆�씪 �궘�젣 -->
+    <delete id="deleteAttachedFileByIssueIds" parameterType="kr.wisestone.owl.web.condition.AttachedFileCondition">
+        DELETE FROM attached_file WHERE
+        <choose>
+            <when test="issueIds.size != 0">
+                issue_id IN
+                <foreach collection="issueIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND workspace_id = #{workspaceId};
+    </delete>
+
+    <!--    �씠�뒋�� �뿰寃곕릺吏� �븡�� �엫�떆 泥⑤� �뙆�씪�쓣 �궘�젣�븳�떎. -->
+    <delete id="deleteAttachedFileNotId">
+        DELETE FROM attached_file WHERE issue_id IS NULL AND attached_type = 'TEMP_SUMMER';
+    </delete>
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/mybatis/query-template/customField-template.xml b/src/main/resources/mybatis/query-template/customField-template.xml
new file mode 100644
index 0000000..e3cd4cc
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/customField-template.xml
@@ -0,0 +1,54 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.CustomFieldMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.CustomFieldCondition">
+        SELECT
+        DISTINCT cf.id as id,
+        cf.name as name,
+        cf.custom_field_type as customFieldType,
+        cf.default_value as defaultValue
+        FROM
+        custom_field cf
+        INNER JOIN workspace ws on cf.workspace_id = ws.id
+        WHERE cf.use_flag = 'Y'
+        <if test="name != '' and name != null">
+            AND cf.name like CONCAT('%',#{name},'%')
+        </if>
+        <choose>
+            <when test="customFieldTypes.size != 0">
+                AND cf.custom_field_type IN
+                <foreach collection="customFieldTypes" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND ws.id = #{workspaceId}
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.CustomFieldCondition">
+        SELECT
+        COUNT(DISTINCT cf.id)
+        FROM
+        custom_field cf
+        INNER JOIN workspace ws on cf.workspace_id = ws.id
+        WHERE cf.use_flag = 'Y'
+        <if test="name != '' and name != null">
+            AND cf.name like CONCAT('%',#{name},'%')
+        </if>
+        <choose>
+            <when test="customFieldTypes.size != 0">
+                AND cf.custom_field_type IN
+                <foreach collection="customFieldTypes" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND ws.id = #{workspaceId}
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/mybatis/query-template/event-template.xml b/src/main/resources/mybatis/query-template/event-template.xml
new file mode 100644
index 0000000..d221116
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/event-template.xml
@@ -0,0 +1,39 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.EventMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.EventCondition">
+        SELECT
+        DISTINCT n.id as id,
+        n.title as title,
+        n.description as description,
+        n.register_id as registerId,
+        n.status as status,
+        n.start_date as startDate,
+        n.end_date as endDate,
+        u.name as writer,
+        SUBSTRING (n.register_date, 1, 10) as registerDate
+        FROM event n
+        INNER JOIN user u on u.id = n.register_id
+        WHERE 1=1
+        <if test="title != '' and title != null">
+            AND n.title like CONCAT('%',#{title},'%')
+        </if>
+        ORDER BY ID DESC
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.EventCondition">
+        SELECT
+        count(DISTINCT n.id)
+        FROM event n
+        WHERE 1=1
+        <if test="title != '' and title != null">
+            AND n.title like CONCAT('%',#{title},'%')
+        </if>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mybatis/query-template/faq-template.xml b/src/main/resources/mybatis/query-template/faq-template.xml
new file mode 100644
index 0000000..3da048a
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/faq-template.xml
@@ -0,0 +1,37 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.FaqMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.FaqCondition">
+        SELECT
+        DISTINCT n.id as id,
+        n.title as title,
+        n.description as description,
+        n.register_id as registerId,
+        n.status as status,
+        u.name as writer,
+        SUBSTRING (n.register_date, 1, 10) as registerDate
+        FROM faq n
+        INNER JOIN user u on u.id = n.register_id
+        WHERE 1=1
+        <if test="title != '' and title != null">
+            AND n.title like CONCAT('%',#{title},'%')
+        </if>
+        ORDER BY ID DESC
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.FaqCondition">
+        SELECT
+        count(DISTINCT n.id)
+        FROM faq n
+        WHERE 1=1
+        <if test="title != '' and title != null">
+            AND n.title like CONCAT('%',#{title},'%')
+        </if>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mybatis/query-template/guide-template.xml b/src/main/resources/mybatis/query-template/guide-template.xml
new file mode 100644
index 0000000..75b976a
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/guide-template.xml
@@ -0,0 +1,37 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.GuideMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.GuideCondition">
+        SELECT
+        DISTINCT n.id as id,
+        n.title as title,
+        n.description as description,
+        n.register_id as registerId,
+        n.status as status,
+        u.name as writer,
+        SUBSTRING (n.register_date, 1, 10) as registerDate
+        FROM guide n
+        INNER JOIN user u on u.id = n.register_id
+        WHERE 1=1
+        <if test="title != '' and title != null">
+            AND n.title like CONCAT('%',#{title},'%')
+        </if>
+        ORDER BY ID DESC
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.GuideCondition">
+        SELECT
+        count(DISTINCT n.id)
+        FROM guide n
+        WHERE 1=1
+        <if test="title != '' and title != null">
+            AND n.title like CONCAT('%',#{title},'%')
+        </if>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mybatis/query-template/issue-template.xml b/src/main/resources/mybatis/query-template/issue-template.xml
new file mode 100644
index 0000000..9e7f592
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/issue-template.xml
@@ -0,0 +1,426 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.IssueMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.IssueCondition">
+        SELECT
+        DISTINCT issue.id as id,
+        issue.register_id as registerId,
+        issue.reverse_index as reverseIndex,
+        issue.title as title,
+        issue.description as description,
+        issue.start_date as startDate,
+        issue.complete_date as completeDate,
+        issue.issue_number as issueNumber,
+        issue.register_date as registerDate,
+        SUBSTRING(issue.modify_date, 1, 19) as modifyDate,
+        project.id as projectId,
+        project.name as projectName,
+        project.project_key as projectKey,
+        issue_type.id as issueTypeId,
+        issue_type.name as issueTypeName,
+        issue_status.id as issueStatusId,
+        issue_status.issue_status_type as issueStatusType,
+        issue_status.name as issueStatusName,
+        issue_status.color as issueStatusColor,
+        priority.id as priorityId,
+        priority.name as priorityName,
+        priority.color as priorityColor,
+        severity.id as severityId,
+        severity.name as severityName,
+        severity.color as severityColor,
+        IFNULL(temp_attached_file.attachedFileCount, 0) as attachedFileCount,
+        IFNULL(temp_issue_comment.issueCommentCount, 0) as issueCommentCount
+        FROM issue issue FORCE INDEX(reverseIndex)
+        INNER JOIN project project FORCE INDEX(workspaceIdIndex) ON issue.project_id = project.id
+        INNER JOIN workspace workspace ON workspace.id = project.workspace_id
+        INNER JOIN issue_status issue_status FORCE INDEX(PRIMARY) ON issue.issue_status_id = issue_status.id
+        INNER JOIN issue_type issue_type FORCE INDEX(PRIMARY) ON issue.issue_type_id = issue_type.id
+        INNER JOIN priority priority FORCE INDEX(PRIMARY) ON issue.priority_id = priority.id
+        INNER JOIN severity severity FORCE INDEX(PRIMARY) ON issue.severity_id = severity.id
+        LEFT OUTER JOIN issue_user issue_user FORCE INDEX(issueIdIndex) ON issue.id = issue_user.issue_id
+        LEFT OUTER JOIN (SELECT issue_id, COUNT(id) as attachedFileCount FROM attached_file GROUP BY issue_id)
+        temp_attached_file on (temp_attached_file.issue_id = issue.id)
+        LEFT OUTER JOIN (SELECT issue_id, COUNT(id) as issueCommentCount FROM issue_comment GROUP BY issue_id)
+        temp_issue_comment on (temp_issue_comment.issue_id = issue.id)
+        LEFT OUTER JOIN user user ON issue_user.user_id = user.id
+        WHERE 1=1
+        <if test="title != null and !title.equals('') ">
+            AND issue.title like CONCAT('%',#{title},'%')
+        </if>
+
+        <if test="description != null and !description.equals('')">
+            AND issue.description like CONCAT('%',#{description},'%')
+        </if>
+
+        <if test="issueNumber != null and !issueNumber.equals('')">
+            AND issue.issue_number = #{issueNumber}
+        </if>
+
+        <if test="beginRegisterDate != null and !beginRegisterDate.equals('')">
+            ANd issue.register_date >= #{beginRegisterDate}
+        </if>
+
+        <if test="endRegisterDate != null and !endRegisterDate.equals('')">
+            ANd issue.register_date <![CDATA[ <= ]]> #{endRegisterDate}
+        </if>
+
+        <if test="beginStartDate != null and !beginStartDate.equals('')">
+            ANd issue.start_date >= #{beginStartDate}
+        </if>
+
+        <if test="endStartDate != null and !endStartDate.equals('')">
+            ANd issue.start_date <![CDATA[ <= ]]> #{endStartDate}
+        </if>
+
+        <if test="beginCompleteDate != null and !beginCompleteDate.equals('')">
+            ANd issue.complete_date >= #{beginCompleteDate}
+        </if>
+
+        <if test="endCompleteDate != null and !endCompleteDate.equals('')">
+            ANd issue.complete_date <![CDATA[ <= ]]> #{endCompleteDate}
+        </if>
+
+        <choose>
+            <when test="projectIds.size != 0">
+                AND project.id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="issueStatusIds.size != 0">
+                AND issue_status.id IN
+                <foreach collection="issueStatusIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="issueTypeIds.size != 0">
+                AND issue_type.id IN
+                <foreach collection="issueTypeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="priorityIds.size != 0">
+                AND priority.id IN
+                <foreach collection="priorityIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="severityIds.size != 0">
+                AND severity.id IN
+                <foreach collection="severityIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="userIds.size != 0">
+                AND issue_user.user_id IN
+                <foreach collection="userIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="registerIds.size != 0">
+                AND issue.register_id IN
+                <foreach collection="registerIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="issueIds.size != 0">
+                AND issue.id IN
+                <foreach collection="issueIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND issue.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND issue.reverse_index <![CDATA[ < ]]> 0
+        AND workspace.id = #{workspaceId}
+        ORDER BY issue.register_date DESC
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.IssueCondition">
+        SELECT
+        COUNT(DISTINCT issue.id)
+        FROM issue issue
+        LEFT OUTER JOIN issue_user issue_user ON issue.id = issue_user.issue_id
+        WHERE 1=1
+        <if test="title != null and !title.equals('') ">
+            AND issue.title like CONCAT('%',#{title},'%')
+        </if>
+
+        <if test="description != null and !description.equals('')">
+            AND issue.description like CONCAT('%',#{description},'%')
+        </if>
+
+        <if test="issueNumber != null and !issueNumber.equals('')">
+            AND issue.issue_number = #{issueNumber}
+        </if>
+
+        <if test="beginRegisterDate != null and !beginRegisterDate.equals('')">
+            ANd issue.register_date >= #{beginRegisterDate}
+        </if>
+
+        <if test="endRegisterDate != null and !endRegisterDate.equals('')">
+            ANd issue.register_date <![CDATA[ <= ]]> #{endRegisterDate}
+        </if>
+
+        <if test="beginStartDate != null and !beginStartDate.equals('')">
+            ANd issue.start_date >= #{beginStartDate}
+        </if>
+
+        <if test="endStartDate != null and !endStartDate.equals('')">
+            ANd issue.start_date <![CDATA[ <= ]]> #{endStartDate}
+        </if>
+
+        <if test="beginCompleteDate != null and !beginCompleteDate.equals('')">
+            ANd issue.complete_date >= #{beginCompleteDate}
+        </if>
+
+        <if test="endCompleteDate != null and !endCompleteDate.equals('')">
+            ANd issue.complete_date <![CDATA[ <= ]]> #{endCompleteDate}
+        </if>
+
+        <choose>
+            <when test="projectIds.size != 0">
+                AND issue.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="issueStatusIds.size != 0">
+                AND issue.issue_status_id IN
+                <foreach collection="issueStatusIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="issueTypeIds.size != 0">
+                AND issue.issue_type_id IN
+                <foreach collection="issueTypeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="priorityIds.size != 0">
+                AND issue.priority_id IN
+                <foreach collection="priorityIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="severityIds.size != 0">
+                AND issue.severity_id IN
+                <foreach collection="severityIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="userIds.size != 0">
+                AND issue_user.user_id IN
+                <foreach collection="userIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="registerIds.size != 0">
+                AND issue.register_id IN
+                <foreach collection="registerIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="issueIds.size != 0">
+                AND issue.id IN
+                <foreach collection="issueIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND issue.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <!--    �씠�뒋 �쑀�삎�쓣 �궗�슜�븯�뒗 �씠�뒋 紐⑸줉�쓣 議고쉶�븳�떎 -->
+    <select id="findByIssueTypeId" resultType="java.util.HashMap"
+            parameterType="java.lang.Long">
+        SELECT
+        i.id as issueId,
+        iss.id as issueStatusId,
+        iss.name as issueStatusName
+        FROM issue i
+        INNER JOIN issue_status iss on iss.id = i.issue_status_id
+        WHERE i.issue_type_id = #{issueTypeId}
+    </select>
+
+    <!--    �봽濡쒖젥�듃�뿉 �엳�뒗 �씠�뒋 紐⑸줉�쓣 議고쉶�븳�떎 -->
+    <select id="findByProjectId" resultType="java.util.HashMap"
+            parameterType="java.lang.Long">
+        SELECT
+        id
+        FROM issue
+        WHERE project_id = #{projectId}
+    </select>
+
+    <insert id="insertBatch" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
+        INSERT INTO issue(title, description, issue_number, project_id, issue_type_id, priority_id, severity_id,
+        start_date, complete_date,
+        issue_status_id, register_id, modify_id, register_date, modify_date)
+        VALUES
+        <foreach collection="list" item="issueForm" index="index" separator="," open="" close="">
+            (#{issueForm.title}, #{issueForm.description}, #{issueForm.issueNumber}, #{issueForm.projectId},
+            #{issueForm.issueTypeId}, #{issueForm.priorityId}, #{issueForm.severityId}, #{issueForm.startDate},
+            #{issueForm.completeDate},
+            #{issueForm.issueStatusId}, #{issueForm.registerId}, #{issueForm.registerId}, NOW(), NOW())
+        </foreach>
+    </insert>
+
+    <update id="updateBatch">
+        <foreach collection="list" item="issueForm" index="index" separator=";">
+            UPDATE issue SET reverse_index = (#{issueForm.id} * -1) WHERE id=#{issueForm.id}
+        </foreach>
+    </update>
+
+    <!--    �씠�뒋 �씠�젰 bulk insert, import �뿉�꽌 �궗�슜 -->
+    <insert id="insertHistoryBatch" keyColumn="id" keyProperty="id" useGeneratedKeys="true"
+            parameterType="java.util.HashMap">
+        INSERT INTO issue_history(issue_id, project_id, issue_history_type, description, register_id, modify_id,
+        register_date, modify_date)
+        VALUES
+        <foreach collection="list" item="map" index="index" separator="," open="" close="">
+            (#{map.issueId}, #{map.projectId}, #{map.issueHistoryType}, #{map.description}, #{map.registerId},
+            #{map.registerId}, NOW(), NOW())
+        </foreach>
+    </insert>
+
+    <!--    �씠�뒋 由ъ뒪�겕 bulk insert, import �뿉�꽌 �궗�슜 -->
+    <insert id="insertIssueRiskBatch" keyColumn="id" keyProperty="id" useGeneratedKeys="true"
+            parameterType="java.util.HashMap">
+        INSERT INTO issue_risk(issue_id, workspace_id, change_assignee_count, change_issue_status_count,
+        issue_status_ids,
+        register_id, modify_id, register_date, modify_date)
+        VALUES
+        <foreach collection="list" item="map" index="index" separator="," open="" close="">
+            (#{map.issueId}, #{map.workspaceId}, #{map.changeAssigneeCount}, #{map.changeIssueStatusCount},
+            #{map.issueStatusIds},
+            #{map.registerId}, #{map.registerId}, NOW(), NOW())
+        </foreach>
+    </insert>
+
+
+    <!--    �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶 媛� bulk insert, import �뿉�꽌 �궗�슜 -->
+    <insert id="insertIssueCustomFieldValueBatch" keyColumn="id" keyProperty="id" useGeneratedKeys="true"
+            parameterType="java.util.HashMap">
+        INSERT INTO issue_custom_field_value(issue_id, issue_type_custom_field_id, custom_field_id, use_value, register_id, modify_id,
+        register_date, modify_date)
+        VALUES
+        <foreach collection="list" item="map" index="index" separator="," open="" close="">
+            (#{map.issueId}, #{map.issueTypeCustomFieldId}, #{map.customFieldId}, #{map.useValue}, #{map.registerId}, #{map.registerId}, NOW(), NOW())
+        </foreach>
+    </insert>
+
+    <select id="findByProjectIdIn" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.IssueCondition">
+        SELECT
+        id,
+        title
+        FROM issue FORCE INDEX(projectIdIndex)
+        WHERE 1=1
+        <choose>
+            <when test="projectIds.size != 0">
+                AND project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <select id="findIssueUser" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.IssueCondition">
+        SELECT
+        issue.id AS issueId,
+        user.id AS id,
+        user.name AS name,
+        user.account AS account,
+        user.profile AS profile
+        FROM issue issue
+        INNER JOIN issue_user issue_user ON issue_user.issue_id = issue.id
+        INNER JOIN user user ON user.id = issue_user.user_id
+        WHERE 1=1
+        <choose>
+            <when test="issueIds.size != 0">
+                AND issue.id IN
+                <foreach collection="issueIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <!--    �씠�뒋 �쑀�삎�쓣 �궗�슜�븯�뒗 �씠�뒋 媛��닔瑜� 議고쉶�븳�떎 -->
+    <select id="countByIssueTypeId" resultType="java.lang.Long" parameterType="java.lang.Long">
+      SELECT COUNT(DISTINCT id) FROM
+      issue WHERE issue_type_id = #{issueTypeId};
+    </select>
+
+
+    <!--    �씠�뒋 �긽�깭瑜� �궗�슜�븯�뒗 �씠�뒋 媛��닔瑜� 議고쉶�븳�떎. -->
+    <select id="countByIssueStatusId" resultType="java.lang.Long" parameterType="java.lang.Long">
+        SELECT COUNT(DISTINCT id) FROM
+        issue WHERE issue_status_id = #{issueStatusId};
+    </select>
+
+
+</mapper>
diff --git a/src/main/resources/mybatis/query-template/issueCustomFieldValue-template.xml b/src/main/resources/mybatis/query-template/issueCustomFieldValue-template.xml
new file mode 100644
index 0000000..1003c6d
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/issueCustomFieldValue-template.xml
@@ -0,0 +1,57 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.IssueCustomFieldValueMapper">
+
+    <select id="findLikeUseValue" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.IssueCustomFieldValueCondition">
+        SELECT
+        GROUP_CONCAT(issue_custom_field_value.issue_id SEPARATOR ',') as issueIds
+        FROM issue_custom_field_value issue_custom_field_value
+        WHERE issue_custom_field_value.custom_field_id = #{customFieldId}
+        AND issue_custom_field_value.use_value LIKE CONCAT('%',#{useValue},'%')
+    </select>
+
+    <select id="findByUseValue" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.IssueCustomFieldValueCondition">
+        SELECT
+        GROUP_CONCAT(issue_custom_field_value.issue_id SEPARATOR ',') as issueIds
+        FROM issue_custom_field_value issue_custom_field_value
+        WHERE issue_custom_field_value.custom_field_id = #{customFieldId}
+        <choose>
+            <when test="useValues.size != 0">
+                AND issue_custom_field_value.use_value IN
+                <foreach collection="useValues" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <delete id="deleteIssueCustomFieldValue" parameterType="java.lang.Long">
+        DELETE FROM issue_custom_field_value WHERE issue_type_custom_field_id = #{issueTypeCustomFieldId}
+    </delete>
+
+    <select id="findInIssueIds" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.IssueCondition">
+        SELECT
+        issue_custom_field_value.issue_id as issueId,
+        issue_custom_field_value.custom_field_id as customFieldId,
+        issue_custom_field_value.use_value as useValue
+        FROM issue_custom_field_value issue_custom_field_value
+        WHERE 1=1
+        <choose>
+            <when test="issueIds.size != 0">
+                AND issue_custom_field_value.issue_id IN
+                <foreach collection="issueIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <delete id="deleteByIssueCustomFieldValueId" parameterType="java.util.ArrayList">
+        DELETE FROM issue_custom_field_value WHERE id IN (
+        <foreach collection="list" item="item" index="index" separator="," open="" close="">
+            #{item}
+        </foreach>
+        )
+    </delete>
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/mybatis/query-template/issueHistory-template.xml b/src/main/resources/mybatis/query-template/issueHistory-template.xml
new file mode 100644
index 0000000..6d1ca3b
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/issueHistory-template.xml
@@ -0,0 +1,26 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.IssueHistoryMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.IssueHistoryCondition">
+        SELECT
+        DISTINCT ih.id as id,
+        p.name as projectName,
+        p.project_key as projectKey,
+        i.issue_number as issueNumber,
+        i.title as issueTitle,
+        ih.issue_history_type as issueHistoryType,
+        SUBSTRING(ih.register_date, 1, 10) as registerDate,
+        SUBSTRING(ih.register_date, 12, 16) as registerTime,
+        ih.description as description
+        FROM
+        issue_history ih
+        INNER JOIN project p on ih.project_id = p.id
+        INNER JOIN issue i on ih.issue_id = i.id
+        WHERE 1=1
+        AND ih.register_id = #{userId}
+        AND ih.register_date between #{searchStartDate} and #{searchEndDate}
+        ORDER BY ih.register_date DESC
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/mybatis/query-template/issueStatus-template.xml b/src/main/resources/mybatis/query-template/issueStatus-template.xml
new file mode 100644
index 0000000..3dbf427
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/issueStatus-template.xml
@@ -0,0 +1,55 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.IssueStatusMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.IssueStatusCondition">
+        SELECT
+        DISTINCT i.id as id,
+        i.name as name,
+        i.color as color,
+        i.issue_status_type as issueStatusType,
+        CASE i.default_yn WHEN 'Y' THEN 'true' ELSE 'false' END as defaultYn
+        FROM
+        issue_status i
+        INNER JOIN workspace ws on i.workspace_id = ws.id
+        WHERE 1=1
+        <if test="name != '' and name != null">
+            AND i.name like CONCAT('%',#{name},'%')
+        </if>
+        <choose>
+            <when test="issueStatusTypes.size != 0">
+                AND i.issue_status_type IN
+                <foreach collection="issueStatusTypes" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND ws.id = #{workspaceId}
+        ORDER BY i.name DESC
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.IssueStatusCondition">
+        SELECT
+        count(DISTINCT i.id)
+        FROM issue_status i
+        INNER JOIN workspace ws on i.workspace_id = ws.id
+        WHERE 1=1
+        <if test="name != '' and name != null">
+            AND i.name like CONCAT('%',#{name},'%')
+        </if>
+        <choose>
+            <when test="issueStatusTypes.size != 0">
+                AND i.issue_status_type IN
+                <foreach collection="issueStatusTypes" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND ws.id = #{workspaceId}
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/mybatis/query-template/issueType-template.xml b/src/main/resources/mybatis/query-template/issueType-template.xml
new file mode 100644
index 0000000..8beb810
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/issueType-template.xml
@@ -0,0 +1,38 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.IssueTypeMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.IssueTypeCondition">
+        SELECT
+        DISTINCT it.id as id,
+        it.name as name,
+        it.description as description,
+        it.color as color
+        FROM
+        issue_type it
+        INNER JOIN workspace ws on it.workspace_id = ws.id
+        WHERE 1=1
+        <if test="name != '' and name != null">
+            AND it.name like CONCAT('%',#{name},'%')
+        </if>
+        AND ws.id = #{workspaceId}
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.IssueTypeCondition">
+        SELECT
+        COUNT(DISTINCT it.id)
+        FROM
+        issue_type it
+        INNER JOIN workspace ws on it.workspace_id = ws.id
+        WHERE 1=1
+        <if test="name != '' and name != null">
+            AND it.name like CONCAT('%',#{name},'%')
+        </if>
+        AND ws.id = #{workspaceId}
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/mybatis/query-template/issueUser-template.xml b/src/main/resources/mybatis/query-template/issueUser-template.xml
new file mode 100644
index 0000000..9db501f
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/issueUser-template.xml
@@ -0,0 +1,45 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.IssueUserMapper">
+
+    <!--    �씠�뒋 �떞�떦�옄 bulk insert, import, modify, add �뿉�꽌 �궗�슜 -->
+    <insert id="insertIssueUser" keyColumn="id" keyProperty="id" useGeneratedKeys="true"
+            parameterType="java.util.HashMap">
+        INSERT INTO issue_user(user_id, issue_id, workspace_id, register_id, modify_id, register_date, modify_date)
+        VALUES
+        <foreach collection="list" item="map" index="index" separator="," open="" close="">
+            (#{map.userId}, #{map.issueId}, #{map.workspaceId}, #{map.registerId}, #{map.registerId}, NOW(), NOW())
+        </foreach>
+    </insert>
+
+    <!--    �씠�뒋 �떞�떦�옄 bulk �궘�젣 - modify, add �뿉�꽌 �궗�슜  -->
+    <delete id="deleteIssueUserByIssueIdAndMultiUserId" parameterType="java.util.HashMap">
+        DELETE FROM issue_user WHERE issue_id = #{issueId} AND user_id IN (
+        <foreach collection="userIds" item="item" index="index" separator="," open="" close="">
+            #{item}
+        </foreach>
+        )
+    </delete>
+
+    <delete id="deleteIssueUserByUserIdAndMultiIssueId" parameterType="java.util.HashMap">
+        DELETE FROM issue_user WHERE user_id = #{userId} AND issue_id IN (
+        <foreach collection="issueIds" item="item" index="index" separator="," open="" close="">
+            #{item}
+        </foreach>
+        )
+    </delete>
+
+
+
+
+    <!--    �씠�뒋 �떞�떦�옄瑜� 議고쉶�븳�떎 -->
+    <select id="findByUserIdAndProjectId" resultType="java.util.HashMap" parameterType="java.util.HashMap">
+      SELECT DISTINCT(i.id) FROM issue i
+        INNER JOIN issue_user iu ON iu.issue_id = i.id
+        INNER JOIN user u ON u.id = iu.user_id
+        WHERE i.project_id = #{projectId} AND u.id = #{userId}
+    </select>
+
+
+</mapper>
diff --git a/src/main/resources/mybatis/query-template/notice-template.xml b/src/main/resources/mybatis/query-template/notice-template.xml
new file mode 100644
index 0000000..28024a8
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/notice-template.xml
@@ -0,0 +1,34 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.NoticeMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.NoticeCondition">
+        SELECT
+        DISTINCT n.id as id,
+        n.title as title,
+        n.description as description,
+        n.register_id as registerId,
+        SUBSTRING (n.register_date, 1, 10) as registerDate
+        FROM notice n
+        WHERE 1=1
+        <if test="title != '' and title != null">
+            AND n.title like CONCAT('%',#{title},'%')
+        </if>
+        ORDER BY ID DESC
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.NoticeCondition">
+        SELECT
+        count(DISTINCT n.id)
+        FROM notice n
+        WHERE 1=1
+        <if test="title != '' and title != null">
+            AND n.title like CONCAT('%',#{title},'%')
+        </if>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mybatis/query-template/project-template.xml b/src/main/resources/mybatis/query-template/project-template.xml
new file mode 100644
index 0000000..c896e98
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/project-template.xml
@@ -0,0 +1,426 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.ProjectMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.ProjectCondition">
+        SELECT
+        DISTINCT p.id as id,
+        p.name as name,
+        p.description as description,
+        p.status as status,
+        p.start_date as startDate,
+        p.end_date as endDate,
+        p.project_key as projectKey,
+        pc.parent_project_id as parentProjectId,
+        CASE p.default_yn WHEN 'Y' THEN 'true' ELSE 'false' END as defaultYn
+        FROM
+        project p
+        INNER JOIN project_role pr on p.id = pr.project_id
+        INNER JOIN project_role_user pru on pru.project_role_id = pr.id
+        INNER JOIN workspace ws on ws.id = p.workspace_id
+        LEFT JOIN project_closure pc ON p.id = pc.project_id
+        WHERE if (pc.parent_project_id > -1, pc.parent_project_id, -1) = -1
+              AND pru.user_id = #{loginUserId}
+        <if test="name != '' and name != null">
+            AND p.name like CONCAT('%',#{name},'%')
+        </if>
+
+        <choose>
+            <when test="roleTypes.size != 0">
+                AND pr.role_type IN
+                <foreach collection="roleTypes" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="statuses.size != 0">
+                AND p.status IN
+                <foreach collection="statuses" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND p.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND ws.id = #{workspaceId}
+        ORDER BY p.id desc
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.ProjectCondition">
+        SELECT
+        COUNT(DISTINCT p.id)
+        FROM
+        project p
+        INNER JOIN project_role pr on p.id = pr.project_id
+        INNER JOIN project_role_user pru on pru.project_role_id = pr.id
+        INNER JOIN workspace ws on ws.id = p.workspace_id
+        LEFT JOIN project_closure pc on p.id = pc.project_id
+        WHERE pru.user_id = #{loginUserId}
+        AND if (pc.parent_project_id > -1, pc.parent_project_id, -1) = -1
+        <if test="name != '' and name != null">
+            AND p.name like CONCAT('%',#{name},'%')
+        </if>
+
+        <choose>
+            <when test="roleTypes.size != 0">
+                AND pr.role_type IN
+                <foreach collection="roleTypes" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="statuses.size != 0">
+                AND p.status IN
+                <foreach collection="statuses" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND p.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND ws.id = #{workspaceId}
+    </select>
+
+    <select id="findByWorkspaceManager" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.ProjectCondition">
+        SELECT
+        p.id as id,
+        p.name as name,
+        p.description as description,
+        p.status as status,
+        p.start_date as startDate,
+        p.end_date as endDate,
+        p.project_key as projectKey,
+        pc.parent_project_id as parentProjectId,
+        CASE p.default_yn WHEN 'Y' THEN 'true' ELSE 'false' END as defaultYn
+        FROM
+        project p
+        LEFT JOIN project_closure pc ON p.id = pc.project_id
+        WHERE if (pc.parent_project_id > -1, pc.parent_project_id, -1) = -1
+        <if test="name != '' and name != null">
+            AND p.name like CONCAT('%',#{name},'%')
+        </if>
+        <choose>
+            <when test="statuses.size != 0">
+                AND p.status IN
+                <foreach collection="statuses" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND p.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND p.workspace_id = #{workspaceId}
+        <if test="page != null and !page.equals('')">
+            ORDER BY p.id desc
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+
+    <select id="findByWorkspaceManagerAll" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.ProjectCondition">
+        SELECT
+        p.id as id,
+        p.name as name,
+        p.description as description,
+        p.status as status,
+        p.start_date as startDate,
+        p.end_date as endDate,
+        p.project_key as projectKey,
+        pc.parent_project_id as parentProjectId,
+        CASE p.default_yn WHEN 'Y' THEN 'true' ELSE 'false' END as defaultYn
+        FROM
+        project p
+        LEFT JOIN project_closure pc ON p.id = pc.project_id
+        WHERE 1=1
+        <if test="name != '' and name != null">
+            AND p.name like CONCAT('%',#{name},'%')
+        </if>
+        <choose>
+            <when test="statuses.size != 0">
+                AND p.status IN
+                <foreach collection="statuses" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND p.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND p.workspace_id = #{workspaceId}
+    </select>
+
+
+    <select id="countByWorkspaceManager" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.ProjectCondition">
+        SELECT
+        COUNT(DISTINCT p.id)
+        FROM
+        project p
+        LEFT JOIN project_closure pc ON p.id = pc.project_id
+        WHERE if (pc.parent_project_id > -1, pc.parent_project_id, -1) = -1
+        <if test="name != '' and name != null">
+            AND p.name like CONCAT('%',#{name},'%')
+        </if>
+
+        <choose>
+            <when test="statuses.size != 0">
+                AND p.status IN
+                <foreach collection="statuses" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND p.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND p.workspace_id = #{workspaceId}
+    </select>
+
+
+    <!--    �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 李몄뿬�븯怨� �엳�뒗 吏꾪뻾以묒씤 �봽濡쒖젥�듃瑜� 議고쉶�븳�떎  -->
+    <select id="findByWorkspaceIdAndIncludeProjectAll" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.ProjectCondition">
+        SELECT
+        DISTINCT p.id as id,
+        p.name as name,
+        p.description as description,
+        p.status as status,
+        p.start_date as startDate,
+        p.end_date as endDate,
+        p.project_key as projectKey,
+        CASE p.default_yn WHEN 'Y' THEN 'true' ELSE 'false' END as defaultYn
+        FROM
+        project p
+        INNER JOIN project_role pr on p.id = pr.project_id
+        INNER JOIN project_role_user pru on pru.project_role_id = pr.id
+        WHERE pru.user_id = #{loginUserId}
+        AND p.workspace_id = #{workspaceId}
+        <if test="name != '' and name != null">
+            AND p.name like CONCAT('%',#{name},'%')
+        </if>
+        <choose>
+            <when test="statuses.size != 0">
+                AND p.status IN
+                <foreach collection="statuses" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND p.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <!--    �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 李몄뿬�븯怨� �엳�뒗 吏꾪뻾以묒씤 �봽濡쒖젥�듃瑜� 議고쉶�븳�떎  -->
+    <select id="findByWorkspaceIdAndIncludeProject" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.ProjectCondition">
+        SELECT
+        DISTINCT p.id as id,
+        p.name as name,
+        p.description as description,
+        p.status as status,
+        p.start_date as startDate,
+        p.end_date as endDate,
+        p.project_key as projectKey,
+        pc.parent_project_id as parentProjectId,
+        CASE p.default_yn WHEN 'Y' THEN 'true' ELSE 'false' END as defaultYn
+        FROM
+        project p
+        INNER JOIN project_role pr on p.id = pr.project_id
+        INNER JOIN project_role_user pru on pru.project_role_id = pr.id
+        LEFT JOIN project_closure pc ON p.id = pc.project_id
+        WHERE pru.user_id = #{loginUserId}
+        AND if (pc.parent_project_id > -1, pc.parent_project_id, -1) = -1
+        AND p.workspace_id = #{workspaceId}
+        <if test="name != '' and name != null">
+            AND p.name like CONCAT('%',#{name},'%')
+        </if>
+        <choose>
+            <when test="statuses.size != 0">
+                AND p.status IN
+                <foreach collection="statuses" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND p.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <select id="checkIncludeProject" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.ProjectCondition">
+        SELECT
+            DISTINCT p.id as id
+        FROM
+            project p
+                INNER JOIN project_role pr on p.id = pr.project_id
+                INNER JOIN project_role_user pru on pru.project_role_id = pr.id
+                INNER JOIN user u on u.id = pru.user_id
+        WHERE 1=1
+          AND p.id = #{id} AND u.id = #{loginUserId}
+    </select>
+
+    <select id="findChildrenProject" resultType="java.util.HashMap" parameterType="long">
+        SELECT
+        p.id as id,
+        p.name as name,
+        p.description as description,
+        p.status as status,
+        p.start_date as startDate,
+        p.end_date as endDate,
+        p.project_key as projectKey,
+        pc.parent_project_id as parentProjectId
+        FROM
+        project p
+        LEFT JOIN project_closure pc ON p.id = pc.project_id
+        WHERE pc.parent_project_id = #{parent_project_id}
+    </select>
+
+    <!--    �봽濡쒖젥�듃 �궘�젣 -->
+    <delete id="deleteProject" parameterType="java.util.HashMap">
+        <!--    �봽濡쒖젥�듃�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂� �궘�젣   -->
+        DELETE FROM issue_type_custom_field WHERE project_id = #{projectId};
+
+        <choose>
+            <when test="projectRoleIds.size != 0">
+                <!--    �봽濡쒖젥�듃 李몄뿬 �궗�슜�옄 �궘�젣  -->
+                DELETE FROM project_role_user WHERE project_role_id IN (
+                <foreach collection="projectRoleIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+                <!--    �봽濡쒖젥�듃 沅뚰븳 �뿰寃� �젙蹂� �궘�젣  -->
+                DELETE FROM project_role_permission WHERE project_role_id IN(
+                <foreach collection="projectRoleIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+            </when>
+        </choose>
+
+        <!--    �봽濡쒖젥�듃 �뿭�븷 �궘�젣  -->
+        DELETE FROM project_role WHERE project_id = #{projectId};
+
+        <!--    �씠�뒋 怨좎쑀 踰덊샇 �깮�꽦 �젙蹂� �궘�젣  -->
+        DELETE FROM issue_number_generator WHERE project_id = #{projectId};
+
+        <!--    �씠�뒋 �씠�젰 �젙蹂� �궘�젣 -->
+        DELETE FROM issue_history WHERE project_id = #{projectId};
+
+        <!--    �씠�뒋 踰꾩쟾 �젙蹂� �궘�젣 -->
+        DELETE FROM issue_version WHERE project_id = #{projectId};
+
+        <!--    �뾽臾� 怨듦컙�뿉 珥덈��븳 �봽濡쒖젥�듃 �젙蹂� �궘�젣   -->
+        DELETE FROM user_invite_project WHERE project_id = #{projectId};
+
+        <choose>
+            <when test="issueIds.size != 0">
+                <!--    �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂� �궘�젣  -->
+                DELETE FROM issue_custom_field_value WHERE issue_id IN (
+                <foreach collection="issueIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+                <!--    �씠�뒋 �뙎湲� �궘�젣    -->
+                DELETE FROM issue_comment WHERE issue_id IN (
+                <foreach collection="issueIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+                <!--    �씠�뒋 由ъ뒪�겕 �젙蹂� �궘�젣    -->
+                DELETE FROM issue_risk WHERE issue_id IN (
+                <foreach collection="issueIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+                <!--    �씠�뒋 �떞�떦�옄 �젙蹂� �궘�젣    -->
+                DELETE FROM issue_user WHERE issue_id IN (
+                <foreach collection="issueIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+                <!--    愿��떖 �씠�뒋 �젙蹂� �궘�젣 -->
+                DELETE FROM user_like_issue WHERE issue_id IN (
+                <foreach collection="issueIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+                <!--    �씠�뒋 諛쒖깮 �삁�빟 �젙蹂� �궘�젣  -->
+                DELETE FROM issue_reservation WHERE issue_id IN (
+                <foreach collection="issueIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+            </when>
+        </choose>
+
+        <!--    �씠�뒋 怨좎쑀 踰덊샇 �깮�꽦 �젙蹂� �궘�젣  -->
+        DELETE FROM issue_number_generator WHERE project_id = #{projectId};
+
+        <!--    �씠�뒋 �궘�젣   -->
+        DELETE FROM issue WHERE project_id = #{projectId};
+
+        <!--    �봽濡쒖젥�듃 �궘�젣 -->
+        DELETE FROM project WHERE id = #{projectId};
+
+    </delete>
+
+
+</mapper>
diff --git a/src/main/resources/mybatis/query-template/projectRoleUser-template.xml b/src/main/resources/mybatis/query-template/projectRoleUser-template.xml
new file mode 100644
index 0000000..a7f28a9
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/projectRoleUser-template.xml
@@ -0,0 +1,46 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.ProjectRoleUserMapper">
+
+    <!--    �봽濡쒖젥�듃 �뿭�븷怨� �궗�슜�옄瑜� �뿰寃고븳�떎. -->
+    <insert id="insertProjectRoleUser" keyColumn="id" keyProperty="id" useGeneratedKeys="true" parameterType="java.util.HashMap">
+        INSERT INTO project_role_user(project_role_id, user_id, register_id, modify_id, register_date, modify_date)
+        VALUES
+        <foreach collection="list" item="map" index="index" separator="," open="" close="">
+            (#{map.projectRoleId}, #{map.userId}, #{map.registerId}, #{map.registerId}, NOW(), NOW())
+        </foreach>
+    </insert>
+
+    <!--    �봽濡쒖젥�듃 �뿭�븷怨� �궗�슜�옄 �뿰寃� �젙蹂대�� �궘�젣�븳�떎.  -->
+    <delete id="deleteProjectRoleUser" parameterType="java.util.HashMap">
+        DELETE FROM project_role_user WHERE project_role_id = #{projectRoleId} AND user_id = #{userId}
+    </delete>
+
+    <!--    �봽濡쒖젥�듃�뿉 李몄뿬�븯�뒗 �씪諛� �궗�슜�옄, 愿�由ъ옄瑜� 議고쉶�븳�떎.   -->
+    <select id="findProjectRoleUser" resultType="java.util.HashMap" parameterType="java.util.HashMap">
+        SELECT
+        DISTINCT u.id as id,
+        u.name as name,
+        u.account as account,
+        u.profile as profile,
+        u.status as status
+        FROM
+        project p
+        INNER JOIN project_role pr on p.id = pr.project_id
+        INNER JOIN project_role_user pru on pru.project_role_id = pr.id
+        INNER JOIN user u on u.id = pru.user_id
+        WHERE 1=1
+        <choose>
+            <when test="statuses.size != 0">
+                AND pr.role_type IN
+                <foreach collection="statuses" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND p.id = #{id}
+    </select>
+
+
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/mybatis/query-template/qna-template.xml b/src/main/resources/mybatis/query-template/qna-template.xml
new file mode 100644
index 0000000..0535b81
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/qna-template.xml
@@ -0,0 +1,36 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.QnaMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.QnaCondition">
+        SELECT
+        DISTINCT n.id as id,
+        n.title as title,
+        n.description as description,
+        n.register_id as registerId,
+        u.name as writer,
+        SUBSTRING (n.register_date, 1, 10) as registerDate
+        FROM qna n
+        INNER JOIN user u on u.id = n.register_id
+        WHERE 1=1
+        <if test="title != '' and title != null">
+            AND n.title like CONCAT('%',#{title},'%')
+        </if>
+        ORDER BY ID DESC
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.QnaCondition">
+        SELECT
+        count(DISTINCT n.id)
+        FROM qna n
+        WHERE 1=1
+        <if test="title != '' and title != null">
+            AND n.title like CONCAT('%',#{title},'%')
+        </if>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mybatis/query-template/user-template.xml b/src/main/resources/mybatis/query-template/user-template.xml
new file mode 100644
index 0000000..161cd1b
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/user-template.xml
@@ -0,0 +1,174 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.UserMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.UserCondition">
+        SELECT
+        DISTINCT u.id as id,
+        u.name as name,
+        u.account as account,
+        u.profile as profile,
+        u.status as status
+        FROM
+        user u
+        INNER JOIN user_workspace uw on u.id = uw.user_id
+        INNER JOIN workspace w on w.id = uw.workspace_id
+        <if test="projectId != '' and projectId != null">
+            INNER JOIN project_role_user pru on pru.user_id = u.id
+            INNER JOIN project_role pr on pr.id = pru.project_role_id
+            INNER JOIN project p on p.id = pr.project_id
+        </if>
+
+        WHERE w.id = #{workspaceId}
+        <if test="name != '' and name != null">
+            AND u.name like CONCAT('%',#{name},'%')
+        </if>
+        <choose>
+            <when test="statuses.size != 0">
+                AND u.status IN
+                <foreach collection="statuses" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND u.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        <if test="projectId != '' and projectId != null">
+            AND p.id = #{projectId}
+        </if>
+        limit #{pageSize} offset #{page};
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.UserCondition">
+        SELECT
+        COUNT(DISTINCT u.id)
+        FROM
+        user u
+        INNER JOIN user_workspace uw on u.id = uw.user_id
+        INNER JOIN workspace w on w.id = uw.workspace_id
+        <if test="projectId != '' and projectId != null">
+            INNER JOIN project_role_user pru on pru.user_id = u.id
+            INNER JOIN project_role pr on pr.id = pru.project_role_id
+            INNER JOIN project p on p.id = pr.project_id
+        </if>
+        WHERE w.id = #{workspaceId}
+        <if test="name != '' and name != null">
+            AND u.name like CONCAT('%',#{name},'%')
+        </if>
+        <choose>
+            <when test="statuses.size != 0">
+                AND u.status IN
+                <foreach collection="statuses" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND u.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        <if test="projectId != '' and projectId != null">
+            AND p.id = #{projectId}
+        </if>
+    </select>
+
+    <select id="findProjectMember" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.UserCondition">
+        select DISTINCT (u.id) as userId, u.account as account from user u
+        inner join project_role_user pru on pru.user_id = u.id
+        inner join project_role pr on pr.id = pru.project_role_id
+        inner join project p on p.id = pr.project_id
+        where p.id = #{projectId}
+    </select>
+
+    <!--  �쉶�썝 �깉�눜瑜� 吏꾪뻾�븷 �븣 �뿰愿� �뀒�씠釉� �젙蹂대�� �궘�젣�븳�떎. -->
+    <delete id="deleteCascadeUser" parameterType="kr.wisestone.owl.web.condition.UserCondition">
+        <!--    愿��떖 �씠�뒋 �젙蹂� �궘�젣 -->
+        DELETE FROM user_like_issue WHERE user_id = #{id};
+
+        <!--    �씠�뒋 寃��깋 議곌굔 �궘�젣 -->
+        DELETE FROM issue_search WHERE user_id = #{id};
+
+        <!--    �씠�뒋 �떞�떦�옄 �젙蹂� �궘�젣 -->
+        DELETE FROM issue_user WHERE user_id = #{id};
+
+        <!--    �빐�떦 �궗�슜�옄�쓽 �뾽臾� 怨듦컙�뿉 李몄뿬�븯�뒗 �궗�슜�옄 �젙蹂� �궘�젣 -->
+        DELETE FROM user_workspace WHERE user_id = #{id} AND manager_yn = 'N';
+
+        <!--    �깉�눜�븯�뒗 �궗�슜�옄媛� 諛쒖넚諛쏆쓣 �씠硫붿씪 �젙蹂� �궘�젣 -->
+        DELETE FROM system_email WHERE send_address = #{account};
+
+    </delete>
+
+    <!--    �씠硫붿씪 �삁�빟 �떆媛� 李얘린    -->
+    <select id="findByReservationNotifyTime" resultType="java.util.HashMap" parameterType="java.util.HashMap">
+        select account from user where reservation_notify_time between
+        #{startTime} and #{endTime}
+    </select>
+
+    <select id="findByAllWorkspace" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.UserCondition">
+        SELECT
+        DISTINCT u.id as id,
+        u.name as name,
+        u.account as account,
+        u.profile as profile,
+        u.status as status
+        FROM
+        user u
+        WHERE 1=1
+        <if test="name != '' and name != null">
+            AND u.name like CONCAT('%',#{name},'%')
+        </if>
+        AND u.status = '01'
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND u.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        limit #{pageSize} offset #{page};
+    </select>
+
+    <select id="countByAllWorkspace" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.UserCondition">
+        SELECT
+        COUNT(DISTINCT u.id)
+        FROM
+        user u
+        WHERE 1=1
+        <if test="name != '' and name != null">
+            AND u.name like CONCAT('%',#{name},'%')
+        </if>
+        AND u.status = '01'
+        <choose>
+            <when test="excludeIds.size != 0">
+                AND u.id NOT IN
+                <foreach collection="excludeIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <select id="findEvent" resultType="java.util.HashMap">
+        SELECT u.id, u.name as name, u.account, u.phone, u.register_id, u.register_date, COUNT(lh.user_id) AS loginCount,
+        (SELECT COUNT(i.id) FROM issue i WHERE i.register_id = u.id) AS issueCount  FROM login_history lh
+        INNER JOIN user u ON u.id = lh.user_id
+        WHERE DATE(u.register_date) BETWEEN '2019-05-31' AND '2019-06-14' AND u.STATUS = '01'
+        GROUP BY u.id ORDER BY issueCount desc, loginCount DESC;
+    </select>
+
+
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/mybatis/query-template/userWorkspace-template.xml b/src/main/resources/mybatis/query-template/userWorkspace-template.xml
new file mode 100644
index 0000000..402aff6
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/userWorkspace-template.xml
@@ -0,0 +1,71 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.UserWorkspaceMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.UserWorkspaceCondition">
+        SELECT
+        DISTINCT uw.id as id,
+        u.id as userId,
+        u.name as userName,
+        u.account as account,
+        u.permission as permission,
+        CASE WHEN uw.use_yn = 'Y' THEN 'true' ELSE 'false' END as useYn
+        FROM
+        user_workspace uw
+        INNER JOIN user u on uw.user_id = u.id
+        INNER JOIN workspace w on uw.workspace_id = w.id
+        WHERE 1=1
+        AND w.id = #{workspaceId}
+        AND uw.manager_yn = 'N'
+        <if test="name != '' and name != null">
+            AND u.name like CONCAT('%',#{name},'%')
+        </if>
+
+        <if test="account != '' and account != null">
+            AND u.account like CONCAT('%',#{account},'%')
+        </if>
+
+        <choose>
+            <when test="statuses.size != 0">
+                AND uw.use_yn IN
+                <foreach collection="statuses" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        limit #{pageSize} offset #{page};
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.UserWorkspaceCondition">
+        SELECT
+        count(DISTINCT uw.id),
+        u.name as name,
+        u.account as account,
+        uw.use_yn as useYn
+        FROM
+        user_workspace uw
+        INNER JOIN user u on uw.user_id = u.id
+        INNER JOIN workspace w on uw.workspace_id = w.id
+        WHERE 1=1
+        AND w.id = #{workspaceId}
+        AND uw.manager_yn = 'N'
+        <if test="name != '' and name != null">
+            AND u.name like CONCAT('%',#{name},'%')
+        </if>
+
+        <if test="account != '' and account != null">
+            AND u.account like CONCAT('%',#{account},'%')
+        </if>
+
+        <choose>
+            <when test="statuses.size != 0">
+                AND uw.use_yn IN
+                <foreach collection="statuses" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/mybatis/query-template/widget-template.xml b/src/main/resources/mybatis/query-template/widget-template.xml
new file mode 100644
index 0000000..3ff9f57
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/widget-template.xml
@@ -0,0 +1,746 @@
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.WidgetMapper">
+
+    <!-- �쟾泥� �쐞�젽 -->
+    <!--    1踰� �쐞�젽 �떆�옉 -->
+
+    <!--    �옍�뿬 �씠�뒋 媛쒖닔 -->
+    <select id="countRemainIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        SELECT
+        COUNT(i.id) FROM issue i
+        WHERE EXISTS(SELECT 1 FROM issue_status iss WHERE iss.issue_status_type != 'CLOSE' AND i.issue_status_id =
+        iss.id)
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <!--    吏��뿰 �씠�뒋 媛쒖닔   -->
+    <select id="countTodayDelayIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        SELECT
+        COUNT(i.id) FROM issue i where
+        exists(select 1 from issue_status iss where iss.id = i.issue_status_id and iss.issue_status_type != 'CLOSE')
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND i.complete_date IS NOT NULL
+        AND i.complete_date <![CDATA[ < ]]> #{completeDate}
+    </select>
+
+    <!--    �븷�떦 �씠�뒋 媛쒖닔   -->
+    <select id="countAssigneeIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        SELECT
+        COUNT(i.id) FROM issue i
+        WHERE EXISTS(SELECT 1 FROM issue_user iu WHERE iu.issue_id = i.id AND iu.user_id = #{loginUserId})
+        AND EXISTS(SELECT 1 FROM issue_status iss WHERE iss.id = i.issue_status_id and iss.issue_status_type != 'CLOSE')
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <!--    �궡媛� �삤�뒛 �벑濡앺븳 �씠�뒋 媛쒖닔 -->
+    <select id="countTodayRegisterIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select
+        count(i.id) as todayCount from issue i where
+        i.register_id = #{loginUserId}
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND i.register_date BETWEEN (CURDATE()) AND (CURDATE() + INTERVAL 1 DAY)
+    </select>
+
+    <!--    誘명븷�떦 �씠�뒋 媛쒖닔   -->
+    <select id="countNoAssigneeIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        SELECT
+        COUNT(DISTINCT i.id) FROM issue i
+        LEFT OUTER JOIN issue_user iu ON iu.issue_id = i.id
+        WHERE
+        EXISTS(select 1 from issue_status iss where iss.id = i.issue_status_id and iss.issue_status_type != 'CLOSE')
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND iu.id IS NULL
+    </select>
+    <!--    �셿猷뚮맂 �씠�뒋 媛쒖닔   -->
+    <select id="countCompleteIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select
+        count(*) as issueCount from issue i
+        inner join issue_status iss on iss.id = i.issue_status_id
+        where iss.issue_status_type = 'CLOSE'
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <!--    1踰� �쐞�젽 �걹 -->
+
+    <!--    2踰� �쐞�젽 �떆�옉  -->
+
+    <!--    �쟾泥� �씠�뒋 泥섎━�쁽�솴   -->
+    <select id="findIssueComplete" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select (DATE_FORMAT(i.modify_date, '%Y-%m-%d')) as modifyDate,
+        count(*) as issueCount from issue i
+        inner join issue_status iss on iss.id = i.issue_status_id
+        where iss.issue_status_type = 'CLOSE'
+        and i.modify_date between #{searchStartDate} and #{searchEndDate}
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        group by (DATE_FORMAT(i.modify_date, '%Y-%m-%d'))
+    </select>
+
+    <!--    �쟾泥� �씠�뒋 媛쒖닔   -->
+    <select id="countTotalIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select count(distinct i.id) from issue i
+        inner join issue_status iss on iss.id = i.issue_status_id
+        where 1=1
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <!--    2踰� �쐞�젽 �걹 -->
+
+
+    <!--    3踰� �쐞�젽 �떆�옉 -->
+
+    <!--    吏꾪뻾以묒씤 �봽濡쒖젥�듃 �쁽�솴    -->
+    <select id="findProjectProgress" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select
+        p.id as id,
+        p.name as name,
+        replace(p.start_date, "-", ".") as startDate,
+        replace(p.end_date, "-", ".") as endDate,
+        count(case when iss.issue_status_type = 'CLOSE' THEN 1 END) as 'close',
+        count(case when iss.issue_status_type != 'CLOSE' THEN 1 END) as 'remain',
+        (select concat(u.name, "%", u.account, "%", u.profile) from user u
+        inner join project_role_user pru on pru.user_id = u.id
+        inner join project_role pr on pr.id = pru.project_role_id
+        where pr.project_id = p.id and pr.role_type = '02'
+        ) as managerInfo
+        ,
+        (
+        select count(distinct(u.id)) from user u
+        inner join project_role_user pru on pru.user_id = u.id
+        inner join project_role pr on pr.id = pru.project_role_id
+        where pr.project_id = p.id and pr.role_type = '01'
+        ) as teamCount
+        from
+        project p
+        left outer join issue i on p.id = i.project_id
+        left outer join issue_status iss on iss.id = i.issue_status_id
+        WHERE 1=1
+        <choose>
+            <when test="projectIds.size != 0">
+                AND p.id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        GROUP by p.id
+    </select>
+
+    <!--    吏꾪뻾以묒씤 �봽濡쒖젥�듃 �쁽�솴(�쟾泥�)    -->
+    <select id="findProjectProgressAll" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select
+        p.id as id,
+        p.name as name,
+        replace(p.start_date, "-", ".") as startDate,
+        replace(p.end_date, "-", ".") as endDate,
+        count(case when iss.issue_status_type = 'CLOSE' THEN 1 END) as 'close',
+        count(case when iss.issue_status_type != 'CLOSE' THEN 1 END) as 'remain',
+        (select concat(u.name, "%", u.account, "%", u.profile) from user u
+        inner join project_role_user pru on pru.user_id = u.id
+        inner join project_role pr on pr.id = pru.project_role_id
+        where pr.project_id = p.id and pr.role_type = '02'
+        ) as managerInfo
+        ,
+        (
+        select count(distinct(u.id)) from user u
+        inner join project_role_user pru on pru.user_id = u.id
+        inner join project_role pr on pr.id = pru.project_role_id
+        where pr.project_id = p.id and pr.role_type = '01'
+        ) as teamCount
+        from
+        project p
+        left outer join issue i on p.id = i.project_id
+        left outer join issue_status iss on iss.id = i.issue_status_id
+        WHERE 1=1
+        GROUP by p.id
+    </select>
+
+    <!--    3踰� �쐞�젽 �걹 -->
+
+
+    <!--    4踰� �쐞�젽 �떆�옉 -->
+
+    <!--    �궡媛� �삤�뒛 �븷�떦諛쏆� �씠�뒋 媛쒖닔 -->
+    <select id="countTodayMyAssigneeIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select
+        count(i.id) as todayCount from issue i where
+        exists(select 1 from issue_user iu where iu.issue_id = i.id and iu.user_id = #{loginUserId} and iu.register_date
+        BETWEEN (CURDATE()) AND (CURDATE() + INTERVAL 1 DAY))
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <!--    �궡媛� �떞�떦�븯�뒗 �씠�뒋 媛쒖닔  -->
+    <select id="countMyAssigneeIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select
+        count(distinct i.id)
+        from issue i
+        inner join issue_user iu on iu.issue_id = i.id
+        inner join issue_status iss on iss.id = i.issue_status_id
+        WHERE 1=1
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        and iu.user_id = #{loginUserId}
+        and iss.issue_status_type != 'CLOSE'
+    </select>
+
+    <!--    �궡媛� �떞�떦�븯�뒗 �씠�뒋  -->
+    <select id="findMyAssigneeIssue" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select
+        distinct i.id as id,
+        i.title as title,
+        CONCAT(p.project_key, '-', i.issue_number) AS issueKey,
+        p.project_key as projectKey,
+        i.issue_number as issueNumber,
+        iss.name as issueStatusName,
+        replace(i.complete_date, "-", ".") as completeDate,
+        replace(SUBSTRING(i.register_date, 1, 10), "-", ".") as registerDate
+        from issue i
+        inner join issue_user iu on iu.issue_id = i.id
+        inner join issue_status iss on iss.id = i.issue_status_id
+        inner join project p on p.id = i.project_id
+        WHERE 1=1
+        <choose>
+            <when test="projectIds.size != 0">
+                AND p.id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        and iu.user_id = #{loginUserId}
+        and iss.issue_status_type != 'CLOSE'
+        GROUP by i.id
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <!--    4踰� �쐞�젽 �걹 -->
+
+
+    <!--    5踰� �쐞�젽 �떆�옉 -->
+
+    <!--    踰덈났�릺�뒗 �긽�깭 蹂�寃� 諛� 鍮덈쾲�븳 �떞�떦�옄 蹂�寃� 媛쒖닔   -->
+    <select id="countChangeStatusAndAssigneeIssue" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select
+        count(case when ir.change_assignee_count > 3 then 1 end) as changeAssigneeCount,
+        count(case when ir.change_issue_status_count > 3 then 1 end) as changeIssueStatusCount
+        from issue i
+        inner join issue_risk ir on ir.issue_id = i.id
+        inner join issue_status iss on iss.id = i.issue_status_id
+        where iss.issue_status_type != 'CLOSE'
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <!--    �쐞�뿕 愿�由�    -->
+    <select id="findRiskIssue" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select
+        DISTINCT i.id, i.title, iss.name as issueStatusName,
+        (case when ir.change_assignee_count > 3 then true else false end) as changeAssigneeType,
+        (case when ir.change_issue_status_count > 3 then true else false end) as changeIssueStatusType,
+        CONCAT(p.project_key, '-', i.issue_number) AS issueKey,
+        i.issue_number as issueNumber,
+        p.project_key as projectKey
+        from issue i
+        inner join issue_risk ir on ir.issue_id = i.id
+        inner join issue_status iss on iss.id = i.issue_status_id
+        inner join project p on p.id = i.project_id
+        where iss.issue_status_type != 'CLOSE'
+        <choose>
+            <when test="projectIds.size != 0">
+                AND p.id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        and (ir.change_assignee_count > 3 || ir.change_issue_status_count > 3)
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <!--    �쐞�뿕 愿�由� 媛쒖닔    -->
+    <select id="countRiskIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select
+        count(DISTINCT i.id)
+        from issue i
+        inner join issue_risk ir on ir.issue_id = i.id
+        inner join issue_status iss on iss.id = i.issue_status_id
+        where iss.issue_status_type != 'CLOSE'
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        and (ir.change_assignee_count > 3 || ir.change_issue_status_count > 3)
+    </select>
+
+    <!--    5踰� �쐞�젽 �걹 -->
+
+
+    <!--    6踰� �쐞�젽 �떆�옉 -->
+
+    <!--    �궡媛� �벑濡앺븳 �씠�뒋 議고쉶    -->
+    <select id="findRegisterIssue" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        SELECT
+        DISTINCT i.id
+        <if test="page != null and !page.equals('')">
+            , replace(i.start_date, "-", ".") as startDate
+            , replace(i.complete_date, "-", ".") as completeDate
+            , i.title as title
+            , iss.name as issueStatusName
+            , i.issue_number as issueNumber
+            , p.project_key as projectKey
+            , CONCAT(p.project_key, '-', i.issue_number) AS issueKey
+        </if>
+        FROM issue i
+        <if test="page != null and !page.equals('')">
+            INNER JOIN issue_status iss ON iss.id = i.issue_status_id
+            INNER JOIN project p ON p.id = i.project_id
+        </if>
+        WHERE 1=1
+        AND i.register_id = #{loginUserId}
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        <if test="page != null and !page.equals('')">
+            AND iss.issue_status_type != 'CLOSE'
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <!--    �궡媛� �벑濡앺븳 �씠�뒋 移댁슫�듃 援ы븯湲�   -->
+    <select id="countRegisterIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        SELECT
+        <choose>
+            <when test="page != null and !page.equals('')">
+                COUNT(i.id)
+            </when>
+            <otherwise>
+                COUNT(DISTINCT i.id)
+            </otherwise>
+        </choose>
+        FROM issue i
+        WHERE i.register_id = #{loginUserId}
+        <if test="page != null and !page.equals('')">
+            AND EXISTS (SELECT 1 FROM issue_status iss WHERE iss.id = i.issue_status_id AND iss.issue_status_type != 'CLOSE')
+        </if>
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <!--    6踰� �쐞�젽 �걹 -->
+
+
+    <!--    7踰� �쐞�젽 �걹 -->
+
+    <!--    8踰� �쐞�젽 �떆�옉 -->
+
+    <!--    吏��뿰以묒씤 �씠�뒋 議고쉶 (�삤�뒛 �궇吏� 湲곗�)    -->
+    <select id="findDelayIssue" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        SELECT
+        DISTINCT i.id
+        <if test="page != null and !page.equals('')">
+            , replace(i.start_date, "-", ".") as startDate
+            , replace(i.complete_date, "-", ".") as completeDate
+            , i.title as title
+            , iss.name as issueStatusName
+            , i.issue_number as issueNumber
+            , p.project_key as projectKey
+            , CONCAT(p.project_key, '-', i.issue_number) AS issueKey
+        </if>
+        FROM issue i
+        <if test="page != null and !page.equals('')">
+            INNER JOIN issue_status iss ON iss.id = i.issue_status_id
+            INNER JOIN project p ON p.id = i.project_id
+        </if>
+        WHERE 1=1
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND i.complete_date IS NOT NULL
+        AND i.complete_date <![CDATA[ < ]]> #{completeDate}
+        <if test="page != null and !page.equals('')">
+            AND iss.issue_status_type != 'CLOSE'
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <!--    吏��뿰以묒씤 �씠�뒋 移댁슫�듃 援ы븯湲�   -->
+    <select id="countDelayIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        SELECT
+        COUNT(DISTINCT i.id) FROM issue i
+        INNER JOIN project p ON p.id = i.project_id
+        <if test="page != null and !page.equals('')">
+            INNER JOIN issue_status iss ON iss.id = i.issue_status_id
+        </if>
+        WHERE 1=1
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        AND i.complete_date IS NOT NULL
+        AND i.complete_date <![CDATA[ < ]]> #{completeDate}
+        <if test="page != null and !page.equals('')">
+            AND iss.issue_status_type != 'CLOSE'
+        </if>
+    </select>
+
+    <!--    8踰� �쐞�젽 �걹 -->
+
+
+    <!--    9踰� �쐞�젽 �떆�옉 -->
+
+    <!--    �봽濡쒖젥�듃 蹂� �긽�깭蹂� �씠�뒋 �쁽�솴 議고쉶 -->
+    <select id="findByStandIssueStatus" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select
+        <choose>
+            <when test="issueStatuses.size != 0">
+                <foreach collection="issueStatuses" item="item" index="index" separator="," open="" close="">
+                    count(case when i.issue_status_id = #{item.id} then 1 end) as #{item.name}
+                </foreach>
+            </when>
+        </choose>
+        ,p.id,
+        p.name as projectName
+        from issue i
+        inner join project p on p.id = i.project_id
+        inner join issue_status iss on iss.id = i.issue_status_id
+        where 1=1
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        group by p.id
+    </select>
+
+    <!--    9踰� �쐞�젽 �걹 -->
+
+    <!--    11踰� �쐞�젽 �떆�옉 -->
+
+    <!--    硫ㅻ쾭蹂� 吏꾪뻾瑜� -->
+    <select id="findProjectMemberIssue" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select
+        u.name, u.profile, u.account,
+        (select count(*) from issue i
+        inner join issue_user iu on iu.issue_id = i.id
+        inner join issue_status iss on iss.id = i.issue_status_id
+        where iu.user_id = u.id and i.project_id = p.id and iss.issue_status_type = 'CLOSE') as completeCount,
+        (select count(*) from issue i
+        inner join issue_user iu on iu.issue_id = i.id
+        inner join issue_status iss on iss.id = i.issue_status_id
+        where iu.user_id = u.id and i.project_id = p.id and iss.issue_status_type != 'CLOSE') as remainCount,
+        (select count(*) from issue i
+        inner join issue_user iu on iu.issue_id = i.id
+        inner join issue_status iss on iss.id = i.issue_status_id
+        where iu.user_id = u.id and i.project_id = p.id and iss.issue_status_type != 'CLOSE' and i.complete_date is not null and i.complete_date <![CDATA[ < ]]> now()) as delayCount
+        from
+        user u
+        inner join project_role_user pru on pru.user_id = u.id
+        inner join project_role pr on pr.id = pru.project_role_id
+        inner join project p on p.id = pr.project_id
+        where p.id = #{projectId} and p.status = '02'
+    </select>
+
+    <!--    11踰� �쐞�젽 �걹 -->
+
+
+    <!--    12踰� �쐞�젽 �떆�옉 -->
+
+    <!--    �벑濡앺븳 �씠�뒋 以� �셿猷� 媛��닔  -->
+    <select id="findMyRegisterCompleteIssue" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select i.project_id as projectId, count(i.id) as issueCount from issue i
+        where exists (select 1 from issue_status iss where iss.id = i.issue_status_id and iss.issue_status_type =
+        'CLOSE') AND
+        i.register_id = #{loginUserId}
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        group by i.project_id
+    </select>
+
+    <!--    �벑濡앺븳 �씠�뒋 以� 吏꾪뻾 媛��닔  -->
+    <select id="findMyRegisterRemainIssue" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select i.project_id as projectId, count(i.id) as issueCount from issue i
+        where exists (select 1 from issue_status iss where iss.id = i.issue_status_id and iss.issue_status_type !=
+        'CLOSE')
+        AND i.register_id = #{loginUserId}
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        group by i.project_id
+    </select>
+
+    <!--    �떞�떦�븳 �씠�뒋 以� �셿猷� 媛��닔  -->
+    <select id="findMyAssigneeCompleteIssue" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select i.project_id as projectId, count(distinct i.id) as issueCount from issue i
+        inner join issue_user iu on iu.issue_id = i.id
+        inner join issue_status iss on iss.id = i.issue_status_id
+        where iu.user_id = #{loginUserId} and iss.issue_status_type = 'CLOSE'
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        group by i.project_id
+    </select>
+
+    <!--    �떞�떦�븳 �씠�뒋 以� 吏꾪뻾 媛��닔  -->
+    <select id="findMyAssigneeRemainIssue" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        select i.project_id as projectId, count(distinct i.id) as issueCount from issue i
+        inner join issue_user iu on iu.issue_id = i.id
+        inner join issue_status iss on iss.id = i.issue_status_id
+        where iu.user_id = #{loginUserId} and iss.issue_status_type != 'CLOSE'
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        group by i.project_id
+    </select>
+
+    <!--    12踰� �쐞�젽 �걹 -->
+
+    <!--    �씠�뒋 ���엯 蹂� �씠�뒋 �쁽�솴   -->
+
+    <select id="findByStandIssueType" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        SELECT issue_type.name as name, COUNT(issue.id) as issueCount FROM issue issue
+        INNER JOIN issue_type issue_type ON issue.issue_type_id = issue_type.id
+        WHERE issue.project_id = #{projectId}
+        GROUP BY issue_type.name
+    </select>
+
+    <!-- 13踰� �쐞�젽 �떆�옉 -->
+
+    <!-- 以묒슂�룄 蹂� �씠�뒋 媛��닔 -->
+    <select id="countSeverityIssue" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        SELECT
+        COUNT(case when s.id = 1 then 'CRITICAL' END) AS 'critical',
+        COUNT(case when s.id = 2 then 'MAJOR' END) AS 'major',
+        COUNT(case when s.id = 3 then 'MINOR' END) AS 'minor',
+        COUNT(case when s.id = 4 then 'TRIVIAL' END) AS 'trivial'
+        FROM issue i
+        INNER JOIN project p ON p.id = i.project_id
+        INNER JOIN workspace w ON w.id = p.workspace_id
+        INNER JOIN severity s ON s.id = i.severity_id
+        INNER JOIN issue_status iss ON iss.id = i.issue_status_id
+        WHERE w.id = #{workspaceId}
+        AND iss.issue_status_type != 'CLOSE'
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <!-- 以묒슂�룄 �씠�뒋 紐⑸줉 -->
+    <select id="findSeverityIssues" resultType="java.util.HashMap"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        SELECT i.title AS title,
+        s.name AS severityName,
+        s.color AS severityColor,
+        replace(i.start_date, "-", ".") AS startDate,
+        replace(i.complete_date, "-", ".")  AS completeDate,
+        i.issue_number AS issueNumber,
+        p.project_key AS projectKey,
+        iss.name AS issueStatusName,
+        CONCAT(p.project_key, '-', i.issue_number) AS issueKey
+        FROM issue i
+        INNER JOIN project p ON p.id = i.project_id
+        INNER JOIN workspace w ON w.id = p.workspace_id
+        INNER JOIN severity s ON s.id = i.severity_id
+        INNER JOIN issue_status iss ON iss.id = i.issue_status_id
+        WHERE w.id = #{workspaceId}
+        AND s.id = #{severityId}
+        AND iss.issue_status_type != 'CLOSE'
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <!-- 以묒슂�룄 �빆紐� 蹂� 媛��닔 -->
+    <select id="countSearchIssue" resultType="java.lang.Long"
+            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
+        SELECT COUNT(distinct i.id)
+        FROM issue i
+        INNER JOIN project p ON p.id = i.project_id
+        INNER JOIN workspace w ON w.id = p.workspace_id
+        INNER JOIN severity s ON s.id = i.severity_id
+        INNER JOIN issue_status iss ON iss.id = i.issue_status_id
+        WHERE w.id = #{workspaceId}
+        AND s.id = #{severityId}
+        AND iss.issue_status_type != 'CLOSE'
+        <choose>
+            <when test="projectIds.size != 0">
+                AND i.project_id IN
+                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
+
+    <!-- 13踰� �쐞�젽 �걹 -->
+
+</mapper>
diff --git a/src/main/resources/mybatis/query-template/workflow-template.xml b/src/main/resources/mybatis/query-template/workflow-template.xml
new file mode 100644
index 0000000..0f33f37
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/workflow-template.xml
@@ -0,0 +1,37 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.WorkflowMapper">
+
+    <select id="find" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.WorkflowCondition">
+        SELECT
+        DISTINCT w.id as id,
+        w.name as name,
+        w.description as description
+        FROM
+        workflow w
+        INNER JOIN workspace ws on w.workspace_id = ws.id
+        WHERE 1=1
+        <if test="name != '' and name != null">
+            AND w.name like CONCAT('%',#{name},'%')
+        </if>
+        AND ws.id = #{workspaceId}
+        <if test="page != null and !page.equals('')">
+            limit #{pageSize} offset #{page};
+        </if>
+    </select>
+
+    <select id="count" resultType="java.lang.Long" parameterType="kr.wisestone.owl.web.condition.WorkflowCondition">
+        SELECT
+        count(DISTINCT w.id)
+        FROM
+        workflow w
+        INNER JOIN workspace ws on w.workspace_id = ws.id
+        WHERE 1=1
+        <if test="name != '' and name != null">
+            AND w.name like CONCAT('%',#{name},'%')
+        </if>
+        AND ws.id = #{workspaceId}
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/mybatis/query-template/workspace-template.xml b/src/main/resources/mybatis/query-template/workspace-template.xml
new file mode 100644
index 0000000..52ea13f
--- /dev/null
+++ b/src/main/resources/mybatis/query-template/workspace-template.xml
@@ -0,0 +1,160 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="kr.wisestone.owl.mapper.WorkspaceMapper">
+
+    <!--    �뾽臾� 怨듦컙 �궘�젣  -->
+    <delete id="deleteWorkspace" parameterType="java.util.HashMap">
+        <choose>
+            <when test="workflowIds.size != 0">
+                <!--    �썙�겕�뵆濡쒖슦 �뿰寃� �젙蹂� �궘�젣  -->
+                DELETE FROM workflow_transition WHERE workflow_id IN (
+                <foreach collection="workflowIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+            </when>
+        </choose>
+
+        <choose>
+            <when test="userInviteIds.size != 0">
+                <!--    �뾽臾� 怨듦컙�뿉 珥덈��븳 �봽濡쒖젥�듃 �젙蹂� �궘�젣   -->
+                DELETE FROM user_invite_project WHERE user_invite_id IN (
+                <foreach collection="userInviteIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+            </when>
+        </choose>
+
+        <choose>
+            <when test="projectRoleIds.size != 0">
+                <!--    �봽濡쒖젥�듃 李몄뿬 �궗�슜�옄 �궘�젣  -->
+                DELETE FROM project_role_user WHERE project_role_id IN (
+                <foreach collection="projectRoleIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+                <!--    �봽濡쒖젥�듃 沅뚰븳 �뿰寃� �젙蹂� �궘�젣  -->
+                DELETE FROM project_role_permission WHERE project_role_id IN(
+                <foreach collection="projectRoleIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+            </when>
+        </choose>
+
+        <choose>
+            <when test="projectIds.size != 0">
+
+                <!--    �봽濡쒖젥�듃 �뿭�븷 �궘�젣  -->
+                DELETE FROM project_role WHERE project_id IN (
+                <foreach collection="projectIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+                <!--    �봽濡쒖젥�듃�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂� �궘�젣   -->
+                DELETE FROM issue_type_custom_field WHERE project_id IN (
+                <foreach collection="projectIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+                <!--    �씠�뒋 怨좎쑀 踰덊샇 �깮�꽦 �젙蹂� �궘�젣  -->
+                DELETE FROM issue_number_generator WHERE project_id IN (
+                <foreach collection="projectIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+                <!--    �씠�뒋 �씠�젰 �젙蹂� �궘�젣 -->
+                DELETE FROM issue_history WHERE project_id IN (
+                <foreach collection="projectIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+                <!--    �씠�뒋 �궘�젣   -->
+                DELETE FROM issue WHERE project_id IN (
+                <foreach collection="projectIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+            </when>
+        </choose>
+
+        <choose>
+            <when test="customFieldIds.size != 0">
+
+                <!--    �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂� �궘�젣  -->
+                DELETE FROM issue_custom_field_value WHERE custom_field_id IN (
+                <foreach collection="customFieldIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+
+                <!--    �궗�슜�옄 �젙�쓽 �븘�뱶 媛� �궘�젣  -->
+                DELETE FROM custom_field_value WHERE custom_field_id IN (
+                <foreach collection="customFieldIds" item="item" index="index" separator="," open="" close="">
+                    #{item}
+                </foreach>
+                );
+            </when>
+        </choose>
+
+        <!--    �궗�슜�옄 �젙�쓽 �븘�뱶 �궘�젣    -->
+        DELETE FROM custom_field WHERE workspace_id = #{workspaceId};
+
+        <!--    �씠�뒋 �긽�깭 �궘�젣    -->
+        DELETE FROM issue_status WHERE workspace_id = #{workspaceId};
+
+        <!--    �썙�겕�뵆濡쒖슦 �궘�젣    -->
+        DELETE FROM workflow WHERE workspace_id = #{workspaceId};
+
+        <!--    �씠�뒋 ���엯 �궘�젣    -->
+        DELETE FROM issue_type WHERE workspace_id = #{workspaceId};
+
+        <!--    �봽濡쒖젥�듃 �궘�젣 -->
+        DELETE FROM project WHERE workspace_id = #{workspaceId};
+
+        <!--    �뾽臾� 怨듦컙�뿉 珥덈� �젙蹂� �궘�젣 -->
+        DELETE FROM user_invite WHERE workspace_id = #{workspaceId};
+
+        <!--    �뾽臾� 怨듦컙 李몄뿬�옄 �궘�젣    -->
+        DELETE FROM user_workspace WHERE workspace_id = #{workspaceId} AND manager_yn = 'N';
+
+        <!--    �씠�뒋 �떞�떦�옄 �젙蹂� �궘�젣    -->
+        DELETE FROM issue_user WHERE workspace_id = #{workspaceId};
+
+        <!--    �씠�뒋 由ъ뒪�겕 �젙蹂� �궘�젣    -->
+        DELETE FROM issue_risk WHERE workspace_id = #{workspaceId};
+
+        <!--    愿��떖 �씠�뒋 �젙蹂� �궘�젣 -->
+        DELETE FROM user_like_issue WHERE workspace_id = #{workspaceId};
+
+        <!--    �씠�뒋 �뙎湲� �궘�젣    -->
+        DELETE FROM issue_comment WHERE workspace_id = #{workspaceId};
+
+        <!--    �씠�뒋 寃��깋 議곌굔 �궘�젣 -->
+        DELETE FROM issue_search WHERE workspace_id = #{workspaceId};
+
+        <!--    �씠�뒋 �뀒�씠釉� �꽕�젙 �궘�젣 -->
+        DELETE FROM issue_table_config WHERE workspace_id = #{workspaceId};
+
+        <!--    �씠�뒋 踰꾩쟾 �젙蹂� �궘�젣 -->
+        DELETE FROM issue_version WHERE workspace_id = #{workspaceId};
+
+        <!--    �슦�꽑 �닚�쐞 �젙蹂� �궘�젣 -->
+        DELETE FROM priority WHERE workspace_id = #{workspaceId};
+
+        <!--    以묒슂�룄 �젙蹂� �궘�젣   -->
+        DELETE FROM severity WHERE workspace_id = #{workspaceId};
+
+        <!--    �씠�뒋 諛쒖깮 �삁�빟 �젙蹂� �궘�젣  -->
+        DELETE FROM issue_reservation WHERE workspace_id = #{workspaceId};
+
+    </delete>
+
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/system_design.properties b/src/main/resources/system_design.properties
new file mode 100644
index 0000000..a71c449
--- /dev/null
+++ b/src/main/resources/system_design.properties
@@ -0,0 +1,76 @@
+db.primary.driverName=org.mariadb.jdbc.Driver
+db.primary.url=jdbc:mariadb://IP\uC8FC\uC18C \uC785\uB825/dev_db?allowMultiQueries=true
+db.replica1.url=jdbc:mariadb://IP\uC8FC\uC18C \uC785\uB825/dev_db?allowMultiQueries=true
+db.replica2.url=
+db.replica3.url=
+db.replica4.url=
+db.replica5.url=
+db.primary.userName=ID
+db.primary.password=\uBE44\uBC00\uBC88\uD638
+
+
+
+# mail attached file path
+mail.file.path=C:/downloads/
+mail.account=
+mail.password=
+
+# email send
+email.host=mail.g2works.kr
+email.port=587
+email.userName=supportowl@wisestone.kr
+email.password=Stone0620**
+email.transport.protocol=smtp
+email.smtp.auth=true
+email.smtp.starttle.enable=true
+email.debug=true
+
+# \uD68C\uACC4 \uB2F4\uB2F9\uC790 \uACB0\uC81C \uCDE8\uC18C \uC54C\uB9BC - \uD68C\uACC4 \uB2F4\uB2F9\uC790\uB294 \uAF2D \uC2DC\uC2A4\uD15C\uC5D0 \uAC00\uC785 \uB418\uC5B4 \uC788\uC5B4\uC57C \uD55C\uB2E4.
+payment.cancel.manager.email=jslee1@wisestone.kr
+
+# \uC0AC\uC6A9\uC790 \uAC00\uC785 \uC815\uBCF4 \uC54C\uB9BC - OWL ITS \uC9C1\uBB34 \uAD00\uB828\uC790\uC5D0\uAC8C \uC804\uC1A1
+user.join.statistics.email=jslee1@wisestone.kr
+
+# \uC2DC\uC2A4\uD15C \uD604\uD669 \uC815\uBCF4 \uC54C\uB9BC
+total.statistics.email=jslee1@wisestone.kr
+
+# saas service max user & use period
+saas.maxUser=10
+saas.period=30
+
+# \uD658\uC728
+saas.usdkrw=1163
+
+# use aws or not
+# added by zenith at 20200623
+use.aws=false
+
+# aws upload path
+attached.file.path=/dev-upload/
+profile.file.path=/dev-profile
+# aws bucket name
+aws.bucket.name=wisestone
+# aws s3 url
+aws.s3.url=https://s3.ap-northeast-2.amazonaws.com/
+# OAuth 2.0 \uC778\uC99D \uC815\uBCF4
+OAuth.google.clientId=545115864261-lumkhr0qhei643koiva5b130410s032e.apps.googleusercontent.com
+OAuth.google.clientSecret=olvwp9OipUzaAj86Hx5HKPE5
+OAuth.google.redirectUri=http://localhost:8080/googleOAuth2CallBack
+
+OAuth.naver.clientId=Trl8vV30ctsUDlgGoWqZ
+OAuth.naver.clientSecret=suJXIbB8dz
+
+OAuth.kakao.clientId=13d56a63b9b9b1003d779261ce1651e3
+OAuth.kakao.clientSecret=kumDB7dtnBumpjydGZPqScl7Vd1tezcq
+
+OAuth.facebook.clientId=1967163700251105
+OAuth.facebook.clientSecret=34c4c009bc85caf08a6e27ecfe65744d
+OAuth.facebook.redirectUri=https://www.owlsolution.io/facebookOAuth2CallBack
+
+OAuth.common.state=state_parameter_owl_its_value
+
+# redis setting
+redis.host=192.168.0.64
+redis.port=6379
+redis.database=0
+
diff --git a/src/main/resources/system_dev.properties b/src/main/resources/system_dev.properties
new file mode 100644
index 0000000..7648f2d
--- /dev/null
+++ b/src/main/resources/system_dev.properties
@@ -0,0 +1,102 @@
+# mariaDB \uC124\uC815
+db.primary.driverName=org.mariadb.jdbc.Driver
+db.primary.url=jdbc:mariadb://127.0.0.1/dev_db?allowMultiQueries=true
+db.replica1.url=jdbc:mariadb://127.0.0.1/dev_db?allowMultiQueries=true
+db.replica2.url=
+db.replica3.url=
+db.replica4.url=
+db.replica5.url=
+db.primary.userName=root
+db.primary.password=maponrex
+
+
+# elasticSearch \uC124\uC815
+elastic.search.hosts=http://52.78.198.178:9200
+
+# kafka \uC124\uC815
+# use kafka or not
+# kafka by zenith at 20200625
+use.kafka=false
+
+# kafka.bootstrap.servers=ec2-52-78-150-61.ap-northeast-2.compute.amazonaws.com:9092,ec2-52-79-150-7.ap-northeast-2.compute.amazonaws.com:9092,ec2-52-79-193-191.ap-northeast-2.compute.amazonaws.com:9092
+kafka.bootstrap.servers=127.0.0.2:9092
+kafka.consumer.group.id=dev-common-group
+kafka.common.topic=dev-common-topic
+
+# redis \uC124\uC815
+redis.host=127.0.0.1
+redis.port=6379
+redis.common.topic=dev-common-topic
+
+# License Key \uC124\uC815
+owl.license.key=1234
+
+# mail attached file path
+mail.file.path=C:/downloads/
+mail.account=
+mail.password=
+
+# email \uC124\uC815
+email.host=mail.g2works.kr
+email.port=587
+email.userName=supportowl@wisestone.kr
+email.password=Stone0620**
+email.transport.protocol=smtp
+email.smtp.auth=true
+email.smtp.starttle.enable=true
+email.debug=true
+email.sendUrl=http://localhost:8081
+
+# \uD68C\uACC4 \uB2F4\uB2F9\uC790 \uACB0\uC81C \uCDE8\uC18C \uC54C\uB9BC - \uD68C\uACC4 \uB2F4\uB2F9\uC790\uB294 \uAF2D \uC2DC\uC2A4\uD15C\uC5D0 \uAC00\uC785 \uB418\uC5B4 \uC788\uC5B4\uC57C \uD55C\uB2E4.
+payment.cancel.manager.email=jslee1@wisestone.kr
+
+# \uC0AC\uC6A9\uC790 \uAC00\uC785 \uC815\uBCF4 \uC54C\uB9BC - OWL ITS \uC9C1\uBB34 \uAD00\uB828\uC790\uC5D0\uAC8C \uC804\uC1A1
+user.join.statistics.email=jslee1@wisestone.kr
+
+# \uC2DC\uC2A4\uD15C \uD604\uD669 \uC815\uBCF4 \uC54C\uB9BC
+total.statistics.email=jslee1@wisestone.kr
+
+# saas service max user & use period
+# packageteyp 1 : lite (~200 Users), 2 : medium (~500 users), 3 : Enterprice (~1000 users) (0 \uC740 demo version)
+saas.packagetype=1 
+
+saas.maxUser=200
+saas.period=3650
+
+# \uD658\uC728
+saas.usdkrw=1183
+
+# use aws or not
+# added by zenith at 20200623
+use.aws=false
+
+# upload path
+attached.file.path=/dev-upload/
+profile.file.path=/dev-profile
+
+# aws bucket name
+aws.bucket.name=wisestone-test
+# aws bucket access key
+aws.access.key=AKIARX6BJQMZKUYEEJVD
+aws.access.password=eAQvouvSJJFl47h2dkMJji/6OtzsGBGF4h9Df3qH
+# aws s3 url
+# aws.s3.url=https://s3.ap-northeast-2.amazonaws.com/
+aws.s3.url=http://localhost:8081/
+
+# OAuth 2.0 \uC778\uC99D \uC815\uBCF4 \uC124\uC815
+OAuth.google.clientId=545115864261-lumkhr0qhei643koiva5b130410s032e.apps.googleusercontent.com
+OAuth.google.clientSecret=olvwp9OipUzaAj86Hx5HKPE5
+OAuth.google.redirectUri=http://localhost:8080/googleOAuth2CallBack
+
+OAuth.naver.clientId=Trl8vV30ctsUDlgGoWqZ
+OAuth.naver.clientSecret=suJXIbB8dz
+
+OAuth.kakao.clientId=8db70e7979edc86b76c7b1d33312282d
+OAuth.kakao.clientSecret=yIpsPh81H326UL7jdcXAu4OhfmKnmmpx
+
+OAuth.facebook.clientId=1967163700251105
+OAuth.facebook.clientSecret=34c4c009bc85caf08a6e27ecfe65744d
+OAuth.facebook.redirectUri=https://www.owlsolution.io/facebookOAuth2CallBack
+
+OAuth.common.state=state_parameter_owl_its_value
+
diff --git a/src/main/resources/system_prod.properties b/src/main/resources/system_prod.properties
new file mode 100644
index 0000000..29b8c63
--- /dev/null
+++ b/src/main/resources/system_prod.properties
@@ -0,0 +1,92 @@
+db.primary.driverName=org.mariadb.jdbc.Driver
+db.primary.url=jdbc:mariadb://IP\uC8FC\uC18C \uC785\uB825/dev_db?allowMultiQueries=true
+db.replica1.url=jdbc:mariadb://IP\uC8FC\uC18C \uC785\uB825/dev_db?allowMultiQueries=true
+db.replica2.url=
+db.replica3.url=
+db.replica4.url=
+db.replica5.url=
+db.primary.userName=ID
+db.primary.password=\uBE44\uBC00\uBC88\uD638
+
+
+# elasticSearch \uC124\uC815
+elastic.search.hosts=http://52.78.198.178:9200
+
+# kafka \uC124\uC815
+kafka.bootstrap.servers=ec2-52-78-150-61.ap-northeast-2.compute.amazonaws.com:9092,ec2-52-79-150-7.ap-northeast-2.compute.amazonaws.com:9092,ec2-52-79-193-191.ap-northeast-2.compute.amazonaws.com:9092
+kafka.consumer.group.id=prod-common-group
+kafka.common.topic=prod-common-topic
+
+
+# redis setting
+redis.host=prd-session-01.80nhsk.ng.0001.apn2.cache.amazonaws.com
+redis.port=6379
+redis.common.topic=prod-common-topic
+
+
+# mail attached file path
+mail.file.path=C:/downloads/
+mail.account=
+mail.password=
+
+# email send
+email.host=mail.g2works.kr
+email.port=587
+email.userName=supportowl@wisestone.kr
+email.password=Stone0620**
+email.transport.protocol=smtp
+email.smtp.auth=true
+email.smtp.starttle.enable=true
+email.debug=true
+email.sendUrl=http://maprex.iptime.org:8081
+
+# \uD68C\uACC4 \uB2F4\uB2F9\uC790 \uACB0\uC81C \uCDE8\uC18C \uC54C\uB9BC - \uD68C\uACC4 \uB2F4\uB2F9\uC790\uB294 \uAF2D \uC2DC\uC2A4\uD15C\uC5D0 \uAC00\uC785 \uB418\uC5B4 \uC788\uC5B4\uC57C \uD55C\uB2E4.
+payment.cancel.manager.email=wisestoneowl@gmail.com
+
+# \uC0AC\uC6A9\uC790 \uAC00\uC785 \uC815\uBCF4 \uC54C\uB9BC - OWL ITS \uC9C1\uBB34 \uAD00\uB828\uC790\uC5D0\uAC8C \uC804\uC1A1
+user.join.statistics.email=saheo@wisestone.kr, yjlee1@wisestone.kr, twgoh@wisestone.kr, stone@wisestone.kr, bmkim2@wisestone.kr, seshin@wisestone.kr
+
+# \uC2DC\uC2A4\uD15C \uD604\uD669 \uC815\uBCF4 \uC54C\uB9BC
+total.statistics.email=saheo@wisestone.kr, bmkim2@wisestone.kr, seshin@wisestone.kr, yjlee1@wisestone.kr, twgoh@wisestone.kr, stone@wisestone.kr
+
+
+# saas service max user & use period
+saas.maxUser=10
+saas.period=180
+
+# \uD658\uC728
+saas.usdkrw=1221
+
+# use aws or not
+# added by zenith at 20200623
+use.aws=false
+
+# aws upload path
+attached.file.path=/prod-upload/
+profile.file.path=/prod-profile
+# aws bucket name
+aws.bucket.name=wisestone
+
+# aws bucket access key
+aws.access.key=AKIAIJ75KSHFO65GIVNA
+aws.access.password=7SfHA9wwWded9baqtnnGi230xhMjV5s/YilZbSjS
+
+# aws s3 url
+aws.s3.url=https://s3.ap-northeast-2.amazonaws.com/
+
+# OAuth 2.0 \uC778\uC99D \uC815\uBCF4
+OAuth.google.clientId=555830465708-9d50teb7cqgovfp9pqf3ouc4a3lc215f.apps.googleusercontent.com
+OAuth.google.clientSecret=49rssyY8Vv9PDCXKjxhHO7uf
+OAuth.google.redirectUri=https://owlsolution.io/googleOAuth2CallBack
+
+OAuth.naver.clientId=Trl8vV30ctsUDlgGoWqZ
+OAuth.naver.clientSecret=suJXIbB8dz
+
+OAuth.kakao.clientId=13d56a63b9b9b1003d779261ce1651e3
+OAuth.kakao.clientSecret=kumDB7dtnBumpjydGZPqScl7Vd1tezcq
+
+OAuth.facebook.clientId=1967163700251105
+OAuth.facebook.clientSecret=34c4c009bc85caf08a6e27ecfe65744d
+OAuth.facebook.redirectUri=https://www.owlsolution.io/facebookOAuth2CallBack
+
+OAuth.common.state=state_parameter_owl_its_value
diff --git a/src/main/resources/system_test.properties b/src/main/resources/system_test.properties
new file mode 100644
index 0000000..3013451
--- /dev/null
+++ b/src/main/resources/system_test.properties
@@ -0,0 +1,90 @@
+# mariaDB \uC124\uC815
+db.primary.url=jdbc:mariadb://IP\uC8FC\uC18C \uC785\uB825/dev_db?allowMultiQueries=true
+db.replica1.url=jdbc:mariadb://IP\uC8FC\uC18C \uC785\uB825/dev_db?allowMultiQueries=true
+db.replica2.url=
+db.replica3.url=
+db.replica4.url=
+db.replica5.url=
+db.primary.userName=ID
+db.primary.password=\uBE44\uBC00\uBC88\uD638
+
+
+# elasticSearch \uC124\uC815
+elastic.search.hosts=http://52.78.198.178:9200
+
+# kafka \uC124\uC815
+kafka.bootstrap.servers=ec2-52-78-150-61.ap-northeast-2.compute.amazonaws.com:9092,ec2-52-79-150-7.ap-northeast-2.compute.amazonaws.com:9092,ec2-52-79-193-191.ap-northeast-2.compute.amazonaws.com:9092
+kafka.consumer.group.id=test-common-group
+kafka.common.topic=test-common-topic
+
+# redis \uC124\uC815
+#  test-session-01.9pesbb.ng.0001.apn2.cache.amazonaws.com
+redis.host=192.168.0.64
+redis.port=6379
+redis.common.topic=test-common-topic
+
+# mail attached file path
+mail.file.path=C:/downloads/
+mail.account=
+mail.password=
+
+# email send
+email.host=mail.g2works.kr
+email.port=587
+email.userName=supportowl@wisestone.kr
+email.password=Stone0620**
+email.transport.protocol=smtp
+email.smtp.auth=true
+email.smtp.starttle.enable=true
+email.debug=true
+
+# \uD68C\uACC4 \uB2F4\uB2F9\uC790 \uACB0\uC81C \uCDE8\uC18C \uC54C\uB9BC - \uD68C\uACC4 \uB2F4\uB2F9\uC790\uB294 \uAF2D \uC2DC\uC2A4\uD15C\uC5D0 \uAC00\uC785 \uB418\uC5B4 \uC788\uC5B4\uC57C \uD55C\uB2E4.
+payment.cancel.manager.email=jslee1@wisestone.kr
+
+# \uC0AC\uC6A9\uC790 \uAC00\uC785 \uC815\uBCF4 \uC54C\uB9BC - OWL ITS \uC9C1\uBB34 \uAD00\uB828\uC790\uC5D0\uAC8C \uC804\uC1A1
+user.join.statistics.email=jslee1@wisestone.kr
+
+# \uC2DC\uC2A4\uD15C \uD604\uD669 \uC815\uBCF4 \uC54C\uB9BC
+total.statistics.email=jslee1@wisestone.kr
+
+# saas service max user & use period
+saas.maxUser=10
+saas.period=30
+
+# \uD658\uC728
+saas.usdkrw=1163
+
+# use aws or not
+# added by zenith at 20200623
+use.aws=false
+
+# aws upload path
+attached.file.path=/test-upload/
+profile.file.path=/test-profile
+# aws bucket name
+aws.bucket.name=wisestone-test
+# aws bucket access key
+aws.access.key=AKIARX6BJQMZKUYEEJVD
+aws.access.password=eAQvouvSJJFl47h2dkMJji/6OtzsGBGF4h9Df3qH
+
+# aws s3 url
+aws.s3.url=https://s3.ap-northeast-2.amazonaws.com/
+# OAuth 2.0 \uC778\uC99D \uC815\uBCF4
+OAuth.google.clientId=545115864261-lumkhr0qhei643koiva5b130410s032e.apps.googleusercontent.com
+OAuth.google.clientSecret=olvwp9OipUzaAj86Hx5HKPE5
+OAuth.google.redirectUri=http://localhost:8080/googleOAuth2CallBack
+
+OAuth.naver.clientId=Trl8vV30ctsUDlgGoWqZ
+OAuth.naver.clientSecret=suJXIbB8dz
+
+OAuth.kakao.clientId=13d56a63b9b9b1003d779261ce1651e3
+OAuth.kakao.clientSecret=kumDB7dtnBumpjydGZPqScl7Vd1tezcq
+
+OAuth.facebook.clientId=1967163700251105
+OAuth.facebook.clientSecret=34c4c009bc85caf08a6e27ecfe65744d
+OAuth.facebook.redirectUri=https://www.owlsolution.io/facebookOAuth2CallBack
+
+OAuth.common.state=state_parameter_owl_its_value
+
+
+

--
Gitblit v1.8.0