From a42f592181c23cb1d84d5b66b4b165aa855d57e2 Mon Sep 17 00:00:00 2001
From: jhjang <jhjang@maprex.co.kr>
Date: 수, 01 12월 2021 11:06:42 +0900
Subject: [PATCH] - api 이슈 추가 기능 완료

---
 src/main/java/kr/wisestone/owl/web/controller/ApiController.java                  |   35 ++
 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                  |    5 
 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/java/kr/wisestone/owl/service/impl/AttachedFileServiceImpl.java          |   29 ++
 src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java                 |  166 +++++++++--
 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/service/impl/ApiTokenServiceImpl.java              |   33 ++
 src/main/java/kr/wisestone/owl/service/IssueDepartmentService.java                |    3 
 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/java/kr/wisestone/owl/service/impl/IssueCustomFieldValueServiceImpl.java |   23 +
 src/main/java/kr/wisestone/owl/service/AttachedFileService.java                   |    2 
 src/main/java/kr/wisestone/owl/service/IssueService.java                          |    7 
 src/main/java/kr/wisestone/owl/web/form/IssueApiForm.java                         |  195 +++++++++++++
 src/main/java/kr/wisestone/owl/service/impl/IssueDepartmentServiceImpl.java       |   17 
 src/main/java/kr/wisestone/owl/web/form/IssueCustomFieldValueForm.java            |   24 +
 31 files changed, 722 insertions(+), 72 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..0ae4c64 100644
--- a/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java
+++ b/src/main/java/kr/wisestone/owl/config/SecurityConfiguration.java
@@ -1,6 +1,7 @@
 package kr.wisestone.owl.config;
 
 import kr.wisestone.owl.config.security.filter.AjaxSessionExpiredFilter;
+import kr.wisestone.owl.config.security.filter.CustomAuthenticationFilter;
 import kr.wisestone.owl.config.security.handler.AjaxAuthenticationEntryPoint;
 import kr.wisestone.owl.config.security.handler.AjaxAuthenticationFailureHandler;
 import kr.wisestone.owl.config.security.handler.AjaxAuthenticationSuccessHandler;
@@ -22,6 +23,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 +145,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 3672534..8b86bd5 100644
--- a/src/main/java/kr/wisestone/owl/constant/MsgConstants.java
+++ b/src/main/java/kr/wisestone/owl/constant/MsgConstants.java
@@ -149,6 +149,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";   //  �궗�슜�옄�뒗 �솢�꽦 �긽�깭媛� �븘�땲硫� 濡쒓렇�씤�븷 �닔 �뾾�뒿�땲�떎.
@@ -226,4 +227,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 4133115..e67a094 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);
@@ -1125,12 +1233,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 +1594,7 @@
         }
 
         //  �씠�뒋 �깮�꽦, �궘�젣�떆 �삁�빟 �씠硫붿씪�뿉 �벑濡앺빐�넃�뒗�떎.
-        this.reservationIssueEmail(issue.getId(), EmailType.ISSUE_REMOVE);
+        this.reservationIssueEmail(issue, EmailType.ISSUE_REMOVE);
         //  �씠�뒋 �궘�젣
         this.issueRepository.delete(issue);
 
@@ -2502,7 +2612,7 @@
         //  �궗�슜�옄 �떆�뒪�뀥 湲곕뒫 �궗�슜 �젙蹂� �닔吏�
         log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_ANOTHER_USER_SEND_EMAIL));
 
-        this.systemEmailService.directEmail(issueForm.getSendEmails().toArray(new String[issueForm.getSendEmails().size()]), EmailType.ISSUE_SEND, issueMap, null); //mailType.ISSUE_SEND - �뀥�뵆由� �꽕�젙
+        this.systemEmailService.directEmail(issueForm.getSendEmails().toArray(new String[issueForm.getSendEmails().size()]), EmailType.ISSUE_SEND, issueMap, null);
     }
 
     //  �삁�빟 諛쒖깮 �씠�뒋瑜� �떎�뻾�븳�떎
@@ -2510,7 +2620,6 @@
     @Transactional
     public void reservationIssue() {
         List<IssueReservation> issueReservations = this.issueReservationService.findByIssueReservationTypeNotNull();
-
 
         Calendar calendar = Calendar.getInstance();
         int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
@@ -2675,18 +2784,13 @@
     @Override
     public void modifyParentIssue(IssueForm issueDownForm) {
         Issue issue = this.getIssue(issueDownForm.getId()); //�븯�쐞 �씠�뒋
-        Long newParentIssueId = issueDownForm.getParentIssueId(); //蹂�寃쏀븷 �븯�쐞�씠�뒋�쓽 �긽�쐞�씠�뒋
+        Issue parentIssue = issue.getParentIssue(); //蹂�寃� �쟾 �븯�쐞�씠�뒋�쓽 �긽�쐞�씠�뒋
 
+        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());
-        }
-
         if (newParentIssueId != null) { // 異붽� �븷 寃쎌슦
-           // todo �씠�쟾 �븯�쐞 �씪媛� �엳�뒪�넗由ш린濡� �븘�슂
+            // todo �씠�쟾 �븯�쐞 �씪媛� �엳�뒪�넗由ш린濡� �븘�슂
             parentIssue = this.getIssue(newParentIssueId); //�긽�쐞�씠�뒋(myIssue)
             issue.setParentIssue(parentIssue); //myIssue瑜� �븯�쐞�씠�뒋�쓽 �긽�쐞�씠�뒋濡� set
             this.issueHistoryService.detectDownIssues(IssueHistoryType.ADD, issue, sb); //issue = �븯�쐞�씠�뒋
@@ -2716,7 +2820,7 @@
         }
     }
 
-    @Override
+    /*@Override
     @Transactional
     public void findMailTargetAll(Map<String, Object> resJsonData, IssueCondition condition, Pageable pageable) {
         IssueVo issueVo = new IssueVo();
@@ -2752,7 +2856,7 @@
         resJsonData.put(Constants.RES_KEY_CONTENTS, emailList);
         resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
                 totalPage, totalCount));
-    }
+    }*/
 
     /*@Override
     @Transactional
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..5d4c383 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,15 @@
 package kr.wisestone.owl.web.controller;
 
 import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.Issue;
 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,6 +18,7 @@
 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;
@@ -20,22 +27,36 @@
 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 Exception {
         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 436a3b5..0cbcbda 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 9f3a6cc..bf58d29 100644
--- a/src/main/java/kr/wisestone/owl/web/form/IssueForm.java
+++ b/src/main/java/kr/wisestone/owl/web/form/IssueForm.java
@@ -40,7 +40,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() {
     }
@@ -110,6 +112,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;
     }
@@ -333,4 +340,20 @@
     public void setParentIssueId(Long parentIssueId) {
         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;
+    }
 }
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>

--
Gitblit v1.8.0