From 6adb341db180240e0af34ace40100912d4ed5257 Mon Sep 17 00:00:00 2001
From: 이민희 <mhlee@maprex.co.kr>
Date: 목, 06 1월 2022 11:02:57 +0900
Subject: [PATCH] Merge branch 'master' of http://192.168.0.25:9001/r/owl-kisa

---
 src/main/webapp/scripts/app/project/projectModify.controller.js                  |    2 
 src/main/webapp/scripts/app/issue/issue.js                                       |    4 
 src/main/java/kr/wisestone/owl/web/form/ApiTokenForm.java                        |    8 
 src/main/resources/mybatis/query-template/issue-template.xml                     |    8 
 src/main/java/kr/wisestone/owl/config/SwaggerConfig.java                         |   94 ++
 src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java                 |    2 
 pom.xml                                                                          |   33 
 src/main/java/kr/wisestone/owl/web/condition/IssueCustomFieldValueCondition.java |   23 
 src/main/webapp/custom_components/js-rel/relColumnGenerator.directive.js         |  142 +++
 src/main/java/kr/wisestone/owl/web/form/ApiIssueAddForm.java                     |   53 +
 src/main/java/package-info.java                                                  |    4 
 src/main/webapp/scripts/components/auth/auth.interceptor.js                      |   10 
 src/main/webapp/custom_components/js-down/js-down.html                           |   47 +
 src/main/java/kr/wisestone/owl/web/controller/Api/ApiController.java             |   98 ++
 src/main/java/kr/wisestone/owl/web/controller/IssueController.java               |   12 
 src/main/webapp/views/issue/issueSendMailPartners.html                           |   69 +
 src/main/webapp/custom_components/js-down/js-down.directive.js                   |   36 
 src/main/webapp/scripts/app/issue/issueSendMailPartners.controller.js            |   21 
 src/main/webapp/scripts/main.js                                                  |   26 
 src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java                |   36 
 src/main/java/kr/wisestone/owl/web/form/ApiIssueModifyForm.java                  |   63 +
 src/main/webapp/custom_components/js-rel/js-rel.directive.js                     |   36 
 src/main/webapp/custom_components/js-down/down.provider.js                       |  232 +++++
 src/main/java/kr/wisestone/owl/web/form/EmailCommonForm.java                     |   55 +
 src/main/webapp/scripts/components/issue/issue.service.js                        |    6 
 src/main/java/kr/wisestone/owl/constant/MsgConstants.java                        |   61 +
 src/main/webapp/scripts/app/issue/issueDetail.controller.js                      |  202 ++--
 src/main/resources/log4j2.xml                                                    |    8 
 src/main/webapp/assets/styles/main.css                                           |    8 
 /dev/null                                                                        |   71 -
 src/main/webapp/views/issue/issueCommonSendMail.html                             |   69 +
 src/main/webapp/scripts/app/issue/issueCommonSendMail.controller.js              |  111 ++
 src/main/webapp/scripts/config.js                                                |   11 
 src/main/java/kr/wisestone/owl/service/IssueService.java                         |    3 
 src/main/java/kr/wisestone/owl/web/controller/ApiTokenController.java            |   21 
 src/main/webapp/custom_components/js-rel/js-rel.html                             |   47 +
 src/main/java/kr/wisestone/owl/web/form/IssueApiForm.java                        |   65 
 src/main/webapp/scripts/app/issue/issueList.controller.js                        |    5 
 src/main/webapp/i18n/ko/global.json                                              |   13 
 src/main/webapp/views/issue/issueDetail.html                                     |   11 
 src/main/webapp/custom_components/js-down/downColumnGenerator.directive.js       |  141 +++
 src/main/webapp/custom_components/js-table/tableColumnGenerator.directive.js     |  234 ++--
 src/main/webapp/custom_components/js-rel/rel.provider.js                         |  239 +++++
 43 files changed, 2,048 insertions(+), 392 deletions(-)

diff --git a/pom.xml b/pom.xml
index 569ef29..b2bb567 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,7 +48,7 @@
 
         <!-- Logging -->
         <slf4j.version>1.7.28</slf4j.version>
-        <log4j.version>2.12.1</log4j.version>
+        <log4j.version>2.17.1</log4j.version>
 
         <!-- Spring Session Redis -->
         <spring.session.version>2.1.8.RELEASE</spring.session.version>
@@ -67,6 +67,9 @@
 
         <!-- Elastic Search -->
         <elastic.search.version>7.3.0</elastic.search.version>
+
+        <!-- jsoup -->
+        <jsoup.version>1.8.3</jsoup.version>
 
         <!-- Util -->
         <jackson.version>2.9.9</jackson.version>
@@ -151,6 +154,11 @@
             <version>${java.mail.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>javax.interceptor</groupId>
+            <artifactId>javax.interceptor-api</artifactId>
+            <version>1.2</version>
+        </dependency>
         <!--    Excel import    -->
         <dependency>
             <groupId>org.apache.poi</groupId>
@@ -255,6 +263,14 @@
             <artifactId>commons-validator</artifactId>
             <version>${common.validator.version}</version>
         </dependency>
+
+        <!-- jsoup -->
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>${jsoup.version}</version>
+        </dependency>
+
 
         <!-- spring -->
         <dependency>
@@ -505,6 +521,17 @@
             <version>0.9.1</version>
         </dependency>
 
+        <!-- Swagger(api 臾몄꽌�솕) -->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>2.9.2</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>2.9.2</version>
+        </dependency>
     </dependencies>
 
     <repositories>
@@ -545,10 +572,6 @@
             <url>https://maven.java.net/content/repositories/public/</url>
         </repository>
 
-        <repository>
-            <id>JBoss repository</id>
-            <url>http://repository.jboss.org/nexus/content/groups/public/</url>
-        </repository>
     </repositories>
 
     <build>
diff --git a/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java b/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java
index 62056ea..3df8251 100644
--- a/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java
+++ b/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java
@@ -145,7 +145,9 @@
                 .antMatchers("/language/change").permitAll()
                 .antMatchers("/security/*").permitAll()
                 .antMatchers("/api/issue").permitAll()
+                .antMatchers("/api/issue/*").permitAll()
                 .antMatchers("/**/*").authenticated();
+
 //        http.addFilter(new CustomAuthenticationFilter());
 //        http.addFilterBefore(new CustomAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
 
diff --git a/src/main/java/kr/wisestone/owl/config/SwaggerConfig.java b/src/main/java/kr/wisestone/owl/config/SwaggerConfig.java
new file mode 100644
index 0000000..ab42cb3
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/config/SwaggerConfig.java
@@ -0,0 +1,94 @@
+package kr.wisestone.owl.config;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Parameter;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * API 臾몄꽌 �젣�옉 �뙣�궎吏� Swagger �꽕�젙 �겢�옒�뒪
+ */
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+    private static final String API_NAME = "OWL API";
+    private static final String API_VERSION = "1.0.0";
+    private static final String API_DESCRIPTION = "OWL API 紐낆꽭�꽌";
+
+
+    /**
+     * API �꽕�젙
+     * @return docket class
+     */
+    @Bean
+    public Docket api() {
+//        Parameter parameterBuilder = new ParameterBuilder()
+//                .name(HttpHeaders.AUTHORIZATION)
+//                .description("Access Tocken")
+//                .modelRef(new ModelRef("string"))
+//                .parameterType("header")
+//                .required(false)
+//                .build();
+
+        List<Parameter> globalParameters = new ArrayList<>();
+//        globalParameters.add(parameterBuilder);
+
+        return new Docket(DocumentationType.SWAGGER_2)
+                .globalOperationParameters(globalParameters)
+                .consumes(getConsumeContentTypes())
+                .produces(getProduceContentTypes())
+                .apiInfo(apiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("kr.wisestone.owl.web.controller.Api"))
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+
+    /**
+     * API 臾몄꽌 ���씠��/踰꾩쟾/�꽕紐� �꽕�젙
+     * @return ApiInfo �겢�옒�뒪
+     */
+    public ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title(API_NAME)
+                .version(API_VERSION)
+                .description(API_DESCRIPTION)
+                .build();
+    }
+
+
+    /**
+     * API 臾몄꽌 �뀒�뒪�듃 Request Content-Type �꽕�젙 異붽�
+     * @return consumes content-type 紐⑸줉
+     */
+    private Set<String> getConsumeContentTypes() {
+        Set<String> consumes = new HashSet<>();
+        consumes.add("application/json;charset=UTF-8");
+        consumes.add("application/x-www-form-urlencoded");
+        return consumes;
+    }
+
+    /**
+     * API 臾몄꽌 �뀒�뒪�듃 Response content-type �꽕�젙 異붽�
+     * @return produce content-type 紐⑸줉
+     */
+    private Set<String> getProduceContentTypes() {
+        Set<String> produces = new HashSet<>();
+        produces.add("application/json;charset=UTF-8");
+        return produces;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/kr/wisestone/owl/constant/MsgConstants.java b/src/main/java/kr/wisestone/owl/constant/MsgConstants.java
index e581672..b2f6bc2 100644
--- a/src/main/java/kr/wisestone/owl/constant/MsgConstants.java
+++ b/src/main/java/kr/wisestone/owl/constant/MsgConstants.java
@@ -1,6 +1,7 @@
 package kr.wisestone.owl.constant;
 
 /**
+ * exception �슜 硫붿꽭吏� �겢�옒�뒪
  * Created by jeong on 2017-08-02.
  */
 public class MsgConstants {
@@ -242,16 +243,52 @@
 
     public static final String EMAIL_TEMPLATE_NOT_EXIST = "EMAIL_TEMPLATE_NOT_EXIST"; //  �씠硫붿씪 �뀥�뵆由우쓣 李얠쓣�닔 �뾾�뒿�땲�떎.
 
-    public static final String API_PARAMETER_ISSUE_TYPE_ERROR = "API_PARAMETER_ISSUE_TYPE_ERROR";     // api �뙆�씪誘명꽣 �삤瑜�(�씠�뒋���엯)
-    public static final String API_PARAMETER_PROJECT_ERROR = "API_PARAMETER_PROJECT_ERROR";     // api �뙆�씪誘명꽣 �삤瑜�(�봽濡쒖젥�듃)
-    public static final String API_PARAMETER_ERROR = "API_PARAMETER_ERROR";     // api �뙆�씪誘명꽣 �삤瑜�
-    public static final String API_USER_ERROR = "API_USER_ERROR";     // api �궗�슜�옄 �삤瑜�
-    public static final String API_OVERLAP_ERROR = "API_OVERLAP_ERROR";     // API 以묐났�맂 �긽�쐞 �씠�뒋媛� �뿬�윭媛쒖씪 寃쎌슦
-    public static final String API_OVERLAP_SETTING_NOT_EXIST = "API_OVERLAP_SETTING_NOT_EXIST";     // API 以묐났�맂 �꽕�젙�씠 �븞�릺�뼱 �엳�쓣 寃쎌슦
-    public static final String API_ISSUE_NOT_EXIST = "API_ISSUE_NOT_EXIST";     // �닔�젙�븷 �씠�뒋瑜� 李얠쓣�닔 �뾾�뒿�땲�떎.
-    public static final String API_COMPLETE_ISSUE_STATUS_NOT_EXIST = "API_COMPLETE_ISSUE_STATUS_NOT_EXIST";     // �옄�룞 醫낅즺 泥섎━�븷 �긽�깭媛� �꽕�젙�릺吏� �븡�븯�뒿�땲�떎.
-    public static final String API_ISSUE_STATUS_NOT_EXIST = "API_ISSUE_STATUS_NOT_EXIST";     // �씠�뒋 �긽�깭瑜� 李얠쓣�닔 �뾾�뒿�땲�떎
-    public static final String API_ISSUE_STATUS_IS_NULL = "API_ISSUE_STATUS_IS_NULL";     // �씠�뒋 �긽�깭 媛믪씠 �뾾�뒿�땲�떎.
-    public static final String API_CUSTOM_FIELD_NOT_EXIST = "API_CUSTOM_FIELD_NOT_EXIST";     // �궗�슜�옄 �젙�쓽 �븘�뱶瑜� 議댁옱�븯吏� �븡�뒿�땲�떎.
-    public static final String API_ISSUE_STATUS_NOT_IN_WORKFLOW = "API_ISSUE_STATUS_NOT_IN_WORKFLOW";     // �씠�뒋 �긽�깭媛� �썙�겕�뵆濡쒖슦�뿉 �룷�븿�릺�뼱 �엳吏� �븡�뒿�땲�떎
+    /**
+     * api �뙆�씪誘명꽣 �삤瑜�(�씠�뒋���엯)
+     */
+    public static final String API_PARAMETER_ISSUE_TYPE_ERROR = "API_PARAMETER_ISSUE_TYPE_ERROR";
+    /**
+     * api �뙆�씪誘명꽣 �삤瑜�(�봽濡쒖젥�듃)
+     */
+    public static final String API_PARAMETER_PROJECT_ERROR = "API_PARAMETER_PROJECT_ERROR";
+    /**
+     * api �뙆�씪誘명꽣 �삤瑜�
+     */
+    public static final String API_PARAMETER_ERROR = "API_PARAMETER_ERROR";
+    /**
+     * api �궗�슜�옄 �삤瑜�
+     */
+    public static final String API_USER_ERROR = "API_USER_ERROR";
+    /**
+     * API 以묐났�맂 �긽�쐞 �씠�뒋媛� �뿬�윭媛쒖씪 寃쎌슦
+     */
+    public static final String API_OVERLAP_ERROR = "API_OVERLAP_ERROR";
+    /**
+     * API 以묐났�맂 �꽕�젙�씠 �븞�릺�뼱 �엳�쓣 寃쎌슦
+     */
+    public static final String API_OVERLAP_SETTING_NOT_EXIST = "API_OVERLAP_SETTING_NOT_EXIST";
+    /**
+     * �닔�젙�븷 �씠�뒋瑜� 李얠쓣�닔 �뾾�뒿�땲�떎.
+     */
+    public static final String API_ISSUE_NOT_EXIST = "API_ISSUE_NOT_EXIST";
+    /**
+     * �옄�룞 醫낅즺 泥섎━�븷 �긽�깭媛� �꽕�젙�릺吏� �븡�븯�뒿�땲�떎.
+     */
+    public static final String API_COMPLETE_ISSUE_STATUS_NOT_EXIST = "API_COMPLETE_ISSUE_STATUS_NOT_EXIST";
+    /**
+     * �씠�뒋 �긽�깭瑜� 李얠쓣�닔 �뾾�뒿�땲�떎
+     */
+    public static final String API_ISSUE_STATUS_NOT_EXIST = "API_ISSUE_STATUS_NOT_EXIST";
+    /**
+     * �씠�뒋 �긽�깭 媛믪씠 �뾾�뒿�땲�떎.
+     */
+    public static final String API_ISSUE_STATUS_IS_NULL = "API_ISSUE_STATUS_IS_NULL";
+    /**
+     * �궗�슜�옄 �젙�쓽 �븘�뱶瑜� 議댁옱�븯吏� �븡�뒿�땲�떎.
+     */
+    public static final String API_CUSTOM_FIELD_NOT_EXIST = "API_CUSTOM_FIELD_NOT_EXIST";
+    /**
+     * �씠�뒋 �긽�깭媛� �썙�겕�뵆濡쒖슦�뿉 �룷�븿�릺�뼱 �엳吏� �븡�뒿�땲�떎
+     */
+    public static final String API_ISSUE_STATUS_NOT_IN_WORKFLOW = "API_ISSUE_STATUS_NOT_IN_WORKFLOW";
 }
diff --git a/src/main/java/kr/wisestone/owl/service/IssueService.java b/src/main/java/kr/wisestone/owl/service/IssueService.java
index caeca69..c47ce15 100644
--- a/src/main/java/kr/wisestone/owl/service/IssueService.java
+++ b/src/main/java/kr/wisestone/owl/service/IssueService.java
@@ -9,6 +9,7 @@
 import kr.wisestone.owl.web.condition.DepartmentCondition;
 import kr.wisestone.owl.web.condition.IssueCondition;
 import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.form.EmailCommonForm;
 import kr.wisestone.owl.web.form.EmailTemplateForm;
 import kr.wisestone.owl.web.form.IssueApiForm;
 import kr.wisestone.owl.web.form.IssueForm;
@@ -112,4 +113,6 @@
     void setCountDownIssues(List<IssueVo> issueVos);
 
     void makeIssueMapToIssue(Issue issue, Map<String, Object> issueMap);
+
+    void sendCommonEmail(EmailCommonForm make);
 }
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
index b4b9710..7be00d2 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
@@ -289,7 +289,7 @@
                     issueApiForm.addUseIssueCustomFieldId(customFieldApiOverlap.getCustomField().getId());
                 }
 
-                // 以묐났�맂 �씠�뒋寃��깋
+                // 醫낅즺�긽�깭媛� �븘�땶 以묐났�맂 �긽�쐞 �씠�뒋寃��깋
                 List<Issue> issues = this.findIssue(issueApiForm, customFieldApiOverlaps, user.getId());
                 int size = issues.size();
                 if (size > 0) {
@@ -446,6 +446,7 @@
             issueCustomFieldValueCondition.setUseValue(concatUseValue);
             issueCustomFieldValueCondition.setUseValues(userValues);
             issueCustomFieldValueCondition.setIssueTypeId(issueApiform.getIssueTypeId());
+            issueCustomFieldValueCondition.setIssueStatusType("CLOSE");
             List<Map<String, Object>> results = this.issueMapper.findByCustomFieldValue(issueCustomFieldValueCondition);
             if (results != null && results.size() > 0) {
                 for (Map<String, Object> result : results) {
@@ -944,7 +945,7 @@
 //        if (!this.userWorkspaceService.checkWorkspaceManager(user)
 //                && !MngPermission.checkMngPermission(userLevel.getPermission(), MngPermission.USER_PERMISSION_MNG_ISSUE)) { //理쒓퀬愿�由ъ옄 & �봽濡쒖젥�듃,�씠�뒋 愿�由ъ옄 �씪 寃쎌슦 紐⑤뱺 �씠�뒋 蹂닿린
 //            this.SetMyDepartmentId(issueCondition);
-            //this.SetAllDepartmentId(issueCondition);
+        //this.SetAllDepartmentId(issueCondition);
 //        } /*else{
 //            results = this.issueMapper.findByDepartment(issueCondition);
 //            totalCount = this.issueMapper.countByDepartment(issueCondition);
@@ -3593,6 +3594,37 @@
         this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.SEND, sb.toString());
     }
 
+    @Override
+    public void sendCommonEmail(EmailCommonForm emailCommonForm) {
+        if (emailCommonForm.getSendEmails().size() < 1) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_SEND_USER));
+        } else if (emailCommonForm.getIssueId() == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_EXIST));
+        }
+
+        Issue issue = this.getIssue(emailCommonForm.getIssueId());
+
+        //  諛쒖떊�옄 �몴�떆
+        User user = this.webAppUtil.getLoginUserObject();
+        UserVo toUser = this.webAppUtil.getLoginUser();
+
+        //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_ANOTHER_USER_SEND_EMAIL));
+        StringBuilder sb = new StringBuilder();
+
+        Locale locale = CommonUtil.getUserLanguage(user.getLanguage());
+        String[] sendMails = ConvertUtil.ToArray(emailCommonForm.getSendEmails());
+        for(int i=0; i < sendMails.length; i++) {
+            sendMails[i] = CommonUtil.decryptAES128(sendMails[i]);
+        }
+        this.systemEmailService.sendEmail(emailCommonForm.getTitle(), emailCommonForm.getDescription(), sendMails, null);
+
+        this.issueHistoryService.detectSendIssueMail(IssueHistoryType.SEND, emailCommonForm.getSendEmails(), sb);
+        this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.SEND, sb.toString());
+    }
+
     //  �삁�빟 諛쒖깮 �씠�뒋瑜� �떎�뻾�븳�떎
     @Override
     @Transactional
diff --git a/src/main/java/kr/wisestone/owl/web/condition/IssueCustomFieldValueCondition.java b/src/main/java/kr/wisestone/owl/web/condition/IssueCustomFieldValueCondition.java
index 063557f..5475ac0 100644
--- a/src/main/java/kr/wisestone/owl/web/condition/IssueCustomFieldValueCondition.java
+++ b/src/main/java/kr/wisestone/owl/web/condition/IssueCustomFieldValueCondition.java
@@ -10,6 +10,7 @@
 import java.util.Map;
 
 /**
+ * �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶 媛� 寃��깋 議곌굔 �겢�옒�뒪
  * Created by wisestone on 2018-06-07.
  */
 public class IssueCustomFieldValueCondition {
@@ -17,9 +18,19 @@
     private Long workspaceId;
     private Long customFieldId;
     private String customFieldType;
-    private List<String> useValues = Lists.newArrayList();  //  �떒�씪, �떎以� �씪�븣 寃��깋 媛�
-    private String useValue;    //  �뀓�뒪�듃 �븘�뱶�씪 �븣 寃��깋 媛�
+    /**
+     * �궗�슜�옄 �젙�쓽 �븘�뱶 �궗�슜 媛�
+     */
+    private List<String> useValues = Lists.newArrayList();
+    /**
+     * �뀓�뒪�듃 �븘�뱶�씪 �븣 寃��깋 媛�
+     */
+    private String useValue;
     private boolean useParentIssueId = true;
+    /**
+     * �씠�뒋 �긽�깭 �쑀�삎(READY / OPEN / CLOSE)
+     */
+    private String issueStatusType;
 
     public IssueCustomFieldValueCondition(){}
 
@@ -62,6 +73,14 @@
         return condition;
     }
 
+    public String getIssueStatusType() {
+        return issueStatusType;
+    }
+
+    public void setIssueStatusType(String issueStatusType) {
+        this.issueStatusType = issueStatusType;
+    }
+
     public Long getIssueTypeId() {
         return issueTypeId;
     }
diff --git a/src/main/java/kr/wisestone/owl/web/controller/Api/ApiController.java b/src/main/java/kr/wisestone/owl/web/controller/Api/ApiController.java
new file mode 100644
index 0000000..0fbbf81
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/Api/ApiController.java
@@ -0,0 +1,98 @@
+package kr.wisestone.owl.web.controller.Api;
+
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import kr.wisestone.owl.constant.MsgConstants;
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.exception.OwlRuntimeException;
+import kr.wisestone.owl.service.IssueService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.web.controller.BaseController;
+import kr.wisestone.owl.web.form.ApiIssueAddForm;
+import kr.wisestone.owl.web.form.ApiIssueModifyForm;
+import kr.wisestone.owl.web.form.IssueApiForm;
+import org.json.simple.parser.ParseException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * OWL-API 而⑦듃濡ㅻ윭
+ */
+@Controller
+@RequestMapping("/api")
+public class ApiController extends BaseController {
+
+    @Autowired
+    private IssueService issueService;
+
+
+    /**
+     * �씠�뒋 異붽�
+     * @param apiIssueAddForm �엯�젰 �뤌 �뜲�씠�꽣
+     * @param files �뙆�씪
+     * @return JSON
+     * @throws OwlRuntimeException �뙆�씪誘명꽣 �삤瑜섏떆 諛쒖깮
+     * @throws CloneNotSupportedException �씠�뒋 蹂듭궗 �떆�뿉 諛쒖깮
+     */
+    @PostMapping(value = "/issue")
+    @ApiOperation(value = "�씠�뒋 異붽�", notes = "�깉濡쒖슫 �씠�뒋瑜� 異붽��븳�떎.")
+    @ApiImplicitParam(name = "files", required = false, dataType = "file")
+    public
+    @ResponseBody
+    Map<String, Object> addIssue(ApiIssueAddForm apiIssueAddForm, @RequestParam("files") List<MultipartFile> files) throws OwlRuntimeException, CloneNotSupportedException, ParseException {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        IssueApiForm issueApiForm = ConvertUtil.copyProperties(apiIssueAddForm, IssueApiForm.class);
+//        String str = request.getParameter(Constants.REQ_KEY_CONTENT);
+        issueApiForm.setMultipartFiles(files);
+        issueApiForm.parseCustomFields(apiIssueAddForm.getCustomFields());
+        issueApiForm.setApiType(IssueApiForm.ApiType.add);
+        // �궗�슜�옄 �젙�쓽 �븘�뱶媛� �뾾�쓣 寃쎌슦 寃��깋�쓣 �븷 �닔 �뾾湲� �븣臾몄뿉 �삁�쇅泥섎━
+        if (issueApiForm.getCustomFieldValues() == null || issueApiForm.getCustomFieldValues().size() == 0) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_CUSTOM_FIELD_NOT_EXIST));
+        }
+
+        List<Issue> issues = this.issueService.addApiIssue(issueApiForm);
+        //  踰꾩쟾 �깮�꽦
+        for (Issue issue : issues) {
+            this.issueService.addIssueVersion(issue.getId(), issue.getRegisterId());
+        }
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    /**
+     * �씠�뒋 �긽�깭 �닔�젙
+     * @param apiIssueModifyForm �닔�젙 �뤌 �뜲�씠�꽣
+     * @return JSON
+     * @throws OwlRuntimeException �뙆�씪誘명꽣 �삤瑜섏떆 諛쒖깮
+     * @throws CloneNotSupportedException �씠�뒋 蹂듭궗 �떆�뿉 諛쒖깮
+     */
+    @PostMapping(value = "/issue/1")
+    @ApiOperation(value = "�씠�뒋 �긽�깭 �닔�젙", notes = "�궗�슜�옄 �젙�쓽 �븘�뱶媛� �룞�씪�븳 湲곗〈 �씠�뒋瑜� 蹂�寃쏀븳�떎.")
+    public
+    @ResponseBody
+    Map<String, Object> modifyIssue(ApiIssueModifyForm apiIssueModifyForm) throws OwlRuntimeException, CloneNotSupportedException, ParseException {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        IssueApiForm issueApiForm = ConvertUtil.copyProperties(apiIssueModifyForm, IssueApiForm.class);
+        issueApiForm.parseCustomFields(apiIssueModifyForm.getCustomFields());
+        issueApiForm.setApiType(IssueApiForm.ApiType.add);
+        // �궗�슜�옄 �젙�쓽 �븘�뱶媛� �뾾�쓣 寃쎌슦 寃��깋�쓣 �븷 �닔 �뾾湲� �븣臾몄뿉 �삁�쇅泥섎━
+        if (issueApiForm.getCustomFieldValues() == null || issueApiForm.getCustomFieldValues().size() == 0) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_CUSTOM_FIELD_NOT_EXIST));
+        }
+
+        this.issueService.modifyIssue(issueApiForm, new ArrayList<>());
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
+
diff --git a/src/main/java/kr/wisestone/owl/web/controller/ApiController.java b/src/main/java/kr/wisestone/owl/web/controller/ApiController.java
deleted file mode 100644
index 5c13abc..0000000
--- a/src/main/java/kr/wisestone/owl/web/controller/ApiController.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package kr.wisestone.owl.web.controller;
-
-import kr.wisestone.owl.constant.Constants;
-import kr.wisestone.owl.constant.MsgConstants;
-import kr.wisestone.owl.domain.Issue;
-import kr.wisestone.owl.exception.OwlRuntimeException;
-import kr.wisestone.owl.service.IssueService;
-import kr.wisestone.owl.util.ConvertUtil;
-import kr.wisestone.owl.web.form.IssueApiForm;
-import org.springframework.beans.factory.annotation.Autowired;
-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.ResponseBody;
-import org.springframework.web.multipart.MultipartHttpServletRequest;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@Controller
-public class ApiController extends BaseController {
-
-    @Autowired
-    private IssueService issueService;
-
-    @RequestMapping(value = "api/issue", method = RequestMethod.POST)
-    public
-    @ResponseBody
-    Map<String, Object> addIssue(MultipartHttpServletRequest request) throws OwlRuntimeException, CloneNotSupportedException {
-        Map<String, Object> resJsonData = new HashMap<>();
-
-        String str = request.getParameter(Constants.REQ_KEY_CONTENT);
-
-        IssueApiForm issueForm = IssueApiForm.make(ConvertUtil.convertJsonToMap(str), request.getFiles("file"));
-        if (issueForm == null) {
-            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_PARAMETER_ERROR));
-        }
-        // �궗�슜�옄 �젙�쓽 �븘�뱶媛� �뾾�쓣 寃쎌슦 寃��깋�쓣 �븷 �닔 �뾾湲� �븣臾몄뿉 �삁�쇅泥섎━
-        else if (issueForm.getCustomFieldValues() == null || issueForm.getCustomFieldValues().size() == 0) {
-            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_CUSTOM_FIELD_NOT_EXIST));
-        }
-
-        if (issueForm.getApiType() == IssueApiForm.ApiType.add) {
-            List<Issue> issues = this.issueService.addApiIssue(issueForm);
-            //  踰꾩쟾 �깮�꽦
-            for (Issue issue : issues) {
-                this.issueService.addIssueVersion(issue.getId(), issue.getRegisterId());
-            }
-        } else {
-
-
-            this.issueService.modifyIssue(issueForm, request.getFiles("file"));
-        }
-
-        return this.setSuccessMessage(resJsonData);
-    }
-
-    //  �씠�뒋 議고쉶
-//    @RequestMapping(value = "/api/issueList", method = RequestMethod.GET, 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));
-//
-//        // todo
-//        return this.setSuccessMessage(resJsonData);
-//    }
-}
-
diff --git a/src/main/java/kr/wisestone/owl/web/controller/ApiTokenController.java b/src/main/java/kr/wisestone/owl/web/controller/ApiTokenController.java
index 6e45b8a..625f7c6 100644
--- a/src/main/java/kr/wisestone/owl/web/controller/ApiTokenController.java
+++ b/src/main/java/kr/wisestone/owl/web/controller/ApiTokenController.java
@@ -6,6 +6,7 @@
 import kr.wisestone.owl.util.ConvertUtil;
 import kr.wisestone.owl.vo.ApiTokenVo;
 import kr.wisestone.owl.web.condition.ApiTokenCondition;
+import kr.wisestone.owl.web.controller.BaseController;
 import kr.wisestone.owl.web.form.ApiTokenForm;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Pageable;
@@ -20,13 +21,20 @@
 import java.util.List;
 import java.util.Map;
 
+/**
+ * API �넗�겙 而⑦듃濡ㅻ윭 �겢�옒�뒪
+ */
 @Controller
 public class ApiTokenController extends BaseController {
 
     @Autowired
     private ApiTokenService apiTokenService;
 
-    //  �넗�겙 �깮�꽦
+    /**
+     * �넗�겙 �깮�꽦
+     * @param params �넗�겙 �깮�꽦�뿉 �븘�슂�븳 �뙆�씪誘명꽣
+     * @return �깮�꽦 寃곌낵 Map<String, Object>
+     */
     @RequestMapping(value = "/apiToken/add", produces = MediaType.APPLICATION_JSON_VALUE)
     public
     @ResponseBody
@@ -41,6 +49,12 @@
         return this.setSuccessMessage(resJsonData);
     }
 
+
+    /**
+     * �넗�겙 議고쉶
+     * @param params �넗�겙 議고쉶�뿉 �븘�슂�븳 �뙆�씪誘명꽣 
+     * @return 議고쉶 寃곌낵, Map<String, ApiTokenVo>
+     */
     //  �넗�겙 議고쉶
     @RequestMapping(value = "/apiToken/find", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
     public
@@ -55,6 +69,11 @@
         return this.setSuccessMessage(resJsonData);
     }
 
+    /**
+     * �넗�겙 �궘�젣
+     * @param params �넗�겙 �궘�젣�뿉 �븘�슂�븳 �뙆�씪誘명꽣
+     * @return �궘�젣 寃곌낵, Map<String, Object>
+     */
     //  �넗�겙 �궘�젣
     @RequestMapping(value = "/apiToken/remove", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
     public
diff --git a/src/main/java/kr/wisestone/owl/web/controller/IssueController.java b/src/main/java/kr/wisestone/owl/web/controller/IssueController.java
index dd0da5f..ff92657 100644
--- a/src/main/java/kr/wisestone/owl/web/controller/IssueController.java
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueController.java
@@ -7,6 +7,7 @@
 import kr.wisestone.owl.web.condition.ApiMonitorCondition;
 import kr.wisestone.owl.web.condition.DepartmentCondition;
 import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.form.EmailCommonForm;
 import kr.wisestone.owl.web.form.EmailTemplateForm;
 import kr.wisestone.owl.web.form.IssueForm;
 import org.slf4j.Logger;
@@ -248,6 +249,17 @@
     }
 
 
+    // �씪諛� 硫붿씪 諛쒖넚 (�궗�슜�옄 吏곸젒 �옉�꽦)
+    @RequestMapping(value = "/issue/sendCommonEmail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> sendCommonEmail(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+        this.issueService.sendCommonEmail(EmailCommonForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+        return this.setSuccessMessage(resJsonData);
+    }
+
+
     //  api 湲곕줉 議고쉶
     @RequestMapping(value = "/api/findHistory", produces = MediaType.APPLICATION_JSON_VALUE)
     public
diff --git a/src/main/java/kr/wisestone/owl/web/form/ApiIssueAddForm.java b/src/main/java/kr/wisestone/owl/web/form/ApiIssueAddForm.java
new file mode 100644
index 0000000..9a8d273
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/ApiIssueAddForm.java
@@ -0,0 +1,53 @@
+package kr.wisestone.owl.web.form;
+
+import io.swagger.annotations.ApiParam;
+
+
+/**
+ * API �씠�뒋 異붽��슜 Form class
+ */
+public class ApiIssueAddForm {
+    @ApiParam(value = "�궗�슜�옄 �넗�겙", required = true)
+    private String token;
+    @ApiParam(value = "�씠�뒋 �젣紐�")
+    private String title;
+    @ApiParam(value = "�씠�뒋 ���엯 ID", required = true)
+    private Long issueTypeId;
+    @ApiParam(value = "�궗�슜�옄 �젙�쓽 �븘�뱶", required = true)
+    private String customFields;
+
+    public ApiIssueAddForm() {
+    }
+
+    public String getCustomFields() {
+        return customFields;
+    }
+
+    public void setCustomFields(String customFields) {
+        this.customFields = customFields;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public Long getIssueTypeId() {
+        return issueTypeId;
+    }
+
+    public void setIssueTypeId(Long issueTypeId) {
+        this.issueTypeId = issueTypeId;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/ApiIssueModifyForm.java b/src/main/java/kr/wisestone/owl/web/form/ApiIssueModifyForm.java
new file mode 100644
index 0000000..4a3be36
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/ApiIssueModifyForm.java
@@ -0,0 +1,63 @@
+package kr.wisestone.owl.web.form;
+
+import io.swagger.annotations.ApiParam;
+
+
+/**
+ * API �씠�뒋 �긽�깭 �닔�젙�슜 Form class
+ */
+public class ApiIssueModifyForm {
+    @ApiParam(value = "�궗�슜�옄 �넗�겙", required = true)
+    private String token;
+    @ApiParam(value = "�씠�뒋 ���엯 ID", required = true)
+    private Long issueTypeId;
+    @ApiParam(value = "�씠�뒋 �긽�깭 ID", required = true)
+    private Long issueStatusId;
+    @ApiParam(value = "�뙎湲�", required = true)
+    private String comment;
+    @ApiParam(value = "�궗�슜�옄 �젙�쓽 �븘�뱶", required = true)
+    private String customFields;
+
+    public ApiIssueModifyForm() {
+    }
+
+    public Long getIssueTypeId() {
+        return issueTypeId;
+    }
+
+    public void setIssueTypeId(Long issueTypeId) {
+        this.issueTypeId = issueTypeId;
+    }
+
+    public Long getIssueStatusId() {
+        return issueStatusId;
+    }
+
+    public void setIssueStatusId(Long issueStatusId) {
+        this.issueStatusId = issueStatusId;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    public String getComment() {
+        return comment;
+    }
+
+    public void setComment(String comment) {
+        this.comment = comment;
+    }
+
+    public String getCustomFields() {
+        return customFields;
+    }
+
+    public void setCustomFields(String customFields) {
+        this.customFields = customFields;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/ApiTokenForm.java b/src/main/java/kr/wisestone/owl/web/form/ApiTokenForm.java
index ab0b978..281ddea 100644
--- a/src/main/java/kr/wisestone/owl/web/form/ApiTokenForm.java
+++ b/src/main/java/kr/wisestone/owl/web/form/ApiTokenForm.java
@@ -1,13 +1,11 @@
 package kr.wisestone.owl.web.form;
 
-import com.google.common.collect.Lists;
 import kr.wisestone.owl.domain.User;
-import kr.wisestone.owl.util.ConvertUtil;
-import kr.wisestone.owl.util.MapUtil;
 
-import java.util.List;
-import java.util.Map;
 
+/**
+ * API �넗�겙 Form class
+ */
 public class ApiTokenForm {
     private Long id;
     private User user;
diff --git a/src/main/java/kr/wisestone/owl/web/form/EmailCommonForm.java b/src/main/java/kr/wisestone/owl/web/form/EmailCommonForm.java
new file mode 100644
index 0000000..bc7d803
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/EmailCommonForm.java
@@ -0,0 +1,55 @@
+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 EmailCommonForm {
+    private List<String> sendEmails = Lists.newArrayList();
+    private String title;
+    private String description;
+    private Long issueId;
+
+    public static EmailCommonForm make(Map<String, Object> params) {
+        EmailCommonForm emailCommonForm = ConvertUtil.convertMapToClass(params, EmailCommonForm.class);
+        if (MapUtil.getStrings(params, "sendEmails") != null) {
+            emailCommonForm.setSendEmails(MapUtil.getStrings(params, "sendEmails"));
+        }
+        return emailCommonForm;
+    }
+
+    public List<String> getSendEmails() {
+        return sendEmails;
+    }
+
+    public void setSendEmails(List<String> sendEmails) {
+        this.sendEmails = sendEmails;
+    }
+
+    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 getIssueId() {
+        return issueId;
+    }
+
+    public void setIssueId(Long issueId) {
+        this.issueId = issueId;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/IssueApiForm.java b/src/main/java/kr/wisestone/owl/web/form/IssueApiForm.java
index aaab370..9ab2b02 100644
--- a/src/main/java/kr/wisestone/owl/web/form/IssueApiForm.java
+++ b/src/main/java/kr/wisestone/owl/web/form/IssueApiForm.java
@@ -4,10 +4,13 @@
 import kr.wisestone.owl.util.ConvertUtil;
 import kr.wisestone.owl.util.MapUtil;
 import org.springframework.web.multipart.MultipartFile;
-
+import java.io.Serializable;
 import java.util.*;
 
-public class IssueApiForm {
+/**
+ * API �씠�뒋 異붽� / �닔�젙�슜 form class
+ */
+public class IssueApiForm implements Serializable {
 
     public enum ApiType {
         add,
@@ -37,44 +40,30 @@
     public IssueApiForm() {
     }
 
-    public static IssueApiForm make(Map<String, Object> content, List<MultipartFile> files) {
-        IssueApiForm form = ConvertUtil.convertMapToClass(content, IssueApiForm.class);
-        form.setMultipartFiles(files);
 
-        // api ���엯
-        if (MapUtil.getString(content, "apiType") != null) {
-            try {
-                form.setApiType(ApiType.valueOf(MapUtil.getString(content, "apiType")));
-            } catch (Exception ex) {
-                return null;
-            }
+    /**
+     * �궗�슜�옄 �젙�쓽 �븘�뱶 蹂��솚
+     * @param customFieldJson �궗�슜�옄 �젙�쓽 �븘�뱶 json
+     */
+    public void parseCustomFields(String customFieldJson) {
+
+        Map<String, Object> json = ConvertUtil.convertJsonToMap(customFieldJson);
+        List<Map<String, Object>> customFields = (List) MapUtil.getObject(json, "customFields");
+        for (Map<String, Object> customField : customFields) {
+            IssueCustomFieldValueForm issueCustomFieldValueForm = ConvertUtil.convertMapToClass(customField, IssueCustomFieldValueForm.class);
+            this.addIssueCustomFieldValue(issueCustomFieldValueForm);
+
+            Map<String, Object> customFieldVo = new HashMap<>();
+            customFieldVo.put("id", issueCustomFieldValueForm.getCustomFieldId());
+
+            customField.put("customFieldVo", customFieldVo);
+
+            List<String> useValues = Lists.newArrayList();
+            useValues.add(issueCustomFieldValueForm.getUseValue());
+            customField.put("useValues", useValues);
+
+            this.addCustomFieldValue(customField);
         }
-
-        //  �궗�슜�옄 �븘�뱶 �젙蹂�
-        if (MapUtil.getObject(content, "customFields") != null){
-            List<Map<String, Object>> customFields = (List)MapUtil.getObject(content, "customFields");
-            for (Map<String, Object> customField : customFields) {
-                IssueCustomFieldValueForm issueCustomFieldValueForm = ConvertUtil.convertMapToClass(customField, IssueCustomFieldValueForm.class);
-                form.addIssueCustomFieldValue(issueCustomFieldValueForm);
-
-                Map<String, Object> customFieldVo = new HashMap<>();
-                customFieldVo.put("id", issueCustomFieldValueForm.getCustomFieldId());
-
-                customField.put("customFieldVo", customFieldVo);
-
-                List<String> useValues = Lists.newArrayList();
-                useValues.add(issueCustomFieldValueForm.getUseValue());
-                customField.put("useValues", useValues);
-
-                form.addCustomFieldValue(customField);
-            }
-        }
-
-        // 泥⑤� �뙆�씪
-//        if (MapUtil.getObject(content, "files") != null){
-//            form.setFiles((List)MapUtil.getObject(content, "files"));
-//        }
-        return form;
     }
 
     public String getToken() {
diff --git a/src/main/java/package-info.java b/src/main/java/package-info.java
new file mode 100644
index 0000000..dbb8cd6
--- /dev/null
+++ b/src/main/java/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * OWL-ITS
+ */
+package kr.wisestone.owl;
\ No newline at end of file
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
index e0dc65b..5f166fd 100644
--- a/src/main/resources/log4j2.xml
+++ b/src/main/resources/log4j2.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<Configuration status="warn" name="owl-its" monitorInterval="600">
+<Configuration status="ERROR" 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>
@@ -50,6 +50,12 @@
             <AppenderRef ref="File"/>
         </Logger>
 
+        <!-- Crawling Logger -->
+        <Logger name="kr.wisestone.owl.crawling" level="debug" additivity="false">
+            <AppenderRef ref="Console"/>
+            <AppenderRef ref="File"/>
+        </Logger>
+
         <!-- Root Loggers -->
         <Root level="warn">
             <AppenderRef ref="Console"/>
diff --git a/src/main/resources/mybatis/query-template/issue-template.xml b/src/main/resources/mybatis/query-template/issue-template.xml
index 6dc1b5a..8685b96 100644
--- a/src/main/resources/mybatis/query-template/issue-template.xml
+++ b/src/main/resources/mybatis/query-template/issue-template.xml
@@ -1115,8 +1115,12 @@
                 </when>
             </choose>
             ) customFieldValue ON customFieldValue.issueId = issue.id
-        WHERE issStatus.issue_status_type != 'CLOSE'
-          AND issue.issue_type_id = #{issueTypeId}
+        WHERE issue.issue_type_id = #{issueTypeId}
+        <choose>
+            <when test="issueStatusType != null">
+                AND issStatus.issue_status_type != #{issueStatusType}
+            </when>
+        </choose>
         GROUP BY issue.id
         HAVING concatUseValue LIKE CONCAT('%', #{useValue}, '%')
     </select>
diff --git a/src/main/webapp/assets/styles/main.css b/src/main/webapp/assets/styles/main.css
index 98f700e..bedb35f 100644
--- a/src/main/webapp/assets/styles/main.css
+++ b/src/main/webapp/assets/styles/main.css
@@ -11310,6 +11310,14 @@
     font-size: 0.79rem;
 }
 
+.select3-selection__email__remove {
+    color: #0066ff;
+    margin-left: 8px;
+    margin-top: 7px;
+    cursor: pointer;
+    font-size: 0.79rem;
+}
+
 .select4-selection__choice {
     font-size: 0.66rem;
     letter-spacing: -0.01em;
diff --git a/src/main/webapp/custom_components/js-down/down.provider.js b/src/main/webapp/custom_components/js-down/down.provider.js
new file mode 100644
index 0000000..7eb0bf3
--- /dev/null
+++ b/src/main/webapp/custom_components/js-down/down.provider.js
@@ -0,0 +1,232 @@
+'use strict';
+
+define(['app', 'angular'],
+    function (app, angular) {
+        app.provider("$downProvider", function () {
+            return {
+                $get : function ($log) {
+                    return {
+                        config : function () {
+                            var tableConfig = {
+                                hName : "",	//	�뿤�뜑 �씠由�
+                                hWidth : "",	//	移쇰읆 湲몄씠
+                                hChecked : false,	//	泥댄겕 諛뺤뒪 �꽑�깮 �뿬遺�
+                                hAlign : "text-center",	//	�뿤�뜑 �젙�젹 湲곗�
+                                hSort : true,	//	�젙�젹 媛��뒫 �뿬遺�
+                                dName : "",	//	�뜲�씠�꽣 �씠由�
+                                dAlign : "text-left",	//	�뜲�씠�꽣 �젙�젹 湲곗�
+                                dRenderer : "",	//	�젋�뜑�윭 �뿬遺�
+                                dVisible : "",  //      bootstrap 諛섏쓳�삎 而щ읆 �몴�떆 �뿬遺�
+                                dType : "none",        // �깭洹� ���엯
+                                dDateFormat : "",   //  �궇吏� �삎�떇
+                                rowSpan : 0,    //  rowspan �쓣 吏��썝�븳�떎.
+                                colSpan : 0,    //  colspan �쓣 吏��썝�븳�떎.
+                                subHead : false,    //  留뚯빟 rowspan, colspan �쓣 �궗�슜�븯寃� �릺硫� true 濡� �뀑�똿.
+                                columnHint : "",    //  而щ읆 �젙蹂대�� 異붿텧�븯湲� �쐞�븳 �엺�듃 �젙蹂대�� 以��떎 - downColumnGenerator �쓽 �궗�슜�옄 �젙�쓽 �븘�뱶 遺�遺꾩뿉�꽌 �궗�슜
+                                columnTooltip : "", // hover �떆 �댋�똻 蹂댁뿬二쇨린
+                                setHName : function (hName) {
+                                    this.hName = hName;
+                                    return this;
+                                },
+                                setHWidth : function (hWidth) {
+                                    this.hWidth = hWidth;
+                                    return this;
+                                },
+                                setHChecked : function (hChecked) {
+                                    this.hChecked = hChecked;
+                                    return this;
+                                },
+                                setHAlign : function (hAlign) {
+                                    this.hAlign = hAlign;
+                                    return this;
+                                },
+                                setHSort : function (hSort) {
+                                    this.hSort = hSort;
+                                    return this;
+                                },
+                                setDName : function (dName) {
+                                    this.dName = dName;
+                                    return this;
+                                },
+                                setDAlign : function (dAlign) {
+                                    this.dAlign = dAlign;
+                                    return this;
+                                },
+                                setDRenderer : function (dRenderer) {
+                                    this.dRenderer = dRenderer;
+                                    return this;
+                                },
+                                setDVisible : function (dVisible) {
+                                    this.dVisible = dVisible;
+                                    return this;
+                                },
+                                setDType : function (dType) {
+                                    this.dType = dType;
+                                    return this;
+                                },
+                                setDDateFormat : function (dDateFormat) {
+                                    this.dDateFormat = dDateFormat;
+                                    return this;
+                                },
+                                setRowSpan : function (dRowSpan) {
+                                    this.rowSpan = dRowSpan;
+                                    return this;
+                                },
+                                setColSpan : function (dColSpan) {
+                                    this.colSpan = dColSpan;
+                                    return this;
+                                },
+                                setSubHead : function (dSubHead) {
+                                    this.subHead = dSubHead;
+                                    return this;
+                                },
+                                setColumnHint : function (dColumnHint) {
+                                    this.columnHint = dColumnHint;
+                                    return this;
+                                },
+                                setColumnTooltip : function (hTooltip) {
+                                    this.columnTooltip = hTooltip;
+                                    return this;
+                                }
+                            };
+
+                            return tableConfig;
+                        },
+                        toggleChecked : function (checkStatus, datas) {
+                            //  �쟾泥� �꽑�깮 泥댄겕 諛뺤뒪瑜� �겢由��뻽�쓣 寃쎌슦
+                            angular.forEach(datas, function (data) {
+                                data.checked = checkStatus;
+                            });
+                        },
+                        radioChecked : function (target, datas) {
+                            //  �빐�떦 row 媛� �씪�뵒�삤 踰꾪듉�씪 寃쎌슦
+                            angular.forEach(datas, function (data) {
+                                if (target.id == data.id) {
+                                    data.checked = true;
+                                }
+                                else {
+                                    data.checked = false;
+                                }
+                            });
+                        },
+                        rowChecked : function (tableConfig, target, datas) {
+                            //  媛� row �쓽 泥댄겕諛뺤뒪/�씪�뵒�삤 踰꾪듉�쓣 �겢由��뻽�쓣 寃쎌슦
+                            if (tableConfig[0].dType == "checkbox") {
+                                target.checked = !target.checked;
+
+                                for (var data in datas) {
+                                    if (!data.checked) {
+                                        this.hChecked = false;
+                                        break;
+                                    }
+                                }
+                            }
+                            else if (tableConfig[0].dType == "radio") {
+                                this.radioChecked(target, datas);
+                            }
+                        },
+                        orderByColumn : "",  // table order By column name
+                        reverse : true,
+                        setOrderByColumn : function (column) {
+                            if (column == "") {
+                                return;
+                            }
+
+                            if (this.orderByColumn == column) {
+                                this.reverse = !this.reverse;
+                            }
+                            else {
+                                this.reverse = true;
+                            }
+
+                            this.orderByColumn = column;
+                            return this;
+                        },
+                        getDateFormat : function (formatType, date) {
+                            if (formatType == "" || formatType == null) {
+                                formatType = "01";
+                            }
+                            Date.prototype.format = function (f) {
+                                if (!this.valueOf()) {
+                                    return " ";
+                                }
+                                var weekName = ["�씪�슂�씪", "�썡�슂�씪", "�솕�슂�씪", "�닔�슂�씪", "紐⑹슂�씪", "湲덉슂�씪", "�넗�슂�씪"];
+                                var d = this;
+
+                                return f.replace(/(yyyy|yy|MM|dd|E|hh|mm|ss|a\/p)/gi, function ($1) {
+                                    switch ($1) {
+                                        case "yyyy":
+                                            return d.getFullYear();
+                                        case "yy":
+                                            return (d.getFullYear() % 1000).zf(2);
+                                        case "MM":
+                                            return (d.getMonth() + 1).zf(2);
+                                        case "dd":
+                                            return d.getDate().zf(2);
+                                        case "E":
+                                            return weekName[d.getDay()];
+                                        case "HH":
+                                            return d.getHours().zf(2);
+                                        case "hh":
+                                            var h = d.getHours();
+                                            return ((h = d.getHours() % 12) ? h : 12).zf(2);
+                                        case "mm":
+                                            return d.getMinutes().zf(2);
+                                        case "ss":
+                                            return d.getSeconds().zf(2);
+                                        case "a/p":
+                                            return d.getHours() < 12 ? "�삤�쟾" : "�삤�썑";
+                                        default:
+                                            return $1;
+                                    }
+                                });
+                            };
+
+                            String.prototype.string = function (len) {
+                                var s = '', i = 0;
+                                while (i++ < len) {
+                                    s += this;
+                                }
+                                return s;
+                            };
+                            String.prototype.zf = function (len) {
+                                return "0".string(len - this.length) + this;
+                            };
+                            Number.prototype.zf = function (len) {
+                                return this.toString().zf(len);
+                            };
+
+                            var dateFormat = "";
+                            var dynamicTime = false;
+                            var today = new Date().format("yyyy-MM-dd");
+                            var compareDate = new Date(date).format("yyyy-MM-dd");
+
+                            if (today == compareDate) {
+                                dynamicTime = true;
+                            }
+
+                            switch (formatType) {
+                                case "01":  //  �궇吏�
+                                    dateFormat = "yyyy-MM-dd";
+                                    break;
+                                case "02":  //  �궇吏� + �떆媛�
+                                    dateFormat = "yyyy-MM-dd HH:mm";
+                                    break;
+                                case "03":  //  �쑀�룞�쟻 �몴�떆
+                                    if (dynamicTime) {
+                                        dateFormat = "HH:mm";
+                                    }
+                                    else {
+                                        dateFormat = "yyyy-MM-dd HH:mm";
+                                    }
+
+                                    break;
+                            }
+
+                            return dateFormat;
+                        }
+                    }
+                }
+            }
+        });
+    });
diff --git a/src/main/webapp/custom_components/js-down/downColumnGenerator.directive.js b/src/main/webapp/custom_components/js-down/downColumnGenerator.directive.js
new file mode 100644
index 0000000..2d070fb
--- /dev/null
+++ b/src/main/webapp/custom_components/js-down/downColumnGenerator.directive.js
@@ -0,0 +1,141 @@
+'use strict';
+
+define(['app', 'angular'],
+    function (app, angular) {
+
+        app.directive('downColumnGenerator', ['$compile', '$log', '$rootScope', '$downProvider', '$filter',
+            function ($compile, $log, $rootScope, $downProvider, $filter) {
+                return {
+                    restrict : "A",
+                    compile : function (tElement, tAttrs) {
+                        return function (scope, element, attrs) {
+                            scope.data = scope[attrs["downColumnGenerator"]];
+
+                            var myData = scope.data;
+                            var makeTag = "";
+
+                            scope.tableConfigs.forEach(function (tableConfig, index) {
+
+                                if (tableConfig.colSpan > 0) {
+                                    return;
+                                }
+
+                                // �븯�쐞 �떒怨� �몴�떆 異붽�
+                                var myToken = "";
+                                if ( scope.data.depth > 0)  {
+                                    for(var i=0; i<scope.data.depth; i++) {
+                                        if (i == scope.data.depth - 1) {
+                                            myToken += treeStartToken;
+                                        } else {
+                                            myToken += "&emsp;";
+                                        }
+                                    }
+                                    myToken += "&nbsp;";
+                                }
+
+                                makeTag = '<td class="' + tableConfig.dAlign + ' ' + tableConfig.dVisible + '">';
+
+                                if (tableConfig.dType === "checkbox") {
+                                    //  泥댄겕 諛뺤뒪�씪�븣
+                                    /*if (scope.data.defaultYn) {
+                                        makeTag += '<input type="checkbox" ng-checked="data.checked == true ? true : false" disabled ng-click="$root.$downProvider.rowChecked(tableConfigs, data, fn.getResponseData())">';
+                                    }
+                                    else {*/
+                                    makeTag += '<input type="checkbox" ng-checked="data.checked == true ? true : false" ng-click="$root.$downProvider.rowChecked(tableConfigs, data, fn.getResponseData())">';
+                                    //}
+
+                                    tableConfig.hChecked = false;
+                                }
+                                else if (tableConfig.dType === "radio") {
+                                    //  �씪�뵒�삤 踰꾪듉�씪�븣
+                                    makeTag += '<input type="radio" ng-checked="data.checked == true ? true : false" ng-click="$root.$downProvider.rowChecked(tableConfigs, data, fn.getResponseData())">';
+                                }
+                                else if (tableConfig.dType === "renderer") {
+                                    //  �옖�뜑�윭 �씪�븣
+                                    switch (tableConfig.dRenderer) {
+
+                                        // �븯�쐞 �씠�뒋 �씠�룞(�젣紐�)
+                                        case "ISSUE_DOWN_MOVE" :
+                                            makeTag += "<span class=\"titlename cursor\" ng-click=\"event.changeDetailView(data)\">" + scope.data.title + "</span></a>";
+                                            break;
+
+                                        // �븯�쐞 �씠�뒋 ���엯
+                                        case "ISSUE_DOWN_STATUS_TYPE" :
+                                            makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.issueStatusVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.issueStatusVo.color + "\", \"color\": \"#FFFFFF\" }'>" + scope.data.issueStatusVo.name + "</span>";
+                                            break;
+
+                                        // �븯�쐞 �씠�뒋 �궘�젣
+                                        case "ISSUE_DOWN_DELETE":
+                                            if (scope.data.modifyPermissionCheck) {
+                                                makeTag += '<img class="cursor" src="/assets/images/delete-icon.png" ng-click="event.removeDownIssue(data.id)">';
+                                            }
+                                            break;
+
+                                        // �븯�쐞 �씠�뒋 �슦�꽑 �닚�쐞
+                                        case "DOWN_COMMON_PRIORITY" :
+                                            makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.priorityVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.priorityVo.color + "\", \"color\": \"#FFFFFF\" }' translate='" + scope.data.priorityVo.name + "'></span>";
+                                            break;
+
+                                        // �븯�쐞 �씠�뒋 以묒슂�룄
+                                        case "DOWN_COMMON_SEVERITY" :
+                                            makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.severityVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.severityVo.color + "\", \"color\": \"#FFFFFF\" }' translate='" + scope.data.severityVo.name + "'></span>";
+                                            break;
+
+                                        // �븯�쐞 �씠�뒋 �떞�떦遺��꽌
+                                        case "DOWN_ISSUE_DEPARTMENT" :
+                                            makeTag += "<ul class='ul-not-comma'>";
+                                            makeTag += "<div style='color: #000000'>";
+                                            angular.forEach(scope.data.departmentVos, function (departments) {
+                                                makeTag += "<li>" + departments.departmentName + "</li>";
+                                            });
+                                            makeTag += "</div>";
+                                            makeTag += "</ul>";
+                                            break;
+
+                                        // �븯�쐞 �씠�뒋 �벑濡앹옄
+                                        case "DOWN_REGISTER":
+                                            scope.data.registerVos = [scope.data.registerVo];
+                                            makeTag += '<div owl-profile-over class="" table-user-image="data" target="registerVos"></div>';
+                                            break;
+
+                                        // �븯�쐞 �씠�뒋 湲곌컙
+                                        case "DOWN_ISSUE_DUE_DATE" :
+                                            if (!$rootScope.isDefined(scope.data.startDate) && !$rootScope.isDefined(scope.data.completeDate)) {
+                                                makeTag += "<span translate='common.noDate'>湲곌컙 �뾾�쓬</span>";
+                                            }
+                                            else {
+                                                makeTag += "<span>" + scope.data.startDate + " ~ " + scope.data.completeDate + "</span>";
+                                            }
+                                            break;
+
+                                        // �븯�쐞 �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶
+                                        case "DOWN_ISSUE_CUSTOM_FIELD_VALUE_VIEW" :
+                                            var values = [];
+
+                                            for (var count in scope.data.issueCustomFieldValueVos) {
+                                                var issueCustomFieldValueVo = scope.data.issueCustomFieldValueVos[count];
+                                                //  �뀒�씠釉� �꽕�젙�뿉�꽌 dName 遺�遺꾩뿉 �궗�슜�옄 �젙�쓽 �븘�뱶 id 瑜� �꽔怨� �빐�떦 媛믪쓣 異붿텧�븳�떎.
+                                                if (tableConfig.columnHint.id == issueCustomFieldValueVo.customFieldVo.id) {
+                                                    values.push(issueCustomFieldValueVo.useValue);
+                                                }
+                                            }
+                                            angular.forEach(values, function (value) {
+                                                makeTag += '<span class="table-word-break-all">' + value + '<span><br>';
+                                            });
+
+                                            break;
+                                    }
+                                }
+
+                                makeTag += '</td>';
+
+                                var linkFn = $compile(makeTag);
+                                var content = linkFn(scope);
+
+                                element.append(content);
+                            });
+                        }
+                    }
+                }
+        }]);
+    });
diff --git a/src/main/webapp/custom_components/js-down/js-down.directive.js b/src/main/webapp/custom_components/js-down/js-down.directive.js
new file mode 100644
index 0000000..f456e55
--- /dev/null
+++ b/src/main/webapp/custom_components/js-down/js-down.directive.js
@@ -0,0 +1,36 @@
+'use strict';
+
+define(['app'],
+    function (app) {
+        app.directive('jsDown', ['$log',
+            function ($log) {
+                return {
+                    restrict : 'E',
+                    scope : {
+                        event : '=',
+                        data : '=',
+                        tableConfigs : '=',
+                        hideHeader : '=', // �뿤�뜑 遺�遺� �닲源� �뿬遺�
+                        useSort : '=', // �젙�졊 湲곕뒫 �궗�슜 �뿬遺�
+                        detailView : "="    //  �씠�뒋 紐⑸줉 �긽�꽭�삎 蹂�寃쎌쓣 �쐞�빐 �궗�슜. �떎瑜� �솕硫댁� �궗�슜�븯吏� �븡�쓬.
+                    },
+                    replace : true,
+                    templateUrl : '/custom_components/js-down/js-down.html',
+                    controller : function ($scope, $element, $attrs) {
+                        $scope.fn = {
+                            getResponseData : getResponseData
+                        };
+
+                        //  �뀒�씠釉� �젙蹂� 媛��졇�삤湲�
+                        function getResponseData() {
+                            return $scope.data;
+                        }
+                    },
+                    link : function (scope, element, attrs) {
+
+                    }
+                };
+            }])
+    });
+
+
diff --git a/src/main/webapp/custom_components/js-down/js-down.html b/src/main/webapp/custom_components/js-down/js-down.html
new file mode 100644
index 0000000..4dbe056
--- /dev/null
+++ b/src/main/webapp/custom_components/js-down/js-down.html
@@ -0,0 +1,47 @@
+<table class="table table-striped table-layout-fixed" ng-class="{ 'width768' : !detailView }" bindonce>
+    <!-- �뀒�씠釉� 癒몃━ -->
+    <thead>
+    <tr ng-if="hideHeader != true">
+        <th bindonce ng-repeat="tableConfig in tableConfigs"
+            bo-class="[tableConfig.hAlign, tableConfig.hWidth, tableConfig.dVisible]"
+            ng-click="$root.$downProvider.setOrderByColumn(tableConfig.dName)"
+            bo-style="{ 'cursor' : tableConfig.dName != '' ? 'pointer' : '' }"
+            rowspan="{{tableConfig.rowSpan}}"
+            colspan="{{tableConfig.colSpan}}"
+            ng-if="!tableConfig.subHead">
+            <!-- 泥댄겕 諛뺤뒪�씪 寃쎌슦 -->
+            <div bo-switch="tableConfig.dType">
+                <div bo-switch-when="checkbox">
+                    <input type="checkbox" tabindex="-1" ng-model="tableConfig.hChecked" ng-click="$root.$downProvider.toggleChecked(tableConfig.hChecked, fn.getResponseData())">
+                </div>
+                <div bo-switch-default>
+                    <span ng-if="tableConfig.columnTooltip != ''" function-tool-tip data-placement="top" data-toggle="tooltip" data-original-title="{{tableConfig.columnTooltip}}"
+                          translate="{{tableConfig.hName}}"></span>
+                    <span ng-if="tableConfig.columnTooltip == ''" translate="{{tableConfig.hName}}"></span>
+                    <span ng-if="($root.$downProvider.orderByColumn == tableConfig.dName) && (tableConfig.dName != '')"><i class="fa fa-arrow-circle-down" ng-show="!$root.$downProvider.reverse"></i>
+                        <i class="fa fa-arrow-circle-up" ng-show="$root.$downProvider.reverse"></i>
+                    </span>
+                </div>
+            </div>
+        </th>
+    </tr>
+    </thead>
+
+    <tbody>
+    <tr ng-if="useSort != false" ng-repeat="row in fn.getResponseData() | orderBy:$root.$downProvider.orderByColumn:$root.$downProvider.reverse"
+        bo-class="[row.checked == true ? 'table-active' : '', tableConfig.hWidth]"
+        down-column-generator="row">
+    </tr>
+
+    <tr ng-if="useSort == false" ng-repeat="row in fn.getResponseData()"
+        bo-class="[row.checked == true ? 'table-active' : '', tableConfig.hWidth]"
+        down-column-generator="row">
+    </tr>
+
+    <tr ng-if="fn.getResponseData().length == 0">
+        <td colspan="{{tableConfigs.length}}">
+            <span translate="common.noData">�뜲�씠�꽣媛� �뾾�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    </tbody>
+</table>
diff --git a/src/main/webapp/custom_components/js-rel/js-rel.directive.js b/src/main/webapp/custom_components/js-rel/js-rel.directive.js
new file mode 100644
index 0000000..e3f89eb
--- /dev/null
+++ b/src/main/webapp/custom_components/js-rel/js-rel.directive.js
@@ -0,0 +1,36 @@
+'use strict';
+
+define(['app'],
+    function (app) {
+        app.directive('jsRel', ['$log',
+            function ($log) {
+                return {
+                    restrict : 'E',
+                    scope : {
+                        event : '=',
+                        data : '=',
+                        tableConfigs : '=',
+                        hideHeader : '=', // �뿤�뜑 遺�遺� �닲源� �뿬遺�
+                        useSort : '=', // �젙�졊 湲곕뒫 �궗�슜 �뿬遺�
+                        detailView : "="    //  �씠�뒋 紐⑸줉 �긽�꽭�삎 蹂�寃쎌쓣 �쐞�빐 �궗�슜. �떎瑜� �솕硫댁� �궗�슜�븯吏� �븡�쓬.
+                    },
+                    replace : true,
+                    templateUrl : '/custom_components/js-rel/js-rel.html',
+                    controller : function ($scope, $element, $attrs) {
+                        $scope.fn = {
+                            getResponseData : getResponseData
+                        };
+
+                        //  �뀒�씠釉� �젙蹂� 媛��졇�삤湲�
+                        function getResponseData() {
+                            return $scope.data;
+                        }
+                    },
+                    link : function (scope, element, attrs) {
+
+                    }
+                };
+            }])
+    });
+
+
diff --git a/src/main/webapp/custom_components/js-rel/js-rel.html b/src/main/webapp/custom_components/js-rel/js-rel.html
new file mode 100644
index 0000000..090a59e
--- /dev/null
+++ b/src/main/webapp/custom_components/js-rel/js-rel.html
@@ -0,0 +1,47 @@
+<table class="table table-striped table-layout-fixed" ng-class="{ 'width768' : !detailView }" bindonce>
+    <!-- �뀒�씠釉� 癒몃━ -->
+    <thead>
+    <tr ng-if="hideHeader != true">
+        <th bindonce ng-repeat="tableConfig in tableConfigs"
+            bo-class="[tableConfig.hAlign, tableConfig.hWidth, tableConfig.dVisible]"
+            ng-click="$root.$relProvider.setOrderByColumn(tableConfig.dName)"
+            bo-style="{ 'cursor' : tableConfig.dName != '' ? 'pointer' : '' }"
+            rowspan="{{tableConfig.rowSpan}}"
+            colspan="{{tableConfig.colSpan}}"
+            ng-if="!tableConfig.subHead">
+            <!-- 泥댄겕 諛뺤뒪�씪 寃쎌슦 -->
+            <div bo-switch="tableConfig.dType">
+                <div bo-switch-when="checkbox">
+                    <input type="checkbox" tabindex="-1" ng-model="tableConfig.hChecked" ng-click="$root.$relProvider.toggleChecked(tableConfig.hChecked, fn.getResponseData())">
+                </div>
+                <div bo-switch-default>
+                    <span ng-if="tableConfig.columnTooltip != ''" function-tool-tip data-placement="top" data-toggle="tooltip" data-original-title="{{tableConfig.columnTooltip}}"
+                          translate="{{tableConfig.hName}}"></span>
+                    <span ng-if="tableConfig.columnTooltip == ''" translate="{{tableConfig.hName}}"></span>
+                    <span ng-if="($root.$relProvider.orderByColumn == tableConfig.dName) && (tableConfig.dName != '')"><i class="fa fa-arrow-circle-down" ng-show="!$root.$relProvider.reverse"></i>
+                        <i class="fa fa-arrow-circle-up" ng-show="$root.$relProvider.reverse"></i>
+                    </span>
+                </div>
+            </div>
+        </th>
+    </tr>
+    </thead>
+
+    <tbody>
+    <tr ng-if="useSort != false" ng-repeat="row in fn.getResponseData() | orderBy:$root.$relProvider.orderByColumn:$root.$relProvider.reverse"
+        bo-class="[row.checked == true ? 'table-active' : '', tableConfig.hWidth]"
+        rel-column-generator="row">
+    </tr>
+
+    <tr ng-if="useSort == false" ng-repeat="row in fn.getResponseData()"
+        bo-class="[row.checked == true ? 'table-active' : '', tableConfig.hWidth]"
+        rel-column-generator="row">
+    </tr>
+
+    <tr ng-if="fn.getResponseData().length == 0">
+        <td colspan="{{tableConfigs.length}}">
+            <span translate="common.noData">�뜲�씠�꽣媛� �뾾�뒿�땲�떎.</span>
+        </td>
+    </tr>
+    </tbody>
+</table>
diff --git a/src/main/webapp/custom_components/js-rel/rel.provider.js b/src/main/webapp/custom_components/js-rel/rel.provider.js
new file mode 100644
index 0000000..8e7125e
--- /dev/null
+++ b/src/main/webapp/custom_components/js-rel/rel.provider.js
@@ -0,0 +1,239 @@
+'use strict';
+
+define(['app', 'angular'],
+    function (app, angular) {
+        app.provider("$relProvider", function () {
+            return {
+                $get : function ($log) {
+                    return {
+                        config : function () {
+                            var tableConfig = {
+                                hName : "",	//	�뿤�뜑 �씠由�
+                                hWidth : "",	//	移쇰읆 湲몄씠
+                                hChecked : false,	//	泥댄겕 諛뺤뒪 �꽑�깮 �뿬遺�
+                                hAlign : "text-center",	//	�뿤�뜑 �젙�젹 湲곗�
+                                hSort : true,	//	�젙�젹 媛��뒫 �뿬遺�
+                                dName : "",	//	�뜲�씠�꽣 �씠由�
+                                dAlign : "text-left",	//	�뜲�씠�꽣 �젙�젹 湲곗�
+                                dRenderer : "",	//	�젋�뜑�윭 �뿬遺�
+                                dVisible : "",  //      bootstrap 諛섏쓳�삎 而щ읆 �몴�떆 �뿬遺�
+                                dType : "none",        // �깭洹� ���엯
+                                dDateFormat : "",   //  �궇吏� �삎�떇
+                                rowSpan : 0,    //  rowspan �쓣 吏��썝�븳�떎.
+                                colSpan : 0,    //  colspan �쓣 吏��썝�븳�떎.
+                                subHead : false,    //  留뚯빟 rowspan, colspan �쓣 �궗�슜�븯寃� �릺硫� true 濡� �뀑�똿.
+                                columnHint : "",    //  而щ읆 �젙蹂대�� 異붿텧�븯湲� �쐞�븳 �엺�듃 �젙蹂대�� 以��떎 - relColumnGenerator �쓽 �궗�슜�옄 �젙�쓽 �븘�뱶 遺�遺꾩뿉�꽌 �궗�슜
+                                columnTooltip : "", // hover �떆 �댋�똻 蹂댁뿬二쇨린
+                                setHName : function (hName) {
+                                    this.hName = hName;
+                                    return this;
+                                },
+                                setHWidth : function (hWidth) {
+                                    this.hWidth = hWidth;
+                                    return this;
+                                },
+                                setHChecked : function (hChecked) {
+                                    this.hChecked = hChecked;
+                                    return this;
+                                },
+                                setHAlign : function (hAlign) {
+                                    this.hAlign = hAlign;
+                                    return this;
+                                },
+                                setHSort : function (hSort) {
+                                    this.hSort = hSort;
+                                    return this;
+                                },
+                                setDName : function (dName) {
+                                    this.dName = dName;
+                                    return this;
+                                },
+                                setDAlign : function (dAlign) {
+                                    this.dAlign = dAlign;
+                                    return this;
+                                },
+                                setDRenderer : function (dRenderer) {
+                                    this.dRenderer = dRenderer;
+                                    return this;
+                                },
+                                setDVisible : function (dVisible) {
+                                    this.dVisible = dVisible;
+                                    return this;
+                                },
+                                setDType : function (dType) {
+                                    this.dType = dType;
+                                    return this;
+                                },
+                                setDDateFormat : function (dDateFormat) {
+                                    this.dDateFormat = dDateFormat;
+                                    return this;
+                                },
+                                setRowSpan : function (dRowSpan) {
+                                    this.rowSpan = dRowSpan;
+                                    return this;
+                                },
+                                setColSpan : function (dColSpan) {
+                                    this.colSpan = dColSpan;
+                                    return this;
+                                },
+                                setSubHead : function (dSubHead) {
+                                    this.subHead = dSubHead;
+                                    return this;
+                                },
+                                setColumnHint : function (dColumnHint) {
+                                    this.columnHint = dColumnHint;
+                                    return this;
+                                },
+                                setColumnTooltip : function (hTooltip) {
+                                    this.columnTooltip = hTooltip;
+                                    return this;
+                                }
+                            };
+
+                            return tableConfig;
+                        },
+                        toggleChecked : function (checkStatus, datas) {
+                            //  �쟾泥� �꽑�깮 泥댄겕 諛뺤뒪瑜� �겢由��뻽�쓣 寃쎌슦
+                            angular.forEach(datas, function (data) {
+                                /*if (angular.isDefined(data.defaultYn)) {
+                                    if (!data.defaultYn) {
+                                        data.checked = checkStatus;
+                                    }
+                                }
+                                else {*/
+                                    data.checked = checkStatus;
+                                //}
+                            });
+                        },
+                        radioChecked : function (target, datas) {
+                            //  �빐�떦 row 媛� �씪�뵒�삤 踰꾪듉�씪 寃쎌슦
+                            angular.forEach(datas, function (data) {
+                                if (target.id == data.id) {
+                                    data.checked = true;
+                                }
+                                else {
+                                    data.checked = false;
+                                }
+                            });
+                        },
+                        rowChecked : function (tableConfig, target, datas) {
+                            //  媛� row �쓽 泥댄겕諛뺤뒪/�씪�뵒�삤 踰꾪듉�쓣 �겢由��뻽�쓣 寃쎌슦
+                            if (tableConfig[0].dType == "checkbox") {
+                                target.checked = !target.checked;
+
+                                for (var data in datas) {
+                                    if (!data.checked) {
+                                        this.hChecked = false;
+                                        break;
+                                    }
+                                }
+                            }
+                            else if (tableConfig[0].dType == "radio") {
+                                this.radioChecked(target, datas);
+                            }
+                        },
+                        orderByColumn : "",  // table order By column name
+                        reverse : true,
+                        setOrderByColumn : function (column) {
+                            if (column == "") {
+                                return;
+                            }
+
+                            if (this.orderByColumn == column) {
+                                this.reverse = !this.reverse;
+                            }
+                            else {
+                                this.reverse = true;
+                            }
+
+                            this.orderByColumn = column;
+                            return this;
+                        },
+                        getDateFormat : function (formatType, date) {
+                            if (formatType == "" || formatType == null) {
+                                formatType = "01";
+                            }
+                            Date.prototype.format = function (f) {
+                                if (!this.valueOf()) {
+                                    return " ";
+                                }
+                                var weekName = ["�씪�슂�씪", "�썡�슂�씪", "�솕�슂�씪", "�닔�슂�씪", "紐⑹슂�씪", "湲덉슂�씪", "�넗�슂�씪"];
+                                var d = this;
+
+                                return f.replace(/(yyyy|yy|MM|dd|E|hh|mm|ss|a\/p)/gi, function ($1) {
+                                    switch ($1) {
+                                        case "yyyy":
+                                            return d.getFullYear();
+                                        case "yy":
+                                            return (d.getFullYear() % 1000).zf(2);
+                                        case "MM":
+                                            return (d.getMonth() + 1).zf(2);
+                                        case "dd":
+                                            return d.getDate().zf(2);
+                                        case "E":
+                                            return weekName[d.getDay()];
+                                        case "HH":
+                                            return d.getHours().zf(2);
+                                        case "hh":
+                                            var h = d.getHours();
+                                            return ((h = d.getHours() % 12) ? h : 12).zf(2);
+                                        case "mm":
+                                            return d.getMinutes().zf(2);
+                                        case "ss":
+                                            return d.getSeconds().zf(2);
+                                        case "a/p":
+                                            return d.getHours() < 12 ? "�삤�쟾" : "�삤�썑";
+                                        default:
+                                            return $1;
+                                    }
+                                });
+                            };
+
+                            String.prototype.string = function (len) {
+                                var s = '', i = 0;
+                                while (i++ < len) {
+                                    s += this;
+                                }
+                                return s;
+                            };
+                            String.prototype.zf = function (len) {
+                                return "0".string(len - this.length) + this;
+                            };
+                            Number.prototype.zf = function (len) {
+                                return this.toString().zf(len);
+                            };
+
+                            var dateFormat = "";
+                            var dynamicTime = false;
+                            var today = new Date().format("yyyy-MM-dd");
+                            var compareDate = new Date(date).format("yyyy-MM-dd");
+
+                            if (today == compareDate) {
+                                dynamicTime = true;
+                            }
+
+                            switch (formatType) {
+                                case "01":  //  �궇吏�
+                                    dateFormat = "yyyy-MM-dd";
+                                    break;
+                                case "02":  //  �궇吏� + �떆媛�
+                                    dateFormat = "yyyy-MM-dd HH:mm";
+                                    break;
+                                case "03":  //  �쑀�룞�쟻 �몴�떆
+                                    if (dynamicTime) {
+                                        dateFormat = "HH:mm";
+                                    }
+                                    else {
+                                        dateFormat = "yyyy-MM-dd HH:mm";
+                                    }
+
+                                    break;
+                            }
+
+                            return dateFormat;
+                        }
+                    }
+                }
+            }
+        });
+    });
diff --git a/src/main/webapp/custom_components/js-rel/relColumnGenerator.directive.js b/src/main/webapp/custom_components/js-rel/relColumnGenerator.directive.js
new file mode 100644
index 0000000..c7e184f
--- /dev/null
+++ b/src/main/webapp/custom_components/js-rel/relColumnGenerator.directive.js
@@ -0,0 +1,142 @@
+'use strict';
+
+define(['app', 'angular'],
+    function (app, angular) {
+
+        app.directive('relColumnGenerator', ['$compile', '$log', '$rootScope', '$relProvider', '$filter',
+            function ($compile, $log, $rootScope, $relProvider, $filter) {
+                return {
+                    restrict : "A",
+                    compile : function (tElement, tAttrs) {
+                        return function (scope, element, attrs) {
+                            scope.data = scope[attrs["relColumnGenerator"]];
+
+                            var myData = scope.data;
+                            var makeTag = "";
+
+                            scope.tableConfigs.forEach(function (tableConfig, index) {
+
+                                if (tableConfig.colSpan > 0) {
+                                    return;
+                                }
+
+                                // �븯�쐞 �떒怨� �몴�떆 異붽�
+                                var myToken = "";
+                                if ( scope.data.depth > 0)  {
+                                    for(var i=0; i<scope.data.depth; i++) {
+                                        if (i == scope.data.depth - 1) {
+                                            myToken += treeStartToken;
+                                        } else {
+                                            myToken += "&emsp;";
+                                        }
+                                    }
+                                    myToken += "&nbsp;";
+                                }
+
+                                makeTag = '<td class="' + tableConfig.dAlign + ' ' + tableConfig.dVisible + '">';
+
+                                if (tableConfig.dType === "checkbox") {
+                                    //  泥댄겕 諛뺤뒪�씪�븣
+                                    /*if (scope.data.defaultYn) {
+                                        makeTag += '<input type="checkbox" ng-checked="data.checked == true ? true : false" disabled ng-click="$root.$relProvider.rowChecked(tableConfigs, data, fn.getResponseData())">';
+                                    }
+                                    else {*/
+                                    makeTag += '<input type="checkbox" ng-checked="data.checked == true ? true : false" ng-click="$root.$relProvider.rowChecked(tableConfigs, data, fn.getResponseData())">';
+                                    //}
+
+                                    tableConfig.hChecked = false;
+                                }
+                                else if (tableConfig.dType === "radio") {
+                                    //  �씪�뵒�삤 踰꾪듉�씪�븣
+                                    makeTag += '<input type="radio" ng-checked="data.checked == true ? true : false" ng-click="$root.$relProvider.rowChecked(tableConfigs, data, fn.getResponseData())">';
+                                }
+                                else if (tableConfig.dType === "renderer") {
+                                    //  �옖�뜑�윭 �씪�븣
+                                    switch (tableConfig.dRenderer) {
+
+                                        // �뿰愿� �씠�뒋 �씠�룞(�젣紐�)
+                                        case "ISSUE_RELATION_MOVE" :
+                                            makeTag += "<span class=\"titlename cursor text-center\" ng-click=\"event.changeDetailView(data.issueRelation)\">" + scope.data.title + "</span></a>";
+                                            break;
+
+                                        // �뿰愿��씠�뒋 援щ텇
+                                        case "ISSUE_RELATION_TYPE":
+                                            makeTag += "<span>" + scope.data.relationIssueTypeName + "</span>";
+                                            break;
+
+                                        // �뿰愿��씠�뒋 �궘�젣
+                                        case "ISSUE_RELATION_DELETE":
+                                            if (scope.data.modifyPermissionCheck) {
+                                                makeTag += '<img class="cursor" src="/assets/images/delete-icon.png" ng-click="event.removeRelationIssue(data.id)">';
+                                            }
+                                            break;
+
+                                        // �뿰愿� �씠�뒋 �슦�꽑�닚�쐞
+                                        case "REL_COMMON_PRIORITY" :
+                                            makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.priorityVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.priorityVo.color + "\", \"color\": \"#FFFFFF\" }' translate='" + scope.data.priorityVo.name + "'></span>";
+                                            break;
+
+                                        // �뿰愿� �씠�뒋 以묒슂�룄
+                                        case "REL_COMMON_SEVERITY" :
+                                            makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.severityVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.severityVo.color + "\", \"color\": \"#FFFFFF\" }' translate='" + scope.data.severityVo.name + "'></span>";
+                                            break;
+
+                                        // �뿰愿� �씠�뒋 �벑濡앹옄
+                                        case "REL_REGISTER":
+                                            scope.data.registerVos = [scope.data.issueRelation.registerVo];
+                                            makeTag += '<div owl-profile-over class="" table-user-image="data" target="registerVos"></div>';
+                                            break;
+
+                                            break;
+
+                                        // �뿰愿� �씠�뒋 �떞�떦遺��꽌
+                                        case "REL_ISSUE_DEPARTMENT" :
+                                            makeTag += "<ul class='ul-not-comma'>";
+                                            makeTag += "<div style='color: #000000'>";
+                                            angular.forEach(scope.data.issueRelation.departmentVos, function (departments) {
+                                                makeTag += "<li>" + departments.departmentName + "</li>";
+                                            });
+                                            makeTag += "</div>";
+                                            makeTag += "</ul>";
+                                            break;
+
+                                        // �뿰愿� �씠�뒋 紐⑸줉�뿉�꽌 湲곌컙 �몴�떆
+                                        case "REL_ISSUE_DUE_DATE" :
+                                            if (!$rootScope.isDefined(scope.data.issueRelation.startDate) && !$rootScope.isDefined(scope.data.issueRelation.completeDate)) {
+                                                makeTag += "<span translate='common.noDate'>湲곌컙 �뾾�쓬</span>";
+                                            } else {
+                                                makeTag += "<span>" + scope.data.issueRelation.startDate + " ~ " + scope.data.issueRelation.completeDate + "</span>";
+                                            }
+                                            break;
+
+                                        // �뿰愿� �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶
+                                        case "REL_ISSUE_CUSTOM_FIELD_VALUE_VIEW" :
+                                            var values = [];
+
+                                            for (var count in scope.data.issueRelation.issueCustomFieldValueVos) {
+                                                var issueCustomFieldValueVo = scope.data.issueRelation.issueCustomFieldValueVos[count];
+                                                //  �뀒�씠釉� �꽕�젙�뿉�꽌 dName 遺�遺꾩뿉 �궗�슜�옄 �젙�쓽 �븘�뱶 id 瑜� �꽔怨� �빐�떦 媛믪쓣 異붿텧�븳�떎.
+                                                if (tableConfig.columnHint.id == issueCustomFieldValueVo.customFieldVo.id) {
+                                                    values.push(issueCustomFieldValueVo.useValue);
+                                                }
+                                            }
+                                            angular.forEach(values, function (value) {
+                                                makeTag += '<span class="table-word-break-all cursor">' + value + '<span><br>';
+                                            });
+
+                                            break;
+                                    }
+                                }
+
+                                makeTag += '</td>';
+
+                                var linkFn = $compile(makeTag);
+                                var content = linkFn(scope);
+
+                                element.append(content);
+                            });
+                        }
+                    }
+                }
+        }]);
+    });
diff --git a/src/main/webapp/custom_components/js-table/tableColumnGenerator.directive.js b/src/main/webapp/custom_components/js-table/tableColumnGenerator.directive.js
index 7bc7528..cdbf380 100644
--- a/src/main/webapp/custom_components/js-table/tableColumnGenerator.directive.js
+++ b/src/main/webapp/custom_components/js-table/tableColumnGenerator.directive.js
@@ -235,148 +235,148 @@
                                             break;*/
 
                                         // �뿰愿� �씠�뒋 �씠�룞(�젣紐�)
-                                        case "ISSUE_RELATION_MOVE" :
-                                            makeTag += "<span class=\"titlename cursor text-center\" ng-click=\"event.changeDetailView(data.issueRelation)\">" + scope.data.title + "</span></a>";
-                                            break;
+                                        // case "ISSUE_RELATION_MOVE" :
+                                        //     makeTag += "<span class=\"titlename cursor text-center\" ng-click=\"event.changeDetailView(data.issueRelation)\">" + scope.data.title + "</span></a>";
+                                        //     break;
 
                                         // �뿰愿��씠�뒋 援щ텇
-                                        case "ISSUE_RELATION_TYPE":
-                                            makeTag += "<span>" + scope.data.relationIssueTypeName + "</span>";
-                                            break;
+                                        // case "ISSUE_RELATION_TYPE":
+                                        //     makeTag += "<span>" + scope.data.relationIssueTypeName + "</span>";
+                                        //     break;
 
                                         // �뿰愿��씠�뒋 �궘�젣
-                                        case "ISSUE_RELATION_DELETE":
-                                            if (scope.data.modifyPermissionCheck) {
-                                                makeTag += '<img class="cursor" src="/assets/images/delete-icon.png" ng-click="event.removeRelationIssue(data.id)">';
-                                            }
-                                            break;
+                                        // case "ISSUE_RELATION_DELETE":
+                                        //     if (scope.data.modifyPermissionCheck) {
+                                        //         makeTag += '<img class="cursor" src="/assets/images/delete-icon.png" ng-click="event.removeRelationIssue(data.id)">';
+                                        //     }
+                                        //     break;
 
                                         // �뿰愿� �씠�뒋 �슦�꽑�닚�쐞
-                                        case "REL_COMMON_PRIORITY" :
-                                            makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.priorityVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.priorityVo.color + "\", \"color\": \"#FFFFFF\" }' translate='" + scope.data.priorityVo.name + "'></span>";
-                                            break;
+                                        // case "REL_COMMON_PRIORITY" :
+                                        //     makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.priorityVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.priorityVo.color + "\", \"color\": \"#FFFFFF\" }' translate='" + scope.data.priorityVo.name + "'></span>";
+                                        //     break;
 
                                         // �뿰愿� �씠�뒋 以묒슂�룄
-                                        case "REL_COMMON_SEVERITY" :
-                                            makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.severityVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.severityVo.color + "\", \"color\": \"#FFFFFF\" }' translate='" + scope.data.severityVo.name + "'></span>";
-                                            break;
+                                        // case "REL_COMMON_SEVERITY" :
+                                        //     makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.severityVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.severityVo.color + "\", \"color\": \"#FFFFFF\" }' translate='" + scope.data.severityVo.name + "'></span>";
+                                        //     break;
 
                                         // �뿰愿� �씠�뒋 �벑濡앹옄
-                                        case "REL_REGISTER":
-                                            scope.data.registerVos = [scope.data.issueRelation.registerVo];
-                                            makeTag += '<div owl-profile-over class="" table-user-image="data" target="registerVos"></div>';
-                                            break;
-
-                                            break;
+                                        // case "REL_REGISTER":
+                                        //     scope.data.registerVos = [scope.data.issueRelation.registerVo];
+                                        //     makeTag += '<div owl-profile-over class="" table-user-image="data" target="registerVos"></div>';
+                                        //     break;
+                                        //
+                                        //     break;
 
                                         // �뿰愿� �씠�뒋 �떞�떦遺��꽌
-                                        case "REL_ISSUE_DEPARTMENT" :
-                                            makeTag += "<ul class='ul-not-comma'>";
-                                            makeTag += "<div style='color: #000000'>";
-                                            angular.forEach(scope.data.issueRelation.departmentVos, function (departments) {
-                                                makeTag += "<li>" + departments.departmentName + "</li>";
-                                            });
-                                            makeTag += "</div>";
-                                            makeTag += "</ul>";
-                                            break;
+                                        // case "REL_ISSUE_DEPARTMENT" :
+                                        //     makeTag += "<ul class='ul-not-comma'>";
+                                        //     makeTag += "<div style='color: #000000'>";
+                                        //     angular.forEach(scope.data.issueRelation.departmentVos, function (departments) {
+                                        //         makeTag += "<li>" + departments.departmentName + "</li>";
+                                        //     });
+                                        //     makeTag += "</div>";
+                                        //     makeTag += "</ul>";
+                                        //     break;
 
                                         // �뿰愿� �씠�뒋 紐⑸줉�뿉�꽌 湲곌컙 �몴�떆
-                                        case "REL_ISSUE_DUE_DATE" :
-                                            if (!$rootScope.isDefined(scope.data.issueRelation.startDate) && !$rootScope.isDefined(scope.data.issueRelation.completeDate)) {
-                                                makeTag += "<span translate='common.noDate'>湲곌컙 �뾾�쓬</span>";
-                                            }
-                                            else {
-                                                makeTag += "<span>" + scope.data.issueRelation.startDate + " ~ " + scope.data.issueRelation.completeDate + "</span>";
-                                            }
-                                            break;
+                                        // case "REL_ISSUE_DUE_DATE" :
+                                        //     if (!$rootScope.isDefined(scope.data.issueRelation.startDate) && !$rootScope.isDefined(scope.data.issueRelation.completeDate)) {
+                                        //         makeTag += "<span translate='common.noDate'>湲곌컙 �뾾�쓬</span>";
+                                        //     }
+                                        //     else {
+                                        //         makeTag += "<span>" + scope.data.issueRelation.startDate + " ~ " + scope.data.issueRelation.completeDate + "</span>";
+                                        //     }
+                                        //     break;
 
                                         // �뿰愿� �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶
-                                        case "REL_ISSUE_CUSTOM_FIELD_VALUE_VIEW" :
-                                            var values = [];
-
-                                            for (var count in scope.data.issueRelation.issueCustomFieldValueVos) {
-                                                var issueCustomFieldValueVo = scope.data.issueRelation.issueCustomFieldValueVos[count];
-                                                //  �뀒�씠釉� �꽕�젙�뿉�꽌 dName 遺�遺꾩뿉 �궗�슜�옄 �젙�쓽 �븘�뱶 id 瑜� �꽔怨� �빐�떦 媛믪쓣 異붿텧�븳�떎.
-                                                if (tableConfig.columnHint.id == issueCustomFieldValueVo.customFieldVo.id) {
-                                                    values.push(issueCustomFieldValueVo.useValue);
-                                                }
-                                            }
-                                            angular.forEach(values, function (value) {
-                                                makeTag += '<span class="table-word-break-all cursor">' + value + '<span><br>';
-                                            });
-
-                                            break;
+                                        // case "REL_ISSUE_CUSTOM_FIELD_VALUE_VIEW" :
+                                        //     var values = [];
+                                        //
+                                        //     for (var count in scope.data.issueRelation.issueCustomFieldValueVos) {
+                                        //         var issueCustomFieldValueVo = scope.data.issueRelation.issueCustomFieldValueVos[count];
+                                        //         //  �뀒�씠釉� �꽕�젙�뿉�꽌 dName 遺�遺꾩뿉 �궗�슜�옄 �젙�쓽 �븘�뱶 id 瑜� �꽔怨� �빐�떦 媛믪쓣 異붿텧�븳�떎.
+                                        //         if (tableConfig.columnHint.id == issueCustomFieldValueVo.customFieldVo.id) {
+                                        //             values.push(issueCustomFieldValueVo.useValue);
+                                        //         }
+                                        //     }
+                                        //     angular.forEach(values, function (value) {
+                                        //         makeTag += '<span class="table-word-break-all cursor">' + value + '<span><br>';
+                                        //     });
+                                        //
+                                        //     break;
 
                                         // �븯�쐞 �씠�뒋 �씠�룞(�젣紐�)
-                                        case "ISSUE_DOWN_MOVE" :
-                                            makeTag += "<span class=\"titlename cursor\" ng-click=\"event.changeDetailView(data)\">" + scope.data.title + "</span></a>";
-                                            break;
-
-                                        // �븯�쐞 �씠�뒋 ���엯
-                                        case "ISSUE_DOWN_STATUS_TYPE" :
-                                            makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.issueStatusVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.issueStatusVo.color + "\", \"color\": \"#FFFFFF\" }'>" + scope.data.issueStatusVo.name + "</span>";
-                                            break;
-
+                                        // case "ISSUE_DOWN_MOVE" :
+                                        //     makeTag += "<span class=\"titlename cursor\" ng-click=\"event.changeDetailView(data)\">" + scope.data.title + "</span></a>";
+                                        //     break;
+                                        //
+                                        // // �븯�쐞 �씠�뒋 ���엯
+                                        // case "ISSUE_DOWN_STATUS_TYPE" :
+                                        //     makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.issueStatusVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.issueStatusVo.color + "\", \"color\": \"#FFFFFF\" }'>" + scope.data.issueStatusVo.name + "</span>";
+                                        //     break;
+                                        //
                                         // �븯�쐞 �씠�뒋 �궘�젣
-                                        case "ISSUE_DOWN_DELETE":
-                                            if (scope.data.modifyPermissionCheck) {
-                                                makeTag += '<img class="cursor" src="/assets/images/delete-icon.png" ng-click="event.removeDownIssue(data.id)">';
-                                            }
-                                            break;
-
+                                        // case "ISSUE_DOWN_DELETE":
+                                        //     if (scope.data.modifyPermissionCheck) {
+                                        //         makeTag += '<img class="cursor" src="/assets/images/delete-icon.png" ng-click="event.removeDownIssue(data.id)">';
+                                        //     }
+                                        //     break;
+                                        //
                                         // �븯�쐞 �씠�뒋 �슦�꽑 �닚�쐞
-                                        case "DOWN_COMMON_PRIORITY" :
-                                            makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.priorityVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.priorityVo.color + "\", \"color\": \"#FFFFFF\" }' translate='" + scope.data.priorityVo.name + "'></span>";
-                                            break;
-
+                                        // case "DOWN_COMMON_PRIORITY" :
+                                        //     makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.priorityVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.priorityVo.color + "\", \"color\": \"#FFFFFF\" }' translate='" + scope.data.priorityVo.name + "'></span>";
+                                        //     break;
+                                        //
                                         // �븯�쐞 �씠�뒋 以묒슂�룄
-                                        case "DOWN_COMMON_SEVERITY" :
-                                            makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.severityVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.severityVo.color + "\", \"color\": \"#FFFFFF\" }' translate='" + scope.data.severityVo.name + "'></span>";
-                                            break;
-
+                                        // case "DOWN_COMMON_SEVERITY" :
+                                        //     makeTag += "<span class='badge' ng-style='{ \"background-color\" : \"" + scope.data.severityVo.color + "\"," + "\"border-color\"" + " : \"" + scope.data.severityVo.color + "\", \"color\": \"#FFFFFF\" }' translate='" + scope.data.severityVo.name + "'></span>";
+                                        //     break;
+                                        //
                                         // �븯�쐞 �씠�뒋 �떞�떦遺��꽌
-                                        case "DOWN_ISSUE_DEPARTMENT" :
-                                            makeTag += "<ul class='ul-not-comma'>";
-                                            makeTag += "<div style='color: #000000'>";
-                                            angular.forEach(scope.data.departmentVos, function (departments) {
-                                                makeTag += "<li>" + departments.departmentName + "</li>";
-                                            });
-                                            makeTag += "</div>";
-                                            makeTag += "</ul>";
-                                            break;
-
+                                        // case "DOWN_ISSUE_DEPARTMENT" :
+                                        //     makeTag += "<ul class='ul-not-comma'>";
+                                        //     makeTag += "<div style='color: #000000'>";
+                                        //     angular.forEach(scope.data.departmentVos, function (departments) {
+                                        //         makeTag += "<li>" + departments.departmentName + "</li>";
+                                        //     });
+                                        //     makeTag += "</div>";
+                                        //     makeTag += "</ul>";
+                                        //     break;
+                                        //
                                         // �븯�쐞 �씠�뒋 �벑濡앹옄
-                                        case "DOWN_REGISTER":
-                                            scope.data.registerVos = [scope.data.registerVo];
-                                            makeTag += '<div owl-profile-over class="" table-user-image="data" target="registerVos"></div>';
-                                            break;
-
+                                        // case "DOWN_REGISTER":
+                                        //     scope.data.registerVos = [scope.data.registerVo];
+                                        //     makeTag += '<div owl-profile-over class="" table-user-image="data" target="registerVos"></div>';
+                                        //     break;
+                                        //
                                         // �븯�쐞 �씠�뒋 湲곌컙
-                                        case "DOWN_ISSUE_DUE_DATE" :
-                                            if (!$rootScope.isDefined(scope.data.startDate) && !$rootScope.isDefined(scope.data.completeDate)) {
-                                                makeTag += "<span translate='common.noDate'>湲곌컙 �뾾�쓬</span>";
-                                            }
-                                            else {
-                                                makeTag += "<span>" + scope.data.startDate + " ~ " + scope.data.completeDate + "</span>";
-                                            }
-                                            break;
-
+                                        // case "DOWN_ISSUE_DUE_DATE" :
+                                        //     if (!$rootScope.isDefined(scope.data.startDate) && !$rootScope.isDefined(scope.data.completeDate)) {
+                                        //         makeTag += "<span translate='common.noDate'>湲곌컙 �뾾�쓬</span>";
+                                        //     }
+                                        //     else {
+                                        //         makeTag += "<span>" + scope.data.startDate + " ~ " + scope.data.completeDate + "</span>";
+                                        //     }
+                                        //     break;
+                                        //
                                         // �븯�쐞 �씠�뒋 �궗�슜�옄 �젙�쓽 �븘�뱶
-                                        case "DOWN_ISSUE_CUSTOM_FIELD_VALUE_VIEW" :
-                                            var values = [];
-
-                                            for (var count in scope.data.issueCustomFieldValueVos) {
-                                                var issueCustomFieldValueVo = scope.data.issueCustomFieldValueVos[count];
-                                                //  �뀒�씠釉� �꽕�젙�뿉�꽌 dName 遺�遺꾩뿉 �궗�슜�옄 �젙�쓽 �븘�뱶 id 瑜� �꽔怨� �빐�떦 媛믪쓣 異붿텧�븳�떎.
-                                                if (tableConfig.columnHint.id == issueCustomFieldValueVo.customFieldVo.id) {
-                                                    values.push(issueCustomFieldValueVo.useValue);
-                                                }
-                                            }
-                                            angular.forEach(values, function (value) {
-                                                makeTag += '<span class="table-word-break-all">' + value + '<span><br>';
-                                            });
-
-                                            break;
+                                        // case "DOWN_ISSUE_CUSTOM_FIELD_VALUE_VIEW" :
+                                        //     var values = [];
+                                        //
+                                        //     for (var count in scope.data.issueCustomFieldValueVos) {
+                                        //         var issueCustomFieldValueVo = scope.data.issueCustomFieldValueVos[count];
+                                        //         //  �뀒�씠釉� �꽕�젙�뿉�꽌 dName 遺�遺꾩뿉 �궗�슜�옄 �젙�쓽 �븘�뱶 id 瑜� �꽔怨� �빐�떦 媛믪쓣 異붿텧�븳�떎.
+                                        //         if (tableConfig.columnHint.id == issueCustomFieldValueVo.customFieldVo.id) {
+                                        //             values.push(issueCustomFieldValueVo.useValue);
+                                        //         }
+                                        //     }
+                                        //     angular.forEach(values, function (value) {
+                                        //         makeTag += '<span class="table-word-break-all">' + value + '<span><br>';
+                                        //     });
+                                        //
+                                        //     break;
 
                                         //  �씠由꾩쓣 �겢由��븯硫� �닔�젙 �뙘�뾽 �몴�떆
                                         case "COMMON_MODIFY" :
diff --git a/src/main/webapp/i18n/ko/global.json b/src/main/webapp/i18n/ko/global.json
index fa6a8c1..860aabd 100644
--- a/src/main/webapp/i18n/ko/global.json
+++ b/src/main/webapp/i18n/ko/global.json
@@ -281,12 +281,16 @@
         "setIssueRelationTableDisplay": "�뿰愿� �씠�뒋 �뀒�씠釉� �몴�떆 �꽕�젙",
         "setIssueDownTableDisplay": "�븯�쐞 �씠�뒋 �뀒�씠釉� �몴�떆 �꽕�젙",
         "columnName": "而щ읆紐�",
+        "selectPartners": "�뾽泥� �꽑�깮",
+        "partners" : "�뾽泥� �씠硫붿씪",
         "area": "�꼻�씠",
         "displayed": "�몴�떆 �뿬遺�",
         "sequence": "�닚�꽌",
         "cannotChangedIssueTitle": "�씠�뒋 �젣紐⑹� 蹂�寃쏀븷 �닔 �뾾�뒿�땲�떎.",
         "cannotChangedIssueType": "�씠�뒋 援щ텇�� 蹂�寃쏀븷 �닔 �뾾�뒿�땲�떎.",
-        "selectSendIssueMail": "�씠�뒋 硫붿씪 諛쒖넚 ���긽�옄 �꽑�깮",
+        "selectSendIssueMail": "�뾽泥� 硫붿씪 諛쒖넚",
+        "CommonSendIssueMail": "�씪諛� 硫붿씪 諛쒖넚",
+        "SendIssueMail": "硫붿씪 諛쒖넚 �엯�젰",
         "sendIssueSelectedUsers": "�봽濡쒖젥�듃�뿉 李몄뿬�븯怨� �엳�뒗 �떎瑜� �궗�슜�옄�뿉寃� �씠�뒋 �젙蹂대�� 蹂대깄�땲�떎.",
         "sendMail": "�씠硫붿씪 諛쒖넚",
         "changedHistory": "�씠�뒋 蹂�寃� �씠�젰 �긽�꽭�젙蹂�",
@@ -327,6 +331,10 @@
         "succeededIssueMail": "�씠�뒋 硫붿씪 諛쒖넚 �셿猷�",
         "sentToTheSelectedUser": "�꽑�깮�븳 �궗�슜�옄�뿉寃� �씠硫붿씪�씠 諛쒖넚�릺�뿀�뒿�땲�떎.",
         "failedIssueMail": "�씠�뒋 硫붿씪 諛쒖넚 �떎�뙣",
+        "selectedPartnersMail": "�뾽泥� �씠硫붿씪�쓣 �꽑�깮�븯�꽭�슂.",
+        "selectedPartnersTemplate": "�뾽泥� �씠硫붿씪 �뀥�뵆由우쓣 �꽑�깮�빐二쇱꽭�슂.",
+        "writeIssueMail": "�씠硫붿씪�쓣 �엯�젰�빐二쇱꽭�슂.",
+        "writeMail": "�씠硫붿씪�쓣 �엯�젰�븯�뀛�빞 異붽��븷�닔 �엳�뒿�땲�떎.",
         "issueVersionLookupFailed": "�씠�뒋 踰꾩쟾 議고쉶 �떎�뙣",
         "relationIssueType1" : "�떎�쓬 �씠�뒋�� 愿��젴�맖",
         "relationIssueType2" : "�떎�쓬 �씠�뒋�뿉 以묐났�맖",
@@ -694,6 +702,8 @@
         "checkAll": "�쟾泥� �꽑�깮",
         "unCheckAll": "�쟾泥� �빐�젣",
         "send": "蹂대궡湲�",
+        "emailExplain": "諛쏅뒗 �궗�엺�쓽 �씠硫붿씪 �삎�떇�쓣 �엯�젰�븯�뀛�빞 �빀�땲�떎.",
+        "sendToPerson" : "�떎瑜� �궗�슜�옄�뿉寃� 硫붿씪�쓣 蹂대깄�땲�떎.",
         "selected": "�꽑�깮�맖",
         "selectable": "�꽑�깮 媛��뒫",
         "password": "鍮꾨�踰덊샇",
@@ -710,6 +720,7 @@
         "priority": "�슦�꽑 �닚�쐞",
         "importance": "以묒슂�룄",
         "assignee": "�떞�떦�옄",
+        "toPerson": "諛쏅뒗 �궗�엺",
         "assigneeTeam" : "�떞�떦遺��꽌",
         "register": "�벑濡앹옄",
         "startDate": "�떆�옉�씪",
diff --git a/src/main/webapp/scripts/app/issue/issue.js b/src/main/webapp/scripts/app/issue/issue.js
index 81cc436..da32644 100644
--- a/src/main/webapp/scripts/app/issue/issue.js
+++ b/src/main/webapp/scripts/app/issue/issue.js
@@ -34,10 +34,10 @@
                             var deferred = $q.defer();
                             require([
                                 'issueListTimelineController', 'issueManagerController', 'issueListController', 'issueAddController', 'issueModifyController', 'issueDetailController', 'issueAddRelationController', 'issueAddDownController', 'issueImportExcelController',
-                                'chartLoader', 'jsTable', 'jsTree', 'tableColumnGenerator', 'treeColumnGenerator', 'modalFormAutoScroll', 'summerNote', 'summerNote-ko-KR', 'fullScroll', 'workflowService', 'priorityService', 'issueSearchService', 'issueTableConfigService', 'inputRegex',
+                                'chartLoader', 'jsTable', 'jsTree', 'jsRel', 'jsDown', 'tableColumnGenerator', 'treeColumnGenerator', 'relColumnGenerator', 'downColumnGenerator', 'modalFormAutoScroll', 'summerNote', 'summerNote-ko-KR', 'fullScroll', 'workflowService', 'priorityService', 'issueSearchService', 'issueTableConfigService', 'inputRegex',
                                 'severityService', 'issueTypeService', 'issueTypeCustomFieldService', 'issueService', 'issueStatusService', 'emailTemplateService','issueUserService','issueDepartmentService','issueModifyUserController', 'issueModifyDepartmentController', 'customFieldService', 'issueSearchFieldKeyViewElement',
                                 'issueSearchCustomFieldViewElement', 'tableUserImage', 'fullScroll', 'issueCommentService', 'detectIssueEditor', 'formSubmit', 'issueModifyStatusController', 'downIssueModifyStatusController', 'jsShortCut',
-                                'issueAddTableConfigController','issueAddRelationTableConfigController','issueAddDownTableConfigController','domAppend', 'issueDetailImagePreview', 'issueSendMailPartnersController', 'htmlDiff', 'issueVersionViewController', 'issueVersionService',
+                                'issueAddTableConfigController','issueAddRelationTableConfigController','issueAddDownTableConfigController','domAppend', 'issueDetailImagePreview', 'issueSendMailPartnersController', 'issueCommonSendMailController', 'htmlDiff', 'issueVersionViewController', 'issueVersionService',
                                 'jsHtmlDiff', 'issueReservationController', 'issueReservationService', 'issueVersionService', 'issueStatusAutoFocus', 'issueRelationService'
                             ], function () {
                                 deferred.resolve();
diff --git a/src/main/webapp/scripts/app/issue/issueCommonSendMail.controller.js b/src/main/webapp/scripts/app/issue/issueCommonSendMail.controller.js
new file mode 100644
index 0000000..6440824
--- /dev/null
+++ b/src/main/webapp/scripts/app/issue/issueCommonSendMail.controller.js
@@ -0,0 +1,111 @@
+'use strict';
+
+define([
+        'app'
+    ],
+    function (app) {
+        app.controller('issueCommonSendMailController', ['$scope', '$rootScope', '$state', '$log', '$resourceProvider', '$uibModalInstance', '$controller', '$injector', '$q','SweetAlert', '$filter', 'Issue', 'parameter',
+            function ($scope, $rootScope, $state, $log, $resourceProvider, $uibModalInstance, $controller, $injector, $q, SweetAlert, $filter, Issue, parameter) {
+
+                $scope.fn = {
+                    cancel : cancel,    //  �뙘�뾽 李� �떕湲�
+                    formSubmit : formSubmit,    //  �뤌 �쟾�넚
+                    formCheck : formCheck,   //  �뤌 泥댄겕
+                    addInput : addInput,
+                    removeInput : removeInput
+                };
+
+                $scope.vm = {
+                    form : {
+                        issueId : parameter.issueId,
+                        title : "",
+                        description: "",
+                        inputs : [0],
+                        emails : {},    //  �엯�젰�맂 �궗�슜�옄 �씠硫붿씪
+                    },
+                    userName : "",
+                    autoCompletePage : {
+                        user : {
+                            page : 0,
+                            totalPage : 0
+                        }
+                    }
+                };
+
+                // �뤌 泥댄겕
+                function formCheck(formInvalid) {
+                    if (formInvalid) {
+                        return true;
+                    }
+                    return false;
+                }
+
+                // 硫붿씪 二쇱냼 input 李� 異붽� 踰꾪듉
+                function addInput() {
+                    var arrayFull = true;      // 諛곗뿴�씠 媛��뱷 李� �엳�뒗吏� �뿬遺�
+                    var index = 0;
+                    $scope.vm.form.inputs.forEach(function (email) {
+                        if (!$rootScope.isDefined($scope.vm.form.emails[index])) {
+                            arrayFull = false;
+                        }
+                        index++;
+                    });
+
+                    if (arrayFull) {
+                        $scope.vm.form.inputs.push(index);
+                        $scope.vm.form.emails[index] = ""
+                    } else {
+                        SweetAlert.warning($filter("translate")("issue.writeIssueMail"), $filter("translate")("issue.writeMail")); // 異붽�踰꾪듉 寃쎄퀬
+                    }
+                }
+
+                //  �뤌 �쟾�넚
+                function formSubmit() {
+                    $rootScope.spinner = true;
+
+                    var content = {
+                        issueId : $scope.vm.form.issueId,
+                        title : $scope.vm.form.title,
+                        description : $scope.vm.form.description,
+                        sendEmails : (function () {
+                            var sendEmails = [];
+                            var index = 0
+                            $scope.vm.form.inputs.forEach(function (email) {
+                                if ($rootScope.isDefined($scope.vm.form.emails[index])) {
+                                    sendEmails.push($rootScope.encryption($scope.vm.form.emails[index]));   // �씠硫붿씪二쇱냼 �븫�샇�솕
+                                }
+                                index++;
+                            });
+
+                            return sendEmails;
+                        })()
+                    };
+
+                    Issue.sendCommonEmail($resourceProvider.getContent(
+                        content,
+                        $resourceProvider.getPageContent(0, 10))).then(function (result) {
+
+                        if (result.data.message.status === "success") {
+                            SweetAlert.success($filter("translate")("issue.succeededIssueMail"), $filter("translate")("issue.sentToTheSelectedUser")); // "�씠�뒋 硫붿씪 諛쒖넚 �셿猷�"
+                            $scope.fn.cancel();
+                        }
+                        else {
+                            SweetAlert.error($filter("translate")("issue.failedIssueMail"), result.data.message.message); // "�씠�뒋 硫붿씪 諛쒖넚 �떎�뙣"
+                        }
+
+                        $rootScope.spinner = false;
+                    });
+                }
+
+                // �씠硫붿씪 二쇱냼 input �궘�젣
+                function removeInput(index) {
+                    $scope.vm.form.inputs.splice(index, 1);
+                }
+
+                function cancel() {
+                    $rootScope.$broadcast("closeLayer");    //  �뙘�뾽�씠 �뿴由ш퀬 �굹�꽌 js-multi, js-single �벑�뿉�꽌 body �씠踰ㅽ듃媛� �궇�븘媛��뒗 �쁽�긽 �닔�젙
+                    $uibModalInstance.dismiss('cancel');
+                    $(document).unbind("keydown");  //  �떒異뺥궎 �씠踰ㅽ듃 �젣嫄�
+                }
+            }]);
+    });
diff --git a/src/main/webapp/scripts/app/issue/issueDetail.controller.js b/src/main/webapp/scripts/app/issue/issueDetail.controller.js
index e07ca8d..dab59ac 100644
--- a/src/main/webapp/scripts/app/issue/issueDetail.controller.js
+++ b/src/main/webapp/scripts/app/issue/issueDetail.controller.js
@@ -8,9 +8,9 @@
         'angular'
     ],
     function (app, angular) {
-        app.controller('issueDetailController', ['$scope', '$rootScope', '$log', '$resourceProvider', '$tableProvider', '$state', '$uibModal', '$q',
+        app.controller('issueDetailController', ['$scope', '$rootScope', '$log', '$resourceProvider', '$tableProvider', '$relProvider', '$downProvider', '$state', '$uibModal', '$q',
             '$controller', '$injector', 'SweetAlert', '$timeout', 'Issue', 'IssueComment', 'IssueRelation', 'AttachedFile',  'Priority', 'Severity','IssueStatus', 'IssueTableConfig', '$filter',
-            function ($scope, $rootScope, $log, $resourceProvider, $tableProvider, $state, $uibModal, $q, $controller, $injector, SweetAlert, $timeout, Issue, IssueComment, IssueRelation, AttachedFile, Priority, Severity, IssueStatus, IssueTableConfig, $filter) {
+            function ($scope, $rootScope, $log, $resourceProvider, $tableProvider, $relProvider, $downProvider, $state, $uibModal, $q, $controller, $injector, SweetAlert, $timeout, Issue, IssueComment, IssueRelation, AttachedFile, Priority, Severity, IssueStatus, IssueTableConfig, $filter) {
 
                 //  IssueListController vm, fn 蹂��닔 �긽�냽.
 
@@ -54,6 +54,7 @@
                 $scope.fn.removeRelationIssue = removeRelationIssue;
                 $scope.fn.removeDownIssue = removeDownIssue;
                 $scope.fn.changeDetailPageRowCount = changeDetailPageRowCount;    //  �럹�씠吏� 蹂�寃�
+                $scope.fn.sendCommonMail = sendCommonMail;
 
                 //  �씠�뒋 紐⑸줉 而⑦듃濡ㅻ윭 vm, fn �긽�냽 以�
                 $scope.vm.viewer = {};      // �쁽�옱 �씠�뒋
@@ -327,90 +328,98 @@
                 }
 
                 //  �뀒�씠釉붿쓽 �뿰愿� �씠�뒋 而щ읆�쓣 留뚮뱾�뼱以��떎.
-                function setRelTableColumn(Rel_issueTableConfig) {
+                function setRelTableColumn(issueTableConfig) {
 
                     //  �뿰愿� �씠�뒋 而щ읆
-                    switch(Rel_issueTableConfig.key) {
+                    switch(issueTableConfig.key) {
                         case "RELATION_ISSUE_TYPE" :    // �뿰愿� �씠�뒋 援щ텇
-                            $scope.vm.relTableConfigs.push($tableProvider.config()
+                            $scope.vm.relTableConfigs.push($relProvider.config()
                                 .setHName("issue.relationIssueType")
+                                .setDName("relationIssueType")
                                 .setDType("renderer")
                                 .setDAlign("text-center")
-                                .setHWidth("bold " + Rel_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDRenderer("ISSUE_RELATION_TYPE"))
                                 /*.setHWidth("width-30 bold")*/
                                 /*.setHSort(false)*/
                             break;
                         case "RELATION_ISSUE_TITLE" :   // �뿰愿� �씠�뒋 �젣紐�
-                            $scope.vm.relTableConfigs.push($tableProvider.config()
+                            $scope.vm.relTableConfigs.push($relProvider.config()
                                 .setHName("issue.relationIssueTitle")
+                                .setDName("title")
                                 .setDType("renderer")
                                 .setDAlign("text-center")
-                                .setHWidth("bold " + Rel_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDRenderer("ISSUE_RELATION_MOVE"))
                                 /*.setHWidth("width-60 bold")*/
                                 /*.setHSort(true)*/
                             break;
                         case "RELATION_PRIORITY" :   // �뿰愿� �씠�뒋 �슦�꽑�닚�쐞
-                            $scope.vm.relTableConfigs.push($tableProvider.config()
+                            $scope.vm.relTableConfigs.push($relProvider.config()
                                 .setHName("common.priority")
+                                .setDName("priorityVo.id")
                                 .setDType("renderer")
-                                .setHWidth("bold " + Rel_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
                                 .setDRenderer("REL_COMMON_PRIORITY"));
                             break;
                         case "RELATION_SEVERITY" :   //  �뿰愿� �씠�뒋 以묒슂�룄
-                            $scope.vm.relTableConfigs.push($tableProvider.config()
+                            $scope.vm.relTableConfigs.push($relProvider.config()
                                 .setHName("common.importance")
+                                .setDName("severityVo.id")
                                 .setDType("renderer")
-                                .setHWidth("bold " + Rel_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
                                 .setDRenderer("REL_COMMON_SEVERITY"));
                             break;
                         case "RELATION_ASSIGNEE_TEAM" :   // �뿰愿� �씠�뒋 �떞�떦遺��꽌
-                            $scope.vm.relTableConfigs.push($tableProvider.config()
+                            $scope.vm.relTableConfigs.push($relProvider.config()
                                 .setHName("common.assigneeTeam")
+                                .setDName("departmentVos.departmentName")
                                 .setDType("renderer")
-                                .setHWidth("bold " + Rel_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
                                 .setDRenderer("REL_ISSUE_DEPARTMENT"));
                             break;
 
-                        case "RELATION_REGISTER" :   // �뿰愿� �씠�뒋  �벑濡앹옄
-                            $scope.vm.relTableConfigs.push($tableProvider.config()
+                        case "RELATION_REGISTER" :   // �뿰愿� �씠�뒋 �벑濡앹옄
+                            $scope.vm.relTableConfigs.push($relProvider.config()
                                 .setHName("common.register")
+                                .setDName("registerVo.id")
                                 .setDType("renderer")
-                                .setHWidth("bold " + Rel_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
                                 .setDRenderer("REL_REGISTER"));
                             break;
                         case "RELATION_PERIOD" : // �뿰愿� �씠�뒋 湲곌컙
-                            $scope.vm.relTableConfigs.push($tableProvider.config()
+                            $scope.vm.relTableConfigs.push($relProvider.config()
                                 .setHName("common.period")
                                 .setDType("renderer")
-                                .setHWidth("bold " + Rel_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
                                 .setDRenderer("REL_ISSUE_DUE_DATE"));
                             break;
-                        case "RELATION_MODIFY_DATE" : // �뿰愿� �씠�뒋  理쒓렐 蹂�寃쎌씪
-                            $scope.vm.relTableConfigs.push($tableProvider.config()
+                        case "RELATION_MODIFY_DATE" : // �뿰愿� �씠�뒋 理쒓렐 蹂�寃쎌씪
+                            $scope.vm.relTableConfigs.push($relProvider.config()
                                 .setHName("common.lastChangeDate")
-                                .setHWidth("bold " + Rel_issueTableConfig.width)
-                                .setDAlign("text-center"));
+                                .setHWidth("bold " + issueTableConfig.width)
+                                .setDAlign("text-center")
+                                .setDName("modifyDate"));
                             break;
                     }
 
                     //  �궗�슜�옄 �젙�쓽 �븘�뱶 而щ읆
-                    if (Rel_issueTableConfig.key.indexOf("CUSTOM_FIELD_") !== -1) {
+                    if (issueTableConfig.key.indexOf("CUSTOM_FIELD_") !== -1) {
                         //  留뚯빟 �씠�뒋 �뀒�씠釉� 而щ읆紐낆씠 �몴�떆�릺吏� �븡�쑝硫� �씠履쎌씠 臾몄젣
                         for (var count in $scope.vm.customFields) {
                             var customField = $scope.vm.customFields[count];
 
-                            if (customField.id === Number(Rel_issueTableConfig.key.substring(13))) {
-                                $scope.vm.relTableConfigs.push($tableProvider.config()
+                            if (customField.id === Number(issueTableConfig.key.substring(13))) {
+                                $scope.vm.relTableConfigs.push($relProvider.config()
                                     .setHName(customField.name)
+                                    .setDName("relCustomFieldName" + [count])
                                     .setDType("renderer")
-                                    .setHWidth("bold " + Rel_issueTableConfig.width)
+                                    .setHWidth("bold " + issueTableConfig.width)
                                     .setDAlign("text-center")
                                     .setColumnHint(customField)
                                     .setDRenderer("REL_ISSUE_CUSTOM_FIELD_VALUE_VIEW"));
@@ -422,88 +431,96 @@
 
 
                 //  �뀒�씠釉붿쓽 �븯�쐞 �씠�뒋 而щ읆�쓣 留뚮뱾�뼱以��떎.
-                function setDownTableColumn(Down_issueTableConfig) {
+                function setDownTableColumn(issueTableConfig) {
                     // if (issueTableConfig == null) return;
 
                     //  �븯�쐞 �씠�뒋 而щ읆
-                    switch(Down_issueTableConfig.key) {
+                    switch(issueTableConfig.key) {
                         case "DOWN_ISSUE_TITLE" :   //  �븯�쐞 �씠�뒋 �젣紐�
-                            $scope.vm.downTableConfigs.push($tableProvider.config()
+                            $scope.vm.downTableConfigs.push($downProvider.config()
                                 .setHName("issue.downIssueTitle")
+                                .setDName("title")
                                 .setDType("renderer")
-                                .setHWidth("bold " + Down_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
                                 .setDRenderer("ISSUE_DOWN_MOVE"));
                             break;
 
-                        case "ISSUE_DOWN_STATUS_TYPE" : //  �씠�뒋 �긽�깭
-                            $scope.vm.downTableConfigs.push($tableProvider.config()
+                        case "ISSUE_DOWN_STATUS_TYPE" : // �븯�쐞 �씠�뒋 �긽�깭
+                            $scope.vm.downTableConfigs.push($downProvider.config()
                                 .setHName("issue.issueStatus")
+                                .setDName("issueStatusVo.id")
                                 .setDType("renderer")
-                                .setHWidth("bold " + Down_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
                                 .setDRenderer("ISSUE_DOWN_STATUS_TYPE"));
                             break;
 
                         case "DOWN_PRIORITY" :   // �븯�쐞 �씠�뒋 �슦�꽑�닚�쐞
-                            $scope.vm.downTableConfigs.push($tableProvider.config()
+                            $scope.vm.downTableConfigs.push($downProvider.config()
                                 .setHName("common.priority")
+                                .setDName("priorityVo.id")
                                 .setDType("renderer")
-                                .setHWidth("bold " + Down_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
                                 .setDRenderer("DOWN_COMMON_PRIORITY"));
                             break;
-                        case "DOWN_SEVERITY" :   //  以묒슂�룄
-                            $scope.vm.downTableConfigs.push($tableProvider.config()
+                        case "DOWN_SEVERITY" :   // �븯�쐞 �씠�뒋 以묒슂�룄
+                            $scope.vm.downTableConfigs.push($downProvider.config()
                                 .setHName("common.importance")
+                                .setDName("severityVo.id")
                                 .setDType("renderer")
-                                .setHWidth("bold " + Down_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
                                 .setDRenderer("DOWN_COMMON_SEVERITY"));
                             break;
-                        case "DOWN_ASSIGNEE_TEAM" :   //  �떞�떦遺��꽌
-                            $scope.vm.downTableConfigs.push($tableProvider.config()
+                        case "DOWN_ASSIGNEE_TEAM" :   // �븯�쐞 �씠�뒋 �떞�떦遺��꽌
+                            $scope.vm.downTableConfigs.push($downProvider.config()
                                 .setHName("common.assigneeTeam")
+                                .setDName("departmentVos.departmentName")
                                 .setDType("renderer")
-                                .setHWidth("bold " + Down_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
                                 .setDRenderer("DOWN_ISSUE_DEPARTMENT"));
                             break;
-                        case "DOWN_REGISTER" :   //  �벑濡앹옄
-                            $scope.vm.downTableConfigs.push($tableProvider.config()
+                        case "DOWN_REGISTER" :   // �븯�쐞 �씠�뒋 �벑濡앹옄
+                            $scope.vm.downTableConfigs.push($downProvider.config()
                                 .setHName("common.register")
+                                .setDName("registerVo.id")
                                 .setDType("renderer")
-                                .setHWidth("bold " + Down_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
                                 .setDRenderer("DOWN_REGISTER"));
                             break;
-                        case "DOWN_PERIOD" : //  湲곌컙
-                            $scope.vm.downTableConfigs.push($tableProvider.config()
+                        case "DOWN_PERIOD" : // �븯�쐞 �씠�뒋 湲곌컙
+                            $scope.vm.downTableConfigs.push($downProvider.config()
                                 .setHName("common.period")
                                 .setDType("renderer")
-                                .setHWidth("bold " + Down_issueTableConfig.width)
+                                .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
                                 .setDRenderer("DOWN_ISSUE_DUE_DATE"));
                             break;
-                        case "DOWN_MODIFY_DATE" : //  理쒓렐 蹂�寃쎌씪
-                            $scope.vm.downTableConfigs.push($tableProvider.config()
+                        case "DOWN_MODIFY_DATE" : // �븯�쐞 �씠�뒋 理쒓렐 蹂�寃쎌씪
+                            $scope.vm.downTableConfigs.push($downProvider.config()
                                 .setHName("common.lastChangeDate")
-                                .setHWidth("bold " + Down_issueTableConfig.width)
-                                .setDAlign("text-center"));
+                                .setHWidth("bold " + issueTableConfig.width)
+                                .setDAlign("text-center")
+                                .setDName("modifyDate"));
                             break;
                     }
 
                     //  �궗�슜�옄 �젙�쓽 �븘�뱶 而щ읆
-                    if (Down_issueTableConfig.key.indexOf("CUSTOM_FIELD_") !== -1) {
+                    if (issueTableConfig.key.indexOf("CUSTOM_FIELD_") !== -1) {
                         //  留뚯빟 �씠�뒋 �뀒�씠釉� 而щ읆紐낆씠 �몴�떆�릺吏� �븡�쑝硫� �씠履쎌씠 臾몄젣
                         for (var count in $scope.vm.customFields) {
                             var customField = $scope.vm.customFields[count];
 
-                            if (customField.id === Number(Down_issueTableConfig.key.substring(13))) {
-                                $scope.vm.downTableConfigs.push($tableProvider.config()
+                            if (customField.id === Number(issueTableConfig.key.substring(13))) {
+                                $scope.vm.downTableConfigs.push($downProvider.config()
                                     .setHName(customField.name)
+                                    .setDName("downCustomFieldName" + [count])
                                     .setDType("renderer")
-                                    .setHWidth("bold " + Down_issueTableConfig.width)
+                                    .setHWidth("bold " + issueTableConfig.width)
                                     .setDAlign("text-center")
                                     .setColumnHint(customField)
                                     .setDRenderer("DOWN_ISSUE_CUSTOM_FIELD_VALUE_VIEW"));
@@ -517,18 +534,18 @@
                 //  �뿰愿� �씠�뒋 �뀒�씠釉� �꽕�젙
                 function makeRelTableConfigs() {
                     $scope.vm.relTableConfigs = [];
-                    $scope.vm.relTableConfigs.push($tableProvider.config()
+                    $scope.vm.relTableConfigs.push($relProvider.config()
                         .setDType("checkbox")
                         .setHWidth("width-20-p")
                         .setDAlign("text-center"))
-                    $scope.vm.relTableConfigs.push($tableProvider.config()
+                    $scope.vm.relTableConfigs.push($relProvider.config()
                         .setHName("issue.relationIssueType")
                         .setDType("renderer")
                         .setDAlign("text-center")
                         .setHWidth("width-60-p bold")
                         .setHSort(false)
                         .setDRenderer("ISSUE_RELATION_TYPE"))
-                    $scope.vm.relTableConfigs.push($tableProvider.config()
+                    $scope.vm.relTableConfigs.push($relProvider.config()
                         .setHName("issue.relationIssueTitle")
                         .setDType("renderer")
                         .setDAlign("text-center")
@@ -536,7 +553,7 @@
                         .setHSort(false)
                         .setDRenderer("ISSUE_RELATION_MOVE"))
                     /*if($scope.vm.viewer.modifyPermissionCheck) {
-                        $scope.vm.relTableConfigs.push($tableProvider.config()
+                        $scope.vm.relTableConfigs.push($relProvider.config()
                             .setHName("issue.relationIssueDelete")
                             .setDType("renderer")
                             .setDAlign("text-center")
@@ -545,11 +562,11 @@
                             .setHSort(false)
                             .setDAlign("text-center"))
                     }*/
-                    angular.forEach($scope.vm.relTableConfigs, function (Rel_issueTableConfig) {
+                    angular.forEach($scope.vm.relTableConfigs, function (issueTableConfig) {
                         //  �몴�떆 ���긽�씤 而щ읆留� �솕硫댁뿉 洹몃젮以��떎.
-                        if (Rel_issueTableConfig.display) {
+                        if (issueTableConfig.display) {
                             //  �뀒�씠釉붿쓽 而щ읆�쓣 留뚮뱾�뼱以��떎.
-                            $scope.fn.setRelTableColumn(Rel_issueTableConfig);
+                            $scope.fn.setRelTableColumn(issueTableConfig);
                         }
                     });
                 }
@@ -557,11 +574,11 @@
                 //  �븯�쐞 �씠�뒋 �뀒�씠釉� �꽕�젙
                 function makeDownTableConfigs() {
                     $scope.vm.downTableConfigs = [];
-                    $scope.vm.downTableConfigs.push($tableProvider.config()
+                    $scope.vm.downTableConfigs.push($downProvider.config()
                         .setDType("checkbox")
                         .setHWidth("width-20-p")
                         .setDAlign("text-center"))
-                    $scope.vm.downTableConfigs.push($tableProvider.config()
+                    $scope.vm.downTableConfigs.push($downProvider.config()
                         .setHName("issue.downIssueTitle")
                         .setDType("renderer")
                         .setDAlign("text-center")
@@ -569,7 +586,7 @@
                         .setHSort(false)
                         .setDRenderer("ISSUE_DOWN_MOVE"))
                     /*if($scope.vm.viewer.modifyPermissionCheck){
-                        $scope.vm.downTableConfigs.push($tableProvider.config()
+                        $scope.vm.downTableConfigs.push($downProvider.config()
                             .setHName("issue.relationIssueDelete")
                             .setDType("renderer")
                             .setDAlign("text-center")
@@ -579,11 +596,11 @@
                             .setDAlign("text-center"))
                     }*/
 
-                    angular.forEach($scope.vm.downTableConfigs, function (Down_issueTableConfig) {
+                    angular.forEach($scope.vm.downTableConfigs, function (issueTableConfig) {
                         //  �몴�떆 ���긽�씤 而щ읆留� �솕硫댁뿉 洹몃젮以��떎.
-                        if (Down_issueTableConfig.display) {
+                        if (issueTableConfig.display) {
                             //  �뀒�씠釉붿쓽 而щ읆�쓣 留뚮뱾�뼱以��떎.
-                            $scope.fn.setDownTableColumn(Down_issueTableConfig);
+                            $scope.fn.setDownTableColumn(issueTableConfig);
                         }
                     });
                 }
@@ -596,7 +613,7 @@
                     }
                     var issueTableConfigs = issueTableConfigVo.issueTableConfigs;
 
-                    //  �뿰愿� �뒋 紐⑸줉 �뀒�씠釉� �꽕�젙 媛믪쓣 媛��졇���꽌 �쟻�슜�븳�떎.
+                    //  �뿰愿� �씠�뒋 紐⑸줉 �뀒�씠釉� �꽕�젙 媛믪쓣 媛��졇���꽌 �쟻�슜�븳�떎.
                     if ($rootScope.isDefined(issueTableConfigs)) {
                         //  �씠�뒋 �뀒�씠釉� �꽕�젙 �젙蹂대�� ���옣 �븳�떎.
 
@@ -607,34 +624,34 @@
                         });
 
                         $scope.vm.relTableConfigs = [];
-    /*                  $scope.vm.relTableConfigs.push($tableProvider.config()
+    /*                  $scope.vm.relTableConfigs.push($relProvider.config()
                            .setHName("issue.relationIssueType")
                            .setDType("renderer")
                            .setDAlign("text-center")
                            .setHWidth("width-30-p bold")
                            .setHSort(false)
                            .setDRenderer("ISSUE_RELATION_TYPE"))
-                        $scope.vm.relTableConfigs.push($tableProvider.config()
+                        $scope.vm.relTableConfigs.push($relProvider.config()
                            .setHName("issue.relationIssueTitle")
                            .setDType("renderer")
                            .setDAlign("text-center")
                            .setHWidth("width-60-p bold")
                            .setHSort(false)
                            .setDRenderer("ISSUE_RELATION_MOVE"))*/
-                        $scope.vm.relTableConfigs.push($tableProvider.config()
+                        $scope.vm.relTableConfigs.push($relProvider.config()
                             .setDType("checkbox")
                             .setHWidth("width-20-p")
                             .setDAlign("text-center"))
-                        angular.forEach($scope.vm.issueRelTableConfigs, function (Rel_issueTableConfig) {
+                        angular.forEach($scope.vm.issueRelTableConfigs, function (issueTableConfig) {
                             //  �몴�떆 ���긽�씤 而щ읆留� �솕硫댁뿉 洹몃젮以��떎.
-                            if (Rel_issueTableConfig.display) {
+                            if (issueTableConfig.display) {
                                 //  �뀒�씠釉붿쓽 而щ읆�쓣 留뚮뱾�뼱以��떎.
-                                $scope.fn.setRelTableColumn(Rel_issueTableConfig);
+                                $scope.fn.setRelTableColumn(issueTableConfig);
 
                             }
                         });
                         /*if($scope.vm.viewer.modifyPermissionCheck) {
-                            $scope.vm.relTableConfigs.push($tableProvider.config()
+                            $scope.vm.relTableConfigs.push($relProvider.config()
                                 .setHName("issue.relationIssueDelete")
                                 .setDType("renderer")
                                 .setHWidth("width-40-p bold")
@@ -653,7 +670,7 @@
                     if (issueTableConfigVo == null) return;
                     var issueTableConfigs = issueTableConfigVo.issueTableConfigs;
 
-                    //  �뿰愿� �뒋 紐⑸줉 �뀒�씠釉� �꽕�젙 媛믪쓣 媛��졇���꽌 �쟻�슜�븳�떎.
+                    //  �뿰愿� �씠�뒋 紐⑸줉 �뀒�씠釉� �꽕�젙 媛믪쓣 媛��졇���꽌 �쟻�슜�븳�떎.
                     if ($rootScope.isDefined(issueTableConfigs)) {
                         //  �씠�뒋 �뀒�씠釉� �꽕�젙 �젙蹂대�� ���옣 �븳�떎.
                         $scope.vm.issueDownTableConfigs = [];
@@ -662,26 +679,26 @@
                             return a.position < b.position ? -1 : a.position > b.position ? 1 : 0;
                         });
                         $scope.vm.downTableConfigs = [];
-/*                      $scope.vm.downTableConfigs.push($tableProvider.config()
+/*                      $scope.vm.downTableConfigs.push($downProvider.config()
                             .setHName("issue.downIssueTitle")
                             .setDType("renderer")
                             .setDAlign("text-center")
                             .setHWidth("width-60-p bold")
                             .setHSort(false)
                             .setDRenderer("ISSUE_DOWN_MOVE"))*/
-                        $scope.vm.downTableConfigs.push($tableProvider.config()
+                        $scope.vm.downTableConfigs.push($downProvider.config()
                             .setDType("checkbox")
                             .setHWidth("width-20-p")
                             .setDAlign("text-center"))
-                        angular.forEach($scope.vm.issueDownTableConfigs, function (Down_issueTableConfig) {
+                        angular.forEach($scope.vm.issueDownTableConfigs, function (issueTableConfig) {
                             //  �몴�떆 ���긽�씤 而щ읆留� �솕硫댁뿉 洹몃젮以��떎.
-                            if (Down_issueTableConfig.display) {
+                            if (issueTableConfig.display) {
                                 //  �뀒�씠釉붿쓽 而щ읆�쓣 留뚮뱾�뼱以��떎.
-                                $scope.fn.setDownTableColumn(Down_issueTableConfig);
+                                $scope.fn.setDownTableColumn(issueTableConfig);
                             }
                         });
                         /*if($scope.vm.viewer.modifyPermissionCheck) {
-                            $scope.vm.downTableConfigs.push($tableProvider.config()
+                            $scope.vm.downTableConfigs.push($downProvider.config()
                                 .setHName("issue.relationIssueDelete")
                                 .setDType("renderer")
                                 .setHWidth("width-40-p bold")
@@ -1023,6 +1040,8 @@
                                 $scope.vm.viewer.startDate = result.data.data.startDate == null ? "common.unspecified" : result.data.data.startDate; // 誘몄��젙
                                 $scope.vm.viewer.completeDate = result.data.data.completeDate == null ? "common.unspecified" : result.data.data.completeDate; // 誘몄��젙
 
+                                $scope.vm.rangeDate = result.data.data.startDate + "~" + result.data.data.completeDate
+
                                 //  �씠�뒋 �쑀�삎�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂대�� �엯�젰 �뤌�뿉�꽌 �궗�슜�븷 �닔 �엳寃� 媛�怨듯븳�떎.
                                 $scope.fn.setFormByIssueTypeCustomFields(result.data.data.issueTypeCustomFieldVos);
                                 //  �씠�뒋�뿉�꽌 �궗�슜�옄媛� �꽑�깮�븳 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 �엯�젰 �뤌�뿉 �뀑�똿�븳�떎.
@@ -1339,6 +1358,21 @@
                     });
                 }
 
+                // �씪諛� 硫붿씪 諛쒖넚 (�궗�슜�옄 吏곸젒 �옉�꽦)
+                function sendCommonMail() {
+                    $uibModal.open({
+                        templateUrl : 'views/issue/issueCommonSendMail.html',
+                        size : "md",
+                        controller : 'issueCommonSendMailController',
+                        backdrop : 'static',
+                        resolve : {
+                            parameter : {
+                                issueId : $scope.vm.viewer.id,
+                            }
+                        }
+                    });
+                }
+
                 function getParametersAll() {
                     var params = $scope.fn.getParameters(null, $scope.vm.viewer.issueCompanyVos);
                     params = $scope.fn.getParameters(params, $scope.vm.viewer.issueIspVos);
diff --git a/src/main/webapp/scripts/app/issue/issueList.controller.js b/src/main/webapp/scripts/app/issue/issueList.controller.js
index 8f38e3d..5915af6 100644
--- a/src/main/webapp/scripts/app/issue/issueList.controller.js
+++ b/src/main/webapp/scripts/app/issue/issueList.controller.js
@@ -336,7 +336,7 @@
                         case "ISSUE_TITLE" :   //  �씠�뒋 �젣紐�
                             $scope.vm.tableConfigs.push($tableProvider.config()
                                 .setHName("issue.issueTitle")
-                                .setDName("issueNumber") /* todo �씠嫄� ���씠��濡� 蹂�寃쏀빐�빞�븯�뒗�뜲*/
+                                .setDName("issueTitle")
                                 .setDType("renderer")
                                 .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
@@ -373,7 +373,7 @@
                         case "ASSIGNEE_TEAM" :   //  �떞�떦遺��꽌
                             $scope.vm.tableConfigs.push($tableProvider.config()
                                 .setHName("common.assigneeTeam")
-                                .setDName("departmentName") /* todo 泥댄겕*/
+                                .setDName("departmentName")
                                 .setDType("renderer")
                                 .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
@@ -391,7 +391,6 @@
                         case "PERIOD" : //  湲곌컙
                             $scope.vm.tableConfigs.push($tableProvider.config()
                                 .setHName("common.period")
-                                .setDName("startDate")
                                 .setDType("renderer")
                                 .setHWidth("bold " + issueTableConfig.width)
                                 .setDAlign("text-center")
diff --git a/src/main/webapp/scripts/app/issue/issueSendMailPartners.controller.js b/src/main/webapp/scripts/app/issue/issueSendMailPartners.controller.js
index 9e8d0c7..32b7a6e 100644
--- a/src/main/webapp/scripts/app/issue/issueSendMailPartners.controller.js
+++ b/src/main/webapp/scripts/app/issue/issueSendMailPartners.controller.js
@@ -17,7 +17,6 @@
                     formCheck : formCheck,   //  �뤌 泥댄겕
                     onChangeEmailTemplate : onChangeEmailTemplate, // �씠硫붿씪 �뀥�뵆由� �꽑�깮�떆 �떎�뻾
                     getEmailTemplateList : getEmailTemplateList // �씠硫붿씪 �뀥�뵆由� 紐⑸줉 媛��졇�삤湲�
-                    // showEmailTemplate : showEmailTemplate,
                 };
 
                 $scope.vm = {
@@ -26,6 +25,7 @@
                     hostingVos : parameter.hostingVos,
                     partners : parameter.partnersAll.slice(),
                     html : "",
+                    tab : "SEND_TEMPLATE",
                     form : {
                         id : parameter.issueId,  //  �씠�뒋 踰덊샇
                         projects : [{ id : parameter.projectId}],  //  �봽濡쒖젥�듃
@@ -59,7 +59,8 @@
                             $scope.vm.html = result.data.data.template;
                         }
                         else {
-                            SweetAlert.error($filter("translate")("issue.failedIssueMail"), result.data.message.message); // "�씠�뒋 硫붿씪 諛쒖넚 �떎�뙣"
+                            $scope.vm.html = "";
+                            //SweetAlert.warning($filter("translate")("issue.selectedPartnersMail"),$filter("translate")("issue.selectedPartnersTemplate")); // option �꽑�깮 寃쎄퀬
                         }
 
                         $rootScope.spinner = false;
@@ -85,14 +86,13 @@
 
                     $scope.vm.emailTemplateId = 1;
                     $scope.vm.emailTitle = "";
-                    $scope.vm.emailTemplates.forEach(function (emailTemplate) {
-                        /*if (emailTemplate.id === $scope.vm.emailTemplateId) {
-                            $scope.vm.emailTitle = emailTemplate.title;
-                        }*/
-                        if (emailTemplate.templateType === $scope.vm.emailTemplateType) {
-                            $scope.vm.emailTitle = emailTemplate.title;
-                        }
-                    })
+                    if ($rootScope.isDefined($scope.vm.emailTemplateType)) {
+                        $scope.vm.emailTemplates.forEach(function (emailTemplate) {
+                            if (emailTemplate.templateType === $scope.vm.emailTemplateType) {
+                                $scope.vm.emailTitle = emailTemplate.title;
+                            }
+                        })
+                    }
                 }
 
 
@@ -182,6 +182,7 @@
                 }
 
                 $scope.fn.getEmailTemplateList();
+
             }]);
 
     });
diff --git a/src/main/webapp/scripts/app/project/projectModify.controller.js b/src/main/webapp/scripts/app/project/projectModify.controller.js
index 722b8ba..0abec6f 100644
--- a/src/main/webapp/scripts/app/project/projectModify.controller.js
+++ b/src/main/webapp/scripts/app/project/projectModify.controller.js
@@ -182,7 +182,7 @@
                             if (angular.isDefined(result.data.data)) {
                                 $scope.vm.form.name = result.data.data.name;
                                 $scope.vm.form.status = result.data.data.status;
-                                if (result.data.data.startDate == "" && result.data.data.endDate == "") {
+                                if (result.data.data.startDate == "" && result.data.data.endDate == "" || result.data.data.startDate == "null" && result.data.data.endDate == "null") {
                                     $scope.vm.form.startEndDateRange = ""
                                 } else {
                                     $scope.vm.form.startEndDateRange = result.data.data.startDate + " ~ " + result.data.data.endDate;
diff --git a/src/main/webapp/scripts/components/auth/auth.interceptor.js b/src/main/webapp/scripts/components/auth/auth.interceptor.js
index 632510c..26869c0 100644
--- a/src/main/webapp/scripts/components/auth/auth.interceptor.js
+++ b/src/main/webapp/scripts/components/auth/auth.interceptor.js
@@ -11,6 +11,16 @@
                         $tableProvider.setOrderByColumn();
                         $tableProvider.reverse = false;
 
+                        //  �뿰愿� �뀒�씠釉� 而щ읆 �젙�젹 珥덇린�솕
+                        var $relProvider = $injector.get('$relProvider');
+                        $relProvider.setOrderByColumn();
+                        $relProvider.reverse = false;
+
+                        //  �븯�쐞 �뀒�씠釉� 而щ읆 �젙�젹 珥덇린�솕
+                        var $downProvider = $injector.get('$downProvider');
+                        $downProvider.setOrderByColumn();
+                        $downProvider.reverse = false;
+
                         if (angular.isDefined(config.data)) {
                             $log.debug(config.url + " : ", config.data);
                         }
diff --git a/src/main/webapp/scripts/components/issue/issue.service.js b/src/main/webapp/scripts/components/issue/issue.service.js
index 8290ed9..d9f9086 100644
--- a/src/main/webapp/scripts/components/issue/issue.service.js
+++ b/src/main/webapp/scripts/components/issue/issue.service.js
@@ -144,6 +144,12 @@
                     $log.debug("�씠�뒋 �씠硫붿씪 諛쒖넚 寃곌낵 : ", response);
                     return response;
                 });
+            },
+            sendCommonEmail : function (conditions) {
+                return $http.post("issue/sendCommonEmail", conditions).then(function (response) {
+                    $log.debug("�씠�뒋 �씠硫붿씪 諛쒖넚 寃곌낵 : ", response);
+                    return response;
+                });
             }
         }
     }
diff --git a/src/main/webapp/scripts/config.js b/src/main/webapp/scripts/config.js
index be59c2b..4eb1604 100644
--- a/src/main/webapp/scripts/config.js
+++ b/src/main/webapp/scripts/config.js
@@ -35,7 +35,7 @@
                 //  濡쒓렇 �젣�뼱
                 $logProvider.debugEnabled(true);
             })
-            .run(function ($rootScope, $state, $sce, $log, $injector, $translate, $tableProvider, Principal, Auth, Language, SweetAlert, $filter, Workspace, $resourceProvider, User, constants, Project) {
+            .run(function ($rootScope, $state, $sce, $log, $injector, $translate, $tableProvider, $relProvider, $downProvider, Principal, Auth, Language, SweetAlert, $filter, Workspace, $resourceProvider, User, constants, Project) {
 
                 $rootScope.$state = $state;
                 //  html �깭洹� �쎒 蹂댁븞 �쟻�슜�븯�뿬 諛붿씤�뵫.
@@ -117,6 +117,12 @@
 
                 //  �뀒�씠釉� �꽕�젙 諛� 湲곕뒫�쓣 愿�由ы븯�뒗 �꽌鍮꾩뒪
                 $rootScope.$tableProvider = $tableProvider;
+
+                //  �뿰愿��뀒�씠釉� �꽕�젙 諛� 湲곕뒫 愿�由ы븯�뒗 �꽌鍮꾩뒪
+                $rootScope.$relProvider = $relProvider;
+
+                //  �븯�쐞�뀒�씠釉� �꽕�젙 諛� 湲곕뒫 愿�由ы븯�뒗 �꽌鍮꾩뒪
+                $rootScope.$downProvider = $downProvider;
 
                 //  html tag convert - �떊猶고븷 �닔 �엳�뒗 �엯�젰 媛믪씪 �븣留� �궗�슜, �궗�슜�옄媛� �벑濡앺븯�뒗 媛믪뿉�뒗 �궗�슜 湲덉�
                 $rootScope.trustAsHtml = function (string) {
@@ -568,6 +574,9 @@
                     //  table orderBy column init setting
                     $tableProvider.setOrderByColumn();
                     $tableProvider.reverse = false;
+
+                    $relProvider.setOrderByColumn();
+                    $relProvider.reverse = false;
                     //  �씠�뒋 紐⑸줉->�긽�꽭�솕硫댁뿉�꽌 留덉�留됱쑝濡� �젒洹쇳븳 �씠�뒋 �븘�씠�뵒 - �씪�슦�듃 �깉�븣留덈떎 珥덇린�솕
                     $rootScope.currentDetailIssueId = null;
                     // �궗�슜�옄 �젙蹂대�� 媛��졇�삩�떎.
diff --git a/src/main/webapp/scripts/main.js b/src/main/webapp/scripts/main.js
index 20165a1..7e5677d 100644
--- a/src/main/webapp/scripts/main.js
+++ b/src/main/webapp/scripts/main.js
@@ -56,13 +56,19 @@
         'commonController' : 'app/common/common.controller', //  怨듯넻 而⑦듃濡ㅻ윭
         'tableProvider' : '../custom_components/js-table/table.provider', //  �뀒�씠釉� �냽�꽦 媛믪쓣 愿�由ы븳�떎.
         'treeProvider' : '../custom_components/js-tree/tree.provider', //   �듃由� �냽�꽦 媛믪쓣 愿�由ы븳�떎.
+        'relProvider' : '../custom_components/js-rel/rel.provider', //   �뿰愿� �뀒�씠釉� �냽�꽦 媛믪쓣 愿�由ы븳�떎.
+        'downProvider' : '../custom_components/js-down/down.provider', //   �븯�쐞 �뀒�씠釉� �냽�꽦 媛믪쓣 愿�由ы븳�떎.
         'resourceProvider' : 'components/utils/resource.provider',   //  怨듯넻�쟻�쑝濡� �꽌踰� json �쟾�넚�뿉 �궗�슜
         'lodash' : '../bower_components/lodash/lodash.min', //  硫��떚 ���젆�듃, auto complete 而댄룷�꼳�듃�뱾�뿉�꽌 �궗�슜
         'angularDropMultiSelect' : '../custom_components/angular-multi-select/angularjs-dropdown-multiselect',  //  硫��떚 ���젆�듃 而댄룷�꼳�듃
         'jsTable' : '../custom_components/js-table/js-table.directive',   //  紐⑸줉 �솕硫댁뿉�꽌 �궗�슜�릺�뒗 �뀒�씠釉붿쓣 �샇異쒗븳�떎.
         'jsTree' : '../custom_components/js-tree/js-tree.directive',   //  紐⑸줉 �솕硫댁뿉�꽌 �궗�슜�릺�뒗 �뀒�씠釉�(�듃由ш뎄議�)�쓣 �샇異쒗븳�떎.
+        'jsRel' : '../custom_components/js-rel/js-rel.directive',   //  �씠�뒋�긽�꽭 �솕硫댁뿉�꽌 �궗�슜�릺�뒗 �뿰愿� �뀒�씠釉붿쓣 �샇異쒗븳�떎.
+        'jsDown' : '../custom_components/js-down/js-down.directive',   //  �씠�뒋�긽�꽭 �솕硫댁뿉�꽌 �궗�슜�릺�뒗 �븯�쐞 �뀒�씠釉붿쓣 �샇異쒗븳�떎.
         'tableColumnGenerator' : '../custom_components/js-table/tableColumnGenerator.directive', //  �뀒�씠釉� �옖�뜑�윭瑜� �떞�떦�븳�떎.
-        'treeColumnGenerator' : '../custom_components/js-tree/treeColumnGenerator.directive', //  �뀒�씠釉� �옖�뜑�윭瑜� �떞�떦�븳�떎.
+        'treeColumnGenerator' : '../custom_components/js-tree/treeColumnGenerator.directive', //  �씠�뒋由ъ뒪�듃 �듃由� �옖�뜑�윭瑜� �떞�떦�븳�떎.
+        'relColumnGenerator' : '../custom_components/js-rel/relColumnGenerator.directive', //  �씠�뒋�긽�꽭 �뿰愿� �옖�뜑�윭瑜� �떞�떦�븳�떎.
+        'downColumnGenerator' : '../custom_components/js-down/downColumnGenerator.directive', //  �씠�뒋�긽�꽭 �븯�쐞 �옖�뜑�윭瑜� �떞�떦�븳�떎.
         'jsAutoCompleteMulti' : '../custom_components/js-autocomplete-multi/js-autocomplete-multi', //  �떎以� �꽑�깮�씠 媛��뒫�븳 autoComplete 而댄룷�꼳�듃
         'jsInputAutoComplete' : '../custom_components/js-input-autocomplete/js-input-autocomplete',   //  input 諛뺤뒪�뿉 autoComplete 湲곕뒫�씠 遺숈� 而댄룷�꼳�듃
         'jsAutoCompleteSingle' : '../custom_components/js-autocomplete-single/js-autocomplete-single',   //  input 諛뺤뒪�뿉 �븳媛쒖쓽 ���긽留� �꽑�깮 媛��뒫�븷�닔 �엳�뒗 autoComplete 湲곕뒫�씠 遺숈� 而댄룷�꼳�듃
@@ -191,7 +197,8 @@
         'issueAddTableConfigController' : 'app/issue/issueAddTableConfig.controller',   //  �씠�뒋 �뀒�씠釉� �꽕�젙 而⑦듃濡ㅻ윭
         'issueAddRelationTableConfigController' : 'app/issue/issueAddRelationTableConfig.controller',   //  �씠�뒋 �뀒�씠釉� �꽕�젙 而⑦듃濡ㅻ윭
         'issueAddDownTableConfigController' : 'app/issue/issueAddDownTableConfig.controller',   //  �씠�뒋 �뀒�씠釉� �꽕�젙 而⑦듃濡ㅻ윭
-        'issueSendMailPartnersController' : 'app/issue/issueSendMailPartners.controller',   //  �씠�뒋 �씠硫붿씪 諛쒖넚 而⑦듃濡ㅻ윭
+        'issueSendMailPartnersController' : 'app/issue/issueSendMailPartners.controller',   //  �뾽泥� �씠硫붿씪 諛쒖넚 而⑦듃濡ㅻ윭
+        'issueCommonSendMailController' : 'app/issue/issueCommonSendMail.controller',   //  �씪諛� �씠硫붿씪 諛쒖넚 而⑦듃濡ㅻ윭
         'issueVersionViewController' : 'app/issue/issueVersionView.controller', //  �씠�뒋 踰꾩쟾 �솗�씤 而⑦듃濡ㅻ윭
         'issueReservationController' : 'app/issue/issueReservation.controller', //  �씠�뒋 諛쒖깮 �삁�빟 而⑦듃濡ㅻ윭
         'issueModifyUserController' : 'app/issue/issueModifyUser.controller', // �씠�뒋 �떞�떦�옄 而⑦듃濡ㅻ윭
@@ -409,10 +416,22 @@
         'jsTree' : {
             deps : ['app']
         },
+        'jsRel' : {
+            deps : ['app']
+        },
+        'jsDown' : {
+            deps : ['app']
+        },
         'tableColumnGenerator' : {
             deps : ['app']
         },
         'treeColumnGenerator' : {
+            deps : ['app']
+        },
+        'relColumnGenerator' : {
+            deps : ['app']
+        },
+        'downColumnGenerator' : {
             deps : ['app']
         },
         'ngStomp' : {
@@ -506,6 +525,7 @@
     'config',   //  angularJs �꽕�젙 - route �씠�룞 愿��젴, �씠�룞�떆 珥덇린�솕 濡쒖쭅�씠 �뱾�뼱�엳�떎. - 吏곸젒 濡쒕뱶
     'constants',
     'commonController',
+    'issueCommonSendMailController',
     'autoCompleteController',
     'userInviteController',
     'issueAddController',   //  �씠�뒋 留뚮뱾湲곗뿉�꽌 �궗�슜
@@ -534,6 +554,8 @@
     'resourceProvider',
     'tableProvider',
     'treeProvider',
+    'relProvider',
+    'downProvider',
     'permissionService',
     'authService',
     'userInviteService',
diff --git a/src/main/webapp/views/issue/issueCommonSendMail.html b/src/main/webapp/views/issue/issueCommonSendMail.html
new file mode 100644
index 0000000..c2f3ab4
--- /dev/null
+++ b/src/main/webapp/views/issue/issueCommonSendMail.html
@@ -0,0 +1,69 @@
+<div class="formModal">
+    <div class="modal-header faded smaller">
+        <div class="modal-title">
+            <strong translate="issue.CommonSendIssueMail">�씪諛� 硫붿씪 諛쒖넚</strong>
+        </div>
+        <button aria-label="Close" class="close" type="button" ng-click="fn.cancel()">
+            <span aria-hidden="true"> &times;</span>
+        </button>
+    </div>
+
+    <div class="modal-body">
+        <form role="form" name="issueSendForm">
+            <button type="button" class="btn btn-secondary mr-3 float-right mb-1" ng-click="fn.addInput()">
+                <span translate="common.add">異붽�</span>
+            </button>
+            <div class="form-group">
+                <label class="issue-label mt-2"><span translate="common.toPerson">諛쏅뒗 �궗�엺</span>&nbsp;<code class="highlighter-rouge">*</code></label>
+                <div class="input-group" ng-repeat="i in vm.form.inputs">
+                    <input type="text"
+                           name="email"
+                           class="form-control mt-1"
+                           kr-input
+                           ng-model="vm.form.emails[$index]"
+                           ng-pattern="/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/"
+                           required
+                           autocomplete="off">
+                    <span class="select3-selection__email__remove" ng-click="fn.removeInput($index)">횞</span>
+                </div>
+                <small translate="common.emailExplain">諛쏅뒗 �궗�엺�쓽 �씠硫붿씪 �삎�떇�쓣 �엯�젰�븯�뀛�빞 �빀�땲�떎.</small>
+                <div ng-if="issueSendForm.email.$error.pattern" class="help-block form-text text-danger"
+                     translate="common.invalidEmailFormat">�씠硫붿씪 �삎�떇�씠 留욎� �븡�뒿�땲�떎.
+                </div>
+            </div>
+
+
+            <label class="issue-label"><span translate="common.title">�젣紐�</span></label>
+            <input id="title"
+                   type="text"
+                   name="title"
+                   class="form-control mt-1"
+                   kr-input
+                   ng-model="vm.form.title"
+                   autocomplete="off">
+
+            <div class="form-group mb10 mt-10">
+                <label class="issue-label"><span translate="common.content">�궡�슜</span></label>
+                <summernote
+                        class="summernote mt-1"
+                        lang="ko-KR"
+                        summer-note-auto-focus
+                        ng-model="vm.form.description"
+                        data-editor="vm.summerNote.editor"
+                        data-editable="vm.summerNote.editable"
+                        on-image-upload="fn.imageUpload(files)"
+                        target=".note-editable"></summernote>
+            </div>
+            <small class="mt-1" translate="common.sendToPerson">�떎瑜� �궗�슜�옄�뿉寃� 硫붿씪�쓣 蹂대깄�땲�떎.</small>
+        </form>
+    </div>
+
+    <div class="modal-footer buttons-on-right">
+        <button type="button" class="btn btn-md btn-grey" ng-click="fn.cancel()"><span
+                translate="common.cancel">痍⑥냼</span></button>
+        <button type="button" class="btn btn-md btn-primary bold"
+                ng-disabled="fn.formCheck(issueSendForm.$invalid)"
+                ng-click="fn.formSubmit()"><span translate="issue.sendMail">�씠硫붿씪 諛쒖넚</span>
+        </button>
+    </div>
+</div>
diff --git a/src/main/webapp/views/issue/issueDetail.html b/src/main/webapp/views/issue/issueDetail.html
index 2bbc36d..3d7c70e 100644
--- a/src/main/webapp/views/issue/issueDetail.html
+++ b/src/main/webapp/views/issue/issueDetail.html
@@ -102,7 +102,8 @@
                 </div>
             </span>
             <a class="show-ticket-info cursor">
-                <i class="os-icon os-icon-email-forward mr-20" ng-if="vm.viewer.modifyPermissionCheck" ng-click="fn.sendMailAll()" data-toggle="tooltip" data-placement="right" title="�떎瑜� �궗�슜�옄�뿉寃� �씠�뒋 蹂대궡湲�"></i>
+                <i class="os-icon os-icon-email-2-at2 mr-20" ng-if="vm.viewer.modifyPermissionCheck" ng-click="fn.sendCommonMail()" data-toggle="tooltip" data-placement="right" title="吏곸젒 �옉�꽦�븳 硫붿씪 蹂대궡湲�"></i>
+                <i class="os-icon os-icon-email-forward mr-20" ng-if="vm.viewer.modifyPermissionCheck" ng-click="fn.sendMailAll()" data-toggle="tooltip" data-placement="right" title="�뾽泥� 硫붿씪 蹂대궡湲�"></i>
                 <i class="os-icon os-icon-airplay mr-20" ng-click="fn.versionView(vm.viewer.id)" data-toggle="tooltip" data-placement="right" title="�씠�뒋 蹂�寃� �씠�젰 �긽�꽭 蹂닿린"></i>
                 <i class="os-icon os-icon-calendar-time mr-20" ng-if="vm.viewer.modifyPermissionCheck"
                    ng-click="fn.reservation(vm.viewer.id)" data-toggle="tooltip" data-placement="right" title="�씠�뒋 諛쒖깮 �삁�빟 �븯湲�"></i>
@@ -524,8 +525,8 @@
 
                 <!--  �뿰愿� �씠�뒋 �뀒�씠釉� -->
                 <div class="mt-10 issue-detail-word-break width-100">
-                    <js-table data="vm.viewer.issueRelationVos" table-configs="vm.relTableConfigs"
-                              event="relTableEvent" detail-view="true" hide-header="false" use-sort="true"></js-table>
+                    <js-rel data="vm.viewer.issueRelationVos" table-configs="vm.relTableConfigs"
+                              event="relTableEvent" detail-view="true" hide-header="false" use-sort="true"></js-rel>
 
                     <div class="row" ng-if="vm.viewer.modifyPermissionCheck">
                         <div class="col-sm-4">
@@ -625,8 +626,8 @@
 
                 <!-- �븯�쐞 �씠�뒋 �뀒�씠釉� -->
                 <div class="mt-10 issue-detail-word-break width-100">
-                    <js-table data="vm.viewer.issueDownVos" table-configs="vm.downTableConfigs"
-                              event="downTableEvent" detail-view="true" hide-header="false" use-sort="true"></js-table>
+                    <js-down data="vm.viewer.issueDownVos" table-configs="vm.downTableConfigs"
+                              event="downTableEvent" detail-view="true" hide-header="false" use-sort="true"></js-down>
 
                     <div class="row" ng-if="vm.viewer.modifyPermissionCheck">
                         <div class="col-sm-6">
diff --git a/src/main/webapp/views/issue/issueSendMailPartners.html b/src/main/webapp/views/issue/issueSendMailPartners.html
index 6fdc84d..40eb4af 100644
--- a/src/main/webapp/views/issue/issueSendMailPartners.html
+++ b/src/main/webapp/views/issue/issueSendMailPartners.html
@@ -1,7 +1,7 @@
 <div class="formModal">
     <div class="modal-header faded smaller">
         <div class="modal-title">
-            <strong translate="issue.selectSendIssueMail">�씠�뒋 硫붿씪 諛쒖넚 ���긽�옄 �꽑�깮</strong>
+            <strong translate="issue.selectSendIssueMail">�씠�뒋 硫붿씪 諛쒖넚</strong>
         </div>
         <button aria-label="Close" class="close" type="button" ng-click="fn.cancel()">
             <span aria-hidden="true"> &times;</span>
@@ -20,6 +20,9 @@
                     </span>
                 </div>
 
+                <label class="issue-label mt-10">
+                    <span translate="issue.selectPartners">�뾽泥� �꽑�깮</span>&nbsp;<code class="highlighter-rouge">*</code>
+                </label>
                 <js-input-autocomplete data-input-name="mailUsers"
                                        owl-auto-focus
                                        target=".auto-complete-i0nput"
@@ -29,38 +32,50 @@
                                        page="vm.autoCompletePage.user.page"
                                        total-page="vm.autoCompletePage.user.totalPage"
                                        source="fn.getMailTargetAll(vm.form.mailUsers)"
-                                       translation-texts="{ empty : 'common.emptyUser'}"
+                                       translation-texts="{ empty : 'common.emptyCompanyPartners'}"
                                        input-disabled="vm.form.mailUsers == null"
                                        extra-settings="{ displayProp : 'name' , idProp : 'id', imageable : false, imagePathProp : 'profile',
                                        type : 'partner', maxlength : 100, autoResize : true, stopRemoveBodyEvent : true }"></js-input-autocomplete>
-                    <div class="Template-area mt-20">
-                        <div class="form-group mb10">
-                            <label for="emailTemplateForm" class="issue-label">
-                                <span translate="common.emailTemplate">�씠硫붿씪 �뀥�뵆由�</span>
-                            </label>
-                            <select id="emailTemplateForm"
-                                    name="emailTemplate"
-                                    class="form-control input-sm issue-select-label"
-                                    ng-model="vm.emailTemplateType"
-                                    ng-change="fn.onChangeEmailTemplate()"
-                                    required>
-                                <option ng-repeat="emailTemplate in vm.emailTemplates"
-                                        value="{{emailTemplate.templateType}}"
-                                        translate="{{emailTemplate.title}}">
-                                </option>
-                            </select>
-                        </div>
-                    </div>
-                <summernote
-                        class="summernote"
-                        lang="ko-KR"
-                        config="vm.options"
-                        ng-model="vm.html"
-                        target=".note-editable"></summernote>
+
+                <div class="form-group mb10 mt-20">
+                    <label for="emailTemplateForm" class="issue-label">
+                        <span translate="issue.partners">�뾽泥� �씠硫붿씪</span>&nbsp;<code class="highlighter-rouge">*</code>
+                    </label>
+                    <select id="emailTemplateForm"
+                            name="emailTemplate"
+                            class="form-control input-sm issue-select-label"
+                            ng-model="vm.emailTemplateType"
+                            ng-change="fn.onChangeEmailTemplate()"
+                            required>
+                        <option value="" ng-selected="true">�꽑�깮�븯�꽭�슂</option>
+                        <option ng-repeat="emailTemplate in vm.emailTemplates"
+                                value="{{emailTemplate.templateType}}"
+                                translate="{{emailTemplate.title}}">
+                        </option>
+                    </select>
+                </div>
+
+                <label class="issue-label"><span translate="common.title">�젣紐�</span></label>
+                <input id="title"
+                       type="text"
+                       name="title"
+                       class="form-control"
+                       kr-input
+                       ng-model="vm.emailTitle"
+                       autocomplete="off">
+
+                <div class="form-group mb10 mt-10">
+                    <label class="issue-label"><span translate="common.content">�궡�슜</span></label>
+                    <summernote
+                            class="summernote"
+                            lang="ko-KR"
+                            config="vm.options"
+                            ng-model="vm.html"
+                            target=".note-editable"></summernote>
+                </div>
             </div>
         </form>
     </div>
-
 
     <div class="modal-footer buttons-on-right">
         <button type="button" class="btn btn-md btn-grey" ng-click="fn.cancel()"><span

--
Gitblit v1.8.0