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

    2021년 08월 02일

    리액트에서 컴포넌트를 선언하는 방식에는 클래스형 컴포넌트와 함수형 컴포넌트 두 가지가 있습니다. 과거에는 클래스형 컴포넌트를 주로 사용했지만, 2019년 v16.8부터 함수형 컴포넌트에 리액트 훅(Hook)을 지원하며 현재 리액트 공식 문서에서는 함수형 컴포넌트 사용을 권장하고 있습니다.

    하지만, 아직 클래스형 컴포넌트를 사용하고 있는 곳들이 있어 유지보수를 위해 두 가지 방식 모두 알고는 있어야 힙니다.

    클래스형 컴포넌트

    클래스형 컴포넌트의 특징은 다음과 같습니다.

    Class keyword를 사용해서 선언

    import React, { Component } from 'react'; 
    
    class App extends Component { // 1. Component로 상속을 받아야합니다.
    	render() { // 2. render()를 사용하여 JSX를 반환합니다.
    	return ...
      }
    }
    
    export default App;

    상태 업데이트

    import React, { Component } from "react";
    
    class App extends Component {
      constructor(props) {
        super(props);
        this.state = {
          name: "choi",
        };
      }
    
      render() {
        return (
          <>
            <div>{this.state.name}</div>
            <button onClick={()=>this.setState({ name: "kim" })}>버튼</button>
          </>
        );
      }
    }
    
    export default App;

    1. constructor에서 this.state 로 초기값 설정이 가능합니다.

    2. 클래스형 컴포넌트의 state는 객체 형식입니다.

    3. this.setState()를 사용해서 state 값을 변경합니다.


    그런데 super(props)는 갑자기 어디서 나왔을까요? 리액트 공식 문서에 다음과 같이 나와있습니다.

    constructor()

    constructor(props)

    메서드를 바인딩하거나 state를 초기화하는 작업이 없다면, 해당 React 컴포넌트에는 생성자를 구현하지 않아도 됩니다.

    React 컴포넌트의 생성자는 해당 컴포넌트가 마운트되기 전에 호출됩니다.

    React.Component를 상속한 컴포넌트의 생성자를 구현할 때에는 다른 구문에 앞서 super(props)를 호출해야 합니다. 그렇지 않으면 this.props가 생성자 내에서 정의되지 않아 버그로 이어질 수 있습니다.


    React에서 생성자는 보통 아래의 두 가지 목적을 위하여 사용됩니다.

    1.this.state에 객체를 할당하여 지역 state를 초기화

    2.인스턴스에 이벤트 처리 메서드를 바인딩


    props를 조회하거나 이벤트 핸들링을 할 때 this를 사용

    import React, { Component } from "react";
    
    class App extends Component {
      constructor(props) {
        super(props);
        this.state = {
          number: 1,
        };
    
      }
      
      // 화살표 함수로도 선언 가능
      plusNumber = () => {
        this.setState({ number: this.state.number + 1 });
      };
    
      // plusNumber() {
      //   this.setState({ number: this.state.number + 1 });
      // }
      // plusNumber = this.plusNumber.bind(this);
    
      render() {
        // const [name,age] = this.props
        return (
          <div>
          {name}
            <li>{this.state.number}</li>
            <button
              onClick={this.plusNumber}
            >
              버튼
            </button>
          </div>
        );
      }
    }
    export default App;

    LifeCycle 메소드를 가진다.

    리액트에서는 컴포넌트 LifeCycle(생명주기)라는 것을 가집니다.

    컴포넌트가 실행(생성)될 때, 업데이트될 때, 제거될 때 호출되는 메서드들입니다.


    react-class-lifecycle

    (출처: http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/)

    LifeCycle에는 componentDidMount, componentDidUpdate, componentWillUnmount 등의 메서드가 있습니다.

    요즘은 거의 함수형 컴포넌트를 사용하기 때문에 이런게 있다는 정도만 알고 지나가겠습니다.


    함수형 컴포넌트

    함수형 컴포넌트는 말 그대로 함수를 기반으로 작성하는 컴포넌트입니다.

    리액트 v16.8 이전의 함수형 컴포넌트는 state나 LifeCycle에 정의된 메서드를 사용할 수 없어서 비교적 간단한 동작을 하는데에 사용했습니다.

    이후 Hook이 등장하면서 함수형 컴포넌트에서도 클래스형 컴포넌트처럼 여러 가지 동작을 할 수 있게 됐습니다.

    메모리 자원을 클래스형 컴포넌트보다 덜 사용하며, 코드도 클래스형 컴포넌트에 비해 짧고 컴포넌트 선언이 편리합니다.


    함수형 컴포넌트의 특징은 다음과 같습니다.

    선언 방식이 간단합니다.

    import React from "react";
    
    function App() {
      const name = 'Functional Component" 
      return <div>{name}</div>;
    }
    
    export default App;

    useState Hook을 이용한 상태 업데이트

    클래스형 컴포넌트에서는 상태 설정을 위해 state를 사용했지만, 함수형 컴포넌트에는 state가 없습니다.

    대신 useState라는 Hook을 사용합니다. useState는 배열 형태로 사용 가능합니다.

    배열의 첫 번째 요소는 현재 상태, 두 번째 요소는 상태를 바꿀 수 있는 함수입니다.

    또한, 클래스형 컴포넌트에서는 this 키워드가 필요했지만, 함수형 컴포넌트는 그냥 바로 변수를 사용하면 됩니다.

    import React, { useState } from "react";
    
    function App() {
      const [number, setNumber] = useState(0) // 초기값 0
    
      return (
        <>
          <div>{number}</div>
          <button onClick={()=>{setNumber(prev => prev + 1)}}>버튼</button>
        </>
      )
      ;
    }
    
    export default App;

    props와 이벤트 핸들링, 함수 선언

    클래스형 컴포넌트에서는 props를 사용하거나 이벤트 핸들링을 할 때 this를 사용해야 했지만 함수형 컴포넌트에서는 this가 필요 없습니다.

    import React, { useState } from "react";
    
    function App() {
      const [number, setNumber] = useState(0) // 초기값 0
    
      // 함수 표현식으로 선언 가능합니다.
      const plusNumber = () => {
        setNumber(pre=>pre+1)
      }
      // 함수 선언식으로도 선언 가능합니다.
      // function plusNumber(){
      //   setNumber(pre=>pre+1)
      // }
    
      return (
        <>
          <div>{number}</div>
          <button onClick={plusNumber}>버튼</button>
        </>
      )
      ;
    }
    
    export default App;

    LifeCycle

    함수형 컴포넌트에서는 useEffect Hook을 사용해서 라이프 사이클을 별도로 구현해야합니다.

    또한 클래스 컴포넌트에서는 컴포넌트를 중심으로 라이프 사이클이 진행되었지만, 함수형 컴포넌트에서는 특정 데이터를 중심으로 라이프 사이클이 진행되는 것이 차이점입니다.

    const [number, setNumber] = useState(0)
    
    useEffect(() => {
      console.log('run!!')
    }, [number])

    useEffect 안의 코드는 컴포넌트가 맨 처음 렌더링될 때와 number이라는 state가 변경될 때 마다 실행됩니다.

    componentDidMount와 componentDidUpdate가 합쳐졌다고 볼 수 있습니다.

    const [number, setNumber] = useState(0)
    
    useEffect(() => {
    
      return () => {
        console.log('componentWillUnmount!')
      }
    }, [number])

    return으로 함수를 반환하면 componentWillUnmount 역할을 할 수 있습니다. 컴포넌트가 언마운트 될 때 return의 함수가 실행됩니다.

    const [number, setNumber] = useState(0)
    
    useEffect(() => {
      console.log('run!!')
    }, [])

    두 번째 인자로 빈 배열을 넣어주면 컴포넌트가 맨 처음 마운트 될 때만 실행됩니다.

    useEffect(() => {
      console.log('Rerendering')
    })

    두 번째 인자를 넣지 않으면 데이터와 관계 없이 리렌더링할 때마다 실행됩니다.

    const [number, setNumber] = useState(0)
    const [age, setAge] = useState(0)
    useEffect(() => {
      console.log('Changed number')
    }, [number])
    
    useEffect(() => {
      console.log('Changed age')
    }, [age])

    state가 여러 개라면 useEffect를 여러 개 사용하면 됩니다.

    Tags
      © 2021 dmchoi, Powered By Gatsby.