React(생활코딩)_14일차_React Router_리액트라우터란, BrowserRouter, HashRouter, Routes, Route, Link, NavLink, useNavigate()

Lina Hongbi Ko·2023년 9월 25일
0

React_생활코딩

목록 보기
15/23

저번 시간에는 라이프사이클 메소드와 useEffect()에 대해서 배워보았다.

오늘은 리액트 라우터에 대해서 공부하고 또 기록해보려고 한다.

📍 React Router

리액트 라우터를 공부하기 전 먼저,

라우터가 무엇인지 대충 이해하고 있어서 그 개념을 먼저 정리 하려고 한다.

✏️ Router

: 라우팅 시켜주는 것을 라우터라고 한다. 라우팅이라는 것은 사용자가 어떤 주소로 들어왔을때 그 주소에 해당하는 적당한 페이지(데이터)를 사용자에게 보내주는 것을 말한다.

방통대에서 라우트에 관해서 네트워크 통신 시간에 배웠던게 생각났다.

네트워크상에서 데이터를 보내주는 역할을 하는 아이가 라우터였다는 것이 문득 떠올랐다.

그럼 '리액트 라우터'는 뭔데?? 하는 생각이 들 것이다.

말 그대로다 :)

리액트에서 라우터 역할을 수행하는 것을 '리액트 라우터' 라 하고, 우리는 이것을 설치해 이용할 수 있다.

✏️ React Router

: 리액트는 SPA (Single Page Application) 방식이다.
여러개의 페이지를 사용하며, 새로운 페이지를 로드하는 기존의 MPA 방식과 달리, 새로운 페이지를 로드하지 않고 하나의 페이지 안에서 필요한 데이터만 가져오는 형태를 가진다.

💡 SPA와 MPA가 뭔데요?

공부를 하다보니, SPA와 MPA의 차이점을 먼저 설명하면서 리액트 라우터에 대해 얘기하는 사람이 대부분이었고 나는 이 둘을 잘 몰랐다. 그래서 찾아보았다.

  • SPA (Single Page Application) : 페이지가 1개인 어플리케이션
  • MPA (Multiple Page Application) : 페이지가 다수인 어플리케이션

이전부터 우리는 웹어플리케이션의 구조를 주로 MPA방식으로 사용했다. 여러 페이지로 구성되어 있고, 유저가 요청 할 때 마다 페이지가 새로고침 되었다. 따라서 페이지를 로딩 할 때 마다 서버로부터 리소스를 전달받아 해석 후 렌더링 하는 형태를 가졌다.

하지만 캐싱과 압축을 이용함에도 불구하고, 매번 서버에서 렌더링하는 것은 자원을 낭비하고 불필요한 트래픽도 생기는 일이었다. 게다가 시대가 발전하면 발전할수록 어플리케이션이 하는 일도 많아졌다.

그래서 리액트 같은 라이브러리 혹은 프레임워크를 사용해서 뷰 렌더링을 유저의 브라우저가 담당하도록 하고, 먼저 어플리케이션을 브라우저에 로드 한 다음에 정말 필요한 데이터만 전달받아 보여주도록 하는 SPA방식을 채택하도록 하였다.

예를 들어,

로그인과 회원가입 화면을 구성한다고 가정했을 때, MPA방식은 로그인.html 페이지와 회원가입.html가 별도로 나눠져 있다. 사용자가 회원가입 버튼을 누르면 로그인.html 에서 회원가입.html 로 페이지를 이동하는 방식을 가지고 있지만, SPA 방식에서는 오직 하나의 html 페이지 안에서 로그인과 회원가입에 대한 데이터 js 파일을 가지고 사용자가 회원가입 버튼을 눌렀을 때, 회원가입에 대한 데이터 자료를 렌더링하는 방식이다.

cf) SPA의 단점

: 앱의 규모가 커지면 자바스크립트 파일 사이즈가 너무 커진다. 유저가 실제로 방문하지 않을수도 있는 페이지에 관련된 렌더링 관련 스크립트도 불러오기 때문이다.

그렇다면 싱글페이지라고 해서, 한 종류의 화면만 있는 것일까?

No.

예를 들어 예약 게시판 페이지를 만든다면 홈, 게시판 목록, 글쓰기 등의 화면이 있어야 한다. 또한 이 화면에 따라 주소도 만들어줘야 한다. 그 이유는 주소가 있어야 북마크도 할 수 있고, 주소를 이용해야 크롤러들이 이를 수집하고 나중에 구글이나 네이버 등 검색엔진을 통해 유입될 수 있기 때문이다.

앞에서 언급했듯이 리액트는 SPA 방식을 따르므로, 다른 주소에 따라 다른 뷰를 보여주는 '라우팅'이라는 것을 하기 위해서 'React Router' 이라고 하는 라이브러리를 사용한다.

React Router는 페이지를 새로 불러오지 않는 상황에서 각 선택에 따라서 선택된 데이터를 하나의 페이지에서 렌더링 해주는 라이브러리이다.

cf) 리액트 라우터의 단점

리액트 라우터같이 브라우저측에서 자바스크립트를 사용하여 라우트를 관리하는것의 잠재적인 단점으로, 자바스크립트를 실행하지 않는 일반 크롤러에선 페이지의 정보를 제대로 받아가지 못한다는 점이다. 그렇기 때문에, 구글, 네이버, 다음 등 검색엔진에서 페이지가 검색결과에 잘 안 나타날수도 있다. 그리고 자바스크립트가 실행될 때까지 페이지가 비어있기 때문에, 자바스크립트 파일이 아직 캐싱되지 않은 사용자는 아주 짧은 시간동안 흰 페이지가 나타날 수도 있다는 단점도 있지만 서버사이드 렌더링을 통하여 해결 할 수 있다.


React Router 사용하기

자, 이제 리액트 라우터에 대해서 알아봤으면 본격적으로 사용해 보면서 실습해보자.

리액트 라우터를 사용하려면 먼저 설치해줘야 한다. 터미널에서 해당 디렉토리로 간 후, 아래처럼 입력해준다.

npm install react-router-dom
yarn add react-router-dom

요렇게 입력해주면 리액트 라우터가 설치된다.

그리고나서 라우터를 임포트 한다.

// App.js 파일

import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";

function App() {
	return(
    	...
    )
}

export default App;

여러 종류의 라우터 컴포넌트를 제공하는데, 이중 가장 많이 사용하는 라우터 컴포넌트는 BrowserRouter와 HashRouter이다.

✏️ BrowserRouter

: HTML5를 지원하는 브라우저의 주소를 감지 한다.
(브라우저 History API를 사용해 현재 위치의 URL을 저장해주는 역할)

✏️ HashRouter

: http://~.com/#hashrouter (해쉬태그)를 감지 한다.

우리는 Browser Router을 사용할 것이므로 요놈을 import 해주었다. 이 BrowserRouter은 리액트 라우터의 도움을 받고 싶은 컴포넌트의 최상위 컴포넌트를 감싸는 래퍼 컴포넌트이다. 현재 최상위 컴포넌트는 App 컴포넌트 이므로 아래처럼 감싸준다.

그리고 사용자가 이 웹사이트에 아무런 path도 지정하지 않고 들어왔을 때는 홈 컴포넌트를 보여주고, 사용자가 각 URL로 들어오면 해당하는 컴포넌트를 보여주는 것을 구현하려고 한다.

// App.js 파일

import React from "react";
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './components/Home';
import Topics from './components/Topics';
import Contact from './components/Contact';

function App() {
	return(
    	<BrowserRouter>
        	<div className="App">
            	<h1>Hello React Router DOM</h1>
                <ul>
                	<li><a href="/">Home</a></li>
                    <li><a href="/topics">Topics</a></li>
                    <li><a href="/contact">Contact</a></li>
                </ul>
                <Routes>
                    <Route path="/" elements={<Home />}></Route>
                    <Route path="/topics" elements={<Topics />}></Route>
                    <Route path="/contact" elements={<Contact />}></Route>
                </Routes>
            </div>
        </BrowserRouter>
    );
}

export default App;
// Home.js 파일

import React from 'react';

function Home () {
	return(
    	<div>
        	<h2>Home</h2>
            Home...
        </div>
    );
}

export default Home;
// Topics.js 파일

import React from 'react';

function Topics () {
	return(
    	<div>
        	<h2>Topics</h2>
            Topics...
        </div>
    );
}

export default Topics;
// Contact.js 파일

import React from 'react';

function Contact () {
	return(
    	<div>
        	<h2>Contact</h2>
            Contact...
        </div>
    );
}

export default Contact;

Home, Topic, Contact 컴포넌트를 URL에 따라 달라지게 구현했다. 그렇게 하기 위해서 Routes, Route를 사용했다.

✏️ Routes

: 자식 route들을 구성하고 있는 단위이고, 여러 Route를 감싸서 그 중 규칙이 일치하는 라우트 단 하나만을 렌더링 시켜주는 역할을 한다.

✏️ Route

: path 속성에는 경로, element속성에는 보여줄 컴포넌트를 넣어 준다. 하위경로에 여러 라우팅을 매칭시키고 싶다면 URL 뒤에 *을 사용하여 일치시킬 수 있다.

💡 생코님 책에서는 Switch를 소개하고 있지만 리액트 v6 이후부터 Switch 컴포넌트는 Routes 컴포넌트로 대체되었다.

💡 Route 컴포넌트에 v6이전 버전에서는 elements 속성을 쓰지 않고 직접 컴포넌트 자체를 입력해주었으나 elements 속성을 써서 바로 원하는 컴포넌트를 넣을 수 있게 되었다.

💡 Route 컴포넌트에 v6이전 버전에서는 exact 라는 속성을 넣어서 path가 정확하게 매치되는 컴포넌트만 보여줬었지만 업데이트 이후 없어졌다. (정확하게 매치되는 것만 바로 보여주도록 바뀜)

// v6 이전에서 썼던 방식
<Switch>
	<Route path="/" exact><Home></Home></Route>
    <Route pat="/topics"><Topics></Topics></Route>
</Switch>

// v6 이후 쓰는 방식 (어쩌면 또 바뀔 수도 있겠지?!)
<Routes>
	<Route path="/" elements={<Home />}></Route>
    <Route path="/topics" elements={<Topics />}<Route>
</Routes>

결과화면

위처럼 결과 화면이 나온다. 각 링크를 클릭하면 해당 컴포넌트가 url에 매치 되어 나온다. 나중에 컴포넌트의 계층 구조가 깊어지면 그 컴포넌트가 어디에 있든 간에 path를 지정해주고, 그 주소와 매칭되는 컴포넌트만 표시된다. 컴포넌트를 여러번 작성하면 주소에 따라 해당 컴포넌트도 여러번 표시된다.

자, 다음으로는 Link 컴포넌트에 대해 알아보자.

SPA방식에서는 페이지가 리로드 되지 않고, 동적으로 데이터를 가져오거나 ajax 같은 기술을 이용해 비동기적으로 데이터를 가져와서 페이지를 만드는 것이라고 했다. 하지만 위의 리스트들은 누를때마다 우리는 페이지가 새로 로딩되는 것을 확인 할 수 있다. 이때 우리는 a태그가 아닌 Link 컴포넌트를 이용해서 페이지를 리로드 하지 않고, 데이터를 가져올 수 있도록 할 수 있다.

Link 컴포넌트를 import하고 사용해보자.

// App.js 파일

import { BrowserRouter, Routes, Route, Link } from "react-router-dom";

... 생략 ...

function App() {
	return(
    	<BrowserRouter>
        	<div className="App">
            	<h1>Hello React Router DOM</h1>
                <ul>
                	<li><Link to="/">Home</Link></li>
                    <li><Link to="/topics">Topics</Link></li>
                    <li><Link to="/contact">Contact</Link></li>
                </ul>
                <Routes>
                    <Route path="/" elements={<Home />}></Route>
                    <Route path="/topics" elements={<Topics />}></Route>
                    <Route path="/contact" elements={<Contact />}></Route>
                </Routes>
            </div>
        </BrowserRouter>
    );
... 생략 ...

확인해보면 페이지가 바뀌지 않는 것을 알 수 있다.

: to 속성을 넣어서 경로를 작성해준다.

이제 링크를 클릭하면 페이지가 리로드 되지 않지만, 제대로 동작하려면 어플리케이션의 웹서버 설정이 사용자가 어떤 패스로 들어와도 동일한 웹페이지를 서비스 할 수 있어야 한다. 이것를 위한 Router가 위에서 언급한 HashRouter이다.

BrowserRouter을 HashRouter로 변경해보자.

// App.js 파일

import { HashRouter, Routes, Route, Link} from "react-router-dom";
... 생략 ...

function App() {
	<HashRouter>
    ... 생략 ...

바꾼 후 주소창을 보면 url에 #이 들어간 것을 볼 수 있다. #이 붙어 있으면 뒤의 내용은 북마크라는 뜻이다. 웹서버는 #문자 뒷부분의 주소를 무시하지만, react-router-dom은 #뒷부분의 내용을 알 수 있기 때문에 이 url을 읽어서 적절한 컴포넌트로 라우팅 해줄 수 있다고 한다.

하지만 #이 들어가 깔끔해 보이지 않기 때문에 웹 서버 설정에 따라 어떤 패스로 들어오든, 루트 페이지에 있는 HTML 파일을 서비스 할 수 있다면 BrowserRouter을 사용하고, 그렇지 않다면 HashRouter를 사용하면 된다고 책에서 말하고 있다.

다음으로는 NavLink 컴포넌트에 대해 알아보자.

Link 컴포넌트를 NavLink 컴포넌트로 바꿔서 사용해보면,

// App.js 파일

import { BrowserRouter, Routes, Route, NavLink } from "react-router-dom";

... 생략 ...

function App() {
	return(
    	<BrowserRouter>
        	<div className="App">
            	<h1>Hello React Router DOM</h1>
                <ul>
                	<li><NavLink to="/">Home</NavLink></li>
                    <li><NavLink to="/topics">Topics</NavLink></li>
                    <li><NavLink to="/contact">Contact</NavLink></li>
                </ul>
				... 생략 ...

리스트 목록 하나를 클릭하고 콘솔창을 확인해보면 'active'클래스가 들어간 것을 확인할 수 있다. 이 클래스를 이용해서 css를 변경해 스타일을 바꿀 수 있다.

: 만약 현재 경로와 Link에서 사용하는 경로가 일치하는 경우 특정 스타일 혹은 클래스를 적용할 수 있는 컴포넌트

💡 v6이후부터 NavLink 컴포넌트의 속성 activeStyle, activeClassName(특정 스타일, 클래스를 적용해주던 속성) 이 사라졌다.

✏️ useNavigate() hook

: 이 훅을 실행하면 페이지 이동을 할 수 있게 해주는 함수를 반환한다. 반환하는 함수를 특정 변수에 저장 한 후, 변수의 인자에 원하는 path값을 넘겨주면 해당 경로로 이동할 수 있다. Link와 다른 점은 함수 호출을 통해 페이지를 이동하기 때문에 특정 조건을 충족할 경우에 페이지 이동을 하도록 할 수 있다.

먼저, 임포트하고 hook를 호출해 변수에 담아서 사용한다.

// useNavigate() hook 사용해보기

import React from "react";
import { useNavigate } from "react-router-dom";

function Login() {
    const navigate = useNavigate();
    const goToMain = () => {
    		navigate("/main");
          };

	return (
      <div>
        <button onClick={goToMain}>
          로그인
        </button>
      </div>
  	);
}

export default Login;

📍path 이동

  • a 태그 : 외부 프로젝트로 이동하는 경우
  • Link 컴포넌트 : 프로젝트 내에서 페이지 전환하는 경우
  • useNavgate 훅 : 특정 이벤트시 페이지 전환하는 경우

📍 Link vs useNaviagte()

  • Link : 클릭 시 바로 이동하는 로직 구현 시에 사용
    ex) 상품 리스트에서 상세 페이지 이동 시

  • useNavigate : 페이지 전환 시 추가로 처리해야 하는 로직이 있을 시 사용
    ex) 로그인 버튼 클릭 후,
    회원가입 되어 있는 사용자 -> Main 페이지로 이동
    회원가입이 되어 있지 않은 사용자 -> SignUp 페이지로 이동

💡 TIP 💡
: BrowserRouter로 감쌀때, index.js파일에서 감싸주면 더 편리하다. 작업을 하다보면 태그 바깥에 또 다른 태그를 선언할 수 있기 때문에 index.js 파일에서 감싸주고 관리하는 것이 좋다.

// index.js 파일

... 생략 ...
import { BrowserRouter } from 'react-router-dom'; 

... 생략 ...
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<BrowserRouter><App /><BrowserRouter>);

... 생략 ...

출처

https://goddaehee.tistory.com/305?category=395445
https://leeseong010.tistory.com/25
https://react.vlpt.us/react-router/
https://m.blog.naver.com/sejun3278/221797203201
https://phsun102.tistory.com/72?category=880183
https://0cddo-room.tistory.com/entry/react-router%EC%82%AC%EC%9A%A9%EA%B3%BC-v6%EC%97%90%EC%84%9C%EC%9D%98-switch%EC%9D%98-%EB%B3%80%ED%99%94
https://kyung-a.tistory.com/36
https://velog.io/@seokkitdo/React-Link-useNavigate
생활코딩! 리액트 프로그래밍 책

profile
프론트엔드개발자가 되고 싶어서 열심히 땅굴 파는 자

0개의 댓글