알림톡은 **승인된 템플릿에 변수를 채워 보내는** 방식입니다. 본문을 코드에서 만드는 게 아니라, 이미 심사를 통과한 문안의 빈칸만 채웁니다.

```text
[#{var2}] 주문이 확인되었습니다.

주문번호: #{var1}
결제금액: #{var3}
```

이 템플릿이 `ORDER_CONFIRM_001` 로 승인돼 있다면, 코드는 `var1`, `var2`, `var3` 만 넘기면 됩니다.

## 준비물

- 승인 완료된 **템플릿 코드** → [알림톡 템플릿 등록과 심사 통과](/ko/cookbook/alimtalk-template)
- **카카오 발신프로필 키**(`kakaoSenderKey`) → [발신번호 사전등록](/ko/cookbook/sender-number)
- 액세스 키 / 시크릿 키

## 필수 파라미터

`POST /api/v2/notices/send`

| 파라미터 | 필수 | 설명 |
| --- | --- | --- |
| `templateCode` | ✅ | 승인된 템플릿 코드 |
| `contacts` | ✅ | 수신자 배열. 비어 있으면 `EMPTY_CONTACTS` |
| `contacts[].contact` | ✅ | 수신 번호. **하이픈 없이 숫자만** (`01012345678`) |
| `contacts[].name` | | 수신자 이름 |
| `contacts[].var1`~`var8` | | 템플릿 변수 |
| `kakaoSenderKey` | ✅ | 카카오 발신프로필 키 (클라이언트에 넣어두면 생략 가능) |
| `senderKey` | | 문자 발신번호 키. SMS 대체 발송을 켤 때 필요 |
| `scheduleType` | | `DIRECTLY`(기본) 또는 `SCHEDULED` |
| `at` | | 예약 시각 `Y-m-d H:i:s`. `scheduleType: SCHEDULED` 일 때 |
| `replaceSms` | | `Y` 면 실패 시 SMS 대체 발송 |
| `smsSubject` / `smsContent` | | 대체 발송 본문. `replaceSms: Y` 면 필수 |

## 언어별 예제

### Node.js / TypeScript

```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:    '3,490,000원',
  }],
});
```

### Python

```python
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", "var2": "맥북 프로"}
    ],
)
```

Django 라면 `sendgo-django`, FastAPI 라면 `sendgo-fastapi` 를 쓰면 설정과 주입이 붙어 있습니다.

### PHP

```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', 'var2' => '29,000원'],
    ],
]);
```

### Laravel

```php
<?php

namespace App\Services;

use Sendgo\Php\Sendgo;
use Sendgo\Php\Exception\SendgoException;
use Illuminate\Support\Facades\Log;

class OrderNotifier
{
    public function __construct(private Sendgo $sendgo) {}

    public function confirmed(Order $order): void
    {
        try {
            $this->sendgo->alimtalk->send([
                'templateCode' => 'ORDER_CONFIRM_001',
                'contacts'     => [[
                    'contact' => $order->user->phone,
                    'name'    => $order->user->name,
                    'var1'    => $order->number,
                    'var2'    => $order->items->first()->name,
                    'var3'    => number_format($order->total).'원',
                ]],
            ]);
        } catch (SendgoException $e) {
            // 발송 실패가 주문 처리를 막지 않도록 로깅만 하고 넘어간다.
            Log::error('알림톡 발송 실패', ['order' => $order->id, 'message' => $e->getMessage()]);
        }
    }
}
```

발송은 **큐에 넣는 편이 낫습니다.** 외부 API 호출이 HTTP 응답 시간에 들어가면 카카오 쪽이 느릴 때 사용자 요청까지 함께 느려집니다.

### Java / Spring Boot

```java
import io.sendgo.*;
import io.sendgo.model.*;
import java.util.List;

sendgo.alimtalk().send(AlimtalkRequest.builder()
    .templateCode("ORDER_CONFIRM_001")
    .contacts(List.of(
        Contact.builder()
            .contact("01012345678")
            .name("홍길동")
            .var1("ORD-001")
            .var2("29,000원")
            .build()
    ))
    .build());
```

### Go

```go
result, err := client.Alimtalk.Send(sendgo.AlimtalkRequest{
    TemplateCode: "ORDER_CONFIRM_001",
    Contacts: []sendgo.Contact{
        {Contact: "01012345678", Name: "홍길동", Var1: "ORD-001", Var2: "29,000원"},
    },
})
if err != nil {
    log.Printf("알림톡 발송 실패: %v", err)
}
```

### Ruby

```ruby
client.alimtalk.send(
  template_code: 'ORDER_CONFIRM_001',
  contacts: [
    { contact: '01012345678', name: '홍길동', var1: 'ORD-001', var2: '29,000원' }
  ]
)
```

### C# / .NET

```csharp
await client.SendAlimtalkAsync(new AlimtalkRequest
{
    TemplateCode = "ORDER_CONFIRM_001",
    Contacts =
    [
        new Contact { PhoneNumber = "01012345678", Name = "홍길동", Var1 = "ORD-001" }
    ],
});
```

### REST 직접 호출

```bash
curl -X POST https://sendgo.io/api/v2/notices/send \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "templateCode": "ORDER_CONFIRM_001",
    "scheduleType": "DIRECTLY",
    "replaceSms": "N",
    "kakaoSenderKey": "your_kakao_sender_key",
    "senderKey": "your_sms_sender_key",
    "contacts": [
      { "contact": "01012345678", "name": "홍길동", "var1": "ORD-001" }
    ]
  }'
```

## 발송이 실패하는 이유

| 오류 코드 | 원인 | 재시도로 해결되나 |
| --- | --- | --- |
| `INVALID_TEMPLATE_CODE` | 없는 코드이거나 아직 승인되지 않음 | ❌ |
| `INVALID_KAKAO_SENDER_KEY` | 발신프로필 키가 틀림 | ❌ |
| `EMPTY_CONTACTS` | 수신자 배열이 비어 있음 | ❌ |
| `PAYMENT_REQUIRED` | 크레딧 부족 | ❌ (충전 필요) |
| `ACCESS_KEY_NOT_APPROVED` | 앱 미승인 | ❌ |
| `IP_NOT_ALLOWED` | 허용 IP 밖 | ❌ |
| 네트워크 타임아웃 | 일시적 | ✅ |

거의 모든 실패는 **재시도해도 똑같이 실패**합니다. 무한 재시도 대신 로깅하고 넘어가세요. 자세한 전략은 [오류 코드와 재시도 전략](/ko/cookbook/error-handling)에 있습니다.

## 놓치기 쉬운 것

- **전화번호에 하이픈을 넣지 마세요.** `010-1234-5678` 이 아니라 `01012345678` 입니다.
- **템플릿 변수를 빠뜨리면 치환되지 않은 채 발송됩니다.** 템플릿에 `#{var4}` 가 있는데 `var4` 를 안 넘기면 수신자가 그 문자열을 그대로 봅니다.
- **광고 문구는 알림톡으로 나가지 않습니다.** 정보성만 승인됩니다. 홍보는 [브랜드메시지](/ko/cookbook/send-brand-message)입니다.
- **클라이언트를 요청마다 새로 만들지 마세요.** 캐시된 토큰이 버려집니다.

## 다음 단계

- [대량 발송과 치환 변수](/ko/cookbook/bulk-send)
- [SMS 대체 발송](/ko/cookbook/sms-fallback)
- [예약 발송](/ko/cookbook/scheduled-send)