📚 CS/React

[React] 클래스형 컴포넌트 vs 함수형 컴포넌트

dev.daisy 2025. 9. 12. 12:49
리액트를 배우면서 당연하게 함수형 컴포넌트를 접했지만, 클래스형 컴포넌트의 복잡성과 유지보수 문제를 해결하기 위해 함수형 컴포넌트가 탄생했다는 배경을 이해하게 되어 의미 있는 시간이었습니다. 단순히 기술을 사용하는 것을 넘어, '왜 이 기술이 필요했는가?'에 대한 근본적인 질문에 답을 찾은 것 같아 매우 좋았습니다. 특히, this 바인딩 문제나 복잡한 생명주기 메서드 같은 클래스형 컴포넌트의 단점들을 함수형 컴포넌트가 Hooks라는 간결한 도구로 해결했다는 점이 가장 인상 깊었습니다.

1. 함수형 컴포넌트란? (Function Component)

함수형 컴포넌트는 이름 그대로 자바스크립트 함수로 작성하는 리액트 컴포넌트입니다. props를 인자로 받아 JSX를 반환하는 구조로, Hooks를 통해 상태(state)나 생명주기(lifecycle) 같은 기능을 추가할 수 있습니다. 이는 클래스형 컴포넌트의 복잡성을 해결하기 위해 등장한 접근 방식입니다.

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

function Counter() {
  const [count, setCount] = useState(0); // ① 상태 관리: useState Hook 사용

  useEffect(() => { // ② 생명주기 관리: useEffect Hook 사용
    // 컴포넌트가 렌더링될 때마다 실행되는 '부수 효과'
    document.title = `You clicked ${count} times`;
  }, [count]); // ③ 의존성 배열: count가 변경될 때만 useEffect가 다시 실행됨

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

 

  • useState(0): count라는 상태 변수와 setCount라는 상태 업데이트 함수를 반환합니다. 0은 count의 초기값입니다.
  • useEffect(() => { ... }, [count]): 컴포넌트가 처음 렌더링될 때, 그리고 count 값이 변경될 때마다 첫 번째 인자의 함수를 실행합니다. 이는 클래스형 컴포넌트의 componentDidMount와 componentDidUpdate를 통합한 역할을 합니다. [count]를 의존성 배열이라 부르며, 이 배열이 없다면 매 렌더링마다 함수가 실행됩니다.

2. 클래스형 컴포넌트란? (Class Component)

클래스형 컴포넌트는 ES6 클래스 문법을 사용합니다. React.Component를 상속받고, render() 메서드 내에서 JSX를 반환해야 하며, this.state와 this.setState()를 통해 상태를 관리합니다.

 

주요 생명주기 메서드

  • componentDidMount(): 컴포넌트가 화면에 처음 렌더링(마운트)된 직후에 딱 한 번 실행됩니다. 주로 외부 API에서 데이터를 가져오거나, DOM에 직접 접근하는 작업을 수행할 때 사용됩니다.
  • componentDidUpdate(): 컴포넌트의 상태(state)나 속성(props)이 변경되어 재렌더링이 완료된 직후에 실행됩니다. 변경된 값에 따라 특정 로직을 실행할 때 유용하며, 이전 props와 state를 인자로 받아 현재 값과 비교하여 로직을 분기할 수 있습니다.
  • componentWillUnmount(): 컴포넌트가 화면에서 사라지기(언마운트) 직전에 실행됩니다. 타이머나 구독을 해제하는 등 불필요한 메모리 누수를 방지하는 정리(cleanup) 작업을 할 때 사용됩니다.

 

import React from 'react';

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { // ① 상태 정의: this.state 객체 사용
      count: 0
    };
  }

  componentDidMount() { // ② 생명주기 메서드: 컴포넌트가 처음 마운트된 후 실행
    document.title = `You clicked ${this.state.count} times`;
  }

  componentDidUpdate() { // ③ 생명주기 메서드: 컴포넌트가 업데이트된 후 실행
    document.title = `You clicked ${this.state.count} times`;
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

 

  • constructor(props): 컴포넌트가 생성될 때 가장 먼저 실행되는 메서드입니다. this.state에 초기 상태를 정의하고 this.setState()를 통해 상태를 변경합니다.
  • this 키워드: 상태(this.state), props(this.props), 메서드(this.setState()) 등 클래스 내부의 속성에 접근하기 위해 반드시 사용해야 합니다. 이로 인해 this의 바인딩 문제를 개발자가 직접 신경 써야 하는 복잡성이 따릅니다.

3. 상태(State)와 생명주기(Lifecycle) 관리의 차이

1) 함수형 컴포넌트: Hooks로 기능 분리

함수형 컴포넌트는 useState, useEffect, useContext와 같은 Hooks를 사용해 상태와 생명주기 기능을 구현합니다. 이 Hooks 덕분에 관련된 로직을 하나의 함수 내에 모을 수 있어 코드가 훨씬 깔끔하고 재사용성이 높아집니다. 예를 들어, 데이터 로딩 로직을 하나의 커스텀 Hook으로 만들어 여러 컴포넌트에서 쉽게 재활용할 수 있습니다.

  • useState: 컴포넌트의 상태를 관리합니다.
  • useEffect: 렌더링 후 부수 효과(side effects)를 처리합니다. 이는 componentDidMount, componentDidUpdate, componentWillUnmount의 역할을 통합합니다.
  • useContext: 전역 상태를 구독합니다.

2) 클래스형 컴포넌트: 메서드와 클래스 인스턴스

클래스형 컴포넌트는 상태를 this.state 객체에 저장하고 this.setState() 메서드로 업데이트합니다. 생명주기 이벤트는 componentDidMount(), componentDidUpdate(), componentWillUnmount()와 같은 별도의 메서드를 통해 관리됩니다. 이로 인해 로직이 여러 메서드에 분산되고, this 키워드를 사용해야 하므로 코드가 복잡해질 수 있습니다.


4. 성능 최적화: 메모이제이션과 재렌더링

1) 함수형 컴포넌트: useMemo와 useCallback

함수형 컴포넌트의 가장 큰 장점 중 하나는 메모이제이션(Memoization)을 통한 성능 최적화가 용이하다는 점입니다.

  • React.memo: props가 변경되지 않으면 컴포넌트를 재렌더링하지 않도록 합니다.
  • useMemo: 복잡한 연산 결과를 기억해 불필요한 재계산을 막습니다.
  • useCallback: 함수 자체를 기억해 props로 전달되는 함수의 불필요한 재생성을 방지합니다.

이러한 Hooks 덕분에 불필요한 렌더링을 효과적으로 줄여 애플리케이션의 성능을 최적화할 수 있습니다.

2) 클래스형 컴포넌트: shouldComponentUpdate

클래스형 컴포넌트에서 성능 최적화를 위해 주로 사용되는 메서드는 shouldComponentUpdate()입니다. 이 메서드는 컴포넌트의 재렌더링 여부를 직접 제어할 수 있지만, 수동으로 props와 state를 비교해야 하므로 복잡하고 실수를 유발할 수 있습니다.


요약

React는 현재 공식 문서와 커뮤니티에서 함수형 컴포넌트와 Hooks 사용을 권장합니다. 그 이유는 다음과 같습니다.

특징 함수형 컴포넌트 (Hooks) 클래스형 컴포넌트
문법 간결하고 직관적 복잡하고 정형화된 클래스 문법
상태/생명주기 Hooks를 사용해 관련 로직을 한 곳에 모음 별도의 메서드에 로직이 분산됨
로직 재사용 커스텀 Hooks로 로직을 쉽게 재활용 복잡한 HOC나 Render Props 패턴 필요
가독성 this 키워드 없이 깨끗한 코드 this 바인딩 문제 및 복잡성
최적화 useMemo, useCallback으로 메모이제이션 용이 shouldComponentUpdate로 수동 최적화

 

물론 클래스형 컴포넌트도 여전히 유효하지만, 새로운 React 프로젝트를 시작하거나 기존 컴포넌트를 리팩토링할 때는 함수형 컴포넌트를 채택하는 것이 훨씬 효율적이고 유지보수가 용이합니다.

 

함수형 컴포넌트는 React의 본질인 '컴포넌트'를 순수 함수의 개념에 더 가깝게 만들어줍니다. 덕분에 React 개발은 더욱 간결하고 예측 가능해졌으며, 현대 자바스크립트의 흐름과도 잘 맞물리게 되었습니다. React 개발자라면 함수형 컴포넌트와 Hooks에 익숙해지는 것이 필수적인 역량이 된 것 같습니다.