
discover: lazy route discovery 동작 정의render (default): 폼 렌더링 시 route 미리 탐색none: 미리 탐색하지 않고 폼 제출 시 route 탐색preventToScrollReset: <ScrollRestoration> 컴포넌트를 사용했을 때 스크롤이 위로 초기화되는 것을 막는다.relative: action의 경로를 route 기준으로 계산할지, URL 경로 기준으로 계산할지 결정한다.route (default): route 계층 구조를 기반으로 계산하여 route("posts/:id")에서 ..로 이동할 경우 /로 이동하게 된다. route의 정의 자체가 posts/:id이기 때문!route("posts/:id")에서 ..로 이동할 경우 /post로 이동하게 된다. URL 세그먼트 하나만 제거하면 되기 때문!reloadDocument: SPA의 동작을 무시하여 clide side 라우팅을 하지 않고 강제로 전체 페이지를 새로고침한다.replace: 브라우저 히스토리 스택에 state를 새로 쌓지 않고 현재 state를 교체한다. 뒤로 가기를 눌렀을 때 이전 폼 페이지로 돌아가지 않는다.state: 히스토리 스텍에 쌓일 state를 정의한다.viewTransition: View Transition을 활성화한다.unstable_defaultShouldRevalidate: 폼 제출 이후 revalidation 동작을 수정한다. 현재 라우트에 shouldRevalidate 함수가 없으면 이 값을 사용하고 있으면 shouldRevalidate 함수에 전달된다.Await 컴포넌트는 Promise 값을 사용하여 렌더링하며 에러도 함께 처리할 수 있다.
Await는 React.Suspense 내부에서 사용되어야 한다.
<React.Suspense fallback={<ReviewsSkeleton />}>
<Await
resolve={reviews}
errorElement={<div>Could not load reviews 😬</div>}
children={(resolvedReviews) => (
<Reviews items={resolvedReviews} />
)}
/>
</React.Suspense>
// 또는
<React.Suspense fallback={<ReviewsSkeleton />}>
<Await
resolve={reviews}
errorElement={<div>Could not load reviews 😬</div>}
>
{(resolvedReviews) => <Reviews items={resolvedReviews} />}
</Await>
</React.Suspense>
props
children: resolve된 값을 받아 렌더링할 컴포넌트. children 내부에서는 useAsyncValue 훅을 사용하여 resolve된 값에 접근할 수 있다.errorElement: Promise가 reject될 경우 children 대신 렌더링할 컴포넌트. errorElement 내부에서는 useAsyncError 훅을 사용하여 error 객체에 접근할 수 있다.resolve: Promise 객체. 일반적으로 loader에서 리턴한 값을 사용한다.React Router의 Form은 HTML의 <form>을 확장한 컴포넌트로, fetch 기반 폼 제출, useNavigation과 연동, 자동 revalidation과 같은 기능을 제공한다.
HTML form API를 사용하므로 JavaScript 로드 전에는 기본 HTML form처럼 동작이 가능하고, JavaScript 로드 후에는 React Router가 더 좋은 사용자 경험이 가능하도록 처리할 수 있게 된다. (이것이 바로 Progressive enhancement!)
Form은 URL 변경이나 브라우저 히스토리에 기록이 남아야 하는 경우에 적합하고, 브라우저 히스토리 스택에 변경이 없게 하려면 <fetcher.form>을 사용해야 한다.
❓ Progressive enhancement란?
가장 낮은 환경에서도 핵심 기능이 작동하게 만들고, 환경이 좋아질수록 기능을 덧붙여 사용자 경험을 향상시키는 전략
import { Form } from "react-router";
function NewEvent() {
return (
<Form action="/events" method="post">
<input name="title" type="text" />
<input name="description" type="text" />
</Form>
);
}
props
action: form 데이터를 제출할 URL. action이 undefined일 경우 가장 가까운 route의 경로를 기본값으로 사용한다.encType: 인코딩 타입 정의fetcherKey: navigate={false} 옵션을 사용할 경우 다른 컴포넌트에서 useFetcher에 키를 전달하여 상태를 공유할 수 있도록 함method: 폼 제출 시 사용할 HTTP 메서드. Progressive enhancement를 사용하고 싶다면 get이랑 post만 사용하는 것이 좋다.navigate: 이 옵션이 false일 경우 navigation을 하지 않고 fetcher를 통해 폼 제출을 처리한다.onSubmit: 폼 제출 시 실행할 콜백함수. onSubmit 내부에서 e.preventDefault()를 사용할 경우 아무것도 동작하지 않는다.discoverpreventToScrollResetrelativereloadDocument:replace.stateviewTransitionunstable_defaultShouldRevalidate❓ Lazy route discovery란?
앱의 모든 경로(Route) 정보를 처음부터 한꺼번에 다 불러오는 대신, 사용자가 이동하는 흐름에 맞춰 필요한 시점에 경로 메타데이터를 점진적으로 불러오는 성능 최적화 기술이다. 현재 화면에 보이는 모든
<Link>나<NavLink>를 미리 감지하고 해당 라우트의 정보를 미리 요청한다.
참고: https://reactrouter.com/explanation/lazy-route-discovery
❓ View Transition이란?
View Transition API는 웹 페이지의 상태가 바뀔 때(예: 다른 페이지로 이동하거나 DOM 요소가 추가/삭제될 때) 부드러운 애니메이션 효과를 아주 쉽게 구현할 수 있도록 도와주는 최신 브라우저 API이다.
참고: https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API
점진적 향상(Progressive enhancement)된 <a href> 태그 wrapper이다.
import { Link } from "react-router";
<Link to="/dashboard">Dashboard</Link>;
<Link
to={{
pathname: "/some/path",
search: "?query=string",
hash: "#hash",
}}
/>;
props
to: 이동할 경로. string 타입이거나 Partial<Path> 타입prefetch: 이동할 라우트의 데이터와 모듈을 prefetch할 지 동작 정의none (default): 아무것도 하지 않음intent: hover나 focus 시 prefetchrender: <Link> 컴포넌트 렌더링 시 prefetchviewport <Link> 컴포넌트가 화면에 보이면 prefetchdiscoverpreventToScrollResetrelativereloadDocumentreplacestateviewTransitionunstable_defaultShouldRevalidateroute module에서 export한 link 배열을 사용하여 <link> 태그들을 렌더링하는 컴포넌트
import { Links } from "react-router";
export default function Root() {
return (
<html>
<head>
<Links />
</head>
<body></body>
</html>
);
}
props
nonce: <link>에 전달할 nonce 속성crossOrigin: <link>에 전달할 crossOrigin 속성route module에서 export한 meta 배열을 사용하여 <meta> 태그들을 렌더링하는 컴포넌트
import { Meta } from "react-router";
export default function Root() {
return (
<html>
<head>
<Meta />
</head>
</html>
);
}
active 상태와 pending 상태 처리를 추가한 <Link> 컴포넌트 wrapper로, 아래와 같은 기능을 제공한다.
aria-current=”page” 속성 추가className, style, children에 상태와 관련된 render props 지원<NavLink to="/message">Messages</NavLink>
<NavLink
to="/messages"
className={({ isActive, isPending }) =>
isPending ? "pending" : isActive ? "active" : ""
}
>
Messages
</NavLink>
props
caseSensitive: 대소문자 구분 여부children: NavLink 내부에서 상태에 따라 렌더링할 컴포넌트className: 상태에 따라 적용될 class 커스텀style: 상태에 따라 적용될 스타일end: 기본적으로는 URL path가 부분 매칭되어도 active 상태지만, end를 사용하면 정확히 끝까지 매칭해야 active 상태가 됨to: 이동할 경로. string 타입이거나 Partial<Path> 타입prefetch: 이동할 라우트의 데이터와 모듈을 prefetch할 지 동작 정의none (default): 아무것도 하지 않음intent: hover나 focus 시 prefetchrender: <Link> 컴포넌트 렌더링 시 prefetchviewport <Link> 컴포넌트가 화면에 보이면 prefetchdiscoverpreventToScrollResetrelativereloadDocumentreplacestateviewTransitionhook을 사용할 수 없는 React의 클래스형 컴포넌트에서 useNavigate 대신 사용할 컴포넌트
<Navigate to="/tasks" />
props
to: 이동할 경로. string 타입이거나 Partial<Path> 타입relativereplacestate부모 라우트 안에서 자식 라우트를 렌더링하는 컴포넌트. 자식 라우트가 없다면 아무것도 렌더링하지 않는다.
import { Outlet } from "react-router";
export default function SomeParent() {
return (
<div>
<h1>Parent Content</h1>
<Outlet />
</div>
);
}
props
context: 부모에서 자식으로 전달하는 context 데이터. 자식 내부에서는 useOutletContext 훅을 사용하여 접근할 수 있다.특정 페이지의 모듈과 데이터를 미리 가져와서 바로 이동할 수 있게 하기 위해 <linke rel=prefetch|modulepreload> 태그를 렌더링하는 컴포넌트. prefetch 옵션을 사용하는 <Link>가 내부적으로 이 컴포넌트를 사용하지만, 링크가 없어도 특정 조건에서 페이지를 미리 로드하고 싶을 때 사용할 수 있다.
import { PrefetchPageLinks } from "react-router";
<PrefetchPageLinks page="/absolute/path" />
props
page: prefetch할 대상 페이지의 절대경로linkProps: 생성되는 <link> 태그에 추가할 속성현재 경로와 path가 매치되면 렌더링할 컴포넌트를 설정하는 컴포넌트로, Routes 컴포넌트 내부에서만 사용할 수 있다.
// Usually used in a declarative router
function App() {
return (
<BrowserRouter>
<Routes>
<Route index element={<StepOne />} />
<Route path="step-2" element={<StepTwo />} />
<Route path="step-3" element={<StepThree />} />
</Routes>
</BrowserRouter>
);
}
// But can be used with a data router as well if you prefer the JSX notation
const routes = createRoutesFromElements(
<>
<Route index loader={step1Loader} Component={StepOne} />
<Route path="step-2" loader={step2Loader} Component={StepTwo} />
<Route path="step-3" loader={step3Loader} Component={StepThree} />
</>
);
const router = createBrowserRouter(routes);
function App() {
return <RouterProvider router={router} />;
}
props
actioncaseSensitiveComponent: 경로가 매칭될경우 렌더링할 React Component. element와 상호 배타적children: 자식 Route 컴포넌트element: 경로가 매칭될 경우 렌더링할 React Element. Component 와 상호 배타적ErrorBoundary: 에러가 발생할 경우 렌더링할 React Component. errorElement와 상호 배타적errorElement: 에러가 발생할 경우 렌더링할 React Element. ErrorBoundary와 상호 배타적handle: 라우트에 임의의 데이터를 전달할 수 있다. 라우트 내부에서 useMatches 훅으로 접근 가능하다.HydrateFallback: 로딩 중 렌더링할 React Component. hydrateFallbackElement와 상호 배타적hydreateFallbackElement: 로딩 중 렌더링할 React Element. HydrateFallback 과 상호 배타적id: DataRouter에서 사용하는 유니크한 라우트 식별값index: 부모 경로의 기본 자식이 된다. path와 함께 사용하지 않는다.lazy: route object를 반환하는 함수로, code-splitting을 위해 사용한다. Component나 element와 함께 사용하지 않는다.loaderpath: 매칭할 경로 패턴. 생략하면 layout route가 된다.shouldRevalidate: 해당 라우트에 적용할 shouldRevalidate 함수❓ Element 방식과 Component 방식의 차이는?
Element 방식은 이미 생성된 React Element를 넘기는 것이고, Component 방식은 React Component 함수 자체를 넘기는 것이다. 따라서 Element 방식은
<Route element={<Home />} />로 사용하고, App 렌더링 시점에 이미<Home />이 생성되어 있다. 반면에 Component 방식은<Route Component={Home} />로 사용하고, 라우트가 실제로 매칭될 때 생성된다.
❓ 상호 배타적(Mutually Exclusive)이 무슨 뜻이지?
서로 동시에 존재할 수 없는 관계라는 뜻이다. 즉, 하나를 선택하면 다른 하나는 사용할 수 없다.
현재 location에 가장 잘 맞는 <Route>를 찾아서 하나의 branch를 렌더링한다.
import { Route, Routes } from "react-router";
<Routes>
<Route index element={<StepOne />} />
<Route path="step-2" element={<StepTwo />} />
<Route path="step-3" element={<StepThree />} />
</Routes>
props
children: 중첩된 Route들location: 매칭할 location. 기본값은 window.location 기반의 현재 URL이다.❓ branch란?
중첩 라우트일 때 하나의 트리 경로를 말한다. 렌더링 시
Outlet을 통해 단계적으로 렌더링된다.
client runtime을 렌더링하는 컴포넌트. <body> 태그 안에 위치해야 한다.
서버 렌더링 중에는 <Scripts /> 컴포넌트가 생략되어 자바스크립트 없이 HTML과 브라우저 동작에만 의존하여 동작하게 된다.
즉, <Scripts /> 는 서버에서 HTML을 먼저 렌더링한 후 브라우저에서 React가 hydrate 되도록 JS를 로드해주는 역할을 한다. <Scripts /> 가 있어야 loader 데이터 hydration, client navigation, fetcher, action 처리 등이 가능해진다.
import { Scripts } from "react-router";
export default function Root() {
return (
<html>
<head />
<body>
<Scripts />
</body>
</html>
);
}
props
<script> 태그에 넣을 속성들❓ client runtime 이란?
클라이언트에서 실행될 자바스크립트 번들로, Hydration을 통해 브라우저에서 리액트의 로직과 라우팅 기능이 실제로 작동할 수 있게 해준다.
브라우저의 스크롤 복원 동작을 흉내내서 구현하는 컴포넌트이다. scroll flashing을 방지하는 inline <script> 태그를 렌더링한다.
앱에서 딱 한 번만 렌더링 해야 하며, <Scripts /> 컴포넌트 바로 위에 위치해야 한다.
inline script를 생성하기 때문에 CSP가 strict하면 차단될 수 있는데, 그럴 경우 nonce={cspNonce} prop을 사용한다.
import { ScrollRestoration } from "react-router";
export default function Root() {
return (
<html>
<body>
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
props
getKey: 스크롤을 저장할 때 사용할 키. 기본값은 location.key이며, 커스텀한 스크롤 복구 로직을 구현할 때 사용하면 좋다.location.key: 히스토리 단위로 복구location.pathname: 동일한 path에 접근할 경우 항상 이전 스크롤 위치 복구nonce: <script> 태그에 전달될 nonce 속성storageKey: session storage에 스크롤 위치를 저장할 때 사용하는 키. 기본값은 "react-router-scroll-positions”이다.❓ scroll flashing이란?
화면이 잠깐 잘못된 스크롤 위치를 보여줬다가 다시 올바른 위치로 이동하는 현상
❓ nonce란?
한 번만 사용하는 임의의 보안 토큰 (number used once)으로, 브라우저의 CSP 환경에서 특정
<script>나<style>태그만 실행을 허용하기 위해 사용한다.
❓ CSP(Content Security Policy)란?
웹 보안을 위한 브라우저 보안 정책 시스템으로, 어디에서 온 리소스만 실행해도 되는지를 명확하게 지시한다. 보통 XSS 공격 방지, 악성 스크립트 실행 차단, 외부 리소스 오용 방지를 위해 사용한다. 활성화하기 위해서는 HTTP 응답 헤더로
Content-Security-Policy를 설정하면 된다.