Functional Component — 일반 JavaScript 함수를 사용하여 React 컴포넌트를 생성하는 방법으로, React 16.8 릴리스와 훅의 등장 이후 표준이 되었습니다. 함수형 컴포넌트는 props를 받아들이고 React.Component 상속 없이 React 요소를 반환합니다. React, 2024에 따르면, React 생태계의 97% 새로운 컴포넌트가 함수로 작성됩니다.
핵심 요점
함수형 컴포넌트는 props 객체를 받아들이고 React 요소를 반환하는 함수로 정의된 React 컴포넌트입니다. React 16.8 이전에는 이러한 컴포넌트를 상태 비저장(stateless)이라고 불렀는데, 내부 상태를 관리할 수 없었기 때문입니다.
React 16.8(2019년 2월)에서 훅이 도입되면서 함수형 컴포넌트는 상태(useState), 부작용(useEffect), 컨텍스트(useContext) 및 ref(useRef)에 접근할 수 있게 되었습니다. ‘상태 비저장’이라는 용어는 구식이 되었습니다 — 현대의 함수형 컴포넌트는 상태를 완전히 관리합니다.
React는 공식적으로 함수형 컴포넌트를 컴포넌트 생성의 기본 방법으로 권장합니다. react.dev의 모든 문서, 예제, 튜토리얼 및 새로운 API(Server Components, Actions)는 함수형 접근 방식으로 구축되었습니다. 클래스 컴포넌트는 제거되지 않았지만, React의 새로운 기능은 훅을 통해서만 사용할 수 있습니다.
가장 간단한 함수형 컴포넌트는 JSX를 반환하는 함수입니다. props를 인수로 받아들이고, 생성자, super 호출 또는 this 바인딩이 필요하지 않습니다. Props는 함수 인수를 통해 직접 접근할 수 있습니다 — this.props가 필요 없습니다.
// Functional component without state
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
// Arrow function (common variant)
const Greeting = ({ name }) => (
<h1>Hello, {name}!</h1>
);
상태를 추가하려면 useState를 사용하세요. 훅은 현재 상태와 설정자 함수라는 두 값의 튜플을 반환합니다. 클래스 기반 this.setState와 달리, useState는 이전 값과 병합하는 대신 값을 완전히 대체합니다.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
훅은 현대 React의 기초입니다. 각 훅은 특정 작업을 해결합니다: useState — 상태, useEffect — 부작용, useContext — 컨텍스트 접근, useRef — DOM 요소 참조, useCallback 및 useMemo — 메모이제이션.
useEffect는 componentDidMount, componentDidUpdate 및 componentWillUnmount를 동시에 대체합니다. 첫 번째 인수는 효과가 있는 콜백이고, 두 번째는 의존성 배열입니다. 배열이 비어 있으면 효과는 마운트 후 한 번 실행됩니다. 의존성이 지정되면 그 중 하나라도 변경될 때 실행됩니다.
import React, { useState, useEffect } from 'react';
function UserData({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let cancelled = false;
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
if (!cancelled) setUser(data);
});
// Cleanup - analog of componentWillUnmount
return () => { cancelled = true; };
}, [userId]); // Dependency
return <div>{user?.name}</div>;
}
함수형 컴포넌트의 주요 장점은 커스텀 훅을 통한 로직 재사용입니다. 클래스 컴포넌트에서 로직이 componentDidMount와 componentDidUpdate 사이에서 중복되었던 반면, 함수형 컴포넌트에서는 use 접두사가 있는 별도의 함수로 추출됩니다. 커스텀 훅은 HOC 및 render props의 주요 대안입니다.
React는 컴포넌트 간 코드 재사용을 위해 상속보다 합성을 권장합니다. 함수형 컴포넌트는 합성에 이상적입니다: 하나의 컴포넌트가 여러 다른 컴포넌트를 포함하고 props와 children을 전달할 수 있습니다.
Children 패턴 — 컴포넌트가 children prop을 통해 자식 요소를 받는 경우 — 자연스럽게 구현됩니다: function Layout({ children }). Vue 슬롯의 유사 기능은 명명된 컴포넌트 props를 통해 구현됩니다: function Page({ header, sidebar, content }).
상속은 React에서 거의 사용되지 않습니다. 기술적으로 extends를 지원하는 클래스 컴포넌트조차도 한 단계보다 깊은 계층 구조를 형성하는 경우는 드뭅니다. 합성을 사용한 함수형 컴포넌트는 상속 없이 코드 재사용 요구를 완전히 충족시킵니다.
함수형 컴포넌트에는 내장 최적화 메커니즘이 있습니다. React.memo — 함수용 PureComponent에 해당: props가 변경되지 않은 경우 재렌더링을 방지합니다(얕은 비교). useMemo는 렌더링 간 계산 결과를 캐시합니다.
import React, { useMemo } from 'react';
// React.memo - memoize component
const ExpensiveList = React.memo(({ items }) => {
return <ul>
{items.map(item =>
<li key={item.id}>{item.name}</li>
)}
</ul>;
});
// useMemo - caching result
function Dashboard({ transactions }) {
const summary = useMemo(() => {
return transactions.reduce((acc, t) => acc + t.amount, 0);
}, [transactions]);
return <p>Total: {summary}</p>;
}
useCallback은 콜백 함수를 메모이제이션하여 매 렌더링마다 다시 생성되지 않도록 합니다. 이는 React.memo로 래핑된 자식 컴포넌트에 함수를 전달할 때 중요합니다: useCallback이 없으면 함수 참조가 변경될 때마다 자식 컴포넌트가 재렌더링됩니다.
접근 방식의 차이는 구문을 넘어섭니다. 함수형 컴포넌트는 클로저입니다: 상태와 효과가 특정 렌더링에 바인딩됩니다. 클래스 컴포넌트는 this를 사용합니다: 상태는 항상 this.state에서 가져오며, 비동기 콜백이 실행될 때는 오래된 상태일 수 있습니다.
| 측면 | 함수형 | 클래스 |
|---|---|---|
| 상태 | useState(독립적) | this.state + this.setState |
| 효과 | 의존성 있는 useEffect | componentDidMount + componentDidUpdate |
| 컨텍스트 | useContext | Context.Consumer 또는 static contextType |
| 최적화 | React.memo + useMemo | PureComponent + shouldComponentUpdate |
| 로직 재사용 | 커스텀 훅 | HOC / Render props |
| this | 없음 | 생성자에서 bind |
함수형 컴포넌트는 선형 실행 흐름을 제공합니다. 클래스 컴포넌트에서는 관련 로직이 여러 라이프사이클 메서드에 흩어져 있는 경우가 많습니다. 예를 들어, WebSocket 구독은 componentDidMount에서 설정되고 componentWillUnmount에서 해제됩니다. 함수형 컴포넌트에서는 두 작업 모두 정리 함수를 반환하는 단일 useEffect에 있습니다.
TypeScript는 클래스보다 함수형 컴포넌트와 더 자연스럽게 작동합니다. Props 타입 지정 — 함수 인수에 대한 인터페이스입니다. 반환 타입은 JSX.Element 또는 ReactNode입니다. useState는 초기값에서 자동으로 타입을 추론합니다.
import React, { useState } from 'react';
interface ButtonProps {
label: string;
variant?: 'primary' | 'secondary';
onClick: () => void;
}
const Button: React.FC<ButtonProps> =
({ label, variant = 'primary', onClick }) => (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{label}
</button>
);
최신 TypeScript에서는 판별 유니온 타입으로 useReducer를 타입 지정하고, 커스텀 훅에서 제네릭을 사용하며, React.PropsWithChildren을 통해 엄격한 자식 검증이 가능합니다. TypeScript와 함께 사용하는 함수형 컴포넌트는 추가 추상화 없이 완전한 타입 안전성을 제공합니다.
자주 묻는 질문
함수형 컴포넌트는 더 간단합니다: 생성자, bind, this 또는 흩어진 라이프사이클 메서드가 없습니다. 훅은 관련 로직을 그룹화할 수 있습니다. React는 클래스가 아닌 훅 개발에 리소스를 투자합니다. Server Components 및 새로운 기능은 함수에서만 사용할 수 있습니다.
네, useEffect를 통해 내부에 타이머를 두고 clearInterval을 위한 정리 함수를 반환하면 됩니다. 클로저에 주의하세요: 카운터를 증가시키는 경우 setState의 함수형 폼을 사용하세요: setCount(c => c + 1) 클로저의 이전 값에 의존하지 않기 위해서입니다.
이전 값을 저장하려면 useRef를 사용하세요. 각 렌더링 후 useEffect에서 ref를 업데이트하세요. 커스텀 훅 usePrevious가 깔끔한 비교에 도움이 됩니다: const prev = usePrevious(value);
React.memo는 전체 컴포넌트를 메모이제이션하는 HOC입니다: props가 변경되지 않으면 재렌더링을 방지합니다. useMemo는 컴포넌트 내부의 특정 값을 메모이제이션하는 훅입니다: 의존성 배열에 따라 렌더링 간 함수 결과를 캐시합니다.
가장 간단한 방법은 props에서 초기화하는 useState와 동기화를 위해 props에 의존하는 useEffect를 사용하는 것입니다. 드문 경우, React는 입력 데이터가 변경될 때 key를 사용하여 컴포넌트를 강제로 다시 생성하는 것을 권장합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.