프로그래밍 공부방

[Next.js] 당근마켓 프로젝트 성능 최적화 과정3 - 이미지 업로드 후 게시글 올리는 작업 본문

프론트엔드/Next.js

[Next.js] 당근마켓 프로젝트 성능 최적화 과정3 - 이미지 업로드 후 게시글 올리는 작업

김갱갱 2023. 7. 9. 02:02

🐥 당근마켓 프로젝트 최적화 과정3

안녕하세요. 오늘은 당근마켓 프로젝트 최적화 과정 세 번째입니다!

이미지를 업로드해서 게시글을 올리는 작업을 최적화 해보겠습니다.


1. 이미지를 압축시켜서 렌더링 속도 최적화하기
2. 적절한 작업 순서를 고려해서 최적화하기
3. 블러 처리된 이미지 먼저 보여주기
4. Layout shift를 줄여서 CLS 개선시키기

 

위 작업들을 통해서 성능 최적화를 해보도록 하겠습니다!

 

1. 이미지를 압축시켜서 렌더링 속도 최적화하기

이미지를 압축시키는 작업은 browser-image-compression을 이용했습니다.

option에는 maxSizeMB, maxWidthOrHeight이 있습니다.

maxSizeMB는 1로 설정을 해서 이미지의 용량이 1MB를 넘지 않도로 설정했습니다.

maxWidthOrHeight은 576로 설정을 해서 576px을 넘지 않도록 설정했습니다.

(제 웹사이트에서 576px을 넘는 이미지는 필요없기 때문에 다음과 같이 설정한 것입니다.)

import imageCompression from "browser-image-compression";

const options = {
    maxSizeMB: 1,
    maxWidthOrHeight: 576,
};
const compressedFile = await imageCompression(productImage[0], options);
const file = new File([compressedFile], compressedFile.name, {
	type: "image/jpeg",
});

위와 같의 이미지 압축 작업을 진행해주면 아래와 같이 용량이 줄어든 것을 확인할 수 있습니다.

압축시키가 전의 이미지가 9.57MB고 압축 후의 이미지는 618.5KB입니다.

 

←압축시키기 전     압축시킨 후→
s3에 올라간 이미지

2. 적절한 작업 순서를 고려해서 최적화하기

기존에는 제품의 이미지를 선택하고 게시글 올리기를 클릭했을 때 해당 이미지의 압축과 aws로의 업로드가 진행되게 설정했습니다.

하지만 이렇게 했을 경우의 문제점!!

사용자가 클릭한 직후 이미지 압축이 진행되고 aws 업로드까지 끝난 후에 화면이 넘어가기 때문에 업로드까지의 속도가 사용자가 느끼기에 느리다고 생각이 들 정도였습니다. 또한 미리보기 이미지 업로드도 압축을 진행하기 전의 이미지였기 때문에 오래 걸렸습니다.

 

따라서 이를 해결하기 위해 file에서 이미지를 선택하자마자 압축시키고 미리보기 이미지로 출력시켰습니다. 그 후에 게시글 올리기를 클릭했을 때는 이미 압축된 이미지를 가지고 aws 업로드만 진행하도록 바꿔줬습니다.

  const onValid = async ({ name, price, description }: UploadProductForm) => {
    if (loading || !uploadUrl || !compressedFile) return;
    const {
      nFilename,
      image: { url, fields },
    } = uploadUrl;
    const fd = new FormData();
    Object.entries({ ...fields, file: compressedFile }).forEach(([key, value]) => {
      fd.append(key, value as string);
    });
    const uploadImage = await fetch(url, { method: "POST", body: fd });
    if (!uploadImage.ok) return;
    uploadProduct({
      file: uploadImage && uploadImage.ok ? nFilename : null,
      name,
      price,
      description,
    });
  };
  useEffect(() => {
    if (data?.ok) {
      router.replace(`/products/${data.product.id}`);
    }
  }, [data, router]);
  const [productPreview, setProductPreview] = useState("");
  useEffect(() => {
    const compressImage = async () => {
      const options = {
        maxSizeMB: 1,
        maxWidthOrHeight: 576,
      };
      const compressedFile = await imageCompression(productImage[0], options);
      const file = new File([compressedFile], compressedFile.name, {
        type: "image/jpeg",
      });
      setProductPreview(URL.createObjectURL(file));
      setImageInfo({ name: productImage[0].name, type: productImage[0].type });
      setCompressedFile(file);
    }

    if (productImage && productImage.length > 0) {
      compressImage();
    }
  }, [productImage]);

최적화 전

게시글을 올릴 때 압축 작업을 진행하기 때문에

게시글 업로드 클릭 후  다음 화면으로 넘어가는데까지 약 2초

게시글 올리기를 클릭했을 때 이미지의 압축과 AWS 업로드가 진행되는 모습
미리보기 이미지를 띄우는데까지 오래걸리는 모습

최적화 후

작업 순서를 바꾼 후에는 압축 작업을 게시글을 올릴 때 진행하는 것이 아닌 그 전에 끝마치기 때문에

게시글 업로드 클릭 후 다음 화면으로 넘어가는데까지 약 1.2초

3. 블러 처리된 이미지 먼저 보여주기

이미지가 완전히 업로드되기 전에 블러 처리된 이미지를 먼저 보여주도록 설정했습니다.

Next.js의 Image를 이용한다면 위 작업을 간단하게 할 수 있습니다.

placeholder를 blur로 설정해주고 blurDataURL에 블러 처리된 이미지를 가져올 주소를 적어주면 blur가 적용됩니다!

 

<Image
  priority={true}
  loader={() => {
    return data.product.image";
  }}
  src={data.product.image}
  placeholder="blur"
  blurDataURL={data.product.image}
  layout="fill"
/>

4. Layout shift를 줄여서 CLS 개선시키기

이미지 업로드 크기와 주어진 영역의 크기가 달라서 layout Shift가 발생했습니다.

따라서 layout shift 해결하기 위해 미리 이미지의 크기와 같은 공간을 할당해주었습니다.

그리고 같은 크기의 공간을 할당해줘도 border 때문에 width가 2px정도 차이가 생겨서 업로드할 이미지와 크기 차이가 조금 생겼습니다. 이것 또한 layout shift에 영향을 주기 때문에 inner-border처럼 변경시켜서 문제를 해결했습니다.

최적화 전

layout shift 발생

최적화 후

layout shift 없어짐


✨결과물

게시글 업로드 작업 최적화 전 후 차이

제가 최적화 후를 캡쳐할 때 좀 더 빠르게 입력해서 ㅠㅠ 업로드 되는 속도나 레이아웃 변경 같은 걸로 확인해주세욥..ㅎㅎ

최적화 전 후 시간 차이

painting에서 제일 개선이 많이 됐습니다!! 끝~~