본문 바로가기
개발/Django

[Django] Django Rest Framework에서 휴대폰 인증 문자 서비스 만들기

by 유다110 2019. 7. 11.
반응형

서론;

휴대폰 인증을 위해 거쳐야 하는 프로세스는 다음과 같다.

  1. 유저가 휴대폰 번호를 입력한다.
  2. 해당 번호와 랜덤 숫자 4자를 DB에 저장한 뒤, 인증 번호(랜덤 숫자)가 담긴 문자를 유저에게 보낸다. (인증번호 전송 API)
  3. 유저가 인증 번호를 입력한다.
  4. DB에서 유저의 휴대폰 번호와 인증 번호를 대조한 뒤 맞다면 True를, 틀리다면 False를 리턴한다. (인증번호 확인 API)

 

여기서 2, 4번에서 필요한 API 두 개를 만들어보려 한다. 스펙은 다음과 같음

  • Django 2.2
  • PostgreSQL

인증번호 전송 API

 

1) 일단 유저의 전화번호와 랜덤생성한 숫자 4자를 auth에 테이블에 저장한다.

auth 테이블은 단순히 phone_number와 auth_number 만을 저장하게 하고, phone_number를 PK로 두었다. 혹 데이터가 중복된다면 auth_number 칼럼을 업데이트하게 했다.

 

2) DB 저장과 동시에 해당 휴대폰 번호로 인증 번호가 담긴 SMS를 전송한다.

인증번호를 유저 핸드폰번호로 전송하기 위해서는 SMS 발신 서비스가 필요하다. 후보군으로는 다음이 있었는데 나는 가장 저렴한 Naver Cloud를 사용했다. (가입하면 10만원 크레딧을 줘서 테스트하는 데 부담이 없다.)

 

  • AWS SMS (약간의 이해력 필요, [국제발신]이 붙음, 비쌈)
  • Twilio (단순함, [국제발신]이 붙음, 비쌈)
  • 쿨SMS (단순함, 건당 20원)
  • Toast Cloud (안 써봄, 건당 9.9원)
  • Naver Cloud (단순함, 건당 9원)

 

Naver Cloud에서 SMS 서비스를 신청하는 방법은 내가 설명하기보단 공식문서를 보는 게 나을 거다. 참고로 API를 사용하기 위해서는 마이페이지 > 계정 관리 > 인증키 관리에서 API 인증키를 생성해야 한다.

 

 

models.py

import requests
from random import randint
from django.db import models
from model_utils.models import TimeStampedModel

class Auth(TimeStampedModel):
    phone_number = models.CharField(verbose_name='휴대폰 번호', primary_key=True, max_length=11)
    auth_number = models.IntegerField(verbose_name='인증 번호')

    class Meta:
        db_table = 'auth'

    def save(self, *args, **kwargs):
        self.auth_number = randint(1000, 10000)
        super().save(*args, **kwargs)
        self.send_sms()  # 인증번호가 담긴 SMS를 전송

    def send_sms(self):
        url = 'https://api-sens.ncloud.com/v1/sms/services/{serviceId}/messages/'
        data = {
            "type": "SMS",
            "from": "01012345678",
            "to": [self.phone_number],
            "content": "[테스트] 인증 번호 [{}]를 입력해주세요.".format(self.auth_number)
        }
        headers = {
            "Content-Type": "application/json",
            "x-ncp-auth-key": {Sub Account Access Key},
            "x-ncp-service-secret": {SMS Service Secret},
        }
        requests.post(url, json=data, headers=headers)

views.py

from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from . import models as m


class Auth(APIView):

    def post(self, request):
        try:
            p_num = request.data['phone_number']
        except KeyError:
            return Response({'message': 'Bad Request'}, status=status.HTTP_400_BAD_REQUEST)
        else:
            m.Auth.objects.update_or_create(phone_number=p_num)
            return Response({'message': 'OK'})

실행결과

따란

 

인증번호 확인 API

인증번호 확인은 간단하다. 유저의 휴대폰 번호와 제출된 인증 번호가 존재하는지 확인하면 되니까.

다만 휴대폰 인증 서비스는 보통 시간 제한이 있으므로, 처음 인증 번호를 유저에게 보내준 시점, 그러니까 DB가 마지막으로 업데이트된 시점으로부터 5분의 시간 제한을 건다.

 

models.py

import datetime
from django.utils import timezone

class Auth(TimeStampedModel):

    ...
   
    @classmethod
    def check_auth_number(cls, p_num, c_num):
        time_limit = timezone.now() - datetime.timedelta(minutes=5)
        result = cls.objects.filter(
            phone_number=p_num,
            auth_number=c_num,
            modified__gte=time_limit
        )
        if result:
            return True
        return False

views.py

class Auth(APIView):
	...

    def get(self, request):
        try:
            p_num = request.query_params['phone_number']
            a_num = request.query_params['auth_number']
        except KeyError:
            return Response({'message': 'Bad Request'}, status=status.HTTP_400_BAD_REQUEST)
        else:
            result = m.Auth.check_auth_number(p_num, a_num)
            return Response({'message': 'OK', 'result': result})

끝!

+ 덧붙이자면 클래스 명을 Auth 말고 다른 것으로 하길 권한다. 난 AuthSms로 했음...(테이블명은 auth_sms)

 


참고

Node.js로 SMS 인증번호 시스템 직접 구현하기

토스 행운 퀴즈

 

반응형

댓글