โจ[Feat/fix]๐ ์์ ๋ฒํผ๋ง, ๋ ์ด์์ ๋ฒ๊ทธ ์์ ๋ฐ ์์ ๋ฏธ๋ฆฌ๋ฃ๊ธฐ ๊ธฐ๋ฅ ๊ตฌํ (ํ์ ๊ณผ์ ๊ตฌํ ์๋ฃ)#16
Conversation
1. ์ปจํ ์ด๋ ๋ทฐ Leading ์์ผ๋ก 10๋งํผ ๋น๊ธฐ๊ธฐ 2. ๊ตฌ๋ถ์ trailing ์์ผ๋ก 10๋งํผ ๋น๊ธฐ๊ธฐ
600x600์ ๋๋ฌด ๋ฌด๊ฑฐ์์ ๋ก๋ฉ ์๋ ์ ํ๋จ
ํ๊ท ์์์ผ๋ก ์นด๋ ๋ง๋๋ ๊ธฐ๋ฅ ์ ๊ฑฐ
HomeVC, SearchVC ํ๋ ์ด์ด ํตํฉ ๊ด๋ฆฌ
There was a problem hiding this comment.
Code Review
์ด๋ฒ PR์ ์์ ๋ฒํผ๋ง ๋ฐ ๋ ์ด์์ ๋ฒ๊ทธ๋ฅผ ์์ ํ๊ณ , ์์ ๋ฏธ๋ฆฌ๋ฃ๊ธฐ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๋ฑ ๋ง์ ๊ฐ์ ์ฌํญ์ ํฌํจํ๊ณ ์์ต๋๋ค. ์ ๋ฐ์ ์ผ๋ก ๊ธฐ๋ฅ ๊ตฌํ์ ์ ์ด๋ฃจ์ด์ก์ผ๋, ์ฑ์ ์ฑ๋ฅ, ํ ์คํธ ์ฉ์ด์ฑ, ๊ทธ๋ฆฌ๊ณ ์ ์ง๋ณด์์ฑ์ ํฅ์์ํค๊ธฐ ์ํด ๋ช ๊ฐ์ง ๊ตฌ์กฐ์ ์ธ ๊ฐ์ ์ด ํ์ํด ๋ณด์ ๋๋ค. ์ฃผ์ ํผ๋๋ฐฑ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
AudioManager๋ฅผ ์ฑ๊ธํค ๋์ ์์กด์ฑ ์ฃผ์ ์ ์ฌ์ฉํ๋๋ก ๋ฆฌํฉํ ๋งํ์ฌ ํ ์คํธ ์ฉ์ด์ฑ์ ๋์ด๋ ๊ฒ์ ์ ์ํฉ๋๋ค.AVPlayer๋ฅผ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ (UICollectionViewCell) ๋ด์์ ์ง์ ๊ด๋ฆฌํ๋ ๋์ ,ViewController์์ ๋จ์ผ ์ธ์คํด์ค๋ก ๊ด๋ฆฌํ์ฌ ๋ฆฌ์์ค ์ฌ์ฉ์ ์ต์ ํํ๊ณ ์ ์ฌ์ ์ธ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํด์ผ ํฉ๋๋ค.- ์ํ ๋ณ๊ฒฝ์ ๋ฐ๋ฅธ UI ์ ๋ฐ์ดํธ ๋ฐฉ์์ ๋ณด๋ค ๋ฐ์ํ ํจ๋ฌ๋ค์์ ๋ง๊ฒ ์์ ํ์ฌ ์ฝ๋์ ์ผ๊ด์ฑ๊ณผ ์์ ์ฑ์ ๋์ด๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
| private var player: AVPlayer? | ||
| private var playerLayer: AVPlayerLayer? | ||
| private var playerItem: AVPlayerItem? |
There was a problem hiding this comment.
ํ์ฌ SearchCollectionViewCell ๋ด๋ถ์ AVPlayer ์ธ์คํด์ค๋ฅผ ์ง์ ์์ฑํ๊ณ ๊ด๋ฆฌํ๊ณ ์์ต๋๋ค. UICollectionViewCell์ ์ฌ์ฌ์ฉ๋๊ธฐ ๋๋ฌธ์, ๊ฐ ์
์ด ์์ ๋ง์ AVPlayer๋ฅผ ๊ฐ์ง ๊ฒฝ์ฐ ์ฌ๋ฌ ์ฌ๊ฐํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค:
- ๊ณผ๋ํ ๋ฆฌ์์ค ์ฌ์ฉ:
AVPlayer๋ ๋ฌด๊ฑฐ์ด ๊ฐ์ฒด์ ๋๋ค. ํ๋ฉด์ ์ฌ๋ฌ ๋น๋์ค ์ ์ด ๋์์ ๋ณด์ด๋ฉด ๋ค์์AVPlayer์ธ์คํด์ค๊ฐ ์์ฑ๋์ด ๋ฉ๋ชจ๋ฆฌ์ CPU ์ฌ์ฉ๋์ด ๊ธ๊ฒฉํ ์ฆ๊ฐํ๊ณ , ์ฑ ์ฑ๋ฅ ์ ํ ๋ฐ ๋น์ ์ ์ข ๋ฃ์ ์์ธ์ด ๋ ์ ์์ต๋๋ค. - ์ฌ์ ๊ด๋ฆฌ์ ๋ณต์ก์ฑ:
ViewController๊ฐvisibleCells๋ฅผ ์ํํ๋ฉฐ ๊ฐ ์ ์ ๋น๋์ค๋ฅผ ์ง์ ์ ์ด(playVideo,pauseVideo)ํด์ผ ํ๋ฏ๋กViewController์Cell๊ฐ์ ๊ฒฐํฉ๋๊ฐ ๋์์ง๋๋ค.
๊ฐ์ ๋ฐฉ์:
ViewController ๋ ๋ฒจ์์ ๋จ ํ๋์ AVPlayer ์ธ์คํด์ค๋ฅผ ๊ด๋ฆฌํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
ViewController๊ฐAVPlayer๋ฅผ ์์ ํฉ๋๋ค.- ์ฌ์ฉ์๊ฐ ์คํฌ๋กคํ์ฌ ํน์ ๋น๋์ค ์
์ด ํ๋ฉด ์ค์์ ์ค๊ฑฐ๋ ์ฌ์ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋๋ฉด,
ViewController๋ ํด๋น ์ ์ ๋น๋์ค URL๋กAVPlayer์AVPlayerItem์ ๊ต์ฒดํฉ๋๋ค. AVPlayerLayer๋ฅผ ํด๋น ์ ์contentView.layer์ ์ถ๊ฐํ์ฌ ๋น๋์ค๋ฅผ ํ์ํฉ๋๋ค.- ์
์ด ํ๋ฉด ๋ฐ์ผ๋ก ์ฌ๋ผ์ง๋ฉด
AVPlayerLayer๋ฅผ ์ ์ ๋ ์ด์ด์์ ์ ๊ฑฐํฉ๋๋ค.
์ด ๋ฐฉ์์ ํตํด AVPlayer ์ธ์คํด์ค๋ฅผ ํ๋๋ง ์ ์งํ์ฌ ๋ฆฌ์์ค๋ฅผ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ๊ณ , ์ฌ์ ๋ก์ง์ ViewController์์ ์ค์ ์ง์ค์ ์ผ๋ก ๊ด๋ฆฌํ์ฌ ์ฝ๋ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ ํ ์ ์์ต๋๋ค.
References
- ์ ์ฌ์ฌ์ฉ ๊ด๋ฆฌ๋ฅผ ์๋ชปํ์ฌ ๋์ ๋ณด์ด๋ UI ๋ฒ๊ทธ๋ ๋ฆฌ์์ค ๋์๋ฅผ ์ ๋ฐํ ์ ์๋ ๊ฒฝ์ฐ P1(high)์ผ๋ก ๋ถ๋ฅํด์ผ ํฉ๋๋ค. ๊ฐ ์ ์ ๋ฌด๊ฑฐ์ด AVPlayer ๊ฐ์ฒด๋ฅผ ํ ๋นํ๋ ๊ฒ์ ์ฌ๊ฐํ ๋ฆฌ์์ค ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์์ต๋๋ค. (link)
| final class AudioManager { | ||
|
|
||
| static let shared = AudioManager() | ||
|
|
||
| private init() {} | ||
|
|
||
| private var player: AVPlayer? | ||
|
|
||
| func play(url: URL) { | ||
| player?.pause() // ์ผ๋จ ์ฌ์๋๋ ๋ ธ๋๋ ๊บผ์ผ ์๊ฒน์นจ | ||
|
|
||
| player = AVPlayer(url: url) | ||
| player?.play() | ||
| } | ||
|
|
||
| func play() { | ||
| player?.play() | ||
| } | ||
| } |
There was a problem hiding this comment.
AudioManager๊ฐ ์ฑ๊ธํค์ผ๋ก ๊ตฌํ๋์ด ์์ด ์ฑ์ ๋ค๋ฅธ ๋ถ๋ถ๊ณผ ๊ฐํ๊ฒ ๊ฒฐํฉ๋ฉ๋๋ค. ์ด๋ ์ ๋ ํ
์คํธ๋ฅผ ์ด๋ ต๊ฒ ๋ง๋ค๊ณ , ์ค๋์ค ์ฌ์ ๋ก์ง์ ๋ค๋ฅธ ๊ตฌํ์ผ๋ก ๊ต์ฒดํ๊ธฐ ์ด๋ ต๊ฒ ๋ง๋ญ๋๋ค. ์๋ฅผ ๋ค์ด, HomeViewController๋ SearchViewController๋ฅผ ํ
์คํธํ ๋ ์ค์ AVPlayer์ ์์กดํ๊ฒ ๋์ด ํ
์คํธ๊ฐ ๋ณต์กํด์ง๊ณ ๋๋ ค์ง ์ ์์ต๋๋ค. ์์กด์ฑ ์ฃผ์
(Dependency Injection)์ ์ฌ์ฉํ์ฌ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
AudioManagerTypeํ๋กํ ์ฝ์ ์ ์ํฉ๋๋ค.AudioManager๊ฐ ์ด ํ๋กํ ์ฝ์ ์ค์ํ๋๋ก ํฉ๋๋ค.SceneDelegate์์AudioManager์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ , ์ด๋ฅผ ํ์๋ก ํ๋ ๋ทฐ ์ปจํธ๋กค๋ฌ์ ์์ฑ์๋ฅผ ํตํด ์ฃผ์ ํฉ๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด ํ
์คํธ ์์๋ ์ค์ AudioManager ๋์ ์ค๋์ค ์ฌ์ ๋์์ ํ๋ด ๋ด๋ ๊ฐ์ง ๊ฐ์ฒด(mock object)๋ฅผ ์ฃผ์
ํ์ฌ, ๋ทฐ ์ปจํธ๋กค๋ฌ์ ๋ก์ง์ ๋
๋ฆฝ์ ์ผ๋ก ํ
์คํธํ ์ ์์ต๋๋ค.
References
- ์ง์ ์ ์ธ ์ ์ญ/๊ณต์ ์์กด์ฑ ์ฌ์ฉ์ ํ ์คํธ ์ฉ์ด์ฑ๊ณผ ์ ์ฐ์ฑ์ ํฌ๊ฒ ์ ํดํ ๋ P1(high)์ผ๋ก ๋ถ๋ฅ๋์ด์ผ ํฉ๋๋ค. ์ฑ๊ธํค ํจํด์ ์ ์ญ ์ํ์ ๋ํ ์์กด์ฑ์ ๋ง๋ค์ด ์ ๋ ํ ์คํธ๋ฅผ ์ด๋ ต๊ฒ ํฉ๋๋ค. (link)
There was a problem hiding this comment.
์ผ๋ถ๋ฌ ์ฑ๊ธํค์ผ๋ก ํ๊ฑฐ์
| // ์ฌ์ ์ด๋ฏธ์ง | ||
| for cell in self.collectionView.visibleCells { | ||
| if let playableCell = cell as? PlayableUICell, | ||
| let indexPath = self.collectionView.indexPath(for: cell), | ||
| let item = self.dataSource[indexPath] as? ContentItem { | ||
|
|
||
| let isPlaying = (item.previewURL == playingURL) | ||
| playableCell.updatePlayUI(isPlaying: isPlaying) | ||
| } | ||
| } |
There was a problem hiding this comment.
ํ์ฌ playingURL ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค visibleCells๋ฅผ ์ํํ๋ฉฐ ์ง์ UI๋ฅผ ์
๋ฐ์ดํธํ๊ณ ์์ต๋๋ค. ์ด ๋ฐฉ์์ ์ฌ๋ฌ ๋ฌธ์ ๋ฅผ ๊ฐ์ง ์ ์์ต๋๋ค:
- ๋ถ์์ ํ ์ ๋ฐ์ดํธ: ํ์ฌ ๋ณด์ด์ง ์๋ ์ (์คํฌ๋กคํด์ผ ๋ณด์ด๋ ์ )์ ์ํ๋ ์ ๋ฐ์ดํธ๋์ง ์์, ์คํฌ๋กค ์ ์ฌ์ ์ํ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ํ์๋์ง ์์ ์ ์์ต๋๋ค.
- ๋ฐ์ํ ํจ๋ฌ๋ค์ ์๋ฐ: ReactorKit๊ณผ ๊ฐ์ ๋ฐ์ํ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ ๋ชฉ์ ์ ์ํ(State)๊ฐ ๋ณ๊ฒฝ๋๋ฉด UI๊ฐ ์๋์ผ๋ก ์ ๋ฐ์ดํธ๋๋๋ก ํ๋ ๊ฒ์ ๋๋ค. ํ์ฌ ์ฝ๋๋ ์ํ ๋ณ๊ฒฝ์ ๋ฐ๋ผ ์๋์ผ๋ก UI๋ฅผ ์กฐ์ํ๊ณ ์์ด ๋ฐ์ํ ํจ๋ฌ๋ค์์ ๊ฑฐ์ค๋ฆ ๋๋ค.
configureCell ํด๋ก์ ์์ ์ด๋ฏธ isPlaying ์ํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก UI๋ฅผ ์ค์ ํ๋ ๋ก์ง(cell.updatePlayUI(...))์ด ์์ผ๋ฏ๋ก, ์ด ๋ก์ง์ ์ ์ผํ ์ง์ค์ ์์ฒ(Single Source of Truth)์ผ๋ก ํ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
playingURL์ด ๋ณ๊ฒฝ๋ ๋ collectionView.reloadData()๋ฅผ ํธ์ถํ์ฌ ์ ์ฒด ์
์ UI๋ฅผ ๋ค์ ๊ทธ๋ฆฌ๋๋ก ํ๋ ๊ฒ์ด ๋ ๊ฐ๋จํ๊ณ ๋ฐ์ํ ํจ๋ฌ๋ค์์ ๋ถํฉํ๋ ํด๊ฒฐ์ฑ
์
๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด visibleCells๋ฅผ ์ํํ๋ ์ฝ๋๋ฅผ ์ ๊ฑฐํ๊ณ ์ฝ๋์ ์ผ๊ด์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
// ์ฌ์ ์ด๋ฏธ์ง
self.collectionView.reloadData()References
- ์ฌ๋ฌ ์ ์ด๋์ง ์๋ ์์ค์์ UI ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ฉด ๋ ๋๋ง์ด ์ผ๊ด๋์ง ์์ ์ ์์ผ๋ฏ๋ก P1(high)์ผ๋ก ๋ถ๋ฅํด์ผ ํฉ๋๋ค. ํ์ฌ UI ์ํ๋
configureCell๊ณผsubscribe๋ธ๋ก ๋ ๊ณณ์์ ๋ณ๊ฒฝ๋๊ณ ์์ด ์ ์ฌ์ ์ธ ๋น์ผ๊ด์ฑ์ ์ ๋ฐํ ์ ์์ต๋๋ค. (link)
There was a problem hiding this comment.
.reloadData()ํ๋๊ฑด ๋๋ฌด ๋นํจ์จ์ ์ผ๊ฑฐ๊ฐ์ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ปฌ๋ ์ ๋ทฐ๋ฅผ ๋ค ๊ฐ์์์ผ๋ฉด ๋๋ฌด ๋ญ๋น๊ณ ์ฌ์ฉ์ ๋์ ๊น๋นก๊ฑฐ๋ฆฌ๋๊ฒ ๋์ ๋ณด์ผ๊ฑฐ๊ฐ์
| // ์ฌ์ ์ด๋ฏธ์ง | ||
| for cell in self.collectionView.visibleCells { | ||
| if let playableCell = cell as? PlayableUICell, | ||
| let indexPath = self.collectionView.indexPath(for: cell), | ||
| let item = self.dataSource[indexPath] as? ContentItem { | ||
|
|
||
| let isPlaying = (item.previewURL == playingURL) | ||
| playableCell.updatePlayUI(isPlaying: isPlaying) | ||
| } | ||
| } |
There was a problem hiding this comment.
HomeViewController์์์ ๋์ผํ ๋ฌธ์ ๊ฐ ์ฌ๊ธฐ์๋ ๋ฐ๊ฒฌ๋ฉ๋๋ค. playingURL ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋ visibleCells๋ฅผ ์ง์ ์ํํ์ฌ UI๋ฅผ ์
๋ฐ์ดํธํ๋ ๋ฐฉ์์ ๋นํจ์จ์ ์ด๋ฉฐ ๋ฐ์ํ ํ๋ก๊ทธ๋๋ฐ ํจ๋ฌ๋ค์์ ๋ง์ง ์์ต๋๋ค.
configureCell์ ์ด๋ฏธ ์ฌ์ ์ํ์ ๋ฐ๋ฅธ UI ์ค์ ๋ก์ง์ด ์์ผ๋ฏ๋ก, playingURL์ด ๋ณ๊ฒฝ๋ ๋ collectionView.reloadData()๋ฅผ ํธ์ถํ์ฌ ๋ฐ์ดํฐ ์์ค๋ฅผ ํตํด UI๋ฅผ ์ผ๊ด์ฑ ์๊ฒ ์
๋ฐ์ดํธํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
// ์ฌ์ ์ด๋ฏธ์ง
self.collectionView.reloadData()References
- ์ฌ๋ฌ ์ ์ด๋์ง ์๋ ์์ค์์ UI ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ฉด ๋ ๋๋ง์ด ์ผ๊ด๋์ง ์์ ์ ์์ผ๋ฏ๋ก P1(high)์ผ๋ก ๋ถ๋ฅํด์ผ ํฉ๋๋ค. ํ์ฌ UI ์ํ๋
configureCell๊ณผsubscribe๋ธ๋ก ๋ ๊ณณ์์ ๋ณ๊ฒฝ๋๊ณ ์์ด ์ ์ฌ์ ์ธ ๋น์ผ๊ด์ฑ์ ์ ๋ฐํ ์ ์์ต๋๋ค. (link)
์์ ์ฌ์ ์์ ์์ : AVPlayerItem.Status์ ๊ฐ์ด (.readyToPlay)์ธ ์์ ์ player๋ฅผ playํ์ฌ ๋ฒํผ๋ง ๋ฒ๊ทธ๋ฅผ ์ก์์ต๋๋ค.
์์ ๋ ์ด์์ ์์ : ์คํ ๋ ์ด์์์ด ์ค์ ๋๊ธฐ์ ์ ์์์ด play๋์ด ๋ ์ด์์์ด 0์ผ๋ก ์ฐ๊ทธ๋ฌ์ ธ์๋ ๋ฌธ์ ๋ฅผ contentView.bounds๋ฅผ ํ๋ ์ด์ด ๋ ์ด์ด์ ๋์ ํด ๋ฒ๊ทธ๋ฅผ ์ก์์ต๋๋ค.
๋ฏธ๋ฆฌ๋ฃ๊ธฐ ์ค๋์ค ์ฌ์: ๋ฆฌ์คํธ์์ ๊ณก์ ์ ํํ๋ฉด ํด๋น ์์์ด ์ฌ์๋๋๋ก ๊ตฌํํ์ต๋๋ค.
์ฌ์ ์ํ UI ์ฐ๋: ReactorKit์ State๋ก ์ฌ์ ์ค์ธ URL์ ๊ด๋ฆฌํ์ฌ, ์ฌ์ ์ค์ธ ์ ์๋ง dimView์ ํ๋ ์์ด์ฝ(Indicator)์ด ๋จ๋๋ก ๋ฐ์ดํฐ ํ๋ฆ์ ์ฐ๊ฒฐํ์ต๋๋ค.
์ค๋์ค ๋งค๋์ ๋์ : ์ฑ๊ธํค ํจํด์ AudioManager๋ฅผ ๋ง๋ค์ด, ํ๋ฉด์ ๋๋๋ค๊ฑฐ๋ ์ฌ๋ฌ ๊ณก์ ๋๋ฌ๋ ์๋ฆฌ๊ฐ ์ ๋ ๊ฒน์น์ง ์๋๋ก ์ต์ ํํ์ต๋๋ค.
์์ธ ์ฒ๋ฆฌ: ๋ฏธ๋ฆฌ๋ฃ๊ธฐ URL์ด ์๋ ๊ณก์ ํด๋ฆญํ ๊ฒฝ์ฐ, Reactor ๋จ์์ ๋ฐฉ์ด ๋ก์ง์ ํ๊ฒ ํ์ฌ ์๋ฌ ํ์ ์ ๋์ฐ๋๋ก ๋ํ ์ผ์ ์ฑ๊ฒผ์ต๋๋ค.