state는 스냅샷처럼 동작합니다.

modric·2024년 3월 19일
0

세이치즈

목록 보기
4/9
post-thumbnail
post-custom-banner

문제 상황

form으로 이미지 받으면 이것을 리덕스에 저장하고 전역으로 접근하게 했습니다.

왜냐하면 이미지를 업로드하는 이 form 컴포넌트와 이미지를 사용할 canvas 컴포넌트는 서로 형제 컴포넌트이기 때문입니다.

리덕스에 이미지가 올라간 것을 확인했지만, 즉 form에 이미지가 제출되었으나 이것을 지켜보고 있는 미리보기에서는 이미지가 재깍재깍 바뀌지 않고 있습니다.

이미지 재깍재깍 안 바뀌는 에러
와우 진짜 오랜만에 보는 초창기 프레임 에디터 모습이다

이미지 state가 바로 반영 되지 않는 이유

리액트는 지금 내가 보는 화면이 영상이 아니라 사진과 같기 때문에 지금 제출한 state는 렌더링한 다음 사진(화면)에 반영될 뿐이다.

// 프레임 배경을 바꾸는 툴 컴포넌트입니다

export default function BgColor() {
  ... 생략 ...
  const [imgFile, setImgFile] = useState();
  const imgRef = useRef();

  const { bgColor, bgImg } = useSelector((store) => store.frame); // redux에서 bgImg를 가져오고 있습니다.
  const dispatch = useDispatch();

  // 이미지 업로드 input의 onChange
  const saveImgFile = () => {
    const file = imgRef.current.files[0];
    const reader = new FileReader();
    // 이미지 파일을 읽어들이는 비동기적인 작업을 수행합니다.
    reader.readAsDataURL(file);
    // FileReader의 onloadend 이벤트 핸들러가 호출될 때까지 기다립니다.
    reader.onloadend = () => {
      // reader.result에는 이미지 파일의 데이터 URL이 포함됩니다.
      // 이 데이터 URL을 imgFile 상태에 저장합니다.
      // 이때, React는 현재 시점에서의 상태 스냅샷을 유지하며, 변경 사항은 다음 렌더링 사이클에 반영됩니다.
      // 즉, 이 시점에서의 상태는 이후의 변경 사항이 반영되지 않은 과거의 스냅샷과 같습니다.
      setImgFile(reader.result); // 지금 리액트는 동영상이 아니라 멈춰진 사진과 같다는 것!!
      // 변경된 이미지 파일과 사용자가 선택한 배경색을 포함하는 payload를 생성합니다.
      const payload = { color: customColor, image: reader.result };
      // 생성된 payload를 이용하여 Redux 액션을 디스패치합니다.
      // 이 액션은 상태를 업데이트하고, 이후에 변경된 상태가 다음 렌더링 사이클에 반영됩니다.
      dispatch(Repaint(payload));
    };
  };
      ////////// 핵심 /////////////// 
      // image: src={imgFile}였기 때문에 재깍재깍 바뀌지 않았다
      // 리액트 컴포넌트는 일종의 스냅샷이기때문에 지금 이상태에서
      // reader.result와 imgFile은 서로 다르다!!!
      // 그래서 바로바로 반영이 안되고 딜레이가 생기는 것이었다
  return (
    <>
      ...생략...
      <div>이미지 미리보기 </div>
      <img width="100px" src={bgImg} /> 
    </>
  );
}

React 컴포넌트는 일종의 스냅샷으로 볼 수 있습니다. 이 말은 현재 상태를 반영하는 변수가 바로 반영되지 않을 수 있다는 것을 의미합니다.
처음에는 이미지 파일을 업로드할 때 imgFile 상태를 변경하고, 그 상태를 이용하여 배경을 렌더링하고자 했습니다. 그러나 이러한 방식은 우리가 예상한 대로 작동하지 않았습니다. 그 이유는 imgFile이라는 상태는 실제 이미지 파일의 스냅샷을 반영하지 않기 때문입니다.
이미지 파일을 업로드할 때, FileReader를 사용하여 이미지를 읽고, 그 결과를 imgFile 상태에 반영합니다. 그러나 React는 이러한 변경 사항을 즉시 감지하지 못하고, 다음 렌더링 사이클에서 변경 사항을 반영합니다. 이로 인해 이미지 파일이 변경되었음에도 불구하고 실제로는 다른 스냅샷이 반영되어 딜레이가 발생합니다.

따라서 이러한 상황에서는 reader.result와 같이 변경 사항이 즉시 반영되는 값으로 작업해야 합니다.

참고

https://react-ko.dev/learn/state-as-a-snapshot

회고1

프로젝트 초기 반나절 동안 골머리를 앓았던 문제이다.
ㅎㄹ 코치님의 "state는 스냅샷" 이라는 인사이트가 없었다면 이 문제를 해결하는데 많은 시간을 소요했을 것이다.

profile
Github: s01k1m / Email: sk618dev@gmail.com
post-custom-banner

0개의 댓글