외주 개발 상담에서 가장 자주 듣는 말 중 하나가 "로그인 기능 하나만 추가해주세요"입니다. 많은 비개발 직군의 의사결정자분들이 로그인을 단순한 기능 하나로 인식하지만, 실제로는 웹 서비스에서 가장 복잡하고 보안에 민감한 기능 중 하나입니다. 로그인 하나를 제대로 구현하려면 회원가입, 비밀번호 관리, 소셜 로그인, 세션 관리, 보안까지 최소 5개 이상의 하위 시스템이 필요합니다.
회원가입: 로그인의 시작
로그인 기능은 회원가입부터 시작됩니다. 이메일 인증 발송 및 확인 로직이 필요하고, 개인정보처리방침과 이용약관 동의를 법적 요건에 맞게 처리해야 합니다. 한국의 정보통신망법에 따라 선택 동의와 필수 동의를 구분하고, 마케팅 수신 동의는 별도로 받아야 합니다.
동일한 이메일로 중복 가입을 방지하는 로직, 비밀번호 강도 검증, 필수 입력 항목의 유효성 검사도 포함됩니다. 여기에 본인 인증까지 추가하면 범위가 더 넓어집니다. PASS 인증, SMS 인증, 아이핀 인증 등 방식에 따라 연동 작업이 다르고, 본인 인증 서비스 제공 업체와의 계약 및 별도 비용이 발생합니다.
회원 탈퇴 기능도 빠뜨릴 수 없습니다. 개인정보 삭제 처리, 관련 데이터 보관 기간 설정, 탈퇴 후 재가입 정책까지 설계해야 합니다.
비밀번호 관리의 복잡성
비밀번호는 절대 그대로 저장해서는 안 됩니다. 비밀번호 보안에 사용되는 주요 방식을 정리하면 이렇습니다.
- 단방향 해싱(One-way Hashing): 비밀번호를 한 번 변환하면 다시 원래대로 되돌릴 수 없는 구조입니다. DB가 통째로 유출되더라도 원래 비밀번호를 알아낼 수 없습니다. bcrypt, Argon2 같은 알고리즘이 대표적입니다
- 솔트(Salt): 비밀번호에 사용자마다 다른 랜덤 값을 붙여서 해싱합니다. 두 사람이 같은 비밀번호(예: "1234")를 쓰더라도 DB에는 완전히 다른 문자열로 저장됩니다. 해커가 미리 만들어놓은 비밀번호-해시 대조표(레인보우 테이블)가 무력화됩니다
- 키 스트레칭(Key Stretching): 해싱을 수천~수만 번 반복해서 해커가 무차별 대입 공격(brute force)으로 비밀번호를 추측하는 속도를 극도로 느리게 만듭니다. bcrypt가 이 방식을 내장하고 있습니다
- 페퍼(Pepper): 솔트와 별개로, 서버만 알고 있는 비밀 값을 추가로 섞습니다. DB가 유출되어도 페퍼 값은 서버 환경변수에만 있으므로 한 단계 더 안전합니다
이 방식들을 조합해서 사용하면 비밀번호 보안의 기본이 갖춰집니다. 프레임워크에 따라 기본 내장된 경우도 많지만(Django는 PBKDF2+솔트가 기본), 직접 구현하는 경우에는 하나라도 빠지면 보안 사고로 이어질 수 있습니다.
비밀번호 찾기 기능에는 일회용 임시 토큰 발급, 토큰 만료 시간 설정, 이메일을 통한 재설정 링크 발송이 포함됩니다. 비밀번호 변경 시에는 기존 비밀번호 확인, 새 비밀번호 유효성 검사, 최근 사용한 비밀번호 재사용 방지 같은 보안 정책도 적용해야 합니다.
소셜 로그인 연동 시 고려할 점
소셜 로그인은 사용자 편의를 크게 높여줍니다. 구현 자체는 각 플랫폼의 설정값을 맞추면 되기 때문에 경험 있는 개발자에게는 크게 어렵지 않습니다. 다만 신경 써야 할 부분은 따로 있습니다. 카카오는 비즈앱 전환과 사전 심사, 구글은 OAuth 동의 화면 심사, 네이버는 별도 검수 절차가 필요합니다.
참고로 Firebase Auth나 Auth0 같은 인증 서비스를 쓰면 Google, Facebook, X(Twitter), Apple 로그인은 비교적 쉽게 붙일 수 있습니다. 하지만 카카오, 네이버 같은 국내 소셜 로그인은 이런 서비스에서 기본 지원하지 않아서 별도로 구현해야 합니다.
한국 서비스라면 카카오 로그인이 사실상 필수이므로 이 부분은 알고 있어야 합니다. 세 가지를 모두 연동하면 각 플랫폼이 제공하는 사용자 정보 형식이 전부 다르기 때문에 통합 사용자 프로필로 매핑하는 로직도 필요합니다.
특히 소셜로 가입된 회원들의 DB 컬럼을 잘 설계해야 합니다. 어떤 소셜로 가입했는지, 소셜 고유 ID와 자체 회원 ID를 어떻게 매핑할지, 같은 이메일로 일반 가입과 소셜 가입이 동시에 존재할 때 계정을 어떻게 병합할지 — 이 부분을 초기에 잘 잡아놓지 않으면 나중에 회원 데이터가 꼬입니다.
세션 관리와 보안
로그인 상태 유지도 정교한 설계가 필요합니다. 자동 로그인 기능을 위한 리프레시 토큰 관리, 토큰 만료 시 사용자 경험을 해치지 않는 자동 갱신 로직, 여러 기기에서 동시 접속 시 처리 방식, 다른 기기에서의 강제 로그아웃 기능 등을 고려해야 합니다.
보안 측면에서는 SQL 인젝션 방지, 무차별 대입 공격 차단을 위한 로그인 시도 횟수 제한과 CAPTCHA, HTTPS 필수 적용, CSRF 토큰 처리, XSS 방어 등이 기본 요구사항입니다. OWASP Top 10에서 인증 관련 취약점은 매년 상위 3위 안에 포함됩니다.
휴대폰 번호 로그인의 복잡함
최근에는 이메일 대신 휴대폰 번호로 로그인하는 서비스도 많습니다. 사용자 입장에선 편하지만, 개발 관점에선 엣지 케이스가 많습니다. 가장 대표적인 게 번호 변경입니다. 사용자가 휴대폰 번호를 바꾸면 기존 계정에 접근할 수 없게 됩니다.
이때 본인 인증을 다시 거쳐서 계정을 연결해야 하는데, 이전 번호로는 인증을 받을 수 없으니 이메일이나 다른 수단으로 복구하는 흐름이 필요합니다. 해외 로밍 중 SMS 수신이 안 되는 경우, 듀얼심 사용자의 번호 관리 같은 케이스도 있습니다.
가장 까다로운 케이스는 번호 재배정입니다. A가 010-1234-5678을 해지하고, B가 같은 번호로 새로 개통합니다. B가 해당 서비스에서 휴대폰 번호 로그인을 누르면, 번호 인증은 정상적으로 통과하지만 A의 계정으로 로그인됩니다.
B는 A의 개인 정보, 주문 내역, 결제 수단까지 볼 수 있게 됩니다. 이걸 방지하는 방법은 여러 가지입니다. 접속 환경(디바이스, 브라우저)이 기존과 다르면 추가 인증을 요구하거나, 본인 인증(CI 값 비교)을 거치게 하거나, 일정 기간 미접속 계정은 번호 연결을 자동 해제하는 정책을 적용할 수 있습니다.
설계 단계에서 이런 시나리오를 빠뜨리면 서비스 운영 중에 실제 사고로 이어질 수 있습니다.
대형 서비스들은 이미 이 문제를 해결하고 있습니다. 예를 들어 쿠팡은 휴대폰 번호로 로그인할 수 있지만, 기존과 다른 디바이스나 브라우저에서 접속하면 번호 인증만으로 끝나지 않고 본인인증이나 이메일 인증 같은 추가 인증을 요구합니다.
겉으로는 "번호 넣고 인증번호 받으면 끝"처럼 보이지만, 뒤에서 접속 환경을 분석해서 평소와 다르면 추가 검증을 거치는 구조입니다. 번호 로그인을 도입하려면 이런 보이지 않는 보안 레이어까지 설계해야 합니다. 단순해 보이지만 실제로는 이메일+비밀번호 방식보다 고려할 케이스가 더 많습니다.
프로덕트 메이커의 인증 시스템
초기 서비스라면 소셜 로그인을 주 인증 수단으로 활용하고, 이메일 로그인을 보조 수단으로 두는 것을 권장합니다. 사용자 입장에서 가입 허들이 낮아지고, 비밀번호 관련 CS 문의도 줄어듭니다. 프로덕트 메이커는 JWT 쿠키 기반 인증 시스템을 사용합니다.
액세스 토큰 60분, 리프레시 토큰 1일로 설정하여 보안과 사용자 경험 사이의 균형을 유지합니다. "로그인 하나 추가해주세요"라고 할 때 실제로 필요한 작업의 범위를 이해하고 있으면, 개발사와의 소통이 훨씬 수월해집니다.
#로그인 #인증 #OAuth #보안