세션에서 상태 불러오기

로그인 정보를 유지하려면 브라우저 세션에 저장하고 불러와야 합니다. 이를 위해 쿠키나 로컬 스토리지를 사용할 수 있습니다. 다행히 AWS Amplify가 이를 자동으로 처리해 주기 때문에, 우리는 단순히 이를 읽어와 애플리케이션 상태에 로드하기만 하면 됩니다.

Amplify는 Auth.currentSession() 메서드를 통해 현재 사용자 세션을 가져올 수 있는 방법을 제공합니다. 이 메서드는 세션 객체(존재하는 경우)로 resolve되는 Promise를 반환합니다.

사용자 세션 불러오기

앱이 로드될 때 사용자 세션을 불러오도록 해봅시다. 이를 위해 React의 또 다른 훅인 useEffect를 사용할 것입니다. Auth.currentSession()이 Promise를 반환하기 때문에, 이 작업이 완료된 후에만 앱의 나머지 부분이 준비되도록 해야 합니다.

Change indicator 이를 위해 src/App.tsx의 상태에 isAuthenticating이라는 새로운 상태 변수를 추가합니다. App 함수의 상단에 추가합니다.

const [isAuthenticating, setIsAuthenticating] = useState(true);

앱이 처음 로드될 때 인증 상태를 확인하는 과정에서 시작되므로 초기값을 true로 설정합니다.

Change indicator 사용자 세션을 불러오기 위해 src/App.tsx의 변수 선언 바로 아래에 다음 코드를 추가합니다.

useEffect(() => {
  onLoad();
}, []);

async function onLoad() {
  try {
    await Auth.currentSession();
    userHasAuthenticated(true);
  } catch (e) {
    if (e !== "No current user") {
      alert(e);
    }
  }

  setIsAuthenticating(false);
}

Change indicator 그런 다음 src/App.tsx의 헤더에 Auth 모듈을 추가합니다.

import { Auth } from "aws-amplify";

Change indicator useEffect 훅을 포함시키기 위해 src/App.tsx의 헤더에서 React import를 다음과 같이 변경합니다.

import { useState, useEffect } from "react";

이제 useEffect 훅이 어떻게 동작하는지 이해해봅시다.

useEffect 훅은 함수와 변수 배열을 인자로 받습니다. 이 함수는 컴포넌트가 렌더링될 때마다 호출됩니다. 그리고 변수 배열은 전달된 배열의 변수가 변경되었을 때만 함수를 다시 실행하도록 React에 지시합니다. 이를 통해 함수가 실행되는 시점을 제어할 수 있습니다. 이는 몇 가지 흥미로운 결과를 가져옵니다:

  1. 변수 배열을 전달하지 않으면, 컴포넌트가 렌더링될 때마다 훅이 실행됩니다.
  2. 일부 변수를 전달하면, React는 렌더링할 때마다 해당 변수가 변경되었는지 확인한 후에 함수를 실행합니다.
  3. 빈 배열을 전달하면, 첫 번째 렌더링 시에만 함수가 실행됩니다.

이 경우, 앱이 처음 로드될 때만 사용자의 인증 상태를 확인하고 싶습니다. 따라서 세 번째 옵션을 사용합니다. 빈 배열 []을 전달하면 됩니다.

앱이 처음 로드될 때 onLoad 함수가 실행됩니다. 이 함수는 현재 세션을 불러오는 역할을 합니다. 세션이 성공적으로 불러와지면, isAuthenticating 상태 변수를 업데이트합니다. 이는 setIsAuthenticating(false)를 호출하여 수행됩니다. Auth.currentSession() 메서드는 현재 로그인한 사용자가 없을 때 No current user 오류를 발생시킵니다. 사용자가 앱을 로드했을 때 로그인하지 않은 상태라면 이 오류를 사용자에게 보여주고 싶지 않습니다. Auth.currentSession()이 성공적으로 실행되면, userHasAuthenticated(true)를 호출하여 사용자가 로그인했음을 설정합니다.

따라서 App 함수의 상단은 이제 다음과 같이 보일 것입니다:

function App() {
  const [isAuthenticating, setIsAuthenticating] = useState(true);
  const [isAuthenticated, userHasAuthenticated] = useState(false);

  useEffect(() => {
    onLoad();
  }, []);

  ...

상태가 준비되었을 때 렌더링하기

사용자 세션을 불러오는 과정은 비동기적이므로, 앱이 처음 로드될 때 상태가 변경되지 않도록 해야 합니다. 이를 위해 isAuthenticatingfalse가 될 때까지 앱 렌더링을 지연시킬 것입니다.

isAuthenticating 플래그를 기반으로 앱을 조건부로 렌더링합니다.

Change indicator src/App.tsxreturn 문을 다음 코드로 교체하세요.

  return (
    !isAuthenticating && (
      <div className="App container py-3">
        <Navbar collapseOnSelect bg="light" expand="md" className="mb-3 px-3">
          <LinkContainer to="/">
            <Navbar.Brand className="fw-bold text-muted">Scratch</Navbar.Brand>
          </LinkContainer>
          <Navbar.Toggle />
          <Navbar.Collapse className="justify-content-end">
            <Nav activeKey={window.location.pathname}>
              {isAuthenticated ? (
                <Nav.Link onClick={handleLogout}>Logout</Nav.Link>
              ) : (
                <>
                  <LinkContainer to="/signup">
                    <Nav.Link>Signup</Nav.Link>
                  </LinkContainer>
                  <LinkContainer to="/login">
                    <Nav.Link>Login</Nav.Link>
                  </LinkContainer>
                </>
              )}
            </Nav>
          </Navbar.Collapse>
        </Navbar>
        <AppContext.Provider
          value={{ isAuthenticated, userHasAuthenticated } as AppContextType}
        >
          <Routes />
        </AppContext.Provider>
      </div>
    )
  );

이제 브라우저로 이동해 페이지를 새로고침하면 사용자가 로그인된 상태로 표시됩니다.

세션 로드 후 로그인 스크린샷

하지만 로그아웃을 한 후 페이지를 새로고침하면 여전히 로그인 상태로 남아 있습니다. 이 문제를 해결하기 위해 다음으로 로그아웃 시 세션을 초기화할 것입니다.