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