ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [React 강의] 레이아웃 컴포넌트와 children / Outlet
    JS&React 2026. 2. 7. 14:22

    공통 컴포넌트 : 공통 디자인(헤더, 사이드바 등)의 틀 잡기

    1. children (리액트 기본: "직접 집어넣는 바구니")

    어떤 컴포넌트가 다른 컴포넌트를 감쌀 때, 그 안에 들어가는 내용물을 부모 컴포넌트에게 전달하는 방식

    • 특징: 내가 코드상에서 <Parent><Child /></Parent>라고 직접 감싸줄 때 작동
    • 비유: "선물 상자 안의 내용물". 상자를 열어보기 전까지는 뭐가 들었는지 모르지만, 일단 상자(Parent) 안에 내용물(Child)을 직접 넣어서 전달하는 것과 같다.

    2. Outlet (리액트 라우터: "예약된 자리")

    레이아웃 컴포넌트 안에서 "자식 라우트들이 나타날 위치"를 미리 지정해두는 구멍

    • 특징: 내가 직접 감싸는 게 아니라, 라우터 설정(Router Config)에 따라 주소(URL)가 바뀔 때마다 리액트 라우터가 알아서 컴포넌트를 갈아 끼워준다.
    • 비유: "기차의 예약석". BasicLayout이라는 기차 칸에 Outlet이라는 이름표가 붙은 좌석을 하나 비워둔다. 주소가 /main이면 메인 손님이, /about이면 어바웃 손님이 그 자리에 앉는 방식
    구분 children Outlet
    누가 넣어주나? 개발자가 직접 태그 사이에 넣음 리액트 라우터가 URL에 맞춰 넣어줌
    어디서 쓰나? 일반적인 디자인 컴포넌트 (버튼, 박스 등) 전체 페이지의 레이아웃을 잡을 때
    유연성 아무거나 막 넣을 수 있음 라우터 설정에 등록된 컴포넌트만 들어옴

     

    BasicLayout은 집(부모)이고, children 안에 있는 Main과 About은 그 집의 거실(Outlet)을 차지하러 들어오는 손님(자식)들이다.

    • basicLayout.tsx
    • children 속성으로 컴포넌트 내부에 다른 컴포넌트 적용 가능

    • frontend/ch1/src/layouts/basicLayout.tsx
    import { Outlet } from 'react-router';
    
    function BasicLayout() {
      return (
        <>
          <header className="bg-teal-400 p-5">
            <h1 className="text-2xl md:text-4xl">Header</h1>
          </header>
          <div className="bg-white my-5 w-full flex flex-col space-y-4 md:flex-row md:space-x-4 md:space-y-0">
            <main className="bg-sky-300 md:w-2/3 lg:w-3/4 px-5 py-40">
              <Outlet />
            </main>
            <aside className="bg-green-300 md:w-1/3 lg:w-1/4 px-5 py-40">
              <h1 className="text-2xl md:text-4xl"> Sidebar </h1>
            </aside>
          </div>
        </>
      );
    }
    
    export default BasicLayout;
    • frontend/ch1/src/router/root.tsx
    import { lazy, Suspense } from 'react';
    import { createBrowserRouter } from 'react-router';
    import BasicLayout from '../layouts/basicLayout';
    
    // 로딩 중에 보여줄 임시 컴포넌트
    // eslint-disable-next-line react-refresh/only-export-components
    const Loading = () => <div>Loading...</div>;
    // 페이지를 필요할 때만 로딩하기 위해 lazy와 Suspense 사용
    // 컴포넌트를 필요할 때만 가져오는 지연 로딩(Lazy Loading) 설정
    const Main = lazy(() => import('../pages/main-page'));
    const About = lazy(() => import('../pages/aboutPage'));
    
    const router = createBrowserRouter([
      // {
      //   path: "",
      //   element: <Suspense fallback={<Loading />}><Main /></Suspense>,
      // },
      // {
      //   path: "about",
      //   element: <Suspense fallback={<Loading />}><About /></Suspense>,
      // }
      {
        path: '',
        Component: BasicLayout, // 모든 자식 페이지에 적용될 공통 레이아웃 (부모)
        children: [             // 레이아웃 내부의 <Outlet /> 자리에 들어갈 자식들
          {
            index: true,        // index: true는 path가 빈 문자열일 때 이 요소를 렌더링하라는 의미
            element: (
              <Suspense fallback={<Loading />}>   
                <Main />
              </Suspense>
            ),
          },
          {
            path: 'about',
            element: (
              <Suspense fallback={<Loading />}>
                <About />
              </Suspense>
            ),
          },
        ],
      },
    ]);
    
    export default router;
    • BasicMenu 추가 (헤더 수정)

    • frontend/ch1/src/components/menus/basicMenu.tsx
    import { NavLink } from 'react-router';
    
    function BasicMenu() {
      return (
        <nav id="navbar" className=" flex bg-blue-300">
          <div className="w-4/5 bg-gray-500">
            <ul className="flex p-4 text-white font-bold">
              <li className="pr-6 text-2xl">
                <NavLink to="/">Main</NavLink>
              </li>
              <li className="pr-6 text-2xl">
                <NavLink to="/about">About</NavLink>
              </li>
              <li className="pr-6 text-2xl">
                <NavLink to="/todo/">Todo</NavLink>
              </li>
            </ul>
          </div>
        </nav>
      );
    }
    
    export default BasicMenu;
    • frontend/ch1/src/layouts/basicLayout.tsx
    import { Outlet } from 'react-router';
    import BasicMenu from '../components/menus/basicMenu';
    
    function BasicLayout() {
      return (
        <>
          <BasicMenu />
          <div className="bg-white my-5 w-full flex flex-col space-y-4 md:flex-row md:space-x-4 md:space-y-0">
            <main className="bg-sky-300 md:w-2/3 lg:w-3/4 px-5 py-40">
              <Outlet />
            </main>
            <aside className="bg-green-300 md:w-1/3 lg:w-1/4 px-5 py-40">
              <h1 className="text-2xl md:text-4xl"> Sidebar </h1>
            </aside>
          </div>
        </>
      );
    }
    
    export default BasicLayout;
Designed by Tistory.