ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Javascript] 이미지 리사이징으로 용량 줄이기 (Canvas API)
    Java Script 2024. 3. 27. 13:43

    최근에 대한민국 철봉 지도라는 프로젝트를 진행하면서
    사용자가 지도에 철봉 위치를 등록하면서 주변 사진을 이미지로 업로드하고,
    업로드한 이미지를 다른 사용자들이 확인할 수 있는 기능을 구현하였습니다.

     

    이때 저희 서비스 특성상 고화질 이미지를 제공할 필요성이 크지 않고,

    거리뷰를 통해 확인할 수 있는 기능을 제공함으로써 서버의 부담을 줄이고, 로딩 속도를 올리기 위해

    이미지를 리사이징 하여 용량을 줄이기로 결정하였습니다.

    | Canvas API를 통한 이미지 리사이징

    먼저 축소하고자 할 비율과 파일을 받아 Promse를 리턴하는 resizeFile함수를 만들어 줍니다.

    const resizeFile = async (file: File, scale: number): Promise<File> => {
      return new Promise((resolve, reject) => {
        ...
      });
    };
    
    export default resizeFile;

     

    이제 리턴하는 해당 프로미스의 콜백함수 안에서
    Canvas API를 사용하여 이미지를 축소하고 내보내면 됩니다.

    const resizeFile = async (file: File, scale: number): Promise<File> => {
      return new Promise((resolve, reject) => {
        // 파일의 내용을 읽을 수 있도록 파일 리더 객체를 생성합니다.
        const reader = new FileReader();
        reader.onload = (e) => {
          // 새 이미지 객체를 생성합니다.
          const img = new Image();
          img.onload = () => {
            // 원하는 스케일을 적용하여 새로운 너비와 높이를 계산합니다.
            const width = img.width * scale;
            const height = img.height * scale;
    
            // 캔버스를 생성하고 크기를 설정합니다.
            const canvas = document.createElement("canvas");
            canvas.width = width;
            canvas.height = height;
    
            // 캔버스의 2D 컨텍스트를 가져옵니다.
            const ctx = canvas.getContext("2d");
            // 컨텍스트를 사용하여 새로 계산된 크기로 이미지를 조정합니다.
            ctx?.drawImage(img, 0, 0, width, height);
            // 캔버스의 내용을 바탕으로 파일로 변환할 Blob 객체를 생성합니다.
            canvas.toBlob((blob) => {
              if (blob) {
                // 새 파일 객체를 생성합니다. 파일 이름과 유형은 원본 파일에서 가져옵니다.
                const resizedFile = new File([blob], file.name, {
                  type: file.type,
                  lastModified: Date.now(),
                });
                // 생성된 파일 객체를 반환합니다.
                resolve(resizedFile);
              } else {
                reject(new Error("실패"));
              }
            }, file.type);
          };
          // FileReader 객체가 로드한 파일 데이터를 이미지 소스로 설정합니다.
          img.src = e.target?.result as string;
        };
        // FileReader에게 파일의 내용을 Data URL 형태로 읽도록 요청합니다.
        reader.readAsDataURL(file);
      });
    };
    
    export default resizeFile;

     

    이제 해당 함수를 사용하여 이미지를 업로드하고,
    파일 형식을 확인하여 미리 보기를 보여주는 기능을 아래와 같이 만들 수 있습니다.

    const handleImageChange = async (e: ChangeEvent<HTMLInputElement>) => {
        const suppertedFormats = [
          "image/jpeg",
          "image/png",
          "image/svg+xml",
          "image/webp",
        ];
    
        if (!e.target.files) return;
    
        if (!suppertedFormats.includes(e.target.files[0]?.type)) {
          setErrorMessage(
            "지원되지 않은 이미지 형식입니다. JPEG, PNG, webp형식의 이미지를 업로드해주세요."
          );
          return;
        }
    
        if (images.length + e.target.files.length > 5) {
          setErrorMessage("최대 5개 까지 등록 가능합니다!");
          return;
        }
    
        let file: File = await resizeFile(e.target.files[0], 0.8);
        let reader = new FileReader();
    
        reader.onloadend = () => {
          const imageData = {
            file: file,
            previewURL: reader.result as string,
            id: nanoid(),
          };
          setImages((prev) => [...prev, imageData]);
          formState.setImageForm(imageData);
        };
    
        if (file.size / (1024 * 1024) > 10) {
          setErrorMessage("이미지는 최대 10MB까지 가능합니다.");
          return;
        }
    
        setErrorMessage("");
    
        reader.readAsDataURL(file);
      };

    | 결과

    확실히 이미지의 크기를 줄이면 로딩 속도가 눈에 보이게 차이가 납니다.

    또한 용량이 줄어들기 때문에 서버의 부담도 줄일 수 있어 좋은 거 같습니다.

    리사이징 적용
    리사이징 미적용

    https://github.com/2YH02/img-toolkit

     

    GitHub - 2YH02/img-toolkit: A library for resizing and editing images

    A library for resizing and editing images. Contribute to 2YH02/img-toolkit development by creating an account on GitHub.

    github.com

     

Designed by Tistory.