Frameworks & Libraries/React

Outlet 컴포넌트

sod0l 2024. 9. 12. 10:45
// About.js
function About() {
	return (
	<div>
		<header>공통 헤더</header>
		<nav>네비게이션 바</nav>
		<main>소개 페이지 내용</main>
		<footer>공통 푸터</footer>
	</div>
	);
}

1. Outlet 이란?

  • `Outlet` 은 주로 React 라우팅 라이브러리인 `react-router-dom` 에서 사용하는 특수한 컴포넌트이다.
  • 이 컴포넌트는 여러 페이지를 구성할 때, 부모-자식 관계를 통해 페이지의 일부를 동적으로 바꾸고 싶을 때 사용된다.
  • 쉽게 말해, 특정 위치에 다른 컴포넌트를 삽입할 수 있는 “구멍”이나 “창구”라고 생각할 수 있다.

 

 

2. 왜 Outlet 이 필요한 이유?

  • 웹사이트를 만들 때, 여러 페이지가 있지만 그 페이지들 사이에 공통적으로 유지되어야 하는 부분이 있을 수 있다.
  • 예를 들어, 모든 페이지에 동일한 헤더와 푸터가 있고, 중간의 콘텐츠만 달라지는 경우가 많다. 이런 경우에 공통 부분(헤더, 푸터 등)은 유지하고, `Outlet` 을 사용해 중간의 내용만 상황에 맞게 바꿀 수 있다.

 

 

3. 더 자세한 예시

1) Outlet 없이 페이지를 만든다면

  • 각 페이지마다 헤더, 네비게이션 바, 푸터를 각각 코딩해야 한다고 상상해보자. 다음과 같이 각 페이지마다 반복적으로 동일한 코드를 작성하게 된다.
// Home.js
function Home() {
	return(
	<div>
		<header>공통 헤더</header>
		<nav>네비게이션 바</nav>
		<main>홈 페이지 내용</mail>
		<footer>공통 푸터</footer>
	</div>
 );
}
// About.js
function About() {
	return (
	<div>
		<header>공통 헤더</header>
		<nav>네비게이션 바</nav>
		<main>소개 페이지 내용</main>
		<footer>공통 푸터</footer>
	</div>
	);
}
  • 이런 식으로 각 페이지에 공통적인 요소를 반복해서 작성하면 코드가 불필요하게 길어지고, 수정할 때도 모든 페이지를 수정해야 한다.

 

2) Outlet 을 사용하면

  • `Outlet` 을 사용하면 공통 부분은 한 번만 정의하고, 각 페이지의 고유한 내용만 바꿀 수 있다.
  • 이제 `Layout` 이라는 공통 레이아웃을 정의해보면
// Layout.js
import { Outlet } from 'react-router-dom';

function Layout() {
	<div>
		<header>공통 헤더</header>
		<nav>네비게이션 바</nav>
		<main>
			<Outlet />  {/* 여기에 각 페이지의 내용이 삽입된다. */}
		</main>
		<footer>공통 푸터</footer>
	</div>
}

export default Layout;
// App.js
import { BrowserRouter as Router, Routes, Route } from 'react-router'dom';
import Layout from './Layout';
import Home from './Home';
import About from './About';

function App() {
	return (
		<Routers>
			<Route path="/" element={<Layout />}>  {/* outlet 선언 시작 부분 */}
				{ /* 컴포넌트의 부모-자식 관계를 시각적으로 명확하게 표현하기 위해서 들여쓰기함 */}
				<Route index element={<Home />} />    
				<Route path="about" element={<About />} />
			</Route>  {/* outlet 선언 끝 부분 */}
		</Routers>
	);
}

export default App;
  • 이제 `Home` 과 `About` 컴포넌트는 각각의 내용만을 정의하면 된다.
// Home.js
function Home() {
	return <div>홈 페이지 내용</div>;
}

export default Home;
// About.sj
function About() {
	return <div>소개 페이지 내용</div>;
}

export default About;

 

Q. 어떻게 동작하는가?

  1. 라우트 설정: `App.js` 에서 `Route path=”/” element={<Layout />}` 로 기본 레이아웃을 지정한다.
  2. `Outle`t 사용: `Layout.js` 에서 `Outlet` 컴포넌트를 통해 자식 컴포넌트가 렌더링될 위치를 지정한다.
  3. 동적 콘텐츠 교체: URL 경로가 바뀔 때마다 `Outlet` 에 다른 컴포넌트가 렌더링된다. 예를 들어, `/` 경로로 접근하면 `Home` 컴포넌트가 `Outlet` 에 표시되고, `/about` 경로로 접근하면 `About` 컴포넌트가 `Outlet` 에 표시된다.

 

 

4. Outlet 의 역할과 동작 방식

  • `Outlet` 의 선언 위치: `Outlet` 은 보통 레이아웃 컴포넌트 내에서 선언된다. 자식 라우트가 렌더링될 “자리”를 지정하는 것.
  • 라우트의 중첩: `react-router-dom` 에서는 라우트를 중첩시킬 수 있다. 즉, 하나의 라우트가 다른 라우트의 자식이 될 수 있다는 뜻이다.

 

4-1) 왜 하위 라우트가 Layout 의 Outlet 안에 들어가는가?

  • `react-router-dom`의 동작 방식을 살펴보면
<Router>
	<Routes>
		<Route path="/" element={<Layout />}>
			<Route index element={<Home />} />
			<Route path="about" element={<About />} />
		</Route>
	</Routes>
</Router>
  • 위 코드에서 중요한 부분은 `<Route path=”/” element={<Layout />}>` 이다. 이 라우트는 `Layout` 컴포넌트를 렌더링한다. `Layout` 컴포넌트는 다음과 같이 생겼다.
// Layout.js
import { Outlet } from 'react-router-dom';

function Layout() {
	return (
		<div>
			<header>공통 헤더</header>
			<nav>네비게이션 바</nav>
			<main>
				<Outlet /> {/* 여기서 자식 라우트가 렌더링된다. */}
			</main>
			<footer>공통 푸터</footer>
		</div>
	)
}

export default Layout;

 

동작 방식

  1. 기본 구조: `<Layout / >` 컴포넌트가 렌더링되면서 `<header>`, `<nav>` , `<main>` , `<footer>` 가 화면에 표시된다.
  2. `Outlet`의 역할: <Outer />은 자식 라우트가 렌더링될 위치를 지정한다. 즉, Outlet 이 있는 곳이 자식 컴포넌트가 표시되는 자리입니다.
  3. 자식 라우트의 렌더링:
    • 첫 번째 `<Route index element={<Home />} /> `는 기본 경로 `/ `에 대한 자식 라우트이다. `index` 속성은 부모 경로와 동일한 경로를 의미한다. 따라서 / 경로로 접근할 떄 `<Home />` 컴포넌트가 `Outle` 자리에 렌더링 된다.
    • 두 번째 `<Route path=”about” element={<About />} />` 는 `/about` 경로에 대한 자식 라우트이다. `/about` 경로로 접근할 때 `<About />` 컴포넌트가 `Outlet` 에 렌더링되도록 설정되어 있기 때문이다.
  • 이렇게 함으로써 하나의 레이아웃에서 다양한 콘텐츠를 표시할 수 있고, 웹 애플리케이션에서의 유지보수성과 재사용성을 높일 수 있다.