문제 정의
현재 로그인 화면은 이메일 입력 → 비밀번호 입력을 고정 전제로 동작한다.
하지만 accountLinking 구조에서는 소셜로만 가입한 사용자도 존재하며, 이들은 비밀번호가 없다.
이 상태에서 비밀번호 입력을 요구하면 로그인 성공 가능성이 없는 경로로 사용자를 보내게 된다.
왜 이게 실제 문제인가
입력 UI는 단순하지만, 실제 계정 상태는 분기형이다.
- 이메일+비밀번호 계정
- 소셜 only 계정
- 여러 provider가 링크된 계정 (google, reddit, kakao 등)
즉, 같은 이메일이어도 인증 경로가 다르다.
프론트가 이를 반영하지 않으면 사용자는 틀린 비밀번호를 반복 시도하고, 왜 실패하는지 이해하지 못한 채 이탈할 가능성이 커진다.
해결 목표
이번 개선의 목표는 세 가지다.
- 소셜 only 계정을 비밀번호 단계로 보내지 않는다.
- 사용자가 추가 선택(버튼 재클릭 등) 없이 가능한 경로로 자동 진행한다.
- 자동 진행이 실패해도 플로우를 깨지 않고, 현재 단계에서 실패 이유를 즉시 이해하게 한다.
해결 방식: Identifier-first
핵심 아이디어는 identifier-first(이메일 우선 판별)이다.
이메일을 입력받는 즉시, 다음 단계가 비밀번호인지 소셜인지 먼저 결정한다.
POST /api/auth/email-sign-in-intent body: { email }
백엔드는 다음 순서로 판단한다.
- user + account 조회
- 소셜 provider가 하나 이상이면 SOCIAL_REDIRECT 반환
- 여러 provider가 있으면 createdAt 기준 첫 번째 provider 선택
- 소셜이 없으면 PASSWORD 반환
클라이언트는 응답에 따라 분기한다.
if (intent.nextAction === "SOCIAL_REDIRECT") { signIn.social(...) } else { step = "password" }
UX 원칙과 변경점
초기에는 “자동 소셜 실패 시 비밀번호 단계로 강제 이동”도 검토했지만, 이 방식은 사용자 의도와 문맥을 잃게 만든다.
최종적으로는 현재 단계 유지 + 명확한 에러 노출로 정리했다.
- 실패 시 step=password 강제 fallback 금지
- 이메일 단계에서 바로 에러 표시
- 에러 위치는 이메일 필드 바로 아래
- Continue 버튼은 요청 중 스피너 표시 + disabled 처리
Google 연동에서의 현실 제약과 대응
소셜 자동 진입 시 Google 계정 선택 화면이 다시 뜰 수 있어, “이메일을 입력했는데 왜 또 선택해야 하지?”라는 피로가 생긴다. 이를 줄이기 위해 다음을 적용했다.
- socialProviders.google.prompt = "none"
- 자동 소셜 시도는 disableRedirect: true로 결과를 직접 확인
- 실패하면 /sign-in으로 복귀하고 이메일 단계 에러를 그대로 유지
최종 플로우
- 사용자가 이메일 입력
- 백엔드가 인증 경로 판별
- 소셜 계정이면 자동 소셜 시도
- 성공 시 즉시 인증 진행
- 실패 시 이메일 단계 유지 + 에러 표시
- 비밀번호 계정(또는 신규)만 비밀번호 단계 진입
이번 변경의 본질은 “입력 UI 중심”이 아니라 “계정 상태 중심”으로 로그인 흐름을 재설계한 것이다.
그 결과, 소셜 only 사용자를 잘못된 비밀번호 경로로 보내지 않고, 자동 분기와 명확한 실패 피드백을 통해 로그인 경험을 더 자연스럽고 덜 혼란스럽게 만들 수 있다.