번들 확장 20개 전부에 AGENTS.md · README.md · docs/ 를 채우고, 코어가 들고 있던 확장 소유 문서 두 갈래를 그 확장으로 옮긴다. 번들 템플릿의 컴포넌트· 핸들러·레이아웃 상세와 확장이 구독하는 활동 로그 훅 목록이 그 대상이며, 코어에는 총계와 링크만 남아 확장이 기능을 늘릴 때 코어 문서를 고쳐야 하던 역방향 의존이 사라진다. 전수 완비를 확인하고 강제를 조인다 — 문서 동반 룰을 대상 목록 없는 error 로 승격하고, 검사 스크립트가 문서 미보유를 실패로 올리며, 미채움 마커 baseline 을 0 으로 기록한다. 한쪽만 조이면 "새 확장이 문서 없이 들어와도 초록" 인 상태가 남는데 그 결과는 이상 0건과 구분되지 않는다. 집필 과정에서 드러난 생성기 결함 셋을 함께 고친다. 스케줄 주기 열이 계약 키를 읽지 않아 모든 확장에서 '-' 였고, 네임스페이스를 붙인 핸들러 등록 키가 수집에서 통째로 빠졌으며, README 골격이 폐기된 히어로 배지를 계속 찍어내고 있었다. 셋 다 산출물이 아니라 원천이 틀린 것이라, 가드의 모집단에 생성기 출력 자체를 넣어 다음 확장이 같은 상태로 태어나는 경로를 막는다.
7.6 KiB
비즈뿌리오 메시지 발송 — 데이터 모델
모델·소유 테이블·마이그레이션·Enum · 진입점: AGENTS.md
모델
| 모델 | 테이블 | fillable | 관계 | 특성 |
|---|---|---|---|---|
BizppurioDispatch |
bizppurio_dispatches |
20 | user→User | - |
BizppurioTemplate |
bizppurio_templates |
15 | - | - |
둘의 역할이 발송 기록과 템플릿 상태로 갈립니다.
BizppurioDispatch(bizppurio_dispatches) — 발송 한 건의 기록입니다. fillable 이 20개로 큰 이유는 발송 시점의 상태를 그대로 남기기 때문입니다: 대상·채널·본문·비즈뿌리오 응답· webhook 으로 나중에 채워지는 결과 코드와 사유. 발송과 결과가 시점이 다르므로 한 행이 두 번에 걸쳐 완성됩니다.BizppurioTemplate(bizppurio_templates) — 알림별 알림톡 템플릿과 그 검수 상태입니다. 카카오 승인 시점의 내용이 여기 박제되며, 발송할 때마다 카카오를 조회하지 않습니다. 문자(SMS) 본문도 같은 행에 함께 있습니다 — 대체 SMS 와 SMS 단독이 같은 본문을 공유하기 때문입니다.
BizppurioTemplate 에 관계가 없는 것은 알림 종류를 문자열 키로 참조하기 때문입니다. 코어와
게시판·이커머스가 각자 알림을 정의하므로 외래키로 묶을 단일 테이블이 없고, 알림이 사라지면 그
템플릿 행은 참조 대상 없이 남습니다.
BizppurioDispatch 의 user→User 관계는 회원 발송에만 채워집니다 — 비회원 발송은
user_id 가 없고 주문 시 입력한 연락처만 남습니다.
소유 테이블
| 테이블 | 모델 |
|---|---|
bizppurio_dispatches |
BizppurioDispatch |
bizppurio_templates |
BizppurioTemplate |
둘 다 bizppurio_ 접두사를 갖습니다.
bizppurio_dispatches 는 계속 쌓이는 로그성 테이블입니다. 발송량에 비례해 늘어나므로 조회는
반드시 페이지네이션을 거쳐야 하고, 목록에 본문 같은 큰 컬럼을 싣지 않습니다.
bizppurio_templates 는 알림 종류 수만큼만 존재하는 설정성 테이블입니다. 알림 하나에 행
하나이므로 크기가 데이터 증가에 비례하지 않습니다.
발송 기록은 코어 알림 로그(notification_logs)와 별개이며,
LinkNotificationLogListener 가 둘을 연결합니다. 두 로그가 따로 있는 이유는 코어 로그가
"알림이 발송 요청되었다" 를, 이쪽이 "그 요청이 비즈뿌리오에서 어떻게 끝났다" 를 담기 때문입니다.
마이그레이션
마이그레이션 2개.
| 파일 | 생성 테이블 | 변경 테이블 | down() |
|---|---|---|---|
2026_07_13_000001_create_bizppurio_dispatches_table.php |
bizppurio_dispatches |
bizppurio_dispatches |
✅ |
2026_07_13_000002_create_bizppurio_templates_table.php |
bizppurio_templates |
bizppurio_templates |
✅ |
2개이며 둘 다 초기 테이블 생성입니다 — 아직 스키마 변경 이력이 없는 젊은 확장입니다.
발송 기록 테이블에 컬럼을 더할 때는 로그성 테이블이라는 점을 염두에 둡니다. 이미 쌓인 행에
기본값을 채우는 백필은 행 수에 비례하므로, chunkById() 로 순회하고 상한 없는 get() 을
쓰지 않습니다(chunk() 계열은 OFFSET 기반이라 갱신된 행이 필터에서 이탈한 만큼 커서가 밀려
미처리 행을 조용히 건너뜁니다).
새 컬럼을 더할 때 초기 create_* 파일을 고치지 않습니다 — 이미 설치된 사이트는 그 파일을
다시 실행하지 않으므로 반영되지 않으며, 기존 행을 손봐야 하는 변경은 upgrades/ 의 업그레이드
스텝 백필이 함께 필요합니다. 한국어 comment 와 down() 은 필수입니다.
Enum
| Enum | backing | case 수 | case |
|---|---|---|---|
BizppurioTemplateStatus |
string |
7 | draft, requested, approved, rejected, stopped, blocked, dormant |
DispatchChannel |
string |
3 | sms, lms, alimtalk |
DispatchSource |
string |
3 | auto, manual, bulk |
DispatchStatus |
string |
4 | pending, sent, success, failed |
ResultCategory |
string |
4 | success, retry, permanent_failure, balance_low |
PHP Enum 은 없지만 닫힌 어휘가 셋 있습니다.
| 어휘 | 값 | SSoT |
|---|---|---|
| 템플릿 상태 | 미작성·작성중·검수중·승인·반려·중지·차단·휴면 | 비즈뿌리오/카카오가 정의 — 이쪽에서 만들 수 없습니다 |
| 결과 코드 분류 | 성공 / 재시도 / 잔액 부족 / 영구 실패 | 이 플러그인이 정의 |
| 결과 코드 자체 | 1000 9070 7436 4400 … |
비즈뿌리오/카카오가 정의, 사유 문구는 lang/{ko,en}/result_codes.php |
앞의 둘은 외부가 정하는 어휘라 Enum 으로 굳히면 상대가 값을 추가할 때마다 이쪽이 깨집니다. 그래서 문자열로 다루고, 모르는 값이 오면 코드만 그대로 표시합니다 — "알 수 없는 코드" 로 뭉뚱그리면 운영자가 비즈뿌리오에 문의할 근거를 잃습니다.
가운데 분류만 이 플러그인이 정하므로 여기가 확장 지점입니다. 새 코드가 어느 분류에 들어가는지 판정이 바뀌면 재시도 여부와 관리자 알림 발화가 함께 바뀝니다.
Repository
| 클래스 | 종류 | 설명 |
|---|---|---|
BizppurioDispatchRepository |
구현 | 비즈뿌리오 발송 이력 Repository 구현체. |
BizppurioDispatchRepositoryInterface |
인터페이스 | 비즈뿌리오 발송 이력 Repository 계약. |
BizppurioTemplateRepository |
구현 | 비즈뿌리오 알림 템플릿 Repository 구현체 (#597). |
BizppurioTemplateRepositoryInterface |
인터페이스 | 비즈뿌리오 알림 템플릿 Repository 인터페이스 (#597). |
발송 기록과 템플릿 각각에 Repository 가 있고, 서비스는 인터페이스만 주입받습니다(구체 클래스 타입힌트 금지).
발송 기록 Repository 에서 특히 걸리는 것 둘:
- 목록 조회는 페이지네이션과 컬럼 프루닝. 로그성 테이블이라 발송량에 비례해 늘어나고, 본문 컬럼이 큽니다. 목록이 실제로 그리는 것만 싣습니다.
- 결과 갱신은 발송 기록을 특정해서. webhook 통보는 비즈뿌리오가 준 식별자로 해당 발송을 찾아 갱신합니다 — 찾지 못한 통보를 조용히 버리지 않고 그 사실이 남아야 합니다.
리스너는 Repository 를 거쳐 데이터에 접근합니다. Model::query() · DB::table() ·
$row->save() 를 직접 부르지 않습니다.