diff --git a/adm/admin.js b/adm/admin.js
index f6c0563c1..7ce8677f4 100644
--- a/adm/admin.js
+++ b/adm/admin.js
@@ -1,11 +1,115 @@
-function check_all(f)
-{
- var chk = document.getElementsByName("chk[]");
+/** 공통 UI 모듈 */
+window.CommonUI = {
+ bindTabs(tabSelector, contentSelector, options = {}) {
+ const tabs = document.querySelectorAll(tabSelector);
+ const contents = document.querySelectorAll(contentSelector);
- for (i=0; i
팝빌 서비스 설정이 되어 있지 않아, 프리셋 서비스를 사용할 수 없습니다.
+
팝빌 설정은 환경설정 > 기본환경설정 > 기본알림환경 에서 확인 및 설정해 주셔야 사용하실 수 있습니다.
* 설정 오류 내용 :
+템플릿관리를 통해 신청 후 승인된 템플릿만 사용가능합니다.
템플릿 내용 작성 시, 동일한 [구분]에 속한 변수만 사용 가능하며, 아래에 제공된 변수 외의 항목을 입력할 경우 적용되지 않습니다.
아래 표의 #{변수명}만 템플릿 내용에 사용할 수 있으며, 실제 발송 시 값으로 자동 치환됩니다.
※ 표에 없는 변수는 치환되지 않습니다.
[사용 가능한 변수 리스트▼]
+| 구분 | +변수명 | +설명 | +
|---|---|---|
| + + | + | + |
아래 표의 #{버튼링크명}은 버튼에 사용할 수 있으며, 실제 발송 시 지정된 URL로 자동 치환됩니다.
※ 표에 없는 버튼 링크 변수는 치환되지 않습니다. 등록 시 [https://#{버튼링크명}]으로 작성하시면 됩니다.
* 관리자 휴대폰번호 : 관리자로 설정된 []의 휴대폰 번호를 사용합니다.
+* 그룹 관리자 휴대폰번호 : 그룹 관리자로 지정된 아이디의 휴대폰 번호를 사용합니다.
+* 게시판 관리자 휴대폰번호 : 게시판 관리자로 지정된 아이디의 휴대폰 번호를 사용합니다.
++ 환경설정 > 기본환경설정 > 회원가입'; + + if (!empty($config['cf_use_hp'])) { + // 보이기만 설정된 경우 + echo '[휴대폰번호 입력]이 [보이기]로 설정되어 있습니다. 일부 회원은 휴대폰 번호를 입력하지 않아 발송이 제한될 수 있습니다.' + . $link . '에서 [필수입력]으로 설정하는 것을 권장합니다.'; + } else { + // 둘 다 설정 안 된 경우 + echo '[휴대폰번호 입력]이 [보이기] 또는 [필수입력]으로 설정되어 있지 않습니다. 현재 상태에서는 알림톡 발송이 불가능합니다.' + . $link . '에서 반드시 [보이기]나 [필수입력] 중 하나 이상으로 설정해야 합니다.'; + } + ?> +
+| + | + + + | +
|---|---|
| 사업자등록번호 | ++ + + | +
| 회신번호 | ++ + + | +
| + | + + + | +
| + | + + + | +
| + | + + + | +
| + | + + 설정 확인 버튼을 클릭하면 팝빌 API와의 연결 상태 및 포인트 정보를 확인할 수 있습니다."); ?> + + + | +
| 팝빌 연동신청 | ++ 회원가입 시 연동회원으로 선택후 링크아이디 [SIRSOFT]를 넣어주세요.'); ?> + 팝빌 연동신청 + | +
| 알림톡 프리셋 설정 | ++ 환경설정 > 알림톡프리셋관리 에서 설정하실 수 있습니다. + | +
| 메일 수신 | +광고성 이메일 수신 | > > + + (동의 일자: ".$mb['mb_mailling_date'].")" : ''; + } ?> | -+ | > > + (동의 일자: ".$mb['mb_sms_date'].")" : ''; + } ?> | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 마케팅 목적의 개인정보 수집 및 이용 |
+ + > + + > + + + (동의 일자: ".$mb['mb_marketing_date'].")" : ''; + } ?> + | ++ | + > + + > + + + (동의 일자: ".$mb['mb_thirdparty_date'].")" : ''; + } ?> + | +|||||||||||
| 약관동의 변경내역 | +
+
+
+
+ |
+ |||||||||||||
| 정보 공개 | @@ -376,6 +445,9 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js > + (동의 일자: ".$mb['mb_open_date'].")" : ''; + } ?> | |||||||||||||
| 게시글 작성 완료 알림 | ++ > + + > + + | + +게시글 답변 알림 | ++ > + + > + + | +|||||||||||
| 댓글 알림 | ++ > + + > + + | + +대댓글 알림 | ++ > + + > + + | +|||||||||||
| diff --git a/adm/member_form_update.php b/adm/member_form_update.php index 29e96a38c..8616200be 100644 --- a/adm/member_form_update.php +++ b/adm/member_form_update.php @@ -18,6 +18,16 @@ $mb_certify_case = isset($_POST['mb_certify_case']) ? preg_replace('/[^0-9a-z_]/ $mb_certify = isset($_POST['mb_certify']) ? preg_replace('/[^0-9a-z_]/i', '', $_POST['mb_certify']) : ''; $mb_zip = isset($_POST['mb_zip']) ? preg_replace('/[^0-9a-z_]/i', '', $_POST['mb_zip']) : ''; +// 광고성 정보 수신 +$mb_marketing_agree = isset($_POST['mb_marketing_agree']) ? clean_xss_tags($_POST['mb_marketing_agree'], 1, 1) : '0'; +$mb_thirdparty_agree = isset($_POST['mb_thirdparty_agree']) ? clean_xss_tags($_POST['mb_thirdparty_agree'], 1, 1) : '0'; + +// 게시판알림 설정 +$mb_board_post = isset($_POST['mb_board_post']) ? (int)$_POST['mb_board_post'] : 0; +$mb_board_reply = isset($_POST['mb_board_reply']) ? (int)$_POST['mb_board_reply'] : 0; +$mb_board_comment = isset($_POST['mb_board_comment']) ? (int)$_POST['mb_board_comment'] : 0; +$mb_board_recomment = isset($_POST['mb_board_recomment']) ? (int)$_POST['mb_board_recomment'] : 0; + // 관리자가 자동등록방지를 사용해야 할 경우 ( 회원의 비밀번호 변경시 캡챠를 체크한다 ) if ($mb_password) { include_once(G5_CAPTCHA_PATH . '/captcha.lib.php'); @@ -86,8 +96,6 @@ foreach ($check_keys as $key) { } } -$mb_memo = isset($_POST['mb_memo']) ? $_POST['mb_memo'] : ''; - $sql_common = " mb_name = '{$posts['mb_name']}', mb_nick = '{$mb_nick}', mb_email = '{$mb_email}', @@ -109,8 +117,15 @@ $sql_common = " mb_name = '{$posts['mb_name']}', mb_mailling = '{$posts['mb_mailling']}', mb_sms = '{$posts['mb_sms']}', mb_open = '{$posts['mb_open']}', + mb_open_date = '".G5_TIME_YMDHIS."', mb_profile = '{$posts['mb_profile']}', mb_level = '{$posts['mb_level']}', + mb_marketing_agree = '{$mb_marketing_agree}', + mb_thirdparty_agree = '{$mb_thirdparty_agree}', + mb_board_post = '{$mb_board_post}', + mb_board_reply = '{$mb_board_reply}', + mb_board_comment = '{$mb_board_comment}', + mb_board_recomment = '{$mb_board_recomment}', mb_1 = '{$posts['mb_1']}', mb_2 = '{$posts['mb_2']}', mb_3 = '{$posts['mb_3']}', @@ -142,6 +157,36 @@ if ($w == '') { alert('이미 존재하는 이메일입니다.\\nID : ' . $row['mb_id'] . '\\n이름 : ' . $row['mb_name'] . '\\n닉네임 : ' . $row['mb_nick'] . '\\n메일 : ' . $row['mb_email']); } + $agree_items = []; + // 마케팅 목적의 개인정보 수집 및 이용 + if ($mb_marketing_agree == 1) { + $sql_common .= " , mb_marketing_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "마케팅 목적의 개인정보 수집 및 이용(동의)"; + } + + // 광고성 이메일 수신 + if ($mb_mailling == 1) { + $sql_common .= " , mb_mailling_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 이메일 수신(동의)"; + } + + // 광고성 SMS/카카오톡 수신 + if ($mb_sms == 1) { + $sql_common .= " , mb_sms_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 SMS/카카오톡 수신(동의)"; + } + + // 개인정보 제3자 제공 + if ($mb_thirdparty_agree == 1) { + $sql_common .= " , mb_thirdparty_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "개인정보 제3자 제공(동의)"; + } + + // 동의 로그 추가 + if (!empty($agree_items)) { + $agree_log = "[".G5_TIME_YMDHIS.", 관리자 회원추가] " . implode(' | ', $agree_items) . "\n"; + $sql_common .= " , mb_agree_log = CONCAT('{$agree_log}', IFNULL(mb_agree_log, ''))"; + } sql_query(" insert into {$g5['member_table']} set mb_id = '{$mb_id}', mb_password = '" . get_encrypt_string($mb_password) . "', mb_datetime = '" . G5_TIME_YMDHIS . "', mb_ip = '{$_SERVER['REMOTE_ADDR']}', mb_email_certify = '" . G5_TIME_YMDHIS . "', {$sql_common} "); } elseif ($w == 'u') { $mb = get_member($mb_id); @@ -193,10 +238,54 @@ if ($w == '') { $sql_certify = ""; } + // 현재 데이터 조회 + $row = sql_fetch("select * from {$g5['member_table']} where mb_id = '{$mb_id}' "); + $agree_items = []; + + // 마케팅 목적의 개인정보 수집 및 이용 + $sql_marketing_date = ""; + if ($row['mb_marketing_agree'] !== $mb_marketing_agree) { + $sql_marketing_date .= " , mb_marketing_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "마케팅 목적의 개인정보 수집 및 이용(" . ($mb_marketing_agree == 1 ? "동의" : "철회") . ")"; + } + + // 광고성 이메일 수신 + $sql_mailling_date = ""; + if ($row['mb_mailling'] !== $mb_mailling) { + $sql_mailling_date .= " , mb_mailling_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 이메일 수신(" . ($mb_mailling == 1 ? "동의" : "철회") . ")"; + } + + // 광고성 SMS/카카오톡 수신 + $sql_sms_date = ""; + if ($row['mb_sms'] !== $mb_sms) { + $sql_sms_date .= " , mb_sms_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 SMS/카카오톡 수신(" . ($mb_sms == 1 ? "동의" : "철회") . ")"; + } + + // 개인정보 제3자 제공 + $sql_thirdparty_date = ""; + if ($row['mb_thirdparty_agree'] !== $mb_thirdparty_agree) { + $sql_thirdparty_date .= " , mb_thirdparty_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "개인정보 제3자 제공(" . ($mb_thirdparty_agree == 1 ? "동의" : "철회") . ")"; + } + + // 동의 로그 추가 + $sql_agree_log = ""; + if (!empty($agree_items)) { + $agree_log = "[".G5_TIME_YMDHIS.", 관리자 회원수정] " . implode(' | ', $agree_items) . "\n"; + $sql_agree_log .= " , mb_agree_log = CONCAT('{$agree_log}', IFNULL(mb_agree_log, ''))"; + } + $sql = " update {$g5['member_table']} set {$sql_common} {$sql_password} {$sql_certify} + {$sql_mailling_date} + {$sql_sms_date} + {$sql_marketing_date} + {$sql_thirdparty_date} + {$sql_agree_log} where mb_id = '{$mb_id}' "; sql_query($sql); } else { diff --git a/adm/member_list.php b/adm/member_list.php index dc6348cdd..96abd12cf 100644 --- a/adm/member_list.php +++ b/adm/member_list.php @@ -127,7 +127,7 @@ $colspan = 16; | 본인확인 | 메일인증 | 정보공개 | -메일수신 | +광고성이메일수신 | 상태 | 휴대폰 | 최종접속 | @@ -137,7 +137,7 @@ $colspan = 16;||||||
| 이름 | 닉네임 | -SMS수신 | +광고성SMS/카카오톡수신 | 성인인증 | 접근차단 | 권한 | @@ -252,14 +252,15 @@ $colspan = 16; > -Yes' : 'No'; ?> | -+ | Yes' : 'No'; ?> | +value="1" id="mb_open_"> | -+ | value="1" id="mb_mailling_"> + | value="1" id="mb_sms_"> + |
diff --git a/adm/member_list_exel.lib.php b/adm/member_list_exel.lib.php
new file mode 100644
index 000000000..32e845670
--- /dev/null
+++ b/adm/member_list_exel.lib.php
@@ -0,0 +1,289 @@
+ [
+ 'mb_id'=>'아이디',
+ 'mb_name'=>'이름',
+ 'mb_nick'=>'닉네임',
+ 'mb_email'=>'이메일',
+ 'mb_tel'=>'전화번호',
+ 'mb_hp'=>'휴대폰번호',
+ 'mb_addr1'=>'주소'
+ ],
+ 'point_cond_map' => [
+ 'gte'=>'≥',
+ 'lte'=>'≤',
+ 'eq'=>'='
+ ],
+ 'intercept_list' => [
+ 'exclude'=>'차단회원 제외',
+ 'only'=>'차단회원만'
+ ],
+ 'ad_range_list' => [
+ 'all' => '수신동의 회원 전체',
+ 'mailling_only' => '이메일 수신동의 회원만',
+ 'sms_only' => 'SMS/카카오톡 수신동의 회원만',
+ 'month_confirm' => date('m월').' 수신동의 확인 대상만',
+ 'custom_period' => '수신동의 기간 직접 입력'
+ ],
+ ];
+
+ return $type ? ($config[$type] ?? []) : $config;
+}
+
+/**
+ * 파라미터 수집 및 유효성 검사
+ */
+function get_member_export_params()
+{
+ // 친구톡 양식 - 엑셀 양식에 포함할 항목
+ $fieldArray = array_map('trim', explode(',', $_GET['fields'] ?? ''));
+ $vars = [];
+ foreach ($fieldArray as $index => $field) {
+ if(!empty($field)){
+ $vars['var' . ($index + 1)] = $field;
+ }
+ }
+
+ $params = [
+ 'page' => 1,
+ 'formatType' => (int)($_GET['formatType'] ?? 1),
+ 'use_stx' => $_GET['use_stx'] ?? 0,
+ 'stx_cond' => clean_xss_tags($_GET['stx_cond'] ?? 'like'),
+ 'sfl' => clean_xss_tags($_GET['sfl'] ?? ''),
+ 'stx' => clean_xss_tags($_GET['stx'] ?? ''),
+ 'use_level' => $_GET['use_level'] ?? 0,
+ 'level_start' => (int)($_GET['level_start'] ?? 1),
+ 'level_end' => (int)($_GET['level_end'] ?? 10),
+ 'use_date' => $_GET['use_date'] ?? 0,
+ 'date_start' => clean_xss_tags($_GET['date_start'] ?? ''),
+ 'date_end' => clean_xss_tags($_GET['date_end'] ?? ''),
+ 'use_point' => $_GET['use_point'] ?? 0,
+ 'point' => $_GET['point'] ?? '',
+ 'point_cond' => $_GET['point_cond'] ?? 'gte',
+ 'use_hp_exist' => $_GET['use_hp_exist'] ?? 0,
+ 'ad_range_only' => $_GET['ad_range_only'] ?? 0,
+ 'ad_range_type' => clean_xss_tags($_GET['ad_range_type'] ?? 'all'),
+ 'ad_mailling' => $_GET['ad_mailling'] ?? 0,
+ 'ad_sms' => $_GET['ad_sms'] ?? 0,
+ 'agree_date_start' => clean_xss_tags($_GET['agree_date_start'] ?? ''),
+ 'agree_date_end' => clean_xss_tags($_GET['agree_date_end'] ?? ''),
+ 'use_intercept' => $_GET['use_intercept'] ?? 0,
+ 'intercept' => clean_xss_tags($_GET['intercept'] ?? 'exclude'),
+ 'vars' => $vars,
+ ];
+
+ // 레벨 범위 검증
+ if ($params['level_start'] > $params['level_end']) {
+ [$params['level_start'] , $params['level_end']] = [$params['level_end'], $params['level_start']];
+ }
+
+ // 가입기간 - 날짜 범위 검증
+ if ($params['use_date'] && $params['date_start'] && $params['date_end']) {
+ if ($params['date_start'] > $params['date_end']) {
+ [$params['date_start'] , $params['date_end']] = [$params['date_end'], $params['date_start']];
+ }
+ }
+
+ // 수신동의기간 - 날짜 범위 검증
+ if ($params['ad_range_type'] == 'custom_period' && $params['agree_date_start'] && $params['agree_date_end']) {
+ if ($params['agree_date_start'] > $params['agree_date_end']) {
+ [$params['agree_date_start'] , $params['agree_date_end']] = [$params['agree_date_end'], $params['agree_date_start']];
+ }
+ }
+
+ return $params;
+}
+
+/**
+ * 전체 데이터 개수 조회
+ */
+function member_export_get_total_count($params)
+{
+ global $g5;
+
+ $where = member_export_build_where($params);
+ $sql = "SELECT COUNT(*) as cnt FROM {$g5['member_table']} {$where}";
+
+ $result = sql_query($sql);
+ if (!$result) {
+ throw new Exception("데이터 조회에 실패하였습니다. 다시 시도해주세요.");
+ }
+
+ $row = sql_fetch_array($result);
+ return (int)$row['cnt'];
+}
+
+/**
+ * WHERE 조건절 생성
+ */
+function member_export_build_where($params)
+{
+ global $config;
+ $conditions = [];
+
+ // 기본 조건 - 탈퇴하지 않은 사용자
+ $conditions[] = "mb_leave_date = ''";
+
+ // 검색어 조건 (sql_escape_string 사용으로 보안 강화)
+ if (!empty($params['use_stx']) && $params['use_stx'] === '1') {
+ $sfl_list = get_export_config('sfl_list');
+ $sfl = in_array($params['sfl'], array_keys($sfl_list)) ? $params['sfl'] : '';
+ $stx = sql_escape_string($params['stx']);
+
+ if(!empty($sfl) && !empty($stx)){
+ if ($params['stx_cond'] === 'like') {
+ $conditions[] = "{$sfl} LIKE '%{$stx}%'";
+ } else {
+ $conditions[] = "{$sfl} = '{$stx}'";
+ }
+ }
+ }
+
+ // 권한 조건
+ if (!empty($params['use_level']) && $params['use_level'] === '1') {
+ $level_start = max(1, (int)$params['level_start']);
+ $level_end = min(10, (int)$params['level_end']);
+
+ $conditions[] = "(mb_level BETWEEN {$level_start} AND {$level_end})";
+ }
+
+ // 가입기간 조건
+ if (!empty($params['use_date']) && $params['use_date'] === '1') {
+ $date_start = isset($params['date_start']) ? sql_escape_string(trim($params['date_start'])) : '';
+ $date_end = isset($params['date_end']) ? sql_escape_string(trim($params['date_end'])) : '';
+
+ if ($date_start && $date_end) {
+ $conditions[] = "mb_datetime BETWEEN '{$date_start} 00:00:00' AND '{$date_end} 23:59:59'";
+ } elseif ($date_start) {
+ $conditions[] = "mb_datetime >= '{$date_start} 00:00:00'";
+ } elseif ($date_end) {
+ $conditions[] = "mb_datetime <= '{$date_end} 23:59:59'";
+ }
+ }
+
+ // 포인트 조건
+ if (!empty($params['use_point']) && $params['use_point'] === '1') {
+ $point = $params['point'];
+ $point_cond = $params['point_cond'];
+
+ if ($point != '') {
+ $point = (int)$point; // 정수로 캐스팅
+
+ switch ($point_cond) {
+ case 'lte':
+ $conditions[] = "mb_point <= {$point}";
+ break;
+ case 'eq':
+ $conditions[] = "mb_point = {$point}";
+ break;
+ default:
+ $conditions[] = "mb_point >= {$point}";
+ break;
+ }
+ }
+ }
+
+ // 휴대폰 번호 존재 조건
+ if (!empty($params['use_hp_exist']) && $params['use_hp_exist'] === '1') {
+ $conditions[] = "(mb_hp is not null and mb_hp != '')";
+ }
+
+ // 정보수신동의 조건
+ if (!empty($params['ad_range_only']) && $params['ad_range_only'] === '1') {
+ $range = $params['ad_range_type'] ?? '';
+
+ // 공통: 마케팅 목적 수집·이용 동의 + (필요 시) 제3자 동의
+ $needs_thirdparty = ($config['cf_sms_use'] !== '' || $config['cf_kakaotalk_use'] !== '');
+ $thirdparty_clause = $needs_thirdparty ? " AND mb_thirdparty_agree = 1" : "";
+ $base_marketing = "mb_marketing_agree = 1{$thirdparty_clause}";
+
+ if ($range === 'all') {
+ // 마케팅 동의 + (이메일 OR SMS 동의)
+ $conditions[] = "({$base_marketing} AND (mb_mailling = 1 OR mb_sms = 1))";
+ } elseif ($range === 'mailling_only') {
+ // 마케팅 동의 + 이메일 동의
+ $conditions[] = "({$base_marketing} AND mb_mailling = 1)";
+ } elseif ($range === 'sms_only') {
+ // 마케팅 동의 + SMS/카카오톡 동의
+ $conditions[] = "({$base_marketing} AND mb_sms = 1)";
+ } elseif ($range === 'month_confirm' || $range === 'custom_period') {
+ // 채널 필터 체크
+ $useEmail = !empty($params['ad_mailling']);
+ $useSms = !empty($params['ad_sms']);
+
+ if ($range === 'month_confirm') {
+ // 23개월 전 그 달
+ $start = date('Y-m-01 00:00:00', strtotime('-23 months'));
+ $end = date('Y-m-t 23:59:59', strtotime('-23 months'));
+ $emailDateCond = "mb_mailling_date BETWEEN '{$start}' AND '{$end}'";
+ $smsDateCond = "mb_sms_date BETWEEN '{$start}' AND '{$end}'";
+
+ } else {
+ // 수신동의기간 직접 입력 - custom_period
+ $date_start = $params['agree_date_start'] ?? '';
+ $date_end = $params['agree_date_end'] ?? '';
+
+ if ($date_start && $date_end) {
+ $emailDateCond = "mb_mailling_date BETWEEN '{$date_start} 00:00:00' AND '{$date_end} 23:59:59'";
+ $smsDateCond = "mb_sms_date BETWEEN '{$date_start} 00:00:00' AND '{$date_end} 23:59:59'";
+ } elseif ($date_start) {
+ $emailDateCond = "mb_mailling_date >= '{$date_start} 00:00:00'";
+ $smsDateCond = "mb_sms_date >= '{$date_start} 00:00:00'";
+ } elseif ($date_end) {
+ $emailDateCond = "mb_mailling_date <= '{$date_end} 23:59:59'";
+ $smsDateCond = "mb_sms_date <= '{$date_end} 23:59:59'";
+ } else {
+ $emailDateCond = "mb_mailling_date <> '0000-00-00 00:00:00'";
+ $smsDateCond = "mb_sms_date <> '0000-00-00 00:00:00'";
+ }
+ }
+
+ if (!$useEmail && !$useSms) {
+ $conditions[] = "0=1"; // 둘 다 해제 ⇒ 결과 0건
+ } else {
+ // 조건 조립
+ $parts = [];
+ if ($useEmail) $parts[] = "(mb_mailling = 1 AND {$emailDateCond})";
+ if ($useSms) $parts[] = "(mb_sms = 1 AND {$smsDateCond})";
+
+ $conditions[] = !empty($parts) ? '(' . implode(' OR ', $parts) . ')' : '';
+ }
+ }
+ }
+
+ // 차단 회원 조건
+ if (!empty($params['use_intercept']) && $params['use_intercept'] === '1') {
+ switch ($params['intercept']) {
+ case 'exclude':
+ $conditions[] = "mb_intercept_date = ''";
+ break;
+ case 'only':
+ $conditions[] = "mb_intercept_date != ''";
+ break;
+ }
+ }
+
+ return empty($conditions) ? '' : 'WHERE ' . implode(' AND ', $conditions);
+}
diff --git a/adm/member_list_exel.php b/adm/member_list_exel.php
new file mode 100644
index 000000000..912b97647
--- /dev/null
+++ b/adm/member_list_exel.php
@@ -0,0 +1,578 @@
+getMessage(); // 메서드 호출 괄호 필수
+}
+
+$g5['title'] = '회원관리파일';
+require_once './admin.head.php';
+$colspan = 14;
+?>
+
+회원 엑셀 생성+ +
+
+
+
+회원수 건 초과 시 건 단위로 분리 저장되며, 엑셀 생성 최대 건수는 건입니다. 초과 시 조건 추가 설정 후 재시도하시기 바랍니다. +수신동의 확인 대상은 만료일까지 1달 미만인 회원을 기준으로 필터링됩니다. + ++ 파일 생성 시 서버에 임시 생성된 파일 중 오늘 날짜를 제외 한 파일은 자동 삭제되며, 수동 삭제 필요 시 회원관리파일 일괄삭제에서 진행하시기 바랍니다. +회원 정보 수정은 회원 관리에서 진행하실 수 있습니다. + ++ 친구톡 양식은 카카오톡 사용 시에만 이용 가능합니다. + +친구톡 (광고성 카카오톡 포함)의 경우 기존 회원 데이터 엑셀 파일 다운로드 후 상단 [친구톡 보내기] 버튼을 누르면 팝빌 홈페이지로 이동하여 업로드 진행하실 수 있습니다. + +
+
+
+ 친구톡 전송하기
+
+
+
+
+
+
+ 총건수
+
+
+
+ 건
+
+
+
+
+
+
+
+
+
+
+
+ false, 'error' => '데이터가 올바르게 전달되지 않아 작업에 실패하였습니다.']);
+ exit;
+}
+
+// 기존 생성된 엑셀 파일 삭제 - LOG 및 오늘 날짜 폴더 제외
+$resultExcelDelete = member_export_delete();
+
+// 서버 전송 이벤트(SSE)를 위한 헤더 설정
+member_export_set_sse_headers();
+
+// 모드 확인
+$mode = $_GET['mode'] ?? '';
+if ($mode !== 'start') {
+ member_export_send_progress("error", "잘못된 요청 입니다.");
+ member_export_write_log($params, ['success' => false, 'error' => '잘못된 요청 입니다.']);
+ exit;
+}
+
+/**
+ * 회원 내보내기 처리 실행 (예외 처리 포함)
+ */
+try {
+ main_member_export($params);
+}
+catch (Exception $e)
+{
+ // 에러 로그 저장 및 SSE 에러 전송
+ error_log("[Member Export Error] " . $e->getMessage());
+ member_export_send_progress("error", $e->getMessage());
+ member_export_write_log($params, ['success' => false, 'error' => $e->getMessage()]);
+}
+
+/**
+ * 메인 내보내기 프로세스
+ */
+function main_member_export($params)
+{
+ $total = member_export_get_total_count($params);
+
+ if($total > MEMBER_EXPORT_MAX_SIZE){
+ throw new Exception("엑셀 다운로드 가능 범위(최대 " . number_format(MEMBER_EXPORT_MAX_SIZE) . "건)를 초과했습니다.조건을 추가로 설정하신 후 다시 시도해 주세요."); + } + + if($total <= 0){ + throw new Exception("조회된 데이터가 없어 엑셀 파일을 생성할 수 없습니다. 조건을 추가로 설정하신 후 다시 시도해 주세요."); + } + + $fileName = 'member_'.MEMBER_BASE_DATE; + $fileList = []; + $zipFileName = ''; + + if ($total > MEMBER_EXPORT_PAGE_SIZE) { + // 대용량 데이터 - 분할 처리 + $pages = (int)ceil($total / MEMBER_EXPORT_PAGE_SIZE); + member_export_send_progress("progress", "", 2, $total, 0, $pages, 0); + + for ($i = 1; $i <= $pages; $i++) { + $params['page'] = $i; + + member_export_send_progress("progress", "", 2, $total, ($pages == $i ? $total : $i * MEMBER_EXPORT_PAGE_SIZE), $pages, $i); + try { + $data = member_export_get_data($params); + $fileList[] = member_export_create_excel($data, $fileName, $i, $params['formatType']); + } catch (Exception $e) { + throw new Exception("총 {$pages}개 중 {$i}번째 파일을 생성하지 못했습니다 " . $e->getMessage()); + } + } + + // 압축 파일 생성 + if (count($fileList) > 1) { + member_export_send_progress("zipping", "", 2, $total, $total, $pages, $i); + $zipResult = member_export_create_zip($fileList, $fileName); // 압축 파일 생성 + + if($zipResult['error']){ + member_export_write_log($params, ['success' => false, 'error' => $zipResult['error']]); + member_export_send_progress("zippingError", $zipResult['error']); + } + + if ($zipResult && $zipResult['result']) { + member_export_delete($fileList); // 압축 후 엑셀 파일 제거 + $zipFileName = $zipResult['zipFile']; + } + } + + } else { + // 소용량 데이터 - 단일 파일 + member_export_send_progress("progress", "", 1, $total, 0); + $data = member_export_get_data($params); + member_export_send_progress("progress", "", 1, $total, $total/2); + $fileList[] = member_export_create_excel($data, $fileName, 0, $params['formatType']); + member_export_send_progress("progress", "", 1, $total, $total); + } + + member_export_write_log($params, ['success' => true, 'total' => $total, 'files' => $fileList, 'zip' => $zipFileName ?? null]); + member_export_send_progress("done", "", 2, $total, $total, $pages, $pages, $fileList, $zipFileName); +} + +/** + * 진행률 전송 + */ +function member_export_send_progress($status, $message = "", $downloadType = 1, $total = 1, $current = 1, $totalChunks = 1, $currentChunk = 1, $files = [], $zipFile = '') +{ + // 연결 상태 확인 + if (connection_aborted()) return; + + $data = [ + 'status' => $status, + 'message' => $message, + 'downloadType' => $downloadType, + 'total' => $total, + 'current' => $current, + 'totalChunks' => $totalChunks, + 'currentChunk' => $currentChunk, + 'files' => $files, + 'zipFile' => $zipFile, + 'filePath' => G5_DATA_URL . "/" . MEMBER_BASE_DIR . "/" . MEMBER_BASE_DATE, + ]; + + echo "data: " . json_encode($data, JSON_UNESCAPED_UNICODE) . "\n\n"; + + // 더 안정적인 플러시 + if (ob_get_level()) ob_end_flush(); + flush(); +} + +/** + * 엑셀 내보내기 설정 + */ +function member_export_get_config($type) +{ + $configs = [ + 1 => [ + 'title' => ["회원관리파일(일반)"], + 'headers' => ['아이디', '이름', '닉네임', '휴대폰번호', '전화번호', '이메일', '주소', '회원권한', '포인트', '가입일', '차단', + '광고성 이메일 수신동의', '광고성 이메일 동의일자', '광고성 SMS/카카오톡 수신동의', '광고성 SMS/카카오톡 동의일자', + '마케팅목적의개인정보수집및이용동의', '마케팅목적의개인정보수집및이용동의일자', '개인정보제3자제공동의', '개인정보제3자제공동의일자'], + 'fields' => ['mb_id', 'mb_name', 'mb_nick', 'mb_hp', 'mb_tel', 'mb_email', 'mb_addr1', 'mb_level', 'mb_point', 'mb_datetime', 'mb_intercept_date', + 'mb_mailling','mb_mailling_date', 'mb_sms','mb_sms_date', 'mb_marketing_agree', + 'mb_marketing_date', 'mb_thirdparty_agree', 'mb_thirdparty_date'], + 'widths' => [20, 20, 20, 20, 20, 30, 30, 10, 15, 25, 10, 20, 25, 20, 25, 20, 25, 20, 25], + ], + 2 => [ + 'title' => ["회원관리파일(팝빌)"], + 'headers' => ['휴대폰번호', '이름', '변수1', '변수2', '변수3'], + 'fields' => ['mb_hp', 'mb_name'], + 'widths' => [20, 15, 30, 30, 30], + ], + ]; + + return isset($configs[$type]) ? $configs[$type] : $configs[1]; +} + +/** + * SSE 헤더 설정 + */ +function member_export_set_sse_headers() +{ + header('Content-Type: text/event-stream'); + header('Cache-Control: no-cache'); + header('Connection: keep-alive'); + header('X-Accel-Buffering: no'); + + if (ob_get_level()) ob_end_flush(); + ob_implicit_flush(true); +} + +/** + * 엑셀 컬럼 문자 반환 + */ +function member_export_column_char($i) +{ + return chr(65 + $i); +} + +/** + * 회원 데이터 조회 + */ +function member_export_get_data($params) +{ + global $g5; + + $config = member_export_get_config($params['formatType']); + $fields = $config['fields']; + + // 팝빌 타입인 경우 var 추가 + if ($params['formatType'] == 2 && !empty($params['vars'])) { + $fields = array_merge($fields, array_values($params['vars'])); + } + + $fields = array_unique($fields); + + // SQL 변환 맵 (가공이 필요한 필드만 정의) + $sqlTransformMap = [ + 'mb_datetime' => "IF(mb_datetime = '0000-00-00 00:00:00', '', mb_datetime) AS mb_datetime", + 'mb_intercept_date' => "IF(mb_intercept_date != '', '차단됨', '정상') AS mb_intercept_date", + 'mb_sms' => "IF(mb_sms = '1', '동의', '미동의') AS mb_sms", + 'mb_sms_date' => "IF(mb_sms != '1' OR mb_sms_date = '0000-00-00 00:00:00', '', mb_sms_date) AS mb_sms_date", + 'mb_mailling' => "IF(mb_mailling = '1', '동의', '미동의') AS mb_mailling", + 'mb_mailling_date' => "IF(mb_mailling != '1' OR mb_mailling_date = '0000-00-00 00:00:00', '', mb_mailling_date) AS mb_mailling_date", + 'mb_marketing_agree' => "IF(mb_marketing_agree = '1', '동의', '미동의') AS mb_marketing_agree", + 'mb_marketing_date' => "IF(mb_marketing_agree != '1' OR mb_marketing_date = '0000-00-00 00:00:00', '', mb_marketing_date) AS mb_marketing_date", + 'mb_thirdparty_agree' => "IF(mb_thirdparty_agree = '1', '동의', '미동의') AS mb_thirdparty_agree", + 'mb_thirdparty_date' => "IF(mb_thirdparty_agree != '1' OR mb_thirdparty_date = '0000-00-00 00:00:00', '', mb_thirdparty_date) AS mb_thirdparty_date", + ]; + + // SQL 필드 생성 + $sqlFields = []; + foreach ($fields as $field) { + $sqlFields[] = $sqlTransformMap[$field] ?? $field; + } + $field_list = implode(', ', $sqlFields); + + $where = member_export_build_where($params); + + $page = (int)($params['page'] ?? 1); + if ($page < 1) $page = 1; + $offset = ($page - 1) * MEMBER_EXPORT_PAGE_SIZE; + + $sql = "SELECT {$field_list} FROM {$g5['member_table']} {$where} ORDER BY mb_no DESC LIMIT {$offset}, " . MEMBER_EXPORT_PAGE_SIZE; + + $result = sql_query($sql); + if (!$result) { + throw new Exception("데이터 조회에 실패하였습니다"); + } + + $excelData = [$config['title'], $config['headers']]; + + while ($row = sql_fetch_array($result)) { + $rowData = []; + foreach ($fields as $field) { + $rowData[] = $row[$field] ?? ''; + } + $excelData[] = $rowData; + } + + return $excelData; +} + +/** + * 엑셀 파일 생성 + */ +function member_export_create_excel($data, $fileName, $index = 0, $type = 1) +{ + $config = member_export_get_config($type); + + if (!class_exists('PHPExcel')) { + error_log('[Member Export Error] PHPExcel 라이브러리를 찾을 수 없습니다.'); + throw new Exception('파일 생성 중 내부 오류가 발생했습니다: PHPExcel 라이브러리를 찾을 수 없습니다.'); + } + + // 현재 설정값 백업 + $currentCache = PHPExcel_Settings::getCacheStorageMethod(); + + // 캐싱 모드 설정 (엑셀 생성 전용) + $cacheMethods = [ + PHPExcel_CachedObjectStorageFactory::cache_to_discISAM, + PHPExcel_CachedObjectStorageFactory::cache_in_memory_serialized + ]; + + foreach ($cacheMethods as $method) { + if (PHPExcel_Settings::setCacheStorageMethod($method)) { + break; + } + } + + try { + $excel = new PHPExcel(); + $sheet = $excel->setActiveSheetIndex(0); + + // 헤더 스타일 적용 + $last_char = member_export_column_char(count($config['headers']) - 1); + $sheet->getStyle("A2:{$last_char}2")->applyFromArray([ + 'fill' => [ + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => ['rgb' => 'D9E1F2'], // 연파랑 배경 + ], + ]); + + // 셀 정렬 및 줄바꿈 설정 + $sheet->getStyle("A:{$last_char}")->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER)->setWrapText(true); + + // 컬럼 너비 설정 + foreach ($config['widths'] as $i => $width) { + $sheet->getColumnDimension(member_export_column_char($i))->setWidth($width); + } + + // 데이터 입력 + $sheet->fromArray($data, NULL, 'A1'); + + // 디렉토리 확인 + member_export_ensure_directory(MEMBER_EXPORT_DIR); + + // 파일명 생성 + $subname = $index == 0 ? 'all' : sprintf("%02d", $index); + $filename = $fileName . "_" . $subname . ".xlsx"; + $filePath = MEMBER_EXPORT_DIR . "/" . $filename; + + // 파일 저장 + $writer = PHPExcel_IOFactory::createWriter($excel, 'Excel2007'); + $writer->setPreCalculateFormulas(false); + $writer->save($filePath); + + unset($excel, $sheet, $writer); // 생성 완료 후 메모리 해제 + } + catch (Exception $e) + { + throw new Exception("엑셀 파일 생성에 실패하였습니다: " . $e->getMessage()); + } + finally + { + // 캐싱 모드 원래 상태로 복원 + if ($currentCache) { + PHPExcel_Settings::setCacheStorageMethod($currentCache); + } + } + + return $filename; +} + +/** + * 압축 파일 생성 + */ +function member_export_create_zip($files, $zipFileName) +{ + if (!class_exists('ZipArchive')) { + error_log('[Member Export Error] ZipArchive 클래스를 사용할 수 없습니다.'); + return ['error' => '파일을 압축하는 중 문제가 발생했습니다. 개별 파일로 제공됩니다. : ZipArchive 클래스를 사용할 수 없습니다.']; + } + + member_export_ensure_directory(MEMBER_EXPORT_DIR); + $destinationZipPath = rtrim(MEMBER_EXPORT_DIR, "/") . "/" . $zipFileName . ".zip"; + + $zip = new ZipArchive(); + if ($zip->open($destinationZipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) { + return ['error' => "파일을 압축하는 중 문제가 발생했습니다. 개별 파일로 제공됩니다."]; + } + + foreach ($files as $file) { + $filePath = MEMBER_EXPORT_DIR . "/" . $file; + if (file_exists($filePath)) { + $zip->addFile($filePath, basename($filePath)); + } + } + + $result = $zip->close(); + + return [ + 'result' => $result, + 'zipFile' => $zipFileName . ".zip", + 'zipPath' => $destinationZipPath, + ]; +} + +/** + * 디렉토리 생성 및 확인 + */ +function member_export_ensure_directory($dir) +{ + if (!is_dir($dir)) { + if (!@mkdir($dir, G5_DIR_PERMISSION, true)) { + throw new Exception("디렉토리 생성 실패"); + } + @chmod($dir, G5_DIR_PERMISSION); + } + + if (!is_writable($dir)) { + throw new Exception("디렉토리 쓰기 권한 없음"); + } +} + +/** + * 파일 삭제 - 값이 있으면 해당 파일만 삭제, 없으면 디렉토리 내 모든 파일 삭제 + * - 알집 생성 완료 시 엑셀 파일 제거 + * - 작업 전 오늘 날짜 폴더 및 log 폴더를 제외한 나머지 파일 모두 제거 + */ +function member_export_delete($fileList = []) +{ + $cnt = 0; + + // 파일 리스트가 있는 경우 -> 해당 파일만 삭제 + if (!empty($fileList)) { + foreach ($fileList as $file) { + $filePath = rtrim(MEMBER_EXPORT_DIR, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file; + if (file_exists($filePath) && is_file($filePath) && @unlink($filePath)) { + $cnt++; + } + } + } + // 파일 리스트가 없는 경우 -> 디렉토리 내 모든 파일 삭제 + else { + $files = glob(rtrim(G5_DATA_PATH . "/" . MEMBER_BASE_DIR, '/') . '/*'); + + function deleteFolder($dir) { + foreach (glob($dir . '/{.,}*', GLOB_BRACE) as $item) { + if (in_array(basename($item), ['.', '..'])) continue; + is_dir($item) ? deleteFolder($item) : unlink($item); + } + rmdir($dir); + } + + foreach ($files as $file) { + $name = basename($file); + + // log 폴더와 오늘 날짜로 시작하는 폴더는 제외 + if ($name === 'log' || preg_match('/^' . date('Ymd') . '\d{6}$/', $name)) continue; + + if (is_file($file) && pathinfo($file, PATHINFO_EXTENSION) !== 'log' && @unlink($file)) { + $cnt++; + } elseif (is_dir($file)) { + deleteFolder($file); // 재귀 폴더 삭제 함수 사용 + $cnt++; + } + } + } + + return $cnt; +} + +/** + * 로그 작성 + */ +function member_export_write_log($params, $result = []) +{ + global $member; + + $maxSize = 1024 * 1024 * 2; // 2MB + $maxFiles = 10; // 최대 로그 파일 수 (필요시 조정) + $username = $member['mb_id'] ?? 'guest'; + $datetime = date("Y-m-d H:i:s"); + + if (!is_dir(MEMBER_LOG_DIR)) { + @mkdir(MEMBER_LOG_DIR, G5_DIR_PERMISSION, true); + @chmod(MEMBER_LOG_DIR, G5_DIR_PERMISSION); + } + + $logFiles = glob(MEMBER_LOG_DIR . "/export_log_*.log") ?: []; + + // 최신 파일 기준 정렬 (최신 → 오래된) + usort($logFiles, fn($a, $b) => filemtime($b) - filemtime($a)); + + $latestLogFile = $logFiles[0] ?? null; + + // 용량 기준으로 새 파일 생성 + if (!$latestLogFile || filesize($latestLogFile) >= $maxSize) { + $latestLogFile = MEMBER_LOG_DIR . "/export_log_" . date("YmdHi") . ".log"; + file_put_contents($latestLogFile, ''); + array_unshift($logFiles, $latestLogFile); + } + + // 최대 파일 수 초과 시 오래된 파일 제거 + if (count($logFiles) > $maxFiles) { + $filesToDelete = array_slice($logFiles, $maxFiles); + foreach ($filesToDelete as $file) { + @unlink($file); + } + } + + $formatType = (isset($params['formatType']) && $params['formatType'] == 2) ? '팝빌' : '일반'; + $success = isset($result['success']) && $result['success'] === true; + $status = $success ? '성공' : '실패'; + + // 조건 정리 + $condition = []; + + // 검색 조건 + if ($params['use_stx'] == 1 && !empty($params['stx'])) { + $sfl_list = get_export_config('sfl_list'); + + $label = $sfl_list[$params['sfl']] ?? ''; + $condition[] = "검색({$params['stx_cond']}) : {$label} - {$params['stx']}"; + } + + // 레벨 조건 + if ($params['use_level'] == 1 && ($params['level_start'] || $params['level_end'])) { + $condition[] = "레벨: {$params['level_start']}~{$params['level_end']}"; + } + + // 가입일 조건 + if ($params['use_date'] == 1 && ($params['date_start'] || $params['date_end'])) { + $condition[] = "가입일: {$params['date_start']}~{$params['date_end']}"; + } + + // 포인트 조건 + if ($params['use_point'] == 1 && $params['point'] !== '') { + $point_cond_map = get_export_config('point_cond_map'); + $symbol = $point_cond_map[$params['point_cond']] ?? '≥'; + $condition[] = "포인트 {$symbol} {$params['point']}"; + } + + // 휴대폰 여부 + if ($params['use_hp_exist'] == 1) { + $condition[] = "휴대폰번호 있는 경우만"; + } + + // 광고 수신 동의 + if ($params['ad_range_only'] == 1) { + $ad_range_list = get_export_config('ad_range_list'); + $label = $ad_range_list[$params['ad_range_type']] ?? ''; + $condition[] = "수신동의: 예 ({$label})"; + + if ($params['ad_range_type'] == "custom_period" && ($params['agree_date_start'] || $params['agree_date_end'])) { + $condition[] = "수신동의일: {$params['agree_date_start']}~{$params['agree_date_end']}"; + } + + if (in_array($params['ad_range_type'], ["month_confirm", "custom_period"])){ + $channels = array_filter([ + !empty($params['ad_mailling']) && (int)$params['ad_mailling'] === 1 ? '이메일' : null, + !empty($params['ad_sms']) && (int)$params['ad_sms'] === 1 ? 'SMS/카카오톡' : null, + ]); + + if ($channels) { + $condition[] = '수신채널: ' . implode(', ', $channels); + } + } + } + + // 차단회원 처리 + if ($params['use_intercept'] == 1) { + $intercept_list = get_export_config('intercept_list'); + $label = $intercept_list[$params['intercept']] ?? ''; + if ($label) $condition[] = $label; + } + + $conditionStr = !empty($condition) ? implode(', ', $condition) : '없음'; + $line1 = "[{$datetime}] [{$status}] 관리자: {$username} | 형식: {$formatType}"; + + // 성공일 경우 추가 정보 + if ($success) { + $total = $result['total'] ?? 0; + $fileCount = isset($result['zip']) ? 1 : count($result['files'] ?? []); + $line1 .= " | 총 {$total}건 | 파일: {$fileCount}개"; + } + + $logEntry = $line1 . PHP_EOL; + $logEntry .= "조건: {$conditionStr}" . PHP_EOL; + + if (!$success && !empty($result['error'])) { + $logEntry .= "오류 메시지: {$result['error']}" . PHP_EOL; + } + + $logEntry .= PHP_EOL; + + // 파일에 기록 + if (@file_put_contents($latestLogFile, $logEntry, FILE_APPEND | LOCK_EX) === false) { + error_log("[Member Export Error] 로그 파일 기록 실패: {$latestLogFile}"); + } +} \ No newline at end of file diff --git a/adm/member_list_file_delete.php b/adm/member_list_file_delete.php new file mode 100644 index 000000000..d6c1fb058 --- /dev/null +++ b/adm/member_list_file_delete.php @@ -0,0 +1,72 @@ + + +
+
+
+회원관리파일를 열지못했습니다.';
+}
+
+$cnt = 0;
+echo '+ 완료 메세지가 나오기 전에 프로그램의 실행을 중지하지 마십시오. + +
회원관리파일 ' . $cnt . '건 삭제 완료됐습니다.
+
+
+
diff --git a/adm/shop_admin/itemform.php b/adm/shop_admin/itemform.php
index 0f6ec2a11..1ebf105eb 100644
--- a/adm/shop_admin/itemform.php
+++ b/adm/shop_admin/itemform.php
@@ -680,9 +680,9 @@ $(function(){
|
| + | - + > 예 | 상품명 | 휴대폰번호 | -SMS전송 | -SMS전송일시 | +전송결과 | +전송채널 | +전송일시 | 등록일시 | @@ -139,13 +146,14 @@ $listall = '전체목+ | 자료가 없습니다. | '; + echo '|||
| 자료가 없습니다. | ||||||||||||||
주문, 입금, 준비, 배송, 완료는 장바구니와 주문서 상태를 모두 변경하지만, 취소, 반품, 품절은 장바구니의 상태만 변경하며, 주문서 상태는 변경하지 않습니다.
개별적인(이곳에서의) 상태 변경은 모든 작업을 수동으로 처리합니다. 예를 들어 주문에서 입금으로 상태 변경시 입금액(결제금액)을 포함한 모든 정보는 수동 입력으로 처리하셔야 합니다.
+ +* 알림톡 프리셋: [준비, 완료, 취소, 반품, 품절]은 자동으로 발송되며, [입금완료, 배송]은 결제상세정보에서 수동으로 발송하셔야 합니다.
+ @@ -719,6 +723,16 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js