[Django] Naver Cloud Platform의 SENS API 서비스를 이용해 문자보내기
우리는 자동화를 목표로 프로젝트를 진행하였기 때문에 어떻게 자동으로 voc를 수집하고, 해결하고, 콜백하여 확인하는 작업을 할 것인가가 큰 고민이었다.
다시 콜백하여 해결이 되었는지 물어보는 것은 챗봇을 통해 진행하기로 하였고, 그 챗봇을 어떻게 고객에게 전달할 것인가가 큰 문제였다. 보이스 챗봇을 할지, 문자 챗봇을 할지 말이다.
보이스 분류는 사투리등과 같이 변수가 있기 때문에 학습 시간의 부족과 accuracy가 높지 않을 것으로 생각되어, 문자를 통해 챗봇을 전송하고 데이터를 전송받기로 하였다.
이러한 문자 전송 또한 자동으로 하기 위해서 SENS API를 도입하기로 하였다.
문자 메시지 전송기능은 혹시 모르는 네트워크 단절의 영향으로 전송되지 못할 것을 대비하여 자동과 수동으로 모두 가능하도록 구성하였다.
- 자동 : VOC 수리완료가 되면 자동으로 문자 메시지를 전송
- 수동 : 체크박스를 이용하여 체크한 고객에게 문자를 전송
SENS API는 설명이 아주 잘 되어 있기 때문에 보고 하면 쉽게 가능했다 ! (물론 난 쫌 해맸다 ㅎ)
1. 문자 보내기
먼저, 발신 번호와 수신 번호를 직접 입력해서 전송하는 것을 도전해보았다.
html 은 아래와 같아. 체크 박스를 이용하여 수동으로 전송기능을 위해 체크 박스가 필요하다.
{% csrf_token %}
<button class="to-VOC-button" type="button" onclick="sendMMS()">VOC전송</button>
. . .
{% for voc in voc_data %}
<tr class='tr-table-content'>
<td class='voc_content'>
<input type="checkbox" class='check-voc' value="{{ voc.tt }}" data-phone="{{ voc.phone }}">
{{ voc.tt }}
</td>
. . .
</tr>
그리고 클라이언트 측에서 체크 박스로 선택된 전화번호를 서버로 전송한다.
체크를 하고 전송버튼을 누르면 voc의 전화번호를 수집 후 서버로 전송한다.
getCookie()를 통해 CSRF 토큰을 가져온다.
그리고 fetch API를 사용하여 서버로 전송한다.
- fetch() 함수를 사용하여 서버의 /aici/send-mms-proxy/ 엔드포인트로 POST 요청을 보낸다. (이때 선택된 전화번호 리스트가 요청의 body에 포함됨)
- 서버 측에서는 /aici/send-mms-proxy/ 엔드포인트를 처리하는 send_mms_proxy() 함수가 실행된다. (외부 API를 호출하여 선택된 전화번호로 MMS를 보냄)
<script>
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
if (!cookieValue) {
var csrfEl = document.querySelector('input[name="csrfmiddlewaretoken"]');
if (csrfEl) {
cookieValue = csrfEl.value;
}
}
return cookieValue;
}
function sendMMS() {
var vocCheckboxes = document.getElementsByClassName('check-voc');
var selectedPhoneNumbers = [];
// 선택된 체크박스에서 전화번호 추출
for (var i = 0; i < vocCheckboxes.length; i++) {
if (vocCheckboxes[i].checked) {
var phoneNum = vocCheckboxes[i].getAttribute('data-phone');
selectedPhoneNumbers.push(phoneNum);
}
}
console.log('Selected Phone Numbers:', selectedPhoneNumbers);
var csrfToken = getCookie('csrftoken');
console.log('CSRF Token:', csrfToken);
// 프록시 URL로 MMS 전송 요청
fetch('/aici/send-mms-proxy/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
body: JSON.stringify({
phone_numbers: selectedPhoneNumbers
})
})
.then(function (response) {
if (response.ok) {
return response.json();
} else {
throw new Error('MMS 전송에 실패했습니다.');
}
})
.then(function (data) {
alert(data.message);
})
.catch(function (error) {
console.error('MMS 전송 중 오류가 발생했습니다:', error);
alert('MMS 전송 중 오류가 발생했습니다. 다시 시도해주세요.');
});
}
</script>
그리고 외부 API를 호출해서 문자를 보내는 뷰 코드는 아래와 같다. (SENS API 설명에 되어 있는대로 하면 됨 !)
클라이언트 (javascript)에서 전달받은 전화번호에 문자를 보내는 POST를 진행한다.
URI, 타임스탬프, 액세스 키 등등은 설명서를 참조할 것 !!)
make_signature()함수를 통해 서명을 생성한다. 각각 키를 잘 넣어라!
[views.py]
import time
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponseNotAllowed
import requests
from django.http import JsonResponse
import json
import base64
import hashlib
import hmac
def send_mms_proxy(request):
url = "https://sens.apigw.ntruss.com"
uri = "/sms/v2/services/" + "서비스아이디" + "/messages"
api_url = url + uri
timestamp = str(int(time.time() * 1000))
access_key = "액세스키"
string_to_sign = "POST " + uri + "\n" + timestamp + "\n" + access_key
signature = make_signature(string_to_sign)
selected_phone_numbers = request.POST.getlist('phone_numbers')
for phone_number in selected_phone_numbers:
headers = {
'Content-Type': 'application/json; charset=utf-8',
'x-ncp-apigw-timestamp': timestamp,
'x-ncp-iam-access-key': access_key,
'x-ncp-apigw-signature-v2': signature
}
body = {
"type": "SMS",
"countryCode": "82",
"from": "발신번호",
"subject": "MMS 제목",
"contentType": "COMM",
"content": "MMS 내용",
"messages": [
{
"subject": "테스트",
"content": "https://www.example.com/",
"to": phone_number,
}
]
}
body = json.dumps(body)
response = requests.post(api_url, headers=headers, data=body)
if response.status_code == 200:
print(f"MMS sent successfully to {phone_number}")
else:
print(f"Failed to send MMS to {phone_number}. Error: {response.text}")
return JsonResponse({'message': 'MMS sent to selected {phone_numbers}. phone numbers'}, status=200)
def make_signature(string):
secret_key = bytes("시크릿키", 'UTF-8')
string = bytes(string, 'UTF-8')
string_hmac = hmac.new(secret_key, string, digestmod=hashlib.sha256).digest()
string_base64 = base64.b64encode(string_hmac).decode('UTF-8')
return string_base64
아래는 CORS 관련 설정인데 외부 서버로부터 요청을 허용하기 위한 것이다. 미들웨어에 corsheaders 를 추가하여 요청을 처리하였다.
[settings.py]
'corsheaders',
. . .
MIDDLEWARE = [
"django.middleware.csrf.CsrfViewMiddleware",
. . .
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
. . .
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CSRF_TRUSTED_ORIGINS = (
'https://localhost:8000',
'https://127.0.0.1:8000',
)
CORS_ORIGIN_WHITELIST = (
'https://localhost:8000',
'https://127.0.0.1:8000',
)
CORS_ALLOW_HEADERS = (
'access-control-allow-credentials',
'access-control-allow-origin',
'access-control-request-method',
'access-control-request-headers',
'accept',
'accept-encoding',
'accept-language',
'authorization',
'connection',
'content-type',
'dnt',
'credentials',
'host',
'origin',
'user-agent',
'X-CSRFToken',
'csrftoken',
'x-requested-with'
)
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
2. 여러명에게 문자 보내기
먼저 CSRF 토큰이 잘 넘어오는지 확인하였고, 그 다음에 전화번호 하나로 문자가 오는지 확인하였다.
근데 체크박스에 여러명의 전화번호가 있기 때문에 리스트로 묶어서 보내야 했다.
그래서 최최최종 완성된 코드는 아래와 같다.
[views.py]
import time
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponseNotAllowed
import requests
from django.http import JsonResponse
import json
import base64
import hashlib
import hmac
def send_mms_proxy(request):
url = "https://sens.apigw.ntruss.com"
uri = "/sms/v2/services/" + "서비스아이디" + "/messages"
api_url = url + uri
timestamp = str(int(time.time() * 1000))
access_key = "액세스키"
string_to_sign = "POST " + uri + "\n" + timestamp + "\n" + access_key
signature = make_signature(string_to_sign)
data = json.loads(request.body)
selected_phone_numbers = data.get('phone_numbers', [])
for phone_number in selected_phone_numbers:
headers = {
'Content-Type': 'application/json; charset=utf-8',
'x-ncp-apigw-timestamp': timestamp,
'x-ncp-iam-access-key': access_key,
'x-ncp-apigw-signature-v2': signature
}
body = {
"type": "SMS",
"countryCode": "82",
"from": "발신번호",
"subject": "MMS 제목",
"contentType": "COMM",
"content": "MMS 내용",
"messages": [
{
"subject": "테스트",
"content": "https://www.example.com/",
"to": phone_number,
}
]
}
body = json.dumps(body)
response = requests.post(api_url, headers=headers, data=body)
message = f"메시지가 전송되었습니다."
return JsonResponse({'message': message}, status=200)
def make_signature(string):
secret_key = bytes("시크릿키", 'UTF-8')
string = bytes(string, 'UTF-8')
string_hmac = hmac.new(secret_key, string, digestmod=hashlib.sha256).digest()
string_base64 = base64.b64encode(string_hmac).decode('UTF-8')
return string_base64