네이버 클라우드 가입 후 콘솔에서 아래 메뉴(Simple & Easy Notification Service) 클릭한다.

가장 먼저 프로젝트를 생성한다.

필수 항목을 모두 작성한 후 생성하기 버튼을 클릭한다.

SMS Calling Number에서 발신번호를 등록한다.



발신번호 등록이 완료되면 마이페이지-인증키 관리 메뉴로 이동하여 신규 API 인증키를 발급받는다.



API 인증키 생성이 완료되면 API 권한 설정에 사용하는 ip 주소를 입력 후 추가 > 저장한다.

프로젝트 탭에서 서비스 ID를 누르면 서비스 ID를 확인할 수 있다.


SMS_API_KEY, SMS_API_SECRET, SERVICE_ID, FROM_PHONE_NUMBER 등 보안과 관련된 정보는 config.py에 넣어 사용한다.
# config.py
SMS_API_KEY = 'string'
SMS_API_SECRET = 'string'
SMS_SERVICE_ID = 'string'
SMS_SENDER_NUMBER = 'string'
def make_signature(timestamp):
secret_key = bytes(SMS_API_SECRET, 'UTF-8')
method = "POST"
uri = f'/sms/v2/services/{SERVICE_ID}/messages'
message = method + " " + uri + "\n" + timestamp + "\n" + SMS_API_KEY
message = bytes(message, 'UTF-8')
return base64.b64encode(hmac.new(secret_key, message, digestmod=hashlib.sha256).digest())
def send_sms(phone_number, cert_code):
timestamp = str(int(time.time() * 1000))
signature = make_signature(timestamp)
headers = {
'Content-Type': 'application/json; charset=utf-8',
'x-ncp-apigw-timestamp': timestamp,
'x-ncp-iam-access-key': SMS_API_KEY,
'x-ncp-apigw-signature-v2': signature
}
body = {
"type":'sms',
"contentType":"COMM",
"countryCode":'82',
"from":FROM_PHONE_NUMBER,
"content": "[골리단길] 본인인증",
"messages":[
{
"to": phone_number,
"content": f'[골리단길] 본인 확인을 위해 인증번호[{cert_code}]를 입력해 주세요.'
}
]
}
SMS_URL = f'https://sens.apigw.ntruss.com/sms/v2/services/{SERVICE_ID}/messages'
response = requests.post(SMS_URL, headers=headers, json=body)
return response.json()
def generate_otp(phone_number, otp_create_time):
otp = str(randint(100000, 999999))
session[f'otp_{phone_number}'] = otp # 세션에 인증번호 저장
session[f'time_{phone_number}'] = otp_create_time # 인증번호 생성 시간 저장
return otp
@bp.route('/sendOtp', methods=['POST'])
def send_otp():
phone_number = request.get_json(force=True)['phone']
otp_create_time = request.get_json(force=True)['createTime']
# 휴대폰 번호 중복 체크
user = User.query.filter_by(phone = phone_number).first()
if user:
response={
'resultCode': 409,
'resultDesc': "Conflict",
'resultMsg': "이미 가입된 번호입니다."
}
return Response(response=json.dumps(response), status=409, mimetype="application/json")
else:
# 인증 코드 생성 함수 호출
otp = generate_otp(phone_number, otp_create_time)
# SMS 발송 함수 호출
result = send_sms(phone_number, otp)
if result['statusCode'] == '202':
response={
'resultCode': 200,
'resultDesc': "Success",
'resultMsg': "문자가 발송되었습니다."
}
return Response(response=json.dumps(response), status=200, mimetype="application/json")
else:
response={
'resultCode': 401,
'resultDesc': "Unauthorized",
'resultMsg': "문자 발송에 실패했습니다."
}
return Response(response=json.dumps(response), status=401, mimetype="application/json")
@bp.route('/verifyOtp', methods=['POST'])
def verify_otp():
otp = request.get_json(force=True)['otp']
phone = request.get_json(force=True)['phone']
session_otp = session.get(f'otp_{phone}') # 세션에 저장된 인증번호 가져오기
session_time = session.get(f'time_{phone}') # 세션에 저장된 생성 시간 가져오기
# 세션에 인증번호와 생성 시간이 저장되어 있지 않은 경우
if not session_otp or not session_time:
response={
'resultCode': 404,
'resultDesc': "Not Found",
'resultMsg': "해당 데이터가 존재하지 않습니다."
}
return Response(response=json.dumps(response), status=404, mimetype="application/json")
# 시간이 만료된 경우
if time.time() - session_time > 180:
# 세션에서 인증번호와 생성 시간을 삭제
session.pop(f'otp_{phone}')
session.pop(f'time_{phone}')
response={
'resultCode': 401,
'resultDesc': "Unauthorized",
'resultMsg': "인증번호 인증 시간이 만료되었습니다."
}
return Response(response=json.dumps(response), status=401, mimetype="application/json")
# 검증에 성공한 경우
if otp == session_otp:
# 세션에서 인증번호와 생성 시간을 삭제
session.pop(f'otp_{phone}')
session.pop(f'time_{phone}')
response={
'resultCode': 200,
'resultDesc': "Success",
'resultMsg': "인증이 성공적으로 완료되었습니다."
}
return Response(response=json.dumps(response), status=200, mimetype="application/json")
else:
response={
'resultCode': 401,
'resultDesc': "Unauthorized",
'resultMsg': "인증번호가 일치하지 않습니다."
}
return Response(response=json.dumps(response), status=401, mimetype="application/json")

혹시 메시지 결과 조회도 해보셨나요?