
현재 관리자모드 좌측 메뉴의 "웹진관리"는 웹진 콘텐츠 제작(카테고리/웹진 CRUD, WebzineAdminController, )만 담당한다. 여기에 이메일 뉴스레터 기능(구독자 관리 + 발송)을 추가해, 방문자가 웹진 하단에서 구독 신청/취소를 할 수 있고, 관리자는 구독자를 관리하며 특정 웹진을 구독자들에게 메일로 발송할 수 있게 한다. 프로젝트는 프레임워크 없는 커스텀 라우터(의 flat if-elseif 체인) + PDO 직접 사용 구조이며, 메일 발송 관련 코드/라이브러리는 전무하다(Composer/vendor 없음).
사용자 확인 사항:
- 발송 이메일 본문은 웹진의 기존 썸네일 이미지 1장(이미지 클릭 시 새 창으로 실제 웹진 페이지 이동) + 하단에 텍스트 박스(구독안내/구독취소 안내)로 구성. 캔버스 디자인 전체를 이메일 HTML로 재현하지 않는다.
- 발송은 PHP 기본
mail()함수로 처리(신규 라이브러리 도입 없음). 환경설정 화면에 "보내는 이메일 화이트리스트 등록 안내" 문구를 안내용으로 표시.
1. DB 스키마 (, 기존 스타일의 단독 실행 스크립트)
구독자는 그룹 1개만 소속(단순 FK, webzine_categories/webzines 관계 재사용). email에 tenant 단위 UNIQUE를 걸어 공개 구독신청 시 upsert(재구독 시 status만 되돌림)가 가능하게 한다.
2. 좌측 메뉴 구조 변경 ()
현재 단일 <a class="nav-item">웹진관리</a> 를, 이미 존재하는 게시판관리/쇼핑몰관리와 동일한 .nav-group / .nav-item-parent / .nav-submenu 패턴으로 교체:
- 웹진관리 (기존
admin/webzine그대로 연결) - 구독관리 (
admin/webzine/subscribers) - 웹진발송 (
admin/webzine/send)
isActive('webzine', $curr)가 세 URL 모두를 이미 포함하므로 부모 active 판정에 그대로 재사용 가능.
3. 라우팅 추가 (, 기존 webzine 라우트 블록 근처 482줄 부근)
관리자용 (신규 컨트롤러 App\Controllers\NewsletterAdminController):
admin/webzine/subscribers→subscribers()(검색+목록,?sub=list|groups)admin/webzine/subscribers/save→subscriberSave()(등록/수정, POST)admin/webzine/subscribers/delete/(\d+)→subscriberDelete($id)admin/webzine/subscribers/group-save→groupSave()admin/webzine/subscribers/group-delete/(\d+)→groupDelete($id)admin/webzine/send→send()(?tab=compose|history|settings)admin/webzine/send/settings-save→sendSettingsSave()admin/webzine/send/execute→sendExecute()(AJAX, JSON in/out — init/batch/finish 액션)
공개용 (WebzineController에 메서드 추가):
webzine/subscribe→subscribe()(POST, 구독 신청)webzine/unsubscribe→unsubscribe()(POST, 구독 취소)
4. 컨트롤러 설계
NewsletterAdminController (신규, WebzineAdminController 생성자와 동일한 레벨 체크 >= 5 복제)
subscribers():$_GET의name/phone/email/group_id조건으로WHERE tenant_id=?+ 동적AND절 구성 (TenantAdminController::members()패턴 재사용,%like%바인딩). 상단에COUNT(*)전체 건수, 그룹 목록(select용) 조회.sub=groups일 땐webzine_subscriber_groups목록 렌더(카테고리 관리 탭과 동일 구조).subscriberSave(): id 유무로 insert/update. 신규 등록 시source='collected'(관리자 직접 등록), 이메일 중복 시 에러 리다이렉트.subscriberDelete($id): tenant 스코프 delete.groupSave()/groupDelete($id):webzine_subscriber_groupsCRUD (webzine 카테고리 저장/삭제 메서드 그대로 패턴 복제).send():tab=compose(기본): 발행된 웹진 목록(select), 구독자 검색+목록(그룹/성명/전화/이메일 + "구독취소자 제외" 체크박스, 기본 체크 →status='active'만), 전체 건수.tab=history:webzine_sends목록(제목/날짜/건수) 좌측,?id=로 선택된 발송의webzine_send_recipients상세를 우측에 렌더.tab=settings:webzine_newsletter_settings조회(없으면 빈 폼 기본값), 화이트리스트 안내 정적 텍스트 블록 포함.
sendSettingsSave(): upsertwebzine_newsletter_settings(insert-or-update,TenantSetting::updateSettings패턴처럼 존재 여부 확인 후 분기).sendExecute(): JSON 액션 3종, 세션 인증 재확인 후 처리action=init:webzine_id,title,subscriber_ids[]받아 유효성 확인(웹진 존재+thumbnail_path필수 — 없으면 에러 JSON) 후webzine_sendsinsert,send_id+총 건수 반환.action=batch:send_id+ 이번 배치subscriber_ids[]받아 각 구독자에게mail()발송(제목은subject_format치환, 본문은 아래 5번 템플릿), 결과를webzine_send_recipients에 insert, 배치별 성공/실패 카운트 JSON 반환.action=finish:webzine_sends의recipient_count/success_count/fail_count합계 업데이트.- 배치 크기는 서버가 아닌 클라이언트가 나누어 호출(예: 15건씩) — PHP
max_execution_time이슈 회피, 프론트에 진행률(N/전체) 표시.
메일 발송 헬퍼는 NewsletterAdminController 내 private 메서드 sendOne($settings, $webzine, $subscriber)로 구현: mail($to, $subject, $htmlBody, $headers), Content-Type: text/html; charset=UTF-8, From: {sender_name} <{sender_email}> 헤더 구성.
WebzineController 추가 메서드
subscribe(): POST(name/phone/email/agree) →webzine_subscribers에INSERT ... ON DUPLICATE KEY UPDATE name=?, phone=?, status='active'(tenant_id+email UNIQUE 활용),source='request'. JSON 또는 리다이렉트+플래시 메시지 반환(모달에서 fetch 사용 예정이므로 JSON 응답 채택,BaseController::json()재사용).unsubscribe(): POST(name/email) → 일치하는 구독자status='cancelled'로 UPDATE, JSON 응답.
5. 공개 화면 (구독 신청/취소 박스 + 발송 이메일)
웹진 하단 구독 박스 —
canvas-wrapper 이후, 댓글 섹션 이전에 삽입. 컨트롤러(WebzineController::view)에서 webzine_newsletter_settings를 조회해 newsletterSettings 로 뷰에 전달, footer_text가 있을 때만 박스 렌더:
- 박스 안에
footer_text텍스트 + "웹진구독"/"구독취소" 두 버튼. - 두 모달은
webzine_list.php의.modal-overlay/.modal-contentCSS를 그대로 재사용(신규 스타일 불필요). - 웹진구독 모달: 성명/전화번호/이메일 입력 + "신청한 정보는 웹진 구독신청용으로만 사용됩니다." 안내문 + 이용동의 체크박스(필수) + 확인/취소. 확인 시
fetch(TENANT_BASE_URL + '/webzine/subscribe', {method:'POST'})→ 성공 시 alert 후 모달 닫기. - 구독취소 모달: 성명/이메일 입력 + 확인/취소. 확인 시
fetch(.../webzine/unsubscribe)→ "구독취소 신청되었습니다" alert.
발송 메일 본문 (PHP로 생성하는 순수 HTML 문자열, 신규 뷰 파일 불필요 — 컨트롤러 내 템플릿 문자열)
발송 전 선택 웹진에 thumbnail_path가 없으면 컴포즈 화면에서 발송 버튼 비활성화 + 안내 문구("웹진관리에서 썸네일을 먼저 등록해주세요").
6. 관리자 화면 (신규 뷰)
- :
webzine_list.php의 tab(.board-tabs)/modal 패턴 재사용. 목록 tab: 검색폼(성명/전화/이메일/그룹 select) GET + 우상단 "전체 N건" / "그룹관리"(→?sub=groups) 버튼. 테이블: No/성명/전화번호/이메일/신청일/구분(뱃지)/상태(뱃지)/수정/삭제. 수정은webzine_list.php의openManageModal패턴처럼 모달에 값 채워subscribers/save로 POST. 그룹관리 tab:webzine_list.php의 카테고리관리 tab 그대로 복제(이름 입력+추가, 목록+삭제). - :
.board-tabs로 발송/발송내역/환경설정 3탭.- 발송 tab: 발송제목/발송일(오늘 date input)/로그인 담당자(readonly, 세션 닉네임) + 웹진선택 select(발행된 웹진만, 옆에 "미리보기" 버튼
window.open(webzine view url)) + 구독자 검색폼(그룹/성명/전화/이메일 + 구독취소자 제외 체크, 기본 체크) + 체크박스 목록(posts.php의checkAll/.post-checkbox패턴 재사용, name=subscriber_ids[]) + 하단 전체선택/전체취소(선택 초기화) 버튼 + 우측 "[웹진발송]" 버튼. - 발송 진행: 버튼 클릭 시 확인창 → JS가
sendExecuteaction=init 호출 → 진행률 모달(모달 오버레이 재사용, 프로그레스 바 + "N / 전체 건 발송중...") 표시하며 action=batch 반복 호출 → 완료 시 action=finish 호출 후 결과 요약(성공/실패 건수) 표시, "발송내역 보기" 링크 제공. - 발송내역 tab: 좌측 카드형 목록(제목/날짜/건수, 클릭 시
?tab=history&id=N), 우측에 선택된 발송의 수신자 상세 테이블(그룹명/성명/전화/이메일/상태). - 환경설정 tab: 보내는사람/보내는이메일/제목형식/웹진하단문구 입력 폼 + "보내는 이메일 화이트리스트 등록 안내" 정적 안내 박스(각 메일 서비스에 발신 주소를 스팸함으로 분류되지 않도록 등록해두라는 안내 문구, 편집 불가 텍스트).
- 발송 tab: 발송제목/발송일(오늘 date input)/로그인 담당자(readonly, 세션 닉네임) + 웹진선택 select(발행된 웹진만, 옆에 "미리보기" 버튼
검증 방법
- PHP 문법 오류 없는지
php -l로 신규/수정 파일 확인. - XAMPP 로컬 서버로 관리자 로그인 후:
- 좌측 메뉴에 웹진관리 하위 3개 메뉴가 정상 노출/이동되는지 확인.
- 구독관리에서 그룹 추가 → 구독자 수동 등록(수집) → 검색/수정/삭제 동작 확인.
- 웹진 하나에 썸네일 지정 → 공개 웹진 페이지 하단 구독 박스 노출 확인(환경설정에 하단문구 저장 후) → 웹진구독 모달로 신규 구독 신청 → 구독관리 목록에 반영되는지 확인 → 구독취소 모달로 취소 → 상태가 "구독취소"로 바뀌는지 확인.
- 웹진발송에서 환경설정 저장 → 구독중 대상자 선택 → 발송 실행 → 진행률 모달 동작 및 발송내역에 결과 기록되는지 확인(로컬은
mail()미설정으로 실제 수신은 안 될 수 있음 — DB 기록/실패 카운트 로직 위주로 검증).
관리자모드 좌측메뉴 웹진관리에 서브메뉴로 웹진관리, 구독관리, 웹진발송으로 구분
- 웹진관리는 기존 웹진관리 연결
구독관리
- 상단 검색 (성명/전화/이메일/그룹)
- 목록 (No / 성명 / 전화번호 / 이메일 / 신청일 / 구분 / 상태 / 수정 / 삭제)
- 구분 (구독신청 / 수집)
- 상태 (구독중 / 구독취소)
- 목록 우측 상단에 전체 건 수 / 그룹관리
웹진발송
- 탭으로 웹진발송 / 발송내역 / 환경설정
- 환경설정 (보내는 사람, 제목형식, 보내는 이메일), 보내는 이메일 화이트리스트 등록 안내 등, 웹진하단 문구
- 웹진하단문구를 입력하면 웹진 하단에 박스로 노출, 웹진구독, 구독취소 버튼 자동생성, 웹진 구독 누르면 새창으로 성명, 전화번호, 이메일 입력, 신청한 정보는 ... 웹진 구독신청용으로만 사용됩니다. 이용동의 체크, 확인 취소, 모달팝업창
- 구독취소는 성명, 이메일 입력으로 구독취소 신청되었습니다 안내 후 구독내역에 반영
- 발송내역 (좌측에 제목 / 날짜 / 건수)
- 발송제목, 발송일, 로그인 담당자,
- 웹진선택 (미리보기)
- 목록 상단에 검색 (그룹 / 성명 / 전화 / 이메일), [v]구독취소자 제외, 우측에 건수
- 목록 ([ ] / No / 그룹명 / 성명 / 전화 / 이메일)
- 목록 하단에 전체선택, 전체취소, 하단 우측에 [웹진발송]
- 웹진발송 누르면 체크된 회원에게 웹진 발송 진행 창, 발송상태 표시...