From 5fb1952ae91f1a739803247266e87dbd15ea1f27 Mon Sep 17 00:00:00 2001
From: 이민희 <mhlee@maprex.co.kr>
Date: 수, 01 12월 2021 13:23:09 +0900
Subject: [PATCH] 충돌 수정

---
 src/main/webapp/views/issue/issueSendMail.html                                       |  121 +++++
 src/main/java/kr/wisestone/owl/web/controller/ApiController.java                     |   40 +
 src/main/webapp/scripts/app/issue/issueSendMail.controller.js                        |   82 +++
 src/main/webapp/scripts/components/utils/issueDetailImagePreview.directive.js        |    2 
 src/main/java/kr/wisestone/owl/service/WorkspaceService.java                         |    2 
 src/main/resources/mybatis/query-template/issue-template.xml                         |   17 
 src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java                     |    4 
 src/main/java/kr/wisestone/owl/service/impl/IssueApiDefaultServiceImpl.java          |   19 
 src/main/java/kr/wisestone/owl/service/impl/WorkspaceServiceImpl.java                |   28 +
 src/main/java/kr/wisestone/owl/domain/Issue.java                                     |   17 
 src/main/resources/migration/V1_11__Alter_Table.sql                                  |    3 
 src/main/java/kr/wisestone/owl/util/CommonUtil.java                                  |   54 ++
 src/main/java/kr/wisestone/owl/web/form/IssueForm.java                               |   23 +
 src/main/java/kr/wisestone/owl/service/IssueCustomFieldValueService.java             |    3 
 src/main/java/kr/wisestone/owl/web/controller/IssueController.java                   |    2 
 src/main/java/kr/wisestone/owl/web/form/CustomFieldValueForm.java                    |   24 +
 src/main/java/kr/wisestone/owl/service/IssueApiDefaultService.java                   |    1 
 src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java             |   32 +
 src/main/webapp/scripts/components/utils/issueSearchFieldKeyViewElement.directive.js |   11 
 src/main/java/kr/wisestone/owl/service/impl/AttachedFileServiceImpl.java             |   29 +
 src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java                    |  161 ++++++-
 src/main/java/kr/wisestone/owl/service/impl/CustomFieldApiOverlapServiceImpl.java    |    8 
 src/main/java/kr/wisestone/owl/service/IssueHistoryService.java                      |    4 
 src/main/java/kr/wisestone/owl/web/resolver/OwlResponseEntityExceptionHandler.java   |    2 
 src/main/java/kr/wisestone/owl/service/impl/ApiTokenServiceImpl.java                 |   33 +
 src/main/webapp/views/issue/issueListNormal.html                                     |   49 -
 src/main/java/kr/wisestone/owl/service/IssueDepartmentService.java                   |    3 
 src/main/webapp/scripts/components/issue/issue.service.js                            |    6 
 src/main/java/kr/wisestone/owl/service/CustomFieldApiOverlapService.java             |    2 
 src/main/java/kr/wisestone/owl/constant/MsgConstants.java                            |    5 
 src/main/java/kr/wisestone/owl/mapper/IssueMapper.java                               |    3 
 src/main/java/kr/wisestone/owl/service/ApiTokenService.java                          |    8 
 src/main/webapp/scripts/app/issue/issueDetail.controller.js                          |   57 +
 src/main/java/kr/wisestone/owl/service/impl/IssueCustomFieldValueServiceImpl.java    |   23 +
 src/main/webapp/scripts/components/utils/autoComplete.controller.js                  |   43 ++
 src/main/java/kr/wisestone/owl/service/AttachedFileService.java                      |    2 
 src/main/webapp/scripts/config.js                                                    |   32 
 src/main/java/kr/wisestone/owl/service/IssueService.java                             |    7 
 src/main/java/kr/wisestone/owl/web/form/IssueApiForm.java                            |  195 +++++++++
 src/main/webapp/scripts/app/issue/issueList.controller.js                            |   36 +
 src/main/java/kr/wisestone/owl/service/impl/IssueDepartmentServiceImpl.java          |   17 
 src/main/webapp/views/issue/issueDetail.html                                         |   19 
 src/main/java/kr/wisestone/owl/web/form/IssueCustomFieldValueForm.java               |   24 +
 43 files changed, 1,076 insertions(+), 177 deletions(-)

diff --git a/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java b/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java
index 20b073d..62056ea 100644
--- a/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java
+++ b/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java
@@ -22,6 +22,7 @@
 import org.springframework.security.core.session.SessionRegistry;
 import org.springframework.security.crypto.factory.PasswordEncoderFactories;
 import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 import org.springframework.security.web.session.ConcurrentSessionFilter;
 import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
 
@@ -143,7 +144,10 @@
                 .antMatchers("/guide/detail").permitAll()
                 .antMatchers("/language/change").permitAll()
                 .antMatchers("/security/*").permitAll()
+                .antMatchers("/api/issue").permitAll()
                 .antMatchers("/**/*").authenticated();
+//        http.addFilter(new CustomAuthenticationFilter());
+//        http.addFilterBefore(new CustomAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
 
 
         http.rememberMe()
diff --git a/src/main/java/kr/wisestone/owl/constant/MsgConstants.java b/src/main/java/kr/wisestone/owl/constant/MsgConstants.java
index b4d81c6..4aff444 100644
--- a/src/main/java/kr/wisestone/owl/constant/MsgConstants.java
+++ b/src/main/java/kr/wisestone/owl/constant/MsgConstants.java
@@ -150,6 +150,7 @@
     public static final String USER_NOT_EQUAL_PASSWORD = "USER_NOT_EQUAL_PASSWORD"; //  鍮꾨�踰덊샇媛� 留욎� �븡�뒿�땲�떎.
     public static final String USER_NOT_EXIST = "USER_NOT_EXIST";   //  �궗�슜�옄媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
     public static final String USER_NOT_AUTHORIZED = "USER_NOT_AUTHORIZED"; //  �궗�슜�옄 �씤利� 沅뚰븳�씠 �뾾�뒿�땲�떎.
+    public static final String ERROR_TOKEN = "USER_NOT_AUTHORIZED_TOKEN"; //  �쑀�슚�븯吏� �븡�� �넗�겙�엯�땲�떎.
     public static final String USER_EXPIRED_PASSWORD = "USER_EXPIRED_PASSWORD"; //  鍮꾨�踰덊샇媛� 留뚮즺�릺�뿀�뒿�땲�떎.
     public static final String USER_RETURN_PASSWORD_NOT_PROVIDER_SOCIAL_JOIN_USER = "USER_RETURN_PASSWORD_NOT_PROVIDER_SOCIAL_JOIN_USER";   //  鍮꾨�踰덊샇 李얘린 湲곕뒫�쓣 �냼�뀥 怨꾩젙 媛��엯 �궗�슜�옄�뒗 �궗�슜�븷 �닔 �뾾�뒿�땲�떎.
     public static final String USER_NOT_USE_ACTIVE_STATUS = "USER_NOT_USE_ACTIVE_STATUS";   //  �궗�슜�옄�뒗 �솢�꽦 �긽�깭媛� �븘�땲硫� 濡쒓렇�씤�븷 �닔 �뾾�뒿�땲�떎.
@@ -227,4 +228,8 @@
     public static final String ISP_NOT_EXIST = "ISP_NOT_EXIST";   // ISP媛� 議댁옱�븯吏� �븡�뒿�땲�떎.
     public static final String ISP_REMOVE_NOT_SELECT = "ISP_REMOVE_NOT_SELECT";   // �궘�젣�븷 ISP媛� �꽑�깮�릺吏� �븡�븯�뒿�땲�떎.
     public static final String PROJECT_NOT_INCLUDE_DEPARTMENT = "PROJECT_NOT_INCLUDE_DEPARTMENT";   // �꽑�깮�븳 遺��꽌 以� �봽濡쒖젥�듃�뿉 李몄뿬�븯怨� �엳吏� �븡�� 遺��꽌媛� �엳�뒿�땲�떎.
+
+    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_USER_ERROR = "API_USER_ERROR";     // api �궗�슜�옄 �삤瑜�
 }
diff --git a/src/main/java/kr/wisestone/owl/domain/Issue.java b/src/main/java/kr/wisestone/owl/domain/Issue.java
index 9482365..119e58f 100644
--- a/src/main/java/kr/wisestone/owl/domain/Issue.java
+++ b/src/main/java/kr/wisestone/owl/domain/Issue.java
@@ -1,5 +1,8 @@
 package kr.wisestone.owl.domain;
 
+import org.hibernate.annotations.DynamicInsert;
+import org.hibernate.annotations.DynamicUpdate;
+
 import javax.persistence.*;
 import java.io.Serializable;
 import java.util.HashSet;
@@ -9,6 +12,8 @@
  * Created by wisestone on 2018-01-03.
  */
 @Entity
+@DynamicInsert
+@DynamicUpdate
 public class Issue extends BaseEntity implements Serializable {
     private static final long serialVersionUID = 1L;
     public static final String WORKSPACE_MANAGER = "WORKSPACE_MANAGER"; //  �뾽臾� 怨듦컙 愿�由ъ옄
@@ -16,6 +21,9 @@
     public static final String REGISTER = "REGISTER";   //  �씠�뒋 �벑濡앹옄
     public static final String ASSIGNEE = "ASSIGNEE";   //  �씠�뒋 �떞�떦�옄
     public static final String DEPARTMENT = "DEPARTMENT";   //  �씠�뒋 �떞�떦遺��꽌
+
+    public static final String IS_API_YES = "Y";
+    public static final String IS_API_NO = "N";
 
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -26,6 +34,7 @@
     private Long issueNumber;
     private String startDate;
     private String completeDate;
+    private String isApi;
 
     @ManyToOne(fetch=FetchType.LAZY)
     @JoinColumn(name = "project_id")
@@ -330,4 +339,12 @@
     public void setParentIssue(Issue parentIssue) {
         this.parentIssue = parentIssue;
     }
+
+    public String isApi() {
+        return isApi;
+    }
+
+    public void setApi(String api) {
+        isApi = api;
+    }
 }
diff --git a/src/main/java/kr/wisestone/owl/mapper/IssueMapper.java b/src/main/java/kr/wisestone/owl/mapper/IssueMapper.java
index 5f3fd0f..c2aed52 100644
--- a/src/main/java/kr/wisestone/owl/mapper/IssueMapper.java
+++ b/src/main/java/kr/wisestone/owl/mapper/IssueMapper.java
@@ -1,6 +1,7 @@
 package kr.wisestone.owl.mapper;
 
 import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.IssueCustomFieldValueCondition;
 import kr.wisestone.owl.web.form.IssueForm;
 import org.springframework.data.repository.query.Param;
 import org.springframework.stereotype.Repository;
@@ -15,6 +16,8 @@
 public interface IssueMapper {
     List<Map<String, Object>> find(IssueCondition issueCondition);
 
+    List<Map<String, Object>> findByCustomFieldValue(IssueCustomFieldValueCondition issueCustomFieldValueCondition);
+
     Long count(IssueCondition issueCondition);
 
     void insertBatch(@Param("issueForms") List<IssueForm> issueForms);
diff --git a/src/main/java/kr/wisestone/owl/service/ApiTokenService.java b/src/main/java/kr/wisestone/owl/service/ApiTokenService.java
index d1d58e3..8758fec 100644
--- a/src/main/java/kr/wisestone/owl/service/ApiTokenService.java
+++ b/src/main/java/kr/wisestone/owl/service/ApiTokenService.java
@@ -1,12 +1,10 @@
 package kr.wisestone.owl.service;
 
-import kr.wisestone.owl.domain.ApiToken;
-import kr.wisestone.owl.domain.CompanyField;
-import kr.wisestone.owl.domain.CustomField;
-import kr.wisestone.owl.domain.Event;
+import kr.wisestone.owl.domain.*;
 import kr.wisestone.owl.vo.ApiTokenVo;
 import kr.wisestone.owl.vo.CompanyFieldVo;
 import kr.wisestone.owl.vo.EventVo;
+import kr.wisestone.owl.vo.UserVo;
 import kr.wisestone.owl.web.condition.ApiTokenCondition;
 import kr.wisestone.owl.web.condition.CompanyFieldCondition;
 import kr.wisestone.owl.web.condition.EventCondition;
@@ -30,5 +28,7 @@
 
     ApiTokenVo find();
 
+    UserVo certification(String token);
+
     void remove(ApiTokenForm apiTokenForm);
 }
diff --git a/src/main/java/kr/wisestone/owl/service/AttachedFileService.java b/src/main/java/kr/wisestone/owl/service/AttachedFileService.java
index f75d388..6a88a81 100644
--- a/src/main/java/kr/wisestone/owl/service/AttachedFileService.java
+++ b/src/main/java/kr/wisestone/owl/service/AttachedFileService.java
@@ -19,6 +19,8 @@
 
     List<AttachedFile> addAttachedFile(List<MultipartFile> multipartFiles, Map<String, Object> content);
 
+    List<AttachedFile> addAttachedFile(Workspace workspace, Issue issue, List<Map<String, Object>> files);
+
     List<AttachedFileVo> findAttachedFile(Map<String, Object> resJsonData, AttachedFileCondition condition);
 
     List<AttachedFile> findByIssueId(Long issueId);
diff --git a/src/main/java/kr/wisestone/owl/service/CustomFieldApiOverlapService.java b/src/main/java/kr/wisestone/owl/service/CustomFieldApiOverlapService.java
index 0a04dec..a6897a6 100644
--- a/src/main/java/kr/wisestone/owl/service/CustomFieldApiOverlapService.java
+++ b/src/main/java/kr/wisestone/owl/service/CustomFieldApiOverlapService.java
@@ -6,9 +6,11 @@
 import kr.wisestone.owl.web.form.IssueApiDefaultForm;
 import org.springframework.data.jpa.repository.JpaRepository;
 
+import java.util.List;
 import java.util.Map;
 
 public interface CustomFieldApiOverlapService extends AbstractService<CustomFieldApiOverlap, Long, JpaRepository<CustomFieldApiOverlap, Long>> {
     void find(Map<String, Object> resJsonData, CustomFieldApiOverlapForm form);
+    List<CustomFieldApiOverlap> find(Long userId, Long issueTypeId);
     boolean modify(Map<String, Object> resJsonData, CustomFieldApiOverlapForm form);
 }
diff --git a/src/main/java/kr/wisestone/owl/service/IssueApiDefaultService.java b/src/main/java/kr/wisestone/owl/service/IssueApiDefaultService.java
index 72fe5b7..ab47409 100644
--- a/src/main/java/kr/wisestone/owl/service/IssueApiDefaultService.java
+++ b/src/main/java/kr/wisestone/owl/service/IssueApiDefaultService.java
@@ -9,5 +9,6 @@
 
 public interface IssueApiDefaultService extends AbstractService<IssueApiDefault, Long, JpaRepository<IssueApiDefault, Long>> {
     IssueApiDefault find(Map<String, Object> resJsonData, IssueApiDefaultForm form);
+    IssueApiDefault find(IssueApiDefaultForm form);
     boolean modify(Map<String, Object> resJsonData, IssueApiDefaultForm form);
 }
diff --git a/src/main/java/kr/wisestone/owl/service/IssueCustomFieldValueService.java b/src/main/java/kr/wisestone/owl/service/IssueCustomFieldValueService.java
index 28c6e08..5d972f1 100644
--- a/src/main/java/kr/wisestone/owl/service/IssueCustomFieldValueService.java
+++ b/src/main/java/kr/wisestone/owl/service/IssueCustomFieldValueService.java
@@ -6,6 +6,7 @@
 import kr.wisestone.owl.domain.enumType.CustomFieldType;
 import kr.wisestone.owl.vo.IssueCustomFieldValueVo;
 import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.IssueCustomFieldValueCondition;
 import org.springframework.data.jpa.repository.JpaRepository;
 
 import java.util.List;
@@ -27,6 +28,8 @@
 
     boolean find(IssueCondition condition, Set<String> issueIds);
 
+    Map<String, Object> find(IssueCustomFieldValueCondition issueCustomFieldValueCondition);
+
     List<Map<String, Object>> findInIssueIds(IssueCondition issueCondition);
 
     void removeIssueCustomFieldValuesByCustomFieldId(CustomField customField);
diff --git a/src/main/java/kr/wisestone/owl/service/IssueDepartmentService.java b/src/main/java/kr/wisestone/owl/service/IssueDepartmentService.java
index aaf4f7e..be18795 100644
--- a/src/main/java/kr/wisestone/owl/service/IssueDepartmentService.java
+++ b/src/main/java/kr/wisestone/owl/service/IssueDepartmentService.java
@@ -2,6 +2,7 @@
 
 import kr.wisestone.owl.domain.Issue;
 import kr.wisestone.owl.domain.IssueDepartment;
+import kr.wisestone.owl.domain.User;
 import kr.wisestone.owl.domain.Workspace;
 import org.springframework.data.jpa.repository.JpaRepository;
 
@@ -13,6 +14,8 @@
     //�떞�떦遺��꽌
     void modifyIssueDepartment(Issue issue, Workspace workspace, List<Long> departmentIds);
 
+    void modifyIssueDepartment(Issue issue, User user, Workspace workspace, List<Long> departmentIds);
+
     void insertIssueDepartment(List<Map<String, Long>> issueAssigneeMaps);
 
     void removeIssueDepartment(Long projectId, List<Long> excludeUserIds);
diff --git a/src/main/java/kr/wisestone/owl/service/IssueHistoryService.java b/src/main/java/kr/wisestone/owl/service/IssueHistoryService.java
index f9417a5..0951dd2 100644
--- a/src/main/java/kr/wisestone/owl/service/IssueHistoryService.java
+++ b/src/main/java/kr/wisestone/owl/service/IssueHistoryService.java
@@ -18,8 +18,12 @@
 
     void addIssueHistory(Issue issue, IssueHistoryType issueHistoryType, String issueChangeDescription);
 
+    void addIssueHistory(Issue issue, User user, IssueHistoryType issueHistoryType, String issueChangeDescription);
+
     void makeDescription(StringBuilder description, IssueHistoryType issueHistoryType, String issueChangeDescription);
 
+    void makeDescription(User user, StringBuilder description, IssueHistoryType issueHistoryType, String issueChangeDescription);
+
     void findIssueHistory(Map<String, Object> resJsonData, IssueHistoryCondition issueHistoryCondition);
 
     List<IssueHistoryVo> findIssueHistory(Long issueId);
diff --git a/src/main/java/kr/wisestone/owl/service/IssueService.java b/src/main/java/kr/wisestone/owl/service/IssueService.java
index 671e472..a8e1809 100644
--- a/src/main/java/kr/wisestone/owl/service/IssueService.java
+++ b/src/main/java/kr/wisestone/owl/service/IssueService.java
@@ -2,10 +2,12 @@
 
 import kr.wisestone.owl.domain.Issue;
 import kr.wisestone.owl.domain.IssueType;
+import kr.wisestone.owl.domain.User;
 import kr.wisestone.owl.domain.Workflow;
 import kr.wisestone.owl.vo.IssueVo;
 import kr.wisestone.owl.web.condition.IssueCondition;
 import kr.wisestone.owl.web.condition.ProjectCondition;
+import kr.wisestone.owl.web.form.IssueApiForm;
 import kr.wisestone.owl.web.form.IssueForm;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
@@ -22,6 +24,11 @@
 
     Issue addIssue(IssueForm issueForm, List<MultipartFile> files);
 
+    Issue addIssue(User user, IssueForm issueForm, List<MultipartFile> multipartFiles);
+
+    Issue addApiIssue(IssueApiForm issueApiForm);
+
+
     List<IssueVo> findIssue(Map<String, Object> resJsonData,
                             IssueCondition condition, Pageable pageable);
 
diff --git a/src/main/java/kr/wisestone/owl/service/WorkspaceService.java b/src/main/java/kr/wisestone/owl/service/WorkspaceService.java
index e29596d..00d4e28 100644
--- a/src/main/java/kr/wisestone/owl/service/WorkspaceService.java
+++ b/src/main/java/kr/wisestone/owl/service/WorkspaceService.java
@@ -42,6 +42,8 @@
 
     void checkUseWorkspace();
 
+    Workspace checkUseWorkspace(User user, Long workspaceId);
+
     ModelAndView checkUseExcelDownload(Model model);
 
     boolean checkUseTraffic(Long fileSize);
diff --git a/src/main/java/kr/wisestone/owl/service/impl/ApiTokenServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/ApiTokenServiceImpl.java
index d7d4039..a1a0bf9 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/ApiTokenServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/ApiTokenServiceImpl.java
@@ -4,8 +4,10 @@
 import io.jsonwebtoken.Jws;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.SignatureAlgorithm;
+import kr.wisestone.owl.constant.MsgConstants;
 import kr.wisestone.owl.domain.ApiToken;
 import kr.wisestone.owl.domain.User;
+import kr.wisestone.owl.exception.OwlRuntimeException;
 import kr.wisestone.owl.repository.ApiTokenRepository;
 import kr.wisestone.owl.service.ApiTokenService;
 import kr.wisestone.owl.util.ConvertUtil;
@@ -84,8 +86,19 @@
     }
 
     //JWT 蹂듯샇�솕
-    public UserVo getUser(String jwt) {
+    private UserVo getUserVo(String jwt) {
 
+        //寃곌낵媛� = Claims
+        Jws<Claims> claims = decryption(jwt);
+        if (claims == null)
+            return  null;
+
+        ObjectMapper objectMapper = new ObjectMapper();
+        //諛섑솚 ���엯�� LinkedHashMap �씠�떎. �씠瑜� User ���엯�쑝濡� 蹂��솚�븯湲� �쐞�빐 ObjectMapper �궗�슜
+        return objectMapper.convertValue(claims.getBody().get(DATA_KEY), UserVo.class);
+    }
+
+    private Jws<Claims> decryption(String jwt) {
         //寃곌낵媛� = Claims
         Jws<Claims> claims = null;
 
@@ -98,11 +111,9 @@
         } catch (Exception e) {
             log.debug(e.getMessage(), e);
         }
-
-        ObjectMapper objectMapper = new ObjectMapper();
-        //諛섑솚 ���엯�� LinkedHashMap �씠�떎. �씠瑜� User ���엯�쑝濡� 蹂��솚�븯湲� �쐞�빐 ObjectMapper �궗�슜
-        return objectMapper.convertValue(claims.getBody().get(DATA_KEY), UserVo.class);
+        return claims;
     }
+
 
     // �넗�겙 議고쉶
     @Override
@@ -115,6 +126,18 @@
         return null;
     }
 
+    // �넗�겙 �궗�슜�옄 �씤利�
+    @Override
+    public UserVo certification(String token) {
+         UserVo userVo = this.getUserVo(token);
+         if (userVo != null){
+             return userVo;
+         } else  {
+             throw new OwlRuntimeException(
+                     this.messageAccessor.getMessage(MsgConstants.ERROR_TOKEN));
+         }
+    }
+
     // �넗�겙 �궘�젣
     @Override
     public void remove(ApiTokenForm apiTokenForm) {
diff --git a/src/main/java/kr/wisestone/owl/service/impl/AttachedFileServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/AttachedFileServiceImpl.java
index 8057f64..0e118f1 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/AttachedFileServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/AttachedFileServiceImpl.java
@@ -119,6 +119,35 @@
         }
     }
 
+    //  泥⑤� �뙆�씪�쓣 �벑濡앺븳�떎. - API �뿉�꽌 �궗�슜
+    @Override
+    @Transactional
+    public List<AttachedFile> addAttachedFile(Workspace workspace, Issue issue, List<Map<String, Object>> files) {
+        if (workspace == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKSPACE_NOT_EXIST));
+        }
+
+        if (issue == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_EXIST));
+        }
+
+        if (files != null && files.size() > 0) {
+            List<Map<String, Object>> convertFileMaps = Lists.newArrayList();
+
+            for (Map<String, Object> file : files) {
+                String fileName = MapUtil.getString(file, "fileName");
+                String fileStr = MapUtil.getString(file, "file");
+                String contentType = MapUtil.getString(file, "contentType");
+                convertFileMaps.add(CommonUtil.makeFileMap(fileName, fileStr, contentType));
+            }
+
+            return this.addAttachedFiles(workspace, convertFileMaps, issue, null, AttachedType.SUMMER);
+        }
+        return null;
+    }
+
     //  泥⑤� �뙆�씪�쓣 �벑濡앺븳�떎. - �씠�뒋 �깮�꽦, �닔�젙�뿉�꽌 �궗�슜
     @Override
     @Transactional
diff --git a/src/main/java/kr/wisestone/owl/service/impl/CustomFieldApiOverlapServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/CustomFieldApiOverlapServiceImpl.java
index 4b3be98..84bce83 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/CustomFieldApiOverlapServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/CustomFieldApiOverlapServiceImpl.java
@@ -41,10 +41,14 @@
     }
 
     @Override
+    public List<CustomFieldApiOverlap> find(Long userId, Long issueTypeId) {
+        return this.customFieldApiOverlapRepository.findByUserIdAndIssueTypeId(userId, issueTypeId);
+    }
+
+    @Override
     @Transactional
     public void find(Map<String, Object> resJsonData, CustomFieldApiOverlapForm form) {
-        UserVo userVo = this.webAppUtil.getLoginUser();
-        List<CustomFieldApiOverlap> customFieldApiOverlaps = this.customFieldApiOverlapRepository.findByUserIdAndIssueTypeId(userVo.getId(), form.getIssueTypeId());
+        List<CustomFieldApiOverlap> customFieldApiOverlaps = this.find(form.getUserId(), form.getIssueTypeId());
         if (customFieldApiOverlaps != null && customFieldApiOverlaps.size() > 0) {
 
             List<CustomFieldApiOverlapVo> customFieldApiOverlapVos =  Lists.newArrayList();
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueApiDefaultServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueApiDefaultServiceImpl.java
index 8df1a36..1c36cd7 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/IssueApiDefaultServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueApiDefaultServiceImpl.java
@@ -46,10 +46,9 @@
     @Override
     @Transactional
     public IssueApiDefault find(Map<String, Object> resJsonData, IssueApiDefaultForm form) {
-        UserVo userVo = this.webAppUtil.getLoginUser();
-        List<IssueApiDefault> issueApiDefaults = this.issueApiDefaultRepository.findByUserIdAndIssueTypeId(userVo.getId(), form.getIssueTypeId());
-        if (issueApiDefaults != null && issueApiDefaults.size() > 0) {
-            IssueApiDefault issueApiDefault = issueApiDefaults.get(0);
+        form.setUserId(this.webAppUtil.getLoginId());
+        IssueApiDefault issueApiDefault = this.find(form);
+        if (issueApiDefault != null) {
             IssueApiDefaultVo issueApiDefaultVo = ConvertUtil.copyProperties(issueApiDefault, IssueApiDefaultVo.class);
             Project project = issueApiDefault.getProject();
             if (project != null) {
@@ -67,7 +66,17 @@
             }
 
             resJsonData.put(Constants.RES_KEY_CONTENTS, issueApiDefaultVo);
-            return issueApiDefault;
+        }
+        return issueApiDefault;
+    }
+
+    @Override
+    public IssueApiDefault find(IssueApiDefaultForm form) {
+        if (form.getUserId() != null && form.getIssueTypeId() != null) {
+            List<IssueApiDefault> issueApiDefaults = this.issueApiDefaultRepository.findByUserIdAndIssueTypeId(form.getUserId(), form.getIssueTypeId());
+            if (issueApiDefaults != null && issueApiDefaults.size() > 0) {
+                return issueApiDefaults.get(0);
+            }
         }
         return null;
     }
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueCustomFieldValueServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueCustomFieldValueServiceImpl.java
index 34e6886..f8e1f52 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/IssueCustomFieldValueServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueCustomFieldValueServiceImpl.java
@@ -333,6 +333,29 @@
         return customFieldSearch;
     }
 
+    @Override
+    public Map<String, Object> find(IssueCustomFieldValueCondition issueCustomFieldValueCondition) {
+        if (issueCustomFieldValueCondition.getUseValues().size() > 0 || !StringUtils.isEmpty(issueCustomFieldValueCondition.getUseValue())) {
+            issueCustomFieldValueCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+
+            //  �궗�슜�옄 �젙�쓽 �븘�뱶 媛� 寃��깋 �떆�옉
+            Map<String, Object> result = new HashMap<>();
+
+            switch (CustomFieldType.valueOf(issueCustomFieldValueCondition.getCustomFieldType())) {
+                case INPUT:
+                    result = this.issueCustomFieldValueMapper.findLikeUseValue(issueCustomFieldValueCondition);
+                    break;
+                case MULTI_SELECT:
+                case SINGLE_SELECT:
+                    result = this.issueCustomFieldValueMapper.findByUseValue(issueCustomFieldValueCondition);
+                    break;
+            }
+
+            return result;
+        }
+        return null;
+    }
+
     //  �씠�뒋�뿉�꽌 ���옣�븳 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪쓣 議고쉶�븳�떎.
     @Override
     @Transactional(readOnly = true)
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueDepartmentServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueDepartmentServiceImpl.java
index 5835a65..1390d02 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/IssueDepartmentServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueDepartmentServiceImpl.java
@@ -1,10 +1,7 @@
 package kr.wisestone.owl.service.impl;
 
 import com.google.common.collect.Lists;
-import kr.wisestone.owl.domain.Issue;
-import kr.wisestone.owl.domain.IssueDepartment;
-import kr.wisestone.owl.domain.IssueUser;
-import kr.wisestone.owl.domain.Workspace;
+import kr.wisestone.owl.domain.*;
 import kr.wisestone.owl.mapper.IssueDepartmentMapper;
 import kr.wisestone.owl.mapper.IssueUserMapper;
 import kr.wisestone.owl.repository.IssueDepartmentRepository;
@@ -42,6 +39,14 @@
     @Override
     @Transactional
     public void modifyIssueDepartment(Issue issue, Workspace workspace, List<Long> departmentIds) {
+        User user = this.webAppUtil.getLoginUserObject();
+        modifyIssueDepartment(issue, user, workspace, departmentIds);
+    }
+
+    //  �씠�뒋 �떞�떦遺��꽌瑜� 蹂�寃쏀븳�떎.
+    @Override
+    @Transactional
+    public void modifyIssueDepartment(Issue issue, User user, Workspace workspace, List<Long> departmentIds) {
         List<Long> oldDepartmentIds = Lists.newArrayList();
 
         //  �씠�쟾 �떞�떦 遺��꽌
@@ -69,8 +74,8 @@
                 issueAssigneeMap.put("departmentId", departmentId); //�떞�떦遺��꽌
                 issueAssigneeMap.put("issueId", issue.getId());
                 issueAssigneeMap.put("workspaceId", workspace.getId());
-                issueAssigneeMap.put("registerId", this.webAppUtil.getLoginId());
-                issueAssigneeMap.put("modifyId", this.webAppUtil.getLoginId());
+                issueAssigneeMap.put("registerId", user.getId());
+                issueAssigneeMap.put("modifyId", user.getId());
                 addIssueAssigneeMaps.add(issueAssigneeMap);
             }
 
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java
index 71671dd..375f32b 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java
@@ -63,13 +63,22 @@
     @Override
     @Transactional
     public void addIssueHistory(Issue issue, IssueHistoryType issueHistoryType, String issueChangeDescription) {
+        User user = this.webAppUtil.getLoginUserObject();
+        addIssueHistory(issue, user, issueHistoryType, issueChangeDescription);
+    }
+
+    //  �씠�젰 �깮�꽦
+    @Override
+    @Transactional
+    public void addIssueHistory(Issue issue, User user, IssueHistoryType issueHistoryType, String issueChangeDescription) {
         IssueHistory issueHistory = new IssueHistory();
         issueHistory.setIssue(issue);
         issueHistory.setProject(issue.getProject());
         issueHistory.setIssueHistoryType(issueHistoryType);
         StringBuilder description = new StringBuilder();
         //  �씠�젰 �젙蹂대�� 留뚮뱾�뼱 �궦�떎.
-        this.makeDescription(description, issueHistoryType, issueChangeDescription);
+
+        this.makeDescription(user, description, issueHistoryType, issueChangeDescription);
         issueHistory.setDescription(description.toString());
 
         this.issueHistoryRepository.saveAndFlush(issueHistory);
@@ -81,6 +90,13 @@
     //  �씠�젰 �젙蹂대�� 留뚮뱾�뼱 �궦�떎.
     @Override
     public void makeDescription(StringBuilder description, IssueHistoryType issueHistoryType, String issueChangeDescription) {
+        User user = this.webAppUtil.getLoginUserObject();
+        makeDescription(user, description, issueHistoryType, issueChangeDescription);
+    }
+
+    //  �씠�젰 �젙蹂대�� 留뚮뱾�뼱 �궦�떎.
+    @Override
+    public void makeDescription(User user, StringBuilder description, IssueHistoryType issueHistoryType, String issueChangeDescription) {
         description.append("<div class=\"activity-text\">");
 
         //  �깮�꽦, �닔�젙, �궘�젣�뿉 ���빐 湲곕줉�쓣 �궓湲대떎.
@@ -90,9 +106,9 @@
                 description.append("<span class='activity-timestamp'>");
                 description.append(DateUtil.convertDateToStr(new Date()));
                 description.append(" (");
-                description.append(this.webAppUtil.getLoginUser().getName());
+                description.append(user.getName());
                 description.append(" - ");
-                description.append(CommonUtil.decryptAES128(this.webAppUtil.getLoginUser().getAccount()));
+                description.append(CommonUtil.decryptAES128(user.getAccount()));
                 description.append(")");
                 description.append("</span></h6>");
                 break;
@@ -103,10 +119,10 @@
                 description.append(DateUtil.convertDateToStr(new Date()));
                 description.append(" (");
 
-                if (this.webAppUtil.getLoginUser() != null) {
-                    description.append(this.webAppUtil.getLoginUser().getName());
+                if (user != null) {
+                    description.append(user.getName());
                     description.append(" - ");
-                    description.append(CommonUtil.decryptAES128(this.webAppUtil.getLoginUser().getAccount()));
+                    description.append(CommonUtil.decryptAES128(user.getAccount()));
                 }
                 else {
                     description.append("OWL-ITS-SYSTEM");
@@ -124,9 +140,9 @@
                 description.append("<span class=\"activity-timestamp\">");
                 description.append(DateUtil.convertDateToStr(new Date()));
                 description.append(" (");
-                description.append(this.webAppUtil.getLoginUser().getName());
+                description.append(user.getName());
                 description.append(" - ");
-                description.append(CommonUtil.decryptAES128(this.webAppUtil.getLoginUser().getAccount()));
+                description.append(CommonUtil.decryptAES128(user.getAccount()));
                 description.append(")");
                 description.append("</span></h6>");
                 break;
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 0005bb0..49a32b4 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
@@ -21,10 +21,10 @@
 import kr.wisestone.owl.util.DateUtil;
 import kr.wisestone.owl.vo.*;
 import kr.wisestone.owl.web.condition.IssueCondition;
+import kr.wisestone.owl.web.condition.IssueCustomFieldValueCondition;
 import kr.wisestone.owl.web.condition.IssueTypeCustomFieldCondition;
 import kr.wisestone.owl.web.condition.ProjectCondition;
-import kr.wisestone.owl.web.form.IssueCommentForm;
-import kr.wisestone.owl.web.form.IssueForm;
+import kr.wisestone.owl.web.form.*;
 import kr.wisestone.owl.web.view.ExcelView;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.time.StopWatch;
@@ -71,6 +71,15 @@
 
     @Autowired
     private SeverityService severityService;
+
+    @Autowired
+    private CustomFieldApiOverlapService customFieldApiOverlapService;
+
+    @Autowired
+    private IssueApiDefaultService issueApiDefaultService;
+
+    @Autowired
+    private ApiTokenService apiTokenService;
 
     @Autowired
     private CompanyFieldService companyFieldService;
@@ -182,12 +191,109 @@
     }
 
 
+
+    //  API 瑜� �넻�빐 �씠�뒋 異붽�.
+    @Override
+    @Transactional
+    public Issue addApiIssue(IssueApiForm issueApiForm) {
+        if (issueApiForm.getIssueTypeId() == null) {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_PARAMETER_ISSUE_TYPE_ERROR));
+        }
+
+        IssueForm issueForm = ConvertUtil.copyProperties(issueApiForm, IssueForm.class);
+//        issueForm.setFiles(issueApiForm.getFiles());
+        IssueType issueType = this.issueTypeService.getIssueType(issueApiForm.getIssueTypeId());
+        if (issueType == null){
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_PARAMETER_ISSUE_TYPE_ERROR));
+        }
+
+        // �봽濡쒖젥�듃 �엯�젰
+        Project project = issueType.getProject();
+        if (project == null){
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_PARAMETER_PROJECT_ERROR));
+        }
+        issueForm.setProjectId(project.getId());
+
+        // �넗�겙�쑝濡� �쑀�� �젙蹂� 媛��졇�삤湲�
+        String token = issueApiForm.getToken();
+        UserVo userVo = this.apiTokenService.certification(token);
+
+        // �빐�떦 �쑀�� �젙蹂닿� �쁽�옱 db�뿉 �엳�뒗吏� �솗�씤
+        User user = this.userService.getUser(userVo.getId());
+        if (user != null) {
+
+            // 湲곕낯媛� �엯�젰�븯湲�
+            IssueApiDefaultForm issueApiDefaultForm = new IssueApiDefaultForm();
+            issueApiDefaultForm.setUserId(user.getId());
+            issueApiDefaultForm.setIssueTypeId(issueForm.getIssueTypeId());
+            IssueApiDefault issueApiDefault = this.issueApiDefaultService.find(issueApiDefaultForm);
+            if (issueApiDefault != null) {
+                ConvertUtil.copyProperties(issueApiDefault, issueForm);
+                issueForm.setPriorityId(issueApiDefault.getPriority().getId());
+                issueForm.setSeverityId(issueApiDefault.getSeverity().getId());
+            }
+
+            // 以묐났 媛� �븯�쐞 �씠�뒋濡� 泥섎━�븯湲�
+            CustomFieldApiOverlapForm customFieldApiOverlapForm = new CustomFieldApiOverlapForm();
+            customFieldApiOverlapForm.setUserId(user.getId());
+            customFieldApiOverlapForm.setIssueTypeId(issueForm.getIssueTypeId());
+
+            IssueVo issueVo = this.findIssue(issueApiForm, user.getId());
+            if (issueVo != null) {
+                issueForm.setParentIssueId(issueVo.getId());
+            }
+
+            issueForm.setIsApi(Issue.IS_API_YES);
+
+            // �궗�슜�옄 �젙�쓽 �븘�뱶 �꽕�젙
+            issueForm.setIssueCustomFields(issueApiForm.getCustomFieldValues());
+
+            // api �엯�젰媛� �쟻�슜
+            ConvertUtil.copyProperties(issueApiForm, issueForm);
+
+            return addIssue(user, issueForm, issueApiForm.getMultipartFiles());
+        } else {
+            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_USER_ERROR));
+        }
+    }
+
+    // 以묐났�맂 �긽�쐞 �씠�뒋 寃��깋
+    private IssueVo findIssue(IssueApiForm issueApiForm, Long userId) {
+        IssueCustomFieldValueCondition issueCustomFieldValueCondition = new IssueCustomFieldValueCondition();
+
+        List<CustomFieldApiOverlap> customFieldApiOverlaps = this.customFieldApiOverlapService.find(userId, issueApiForm.getIssueTypeId());
+        if (customFieldApiOverlaps != null && customFieldApiOverlaps.size() > 0) {
+            for (CustomFieldApiOverlap customFieldApiOverlap : customFieldApiOverlaps) {
+                for (IssueCustomFieldValueForm issueCustomFieldValue : issueApiForm.getIssueCustomFieldValues()) {
+                    if (customFieldApiOverlap.getCustomField().getId().equals(issueCustomFieldValue.getCustomFieldId())) {
+                        issueCustomFieldValueCondition.addUseValues(issueCustomFieldValue.getUseValue());
+                    }
+                }
+            }
+            List<Map<String, Object>> results = this.issueMapper.findByCustomFieldValue(issueCustomFieldValueCondition);
+            if (results != null && results.size() > 0) {
+                IssueVo issueVo = new IssueVo();
+                ConvertUtil.convertMapToObject(results.get(0), issueVo);
+                return issueVo;
+            }
+        }
+        return null;
+    }
+
     //  �씠�뒋瑜� �깮�꽦�븳�떎.
     @Override
     @Transactional
     public Issue addIssue(IssueForm issueForm, List<MultipartFile> multipartFiles) {
+        User user = this.webAppUtil.getLoginUserObject();
+        return addIssue(user, issueForm, multipartFiles);
+    }
+
+    //  �씠�뒋瑜� �깮�꽦�븳�떎.
+    @Override
+    @Transactional
+    public Issue addIssue(User user, IssueForm issueForm, List<MultipartFile> multipartFiles) {
         //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
-        this.workspaceService.checkUseWorkspace();
+        Workspace workspace = this.workspaceService.checkUseWorkspace(user, user.getLastWorkspaceId());
         //  �봽濡쒖젥�듃 �쑀�슚�꽦 泥댄겕
         Project project = this.projectService.getProject(issueForm.getProjectId());
         //  �씠�뒋 �쑀�삎 �쑀�슚�꽦 泥댄겕
@@ -216,7 +322,7 @@
 
         issue.setIssueNumber(this.issueNumberGeneratorService.generateIssueNumber(project));    //  媛� �봽濡쒖젥�듃�쓽 怨좎쑀 �씠�뒋 踰덊샇 �깮�꽦
 
-        this.issueRepository.saveAndFlush(issue);
+        issue = this.issueRepository.saveAndFlush(issue);
 
         issue.setReverseIndex(issue.getId() * -1);  //  荑쇰━ �냽�룄 媛쒖꽑�쓣 �쐞�빐 由щ쾭�뒪 �씤�뜳�뒪 �깮�꽦
         //  �떞�떦�옄 吏��젙
@@ -230,32 +336,34 @@
         //  HOSTING �젙蹂� ���옣
         this.issueHostingService.modifyIssueHostingField(issue, issueForm.getIssueHostingFields());
 
+
+        //  泥⑤� �뙆�씪 ���옣
         //  multipartFile �쓣 file Map List 媛앹껜濡� 蹂�寃쏀븳�떎.
         List<Map<String, Object>> convertFileMaps = this.convertMultipartFileToFile(multipartFiles);
-        //  泥⑤� �뙆�씪 ���옣
-        this.attachedFileService.addAttachedFile(convertFileMaps, issue, this.webAppUtil.getLoginUser().getAccount());
+        this.attachedFileService.addAttachedFile(convertFileMaps, issue, user.getAccount());
+
         //  �뀓�뒪�듃 �뿉�뵒�꽣�뿉 泥⑤��븳 �뙆�씪�쓣 �씠�뒋�� �뿰寃�
         this.checkNotHaveIssueIdAttachedFile(issue, issueForm);
         //  �궗�슜�옄 �젙�쓽 �븘�뱶 ���옣
         this.issueCustomFieldValueService.modifyIssueCustomFieldValue(issue, issueForm.getIssueCustomFields());
         //  �씠�뒋 �씠�젰 �깮�꽦
-        this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.ADD, null);
+        this.issueHistoryService.addIssueHistory(issue, user, IssueHistoryType.ADD, null);
         //  �씠�뒋 �쐞�뿕 愿�由� �깮�꽦
         this.issueRiskService.addIssueRisk(issue, project.getWorkspace());
         //  �쁺�냽�꽦 而⑦뀓�뒪�듃 鍮꾩슦湲�
         this.clear();
         //  �씠�뒋 �깮�꽦, �궘�젣�떆 �삁�빟 �씠硫붿씪�뿉 �벑濡앺빐�넃�뒗�떎.
-        this.reservationIssueEmail(issue.getId(), EmailType.ISSUE_ADD);
+        this.reservationIssueEmail(issue, EmailType.ISSUE_ADD);
         //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
-        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_ADD));
+
+        UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class);
+        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(userVo, ElasticSearchConstants.ISSUE_ADD));
 
         return issue;
     }
 
     //  �씠�뒋 �깮�꽦, �궘�젣�떆 �삁�빟 �씠硫붿씪�뿉 �벑濡앺빐�넃�뒗�떎.
-    private void reservationIssueEmail(Long id, EmailType emailType) {
-        Issue issue = this.getIssue(id);
-
+    private void reservationIssueEmail(Issue issue, EmailType emailType) {
         Map<String, Object> issueMap = new HashMap<>();
         //  �씠�뒋 �젙蹂대�� �씠硫붿씪 �쟾�넚�뿉 �궗�슜�븯湲� �쐞�빐 Map �삎�깭濡� 蹂��솚�븳�떎.
         this.makeIssueMapToIssue(issue, issueMap);
@@ -815,6 +923,7 @@
 
                 case "02": //  �봽濡쒖젥�듃, �씠�뒋 �쑀�삎, �씠�뒋 �긽�깭,  �슦�꽑�닚�쐞, 以묒슂�룄, �떞�떦�옄, 泥⑤��뙆�씪, �궗�슜�옄 �젙�쓽 �븘�뱶 �젙蹂�, �뙎湲�, 湲곕줉�쓣 �뀑�똿�븳�떎.
                     this.setIssueDetail(issueVo, issue);    //  �씠�뒋 �긽�꽭 �젙蹂대�� �뀑�똿�븳�떎.
+                    issueVo.setProjectVo(ConvertUtil.copyProperties(issue.getProject(), ProjectVo.class));
                     break;
             }
         }
@@ -1125,12 +1234,14 @@
     private List<Map<String, Object>> convertMultipartFileToFile(List<MultipartFile> multipartFiles) {
         List<Map<String, Object>> convertFileMaps = Lists.newArrayList();
 
-        for (MultipartFile multipartFile : multipartFiles) {
-            try {
-                Map<String, Object> fileMap = CommonUtil.makeFileMap(multipartFile);
-                convertFileMaps.add(fileMap);
-            } catch (Exception e) {
-                log.debug("multipartFile -> file 蹂��솚 �삤瑜�" + e.getMessage());
+        if (multipartFiles != null && multipartFiles.size() > 0) {
+            for (MultipartFile multipartFile : multipartFiles) {
+                try {
+                    Map<String, Object> fileMap = CommonUtil.makeFileMap(multipartFile);
+                    convertFileMaps.add(fileMap);
+                } catch (Exception e) {
+                    log.debug("multipartFile -> file 蹂��솚 �삤瑜�" + e.getMessage());
+                }
             }
         }
 
@@ -1484,7 +1595,7 @@
         }
 
         //  �씠�뒋 �깮�꽦, �궘�젣�떆 �삁�빟 �씠硫붿씪�뿉 �벑濡앺빐�넃�뒗�떎.
-        this.reservationIssueEmail(issue.getId(), EmailType.ISSUE_REMOVE);
+        this.reservationIssueEmail(issue, EmailType.ISSUE_REMOVE);
         //  �씠�뒋 �궘�젣
         this.issueRepository.delete(issue);
 
@@ -2521,7 +2632,6 @@
     public void reservationIssue() {
         List<IssueReservation> issueReservations = this.issueReservationService.findByIssueReservationTypeNotNull();
 
-
         Calendar calendar = Calendar.getInstance();
         int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
         int month = calendar.get(Calendar.MONTH) + 1;
@@ -2685,15 +2795,10 @@
     @Override
     public void modifyParentIssue(IssueForm issueDownForm) {
         Issue issue = this.getIssue(issueDownForm.getId()); //�븯�쐞 �씠�뒋
-        Long newParentIssueId = issueDownForm.getParentIssueId(); //蹂�寃쏀븷 �븯�쐞�씠�뒋�쓽 �긽�쐞�씠�뒋
-
-        StringBuilder sb = new StringBuilder();
-
         Issue parentIssue = issue.getParentIssue(); //蹂�寃� �쟾 �븯�쐞�씠�뒋�쓽 �긽�쐞�씠�뒋
-        if(parentIssue != null){ //蹂�寃� �쟾 �븯�쐞�씠�뒋�쓽 �긽�쐞�씠�뒋媛� 議댁옱 �븷 寃쎌슦
-            this.issueHistoryService.detectDownIssues(IssueHistoryType.DELETE, issue, sb);
-            this.issueHistoryService.addIssueHistory(parentIssue, IssueHistoryType.MODIFY, sb.toString());
-        }
+
+        Long newParentIssueId = issueDownForm.getParentIssueId(); //蹂�寃쏀븷 �븯�쐞�씠�뒋�쓽 �긽�쐞�씠�뒋
+        StringBuilder sb = new StringBuilder();
 
         if (newParentIssueId != null) { // 異붽� �븷 寃쎌슦
             parentIssue = this.getIssue(newParentIssueId); //�긽�쐞�씠�뒋(myIssue)
diff --git a/src/main/java/kr/wisestone/owl/service/impl/WorkspaceServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/WorkspaceServiceImpl.java
index 01c08e2..6e6e880 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/WorkspaceServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/WorkspaceServiceImpl.java
@@ -553,25 +553,47 @@
     @Override
     @Transactional
     public void checkUseWorkspace() {
-        Workspace workspace = this.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
+        User user = this.webAppUtil.getLoginUserObject();
+        Workspace workspace = this.getWorkspace(user.getLastWorkspaceId());
+
+        this.checkUseWorkspace(user, workspace.getId());
+    }
+
+    //  �궗�슜�븯怨� �엳�뒗 �뾽臾� 怨듦컙�씠 �솢�꽦 �긽�깭�씤吏� �솗�씤�븳�떎. �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븳�떎.
+    @Override
+    @Transactional
+    public Workspace checkUseWorkspace(User user, Long workspaceId) {
+        if (user == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.USER_NOT_EXIST));
+        }
+
+        if (workspaceId == null) {
+            throw new OwlRuntimeException(
+                    this.messageAccessor.getMessage(MsgConstants.WORKSPACE_NOT_EXIST));
+        }
+
+        Workspace workspace = this.getWorkspace(workspaceId);
 
         if (workspace.getServiceType().equals(ServiceType.UNUSED)) {
             throw new OwlRuntimeException(
                     this.messageAccessor.getMessage(MsgConstants.WORKSPACE_USE_PERIOD_EXCESS));
         }
 
-        UserWorkspace userWorkspace = this.userWorkspaceService.findByUserIdAndWorkspaceId(this.webAppUtil.getLoginId(), workspace.getId());
+        UserWorkspace userWorkspace = this.userWorkspaceService.findByUserIdAndWorkspaceId(user.getId(), workspace.getId());
 
         if (!userWorkspace.getUseYn()) {
             //  �빐�떦 �궗�슜�옄媛� 愿�由ы븯�뒗 �뾽臾� 怨듦컙瑜� 留덉�留� �젒洹� �뾽臾� 怨듦컙濡� 蹂�寃쏀븳�떎.
             this.userService.updateLastMyWorkspace(userWorkspace.getUser());
 
             //  鍮꾪솢�꽦 �궗�슜�옄�뒗 �뜑�씠�긽 �빐�떦 �뾽臾� 怨듦컙�뿉 �엳�쑝硫� �븞�맂�떎.
-            this.simpMessagingTemplate.convertAndSendToUser(this.webAppUtil.getLoginUser().getAccount(), "/notification/workspace-remove", this.messageAccessor.getMessage(MsgConstants.WORKSPACE_OUT, workspace.getName()));
+            this.simpMessagingTemplate.convertAndSendToUser(user.getAccount(), "/notification/workspace-remove", this.messageAccessor.getMessage(MsgConstants.WORKSPACE_OUT, workspace.getName()));
 
             throw new OwlRuntimeException(
                     this.messageAccessor.getMessage(MsgConstants.WORKSPACE_INCLUDE_DISABLED));
         }
+
+        return workspace;
     }
 
     //  �궗�슜 怨듦컙�뿉�꽌 濡쒓렇�씤�븳 �궗�슜�옄媛� 鍮꾪솢�꽦�씤吏� �솗�씤�븯怨� 鍮꾪솢�꽦�씪 寃쎌슦 �뿊�� �떎�슫濡쒕뱶瑜� 湲덉��븳�떎.
diff --git a/src/main/java/kr/wisestone/owl/util/CommonUtil.java b/src/main/java/kr/wisestone/owl/util/CommonUtil.java
index fb52889..5659bfd 100644
--- a/src/main/java/kr/wisestone/owl/util/CommonUtil.java
+++ b/src/main/java/kr/wisestone/owl/util/CommonUtil.java
@@ -344,6 +344,24 @@
         return fileMap;
     }
 
+    //  string file �젙蹂대�� file Map �삎�깭濡� 蹂�寃쏀븳�떎.
+    public static Map<String, Object> makeFileMap(String fileName, String file, String contentType) {
+        Map<String, Object> fileMap = new HashMap<>();
+
+        try {
+            byte[] bytes = Base64.decodeBase64(file);
+
+            fileMap.put("fileName", fileName);
+            fileMap.put("fileSize", bytes.length);
+            fileMap.put("contentType", contentType);
+            fileMap.put("file", CommonUtil.bytesToFile(fileName, bytes));
+        } catch (Exception e) {
+            LOGGER.debug(e.getMessage());
+        }
+
+        return fileMap;
+    }
+
     public static String getPostDataString(Map<String, String> params) throws UnsupportedEncodingException {
         StringBuilder result = new StringBuilder();
         boolean first = true;
@@ -378,6 +396,42 @@
         return convertFile;
     }
 
+    // string�쓣 �뙆�씪濡� 蹂��솚
+    public static File stringToFile(String fileName, String file) {
+
+        byte[] bytes = null;
+        try {
+            bytes = Base64.decodeBase64(file);
+
+        } catch (Exception ex) {
+            LOGGER.debug("string to bytes 蹂��솚 �삤瑜�");
+        }
+
+        if (bytes != null) {
+            return bytesToFile(fileName, bytes);
+        }
+        return  null;
+    }
+
+    // bytes瑜� �뙆�씪濡� 蹂��솚
+    public static File bytesToFile(String fileName, byte[] bytes) {
+        File convertFile = new File(WebAppUtil.getContextRealPath() + TMP_UPLOAD_FOLDER + getFileNameByUUID(fileName));
+        if (!convertFile.exists()) {
+            convertFile.mkdirs();
+        }
+
+        try{
+            FileOutputStream lFileOutputStream = new FileOutputStream(convertFile);
+            lFileOutputStream.write(bytes);
+            lFileOutputStream.close();
+
+        }catch (IllegalStateException | IOException e) {
+            LOGGER.debug("bytes �뙆�씪 file 蹂��솚 �삤瑜�");
+        }
+
+        return convertFile;
+    }
+
     public static InputStream getFileInputStream(String strPath, String strNewFileName){
         InputStream objInputStream = null;
 
diff --git a/src/main/java/kr/wisestone/owl/web/controller/ApiController.java b/src/main/java/kr/wisestone/owl/web/controller/ApiController.java
index 80ee790..b8c060c 100644
--- a/src/main/java/kr/wisestone/owl/web/controller/ApiController.java
+++ b/src/main/java/kr/wisestone/owl/web/controller/ApiController.java
@@ -1,9 +1,16 @@
 package kr.wisestone.owl.web.controller;
 
 import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.Issue;
+import kr.wisestone.owl.exception.OwlRuntimeException;
 import kr.wisestone.owl.service.GuideService;
+import kr.wisestone.owl.service.IssueService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
 import kr.wisestone.owl.web.condition.GuideCondition;
 import kr.wisestone.owl.web.form.GuideForm;
+import kr.wisestone.owl.web.form.IssueApiForm;
+import kr.wisestone.owl.web.form.IssueForm;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Pageable;
 import org.springframework.http.MediaType;
@@ -12,30 +19,49 @@
 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.Map;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Stack;
 
 @Controller
 public class ApiController extends BaseController {
 
     @Autowired
-    private GuideService guideService;
+    private IssueService issueService;
 
-    //  �씠�뒋 異붽�
-    @RequestMapping(value = "api/issue", produces = MediaType.APPLICATION_JSON_VALUE)
+    //  �씠�뒋 異붽�(json 諛⑹떇�쑝濡� �뙆�씪�쟾�넚)
+//    @RequestMapping(value = "api/issue", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+//    public
+//    @ResponseBody
+//    Map<String, Object> addIssue(@RequestBody Map<String, Map<String, Object>> params) {
+//        Map<String, Object> resJsonData = new HashMap<>();
+//
+//        IssueApiForm issueForm = IssueApiForm.make(params.get(Constants.REQ_KEY_CONTENT));
+//        Issue issue = this.issueService.addApiIssue(issueForm);
+//        //  踰꾩쟾 �깮�꽦
+//        this.issueService.addIssueVersion(issue.getId());
+//        return this.setSuccessMessage(resJsonData);
+//    }
+    @RequestMapping(value = "api/issue", method = RequestMethod.POST)
     public
     @ResponseBody
-    Map<String, Object> add(@RequestBody Map<String, Map<String, Object>> params) {
+    Map<String, Object> addIssue(MultipartHttpServletRequest request) throws OwlRuntimeException {
         Map<String, Object> resJsonData = new HashMap<>();
 
-        // todo
-
+        IssueApiForm issueForm = IssueApiForm.make(ConvertUtil.convertJsonToMap(request.getParameter(Constants.REQ_KEY_CONTENT)), request.getFiles("file"));
+        Issue issue = this.issueService.addApiIssue(issueForm);
+        //  踰꾩쟾 �깮�꽦
+        this.issueService.addIssueVersion(issue.getId());
         return this.setSuccessMessage(resJsonData);
     }
 
     //  �씠�뒋 議고쉶
-    @RequestMapping(value = "/api/issuelist", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
+    @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) {
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 68898f9..2e224eb 100644
--- a/src/main/java/kr/wisestone/owl/web/controller/IssueController.java
+++ b/src/main/java/kr/wisestone/owl/web/controller/IssueController.java
@@ -172,7 +172,7 @@
         Map<String, Object> resJsonData = new HashMap<>();
         Pageable pageable = this.pageUtil.convertPageable(this.getPageVo(params));
 
-        this.issueService.findMailTargetAll(resJsonData, IssueCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
+//        this.issueService.findMailTargetAll(resJsonData, IssueCondition.make(params.get(Constants.REQ_KEY_CONTENT)), pageable);
 
         return this.setSuccessMessage(resJsonData);
     }*/
diff --git a/src/main/java/kr/wisestone/owl/web/form/CustomFieldValueForm.java b/src/main/java/kr/wisestone/owl/web/form/CustomFieldValueForm.java
new file mode 100644
index 0000000..81a406d
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/CustomFieldValueForm.java
@@ -0,0 +1,24 @@
+package kr.wisestone.owl.web.form;
+
+public class CustomFieldValueForm {
+    private Long customFieldId;
+    private String value;
+
+    public CustomFieldValueForm(){}
+
+    public Long getCustomFieldId() {
+        return customFieldId;
+    }
+
+    public void setCustomFieldId(Long customFieldId) {
+        this.customFieldId = customFieldId;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/IssueApiForm.java b/src/main/java/kr/wisestone/owl/web/form/IssueApiForm.java
new file mode 100644
index 0000000..ed11829
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/IssueApiForm.java
@@ -0,0 +1,195 @@
+package kr.wisestone.owl.web.form;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.domain.IssueCustomFieldValue;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+import kr.wisestone.owl.vo.CustomFieldVo;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.*;
+
+public class IssueApiForm {
+    private String token;
+    private String title;
+    private String projectKey;
+    private String description;
+    private Long issueTypeId;
+    private Long priorityId;
+    private Long severityId;
+    private Date startDate;
+    private Date endDate;
+    private Date searchTime;
+    private List<DepartmentForm> departments = Lists.newArrayList();
+    private List<IssueCustomFieldValueForm> issueCustomFieldValues = Lists.newArrayList();
+    private List<Map<String, Object>> CustomFieldValues = Lists.newArrayList();
+//    private List<Map<String, Object>> files = Lists.newArrayList();
+    private List<MultipartFile> multipartFiles = Lists.newArrayList();
+
+    public IssueApiForm() {
+    }
+
+    public static IssueApiForm make(Map<String, Object> content, List<MultipartFile> files) {
+        IssueApiForm form = ConvertUtil.convertMapToClass(content, IssueApiForm.class);
+        form.setMultipartFiles(files);
+
+        //  �궗�슜�옄 �븘�뱶 �젙蹂�
+        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() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getProjectKey() {
+        return projectKey;
+    }
+
+    public void setProjectKey(String projectKey) {
+        this.projectKey = projectKey;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Long getIssueTypeId() {
+        return issueTypeId;
+    }
+
+    public void setIssueTypeId(Long issueTypeId) {
+        this.issueTypeId = issueTypeId;
+    }
+
+    public Long getPriorityId() {
+        return priorityId;
+    }
+
+    public void setPriorityId(Long priorityId) {
+        this.priorityId = priorityId;
+    }
+
+    public Long getSeverityId() {
+        return severityId;
+    }
+
+    public void setSeverityId(Long severityId) {
+        this.severityId = severityId;
+    }
+
+    public List<DepartmentForm> getDepartments() {
+        return departments;
+    }
+
+    public void setDepartments(List<DepartmentForm> departments) {
+        this.departments = departments;
+    }
+
+    public Date getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(Date startDate) {
+        this.startDate = startDate;
+    }
+
+    public Date getEndDate() {
+        return endDate;
+    }
+
+    public void setEndDate(Date endDate) {
+        this.endDate = endDate;
+    }
+
+    public Date getSearchTime() {
+        return searchTime;
+    }
+
+    public void setSearchTime(Date searchTime) {
+        this.searchTime = searchTime;
+    }
+
+    public List<IssueCustomFieldValueForm> getIssueCustomFieldValues() {
+        return issueCustomFieldValues;
+    }
+
+    public void setIssueCustomFieldValues(List<IssueCustomFieldValueForm> issueCustomFieldValues) {
+        this.issueCustomFieldValues = issueCustomFieldValues;
+    }
+
+    public void addIssueCustomFieldValue(IssueCustomFieldValueForm issueCustomFieldValueForm) {
+        if (this.issueCustomFieldValues != null) {
+            this.issueCustomFieldValues.add(issueCustomFieldValueForm);
+        }
+    }
+
+    public void addCustomFieldValue(Map<String, Object> map) {
+        if (this.CustomFieldValues != null) {
+            this.CustomFieldValues.add(map);
+        }
+    }
+
+    public List<Map<String, Object>> getCustomFieldValues() {
+        return CustomFieldValues;
+    }
+
+    public void setCustomFieldValues(List<Map<String, Object>> customFieldValues) {
+        CustomFieldValues = customFieldValues;
+    }
+
+//    public List<Map<String, Object>> getFiles() {
+//        return files;
+//    }
+//
+//    public void setFiles(List<Map<String, Object>> files) {
+//        this.files = files;
+//    }
+
+    public List<MultipartFile> getMultipartFiles() {
+        return multipartFiles;
+    }
+
+    public void setMultipartFiles(List<MultipartFile> multipartFiles) {
+        this.multipartFiles = multipartFiles;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/IssueCustomFieldValueForm.java b/src/main/java/kr/wisestone/owl/web/form/IssueCustomFieldValueForm.java
new file mode 100644
index 0000000..d6c7278
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/IssueCustomFieldValueForm.java
@@ -0,0 +1,24 @@
+package kr.wisestone.owl.web.form;
+
+public class IssueCustomFieldValueForm {
+    private Long customFieldId;
+    private String useValue;
+
+    public IssueCustomFieldValueForm(){}
+
+    public Long getCustomFieldId() {
+        return customFieldId;
+    }
+
+    public void setCustomFieldId(Long customFieldId) {
+        this.customFieldId = customFieldId;
+    }
+
+    public String getUseValue() {
+        return useValue;
+    }
+
+    public void setUseValue(String useValue) {
+        this.useValue = useValue;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/IssueForm.java b/src/main/java/kr/wisestone/owl/web/form/IssueForm.java
index 9ff571f..87aade8 100644
--- a/src/main/java/kr/wisestone/owl/web/form/IssueForm.java
+++ b/src/main/java/kr/wisestone/owl/web/form/IssueForm.java
@@ -41,7 +41,9 @@
     private List<Map<String, Object>> issueCompanyFields = Lists.newArrayList();
     private List<Map<String, Object>> issueIspFields = Lists.newArrayList();
     private List<Map<String, Object>> issueHostingFields = Lists.newArrayList();
+    private List<Map<String, Object>> files = Lists.newArrayList(); // api�슜 泥⑤��뙆�씪
     private Long parentIssueId; // �긽�쐞 �씠�뒋
+    private String isApi;
 
     public IssueForm() {
     }
@@ -116,6 +118,11 @@
         //  HOSTING �븘�뱶 �젙蹂�
         if (MapUtil.getObject(params, "issueHostingFields") != null){
             form.setIssueHostingFields((List)MapUtil.getObject(params, "issueHostingFields"));
+        }
+
+        //  api 泥⑤��뙆�씪
+        if (MapUtil.getObject(params, "files") != null){
+            form.setFiles((List)MapUtil.getObject(params, "files"));
         }
         return form;
     }
@@ -340,6 +347,22 @@
         this.parentIssueId = parentIssueId;
     }
 
+    public String getIsApi() {
+        return isApi;
+    }
+
+    public void setIsApi(String isApi) {
+        this.isApi = isApi;
+    }
+
+    public List<Map<String, Object>> getFiles() {
+        return files;
+    }
+
+    public void setFiles(List<Map<String, Object>> files) {
+        this.files = files;
+    }
+
     public String getTemplate() {
         return template;
     }
diff --git a/src/main/java/kr/wisestone/owl/web/resolver/OwlResponseEntityExceptionHandler.java b/src/main/java/kr/wisestone/owl/web/resolver/OwlResponseEntityExceptionHandler.java
index 560217e..20fc132 100644
--- a/src/main/java/kr/wisestone/owl/web/resolver/OwlResponseEntityExceptionHandler.java
+++ b/src/main/java/kr/wisestone/owl/web/resolver/OwlResponseEntityExceptionHandler.java
@@ -39,7 +39,7 @@
         Map<String, Object> resJsonData = new HashMap<String, Object>();
         resJsonData.put(Constants.RES_KEY_MESSAGE, this.messageAccessor.getResMessage(ex, Constants.RES_KEY_MSG_FAIL));
 
-        return this.handleExceptionInternal(ex, resJsonData, new HttpHeaders(), HttpStatus.OK, request);
+        return this.handleExceptionInternal(ex, resJsonData, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
     }
 
     @ExceptionHandler({ StackOverflowError.class,
diff --git a/src/main/resources/migration/V1_11__Alter_Table.sql b/src/main/resources/migration/V1_11__Alter_Table.sql
index e89439e..9fa9b72 100644
--- a/src/main/resources/migration/V1_11__Alter_Table.sql
+++ b/src/main/resources/migration/V1_11__Alter_Table.sql
@@ -209,3 +209,6 @@
 
 -- issue_type �뀒�씠釉� �뾽泥�,ISP,�샇�뒪�똿 而щ읆 異붽�
 ALTER TABLE `issue_type` ADD COLUMN  `use_partner` BIGINT(11) NOT NULL DEFAULT '0';
+
+-- api �씠�뒋 �뿬遺�
+ALTER TABLE `issue` ADD COLUMN  `is_api` VARCHAR(1) NOT NULL DEFAULT 'N';
diff --git a/src/main/resources/mybatis/query-template/issue-template.xml b/src/main/resources/mybatis/query-template/issue-template.xml
index c11a20c..fb6f2c4 100644
--- a/src/main/resources/mybatis/query-template/issue-template.xml
+++ b/src/main/resources/mybatis/query-template/issue-template.xml
@@ -482,5 +482,22 @@
         issue WHERE issue_status_id = #{issueStatusId};
     </select>
 
+    <!--    �듅�젙 �궗�슜�옄 �젙�쓽 �븘�뱶 媛믪씠 媛숈� �씠�뒋瑜� 議고쉶 -->
+    <select id="findByCustomFieldValue" resultType="java.util.HashMap" parameterType="kr.wisestone.owl.web.condition.IssueCustomFieldValueCondition">
+        SELECT
+        id
+        FROM issue
+        LEFT OUTER JOIN issue_custom_field_value issue_custom FORCE INDEX(issueIdIndex) ON issue.id = issue_custom.issue_id
+        WHERE 1=1
+        AND issue.parent_issue_id IS NULL
+        <choose>
+            <when test="useValues.size != 0">
+                AND issue_custom.use_value IN
+                <foreach collection="useValues" item="item" index="index" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+    </select>
 
 </mapper>
diff --git a/src/main/webapp/scripts/app/issue/issueDetail.controller.js b/src/main/webapp/scripts/app/issue/issueDetail.controller.js
index fe209d8..955bfbb 100644
--- a/src/main/webapp/scripts/app/issue/issueDetail.controller.js
+++ b/src/main/webapp/scripts/app/issue/issueDetail.controller.js
@@ -43,6 +43,7 @@
                 $scope.fn.getDownTableConfigs = getDownTableConfigs;
                 $scope.fn.containsPartner = containsPartner;
                 $scope.fn.onActivate = onActivate;
+                $scope.fn.makePartnersEmail = makePartnersEmail;
 
                 //  �씠�뒋 紐⑸줉 而⑦듃濡ㅻ윭 vm, fn �긽�냽 以�
                 $scope.vm.viewer = {};
@@ -72,7 +73,10 @@
 
                 $scope.vm.form = {
                     issues : [], //�뿰愿� �씪媛�
-                    issuesDown : []  // �븯�쐞 �씪媛�
+                    issuesDown : [],  // �븯�쐞 �씪媛�
+                    issueCompanyVos : [],
+                    issueIspVos : [],
+                    issueHostingVos : []
                 };
 
 
@@ -636,16 +640,25 @@
                 }
 
                 //  �씠�뒋紐낆쓣 �겢由��븯硫� �씠�뒋 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
-                $rootScope.$on("getIssueDetail", function (event, args) {
-                    $scope.vm.viewer.id = args["id"];
-                    $scope.fn.getIssueDetail();
-                });
+                // $rootScope.$on("getIssueDetail", function (event, args) {
+                //     $scope.vm.viewer.id = args["id"];
+                //     $scope.fn.getIssueDetail();
+                // });
 
                 //  �씠�뒋紐낆쓣 �겢由��븯硫� �씠�뒋 �긽�꽭 �젙蹂대�� 議고쉶�븳�떎.
-                $scope.$on("getIssueDetail", function (event, args) {
-                    $scope.vm.viewer.id = args["id"];
-                    $scope.fn.getIssueDetail();
-                });
+                // $scope.$on("getIssueDetail", function (event, args) {
+                //     $scope.vm.viewer.id = args["id"];
+                //     $scope.fn.getIssueDetail();
+                // });
+
+                $scope.$watch(function() {
+                    return $rootScope.currentDetailIssueId;
+                }, function() {
+                    if ($rootScope.currentDetailIssueId != null) {
+                        $scope.vm.viewer.id = $rootScope.currentDetailIssueId;
+                        $scope.fn.getIssueDetail();
+                    }
+                }, true);
 
                 //  珥덇린�솕 �빐�빞�븷 �븷紐⑹쓣 吏��젙�븯�뿬 �떎瑜� �씠�뒋瑜� �겢由��븷 �븣 珥덇린�솕�빐以��떎.
                 function initReload() {
@@ -653,9 +666,9 @@
                     $scope.vm.issueForm.issueStatusList = [];
                     $scope.vm.issueForm.issueStatusId = "";
                     $scope.vm.issueTypeId = $rootScope.getCurrentIssueTypeId();
-                    /*if ($scope.$root.$$phase !== '$apply' && $scope.$root.$$phase !== '$digest') {
-                        $scope.$apply();
-                    }*/
+                    // if ($scope.$root.$$phase !== '$apply' && $scope.$root.$$phase !== '$digest') {
+                    //     $scope.$apply();
+                    // }
                 }
 
                 //  �씠�뒋 �긽�깭 蹂�寃�
@@ -804,7 +817,7 @@
                     $scope.fn.getDownTableConfigs();
 
                         Issue.detail($resourceProvider.getContent(
-                        {id : $scope.vm.viewer.id, deep : "02", customFields : $scope.vm.customFields},
+                        {id : $scope.vm.viewer.id, deep : "02"},
                         $resourceProvider.getPageContent(0, 1))).then(function (result) {
 
                         if (result.data.message.status === "success") {
@@ -846,6 +859,10 @@
                                 }
                                 $scope.vm.viewer.issueRelationVos = result.data.data.issueRelationVos;
                                 $scope.vm.viewer.issueDownVos = result.data.data.issueDownVos;
+
+                                if ($rootScope.workProject.id > -1) {
+                                    $rootScope.changeLastProject(result.data.data.projectVo.id);
+                                }
                             }
                         }
                         else {
@@ -1038,8 +1055,15 @@
                     });
                 }
 
+                // �뾽泥�/ISP/�샇�뒪�똿 諛곗뿴媛믪쓣 �븯�굹濡� �빀爾먯꽌 硫붿씪 �겢由��떆 �쟾�떖
+                function makePartnersEmail() {
+                    $scope.vm.form.partnersEmail = $scope.vm.viewer.issueCompanyVos.concat($scope.vm.viewer.issueIspVos, $scope.vm.viewer.issueHostingVos)
+                    return $scope.vm.form.partnersEmail;
+                }
+
                 //  �듅�젙 �궗�슜�옄�뿉寃� �씠�뒋瑜� 硫붿씪濡� 諛쒖넚
                 function sendMail(issueId, projectId) {
+                    makePartnersEmail();
                     $uibModal.open({
                         templateUrl : 'views/issue/issueSendMail.html',
                         size : "md",
@@ -1049,11 +1073,12 @@
                             parameter : function () {
                                 return {
                                     departmentVos : [$scope.vm.form.issues[0].departmentVos],
-                                    issueCompanyFields : [$scope.vm.form.issues[0].issueCompanyVos],
-                                    issueIspFields : [$scope.vm.form.issues[0].issueIspVos],
-                                    issueHostingFields : [$scope.vm.form.issues[0].issueHostingVos],
+                                    partners : $scope.vm.form.partnersEmail,
                                     issueId : issueId,
                                     projectId : projectId
+                                    // issueCompanyFields : [$scope.vm.viewer.issueCompanyVos[0]],
+                                    // issueIspFields : [$scope.vm.viewer.issueIspVos[0]],
+                                    // issueHostingFields : [$scope.vm.viewer.issueHostingVos[0]],
                                 };
                             }
                         }
diff --git a/src/main/webapp/scripts/app/issue/issueList.controller.js b/src/main/webapp/scripts/app/issue/issueList.controller.js
index fe9dc27..7c348ac 100644
--- a/src/main/webapp/scripts/app/issue/issueList.controller.js
+++ b/src/main/webapp/scripts/app/issue/issueList.controller.js
@@ -169,10 +169,11 @@
                     //  �쁽�옱 �긽�꽭�솕硫댁쑝濡� 蹂대젮怨좏븯�뒗 �씠�뒋 id瑜� 湲곗뼲�븳�떎.
                     $rootScope.currentDetailIssueId = id;
                     //  �씠�뒋 �긽�꽭 �솕硫� �슂泥�
-                    $rootScope.$broadcast("getIssueDetail", {
-                        id : id
-                    });
+                    // $scope.$broadcast("getIssueDetail", {
+                    //     id : id
+                    // });
                 }
+
 
                 //  紐⑸줉 �솕硫댁쑝濡� 蹂�寃쏀븳�떎.
                 function listView() {
@@ -183,6 +184,7 @@
                     $scope.vm.responseData.data = angular.copy(temp);
                     //  留덉�留됱쑝濡� 蹂닿퀬�엳�뜕 �씠�뒋 id瑜� 珥덇린�솕�븳�떎.
                     $rootScope.currentDetailIssueId = null;
+                    $scope.$broadcast("getIssueList", {id: $rootScope.currentDetailIssueId});
                 }
 
                 //  �씠�뒋 �뀒�씠釉� �꽕�젙
@@ -508,6 +510,31 @@
                                 if (detail) {
                                     changeDetailView(result.data.data[0].id);
                                 }
+                            }
+                        }
+                        else {
+                            SweetAlert.error($filter("translate")("issue.failedIssueLookup"), result.data.message.message); // �씠�뒋 議고쉶 �떎�뙣
+                        }
+                    });
+                }
+
+                // �씠硫붿씪 �봽濡쒖젥�듃 寃쎈줈濡� �씠�룞 �썑 �긽�꽭 吏꾩엯�떆 議고쉶
+                function getDetailList(projectKey, number) {
+                    var conditions = {
+                        projectKey : projectKey,
+                        combinationIssueNumber : number
+                    };
+
+                    Issue.find($resourceProvider.getContent(conditions,
+                        $resourceProvider.getPageContent(0, 1))).then(function (result) {
+
+                        if (result.data.message.status === "success") {
+                            if (result.data.data !=  null && result.data.data.length > 0) {
+                                $scope.vm.projectKey = result.data.data[0].projectKey;
+                                $scope.vm.issueNumber = result.data.data[0].issueNumber;
+                                $scope.vm.responseData = result.data;
+
+                                changeDetailView(result.data.data[0].id);
                             }
                         }
                         else {
@@ -885,7 +912,8 @@
                     // �뙆�씪誘명꽣 �씫湲�
                     var params = $rootScope.previousGetParams;
                     if ($rootScope.isDefined(params)) {
-                        $rootScope.$broadcast("makeIssueSearch", {  projectKey : params.projectKey, issueNumber : params.issueNumber });
+                        // $rootScope.$broadcast("makeIssueSearch", {  projectKey : params.projectKey, issueNumber : params.issueNumber });
+                        getDetailList(params.projectKey, params.issueNumber);
                         $rootScope.previousGetParams = null;
                         // $rootScope.issueTypeId = $rootScope.issueTypeMenu.id;
                         return;
diff --git a/src/main/webapp/scripts/app/issue/issueSendMail.controller.js b/src/main/webapp/scripts/app/issue/issueSendMail.controller.js
index 29e991e..3643b48 100644
--- a/src/main/webapp/scripts/app/issue/issueSendMail.controller.js
+++ b/src/main/webapp/scripts/app/issue/issueSendMail.controller.js
@@ -13,49 +13,63 @@
                     removeManager : removeManager,  //  �쟾�넚 ���긽�옄 �궘�젣
                     cancel : cancel,    //  �뙘�뾽 李� �떕湲�
                     formSubmit : formSubmit,    //  �뤌 �쟾�넚
-                    formCheck : formCheck   //  �뤌 泥댄겕
+                    // formCheck : formCheck  //  �뤌 泥댄겕
                 };
+
 
                 $scope.vm = {
                     form : {
                         id : parameter.issueId,  //  �씠�뒋 踰덊샇
                         projects : [{ id : parameter.projectId}],  //  �봽濡쒖젥�듃
-                        users : []    //  硫붿씪 �쟾�넚諛쏅뒗 �궗�슜�옄
+                        partners : parameter.partners,
+                        users : [],    //  硫붿씪 �쟾�넚諛쏅뒗 �궗�슜�옄
+                        issueCompanyVos : [],
+                        issueIspVos : [],
+                        issueHostingVos : []
+                        // companyFields : parameter.issueCompanyFields,  //  �뾽泥� �씠硫붿씪
+                        // ispFields : parameter.issueIspFields,  //  ISP �씠硫붿씪
+                        // hostingFields : parameter.issueHostingFields,  //  �샇�뒪�똿 �씠硫붿씪
                     },
                     userName : "",
+                    partnerName : "",
                     autoCompletePage : {
                         user : {
                             page : 0,
                             totalPage : 0
+                        },
+                        partnersMail : {
+                            page : 0,
+                            totalPage :0
                         }
                     }
                 };
 
+
+
                 angular.extend(this, $controller('autoCompleteController', {$scope : $scope, $injector : $injector}));
 
-                function formCheck(formInvalid) {
-                    if (formInvalid) {
-                        return true;
-                    }
-
-                    if ($scope.vm.form.users.length < 1) {
-                        return true;
-                    }
-
-                    return false;
-                }
+                // function formCheck(formInvalid) {
+                //     if (formInvalid) {
+                //         return true;
+                //     }
+                //
+                //     if ($scope.vm.form.users.length < 1) {
+                //         return true;
+                //     }
+                //
+                //     return false;
+                // }
 
                 //  �뤌 �쟾�넚
                 function formSubmit() {
                     $rootScope.spinner = true;
-
                     var content = {
                         id : $scope.vm.form.id,
                         sendEmails : (function () {
                             var sendEmails = [];
 
-                            angular.forEach($scope.vm.form.users, function (user) {
-                                sendEmails.push($rootScope.encryption(user.account));
+                            angular.forEach($scope.vm.form.projects, function (project) {
+                                sendEmails.push(project.id);
                             });
 
                             return sendEmails;
@@ -78,6 +92,42 @@
                     });
                 }
 
+                // function formSubmit() {
+                //     $rootScope.spinner = true;
+                //
+                //     var content = {
+                //         id : $scope.vm.form.id,
+                //         companyFieldsEmail : $scope.vm.form.companyFieldsEmail,
+                //         ispFieldsEmail : $scope.vm.form.ispFieldsEmail,
+                //         hostingFieldsEmail : $scope.vm.form.hostingFieldsEmail,
+                //         sendEmails : (function () {
+                //             var sendEmails = [];
+                //
+                //             angular.forEach($scope.vm.form.projects, function (project) {
+                //                 sendEmails.push(project.id);
+                //             });
+                //
+                //             return sendEmails;
+                //         })()
+                //     };
+                //
+                //     Issue.findMailTargetAll($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;
+                //     });
+                // }
+
+
                 //  �궗�슜�옄 auto complete callback function
                 function getUserListCallBack(result) {
                     $scope.vm.autoCompletePage.user.totalPage = result.data.page.totalPage;
diff --git a/src/main/webapp/scripts/components/issue/issue.service.js b/src/main/webapp/scripts/components/issue/issue.service.js
index 4c28715..22cf448 100644
--- a/src/main/webapp/scripts/components/issue/issue.service.js
+++ b/src/main/webapp/scripts/components/issue/issue.service.js
@@ -98,6 +98,12 @@
                     return response;
                 });
             },
+            findMailTargetAll : function (conditions) {
+                return $http.post("issue/findMailTargetAll", conditions).then(function (response) {
+                    $log.debug("�씠�뒋 �씠硫붿씪 諛쒖넚 寃곌낵 : ", response);
+                    return response;
+                });
+            }
         }
     }
     ])
diff --git a/src/main/webapp/scripts/components/utils/autoComplete.controller.js b/src/main/webapp/scripts/components/utils/autoComplete.controller.js
index ef94066..1b527bf 100644
--- a/src/main/webapp/scripts/components/utils/autoComplete.controller.js
+++ b/src/main/webapp/scripts/components/utils/autoComplete.controller.js
@@ -40,6 +40,7 @@
                 $scope.fn.getPartnerList = getPartnerList;          // �뾽泥�/isp/�샇�뒪�똿 紐⑸줉 議고쉶
                 $scope.fn.getIssueTypeList = getIssueTypeList;          // �씠�뒋 �쑀�삎 紐⑸줉 議고쉶
                 $scope.fn.getCustomFieldList = getCustomFieldList;          // �궗�슜�옄 �젙�쓽 �븘�뱶 紐⑸줉 議고쉶
+                $scope.fn.getMailTargetAll = getMailTargetAll;          // �궗�슜�옄 �젙�쓽 �븘�뱶 紐⑸줉 議고쉶
 
                 function getUserList(query, excludeList, page, callBack) {
                     var conditions = {
@@ -480,6 +481,48 @@
 
                     return deferred.promise;
                 }
+
+                function getMailTargetAll(query, excludeList, page, callBack) {
+                    var conditions = {
+                        id : query,
+                        excludeIds : (function () {
+                            var excludeIds = [];
+
+                            angular.forEach(excludeList, function (exclude) {
+                                excludeIds.push(exclude.id);
+                            });
+
+                            return excludeIds;
+                        })(),
+                        partnersEmailIds : (function () {
+                            var partnersEmailIds = [];
+
+                            angular.forEach($scope.vm.form.partners, function (partner) {
+                                partnersEmailIds.push(partner.email);
+                            });
+
+                            return partnersEmailIds;
+                        })(),
+                    };
+                    var deferred = $q.defer();
+
+                    Issue.findMailTargetAll($resourceProvider.getContent(         //  �럹�씠吏� �뾽�뜲�씠�듃媛� �븘�슂�븳 而댄룷�꼳�듃 �씪寃쎌슦, page �뾽�뜲�씠�듃媛� �엳�쓣 寃쎌슦 湲곕낯 10媛쒖뵫 媛��졇�삤怨� �븘�땺寃쎌슦 25媛쒖뵫 媛��졇�삩�떎.
+                        conditions, $resourceProvider.getPageContent($rootScope.isDefined(page) ? page : 0, $rootScope.isDefined(page) ? 10 : 25))).then(function (result) {
+                        if (result.data.message.status === "success") {
+
+                            if ($rootScope.isDefined(callBack)) {
+                                callBack(result);
+                            }
+
+                            deferred.resolve(result.data.data);
+                        }
+                        else {
+                            SweetAlert.swal($filter("translate")("issue.failedToIssueTypeListLookup"), result.data.message.message, "error"); // "�씠�뒋 �쑀�삎 紐⑸줉 議고쉶 �떎�뙣"
+                        }
+                    });
+
+                    return deferred.promise;
+                }
             }
         ]);
     }
diff --git a/src/main/webapp/scripts/components/utils/issueDetailImagePreview.directive.js b/src/main/webapp/scripts/components/utils/issueDetailImagePreview.directive.js
index 4f8cf0f..38d3899 100644
--- a/src/main/webapp/scripts/components/utils/issueDetailImagePreview.directive.js
+++ b/src/main/webapp/scripts/components/utils/issueDetailImagePreview.directive.js
@@ -25,7 +25,7 @@
 
                         //  �씠誘몄� 媛ㅻ윭由� 留뚮뱾湲�
                         function makeNgImageGallery() {
-                            var makeTag = '<ng-image-gallery images="images" thumb-size="80" bubbles="true" bubble-size="50" img-bubbles="true"></ng-image-gallery>';
+                            var makeTag = '<ng-image-gallery images="images" thumb-size="60" bubbles="true" bubble-size="50" img-bubbles="true"></ng-image-gallery>';
                             var linkFn = $compile(makeTag);
                             var content = linkFn($scope);
                             $element.append(content);
diff --git a/src/main/webapp/scripts/components/utils/issueSearchFieldKeyViewElement.directive.js b/src/main/webapp/scripts/components/utils/issueSearchFieldKeyViewElement.directive.js
index 92ee0a0..5c4ae0c 100644
--- a/src/main/webapp/scripts/components/utils/issueSearchFieldKeyViewElement.directive.js
+++ b/src/main/webapp/scripts/components/utils/issueSearchFieldKeyViewElement.directive.js
@@ -51,9 +51,14 @@
                                         break;
                                     }
                                 }
-
-                                makeTag += "<span ng-click='fn.remove(" + key.fieldKey + ")'>횞</span>";
-                                makeTag += "</p>";
+                                if (target.fieldValue !== null) {
+                                    makeTag += "<span></span>";
+                                    makeTag += "</p>";
+                                }
+                                else {
+                                    makeTag += "<span ng-click='fn.remove(" + key.fieldKey + ")'>횞</span>";
+                                    makeTag += "</p>";
+                                }
                             });
 
                             var linkFn = $compile(makeTag);
diff --git a/src/main/webapp/scripts/config.js b/src/main/webapp/scripts/config.js
index d118d3c..74ad46c 100644
--- a/src/main/webapp/scripts/config.js
+++ b/src/main/webapp/scripts/config.js
@@ -318,23 +318,25 @@
                 };
 
                 $rootScope.changeLastProject = function (projectId, reload = true) {
-                    if (User != null) {
-                        User.updateLastProject($resourceProvider.getContent(
-                            {lastProjectId: projectId},
-                            $resourceProvider.getPageContent(0, 0))).then(function (result) {
+                    if ($rootScope.workProject == null || $rootScope.workProject.id !== projectId ) {
+                        if (User != null) {
+                            User.updateLastProject($resourceProvider.getContent(
+                                {lastProjectId: projectId},
+                                $resourceProvider.getPageContent(0, 0))).then(function (result) {
 
-                            if (result.data.message.status === "success") {
-                                $rootScope.user = result.data.data;
-                                $rootScope.projects.forEach(function (el) {
-                                    if (el.id == projectId) {
-                                        $rootScope.workProject = el;
+                                if (result.data.message.status === "success") {
+                                    $rootScope.user = result.data.data;
+                                    $rootScope.projects.forEach(function (el) {
+                                        if (el.id == projectId) {
+                                            $rootScope.workProject = el;
 
-                                        $rootScope.$broadcast("changeLastProject", { id : el.id });
-                                    }
-                                });
-                                $state.go($state.current, {}, {reload: reload});
-                            }
-                        });
+                                            $rootScope.$broadcast("changeLastProject", {id: el.id});
+                                        }
+                                    });
+                                    $state.go($state.current, {}, {reload: reload});
+                                }
+                            });
+                        }
                     }
                 };
 
diff --git a/src/main/webapp/views/issue/issueDetail.html b/src/main/webapp/views/issue/issueDetail.html
index cad525e..765c919 100644
--- a/src/main/webapp/views/issue/issueDetail.html
+++ b/src/main/webapp/views/issue/issueDetail.html
@@ -192,6 +192,11 @@
                             <span class="issue-detail-label" style="position: relative; top: 1rem" ng-show="vm.viewer.issueCustomFields == ''">�궗�슜�옄 �젙�쓽 �븘�뱶媛믪씠 �뾾�뒿�땲�떎.</span>
                         </div>
                     </div>
+                    <div class="col-md-2">
+                        <issue-detail-image-preview images="vm.images"></issue-detail-image-preview>
+                    </div>
+                    <div ng-show="vm.images.length < 1" class="detail-not-elements width-100">
+                    </div>
                 </div>
                 <!-- �궗�슜�옄 �젙�쓽 �븘�뱶 -->
                 <div class="row">
@@ -256,13 +261,9 @@
                             </div>
                         </div>
                     </div>
-                    <div class="pdt0" style="position: relative; bottom: 1rem">
-                        <issue-detail-image-preview images="vm.images"></issue-detail-image-preview>
-                    </div>
-                    <div ng-show="vm.images.length < 1" class="detail-not-elements width-100">
-                        <span></span>
-                    </div>
+
                 </div>
+
 
                            <!-- <div class="">
                                 <label class="issue-detail-label"><span>{{vm.viewer.issueCustomFieldValueVos[0].useValue}}</span> </label>
@@ -312,7 +313,7 @@
                         <div class="form-group">
                             <div class="">
                                 <span translate="companyField.email">�씠硫붿씪</span>:
-                                <span class="email_color">{{vm.viewer.issueCompanyVos[0].email}}</span>
+                                <span class="email_color cursor" ng-click="fn.sendMail(vm.viewer.id, vm.viewer.projectVo.id)">{{vm.viewer.issueCompanyVos[0].email}}</span>
                             </div>
                         </div>
                     </div>
@@ -372,7 +373,7 @@
                         <div class="form-group">
                             <div class="">
                                 <span translate="ispField.email">�씠硫붿씪</span>:
-                                <span class="email_color">{{vm.viewer.issueIspVos[0].email}}</span>
+                                <span class="email_color cursor" ng-click="fn.sendMail(vm.viewer.id, vm.viewer.projectVo.id)">{{vm.viewer.issueIspVos[0].email}}</span>
                             </div>
                         </div>
                     </div>
@@ -433,7 +434,7 @@
                         <div class="form-group">
                             <div class="">
                                 <span translate="hostingField.email">�씠硫붿씪</span>:
-                                <span class="email_color">{{vm.viewer.issueHostingVos[0].email}}</span>
+                                <span class="email_color cursor" ng-click="fn.sendMail(vm.viewer.id, vm.viewer.projectVo.id)">{{vm.viewer.issueHostingVos[0].email}}</span>
                             </div>
                         </div>
                     </div>
diff --git a/src/main/webapp/views/issue/issueListNormal.html b/src/main/webapp/views/issue/issueListNormal.html
index 6fec887..7575114 100644
--- a/src/main/webapp/views/issue/issueListNormal.html
+++ b/src/main/webapp/views/issue/issueListNormal.html
@@ -38,9 +38,7 @@
                                             <!--    �봽濡쒖젥�듃    -->
                                             <issue-search-array-view-element lists="vm.projects"
                                                                              type="'project'"></issue-search-array-view-element>
-                                            <p ng-if="$root.isDefined(vm.projectKey)">
-                                                {{vm.projectKey}}
-                                            </p>
+
                                             <!--    �씠�뒋 ���엯   -->
                                             <issue-search-field-key-view-element lists="vm.issueTypes"
                                                                                  keys="vm.search.issueTypeIds"></issue-search-field-key-view-element>
@@ -48,16 +46,6 @@
                                             <!--    �씠�뒋 �긽�깭   -->
                                             <issue-search-field-key-view-element lists="vm.issueStatuses"
                                                                                  keys="vm.search.issueStatusIds"></issue-search-field-key-view-element>
-
-                                            <!--    �씠�뒋 踰덊샇   -->
-                                            <p ng-if="$root.isDefined(vm.issueNumber)">
-                                                {{vm.issueNumber}}
-                                            </p>
-
-                                            <p ng-if="$root.isDefined(vm.search.combinationIssueNumber)">
-                                                {{vm.search.combinationIssueNumber}}
-                                                <span ng-click="vm.search.combinationIssueNumber = ''">횞</span>
-                                            </p>
 
                                             <!--    �씠�뒋 �궡�슜   -->
                                             <p ng-if="$root.isDefined(vm.search.description)">
@@ -159,7 +147,9 @@
                                                                              options="::vm.issueStatuses"></ng-dropdown-multiselect>
                                                 </div>
                                             </div>
+                                        </div>
 
+                                        <div class="row">
                                             <div class="col-lg-3">
                                                 <div class="form-group">
                                                     <label> <span translate="issue.issueNumber">�씠�뒋 踰덊샇</span></label>
@@ -169,13 +159,10 @@
                                                            autocomplete="off"
                                                            kr-input
                                                            maxlength="20"
-                                                           ng-model="vm.issueNumber">
-<!--                                                           ng-model="vm.search.combinationIssueNumber">-->
+                                                           ng-model="vm.search.combinationIssueNumber">
                                                 </div>
                                             </div>
-                                        </div>
 
-                                        <div class="row">
                                             <div class="col-lg-3">
                                                 <div class="form-group">
                                                     <label> <span translate="issue.issueContent">�씠�뒋 �궡�슜</span></label>
@@ -206,20 +193,6 @@
                                                                              data-input-name="severities"
                                                                              selected-model="vm.search.severityIds"
                                                                              options="::vm.severities"></ng-dropdown-multiselect>
-                                                </div>
-                                            </div>
-
-                                            <div class="col-lg-3">
-                                                <div class="form-group">
-                                                    <label> <span translate="common.assigneeTeam">�떞�떦遺��꽌</span></label>
-                                                    <js-autocomplete-multi data-input-name="departments"
-                                                                           selected-model="vm.departments"
-                                                                           search="vm.departmentName"
-                                                                           input-disabled="false"
-                                                                           source="fn.getUserDepartmentList(vm.departmentName, vm.departments)"
-                                                                           translation-texts="{ count : 'common.userNum', empty : 'common.emptyProjectDepartment' }"
-                                                                           extra-settings="{ displayProp : 'byName' , idProp : 'id', widthable : false, width : '', imageable : true, imagePathProp : 'profile', type : 'department', maxlength : 100 }">
-                                                    </js-autocomplete-multi>
                                                 </div>
                                             </div>
                                         </div>
@@ -280,6 +253,20 @@
                                                 </div>
                                             </div>
 
+                                            <div class="col-lg-3">
+                                                <div class="form-group">
+                                                    <label> <span translate="common.assigneeTeam">�떞�떦遺��꽌</span></label>
+                                                    <js-autocomplete-multi data-input-name="departments"
+                                                                           selected-model="vm.departments"
+                                                                           search="vm.departmentName"
+                                                                           input-disabled="false"
+                                                                           source="fn.getUserDepartmentList(vm.departmentName, vm.departments)"
+                                                                           translation-texts="{ count : 'common.userNum', empty : 'common.emptyProjectDepartment' }"
+                                                                           extra-settings="{ displayProp : 'byName' , idProp : 'id', widthable : false, width : '', imageable : true, imagePathProp : 'profile', type : 'department', maxlength : 100 }">
+                                                    </js-autocomplete-multi>
+                                                </div>
+                                            </div>
+
                                             <div class="col-lg-3" ng-repeat="customField in vm.customFields">
                                                 <label>{{::customField.name}}</label>
 
diff --git a/src/main/webapp/views/issue/issueSendMail.html b/src/main/webapp/views/issue/issueSendMail.html
index d9998ff..4eb604b 100644
--- a/src/main/webapp/views/issue/issueSendMail.html
+++ b/src/main/webapp/views/issue/issueSendMail.html
@@ -20,21 +20,97 @@
                                       ng-click="fn.removeManager($index)">횞</span>
                             </span>
                 </div>
-                <js-input-autocomplete data-input-name="users"
-                                       owl-auto-focus
-                                       target=".auto-complete-input"
-                                       selected-model="vm.form.users"
-                                       search="vm.userName"
-                                       page="vm.autoCompletePage.user.page"
-                                       total-page="vm.autoCompletePage.user.totalPage"
-                                       source="fn.getUserList(vm.userName, vm.form.users, vm.autoCompletePage.user.page, fn.getUserListCallBack)"
-                                       translation-texts="{ empty : 'common.emptyUser'}"
-                                       input-disabled="vm.form.projects.length == 0"
-                                       extra-settings="{ displayProp : 'byName' , idProp : 'id', imageable : true, imagePathProp : 'profile',
-                                               type : 'user', maxlength : 100, autoResize : true, stopRemoveBodyEvent : true }"></js-input-autocomplete>
+
+<!--                <js-autocomplete-single data-input-name="issue"-->
+<!--                                        selected-model="vm.form.issues"-->
+<!--                                        search="vm.issueName"-->
+<!--                                        source="fn.getIssueList(vm.issueName, vm.issueTypeId, vm.form.issues, vm.autoCompletePage.issue.page, fn.getIssueListCallBack)"-->
+<!--                                        page="vm.autoCompletePage.issue.page"-->
+<!--                                        total-page="vm.autoCompletePage.issue.totalPage"-->
+<!--                                        input-disabled="false"-->
+<!--                                        translation-texts="{ empty : 'common.emptyIssue' }"-->
+<!--                                        extra-settings="{ displayProp : 'title' , idProp : 'id', imageable : false, imagePathProp : '',-->
+<!--                                            type : '', maxlength : 200, autoResize : true, stopRemoveBodyEvent : true }"></js-autocomplete-single>-->
+
+<!--                {{vm.form.companyFieldsEmail}}-->
+                <label><span>�삊�젰�궗 硫붿씪</span></label>
+                <js-autocomplete-multi data-input-name="partnersEmail"
+                                       selected-model="vm.form.partnersEmail"
+                                       search="vm.partnerName"
+                                       source="fn.getMailTargetAll(vm.form.partnersEmail)"
+                                       input-disabled="false"
+                                       page="vm.autoCompletePage.partnersMail.page"
+                                       total-page="vm.autoCompletePage.partnersMail.totalPage"
+                                       modal-form-auto-scroll
+                                       extra-settings="{ displayProp : 'byName' , idProp : 'email', imageable : false, maxlength : 100, autoResize : true }"></js-autocomplete-multi>
+
+<!--                <ng-dropdown-multiselect class="multiSelect cursor"-->
+<!--                                         data-input-name="companyFieldsEmail"-->
+<!--                                         selected-model="vm.form.companyFieldsEmail.concat(vm.form.ispFieldsEmail,vm.form.hostingFieldsEmail)"-->
+<!--                                         extra-settings="{ stringTypeOption : true }"-->
+<!--                                         options="vm.options.companyFieldsEmail"></ng-dropdown-multiselect>-->
+
+<!--                <span class="issue-detail-label" translate="companyField.info"></span>-->
+<!--                <input ng-if="vm.form.companyFieldsEmail != null"-->
+<!--                       type="text"-->
+<!--                       class="form-control"-->
+<!--                       kr-input-->
+<!--                       autocomplete="off"-->
+<!--                       ng-model="vm.form.companyFieldsEmail">-->
+
+<!--                <span class="issue-detail-label" translate="ispField.info"></span>-->
+<!--                <input ng-if="vm.form.ispFieldsEmail != null"-->
+<!--                       type="text"-->
+<!--                       class="form-control"-->
+<!--                       kr-input-->
+<!--                       autocomplete="off"-->
+<!--                       ng-model="vm.form.ispFieldsEmail">-->
+
+<!--                <span class="issue-detail-label" translate="hostingField.info"></span>-->
+<!--                <input ng-if="vm.form.hostingFieldsEmail != null"-->
+<!--                       type="text"-->
+<!--                       class="form-control"-->
+<!--                       kr-input-->
+<!--                       autocomplete="off"-->
+<!--                       ng-model="vm.form.hostingFieldsEmail">-->
+
+<!--                <a style="display: flex; text-align: center; justify-content: center;">-->
+<!--                    <i class="os-icon os-icon-email-forward mr-20 mt-20 cursor" ng-click="fn.formSubmit(data.id)">1</i>-->
+<!--                    <i class="os-icon os-icon-email-forward mr-20 mt-20 cursor" ng-click="fn.formSubmit(data.id)">2</i>-->
+<!--                    <i class="os-icon os-icon-email-forward mr-20 mt-20 cursor" ng-click="fn.formSubmit(data.id)">3</i>-->
+<!--                </a>-->
+<!--                <div class="modal-content">-->
+<!--                    <a style="display: flex; text-align: center; justify-content: center;">-->
+<!--                        <img onclick="emailTemplate()" id="template1" class="cursor" src="assets/images/btn_facebook.png">-->
+<!--                        <img onclick="emailTemplate()" id="template2" class="cursor" src="assets/images/btn_kakao.png">-->
+<!--                        <img onclick="emailTemplate()" id="template3" class="cursor" src="assets/images/btn_google.png">-->
+<!--                    </a>-->
+<!--                </div>-->
+<!--                <form action="${pageContext.request.contextPath}/updatetNoticePro.do" method="post" enctype="multipart/form-data" name="noticeForm">-->
+<!--                    <input type='file' id="filename" name="filename"/>-->
+<!--                    <img id="preImage" src="${pageContext.request.contextPath}/saveFile/${noticeVO.filename}" alt="image_title" onerror='this.src="${pageContext.request.contextPath}/images/no_img.jpg"'/>-->
+<!--                </form>-->
+
             </div>
         </form>
+
     </div>
+<!--    <h6>{{vm.form.companyFieldsEmail}}</h6>-->
+<!--   <span>{{vm.form.id}}===============</span>-->
+<!--   <h6>{{vm.form.ispFieldsEmail}}===============</h6>-->
+<!--   <span>===============</span>-->
+<!--   <h6>{{vm.form.hostingFieldsEmail}}</h6>-->
+<!--    <div style="display: flex; text-align: center">-->
+<!--            �뀥�뵆由�1-->
+<!--        </div>-->
+<!--        <div style="border: 1px #111111; margin-left: 20px; box-sizing: border-box;">-->
+<!--            �뀥�뵆由�2-->
+    <!--        <div style="border: 1px #111111; margin-left: 20px; box-sizing: border-box;">-->
+<!--        </div>-->
+<!--        <div style="border: 1px #111111; margin-left: 20px; box-sizing: border-box;">-->
+<!--            �뀥�뵆由�3-->
+<!--        </div>-->
+<!--    </div>-->
 
     <div class="modal-footer buttons-on-right">
         <button type="button" class="btn btn-md btn-grey" ng-click="fn.cancel()"><span
@@ -45,3 +121,24 @@
         </button>
     </div>
 </div>
+
+<script>
+
+    function emailTemplate() {
+        $('#template1').click(function() {
+            $('#template1').hide();
+        });
+
+        $('#template2').click(function() {
+            $('#template2').hide();
+        });
+
+        $('#template3').click(function() {
+            $('#template3').hide();
+        });
+    }
+
+    function changeIMG() {
+        $('#template1').attr("src", "assets/images/previewTemplate.png")
+    }
+</script>
\ No newline at end of file

--
Gitblit v1.8.0