리액트를 공부하며 react-router를 사용하여 동물 관련 페이지를 만들었습니다. 전체 코드는 GitHub에 있습니다.
"페이지 이동"이라는 기능을 리액트에서 리액트 라우터를 사용하여 처리가 가능합니다.
<a>
태그를 사용하면 페이지가 전체 새로 로딩되며, 이로 인해 화면이 깜빡이게 됩니다. 이는 사용자 경험을 저해하는 중요한 요인 중 하나입니다. React Router는 이러한 문제를 해결하고 클라이언트 측 라우팅을 구현함으로써, 페이지 전환 시 전체를 다시 로드하지 않고 필요한 부분만 업데이트하여 부드러운 화면 전환을 제공합니다.
React Router는 SPA에서 매우 유용하게 활용됩니다. 페이지 간의 이동이 자주 일어나는 경우, 서버에 불필요한 요청을 보내지 않고, 애플리케이션 내에서 상태를 관리하며 필요한 컴포넌트만 렌더링하기 때문에 성능 개선에도 도움이 됩니다.
따라서, React Router는 단순한 링크 이동 이상의 기능을 제공하며, 동적 라우팅, URL 파라미터 처리, 중첩된 라우트, 비동기 데이터 로딩 등 다양한 기능을 통해 복잡한 SPA 구조를 간결하고 효율적으로 관리할 수 있게 도와줍니다.
npm install react-router-dom
설치가 완료되면 package.json에 react-router-dom
이 추가가 됩니다.
그럼 이제 동물 관련 페이지를 만들어보겠습니다. 이미지와 더미데이터를 미리 프로젝트 폴더에 준비해줍니다.
메인 페이지에서는 데이터에 있는 동물 목록을 화면에 렌더링하는 컴포넌트를 작성했습니다. data
배열을 map
함수를 사용하여 각 동물을 리스트 아이템으로 표시하며, 동물 항목을 클릭하면 해당 동물의 상세 페이지로 이동할 수 있도록 Link
를 활용합니다.
// src/page/Main.jsx
import { data } from '../assets/data/data';
import { Link } from 'react-router-dom';
const Main = () => {
return (
<div className="main-container">
<ul className="animal-list">
{data.map((el) => (
<li key={el.id} className="animal-item">
<Link to={`detail/${el.id}`} className="animal-link">
<img src={el.img} alt={el.name} className="animal-image" />
<div className="animal-name">{el.name}</div>
</Link>
</li>
))}
</ul>
</div>
);
};
export default Main;
data
배열에서 각 동물의 id
, img
, name
을 가져와 화면에 표시합니다.Link
를 사용하여 동물의 id
를 URL 경로에 포함시키고, 클릭 시 해당 상세 페이지로 이동합니다.각 동물 항목은 클릭시 해당 동물의 상세 페이지로 이동할 수 있도록 Link
를 사용합니다. App.jsx
에서 Main
컴포넌트를 루트로 설정합니다.
<Routes>
<Route path="/" element={<Main />} />
</Routes>
상세 정보 페이지는 각 동물 데이터의 고유한 id
를 URL 파라미터로 받아 해당 동물의 데이터를 조회합니다. 이때 useParams
훅을 사용하여 URL 파라미터에서 동물 id
를 추출하고, 이를 통해 동물의 데이터를 찾습니다.
<Routes>
<Route path="/" element={<Main />} />
<Route path="/detail/:id" element={<Detail />} />
</Routes>
Detail.jsx
을 생성하고 URL 파라미터를 사용하여 동물의 id
를 추출하고, 해당 id
에 맞는 동물 데이터를 찾아 화면에 표시합니다.
useParams
훅을 사용하여 URL 파라미터를 가져옵니다.
// src/page/Detail.jsx
import { useParams } from 'react-router-dom';
import { data } from '../assets/data/data';
const Detail = () => {
const params = useParams();
const animalData = data.find((el) => el.id === Number(params.id));
if (!animalData) {
return <div className="error">해당 동물을 찾을 수 없습니다.</div>;
}
return (
<section className="detail">
<img src={animalData.img} alt={animalData.name} className="detail-image" />
<h2 className="detail-name">{animalData.name}</h2>
<p className="detail-description">{animalData.description}</p>
</section>
);
};
export default Detail;
useParams
를 통해 URL에서 동물 id
를 가져옵니다.id
를 사용하여 data
배열에서 동물 데이터를 검색하고, 그 데이터를 화면에 표시합니다. URL을 보면 /detail/0
을 확인이 됩니다. 데이터에서 고양이의 id
가 0이기 때문에 고양이를 클릭하면 /detail/0
으로 이동하고 고양이의 정보가 보입니다.
사용자가 동물 이름을 입력하여 검색할 수 있는 기능을 추가하고, 이를 통해 검색 결과를 별도의 페이지에 표시합니다. 검색어는 쿼리 파라미터로 전달되며, 사용자가 검색 버튼을 클릭하면 /search
경로로 이동합니다.
// src/App.jsx
import React, { useState } from 'react';
import { Route, Routes, useNavigate } from 'react-router-dom';
import Main from './page/Main';
import Detail from './page/Detail';
import Search from './page/Search';
import './App.css';
const App = () => {
const [inputValue, setInputValue] = useState('');
const navigate = useNavigate();
const handleSearch = () => {
if (inputValue.trim() !== '') {
navigate(`/search?animal=${inputValue}`);
}
};
return (
<>
<header className="header">
<h1 className="title">동물은 귀엽고 항상 옳다</h1>
<div className="search-container">
<input
className="search-input"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="동물을 검색하세요..."
/>
<button className="search-button" onClick={handleSearch}>
Search
</button>
</div>
</header>
<main className="main-content">
<Routes>
<Route path="/" element={<Main />} />
<Route path="/detail/:id" element={<Detail />} />
<Route path="/search" element={<Search />} />
</Routes>
</main>
<footer className="footer">All rights reserved to MINJAE</footer>
</>
);
};
export default App;
Search.jsx
를 생성하고 쿼리 파라미터를 가져와 입력된 검색어에 맞는 동물을 필터링한 후 결과를 화면에 표시합니다.
검색어가 한국어이기 때문에 korean-regexp
를 사용하여 정확한 검색 결과를 얻도록 했습니다.
npm install korean-regexp
// src/page/Search.jsx
import { Link, useSearchParams } from 'react-router-dom';
import { data } from '../assets/data/data';
import { getRegExp } from 'korean-regexp';
const Search = () => {
const [searchParams] = useSearchParams();
const param = searchParams.get('animal') || '';
const reg = getRegExp(param);
const filteredData = data.filter((el) => el.name.match(reg));
return (
<div className="search-container">
{filteredData.length > 0 ? (
<ul className="animal-list">
{filteredData.map((el) => (
<li key={el.id} className="animal-item">
<Link to={`detail/${el.id}`} className="animal-link">
<img src={el.img} alt={el.name} className="animal-image" />
<div className="animal-name">{el.name}</div>
</Link>
</li>
))}
</ul>
) : (
<div className="no-results">검색 결과가 없습니다.</div>
)}
</div>
);
};
export default Search;
검색창에 'ㄱ'만 입력하면 'ㄱ'이 포함된 동물들이 모두 나타납니다.