# ์ผ๊ธฐ์žฅ README # ๐Ÿ“” ์ผ๊ธฐ์žฅ > ๋‚˜์˜ ์ผ๊ธฐ๋ฅผ ๋“ฑ๋ก, ์ˆ˜์ •, ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋Š” ์•ฑ > > ํ”„๋กœ์ ํŠธ ๊ธฐ๊ฐ„: 2023.04.24-2023.05.12 > ## ํŒ€์› | kokkilE | ํ˜œ๋ชจ๋ฆฌ | | :---:|:---:| | <Img src ="https://i.imgur.com/4I8bNFT.png" width="200" height="200"/> |<Img src ="https://i.imgur.com/VJtnO5j.png" width="200" height="200"/> | [Github Profile](https://github.com/kokkilE) |[Github Profile](https://github.com/hyemory) ## ๋ชฉ์ฐจ 1. [ํƒ€์ž„๋ผ์ธ](#ํƒ€์ž„๋ผ์ธ) 2. [ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ](#ํ”„๋กœ์ ํŠธ-๊ตฌ์กฐ) 3. [์‹คํ–‰ ํ™”๋ฉด](#์‹คํ–‰-ํ™”๋ฉด) 4. [ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…](#ํŠธ๋Ÿฌ๋ธ”-์ŠˆํŒ…) 5. [์ฐธ๊ณ  ๋งํฌ](#์ฐธ๊ณ -๋งํฌ) 6. [ํŒ€ ํšŒ๊ณ ](#ํŒ€-ํšŒ๊ณ ) # ํƒ€์ž„๋ผ์ธ |๋‚ ์งœ|๋‚ด์šฉ| |:-----:| ------ | | 2023.04.24 | - JSON Decode ๋ชจ๋ธ์ธ Contents ํƒ€์ž… ๊ตฌํ˜„ <br>- ์ผ๊ธฐ ๋ฆฌ์ŠคํŠธ ํ™”๋ฉด ๊ตฌํ˜„ <br> - custom TableviewCell ๊ตฌํ˜„ <br>- SwiftLint ์ ์šฉ| | 2023.04.25 | - ๋‚ ์งœ ์ง€์—ญํ™” ๊ตฌํ˜„ <br>- ์ƒ์„ธํŽ˜์ด์ง€ ํ™”๋ฉด ๊ตฌํ˜„ <br> - KeyBoard์— ๋”ฐ๋ฅธ ๋ทฐ ์œ„์น˜ ๋ณ€๊ฒฝ ๊ตฌํ˜„| | 2023.04.26 | - DecodeManager ๊ตฌํ˜„<br>- AlertManager ๊ตฌํ˜„ <br>- keyboardLayoutGuide ์ ์šฉ <br>- ํ”„๋กœ์ ํŠธ Minimum DeployMents ๋ณ€๊ฒฝ (14.0 โ†’ 15.0) | | 2023.04.28 | - CoreDataManager - Create, Read ๊ธฐ๋Šฅ ๊ตฌํ˜„<br>- Coredata์˜ Entity ๊ตฌํ˜„ | | 2023.05.01 | - Core Data Update, Delete ๊ตฌํ˜„์„ ์œ„ํ•œ ์ถ”๊ฐ€ ํ•™์Šต | | 2023.05.02 | - CoreDataManager - Update, Delete ๊ธฐ๋Šฅ ๊ตฌํ˜„<br>- VC์˜ ๋ฐ์ดํ„ฐ CRUD ๊ธฐ๋Šฅ ๊ตฌํ˜„ | | 2023.05.03 | - ์—๋Ÿฌ Alert ๊ธฐ๋Šฅ ๊ตฌํ˜„ <br> - ๋ฐ์ดํ„ฐ๊ฐ€ ํŽธ์ง‘๋  ๋•Œ ์ „์ฒด ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹Œ ํŽธ์ง‘๋œ ๋ฐ์ดํ„ฐ๋งŒ reload ํ•˜๋„๋ก ๊ธฐ๋Šฅ ์ˆ˜์ • | | 2023.05.04 | - ์—๋Ÿฌ ์ฒ˜๋ฆฌ ์œ„์น˜ ์ˆ˜์ • (model โ†’ VC)| | 2023.05.05 | - ํ”„๋กœ์ ํŠธ ํšŒ๊ณ  ๋ฐ ํœด์‹ | | 2023.05.08 | - NetworkManager, EndPoint ๊ตฌํ˜„ | | 2023.05.09 | - Core Location์œผ๋กœ ์‚ฌ์šฉ์ž ์œ„์น˜์ •๋ณด ์ €์žฅ ๊ตฌํ˜„ | | 2023.05.10 | - ํ™”๋ฉด์— ๋‚ ์”จ ์•„์ด์ฝ˜์„ ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„ | | 2023.05.11 | - ์ฝ”๋“œ ์ „์ฒด ๋ฆฌํŒฉํ† ๋ง (ํƒ€์ž… ๋ถ„๋ฆฌ, ์ปจ๋ฒค์…˜ ์ •๋ฆฌ) | | 2023.05.12 | - ํ”„๋กœ์ ํŠธ ํšŒ๊ณ  | <br/> # ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ## Class Diagram <details> <summary> ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ ๋ณด๊ธฐ (ํด๋ฆญ) </summary> <div markdown="1"> ![](https://github.com/hyemory/ios-diary/blob/64756e52bd1a257e2bb5c6d5d059e6e9d04b93d6/images/Class%20Diagram.png?raw=true) </div> </details> ## File Tree <details> <summary> ํŒŒ์ผ ํŠธ๋ฆฌ ๋ณด๊ธฐ (ํด๋ฆญ) </summary> <div markdown="1"> ```typescript! โ”œโ”€โ”€ .swiftlint.yml โ”œโ”€โ”€ Common โ”‚ โ”œโ”€โ”€ CoreData โ”‚ โ”‚ โ”œโ”€โ”€ Diary.xcdatamodeld โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Diary v2.xcdatamodel โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ Diary.xcdatamodel โ”‚ โ”‚ โ”œโ”€โ”€ ContentsEntity+CoreDataClass.swift โ”‚ โ”‚ โ”œโ”€โ”€ ContentsEntity+CoreDataProperties.swift โ”‚ โ”‚ โ””โ”€โ”€ CoreDataManager.swift โ”‚ โ”œโ”€โ”€ Extension โ”‚ โ”‚ โ”œโ”€โ”€ Date+.swift โ”‚ โ”‚ โ””โ”€โ”€ NotificationName+.swift โ”‚ โ”œโ”€โ”€ Network โ”‚ โ”‚ โ”œโ”€โ”€ EndPoint.swift โ”‚ โ”‚ โ””โ”€โ”€ NetworkManager.swift โ”‚ โ”œโ”€โ”€ Error โ”‚ โ”‚ โ”œโ”€โ”€ DiaryError.swift โ”‚ โ”‚ โ””โ”€โ”€ NetworkError.swift โ”‚ โ”œโ”€โ”€ Utility โ”‚ โ”‚ โ”œโ”€โ”€ AlertManager.swift โ”‚ โ”‚ โ”œโ”€โ”€ DecodeManager.swift โ”‚ โ”‚ โ””โ”€โ”€ LocationManager.swift โ”‚ โ””โ”€โ”€ Model โ”‚ โ”œโ”€โ”€ ContentsDTO.swift โ”‚ โ”œโ”€โ”€ WeatherDTO.swift โ”‚ โ””โ”€โ”€ Coordinate.swift โ”œโ”€โ”€ Presentation โ”‚ โ”œโ”€โ”€ DiaryList โ”‚ โ”‚ โ”œโ”€โ”€ Protocol โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ DiaryDetailViewControllerDelegate.swift โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ IdentifierType.swift โ”‚ โ”‚ โ”œโ”€โ”€ ContentsTableViewCell.swift โ”‚ โ”‚ โ””โ”€โ”€ DiaryListViewController.swift โ”‚ โ””โ”€โ”€ DiaryDetail โ”‚ โ”œโ”€โ”€ DiaryDetailViewController.swift โ”‚ โ””โ”€โ”€ WeatherNetworkManager.swift โ”œโ”€โ”€ Resources โ”‚ โ””โ”€โ”€ Info.plist โ””โ”€โ”€ Application โ”œโ”€โ”€ AppDelegate.swift โ””โ”€โ”€ SceneDelegate.swift ``` </div> </details> # ์‹คํ–‰ ํ™”๋ฉด |<center>์ดˆ๊ธฐ ํ™”๋ฉด<br>์ผ๊ธฐ ๋ชฉ๋ก ํ™”๋ฉด</center>|<center>๋ฐ์ดํ„ฐ ๋กœ๋“œ ์‹คํŒจ ์‹œ<br>์•Œ๋ฆผ ํ‘œ์‹œ</center>|<center>์ผ๊ธฐ ๋ชฉ๋ก ํ™”๋ฉด<br>์Šค์™€์ดํ”„๋กœ ๊ณต์œ  ๋ฐ ์‚ญ์ œ</center>| |--| -- | -- | |<img src="https://hackmd.io/_uploads/H1CTDXj42.gif" width=250> | <img src="https://i.imgur.com/kWbnD8y.gif" width=250> | <img src="https://hackmd.io/_uploads/SJn-uXiNn.gif" width=250> | |<center>์ผ๊ธฐ ์ƒ์„ธ ํ™”๋ฉด<br>์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ ์ €์žฅ</center> |<center>์ผ๊ธฐ ์ƒ์„ธ ํ™”๋ฉด<br>๋ฐ์ดํ„ฐ ํŽธ์ง‘ ํ›„ ์ €์žฅ</center>|<center>์ผ๊ธฐ ์ƒ์„ธ ํ™”๋ฉด<br>๋”๋ณด๊ธฐ โ†’ ๊ณต์œ  ๋ฐ ์‚ญ์ œ</center> | | -- | -- | -- | |<img src="https://hackmd.io/_uploads/SJhKuXjE2.gif" width=250> | <img src="https://hackmd.io/_uploads/HkST_7sVn.gif" width=250> | <img src="https://hackmd.io/_uploads/Hy9wYQoNn.gif" width=250> | # ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ… ## 1๏ธโƒฃ ํ‚ค๋ณด๋“œ๊ฐ€ ํŽธ์ง‘์ค‘์ธ ํ…์ŠคํŠธ๋ฅผ ๊ฐ€๋ฆฌ์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌ ํ‚ค๋ณด๋“œ๊ฐ€ ๋‚˜ํƒ€๋‚  ๋•Œ ํŽธ์ง‘์ค‘์ธ ํ…์ŠคํŠธ๋ฅผ ๊ฐ€๋ฆฌ์ง€ ์•Š๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด ์„ธ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„ ๊ณ ๋ คํ•˜์˜€์Šต๋‹ˆ๋‹ค. - ํ‚ค๋ณด๋“œ๊ฐ€ ๋‚˜ํƒ€๋‚  ๋•Œ textView์˜ ์˜คํ† ๋ ˆ์ด์•„์›ƒ์„ ์กฐ์ •ํ•˜์—ฌ ๊ตฌํ˜„ - ํ‚ค๋ณด๋“œ๊ฐ€ ๋‚˜ํƒ€๋‚  ๋•Œ textView.contentInset์„ ์กฐ์ •ํ•˜์—ฌ ๊ตฌํ˜„ - **keyboardLayoutGuide์˜ ์ œ์•ฝ์œผ๋กœ ๊ตฌํ˜„** ์œ„์˜ ์ฒซ ๋ฒˆ์งธ, ๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ Notification์„ ํ™œ์šฉํ•˜์—ฌ ํ‚ค๋ณด๋“œ ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์ธ ์˜คํ† ๋ ˆ์ด์•„์›ƒ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๊ตฌํ˜„ํ•  ๊ฒฝ์šฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋”œ๋ ˆ์ด ์‹œ๊ฐ„๋™์•ˆ ํ‚ค๋ณด๋“œ๊ฐ€ ๋‚ด๋ ค๊ฐ์—๋„ ์ฑ„์›Œ์ง€์ง€ ์•Š๋Š” ๋ ˆ์ด์•„์›ƒ์ด ์–ด์ƒ‰ํ•˜๊ฒŒ ๋А๊ปด์กŒ์Šต๋‹ˆ๋‹ค. <br/> <img src= "https://i.imgur.com/L3u84Y7.gif" width=250> <br/> ๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•๊ณผ ์„ธ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ๋™์ž‘์ƒ์˜ ๋ฌธ์ œ๋Š” ์—†๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์„ธ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ keyboardLayoutGuide์˜ ์ œ์•ฝ์กฐ๊ฑด์œผ๋กœ ๋น„๊ต์  ๊ฐ„๋‹จํžˆ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•ด ์„ธ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์„ ์ฑ„ํƒํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ keyboardLayoutGuide์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด Minimum Deployments๋ฅผ iOS 15.0์œผ๋กœ ์˜ฌ๋ ค์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ### ๐Ÿ“„ ์ฝ”๋“œ ์ฐธ์กฐ #### ํ‚ค๋ณด๋“œ๊ฐ€ ๋‚˜ํƒ€๋‚  ๋•Œ textView์˜ ์˜คํ† ๋ ˆ์ด์•„์›ƒ์„ ์กฐ์ •ํ•˜์—ฌ ๊ตฌํ˜„ ``` swift @objc func keyboardWillShow(notification: Notification) { guard let userInfo = notification.userInfo, let keyboardFrameValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return } let keyboardHeight = keyboardFrameValue.cgRectValue.height textViewBottomAnchor.isActive = false // ํ‚ค๋ณด๋“œ๊ฐ€ ๋‚˜ํƒ€๋‚  ๊ฒฝ์šฐ textView์˜ bottomAnchor ์กฐ์ • textViewBottomAnchor = textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -keyboardHeight) textViewBottomAnchor.isActive = true } ``` #### ํ‚ค๋ณด๋“œ๊ฐ€ ๋‚˜ํƒ€๋‚  ๋•Œ textView.contentInset์„ ์กฐ์ •ํ•˜์—ฌ ๊ตฌํ˜„ ``` swift @objc private func keyboardWillShow(notification: Notification) { guard let userInfo = notification.userInfo, let keyboardFrameValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return } let keyboardHeight = keyboardFrameValue.cgRectValue.height textView.contentInset.bottom = keyboardHeight textView.verticalScrollIndicatorInsets.bottom = keyboardHeight } ``` #### โœ… keyboardLayoutGuide์˜ ์ œ์•ฝ์œผ๋กœ ๊ตฌํ˜„ ``` swift private func configureLayout() { //... view.keyboardLayoutGuide.followsUndockedKeyboard = true NSLayoutConstraint.activate([ view.keyboardLayoutGuide.topAnchor.constraint(equalTo: textView.bottomAnchor), //... textView.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor) ]) } ``` ## 2๏ธโƒฃ ์ผ๊ธฐ์žฅ ๋‚ด์šฉ์ด ์ „๋ถ€ ๋ณด์ด๋„๋ก ์ˆ˜์ • ### ๐Ÿ” ๋ฌธ์ œ์  ๊ฐ€์žฅ ๊ธด ๊ธ€์˜ ์ˆ˜์ • ํŽ˜์ด์ง€๋กœ ์ด๋™ํ–ˆ์„ ๋•Œ ์œ„๋กœ ์Šคํฌ๋กคํ•˜์ง€ ์•Š์œผ๋ฉด ์ œ๋ชฉ ๋ถ€๋ถ„์ด ์ž˜๋ ค๋ณด์ด๋Š” ํ˜„์ƒ์ด ํ™•์ธ๋์Šต๋‹ˆ๋‹ค. <img src= "https://i.imgur.com/6GIrrZx.gif" width=250> ### โš’๏ธ ํ•ด๊ฒฐ๋ฐฉ์•ˆ `contentSize`๊ฐ€ ํ…์ŠคํŠธ ๋‚ด์šฉ๋ณด๋‹ค ์ž‘์•„ ๋ฐœ์ƒํ•œ ํ˜„์ƒ์œผ๋กœ, ํ•˜์ด์–ด๋ผํ‚ค๋ฅผ ์„ค์ •ํ•  ๋•Œ ๋ทฐ์˜ offset์˜ ํฌ๊ธฐ๋ฅผ ์ดˆ๊ธฐํ™”์‹œ์ผœ์ฃผ๋‹ˆ ์ •์ƒ์ ์œผ๋กœ ํ‘œ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ``` swift private func configureLayout() { view.addSubview(textView) textView.contentOffset = .zero // ์ถ”๊ฐ€ // ์ดํ•˜ layout ์„ค์ • } ``` ## 3๏ธโƒฃ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ •๋ฆฌ ### ๐Ÿ” ๋ฌธ์ œ์  ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์ค‘ ์Šคํ… ์š”๊ตฌ์‚ฌํ•ญ ์™ธ์— ์–ด์ƒ‰ํ•˜๋‹ค๊ณ  ์ƒ๊ฐ๋˜๋Š” ๋กœ์ง์ด ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋‚ด์šฉ๋“ค์€ ์˜ˆ์™ธ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜์—ฌ ๊ด€๋ จ ์ •์ฑ…์„ ์ž์ฒด์ ์œผ๋กœ ํ˜‘์˜ํ•˜์—ฌ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ### โš’๏ธ ๊ฒฐ๊ณผ 1. ์ƒˆ ๊ธ€์„ createํ•  ๋•Œ ์•„๋ฌด ๋‚ด์šฉ๋„ ์ž…๋ ฅํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ โ†’ ์ €์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. 3. ์ƒˆ ๊ธ€์„ createํ•˜๊ณ  + ์ž๋™ ์ €์žฅ๋œ ํ›„ ๊ธ€ ๋‚ด์šฉ์„ ์ „๋ถ€ ์‚ญ์ œํ•œ ๊ฒฝ์šฐ โ†’ ๋น„์–ด์žˆ๋Š” ๋‚ด์šฉ์ด ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. 5. ๊ธ€์„ ํ•œ ์ค„๋งŒ ๋“ฑ๋กํ•œ ๊ฒฝ์šฐ (title๋งŒ) โ†’ title์€ ์ž…๋ ฅํ•œ ๋‚ด์šฉ์ด ์ €์žฅ๋˜๊ณ  body๋Š” ๋นˆ ๋ฌธ์ž์—ด๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. 7. ์ด๋ฏธ ๋“ฑ๋ก๋œ ๊ธ€์˜ ๋‚ด์šฉ์„ ์ „๋ถ€ ์ง€์šด ๊ฒฝ์šฐ update ์—ฌ๋ถ€ โ†’ ๋น„์–ด์žˆ๋Š” ๋‚ด์šฉ์ด ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. 9. ์ƒˆ ๊ธ€์„ createํ•  ๋•Œ ๋”๋ณด๊ธฐ ๋ฒ„ํŠผ(right bar button) ๋…ธ์ถœ ์—ฌ๋ถ€ โ†’ ์ž…๋ ฅ ํ›„ ๋‚ด์šฉ์„ ๋ฐ”๋กœ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜, ์ž๋™์ €์žฅ๋œ ๋‚ด์šฉ์„ ๋ฐ”๋กœ ์‚ญ์ œ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒˆ ๊ธ€์„ ์ €์žฅํ•  ๋•Œ๋„ ๋ฒ„ํŠผ์ด ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ## 4๏ธโƒฃ ๋ชฉ๋ก ํ™”๋ฉด๊ณผ ์ƒ์„ธ ํ™”๋ฉด๊ฐ„ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™” ์ƒ์„ธ ํ™”๋ฉด์—์„œ ํŽธ์ง‘๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชฉ๋ก ํ™”๋ฉด์—๋„ ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•œ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ฐœ์„  ์ „์—๋Š” ๋ชฉ๋ก ํ™”๋ฉด์œผ๋กœ ๋Œ์•„์˜ฌ ๋•Œ ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ์ฝ๊ณ , ๋ชฉ๋ก์„ reloadํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ฐœ์„  ํ›„์—๋Š” ํŽธ์ง‘๋œ ๋ฐ์ดํ„ฐ๋งŒ ๊ฐฑ์‹ ๋˜๋„๋ก ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ### ๐Ÿ” ๋ฌธ์ œ์  ์ƒ์„ธ ํ™”๋ฉด์—์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ํŽธ์ง‘๋œ ํ›„ ๋‹ค์‹œ ๋ชฉ๋ก ํ™”๋ฉด์œผ๋กœ ๋Œ์•„์˜ฌ ๋•Œ, ํŽธ์ง‘๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชฉ๋ก ํ™”๋ฉด์— ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•ด `DiaryDetailViewController`์—์„œ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ํŽธ์ง‘๋  ๋•Œ CoreData ์ €์žฅ์†Œ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ ๋ชฉ๋ก ํ™”๋ฉด์ด ๋‹ค์‹œ ๋‚˜ํƒ€๋‚  ๋•Œ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐฑ์‹ ํ•˜์˜€์Šต๋‹ˆ๋‹ค. **DiaryListViewController** ``` swift override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) fetchContents() tableView.reloadData() } ``` ์œ„ ์ฝ”๋“œ๋Š” ๋™์ž‘์— ๋ฌธ์ œ๋Š” ์—†์—ˆ์ง€๋งŒ, ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ๋งŒ ํŽธ์ง‘๋˜๋Š” ์ƒํ™ฉ์—์„œ ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ์ฝ์–ด์˜ค๊ณ , reloadํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก ์˜ค๋ฒ„ํ—ค๋“œ๋Š” ์ปค์ง€๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ์„ ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์˜€์Šต๋‹ˆ๋‹ค. ### โš’๏ธ ํ•ด๊ฒฐ๋ฐฉ์•ˆ ์ƒ์„ธ ํ™”๋ฉด์—์„œ ํŽธ์ง‘๋œ ๋ฐ์ดํ„ฐ๋งŒ ๋ชฉ๋ก ํ™”๋ฉด์—์„œ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ๋„๋ก delegate ํŒจํ„ด์„ ์ ์šฉํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ๊ฐœ์„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค. **DiaryListViewController** ``` swift // MARK: - DiaryDetailViewController Delegate extension DiaryListViewController: DiaryDetailViewControllerDelegate { func createCell(contents: Contents) { // ... contentsList?.append(contents) tableView.insertRows(at: [selectedCellIndex], with: .automatic) } func updateCell(contents: Contents) { // ... contentsList?[selectedCellIndex.row] = contents tableView.reloadRows(at: [selectedCellIndex], with: .automatic) } func deleteCell() { // ... contentsList?.remove(at: selectedCellIndex.row) tableView.deleteRows(at: [selectedCellIndex], with: .fade) } } ``` **DiaryDetailViewController** ``` swift // ... weak var delegate: DiaryDetailViewControllerDelegate? // ... private func updateContents() { // ... delegate?.updateCell(contents: contents) // ... } private func createContents() { // ... delegate?.createCell(contents: contents) // ... } private func deleteContents() { // ... delegate?.deleteCell() // ... } ``` ## 5๏ธโƒฃ ๋‚ ์”จ ํ‘œ์‹œ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์ „์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์กดํ•˜๊ธฐ ์œ„ํ•œ ์ฒ˜๋ฆฌ ### ๐Ÿ” ๋ฌธ์ œ์  ๋‚ ์”จ ์ •๋ณด๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ฉด์„œ Core Data์˜ Entity ๋ชจ๋ธ์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ### โš’๏ธ ํ•ด๊ฒฐ๋ฐฉ์•ˆ ๊ทธ์— ๋”ฐ๋ผ ๊ธฐ์กด์— Core Data์— ์ €์žฅ๋˜์–ด์žˆ๋˜ ๋ชจ๋ธ๊ณผ Migrationํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ๊ธฐ์กด์— ๋‚ ์”จ ์ •๋ณด๊ฐ€ ์—†๋˜ ๋ฐ์ดํ„ฐ๋Š” ๋‚ ์”จ ์ •๋ณด๊ฐ€ ์—†๋Š” ์ฑ„๋กœ ๋ฐ›์•„์˜ค๊ธฐ ์œ„ํ•ด, `ContentsDTO`์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด `weather`๋ฅผ ์˜ต์…”๋„๋กœ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ``` swift /* ContentsDTO์€ ๋‹ค์Œ์˜ ์šฉ๋„๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. 1. JSON ๋ฐ์ดํ„ฐ Decode๋ฅผ ์œ„ํ•œ ๋ชจ๋ธ 2. VC์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ชจ๋ธ 3. VC์—์„œ CoreData์™€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ๋ชจ๋ธ */ struct ContentsDTO: Codable { var title: String var body: String let date: Double let identifier: UUID? var weather: Weather? ... } ``` ๋‚ ์งœ ์ •๋ณด๊ฐ€ ์—†์œผ๋ฉด ์—†๋Š” ๋Œ€๋กœ, ์žˆ์œผ๋ฉด ์žˆ๋Š” ๋Œ€๋กœ ์•ฑ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. # ์ฐธ๊ณ  ๋งํฌ ## ๋ธ”๋กœ๊ทธ - [WWDC 21 ๋ถ„์„: Adjust Your Layout with Keyboard Layout Guide](https://zeddios.tistory.com/1282) - [iOS) CoreData - Migration](https://yeonduing.tistory.com/48) - [IOS SWIFT ํ˜„์žฌ์œ„์น˜ ๊ตฌํ•˜๊ธฐ (CoreLocation, CLLocationManager)](https://tom7930.tistory.com/28) ## ๊ณต์‹ ๋ฌธ์„œ - [UITextView](https://developer.apple.com/documentation/uikit/uitextview) - [DateFormatter](https://developer.apple.com/documentation/foundation/dateformatter) - [preferredLanguages](https://developer.apple.com/documentation/foundation/nslocale/1415614-preferredlanguages) - [Adjusting Your Layout with Keyboard Layout Guide](https://developer.apple.com/documentation/uikit/keyboards_and_input/adjusting_your_layout_with_keyboard_layout_guide) - [keyboardLayoutGuide](https://developer.apple.com/documentation/uikit/uiview/3752221-keyboardlayoutguide) - [followsUndockedKeyboard](https://developer.apple.com/documentation/uikit/uikeyboardlayoutguide/3752189-followsundockedkeyboard) - [textViewDidEndEditing(_:)](https://developer.apple.com/documentation/uikit/uitextviewdelegate/1618628-textviewdidendediting) - [didEnterBackgroundNotification](https://developer.apple.com/documentation/uikit/uiapplication/1623071-didenterbackgroundnotification) - [willDeactivateNotification](https://developer.apple.com/documentation/uikit/uiscene/3197924-willdeactivatenotification) - [description](https://developer.apple.com/documentation/swift/customstringconvertible/description) - [init(context:)](https://developer.apple.com/documentation/coredata/nsmanagedobject/1640602-init) - [UIActivityViewController](https://developer.apple.com/documentation/uikit/uiactivityviewcontroller) - [init(activityItems:applicationActivities:)](https://developer.apple.com/documentation/uikit/uiactivityviewcontroller/1622019-init) - [dataTask(with:completionHandler:)](https://developer.apple.com/documentation/foundation/urlsession/1407613-datatask) - [Core Location](https://developer.apple.com/documentation/corelocation) - [Getting the Userโ€™s Location](https://developer.apple.com/documentation/corelocation/getting_the_user_s_location) - [Adding Location Services to Your App](https://developer.apple.com/documentation/corelocation/adding_location_services_to_your_app) - [Requesting Authorization for Location Services](https://developer.apple.com/documentation/corelocation/requesting_authorization_for_location_services) - [Using Lightweight Migration](https://developer.apple.com/documentation/coredata/using_lightweight_migration) ## API - [Open Weather - Current weather data](https://openweathermap.org/current) # ํŒ€ ํšŒ๊ณ  <details> <summary> ํŒ€ ํšŒ๊ณ  ๋ณด๊ธฐ (ํด๋ฆญ) </summary> <div markdown="1"> ### ์šฐ๋ฆฌ ํŒ€์ด ์ž˜ํ•œ ์  - ์‹œ๊ฐ„ ์•ฝ์†์„ ํ•˜๋ฃจ๋„ ๋น ์ง์—†์ด ์ž˜ ์ง€์ผฐ์Šต๋‹ˆ๋‹ค. - ์„œ๋กœ ์›ํ•˜๋Š” ์ ์„ ์†”์งํ•˜๊ฒŒ ์ด์•ผ๊ธฐํ•˜๊ณ  ์ž˜ ํ˜‘์˜ํ•˜์—ฌ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋„์ถœํ–ˆ์Šต๋‹ˆ๋‹ค. - ์ ์šฉํ•  ๊ธฐ์ˆ ์„ ์ถฉ๋ถ„ํžˆ ์ดํ•ดํ•˜๋ฉด์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ### ์„œ๋กœ ์นญ์ฐฌํ•  ์  - ์ฝ”๋‚„์ด -> ํ˜œ๋ชจ๋ฆฌ - ํŒ€์›์˜ ์˜๊ฒฌ์„ ์ž˜ ๋“ค์–ด์ฃผ๊ณ , ๋ณธ์ธ์˜ ์˜๊ฒฌ๋„ ์ ๊ทน์ ์œผ๋กœ ํ‘œํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ํ•ญ์ƒ ์˜๊ฒฌ์— ๊ทผ๊ฑฐ๊ฐ€ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ˜‘์˜๊ฐ€ ์›๋งŒํ–ˆ์Šต๋‹ˆ๋‹ค. - ๋Œ€ํ™”๋ฅผ ์ž˜ ์ด๋Œ์–ด์ค˜์„œ ์ฆ๊ฒ๊ฒŒ ํ˜‘์—…ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. - ํ˜œ๋ชจ๋ฆฌ -> ์ฝ”๋‚„์ด - ์ฝ”๋‚„์ด๋Š” ์ •๋ง ๊ผผ๊ผผํ•˜์…”์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๋„ ๊ธˆ๋ฐฉ ์ฐพ์„ ์ˆ˜ ์žˆ์—ˆ๊ณ , ์ œ ์–˜๊ธฐ๋ฅผ ์ถฉ๋ถ„ํžˆ ์ž˜ ๋“ค์–ด์ฃผ์‹œ๊ณ  ์ ์ ˆํ•œ ๋Œ€์•ˆ์„ ์ฃผ์…”์„œ ์ •๋ง ๋งŽ์ด ๋ฐฐ์šธ ์ˆ˜ ์žˆ๋Š” ๊ธฐํšŒ์˜€์Šต๋‹ˆ๋‹ค. - ์–ด๋ ค์šด ๊ฐœ๋…์„ ์ž˜ ์ดํ•ดํ•˜์‹œ๊ณ  ์ž˜ ์„ค๋ช…ํ•ด ์ฃผ์…”์„œ ๋“ฃ๋Š” ์ €๋„ ์ดํ•ดํ•˜๊ธฐ๊ฐ€ ๋ฌด์ฒ™ ์‰ฌ์› ์Šต๋‹ˆ๋‹ค. </div> </details>