React SPA & React Router

1Hoit·2023년 1월 30일
0

React 기초

목록 보기
4/12

SPA(Single Page Application)

한개의 페이지로 이루어진 어플리케이션,

서버로부터 완전한 새로운 페이지를 불러오지 않고 페이지 갱신에 필요한 데이터만 받아 현재의 페이지를 업데이트하는 어플리케이션.

SPA 장단점

  • 장점
    1. 필요한 부분만 업데이트 하므로 사용자와의 interaction 이 빠르다.
    2. 서버에서는 요청 받은 데이터만 넘겨주므로 서버 과부하 문제가 줄어든다.
    3. 전체 페이지 렌더링이 아니기에 더 나은 UX(유저경험) 제공
  • 단점
    1. 앱의 규모가 커지면 Javascript 파일의 크기가 크기 때문에 첫 화면 로딩 시간이 길어진다.
      (그 이유는 첫 화면 로딩 시 읽어들인 HTML 파일은 거의 비어있고, 대부분의 코드는 JavaScript 파일 안에 들어있다 보니 자연스럽게 JavaScript 파일이 무거워지고 때문에 이 JavaScript 파일을 기다리는 시간으로 인해 첫 화면의 로딩 시간이 길어진다.
      또한 사용자가 실제로 방문하지 않는 페이지의 스크립트도 불러오기 때문이다.)
      --> 이 문제는 추후에 코드 스플리팅을 사용하여 라우트별로 파일들을 나누어 해결 할 것임.

      1. 검색 엔진 최적화(SEO)가 좋지 않습니다. 구글이나 네이버 같은 검색엔진이 자료를 수집하기 좋도록 웹 페이지를 구성하는 것을 뜻함.
        검색 엔진의 작동 방식은 검색 로봇이 웹 페이지에 있는 정보를 수집하고 분석해서 그 결괏값에 인덱스를 만들어 보관하고 있다가 사용자가 검색어를 입력하면 보관하고 있던 인덱스에서 검색어와 가장 연관성이 높은 웹 페이지들을 순서대로 보여주는 방식으로 작동하기에 SPA의 경우 HTML파일은 별다른 자료가 없기 때문에 검색엔진이 적절히 작동하지 않는다.
        --> 이 문제는 서버사이드 렌더링 (SSR)로 해결할 수 있다.

React Router

SPA는 하나의 페이지를 가지고 있지만 사실 한 종류의 화면만 사용하지 않는다.

라우팅(Routing) : 다른 주소에 따라 다른 뷰를 보여주는 과정을 경로에 따라 변경한다.

React SPA에서는 라우팅을 위해 React Router라는 라이브러리를 가장 많이 사용한다.

React Router의 주요 컴포넌트

import { BrowserRouter, Routes, Route, Link, NavLink } from "react-router-dom";
위와 같이 불어와서 사용할 수 있다.

  1. BrowserRouter : 라우터 역할

  2. Routes 와 Route : 경로를 매칭

  3. Link : 경로를 변경하는 역할

    • css 가상선택자로 스타일을 적용할 수 없음
  4. NavLink : 공식문서에선 NavLink 컴포넌트는 Link의 special version 이라고 명시 되어 있다.
    특정 링크에 스타일을 넣어 줄 수 있다.

    • 자체적으로 isActive라는 boolean값을 가지고 있다.

    • NavLink 태그안에 isActive를 선언하여 활성화시키고 싶은 스타일에 css를 적용 할 수 있다.
      (클릭/활성화 시 active class 변경됨)

    • NavLink 공식문서

      //예시
      const Category = styled(NavLink)`
       cursor: pointer;
       text-decoration: none;
       color: inherit;
       padding-bottom: 0.25rem;
       &:hover {
         color: #495057;
         display: flex;
       }
       &.active {
         font-weight: 600;
         border-bottom: 2px solid #22b8cf;
         color: #22b8cf;
         &:hover {
           color: #3bc9db;
         }
       }
      `;
      // ...
      const Categories = () => {
       return (
         <CategoriesBlock>
           {categories.map((c) => (
             <Category
               key={c.name}
               {/* 아래와 같이 특정 스타일을 줄 수 있다.  */}
               className={({ isActive }) => (isActive ? 'active' : '')}
               to={c.name === 'all' ? '/' : `/${c.name}`}
             >
               {c.text}
             </Category>
           ))}
         </CategoriesBlock>
       );
      };

리액트 라우터를 위한 URL 파라미터와 쿼리 스트링 사용하기

  1. URL 파라미터를 가져오기 위한 방법?

useParams

useParams란?
useParams는 리액트에서 제공하는 Hook으로 동적으로 라우팅을 생성하기 위해 사용한다.

  • URL에 포함되어있는 Key, Value 형식의 객체를 반환해주는 역할
  • Route 부분에서 Key를 지정해주고, 해당하는 Key에 적합한 Value를 넣어 URL을 변경시키면,
    useParams를 통해 Key, Value 객체를 반환받아 확인할 수 있다.
    반환받은 Value를 통해 게시글을 불러오거나, 검색목록을 변경시키는 등 다양한 기능으로 확장시켜 사용할 수 있다.

내가 사용해본 예제

//App.js
import './App.css';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import NewsPage from './pages/NewsPage';

const App = () => {
  return (
    <>
      <BrowserRouter>
        <Routes>
           {/* ?를 붙이면 category 값이 선택적이라는 의미다,
          있을수도 없을 수도 있다는 것이다. */}
          <Route path="/:category?" element={<NewsPage />}></Route>
        </Routes>
      </BrowserRouter>
    </>
  );
};
export default App;

// NewsPage.js
import React from 'react';
import Categories from '../components/Categories';
import NewsList from '../components/NewsList';
//import해오기
import { useParams } from 'react-router-dom';

const NewsPage = () => {
  //Route의 path와 이름을 맞춰줘야 한다.
  const { category } = useParams();
  return (
    <>
      <Categories />
      {category === undefined ? <NewsList category={'all'} /> : <NewsList category={category} />}
    </>
  );
};

export default NewsPage;

위와 같이 category를 params 로 받아와 아래와 같이 비동기적 처리를 할 수 있다.

// 받아온 category로 비동기 처리를 하는 경우
useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const query = category === 'all' ? '' : `&category=${category}`;
        const response = await axios.get(`https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=${API_KEY}`);
        setArticles(response.data.articles);
      } catch (e) {
        console.log(e);
      }
      setLoading(false);
    };
    fetchData();
  }, [category]);

간단한 예제도 살펴보면 이해가 쉽다.

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

function App() {
  return (
    <BrowserRouter> //ReactDOM의 렌더 단계인 index.js 에 <BrowserRouter>를 넣어서 사용가능
      <div>
        <nav>
          <ul>
            <li>
    		{/*<Link> 의 to 속성을 활용하여 <Route> 컴포넌트에 설정해 준 path 주소를 연결*/}
    {/*ReactDOM으로 렌더를 시키게 되면 <Link> 컴포넌트는 <a> 요소로 바뀐다.
     하지만 <a> 요소는 페이지를 전환하는 과정에서 페이지를 불러오기 때문에 다시 처음부터 렌더링을 시킨다. 
     즉, 새로고침 된다.
    <Link> 컴포넌트는 페이지 전환을 방지하는 기능이 내장되어 있기 때문에 SPA를 구현할 수 있다.*/}
              <Link to="/">Home</Link>{/* Link 컴포넌트를 이용하여 경로를 연결합니다 */}
            </li>
            <li>
              <Link to="/mypage">MyPage</Link>
            </li>
            <li>
              <Link to="/dashboard">Dashboard</Link>
            </li>
          </ul>
        </nav>
    {/*<Routes> 컴포넌트는 여러 <Route> 컴포넌트를 감싸서 그중 경로가 일치하는 단 하나의 라우터만 렌더링을 시켜주는 역할*/}
         <Routes>
		  {/* 경로는 path로 컴포넌트는 element로 연결해 줍니다. */}
	      {/*<Link> 컴포넌트가 정해주는 URL 경로와 일치하는 경우에만 작동*/}
          {/* path=”*” 사용시 지정되지 않은 주소로 접근할 시에 이 속성이 설정되어 있는 컴포넌트를 보여준다*/}
          <Route path="/" element={<Home />} /> 
          <Route path="/mypage" element={<MyPage />} /> 
          <Route path="/dashboard" element={<Dashboard />} />
        </Routes>
      </div>
    </BrowserRouter>
  );
}

function Home() {
  return <h1>Home</h1>;
}

function MyPage() {
  return <h1>MyPage</h1>;
}

function Dashboard() {
  return <h1>Dashboard</h1>;
}

export default App;

만약 index.js에서 <BrowserRouter>를 넣어서 활용시

//(React Version 17 )
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(<BrowserRouter><App/></BrowserRouter>, document.querySelector('#root'));
                
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

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

URL 파라미터와 쿼리
추가적으로 페이지 주소를 정의할 때 가끔 유동적인 값을 전달해야 한다.

  • 파라미터 : /profile/veloper
  • 쿼리 : /about?details=true

유동적인 값을 사용해야 하는 상황에서는 일반적으로
파라미터는 특정 아이디 또는 이름을 사용하여 조회할 때 사용하고,
쿼리는 어떤 키워드를 검색하거나 페이지에 필요한 옵션을 전달할 때 사용한다.


추가적인 기능들

useNavigate : 특정 이벤트 발생시 url을 조작할 수 있는 interface를 제공.

import { useNavigate } from 'react-router-dom';

function Func() {
  const navigate = useNavigate();

  const onClickImg = () => {
    navigate(`/comment/id/등등 내가 원하는 주소`);
  };

  return (
  	<img src={...} alt={...} onClick={onClickImg} />
  );
}

export default Func;

구조 : navigate('/about', {replace: true});
navigate는 첫번째 인자로는 url, 두번째 인자로는 {replace} 인수를 받는다.

  • replace() 값으로 true를 사용하면 navigate에 적힌 주소로 넘어간 후 뒤로가기를 하더라도 종전 페이지로 돌아오지 않고 메인 페이지(’/’) 로 이동한다.
  • default값은 false로 뒤로가기가 가능.
  • useNavigate는 react-router-dom V6에서 새로 생긴 함수로 기존의 useHistory기능을 전부 대체할 수 있다.
  • useHistory의 history는 객체, useNavigate의 navigate는 함수이다!
// v6
const navigate = useNavigate();
navigate('/home');
navigate('/home', {replace: true});
navigate(-1); // 뒤로가기
navigate(1); // 앞으로 가기

마무리..

추가적인 history 나 match 객체와 같은 내용을 담아 수정해야겠다.

profile
프론트엔드 개발자를 꿈꾸는 원호잇!

0개의 댓글