20_리액트_라우터
FrontEnd/React

20_리액트_라우터

728x90

본 게시물은 react-router-dom v5버전을 다룹니다! v6버전은 사용하지 않습니다.

 

Single Page Application

 

라우팅을 클라이언트가 담당

 

라우팅???

어던 주소에 어떤 UI를 보여줄지 처리하는 작업!

 

이전에는 위와같은 구조로 서버에서 정보를 가져왔는데 이는 사용자가 늘어나면 불필요한 작업이 많아진다.

 

SPA를 사용하면 굳이 /about페이지로 이동하지 않아도 되고 필요한 정보가 있다면 그 부분만 json 형태로 정보를 받게 된다.

 

SPA의 단점

 

1. 앱의 규모가 너무 커지면 JS파일의 크기가 너무 커진다.

>> code splitting로 해결

각 기능별로 파일을 분리시켜 필요한 시점마다 파일을 불러오는 방식

 

2/ 자바스크립트가 구동되지 않으면 UI를 볼 수 없음

>> Server Side Rendering으로 해결

 

 

리액트 라우터

컴포넌트를 기반으로 라우팅을 한다.

 

Next.js

파일경로,이름을 기반으로 라우팅

서버사이드 렌더링이 간편함

 

이 중에서 리액트 라우터에 대해 좀 더 자세히 알아보자.

 

<BrowserRouter>

HTML5 History API 사용

주소만 바꾸고 페이지는 다시 불러오지 않음

 

<HashRouter>

주소뒤에 #를 사용함

구형 브라우저에서도 작동한다.

 

<MemoryRouter>

브라우저 주소와 무관함.

임베디드 웹앱 등 브라우저 주소랑 상관없이 작동함

 

<StaticRouter>

서버사이드 렌더링에서 사용

 

<Route>

라우트를 정의할 때 사용

 

<Link>

사용한 Router의 주소를 바꿈

a태그이지만 새로고침이 되진 않음

 

 

리액트 프로젝트를 하나 만든 후,

 

yarn add react-router-dom

react-router-dom 라이브러리를 하나 설치해준다.

 

 

//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

먼저 index.js에서 BrowserRouter로 감싸주고

 

//App.js
import React from "react";
import {Route} from 'react-router-dom';
import About from "./About";
import Home from "./Home";

function App() {
  return (
    <div>
      <Route path ="/" component = {Home} />
      <Route path="/about" component = {About} />
    </div>
  );
}

export default App;
//About.js
import React from 'react';

function About() {
    return (
        <div>
            <h1>소개</h1>
            <p>리액트 라우터 실습</p>
        </div>
    );
    
}


export default About;

첫페이지와 소개페이지를 구현할 컴포넌트를 간단히 2개 만들어준다.

 

//App.js
import React from "react";
import {Route} from 'react-router-dom';
import About from "./About";
import Home from "./Home";

function App() {
  return (
    <div>
      <Route path ="/" component = {Home} />
      <Route path="/about" component = {About} />
    </div>
  );
}

export default App;

APP.js에서 위와같이 Route경로와 컴포넌트를 실행하고 서버를 열어주면

잘 작동한다

 

주소 뒤에 /about을 붙여주면

 

제대로 나오는 것을 확인할 수 있다

 

//App.js
import React from "react";
import { Routes,Route, Link } from 'react-router-dom';
import About from "./About";
import Home from "./Home";
import Profile from "./Profile";

function App() {
  return (
    <div>
      <ul>
        <li>
          <Link to="/">홈</Link>
        </li>
        <li>
          <Link to="/about">소개</Link>
        </li>
      </ul>
      <hr />
      <Route path="/" exact={true} component={Home} />
      <Route path="/about" component={About} />
      
    </div>
  );
}

export default App;

Link를 사용하면 두 주소를 왔다갔다 하는게 가능하다. <a>태그를 사용하지 않는이유는, <a>태그는 누를 때마다 주소가 새로고침 되는 형식이기 때문이다.

 

파라미터와 쿼리

주소를 통해 동적인 값을 가져오는 것

 

URL Parameter

/profiles/mingyu

 

Query

옵션이 추가될때

/filter?type=book&sort_By=date

 

 

 

URL Parametr

 

App.js에 해당 파일 경로를 지정해둔 후

<Route path="/profiles/:username" component={Profile} />

 

 

//Profile.js
import React from 'react';


const profileData = {
    mingyu : {
        name : '정민규',
        description : '곧 반오십'
    },
    sumin : {
        name : '홍수민',
        description : '정민규 여자친구'
    }
}

function Profile( {match} ) {
 
    
    const { username } = match.params;
    const profile = profileData[username];

    if (!profile) {
        return <div>존재하지 않는 사용자</div>
    }

    return (
        <div>
            <h3>{username} ({profile.name})</h3>
            <p>{profile.description}</p>
        </div>
    );
    
}


export default Profile;

위 컴포넌트와 연결후 주소 뒤에 /id를 붙이면 해당 정보가 표시되게끔 할 수 있다.

 

Query

//About.js
import React from 'react';

function About({location}  ) {
    
    console.log(location);
    
    return (        
        <div>
            <h1>소개</h1>
            <p>리액트 라우터 실습</p>
        </div>
    );    
}


export default About;

위처럼 location을 불러오고 확인하고 주소창에

 

http://localhost:3000/about?a=1

 

와 같은 query값을 하나 넣어주면 콘솔창에서 빼낼 수 있는 형태가 된다.

 

이를 직접빼서 사용해도 되지만 qs 라이브러리를 사용해 보자.

 

yarn add qs

 

const query = qs.parse(location.search, {
        ignoreQueryPrefix : true,  // ?a:1 이아닌 a:1로 들어오게
    })

    console.log(query)

위 코드를 추가함으로 손쉽게 query값을 추출해낼 수 있다.

 

//About.js
import React from 'react';
import qs from 'qs';

function About({location}  ) {
    
    
    const query = qs.parse(location.search, {
        ignoreQueryPrefix : true,  // ?a:1 이아닌 a:1로 들어오게
    })

    const detail = query.detail === 'true'; //문자열로 비교!

    
    return (        
        <div>
            <h1>소개</h1>
            <p>리액트 라우터 실습</p>
            {detail && <p>디테일 값이 true입니다!</p>}
        </div>
    );    
}


export default About;

 

다음과 같은 주소에 따라 추가적인 문구를 작성하게도 할 수 있다.

 

주소 : http://localhost:3000/about?detail=true

 

 

 

 

 

 

 

서브라우트

라우트 안에 들어있는 또다른 라우트

 

//Profiles.js
import React from 'react';
import Profile from './Profile';
import {Link,Route} from 'react-router-dom';

function Profiles() {
    return (
        <div>
            <h3>사용자 목록</h3>
            <ul>
                <li><Link to ="/profiles/mingyu">mingyu</Link></li>
                <li><Link to ="/profiles/sumin">sumin</Link></li>
            </ul>

            <Route 
                path = "/profiles" 
                exact render = { () => <div>사용자를 선택해 주세요</div>}
             />
            <Route path="/profiles/:username" component={Profile} />
        </div>
    );
    
}


export default Profiles;

컴포넌트를 하나 만들고 그 안에 Route를 넣어준다. 이 profiles를 app.js에서 호출하게 되면

 

위처럼 사용할 수 있다.

 

history

history를 사용하면 여러 기능을 사용할 수 있다.

//HistorySample.js
import React, { useEffect } from 'react';

function HistorySample( {history}) {

    const goBack = () => {
        history.goBack();
    }

    const goHome = () => {
        history.push('/'); //방문기록 O
    }

    const replaceToHome = () => {
        history.replace('/');   //방문기록 x
    }



    useEffect(() => {
        console.log(history)
        const unblock = history.block('정말 떠나실건가요?');
        return () => {  //컴포넌트가 언마운트될때 실행
            unblock();
        }
    }, [history])

    return (
        <div>
            <button onClick={goBack}>뒤로가기</button>
            <button onClick={goHome}>홈</button>
            <button onClick={replaceToHome}>홈(Replace)</button>
        </div>
    );
    
}


export default HistorySample;

위 컴포넌트를 작성하고 App.js와 연동한다. 

goBack은 뒤로가기, Push는 그곳으로 이동하기, block는 나가기전에 출력할 메시지 를 출력하게 된다.

 

다음과 같은 창이 출력되게 된다.

 

withRouter

라우터 컴포넌트가 아닌 곳에서 match,location,history를 props로 받아서 사용

 

//WithRouterSample.js
import React from 'react';
import {withRouter} from 'react-router-dom';

// <Route> 식으로 선언되지 않아도 history를 사용가능

function WithRouterSample( {location, match, history} ) {
    return (
        <div>
            <h4>location</h4>
            <textarea value = {JSON.stringify(location,null,2)} readOnly />
            <h4>match</h4>
            <textarea value = {JSON.stringify(match,null,2)} readOnly />
            <button onClick={() => history.push('/')}>홈으로</button>  
        </div>
    );
    
}


export default withRouter(WithRouterSample); // 감싸줌

위 컴포넌트를 <WithRouterSample/>과 같이 선언해도 history를 사용할 수 있다.

 

Switch

여러 라우트 중 하나만 보여줌

 

<Switch>
        <Route path="/" exact component={Home} />
        <Route path="/about" component={About} />
        <Route path="/profiles" component={Profiles} />
        <Route path="/history" component={HistorySample} />
        <Route render = {({location}) => <div>
          <h2>이 페이지는 존재하지 않음!</h2>
          <p>{location.pathname}</p>
        </div>} />
</Switch>

마치 스위치문과 비슷한데, 여러 Route문중에서 걸리는게 있을때 그 라우트 페이지로 이동하게 된다.

 

아무 주소나 넣었을 경우

404페이지나 오류 페이지를 만들때 사용하면 좋다.

 

 

NavLink

현재주소와 일치한다면 스타일 바꾸기

Link와 유사하다.

 

<li>
<NavLink 
	to ="/profiles/mingyu"
	activeStyle={{
		background : 'black',
		color : 'white'
		}}
	>mingyu</NavLink>
</li>

위와같이 NavLink를 사용하면 Link가 눌렸을때 서식을 설정할 수 있다.

 

isActive를 쓰면 함수형으로 param을 받아서 if문처럼 사용할 수도 있다.

 

 

 

<Prompt>

history블록을 컴포넌트로 구현

 

 

useReactRouter Hook 사용

 

yarn add use-react-router

 

해당 라이브러리를 설치한다.

 

//RouterHookSample.js
import React from 'react';
import useReactRouter from 'use-react-router'


function RouterHookSample() {
    const {history, location, match } =useReactRouter();

    console.log( {history,location,match});

    return (
        null
    );
    
}


export default RouterHookSample;

위처럼 구현하고 profiles.js파일 안에 추가시켜두면 history,lcoation,match값을 잘 가지고 오는 것을 알 수 있다.

 

아래는 결과적으로 만든 프로그램의 코드들이다!

 

더보기
//App.js
import React from "react";
import { Route, Link, Switch } from 'react-router-dom';
import About from "./About";
import HistorySample from "./HistorySample";
import Home from "./Home";
import Profiles from "./Profiles";

function App() {
  return (
    <div>
      <ul>
        <li>
          <Link to="/">홈</Link>
        </li>
        <li>
          <Link to="/about">소개</Link>
        </li>
        <li>
          <Link to="/profiles">프로필 목록</Link>
        </li>
        <li>
          <Link to="/history">예제</Link>
        </li>
      </ul>
      <hr />

      <Switch>
        <Route path="/" exact component={Home} />
        <Route path="/about" component={About} />
        <Route path="/profiles" component={Profiles} />
        <Route path="/history" component={HistorySample} />
        <Route render = {({location}) => <div>
          <h2>이 페이지는 존재하지 않음!</h2>
          <p>{location.pathname}</p>
        </div>} />
      </Switch>
    
    </div>
  );
}

export default App;

 

//HistorySample.js
import React, { useEffect } from 'react';

function HistorySample( {history}) {

    const goBack = () => {
        history.goBack();
    }

    const goHome = () => {
        history.push('/'); //방문기록 O
    }

    const replaceToHome = () => {
        history.replace('/');   //방문기록 x
    }



    useEffect(() => {
        console.log(history)
        const unblock = history.block('정말 떠나실건가요?');
        return () => {  //컴포넌트가 언마운트될때 실행
            unblock();
        }
    }, [history])

    return (
        <div>
            <button onClick={goBack}>뒤로가기</button>
            <button onClick={goHome}>홈</button>
            <button onClick={replaceToHome}>홈(Replace)</button>
        </div>
    );
    
}


export default HistorySample;
//Profiles.js
import React from 'react';
import Profile from './Profile';
import { NavLink,Route} from 'react-router-dom';
import WithRouterSample from './WithRouterSample';
import RouterHookSample from './RouterHookSample';

function Profiles() {
    return (
        <div>
            <h3>사용자 목록</h3>
            <ul>
                <li>
                    <NavLink 
                        to ="/profiles/mingyu"
                        activeStyle={{
                            background : 'black',
                            color : 'white'
                        }}
                        
                    >mingyu</NavLink>
                </li>
                <li>
                    <NavLink 
                        to ="/profiles/sumin"
                        activeStyle={{
                            background : 'black',
                            color : 'white'
                        }}
                    >sumin</NavLink>
                </li>
            </ul>

            <Route 
                path = "/profiles" 
                exact render = { () => <div>사용자를 선택해 주세요</div>}
             />
            <Route path="/profiles/:username" component={Profile} />
            <WithRouterSample/>
            <RouterHookSample/>
        </div>
    );
    
}


export default Profiles;
//Profile.js
import React from 'react';


const profileData = {
    mingyu : {
        name : '정민규',
        description : '곧 반오십'
    },
    sumin : {
        name : '홍수민',
        description : '정민규 여자친구'
    }
}

function Profile( {match} ) {
 
    
    const { username } = match.params;
    const profile = profileData[username];

    if (!profile) {
        return <div>존재하지 않는 사용자</div>
    }

    return (
        <div>
            <h3>{username} ({profile.name})</h3>
            <p>{profile.description}</p>
        </div>
    );
    
}


export default Profile;
728x90

'FrontEnd > React' 카테고리의 다른 글

22_리덕스 미들웨어  (0) 2022.01.05
21_리액트_리덕스  (0) 2022.01.02
19_리액트 API연동  (0) 2021.12.29
18_styled-components  (0) 2021.12.27
17_리액트_CSS Module  (0) 2021.12.24