[Frontend] 이미지 최적화해서 보내기
2024. 9. 23.

서버 배포하고 cors 에러도 다 고쳤겠다 싶어서 다 끝난줄...알았으나 갑자기 백엔드 측에서 헬프를 쳐서 급하게 코드를 다시 수정하게 되었다.

일반 이미지는 다 괜찮은데, 이미지가 큰 파일의 경우 이미지가 첨부가 안된다는것....

500에러....

500에러가 뜨긴 했지만 느낌상 프론트 측에서 해결이 가능할 것 같았다.

아마 이미지 최적화 후(이미지 압축 or 이미지 리사이징) 서버에 보내면 수월하게 에러가 해결될 듯 싶었다.

이미지의 해상도를 유지하면서, 이미지의 크기를 줄여서 서버로 보내기 위해 이미지 압축을 이용해서 첨부된 이미지 압축 후 서버로 파일을 전송하기로 하였다.

라이브러리

browser-image-compression
npm i browser-image-compression --force

browser-image-compression 라이브러리를 사용했다.

해당 라이브러리는 아래와 같은 옵션이 있다.

const options = {
      maxSizeMB: 1, // 최대 파일 크기를 1MB로 제한
      maxWidthOrHeight: 1024, // 이미지의 최대 너비 또는 높이
      useWebWorker: true, // 웹 워커 사용
 };

web worker는 간단하게 말해 필요시 백그라운드에서도 스크립트를 사용하겠냐는 의미,

프론트엔드 인터페이스버 배포하고 cors 에러도 다 고쳤겠다 싶어서 다 끝난줄...알았으나 갑자기 백엔드 측에서 헬프를 쳐서 급하게 코드를 다시 수정하게 되었다.

 

일반 이미지는 다 괜찮은데, 이미지가 큰 파일의 경우 이미지가 첨부가 안된다는것....

500에러....

500에러가 뜨긴 했지만 느낌상 프론트 측에서 해결이 가능할 것 같았다.

 

아마 이미지 최적화 후(이미지 압축 or 이미지 리사이징) 서버에 보내면 수월하게 에러가 해결될 듯 싶었다.

 

이미지의 해상도를 유지하면서, 이미지의 크기를 줄여서 서버로 보내기 위해 이미지 압축을 이용해서 첨부된 이미지 압축 후 서버로 파일을 전송하기로 하였다.

 

라이브러리

browser-image-compression

npm i browser-image-compression --force

browser-image-compression 라이브러리를 사용했다.

 

해당 라이브러리는 아래와 같은 옵션이 있다.

const options = {

      maxSizeMB: 1, // 최대 파일 크기를 1MB로 제한

      maxWidthOrHeight: 1024, // 이미지의 최대 너비 또는 높이

      useWebWorker: true, // 웹 워커 사용

 };

web worker는 간단하게 말해 필요시 백그라운드에서도 스크립트를 사용하겠냐는 의미,

프론트엔드 인터페이스에는 딱히 방해되지 않으므로 true라고 디폴트 설정값 그대로 가져간 후에 이미지 압축 후 파일 전송을 적용해보자.

전체 코드

우선 전체 코드부터 작성

const handleImageChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files;
    
    // 총 이미지 갯수 제한 확인 (새로 추가된 이미지와 기존 이미지 포함)
    if (files && files.length + newImageFiles.length + prevImageUrls.length > 4) {
      Swal.fire(
        "Error",
        "이미지는 최대 4개까지 업로드할 수 있습니다.",
        "error"
      );
      return;
    }
  
    const fileArray = Array.from(files || []);
    
    const options = {
      maxSizeMB: 1, // 최대 파일 크기를 1MB로 제한
      maxWidthOrHeight: 1024, // 이미지의 최대 너비 또는 높이
      useWebWorker: true, // 웹 워커 사용
    };
  
    // 이미지 파일을 압축하고, 파일 이름을 정규화하여 새로운 File 객체로 반환
    const compressedAndSanitizedFiles = await Promise.all(
      fileArray.map(async (file) => {
        try {
          // 1. 이미지 압축
          const compressedFile = await imageCompression(file, options);
          
          // 2. 파일 이름 정규화
          const sanitizedFileName = compressedFile.name
            .normalize('NFKD') // 유니코드 정규화
            .replace(/[\s]/g, '-') // 공백을 대시(-)로 변환
            .replace(/[^a-zA-Z0-9.-]/g, ''); // 알파벳, 숫자, 점, 하이픈을 제외한 모든 문자 제거
          
          // 3. 새 File 객체 생성 (압축된 파일에 정규화된 이름 적용)
          return new File([compressedFile], sanitizedFileName, { type: compressedFile.type });
        } catch (error) {
          console.error('Error compressing the image:', error);
          return file; // 압축 실패 시 원본 파일 사용
        }
      })
    );
  
    // 미리보기 URL 생성
    const newUrls = compressedAndSanitizedFiles.map(file => URL.createObjectURL(file));
    
    // 새로운 이미지 URL 및 파일을 상태로 업데이트
    setNewImageUrls((prev) => [...prev, ...newUrls]);
    setNewImageFiles((prev) => [...prev, ...compressedAndSanitizedFiles]);
  };

fileArray는 첨부된 파일들이 담긴 배열이다. 해당 배열의 이미지 파일들을 하나하나 압축을 수행한다.

const options = {
      maxSizeMB: 1, // 최대 파일 크기를 1MB로 제한
      maxWidthOrHeight: 1024, // 이미지의 최대 너비 또는 높이
      useWebWorker: true, // 웹 워커 사용
    };
const compressedFile = await imageCompression(file, options);

options에서 세부사항 설정 후, 파일 배열의 파일들을 하나하나 가져와서 option을 적용하면 된다.

여기서 파일 이름의 정규화(sanitized)도 수행하기 위해 규칙을 더 선언해 주었다.

const sanitizedFileName = compressedFile.name
  .normalize('NFKD') // 유니코드 정규화
  .replace(/[\s]/g, '-') // 공백을 대시(-)로 변환
  .replace(/[^a-zA-Z0-9.-]/g, ''); // 알파벳, 숫자, 점, 하이픈을 제외한 모든 문자 제거
          
  return new File([compressedFile], sanitizedFileName, { type: compressedFile.type });

압축된 파일의 파일 이름을 정규화하기 위한 규칙을 세운 후, 이를 적용하기 위해 new File로 새 파일 객체를 만들고, 기존 압축된 파일의 파일 이름만 정규화된 파일 이름으로 바꾸어 주었다.

참고로 파일 이름도 한글/특수문자의 경우 s3에서 인식 못해서 에러가 발생할 수 있기 때문에(경험담....) 파일 이름도 sanitized한 후 파일을 첨부해서 전송해 보았다.

 

에러가 고쳐진 것을 확인할 수 있다.