전역 상태 관리 Zustand state update 지연 문제 (문제 해결)

Devinix·2023년 10월 12일
1

[문제 해결]

목록 보기
5/29
post-thumbnail

개요

로그인이 성공한 후, 사용자의 이메일 정보를 Zustand로 전역 상태에 저장하여 다른 컴포넌트들이 접근할 수 있도록 계획했다. 그러나 무언가 문제가 있었는지, 로그인 후에도 사용자의 이메일이 바로 브라우저 창에 표시되지 않았다. 페이지를 새로고침해야만 이메일이 렌더링되는 상황이 발생한 것이다.

기존의 코드를 보자.

// useEmailStore.ts...

import { create } from "zustand";

interface IUseEmailStore {
  email: string | null;
}

const useEmailStore = create<IUseEmailStore>(() => ({
  email: sessionStorage.getItem("email"),
}));


export default useEmailStore;

나는 Zustand 전역 상태 관리 라이브러리로 sessionStorage에서 이메일 정보를 추출하고, 이렇게 가져온 이메일 정보를 필요한 컴포넌트들에서 import하여 사용하고 있었다.

// useSignInMutation.ts...

import { useNavigate } from "react-router-dom";
import signInApi from "../../../api/signInApi";
import { useMutation } from "@tanstack/react-query";

interface IUseSignInMutation {
  mutateSignIn: (values: ISignInValues) => void;
} 

function useSignInMutation(): IUseSignInMutation {
  const navigate = useNavigate();

  const signInMutation = useMutation({
    mutationFn: (values: ISignInValues) => {
      return signInApi(values);
    },
    onSuccess: () => {
      navigate("/home");
    },
    onError: () => {
      alert("로그인에 실패했습니다. 이메일과 비밀번호를 확인해주세요.");
    },
  });
  const mutateSignIn = signInMutation.mutate;
  return { mutateSignIn };
}

export default useSignInMutation;

리액트 쿼리를 활용한 훅이다. 이 훅은 SignIn.tsx 컴포넌트에서 사용된다. mutate 과정에서 signInApi에 values 인자를 전달한다. 요청이 성공하면 /home 경로로 페이지를 이동시키고, 에러가 발생할 경우 alert 창을 표시한다.

문제 상황

사용자가 로그인한 직후, 해당 정보가 즉시 반영되지 않았다. 이메일 필드가 초기에 비어 있었고, 페이지를 새로고침해야만 이메일 정보가 나타났다.

원인

문제의 원인은 로그인에 성공했을 때 Zustand 스토어에 사용자의 이메일 정보를 업데이트하지 않았기 때문이었다. 컴포넌트가 렌더링되는 시점에 필요한 데이터가 Zustand 스토어에 존재하지 않았었고, 이 때문에 UI는 처음에 빈 이메일 필드를 표시했으며 이후에 페이지를 새로 고침할 때만 상태가 업데이트되어 정확한 정보를 보여주었던 것이었다.

해결 과정

로그인이 성공했을 때 서버로부터 받은 사용자 정보 중 이메일을 Zustand 스토어에 전역적으로 업데이트하는 로직을 추가하여 문제를 해결했다. 코드를 살펴보자.

import { create } from "zustand";

interface IUseEmailStore {
  email: string | null;
  setEmail: (email: string) => void;
}

const useEmailStore = create<IUseEmailStore>((set) => ({
  email: sessionStorage.getItem("email"),
  setEmail: (email) => set({ email: email }),
}));

export default useEmailStore;

추가된 setEmail 함수는 email이라는 매개변수를 받아 현재의 email 상태를 업데이트하는 기능을 한다. 다음 단계로, useSignInMutation 훅의 수정이 필요하다.

import { useNavigate } from "react-router-dom";
import signInApi from "../../../api/signInApi";
import { useMutation } from "@tanstack/react-query";
import useEmailStore from "../../../stores/email/useEmailStore";

interface IUseSignInMutation {
  mutateSignIn: (values: ISignInValues) => void;
}

function useSignInMutation(): IUseSignInMutation {
  const navigate = useNavigate();
  const { setEmail } = useEmailStore();

  const signInMutation = useMutation({
    mutationFn: (values: ISignInValues) => {
      return signInApi(values);
    },
    onSuccess: (data) => {
      setEmail(data.data.email);
      navigate("/home");
    },
    onError: () => {
      alert("로그인에 실패했습니다. 이메일과 비밀번호를 확인해주세요.");
    },
  });
  const mutateSignIn = signInMutation.mutate;
  return { mutateSignIn };
}

export default useSignInMutation;

useEmailStore에서 setEmail 함수를 추출했고, 이 함수를 useSignInMutation 훅의 onSuccess 함수 로직에 추가하였다. 그 후 서버로부터 받은 데이터 중 이메일에 해당하는 data.data.email을 setEamil함수의 인자로 전달하는 방식으로 수정했다. 이제 문제 없이 작동한다.

결론

이 방법을 통해 사용자가 페이지를 새로고침하지 않아도 렌더링 즉시 이메일이 반영되는 결과를 얻을 수 있었고, 이로 인해 사용자 경험이 크게 향상되었다.

profile
프론트엔드 개발

0개의 댓글