이 주제를 한 문장으로 설명하면?
함수형 컴포넌트와 클래스 컴포넌트의 구조와 차이점을 이해하고, 왜 현재 React에서는 함수형 컴포넌트가 표준이 되었는지 이해하는 것
이 주제가 왜 중요한가?
React 컴포넌트 구조를 이해하는 가장 기본적인 개념으로,
왜 현재 React가 Hooks 기반 함수형 컴포넌트가 표준이 되었는지 이해하는데에 바탕이 된다고 생각한다.
관련 키워드
[React 컴포넌트 구조]├── 함수형 컴포넌트│ ├── useState (상태 관리)│ ├── useEffect (Side Effect 처리)│ ├── Custom Hook (로직 재사용)│ └── Hooks 기반 구조├── 클래스 컴포넌트│ ├── state / props│ ├── lifecycle methods│ └── this 바인딩 문제├── 클래스 컴포넌트의 한계│ ├── 데이터 흐름 추적 어려움│ ├── 로직 재사용 문제│ ├── Wrapper Hell (HOC / Render Props)│ └── 코드 복잡도 증가└── Hooks 등장├── 상태 관리 단순화├── 로직 재사용 개선└── 함수형 컴포넌트 표준화
참고 : https://ko.javascript.info/class
클래스형 컴포넌트는 ES6의 클래스 문법을 이용하여 작성한다.
클래스를 선언하고, React.Component를 extends 하여 구현하며, render() 메소드를 통해 UI를 렌더링한다.
import React, { Component } from 'react';class ClassComponent extends Component {constructor(props) {super(props);this.
클래스형 컴포넌트에서는 state와 props를 이용하여 상태를 관리한다.
this.state 를 통해 상태를 관리하고, this.setState() 메소드를 통해 상태를 업데이트한다.
따라서, setState()가 호출되면 컴포넌트가 리렌더링 된다.
부모 컴포넌트에서 전달되는 데이터를 의미한다.
클래스형 컴포넌트에서 가장 큰 특징은 생명주기 메서드이다.

이 생명주기 메서드가 실행되는 시점은 크게 3가지로 볼 수 있다.
constructorrendercomponentDidMount
shouldComponentUpdaterendercomponentDidUpdate
더욱 다양한 생명주기 메서드들이 있지만! 대표적인 것들 위주로 보자.
아래 4가지가 가장 많이 쓰이며, 함수형 컴포넌트의 useEffect와 직접적으로 비교되는 메서드들입니다.
→ 렌더함수 내부에서 this.setState를 호출하면 안됨
하지만, state를 다루는 것은 보통 생성자에서 하는것이 좋다.
예시로 정리하기
class Example extends React.Component {constructor(props) {super(props);this.state = { count: 0 };}
React 초기에 사용되던 클래스 컴포넌트는 점차 여러 문제가 발생했다.
클래스 컴포넌트에서는 상태(state)가 여러 생명주기 메서드에서 변경될 수 있다.
이에 따라 여러 위치에서 상태 업데이트가 가능하다보니 상태 변화 흐름을 추적하기 어려웠다.
어디에서 state가 변경되는지 어떤 lifecycle에서 실행되는지 등 ..
클래스 컴포넌트에서는 로직 재사용을 위해 여러 패턴을 사용했다.
<DataFetcherrender={(data) => (<div>{data}</div>)}/>
하지만 이러한 방식들은 결국 구조를 복잡하게 만들고, 컴포넌트 구조를 깊게 만든다.
클래스 컴포넌트에서는 하나의 컴포넌트에 여러 로직이 포함되는 경우가 많다.
그래서 컴포넌트가 거대한 클래스 구조가 되기가 쉽다.
JavaScript에서 this는 함수가 호출되는 방식에 따라 결정된다. 클래스 메서드를 그냥 함수로 전달하면 this가 사라진다.
class Counter extends React.Component {constructor(props) {super(props);this.state = { count
클래스 컴포넌트는 더 많은 것들을 담기에 함수 컴포넌트보다 번들 최적화에 불리하다.
prototypeconstructorlifecycle methods
클래스 컴포넌트는 컴포넌트 인스턴스 내부에서 상태를 관리한다.
그래서 코드가 변경되면 컴포넌트 인스턴스를 새로 생성해야 하는 경우가 많다.
→ 그래서 개발 환경에서 Hot Reload 경험이 좋지 않다.
함수형 컴포넌트는 자바스크립트의 함수 문법을 이용하여 작성한다.
컴포넌트는 props를 입력받아 상태를 관리하며, JSX를 반환하는 형태이다.
function FunctionComponent(props) {return <div>{props.name}</div>;}
React 16.8에서부터는 Hooks가 도입되면서 함수형 컴포넌트에서도 state 관리와 생명주기 처리가 가능해졌다.
우선 함수형 컴포넌트에서는 useState Hook을 통해 상태를 관리한다.
useState는 현재 상태 값과 상태를 업데이트하는 함수를 배열 형태로 반환한다.
import React, { useState } from 'react';function FunctionComponent(props) {const [count, setCount] = useState(0);const
함수형 컴포넌트에는 클래스 컴포넌트에 있는 생명주기 메서드 등이 없다.
대신 useEffect 같은 훅을 사용해서 아래와 같은 작업들을 수행할 수 있다.
import { useEffect } from"react";useEffect(() => {console.log("component mounted");return () => {console
이 코드는 다음 lifecycle과 유사하다.
| 클래스 컴포넌트 | 함수 컴포넌트 |
| componentDidMount | useEffect |
| componentDidUpdate | useEffect |
| componentWillUnmount | useEffect cleanup |
클래스 컴포넌트
componentDidMountcomponentDidUpdatecomponentWillUnmount
함수 컴포넌트
useEffect
useEffect로 여러 lifecycle을 처리할 수 있다.
함수 컴포넌트는 말그대로 함수이다.
렌더링이 발생할 때마다 함수가 다시 실행된다는 것!
즉, 함수가 실행될 때 전달받은 props와 내부의 state는 그 함수 실행 시점의 지역 변수이므로, 렌더링 시점의 값이 고정된다.
클래스 컴포넌트는 인스턴스(객체)이다.
그러니 컴포넌트가 처음 생성될 때 한 번 만들어지고, 리렌더링되어도 개체는 그대로 유지된다.
this.props와 this.state는 클래스라는 객체 안에 담긴 속성이므로, 리액트는 값이 바뀔 때마다 객체의 속성을 바꾸는 개념이다.
'팔로우' 버튼을 구현했다고 가정해보자
| 구분 | 결과 | 이유 |
| 함수 컴포넌트 | "사용자 A를 팔로우했습니다!" | 클릭 시점의 props(A)를 클로저로 꽉 잡고 있기 때문 |
| 클래스 컴포넌트 | "사용자 B를 팔로우했습니다!" | 알림이 뜨는 순간 this.props |
| 함수 컴포넌트 | 클래스 컴포넌트 | |
| 상태 관리 | useState | this.state |
| 생명주기 | useEffect | lifecycle methods |
| this 사용 | 없음 | 있음 |
| 로직 재사용 | Custom Hook | HOC / Render Props |
| 코드 복잡도 | 낮음 | 높음 |
여전히 리액트에서 사용되고 있는 클래스 컴포넌트 형태가 있다
현재 React에서 Error Boundary는 클래스 컴포넌트로 구현된다.
classErrorBoundaryextendsReact.Component {componentDidCatch(error,info) {console.error(error)}render() {returnthis.props.children}
componentWillUnmount
→ 안 그러면 무한 렌더링됨.
위 코드에서 버튼을 클릭하면 이런 문제가 생길 수 있다.
Cannot read property 'state' of undefined
왜냐면 onClick 으로 넘긴 코드는 사실상 함수만 전달된 개념이라 this 가 컴포넌트를 가리키지 않기 때문이다.
button.onclick = handleClick
그래서 이런 문제를 해결하기 위해 크게 2가지 방법을 사용한다.
constructor(props) {super(props);this.handleClick = this.handleClick.bind(this);}
이렇게 하면 handleClick 내부의 this → 컴포넌트 인스턴스 로 고정된다.
하지만 그럼 모든 메서드마다 이렇게 바인딩을 해줘야한다 ..
화살표 함수는 this를 상위 스코프에서 가져온다.
handleClick = () => {console.log(this.state.count);};