Act99 기술블로그

[React] class 형 컴포넌트 vs function 형 컴포넌트 본문

개발팁저장소/react

[React] class 형 컴포넌트 vs function 형 컴포넌트

Act99 2022. 2. 7. 09:28

클래스형 컴포넌트 vs 함수형 컴포넌트

 

 

현재 리액트 공식문서에서는 함수형 컴포넌트와 훅을 사용하는 것을 권장하고 있다. 컴포넌트란 템플릿 기능 뿐만 아니라 데이터가 주여졌을 때 이에 맞추어 UI를 만들어주는 기능을 하며, 라이플 사이클 API 를 통해 컴포넌트가 화면에 나타날 때, 사라질 때, 변화할 때 작업들을 사용할 수 있다.

물론 클래스형 컴포넌트와 함수형 컴포넌트는 하는 일은 동일하지만, 코드 구조에서 약간의 차이가 있으며, 엄밀히 말하면 클래스 컴포넌트는 라이프 사이클 기능과 state 관리 기능이 코드에 구현되며, 함수형 컴포넌트는 hook 을 통해 라이프 사이클 기능과 state 관리 기능 코드를 짤 수 있다. 또한, 함수형 컴포넌트는 클래스형 컴포넌트보다 선언하기 좀 더 편하고, 메모리 자원을 덜 사용한다는 장점이 있다. 과거, 함수형 컴포넌트는 state 와 라이프 사이클 API 를 사용할 수 없다는 단점이 있었지만 현재, 훅에 의해 이 모든 문제가 해결되었다.

 

// 클래스형 컴포넌트

class ClassComponent extends Component {
  state = { count: 0 }
  
  increase = () => {
    this.setState(({ count }) => ({ count: count + 1 })
  }
  
  render() {
    return (
      <div>
        <p>count: {this.state.count}</p>
        <button onClick={this.increase}>INCREASE</button>
      </div>
    )
  }
}

 

// 함수형 컴포넌트

function FunctionComponent(props) {
  const [count, setCount] = useState(props.count)

  return (
    <div>
      <p>count: {count}</p>
      <button onClick={() => setCount(count + 1)}>INCREASE</button>
    </div>
  )
}

 

 

React Hooks

그렇다면 React Hooks에는 어떤 것들이 있을까?

 

 

1. useState

 

useState 는 가장 기본적인 훅이며, 컴포넌트 안에서 상태 관리를 해야하는 일이 발생한다면 이 훅을 사용한다.

import React, { useState } from 'react';

const Counter = () => {
  const [value, setValue] = useState(0);
  return (
    <div>
      <p>
        현재 카운터 값은 <b>{value}</b> 입니다.
      </p>
      <button onClick={() => setValue(value + 1)}>+1</button>
      <button onClick={() => setValue(value - 1)}>-1</button>
    </div>
  );
};

 

 

2. useEffect

 

useEffect는 리액트 컴포넌트가 리렌더링 될 때마다 특정한 작업을 수행하도록 설정할 수 있게 하는 Hook 이다. 클래스형 컴포넌트로 치면, componentDidMount, componentDidUpdate, componentWillUnmount 의 기능을 제공한다.

 

import React, { useState, useEffect } from 'react';

const Info = () => {
  const [name, setName] = useState('');
  const [nickname, setNickname] = useState('');
  useEffect(() => {
    console.log('렌더링이 완료되었습니다!');
    console.log({
      name,
      nickname
    });
  });

  const onChangeName = e => {
    setName(e.target.value);
  };

  const onChangeNickname = e => {
    setNickname(e.target.value);
  };

  return (
    (...)
  );
};

export default Info;

 

3. uesContext

 

이 훅을 사용하면 부모 컴퍼넌트에서 자식 컴퍼넌트로 넘기는 props 과정에서 props drilling 지옥에 빠지지 않는다. 또한, 함수형 컴포넌트에서 Context를 보다 쉽게 사용할 수 있다. 자세한 것은 이 블로그에 useContext vs 상태관리 툴 차이 게시물을 보면 된다.

 

import React, { createContext, useContext } from 'react';

const ThemeContext = createContext('black');
const ContextSample = () => {
  const theme = useContext(ThemeContext);
  const style = {
    width: '24px',
    height: '24px',
    background: theme
  };
  return <div style={style} />;
};

export default ContextSample;

 

4. useReducer

 

이 훅은 useState보다 컴포넌트에서 더 다양한 상황에 다양한 상태를 다른 값으로 업데이트 시켜주고 싶을 때 사용한다. 리듀서라는 개념은 Redux를 사용하는 유저는 쉽게 이해할 수 있으며, 이 리듀서 개념은 현재 상태와 업데이트를 위해 필요한 정보를 담은 action 값을 받아 새로운 상태를 반환해주는 함수이다. 이 리듀서 함수의 특징은 새로운 상태를 만들 때는 꼭 불변성을 지켜주어야 한다는 것이 있다.

 

 

5. useMemo

 

useMemo를 사용한다면 함수형 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있다. 단순히 useState 훅을 사용하면 state가 수정될 때마다 setState 함수가 반복적으로 호출되며, 렌더링 될 때마다 함수 내에서 계산이 진행되는데, useMemo Hook 을 사용하면 이러한 작업을 최적화 시킬 수 있다 .렌더링 하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행하며, 만약 원하는 값이 바뀐 것이 아니라면 이전에 연산했던 결과를 다시 사용하는 방식이다.

 

import React, { useState, useMemo } from 'react';

const getAverage = numbers => {
  console.log('평균값 계산중..');
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

const Average = () => {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState('');

  const onChange = e => {
    setNumber(e.target.value);
  };
  const onInsert = e => {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber('');
  };

  const avg = useMemo(() => getAverage(list), [list]);

  return (
    <div>
      <input value={number} onChange={onChange} />
      <button onClick={onInsert}>등록</button>
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
        <b>평균 값:</b> {avg}
      </div>
    </div>
  );
};

export default Average;

 

 

6. useCallback

 

useCallback 훅은 useMemo 와 상당히 비슷한 함수이다. 주로 렌더링 성능을 최적화해야 하는 상황에서 사용하며, 이벤트 핸들러 함수를 필요할 때만 생성할 수 있는 기능을 제공한다.

 

import React, { useState, useMemo, useCallback } from 'react';

const getAverage = numbers => {
  console.log('평균값 계산중..');
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

const Average = () => {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState('');

  const onChange = useCallback(e => {
    setNumber(e.target.value);
  }, []); // 컴포넌트가 처음 렌더링 될 때만 함수 생성
  const onInsert = useCallback(
    e => {
      const nextList = list.concat(parseInt(number));
      setList(nextList);
      setNumber('');
    },
    [number, list]
  ); // number 혹은 list 가 바뀌었을 때만 함수 생성

  const avg = useMemo(() => getAverage(list), [list]);

  return (
    <div>
      <input value={number} onChange={onChange}  />
      <button onClick={onInsert}>등록</button>
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
        <b>평균값:</b> {avg}
      </div>
    </div>
  );
};

export default Average;

 

그리고, 다음 예시를 보면 useMemo 와 useCallback이 어느정도 동일한 기능을 제공한다고 볼 수 있다.

 

useCallback(() => {
  console.log('hello world!');
}, [])

useMemo(() => {
  const fn = () => {
    console.log('hello world!');
  };
  return fn;
}, [])

 

7. useRef

 

useRef Hook 은 함수형 컴포넌트에서 ref를 쉽게 사용할 수 있도록 해준다. useRef 를 사용하여 ref 를 설정하면 useRef 를 통해 만든 객체 안의 current 값이 실제 엘리먼트를 가르키게 된다.

 

 

 

이 외에도 react hook form, useInput, useWindowSize 등의 커스텀 훅이 존재하며, 다른 개발자들이 만든 Hooks 도 라이브러리로 설치하여 사용할 수 있다.