[React] - Class 컴포넌트 vs Function 컴포넌트

Yoochan·2022년 2월 19일
8

React에서 컴포넌트를 ClassFunction으로 모두 만들 수 있다.

Class는 React에서 제공하는 Component라는 클래스를 extends, 상속해서 만들 수 있다.
Function은 간단하게 함수로 만들 수 있다.

Class에는 상태, 데이터를 담을 수 있는 state라는 오브젝트가 들어있다. 그래서 state의 내용이 업데이트 될 때마다 렌더 함수가 호출이 되면서 업데이트된 내용이 보여진다. 그리고 라이프사이클 메소드라는 컴포넌트가 사용자에게 보여질 때, 돔트리에 올라갔을 때, 돔트리에서 나왔을 때, 컴포넌트가 업데이트 되었을 때 등등 이렇게 다양한 컴포넌트의 상태에 따라서 우리가 함수를 구현해 놓으면 리액트가 알아서 불러주었다.

반면 함수에는 그런 기능이 없다. 하지만 리액트 16.8 버전부터 React Hook이 도입되면서 함수에서도 스테이트도 가지고, 라이프사이클 메소드도 사용할 수 있게 되었다. 기존의 클래스 컴포넌트에서만 할 수 있었던 것들을 함수형 컴포넌트에서도 할 수 있도록 해준 것이 바로 이 React Hook이다.

그런데 클래스 컴포넌트를 이용하면 다 할 수 있는데 왜 굳이 React Hook이 도입이 되었을까?

그 이유는 단순하게 클래스가 어렵기 때문이라고 한다. 그리고 클래스를 이용하면 맴버변수에 접근할 때 항상 this 바인딩을 해야하는데, 이렇게 반복적으로 호출하는 것이 너무 불편하기도 했다고 한다. 그리고 많은 사람들이 fucntional programming이 유행하고 있으니 react에서도 함수형 프로그래밍을 한 번 이용해보자! 라는 바람이 불면서 React Hook이 도입 되었다고 한다.

그러면 리액트 훅만 공부하면 되겠네! 라는 생각을 가지면 안된다. 리액트 훅은 최신버전이기에, 이미 클래스 컴포넌트로 만들어진 경우도 많고 진행하는 팀마다 호불호가 갈린다고 한다. 둘 다 빼놓지 않고 개념을 알아놓고, 같은 로직을 수행하고자 할 때, 두 버전에서 어떻게 서로 다르게 코드가 작성되는지를 알고 공부하는 것이 중요할 것 같다.

그럼 이제 코드로 어떻게 두 버전이 다른지 확인해보자! 간단하게 버튼을 누르면 숫자를 증가시키는 프로그램으로 비교할 것이다.

1. 선언 방식

앱을 class 방식과 function으로 각각 보여줄 컴포넌트를 만들었다.

// app.jsx

import React from "react";
import "./app.css";
import AppClass from "./components/app_class";
import AppFunc from "./components/app_func";

function App() {
  return (
    <div>
      <AppClass />
      <AppFunc />
    </div>
  );
}

export default App;
  • Class

위에서 언급했듯이, 클래스는 React에서 제공하는 Component라는 클래스를 extends, 상속해서 만들 수 있다.

// app_class.jsx

import React, { Component } from "react";

class AppClass extends Component {
  render() {
    return <div>Class</div>;
  }
}

export default AppClass;
  • Function

함수형은 평소에 보던 함수방식과 별 다른점이 없다.

// app_func.jsx

import React from "react";

const AppFunc = () => <h1>Function</h1>;

export default AppFunc;

2. State

이제 각 컴포넌트에 state를 만들고 count를 보여주려고 한다.

  • Class
    클래스에서는 직접 state 오브젝트를 만들고, this를 이용하여 접근해 count를 보여줄 수 있다.
import React, { Component } from "react";

class AppClass extends Component {
  state = {
    count: 0,
  };
  render() {
    return (
      <>
        <h1>Class</h1>
        <div>{this.state.count}</div>
      </>
    );
  }
}

export default AppClass;
  • Function

함수에서는 useState 로 state를 사용한다. 이것을 사용해 컴포넌트에서 상태를 관리한다. 첫번째 원소는 현재 상태, 두번째 원소는 상태를 바꿔주는 함수를 설정한다. 우선은 처음 count는 0으로 설정했다. 그리고 this가 없이 그냥 count 변수를 불러오면 된다.

import React, { useState } from "react";

const AppFunc = () => {
  const [count, setCount] = useState(0);
  return (
    <>
      <h1>Function</h1>
      <div>{count}</div>
    </>
  );
};

export default AppFunc;

그럼 이렇게 둘 다 잘 나오는 것을 확인할 수 있다.
이제는 버튼을 만들어 숫자를 증감시키는 것을 만들어보자.

  • Class

onIncrease와 onDecrease 함수를 만들어 button onClick 이벤트에 설정해준다. class에서는 setState라는 함수를 이용해 현재의 state를 업데이트 할 수 있다. class내에서 만든 함수이므로 역시 this로 접근해 함수를 불러줘야한다.

import React, { Component } from "react";

class AppClass extends Component {
  state = {
    count: 0,
  };

  onIncrese = () => {
    const newCount = this.state.count + 1;
    this.setState({ count: newCount });
  };

  onDecrese = () => {
    const newCount = this.state.count - 1;
    this.setState({ count: newCount });
  };

  render() {
    return (
      <>
        <h1>Class</h1>
        <div>{this.state.count}</div>
        <button onClick={this.onIncrese}>+1</button>
        <button onClick={this.onDecrese}>-1</button>
      </>
    );
  }
}

export default AppClass;

  • Function

함수에서는 우리가 useState에서 두번째 인자로 설정해줬던 setCount로 state를 업데이트한다. 즉 class에서의 setState의 역할을 수행해주는 것이다. 간단하게 변경된 count를 setCount의 인자로 넣어주면 된다.

import React, { useState } from "react";

const AppFunc = () => {
  const [count, setCount] = useState(0);

  const onIncrese = () => {
    const newCount = count + 1;
    setCount(newCount);
  };

  const onDecrese = () => {
    const newCount = count - 1;
    setCount(newCount);
  };
  return (
    <>
      <h1>Function</h1>
      <div>{count}</div>
      <button onClick={onIncrese}>+1</button>
      <button onClick={onDecrese}>-1</button>
    </>
  );
};

export default AppFunc;

3. props

props 전달하는 방식에서도 차이가 있다. app.jsx에서 Hello라는 문자열을 props으로 넘겨주고 어떻게 다른지 확인해보자.

import React, { useState } from "react";
import "./app.css";
import AppClass from "./components/app_class";
import AppFunc from "./components/app_func";

function App() {
  const [hello, setHello] = useState("Hello");
  return (
    <div>
      <AppClass hello={hello} />
      <AppFunc hello={hello} />
    </div>
  );
}

export default App;
  • Class
    class에서는 this.props를 호출하여 props에 접근한다.
import React, { Component } from "react";

class AppClass extends Component {
  state = {
    count: 0,
  };

  onIncrese = () => {
    const newCount = this.state.count + 1;
    this.setState({ count: newCount });
  };

  onDecrese = () => {
    const newCount = this.state.count - 1;
    this.setState({ count: newCount });
  };

  render() {
    return (
      <>
        <h1>Class</h1>
        <div>{this.state.count}</div>
        <button onClick={this.onIncrese}>+1</button>
        <button onClick={this.onDecrese}>-1</button>
        <div>{this.props.hello}</div>
      </>
    );
  }
}

export default AppClass;
  • Function
    함수에서는 이렇게 props로 전달받아 사용할 수도 있지만,
import React, { useState } from "react";

const AppFunc = (props) => {
  const [count, setCount] = useState(0);

  const onIncrese = () => {
    const newCount = count + 1;
    setCount(newCount);
  };

  const onDecrese = () => {
    const newCount = count - 1;
    setCount(newCount);
  };
  return (
    <>
      <h1>Function</h1>
      <div>{count}</div>
      <button onClick={onIncrese}>+1</button>
      <button onClick={onDecrese}>-1</button>
      <div>{props.hello}</div>
    </>
  );
};

export default AppFunc;

이렇게 props 값을 바로 전달받을 수도 있다.

import React, { useState } from "react";

const AppFunc = ({ hello }) => {
  const [count, setCount] = useState(0);

  const onIncrese = () => {
    const newCount = count + 1;
    setCount(newCount);
  };

  const onDecrese = () => {
    const newCount = count - 1;
    setCount(newCount);
  };
  return (
    <>
      <h1>Function</h1>
      <div>{count}</div>
      <button onClick={onIncrese}>+1</button>
      <button onClick={onDecrese}>-1</button>
      <div>{hello}</div>
    </>
  );
};

export default AppFunc;

4. LifeCycle

  • Class

class에서의 LifeCycle이다. componentDidMount()는 컴포넌트가 마운트된 직후, componentDidUpdate()는 갱신이 일어난 직후, componentWillInmount()는 컴포넌트가 마운트 해제되어 제거되지 직전에 호출되는 것을 알 수 있다.

코드로 예시를 살펴보자.

기존에 작성했던 app_class.jsx에 다음 코드를 추가했다. componentDidMount는 새로고침 했을 때 console에 출력되는 것이 보이고, componentDidUpdate는 버튼을 누를 때마다, 즉 컴포넌트가 업데이트 될 때마다 출력되는 것이 보인다.

componentDidMount() {
    console.log("Component did Mount!!");
  }

  componentDidUpdate() {
    console.log("Component did Update!!");
  }

  componentWillUnmount() {
    console.log("Component will unMount!!");
  }

  • Function
    함수에서는 useEffect 라는 Hook을 이용하면 생명주기 메소드를 사용할 수 있다. 다음 예시들처럼 사용할 수 있다.

  1. Component가 mount 되었을 때
  • 컴포넌트가 가장 처음 렌더링 될 때는 빈 배열을 넘긴다. class에서의 componentDidMount와 유사하다.
useEffect(() => {
    console.log("마운트 될때만 실행!");
  }, []);
  • 만약 배열을 생략하면, 렌더링 될때마다 실행된다.
  useEffect(() => {
    console.log("렌더링 될 때 마다 실행!!");
  });
  1. Component가 update 되었을 때 (특정 state나 props가 변할 때)
  • 특정값이 업데이트 될 때 실행하고 싶을 때는 검사하고 싶은 값을 배열 값으로 넣어준다. class에서의 componentDidUpdate와 유사하다.
  useEffect(() => {
    console.log("count의 값이 변할 때만 실행!!!");
  }, [count]);

이렇게 class와 Function 컴포넌트 방식이 어떻게 다른지 정리해보았다. 그래도 React Hook을 이용한 Function 컴포넌트가 최신버전이고 요즘은 이것을 자주 사용하는 추세로 가고 있으니 개인 프로젝트를 진행하거나 React를 공부할 때는 Function 컴포넌트로 진행하려 한다. React Hook에 대한 더 자세한 내용은 나중에 따로 글로 작성하여 정리해볼 것이다.

1개의 댓글

comment-user-thumbnail
2023년 12월 9일

잘 봤습니다!

답글 달기