Developer/Posting

Django_네이버 SMS 인증 구현하기 (장고 휴대폰 번호 인증, Simple & Easy Notification Service)

codingzipsa 2021. 1. 6. 18:42
반응형

1. 네이버 클라우드 플랫폼 회원가입 및 Simple & Easy Notification Service 등록

1) 회원가입 및 무료크레딧 수령

네이버 클라우드로 들어가서 가입을 진행한다.

https://www.ncloud.com/

 

NAVER CLOUD PLATFORM

cloud computing services for corporations, IaaS, PaaS, SaaS, with Global region and Security Technology Certification

www.ncloud.com

신규로 가입한 회원에게는 3개월동안 사용할 수 있는 무료크레딧 10만원이 제공된다.

회원가입 완료 후 하기 링크를 통해 10만원 꼭 받고 시작하자.

www.ncloud.com/main/creditEvent

 

NAVER CLOUD PLATFORM

cloud computing services for corporations, IaaS, PaaS, SaaS, with Global region and Security Technology Certification

www.ncloud.com

2) Simple & Easy Notification Service 등록

로그인 후 Console로 진입한다.

 

왼쪽 Products & Services -> 우측편  Simple & Easy Notification Service 클릭

 

'프로젝트 생성' 을 누르면 창이 하나 뜨게 된다.

서비스 Type: PUSH, SMS, Biz Message 중 원하는 서비스 선택 (각각 어떤 서비스인지 나도 잘 모르겠다; 네이버 설명 확인해주세요)

이름: 소문자로 원하는 명칭, 영어만 되는 것 같다.

설명: 다른 프로젝트와 헷갈리지 않게 설명에 잘 써놓는게 좋을 것 같다.

 

프로젝트를 생성한 후 왼쪽 Project -> 우측 서비스 ID를 클릭한다.

 

아래와 같은 정보가 뜨는데, ID 부분에 있는 내용이 중요하다.

(우측 Secret Key는 이번 인증 구현에서 사용하지 않는다)

 

그리고 발신번호를 사전 등록해야 한다. 등록되어 있지 않으면 SMS 인증을 수행할 수 없다.

왼쪽 Calling Number -> 우측 본인인증 (SMS)를 통해 개인 번호를 발신번호로 등록할 수 있다.

혹시 회사나 법인 등 개인 명의가 아닌 번호는 통신서비스 이용증명원을 1:1 문의로 제출해야 등록을 해준다고 한다.

통신서비스 이용증명원은 각 사용 통신사에 문의, 발급받으면 된다고 한다.

 

3) API 인증키 확인

개인 API 인증정보 확인이 필요하다.

로그인 후 마이페이지 - 계정관리 - 인증키 관리로 들어가면 확인할 수 있다.

여기에 있는 Access Key ID와 Secret Key 가 추후에 필요하니 확인해두도록 하자.

 

네이버 클라우드 플랫폼에서 준비 필요한 사항 마무리, 끝!

2. Django models.py 및 views.py (+ utils.py) 작성

1) models.py 작성

User 모델 내 휴대폰 번호, 인증번호 컬럼을 만들어서 해도 되지만, 인증을 위한 별도 Authentication 모델을 작성하였다.

Admin 페이지에서 인증부분만 별도로 관리하는 것이 좋을 것 같아 선택을 하게 되었다.

앱 내부에 있는 models.py에 하기와 같이 모델을 구성했다.

class Authentication(models.Model):
    phone_number = models.CharField('휴대폰 번호', max_length=30)
    auth_number = models.CharField('인증번호', max_length=30)

    class Meta:
        db_table = 'authentications' # DB 테이블명
        verbose_name_plural = "휴대폰인증 관리 페이지" # Admin 페이지에서 나타나는 설명

 

이후 Migrate를 진행하고 서버 진입하여 DB에 테이블 잘 작성되어 있는지 확인하였다.

 

2) views.py (+ utils.py) 작성

기본적으로는 네이버에서 제공해주는 SMS API - API 참조서를 참고하였고, 구글링해서 찾은 블로그를 종합하여 코드를 완성하였다.

apidocs.ncloud.com/ko/ai-application-service/sens/sms_v2/

 

SMS API - API 참조서

기본 정보 API URL https://sens.apigw.ntruss.com/sms/v2 항목 Mandatory 설명 Content-Type Mandatory 요청 Body Content Type을 application/json으로 지정 (POST) x-ncp-apigw-timestamp Mandatory 1970년 1월 1일 00:00:00 협정 세계시(UTC)부

apidocs.ncloud.com

 API 참조서에 따르면 header와 body에 각각 필요한 부분을 넣어서 호출해야 하며 (Mandatory 내용은 문자 그대로 다 넣어줘야 한다) 호출에 필요한 예제코드도 참조서에 나와 있어서 참고할 수 있었다. (Java, JS, Python 코드들이 있다)

위 필요한 내용 중 x-ncp-apigw-signature-v2(서명)부분을 만들기 위해 views.py와 동일한 위치에 utils.py를 만들고 아래와 같이 코드를 입력하였다.

 

import hashlib
import hmac
import base64

def make_signature(timestamp):
    access_key = '개인 API 정보 중 Access Key ID' (예시 = 'nCH86JcJ6FCl40eYc4qp')
    secret_key = '개인 API 정보 중 Secret Key' (예시 = 'jkaPmt102mwMy9YvI7i6IGWTt4QiAn731SF1cNUA

')
    secret_key = bytes(secret_key, 'UTF-8')

    uri = "/sms/v2/services/ncp:sms:kr:263092132141:sms/messages"
    # uri 중간에 Console - Project - 해당 Project 서비스 ID 입력 (예시 = ncp:sms:kr:263092132141:sms)

    
    message = "POST" + " " + uri + "\n" + timestamp + "\n" + access_key
    message = bytes(message, 'UTF-8')
    signingKey = base64.b64encode(hmac.new(secret_key, message, digestmod=hashlib.sha256).digest())
    return signingKey

 

views.py에는 SMS 인증 및 SMS 인증번호 검증 API를 작성한다.

 

# Python
import json, requests, time, random

# Django
from django.views import View
from django.http import JsonResponse
from .utils import make_signature
from .models import Authentication

# 네이버 SMS 인증
class SmsSendView(View):
    def send_sms(self, phone_number, auth_number):
        timestamp = str(int(time.time() * 1000))  
        headers = {
            'Content-Type': "application/json; charset=UTF-8", # 네이버 참고서 차용
            'x-ncp-apigw-timestamp': timestamp, # 네이버 API 서버와 5분이상 시간차이 발생시 오류
            'x-ncp-iam-access-key': '개인 API 정보 중 Access Key ID' (예시 = 'nCH86JcJ6FCl40eYc4qp')
            'x-ncp-apigw-signature-v2': make_signature(timestamp) # utils.py 이용
        }
        body = {
            "type": "SMS", 
            "contentType": "COMM",
            "from": "등록 발신번호 입력", # 사전에 등록해놓은 발신용 번호 입력, 타 번호 입력시 오류
            "content": f"[인증번호:{auth_number}]", # 메세지를 이쁘게 꾸며보자
            "messages": [{"to": f"{phone_number}"}] # 네이버 양식에 따른 messages.to 입력
        }
        body = json.dumps(body)
        requests.post(발송 URI, headers=headers, data=body)
        # 발송 URI 부분에는 아래 URL을 넣어주면 된다.
        https://sens.apigw.ntruss.com/sms/v2/services/ncp:sms:kr:263092132141:sms/messages 
        # 다만, 너무 길고 동시에 보안이슈가 있기에 별도로 분기해놓은 settings 파일에 넣어서 불러오는 것을 추천한다.
        
    def post(self, request):
        data = json.loads(request.body)
        try:
            input_mobile_num = data['phone_number']
            auth_num = random.randint(10000, 100000) # 랜덤숫자 생성, 5자리로 계획하였다.
            auth_mobile = Authentication.objects.get(phone_number=input_mobile_num)
            auth_mobile.auth_number = auth_num
            auth_mobile.save()
            self.send_sms(phone_number=data['phone_number'], auth_number=auth_num)
            return JsonResponse({'message': '인증번호 발송완료'}, status=200)
        except Authentication.DoesNotExist: # 인증요청번호 미 존재 시 DB 입력 로직 작성
            Authentication.objects.create(
                phone_number=input_mobile_num,
                auth_number=auth_num,
            ).save()
            self.send_sms(phone_number=input_mobile_num, auth_number=auth_num)
            return JsonResponse({'message': '인증번호 발송 및 DB 입력완료'}, status=200)

# 네이버 SMS 인증번호 검증
class SMSVerificationView(View):
    def post(self, request):
        data = json.loads(request.body)

        try:
            verification = Authentication.objects.get(phone_number=data['phone_number'])

            if verification.auth_number == data['auth_number']:
                return JsonResponse({'message': '인증 완료되었습니다.'}, status=200)

            else:
                return JsonResponse({'message': '인증 실패입니다.'}, status=400)

        except Authentication.DoesNotExist:
            return JsonResponse({'message': '해당 휴대폰 번호가 존재하지 않습니다.'}, status=400)
반응형