인프런 커뮤니티 질문&답변

초무님의 프로필 이미지
초무

작성한 질문수

[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스

23-03-login-check / login-check-success

로그인 / 로그아웃 시 이전의 data가 남아있습니다.

해결된 질문

작성

·

94

0

import React, { useState, MouseEvent } from "react";
import * as S from "./mypoint.index.styles";
import TapPointLoading from "./mypoint_loading";
import TapPointBuying from "./mypoint_buying";
import TapPointAll from "./mypoint_all";
import TapPointSelling from "./mypoint_selling";

export default function MyPoint(): JSX.Element {
  const [selectedValue, setSelectedValue] = useState<{
    name: string;
    index: number;
  }>({
    name: "전체내역",
    index: 0,
  });

  const MyPageHeadInfoButton = [
    { name: "전체내역", component: <TapPointAll /> },
    { name: "충전내역", component: <TapPointLoading /> },
    { name: "구매내역", component: <TapPointBuying /> },
    { name: "판매내역", component: <TapPointSelling /> },
  ];

  const onClickTap = (
    event: MouseEvent<HTMLDivElement>,
    index: number
  ): void => {
    const newValue = event.currentTarget.id;

    setSelectedValue({
      name: newValue,
      index,
    });
  };

  // 버튼 클릭으로 해당 data 값을 보여주기 위함
  const renderData = (): JSX.Element | undefined => {
    return (
      <S.TableWrap>
        {/* 해당 탭의 배열의 객체 name통해 컴포넌트 가져옴 */}
        {MyPageHeadInfoButton.map((el) => {
          if (el.name === selectedValue.name && el.component) {
            return <div key={el.name}>{el.component}</div>;
          }
          return null;
        })}
      </S.TableWrap>
    );
  };

  return (
    <S.Wrap>
      <S.MyPageHeadWrap>
        <S.MyPageHeadBtn>
          {/* 서브 탭 버튼 */}
          {MyPageHeadInfoButton.map((el, index) => (
            <S.MyPageInfoBtn
              key={el.name}
              id={el.name}
              onClick={(event) => onClickTap(event, index)}
              selected={selectedValue.name === el.name}
            >
              {el.name}
            </S.MyPageInfoBtn>
          ))}
        </S.MyPageHeadBtn>
      </S.MyPageHeadWrap>
      <S.MyPageBodyWrap>
        {/* 해당 탭 data */}
        <div>{renderData()}</div>
      </S.MyPageBodyWrap>
    </S.Wrap>
  );
}

이렇게 탭을 클릭을 해서 해당 컴포넌트가 보이도록 했습니다.

그러면서 각각의 API data도 받아와 cache에 저장을 하도록 해 추가 API 요청이 일어나지 않도록 했는데

여기서 로그아웃을 하고 다른 아이디로 로그인을 하면

이전의 로그인 했던 data가 그대로 남아있어 최신 data를 가져오기 위해 여러가지 방법으로 시도했습니다.

  1. 로그아웃 시 useApolloClient를 활용해 cache를 삭제 했습니다. 이렇게 하면 다시 로그인 한 유저의 data를 잘 가져오는데 로그아웃을 하면서 cache data가 삭제되니 다른 API, Board, Markets 등 해당 page의 사용한 query문의 data를 다시 요청하는 문제가 있습니다.

     

 

  1. refetch()로 query문을 선언하면 해당 refetch()를 실행하지 않아도 API 요청이 일어나 refetchQueries를 사용해 data 최신화 하려는데 그렇게 되면 모든 탭을 refetch 해줘야 되는 문제가 있습니다.

각 탭 이동마다 API 요청 나가는게 싫어서 fetchporicy나 refetch()를 사용 하지 않았는데 보통 어떻게 사용을 하나요?

 

로그인에 의한 data는 탭 이동마다 refetch를 시켜주는게 좋을까요?

 

그리고 구매, 충전 등 하면 바로 MyPoint에 업데이트 되도록

client.writeQuery / readQeury 나 refetchQueries를 사용했는데 이런 결제 정보 같은거는 refetch로 실시간으로 data를 가져오는게 좋겠죠?

 

로그인, 로그아웃은 LayoutHeader에서 사용하고 있습니다.

답변 2

0

노원두님의 프로필 이미지
노원두
지식공유자

안녕하세요! 초무님!
아래에 순서대로 답변 드려 볼게요!

  1. 로그아웃시 처리 이슈
    => apollo-client 에서는 로그아웃시 client.resetStore()를 사용해서 제거된 토큰으로 요청했던 api들에 대하여 일괄 재요청하실 수 있습니다!
    또한, 반대로 모두 제거하되, 자동으로 재요청 하지 않는 방법으로는 client.clearStore() 라는 기능을 사용해 보실 수 있어요.
    만약, 로그아웃시 토큰을 필요로하는 캐시만 지우기 위해서는 직접 client-cache를 수정해 주셔야 할 것 같네요!

  2. refetch()로 query문을 선언하면 해당 refetch()를 실행하지 않아도 API 요청이 일어나 refetchQueries를 사용해 data 최신화 하려는데 그렇게 되면 모든 탭을 refetch 해줘야 되는 문제가 있습니다.

    각 탭 이동마다 API 요청 나가는게 싫어서 fetchporicy나 refetch()를 사용 하지 않았는데 보통 어떻게 사용을 하나요?
    => 이 질문은 위 1번 질문에 이어지는 내용으로 로그아웃시 토큰이 사라진 유저에 대한 모든 탭의 refetch의 질문으로 이해를 하였는데요! 앞서 말씀 드린 것과 같이 client.resetStore()를 사용하여 변경된 토큰으로 재쿼리를 날려 볼 수 있겠어요!

  3. 그리고 구매, 충전 등 하면 바로 MyPoint에 업데이트 되도록

    client.writeQuery / readQeury 나 refetchQueries를 사용했는데 이런 결제 정보 같은거는 refetch로 실시간으로 data를 가져오는게 좋겠죠?
    => 실시간 동기화가 중요한 데이터의 경우, refetch를 사용하시는 것이 좋아 보이네요!^^

0

안녕하세요, 인프런 AI 인턴입니다. 초무님의 질문에 대한 답을 드리기 앞서, Apollo Client의 캐시 관리는 상당히 중요하며 많은 개발자들이 고민하는 문제 중 하나입니다. 로그인 및 로그아웃 시 이전 데이터가 남아있는 문제와 관련해, 여러 방법으로 시도해보셨으나 완전한 해결책을 찾지 못한 것 같습니다. 그렇다면, 아래의 내용을 참고하셔서 문제 해결에 도움이 되었으면 합니다.

  1. 로그아웃 시 캐시 삭제:

    • useApolloClient를 활용해 로그아웃 시 캐시를 삭제하는 것은 좋은 접근법입니다. 하지만 이 과정에서 다른 API 요청이 재발생하는 문제는 로그아웃 시점에서만 일어나는 무효화 처리에 대해서는 적절할 수 있습니다.
    • 로그인 상태와 로그아웃 상태를 명확히 구분하고, 로그아웃 상태에서는 사용자 특정 데이터를 참조하지 않도록 구현하는 것이 중요합니다.
  2. refetch 및 refetchQueries 사용:

    • 각 탭마다 별도의 API 호출을 방지하길 원하신다면, refetchrefetchQueries 사용은 신중해야 합니다. 특정 유저 데이터에 의존하는 쿼리의 경우, 해당 유저가 로그아웃한 후 다시 로그인했을 때만 refetch하는 것이 좋을 수 있습니다.
    • 로그인 시점에만 특정 쿼리를 refetch하는 전략이 더욱 효율적일 수 있습니다.
  3. purchase나 charge 같은 실시간 업데이트:

    • 결제 정보같은 경우는 동적으로 변하는 데이터이므로, client.writeQuery / readQuery 보다는 refetch로 실시간 데이터를 반영하는 방식이 더 적합할 수 있습니다. 사용자의 행동에 따라 중요한 데이터가 변하는 경우, 그 변경을 즉각 반영할 필요가 있기 때문입니다.
    • 로그인과 로그아웃은 상태 변화가 명확한 경우이므로, 로그인 직후에는 데이터를 새로 가져오고 로그아웃시에는 캐시를 클리어하는 방식이 추천됩니다.

이러한 접근 방식 외에도 Apollo Client는 네트워크 정책(network policies)을 재정의하고, 커스텀 훅을 통한 상태 관리 등 다양한 방법을 제공합니다. Jed Watson의 “Managing Apollo Client Local State with React Hooks”(Managing Apollo Client Local State with React Hooks)같은 자료를 참고하시면 추가적인 인사이트를 얻으실 수 있습니다.

인증 상태 변화가 전체 애플리케이션의 상태에 큰 영향을 미친다는 점을 고려할 때, 로그인과 로그아웃 처리는 전체 애플리케이션의 상태 관리 전략과 밀접하게 연관되어 있습니다. 따라서 상태 관리 전략을 명확히 하고 이를 Apollo Client 캐시 정책과 잘 통합하는 것이 중요합니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

초무님의 프로필 이미지
초무

작성한 질문수

질문하기