mjeongriver
article thumbnail
Published 2023. 1. 19. 15:34
day70-react TIL/React

1. 리액트 라우터 (책 p.403)

1) spa란?

- 하나의 페이지로 만들어진 어플리케이션을 의미합니다.

 

전통적 웹 어플리케이션 vs SPA

 

- SPA는 html파일을 브라우저 측에서 로드하고, 필요한 데이터는 API와 ajax통신을 이용해서 처리합니다.

- 브라우저에서 사용자가 상호작용 하면 필요한 부분만 업데이트 해서 처리합니다.

- 멀티플랫폼 Android, IOS에 대응하여 웹뷰로 처리하는 목적으로도 사용됩니다.

 

2) SPA의 단점

- 앱의 규모가 커지면, JS파일도 너무 커져서 로딩이 오래걸리게 됩니다.

- 브라우저에서 렌더링이 완료되기 까지 비어있는 화면이 나오게 됩니다.

- 그래서 규모가 큰 어플리케이션은 SSR(서버사이드 렌더링) 방식으로 처리합니다. (웹팩 설정 필요)

 

3) 라우팅이란?

- 브라우저의 주소상태에 따라 다양한 화면을 보여주도록 처리하는 것을 라우팅이라고 합니다.

 

4) 사용 이유

- create-react-app로 프로젝트를 생성하게 되면 기본적으로  SPA에 CSR(클라이언트 렌더링) 입니다.

- 하나의 페이지만 사용하게 되는 것입니다.

- SPA이지만 라우터를 활용해서 사용자로 하여금 여러 페이지가 존재하는 것처럼 느껴지게 할 수 있습니다.

 

2. 라우터 설치하고 프로젝트 시작하기

cd .. 하면 상위의 폴더로 이동해서 프로젝트 생성 - 완료 되면 open folder 해서 react-router로 폴더 선택
하고 npm start

1) 라우터를 적용할 프로젝트 생성

- npm create react-app 프로젝트명

2) 라우터 설치

- npm add react-router-dom

3) 프로젝트 시작

- npm start

 

3. 라우터 적용하기

- 라우터를 사용할때는 index.js에서 App컴포넌트를 <BrowserRouter>로 감싸줍니다.

- 페이지 컴포넌트 만들기

- App 컴포넌트에서 <Routes>를 사용해서 분기하기

- 주소 요청을 통해 확인하기

 

* index.js

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

);

 

App.js

- /요청은 Home컴포넌트가 나타납니다

- /user요청은 User컴포넌트가 나타납니다.

- /info요청은 Info컴포넌트가 나타납니다.

function App() {

  return (
    <Routes>
        <Route path='/' element={<Home/>}/>
        <Route path='/user' element={<User/>}/>
        <Route path='/info' element={<Info/>}/>
    </Routes>
  );
}

export default App;

 

4. Link를 이용해서 다른 페이지로 이동하기

- link태그는 화면에서 a태그로 생성됩니다.

- to 속성에는 연결할 요청주소를 적습니다.

- 어느 컴포넌트든 사용할 수 있습니다.

 

* /로 연결되는 Home

//.....
return (
	<ul>
		<li><Link to='/user'>회원페이지</Link></li>
	</ul>
)

 

5. URL 파라미터 or 쿼리 스트링

- 같은 컴포넌트라도 전달되는 데이터에 따라서 다른 내용을 보여줘야 하는 경우가 있습니다.

ex) 글 상세-이 때 URL주소의 매개변수를 전달 할 수 있고, 컴포넌트는 그 값을 받아서 사용 할 수 있습니다.

 

1) 쿼리 스트링

- 쿼리스트링은 주소의 ? 뒤에 키=값 의 형태로 넘어가는 매개값입니다.

-별도의 라우터 설정은 하지 않아도 됩니다.

- 컴포넌트에서는 useLocation() 훅 or useSearchParams() 훅을 사용 해서 쿼리스트링을 받을 수 있습니다.

/경로?키=값&키=값

 

* 링크

//.....
return (
	<ul>
		<li><Link to='/user?id=aa123&key=1'>회원페이지(쿼리파람)</Link></li>
	</ul>
)

* 컴포넌트

- useLocation() 훅

const location = useLocation();

 

- useSearchParams() 훅 

  • 배열을 반환합니다.
  • 첫 번째 요소는 쿼리파라미터를 조회하거나 수정하는 get, set이 담긴 객체
  • 두 번째 요소는 쿼리파라미터를 객체로 업데이트 하는 함수 반환
const [객체, function] = useSearchParams()
const [obj, setObj] = useSearchParams();
    let id = obj.get("id"); //request.getParameter("키")
    let key = obj.get("key");

 

6. URL 파라미터

  • URL파라미터는 주소 뒤에 /경로/값/값 의 형태로 넘어가는 매개값입니다.
  • 라우터에 추가적인 설정이 필요합니다. ( /주소:키 )
  • 컴포넌트에서는 useParams() 훅을 사용하여 URL파라미터를 받을 수 있습니다.

 

* 라우터

function App() {

  return (
    <Routes>
        .....
        <Route path='/info:num' element={<Info/>}/>
    </Routes>
  );
}

export default App;

 

* 링크

//.....
return (
	<ul>
		<li><Link to='/info/1'>1번 info</Link></li>
		<li><Link to='/info/2'>2번 info</Link></li>
		<li><Link to='/info/3'>3번 info</Link></li>
	</ul>
)

 

* 컴포넌트: useParams() 훅, /param에는 link로 넘어온 키가 담겨 있습니다.

let param = useParams();

 

* app.js,  index.js, Home.js, User.js, Info.js

import { Route, Routes } from "react-router-dom";
import Home from "./component/Home";
import User from "./component/User";
import Info from "./component/Info";

function App() {

  /* 
  라우터 적용 방법

  1. index.js에서 브라우저 라우터로 app을 감싸 줍니다.
  2. 각각의 컴포넌트를 만듭니다.
  3. Route를 이용해서 주소별로 컴포넌트 연결합니다.

  Link컴포넌트 - a태그를 대체 합니다.
  - to속성에 "라우터 주소" 적어서 이동하면 됩니다.

  쿼리스트링 ?키=값
  - useLocation()
  - useSearchParams()

  URL 파라미터
  - 라우터를 설정 /경로/:값
  - useParams() 값을 받습니다.

  */

  return (
    <Routes>
      <Route path="/" element={<Home/>} />
      <Route path="/user" element={<User/>} /> {/* 쿼리 스트링 */}
      <Route path="/info/:num" element={<Info/>} /> {/* URL 파라미터 */}
    </Routes>
  )
}

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

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(


  <BrowserRouter>
    <App />
  </BrowserRouter>


);

// 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();
import {Link} from "react-router-dom"

const Home = () => {

    return (
        <div>
        <h3>홈 화면...</h3>

        <ul>
            <li><Link to="/user">회원 페이지</Link></li>
            <li><Link to="/user?id=aaa123&age=1">회원 페이지</Link></li>
            
            <li><Link to="/info/1">info 페이지</Link></li>
            <li><Link to="/info/2">info 페이지</Link></li>
            <li><Link to="/info/3">info 페이지</Link></li>

        </ul>

        </div>
    )
}

export default Home;
import { useLocation, useSearchParams } from "react-router-dom";

const User = () => {

    //쿼리 스트링으로 넘어오는 값을 받기
    //1 - useLocation 훅
    // const location = useLocation();
    // console.log(location); //객체 안에 쿼리스트링 값은 분해해서 사용

    //2. - useSearchParams 훅
    //배열을 반환 [값을 조회하거나 수정하는 get, set, 쿼리스트링을 업데이트 하는 함수]
    const [obj, setObj] = useSearchParams();
    // console.log(obj);
    // console.log(setObj);

    //home.js에 <li><Link to="/user?id=aaa123&age=1">
    let id = obj.get("id"); //request.getParameter("id")
    let age = obj.get("age");
    // console.log(id, age);

    const handleClick = () => {
        let num = parseInt(age) + 1; //age값에 1 더함
        setObj({id: "변경", age: num});
    }

    return (
        <div>
            <h3>user페이지...</h3>
            쿼리 스트링으로 넘어온 id: {id}<br />
            쿼리 스트링으로 넘어온 age: {age}<br />

            <button onClick={handleClick}>쿼리 스트링 강제 수정</button>
        </div>
    )
}

export default User;
import { useParams } from "react-router-dom";

const Info = () => {

    //예시 데이터
    const data = {
        1: {name: "홍길동", subject: "리액트 라우터"},
        2: {name: "이순신", subject: "리액트 props"},
        3: {name: "coding404", subject: "리액트 state"},
    }

    //useParams()
    let param = useParams();
    // console.log(param); //URL 주소의 값을 키로 받아줍니다.
    // console.log(param.num); //1번 페이지는 1, 2번 페이지는 2.. url 주소에 실린 값

    //구조 분해 할당
    const {name, subject} = data[param.num];
    // console.log(obj);

    return (
        <div>
        <h3>정보 화면...</h3>
        {name}님의 데이터 {subject}
        
        </div>
    )
}

export default Info;

 

7. 중첩 라우터로 공통 부분 처리하기

  • 글페이지가 있고, 글에 따른 상세화면이 있다고 가정해 봅시다.
  • 라우터의 설정은 아래 처럼 보여질 수 있습니다.
  • 아래 설정은 두 라우터가 다르기 때문에 각각 다른 화면이 보입니다.
<Routes>
	<Route path='/board' element={<Board/>}/>
	<Route path='/board/:num' element={<BoardContent/>}/>
</Routes>
  • 만약 Board 목록 페이지를 공통으로 사용하고 상세페이지를 서브로 보여주도록 처리하려면 중첩라우터를 활용할 수 있습니다.
  • 중첩라우터로 적용되면 부모컴포넌트에서 <Outlet> 컴포넌트를 활용해서 하위 라우터를 보여지게 할 수 있습니다.
<Routes>
	<Route path='/board' element={<Board/>}>
		<Route path=':num' element={<BoardContent/>}/>
	</Route>
</Routes>

 

* board

const Board = () => {

    return (
        <div>
            <h3>게시글목록</h3>
            <ul>
                <li><Link to='/board/1' >글1</Link></li>
                <li><Link to='/board/2' >글2</Link></li>
                <li><Link to='/board/3' >글3</Link></li> 
             </ul>

            {/* Oulet컴포넌트가 사용된 자리에 중첩된 내용이 보여지게됩니다 */}
            <Outlet/>
        </div>
    )
}

export default Board;

 

* boardContent

const BoardContent = () => {

    let {num} = useParams()

    return (
        <div>
            <h3>글 상세페이지</h3>
            {num}번 글입니다      
        </div>
    )
}

export default BoardContent;

1번 누르면 위는 그대로 아래만 변경됨

 

* app.js, Board.js, BoardContent.js, Home.js, Header.js, Header.module.css

import { Route, Routes } from "react-router-dom";
import Home from "./component/Home";
import User from "./component/User";
import Info from "./component/Info";
import BoardContent from "./component/BoardContent";
import Board from "./component/Board";
import Header from "./layout/Header";

function App() {

  /* 
  라우터 적용 방법

  1. index.js에서 브라우저 라우터로 app을 감싸 줍니다.
  2. 각각의 컴포넌트를 만듭니다.
  3. Route를 이용해서 주소별로 컴포넌트 연결합니다.

  Link컴포넌트 - a태그를 대체 합니다.
  - to속성에 "라우터 주소" 적어서 이동하면 됩니다.

  쿼리스트링 ?키=값
  - useLocation()
  - useSearchParams()

  URL 파라미터
  - 라우터를 설정 /경로/:값
  - useParams() 값을 받습니다.

  */

  return (

    <Routes>

      {/* 중첩 라우터 - 헤더 부분 처리 */}
      <Route element={<Header/>}>
      <Route path="/" element={<Home/>}/>
      <Route path="/user" element={<User/>} /> {/* 쿼리 스트링 */}
      <Route path="/info/:num" element={<Info/>} /> {/* URL 파라미터 */}
      </Route>

      {/* 각각 다른 화면이 보입니다. */}
      {/*  
      <Route path="/board" element={<Board/>}/>
      <Route path="/board/:num" element={<BoardContent/>} /> 
      */}

      {/* 중첩 라우터 - 공통 부분 처리(board에 가서 outlet 컴포넌트 표기) */}
      <Route path="/board" element={<Board />}>
        <Route path=":num" element={<BoardContent />} />
      </Route>

    </Routes>
  )
}

export default App;
import { Link, Outlet } from "react-router-dom";


const Board = () => {

    return (
        <div>
            <h3>글 목록 페이지</h3>

            <ul>
                <li><Link to="/board/1">글1</Link></li>
                <li><Link to="/board/2">글2</Link></li>
                <li><Link to="/board/3">글3</Link></li>
            </ul>

            {/* 하위 라우터가 표기 됩니다. */}
            <Outlet/>

            
        </div>
    )

}

export default Board;
import { useParams } from "react-router-dom";


const BoardContent = () => {

    let {num} = useParams();


    return(

        <div>
            <h6>글 상세 페이지..</h6>
            {num}번 글 내용!
        </div>
    )
}

export default BoardContent;
import { Link } from "react-router-dom"

const Home = () => {

    return (
        <div>
            <h3>홈 화면...</h3>

            <ul>
                <li><Link to="/user">회원 페이지</Link></li>
                <li><Link to="/user?id=aaa123&age=1">회원 페이지</Link></li>

                <li><Link to="/info/1">info 페이지</Link></li>
                <li><Link to="/info/2">info 페이지</Link></li>
                <li><Link to="/info/3">info 페이지</Link></li>

                {/*  */}
                <li><Link to="/board">board 페이지</Link></li>
            </ul>
        </div>
    )
}

export default Home;
import { Fragment } from "react";
import { Outlet } from "react-router-dom";

import styled from './Header.module.css';


const Header = () => {

    return (

        <Fragment>
            <header className= {styled.wrap}>
                <h3>헤더 파일</h3>
                <ul className={styled.wrap_list}>
                    <li>목록</li>
                    <li>목록</li>
                    <li>목록</li>
                </ul>
                <div>
                    <button>버튼</button>
                    <button>버튼</button>
                </div>
            </header>

            <section>
                {/* 헤더 하위의 라우터 표현 */}
                <Outlet/>
            </section>
        </Fragment>

    )

}

export default Header;

* {padding: 0; margin: 0;}

.wrap {
    background-color: black;
    display: flex;
    justify-content: space-between; /* 수평 정렬 */
    align-items: center; /* 한줄일 때 수직 정렬 */
    color: #fff;
    padding: 20px;
}

.wrap .wrap_list {
    display: flex;
    justify-content: space-around;
    flex-basis: 300px;
}

 

8. 라우터의 부가적인 기능

1) NavLink 컴포넌트

  • NavLink는 링크의 경로가 라우터의 경로와 일치하면 특정스타일을 적용해 줍니다.
  • NavLink는 style속성을 제공하합니다
  • style속성에는 실행시킬 함수를 작성합니다.
  • 이 함수에 매개변수로 {isActive : boolean} 객체를 넣어 주는데, 활성화 여부를 표시가 가능합니다.
  • 사용은 반드시 {isActive}변수로 구조분해할당 합니다.

* board.js

import { Link, NavLink, Outlet } from "react-router-dom";


const Board = () => {

    const myStyle = {color: "red", backgroundColor: "yellow"}

    return (
        <div>
            <h3>글 목록 페이지</h3>

            <ul>
               {/*  <li><Link to="/board/1">글1</Link></li>
                <li><Link to="/board/2">글2</Link></li>
                <li><Link to="/board/3">글3</Link></li> */}

                {/* 
                NavLinks는 {isActive: boolean}을 함수의 매개변수로 사용할 수 있게 전달해줍니다.
                반드시 {isActive} 변수로 구조분해 할당 해야 합니다.
                */}

                <li><NavLink to="/board/1" style={ ({isActive}) => isActive ? myStyle : null }>글1</NavLink></li>   
                <li><NavLink to="/board/2" style={ ({isActive}) => isActive ? myStyle : null }>글2</NavLink></li>   
                <li><NavLink to="/board/3" style={ ({isActive}) => isActive ? myStyle : null }>글3</NavLink></li>   

            </ul>

            {/* 하위 라우터가 표기 됩니다. */}
            <Outlet/>

            
        </div>
    )

}

export default Board;

누를 때마다 style 속성이 적용됨

 

2) useNavigate() 훅 vs Navigate 컴포넌트 (비교해서 구분)

 

- useNavigate() 훅

  • useNavigate훅 은 특정 event가 발생할 때,  url을 조작할 수 있는 함수를 제공합니다. (이벤트에서 사용)
  • react v6 에서 useHistory 가 변화한 것입니다. (JS의 history객체를 대신 합니다)

똑같은 건데 버전이 up된 것

let navigator = useNavigate();

* Header

import { Fragment } from "react";
import { Outlet, useNavigate } from "react-router-dom";

import styled from './Header.module.css';


const Header = () => {

    //useNavigate 훅 -함수로 반환
    let nav = useNavigate();

    const goHome = () => {
        nav('/'); //경로
    }

    const goBack = () => {
        nav(-1); //뒤로 가기, +1은 앞으로 가기

    }

    return (

        <Fragment>
            <header className= {styled.wrap}>
                <h3>헤더 파일</h3>
                <ul className={styled.wrap_list}>
                    <li>목록</li>
                    <li>목록</li>
                    <li>목록</li>
                </ul>
                <div>
                    <button onClick={goHome}>홈 화면</button>
                    <button onClick={goBack}>뒤로 가기</button>
                </div>
            </header>

            <section>
                {/* 헤더 하위의 라우터 표현 */}
                <Outlet/>
            </section>
        </Fragment>

    )

}

export default Header;

 

- Navigate 컴포넌트 (특정 화면에 권한이 없으면 들어가지 못하도록)

  • <Navigate> 컴포넌트는 렌더링 될 때 현재 위치를 변경합니다.
  • useNavigate()훅과 비슷해보이지만 useNavigate()훅은 렌더링 과정에 사용할 수 없습니다.
  • 리다이렉트 기능

* App.js, Home.js, MyPage.js

import { Route, Routes } from "react-router-dom";
import Home from "./component/Home";
import User from "./component/User";
import Info from "./component/Info";
import BoardContent from "./component/BoardContent";
import Board from "./component/Board";
import Header from "./layout/Header";
import MyPage from "./component/MyPage";

function App() {

  /* 
  라우터 적용 방법

  1. index.js에서 브라우저 라우터로 app을 감싸 줍니다.
  2. 각각의 컴포넌트를 만듭니다.
  3. Route를 이용해서 주소별로 컴포넌트 연결합니다.

  Link컴포넌트 - a태그를 대체 합니다.
  - to속성에 "라우터 주소" 적어서 이동하면 됩니다.

  쿼리스트링 ?키=값
  - useLocation()
  - useSearchParams()

  URL 파라미터
  - 라우터를 설정 /경로/:값
  - useParams() 값을 받습니다.

  */

  return (

    <Routes>

      {/* 중첩 라우터 - 헤더 부분 처리(Header에서 Outlet 컴포넌트 표기) */}
      <Route element={<Header/>}>
      <Route path="/" element={<Home/>}/>
      <Route path="/user" element={<User/>} /> {/* 쿼리 스트링 */}
      <Route path="/info/:num" element={<Info/>} /> {/* URL 파라미터 */}
      </Route>

      {/* 각각 다른 화면이 보입니다. */}
      {/*  
      <Route path="/board" element={<Board/>}/>
      <Route path="/board/:num" element={<BoardContent/>} /> 
      */}

      {/* 중첩 라우터 - 공통 부분 처리(board에 가서 outlet 컴포넌트 표기) */}
      <Route path="/board" element={<Board />}>
        <Route path=":num" element={<BoardContent />} />
      </Route>

      {/* navgate 컴포넌트 */}
      <Route path="/mypage" element={<MyPage/>}/>


    </Routes>
  )
}

export default App;
import { Link } from "react-router-dom"

const Home = () => {

    return (
        <div>
            <h3>홈 화면...</h3>

            <ul>
                <li><Link to="/user">회원 페이지</Link></li>
                <li><Link to="/user?id=aaa123&age=1">회원 페이지</Link></li>

                <li><Link to="/info/1">info 페이지</Link></li>
                <li><Link to="/info/2">info 페이지</Link></li>
                <li><Link to="/info/3">info 페이지</Link></li>

                {/* 중첩 라우터 */}
                <li><Link to="/board">board 페이지</Link></li>

                {/* navigate 컴포넌트  */}
                <li><Link to ="/mypage">마이 페이지</Link></li>

            </ul>
        </div>
    )
}

export default Home;
import { Navigate, useNavigate } from "react-router-dom";

const MyPage = () => {

    /* 
    첫번째 렌더링 과정에서는 사용할 수 없음
    let nav = useNavigate();

    let loginYN = false; //로그인 여부
    if(!loginYN === false) {
        nav('/');
    }
    */

    //권한이 없으면 리다이렉트
    //replace={true}를 설정하면 기록을 남기지 않습니다.
    let loginYN = false;
    if(loginYN === false) {
        return <Navigate to="/" replace={true} />
    }

    return (
        <div>
            권한 있는 사람만 접근이 가능함.
        </div>
    )
}

export default MyPage;

 

- mypage 누르면 튕겨져 나옴

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

day72-react  (0) 2023.01.25
day71-react  (0) 2023.01.20
day69-react  (0) 2023.01.18
day68-react  (0) 2023.01.18
day67-react  (0) 2023.01.17
profile

mjeongriver

@mjeongriver

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그