이번에 Vite를 이용하여 React + Typescript 프로젝트를 처음 생성해 봤다.
yarn create vite [프로젝트 명] --template react-ts
npm create vite@latest [프로젝트 명] --template react-ts
이번 원티드 7월 챌린지에 참여하면서 수업 실습 프로젝트가 Vite 라는 생소한걸 처음 써보게 되었다.
간단하게 프로젝트를 크게 다뤄본건 아니고 간단한 과제를 하면서 느낀점으론 작업하면서 Vite 가 CRA에 비해 정말 빨랐다. 정말 궁금해서 간단하게 검색하면서 알아본 결론은
과제를 진행하는데 있어서 최근에 계속 Next.js 를 쓰다보니 과제에 대한 글을 제대로 읽지 않고 , 뭐야 그냥 Router 를 통해서 Page 이동을 하면되는거 잖아 하면서 쉽게 생각 했었다.
하지만 , React usenavigate() 을 사용하는게 아닌 History API 를 이용해 SPA Router 기능 구현이 목적인것이다.
먼저 main.tsx 코드 수정이 필요하다.
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
index.html 파일을 열어봤을때 태그 안에
초기 페이지 / , /about 페이지 2개의 페이지를 만들어야하는데
현재 main.tsx 파일은 App 구성 요소만 렌더링을 하고있다. URL 경로 기반으로 App , About 구성 요소로 전환 할수 있게 수정을 해야한다.
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom/client";
// import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import App from "./App";
import About from "./About";
import "./index.css";
const Router = () => {
const [route, setRoute] = useState(window.location.pathname);
useEffect(() => {
const handlePopstate = () => {
setRoute(window.location.pathname);
};
window.addEventListener("popstate", handlePopstate);
return () => {
window.removeEventListener("popstate", handlePopstate);
};
}, []);
switch (route) {
case "/about":
return <About />;
default:
return <App />;
}
};
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<Router />
</React.StrictMode>
);
Router 구성 요소를 추가했다. useState Hook 을 사용하여 현재 경로를 보유하고 , useEffect Hook 을 사용하여 handlePopstate 함수는 popstate 이벤트가 발생할 때 마다 실행됩니다. 새 경로로 route 상태를 업데이트 합니다.
window.addEventListener("popstate" , handlePopstate) popstate 이벤트 리스너는 window 개체에 추가 됩니다. handlePopstate 는 이벤트가 실행될때마다 실행됩니다.
정리 함수 return 에서는 window.removeEventListener("popstate" , handlePopstate)
는 Router 구성요소가 DOM에서 마운트 해제되거나 다시 렌더링 되기 전에 이벤트 리스너를 제거하기 위해 실행됩니다. 이 잠재적인 메모리 누수를 방지하는데 있어서 중요합니다!
switch (route) {
case "/about":
return <About />;
default:
return <App />;
}
여기서 switch 문은 현재 경로를 기반으로 렌더링 할 구성 요소를 결정하는 데 사용됩니다. 경로가 "/about" 이면 About 컴포넌트를 렌더링 하고 있습니다. 다른 경로 같은 경우 현재는 2개의 페이지만 표현하기에 "/" App 컴포넌트가 렌더링이 됩니다.
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";
function App() {
const navigateTo = (url: string) => {
window.history.pushState(null, "", url);
const popStateEvent = new PopStateEvent("popstate");
window.dispatchEvent(popStateEvent);
};
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React Main Home</h1>
<p>Root</p>
<button onClick={() => navigateTo("/about")}>About Page Click</button>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
);
}
export default App;
const navigateTo = (url: string) => {
window.history.pushState(null, "", url);
const popStateEvent = new PopStateEvent("popstate");
window.dispatchEvent(popStateEvent);
};
과제 목적이 historyAPI 를 사용하는게 목적이기에 위와 같은 함수를 만들었다.
window.history.pushState(null, "" ,url);
history 개체에 pushState 메서드를 사용하여 브라우저 기록에 새 항목을 추가합니다. 이는 새 URL 이 주소 표시줄에 표시되고 사용자가 브라우저의 뒤로 버튼을 사용하여 이전 URL 로 돌아갈 수 있음을 의미합니다. 그러나 이것은 페이지 로드를 트리거 하지않습니다.
const popStateEvent = new PopStateEvent("popstate");
새로운 PopStateEvent 를 생성합니다. 이 유형의 이벤트는 일반적으로 사용자가 기록을 사용하여 다른 URL 로 이동할 때 브라우저에 의해 트리거 되지만 여기서는 수동으로 생성합니다.
window.dispatchEvent(popStateEvent)
이벤트를 전달합니다. 즉 , 설정된 모든 popstate 이벤트 핸들러(Router 구성 요소의 main.tsx useEffect Hook 에 있는 popstate 이벤트 핸들러) 가 호출됩니다.
위와 같은 함수가 button 을 클릭시 실행이 되면 route 상태를 업데이트하여 Router 구성 요소가 다시 렌더링되고 새 URL에 대한 올바른 구성 요소를 표시하도록 합니다.
화면을 녹화하고 보니 , url 부분이 짤려있고 페이지만 녹화되어져 있어서 아쉽지만
about 페이지에서도 함수는 동일하며 , url 위치 경로만 "/" 한다면 위와같은
결과물을 볼수있습니다 .