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

비밀번호 강도 UI 9종 — 강도 값이 계산되는 아홉 가지 방식

비밀번호 강도는 입력한 값이 얼마나 뚫기 쉬운지를 점수나 막대로 보여주는 계산인데, 아홉 개 중 하나는 문자 종류 값이 몇 가지 섞였는지에 따라 강도 값이 정해지고, 하나는 흔한 값 목록과 같은 상태가 되면 강도 값이 깎이며, 또 하나는 확인 입력 값과 원래 값이 갈라지는 위치 값을

아홉 개 다 <input type="text">를 쓰면서 style.scss의 -webkit-text-security: disc 한 줄로 값을 점으로 가려, type 은 text 라 값을 그대로 읽어 계산하면서도 화면엔 비밀번호 입력창처럼 보이게 하는 CSS 마스킹입니다. 리스너는 아홉 개 전부 input 이벤트로 등록돼 있고(08 은 비밀번호·확인란 두 입력 모두에), 자동 재생 중이라도 직접 타이핑하는 순간 그 자리에서 루프가 멎고 손으로 넣은 값으로 같은 계산 함수가 다시 도는 실사용자 입력 방식입니다. 자동 루프가 도는 값도 무작위가 아니라 시드로 고정해 둔 배열(SEQ)을 순서대로 도는 결정적 시퀀스입니다.

아홉 개를 가르는 기준은 강도 값이 어디서 나오느냐입니다 — 문자 구성 값 하나를 계산해 그대로 강도로 쓰는 것(01·02·03), 기본 점수에서 특정 패턴을 찾아 값을 깎는 것(04·05), 여러 조건이나 두 입력을 비교해 값을 실시간으로 다시 매기는 것(06·07·08·09)으로 나눴습니다.

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

아래 실측 표를 먼저 보면, 비밀번호 강도 측정 화면 중에서도 02(문자 종류 수 채움)처럼 배지 네 개가 한꺼번에 켜지는 항목과 08(확인 값 실시간 대조)처럼 칩 한두 개만 조용히 늘어나는 항목이 격자 미리보기 단계에서부터 갈립니다.

아홉 개의 해부 — 어떤 값이 언제 바뀌는가

실측 한눈에

항목 초점 누적면적% 강도 평균 움직인 프레임
01 글자 수 경계 채움 길이 값 → stageOf() 재계산 3.32 103.7 15/23
02 문자 종류 수 채움 문자 종류 값 → countClasses() 재계산 9.39 119.4 22/23
03 엔트로피 비트 값 표시 비트 값 → bitsOf() 재계산 4.30 183.8 22/23
04 흔한 값 감점 목록 일치 값 → scoreOf() 재계산 3.39 105.5 21/23
05 반복·순차 값 감점 구간 위치 값 → flagPositions() 재계산 3.09 135.5 22/23
06 추정 시간 값 단위 전환 경우의 수 값 → guessesOf() 재계산 7.12 179.7 14/23
07 조건 충족 비율 실시간 반영 충족 개수 값 → paint() 재계산 4.90 136.7 20/23
08 확인 값 실시간 대조 일치 위치 값 → matchLenOf() 재계산 1.86 125.0 7/23
09 연속 색 값 전환 연속 값 → scoreT()·colorAt() 재계산 3.35 90.9 22/23

02 가 9.39%로 아홉 중 누적면적이 가장 커 배지 네 칸과 채움 막대가 한꺼번에 값을 다시 그린다는 뜻이고, 08 은 1.86%로 가장 작아 칩 한두 개가 좁게 늘어나는 값만 바뀝니다. 강도 평균은 03 이 183.8 로 가장 높아 비트 막대가 채워지는 자리와 배경의 픽셀 값 차이가 크고, 09 는 90.9 로 가장 낮아 색이 서서히 섞이는 보간이라 픽셀 값 차이가 완만합니다. 움직인 프레임은 02·03·05·09 가 22/23 으로 매 프레임 값을 다시 그려 타이핑하는 내내 살아 있고, 08 은 7/23 으로 가장 낮아 비밀번호 강도 테스트라기보다 문자 하나가 어긋나는 순간에만 값이 바뀌는 성격입니다.

값 하나를 계산해 그대로 강도로 쓰는 것 — 01·02·03

01 은 stageOf()가 입력 값의 글자 수가 정해둔 경계 값(4·8·12·16) 중 몇 개를 넘었는지 세어 그 개수를 그대로 강도 단계로 씁니다. 02 는 countClasses()가 소문자·대문자·숫자·기호 네 정규식 중 몇 개가 값에 걸리는지 세고, 그 개수를 전체 종류 수로 나눈 비율을 채움 값으로 씁니다. 03 은 bitsOf()가 문자 집합 크기 값과 글자 수 값을 곱해(정확히는 글자 수 × log2(문자 집합 크기)) 비트 값을 만들고, 이 비트 값이 막대 눈금(MAX_BITS)에 대한 비율로 그대로 채워집니다.

기본 점수에서 패턴을 찾아 값을 깎는 것 — 04·05

04 는 scoreOf()가 글자 수 기반 기본 점수를 먼저 구한 뒤, 입력 값이 미리 정해둔 흔한 값 목록(COMMON)과 완전히 같은 문자열이면 고정된 PENALTY(40)만큼 점수를 뺍니다. 05 는 flagPositions()가 같은 글자가 3번 이상 연속되거나 문자코드가 1씩 오르내리는 구간을 찾아 위치를 표시하고, 표시된 글자 수에 PER_CHAR_PENALTY(8)를 곱한 값을 점수에서 뺍니다.

여러 조건·두 입력을 비교해 값을 다시 매기는 것 — 06·07·08·09

06 은 guessesOf()가 문자 집합 크기의 글자 수 제곱으로 경우의 수를 구한 뒤 가정한 초당 시도 수(GUESSES_PER_SEC)로 나눠 초 값을 만들고, 이 값이 UNITS 배열의 경계를 지날 때마다 표시 단위가 초→분→시간→일→년→세기로 바뀝니다. 07 은 네 가지 CHECKS 조건이 참인지 매 입력마다 다시 검사해 참이 된 개수를 전체 조건 수로 나눈 비율을 강도 값으로 씁니다. 08 은 matchLenOf()가 비밀번호 값과 확인 값을 앞에서부터 한 글자씩 비교해 갈라지는 위치 값을 찾습니다. 09 는 scoreT()가 길이 비율과 문자 종류 비율을 절반씩 섞어 0~1 사이 연속 값을 만들고, 이 값을 colorAt()이 색 정지점(STOPS) 사이에서 보간해 막대 색으로 씁니다.

패턴별 코드

01글자 수 경계 채움 — 입력 값의 글자 수가 정해진 여러 경계 값을 넘을 때마다 강도 값이 다음 단계로 오르는 방식

입력된 값의 글자 수가 정해진 여러 경계 값을 넘을 때마다 강도 값이 다음 단계로 올라갑니다. 회원가입 폼의 기본 비밀번호 입력이나 최소 길이 기준을 안내하는 계정 설정에 맞는 형태입니다.

경계 배열을 넘은 횟수를 단계로 세는 stageOf() 와 paint()

  var THRESHOLDS = [4, 8, 12, 16];
  var STAGE_CLASS = ['pw--s0', 'pw--s1', 'pw--s2', 'pw--s3', 'pw--s4'];

  function stageOf(len) {
    var s = 0;
    for (var i = 0; i < THRESHOLDS.length; i++) { if (len >= THRESHOLDS[i]) s = i + 1; }
    return s;
  }

  function paint(value) {
    var len = value.length;
    var stage = stageOf(len);
    var ratio = stage / THRESHOLDS.length;
    fill.style.transform = 'scaleX(' + ratio + ')';
    STAGE_CLASS.forEach(function (c) { root.classList.remove(c); });
    root.classList.add(STAGE_CLASS[stage]);
    label.textContent = '경계 ' + stage + '/4';
    lenEl.textContent = String(len);
  }
글자 수 경계 채움 — 입력 값의 글자 수가 정해진 여러 경계 값을 넘을 때마다 강도 값이 다음 단계로 오르는 방식
새 창에서 열기

01 · 글자 수 경계 채움 — 입력 값의 글자 수가 정해진 여러 경계 값을 넘을 때마다 강도 값이 다음 단계로 오르는 방식 — 측정 누적면적 3.317% · 움직인 프레임 15/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

THRESHOLDS = [4,8,12,16]stageOf(len)STAGE_CLASS

02문자 종류 수 채움 — 입력 값 안에 섞인 서로 다른 문자 종류 값의 개수에 따라 강도 값이 정해지는 방식

입력 값 안에 서로 다른 문자 종류 값이 몇 가지 섞여 있는지에 따라 강도 값이 정해집니다. 비밀번호 강도 측정을 대소문자·숫자·기호 조합으로 요구하는 계정 보안 설정이나 사내 시스템 비밀번호 변경 폼에 어울립니다.

네 정규식으로 문자 종류를 세는 countClasses() 와 paint()

  var CLASSES = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/];

  function countClasses(value) {
    var n = 0;
    for (var i = 0; i < CLASSES.length; i++) {
      var hit = CLASSES[i].test(value);
      badges[i].classList.toggle('cc__badge--on', hit);
      if (hit) n++;
    }
    return n;
  }

  function paint(value) {
    var n = countClasses(value);
    fill.style.transform = 'scaleX(' + (n / CLASSES.length) + ')';
    label.textContent = '문자 종류 ' + n + '/4';
  }
문자 종류 수 채움 — 입력 값 안에 섞인 서로 다른 문자 종류 값의 개수에 따라 강도 값이 정해지는 방식
새 창에서 열기

02 · 문자 종류 수 채움 — 입력 값 안에 섞인 서로 다른 문자 종류 값의 개수에 따라 강도 값이 정해지는 방식 — 측정 누적면적 9.395% · 움직인 프레임 22/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

CLASSES 정규식 4종countClasses(value).cc__badge--on

03엔트로피 비트 값 표시 — 문자 집합 크기 값과 글자 수 값을 곱한 계산에서 강도 값이 나오는 방식

문자 집합의 크기 값과 글자 수 값을 곱한 계산에서 나온 비트 값이 클수록 강도 값이 높게 매겨집니다. 보안 수준을 수치로 확인시키는 개발자용 계정 설정이나 API 키 발급 화면의 비밀 값 강도 표시에 맞는 형태입니다.

문자 집합 크기와 로그로 비트 값을 만드는 charsetSize() 와 bitsOf()

  var MAX_BITS = 100; // 막대 눈금의 오른쪽 끝 — 이 값 이상은 꽉 찬 막대로 표시

  // 계산 겹: 섞인 문자 종류로 문자 집합 크기 값을 구하고, 글자 수 값을 곱한 뒤
  // 밑이 2인 로그를 취해 비트 값을 만든다 (엔트로피 = 글자 수 × log2(문자 집합 크기)).
  function charsetSize(value) {
    var size = 0;
    if (/[a-z]/.test(value)) size += 26;
    if (/[A-Z]/.test(value)) size += 26;
    if (/[0-9]/.test(value)) size += 10;
    if (/[^A-Za-z0-9]/.test(value)) size += 32;
    return size;
  }

  function bitsOf(value) {
    var size = charsetSize(value);
    if (!value.length || size === 0) return { bits: 0, size: size };
    return { bits: value.length * (Math.log(size) / Math.log(2)), size: size };
  }
엔트로피 비트 값 표시 — 문자 집합 크기 값과 글자 수 값을 곱한 계산에서 강도 값이 나오는 방식
새 창에서 열기

03 · 엔트로피 비트 값 표시 — 문자 집합 크기 값과 글자 수 값을 곱한 계산에서 강도 값이 나오는 방식 — 측정 누적면적 4.297% · 움직인 프레임 22/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

charsetSize(value)bitsOf(value)MAX_BITS = 100

04흔한 값 감점 — 입력 값이 미리 정해둔 흔한 값 목록과 같은 상태가 되면 강도 값이 깎이는 방식

입력 값이 미리 정해둔 흔한 값 목록과 같은 상태가 되면 강도 값에서 일정 값만큼 깎입니다. 쉬운 비밀번호 재사용을 막는 가입 폼이나 유출 이력이 흔한 값을 걸러내는 계정 설정에 맞는 형태입니다.

목록 10종과 감점 값을 곱해 점수를 깎는 scoreOf() 와 paint()

  var COMMON = ['password', '123456', 'qwerty', '111111', 'abc123',
                'letmein', 'iloveyou', 'admin', 'welcome', 'monkey'];
  var PENALTY = 40;

  function scoreOf(value) {
    var base = Math.min(100, value.length * 8);
    var hit = value.length > 0 && COMMON.indexOf(value.toLowerCase()) !== -1;
    var score = hit ? Math.max(0, base - PENALTY) : base;
    return { score: score, hit: hit };
  }

  function paint(value) {
    var r = scoreOf(value);
    fill.style.transform = 'scaleX(' + (r.score / 100) + ')';
    root.classList.toggle('cl--hit', r.hit);
    scoreEl.textContent = String(r.score);
    warn.style.opacity = r.hit ? '1' : '0';
    warn.style.transform = r.hit ? 'scale(1)' : 'scale(.7)';
  }

여기 COMMON 배열은 password·123456 같은 일반적인 패턴 10개를 담은 데모용 소형 목록일 뿐, 실제 유출 사고에서 나온 목록이 아닙니다.

흔한 값 감점 — 입력 값이 미리 정해둔 흔한 값 목록과 같은 상태가 되면 강도 값이 깎이는 방식
새 창에서 열기

04 · 흔한 값 감점 — 입력 값이 미리 정해둔 흔한 값 목록과 같은 상태가 되면 강도 값이 깎이는 방식 — 측정 누적면적 3.392% · 움직인 프레임 21/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

COMMON 배열 10종PENALTY = 40.cl--hit

05반복·순차 값 감점 — 같은 문자가 연속되거나 숫자 값이 순서대로 이어지는 구간만큼 강도 값이 줄어드는 방식

같은 문자가 연속되거나 숫자 값이 순서대로 이어지는 구간이 발견되면 그 길이 값만큼 강도 값이 줄어듭니다. 1234·aaaa 같은 값을 걸러내는 계정 보안 설정이나 패턴형 값을 감지하는 가입 폼에 어울립니다.

반복 구간과 순차 구간의 위치를 찾는 flagPositions()

  function flagPositions(value) {
    var n = value.length;
    var flag = new Array(n).fill(false);
    var i = 0;
    while (i < n) {
      var j = i;
      while (j + 1 < n && value.charCodeAt(j + 1) === value.charCodeAt(j)) j++;
      if (j - i + 1 >= 3) for (var k = i; k <= j; k++) flag[k] = true;
      i = j + 1;
    }
    i = 0;
    while (i < n - 1) {
      var dir = value.charCodeAt(i + 1) - value.charCodeAt(i);
      if (dir === 1 || dir === -1) {
        var j2 = i + 1;
        while (j2 + 1 < n && value.charCodeAt(j2 + 1) - value.charCodeAt(j2) === dir) j2++;
        if (j2 - i + 1 >= 3) for (var k2 = i; k2 <= j2; k2++) flag[k2] = true;
        i = j2;
      } else {
        i++;
      }
    }
    return flag;
  }
반복·순차 값 감점 — 같은 문자가 연속되거나 숫자 값이 순서대로 이어지는 구간만큼 강도 값이 줄어드는 방식
새 창에서 열기

05 · 반복·순차 값 감점 — 같은 문자가 연속되거나 숫자 값이 순서대로 이어지는 구간만큼 강도 값이 줄어드는 방식 — 측정 누적면적 3.086% · 움직인 프레임 22/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

flagPositions(value)PER_CHAR_PENALTY = 8.rs__chip--on

06추정 시간 값 단위 전환 — 계산된 비트 값이 경계 값을 지날 때마다 걸리는 시간을 나타내는 값의 단위가 바뀌는 방식

계산된 비트 값이 몇 개의 정해진 경계 값을 지날 때마다 걸리는 시간을 나타내는 값의 단위가 바뀌어 나타납니다. 보안 인식을 돕는 계정 설정 화면이나 개발자 대상 비밀 값 발급 도구에 맞는 형태입니다.

경우의 수를 초당 시도 수로 나눠 단위를 고르는 guessesOf() 와 UNITS

  function charsetSize(value) {
    var size = 0;
    if (/[a-z]/.test(value)) size += 26;
    if (/[A-Z]/.test(value)) size += 26;
    if (/[0-9]/.test(value)) size += 10;
    if (/[^A-Za-z0-9]/.test(value)) size += 32;
    return size;
  }

  // 계산 겹: 문자 집합 크기의 글자 수 제곱으로 경우의 수를 구하고, 가정 초당
  // 시도 수로 나눠 초 값을 만든 뒤, 자리 단위(초→분→시간→일→년→세기)로 변환한다.
  function guessesOf(value) {
    var size = charsetSize(value);
    if (!value.length || size === 0) return 0;
    return Math.pow(size, value.length);
  }

  var UNITS = [
    { at: 1, name: '초' },
    { at: 60, name: '분', div: 60 },
    { at: 3600, name: '시간', div: 3600 },
    { at: 86400, name: '일', div: 86400 },
    { at: 31536000, name: '년', div: 31536000 },
    { at: 31536000 * 100, name: '세기', div: 31536000 * 100 }
  ];

바로 위 GUESSES_PER_SEC(1e6)는 초당 시도 수를 상수로 가정한 환산일 뿐이라, 실제 하드웨어나 특정 유출 사건과는 무관합니다.

추정 시간 값 단위 전환 — 계산된 비트 값이 경계 값을 지날 때마다 걸리는 시간을 나타내는 값의 단위가 바뀌는 방식
새 창에서 열기

06 · 추정 시간 값 단위 전환 — 계산된 비트 값이 경계 값을 지날 때마다 걸리는 시간을 나타내는 값의 단위가 바뀌는 방식 — 측정 누적면적 7.119% · 움직인 프레임 14/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

GUESSES_PER_SEC = 1e6guessesOf(value)UNITS 단위 배열

07조건 충족 비율 실시간 반영 — 여러 조건 값이 참이 되는 개수 값의 비율이 강도 값으로 바뀌는 방식

여러 조건 값이 각각 참이 되는지 실시간으로 검사되고, 참이 된 조건 값의 개수가 전체 조건 수 값 대비 비율로 강도 값에 반영됩니다. 요구 조건을 항목별로 안내하는 가입 폼이나 사내 정책 조건을 나열하는 계정 설정에 맞는 형태입니다.

네 조건을 검사해 비율로 채우는 CHECKS 와 paint()

  var CHECKS = {
    len: function (v) { return v.length >= 8; },
    upper: function (v) { return /[A-Z]/.test(v); },
    digit: function (v) { return /[0-9]/.test(v); },
    special: function (v) { return /[^A-Za-z0-9]/.test(v); }
  };

  function paint(value) {
    var met = 0;
    items.forEach(function (li) {
      var key = li.getAttribute('data-key');
      var ok = CHECKS[key](value);
      li.classList.toggle('cx__item--on', ok);
      if (ok) met++;
    });
    fill.style.transform = 'scaleX(' + (met / items.length) + ')';
  }

비밀번호 강도 체크 조건은 이 데모에서 길이·대문자·숫자·기호 넷으로 고정해 뒀지만, 실무에서는 정책에 맞는 함수를 CHECKS 객체에 더 추가하면 됩니다.

조건 충족 비율 실시간 반영 — 여러 조건 값이 참이 되는 개수 값의 비율이 강도 값으로 바뀌는 방식
새 창에서 열기

07 · 조건 충족 비율 실시간 반영 — 여러 조건 값이 참이 되는 개수 값의 비율이 강도 값으로 바뀌는 방식 — 측정 누적면적 4.904% · 움직인 프레임 20/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

CHECKS 기준 4종.cx__item--onSVG path 체크 아이콘

08확인 값 실시간 대조 — 확인 입력 값과 원래 값이 갈라지는 위치 값을 실시간으로 찾는 방식

확인 입력 값과 원래 값을 앞에서부터 한 글자씩 비교해 서로 달라지는 위치 값을 실시간으로 찾아냅니다. 비밀번호 확인란이 있는 가입 폼이나 재설정 화면의 값 일치 확인에 어울립니다.

두 값을 앞에서부터 비교해 칩으로 그리는 matchLenOf() 호출부

  function paint() {
    var a = pw.value, b = confirm.value;
    var matchLen = matchLenOf(a, b);
    var full = b.length > 0 && a.length === b.length && matchLen === a.length;

    chipsBox.innerHTML = '';
    for (var i = 0; i < b.length; i++) {
      var chip = document.createElement('span');
      chip.className = 'md__chip ' + (i < matchLen ? 'md__chip--ok' : 'md__chip--bad');
      chipsBox.appendChild(chip);
    }

    if (b.length === 0) statusEl.textContent = '입력 대기';
    else if (full) statusEl.textContent = b.length + '자 전체 일치';
    else if (matchLen === b.length) statusEl.textContent = matchLen + '자까지 일치 · 계속 입력';
    else statusEl.textContent = matchLen + '자까지 일치 · ' + (matchLen + 1) + '번째에서 어긋남';

    root.classList.toggle('md--full', full);
  }
확인 값 실시간 대조 — 확인 입력 값과 원래 값이 갈라지는 위치 값을 실시간으로 찾는 방식
새 창에서 열기

08 · 확인 값 실시간 대조 — 확인 입력 값과 원래 값이 갈라지는 위치 값을 실시간으로 찾는 방식 — 측정 누적면적 1.86% · 움직인 프레임 7/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

matchLenOf(a, b).md__chip--bad.md--full

09연속 색 값 전환 — 강도 값이 단계로 끊기지 않고 연속된 값으로 바뀌며 색상 값도 함께 매끄럽게 이어지는 방식

강도 값이 정해진 몇 단계로 뚝뚝 끊기지 않고 연속된 값으로 바뀌면서 그 값에 대응하는 색상 값도 함께 매끄럽게 이어집니다. 세밀한 시각 피드백을 주는 계정 설정 화면이나 디자인이 강조된 가입 폼에 맞는 형태입니다.

색 정지점 사이를 보간하는 colorAt()

  var STOPS = [
    { t: 0,    rgb: [194, 54, 26] },
    { t: 0.5,  rgb: [255, 210, 63] },
    { t: 1,    rgb: [76, 212, 166] }
  ];

  function lerp(a, b, f) { return a + (b - a) * f; }

  function colorAt(t) {
    t = Math.max(0, Math.min(1, t));
    var lo = STOPS[0], hi = STOPS[STOPS.length - 1];
    for (var i = 0; i < STOPS.length - 1; i++) {
      if (t >= STOPS[i].t && t <= STOPS[i + 1].t) { lo = STOPS[i]; hi = STOPS[i + 1]; break; }
    }
    var span = hi.t - lo.t || 1;
    var f = (t - lo.t) / span;
    var r = Math.round(lerp(lo.rgb[0], hi.rgb[0], f));
    var g = Math.round(lerp(lo.rgb[1], hi.rgb[1], f));
    var b = Math.round(lerp(lo.rgb[2], hi.rgb[2], f));
    return 'rgb(' + r + ',' + g + ',' + b + ')';
  }

여기 들어가는 t 값은 길이 비율과 문자 종류 비율을 절반씩 섞는 scoreT()가 만들고, 그 값을 그대로 colorAt()에 넘겨 막대 색을 정합니다.

연속 색 값 전환 — 강도 값이 단계로 끊기지 않고 연속된 값으로 바뀌며 색상 값도 함께 매끄럽게 이어지는 방식
새 창에서 열기

09 · 연속 색 값 전환 — 강도 값이 단계로 끊기지 않고 연속된 값으로 바뀌며 색상 값도 함께 매끄럽게 이어지는 방식 — 측정 누적면적 3.354% · 움직인 프레임 22/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

STOPS 색 정지점 3개colorAt(t)scoreT(value)

값이 패턴을 바꾸는 곳 — 경계·비율·시간 값의 기준

같은 계산이라도 상수 값 하나만 옮기면 화면에서 받는 인상이 크게 갈립니다.

길이 경계 값의 위치 — 01 THRESHOLDS

01 의 THRESHOLDS([4, 8, 12, 16])는 글자 수가 몇 자를 넘을 때마다 단계를 올릴지 정하는 경계 값이라, 이 값을 [6, 10, 14, 18]로 올리면 같은 길이의 값도 한 단계 아래로 매겨져 더 긴 값을 요구하는 것처럼 보입니다.

흔한 값 감점의 크기 — 04 PENALTY

04 의 PENALTY(40)는 흔한 값 목록에 걸렸을 때 점수에서 빼는 값이라, 40 이면 흔한 값이어도 남는 점수가 있어 완전히 0까지는 안 깎이지만 70 으로 올리면 짧은 흔한 값 대부분이 0점까지 깎여 위험 신호가 훨씬 강하게 보입니다.

크랙 예상 시간의 기준 시도 속도 — 06 GUESSES_PER_SEC

06 의 GUESSES_PER_SEC(1e6)는 초당 시도 수를 가정하는 상수 값이라, 1e6 이면 같은 입력값의 추정 시간이 상대적으로 길게(년 단위) 나오고 1e9 로 올리면 같은 값이 훨씬 짧은 시간(초~일 단위)으로 나와 위협이 더 낙관적으로 보입니다 — 가정 상수라 실제 위협 모델이 아니라 표시 감각만 바뀝니다.

어디서 깨지는가 — 빠른 붙여넣기·긴 문자열·로케일별 문자 집합

값 계산 자체는 결정적이라 같은 값을 넣으면 매번 같은 결과가 나오지만, 값을 극단으로 밀어붙이는 순간 계산의 경계가 화면에 그대로 드러나는 자리가 셋 있습니다.

저장해 둔 값을 빠르게 붙여넣을 때 — 04

04 의 scoreOf()는 COMMON.indexOf(value.toLowerCase())로 입력 값이 목록과 완전히 같은 문자열인지만 봅니다. 관리자가 남겨 둔 password123 같은 값을 빠르게 붙여넣기(paste)해도 input 이벤트는 그대로 발생해 계산은 똑같이 돌지만, 123이 붙는 순간 목록의 어떤 항목과도 정확히 일치하지 않아 감점 없이 글자 수만큼 점수가 올라갑니다 — 흔한 값에 숫자 하나만 더해도 이 계산에서는 안전해 보입니다.

아주 긴 문자열을 넣을 때 — 06

06 의 guessesOf()는 Math.pow(size, value.length)로 경우의 수를 구하는데, 문자 집합 크기 값이 94(대소문자+숫자+기호)일 때 글자 수 값이 157자를 넘으면 이 거듭제곱이 부동소수점이 표현할 수 있는 한계를 넘어 Infinity가 됩니다. 84k56vcx 데모 입력창에 157자가 넘는 값을 직접 넣어 보면 이 한계가 그대로 재현됩니다 — formatTime()은 이 무한대 값을 그대로 받아 세기 단위로 나눠도 여전히 무한대라, 표시되는 값이 정상적인 시간 단위를 벗어나 버립니다.

로케일별 문자 집합을 하나로 뭉뚱그릴 때 — 03·06

03 과 06 이 함께 쓰는 charsetSize()는 소문자·대문자·숫자를 뺀 나머지를 전부 "기호"로 묶어 값이 하나라도 걸리면 32 만 더합니다. 그래서 한글이나 이모지처럼 실제 정보량이 훨씬 큰 문자를 입력해도, 이 함수는 영어 기호 한 글자를 넣었을 때와 같은 32를 더할 뿐입니다 — 로케일에 따라 실제 문자 집합 크기가 다른데도 계산은 이를 구분하지 못한 채 비트 값과 크랙 시간 값을 과소평가합니다.

변형 세 가지와 접근성

값 세 개만 옮겨 봐도 화면에서 받는 인상이 눈에 띄게 갈립니다.

이름 바뀐 값 느낌
흔한 값 감점의 크기 04 PENALTY 40 → 70 40 이면 흔한 값이어도 남는 점수가 있어 완전히 0까지는 안 깎이고, 70 이면 짧은 흔한 값 대부분이 0점까지 깎여 위험 신호가 훨씬 강하게 보인다
크랙 예상 시간의 기준 시도 속도 06 GUESSES_PER_SEC 1e6 → 1e9 1e6 이면 같은 입력값의 추정 시간이 상대적으로 길게(년 단위) 나오고, 1e9 면 같은 값이 훨씬 짧은 시간(초~일 단위)으로 나와 위협이 더 낙관적으로 보인다
길이 경계 값의 위치 01 THRESHOLDS [4,8,12,16] → [6,10,14,18] 경계값이 낮으면 짧은 값에서도 단계가 금방 올라 초록불이 쉽게 뜨고, 경계값을 높이면 같은 길이의 값도 한 단계 아래로 매겨져 더 긴 값을 요구하는 것처럼 보인다

접근성 쪽은 아홉 개가 전부 같은 값을 갖습니다. role이나 aria-live 같은 별도 속성은 붙어 있지 않고(체크리스트의 체크 아이콘·확인 대조의 체크 아이콘에 붙은 aria-hidden="true"만 장식용으로 있습니다), 강도 값은 화면 문구(textContent)로만 전달됩니다. 기기의 동작 줄임 설정이 켜져 있으면 아홉 개 데모 전부 setInterval 자동 재생 값부터 멎고, 각 항목 style.scss에 있는 prefers-reduced-motion 미디어 쿼리가 채움 막대·배지 트랜지션 값도 같이 꺼 버립니다. 다만 사람이 직접 입력창에 값을 넣는 input 이벤트는 reduce 값과 무관하게 그대로 살아 있어, 자동 재생만 꺼질 뿐 손으로 하는 비밀번호 강도 테스트 자체는 막히지 않습니다.

FAQ

비밀번호 강도 체크는 어떤 계산 값을 기준으로 이뤄지나요?

한 가지 정답은 없습니다. 이 아홉 개만 봐도 글자 수 경계(01)·문자 종류 개수(02)·엔트로피 비트(03)처럼 값 하나를 그대로 강도로 쓰는 방식과, 흔한 값 목록(04)·반복 패턴(05)처럼 기본 점수에서 값을 깎는 방식, 조건 충족 비율(07)이나 두 입력 비교(08)처럼 실시간으로 다시 계산하는 방식이 서로 다른 기준을 씁니다. 실무에서는 이 중 두세 가지를 섞어 쓰는 경우가 많습니다.

css 비밀번호 강도 표시를 코드 하나로 옮겨서 그대로 쓸 수 있나요?

네, 아홉 개 다 외부 라이브러리 없이 정규식과 계산 함수(stageOf·bitsOf·scoreOf 등)만으로 동작합니다. 값을 만드는 함수와 input 리스너 등록부만 그대로 옮기고, 클래스 이름이나 변수 이름만 프로젝트 사정에 맞게 바꾸면 됩니다.

확인 입력란 값이 원래 비밀번호와 일치하는지 실시간으로 어떻게 확인하나요?

08 처럼 두 입력 값을 앞에서부터 한 글자씩 비교해 처음 달라지는 위치 값을 찾고, 그 위치까지의 글자 수만큼만 "일치"로 표시하면 됩니다. 전체 길이가 같고 그 위치가 끝까지 이어지면 완전히 일치하는 값이라는 뜻입니다.

더 읽을거리

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

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