클래스 컴포넌트는 React.Component를 상속하고 render() 메서드에서 JSX를 반환하는 자바스크립트 클래스입니다. this.props에서 props를 읽고, this.state에 state를 보관하며, this.setState()로 업데이트합니다. 새 코드에서는 훅을 쓰는 함수 컴포넌트가 클래스를 대체했지만, 클래스는 여전히 완전히 지원되고, 많은 오래된 코드베이스에 등장하며, 에러 바운더리를 작성하는 유일한 방법입니다.
각 카운터는 자기만의 state를 유지합니다. 세 번째 <Counter label="Plums" />를 추가하면 그것도 0부터 시작합니다.
클래스 컴포넌트의 구조
import { Component } from 'react';
class Welcome extends Component {
static defaultProps = { name: 'stranger' };
constructor(props) {
super(props); // required before using this
this.state = { visits: 1 };
}
render() {
return <h1>Hello, {this.props.name}. Visit #{this.state.visits}</h1>;
}
}
- 클래스는
Component(또는import React from 'react'와 함께React.Component)를 상속합니다. render()는 필수입니다. 함수 컴포넌트의 본문처럼 순수해야 합니다.this.props와this.state를 읽고 JSX를 반환합니다.this.props에 props가 들어 있습니다. 읽기 전용입니다.- state는
constructor에서 설정하거나(먼저super(props)를 호출합니다), 요즘 더 흔하게는 클래스 필드로 설정합니다:state = { visits: 1 };. static defaultProps는 React 19에서도 클래스에서는 여전히 동작합니다(함수 컴포넌트에서만 제거되었습니다).
this.setState는 병합하고, 업데이터를 받을 수 있습니다
setState는 useState의 setter와 두 가지가 다릅니다. 넘긴 객체를 현재 state에 병합하므로 이름을 지정한 키만 업데이트합니다. 그리고 모든 React state 업데이트처럼 즉시 적용되지 않으므로, 다음 값이 이전 값에 의존한다면 업데이터 형태를 쓰세요.
"+3 with objects"는 세 호출 모두 같은 this.state.likes를 읽으므로 1만 더합니다. "+3 with updaters"는 3을 더합니다. "Move to Paris"는 name과 likes를 유지하지만(병합) zip을 잃습니다. 병합이 한 단계 깊이까지만 이루어지기 때문입니다. 유지하려면 address: { ...this.state.address, city: 'Paris' }라고 쓰세요. setState의 선택적인 두 번째 인자는 업데이트 후에 실행되며, 콘솔은 그곳에서 새 주소를 보여 줍니다.
이벤트 핸들러 바인딩
onClick={this.handleClick}으로 넘긴 메서드는 나중에 객체 없이 일반 함수로 호출되므로, 그 안의 this는 undefined입니다.
첫 번째 버튼은 undefined의 setState를 읽는 것에 관한 TypeError를 기록합니다. 두 번째 버튼은 클래스 필드의 화살표 함수가 인스턴스가 만들어질 때 this를 붙잡으므로 동작합니다. 레거시 코드에서 여전히 흔한 예전 해결책은 생성자에서 바인딩하는 것입니다.
constructor(props) {
super(props);
this.state = { on: false };
this.handleClick = this.handleClick.bind(this);
}
인라인 화살표 함수 onClick={() => this.handleClick()}도 동작합니다.
생명주기 메서드
클래스는 정해진 이름의 메서드로 마운트, 업데이트, 언마운트에 반응합니다. 가장 많이 보게 될 것들은 다음과 같습니다.
componentDidMount(): 첫 렌더링이 DOM에 들어간 뒤. 타이머, 구독, 요청을 시작하세요.componentDidUpdate(prevProps, prevState): 업데이트마다 그 뒤. 작업하기 전에 이전 값과 비교하세요. 그렇지 않으면 반복에 빠집니다.componentWillUnmount(): 제거 전.componentDidMount가 시작한 것을 되돌리세요.shouldComponentUpdate(nextProps, nextState): 렌더링을 건너뛰려면false를 반환하세요.
컴포넌트 생명주기 페이지에 전체 목록, 실행 순서, 그리고 모든 메서드를 기록하는 컴포넌트가 있습니다.
클래스를 함수 컴포넌트로 바꾸기
다음은 흔한 생명주기 메서드 세 개를 모두 쓰는 클래스입니다. 1초마다 틱하고, label prop이 바뀌면 로그를 남기고, 제거될 때 타이머를 멈춥니다.
그리고 같은 컴포넌트를 함수로 쓴 것입니다. 각 생명주기 메서드가 이펙트의 일부가 되고, 관련된 setup과 클린업이 두 메서드에 나뉘지 않고 서로 옆에 있습니다.
두 타이머 모두 같은 방식으로 세고, label이 바뀔 때마다 로그를 남깁니다. 이펙트는 첫 렌더링 후에도 실행되므로 함수 버전은 마운트 시에도 한 번 로그를 남깁니다. 어떤 변환이든 단계는 다음과 같습니다.
this.props.x는 구조 분해한 prop{ x }가 됩니다.this.state의 각 키는 자기만의useState가 됩니다. setter는 병합하지 않고 교체한다는 점을 기억하세요.- 메서드는 컴포넌트 안의 일반 함수가 됩니다. 바인딩이 필요 없습니다.
componentDidMount,componentDidUpdate,componentWillUnmount는useEffect호출이 되며, 언제 실행되는지가 아니라 무엇을 하는지에 따라 묶습니다.createRef는useRef가 되고,this.context와 함께 쓰는static contextType은useContext나use가 됩니다.
에러 바운더리는 클래스로 남겨 두세요. 대응하는 함수 형태가 없습니다.
PureComponent
일반 클래스 컴포넌트는 부모가 렌더링될 때마다 렌더링됩니다. PureComponent는 먼저 새 props와 state를 이전 것과 한 단계 깊이로 비교하고, 아무것도 바뀌지 않았으면 렌더링을 건너뜁니다. React.memo의 클래스 버전입니다.
버튼을 클릭해 보세요. PureLabel은 같은 text를 보므로 PlainLabel rendered만 기록됩니다. 둘 다에 text={'hello ' + clicks}를 넘기면 클릭할 때마다 둘 다 렌더링됩니다.
비교가 얕기 때문에, 부모가 렌더링될 때마다 새 객체나 함수가 되는 prop(style={{ color: 'red' }}, onClick={() => ...})은 비교를 무력화합니다. React.memo와 같은 함정입니다.
React 19가 클래스에서 제거한 것
오래된 클래스 코드를 유지 보수한다면, 몇 가지 패턴은 React 19에서 더 이상 동작하지 않습니다.
- 문자열 ref(
this.refs.input과 함께 쓰는ref="input").createRef()나 ref 콜백을 쓰세요. - 레거시 컨텍스트(
contextTypes,childContextTypes,getChildContext).static contextType과 함께createContext를 쓰세요. propTypes는 무시됩니다. TypeScript나 직접 만든 검사를 쓰세요.findDOMNode는 사라졌습니다. 대신 DOM 요소에 ref를 붙이세요.
예전의 componentWillMount, componentWillReceiveProps, componentWillUpdate 메서드도 레거시입니다. 이전 이름과 UNSAFE_ 이름(UNSAFE_componentWillMount 등) 모두로 여전히 실행되지만, 지원 중단되었고 StrictMode가 경고합니다. 그 코드는 componentDidMount, componentDidUpdate, getDerivedStateFromProps로 옮기세요.
자주 묻는 질문
React에서 클래스 컴포넌트는 지원 중단되었나요?
아니요. React 19에서도 여전히 지원되며 계속 동작하지만, React 문서는 새 코드에 훅을 쓰는 함수 컴포넌트를 권장합니다. 에러 바운더리에는 여전히 클래스가 필요합니다.
클래스 컴포넌트와 함수 컴포넌트의 차이는 무엇인가요?
클래스 컴포넌트는 state를 this.state에 보관하고, this.setState로 업데이트하며, 이름 있는 메서드로 생명주기에 반응합니다. 함수 컴포넌트는 같은 일에 훅(useState, useEffect)을 쓰고, this가 필요 없으며, 더 짧습니다.
setState는 state를 즉시 업데이트하나요?
아니요. this.setState는 업데이트를 예약하고 React는 나중에 렌더링하므로, 호출 직후에도 this.state는 이전 값을 가지고 있습니다. 새 값이 이전 값에 의존한다면 this.setState((state) => ...)처럼 함수를 넘기거나, 두 번째 인자인 콜백이나 componentDidUpdate에서 결과를 읽으세요.
이벤트 핸들러 안에서 this가 undefined인 이유는 무엇인가요?
onClick={this.handleClick}으로 넘긴 클래스 메서드는 객체 없이 호출되므로 this가 undefined입니다. 핸들러를 클래스 필드 화살표 함수(handleClick = () => { ... })로 정의하거나 생성자에서 바인딩하세요.
PureComponent란 무엇인가요?
props와 state가 이전 것과 얕은 비교로 같으면 렌더링을 건너뛰는 Component입니다. React.memo의 클래스 버전입니다.