Skip to content
This page has been auto-translated and may contain errors.View in English

라우팅

React 앱은 하나의 컴포넌트 트리를 하나의 HTML 문서에 렌더링합니다. 실제 사이트에는 홈페이지, 소개 페이지, 상세 페이지 등이 각각의 URL을 가지고 있습니다. 라우팅은 이 두 가지를 연결하는 방식입니다. URL이 어떤 컴포넌트를 렌더링할지 결정하고, 앱 내에서 클릭하면 URL이 업데이트됩니다. React Router는 대부분의 React 앱이 사용하는 라이브러리이며, 이 강좌에서도 밴 렌탈 앱인 VanLife를 구축하며 전체 섹션을 할애합니다. 이 장에서는 핵심 요소를 다루며, 중첩 라우트라우트 매개변수에서 이를 바탕으로 확장합니다.

클라이언트 사이드 라우팅: MPA vs SPA

전통적인 다중 페이지 애플리케이션(MPA)에서는 매번 네비게이션이 왕복 요청입니다. 브라우저가 서버에 /about을 요청하고, 서버가 HTML 페이지를 조립해 전송하며, 브라우저는 현재 페이지를 버리고 새 페이지를 로드합니다. 이 전체 페이지 바뀜은 클릭할 때마다 눈에 띄는 깜박임으로 보입니다.

단일 페이지 애플리케이션(SPA)은 하나의 문서를 한 번만 로드합니다. 이름이 좀 오해를 살 수 있는데, 사용자 입장에서는 여전히 많은 페이지를 가진 사이트일 수 있습니다. 단일이란 문서가 하나라는 뜻입니다. 첫 로드 이후로는 React 앱 자체가 각 URL에서 무엇을 표시할지 결정합니다. /about으로 이동하면 앱 내에서 컴포넌트만 바뀌며, 새로운 문서 요청도 없고 깜박임도 없습니다. 뷰가 새로운 데이터가 필요하면 앱이 백그라운드에서 JSON을 가져와 제자리에서 업데이트하는데, 이는 데이터 가져오기에서 다루는 패턴입니다.

**클라이언트 사이드 라우팅**은 이를 가능하게 하는 메커니즘입니다. 라이브러리가 URL을 감시하고 네비게이션을 가로채서 브라우저의 리로드 대신 일치하는 컴포넌트를 렌더링합니다. 문서를 살려둔다는 것은 실질적인 이점이 있습니다. 전체 페이지 로드는 모든 React 상태를 삭제하지만, 하나의 문서 안에 머무르면 매번 네비게이션할 때 상태가 유지됩니다.

버전 노트

이 강좌는 React Router 6을 가르칩니다. 버전 7은 React Router와 Remix를 통합했고 패키지를 통합했으므로 react-router가 현재 import 경로입니다. react-router-dom은 v7에서 재내보내기로 남아 있으며, v6과 이 강좌가 사용하는 패키지입니다. 이것이 이 핸드북의 모든 예제가 여기서 import하는 이유이고, v8은 이를 삭제했으므로 새로 설치하면 이 컴포넌트들을 react-router에서 import합니다.

React Router는 선언적, 데이터, 프레임워크 세 가지 모드를 문서화합니다. 선언적은 이 장에서 다루는 BrowserRouterRoute 컴포넌트입니다. 데이터는 createBrowserRouter를 로더 및 액션과 함께 사용하고, 프레임워크는 타입이 지정된 라우트 모듈, 코드 분할 및 서버 렌더링을 추가하는 Vite 플러그인입니다. 이 장의 컴포넌트 및 훅 이름은 그 버전들 전체에서 변경되지 않습니다.

BrowserRouter, Routes, Route

세 개의 컴포넌트가 기본 설정을 정의합니다. BrowserRouter는 앱을 감싸고 라우팅을 활성화하며, Routes는 라우트 정의를 보관하고, 각 Route는 하나의 경로를 하나의 요소에 매핑합니다.

jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom'

export default function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  )
}

내부적으로 BrowserRoutercontext 공급자입니다. 앱을 이것으로 감싸면 아래의 모든 컴포넌트가 라우터의 도구에 접근할 수 있으므로 맨 위에 위치합니다. 일부 코드베이스는 import 시 이름을 바꿉니다 (import { BrowserRouter as Router }). 전체 이름이 길기 때문입니다.

Route는 두 개의 props를 받습니다. path는 도메인 뒤의 URL 부분입니다. 홈페이지는 /, 소개 페이지는 /about입니다. element는 URL이 해당 경로와 일치할 때 렌더링할 JSX이며, 실제 요소로 작성합니다. Home 대신 <Home />입니다. URL이 /about일 때 React Router는 Routes 컴포넌트 위치에 <About />를 렌더링합니다. Routes 바깥의 모든 것은 모든 페이지에서 렌더링됩니다.

매칭되는 라우트가 없는 경로를 방문하면 아무것도 렌더링되지 않습니다. 이 간극은 아래의 캐치올 라우트로 해결됩니다.

페이지 간을 이동하는 HTML 방식은 앵커 태그인데, SPA에서는 정확히 잘못되었습니다. <a href="/about">를 클릭하면 전체 페이지 로드가 트리거되어 실행 중인 앱과 모든 상태를 버립니다. React Router의 답은 Link 컴포넌트입니다.

jsx
import { Link } from 'react-router-dom'

function Header() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
    </nav>
  )
}

Linkhref 대신 to prop을 받으며, 영어처럼 읽힙니다. 다음 라우트로의 링크라는 뜻입니다. 브라우저에서는 여전히 실제 앵커 태그를 렌더링하므로 스크린 리더, 우클릭 메뉴, CSS 요소 선택자 모두 일반 <a>를 봅니다. 차이는 클릭 핸들러에 있습니다. React Router가 클릭을 가로채고 URL을 업데이트하며 렌더링된 컴포넌트를 바꾸는데, 이 모든 것이 문서를 리로드하지 않습니다. 10으로 설정된 카운터는 페이지 간 이동 시 10으로 유지됩니다. React Router 앱 내에서 내부 네비게이션은 항상 Link를 통해야 합니다. 일반 앵커는 외부 URL을 위한 것입니다.

네비게이션 바는 보통 현재 페이지를 강조합니다. NavLink는 정확히 이 목적을 위해 존재합니다. Link처럼 동작하지만, classNamestyle props는 단순한 값 대신 함수를 받을 수 있습니다. React Router는 링크의 라우트가 현재 URL과 일치할 때 true인 isActive 부울 값을 포함하는 객체와 함께 그 함수를 호출합니다. 이것은 render props에서 다루는 render props 패턴을 children 이외의 prop에 적용한 것입니다.

jsx
import { NavLink } from 'react-router-dom'

function Header() {
  return (
    <nav>
      <NavLink
        to="/about"
        className={({ isActive }) => isActive ? 'active-link' : ''}
      >
        About
      </NavLink>
    </nav>
  )
}

함수가 반환하는 것이 클래스 이름이 되므로 active-link 클래스는 /about이 현재 라우트일 때만 적용되고, CSS가 나머지를 처리합니다. style prop은 인라인 스타일 객체로 같은 방식으로 작동합니다.

jsx
const activeStyles = { fontWeight: 'bold', textDecoration: 'underline' }

<NavLink
  to="/about"
  style={({ isActive }) => isActive ? activeStyles : null}
>
  About
</NavLink>

클래스 이름 또는 인라인 스타일은 팀 규약에 따른 선택입니다. 둘 다 완전히 지원되므로 프로젝트에서 이미 사용 중인 방식을 따릅니다.

캐치올 404 라우트

사용자는 존재하지 않는 경로에 도착합니다. 오래된 링크, 오타, 다른 사람의 잘못된 참조입니다. 폴백이 없으면 React Router는 아무것도 렌더링하지 않습니다. 수정 방법은 캐치올 라우트(때로는 splat 라우트라고 함)이며, 경로는 하나의 별표입니다.

jsx
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="*" element={<NotFound />} />
</Routes>

path="*"는 다른 라우트가 주장하지 않는 모든 것과 일치하며, CSS의 범용 선택자처럼 작동합니다. 일반적인 NotFound 컴포넌트는 짧은 메시지와 홈페이지로의 Link를 함께 보여줍니다. 리스트의 순서는 중요하지 않습니다. React Router 6은 모든 라우트를 URL과 비교하고 최고의 일치를 선택하므로 캐치올은 더 구체적인 것이 없을 때만 작동합니다. 마지막에 놓는 것은 규약과 가독성의 문제이지 필수는 아닙니다.

이 모든 것의 메커니즘은 브라우저의 History API입니다. Link는 일반 좌클릭에서 event.preventDefault()를 호출하고 history.pushState로 새 URL을 푸시하며 라우터의 context 공급자가 위치 변경을 구독자에게 알리도록 합니다. 매칭되는 라우트는 다시 렌더링되고 문서는 절대 리로드되지 않습니다. 이것이 상태가 유지되는 이유이기도 합니다. JavaScript 환경이 절대 해체되지 않기 때문입니다.

v6의 라우트 매칭은 순위 기반입니다. 각 경로 세그먼트는 점수를 얻는데, 정적 세그먼트가 동적 세그먼트를 이기고 동적 세그먼트가 splat을 이기므로 정의 순서는 의미가 없습니다. 이는 v5의 첫 번째 일치 우승 모델을 대체했는데, 여기서 맨 위의 보호되지 않은 /는 아래의 모든 것을 가렸습니다.

NavLink는 조상을 활성으로 계산합니다

라우터는 현재 URL의 모든 조상이 일치한다고 간주하므로 /host 링크는 /host/income에 있을 때 isActive를 보고합니다. 보통 네비게이션 바에서 원하는 동작입니다. 그렇지 않을 때는 end prop이 그 링크로 하여금 정확한 경로만 일치하도록 말합니다. 여러 라우트가 의도적으로 동시에 렌더링되는 중첩 라우트에서 가장 중요합니다.

클라이언트 사이드 라우팅에는 배포 주의사항도 있습니다. BrowserRouter/about처럼 깨끗한 URL을 생성하지만 서버는 그 경로를 알지 못합니다. 라우터만 알고 있습니다. 사용자가 새로고침하거나 여기에 직접 링크하면 서버에 직접 접근하므로 호스트는 모든 라우트에 대해 index.html을 제공하고 React Router가 URL에서 인수하도록 구성해야 합니다. 대부분의 정적 호스트는 정확히 이를 위한 한 줄의 재작성 규칙을 가집니다.

서버 설정은 또한 v7의 프레임워크 모드가 그림을 바꾸는 곳입니다. 선언적 모드와 데이터 모드는 빌드와 서버를 여러분에게 맡기는 반면 프레임워크 모드는 서버 렌더링을 처리하는 Vite 플러그인으로 둘 다를 인수합니다. 이 장의 모든 것은 그 경계선의 선언적 쪽에 있습니다.

JunoURL이 컴포넌트를 선택합니다 단일 페이지 앱은 하나의 문서를 로드하고 클릭할 때 보이는 것을 바꾸므로 깜박임이 없고 버려지는 것이 없습니다.

앱을 BrowserRouter로 감싸고, 페이지를 Routes 내에 Route 컴포넌트로 나열하며, 각 path는 자신의 element를 렌더링합니다.

앵커 태그 대신 Link를 사용하면 클릭이 앱을 유지하고, path="*" 라우트를 추가하면 URL을 잘못 입력한 방문자에게 빈 화면 대신 친화적인 페이지를 봅니다.

JunoURL이 컴포넌트를 선택합니다 작업 집합은 작습니다. 맨 위의 BrowserRouter, Route 요소를 보관하는 Routespathelement에 매핑하고, 네비게이션을 위한 Link to, 네비게이션 바가 현재 페이지를 강조해야 할 때의 NavLink입니다.

className 또는 style에 함수를 전달하고 받는 객체에서 isActive를 읽습니다.

path="*" 캐치올로 설정을 완료하여 404 페이지를 만듭니다. 라우트 점수 계산은 리스트에서의 위치가 무관함을 의미합니다.

JunoURL이 컴포넌트를 선택합니다Link는 기본 클릭을 방지하고 history.pushState를 구동하며, BrowserRouter는 위치 변경을 브로드캐스팅하는 context 공급자이므로 상태가 네비게이션을 유지합니다.

매칭은 순서 기반이 아닌 순위 기반이고, 조상은 end를 추가할 때까지 NavLink에 대해 활성으로 계산되며, 깨끗한 URL은 서버가 모든 경로를 앱 셸로 재작성하도록 요구합니다.

여기의 선언적 모드 API는 v7로 바로 이어지며, v7은 로더 및 액션을 위한 데이터 모드와 풀 스택 통합을 위한 프레임워크 모드도 문서화합니다.

다음: 중첩 라우트와 레이아웃. 라우트가 공유 크롬을 얻고 라우트 안의 라우트가 있습니다.