지난 시간 복습
백드롭, cancel 버튼 리팩토링
: 기존의 Click 이벤트 핸들러 대신 라우팅을 연결해서 리팩토링함.
백드롭은useNavigate
훅을 이용해서 상대경로 지정해서.
cancel 은 버튼 속성을Link
로 변경해서 이것도 상대경로인 시작 페이지로 설정함.
"react-router-dom" 6.4 이상 버전으로 설치했다면
매우 편리한 기능들을 사용 할 수 있다. loader()
, action ()
기능이다. 설명하기에 앞서서, 기존의 데이터를 가져오는 방식먼저 다시 정리해보겠다.
데이터를 가져올 때 나의 기존 코드와 같이 useEffect
를 사용해도 된다.
useEffect
를 사용해서 데이터 가져왔던 방식:
1. main.jsx에서Posts
컴포넌트에서는 렌더링
2.Posts
컴포넌트에서는PostList
컴포넌트 렌더링 하게 리팩토링 함.
3.PostList
컴포넌트에서 데이터를 가져오는 코드가 있음.
useEffect
를 사용해 콜백함수로 async await 를 이용해 fetch() 함수를 통해서 말이다.- get(가져오고), post(데이터 보내고) 전부 다
PostList
컴포넌트에서 처리했음.
그런데 이 복잡한 과정(데이터는 보내고 가져오고 하는)을 loader(), action () 기능을 통해서 편리하게 수정 할 수 있다.
답 : 라우트 정의에 'loader'라는 프로퍼티 추가하면 됨.
✍ main.jsx (에러 있는 상태)
//생략 생략 생략
const router = createBrowserRouter([
{
path: "/",
element: <RootLayout />,
children: [
{
path: "/",
element: <Posts />,
loader :() =>{} // 아직 값으로 받을 함수 정의안해서 에러.
children: [{ path: "/create-post", element: <NewPost /> }],
},
],
},
]);
//생략 생략생략
- 함수를 값으로 받는다.
- 리액트 라우터는 해당 라우트가 활성화될 때마다 loader에 있는 함수를 실행한다. 즉, 이 요소가 렌더링되려고 할 때마다 loader에 있는 함수가 실행됨.
여기 있는 함수를 사용해 이 라우트 컴포넌트 또는 이 라우트의 일부로 사용되는 컴포넌트에 필요한 데이터를 미리 로드해 둘 수 있다.- 값으로 사용될 함수엔 비동기 함수도 넣을 수 있다.
- HTTP 요청을 전송해 데이터를 가져오는 로직을 추가
- 다만 라우터를 정의하는 부분인 main.jsx가 무거워 지므로 이쪽에서 함수를 짜지 않고, 해당 라우트 컴포넌트 파일에서 함수를 작성한다. 나는
Posts
컴포넌트부터 시작함.- PostList 컴포넌트에서 데이터를 가져오는 부분 잘라내서 Posts 컴포넌트에 copy.
- 그리고 이제 상태 업데이트 코드는 신경쓰지 않아도 된다. 왜냐하면 loader함수는 컴포넌트 바깥에서 실행되기 때문에 컴포넌트 상태를 바꿀 수 없기 때문이다. 이렇게 해도 컴포넌트로 포스트를 가져올 수 있다.
- 이 함수에서 화면에 표시할 데이터를 return 하면 그 데이터를 현재 라우트에 렌더링된 요소가 받는다. 즉, loader 특성이 정의된 컴포넌트 요소가 받는것임.
여기까지 한다음에 Posts
컴포넌트에서 loader() 에 값에 들어갈 함수를 작성한다. 데이터를 가져온것을 확인한 다음 그 데이터를 돌려주기 위해 비동기로 작성했다.
✍ Posts.jsx
import { Outlet } from "react-router-dom";
import PostList from "../components/PostList";
function Posts() {
return (
<>
<Outlet />
<main>
<PostList />
</main>
</>
);
}
export default Posts;
export async function loader() {
const response = await fetch("http://localhost:8080/posts");
const resData = await response.json();
return resData.Posts;
}
여기서 조금 헷갈리는데, Posts
컴포넌트에서 loader 값에 들어갈 함수를 export 했으면 실제로 라우터가 정의되고 Loader 함수를 사용할 곳인 main.jsx
에서 이 값을 import로 받아야한다.
그리고 main.jsx에서만 사용할 loader의 이름을 설정해준다.
(왜냐하면 이름 충돌을 피하기 위해서 :
: >>> Posts
컴포넌트에서 정의한 Loader 함수는 어디서든 재사용성이 가능하기 떄문에 이름이 겹치는 에러를 방지하기 위해서.)
여기까지 하고서 적용하자. Posts
라우터 정의 부분에 loader
를 추가했다.
✍ main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import Posts, { loader as postLoader } from "./routes/Posts";
import NewPost from "./routes/NewPost";
import RootLayout from "./routes/RootLayout";
import "./index.css";
const router = createBrowserRouter([
{
path: "/",
element: <RootLayout />,
children: [
{
path: "/",
element: <Posts />,
loader: postLoader,
children: [{ path: "/create-post", element: <NewPost /> }],
},
],
},
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
저장하고 실행하면 리액트 라우터는 이 함수를 실행해 프로미스 값을 받을 때까지 기다렸다가 해당 요소를 렌더링할거다. (이 코드에서는
Posts
컴포넌트다)
그리고 이 loader()에서 반환한 데이터는 이 위에 있는 요소에서 사용할 수 있다. 그러니까
Posts
컴포넌트에서loader
함수를 export 하고 있는데 이 컴포넌트에서 반환하는 데이터를 사용할 수 있다는 말이다. 그리고 중첩된 요소(PostList
)도 동일하다.
정리하자면
리액트 라우터는 loader()에서 반환한 데이터에 이 라우트 요소 또는 중첩된 컴포넌트가 접근할 수 있게 해준다. PostsList에서도 접근할 수 있다.
다만 중첩된 컴포넌트인 PostList에서 접근하려면 한가지 작업을 추가해줘야한다.
userLoaderData
라는 훅을 임포트 해야한다.
이 훅을 사용할 수 있는 곳은 컴포넌트 함수 내부고, 다른 훅처럼 이 훅도 컴포넌트 함수 안에서만 사용해야 한다.
이 훅을 사용하면 Posts 라우트에 할당된 loader()에서 반환한 데이터를 얻을 수 있다. 좀 복잡하긴한데 그냥 외우면 된다.
PostList에서userLoaderData' 훅을 추가하면
Posts` 컴포넌트에 추가하고 Loader로 반환한 데이터에 접근할 수 있다.
그러니까 Posts.jsx
에서 export로 명시한 export async function loader()
함수에서 리턴한 값에 접근 가능하다는 말임. 왜냐하면 <PostList />
가 Posts.jsx
에 중첩된 컴포넌트이니까.
✍ PostList.jsx
결국 로더함수를 이용하면 그럼 리액트 라우터는 이 함수를 실행해 프로미스 값을 받을 때까지 기다렸다가 이 요소를 렌더링할것임. 그리고 PostList에선 더이상 state와 useEffect가 필요없음. loader 속성으로 데이터를 받았으니 그걸 쓰면 됨.
userLoaderData
훅을 추가해서.
action에 대해선 다음 포스팅에 정리.