이미지 압축 어떻게 하는거죠?

김재형·2024년 4월 16일
0
post-custom-banner

이미지 압축 어떻게 하는거야..!

서버와 통신하는 작업중
서버에서 요구하는 조건중 이미지 데이터는 5mb 까지 제한이 있었다.
그걸 잊고 통신하던 중 형식에 맞지 않는다는 에러를 받게 되었는데…!

문제의 과정

문제의 과정은 다음과 같다.

 input.saveButtonTap
            .withLatestFrom(tapValidConbine)
            .throttle(.seconds(1), scheduler: MainScheduler.instance)
            .filter({ $0.2 != nil })
            .map({ content in
                return (contents: content.0, datas: content.1, productId: content.2!)
            })
            .flatMap { content in
                NetworkManager.uploadImages(model: ImageDataModel.self, router: .imageUpload, images: content.datas).map { result in
                    return (result, content.contents, content.productId)
                }
            }
            .bind { result in
                switch result.0 {
                case .success(let imageModel):
                    imageUploadScueess.accept((imageModel: imageModel, content: result.1, productId: result.2.identi))
                case .failure(let error):
                    networkError.accept(error)
                }
            }
            .disposed(by: disposeBag)

위와 같은 코드에서 이미지의 데이터를 통해 서버와 통신 하는 과정이다.
문제는 다음과 같았는데

”ErrorCode: 400”
해당 코드는 문서에 의하면,

”파일의 제한 사항과 맞지 않습니다. ” 혹은
“파일을 보내지 않았지만 content-type을 application/json 으로 요청할 경우”

나같은 경우 이미지가 있어야 저로직이 들어가기에
”파일의 제한 사항과 맞지 않습니다.” 가 해당 되겠다고 판단했다.

파일의 제한 사항과 맞지 않을 경우를 알아보자면,
1. 확장자: jpg, jpeg, png, gif, pdf 로 제한
2. 용량: 5mb 제한
3. 파일갯수 : 5개

다음과 같은데 확장자는 jpeg로 통일하였었기 때문에 해당하지 않는 문제라 판단하였고,
파일 갯수 마저 최대 5개로 제한 하였기 해당 문제도 아니라고 판단하였다.

그렇다면 남은 문제는 2. 용량 문제 인데
출력을 통해 판단해보면

// 각 데이터 용량 체크 data.count 
[5903211 bytes, 3425951 bytes, 4395344 bytes, 2275097 bytes]

문제 판단 완료 -> 해결과정

5903211 bytes 는 mb로 환산해보면 5.903211 mb가 나오게 되는데
해당하는 문제로 인해 문제가 발생 하였음을 추측 할수 있다.
그렇다면 압축을 진행해 보고자 한다.

이미지 리사이징으로?

이미지 리사이징을 통해 이미지 용량을 줄일수 있다고 한다

  • 픽셀수 감소 효과 : 이미지의 해상도를 낮추는 방법이기에 픽셀수가 감소하게 됨으로 용량이 줄어든다.
    예) 9000X9000 → 1920 x 1080

하지만 내가 원하는 바는 이미지 자체의 크기( W and H ) 은 유지하고 압축을 원하였기에
이번에는 이미지 리사이징 방법으로 접근을 하지 않았다.

jpegData(compressionQuality:)

JPEG 포멧으로 이미지를 압축시키는데
0.0 ~ 1.0 사이의 압축비율을 가지게 할수 있다.

해당하는 방법으로 이미지 압축을 세밀하게 조절해 보고자 한다.

JPEG Data CompressionQuality

해당하는 메서드를 통해
간단하게 한다고 하면 퀄리티를 아주작게 1 ~ 4 사이 정도로 해서 5mb로 맞추어 줄순 있을것이다.
다….만….!
그렇게 접근 하게 된다면 이미지의 최적의 퀄리티가 아닌 모두 저퀄리티가 되어버리니
적절한 방법은 아니게 될것이라고 생각했다.

그리하여 진행하는 방법은 다음과 같다.

private func imageZipLimit(image: UIImage, zipRate: Double) -> Data? {
        let limitBytes = zipRate * 1024 * 1024
        print("클라이언트가 원하는 크기",limitBytes)
        var currentQuality: CGFloat = 0.95
        var imageData = image.jpegData(compressionQuality: currentQuality)
        
        while let data = imageData,
              Double(imageData!.count) > limitBytes && currentQuality > 0{
            print("현재 이미지 크기 :\(data.count)")
            currentQuality -= 0.05
            imageData = image.jpegData(compressionQuality: currentQuality)
            print("현재 압축중인 이미지 크기 :\(imageData!.count)")
        }
        
        if let data = imageData,
           Double(data.count) <= limitBytes {
            print("압축 \(data.count) bytes, 압축률: \(currentQuality)")
            return data
        } else {
            print("초과")
            return nil
        }
    }

코드는 생각 보다 간단하다

zipRate 는 클라이언트가 정한 예를들어 5.0 이라고 하였을때
5mb → 5242880 bytes 가 나와야 한다.
1 mb = 1024 x 1024 이기에 다음과 같은 식이 나오게 된다.

그리고 점진적으로 0.5씩 퀄리티를 줄이며 압축하여 최적의 퀄리티를 찾아
해당하는 퀄리티를 적용해 방출하는 방법으로 해당하는 문제를 해결하였다.

오늘의 돌아보기

사실 중간에 착각하여

1: [5093211 bytes, 3425951 bytes, 4395344 bytes, 2275097 bytes]

위와 같이 나왔을때 왜 5mb 를 초과했지 라고 착각하여
지연시간이 늘게 되었다.
후에 알고보니 5,242,880 바이트 라는것을 알아챘고 이런 기본적인것을 까먹을 수도 있구나
까먹지 않게끔 계속해서 자신을 돌아보아야 겠다고 생각하게 되었다.

profile
IOS 개발자 새싹이
post-custom-banner

0개의 댓글