알림톡 대량 발송과 치환 변수 — 수신자마다 다른 내용 보내기
contacts 배열로 수신자별 변수를 채워 한 번에 발송하는 방법. 배치 크기, 부분 실패 처리, 큐 사용 패턴까지.
POST /api/v2/notices/send대량 발송의 핵심은 한 템플릿, 여러 값입니다. contacts 배열의 각 항목이 자기 변수를 가지므로, 사람마다 다른 주문번호·금액·날짜를 같은 문안으로 보낼 수 있습니다.
기본형
await sendgo.alimtalk.send({
templateCode: 'ORDER_CONFIRM_001',
contacts: [
{ contact: '01011111111', name: '홍길동', var1: 'ORD-001', var2: '29,000원' },
{ contact: '01022222222', name: '김철수', var1: 'ORD-002', var2: '15,000원' },
{ contact: '01033333333', name: '이영희', var1: 'ORD-003', var2: '52,000원' },
],
});
client.alimtalk.send(
template_code="ORDER_CONFIRM_001",
contacts=[
{"contact": "01011111111", "name": "홍길동", "var1": "ORD-001", "var2": "29,000원"},
{"contact": "01022222222", "name": "김철수", "var1": "ORD-002", "var2": "15,000원"},
],
)
<?php
$sendgo->alimtalk->send([
'templateCode' => 'ORDER_CONFIRM_001',
'contacts' => [
['contact' => '01011111111', 'name' => '홍길동', 'var1' => 'ORD-001', 'var2' => '29,000원'],
['contact' => '01022222222', 'name' => '김철수', 'var1' => 'ORD-002', 'var2' => '15,000원'],
],
]);
배치로 나누기
요청 하나에 수만 건을 담으면 타임아웃이 났을 때 어디까지 처리됐는지 알 수 없고, 재시도 비용이 커집니다. 수백 건 단위로 나누세요.
Node.js
const BATCH = 500;
async function sendInBatches(recipients: Recipient[]) {
const failures: Recipient[] = [];
for (let i = 0; i < recipients.length; i += BATCH) {
const chunk = recipients.slice(i, i + BATCH);
try {
await sendgo.alimtalk.send({
templateCode: 'ORDER_CONFIRM_001',
contacts: chunk.map((r) => ({
contact: r.phone,
name: r.name,
var1: r.orderNo,
var2: r.amount,
})),
});
} catch (error) {
// 배치 하나가 실패해도 나머지는 계속 보낸다.
console.error(`배치 ${i / BATCH} 실패`, error);
failures.push(...chunk);
}
}
return failures;
}
PHP · Laravel
<?php
use Illuminate\Support\Collection;
collect($recipients)->chunk(500)->each(function (Collection $chunk) use ($sendgo) {
try {
$sendgo->alimtalk->send([
'templateCode' => 'ORDER_CONFIRM_001',
'contacts' => $chunk->map(fn ($r) => [
'contact' => $r->phone,
'name' => $r->name,
'var1' => $r->order_no,
'var2' => number_format($r->amount).'원',
])->values()->all(),
]);
} catch (\Sendgo\Php\Exception\SendgoException $e) {
Log::error('배치 발송 실패', ['message' => $e->getMessage()]);
}
});
Python
BATCH = 500
for i in range(0, len(recipients), BATCH):
chunk = recipients[i:i + BATCH]
try:
client.alimtalk.send(
template_code="ORDER_CONFIRM_001",
contacts=[
{"contact": r.phone, "name": r.name, "var1": r.order_no}
for r in chunk
],
)
except SendgoError as e:
logger.error("배치 발송 실패: %s", e)
큐에서 처리하기
대량 발송은 웹 요청 안에서 하지 마세요. 사용자는 응답을 기다리고, 타임아웃이 나면 중간부터 다시 보낼 방법이 없습니다.
<?php
// app/Jobs/SendOrderAlimtalk.php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Sendgo\Php\Sendgo;
use Sendgo\Php\Exception\SendgoException;
class SendOrderAlimtalk implements ShouldQueue
{
use Queueable;
// 대부분의 발송 실패는 재시도해도 똑같이 실패한다. 무한 재시도 금지.
public int $tries = 3;
public int $backoff = 30;
public function __construct(private array $contacts) {}
public function handle(Sendgo $sendgo): void
{
$sendgo->alimtalk->send([
'templateCode' => 'ORDER_CONFIRM_001',
'contacts' => $this->contacts,
]);
}
public function failed(SendgoException $e): void
{
// 실패한 배치를 기록해 두면 나중에 실패분만 재발송할 수 있다.
FailedDispatch::create(['contacts' => $this->contacts, 'reason' => $e->getMessage()]);
}
}
부분 실패 다루기
요청이 200 으로 돌아와도 개별 수신자는 실패할 수 있습니다. 없는 번호, 차단된 수신자, 카카오톡 미사용자 등입니다.
- 전체를 재발송하지 마세요. 성공한 사람에게 같은 메시지가 두 번 갑니다.
- 실패분만 골라 다시 보내거나, SMS 대체 발송을 켜서 자동으로 문자로 넘기세요.
- 건별 결과는 콘솔의 발송 내역에서 확인합니다.
놓치기 쉬운 것
- 번호 정규화를 먼저 하세요. DB 에
010-1234-5678,+821012345678,01012345678이 섞여 있는 경우가 흔합니다. 하이픈과 국가번호를 제거해 숫자만 남기세요. - 중복 번호를 제거하세요. 같은 사람에게 두 번 나가고 크레딧도 두 번 빠집니다.
- 크레딧을 미리 확인하세요. 1만 건 발송 도중 잔액이 떨어지면
PAYMENT_REQUIRED가 나면서 나머지가 통째로 실패합니다. - 광고성이라면 야간 발송 금지가 적용됩니다. 배치가 21시를 넘겨 실행되지 않도록 하세요 → 광고성 메시지 규칙
다음 단계
자주 묻는 질문
- 한 번에 몇 명까지 보낼 수 있나요?
- contacts 배열에 여러 수신자를 담아 한 요청으로 보냅니다. 다만 요청 하나가 지나치게 커지면 타임아웃과 재시도 비용이 커지므로, 수백 건 단위로 나눠 보내는 편이 안정적입니다.
- 수신자마다 다른 값을 넣을 수 있나요?
- 네. contacts 배열의 각 항목이 자기 var1~var8 을 가집니다. 같은 템플릿으로 사람마다 다른 주문번호와 금액을 보내는 것이 기본 사용법입니다.
- 일부만 실패하면 어떻게 되나요?
- 요청 자체가 성공해도 개별 수신자는 실패할 수 있습니다. 잘못된 번호나 차단된 수신자 등입니다. 응답과 콘솔의 발송 내역으로 건별 결과를 확인하고, 전체를 재발송하지 말고 실패분만 다시 보내세요.
- 대량 발송을 웹 요청 안에서 처리해도 되나요?
- 권장하지 않습니다. 외부 API 호출이 HTTP 응답 시간에 들어가면 사용자 요청이 함께 느려지고, 타임아웃 시 어디까지 보냈는지 알 수 없습니다. 큐 작업으로 넘기세요.