React.Component는 React에서 상속을 사용하여 컴포넌트를 생성하기 위한 기본 클래스입니다. 버전 16.8에서 훅이 도입되기 전에는 클래스 컴포넌트가 상태와 생명주기를 관리하는 유일한 방법이었습니다. React, 2024에 따르면, 클래스 컴포넌트는 여전히 지원되지만, 새 프로젝트에는 훅이 있는 함수형 컴포넌트가 권장됩니다.
핵심 사항
React.Component는 상태, props 및 컴포넌트 생명주기 작업을 위한 내장 메서드를 제공하는 추상 클래스입니다. 클래스 컴포넌트를 만들려면 React.Component를 확장하는 클래스를 정의하고 render 메서드를 구현해야 합니다.
React.Component는 React의 첫 번째 버전(2013)에서 등장했으며, 5년 동안 컴포넌트를 만드는 주요 방식으로 남아 있었습니다. React 생태계는 클래스를 중심으로 구축되었습니다: 고차 컴포넌트(HOC), render props, 컨텍스트 — 이러한 모든 패턴은 클래스 구문을 사용하여 구현되었습니다.
2019년 React 16.8 출시와 함께 React 팀은 클래스의 대안으로 훅을 도입했습니다. 훅은 동일한 문제 — 상태, 효과, 컨텍스트 — 를 해결했지만, 상속과 복잡한 컨텍스트 바인딩 없이 해결했습니다. React.Component는 완전히 지원되지만, 새 프로젝트에는 함수형 접근 방식이 권장됩니다.
최소한의 클래스 컴포넌트는 React 요소를 반환하는 단일 render 메서드를 가진 클래스입니다. render는 유일한 필수 메서드입니다. this.props와 this.state의 순수 함수여야 합니다: 동일한 props와 상태 세트는 동일한 결과를 생성합니다.
import React, { Component } from 'react';
class Welcome extends Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
생성자는 마운팅 전에 실행됩니다. 부모 클래스에 props를 전달하기 위해 super(props)를 호출하고 this.state를 초기화합니다. 생성자를 다른 목적 — 구독, API 호출 — 에 사용하는 것은 권장되지 않습니다. 이를 위한 componentDidMount가 있습니다.
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return <p>Count: {this.state.count}</p>;
}
}
this.state는 컴포넌트의 데이터를 포함하는 객체입니다. 직접 변경하는 것(this.state.count = 1)은 금지됩니다 — 리렌더링을 트리거하지 않으며 예측 가능성을 깨뜨립니다. 상태는 this.setState를 통해 업데이트되어야 하며, 새 상태 객체를 생성하고 리렌더링을 트리거합니다.
setState는 객체나 함수를 받을 수 있습니다. 새 상태가 이전 상태에 의존하는 경우 함수형이 선호됩니다: this.setState(prev => ({ count: prev.count + 1 })). React는 성능 최적화를 위해 여러 setState 호출을 하나의 배치로 그룹화할 수 있습니다.
class Toggle extends Component {
constructor(props) {
super(props);
this.state = { isOn: false };
}
handleClick() {
this.setState(prev => ({ isOn: !prev.isOn }));
}
render() {
return (
<button onClick={() => this.handleClick()}>
{this.state.isOn ? 'ON' : 'OFF'}
</button>
);
}
}
setState의 중요한 특성은 비동기라는 것입니다. setState를 호출한 후 상태가 즉시 업데이트되지 않습니다. 업데이트 후 작업을 수행해야 하는 경우 두 번째 인수로 콜백을 전달하세요: this.setState(newState, () => console.log(this.state)).
클래스 컴포넌트에는 React가 특정 시점에 호출하는 미리 정의된 메서드가 있습니다. 마운팅 — constructor → render → componentDidMount. 업데이트 — render → componentDidUpdate. 언마운팅 — componentWillUnmount.
| 메서드 | 단계 | 목적 |
|---|---|---|
| constructor | 마운팅 | 상태 초기화 및 메서드 바인딩 |
| render | 매번 | React 요소 반환 (필수) |
| componentDidMount | 마운팅 후 | API 요청, 구독, 타이머 |
| componentDidUpdate | 업데이트 후 | props/상태 변경에 반응 |
| componentWillUnmount | 제거 전 | 구독, 타이머, 이벤트 리스너 정리 |
componentDidMount는 첫 번째 렌더링 후 사이드 이펙트를 위한 주요 위치입니다. 여기서 HTTP 요청을 수행하고, WebSocket 연결을 설정하고, 타이머를 시작합니다. componentWillUnmount에서 구독을 정리하지 않으면 메모리 누수가 발생합니다.
class UserProfile extends Component {
componentDidMount() {
fetch(`/api/users/${this.props.userId}`)
.then(res => res.json())
.then(data => this.setState({ user: data }));
}
componentWillUnmount() {
// Cancel request on unmount
}
render() {
return <div>{this.state.user?.name}</div>;
}
}
JavaScript에서 클래스 메서드는 기본적으로 인스턴스에 바인딩되지 않습니다. 메서드를 콜백으로 전달하면 — 이벤트 핸들러에, 타이머에 — this는 undefined(엄격 모드) 또는 window(비엄격 모드)가 됩니다. 바인딩이 없으면 this.setState가 오류를 발생시킵니다.
바인딩하는 세 가지 방법이 있습니다: 생성자에서 bind, 콜백에서 화살표 함수, 화살표 함수가 있는 class field. 첫 번째 방법이 React의 공식 권장 사항입니다: 생성자에서 한 번 바인딩하고, 렌더링마다 함수를 다시 만들지 않습니다.
// Method 1: bind in constructor (recommended)
class MyComponent extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ clicked: true });
}
render() {
return <button onClick={this.handleClick}>Click</button>;
}
}
Class fields — 자동 바인딩이 있는 현대적인 구문입니다. 클래스 속성의 화살표 함수는 생성자의 스코프에서 this를 캡처합니다. 단점은 메서드가 프로토타입이 아닌 각 인스턴스에서 생성되어, 동일한 유형의 수백 개 컴포넌트에서 메모리 사용량이 증가한다는 것입니다.
클래스와 함수 중 선택은 단순한 구문 문제가 아닙니다. 훅이 있는 함수형 컴포넌트는 클래스에 내재된 문제를 해결합니다: 다른 생명주기 메서드(componentDidMount + componentDidUpdate)에서의 로직 중복, this의 복잡성, 관련 로직을 재사용 가능한 블록으로 추출할 수 없는 점.
함수형 컴포넌트는 테스트하기 쉽습니다 — 내부 상태가 없고(훅 이전에는 클래스에서만 가능했습니다), 콜백 확인을 위해 DOM 마운팅이 필요하지 않습니다. React는 모든 새 프로젝트에 함수형 컴포넌트를 권장합니다. 문서와 예제도 함수형으로 작성됩니다.
클래스 컴포넌트는 레거시 코드 작업 시 최선의 선택으로 남아 있습니다. 프로젝트가 2019년 이전에 시작된 경우, 수천 개의 클래스 컴포넌트가 있을 가능성이 높습니다. 이를 모두 하나의 스프린트에서 훅으로 다시 작성하는 것은 위험합니다. React는 하위 호환성을 보장합니다: 클래스 컴포넌트는 제거되지 않습니다.
점진적 마이그레이션은 컴포넌트 분석으로 시작됩니다. 상태가 없는 단순한 컴포넌트가 먼저 변환됩니다 — 함수로 쉽게 대체됩니다. 그 다음 — 하나 또는 두 개의 상태 필드가 있는 컴포넌트로, useState가 this.state와 this.setState를 직접 대체합니다.
여러 생명주기 메서드가 있는 복잡한 컴포넌트는 useEffect를 사용하여 변환됩니다. componentDidMount + componentDidUpdate의 로직은 다른 종속성을 가진 별도의 useEffect 호출로 분할됩니다. componentWillUnmount는 useEffect 내부의 반환 함수로 대체됩니다. 복잡한 상태 로직에는 useReducer를 사용하세요.
자동 마이그레이션 도구 — @phenomnomnominal/ts-react-class-to-hook 또는 codemod 스크립트 — 는 단순한 클래스를 자동으로 변환할 수 있습니다. 복잡한 경우에는 마이그레이션이 수동 작업으로 남아 있으며, 원본 컴포넌트의 로직을 이해해야 합니다.
자주 묻는 질문
네. super(props)는 부모 React.Component에 props를 전달합니다. 이 호출이 없으면 생성자 내에서 this.props가 undefined가 됩니다. render 및 생명주기 메서드에서는 super 없이도 props를 사용할 수 있지만, 이는 구현의 부수적 동작입니다.
React는 불필요한 리렌더링을 줄이기 위해 여러 setState 호출을 하나의 배치로 그룹화합니다. setState가 동기식이었다면 각 호출이 즉시 렌더링을 트리거하여 대량 업데이트 중 성능이 저하됩니다.
아니요. 훅은 함수형 컴포넌트에서만 작동합니다. 클래스 컴포넌트가 있는 프로젝트에서는 모든 기존 패턴을 사용할 수 있습니다: render props, HOC 및 컨텍스트용 Consumer. 훅으로 마이그레이션하려면 클래스를 함수로 변환해야 합니다.
React.PureComponent는 props와 상태의 얕은 비교로 shouldComponentUpdate를 구현합니다. setState에서 항상 리렌더링하는 Component와 달리, PureComponent는 데이터가 변경되지 않은 경우 리렌더링을 건너뜁니다. 이는 복잡한 중첩이 없는 컴포넌트를 위한 최적화입니다.
componentDidCatch와 getDerivedStateFromError를 사용하세요. componentDidCatch(error, info)는 오류를 기록합니다. getDerivedStateFromError는 폴백 UI를 표시하기 위해 상태를 업데이트합니다. 이 메서드는 클래스 컴포넌트에서만 작동합니다 — 함수는 ErrorBoundary 래퍼 컴포넌트가 필요합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.