이렇게 본인 인증을 해볼까?

Yehyeok Bang·2024년 8월 31일
25
post-thumbnail

많은 서비스에 구현된 로그인 기능을 어떻게 구현하면 좋을 지 고민하며 작성하는 글이에요. 또한, 왜 그렇게 하고 싶은가?를 포함하여 프로젝트 팀원에게 제안하기 위한 글이에요.

이전 글에서는 어떻게 로그인 기능을 구현하고, 어떻게 회원가입을 시킬 것인가에 대해 고민했어요. 이번에는 회원가입 중 필요한 본인 인증 과정에 대해서 이야기해보려고 해요.

최근 팀원들과 로그인 및 회원가입 기능에 대해 논의하면서, 사용자 인증 방식에 대해 여러 가지 의견이 나왔어요. 그중에서 가장 많은 관심을 받은 것은 휴대폰 인증이었어요. 이번 글에서는 왜 휴대폰 인증이 나오게 되었는지, 그리고 이를 도입할 때 고려해야 할 점들은 무엇이 있는지 알아보려고 해요.

왜 휴대폰 인증을 고려하게 되었는가?

로그인 및 회원가입 과정에서 사용자의 신원을 명확히 하고, 사용자가 여러 개의 계정을 만드는 것을 방지하는 것은 서비스의 품질을 높일 수 있어요. 여러 개의 계정을 만드는 것이 쉽다면 아래와 같은 부작용이 발생할 수 있어요.

  • 사용자가 여러 개의 계정을 만들어 서비스를 악용할 가능성이 있어요. 예를 들어, 가짜 리뷰를 남기거나, 커뮤니티 형태의 서비스인 경우 특정 분위기를 조성하는 등의 문제를 일으킬 수 있어요.

  • 모임 생성 및 가입 기능이 있는 서비스의 경우, 사용자가 책임감을 가지는 것이 중요할 수 있어요. 만약 한 사용자가 여러 개의 계정을 생성하지 못한다면 조금 더 신중하게 서비스를 이용할 것이라고 기대할 수 있어요.

이러한 문제들을 해결하기 위해 팀원들은 휴대폰 번호를 통한 본인 인증이 효과적이라고 생각했어요. 대부분의 사용자는 하나의 휴대폰 번호만 가지고 있기 때문에 다중 계정 생성이 어렵기 때문이에요.

휴대폰 인증의 장점

  • 식별성 : 휴대폰 번호는 일반적으로 하나씩만 소유하는 경우가 많기 때문에 사용자를 식별하기 쉬운 편에 속해요.

  • 책임감 : 결국 여러 개의 계정을 만들기 어렵기 때문에 각 사용자에게 책임감 있는 행동을 기대할 수 있어요.

  • 익숙함 : 거의 모든 사용자가 휴대폰을 가지고 있으며, 휴대폰 인증 절차에 익숙해요.

휴대폰 인증의 단점과 고려 사항

하지만, 늘 그렇듯 단점 하나 없는 만능 방법은 없는 것 같아요. 우선 휴대폰 인증이 만능은 아니에요. 도입 전에 몇 가지 단점을 인지해야 해요.

  • 비용 : SMS를 통한 인증 과정은 비용이 발생해요. 특히, 서비스가 성장함에 따라 인증 요청이 늘어나면, 이 비용도 함께 증가해요.

  • 번호 변경 : 사용자가 휴대폰 번호를 변경하는 경우, 재인증이 필요하게 돼요. 또한 이전 번호 사용자가 서비스를 이용한 경험이 있는 경우 현재 사용자는 계정 생성에 불편함을 느낄 수 있어요.

  • 사용자 이탈 : 본인 인증 과정이 번거롭다고 느껴질 경우, 일부 사용자는 회원가입 자체를 포기할 수 있어요.

이메일 인증은요?

본인 인증 과정은 사용자 이탈의 요소가 될 수 있어요. 그래서 쉽게 이 과정을 넘길 수 있는 소셜 로그인과 이메일을 기준으로 통합 계정을 제공하자는 의견도 나왔어요. 하지만 사용자가 각 SNS 계정에 설정한 이메일이 다를 경우(카카오는 네이버 이메일, 페이스북은 구글 이메일로 설정 등) 같은 사용자로 분류하기 어렵고, 이메일도 사실 쉽게 만들 수 있는 요소이기 때문에 팀 내부에서 만족하는 방법이 되진 못했어요.

휴대폰 인증

대부분의 사용자는 휴대폰 인증 과정이 익숙하다고 생각해요. 제 메시지 수신함에 쌓인 인증번호만 봐도 수많은 서비스에서 사용하고 있다는 것을 알 수 있었어요.

휴대폰 인증을 도입할 경우, 사용자는 아래와 같은 과정을 경험해요.

  1. 사용자가 회원가입 시 휴대폰 번호를 입력해요.
  2. 서버는 입력된 번호로 SMS 인증번호를 전송해요.
  3. 사용자는 받은 인증번호를 웹 사이트에 입력해요.
  4. 서버는 인증번호를 확인한 후, 사용자를 인증하고 회원가입 절차를 완료해요.

사실 이 글을 읽는 분들은 이 과정을 이미 알고 있는 경우도 많을 것 같아요. 우리에게 익숙하고 더 만족스러운 인증 과정을 진행할 수 있기 때문에 팀원 분들이 제안했을 것 같아요.

팀 내에서 휴대폰 인증을 선택하고 싶은 이유와 장단점, 흐름을 간단하게 알아봤어요. 이제 발생할 수 있는 문제들을 알아보려고 해요.

비용

우선 SMS를 통한 인증 과정은 비용이 발생해요. 도입 이후 개발 단계에서 테스트만 하더라도 요금이 발생해요.

위 사진들은 각각 본인 인증 과정까지 포함된 서비스SMS 발송 API의 요금 표에요. 직접 인증 과정을 구현하더라도 SMS 발송 건당 요금이 부가되는 것을 볼 수 있어요. 찾아보면 약간의 무료 크레딧을 제공하는 업체도 있지만, 인증 과정 테스트만 하더라도 모두 사용될 양을 제공해요.

팀원 모두 학생이기 때문에 비용이 발생한다는 것은 큰 단점 중 하나에요.

번호 변경

사용자의 휴대폰 번호가 변경되는 경우를 대비하지 않는다면, 예전에 A라는 번호를 사용하던 사용자의 계정과 (번호 변경 후 ) 나중에 똑같은 A라는 번호를 사용하게 될 사용자의 계정이 잘못 통합될 수 있어요.

이는 개인정보 보호 측면에서도 큰 문제이기 때문에 대비해야 해요.

다른 서비스는 어떻게 이 문제를 대처하고 있는지 알아봤어요.

휴대폰 번호 기반으로 가입할 수 있는 당근은 가입 이후에 휴대폰 번호가 변경된 경우 설정 탭 또는 문의를 통해 계정에 반영할 수 있도록 하며, 만약 새로운 번호로 처음 가입하려는데 이미 가입된 계정이 있는 경우에는 이미 존재하는 계정을 삭제하는 방식을 선택한 것 같아요.

휴대폰 번호는 겹칠 수 없고, 변경되는 경우도 많지 않기 때문에 당근과 같은 프로세스를 채택한다면 이 문제를 해결할 수 있을 것 같아요.

그러면 좋은거 아니야?

사실 팀원 모두가 대부분의 관점에서 휴대폰 인증 방식이 최선의 선택이라고 생각했어요. 하지만 여전히 해결되지 않은 문제가 있어요. 바로 비용 문제에요.

팀원 A: 어느 정도의 비용은 괜찮지 않을까요?
팀원 B: 테스트를 위한 계정은 미리 만들어두고 휴대폰 인증 테스트는 초반에만 진행하고 안쓰는 방향은 어때요?
팀원 C: 테스트 과정에선 절약할 수 있지만, 배포 이후에는 저희가 컨트롤하기 힘들지 않을까요?
...

조금 멀리서 보면 "아직 안해봤잖아 너네들", "해보고 말해" 라는 생각이 들 수 있어요.

어떻게 보면 맞는 말이에요. 배포 이후에 팀원들의 지인 100명이 가입하는 경우 (건당 20원 기준) 2,000원이 부과돼요. (생각보다 작은 비용일 수 있어요.) 또한, 포인트 충전 형식으로 결제하는 경우 포인트를 모두 소진했을 때 발송되지 않도록 설정할 수도 있어요.

하지만, 귀한 사용자 한분이 포인트 소진으로 가입하지 못하는 경우도 두렵고, 악의적인 사용자가 휴대폰 인증만 반복적으로 요청하여 많은 비용이 발생될까 두렵기도 해요.

추가로 저는 개발자가 "모든 사용자가 우리가 원하는 대로 사용할 것 같은데?" 와 같은 안일한 태도를 가진다면 언젠가 큰 문제가 발생할 수 있다고 생각했기 때문에 모든 걱정들이 공감이 됐어요.

반대로 해볼까?

남들과는 다른 방향의 인증 방식을 사용하는 쏘카의 기기인증 방식이 있다는 것을 알게 되었어요.

인증 메시지 보내기를 누르면 자동으로 받는 사람과 보낼 내용이 자동으로 입력되며 그대로 인증 메시지를 보내면 인증되는 방식이에요. 추가로 SMS 인증번호 발송을 통한 인증도 함께 진행하는 것 같아요.

iOS에서는 SMS MO(Mobile Oriented) 인증 시스템을 통해 기기 소유 여부를 확인하고 안드로이드에서는 유심(USIM, 범용가입자식별모듈) 조회 및 인증을 거쳐야합니다. 쏘카 블로그

쏘카는 해당 방식을 활용하여 기기 본인 소유의 휴대폰으로만 쏘카를 이용할 수 있는 인증 시스템을 도입했어요. 가입부터 서비스 이용까지 모든 과정을 본인 소유의 기기를 통해서만 가능하도록 구현한 것이에요.

저는 이 글을 보고 인증번호가 담긴 SMS를 사용자에게 보내는 것이 아니라, 사용자가 직접 보내게끔 구현한다면 거의 무료로 휴대폰 인증을 할 수 있지 않을까? 라는 생각이 들어서 여러 가지 테스트를 진행했어요.

각 통신사의 저렴한 요금제를 사용하더라도 통화와 문자는 기본 제공(무제한)인 시대이기 때문에 큰 무리가 되진 않을 것 같다고 생각했어요.

예상 흐름

사용자가 직접 메시지를 전송하게 만드는 방법으로 휴대폰 인증을 할 수 있지 않을까? 라는 생각으로 빠르게 생각해본 시나리오에요.

  1. 사용자가 휴대폰 번호를 입력하고 인증 메시지 보내기 버튼을 누른다.
  2. 휴대폰 번호는 서버에 전송되고 서버는 해당 휴대폰 번호를 위한 무작위 값(인증 번호)을 생성하여 저장 및 응답한다.
  3. 사용자는 완성된 문자 템플릿 그대로 문자를 전송한다. 문자 내용에는 무작위 값(인증 번호)가 포함된다.
  4. 서버는 문자를 전송한 곳(사용자의 핸드폰 번호)과 인증 번호를 비교하여 결과를 응답한다.

위 과정에서 중요하다고 생각하는 요소는 지정된 문자를 보내게 만들기, 사용자가 보낼 곳, 인증 번호 생성 및 비교에요.

지정된 문자를 보내게 만들기

모바일 기기에서 브라우저를 통해 서비스를 사용하는 것을 생각하고 있기 때문에 그것을 기준으로 찾아봤어요. (추후 앱으로 만들 수도 있어요.)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>테스트</title>
  </head>
  <body>
    <a href="sms:{보낼 곳}?body=인증번호입니다.">인증 메시지 보내기</a>
  </body>
</html>

이 코드는 HTML에서 SMS 링크를 만드는 방법이에요. sms:는 모바일 기기에서 특정 번호로 SMS(문자 메시지)를 보낼 수 있는 링크를 생성하는 프로토콜이에요.

href 속성sms:를 사용하여 링크를 클릭하면 SMS(문자 메시지)를 보낼 수 있도록 설정해요. sms: 뒤에는 보낼 곳을 지정하고, ?body= 뒤에는 SMS 메시지 본문에 포함될 내용을 지정해요.

링크를 클릭하면 사용자의 기본 SMS 앱이 열리고, 지정된 보낼 곳과 메시지 본문이 미리 채워져요. 사용자는 이를 전송하기만 하면 돼요.

실제로 브라우저 주소창에 sms:보낼 곳?body=내용을 입력하면 각 환경에 맞게 기본 SMS 앱이 열리는 것을 확인할 수 있어요.

어디로?

원래는 sms: 뒤에는 휴대폰 번호가 오는 것이 일반적이에요. 하지만 요즘 스마트폰의 기본 메신저 앱은 이메일 전송까지 가능해서 이메일 주소를 사용해도 큰 문제가 없어요. 다만, 이메일을 받는 사람이 휴대폰 전화로 답장을 보내는 것을 불가능해요.

저는 이런 인증 방법을 위해 테스트를 하고 있는 이유가 비용이기 때문에 인증을 위한 전화(유지 비용이 발생해요.)를 두는 것은 의미가 없다고 생각했고, 기본 메신저 앱으로 이메일 전송까지 가능하다는 점에서 이메일을 하나 만들어서 인증 메시지 수신함으로 사용하는 것이 더 괜찮다고 생각했어요.

실제로 전송하면 010xxxxxxxx(사용자의 휴대폰 번호)@이메일.com와 비슷한 형태의 이메일 주소로 메시지가 전송되는 것을 확인할 수 있어요. 인증 번호를 전송한 사용자의 전화번호와 인증 번호 모두를 확인할 수 있고, 서비스를 위해 필요한 휴대폰 인증 요청 비용이 무료에 가깝기 때문에 좋다고 생각했어요.

무엇을?

?body=를 사용하여 미리 보내게 만들 내용을 지정할 수 있어요. 저는 서버에서 생성한 인증 번호를 내용으로 지정하면 될 것 같다고 생각했어요. 그림으로 표현하면 아래와 같아요.

사용자가 메시지를 전송하면 휴대폰 번호와 서버에서 만들어준 인증 번호까지 함께 확인할 수 있기 때문에 가능한 인증 방식일 것 같았어요.

수신 확인하기

그림을 살펴보면 메일함의 수신 내역을 확인하는 로직은 매우 중요해요. 사용자가 아무리 인증 메시지를 전송하더라도 메시지를 수신할 수 없다면, 인증할 수 없기 때문이에요. 간단하게 테스트가 목적이었기 때문에 Python을 사용하여 수신된 메일을 확인할 수 있는지 구현해봤어요.

코드를 보기 전 알면 좋아요.

  • IMAP (Internet Message Access Protocol) : 이메일 서버에서 메시지를 가져오거나 관리할 수 있게 해주는 프로토콜이에요. 이 코드에서는 Gmail의 IMAP 서버를 사용해요.
  • imaplib 라이브러리 : Python에서 IMAP을 사용해 이메일을 처리할 수 있는 표준 라이브러리에요.
  • email 라이브러리 : Python 표준 라이브러리로, 이메일 메시지를 파싱하고 분석할 때 사용해요.
import imaplib
import email
from email.header import decode_header

# Gmail 서버 정보
IMAP_SERVER = "imap.gmail.com"
IMAP_PORT = 993

# 로그인 정보
EMAIL_ACCOUNT = "사용할 이메일 주소"
PASSWORD = "앱 비밀번호"

def clean(text):
    # IMAP에서 사용하는 폴더 이름을 처리하기 위한 간단한 문자열 정리 함수
    return "".join(c if c.isalnum() else "_" for c in text)

def main():
    # IMAP 서버에 연결하고 로그인
    mail = imaplib.IMAP4_SSL(IMAP_SERVER)
    mail.login(EMAIL_ACCOUNT, PASSWORD)
    mail.select("inbox")
    status, messages = mail.search(None, 'ALL')

    mail_ids = messages[0].split()

    # 가장 최근의 10개의 이메일만 가져오기
    for i in mail_ids[-10:]:
        status, msg_data = mail.fetch(i, "(RFC822)")
        for response_part in msg_data:
            if isinstance(response_part, tuple):
                msg = email.message_from_bytes(response_part[1])

                # 이메일의 보낸 사람 정보 추출
                sender = decode_header(msg["From"])[0][0]
                if isinstance(sender, bytes):
                    sender = sender.decode()

                print(f"From: {sender}")

                # 이메일의 내용 추출
                if msg.is_multipart():
                    for part in msg.walk():
                        content_type = part.get_content_type()
                        content_disposition = str(part.get("Content-Disposition"))

                        if "attachment" not in content_disposition:
                            try:
                                body = part.get_payload(decode=True).decode()
                                print(f"Message snippet: {body[:100]}")
                            except:
                                pass
                else:
                    content_type = msg.get_content_type()
                    if content_type == "text/plain" or content_type == "text/html":
                        body = msg.get_payload(decode=True).decode()
                        print(f"Message snippet: {body[:100]}")

    # 연결 종료
    mail.close()
    mail.logout()

if __name__ == "__main__":
    main()

이 코드는 Gmail의 받은 편지함에서 최근 10개의 이메일을 가져와서 보낸 사람과 이메일의 일부 내용을 출력하는 코드에요.

실제 실행하여 결과를 보면 위에서 직접 전송했던 메시지를 확인할 수 있었어요. (From: 부분은 제 휴대폰 번호라서 가렸어요.)

마무리

회원가입 및 인증 방식을 구현하기 전, 팀의 상황을 기반으로 최선의 선택을 하고 싶어서 많은 고민을 했던 것 같아요. 특히 비용 문제를 해결하기 위해 사용자가 직접 메시지를 전송하게 만드는 인증 방식을 적용해보려고 해요. 가능성을 확인하기 위해 우선 간단한 단위 테스트를 진행했고, 실제로 개발을 하면서 보안 문제, 사용성 문제 등 우려되는 부분이 있다면 내용을 추가해보려고 해요.

긴 글 읽어주셔서 감사합니다.

더 고민하면 좋을 부분

  • 간단하게 인증 번호를 서버에서 전송하고 이를 입력하게 만드는 방법보다 구현이 어려운 편이에요. 또한 얼마나 이점이 있는지 아직까지 확신할 수 없어요. 즉, 언제라도 다시 변경될 수 있기 때문에 그 부분을 염두하여 개발해야 할 것 같아요.

  • 기존 인증 방식보다는 생소하기 때문에 사용자의 이탈을 막을 수 있게 가이드라인을 이해하기 쉽게 제공하는 것이 중요할 것 같아요.

  • 실제 구현이 가능한지를 확인하기 위해 간단한 html과 python을 사용했어요. 실제 우리가 개발할 때는 어떤 기술로 만들지, 가능한 로직인지는 직접 해봐야 알 것 같아요.

  • 메일을 생성할 때 010xxxxxxxx의 형태로 만들 수 있는가? 구글 이메일은 불가능하다고 해요.

  • 보안 문제가 발생할 시나리오를 생각해보고 회의를 통해 좋은 인증 방식인지 고민하는 시간이 필요할 것 같아요.

참고

profile
부담 없이 질문하고 싶은 개발자가 목표입니다.

0개의 댓글