diff --git a/src/main/java/com/eactive/apim/portal/apps/app/controller/MyAppController.java b/src/main/java/com/eactive/apim/portal/apps/app/controller/MyAppController.java index 0a013d8..c5c6a3c 100644 --- a/src/main/java/com/eactive/apim/portal/apps/app/controller/MyAppController.java +++ b/src/main/java/com/eactive/apim/portal/apps/app/controller/MyAppController.java @@ -645,14 +645,8 @@ public class MyAppController { return new ModelAndView("redirect:/myapikey/register/step1"); } - // API 선택 검증 - if (selectedApis == null || selectedApis.isEmpty()) { - redirectAttributes.addFlashAttribute("error", "최소 1개 이상의 API를 선택해주세요."); - return new ModelAndView("redirect:/myapikey/register/step2"); - } - - // 선택된 API를 세션에 저장 - registration.setSelectedApis(selectedApis); + // API 선택은 선택 사항 — 미선택(빈 목록)도 허용한다. + registration.setSelectedApis(selectedApis != null ? selectedApis : new ArrayList<>()); // 등록이 완료되었는지 최종 검증 if (!registration.isComplete()) { diff --git a/src/main/java/com/eactive/apim/portal/apps/app/dto/ApiKeyRegistrationDTO.java b/src/main/java/com/eactive/apim/portal/apps/app/dto/ApiKeyRegistrationDTO.java index 2672c3a..41325a5 100644 --- a/src/main/java/com/eactive/apim/portal/apps/app/dto/ApiKeyRegistrationDTO.java +++ b/src/main/java/com/eactive/apim/portal/apps/app/dto/ApiKeyRegistrationDTO.java @@ -99,6 +99,7 @@ public class ApiKeyRegistrationDTO implements Serializable { } public boolean isComplete() { - return isStep1Complete() && isStep2Complete(); + // API 선택은 선택 사항이므로 기본 정보(Step1)만 완료되면 등록 가능하다. + return isStep1Complete(); } } diff --git a/src/main/java/com/eactive/apim/portal/apps/app/dto/AppRequestDTO.java b/src/main/java/com/eactive/apim/portal/apps/app/dto/AppRequestDTO.java index 2889f4c..adec83c 100644 --- a/src/main/java/com/eactive/apim/portal/apps/app/dto/AppRequestDTO.java +++ b/src/main/java/com/eactive/apim/portal/apps/app/dto/AppRequestDTO.java @@ -9,7 +9,6 @@ import com.eactive.apim.portal.apps.user.dto.PortalOrgDTO; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; -import javax.validation.constraints.NotEmpty; import lombok.Data; @Data @@ -25,8 +24,7 @@ public class AppRequestDTO { private ApprovalDTO approval; - @NotEmpty - private String apiList = ""; //comma separated api id list + private String apiList = ""; //comma separated api id list (미선택 허용) private String apiGroupList = ""; //comma separated api group id list diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 76c9d79..c926b0e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -361,7 +361,7 @@ page: name: "이용자 정보" path: "/users/detail" apikey: - name: "인증키 관리" + name: "앱 관리" path: "/myapikey" app_request_detail: name: "인증키 신청 상세" diff --git a/src/main/resources/static/js/api-selector.js b/src/main/resources/static/js/api-selector.js index 5e12991..aff6473 100644 --- a/src/main/resources/static/js/api-selector.js +++ b/src/main/resources/static/js/api-selector.js @@ -447,18 +447,42 @@ document.addEventListener('DOMContentLoaded', function() { }); } - // Form validation + // Form submit + // - Webhook/앱 변경은 API 가 필수(form data-api-required=true)이므로 미선택 시 차단한다. + // - 앱 신청(myapikey register)은 선택 사항이라, 미선택 시 확인 팝업으로 한 번 더 묻고 진행한다. form.addEventListener('submit', function(e) { - if (selectedApis.size === 0) { + if (selectedApis.size !== 0) { + syncHiddenSelected(); + return; + } + + var apiRequired = form.dataset.apiRequired === 'true'; + if (apiRequired) { e.preventDefault(); - if (window.customPopups && customPopups.showAlert) { + if (typeof customPopups !== 'undefined' && customPopups.showAlert) { customPopups.showAlert('최소 1개 이상의 API를 선택해주세요.'); } else { alert('최소 1개 이상의 API를 선택해주세요.'); } return false; } - syncHiddenSelected(); + + // 미선택 상태로 앱 신청 → 확인 후 진행 + e.preventDefault(); + var message = '선택한 API가 없습니다. API 없이 앱을 신청하시겠습니까?'; + // custom-popups.js 는 top-level const 라 window.customPopups 가 아닌 렉시컬 전역이다. + if (typeof customPopups !== 'undefined' && customPopups.showConfirm) { + customPopups.showConfirm(message, function (ok) { + if (ok) { + syncHiddenSelected(); + form.submit(); // submit() 은 submit 이벤트를 재발생시키지 않음 → 재확인 루프 없음 + } + }); + } else if (window.confirm(message)) { + syncHiddenSelected(); + form.submit(); + } + return false; }); // Initialize diff --git a/src/main/resources/templates/views/fragment/api_selector.html b/src/main/resources/templates/views/fragment/api_selector.html index 984685e..a282248 100644 --- a/src/main/resources/templates/views/fragment/api_selector.html +++ b/src/main/resources/templates/views/fragment/api_selector.html @@ -56,7 +56,7 @@