알림톡 대량 발송과 치환 변수 — 수신자마다 다른 내용 보내기

알림톡 대량 발송과 치환 변수 — 수신자마다 다른 내용 보내기

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 응답 시간에 들어가면 사용자 요청이 함께 느려지고, 타임아웃 시 어디까지 보냈는지 알 수 없습니다. 큐 작업으로 넘기세요.

이 문서에서 쓰는 패키지

관련 문서