# ์ผ๊ธฐ์ฅ ๐
2023-08-28 ~ 2023-09-15
## Ground Rules
### ๊ท์น
- ์/๋ชฉ
- ์ค์ ์ ๊ฐ์ ํ์ตํ๋ ์์ต
- ํ์ตํ๋ 14:00 ~ 17:00
- ์ ๋
์์ฌ 17:00 ~ 18:00
- ์คํฌ๋ผ 18:00
- ํํ๋ก์ ํธ 18:05 ~ 20:00
- ํ/์/๊ธ
- ์คํฌ๋ผ ์ดํ ํ๋ก์ ํธ ์งํ
- ์คํฌ๋ผ 10:00
- ํํ๋ก์ ํธ 10:05 ~ 12:00
- ์ ์ฌ์์ฌ 12:00 ~ 13:00
- ํํ๋ก์ ํธ 13:00 ~ 18:00
- ์ฃผ๋ง์ ํน๋ณํ ์ผ ์์ผ๋ฉด ์์ ์๊ฐ
### ์ผ์ ์ ํน์ด์ฌํญ
- Max
- 9/1(๊ธ) ์ค์ 11์๋ถํฐ ๊ฐ๋ฅ! (๋ฌธ์ ์ถ์ ๊ด๋ จ์ผ๋ก ํ์๊ฐ๋ง ๋น์ฐ๊ฒ ์ต๋๋คใ
)
- 9/8(๊ธ) ๊ธ์์ผ ์คํ์ ํผ์น๋ชปํ ์ฌ์ ์ด ์์ต๋๋คใ
ใ
์์นจ ๋ฆฌ๋๋ฏธ ์ ๋ฆฌ๋ ๊ฐ๋ฅํ ๊ฑฐ๊ฐ์ ์ฉ
- 9/14(๋ชฉ) ์ ๋
์ ์ฝ์์ด ์์ต๋๋ค!(ํ์ตํ๋ ๋๋๊ณ ๋ค์ ๋ชจ์ด๊ธฐ ์ด๋ ค์ธ ๊ฒ ๊ฐ์์ใ
)
- Hemg
- 9.4์ผ ์์์ผ ์ ๋
์๋ ์ฝ์์ด ์์ต๋๋ค.
### ์คํฌ๋ผ(5๋ถ ๋ด์ธ)
- ์/๋ชฉ 18:00
- ํ/์/๊ธ 10:00
### ํ๋ก์ ํธ ๊ท์น
- ๋ค์ด๋ฐ ์ค์ํ๊ธฐ(๊ฐ์ด๋ ๋ผ์ธ)
- ๋ธ๋์น - ์คํ
๋ณ๋ก
- ์ปค๋ฐ ๋ฉ์์ง - ์นด๋ฅด๋ง์คํ์ผ
- ๋ฆฌ๋๋ฏธ์์ฑ๊ณผ PR์ ๋ฆฌ๋ฅผ ์ํด ์ฝ๋์ ๋ํ ๊ธฐ๋ก ๊ทธ๋ ๊ทธ๋ ํ๊ธฐ
- ์ค๊ฐ์ค๊ฐ ๋ฆฌ๋๋ฏธ ์์ฑ
## ์ผ์ผ ์คํฌ๋ผ
### ๐ 08/28(์)
- ์ค๋์ ์ปจ๋์
- Max๐ค: ์ฌ๊ณ ์๋๋ ๋จธ๋ฆฌ๊ฐ ๋ฉํ๋ค์... ๊ทธ์น๋ง ํ๋ด๋ณด๊ฒ ์ต๋๋นใ
ใ
ใ
- Hemg๐ป: ์์ฃผ ์ข์ต๋๋ค. ๋น๊ฐ ์์ ํน์๊ณ ์ผ์ด๋ฌ๋ค์
- ํน์ด์ฌํญ
- Max๐ค: ์์ต๋๋ค!
- Hemg๐ป: ์ค๋์ ๋ฑํ ์์ต๋๋ค!
- ์ค๋ ํ ์ผ
- [x] ํ๋ํ์ต ์์ต
- [x] STEP 1 ํ์
### ๐ 08/29(ํ)
- ์ค๋์ ์ปจ๋์
- Max๐ค: ์ปจ๋์
๋์์ง ์์ต๋๋ค! ๊ทผ๋ฐ ๋น๊ฐ ์์ ์ข ๋์ด์ง๋ค์ใ
- Hemg๐ป: ํ๋ฒํ๊ฑฐ ๊ฐ์ต๋๋ค ใ
- ํน์ด์ฌํญ
- Max๐ค: ์์ต๋๋น
- Hemg๐ป: ๋ฐฉํ ์ดํ ์ฃผ๋ผ์ ๊ทธ๋ฐ๊ฐ ์ ๋ง ์ง์ค์ด ์์๋๊ธดํ๋ค์ใ
- ์ค๋ ํ ์ผ
- [x] STEP1 ์์
- [x] STEP1์ ํ์ํ ๋ฌธ์ ํ์ธ
### ๐ 08/30(์)
- ์ค๋์ ์ปจ๋์
- Max๐ค: ์ปจ๋์
๊ด์ฐฎ์ต๋๋ค
- Hemg๐ป: ์ด์ ์ปคํผ๋ฅผ ๋ฆ๊ฒ ๋ง์
์ ๊ทธ๋ฐ๊ฐ ์ ์ ์ข ๋ชป์ค์ต๋๋ค ใ
- ํน์ด์ฌํญ
- Max๐ค: ์์ต๋๋ค!
- Hemg๐ป: ๋ฐ๋ก ์์ต๋๋ค!
- ์ค๋ ํ ์ผ
- [x] STEP1 PR ์งํ
- [ ] (๋ฆฌ๋ทฐ ์ค๋ฉด) STEP1 ์์ ์งํ
- [ ] CoreData ๊ณต๋ถ
### ๐ 08/31(๋ชฉ)
- ์ค๋์ ์ปจ๋์
- Max๐ค: ์ปจ๋์
๊ด์ฐฎ์ต๋๋น!
- Hemg๐ป: ํน์๊ณ ์ผ์ด๋์ ์ข์ต๋๋ค.
- ํน์ด์ฌํญ
- Max๐ค: ์์ต๋๋ค
- Hemg๐ป: ์์ต๋๋ค
- ์ค๋ ํ ์ผ
- [x] ํ๋ํ์ต ์์ต
- [ ] CoreData ๊ณต๋ถ
### ๐ 09/01(๊ธ)
- ์ค๋์ ์ปจ๋์
- Max๐ค: ์ปจ๋์
์ ์ข์ต๋๋ค ๋ ์จ๋ ์ข๋ค์ฉ
- Hemg๐ป: ํค๋กฑํค๋กฑ ํฉ๋๋ค
- ํน์ด์ฌํญ
- Max๐ค: ์์ต๋๋ค~
- Hemg๐ป: ์์ต๋๋ค. coreData Manual/None // class definition
- ์ค๋ ํ ์ผ
- [x] STEP 1 ์ถ๊ฐ ์์ ์ฌํญ ์งํ
- [x] STEP 2 : CoreData ์ฐ๊ฒฐ
### ๐ 09/02(์)
- ์ค๋์ ์ปจ๋์
- Max๐ค: ์ปจ๋์
์ข์ต๋๋น
- Hemg๐ป: ์ข์ต๋๋ค!
- ํน์ด์ฌํญ
- Max๐ค: ์์ต๋๋ค
- Hemg๐ป: ์์ต๋๋ค.
- ์ค๋ ํ ์ผ
- [ ] STEP 2 ์งํ
- [ ] .
### ๐ 09/03(ํ)
- ์ค๋์ ์ปจ๋์
- Max๐ค: ๊ด์ฐฎ์ต๋๋น
- Hemg๐ป: ์ข์ต๋๋ค.
- ํน์ด์ฌํญ
- Max๐ค: ์์ต๋๋ค!
- Hemg๐ป: ์์ต๋๋ค.
- ์ค๋ ํ ์ผ
- [x] STEP 2 ์งํ
### ๐ 09/04(์)
- ์ค๋์ ์ปจ๋์
- Max๐ค: ์ ๋ฉ๋๋น
- Hemg๐ป: ๋๋ฌด ์ข์ต๋๋ค.
- ํน์ด์ฌํญ
- Max๐ค: ๋ชจ๊ฐ์ฝ
- Hemg๐ป: ํฉ์ ์ผ๋ก ๊ฐ์. ~~์ง๊ฐ..~~
- ์ค๋ ํ ์ผ
- [x] STEP 2 PR ์งํ
- [x] README ์์ฑ
---
# PR
## STEP 1
> ์ผ๊ธฐ์ฅ [STEP 1] Hamg, maxhyunm
์๋
ํ์ธ์ ๊ทธ๋ฆฐ!(@GREENOVER)
์ด๋ฒ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ๊ฒ ๋ Max, Hemg ์
๋๋ค.
3์ฃผ๊ฐ ์ ๋ถํ๋๋ฆฝ๋๋ค. ๊ฐ์ฌํฉ๋๋ค!๐โโ๏ธ
์ผ๊ธฐ์ฅ ์ฒซ PR ์งํํ์ต๋๋ค.
์๊ฐ ๋์ค๋ ํ์ธ ๋ถํ๋๋ฆฝ๋๋ค!
## ๊ณ ๋ฏผํ๋ ์
### ๐น ํค๋ณด๋ ๋์ด์ ๋ฐ๋ฅธ ํ
์คํธ๋ทฐ ํฌ๊ธฐ ๋ณ๊ฒฝ
ํค๋ณด๋๊ฐ ํ๋ฉด์ ๋ํ๋๊ณ ์ฌ๋ผ์ง์ ๋ฐ๋ผ ํ
์คํธ๋ทฐ ์์ญ์ ํฌ๊ธฐ๊ฐ ํจ๊ป ๋ฐ๋๋๋ก ์์
ํด์ผ ํ๋๋ฐ, ์๋ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ ์ค์์ ๊ณ ๋ฏผ์ด ์์์ต๋๋ค.
- `NotificationCenter`๋ฅผ ํ์ฉํ์ฌ ํค๋ณด๋๊ฐ ํ์ฑํ๋ ๋๋ง๋ค ์๋ฆผ์ ๋ฐ์ `TextView`์ `contentInset`์ ๋ณ๊ฒฝํ๋ ๋ฐฉ๋ฒ
- `TextView`์ `bottomAnchor`๋ฅผ `keyboardLayoutGuide`์ `topAnchor`์ ๋ง์ถ๋ ๋ฐฉ๋ฒ
- ์์)
```swift
textView.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor)
```
๋ ๋ฒ์งธ ๋ฐฉ๋ฒ์ ์ฝ๋ ํ ์ค๋ง ์ถ๊ฐ๋๋ฉด ๋๊ธฐ ๋๋ฌธ์ ํจ์ฌ ์ฌํํ์ง๋ง `iOS 15` ๋ฒ์ ์ด์์์๋ง ํ์ฉ์ด ๊ฐ๋ฅํ์ฌ, ํด๋น ๋ถ๋ถ์ ์ผ๋จ ์ ์๋ก ์งํํ์์ต๋๋ค.
### ๐น ํ
์ด๋ธ ๋ทฐ ์
contentView
ํ
์ด๋ธ๋ทฐ ์
์์ `label` ๋ด์ฉ๋ค์ `cell` ์์ฒด์ `addSubview`๋ก ์ถ๊ฐํ ์ง, ์๋๋ฉด `contentView`์ ์ถ๊ฐํ ์ง ๊ณ ๋ฏผํ์์ต๋๋ค.
`cell` ์์ฒด์ `addSubview`๋ฅผ ํ๋ฉด `safeAreaLayoutGuide`๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ ์ด์์์ ์ก์ ์ ์๊ณ , `contentView`์ ์ถ๊ฐํ๋ฉด `contentView`๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ ์ด์์์ ์ก๊ฒ ๋ฉ๋๋ค. ์ ํฌ๋ `safeArea`๋ฅผ ํ์ฉํ๊ณ ์ถ์ด์ ์ฒ์์๋ `cell`์ ์ง์ ์ถ๊ฐํ๋ ๋ฐฉ์์ ๊ณ ๋ คํ์ง๋ง `contentView`์ ๊ณต์๋ฌธ์๋ฅผ ํ์ธํด ๋ณด๋ ์๋์ ๊ฐ์ด ์ ๋ฆฌ๋์ด ์์์ต๋๋ค.
> ์
์ ์ฌ์ฉ์ ์ง์ ์ปจํ
์ธ ๋ฅผ ์ถ๊ฐํ ์ ์๋ ๋ฉ์ธ ๋ทฐ.
> ์
์ ๊ตฌ์ฑํ ๋, ์
์ ๋ค์ด๊ฐ์ผ ํ๋ ์ปจํ
์ธ ๋ฅผ ์ด ๋ทฐ์ ์ถ๊ฐํ๋ฉด ์
์ ์๋์ผ๋ก ๋ฐฐ๊ฒฝ์ ๋ค์ด๊ฐ๋ ๋ค๋ฅธ ๋ทฐ๋ค๋ณด๋ค ์์ชฝ์ ์ด ์ปจํ
์ธ ๋ค์ ๋ณด์ฌ์ค๋๋ค.
`label`๋ค์ ๊ฐ์ฅ ์์ชฝ์ ๋ณด์ฌ์ ธ์ผ ํ๋ ๋ด์ฉ์ด๋ฏ๋ก, `contentView`์ ์ถ๊ฐํ๋ ์ชฝ์ผ๋ก ์์ ํ์ฌ ์งํํ์์ต๋๋ค.
```swift
contentView.addSubview(titleLabel)
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor)
```
### ๐น ๋ฐ๋ณต์ ์ธ ๋ ์ง ํฌ๋งคํ
์ฒ๋ฆฌ
์๊ตฌ์ฌํญ์ ํ์ธํ์ ๋, ์ผ๊ธฐ ๋ฆฌ์คํธ ํ๋ฉด๊ณผ ์๋ก์ด ์ผ๊ธฐ๋ฅผ ์์ฑํ๋ ํ๋ฉด์์ ๋ชจ๋ ์๋์ ๊ฐ์ด ๋ ์ง ํฌ๋งคํ
์ ์ฌ์ฉํด์ผ ํ๋ ๊ฒ์ ์ ์ ์์์ต๋๋ค.
```swift
private let formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ko_kr")
formatter.dateFormat = "yyyy๋
MM์dd์ผ"
return formatter
}()
```
๋์ผํ ์ฝ๋๊ฐ ๋ ๊ฐ์ `ViewController`์์ ๋ฐ๋ณต๋๋ ๊ฒ์ ๋ง๊ธฐ ์ํด ํด๋น ์ฝ๋๋ฅผ ๋ถ๋ฆฌํ๊ณ ์ ํ์ต๋๋ค.
์ด๋ ๋จ์ํ ๋๊ฐ์ ํํ์ ํฌ๋งคํฐ๋ฅผ ์ฑ๊ธํค ๋ฐฉ์์ผ๋ก ๋ง๋ค์ง, ์๋๋ฉด ๋ค์ํ ํฌ๋งท์ผ๋ก ํ์ฉํ ์ ์๋๋ก `extension`์ผ๋ก ๋ฉ์๋๋ฅผ ๋ง๋ค์ง ๊ณ ๋ฏผํ์์ง๋ง ํ์ฅ์ฑ์ ๊ณ ๋ คํ์ฌ ํ์๋ฅผ ์ ํํ์์ต๋๋ค.
```swift
extension DateFormatter {
func formatToString(from date: Date, with format: String) -> String {
self.dateFormat = format
return self.string(from: date)
}
}
DateFormatter().formatToString(from: entity.createdAt, with: "YYYY๋
MM์ dd์ผ")
```
## ์กฐ์ธ์ด ํ์ํ ์
### ํค๋ณด๋ ํ์ฉ์ ์๋ฎฌ๋ ์ดํฐ ์๋ฌ ๋ฐ์
- command + k ๋ก ์งํํ์ฌ ํค๋ณด๋๋ฅผ ๋ถ๋ฌ ์์๋ ์๋์ ๊ฐ์ ๋ฌธ๊ตฌ๊ฐ ๋ํ๋๊ณ ์์ต๋๋ค. ์๋ฎฌ๋ ์ดํฐ ์๋ฌ๋ก ํ์ธ๋๋๋ฐ, ํน์ ์ด๋ถ๋ถ์ ๋ํด์๋ ๋ฐ๋ก ๋ ํด๊ฒฐ ํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์์๊น์?
```
Diary[88951:12721534] [HardwareKeyboard] -[UIApplication getKeyboardDevicePropertiesForSenderID:shouldUpdate:usingSyntheticEvent:], failed to fetch device property for senderID (778835616971358209) use primary keyboard info instead.
```
---
# PR
## STEP 2
> ์ผ๊ธฐ์ฅ [STEP 2] Hamg, maxhyunm
์๋
ํ์ธ์ ๊ทธ๋ฆฐ!(@GREENOVER)
์ผ๊ธฐ์ฅ STEP 2 PR ์ฌ๋ ค๋๋ฆฝ๋๋ค.
์ฝ์ด ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ก ๋ง์ด ํค๋งธ๋ ๊ฒ ๊ฐ์ต๋๋ค๐
์ด๋ฒ ์คํ
๋ ๋ฆฌ๋ทฐ ์ ๋ถํ๋๋ฆฝ๋๋ค!๐โ
## ๊ณ ๋ฏผํ๋ ์
### ๐น Core Data ์ฒ๋ฆฌ์ฉ ๋ก์ง ์์น
์ฒ์์๋ `SceneDelegate` ๋๋ `AppDelegate`์ `PersistentContainer`์ `saveContext` ๋ฉ์๋๋ฅผ ๋๋ ค๊ณ ํ์ต๋๋ค. ํ์ง๋ง ์ด ๋ ๊ฐ์ง ๋ด์ฉ์ ํฌํจํ์ฌ, CRUD ์์
์ ๋ฐ๊ณผ ๊ด๋ จ๋ ๋ด์ฉ์ ๋ณ๊ฐ์ ํ์
์ผ๋ก ๋ถ๋ฆฌํ์ฌ `CoreDataManager`๋ฅผ ๋ง๋ค๋ฉด ์ข ๋ ๊น๋ํ ๊ด๋ฆฌํ ์ ์์ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์์ต๋๋ค. ์ด์ ๋ฐ๋ผ ์๋์ ๊ฐ์ด ์๋ก์ด ํ์
์ ์์ฑํ์์ต๋๋ค.
Manager ํ์
์ ํ๋์ ์ธ์คํด์ค๋ฅผ ์ฑ ์ ์ฒด๊ฐ ๊ณตํต์ ์ผ๋ก ์ฌ์ฉํ๋ฏ๋ก ์ฑ๊ธํค ์ฒ๋ฆฌ๋ฅผ ํ์์ต๋๋ค.
```swift
class CoreDataManager {
static let shared = CoreDataManager()
private init() {}
func fetchDiary() throws -> [Diary] {
...
}
func createDiary() -> Diary {
...
}
func deleteDiary(_ diary: Diary?) {
...
}
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
...
}()
func saveContext () {
...
}
}
```
### ๐น ์ผ๊ธฐ ์์ฑ/์์ ์ฒ๋ฆฌ ๋ฐฉ์
์ผ๊ธฐ ์ ๊ท ์์ฑ๊ณผ ๊ธฐ์กด ์ผ๊ธฐ ์์ ์์
์ ๋ชจ๋ `CreateDiaryViewController`๋ฅผ ํตํด ๊ตฌํ๋ฉ๋๋ค.
์ด๋ ์ ๊ท/์์ ์ฌ๋ถ๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ํด, ์์ ์์
์ธ ๊ฒฝ์ฐ์๋ `init`์ ํตํด ์์ ํ ์ผ๊ธฐ ์ธ์คํด์ค๋ฅผ ์ ๋ฌํด์ค ์ ์๋๋ก ์๋ก์ด ์์ฑ์๋ฅผ ์ถ๊ฐํ์์ต๋๋ค. ๋ํ ์ ๊ท ์์
์ธ ๊ฒฝ์ฐ์๋ `init`์์ uuid, ๋ ์ง๋ง ์
๋ ฅ๋ ์๋ก์ด `diary` ๊ฐ์ฒด๋ฅผ ์์ฑํ๋๋ก ํ์์ต๋๋ค.
```swift
init() {
self.diary = CoreDataManager.shared.createDiary()
self.isNew = true
super.init(nibName: nil, bundle: nil)
}
init(_ diary: Diary) {
self.diary = diary
self.isNew = false
super.init(nibName: nil, bundle: nil)
}
```
### ๐น ์ผ๊ธฐ ์๋์ ์ฅ ๊ตฌํ ๋ฐฉ์
์๊ตฌ์ฌํญ์ ๋ฐ๋ฅธ ์๋์ ์ฅ ์๊ธฐ๋ ๋ค์๊ณผ ๊ฐ์์ต๋๋ค.
- ์ฌ์ฉ์๊ฐ ์
๋ ฅ์ ๋ฉ์ถ๋ ๊ฒฝ์ฐ(ํค๋ณด๋๊ฐ ์ฌ๋ผ์ง๋ ๊ฒฝ์ฐ)
- ์ฑ์ด ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ์ง์
ํ๋ ๊ฒฝ์ฐ
- ์ด์ ํ๋ฉด(๋ฆฌ์คํธ ํ๋ฉด)์ผ๋ก ์ด๋ํ๋ ๊ฒฝ์ฐ
์ด์ ๋ฐ๋ผ `UITextViewDelegate`์ `textViewDidEndEditing` ๋ฉ์๋๋ฅผ ํ์ฉํ์ฌ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํํ์์ต๋๋ค.
```swift
func textViewDidEndEditing(_ textView: UITextView) {
let contents = textView.text.split(separator: "\n")
guard !contents.isEmpty else { return }
CoreDataManager.shared.saveContext()
}
```
์์ ์์
์ ํตํด ํค๋ณด๋ ๋นํ์ฑํ์ ํ๋ฉด ๋ค๋ก๊ฐ๊ธฐ ์์
์์๋ ์๋์ผ๋ก ๋ด์ฉ์ title / body ๋ก ๋ถ๋ฆฌํ์ฌ ์ ์ฅํ ์ ์์์ต๋๋ค.
ํ์ง๋ง ์ฑ์ด ๋ฐฑ๊ทธ๋ผ์ด๋์ ์ง์
ํ๋ ๊ฒฝ์ฐ๋ `SceneDelegate`์ `sceneDidEnterBackground` ๋ฉ์๋๋ฅผ ํตํด ์์
ํ๋ ๊ฒ์ผ๋ก ์๊ณ ์์ด, `CreateDiaryViewController` ๋ด๋ถ์ textView ๋ด์ฉ์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ ์ฐพ๊ธฐ๊ฐ ์ด๋ ค์ ์ต๋๋ค. ์ฑ๊ธํค์ผ๋ก ๋ง๋ค์ด์ง `CoreDataManager`์ `saveContext`๋ฉ์๋๋ฅผ ํตํด ํด๋น ์ผ๊ธฐ ๊ฐ์ฒด๋ฅผ ์ฝ์ด๋ฐ์ดํฐ์ ์ ์ฅํ ์๋ ์์์ง๋ง, ํ์ฌ ์์ฑ๋ ์ผ๊ธฐ ๋ด์ฉ์ title๊ณผ body๋ก ๋๋์ด ์
๋ฐ์ดํธํ๋ ์์
์ด ๋ถ๊ฐ๋ฅํ์ต๋๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ฒ์์๋ `CreateViewController`์ `viewWillDisappear` ๋ฉ์๋์์ ์ ์ฅ์ฒ๋ฆฌ๋ฅผ ์งํํ ์ ์๋๋ก ๊ตฌํํด ๋ณด์์ต๋๋ค.
```swift
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
saveDiary()
}
```
ํ์ง๋ง ์ด๋ ๊ฒ ํ๋ ์ผ๊ธฐ ์ญ์ ์ฒ๋ฆฌ๋ฅผ ํ ๋ค ๋ทฐ์ปจํธ๋กค๋ฌ๋ฅผ popํ ๋์๋ ์ ์ฅ์ฒ๋ฆฌ๋ฅผ ๊ฑฐ์น๊ฒ ๋์ด, ์ค๋ฅ๋ฅผ ํผํ๋ ค๋ค ๋ณด๋ ์คํ๋ ค ๋ก์ง์ด ๋ณต์กํด์ก์ต๋๋ค.
๋๋ฌธ์ ์ ํฌ๋ `TextView`๊ฐ ์์ ๋ ๋๋ง๋ค ๋ทฐ์ปจํธ๋กค๋ฌ๊ฐ ๊ฐ์ง๊ณ ์๋ ์ผ๊ธฐ ๊ฐ์ฒด์ ๋ด์ฉ์ ๋ฐ๊ฟ์ฃผ๊ณ , ์ ์ฅ์ด ํ์ํ ์๊ฐ์ `saveContext` ์ฒ๋ฆฌ๋ง ์งํํ ์ ์๋๋ก ์๋์ ๊ฐ์ด ๊ตฌํํ์์ต๋๋ค.
```swift
func textViewDidChange(_ textView: UITextView) {
let contents = textView.text.split(separator: "\n")
guard !contents.isEmpty,
let title = contents.first else { return }
let body = contents.dropFirst().joined(separator: "\n")
diary.title = "\(title)"
diary.body = body
}
```
### ๐น alert๊ณผ swipe ์ฝ๋ ๋ถ๋ฆฌ
๋ฐ๋ณต์ ์ธ alertController ์์ฑ ์์
, ๊ทธ๋ฆฌ๊ณ swipe ์ฝ๋๋ฅผ ๋ทฐ์ปจํธ๋กค๋ฌ์์ ์กฐ๊ธ์ด๋ผ๋ ๋ถ๋ฆฌํ๋ฉด ์ข์ ๊ฒ ๊ฐ์ protocol๊ณผ extension์ ํ์ฉํ์์ต๋๋ค.
- `Alert` ๋ถ๋ฆฌ์ ์ฝ๋
```swift
private func showDeleteConfirmation() {
let alertController = UIAlertController(title: "์ง์ง์?", message: "์ ๋ง๋ก ์ญ์ ํ์๊ฒ ์ต๋๊น?", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "์ทจ์", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
let deleteAction = UIAlertAction(title: "์ญ์ ", style: .destructive) { [weak self] _ in
guard let self else { return }
self.performDelete()
}
alertController.addAction(deleteAction)
present(alertController, animated: true, completion: nil)
}
```
- ๋ถ๋ฆฌ ํ ์ฝ๋
```swift
protocol AlertDisplayable {
func showAlert(title: String?, message: String?, actions: [UIAlertAction])
}
extension AlertDisplayable where Self: UIViewController {
func showAlert(title: String?, message: String?, actions: [UIAlertAction]) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
actions.forEach { alertController.addAction($0) }
present(alertController, animated: true, completion: nil)
}
}
```
- ๊ตฌํ๋ถ ์ฝ๋
```swift
private func showDeleteConfirmation() {
let cancelAction = UIAlertAction(title: "์ทจ์", style: .cancel, handler: nil)
let deleteAction = UIAlertAction(title: "์ญ์ ", style: .destructive) { [weak self] _ in
self?.performDelete()
}
showAlert(title: "์ง์ง์?", message: "์ ๋ง๋ก ์ญ์ ํ์๊ฒ ์ต๋๊น?", actions: [cancelAction, deleteAction])
}
```
### ๐น ์ผ๊ธฐ ๋ฆฌ์คํธ ์๋ก๊ณ ์นจ ์์
์ฒ์์๋ `DiaryListViewController`์ `CreateDiaryViewController`๋ฅผ `delegate`๋ก ์ฐ๊ฒฐํ์ฌ ์์ฑํ๋ฉด์ด pop๋ ๋ ๋ฆฌ์คํธ ํ๋ฉด์ `readCoreData` ๋ฉ์๋๋ฅผ ํธ์ถํ๋๋ก ๊ตฌํํ์์ต๋๋ค. ํ์ง๋ง ์ด๋ณด๋ค๋ ๋ทฐ์ ์๋ช
์ฃผ๊ธฐ๋ฅผ ํ์ฉํ๋ ํธ์ด ๋ ๊น๋ํ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์์ต๋๋ค. ์ด์ ๋ฐ๋ผ `viewWillAppear` ๋ฉ์๋์์ ์๋์ ๊ฐ์ด ์ฒ๋ฆฌํด์ฃผ์์ต๋๋ค.
```swift
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
readCoreData()
}
```
## ์กฐ์ธ์ ์ป๊ณ ์ถ์ ์
### ๋น ํ๋ฉด์์ ๋ค๋ก๊ฐ๊ธฐ๋ฅผ ๋๋ ์ ๋์ ์ฒ๋ฆฌ๋ฐฉ๋ฒ
์๊ตฌ์ฌํญ์ ๋ฐ๋ฅด๋ฉด, ์ผ๊ธฐ์ฅ์ + ๋ฒํผ์ ๋๋ฌ ์ ์ผ๊ธฐ์ฅ์ ์์ฑํ์ ๊ฒฝ์ฐ ํค๋ณด๋๊ฐ ์๋์ผ๋ก ์ฌ๋ผ์ค๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ค๋ก๊ฐ๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํ๊ฑฐ๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ํ๋ก ๋ณํ๋๋ ๋ฑ ํ์ฌ ํ๋ฉด์์ ๋ฒ์ด๋๋ฉด ์์
์ค์ธ ๋ด์ฉ์ด ์๋์ผ๋ก ์ ์ฅ์ด ๋์ด์ผ ํฉ๋๋ค. ๊ทธ๋ ๋ค๋ฉด + ๋ฒํผ์ ๋๋ ๋ค๊ฐ ์๋ฌด๋ฐ ๋ด์ฉ์ ์์ฑํ์ง ์์ ์ฑ ๋ค๋ก๊ฐ๊ธฐ๋ฅผ ๋๋ฌ๋ ์ ๋ชฉ/๋ด์ฉ์ด ์๋ ๋น ์ผ๊ธฐ๊ฐ ์์ฑ๋๋ ๊ฒ์ด ๋ง์๊น์? ์ ํฌ๋ ์ด ๋ถ๋ถ์ด ์กฐ๊ธ ์ด์ํ ๊ฒ ๊ฐ์์ ์์ ๋น์ด์๋ ๋ด์ฉ์ context์์ save์ฒ๋ฆฌ๋ฅผ ํ์ง ์๋๋ก ๋ง๋ค์ด๋์์ต๋๋ค. ๊ทธ๋ฆฐ์ ์๊ฒฌ์ด ๊ถ๊ธํฉ๋๋ค๐ญ
---
### STEP2 ํ ์ผ
1. Foundation์ด ๊ผญ ํ์ํ๊ฐ? ์๋๋ผ๋ฉด ์์ผ๊น?
- CoreData์์ ์์ฒด์ ์ผ๋ก Foundation์ importํ๊ณ ์๊ธฐ ๋๋ฌธ์ ์ง์๋ ์๊ด์์ต๋๋ค! ์ด ๋ถ๋ถ ์ญ์ ์ฒ๋ฆฌํ์ต๋๋ค.
2. NSManagedObject๊ฐ ์ด๋ค ์ญํ ์ ํ๋๊ฐ?
- NSManagedObject๋ CoreData ๋ชจ๋ธ์ ์ฌ์ฉํ๊ฒ ๋ ๊ฒฝ์ฐ ์์์ ์งํํด์ผํ๋ ๊ธฐ๋ณธ ํด๋์ค์
๋๋ค. ์ํฐํฐ๋ช
, ์ดํธ๋ฆฌ๋ทฐํธ๋ช
, ์ดํธ๋ฆฌ๋ทฐํธ ๊ฐ์ ๊ด๊ณ ๋ฑ์ ํฌํจํ (NSObject ๊ฐ์ฒด๋ค์)๋ฉํ์ ๋ณด๋ฅผ ๊ฐ๊ณ ์์ต๋๋ค.
- [NSManagedObject](https://developer.apple.com/documentation/coredata/nsmanagedobject)
3. ๊ฐํ ์ด์
- Diary์ ์ฝ๋๋ฅผ ์ถ๊ฐ๋ณด๋ ค๋ค๊ฐ ์ญ์ ํ๋ฉด์ ์ค์๋ก ๋จ๊ธด ๊ฒ ๊ฐ์ต๋๋ค. ๊ฐํ ์ญ์ ํ์ต๋๋ค!
-
4. Diary: Identifiable ์ ํ์ํ ๋ด์ฉ์ธ๊ฐ? ์์ผ๋ฉด ์ด๋ค ๋ฌธ์ ๊ฐ ์๊ธฐ๋?
- Identifiable์ ํด๋น ํ๋กํ ์ฝ์ ์ฑํํ ํ์
์์ Hashable ๊ฐ์ผ๋ก id ํ๋กํผํฐ๋ฅผ ๋ง๋ค๋๋ก ๊ฐ์ ํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ชจ๋ ๊ฐ์ฒด๋ฅผ id๋ก ๊ตฌ๋ถํ ์ ์๋๋ก ํ๊ธฐ ์ํจ์
๋๋ค. ๋ง์ฝ id๋ก ๊ตฌ๋ถํ ์ ์๋ ๊ฐ์ด ์์ ๊ฒฝ์ฐ ๋ฐ์ดํฐ๋ฅผ ํน์ ํ ์ ์์ด ๋ฌธ์ ๊ฐ ์๊ธฐ๋ ๊ฒ์ผ๋ก ์๊ณ ์์ต๋๋ค. ์ ํฌ์ Diary๋ uuid๋ก id ํ๋กํผํฐ๋ฅผ ๊ฐ๊ณ ์๊ธฐ์, ๊ธฐ๋ฅ์ ๋ฌธ์ ๋ ๋ฐ์ํ์ง ์๋ ๊ฒ ๊ฐ์ต๋๋ค. ํ์ง๋ง Identifiable์ ์ฑ์ง์ ๋ช
์์ ์ผ๋ก ๋ณด์ฌ์ฃผ๋ ๊ฒ์ด ์ข์ ๊ฒ ๊ฐ์์ ์ญ์ ํ์ง ์๊ณ ๊ทธ๋๋ก ๋์์ต๋๋ค!
5. ShareDiary ํ๋กํ ์ฝ ๋ค์ด๋ฐ: ShareDisplayable (Alert ํ์ผ๋ช
๋ ์์ )
- ๋ ๊ฐ์ง ๋ค์ด๋ฐ์ ๋น์ทํ๊ฒ ๋ง์ถ๋ ํธ์ด ์ข์์ ๊ฒ ๊ฐ๋ค์...!! ์์ ์งํํ์์ต๋๋ค!
6. shareText ๊ฐ๋
์ฑ ๋์ด๋ ๋ฐฉ๋ฒ
- ๊ทธ๋ฆฐ๊ป์ ๋ง์ํ์ ์์ ์ ์ด ์ด๋๊น์ ๋ํ์ฌ ๋ง์ ๊ณ ๋ฏผ์ ํ์์ต๋๋ค ๐ข ์ฒ์์๋ shareํ ํญ๋ชฉ์ ๋ฐ๋ก ์ ๋ฆฌํด๋์ด์ผ ํ๋? ๋ผ๋ ์๊ฐ๋ ํ๋๋ฐ, ๊ณ ๋ฏผ ๋์ '๊ฐ๋
์ฑ'์ ์ด์ ์ ๋ง์ถ๋ ๊ฒ์ผ๋ก ๊ฒฐ๋ก ์ ๋ด๋ ธ์ต๋๋ค.(ํน์ ์๋ํ์ ๋ฐ๊ฐ ์ด๊ฒ ์๋์๋ผ๋ฉด ์ถ๊ฐ๋ก ๋ง์ ๋ถํ๋๋ ค์!!) ์ด์ ๋ฐ๋ผ ๊ธฐ์กด์๋ ํ ์ค๋ก ์์ฑ๋์๋ ๋ถ๋ถ์ ์ฌ๋ฌ ์ค String ํ์์ผ๋ก ๋ณ๊ฒฝํ์ฌ ์์ฑํ์์ต๋๋ค.
```swift
let shareText = "์ ๋ชฉ: \(title)\n์์ฑ์ผ์: \(date)\n๋ด์ฉ: \(body)"
```
```swift
let shareText = """
์ ๋ชฉ: \(title)
์์ฑ์ผ์: \(date)
๋ด์ฉ: \(body)
"""
```
7. preferredStyle ํ๋ผ๋ฏธํฐ๋ก ๋ณด๋ด๊ธฐ
- ์ฝ๋ ์ถ๊ฐ ํ ๋ณ๊ฒฝ ์๋ฃ ํ์ต๋๋ค ๊ฐ์ฌํฉ๋๋ค!!
8. Create, delete ์๋ฌ ๋ฐ์ํ์ง ์๋์ง?
- create ์์๋ ๋จ์ํ ๊ฐ์ฒด๋ฅผ ์์ฑ๋ง ํ๋ ๊ฒ์ด๋ผ ์ค๋ฅ๊ฐ ๋์ง ์์ง๋ง, delete ์์๋ ์ค๋ฅ๊ฐ ๋ ์ ์์ ๊ฒ ๊ฐ์์ ์๋ฌํ์
์ ์ถ๊ฐํ์์ต๋๋ค.
9. Fatalerror ์ฌ์ฉ ์ด์ -> ๊ทธ๋๋ก ๊ธ์ด์์์ ใ
- persistentContainer์ ๊ฒฝ์ฐ ์ฑ๊ธํค ๊ฐ์ฒด ๋ด๋ถ ํ๋กํผํฐ ํ์์ผ๋ก ๋์ด ์์ด, ์ถ๊ฐ ์ค๋ฅ์ฒ๋ฆฌ๊ฐ ์ด๋ ต๋ค๊ณ ํ๋จ๋์ด fatalError๋ฅผ ์ฌ์ฉํ์์ต๋๋ค. ํด๋น ๋ถ๋ถ์์ ์ค๋ฅ๊ฐ ๋๋ค๋ฉด ์ ์ฅ์ ์์ฒด๋ฅผ ๋ถ๋ฌ์ค์ง ๋ชปํ ์ผ์ด์ค๋ผ์ ์ ์์ ์ผ๋ก ์ฑ์ด ์๋ํ์ง ๋ชปํ ๊ฒ์ด๋ผ๊ณ ์๊ฐ๋์์ต๋๋ค. ๋ฐ๋ฉด saveContext์ ์๋ fatalError์ ๊ฒฝ์ฐ๋ ์์ฒด์ ์ผ๋ก save ์คํจ ์ค๋ฅ๋ฅผ ๋ง๋ค์ด ๋ทฐ์ปจํธ๋กค๋ฌ๋ก throwํด์ฃผ๋ฉด ์ฑ์ ๊ฐ์ ์ข
๋ฃ์ํค๋ ๋์ ์ผ๋ฟ ์ฒ๋ฆฌ๋ก ๋์ฒดํ ์ ์์ ๊ฒ ๊ฐ์์ ์์ ์งํํ์ต๋๋ค!
10. ๊ฐํ ์ ์งํ๊ธฐ (ํ๋๋ง components๋ก ๋ฐ๊พธ๊ธฐ & cell์ ๋ณด๋ด๊ธฐ ์ ์ ์ฒ๋ฆฌ ์ถ๊ฐ)
- ๋ง์ ๋ฃ๊ณ ๋ณด๋ ์๋์ ์ธ ๊ฐํ์ ์ ์งํ๋ ๊ฒ์ด ์ข์ ๊ฒ ๊ฐ์์ ์์ ์งํํ์์ต๋๋ค!
11. ๋ค์์คํ์ด์ค ๋ง๋ค๊ธฐ
- ๋ค์์คํ์ด์ค๋ฅผ ์ฐ๋ฉด ๊ด๋ฆฌ์ ์ฉ์ด์ฑ์ ์ฌ๋ผ๊ฐ์ง๋ง ์ฝ๋์ ๊ฐ๋
์ฑ์ด ์คํ๋ ค ๋จ์ด์ง๋ ๋จ์ ๋ ํจ๊ป ์ค๋ ๊ฒ ๊ฐ์์, ์ฒ์์๋ ๋ค์์คํ์ด์ค ์์ด ์์
์ ํ์์ต๋๋ค...! ๊ทธ๋ฆฐ์ ์๊ฒฌ์ ๋ฃ๊ณ ๊ณ ๋ฏผ์ ํด๋ณด๋ค๊ฐ ์ผ๋จ ๋งค์ง ๋ฆฌํฐ๋ด๋ค์ ๋ค์์คํ์ด์ค๋ก ๊ตฌ๋ถํ์ฌ ์ฎ๊ฒจ๋ณด์์ต๋๋ค. (๋ค๋ง ์ค๋ฅ์ ๊ด๋ จ๋ alert ๋ด์ฉ์ ๋ชจ๋ ์ค๋ฅ ํ์
์ผ๋ก ์ฎ๊ฒผ๋๋ ๋ค์์คํ์ด์ค์ ๊ตฌ๋ถ์ด ๋ค์ ์ ๋งคํด์ง ๊ฒ ๊ฐ๋ค์๐ญ)
12. ๊ฐ์ข
ํ
์คํธ -> ์ด๋ป๊ฒ ํ ์ง?
- SceneDelegate์ applicationWillTerminate ๋ฉ์๋์ ์ ์ฅ ๋ด์ฉ์ ์ถ๊ฐํด ๋ณด์๋๋ฐ, ์๋ฎฌ๋ ์ดํฐ ์์ฒด๋ฅผ ๊ฐ์ ์ข
๋ฃ์ํค๋ ์์ผ๋ก ํ
์คํธ๋ฅผ ์งํํ๋๋ ์ฑ๋ง ๊ฐ์ ์ข
๋ฃ๋ ๊ฒฝ์ฐ์ ๋ํ ํ
์คํธ๊ฐ ์ ๋๋ก ์ด๋ฃจ์ด์ง์ง ์๋ ๊ฒ ๊ฐ์ต๋๋ค๐ญ ํ์ธ์ด ์ด๋ ค์์ ํด๋น ๋ด์ฉ์ ๋ค์ ์๋ณตํด ๋์์ต๋๋ค.
```swift
func applicationWillTerminate(_ application: UIApplication) {
CoreDataManager.shared.saveContext()
}
```
13. createVC ๋ค์ด๋ฐ: DiaryDetailViewContoller
- ๋ ๊ฐ์ง ๋ค๋ฅธ ์์
์ ์งํํ๋ ๋ทฐ์ปจํธ๋กค๋ฌ์ ๋ค์ด๋ฐ์ ์ ํ๊ธฐ๊ฐ ์ฝ์ง ์์์ ๊ณ ๋ฏผ์ด ์์์ต๋๋ค๐ญ ๊ฒฐ๊ณผ์ ์ผ๋ก List๋ผ๋ ์ด๋ฆ์ ๋๋น๋๋ Detail์ด๋ผ๋ ๋ช
์นญ์ ํ์ฉํ์์ต๋๋ค.
---
# PR
## STEP 3
> ์ผ๊ธฐ์ฅ [STEP 3] Hamg, maxhyunm
์๋
ํ์ธ์ ๊ทธ๋ฆฐ!(@GREENOVER)
์ผ๊ธฐ์ฅ STEP 3 PR ์ฌ๋ ค๋๋ฆฝ๋๋ค.
์ด๋ฒ ์คํ
๋ ์ ๋ถํ๋๋ฆฝ๋๋ค๐โ
## ๊ณ ๋ฏผํ๋ ์
### ๐น CoreLocation ํ์ฉ
#### CoreLocation์ ํตํด ์ ๋ณด๋ฅผ ๋ฐ์์ค๋ ์์น
์ค์ง์ ์ผ๋ก Location ์ ๋ณด๊ฐ ํ์ํ ๊ฒ์ DiaryDetailViewController์์ ๋ ์จ API๋ฅผ ํธ์ถํ ๋์
๋๋ค. ๊ทธ๋ ์ง๋ง DiaryDetailViewController์์ ์์น์ ๋ณด ํ์ฉ ๋์๋ฅผ ๋ฐ๊ณ ์
๋ฐ์ดํธ๋ฅผ ์งํํ๋ ๊ฒ๋ณด๋ค๋ ์ฑ์ ์ฒซ ํ๋ฉด์์ ํด๋น ๋ด์ฉ์ ์งํํ๋ ํธ์ด ๋ ์์ฐ์ค๋ฌ์ธ ๊ฒ ๊ฐ๋ค๊ณ ํ๋จ๋์์ต๋๋ค. ์ด์ ๋ฐ๋ผ ์์น ์ ๋ณด ์
๋ฐ์ดํธ ์์ฒด๋ ์ฒซ ํ๋ฉด์ธ DiaryListViewController์์ ์งํํ๊ณ , DiaryDetailViewController์์๋ API ํต์ ์ ํ์ํ ์๋, ๊ฒฝ๋ ๋ฐ์ดํฐ๋ง ๋๊ฒจ๋ฐ์ ์ ์๋๋ก ๊ตฌํํ์์ต๋๋ค.
```swift
let createDiaryView = DiaryDetailViewController(latitude: self.latitude, longitude: self.longitude)
self.navigationController?.pushViewController(createDiaryView, animated: true)
```
๋ํ ์์น์ ๋ณด ํ์ฉ์ ๋์ํ์ง ์์ ๊ฒฝ์ฐ์๋ ์ผ๊ธฐ ์์ฒด๋ ์์ฑ ๊ฐ๋ฅํ๋๋ก ๊ตฌํํ๊ธฐ ์ํด(๋ ์จ ์ด๋ชจํฐ์ฝ๋ง ์ ์ธ) ์๋, ๊ฒฝ๋ ๋ฐ์ดํฐ๋ nil๋ก๋ ์ ๋ฌ๋ ์ ์๋๋ก ํ์์ต๋๋ค.
```swift
init(latitude: Double?, longitude: Double?) {
self.diary = CoreDataManager.shared.createDiary()
self.isNew = true
self.latitude = latitude
self.longitude = longitude
super.init(nibName: nil, bundle: nil)
fetchWeather()
}
```
#### ์๋ฎฌ๋ ์ดํฐ์ ์์น ์ ๋ณด ์ค์
์๋ฎฌ๋ ์ดํฐ๋ก CoreLocation ๊ธฐ๋ฅ์ ํ
์คํธํ๋ฉด ์๋ฎฌ๋ ์ดํฐ ์์ฒด์ ์ค์ ๋ Location ์ ๋ณด์ ๋ฐ๋ผ ์์น๋ฅผ ํ์ํ๊ฒ ๋ฉ๋๋ค. ๋ฐ๋ผ์ ์ด ์ค์ ์ด None์ผ๋ก ๋์ด์์ ๊ฒฝ์ฐ์๋ ์์น๊ฐ ์ ์์ ์ผ๋ก ๋ถ๋ฌ์์ง์ง ์์ต๋๋ค. ์ด ์ฌ์ค์ ๊ฐ๊ณผํ์ฌ ํ
์คํธ ๊ณผ์ ์์ ๋ง์ ์ํ์ฐฉ์ค๋ฅผ ๊ฑฐ์ณค์ต๋๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก๋ Custom Location์ ํ์ฉํ์ฌ ์ ์์ ์ผ๋ก ํ
์คํธ๋ฅผ ์งํํ ์ ์์์ต๋๋ค.
<img src="https://hackmd.io/_uploads/BJ20Xkgyp.png" width="500">
<br/>
### ๐น ์์ด์ฝ ์บ์ฑ
ํ์ฌ ์ ํฌ์ ์ผ๊ธฐ์ฅ ์ฑ์ ๋ชจ๋ ์
์ด ์๋ฒํต์ ์ ํตํด ์์ด์ฝ์ ๊ฐ์ง๊ณ ์ค๋๋ก ๊ตฌํ๋์ด ์์ต๋๋ค. ํ์ง๋ง ๋ ์จ ์์ด์ฝ์ ๋ช ๊ฐ์ ์ ํด์ง ์์ด์ฝ์ด ๋ฐ๋ณต๋์ด ํ์ฉ๋๊ณ ์๊ธฐ์, ์ด๋ฏธ์ง ์บ์ฑ์ผ๋ก ์ ์ฅํ์ฌ ๋ฐ๋ก ๋ณด์ฌ์ฃผ๋ฉด ์ข๊ฒ ๋ค๊ณ ์๊ฐํ์ต๋๋ค.
```swfit
class ImageCachingManager {
static let shared = NSCache<NSString, UIImage>()
...
}
```
```swift
switch result {
case .success(let data):
guard let image = UIImage(data: data) else { return }
DispatchQueue.main.async {
ImageCachingManager.shared.setObject(image, forKey: NSString(string: icon))
self?.weatherIconImageView.image = image
}
```
## ์กฐ์ธ์ด ํ์ํ ์
### ๐น DTO ํ์ฉ
๋ ์จ ์๋ฒ์ API์๋ ์ ํฌ๊ฐ ์ฌ์ฉํ๊ณ ์ ํ๋ `Weather`๋ฅผ ์ ์ธํ๊ณ ๋ ๋ง์ ๋ด์ฉ์ด ํฌํจ๋์ด ์์ต๋๋ค. ์ ํฌ๋ ์ด๋ฅผ ํ์ฉํ๊ธฐ ์ํด ์ผ๋จ API์์ ์ ๋ฌ๋ฐ๋ ๋ชจ๋ ๋ด์ฉ์ Decodingํ ์ ์๋๋ก ๊ด๋ จ ํ์
์ ๋ชจ๋ ์์ฑํ์์ต๋๋ค.
```swift
struct WeatherResult: Decodable {
let coord: Coord
let weather: [Weather]
let base: String
let main: Main
let visibility: Int
let wind: Wind
let clouds: Clouds
let date: Int
......
}
```
ํ์ง๋ง ๋ชจ๋ ๋ด์ฉ์ ๊ตฌํํ์ง ์๊ณ ์๋์ ๊ฐ์ด ํ์ํ ์ ๋ณด๋ง ๊ณจ๋ผ DTO ํ์
์ ๋ง๋ค์ด๋ ์ ์์ ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ๋ถ๋ฌ์์ง๋ ๊ฒ์ ํ์ธํ์์ต๋๋ค.
```swift
struct WeatherResult: Decodable {
let weather: [Weather]
}
struct Weather: Decodable {
let id: Int
let main: String
let description: String
let icon: String
}
```
์ฌ๊ธฐ์ ์ ์ฒด๋ฅผ ๋ค ์์ฑํ์ฌ ์ ์ฐ์ฑ ์๊ฒ ์ฌ์ฉํ ์ง ์๋๋ฉด
ํ์ํ ๋ถ๋ถ๋ง ๋ง๋ค์ด์ ์ฌ์ฉํด๋ ๋๋ ๊ฒ์ธ์ง ๊ณ ๋ฏผ์ด ๋ฉ๋๋ค.
๋ณดํธ์ ์ผ๋ก ์ด๋ป๊ฒ ์ ํํ์๋์ง ๊ถ๊ธํฉ๋๋ค!