From 4413c5aaeab6f35a467e0cad39eaf57fe120fb92 Mon Sep 17 00:00:00 2001
From: infraurp
Date: Mon, 10 Aug 2026 16:40:46 +0900
Subject: [PATCH] =?UTF-8?q?=EC=9E=A5=EA=B8=B0=20=EB=AF=B8=EC=82=AC?=
=?UTF-8?q?=EC=9A=A9=20=EA=B3=84=EC=A0=95=20=ED=8C=8C=EA=B8=B0=20=EB=B0=B0?=
=?UTF-8?q?=EC=B9=98=EB=A5=BC=20=EA=B4=80=EB=A6=AC=EC=9E=90(EMP)=20?=
=?UTF-8?q?=EA=B3=84=EC=A0=95=20=ED=95=9C=EC=A0=95=EC=9C=BC=EB=A1=9C=20?=
=?UTF-8?q?=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
일반회원(GNR)은 탈퇴 즉시 하드 DELETE되어 휴면 전환 기반 지연 파기 대상이
아니었으므로 배치에서 제외. 관리자(개인정보취급자) 계정은 SFR-002 핵심요건
(개인정보보호위 점검 지적사항 — 휴면 대상이 일반회원에 한정되고 개인정보
취급자가 미포함)이라 로직은 그대로 유지하고 대상만 EMP로 좁혔다.
정책 화면(계정 관리 정책) 안내 문구도 관리자 계정 대상임을 명시.
스키마 변경 없음(ACCOUNT_MNG_POLICY.DELETE_PRESERVE_DAY/PURGE_USE_YN,
ACCOUNT_DELETE_HISTORY 재사용).
Co-Authored-By: Claude Sonnet 5
---
.../cmm/privacy/account/AccountDeleteDAO.java | 18 ++-----
.../account/AccountDeleteServiceImpl.java | 33 ++++++-------
.../account/AccountDeleteTargetVO.java | 2 +-
.../privacy/account/AccountMngPolicyVO.java | 4 +-
.../kr/or/arko/cmm/batch/BatchScheduler.java | 16 +++++--
.../PrivacyAccountDelete_SQL_Mysql.xml | 47 ++-----------------
.../PrivacyAccountDelete_SQL_Oracle.xml | 41 ++--------------
.../jsp/site/mnt/sec/accountPolicy/policy.jsp | 12 +++--
8 files changed, 48 insertions(+), 125 deletions(-)
diff --git a/src/main/java/egovframework/com/cmm/privacy/account/AccountDeleteDAO.java b/src/main/java/egovframework/com/cmm/privacy/account/AccountDeleteDAO.java
index 9145e4a..08060d7 100644
--- a/src/main/java/egovframework/com/cmm/privacy/account/AccountDeleteDAO.java
+++ b/src/main/java/egovframework/com/cmm/privacy/account/AccountDeleteDAO.java
@@ -9,30 +9,22 @@ import egovframework.com.cmm.service.impl.EgovComAbstractDAO;
/**
* 장기 미사용 계정 개인정보 파기 DAO (SFR-002 / 개발항목 #12).
*
+ *
대상은 개인정보취급자(관리자, EMP) 계정 한정 — 일반회원(GNR)은 탈퇴 즉시 하드 DELETE
+ * 되어 이 배치의 대상이 아니다(2026-08 정리).
+ *
*
statement 는 {@code PrivacyAccountDelete_SQL_Mysql.xml}(namespace {@code AccountDeleteDAO}),
* sql-map-config 등록. 보존기간은 SQL 내부에서 ACCOUNT_MNG_POLICY.DELETE_PRESERVE_DAY 를
- * 직접 조회하므로 자바 측 하드코딩이 없다. 1회 처리건수 상한(200)도 SQL 측에 있다.
+ * 직접 조회하므로 자바 측 하드코딩이 없다. 1회 처리건수 상한(1,000)도 SQL 측에 있다.
*/
@Repository("accountDeleteDAO")
public class AccountDeleteDAO extends EgovComAbstractDAO {
- /** 파기 대상(일반회원) — 휴면확정 후 보존기간 경과분, 최대 200건. */
- @SuppressWarnings("unchecked")
- public List selectExpiredGnrAccounts() {
- return list("AccountDeleteDAO.selectExpiredGnrAccounts", null);
- }
-
- /** 파기 대상(개인정보취급자) — 휴면확정 후 보존기간 경과분, 최대 200건. */
+ /** 파기 대상(개인정보취급자) — 휴면확정 후 보존기간 경과분, 최대 1,000건. */
@SuppressWarnings("unchecked")
public List selectExpiredEmpAccounts() {
return list("AccountDeleteDAO.selectExpiredEmpAccounts", null);
}
- /** 일반회원 개인정보 파기(마스킹 + MBER_STTUS='D'). */
- public int anonymizeGnrMember(String mberId) {
- return update("AccountDeleteDAO.anonymizeGnrMember", mberId);
- }
-
/** 개인정보취급자 개인정보 파기(마스킹 + EMPLYR_STTUS_CODE='D'). */
public int anonymizeEmplyr(String emplyrId) {
return update("AccountDeleteDAO.anonymizeEmplyr", emplyrId);
diff --git a/src/main/java/egovframework/com/cmm/privacy/account/AccountDeleteServiceImpl.java b/src/main/java/egovframework/com/cmm/privacy/account/AccountDeleteServiceImpl.java
index 0eef632..e693647 100644
--- a/src/main/java/egovframework/com/cmm/privacy/account/AccountDeleteServiceImpl.java
+++ b/src/main/java/egovframework/com/cmm/privacy/account/AccountDeleteServiceImpl.java
@@ -11,15 +11,19 @@ import org.springframework.stereotype.Service;
/**
* 장기 미사용 계정 개인정보 파기 서비스 구현 (SFR-002 / 개발항목 #12).
*
+ *
대상은 개인정보취급자(관리자, EMP) 계정 한정이다. 일반회원(GNR)은 탈퇴 신청 즉시
+ * {@code EgovMberManageServiceImpl.deleteMber()} 경로에서 하드 DELETE 되어 보존기간을 두고
+ * 지연 파기할 대상 자체가 없으므로(2026-08 정리), 이 배치의 GNR 처리 경로를 제거했다.
+ *
*
보존기간은 코드에 상수로 두지 않고 {@code ACCOUNT_MNG_POLICY.DELETE_PRESERVE_DAY}
* 를 SQL 실행 시점에 조회해 적용한다(정책 화면 #13에서 변경 시 즉시 반영).
*
*
파기 방식은 행 DELETE 가 아니라 PII 컬럼 마스킹 + 상태코드 'D' 전환이다.
- * 게시글·활동확인·마일리지 등 다수 테이블이 MBER_ID/EMPLYR_ID 를 FK 없이 참조하고 있어
- * 행을 지우면 해당 이력이 고아가 되기 때문이며, 기존 회원탈퇴 컨벤션과도 동일하다.
+ * 게시글·활동확인·마일리지 등 다수 테이블이 EMPLYR_ID 를 FK 없이 참조하고 있어
+ * 행을 지우면 해당 이력이 고아가 되기 때문이다.
*
*
처리량 상한: 배치가 장기간 정지했다 재가동되는 날 백로그가 한꺼번에 처리되지
- * 않도록 1회 200건으로 제한(SQL의 LIMIT). 남은 대상은 다음 배치에서 이어 처리한다.
+ * 않도록 1회 1,000건으로 제한(SQL의 LIMIT). 남은 대상은 다음 배치에서 이어 처리한다.
*
*
계정 1건 처리는 마스킹→휴면행 제거→이력적재 순이며, 개별 실패는 로깅 후 계속(fail-safe)
* 하여 한 건의 오류가 배치 전체를 막지 않는다.
@@ -29,7 +33,6 @@ public class AccountDeleteServiceImpl implements AccountDeleteService {
private static final Logger LOG = LoggerFactory.getLogger(AccountDeleteServiceImpl.class);
- private static final String TY_GNR = "GNR";
private static final String TY_EMP = "EMP";
@Resource(name = "accountDeleteDAO")
@@ -48,31 +51,25 @@ public class AccountDeleteServiceImpl implements AccountDeleteService {
return 0;
}
- int purged = 0;
- purged += purge(accountDeleteDAO.selectExpiredGnrAccounts(), TY_GNR);
- purged += purge(accountDeleteDAO.selectExpiredEmpAccounts(), TY_EMP);
- LOG.info("[개인정보/#12] 장기 미사용 계정 파기 배치 완료 — {}건", Integer.valueOf(purged));
+ int purged = purge(accountDeleteDAO.selectExpiredEmpAccounts());
+ LOG.info("[개인정보/#12] 장기 미사용 관리자 계정 파기 배치 완료 — {}건", Integer.valueOf(purged));
return purged;
}
- /** 유형별 파기 처리. 개별 계정 실패는 건너뛰고 계속한다. */
- private int purge(List targets, String mberTyCode) {
+ /** 개인정보취급자(EMP) 계정 파기 처리. 개별 계정 실패는 건너뛰고 계속한다. */
+ private int purge(List targets) {
if (targets == null || targets.isEmpty()) {
return 0;
}
int cnt = 0;
for (AccountDeleteTargetVO t : targets) {
try {
- if (TY_EMP.equals(mberTyCode)) {
- accountDeleteDAO.anonymizeEmplyr(t.getMberId());
- } else {
- accountDeleteDAO.anonymizeGnrMember(t.getMberId());
- }
+ accountDeleteDAO.anonymizeEmplyr(t.getMberId());
accountDeleteDAO.deleteDiapauseRow(t.getMberId());
AccountDeleteHistoryVO hist = new AccountDeleteHistoryVO();
hist.setMberId(t.getMberId());
- hist.setMberTyCode(mberTyCode);
+ hist.setMberTyCode(TY_EMP);
hist.setEsntlId(t.getEsntlId());
hist.setDiapauseDt(t.getDiapauseDt());
hist.setPreserveDay(t.getPreserveDay());
@@ -80,8 +77,8 @@ public class AccountDeleteServiceImpl implements AccountDeleteService {
cnt++;
} catch (Exception e) {
- LOG.error("[개인정보/#12] 계정 파기 실패 — 건너뜀 (id={}, ty={}): {}",
- new Object[] { t.getMberId(), mberTyCode, e.getMessage() });
+ LOG.error("[개인정보/#12] 관리자 계정 파기 실패 — 건너뜀 (id={}): {}",
+ new Object[] { t.getMberId(), e.getMessage() });
}
}
return cnt;
diff --git a/src/main/java/egovframework/com/cmm/privacy/account/AccountDeleteTargetVO.java b/src/main/java/egovframework/com/cmm/privacy/account/AccountDeleteTargetVO.java
index d4a542d..6d58d43 100644
--- a/src/main/java/egovframework/com/cmm/privacy/account/AccountDeleteTargetVO.java
+++ b/src/main/java/egovframework/com/cmm/privacy/account/AccountDeleteTargetVO.java
@@ -12,7 +12,7 @@ public class AccountDeleteTargetVO implements Serializable {
private static final long serialVersionUID = 1L;
- /** 파기 대상 계정ID(일반회원 MBER_ID / 취급자 EMPLYR_ID) */
+ /** 파기 대상 계정ID(개인정보취급자 EMPLYR_ID) */
private String mberId;
/** 고유식별자(ESNTL_ID) */
private String esntlId;
diff --git a/src/main/java/egovframework/com/cmm/privacy/account/AccountMngPolicyVO.java b/src/main/java/egovframework/com/cmm/privacy/account/AccountMngPolicyVO.java
index 29d6244..e21faa9 100644
--- a/src/main/java/egovframework/com/cmm/privacy/account/AccountMngPolicyVO.java
+++ b/src/main/java/egovframework/com/cmm/privacy/account/AccountMngPolicyVO.java
@@ -30,12 +30,12 @@ public class AccountMngPolicyVO implements Serializable {
private int alertD2 = 7;
/** 사전알림 3차(D-Day, 기본 0). */
private int alertD3 = 0;
- /** 휴면/탈퇴 후 삭제 보존기간(일). */
+ /** 휴면 후 삭제 보존기간(일) — 개인정보취급자(관리자, EMP) 계정 한정. 일반회원은 탈퇴 즉시 삭제되어 대상이 아니다. */
private int deletePreserveDay = 365;
/** 배치 주기(DAILY/WEEKLY/MONTHLY). */
private String batchCycle = "DAILY";
/**
- * 장기 미사용 계정 파기 배치 사용여부(#12). Y:사용 N:미사용.
+ * 장기 미사용 관리자(개인정보취급자) 계정 파기 배치 사용여부(#12). Y:사용 N:미사용.
* 기본 'N' — 관리자가 정책 화면에서 명시적으로 켜야 파기가 수행된다(되돌릴 수 없는 처리).
*/
private String purgeUseYn = "N";
diff --git a/src/main/java/kr/or/arko/cmm/batch/BatchScheduler.java b/src/main/java/kr/or/arko/cmm/batch/BatchScheduler.java
index c11ec04..5b64568 100644
--- a/src/main/java/kr/or/arko/cmm/batch/BatchScheduler.java
+++ b/src/main/java/kr/or/arko/cmm/batch/BatchScheduler.java
@@ -301,22 +301,28 @@ public class BatchScheduler {
}
/**
- * [개인정보보호 #12] 장기 미사용 계정 개인정보 파기 배치.
+ * [개인정보보호 #12] 장기 미사용 관리자(개인정보취급자) 계정 개인정보 파기 배치.
* 매일 02:30, 휴면 확정('P') 후 보존기간(ACCOUNT_MNG_POLICY.DELETE_PRESERVE_DAY 정책값)이
- * 경과한 계정의 개인정보를 파기(PII 마스킹 + 상태 'D')하고 ACCOUNT_DELETE_HISTORY 에 이력을 남긴다.
- * 휴면 등록 배치(01:00)·사전알림(06:00)과 시각을 분리했다.
+ * 경과한 관리자 계정의 개인정보를 파기(PII 마스킹 + 상태 'D')하고 ACCOUNT_DELETE_HISTORY 에
+ * 이력을 남긴다. 휴면 등록 배치(01:00)·사전알림(06:00)과 시각을 분리했다.
+ *
+ *
일반회원(GNR) 계정은 이 배치의 대상이 아니다(2026-08 정리). 탈퇴 신청 즉시
+ * {@code EgovMberManageServiceImpl.deleteMber()} 경로에서 하드 DELETE 되므로 휴면 전환을
+ * 거쳐 지연 파기할 대상 자체가 없다. 대상은 관리자(EMP) 계정 한정이며, 이는 SFR-002가
+ * 지적한 "휴면 대상이 일반회원에 한정되고 개인정보취급자가 미포함"인 취약점을 보완하는
+ * 핵심 구현 대상이라 유지한다.
*
*
실제 파기 수행 여부는 정책 플래그로 제어한다. 배치는 매일 등록되어 돌지만
* {@code ACCOUNT_MNG_POLICY.PURGE_USE_YN}(기본 'N')이 'Y'가 아니면 서비스가 즉시 0건으로
* 종료한다. 켜고 끄는 것은 계정 관리 정책 화면(#13)에서 하며 재배포가 필요 없다.
- * 1회 처리건수는 SQL에서 200건으로 상한(백로그 일시 폭주 방지).
+ * 1회 처리건수는 SQL에서 1,000건으로 상한(백로그 일시 폭주 방지).
*
* @throws Exception 파기 오류
*/
@Scheduled(cron = "0 30 2 * * ?")
public void purgeExpiredAccountScheduler() throws Exception {
int purged = accountDeleteService.purgeExpiredAccounts();
- log.info("[개인정보/#12] 장기 미사용 계정 파기 배치 완료 — " + purged + "건");
+ log.info("[개인정보/#12] 장기 미사용 관리자 계정 파기 배치 완료 — " + purged + "건");
}
/**
diff --git a/src/main/resources/egovframework/sqlmap/site/mnt/biz/privacy/PrivacyAccountDelete_SQL_Mysql.xml b/src/main/resources/egovframework/sqlmap/site/mnt/biz/privacy/PrivacyAccountDelete_SQL_Mysql.xml
index 6bf622f..9565815 100644
--- a/src/main/resources/egovframework/sqlmap/site/mnt/biz/privacy/PrivacyAccountDelete_SQL_Mysql.xml
+++ b/src/main/resources/egovframework/sqlmap/site/mnt/biz/privacy/PrivacyAccountDelete_SQL_Mysql.xml
@@ -3,9 +3,12 @@
@@ -14,22 +17,6 @@
-
-
-
-
-
-
-
-
+ 대상 운영 DB가 MySQL(csv)이라 sql-map-config 미등록(다른 Privacy* Oracle 파일과 동일 관례).
+ 대상은 개인정보취급자(관리자, EMP) 계정 한정 — 일반회원(GNR)은 탈퇴 즉시 하드 DELETE 되어
+ 이 배치의 대상이 아니므로 GNR statement 를 제거했다(2026-08). -->
-
-
-
-
-
-
-
*
- 일 (휴면 전환 후 이 기간이 지나면 개인정보 파기 대상)
+ 일 — 개인정보취급자(관리자) 계정 대상. 휴면 전환 후 이 기간이 지나면 개인정보 파기 대상
+ (일반회원은 탈퇴 시 즉시 삭제되므로 이 설정과 무관)
@@ -157,7 +159,7 @@ function fn_reload() {
- '사용' 선택 시 매일 02:30 배치가 위 기준일 경과 휴면계정의 개인정보를 파기합니다(1회 최대 200건).
+ 개인정보취급자(관리자) 계정 대상. '사용' 선택 시 매일 02:30 배치가 위 기준일 경과 휴면 관리자계정의 개인정보를 파기합니다(1회 최대 1,000건).
파기된 개인정보는 복구할 수 없습니다.