React.FC에 대해

서성원·2026년 3월 9일

리액트

목록 보기
28/28
post-thumbnail

React.FC를 지양해야 할 때

지금은 사용하지 않는 CRA에서는 React.FC를 템플릿에서 제거한 PR을 확인할 수 있다. 그렇다면 왜 React.FC 사용을 지양해야 할까? 무조건 쓰지 말아야 하는 걸까?

// ❌ 지양                                                                                                                                      
  const Button: React.FC<ButtonProps> = ({ children }) => {                                                                                       
    return <button>{children}</button>;                                                                                                           
  };
  
// ✅ 권장
  const Button = ({ children }: ButtonProps) => {
    return <button>{children}</button>;
  };

1. children 이 암묵적으로 포함된다.

React.FC는 자동으로 children을 props에 추가하므로, children을 받지 않는 컴포넌트에서도 children을 넘길 수 있어서 버그 가능성이 생긴다. (React 18에서는 제거됐지만 레거시에서 고려할 필요)

2. 제네릭 컴포넌트를 못 만든다.

// ❌ React.FC로는 불가능
const List: React.FC<ListProps<T>> = ... // T를 어디에?

// ✅ 직접 선언하면 가능
const List = <T,>(props: ListProps<T>) => { ... };

3. compound component가 번거롭다.

예시: Dropdown

// ❌ React.FC — sub-component마다 타입 선언에 다 적어야 함                                                                                     
  const Dropdown: React.FC<DropdownProps> & {                                                                                                     
    Trigger: React.FC<TriggerProps>;                                                                                                              
    Menu: React.FC<MenuProps>;
    Item: React.FC<ItemProps>;
  } = ({ children }) => {
    return <div className="dropdown">{children}</div>;
  };

  Dropdown.Trigger = ({ children }) => <button>{children}</button>;
  Dropdown.Menu = ({ children }) => <ul>{children}</ul>;
  Dropdown.Item = ({ label, onClick }) => <li onClick={onClick}>{label}</li>;

React.FC<DropdownProps>로 타입을 선언하면, TypeScript는 Dropdown이 정확히 그 타입만 가진다고 본다.

  // TypeScript가 보는 관점:
  const Dropdown: React.FC<DropdownProps> = ...                                                                                                        
  // → Dropdown의 타입 = (props: DropdownProps) => ReactElement | null
  // → 그 외 프로퍼티? 없음

Dropdown.Trigger는 React.FC에 없는 프로퍼티기에, 미리 “이 함수에는 Trigger, Menu, Item이 있어.”라고 intersection(&) 으로 알려줘야 한다.

const Dropdown: React.FC<DropdownProps> & {
    Trigger: React.FC<TriggerProps>;   // ← "Trigger도 있을 거야"
    Menu: React.FC<MenuProps>;         // ← "Menu도 있을 거야"
    Item: React.FC<ItemProps>;         // ← "Item도 있을 거야"
  } = ({ children }) => { ... };

하지만 서브 컴포넌트가 많아질수록 React.FC로 타입 선언을 일일이 해 줘야 하기에 매우 번거롭고, 코드도 복잡해진다.

반면 FC 없이는 TS가 타입 추론만 하고 고정하지는 않는다. 그래서 프로퍼티를 붙이면 자동으로 타입을 추론한다.

const Dropdown = ({ children }: DropdownProps) => { ... };

Dropdown.Trigger = ... // 자동 추론해서 반영

정리

FC를 쓰면 타입이 잠겨서 미리 다 열거해야 하고, 안 쓰면 타입이 열려있어서 그냥 붙이기만 하면 되는 것으로 이해할 수 있겠다.

React.FC를 사용하는 예시

const TRIGGER_TYPE_ICON_MAP: Record<
TriggerType,
React.FC<{ color?: string; size?: string }>
> 

여기서 React.FC를 쓴 이유는 compound component가 아니라 Record의 value 타입을 지정하는 용도이기 때문이다.

⇒ “이 Record의 value는 color와 size props를 받는 React 컴포넌트다.”라는 타입 제약을 뜻한다.

만약 React.FC없이 같은 걸 표현하려면

  const TRIGGER_TYPE_ICON_MAP: Record<
    TriggerType,
    (props: { color?: string; size?: string }) => React.ReactElement | null
  >

더 길고 번거롭다. 그래서 컴포넌트 타입을 참조용으로 쓸 때는 React.FC가 간결하다.

예시는 타입 참조기 때문에 children을 암묵적으로 포함하지 않으며, 타입 파라미터로 넘기면 되니까 제네릭과 상관이 없고, compound와도 무관하기 때문에 가독성과 간결함을 위해 사용한 것이다.

ex. 타입 참조 시 - FC로 제네릭 문제는 없음

  type ListProps<T> = {
    items: T[];
    renderItem: (item: T) => React.ReactNode;
  };

  // 타입 참조에서는 T를 구체적으로 지정하니까 문제없음
  type Props = {
    userList: React.FC<ListProps<User>>;
    productList: React.FC<ListProps<Product>>;
  };

선언 시에는 를 함수 앞에 붙여야 하는데 FC 문법 구조상 넣을 곳이 없고, 타입 참조 시에는 이미 <User> 같이 구체적인 타입을 넣기 때문에 문제가 생기지 않는다.

참고

https://www.reddit.com/r/reactjs/comments/ys70t9/is_is_still_problematic_to_use_reactfc_if_our/?tl=ko

https://github.com/facebook/create-react-app/pull/8177

profile
Frontend Developer

0개의 댓글