๐Ÿ”ฅTIL#10. ์‚ฌ์šฉ์ž ๋ฌธ์ž ์ธ์ฆ ๊ธฐ๋Šฅ

๋ฐฑ์Šน์ง„ยท2020๋…„ 11์›” 27์ผ
1

wecode Django ์‹ค์Šต

๋ชฉ๋ก ๋ณด๊ธฐ
12/16

Today I learned

์ž‘์—…์ค‘์ธ project์—์„œ ํšŒ์›๊ฐ€์ž…์‹œ ํ•ธ๋“œํฐ ๋ฒˆํ˜ธ๋ฅผ ํ†ตํ•œ ๋ฌธ์ž์ธ์ฆ์„ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค.
googling์„ ํ†ตํ•ด NAVER cloud ๊ฐ€ SMS API ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธ, django์—์„œ ์‚ฌ์šฉ์ž ํ•ธ๋“œํฐ๋ฒˆํ˜ธ๋ฅผ ๋ฐ›์œผ๋ฉด ์ธ์ฆ์ฝ”๋“œ๋ฅผ ๋ฐœ๊ธ‰ํ•˜์—ฌ ncloud์— ๋ฌธ์ž ์ „์†ก ์š”์ฒญ์„ ํ•˜๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•˜๋Š” ์ธ์ฆ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•˜์—ฌ "์Šน์ธ"์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ–ˆ๋‹ค.

1. Naver cloud

SMS Service ์ด์šฉ์„ ์œ„ํ•ด ํ”Œ๋žซํผ์„ ๊ฒ€์ƒ‰ํ•˜๋‹ค๊ฐ€ ๊ฐ€์žฅ ์ €๋ ดํ•˜๊ณ  ์ฒซ ๊ฐ€์ž…์‹œ 10๋งŒ ํฌ์ธํŠธ๋ฅผ ์ œ๊ณตํ•˜๋Š” naver cloud๋ฅผ ์„ ํƒํ–ˆ๋‹ค. Service๋ฅผ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•ด์„  Access key์™€ secret key๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์•„์•ผ ํ•˜๋Š”๋ฐ ์ด ๋ฐฉ์‹์€ naver cloud์—์„œ ์นœ์ ˆํ•˜๊ฒŒ ์†Œ๊ฐœํ•˜๊ณ  ์žˆ๋‹ค.

naver cloud ์‚ฌ์šฉ๋ฐฉ๋ฒ•
SMS api ์‚ฌ์šฉ๋ฐฉ๋ฒ•

2. django ์—์„œ ์ธ์ฆ์ฒ˜๋ฆฌ ๊ณผ์ • ๊ตฌํ˜„

Backend๊ฐ€ ๋ฐœ๊ธ‰ํ•œ ์ธ์ฆ์ฝ”๋“œ๋ฅผ ๊ฐ€์ž…์‹ ์ฒญ์ž๋Š” 5๋ถ„ ์ด๋‚ด์— Service์— ์ „์†กํ•ด์•ผ ํ•œ๋‹ค. Backend์—์„  ์ด๋ฅผ ์œ„ํ•ด TimeStampModel์˜ table์„ ์ƒ์„ฑ, "์ธ์ฆ์š”์ฒญ" ๋ฉ”์‹œ์ง€ ์ˆ˜์‹ ์‹œ ํ•ด๋‹น phone number์™€ ์ƒ์„ฑํ•œ ์ธ์ฆ์ฝ”๋“œ๋ฅผ table์— ๋‹ด๋Š”๋‹ค.

# user/models.py
from random import randint
import time
import datetime
import hmac
import base64
import hashlib
import requests
import json

from django.db import models
from django.utils import timezone
from model_utils.models import TimeStampedModel

class SMSAuthRequest(TimeStampedModel):
    phone_number = models.CharField(verbose_name='ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ', primary_key=True, max_length=50)
    auth_number = models.IntegerField(verbose_name='์ธ์ฆ ๋ฒˆํ˜ธ')

    class Meta:
        db_table = 'sms_auth_requests'

    def save(self, *args, **kwargs):
        self.auth_number = randint(1000, 10000)
        super().save(*args, **kwargs)
        self.send_sms() # ์ธ์ฆ๋ฒˆํ˜ธ๊ฐ€ ๋‹ด๊ธด SMS๋ฅผ ์ „์†ก

    def send_sms(self):
    	service_id = '์„œ๋น„์ŠคID'
        url = 'https://sens.apigw.ntruss.com'
        uri = '/sms/v2/services/' + service_id + '/messages'
        api_url = url + uri

        body = {
            "type": "SMS",
            "contentType": "COMM",
            "from": "{๋ฐœ์†ก ๋ฒˆํ˜ธ}",
            "content": "[ํ…Œ์ŠคํŠธ] ์ธ์ฆ ๋ฒˆํ˜ธ [{}]๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.".format(self.auth_number),
            "messages":[{"to":self.phone_number}]
        }

        timeStamp = str(int(time.time() * 1000))
        access_key = '{๋ฐœ๊ธ‰๋ฐ›์€ access_key}'
        string_to_sign = "POST " + uri + "\n" + timeStamp + "\n" + access_key
        signature = self.make_signature(string_to_sign)

        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
        }

    def make_signature(self, 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

    @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

์‚ฌ์šฉ์ž ์š”์ฒญ์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌํ•˜๋Š” View ๋กœ์ง์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค

# user/views.py

import re
import jwt
import json
import bcrypt
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

class SMSCheckView(APIView):
    def post(self, request):
        try:
            phone_number = request.data['phone_number']
          SMSAuthRequest.objects.update_or_create(phone_number=phone_number)

            return Response({'message': 'OK'})

        except KeyError:
            return Response({'message': 'Bad Request'}, status=400)

    def get(self, request):
        try:
            phone_number = request.query_params['phone_number']
            auth_number  = request.query_params['auth_number']
            result       = SMSAuthRequest.check_auth_number(phone_number, auth_number)

            return Response({'message': 'OK', 'result': result})

        except KeyError:
            return Response({'message': 'Bad Request'}, status=400)

์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญ์„ ํ•˜๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ table์— ์š”์ฒญ์ž์˜ ํ•ธ๋“œํฐ๋ฒˆํ˜ธ์™€ ๋ฐœ๊ธ‰ํ•œ ์ธ์ฆ์ฝ”๋“œ, timestamp๊ฐ€ ๋‹ด๊ธด๋‹ค.

๊ทธ ํ›„ naver cloud SMS service์— ํ•ด๋‹น ํ•ธ๋“œํฐ๋ฒˆํ˜ธ๋กœ ์ธ์ฆ์ฝ”๋“œ๋ฅผ ๋ฌธ์ž์ „์†ก ์š”์ฒญํ•˜๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ๋ฌธ์ž๋ฅผ ๋ฐ›์•„ ์ตœ์ข…์ ์œผ๋กœ ํ•ธ๋“œํฐ๋ฒˆํ˜ธ์™€ ์ธ์ฆ์ฝ”๋“œ๋ฅผ ๋ณด๋‚ด๋ฉด Backend๋Š” ์Šน์ธ์—ฌ๋ถ€๋ฅผ ๋ฆฌํ„ด

profile
12๋…„ .NET ๊ฐœ๋ฐœ ๊ฒฝ๋ ฅ์„ ๊ฐ€์ง„ ์›น ์ดˆ์งœ ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค :)

0๊ฐœ์˜ ๋Œ“๊ธ€