-
[React 강의] 레이아웃 컴포넌트와 children / OutletJS&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;'JS&React' 카테고리의 다른 글
[React 강의] useParams / useSearchParams / Navigate (0) 2026.02.20 [React 강의] React-Router (0) 2026.02.04 [React 강의] 환경설정 - Node.js / Vite / Tailwind CSS / Spring Boot (0) 2026.01.27 리액트 기본 - State(uesState), 재렌더링과 부작용(useEffect), Export (1) 2024.08.30 리액트 기본 - Props (0) 2024.08.29