ํ๋ก์ ํธ ๊ธฐ๊ฐ: 2023.01.02 - 2023.01.06
์ํ์งํฅ์์ํ Open API, OMDb API, Firebase-FireStore ํ์ฉํ์ฌ ๊ตฌํํ๋ ์ฑ
์๋์ ๊ฐ์ด .plist ํ์ผ์ ํ๋ก์ ํธ ๊ฒฝ๋ก์ ์ถ๊ฐํด์ฃผ์ธ์.
์ํค | ์๋ฆฌ |
---|---|
- FirebaseManager ๊ตฌํ - ์ํ ์์ธ์ ๋ณด ๊ตฌํ |
- ๋คํธ์ํฌ ๋์ ๊ตฌํ - ๋ฐ์ค์คํผ์ค ๋ชฉ๋ก ํ๋ฉด ๊ตฌํ - ๋ฆฌ๋ทฐ์์ฑ ํ๋ฉด ๊ตฌํ |
- ํ์ผ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ
- ๊ธฐ์ ์คํ
- ๊ธฐ๋ฅ ๋ฐ UI
- ์ค๊ณ ๋ฐ ๊ตฌํ
- ์คํ ํ๋ฉด
- ๊ธฐ์ ์ ๋์
- Truoble Shooting
- ๊ณ ๋ฏผํ๋ ์
BoxOffice
โโโ Resources
โ โโโ Assets.xcassets
โ โโโ Base.lproj
โโโ Sources
โโโ App
โโโ Extensions
โ โโโ Combine
โ โโโ UI
โโโ Presentation
โ โโโ Coordinator
โ โโโ CreateReview
โ โ โโโ Coordinator
โ โ โโโ ViewController
โ โ โโโ ViewModel
โ โ โโโ Views
โ โโโ Detail
โ โ โโโ ViewController
โ โ โโโ ViewModel
โ โ โโโ Views
โ โโโ List
โ โโโ Coordinator
โ โโโ ViewController
โ โโโ ViewModel
โ โโโ Views
โโโ Repositories
โโโ Model
โโโ Network
โโโ Protocol
โโโ Request
โโโ Response
- Swift/UIKit
- Combine
- MVVM
- Input/Output Modeling
- Coordinator
๊ธฐ๋ฅ/UI | ์ค๋ช |
---|---|
๋ฐ์ค์คํผ์ค ๋ชฉ๋ก | ์ผ๊ฐ, ์ฃผ๊ฐ, ์ฃผ๋ง๋ณ๋ก ๋ฐ์ค์คํผ์ค ์์๋ฅผ ๋ณด์ฌ์ค๋ค. |
์ํ ์์ธํ๋ฉด | ๋ชฉ๋กํ๋ฉด์์ ์ ํํ ์ํ์ ์์ธํ ์ ๋ณด๋ฅผ ๋ณด์ฌ์ค๋ค. ๊ณต์ ๋ฒํผ์ ํตํด ์ํ์ ์ ๋ณด๋ฅผ ๊ณต์ ํ ์ ์๋ค. ๊ทธ๋ฆฌ๊ณ '๋ฆฌ๋ทฐ์ฐ๊ธฐ' ๋ฒํผ์ ํตํด ์ํ์ ๋ํ ์๋ก์ด ํ๊ฐ๋ฅผ ์์ฑํ ์ ์๋ค. ์์ฑ๋์๋ ๋ฆฌ๋ทฐ์ ๋ชฉ๋ก๋ ๋ณด์ฌ์ค๋ค. |
๋ฆฌ๋ทฐ ์ฐ๊ธฐ | ์๋ก์ด ๋ฆฌ๋ทฐ๋ฅผ ์์ฑํ ์ ์๋ค. ๋ณ๋ช , ์ํธ, ๋ฆฌ๋ทฐ๋ด์ฉ, ๋ณ์ , ํ๋กํ ์ฌ์ง ๋ฑ์ผ๋ก ๊ตฌ์ฑ๋์ด์๊ณ , ํ์ ์ ๋ณด๋ฅผ ์ ๋ ฅํ๋ฉด ๋ฆฌ๋ทฐ๋ฅผ ๋ฑ๋กํ ์ ์๋ค. |
class/struct | ์ญํ |
---|---|
AppCoordinator |
์ฑ์ ๋ฃจํธ. ์ฒซ ํ๋ฉด์ ์ค๋นํ๊ธฐ ์ํ ํ์ |
BoxOfficeListCoordinator |
๋ชฉ๋ก ํ๋ฉด์ ํ๋ฉด ์ ํ์ ๋ด๋นํ๋ ํ์ |
BoxOfficeListViewController |
๋ฐ์ค์คํผ์ค ์์๋ฅผ ์ผ๊ฐ/์ฃผ๊ฐ/์ฃผ๋ง ๋ณ๋ก ๋ณด์ฌ์ค๋ค. |
MovieDetailViewController |
์ํ์ ์์ธํ ์ ๋ณด๋ฅผ ๋ณด์ฌ์ค๋ค. |
CreateReviewCoordinator |
๋ฆฌ๋ทฐ ์์ฑ ํ๋ฉด์ ํ๋ฉด ์ ํ์ ๋ด๋นํ๋ ํ์ |
CreateReviewViewController |
์ํ์ ๋ํ ์๋ก์ด ๋ฆฌ๋ทฐ๋ฅผ ์์ฑํ ์ ์๋ ํ๋ฉด์ด๋ค. |
class/struct | ์ญํ |
---|---|
DefaultAPIProvider |
๋คํธ์ํฌ ์์ฒญ์ ํ๋ ๊ธฐ๋ณธ ํ์ ์ด๋ค. |
BoxOfficeRepository |
๋คํธ์ํฌ๋ฅผ ํตํด ๋ฐ์์จ Response๋ฅผ ๋ทฐ์์ ์ฌ์ฉํ๊ธฐ ์์ํ๋๋ก ์ฒ๋ฆฌํด์ฃผ๋ ํ์ ์ด๋ค. |
FirebaseManager |
ํ์ด์ด๋ฒ ์ด์ค๋ฅผ ํ์ฉํ์ฌ ๋ฆฌ๋ทฐ๋ฅผ ๋ฑ๋ก, ์ญ์ , ๋ถ๋ฌ์ค๊ธฐ๋ฅผ ํ ์ ์๋ ํ์ ์ด๋ค. |
ImageCacheManager |
์ด๋ฏธ์ง ์บ์ฑ์ ๋ด๋นํ๋ ํ์ ์ด๋ค. |
๐ฅ ๋ฐ์ค์คํผ์ค ๋ญํน ํ๋ฉด
๐บ ์ํ์ ์์ธ ์ ๋ณด๋ฅผ ๋ํ๋ด๋ ํ๋ฉด
๐ ๋ฆฌ๋ทฐ๋ฅผ ์์ฑํ ์ ์๋ ํ๋ฉด
์ฐ์๋ escaping closure๋ฅผ ํผํ๊ณ , ์ ์ธํ ํ๋ก๊ทธ๋๋ฐ์ ํตํ ๋์ ๊ฐ๋ ์ฑ๊ณผ ์คํผ๋ ์ดํฐ๋ค์ ํตํ ํจ์จ์ ์ธ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ํด์ Combine์ ์ฌ์ฉํ๊ฒ ๋์์ต๋๋ค.
ํ๋ฉด ์ ํ์ ๋ํ ๋ก์ง์ ViewController๋ก๋ถํฐ ๋ถ๋ฆฌํ๊ณ ์์กด์ฑ ๊ฐ์ฒด์ ๋ํ ์ฃผ์ ์ ์ธ๋ถ์์ ์ฒ๋ฆฌํ๋๋ก ํ๊ธฐ ์ํด ์ฝ๋๋ค์ดํฐ๋ฅผ ์ ์ฉํ์ต๋๋ค.
ํ๋์ ์ฟผ๋ฆฌ์ ์ ๋ ฌ๊ณผ ํํฐ๋ง ๋ชจ๋ ๊ฐ๋ฅํ์ฌ ๋ณตํฉ์ ์ธ ์ฟผ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๊ณ , ๋์ฉ๋ ๋ฐ์ดํฐ๊ฐ ์์ฃผ ์ฝํ ๋ ์ฌ์ฉํ๊ธฐ ์ข์ FireStore ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ์์ต๋๋ค.
PassthroughSubject๋ก ์ด๋ฒคํธ๋ฅผ ๋ณด๋๋๋ฐ, ์ ๋ทฐ๊ฐ ์ด๋ฒคํธ๋ฅผ ์ ๋ฌ๋ฐ์ง ๋ชปํ๋ ๊ฑธ๊น?
๋ฌธ์ ์ํฉ
๋ทฐ๋ชจ๋ธ์์ PassthroughSubject ํ์ ์ผ๋ก Publisher๋ฅผ ๊ตฌํํ๊ณ , ๋ทฐ์๊ฒ ์ ๋ฌํ ๋๋ AnyPublisher๋ก ๋ณํํ์ฌ ๊ตฌ๋ ํ ์ ์๋๋ก ํด์ฃผ์๋ค. ํ์ง๋ง send๋ฅผ ํตํด ์๋ก์ด ๊ฐ์ ์ ๋ฌํ์์๋ ๋ถ๊ตฌํ๊ณ , ๊ตฌ๋ ํ๊ณ ์๋ ๋ทฐ์์ ์ ํ ์ ๋ฌ๋ฐ์ง ๋ชปํ๋ ์ํฉ์ด์๋ค.์ด์
PassthroughSubject์ ๊ฒฝ์ฐ ์๋ก์ด ๊ฐ์ ์ ๋ฌํ ๋, ๊ตฌ๋ ํ๊ณ ์๋ Subscriber๊ฐ ์กด์ฌํ์ง ์์ผ๋ฉด ๊ฐ์ ์ ๋ฌํ์ง ์๊ณ ์ญ์ ํ๊ธฐ ๋๋ฌธ์ด์๋ค.ํด๊ฒฐ
๊ทธ๋์ PassthroughSubject ํ์ ์ CurrentValueSubject๋ก ๋ณ๊ฒฝํด์ฃผ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ฒ ๋์๋ค. CurrentValueSubject๋ PassthroughSubject์ ๋ฌ๋ฆฌ ๊ฐ์ฅ ์ต๊ทผ์ publish๋ element์ ๋ฒํผ๋ฅผ ์ ์งํ๋ค.
ํ๋ก์ ํธ ์๊ตฌ์ฌํญ์๋ ํ๋ฉด์ ์์ ๋กญ๊ฒ ๊ตฌ์ฑ ๋ฐ ๋ฐฐ์นํ๋ผ๊ณ ๋์ด์์ด์ ๋ง๋งํ๋ค. ๊ทธ๋์ ํจ๊ป ํ๋ก์ ํธ ์๊ตฌ์ฌํญ์ ์ฒดํฌํด์ ๊ตฌํํด์ผํ๋ ๊ธฐ๋ฅ ๋ช ์ธ๋ฅผ ์ ๋ฆฌํ๊ณ , ํ์ฌ ์ฑ๋ค์ ์ฐธ๊ณ ํด๊ฐ๋ฉฐ Figma๋ฅผ ์ฌ์ฉํด์ ์ด์คํ์ง๋ง[?] ์ต๋ํ ํ ์ ์๋ ๋งํผ ์ค๋ ฅ์ ๋ฐํํด์ UI ๋ฐฐ์น๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ๋ง๋ฌด๋ฆฌํ๋ค. ๊ณ ๋ฏผํ๋ ๋ถ๋ถ์...
- ๋ณ์ ์ ์ด๋ค ๋ฐฉ์์ผ๋ก ๋ฑ๋กํ๋ ๊ฒ์ด ํธํ ๊น?
- ๋ฐ์ค์คํผ์ค ์์๋ฅผ ๋์ ๊ฐ๋ ์ฑ์ผ๋ก ๊ตฌ์ฑํ๊ณ ์ถ๋ค.
- ํ๋กํ ์ฌ์ง์ ๊ฒฝ์ฐ, ์ด๋ค ๋์์ผ๋ก ๋ฑ๋ก์ ํ ์ ์๋๋ก ํ๋ฉด ์ข์๊น?
๊ณ ๋ฏผํ๋ ์ํฉ์ ์์ฑ๋์ด ์๋ ๋ฆฌ๋ทฐ๋ฅผ ์ญ์ ํ๋ ๋ฒํผ์ ๋๋ฅด๋ฉด ๊ทธ ๋ฆฌ๋ทฐ๋ฅผ ์์ฑํ ์ฌ๋์ด ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํ๊ณ , ๊ทธ ๋น๋ฐ๋ฒํธ๊ฐ ์ ์ฅ๋์ด์๋ ๋น๋ฐ๋ฒํธ์ ์ผ์นํ๋ฉด ์ญ์ ๊ฐ ๋๊ณ , ์ผ์นํ์ง ์์ผ๋ฉด ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์๋๋ค๋ ์๋ฆผ์ด ๋จ๋๋ก ํด์ผํ๋ค.
๋งจ ์ฒ์์ ์๊ฐํ ๋ฐฉ์์, ๋ทฐ์์ ๋ทฐ๋ชจ๋ธ๋ก ํด๋น ์
์ index๋ฅผ ์ ๋ฌํด์ฃผ๋ฉด ๋ทฐ๋ชจ๋ธ์์ ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ๋์ง ํ์ธํ์ฌ UIAlertController
๊น์ง ์์ฑํ์ฌ ๋ทฐ๋ก ์ ๋ฌํด์ฃผ๋๋ก ํ๋ ๋ฐฉ์์ด์๋ค.
ํ์ง๋ง ๋ทฐ๋ชจ๋ธ์์ UIAlertController๋ฅผ ์์ฑํ์ฌ ์ ๋ฌํด์ฃผ๋ ค๊ณ ํ๋ UIKit์ importํด์ผํ๊ณ , ๋ ๋ทฐ๋ชจ๋ธ์ ์ญํ ๊ณผ ๋ง์ง ์๋ค๋ ๊ฒ์ ์๊ฒ๋์๋ค. ๋ฐ๋ผ์ ์๋์ ๊ฐ์ ์์๋ก ๋ทฐ์ ๋ทฐ๋ชจ๋ธ์ ์ญํ ์ ๋๋์ด ๊ตฌํํด์ฃผ์๋ค.
view
: ์ผ๋ฟ์ ๋์์ ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅ๋ฐ๋๋ค.view
: ๋น๋ฐ๋ฒํธ ์ ๋ ฅ ํ ์ฌ์ฉ์๊ฐ ํ์ธ ๋ฒํผ์ ๋๋ฅด๋ฉด ํด๋น ๋น๋ฐ๋ฒํธ๋ฅผ viewModel์๊ฒ ์ ๋ฌํด์ค๋ค.viewModel
: ์ ๋ฌ๋ฐ์ ๋น๋ฐ๋ฒํธ๋ฅผ ๊ฒ์ฌํ๊ณ , ๋น๋ฐ๋ฒํธ๊ฐ ์ ์์ด๋ผ๋ฉด ๋ฆฌ๋ทฐ๋ฅผ ์ ๊ฑฐํ๊ณ , ๊ทธ๋ ์ง ์๋ค๋ฉด ์๋ฌ๋ฉ์ธ์ง๋ฅผ view์๊ฒ ์ ๋ฌํ๋ค.view
: ์๋ฌ๋ฉ์ธ์ง๋ฅผ ์ ๋ฌ๋ฐ๋๋ค๋ฉด ์๋ฌ ์ผ๋ฟ์ ๋์ด๋ค.
// ViewModel
var _errorMessage = CurrentValueSubject<String?, Never>(nil)
var errorMessage: AnyPublisher<String?, Never> { return _errorMessage.eraseToAnyPublisher() }
func checkPassword(_ inputPassword: String, index: Int) {
// ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์๋ ์ํฉ
_errorMessage.send("๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์์ต๋๋ค.") // _errorMessage์ string๊ฐ ๋ณด๋
}
// ViewController
func bind() {
viewModel.output.errorMessage // ๋ทฐ๋ชจ๋ธ์ errorMessage๋ฅผ ๊ตฌ๋
.compactMap { $0 }
.sinkOnMainThread(receiveValue: { [weak self] message in
self?.showAlert(message: message)
})
.store(in: &cancelable)
}
์์ฝํ๋ฉด, _errorMessage
์๊ฒ ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์๋๋ค๋ string๊ฐ์ send
ํ๋ฉด _errorMessage๋ฅผ AnyPublisher๋ก ๋ฐ๊ฟ์ฃผ๋ errorMessage
๊ฐ ๊ฐ์ ๊ฐ์ง๊ณ ์๊ฒ ๋๋ค. ๋ฐ๋ผ์ ๋ทฐ์์๋ ๊ฐ์ ๊ฐ์ง๊ณ ์๋ errorMessage
๋ฅผ sink
(๊ตฌ๋
)ํ์ฌ ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ฒ๋ฆฌํด์ค ๋ก์ง์ ๋ด๋ถ์ ์์ฑํด๋๋ฉด ๋๋ ๊ฒ์ด๋ค.
๋ฆฌ๋ทฐ๋ฅผ ์ญ์ ํ๋ ๋ฒํผ์ ๋๋ ์ ๋ ํด๋น ์ ์ ์์น๋ฅผ ์์์ผ ๋ฆฌ๋ทฐ๋ฅผ ๋ด๊ณ ์๋ ๋ฐ์ดํฐ์ ์์น ๋ํ ์ ์ ์๊ธฐ ๋๋ฌธ์ index๋ฅผ ์์์ผํ๋ค. ์ฐพ์๋ณด๋ ์ ์ ์์น๋ฅผ ์ ์ ์๋ ๋ฐฉ๋ฒ์ผ๋ก UIGestureRecognizer๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ delegate๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ ๋ฑ๋ฑ ์ฌ๋ฌ ๋ฐฉ๋ฒ์ด ์์๋๋ฐ ์ฐ๋ฆฌ๋ ์๋์ ๊ฐ์ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ค.
@objc func deleteButtonDidTap(_ sender: UIButton) {
let buttonPosition: CGPoint = sender.convert(CGPoint.zero, to:self.tableView)
let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
}
deleteButtonDidTap ๋ฉ์๋ ๋ด๋ถ์์ ๋ฒํผ์ ์์น๋ฅผ ์ก๊ณ tableView์ ์ขํ๋ก ๋ณํํ ๋ค์ ํด๋น ์ขํ์์ ํ์ indexPath๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์๋ค.