초보 개발자의 성장 일기

로그인 유효성 검사 시 효율적인 렌더링 본문

Development/React JS

로그인 유효성 검사 시 효율적인 렌더링

YUNA 2023. 12. 9. 15:32

이메일형식 체크 및 비밀번호 길이를 체크할 때 유효성 검사를 실시해야 한다. 유효성 검사를 실시하면 이메일과 비밀번호의 상태를 계속 확인해야하는데 HTTP requset를 보낼 때 너무 많이 보내지지 않을까?

 

const [enteredEmail, setEnteredEmail] = useState('');
const [enteredPassword, setEnteredPassword] = useState('');

 

const emailChangeHandler = (event) => {
	setEnteredEmail(event.target.value);
};

const passwordChangeHandler = (event) => {
	setEnteredPassword(event.target.value);
};

 

 

우선 이메일과 비밀번호의 상태를 업데이트 해줄 useState를 만들어 준다. 이 상태를 input의 value 이벤트에 넣어주고 값이 바뀔때마다 수시로 상태를 업데이트 해줄 핸들러 함수를 만들어서 이 함수는 input의 onChange 이벤트에 넣어준다. 

 

const [emailIsValid, setEmailIsValid] = useState();
const [passwordIsValid, setPasswordIsValid] = useState();

 

const validateEmailHandler = () => {
    setEmailIsValid(enteredEmail.includes('@'));
};

const validatePasswordHandler = () => {
    setPasswordIsValid(enteredPassword.trim().length > 6);
};

유효성 검사를 실시해줄 useState도 만들어주고 이메일은 '@'가 들어가는지, 비밀번호는 길이가 6자 이상인지 확인할 핸들러 함수를 만들어 input의 onBlur 이벤트에 이 함수를 넣어준다.

 

onBlur

엘리먼트(또는 자식 엘리먼트)에서 포커스가 사라졌을 때 호출된다. 예를 들어, 유저가 포커스된 텍스트 인풋의 바깥 영역을 클릭했을 때 호출된다.

 

const submitHandler = (event) => {
    event.preventDefault();
    props.onLogin(enteredEmail, enteredPassword);
};

폼이 제출될때마다 실행해 줄 핸들러함수를 만들어서 이 함수는 자동으로 새로고침하는 이벤트를 막고 입력된 이메일과 패스워드 값을 props를 통해 넣어준다. 이 핸들러는 form의 onSubmit 이벤트로 넣어준다.

 

useEffect(() => {
    setFormIsValid(
    	enteredEmail.includes('@') && enteredPassword.trim().length > 6
    );
}, [enteredEmail, enteredPassword]);

useEffect를 사용해서 의존성 배열에 이메일 값과 비밀번호 값을 넣어 두 값이 변할때만 렌더링이 되도록 해 주었다.

 

useEffect(() => {
	console.log('Checking form validity');
    setFormIsValid(
    	enteredEmail.includes('@') && enteredPassword.trim().length > 6
    );
}, [enteredEmail, enteredPassword]);

콘솔을 찍어서 렌더링이 되는것을 확인해봤다. 이 때 콘솔에는 키가 입력될 때마다 실행이 되었다. 이때 사용자를 확인하기위해  HTTP request를 보내게 될 때 키가 입력될때마다 요청을 보내게 되면 너무 많은 요청을 보내게된다. 이때 불필요한 네트워크 트래픽을 만들게된다. 또한 키 한글자 한글자 입력할 때 마다 상태가 업데이트되는 것은 불필요하다. 이 때 우리는 일정량의 키 입력을 수집하거나 또는 키 입력 후 일정시간 동안은 잠시 상태 업데이트가 중지되는 것이다. 예를들어 사용자가 이메일을 계속해서 입력중일 때는 굳이 유효한 이메일인지 확인하지 않아도 된다. 유효성 검사는 사용자가 타이핑을 멈출 때 확인하면 된다.

 

사용자가 어느정도 시간이 지나면 다 입력했을 것이라고 생각하고 그 후에 유효성 검사를 실시하는 것이다. 이것을 디바운싱이라고 한다. 사용자의 입력을 그룹화 하는 것이다. 이때 setTimeout 함수를 사용하면 된다.

 

useEffect(() => {
	setTimeout(() => {
      console.log('Checking form validity');
      setFormIsValid(
      	enteredEmail.includes('@') && enteredPassword.trim().length > 6
      );
    }, 500);
}, [enteredEmail, enteredPassword]);

setTimeout을 설정하면 함수를 실행하기 전에 두번째 인자로 지정한 밀리초를 기다려야 한다. 타이머가 한번에 하나만 실행되도록 하기 위해서는 키가 입력되면 지워야한다. 따라서 완료되는 타이머는 하나뿐이다. 이 때 첫번째 인수로 전달하는 함수는 return값을 반환할 수 있는데 이 반환값은 함수여야한다. 반환하는 함수를 cleanup(클린업)함수라고 한다.

 

useEffect(() => {
    const identifier = setTimeout(() => {
      console.log('Checking form validity');
      setFormIsValid(
        enteredEmail.includes('@') && enteredPassword.trim().length > 6
      );
    }, 500);

    return () => {
      clearTimeout(identifier);
    };
  }, [enteredEmail, enteredPassword]);

 

클린업함수는 첫번째 렌더링을 제외한 useEffect함수가 실행되기 전 클린업 함수가 실행이된다. 이벤트를 특정한 컴포넌트가 DOM에서 마운트 해제될때마다 실행이 된다.컴포넌트가 재사용될 때 실행이 된다. 첫번째 사이드이펙트 함수가 실행되기 전에는 실행되지 않고 그 이후에는 실행이 된다.

 

언제 실행이 되는지 정확하게 확인하기 위해 콘솔을 찍어봤다.

useEffect(() => {
    setTimeout(() => {
      console.log('Checking form validity');
      setFormIsValid(
        enteredEmail.includes('@') && enteredPassword.trim().length > 6
      );
    }, 500);

    return () => {
      console.log('clean up');
    };
}, [enteredEmail, enteredPassword]);

 

처음 렌더링 될 때 useEffect안에 있는 유효성 검사 함수가 실행이 되는걸 알 수 있다. 여기서 클린업 함수가 실행이 되지 않는 이유는 첫번째 사이드 이펙트가 실행되기 전에는 실행이 되지 않기 때문이다. 여기서 한문자라도 입력하면 클린업함수가 실행이 된다.

 

이메일이 입력중일 때는 유효성 검사 함수가 실행되지 않고 클린업 함수만 실행이 되다가 입력이 멈추고 나서야 유효성 검사 함수가 실행이 된다. 

 

설정된 타이머의 식별자를 반환할 때 이 식별자를 이용하여 타이머를 지울 수 있다.

useEffect(() => {
    const identifier = setTimeout(() => {
      console.log('Checking form validity');
      setFormIsValid(
        enteredEmail.includes('@') && enteredPassword.trim().length > 6
      );
    }, 500);

    return () => {
      console.log('clean up');
      clearTimeout(identifier);
    };
}, [enteredEmail, enteredPassword]);

 

클린업 함수 내에서 내장함수인 clearTimeout을 설정하면 클린업 함수가 실행될 때 마다 클린업 함수가 실행되기 전에 설정된 타이머를 지울 수 있다. 새로운 타이머가 실행되기 전 마지막 타이머를 지운다. 

 

이렇게 하면 HTTP request를 보낼 때 키가 입력될 때 마다 보내는 것이 아닌 한번만 보냈을 것이다.

 

여기서 문제점이 하나 생겼다. 유효성 검사를 마치고 이미 유효한 값에 문자를 추가할 때도 계속해서 유효성 검사가 실시된다. 이미 유효성 검사를 통과했는데 그 후 문자를 추가한다고 해서 굳이 유효성 검사를 다시할 필요가 없는 것이다.

 

 

해결 방법은 객체 디스트럭처링을 이용하면 된다. 객체의 특정 속성을 추출하는 것이다. 객체에는 해당 값을 추출하기 위해 별칭 사용이 가능하다.

const { isValid: emailIsValid } = emailState;
const { isValid: passwordIsValid } = passwordState;

emailState에서 isValid의 속성을 추출하기 위해서 새 상수 emailIsValid이라는 별칭을 할당해서 저장하는 것이다. emailIsValid는 값 할당이 아닌 별칭할당이다.

 

이제 emailIsValid와 passwordIsValid가 사용이 가능해졌다.

 

useEffect(() => {
    const identifier = setTimeout(() => {
      console.log('Checking form validity');
      setFormIsValid(emailIsValid && passwordIsValid);
    }, 500);

    return () => {
      console.log('clean up');
      clearTimeout(identifier);
    };
}, [emailIsValid, passwordIsValid]);

앞에 사용했던 useEffect에 의존성 배열과 유효성 검사 함수 안에 상수를 사용하여 바꿔주었다.

 

이제 값만 변경되고 유효성이 변경되지 않으면 useEffect는 실행되지 않는다. 

유효성이 통과되고 난 후 타이핑을 멈췄다가 다시 작성을 해도 콘솔이 다시 나타나지 않는 것을 확인 할 수 있다.

 

이렇게 함으로써 useEffect는 최적화하고 이펙트가 불필요하게 실행되는 것을 피할 수 있다.