페이지가 실행되는 시작점으로 src/index.js
이 있다.
여기서 root id를 가진 div
엘리먼트를 잡아준다.
그래서 그 엘리먼트 안에서 화면을 꾸밀 수 있게 된다.
이렇게 꾸밀 수 있는 이유가 SPA 방식을 사용하기 때문이다.
원래는 페이지를 만들면 여러 페이지로 웹을 만들었다.
a 페이지를 만들면 a.html, b 페이지를 만들면 b.html 식으로!
위와 같은 방식을 Multi Page Application
이라고 한다.
요즘은 전체 페이지를 하나의 페이지에 담아 동적으로 화면을 바꿔가며 표현한다.
이것을 SPA Single Page Application
이라고 부른다.
HTML5의 history API
를 사용해서 하나의 페이지에서 동적으로 화면을 바꿀 수 있도록 한다.
자바스크립트 영역에서 History API를 이용해서 현재 페이지 내에서 화면 이동이 일어난 것처럼 작동하게 해준다.
History API | 설명 |
---|---|
History.back() | 세션 기록의 바로 뒤 페이지로 이동하기 비동기 메서드로, 브라우저의 뒤로 가기 효과를 가진다. |
History.forward() | 세션 기록의 바로 앞 페이지로 이동하는 비동기 메소드로, 브라우저의 앞으로 가기 효과를 가진다. |
History.go() | 특정한 세션 기록으로 이동하게 해주는 비동기 메소드로, 1을 넣어 호출하면 바로 앞 페이지로, -1을 넣어 호출하면 바로 뒤 페이지로 이동한다. |
History.pushState() | 주어진 데이터를 세션 기록 스택에 넣는다. 직렬화 가능한 모든 JavaScript 객체를 저장하는 것이 가능하다. |
History.replaceState() | 최근 세션 기록 스택의 내용을 주어진 데이터로 교체한다. |
라우팅은 사용자가 웹사이트의 다른 페이지로 이동하는 프로세스로, 라우팅
과 렌더링
은 애플리케이션의 효율성과 속도에 미치는 영향을 추측하는데 도움이 된다.
클라이언트 측 라우팅은 페이지의 javascript
에 의해서 처리된다.
경로가 바뀌는 경우, 서버에 별다른 요청을 보내지 않고 클라이언트의 브라우저단에서만 여러 페이지를 바꾸어 방문하는 기능을 의미한다.
이러한 SPA는 전체 페이지를 새로 고치지 않기 때문에 이용자에게 부드러운 UI와 UX를 제공한다.
클라이언트 사이트 라우팅의 구현에 있어서 가장 중요한 핵심 세 가지는 다음과 같다.
URL
에 맞는 컴포넌트를 렌더링할 수 있어야 한다.history
기능을 사용하여 앞으로 가기, 뒤로 가기 시 이를 감지하고 처리할 수 있어야 한다.// App.jsx
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<MainPage/>}/>
<Route path="/search" element={<SearchPage/>}/>
<Route path="/user-info" element={<UserInfoPage/>}/>
</Routes>
</BrowserRouter>
);
}
react-router
컴포넌트들의 최상단에 위치시키는 컴포넌트로, 자식 컴포넌트인 Routes
컴포넌트에 props
로 history
객체를 전달한다.
마운트 되는 순간에 props
로 전달받은 history
객체의 프로퍼티인 location
객체에 자신의 지역 상태를 저장한다.
props
로 받은 history
객체를 구독하여, 브라우저의 현재 url이 변경될 때마다 자신의 지역 상태에 해당하는 location
객체가 새로운 location
객체로 대체되도록 한다.
즉, url의 정보를 Routes
컴포넌트에서 실시간으로 추적하여 해당 Route
컴포넌트를 반환하는 작업을 한다.
props
로 전달 받은 path
값이 브라우저의 현재 url과 매칭될 때 특정 컴포넌트를 렌더링한다.
Routes
컴포넌트는 RouterContext.Provider
를 렌더링하여 match, location, history 객체를 전달하면 Route
컴포넌트 RouterContext.Consumer
컴포넌트를 렌더링하여 path값과 일치하는 경우, 전달받은 컴포넌트를 렌더링하고, 아니라면 null 값을 나타낸다.
매칭 시, 렌더링하는 컴포넌트에게 RouterContext
객체 값을 그대로 전달한다.
<a>
태그의 기능인 링크 이동을 가진 컴포넌트이다.
단, 페이지의 리로드 없이 네비게이션 기능을 수행한다.
실제 <a>
태그로 렌더링 되지만, preventDefault
를 통해 기본 동작을 막고, RouterContext
의 history
객체를 이용하여 네비게이션 기능을 수행한다.
리액트를 이용해서 SPA를 구현할 때 리액트 라우터를 이용하면 어떤 메뉴를 클릭했을 때, 페이지를 이동하지 않고 업데이트되는데, 이때 url 역시 바뀐다.
브라우저의 이전 페이지, 다음 페이지 버튼 역시 정상적으로 일반적인 웹 페이지를 이동하듯이 적용된다.
이러한 클라이언트 사이드 라우팅의 원리로는 HTML5-History API의 pushState
메소드를 이용해 임의의 히스토리 기록을 만들어서 이동할 수 있다.
이때 만들어진 히스토리 페이지는 서버에서 제공하는 페이지가 아니기 때문에, 실제 페이지를 이동하는 것이 아니라, history API가 만들어낸 페이지로 이동하는 것이다.
그렇기 때문에 http 요청을 보낼 수 있는 URL은 아니다.
예를 들어, 리액트 앱으로 빌드한 페이지를 새 창을 띄워서 들어가보면 get 요청을 보낼 수 없는 URL이라고 뜬다.
렌더링되는 데이터의 양이 적기 때문에, 구성요소 간 라우팅 속도가 빠르다.
나머지 데이터는 DOM에 의해 렌더링되며, 렌더링해야 할 HTML과 CSS가 많아도 DOM은 순식간에 해당 부분을 처리한다.
더 나은 사용자 경험을 위해 애니메이션과 트랜지션은 서로 다른 컴포넌트 간 전환에서 쉽게 구현될 수 있다.
SPA가 작동하는 실제 느낌을 준다.
별도의 페이지가 렌더링되지 않으며, 현재 페이지는 새 화면을 보여주기 위해 새로고침되지 않는다.
초기 로딩 시간이 길 수 있다.
모든 라우터와 컴포넌트, 그리고 HTML이 한 번에 로딩되기 때문에 모든 웹 사이트가 첫 요청에 로딩되어야 한다.
불필요한 데이터를 다운받는 시간이 존재할 수 있다.
외부 라이브러리 사용이 필요하다.
서버 사이드 라우팅과 다르게, 이는 많은 코드 작성과 외부 패키지에 대한 의존성을 높일 수 있다.
SEO 사용이 어려울 수 있다.
라우팅이 클라이언트 측에서 일어나게 되면, 주소가 변경될 때마다 매번 서버에서 페이지를 받아오지 않아도 되기 때문에 서버와 클라이언트 간의 데이터 트래픽이 감소한다는 이점이 있다.
또한, 페이지 전환 시 새로고침을 방지할 수 있기에, 자연스러운 페이지 이동과 사용자 경험을 제공하는 것이 가능해진다.
Client-Side Routing
에는 단점도 존재하는데, 대표적으로 검색엔진 최적화(SEO)에 불리하다는 점이 있다.
라우팅시 콘텐츠 구성이 완료된 HTML을 서버에서 받아오는 것이 아닌, 화면 구성에 필요한 모든 HTML을 클라이언트 측에서 처리하기 때문에 검색엔진에서 페이지 파악이 불리해진다.
이 때문에 검색 결과에서 더 높은 순위를 점하는 것이 어렵다.
보안 관련해서도 주의할 점이 있는데, Client-Side Routing에서 사용자 정보 저장이 기존 서버 기반 세션이 아닌 상대적으로 보안에 취약한 클라이언트 기반의 쿠키에서 이루어지기 때문이다.