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

웨이팅 화면 9가지 — 대기 순서와 예상 시간을 계산하는 순번판

웨이팅 화면은 호출 번호와 남은 인원, 예상 대기 시간을 숫자로 보여 주는 대기 안내 화면이다. 평균 처리 시간이 바뀌면 남은 대기 인원과 곱해진 값도 같이 바뀌어 예상 대기 시간이 숫자로 다시 뜨고, 목록은 빠진 자리만큼 위로 당겨지며 순서를 다시 그리고, 전체 인원 숫자는 그

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

웨이팅 화면 9가지 — 대기 순서와 예상 시간을 계산하는 순번판

아홉 개는 화면을 보는 사람이 던지는 질문 순서로 놓았다. 01 은 지금 몇 번인가, 02 는 몇 분 기다리나, 03 은 전체가 어디까지 왔나를 숫자와 링으로 답하고, 04 는 내 앞 번호가 빠질 때 목록이 어떻게 당겨지는지, 05 는 불렀는데 오지 않으면 언제 다시 부르는지를 보여 준다. 06 은 창구가 여럿일 때 어디가 빠른지, 07 은 전체 인원이 늘고 주는 폭, 08 은 우선 대상이 끼어드는 자리를 계산하고, 09 는 그중 세 계산을 한 화면에 겹친다. 대기열 시스템 화면을 처음 만드는 사람이라면 01·02·07 세 개만 먼저 붙여도 번호와 시간과 인원이 한 화면에서 서로 맞물려 움직인다.

패턴별 코드

아홉 항목 각각 값을 계산하는 핵심 구간만 발췌했고, 나머지 마크업과 스타일은 데모 아래 압축 파일 안에 통째로 들어 있다. 번호와 비고 문구는 전부 데모용 가짜 값이고 실제 서버와는 통신하지 않는다.

01남은 인원 수를 다시 계산해 자릿수가 굴러가는 호출 번호

새 번호를 호출하면 rollDigits 가 호출 번호 세 자리와 남은 인원 두 자리를 자릿수마다 DIGIT_STAGGER_MS 씩 늦게 출발시켜 슬롯머신처럼 차례로 굴리고, 남은 인원은 전체 42명에서 호출한 횟수를 뺀 값으로 다시 계산된다. 병원 진료 대기 화면의 호출 번호판처럼 번호가 바뀌는 순간을 눈이 놓치면 안 되는 자리에 쓴다. 01 항목은 누적면적 16.7%·강도 평균 80.6·23프레임 중 10프레임에서 움직였다.

남은 인원 수를 다시 계산해 자릿수가 굴러가는 호출 번호
새 창에서 열기

01 · 남은 인원 수를 다시 계산해 자릿수가 굴러가는 호출 번호 — 측정 누적면적 16.738% · 움직인 프레임 10/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

CALL_NUMBER_ROLL_MSrollDigitsDIGIT_STAGGER_MS
  function rollDigits(digits, num, nowMs) {
    if (reduce) { setDigitsImmediate(digits, num); return; }
    flash(callOdo.parentNode);
    var s = String(num);
    while (s.length < digits.length) s = '0' + s;
    for (var i = 0; i < digits.length; i += 1) {
      var v = Number(s[i]);
      if (v === digits[i].to) continue;
      digits[i].from = digits[i].value;
      digits[i].to = v;
      digits[i].start = nowMs + i * DIGIT_STAGGER_MS;
      digits[i].dur = CALL_NUMBER_ROLL_MS;
    }
  }

02평균 처리 시간과 인원 수를 곱해 다시 계산하는 예상 대기 시간

처리 기록의 평균(avgMinutes)에 남은 인원을 곱한 값이 예상 대기 분이고, 처리 버튼을 누를 때마다 이 숫자가 ETA_TWEEN_MS 동안 굴러 새 값에 닿으며 값이 크게 바뀔수록 뒤의 빛이 더 세게 켜진다. 음식점 웨이팅 등록 화면처럼 손님이 몇 분을 기다려야 하는지만 보는 자리에 쓴다. 02 항목은 누적면적 12.8%·강도 평균 70.4·23프레임 중 7프레임에서 움직였다.

평균 처리 시간과 인원 수를 곱해 다시 계산하는 예상 대기 시간
새 창에서 열기

02 · 평균 처리 시간과 인원 수를 곱해 다시 계산하는 예상 대기 시간 — 측정 누적면적 12.84% · 움직인 프레임 7/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

ETA_TWEEN_MSavgMinutesSERVICE_LOG_MAX
  function avgMinutes() {
    var sum = 0;
    for (var i = 0; i < serviceLog.length; i += 1) sum += serviceLog[i];
    return sum / serviceLog.length;
  }

  function targetEta() {
    return avgMinutes() * remain;
  }

  function paintGlow(ratio) {
    glowEl.style.opacity = (0.5 * ratio).toFixed(3);
    glowEl.style.transform = 'translate(-50%, -50%) scale(' + (0.85 + ratio * 0.5).toFixed(3) + ')';
  }

03완료 비율을 계산해 채워지는 대기열 진행 링

처리 버튼을 한 번 누르면 1명에서 3명이 한꺼번에 처리된 것으로 치고, 처리 인원을 전체 30명으로 나눈 비율만큼 paintRing 이 strokeDashoffset 을 줄여 링을 채우며 3분의 1 지점과 3분의 2 지점에서 링 색을 한 단계씩 갈아 끼운다. 대기열 전체가 어디까지 왔는지 한눈에 요약하는 판에 쓴다. 03 항목은 누적면적 15.8%·강도 평균 58.1·23프레임 중 11프레임에서 움직였다.

완료 비율을 계산해 채워지는 대기열 진행 링
새 창에서 열기

03 · 완료 비율을 계산해 채워지는 대기열 진행 링 — 측정 누적면적 15.848% · 움직인 프레임 11/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

RING_FILL_DURATION_MSCIRCUMFERENCEtweenRing
  function stageOf(ratio) {
    if (ratio >= 2 / 3) return 'is-late';
    if (ratio >= 1 / 3) return 'is-mid';
    return 'is-early';
  }

  function paintRing(ratio) {
    var offset = CIRCUMFERENCE * (1 - ratio);
    fillEl.style.strokeDashoffset = offset.toFixed(2);
    fillEl.classList.remove('is-early', 'is-mid', 'is-late');
    fillEl.classList.add(stageOf(ratio));
    pctEl.textContent = Math.round(ratio * 100) + '%';
  }

04이동 거리를 계산해 스프링으로 자리 잡는 대기 목록

맨 앞 줄을 호출하면 그 줄은 220ms 동안 높이가 0 으로 접히고, 아래 줄들은 접힌 줄 높이에 8px 을 더한 거리(gap)만큼 움직였다가 linear() 곡선을 따라 제자리로 돌아와 출렁이며 자리를 잡는다. 새 맨 앞 줄에는 is-next 가 붙어 살짝 커지므로, 여러 사람이 한 화면을 같이 보는 순번 목록에서 다음 차례를 알릴 때 쓴다. 04 항목은 누적면적 32.8%·강도 평균 82.8·23프레임 중 14프레임에서 움직였다.

이동 거리를 계산해 스프링으로 자리 잡는 대기 목록
새 창에서 열기

04 · 이동 거리를 계산해 스프링으로 자리 잡는 대기 목록 — 측정 누적면적 32.848% · 움직인 프레임 14/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

LIST_SHIFT_SPRING_STIFFNESSshiftDurationSPRING_SETTLE_EASING
      row.animate(
        [{ height: h + 'px', opacity: 1 }, { height: '0px', opacity: 0 }],
        { duration: 220, easing: 'ease-in', fill: 'forwards' }
      ).onfinish = function () { row.style.display = 'none'; };

      var dur = shiftDuration(gap);
      below.forEach(function (el, i) {
        el.animate(
          [
            { transform: 'translateY(0px)' },
            { transform: 'translateY(' + gap + 'px)' },
            { transform: 'translateY(0px)' }
          ],
          { duration: dur, delay: 220, easing: SPRING_SETTLE_EASING, fill: 'both' }
        );
      });

05재호출까지 남은 시간을 게이지로 줄여가는 노쇼 카운트다운

번호가 호출된 시각을 기억해 두고 경과 시간을 RECALL_COUNTDOWN_SEC(60초)로 나눈 남은 비율만큼 게이지 폭을 줄이며, 남은 비율이 34% 이하면 경고 색이 켜지고 0 이 되면 만료 색과 함께 SHAKE_DURATION_MS 동안 좌우로 한 번 떨린다. 호출했는데 대답이 없는 번호를 다시 부를 때가 됐다고 알리는 자리에 쓴다. 05 항목은 누적면적 6.2%·강도 평균 125.1·23프레임 중 23프레임에서 움직였다.

재호출까지 남은 시간을 게이지로 줄여가는 노쇼 카운트다운
새 창에서 열기

05 · 재호출까지 남은 시간을 게이지로 줄여가는 노쇼 카운트다운 — 측정 누적면적 6.165% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

RECALL_COUNTDOWN_SECstepGaugeSHAKE_DURATION_MS
  function stepGauge(nowMs) {
    var elapsedS = (nowMs - callStartMs) / 1000;
    var ratio = Math.max(0, 1 - elapsedS / RECALL_COUNTDOWN_SEC);
    paintGauge(ratio);
    var remainSec = Math.max(0, Math.ceil(RECALL_COUNTDOWN_SEC - elapsedS));
    secEl.textContent = remainSec + '초 남음';
    if (ratio <= 0 && !expired) {
      expired = true;
      badgeEl.textContent = '만료 · 재호출 필요';
      badgeEl.classList.add('is-expired');
      status.textContent = ticketNum() + '번 시간 만료 · 재호출이 필요합니다.';
      if (!shaken) { shaken = true; shake(); }
    }
  }

06창구별 처리 속도를 계산해 분산 배치하는 다중 카운터 현황

창구마다 쌓은 처리 기록의 평균을 구해 막대 길이를 정하는데, 가장 느린 창구가 30% 폭이 되고 평균이 짧을수록 길어지며, 평균이 가장 짧은 창구는 fastestId 로 뽑혀 은은한 빛이 켜진다. 창구가 나란히 여러 개 열려 있는 은행이나 관공서 현황판에 쓴다. 06 항목은 누적면적 2.7%·강도 평균 97.2·23프레임 중 7프레임에서 움직였다.

창구별 처리 속도를 계산해 분산 배치하는 다중 카운터 현황
새 창에서 열기

06 · 창구별 처리 속도를 계산해 분산 배치하는 다중 카운터 현황 — 측정 누적면적 2.726% · 움직인 프레임 7/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

BAR_TWEEN_MSfastestIdrecalcAll
  function avgMinutes(c) {
    if (c.log.length === 0) return 0;
    var sum = 0;
    for (var i = 0; i < c.log.length; i += 1) sum += c.log[i];
    return sum / c.log.length;
  }

  function maxAvg() {
    var m = 0.0001;
    COUNTERS.forEach(function (c) { m = Math.max(m, avgMinutes(c)); });
    return m;
  }

  function fastestId() {
    var best = COUNTERS[0];
    COUNTERS.forEach(function (c) { if (avgMinutes(c) < avgMinutes(best)) best = c; });
    return best.id;
  }

07차이만큼 굴러가는 전체 대기 인원 카운터

사람이 들어오거나 빠지면 applyDelta 가 전체 인원에 차이를 더해 0 에서 99 사이로 자르고, 두 자리 숫자가 COUNT_ROLL_MS 동안 굴러 새 값에 닿으며 늘 때와 줄 때 다른 색이 FLASH_MS 만큼 켜진다. 입구나 접수대 위에 걸어 두는 전체 대기 인원 표시판에 쓴다. 07 항목은 누적면적 12.8%·강도 평균 63.4·23프레임 중 14프레임에서 움직였다.

차이만큼 굴러가는 전체 대기 인원 카운터
새 창에서 열기

07 · 차이만큼 굴러가는 전체 대기 인원 카운터 — 측정 누적면적 12.766% · 움직인 프레임 14/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

COUNT_ROLL_MSapplyDeltaFLASH_MS
  function applyDelta(delta, nowMs) {
    if (delta === 0) { paintStatus(0); return; }
    var next = Math.max(0, Math.min(99, TOTAL + delta));
    var actualDelta = next - TOTAL;
    TOTAL = next;
    rollDigits(TOTAL, nowMs);
    board.classList.remove('is-up', 'is-down');
    if (actualDelta > 0) board.classList.add('is-up');
    else if (actualDelta < 0) board.classList.add('is-down');
    flashUntil = nowMs + FLASH_MS;
    paintStatus(actualDelta);
  }

08우선순위 차이를 계산해 끼어드는 우선 호출 줄

우선 접수가 들어오면 priorityInsertIndex 가 기존 줄의 가중치를 위에서부터 훑으며 새 가중치 10 이상인 줄이 이어지는 동안만 내려가 그 바로 뒤를 자리로 정하고, 그 자리에 우선 배지가 달린 줄이 나타나며 아래 줄들은 이동 전후 위치 차이만큼 스프링으로 밀려 내려간다. 우선 호출 대상 표시처럼 일반 순번 사이에 끼워 넣을 줄이 있는 대기 목록에 쓴다. 08 항목은 누적면적 27.4%·강도 평균 82.1·23프레임 중 10프레임에서 움직였다.

우선순위 차이를 계산해 끼어드는 우선 호출 줄
새 창에서 열기

08 · 우선순위 차이를 계산해 끼어드는 우선 호출 줄 — 측정 누적면적 27.413% · 움직인 프레임 10/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

PRIORITY_SHIFT_SPRING_STIFFNESSpriorityInsertIndexinsertPriority
  function priorityInsertIndex(newWeight) {
    var idx = 0;
    for (var i = 0; i < ORDER.length; i += 1) {
      if (ORDER[i].weight >= newWeight) idx = i + 1; else break;
    }
    return idx;
  }

  function paintStatus(row, idx) {
    status.textContent = row.ticket + ' 우선 접수 · ' + (idx + 1) + '번째 자리로 끼어들었습니다.';
  }

09여러 계산을 한 화면에 모은 대기 순번판 히어로 장면

호출 버튼 하나가 01 의 번호 굴림과 04 의 줄 접힘·스프링과 07 의 인원 카운터를 한 번의 호출 함수 안에서 함께 출발시키고, 접수 버튼은 새 줄을 맨 아래에 붙이면서 인원 숫자만 굴린다. 매장 입구 대기 순번 화면처럼 번호와 목록과 인원이 한꺼번에 움직여야 하는 완성 장면에 쓴다. 09 항목은 누적면적 26.1%·강도 평균 59.5·23프레임 중 18프레임에서 움직였다.

여러 계산을 한 화면에 모은 대기 순번판 히어로 장면
새 창에서 열기

09 · 여러 계산을 한 화면에 모은 대기 순번판 히어로 장면 — 측정 누적면적 26.094% · 움직인 프레임 18/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

HERO_LIST_SPRING_STIFFNESSHERO_CALL_ROLL_MSflashTotal
  function joinQueue(nowMs) {
    var ticket = nextTicket;
    nextTicket += 1;
    var id = 'j' + ticket;
    var li = document.createElement('li');
    li.className = 'whb__item';
    li.id = 'whbRow-' + id;
    fillRow(li, ticket + '번', '대기 접수');
    listEl.appendChild(li);
    rows[id] = li;
    if (!reduce) {
      li.animate(
        [{ opacity: 0, transform: 'translateY(6px)' }, { opacity: 1, transform: 'translateY(0px)' }],
        { duration: 240, easing: 'ease-out' }
      );
    }
    var nextTotal = Math.min(99, total + 1);
    var dir = nextTotal - total;
    total = nextTotal;
    rollDigits(totalDigits, total, nowMs, HERO_COUNT_ROLL_MS);
    flashTotal(dir, nowMs);
    paintStatus();
  }

값이 반응을 바꾸는 곳 — 자릿수 굴림 속도·재호출 대기 시간·목록 이동 탄성

번호판이 얼마나 부산하게 느껴지는지는 항목 개수가 아니라 이 표의 세 수치와 세 상수가 정한다.

항목 누적면적 % 강도 평균 움직인 프레임
01 호출 번호 굴림 16.7% 80.6 10/23
02 예상 대기 시간 12.8% 70.4 7/23
03 완료 비율 링 15.8% 58.1 11/23
04 목록 스프링 32.8% 82.8 14/23
05 재호출 카운트다운 6.2% 125.1 23/23
06 다중 창구 2.7% 97.2 7/23
07 전체 인원 카운터 12.8% 63.4 14/23
08 우선 호출 줄 27.4% 82.1 10/23
09 히어로 순번판 26.1% 59.5 18/23

05 와 06 이 10% 아래인 것은 컴포넌트 편에서 정상이다 — 05 는 가는 막대 하나가 줄어들 뿐이라 면적은 작아도 게이지가 매 프레임 줄어 23/23 으로 계속 움직이고, 06 은 막대 셋이 짧게 늘었다 멈추는 정도라 프레임 수도 적다. 01·02·03 은 값이 바뀔 때 숫자판이 잠깐 밝아지는 is-flash 층이 얹혀 있는데, 이 층은 계산이 아니라 표현이고 그만큼 측정 면적을 보강한다. CALL_NUMBER_ROLL_MS 는 번호 한 자리가 굴러 도착하는 시간을, RECALL_COUNTDOWN_SEC 는 호출 뒤 재호출까지 기다리는 초를, LIST_SHIFT_SPRING_STIFFNESS 는 목록이 자리를 잡는 시간을 정하는데, 04 의 이동 시간은 60 + 100000 / 탄성 + 간격 항 으로 계산되어 탄성이 클수록 짧아진다. 05 는 데모 재생에서만 60초를 40배로 압축해 짧은 캡처 안에 만료까지 보여 주고, 사용자가 버튼을 누르면 그 압축이 꺼져 실제 초 단위로 돌아간다.

어디서 깨지는가 — 인원 급증 시 자릿수 겹침·재호출 타이머 밀림·우선 호출 끼어들기 충돌

01 의 호출 번호판은 세 자리(buildOdometer(callOdo, 3))로 만들어져 있고 rollDigits 는 번호를 문자열로 바꿔 앞에서 세 글자만 읽기 때문에, 번호가 네 자리로 넘어가면 1000 의 앞 세 글자인 100 이 판에 뜬다. 07·09 의 두 자리 판은 99 에서 값을 막지만 01 의 호출 번호에는 그런 상한이 없으니, 인원이 갑자기 불어 자릿수가 늘 수 있는 판이라면 자릿수를 값에서 계산해 만들어야 한다. 번호를 연달아 호출하면 아직 도는 자릿수는 from 을 지금 보이는 값으로 갈아 끼워 거기서 이어 굴러가므로 앞 굴림과 겹쳐도 값이 튀지는 않는다.

05 는 호출 시각과 매 프레임의 경과 시간 차로 남은 시간을 매번 새로 계산하므로 프레임이 몇 번 빠져도 시간이 밀리지 않지만, 브라우저가 탭을 쉬게 하는 동안 requestAnimationFrame 이 멈추면 돌아온 첫 프레임에서 경과 시간이 한꺼번에 커져 게이지가 단숨에 만료 상태로 넘어간다 — 코드 구조에서 나오는 동작이다. 08 은 줄을 넣을 때마다 아래 줄에 새 animate 를 얹는데 앞선 애니메이션을 취소하는 cancel() 호출이 0건이고, 줄 수의 상한도 없어서 우선 접수를 연달아 누르면 앞선 이동의 중간 위치가 다음 계산에 섞이며 목록이 계속 길어진다. 종이 순번표 양식처럼 번호와 비고를 한 줄에 놓는 이 구성에 실제 현장 규모의 접수를 붙일 때는 줄 수 상한과 이동 중 취소를 같이 넣는 편이 안전하다. 이 세 가지 한계까지 포함한 아홉 개 소스를 그대로 열어 보려면 내려받은 압축 파일을 풀 때 비밀번호 auwdqs3v 을 입력한다.

변형 세 가지와 접근성

항목 상수 값 느낌
04 LIST_SHIFT_SPRING_STIFFNESS 260 → 140 값을 늘리면 앞 순번이 빠질 때 목록이 빠르고 뻣뻣하게 자리를 잡고, 값을 줄이면 출렁이듯 천천히 흔들리며 자리를 잡는다
01 CALL_NUMBER_ROLL_MS 480 → 240 값을 줄이면 새 번호가 호출될 때 자릿수가 거의 순간적으로 바뀌고, 값을 늘리면 슬롯머신처럼 천천히 굴러가며 바뀐다
05 RECALL_COUNTDOWN_SEC 60 → 30 값을 늘리면 응답이 없어도 재호출까지 여유 있게 기다리고, 값을 줄이면 짧은 시간 안에 게이지가 다 줄어 재호출 경고가 뜬다

04 의 이동 시간은 간격 항을 뺀 기본 값이 탄성 260 에서 약 445ms, 140 에서 약 774ms 로 늘어난다. prefers-reduced-motion: reduce 분기는 matchMedia 로 아홉 항목 전부에 있어서, 동작 줄임 설정을 켠 방문자에게는 자동 재생이 시작되지 않고 01·07·09 는 requestAnimationFrame 루프 자체를 돌리지 않고 05 는 루프만 돌 뿐 게이지를 계산하지 않아 가득 찬 채로 있으며, 버튼을 누르면 애니메이션 없이 결과가 바로 반영되고 aria-live="polite" 상태 줄도 함께 알린다. 키보드 경로는 아홉 항목 전부에 있다 — keydown 리스너가 합쳐서 20개이고 버튼이 셋인 07·09 가 3개씩, 나머지는 2개씩이며 Enter 와 Space 로 조작한다. 상태 줄(aria-live)은 항목마다 하나씩 아홉 개이고, 목록 마크업이 있는 04·06·08·09 네 곳에는 role="list" 가 붙어 있다.

웨이팅 화면은 대기표 시스템처럼 평균 처리 시간만으로 순서를 예측할 수 있는가?

시간은 가능하고 순서는 따로 정해 두어야 한다 — 02 는 최근 처리 기록의 평균에 남은 인원을 곱하는 계산 하나로 예상 대기 분을 만들지만, 누가 먼저 불리는지는 이 평균과 상관없이 이미 들어온 번호 순이거나 08 처럼 가중치가 정한다. 평균은 최근 12건만 남기고 오래된 것부터 버리므로 갑자기 느려진 창구가 있으면 몇 건 처리하는 사이 예상 시간이 따라 올라온다. 번호와 단계를 한 줄로 진행시키는 화면이라면 진행 단계 ui 9가지 — 스텝 인디케이터를 물리 계산으로 다듬기 와 비교해 보면 좋고, 05 처럼 숫자가 줄다가 한계에서 색이 바뀌는 게이지가 필요하면 게이지 바 9종 — 게임처럼 채워지는 아홉 가지 방식, 시간 제한 카운트다운이 더 궁금하다면 qr 코드 만료 카운트다운 css 애니메이션 9가지 의 계산을 이어서 읽을 만하다.

웨이팅 화면을 현장 화면에 올리기 전에 자주 묻는 질문

대기표 발급기 화면에도 이 순번판을 그대로 붙일 수 있나요?

표시 쪽은 붙일 수 있다 — 아홉 항목 모두 번호와 인원을 받아 그리기만 하고 발급이나 인쇄는 하지 않으니, 발급기 쪽에서 번호를 넘겨 주는 자리에 01 의 rollDigits 나 07 의 applyDelta 호출을 걸면 된다.

예상 대기 시간을 평균 말고 다른 값으로 계산해도 되나요?

된다 — 02 의 targetEta 는 평균에 남은 인원을 곱하는 한 줄이라, 그 줄만 다른 식으로 바꾸어도 숫자가 굴러가는 부분은 그대로 재사용된다.

재호출까지 60초가 너무 길면 어디를 고치나요?

RECALL_COUNTDOWN_SEC 값을 줄이면 된다 — 변형 표의 05 처럼 30 으로 바꾸면 게이지 폭 계산과 남은 초 문구가 모두 같은 상수에서 나오므로 다른 곳을 건드릴 필요가 없다.

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

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