-
Notifications
You must be signed in to change notification settings - Fork 0
[REFACTOR] Kingfisher 기능을 대체하기 위한 ImageLoader 개발 #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[REFACTOR] Kingfisher 기능을 대체하기 위한 ImageLoader 개발 #97
Conversation
…tor/#92-Replacing-Kingfisher-with-a-custom-image-caching-and-resizing-module
0Hooni
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수고하셨습니다!! 몇가지 질문이 좀 있어서 Approve는 다음 리뷰로 넘기겠습니다
이미지 캐싱을 Kingfisher에 의존하지 않기 위한 매니저를 만드는것과, 이를 사용하고 읽기좋게 짜신 노력이 보여서 좋았습니다👍🏻
추가로 아마 캐시 저장에 제한을 걸 때 지금과 같이 주기적으로 처리해 주거나 데이터의 만료를 정해주는 방식 말고도 디바이스 용량의 n%로 거는 방식도 있는것으로 알고 있습니다! 이것도 같이 고민해보면 좋을것 같아요.
코멘트 외적으로 궁금한것이 있는데 같이 생각해봐주시면 감사하겠습니다
- 이러한 캐시 매니저들은 어느 레이어에 있어야 되는걸까요? 이건 저도 항상 고민인것 같아서 준영님의 의견이 궁금합니다.
| private func startCacheCleanup() { | ||
| DispatchQueue.global(qos: .background).async { [weak self] in | ||
| guard let self = self else { return } | ||
|
|
||
| let cleanTimer = Timer.scheduledTimer(withTimeInterval: 300, repeats: true) { _ in | ||
| let files = (try? self.fileManager.contentsOfDirectory(at: self.cacheDirectory, includingPropertiesForKeys: nil)) ?? [] | ||
|
|
||
| for file in files { | ||
| if file.pathExtension == "metadata", | ||
| let metadataData = try? Data(contentsOf: file), | ||
| let metadata = try? JSONSerialization.jsonObject(with: metadataData) as? [String: TimeInterval], | ||
| let expirationTime = metadata["expiration"] { | ||
|
|
||
| // 만료 시간이 지나면 이미지와 메타데이터 삭제 | ||
| if Date().timeIntervalSince1970 > expirationTime { | ||
| let imageFileURL = file.deletingPathExtension() // 메타데이터와 동일한 이름의 이미지 파일 | ||
| do { | ||
| try self.fileManager.removeItem(at: imageFileURL) | ||
| try self.fileManager.removeItem(at: file) // 메타데이터 삭제 | ||
| } catch { | ||
| print("Failed to delete expired cache: \(error)") | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| // 백그라운드에서 실행되는 타이머를 메인 루프에 추가 | ||
| RunLoop.current.add(cleanTimer, forMode: .common) | ||
| RunLoop.current.run() // 백그라운드 스레드에서 타이머를 계속 실행하기 위해 RunLoop를 유지 | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
캐시 클리닝을 주기적으로 처리하는 이유가 있을까요?
디스크 스토리지에 저장하는 경우에는 일반적으로 반영구적인 데이터를 캐싱하기 위해 사용되는 공간으로 알고 있는데, 주기적으로 삭제가 되면 디스크 캐싱이 갖는 이점(메모리 대비 큰 용량)을 잘 못 활용할 수 있다 생각합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
프린트문 보다는 로거로 대체해주시면 더 좋을것 같습니다!
마찬가지로 영훈님 의견과 마찬가지로 좀더 부가적으로 의견을 덧대자면
주기적인 처리보단 특정 한계치를 설정하면 이후부터 삭제되는 방향으로 고려하셔도 좋을듯합니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
타이머를 사용하여 주기적으로 확인할 필요가 없을 것 같기는 합니다만 특정 시점에 캐시를 정리하는 과정은 필요하다고 생각을 합니다 사용되지 않는 데이터를 가지고 있게되는 문제도 있을 것 같기도 하고 이미지가 수정되었음에도 동일한 URL을 가지고 있다면 수정사항을 반영할 수 없을 것 같아서 캐시를 정리하는 기능 자체는 필요할 것 같다고 생각합니다
| do { | ||
| try metadataData.write(to: metadataURL) | ||
| } catch { | ||
| print("Error writing metadata: \(error)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Print가 아마 추후 디버깅에 사용하시려고 남기신것 같은데 이 부분 Print대신 사용할 로그 방식에 대해 의논해서 로그로 대체하면 좋겠습니다!
로그 양식같은것도 고민해봐야겠네요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로그를 어떻게 남길까 고민하다가 일단 print문을 남겼는데 의논해 보면 좋을 것 같습니다 :>
| let cleanTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in | ||
| for key in self.cachedKeys { | ||
| let nsKey = key as NSString | ||
| if let cachedData = self.cache.object(forKey: nsKey), cachedData.isExpired() { | ||
| self.cache.removeObject(forKey: nsKey) | ||
| self.cachedKeys.remove(key) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
디스크는 인터벌이 5분이고 메모리는 1분인데 혹시 기반이 되는 근거가 있었을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
인터벌 및 이미지 사이즈의 기준은 일단 임의로 설정해 둔 값입니다! 기능 구현의 목적을 둔 초안이라 고려하지 않고 작성하였어요 ㅠ
| var memoryCacheExpiration: TimeInterval = 300 | ||
| var diskCacheExpiration: TimeInterval = 86_400 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
시간은 자체적으로 한번 지정한 뒤 바뀌지 않을것으로 생각되는데 맞을까요?
맞다면 var → let으로 하시면 좋을것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
모듈로 만들었을 때 AppDelegate에서
ImageLoader.shared.configure.memoryCacheExpiration = 360 이렇게 사용하려고 var로 선언하였습니다.! 영훈님 생각에는 let으로 선언하는게 좋을까요?
| var size: CGSize { | ||
| switch self { | ||
| case .low: | ||
| return CGSize(width: 100, height: 100) | ||
| case .middle: | ||
| return CGSize(width: 200, height: 200) | ||
| case .high: | ||
| return CGSize(width: 400, height: 400) | ||
| case .origin: | ||
| return CGSize(width: 1000, height: 1000) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
혹시 4단계로 분류된 이미지 사이즈의 기준이 뭐였었나요?
|
@0Hooni 저는 해당 모듈은 프로젝트와 분리하여 라이브러리로 만들어서 관리해 보는 것도 좋을 것 같습니다 이후 presentation layer에서 사용해보면 괜찮지 않을까 생각해요! |
…tor/#92-Replacing-Kingfisher-with-a-custom-image-caching-and-resizing-module # Conflicts: # Poppool/Poppool.xcodeproj/project.pbxproj # Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved # Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift # Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift # Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift
|
@0Hooni , @zzangzzangguy 리뷰 한 번 부탁드립니다! 그리고 ETC 내용 참고 부탁드리겠습니다! |
| private func startCacheCleanup() { | ||
| DispatchQueue.global(qos: .background).async { [weak self] in | ||
| guard let self = self else { return } | ||
|
|
||
| let cleanTimer = Timer.scheduledTimer(withTimeInterval: 300, repeats: true) { _ in | ||
| let files = (try? self.fileManager.contentsOfDirectory(at: self.cacheDirectory, includingPropertiesForKeys: nil)) ?? [] | ||
|
|
||
| for file in files { | ||
| if file.pathExtension == "metadata", | ||
| let metadataData = try? Data(contentsOf: file), | ||
| let metadata = try? JSONSerialization.jsonObject(with: metadataData) as? [String: TimeInterval], | ||
| let expirationTime = metadata["expiration"] { | ||
|
|
||
| // 만료 시간이 지나면 이미지와 메타데이터 삭제 | ||
| if Date().timeIntervalSince1970 > expirationTime { | ||
| let imageFileURL = file.deletingPathExtension() // 메타데이터와 동일한 이름의 이미지 파일 | ||
| do { | ||
| try self.fileManager.removeItem(at: imageFileURL) | ||
| try self.fileManager.removeItem(at: file) // 메타데이터 삭제 | ||
| } catch { | ||
| print("Failed to delete expired cache: \(error)") | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| // 백그라운드에서 실행되는 타이머를 메인 루프에 추가 | ||
| RunLoop.current.add(cleanTimer, forMode: .common) | ||
| RunLoop.current.run() // 백그라운드 스레드에서 타이머를 계속 실행하기 위해 RunLoop를 유지 | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
프린트문 보다는 로거로 대체해주시면 더 좋을것 같습니다!
마찬가지로 영훈님 의견과 마찬가지로 좀더 부가적으로 의견을 덧대자면
주기적인 처리보단 특정 한계치를 설정하면 이후부터 삭제되는 방향으로 고려하셔도 좋을듯합니다!
0Hooni
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!!
사실 킹피셔를 사용하는곳이 몇곳 없었네요
📌 이슈
✅ 작업 사항
🚀 테스트 방식
👀 ETC (추후 개발해야 할 것, 참고자료 등) ->