5분 만에 카카오 알림톡 발송하기 — 샌드고 빠른 시작
액세스 키 발급부터 첫 카카오 알림톡 발송까지, PHP · Node.js · Python · Java · Go 코드로 5분 안에 끝내는 방법.
POST /api/v2/notices/send카카오 알림톡을 처음 보내기까지 필요한 건 다섯 단계입니다. 그중 코드는 마지막 하나뿐이고, 앞의 넷은 한 번만 하면 되는 계정 설정입니다.
급하다면: 계정 설정(1~3단계)이 이미 끝나 있으면 5단계로 바로 가세요.
1단계 — 액세스 키와 시크릿 키 발급
샌드고 콘솔에 로그인한 뒤 연동 관리 → 앱에서 앱을 만들면 accessKey 와 secretKey 가 발급됩니다.
이 두 값은 계정의 발송 권한 전체를 가집니다. 환경변수에 넣으세요.
# .env
SENDGO_ACCESS_KEY=your_access_key
SENDGO_SECRET_KEY=your_secret_key
SENDGO_KAKAO_SENDER_KEY=your_kakao_sender_key
SENDGO_SMS_SENDER_KEY=your_sms_sender_key
SENDGO_API_VERSION=v2
앱에 허용 IP 를 걸어두면 키가 유출돼도 다른 곳에서는 쓸 수 없습니다. 서버 IP 가 고정이라면 걸어두는 편이 좋습니다.
2단계 — 발신번호와 카카오 발신프로필 등록
한국에서는 전기통신사업법상 사전등록된 번호로만 문자를 보낼 수 있습니다. 등록되지 않은 번호는 가입할 때가 아니라 발송할 때 실패하므로, 코드를 쓰기 전에 끝내야 합니다.
- 발신번호(SMS): 콘솔 → 발신번호에서 통신서비스 이용증명원 등을 제출해 등록합니다. 승인까지 영업일 기준 시간이 걸립니다.
- 카카오 발신프로필: 카카오톡 채널을 샌드고에 연결하면
kakaoSenderKey가 발급됩니다. 채널은 카카오 비즈니스에서 비즈니스 채널로 전환돼 있어야 합니다.
자세한 절차는 발신번호 사전등록에 정리돼 있습니다.
3단계 — 알림톡 템플릿 등록과 승인
알림톡의 본문은 미리 승인받은 템플릿입니다. 발송할 때 하는 일은 템플릿의 변수를 채우는 것뿐입니다.
[#{var2}] 주문이 확인되었습니다.
주문번호: #{var1}
결제금액: #{var3}
이런 템플릿을 등록해 ORDER_CONFIRM_001 같은 템플릿 코드를 받으면 준비가 끝납니다. 심사에서 자주 반려되는 이유와 통과 요령은 알림톡 템플릿 등록과 심사 통과에 있습니다.
광고성 문구는 알림톡 템플릿으로 승인되지 않습니다. 정보성만 가능합니다. 홍보 메시지는 브랜드메시지를 쓰세요.
4단계 — SDK 설치
쓰고 있는 언어의 공식 패키지를 설치합니다. 프레임워크 패키지(Laravel, Spring, NestJS 등)는 코어를 자동으로 끌어오므로 둘 다 설치하지 마세요.
composer require sendgo/laravel # Laravel
composer require sendgo/php # 순수 PHP
npm install @sendgo/node # Node.js / TypeScript
pip install sendgo-python # Python
go get github.com/send-go/go # Go
gem install sendgo # Ruby
dotnet add package Sendgo.SDK # .NET
전체 목록과 선택 기준은 내 언어에 맞는 SDK 고르기에 있습니다.
5단계 — 알림톡 발송
클라이언트를 한 번 만들어 두고 재사용합니다. 발신 키는 클라이언트 생성 시점에 넣어두면 발송할 때마다 다시 쓰지 않아도 됩니다.
Node.js / TypeScript
import Sendgo from '@sendgo/node';
const sendgo = new Sendgo({
accessKey: process.env.SENDGO_ACCESS_KEY!,
secretKey: process.env.SENDGO_SECRET_KEY!,
kakaoSenderKey: process.env.SENDGO_KAKAO_SENDER_KEY,
smsSenderKey: process.env.SENDGO_SMS_SENDER_KEY,
apiVersion: 'v2',
});
await sendgo.alimtalk.send({
templateCode: 'ORDER_CONFIRM_001',
contacts: [
{ contact: '01012345678', name: '홍길동', var1: 'ORD-001', var2: '주문', var3: '29,000원' },
],
});
@sendgo/node 는 기본 내보내기(default export) 입니다. import { Sendgo } from '@sendgo/node' 는 동작하지 않습니다.
Python
import os
from sendgo import Sendgo
client = Sendgo(
access_key=os.environ["SENDGO_ACCESS_KEY"],
secret_key=os.environ["SENDGO_SECRET_KEY"],
kakao_sender_key=os.environ.get("SENDGO_KAKAO_SENDER_KEY"),
sms_sender_key=os.environ.get("SENDGO_SMS_SENDER_KEY"),
api_version="v2",
)
client.alimtalk.send(
template_code="ORDER_CONFIRM_001",
contacts=[
{"contact": "01012345678", "name": "홍길동", "var1": "ORD-001", "var3": "29,000원"}
],
)
PHP
<?php
use Sendgo\Php\Sendgo;
$sendgo = new Sendgo([
'access_key' => $_ENV['SENDGO_ACCESS_KEY'],
'secret_key' => $_ENV['SENDGO_SECRET_KEY'],
'kakao_sender_key' => $_ENV['SENDGO_KAKAO_SENDER_KEY'],
'sms_sender_key' => $_ENV['SENDGO_SMS_SENDER_KEY'],
'api_version' => 'v2',
]);
$sendgo->alimtalk->send([
'templateCode' => 'ORDER_CONFIRM_001',
'contacts' => [
['contact' => '01012345678', 'name' => '홍길동', 'var1' => 'ORD-001', 'var3' => '29,000원'],
],
]);
Laravel
sendgo/laravel 은 ServiceProvider 를 자동 등록하므로 컨테이너에서 바로 주입받습니다.
<?php
use Sendgo\Php\Sendgo;
class OrderController extends Controller
{
public function __construct(private Sendgo $sendgo) {}
public function confirm(Order $order)
{
$this->sendgo->alimtalk->send([
'templateCode' => 'ORDER_CONFIRM_001',
'contacts' => [[
'contact' => $order->user->phone,
'name' => $order->user->name,
'var1' => $order->number,
'var3' => number_format($order->total).'원',
]],
]);
return response()->json(['success' => true]);
}
}
Java
import io.sendgo.*;
import io.sendgo.model.*;
import java.util.List;
SendgoClient sendgo = new SendgoClient(SendgoConfig.builder()
.accessKey(System.getenv("SENDGO_ACCESS_KEY"))
.secretKey(System.getenv("SENDGO_SECRET_KEY"))
.kakaoSenderKey(System.getenv("SENDGO_KAKAO_SENDER_KEY"))
.smsSenderKey(System.getenv("SENDGO_SMS_SENDER_KEY"))
.apiVersion("v2")
.build());
sendgo.alimtalk().send(AlimtalkRequest.builder()
.templateCode("ORDER_CONFIRM_001")
.contacts(List.of(
Contact.builder().contact("01012345678").name("홍길동").var1("ORD-001").build()
))
.build());
Go
package main
import (
"log"
"os"
sendgo "github.com/send-go/go/sendgo"
)
func main() {
client, err := sendgo.New(sendgo.Config{
AccessKey: os.Getenv("SENDGO_ACCESS_KEY"),
SecretKey: os.Getenv("SENDGO_SECRET_KEY"),
KakaoSenderKey: os.Getenv("SENDGO_KAKAO_SENDER_KEY"),
SmsSenderKey: os.Getenv("SENDGO_SMS_SENDER_KEY"),
ApiVersion: "v2",
})
if err != nil {
log.Fatal(err)
}
_, err = client.Alimtalk.Send(sendgo.AlimtalkRequest{
TemplateCode: "ORDER_CONFIRM_001",
Contacts: []sendgo.Contact{
{Contact: "01012345678", Name: "홍길동", Var1: "ORD-001"},
},
})
if err != nil {
log.Fatal(err)
}
}
첫 발송이 실패했다면
| 오류 코드 | 뜻 | 할 일 |
|---|---|---|
INVALID_ACCESS_KEY |
액세스 키가 틀렸다 | 환경변수가 실제로 로드됐는지 확인 |
ACCESS_KEY_NOT_APPROVED |
앱이 아직 승인되지 않았다 | 콘솔에서 앱 승인 상태 확인 |
IP_NOT_ALLOWED |
허용 IP 밖에서 호출했다 | 앱의 허용 IP 목록에 서버 IP 추가 |
INVALID_TEMPLATE_CODE |
없는 템플릿 코드 | 콘솔의 템플릿 코드와 대조 (승인 완료 상태여야 함) |
INVALID_KAKAO_SENDER_KEY |
카카오 발신프로필 키가 틀렸다 | 콘솔 → 카카오 발신프로필에서 키 재확인 |
EMPTY_CONTACTS |
수신자가 비었다 | contacts 배열이 실제로 채워졌는지 확인 |
PAYMENT_REQUIRED |
크레딧 부족 | 충전 후 재시도 (재시도만으로는 해결되지 않음) |
전체 목록과 재시도 전략은 오류 코드와 재시도 전략에 있습니다.
다음 단계
- 수신자마다 다른 값을 넣어 한 번에 보내기 → 대량 발송과 치환 변수
- 알림톡이 실패하면 문자로 대신 보내기 → SMS 대체 발송
- 정해진 시각에 보내기 → 예약 발송
- 템플릿 없이 자유 본문 보내기 → SMS·LMS·MMS 보내기
자주 묻는 질문
- 알림톡을 보내려면 반드시 템플릿을 먼저 등록해야 하나요?
- 네. 알림톡은 카카오 심사를 통과한 템플릿에만 발송할 수 있습니다. 발송 시점에 본문을 자유롭게 작성할 수 없고, 승인된 템플릿의 변수(var1~var8)만 채워 보냅니다. 자유 본문이 필요하면 브랜드메시지나 SMS/LMS 를 사용하세요.
- 발신번호를 등록하지 않고 테스트할 수 있나요?
- 없습니다. 전기통신사업법에 따라 사전등록된 발신번호로만 발송할 수 있고, 등록되지 않은 번호는 가입 시점이 아니라 발송 시점에 실패합니다. 테스트 전에 발신번호 등록을 먼저 끝내세요.
- 액세스 키를 코드에 직접 넣어도 되나요?
- 안 됩니다. accessKey 와 secretKey 는 계정 전체의 발송 권한을 가지므로 환경변수나 시크릿 매니저에 두고 읽어야 합니다. 저장소에 커밋된 키는 즉시 폐기하고 재발급하세요.
- 토큰을 직접 발급하고 갱신해야 하나요?
- 아니요. 모든 공식 SDK 가 토큰 발급·캐싱·만료 시 재발급과 401/403 재시도를 자동으로 처리합니다. 직접 토큰 루프를 만들면 요청마다 토큰을 새로 받는 비효율이 생깁니다.