๋ณธ ํ๋ก์ ํธ๋ ๋น์ธ iOS ์ฝ์ค ํ ํฌ ์บ ํ 1๊ธฐ ๊ณผ์ ๋ก ์งํ๋์์ต๋๋ค.
ํ๋ก์ ํธ ๊ธฐ๊ฐ : 2022.02.21~2022.03.13 (3์ฃผ)
Fakethumb_video.mp4
์ฝฉ์ด
ํ๋ก์ ํธ ์ธํ (REST API, Github Action)
- URL Session
: async/await์ ๊ฒฝํํด๋ณด๊ธฐ ์ํด ๋ณ๋์ ๋คํธ์ํฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ง ์๊ธฐ๋ก ๊ฒฐ์ ํ๋ค. โ await๋ฅผ ์ฐ๋ ์ด์ ๋ completionhandler๋ฅผ ์ฌ์ฉํ์ง ์์์ผ๋ก์จ ์ค๋ฅ ์ฒ๋ฆฌ์ ๋ณต์ก์ฑ์ ์ค์ด๊ณ , ํด๋ก์ ๋ฅผ ์ฌ์ฉํ์ง ์์์ผ๋ก์จ ์ฝ๋ ๊ฐ๋ ์ฑ์ ํฅ์ํจ์ผ๋ก์จ ํ ์ค๋ก ๋น๋๊ธฐ ์ฝ๋๋ฅผ ๋ง์น ๋๊ธฐ ์ฝ๋์ฒ๋ผ ๋ณด์ด๊ฒ ํ๊ธฐ ์ํ ๊ฒ.
await/async๋ iOS 13.0๋ถํฐ ์ง์ํ์ง๋ง, async๋ฅผ ์ง์ํ๋ URLSession API๋ iOS 15.0์ด ํ์ํ๋ค.
โ ๊ทธ๋ฐ๋ฐ ์ด์ ๋ฒ์ ๋ถํฐ ๋์ํ๊ฒ ๋๋ฉด ์ฝ๋ ๊ฐ๋ ์ฑ์ ์ฅ์ ์ด ์ฌ๋ผ์ ธ์ try await์ ์ฌ์ฉํ๋ ์๋ฏธ๊ฐ ์์ด์ง๊ฒ ๋๋ค.
โ ๋ฐ๋ผ์ Global ์ฝ๋๋ ๋ค๋ฅธ ์ฝ๋์์ ์ฌ์ฌ์ฉ๋๋ ์ฝ๋๋ค์ ๋ชจ์๋์ ํด๋์ธ ๋งํผ, ๊ฐ๊ฒฐํ๊ณ ๊น๋ํ๊ฒ ์์ฑํ๊ณ ์ถ๊ธฐ์ iOS 15๋ถํฐ ๋์ํ๋๋ก ์ค์ ํ๋ค.
- CI
: ์ฌ์ ์ ๋น๋ ์ค๋ฅ๋ฅผ ๋ฐ๊ฒฌํ์ง ๋ชปํ ์ฑ main ๋ธ๋์น์ mergeํ๋ ์ํฉ์ ๋ฐฉ์งํ๊ธฐ ์ํด CI ํด์ ์ฑํํ๊ธฐ๋ก ํ๋ค.
โ Github Action๊ณผ Jenkins๊ฐ ๊ฐ์ฅ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ค.
โ Jenkins์ ๊ฒฝ์ฐ ๋ณ๋์ ์๋ฒ์ ํธ์คํ ํด์ผํ๋ ์ค๋ฒํค๋๊ฐ ๋ฐ์ํ๋ฏ๋ก, Github์์ ์๋ฒ๋ฅผ ์ ๊ณตํ๋ Github Action์ ์ฌ์ฉ
name: FakethumbBuildTest
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Run tests
run: |
pod install --repo-update --clean-install --project-directory=FakeBithumbAssignment/
xcodebuild test -workspace ./FakeBithumbAssignment/FakeBithumbAssignment.xcworkspace -scheme FakeBithumbAssignment -destination 'platform=iOS Simulator,name=iPhone 11 Pro,OS=15.2'
CoinView ๋ฐ ํธ๊ฐ ์ ๋ณด์ฐฝ ๊ตฌํ
- CoinView - UIPageViewController
: ๋ทฐ ์ปจํธ๋กค๋ฌ ๋ด์์ ๋ ๋ค๋ฅธ ๋ทฐ ์ปจํธ๋กค๋ฌ๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด pageViewController๋ฅผ ์ฌ์ฉํ๋ค.
โ ๋ถ๋ชจ ๋ทฐ ์ปจํธ๋กค๋ฌ ๋ด์ pageViewController๊ฐ ์กด์ฌํ๊ณ , pageViewController ์์ ์ํ๋ child View Controller๋ฅผ ํ์ํ๋ ๋ฐฉ์์ผ๋ก ๋์ํ๋ค.
โ pageViewController๋ childViewController๋ค์ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, delegate์ dataSource๋ฅผ ํตํด ํ์ด์ง๋ฅผ ๊ด๋ฆฌํ๋ค.
// pageViewController๋ฅผ ์ค์
private func setPageView() {
self.pageViewController = CoinPagingViewController()
if let pageViewController = pageViewController {
self.addChild(pageViewController)
self.pageView.addSubview(pageViewController.view)
self.pageViewController?.view.snp.makeConstraints { make in
make.top.leading.trailing.bottom.equalTo(self.pageView)
}
pageViewController.didMove(toParent: self)
}
}
โ ๋ถ๋ชจ ๋ทฐ ์ปจํธ๋กค๋ฌ์ addChild๋ฅผ ์ด์ฉํด pageViewController๋ฅผ ๋ถ์ฌ์ค ํ, ๋ณด์ฌ์ง pageView์๋ pageViewController์ view๋ฅผ addSubViewํ๋ค.
โ ๋ถ๋ชจ ๋ทฐ ์ปจํธ๋กค๋ฌ์ ์๋ก์ด ์ปจํธ๋กค๋ฌ๊ฐ ์ถ๊ฐ๋์์ผ๋ฏ๋ก didMove()๋ฅผ ํธ์ถํด์ค๋ค.
// ํ์ด์ง๋ฅผ ์ ํํ๋ ์ฝ๋
func setTabViewController(to type: TabView) {
guard let page = pages[type] else { return }
self.setViewControllers([page], direction: .forward, animated: false, completion: nil)
}
โ ์ดํ pageViewController ๋ด๋ถ์์ setViewController ๋ฉ์๋๋ฅผ ์ด์ฉํด ํ๋ฉด ์ ํํ๋ ์ฝ๋๋ฅผ ๊ตฌํํ๋ค.
- ํธ๊ฐ ์ ๋ณด์ฐฝ - ์น์์ผ (๋ชจ๋ชจ ๋ฆฌํฉํ ๋ง ์ด์ )
2-1. ๋ณ๊ฒฝ๋ ํธ๊ฐ์ quantity๊ฐ 0์ธ ๊ฒฝ์ฐ, ํ์ฌ ๋ฆฌ์คํธ์์ ํด๋น price๋ฅผ ์ฐพ์ ์ญ์ ํด์ค์ผ ํ๋ค.
```swift
if Double(quote.quantity) == 0 {
self.removeQuantityIsZero(type: .ask, data: quote)
continue
}
private func removeQuantityIsZero(type: BTSocketAPIResponse.OrderBookResponse.Content.OrderBook.OrderType,
data: Quote) {
switch type {
case .ask:
var count = self.asksList.count
var index = 0
while(index < count) {
if Int(asksList[index].price) == Int(data.price) {
self.asksList.remove(at: index)
count -= 1
}
index += 1
}
case .bid:
var count = self.bidsList.count
var index = 0
while(index < count) {
if Int(bidsList[index].price) == Int(data.price) {
self.bidsList.remove(at: index)
count -= 1
}
index += 1
}
}
}
```
2-2. ๋จ์ํ ํธ๊ฐ ์ ๋ณด 30๊ฐ์ฉ ๋ฐ์์ ํ์ํด์ค ๊ฒฝ์ฐ, (1) ์ฒ๋ผ remove๋ ๊ฒฝ์ฐ์ ํ์ํด์ค ๋ฐ์ดํฐ๊ฐ ์๋ค.
-> ๊ทธ๋ฌ๋ ๋น์ธ์์ ๋ฐ์์ฌ ์ ์๋ ์ต๋ ๋ฐ์ดํฐ๋ 30๊ฐ์ด๋ฏ๋ก, ๊ทธ ์ด์์ ๋ฐ์ดํฐ๋ฅผ ๋ก์ปฌ์์ ๊ด๋ฆฌํ๊ณ ๊ทธ ์ค์์ 30๊ฐ๋ง ํ์ํ๋๋ก ํด์ผํ๋ค.
-> ๊ทธ๋ฆฌ๊ณ ๋ฆฌ์คํธ ์ค asks(๋งค๋)๋ ํ์ 30๊ฐ, bids(๋งค์)๋ ์์ 30๊ฐ๋ฅผ ํ์ํ๋ค. ๋ฌผ๋ก price ์์ผ๋ก ์ ๋ ฌํด์.
```swift
private func sortQuoteList(type: BTSocketAPIResponse.OrderBookResponse.Content.OrderBook.OrderType) {
switch type {
case .ask:
self.asksList = asksList.sorted(by: {$0.price > $1.price})
case .bid:
self.bidsList = bidsList.sorted(by: {$0.price > $1.price})
}
}
```
2-3. ๋ณ๊ฒฝ๋ ํธ๊ฐ๊ฐ ๋ก์ปฌ ๋ฆฌ์คํธ์ ์๋ ํธ๊ฐ์ ๊ฐ์ ๊ฒฝ์ฐ, ํด๋น ํธ๊ฐ๋ฅผ ์ฐพ์ quantity๋ฅผ ์ ๋ฐ์ดํธ ํด์ค๋ค.
```swift
if !self.replaceQuote(type: .ask, data: quote) {
self.asksList.append(quote)
}
private func replaceQuote(type: BTSocketAPIResponse.OrderBookResponse.Content.OrderBook.OrderType,
data: Quote) -> Bool {
switch type {
case .ask:
let count = self.asksList.count
var index = 0
while(index < count) {
if Int(asksList[index].price) == Int(data.price) {
self.asksList[index] = data
return true
}
index += 1
}
case .bid:
let count = self.bidsList.count
for index in 0..<count {
if Int(self.bidsList[index].price) == Int(data.price) {
self.bidsList[index] = data
return true
}
}
}
return false
}
```
2-4. ๋ก์ปฌ์ ์๋ list์์ ์ฐพ์ removeํ๋ ์์ ์ ์ํํ๋ ๋์ค, websocket api์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ list๊ฐ ์ ๋ฐ์ดํธ ๋๋ฉด ๋ฐ๋ณต๋ฌธ์ ๋ฒ์๊ฐ ์กฐ์ ๋์ด index out of range ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
-> ๋ฐ๋ผ์ remove ์์
์ ์ํํ๋ ๋์์๋ ๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ ์ํด sempaphore๋ฅผ ํ์ฉํ๋ค.
```swift
private func updateTransactionData(coin: Coin,
data: BTSocketAPIResponse.TransactionResponse) {
let semaphore = DispatchSemaphore(value: 0)
DispatchQueue.global(qos: .background).async {
for transaction in data.content.list {
switch transaction.buySellGb {
case .sell:
var count = self.asksList.count
var index = 0
while(index < count) {
if Double(self.asksList[index].price) == Double(transaction.contPrice) {
guard let quantity = Double(self.asksList[index].quantity) else { return }
if quantity - transaction.contQty <= 0 {
self.asksList.remove(at: index)
count -= 1
} else {
self.asksList[index].quantity = "\(quantity - transaction.contQty)"
}
}
index += 1
}
case .buy:
var count = self.bidsList.count
var index = 0
while(index < count) {
if Double(self.bidsList[index].price) == Double(transaction.contPrice) {
guard let quantity = Double(self.bidsList[index].quantity) else { return }
if Double(self.bidsList[index].quantity)! - transaction.contQty <= 0 {
self.bidsList.remove(at: index)
count -= 1
} else {
self.bidsList[index].quantity = "\(quantity - transaction.contQty)"
}
}
index += 1
}
}
}
DispatchQueue.main.async {
self.patchOrderbookData()
}
semaphore.signal()
}
semaphore.wait()
}
```
2-5. ๋ง์ง๋ง์ผ๋ก, ๊ฐ๋ ๋งค๋/๋งค์์ ๊ฒฝ๊ณ์ ์ด ์๋๋ผ ์์ฃผ ๋ฎ์ ๊ฐ๊ฒฉ์ ๋งค๋๋ฅผ ํ๊ฑฐ๋, ์์ฃผ ๋์ ๊ฐ๊ฒฉ์ ๋งค์๋ฅผ ํ๋ ์ผ์ด์ค๊ฐ ์กด์ฌํ๋ค.
-> ์ด๋ฐ ๊ฒฝ์ฐ๋ ๋๊ฐ ์ฒด๊ฒฐ์ด ๋์ง๋ง, ์์ฃผ ๊ทน๋จ์ ์ผ๋ก ์๊ฐํด์ ๋งค๋ํ๋ ์ฌ๋๋ง ์๊ณ ๋งค์ํ๋ ์ฌ๋์ ์๋ ๊ฒฝ์ฐ, ๋งค์ํ๋ ์ฌ๋๋ง ์๊ณ ๋งค๋ํ๋ ์ฌ๋๋ง ์๋ ๊ฒฝ์ฐ๋ ์กด์ฌํ ์ ์๋ค. (์๋ฅผ ๋ค์ด.. ์ฃผ์์์ ์ํ๊ฐ, ํํ๊ฐ ์ณค์ ๋)
-> ์ฒด๊ฒฐ ์ ๋ก์ปฌ list์ ์๋ quantity - ์ฒด๊ฒฐ quanity = 0์ธ ๊ฒฝ์ฐ๋ง ํธ๊ฐ์ฐฝ์์ ์ญ์ ํ๋๋ก ํ๋ค.
์ถ๋
๋ฉ์ธํ๋ฉด์ ํค๋๋ทฐ๋ฅผ ๊ตฌํํ ๋, ์ํ, ๊ด์ฌ ๋ฒํผ์ ํฐ์น๋๋ฉด ์๋์ ๋ฐ๊ฐ ์๊ธฐ๊ฒ ๋ฉ๋๋ค. ์ด๋ฅผ ์ปฌ๋ ์ ๋ทฐ์ ๋ก ๊น๋ํ๊ฒ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์ํ, ๊ด์ฌ ๋ฒํผ์ ์ปฌ๋ ์ ๋ทฐ๋ฅผ ํ์ฉํด์ ๊ตฌํํ์์ต๋๋ค. ๋ํ ๋ชจ๋ ์ ์ ๋ํด ์๋์ ์ผ๋ก ๊ฐ๊ฒฉ, ํฌ๊ธฐ ๋ฑ์ ์ง์ ํ ์ ์๊ณ , ์ถํ์ ๋ฒํผ์ด ์ถ๊ฐ๋ ์๋ ์๊ธฐ ๋๋ฌธ์ ์ปฌ๋ ์ ๋ทฐ๋ฅผ ํ์ฉํ์์ต๋๋ค. ํ์ง๋ง ์ปฌ๋ ์ ๋ทฐ๋ ํ ์ด๋ธ ๋ทฐ์ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ง์ ์์ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด ์กด์ฌํ๋ค๊ณ ์๊ฐ์ด ๋ค์์ต๋๋ค. ํ์ฌ์ 2๊ฐ์ ๋ฒํผ์ ์ปฌ๋ ์ ๋ทฐ๋ก ๊ตฌํํ๊ธฐ์๋ ๋ฐ์ดํฐ๊ฐ ์ ๋ค๊ณ ํ๋จ๋์๊ณ , UIButton์ผ๋ก ๊ตฌํํ๊ฒ ๋์์ต๋๋ค.
UI ๊ฐ์ฒด๋ค์ ๋ ์ด์์ํ ๋, ๊ฐ์ฒด๋ฅผ ํ๋ํ๋ ๋ค ์ค์ ํด์ฃผ๋ ๋ฐฉ๋ฒ์ด ์์ ์ ์คํ ๋ ์ด์์ ๊ณต๋ถํ๋ฉฐ ์ต์ํด์ก์ต๋๋ค. ํ์ง๋ง ์คํ๋ทฐ๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด UI ๊ฐ์ฒด๋ค์ ๋ ์ด์์์ ๊ฐ๊ฐ ๊ฑธ์ด์ฃผ์ง ์์๋ ๋๊ณ , ์ฝ๋๊ฐ ๊ฐ๊ฒฐํด์ง๋ฉฐ ๊ฐ๋ ์ฑ์ด ์ข์๋ณด์ธ๋ค ์๊ฐํด ์คํ๋ทฐ๋ฅผ ์ฌ์ฉํ๊ฒ ๋์์ต๋๋ค.
๊ฒ์์ฐฝ์ ์ด์ฉํ๊ฒ ๋๋ฉด ํค๋ณด๋๊ฐ ์๋์ผ๋ก ์ฌ๋ผ์ค๊ณ , ๊ฐ์ ํํ ๋ชฉ๋ก์ ๊ฐ๋ฆฌ๊ฒ ๋ฉ๋๋ค. ๊ฒ์์ฐฝ์ด ์๋ ๋ค๋ฅธ ๋ถ๋ถ์ ํฐ์นํ๊ฒ ๋๋ฉด ํค๋ณด๋๊ฐ ๋ด๋ ค๊ฐ๋ ๋ฐฉ์์ ์์์ ์ผ๋ก ์๊ฐํ๊ฒ ๋์์ต๋๋ค. ํ์ง๋ง ์ด๋ ํ ์ด๋ธ ๋ทฐ ์ ์ ํฐ์นํ๋ ๊ฒ๋ ํฌํจ์ด ๋์ด ํฐ์น๋ ๊ฐ์ ํํ์ ํธ๊ฐ์ ๋ณด ์ฐฝ์ผ๋ก ์ง์ ํ๊ฒ ๋ฉ๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๊ฒ์์ฐฝ์ dismiss ํ๋ ๊ฒ๊ณผ ๋ทฐ์ปจํธ๋กค๋ฌ์ push ์ง์ ์ ๋์์ ๋์ํ ์ ์์์ ์ด์ฉํ์ฌ ๊ฒ์ ๋์ค ์ ์ด ํฐ์น๋์์ ๋ ํค๋ณด๋๋ฅผ ๋ด๋ฆฌ๊ณ , ๋ทฐ์ปจํธ๋กค๋ฌ์ ์ ํ์ด ๋ ์ ์๋๋ก ํ์์ต๋๋ค. ํ์ง๋ง ์ด๋ ๊ผผ์์ ๋ถ๊ณผํ๋ฉฐ, ๊ณ์ํด์ ๊ฒฝ๊ณ ๋ฉ์ธ์ง๊ฐ ๋จ๊ฒ ๋ฉ๋๋ค. ๊ทธ๋์ ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ํค๋ณด๋๋ฅผ ๋ด๋ฆฌ๋ ๋ฐฉ๋ฒ์ ์๊ฐํ๊ณ , ๊ฒ์ ๋์ค ํ ์ด๋ธ ๋ทฐ๋ฅผ ์คํฌ๋กคํ๋ฉด ํค๋ณด๋๊ฐ ๋ด๋ ค๊ฐ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ๊ฒ ๋์์ต๋๋ค.
์ค์๊ฐ ๊ฐ์ ํํ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ํ ์ด๋ธ ๋ทฐ์ ๋ํ๋ด๊ธฐ ์ํด ๋จ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ๋, ํ ์ด๋ธ ๋ทฐ๋ฅผ reload์ํค์๋ ์๊ฐ์ ํ์์ต๋๋ค. ์ ์๊ฐ๊ณผ ๋ค๋ฅด๊ฒ ๊ธฐ๋ณธ์ ์ธ ํ ์ด๋ธ๋ทฐ๋ฅผ ์ฌ์ฉํ์์ ๊ฒฝ์ฐ ๋ง์ ์์ ๋ฐ์ดํฐ๊ฐ ์ฐ์์ ์ผ๋ก ๋ค์ด์ ์ฑ์ด ๋ฉ์ถ๋ ํ์์ด ๋ฐ์ํ์์ต๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด UITableViewDiffableDataSource๋ฅผ ์ฌ์ฉํ๊ฒ ๋์๊ณ , iOS 15๋ถํฐ ๋์ ๋ reconfigureItems ๋ฉ์๋๋ฅผ ํตํด ํ๋ฉด์ ๋๊น ์์ด ํ ์ด๋ธ ๋ทฐ๋ฅผ ์ ๋ฐ์ดํธํ ์ ์์์ต๋๋ค.
๋ชจ๋ ๊ฐ์ ํํ์ ๋ชฉ๋ก ํ ์ด๋ธ๋ทฐ์ ๊ด์ฌ ๋ชฉ๋ก ํ ์ด๋ธ๋ทฐ๋ฅผ ๊ตฌ๋ถํด์ ๋ํ๋ด์ผํ๋๋ฐ ์ด๋ฅผ ํ๋์ ๋ทฐ์ปจํธ๋กค๋ฌ๊ฐ ํ๋์ ํ ์ด๋ธ ๋ทฐ๋ฅผ ๊ด๋ฆฌํ๊ณ , ์ํ, ๊ด์ฌ ํญ์ด ๋๋ฆด ๋๋ง๋ค ๊ทธ์ ๋ง๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ฃผ์๋ ์๊ฐ์ ํ์์ต๋๋ค. ๊ฐ๊ฐ์ ํ ์ด๋ธ ๋ทฐ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๋ก์ง์ ๋น์ทํ๋ค ์๊ฐํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ํ์ง๋ง ์ด๋ ํ๋์ ํ์ผ์ ๋๋ฌด ๋ง์ ์์ ์ฝ๋๋ฅผ ์์ฑํ๊ฒ ํ๊ณ , ํ ์ด๋ธ ๋ทฐ๋ฅผ ์ ์ดํ๋ ๋ก์ง์๋ง์ด ๋ณต์กํ๊ฒ ํ์์ผ๋ฉฐ, ๊ตฌํํ๋ค๋ณด๋ ๋ ๊ฐ์ ํ ์ด๋ธ๋ทฐ๊ฐ ์กฐ๊ธ์ ๋ค๋ฅธ ๋ก์ง์ ํตํด ๋ณด์ฌ์ค ๋ค๋ ๊ฒ์ ๊นจ๋ฌ์์ต๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋จผ์ ํ ์ด๋ธ ๋ทฐ๋ฅผ 2๊ฐ์ ๋ทฐ๋ก ๋ถ๋ฆฌํ์์ต๋๋ค. ์ปจํธ๋กค๋ฌ๋ ํด๋น ํญ์ ๋ํ ๋ทฐ๋ฅผ ๋ณด์ฌ์ฃผ๊ณ , ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ผ๋ฉด ์ ๋ฐ์ดํธํ๋ผ๋ ๋ช ๋ น์ ๋ด๋ฆฌ๊ฒ ํ์์ต๋๋ค. ์ฌ์ค ๋๊ฐ์ ๋ทฐ๋ ๋น์ทํ ๋ก์ง๋ ๋ถ๋ช ์กด์ฌํฉ๋๋ค. ์ด๋ฅผ ํด๋์ค ์์์ ํตํด ๊ธฐ๋ณธ ๊ตฌํํ๊ณ , ์กฐ๊ธ ๋ค๋ฅธ ๋ก์ง์ ๊ฐ์ง ๋ทฐ๋ ์ค๋ฒ๋ผ์ด๋ฉํ์ฌ ๊ตฌํํ๋ฉด ์ฝ๋๊ฐ ๋ ๊น๋ํด์ง ๊ฒ ๊ฐ์ต๋๋ค.
๋ชจ๋ชจ
- ํ์
- ์บ๋ค์คํฑ ์ฐจํธ์์ ์คํฌ๋กค์ด๋ ํ์น ์ ์ค์ฒ๊ฐ ์์ ๊ฒฝ์ฐ, ํ๋ฉด ์์ ๋ค์ด์จ ์บ๋ค์คํฑ์ ๋์์ผ๋ก y์ถ ์ค์ผ์ผ๋ง์ด ๋๋๋ก ํ๊ณ ์ถ์์ผ๋, ์ด๋ฌํ ์ํ ๋ณํ๊ฐ ์์์ ๊ฒฝ์ฐ ์ฑ์ด ๊บผ์ง์ง ์๊ณ ๋ฉ์ถ ์ํ๋ก ์ง์๋๊ฑฐ๋, ํ๋ฉด ์ ํ์ด ๋๊ธฐ๋ ํ์์ด ์์์ต๋๋ค. (CPU ์ฌ์ฉ๋ฅ 100%)
- ์์ธ
- ์บ๋ค์คํฑ ์ฐจํธ๋ CALayer๋ค์ ์กฐํฉ์ผ๋ก ๊ตฌ์ฑ ๋์์ต๋๋ค. ๊ธธ์ด ์กฐ์ ๋ฑ์ด ํ์ํ ๊ฒฝ์ฐ์ ์ด ์ ์ฒด Layer๋ค์ ๋ค ์ง์ ์ค ๋ค์ ๋ค์ ์ถ๊ฐํด ํ๋ฉด์ ๊ทธ๋ ค์ฃผ๋ ์ฐ์ฐ์ด ํ์ํฉ๋๋ค. ๋งค ์ด๋ฒคํธ(์คํฌ๋กค, ํ์น) ๋ง๋ค ๋ชจ๋ ์บ๋ค์คํฑ์ ๋ํด ์ด๋ฌํ ์ฒ์๋ถํฐ ๋ค์ ๊ทธ๋ ค์ฃผ๋ ์ฐ์ฐ์ด ๋ฐ๋ณต๋๊ณ ์์ด ์ด๋ฌํ ๋ฐฉ๋ํ ์ฐ์ฐ์ด ์์ธ์ด์์ต๋๋ค.
- ํด๊ฒฐ
- ๊ทธ๋ฆฌ๊ธฐ ์ฐ์ฐ์ ์ต์ํ ํด CPU ๋ถํ๋ฅผ ๋๋ ๋ฐฉ๋ฒ์ผ๋ก ํด๊ฒฐ ํ์ต๋๋ค. ๋ชจ๋ ๋ฉ์๋๊ฐ O(N)์ ์๊ฐ ๋ณต์ก๋๋ฅผ ๊ฐ๊ณ ์์ด 2์ฐจ์์ ๋ ์ด์ด ์ฐ์ฐ์ ํฐ ๋ถ๋ด์ด ๋์ง ์์ ๊ฒ์ด๋ผ ์๊ฐํ์ง๋ง ์ด๋ฒคํธ๋ง๋ค 5000๊ฐ์ ๋์ ๋ ์ด์ด ์ญ์ ํ ์ถ๊ฐ๋ ์๋นํ ๋ถ๋ด์ด ๋์๋ ๊ฒ ๊ฐ์ต๋๋ค. ๊ธฐ์กด์๋ ์บ๋ค์คํฑ ์ฒ์๋ถํฐ ๋๊น์ง ๊ฐ๊ฐ์ ๋ ์ด์ด๋ฅผ ๋ค ์ถ๊ฐํด์ฃผ๊ณ ์ง์์ฃผ๋ ๋ฐฉ์์ผ๋ก ๊ฐ๋ฐ์ด ๋์ด์์์ผ๋, ๊ผญ ๊ทธ๋ ค์ค์ผ ํ๋ ์บ๋ค์คํฑ๋ง ๋์์ผ๋ก ๋ ์ด์ด๋ฅผ ์ถ๊ฐ ํด์ฃผ๋๋ก ์์ ํ์ต๋๋ค. ๊ผญ ๊ทธ๋ ค์ค์ผ ํ๋ ์บ๋ค์คํฑ์ ์์ญ์ ์คํฌ๋กค ๋ทฐ์ contentOffset์ผ๋ก ๊ณ์ฐ์ ํด ์ฃผ์์ต๋๋ค. ๋ฐ๋ณต์ ์ผ๋ก ํ์ํ ๊ฐ๋ค์ computed property๋ฅผ ํตํ์ง ์๊ณ stored property์ ํ๋ฒ๋ง ๊ณ์ฐ ํด์ ๋ฃ์ด์ฃผ๋ ๋ฐฉ๋ฒ์ผ๋ก๋ ์ฐ์ฐ์ ์ต์ํ ํ์ต๋๋ค. ๋ํ ๋ ์ด์ด ์์ ์ ์๋์ผ๋ก ์ ๋๋ฉ์ด์ ํจ๊ณผ๊ฐ ๋ค์ด๊ฐ ์์ด ๋ ์ด์ด ์ ์ฒด๋ฅผ CATransaction์ผ๋ก ๋ฌถ์ด ์ ๋๋ฉ์ด์ ํจ๊ณผ๋ ์ ๊ฑฐ ํด ์ฃผ์์ต๋๋ค.
- ๊ฒฐ๊ณผ
- ์ํ๋ ๋๋ก ์คํฌ๋กค, ํ์น ์ ์ค์ฒ ์ ์ ์ ํ ์ค์ผ์ผ์ ์บ๋ค์คํฑ ์ฐจํธ๊ฐ ํ๋ฉด์ ๊ฐ๋ ์ฐจ๋๋ก ์๋ ํ์ต๋๋ค.
- ํ์
- ์บ๋ค์คํฑ ์ฐจํธ์์ ์ฝ์ด๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋๋ฐ, ๊ฐ๊ฐํ ์ฑ ํฌ๋์๊ฐ ๋ฐ์
[error] error: Serious application error. Exception was caught during Core Data change processing.
- ์บ๋ค์คํฑ ์ฐจํธ์์ ์ฝ์ด๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋๋ฐ, ๊ฐ๊ฐํ ์ฑ ํฌ๋์๊ฐ ๋ฐ์
- ์์ธ
NSManagedObjectContext
๋ ์ฐ๋ ๋ ํ๋์์๋ง ๋์ํ๋๋ก ์ ์ฑ ์ด ์ ํด ์ ธ ์์ต๋๋ค. ์ด๋ ์ฐ๋ ๋ ์์ ํ์ง ์์ ๊ฐ์ฒด์ด๊ธฐ ๋๋ฌธ์ ๋๋ค. ๋ฉ์ธ ์ฐ๋ ๋์์๋ง ๋์ํ๋๋ก ์ ํด์ ธ ์๋persistentContainer.viewContext
๋ฅผ ๋คํธ์ํฌ ์์ฒญ ๋ฑ์ผ๋ก ์ธํด ๋ฉ์ธ ์ฐ๋ ๋๊ฐ ์๋ ๊ณณ์์ ์ฝ์ด ๋ฐ์ดํฐ์ ์ ๊ทผ์ ํ๊ฒ ๋๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํ์ต๋๋ค. ๋ฐ๋ผ์ MOC์ ์ ๊ทผํ ๋์๋ ํญ์ ๊ฐ์ ์ฐ๋ ๋๊ฐ ๋๋๋ก ์ ๊ฒฝ์ ์จ์ฃผ์ด์ผ ํฉ๋๋ค.
- ํด๊ฒฐ
- concurrencyType์ ์ง์ ํ ์ ์๋
NSManagedObjectContext
์์ฑ์๋ฅผ ์ด์ฉํด ์์ฑํด ์ฃผ์์ต๋๋ค.NSPrivateQueueConcurrencyType
**์ ์์ ๋ง์ ํ๋ฅผ ๊ฐ๊ณ ์์ด **NSManagedObjectContext
์ ์ฐ์ฐ๋ค์ ํด๋น ํ์์ ๋์ํ๋๋ก ๊ด๋ฆฌ ํด ์ค๋๋ค. ๋ฐ๋ผ์ ์ฐ๋ ๋ ํ๋์์๋ง ๋์ํ๋๋ก ์ ํด์ง ์ ์ฑ ์ ์๋ฐํ์ง ์๊ณ , ์ฑ ํฌ๋์๊ฐ ๋ฐ์ํ์ง ์์ต๋๋ค.
- concurrencyType์ ์ง์ ํ ์ ์๋
- ๊ฒฐ๊ณผ
- Core Data๋ฅผ ์ฑ ํฌ๋์ ์์ด ์ ์ฌ์ฉํ ์ ์๊ฒ ๋์์ต๋๋ค.
Git branch
- git flow๋ฅผ ๋ฐ๋ฅธ๋ค.
- main ๋ธ๋์น๋ฅผ default ๋ธ๋์น๋ก ์ค์ ํฉ๋๋ค.
- ๊ธฐ๋ฅ ๋ณ๋ก feaure ๋ธ๋์น๋ฅผ ์์ฑํ๊ณ , main ๋ธ๋์น์ mergeํฉ๋๋ค.
- feature ๋ธ๋์น์ ๋ค์ด๋ฐ์ feature/์ด์๋ฒํธ-๊ธฐ๋ฅ์ด๋ฆ ์ผ๋ก ์ฌ์ฉํฉ๋๋ค.
์ฝ๋ ์ปจ๋ฒค์
-
Swift API Design GuideLine ๋ฐ๋ฆ ๋๋ค.
-
์ด์ธ์ ๋ฃฐ์ ๋ํด์๋ StyleShare์ Swift-Style-GuideLine์ ๋ฐ๋ฆ ๋๋ค.
-> ๋จ, ๋ค์์ ๋ฃฐ์ ์ถ๊ฐํฉ๋๋ค.
1) ๋ค์ฌ์ฐ๊ธฐ์๋ ํญ(tab) ๋์ 2๊ฐ์ space๋ฅผ ์ฌ์ฉํฉ๋๋ค. 2) ํ์ ์ฒซ ์ค ๋์ด์ฐ๊ธฐ ์์ด ๋ถ์ฌ์ฐ๊ธฐ
-
MARK ์ฃผ์์ ํ์ฉํฉ๋๋ค.
// MARK: - @IBOutlets Action // MARK: - @IBOutlet Outlets // MARK: - Life Cycle func // MARK: - custom func // MARK: - extension์ผ๋ก ๋นผ๊ณ ์ด๋ค ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋์ง ์ ๊ธฐ // MARK: - @objc // MARK: - Type Property // MARK: - Instance Property // MARK: - Initializer
์ปค๋ฐ ์ปจ๋ฒค์
# [ํ์
] : ์ ๋ชฉ (#์ด์๋ฒํธ)
##### ์ ๋ชฉ์ ์ต๋ 50 ๊ธ์๊น์ง๋ง ์
๋ ฅ ############## -> |
# ๋ณธ๋ฌธ์ ์์ ์์ฑ
######## ๋ณธ๋ฌธ์ ํ ์ค์ ์ต๋ 72 ๊ธ์๊น์ง๋ง ์
๋ ฅ ########################### -> |
# --- COMMIT END ---
# [ํ์
] ๋ฆฌ์คํธ
# FEAT : ๊ธฐ๋ฅ (์๋ก์ด ๊ธฐ๋ฅ) -> ๊ธฐ๋ฅ์ ์ธ ์์
# FIX : ๋ฒ๊ทธ (๋ฒ๊ทธ ์์ )
# REFACTOR: ๋ฆฌํฉํ ๋ง -> ์ฝ๋ ์ฌ๋ฐฐ์น์ ๊ฐ์, ๊ธฐ๋ฅ์ ์ธ ์์
# STYLE : ์คํ์ผ (์ฝ๋ ํ์, ์ธ๋ฏธ์ฝ๋ก ์ถ๊ฐ: ๋น์ฆ๋์ค ๋ก์ง์ ๋ณ๊ฒฝ ์์)
# DOCS : ๋ฌธ์ (๋ฌธ์ ์ถ๊ฐ, ์์ , ์ญ์ )
# TEST : ํ
์คํธ (ํ
์คํธ ์ฝ๋ ์ถ๊ฐ, ์์ , ์ญ์ : ๋น์ฆ๋์ค ๋ก์ง์ ๋ณ๊ฒฝ ์์)
# CHORE : ๊ธฐํ ๋ณ๊ฒฝ์ฌํญ (๋น๋ ์คํฌ๋ฆฝํธ ์์ ๋ฑ)
# ------------------
# ํ์
์ ๋๋ฌธ์๋ก
# ์ ๋ชฉ์ ๋ช
๋ น๋ฌธ์ผ๋ก
# ์ ๋ชฉ ๋์ ๋ง์นจํ(.) ๊ธ์ง
# ์ ๋ชฉ๊ณผ ๋ณธ๋ฌธ์ ํ ์ค ๋์ ๋ถ๋ฆฌํ๊ธฐ
# ๋ณธ๋ฌธ์ "์ด๋ป๊ฒ" ๋ณด๋ค "๋ฌด์์", "์"๋ฅผ ์ค๋ช
ํ๋ค.
# ๋ณธ๋ฌธ์ ์ฌ๋ฌ์ค์ ๋ฉ์์ง๋ฅผ ์์ฑํ ๋ "-"๋ก ๊ตฌ๋ถ
# ------------------
ํด๋๋ง ์ปจ๋ฒค์
FakethumbAssignment
|
โโโ FakethumbAssignment
|โโ Global
โ โโโ Literal
โ โโโ Base
โ โโโ Protocol
โ โโโ Supports
โ โ โโโ AppDelegate
โ โ โโโ SceneDelegate
โ โ โโโ Info.plist
โ โโโ Utils
โ โโโ Extension
โ โโโ UIComponent
โ โโโ Resource
โ โโโ Assets.xcassets
โ
|โโ Network
โ โโโ APIService
โ โโโ API
โ โโโ Model
โ โโโ Foundation
โ
โโโ Screens
โโโ Main
โโโ View
์ฝ๋ ๋ฆฌ๋ทฐ ๊ท์น
1) merge ๊ธฐ์ค
: 2๋ช
๋ชจ๋ approveํ๋ฉด mergeํ๋ค.
2) ๋ฆฌ๋ทฐ ์๊ฐ
: ๋ง์ง๋ง comment๋ก๋ถํฐ ํ๋ฃจ๊ฐ ์ง๋๋ฉด approveํ๋ค.
: ์์
์ง์ฐ์ ๋ฐฉ์งํ๊ธฐ ์ํด 24์๊ฐ์ด ์ง๋๋ฉด ๋ณธ์ธ์ด mergeํ๋ค.
- ํ๋ก์ ํธ ์ผ์ ๊ด๋ฆฌ : Notion
- ์คํฌ๋ผ ๋ฏธํ ๋ฐ ์ํต : Discord
- Webhook : Discord
- API : ๋น์ธ API
- ์ฐธ๊ณ ์๋ฃ : Apple Human Interface Guideline
- Library
๋ผ์ด๋ธ๋ฌ๋ฆฌ | Version | |
---|---|---|
Starscream | 4.0.0 |
CocoaPods |
SnapKit | 5.0.0 |
CocoaPods |
Then | 2.7.0 |
CocoaPods |