# ๐Ÿ”ฅ๋ถˆํƒ€๋Š” ํ† ์š”์Šคํ„ฐ๋””_A๋ฐ˜๐Ÿ”ฅ [1์ฐจ ์ •๋ฆฌ ๋งํฌ](https://hackmd.io/gU8gQdVNQaWbhEnIBN20yw?edit) # ํ† ์š”์Šคํ„ฐ๋”” ์–‘์‹ ๐Ÿ”ฅ ๋ถˆํƒ€๋Š” ํ† ์š”์Šคํ„ฐ๋”” 9๊ธฐ A๋ฐ˜๐Ÿ”ฅ ๐Ÿ“† 2023.08.12 ํ† ) Week #14 ๐Ÿซ‚ ์Šคํ„ฐ๋””์›: Zion๐Ÿ‘‘ , MINT๐Ÿ˜ˆ , yyss99๐Ÿฃ , Yetti๐ŸฆŠ , redmango๐Ÿฅญ , jusbug๐Ÿ˜ƒ [๊ธˆ์ฃผ์˜ ์‹คํ—˜ ์ฃผ์ œ] : # 11์ฃผ์ฐจ_1: Protocol Oriented Programming ## ๋ฐœํ‘œ์ž Zion pop๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ํ•ต์‹ฌ ์ ์ธ ์š”์†Œ๋Š” ๊ธฐ๋Šฅ๋ณ„๋กœ ์ž‘์€ ์กฐ๊ฐ๋“ค๋กœ ๋‚˜๋‰œ ํ”„๋กœํ† ์ฝœ๊ณผ ๊ธฐ๋ณธ๊ตฌํ˜„(extension)์ด๋‹ค. ๋จผ์ € ๊ณตํ†ต์ ์œผ๋กœ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„๋“ค์„ ๊ธฐ๋Šฅ๋ณ„๋กœ Protocol๋กœ ๋ฌถ์–ด์ฃผ๊ณ  ๋ฌถ์–ด์ค€ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ์‚ฌ์šฉํ•ด์„œ ๊ธฐ๋ณธ๊ตฌํ˜„์„ ํ†ตํ•ด์„œ ํ™•์žฅ์„ฑ ์žˆ๊ฒŒ ํ•ด๋‹น Protocol์„ ์ฑ„ํƒํ•˜๊ณ  ์žˆ๋Š” ํƒ€์ž…์ด๋ฉด ๋ˆ„๊ตฌ๋“  ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ```swift protocol ContainContents { var contents: [Content] { get } var numberOfContents: Int { get } } extension ContainContents { var contents: [Content] { return TimeLineContentObject.shared.contents } var numberOfContents: Int { return contents.count } } ``` ViewController๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผํ•˜๋Š” ๊ณตํ†ต์ ์ธ contents๋ฅผ ๋‹ด๊ณ  ์žˆ๊ณ  Contents๋ฅผ ๊ฐ€์ง€๊ณ  ์˜ค๋Š” ์—ญํ• ์„ ํ•˜๋Š” Protocol์ด๋‹ค. ํ”„๋กœ์ ํŠธ์—์„œ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ViewController(TableView, CollectionView, DetailView)์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” contents๋ฅผ ์ •์˜ํ•˜๊ณ  extension์„ ํ†ตํ•ด ๊ณตํ†ต์ ์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„ํ–‡๋‹ค. ```swift protocol MediaContainer { var content: Content? { get set } var media: ContentPresentable { get } var note: UILabel { get set } var videoLayer: AVPlayerLayer { get } var mediaImageView: UIImageView { get } func contentChanged() } extension MediaContainer { func contentChanged() { guard let content = content else { mediaImageView.isHidden = true mediaImageView.image = nil videoLayer.isHidden = true videoLayer.player = nil return } switch content.type { case .image: mediaImageView.isHidden = false videoLayer.isHidden = true let image = UIImage(contentsOfFile: content.urlString) mediaImageView.image = image case .video: mediaImageView.isHidden = true videoLayer.isHidden = false let videoURL = URL(filePath: content.urlString) let player = AVPlayer(url: videoURL) videoLayer.player = player } note.text = content.note } var media: ContentPresentable { switch content!.type { case .image: return mediaImageView case .video: return videoLayer } } } ``` Cell (TableView, CollectionView) ๋ฐ DetailView์—์„œ ์ง์ ‘์ ์œผ๋กœ ๋ณด์—ฌ์ค˜์•ผํ•˜๋Š” content๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  content๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๊ฒฝ์šฐ ์กด์žฌํ•˜๋Š” content์— ๋Œ€ํ•œ ๊ธฐ๋ณธ setting์„ ์ง„ํ–‰ํ•˜๋Š” MediaContrainer Protocol์ด๋‹ค. Extension์„ ํ†ตํ•ด์„œ content์— ๋”ฐ๋ฅธ ๊ธฐ๋ณธ ์„ธํŒ…์„ ๊ธฐ๋ณธ๊ตฌํ˜„๋˜์–ด์žˆ๋‹ค. content๋กœ ๋“ค์–ด์˜ค๋Š” data๊ฐ€ image๋ฟ๋งŒ์•„๋‹ˆ๋ผ ๋™์˜์ƒ๋„ ๋“ค์–ด์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ์œ ์—ฐํ•˜๊ฒŒ ์„ค๊ณ„๋  ์ˆ˜ ์žˆ์–ด์•ผํ•˜๋ฏ€๋กœ ContentPresentable Protocol์„ ์‚ฌ์šฉํ•ด์„œ ํ•ด๋‹น Protocol์„ ์ฑ„ํƒํ•œ ์–ด๋–ค ํƒ€์ž…์ด๋“  ์ ์šฉ๋˜์–ด ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ˜„์žฌ๋Š” image, ๋™์˜์ƒ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋Œ€์‘ํ•œ ์ƒํƒœ์ด๋‹ค. Protocol์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ณตํ†ต์ ์œผ๋กœ ๋ฌถ์„ ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์— ๋Œ€ํ•œ ์ฝ”๋“œ์‚ฌ์šฉ ๋ฐ ๊ธฐ๋Šฅ์˜ ์‚ฌ์šฉ์„ ํ†ต์ผํ™” ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ Protocol Extension์„ ํ†ตํ•ด ๊ธฐ๋ณธ๊ตฌํ˜„์„ ํ•˜๊ฒŒ๋  ๊ฒฝ์šฐ ํ•ด๋‹น Protocol์—์„œ ๋ช…์‹œํ•˜๊ณ  ์žˆ๋Š” ํ”„๋กœํผํ‹ฐ๋“ค์—์„œ๋งŒ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•œ๋ฐ where Clause์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด where Clause๋ฅผ ํ†ตํ•ด ์ถ”๊ฐ€์ ์œผ๋กœ ์ œํ•œ๋œ ๊ธฐ๋Šฅ์„ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ extension์„ ํ†ตํ•œ ๊ธฐ๋ณธ๊ตฌํ˜„ ์‚ฌ์šฉ์‹œ where Clause๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค. ์‚ฌ์šฉํ•œ์ฝ”๋“œ: https://github.com/LeeZion94/POPExample.git <์ถœ์ฒ˜> ๋ฐœํ‘œ์ž๋ฃŒ : https://www.slideshare.net/JoSeongGyu/protocol-oriented-programming-in-swift ์ฐธ๊ณ ํ•œ ์˜ˆ์‹œ ์ฝ”๋“œ : https://bitbucket.org/yagom/popsocialmediaexample/src/master/ ์ฐธ๊ณ ์˜์ƒ : https://www.youtube.com/watch?v=9gkzHUsQiUc # 11์ฃผ์ฐจ_2: Protocol Oriented Programming ์‹คํ—˜ ## ๋ฐœํ‘œ์ž ๐ŸฅญREADMANGO๐Ÿฅญ <details> <summary>์•ผ๊ณฐ ์‹คํ—˜์‹ค ๊ธฐ๋ฐ˜ ์‹ค์Šต(์ผ๋ถ€์ˆ˜์ •)</summary> Protocol Oriented Programming(ํ”„๋กœํ† ์ฝœ ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ) - Protocol Oriented Programming(POP) ํƒ„์ƒ ๋ฐฐ๊ฒฝ ์ผ๋ฐ˜์ ์œผ๋กœ OOP์—์„œ ํด๋ž˜์Šค์˜ ์ƒ์† ๊ฐœ๋…์„ ํ†ตํ•ด ๊ณตํ†ต ๊ธฐ๋Šฅ๋“ค์„ ๋ชจ๋“ˆํ™”ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ์œผ๋‚˜, ๊ตฌ์กฐ์ฒด ํ˜น์€ ์—ด๊ฑฐํ˜•์˜ ๊ฒฝ์šฐ์—๋Š” ์ƒ์†์ด ๋ถˆ๊ฐ€ํ•˜๊ธฐ์— ๊ฐ™์€ ํƒ€์ž…์— ๋Œ€ํ•ด์„œ ๊ณตํ†ต์ ์ธ ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ์œ„ํ•ด์„œ๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์ด์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. - ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ์•ˆ์ด Apple WWDC15(Swift 2)์—์„œ Extension ๋ฐœํ‘œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Protocol(์ฒญ์‚ฌ์ง„)์„ ๊ตฌํ˜„ํ•˜๊ณ  Extension์œผ๋กœ ํ•ด๋‹น ํ”„๋กœํ† ์ฝœ์˜ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜๋Š” ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ”„๋กœํ† ์ฝœ ์ดˆ๊ธฐ ๊ตฌํ˜„(Protocol Default Implementation)์ด๋ผ๊ณ  ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค. --- ```swift protocol MacBookChargeabel { var maximumWattPerHour: WattPerHour { get } //์ถฉ์ „๊ธฐ์™€ ๊ธฐ๊ธฐ์˜ ํ—ˆ์šฉ ์™€ํŠธ์‹œ๋ฅผ ๊ฒ€์‚ฌํ•ด์„œ, ๊ธฐ๊ธฐ์˜ ํ—ˆ์šฉ ์™€ํŠธ์‹œ๊ฐ€ ๋” ๋‚ฎ๋‹ค๋ฉด, //๊ธฐ๊ธฐ์— ๋งž์ถ”์–ด ์™€ํŠธ์‹œ๋ฅผ ๋‚ฎ์ถ”์–ด ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. func convert(chargeableWattPerHour: WattPerHour) -> WattPerHour } ``` ์œ„์™€ ๊ฐ™์ด `Chargeabel` ํ”„๋กœํ† ์ฝœ์„ ์ •์˜ํ–ˆ๋‹ค. ```swift struct Charger: MacBookChargeabel { var maximumWattPerHour: WattPerHour func convert(chargeableWattPerHour: WattPerHour) -> WattPerHour { if maximumWattPerHour > chargeableWattPerHour { return chargeableWattPerHour } return maximumWattPerHour } } struct TroubleMacBookCharger: MacBookChargeabel { var maximumWattPerHour: WattPerHour let troublenProbability: Int = Int.random(in: 1...100) var isTrouble: Bool { if self.troublenProbability > 50 { return true } return false } func convert(chargeableWattPerHour: WattPerHour) -> WattPerHour { guard isTrouble else { if maximumWattPerHour > chargeableWattPerHour { return chargeableWattPerHour } return maximumWattPerHour } return maximumWattPerHour } } ``` `MacBookChargeabel`ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•œ `Charger`ํƒ€์ž…๊ณผ `TroubleMacBookCharger`๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•ด๋ณด์ž. ```swift struct MacBook: Portable { private let ๊ธฐ๊ธฐํ—ˆ์šฉ์ถฉ์ „์™€ํŠธ์‹œ: Watt = 20 private let ํ˜„์žฌ์ €์žฅ๋œ๋ฐฐํ„ฐ๋ฆฌ์šฉ๋Ÿ‰ : Watt = 20 private let ์ตœ๋Œ€๋ฐฐํ„ฐ๋ฆฌ์šฉ๋Ÿ‰: Watt = 100 mutating func chargerBattery(charger: TroubleMacBookCharger) { let ํ•„์š”ํ•œ๋ฐฐํ„ฐ๋ฆฌ๋Ÿ‰ = ์ตœ๋Œ€๋ฐฐํ„ฐ๋ฆฌ์šฉ๋Ÿ‰ - ํ˜„์žฌ์ €์žฅ๋œ๋ฐฐํ„ฐ๋ฆฌ์šฉ๋Ÿ‰ let ์ถฉ์ „๋˜๋Š”๋ฐฐํ„ฐ๋ฆฌ๋Ÿ‰ = charger.convert(chargeableWattPerHour: ๊ธฐ๊ธฐํ—ˆ์šฉ์ถฉ์ „์™€ํŠธ์‹œ) guard ์ถฉ์ „๋˜๋Š”๋ฐฐํ„ฐ๋ฆฌ๋Ÿ‰ <= ๊ธฐ๊ธฐํ—ˆ์šฉ์ถฉ์ „์™€ํŠธ์‹œ else { print("๊ณผ์ถฉ์ „์œผ๋กœ ๋งฅ๋ถ์˜ ํšŒ๋กœ๊ฐ€ ํƒ€๋ฒ„๋ ธ์Šต๋‹ˆ๋‹ค. ์œ ๊ฐ์ž…๋‹ˆ๋‹ค.") return } let ์™„์ถฉ๊นŒ์ง€๊ฑธ๋ฆฐ์‹œ๊ฐ„ = ํ•„์š”ํ•œ๋ฐฐํ„ฐ๋ฆฌ๋Ÿ‰/์ถฉ์ „๋˜๋Š”๋ฐฐํ„ฐ๋ฆฌ๋Ÿ‰ print("์™„์ถฉ๊นŒ์ง€ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„: \(์™„์ถฉ๊นŒ์ง€๊ฑธ๋ฆฐ์‹œ๊ฐ„)์‹œ๊ฐ„") } } ``` `MacBook`ํƒ€์ž…์€ ๋ฐฐํ„ฐ๋ฆฌ์ถฉ์ „๊ธฐ๋Šฅ ์‚ฌ์šฉ์‹œ `TroubleMacBookCharger`ํƒ€์ž…์„ ํ•„์š”๋กœํ•œ๋‹ค. ```swift var macBook = MacBook() let ์• ํ”Œ์›Œ์น˜์ถฉ์ „๊ธฐ: Charger = Charger(maximumWattPerHour: 5) let ์•„์ดํฐ๊ณ ์†์ถฉ์ „๊ธฐ: Charger = Charger(maximumWattPerHour: 18) let ์•„์ดํŒจ๋“œ์ถฉ์ „๊ธฐ: Charger = Charger(maximumWattPerHour: 30) let ๋…ธํŠธ๋ถ์ถฉ์ „๊ธฐ: Charger = Charger(maximumWattPerHour: 96) let ๊ณ ์†๋…ธํŠธ๋ถ์ถฉ์ „๊ธฐ: Charger = Charger(maximumWattPerHour: 106) let ๋‚ก์€๋งฅ๋ถ์ถฉ์ „๊ธฐ: TroubleCharger = TroubleCharger(maximumWattPerHour: 96) macBook.chargerBattery(charger: ๋‚ก์€๋งฅ๋ถ์ถฉ์ „๊ธฐ) // 4์‹œ๊ฐ„ or ์ผ์ •ํ™•๋ฅ ๋กœ ํšŒ๋กœ๊ฐ€ ๊ตฌ์›Œ์ง„ ๋งฅ๋ถ macBook.chargerBattery(charger: ๋…ธํŠธ๋ถ์ถฉ์ „๊ธฐ) // error / ํ˜ธํ™˜์ด ์•ˆ๋œ๋‹ค! ``` ์ด๋Ÿฐ ๊ฒฝ์šฐ ๋งฅ๋ถ์„ ์ถฉ์ „ํ• ๋•Œ๋งˆ๋‹ค ์ถฉ์ „์ด ๋ ์ง€ ์ผ์ •ํ™•๋ฅ ๋กœ ์žฅ์ž‘์ด ๋˜์–ด๋ฒ„๋ฆด์ง€ ๋ชฐ๋ผ ๋ฒŒ๋ฒŒ๋–จ๊ฒŒ ๋ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ ‡์ง€๋งŒ ์• ํ”Œ์— ๋ฌธ์˜ํ•ด๋„ ์ถฉ์ „ํ• ๋•Œ `TroubleCharger`๋งŒ ์“ธ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•˜๋‹ˆ ์–ด์ฉ” ์ˆ˜ ์—†๋‹ค. ๋งฅ๋ถ์ด ์žฅ์ž‘์ด ๋˜๋ฉด ์• ํ”Œ๋ณธ์‚ฌ๋„ ์žฅ์ž‘์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์ˆ˜ ๋ฐ–์—... ํ•˜์ง€๋งŒ ์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„ ์• ํ”Œ๋„ ๋ฌธ์ œ๋ฅผ ํŒŒ์•…ํ–ˆ๋Š”์ง€ ๋งฅ๋ถ์„ ์ „๋ถ€ ์ˆ˜๊ฑฐํ•ด ๊ฐœ์กฐ(?)๋ฅผ ํ•ด ์คฌ๋‹ค! ์ถฉ์ „์‹œ ๋‹ค๋ฅธ ์ถฉ์ „๊ธฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๊ฒƒ์ด๋‹ค! ๋ฌผ๋ก  ์• ํ”Œ์ด ์ œ์‹œํ•œ ์ถฉ์ „๊ธฐ ๊ทœ์•ฝ `MacBookChargeabel`ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•ด ์ค€์ˆ˜ํ•˜๋Š” ์ถฉ์ „๊ธฐ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ–ˆ์ง€๋งŒ ์ด๊ฒŒ ์–ด๋””์ธ๊ฐ€! ```swift struct MacBook: Portable { private let ๊ธฐ๊ธฐํ—ˆ์šฉ์ถฉ์ „์™€ํŠธ์‹œ: Watt = 20 private let ํ˜„์žฌ์ €์žฅ๋œ๋ฐฐํ„ฐ๋ฆฌ์šฉ๋Ÿ‰ : Watt = 20 private let ์ตœ๋Œ€๋ฐฐํ„ฐ๋ฆฌ์šฉ๋Ÿ‰: Watt = 100 mutating func chargerBattery(charger: MacBookChargeabel) { let ํ•„์š”ํ•œ๋ฐฐํ„ฐ๋ฆฌ๋Ÿ‰ = ์ตœ๋Œ€๋ฐฐํ„ฐ๋ฆฌ์šฉ๋Ÿ‰ - ํ˜„์žฌ์ €์žฅ๋œ๋ฐฐํ„ฐ๋ฆฌ์šฉ๋Ÿ‰ let ์ถฉ์ „๋˜๋Š”๋ฐฐํ„ฐ๋ฆฌ๋Ÿ‰ = charger.convert(chargeableWattPerHour: ๊ธฐ๊ธฐํ—ˆ์šฉ์ถฉ์ „์™€ํŠธ์‹œ) guard ์ถฉ์ „๋˜๋Š”๋ฐฐํ„ฐ๋ฆฌ๋Ÿ‰ <= ๊ธฐ๊ธฐํ—ˆ์šฉ์ถฉ์ „์™€ํŠธ์‹œ else { print("๊ณผ์ถฉ์ „์œผ๋กœ ๋งฅ๋ถ์˜ ํšŒ๋กœ๊ฐ€ ํƒ€๋ฒ„๋ ธ์Šต๋‹ˆ๋‹ค. ์œ ๊ฐ์ž…๋‹ˆ๋‹ค.") return } let ์™„์ถฉ๊นŒ์ง€๊ฑธ๋ฆฐ์‹œ๊ฐ„ = ํ•„์š”ํ•œ๋ฐฐํ„ฐ๋ฆฌ๋Ÿ‰/์ถฉ์ „๋˜๋Š”๋ฐฐํ„ฐ๋ฆฌ๋Ÿ‰ print("์™„์ถฉ๊นŒ์ง€ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„: \(์™„์ถฉ๊นŒ์ง€๊ฑธ๋ฆฐ์‹œ๊ฐ„)์‹œ๊ฐ„") } } ``` ์ด์ œ ์šฐ๋ฆฐ ๋ฌธ์ œ์žˆ๋Š” ์ถฉ์ „๊ธฐ๋ฅผ ๊ฐ–๋‹ค ๋ฒ„๋ฆฌ๊ณ  ์•ˆ์ „ํ•œ ์ถฉ์ „๊ธฐ๋กœ ๋งฅ๋ถ์„ ์ถฉ์ „ํ•  ์ˆ˜ ์žˆ๋‹ค. ์• ํ”Œ ๋งŒ์„ธ! ```swift macBook.chargerBattery(charger: ๋…ธํŠธ๋ถ์ถฉ์ „๊ธฐ) // 4์‹œ๊ฐ„ ``` ์œ„์™€ ๊ฐ™์ด ํ”„๋กœํ† ์ฝœ์„ ํ™œ์šฉํ•  ๊ฒฝ์šฐ ์ผ๋‹จ ์šฐ๋ฆฐ ํƒ€์ž…์„ ์•Œ ํ•„์š”๊ฐ€ ์—†์–ด์ง„๋‹ค. ๊ทธ๋ž˜์„œ Protocol์„ ์ด์šฉํ•˜๋ฉด ๊ฐœ๋ฐœ์ž๋Š” ๊ตฌ์ฒด์ ์ธ Type์— ์ผ์ผํžˆ ๋Œ€์‘ํ•˜๊ฑฐ๋‚˜ ํŠน์ • ๊ตฌ์กฐ์ฒด(Type)๋ฅผ returnํ•˜๋Š” ๊ฒฝ์šฐ๋“ค ํ•˜๋‚˜ํ•˜๋‚˜์— ์—ฐ์—ฐํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค. ๊ธฐ์กด์— ๊ตฌ์ฒด์ ์ธ ํƒ€์ž… ํ•˜๋‚˜ํ•˜๋‚˜์— ์˜์กดํ–ˆ๋‹ค๋ฉด ์ด์   ์ถ”์ƒ์ ์ธ ํ”„๋กœํ† ์ฝœ๋งŒ ์˜์กดํ•˜๋ฉด ๋œ๋‹ค๋Š” ๋œป์ด๋‹ค. ๋งŒ์ผ ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ๋งฅ๋ถ์€ ์—ฌ๋Ÿฌ๋ฐฉ๋ฒ•์œผ๋กœ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผํ–ˆ์„๊ฒƒ์ด๊ณ  ๊ทธ์ค‘ ํ•˜๋‚˜์˜ ๋ฐฉ๋ฒ•์ด ์•„๋ž˜์™€ ๊ฐ™์„ ์ˆ˜ ์žˆ๋‹ค. ```swift struct MacBook: Portable { let ... mutating func chargerBatteryPortA(charger: TroubleMacBookCharger) { ... } mutating func chargerBatteryPortB(charger: Charger) { ... } } ``` ๋˜‘๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ํƒ€์ž…๋งŒ ๋ฐ”๊ฟ”์„œ ์ถ”๊ฐ€ํ•œ๊ฒƒ์ด๋‹ค. ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฌํ•œ ์ค‘๋ณต๋œ ์ฝ”๋“œ๋ฅผ ํ”ผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค. ์ฆ‰ ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋†’์•„์ง„๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋งŒ์•ฝ `Charger`ํƒ€์ž…์˜ ๊ธฐ๋Šฅ์ด ๋ณ€๊ฒฝ๋˜๊ฑฐ๋‚˜ ์‚ญ์ œ๋œ๋‹ค๋ฉด `chargerBatteryPortB`๋ฉ”์„œ๋“œ์˜ ์ž‘๋™์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋Š”๋ฐ, ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒ ๋ฐ ์ค€์ˆ˜ํ•œ๋‹ค๋Š”๊ฒƒ์€ ์ฑ„ํƒํ•œ ํƒ€์ž…์—์„œ ๊ธฐ๋Šฅ์ด ๊ตฌํ˜„ ๋˜์–ด์žˆ๊ณ  ๋ณ€๊ฒฝ๋˜๊ฑฐ๋‚˜ ์‚ญ์ œ๋ ๋ฆฌ ์—†๋‹ค๋Š”๊ฑธ ๋ณด์žฅํ•จ์œผ๋กœ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ์™€ ๊ด€๋ จํ•ด์„  ๋ฉ”์„œ๋“œ์˜ ์ž‘๋™์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ์ผ์ด ์—†์–ด์ง„๋‹ค. ์•„๋ž˜๋Š” POP์˜ ์žฅ์ ์„ ๊ฐ„๋žตํ•˜๊ฒŒ ์ •๋ฆฌํ•ด๋ณธ๊ฒƒ์ด๋‹ค. - ์ฝ”๋“œ์˜ ์ค‘๋ณต์„ ์ตœ์†Œํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค. - ๊ฐ€๋ณ๊ณ  ์•ˆ์ „ํ•˜๋‹ค. - ์ƒ์†๊ณผ ๋‹ฌ๋ฆฌ ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ๊ณจ๋ผ์„œ ์“ธ ์ˆ˜ ์žˆ๋‹ค. - ์ƒ์†์˜ ๊ฒฝ์šฐ, ์ฐธ์กฐ ํƒ€์ž…์ด๋ฏ€๋กœ ์ฐธ์กฐ ์ถ”์ ์„ ์œ„ํ•œ ๋น„์šฉ์ด ๋งŽ์ด ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์†๋„์ธก๋ฉด์—์„œ ๋‹ค์†Œ ๋А๋ฆฌ๋‹ค. - ๊ฐ’ ํƒ€์ž…์˜ ์ƒ์† ํšจ๊ณผ - ๊ณตํ†ต๋œ ๊ธฐ๋Šฅ์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. - ์ˆ˜ํ‰์ ์ธ ํ™•์žฅ ๊ธฐ๋Šฅ - Class๋Š” ํ•˜๋‚˜์˜ ์ƒ์†๋งŒ ๊ฐ€๋Šฅํ•˜๊ณ  ์ˆ˜์ง์ ์ธ ๊ตฌ์กฐ๊ฐ€ ๋‚˜ํƒ€๋‚˜์ง€๋งŒ Protocol์„ ์ด์šฉํ•˜๋ฉด ์ˆ˜ํ‰์ ์ธ ํ™•์žฅ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. - ์ œ๋„ค๋ฆญ์˜ ํ™œ์šฉ - ์˜ˆ๋ฅผ ๋“ค์–ด, Swift์˜ Array์˜ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ ํƒ€์ž…์˜ ๊ด€๊ณ„ ์—†์ด ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ณ  ์ด๋“ค ๋ชจ๋‘๊ฐ€ ๊ทธ์— ๋”ฐ๋ฅธ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. </div> </details> # 12์ฃผ์ฐจ: Responder Chain ## ๐Ÿ”ฅ์‹คํ—˜๋ชฉํ‘œ ### ๐Ÿ‘จโ€๐Ÿ”ฌ ์‹คํ—˜ 1: UIKit์ด ๊ฐ€์žฅ ๋ View๋ฅผ ์ฐพ์•„๋‚ด๋Š” ๋ฐฉ๋ฒ• ### ๐Ÿ‘จโ€๐Ÿ”ฌ ์‹คํ—˜ 2: ResponderChain ํ™œ์šฉํ•ด๋ณด๊ธฐ ## ๐Ÿ’ป ๋ฐœํ‘œ์ž๋ฃŒ ![](https://hackmd.io/_uploads/BydNtlfjn.png) ![](https://hackmd.io/_uploads/Hkk7qezin.png) **< Outline >** 1.Responder chain 2.hitTest 3.hitTest Logic 4.hitTest Code ![](https://hackmd.io/_uploads/BkewYefih.png) **Responder**: UIResponder๋Š” ์ผ๋ จ์˜ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›๊ณ  ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ๊ฐ์ฒด์— ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. **Responder chain**:์—ฌ๊ธฐ์„œ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•ด ๋„˜๊ฒจ์งˆ ๊ฒฝ์šฐ ๋‹ค์Œ ๊ฐœ์ฒด๋กœ ์—ฐ๊ฒฐ๋˜์–ด ์ด๋™ํ•˜๋Š”๋ฐ ์ด์ฒ˜๋Ÿผ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๋ฅผ ๋ชฉ์ ์œผ๋กœ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” Responder ๊ฐœ์ฒด๋“ค์˜ ์—ฐ๊ฒฐ ๊ด€๊ณ„๊ฐ€ Responder Chain ์ž…๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/Hko8YgMjh.png) **First Responder**: UIKit์€ ์ž๋™์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ์— ๊ฐ€์žฅ ์ ์ ˆํ•œ Responder ๊ฐ์ฒด๋ฅผ ์ฐพ์•„ ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌํ•˜๋Š”๋ฐ ์ด ๊ฐ์ฒด๊ฐ€ First Responder์ž…๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/HkvOKgfj3.png) UIEvent ํƒ€์ž…์˜ ์ข…๋ฅ˜์™€ ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌ ๋ฐ›๋Š” First Reponder ![](https://hackmd.io/_uploads/HkskixMih.png) **< Responder chain Logic >** 1. ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ํ•ด๋‹น ์ด๋ฒคํŠธ๋Š” ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ๋ทฐ๋ฅผ ๊ธฐ์ค€์œผ๋กœ Responder Chain์„ ๋”ฐ๋ผ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. 2. ๋ทฐ ๊ณ„์ธต ๊ตฌ์กฐ์—์„œ ๊ฐ€์žฅ ์•ž์ชฝ์— ์žˆ๋Š” ๋ทฐ๋ถ€ํ„ฐ ์ด๋ฒคํŠธ์— ์‘๋‹ตํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ์‘๋‹ตํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์‘๋‹ตํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” ์ƒ์œ„ ๋ทฐ๋กœ ์ด๋ฒคํŠธ๊ฐ€ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. 3. ์ด๋ฒคํŠธ๋Š” ๋ทฐ ๊ณ„์ธต ๊ตฌ์กฐ์—์„œ ์ƒ์œ„๋กœ ์ „๋‹ฌ๋˜๋ฉฐ, UIView๋Š” UIResponder์˜ ์„œ๋ธŒํด๋ž˜์Šค์ด๋ฏ€๋กœ, ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด๊ฐ€ UIResponder์˜ ์„œ๋ธŒํด๋ž˜์Šค ์ค‘ ํ•˜๋‚˜๋ผ๋ฉด ํ•ด๋‹น ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 4. ๋งŒ์•ฝ ์ด๋ฒคํŠธ๊ฐ€ ์ตœ์ƒ์œ„๊นŒ์ง€ ์ „๋‹ฌ๋˜์–ด๋„ ํ•ด๋‹น ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ์ฐพ์ง€ ๋ชปํ•˜๋ฉด ์ด๋ฒคํŠธ๋Š” ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/r1utKefo3.png) **hitTest**: UIViewํด๋ž˜์Šค์— ์ •์˜๋œ ๋ฉ”์„œ๋“œ๋กœ, ํ„ฐ์น˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ํฌ์ธํŠธ์— ํ•ด๋‹นํ•˜๋Š” ์ตœ์ƒ๋‹จ์˜ ๋ทฐ๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ์—ญํ• ์„ ๊ฐ–์Šต๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํ„ฐ์น˜ํ•œ ์ขŒํ‘œ๊ฐ€ ์–ด๋–ค ๋ทฐ์— ์œ„์น˜ํ•˜๋Š”์ง€ ํŒŒ์•…ํ•ด์•ผํ•˜๋Š”๋ฐ ์—ฌ๊ธฐ์„œ **hitTest**๋ฅผ ํ†ตํ•ด ํƒ์ƒ‰ํ•œ ๋ทฐ์— ๋Œ€ํ•œ ํ„ฐ์น˜ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/BkWj6lMsh.png) ์ง€๊ธˆ ๋ณด์ด๋Š” ์ฝ”๋“œ๋Š” UIView ํด๋ž˜์Šค์˜ **hitTest** ๋ฉ”์„œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜์—ฌ ์ •์˜ํ•œ ๊ตฌํ˜„์„ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. **hitTest** ๋ฉ”์„œ๋“œ๋Š” ํŠน์ • ์ขŒํ‘œ์— ํ•ด๋‹นํ•˜๋Š” ๋ทฐ๋ฅผ ์ฐพ๋Š” ์—ญํ• ์„ ํ•˜์ฃ . ์ด ์ฝ”๋“œ๋Š” ๋ทฐ ๊ณ„์ธต ๊ตฌ์กฐ์—์„œ ํ„ฐ์น˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ์ขŒํ‘œ์— ํ•ด๋‹นํ•˜๋Š” ๋ทฐ๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. - `isUserInteractionEnabled`: false์ผ ๊ฒฝ์šฐ, ํ•ด๋‹น ๋ทฐ์™€ ๊ทธ ํ•˜์œ„ ๋ทฐ๋“ค์€ ์‚ฌ์šฉ์ž์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๋ฐ›์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. - `isHidden`: true์ผ ๊ฒฝ์šฐ, ํ•ด๋‹น ๋ทฐ์™€ ๊ทธ ํ•˜์œ„ ๋ทฐ๋“ค์€ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. - `alpha`: ๋ทฐ์˜ ๋ถˆํˆฌ๋ช…๋„๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. 0.0๋ถ€ํ„ฐ 1.0 ์‚ฌ์ด์˜ ๊ฐ’์„ ๊ฐ€์ง€๋ฉฐ, 0.01๋ณด๋‹ค ์ž‘์œผ๋ฉด ๋ทฐ๋Š” ๊ฑฐ์˜ ํˆฌ๋ช…ํ•œ ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•ด๋‹น ๋ทฐ๊ฐ€ ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ์„ ๋ฐ›์„ ์ˆ˜ ์—†๊ฑฐ๋‚˜, ํ™”๋ฉด์— ํ‘œ์‹œ๋˜์ง€ ์•Š๊ฑฐ๋‚˜, ๊ฑฐ์˜ ํˆฌ๋ช…ํ•œ ์ƒํƒœ๋ผ๋ฉด, ํ•ด๋‹น ๋ทฐ๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ nil์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ, ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ์ขŒํ‘œ(point)๊ฐ€ ํ˜„์žฌ ๋ทฐ์˜ ๋ฒ”์œ„์— ์†ํ•˜๋Š”์ง€ (self.point(inside: point, with: event))๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•ด๋‹น ์ขŒํ‘œ๊ฐ€ ๋ทฐ์˜ ๋ฒ”์œ„์— ์†ํ•œ๋‹ค๋ฉด, ๋ทฐ ๊ณ„์ธต ๊ตฌ์กฐ์—์„œ ํ•˜์œ„ ๋ทฐ๋“ค์„ ๊ฑฐ๊พธ๋กœ ์ˆœํšŒํ•˜๋ฉฐ ํ„ฐ์น˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ์ขŒํ‘œ์— ํ•ด๋‹นํ•˜๋Š” ๋ทฐ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ๋ทฐ์— ๋Œ€ํ•ด์„œ ๋‹ค์‹œ **hitTest** ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•˜์—ฌ ํ•˜์œ„ ๋ทฐ๋“ค์„ ์ˆœํšŒํ•˜๊ณ , ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ทฐ๋ฅผ ์ฐพ์•„์„œ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์œ„์˜ ์ฝ”๋“œ๋ฅผ ์š”์•ฝํ•˜๋ฉด, ํ„ฐ์น˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ์ขŒํ‘œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ทฐ ๊ณ„์ธต ๊ตฌ์กฐ์—์„œ ์ •์˜ ์กฐ๊ฑด์— ๋”ฐ๋ผ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ทฐ๋ฅผ ์ฐพ๊ณ , ํ•ด๋‹น ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด iOS ์•ฑ์€ ์ ์ ˆํ•œ ๋ทฐ์— ๋Œ€ํ•ด ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ , ์‚ฌ์šฉ์ž์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ํšจ๊ณผ์ ์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/r1aqpgzin.png) 1. touches์—์„œย ์ฒซ ๋ฒˆ์งธ ํ„ฐ์น˜ย ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. 2. ์ขŒํ‘œย ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. 3. hitTest๋ฅผ ํ†ตํ•ด์„œ ํ•ด๋‹น ์ขŒํ‘œ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š”ย ๋ทฐ๋ฅผ ํƒ์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. 4. ๋งŒ์•ฝ ํ•ด๋‹น ๋ทฐ๊ฐ€ ์ขŒํ‘œ๋ฅผ ๊ฐ–๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋ฉด nil์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/Bye5Kgfin.png) ์œ„์™€ ๊ฐ™์ด ์‚ฌ์šฉ์ž๊ฐ€ย View_B1(green)์„ ํ„ฐ์น˜ํ•˜๋ฉด ์ƒ์œ„๋ทฐ ๊ณ„์ธต๋ถ€ํ„ฐ ํƒ์ƒ‰ํ•˜๋ฉด์„œ ๋‚ด๋ ค์˜ค๋Š”๋ฐย View_C(grey)๋Š” ํ„ฐ์น˜๋œ point๋ฅผ ๊ฐ–๊ณ  ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— false ๋ฐ˜ํ™˜ํ•˜๊ณ ย View B๋กœ ๋„˜์–ด์™€ hitTest๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋˜๋ฉด์„œย View_B1(green)๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ### ๐Ÿ“‹ ์‹คํ—˜ View ![](https://hackmd.io/_uploads/BJ3cYgzj3.png) ### ๐Ÿ“‹ ์‹คํ—˜ 1 ![](https://hackmd.io/_uploads/SJJjJ-fih.png) ์‚ฌ์šฉ์ž๊ฐ€ ํ„ฐ์น˜ํ•œ ์ขŒํ‘œ๋ฅผ ํฌํ•จํ•˜๋Š” ์ตœ์ƒ์œ„ ๋ทฐ์˜ `backgroundColor`๊ฐ€ ์ถœ๋ ฅ๋˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ—€์Šต๋‹ˆ๋‹ค. ### ๐Ÿ“‹ ์‹คํ—˜ 2 ![](https://hackmd.io/_uploads/SyZCIWMo2.png) GestureRecognizer ์ƒ์„ฑํ•˜๊ณ , ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ํ•ด๋‹น `greenView`์˜ ๋ฐฐ๊ฒฝ์ƒ‰์„ black๊ณผ ๊ต์ฐจ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ๋Š” `tapView()`๊ฐ€ ํ˜ธ์ถœ๋˜๋„๋ก ์„ค์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค. **โœ๏ธTip** - ๋ทฐ ๊ฐ์ฑ„์˜ ๋ฐฐ๊ฒฝ ์ƒ‰์ƒ์ด `System Color`์ผ ๊ฒฝ์šฐ์—๋Š” ์ƒ‰์ƒ ๋ณ€๊ฒฝ์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ•ด๋‹น ์ƒ‰์„ `green`์œผ๋กœ ์ˆ˜์ •ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ### ์‹คํ–‰ํ™”๋ฉด ![](https://hackmd.io/_uploads/By9jwbGj3.gif) # 13์ฃผ์ฐจ_1: Collection View ## ๋ฐœํ‘œ์ž Yetti ### Collection View? ![](https://hackmd.io/_uploads/S15rism33.png) ![CollectionView](https://hackmd.io/_uploads/SyVCqo7hn.png) - ์ •๋ ฌ๋œ ๋ฐ์ดํ„ฐ ํ•ญ๋ชฉ ๋ชจ์Œ์„ ๊ทธ๋ฆฌ๋“œ๋‚˜ ๋‹ค๋ฅธ ์ปค์Šคํ…€ ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ํ‘œ์‹œํ•˜๋Š” ์œ ์—ฐํ•˜๊ณ  ๊ฐ•๋ ฅํ•œ UI์š”์†Œ - ํ…Œ์ด๋ธ” ๋ทฐ์ฒ˜๋Ÿผ data source๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  delegate๋กœ ์œ ์ €์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๊ด€๋ฆฌ ![](https://hackmd.io/_uploads/HJ_aws7nn.png) ![](https://hackmd.io/_uploads/BJ0avjmhn.png) ### Collection View ๊ตฌ์„ฑ์š”์†Œ 4๊ฐ€์ง€ 1. ์…€(Cell) 2. ๋ณด์ถฉ ๋ทฐ(Supplementary View) 3. ๋ฐ์ฝ”๋ ˆ์ด์…˜ ๋ทฐ(Decoration View) 4. ๋ ˆ์ด์•„์›ƒ ๊ฐ์ฒด(Layout Object) ![](https://hackmd.io/_uploads/ryvOtiQh3.png) - Line๊ฐ™์€ ๊ฒฝ์šฐ๋„ ๋ฉ”์‹ ์ € ๋‚ด์—์„œ ํ…Œ์ด๋ธ”๋ทฐ๋ฅผ ์‚ฌ์šฉํ•˜๋‹ค๊ฐ€ ์ฝœ๋ ‰์…˜๋ทฐ๋กœ ๋ฆฌํŒฉํ† ๋งํ•˜์˜€๋‹ค. ![CollectionViewFlowLayout](https://hackmd.io/_uploads/Bk6_qiQh3.png) ![](https://hackmd.io/_uploads/rymKqiQnh.png) ![](https://hackmd.io/_uploads/B1vKcjXn2.png) ### CollectionView ๊ตฌ์„ฑ๋‹จ๊ณ„ 1. FlowLayout๊ฐ์ฒด ์ƒ์„ฑ ํ›„ CollectionView์— ํ• ๋‹น 2. ์…€์˜ ๋„ˆ๋น„์™€ ๋†’์ด๋ฅผ ์„ค์ • 3. ํ•„์š”์‹œ item, lines์˜ ๊ฐ„๊ฒฌ์„ ์„ค์ • 4. header, footer์‚ฌ์šฉ์‹œ ์‚ฌ์ด์ฆˆ ๋ช…์‹œ 5. ๋ ˆ์ด์•„์›ƒ์˜ ์Šคํฌ๋กค ๋ฐฉํ–ฅ ์„ค์ •. ๊ธฐ๋ณธ๊ฐ’์€ ์„ธ๋กœ ![](https://hackmd.io/_uploads/BJKK9sQh3.png) ![](https://hackmd.io/_uploads/rJyc5om2n.png) ![](https://hackmd.io/_uploads/S1Xq5om3h.png) ### UICollectionViewDataSource - ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด UICollectionView์˜ ๋ฐ์ดํ„ฐ์™€ ๋ทฐ๋ฅผ ๊ด€๋ฆฌ - ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‹œ์—๋Š” reloadData()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๋กœ๋“œํ•ด์•ผํ•จ - ์„ธ๋ถ€์ ์ธ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ถ”๊ฐ€์ ์ธ ๋กœ์ง ๋ฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ฝ”๋“œ๊ฐ€ ํ•„์š” - ๋”ฐ๋กœ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ฝ”๋“œ๊ฐ€ ์—†๋‹ค๋ฉด ๋š๋š ๋Š๊ฒจ์„œ ๋ณด์ด๋Š” ๋‹จ์  -> ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX) ๊ฐ์†Œ - ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ๋‹ค๋ฃจ๊ธฐ์—๋Š” ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Œ ### UICollectionViewDiffableDataSource - UICollectionViewDiffableDataSourceํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉฐ, ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์„ ๋” ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ - CollectionView์™€ DataSource ๊ฐ„์— ๋ฐ์ดํ„ฐ ์ž๋™์œผ๋กœ ๋™๊ธฐํ™”๋˜์–ด ํŽธ๋ฆฌํ•จ - ๋ฐ์ดํ„ฐ ์Šค๋ƒ…์ƒท(snapshot)์„ ์‚ฌ์šฉํ•˜์—ฌ ์„น์…˜๊ณผ ์•„์ดํ…œ์˜ ๊ตฌ์„ฑ์„ ๊ด€๋ฆฌ (์Šค๋ƒ…์ƒท: ํ˜„์žฌ UICollectionView์˜ ํ‘œ์‹œ ์ƒํƒœ๋ฅผ ๋œปํ•จ) - ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์žˆ์–ด์„œ ๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—†์ด๋„ ๋น ๋ฅธ ์„ฑ๋Šฅ ๋ฐ ์ ์ ˆํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ž๋™์œผ๋กœ ์ œ๊ณต -> ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX) ํ–ฅ์ƒ - ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ๊ฒฝ์šฐ์—๋„ ๋” ํšจ์œจ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ ๋ฐ ์—…๋ฐ์ดํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Œ ![](https://hackmd.io/_uploads/BySq9sQnh.png) ![](https://hackmd.io/_uploads/BJqq9o7nh.png) ![](https://hackmd.io/_uploads/r1TcqjX2n.png) - ์œ ํŠœ๋ธŒ๋ฎค์ง, ์ธ์Šคํƒ€๊ทธ๋žจ, ์•ฑ์Šคํ† ์–ด ๋“ฑ๋“ฑ ๋งŽ์€ ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋‹ค. ![](https://hackmd.io/_uploads/Bygicj733.png) # 13์ฃผ์ฐจ_2: URL Session ## ๋ฐœํ‘œ์ž yyss99 ![](https://hackmd.io/_uploads/HJXd60V33.png) ![](https://hackmd.io/_uploads/H1NKp0N3n.png) ![](https://hackmd.io/_uploads/HkGqpRVn3.png) ![](https://hackmd.io/_uploads/H17jp0Vnh.png) ![](https://hackmd.io/_uploads/rke2a0N23.png) ![](https://hackmd.io/_uploads/S1onpC4nn.png) ![](https://hackmd.io/_uploads/r1UTp04n2.png) ![](https://hackmd.io/_uploads/SybCpC43h.png) ![](https://hackmd.io/_uploads/SJTCpCVhh.png) ![](https://hackmd.io/_uploads/H1k4R0Vhn.png) ![](https://hackmd.io/_uploads/rJaN0043n.png) ![](https://hackmd.io/_uploads/H1jBCAN23.png) ![](https://hackmd.io/_uploads/ry6vR0V23.png) ![](https://hackmd.io/_uploads/S1puRRE22.png) ![](https://hackmd.io/_uploads/rk3tCRE2n.png) ![](https://hackmd.io/_uploads/ry2qCCVhn.png) ![](https://hackmd.io/_uploads/rkFjAANn3.png) ![](https://hackmd.io/_uploads/H1j2C0Nnn.png) ![](https://hackmd.io/_uploads/Bk6RRA433.png) ![](https://hackmd.io/_uploads/ryiJJyrh2.png) ![](https://hackmd.io/_uploads/BJPgJyS3h.png) ![](https://hackmd.io/_uploads/r1xWbJ1H3h.png) ![](https://hackmd.io/_uploads/r1Uzk1Bnn.png) # 14์ฃผ์ฐจ: Cache ๋ฏผํŠธ ํ™”์ดํŒ…! ํ—คํ—ค ๊ฐ์Ÿˆ๊ฐ์Ÿˆ์š”~ ## ๋ฐœํ‘œ์ž MINT ![](https://hackmd.io/_uploads/H1whE-Whn.png) ![](https://hackmd.io/_uploads/BJahVW-2h.png) ๋ฐ์ดํ„ฐ๋“ค์€ ์–ด๋””์— ์ €์žฅ๋˜๊ณ  ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋ ๊นŒ์š”? ๋จผ์ € ํ•˜๋“œ ๋””์Šคํฌ์— ์šฐ๋ฆฌ์˜ ๋ฐ์ดํ„ฐ๋“ค์ด ์ „๋ถ€ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— ํ•˜๋“œ ๋””์Šคํฌ๋Š” ์šฉ๋Ÿ‰์ด ๋งค์šฐ ํฌ๊ณ , ๊ธฐ์–ต์„ ํ•˜๋Š” ๊ฒƒ์— ์น˜์ค‘๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. RAM, ๋ฉ”๋ชจ๋ฆฌ๋Š” ๊ทธ๋Ÿฌํ•œ ํ•˜๋“œ ๋””์Šคํฌ์—์„œ ์•ฑ์„ ์‹คํ–‰ํ•˜๋Š”๋ฐ์— ํ•„์š”ํ•œ ์ค‘์š” ๋ฐ์ดํ„ฐ๋“ค์„ ๋“ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์—ฌ์ „ํžˆ ๋‚ด์šฉ์€ ๋ฐฉ๋Œ€ํ•˜๊ณ , RAM์€ ๋””์Šคํฌ๋ณด๋‹จ ๊ธฐ์–ตํ•  ์ˆ˜ ์žˆ๋Š” ์–‘์ด ์ ๊ณ , CPU์™€ ๊ฐ€๊นŒ์›Œ์„œ ๋น ๋ฅด์ง€๋งŒ ์–ด์ฐŒ ๋˜์—ˆ๊ฑด ๊ธฐ์–ต์„ ํ•˜๋Š” ๊ฒƒ์—๋งŒ ๋Šฅ๋ ฅ์ด ์น˜์ค‘๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. CPU๋Š” RAM์—์„œ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋“ค์„ ์ด์šฉํ•ด ์•ฑ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์–ต๋ณด๋‹ค๋Š” ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•ด, ๊พธ์ค€ํ•˜๊ฒŒ ์„ฑ๋Šฅ์ด ์—…๊ทธ๋ ˆ์ด๋“œ ๋˜์–ด ์™”์Šต๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— RAM๊ณผ CPU๋Š” ๊ธฐ์–ต๊ณผ ์„ฑ๋Šฅ์œผ๋กœ ๊ฐ์ž์˜ ๋ชฉ์ ์ด ๋‹ค๋ฅด๊ธฐ์— ์„œ๋กœ์„œ๋กœ ์„ฑ๋Šฅ์ฐจ์ด๊ฐ€ ์ ์  ์‹ฌํ•˜๊ฒŒ ๋‚˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. RAM์ด CPU๋ฅผ ์ซ“์•„๊ฐ€์ง€ ๋ชปํ•ด CPU๊ฐ€ RAM์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์•„์„œ ์ฃผ๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ ค์•ผ ํ•˜๋Š” ๋ณ‘๋ชฉ ํ˜„์ƒ์ด ๋ฐœ์ƒํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/H1DyBWZ22.png) ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์™„์ถฉ๊ธฐ์˜ ์—ญํ• ์„ ํ•˜๋Š” ์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์†๋„๋ฅผ ๋†’์ด๊ณ  ๋น„์šฉ์„ ๋‚ฎ์ถ”๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. CPU์˜ ์ผ๋ถ€๋ผ๊ณ ๋„ ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ์ž‘์€ ์นฉ์œผ๋กœ CPU์— ์ง์ ‘ ๋ถ™์–ด ์žˆ๊ฑฐ๋‚˜ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/S1rZyGZh2.png) ์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ๋Š” ์ €์žฅํ•  ๋•Œ ๊ธฐ๋ณธ์ ์ธ ๊ทœ์น™์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์• ์ดˆ์— ์บ์‹œ๋ผ๋Š” ๊ฐœ๋… ์ž์ฒด๊ฐ€ ๋งŒ๋“ค์–ด์ง„ ์ด์œ ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด์„œ์˜€๊ธฐ์— ์•ž์œผ๋กœ ์“ฐ์ผ ๋ฐ์ดํ„ฐ๋“ค์„ ๋ฏธ๋ฆฌ ์˜ˆ์ƒํ•˜์—ฌ ์บ์‹œ์— ๋„ฃ์–ด๋†“์„ ๋•Œ์˜ ๊ทœ์น™์œผ๋กœ ์ปดํ“จํ„ฐ์˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ์‹œ๊ฐ„ ์ง€์—ญ์„ฑ ๊ณต๊ฐ„ ์ง€์—ญ์„ฑ ![](https://hackmd.io/_uploads/H1obsb-3n.png) ์ง€๊ธˆ๊นŒ์ง€์˜ ์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ๋Š” ํ•˜๋“œ์›จ์–ด์ ์œผ๋กœ ์กด์žฌํ•˜๋Š” ์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ์— ๋Œ€ํ•œ ๋‚ด์šฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์บ์‹œ๋Š” `์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋‚˜ ๊ฐ’์„ ๋ฏธ๋ฆฌ ๋ณต์‚ฌํ•ด๋†“๋Š” ์ž„์‹œ ๊ณต๊ฐ„`์„ ๋œปํ•˜๋Š” ๋ง๋กœ ์†Œํ”„ํŠธ์›จ์–ด์ ์ธ ๋ถ€๋ถ„์—์„œ๋„ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— CPU, ๋ฉ”๋ชจ๋ฆฌ, ๋””์Šคํฌ ์„ธ ๊ณณ ์–ด๋””๋“  ์ž„์‹œ ๊ณต๊ฐ„์„ ๋งŒ๋“ค์–ด ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋ฉด ์บ์‹œ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/Sk9YnWW33.png) ์ง€๊ธˆ๊นŒ์ง€ ์บ์‹œ๋ผ๋Š” ๊ฐœ๋…์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์ €ํฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” iOS์—์„œ ์บ์‹ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋‘๊ฐ€์ง€์˜ ํด๋ž˜์Šค์ด์ž ์บ์‹ฑ ๋ฐฉ์‹์ธ NS Cache์™€ URL Cache์ž…๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/SybFKQbn2.png) ๋จผ์ € NS Cache์ž…๋‹ˆ๋‹ค. key-value ํƒ€์ž…์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ์—๋งŒ ์ €์žฅ๋˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ์บ์‹ฑ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ ์บ์‹ฑ์ด๊ธฐ์— ์•ฑ์„ ์ข…๋ฃŒํ•˜๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚ ์•„๊ฐ‘๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/r1DaItMhn.png) ๋‚ด๋ถ€์— ๋งํฌ๋“œ ๋ฆฌ์ŠคํŠธ์ฒ˜๋Ÿผ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์œผ๋ฉฐ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์„ค์ •์„ ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด์ค‘ ๋ˆˆ์—ฌ๊ฒจ๋ณผ ๊ฒƒ์œผ๋กœ totalCostLimit๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์บ์‹œ๋Š” ์ž‘์€ ๊ณต๊ฐ„์ด๊ธฐ์— ๋งค๋ฒˆ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๋“ค๊ณ  ์žˆ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— ์šฉ๋Ÿ‰์ด ๊ฐ€๋“ ์ฐจ๋ฉด ๊ธฐ์กด์— ์žˆ๋˜ ๊ฒƒ๋“ค์„ ์‚ญ์ œํ•ด์„œ ์ž๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/By5C8tz2n.png) ๊ธฐ๋ณธ์ ์œผ๋กœ NSCache๋Š” ์šฉ๋Ÿ‰์ด ๊ฐ€๋“ ์ฐผ์„ ๋•Œ ์ž๋™์œผ๋กœ ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋“ค์„ ์‚ญ์ œํ•ด์ฃผ๋Š”๋ฐ ์ด๋•Œ ์–ด๋– ํ•œ ๊ฒƒ๋ถ€ํ„ฐ ์‚ญ์ œํ•  ๊ฒƒ์ธ์ง€๋ฅผ ํŒ๋ณ„ํ•˜๋Š” ๊ธฐ์ค€์„ ์ฃผ๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ cost์ž…๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์ž‘์€ cost๊ฐ€ ์ œ์ผ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋กœ ํŒ๋‹จํ•ด ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— totalCostLimit๋ฅผ ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค๋ฉด ๋งค๋ฒˆ ์บ์‹œํ•  ๋•Œ cost๊ฐ’๋„ ๊ฐ™์ด ๋„˜๊ฒจ์ค˜์•ผํ•ฉ๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/BkroPYM33.png) ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์„ ์„ค์ •ํ•ด์ฃผ์ง€ ์•Š๊ณ  ๋งŒ๋“ค์—ˆ์—ˆ๊ธฐ์— setObject์—์„œ cost๊ฐ’์„ ๋„˜๊ฒจ์ค„ ํ•„์š”๊ฐ€ ์—†๊ณ  ์ด ๊ฒฝ์šฐ ๋งŒ์ผ ์šฉ๋Ÿ‰์ด ๊ฐ€๋“ ์ฐจ๋ฉด NSCache๋Š” ์บ์‹ฑ๋œ Object์˜ ์ˆœ์„œ๋Œ€๋กœ ์ž๋™ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/Sy1ZuYG3h.png) ๋‹ค์Œ์€ URL Cache์ž…๋‹ˆ๋‹ค. URL Cache๋Š” NS Cache๋ณด๋‹ค ์กฐ๊ธˆ ๋” ๋‚˜์ค‘์— ๋งŒ๋“ค์–ด์กŒ์–ด์š”. ๋‘ ๊ฐ€์ง€ ํฐ ์ฐจ์ด์ ์ด ์žˆ๋Š”๋ฐ NS Cache๋Š” key - value๋กœ data๋ฅผ ๋„˜๊ฒจ์ฃผ์—ˆ๋‹ค๋ฉด URL Cache๋Š” Request๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜์—ฌ ํ™•์ธํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. Request์— ์•Œ๋งž๋Š” response๊ฐ€ ์ €์žฅ๋˜์–ด ์žˆ๋‹ค๋ฉด ๊ทธ๊ฒƒ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋ฉ”๋ชจ๋ฆฌ ์บ์‹ฑ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋””์Šคํฌ ์บ์‹ฑ๋„ ๊ฐ€๋Šฅํ•ด ์•ฑ์„ ์ข…๋ฃŒํ•ด๋„ ์บ์‹œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚ ์•„๊ฐ€์ง€ ์•Š๊ณ  ์„ค์ •์— ๋”ฐ๋ผ ์•ฑ์„ ์‚ญ์ œํ•ด๋„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์กดํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ![](https://hackmd.io/_uploads/rkFdFFGh2.png) shared๊ฐ€ ๊ธฐ๋ณธ๊ตฌํ˜„๋˜์–ด ์žˆ๊ธฐ์— ์ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์„ ์–ธํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋“ค์€ ๋ฉ”๋ชจ๋ฆฌ์™€ ๋””์Šคํฌ ์ค‘ ์–ด๋””์— ์ €์žฅ๋ ๊นŒ์š”? ๊ณต์‹ ๋ฌธ์„œ์— ๋‚˜์™€์žˆ๋Š” ๊ธฐ๋ณธ ์ •์ฑ… ๊ฐ’์€ .allowed๋กœ ๋‘ ์œ„์น˜ ๋ชจ๋‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/H1uTKtfnh.png) ๊ทธ๋Ÿฌ๋‚˜ ๋‚ด๋ถ€ ๊ตฌํ˜„์„ ๋ณด๋ฉด ๋‘ ์œ„์น˜ ๋ชจ๋‘ 4 MB, 20 MB๋กœ ์šฉ๋Ÿ‰์ด ํ• ๋‹น๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ response๊ฐ€ ์บ์‹œ ์šฉ๋Ÿ‰์˜ 5% ์ด์ƒ์ธ ๊ฒฝ์šฐ๋Š” ์ €์žฅ๋˜์ง€ ์•Š๊ณ  ๋งค๋ฒˆ ์ƒˆ๋กœ์šด request๋ฅผ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ์˜ ์ด๋ฏธ์ง€๋Š” ์šฉ๋Ÿ‰์ด 1.5MB ์ •๋„ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์•„๋‹Œ ๋””์Šคํฌ์— ์ €์žฅ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์ธ ๋””์Šคํฌ ์บ์‹œ ๋””๋ ‰ํ† ๋ฆฌ์— ํ•˜์œ„ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋„ฃ์–ด์ฃผ๊ณ  ์‹ถ๋‹ค๋ฉด nil ๋Œ€์‹  diskPath๋ฅผ ๋งŒ๋“ค์–ด ๋„ฃ์–ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/B1p-qtM3h.png) request๋ฅผ ๋„ฃ์–ด ์ €์žฅ๋œ response๊ฐ€ ์žˆ๋‹ค๋ฉด ํ•ด๋‹น response๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/B1145YG3n.png) ๊ทธ๋Ÿฌ๋‚˜ ๋งŒ์ผ ์—†๋‹ค๋ฉด ์ƒˆ๋กญ๊ฒŒ ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜ค๋ฉฐ storeCachedResponse๋ฅผ ์ด์šฉํ•ด request์™€ response๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/HJwd9Yf23.png) ![](https://hackmd.io/_uploads/ryptcYzh2.png) URL Request์—๋„ ์บ์‹œ ์ •์ฑ…์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์ธ ์„ค์ •์€ ์ด ๊ทธ๋ฆผ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. Q. ๊ทธ๋Ÿฐ๋ฐ ์œ„์˜ ์‹คํ—˜์—์„œ ์ œ์‹œํ•œ ์ด๋ฏธ์ง€ URL์„ URLRequest์— ๋‹ด์•„์„œ ์š”์ฒญ์„ ํ•  ๊ฒฝ์šฐ, ์ด๋ฏธ์ง€๊ฐ€ ์ž๋™์œผ๋กœ ์บ์‹ฑ์ด ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋ฌด์—‡์ผ๊นŒ์š”? -> ํ•ด๊ฒฐ ๋ชป ํ•จ. ![](https://hackmd.io/_uploads/SJ_EsFz23.png) ์ง€๊ธˆ๊นŒ์ง€ ์บ์‹œ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ์œ„ํ•ด ์ž์ฃผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์ตœ๊ทผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์‚ฌ์šฉํ•  ๊ฒƒ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋“ค์„ ์ž„์‹œ ๊ณต๊ฐ„์— ์ €์žฅํ•˜๋Š” ๋‚ด์šฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ฒ˜๋Ÿผ ๋ชฉ์ ์€ ์กฐ๊ธˆ ๋‹ค๋ฅด์ง€๋งŒ ์ผ๋ถ€์˜ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ๋‹ค๋ฅธ ๊ฒƒ๋“ค๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/H1cFjFGh2.png) ๋จผ์ € ์ฟ ํ‚ค์ž…๋‹ˆ๋‹ค. HTTP๋Š” Stateless๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€, ์–ด๋–ค ์ƒํƒœ์ธ์ง€์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€๋ฅผ ์„œ๋ฒ„๊ฐ€ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ ์ฟ ํ‚ค๊ฐ€ ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•œ๋ฒˆ์ด๋ผ๋„ ํ•ด๋‹น ์„œ๋ฒ„์— ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ ‘๊ทผํ•œ๋‹ค๋ฉด ์„œ๋ฒ„๋Š” response์— ์ฟ ํ‚ค๋ฅผ ๋“ค๋ ค์„œ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋Š” ํ•ด๋‹น ์ฟ ํ‚ค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉด์„œ ๊ธฐ๋ณธ์ ์ธ ์ •๋ณด, ์„ค์ • ๋“ฑ์ด ์ฟ ํ‚ค์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ถ”ํ›„์— ์„œ๋ฒ„์— ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญ์„ ํ•  ๋•Œ ์ž๋™์œผ๋กœ ์ฟ ํ‚ค๋„ ํ•จ๊ป˜ ์š”์ฒญ์„ ํ†ตํ•ด ์„œ๋ฒ„๋กœ ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค. ์„œ๋ฒ„๋Š” ํ•ด๋‹น ์ฟ ํ‚ค๋ฅผ ๋ฐ›๊ณ  ๊ธฐ์กด์— ์ ‘์†ํ–ˆ๋˜ ์‚ฌ์šฉ์ž์ž„๊ณผ ๋™์‹œ์— ์–ด๋– ํ•œ ์„ค์ •์„ ํ•ด๋†“์•˜๋Š”์ง€ ์•Œ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/Sky2pFz22.png) ๊ทธ๋Ÿฌ๋‚˜ ์ฟ ํ‚ค๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ๋งŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ์—์„œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/Hkt_g5znh.png) ์ฐธ๊ณ ๋กœ ์ €ํฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” URL Session์—์„œ๋„ ์ฟ ํ‚ค ์ •์ฑ…์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/SJe6atMnh.png) ์ด๋Ÿฌํ•œ ์ฟ ํ‚ค์˜ ๋‹จ์ ์„ ์–ด๋А ์ •๋„ ํ•ด๊ฒฐํ•˜๋Š” ์นœ๊ตฌ๋“ค์ด ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋งŒ ์•Œ๊ณ  ์žˆ๊ฒŒ ํ•˜๋Š” Web Storage์™€ ์„œ๋ฒ„๋งŒ ์•Œ๊ณ  ์žˆ๊ฒŒ ํ•˜๋Š” Session์ž…๋‹ˆ๋‹ค. Web Storage๋Š” ํด๋ผ์ด์–ธํŠธ์— ์ €์žฅ๋งŒ ํ•  ๋ฟ ๋งค๋ฒˆ ๋‹ค์‹œ ์„œ๋ฒ„์— ์ „์†กํ•˜๋Š” ์ฟ ํ‚ค์™€ ๋‹ค๋ฅด๊ฒŒ ์„œ๋ฒ„์—๋Š” ์ „์†กํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ €์žฅ ์šฉ๋Ÿ‰๋„ 5MB๋กœ ๋” ํฝ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๋…๋ฆฝ๋œ storage๋กœ ๊ฐ๊ฐ ๊ณต์œ ํ•  ์ˆ˜๊ฐ€ ์—†๊ณ  ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„์„ ์„ค์ •ํ•ด ์ค„ ์ˆ˜๋„ ์—†์Šต๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/SJELAYf32.png) ์•ž์„œ ์ฟ ํ‚ค๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋ณด์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์–ด๋– ํ•œ ๊ฒฐ์ œ๋ฅผ ํ–ˆ์—ˆ๊ณ ์™€ ๊ฐ™์€ ์ค‘์š”ํ•œ ์ •๋ณด๋“ค์€ ์„œ๋ฒ„๋งŒ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ๋ฅผ ์œ„ํ•ด ์„œ๋ฒ„์—์„œ๋งŒ ์•Œ๊ณ  ์žˆ๋Š” Session์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. Session์€ ์„œ๋ฒ„์— Session DataBase๋ผ๋Š” ์ €์žฅ๊ณต๊ฐ„์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด๊ณณ์—๋Š” ์‚ฌ์šฉ์ž๋“ค์— ๋Œ€ํ•œ ์ค‘์š”ํ•œ ์ •๋ณด๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์•„์ด๋””์™€ ๋น„๋ฒˆ์„ ์ณ์„œ ๋กœ๊ทธ์ธํ•˜๋ฉด Session DataBase์— ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ์„ธ์…˜ id๋ฅผ ์ฟ ํ‚ค์— ๋“ค๋ ค์„œ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋Š” ์˜ค์ง ์ด ์„ธ์…˜ id๋งŒ ์•Œ๊ณ  ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‹ค์‹œ ์„œ๋ฒ„์— ์š”์ฒญ์„ ํ•  ๋•Œ ํด๋ผ์ด์–ธํŠธ๋Š” ์ฟ ํ‚ค์— ์„ธ์…˜ id๋ฅผ ๊ฐ™์ด ๋ณด๋‚ด๊ฒŒ ๋˜๊ณ  ์ด ์„ธ์…˜ id๋ฅผ ๋ณด๊ณ  ์„œ๋ฒ„๋Š” ์„ธ์…˜ DB์—์„œ ํด๋ผ์ด์–ธํŠธ์˜ ์ •๋ณด๋ฅผ ์ฐพ์•„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/rytSg9M2n.png) ๊ทธ๋Ÿฌ๋‚˜ Session์€ DB๊ฐ€ ํ•„์š”ํ•œ ๋งŒํผ ์ˆ˜์—†์ด ๋งŽ์€ ์œ ์ € ์ •๋ณด๋ฅผ ๋งŒ๋“ค์–ด์ค„ ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋น„์šฉ์ด ๋งค์šฐ ๋งŽ์ด ๋“ค๊ฒŒ ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/HyElmczh3.png) ์ด๋Ÿฌํ•œ ์ ๋“ค์„ ๋ณด์™„ํ•œ ๊ฒƒ์ด ํ† ํฐ์ž…๋‹ˆ๋‹ค. ํ† ํฐ์€ ์„œ๋ฒ„๊ฐ€ DB๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฟ ํ‚ค์ฒ˜๋Ÿผ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”๋ฐ ํŠน์ˆ˜ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋กœ๊ทธ์ธํ•ด์„œ ์ƒ๊ฒจ๋‚œ ์œ ์ € data๋ฅผ ์„œ๋ฒ„๋Š” ์‚ฌ์ธ์„ ํ•ด์„œ ํด๋ผ์ด์–ธํŠธ์— ๋ณด๋ƒ…๋‹ˆ๋‹ค. ์•”ํ˜ธํ™”ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ์ค‘๊ฐ„์— ๊ฐ€๋กœ์ฑ„๊ฑฐ๋‚˜ ํ–ˆ์„ ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋ถ€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‚ด์šฉ์„ ๋ณ€๊ฒฝํ•˜๊ฒŒ ๋˜๋ฉด ์‚ฌ์ธ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ–ˆ์„ ๋•Œ ํ†ต๊ณผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— ์„œ๋ฒ„๋Š” ๋‹จ์ˆœํžˆ ํ† ํฐ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌ๋งŒ ํ•  ๋ฟ ์ €์žฅํ•˜๊ณ  ์žˆ์ง€๋Š” ์•Š๊ฒŒ ๋˜์„œ ๋น„์šฉ์ด ์ ๊ฒŒ ๋“ญ๋‹ˆ๋‹ค. ![](https://hackmd.io/_uploads/S1e3X5z33.png) ![](https://hackmd.io/_uploads/S1f3YvNhh.png) ์ง€๊ธˆ๊นŒ์ง€ ์บ์‹œ, ์ฟ ํ‚ค, ์„ธ์…˜, ํ† ํฐ ๋“ฑ ๋‹ค์–‘ํ•œ ์ €์žฅ ๋ฐ ์ธ์ฆ๋ฐฉ์‹์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค~ ### ์ฐธ๊ณ ์ž๋ฃŒ [๐ŸŽ NS Cache](https://developer.apple.com/documentation/foundation/nscache) [๐Ÿ“– NS Cache](https://github.com/apple/swift-corelibs-foundation/blob/main/Sources/Foundation/NSCache.swift) [๐ŸŽ URL Cache](https://developer.apple.com/documentation/foundation/urlcache) [๐Ÿ“– URL Cache](https://github.com/apple/swift-corelibs-foundation/blob/main/Sources/FoundationNetworking/URLCache.swift) [์บ์‹œ๋ฉ”๋ชจ๋ฆฌ](https://www.techtarget.com/searchstorage/definition/cache-memory) # 15์ฃผ์ฐจ: ## ๋ฐœํ‘œ์ž Zion # KeyChain Service ๊ตฌํ˜„(add, update,read ) - KeyChain Service๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•”ํ˜ธํ™”๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•˜๊ณ  ์ฝ์–ด์˜ค๋Š” ์ฝ”๋“œ๋ฅผ ์ง์ ‘์ž‘์„ฑํ•ด๋ณด๊ณ  KeyChain Service๋ฅผ ์ดํ•ดํ•ด๋ณด์ž. ### KeyChainService๋ž€ ๋ฌด์—‡์ผ๊นŒ? - ์ค‘์š”!!! KeyChain์€ App์ด ์‚ญ์ œ๋˜๋”๋ผ๋„ Data๊ฐ€ ๋‚จ์•„์žˆ๋‹ค. - https://medium.com/@LeeZion94/keychain-service-vs-userdefaults-1a550227d749 ### KeyChain์— Data๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž ```swift @IBAction func addNewPassword(_ sender: Any) { guard let password = pwTextField.text else { return } let passwordData = password.data(using: .utf8)! let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: keyName, kSecValueData as String: passwordData] let status = SecItemAdd(query as CFDictionary, nil) switch status { case errSecSuccess: print("์„ฑ๊ณต") case errSecDuplicateItem: updatePasswordOnKeyChain(keyName: keyName, password: passwordData) default: print("KeyChain ๋“ฑ๋ก ์‹คํŒจ") } } ``` - ์œ„์˜ ์ฝ”๋“œ๋Š” KeyChainService๋ฅผ ํ™œ์šฉํ•˜์—ฌ KeyChain์— User๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” Password๋ฅผ ์ €์žฅ(Add)ํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์œ ์ €๊ฐ€ ์ž…๋ ฅํ•œ password๋ฅผ ๋ฐ›์•„์„œ ์ด๋ฅผ dataํ™” ์‹œํ‚จ๋’ค keychain์— ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ query๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ CFDictionary์˜ ํ˜•ํƒœ๋กœ ์ €์žฅํ•˜๊ณ  ์ €์žฅ๋œ ๊ฒฐ๊ณผ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ```swift let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: keyName, kSecValueData as String: passwordData] ``` - kSecClass๋Š” Keychain์— ์ €์žฅํ•˜๋ ค๊ณ  ํ•˜๋Š” data์˜ ์ข…๋ฅ˜์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ์˜ˆ์‹œ์—์„œ๋Š” Keychain์œผ๋กœ User๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” Password๋ฅผ ์ €์žฅํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ kSecClassGenericPassword๋ฅผ Value๊ฐ’์œผ๋กœ ์„ค์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค. - kSecAttr ํ‚ค์›Œ๋“œ๊ฐ€ ๋ถ™์€ kSecAttrAccount๋Š” Data๋ฅผ ๊ตฌ๋ถ„์ง“๊ธฐ ์œ„ํ•œ ํŠน์„ฑ ๊ฐ’์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ’์ž…๋‹ˆ๋‹ค. kSecAttr ํ‚ค์›Œ๋“œ๊ฐ€ ๋ถ™์€ ๊ฐ’๋“ค์€ ํ•ด๋‹น Data๋ฅผ ๊ตฌ๋ถ„ ์ง“๋Š” ํŠน์„ฑ ๊ฐ’์œผ๋กœ ํ™œ์šฉ๋  ์ˆ˜ ์žˆ๊ณ  ์œ„์˜ ์˜ˆ์‹œ์—์„œ๋Š” User์˜ Account ๊ฐ’์„ Data๋ฅผ ๊ตฌ๋ถ„์ง“๊ธฐ ์œ„ํ•œ ํŠน์„ฑ ๊ฐ’์œผ๋กœ ํ™œ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ kSecAttrAccount ๊ฐ’์„ ํŠน์„ฑ ๊ฐ’์œผ๋กœ ๋„ฃ์–ด์ฃผ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. - kSecValueData๋Š” ์‹ค์งˆ์ ์œผ๋กœ ์ €์žฅํ•˜๋ ค๊ณ  ํ•˜๋Š” Data์ž…๋‹ˆ๋‹ค. kSecClass์—์„œ User์˜ Password๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด kSecClassGenericPassword๋ฅผ Value๊ฐ’์œผ๋กœ ๋„ฃ์–ด์คซ์œผ๋ฏ€๋กœ Data ๊ฐ’์œผ๋กœ๋Š” User๊ฐ€ ์ž…๋ ฅํ•œ Password๋ฅผ ๊ฐ’์œผ๋กœ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค. ```swift let status = SecItemAdd(query as CFDictionary, nil) switch status { case errSecSuccess: print("์„ฑ๊ณต") case errSecDuplicateItem: updatePasswordOnKeyChain(keyName: keyName, password: passwordData) default: print("") } ``` - SecItemAdd์€ ์ง€๊ธˆ๊นŒ์ง€ ๋งŒ๋“  query ๊ฐ’์„ CFDictionary๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์‹ค์งˆ์ ์œผ๋กœ Keychain์œผ๋กœ ์ €์žฅํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค. return ๊ฐ’์œผ๋กœ OSStatus ๊ฐ’์„ ๊ฐ€์ง€๋ฉฐ ์ €์žฅ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋˜์—ˆ๋Š”์ง€, ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด ์–ด๋–คํ•œ ์ด์œ ๋กœ ๋˜์ง€ ์•Š์•˜๋Š”์ง€๋ฅผ status ๊ฐ’์œผ๋กœ return ํ•ด์ค๋‹ˆ๋‹ค. - errSecSuccess๋Š” KeyChain์— data๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ €์žฅ์ด ๋˜์—ˆ๋‹ค๋Š” ์ƒํƒœ๊ฐ’์ž…๋‹ˆ๋‹ค. - errSecDuplicateItem๋Š” ํ•ด๋‹น attr(์†์„ฑ) ๊ฐ’์œผ๋กœ ์ด๋ฏธ ์ €์žฅ๋œ keyChain data๊ฐ€ ์กด์žฌํ•œ๋‹ค๋Š” ์ƒํƒœ๊ฐ’์ž…๋‹ˆ๋‹ค. ์•„๋ฌด๋Ÿฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ ๋“ฑ๋กํ–ˆ์—ˆ๋˜ ํŠน์„ฑ ๊ฐ’์„ ํ™œ์šฉํ•˜์—ฌ ๊ธฐ์กด์— ์กด์žฌํ–ˆ๋˜ ๊ฐ’์„ ์ƒˆ๋กญ๊ฒŒ ์ž…๋ ฅ ๋ฐ›์€ ๊ฐ’์œผ๋กœ Update ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ### KeyChain์— Data๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ด๋ณด์ž ```swift private func updatePasswordOnKeyChain(keyName: String, password: Data) { let previousQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: keyName] let updateQuery: [String: Any] = [kSecValueData as String: password] let state = SecItemUpdate(previousQuery as CFDictionary, updateQuery as CFDictionary) switch state { case errSecSuccess: print("update ์„ฑ๊ณต") default: print("์‹คํŒจ") } } ``` - KeyChain ์ €์žฅ์‹œ ํ•ด๋‹น attr ์†์„ฑ๊ฐ’์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ ์ด๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” query๋ฅผ ๋งŒ๋“ค์–ด์„œ ํ•ด๋‹น KeyChain ์†์„ฑ ๊ฐ’์— ์ €์žฅ๋œ data ๊ฐ’์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ```swift let previousQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: keyName] let updateQuery: [String: Any] = [kSecValueData as String: password] ``` - previouQuery๋Š” ์–ด๋–ค data๋ฅผ ์—…๋ฐ์ดํŠธ ํ•  ๊ฒƒ ์ธ์ง€ ์ €์žฅ๋œ data์˜ ์ข…๋ฅ˜์™€ ์†์„ฑ ๊ฐ’์„ ๋ช…์‹œํ•ด์ค˜์„œ ์–ด๋–ค data๋ฅผ ์—…๋ฐ์ดํŠธ ํ•  ๊ฒƒ์ธ์ง€๋ฅผ query๋กœ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - updateQuery์˜ ๊ฒฝ์šฐ์—๋Š” ์‹ค์งˆ์ ์œผ๋กœ update ํ•˜๋ ค๋Š” data์˜ ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๊ณ  ๊ฐฑ์‹ ์‹œ์ผœ์ค๋‹ˆ๋‹ค. ```swift let state = SecItemUpdate(previousQuery as CFDictionary, updateQuery as CFDictionary) switch state { case errSecSuccess: print("update ์„ฑ๊ณต") default: print("์‹คํŒจ") } ``` - SecItemUpdate ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฒซ๋ฒˆ์งธ ์ธ์ž๋กœ๋Š” data๋ฅผ ์ฐพ์•„๊ฐ€๊ธฐ ์œ„ํ•œ query, ๋‘๋ฒˆ์งธ ์ธ์ž๋กœ๋Š” ๊ฐฑ์‹ ํ•˜๋ ค๊ณ  ํ•˜๋Š” data์˜ query๋ฅผ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค. - state ๊ฐ’์„ return ๊ฐ’์œผ๋กœ ๋ฐ›์•„์™€์„œ ์—…๋ฐ์ดํŠธ์— ์„ฑ๊ณตํ–ˆ๋Š”์ง€ ์‹คํŒจํ–ˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ### KeyChain์—์„œ Data๋ฅผ ์ฝ์–ด๋ณด์ž ```swift private func readPasswordOnKeyChain(_ inputPassword: String) { let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: keyName, kSecReturnAttributes as String: true, kSecReturnData as String: true] var item: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &item) switch status { case errSecSuccess: guard let existingItems = item as? [String: Any], let passwordData = existingItems[kSecValueData as String] as? Data, let password = String(data: passwordData, encoding: .utf8), inputPassword == password else { fallthrough } print("๋น„๋ฐ€๋ฒˆํ˜ธ - \(password)") default: print("๋กœ๊ทธ์ธ ์‹คํŒจ") } } ``` - ํ•ด๋‹น ๋กœ์ง์€ ์ €์žฅ๋œ ํ‚ค์ฒด์ธ์˜ ๊ฐ’์„ ๋ถ€์—ฌํ•œ attr ์†์„ฑ ๊ฐ’์„ ํ†ตํ•ด์„œ ์ฝ์–ด์˜ค๋Š” ๋กœ์ง์ž…๋‹ˆ๋‹ค. ```swift let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: keyName, kSecReturnAttributes as String: true, kSecReturnData as String: true] ``` - ๋จผ์ € ์ฝ์„ data์˜ ์ข…๋ฅ˜๋ฅผ kSecClassGenericPassword๋กœ ๋ช…์‹œํ•˜๊ณ  ๋“ฑ๋กํ•œ Attr ๊ฐ’์„ ํ†ตํ•ด ์–ด๋– ํ•œ ์†์„ฑ๊ฐ’์„ ๊ฐ€์ง€๋Š” Data๋ฅผ ์ฝ์–ด์˜ฌ์ง€ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. - ๋˜ํ•œ kSecReturnAttributes ํ•ด๋‹น ์˜ต์…˜ ๊ฐ’์„ ํ†ตํ•ด์„œ data๋ฅผ ์ฝ์–ด ์˜ฌ ๋•Œ ๋“ฑ๋กํ•ด ๋‘์—ˆ๋˜ attr ์†์„ฑ ๊ฐ’ ๋˜ํ•œ ๊ฐ™์ด Dictionary๋กœ ๋ฐ›์•„์˜ฌ์ง€ ์ง€์ •ํ•ด ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. true๋กœ ์ค€๋‹ค๋ฉด data๋ฅผ ์ €์žฅํ•  ๋•Œ ๊ฐ™์ด ๋“ฑ๋กํ–ˆ๋˜ attr ์†์„ฑ ๊ฐ’ ๋˜ํ•œ ๊ฐ™์ด load๋˜๊ณ  false๋ฅผ ๋ถ€์—ฌํ•œ๋‹ค๋ฉด read ํ•˜๊ธฐ๋ฅผ ์›ํ–ˆ๋˜ data๋งŒ ๋ฐ›์•„์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. - kSecReturnData data ๊ฐ’์„ ๋ฐ›์•„์˜ค๊ฒ ๋‹ค๋Š” ์˜ต์…˜ ๊ฐ’์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ```swift var item: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &item) ``` - SecItemCopyMatching ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ KeyChain์— ์ €์žฅ๋œ ๊ฐ’์„ ์ฝ์–ด์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. - ๋“ฑ๋ก๋œ ๊ฐ’์„ ์ฝ์–ด์˜ค๊ธฐ์œ„ํ•ด ๋งŒ๋“ค์–ด์ฃผ์—‡๋˜ query๋ฅผ ํ•ด๋‹น ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„ฃ์–ด์ฃผ๊ณ , data ๊ฐ’์„ ๋ฐ›์•„์˜ค๊ธฐ์œ„ํ•ด ๋งŒ๋“ค์–ด ๋‘์—ˆ๋˜ item๋ณ€์ˆ˜์˜ ์ฃผ์†Œ๊ฐ’์„ ํ•ด๋‹น ๋ฉ”์„œ๋“œ์— ์ „๋‹ฌํ•ด์ฃผ๋ฉด์„œ item ๊ฐ’์„ ํ†ตํ•ด ์›ํ•˜๋Š” data ๋ฐ attr ์†์„ฑ ๊ฐ’์„ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ```swift case errSecSuccess: guard let existingItems = item as? [String: Any], let passwordData = existingItems[kSecValueData as String] as? Data, let password = String(data: passwordData, encoding: .utf8), inputPassword == password else { fallthrough } ``` - item์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ Read๋ฃŒ ํ–ˆ๋‹ค๋ฉด ๋ถ€์—ฌํ–ˆ๋˜ ์˜ต์…˜ ๊ฐ’์„ ํ†ตํ•ด data์™€ attr ์†์„ฑ ๊ฐ’๋“ค์„ Dictionary๋กœ ์ „๋‹ฌ๋ฐ›์•„์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. - kSecReturnAttributes ์˜ต์…˜ ๊ฐ’์„ true๋กœ ๋ถ€์—ฌํ–ˆ์œผ๋ฏ€๋กœ Dictionary๋กœ ํ˜•๋ณ€ํ™˜ ์ž‘์—…์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. false๋กœ ๋ถ€์—ฌํ–ˆ๋‹ค๋ฉด attr ์†์„ฑ ๊ฐ’์ด ๊ฐ™์ด read ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— Dictionary๋กœ ํ˜•๋ณ€ํ™˜์„ ํ•  ํ•„์š”๊ฐ€ ์—†์ด Data๋งŒ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. - Data์— ํ•ด๋‹นํ•˜๋Š” Key๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ data๋ฅผ ๋ฐ›๊ณ  ์ด๋ฅผ ๋‹ค์‹œ encodingํ•œ๋‹ค๋ฉด ์ €์žฅ๋˜์–ด ์žˆ์—ˆ๋˜ password ๊ฐ’์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฝ์–ด์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. --- # 16์ฃผ์ฐจ ## ๋ฐœํ‘œ์ž - redmango # CoreData ![](https://hackmd.io/_uploads/HJ4hLOzyp.jpg) --- ![](https://hackmd.io/_uploads/SkN28OMkT.jpg) --- ![](https://hackmd.io/_uploads/HJlV2LuzJT.jpg) - ๊ฐ์ฒด ๊ด€๋ฆฌ ๊ทธ๋ž˜ํ”„๋ž€: ๊ฐ์ฒด๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ๊ทธ๋ž˜ํ”„๋กœ ๋‚˜ํƒ€๋‚ธ ๊ฒƒ --- ![](https://hackmd.io/_uploads/SylNhLdGJa.jpg) - DB์–ธ์–ด๋ฅผ ๋ฐฐ์šธ ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ์ ์—์„œ ๋งค์šฐ ํŽธ๋ฆฌ --- ![](https://hackmd.io/_uploads/r1lEnUdfyp.jpg) --- ![](https://hackmd.io/_uploads/HybV38dzya.jpg) - ์Šค๋ ˆ๋“œ ์„ธ์ดํ”„๋ฅผ ๊ณ ๋ คํ•˜์ง€ ์•Š์•˜๋‹ค๋Š”๊ฑธ ์ฃผ์˜ - mainQueue๋Š” ์•„๋ฌด๋ž˜๋„ UI๋•Œ๋ฌธ์ธ๊ฒƒ์œผ๋กœ ์ถ”์ธก(๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋•Œ๋ฌธ์— UI๋จนํ†ต ๋ ์ˆ˜๋„..) --- ![](https://hackmd.io/_uploads/HJNhL_zJp.jpg) --- ![](https://hackmd.io/_uploads/HJWV38dfJp.jpg) - ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚ ์•„๊ฐ€๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒ ํ•  ์ˆ˜ ์žˆ๋‹ค. --- ![](https://hackmd.io/_uploads/ryEnLuGka.jpg) --- ![](https://hackmd.io/_uploads/ByeEnI_Gkp.jpg) --- # 17์ฃผ์ฐจ: ## ๋ฐœํ‘œ์ž: Jusbug # UIView Animations ![Main Page](https://hackmd.io/_uploads/rJtTIjNka.png) ![Abstract](https://hackmd.io/_uploads/HJ0pIjVJ6.png) ![Animation](https://hackmd.io/_uploads/Hyg0UiEkp.png) ํ™”๋ฉด ์š”์†Œ์˜ ์›€์ง์ž„๊ณผ ๋ณ€ํ™”๋ฅผ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. UIView ์• ๋‹ˆ๋ฉ”์ด์…˜์€ ์‹œ๊ฐ์  ํšจ๊ณผ๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค ์š”์†Œ์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. UIView ์• ๋‹ˆ๋ฉ”์ด์…˜์€ ๊ธฐ๋ณธ์ ์œผ๋กœ UIView ํด๋ž˜์Šค์˜ ์ผ๋ถ€ ๋ฉ”์„œ๋“œ์™€ ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์„ฑ๋ฉ๋‹ˆ๋‹ค. ![Animate()-1](https://hackmd.io/_uploads/H11QvsEy6.png) ![Animate()-2](https://hackmd.io/_uploads/HJDC8sVyT.png) ![AnimateKeyframes-1](https://hackmd.io/_uploads/S15A8j4kT.png) ๋‹ค๋‹จ๊ณ„ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋งŒ๋“ค๊ณ  ์‹คํ–‰ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ฉ”๋“œ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋กœ ๊ตฌ์„ฑ๋œ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ตฌํ˜„ํ•˜๊ณ  ๊ฐ ๋‹จ๊ณ„์—์„œ ๋‹ค์–‘ํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - `animateKeyframes()` ๋ฉ”์„œ๋“œ๋Š” ๋ทฐ์˜ ํ‚คํ”„๋ ˆ์ž„ ๊ธฐ๋ฐ˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ƒ์„ฑํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. - ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ‚คํ”„๋ ˆ์ž„์„ ์ •์˜ํ•˜๊ณ , ๊ฐ ํ‚คํ”„๋ ˆ์ž„์—์„œ ๋ทฐ์˜ ์†์„ฑ์„ ๋ณ€๊ฒฝํ•˜๋ฉฐ, ๊ฐ ํ‚คํ”„๋ ˆ์ž„ ์‚ฌ์ด์˜ ์ „ํ™˜์„ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค. - iOS 7 ์ด์ƒ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๋” ๋ณต์žกํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ๋‚˜๋ฆฌ์˜ค์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ![AnimateKeyframes-2](https://hackmd.io/_uploads/BkCC8iNk6.png) ์ฃผ์š” ๋ชฉ์ ์€ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋กœ ๊ตฌ์„ฑ๋œ ๋ณต์žกํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ ๋‹จ๊ณ„์—์„œ addKeyframe๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ์†์„ฑ์˜ ๋ณ€ํ™”๋ฅผ ์ •์˜ํ•˜๊ณ , ์ „์ฒด ์ง€์† ์‹œ๊ฐ„ ๋‚ด์—์„œ ๋‹ค์–‘ํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ![CGAffineTransform-1](https://hackmd.io/_uploads/BkZJwiNyT.png) Core Graphics ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ณ€ํ™˜ ๋งคํŠธ๋ฆญ์Šค๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. ์ด ๋งคํŠธ๋ฆญ์Šค๋Š” 2D ๊ณต๊ฐ„์—์„œ ๊ฐ์ฒด์˜ ๋ณ€ํ™˜(์ด๋™, ํšŒ์ „, ํฌ๊ธฐ, ๊ธฐ์šธ์ž„ ๋“ฑ)์„ ํ‘œํ˜„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ![CGAffineTransform-2](https://hackmd.io/_uploads/r1SkPi4yT.png) translation(์œ„์น˜), scaling(ํฌ๊ธฐ), rotation(ํšŒ์ „)์˜ ์š”์†Œ๋ฅผ ๋ณ€ํ™˜ํ•˜์—ฌ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Concatenation(ํ•ฉ๋ณ‘ ์• ๋‹ˆ๋ฉ”์ด์…˜)์„ ์ด์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ณ€ํ™˜์„ ํ•˜๋‚˜๋กœ ๊ฒฐํ•ฉํ•˜์—ฌ ๋™์‹œ์— ์‹คํ–‰ํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ์–ด ๋” ๋ณต์žกํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ![frame vs transform](https://hackmd.io/_uploads/rkF1DjVka.png) ์• ๋‹ˆ๋ฉ”์ด์…˜์—์„œ `frame` ์†์„ฑ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์€ ์‹ค์ œ ํ•ด๋‹น ๋ทฐ์˜ ํฌ๊ธฐ์™€ ์œ„์น˜ ๋“ฑ์„ ์ง์ ‘ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ๋  ๊ฒฝ์šฐ, ๋‹ค๋ฅธ ํ•˜์œ„ ๋ทฐ์™€ ์ปจํ…์ธ ์— ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ๋Š” ๋ฆฌ์Šคํฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด, `transform`์„ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์€ ์‹ค์ œ ์œ„์น˜์™€ ํฌ๊ธฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ Œ๋”๋ง์—๋งŒ ์˜ํ–ฅ์„ ์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ๋ทฐ์™€์˜ ๊ฐ„์„ญ์ด ์ผ์–ด๋‚  ์šฐ๋ ค๊ฐ€ ์—†์–ด ์—ฌ๋Ÿฌ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ตฌํ˜„ํ•  ๋•Œ์—๋Š” `transform`์„ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ๋” ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค. ![identity & removeAnimation](https://hackmd.io/_uploads/S1jJDo4kp.png) `identity`๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋™์ž‘ ์ด์ „ ๋ทฐ์˜ ์ •๋ณด๋ฅผ ๊ฐ–๊ณ  ์žˆ๋Š” ์†์„ฑ์œผ๋กœ ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋™์ž‘ ํ›„ ์ดˆ๊ธฐ ๋ทฐ๋กœ ๋˜๋Œ๋ฆด ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹จ ์œ„์น˜, ํฌ๊ธฐ, ํšŒ์ „์— ๋Œ€ํ•œ ์ •๋ณด๋งŒ ๊ฐ–๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— alpha, image์™€ ๊ฐ™์€ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์š”์†Œ๋“ค์€ `removeAnimation()`๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ง์ ‘ ์ •์ง€์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ![Quiz](https://hackmd.io/_uploads/BkR1DoVJa.png) 1. Completion Handler 2. ์—ฌ๋Ÿฌ ๋‹จ๊ณ„์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ตฌํ˜„ํ•˜๊ธฐ์— ์šฉ์ดํ•˜๋‹ค. 3. ์œ„์น˜, ํฌ๊ธฐ, ํšŒ์ „ 4. frame, transform 5. alpha, image... ![Last Page](https://hackmd.io/_uploads/BkexwoV1a.png) ### ๐Ÿ‘จโ€๐Ÿ”ฌ ์‹คํ—˜ ๋ชฉํ‘œ UIView Animation์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ### ๐Ÿ‘จโ€๐Ÿ”ฌ ์‹คํ—˜ 1: ์ด๋ชจํ‹ฐ์ฝ˜ ๋งŒ๋“ค๊ธฐ - ์—๋Ÿฌ๋ฅผ ๋งŒ๋‚œ ์•ผ๊ณฐ ```swift @IBAction func tapErrorYagom(_ sender: Any) { UIView.animateKeyframes(withDuration: 0, delay: 0, options: [.repeat, .autoreverse], animations: { UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1/4) { self.YagomImageView.transform = CGAffineTransform(translationX: 10, y: 0) } UIView.addKeyframe(withRelativeStartTime: 1/4, relativeDuration: 3/4) { self.YagomImageView.transform = CGAffineTransform(translationX: -20, y: 0) } UIView.addKeyframe(withRelativeStartTime: 3/4, relativeDuration: 1/4) { self.YagomImageView.transform = CGAffineTransform(translationX: +10, y: 0) } }) } ``` ### ๐Ÿ‘จโ€๐Ÿ”ฌ ์‹คํ—˜ 2: ์ด๋ชจํ‹ฐ์ฝ˜ ๋งŒ๋“ค๊ธฐ - ์ƒˆ ๋งฅ๋ถ์„ ๊ตฌ๋งคํ•œ ์•ผ๊ณฐ ```swift @IBAction func tapNewMacbookYagom(_ sender: Any) { UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: [.repeat, .autoreverse], animations: { UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1/2) { self.YagomImageView.transform = CGAffineTransform(scaleX: 1, y: 2) // ๋ฐฐ์œจ ๊ฐ’ } UIView.addKeyframe(withRelativeStartTime: 1/2, relativeDuration: 1/2) { self.YagomImageView.transform = CGAffineTransform(scaleX: 2, y: 1) } }) } ``` ### ๐Ÿ‘จโ€๐Ÿ”ฌ ์‹คํ—˜ 3 (์„ ํƒ): Custom ์ด๋ชจํ‹ฐ์ฝ˜ ๋งŒ๋“ค๊ธฐ ```swift @IBAction func tapcustomYagom(_ sender: Any) { UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: [.repeat, .autoreverse], animations: { UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1/4) { //self.YagomImageView.image = .checkmark // ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ //self.YagomImageView.image = .add // ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ self.YagomImageView.alpha = 0.5 // 0.0 ~ 1.0 self.YagomImageView.transform = CGAffineTransform(rotationAngle: 45.0) self.YagomImageView.transform = CGAffineTransform(translationX: 20, y: -20) } UIView.addKeyframe(withRelativeStartTime: 1/4, relativeDuration: 3/4) { self.YagomImageView.alpha = 0.2 // 0.0 ~ 1.0 self.YagomImageView.transform = CGAffineTransform(rotationAngle: -90.0) } UIView.addKeyframe(withRelativeStartTime: 3/4, relativeDuration: 1/4) { self.YagomImageView.alpha = 1.0 // 0.0 ~ 1.0 self.YagomImageView.transform = CGAffineTransform(rotationAngle: 45.0) } }) } ``` ### ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ •์ง€ ๋ฒ„ํŠผ ```swift= @IBAction func tapBackToNormal(_ sender: Any) { self.YagomImageView.layer.removeAllAnimations() // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ค‘์ง€ UIView.animate(withDuration: 0.5, delay: 0) { self.YagomImageView.transform = .identity // ๋ทฐ๋ฅผ ์›๋ž˜๋Œ€๋กœ ๋ณต์› } } ``` --- # 18์ฃผ์ฐจ: ## ๋ฐœํ‘œ์ž: Yetti # SWiftUI ![SwiftUI](https://hackmd.io/_uploads/HkU82HV1a.png) Better apps. Less Code.๋ผ๋Š” ํ‘œ์–ด๊ฐ€ SwiftUI๋ฅผ ์ž˜ ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ![Contents](https://hackmd.io/_uploads/Hk582SEyp.png) ![SwiftUI ์†Œ๊ฐœ](https://hackmd.io/_uploads/ry6U3r4kp.png) ์„ ์–ธํ˜• UI์ธ๊ฒƒ๊ณผ ๋ฉ€ํ‹ฐ ํ”Œ๋žซํผ์ธ ์ ์ด ๋ˆˆ์— ๋•๋‹ˆ๋‹ค ![์ฝ”๋“œ๊ธธ์ด๋น„๊ต](https://hackmd.io/_uploads/H1BDnBE1a.png) ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐ™์€ UI์„ ๊ตฌํ˜„ํ–ˆ์„ ๋–„ UIKit๋ณด๋‹ค ํ™•์—ฐํžˆ ์ ์€ ์ฝ”๋“œ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ![UIKit๊ณผ ๋น„๊ต](https://hackmd.io/_uploads/SJvv2rNya.png) ์—ฌ๋Ÿฌ์ฐจ์ด์ ์ด ์žˆ๊ฒ ์ง€๋งŒ 4๊ฐ€์ง€์ •๋„๋กœ ๊ตฌ๋ถ„ํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ![๋ช…๋ นํ˜•๊ณผ ์„ ์–ธํ˜• UI ์ฐจ์ด](https://hackmd.io/_uploads/HyYw3HVk6.png) ![๋ช…๋ นํ˜• UI](https://hackmd.io/_uploads/HynvhH41p.png) ๋ช…๋ นํ˜•์€ UI๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ํ–‰์œ„๋ฅผ ํ•˜๋‚˜ํ•˜๋‚˜ ์•Œ๋ ค์ค˜์•ผํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ![์„ ์–ธํ˜• UI](https://hackmd.io/_uploads/ry7T2SEk6.png) ์ตœ์ข…์ ์œผ๋กœ ๋ฌด์—‡์„ ํ• ์ง€ ์•Œ๋ ค์ฃผ๋ฉด ๊ทธ ๋ชฉํ‘œ์— ๋„๋‹ฌํ•˜๊ธฐ๊นŒ์ง€์˜ ๋™์ž‘์€ ์•Œ์•„์„œ ํ•ด์ฃผ๋Š” ๋А๋‚Œ?์ด๋ž„๊นŒ ![์ด๋ฒคํŠธ, ๋ฐ์ดํ„ฐ ์ค‘์‹ฌ](https://hackmd.io/_uploads/S1w6hrEka.png) ![ํด๋ž˜์Šค ๊ธฐ๋ฐ˜, ๊ตฌ์กฐ์ฒด ํ”„๋กœํ† ์ฝœ ๊ธฐ๋ฐ˜](https://hackmd.io/_uploads/r1KTnrNJT.png) ![layout 3๋‹จ๊ณ„](https://hackmd.io/_uploads/ryip3S4J6.png) ![1๋‹จ๊ณ„](https://hackmd.io/_uploads/r1T6hSN1p.png) ![2๋‹จ๊ณ„](https://hackmd.io/_uploads/rkJ0hHN16.png) ![3๋‹จ๊ณ„](https://hackmd.io/_uploads/SJfR2B41a.png) ![some](https://hackmd.io/_uploads/SJ_X6r4JT.png) ![property wrapper1](https://hackmd.io/_uploads/r1aQpB4J6.png) ![property wrapper2](https://hackmd.io/_uploads/SyRmpSVJT.png) ![observedObject, StateObject๋น„๊ต](https://hackmd.io/_uploads/HyZEaHNy6.png) ![๊ทธ์ทจ๋ญ](https://hackmd.io/_uploads/H1EVpH41a.png) ![์ฃผ๊ด€์  ์˜๊ฒฌ](https://hackmd.io/_uploads/r1IN6SN1a.png)