본문 바로가기
← 패키지 목록

Sendgo Notification

인증된 제출자

techigh/sendgo-notification

제출자 이우진
최신 v1.1.0라이센스 MIT배포 2026년 10월 3일
큐·작업SaaS 킷

Laravel용 알림(Notification) 패키지입니다.

SendGo Notification — Laravel 카카오톡·SMS 연동 패키지

License: MIT Version Laravel PHP

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로 패키지를 추가합니다.

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=v1

위 키 값들은 SendGo.io 콘솔 → 연동 정보 메뉴에서 확인할 수 있습니다.


사용 방법

수신자 전달 방식

to(array $to)는 단일 수신자 전용 메서드입니다.

->to([ 'contact' => '01066443892', 'name' => 'John Doe', 'var1' => 'content1', ])

여러 수신자에게 한 번에 보내야 한다면 반드시 toMany(array $to)를 사용해야 합니다.

->toMany([ [ 'contact' => '01066443892', 'name' => 'John Doe', 'var1' => 'content1', ], [ 'contact' => '01012345678', 'name' => 'Jane Doe', 'var1' => 'content2', ], ])

각 수신자 항목은 contact를 필수로 가지며, 필요에 따라 name과 var1~var8 변수를 추가할 수 있습니다.

NOTE

toMany()는 Laravel Notification 채널에서는 사용할 수 없습니다. Sms, AlimTalk, FriendTalk 서비스를 직접 호출하는 경우에만 지원되는 기능이라는 점에 유의하세요.

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' => 'John Doe'], ['contact' => '01012345678', 'name' => 'Jane Doe'], ]) ->toArray() );

Notification 채널 안에서 toMany()를 호출하면 MULTIPLE_RECIPIENTS_NOT_SUPPORTED_IN_NOTIFICATION 예외가 발생하므로, 다건 발송이 필요하다면 서비스 클래스를 직접 사용하세요.


알림톡 (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 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 제목
smsContent(string)조건부대체 SMS 내용
to(array)✅단일 수신자 정보
toMany(array)배치 전용다중 수신자 정보. Notification 채널에서는 사용 불가
at(string|null)조건부예약 시각

더 많은 예제는 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)조건부예약 시각

더 많은 예제는 examples/SMS.md 문서를 참고하세요.


예제 문서


변경 이력

전체 변경 사항은 CHANGELOG.md에서 확인할 수 있습니다.


예외 처리

발송 과정에서 발생하는 모든 오류는 SendGoException으로 던져집니다.

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(), ]); }

context()로 얻은 정보를 로그에 남겨두면 나중에 특정 에러 코드별로 알림 재발송 로직을 분기하거나, 장애 원인을 추적할 때 큰 도움이 됩니다.


큐 비동기 발송

Notification 클래스에 ShouldQueue를 구현하면 발송 작업이 Laravel 큐를 통해 비동기로 처리됩니다. 알림톡·SMS API 호출은 외부 네트워크 요청이므로, 트래픽이 몰리는 상황에서는 큐 처리가 사실상 필수입니다.

use Illuminate\Contracts\Queue\ShouldQueue; 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 캐시 사용 권장 (다중 워커 환경)CACHE_DRIVER=redisQUEUE_CONNECTION=redis

NOTE

여러 대의 큐 워커를 동시에 운영한다면 redis 또는 database 캐시 드라이버를 사용해야 합니다. SendGo 인증 토큰은 캐시에 저장되어 재사용되는데, 워커마다 캐시가 따로 놀면(예: file 드라이버) 워커마다 토큰을 중복 발급받아 불필요한 API 호출이 늘어날 수 있습니다.


v1 → v2 마이그레이션

Notification 코드를 전혀 건드리지 않고, .env의 한 줄만 바꾸면 v2로 전환됩니다.

SENDGO_API_VERSION=v2

v2 주요 변경점:

항목v1v2
토큰 형식base64 인코딩그대로 사용 (sgv2. 접두사)
토큰 재발급401/403 발생 시 무조건 재발급에러 코드별로 분기 처리 (설정 오류는 재발급하지 않음)
에러 응답—code / message / traceId 포함

v2 에러 코드 전체 목록은 API_V2_ERROR_CODES.md에서 확인할 수 있습니다.


에러 코드

에러가 발생하면 SendGoException::context()['error_code'] 값으로 원인을 확인할 수 있습니다.

인증 (401)

코드설명
INVALID_AUTH_HEADERAuthorization 헤더 없음
INVALID_BASIC_AUTHBasic 인증 형식 오류
INVALID_BASIC_AUTH_PAYLOADBasic 인증 페이로드 오류
INVALID_ACCESS_KEY유효하지 않은 Access Key
INVALID_SECRET_KEY유효하지 않은 Secret Key
INVALID_BEARER_TOKENBearer 토큰 없음
INVALID_BEARER_TOKEN_PREFIXBearer 토큰 접두사 오류 (v2)
INVALID_BEARER_TOKEN_PAYLOADBearer 토큰 페이로드 오류 (v2)
INVALID_BEARER_SIGNATUREBearer 토큰 서명 불일치 (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_MISMATCHSMS 발신키가 앱과 불일치
KAKAO_SENDER_APPLICATION_MISMATCH카카오 발신키가 앱과 불일치

리소스 (404)

코드설명
INVALID_SENDER_KEY유효하지 않은 SMS 발신키
INVALID_KAKAO_SENDER_KEY유효하지 않은 카카오 발신키
INVALID_TEMPLATE_CODE유효하지 않은 템플릿 코드
OWNER_NOT_FOUND캠페인 소유자 확인 불가
SENDER_OWNER_NOT_FOUNDSMS 발신키 소유자 확인 불가
KAKAO_SENDER_OWNER_NOT_FOUND카카오 발신키 소유자 확인 불가

요청 (422)

코드설명
EMPTY_CONTACTS수신자 없음
PAYMENT_REQUIRED크레딧 부족
MESSAGE_PROCESSING_FAILEDSMS/MMS 처리 실패
NOTICE_PROCESSING_FAILED알림톡 처리 실패
FRIEND_PROCESSING_FAILED친구톡 처리 실패

패키지 내부

코드설명
INVALID_API_VERSION지원하지 않는 API 버전
MULTIPLE_RECIPIENTS_NOT_SUPPORTED_IN_NOTIFICATIONLaravel Notification 채널에서 toMany() 사용 불가

v2 에러 코드에 대한 상세 설명은 API_V2_ERROR_CODES.md를 참고하세요.


트러블슈팅

메시지가 전송되지 않을 때

  1. .env에 설정한 API 키가 올바른지 확인합니다.
  2. SendGo 계정 잔액(크레딧)을 확인합니다.
  3. 전화번호 형식을 확인합니다 (01012345678처럼 하이픈 없이 전달해야 합니다).
  4. storage/logs/laravel.log에서 에러 로그를 확인합니다.

알림톡이 의도치 않게 SMS로 대체 발송될 때

  1. SendGo 콘솔에서 템플릿 승인 상태를 확인합니다.
  2. 템플릿 변수(var1~var8) 매핑이 올바른지 확인합니다.
  3. 카카오톡 채널 연동 상태를 확인합니다.

다중 워커 환경에서 토큰 충돌이 발생할 때

  • CACHE_DRIVER=redis로 변경하세요 (워커 간 캐시 공유가 필수입니다).

INVALID_API_VERSION 에러가 발생할 때

  • SENDGO_API_VERSION 값이 v1 또는 v2로 정확히 설정되어 있는지 확인하세요.

라이선스

MIT 라이선스를 따릅니다. 자세한 내용은 LICENSE 파일을 참고하세요.


Made with ❤️ by Techigh | Powered by SendGo.io

최신 코어 API 연동

기존 Notification 채널 호출 방식은 그대로 유지됩니다. 다만 최신 브랜드메시지·템플릿·발신번호 관리 기능은 sendgo/php ^1.4 코어 패키지를 통해 제공됩니다. SENDGO_API_VERSION=v2로 설정한 뒤 app(\Sendgo\Php\Sendgo::class)로 코어 서비스를 가져와 사용하면 됩니다.

계정·조직·API 키·허용 IP 관리처럼 발송과 무관한 기능은 SENDGO_AGENT_TOKEN을 설정한 뒤 app(\Sendgo\Php\AccountClient::class)를 통해 사용합니다. 계정 클라이언트는 발송용 API 키 없이도 동작합니다. SENDGO_URL을 생략하면 기본값으로 https://sendgo.io를 사용합니다.

템플릿 폴더 (1.6.0)

템플릿 폴더 기능은 기업 계정의 발송용 API 키와 apiVersion=v2 설정 조합으로 사용하는 서버 전용 API입니다. 알림톡과 브랜드메시지는 같은 폴더 구조를 공유하며, 목록 조회 시 templateType은 notice 또는 brand 값을 가집니다. 목록 응답은 data.folders 트리 구조와 함께 total, uncategorised(미분류) 개수를 반환합니다. templateCount는 하위 폴더의 템플릿 수를 제외한, 해당 폴더 자체의 템플릿 수를 의미합니다.

  • 생성: name은 필수, parentUuid는 선택입니다. 폴더는 최대 5단계까지 중첩할 수 있으며, 같은 부모 폴더 아래에 이름이 중복되면 409 에러가 발생합니다.
  • 이동: 동일한 발신 프로필에 속한 templateCodes를 1~100개까지 한 번에 이동할 수 있습니다. folderUuid는 필수이며, null을 전달하면 미분류 상태로 이동합니다.
  • 템플릿 목록 조회: folderUuid=none은 미분류 템플릿, 특정 UUID는 해당 폴더 내 템플릿, 값을 생략하면 전체 템플릿을 반환합니다.
  • 템플릿 등록: 선택 필드인 folderUuid로 등록할 폴더를 지정할 수 있습니다. 등록 이후 폴더를 바꾸고 싶다면 기존 템플릿 수정 API 대신 반드시 폴더 이동 API를 사용해야 합니다.

NOTE

승인되지 않은 키로 호출하면 403 ACCESS_KEY_NOT_APPROVED가 즉시 반환되며, 토큰 재발급이나 재시도는 일어나지 않습니다. 계정 API의 autoApprove 값은 서버에 설정된 실제 승인 정책을 나타내는 값이니 참고하세요.

app(\Sendgo\Php\Sendgo::class)->templateFolders->list(['templateType' => 'notice']);

이 기능을 사용하려면 코어 패키지 1.6.0 이상이 필요합니다. 전체 메서드 목록은 코어 문서를 참고하세요.

1.6 이메일 API와 브랜드 타기팅

이메일 발송 기능은 서버 전용이며, 클라이언트 설정에서 API 버전을 v2로 지정해야 사용할 수 있습니다. 브랜드 타기팅 옵션으로는 M(친구+비친구), N(비친구), I(친구 교집합), O(친구만), F(동보) 값을 지원합니다. O 값은 SDK에서 임의로 변환하지 않고 그대로 서버로 전달됩니다.

이메일 발송, 발송 전 견적 조회, 상태 조회, 취소 기능은 물론 발신자·도메인 인증, 자격 증명 관리, 수신함 및 원본 EML 조회, 템플릿·주소록·연락처·발신자 프로필·캠페인 API까지 폭넓게 지원합니다. 일반 API는 기존 앱의 Bearer 인증 방식을 그대로 사용합니다. 반면 EmailService의 withCredentials(PHP/Node), with_credentials(Python 계열), WithCredentials(C# 계열), Go의 NewEmailWithCredentials는 별도로 발급받은 이메일 전용 credential ID/password를 사용하며, /api/v2/email-service 엔드포인트로 호출됩니다. 이 인증 방식은 인증(auth), 발송·견적·조회·취소, 도메인 관련 API에만 사용할 수 있습니다. 내부용 email-gateway나 공개 서명 기반 수신거부 URL은 SDK가 관리하는 API 범위에 포함되지 않습니다.

단건 발송 시 to에는 이메일 주소 하나만 지정합니다. send 호출에는 idempotency_key를 반드시 지정해야 하며, 같은 발송을 재시도할 때는 동일한 키를 재사용해야 중복 발송을 막을 수 있습니다. 캠페인 발송에는 견적 응답에서 받은 quote_hash와 idempotency_key가 함께 필요합니다. SDK가 키를 임의로 생성해주거나, 네트워크 오류·429·5xx 응답을 자동으로 재시도해주지는 않는다는 점에 유의하세요. Bearer 인증에서 발생한 401만 최대 한 번 자동으로 토큰을 갱신하며, 이메일 권한 거부로 인한 403이나 Basic 인증 실패는 그대로 반환됩니다. 마케팅 메일을 발송할 때는 sender_name, sender_address, sender_contact 값도 함께 지정해야 합니다. 첨부파일은 attachments: [{name, type, content}] 형태로 전달하며, content는 base64로 인코딩된 값이어야 합니다.

응답은 서버가 내려준 JSON 객체 또는 배열을 가공 없이 그대로 반환하며, 204 응답은 언어별로 null/nil/None 등으로 매핑됩니다. 원본 EML 데이터는 바이트 형태(PHP·Ruby에서는 바이트 문자열)로 반환됩니다. Java/Go/.NET/Dart에서는 다양한 응답 형태를 담기 위해 각각 Object/any/object/dynamic 타입을 사용합니다 (.NET의 경우 JSON 응답은 JsonElement 타입입니다). 모든 관리용 API 요청 본문은 서버에서 정의한 필드명(snake_case 표기)을 그대로 사용해야 합니다.

이 기능은 sendgo/php 클라이언트에 의존하는 $email 속성을 통해 접근할 수 있으며, 코어 패키지 최소 요구 버전은 1.6.0입니다.