티스토리 뷰

React 16.8 버전부터 FC - Functional Component(함수형 컴포넌트)에 State를 사용할 수 있도록 해 주는 Hooks 라는 개념이 생겼습니다. 스스로 Hooks 는 별로 중요한 기능이 아니라고 생각했었지만, React 개발자들 사이에서는 Hooks 가 중요하게 여겨지고 있었습니다. 이에 의문을 가지고 Hooks 와 함수형 컴포넌트에 대해서 학습하였고 함수형 컴포넌트와 Hooks 의 중요성에 공감하게 되어 이번 포스팅에서는 이에 대해 정리해보고자 합니다.

 


왜 Functional Component(함수형 컴포넌트)에 집중하는가

Hook을 보며 많은 개발자들이 클래스 컴포넌트보다 함수형 컴포넌트를 더 선호하고 둘의 차이를 좁히기위해 노력하고 있다는걸 알게되었습니다. 그래서 함수형 컴포넌트를 선호하는 이유에 대해 먼저 알아보려고 합니다.

// CLASS COMPONENT
class TestComponent extends React.Component {
  render() {
    return (
      <h1>
      	테스트 {this.props.text}
      </h1>
    )
  }
}

// FUNCTIONAL COMPONENT
function TestComponent(props) {
  return (
    <h1>
      	테스트 {this.props.text}
    </h1>
  )
}

위의 예제와 같이 클래스 컴포넌트는 클래스를 기반으로 작성되는 컴포넌트이며, 함수형 컴포넌트는 함수를 기반으로 작성되는 함수형 컴포넌트 입니다. 두 컴포넌트의 가장 큰 차이점은 라이프 사이클이라고 할 수 있습니다.

 

클래스 컴포넌트는 componentDidMount, getDerivedStateFromProps등의 React Component의 라이프사이클을 사용할 수 있지만 기존의 함수형 컴포넌트는 이러한 라이프사이클을 사용할 수 없었습니다. 그래서 온전히 render 만 구현할 수 있었다고 볼 수 있습니다.

 

(이러한 라이프사이클을 함수형 컴포넌트에서도 사용할 수 있도록 Hooks가 등장했지만 이는 잠시 뒤에 설명하도록 하겠습니다)

 

그렇다면 기능도 부실한 함수형 컴포넌트가 무슨 장점이 있는건가 의문이 드실겁니다. 이는 클래스 컴포넌트의 문제점에 있습니다. 간단한 예제를 살펴보겠습니다.

 

 

Class, Functional Component의 timeout 예제
예제에서 발생하는 에러

위의 두 컴포넌트는 사용자를 선택하고 follow 버튼을 누르면 3초 이후에(setTimeout) 선택한 사용자의 이름을 출력하는 예제입니다. 영상을 보면 두 컴포넌트 모두 정상동작하는 것 처럼 보이지만 클래스 컴포넌트에서 유저를 follow 한 후 3초가 지나기 이전에 다른 유저를 follow 하면 첫번째 유저가 아닌 두번째로 선택한 유저에대한 알림이 발생합니다.

 

클래스 컴포넌트는 props를 재사용 하기 때문에 생기는 문제 입니다. 타임아웃이 지나기전에 props가 두 번째 유저로 변경되었기 때문에 두 번째 유저가 출력되는 것 입니다. 자세한 내용은 Overreacted에서 확인하실 수 있습니다. 간단히 정리하자면 클래스 컴포넌트는 props를 재사용하기 때문에 개발자의 예상과 다르게 동작할 수 있는 문제점이 존재합니다. 따라서 별도의 예방 작업을 해 주어야 하지만 함수형 컴포넌트는 이러한 문제점이 발생하지 않습니다.

 

이 외에도 함수형 컴포넌트는 클래스 컴포넌트에 비해 성능과 가독성이 좋고 테스트 하기 쉽습니다. 또한 말그대로 함수형 컴포넌트이기에 함수형 프로그래밍의 장점을 활용하기에도 유리합니다.


Hooks - Functional Component에 날개를 달다(?)

React 16.8 버전부터 함수형 컴포넌트에서 state와 라이프사이클을 사용할 수 있는 Hook이라는 개념이 등장했습니다. 때문에 기존에 동일시 되었던 Stateless Component와 Functional Component는 개념적으로 분리가 되었다고 볼 수 있습니다.

 

State hook

Hooks에는 state를 사용할 수 있는 useState함수가 추가되었습니다. 인자로 초기 state값을 넣어주면 state와 state를 업데이트 시켜주는 함수가 배열 형태로 반환됩니다. 아래의 간단한 count 예제를 통해 자세히 살펴보겠습니다.

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

초기값이 0인 state를 선언하였고 버튼을 클릭하면 count를 증가시키는 컴포넌트 입니다. 위처럼 useState를 사용하면 기존에 state를 활용할 수 없었던 함수형 컴포넌트에서도 state를 사용할 수 있습니다. 사용하는 state의 수는 제한이 없기때문에 useState를 여러번 사용해도 됩니다.

 

Effect Hook

state hook외에도 비교적 간단한 라이프사이클을 사용하기 위해 useEffect라는 함수도 추가되었습니다. useEffect는 컴포넌트가 마운트 되었을 때(componentDidMount), 컴포넌트가 업데이트 되었을 때(componentDidUpdate), 컴포넌트가 마운트 해제될때(componentWillUnmount) 세가지 경우의 라이프 사이클을 사용할 수 있습니다.

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

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

위의 예제는 componentDidMount와 componentDidUpdate 라이프사이클과 동일하게 동작하는 effect hook예제 입니다. 위의 예제처럼 기본적인 effect hook는 컴포넌트가 렌더링 될 때마다 호출됩니다.

 

두 번째 예제는 componentWillUnmount 라이프사이클과 비슷한 동작을 하는 예제 입니다.

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

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);

    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

effect hook의 반환값으로 함수를 반환해주는데요, 반환해준 함수는 컴포넌트의 마운트가 해제될때나 컴포넌트가 업데이트 되어 다음 effect가 호출되기 이전에 호출됩니다. 따라서 함수형 컴포넌트에서 componentWillUnmount기능이 필요한 경우 위의 예제처럼 사용할 수 있습니다.

 

아래 마지막 예제는 componentDidMount처럼 오직 컴포넌트가 마운트 되었을 때에만 effect hook를 호출하는 방법입니다.

import React, { useState, useEffect } from 'react';
function ComponentDidMountExample(props) {

  useEffect(() => {
    TestAPI.request();
  }, []);

  return (
	<div>
		test
	</div>
  );
}

useEffect의 두번째 인자로 props나 state값을 배열로 넣어줄 수 있습니다. 따라서 넣어준 값들이 변경되었을 때에만 effect hook가 호출됩니다. 하지만 빈 배열을 넣어준다면 업데이트를 감시할 필요가 없기 때문에 컴포넌트가 마운트 되었을때만 effect hook가 호출되어 componentDidMount처럼 동작할 수 있습니다.

 

 

 마치며

React Hooks 는 점진적으로 클래스컴포넌트에서만 존재하는 기능들을 옮겨올예정이라고 합니다. 하지만 릴리즈된지 얼마 되지않아 버그나 문제점들도 존재할 수 있기 때문에 지금 당장 구현된 것들을 갈아엎고 급하게 도입하기보다 앞으로 추가되는 기능, 프로젝트들에 천천히 도입해보는것도 좋을 것 같습니다.

 

저의 계획은 앞으로 React를 진행하는 프로젝트에 Class Component는 최소화 시키고 대부분의 컴포넌트를 Functional Component로 작성하려고 합니다. Functional Component의 장점과 성능을 보면 사용하지 않을 이유가 없는것같습니다.

 

React Hooks 에 대한 자세한 내용은 React의 공식 문서에서 찾아볼 수 있습니다.

 

Hooks at a Glance – React

A JavaScript library for building user interfaces

reactjs.org

 

Introducing Hooks – React

A JavaScript library for building user interfaces

reactjs.org

 

How Are Function Components Different from Classes?

They’re a whole different Pokémon.

overreacted.io

 

[번역] React Stateless Functional Components: 우리가 간과하고 있는 9가지

아래 내용은 https://hackernoon.com/react-stateless-functional-components-nine-wins-you-might-have-overlooked-997b0d933dbc를 해석한 내용입니다. 의역과 오역이…

medium.com

댓글
  • 프로필사진 Re-react 무슨 말씀을 하시는 건지; error 관련 라이프사이클 메서드를 제외하면 모든 클래스의 라이프사이클 기능을 Hooks 로 구현할 수 있습니다. 2019.10.03 01:59
  • 프로필사진 박스여우 제 글에 오류가 있었다면 죄송합니다.
    하지만 해당 글에서는 Hook을 통해 함수형 컴포넌트에서도 라이프사이클을 구현할 수 있다고 설명되어 있습니다.
    어느 부분을 보고 말씀하시는건지 정확하게 말씀해 주시면 정정하도록 하겠습니다.
    감사합니다~
    2019.10.04 11:44 신고
댓글쓰기 폼
Total
354,596
Today
15
Yesterday
658
링크
«   2019/11   »
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함