React

[React] Custom Hook

mhko411 2021. 10. 10. 20:18
728x90

React에는 useState, useEffect와 같은 Hook이 존재한다. Hook은 기존 클래스형 컴포넌트에서의 상태관리와 Life Cycle Method를 가볍게 사용할 수 있게 도와주며 로직의 재사용이 가능하도록 하였다. 또한 반복되는 로직을 자신만의 Hook을 만들어 구현하면 복잡함을 줄일 수 있다.


Custom Hook의 장점

자신만의 Hook을 만드는 것이 어떠한 장점이 있는지 알면 알맞게 사용할 수 있을 것이다. 먼저 Custom Hook을 사용한 사례를 통해 필요성과 장점을 알아본 후에 특징을 살펴보자.

 

아래는 React로 간단한 로그인 폼을 작성한 것이다. 이제 아이디와 비밀번호에 대한 유효성 검사를 위한 로직을 구성해보자.

function Login() {
  return (
    <>
      <form>
        <div>
          <label htmlFor="userId">아이디: </label>
          <input type="text" id="userId" />
        </div>
        <div>
          <label htmlFor="password">비밀번호</label>
          <input type="password" id="password" />
        </div>
        <button>로그인</button>
      </form>
    </>
  );
}

export default Login;

 

Hook을 적용하기 전

먼저 useState를 사용하여 아이디와 비밀번호의 상태를 관리한다. 그리고 아이디와 비밀번호의 onChange에 대입할 함수를 각각 정의하였다. handleChangeUserId는 입력한 아이디에 특수문자가 있을 때 알람을 주며 handleChangePassword는 입력한 비밀번호에 대문자가 있을 때 알람을 준다. 이를 위해 로직이 비슷한 두 개의 함수를 작성하였다. 

import { useState } from 'react';

function Login() {
  const [userId, setUserId] = useState('');
  const [password, setPassword] = useState('');

  const handleChangeUserId = (e) => {
    const checkSpc = /[~!@#$%^&*()_+|<>?:{}]/;
    if (checkSpc.test(e.target.value)) {
      alert('아이디에 특수문자는 입력할 수 없습니다.');
    } else {
      setUserId(e.target.value);
    }
  };

  const handleChangePassword = (e) => {
    const checkUpper = /[A-Z]/;
    if (checkUpper.test(e.target.value)) {
      alert('비밀번호에 대문자는 입력할 수 없습니다.');
    } else {
      setPassword(e.target.value);
    }
  };
  return (
    <>
      <form>
        <div>
          <label htmlFor="userId">아이디: </label>
          <input
            type="text"
            id="userId"
            value={userId}
            onChange={handleChangeUserId}
          />
        </div>
        <div>
          <label htmlFor="password">비밀번호</label>
          <input
            type="password"
            id="password"
            value={password}
            onChange={handleChangePassword}
          />
        </div>
        <button>로그인</button>
      </form>
    </>
  );
}

export default Login;

 

Hook 사용하기

Hook을 사용하기 전에는 비슷한 로직을 갖는 함수를 중복으로 정의하며 낭비를 하였다. 이제 Hook을 통해 재사용이 가능한 로직을 만들어보자.

 

먼저 유효성 검사를 위한 validator 함수를 만들어줬다. 아이디와 비밀번호에 대한 유효성 검사를 진행하는 두 개의 함수를 각각 만들어줬으며 추후에 만들어진 Hook에 전달되어 사용될 것이다.

// 아이디에 대한 유효성 검사를 진행
const userIdValidator = (value) => {
  const checkSpc = /[~!@#$%^&*()_+|<>?:{}]/;
  if (checkSpc.test(value)) {
    return {
      isValid: false,
      message: '아이디에 특수문자는 입력할 수 없습니다.',
    };
  }
  return { isValid: true, message: '' };
};

//비밀번호에 대한 유효성 검사를 진행
const passwordValidator = (value) => {
  const checkUpper = /[A-Z]/;
  if (checkUpper.test(value)) {
    return {
      isValid: false,
      message: '비밀번호에 대문자는 입력할 수 없습니다.',
    };
  }
  return { isValid: true, message: '' };
};

 

이제 Hook을 설계해보자. useInput이라는 Hook은 초기 값과 validator 함수를 전달받으며 onChange를 반환하고 onChange에 의해 갱신된 값을 반환한다. Hook 안에서는 onChange를 통해 전달받은 값을 validator 함수로 전달하여 유효성 검사를 진행하고 유효성 검사를 통과했을 때 useState를 통해 상태를 관리한다. 그렇지 않을 때는 알람을 출력한다.

const useInput = (initialValue, validator) => {
  const [value, setValue] = useState(initialValue);
  const onChange = (e) => {
    const result = validator(e.target.value);
    if (result.isValid) {
      setValue(e.target.value);
    } else {
      alert(result.message);
    }
  };

  return { value, onChange };
};

 

이제 위에서 설계한 Hook을 아래와 같이 사용할 수 있다. useInput을 통해 반환된 객체를 각각 userId와 password라는 변수로 받는다. 이를 활용하여 input의 value와 onChange를 지정해준다. 이를 통해 Hook을 사용하기 전 비슷한 역할을 하는 함수를 두 개 작성했지만 이제 하나의 함수로 합쳤으며 가독성을 높였다.

function Login() {
  const userId = useInput('', userIdValidator);
  const password = useInput('', passwordValidator);
  return (
    <>
      <form>
        <div>
          <label htmlFor="userId">아이디: </label>
          <input
            type="text"
            id="userId"
            value={userId.value}
            onChange={userId.onChange}
          />
        </div>
        <div>
          <label htmlFor="password">비밀번호</label>
          <input
            type="password"
            id="password"
            value={password.value}
            onChange={password.onChange}
          />
        </div>
        <button>로그인</button>
      </form>
    </>
  );
}

Custom Hook의 특징

  • Custom Hook의 이름은 use로 시작하도록 한다.
  • Custom Hook 내에 다른 Hook을 정의하여 사용할 수 있다.
  • 전달받는 인자와 반환되는 인자가 따로 정해진 것은 아니기 때문에 로직에 따라서 다르게 정의해도 된다.
  • 같은 Hook을 사용하는 컴포넌트가 존재할 수 있지만 상태는 독립적이다.

'React' 카테고리의 다른 글

[React] JSX  (0) 2021.11.16
[React] PropTypes 사용하기  (0) 2021.09.21
[React] 디자인 패턴 : Container - Presenter 패턴  (0) 2021.07.31
[React] Life Cycle  (0) 2021.07.23
[React] 리듀서 구조잡기  (0) 2021.07.14