[Swift 톺아보기] upload image which is converted binary in swift

kio·2022년 7월 10일
0

Swift

목록 보기
1/11
post-thumbnail


내가 개인 프로젝트 ( aka. Udong 네를 소개합니다.)를 하면서 만났던 문제중에 내가 가지고 있던 로컬 이미지를 binary형태로 전환하고, body에 넣어 AWSS3에 업로드해야되는 일이 있었다.

우선 코드 보기

코드는 크게 2개로 나뉜다. 업로드하는 service, 그걸 가져다 쓰는 viewcontroller
우선 viewcontroller를 볼려고 한다.

uploadImage.shared.uploadToBinary(image: willChangeImage ?? UIImage(), imagetype: .profileImage){ url in
            if let newName = self.textFieldForName.text {
               EditProfileService.shared.edit(ImageURL: url, ChangedName: newName)
            } else {
                EditProfileService.shared.edit(ImageURL: url, ChangedName: self.model?.username ?? "")
            }
        }

나는 함수를 만들면 사용법은 세상 간단해야 된다고 생각한다. 그래서 위와같이 짜봤는데 좀 쉬운가?
사용법은 uploadImage struct의 싱글톤 패턴으로 uploadToBinary를 호출에 image를 넣고 imageType을 넣는다.
여기서 imageType은

enum ImageType: String{
    case profileImage = "profileImage"
    case reviewImage = "reviewImage"
}

이렇게 업로드한 이미지의 분류를 나타내고, 원시값을 가지고 있어 s3의 폴더를 나누는 역할을 하기도 하는 열거형이다.

그 후 클로져를 통해 업로드한 이미지의 url을 뱉으면 그걸 통해 edit을 하는 방식이다. edit은 그냥 평범한 http PUT이다.

그럼 중요한 uploadImage를 보자

struct uploadImage {
    static let shared = uploadImage()
    
    func uploadToBinary(image: UIImage, imagetype: ImageType ,compeltion: @escaping (String) -> Void) {
        
        let semaphore = DispatchSemaphore (value: 0)
        
        var fileKey = String()
        
        let dateFormat = DateFormatter()
        dateFormat.dateFormat = "yyyyMMdd"
        fileKey += dateFormat.string(from: Date())
        fileKey += String(Int64(Date().timeIntervalSince1970)) + "_"
        fileKey += UUID().uuidString + ".png"
        print(fileKey)
        
        let imageToData = image.jpegData(compressionQuality: 1)
        
        let url = "http://요긴 비밀.s3.ap-northeast-2.amazonaws.com/\(imagetype.rawValue)/\(fileKey)"
        
        var request = URLRequest(url: URL(string: url)!)
        request.addValue("요긴 비밀", forHTTPHeaderField: "x-amz-server-side-encryption")
        request.addValue("image/jpeg", forHTTPHeaderField: "Content-Type")
        
        request.httpMethod = "PUT"
        request.httpBody = imageToData
        
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            guard let data = data else {
                semaphore.signal()
                return
            }
            semaphore.signal()
        }
        task.resume()
        semaphore.wait()
        compeltion(url)
    }
}

여기서 볼건 많진 않지만 몇개 짚으려고 한다.
우선 swift에서 image를 multipart/form-data가 아닌 binary로 하는 것은 많지 않았다.
그래서 나는 postman의 자동 코드 생성의 도움을 받아 위 코드를 작성했다.
우선 파일의 키를 현재 날짜와 타임인터벌을 통해 차이를 두었고, 폴더 명은 이미지 타입의 원시값을 사용해

let url = "http://요긴 비밀.s3.ap-northeast-2.amazonaws.com/\(imagetype.rawValue)/\(fileKey)"하세요

를 구했고, 그 다음 평범하게 session을 통해 통신했는데, 여기서 배운점은 semaphore이다.
학교 수업중에만 들었던 semaphore, 내가 기억하는 바는 멀티쓰레딩환경에서 동시 제어(concurrently)를 막기 위해 TAS, mutex lock 과 더불어 advanced한 방법이라고 알고 있다.
이걸 배웠을 때, 뭔가 iOS의 ARC와 비슷하다고 생각했다. reference count가 올라가는 방식으로 현재 해당 객체를 참조하는 것의 갯수를 확인한다면 semaphore는 이 자원이 사용되고 있으면 -1, 다 끝나으면 1을 반납하면 서로 공유자원 충돌이 일어나지 않게하는 방식이다.

extension DispatchSemaphore {

    public func signal() -> Int

    public func wait()

    public func wait(timeout: DispatchTime) -> DispatchTimeoutResult

    public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
}

설명을 간단히 한만큼 실제로 간단한 편이다

  • signal - 즉 나 이제 이 자원 사용할거야 하고 알리는 것이다.
  • wait - 나 이거 다썼으니까 반납할게~~ 하는 것이다

물론 쉽게 얘기한거지만 실제로 이런 느낌이다.
그리고 코드를 보자

let task = URLSession.shared.dataTask(with: request) { data, response, error in
            guard let data = data else {
                
                semaphore.signal()
                return
            }
            print("++++++++++++++++++++++++++++++++")
            print(semaphore.signal())
            print("++++++++++++++++++++++++++++++++")

        }
        task.resume()
        semaphore.wait()

task는 생성과 동시후에 클로져를 통해 "task 기본 셋팅이 끝났으니 1을 줄게 한번 써봐 (위에 시작 수를 0으로 해두었다)" 그래서 1을 int로 뱉는다
그리고 resume을 한다 그리고 resume이 끝나면 해당 1을 반납하고, 이제 아무도 사용할 수 없게 한다.

그럼 왜 semaphore을 사용할까???

위에서도 살짝 언급했지만 공유자원에 동시접근때문에 사용한다.
만약 semaphore가 없이 사용한다면 해당 테스크를 언제든 다른 쓰레드에서 또 쓸수있다. 그렇게 되면 mutate가 발생하게되고 원하던 결과와 다른 값이 될 수도 있다. 즉 같은 url로 다른 사진 사진이 써진다던가, 다른 url에 사진이 저장될수도있다.
그걸 막기위해 비동기 과정 속에서 이 부분만 동시적으로 atomic한 느낌으로 만들어준것이다 ( 어디까지나 느낌 )

이 글을 통해 ios bianry put in body를 쉽게 할 수 있었으면 좋겠습니다.
언제나 피드백 환영 ~~ 함다~~~

0개의 댓글