# 전자계약 알림톡/SMS 환경별 설정 가이드

> **작성일**: 2026-02-27
> **상태**: 운영 중
> **대상 프로젝트**: MNG

---

## 1. 개요

### 1.1 목적

전자계약(E-Sign) 시스템의 카카오톡 알림톡, SMS, 이메일 발송을 **3개 환경(로컬/개발/운영)**에서 올바르게 설정하고 테스트하기 위한 가이드이다.

### 1.2 핵심 원칙

- **역할 기반 알림**: 본사(creator)는 이메일, 상대방(counterpart)은 카카오톡/SMS
- **환경별 템플릿 분리**: 운영은 원본 템플릿, 개발은 `_DEV` 접미사 템플릿 사용
- **URL 자동 분기**: `config('app.url')`로 환경별 도메인 자동 적용

---

## 2. 환경별 설정

### 2.1 도메인 및 APP_URL

| 환경 | `APP_ENV` | `APP_URL` | 알림톡 버튼 URL 도메인 |
|------|-----------|-----------|----------------------|
| 로컬 (Docker) | `local` | `https://mng.sam.kr` | 로컬 — 알림톡 미사용 |
| 개발 서버 | `local` | `https://admin.codebridge-x.com` | `admin.codebridge-x.com` |
| 운영 서버 | `production` | `https://mng.codebridge-x.com` | `mng.codebridge-x.com` |

### 2.2 바로빌 서버 모드

`barobill_members.server_mode` 컬럼으로 바로빌 API 엔드포인트를 결정한다:

| server_mode | WSDL (카카오톡) | WSDL (SMS) | 용도 |
|-------------|----------------|------------|------|
| `test` | `testws.baroservice.com/KAKAOTALK.asmx` | `testws.baroservice.com/SMS.asmx` | 테스트 |
| `production` | `ws.baroservice.com/KAKAOTALK.asmx` | `ws.baroservice.com/SMS.asmx` | 실제 발송 |

> `server_mode`는 환경(로컬/개발/운영)과 독립적이다. 개발서버에서도 `production` 모드로 실제 발송 가능.

### 2.3 알림톡 템플릿 환경별 분기

코드에서 `resolveTemplateName()` 메서드가 `APP_ENV`에 따라 템플릿명을 자동 결정한다:

```php
private function resolveTemplateName(string $baseName): string
{
    return $baseName . (app()->environment('production') ? '' : '_DEV');
}
```

| 기본 템플릿명 | 운영 (`production`) | 개발/로컬 (기타) |
|-------------|--------------------|--------------------|
| `전자계약_서명요청` | `전자계약_서명요청` | `전자계약_서명요청_DEV` |
| `전자계약_완료` | `전자계약_완료` | `전자계약_완료_DEV` |
| `전자계약_리마인드` | `전자계약_리마인드` | `전자계약_리마인드_DEV` |

---

## 3. 등록된 알림톡 템플릿

### 3.1 운영 템플릿 (mng.codebridge-x.com)

| 템플릿명 | 용도 | 상태 | 버튼 URL |
|---------|------|------|---------|
| `전자계약_서명요청` | 서명 요청 알림 | 승인 완료 | `https://mng.codebridge-x.com/esign/sign/#{토큰}` |
| `전자계약_완료` | 서명 완료 알림 | 승인 완료 | `https://mng.codebridge-x.com/esign/sign/#{토큰}` |
| `전자계약_리마인드` | 서명 독촉 알림 | 승인 완료 | `https://mng.codebridge-x.com/esign/sign/#{토큰}` |

### 3.2 개발 템플릿 (admin.codebridge-x.com)

| 템플릿명 | 용도 | 상태 | 버튼 URL |
|---------|------|------|---------|
| `전자계약_서명요청_DEV` | 서명 요청 알림 | 심사 중 | `https://admin.codebridge-x.com/esign/sign/#{토큰}` |
| `전자계약_완료_DEV` | 서명 완료 알림 | 심사 중 | `https://admin.codebridge-x.com/esign/sign/#{토큰}` |
| `전자계약_리마인드_DEV` | 서명 독촉 알림 | 심사 중 | `https://admin.codebridge-x.com/esign/sign/#{토큰}` |

> 개발 템플릿 본문은 운영 템플릿과 동일하며, 버튼 URL 도메인만 다르다.

### 3.3 템플릿 변수

| 변수 | 용도 | 사용 템플릿 |
|------|------|-----------|
| `#{이름}` | 서명자 이름 | 서명요청, 완료, 리마인드 |
| `#{계약명}` | 계약 제목 | 서명요청, 완료, 리마인드 |
| `#{기한}` | 서명 기한 | 서명요청, 리마인드 |
| `#{완료일}` | 계약 완료일 | 완료 |
| `#{토큰}` | 서명자 액세스 토큰 | 버튼 URL |

---

## 4. 역할 기반 알림 흐름

### 4.1 전체 흐름

```
① 계약 발송 ─→ 본사: 이메일 / 상대방: 카카오톡 알림톡
② OTP 인증 ─→ 본사: 이메일 / 상대방: SMS
③ 다음 서명자 ─→ 본사: 이메일 / 상대방: 카카오톡 알림톡
④ 서명 완료 ─→ 본사: 이메일(PDF) / 상대방: 카카오톡(PDF 다운로드)
```

### 4.2 역할 판별

```php
$isCounterpart = $signer->role === EsignSigner::ROLE_COUNTERPART;
```

| 역할 | 상수 | 알림톡 | SMS(OTP) | 이메일 |
|------|------|--------|----------|--------|
| 본사 (creator) | `ROLE_CREATOR` | ❌ | ❌ | ✅ 항상 |
| 상대방 (counterpart) | `ROLE_COUNTERPART` | ✅ 우선 | ✅ OTP만 | ✅ 폴백 |

### 4.3 이메일 폴백 조건

상대방(counterpart)에게도 이메일을 보내는 경우:
- 전화번호가 없을 때 (`$signer->phone` 없음)
- 알림톡 발송 실패 시 (`$alimtalkFailed = true`)
- 발송 방식이 `email` 또는 `both`일 때

### 4.4 완료 알림 특수 처리

완료 알림톡 버튼은 **서명 페이지가 아닌 문서 다운로드 URL**로 강제 변경된다:

```php
// sendCompletionAlimtalk() 내부
$documentUrl = config('app.url') . '/esign/sign/' . $signer->access_token . '/api/document';

// 버튼 URL 강제 변경 (서명페이지 → 문서 다운로드)
if (str_contains($btn[$urlKey], '/esign/sign/') && !str_contains($btn[$urlKey], '/api/document')) {
    $btn[$urlKey] = $documentUrl;
}
```

---

## 5. SMS (OTP 인증)

### 5.1 발송 조건

상대방(counterpart)이 `alimtalk` 또는 `both` 발송 방식이고 전화번호가 있을 때 SMS로 OTP 발송:

```php
if (in_array($sendMethod, ['alimtalk', 'both'])
    && $signer->phone
    && $signer->role === EsignSigner::ROLE_COUNTERPART) {
    $this->sendOtpViaSms($contract, $signer, $otpCode);
}
```

### 5.2 SMS 발송 파라미터

| 항목 | 값 |
|------|-----|
| API | `BarobillService::sendSMSMessage()` |
| 발신번호 | `barobill_members.manager_hp` |
| 수신번호 | `esign_signers.phone` |
| 메시지 | `[SAM] 전자계약 인증코드: {코드} (5분 이내 입력)` |
| OTP 유효시간 | 5분 |
| 최대 시도 | 5회 |

### 5.3 SMS 실패 시 이메일 폴백

SMS 발송 실패 → 이메일 OTP 폴백 → 이메일도 없으면 500 에러 반환.

---

## 6. 바로빌 템플릿 등록 절차

### 6.1 관리자 페이지

```
https://www.barobill.co.kr 로그인 → 카카오톡 → 템플릿관리
```

### 6.2 DEV 템플릿 등록 시 주의사항

1. **본문**: 운영 템플릿과 **완전히 동일** (1글자도 다르면 안 됨)
2. **버튼 URL**: 도메인만 `admin.codebridge-x.com`으로 변경
3. **템플릿명**: 운영 이름 + `_DEV` 접미사 (예: `전자계약_서명요청_DEV`)
4. **검수 기간**: 영업일 기준 2~3일

### 6.3 새 템플릿 추가 시 체크리스트

- [ ] 바로빌에서 운영용 + 개발용 2개 등록
- [ ] 코드에서 `resolveTemplateName('기본명')`으로 호출
- [ ] 본문의 변수 치환 로직 추가 (str_replace)
- [ ] 버튼 URL의 `#{토큰}` 치환 확인
- [ ] 2단계 검증 (SendKey → GetSendKakaotalk) 포함

---

## 7. 관련 파일

| 파일 | 역할 |
|------|------|
| `app/Http/Controllers/ESign/EsignApiController.php` | 계약 발송, `sendAlimtalk()`, `resolveTemplateName()` |
| `app/Http/Controllers/ESign/EsignPublicController.php` | OTP SMS, 완료 알림톡, `sendCompletionAlimtalk()` |
| `app/Services/Barobill/BarobillService.php` | SOAP 클라이언트 (`sendATKakaotalkEx`, `sendSMSMessage`) |
| `app/Models/ESign/EsignSigner.php` | `ROLE_CREATOR`, `ROLE_COUNTERPART` 상수 |
| `app/Mail/EsignCompletedMail.php` | 완료 이메일 (PDF 다운로드 링크) |
| `app/Services/ESign/PdfSignatureService.php` | 서명 PDF 합성 (`mergeSignatures`) |

---

## 8. 트러블슈팅

### 8.1 환경별 템플릿 미스매치

**증상**: `ResultCode=4` (템플릿 데이터 일치 오류)
**원인**: 개발서버에서 운영용 템플릿(`전자계약_서명요청`)으로 발송 시 버튼 URL 도메인 불일치
**해결**: DEV 템플릿 등록 후 `APP_ENV`가 `production`이 아닌지 확인

### 8.2 서명 PDF 누락 (이메일)

**증상**: 완료 이메일의 다운로드 링크가 서명 없는 초안 PDF 반환
**원인**: `mergeSignatures()` 실패 → `signed_file_path` 미설정 → preview PDF 폴백
**해결**: `downloadDocument()`가 완료 상태에서 자동 재생성 시도. 로그에서 trace 확인:

```bash
# 개발서버 로그 확인
ssh pro@114.203.209.83 "tail -100 /home/webservice/mng/storage/logs/laravel.log | grep 'PDF 서명'"
```

**주요 실패 원인**:
- `storage/fonts/Pretendard-Regular.ttf` 폰트 파일 누락
- FPDI/TCPDF 패키지 미설치 → `composer install` 필요
- `storage/app/esign/{tenant_id}/signed/` 디렉토리 권한 문제

### 8.3 MNG 모델 상수 누락

**증상**: `Undefined constant App\Models\ESign\EsignSigner::ROLE_COUNTERPART`
**원인**: API 프로젝트와 MNG 프로젝트의 모델이 독립적 — API에만 상수 정의됨
**해결**: MNG `EsignSigner.php`에도 동일한 상수 추가 (2026-02-26 핫픽스 완료)

---

## 관련 문서

- [바로빌 카카오톡 연동 README](./README.md) — SOAP API 전체 연동 가이드
- [E-Sign 기술 설계](../../projects/e-sign/technical-design.md) — 전자계약 아키텍처
- [E-Sign API 명세](../../projects/e-sign/api-specification.md) — API 엔드포인트
- [알림톡 연동 계획](../../plans/esign-alimtalk-integration.md) — 초기 계획 (구현 완료)

---

**최종 업데이트**: 2026-02-27
