페이지 이동을 할 때, 새로 로딩을 하지 않고 이동해보자

Jeong·2023년 8월 11일
0

React Router

목록 보기
4/4
post-thumbnail

키워드

  • Web APIs - History
  • React Router - NavLink, Link, Navigate, useNavigate

최종 목표

React에서 라우팅 처리에 대해 배워보자.

현재 목표

페이지 이동을 할 때, 새로 로딩을 하지 않고 이동해보자.

현재 문제는?

F12로 네트워크 탭을 켜보자. 페이지가 이동할 때마다 새로 로딩을 하고 있다.

어떻게 새로 로딩을 하지 않고 페이지를 이동할 수 있을까?

방법1: # 혹은 #! 으로 URL을 변경하자

주소 앞에 # 혹은 #! 을 붙이면 새로 로딩하지 않고 이동할 수 있다.

<li>
	<a href='/#/'>Home</a>
</li>
<li>
	<a href='/#/about'>About</a>
</li>

이 방식은 history.pushState 도입 전에 방식이다.

지금도 Gmail은 이 방식을 쓰고 있다. 예를 들어, https://mail.google.com/mail/u/0/#inbox

# 는 해시라고 하며, #! 는 해시뱅이라고 한다.

방법2: history.pushState를 사용하자

실제로 이동은 하지 않고 눈 앞에 보이는 주소만 바꿔보자.

이동을 하지 않으니 내용은 바뀌지 않는다.

STEP1: 이벤트를 막자

preventDefault 을 이용해서 이벤트를 막는다. 그러면 이동하는 이벤트도 막힌다. 클릭해도 아무 일도 일어나지 않게 된다.

const handleClick = (event: SyntheticEvent) => {
		event.preventDefault();
};

STEP2: 강제로 이동시키자

이 상태에서 강제로 이동해보자. history.pushState 쓰면 된다.

history.pushState 는 3개의 값을 받게 되어 있다.

  • state
    • 이동하면서 저장하고 싶은 현재 값이다.
    • 아무 객체나 넣을 수 있다.
  • title
  • url
const handleClick = (event: SyntheticEvent) => {
		event.preventDefault();
		const state = {};
		const title =  '';
		const url = '/about';
		history.pushState(state, title, url);
	};

클릭했을 때 실제로 이동은 하지 않고 눈 앞에 보이는 주소만 바뀌게 된다.

이게 안되던 시절에 네이버 블로그는 현재 글에서 다른 글로 이동할 때 주소가 바뀌지 않았다. 지금은 웹 브라우저가 history.pushState 같은 걸 제공해주니까 주소 변경이 가능한 것이다.

history.pushState 는 HTML5부터는 제공되는 전역 객체의 함수이다.
그래서 테스트 코드에서는 돌아가지 않는다.

전체 코드

const handleClick = (event: SyntheticEvent) => {
		event.preventDefault();
		const state = {};
		const title = '';
		const url = '/about';
		history.pushState(state, title, url);
	};

	return (
		<header>
			<nav>
				<ul>
					<li>
						<a href='/' onClick={handleClick}>Home</a>
					</li>
					<li>
						<a href='/about' onClick={handleClick}>About</a>
					</li>

				</ul>
			</nav>
		</header>
	);

방법3: React Router에서 제공하는 것을 사용하자

history.pushState 를 썼을 때는 주소는 바꿀 수 있었다. 내용은 바꿀 수 없었다.

React Router에서 제공하는 것을 사용해보자. 페이지 이동할 때 주소와 내용을 모두 바꿀 수 있다.

내용이 바꿀 수 있는 이유는 주소가 바뀌면 React Router 한테 "지금 주소 바뀌었으니까 확인해봐" 라고 하기 때문이다.

Link란?

위에서 onClick 으로 잡았던 게 이제 필요없다. 알아서 처리해준다.

마찬가지로 페이지 이동을 해도 새로 페이지를 불어오지 않고 이동한다.

return (
  <header>
    <nav>
      <ul>
        <li>
          <Link to='/' >Home</Link>
        </li>
        <li>
          <Link to='/about' >About</Link>
        </li>

      </ul>
    </nav>
  </header>
);

Link 와 다른 NavLink 의 특징은 특정 페이지를 가면 그 페이지가 현재 위치라는 것을 알 수 있다.

예를 들어 현재 /about 주소에 있다면 /about 를 잡고 있는 링크 태그의 class에 active 가 잡히게 된다.

현재 페이지에 해당 하는 링크 태그에 css를 주고 싶을 때 유용하겠다.

Navigate란?

Navigate 는 무조건 리다이렉션을 해준다.

예를 들어 /Logout 주소로 가면 / 으로 리다이렉션 하게 할 수 있다.

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

export default function LogoutPage() {
	return (
		<div>
            ... 로그아웃 처리 ...

			<Navigate to='/' /> // 추가
		</div>
	);
}
export default function Header() {
	return (
		<header>
			<nav>
				<ul>
					<li>
						<NavLink to='/' >Home</NavLink>
					</li>
					<li>
						<NavLink to='/about' >About</NavLink>
					</li>
					<li>
						<NavLink to='/logout' >Log out</NavLink> // 추가
					</li>
				</ul>
			</nav>
		</header>
	);
}
const routes = [
	{
		element: <Layout />,
		children: [
			{path: '/', element: <HomePage />},
			{path: '/about', element: <AboutPage />},
			{path: '/logout', element: <LogoutPage />}, // 추가
		],
	},
];

Navigate를 테스트 하면 터진다

이렇게 테스트를 하면 테스트가 터진다.

context('when the current path is “/about”', () => {
  it('redirects to the home page', () => {
    renderRouter('/logout');

    screen.getByText(/Hello, World/);
  });
});

return new Request(url, init);
^
ReferenceError: Request is not defined

whatwg-fetch로 테스트가 터지는 것을 막자

테스트에서 ReferenceError: Request is not defined 에러가 나면 whatwg-fetch 를 임포트 해서 해결하면 된다.

whatwg-fetch 설치

npm i -D whatwg-fetch

jest.config.js 파일 수정

'<rootDir>/src/setupTests.ts', 를 추가한다.

module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: [
    '@testing-library/jest-dom/extend-expect',
    '<rootDir>/src/setupTests.ts',
  ],
  transform: {
    '^.+\\.(t|j)sx?$': ['@swc/jest', {
      jsc: {
        parser: {
          syntax: 'typescript',
          jsx: true,
          decorators: true,
        },
        transform: {
          react: {
            runtime: 'automatic',
          },
        },
      },
    }],
  },
  testPathIgnorePatterns: [
    '<rootDir>/node_modules/',
    '<rootDir>/dist/',
  ],
};

src/setupTests.ts 추가 및 작성

import 'whatwg-fetch';

개발용으로 설치해서 lint 에러가 날 수 있다. 괜찮다.

whatwg-fetch로 해결할 수 있는 이유는?

유승완님께서 주신 답변이다.

테스트가 터질때 어디서 터지는지 에러 메시지를 보면 아래 코드에서 터지는 걸 확인할 수 있는데요.
Request라는 생성자 함수가 존재하지 않아서 그런걸로 추측을 할 수 있을 것 같아요.
return new Request(url, init);

// ReferenceError: Request is not defined

그런데 whatwg-fetch를 import했을 때 에러를 해결된다는 것은, whatwg-fetch라는 패키지가 Request라는 생성자 함수를 갖고 있을거라고 추측 을 할 수 있을 것 같아요. 실제로 MDN에서 Fetch API를 확인해보시면 Request라는 인터페이스를 확인할 수 있어요.
Request - Web API | MDN

따라서 whatwg-fetch를 import 해주면 내부에는 Request에 대한 구현체가 있기 때문에 에러를 해결한다고 볼 수 있을 것 같아요.

whatwg-fetch Github에서 코드를 보면 Request에 대한 구현체를 확인할 수 있어요.
fetch/fetch.js at master · JakeChampion/fetch

whatwg-fetch이란?

whatwg-fetch 는 Fetch API의 폴리필(polyfill)이다.

Fetch API를 지원하지 않는 구형 브라우저에서도 이를 사용할 수 있게 해주는 도구이다.

폴리필(polyfill)이란?

새로운 브라우저 기능이나 API를 사용하려는데, 현재 실행 중인 브라우저에서 해당 기능을 지원하지 않을 때가 있다.

이때 이를 대처하여 사용할 수 있게 해주는 코드 조각이다.

폴리필은 구형 브라우저나 환경에서도 최신 기능을 사용할 수 있게 해준다.

useNavigate란?

useNavigate 를 가장 많이 쓴다. useNavigate 는 훅이다.

내가 직접 컨트롤 할 수 있다.

import {NavLink, Navigate, useNavigate} from 'react-router-dom';

export default function Header() {
	const navigate = useNavigate();
  
	const handleClickLogout = () => {
		navigate('/');
	};

	return (
		<header>
			<nav>
				<ul>
					<li>
						<NavLink to='/' >Home</NavLink>
					</li>
					<li>
						<NavLink to='/about' >About</NavLink>
					</li>
					<li>
						<button type='button' onClick={handleClickLogout}>
							Log out
						</button>
					</li>
				</ul>
			</nav>
		</header>
	);
}

/logout 주소를 들리지 않도록 button으로 다시 만들었다.

아하! 포인트

이번 시간에는 주소 이동을 할 때 새로 불러오지 않는 방법에 대해 배웠다.

LinkNavLink 는 css를 어떻게 적용할지에 따라서 결정하면 될 듯 싶다.

리다이렉션에 대한 간단한 처리라면 Navigate 로, 로직이 있다면 useNavigate 쓸 것 같다.

Navigate 가 테스트를 통과하지 못하는 이유가 궁금했는데, 테스트 코드의 오류를 자세히 보면 금방 추측할 수 있다는 것을 깨달았다. Chat GPT에 너무 의존해서 이 친구가 답을 못 내니, 나도 아무것도 못하는 상태가 됐던 게 아니었나 반성한다.

profile
성장중입니다 🔥 / 나무위키처럼 끊임없이 글이 수정됩니다!

0개의 댓글