프로그래밍 공부방

[Next.js] 리뷰 구현 중 생긴 문제점과 해결방법 본문

프론트엔드/Next.js

[Next.js] 리뷰 구현 중 생긴 문제점과 해결방법

김갱갱 2023. 6. 14. 06:38

🙀문제점

👉문제 내용

리뷰창을 띄운 후 리뷰를 작성할 수 있게 하는 기능을 구현하던 중에 생긴 문제가 생겼습니다.

리뷰 창에 별점을 줄 수 있는 기능이 있었는데 별점이 달라질 때마다 리뷰창이 사라졌습니다.

 

👉문제 원인

리뷰 점수 설정과 리뷰창 상태를 useState()로 설정을 해놨기 때문에 별점이 달라질 때마다 리렌더링되어서 리뷰창이 사라지는 것이었습니다.

const [reviewState, setReviewState] = useState("false"); // 리뷰창 상태
const [reviewScore, setReviewScore] = useState(1); // 리뷰 점수코드 작성

✨해결 방법

리뷰창의 상태를 useState()만으로 설정하지 않고 localstorage에도 저장해주는 방식으로 문제를 해결했습니다.

const ChatDetail: NextPage = () => {
  const { user } = useUser();
  
  const [reviewState, setReviewState] = useState("false"); // 리뷰창 상태
  const [reviewScore, setReviewScore] = useState(1); // 리뷰 점수

  useEffect(() => {
    socket.on("review", (msg: any) => {
      if (user?.id === msg) {
        localStorage.setItem("reviewState", "true");
        setReviewState("true");
      }
    });

    return () => {
      socket.off("review");
      localStorage.removeItem("reviewState");
    };
  }, [user]);
  
  useEffect(() => {
    if (data?.ok) {
      localStorage.setItem("reviewState", "false");
      setReviewState("false");
    }
  }, [data]);

  useEffect(() => {
    let state = localStorage.getItem("reviewState");
    if (!state) return;
    setReviewState(state as string);
  });

  const preventClose = (e: BeforeUnloadEvent) => {
    localStorage.setItem("reviewState", "false");
  };
  useEffect(() => {
    (() => {
      window.addEventListener("beforeunload", preventClose);
    })();

    return () => {
      window.removeEventListener("beforeunload", preventClose);
    };
  }, []);

  return (
    <>
      {reviewState === "true" ? (
        <div
          className={cls(
            "flex flex-col justify-center items-center fixed left-0 top-0 z-10 h-screen w-full bg-black/[0.7]",
            reviewState === "true" ? "" : "hidden"
          )}
        >
          <form
            onSubmit={handleSubmit(onValid)}
            className="bg-orange-500 w-10/12 max-w-xl h-full max-h-[500px] p-5"
          >
            <h1 className="text-gray-200 font-bold text-center text-xl pb-3">
              거래 후기를 남겨주세요
            </h1>
            <div className="flex h-1/6">
              {[1, 2, 3, 4, 5].map((star, i) => (
                <svg
                  key={star}
                  className={cls(
                    "h-full w-16",
                    reviewScore >= star ? "text-yellow-400" : "text-gray-400"
                  )}
                  xmlns="http://www.w3.org/2000/svg"
                  viewBox="0 0 20 20"
                  fill="currentColor"
                  aria-hidden="true"
                  stroke="#000000"
                  onClick={() => {
                    setReviewScore(i + 1);
                  }}
                >
                  <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
                </svg>
              ))}
            </div>
            <textarea
              placeholder="후기 작성"
              className="w-full h-1/2 resize-none outline-none px-3 py-2"
              {...register("review", { required: true, minLength: 1 })}
            />
            <button className="w-full bg-orange-300 hover:bg-orange-400 text-white px-4 py-3 text-base border border-transparent rounded-md shadow-sm font-medium focus:ring-2 focus:ring-offset-2 focus:ring-orange-500 focus:outline-none mb-2">
              등록
            </button>
            <button
              className="w-full bg-gray-400 hover:bg-gray-500 text-white px-4 py-3 text-base border border-transparent rounded-md shadow-sm font-medium focus:ring-2 focus:ring-offset-2 focus:ring-orange-500 focus:outline-none"
              onClick={() => {
                localStorage.setItem("reviewState", "false"); // localstorage에도 reviewState 저장
                setReviewState("false"); 
              }}
            >
              취소
            </button>
          </form>
        </div>
      ) : null}
    </>
  );
};

 

localstorage에 리뷰상태를 저장하고 useState의 reviewState 변수에 해당 값을 넣어주기 때문에,

localstorage에 계속 값이 true로 남아있으면 다른 채팅창을 들어가도 review창이 뜨는 문제가 발생할 수 있습니다.

  useEffect(() => {
    let state = localStorage.getItem("reviewState");
    if (!state) return;
    setReviewState(state as string);
  });

 

따라서 unmount될 때 localstorage에 있는 reviewState값을 제거하도록 설정해주었습니다.

👉 useEffect()의 return() 안에 들어가는 코드는  [ ] 안에 있는 값이 달라지거나 해당 component가 unmount될 때 실행됩니다.

  useEffect(() => {
    socket.on("review", (msg: any) => {
      if (user?.id === msg) {
        localStorage.setItem("reviewState", "true");
        setReviewState("true");
      }
    });

    return () => {
      socket.off("review");
      localStorage.removeItem("reviewState");
    };
  }, [user]);

✨ 결과물

별점을 바꿔줘도 리뷰창이 변화없이 잘 떠있는 것을 확인할 수 있습니다!