From 7913fd1e31a7ecc4838bcdf8e85c75a155af4e14 Mon Sep 17 00:00:00 2001 From: leesangdal Date: Wed, 23 Apr 2025 18:09:37 +0900 Subject: [PATCH 01/30] =?UTF-8?q?refactor:=20css=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/global/common.css | 289 +++++++++++++++++++++++++++ {styles => css/global}/reset.css | 0 {styles => css/global}/variables.css | 0 styles/style.css => css/index.css | 188 +---------------- css/pages/auth.css | 82 ++++++++ index.html | 2 +- login.html | 2 +- signup.html | 2 +- styles/common.css | 150 -------------- 9 files changed, 378 insertions(+), 337 deletions(-) create mode 100644 css/global/common.css rename {styles => css/global}/reset.css (100%) rename {styles => css/global}/variables.css (100%) rename styles/style.css => css/index.css (55%) create mode 100644 css/pages/auth.css delete mode 100644 styles/common.css diff --git a/css/global/common.css b/css/global/common.css new file mode 100644 index 00000000..819ef163 --- /dev/null +++ b/css/global/common.css @@ -0,0 +1,289 @@ +@charset "utf-8"; +html { + font-size: 14px; +} + +body { + font-family: "Pretendard", sans-serif; + background: var(--background-light); + overflow-x: hidden; +} + +/* fonts */ +@font-face { + font-family: "Pretendard"; + font-weight: 400; + src: url("/fonts/Pretendard-Regular.woff2") format("woff2"); +} +@font-face { + font-family: "Pretendard"; + font-weight: 500; + src: url("/fonts/Pretendard-Medium.woff2") format("woff2"); +} +@font-face { + font-family: "Pretendard"; + font-weight: 700; + src: url("/fonts/Pretendard-Bold.woff2") format("woff2"); +} + +/* layout */ +.content { + padding-top: 70px; +} + +/* input */ +input { + font-family: "Pretendard", sans-serif; +} + +/* button */ +button { + font-family: "Pretendard", sans-serif; +} +button:disabled { + cursor: default; + background-color: var(--gray400); +} +button:disabled:hover { + background-color: var(--gray400); +} + +.btn-lg { + padding: 12px; + border-radius: var(--border-radius-lg); + font-size: 20px; + font-weight: 600; + line-height: 32px; +} + +.btn-sm { + padding: 12px 24px; + font-size: 16px; + border-radius: var(--border-radius-xs); +} + +.btn-primary { + background: var(--primary-color); + color: #fff; +} +.btn-primary:hover { + background: var(--primary-hover-color); +} +.btn-primary:active { + background: var(--primary-click-color); +} + +a[class*="btn"] { + display: inline-flex; + align-items: center; + justify-content: center; +} + +/* form */ +.form-container { + display: flex; + justify-content: center; + align-items: center; +} + +.form { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} + +.form-label { + display: block; + margin-bottom: var(--form-label-margin-bottom); + font-size: var(--form-label-font-size); + font-weight: 700; + color: var(--gray800); +} + +.form-input { + width: 100%; + padding: 15px 24px; + background: var(--gray100); + border-radius: var(--border-radius-sm); + font-size: 16px; + line-height: 26px; +} + +.visible-wrap .form-input { + padding-right: 60px; +} + +.form-logo img { + height: auto; +} + +.form-contents { + display: flex; + flex-direction: column; + gap: var(--form-contents-gap); + width: 100%; +} + +.visible-wrap { + position: relative; +} + +.form .btn-lg { + width: 100%; +} + +.btn-password-visible { + position: absolute; + top: 50%; + transform: translateY(-50%); + right: 24px; + width: 24px; + height: 24px; + z-index: 1; +} +.btn-password-visible.on { + background: url("/images/ic_visibility_on.svg") no-repeat center; +} +.btn-password-visible.off { + background: url("/images/ic_visibility_off.svg") no-repeat center; +} + +/* header */ +.header { + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 70px; + padding: 0 var(--header-padding-lr); + z-index: 9; + background: #fff; + display: flex; +} + +.header-container { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: 0.714rem 0; +} + +.logo { + display: flex; + align-items: center; +} + +.logo-img { + display: none; + width: 40px; + margin-right: 8px; +} + +.login-button { + width: 128px; + height: 48px; +} + +/* footer */ +.footer { + background: var(--gray900); + color: var(--gray200); +} + +.footer-container { + display: flex; + justify-content: space-between; + padding: var(--footer-padding); + flex-wrap: wrap; + gap: 24px; + font-size: 16px; +} + +.copyright { + order: 3; + width: 100%; + padding-top: 36px; + color: var(--gray400); +} + +.footer-menu { + display: flex; + gap: 30px; +} + +.sns { + display: flex; + gap: 12px; +} + +/*================ 반응형 ================*/ +/* Tablet */ +@media (min-width: 640px) { + :root { + --footer-padding: 2rem 6.5rem 6.75rem; + --form-padding-top: 60px; + --form-label-margin-bottom: 1rem; + --form-contents-gap: 1.5rem; + } + + .logo-img { + display: block; + } + + .footer-container { + flex-wrap: initial; + } + + .copyright { + order: initial; + width: auto; + padding-top: 0; + } +} + +@media (min-width: 768px) { + :root { + --form-padding-top: 48px; + --header-padding-lr: 24px; + } + + html { + font-size: 16px; + } +} + +/* PC */ +@media (min-width: 1200px) { + :root { + --header-padding-lr: 12.5rem; + } + + .form-container { + min-height: 100vh; + } + + .header-container, + .footer-container { + width: var(--container-width); + margin: 0 auto; + } +} + +/* wide PC */ +@media (min-width: 1921px) { + html { + font-size: 18px; + } +} + +/* 반응형 요소에 clamp() 적용 */ +@supports (font-size: clamp(1rem, 2vw, 3rem)) { + :root { + --heading-font-size: clamp(24px, 3vw, 40px); + --description-font-size: clamp(16px, 2vw, 24px); + --label-font-size: clamp(16px, 1.5vw, 18px); + --form-label-font-size: clamp(14px, 1.25vw, 18px); + } +} diff --git a/styles/reset.css b/css/global/reset.css similarity index 100% rename from styles/reset.css rename to css/global/reset.css diff --git a/styles/variables.css b/css/global/variables.css similarity index 100% rename from styles/variables.css rename to css/global/variables.css diff --git a/styles/style.css b/css/index.css similarity index 55% rename from styles/style.css rename to css/index.css index 8ce4f515..5e652149 100644 --- a/styles/style.css +++ b/css/index.css @@ -1,46 +1,9 @@ @charset "utf-8"; -@import "reset.css"; -@import "common.css"; -@import "variables.css"; +@import "./global/reset.css"; +@import "./global/common.css"; +@import "./global/variables.css"; /*================ 메인 ================*/ -/* header */ -.header { - position: fixed; - left: 0; - top: 0; - width: 100%; - height: 70px; - padding: 0 var(--header-padding-lr); - z-index: 9; - background: #fff; - display: flex; -} - -.header-container { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - padding: 0.714rem 0; -} - -.logo { - display: flex; - align-items: center; -} - -.logo-img { - display: none; - width: 40px; - margin-right: 8px; -} - -.login-button { - width: 128px; - height: 48px; -} - /* section */ .sections { padding: var(--sections-padding); @@ -148,146 +111,25 @@ margin: 0 auto; } -/* footer */ -.footer { - background: var(--gray900); - color: var(--gray200); -} - -.footer-container { - display: flex; - justify-content: space-between; - padding: var(--footer-padding); - flex-wrap: wrap; - gap: 24px; - font-size: 16px; -} - -.copyright { - order: 3; - width: 100%; - padding-top: 36px; - color: var(--gray400); -} - -.footer-menu { - display: flex; - gap: 30px; -} - -.sns { - display: flex; - gap: 12px; -} - -/*================ 로그인, 회원가입 ================*/ -.auth .form { - max-width: 400px; - padding: 0 16px; -} - -.auth .form-logo { - width: 198px; - margin: 0 0 2.5rem; -} - -.auth .form-footer { - text-align: center; - font-size: 14px; - font-weight: 500; -} - -.auth .form-footer-link { - text-decoration: underline; - color: var(--primary-color); - font-size: 14px; -} - -.login .form-container { - padding-top: 80px; -} - -.signup .form-container { - padding-top: var(--form-padding-top); - padding-bottom: 178px; -} - -.easy-login { - display: flex; - justify-content: space-between; - align-items: center; - margin: 8px 0; - padding: 16px 24px; - border-radius: var(--border-radius-xs); - background: var(--background-blue-light); - font-size: 16px; - color: var(--gray800); -} - -.easy-login-icons { - display: flex; - gap: 1rem; -} - /*================ 반응형 ================*/ /* Tablet */ @media (min-width: 640px) { :root { --sections-padding: 24px 24px 56px; --section-margin-bottom: 24px; - --footer-padding: 2rem 6.5rem 6.75rem; - --form-padding-top: 60px; - --form-label-margin-bottom: 1rem; - --form-contents-gap: 1.5rem; - } - - .logo-img { - display: block; - } - - .auth .form { - max-width: 640px; - } - - .auth .form-logo { - width: 396px; - } - - .easy-login { - margin: 0; } .banner-hero .banner-info { padding: 84px 0 210px; max-width: none; } - - .footer-container { - flex-wrap: initial; - } - - .copyright { - order: initial; - width: auto; - padding-top: 0; - } } @media (min-width: 768px) { :root { - --form-padding-top: 48px; - --header-padding-lr: 24px; --banner-btn-font-size: 20px; } - html { - font-size: 16px; - } - - .auth .form { - max-width: 40rem; - } - .banner { height: 926px; } @@ -309,20 +151,9 @@ :root { --sections-padding: 138px 24px; --section-margin-bottom: 1.5rem; - --header-padding-lr: 12.5rem; - } - - .form-container { - min-height: 100vh; - } - - .login .form-container { - padding-top: 0; } - .header-container, - .banner-container, - .footer-container { + .banner-container { width: var(--container-width); margin: 0 auto; } @@ -381,20 +212,9 @@ } } -/* wide PC */ -@media (min-width: 1921px) { - html { - font-size: 18px; - } -} - /* 반응형 요소에 clamp() 적용 */ @supports (font-size: clamp(1rem, 2vw, 3rem)) { :root { --banner-font-size: clamp(32px, 5vw, 40px); - --heading-font-size: clamp(24px, 3vw, 40px); - --description-font-size: clamp(16px, 2vw, 24px); - --label-font-size: clamp(16px, 1.5vw, 18px); - --form-label-font-size: clamp(14px, 1.25vw, 18px); } } diff --git a/css/pages/auth.css b/css/pages/auth.css new file mode 100644 index 00000000..32f0d2ab --- /dev/null +++ b/css/pages/auth.css @@ -0,0 +1,82 @@ +@charset "utf-8"; +@import "../global/reset.css"; +@import "../global/common.css"; +@import "../global/variables.css"; + +/*================ 로그인, 회원가입 ================*/ +.auth .form { + max-width: 400px; + padding: 0 16px; +} + +.auth .form-logo { + width: 198px; + margin: 0 0 2.5rem; +} + +.auth .form-footer { + text-align: center; + font-size: 14px; + font-weight: 500; +} + +.auth .form-footer-link { + text-decoration: underline; + color: var(--primary-color); + font-size: 14px; +} + +.login .form-container { + padding-top: 80px; +} + +.signup .form-container { + padding-top: var(--form-padding-top); + padding-bottom: 178px; +} + +.easy-login { + display: flex; + justify-content: space-between; + align-items: center; + margin: 8px 0; + padding: 16px 24px; + border-radius: var(--border-radius-xs); + background: var(--background-blue-light); + font-size: 16px; + color: var(--gray800); +} + +.easy-login-icons { + display: flex; + gap: 1rem; +} + +/*================ 반응형 ================*/ +/* Tablet */ +@media (min-width: 640px) { + .auth .form { + max-width: 640px; + } + + .auth .form-logo { + width: 396px; + } + + .easy-login { + margin: 0; + } +} + +@media (min-width: 768px) { + .auth .form { + max-width: 40rem; + } +} + +/* PC */ +@media (min-width: 1200px) { + .login .form-container { + padding-top: 0; + } +} diff --git a/index.html b/index.html index 0ceb4f04..227a5913 100644 --- a/index.html +++ b/index.html @@ -36,7 +36,7 @@ sizes="16x16" href="images/favicon-16x16.png" /> - +
diff --git a/login.html b/login.html index 8923a7aa..7cfa63e1 100644 --- a/login.html +++ b/login.html @@ -10,7 +10,7 @@ sizes="16x16" href="images/favicon-16x16.png" /> - +
diff --git a/signup.html b/signup.html index c4d6c38d..dbe52b70 100644 --- a/signup.html +++ b/signup.html @@ -10,7 +10,7 @@ sizes="16x16" href="images/favicon-16x16.png" /> - +
diff --git a/styles/common.css b/styles/common.css deleted file mode 100644 index 1f34e090..00000000 --- a/styles/common.css +++ /dev/null @@ -1,150 +0,0 @@ -@charset "utf-8"; -html { - font-size: 14px; -} - -body { - font-family: "Pretendard", sans-serif; - background: var(--background-light); - overflow-x: hidden; -} - -/* fonts */ -@font-face { - font-family: "Pretendard"; - font-weight: 400; - src: url("../fonts/Pretendard-Regular.woff2") format("woff2"); -} -@font-face { - font-family: "Pretendard"; - font-weight: 500; - src: url("../fonts/Pretendard-Medium.woff2") format("woff2"); -} -@font-face { - font-family: "Pretendard"; - font-weight: 700; - src: url("../fonts/Pretendard-Bold.woff2") format("woff2"); -} - -/* layout */ -.content { - padding-top: 70px; -} - -/* input */ -input { - font-family: "Pretendard", sans-serif; -} - -/* button */ -button { - font-family: "Pretendard", sans-serif; -} -button:disabled { - cursor: default; - background-color: var(--gray400); -} -button:disabled:hover { - background-color: var(--gray400); -} - -.btn-lg { - padding: 12px; - border-radius: var(--border-radius-lg); - font-size: 20px; - font-weight: 600; - line-height: 32px; -} - -.btn-sm { - padding: 12px 24px; - font-size: 16px; - border-radius: var(--border-radius-xs); -} - -.btn-primary { - background: var(--primary-color); - color: #fff; -} -.btn-primary:hover { - background: var(--primary-hover-color); -} -.btn-primary:active { - background: var(--primary-click-color); -} - -a[class*="btn"] { - display: inline-flex; - align-items: center; - justify-content: center; -} - -/* form */ -.form-container { - display: flex; - justify-content: center; - align-items: center; -} - -.form { - display: flex; - flex-direction: column; - align-items: center; - width: 100%; -} - -.form-label { - display: block; - margin-bottom: var(--form-label-margin-bottom); - font-size: var(--form-label-font-size); - font-weight: 700; - color: var(--gray800); -} - -.form-input { - width: 100%; - padding: 15px 24px; - background: var(--gray100); - border-radius: var(--border-radius-sm); - font-size: 16px; - line-height: 26px; -} - -.visible-wrap .form-input { - padding-right: 60px; -} - -.form-logo img { - height: auto; -} - -.form-contents { - display: flex; - flex-direction: column; - gap: var(--form-contents-gap); - width: 100%; -} - -.visible-wrap { - position: relative; -} - -.form .btn-lg { - width: 100%; -} - -.btn-password-visible { - position: absolute; - top: 50%; - transform: translateY(-50%); - right: 24px; - width: 24px; - height: 24px; - z-index: 1; -} -.btn-password-visible.on { - background: url("../images/ic_visibility_on.svg") no-repeat center; -} -.btn-password-visible.off { - background: url("../images/ic_visibility_off.svg") no-repeat center; -} From af7a8a7353b9cd3845e8e9abdf9086599d6cdcea Mon Sep 17 00:00:00 2001 From: "asksa1256@gmail.com" Date: Thu, 24 Apr 2025 11:41:07 +0900 Subject: [PATCH 02/30] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/global/common.css | 11 +++++++++ css/global/variables.css | 1 + login.html | 10 ++++++++- script/login.js | 38 +++++++++++++++++++++++++++++++ script/util/validators.js | 47 +++++++++++++++++++++++++++++++++++++++ signup.html | 1 + 6 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 script/login.js create mode 100644 script/util/validators.js diff --git a/css/global/common.css b/css/global/common.css index 819ef163..41e5a544 100644 --- a/css/global/common.css +++ b/css/global/common.css @@ -109,6 +109,17 @@ a[class*="btn"] { font-size: 16px; line-height: 26px; } +.form-input.invalid { + border: 1px solid var(--error-color); +} + +.form-input-hint { + display: block; + margin: 8px 0 0 16px; + font-size: 14px; + font-weight: 600; + color: var(--error-color); +} .visible-wrap .form-input { padding-right: 60px; diff --git a/css/global/variables.css b/css/global/variables.css index 04c16309..ca1dfa44 100644 --- a/css/global/variables.css +++ b/css/global/variables.css @@ -18,6 +18,7 @@ --background-light: #fcfcfc; --background-blue: #cfe5ff; --background-blue-light: #e6f2ff; + --error-color: #f74747; /* button */ --banner-btn-font-size: 18px; diff --git a/login.html b/login.html index 7cfa63e1..b09755b4 100644 --- a/login.html +++ b/login.html @@ -11,6 +11,7 @@ href="images/favicon-16x16.png" /> +
@@ -38,6 +39,7 @@ placeholder="이메일" required /> +
@@ -59,8 +61,14 @@ aria-pressed="false" >
+ -
@@ -50,6 +51,7 @@ placeholder="닉네임" required /> +
@@ -71,6 +73,7 @@ aria-pressed="false" >
+
+ - From fa159d05e4b46bfa8b139126f23a0fc6e421e8f1 Mon Sep 17 00:00:00 2001 From: leesangdal Date: Thu, 24 Apr 2025 14:54:42 +0900 Subject: [PATCH 08/30] =?UTF-8?q?refactor:=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=ED=91=9C=EC=8B=9C/=EC=88=A8=EA=B9=80=20early=20ret?= =?UTF-8?q?urn,=20=EC=A1=B0=EA=B1=B4=EB=AC=B8=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/util/togglePassword.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/script/util/togglePassword.js b/script/util/togglePassword.js index a2587245..5be7c069 100644 --- a/script/util/togglePassword.js +++ b/script/util/togglePassword.js @@ -1,19 +1,15 @@ "use strict"; -export default function togglePasswordHandler(area = "body") { +export default function togglePasswordHandler(area = document.body) { area.addEventListener("click", (e) => { - const targetInput = e.target.parentNode.querySelector(".form-input"); + const toggleBtn = e.target.closest(".btn-password-visible"); + if (!toggleBtn) return; - if (!e.target.classList.contains("btn-password-visible")) return; + const targetInput = toggleBtn.parentNode.querySelector(".form-input"); + if (!targetInput) return; - if (!e.target.classList.contains("on")) { - e.target.classList.add("on"); - targetInput.type = "text"; - targetInput.setAttribute("aria-pressed", "true"); - } else { - e.target.classList.remove("on"); - targetInput.type = "password"; - targetInput.setAttribute("aria-pressed", "false"); - } + const isVisible = toggleBtn.classList.toggle("on"); + targetInput.type = isVisible ? "text" : "password"; + targetInput.setAttribute("aria-pressed", `${isVisible}`); }); } From 345d14f022fb6185058742bcb93d75b09f71cab7 Mon Sep 17 00:00:00 2001 From: leesangdal Date: Thu, 24 Apr 2025 16:12:30 +0900 Subject: [PATCH 09/30] =?UTF-8?q?refactor:=20early=20return=20+=20if=20els?= =?UTF-8?q?e=20=EC=A4=91=EC=B2=A9=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/util/validators.js | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/script/util/validators.js b/script/util/validators.js index bbbf7c14..7466b3e6 100644 --- a/script/util/validators.js +++ b/script/util/validators.js @@ -20,13 +20,15 @@ function validateEmail(input) { if (input.value.trim().length === 0) { setInvalid(input, "이메일을 입력해주세요."); return false; - } else if (!input.checkValidity()) { + } + + if (!input.checkValidity()) { setInvalid(input, "잘못된 이메일 형식입니다."); return false; - } else { - setValid(input); - return true; } + + setValid(input); + return true; } function validatePassword(input, chkInput) { @@ -43,33 +45,35 @@ function validatePassword(input, chkInput) { if (input.value.trim().length === 0) { setInvalid(input, "비밀번호를 입력해주세요."); return false; - } else if (input.value.trim().length < 8) { + } + + if (input.value.trim().length < 8) { setInvalid(input, "비밀번호를 8자 이상 입력해주세요."); return false; - } else { - setValid(input); - return true; } + + setValid(input); + return true; } function validatePasswordChk(input, chkInput) { if (input.value !== chkInput.value) { setInvalid(chkInput, "비밀번호가 일치하지 않습니다."); return false; - } else { - setValid(chkInput); - return true; } + + setValid(chkInput); + return true; } function validateNickname(input) { if (input.value.trim().length === 0) { setInvalid(input, "닉네임을 입력해주세요."); return false; - } else { - setValid(input); - return true; } + + setValid(input); + return true; } export { From 9c2c931e988d04b142d9365be7020e96cacffb4c Mon Sep 17 00:00:00 2001 From: leesangdal Date: Thu, 24 Apr 2025 16:24:56 +0900 Subject: [PATCH 10/30] =?UTF-8?q?refactor:=20=EC=9C=A0=ED=9A=A8=EC=84=B1?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=20=EC=84=A4=EC=A0=95=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=20=ED=95=A9=EC=B9=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/util/validators.js | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/script/util/validators.js b/script/util/validators.js index 7466b3e6..f0650037 100644 --- a/script/util/validators.js +++ b/script/util/validators.js @@ -1,29 +1,30 @@ "use strict"; -function setInvalid(input, message) { +/* 유효성 상태 설정 */ +function setValid(input, valid = true, msg) { const inputHintEl = input .closest(".form-control") .querySelector(".form-input-hint"); - input.classList.add("invalid"); - inputHintEl.textContent = message; -} -function setValid(input) { - const inputHintEl = input - .closest(".form-control") - .querySelector(".form-input-hint"); + if (!valid) { + input.classList.add("invalid"); + inputHintEl.textContent = msg; + return; + } + input.classList.remove("invalid"); inputHintEl.textContent = ""; } +/* 이메일 유효성 */ function validateEmail(input) { if (input.value.trim().length === 0) { - setInvalid(input, "이메일을 입력해주세요."); + setValid(input, false, "이메일을 입력해주세요."); return false; } if (!input.checkValidity()) { - setInvalid(input, "잘못된 이메일 형식입니다."); + setValid(input, false, "잘못된 이메일 형식입니다."); return false; } @@ -31,6 +32,7 @@ function validateEmail(input) { return true; } +/* 비밀번호 유효성 */ function validatePassword(input, chkInput) { // 비밀번호 체크 연동 if (chkInput) { @@ -43,12 +45,12 @@ function validatePassword(input, chkInput) { // 비밀번호 유효성 검사 if (input.value.trim().length === 0) { - setInvalid(input, "비밀번호를 입력해주세요."); + setValid(input, false, "비밀번호를 입력해주세요."); return false; } if (input.value.trim().length < 8) { - setInvalid(input, "비밀번호를 8자 이상 입력해주세요."); + setValid(input, false, "비밀번호를 8자 이상 입력해주세요."); return false; } @@ -56,9 +58,10 @@ function validatePassword(input, chkInput) { return true; } +/* 비밀번호 확인 유효성 */ function validatePasswordChk(input, chkInput) { if (input.value !== chkInput.value) { - setInvalid(chkInput, "비밀번호가 일치하지 않습니다."); + setValid(chkInput, false, "비밀번호가 일치하지 않습니다."); return false; } @@ -66,9 +69,10 @@ function validatePasswordChk(input, chkInput) { return true; } +/* 닉네임 유효성 */ function validateNickname(input) { if (input.value.trim().length === 0) { - setInvalid(input, "닉네임을 입력해주세요."); + setValid(input, false, "닉네임을 입력해주세요."); return false; } From 32dd9700a597777e750d77c871cf950e9da3522e Mon Sep 17 00:00:00 2001 From: leesangdal Date: Thu, 24 Apr 2025 17:04:50 +0900 Subject: [PATCH 11/30] =?UTF-8?q?feat:=20=ED=8F=BC=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=EC=99=84=EC=84=B1=20=EB=90=98=EC=96=B4=EC=9E=88=EC=9D=84=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=ED=8F=BC=20=EB=B2=84=ED=8A=BC=20=ED=99=9C?= =?UTF-8?q?=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/login.js | 9 +++++++-- script/signup.js | 12 ++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/script/login.js b/script/login.js index cd13a180..0293e20b 100644 --- a/script/login.js +++ b/script/login.js @@ -31,9 +31,14 @@ form.addEventListener("focusout", formValidate); /* 비밀번호 토글 */ togglePasswordHandler(form); -/* UX: 로그인 페이지 진입 시 첫번째 input focus 처리 */ +/* 페이지 진입 UX */ window.addEventListener("DOMContentLoaded", () => { - emailInput.focus(); + // 첫번째 input focus 처리 + form.querySelector(".form-input").focus(); + + // 페이지 진입 시, 폼 자동완성 되어있으면 버튼 활성화 + loginBtn.disabled = + emailInput.value.length === 0 && passwordInput.value.length === 0; }); /* 로그인 버튼 클릭 시 'items'로 이동 */ diff --git a/script/signup.js b/script/signup.js index 5345d88a..b9b99e1a 100644 --- a/script/signup.js +++ b/script/signup.js @@ -51,9 +51,17 @@ form.addEventListener("focusout", formValidate); /* 비밀번호 토글 */ togglePasswordHandler(form); -/* UX: 페이지 진입 시 첫번째 input focus 처리 */ +/* 페이지 진입 UX */ window.addEventListener("DOMContentLoaded", () => { - emailInput.focus(); + // 첫번째 input focus 처리 + form.querySelector(".form-input").focus(); + + // 페이지 진입 시, 폼 자동완성 되어있으면 버튼 활성화 + signupBtn.disabled = + emailInput.value.length === 0 && + nickName.value.length === 0 && + passwordInput.value.length === 0 && + passwordChkInput.value.length === 0; }); /* 회원가입 버튼 클릭 시 'login'으로 이동 */ From 180b6c0251eaf81055fa696e38339ee42d6ea46b Mon Sep 17 00:00:00 2001 From: leesangdal Date: Thu, 24 Apr 2025 18:00:29 +0900 Subject: [PATCH 12/30] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=EC=99=84=EC=84=B1=20=EC=9D=B8=EC=8B=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/login.js | 7 +------ script/signup.js | 10 +--------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/script/login.js b/script/login.js index 0293e20b..91413dae 100644 --- a/script/login.js +++ b/script/login.js @@ -31,14 +31,9 @@ form.addEventListener("focusout", formValidate); /* 비밀번호 토글 */ togglePasswordHandler(form); -/* 페이지 진입 UX */ +/* UX: 첫번째 input focus 처리 */ window.addEventListener("DOMContentLoaded", () => { - // 첫번째 input focus 처리 form.querySelector(".form-input").focus(); - - // 페이지 진입 시, 폼 자동완성 되어있으면 버튼 활성화 - loginBtn.disabled = - emailInput.value.length === 0 && passwordInput.value.length === 0; }); /* 로그인 버튼 클릭 시 'items'로 이동 */ diff --git a/script/signup.js b/script/signup.js index b9b99e1a..3201db7e 100644 --- a/script/signup.js +++ b/script/signup.js @@ -51,17 +51,9 @@ form.addEventListener("focusout", formValidate); /* 비밀번호 토글 */ togglePasswordHandler(form); -/* 페이지 진입 UX */ +/* UX: 첫번째 input focus 처리 */ window.addEventListener("DOMContentLoaded", () => { - // 첫번째 input focus 처리 form.querySelector(".form-input").focus(); - - // 페이지 진입 시, 폼 자동완성 되어있으면 버튼 활성화 - signupBtn.disabled = - emailInput.value.length === 0 && - nickName.value.length === 0 && - passwordInput.value.length === 0 && - passwordChkInput.value.length === 0; }); /* 회원가입 버튼 클릭 시 'login'으로 이동 */ From b0e40c0244956c34a4d46de132be1ae809a7023f Mon Sep 17 00:00:00 2001 From: leesangdal Date: Thu, 24 Apr 2025 18:02:36 +0900 Subject: [PATCH 13/30] =?UTF-8?q?style:=20=ED=8C=90=EB=8B=A4=EB=A7=88?= =?UTF-8?q?=EC=BC=93=20items=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=86=8C?= =?UTF-8?q?=EC=A0=9C=EB=AA=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- items.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/items.html b/items.html index c99f02f8..9726fdcb 100644 --- a/items.html +++ b/items.html @@ -10,6 +10,9 @@ sizes="16x16" href="images/favicon-16x16.png" /> + - + +

판다마켓 상품 목록

+ From 4e386253cad6b349c26f2e69d55113fad70070e1 Mon Sep 17 00:00:00 2001 From: "asksa1256@gmail.com" Date: Thu, 24 Apr 2025 23:46:25 +0900 Subject: [PATCH 14/30] =?UTF-8?q?refactor:=20=ED=8F=BC=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EC=8B=A4=ED=96=89=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=8B=A8=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/login.js | 3 +-- script/signup.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/script/login.js b/script/login.js index 91413dae..42e8f9dd 100644 --- a/script/login.js +++ b/script/login.js @@ -25,8 +25,7 @@ function createFormValidator() { }; } -const formValidate = createFormValidator(); -form.addEventListener("focusout", formValidate); +form.addEventListener("focusout", createFormValidator()); /* 비밀번호 토글 */ togglePasswordHandler(form); diff --git a/script/signup.js b/script/signup.js index 3201db7e..7ee3e5f7 100644 --- a/script/signup.js +++ b/script/signup.js @@ -45,8 +45,7 @@ function createFormValidator() { }; } -const formValidate = createFormValidator(); -form.addEventListener("focusout", formValidate); +form.addEventListener("focusout", createFormValidator()); /* 비밀번호 토글 */ togglePasswordHandler(form); From ca339c2f94bb78094222595952a16519996d5b51 Mon Sep 17 00:00:00 2001 From: "asksa1256@gmail.com" Date: Wed, 30 Apr 2025 09:51:40 +0900 Subject: [PATCH 15/30] =?UTF-8?q?refactor(mentor):=20font-display:=20swap?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9,=20common.css=20=ED=8F=B0=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=AC=EC=A0=95=EC=9D=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/global/common.css | 11 +++-------- css/global/reset.css | 6 ++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/css/global/common.css b/css/global/common.css index e7725665..884e33e3 100644 --- a/css/global/common.css +++ b/css/global/common.css @@ -14,16 +14,19 @@ body { font-family: "Pretendard"; font-weight: 400; src: url("/fonts/Pretendard-Regular.woff2") format("woff2"); + font-display: swap; } @font-face { font-family: "Pretendard"; font-weight: 500; src: url("/fonts/Pretendard-Medium.woff2") format("woff2"); + font-display: swap; } @font-face { font-family: "Pretendard"; font-weight: 700; src: url("/fonts/Pretendard-Bold.woff2") format("woff2"); + font-display: swap; } /* layout */ @@ -31,15 +34,7 @@ body { padding-top: 70px; } -/* input */ -input { - font-family: "Pretendard", sans-serif; -} - /* button */ -button { - font-family: "Pretendard", sans-serif; -} button:disabled { cursor: default; background-color: var(--gray400); diff --git a/css/global/reset.css b/css/global/reset.css index cb25b93a..701575a4 100644 --- a/css/global/reset.css +++ b/css/global/reset.css @@ -30,6 +30,12 @@ svg { display: block; max-width: 100%; } +input, +button, +textarea, +select { + font-family: inherit; +} button { background: none; border: 0; From b6ef680b052933f87b3cbf78c54e4eae0997c6fe Mon Sep 17 00:00:00 2001 From: "asksa1256@gmail.com" Date: Wed, 30 Apr 2025 09:53:04 +0900 Subject: [PATCH 16/30] =?UTF-8?q?refactor(mentor):=20css=20import=20?= =?UTF-8?q?=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/css/index.css b/css/index.css index 5e652149..cf730fda 100644 --- a/css/index.css +++ b/css/index.css @@ -1,7 +1,7 @@ @charset "utf-8"; @import "./global/reset.css"; -@import "./global/common.css"; @import "./global/variables.css"; +@import "./global/common.css"; /*================ 메인 ================*/ /* section */ From 042230294281491d196d6cdb10712adfb97ddf4f Mon Sep 17 00:00:00 2001 From: "asksa1256@gmail.com" Date: Wed, 30 Apr 2025 17:22:40 +0900 Subject: [PATCH 17/30] =?UTF-8?q?refactor(mentor):=20[login.js]=20?= =?UTF-8?q?=ED=81=B4=EB=A1=9C=EC=A0=80=20=ED=95=A8=EC=88=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20+=20validate=20=ED=95=A8=EC=88=98=EC=97=90=EC=84=9C?= =?UTF-8?q?=20UI=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/login.js | 101 +++++++++++++++++++++--------- script/util/updateValidationUI.js | 12 ++++ script/util/validators.js | 71 +++++---------------- 3 files changed, 100 insertions(+), 84 deletions(-) create mode 100644 script/util/updateValidationUI.js diff --git a/script/login.js b/script/login.js index 42e8f9dd..d454bb3e 100644 --- a/script/login.js +++ b/script/login.js @@ -1,42 +1,87 @@ "use strict"; import { validateEmail, validatePassword } from "./util/validators.js"; +import { updateValidationUI } from "./util/updateValidationUI.js"; import togglePasswordHandler from "./util/togglePassword.js"; -const form = document.querySelector(".form"); +const form = document.querySelector("#loginForm"); const emailInput = document.querySelector("#userEmail"); const passwordInput = document.querySelector("#userPassword"); const loginBtn = document.querySelector("#loginBtn"); -function createFormValidator() { - let emailValid = false; - let passwordValid = false; - - return function formValidate(e) { - switch (e.target.id) { - case "userEmail": - emailValid = validateEmail(emailInput); - break; - case "userPassword": - passwordValid = validatePassword(passwordInput); - break; - } - - loginBtn.disabled = !(emailValid && passwordValid); - }; +// 상수 정의 +const FORM_INPUT_IDS = { + EMAIL: emailInput.id, + PASSWORD: passwordInput.id, +}; + +// DOM과 유효성 검사기 연결 +const inputValidatorMap = { + [FORM_INPUT_IDS.EMAIL]: validateEmail, + [FORM_INPUT_IDS.PASSWORD]: validatePassword, +}; + +const FORM_SUBMIT_BUTTON = loginBtn; + +const REDIRECT_TARGET = "/items.html"; + +// 각 키의 유효성 검사값 초기화: [input.id, false] +const validatorKey = Object.keys(inputValidatorMap); +const validStateMap = new Map(validatorKey.map((id) => [id, false])); + +// form에 유효성 검사 위임 +function delegateFormValidation() { + form.addEventListener("focusout", handleFormValidation); +} + +// 유효성 검사 전, 검사 대상 필터 +function handleFormValidation(e) { + const input = e.target; + if (!validatorKey.includes(input.id)) return; + + handleFormInputValidation(input); } -form.addEventListener("focusout", createFormValidator()); +// 유효성 검사 +function handleFormInputValidation(input) { + const validationFunc = inputValidatorMap[input.id]; -/* 비밀번호 토글 */ -togglePasswordHandler(form); + if (!validationFunc) return; -/* UX: 첫번째 input focus 처리 */ -window.addEventListener("DOMContentLoaded", () => { - form.querySelector(".form-input").focus(); -}); + const validationResult = validationFunc(input); + updateValidationUI(input, validationResult); -/* 로그인 버튼 클릭 시 'items'로 이동 */ -loginBtn.addEventListener("click", (e) => { + // 변경된 유효성 상태 업데이트 + validStateMap.set(input.id, validationResult.isValid); + updateSubmitButtonState(); +} + +// 제출 버튼 상태 변경 +function updateSubmitButtonState() { + const isAllValid = [...validStateMap.values()].every(Boolean); + loginBtn.disabled = !isAllValid; +} + +function navigateOnFormSuccess() { e.preventDefault(); - location.href = "/items.html"; -}); + location.href = REDIRECT_TARGET; +} + +function focusFirstInput() { + form.querySelector(".form-input").focus(); +} + +function init() { + /* 폼 유효성 검사 */ + delegateFormValidation(); + + /* 비밀번호 토글 */ + togglePasswordHandler(form); + + /* UX: 첫번째 input focus 처리 */ + focusFirstInput(); + + /* 폼 제출 성공 시 페이지 이동 */ + FORM_SUBMIT_BUTTON.addEventListener("click", navigateOnFormSuccess); +} + +window.addEventListener("DOMContentLoaded", init); diff --git a/script/util/updateValidationUI.js b/script/util/updateValidationUI.js new file mode 100644 index 00000000..17725758 --- /dev/null +++ b/script/util/updateValidationUI.js @@ -0,0 +1,12 @@ +export function updateValidationUI(input, validationResult) { + const inputHint = input + .closest(".form-control") + .querySelector(".form-input-hint"); + + if (!inputHint) return; + + !validationResult.isValid + ? input.classList.add("invalid") + : input.classList.remove("invalid"); + inputHint.textContent = validationResult.message; +} diff --git a/script/util/validators.js b/script/util/validators.js index f0650037..5b94a512 100644 --- a/script/util/validators.js +++ b/script/util/validators.js @@ -1,88 +1,47 @@ -"use strict"; - -/* 유효성 상태 설정 */ -function setValid(input, valid = true, msg) { - const inputHintEl = input - .closest(".form-control") - .querySelector(".form-input-hint"); - - if (!valid) { - input.classList.add("invalid"); - inputHintEl.textContent = msg; - return; - } - - input.classList.remove("invalid"); - inputHintEl.textContent = ""; -} - /* 이메일 유효성 */ -function validateEmail(input) { +export function validateEmail(input) { if (input.value.trim().length === 0) { - setValid(input, false, "이메일을 입력해주세요."); - return false; + return { isValid: false, message: "이메일을 입력해주세요." }; } - if (!input.checkValidity()) { - setValid(input, false, "잘못된 이메일 형식입니다."); - return false; + return { isValid: false, message: "잘못된 이메일 형식입니다." }; } - - setValid(input); - return true; + return { isValid: true, message: "" }; } /* 비밀번호 유효성 */ -function validatePassword(input, chkInput) { +export function validatePassword(input, chkInput) { // 비밀번호 체크 연동 if (chkInput) { if (input.value !== chkInput.value) { validatePasswordChk(input, chkInput); } else { - setValid(chkInput); + return { isValid: true, message: "" }; } } // 비밀번호 유효성 검사 if (input.value.trim().length === 0) { - setValid(input, false, "비밀번호를 입력해주세요."); - return false; + return { isValid: false, message: "비밀번호를 입력해주세요." }; } - if (input.value.trim().length < 8) { - setValid(input, false, "비밀번호를 8자 이상 입력해주세요."); - return false; + return { isValid: false, message: "비밀번호를 8자 이상 입력해주세요." }; } - - setValid(input); - return true; + return { isValid: true, message: "" }; } /* 비밀번호 확인 유효성 */ -function validatePasswordChk(input, chkInput) { +export function validatePasswordChk(input, chkInput) { if (input.value !== chkInput.value) { - setValid(chkInput, false, "비밀번호가 일치하지 않습니다."); - return false; + return { isValid: false, message: "비밀번호가 일치하지 않습니다." }; } - - setValid(chkInput); - return true; + return { isValid: true, message: "" }; } /* 닉네임 유효성 */ -function validateNickname(input) { +export function validateNickname(input) { if (input.value.trim().length === 0) { - setValid(input, false, "닉네임을 입력해주세요."); - return false; + return { isValid: false, message: "닉네임을 입력해주세요." }; } - - setValid(input); - return true; + return { isValid: true, message: "" }; } - -export { - validateEmail, - validatePassword, - validatePasswordChk, - validateNickname, -}; From 44ba6eb4beedb77892101971c852073366abda22 Mon Sep 17 00:00:00 2001 From: "asksa1256@gmail.com" Date: Wed, 30 Apr 2025 17:49:23 +0900 Subject: [PATCH 18/30] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=8F=BC=20=EC=A0=9C=EC=B6=9C=20=EC=84=B1=EA=B3=B5=20=EC=8B=9C?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9D=B4=EB=8F=99=20=EC=95=88?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EC=9D=B4=EC=8A=88=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/login.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/login.js b/script/login.js index d454bb3e..68455616 100644 --- a/script/login.js +++ b/script/login.js @@ -58,10 +58,10 @@ function handleFormInputValidation(input) { // 제출 버튼 상태 변경 function updateSubmitButtonState() { const isAllValid = [...validStateMap.values()].every(Boolean); - loginBtn.disabled = !isAllValid; + FORM_SUBMIT_BUTTON.disabled = !isAllValid; } -function navigateOnFormSuccess() { +function navigateOnFormSuccess(e) { e.preventDefault(); location.href = REDIRECT_TARGET; } @@ -77,7 +77,7 @@ function init() { /* 비밀번호 토글 */ togglePasswordHandler(form); - /* UX: 첫번째 input focus 처리 */ + /* 첫번째 input focus 처리 */ focusFirstInput(); /* 폼 제출 성공 시 페이지 이동 */ From 2a3ebf7f67293eb15309469a10b977f39d0ef573 Mon Sep 17 00:00:00 2001 From: "asksa1256@gmail.com" Date: Wed, 30 Apr 2025 18:53:13 +0900 Subject: [PATCH 19/30] =?UTF-8?q?refactor(mentor):=20[signup.js]=20?= =?UTF-8?q?=ED=81=B4=EB=A1=9C=EC=A0=80=20=ED=95=A8=EC=88=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/signup.js | 135 +++++++++++++++++++++++++------------- script/util/validators.js | 4 +- 2 files changed, 90 insertions(+), 49 deletions(-) diff --git a/script/signup.js b/script/signup.js index 7ee3e5f7..45056f41 100644 --- a/script/signup.js +++ b/script/signup.js @@ -2,61 +2,102 @@ import { validateEmail, validatePassword, - validatePasswordChk, + validatePasswordCheck, validateNickname, } from "./util/validators.js"; +import { updateValidationUI } from "./util/updateValidationUI.js"; import togglePasswordHandler from "./util/togglePassword.js"; -const form = document.querySelector(".form"); +const form = document.querySelector("#signupForm"); const emailInput = document.querySelector("#userEmail"); const nicknameInput = document.querySelector("#userNickname"); const passwordInput = document.querySelector("#userPassword"); -const passwordChkInput = document.querySelector("#userPasswordChk"); +const passwordCheckInput = document.querySelector("#userPasswordChk"); const signupBtn = document.querySelector("#signupBtn"); -function createFormValidator() { - let emailValid = false; - let nicknameValid = false; - let passwordValid = false; - let passwordChkValid = false; - - return function formValidate(e) { - switch (e.target.id) { - case "userEmail": - emailValid = validateEmail(emailInput); - break; - case "userNickname": - nicknameValid = validateNickname(nicknameInput); - break; - case "userPassword": - passwordValid = validatePassword(passwordInput, passwordChkInput); - break; - case "userPasswordChk": - passwordChkValid = validatePasswordChk(passwordInput, passwordChkInput); - break; - } - - signupBtn.disabled = !( - emailValid && - nicknameValid && - passwordValid && - passwordChkValid - ); - }; -} - -form.addEventListener("focusout", createFormValidator()); - -/* 비밀번호 토글 */ -togglePasswordHandler(form); - -/* UX: 첫번째 input focus 처리 */ -window.addEventListener("DOMContentLoaded", () => { - form.querySelector(".form-input").focus(); -}); +// 상수 정의 +const FORM_INPUT_IDS = { + EMAIL: emailInput.id, + PASSWORD: passwordInput.id, + PASSWORD_CHECK: passwordCheckInput.id, + NICKNAME: nicknameInput.id, +}; + +// DOM과 유효성 검사기 연결 +const inputValidatorMap = { + [FORM_INPUT_IDS.EMAIL]: validateEmail, + [FORM_INPUT_IDS.PASSWORD]: validatePassword, + [FORM_INPUT_IDS.PASSWORD_CHECK]: validatePasswordMatch, + [FORM_INPUT_IDS.NICKNAME]: validateNickname, +}; + +const FORM_SUBMIT_BUTTON = signupBtn; + +const REDIRECT_TARGET = "/login.html"; + +// 각 키의 유효성 검사값 초기화: [input.id, false] +const validatorKey = Object.keys(inputValidatorMap); +const validStateMap = new Map(validatorKey.map((id) => [id, false])); + +// 비밀번호 확인 유효성 검사 (비밀번호, 비밀번호 확인 value 연동) +function validatePasswordMatch(input) { + return validatePasswordCheck(passwordInput, input); +} + +// form에 유효성 검사 위임 +function delegateFormValidation() { + form.addEventListener("focusout", handleFormValidation); +} + +// 유효성 검사 전, 검사 대상 필터 +function handleFormValidation(e) { + const input = e.target; + if (!validatorKey.includes(input.id)) return; + + handleFormInputValidation(input); +} + +// 유효성 검사 +function handleFormInputValidation(input) { + const validationFunc = inputValidatorMap[input.id]; + + if (!validationFunc) return; + + const validationResult = validationFunc(input); + updateValidationUI(input, validationResult); -/* 회원가입 버튼 클릭 시 'login'으로 이동 */ -signupBtn.addEventListener("click", (e) => { + // 변경된 유효성 상태 업데이트 + validStateMap.set(input.id, validationResult.isValid); + updateSubmitButtonState(); +} + +// 제출 버튼 상태 변경 +function updateSubmitButtonState() { + const isAllValid = [...validStateMap.values()].every(Boolean); + FORM_SUBMIT_BUTTON.disabled = !isAllValid; +} + +function navigateOnFormSuccess(e) { e.preventDefault(); - location.href = "/login.html"; -}); + location.href = REDIRECT_TARGET; +} + +function focusFirstInput() { + form.querySelector(".form-input").focus(); +} + +function init() { + /* 폼 유효성 검사 */ + delegateFormValidation(); + + /* 비밀번호 토글 */ + togglePasswordHandler(form); + + /* 첫번째 input focus 처리 */ + focusFirstInput(); + + /* 폼 제출 성공 시 페이지 이동 */ + FORM_SUBMIT_BUTTON.addEventListener("click", navigateOnFormSuccess); +} + +window.addEventListener("DOMContentLoaded", init); diff --git a/script/util/validators.js b/script/util/validators.js index 5b94a512..1d010ab5 100644 --- a/script/util/validators.js +++ b/script/util/validators.js @@ -14,7 +14,7 @@ export function validatePassword(input, chkInput) { // 비밀번호 체크 연동 if (chkInput) { if (input.value !== chkInput.value) { - validatePasswordChk(input, chkInput); + validatePasswordCheck(input, chkInput); } else { return { isValid: true, message: "" }; } @@ -31,7 +31,7 @@ export function validatePassword(input, chkInput) { } /* 비밀번호 확인 유효성 */ -export function validatePasswordChk(input, chkInput) { +export function validatePasswordCheck(input, chkInput) { if (input.value !== chkInput.value) { return { isValid: false, message: "비밀번호가 일치하지 않습니다." }; } From 8d5f95a01e77ba31aeb2b19325fb89389dfc2809 Mon Sep 17 00:00:00 2001 From: leesangdal Date: Wed, 30 Apr 2025 21:21:12 +0900 Subject: [PATCH 20/30] =?UTF-8?q?refactor(mentor):=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=ED=86=A0=EA=B8=80=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?'util'=20->=20'auth'=20=ED=95=A8=EC=88=98=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20+=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- login.html | 3 ++- script/{ => auth}/login.js | 10 ++++++---- script/{ => auth}/signup.js | 10 ++++++---- script/auth/togglePasswordVisible.js | 16 ++++++++++++++++ script/util/togglePassword.js | 15 --------------- signup.html | 4 +++- 6 files changed, 33 insertions(+), 25 deletions(-) rename script/{ => auth}/login.js (86%) rename script/{ => auth}/signup.js (90%) create mode 100644 script/auth/togglePasswordVisible.js delete mode 100644 script/util/togglePassword.js diff --git a/login.html b/login.html index 2f4e0145..c919b9b6 100644 --- a/login.html +++ b/login.html @@ -11,7 +11,7 @@ href="images/favicon-16x16.png" /> - +
@@ -56,6 +56,7 @@