2026년 4월 6일

왜 React는 함수형 컴포넌트를 중심으로 흘러갔을까

🤖이 글의 주제와 흐름은 작성자의 생각이며, 본문 작성에 AI의 도움을 받았습니다.

React를 처음 배울 때 흔히 이런 인상을 받습니다.

“요즘은 함수형 컴포넌트를 쓰는 게 당연합니다.” “클래스형 컴포넌트는 이제 과거의 유산입니다.”

하지만 여기서 한 걸음 더 들어가 보면, 더 흥미로운 질문이 생깁니다.

왜 React는 함수형 컴포넌트 중심으로 흘러가게 되었을까요?

이 질문에 대한 흔한 답은 “함수형이 더 깔끔해서”, “코드가 짧아서”, “Hooks가 편해서” 정도입니다. 물론 모두 맞는 말입니다. 하지만 그것만으로는 충분하지 않습니다. 정말 중요한 이유는 문법 취향보다 더 깊은 곳에 있습니다.

핵심은 React가 풀어야 하는 문제의 성격입니다.

React는 단순히 객체를 잘 모델링하는 방법을 찾는 프레임워크가 아닙니다. 매번 바뀌는 상태를 바탕으로 화면을 다시 계산하고, 그 변화를 빠르게 반영하고, 개발 중에는 수정한 코드를 가능한 한 상태를 잃지 않고 즉시 교체해야 합니다. 이런 환경에서는 컴포넌트를 오래 살아 있는 객체로 보는 방식보다, 상태를 읽어 UI를 계산하는 실행 단위로 보는 방식이 더 잘 맞는 경우가 많습니다.

그리고 이 차이는 개발자가 가장 자주 체감하는 기능, 즉 Fast Refresh에서 특히 선명하게 드러납니다.

먼저 결론부터: 우열보다 적합성의 문제입니다

여기서 강조하고 싶은 것은 우열이 아니라 적합성입니다.

클래스가 낡았고 함수가 진보적이라서 React가 함수형으로 옮겨간 것은 아닙니다. 백엔드나 다른 영역에서는 여전히 객체와 인스턴스 중심 모델이 아주 자연스럽고 강력합니다. 실제로 서비스 객체, 커넥션 풀, 캐시, 도메인 모델처럼 정체성과 생애주기, 내부 상태를 오래 보존하는 단위를 다룰 때는 객체 모델이 잘 맞습니다.

반면 React의 컴포넌트는 대개 그런 종류의 대상을 표현하지 않습니다. React 컴포넌트는 무엇을 오래 들고 있을 것인가보다, 현재 상태에서 무엇을 그릴 것인가에 더 가깝습니다.

이 차이가 추상화의 선호를 바꿉니다.

UI는 본질적으로 다시 계산되는 결과물입니다

프론트엔드 UI는 현재 상태에 따라 계속 바뀝니다.

사용자가 입력을 하면 상태가 바뀌고, 네트워크 응답이 오면 상태가 바뀌고, 탭을 전환하거나 모달을 열어도 상태가 바뀝니다. React는 이 상태 변화를 바탕으로 다음 UI를 다시 계산합니다.

이 관점에서 보면 컴포넌트는 오래 유지되는 비즈니스 객체라기보다, 현재 입력을 바탕으로 결과를 만들어내는 계산 단위에 가깝습니다.

function Greeting({ name }) {
  return <h1>안녕하세요, {name}님</h1>;
}

이 컴포넌트는 name이라는 입력을 받아 JSX를 반환합니다. 물론 실제 React 내부는 이보다 훨씬 복잡하지만, 컴포넌트를 바라보는 기본 시선은 이쪽에 가깝습니다.

중요한 점은 이것입니다.

React에서 컴포넌트는 자기 자신이라는 객체가 오래 살아남는 것보다, 같은 위치에서 같은 규칙으로 다시 실행될 수 있는 것이 더 중요합니다.

이 특성은 함수형 모델과 잘 맞습니다.

클래스형 컴포넌트는 무엇을 중심에 두는가

물론 React에도 원래 클래스형 컴포넌트가 있었습니다.

class Counter extends React.Component {
  state = { count: 0 };
 
  render() {
    return (
      <button onClick={() => this.setState({ count: this.state.count + 1 })}>
        {this.state.count}
      </button>
    );
  }
}

이 모델에서는 상태와 동작이 하나의 인스턴스에 모입니다.

  • 상태는 this.state에 들어 있습니다.
  • 동작은 render, lifecycle 메서드, 인스턴스 메서드에 들어 있습니다.
  • 이 모든 것이 하나의 this를 중심으로 묶입니다.

즉, 클래스형 컴포넌트는 상태와 로직을 함께 보유한 객체에 가깝습니다.

이 방식은 분명 장점이 있습니다. 생애주기를 메서드로 나눠 표현할 수 있고, 객체 모델에 익숙한 개발자에게는 직관적입니다. 하지만 React가 점점 더 중요하게 다루게 된 문제들, 특히 상태 보존, 재실행, 코드 교체, 조합성 같은 측면에서는 점점 마찰이 생기기 시작합니다.

함수형 컴포넌트는 무엇을 중심에 두는가

같은 예제를 함수형으로 쓰면 이렇게 됩니다.

function Counter() {
  const [count, setCount] = useState(0);
 
  return (
    <button onClick={() => setCount(count + 1)}>
      {count}
    </button>
  );
}

표면적으로는 더 짧고 단순해 보입니다. 하지만 진짜 차이는 문법이 아닙니다.

핵심은 상태가 함수 자체에 붙어 있지 않다는 점입니다.

함수형 컴포넌트는 렌더마다 다시 호출됩니다. 상태는 그 함수 안 어딘가에 보관되는 것이 아니라, React 런타임이 컴포넌트의 위치와 Hook 호출 순서를 기준으로 관리합니다. 흔히 상태는 함수 바깥, React 쪽에 저장된다고 설명하는 이유가 이것입니다.

즉 함수형 컴포넌트는 다음과 같습니다.

  • 상태를 소유하는 객체라기보다
  • React가 관리하는 상태를 읽어서
  • 현재 UI를 계산하는 실행 함수에 가깝습니다.

이 구조에서는 상태 저장소와 렌더 로직이 상대적으로 분리됩니다. 그리고 바로 이 점이 React의 여러 특성과 잘 맞물립니다.

이 차이가 가장 잘 드러나는 곳: Fast Refresh

이 구조적 차이는 핫 리로딩, 정확히는 React의 Fast Refresh를 생각해 보면 잘 보입니다.

개발 중 코드를 수정했을 때, 가장 이상적인 경험은 이렇습니다.

  • 브라우저를 새로고침하지 않습니다.
  • 사용자가 입력하던 상태도 유지합니다.
  • 수정한 UI 코드만 즉시 반영합니다.

이건 생각보다 까다로운 요구입니다. 왜냐하면 코드는 바뀌었는데 상태는 유지되어야 하기 때문입니다.

여기서 클래스형과 함수형은 서로 다른 문제를 만납니다.

클래스형에서의 질문: 이 인스턴스를 계속 믿어도 되는가

클래스형 컴포넌트에서는 상태와 로직이 같은 인스턴스 위에 올라가 있습니다.

class Profile extends React.Component {
  state = { editing: true, name: "홍길동" };
 
  render() {
    return <div>{this.state.name}</div>;
  }
}

이때 개발자가 render 내용을 수정했다고 가정해 보겠습니다. React 입장에서는 이런 요구가 생깁니다.

  • 기존 상태는 유지하고 싶습니다.
  • 그런데 구현은 새 코드로 바꾸고 싶습니다.
  • 동시에 이미 살아 있는 인스턴스의 정체성도 크게 흔들고 싶지 않습니다.

문제는 이 세 가지가 클래스형 구조에서는 단단히 얽혀 있다는 점입니다.

이미 존재하는 객체에는 이전 시점의 메서드, 바인딩, lifecycle 이력, 인스턴스 상태가 함께 묶여 있습니다. 그래서 상태는 남기고 구현만 교체하는 작업이 생각보다 까다롭습니다.

단순히 새 코드를 불러왔다고 끝나는 문제가 아니라, 이미 존재하는 객체를 어떤 기준으로 계속 같은 컴포넌트라고 볼 것인가라는 문제가 따라옵니다.

클래스형에서 핫 리로딩이 상대적으로 까다로웠던 이유가 여기에 있습니다.

함수형에서의 질문: 같은 위치에서 새 함수를 다시 실행할 수 있는가

함수형 컴포넌트에서는 질문이 달라집니다.

function Profile() {
  const [editing] = useState(true);
  const [name] = useState("홍길동");
 
  return <div>{name}</div>;
}

여기서는 상태가 인스턴스 객체에 붙어 있지 않습니다. React는 해당 컴포넌트가 트리의 같은 위치에 있고 Hook 구조가 유지된다고 판단하면, 기존 상태를 이어서 들고 갈 수 있습니다. 그러면 비교적 자연스럽게 다음 전략이 가능합니다.

  • 기존 상태는 React 쪽에서 유지합니다.
  • 새로 수정된 함수 구현을 같은 위치에서 다시 실행합니다.
  • 결과 UI만 새로 계산합니다.

즉 클래스형에서는 이 객체의 내부를 어떻게 안전하게 교체할까가 문제였다면, 함수형에서는 같은 자리에서 새 계산 함수를 실행할 수 있을까가 문제에 가깝습니다.

이 차이는 작아 보이지만, 개발 경험에는 큰 영향을 줍니다.

왜 이 구조가 React와 잘 맞았을까

이쯤 되면 함수형 컴포넌트가 React에서 점점 중심이 된 이유를 조금 더 구조적으로 이해할 수 있습니다.

React가 잘하고 싶었던 것은 대체로 이런 것들입니다.

  • 상태 변화에 따라 UI를 선언적으로 다시 계산하기
  • 컴포넌트를 조합 가능한 단위로 다루기
  • 렌더링과 상태 관리를 유연하게 분리하기
  • 개발 중 수정 사항을 빠르게 반영하기
  • 구현을 재실행 가능한 형태로 유지하기

함수형 컴포넌트는 이런 목표들과 잘 맞았습니다. 특히 Hooks가 도입된 이후에는 상태, 이펙트, 재사용 로직을 클래스 없이도 표현할 수 있게 되었고, React는 더 이상 클래스형이 제공하던 틀에 크게 의존하지 않아도 되었습니다.

중요한 것은 함수라서 좋다는 점이 아닙니다.

더 정확히 말하면, 컴포넌트를 재실행 가능한 계산 단위로 보고, 상태는 React가 별도로 관리하는 모델이 React의 방향성과 잘 맞았습니다.

그렇다고 클래스형이 틀렸다는 뜻은 아닙니다

여기서 오해하면 안 되는 점이 있습니다.

이 글은 함수형이 우월하다는 이야기가 아닙니다. 클래스형 컴포넌트는 React 초기에 매우 중요한 역할을 했고, 당시의 제약 안에서는 충분히 합리적인 모델이었습니다. 또 지금도 객체 중심 모델이 더 잘 맞는 문제는 많습니다.

다만 React 생태계가 점점 더 중시하게 된 것은 다음과 같습니다.

  • 컴포넌트의 객체적 정체성보다
  • 같은 규칙으로 반복 실행될 수 있는 성질
  • 상태 저장소와 렌더 로직의 분리
  • 개발 중 코드 교체의 용이성
  • 조합성과 재사용성

이 관점에서 보면, 함수형 컴포넌트의 부상은 유행이 아니라 React가 풀고 있던 문제에 대한 자연스러운 수렴에 가깝습니다.

마무리

왜 React는 함수형 컴포넌트를 중심으로 흘러갔을까요?

답은 단순히 문법이 더 예뻐서가 아닙니다. React는 본질적으로 상태 변화에 따라 UI를 다시 계산하는 시스템이고, 그 과정에서 컴포넌트를 오래 살아 있는 객체보다 재실행 가능한 계산 단위로 다루는 편이 더 잘 맞았습니다.

그리고 그 차이는 Fast Refresh 같은 아주 실용적인 기능에서 선명하게 드러납니다.

클래스형에서는 상태와 로직이 인스턴스에 함께 묶여 있어 구현만 교체하는 문제가 어렵습니다. 함수형에서는 상태는 React가 들고 있고, 컴포넌트는 그 상태를 읽어 실행되는 함수에 가까워서 같은 자리에서 새 함수를 실행한다는 모델이 더 자연스럽습니다.

결국 이것은 패러다임의 승패가 아니라, 문제와 추상화의 궁합에 관한 이야기입니다.

React가 함수형 컴포넌트 중심으로 이동한 이유는, 함수형이 더 고상해서가 아니라 React가 다루는 UI의 성격에 더 잘 맞았기 때문입니다.

KHLogo