GODRICH
#css애니메이션#버튼#로딩#전환효과#스크롤#대시보드#실전ui#디자인트렌드

본인인증 화면 UI 9가지 — 그대로 복사하는 인증 카드

본인인증 화면은 사용자가 본인임을 증명하기 위해 권한 요청부터 인증번호 입력, 로그아웃 확인까지 거치는 일련의 UI 조각을 말합니다. 아래 아홉 개는 서버 쪽 인증 로직이 아니라 이 흐름에서 반복되는 화면 패턴만 애니메이션과 함께 코드로 옮긴 것입니다.

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

아홉 개는 인증을 처음 요청받는 순간부터 로그아웃하는 순간까지, 사용자가 실제로 거치는 순서대로 놓았습니다. 권한을 요청하고 사람인지 확인한 뒤 인증번호 입력 화면을 지나고, 받지 못하면 보안 질문이나 복구 수단으로 넘어갑니다. 인증이 끝나면 온보딩과 계정 연결로 이어지고, 세션이 끊기기 전 경고와 로그아웃 확인으로 마무리됩니다. 이 흐름 앞쪽의 첫 관문인 로그인·가입 카드는 로그인 화면 디자인 9가지에, 인증이 끝난 뒤의 설정 화면은 계정 보안 설정 UI 9종에 있습니다.

01권한 요청 모달

휴대폰 본인인증 화면에서 무엇이 보이나

카메라·위치 같은 접근 권한을 물을 때 아이콘이 통통 튀며 나타나고, 허용·거부 버튼 중 허용 쪽만 파란 배경으로 강조됩니다.

권한 요청 모달
새 창에서 열기

01 · 권한 요청 모달 — 측정 누적면적 26.15% · 움직인 프레임 18/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

@keyframes 아이콘 바운스포커스 트랩aria-modal

코드에서 볼 곳

본인인증 전 카메라·마이크 접근을 요청하는 화면이나 앱 첫 실행 시 알림 권한 안내에 씁니다.

(function(){
  var scene = document.querySelector('.perm-scene');
  var modal = scene.querySelector('.perm-scene__modal');
  var deny = scene.querySelector('.perm-scene__btn--ghost');
  var allow = scene.querySelector('.perm-scene__btn--primary');
  function close(){ scene.classList.remove('is-demo'); scene.classList.add('is-closed'); }
  deny.addEventListener('click', close);
  allow.addEventListener('click', close);
  modal.addEventListener('keydown', function(e){
    if (e.key === 'Escape') close();
  });
}());

02캡차 확인 체크박스

화면에서 일어나는 일

체크박스를 누르면 로딩 스피너가 잠깐 돌다가 초록 체크가 스프링처럼 튀어 오르고, 뒤이어 '확인됨' 배지가 떠오릅니다.

캡차 확인 체크박스
새 창에서 열기

02 · 캡차 확인 체크박스 — 측정 누적면적 13.285% · 움직인 프레임 14/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

@keyframes spin → scalerotate(560deg) 스피너:checked 상태

코드 읽는 법

인증번호 발송 전 사람인지 확인하는 단계나 무료 체험 신청 폼의 스팸 방지에 씁니다.

.captcha__input:checked ~ .captcha__box .captcha__check {
  opacity: 1;
  transform: scale(1);
  transition: opacity $dur-quick $ease-out, transform $dur-quick $ease-pop;
}
.captcha__input:checked ~ .captcha__box { border-color: $color; }
.captcha__input:focus-visible ~ .captcha__box { outline: 2px solid $color; outline-offset: 2px; }

@keyframes capt-check {
  0%, 28%   { opacity: 0; transform: scale(.4); }
  40%       { opacity: 1; transform: scale(1.15); }
  46%, 84%  { opacity: 1; transform: scale(1); }
  92%, 100% { opacity: 0; transform: scale(.4); }
}

03인증번호 입력 그리드

어떤 움직임인가

숫자 칸 6개가 나란히 있고, 한 칸에 입력하면 자동으로 다음 칸으로 넘어가며 칸마다 테두리가 파랗게 반짝입니다.

인증번호 입력 그리드
새 창에서 열기

03 · 인증번호 입력 그리드 — 측정 누적면적 15.469% · 움직인 프레임 12/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

input 이벤트 자동 이동paste 이벤트 6자리 분배inputmode=numeric

어디를 바꾸면 되나

문자로 받은 인증번호 6자리 입력창이나 2단계 인증 코드 입력 화면에 씁니다.

boxes.forEach(function(box, i){
  box.addEventListener('input', function(){
    box.value = box.value.replace(/[^0-9]/g, '').slice(0, 1);
    if (box.value && boxes[i + 1]) boxes[i + 1].focus({ preventScroll: true });
  });
  box.addEventListener('keydown', function(e){
    if (e.key === 'Backspace' && !box.value && boxes[i - 1]) boxes[i - 1].focus({ preventScroll: true });
  });
  box.addEventListener('paste', function(e){
    e.preventDefault();
    var digits = (e.clipboardData.getData('text') || '').replace(/[^0-9]/g, '').split('');
    boxes.forEach(function(b, j){ b.value = digits[j] || ''; });
    (boxes[Math.min(digits.length, boxes.length) - 1] || boxes[0]).focus({ preventScroll: true });
  });
});

04보안 질문 확인 카드

무엇이 보이나

미리 등록한 질문 하나가 카드에 뜨고, 답을 입력하면 입력칸 테두리가 빨강에서 베이지로 서서히 식듯이 바뀝니다.

보안 질문 확인 카드
새 창에서 열기

04 · 보안 질문 확인 카드 — 측정 누적면적 26.226% · 움직인 프레임 7/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

@keyframes sq-panelbox-shadow 글로우aria-label

코드에서 볼 곳

OTP 를 못 받을 때의 대체 인증 단계나 관리자 계정의 2차 확인 화면에 씁니다.

.sq-card__input:focus-visible { border-color: $subject-blue; box-shadow: 0 0 0 3px rgba($subject-blue, .25); }

@keyframes sq-panel {
  0%, 8%    { background: mix($color, white, 24%); box-shadow: 0 0 0 3px rgba($color, .3), 0 0 34px 18px rgba($color, .3), $shadow-raised; }
  23%, 77%  { background: $stage-paper; box-shadow: $shadow-raised; }
  92%, 100% { background: mix($color, white, 24%); box-shadow: 0 0 0 3px rgba($color, .3), 0 0 34px 18px rgba($color, .3), $shadow-raised; }
}
@keyframes sq-input {
  0%, 8%    { border-color: $color; box-shadow: 0 0 0 4px rgba($color, .22), 0 0 20px 10px rgba($color, .25); }
  23%, 77%  { border-color: #d8d2c4; box-shadow: none; }
  92%, 100% { border-color: $color; box-shadow: 0 0 0 4px rgba($color, .22), 0 0 20px 10px rgba($color, .25); }
}

05계정 복구 방법 선택 카드

화면에서 일어나는 일

이메일·문자·보안 질문 세 옵션이 라디오 카드로 나열되고, 고르면 선택된 카드만 배경이 어두운 화면 위에서 밝게 떠오릅니다.

계정 복구 방법 선택 카드
새 창에서 열기

05 · 계정 복구 방법 선택 카드 — 측정 누적면적 23.378% · 움직인 프레임 11/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

role=radiogroup:has(:checked)@keyframes picker-cycle

코드 읽는 법

비밀번호를 잊었을 때 복구 수단을 고르는 첫 화면이나 계정 잠금 해제 전 확인 단계에 씁니다.

.recovery__card:has(.recovery__input:checked) {
  background: mix($color, $stage-ink, 26%);
  border-color: $color;
  box-shadow: 0 0 0 3px rgba($color, .3), 0 0 24px 12px rgba($color, .28);
  transform: scale(1.04);
}
.recovery__card:has(.recovery__input:focus-visible) { outline: 2px solid $color; outline-offset: 2px; }

.recovery.is-demo .recovery__card { animation: picker-cycle $duration $easing infinite; }
.recovery.is-demo .recovery__card:nth-child(1) { animation-delay: 0ms; }
.recovery.is-demo .recovery__card:nth-child(2) { animation-delay: 660ms; }
.recovery.is-demo .recovery__card:nth-child(3) { animation-delay: 1320ms; }

06온보딩 위저드 스텝 카드

어떤 움직임인가

인증이 끝난 뒤 프로필을 채우는 카드로, 다음 버튼을 누르면 지금 카드가 왼쪽으로 밀려나며 다음 카드가 파스텔 색으로 슬라이드해 들어옵니다.

온보딩 위저드 스텝 카드
새 창에서 열기

06 · 온보딩 위저드 스텝 카드 — 측정 누적면적 20.839% · 움직인 프레임 10/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

transform: translateX 슬라이드커스텀 프로퍼티 --step@keyframes wiz-track

어디를 바꾸면 되나

회원가입 인증 직후 이어지는 프로필 설정 단계나 첫 실행 온보딩의 정보 입력 카드에 씁니다.

(function(){
  var track = document.querySelector('.wizard__track');
  var dots = Array.prototype.slice.call(document.querySelectorAll('.wizard__dot'));
  var btn = document.querySelector('.wizard__next');
  var step = 0;
  btn.addEventListener('click', function(){
    step = step === 0 ? 1 : 0;
    track.style.transform = 'translateX(' + (step === 0 ? '0%' : '-50%') + ')';
    dots.forEach(function(d, i){ d.classList.toggle('is-active', i === step); });
    btn.textContent = step === 0 ? 'Next' : 'Back';
  });
}());

07연결된 계정 목록

무엇이 보이나

구글·깃허브 같은 로그인 수단이 줄마다 나열되고, 연결 스위치를 누르면 토글이 미끄러지듯 넘어가며 줄 배경이 옅은 색으로 물듭니다.

연결된 계정 목록
새 창에서 열기

07 · 연결된 계정 목록 — 측정 누적면적 12.77% · 움직인 프레임 11/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

checkbox 기반 토글transform: translateXfocus-visible 링

코드에서 볼 곳

설정 화면의 소셜 로그인 연결 관리나 여러 계정을 하나로 묶는 통합 로그인 대시보드에 씁니다.

.accounts__input:checked ~ .accounts__track { background: $color; }
.accounts__input:checked ~ .accounts__track .accounts__knob { transform: translateX(14px); }
.accounts__input:focus-visible ~ .accounts__track { outline: 2px solid $color; outline-offset: 2px; }
.accounts__row:has(.accounts__input:checked) { background: rgba($subject-mint, .18); }

@keyframes acct-knob-cycle {
  0%, 8%    { transform: translateX(0); }
  14%, 36%  { transform: translateX(14px); }
  42%, 100% { transform: translateX(0); }
}

08세션 만료 임박 배너

화면에서 일어나는 일

화면 위쪽에서 배너가 미끄러져 내려오며 남은 시간이 초 단위로 줄어들고, 연장 버튼을 누르면 남은 시간이 다시 30초로 채워집니다.

세션 만료 임박 배너
새 창에서 열기

08 · 세션 만료 임박 배너 — 측정 누적면적 14.106% · 움직인 프레임 9/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

transform: translateY 슬라이드setInterval 카운트다운role=alert

코드 읽는 법

은행·결제 앱의 자동 로그아웃 전 경고나 관리자 콘솔의 유휴 세션 알림에 씁니다.

(function(){
  var cnt = document.querySelector('.timeout-banner__count'),
      btn = document.querySelector('.timeout-banner__extend'),
      n = 30,
      timer = setInterval(function(){
        n--; if (n < 0) n = 0; cnt.textContent = n;
      }, 1000);
  btn.addEventListener('click', function(){
    n = 30; cnt.textContent = n;
  });
}());

09로그아웃 확인 카드

어떤 움직임인가

확인 카드가 뜨는 순간 살짝 흔들려 눈길을 끌고, 정말 나가기를 누르면 카드 배경이 어두운 빨강으로 짙게 물듭니다.

로그아웃 확인 카드
새 창에서 열기

09 · 로그아웃 확인 카드 — 측정 누적면적 28.216% · 움직인 프레임 7/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

@keyframes shake커스텀 프로퍼티 --dangerfocus-visible 링

어디를 바꾸면 되나

실수로 누른 로그아웃을 한 번 더 확인하는 카드나 여러 기기 로그아웃을 경고하는 계정 설정 화면에 씁니다.

(function(){
  var card = document.querySelector('.signout-card'),
      cancel = card.querySelector('.signout-card__btn--cancel'),
      danger = card.querySelector('.signout-card__btn--danger');
  cancel.addEventListener('click', function(){ card.classList.remove('is-confirmed'); });
  danger.addEventListener('click', function(){ card.classList.add('is-confirmed'); });
}());

어디서 깨지는가 — 자동 포커스와 스크롤

인증번호 입력 그리드(03)를 만들면서 실제로 걸린 함정은 포커스 이동과 스크롤이었습니다. 칸을 채울 때마다 다음 인풋으로 .focus()를 그냥 호출했더니, 브라우저가 기본값으로 그 요소를 화면 안으로 밀어 넣으려 했습니다. 데스크톱 화면에서는 표가 안 났지만 모바일 폭 320px 로 좁혀 인풋이 스크롤 컨테이너 안에 들어가자, 한 자리씩 입력할 때마다 화면이 미세하게 들썩였습니다.

이 들썩임이 데모 하나로 끝나지 않는 이유가 있습니다. 이 사이트는 아홉 개 데모를 홈 목록과 글 안 격자에 iframe 으로 심어 두는데, iframe 안에서 .focus() 가 기본 동작대로 스크롤을 일으키면 그 스크롤이 부모 페이지까지 번집니다. 방문자가 목록을 위로 넘기고 있는데 격자 안 인증번호 칸이 포커스를 받을 때마다 페이지가 아래로 끌려 내려가는 식입니다. 아홉 개 가운데 스크립트에서 .focus() 를 부르는 것은 03 하나뿐이라 고칠 곳은 세 줄이었지만, 자동 이동·백스페이스 되돌아가기·붙여넣기 분배 세 경로가 전부 같은 호출을 지나므로 한 곳만 고치면 나머지 두 경로에서 다시 들썩입니다. { preventScroll: true } 옵션을 넣어 이미 보이는 요소 사이를 옮겨 다닐 때는 스크롤을 막았고, 이 처리는 위 03 코드와 압축 비밀번호 zkv9xjxt로 여는 zip 안 소스 아홉 개 전부에 그대로 들어 있습니다. 인증을 기다리는 동안 아무것도 없는 화면을 어떻게 채우는지는 빈 화면 디자인 9종에서 다뤘습니다. 이후로는 화면 안에서 다음 요소로 넘어가는 모든 .focus() 호출에 같은 옵션을 붙이는 게 이 사이트의 규칙이 됐습니다. 옵션 하나가 어떤 경우에 스크롤을 막는지는 MDN focus() preventScroll 문서에 정리돼 있습니다.

접근성

아홉 개 전부 prefers-reduced-motion: reduce에서 장식용 자동 재생을 끄되 실제 상태 값은 그대로 남깁니다. 권한 모달(01)은 role=alertdialog와 aria-modal로 보조기기에 모달임을 알리고 Esc 로 닫힙니다. 캡차(02)의 체크 배지와 인증번호 그리드(03)의 자동 이동은 reduced-motion 에서 튀는 움직임만 사라지고 체크 상태·inputmode=numeric 같은 실제 정보 전달은 남습니다. 보안 질문(04)은 aria-label로 입력칸의 용도를 알리고, 복구 카드(05)는 role=radiogroup과 라디오 버튼 상태로 지금 선택된 항목을 스크린 리더에 전달하며, 세션 배너(08)는 role=alert로 남은 시간이 줄어드는 것을 중간에 끼어들어서라도 읽어 줍니다.

본인인증 다음 단계로 이어지는 로그인·회원가입 카드도 같은 계정 흐름을 다루고, 이 사이트가 다루는 범위는 소개에 있습니다.

FAQ

otp 인증 구현처럼 실제 서버 인증(OTP 검증·세션 관리)까지 포함하나요?

아니요, 화면 UI 와 애니메이션 패턴만 담았습니다. 인증번호가 맞는지 확인하거나 세션 만료를 판단하는 서버 쪽 로직은 각자의 백엔드에 맞춰 직접 붙여야 합니다.

React 버전도 실제로 상태가 바뀌나요?

네, 상태가 있는 항목은 전부 useState로 들고 있습니다. 인증번호 그리드의 입력 값, 온보딩의 현재 단계, 로그아웃 카드의 확인 여부가 그렇고, 변수 몇 개는 props 로 받아 CSS 사용자 지정 속성으로 넘깁니다.

인증번호 입력 그리드는 실제 전화번호로 문자를 보내나요?

아니요, 칸에 숫자를 채우고 다음 칸으로 넘어가는 동작만 실제로 작동합니다. 문자 발송이나 서버 쪽 인증번호 검증은 포함돼 있지 않고, 화면에 보이는 자동 이동과 붙여넣기 분배 로직만 그대로 코드에 들어 있습니다. 칸 여섯 개의 상태만 아홉 가지로 파고든 편은 인증번호 입력 UI의 9가지 상황입니다.

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.