logo

이미지 용량 리사이징

Server Side April 24, 2019

웹페이지를 운영하거나 서비스하다보면 사용자를 통해 여러 파일이 서버에 업로드됩니다.

 

 

제일 많은 부분은 아마 이미지 파일일꺼고요. 예전과 다르게 요즘엔 고화질 이미지가 인터넷상에 널려있기 때문에 생각보다 이런 이미지파일이 모이고 모이면 서버용량에 부담이 생기구요. 또 사용자 입장에서 페이지 리스팅시 과도한 이미지가 노출이 되면 그만큼 웹서핑 환경에 부담을 주게됩니다. 결국 그 큰 이미지를 보내고 받는데 그만큼 시간이 드는거니까요.

 

 

위의 사진을 보면 정작 페이지를 구성하는 html 파일은 3.62KB불과한데 비해 페이지에서 보여주는 이미지는 용량이 60KB, 1.52MB 합니다. 분명 이미지가 중요한 사이트여도 리스팅 페이지에서 이런 리소스 로딩은 그만큼 서비스 품질을 떨어트리는 원이이 됩니다.

 

고도의 CMS 툴에선 이미 이미지 업로드 시 여러가지 이미지 크기로 파일을 분류해서 올리기도 합니다. 제 경험 기준으론 워드프레스에서 Featured Image 로 고화질 이미지를 업로드하면 150x150px 부터해서 300, 450, 900 이런식으로 여러 해상도 크기로 나누어서 총 5~6개의 파일을 업로드 합니다. 그리고 리스팅 페이지, 썸네일, 검색엔진 등에 최적화된 이미지를 제공하죠.

 

이런 CMS 환경을 사용하지 않고 직접 홈페이지를 구축, 운영하시는 분들은 신경써서 따로 구축을 해둬야하는 부분인거죠.

 

현실적인 접근방법은 두가지 입니다. 이미지의 절대 크기를 낮추냐? 아니면 특정 이미지 포맷으로 다시 이미지를 생성하냐? 입니다. 절대 크기를 낮춘다는 말은 원본 이미지의 픽셀 크기가 1920x1080px인 사진을 320x180px 등으로 다운 스케일링을 하는것입니다. 위에 예로든 워드프레스는 이 방식을 취급하고있죠.

 

 

근데 딱봐도 좀 복잡하긴합니다. 이미지 크기가 웹페이지 기획에 의해 고정적으로 결정된 경우라면 꼭 진행해야겠지만 CSS를 통해 자유자재로이미지를 관리할 수 있다면 전 후자의 방법을 추천합니다.

 

모든 이미지를 압축률 또는 품질을 조정해 다시 생성하는거죠. 여기서는 가장 이미지 압축률이 좋은 JPG(품질 75)를 이용할겁니다. 온라인 커뮤니티에서 흔히 볼 수 있는 화질구지가 거의 여기에 해당한다고 보면됩니다. 커뮤니티간에 업로드가 반복되면서 이미지의 품질이 점점 나빠지는거죠.

 

여튼 내용을 각설하고 아래 두가지 코드에서의 적용예를 한 번 봅시다.

 

 

 

 

첫 번째 경우로는 에디터의 이미지 업로드작업 시 다운 스케일링의 경우입니다.

 

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
<?php
    ...
    case 'img':
      
      if ($_FILES['POST_IMG']['name']) {
        if (!$_FILES['POST_IMG']['error']) {
              // 고유 파일명, 파일확장자 분리
              $temp_name = uniqid('', TRUE);
              $file_path_info = pathinfo($_FILES['POST_IMG']['name']);
              $file_extension = $file_path_info['extension'];
 
            // 파일 확장자 확인
              $allowed_ext = array('jpg''jpeg''png''gif');
              if (!in_array($file_extension$allowed_ext)) {
                echo 'ext';
                exit;
              }
      
              // 파일업로드
              $file_name = $temp_name . '.' . $file_extension;
              $path = '../imgs/' . $file_name;
              $file = $_FILES["POST_IMG"]["tmp_name"];
              move_uploaded_file($file$path);
              echo '/imgs/' . $file_name;
      
              // 업로드된 이미지파일 정보를 가져옵니다
              $file = getimagesize($path);
              // 저용량 jpg 파일을 생성합니다
              if ($file['mime'== 'image/png')
                $image = imagecreatefrompng($path);
              else if ($file['mime'== 'image/gif')
                $image = imagecreatefromgif($path);
              else
                $image = imagecreatefromjpeg($path);
      
              // 파일 압축 및 업로드
              $thumb_path = '../imgs/' . $temp_name . '_thumb.jpg';
              imagejpeg($image$thumb_path75);
              //echo '/imgs/' . $temp_name . '_thumb.jpg';
      
        } else {
              echo $_FILES['POST_IMG']['error'];
        }
      }
      break;
 
    ...
 
cs

 

Ajax 호출로 이미지가 'img' 명령을 통해 백엔드로 넘어왔을때 처리 구문입니다.

 

여기서 체크해야될 부분은 총 서버에 두개의 이미지가 업로드 된다는 점 입니다. 첫 번째 이미지는 사용자가 업로드한 원본 이미지고, 두 번째 이미지는 품질 75로 다운스케일링된 이미지입니다. 보통 이미지 업로드를 한다면 POST로 전송된 이미지 파일을 move_upload_file() 함수를 이용해 하게되는데 이 함수를 실행하면 $_FILE 변수가 가지고 있는 임시 이미지파일이 사라지게됩니다. 네 말그대로 "move"니까요.

 

그래서 업로드한 이미지 경로를 남겨놔서 작업을 시작해야합니다. 위의 경우에는 $path 변수에 업로드된 이미지파일의 경로를 가지고 있습니다. 이어서 코드를 보면 이미지 파일의 타입정보를 가져와서 각각에 맞는 이미지 오브젝트를 생성합니다. 그리고 마지막에 imagejpeg() 함수를 통해 JPG 이미지를 생성하고 업로드를 합니다.

 

imagejpeg() 함수가 이미지파일 생성기능도 하고 두번째 파라미터값의 경로에 파일쓰기도 바로 진행을 할 수 있습니다.

 

 

 

 

두 번째 경우로는 이미 서버상에 존재하는 이미지 파일들을 다운스케일링 하는 경우입니다.

 

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
<?php
$dir = '../../imgs/';
 
// 디렉토리 패스 체크
if (!is_dir($dir)) {
    echo '"' . $dir . '" is not directory. Check the directory path.';
}
 
// 디렉토리 열기
$dir_obj = opendir($dir);
// 디렉토리내에 파일명을 배열에 저장
$files = array();
while (($file = readdir($dir_obj)) != false) {
    // 리눅스에서만 보이는 .와 ..와 디렉토리를 제거한 나머지를 배열에 저장합니다.
    if ($file != '.' && $file != '..' && !is_dir($dir . $file))
        array_push($files$file);
}
 
foreach ($files as $file) {
    $path = $dir . $file;
    
    $file_path_info = pathinfo($path);
    $file_name = $file_path_info['filename'];
    $file_extension = $file_path_info['extension'];
 
    // 업로드된 이미지파일 정보를 가져옵니다
    $img = getimagesize($path);
    // 저용량 jpg 파일을 생성합니다
    if ($img['mime'== 'image/png')
        $image = imagecreatefrompng($path);
    else if ($img['mime'== 'image/gif')
        $image = imagecreatefromgif($path);
    else if ($img['mime'== 'image/jpeg')
        $image = imagecreatefromjpeg($path);
    else
        $image = null;
 
    // 파일 압축 및 업로드
    if (isset($image)) {
        $thumb_path = '../../imgs/' . $file_name . '_thumb.jpg';
        imagejpeg($image$thumb_path75);
        echo 'Complete resize image: ' . $thumb_path . '';
    }
}
 
// 디렉토리 닫기
closedir($dir);
cs

 

저는 기존에 이미 운영중인 서버의 이미지를 모두 다운스케일링이 필요해 위와같이 적용해봤습니다. 어찌보면 이미지 변환기같은 느낌이죠?

 

$dir 변수에 이미지가 잔뜩 들어있는 폴더의 경로를 지정하고 파일리스트를 만들어 작업을 진행합니다. 위의 코드랑 다른점이라면 이미지파일 말고도 다른 파일이 처리될 수 있는 경우에 대해 예외처리가 있다는 점 정도입니다.

 

 

실제 웹 서버에 있는 이미지는 위와 같이 존재했었고 경우 2의 코드를 러닝시키면

 

 

이런 작업 결과와 함께

 

 

모든 이미지 파일에 _thumb 이름이 붙은 다운스케일링 파일이 생성됩니다.

 

품질 75 설정에선 거의 기존크기에 비해 30%정도로 스케일링되었네요. 일부 파일에 대해서는 오히려 용량역전이 발생한경우도 있고요. 이런부분은 필요한 부분에 대해서 능동적으로 응용하면 될 것 같습니다.