React - 작성 중 데이터 보존하기

sarang_daddy·2023년 8월 8일
0

React

목록 보기
12/26
post-thumbnail

문제

상품을 등록하는 페이지에서 다른 페이지로 이동 후 돌아오면 작성했던 내용이 모두 사라지는 문제가 발생했다.

원인

React는 다른 페이지로 이동하면 현재 페이지의 컴포넌트는 언마운트되고, 새로운 페이지의 컴포넌트가 마운트 된다. 언마운트 과정에서 컴포넌트의 상태는 메모리에서 해제된다.
다시 이전 페이지로 돌아오더라도 해당 페이지의 컴포넌트는 다시 마운트 되기에 초기상태로 시작된다.

해결

본 프로젝트에서는 전역 상태 관리 라이브러리를 사용하지 않고 있기에 언마운트되는 상태를 보존하기 위해서는 로컬스토리지나 세션스토리지 같은 클라이언트 측 저장소를 사용해야 한다.

이번에는 브라우저를 실수로 닫아도 데이터가 유지될 수 있도록 로컬스토리지를 활용해서 해결해보자.

// 최상위 컴포넌트에서 관리되고 있는 상태 postObject
const [postObject, setPostObject] = useState<PostObjectType>(initialPostObject);

// 하위 컴포넌트들에서 postObject 상태를 변경 한다.
// useContext로 photo, title, price, comment, category에게 상태를 사용할 수 있게 한다.
const { postObject, setPostObject } = useContext(postSalesItemContext);
  • postObject 상태가 변경되면 변경된 값을 로컬스토리지에 저장한다.
// 최상위 컴포넌트에서 상태가 변경되면 로컬스토리지에 저장한다.
  useEffect(() => {
    const postObjectToStore = { ...postObject };
    localStorage.setItem('postObject', JSON.stringify(postObjectToStore));
  }, [postObject]);
  • 페이지 이동 후, 이전 페이지로 돌아오면 로컬스토리지에 저장된 값을 불러온다.
// 최상위 컴포넌트 마운트 시 로컬스토리지를 확인하고 저장값을 상태로 업데이트 해준다.
  useEffect(() => {
    const storedPostObject = localStorage.getItem('postObject');
    if (storedPostObject) {
      setPostObject(JSON.parse(storedPostObject));
    }
  }, []);
  • postObject 상태가 로컬스토리지 데이터로 업데이트 되면 하위 컴포넌트들도 값을 확인하고 데이터를 불러온다.
  useEffect(() => {
    if (postObject.files) {
      const images: UploadedImageType[] = [];
      for (const fileItem of postObject.files) {
        const fileData = JSON.parse(fileItem);
        images.push(fileData);
      }
      setUploadedImages(images);
    }
  }, [postObject]);


  useEffect(() => {
    if (postObject.title) {
      setHasInputValue(postObject.title);
    }
    if (postObject.categoryId) {
      setSelectedCategoryId(postObject.categoryId);
    }
  }, [postObject]);


  useEffect(() => {
    if (postObject.price) {
      const priceToString = postObject.price.toString();
      setInputPrice(priceToString);
    }
  }, [postObject]);


  useEffect(() => {
    if (postObject.content) {
      setTextareaValue(postObject.content);
    }
  }, [postObject]);
  • "닫기" 혹은 "완료" 버튼을 누르면 로컬스토리지에 저장된 값을 지워준다.
  const handleUploadComplete = async () => {

    // 중략
    await postProducts(formData, accessToken);
    localStorage.removeItem('postObject');
    navigation(-1);
  };


  const handleBackIconClick = () => {
    localStorage.removeItem('postObject');
    navigation(-1);
  };

어려웠던점

이미지를 로컬스토리지에 저장하는 부분에서 많은 시간이 걸렸다...🥲

  • formData 객체로 관리하고 있던 이미지 데이터(상태)
  • 로컬스토리지에는 문자열로 저장이 되어야 해서, FormData 객체를 저장할 수 없다.
  • 서버에 이미지 파일을 먼저 업로드하고 해당 파일의 URL을 받아오고 싶었으나, API의 수정이 어려운 상황
  • 클라이언트 측에서 해결방법을 고민하다 base64 인코딩으로 파일을 문자열로 변환해서 저장했으나 로컬스토리지의 용량 부족으로 여러장의 업로드가 불가했다.
  • 결국 이미지 데이터를 url(string) 값으로 관리하고 POST 요청 로직에서 객체화하여 formData로 보내는 방법으로 해결되었다.

해결 UI

profile
한 발자국, 한 걸음 느리더라도 하루하루 발전하는 삶을 살자.

1개의 댓글

comment-user-thumbnail
2023년 8월 8일

좋은 정보 얻어갑니다, 감사합니다.

답글 달기