React Hooks로 상태 관리하기: useState

Posted at 2020. 4. 23. 04:09 | Posted in 프로그래밍

React Hooks는 왜 Hooks일까? "훅!" 하면 복싱에서 "레프트 훅! 라이트 훅!" 을 외치며 주먹을 꽂는 모습이 떠오르지 않는가? 그렇다면 React Hooks는 무엇을 어디에 "훅!" 꽂는 것일까? 내 생각은 이렇다. 본래 stateless 컴포넌트를 구현할 때 주로 사용하는 function 컴포넌트에 state 기능을 "훅!" 꽂아 넣어서 stateful function 컴포넌트로 만드니까 React Hooks라는 것. 물론, state 기능 이외에도 많은 것들을 꽂아 넣을 수 있다.#

React Hooks가 나오기 전후로 컴포넌트의 상태 관리는 어떻게 하는지 비교해보고 최적화해보자!

Hooks 이전 Stateful Class Component

React는 class 컴포넌트와 function 컴포넌트를 지원한다. 다 그런 것은 아니지만, 예전에는 보통 class 컴포넌트는 stateful 컴포넌트, function 컴포넌트는 stateless 컴포넌트라 했다. 자체적으로 컴포넌트의 상태가 변하면 stateful이고 자체적으로 컴포넌트의 상태가 변하지 않으면 stateless다. stateless 컴포넌트의 업데이트는 주로 stateful 컴포넌트에서 입력을 처리하고 state를 갱신해서 stateless 컴포넌트에 props로 state의 특정 값을 넘겨주는 형태로 이루어진다.

기본적인 React 예시를 보면, class 컴포넌트는 숫자 카운터 같은 현란하고 멋진 것을 만들고 function 컴포넌트는 단순한 HTML 태그의 껍데기(컨테이너) 정도로만 사용했다. 아래 class 컴포넌트의 코드와 결과물인 숫자 카운터를 보라. 얼마나 영롱한지!

class Counter extends React.Component {
  static defaultProps = {
    step: 1,
  };

  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  handleIncrease = () => {
    this.setState((state, props) => ({
      count: state.count + props.step,
    }));
  };

  handleDecrease = () => {
    this.setState((state, props) => ({
      count: state.count - props.step,
    }));
  };

  render() {
    const { count } = this.state;
    return (
      <>
        <input type="number" value={count} />
        <button onClick={this.handleIncrease}>+</button>
        <button onClick={this.handleDecrease}>-</button>
      </>
    );
  }
}

이것만 보면 class 컴포넌트가 멋지게 보일 수 있다. 그러나 현실은 다르다. class 컴포넌트는 부피가 커질수록 코드가 복잡하고 더러워진다. 예로, 상태 관리 로직을 재사용하려면 HOC#, render props#1 #2 등을 써서 코드를 읽기 어렵게 바꿔야 한다. 또, 프로토타입 기반 프로그래밍 언어인 자바스크립트의 특성 때문에, 이벤트의 콜백으로 호출된 함수가 class의 메소드라면 여기서 사용하는 this가 class 인스턴스의 this가 아니라서 사람도 기계도 이 this가 저 this인지 헷갈린다.# 특히나 this는 변경 가능한(mutable) 값이라서 setStatesetTimeout처럼 비동기성 함수에서 막 쓰다가는 끔찍한 버그를 마주한다.# 결국에는 이런저런 불편을 없애고자 React의 상태 관리 기능을 대체할 수 있는 Redux와 MobX 같은 라이브러리가 나타나기까지 한다.

Hooks 이후 Stateful Function Component

React Hooks는 function 컴포넌트에 특정 기능을 "훅!" 꽂아 넣는 것으로 기존 React의 기능(API)을 아주 쉽게 사용할 수 있게 한다. Hooks의 간단한 구조 덕분에 useSWR, useForm 등의 수많은 Hooks 라이브러리가 나오고 있다. 이런 라이브러리들은 차후에 더 다루어보고, 여기서는 제일 기본적인 Hook인 useState로 stateful class 컴포넌트를 stateful function 컴포넌트로 대체해보자.

function CounterWithHooks({ step = 1 }) {
  const [count, setCount] = useState(0);

  function handleIncrease() {
    setCount(count + step);
  }

  function handleDecrease() {
    setCount(count - step);
  }

  return (
    <>
      <input type="number" value={count} />
      <button onClick={handleIncrease}>+</button>
      <button onClick={handleDecrease}>-</button>
    </>
  );
}

Hooks 이전과 이후를 비교하면 props에 기본값을 할당하는 defaultProps를 제거하고 props를 destruct하면서 기본값을 할당한 점이 가장 먼저 눈에 띈다. ES6 문법을 제대로 활용한다. 또, 이벤트의 콜백으로 사용하던 arrow function 멤버 변수를 그냥 함수로 선언했다. 이는 콜백 함수가 CounterWithHooks 함수의 scope를 공유하므로 this를 사용할 필요가 없어서 생긴 변화이다. 비동기로 동작해서 난감한 문제가 발생하는 setState도 동기식으로 동작하는 useState의 set 함수로 대체하였다.

갑자기 내용을 정리하자면, React Hooks는 참 쉽고 직관적이다. 함수형 프로그래밍의 장점을 제대로 활용하고 있고, 문서를 읽다 보면 개발할 맛이 난다.

더 읽을거리

Name __

Password __

Link (Your Website)

Comment