PatternsP4 본문

Login

패턴 정본 1호 — WDS 컴포넌트를 조합한 슬롯형 인증 표면. 이메일·비밀번호를 기본으로 패스키·SSO·소셜 로그인·MFA·복구·패스키 등록 권유를 정책에 따라 선택합니다.

마지막 업데이트 2026-07-02

이 패턴은 **화면(UI)**을 정의합니다. 버튼들을 실제 인증에 연결하는 법(패스키 등록→로그인 생애주기·웹 vs 네이티브 OS 호출·SSO·배선 레시피)은 인증 연동을 참고하세요.

AI / Consumer Implementation Contract#

AI 작업자와 소비 프로젝트는 아래 계약을 먼저 적용합니다. 세부 설명보다 이 섹션과 patterns/login.contract.json을 우선합니다. React 테스트에서는 @wds/ui-web/testingassertLoginPatternCompliance(preset, screen)으로 같은 계약을 검증합니다.

MUST#

  • 단일 패널 — 로그인은 LoginSurface 하나로 구성합니다. 전체 화면·모달·사이드 패널 어디에 넣어도 인증 표면은 하나입니다.
  • 최대 폭 390pxLoginSurface size="sm"을 사용합니다. 인증 표면 자체는 데스크탑에서도 390px를 넘지 않습니다.
  • 정렬 고정 — 브랜드·제목·설명은 중앙 정렬, 입력 라벨·필드·오류는 좌측 정렬입니다.
  • 입력 배선FormFieldInput을 조합해 보이는 라벨, htmlFor/id, aria-describedby, aria-invalid를 유지합니다.
  • 타이포 위계 — 제목, primary CTA, 방식 버튼은 medium; 설명·footer는 regular 기준입니다.
  • Primary CTA는 하나 — 한 로그인 표면 안의 채움 primary action은 1개만 둡니다.
  • 방식 버튼 위계 — passkey/SSO는 AuthMethodButton, Apple/Google/Microsoft는 ProviderButton으로 풀폭 동등 노출합니다.
  • 기업 로그인 구분 — 기업 로그인은 조직 SSO와 관리자 문의가 중심입니다. 공개 가입 권한은 제품/조직 관리자가 소유합니다.
  • 2FA/복구 분리 — 인증 앱 코드와 복구 코드는 첫 로그인 화면에 섞지 않고 별도 단계 표면으로 전환합니다.
  • 접근성LoginSurface는 이름 있는 region, 입력은 보이는 라벨, 오류는 live region, OTP는 OneTimeCodeInput, 식별자는 가능한 경우 autocomplete="username webauthn"을 사용합니다.

SHOULD#

  • 범용 로그인은 이메일·비밀번호를 기본값으로 두고 passkey와 provider 버튼을 같은 LoginMethods 그룹에 배치합니다.
  • 패스키 준비 프리셋은 이메일 자동완성(username webauthn)과 AuthMethodButton method="passkey"를 함께 제공합니다.
  • 인증 실패는 어느 필드가 틀렸는지 말하지 않는 제너릭 FormMessage variant="error"로 CTA 바로 위에 둡니다.
  • 계정 잠금은 FormMessage variant="warning" 또는 동등한 live status를 쓰고, 모든 CTA를 비활성화합니다.
  • 소비 프로젝트 테스트에는 아래 한 줄을 추가합니다.
assertLoginPatternCompliance('enterprise', screen);

NEVER#

  • 마케팅 split 패널, 제품 소개 카드, 캠페인 이미지 패널을 로그인 패턴 내부에 넣지 않습니다.
  • 한 화면에 primary CTA를 2개 이상 두지 않습니다.
  • passkey/SSO/provider 버튼을 작은 링크, tertiary action, 접힌 메뉴로 낮추지 않습니다.
  • 기업 로그인에 Apple/Google/Microsoft provider 버튼 또는 계정 만들기 CTA를 넣지 않습니다.
  • 기업 로그인 footer를 공개 가입으로 보내지 않습니다. footer는 관리자에게 문의입니다.
  • 2FA/복구 코드 입력을 이메일·비밀번호·provider 버튼과 같은 첫 로그인 화면에 섞지 않습니다.
  • placeholder를 라벨 대체로 쓰거나, 비밀번호 붙여넣기/비밀번호 매니저를 막지 않습니다.

프리셋별 구현 계약#

프리셋반드시 있어야 하는 요소절대 없어야 하는 요소
범용 로그인LoginSurface size="sm", 이메일/아이디 FormField, 비밀번호 FormField, primary 로그인 CTA 1개, 계정 만들기 footer마케팅 split 패널, 첫 화면의 OTP/복구 코드 입력, primary CTA 2개 이상
기업 로그인LoginSurface size="sm", AuthMethodButton method="sso", primary CTA 1개, 관리자에게 문의 footerApple/Google/Microsoft ProviderButton, 계정 만들기, 첫 화면의 OTP/복구 코드 입력
패스키 준비이메일/아이디 Input autoComplete="username webauthn", 비밀번호 필드, AuthMethodButton method="passkey", primary CTA 1개패스키를 보조 링크로 숨김, 첫 화면의 OTP/복구 코드 입력, primary CTA 2개 이상
2FAOneTimeCodeInput length={6}, 확인 CTA 1개, 복구 코드 사용 링크이메일 필드, 비밀번호 필드, passkey/SSO/provider 버튼, 계정 만들기
인증 실패이메일/비밀번호 필드, CTA 위 FormMessage variant="error", 제너릭 실패 문구, primary CTA 1개”이 이메일은 없음” 같은 계정 열거 문구, 첫 화면의 OTP/복구 코드 입력, 마케팅 split 패널
계정 잠금FormMessage variant="warning" 또는 live status, 비활성 CTA, 잠금/레이트리밋 안내활성 primary CTA, 활성 provider 버튼, 첫 화면의 OTP/복구 코드 입력

React 소비 레시피#

import {
  AuthMethodButton,
  Button,
  FormField,
  Input,
  LoginMethods,
  LoginSurface,
  TextLink,
} from '@wds/ui-web';

export function EnterpriseLoginRecipe() {
  return (
    <LoginSurface
      brand={<ProductLogo />}
      title="기업 로그인"
      subtitle="회사 계정으로 계속하세요."
      footer={<TextLink href="/help/access">관리자에게 문의</TextLink>}
      size="sm"
    >
      <LoginSurface.Body>
        <FormField label="회사 이메일">
          <Input type="email" autoComplete="username webauthn" />
        </FormField>
        <FormField label="비밀번호">
          <Input type="password" autoComplete="current-password" />
        </FormField>
        <LoginMethods>
          <AuthMethodButton method="sso" />
          <AuthMethodButton method="passkey" />
        </LoginMethods>
      </LoginSurface.Body>
      <LoginSurface.Actions>
        <Button type="submit" fullWidth>
          로그인
        </Button>
      </LoginSurface.Actions>
    </LoginSurface>
  );
}
import {
  AuthMethodButton,
  Button,
  FormField,
  Input,
  LoginMethods,
  LoginSurface,
  ProviderButton,
  TextLink,
} from '@wds/ui-web';

export function GenericLoginRecipe() {
  return (
    <LoginSurface
      brand={<ProductLogo />}
      title="로그인"
      subtitle="계정으로 계속하세요."
      footer={<TextLink href="/signup">계정 만들기</TextLink>}
      size="sm"
    >
      <LoginSurface.Body>
        <FormField label="이메일">
          <Input type="email" autoComplete="username webauthn" />
        </FormField>
        <FormField label="비밀번호">
          <Input type="password" autoComplete="current-password" />
        </FormField>
        <LoginMethods>
          <AuthMethodButton method="passkey" />
          <ProviderButton provider="apple" fullWidth />
          <ProviderButton provider="google" fullWidth />
          <ProviderButton provider="microsoft" fullWidth />
        </LoginMethods>
      </LoginSurface.Body>
      <LoginSurface.Actions>
        <Button type="submit" fullWidth>
          로그인
        </Button>
      </LoginSurface.Actions>
    </LoginSurface>
  );
}
import {
  Button,
  LoginSurface,
  OneTimeCodeInput,
  TextLink,
} from '@wds/ui-web';

export function TwoFactorRecipe() {
  return (
    <LoginSurface
      brand={<ProductLogo />}
      title="인증 앱 확인"
      subtitle="인증 앱의 6자리 코드를 입력하세요."
      footer={<TextLink href="/recovery-code">복구 코드 사용</TextLink>}
      size="sm"
    >
      <LoginSurface.Body>
        <OneTimeCodeInput length={6} name="otp" aria-label="인증 코드" />
      </LoginSurface.Body>
      <LoginSurface.Actions>
        <Button type="submit" fullWidth>
          확인
        </Button>
      </LoginSurface.Actions>
    </LoginSurface>
  );
}
import { render, screen } from '@testing-library/react';
import { assertLoginPatternCompliance } from '@wds/ui-web/testing';

render(<EnterpriseLoginRecipe />);
assertLoginPatternCompliance('enterprise', screen);

데모#

상단 컨트롤로 세 축을 살펴봅니다 — 프리셋(범용 · 패스키 준비 · 기업 로그인 · 소셜 로그인 · 고보안 · 재인증), 상태(라이브 · 로딩 · 인증 실패 · 계정 잠금 · 인증 앱 · 복구 코드 · 성공 · 패스키 등록), 옵션(Apple·Google·Microsoft 계정 연동 표시/숨김). 실제 인증은 없습니다(UI Reference). 라이브에서 빈 값 제출은 필드 오류를 보여주고, 유효 제출·패스키·SSO·Provider 버튼은 성공을 시뮬레이션합니다. 프리셋·상태처럼 선택지가 길거나 모바일 한 줄에서 넘칠 수 있는 데모 선택기는 SegmentedButton이 아니라 ChipGroup을 사용합니다. 짧고 고정된 2~4개 선택지만 한 줄에 안정적으로 들어갈 때 SegmentedButton을 씁니다.

조합형 로그인 — 하나의 표면에 인증 옵션을 정책별로 붙입니다

언제 로그인을 요구하나#

로그인 화면을 어떻게 만드느냐보다 언제 요구하느냐가 먼저입니다.

  • 게스트 우선 — 로그인을 가능한 한 늦춥니다. 둘러보기·열람으로 가치를 먼저 경험하게 하고, 계정이 꼭 필요한 동작(저장·공유·결제)에서만 요구합니다.
  • 가치 교환 — “가치를 주는 대가로만” 로그인을 요청합니다. 진입 즉시 벽을 세우지 않습니다.
  • 계정 최소화 — 핵심 기능이 계정을 요구하지 않으면 계정 생성을 강제하지 않습니다.
  • 삭제 진입점 의무 — 계정 생성을 돕는다면 삭제(비활성화 아님) 경로도 앱 안에서 제공해야 합니다.
  • provider 동등성 — 서드파티 로그인(Google·Microsoft 등)만으로 계정을 만드는 진입은 동급의 대안(Sign in with Apple·자체 계정)을 함께 둡니다.

이 패턴은 “로그인 표면”의 정본일 뿐, 로그인을 띄울지 말지는 제품이 위 정책으로 결정합니다.

구성#

로그인 화면의 정본은 단일 인증 표면입니다. 제품 브랜드·캠페인 메시지·마케팅 이미지는 페이지 배경이나 상위 레이아웃에서 처리하고, 패턴 자체는 인증에 필요한 입력과 선택지만 담습니다.

영역역할구성 요소
표면 헤더최소 브랜드 확인LoginSurface의 서비스 로고 슬롯 · 서비스명
인증 표면입력·방식 선택·2차 인증·복구LoginSurface · 이메일 · 비밀번호 · 대체 방식 · MFA · 복구 코드 · 패스키 등록 권유

데모의 로고는 실제 WDS·제품 로고가 아니라 서비스 로고 자리표시자입니다. 제품은 이 슬롯에 자사 심볼, 앱 아이콘, 조직 브랜드를 넣고, 패턴은 로고의 모양을 강제하지 않습니다.

아트디렉션 계약#

로그인 표면은 기능 옵션보다 먼저 읽는 순서와 시각 리듬이 고정되어야 합니다. 아래 계약을 깨면 구글·애플·마이크로소프트 계정 연동을 붙여도 화면이 불안정해집니다.

항목계약구현 기준
표면데스크탑과 모바일이 같은 단일 패널을 재사용패널 최대 폭 390px, 마케팅 split 패널 금지
정렬브랜드·제목·설명은 중앙, 입력 라벨·필드·오류는 좌측FormField는 입력 박스 기준 좌측 정렬
타이포로그인 표면 안의 텍스트는 과한 굵기를 피함제목·CTA·방식 버튼 medium, 보조 문장 regular
세로 리듬각 묶음은 spacing scale로만 떨어뜨림라벨→입력 space-2, 필드 간 space-4, 폼→구분선·Provider→footer space-5
CTA 위계강한 primary action은 한 화면에 하나로그인만 채움 CTA, 패스키·SSO·Provider는 동등한 방식 버튼
Provider 버튼Apple·Google·Microsoft는 브랜드 규칙을 보존하되 다른 방식과 같은 폭·높이ProviderButton은 full width, LoginMethods 안에서 같은 stack 간격
패스키낯선 3차 옵션으로 숨기지 않음이메일 필드 autocomplete="username webauthn" + 명시 버튼
2FA/복구첫 화면 로그인 방식과 2차 인증 fallback을 섞지 않음복구 코드는 MFA/복구 단계 안에서만 전환
터치 타깃작은 링크·아이콘도 최소 타깃/간격을 확보버튼은 WDS md 높이, 작은 보조 링크는 인접 타깃과 겹치지 않게 배치

이 계약은 레퍼런스 조사의 기본 산출물에도 포함됩니다. 새 로그인 변형을 제안할 때는 기능 흐름뿐 아니라 레이아웃·정렬·간격·여백·타이포·버튼 위계·반응형 검증을 함께 기록해야 합니다.

적응형 동작#

패턴은 같은 단일 패널을 재사용합니다. 상위 화면이 넓어져도 인증 표면 자체는 390px를 넘지 않고, 좁은 배치에서는 padding·OTP 칸·간격만 컨테이너 폭에 맞춰 축소합니다.

배치 폭레이아웃
넓은 페이지·모달·사이드 패널모바일과 같은 폭의 중앙 단일 인증 패널(최대 390px)
좁은 모바일 폭같은 단일 인증 패널, padding·OTP 칸·간격만 축소

입력·CTA의 터치 타깃은 유지하고, 동일 패턴을 모달·사이드 패널·전체 화면 어디에 배치해도 깨지지 않습니다. 인증 앱·복구 코드·패스키 등록 권유도 같은 단일 표면 안에서 단계 전환됩니다. 코드 입력은 모바일에서도 한 줄을 유지하되, 칸과 간격만 컨테이너 폭에 맞춰 축소합니다.

조합 모델#

로그인은 이제 “식별자 우선”과 “단일 화면” 중 하나를 고르는 문제가 아닙니다. 제품 정책에 따라 한 인증 표면의 슬롯을 켜고 끕니다. 기본값은 이메일·비밀번호를 바로 사용할 수 있는 표면이며, 패스키·SSO·소셜 로그인은 동등한 대체 방식으로 붙습니다. 인증 앱과 복구 코드는 1차 로그인 방식이 아니라 2차 인증/복구 단계입니다.

슬롯역할대표 컴포넌트
identity이메일·아이디 입력, 패스키 conditional UIInput, FormField
primaryCredentials비밀번호, 표시 토글, 로그인 유지, 비밀번호 찾기Input, Checkbox, TextLink
alternateMethods패스키·SSO·소셜·매직 링크LoginMethods, AuthMethodButton, ProviderButton
secondFactor인증 앱·보안 키·복구 코드OneTimeCodeInput, FormMessage
postAuthEnrollment패스키 등록 권유AuthMethodButton, Button

패스키 conditional UI — 패스키의 1차 발견 경로는 명시 버튼만이 아니라 이메일 필드 autofill입니다. 식별자 입력에 autocomplete="username webauthn"을 주면 브라우저가 사용 가능한 패스키를 자동완성 목록에 함께 띄웁니다. AuthMethodButton method="passkey"는 이 conditional UI를 보완·명시하는 경로로 둡니다.

프리셋#

프리셋은 별도 패턴이 아니라 옵션 묶음입니다. 제품팀은 하나를 시작점으로 삼고 필요한 슬롯만 조정합니다.

프리셋우선 노출보조/다음 단계
범용이메일·비밀번호패스키·Apple/Google/Microsoft 계정 연동(옵션)·계정 만들기
패스키 준비이메일·비밀번호 + 패스키 동등 노출로그인 후 패스키 등록 권유
기업 로그인조직 SSO비밀번호·패스키 폴백·관리자 문의
소셜 로그인Apple·Google·Microsoft자체 계정 로그인
고보안비밀번호 또는 SSO인증 앱·복구 코드(2FA 단계 안에서)
재인증비밀번호 또는 패스키필요 시 MFA, 가입 CTA 없음

로그인 프로세스#

범용 로그인과 기업 로그인은 같은 인증 표면을 쓰지만 계정 생성 권한이 다릅니다.

프로세스계정 생성1차 진입보조 경로계정 없음/접근 없음
범용사용자 셀프 가입 허용이메일·비밀번호 또는 패스키Apple/Google/Microsoft 계정 연동·비밀번호 찾기계정 만들기
기업사용자 셀프 가입 금지조직 SSO 또는 회사 이메일허용된 경우 비밀번호·패스키 폴백관리자에게 문의

기업 로그인은 공개 회원가입 플로우로 이어지면 안 됩니다. 계정은 조직 관리자가 초대·발급하거나 IdP/SCIM 같은 조직 프로비저닝으로 생성합니다. 사용자가 접근 권한이 없거나 조직 도메인이 매칭되지 않는 경우에는 가입 CTA 대신 관리자 문의, 헬프데스크, 접근 요청 흐름으로 보냅니다.

공통 로그인 진행#

단계사용자가 보는 것제품/백엔드 책임다음 분기
1. 식별자 입력이메일 또는 아이디 입력, 패스키 자동완성 제안식별자 형식 검증, 도메인·조직 정책 조회범용은 자체 계정/소셜, 기업은 SSO 라우팅 가능
2. 1차 인증비밀번호·패스키·Provider·SSO 중 허용된 방식비밀번호 검증, WebAuthn/OIDC/SAML/OAuth 검증, 레이트리밋성공, 실패, 잠금, 2FA 필요
3. 2차 확인인증 앱 6자리 코드 또는 복구 코드TOTP 검증, 이미 사용한 코드 재사용 차단, 실패 횟수 제한세션 발급 또는 로그인 화면 복귀
4. 세션 발급완료 상태, 필요한 경우 다음 화면 이동세션/토큰 발급, 새 기기·위치 알림, 기존 위험 세션 처리앱 진입, 패스키 등록 권유
5. 보안 후속 조치패스키 추가, 로그인 알림, 활성 세션 관리패스키 저장·대표 지정, 세션 회수, 비밀번호 변경/만료 정책보안 설정 또는 일반 사용 흐름

보조 프로세스#

흐름진입점진행완료/실패 처리
로그인로그인 CTA, 패스키, Apple/Google/Microsoft, 조직 SSO1차 인증 → 정책 확인 → 필요 시 2FA → 세션 발급실패 메시지는 제너릭, 반복 실패는 잠금·알림
아이디/이메일 찾기로그인 표면의 보조 링크 또는 고객지원사용자가 소유한 이메일·전화·조직 정보를 제출계정 존재 여부를 직접 노출하지 않고 안내 메일·지원 티켓으로 응답
비밀번호 찾기비밀번호 찾기복구 이메일/문자 요청 → 1회용 링크 또는 코드 → 새 비밀번호 설정링크는 짧은 만료와 1회 사용, 성공 후 기존 세션 회수
패스키 등록로그인 성공 직후 권유 또는 보안 설정OS/브라우저 생체 인증 → 공개키 저장 → 기기 이름·대표 여부 관리실패하면 비밀번호/SSO 폴백 유지, 등록된 패스키는 설정에서 삭제 가능
2FA 설정보안 설정 또는 조직 정책QR 스캔 → 인증 앱 6자리 코드 검증 → 복구 코드 발급 권장다음 비밀번호 로그인부터 2차 확인, 복구 코드는 인증 앱 분실 시 사용
계정 만들기범용 프리셋 하단 링크이메일/소셜로 신원 생성 → 약관·이메일 확인 → 비밀번호 또는 패스키 등록제품은 계정 삭제 경로도 함께 제공
관리자에게 문의기업 프리셋 하단 링크조직 도메인·초대 상태·권한 요청을 관리자/헬프데스크로 전달공개 가입으로 보내지 않고 초대, IdP, SCIM 프로비저닝으로 해결

어드민 콘솔 연결#

로그인 패턴은 어드민 콘솔의 보안 기능과 이어져야 합니다. 콘솔의 인증 & 접근 영역은 패스키·2FA·조직 SSO를 관리하고, 로그인 알림은 새 기기·비정상 위치·반복 실패를 알려줍니다. 비밀번호 영역은 변경, 만료 정책, 재설정 링크를 담당하며, 활성 세션은 현재 기기와 의심 세션 회수를 보여줍니다.

이 구조를 쓰면 로그인 화면은 얇게 유지하면서도, 계정 복구·패스키 등록·2FA 설정·세션 보안 같은 운영 프로세스는 어드민 콘솔 또는 계정 설정으로 자연스럽게 이어집니다.

변형#

같은 인증 표면을 맥락에 따라 배치합니다 — 컴포넌트·상태는 공유하고 주변 셸만 바꿉니다.

변형형태쓰는 곳
centered auth panel인증 표면 단독 중앙 카드(기본 데모)전용 로그인 페이지·미니멀 진입·임베드
step-up panel2차 인증·복구·등록을 위한 같은 표면의 단계 상태MFA·복구 코드·패스키 등록 권유
modal / embedded오버레이·인라인 패널에 표면만작업 중 재인증·세션 만료 복귀
enterprise SSO landingSSO를 우선 노출하고 비밀번호/패스키 폴백 제공도메인 페더레이션 조직 진입

modal·SSO landing은 같은 표면을 다른 셸에 끼워 재사용합니다 — 새 컴포넌트를 만들지 않습니다.

상태#

데모의 상태 컨트롤로 로딩·인증 실패·계정 잠금·MFA·복구·패스키 등록 권유를 강제 표시해 검수할 수 있습니다.

상태트리거표현
기본초기이메일·비밀번호·CTA·대체 방식 노출, 오류 없음
로딩제출 직후1차 CTA loading(Spinner·aria-busy)·중복 제출 차단
인라인 오류빈·형식 오류 필드해당 FormField에 오류 텍스트 + aria-invalid 보더
인증 실패자격 증명 불일치CTA 위 FormMessage(error) role="alert"제너릭 메시지(계정 열거 방지)
계정 잠금연속 실패(레이트리밋)CTA 위 FormMessage(warning) + 모든 CTA 비활성
인증 앱1차 인증 성공 후 MFA 필요6자리 OneTimeCodeInput + 복구 코드 경로
복구 코드인증 앱 사용 불가8자리 영숫자 OneTimeCodeInput + 인증 앱 복귀
패스키 등록 권유로그인 성공 후 패스키 미등록”패스키 추가” CTA + 나중에 하기
Caps Lock비밀번호 포커스 중 Caps Lock필드 하단 role="status" 라이브 경고
완료유효 제출 · 패스키 · SSO · Provider성공 마크 + 환영 메시지 + 다른 계정 전환

실제 인증 로직은 포함하지 않습니다 — 패턴은 UI Reference만 제공합니다(MASTER_PLAN Portal Boundary). 제품은 이 표면에 자사 인증 흐름을 연결합니다.

모션#

전환은 흐름을 보조할 뿐 주의를 끌지 않습니다 — spring 토큰을 그대로 소비합니다.

대상토큰동작
2차 인증·복구·등록 상태 진입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
인증 앱·복구 코드는 첫 화면의 로그인 방식 그룹에 섞지 않음1차 인증과 2차/복구 단계 구분
가입·문의 링크는 도메인 중립 문구 사용범용은 “계정 만들기”, 기업은 “관리자에게 문의”처럼 제품명이 들어가지 않는 링크
기업 로그인은 회원가입 링크 금지계정 생성은 조직 관리자·IdP·프로비저닝 시스템이 소유

외부 기준#

기준패턴 반영
web.dev — passkey form autofill기존 비밀번호 사용자를 유지하면서 이메일 필드의 username webauthn 자동완성으로 패스키를 제안
Google Identity — PasskeysWebAuthn challenge·공개키 검증·등록/인증 생애주기를 제품 배선 책임으로 분리
Google Identity — Branding guidelinesGoogle 버튼은 브랜드 외형을 보존하고 다른 로그인 방식과 같은 폭·위계로 배치
Apple — Sign in with Apple for the webApple 로그인은 별도 Provider 버튼으로 노출하고 제품이 서비스 ID·리디렉션을 배선
Microsoft identity platform — supported account typesMicrosoft 버튼은 계정 공급자 진입이고, 조직 SSO는 tenant/authority 정책으로 제품이 분리
Microsoft identity platform — OpenID ConnectMicrosoft OAuth/OIDC redirect·token·session 처리는 DS가 아니라 제품 인증 코드가 배선
Microsoft Fluent 2 — Button usage한 레이아웃의 primary action은 하나만 두고, 다수 보조 동작은 neutral/outline 위계로 정리
Microsoft Entra — SSPR비밀번호 찾기는 1회용 링크/코드, 짧은 만료, 재설정 후 세션 회수 흐름으로 정리
Microsoft Entra — Application provisioning기업 계정 생성은 공개 가입이 아니라 관리자 초대, IdP, SCIM 프로비저닝으로 처리
W3C WAI — Labeling Controls입력 목적을 보이는 라벨로 제공하고 label–control 연결을 유지
WCAG 2.2 — Target Size (Minimum)포인터 타깃은 최소 크기 또는 충분한 간격을 확보
WCAG 2.2 — Accessible Authentication비밀번호 매니저·붙여넣기·대체 인증 경로로 인지 부담을 줄임

접근성#

WCAG 2.2 AA를 기준으로, 로그인 특화 신규 기준(§3.3.8 Accessible Authentication)을 명시적으로 만족시킵니다.

계약구현
폼 영역LoginSurface 기본 role="region" + aria-label="로그인" 으로 랜드마크화
라벨 연결FormFieldlabel–입력을 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" 라이브 리전
비밀번호 토글aria-pressed + 상태별 aria-label(표시/숨기기), 기본 마스킹
코드 입력OneTimeCodeInput 그룹 라벨·자리별 라벨·autocomplete="one-time-code"
대비인증 표면은 semantic 토큰으로 AA 충족

사용 컴포넌트#

Input(leadingIcon·trailingIcon·invalid) · FormField(error) · Button(primary·secondary·ghost·fullWidth·loading) · Checkbox · TextLink · FormMessage(error·warning, Tier 2) · ChipGroup(긴 데모 선택 컨트롤) · LoginSurface · AuthMethodButton · LoginMethods · ProviderButton · OneTimeCodeInput. 전부 06 컴포넌트의 실물을 그대로 조합했습니다. LoginSurface는 패키지로 배포되는 표면 recipe이고, 프리셋·상태·실제 인증 프로세스는 제품이 조합합니다.

토큰#

패턴 전용 토큰은 두지 않고 semantic 토큰을 직접 소비합니다.

속성토큰
폼 표면color.surface-raised
표면 폭최대 390px
라운드/그림자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 실연동 — JWT 로그인 → Bearer /me 호출
흐름엔드포인트
로그인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/.envWDS_DEMO_ADMIN_PASSWORD. 자세한 API 계약은 BACKEND_GUIDE(P6 문서)를 참고하세요.