# 전자계약 알림톡/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