Login
패턴 정본 1호 — WDS 컴포넌트를 조합한 적응형 로그인. 단일 화면·식별자 우선 흐름과 비밀번호·패스키·SSO 방식을 한 표면에서 다루는 인증 패턴 패밀리.
마지막 업데이트 2026-06-13
데모#
상단 컨트롤로 두 축을 전환하며 살펴봅니다 — 흐름(식별자 우선 ↔ 단일 화면)과 상태(라이브 · 로딩 · 인증 실패 · 계정 잠금). 라이브에서 빈 값 제출은 인라인 필드 오류, 유효 제출은 완료로 진행하며 비밀번호 표시 토글·패스키·조직 SSO가 모두 동작합니다. 실제 인증은 없습니다(UI Reference).
로그인
이메일을 입력하면 사용 가능한 로그인 방식을 안내합니다.
계정이 없으신가요?워크스페이스 만들기
언제 로그인을 요구하나#
로그인 화면을 어떻게 만드느냐보다 언제 요구하느냐가 먼저입니다(Apple HIG — Managing accounts).
- 게스트 우선 — 로그인을 가능한 한 늦춥니다. 둘러보기·열람으로 가치를 먼저 경험하게 하고, 계정이 꼭 필요한 동작(저장·공유·결제)에서만 요구합니다.
- 가치 교환 — "가치를 주는 대가로만" 로그인을 요청합니다. 진입 즉시 벽을 세우지 않습니다.
- 계정 최소화 — 핵심 기능이 계정을 요구하지 않으면 계정 생성을 강제하지 않습니다.
- 삭제 진입점 의무 — 계정 생성을 돕는다면 삭제(비활성화 아님) 경로도 앱 안에서 제공해야 합니다.
- provider 동등성 — 서드파티 로그인(Google 등)만으로 계정을 만드는 진입은 동급의 대안(Sign in with Apple·자체 계정)을 함께 둡니다.
이 패턴은 "로그인 표면"의 정본일 뿐, 로그인을 띄울지 말지는 제품이 위 정책으로 결정합니다.
구성#
로그인 화면은 두 영역으로 나뉩니다.
| 영역 | 역할 | 구성 요소 |
|---|---|---|
| 브랜드 패널 | 제품 맥락·신뢰 형성 | WIZ 심볼 · 워드마크 · 헤드라인 · 핵심 가치 3종 · 보안 고지 |
| 폼 패널 | 인증 입력·방식 선택 | 이메일 · 비밀번호(표시 토글) · 로그인 유지 · 비밀번호 찾기 · 1차 CTA · 패스키 · 조직 SSO · 가입 안내 |
브랜드 패널 배경은 color.primary 파생 그라데이션과 심볼 색을 흩뿌린 저투명 블러로
깊이를 만듭니다 — 별도 이미지 자산 없이 양 테마에서 일관됩니다.
적응형 동작#
패턴은 컨테이너 쿼리로 자기 폭에 반응합니다(뷰포트가 아니라 배치된 영역 기준).
| 컨테이너 폭 | 레이아웃 |
|---|---|
≥ 600px | 좌 브랜드 패널 + 우 폼의 2열 split (1.05 : 0.95) |
< 600px | 브랜드는 심볼·워드마크 슬림 헤더로 축약, 폼이 단독 stack |
좁은 폭에서 헤드라인·가치 목록은 숨겨 세로 길이를 줄이고, 입력·CTA의 터치 타깃은 유지합니다. 동일 패턴을 모달·사이드 패널·전체 화면 어디에 배치해도 깨지지 않습니다.
흐름#
로그인은 단일 화면이 아니라 인증 방식 패밀리로 설계합니다. 식별자(이메일)를 먼저 받고 사용 가능한 방식으로 분기하면 SSO·패스키 라우팅과 비밀번호 없는 인증을 한 표면에서 수용합니다.
| 흐름 | 동작 | 쓰는 곳 |
|---|---|---|
| 단일 화면 | 이메일·비밀번호를 한 화면에 제시 | 방식이 고정된 단순 진입 |
| 식별자 우선 | 이메일 → "계속" → 방식(비밀번호·패스키·SSO) 분기 | 조직 SSO 라우팅·패스키 발견이 필요한 엔터프라이즈 |
| 패스키 | 식별자 필드 autocomplete="username webauthn" 조건부 자동완성 + "패스키로 로그인" 버튼 | 비밀번호 없는 1차 방식(권장) |
| 조직 SSO | "조직 SSO로 계속하기" — IdP 위임 | 도메인 페더레이션 사용자 |
| 서드파티 | Apple·Google 사인인(ProviderButton) | 소비자 제품의 소셜 로그인 |
식별자-우선에서 방식 단계로 넘어갈 때 포커스를 비밀번호로 이동하고, 다단계에서도
autocomplete="username" hidden 필드를 동반해 비밀번호 매니저 저장을 보존합니다.
매직 링크·OTP는 복구·온보딩용 보조 방식으로, 후속 단계에서 동일 패밀리에 편입합니다.
변형#
같은 인증 표면을 맥락에 따라 네 가지 레이아웃으로 배치합니다 — 컴포넌트·흐름·상태는 공유하고 틀만 바꿉니다.
| 변형 | 형태 | 쓰는 곳 |
|---|---|---|
| split 브랜드 패널 | 좌 브랜드 + 우 폼(기본 데모) | 전용 로그인 페이지·제품 첫인상 |
| centered card | 폼 단독 중앙 카드(브랜드는 슬림 헤더) | 좁은 폭·미니멀 진입·임베드 |
| modal / embedded | 오버레이·인라인 패널에 폼만 | 작업 중 재인증·세션 만료 복귀 |
| enterprise SSO landing | 식별자 우선 + SSO를 1차로 노출 | 도메인 페더레이션 조직 진입 |
split ↔ centered는 위 적응형 동작으로 자동 전환됩니다(컨테이너 < 600px이면 centered로 접힘).
modal·SSO landing은 같은 폼·흐름을 다른 셸에 끼워 재사용합니다 — 새 컴포넌트를 만들지 않습니다.
상태#
폼은 다음 표시 상태를 가집니다. 데모의 상태 컨트롤로 로딩·인증 실패·계정 잠금을 강제 표시해 검수할 수 있습니다.
| 상태 | 트리거 | 표현 |
|---|---|---|
| 기본 | 초기 | 입력·CTA 노출, 오류 없음 |
| 로딩 | 제출 직후 | 1차 CTA loading(Spinner·aria-busy)·중복 제출 차단 |
| 인라인 오류 | 빈·형식 오류 필드 | 해당 FormField에 오류 텍스트 + aria-invalid 보더 |
| 인증 실패 | 자격 증명 불일치 | CTA 위 FormMessage(error) role="alert" — 제너릭 메시지(계정 열거 방지) |
| 계정 잠금 | 연속 실패(레이트리밋) | CTA 위 FormMessage(warning) + 모든 CTA 비활성 |
| Caps Lock | 비밀번호 포커스 중 Caps Lock | 필드 하단 role="status" 라이브 경고 |
| 완료 | 유효 제출 · 패스키 · SSO | 성공 마크 + 환영 메시지 + 다른 계정 전환 |
실제 인증 로직은 포함하지 않습니다 — 패턴은 UI Reference만 제공합니다(MASTER_PLAN Portal Boundary). 제품은 이 표면에 자사 인증 흐름을 연결합니다.
모션#
전환은 흐름을 보조할 뿐 주의를 끌지 않습니다 — spring 토큰을 그대로 소비합니다.
| 대상 | 토큰 | 동작 |
|---|---|---|
| 방식 단계 진입(식별자 → 비밀번호) | spring.spatial.fast (256ms · ζ0.8) | 식별자 행이 약한 오버슈트로 슬라이드+페이드 |
| 폼 메시지 등장(인증 실패·계정 잠금) | spring.effect.default (188ms · ζ1.0) | 오버슈트 없이 페이드 인 |
transform·opacity만 애니메이트해 레이아웃을 흔들지 않습니다(컴포지터 친화).
prefers-reduced-motion: reduce면 모든 전환을 끄고 즉시 정적으로 표시합니다(WCAG 2.3.3).
콘텐츠·보안#
| 규칙 | 근거 |
|---|---|
| 실패 메시지는 제너릭 — 어느 필드가 틀렸는지 노출 금지 | 계정 열거 방지(OWASP Authentication) |
메시지 배치는 범위(scope) 기준 — 필드 오류는 인풋 아래(Tier 1), 제출 실패·잠금은 CTA 위 FormMessage(Tier 2) | 메시지 위계 |
| 라벨은 지시 아닌 명사(이메일·비밀번호), placeholder는 라벨 대체 금지 | 라벨 상시 가시성 |
| 1차 동사는 행동형(로그인·계속), 방식 버튼은 "패스키로 로그인"·"조직 SSO로 계속하기" | 동사 일관성 |
| 비밀번호는 붙여넣기·비밀번호 매니저 허용(차단 금지) | WCAG 2.2 §3.3.8 |
접근성#
WCAG 2.2 AA를 기준으로, 로그인 특화 신규 기준(§3.3.8 Accessible Authentication)을 명시적으로 만족시킵니다.
| 계약 | 구현 |
|---|---|
| 폼 영역 | section[aria-label="로그인"] 로 랜드마크화 |
| 라벨 연결 | FormField 가 label–입력을 htmlFor/id로 배선, 오류 시 aria-describedby/aria-invalid 자동 주입 |
| 입력 목적 (1.3.5) | 식별자 autocomplete="username webauthn"·비밀번호 current-password·inputmode 지정 |
| 인증 접근성 (§3.3.8) | 붙여넣기·비밀번호 매니저 허용 + 비(非)인지 경로(패스키·SSO) 제공 |
| 중복 입력 (3.3.7) | 식별자-우선 2단계에서 이메일을 재입력 없이 이월 |
| 상태 메시지 (4.1.3) | 폼 오류 FormMessage(error)=role="alert", Caps Lock=role="status" 라이브 리전 |
| 포커스 관리 | 방식 단계 진입 시 비밀번호로 포커스 이동, 포커스 링 상시 가시(2.4.7)·가림 없음(2.4.11) |
| 비밀번호 토글 | aria-pressed + 상태별 aria-label(표시/숨기기), 기본 마스킹 |
| 대비 | 브랜드 패널 흰 텍스트는 primary 위 AA 충족, 폼은 semantic 토큰 |
사용 컴포넌트#
Input(leadingIcon·trailingIcon·invalid) · FormField(error) · Button(primary·secondary·ghost·fullWidth·loading) ·
Checkbox · TextLink · FormMessage(error·warning, Tier 2) · SegmentedButton(데모 컨트롤). 전부
06 컴포넌트의 실물을 그대로 조합했습니다 — 패턴은 새 컴포넌트를 만들지 않습니다.
토큰#
패턴 전용 토큰은 두지 않고 semantic 토큰을 직접 소비합니다.
| 속성 | 토큰 |
|---|---|
| 폼 표면 | color.surface-raised |
| 브랜드 배경 | color.primary · color.primary-active 그라데이션 |
| 라운드/그림자 | radius.xl · shadow.lg |
| 구분선/보더 | color.border-subtle |
| 본문/보조 텍스트 | color.text · color.text-muted · color.text-subtle |
| 완료 마크 | color.success |
백엔드 연동 (P6)#
위 패턴 데모는 UI Reference라 실제 인증이 없습니다. P6 Backend Base의 core-api와
실제로 통신하는 데모는 아래에 있습니다 — 루트에서 make stack-up으로 로컬 풀스택을
먼저 띄워야 동작합니다(진입점 localhost:58080, Nginx 리버스 프록시).
아래 폼은 실제 core-api(http://localhost:58080)로 요청합니다. 시드 계정 비밀번호는 infra/docker/.env의 WDS_DEMO_ADMIN_PASSWORD 값입니다.
| 흐름 | 엔드포인트 |
|---|---|
| 로그인 | POST /api/v1/auth/login → access(15분) + refresh(14일, 회전) |
| 내 정보 | GET /api/v1/auth/me (Bearer) |
| 실패 | 401 ProblemDetail · 연속 실패 시 429(레이트리밋) |
시드 계정은 demo 프로파일이 만듭니다 — 이메일 admin@wiz-factory.com,
비밀번호는 infra/docker/.env의 WDS_DEMO_ADMIN_PASSWORD. 자세한 API 계약은
BACKEND_GUIDE(P6 문서)를 참고하세요.