프로젝트를 처음엔 하나의 페이지 구성으로 계획 했지만 검색에 따라 결과를 다르게 보여주기 위해 페이지 분기 처리가 필요했다. 때문에 react-router-dom
을 사용해 페이지를 나누기로 결정했다.
react-router-dom 구성
react-router-dom
의 공식문서를 찾아보고 페이지 분기를 나누는 코드를 작성 할 수 있었다.
// main.js
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import './index.css';
import IndexPage from './page/indexPage';
import DataPage from './page/dataPage';
const router = createBrowserRouter([
{
path: '/',
element: <IndexPage />,
},
{
path: ':id',
element: <DataPage />,
},
]);
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
다음과 같이 구성했고 /
와 /:id
페이지로 나눴다.
Header, IndexPage, DataPage 구성
Header
컴포넌트를 만들어 <IndexPage />
와 <DataPage />
에 넣어주었다.
// src/components/header
import { useRef } from 'react';
import { useNavigate } from 'react-router-dom';
export default function Header() {
const inputRef = useRef();
const navigate = useNavigate();
const handleSubmit = (e) => {
e.preventDefault();
const id = inputRef.current.value;
navigate(`/${id}`);
};
return (
<header>
<form onSubmit={handleSubmit}>
<input ref={inputRef} />
<button type='submit' />
</form>
</header>
);
}
// src/page/indexPage
// IndexPage
import Header from '../../components/header';
export default function IndexPage() {
return (
<>
<Header />
<div>index page</div>
</>
);
}
// src/page/dataPage
// DataPage
import { useParams } from 'react-router-dom';
import Header from '../../components/header';
import { useEffect, useState } from 'react';
export default function DataPage() {
const [data, setData] = useState();
const { id } = useParams();
const fetchData = (id) => {
console.log(`${id}의 데이터를 받아왔습니다.`);
return id;
};
useEffect(() => {
const data = fetchData(id);
setData(data);
}, []);
return (
<>
<Header />
<div>{data} data page</div>
</>
);
}
header
컴포넌트는 id
를 검색해 dataPage
에서 보여주는 역할을 한다.
나는 useNavigate()
를 통해 header
에서 페이지 이동을 시켜주니 당연히 dataPage
가 다시 마운트 되어 데이터를 받아오고 컴포넌트를 그려줄거라고 생각했다.
하지만 그렇지 않고 <DataPage />
컴포넌트는 다시 그려지기는 했지만 useEffect
안에 있는 데이터는 받아오지 않았다.
처음엔 내가 react-router-dom
을 잘못 사용 한 줄 알았다.
그렇게 고민하던중 useEffect
안에 로직이 동작하지 않는다는걸 알았다.
그리고 useEffect
의 dependency
에 id
를 넣어보니 정상적으로 동작하는걸 볼 수 있었다.
내가 생각하는 문제의 이유
react
는 Single Page Application
이다.
처음에 index.html
을 받아온 다음 JavaScript
를 통해 동적으로 움직인다.
react-router-dom
은 이런 SPA
를 Multiple Page Application
처럼 보이게 움직이도록 보여 주는 것이지 진짜로 MPA
로 만들어 주는게 아니다.
그래서 처음 <IndexPage />
에서 <DataPage />
로 이동 할때는
처음 마운트 되기 때문에 useEffect
안에 로직이 동작하지만
<DataPage />
에서 <DataPage />
로 이동하게 될때는 useNavigate
때문에 주소는 변경되지만 <DataPage />
는 마운트 되는것이 아니라 useEffect
안에 로직은 동작하지 않는다
그러므로, dependency
에 id
를 넣어주면서 id
가 변경 될 때마다 useEffect
안에 로직이 동작 되게 만들어 줄 수 있다.
// src/page/dataPage
// DataPage
...
useEffect(() => {
const data = fetchData(id);
setData(data);
}, [id]);
...
}
❌ 주의
useNavigate
로 페이지를 이동하는것과 주소창에서 페이지를 입력해서 이동하는것은 다르다.
주소창에 입력해서 이동 할 경우 새로운 페이지를 요청해서 받아오기 때문에 위와같은 문제를 겪을수 없습니다.
react가 SPA인것은 알고있었지만 react-router-dom을 사용하면 새로운 페이지로 이동하는것처럼 구현이 되어있고 새롭게 컴포넌트가 구성되는줄 알았지만 전혀 그렇지 않고 한개의 페이지에서 필요한 동작을 만들어줘야하는걸 알았다.