React로 URI 라우팅을 하는 방법을 배웠다.
오늘은 실습으로 나온 코드를 정리하며 배운 정보에 대해 정리해보려고 한다.
1. SPA와 MPA
SPA(Single Page Application)
기존에는 링크 태그(a 태그 등)를 사용해 새로운 페이지 요청 시마다 서버에서 정적 리소스를 전송하고, 전체 페이지를 다시 렌더링하는 방식으로 매 페이지 요청 시마다 리로드가 발생했다. 그렇기 때문에 웹 페이지를 사용할 때 매끄럽지 않다는 인상을 받을 수 있다. 이것이 위에 말했던 MPA(Multi Page Application)이다. 또 페이지 요청 시마다 새로운 페이지를 서버에서 빌드하여 주기때문에 페이지 간 데이터의 재활용도 어려웠다.
SPA는 기본적으로 CSR 기술을 활용해 페이지 진입 시 리로드 없이 URI 라우팅이 가능하다. 여기서 CSR(Client Side Rendering)은 클라이언트가 서버로부터 데이터를 받아 화면을 그리는 기술이다. 브라우저가 렌더링을 담당하여 서버 트래픽을 감소시키고, 사용자에게 더 빠른 인터랙션의 제공이 가능하다. 또, 여러 페이지 간 스타일이나 컴포넌트의 재활용이 가능하다.
장점 | 단점 |
CDN에 웹사이트 콘텐츠 캐싱 가능 | SEO(Search Engine Optimization, 검색 엔진 최적화)에 불리 |
매번 페이지 요청을 하지 않아 네트워크 요청이 줄어듦 | 하나의 자바스크립트 앱에 지속하므로, 메모리 관리나 성능, 데이터 활용 등에 신경을 써야함 |
데이터 요청 등을 캐싱해 재사용하는 등 제약 조건이 줄어듦 | 하나의 거대한 자바스크립트 앱을 전송받아야 하므로 코드가 많아질수록 로드 속도가 느려질 수 있음 |
하나의 네이티브 앱처럼 사용 가능 |
2. react-router-dom
react-router-dom은 React에서 사용자가 입력한 주소를 감지해 해당 주소에 해당하는 페이지(컴포넌트)로 이동시켜주는라우팅 라이브러리 중 가장 많이 쓰이는 라이브러리다.
라이브러리 내에서 자주 사용하는 컴포넌트를 예시와 함께 정리해보겠다.
BrowserRouter
깨끗한 URL을 사용하여 브라우저의 주소 표시줄에 현재 위치를 저장하고 브라우저에 내장된 기록 스택을 사용하여 내비게이팅한다.
깨끗한 URL이 뭔지 아직 잘 감이 잡히지 않지만, 아무튼 공식 문서에는 이렇게 적혀있다.
import * as React from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
const root = createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
{/* 앱의 컴포넌트가 이곳에 들어간다 */}
</BrowserRouter>
);
BrowserRouter 내에서 사용할 수 있는 옵션으로는 다음이 있다.
basename
특정 basename 아래에서 실행되도록 애플리케이션을 구성한다.
function App() {
return (
<BrowserRouter basename="/app"> {/*👈 /가 아닌 /app까지 작성해야한다. */}
<Routes>
<Route path="/" /> {/* 👈 /app/ 이라는 주소에서 렌더링된다.*/}
</Routes>
</BrowserRouter>
);
}
future
선택적으로 사용할 수 있는 Future Flags(미래 플래그) 세트이다.
v7로의 최종 마이그레이션을 용이하게 하기 위해서는 빨리 새로 출시된 미래 플래그를 사용하는 것이 좋다.
v7에서 제공될 기능을 미리 사용할 수 있고, 그 사이에서 피드백을 제공해 버그를 즉시 보고할 수 있다. 다만 변경 사항이 필요한 경우 지금 사용하는 기능을 나중에는 사용하지 못할 수도 있는 것 같다.
function App() {
return (
<BrowserRouter future={{ v7_startTransition: true }}>
<Routes>{/*...*/}</Routes>
</BrowserRouter>
);
}
window
BrowserRouter는 기본적으로 현재 문서의 defaultView를 사용하지만,
예를 들어 <iframe>에서 다른 창의 URL에 대한 변경사항을 추적하는 데 사용될 수도 있습니다.
Switch (➡️ Routes in v6)
경로와 일치하는 첫 번째 자식 <Route> 또는 <Redirect>만을 렌더링한다. 단순히 <Route>를 여러 개 사용하는 것과 다른 점은, <Routes>는 경로를 독점적으로 렌더링한다는 것이다. <Route>를 여러 개 사용하면 일치하는 경로 발견 시 모든 <Route>를 렌더링한다.
interface RoutesProps {
children?: React.ReactNode;
location?: Partial<Location> | string;
}
<Routes location> {/*location은 현재 위치*/}
<Route />
</Routes>;
<Routes>
<Route path="/" element={<Dashboard />}>
<Route
path="messages"
element={<DashboardMessages />}
/>
<Route path="tasks" element={<DashboardTasks />} />
</Route>
<Route path="about" element={<AboutPage />} />
</Routes>
Routes는 Route 컴포넌트만을 자식 컴포넌트로 가진다. (안 그러면 오류가 나더라)
Route
Route는 아마도 React Router 애플리케이션에서 가장 중요한 부분일 것이다. 그들은 URL 세그먼트를 구성 요소로, 데이터 로딩 및 데이터 변형을 결합한다. route nesting을 통해 복잡한 애플리케이션 레이아웃과 데이터 종속성은 단순하고 선언적으로 변한다.
Route는 라우터 생성 함수에 전달되는 객체다.
<Route
element={
<div>
<h1>Layout</h1>
<Outlet /> {/*중첩 라우팅을 가능하도록 한다.*/}
</div>
}
>
<Route path="/" element={<h2>Home</h2>} />
{/*element로 라우팅 시 보여줄 페이지(컴포넌트)를 삽입한다.*/}
<Route path="/about" element={<h2>About</h2>} />
</Route>
caseSensitive
해당 옵션 사용 시 소문자 대문자 구분을 철저히 하여 라우팅한다.
<Route caseSensitive path="/wEll-aCtuA11y" />
loader
라우터가 해당 페이지를 렌더링하기 전 route loader가 호출되고 useLoaderData를 이용해 데이터를 제공한다.
<Route
path="/teams/:teamId"
loader={({ params }) => {
return fetchTeam(params.teamId);
}}
/>;
function Team() {
let team = useLoaderData();
// ...
}
action
route action은 Form 또는 fetcher, submission 등으로부터의 데이터가 경로로 전송될 때 호출된다.
<Route
path="/teams/:teamId"
action={({ request }) => {
const formData = await request.formData();
return updateTeam(formData);
}}
/>
element / Component
React Element/Compoent는 URL과 매칭되는 경로를 찾았을 때 렌더링된다.
<Route path="/for-sale" element={<Properties />} />
<Route path="/for-sale" Component={Properties} />
{/*각 코드는 같은 동작을 하는 것 같다.*/}
errorElement / ErrorBoundary
렌더링 과정에서, loader 또는 action 중 오류 발생 시 이 errorElement가 렌더링된다.
(잘못된 주소 입력 시 Not Found 페이지가 뜨는 느낌인 것 같다.)
<Route
path="/for-sale"
// if this throws an error while rendering
element={<Properties />}
// or this while loading properties
loader={() => loadProperties()}
// or this while creating a property
action={async ({ request }) =>
createProperty(await request.formData())
}
// then this element will render
errorElement={<ErrorBoundary />}
/>
<Route
path="/for-sale"
Component={Properties}
loader={() => loadProperties()}
action={async ({ request }) =>
createProperty(await request.formData())
}
ErrorBoundary={ErrorBoundary}
/>
lazy
애플리케이션 번들을 작게 유지하고 경로의 코드 분할을 지원하기 위해,
각 경로는 경로 정의의 non-route-matching portions (loader, action, Component/element, ErrorBoundary/errorElement 등)을 해결하는 비동기 기능을 제공할 수 있다.
let routes = createRoutesFromElements(
<Route path="/" element={<Layout />}>
<Route path="a" lazy={() => import("./a")} />
<Route path="b" lazy={() => import("./b")} />
</Route>
);
Redirect (➡️ redirect in v6)
loader와 action 내에서 응답을 던지거나 반환하려고 할 때, 다른 경로로 redirect 할 수 있다.
import { redirect } from "react-router-dom";
const loader = async () => {
const user = await getUser();
if (!user) {
return redirect("/login");
}
return null;
};
아마 이전 버전에서는 컴포넌트처럼 사용했었는데, v6에서는 함수처럼 사용한다.
Link
<Link>는 사용자가 다른 페이지를 클릭하거나 탭하여 이동할 수 있도록 하는 요소이다. <Link>는 react-router-dom에서 접근 가능한 <a> 요소를 링크하고 있는 리소스를 가리키는 실제 href로 렌더링한다. 즉, <Link>를 마우스 오른쪽 클릭하는 것은 사용자의 예상대로 작동한다. <Link reloadDocument>를 사용하여 CSR을 건너뛰고, 브라우저가 전환을마치 <a href>였던 것처럼 정상적으로 처리하도록 할 수 있다.
import * as React from "react";
import { Link } from "react-router-dom";
function UsersIndexPage({ users }) {
return (
<div>
<h1>Users</h1>
<ul>
{users.map((user) => (
<li key={user.id}>
<Link to={user.id}>{user.name}</Link>
</li>
))}
</ul>
</div>
);
}
relative
기본적으로 링크는 경로 계층 구조(relative="route")에 상대적이므로, ...는 경로 레벨을 한 단계 상승시킨다. (Route컴포넌트의 부모 컴포넌트 경로로 이동). 때로는 nested되지 않는 경로로 이동하고자 할 때, 상대적 경로 라우팅을 사용할 수 있다.
// Contact와 EditContact는 UI 레이아웃을 공유하지 않는다.
<Route path="/" element={<Layout />}>
<Route path="contacts/:id" element={<Contact />} />
<Route
path="contacts/:id/edit"
element={<EditContact />}
/>
</Route>;
function EditContact() {
// Contact가 EditContact의 부모가 아니기 때문에, 상위 경로로 이동하려면
// route 계층의 상대적 위의 경로로 이동하기보단 path 계층의 상대적 경로로 이동한다.
return (
<Link to=".." relative="path">
Cancel
</Link>
);
}
preventScrollReset
페이지 이동 시 스크롤이 위로 이동하는 것을 막는다.
<Link to="?tab=one" preventScrollReset={true} />
replace
원래는 경로 이동 시 이동하는 경로를 스택에 축적하는 식이었지만, replace를 사용하면 스택에 있던 기존 경로를 이동하려는 경로로 변경한다.
state
경로 이동 시 state(데이터)를 전송할 수 있다. useLocation을 사용해 이후에 데이터에 접근할 수 있다.
<Link to="new-path" state={{ some: "value" }} />
let { state } = useLocation();
#엘리스트랙 #엘리스트랙후기 #리액트네이티브강좌 #온라인코딩부트캠프 #온라인코딩학원 #프론트엔드학원 #개발자국비지원 #개발자부트캠프 #국비지원부트캠프 #프론트엔드국비지원 #React #Styledcomponent #React Router Dom #Redux #Typescript #Javascript