GrowingTalk
๐๏ธํ์
์ ํตํ ์ฑ์ฅ ๊ณต๊ฐ, GrowingTalk
- ์ด๋ฉ์ผ ๊ธฐ๋ฐ ํ์ ์ธ์ฆ
- ์ํฌ์คํ์ด์ค(๋ฉ์ธ ํ๋ฉด) ๋ด ์ฑํ
๋ฐฉ ๊ฐ์ค ๊ธฐ๋ฅ ๋ฐ ํ์ ์ด๋ ๊ธฐ๋ฅ
- ์ค์๊ฐ ์ฑํ
๊ธฐ๋ฅ ๋ฐ ์๋ก์ด ์ฑํ
๋์ฐฉ์ ์๋ฆผ ๊ธฐ๋ฅ
- ๊ฒฐ์ ์์คํ
๊ธฐ๋ฅ ํฌํจ
๋ ์์ธํ ์คํฌ๋ฆฐ ์ท ๋ณด๊ธฐ
----------
**๐ํต์ฌ ๊ธฐ์ **
- [`RxSwift`](https://github.com/ReactiveX/RxSwift) ๊ธฐ๋ฐ MVVM ํจํด ์ ์ฉ์ ํตํด ๋น์ฆ๋์ค ๋ก์ง ๋ถ๋ฆฌ ๋ฐ ๋ฐ์ํ ํ๋ก๊ทธ๋๋ฐ ๊ตฌํ
- [`Realm`](https://github.com/realm/realm-swift.git) DataBase์ ๊ธฐ์กด ์ฑํ
๋ฐ์ดํฐ๋ฅผ ์ฐ๊ธฐ/์ฝ๊ธฐํ์ฌ ๋คํธ์ํฌ Reqeust ์ต์ํ
- [`SocketIO`](https://github.com/socketio/socket.io-client-swift.git)๋ฅผ ํ์ฉํ Socket ํต์ ๊ธฐ๋ฐ ์ค์๊ฐ ์ฑํ
๊ตฌํ
- Payment Gateway(PG)๋ฅผ ํตํ ์ ์ฉ ์นด๋ ๊ฒฐ์ ์ง์
- `Modern CollectionView`ย +ย `DiffableDataSource`๋ฅผ ํ์ฉํ Expandable List ๊ตฌํ
- `Async/Await`๋ฅผ ๋์
ํย Realm Transaction ๋น๋๊ธฐ ์คํ ๊ตฌํ
- `Rx operator`๋ฅผ ํ์ฉํ `JWT๊ธฐ๋ฐ AccessToken, Refresh Token ๊ฐฑ์ ๋ก์ง` ๊ตฌํ
- `UIGraphicsImageRenderer`๋ฅผ ํตํย ์ด๋ฏธ์ง Rendering resizing
- ๊ณตํต UI Component ๋ถ๋ฆฌ ๋ฐ ์บก์ํ ๊ตฌํ์ ํตํ ์ฌ ์ฌ์ฉ์ฑ ํฅ์
## ๐ ๏ธ๊ฐ๋ฐ
***๐๊ฐ๋ฐ ํ๊ฒฝ***
> ๊ฐ๋ฐ ๊ธฐ๊ฐ: 2024.01.02. ~ 03.01.
> ๊ฐ๋ฐ ์ธ์: 1์ธ
> ๊ฐ๋ฐ ์ธ์ด: Swift
> Minimum Deployment: iOS 16.0+: `UISheetPresentationController.Detent.custom`
---------
***โ๏ธ๊ธฐ์ ์คํ***
- **BaseSDK**: `UIKit`
- **Pattern**: `MVVM`, `Singleton`, `Input-Output Pattern`
- **Reactive Programming**: `RxSwift`
- **Package Management**: `SPM`, `CocoaPods`
- **CodeBaseUI**: `PHPickerViewController`, `SnapKit`, `Then`, `Toast`
- **Database**: `RealmSwift`
- **Network**: `Moya`, `SocketIO`, `Kingfisher`
- **Management**: `FireBase Cloud Messaging`
## ๐ฅ๊ฐ๋ฐ Point
### ์ฌ์ด๋ ๋ฐ ๊ตฌํ
- ์ฌ์ด๋ ๋ฐ๋ฅผ ๊ตฌํํ๊ธฐ ์ํด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ ์์ผ๋, ๋ณดํธ์ ์ธ ์ฌ์ด๋ ๋ฐ ๊ตฌํ **๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ง์์ด ๋๊ธด์ง ์ค๋ ๋์์**์ ๋ฐ๋ผ ์ฌ์ด๋ ๋ฐ **์ง์ ๊ตฌํ**์ ์ ํ.
- `UIView.animate()`๋ฅผ ํตํด ViewWillAppear ์์ ๊ณผ viewWillDisappear ์์ ์์์ ์ ๋๋ฉ์ด์
์ ๊ตฌํ.
- `UIPanGestureRecognizer`๋ฅผ ํตํด ๋ทฐ์ `Animate`๋ฅผ ์ ์ฉํ๊ณ **View์ dismiss๋ฅผ ๊ฒฐ์ **ํ ์ ์๋ค.
### ์ฑํ
๋ก์ง
- ์๋ฒ์์ ์ฑํ
๋ด์ญ์ ๋ํ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ๋, ๋ชจ๋ ์ฑํ
๋ด์ญ์ ๋ฐ๊ฒ๋๋ฉด ์ฑํ
์ ํ๋ฉด ํ ์๋ก ์๋ฒ ๋ฐ ํต์ ์ ๋ํด์ ๋น์ฉ์ด ๋๋ฌด ์ปค์ง๊ฒ ๋๋ค.
- ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์๋ฒ๋ก๋ถํฐ ์ด๋ฏธ ๋ฐ์ ์ฑํ
๋ด์ญ์ ๋ํด์๋ ๋ก์ปฌ์ ์ ์ฅํ์ฌ CRDํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ ๋ค, ๋ก์ปฌ์์์ ๋ง์ง๋ง ์ฑํ
์ ๊ธฐ์ค์ผ๋ก ๊ทธ ์ดํ ์ฑํ
๋ด์ญ์ ๋ฐ๋ ๊ฒ์ผ๋ก ๋น์ฉ์ ์ ๊ฐํ ์ ์๋ค.
- ๋ก์ปฌ DB์ ์ ์ฅ๋์ด ์๋ ์ฑํ
๋ด์ญ, ์๋ฒ ํต์ ์ ํตํด ์ฑํ
๋ด์ญ์ ๋ฐ์์ค๊ณ ๋๋ฉด `Socket`์ ํตํด ์ค์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ฑํ
์ ๊ตฌํํ๋ค.
## โ Trouble Shooting
### ์ฌ์ด๋๋ฐ์ constraints + animate ๋ฌธ์ : (`Main event loop`์ ์ดํด)
|๋ฌธ์ ์ํฉ|์ ์|
|:--:|:--:|
|
|
|
#### ๋ฌธ์ ์
- ์ฌ์ด๋ ๋ฐ์ ๋ฑ์ฅ ์ ๋๋ฉ์ด์
ํจ๊ณผ๋ฅผ ์ ์ฉํ๊ธฐ ์ํด ์ฌ์ด๋ ๋ฐ์ View ์ด๊ธฐ ์์น๋ฅผ ๋๋น๋งํผ ํ์ฌ View๋ก๋ถํฐ ์์ ๋ฐฉํฅ์ผ๋ก Constraints๋ฅผ viewDidLoad์์ ์ ์ค์ ํ ๋ค์, ViewWillAppear ์์ ์ Constraints๋ฅผ ํ์ฌ View๋ก ๋ง์ถฐ์ฃผ์ด UIView.animate() ๋ฉ์๋๋ฅผ ์คํํ์ผ๋, ๋ทฐ์ ์ ๋๋ฉ์ด์
์ด **X ์ขํ ๋ฟ๋ง ์๋๋ผ Y์ขํ๋ ๊ฐ์ด Animation์ด ์คํ๋๋ ๋ฌธ์ ์ **์ด ๋ฐ์
```Swift
private func sideBarAppearAnimation() {
self.sideBarView.snp.updateConstraints { make in
make.leading.equalTo(self.view)
}
UIView.animate(withDuration: 0.5, delay: 0) {
self.view.layoutIfNeeded()
}
}
```
#### ์์ธ ๋ฐ ํด๊ฒฐ
- ๋๋ฒ๊น
์ ์งํํ์ ๋, viewWillAppear์์ ์ ๊น์ง ์ฌ์ด๋๋ฐ View์ ์ด๊ธฐ ํฌ๊ธฐ ๋ฐ ์์น๊ฐ ๋ชจ๋ ์ ํด์ง์ง ์๋ ์ํ์์ ํ์ธ
- `Main event loop`์ ๊ฐ๋
์ด ํ์ํจ.
- ๋ฌด์์ Constraints๋ฅผ ์ค์ ํ๋ค๊ณ ํด์ ๋ฐ๋ก View์ Constraints๊ฐ ์ ์ฉ๋์ด ๋ทฐ์ ์์น์ ํฌ๊ธฐ๊ฐ ๊ฒฐ์ ๋๋ ๊ฒ์ด ์๋.
- `Main run loop`์ ์์ ์ด ๋์๋์ด์ผ ๋น๋ก์ ์ค์ง์ Constraints๊ฐ ์ ์ฉ๋์ด ๋ทฐ์ ์์น์ ํฌ๊ธฐ๊ฐ ๊ฒฐ์ ๋จ.
- UIView.animate()๋ Scope๋ด์์์ View ๋ณ๊ฒฝ์ฌํญ์ ๊ทธ ์ด์ ๊ณผ ๋น๊ตํ์ฌ ์ ๋๋ฉ์ด์
์ ์คํํ๋ ๊ตฌ์กฐ๋ก ๋์ํจ.
- ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ViewDidLoad() ์คํ ์์ ๊ณผ ViewWillAppear()๊ฐ ์คํ๋๋ ์์ ์ ์ฐจ์ด๊ฐ ๊ต์ฅํ ์งง์ ๊ฒฝ์ฐ, ์ค์ง์ ์ธ ์ด๊ธฐ Constraints๊ฐ ์ ์ฉ๋๊ธฐ ์ ์ Constratints๊ฐ ๋ฎ์ด์ฐ๊ธฐ ๋์ด ์ขํ(0, 0)๊ณผ Frame(0, 0)์ ์ํ์์ ์ต์ข
์ ๋๋ฉ์ด์
์ด ์คํ๋๋ ๊ฒ์ด๊ธฐ์ ์ด๋ฌํ ๋ฌธ์ ๊ฐ ๋ฐ์.
- Constraints๊ฐ ๋ฎ์ด์ฐ๊ธฐ ๋๊ธฐ ์ ์ `Main run loop`๋ฅผ ์์๋ก ๋์์์ผ ์ด๊ธฐ ๋ทฐ๋ฅผ ์ค์ ํด ์ค ๋ค์, Constraints๋ฅผ ๋ฐ๊พธ์ด animate๋ฅผ ์คํํ๋ฉด ํด๋น ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋จ.
```Swift
private func sideBarAppearAnimation() {
self.view.layoutIfNeeded() //AutoLayout์ ํตํด ๋ทฐ์ ์ด๊ธฐ ์์น์ ํฌ๊ธฐ๋ฅผ ์ก์๊ธฐ์ ์ ๋๋ฉ์ด์
์ ํด๋น ๋ฉ์๋ ์คํ -> ๋ทฐ๊ฐ ์ค์ ๋ก ๋ณด์ฌ์ง๊ธฐ ์ ๊น์ง ์ด๊ธฐ AutoLayout์ ์คํ๋์ง ์์.
sideBarView.snp.updateConstraints { make in
make.leading.equalTo(self.view)
}
UIView.animate(withDuration: 0.5, delay: 0) {
self.view.layoutIfNeeded()
}
}
```
### ๋ค๋น๊ฒ์ด์
๋ฐ์ UIBarButtonItem์ ํฌ๊ธฐ๊ฐ ์กฐ์ ๋์ง ์๋ ๋ฌธ์
|๋ฌธ์ ์ํฉ|์ ์|
|:--:|:--:|
|
|
|
- Left Bar Button Item์ ๊ธฐํ ๋ฐ ๋์์ธ์ ๋ง์ถ์ด ๋ฒํผ ํฌ๊ธฐ์ ์ค์ ์ด ํ์ํจ.
```Swift
let workSpaceImageButton = UIButton().then { view in
view.frame = CGRect(origin: .zero, size: CGSize(width: 30, height: 30))
let defaultImage = UIImage(named: "WorkSpace")
view.setBackgroundImage(defaultImage, for: .normal)
view.backgroundColor = .clear
view.layer.cornerRadius = 8
view.clipsToBounds = true
view.contentMode = .scaleAspectFit
}
lazy var workSpaceImageBarButton = UIBarButtonItem(customView: workSpaceImageButton)
...
navigationItem.setLeftBarButton(workSpaceImageBarButton, animated: true)
- ์ด ์ํฉ์์ ๋ฒํผ์ ์ฌ์ด์ฆ๋ฅผ **Constraints ํน์ Frame์ผ๋ก ํฌ๊ธฐ๋ฅผ ์ค์ ํด ์ฃผ์ด๋ ์ง์ ํ ์ฌ์ด์ฆ๋๋ก ๊ตฌํ๋์ง ์๋ ๋ฌธ์ ** ๋ฐ์.
- `UIButton`์์ image๋ฅผ ์ค์ ํ๋ ๊ฒฝ์ฐ, **์ค์ ํ image์ ํฌ๊ธฐ์ ๋ฐ๋ผ button๋ด ImageView์ ํฌ๊ธฐ๊ฐ ๊ฒฐ์ ๋๊ณ button์ ํด๋น imageView์ ํฌ๊ธฐ๋ณด๋ค ์๊ฒ ์ค์ ๋ ์ ์๊ธฐ ๋๋ฌธ์ ํด๋น ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ ํ์ธ.**
- button ๋ด image๋ฅผ ์ค์ ํ๊ณ ์ถ์ button์ ์ฌ์ด์ฆ๋ณด๋ค ์๊ฒ resizingํ์ฌ button์ ์ฌ์ด์ฆ๋ฅผ ์ค์ ํด์ฃผ๋ฉด ์ ์์ ์ผ๋ก ์ฌ์ด์ฆ ์กฐ์ ์ด ๊ฐ๋ฅ.
```Swift
let workSpaceImageButton = UIButton().then { view in
view.frame = CGRect(origin: .zero, size: CGSize(width: 30, height: 30))
let defaultImage = UIImage(named: "WorkSpace")?.resizingByRenderer(size: CGSize(width: 30, height: 30), tintColor: .BackgroundColor.backgroundPrimaryColor)
view.setBackgroundImage(defaultImage, for: .normal)
view.backgroundColor = .clear
view.layer.cornerRadius = 8
view.clipsToBounds = true
view.contentMode = .scaleAspectFit
}
lazy var workSpaceImageBarButton = UIBarButtonItem(customView: workSpaceImageButton)
...
navigationItem.setLeftBarButton(workSpaceImageBarButton, animated: true)
## ๐ํ๊ณ
- ์ต์ด๋ก PG์ฌ์ SDK๋ฅผ ํตํด ๊ฒฐ์ ๋ฅผ ๋ฌ ์ ์์ด, **๊ฒฐ์ ์์คํ
๊ตฌํ์ ๋ํ ๋๋ ค์์ด ํด์**๋์๋ค.
- ์ด๊ฑฐํ์ RawValue๋ก ์ด๊ธฐํ ํด์ผํ๋ ์ํฉ์์ ์ถ์ํ๋ฅผ ํ๊ธฐ ์ํด ๋ง์ ๊ณ ๋ฏผ์ ๋์ `RawValue Protocol`์ ์๊ฒ๋์๊ณ ์ด๋ฅผ ํตํด NetworkError์ ๊ดํด์ ์ถ์ํํ์ฌ Generic์ฌ์ฉ์ด ๊ฐ๋ฅํ๊ฒ ํ์ฌ ์ฌ์ฌ์ฉ์ฑ์ ๋์ผ ์ ์๊ฒ ๋์๋ค.
- Moya์ TargetType(Router Pattern)์ **DI๋ฅผ ํตํด ๋ถ๋ฆฌ**๋ฅผ ํ๋ค๋ฉด ์ ์ง๋ณด์์ฑ์ด ์ข๊ณ ๊ฐ๊ฒฐํ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ ๊ฒ ๊ฐ์ง๋ง ์ค์ ๋ก ์ ์ฉํ์ง ๋ชปํด ์์ฝ๋ค.