์๋ฒ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ ์ํ์ ๋ณด์ฌ์ฃผ๊ณ ๋ฑ๋ก ์์ ๋ฐ ์ญ์ ๋ฅผ ํ ์ ์๋ ๋น๊ทผ๋ง์ผ๐ฅ ๊ฐ์ ์ฑ ๊ตฌํํ๊ธฐ
์๋ฒ๋ก๋ถํฐ ์ํ ๋ชฉ๋ก์ ๋ฐ์์ค๋ฉฐ LIST, GRID ํํ๋ฅผ ์ ํํ์ฌ ๋ณผ ์ ์๋ค.
๋ํ ์คํฌ๋กค์ ์๋๋ก ๋ด๋ ค์ ์๋ฒ์ ๋ฑ๋ก๋ ์ํ๋ค์ ํ์ธํ ์ ์๋ค.
LIST, GRID ์ํ์์ ์ํ์ ์ ํํ๋ฉด ์ํ์ ๋ํ ์์ธ ์ ๋ณด๋ฅผ ํ์ธํ ์ ์๋ค. (์ผ์ชฝ: LIST / ์ค๋ฅธ์ชฝ: GRID)
์ฌ์ฉ์๋ก๋ถํฐ ์ ๋ ฅ ์ฌํญ๋ค์ ์ ๋ ฅ ๋ฐ๊ณ , ๋ฑ๋ก ๋ฒํผ์ ๋๋ ์ ๋ ํ์ ์ ๋ ฅ ์ฌํญ๋ค์ด ์ ๋ ฅ๋์ง ์์ผ๋ฉด ์๊ตฌํ๋ค.
์ ๋ถ ์ ๋ ฅ๋ ๊ฒฝ์ฐ์๋ ๋ฑ๋ก ์๋ฃ ์ฐฝ์ด ๋ฌ๋ค.
ํ์ ์ ๋ ฅ ์ฌํญ๋ค์ด ์ ๋ถ ์ ๋ ฅ๋๊ณ ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ๋ ๊ฒฝ์ฐ์๋ ์์ ์๋ฃ ์ฐฝ์ด ๋ฌ๋ค. (์ผ์ชฝ)
๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์๋ ๊ฒฝ์ฐ์๋ ๋น๋ฐ๋ฒํธ๊ฐ ๋ฌ๋ผ์ ์์ ํ ์ ์๋ค๋ ์ฐฝ์ด ๋ฌ๋ค. (์ค๋ฅธ์ชฝ)
๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ๋ ๊ฒฝ์ฐ์๋ ์ญ์ ์๋ฃ ์ฐฝ์ด ๋ฌ๋ค. (์ผ์ชฝ)
๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์๋ ๊ฒฝ์ฐ์๋ ๋น๋ฐ๋ฒํธ๊ฐ ๋ฌ๋ผ์ ์ญ์ ํ ์ ์๋ค๋ ์ฐฝ์ด ๋ฌ๋ค. (์ค๋ฅธ์ชฝ)
OpenMarketApp ์์ํ ๋ loading animation ํ๋ฉด ๊ตฌํ
์๋ฒ๋ก๋ถํฐ ์ด๋ฏธ์ง๋ฅผ ๋ฐ์์ฌ ๋, ์ฌ์ฉ์์๊ฒ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์จ๋ค๋ ๊ฒ์ ๋์ฑ ๋ฐ์์ ์ผ๋ก ์๋ ค์ฃผ๊ธฐ ์ํด์ skeleton view ์ ์ฉ
์ฌ์ฉ์๊ฐ ์ํ๋ ๊ฒฝ์ฐ์๋ ๋ฐ์ดํฐ๋ฅผ refresh ๊ฐ๋ฅ
LaunchScreenViewController
: LaunchScreen ์ดํ์ Loading Animation์ ๋ณด์ฌ์ฃผ๊ธฐ ์ํ ViewControllerItemListViewController
: LIST, GRID ํํ๋ก ๋ณผ ์ ์์ผ๋ฉฐ ์๋ฒ๋ก๋ถํฐ ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ ๋ณด์ฌ์ฃผ๋ ViewControllerItemUploadViewController
: ์ํ์ ๋ฑ๋กํ ์ ์๋ ViewControllerItemDetailViewController
: ItemListViewController์์ ํน์ ์ํ์ ์ ํํ๋ฉด ์์ธ ์ ๋ณด๋ฅผ ๋ณด์ฌ์ฃผ๋ฉฐ, ์ค๋ฅธ์ชฝ ์์ ๋ฒํผ์ ๋๋ฅด๋ฉด ์ํ์ ์์ ํ๊ฑฐ๋ ์ญ์ ํ ์ ์๋ ViewController
Item
: ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ฌ ์ ์๋ Decodable์ ์ฑํํ ModelItemListViewModel
: ์ค์ ๋ก ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ Observable property์ธitemList
์ ๊ฐ์ ์ถ๊ฐํด์ ItemListViewController์ itemTableView, itemCollectionView์ ์ฌ์ฉ๋๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์๋ ViewModelItemListCellViewModel
: ItemTableViewCell, ItemTableViewFooterView, ItemCollectionViewCell, ItemCollectionReusableFooterView์ ๊ฐ์ด ์ค์ ๋ก ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ง๋ ๋ชจ์ต์ผ๋ก Model์ธ Item ํ์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ณตํ๋ ViewModelItemListViewController
: ItemTableView, ItemCollectionView๋ฅผ ๊ฐ์ง๊ณ ์๊ณ , ItemListViewModel ํ์ ์ธviewModel
property๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, viewModel์itemList
์ ๊ฐ์ด ๋ณํ ๋ ๋ง๋ค ํ๋ฉด๋ ๊ทธ์ ๋ง๊ฒ ์ ๋ฐ์ดํธ๋ฅผ ํ๊ฒ ๋๋ค.Observable
: ์ ๋ค๋ฆญ ํด๋์ค์ด๋ฉฐ ๊ด์ฐฐํ๊ณ ์ถ์ ๋์์ ๋ํด์ ViewModel์์ Observable type์ผ๋ก ์ ์ธํ ๋ค, ๊ฐ์ด ๋ฐ๋์์ ๋ ์ฒ๋ฆฌ์ ๋ํด์ View์์ bind๋ฅผ ์์ฑํด์ฃผ๋ฉด ๋๋ค.
ItemToUpload
: ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ก๋ ํ ์ ์๋ Encodable์ ์ฑํํ ModelItemUploadViewModel
: ์ฌ์ฉ์์ ์ฌ์ง ์ ํ๊ณผ, ์ํ์ ๋ํ ๋ด์ฉ๋ค์ ์ ๋๋ก ์ ๋ ฅํ๋์ง ํ์ธํ๊ธฐ ์ํ Observable property๋ค๊ณผ, ItemDetailViewController์์ ์์ ๋ฒํผ์ ํด๋ฆญํ์ฌ ์จ ๊ฒฝ์ฐ์ ๊ธฐ์กด์ ๊ฐ๋ค์ ํ๋ฉด์ ํ์ํด์ฃผ๊ธฐ ์ํ Observable property๋ฅผ ๊ฐ์ง๊ณ ์๋ค. ๋ํ ์ค์ ๋ก ์ํ์ HTTP Method์ธ POST, PATCH๋ฅผ ํ๋ ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ณ ์๋ค.ItemUploadCollectionViewCell
: ์ฌ์ฉ์๊ฐ ์ฌ์ง์ ์ ํํ์ ๋, ์ ํํ ์ฌ์ง๋ค์ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด์ CollectionView๋ฅผ ์ฌ์ฉํ๊ณ ์ด๋ Custom Cell๋ก์ ์ฌ์ฉ๋๋ค.ItemUploadCollectionReusableHeaderView
: collectionView์ headerView๋ก ์ฌ์ง ์ ํ์ ํ ์ ์๋ ๋ฒํผ์ ๋ฃ์ด์ค๋ค.ItemUploadViewController
: ImageCollectionView๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, ItemUploadViewModel ํ์ ์ธviewModel
property๋ฅผ ๊ฐ์ง๊ณ ์๋ค. viewModel์ Observable property ๊ฐ๋ค์ด ๋ณํ ๋ ๋ง๋ค bind ๋ฉ์๋์์ ์ฒ๋ฆฌํ ๋๋ก ํ๋ฉด๋ ์ ๋ฐ์ดํธ ๋๋ค.ItemListViewController
์์ + ๋ฒํผ์ ํตํด์ ๋ค์ด์ค๋ ๊ฒฝ์ฐ์ItemDetailViewController
์์ ์์ ๋ฒํผ์ ํตํด์ ๋ค์ด์ค๋ ๊ฒฝ์ฐ์ ๋ค๋น๊ฒ์ด์ ๋ฐ์ ๋ฒํผ์ ํ์ดํ์ด ๋ค๋ฅด๊ณ , ์๋ฒ๋ก์ ๋ฐ์ดํฐ ์ ์ก ๋ฐฉ๋ฒ์ด POST, PATCH๋ก ๋ค๋ฅด๋ค.
Item
: ์์ธ ์ ๋ณด๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด์ Decodable์ ์ฑํํ ModelItemToUpload
: ์์ ์ ์ ํํ๋ ๊ฒฝ์ฐ์๋ ์ฌ์ฉํ๋ Encodable์ ์ฑํํ ModelItemToDeletion
: ์ญ์ ๋ฅผ ์ ํํ๋ ๊ฒฝ์ฐ ์ฌ์ฉํ๋ Encodable์ ์ฑํํ ModelItemDetailViewModel
: ์๋ฒ๋ก๋ถํฐ ์ํ์ ์ ๋ณด๋ฅผ ๋ฐ์์ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๊ธฐ ์ํ Observable property๋ค์ ๊ฐ์ง๊ณ ์๋ค. ๋ํ ์๋ฒ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๊ณ , ์ญ์ ์์ฒญ์ ๋ณด๋ด๋ ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ณ ์๋ค.ItemDetailCollectionViewCell
: ์ํ ์ด๋ฏธ์ง๋ค์ด ์ฌ๋ฌ ๊ฐ์ธ ๊ฒฝ์ฐ์๋ collectionView์ page control์ ์ฌ์ฉํด์ ์ข/์ฐ๋ก ์คํฌ๋กคํ์ฌ ์ด๋ฏธ์ง๋ฅผ ํ์ธํ ์ ์๋ค.ItemDetailViewController
: ImageCollectionView๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, ItemDetailViewModel ํ์ ์ธviewModel
property๋ฅผ ๊ฐ์ง๊ณ ์๋ค. viewModel์ Observable property๋ค์ ๊ฐ์ ๋ณํ๊ฐ ์์ ๋, bind ๋ฉ์๋์ ์ํด์ ์ฒ๋ฆฌํ๋๋ก ํ๋ฉด์ด ์ ๋ฐ์ดํธ ๋๋ค.
ViewController์ ViewController, View ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ backward๋ก ๋ฐ๊ธฐ ์ํด์ delegate protocol์ ๊ตฌํํ์ฌ ์ฌ์ฉํ์๋ค.
class | ์ญํ |
---|---|
LaunchScreenViewController |
LaunchScreen ์ดํ์ Loading animation์ ๋ณด์ฌ์ฃผ๊ณ , ItemListViewController ๋ก ํ๋ฉด ์ ํ |
ItemListViewController |
- LIST, GRID ํํ๋ก ์ํ๋ค์ ๋ณผ ์ ์๋ค - ๋ด๋น๊ฒ์ด์ ์ค๋ฅธ์ชฝ + ๋ฒํผ์ ๋๋ฌ์ ์ํ์ ๋ฑ๋กํ ์ ์๋ค - ์ํ์ ๋๋ฌ์ ์ํ์ ์์ธ ์ ๋ณด๋ฅผ ๋ณผ ์ ์๋ค |
ItemUploadViewController |
- ItemListViewController ์์ + ๋ฒํผ์ ๋๋ฌ์ ์ํ ๋ฑ๋ก ํ๋ฉด์ผ๋ก ์ฌ ์ ์๋ค - ItemDetailViewController ์์ ์์ ๋ฒํผ์ ๋๋ฌ์ ์ํ ๋ฑ๋ก ํ๋ฉด์ผ๋ก ์ฌ ์ ์๋ค - + ๋ฒํผ์ผ๋ก ๋ค์ด์ค๋ฉด ๋ชจ๋ ์ ๋ณด ์๋กญ๊ฒ ์ ๋ ฅ, ์์ ๋ฒํผ์ผ๋ก ๋ค์ด์ค๋ฉด ๊ธฐ์กด์ ์ ๋ณด๊ฐ ์ ๋ ฅ๋์ด์ ธ ์๋ค - ์ํ์ ์ ๋ณด๋ฅผ ์ ๋ ฅํ๊ณ ๋ฑ๋กํ ์ ์๋ค |
ItemDetailViewController |
- ItemListViewController ์์ ์ํ์ ๋๋ฌ์ ์์ธ ์ ๋ณด ํ๋ฉด์ผ๋ก ์ฌ ์ ์๋ค - ๋ฑ๋ก๋ ์ฌ์ง์ด ์ฌ๋ฌ ์ฅ์ด๋ฉด ์ข/์ฐ๋ก ์คํฌ๋กคํด์ ๋ณผ ์ ์๋ค - ๋ด๋น๊ฒ์ด์ ์ค๋ฅธ์ชฝ ๋ฒํผ์ ๋๋ฌ์ ์ํ์ ์์ ํ๊ฑฐ๋ ์ญ์ ํ ์ ์๋ค - ์์ ์ ๋๋ฅด๋ฉด ItemUploadViewController ๋ก ์ด๋ํ๋ค - ์ญ์ ๋ฅผ ๋๋ฅด๋ฉด ์ฌ์ฉ์๋ก๋ถํฐ ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅ๋ฐ๊ณ ์ผ์นํ๋ฉด ์ํ์ ์ญ์ ํ๋ค |
ItemListViewModel |
- ์๋ฒ์์ page์ ๋ง๋ ItemList๋ฅผ fetchํ๋ ๋ฉ์๋๊ฐ ์๊ณ , ๊ฒฐ๊ณผ๋ฅผ Observable property์ Item ํํ๋ก ์ ์ฅํ๋ค - paging๊ณผ ๊ด๋ จ๋ property๋ฅผ ๊ฐ์ง๊ณ ์๋ค |
ItemListCellViewModel |
- ๋คํธ์ํน์ ํตํด ๋ฐ์์จ Item ํ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ค์ ํ๋ฉด์ ๋ณด์ฌ์ง๋ ๊ฐ์ผ๋ก ๊ฐ๊ณตํ๋ค |
ItemUploadViewModel |
- ItemListViewController ๋ก๋ถํฐ ์ค๋ ๊ฒฝ์ฐ์๋ ๋น์ด์๋ ํ๋ฉด์ ์ ๊ณตํ๋ค - ItemListViewController ๋ก๋ถํฐ ์ค๋ ๊ฒฝ์ฐ์๋ ์ํ์ HTTP Method ์ค์์ POST๋ก ๋ณด๋ธ๋ค - ItemDetailViewController ๋ก๋ถํฐ ์ค๋ ๊ฒฝ์ฐ์๋ ๊ธฐ์กด์ ์ํ์ ์ ๋ณด๋ฅผ ํ๋ฉด์ ์ ๊ณตํ๋ค - ItemDetailViewController ๋ก๋ถํฐ ์ค๋ ๊ฒฝ์ฐ์๋ ๋ณ๊ฒฝ๋ ๋ถ๋ถ๋ง HTTP Method ์ค์์ PATCH๋ก ๋ณด๋ธ๋ค - ๋ ๊ฒฝ์ฐ ๋ชจ๋ ์ฌ์ฉ์์ ์ ๋ ฅ์ ๋ํ ์กฐ๊ฑด์ ๊ฒ์ฌํ๊ณ ๋ง์กฑํ์ง ์๋ ๊ฒฝ์ฐ error message์ ํจ๊ป ๋นจ๊ฐ์์ผ๋ก ํ์ํด์ค๋ค |
ItemDetailViewModel |
- ItemListViewController ๋ก๋ถํฐ ์ฌ์ฉ์๊ฐ ์ ํํ๋ ๊ฒฝ์ฐ ํด๋น Item ์ ๋ณด๋ฅผ ๋คํธ์ํน์ ํตํด ๋ฐ์์์ ์ ๊ณตํ๋ค - ์์ ๋ฒํผ์ ๋๋ฅด๋ ๊ฒฝ์ฐ ItemToUpload ํ์ ์ผ๋ก Item ์ ๋ณด๋ฅผ ๋ณํํด์ฃผ๋ ํจ์๋ฅผ ๊ฐ์ง๋ค - ์ญ์ ๋ฒํผ์ ๋๋ฅด๋ ๊ฒฝ์ฐ ItemToDeletion ํ์ ์ ์์ฑํ์ฌ DELETE๋ฅผ ์์ฒญํ๋ค. |
Observable |
- ViewModel์ ๋ฐ์ดํฐ๋ค ์ค์์ ๊ด์ฐฐ์ด ํ์ํ ๋ฐ์ดํฐ ํ์
์ observer ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ ์ํ ์ ๋ค๋ฆญ ํ์
ํด๋์ค - ViewModel์์ Observable ํ๋กํผํฐ๋ค์ ViewController์์ bind ๋ฉ์๋๋ฅผ ํตํด์ ๋ณํ๊ฐ ์์ ๋ ์ฒ๋ฆฌ๋ฅผ ๊ตฌํํด์ค๋ค |
class/struct/enum | ์ญํ |
---|---|
APIRequest |
- makeRequest(), parseResponse() ํจ์๋ฅผ ๊ฐ์ง๋ protocol |
APIReqeustLoader |
- APIRequest protocol ์ฑํ - URLSession ์ ์์ฑ์๋ฅผ ํตํด์ ์ค์ ํ ์ ์๋ค - loadAPIRequest() ๋ผ๋ ๋คํธ์ํน์ ํ๋ ๋ฉ์๋๋ฅผ ๊ฐ์ง๋ค |
HTTPMethod |
- HTTP Method๋ฅผ ๊ฐ์ง๋ enum |
OpenMarketAPI |
- OpenMarketAPI baseURL ๊ฐ์ง๋ enum |
GetItemListAPIRequest |
- APIReqeust protocol ์ฑํํ๋ฉฐ ItemList์ ๋ํด์ GET ์์ฒญ ์ ์ฌ์ฉ |
GetItemAPIRequest |
- APIReqeust protocol ์ฑํํ๋ฉฐ Item์ ๋ํด์ GET ์์ฒญ ์ ์ฌ์ฉ |
GetImageAPIRequest |
- APIReqeust protocol ์ฑํํ๋ฉฐ Image๋ฐ์ดํฐ์ ๋ํด์ GET ์์ฒญ ์ ์ฌ์ฉ |
PostItemAPIRequest |
- APIReqeust protocol ์ฑํํ๋ฉฐ POST ์์ฒญ ์ ์ฌ์ฉ |
PatchItemAPIRequest |
- APIReqeust protocol ์ฑํํ๋ฉฐ PATCH ์์ฒญ ์ ์ฌ์ฉ |
DeleteItemAPIRequest |
- APIReqeust protocol ์ฑํํ๋ฉฐ DELETE ์์ฒญ ์ ์ฌ์ฉ |
enum/protocol | ์ญํ |
---|---|
OpenMarketError |
OpenMarketApp ๋ด๋ถ์์ ๋ฐ์ํ๋ Error๋ค์ ์ ์ํ enum |
AlertString |
Alert๋ฅผ ๋ณด์ฌ์ค ๋ ์ฌ์ฉ๋๋ String์ ์ ์ํ enum |
AlertShowable |
Alert๋ฅผ ๋ณด์ฌ์ฃผ์ด์ผํ๋ ViewController๊ฐ ์ฑํํ๋ protocol |
class/struct/enum | ์ญํ |
---|---|
CustomNumberFormatter |
ViewModel์์ View์ ๋ณด์ฌ์ง ๋ ์๋, ๊ธ์ก ๋ฑ์ ๋ํด์ ์๋ฆฌ์์ ๋ฐ๋ฅธ ์ฝค๋ง(,)๋ฅผ ๋ฃ๊ธฐ ์ํ struct |
OpenMarketViewString |
OpenMarket ์ฑ ๋ด๋ถ์ View๋ค์์ ๋ณด์ฌ์ง๋ String๋ค์ ์ ์ํ enum (ItemListViewString, ItemUploadViewString, ItemDetailViewString) |
ImageCacheManager |
์ด๋ฏธ์ง ์บ์ฑ์ ์ํด์ ์ฌ์ฉํ๋ singleton class |
ISO4217_CurrencyCode |
ISO4217; ํตํ์ ์ด๋ฆ์ ์ ์ํ๊ธฐ ์ํ ํตํ์ ์ข ๋ฅ๋ฅผ ๊ฐ์ง๋ enum |
SkeletonView third-party ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
SkeletonView ์ฌ์ฉ ์ด์ ๋ฐ ๋ฐฉ๋ฒ
Skeleton View
: Loading ๋๋ ๋์์ ์ค์ ๋ก ๋ณด์ฌ์ง View์ ๋น์ทํ ํํ์ View๋ฅผ ์๋ฏธ
์ฌ์ฉ ์ด์
: ์ฌ์ฉ์๊ฐ ๋ฐ์ดํฐ๊ฐ ๋ก๋ฉ๋๋ ๋์์ ๋น ํ์ด์ง๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ฒ๋ณด๋ค skeletion view๋ฅผ ํตํด ๋ก๋ฉ๋๊ณ ์๋ค๋ ๊ฒ์ ๋ณด์ฌ์ฃผ๋ฉด App์ด ๋ ๋ฐ์์ ์ด๊ณ ๋น ๋ฅด๋ค๊ณ ๋๋ผ๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ๋ค. ๋ํ ๊ธฐ์กด์ loading spinner๋ณด๋ค๋ ์ปจํ ์ธ ์ ๋๋ต์ ์ธ ํํ๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์๋ก ํ์ฌ๊ธ App์ด ์งํ๋๊ณ ์๋ค๊ณ ๋๋ผ๊ฒ ํ๋ค.์ฌ์ฉ ๋ฐฉ๋ฒ
: SkeletionView ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฅผ ์ฌ์ฉUITableView
์ ์ ์ฉํ๋ฏ๋ก, ๊ธฐ์กด์UITableViewDatasource
๋์ ์SkeletonTableViewDataSource
๋ฅผ ์ฑํํ์ฌ ๊ตฌํ- Storyboard ์ ์ฝ๋๋ก ๊ตฌํ๋ View๋ค ์ค์์ SkeletionView๋ฅผ ์ ์ฉํ View์
isSkeletonable
์์ฑ์ true๋ก ์ค์ - ๋ณด์ฌ์ง ์์ ๊ณผ ์ฌ๋ผ์ง ์์ ์์ SkeletonView๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ์จ๊ธฐ๋ ๋ฉ์๋ ํธ์ถ
๊ณ ๋ คํ ๋งํ ์ฌํญ
SkeletionView ์ฌ์ฉํ ํ๋ฉด
: OpenMarketApp์์๋ ๋งจ ์ฒ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ ๋ณด์ฌ์ฃผ๋ ํ๋ฉด์์์ ๋ก๋ฉ์ด ์์ ์ ์๊ธฐ ๋๋ฌธ์ ํด๋น ํ๋ฉด์๋ง SkeletionView๋ฅผ ์ ์ฉSkeletionView๊ฐ ๋งค๋ฒ ๋ณด์ฌ์ ธ์ผํ๋๊ฐ์ ๋ํ ๊ณ ๋ฏผ
: ๋งจ ์ฒ์ ์ดํ์ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ฐ์์ค๊ฑฐ๋ ํ ๋๋ SkeletionView๋ฅผ ์ฌ์ฉํ์ง ์์์ต๋๋ค. ๊ทธ ์ด์ ๋ ๋งจ ์ฒ์์ ์ฑ์ ๋ค์ด์์ ๋์๋ ์ด๋ ํ ์ปจํ ์ธ ๊ฐ ๋์ฌ์ง ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์ ์ปจํ ์ธ ์ ํํ๋ฅผ ์์ํ ์ ์๋ SkeletionView๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ฌ์ฉ์ฑ์ ๋์ฌ์ค ์ ์๋ค๊ณ ์๊ฐํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ฑ์ ์ฌ์ฉํ๋ ๋์ค์ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ค์ด ๋ฐ๋ ๊ฒฝ์ฐ์๋ ์ปจํ ์ธ ๊ฐ ์ด๋ ํ ํํ์ธ์ง ์ด๋ฏธ ์๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ตณ์ด SkeletionView๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ์กด์ ๋ฐ์ดํฐ๋ค์ ๊ฐ๋ฆฌ์ง ์๊ณ , indicator๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๋ฉ์ค์์ ํ์ํ๋๋ก ํ์์ต๋๋ค.
ScrollViewDidScroll() ๋ฉ์๋ ์ฌ์ฉ
Pagination ์ฌ์ฉ ์ด์ ๋ฐ ๋ฐฉ๋ฒ
-
์ฌ์ฉ ์ด์
: tableview๋ collectionview๋ฅผ ์ฌ์ฉํ ๋, ์๋ฒ์์ ๋ง์ ๋ฆฌ์คํธ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ฌ ๋ ํ ๋ฒ์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋ฉด ๊ธฐ๊ธฐ์ ์๋ฒ์๊ฒ ๋ถ๋ด์ด ๋๋ฏ๋ก ์ผ์ ๋์ ๋ฆฌ์คํธ ๋ฐ์ดํฐ๋ง ๋ณด๋ด์ฃผ๊ณ ์คํฌ๋กค์ ๋ด๋ ธ์ ๋ ์ถ๊ฐ๋ก ๋ฆฌ์คํธ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด์ฃผ๊ธฐ ์ํด์ ์ฌ์ฉ -
๊ตฌํ ๋ฐฉ๋ฒ
:ScrollYOffset
์ ํ์ฉํ์ฌ ๊ตฌํ-
1๏ธโฃ scrollView์
contentOffset.y
๊ตฌํ๊ธฐcontentOffset.y
= scroll์ด ๋๊ธฐ ์ scrollview์ scrollview.frame.size.height๋ฅผ ๊ธฐ์ค์ผ๋ก ํ๋ฉด์์์ scrollview์ y ์ขํ -
2๏ธโฃ scrollView์
contentSize.height
๊ตฌํ๊ธฐcontentSize.height
= scrollView ๋ด content์ ํฌ๊ธฐ (ํ์ฌ tableview์ ๋ค์ด์๋ cell๋ค์ ๊ฐ์์ ๋ฐ๋ฅธ ๋์ด) -
3๏ธโฃ scrollView์
frame.height
๊ตฌํ๊ธฐ
frame.height
= ํ๋ฉด์ ์ธ๋ก ๋์ด -
1๏ธโฃ > 2๏ธโฃ - 3๏ธโฃ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๊ฒฝ์ฐ์ paging ์ํ
-
BSImagePicker third-party ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
BSImagePicker ์ฌ์ฉ ์ด์ ๋ฐ ๋ฐฉ๋ฒ
BSImagePicker
: iOS์์ ์ฌ๋ฌ ์ฅ์ ์ฌ์ง์ ์ ํํ๊ธฐ ์ํด์ ์ฌ์ฉํ๋ ์๋ํํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
์ฌ์ฉ ์ด์
: Swift์์๋UIImagePickerController
๋ฅผ ์ ๊ณตํ์ฌ ์ด๋ฏธ์ง๋ฅผ ์ ํํ ์ ์๋ค. ๊ทธ๋ฌ๋UIImagePickerController
์์ ์ฌ๋ฌ ์ฅ์ ์ ํํ๋ ๋ฐฉ๋ฒ์ ์ด๋ฏธ์ง๋ฅผ ํ ์ฅ์ฉ ์ฌ๋ฌ ๋ฒ ์ ํํด์ผํ๋ค. ์ด๋ ์ฌ๋ฌ ์ฅ์ ์ฌ์ง์ ์ ํํ ๋ ๋งค๋ฒUIImagePickerController
๋ฅผ ๋์ฐ๋ ๊ฒ์ ๋นํจ์จ์ ์ด๋ผ๊ณ ์๊ฐํ๋ค. ๊ฒ์ ๊ฒฐ๊ณผ iOS14 ์ด์์์๋ถํฐ๋ ์ ์ฉ๊ฐ๋ฅํPHPickerViewController
๊ฐ ์์ง๋ง iOS 14 ์ด์์์๋ง ์ฌ์ฉ๊ฐ๋ฅํด์ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค.๊ธฐ๋ฅ
: ์ฌ๋ฌ ์ฅ ์ ํ / ์ ์ฒดํ๋ฉด preview / ์จ๋ฒ ์ ํ / images, Live Photo, video ์ ํ ๊ฐ๋ฅ์ฌ์ฉ ๋ฐฉ๋ฒ
- Cocoapods์
BSImagePicker
์ถ๊ฐ Info.plist
์์ ์ฌ์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ ๊ถํ ์์ฒญ ์์ฑBSImagePicker
๋ฅผ import ํ๊ณ ,ImagePickerController
์์ฑpresentImagePicker()
๋ฉ์๋์์ finished ๋ถ๋ถ ๊ตฌํ
์ด๋ ์ด๋ฏธ์ง๋PHAsset
ํ์ ์ด๋ฏ๋ก, ์ด๋ฅผUIImage
ํ์ ์ผ๋ก ๋ณํํ๊ธฐ ์ํด์PHImageManager
์ฌ์ฉ
- Cocoapods์
PickerView๋ฅผ ์ฌ์ฉ
PickerView ์ฌ์ฉ ์ด์ ๋ฐ ๋ฐฉ๋ฒ
PickerView
: ์ฌ์ฉ์๊ฐ ํ๋ ์ด์์ ์ ํ์ง ๊ฐ์ด๋ฐ ํ๋๋ฅผ ์ ํํด์ผํ๋ ๊ฒฝ์ฐ ์ฌ์ฉํ๋ view
์ฌ์ฉ ์ด์
: OpenMarketApp ๋ด๋ถ์์ ์ฌ์ฉ์๊ฐ ์ํ์ ๋ฑ๋กํ๊ฑฐ๋, ์์ ํ๋ ํ๋ฉด์์ ํตํ๋ฅผ ์ ํํ ๋ pickerview๋ฅผ ์ฌ์ฉํ์๋ค. ์ด์ ๋ ํตํ์ ๊ฒฝ์ฐ์๋ ์๋ฒ์์ ISO4217๋ฅผ ๋ฐ๋ฅด๊ธฐ ๋๋ฌธ์ ์ ํด์ง ํตํ ๋ฌธ์์ด์ ์๋ฒ๋ก ์ ์กํด์ผํ๋ค. ๊ทธ๋ฌ๋ ์ฌ์ฉ์๊ฐ ISO4217์ ํด๋นํ๋ ํตํ๋ช ์ ๋ชจ๋ฅผ ์ ์๊ธฐ ๋๋ฌธ์ ์ค์ ๋ก ์ฌ์ฉํ๋ ํตํ์ ์ด๋ฆ์ ์์ด๋ก ํ๊ธฐํ๊ณ , ํด๋น ํตํ๋ฅผ ์ ํํ์ ๋ ISO4217์ ๋์ํ๋ ํตํ๋ช ์ด ๋ฐํ๋๋๋ก ๊ตฌํํ์๋ค.์ฌ์ฉ ๋ฐฉ๋ฒ
- ISO4217์ ๋ฐ๋ฅด๋ ํตํ์ ์ข ๋ฅ๋ฅผ enum์ผ๋ก ์์ฑ
UIPickerView
๋ฅผUITextfield
์ inputView๋ก ์ ์- pickerView์ delegate, dataSource๋ฅผ ์ค์ ํ๊ณ ํ์ํ ๋ฉ์๋๋ฅผ ๊ตฌํ
CollectionView & PageControl ์ฌ์ฉํด์ ๊ตฌํ
PageControl ์ฌ์ฉํ ๋ถ๋ถ ๋ฐ ๋ฐฉ๋ฒ
PageControl
: ํ์ด์ง์ ํ๋ ํ๋ฉด์ด ์์ ๋, ํ์ด์ง๋ฅผ ํ์ํ๋ ์ํ์ผ๋ก ์ด๋ค์ง ์ ๋ค์ ํ์ํ๋ ์ปจํธ๋กค
์ฌ์ฉ ๋ถ๋ถ
: ์ํ ์์ธ ์ ๋ณด ํ๋ฉด์์ ์ฌ๋ฌ ๊ฐ์ ์ด๋ฏธ์ง๊ฐ ์์ ๋ ์ด๋ฅผ ์ข/์ฐ๋ก ์คํฌ๋กค ํ ๋ ํ์ด์ง๋ฅผ ํ์ํ๊ธฐ ์ํด์ ์ฌ์ฉ์ฌ์ฉ ๋ฐฉ๋ฒ
- pageControl์ View์ ์์ฑ
- collectionView์์ ๊ฐ๋ก๋ก ์คํฌ๋กค์ ํ๋๋ฐ ์ด๋ pageControl์ ์ฌ์ฉํ๋ฏ๋ก
showHorizontalScrollIndicator
์ false ์ค์ - collectionView์
isPagingEnable
์ true๋ก ์ค์ scrollViewDidScroll
ํจ์์์ pageControl์currentPage
๋ฅผ ์ค์ ํ๋ ๋ก์ง ์์ฑ
1๏ธโฃ ๋จ์ ํ ์คํธ ๊ฐ๋ฅํ๋๋ก ๋ชจ๋ธ ๋ฐ ๋คํธ์ํน ๋ด๋น ํ์ ์ ๊ตฌํ
2๏ธโฃ ์์ ํ๋ฉด์์ ์ ๋๋ฉ์ด์ ๋ฃ๊ธฐ
4๏ธโฃ ์ด๋ฏธ์ง ํ ๋ฒ์ ์ฌ๋ฌ ๊ฐ ์ ํํ๋ ๋ฐฉ๋ฒ
5๏ธโฃ ItemListViewController์์์ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ reloadํ๋ ๊ฒฝ์ฐ
-
๋ฌธ์ ์ํฉ
- ๋คํธ์ํฌ ์์ด๋ ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋๋ก ๋คํธ์ํน ํ์ ์ ๊ตฌํํ๊ณ ์ถ์๋ค. ๊ทธ๋ฆฌ๊ณ ์ง๋ ํ๋ก์ ํธ์์๋ ๋คํธ์ํน ํ์ ์ Singleton์ ์ฌ์ฉํด์ ๋ด๋ถ์ ๋ชจ๋ fetch, patch, post, delete์ ๊ฐ์ ๋ฉ์๋๋ฅผ ๊ฐ์ง๋๋ก ๊ตฌํํ์๋๋ฐ, singleton์ ์ฌ์ฉํ๊ฒ ๋๋ฉด ์ฑ ๋ด๋ถ์์ ๊ณ์ํด์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฐจ์งํ๊ณ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฌํ ๋ฐฉ๋ฒ๋ณด๋ค๋ ๋คํธ์ํน์ด ํ์ํ ๋๋ง๋ค ์์ฑํ์ฌ ์ฌ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ ๊ณ ๋ฏผํด๋ณด๊ฒ ๋์๋ค. ๋ํ API๊ฐ ์ถ๊ฐ๋์์ ๋ SOLID์ OCP์ ๋ฐ๋ผ์ ๊ธฐ์กด์ ํ์ผ์ด๋, ์ฝ๋๋ฅผ ์์ ํ์ง ์๊ณ ์ฌ์ฉํ ์ ์๋๋ก ๊ตฌํํ๋ ค๊ณ ๋ ธ๋ ฅํด๋ณด์๋ค.
-
ํด๊ฒฐ ๋ฐฉ๋ฒ
-
๋จผ์ ๋คํธ์ํฌ๊ฐ ์์ด๋ ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋๋กํ๊ธฐ ์ํด์๋
MockURLProtocol
์ ์ฌ์ฉํด์ ์์กด์ฑ์ ์ฃผ์ ์์ผ์ ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋๋ก ๊ตฌํํ์๋ค. ๊ทธ๋ฆฌ๊ณ OCP๋ฅผ ์งํค๊ธฐ ์ํด์ ๊ฐ๊ฐ์ APIRequest๋ฅผ ๊ฐ๊ฐ์ ํ์ผ๋ก ๋ง๋ค๊ณ ,APIRequest
protocol์ ์์ฑํ๊ณ APIRequstLoader ํด๋์ค๋ฅผ ์์ฑํ์ฌ API์ ๋ฐ๋ผ์ ์ฌ์ฉํ ์ ์๋๋ก ๊ตฌํํ์๋ค.protocol APIRequest { associatedtype RequestDataType associatedtype ResponseDataType func makeRequest(from data: RequestDataType) throws -> URLRequest func parseResponse(data: Data) throws -> ResponseDataType } final class APIRequestLoader<T: APIRequest> { let apiRequest: T let urlSession: URLSession // ์์กด์ฑ์ ์ฃผ์ ํด์ฃผ๋ ๋ถ๋ถ์ด๊ธฐ๋ ํ๋ฉฐ, URLSession์ ๊ธฐ๋ณธ ๊ฐ์ .shared๋ก ๋์ ํ์ฌ ์ฃผ์ ํ์ง ์๋ ๊ฒฝ์ฐ์๋ // ๊ธฐ๋ณธ์ ์ผ๋ก URLSession.shared๋ฅผ ์ฌ์ฉํ๋๋ก ํ๋ค. init(apiReqeust: T, urlSession: URLSession = .shared) { self.apiRequest = apiReqeust self.urlSession = urlSession } func loadAPIReqeust(requestData: T.RequestDataType, completion: @escaping (T.ResponseDataType?, OpenMarketError?) -> Void) { // networking } } // ๊ฐ๊ฐ์ APIRequest ์ค์ ํ๋์ธ GetItemListAPIRequest struct GetItemListAPIRequest: APIRequest { func makeRequest(from page: Int) throws -> URLRequest { guard var components = URLComponents(string: OpenMarketAPI.baseURL) else { throw OpenMarketError.failToMakeURL } components.path += "items/\(page)" return URLRequest(url: components.url!) } func parseResponse(data: Data) throws -> ItemList { return try JSONDecoder().decode(ItemList.self, from: data) } }
-
- ๋ฌธ์ ์ํฉ
- ์ค์ ๋น๊ทผ ๋ง์ผ ์ฑ์ ๋ณด๋ฉด ์ฑ์ด ์์๋ ๋ indicator๊ฐ ๋์๊ฐ๊ณ ์๋ค. ๋ฐ๋ผ์ LauchScreen์์ loading indicator๋ฅผ startํ๋ ค๊ณ ํ์ผ๋ ์คํ๋์ง ์์๋ค.
- ํด๊ฒฐ ๋ฐฉ๋ฒ
- ์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์์ธ์ LauchScreen์์๋ Custom Class ๋ฐ Attributes ๋ฐฐ์น๊ฐ ๋ถ๊ฐํ๋ค. ์ฆ, LauchScreen์ staticํ ์ํ์ด๊ธฐ ๋๋ฌธ์ ๋น๊ทผ ๋ง์ผ ์ฑ์ฒ๋ผ animation์ด ๋์ํ๊ธฐ ์ํด์๋ gif ํํ์ image๋ฅผ ๋ฃ์ด์ LaunchScreen์์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋, Inital ViewController๋ฅผ Launch Screen ์ดํ์ ๋์ฐ๋ฉด์ ์ ๋๋ฉ์ด์
์ ์ถ๊ฐํ๊ณ ์ดํ์ ๋ค๋ฅธ ViewController๋ก ๋์ด๊ฐ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ ์ ์๋ค. ์ด๋ฒ ํ๋ก์ ํธ์์๋
LaunchScreenViewController
๋ฅผ initial ViewController๋ก ์ฌ์ฉํ์ฌ ์ ๋๋ฉ์ด์ ์ ์ถ๊ฐํ๊ณItemListViewController
๋ก ๋์ด๊ฐ๋ ๋ฐฉ๋ฒ์ผ๋ก ๊ตฌํํ์๋ค.
- ์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์์ธ์ LauchScreen์์๋ Custom Class ๋ฐ Attributes ๋ฐฐ์น๊ฐ ๋ถ๊ฐํ๋ค. ์ฆ, LauchScreen์ staticํ ์ํ์ด๊ธฐ ๋๋ฌธ์ ๋น๊ทผ ๋ง์ผ ์ฑ์ฒ๋ผ animation์ด ๋์ํ๊ธฐ ์ํด์๋ gif ํํ์ image๋ฅผ ๋ฃ์ด์ LaunchScreen์์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋, Inital ViewController๋ฅผ Launch Screen ์ดํ์ ๋์ฐ๋ฉด์ ์ ๋๋ฉ์ด์
์ ์ถ๊ฐํ๊ณ ์ดํ์ ๋ค๋ฅธ ViewController๋ก ๋์ด๊ฐ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ ์ ์๋ค. ์ด๋ฒ ํ๋ก์ ํธ์์๋
ItemListViewController์์ tableview์ collectionview์์ ์คํฌ๋กค์ ๋ฒ๋ฒ ์๊ณผ ์ด๋ฏธ์ง๊ฐ ์ ๋๋ก ๋ค์ด๊ฐ์ง ์๋ ๋ฌธ์
- ๋ฌธ์ ์ํฉ
ItemListViewController
์์๋ ์๋ฒ๋ก๋ถํฐ ์ํ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ tableview์ collectionview์ ๋ณด์ฌ์ฃผ๊ฒ ๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ paging์ ์ฌ๋ฌ ๋ฒ ํ๊ณ ์คํฌ๋กค์ ์ ์๋๋ก ๋น ๋ฅด๊ฒ ์์ง์ด๊ฑฐ๋ ํ ๋ ์คํฌ๋กค์ด ๋ฒ๋ฒ ์ด๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ๋ํ ์ด๋ฏธ์ง๊ฐ ์ค์ ์ํ์ ์ด๋ฏธ์ง์๋ ๋ค๋ฅธ ์ด๋ฏธ์ง๊ฐ ๋ค์ด๊ฐ ์๋ ๊ฒฝ์ฐ๋ ๋ฐ์ํ๋ค.
- ํด๊ฒฐ ๋ฐฉ๋ฒ
- ์ด๋ฌํ ๋ฌธ์ ์ ์์ธ์ ์ํ์ ์ด๋ฏธ์ง๋ฅผ ์๋ฒ๋ก๋ถํฐ ๋ฐ์์ค๋ ๊ณผ์ ์ ์ฌ์ฉ์๊ฐ ์คํฌ๋กค์ ์์ง์ด๊ฒ ๋๋ฉด, ๋ค๋ฅธ cell์ ๋ฐ์์จ ์ด๋ฏธ์ง๊ฐ ๋ค์ด๊ฐ๋ ๊ฒฝ์ฐ๊ฐ ์๊ธฐ๊ธฐ ๋๋ฌธ์ด๋ค. ๋ํ ์คํฌ๋กค์ด ๋ฒ๋ฒ ๊ฑฐ๋ฆฌ๋ ๋ฌธ์ ์ญ์๋ ์๋ฒ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ imageview์ ๋ฃ๋ ๋น๋๊ธฐ ๊ณผ์ ์์ ์คํฌ๋กค์ ๋ด๋ฆฌ๋ ๊ฒ ์๋๋ณด๋ค ๋ง์ ์๊ฐ์ด ์์๋๊ธฐ ๋๋ฌธ์ด๋ค.
- ๋ฐ๋ผ์ ์ด๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์๋
cellForItemAt()
,cellForRowAt()
๋ฉ์๋์์ image๋ฅผ ๋ฃ์ผ๋ ค๊ณ ํ๋ cell์ index์ ํ์ฌ dequeueํ reusableCell์ index๋ฅผ ๋น๊ตํด์ ๊ฐ์ ๊ฒฝ์ฐ์๋ง ๋ฃ์ด์ฃผ๋๋ก ํ๋ฉด ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ด๋ฏธ์ง๋ฅผ ์บ์ฑํ์ฌ ํ ๋ฒ ๋ค์ด ๋ฐ์ ์ด๋ฏธ์ง์ ๊ฒฝ์ฐ์๋ ๋ฐ๋ก ์ฌ์ฉํ ์ ์๋๋ก ํ์ฌ์ ์คํฌ๋กค์ด ๋ฒ๋ฒ ๊ฑฐ๋ฆฌ๊ฑฐ๋, ์ด๋ฏธ์ง๊ฐ ์๋ชป๋ค์ด๊ฐ๋ ๊ฒฝ์ฐ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
-
๋ฌธ์ ์ํฉ
ItemUploadViewController
์์๋ ์ฌ์ฉ์๋ก๋ถํฐ ์ด๋ฏธ์ง๋ฅผ ์ต์ 1๊ฐ๋ถํฐ ์ต๋ 5๊ฐ๊น์ง ๋ฐ์ ์ ์์ด์ผํ๋ค. ๊ทธ๋ ๊ธฐ ์ํด์๋ ์ฌ์ฉ์๋ ์ฌ์ง์ฒฉ์ผ๋ก๋ถํฐ ์ด๋ฏธ์ง๋ฅผ ์ฌ๋ฌ ๊ฐ ์ ํํ ์ ์์ด์ผํ๋ค. ๋ฌผ๋กUIImagePickerController
์ delegate ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ์ด๋ฏธ์ง๋ฅผ ํ ๊ฐ์ฉ ์ฌ๋ฌ๋ฒ ์ ํํ์ฌ ๊ณ ๋ฅผ ์ ์๊ฒ ๊ตฌํํ ์ ์์ง๋ง, ์ต์ 1๊ฐ ์ต๋ 5๊ฐ์ ์ฌ์ง์ ์ฌ๋ฆด ์ ์๊ธฐ ๋๋ฌธ์ ImagePickerController๋ฅผ ์ ๊ฒ๋ 1๋ฒ๋ถํฐ, ์ต๋ 5๋ฒ ํน์ ์ฌ์ง์ด ๋ง์ ๋ค์ง ์์์ ์ญ์ ํ๋ค๊ฐ ๋ค๋ฅธ ์ฌ์ง์ ๊ณ ๋ฅด๊ฒ ๋๋ค๋ฉด ๊ณ์ํด์ ImagePickerController๋ฅผ ๋์์ผํ๋ค. ๋ฐ๋ผ์ ํ ๋ฒ์ ์ฌ๋ฌ์ฅ์ ์ ํํ ์ ์๋ ๋ฐฉ๋ฒ์ ๊ณ ๋ฏผํ๊ฒ ๋์๋ค.
-
ํด๊ฒฐ ๋ฐฉ๋ฒ
-
์ด๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์๋ iOS 14์ด์์์๋ ์๋ก ์๊ฐ๋
PHPickerViewController
๋ฅผ ์ฌ์ฉํด์ ์ฌ๋ฌ ์ฅ์ ์ฌ์ง์ ์ ํํ ์ ์๋ค. ๊ทธ๋ฌ๋ iOS 14์ด์์ ์ ์ ์จ์ 2021๋ 6์ ๊ธฐ์ค์ผ๋ก iPhone์ 85%, iPad๋ 79%์ด๋ฏ๋ก iOS 14 ์ด์ ์ ๋ฒ์ ์์๋ ์ฑ์ ์ฌ์ฉํ ์ ์๋๋ก ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ฑํํ๊ฒ ๋์๋ค. ๋ฐ๋ผ์ ์ด๋ฏธ์ง๋ฅผ ์ฌ๋ฌ ๊ฐ ์ ํํ ์ ์๋ ์๋ํํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฑํํ์ฌ ์ฌ์ฉํ์๋ค. BSImagePicker, OpalImagePicker, RMImagePicker ๋ฑ ์ฌ๋ฌ ์๋ ํํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์์ง๋ง ๊ทธ ์ค์์ Apple์ ๊ธฐ๋ณธ ์ฌ์ง์ฒฉ๊ณผ ๋น์ทํ UI๋ฅผ ๊ฐ์ง๊ณ ์์ด์ ์ฌ์ฉ์๋ค์ด ์ด์ง๊ฐ์ ๋๋ผ์ง ์๊ณ ์ฌ์ฉํ ์ ์๋ BSImagePicker๋ฅผ ์ฑํํ์๋ค. ๋ฐ๋ผ์ BSImagePicker๋ฅผ pod์ ์ถ๊ฐํ๊ณ ์ฌ์ฉํด์ฃผ์๋ค. -
์ด๋ ImagePickerController()๋ฅผ ๋์ฐ๊ธฐ ์ํด์ ํ์ฌ ๋ณด์ด๋ ํ๋ฉด์์์ ์ต์์ ViewController๋ฅผ ์์์ผ ํ ํ์๊ฐ ์์๋ค. ์ด๋ฅผ ์ํด์ UIWindow๋ฅผ extensionํ์ฌ currentViewController๋ฅผ ๋ฐํํ๋๋ก ๊ตฌํํ์๋ค.
extension UIWindow { // iOS 13 ์ด์ ์์๋ keyWindow๊ฐ ์์ง๋ง, 13 ์ดํ์ ๋ฒ์ ์ scene์ ๊ฐ๋ ์ด ์๊ธฐ๋ฉด์ // ์ฌ๋ฌ ๊ฐ์ scene ์ค์์ keyWindow๋ฅผ ์ฐพ๊ธฐ ์ํด์ ์๋์ ๊ฐ์ด filter๋ฅผ ํด์ฃผ๋ ์์ ํ์ static var key: UIWindow? { if #available(iOS 13, *) { return UIApplication.shared.windows.filter{ $0.isKeyWindow }.first } else { return UIApplication.shared.keyWindow } } public var currentViewController: UIViewController? { return self.getCurrentViewController(from: self.rootViewController) } public func getCurrentViewController(from viewController: UIViewController?) -> UIViewController? { if let navigationController = viewController as? UINavigationController { return self.getCurrentViewController(from: navigationController.visibleViewController) } else if let tabBarController = viewController as? UITabBarController { return self.getCurrentViewController(from: tabBarController.selectedViewController) } else { guard let currentViewController = viewController?.presentedViewController else { return viewController } return self.getCurrentViewController(from: currentViewController) } } }
-
-
๋ฌธ์ ์ํฉ
-
ItemUploadViewController
์์ ์ํ ๋ฑ๋ก, ์ํ ์์ ์ด ์ด๋ค์ง๊ฑฐ๋,ItemDetailViewController
์์ ์ํ ์ญ์ ๊ฐ ์ผ์ด๋๋ ๊ฒฝ์ฐ์๋ ์๋ฒ์์ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋๋ฏ๋ก ์๋กญ๊ฒ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ ํ๋ฉด์ ๋ณด์ฌ์ฃผ์ด์ผํ๋ค. ๋ํ ์ฌ์ฉ์๊ฐ ๊ฐ์ฅ ์ต๊ทผ์ ์ํ ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ์ ์๊ธฐ ์ํด์ refresh๋ฅผ ํ๋ ค๊ณ ํ ๋์๋ ๋ฐ์ดํฐ๋ฅผ ์๋กญ๊ฒ ๋ฐ์์์ ํ๋ฉด์ reloadํด์ฃผ์ด์ผํ๋ค. -
๋ฐ๋ผ์
viewWillAppear()
๋ฉ์๋์์ ์๋ฒ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๊ณ ํ๋ฉด์ reloadํ๋ ๋ก์ง์ ๋ฃ์๋๋ฐ + ๋ฒํผ์ ๋๋ฅด๊ณ ๋ฑ๋ก์ ํ์ง ์๊ณ ๋ค๋ก ๊ฐ๋ ๊ฒฝ์ฐ๋, ๋น๋ฐ๋ฒํธ์ ์ค๋ฅ๋ก ์ธํด์ ์์ ์ด๋ ์ญ์ ๊ฐ ๋์ง ์๋ ๊ฒฝ์ฐ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๊ณ reloadํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ๋ํ ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ๋๋, 1ํ์ด์ง๋ถํฐ reload๋ฅผ ํ์ง๋ง ํ๋ฉด์ ๋ณด์ด๋ page๋ 1ํ์ด์ง๋ถํฐ ๋ณด์ฌ์ง์ง ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) viewModel.currentPage = 1 viewModel.fetchData(page: viewModel.currentPage) }
-
-
ํด๊ฒฐ ๋ฐฉ๋ฒ
-
์ค์ ๋ก ์ํ์ด ์๋ฒ์ ๋ฑ๋ก, ์์ , ์ญ์ ๊ฐ ๋๋ ๊ฒฝ์ฐ์๋ง
ItemListViewController
์์ ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๊ณ ํ๋ฉด์ reloadํ๋๋ก Notification์isItemChange
๋ณ์์ ๊ฐ์ ๋ฐ๊ฟ์ฃผ๋๋ก ์์ ํ์๋ค. ๊ทธ๋ฆฌ๊ณ 1ํ์ด์ง๋ถํฐ reload๋ฅผ ํด์ฃผ๊ธฐ ์ํด์ dataSource์์ ์ฌ์ฉํ๋ ๋ฐฐ์ด์ ๋ชจ๋ ๊ฐ์ ์ง์์ฃผ๋ ๋ฐฉ์์ผ๋ก ์์ ํ์๋ค.override func viewWillAppear(_ animated: Bool) { if viewModel.isItemChanged { viewModel.currentPage = 1 viewModel.itemList.value?.removeAll() viewModel.fetchData(page: viewModel.currentPage) viewModel.isItemChanged = false } } // ์ค์ ๋ก ๋ฑ๋ก, ์์ , ์ญ์ ๊ฐ ์ด๋ค์ง๋ ๊ฒฝ์ฐ์๋ Notification์ ํตํด์ viewModel.isItemChange ๊ฐ ๋ณ๊ฒฝ @objc private func didReceiveItemDataChanged(_ notification: Notification) { viewModel.isItemChanged = true }
-
์ ์ฉ ์ด์
: ๊ธฐ์กด์ ๋ ์จ ์ฑ ํ๋ก์ ํธ ์์๋ MVC ๋์์ธ ํจํด์ ์ฌ์ฉํ์๋ค. ์ด๋ ๋ค์ํ ๋ก์ง๋ค์ Controller์์ ์ฒ๋ฆฌํ๋ค๋ณด๋ Controller๊ฐ massiveํ๊ฒ ๋์๊ณ , ์ด๋ฅผ ํด๊ฒฐํ ๋ฐฉ๋ฒ์ ๋ํด์ ์์๋ณด๋ค๊ฐ MVVM ๋์์ธ ํจํด์ ๋ํด์ ๊ณต๋ถํ๊ฒ ๋์๊ณ ์ด๋ฒ OpenMarketApp ํ๋ก์ ํธ์ ์ ์ฉํ๊ฒ ๋์๋ค.์ ์ฉ ๊ฒฐ๊ณผ
: MVVM ๋์์ธ ํจํด์ MVC ๋์์ธ ํจํด๊ณผ๋ ๋ค๋ฅด๊ฒ View์ ์ ๋ฐ์ดํธ ํ ๋ฐ์ดํฐ๋ฅผ ViewModel์ ํตํด์ ์ฒ๋ฆฌํจ์ผ๋ก์จ ๊ธฐ์กด์ Controller๊ฐ ์ฒ๋ฆฌ๋ ํ๋ ๊ฒ๋ค์ ๋ฐฉ์งํด Controller๊ฐ ์ปค์ง๋ ๊ฒ์ ๋ง์ ์ ์์๊ณ , ๋ํ view์ model์ฌ์ด์ ๋ ๋ฆฝ์ฑ์ ๊ฐ์ง ์ ์์๋ค.
M(Model)
- ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ์ ์
- ViewModel์ด Model์ ์์ ํ๊ณ ๊ฐ๊ณตํ์ฌ View์ ๊ฐฑ์
V(View)
- UIView, UIViewController๊ฐ MVVM์ View์ ์ํจ
- ๋ง ๊ทธ๋๋ก ๋ณด์ฌ์ฃผ๋ ์์ ๊ณผ ์ ์ ์ ์ธํฐ๋์ ์ ๋ฐ๋ ์ญํ
- ์ ์ ์ ์ธํฐ๋์ ์ ViewModel์๊ฒ ๋ช ๋ นํ๊ณ , ViewModel์ด ์ ๋ฐ์ดํธ ์์ฒญํ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค๋ค
VM(ViewModel)
- View์ ์ค์ ๋ก ๋ณด์ฌ์ง ๋ฐ์ดํฐ๋ก Model์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ณตํ๋ ์ญํ
- View๊ฐ ์ ์ ์ธํฐ๋ ์ ์ ๋ณด๋ด์ฃผ๋ฉด ์ด์ ์๋ง๋ ์์ ์ ์ฒํ๊ณ View๋ฅผ ๋ณ๊ฒฝ
- View๊ฐ ๋ณํ๊ฒ ๋ ๋, Model๊ณผ UI์์(View) ๊ฐ์ ์ฑํฌ๋ฅผ ๋ง์ถฐ์ฃผ๋ ๊ฒ
- View์ ๋ก์ง์ด ๋ถ๋ฆฌ๋์ด ์์ด๋ ํ ์ชฝ์ด ๋ฐ๋๋ฉด, ๋ค๋ฅธ ์ชฝ๋ ์ ๋ฐ์ดํธ๊ฐ ์ด๋ฃจ์ด์ ธ ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ์ ์ ์งํ๋๋ก ํด์ฃผ๋ ๊ฒ์ด๋ค.
- View๊ฐ ์์ ์ด ๋ณํํ๊ธฐ ์ํด์ ๊ฐ์งํด์ผ ํ ํ์๊ฐ ์๋ ViewModel์ ์์๋ฅผ ๊ฐ์ง ๋์์ผ๋ก ์ค์ ํ๊ณ , ์์์ ๋ณํ๊ฐ ์๊ธฐ๋ฉด ์ค์ค๋ก ๋ณํํจ
- ๋ฐฉ๋ฒ : KVO / Delegation / Property Observer / Combine
- MVC ํจํด์ View์ Model ์ฌ์ด์ ๋ ๋ฆฝ์ฑ์ ๊ฐ์ง ์ ์๋๋กํจ
- View์ ๊ดํ ๋ก์ง๊ณผ ๋น์ง๋์ค ๋ก์ง์ ์ฒ ์ ํ ๊ตฌ๋ถํ์ฌ ๋จ์ ํ ์คํธ ๊ฐ๋ฅ
- ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ด ํ์์ ์ผ๋ก ์๊ตฌ๋จ
- ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ์ํด์ Boilerplate code๋ฅผ ์์ฑํด์ผํ๋๋ฐ, View๊ฐ ๊ฐ๋จํ ๋ก์ง์ด๋ผ๋ฉด ๋ฐฐ๋ณด๋ค ๋ฐฐ๊ผฝ์ด ๋ ํฐ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ๊ฒ ๋จ
๐๐ป MVVM Design Pattern์ ๋ํ ํ์ต ๋ธ๋ก๊ทธ๋ก ๊ฐ๊ธฐ
App์ด ์์๋ ๋ ๋ํ๋ฌ๋ค๊ฐ ์ฒซ ๋ฒ์งธ ํ๋ฉด์ผ๋ก ๋น ๋ฅด๊ฒ ์ ํ๋๋ฉฐ, App์ด ๋ฐ์์ ์ด๊ณ ๋น ๋ฅด๋ค๋ ์ธ์์ ์ฃผ๊ธฐ ์ํด์ ์ฌ์ฉ๋๋ค.
์๊ฐ์ ์ธ ํจ๊ณผ๋ฅผ ์ฃผ๊ธฐ ์ํด์ ์ฌ์ฉ๋๋ ๊ฒ์ด ์๋๋ผ ๋ฐ์์ ์ด๊ณ ๋น ๋ฅด๋ค๋ ์ธ์์ ์ฃผ๊ธฐ ์ํด์ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
- ์คํํ๋ฉด 2์ด ์ ๋ Launch Screen์ด ๋ํ๋๋๋ฐ, OS์์ ํ์ํ ์ ๋ณด๋ค์ Disk์์ ๋ฉ๋ชจ๋ฆฌ๋ก ์ฌ๋ฆฌ๋ ๊ณผ์ ์์ ์ง์ฐ๋ ๋ ๋ณด์ฌ์ฃผ๋ ๊ฒ์ด๋ค
- LauchScreen ViewController์๋ Custom Class ๋ฐ Attributes ๋ฐฐ์น ๋ถ๊ฐ
- Static ํ ์ํ์ด๊ธฐ ๋๋ฌธ์ ์ ๋๋ฉ์ด์ ์ ์ฌ์ฉํ ์ ์๋ค.
๋ฐ๋ผ์ ์์ ํ๋ฉด์์ indicator๊ฐ ๋์๊ฐ๋ Launch Screen์ ๋ง๋ค๊ธฐ ์ํด์๋ Inital ViewController๋ฅผ Launch Screen ์ดํ์ ๋์ฐ๋ฉด์ ์ ๋๋ฉ์ด์ ์ ์ถ๊ฐํ๊ณ ์ดํ์ ๋ค๋ฅธ ViewController๋ก ๋์ด๊ฐ๋๋ก ํ๋ ๋ฐฉ๋ฒ์ ์ฑํํ๋ค.
์ธํฐ๋ท ์์์ ํด๋ผ์ด์ธํธ์ ์๋ฒ๊ฐ ์์์ ์ฃผ๊ณ ๋ฐ์ ๋ ์ฐ๋ ํต์ ๊ท์ฝ
ํด๋ผ์ด์ธํธ์์ ์๋ฒ๋ก ํ์ผ์ ์ ๋ก๋ ํ๋ ๊ณผ์
- ํด๋ผ์ด์ธํธ๊ฐ ์น๋ธ๋ผ์ฐ์ ๋ผ๋ฉด ํผ์ ํตํด์ ํ์ผ์ ๋ฑ๋กํด์ ์ ์กํ๊ฒ ๋๊ณ , ์น ๋ธ๋ผ์ฐ์ ๊ฐ ๋ณด๋ด๋ HTTP ๋ฉ์์ง๋ Content-Type ์์ฑ์ด multipart/form-data๋ก ์ง์ ๋๋ค. ์๋ฒ๋ ๋ฉํฐํํธ ๋ฉ์์ง์ ๋ํด์ ๊ฐ ํ๋๋ณ๋ก ๋ถ๋ฆฌํ์ฌ ๊ฐ๋ณ ํ์ผ์ ์ ๋ณด๋ฅผ ์ป๊ฒ ๋๋ค.
- ์ด๋ฏธ์ง ํ์ผ๋ ๋ฌธ์๋ก ์ด๋ค์ ธ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฏธ์ง ํ์ผ์ ์คํ์ ๋ง๊ฒ ๋ฌธ์๋ก ์์ฑํ์ฌ HTTP request body์ ๋ด์์ ์๋ฒ๋ก ์ ์กํ๋ ๊ฒ์ด๋ค.
HTTP(request, response)
๋ ์์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด 4๊ฐ์ ํํธ๋ก ๋๋ ์ ์์ผ๋ฉฐ Message Body์ ๋ค์ด๊ฐ๋ ํ์
์ HTTP Header์ Content-Type ํ๋์ ๋ช
์ํด ์ค ์ ์๋ค. ํด๋น ํ๋์ ๋ค์ด๊ฐ ์ ์๋ ํ์
์ค ํ๋๊ฐ multipart์ด๋ค.
์ ๋ ฅ ์์ ์ ์ฒด๋ฅผ ๊ฐ์ธ๋ ํ๊ทธ๋ฅผ ์๋ฏธํ๋ค.
name
: form์ ์ด๋ฆ์ผ๋ก ์๋ฒ๋ก ๋ณด๋ด์ง ๋ ์ด๋ฆ์ ๊ฐ์ผ๋ก ๋ฐ์ดํฐ ์ ์กaction
: form์ด ์ ์ก๋๋ ์๋ฒ url ๋๋ html ๋งํฌmethod
: ์ ์ก ๋ฐฉ๋ฒ / GET: Default / POST: ๋ฐ์ดํฐ๋ฅผ url์ ๊ณต๊ฐํ์ง ์๊ณ ์จ๊ฒจ์ ์ ์กautocomplete
:.on
์ผ๋ก ์ค์ ํ๋ฉด form ์ ์ฒด์ ์๋ ์์ฑ ํ์ฉenctype
: ํผ ๋ฐ์ดํฐ๊ฐ ์๋ฒ๋ก ์ ์ถ๋ ๋ ํด๋น ๋ฐ์ดํฐ๊ฐ ์ธ์ฝ๋ฉ ๋๋ ๋ฐฉ๋ฒapplication/x-www-form-urlencoded
: default๋ก ๊ฐ์ผ๋ก ๋ชจ๋ ๋ฌธ์๋ค์ ์๋ฒ๋ก ๋ณด๋ด๊ธฐ ์ ์ ์ธ์ฝ๋ฉ๋จ์ ๋ช ์text/plain
: ๊ณต๋ฐฑ๋ฌธ์๋ "+" ๊ธฐํธ๋ก ๋ณํํ์ง๋ง ๋๋จธ์ง ๋ฌธ์๋ ๋ชจ๋ ์ธ์ฝ๋ฉ๋์ง ์์์ ๋ช ์multipart/form-data
: ๋ชจ๋ ๋ฌธ์๋ฅผ ์ธ์ฝ๋ฉํ์ง ์์์ ๋ช ์ / ์ฃผ๋ก ํ์ผ์ด๋ ์ด๋ฏธ์ง๋ฅผ ์๋ฒ๋ก ์ ์กํ ๋ ์ฌ์ฉ
HTTP Header์ Message Body์ ๋ค์ด๊ฐ ๋ฐ์ดํฐ ํ์ ์ ์ ์ํ๋ Content-type์ ํ๋ ์ค์์ MIME(Multipurpose Internet Mail Extensions) ํ์ ์ค์ ํ๋์ด๋ค.
ํ์ผ์ ์
๋ก๋ ํ ๋ ์ฌ์ง ์ค๋ช
๊ณผ ์ฌ์ง์ ์ํ input
2๊ฐ ์๋ค๊ณ ํ ๋, ์ฌ์ง ์ค๋ช
input
์ content-type์ application/x-www-form-urlencoded
์ด ๋ ๊ฒ์ด๊ณ , ์ฌ์ง input
์ content-type์ image/jpeg
๊ฐ ๋ ๊ฒ์ด๋ค. ์ด๋ HTTP Request Body์ content-type์ผ๋ก๋ ํ๋์ ํ์
์ด ๋ค์ด๊ฐ์ผํ๊ธฐ ๋๋ฌธ์ ์ด๋ฌํ ๊ฒฝ์ฐ, ์ฆ ํ ๊ฐ์ body์ 2์ข
๋ฅ ์ด์์ ๋ฐ์ดํฐ๋ ์ฌ๋ฌ ๊ฐ์ ๋ฉ์์ง๋ฅผ ํ๋์ ๋ฉ์ธ์ง๋ก ๊ตฌ๋ถํด์ ๋ง๋ค์ด์ฃผ๋ ๊ฒ์ด ๋ฐ๋ก multipart
ํ์
์ด๋ค.
- Multipart ํ์ ์ ํตํด MIME์ ํธ๋ฆฌ ๊ตฌ์กฐ์ ๋ฉ์ธ์ง ํ์์ ์ ์ํ ์ ์๋ค
- Multipart ๋ฉ์์ง๋ "Content-type:" ํค๋์ boundary ํ๋ผ๋ฏธํฐ๋ฅผ ํฌํจ
- boundary๋ ๋ฉ์์ง ํํธ๋ฅผ ๊ตฌ๋ถํ๋ ์ญํ ์ ํ๋ฉฐ, ๋ฉ์์ง์ ์์๊ณผ ๋ ๋ถ๋ถ๋ ๋ํ๋
- ์ฒซ ๋ฒ์งธ boundary ์ ์ ๋์ค๋ ๋ด์ฉ์ MIME์ ์ง์ํ์ง ์๋ ํด๋ผ์ด์ธํธ๋ฅผ ์ํด ์ ๊ณต
- boundary๋ฅผ ์ ํํ๋ ๊ฒ์ ํด๋ผ์ด์ธํธ์ ๋ชซ (์ฃผ๋ก ๋ฌด์์ ๋ฌธ์(UUID)๋ฅผ ์ ํํด์ ๋ฉ์์ง ๋ณธ๋ฌธ๊ณผ์ ์ถฉ๋์ ํผํจ)
Content-Type
: multipart/form-data๋ก ์ง์ ๋์ด์ผํจ- ์ ์ก๋๋ ํ์ผ ๋ฐ์ดํฐ์ ๊ตฌ๋ถ์๋ก boundary์ ์ง์ ๋์ด ์๋ ๋ฌธ์์ด์ ์ด์ฉ
- boundary์ ๋ฌธ์์ด ์ค ๋ง์ง๋ง
------WebKitFormBoundary(UUID)--
๋ ๋ง์ง๋ง์--
๊ฐ ์ถ๊ฐ๋ก ๋ถ๋๋ฐ, ์ด๋ body์ ๋์ ์๋ฆฌ๋ ์๋ฏธ
header
์ header
๋ฅผ ๊ตฌ๋ถํ๋ ๊ฒ์ ๊ฐํ ๋ฌธ์์ด๊ณ , header
์ body
๋ฅผ ๊ตฌ๋ถํ๋ ๊ฒ์ ๊ฐํ ๋ฌธ์ 2๊ฐ, body
์ ํฌํจ๋์ด ์๋ filedata
๋ฅผ ๊ตฌ๋ถํ๋ ๊ฒ์ boundary์ด๋ค.
๐๐ป HTTP multipart/form-data์ ๋ํ ํ์ต ๋ธ๋ก๊ทธ๋ก ๊ฐ๊ธฐ
๋จ์ ํ ์คํธ๋ฅผ ์งํํ๊ธฐ ์ํด์๋ ์ฌ๋ฌ ํ๊ฒฝ์์๋ถํฐ ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธ๋ฅผ ์งํํ ์ ์์ด์ผํ๋ค. ๊ทธ๋ฌ๋ ํ ์คํธ์์ ์ค์ ๋ก ๋คํธ์ํฌ ํธ์ถ์ด ์ด๋ค์ง๋ค๋ฉด, ํธ์ถ์ ๋ฐ๋ฅธ ๊ฒฐ๊ณผ ๊ฐ์ ์์ํด์ผํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๋ ๋คํธ์ํฌ ํธ์ถ์ ๋ฐ๋ผ ๊ฒฐ๊ณผ ๊ฐ์ ๋คํธ์ํฌ์ ์ํฉ ํน์ ์๋ฒ์ ์ ์ฅ๋์ด ์๋ ๋ฐ์ดํฐ์ ๋ฐ๋ผ ๋งค๋ฒ ๋ฌ๋ผ์ง ์ ์๋ค.
์ฆ, ํด๋น ๋คํธ์ํน ๋ชจ๋๋ง ๊ฐ์ง๊ณ ํ
์คํธ๋ฅผ ํ๋ ๊ฒ์ด ์๋, **๋คํธ์ํฌ ์ํฉ์ โ์์กด์ โ**์ด๊ฒ ๋๊ณ , ์ด๋ ํญ์ ๊ฐ์ ๊ฒฐ๊ณผ ๊ฐ์ ๋ณด์ฅํ์ง ์๊ธฐ ๋๋ฌธ์ ์ ๋ํ
์คํธ ์์ฑ ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ฒ ๋๋ค. ๋ฐ๋ผ์ ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ URLProtocol
์ ์์๋ฐ์ MockURLProtocol
์ ์ฌ์ฉํ์ฌ ๋คํธ์ํฌ ์ํฉ์ ์์กด์ ์ด์ง ์๊ณ input, output์ ์ฃผ์
ํ์ฌ ๋ชจ๋์ด ์ ์์ ์ผ๋ก ๋์ํ๋์ง ํ
์คํธํ ์ ์๋ค.
๐๐ป URLSession Unit Test์ ๋ํ ํ์ต ๋ธ๋ก๊ทธ๋ก ๊ฐ๊ธฐ
extension UIWindow {
public var currentViewController: UIViewController? {
return self.getCurrentViewController(from: self.rootViewController)
}
public func getCurrentViewController(from viewController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = viewController as? UINavigationController {
return self.getCurrentViewController(from: navigationController.visibleViewController)
}
else if let tabBarController = viewController as? UITabBarController {
return self.getCurrentViewController(from: tabBarController.selectedViewController)
}
else {
guard let currentViewController = viewController?.presentedViewController else {
return viewController
}
return self.getCurrentViewController(from: currentViewController)
}
}
}
์์ extension์์์ ๊ตฌํ์ ํ์ฉํ๋ฉด ๊ธฐ๊ธฐ์์ ๋ณด์ด๋ ํ์ฌ viewController๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค. ๊ทธ๋ฌ๋ ์ด๋ keyWindow
๊ฐ iOS 13์ด์๋ถํฐ multiple scenes์ ์ง์ํ๋ app์์๋ ์ฌ๋ฌ ๊ฐ์ scene์ด ์์ ์ ์๊ณ , ์ด๋ ์ฐ๊ฒฐ๋ ๋ชจ๋ scene๋ค์ key window๋ฅผ ๋ฐํํ๊ธฐ ๋๋ฌธ์ deprecated๋์๋ค. ๋ฐ๋ผ์ ์ด๋ฅผ ๋ฒ์ ํน์ ๊ธฐ๊ธฐ์ ๋ฐ๋ผ ์ฒ๋ฆฌํด์ฃผ์ด์ผํ๋ค.
extension UIWindow {
static var key: UIWindow? {
if #available(iOS 13, *) {
return UIApplication.shared.windows.filter{ $0.isKeyWindow }.first
}
else {
return UIApplication.shared.keyWindow
}
}
}
๋ฐ๋ผ์ iOS 13 ์ด์ ๊ทธ๋ฆฌ๊ณ ์ด์ ๋ฒ์ ์ ๋ํด์ key๋ผ๋ ๋ณ์๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ๊ตฌํํด์ฃผ์๋ค.
UINavigationController
์ navigation stack์์ ์ํ๋ ViewController๋ก ๊ฐ๊ธฐ ์ํด์๋ popToViewController
๋ฅผ ์ฌ์ฉํ๋ค. ์ด๋ ํด๋น ViewController๊ฐ navigation stack์ ์๋์ง ๊ณ ์ฐจํจ์ filter๋ฅผ ์ฌ์ฉํด์ ํ์ธํ๊ณ ์๋ ๊ฒฝ์ฐ popToViewController
๋ฉ์๋ ํธ์ถํ๋ค.
guard let itemListViewController = viewController.navigationController?.viewControllers.filter({$0.isKind(of: ItemListViewController.self)}).first else {
return
}
viewController.navigationController?.popToViewController(itemListViewController, animated: true)