Skip to content

비밀번호 찾기 폼 (ForgotPasswordForm) ​

비밀번호를 잊은 사용자가 임시 비밀번호를 SMS로 받는 화면입니다. 옵션이 하나도 없는 컴포넌트라, 볼 것은 네 가지 상태와 notice 세 자리입니다.

Alpine 컴포넌트 · Web · 진행 중(WIP) · 문서 포맷 시범 적용

Interactive playground ​

옵션을 바꾸면 미리보기와 코드가 함께 바뀌어요.
미리보기 테마
비밀번호 찾기
SMS를 받지 못한 경우 관리자에게 문의해 주세요.
사번 또는 이메일을 잊으신 경우 관리자에게 문의해 주세요.
실제 forgot-password-form.html 마크업 그대로예요. 옵션이 하나도 없어서 코드는 늘 같습니다 — 아래 항목은 전부 서버·사용자가 만드는 상태입니다.
<!-- 옵션이 없다 — 제목·부제는 init() 이 로케일에서 채운다 -->
<div x-data="forgotPasswordForm">
  <!-- include "../../packages/components/html/forgot-password-form.html" -->
</div>

넘길 옵션이 하나도 없어서 코드는 늘 같습니다. 팩토리가 인자를 받지 않고 제목·부제도 init()이 로케일에서 채웁니다 — 그래서 아홉 앱의 호출이 글자 하나까지 같습니다.

규칙 (강제) · 코딩 에이전트용

ForgotPasswordForm 강제 규칙. 상세는 아래 본문.

마크업

  • 호스트가 x-data="forgotPasswordForm"으로 감싸고 packages/components/html/forgot-password-form.html을 include 합니다.
  • 옵션을 넘기지 마세요. 팩토리가 인자를 받지 않아 forgotPasswordForm({ … })는 조용히 무시됩니다.

⚠️ 트랩 ① 링크가 아니라 임시 비밀번호를 보냅니다 흔한 「재설정 링크 메일」 방식이 아닙니다. 서버가 임시 비밀번호를 만들어 SMS로 보내고, 사용자는 그것으로 로그인한 뒤 비밀번호 변경 화면으로 강제 이동합니다. 안내 문구를 링크 방식으로 고쳐 쓰지 마세요.

⚠️ 트랩 ② notice 가 세 자리에 서로 다른 변형으로 있습니다 맨 위 상시 안내는 --info, 버튼 위 오류는 --danger, 발송 뒤 완료는 --success입니다. 같은 박스처럼 보이지만 자리와 변형이 각각 정해져 있으니 하나로 합치지 마세요.

⚠️ 트랩 ③ 오류 문구는 서버 message 가 그대로 뜹니다err.message를 그대로 화면에 넣습니다. API에서 던지는 message는 한글 · 해요체여야 합니다.

필드 규격은 로그인과 같습니다 라벨 text-sm + mb-1.5(6px), 입력 ds-input--48(48px · 모서리 4 · 좌우 16 · 14px — 2026-10-01 게이트 화면 통일). 2026-09-08 에 네 인증 폼을 로그인 기준으로 통일했습니다 — 예전에 이 파일만 갖고 있던 손수 짠 유틸과 인라인 style="height: 40px" 는 그때 걷어냈습니다.

  • 다크 모드(2026-10-19) — 위 열쇠 아이콘 원은 bg-status-warning-bg(전 bg-yellow-50 고정 — 다크에서 밝은 노란 원으로 떠 보임)로, 글자 · 링크는 label 토큰으로 바꿨다. 바깥 상자는 로그인과 같다. 확인: Playground 「미리보기 테마 → 다크」.

무슨 흐름인가 ​

링크를 보내는 방식이 아닙니다. 임시 비밀번호를 SMS로 보냅니다.

  1. 사용자가 사번 또는 이메일을 넣고 「임시 비밀번호 발송」
  2. 서버가 임시 비밀번호를 만들어 등록된 휴대폰으로 SMS 발송
  3. 사용자가 그 비밀번호로 로그인
  4. 스토어의 requiresPasswordChange가 켜져 라우터가 비밀번호 변경으로 강제 이동

그래서 이 화면은 혼자 완결되지 않습니다. 4단계까지 가야 끝이고, 세 화면(찾기 → 로그인 → 변경)이 한 흐름입니다.

어디로 들어오나 ​

로그인 화면의 「비밀번호 찾기」 링크입니다. 그 링크는 서버가 이 기능을 켰을 때만 뜹니다(sdk.auth.passwordResetAvailable()) — 앱이 옵션으로 켜고 끄는 것이 아닙니다.

goBack()은 두 자리(폼 아래, 그리고 발송 완료 뒤)에서 /signin으로 돌려보냅니다.

notice 세 자리 ​

이 작은 폼에 notice가 세 개 있고, 자리와 변형이 각각 다릅니다.

자리변형언제
폼 맨 위--info항상 — 「사번 또는 이메일을 잊으신 경우 관리자에게 문의해 주세요」
입력과 버튼 사이--danger요청 실패
폼 대신--success발송 완료 (폼이 통째로 사라짐)

맨 위 안내는 오류가 아니라 상시 안내입니다. 오류가 났을 때 그 자리를 덮어쓰지 않고 아래에 따로 뜹니다.

상태 ​

상태화면
기본열쇠 배지 + 제목·부제 + 안내 notice + ID 입력 + 버튼 + 돌아가기
오류위와 같고 입력과 버튼 사이에 --danger notice
발송 중버튼 안 아이콘 스피너 + 「발송 중...」
발송 완료폼이 통째로 사라지고 --success notice + 「로그인으로 돌아가기」 버튼

발송 완료에 버튼이 남아 있는 것이 비밀번호 변경과 다른 점입니다. 변경 화면은 자동으로 홈으로 가지만, 여기는 사용자가 SMS를 확인한 뒤 스스로 로그인하러 가야 하므로 버튼을 남깁니다.

아이콘 배지 ​

상단의 노란 원형 배지(w-12 h-12 bg-yellow-50 rounded-full)에 열쇠(ic-key)가 들어갑니다. 비밀번호 변경은 **같은 배지에 자물쇠(ic-lock)**입니다 — 두 화면을 구분하는 유일한 시각 신호이므로 아이콘을 바꾸지 마세요.

검증 ​

공백을 뺀 ID가 비어 있는지 하나만 봅니다(auth.identifier_required). 형식(이메일인지 사번인지)은 서버가 판단합니다 — 화면에서 갈라내지 마세요.

버튼은 입력이 비면 죽습니다(:disabled="isLoading || !identifier.trim()"). 비밀번호 변경은 반대로 항상 눌리므로, 두 화면의 방식이 다릅니다.

호출 패턴 ​

html
<!-- 옵션이 없다 — 제목·부제는 init() 이 로케일에서 채운다 -->
<div x-data="forgotPasswordForm">
  <!-- include "../../packages/components/html/forgot-password-form.html" -->
</div>

아홉 앱의 호출이 글자 하나까지 같습니다.

함께 필요한 것 ​

  • 라우터 스토어 — goBack()이 navigate('/signin')을 부릅니다. 없으면 조용히 아무 데도 안 갑니다(옵셔널 체이닝이라 에러도 안 납니다).
  • hereby 스토어 — 문구(t())와 sdk.auth.requestPasswordReset(identifier).
  • /forgot-password 라우트 — 라우터의 인증 경로 목록(AUTH_ROUTES)에 있어야 로그인 전에 열립니다.
  • 아이콘 레지스트리 — ic-key · ic-spinner · ic-info-circle · ic-check-circle · ic-exclamation-circle.

API ​

forgotPasswordForm Alpine.data factory. 인자를 받지 않습니다.

노출하는 상태 ​

멤버설명
title · subtitleinit()이 로케일에서 채웁니다 (「비밀번호 찾기」 / 「SMS를 받지 못한 경우 관리자에게 문의해 주세요.」)
identifier사번 또는 이메일
error서버 오류 문구 (notice)
isLoading · success발송 중 · 발송 완료

메서드 ​

멤버설명
init()제목·부제를 로케일에서 채웁니다. 옵션 폴백이 아니라 항상 덮어씁니다
submit()공백 검사 후 sdk.auth.requestPasswordReset(identifier.trim()) → success = true
goBack()router.navigate('/signin')

init() 이 제목을 항상 덮어씁니다

ChangePasswordForm의 init()은 if (!this.title)로 비어 있을 때만 채우는데, 여기는 조건 없이 대입합니다. 그래서 옵션을 넘길 수 있게 바꾸더라도 init()을 함께 고치지 않으면 값이 덮입니다.

사용처 ​

아홉 앱이 같은 partial을 인라인합니다.

admin · drive · employee-app · general-affairs · hr-system · inventory · management-dashboard · module-manager · team-manager

ChangePasswordForm보다 한 곳 많습니다 — module-manager는 찾기 화면은 있는데 변경 화면이 없습니다.

단일 출처 ​

파트파일
Alpine 컴포넌트packages/components/src/components/ForgotPasswordForm.ts
HTML partialpackages/components/html/forgot-password-form.html
Alpine 등록forgotPasswordForm (plugin.ts)

검토 필요 사항 ​

  • module-manager 는 찾기 화면만 있고 변경 화면이 없습니다. 임시 비밀번호를 받아 로그인하면 강제 변경 상태가 되는데, 그 앱에는 갈 화면이 없습니다.
  • init() 이 제목·부제를 조건 없이 덮어씁니다. ChangePasswordForm은 비어 있을 때만 채웁니다. 두 컴포넌트가 서로 다른 방식이라 하나로 맞출지 결정이 필요합니다.

변경 이력 ​

날짜내용
2026-09-08ForgotPasswordForm 페이지 신규 작성 — Playground(상태 4종) + 모아보기 4상태