본문 바로가기
Web/React, Redux

[React] React routing

by 오우영 2021. 7. 14.
라우팅
사용자가 웹사이트의 다른 페이지로 이동하는 프로세스
렌더링
해당 페이지를 UI에 배치하는 프로세스
특정 페이지에 대한 경로를 요청할 때마다 해당 페이지도 렌더링되지만 모든 렌더링이 경로의 결과인 것은 아닙니다

 

클라이언트 사이드 라우팅


클라이언트 사이드 라우팅이란 서버에게 별다른 요청을 보내지 않고 클라이언트의 브라우저 단에서만 여러 페이지들을 왔다 갔다 방문할 수 있는 기능을 말한다

 

클라이언트 라우팅의 구현에 중요한 핵심 3가지
1. 현재 URL에 맞는 UI(즉, 컴포넌트)를 렌더링 할 수 있어야 한다 
2. 페이지의 리로드 없이 다른 페이지를 방문할 수 있는 네비게이션 기능이 있어야 한다
3. 사용자의 액션에 의해 URL이 변경될 때 이를 감지하고 처리할 수 있어야 한다 

 

React 웹 애플리케이션을 개발할 때 클라이언트 사이드 라우팅을 위해 많이 사용하는 패키지들의 동작 원리에 대해 간단히 알아 봅니다. 대표적으로 가장 많이 사용하는 패키지는 react-router-dom이며, 이는 react-router 패키지에 의존하는 패키지로써 브라우저에서 클라이언트 사이드 라우팅을 수행할 수 있도록 해줍니다

 

React는 리로딩을 하지 않고 변화가 필요한 부분만 렌더링을 하여 기존 페이지 방식보다 빠릅니다. 해당 방식을 하기 위해서 페이지 이동에 react-router패키지의 <Router>을 사용한다

 

<BrowserRouter>, <Router> 컴포넌트


react-router-dom 패키지는 history 패키지를 활용하여 클라이언트 사이드 라우팅을 구현한 것이고, history 패키지는 HTML5 history API를 이용하여 세션 히스토리 관리 및 내비게이션 기능을 구현한 것이다

 

브라우저와 리액트앱의 라우터를 연결하게 되면 그 결과 라우터가 history api에 접근할 수 있게 되며 각각의 Route와 연결된 컴포넌트에 props로 match, location, history라는 객체를 전달하게 된다

 

1. <BrowserRouter>

  • React 웹 애플리케이션 개발 시 클라이언트 사이드 라우팅을 위해 라우팅 관련 컴포넌트들의 최상단에 위치시켜야 하는 컴포넌트
  • <Router> 컴포넌트를 렌더링 할 때 props로 history 객체를 전달
  • 이 객체는 history 패키지의 createBrowserHistory() 함수를 호출함으로써 생성
  • 해당 history 객체는 HTML5 history API 기반으로 브라우저에서 쉽게 내비게이션 기능을 구현할 수 있도록 각종 API를 제공하는 역할을 수행

history 패키지가 제공하는 API 사용 예시 (출처 : 공식 다큐먼트)

// Create your own history instance.
import { createBrowserHistory } from 'history';
let history = createBrowserHistory();

...

// Get the current location.
let location = history.location;

// Listen for changes to the current location.
let unlisten = history.listen(({ location, action }) => {
  console.log(action, location.pathname, location.state);
});

// Use push to push a new entry onto the history stack.
history.push('/home', { some: 'state' });

// Use replace to replace the current entry in the stack.
history.replace('/logged-in');

// Use back/forward to navigate one entry back or forward.
history.back();

// To stop listening, call the function returned from listen().
unlisten();

 

이렇게 생성된 history 객체를 react-router 패키지의 <Router> 컴포넌트에 history props로 넘겨서 래핑 한 컴포넌트가 바로 react-router-dom 패키지의 <BrowserRouter> 컴포넌트이다

 

react-router 패키지의 <Router> 컴포넌트는 여러 환경에서의 라우터 컴포넌트를 구현하기 위한 저수준의 인터페이스(토대)일 뿐, 실제로 사용할 때는 이것을 각 환경에서 사용할 수 있는 형태로 구현한 고수준의 라우터 컴포넌트(<BrowserRouter>, <HashRouter>, <MemoryRouter>, <NativeRouter>, 혹은 <StaticRouter>)를 사용한다.

 

 

2. <Router>

  • 마운트 되는 순간에 props로 전달받은 history 객체의 프로퍼티인 location 객체를 자신의 지역 상태에 저장
  • props로 전달받은 history 객체를 구독하여(history.listen 메소드) 브라우저의 현재 URL이 변경될 때마다 자신의 지역 상태에 해당하는 location 객체가 새로운 location 객체로 대체되도록 한다
  • 브라우저의 현재 URL에 관한 정보를 <Router> 컴포넌트가 지역 상태로서 실시간으로 추적하겠다는 의미

 

<Router> 렌더링 로직
<Router> 컴포넌트는 현재의 URL과 관련된 몇몇 정보들을 Context로 구성해서 해당 Context의 Provider 컴포넌트<RouterContext.Provider>를 렌더링 한다. 이는 트리의 깊은 곳에 위치하는 각종 라우팅 관련 컴포넌트(<Switch>,  <Route> 등)들이 어디서든 브라우저의 현재 URL과 관련된 정보들을 참조할 수 있도록 하기 위함이다. 이때 Context로 구성되는 정보로는 match 객체, location 객체, history 객체가 있다
History 객체
history 객체는 브라우저의 history와 유사하다. 스택(stack)에 현재까지 이동한 url 경로들이 담겨있는 형태로 주소를 임의로 변경하거나 되돌아갈 수 있도록 해준다

· length : [number] 전체 history 스택의 길이
· action : [string] 최근에 수행된 action (PUSH, REPLACE or POP)
· location : [JSON object] 최근 경로 정보
· push(path, [state]) : [function] 새로운 경로를 history 스택으로 푸시하여 페이지를 이동
· replace(path, [state]) : [function] 최근 경로를 history 스택에서 교체하여 페이지를 이동
· go(n) : [function] : history 스택의 포인터를 n번째로 이동
· goBack() : [function] 이전 페이지로 이동
· goForward() : [function] 앞 페이지로 이동
· block(prompt) : [function] history 스택의 PUSH/POP 동작을 제어
· listen(listener) : [function] history 객체를 실시간으로 감지
Location ❓
location 객체에는 현재 페이지의 정보를 가지고 있다

· pathname : [string] 현재 페이지의 경로명
· search : [string] 현재 페이지의 query string
· hash : [string] 현재 페이지의 hash

Match
match 객체에는 <Route path>와 URL이 매칭된 값에 대한 정보가 담겨져있다

· path : [string] 라우터에 정의된 path
· url : [string] 실제 클라이언트로부터 요청된 url path
· isExact : [boolean] true일 경우 전체 경로가 완전히 매칭될 경우에만 요청을 수행
이러한 비교 작업은 해당 <Router> 컴포넌트가 렌더링 될 때마다 이뤄지므로 match는 매번 새로운 참조값으로 생성된다
Context.Provider
Provider 의 Value는 하위의 모든 Consumer 에서 사용할 수 있으며, Provider 하위의 모든 Consumer 는 Provider 의 value가 변경 될 때마다 리렌더링 된다

 

<Route> 컴포넌트


<Route>는 props로 전달받는 path의 값이 브라우저의 현재 URL과 매칭 될 때 특정 컴포넌트를 렌더링 하는 컴포넌트이다. 이는 <RouterContext.Consumer> 컴포넌트를 렌더링함으로써 RouterContext를 참조한다. 이때 RouterContext의 location 객체 정보와 props로 전달받은 path 값을 비교한다. 만약 매칭이 된다면 component props로 전달받은 컴포넌트를 렌더링 하고, 아니라면 null을 렌더링 한다. 매칭 되는 경우 props로는 RouterContext와 동일한 구성의 값들(match 객체, location 객체, history 객체)을 넘겨준다. 다만 이때 넘겨주는 match 객체는 RouterContext에 저장된 것이 아닌, 방금 매칭 작업을 수행하여 만들어낸 match 객체이다.

 

<Link> 컴포넌트


<Link>는 페이지의 깜빡임(리로드) 없이 내비게이션을 수행하기 위한 컴포넌트이다. <a> 태그로 렌더링 되지만, 일반적인 <a> 태그와는 조금 다르게 동작하도록 구현이 된다. 먼저, preventDefault() 함수를 호출하여 <a> 태그의 기본 동작을 막는다. <a> 태그의 기본 동작은 href 어트리뷰트로 설정된 경로의 페이지를 리로드 하는 것이다. 이는 SPA에서 원하는 클라이언트 사이드 라우팅의 동작과 맞지 않는다. 다음으로, <a> 태그의 기본 동작을 막은 대신 클릭 시에 RouterContext에 존재하는 history 객체를 이용하여 내비게이션을 수행하도록 구현한다. 이를 위해서는 <Link> 컴포넌트도 <RouterContext.Consumer> 컴포넌트를 렌더링 하여 RouterContext를 참조해야 한다. 기본적으로는 클릭 시에 history.push()와 같은 함수를 이용해서 현재 URL을 바꾸도록 구현이 될 것이다

 

React router 동작 원리


1
브라우저를 켜서 처음 서버에 접속하면 <Router> 컴포넌트의 지역 상태가 history.location 객체로 초기화된다
2
유저가 <Link> 컴포넌트에 의해 랜더링 된 <a> 태그를 클릭하거나 브라우저의 특정 액션(histoy.back, history.forward)을 수행함으로써 현재 URL을 바꿀 수 있다
-> 페이지의 리로드 없는 내비게이션
3
그러면 앞서 history 객체를 이용하여 설정해둔 구독 메커니즘에 의해 <Router> 컴포넌트의 지역 상태인 location 객체가 새로운 것으로 변경된다
-> 현재 URL 관련 정보를 <Router> 컴포넌트의 지역 상태로 관리 (현재 URL 구독)
4
이로 인해 <Router> 컴포넌트가 리렌더링 되고, 그 결과 RouterContext의 값이 새로 구성되면서, 트리의 하위에 존재하는 각종 라우팅 관련 컴포넌트들이 리렌더링 된다
-> 현재 URL에 맞는 UI 렌더링

· <Route> 컴포넌트는 현재 URL과 path props 값을 다시 매칭 해서 match 객체, location 객체, history 객체를 렌더링 할 컴포넌트에게 넘겨준다. 이때 match 객체와 location 객체는 history 객체와 달리 참조값이 다른 새로운 객체이다

 

'Web > React, Redux' 카테고리의 다른 글

[Redux] 사용했을때 장점  (0) 2021.06.21

댓글