본문 바로가기
카테고리 없음

[AutoSume 프로젝트 이슈] [React + TypeScript] children 관련 타입 오류와 React Router 연동 시 발생하는 타입 에러 해결법 정리

by 시간기억자 2025. 4. 22.
반응형

💡 에러 개요

React + TypeScript로 작업 중 App.tsx에서 MainLayout 컴포넌트를 사용하는 부분에서 아래와 같은 타입 오류가 발생했다.

// App.tsx
import { Outlet } from "react-router-dom";
import MainLayout from "./layouts/MainLayout";

function App() {
  return (
    <MainLayout>
      <Outlet />
    </MainLayout>
  );
}

⚠️ 발생한 에러 메시지

Type '{ children: Element; }' has no properties in common with type 'IntrinsicAttributes'.ts(2559)
(alias) function MainLayout(): JSX.Element

🔍 에러 원인

이 에러는 MainLayout 컴포넌트가 children을 props로 받을 수 있도록 정의되어 있지 않기 때문에 발생한 것이다.
TypeScript에서는 props로 어떤 값이 들어오는지를 명시적으로 타입 선언해줘야 하며,

그렇지 않으면 해당 JSX 태그에 전달되는 속성들이 허용되지 않는다고 판단하고 에러를 발생시킨다.


📚 관련 개념 정리

  • children : React 컴포넌트 내부에서 다른 컴포넌트를 감싸 사용할 수 있게 해주는 특별한 prop이다.
  • ReactNode : React에서 렌더링 가능한 모든 요소를 포함하는 타입이다.JSX 요소, 문자열, 숫자, 배열, fragment, null 등 모두 포함된다.
  • IntrinsicAttributes : JSX 문법을 해석할 때 사용되는 React 내부 타입이다. props가 명시되지 않은 컴포넌트에 값을 전달하려고 하면 타입이 일치하지 않아 에러가 발생할 수 있다.

✅ 해결 방법

MainLayout 컴포넌트에 전달되는 children의 타입을 명시해주면 해결된다. 아래와 같이 수정한다.

// src/layouts/MainLayout.tsx
import { ReactNode } from "react";

type MainLayoutProps = {
  children: ReactNode;
};

const MainLayout = ({ children }: MainLayoutProps) => {
  return (
    <div className="max-w-screen-md mx-auto px-4 py-6">
      {children}
    </div>
  );
};

export default MainLayout;

 

이제 App.tsx에서는 아래와 같이 문제없이 사용할 수 있다.

<MainLayout>
  <Outlet />
</MainLayout>

 


⚠️ 연장선상에서 발생한 두 번째 오류 : 

App.tsx에서 에러를 수정한 후, 이번에는 `router.tsx` 파일에서 아래와 같은 에러가 발생했다.
 
Property 'children' is missing in type '{}' but required in type 'MainLayoutProps'.ts(2741)
MainLayout.tsx(7, 3): 'children' is declared here.
(alias) function MainLayout({ children }: MainLayoutProps): JSX.Element
import MainLayout

 

이는 MainLayout 컴포넌트가 여전히 children을 필수 props로 받고 있기 때문에, createBrowserRouter에서 다음처럼 사용했을 때:

element: <MainLayout />,

React Router가 children을 직접 전달하지 않아서 타입 오류가 발생한 것이다.

✅ 해결 방법: MainLayout을 React Router 전용으로 리팩토링

이 경우, MainLayout 컴포넌트는 children 대신 Outlet을 사용해야 한다.

React Router의 <Outlet />은 중첩 라우팅 구조에서 하위 라우트의 컴포넌트를 렌더링해주는 전용 컴포넌트다.

아래와 같이 MainLayout을 수정한다:

// src/layouts/MainLayout.tsx
import { Outlet } from 'react-router-dom';
import Header from '../components/layout/Header';
import Footer from '../components/layout/Footer';

export default function MainLayout() {
  return (
    <div className="flex flex-col min-h-screen">
      <Header />
      <main className="flex-1 px-4 py-8 max-w-screen-xl mx-auto w-full">
        <Outlet />
      </main>
      <Footer />
    </div>
  );
}

이제 router.tsx에서도 문제없이 사용 가능하다:

{
  path: '/',
  element: <MainLayout />,
  children: [
    { index: true, element: <Home /> },
    { path: 'list', element: <List /> },
    ...
  ],
}

 


📝 정리

React + TypeScript 환경에서는 컴포넌트에 전달되는 props의 타입을 명확하게 정의하는 것이 매우 중요하다. 
특히 `children`을 사용하는 컴포넌트는 반드시 `props` 타입에 `children: ReactNode`를 포함해야 하며, 
이를 누락하면 JSX 요소를 감싸는 구조에서 타입 오류가 발생할 수 있다.

 

또한, React Router의 `createBrowserRouter`를 사용할 때 `element`로 전달되는 컴포넌트는

`children`을 직접 받는 구조가 아니라 `<Outlet />`을 통해 중첩 라우트를 렌더링해야 한다.

따라서 레이아웃 컴포넌트라면 상황에 따라 `props.children`이 아닌 `Outlet`을 활용한 구성으로 작성해야 타입 오류 없이 동작한다.

결국 이슈의 핵심은 "컴포넌트의 역할에 맞는 props 설계와 타입 정의"이며, 

레이아웃처럼 JSX 요소를 감싸거나 라우팅 구조를 담당하는 컴포넌트는 그 용도에 따라 

`children` 또는 `Outlet` 중 적절한 방식을 선택해야 한다.


🔚 마무리

이슈의 핵심은 컴포넌트에서 children의 타입을 명시하지 않아 발생한 오류와, React Router의 `element` 속성에서

children을 전달하지 않는데도 props로 요구해 생기는 타입 오류라는 두 가지 문제다.

첫 번째는 `children`을 props로 받는 컴포넌트에서 타입을 명확히 정의하지 않아 생긴 문제이며, 

두 번째는 React Router에서는 children이 자동으로 전달되지 않기 때문에 `<Outlet />`을 사용하는 구조로 변경해야 해결되는 문제다.

두 이슈 모두 컴포넌트의 용도에 따라 적절한 타입 정의와 구조 설계만 해주면 쉽게 해결할 수 있다. 

하지만 TypeScript를 처음 접하는 입장에서는 에러 메시지가 익숙하지 않고 원인 파악도 쉽지 않기 때문에, 

비슷한 문제를 겪는 개발자들에게 이 글이 도움이 되었으면 한다.

앞으로도 레이아웃 컴포넌트나 중첩 라우팅을 설계할 때는 props와 타입의 흐름을 명확히 이해하고, 상황에 맞는 구조(`children` vs `Outlet`)를 선택하는 습관을 들이는 것이 중요하다.

반응형

댓글