From 1be7d84e88602aee1d5776d796a2d6eb42a0c01a Mon Sep 17 00:00:00 2001 From: infraurp Date: Tue, 11 Aug 2026 09:14:38 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B0=9C=EC=9D=B8=EC=A0=95=EB=B3=B4=EB=B3=B4?= =?UTF-8?q?=ED=98=B8=20=EA=B8=B0=EB=8A=A5=EA=B0=9C=EC=84=A0=205=EC=B0=A8?= =?UTF-8?q?=20=E2=80=94=20comvnusermaster/comvnuserinfo=20=EB=B7=B0=20GNR?= =?UTF-8?q?=20=EC=BB=B7=EC=98=A4=EB=B2=84=20DDL=20=EC=8B=A4=ED=96=89?= =?UTF-8?q?=EB=B6=84=20=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 회원정보 암호화(Action4)의 마지막 단계. 545b49d로 반영한 소비자 정비(66개 표시 + 전체 검색전환)를 전제로, 두 공유 뷰의 GNR 분기를 MBER_NM(평문)에서 MBER_NM_ENC(암호문)로 전환하고 검색용 USER_NM_HASH 컬럼을 추가하는 DDL을 127.0.0.1:3306 dev DB에 실제 실행했다. - information_schema.views.VIEW_DEFINITION을 서버사이드 REPLACE()로 최소 치환(PREPARE/ EXECUTE) — comvnuserinfo의 한글 리터럴(USER_GUBUN_NM)을 클라이언트에서 재입력하지 않아 터미널 인코딩 문제를 원천 회피 - 실행 전 원본 정의를 privacy_view_def_backup 테이블에 백업, 치환 건수/컬럼 존재 여부를 assertion으로 강제해 잘못된 정의 반영을 차단 - 행 수(USER_SE/USER_GUBUN별 COUNT) 실행 전후 동일 확인, GNR 표본 암호문+해시 노출 확인, USR/ENT 표본 평문 그대로 유지 확인 — 상세 로그는 _workspace/deploy/20260811_0850/ DB_APPLY_LOG.md(gitignored, 로컬 전용) 주의: 이번엔 코드(545b49d) 커밋 후 로컬 WAS 재기동 전에 DB DDL을 먼저 실행했다 — 다음 반영 시 코드부터 배포하고 재기동할 것(LAST_DEPLOYED.md 참조). Co-Authored-By: Claude Sonnet 5 --- ...rivacy.mber_nm_view_hash_cutover_mysql.sql | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 src/script/mysql/ddl/privacy.mber_nm_view_hash_cutover_mysql.sql diff --git a/src/script/mysql/ddl/privacy.mber_nm_view_hash_cutover_mysql.sql b/src/script/mysql/ddl/privacy.mber_nm_view_hash_cutover_mysql.sql new file mode 100644 index 0000000..ef255db --- /dev/null +++ b/src/script/mysql/ddl/privacy.mber_nm_view_hash_cutover_mysql.sql @@ -0,0 +1,160 @@ +-- 개인정보보호 기능개선 사업 추가제안 Action4 (2026-08-11) +-- comvnusermaster / comvnuserinfo 뷰 재정의: GNR 분기를 암호문(MBER_NM_ENC)으로 전환하고 +-- 검색용 해시 컬럼(USER_NM_HASH)을 추가한다. +-- +-- 주의: 이 스크립트는 기존 뷰의 정확한 바이트 내용(특히 comvnuserinfo의 한글 리터럴 +-- USER_GUBUN_NM 값)을 절대 재입력하지 않는다 — information_schema.views.VIEW_DEFINITION을 +-- 서버 사이드에서 REPLACE()로 최소 치환만 하고, 결과를 다시 CREATE OR REPLACE VIEW로 +-- 반영한다(PREPARE/EXECUTE 동적 SQL). 클라이언트 터미널 인코딩 문제로 한글이 깨져 +-- 보이는 현상과 무관하게 안전하다. +-- +-- VIEW_DEFINITION은 테이블명을 스키마 한정자(`csv`.`table`)로 저장한다는 점에 +-- 주의(SHOW CREATE VIEW의 표시와 다름) — 2026-08-11 dryrun으로 실측 확인. +-- +-- 사전조건: 이 스크립트를 실행하기 전에 comvnusermaster/comvnuserinfo를 소비하는 +-- 모든 구문(66개 표시 + 검색 전환분, 2026-08-11 커밋 545b49d)이 이미 반영되어 있어야 +-- 한다 — all-or-nothing 게이트. 되돌리려면 아래 백업 섹션의 CREATE OR REPLACE VIEW로 복원한다. + +-- ============================================================ +-- 0. 백업: 현재 뷰 정의를 별도 테이블에 저장(롤백용) +-- ============================================================ +CREATE TABLE IF NOT EXISTS privacy_view_def_backup ( + view_name VARCHAR(64) NOT NULL, + backup_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + view_definition MEDIUMTEXT NOT NULL, + PRIMARY KEY (view_name, backup_at) +); + +INSERT INTO privacy_view_def_backup (view_name, view_definition) +SELECT 'comvnusermaster', VIEW_DEFINITION +FROM information_schema.views +WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'comvnusermaster'; + +INSERT INTO privacy_view_def_backup (view_name, view_definition) +SELECT 'comvnuserinfo', VIEW_DEFINITION +FROM information_schema.views +WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'comvnuserinfo'; + +SELECT view_name, COUNT(*) AS backup_count FROM privacy_view_def_backup GROUP BY view_name; + +-- ============================================================ +-- 1. comvnusermaster 재정의 +-- ============================================================ +SET @def_master = ( + SELECT VIEW_DEFINITION FROM information_schema.views + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'comvnusermaster' +); + +SET @def_master = REPLACE( + @def_master, + '`comtngnrlmber`.`MBER_NM` AS `USER_NM`', + '`comtngnrlmber`.`MBER_NM_ENC` AS `USER_NM`' +); + +SET @def_master = REPLACE( + @def_master, + 'end) AS `ADULT` from `csv`.`comtngnrlmber` where', + 'end) AS `ADULT`,`csv`.`comtngnrlmber`.`MBER_NM_HASH` AS `USER_NM_HASH` from `csv`.`comtngnrlmber` where' +); + +SET @def_master = REPLACE( + @def_master, + '\'N\' AS `ADULT` from `csv`.`comtnemplyrinfo` union all', + '\'N\' AS `ADULT`,NULL AS `USER_NM_HASH` from `csv`.`comtnemplyrinfo` union all' +); + +SET @def_master = REPLACE( + @def_master, + '\'N\' AS `ADULT` from `csv`.`comtnentrprsmber` where', + '\'N\' AS `ADULT`,NULL AS `USER_NM_HASH` from `csv`.`comtnentrprsmber` where' +); + +SET @def_master = REPLACE( + @def_master, + '\'N\' AS `ADULT` from `csv`.`biz_group` where', + '\'N\' AS `ADULT`,NULL AS `USER_NM_HASH` from `csv`.`biz_group` where' +); + +-- 안전장치: 치환이 정확히 4건(분기당 1건)이 아니면 실행을 중단시킨다(0으로 나누기 강제 에러). +SELECT CASE WHEN + (LENGTH(@def_master) - LENGTH(REPLACE(@def_master, 'USER_NM_HASH', ''))) / LENGTH('USER_NM_HASH') = 4 + AND @def_master LIKE '%MBER_NM_ENC%' + AND @def_master NOT LIKE '%`comtngnrlmber`.`MBER_NM` AS `USER_NM`%' + THEN 1 ELSE 1/0 END AS master_assertion_ok; + +SET @sql_master = CONCAT('CREATE OR REPLACE VIEW comvnusermaster AS ', @def_master); +PREPARE stmt_master FROM @sql_master; +EXECUTE stmt_master; +DEALLOCATE PREPARE stmt_master; + +-- ============================================================ +-- 2. comvnuserinfo 재정의 +-- ============================================================ +SET @def_info = ( + SELECT VIEW_DEFINITION FROM information_schema.views + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'comvnuserinfo' +); + +SET @def_info = REPLACE( + @def_info, + '`comtngnrlmber`.`MBER_NM` AS `USER_NM`', + '`comtngnrlmber`.`MBER_NM_ENC` AS `USER_NM`' +); + +SET @def_info = REPLACE( + @def_info, + '`comtngnrlmber`.`ADMIN_UNLOCK_DT` AS `ADMIN_UNLOCK_DT` from `csv`.`comtngnrlmber` union all', + '`comtngnrlmber`.`ADMIN_UNLOCK_DT` AS `ADMIN_UNLOCK_DT`,`csv`.`comtngnrlmber`.`MBER_NM_HASH` AS `USER_NM_HASH` from `csv`.`comtngnrlmber` union all' +); + +SET @def_info = REPLACE( + @def_info, + '`biz_group`.`ADMIN_UNLOCK_DT` AS `ADMIN_UNLOCK_DT` from `csv`.`biz_group` union all', + '`biz_group`.`ADMIN_UNLOCK_DT` AS `ADMIN_UNLOCK_DT`,NULL AS `USER_NM_HASH` from `csv`.`biz_group` union all' +); + +SET @def_info = REPLACE( + @def_info, + '`comtnentrprsmber`.`ADMIN_UNLOCK_DT` AS `ADMIN_UNLOCK_DT` from `csv`.`comtnentrprsmber` union all', + '`comtnentrprsmber`.`ADMIN_UNLOCK_DT` AS `ADMIN_UNLOCK_DT`,NULL AS `USER_NM_HASH` from `csv`.`comtnentrprsmber` union all' +); + +SET @def_info = REPLACE( + @def_info, + '`comtnemplyrinfo`.`ADMIN_UNLOCK_DT` AS `ADMIN_UNLOCK_DT` from `csv`.`comtnemplyrinfo`', + '`comtnemplyrinfo`.`ADMIN_UNLOCK_DT` AS `ADMIN_UNLOCK_DT`,NULL AS `USER_NM_HASH` from `csv`.`comtnemplyrinfo`' +); + +SELECT CASE WHEN + (LENGTH(@def_info) - LENGTH(REPLACE(@def_info, 'USER_NM_HASH', ''))) / LENGTH('USER_NM_HASH') = 4 + AND @def_info LIKE '%MBER_NM_ENC%' + AND @def_info NOT LIKE '%`comtngnrlmber`.`MBER_NM` AS `USER_NM`%' + THEN 1 ELSE 1/0 END AS info_assertion_ok; + +SET @sql_info = CONCAT('CREATE OR REPLACE VIEW comvnuserinfo AS ', @def_info); +PREPARE stmt_info FROM @sql_info; +EXECUTE stmt_info; +DEALLOCATE PREPARE stmt_info; + +-- ============================================================ +-- 3. 반영 후 검증 +-- ============================================================ +SELECT TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION +FROM information_schema.columns +WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME IN ('comvnusermaster', 'comvnuserinfo') + AND COLUMN_NAME IN ('USER_NM', 'USER_SE', 'USER_GUBUN', 'USER_NM_HASH') +ORDER BY TABLE_NAME, ORDINAL_POSITION; + +SELECT USER_ID, USER_SE, LEFT(USER_NM, 20) AS user_nm_sample, LEFT(USER_NM_HASH, 20) AS hash_sample +FROM comvnusermaster WHERE USER_SE = 'GNR' LIMIT 1; + +SELECT USER_ID, USER_SE, LEFT(USER_NM, 20) AS user_nm_sample, USER_NM_HASH +FROM comvnusermaster WHERE USER_SE = 'USR' LIMIT 1; + +SELECT USER_SE, COUNT(*) FROM comvnusermaster GROUP BY USER_SE; + +SELECT USER_GUBUN, LEFT(USER_NM, 20) AS user_nm_sample, LEFT(USER_NM_HASH, 20) AS hash_sample +FROM comvnuserinfo WHERE USER_GUBUN = 'GNR' LIMIT 1; + +SELECT USER_GUBUN, COUNT(*) FROM comvnuserinfo GROUP BY USER_GUBUN;