로그인 중 피드백 제공하기

사용자가 로그인하는 동안 피드백을 제공하는 것이 중요합니다. 이를 통해 앱이 응답하지 않는 상태가 아니라 여전히 작동 중이라는 느낌을 줄 수 있습니다.

isLoading 플래그 사용하기

Change indicator 이를 위해 src/containers/Login.tsx의 상태에 isLoading 플래그를 추가할 것입니다. Login 함수 컴포넌트의 상단에 다음을 추가하세요.

const [isLoading, setIsLoading] = useState(false);

Change indicator 그리고 로그인 중에 이 값을 업데이트할 것입니다. 따라서 handleSubmit 함수는 이제 다음과 같이 변경됩니다:

async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
  event.preventDefault();

  setIsLoading(true);

  try {
    await Auth.signIn(email, password);
    userHasAuthenticated(true);
    nav("/");
  } catch (error) {
    if (error instanceof Error) {
      alert(error.message);
    } else {
      alert(String(error));
    }
    setIsLoading(false);
  }
}

로더 버튼 만들기

이제 버튼의 상태 변화를 반영하기 위해 isLoading 플래그에 따라 다르게 렌더링할 것입니다. 하지만 이 코드는 여러 곳에서 사용될 가능성이 높습니다. 따라서 재사용 가능한 컴포넌트로 만드는 것이 합리적입니다.

Change indicator frontend/ 디렉토리에서 다음 명령어를 실행해 src/components/ 디렉토리를 생성합니다.

$ mkdir src/components/

여기서는 API와 직접적으로 연관되지 않거나 라우트에 응답하지 않는 모든 React 컴포넌트를 저장할 것입니다.

Change indicator 새 파일을 만들고 src/components/LoaderButton.tsx에 다음 내용을 추가합니다.

import Button from "react-bootstrap/Button";
import { BsArrowRepeat } from "react-icons/bs";
import "./LoaderButton.css";

export default function LoaderButton({
  className = "",
  disabled = false,
  isLoading = false,
  ...props
}) {
  return (
    <Button
      disabled={disabled || isLoading}
      className={`LoaderButton ${className}`}
      {...props}
    >
      {isLoading && <BsArrowRepeat className="spinning" />}
      {props.children}
    </Button>
  );
}

이 컴포넌트는 isLoadingdisabled prop을 받습니다. 후자는 현재 Login 버튼에 있는 기능을 반영한 것입니다. isLoadingtrue일 때 버튼이 비활성화되도록 보장합니다. 이렇게 하면 사용자가 로그인 중에 버튼을 클릭할 수 없게 됩니다.

className prop은 이 컴포넌트에 설정된 CSS 클래스가 내부적으로 사용하는 LoaderButton CSS 클래스를 덮어쓰지 않도록 보장합니다.

isLoading 플래그가 켜져 있을 때 아이콘을 표시합니다. 사용한 아이콘은 React Icons의 Bootstrap 아이콘 세트에서 가져온 것입니다.

로딩 아이콘에 애니메이션을 적용하기 위해 몇 가지 스타일을 추가해 보겠습니다.

Change indicator src/components/LoaderButton.css에 다음 내용을 추가합니다.

.LoaderButton {
  margin-top: 12px;
}

.LoaderButton .spinning {
  margin-right: 7px;
  margin-bottom: 1px;
  animation: spin 1s infinite linear;
}

@keyframes spin {
  from {
    transform: scale(1) rotate(0deg);
  }
  to {
    transform: scale(1) rotate(360deg);
  }
}

이 스타일은 아이콘을 무한히 회전시키며, 각 회전은 1초가 걸립니다. 이 스타일을 LoaderButton의 일부로 추가함으로써 컴포넌트 내부에 자체적으로 포함시킵니다.

isLoading 플래그를 사용하여 렌더링하기

이제 새로운 컴포넌트를 Login 컨테이너에서 사용할 수 있습니다.

Change indicator src/containers/Login.tsx에서 return 문 안에 있는 <Button> 컴포넌트를 찾으세요.

<Button size="lg" type="submit" disabled={!validateForm()}>
    Login
</Button>

Change indicator 그리고 이를 다음으로 교체하세요.

<LoaderButton
  size="lg"
  type="submit"
  isLoading={isLoading}
  disabled={!validateForm()}
>
  Login
</LoaderButton>

Change indicator 또한, 헤더에 있는 Button 임포트를 교체하세요. 다음을 제거하세요.

import Button from "react-bootstrap/Button";

Change indicator 그리고 다음을 추가하세요.

import LoaderButton from "../components/LoaderButton.tsx";

이제 브라우저로 전환하여 로그인을 시도하면, 로그인이 완료되기 전의 중간 상태를 볼 수 있습니다.

로그인 로딩 상태 스크린샷

에러 처리

로그인과 앱 컴포넌트에서 에러가 발생했을 때 단순히 alert를 사용하는 것을 보셨을 겁니다. 우리는 에러 처리를 간단하게 유지할 예정입니다. 하지만 모든 에러를 한 곳에서 처리하면 나중에 도움이 될 것입니다.

Change indicator 이를 위해 src/lib/errorLib.ts 파일을 생성하고 다음 코드를 추가하세요.

export function onError(error: any) {
  let message = String(error);

  if (!(error instanceof Error) && error.message) {
    message = String(error.message);
  }

  alert(message);
}

Auth 패키지는 다른 형식으로 에러를 던지기 때문에, 이 코드는 필요한 에러 메시지를 alert로 표시합니다. 다른 모든 경우에는 단순히 에러 객체 자체를 alert로 표시합니다.

이제 이 함수를 로그인 컨테이너(containers/Login.tsx)에서 사용해 보겠습니다.

Change indicator handleSubmit 함수의 catch 문을 다음과 같이 변경하세요.

catch (error) {
  onError(error);
  setIsLoading(false);
}

Change indicator 그리고 src/containers/Login.tsx 파일 상단에 새로운 에러 라이브러리를 임포트하세요.

import { onError } from "../lib/errorLib";

앱 컴포넌트에서도 비슷한 작업을 수행합니다.

Change indicator onLoad 함수의 catch 문을 다음과 같이 변경하세요.

catch (error) {
  if (error !== "No current user") {
    onError(error);
  }
}

Change indicator 그리고 src/App.tsx 파일 상단에 에러 라이브러리를 임포트하세요.

import { onError } from "./lib/errorLib";

가이드 후반부에서 에러 처리를 조금 더 개선할 예정입니다.

이제 앱의 회원가입 프로세스로 넘어갈 준비가 되었습니다.