📚 PHP 중급 - 4주차: 파일 업로드 및 이미지 처리 - 04 이미지 리사이징 및 썸네일 생성 (GD 라이브러리)

2025. 7. 27. 19:47프로그램/PHP 중급

📚 PHP 중급 - 4주차: 파일 업로드 및 이미지 처리 - 04 이미지 리사이징 및 썸네일 생성 (GD 라이브러리)

📚 PHP 중급 - 4주차: 파일 업로드 및 이미지 처리 - 04 이미지 리사이징 및 썸네일 생성 (GD 라이브러리)



이번 #PHP 중급 4주차에서는 #파일 업로드된 #이미지를 다루는 방법을 알아봅니다. 특히 #GD 라이브러리를 활용하여 이미지 #리사이징 및 #썸네일 생성하는 과정에 초점을 맞출 거예요. 웹 애플리케이션에서 사용자에게 다양한 크기의 이미지를 제공하거나, 페이지 로딩 속도를 최적화할 때 이미지 리사이징은 필수적인 기술이죠.

 


 

1. GD 라이브러리 이해하기

#GD 라이브러리는 PHP에서 이미지를 동적으로 생성하고 조작할 수 있도록 해주는 강력한 도구입니다. 서버에 #PHP와 함께 GD 라이브러리가 설치되어 있어야만 사용할 수 있어요. 대부분의 웹 호스팅 환경에는 기본적으로 설치되어 있지만, 혹시 작동하지 않는다면 서버 관리자에게 문의하거나 phpinfo() 함수를 통해 GD 라이브러리 활성화 여부를 확인할 수 있습니다.

GD 라이브러리는 다양한 이미지 형식을 지원하는데요, 주로 #JPEG, #PNG, #GIF 등의 형식을 다룰 수 있습니다. 각 형식에 맞는 함수를 사용하여 이미지를 읽고 쓸 수 있죠.

 


 

2. 이미지 리사이징 기본 원리

#이미지 리사이징은 원본 이미지의 크기를 변경하여 새로운 이미지를 생성하는 과정입니다. GD 라이브러리에서는 주로 다음과 같은 단계를 거쳐 이루어집니다.

  1. #원본 이미지 로드: imagecreatefromjpeg(), imagecreatefrompng(), imagecreatefromgif() 등 원본 이미지 형식에 맞는 함수를 사용하여 이미지를 메모리에 로드합니다. 이 함수들은 #이미지 리소스를 반환합니다.
  2. #새로운 캔버스 생성: imagecreatetruecolor() 함수를 사용하여 리사이징될 이미지의 새로운 폭과 높이를 가진 빈 캔버스를 생성합니다. 이 캔버스도 이미지 리소스 형태로 반환됩니다.
  3. #이미지 복사 및 리샘플링: imagecopyresampled() 함수를 사용하여 원본 이미지 리소스의 특정 부분을 새로운 캔버스에 복사하면서 동시에 크기를 조절합니다. 이 함수는 이미지 품질을 유지하면서 크기를 변경하는 데 매우 중요합니다.
  4. #새로운 이미지 저장: imagejpeg(), imagepng(), imagegif() 등 원하는 출력 형식에 맞는 함수를 사용하여 새로 생성된 이미지를 파일로 저장하거나 브라우저에 직접 출력합니다.
  5. #메모리 해제: imagedestroy() 함수를 사용하여 사용이 끝난 이미지 리소스를 메모리에서 해제합니다. 이는 서버 자원을 효율적으로 관리하는 데 중요합니다.

 


 

3. 이미지 리사이징 및 썸네일 생성 코드 구현

이제 실제 코드를 통해 이미지 #리사이징과 #썸네일 생성 과정을 살펴보겠습니다. 여기서는 간단한 파일 업로드 후 즉시 #썸네일을 생성하는 시나리오를 가정합니다.

PHP
 
<?php
// 업로드된 파일이 있는지 확인
if (isset($_FILES['uploadFile'])) {
    $uploadDir = 'uploads/'; // 이미지가 저장될 디렉토리
    if (!is_dir($uploadDir)) {
        mkdir($uploadDir, 0777, true); // 디렉토리가 없으면 생성
    }

    $fileName = $_FILES['uploadFile']['name'];
    $fileTmpName = $_FILES['uploadFile']['tmp_name'];
    $fileSize = $_FILES['uploadFile']['size'];
    $fileError = $_FILES['uploadFile']['error'];
    $fileType = $_FILES['uploadFile']['type'];

    $fileExt = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
    $allowedExt = ['jpg', 'jpeg', 'png', 'gif'];

    // 허용된 확장자인지 확인
    if (in_array($fileExt, $allowedExt)) {
        if ($fileError === 0) {
            if ($fileSize < 5000000) { // 5MB 미만
                $fileNewName = uniqid('', true) . '.' . $fileExt;
                $fileDestination = $uploadDir . $fileNewName;

                // 파일 업로드
                if (move_uploaded_file($fileTmpName, $fileDestination)) {
                    echo "파일이 성공적으로 업로드되었습니다: " . $fileNewName . "<br>";

                    // --- 이미지 리사이징 및 썸네일 생성 시작 ---

                    $thumbDir = 'thumbnails/'; // 썸네일이 저장될 디렉토리
                    if (!is_dir($thumbDir)) {
                        mkdir($thumbDir, 0777, true); // 디렉토리가 없으면 생성
                    }

                    $thumbWidth = 200; // 썸네일 폭
                    $thumbHeight = 200; // 썸네일 높이

                    // 원본 이미지 로드
                    $sourceImage = null;
                    if ($fileExt == 'jpg' || $fileExt == 'jpeg') {
                        $sourceImage = imagecreatefromjpeg($fileDestination);
                    } elseif ($fileExt == 'png') {
                        $sourceImage = imagecreatefrompng($fileDestination);
                    } elseif ($fileExt == 'gif') {
                        $sourceImage = imagecreatefromgif($fileDestination);
                    }

                    if ($sourceImage) {
                        $sourceWidth = imagesx($sourceImage);
                        $sourceHeight = imagesy($sourceImage);

                        // 비율 유지하며 썸네일 크기 계산
                        $ratio = max($thumbWidth / $sourceWidth, $thumbHeight / $sourceHeight);
                        $newWidth = $sourceWidth * $ratio;
                        $newHeight = $sourceHeight * $ratio;

                        // 새로운 캔버스 생성
                        $thumbImage = imagecreatetruecolor($thumbWidth, $thumbHeight);

                        // 투명도 유지 (PNG, GIF의 경우)
                        if ($fileExt == 'png' || $fileExt == 'gif') {
                            imagealphablending($thumbImage, false);
                            imagesavealpha($thumbImage, true);
                            $transparent = imagecolorallocatealpha($thumbImage, 255, 255, 255, 127);
                            imagefilledrectangle($thumbImage, 0, 0, $thumbWidth, $thumbHeight, $transparent);
                        }

                        // 이미지 복사 및 리샘플링 (비율에 맞춰 중앙 크롭 또는 여백 추가)
                        // 여기서는 간단하게 비율에 맞춰 줄이고, 썸네일 크기에 맞춰 중앙을 잘라내는 방식으로 구현
                        $sourceX = ($newWidth - $thumbWidth) / 2 / $ratio;
                        $sourceY = ($newHeight - $thumbHeight) / 2 / $ratio;

                        imagecopyresampled(
                            $thumbImage,       // 목적 이미지 (새로운 캔버스)
                            $sourceImage,      // 원본 이미지
                            0,                 // 목적 X 좌표
                            0,                 // 목적 Y 좌표
                            $sourceX,          // 원본 X 좌표
                            $sourceY,          // 원본 Y 좌표 (크롭을 위한 계산)
                            $thumbWidth,       // 목적 폭
                            $thumbHeight,      // 목적 높이
                            $sourceWidth,      // 원본 폭
                            $sourceHeight      // 원본 높이
                        );

                        // 썸네일 저장
                        $thumbFileName = 'thumb_' . $fileNewName;
                        $thumbDestination = $thumbDir . $thumbFileName;

                        if ($fileExt == 'jpg' || $fileExt == 'jpeg') {
                            imagejpeg($thumbImage, $thumbDestination, 90); // 90은 품질 (0-100)
                        } elseif ($fileExt == 'png') {
                            imagepng($thumbImage, $thumbDestination, 9); // 9는 압축 레벨 (0-9)
                        } elseif ($fileExt == 'gif') {
                            imagegif($thumbImage, $thumbDestination);
                        }

                        echo "썸네일이 성공적으로 생성되었습니다: " . $thumbFileName . "<br>";

                        // 메모리 해제
                        imagedestroy($sourceImage);
                        imagedestroy($thumbImage);

                    } else {
                        echo "지원하지 않는 이미지 형식입니다.<br>";
                    }

                    // --- 이미지 리사이징 및 썸네일 생성 끝 ---

                } else {
                    echo "파일 업로드에 실패했습니다.<br>";
                }
            } else {
                echo "파일 크기가 너무 큽니다 (5MB 제한).<br>";
            }
        } else {
            echo "파일 업로드 중 오류가 발생했습니다: " . $fileError . "<br>";
        }
    } else {
        echo "허용되지 않는 파일 확장자입니다.<br>";
    }
} else {
    echo "파일을 업로드해주세요.<br>";
}
?>

<form action="" method="post" enctype="multipart/form-data">
    <input type="file" name="uploadFile">
    <button type="submit">업로드</button>
</form>

 


 

4. 코드 설명 및 추가 고려사항

위 코드는 다음과 같은 단계를 거쳐 이미지 리사이징 및 썸네일 생성을 수행합니다.

  • #파일 업로드 처리: $_FILES 전역 변수를 사용하여 업로드된 파일의 정보를 얻고, 지정된 디렉토리로 파일을 이동시킵니다.
  • #이미지 형식 감지: pathinfo()와 in_array()를 사용하여 업로드된 파일의 확장자를 확인하고, GD 라이브러리가 지원하는 이미지 형식인지 판별합니다.
  • #원본 이미지 로드: imagecreatefromjpeg(), imagecreatefrompng(), imagecreatefromgif() 중 적절한 함수를 사용하여 원본 이미지를 메모리에 로드합니다.
  • #썸네일 크기 및 비율 계산: imagesx()와 imagesy()로 원본 이미지의 폭과 높이를 가져온 후, 원하는 썸네일 크기(thumbWidth, thumbHeight)에 맞게 #비율을 유지하며 새로운 폭과 높이를 계산합니다. 여기서는 썸네일 영역을 채우면서 원본 비율을 유지하기 위해 #중앙 크롭 방식을 사용하도록 imagecopyresampled의 원본 X/Y 좌표를 조절했습니다.
  • #새로운 이미지 캔버스 생성: imagecreatetruecolor() 함수로 썸네일 크기의 빈 이미지를 생성합니다.
  • #투명도 유지: PNG나 GIF와 같이 투명도를 지원하는 이미지의 경우, imagealphablending(false)와 imagesavealpha(true)를 설정하여 투명 배경이 손상되지 않도록 처리합니다.
  • #이미지 복사 및 리샘플링: imagecopyresampled() 함수가 핵심입니다. 이 함수는 원본 이미지의 픽셀을 새로운 캔버스에 복사하면서 동시에 크기를 조절하고, 픽셀 보간(interpolation)을 통해 이미지 품질을 향상시킵니다.
  • #썸네일 저장: imagejpeg(), imagepng(), imagegif() 함수를 사용하여 썸네일을 thumbnails/ 디렉토리에 저장합니다. JPEG의 경우 품질을, PNG의 경우 압축률을 지정할 수 있습니다.
  • #메모리 해제: imagedestroy()를 통해 사용된 이미지 리소스를 반드시 해제하여 메모리 누수를 방지해야 합니다.

 

추가 고려사항:

  • #보안: 파일 업로드 시에는 반드시 파일 확장자, MIME 타입 검사, 파일 크기 제한 등 #보안 검증을 철저히 해야 합니다. 악성 파일 업로드를 방지하기 위함입니다.
  • #오류 처리: 파일 업로드 및 이미지 처리 과정에서 발생할 수 있는 다양한 #오류에 대한 예외 처리를 강화해야 합니다.
  • #대용량 이미지 처리: 매우 큰 이미지를 처리할 때는 메모리 제한 문제를 고려해야 합니다. 필요에 따라 PHP의 memory_limit 설정을 조정하거나, 더 효율적인 이미지 처리 라이브러리(예: ImageMagick)를 검토할 수 있습니다.
  • #비동기 처리: 대량의 이미지 업로드 및 리사이징이 필요한 경우, 사용자 경험을 위해 이미지 처리를 #백그라운드에서 비동기적으로 처리하는 방안을 고려할 수 있습니다.

이번 주차에서는 #GD 라이브러리를 활용한 이미지 #리사이징 및 #썸네일 생성의 기초를 다뤄봤습니다. 이 기술은 실제 웹 서비스에서 이미지 처리 기능을 구현하는 데 핵심적인 역할을 할 거예요.