리액트 라우터는 어떻게 없는 페이지를 만들까?

Plato·2022년 8월 21일
0

서론

단일 페이지 웹 어플리케이션은, 네이티브 어플리케이션과 유사한 경험을 제공할 수 있다. 하지만 하나의 페이지로 구성하면, 원하는 페이지를 즐겨찾기에 등록할 수 없고 이전 화면으로 되돌아가기 힘들며, 검색 엔진이 페이지 단위로 인덱싱할 수 없다. 이를 극복하기 위해, react-router를 사용하여 여러 페이지가 존재하는 것처럼 구현할 수 있다. 이것이 어떻게 가능한지 알아보자. (리액트-라우터를 사용한 경험이 있는 독자를 대상으로 한 글임에 주의하자.)

단어설명

SPA(Single Page Application), 단일 페이지 어플리케이션

웹 개발에서 SPA는, '서버가 클라이언트에 하나의 html 파일을 보내는 웹 어플리케이션'을 일컫는 말이다. 상황에 맞는 웹 페이지를 클라이언트에 전송하는 대신, 단일 페이지의 UI를 클라이언트가 자바스크립트로 변경하여, 상황에 맞는 페이지를 구성한다.
단일 페이지로 구성되지만, react-router와 같은 써드 파티 라이브러리를 사용하여, 마치 여러 페이지로 구성된 것 처럼 구현할 수 있다.

URI(Universal Resource Identifier)

직역하면, '통합 자원 식별자'다.
'식별자'는, 특정 대상을 구별하는 방법을 말한다. 예시로, 대한민국의 국민에게는 고유한 주민등록번호가 부여되기 때문에, 특정 개인의 주민등록번호를 알면, 해당 개인이 누군지 명확하게 구별할 수 있다. 그렇기에 주민등록번호가 '대한민국의 국민을 구별하는 방법', 즉 식별자다.

URI는 식별자기 때문에, 특정 대상을 구별하는 방법이다. 그러면 어떤 대상을 구별하는 걸까? 이름에서 추론할 수 있듯이, '자원'을 구별하는 방법이다. 예시로, 이 글의 URI는 https://velog.io/@hochul/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%97%86%EB%8A%94-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%A5%BC-%EB%A7%8C%EB%93%A4%EA%B9%8C 이다. 그렇기에 이 주소를 복사해서 새 탭의 주소창에 붙여 넣어 이동하면, 이 글로 이동한다. 이게 가능한 이유는, 서버에 이 URI로 요청하면, 클라이언트가 원하는 자원이, 이 글임을 서버가 구별할 수 있기 때문이다.

라우팅(routing)

routing은 route에 e를 제거하고 ing를 붙인 것이다.
Cambridge Dictionary에 의하면, route(루트)는 "to send something somewhere using a particular way or direction". 즉, '특정 경로를 사용하여, 대상을 목적지로 보내는 것'을 의미한다.
SPA에서 라우팅은, 자원을 클라이언트에 보내기 위해, 'URI에 대응하는 자원을 결정하는 과정'이다.

라우터(router)

라우팅을 수행하는 하드웨어/소프트웨어를 일컫는 말이다.

클라이언트-사이드 라우팅(Client-Side Routing)

URI에 대응하는 리소스를 서버에 요청하지 않아도, 마치 서버에 해당 리소스를 요청하여 받아온 것처럼 작동하는 기술.
이는, 마치 여러 페이지가 존재하는 것처럼, SPA가 동작하게 돕는다. 자세한 내용은 이 글의 본론에 담았다.

서버-사이드 라우팅(Server-Side Routing)

URI가 어느 리소스/동작에 대응하는지 판단하는 기술.
서버-사이드 라우팅을 사용하면, 서버에 URI로 요청을 보냈을 때, 서버가 해당 URI에 대응하는 자원이 무엇인지 판단하여, 해당 자원을 전송한다.

리액트-라우터(react-router)

react-router의 공식 문서에 의하면, react-router는 "리액트를 위한 클라이언트/서버사이드 라우팅 라이브러리"라 한다.

필자도, 이 라이브러리가 어떻게 서버사이드 라우팅을 수행하는지 잘 모르겠고 검색해도 찾아볼 수 없었다. 이 글에서는, 클라이언트-사이드 라우팅에 초점을 맞추고 작성했다. 리액트-라우터가 어떻게 서버-사이드 라우팅을 돕는지는 이 글의 내용이 아니니 걱정하지 말자.

window.location

읽기 전용 자바스크립트 객체로, 현재 문서의 위치를 담고있다. 예시로, window.location.href에는, 현재 주소가 담기게 된다.

history api

크롬 브라우저의 좌측 상단을 보면, 화살표 버튼을 볼 수 있다. 이를 꾹 누르면, 해당 탭으로 방문했던 페이지 리스트가 나오는데, 이를 history stack이라 부른다. history stack에 history api를 통해 접근하고 부분적으로 제어할 수 있다.
history.back()함수를 사용해서, 이전 페이지로 되돌아갈 수 있고, history.forward()함수를 사용해서, 다음 페이지로 갈 수 있다. 또한, history.pushState()함수를 사용해서, history stack에 새 페이지를 추가할 수도 있다. 이를 사용하면, 웹 브라우저의 주소창의 주소가 바뀌고, window.location.href의 값도 새 페이지의 주소로 바뀌게 된다. 이때, 서버에 해당 페이지 URI로 요청을 보내지는 않는다.
자세한 내용은, MDN의 Window.location 문서를 참고하자.

본론

SPA가 갖는 문제

사용자가 웹페이지를 요청했을 때, 존재하지 않는 웹페이지를 서버에서 전달해줄 수 없기 때문에, 문제가 생길 수 있다. 자세히 알아보자.

우선, URI를 입력하면 아래와 같은 일이 일어난다.

  1. 해당 URI가 나타내는 리소스를, 서버가 클라이언트에 전달한다. 이때, 해당 URI가 나타내는 자원이 준비돼있을 필요는 없다. 만약 준비돼있다면, 해당 자원을 서버가 보내주면 되고, 준비돼있지 않다면, 요청받았을 때 서버가 해당 자원을 만든 뒤, 보내주는 것도 가능하다.
  2. 만약 html 파일이 전달되면, 해당 html 파일을 파싱하고, 추가적으로 필요한 자원을 요청한다.
  3. 전달받은 자원을 파싱하여, 웹 브라우저가 렌더링해준다.

SPA에서는, 이 과정에서 문제가 발생할 수 있다. 왜 그럴까?
서론에서 얘기했던 이유로, SPA가 마치 여러 페이지를 갖는 것처럼 동작하게 만들고 싶을 수 있다.
그래서, 링크를 클릭하면, 다른 페이지로 이동하도록 만들었다고 가정해보자. 해당 링크는, 해당 페이지를 나타내는 URI를 href attribute로 갖고 있을 것이다. 하지만 SPA 특성상, 웹페이지가 한 개 밖에 없기 때문에, 해당 웹페이지가 존재할 수 없고, 문제가 발생한다.

구체적으로는 아래와 같은 문제가 발생한다.
1. 서버가 해당 URI에 대응하는 자원을 찾지 못하기에, GET 에러가 발생한다.
2. 존재하지 않기 때문에, 해당 페이지를 사용자에게 보여줄 수 없다.

이를 해결하기 위해, 구원투수 react-router가 등판하는데...

리액트 라우터가 위 문제를 해결한 방법

필자는, 리액트 라우터를 처음 접했을 때, 도대체 어떻게 존재하지도 않는 페이지를 라우팅해주는지, 이해할 수 없었고, 마법처럼 느껴질 뿐이었다.

리액트-라우터는, 이를, History 웹 api를 사용해 구현했다.

페이지를 사용자에게 보여줄 수 없는 문제 해결 방법

이는, 현재 문서의 주소에 따라서, 컴포넌트를 선택적으로 렌더링함으로써 해결한다.
예시로, 현재 문서의 주소가 www.naver.com이라 해보자. www.naver.com/today-comic 페이지가, 오늘 제일 핫한 네이버 웹툰 5개를 보여주는 페이지라 가정해보자. 만약, 현재 문서의 주소가 www.naver.com/today-comic이라면, 웹툰 5개를 나타내는 컴포넌트를 렌더링하고 아니라면, 이 컴포넌트를 렌더링하지 않는 방식으로 작동한다. 즉, 주소에 맞는 컴포넌트를 렌더링하여, 마치 새로운 페이지가 서버에서 전달된 것처럼 유저에게 보인다.
그렇다면 리액트-라우터는, 어떻게 현재 문서의 주소를 알 수 있는 걸까?
리액트-라우터는, location이라고 하는, window.location 객체와 유사한 객체를 만들어서, 현재 문서의 주소를 파악한다.

서버가 자원을 찾지 못해 발생하는 GET 오류 해결 방법

리액트-라우터는, 다른 페이지로 이동할 때, 서버에 요청을 보내지 않음으로써 해결한다.
여기서 의아한 점은, '웹 브라우저의 주소창의 주소는 분명히 바뀌었는데, 어떻게 서버에 요청이 가지 않느냐'는 점이다.
이는, 위의 '단어 설명' 섹션에서 설명했던, history api의 pushState()함수를 사용해서, 페이지를 이동하기 때문이다. 이 함수로 페이지를 이동하면, 새 페이지의 URI로 서버에 요청을 보내지 않는다.

그러면 여기서 한 가지 의문점이 더 생긴다. 만약에 주소창에 직접 특정 페이지의 URI를 입력하면...? 아니면 사용자가, 특정 페이지를 즐겨찾기에 추가하여, 나중에 해당 페이지에 직접 접근할 수도 있다. 이런 상황에서는, 서버에 요청이 전달되고, 해당 자원이 존재하지 않기 때문에, 오류가 발생할 거라는 생각이 들었다. 백엔드 공부를 해보지 않았기에, 시도해보지 않았지만, 이런 오류는 얼마든지 가능하리라 생각한다. 이를 어떻게 해결할 수 있을까?

사용자가 직접 특정 페이지에 접근하는 문제 해결 방법

이는, 서버-사이드 라우팅을 사용해 해결할 수 있을 거다. 어느 페이지를 요청하든지 간에, 서버가 갖는 단일 페이지를 전송하도록 설정하면, 간단하게 해결될거라 생각한다. 단일 페이지가 전송되고 나면, 리액트-라우터에 의해, 문서 주소에 맞는 컴포넌트를 렌더링하게 된다.

마무리

리액트-라우터는, 단일 페이지 어플리케이션이, 마치 여러 페이지로 구성된 것처럼 작동하도록 돕는다. 이를 통해, 사용자는, 원하는 페이지를 즐겨찾기에 추가할 수 있고, 뒤로가기와 앞으로가기 버튼을 사용할 수 있으며, 검색 엔진이 우리의 어플리케이션을 이해하기 쉽게 도울 수 있다.
여러 페이지가 존재하는 것처럼 구현하기 위해, 리액트-라우터는 history api와, window.location 객체를 사용한다.

참고자료

https://reactrouter.com/docs/en/v6/getting-started/concepts
https://www.codesmith.io/blog/react-router-and-client-side-routing
https://developer.mozilla.org/ko/docs/Web/API/Window/location

0개의 댓글