React
컴포넌트 (클래스형 vs 함수형)
doonii
2025. 1. 9. 18:05
컴포넌트란?
- HTML 요소를 반환하는 JavaScript 함수 또는 클래스
- UI를 구성하는 독립적이고 재사용 가능한 코드 조각
컴포넌트의 핵심 개념
1. props (속성)
- 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하는 방식
- 읽기 전용이며 컴포넌트 내부에서 변경할 수 없다.
function Child(props) {
return <div>{props.message}</div>;
}
function Parent() {
return <Child message="안녕하세요!" />;
}
2. state (상태)
- 컴포넌트 내부에서 관리되는 데이터
- 함수형 컴포넌트에서는 useState 훅을 사용해 관리한다.
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>
증가
</button>
</div>
);
}
3. 생명주기
- 컴포넌트가 생성(mount)되고, 업데이트(update)되고, 제거(unmount)되는 과정
- 함수형 컴포넌트에서는 useEffect 훅으로 관리된다.
function Example() {
useEffect(() => {
console.log('컴포넌트가 마운트되었습니다');
return () => {
console.log('컴포넌트가 언마운트됩니다');
};
}, []);
}
컴포넌트 활용의 장점
- 한번 만든 컴포넌트는 여러 곳에서 재사용이 가능해 개발 효율이 향상된다.
- 독립적인 단위로 관리되어 코드를 체계적으로 관리할 수 있고 유지보수가 용이하다.
컴포넌트를 정의하는 두가지 주요 방법
클래스형 컴포넌트
- ES6 클래스를 사용하여 정의되며, React.Component 클래스를 상속받는다.
- React 초기부터 사용된 전통적인 방식으로 리액트 훅이 도입된 이후 현재는 함수형 컴포넌트 사용이 권장된다.
- this 키워드를 사용하고 생명주기 메서드(lifecycle methods)를 통해 상태를 관리할 수 있다.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
// 초기 상태
};
}
render() {
return (
<div>
{/* JSX */}
</div>
);
}
}
함수형 컴포넌트
- JavaScript 함수로 정의되며, props를 인자로 받아 React 엘리먼트를 반환한다.
- 클래스형보다 더 간결하고 이해하기 쉬운 문법을 가지며 메모리 자원을 클래스형 컴포넌트보다 덜 사용한다.
- React 16.8 이후 도입된 Hooks를 사용해 상태 관리와 생명주기 기능을 구현할 수 있다.
function MyComponent(props) {
// hooks 사용
const [state, setState] = useState(initialState);
return (
<div>
{/* JSX */}
</div>
);
}
클래스형 컴포넌트의 생명주기 메서드
render()
- 클래스 컴포넌트의 유일한 필수 메서드
- 컴포넌트 UI를 렌더링하기 위해 사용
- 마운트, 업데이트 과정에서 호출된다.
- 순수함수여야 하며 동일한 입력(props, state)에 대해 동일한 출력(React 엘리먼트)을 반환해야 한다.
componentDidMount()
- 컴포넌트가 마운트되고 준비되는 즉시 실행
- 주로 API 호출, 구독 설정, DOM 조작 등의 작업에 사용
componentDidMount() {
// API 호출
fetch('api/data')
.then(response => response.json())
.then(data => this.setState({ data }));
}
componentDidUpdate()
- 컴포넌트 업데이트 직후 실행
- props나 state 변화에 따른 DOM 업데이트에 사용
componentWillUnmount()
- 컴포넌트 제거 직전에 호출
- 클린업 작업 수행(타이머 제거, 네트워크 요청 취소, 구독 해제)
componentWillUnmount() {
clearInterval(this.timer);
this.subscription.unsubscribe();
}
성능 최적화 관련 메서드
shouldComponentUpdate()
- 불필요한 리렌더링 방지를 위해 사용
- 성능 최적화 상황에서만 사용 권장됨
static getDerivedStateFromProps()
- render() 호출 직전에 실행
- static 메서드로 this 접근 불가
getSnapShotBeforeUpdate()
- DOM 업데이트 직전 호출
- 윈도우 크기 조절이나 스크롤 위치 등 DOM 정보 저장에 유용
에러 처리 메서드
getDerivedStateFromError()
- 자식 컴포넌트 에러 발생 시 호출
- 에러 UI 표시를 위한 상태 업데이트
componentDidCatch()
- 에러 발생 후 호출
- 에러 로깅 등에 사용
클래스 컴포넌트의 두 가지 유형
Component
- 기본 React 컴포넌트
- state 변경 시 즉시 리렌더링됨
PureComponent
- 얕은 비교 후 결과가 다를 때만 렌더링 (자동 성능 최적화)
- 단순한 props와 state에 적합
- 얕은 비교만 수행하기 때문에 객체와 같은 복잡한 구조의 데이터 변경은 감지하지 못하므로 제대로 작동하지 않을 수 있다.
클래스 컴포넌트의 한계
- 데이터 흐름 추적이 어려움
- 여러 메서드에서 state의 업데이트가 일어날 수 있고, 생명주기 메서드의 순서 관리가 필요하다.
- 내부 로직의 재사용이 어려움
- 또 다른 고차 컴포넌트로 감싸거나 props를 넘겨줘야하는데, 이때 wrapper hell(래퍼 지옥)이 발생할 수 있음
- 컴포넌트 크기 증가
- 번들링 최적화 어려움
- 핫 리로딩 불리
- hot reloading : 코드에 변경 사항 발생 시 앱 재시작 없이도 변경된 코드만 빠르게 업데이트하는 기법
함수컴포넌트와 클래스 컴포넌트의 차이
생명주기 관리
- 함수형 : useEffect 훅으로 관리
- 클래스형 : 생명주기 메서드 사용
// 함수형
useEffect(() => {
// componentDidMount, componentDidUpdate
return () => {
// componentWillUnmount
};
}, []);
// 클래스형
componentDidMount() {
// 마운트 시 실행
}
렌더링 값 처리
- 함수형 : 렌더링 시점의 값 보전
- 클래스형 : this 를 통한 최신 값 참조
성능 최적화
- 함수형 : useMemo, useCallback, React.memo
- 클래스형 : shouldComponentUpdate, PureComponent
// 함수형
const MemoizedComponent = React.memo(function MyComponent(props) {
// ...
});
// 클래스형
class MyComponent extends React.PureComponent {
// ...
}
함수형 컴포넌트가 권장되는 이유?
- 코드가 간결하고 재사용성이 용이하다.
- 불필요한 보일러플레이트 코드를 줄일 수 있어 개발 효율성이 향상된다.
- 훅을 통해 상태관리를 유연하게 할 수 있다.
- 인스턴스를 생성하지 않아 메모리 사용량이 적으므로 성능이 향상된다.
- React 공식 문서에서도 함수형 컴포넌트와 Hooks 사용을 권장하고 있음