All files / src/lib validateImage.ts

8.92% Statements 5/56
100% Branches 0/0
0% Functions 0/2
8.92% Lines 5/56

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 571x 1x 1x                                                               1x 1x                                          
import { IMAGE_CONFIG } from './constants/image';
 
export const validateImage = async (file: File): Promise<{ valid: boolean; error?: string }> => {
  // 1. 확장자 검증
  const fileName = file.name.toLowerCase();
  const hasValidExtension = IMAGE_CONFIG.allowedExtensions.some((ext) => fileName.endsWith(ext));

  if (!hasValidExtension) {
    return {
      valid: false,
      error: `파일 확장자가 올바르지 않습니다. \n(${IMAGE_CONFIG.allowedExtensions.join(', ')}만 가능)`,
    };
  }

  // 2. 파일 크기 검증
  if (file.size > IMAGE_CONFIG.maxSizeBytes) {
    const currentSizeMB = (file.size / (1024 * 1024)).toFixed(0);
    return {
      valid: false,
      error: `이미지 크기가 너무 큽니다. 최대 20MB까지 가능합니다. \n현재: ${currentSizeMB}MB`,
    };
  }

  // 3. 파일 사이즈 검증
  const { width, height } = await getImageDimensions(file);
  if (width > IMAGE_CONFIG.maxWidth || height > IMAGE_CONFIG.maxHeight) {
    return {
      valid: false,
      error: `이미지는 ${IMAGE_CONFIG.maxWidth}x${IMAGE_CONFIG.maxHeight} 이하여야 합니다. \n현재: 너비(${width}), 높이(${height})`,
    };
  }

  return { valid: true };
};
 
const getImageDimensions = async (file: File): Promise<{ width: number; height: number }> => {
  return new Promise((resolve, reject) => {
    const img = new Image();
    const url = URL.createObjectURL(file);

    img.onload = () => {
      URL.revokeObjectURL(url); // 메모리 해제
      resolve({
        width: img.width,
        height: img.height,
      });
    };

    img.onerror = () => {
      URL.revokeObjectURL(url);
      reject(new Error('이미지 로드 실패'));
    };

    img.src = url;
  });
};