본문 바로가기
기술정리/Django

Django 이미지 최적화 하기

by bingual 2024. 4. 25.
반응형

이미지 최적화가 필요한 이유

 

초고화질인 이미지를 사용자가 업로드할 수 도 있다.

특히 갤러리의 경우 많은 이미지를 포함하게 되는데 위에 이미지와 같이 4.32MB 이상의 고용량의 이미지들이 누적되다 보면 그만큼의 리소스를 소비해야 한다. 따라서 이미지는 적당히 최적화할 필요성이 있다.

 

 

이미지 최적화 적용

helper.py

# 일관성 있게 이미지 저장
def uuid_name_upload_to(instance: models.Model, filename: str) -> str:
    app_label = instance.__class__._meta.app_label
    cls_name = instance.__class__.__name__.lower()
    ymd_path = force_str(timezone.now().strftime("%Y/%m/%d"))
    extension = splitext(filename)[-1].lower()
    new_filename = uuid4().hex + extension # 32자리의 난수 생성
    return "/".join((app_label, cls_name, ymd_path, new_filename)) # ex) accounts/profile/2024/04/25/filename


# 이미지 최적화
def make_thumb(
    image_file: File, max_width: int = 1024, max_height: int = 1024, quality=80
) -> File:
    pil_image = Image.open(image_file)
    max_size = (max_width, max_height)
    pil_image.thumbnail(max_size)

    # 만약 이미지가 RGBA(알파 채널 포함) 모드라면 RGB 모드로 변환
    if pil_image.mode == "RGBA":
        pil_image = pil_image.convert("RGB")

 
    thumb_name = splitext(image_file.name)[0] + ".jpg"
    
    # 썸네일 파일 생성: 비어있는 ContentFile 객체 생성 후 이름 설정
    thumb_file = ContentFile(b"", name=thumb_name)
    
    # PIL을 사용하여 이미지를 JPEG 형식으로 저장하고 지정된 품질로 설정
    pil_image.save(thumb_file, format="jpeg", quality=quality)

    return thumb_file

 


이미지 최적화는 이미지를 사용하는 모델에서 재사용할 수 있게 helper 모듈을 준비해 준다.

 

 

model.py

class Profile(LifecycleModelMixin, models.Model):
    user = models.OneToOneField(
        User,
        on_delete=models.CASCADE,
    )
    avatar = models.ImageField(upload_to=uuid_name_upload_to)

    @hook(BEFORE_SAVE, when="avatar", has_changed=True)
    def on_image_change(self):
        if self.avatar:
            image_width = self.avatar.width
            image_extension = splitext(self.avatar.name)[-1].lower()
			
            if image_width > 1024 or image_extension not in (".jpg", ".jpeg"):
                thumb_file = make_thumb(self.avatar.file, 1024, 1024, 80)
                self.avatar.save(thumb_file.name, thumb_file, save=False) # 모델 인스턴스에 저장

 

 

https://rsinger86.github.io/django-lifecycle/

django-lifecycle 라이브러리를 사용한다.


이미지가 저장되기 전 avatar필드의 변경사항이 있을 경우에만 호출되어 helper에서 정의한 함수를 이용해 이미지를 최적화한다.

실제 이미지를 저장하는 건 모델에서 처리할 거 기 때문에 avatar 필드를 업데이트만 해주고 저장은 수행하지 않는다.

 

 

 

 

이미지가 정상적으로 최적화되어 업로드된 것을 확인할 수 있다.

관련 커밋
https://github.com/bingual/django-5.0/commit/0019a090740a8d680225dfcac67d9642ef5f7763#diff-f6af2ff54b7b2982f691e19c61a9b92e22a98b70d50bef4e7c4af2ae032fb602

 

프로필, 프로필 수정 추가 · bingual/django-5.0@0019a09

bingual committed Apr 24, 2024

github.com