# ๐ฅ๋ถํ๋ ํ ์์คํฐ๋_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 ํ์ฉํด๋ณด๊ธฐ
## ๐ป ๋ฐํ์๋ฃ


**< Outline >**
1.Responder chain
2.hitTest
3.hitTest Logic
4.hitTest Code

**Responder**: UIResponder๋ ์ผ๋ จ์ ์ด๋ฒคํธ๋ฅผ ๋ฐ๊ณ ์ฒ๋ฆฌํ๊ฑฐ๋ ๋ค๋ฅธ ๊ฐ์ฒด์ ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ๋ ์ญํ ์ ํฉ๋๋ค.
**Responder chain**:์ฌ๊ธฐ์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ์ง ๋ชปํด ๋๊ฒจ์ง ๊ฒฝ์ฐ ๋ค์ ๊ฐ์ฒด๋ก ์ฐ๊ฒฐ๋์ด ์ด๋ํ๋๋ฐ ์ด์ฒ๋ผ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ๋ชฉ์ ์ผ๋ก ์ฐ๊ฒฐ๋์ด ์๋ Responder ๊ฐ์ฒด๋ค์ ์ฐ๊ฒฐ ๊ด๊ณ๊ฐ Responder Chain ์
๋๋ค.

**First Responder**: UIKit์ ์๋์ผ๋ก ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ์ ๊ฐ์ฅ ์ ์ ํ Responder ๊ฐ์ฒด๋ฅผ ์ฐพ์ ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ๋๋ฐ ์ด ๊ฐ์ฒด๊ฐ First Responder์
๋๋ค.

UIEvent ํ์
์ ์ข
๋ฅ์ ์ด๋ฒคํธ๋ฅผ ์ ๋ฌ ๋ฐ๋ First Reponder

**< Responder chain Logic >**
1. ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด, ํด๋น ์ด๋ฒคํธ๋ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋ทฐ๋ฅผ ๊ธฐ์ค์ผ๋ก Responder Chain์ ๋ฐ๋ผ ์ด๋ํฉ๋๋ค.
2. ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ์์ ๊ฐ์ฅ ์์ชฝ์ ์๋ ๋ทฐ๋ถํฐ ์ด๋ฒคํธ์ ์๋ตํ ์ ์๋์ง ํ์ธํ๊ณ , ์๋ตํ ์ ์๋ค๋ฉด ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค. ์๋ตํ ์ ์๋ ๊ฒฝ์ฐ์๋ ์์ ๋ทฐ๋ก ์ด๋ฒคํธ๊ฐ ์ ๋ฌ๋ฉ๋๋ค.
3. ์ด๋ฒคํธ๋ ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ์์ ์์๋ก ์ ๋ฌ๋๋ฉฐ, UIView๋ UIResponder์ ์๋ธํด๋์ค์ด๋ฏ๋ก, ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ ๊ฐ์ฒด๊ฐ UIResponder์ ์๋ธํด๋์ค ์ค ํ๋๋ผ๋ฉด ํด๋น ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
4. ๋ง์ฝ ์ด๋ฒคํธ๊ฐ ์ต์์๊น์ง ์ ๋ฌ๋์ด๋ ํด๋น ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ ๊ฐ์ฒด๋ฅผ ์ฐพ์ง ๋ชปํ๋ฉด ์ด๋ฒคํธ๋ ๋ฌด์๋ฉ๋๋ค.

**hitTest**: UIViewํด๋์ค์ ์ ์๋ ๋ฉ์๋๋ก, ํฐ์น ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ํฌ์ธํธ์ ํด๋นํ๋ ์ต์๋จ์ ๋ทฐ๋ฅผ ํ์ํ๋ ์ญํ ์ ๊ฐ์ต๋๋ค. ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด์๋ ์ฌ์ฉ์๊ฐ ํฐ์นํ ์ขํ๊ฐ ์ด๋ค ๋ทฐ์ ์์นํ๋์ง ํ์
ํด์ผํ๋๋ฐ ์ฌ๊ธฐ์ **hitTest**๋ฅผ ํตํด ํ์ํ ๋ทฐ์ ๋ํ ํฐ์น ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.

์ง๊ธ ๋ณด์ด๋ ์ฝ๋๋ UIView ํด๋์ค์ **hitTest** ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ์ฌ ์ ์ํ ๊ตฌํ์ ๋ณด์ฌ์ฃผ๊ณ ์์ต๋๋ค. **hitTest** ๋ฉ์๋๋ ํน์ ์ขํ์ ํด๋นํ๋ ๋ทฐ๋ฅผ ์ฐพ๋ ์ญํ ์ ํ์ฃ . ์ด ์ฝ๋๋ ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ์์ ํฐ์น ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ์ขํ์ ํด๋นํ๋ ๋ทฐ๋ฅผ ์ฐพ๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.
- `isUserInteractionEnabled`: false์ผ ๊ฒฝ์ฐ, ํด๋น ๋ทฐ์ ๊ทธ ํ์ ๋ทฐ๋ค์ ์ฌ์ฉ์์ ์ํธ์์ฉ์ ๋ฐ์ ์ ์์ต๋๋ค.
- `isHidden`: true์ผ ๊ฒฝ์ฐ, ํด๋น ๋ทฐ์ ๊ทธ ํ์ ๋ทฐ๋ค์ ํ๋ฉด์ ํ์๋์ง ์์ต๋๋ค.
- `alpha`: ๋ทฐ์ ๋ถํฌ๋ช
๋๋ฅผ ๋ํ๋
๋๋ค. 0.0๋ถํฐ 1.0 ์ฌ์ด์ ๊ฐ์ ๊ฐ์ง๋ฉฐ, 0.01๋ณด๋ค ์์ผ๋ฉด ๋ทฐ๋ ๊ฑฐ์ ํฌ๋ช
ํ ์ํ๊ฐ ๋ฉ๋๋ค.
๋ง์ฝ ํด๋น ๋ทฐ๊ฐ ์ฌ์ฉ์ ์ํธ์์ฉ์ ๋ฐ์ ์ ์๊ฑฐ๋, ํ๋ฉด์ ํ์๋์ง ์๊ฑฐ๋, ๊ฑฐ์ ํฌ๋ช
ํ ์ํ๋ผ๋ฉด, ํด๋น ๋ทฐ๋ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์์ผ๋ฏ๋ก nil์ ๋ฐํํฉ๋๋ค.
๊ทธ ๋ค์, ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ์ขํ(point)๊ฐ ํ์ฌ ๋ทฐ์ ๋ฒ์์ ์ํ๋์ง (self.point(inside: point, with: event))๋ฅผ ํ์ธํฉ๋๋ค. ๋ง์ฝ ํด๋น ์ขํ๊ฐ ๋ทฐ์ ๋ฒ์์ ์ํ๋ค๋ฉด, ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ์์ ํ์ ๋ทฐ๋ค์ ๊ฑฐ๊พธ๋ก ์ํํ๋ฉฐ ํฐ์น ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ์ขํ์ ํด๋นํ๋ ๋ทฐ๋ฅผ ์ฐพ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ํด๋น ๋ทฐ์ ๋ํด์ ๋ค์ **hitTest** ๋ฉ์๋๋ฅผ ์ฌ๊ท์ ์ผ๋ก ํธ์ถํ์ฌ ํ์ ๋ทฐ๋ค์ ์ํํ๊ณ , ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ ๋ทฐ๋ฅผ ์ฐพ์์ ๋ฐํํฉ๋๋ค.
์์ ์ฝ๋๋ฅผ ์์ฝํ๋ฉด, ํฐ์น ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ์ขํ๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ์์ ์ ์ ์กฐ๊ฑด์ ๋ฐ๋ผ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ ๋ทฐ๋ฅผ ์ฐพ๊ณ , ํด๋น ๋ทฐ๋ฅผ ๋ฐํํ๋ ๋ฉ์๋์
๋๋ค. ์ด๋ฅผ ํตํด iOS ์ฑ์ ์ ์ ํ ๋ทฐ์ ๋ํด ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ณ , ์ฌ์ฉ์์์ ์ํธ์์ฉ์ ํจ๊ณผ์ ์ผ๋ก ์ ์ดํ ์ ์์ต๋๋ค.

1. touches์์ย ์ฒซ ๋ฒ์งธ ํฐ์นย ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
2. ์ขํย ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
3. hitTest๋ฅผ ํตํด์ ํด๋น ์ขํ๋ฅผ ํฌํจํ๊ณ ์๋ย ๋ทฐ๋ฅผ ํ์ํฉ๋๋ค.
4. ๋ง์ฝ ํด๋น ๋ทฐ๊ฐ ์ขํ๋ฅผ ๊ฐ๊ณ ์์ง ์๋ค๋ฉด nil์ ๋ฐํํฉ๋๋ค.

์์ ๊ฐ์ด ์ฌ์ฉ์๊ฐย View_B1(green)์ ํฐ์นํ๋ฉด ์์๋ทฐ ๊ณ์ธต๋ถํฐ ํ์ํ๋ฉด์ ๋ด๋ ค์ค๋๋ฐย View_C(grey)๋ ํฐ์น๋ point๋ฅผ ๊ฐ๊ณ ์์ง ์๊ธฐ ๋๋ฌธ์ false ๋ฐํํ๊ณ ย View B๋ก ๋์ด์ hitTest๋ฅผ ์ํํ๊ฒ ๋๋ฉด์ย View_B1(green)๋ฅผ ๋ฐํํฉ๋๋ค.
### ๐ ์คํ View

### ๐ ์คํ 1

์ฌ์ฉ์๊ฐ ํฐ์นํ ์ขํ๋ฅผ ํฌํจํ๋ ์ต์์ ๋ทฐ์ `backgroundColor`๊ฐ ์ถ๋ ฅ๋๋๋ก ์ฝ๋๋ฅผ ์์ ํ์ต๋๋ค.
### ๐ ์คํ 2

GestureRecognizer ์์ฑํ๊ณ , ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋ ํด๋น `greenView`์ ๋ฐฐ๊ฒฝ์์ black๊ณผ ๊ต์ฐจ๋ก ๋ณ๊ฒฝํด์ฃผ๋ `tapView()`๊ฐ ํธ์ถ๋๋๋ก ์ค์ ํ์์ต๋๋ค.
**โ๏ธTip**
- ๋ทฐ ๊ฐ์ฑ์ ๋ฐฐ๊ฒฝ ์์์ด `System Color`์ผ ๊ฒฝ์ฐ์๋ ์์ ๋ณ๊ฒฝ์ด ๋ถ๊ฐ๋ฅํฉ๋๋ค. ๋ฐ๋ผ์ ํด๋น ์์ `green`์ผ๋ก ์์ ํด์ฃผ์์ต๋๋ค.
### ์คํํ๋ฉด

# 13์ฃผ์ฐจ_1: Collection View
## ๋ฐํ์ Yetti
### Collection View?


- ์ ๋ ฌ๋ ๋ฐ์ดํฐ ํญ๋ชฉ ๋ชจ์์ ๊ทธ๋ฆฌ๋๋ ๋ค๋ฅธ ์ปค์คํ
๋ ์ด์์์ผ๋ก ํ์ํ๋ ์ ์ฐํ๊ณ ๊ฐ๋ ฅํ UI์์
- ํ
์ด๋ธ ๋ทฐ์ฒ๋ผ data source๋ก ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๊ณ delegate๋ก ์ ์ ์์ ์ํธ์์ฉ์ ๊ด๋ฆฌ


### Collection View ๊ตฌ์ฑ์์ 4๊ฐ์ง
1. ์
(Cell)
2. ๋ณด์ถฉ ๋ทฐ(Supplementary View)
3. ๋ฐ์ฝ๋ ์ด์
๋ทฐ(Decoration View)
4. ๋ ์ด์์ ๊ฐ์ฒด(Layout Object)

- Line๊ฐ์ ๊ฒฝ์ฐ๋ ๋ฉ์ ์ ๋ด์์ ํ
์ด๋ธ๋ทฐ๋ฅผ ์ฌ์ฉํ๋ค๊ฐ ์ฝ๋ ์
๋ทฐ๋ก ๋ฆฌํฉํ ๋งํ์๋ค.



### CollectionView ๊ตฌ์ฑ๋จ๊ณ
1. FlowLayout๊ฐ์ฒด ์์ฑ ํ CollectionView์ ํ ๋น
2. ์
์ ๋๋น์ ๋์ด๋ฅผ ์ค์
3. ํ์์ item, lines์ ๊ฐ๊ฒฌ์ ์ค์
4. header, footer์ฌ์ฉ์ ์ฌ์ด์ฆ ๋ช
์
5. ๋ ์ด์์์ ์คํฌ๋กค ๋ฐฉํฅ ์ค์ . ๊ธฐ๋ณธ๊ฐ์ ์ธ๋ก



### UICollectionViewDataSource
- ์ฌ๋ฌ๊ฐ์ง ๋ฉ์๋๋ฅผ ํตํด UICollectionView์ ๋ฐ์ดํฐ์ ๋ทฐ๋ฅผ ๊ด๋ฆฌ
- ๋ฐ์ดํฐ ๋ณ๊ฒฝ ์์๋ reloadData()๋ฅผ ํธ์ถํ์ฌ ์ ์ฒด ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ก๋ํด์ผํจ
- ์ธ๋ถ์ ์ธ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด์๋ ์ถ๊ฐ์ ์ธ ๋ก์ง ๋ฐ ์ ๋๋ฉ์ด์
์ฝ๋๊ฐ ํ์
- ๋ฐ๋ก ์ ๋๋ฉ์ด์
์ฝ๋๊ฐ ์๋ค๋ฉด ๋๋ ๋๊ฒจ์ ๋ณด์ด๋ ๋จ์ -> ์ฌ์ฉ์ ๊ฒฝํ(UX) ๊ฐ์
- ๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋ค๋ฃจ๊ธฐ์๋ ์ ์ง๋ณด์๊ฐ ์ด๋ ค์ธ ์ ์์
### UICollectionViewDiffableDataSource
- UICollectionViewDiffableDataSourceํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ฉฐ, ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ๋ ์ฝ๊ฒ ์ฒ๋ฆฌํ ์ ์์
- CollectionView์ DataSource ๊ฐ์ ๋ฐ์ดํฐ ์๋์ผ๋ก ๋๊ธฐํ๋์ด ํธ๋ฆฌํจ
- ๋ฐ์ดํฐ ์ค๋
์ท(snapshot)์ ์ฌ์ฉํ์ฌ ์น์
๊ณผ ์์ดํ
์ ๊ตฌ์ฑ์ ๊ด๋ฆฌ (์ค๋
์ท: ํ์ฌ UICollectionView์ ํ์ ์ํ๋ฅผ ๋ปํจ)
- ๋ฐ์ดํฐ ๋ณ๊ฒฝ ์ฌํญ์ ์ฒ๋ฆฌํ๋ ๋ฐ ์์ด์ ๋ณ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด๋ ๋น ๋ฅธ ์ฑ๋ฅ ๋ฐ ์ ์ ํ ์ ๋๋ฉ์ด์
์ ์๋์ผ๋ก ์ ๊ณต -> ์ฌ์ฉ์ ๊ฒฝํ(UX) ํฅ์
- ๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ๊ฒฝ์ฐ์๋ ๋ ํจ์จ์ ์ผ๋ก ๋ฐ์ดํฐ ๊ด๋ฆฌ ๋ฐ ์
๋ฐ์ดํธ๋ฅผ ํ ์ ์์



- ์ ํ๋ธ๋ฎค์ง, ์ธ์คํ๊ทธ๋จ, ์ฑ์คํ ์ด ๋ฑ๋ฑ ๋ง์ ๊ณณ์์ ์ฌ์ฉ๋๊ณ ์๋ค.

# 13์ฃผ์ฐจ_2: URL Session
## ๋ฐํ์ yyss99























# 14์ฃผ์ฐจ: Cache ๋ฏผํธ ํ์ดํ
! ํคํค ๊ฐ์๊ฐ์์~
## ๋ฐํ์ MINT


๋ฐ์ดํฐ๋ค์ ์ด๋์ ์ ์ฅ๋๊ณ ์ด๋ป๊ฒ ์ฌ์ฉ๋ ๊น์? ๋จผ์ ํ๋ ๋์คํฌ์ ์ฐ๋ฆฌ์ ๋ฐ์ดํฐ๋ค์ด ์ ๋ถ ์ ์ฅ๋ฉ๋๋ค. ๋๋ฌธ์ ํ๋ ๋์คํฌ๋ ์ฉ๋์ด ๋งค์ฐ ํฌ๊ณ , ๊ธฐ์ต์ ํ๋ ๊ฒ์ ์น์ค๋์ด ์์ต๋๋ค.
RAM, ๋ฉ๋ชจ๋ฆฌ๋ ๊ทธ๋ฌํ ํ๋ ๋์คํฌ์์ ์ฑ์ ์คํํ๋๋ฐ์ ํ์ํ ์ค์ ๋ฐ์ดํฐ๋ค์ ๋ค๊ณ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ฌ์ ํ ๋ด์ฉ์ ๋ฐฉ๋ํ๊ณ , RAM์ ๋์คํฌ๋ณด๋จ ๊ธฐ์ตํ ์ ์๋ ์์ด ์ ๊ณ , CPU์ ๊ฐ๊น์์ ๋น ๋ฅด์ง๋ง ์ด์ฐ ๋์๊ฑด ๊ธฐ์ต์ ํ๋ ๊ฒ์๋ง ๋ฅ๋ ฅ์ด ์น์ค๋์ด ์์ต๋๋ค.
CPU๋ RAM์์ ๋ฐ์ ๋ฐ์ดํฐ๋ค์ ์ด์ฉํด ์ฑ์ ์คํํฉ๋๋ค. ๊ธฐ์ต๋ณด๋ค๋ ์ฑ๋ฅ์ด ์ค์ํด, ๊พธ์คํ๊ฒ ์ฑ๋ฅ์ด ์
๊ทธ๋ ์ด๋ ๋์ด ์์ต๋๋ค. ๋๋ฌธ์ RAM๊ณผ CPU๋ ๊ธฐ์ต๊ณผ ์ฑ๋ฅ์ผ๋ก ๊ฐ์์ ๋ชฉ์ ์ด ๋ค๋ฅด๊ธฐ์ ์๋ก์๋ก ์ฑ๋ฅ์ฐจ์ด๊ฐ ์ ์ ์ฌํ๊ฒ ๋๊ฒ ๋ฉ๋๋ค. RAM์ด CPU๋ฅผ ์ซ์๊ฐ์ง ๋ชปํด CPU๊ฐ RAM์ด ๋ฐ์ดํฐ๋ฅผ ์ฐพ์์ ์ฃผ๊ธฐ๋ฅผ ๊ธฐ๋ค๋ ค์ผ ํ๋ ๋ณ๋ชฉ ํ์์ด ๋ฐ์ํ ๊ฒ์
๋๋ค.

์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์์ถฉ๊ธฐ์ ์ญํ ์ ํ๋ ์บ์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ฑ์ฅํ์ต๋๋ค. ์บ์ ๋ฉ๋ชจ๋ฆฌ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ์๋๋ฅผ ๋์ด๊ณ ๋น์ฉ์ ๋ฎ์ถ๊ธฐ ์ํด ์ฌ์ฉํฉ๋๋ค. CPU์ ์ผ๋ถ๋ผ๊ณ ๋ ๋ณผ ์ ์๊ฒ ์์ ์นฉ์ผ๋ก CPU์ ์ง์ ๋ถ์ด ์๊ฑฐ๋ ์ฐ๊ฒฐ๋์ด ์์ต๋๋ค.

์บ์ ๋ฉ๋ชจ๋ฆฌ๋ ์ ์ฅํ ๋ ๊ธฐ๋ณธ์ ์ธ ๊ท์น์ด ์์ต๋๋ค. ์ ์ด์ ์บ์๋ผ๋ ๊ฐ๋
์์ฒด๊ฐ ๋ง๋ค์ด์ง ์ด์ ๊ฐ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋น ๋ฅด๊ฒ ๊ฐ์ ธ์ค๊ธฐ ์ํด์์๊ธฐ์ ์์ผ๋ก ์ฐ์ผ ๋ฐ์ดํฐ๋ค์ ๋ฏธ๋ฆฌ ์์ํ์ฌ ์บ์์ ๋ฃ์ด๋์ ๋์ ๊ท์น์ผ๋ก ์ปดํจํฐ์ ์ฑ๋ฅ์ ํฅ์์ํค๋ ์ญํ ์ ํฉ๋๋ค.
์๊ฐ ์ง์ญ์ฑ
๊ณต๊ฐ ์ง์ญ์ฑ

์ง๊ธ๊น์ง์ ์บ์ ๋ฉ๋ชจ๋ฆฌ๋ ํ๋์จ์ด์ ์ผ๋ก ์กด์ฌํ๋ ์บ์ ๋ฉ๋ชจ๋ฆฌ์ ๋ํ ๋ด์ฉ์ด์์ต๋๋ค. ๊ทธ๋ฌ๋ ์บ์๋ `์์ฃผ ์ฌ์ฉํ๋ ๋ฐ์ดํฐ๋ ๊ฐ์ ๋ฏธ๋ฆฌ ๋ณต์ฌํด๋๋ ์์ ๊ณต๊ฐ`์ ๋ปํ๋ ๋ง๋ก ์ํํธ์จ์ด์ ์ธ ๋ถ๋ถ์์๋ ์ฌ์ฉ๋ ์ ์์ต๋๋ค. ๋๋ฌธ์ CPU, ๋ฉ๋ชจ๋ฆฌ, ๋์คํฌ ์ธ ๊ณณ ์ด๋๋ ์์ ๊ณต๊ฐ์ ๋ง๋ค์ด ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ฉด ์บ์๋ฅผ ์ ์ฅํ ์ ์๋ ๊ฒ์
๋๋ค.

์ง๊ธ๊น์ง ์บ์๋ผ๋ ๊ฐ๋
์ ๋ํด์ ์์๋ณด์์ต๋๋ค. ๋ค์์ ์ ํฌ๊ฐ ์ฌ์ฉํ๋ iOS์์ ์บ์ฑํ ์ ์๊ฒ ํด์ฃผ๋ ๋๊ฐ์ง์ ํด๋์ค์ด์ ์บ์ฑ ๋ฐฉ์์ธ NS Cache์ URL Cache์
๋๋ค.

๋จผ์ NS Cache์
๋๋ค. key-value ํ์
์ผ๋ก ๋ฉ๋ชจ๋ฆฌ์๋ง ์ ์ฅ๋๋ ๋ฉ๋ชจ๋ฆฌ ์บ์ฑ ๋ฐฉ์์
๋๋ค. ๋ฉ๋ชจ๋ฆฌ ์บ์ฑ์ด๊ธฐ์ ์ฑ์ ์ข
๋ฃํ๋ฉด ๋ฐ์ดํฐ๊ฐ ๋ ์๊ฐ๋๋ค.

๋ด๋ถ์ ๋งํฌ๋ ๋ฆฌ์คํธ์ฒ๋ผ ์ฐ๊ฒฐ๋์ด ์์ผ๋ฉฐ ์ฌ๋ฌ๊ฐ์ง ์ค์ ์ ํ ์ ์๋๋ฐ ์ด์ค ๋์ฌ๊ฒจ๋ณผ ๊ฒ์ผ๋ก totalCostLimit๊ฐ ์์ต๋๋ค. ์บ์๋ ์์ ๊ณต๊ฐ์ด๊ธฐ์ ๋งค๋ฒ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค๊ณ ์์ ์ ์์ต๋๋ค. ๋๋ฌธ์ ์ฉ๋์ด ๊ฐ๋ ์ฐจ๋ฉด ๊ธฐ์กด์ ์๋ ๊ฒ๋ค์ ์ญ์ ํด์ ์๋ฆฌ๋ฅผ ๋ง๋ค์ด์ค์ผ ํฉ๋๋ค.

๊ธฐ๋ณธ์ ์ผ๋ก NSCache๋ ์ฉ๋์ด ๊ฐ๋ ์ฐผ์ ๋ ์๋์ผ๋ก ๊ธฐ์กด ๋ฐ์ดํฐ๋ค์ ์ญ์ ํด์ฃผ๋๋ฐ ์ด๋ ์ด๋ ํ ๊ฒ๋ถํฐ ์ญ์ ํ ๊ฒ์ธ์ง๋ฅผ ํ๋ณํ๋ ๊ธฐ์ค์ ์ฃผ๋ ๊ฒ์ด ๋ฐ๋ก cost์
๋๋ค. ๊ฐ์ฅ ์์ cost๊ฐ ์ ์ผ ๋ถํ์ํ ๋ฐ์ดํฐ๋ก ํ๋จํด ์ญ์ ํฉ๋๋ค. ๋๋ฌธ์ totalCostLimit๋ฅผ ์ค์ ํด์ฃผ์๋ค๋ฉด ๋งค๋ฒ ์บ์ํ ๋ cost๊ฐ๋ ๊ฐ์ด ๋๊ฒจ์ค์ผํฉ๋๋ค.

๊ทธ๋ฌ๋ ์ด๊ฒ์ ์ค์ ํด์ฃผ์ง ์๊ณ ๋ง๋ค์์๊ธฐ์ setObject์์ cost๊ฐ์ ๋๊ฒจ์ค ํ์๊ฐ ์๊ณ ์ด ๊ฒฝ์ฐ ๋ง์ผ ์ฉ๋์ด ๊ฐ๋ ์ฐจ๋ฉด NSCache๋ ์บ์ฑ๋ Object์ ์์๋๋ก ์๋ ์ญ์ ํฉ๋๋ค.

๋ค์์ URL Cache์
๋๋ค. URL Cache๋ NS Cache๋ณด๋ค ์กฐ๊ธ ๋ ๋์ค์ ๋ง๋ค์ด์ก์ด์. ๋ ๊ฐ์ง ํฐ ์ฐจ์ด์ ์ด ์๋๋ฐ NS Cache๋ key - value๋ก data๋ฅผ ๋๊ฒจ์ฃผ์๋ค๋ฉด URL Cache๋ Request๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ์ฌ ํ์ธํ๋ค๋ ์ ์
๋๋ค. Request์ ์๋ง๋ response๊ฐ ์ ์ฅ๋์ด ์๋ค๋ฉด ๊ทธ๊ฒ์ ๋ฐํํฉ๋๋ค. ๋ํ ๋ฉ๋ชจ๋ฆฌ ์บ์ฑ ๋ฟ๋ง ์๋๋ผ ๋์คํฌ ์บ์ฑ๋ ๊ฐ๋ฅํด ์ฑ์ ์ข
๋ฃํด๋ ์บ์ ๋ฐ์ดํฐ๊ฐ ๋ ์๊ฐ์ง ์๊ณ ์ค์ ์ ๋ฐ๋ผ ์ฑ์ ์ญ์ ํด๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด์กดํ ์๋ ์๋ค.

shared๊ฐ ๊ธฐ๋ณธ๊ตฌํ๋์ด ์๊ธฐ์ ์ด๋ฅผ ์ฌ์ฉํด์ ์ ์ธํด์ฃผ์์ต๋๋ค. ์ด๋ ์บ์ฑ๋ ๋ฐ์ดํฐ๋ค์ ๋ฉ๋ชจ๋ฆฌ์ ๋์คํฌ ์ค ์ด๋์ ์ ์ฅ๋ ๊น์? ๊ณต์ ๋ฌธ์์ ๋์์๋ ๊ธฐ๋ณธ ์ ์ฑ
๊ฐ์ .allowed๋ก ๋ ์์น ๋ชจ๋ ์ฌ์ฉํ ์ ์๋ค๊ณ ๋์ด ์์ต๋๋ค.

๊ทธ๋ฌ๋ ๋ด๋ถ ๊ตฌํ์ ๋ณด๋ฉด ๋ ์์น ๋ชจ๋ 4 MB, 20 MB๋ก ์ฉ๋์ด ํ ๋น๋์ด ์์ต๋๋ค. ๋ค๋ง response๊ฐ ์บ์ ์ฉ๋์ 5% ์ด์์ธ ๊ฒฝ์ฐ๋ ์ ์ฅ๋์ง ์๊ณ ๋งค๋ฒ ์๋ก์ด request๋ฅผ ๋ณด๋
๋๋ค. ๋๋ฌธ์ ์ฐ๋ฆฌ์ ์ด๋ฏธ์ง๋ ์ฉ๋์ด 1.5MB ์ ๋ ๋๊ธฐ ๋๋ฌธ์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์๋ ๋์คํฌ์ ์ ์ฅ๋ ๊ฒ์
๋๋ค. ๊ธฐ๋ณธ์ ์ธ ๋์คํฌ ์บ์ ๋๋ ํ ๋ฆฌ์ ํ์ ๋๋ ํ ๋ฆฌ๋ฅผ ๋ฃ์ด์ฃผ๊ณ ์ถ๋ค๋ฉด nil ๋์ diskPath๋ฅผ ๋ง๋ค์ด ๋ฃ์ด์ฃผ๋ฉด ๋ฉ๋๋ค.

request๋ฅผ ๋ฃ์ด ์ ์ฅ๋ response๊ฐ ์๋ค๋ฉด ํด๋น response๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ต๋๋ค.

๊ทธ๋ฌ๋ ๋ง์ผ ์๋ค๋ฉด ์๋กญ๊ฒ ์๋ฒ์์ ๋ฐ์์ค๋ฉฐ storeCachedResponse๋ฅผ ์ด์ฉํด request์ response๋ฅผ ์ ์ฅํฉ๋๋ค.


URL Request์๋ ์บ์ ์ ์ฑ
์ ์ค์ ํ ์ ์์ต๋๋ค. ๊ธฐ๋ณธ์ ์ธ ์ค์ ์ ์ด ๊ทธ๋ฆผ๊ณผ ๊ฐ์ต๋๋ค.
Q. ๊ทธ๋ฐ๋ฐ ์์ ์คํ์์ ์ ์ํ ์ด๋ฏธ์ง URL์ URLRequest์ ๋ด์์ ์์ฒญ์ ํ ๊ฒฝ์ฐ, ์ด๋ฏธ์ง๊ฐ ์๋์ผ๋ก ์บ์ฑ์ด ๋์ง ์๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค. ๊ทธ ์ด์ ๋ ๋ฌด์์ผ๊น์? -> ํด๊ฒฐ ๋ชป ํจ.

์ง๊ธ๊น์ง ์บ์์ ๋ํด์ ์์๋ณด์์ต๋๋ค. ์ฑ๋ฅ ํฅ์์ ์ํด ์์ฃผ ์ฌ์ฉํ๊ฑฐ๋ ์ต๊ทผ ์ฌ์ฉํ๊ฑฐ๋ ์ฌ์ฉํ ๊ฒ ๊ฐ์ ๋ฐ์ดํฐ๋ค์ ์์ ๊ณต๊ฐ์ ์ ์ฅํ๋ ๋ด์ฉ์ด์์ต๋๋ค. ์ด์ฒ๋ผ ๋ชฉ์ ์ ์กฐ๊ธ ๋ค๋ฅด์ง๋ง ์ผ๋ถ์ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ ๋ฐฉ๋ฒ์๋ ๋ค๋ฅธ ๊ฒ๋ค๋ ์์ต๋๋ค.

๋จผ์ ์ฟ ํค์
๋๋ค. HTTP๋ Stateless๋ผ๊ณ ํฉ๋๋ค. ์ด๋ ํด๋ผ์ด์ธํธ๊ฐ ๋๊ตฌ์ธ์ง, ์ด๋ค ์ํ์ธ์ง์ ๋ํ ์ ๋ณด๋ฅผ ํฌํจํ์ง ์๋๋ค๋ ๋ป์
๋๋ค. ๋๋ฌธ์ ํด๋ผ์ด์ธํธ๊ฐ ๋๊ตฌ์ธ์ง๋ฅผ ์๋ฒ๊ฐ ์ ์ ์๊ฒ ์ฟ ํค๊ฐ ๋ฑ์ฅํ์ต๋๋ค. ํ๋ฒ์ด๋ผ๋ ํด๋น ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ ์ ๊ทผํ๋ค๋ฉด ์๋ฒ๋ response์ ์ฟ ํค๋ฅผ ๋ค๋ ค์ ๋ณด๋
๋๋ค. ํด๋ผ์ด์ธํธ๋ ํด๋น ์ฟ ํค๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉด์ ๊ธฐ๋ณธ์ ์ธ ์ ๋ณด, ์ค์ ๋ฑ์ด ์ฟ ํค์ ์ ์ฅ๋ฉ๋๋ค. ์ถํ์ ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญ์ ํ ๋ ์๋์ผ๋ก ์ฟ ํค๋ ํจ๊ป ์์ฒญ์ ํตํด ์๋ฒ๋ก ๋์ด๊ฐ๋๋ค. ์๋ฒ๋ ํด๋น ์ฟ ํค๋ฅผ ๋ฐ๊ณ ๊ธฐ์กด์ ์ ์ํ๋ ์ฌ์ฉ์์๊ณผ ๋์์ ์ด๋ ํ ์ค์ ์ ํด๋์๋์ง ์๊ฒ ๋ฉ๋๋ค.

๊ทธ๋ฌ๋ ์ฟ ํค๋ ๋ธ๋ผ์ฐ์ ์์๋ง ์ฌ์ฉ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์๋ ๋ค์ดํฐ๋ธ ์ฑ์์๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.

์ฐธ๊ณ ๋ก ์ ํฌ๊ฐ ์ฌ์ฉํ๋ URL Session์์๋ ์ฟ ํค ์ ์ฑ
์ ๊ฐ์ง๊ณ ์์ต๋๋ค.

์ด๋ฌํ ์ฟ ํค์ ๋จ์ ์ ์ด๋ ์ ๋ ํด๊ฒฐํ๋ ์น๊ตฌ๋ค์ด ๋ฑ์ฅํ์ต๋๋ค. ํด๋ผ์ด์ธํธ๋ง ์๊ณ ์๊ฒ ํ๋ Web Storage์ ์๋ฒ๋ง ์๊ณ ์๊ฒ ํ๋ Session์
๋๋ค. Web Storage๋ ํด๋ผ์ด์ธํธ์ ์ ์ฅ๋ง ํ ๋ฟ ๋งค๋ฒ ๋ค์ ์๋ฒ์ ์ ์กํ๋ ์ฟ ํค์ ๋ค๋ฅด๊ฒ ์๋ฒ์๋ ์ ์กํ์ง ์์ต๋๋ค. ์ ์ฅ ์ฉ๋๋ 5MB๋ก ๋ ํฝ๋๋ค. ๋ค๋ง ๋
๋ฆฝ๋ storage๋ก ๊ฐ๊ฐ ๊ณต์ ํ ์๊ฐ ์๊ณ ๋ง๋ฃ ๊ธฐ๊ฐ์ ์ค์ ํด ์ค ์๋ ์์ต๋๋ค.

์์ ์ฟ ํค๋ ํด๋ผ์ด์ธํธ๊ฐ ์์ ํ ์ ์๋ ์ ๋ณด์
๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ ํ ๊ฒฐ์ ๋ฅผ ํ์๊ณ ์ ๊ฐ์ ์ค์ํ ์ ๋ณด๋ค์ ์๋ฒ๋ง ์๊ณ ์์ด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค. ์ด๋ฐ ๊ฒฝ์ฐ๋ฅผ ์ํด ์๋ฒ์์๋ง ์๊ณ ์๋ Session์ ์ฌ์ฉํฉ๋๋ค. Session์ ์๋ฒ์ Session DataBase๋ผ๋ ์ ์ฅ๊ณต๊ฐ์ ๋ง๋ญ๋๋ค. ์ด๊ณณ์๋ ์ฌ์ฉ์๋ค์ ๋ํ ์ค์ํ ์ ๋ณด๋ค์ด ์์ต๋๋ค. ํด๋ผ์ด์ธํธ๊ฐ ์์ด๋์ ๋น๋ฒ์ ์ณ์ ๋ก๊ทธ์ธํ๋ฉด Session DataBase์ ์ฌ์ฉ์์ ๋ํ ์ ๋ณด๊ฐ ์์ฑ๋ฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ์ธ์
id๋ฅผ ์ฟ ํค์ ๋ค๋ ค์ ํด๋ผ์ด์ธํธ์๊ฒ ๋ณด๋
๋๋ค. ํด๋ผ์ด์ธํธ๋ ์ค์ง ์ด ์ธ์
id๋ง ์๊ณ ์๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ ๋ค์ ์๋ฒ์ ์์ฒญ์ ํ ๋ ํด๋ผ์ด์ธํธ๋ ์ฟ ํค์ ์ธ์
id๋ฅผ ๊ฐ์ด ๋ณด๋ด๊ฒ ๋๊ณ ์ด ์ธ์
id๋ฅผ ๋ณด๊ณ ์๋ฒ๋ ์ธ์
DB์์ ํด๋ผ์ด์ธํธ์ ์ ๋ณด๋ฅผ ์ฐพ์ ์ฌ์ฉํฉ๋๋ค.

๊ทธ๋ฌ๋ Session์ DB๊ฐ ํ์ํ ๋งํผ ์์์ด ๋ง์ ์ ์ ์ ๋ณด๋ฅผ ๋ง๋ค์ด์ค ์๋ ์์ต๋๋ค. ๋น์ฉ์ด ๋งค์ฐ ๋ง์ด ๋ค๊ฒ ๋๊ธฐ ๋๋ฌธ์
๋๋ค.

์ด๋ฌํ ์ ๋ค์ ๋ณด์ํ ๊ฒ์ด ํ ํฐ์
๋๋ค. ํ ํฐ์ ์๋ฒ๊ฐ DB๋ฅผ ๊ฐ์ง๊ณ ์์ง ์์ต๋๋ค. ์ฟ ํค์ฒ๋ผ ํด๋ผ์ด์ธํธ๊ฐ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์๋๋ฐ ํน์ํ ์ฒ๋ฆฌ๊ฐ ๋์ด ์๋ ๊ฒ์
๋๋ค. ํด๋ผ์ด์ธํธ๊ฐ ๋ก๊ทธ์ธํด์ ์๊ฒจ๋ ์ ์ data๋ฅผ ์๋ฒ๋ ์ฌ์ธ์ ํด์ ํด๋ผ์ด์ธํธ์ ๋ณด๋
๋๋ค. ์ํธํํ๋ ๊ฒ์ ์๋๋๋ค. ์ค๊ฐ์ ๊ฐ๋ก์ฑ๊ฑฐ๋ ํ์ ๋ ๋ฐ์ดํฐ๋ฅผ ์ ๋ถ ํ์ธํ ์ ์์ต๋๋ค. ๋ค๋ง ๋ณ๊ฒฝํ ์ ์์ต๋๋ค. ๋ด์ฉ์ ๋ณ๊ฒฝํ๊ฒ ๋๋ฉด ์ฌ์ธ์ ์ ํจ์ฑ์ ๊ฒ์ฌํ์ ๋ ํต๊ณผํ ์ ์์ต๋๋ค. ๋๋ฌธ์ ์๋ฒ๋ ๋จ์ํ ํ ํฐ์ ์ ํจ์ฑ์ ๊ฒ์ฌ๋ง ํ ๋ฟ ์ ์ฅํ๊ณ ์์ง๋ ์๊ฒ ๋์ ๋น์ฉ์ด ์ ๊ฒ ๋ญ๋๋ค.


์ง๊ธ๊น์ง ์บ์, ์ฟ ํค, ์ธ์
, ํ ํฐ ๋ฑ ๋ค์ํ ์ ์ฅ ๋ฐ ์ธ์ฆ๋ฐฉ์์ ๋ํด ์์๋ณด์์ต๋๋ค. ๊ฐ์ฌํฉ๋๋ค~
### ์ฐธ๊ณ ์๋ฃ
[๐ 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

---

---

- ๊ฐ์ฒด ๊ด๋ฆฌ ๊ทธ๋ํ๋: ๊ฐ์ฒด๊ฐ์ ๊ด๊ณ๋ฅผ ๊ทธ๋ํ๋ก ๋ํ๋ธ ๊ฒ
---

- DB์ธ์ด๋ฅผ ๋ฐฐ์ธ ํ์๊ฐ ์๋ค๋ ์ ์์ ๋งค์ฐ ํธ๋ฆฌ
---

---

- ์ค๋ ๋ ์ธ์ดํ๋ฅผ ๊ณ ๋ คํ์ง ์์๋ค๋๊ฑธ ์ฃผ์
- mainQueue๋ ์๋ฌด๋๋ UI๋๋ฌธ์ธ๊ฒ์ผ๋ก ์ถ์ธก(๋ฐ์ดํฐ ์ฒ๋ฆฌ๋๋ฌธ์ UI๋จนํต ๋ ์๋..)
---

---

- ๋ง์ด๊ทธ๋ ์ด์
์ ํ์ง ์์ ๊ฒฝ์ฐ ๋ฐ์ดํฐ๊ฐ ๋ ์๊ฐ๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ ํ ์ ์๋ค.
---

---

---
# 17์ฃผ์ฐจ:
## ๋ฐํ์: Jusbug
# UIView Animations



ํ๋ฉด ์์์ ์์ง์๊ณผ ๋ณํ๋ฅผ ๋ถ๋๋ฝ๊ฒ ์ฒ๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋๋ ๊ฐ๋ ฅํ ๋๊ตฌ์
๋๋ค. UIView ์ ๋๋ฉ์ด์
์ ์๊ฐ์ ํจ๊ณผ๋ฅผ ์์ฑํ๊ฑฐ๋ ์ฌ์ฉ์ ์ธํฐํ์ด์ค ์์์ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. UIView ์ ๋๋ฉ์ด์
์ ๊ธฐ๋ณธ์ ์ผ๋ก UIView ํด๋์ค์ ์ผ๋ถ ๋ฉ์๋์ ํด๋ก์ ๋ฅผ ์ฌ์ฉํ์ฌ ์์ฑ๋ฉ๋๋ค.



๋ค๋จ๊ณ ์ ๋๋ฉ์ด์
์ ๋ง๋ค๊ณ ์คํํ๋ ๋ฐ ์ฌ์ฉ๋๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ๋ฉ๋์
๋๋ค. ์ด๋ฅผ ์ฌ์ฉํ๋ฉด ์ฌ๋ฌ ๋จ๊ณ๋ก ๊ตฌ์ฑ๋ ์ ๋๋ฉ์ด์
์ ๊ตฌํํ๊ณ ๊ฐ ๋จ๊ณ์์ ๋ค์ํ ์ ๋๋ฉ์ด์
ํจ๊ณผ๋ฅผ ์ ์ฉํ ์ ์์ต๋๋ค.
- `animateKeyframes()` ๋ฉ์๋๋ ๋ทฐ์ ํคํ๋ ์ ๊ธฐ๋ฐ ์ ๋๋ฉ์ด์
์ ์์ฑํ๋๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
- ์ฌ๋ฌ ๊ฐ์ ํคํ๋ ์์ ์ ์ํ๊ณ , ๊ฐ ํคํ๋ ์์์ ๋ทฐ์ ์์ฑ์ ๋ณ๊ฒฝํ๋ฉฐ, ๊ฐ ํคํ๋ ์ ์ฌ์ด์ ์ ํ์ ์ ์ดํฉ๋๋ค.
- iOS 7 ์ด์์์ ์ฌ์ฉ ๊ฐ๋ฅํ๋ฉฐ, ๋ ๋ณต์กํ ์ ๋๋ฉ์ด์
์๋๋ฆฌ์ค์ ์ ์ฉํฉ๋๋ค.

์ฃผ์ ๋ชฉ์ ์ ์ฌ๋ฌ ๋จ๊ณ๋ก ๊ตฌ์ฑ๋ ๋ณต์กํ ์ ๋๋ฉ์ด์
์ ๋ง๋๋ ๊ฒ์
๋๋ค. ๊ฐ ๋จ๊ณ์์ addKeyframe๋ฅผ ์ฌ์ฉํ์ฌ ์๊ฐ์ ๋ฐ๋ฅธ ์์ฑ์ ๋ณํ๋ฅผ ์ ์ํ๊ณ , ์ ์ฒด ์ง์ ์๊ฐ ๋ด์์ ๋ค์ํ ์ ๋๋ฉ์ด์
ํจ๊ณผ๋ฅผ ์ ์ฉํ ์ ์์ต๋๋ค.

Core Graphics ํ๋ ์์ํฌ์์ ์ ๊ณตํ๋ ๋ณํ ๋งคํธ๋ฆญ์ค๋ฅผ ๋ํ๋ด๋ ๊ตฌ์กฐ์ฒด์
๋๋ค. ์ด ๋งคํธ๋ฆญ์ค๋ 2D ๊ณต๊ฐ์์ ๊ฐ์ฒด์ ๋ณํ(์ด๋, ํ์ , ํฌ๊ธฐ, ๊ธฐ์ธ์ ๋ฑ)์ ํํํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.

translation(์์น), scaling(ํฌ๊ธฐ), rotation(ํ์ )์ ์์๋ฅผ ๋ณํํ์ฌ ์ ๋๋ฉ์ด์
์ ๋ค๋ฃฐ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ Concatenation(ํฉ๋ณ ์ ๋๋ฉ์ด์
)์ ์ด์ฉํ์ฌ ์ฌ๋ฌ ์ ๋๋ฉ์ด์
๋ณํ์ ํ๋๋ก ๊ฒฐํฉํ์ฌ ๋์์ ์คํํ๊ฒ ๋ง๋ค ์๋ ์์ด ๋ ๋ณต์กํ ์ ๋๋ฉ์ด์
ํจ๊ณผ๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค.

์ ๋๋ฉ์ด์
์์ `frame` ์์ฑ์ ๋ณ๊ฒฝํ๋ ๊ฒ์ ์ค์ ํด๋น ๋ทฐ์ ํฌ๊ธฐ์ ์์น ๋ฑ์ ์ง์ ๋ณ๊ฒฝํ๋ ๊ฒ์
๋๋ค. ๊ทธ๋ ๊ฒ ๋ ๊ฒฝ์ฐ, ๋ค๋ฅธ ํ์ ๋ทฐ์ ์ปจํ
์ธ ์ ์ํฅ์ ๋ฏธ์น ์ ์๋ ๋ฆฌ์คํฌ๊ฐ ์กด์ฌํฉ๋๋ค. ๋ฐ๋ฉด, `transform`์ ๋ณํํ๋ ๊ฒ์ ์ค์ ์์น์ ํฌ๊ธฐ๊ฐ ๋ณ๊ฒฝ๋๋ ๊ฒ์ด ์๋๋ผ ๋ ๋๋ง์๋ง ์ํฅ์ ์ฃผ๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ๋ทฐ์์ ๊ฐ์ญ์ด ์ผ์ด๋ ์ฐ๋ ค๊ฐ ์์ด ์ฌ๋ฌ ์ ๋๋ฉ์ด์
์ ๊ตฌํํ ๋์๋ `transform`์ ๋ณํํ๋ ๊ฒ์ด ๋ ์์ ํฉ๋๋ค.

`identity`๋ ์ ๋๋ฉ์ด์
๋์ ์ด์ ๋ทฐ์ ์ ๋ณด๋ฅผ ๊ฐ๊ณ ์๋ ์์ฑ์ผ๋ก ์ด๋ฅผ ์ด์ฉํ์ฌ ์ ๋๋ฉ์ด์
๋์ ํ ์ด๊ธฐ ๋ทฐ๋ก ๋๋๋ฆด ๋ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋จ ์์น, ํฌ๊ธฐ, ํ์ ์ ๋ํ ์ ๋ณด๋ง ๊ฐ๊ณ ์๊ธฐ ๋๋ฌธ์ alpha, image์ ๊ฐ์ ์ ๋๋ฉ์ด์
์์๋ค์ `removeAnimation()`๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์ ๋๋ฉ์ด์
์ ์ง์ ์ ์ง์ํค๋ ๋ฐฉ๋ฒ์ผ๋ก ์ ์ดํ ์ ์์ต๋๋ค.

1. Completion Handler
2. ์ฌ๋ฌ ๋จ๊ณ์ ์ ๋๋ฉ์ด์
์ ๊ตฌํํ๊ธฐ์ ์ฉ์ดํ๋ค.
3. ์์น, ํฌ๊ธฐ, ํ์
4. frame, transform
5. alpha, image...

### ๐จโ๐ฌ ์คํ ๋ชฉํ
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

Better apps. Less Code.๋ผ๋ ํ์ด๊ฐ SwiftUI๋ฅผ ์ ์๋ ค์ฃผ๋ ๊ฒ ๊ฐ์ต๋๋ค.


์ ์ธํ UI์ธ๊ฒ๊ณผ ๋ฉํฐ ํ๋ซํผ์ธ ์ ์ด ๋์ ๋๋๋ค

์ผ๋ฐ์ ์ผ๋ก ๊ฐ์ UI์ ๊ตฌํํ์ ๋ UIKit๋ณด๋ค ํ์ฐํ ์ ์ ์ฝ๋๋ฅผ ๋ณด์ฌ์ค๋๋ค.

์ฌ๋ฌ์ฐจ์ด์ ์ด ์๊ฒ ์ง๋ง 4๊ฐ์ง์ ๋๋ก ๊ตฌ๋ถํด ๋ณด์์ต๋๋ค.


๋ช
๋ นํ์ UI๋ฅผ ๊ตฌํํ๊ธฐ ์ํ ํ์๋ฅผ ํ๋ํ๋ ์๋ ค์ค์ผํ๋ ๊ฒ ๊ฐ์ต๋๋ค.

์ต์ข
์ ์ผ๋ก ๋ฌด์์ ํ ์ง ์๋ ค์ฃผ๋ฉด ๊ทธ ๋ชฉํ์ ๋๋ฌํ๊ธฐ๊น์ง์ ๋์์ ์์์ ํด์ฃผ๋ ๋๋?์ด๋๊น











