From dfcfb0ac0b43f3820fadb972e3e5f82992998e3d Mon Sep 17 00:00:00 2001 From: "Rinjae(gf63)" Date: Tue, 15 Sep 2026 13:42:17 +0900 Subject: [PATCH] =?UTF-8?q?-=20=EC=95=BD=EA=B4=80=20=EC=A2=85=EB=A5=98=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=97=90=20'=EC=A0=84=EC=B2=B4=20=EB=8F=99?= =?UTF-8?q?=EC=9D=98'=20=EC=98=B5=EC=85=98=20=EC=B6=94=EA=B0=80=20-=20'?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EB=8F=99=EC=9D=98'=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C/=EC=A0=80=EC=9E=A5=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EB=B0=8F=20=ED=99=94=EB=A9=B4=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../portalterms/portalTermsTypeManPopup.jsp | 22 ++- .../portalterms/PortalTermsManController.java | 13 +- .../PortalTermsTypeConfigService.java | 145 ++++++------------ 3 files changed, 79 insertions(+), 101 deletions(-) diff --git a/WebContent/jsp/onl/apim/portalterms/portalTermsTypeManPopup.jsp b/WebContent/jsp/onl/apim/portalterms/portalTermsTypeManPopup.jsp index b80f855..6ebe1ad 100644 --- a/WebContent/jsp/onl/apim/portalterms/portalTermsTypeManPopup.jsp +++ b/WebContent/jsp/onl/apim/portalterms/portalTermsTypeManPopup.jsp @@ -55,6 +55,7 @@ data: {cmd: 'TYPE_CONFIG'}, success: function(json) { termsTypeRows = json.typeRows || []; + $("#agreeAllEnabled").prop('checked', json.agreeAllEnabled === true); renderTermsTypeConfig(); }, error: function(e) { @@ -134,7 +135,8 @@ $.ajax({ type: "POST", url: url, - data: {cmd: 'TYPE_CONFIG_SAVE', enabledTypes: enabledTypes.join(','), displayTypes: displayTypes.join(',')}, + data: {cmd: 'TYPE_CONFIG_SAVE', enabledTypes: enabledTypes.join(','), displayTypes: displayTypes.join(','), + agreeAllEnabled: $("#agreeAllEnabled").is(':checked')}, success: function() { alert("저장되었습니다."); // 목록 화면의 '(사용안함)' 표기가 바뀌므로 부모 화면을 다시 읽도록 알린다 @@ -188,6 +190,24 @@ + + 회원가입 전체 동의 + + +
+ 사용 : 회원가입(개인/법인) 약관 동의에 '전체 동의' 체크박스를 노출하고, 항목별로 끝까지 스크롤하지 않아도 동의할 수 있습니다.
+ 미사용 : '전체 동의' 체크박스를 노출하지 않고, 각 약관을 펼쳐 끝까지 읽어야 개별 동의할 수 있습니다. +
+ + 약관 페이지 노출 diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/portalterms/PortalTermsManController.java b/src/main/java/com/eactive/eai/rms/onl/apim/portalterms/PortalTermsManController.java index bba5fcd..0fe12b0 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/portalterms/PortalTermsManController.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/portalterms/PortalTermsManController.java @@ -79,24 +79,27 @@ public class PortalTermsManController extends BaseAnnotationController { return ResponseEntity.ok(resultMap); } - /** 약관 종류 관리(사용 여부 / 약관 페이지 노출 순서) 조회 */ + /** 약관 종류 관리(사용 여부 / 약관 페이지 노출 순서 / 전체 동의 허용) 조회 */ @PostMapping(value = "/onl/apim/portalterms/portalTermsMan.json", params = "cmd=TYPE_CONFIG") public ResponseEntity> selectTypeConfig() { Map resultMap = new HashMap<>(); resultMap.put("typeRows", portalTermsTypeConfigService.loadTypeConfig()); + resultMap.put("agreeAllEnabled", portalTermsTypeConfigService.isAgreeAllEnabled()); return ResponseEntity.ok(resultMap); } /** * 약관 종류 관리 저장. * - * @param enabledTypes 사용할 약관 종류 코드(쉼표 구분) - * @param displayTypes 약관 페이지에 배치한 약관 종류 코드(쉼표 구분, 기재 순서 = 노출 순서) + * @param enabledTypes 사용할 약관 종류 코드(쉼표 구분) + * @param displayTypes 약관 페이지에 배치한 약관 종류 코드(쉼표 구분, 기재 순서 = 노출 순서) + * @param agreeAllEnabled 회원가입 약관 동의에서 '전체 동의' 허용 여부 */ @PostMapping(value = "/onl/apim/portalterms/portalTermsMan.json", params = "cmd=TYPE_CONFIG_SAVE") public ResponseEntity saveTypeConfig(@RequestParam(value = "enabledTypes", required = false) String enabledTypes, - @RequestParam(value = "displayTypes", required = false) String displayTypes) { - portalTermsTypeConfigService.saveTypeConfig(enabledTypes, displayTypes); + @RequestParam(value = "displayTypes", required = false) String displayTypes, + @RequestParam(value = "agreeAllEnabled", defaultValue = "false") boolean agreeAllEnabled) { + portalTermsTypeConfigService.saveTypeConfig(enabledTypes, displayTypes, agreeAllEnabled); return ResponseEntity.ok().build(); } diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/portalterms/PortalTermsTypeConfigService.java b/src/main/java/com/eactive/eai/rms/onl/apim/portalterms/PortalTermsTypeConfigService.java index 336635f..6a71c2e 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/portalterms/PortalTermsTypeConfigService.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/portalterms/PortalTermsTypeConfigService.java @@ -1,11 +1,11 @@ package com.eactive.eai.rms.onl.apim.portalterms; import com.eactive.apim.portal.agreements.entity.AgreementType; +import com.eactive.apim.portal.agreements.service.AgreementTypeConfigService; import com.eactive.apim.portal.portalproperty.entity.PortalProperty; import com.eactive.apim.portal.portalproperty.entity.PortalPropertyGroup; import com.eactive.apim.portal.portalproperty.entity.PortalPropertyId; import com.eactive.apim.portal.portalproperty.repository.PortalPropertyGroupRepository; -import com.eactive.apim.portal.portalproperty.service.PortalPropertyService; import com.eactive.eai.rms.common.base.BaseService; import com.eactive.eai.rms.onl.common.exception.BizException; import lombok.RequiredArgsConstructor; @@ -15,7 +15,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Arrays; -import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -25,68 +24,42 @@ import java.util.Set; import java.util.stream.Collectors; /** - * 약관 종류 운영 설정. PTL_PROPERTY(그룹 {@code Portal})로 관리한다. + * 약관 종류 운영 설정의 관리 화면용 조회/저장. * - * - * - * - * - * - * - *
PTL_PROPERTY (group = Portal)
{@code portal.terms.enabled-types}시스템에서 사용할 약관 종류. 쉼표 구분, 순서 무의미
{@code portal.terms.display-types}약관 페이지에 노출할 약관 종류. 쉼표 구분, 기재 순서가 노출 순서
- * - *

노출(display) 목록은 항상 사용(enabled) 목록의 부분집합이어야 한다. - * 알 수 없는 코드는 읽을 때 무시한다(enum 항목이 제거돼도 화면이 깨지지 않도록). + *

값의 의미와 프로퍼티 키는 {@link AgreementTypeConfigService} 참고 — 읽기는 그쪽에 위임하고 + * (admin·portal 이 같은 해석을 쓰도록) 이 클래스는 관리 화면 표현과 저장만 담당한다. * *

PTL_PROPERTY 는 (PROPERTY_GROUP_NAME, PROPERTY_NAME) 에 유니크 제약이 없어 중복 행이 존재할 수 있다 - * ({@code PortalPropertyDuplicateChecker} 참고). 따라서 단건 조회({@code findById})는 쓰지 않고 - * 그룹 단위로 읽고 쓴다. 조회 시에는 값이 없어도 프로퍼티를 만들지 않으며(동시 요청이 각각 행을 만드는 것을 막는다) - * 기본값을 메모리에서만 사용하고, 실제 행은 관리 화면에서 저장할 때 생성된다. + * ({@code PortalPropertyDuplicateChecker} 참고). 따라서 저장도 단건 조회({@code findById}) 없이 + * 그룹의 프로퍼티 컬렉션을 통해 처리한다. */ @Service @RequiredArgsConstructor @Transactional(transactionManager = "transactionManagerForEMS") public class PortalTermsTypeConfigService extends BaseService { - static final String PROPERTY_GROUP = "Portal"; - static final String ENABLED_TYPES_KEY = "portal.terms.enabled-types"; - static final String DISPLAY_TYPES_KEY = "portal.terms.display-types"; - private static final String ENABLED_TYPES_DESC = "시스템에서 사용할 약관 종류(쉼표 구분, AgreementType 코드)"; private static final String DISPLAY_TYPES_DESC = "포털 약관 페이지에 노출할 약관 종류(쉼표 구분, 기재 순서가 노출 순서)"; + private static final String AGREE_ALL_ENABLED_DESC = + "회원가입 약관 동의에서 '전체 동의' 허용 여부(true/false). false 면 항목별로 끝까지 읽고 개별 동의"; - /** 프로퍼티 미설정 시 기본값 — 현재 운영중인 약관 종류 */ - private static final List DEFAULT_TYPES = Arrays.asList( - AgreementType.TERMS_OF_USE, - AgreementType.PRIVACY_COLLECT, - AgreementType.NOTIFICATION_CONSENT); - - private final PortalPropertyService portalPropertyService; + private final AgreementTypeConfigService agreementTypeConfigService; private final PortalPropertyGroupRepository portalPropertyGroupRepository; /** 시스템에서 사용하는 약관 종류 */ public Set getEnabledTypes() { - Set enabled = EnumSet.noneOf(AgreementType.class); - enabled.addAll(readTypes(readGroupProperties(), ENABLED_TYPES_KEY)); - return enabled; + return agreementTypeConfigService.getEnabledTypes(); } - /** 약관 페이지 노출 약관 종류(노출 순서대로) */ - public List getDisplayTypes() { - Map properties = readGroupProperties(); - List enabled = readTypes(properties, ENABLED_TYPES_KEY); - return readTypes(properties, DISPLAY_TYPES_KEY).stream() - .filter(enabled::contains) - .collect(Collectors.toList()); + /** 회원가입 약관 동의에서 '전체 동의' 허용 여부 */ + public boolean isAgreeAllEnabled() { + return agreementTypeConfigService.isAgreeAllEnabled(); } /** 약관 종류 관리 화면 행 목록 (AgreementType 선언 순서) */ public List loadTypeConfig() { - Map properties = readGroupProperties(); - List enabled = readTypes(properties, ENABLED_TYPES_KEY); - List display = readTypes(properties, DISPLAY_TYPES_KEY).stream() - .filter(enabled::contains) - .collect(Collectors.toList()); + Set enabled = agreementTypeConfigService.getEnabledTypes(); + List display = agreementTypeConfigService.getDisplayTypes(); return Arrays.stream(AgreementType.values()) .map(type -> { @@ -104,10 +77,11 @@ public class PortalTermsTypeConfigService extends BaseService { /** * 약관 종류 설정 저장. * - * @param enabledCodes 사용할 약관 종류 코드 목록(쉼표 구분) - * @param displayCodes 약관 페이지 노출 약관 종류 코드 목록(쉼표 구분, 기재 순서 = 노출 순서) + * @param enabledCodes 사용할 약관 종류 코드 목록(쉼표 구분) + * @param displayCodes 약관 페이지 노출 약관 종류 코드 목록(쉼표 구분, 기재 순서 = 노출 순서) + * @param agreeAllEnabled 회원가입 약관 동의에서 '전체 동의' 허용 여부 */ - public void saveTypeConfig(String enabledCodes, String displayCodes) { + public void saveTypeConfig(String enabledCodes, String displayCodes, boolean agreeAllEnabled) { List enabled = parseStrict(enabledCodes); List display = parseStrict(displayCodes); @@ -120,35 +94,13 @@ public class PortalTermsTypeConfigService extends BaseService { } Map values = new LinkedHashMap<>(); - values.put(ENABLED_TYPES_KEY, join(enabled)); - values.put(DISPLAY_TYPES_KEY, join(display)); + values.put(AgreementTypeConfigService.ENABLED_TYPES_KEY, join(enabled)); + values.put(AgreementTypeConfigService.DISPLAY_TYPES_KEY, join(display)); + values.put(AgreementTypeConfigService.AGREE_ALL_ENABLED_KEY, String.valueOf(agreeAllEnabled)); writeGroupProperties(values); - logger.info("약관 종류 설정 저장 - 사용: [{}], 노출: [{}]", join(enabled), join(display)); - } - - /** {@code Portal} 그룹 프로퍼티 전체를 읽는다(중복 행이 있어도 예외 없이 마지막 값을 쓴다). */ - private Map readGroupProperties() { - return portalPropertyService.getPortalPropertiesAsMap(PROPERTY_GROUP); - } - - /** 프로퍼티 값을 약관 종류 목록으로 변환. 미설정이면 기본값, 알 수 없는 코드는 무시한다. */ - private List readTypes(Map properties, String propertyName) { - String value = properties.get(propertyName); - if (value == null) { - value = join(DEFAULT_TYPES); - } - - List types = new ArrayList<>(); - for (String code : splitCodes(value)) { - try { - types.add(AgreementType.valueOf(code)); - } catch (IllegalArgumentException e) { - logger.warn("PTL_PROPERTY {}/{} 에 알 수 없는 약관 종류 코드가 있어 무시합니다. : {}", - PROPERTY_GROUP, propertyName, code); - } - } - return new ArrayList<>(new LinkedHashSet<>(types)); + logger.info("약관 종류 설정 저장 - 사용: [{}], 노출: [{}], 전체 동의: {}", + join(enabled), join(display), agreeAllEnabled); } /** @@ -158,11 +110,12 @@ public class PortalTermsTypeConfigService extends BaseService { * (중복 행이 이미 있더라도 갱신 UPDATE 는 같은 키의 모든 행에 적용되므로 값이 어긋나지 않는다.) */ private void writeGroupProperties(Map values) { - PortalPropertyGroup group = portalPropertyGroupRepository.findByIdWithProperties(PROPERTY_GROUP) - .orElseThrow(() -> new BizException("PTL_PROPERTY 프로퍼티 그룹이 없습니다. : " + PROPERTY_GROUP)); + String group = AgreementTypeConfigService.PROPERTY_GROUP; + PortalPropertyGroup propertyGroup = portalPropertyGroupRepository.findByIdWithProperties(group) + .orElseThrow(() -> new BizException("PTL_PROPERTY 프로퍼티 그룹이 없습니다. : " + group)); values.forEach((propertyName, propertyValue) -> { - Optional existing = group.getPortalProperties().stream() + Optional existing = propertyGroup.getPortalProperties().stream() .filter(property -> property.getId() != null && propertyName.equals(property.getId().getPropertyName())) .findFirst(); @@ -175,44 +128,46 @@ public class PortalTermsTypeConfigService extends BaseService { } } else { PortalProperty property = new PortalProperty(); - property.setId(new PortalPropertyId(PROPERTY_GROUP, propertyName)); + property.setId(new PortalPropertyId(group, propertyName)); property.setPropertyValue(propertyValue); property.setPropertyDesc(describe(propertyName)); - property.setPortalPropertyGroup(group); - group.getPortalProperties().add(property); + property.setPortalPropertyGroup(propertyGroup); + propertyGroup.getPortalProperties().add(property); } }); - portalPropertyGroupRepository.save(group); + portalPropertyGroupRepository.save(propertyGroup); } private static String describe(String propertyName) { - return DISPLAY_TYPES_KEY.equals(propertyName) ? DISPLAY_TYPES_DESC : ENABLED_TYPES_DESC; + if (AgreementTypeConfigService.DISPLAY_TYPES_KEY.equals(propertyName)) { + return DISPLAY_TYPES_DESC; + } + if (AgreementTypeConfigService.AGREE_ALL_ENABLED_KEY.equals(propertyName)) { + return AGREE_ALL_ENABLED_DESC; + } + return ENABLED_TYPES_DESC; } /** 화면에서 넘어온 코드 목록 파싱. 알 수 없는 코드는 오류로 처리한다. */ private List parseStrict(String codes) { List types = new ArrayList<>(); - for (String code : splitCodes(codes)) { - try { - types.add(AgreementType.valueOf(code)); - } catch (IllegalArgumentException e) { - throw new BizException("알 수 없는 약관 종류입니다. : " + code); + if (StringUtils.isNotBlank(codes)) { + for (String code : codes.split(",")) { + String trimmed = code.trim(); + if (trimmed.isEmpty()) { + continue; + } + try { + types.add(AgreementType.valueOf(trimmed)); + } catch (IllegalArgumentException e) { + throw new BizException("알 수 없는 약관 종류입니다. : " + trimmed); + } } } return new ArrayList<>(new LinkedHashSet<>(types)); } - private static List splitCodes(String value) { - if (StringUtils.isBlank(value)) { - return new ArrayList<>(); - } - return Arrays.stream(value.split(",")) - .map(String::trim) - .filter(StringUtils::isNotBlank) - .collect(Collectors.toList()); - } - private static String join(List types) { return types.stream().map(AgreementType::name).collect(Collectors.joining(",")); }