react-router-dom 버전6 업그레이드 가이드
강의는 5.3.4버전이고 현재 최신은 6버전인데 리액트의 철학이 바뀌었기에 차이가 상당하다.
그러므로 5.3.4버전으로 모든 강의를 다 시청하고 이 다음 강의로 돌아가서 시청하는 것을 추천!
5버전을 클래식 버전이라고 하는데 아직도 많은 사람들이 클래식 버전을 좋아한다.
npm I react-router-dom@5.3.4
먼저 BorowserRouter 대신 createBorowserRouter를 사용할 것이다.
한 번 비교해보자.
BorowserRouter

jsx 컴포넌트를 사용하지 않고도 브라우저를 좀 더 선언적으로 바꿔준다.
리액트 라우터 공식문서를 참고하면 잘 나와있다.

첫 번째 라우터는 전체 toute들의 컨테이너와 같은 역할을 할 것이다.

main.tsx에 RouterProvider를 import하고 export한 router를 props로 준다.
하지만 아직 화면에 컴포넌트들이 렌더링되지 않는다.

Outlet 컴포넌트를 root에 넣으면 된다.
Outlet는 필요한 경우 Home, About 등을 소환한다고 보면 된다.
에러 엘리먼트가 추가됐다.
컴포넌트에 에러가 발생해서 충돌하거나, 컴포넌트의 위치를 찾지 못할 때 쓴다.

이렇게 NotFound라는 컴포넌트를 만들고

“/”에 errorElement의 props로 NotFound를 넣는다.

그럼 잘못된 주소를 입력했을 경우 NotFound가 뜬다.

그리고 충돌하는 경우엔 이처럼 “/” 주소에 존재하지 않는 속성을 조회하는 등의 경우

이렇게 미리 에러 컴포넌트를 만들어두고

에러 컴포넌트를 적용한 다음 “/” 페이지에 가보면

이렇게 에러 컴포넌트가 뜬다. 여기서 Header가 표시되는게 중요한데 이게 무슨 뜻이냐면 컴포넌트들을 다른 컴포넌트에서 발생하는 문제로부터 보호해준다는 얘기다.
만약 errorElement를 적용하지 않으면

이런 페이지가 뜰 것이다.
즉, errorElement는 에러가 넘치지 않게 일종의 장벽을 만들어주는 것이다.
useNavigate는 이전 버전의 useLocation.push, useHistory.push와 같은 역할이다.
다른 페이지로 이동시켜 준다는 것이다.
페이지를 이동하는데는 두 가지 방법이 있는데
사용 예시:
const navigate = useNavigate();
// 특정 버튼 클릭 시 페이지 이동
const handleButtonClick = () => {
navigate('/contact');
};
빠르게 user의 db를 만들어보자.

Home에 링크를 만들고 해당 페이지로 이동한다.(에러엘리먼트가 잘 나온다)

페이지를 제대로 띄우기 위해 라우터에 User를 추가하고 확인하면

useParams가 주소의 params를 가져와서 잘 띄우는 것을 확인할 수 있다.

userId는 users에 있는 속성이 아닌 동적인 값이므로 []표기법을 사용하여 name를 가져온다.

그러면 정상적으로 출력 된다.

해당 스크린에 자식이 있다면 Outlet이 스크린의 자식을 render한다.

링크를 누르면 좌측 페이지에서 우측 페이지로 이동한다.
url 주소가 해당 컴포넌트의 하위 컴포넌트 주소로 바뀌면 Outlet이 해당 주소의 하위 컴포넌트로 바뀐다고 생각하면 된다.

모든 Outlet는 Route(이 경우엔 Root)의 자식들을 렌더했다.
https://www.youtube.com/watch?v=0JbCa4yCqAM&ab_channel=%EB%94%A5%EC%94%A8%EC%BD%94%EB%8D%94
이전 시간에 어떻게 라우트가 그 자식들을 렌더하는지에 대해 배웠다.
<Outlet />, children, /:userId 등등
이제 이전 화면에서 데이터를 넘겨받는 것을 알아보자.
👉/users/followers에 유저 정보 보내기
function User() {
const { userId } = useParams();
return (
<>
<div>
<h1>User: {users[Number(userId) - 1].name}</h1>
</div>
<hr />
<Link to="followers">See followers</Link>
<Outlet
context={{
nameOfMyUser: users[Number(userId) - 1].name, //유저 정보를 보내기
}}
/>
</>
);
}
현재 <Outlet context={} />을 통해 User 라우트의 모든 자식들한테 유저정보를 보내고 있다.
function Followers() {
const a = useOutletContext();
console.log(a); //{nameOfMyUser: 'jun'}
return <div>Followers</div>;
}
그리고 /users/:userId/followers에서 useOutletContext()로 <Outlet context={} />를 이용해 보낸 정보를 받을 수 있다.
왜 버튼 등을 만들지 않고 state만을 바꾼 것일까?
새로고침 등을 해도 유저들이/users/1/followers에 있게 하기 위해서다. state를 사용하면 url에 state를 넣을 수 없다.
<Outlet context={} />,useOutletContext()를 사용하면 url을 이용해서 새로고침에도 정보가 초기화되지 않는다.
function Root() {
return (
<>
<Header />
<Outlet context={{darkMode: true}} />
</>
);
}
만약 <Outlet context={darkMode: true} />로 다크모드 정보를 전달한다고 해보자.
Outlet은 Root 화면의 자식들을 렌더한다는 것을 기억하자.
그 말은 Home, About, User 모두가 다크모드인지 아닌지 알게된다는 것이다.
이제 리액트 라우터로 fetch와 form 제출도 가능하다.
하지만 라우터는 라우터인게 좋은 것 같다.
ex) localhost:8080/users?id=123
파라미터 수정, 읽기를 도와준다.
useSearchParams 훅 참고
리액트 라우터 공식문서
요점은 라우팅만 하는게 아닌 데이터 등도 로드하는 함수를 가진다는 것이다.
참고로 리액트 라우터를 만든 사람들과 리믹스를 만든 사람들은 같다.
그래서 리믹스의 아이디어를 리액트 라우터에 많이 적용하고 있다.
리믹스 프레임워크도 나중에 써보자.