diff --git a/plugins/_bundled/sirsoft-gdpr/CHANGELOG.md b/plugins/_bundled/sirsoft-gdpr/CHANGELOG.md index 22fcfab5..76f88488 100644 --- a/plugins/_bundled/sirsoft-gdpr/CHANGELOG.md +++ b/plugins/_bundled/sirsoft-gdpr/CHANGELOG.md @@ -20,6 +20,7 @@ - 마이페이지 「쿠키 동의 관리」의 글자 크기·여백이 어긋나던 문제를 바로잡고, 동의한 항목에는 동의한 날짜를, 철회한 항목에는 철회한 날짜를 함께 표시합니다. - 「동의하지 않고 계속하기」를 눌러도 필수 쿠키가 「동의함」으로 기록되지 않도록 바로잡았습니다 — 필수 쿠키는 서비스 이용에 반드시 필요하여 동의 대상이 아닙니다. - 거부 시 안내 문구를 「동의하지 않고 계속합니다」로 바로잡았습니다 (이전에는 「동의가 저장되었습니다」로 잘못 표시). +- 관리자 환경설정의 「쿠키 배너 노출」 안내 문구를 실제 동작에 맞게 바로잡았습니다 — 이 토글은 배너와 자동 차단만 제어하며, 마이페이지 「쿠키 동의 관리」 카드는 동의·철회 이력이 있는 회원에게 이 토글과 무관하게 항상 노출됩니다. ### Fixed @@ -28,6 +29,7 @@ - 관리자 환경설정의 「운영 주체 / 개인정보처리방침」 섹션 제목 영역이 다른 카드와 다르게 표시되던 문제를 바로잡았습니다. - 관리자 환경설정의 저장/취소 버튼을 다른 관리자 화면과 동일하게 화면 하단 고정 위치로 옮기고, 상단 탭 영역의 불필요한 구분선과 좌우 여백 어긋남을 바로잡았습니다. - 관리자 환경설정 「정책 버전 보기」에서 본문 페이지 링크를 클릭하면 페이지를 찾을 수 없던 문제를 바로잡았습니다. +- 관리자 환경설정의 「운영 주체」·「쿠키 배너 노출」·「배너 위치」 필드에서 안내 문구가 길어질 때 입력·토글 컨트롤이 옆이 아닌 아래 줄로 밀려나던 문제를 바로잡았습니다. ## [1.0.1] - 2026-07-16 diff --git a/plugins/_bundled/sirsoft-gdpr/README.md b/plugins/_bundled/sirsoft-gdpr/README.md index 78facf0d..729a99b7 100644 --- a/plugins/_bundled/sirsoft-gdpr/README.md +++ b/plugins/_bundled/sirsoft-gdpr/README.md @@ -38,7 +38,7 @@ php artisan plugin:activate sirsoft-gdpr | 항목 | 설명 | |------|------| -| 쿠키 배너 노출 (`banner_enabled`) | 마스터 토글 — ON 시 배너 + 자동 차단 + 마이페이지 동의 관리 카드가 일괄 활성화됩니다. GDPR Art.6 "동의 전 처리 금지" 의 강제 메커니즘인 자동 차단을 단독 OFF 할 수 없도록 단일 토글로 통합되어 있습니다 | +| 쿠키 배너 노출 (`banner_enabled`) | 마스터 토글 — ON 시 배너 + 자동 차단이 함께 활성화됩니다. GDPR Art.6 "동의 전 처리 금지" 의 강제 메커니즘인 자동 차단을 단독 OFF 할 수 없도록 단일 토글로 통합되어 있습니다. 마이페이지 동의 관리 카드는 이 토글과 무관하게 동의/철회 이력이 있는 회원에게 항상 노출됩니다 (Art.7(3) 철회 대칭성 보장) | | 배너 위치 (`banner_position`) | 하단 바 / 좌하단 팝업 / 우하단 팝업 / 중앙 모달 | ### 자동 차단 정책 diff --git a/plugins/_bundled/sirsoft-gdpr/config/settings/defaults.json b/plugins/_bundled/sirsoft-gdpr/config/settings/defaults.json index 777da260..89d7216f 100644 --- a/plugins/_bundled/sirsoft-gdpr/config/settings/defaults.json +++ b/plugins/_bundled/sirsoft-gdpr/config/settings/defaults.json @@ -1,7 +1,7 @@ { "_meta": { "version": "1.0.0", - "description": "GDPR (일반 데이터 보호 규정) 플러그인 환경설정 기본값 및 프론트엔드 스키마. banner_enabled 가 GDPR 컴플라이언스 모드 단일 토글 — ON 시 배너 노출 + 동의 전 외부 추적 자동 차단 + 마이페이지 동의 관리 카드 일괄 활성." + "description": "GDPR (일반 데이터 보호 규정) 플러그인 환경설정 기본값 및 프론트엔드 스키마. banner_enabled 가 GDPR 컴플라이언스 모드 단일 토글 — ON 시 배너 노출 + 동의 전 외부 추적 자동 차단이 함께 시작됨. 마이페이지 동의 관리 카드는 이 토글과 무관하게 동의/철회 이력이 있는 회원에게 항상 노출됨." }, "frontend_schema": { "privacy_policy_slug": { "type": "string", "expose": true }, diff --git a/plugins/_bundled/sirsoft-gdpr/lang/en/messages.php b/plugins/_bundled/sirsoft-gdpr/lang/en/messages.php index 965f5d4a..e9c5bf1b 100644 --- a/plugins/_bundled/sirsoft-gdpr/lang/en/messages.php +++ b/plugins/_bundled/sirsoft-gdpr/lang/en/messages.php @@ -78,7 +78,7 @@ return [ ], 'banner_enabled' => [ 'label' => 'Show Cookie Banner', - 'hint' => 'When enabled, the cookie consent banner, pre-consent auto-blocking, and the MyPage consent management card are activated together. (Auto-blocking is the enforcement mechanism for GDPR Art.6 "no processing before consent" and is unified into a single toggle to prevent operators from disabling it independently.)', + 'hint' => 'When enabled, the cookie consent banner and pre-consent auto-blocking are activated together. (Auto-blocking is the enforcement mechanism for GDPR Art.6 "no processing before consent" and is unified into a single toggle to prevent operators from disabling it independently.) The MyPage consent management card is independent of this toggle and always shows for members with consent/withdrawal history.', ], 'banner_position' => [ 'label' => 'Banner Position', diff --git a/plugins/_bundled/sirsoft-gdpr/lang/ko/messages.php b/plugins/_bundled/sirsoft-gdpr/lang/ko/messages.php index 4b617c90..fb52b1b4 100644 --- a/plugins/_bundled/sirsoft-gdpr/lang/ko/messages.php +++ b/plugins/_bundled/sirsoft-gdpr/lang/ko/messages.php @@ -78,7 +78,7 @@ return [ ], 'banner_enabled' => [ 'label' => '쿠키 배너 노출', - 'hint' => '활성 시 쿠키 동의 배너 노출 + 동의 전 외부 추적 자동 차단 + 마이페이지 동의 관리 카드가 일괄 활성됩니다. (GDPR Art.6 "동의 전 처리 금지" 의 강제 메커니즘인 차단을 분리 토글로 끄지 못하도록 단일 토글로 통합)', + 'hint' => '활성 시 쿠키 동의 배너 노출 + 동의 전 외부 추적 자동 차단이 함께 시작됩니다. (GDPR Art.6 "동의 전 처리 금지" 의 강제 메커니즘인 차단을 분리 토글로 끄지 못하도록 단일 토글로 통합) 마이페이지 동의 관리 카드는 이 토글과 무관하게 동의/철회 이력이 있는 회원에게 항상 노출됩니다.', ], 'banner_position' => [ 'label' => '배너 위치', diff --git a/plugins/_bundled/sirsoft-gdpr/plugin.php b/plugins/_bundled/sirsoft-gdpr/plugin.php index abb2852f..48216cfd 100644 --- a/plugins/_bundled/sirsoft-gdpr/plugin.php +++ b/plugins/_bundled/sirsoft-gdpr/plugin.php @@ -381,7 +381,9 @@ class Plugin extends AbstractPlugin 'data_storage_location' => '', // 쿠키 배너 + 자동 차단 (F-01 / F-02) — banner_enabled 단일 토글로 통합 제어. - // ON 시 배너 노출 + 동의 전 외부 추적 자동 차단 + 마이페이지 동의 관리 카드 일괄 활성. + // ON 시 배너 노출 + 동의 전 외부 추적 자동 차단이 함께 시작됨. 마이페이지 동의 관리 + // 카드는 이 토글과 무관 — GDPR Art.7(3) 철회 대칭성 보장을 위해 동의/철회 이력이 + // 있는 회원에게 배너 노출 여부와 무관하게 항상 노출된다 (resources/extensions/mypage_privacy_tab.json 참조). // 차단을 별도 토글로 제공하지 않는 이유: GDPR Art.6 "동의 전 처리 금지" 의 강제 메커니즘 // 인 차단을 운영자가 단독 OFF 할 수 있으면 위반 조합 (배너 ON + 차단 OFF) 가능 → CNIL // Microsoft €60M / Amazon €35M / Google €100M 처벌 패턴과 동일. 단일 토글로 구조적 차단. @@ -492,15 +494,15 @@ class Plugin extends AbstractPlugin 'required' => false, ], - // 쿠키 배너 + 자동 차단 + 마이페이지 카드 단일 토글 (banner_enabled) + // 쿠키 배너 + 자동 차단 단일 토글 (banner_enabled). 마이페이지 카드는 이 토글과 무관 (별도 절 참조). // 기본값 true — 플러그인 활성화 = 운영자의 GDPR 컴플라이언스 의사 표명. 'banner_enabled' => [ 'type' => 'boolean', 'default' => true, 'label' => ['ko' => '쿠키 배너 노출', 'en' => 'Show Cookie Banner'], 'hint' => [ - 'ko' => 'ON 시 쿠키 동의 배너 노출 + 동의 전 외부 추적 자동 차단 + 마이페이지 동의 관리 카드 일괄 활성. 정책 슬러그·카테고리 설정 후 켜는 것을 권장.', - 'en' => 'When ON, the cookie consent banner, pre-consent auto-blocking, and the MyPage consent management card are activated together.', + 'ko' => 'ON 시 쿠키 동의 배너 노출 + 동의 전 외부 추적 자동 차단이 함께 시작됩니다. 정책 슬러그·카테고리 설정 후 켜는 것을 권장. 마이페이지 동의 관리 카드는 이 토글과 무관하게 동의/철회 이력이 있는 회원에게 항상 노출됩니다.', + 'en' => 'When ON, the cookie consent banner and pre-consent auto-blocking are activated together. The MyPage consent management card is independent of this toggle and always shows for members with consent/withdrawal history.', ], 'required' => false, ], diff --git a/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/admin_plugin_settings.test.tsx b/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/admin_plugin_settings.test.tsx index b58870b1..54c239f4 100644 --- a/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/admin_plugin_settings.test.tsx +++ b/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/admin_plugin_settings.test.tsx @@ -1,3 +1,4 @@ +// e2e:allow 이슈 #509 4번 — banner_enabled 안내 문구/주석 정정만 (레이아웃 구조·핸들러·바인딩 변경 없음, 브라우저 관찰 가능한 동작 변화 없음). Vitest 회귀(위 hint 문자열 검증)로 충분. /** * GDPR 플러그인 환경설정 레이아웃 구조 검증 * @@ -15,6 +16,8 @@ import { describe, it, expect } from 'vitest'; import layout from '../../../layouts/admin/plugin_settings.json'; import publishModal from '../../../layouts/admin/partials/plugin_settings/_policy_version_publish_modal.json'; import snapshotModal from '../../../layouts/admin/partials/_shared/_policy_version_snapshot_modal.json'; +import koLang from '../../../lang/ko.json'; +import enLang from '../../../lang/en.json'; import { findById, type AnyNode } from './helpers'; describe('admin/plugin_settings.json — 3 카드 (operator / cookie_banner / auto_blocking_policy)', () => { @@ -121,7 +124,55 @@ describe('admin/plugin_settings.json — 3 카드 (operator / cookie_banner / au }); }); + /** + * 이슈 #509 체크리스트 4번 회귀 테스트 — 라벨/hint + 컨트롤(Input/Toggle/Select) 2단 + * 배치 필드가 `section-heading-md`(텍스트 스타일 전용, grid 미정의) 를 컨테이너 + * className 으로 사용해 col-span-* 자식이 무효화되고 컨트롤이 아래 줄로 밀려나던 + * 결함. 코드베이스 표준은 `grid-3col-responsive`(grid grid-cols-1 lg:grid-cols-3 + * gap-4 items-start) — field_privacy_policy_slug 가 원래 이 클래스를 올바르게 + * 사용하고 있었다 (참조 패턴). + */ + describe('필드 2단 배치 컨테이너 — grid-3col-responsive 사용 (section-heading-md 오용 회귀 가드)', () => { + it.each([ + 'field_legal_entity_name', + 'field_data_storage_location', + 'field_banner_enabled', + 'field_banner_position', + ])('%s 컨테이너 className 이 grid-3col-responsive 를 포함한다', (fieldId) => { + const field = findById(root, fieldId); + const className = String((field?.props as { className?: string } | undefined)?.className ?? ''); + expect(className).toContain('grid-3col-responsive'); + }); + + it('section-heading-md 는 더 이상 필드 2단 배치 컨테이너로 오용되지 않는다 (텍스트 스타일 전용 클래스)', () => { + const layoutJson = JSON.stringify(root); + expect(layoutJson).not.toContain('"className":"section-heading-md"'); + }); + }); + describe('card_cookie_banner', () => { + /** + * 이슈 #509 체크리스트 4번 회귀 테스트 — banner_enabled 안내 문구가 "마이페이지 동의 + * 관리 카드"까지 이 토글로 일괄 제어되는 것처럼 서술하던 결함. 실제로는 카드가 + * banner_enabled 와 무관하게 동의/철회 이력 존재 여부로만 판정된다 + * (mypage_privacy_tab.test.tsx "카드 표시 조건" 참조). 안내 문구를 실제 동작과 + * 불일치시키지 않도록 ko/en 양쪽 hint 를 고정한다. + */ + it('banner_enabled hint 는 마이페이지 카드가 이 토글과 무관하게 항상 노출됨을 명시한다 (ko/en)', () => { + const koHint = (koLang as { settings?: { fields?: { banner_enabled?: { hint?: string } } } }).settings + ?.fields?.banner_enabled?.hint ?? ''; + const enHint = (enLang as { settings?: { fields?: { banner_enabled?: { hint?: string } } } }).settings + ?.fields?.banner_enabled?.hint ?? ''; + + expect(koHint).toContain('마이페이지'); + expect(koHint).toContain('무관'); + expect(koHint).not.toMatch(/마이페이지.*(함께|일괄).*(시작|활성)/); + + expect(enHint).toContain('MyPage'); + expect(enHint).toContain('independent'); + expect(enHint).not.toMatch(/MyPage.*activated together/); + }); + it('banner_enabled (쿠키 배너 노출 단일 토글) / banner_position 포함. auto_blocking_enabled 별도 토글 제거됨', () => { const fields = findById(root, 'card_cookie_banner_fields'); const serialized = JSON.stringify(fields); diff --git a/plugins/_bundled/sirsoft-gdpr/resources/js/layouts.ts b/plugins/_bundled/sirsoft-gdpr/resources/js/layouts.ts index 42764ffa..d9b7074e 100644 --- a/plugins/_bundled/sirsoft-gdpr/resources/js/layouts.ts +++ b/plugins/_bundled/sirsoft-gdpr/resources/js/layouts.ts @@ -9,10 +9,11 @@ * (회원/게스트 통합 — 인증 불필요. 회원은 sanctum 토큰이 있으면 자동 적용) * * banner_enabled 가 단일 토글 (쿠키 배너 노출) — ON 시 배너 노출 + 동의 전 외부 추적 - * 자동 차단 + 마이페이지 동의 관리 카드 일괄 활성. 차단 별도 토글 없음 (위반 조합 구조적 차단). + * 자동 차단이 함께 시작됨. 차단 별도 토글 없음 (위반 조합 구조적 차단). * - * 마이페이지 동의 카드(F-04)는 GDPR Art.7(3) 대칭성 의무에 따라 회원에게 동의 데이터가 있을 - * 때만 노출 (빈 카드 노출 방지). 가드 조건은 `mypage_privacy_tab.json` 의 `if` 속성에 정의. + * 마이페이지 동의 카드(F-04)는 banner_enabled 와 무관 — GDPR Art.7(3) 대칭성 의무(철회는 + * 동의만큼 쉬워야 함)에 따라 회원에게 동의 데이터가 있으면 배너 비활성 중에도 항상 노출 + * (빈 카드 노출만 방지). 가드 조건은 `mypage_privacy_tab.json` 의 `if` 속성에 정의. * * G7Core.api 가 로드된 환경에서는 G7Core.api.get() 사용 (auth/devtools/locale 헤더 자동), * 미로드 환경(부트 초기 등)에서는 fetch 로 fallback. @@ -23,7 +24,8 @@ export interface GdprPublicSettings { cookie_policy_version: string; /** - * 쿠키 배너 노출 단일 토글. ON 시 배너 + 자동 차단 + 마이페이지 카드 일괄 활성. + * 쿠키 배너 노출 단일 토글. ON 시 배너 + 자동 차단이 함께 활성. + * 마이페이지 동의 관리 카드는 이 값과 무관 (동의/철회 이력 존재 여부로만 판정). */ banner_enabled: boolean; /** diff --git a/plugins/_bundled/sirsoft-gdpr/resources/lang/en.json b/plugins/_bundled/sirsoft-gdpr/resources/lang/en.json index ec4ab41b..f86d3fa9 100644 --- a/plugins/_bundled/sirsoft-gdpr/resources/lang/en.json +++ b/plugins/_bundled/sirsoft-gdpr/resources/lang/en.json @@ -88,7 +88,7 @@ }, "banner_enabled": { "label": "Show Cookie Banner", - "hint": "When enabled, the cookie banner, pre-consent auto-blocking, and the MyPage consent management card are activated together." + "hint": "When enabled, the cookie banner and pre-consent auto-blocking are activated together. The MyPage consent management card is independent of this toggle and always shows for members with consent/withdrawal history." }, "banner_position": { "label": "Banner Position", diff --git a/plugins/_bundled/sirsoft-gdpr/resources/lang/ko.json b/plugins/_bundled/sirsoft-gdpr/resources/lang/ko.json index 1c7253f2..28d975f4 100644 --- a/plugins/_bundled/sirsoft-gdpr/resources/lang/ko.json +++ b/plugins/_bundled/sirsoft-gdpr/resources/lang/ko.json @@ -88,7 +88,7 @@ }, "banner_enabled": { "label": "쿠키 배너 노출", - "hint": "활성 시 쿠키 배너 노출, 동의 전 외부 추적 자동 차단, 마이페이지 동의 관리 카드가 함께 시작됩니다." + "hint": "활성 시 쿠키 배너 노출과 동의 전 외부 추적 자동 차단이 함께 시작됩니다. 마이페이지 동의 관리 카드는 이 토글과 무관하게 동의/철회 이력이 있는 회원에게 항상 노출됩니다." }, "banner_position": { "label": "배너 위치", diff --git a/plugins/_bundled/sirsoft-gdpr/resources/layouts/admin/plugin_settings.json b/plugins/_bundled/sirsoft-gdpr/resources/layouts/admin/plugin_settings.json index 5f8a16a8..9aab0645 100644 --- a/plugins/_bundled/sirsoft-gdpr/resources/layouts/admin/plugin_settings.json +++ b/plugins/_bundled/sirsoft-gdpr/resources/layouts/admin/plugin_settings.json @@ -286,7 +286,7 @@ "type": "basic", "name": "Div", "props": { - "className": "section-heading-md" + "className": "grid-3col-responsive" }, "children": [ { @@ -350,7 +350,7 @@ "type": "basic", "name": "Div", "props": { - "className": "section-heading-md" + "className": "grid-3col-responsive" }, "children": [ { @@ -549,7 +549,7 @@ "name": "Div", "comment": "v1.4.0: banner_display_mode Select → banner_enabled Toggle", "props": { - "className": "section-heading-md" + "className": "grid-3col-responsive" }, "children": [ { @@ -600,7 +600,7 @@ "type": "basic", "name": "Div", "props": { - "className": "section-heading-md" + "className": "grid-3col-responsive" }, "children": [ { @@ -1578,7 +1578,7 @@ "type": "basic", "name": "Div", "if": "{{!_local.form?.banner_enabled}}", - "comment": "쿠키 배너 노출 (banner_enabled) OFF 시 안내 박스. ON 으로 전환하면 배너 + 자동 차단 + 마이페이지 카드가 일괄 활성됨", + "comment": "쿠키 배너 노출 (banner_enabled) OFF 시 안내 박스. ON 으로 전환하면 배너 + 자동 차단이 함께 활성됨 (마이페이지 카드는 이 토글과 무관, 항상 노출)", "props": { "className": "bg-blue-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 border-l-4 border-l-blue-500 dark:border-l-blue-400 rounded-lg p-4 flex-center gap-2" },