geeks-lab / practicing_mern

[Krafton Jungle week12-13] Self Study: How to load images faster in a web application with MERN + AWS Lambda + AWS S3 presignedUrl
0 stars 0 forks source link

Sec 9. Pagination & Infinite Scroll #24

Open geeks-lab opened 10 months ago

geeks-lab commented 10 months ago
geeks-lab commented 10 months ago

이미지 여러개 한번에 업로드하기

Back

image

이렇게 image를 그냥 리턴해주면 안됩니다. 맵에서 하나씩 돌면서 리턴하는 값은 image인데 async 라서 promise를 반환합니다. 따라서 promise.all로 감싸줘야합니다. 그러면 promise 배열을 리턴합니다. 각 promise는 이미지를 리턴합니다.

그리고 Promise.all 앞에 await 를 해주면 거의 병렬적으로 처리됩니다.

// server/routes/imageRouter.js
imageRouter.post("/", upload.array("image", 5), async (req, res) => {
  try {
    if (!req.user) throw new Error("권한이 없습니다.");
    const images = await Promise.all(
      req.files.map(async (file) => {
        res.json();
        const image = await new Image({
          user: {
            _id: req.user.id,
            name: req.user.name,
            username: req.user.username,
          },
          public: req.body.public,
          key: file.filename,
          originalFileName: file.originalname,
        }).save();
        return image;
      })
    );
    res.json(images);
  } catch (error) {
    console.log(error);
    res.status(400).json({ message: error.message });
  }
});

Front

Error

여러개 올리는게 이제 가능하긴 한데 마지막 사진을 보면 정상적으로 보이지 않습니다.

image

image

geeks-lab commented 10 months ago

여러 이미지 미리보기

현재는 여러개를 업로드하려고 선택해도 하나의 프리뷰만 보이기 때문에 선택한 이미지들 모두 프리뷰가 보일 수 있게 합니다.

image

이전에 썼던 코드 초록 부분을 보면 비동기 적으로 처리하고 있는 모습이다.(그래야 이미지가 업로드 되는 동안 다른 사진을 구경한다던지 유저가 다른 일을 할 수 있으니까) 그런데 지금 맵 안에서 감싸줬는데 비동기적으로 처리되니까 배열안에 하나의 사진만 들어가게 됩니다.

그보다, 한번 업로드 될 때 배열에 여러개의 사진을 한번에 올리고 싶기 때문에 여러개의 사진이 다 올라갈 때 까지 기다려줘야 합니다.(지난번에 back에서 비슷한 문제를 처리한 적이 있습니다!)

따라서 Promise.all로 감싸줍니다.

image

그렇지만 안의 코드가 promise 를 반환하지 않기 때문에 promise 를 직접 만들어 줘야 합니다.

  const imageSelectHandler = async (event) => {
    const imageFiles = event.target.files;
    setFiles(imageFiles);
    // 여러 이미지 보기
    const imagePreviews = await Promise.all(
      [...imageFiles].map(async (imageFile) => {
        return new Promise((resolve, reject) => {
          try {
            const fileReader = new FileReader();
            fileReader.readAsDataURL(imageFile);
            fileReader.onload = (e) => resolve(e.target.result);
          } catch (error) {
            reject(error);
          }
        });
      })
    );

이 문법 쉽지 않다...

image

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

geeks-lab commented 10 months ago

Infinite Scroll(feat. useRef & useCallback)

IntersectionObserver Intersection Observer API의 IntersectionObserver 인터페이스는 대상 요소와 상위 요소, 또는 대상 요소와 최상위 문서의 뷰포트가 서로 교차하는 영역이 달라지는 경우 이를 비동기적으로 감지할 수 있는 수단을 제공합니다.

useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      entry.isIntersecting;
    });
  }, []);

ImageList 함수 내부의 useEffect 모습

  useEffect(() => {
    if (!elementRef.current) return;
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        loadMoreImages();
      }
    });
    observer.observe(elementRef.current);
    return () => observer.disconnect(); // when the mouse wheel goes up, the same observer
    // is makeing the error sinsce they are calling the same images
  }, [loadMoreImages]);
geeks-lab commented 10 months ago

이미지 업로드시 발생하는 버그 개선(progress bar, preview)