Files
HeuJung 9d3b15e300 feat(core,extensions): 확장 진입 문서 제목에 그누보드7 확장 유형 표기
번들 확장 README 제목이 manifest 확장명 그대로(「게시판」)라 그 문서만 연
사람이 그누보드7의 확장인지, 모듈인지 템플릿인지 알 수 없었다.
README · AGENTS.md · docs/README.md 진입 문서 60개의 제목을
「그누보드7 {확장명} {유형}」 으로 바꾸고, 조립 규칙을 ExtensionInventory::docTitle
한 곳에 두어 골격 생성기와 계약 테스트가 같은 헬퍼를 쓰게 했다.
확장명이 이미 유형으로 끝나면 겹쳐 붙이지 않는다.

공개 규정에는 번들 확장 전용 관례임을 주어로 명시하고 제3자 확장에는
요구하지 않는다는 문장을 두었다. 언어팩은 README 가 없어 대상 밖이다.
2026-09-04 16:40:42 +09:00

17 KiB

그누보드7 비즈뿌리오 메시지 발송 플러그인 — 에이전트 가이드

이 문서는 이 플러그인을 수정하는 에이전트·확장개발자를 위한 것입니다. 도입 검토·운영 관점은 README.md 를 보세요.

TL;DR (5초 요약)

1. 유형: 플러그인 (sirsoft-message_bizppurio) — 코어 알림에 문자(SMS/LMS)·카카오 알림톡 채널을 추가한다. 알림 자체는 정의하지 않고 발송 수단만 담당
2. 확장 방식: 발행 훅 1개 — 잔액 부족·한도 초과 시 `balance.low`(쿨다운 내 1회). 그 외 배선은 구독 6종(코어 알림 로그·설정, 이커머스 비회원 연락처)
3. 건드리면 안 되는 것: webhook 라우트의 IP 화이트리스트 제거, 승인 템플릿 직접 수정(승인 취소 선행), 잔액부족 통지를 같은 채널로만 보내기, 크리덴셜 프론트 노출
4. 작업 위치: `plugins/_bundled/sirsoft-message_bizppurio` — 활성 디렉토리 직접 수정 금지
5. 반영: `php artisan plugin:update sirsoft-message_bizppurio --force`

1. 이 확장은 무엇인가

코어 알림 시스템에 문자(SMS/LMS)와 카카오 알림톡 채널을 추가하는 플러그인입니다. 알림을 새로 만들지 않고, 코어와 모듈이 이미 발화하는 알림을 비즈뿌리오 API 로 내보냅니다.

설계 원칙 넷:

  1. 채널만 추가하고 알림은 만들지 않는다. 어떤 사건에 알림을 보낼지는 코어와 각 모듈이 정합니다. 이 플러그인은 RegisterNotificationChannelsListener 로 채널을 등록하고 SeedChannelTemplatesListener 로 그 채널의 템플릿 자리를 시드할 뿐입니다. 그래서 삭제하면 채널만 사라지고 알림 자체는 남습니다.
  2. 발송 결과는 나중에 돌아온다. 비즈뿌리오는 발송 API 응답이 아니라 webhook 통보로 최종 결과를 줍니다. 그래서 발송과 결과 기록이 분리되어 있고, webhook 이 등록되지 않은 사이트에서는 발송은 되지만 이력에 결과가 남지 않습니다 — 오류가 아니라 결과 미상입니다.
  3. 승인된 내용을 박제한다. 알림톡 템플릿은 카카오 승인 시점의 내용을 로컬에 저장해 두고 발송하며, 발송할 때마다 카카오를 조회하지 않습니다. 그래서 승인 후 내용을 고치려면 승인을 먼저 취소해야 하고, 취소하는 순간 알림톡 발송이 멈춥니다.
  4. 실패를 종류별로 다르게 다룬다. 결과 코드를 성공 / 재시도(일시 오류) / 잔액 부족 / 영구 실패 넷으로 분류합니다. 잔액 부족은 재시도해도 소용없으므로 즉시 실패 처리하고 관리자에게 알립니다 — 다만 그 알림이 같은 채널로 나가면 함께 실패하므로 쿨다운을 두고 다른 채널 병행을 권합니다.

의도적으로 하지 않는 것: 알림 정의·수신자 해석·발송 대상 판정. 그 셋은 코어 GenericNotification 과 각 도메인의 일이며, 이 플러그인은 채널 구현체입니다.

2. 디렉토리 지도

경로 역할 수정 시 필요한 절차
plugin.json manifest (버전 SSoT) version 변경 시 package.json·package-lock.json·composer.json 동기화
plugin.php 진입 클래스 (선언형 표면 SSoT) 표면 변경 시 ext:docgen 재실행 + 코어 최소 버전 검토
src/Controllers/ 컨트롤러 API 표면 변경 시 api:docgen 재실행
src/Http/Requests/ FormRequest (검증 SSoT) 검증 규칙은 Service 가 아니라 여기에 둔다
src/Services/ 비즈니스 로직 Repository 인터페이스 주입 (구체 클래스 금지)
src/Repositories/ 데이터 접근 목록 쿼리는 컬럼 프루닝·정렬 화이트리스트 확인
src/Models/ Eloquent 모델 스키마 변경 시 마이그레이션 + 업그레이드 스텝 동반
src/Listeners/ 훅 리스너 Repository 경유 (Model·DB 파사드 직접 접근 금지)
src/Enums/ 상태·타입·분류 문자열 리터럴 대신 Enum 을 SSoT 로 둔다
src/routes/ 라우트 모든 라우트에 name() 필수
database/migrations/ 마이그레이션 한국어 comment + down() 필수, 기설치본은 업그레이드 스텝으로 백필
resources/layouts/ 레이아웃 JSON php artisan plugin:update sirsoft-message_bizppurio --force (빌드 불필요)
resources/js/ 프론트 엔트리·핸들러 php artisan plugin:build → php artisan plugin:update sirsoft-message_bizppurio --force
resources/extensions/ 다른 확장 레이아웃에 주입하는 조각 php artisan plugin:update sirsoft-message_bizppurio --force
dist/ 커밋되는 빌드 산출물 --production 으로 재빌드 (sourceMappingURL 잔존 금지)
config/ 확장 config 설정 기본값은 settings 스키마와 어긋나지 않게
tests/ 테스트 변경 범위만 필터 실행
CHANGELOG.md 변경 이력 버전 상향 시 항목 추가 (미기재 시 버전 상향 불가)
components.json 편집기 컴포넌트 선언 (레이아웃 저작자가 읽는 props 계약) php artisan plugin:update sirsoft-message_bizppurio --force
docs/ 개발자 문서 표면 변경 시 php artisan ext:docgen 재실행
lang/ 다국어 키 추가 시 ko·en 동시 반영 + 번들 ja 팩 동기화

3. 핵심 흐름

발송: 코어·모듈이 알림 발화 → 코어 알림 시스템이 켜져 있는 채널별로 발송 작업을 큐잉 → 이 플러그인의 채널 구현이 그 작업을 받음 → 알림톡이면 승인된 템플릿 내용으로, 문자면 본문 길이에 따라 SMS/LMS 를 골라 비즈뿌리오 발송 API 호출 → 발송 기록(bizppurio_dispatches) 적재. 알림톡 발송이 불가하거나 실패하면 설정에 따라 대체 SMS 로 내려갑니다.

결과 수신: 비즈뿌리오가 POST /webhook 으로 결과 통보 → BizppurioWebhookIpWhitelist 미들웨어가 발신 IP 를 검사 → WebhookReportService 가 결과 코드를 넷으로 분류해 발송 기록을 갱신. 잔액 부족(9070/9071/7436)이면 sirsoft-message_bizppurio.balance.low 액션을 발행하고 관리자 알림을 보냅니다 — 대량 실패 시 반복을 막기 위해 채널별 쿨다운(기본 3600초) 안에서는 최초 1회만 실행됩니다.

템플릿 수명주기: 관리자 화면에서 작성(POST templates) → 검수 신청 (POST templates/{id}/request) → 카카오 검수 → 승인/반려. 결과는 bizppurio:sync-template-status 스케줄이 30분마다 확인하고, 화면의 [새로고침] (POST templates/{id}/sync)으로 즉시 확인할 수도 있습니다. 승인된 템플릿을 고치려면 POST templates/{id}/cancel-approval 로 승인을 먼저 취소합니다.

알림 이력 연결: 코어가 알림 발송 성공·실패를 기록하면 (core.notification_log.after_log_sent / after_log_failed) LinkNotificationLogListener 가 그 로그와 이 플러그인의 발송 기록을 연결합니다. 관리자 "알림 발송 이력" 화면에서 문자· 알림톡 결과가 함께 보이는 것이 이 연결 덕분입니다.

4. 확장점

확장점 수 상세
발행 훅 1개 발행 훅
구독 훅 6개 구독 훅
훅 리스너 7개 훅 리스너
레이아웃 확장 7개 레이아웃 확장
미들웨어 1개 미들웨어
브로드캐스트 채널 0개 브로드캐스트 채널
스케줄 1개 스케줄
알림 정의 1개 알림 정의

발행 훅은 하나뿐입니다.

sirsoft-message_bizppurio.balance.low — 잔액 부족·후불 한도 초과로 발송이 실패했을 때 (쿨다운 내 최초 1회) 발화합니다. 인수는 string $resultCode, string $channel 이며, $resultCode 는 9070(문자 잔액 부족) · 7436(알림톡 지갑 잔액 부족) · 9071(후불 한도 초과) 중 하나입니다.

use App\Extension\HookManager;

HookManager::addAction(
    'sirsoft-message_bizppurio.balance.low',
    function (string $resultCode, string $channel) {
        // 예: 잔액 부족 시 Slack 으로도 별도 알림
        SlackNotifier::send("비즈뿌리오 잔액 부족: 채널={$channel}, 코드={$resultCode}");
    },
    priority: 10
);

이 훅이 관리자 자체 알림(잔액부족·후불한도초과 안내)을 발화하는 지점과 동일합니다. 잔액 부족 알림을 문자·알림톡이 아닌 다른 경로로 받고 싶을 때 여기에 붙입니다 — 같은 채널로 보내면 잔액이 없으므로 그 알림도 함께 실패합니다.

구독 6종이 이 플러그인의 배선 전부입니다. 코어 알림 로그 2종(발송 기록 연결) · 코어 설정 2종(저장 시 토큰 무효화 · 운영 모드 전환 시 필수값 검증) · 이커머스 1종(비회원 연락처 주입) · 자기 훅 1종(잔액부족 알림 데이터). sirsoft-ecommerce.notification.extract_data 는 manifest 의존이 아니라 훅 구독이므로, 이커머스가 없으면 비회원 문자 발송만 비고 나머지는 정상 동작합니다.

레이아웃 조각 7개가 UI 대부분입니다 — 알림 설정 화면의 비즈뿌리오 탭(코어·게시판·이커머스 각각), 알림 목록 행 하단 요약, 알림 템플릿 편집 창의 알림톡·문자 섹션과 하단 버튼, 발송 이력 화면의 결과 열. 대상 화면이 그 자리를 없애면 조각은 오류 없이 사라집니다.

5. 수정 시 동반 의무

  • _bundled 에서만 수정하고 php artisan plugin:update sirsoft-message_bizppurio --force 로 반영
  • manifest version 상향 시 package.json · package-lock.json · composer.json 동기화 + CHANGELOG 기재
  • 스키마 변경 시 마이그레이션(한국어 comment + down()) + 기설치본 백필용 업그레이드 스텝
  • 발행 훅 추가·이름 변경 시 php artisan ext:docgen 재실행 (구독하는 확장의 계약이 바뀝니다)
  • API 표면 변경 시 php artisan api:docgen --scope=plugin:sirsoft-message_bizppurio 재실행 + docs/api/** 갱신
  • 레이아웃 JSON 변경 시 빌드 없이 update 만 — 신규 Tailwind 클래스는 빌드된 CSS 에 존재하는지 확인
  • TSX/TS 변경 시 --production 재빌드 후 dist/ 커밋 (sourceMappingURL 잔존 금지)
  • 다국어 키 추가 시 ko·en 동시 반영 + 번들 ja 언어팩 증분 동기화
  • webhook 라우트를 추가·변경했다면 getMiddleware() 의 targets 에 그 라우트 이름을 함께 추가 (누락 시 인증 없는 공개 경로가 된다)
  • 결과 코드 분류(성공/재시도/잔액부족/영구실패)를 바꿨다면 lang/{ko,en}/result_codes.php 의 사유 문구도 함께 갱신
  • 코어 알림 시스템(채널 등록·발송 로그 훅)이 바뀌면 이 플러그인의 구독 4종이 조용히 끊기므로 함께 확인
  • 레이아웃 조각 7개는 대상 화면(알림 설정·알림 템플릿 편집·발송 이력)의 자리가 사라지면 오류 없이 빠진다 — 코어·게시판·이커머스 업그레이드 후 노출 확인
  • 크리덴셜 설정을 추가한다면 frontend_schema 에 expose: false + sensitive: true 를 함께 선언
  • dist/ 는 커밋되는 배포 산출물 — TS 를 고쳤으면 --production 재빌드 후 커밋 (sourceMappingURL 잔존 금지)
  • 레이아웃·컴포넌트·data_source 를 건드렸다면 docs/editor-spec.md 를 확인 — 이 확장은 편집기 스펙이 없어 bizppurioCategories · bizppurioProfiles · bizppurio_templates_list · report_url · templates_readiness 가 편집기 캔버스에서 빈 화면으로 보인다. data_source 를 더 늘리면 그 자리도 같은 상태가 된다

6. 금지 패턴

금지 올바른 사용 이유
이 플러그인에서 알림 자체(수신자·발화 조건)를 정의 채널 등록까지만 — 알림은 코어·모듈이 정의 채널 구현이 알림을 소유하면 플러그인을 바꿀 때 알림 이력까지 사라진다
잔액 부족 알림을 문자·알림톡 채널로만 보내도록 두기 사이트 내 알림·메일 등 다른 채널 병행 잔액이 없어서 실패한 상황인데 통지도 같은 수단이면 함께 실패한다
잔액 부족 알림에 쿨다운 없이 매 실패마다 발송 채널별 쿨다운(기본 3600초) 안에서 최초 1회 대량 발송이 한꺼번에 실패하면 통지가 수백 건 쏟아진다
발송할 때마다 카카오에 템플릿을 조회 승인 시점 내용을 로컬에 박제해 발송 외부 조회가 발송 경로에 들어가면 그 서비스 지연이 곧 발송 지연이 된다
승인된 템플릿을 그대로 수정 승인 취소 → 수정 → 재신청 카카오 승인 대상은 특정 내용이다. 승인 후 내용이 바뀌면 승인과 발송물이 어긋난다
webhook 라우트를 IP 화이트리스트 없이 공개 BizppurioWebhookIpWhitelist 부착 유지 인증 없는 공개 엔드포인트다 — 위조 통보로 발송 결과를 조작할 수 있다
webhook 라우트를 추가·변경하면서 미들웨어 targets 선언을 그대로 두기 라우트 이름을 getMiddleware() 선언에도 추가 이름이 어긋나면 미들웨어가 붙지 않는데, 정상 응답이 나가므로 오류도 로그도 남지 않는다
크리덴셜(비밀번호·API 키·발신프로필 키)을 프론트에 노출 expose: false + sensitive: true 유지 발송 권한이 곧 비용이다
일시 오류와 영구 실패를 같은 방식으로 재시도 결과 코드 4분류를 따른다 영구 실패를 재시도하면 비용만 늘고, 일시 오류를 포기하면 발송이 누락된다
문자 본문이 없는 언어에 기본 언어 본문도 없이 발송 시도 두 단계 폴백 후 그 알림의 문자 발송을 건너뛴다 빈 본문 발송은 비용이 나가면서 수신자에게 아무 정보도 주지 않는다

7. 테스트 실행

종류 개수 위치
PHPUnit 37개 plugins/_bundled/sirsoft-message_bizppurio/tests
Vitest 7개 vitest.config.ts
Playwright 2개 tests/Playwright
시나리오 매니페스트 6개 tests/scenarios

기저 TestCase: tests/PluginTestCase.php — 확장 테스트는 이 클래스를 상속합니다 (Tests\TestCase 직접 상속 금지).

# PHPUnit (변경 범위만) (Bash)
php vendor/bin/phpunit plugins/_bundled/sirsoft-message_bizppurio/tests --filter='<대상클래스>'

# Vitest (확장 디렉토리에서) (PowerShell)
cd plugins/_bundled/sirsoft-message_bizppurio && powershell -Command "npm run test:run -- <대상>"

# Playwright E2E (확장 디렉토리에서) (Bash)
cd plugins/_bundled/sirsoft-message_bizppurio && npm run test:e2e -- specs/<대상>.spec.ts

무필터 전체 실행은 금지되어 있습니다 — 변경 범위에 걸리는 대상만 지정해 실행합니다.

8. 문서 목차

문서 내용 상태
docs/README.md 문서 통합 목차와 실측 집계 ✅
docs/architecture.md 설계 의도·계층 지도·디렉토리 맵 ✅
docs/extension-points.md 발행/구독 훅·미들웨어·채널·스케줄 ✅
docs/data-model.md 모델·소유 테이블·마이그레이션·Enum ✅
docs/settings.md 설정 스키마·권한·메뉴·라우트·의존 관계 ✅
docs/frontend.md 레이아웃·액션 핸들러·전역 진입점·에셋 ✅
docs/editor-spec.md 레이아웃 편집기에 선언한 팔레트·컨트롤·샘플 데이터 ✅
docs/api/ API 레퍼런스 (엔드포인트별 파라미터·응답 필드) ✅
CHANGELOG.md 변경 이력 ✅