에러 메세지를 출력하는 기능을 focusout으로 만들었더니 제거 기능도 추가해야했다.
> +//포커스 인 이벤트
+emailInput.addEventListener("focusin", function (e) {
+ e.target.style.border = "none";
+ const message = document.querySelector(".email-error-message");
+ if (message) {
+ message.remove();
+ }
+});
이벤트 유형 중 ‘input’ 유형을 사용하여 값이 변화할 때 유효성 검사를 실시하여 에러 메세지를 실시간으로 출력하도록 변경했다.
form.addEventListener("input", e => {
setErrorMessage(e);
checkFormValidity();
});
기존 생성 방식
> +export const emailErrorMessage = (e, text) => {
+ //빈 값일 경우 빨간 테두리 추가
+ e.target.style.border = "1.5px solid red";
+ //하단에 메세지 추가
+ const message = document.createElement("span");
+ message.textContent = `${text}`;
+ message.style.color = "#F74747";
+ message.style.fontSize = "15px";
+ message.style.fontFamily = "Pretendard";
+ message.style.fontWeight = "600";
+ message.style.marginTop = "-16px";
+ message.style.marginBottom = "24px";
+ message.style.marginLeft = "16px";
+ message.classList.add("email-error-message");
+ message.classList.add("error");
+ e.target.after(message);
이런 생성 로직을 인풋마다 달아놨다.
이벤트를 감지하면 어떤 메세지를 표시할 것인지 text인자로 전달하는 구조로 되어있었다.
리팩토링 결과
import {
checkEmailValidation,
checkPasswordValidation,
checkPasswordCheckValidation,
checkNicknameValidation,
} from "../utils/formValidation.js";
//에러 메세지 출력 함수
const showErrorMessage = (errorDisplayElement, errorMessage) => {
errorDisplayElement.textContent = errorMessage;
};
export const setErrorMessage = e => {
const password = document.querySelector("#password")?.value;
const passwordCheck = document.querySelector("#password-check")?.value;
let errorMessage = "";
const value = e.target.value;
const currentTarget = e.target.id;
const errorDisplayElement = e.target.nextElementSibling;
switch (currentTarget) {
case "email":
errorMessage = checkEmailValidation(value);
break;
case "password":
errorMessage = checkPasswordValidation(value);
break;
case "password-check":
errorMessage = checkPasswordCheckValidation(password, passwordCheck);
break;
case "nickname":
errorMessage = checkNicknameValidation(value);
break;
default:
break;
}
showErrorMessage(errorDisplayElement, errorMessage);
};
이벤트 위임을 사용해서 form에 이벤트 리스너를 추가
유효성 검사 함수를 통해 에러 메세지를 생성
에러 메세지는 이미 존재하는 HTML 요소의 textContent로 추가
이미 존재하는 HTML 요소에 CSS 스타일링을 해뒀기 때문에 자바스크립트로 style을 추가할 필요 없다.
기존 코드
> +//회원가입 활성화 상태 변경하기
+form.addEventListener("keyup", e => {
+ //에러 메세지 존재 유무 확인
+ const errorMesseges = document.querySelectorAll(".error");
+ if (
+ errorMesseges.length === 0 &&
+ emailCheck(emailInput.value) &&
+ nicknameInput.length !== 0 &&
+ passwordInput.value.length >= 8 &&
+ passwordCheck.value.length >= 8
+ ) {
+ loginBtn.disabled = false;
+ loginBtn.style.backgroundColor = "#3692ff";
+ } else {
+ loginBtn.disabled = true;
+ loginBtn.style.backgroundColor = "#9ca3af";
+ }
+});
리팩토링 결과
const loginBtn = document.querySelector("#login-btn");
const signupBtn = document.querySelector("#signup-btn");
function getElementValue(selector) {
return document.querySelector(selector)?.value.trim();
}
const isValid = (validationFunction, ...args) => {
return validationFunction(...args) === undefined;
};
export function checkFormValidity() {
const email = getElementValue("#email");
const password = getElementValue("#password");
const passwordCheck = getElementValue("#password-check");
const nickname = getElementValue("#nickname");
const isEmailValid = isValid(checkEmailValidation, email);
const isPasswordValid = isValid(checkPasswordValidation, password);
const isPasswordCheckValid = isValid(
checkPasswordCheckValidation,
password,
passwordCheck
);
const isNicknameValid = isValid(checkNicknameValidation, nickname);
if (loginBtn) {
isEmailValid && isPasswordValid
? (loginBtn.disabled = false)
: (loginBtn.disabled = true);
}
if (signupBtn) {
isEmailValid && isNicknameValid && isPasswordValid && isPasswordCheckValid
? (signupBtn.disabled = false)
: (signupBtn.disabled = true);
}
}
변수 설정
요소 : 로그인 버튼, 회원가입 버튼
값 : 각 input의 값
참 거짓을 가지는 불리언 타입 변수명은 is로 시작하는 것이 일반적인 네이밍 규칙이다.
(이것을 잘 모르고 조건문을 직접 나열해서 가독성이 떨어졌다.)
함수 설정
가독성을 증가 시키기 위해 함수getElementValue(selector)
를 만들어서 사용했다.
로그인 페이지에는 닉네임 인풋과 비밀번호 재확인 인풋이 없어서 .value에 접근하는 과정에서 에러가 발생하며 멈추는 결과가 발생하는데 체이닝 연산자를 사용하여 값이 없으면 undefined를 반환하고 넘어가도록하여 스크립트 중단을 방지한다.
isValid()함수 생성
(함수도 불리언 값을 반환하는 함수는 변수명과 동일한 네이밍 규칙으로 만들고 조건식은 모듈화해서 가독성을 높일 수 있다.)
disabled와 style을 조작했는데 disabled의 의사클래스를 사용해서 스타일을 변경할 수 있다.
.submit:disabled {
background-color: var(--gray400);
}
에러 메세지를 함수에 하드코딩해서 인자로 전달했었다.
리팩토링 결과
// messages.js
export const ERROR_MESSAGES = {
emailEmpty: "이메일을 입력해주세요",
emailFormat: "잘못된 이메일 형식입니다",
password: "비밀번호를 입력해주세요",
passwordLength: "비밀번호는 8자 이상이어야 합니다",
passwordWrong: "비밀번호가 일치하지 않습니다",
nicknameEmpty: "닉네임을 입력해주세요",
};
// formValidation.js
//이메일 유효성 검사
export const checkEmailValidation = value => {
if (isEmpty(value)) {
return ERROR_MESSAGES.emailEmpty;
} else if (!checkEmailValidity.test(value)) {
return ERROR_MESSAGES.emailFormat;
}
};
메세지를 객체로 만들어 다른 파일에 분리하였다.
매직 넘버, 매직 리터럴이라고 불리는 이런 값은 의미가 담기지 않은 값이라 가독성을 저하시킨다. 그래서 보통 의미를 담은 변수명을 만들어서 상수로 저장하여 사용한다.
기존 코드
> + const atPosition = email.indexOf("@");
+ const dotPosition = email.lastIndexOf(".");
+ return atPosition > 0 &&
+ dotPosition > atPosition + 1 &&
+ dotPosition + 2 < email.length
+ ? true
+ : false;
조건문
이 조건을 모두 참이어야 참을 반환하는 코드를 작성했다.
그러나 정규표현식을 사용할 수 있다.
리팩토링 결과
const checkEmailValidity = /^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\-]+\.[A-Za-z0-9\-]+/;
checkEmailValidity.test(value); // ture or false
.test()
메서드는 정규 표현식을 사용하여 문자열이 특정 패턴에 맞는지 검사한다.
이메일 형식, 전화번호 형식 등 패턴을 검증할 때 일반적으로 사용되기 때문에 정규표현식으로 검증하는 방법이 있다는 것을 기억해야겠다.
정규 표현식이 사용되는 경우
focusout
, input
등)을 활용하여 사용자 인터랙션에 대응하는 방법을 배웠습니다.