-
Notifications
You must be signed in to change notification settings - Fork 39
[김동희] Sprint4 #147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The head ref may contain hidden characters: "Basic-\uAE40\uB3D9\uD76C-sprint4"
[김동희] Sprint4 #147
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export const MIN_PW_LEN = 8; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| import authFormUtils from "./utils/authFormUtils.js"; | ||
|
|
||
| // DOM elements | ||
| const inputEmail = document.querySelector("#email"); | ||
| const inputPw = document.querySelector("#password"); | ||
| const detailEmail = document.querySelector(".email-detail"); | ||
| const detailPw = document.querySelector(".password-detail"); | ||
| const togglePw = document.querySelector(".toggle-visibility"); | ||
| const loginBtn = document.querySelector(".login-btn"); | ||
|
|
||
| // variables | ||
| const inputTags = document.querySelectorAll(".login-form input"); | ||
| const verifiationList = (() => { | ||
| const map = {}; | ||
| inputTags.forEach((el) => { | ||
| map[el.id] = false; | ||
| }); | ||
|
|
||
| return map; | ||
| })(); | ||
|
|
||
| // function | ||
| const verifyInputs = () => { | ||
| authFormUtils.verify(verifiationList) | ||
| ? authFormUtils.enableBtn(loginBtn) | ||
| : authFormUtils.disableBtn(loginBtn); | ||
| }; | ||
|
|
||
| // initialize | ||
| /** | ||
| * 뒤로 가기 등 페이지를 복구했을 때, 브라우저가 입력값을 복원하는 과정에서 요구사항의 focusout 이벤트가 발생하지 않음. | ||
| * 따라서, pageshow 이벤트 발생 후 email 값이 있는 경우 (입력값이 복원된 경우) 강제로 focusout 이벤트 실행 | ||
| */ | ||
| window.addEventListener("pageshow", () => { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 코드들이 일련의 흐름으로 실행된다기보다는 흩어져있어 파악이 어려워보여요. // 페이지 복원 시 폼 상태 검증
const validateFormOnRestore = () => {
const inputs = [
{ element: inputEmail, detail: detailEmail, field: 'email' },
{ element: inputPw, detail: detailPw, field: 'password' }
];
inputs.forEach(({ element, detail, field }) => {
if (element.value) {
validateField(element, detail, field);
}
});
};
// 이벤트 리스너 설정
const setupEventListeners = () => {
// 페이지 복원 이벤트
window.addEventListener('pageshow', validateFormOnRestore);
// 이메일 입력 필드
inputEmail.addEventListener('focusout', (e) => {
validateField(e.target, detailEmail, 'email');
});
// 비밀번호 입력 필드
inputPw.addEventListener('focusout', (e) => {
validateField(e.target, detailPw, 'password');
});
// 비밀번호 표시 토글
togglePw.addEventListener('click', togglePasswordVisibility);
};
// 초기화
setupEventListeners();
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이렇게 작성한다면 코드 중복을 제거하는데도 효과가 있고, 이벤트가 처리되는 흐름을 구조화할수있고, 모든 이벤트 리스너를 한곳에서 관리함으로써 각 이벤트 핸들러가 좀 더 명확한 의도와 역할을 가지고 실행될수있게끔 처리해줄수있겠죠? |
||
| if (inputEmail.value) inputEmail.dispatchEvent(new Event("focusout")); | ||
| }); | ||
|
|
||
| // events | ||
| inputEmail.addEventListener("focusout", (e) => { | ||
| const isCorrect = authFormUtils.isCorrectEmail(e.target.value); | ||
|
|
||
| if (!e.target.value) { | ||
| authFormUtils.changeDetail(e, detailEmail, "이메일을 입력해주세요"); | ||
| verifiationList.email = false; | ||
| } else if (!isCorrect) { | ||
| authFormUtils.changeDetail(e, detailEmail, "잘못된 이메일 형식입니다."); | ||
| verifiationList.email = false; | ||
| } else { | ||
| authFormUtils.changeDetail(e, detailEmail); | ||
| verifiationList.email = true; | ||
| } | ||
|
Comment on lines
+40
to
+51
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 유효성 검사 규칙에 따른 유효성 검사 결과를 반환하는 형태의 함수가 모듈화되어있지않아 페이지 단위의 js에서 매번 유효성 검사를 수행하기위한 코드 블락을 작성해야하는게 비효율적으로 보여요. 이런식으로 간소화해보면 어떨까요? 예시) // 유효성 검사 함수들
export const validators = {
email: (value) => {
if (!value) return { isValid: false, message: '이메일을 입력해주세요' };
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
return { isValid: false, message: '올바른 이메일 형식이 아닙니다' };
}
return { isValid: true, message: '' };
},
password: (value) => {
if (!value) return { isValid: false, message: '비밀번호를 입력해주세요' };
if (value.length < 8) {
return { isValid: false, message: '비밀번호는 8자 이상이어야 합니다' };
}
return { isValid: true, message: '' };
}
};
// 입력 필드 검증 함수
const validateField = (inputEl, detailEl, field) => {
const value = inputEl.value.trim();
const result = validators[field](value);
if (result.isValid) {
authFormUtils.changeDetail(inputEl, detailEl);
} else {
authFormUtils.changeDetail(inputEl, detailEl, result.message);
}
updateFormState(field, value, result.isValid);
}; |
||
|
|
||
| verifyInputs(); | ||
| }); | ||
|
|
||
| inputPw.addEventListener("focusout", (e) => { | ||
| const isCorrect = authFormUtils.isCorrectPw(e.target.value); | ||
|
|
||
| if (!e.target.value) { | ||
| authFormUtils.changeDetail(e, detailPw, "비밀번호를 입력해주세요"); | ||
| verifiationList.password = false; | ||
| } else if (!isCorrect) { | ||
| authFormUtils.changeDetail(e, detailPw, "비밀번호를 8자 이상 입력해주세요"); | ||
| verifiationList.password = false; | ||
| } else { | ||
| authFormUtils.changeDetail(e, detailPw); | ||
| verifiationList.password = true; | ||
| } | ||
|
|
||
| authFormUtils.verify(verifiationList) | ||
| ? authFormUtils.enableBtn(loginBtn) | ||
| : authFormUtils.disableBtn(loginBtn); | ||
|
|
||
| verifyInputs(); | ||
| }); | ||
|
|
||
| togglePw.addEventListener("click", (e) => { | ||
| if (inputPw.type === "password") { | ||
| inputPw.type = "text"; | ||
| e.target.src = "../img/icon/btn_visibility_on_24px.png"; | ||
| e.target.alt = "비밀번호 표시 켜짐"; | ||
| e.target.setAttribute("aria-pressed", "true"); | ||
| } else { | ||
| inputPw.type = "password"; | ||
| e.target.src = "../img/icon/btn_visibility_off_24px.png"; | ||
| e.target.alt = "비밀번호 표시 꺼짐"; | ||
| e.target.setAttribute("aria-pressed", "false"); | ||
| } | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| import authFormUtils from "./utils/authFormUtils.js"; | ||
|
|
||
| // DOM elements | ||
| const inputEmail = document.querySelector("#email"); | ||
| const inputNickname = document.querySelector("#nickname"); | ||
| const inputPw = document.querySelector("#password"); | ||
| const inputPwRepeat = document.querySelector("#password-repeat"); | ||
| const detailEmail = document.querySelector(".email-detail"); | ||
| const detailNickname = document.querySelector(".nickname-detail"); | ||
| const detailPw = document.querySelector(".password-detail"); | ||
| const detailPwRepaet = document.querySelector(".password-repeat-detail"); | ||
| const togglePw = document.querySelector(".toggle-visibility"); | ||
| const togglePwRepeat = document.querySelector(".toggle-repeat-visibility"); | ||
| const signUpBtn = document.querySelector(".sign-up-btn"); | ||
|
|
||
| // variables | ||
| const inputTags = document.querySelectorAll(".sign-up-form input"); | ||
| const verifiationList = (() => { | ||
| const map = {}; | ||
| inputTags.forEach((el) => { | ||
| map[el.id] = false; | ||
| }); | ||
|
|
||
| return map; | ||
| })(); | ||
|
|
||
| // function | ||
| const verifyInputs = () => { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 함수가 하는 역할이 input을 유효성검사하기보다는, 유효성 검사 결과를 button UI state에 반영하는일에 가깝지않을까요? |
||
| authFormUtils.verify(verifiationList) | ||
| ? authFormUtils.enableBtn(signUpBtn) | ||
| : authFormUtils.disableBtn(signUpBtn); | ||
| }; | ||
|
|
||
| // initialize | ||
| /** | ||
| * 뒤로 가기 등 페이지를 복구했을 때, 브라우저가 입력값을 복원하는 과정에서 요구사항의 focusout 이벤트가 발생하지 않음. | ||
| * 따라서, pageshow 이벤트 발생 후 email 값이 있는 경우 (입력값이 복원된 경우) 강제로 focusout 이벤트 실행 | ||
| */ | ||
| window.addEventListener("pageshow", () => { | ||
| if (inputEmail.value) inputEmail.dispatchEvent(new Event("focusout")); | ||
| }); | ||
|
|
||
| // events | ||
| inputEmail.addEventListener("focusout", (e) => { | ||
| const isCorrect = authFormUtils.isCorrectEmail(e.target.value); | ||
|
|
||
| if (!e.target.value) { | ||
| authFormUtils.changeDetail(e, detailEmail, "이메일을 입력해주세요"); | ||
| verifiationList.email = false; | ||
| } else if (!isCorrect) { | ||
| authFormUtils.changeDetail(e, detailEmail, "잘못된 이메일 형식입니다"); | ||
| verifiationList.email = false; | ||
| } else { | ||
| authFormUtils.changeDetail(e, detailEmail); | ||
| verifiationList.email = true; | ||
| } | ||
|
|
||
| verifyInputs(); | ||
| }); | ||
|
|
||
| inputNickname.addEventListener("focusout", (e) => { | ||
| const isCorrect = authFormUtils.isCorrectNickname(e.target.value); | ||
|
|
||
| if (!e.target.value) { | ||
| authFormUtils.changeDetail(e, detailNickname, "닉네임을 입력해주세요"); | ||
| verifiationList.nickname = false; | ||
| } else if (!isCorrect) { | ||
| authFormUtils.changeDetail(e, detailNickname, "잘못된 닉네임 형식입니다"); | ||
| verifiationList.nickname = false; | ||
| } else { | ||
| authFormUtils.changeDetail(e, detailNickname); | ||
| verifiationList.nickname = true; | ||
| } | ||
|
|
||
| verifyInputs(); | ||
| }); | ||
|
|
||
| inputPw.addEventListener("focusout", (e) => { | ||
| const isCorrect = authFormUtils.isCorrectPw(e.target.value); | ||
|
|
||
| if (!e.target.value) { | ||
| authFormUtils.changeDetail(e, detailPw, "비밀번호를 입력해주세요"); | ||
| verifiationList.password = false; | ||
| } else if (!isCorrect) { | ||
| authFormUtils.changeDetail(e, detailPw, "비밀번호를 8자 이상 입력해주세요"); | ||
| verifiationList.password = false; | ||
| } else { | ||
| authFormUtils.changeDetail(e, detailPw); | ||
| verifiationList.password = true; | ||
| } | ||
|
|
||
| if (inputPwRepeat.value) inputPwRepeat.dispatchEvent(new Event("focusout")); | ||
|
|
||
| verifyInputs(); | ||
| }); | ||
|
|
||
| inputPwRepeat.addEventListener("focusout", (e) => { | ||
| const isCorrect = authFormUtils.isCorrectPw(e.target.value); | ||
| const isSame = inputPw.value === e.target.value; | ||
|
|
||
| if (!e.target.value) { | ||
| authFormUtils.changeDetail(e, detailPwRepaet, "비밀번호를 입력해주세요"); | ||
| verifiationList["password-repeat"] = false; | ||
| } else if (!isCorrect) { | ||
| authFormUtils.changeDetail( | ||
| e, | ||
| detailPwRepaet, | ||
| "비밀번호를 8자 이상 입력해주세요" | ||
| ); | ||
| verifiationList["password-repeat"] = false; | ||
| } else if (!isSame) { | ||
| authFormUtils.changeDetail( | ||
| e, | ||
| detailPwRepaet, | ||
| "비밀번호가 일치하지 않습니다" | ||
| ); | ||
| verifiationList["password-repeat"] = false; | ||
| } else { | ||
| authFormUtils.changeDetail(e, detailPwRepaet); | ||
| verifiationList["password-repeat"] = true; | ||
| } | ||
|
|
||
| verifyInputs(); | ||
| }); | ||
|
|
||
| togglePw.addEventListener("click", (e) => { | ||
| if (inputPw.type === "password") { | ||
| inputPw.type = "text"; | ||
| e.target.src = "../img/icon/btn_visibility_on_24px.png"; | ||
| e.target.alt = "비밀번호 표시 켜짐"; | ||
| e.target.setAttribute("aria-pressed", "true"); | ||
| } else { | ||
| inputPw.type = "password"; | ||
| e.target.src = "../img/icon/btn_visibility_off_24px.png"; | ||
| e.target.alt = "비밀번호 표시 꺼짐"; | ||
| e.target.setAttribute("aria-pressed", "false"); | ||
| } | ||
| }); | ||
|
|
||
| togglePwRepeat.addEventListener("click", (e) => { | ||
| if (inputPwRepeat.type === "password") { | ||
| inputPwRepeat.type = "text"; | ||
| e.target.src = "../img/icon/btn_visibility_on_24px.png"; | ||
| e.target.alt = "비밀번호 반복 표시 켜짐"; | ||
| e.target.setAttribute("aria-pressed", "true"); | ||
| } else { | ||
| inputPwRepeat.type = "password"; | ||
| e.target.src = "../img/icon/btn_visibility_off_24px.png"; | ||
| e.target.alt = "비밀번호 반복 표시 꺼짐"; | ||
| e.target.setAttribute("aria-pressed", "false"); | ||
| } | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import { MIN_PW_LEN } from "../constant/constant.js"; | ||
|
|
||
| export const isCorrectEmail = (str) => { | ||
| const emailRegex = /^[a-zA-Z0-9]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; | ||
| return emailRegex.test(str); | ||
| }; | ||
|
|
||
| export const isCorrectNickname = (str) => { | ||
| return true; | ||
| }; | ||
|
|
||
| export const isCorrectPw = (pw) => { | ||
| return MIN_PW_LEN <= pw.length; | ||
| }; | ||
|
|
||
| export const changeDetail = (event, detailElement, text = "") => { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 함수 이름이 좀 모호하게 느껴져요. 결국 이 함수가 하는 일이 유효성 검사 상태에 따른 UI state 업데이트라면 updateFieldState 정도는 어떨까요? |
||
| const action = text ? "add" : "remove"; | ||
| const action2 = !text ? "add" : "remove"; | ||
| event.target.classList[action]("incorrect-input"); | ||
| event.target.classList[action2]("correct-input"); | ||
| detailElement.textContent = text; | ||
| text | ||
| ? detailElement.classList.remove("hidden") | ||
| : detailElement.classList.add("hidden"); | ||
| }; | ||
|
|
||
| export const verify = (verifiationList) => { | ||
| const verifiations = Object.values(verifiationList); | ||
| return verifiations.includes(false) ? false : true; | ||
| }; | ||
|
|
||
| export const disableBtn = (btnElement) => { | ||
| btnElement.disabled = true; | ||
| }; | ||
|
|
||
| export const enableBtn = (btnElement) => { | ||
| btnElement.disabled = false; | ||
| }; | ||
|
|
||
| const authFormUtils = { | ||
| isCorrectEmail, | ||
| isCorrectNickname, | ||
| isCorrectPw, | ||
| changeDetail, | ||
| verify, | ||
| disableBtn, | ||
| enableBtn, | ||
| }; | ||
| export default authFormUtils; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사소하지만, 이런식으로 변수로 만들어주면 함수 내부를 들여다보지않아도 실행 결과값에 대한 예측이 좀 더 쉬워지겠죠? :)