Sendgo Notification
인증된 제출자techigh/sendgo-notification
Laravel용 알림 패키지
SendGo Notification — Laravel 카카오톡·SMS 연동 패키지
Laravel에서 카카오 알림톡, 친구톡, SMS / LMS / MMS를 전송하는 공식 Notification 패키지입니다. SendGo.io API v1 / v2를 모두 지원합니다.
목차
요구사항
- PHP 8.2+
- Laravel 8.x | 9.x | 10.x | 11.x
- SendGo 계정 (sendgo.io)
설치
composer require techigh/sendgo-notification설정 파일을 프로젝트에 복사하려면 아래 명령어를 실행하세요 (선택):
php artisan vendor:publish --tag=sendgo환경 설정
.env 파일에 아래 값을 추가합니다:
SENDGO_URL=https://api.sendgo.io
SENDGO_ACCESS_KEY=your_access_key
SENDGO_SECRET_KEY=your_secret_key
SENDGO_SENDER_KEY=your_sms_sender_key
SENDGO_KAKAO_SENDER_KEY=your_kakao_sender_key
# API 버전 (기본값: v1 / v2 사용 시 명시)
SENDGO_API_VERSION=v1NOTE
API 키는 SendGo.io 콘솔 → 연동 정보 메뉴에서 확인할 수 있습니다.
사용 방법
이 패키지는 Laravel의 표준 Notification 시스템과 통합됩니다. 채널 클래스(AlimTalkChannel, FriendTalkChannel, SmsChannel)를 via() 메서드에 등록하고, 각 채널에 맞는 메시지 객체를 반환하는 메서드(toAlim(), toFriend(), toSms())를 구현하면 됩니다.
수신자 전달 방식
수신자는 to() 또는 toMany() 메서드로 지정합니다.
단일 수신자 — to(array $to):
->to([
'contact' => '01066443892',
'name' => '홍길동',
'var1' => '주문번호 001',
])다중 수신자 — toMany(array $to):
->toMany([
[
'contact' => '01066443892',
'name' => '홍길동',
'var1' => '주문번호 001',
],
[
'contact' => '01012345678',
'name' => '김철수',
'var1' => '주문번호 002',
],
])각 수신자 항목에서 contact는 필수이며, name과 var1~var8는 선택 항목입니다.
NOTE
toMany()는 Laravel Notification 채널에서는 사용할 수 없습니다. Sms, AlimTalk, FriendTalk 서비스를 직접 호출할 때만 지원됩니다. Notification 채널에서 toMany()를 사용하면 MULTIPLE_RECIPIENTS_NOT_SUPPORTED_IN_NOTIFICATION 예외가 발생합니다.
서비스를 직접 호출하는 경우의 다중 발송 예시:
use Techigh\SendgoNotification\Attributes\Sms\Sms;
use Techigh\SendgoNotification\Attributes\Sms\SmsMessage;
app(Sms::class)->send(
SmsMessage::make()
->messageType('SMS')
->content('[공지] 시스템 점검 안내')
->toMany([
['contact' => '01066443892', 'name' => '홍길동'],
['contact' => '01012345678', 'name' => '김철수'],
])
->toArray()
);알림톡 (AlimTalk)
사전에 카카오에서 승인된 템플릿으로 공식 비즈니스 메시지를 전송합니다. 주문 확인, 배송 안내, 예약 알림 등 정형화된 메시지에 적합합니다.
use Techigh\SendgoNotification\Attributes\Alim\AlimTalkChannel;
use Techigh\SendgoNotification\Attributes\Alim\AlimTalkMessage;
class OrderConfirmedNotification extends Notification
{
use Queueable;
public function __construct(private readonly string $orderNumber) {}
public function via(object $notifiable): array
{
return [AlimTalkChannel::class];
}
public function toAlim(object $notifiable): AlimTalkMessage
{
return AlimTalkMessage::make()
->templateCode('ORDER_CONFIRM_001')
->replaceSms('Y')
->smsTitle('[주문 확인]')
->smsContent("주문 {$this->orderNumber}이 확인되었습니다.")
->to([
'contact' => $notifiable->phone,
'name' => $notifiable->name,
'var1' => $this->orderNumber,
])
->at();
}
}$user->notify(new OrderConfirmedNotification('ORD-20260101-001'));AlimTalk 메서드
| 메서드 | 필수 | 설명 |
|---|---|---|
templateCode(string) | ✅ | SendGo에서 승인받은 템플릿 코드 |
scheduleType(string) | 'DIRECTLY'(기본) 또는 'SCHEDULED' | |
replaceSms(string) | 알림톡 실패 시 SMS 대체 ('N' 기본) | |
smsTitle(string) | 조건부 | 대체 SMS 제목 (replaceSms('Y') 시 필수) |
smsContent(string) | 조건부 | 대체 SMS 내용 (replaceSms('Y') 시 필수) |
to(array) | ✅ | 단일 수신자 정보 (contact 필수, name / var1~var8 선택) |
toMany(array) | 배치 전용 | 다중 수신자 정보. Notification 채널에서는 사용 불가 |
at(string|null) | 조건부 | 예약 시각 (SCHEDULED일 때 필수, 형식: Y-m-d H:i:s) |
더 많은 예제 → examples/AlimTalk.md
친구톡 (FriendTalk)
카카오톡 채널 친구에게 이미지, 버튼 등을 포함한 자유로운 형태의 메시지를 전송합니다. 이벤트 안내, 신상품 홍보 등 마케팅 목적에 적합합니다.
use Techigh\SendgoNotification\Attributes\Friend\FriendTalkChannel;
use Techigh\SendgoNotification\Attributes\Friend\FriendTalkMessage;
class NewProductNotification extends Notification
{
use Queueable;
public function __construct(private readonly object $product) {}
public function via(object $notifiable): array
{
return [FriendTalkChannel::class];
}
public function toFriend(object $notifiable): FriendTalkMessage
{
return FriendTalkMessage::make()
->messageType('FI')
->content("신상품 출시!\n\n{$this->product->name}\n{$this->product->price}원")
->imageUrl($this->product->image_url)
->imageLink($this->product->detail_url)
->buttons([
[
'type' => 'WL',
'name' => '자세히 보기',
'linkMo' => $this->product->detail_url,
'linkPc' => $this->product->detail_url,
],
])
->to([
'contact' => $notifiable->phone,
'name' => $notifiable->name,
])
->at();
}
}메시지 타입
| 타입 | 설명 |
|---|---|
FT | 텍스트형 |
FI | 이미지형 |
FW | 와이드 이미지형 (wide('Y') 필수) |
FL | 와이드 아이템 리스트형 |
FM | 커머스형 |
FC | 캐러셀 피드형 |
FA | 캐러셀 커머스형 |
FP | 프리미엄 동영상형 |
FriendTalk 메서드
| 메서드 | 필수 | 설명 |
|---|---|---|
messageType(string) | ✅ | 메시지 타입 (위 표 참고) |
content(string) | ✅ | 메시지 내용 |
scheduleType(string) | 'DIRECTLY'(기본) 또는 'SCHEDULED' | |
imageUrl(string) | 이미지 URL | |
imageLink(string) | 이미지 클릭 시 이동할 링크 | |
buttons(array) | 버튼 배열 | |
wide(string) | 조건부 | 와이드 이미지 여부 (FW일 때 'Y' 필수) |
adFlag(string) | 광고성 메시지 표기 ('N' 기본) | |
replaceSms(string) | 친구톡 실패 시 SMS 대체 | |
smsTitle(string) | 조건부 | 대체 SMS 제목 (replaceSms('Y') 시 필수) |
smsContent(string) | 조건부 | 대체 SMS 내용 (replaceSms('Y') 시 필수) |
to(array) | ✅ | 단일 수신자 정보 (contact 필수, name 선택) |
toMany(array) | 배치 전용 | 다중 수신자 정보. Notification 채널에서는 사용 불가 |
at(string|null) | 조건부 | 예약 시각 (SCHEDULED일 때 필수, 형식: Y-m-d H:i:s) |
더 많은 예제 → examples/FriendTalk.md
SMS / LMS / MMS
use Techigh\SendgoNotification\Attributes\Sms\SmsChannel;
use Techigh\SendgoNotification\Attributes\Sms\SmsMessage;
class VerificationNotification extends Notification
{
use Queueable;
public function __construct(private readonly string $code) {}
public function via(object $notifiable): array
{
return [SmsChannel::class];
}
public function toSms(object $notifiable): SmsMessage
{
return SmsMessage::make()
->messageType('SMS')
->content("[인증번호] {$this->code} (3분 내 입력)")
->to([
'contact' => $notifiable->phone,
'name' => $notifiable->name,
])
->at();
}
}LMS (장문 메시지, subject 필수):
return SmsMessage::make()
->messageType('LMS')
->subject('[점검 안내]')
->content('2026-04-01 02:00~06:00 서비스 점검이 예정되어 있습니다.')
->to(['contact' => $notifiable->phone])
->at();MMS (subject + files 필수, 최대 3개):
return SmsMessage::make()
->messageType('MMS')
->subject('[이벤트]')
->content('첨부 이미지를 확인해주세요.')
->files([
storage_path('app/public/event_01.jpg'),
storage_path('app/public/event_02.jpg'),
])
->to(['contact' => $notifiable->phone])
->at();SMS 메서드
| 메서드 | 필수 | 설명 |
|---|---|---|
messageType(string) | ✅ | 'SMS' / 'LMS' / 'MMS' |
content(string) | ✅ | 메시지 내용 |
campaignType(string) | 'MESSAGE' (기본값) | |
scheduleType(string) | 'DIRECTLY'(기본) 또는 'SCHEDULED' | |
subject(string) | 조건부 | 제목 (LMS / MMS 필수) |
files(array) | 조건부 | 파일 경로 배열 (MMS 필수, 최대 3개) |
to(array) | ✅ | 단일 수신자 정보 (contact 필수, var1~var8 선택) |
toMany(array) | 배치 전용 | 다중 수신자 정보. Notification 채널에서는 사용 불가 |
at(string|null) | 조건부 | 예약 시각 (SCHEDULED일 때 필수, 형식: Y-m-d H:i:s) |
더 많은 예제 → examples/SMS.md
예제 문서
변경 이력
전체 변경 이력은 CHANGELOG.md를 참고하세요.
예외 처리
발송에 실패하면 SendGoException이 던져집니다. context() 메서드로 API 응답의 상세 정보를 확인할 수 있습니다.
use Techigh\SendgoNotification\Exceptions\SendGoException;
try {
$user->notify(new OrderConfirmedNotification($order->number));
} catch (SendGoException $e) {
$ctx = $e->context();
// $ctx['error_code'] — 에러 코드 (예: 'INVALID_TEMPLATE_CODE')
// $ctx['status'] — HTTP 상태 코드
// $ctx['endpoint'] — 호출 엔드포인트
// $ctx['api_version'] — 사용 중인 API 버전
// $ctx['body'] — 원본 응답 body
logger()->error('SendGo 발송 실패', [
'error_code' => $ctx['error_code'] ?? null,
'status' => $ctx['status'] ?? null,
'message' => $e->getMessage(),
]);
}큐 비동기 발송
ShouldQueue 인터페이스를 구현하면 Laravel 큐를 통해 비동기로 처리됩니다. 대량 발송이나 응답 속도가 중요한 상황에서 권장합니다.
use Illuminate\Contracts\Queue\ShouldQueue;
use Techigh\SendgoNotification\Exceptions\SendGoException;
class OrderConfirmedNotification extends Notification implements ShouldQueue
{
use Queueable;
public string $queue = 'notifications';
public int $tries = 3;
public int $backoff = 10;
public function failed(SendGoException $e): void
{
logger()->critical('알림 최종 실패', ['error' => $e->getMessage()]);
}
}큐 워커를 실행하려면:
php artisan queue:work --queue=notifications다중 워커 환경에서는 토큰 캐시를 공유할 수 있도록 redis 또는 database 드라이버를 사용하세요:
CACHE_DRIVER=redis
QUEUE_CONNECTION=redisNOTE
여러 큐 워커가 동시에 실행되는 환경에서 file 캐시 드라이버를 사용하면 인증 토큰이 워커 간에 공유되지 않아 불필요한 토큰 재발급이 발생할 수 있습니다. redis를 권장합니다.
v1 → v2 마이그레이션
기존 Notification 코드를 수정할 필요 없이 .env 파일에서 API 버전만 변경하면 됩니다.
SENDGO_API_VERSION=v2v2 주요 변경 사항:
| 항목 | v1 | v2 |
|---|---|---|
| 토큰 형식 | base64 인코딩 | 그대로 사용 (sgv2. prefix) |
| 토큰 재발급 | 401 / 403 응답 시 무조건 재발급 | 에러 코드별 분기 처리 (설정 오류는 재발급하지 않음) |
| 에러 응답 | 별도 구조 없음 | code / message / traceId 포함 |
v2 에러 코드 전체 목록 → API_V2_ERROR_CODES.md
에러 코드
SendGoException::context()['error_code']로 확인합니다.
인증 오류 (401)
| 코드 | 설명 |
|---|---|
INVALID_AUTH_HEADER | Authorization 헤더 없음 |
INVALID_BASIC_AUTH | Basic 인증 형식 오류 |
INVALID_BASIC_AUTH_PAYLOAD | Basic 인증 페이로드 오류 |
INVALID_ACCESS_KEY | 유효하지 않은 Access Key |
INVALID_SECRET_KEY | 유효하지 않은 Secret Key |
INVALID_BEARER_TOKEN | Bearer 토큰 없음 |
INVALID_BEARER_TOKEN_PREFIX | Bearer 토큰 prefix 오류 (v2) |
INVALID_BEARER_TOKEN_PAYLOAD | Bearer 토큰 페이로드 오류 (v2) |
INVALID_BEARER_SIGNATURE | Bearer 토큰 서명 불일치 (v2) |
INVALID_BEARER_APPLICATION | 토큰에 해당하는 앱 없음 (v2) |
MALFORMED_BEARER_TOKEN | 잘못된 형식의 Bearer 토큰 (v2) |
UNSUPPORTED_BEARER_TOKEN_VERSION | 지원하지 않는 토큰 버전 (v2) |
TOKEN_MISMATCH | 발급된 토큰과 불일치 (v2) |
TOKEN_EXPIRED | 만료된 토큰 (패키지가 자동으로 재발급 처리) |
TOKEN_RECORD_NOT_FOUND | 토큰 레코드 없음 (v2) |
권한 오류 (403)
| 코드 | 설명 |
|---|---|
ACCESS_KEY_NOT_APPROVED | 미승인 Access Key |
IP_NOT_ALLOWED | 허용되지 않은 IP |
TEAM_REQUIRED_FOR_KAKAO | 카카오 API는 팀 소속 앱만 사용 가능 |
SENDER_APPLICATION_MISMATCH | SMS 발신키가 앱과 불일치 |
KAKAO_SENDER_APPLICATION_MISMATCH | 카카오 발신키가 앱과 불일치 |
리소스 오류 (404)
| 코드 | 설명 |
|---|---|
INVALID_SENDER_KEY | 유효하지 않은 SMS 발신키 |
INVALID_KAKAO_SENDER_KEY | 유효하지 않은 카카오 발신키 |
INVALID_TEMPLATE_CODE | 유효하지 않은 템플릿 코드 |
OWNER_NOT_FOUND | 캠페인 소유자 확인 불가 |
SENDER_OWNER_NOT_FOUND | SMS 발신키 소유자 확인 불가 |
KAKAO_SENDER_OWNER_NOT_FOUND | 카카오 발신키 소유자 확인 불가 |
요청 오류 (422)
| 코드 | 설명 |
|---|---|
EMPTY_CONTACTS | 수신자 없음 |
PAYMENT_REQUIRED | 크레딧 부족 |
MESSAGE_PROCESSING_FAILED | SMS / MMS 처리 실패 |
NOTICE_PROCESSING_FAILED | 알림톡 처리 실패 |
FRIEND_PROCESSING_FAILED | 친구톡 처리 실패 |
패키지 내부 오류
| 코드 | 설명 |
|---|---|
INVALID_API_VERSION | 지원하지 않는 API 버전 (v1 또는 v2만 허용) |
MULTIPLE_RECIPIENTS_NOT_SUPPORTED_IN_NOTIFICATION | Laravel Notification 채널에서 toMany() 사용 불가 |
v2 에러 코드 상세 → API_V2_ERROR_CODES.md
트러블슈팅
메시지가 전송되지 않을 때
.env의 API 키 값이 올바른지 확인합니다.- SendGo 계정의 잔액(크레딧)을 확인합니다.
- 전화번호 형식을 확인합니다. (
01012345678형태, 하이픈 제외) storage/logs/laravel.log에서 에러 메시지를 확인합니다.
알림톡이 SMS로 대체 발송될 때
- SendGo 콘솔에서 템플릿 승인 상태를 확인합니다.
- 템플릿 변수(
var1~var8) 매핑이 올바른지 확인합니다. - 카카오 채널 연동 상태를 확인합니다.
다중 워커 환경에서 토큰 충돌이 발생할 때
CACHE_DRIVER=redis로 변경하여 공유 캐시를 사용합니다.
INVALID_API_VERSION 에러가 발생할 때
SENDGO_API_VERSION값이v1또는v2로 설정되어 있는지 확인합니다.
라이선스
MIT — LICENSE 참조