Next.js 13 로그인 상태 깜빡임 문제

버건디·2023년 8월 1일
1

Next.js

목록 보기
44/52
post-thumbnail
post-custom-banner

Next.js 13 App 디렉토리에서, 로그인 관련 정보를 페이지 컴포넌트에서 fetch 해온 후에, 그 정보를 Header로 보내주는데, 로그인 표시 컴포넌트가 먼저 등장 한 후에 회원 정보가 나타나는 문제가 발생했다.

- 회원 정보 받아오는 페이지 컴포넌트


export default async function Home() {
  const cookieStore = cookies();
  const accessTokenObj = cookieStore.get("accessToken");

  const refreshTokenObj = cookieStore.get("refreshToken");
  const accessToken = accessTokenObj?.value;
  const refreshToken = refreshTokenObj?.value;

  let loginData;
  let userInfo;

  // 리프레시 토큰이 있을때만 sendToken 함수 실행
  if (refreshToken) {
    loginData = await sendToken({ accessToken, refreshToken });

    userInfo = loginData?.user;
  }

  // console.log("userInfo : ", userInfo);

  return (
    <>
      <Card>
        <Header userInfo={userInfo} />
        <ContentCard>
          <MainLinks />
        </ContentCard>
        <Foorter />
      </Card>
    </>
  );
}

- Header 컴포넌트

"use client";

export default function Header({ userInfo }: UserInfoProps) {
  const [darkTheme, handleToggle] = useDarkMode();
  const { nowLoggedIn } = useSelector(selectUserInfo);
  const dispatch = useDispatch();

  useEffect(() => {
    if (userInfo) {
      const { createdAt, updatedAt, ...restUserInfo } = userInfo;
      dispatch(setUserInfo(restUserInfo));
    }
  }, [userInfo, dispatch]);

  return (
    <>
      <div className={classes.header_container}>
        <div className={classes.header_category_container}>
          <div className={classes.login_category}>
            {nowLoggedIn ? <UserProfile /> : <Login />}
          </div>
        </div>
      </div>
    </>
  );
}

현재는 Page 컴포넌트에서 회원 정보를 받아와서 Header 측에서 redux store에 해당 상태를 저장하는 방식인데,

서버에서 먼저 회원정보를 뿌려줬다고 하더라도, 렌더링 후에 회원정보가 스토어에 저장되므로 발생하는 문제 인거 같다.

그럼 애초에 렌더링 될때부터 회원정보를 받아서 렌더링 해주어야하는거 아닌가? 그렇다면 그걸 어떻게 해결할 것인가? 라고 생각이 이어졌다.


- 해결 방법

- 컴포넌트 자체를 props로 넘겨주기

export default async function Home() {
  const cookieStore = cookies();
  const accessTokenObj = cookieStore.get("accessToken");
  const refreshTokenObj = cookieStore.get("refreshToken");
  const accessToken = accessTokenObj?.value;
  const refreshToken = refreshTokenObj?.value;

  console.log("accessToken : ", accessToken);
  console.log("refreshToken : ", refreshToken);

  let loginData;
  let userInfo;
  if (refreshToken) {
    loginData = await sendToken({ accessToken, refreshToken });

    userInfo = loginData?.user;
  }

  console.log("userInfo : ", userInfo);

  return (
    <>
      <Card>
        {/* 로그인 되어있으면 UserProfile컴포넌트 props로 보내주고, 그게 아니라면 Login컴포넌트 보내주기 */}
        <Header
          userInfo={userInfo}
          component={userInfo ? UserProfile : Login}
          componentProps={userInfo ? { userInfo: userInfo } : {}}
        />
        <ContentCard>
          <MainLinks />
        </ContentCard>
        <Foorter />
      </Card>
    </>
  );
}

이런식으로 컴포넌트 자체를 보내주고, 로그인 되어있을때 필요한 회원정보 props 들도 컴포넌트 요소들로 따로 보내주었다 .

- Header 컴포넌트

"use client";

type UserInfoProps = {
  component: React.ElementType;
  componentProps?: object;
};

export default function Header({
  component,
  componentProps,
}: UserInfoProps) {

  return (
    <>
      <div className={classes.header_container}>
        <div className={classes.header_category_container}>
          <div className={classes.login_category}>
            {React.createElement(component, componentProps)}
          </div>
        </div>
      </div>
    </>
  );
}

- UserProfile 컴포넌트

"use client";

export default function UserProfile({ userInfo }: UserInfoProps) {
  const [profileMenu, setProfileMenu] = useState(false);

  const profileMenuHandler = () => {
    setProfileMenu(!profileMenu);
  };

  return (
    <>
      <div className={classes.profile_container}>
        <div className={classes.user_name}>{userInfo.userName}</div>

        <div className={classes.down_icon}>
          <AiFillCaretDown
            className={classes.down_icon}
            onClick={profileMenuHandler}
          />
        </div>
      </div>
      {profileMenu && <ProfileMenu />}
    </>
  );
}

이런식으로 페이지 컴포넌트 내에서 fetch 해서 받는 정보에 따라서 컴포넌트 자체를 내려주어서 깜빡이는 문제를 해결할 수 있었다!

profile
https://brgndy.me/ 로 옮기는 중입니다 :)
post-custom-banner

2개의 댓글

comment-user-thumbnail
2023년 8월 1일

공감하며 읽었습니다. 좋은 글 감사드립니다.

1개의 답글