2FA 팝업 UI 개선 및 기능 추가:

- 디자인 가이드 기반 레이아웃/스타일 재정의(SASS)
- 인증 흐름 개선: 타이머/프로그레스바/버튼 상태 업데이트(JS)
- 사용자 친화적 안내 메시지 및 입력 상태 검증 추가
This commit is contained in:
Rinjae
2026-07-28 09:50:45 +09:00
parent b5ffa69eba
commit a4316545dc
7 changed files with 707 additions and 206 deletions
@@ -90,13 +90,15 @@
var seg = document.getElementById('tfaSegment');
seg.innerHTML = '';
self._maskedByType = {};
var channels = info.channels || [];
channels.forEach(function (ch, idx) {
self._maskedByType[ch.type] = ch.masked;
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'tfa-seg-btn' + (idx === 0 ? ' is-active' : '');
btn.setAttribute('data-channel', ch.type);
btn.innerHTML = (ch.type === 'EMAIL' ? '이메일 인증' : '전화번호 인증')
btn.innerHTML = '<span class="tfa-seg-label">' + (ch.type === 'EMAIL' ? '이메일' : '휴대폰 문자') + '</span>'
+ '<span class="tfa-seg-masked">' + ch.masked + '</span>';
btn.addEventListener('click', function () { self._selectChannel(ch.type); });
seg.appendChild(btn);
@@ -106,8 +108,11 @@
// 초기 상태: 발송 단계
document.getElementById('tfaSendStep').style.display = '';
document.getElementById('tfaVerifyStep').style.display = 'none';
document.getElementById('tfaMessage').textContent = '';
self._setMessage('', false);
document.getElementById('tfaCodeInput').value = '';
document.getElementById('tfaVerifyButton').disabled = true;
self._setStep2Active(false);
self._resetTimerUi();
var notice = document.getElementById('tfaTestNotice');
notice.style.display = 'none';
notice.textContent = '';
@@ -118,6 +123,11 @@
document.getElementById('tfaVerifyButton').onclick = function () { self._verify(); };
document.getElementById('tfaCloseButton').onclick = function () { self._cancel('CANCELLED'); };
document.getElementById('tfaBackdrop').onclick = function () { self._cancel('CANCELLED'); };
document.getElementById('tfaCodeInput').oninput = function () {
var v = (this.value || '').replace(/\D/g, '').slice(0, 6);
this.value = v;
document.getElementById('tfaVerifyButton').disabled = v.length < 6;
};
document.getElementById('tfaCodeInput').onkeydown = function (e) {
if (e.key === 'Enter') { self._verify(); }
};
@@ -157,8 +167,11 @@
// 발송 성공 → 검증 단계 노출 + 타이머
document.getElementById('tfaSendStep').style.display = 'none';
document.getElementById('tfaVerifyStep').style.display = '';
self._setMessage('', false);
self._setStep2Active(true);
var masked = self._maskedByType ? (self._maskedByType[self._channel] || '') : '';
self._setMessage((masked ? masked + ' 으로 ' : '') + '인증번호를 보냈습니다.', false);
document.getElementById('tfaCodeInput').value = '';
document.getElementById('tfaVerifyButton').disabled = true;
document.getElementById('tfaCodeInput').focus();
if (self._testNotice && res.testAuthNumber) {
var notice = document.getElementById('tfaTestNotice');
@@ -226,21 +239,60 @@
});
},
_fmtClock: function (n) {
var m = Math.floor(n / 60);
var s = n % 60;
return m + ':' + (s < 10 ? '0' + s : s);
},
_setStep2Active: function (active) {
var num = document.getElementById('tfaStepNum');
var text = document.getElementById('tfaStepText');
if (num) { num.classList.toggle('is-active', active); }
if (text) { text.classList.toggle('is-active', active); }
},
_resetTimerUi: function () {
var base = this._ttl || 180;
var clock = document.getElementById('tfaTimer');
var bar = document.getElementById('tfaProgressBar');
var label = document.getElementById('tfaTimerLabel');
var total = document.getElementById('tfaTimerTotal');
if (clock) { clock.className = 'tfa-timer'; clock.textContent = this._fmtClock(base); }
if (bar) { bar.className = 'tfa-progress-bar'; bar.style.width = '100%'; }
if (label) { label.textContent = '남은 인증 시간'; }
if (total) { total.textContent = '/ ' + this._fmtClock(base); }
},
_startTimer: function (seconds) {
var self = this;
self._stopTimer();
var total = seconds || self._ttl || 180;
var remaining = seconds;
var el = document.getElementById('tfaTimer');
var clock = document.getElementById('tfaTimer');
var bar = document.getElementById('tfaProgressBar');
var label = document.getElementById('tfaTimerLabel');
var totalEl = document.getElementById('tfaTimerTotal');
if (label) { label.textContent = '남은 인증 시간'; }
if (totalEl) { totalEl.textContent = '/ ' + self._fmtClock(total); }
function tick() {
if (remaining <= 0) {
self._stopTimer();
el.textContent = '00:00';
if (clock) { clock.textContent = '0:00'; clock.className = 'tfa-timer is-expired'; }
if (bar) { bar.style.width = '0%'; }
if (label) { label.textContent = '인증 시간이 만료되었습니다. 재전송해 주세요.'; }
self._cancel('TIMEOUT');
return;
}
var m = Math.floor(remaining / 60);
var s = remaining % 60;
el.textContent = (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
var warn = remaining <= 30;
if (clock) {
clock.textContent = self._fmtClock(remaining);
clock.className = 'tfa-timer' + (warn ? ' is-warning' : '');
}
if (bar) {
bar.style.width = Math.round((remaining / total) * 100) + '%';
bar.className = 'tfa-progress-bar' + (warn ? ' is-warning' : '');
}
remaining--;
}
tick();