기존에는 Tanstack Query의 isLoading과 같은 로딩 상태 값을 통해 서버 데이터를 패칭 하는 과정에 대한 부분을 관리하곤 했었다. 하지만 Remix의 경우 초기 데이터를 Loader 함수를 통해 불러오거나, 추후 변경된 값에 대한 데이터를 Action 함수를 통해 불러오게 되는데 이때 두 함수에 대해 로딩 상태를 알 수 있는 방법이 있는지 궁금했다. 서버를 통해 나오는 값이기 때문에, React의 useState를 통한 직접적인 로딩 상태관리도 쉽지 않았다. 이때 사용할 수 있는 Remix 훅이 존재한다.
리믹스에서는 useFetcher이라는 훅을 제공한다.useFetcher을 통해 Form 태그를 관리한다면 fetcher.Form을 통해 폼 요청이 발생하고, 이때 fetcher.state를 사용하여 로딩 상태를 확인할 수 있다. 이때 로딩 상태는 isSubmitting으로, 기본 상태는 idle 상태의 값을 리턴하게 된다.
const fetcher = useFetcher<actionType>();
const isLoading = fetcher.state === "submitting";
...
<fetcher.Form method="post" css={css`
display: flex;
flex-direction: column;
row-gap: 1rem;
`}>
<Input type="text" name="username" placeholder="브런치 주소 또는 브런치 아이디를 입력해주세요" />
<Button type="submit" disabled={isLoading}> {isLoading ? "로딩 중..." : "RSS 추출하기"} </Button>
</fetcher.Form>
Remix에서 한 페이지에 여러 Action이 존재해야한다면?
또한 Remix에서는 각 라우트 파일당 action과 loader를 각각 하나씩만 정의할 수 있기 때문에 한 파일에서 여러 개의 action을 분리해 정의할 수 없다. 그러나 여러 fetcher 요청을 하나의 action에서 다루는 방법은 type 필드 또는 유사한 식별자를 활용하여 각 요청을 구분할 수 있다. 아래는 URL 경로에 따른 구분 방법인데, action에서 요청을 구분할 때 Form이나 fetcher.sumit()의 action 속성에 쿼리 파라미터를 추가할 수 있다. 예를 들어 /action-url?task=rss나 /action-url?task=profile과 같은 식으로 특정 요청을 구분하면, action 함수에서 URL의 task 파라미터 값을 이용해 분기할 수 있다.
const rssFetcher = useFetcher();
const profileFetcher = useFetcher();
return (
<>
{/* RSS 요청 */}
<rssFetcher.Form method="post" action="/action-url?task=rss">
<input type="text" name="username" placeholder="브런치 아이디 입력" />
<button type="submit">RSS 추출하기</button>
</rssFetcher.Form>
{/* 프로필 요청 */}
<profileFetcher.Form method="post" action="/action-url?task=profile">
<input type="text" name="userId" placeholder="사용자 ID 입력" />
<button type="submit">프로필 조회하기</button>
</profileFetcher.Form>
</>
);
그리고 action 함수에서는 URL 쿼리 파라미터를 읽어서 각 요청을 구분할 수 있다.
export const action: ActionFunction = async ({ request }) => {
const url = new URL(request.url);
const task = url.searchParams.get("task");
if (task === "rss") {
// RSS 처리 로직
} else if (task === "profile") {
// 프로필 조회 처리 로직
} else {
return json({ error: "유효하지 않은 요청입니다." });
}
};
이런 쿼리 파라미터를 이용한 방법 이외에는 별도의 경로를 만들어 추가적인 action을 사용할 수 있다.
특정 요청별로 /api/rss-action, /api/profile-action과 같은 뎁스가 있는 경로를 추가로 만들어 action을 분리할 수 있는데, 이 경우에 각 경로에 별도의 action을 설정하여 유지보수가 쉬워진다는 장점을 가지고 있다.
<fetcher.Form method="post" action="/api/rss-action" css={css`
display: flex;
flex-direction: column;
row-gap: 1rem;
margin-top: 3rem;
`}
>
<Input type={'text'} name={'username'} placeholder={"브런치 주소 또는 브런치 아이디를 입력해주세요"}/>
<Button type={"submit"} loading={fetcher?.state === 'submitting'}> RSS 추출하기 </Button>
</fetcher.Form>
댓글