March 18, 2020
2020๋ 3์ 14์ผ ํ ์์ผ, ์คํ 1์๋ถํฐ 5์๊น์ง ํ๋ก๊ทธ๋๋จธ์ค์์ ์งํํ๋ ์น ํ๋ก ํธ์๋ Dev-matching ์ ๋ํ ํ๊ธฐ ๊ธ์ ๋๋ค. ํ ์คํธ ์ดํ ๊ณผ์ ๋ฅผ ๋ค์ ํ์ด๋ณด๋ฉฐ ๊ณผ์ ๊ณผ ์๊ฐ์ ๊ธฐ๋กํด๋ณด์์ต๋๋ค.
์ฌ๊ณผ emoji๋ ๊ณผ์ ํ ์คํธ์์ ์๊ตฌํ๋ ํ์ ๊ธฐ๋ฅ์ ๋๋ค.
์น ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ผ๋ ๊ฟ์ ํฅํด ํ๋ฃจํ๋ฃจ ์ฆ๊ฒ๊ฒ ๊ณต๋ถํ๊ณ ๋ ์์ง๋ง ๋๋ ์ด์ ๋งํ๊ธฐ์ฌ์ ์ทจ์
์ ๋ํ ์๊ฐ๋ ํ์ง ์์ผ๋ฉด ์๋์๋ค. ๊ทธ๋ฌ๋ ๋์ค ํ๋ก๊ทธ๋๋จธ์ค๋ผ๋ ์ฌ์ดํธ์์ ์น ํ๋ก ํธ์๋ Dev-matching
์ด๋ผ๋ ๊ฐ๋ฐ์ ์ฑ์ฉ ํ๋ก๊ทธ๋จ ์ ์ฒญ์ ๋ฐ๊ณ ์๋ค๋ ๊ฒ์ ์๊ฒ ๋์๋ค.
ํ๋ก๊ทธ๋๋จธ์ค๋ ์ฝํ ๋ฌธ์ ๋ฅผ ํ๊ธฐ์ํด์ ์์ฃผ ์ด์ฉํ์๋๋ฐ,
๋ฌธ์ ์ ์ ์ถ๋ ฅ์ด ํจ์ํ์ผ๋ก ๋์ด์์ด์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๋ฌธ์ ๋ฅผ ํธ๋ ๋์๊ฒ๋ ์ ๋ง ํธํ๊ณ ์ข์๋ค.
์ด๋ฌํ ์ฑ์ฉ ํ๋ก๊ทธ๋จ๋ ์๋ค๋ ๊ฒ์ ์ฒ์ ์๊ฒ ๋์๊ณ ์ข์ ๊ธฐํ, ๊ฒฝํ์ด ๋ ๊ฒ ๊ฐ์ ๊ณ ๋ฏผ์์ด ์ง์ํ๊ฒ ๋์๋ค.
์ถ์ฒ : ํ๋ก๊ทธ๋๋จธ์ค
Dev-matching์ ์ฝ๋ฉํ ์คํธ๊ฐ ์๋๋ผ ๊ณผ์ ํ ์คํธ๋ก ์งํ์ด ๋์๊ณ ๊ณผ์ ๋ ์ค์ ์ดํ๋ฆฌ์ผ์ด์ ์ ๋ํ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ณ ์์ ํ๋ ๋ฐฉ์์ผ๋ก ์งํ๋๋ค๊ณ ์ ํ์ ธ์์๋ค. ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ์ญ๋์ ํ๋จํ๊ธฐ์ ์ข์ ํ ์คํธ๋ผ๊ณ ์๊ฐ์ด ๋ค์๋ค.
์ถ์ฒ : ํ๋ก๊ทธ๋๋จธ์ค
๊ณผ์ ํ ์คํธ์ ๋ํ ๊ฐ๋ตํ ์ ๋ณด๋ ํ ์คํธ ์ด์ ์ ๊ณต๊ฐ๋์๋๋ฐ, ์ด์ ์ ๊ฐ์ธ ํ๋ก์ ํธ๋ก ๋ฐฐ๊ฒฝํ๋ฉด ๊ฒ์ ํฌ๋กค๋ง ์ฌ์ดํธ๋ฅผ ๋ง๋ค์ด๋ณธ ์ ์ด ์์ด์ ๊ดํ ์์ ๊ฐ์ด ์๊ฒผ๋ค.
๊ทธ๋ฐ๋ฐ ๋ฌธ์ ๊ฐ ์ด๋ค ์์ผ๋ก ๋์ฌ์ง ์ ํ ๊ฐ์ด ์ค์ง ์์์ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฌธ์์ด, ๋ฐฐ์ด ๊ด๋ จ ๋ฉ์๋๋ค์ด๋ ์๋ฐ์คํฌ๋ฆฝํธ๋ก DOM์ ์กฐ์ํ๋ ํจ์๋ค ์ ๋๋ง ์์งํ๊ณ ํ ์คํธ๋ฅผ ๋ณด๊ฒ ๋์๋ค.
๊ณผ์ ํ ์คํธ๋ ํ ์คํธ ์ด์ ์ ๊ณต๊ฐ๋์๋ฏ์ด, ๊ณ ์์ด ๊ฒ์ ์น์ฌ์ดํธ์ ๊ธฐ๋ณธ ํ์ด ์ฃผ์ด์ก๋ค.
๋ค์๋ง๋ค์ด๋ณธ ๊ณ ์์ด ๊ฒ์ ์น์ฌ์ดํธ (๊ณผ์ )
์ฒ์์๋ ์ฃผ์ด์ง ์ฝ๋๋ฅผ ์ดํดํ๋๋ฐ๋ง ์์ฒญ ์ค๋๊ฑธ๋ ธ๋ค.
๋จผ์ index.html
์ bodyํ๊ทธ ์์๋ ์ ์ฒด ์ปจํ
์ด๋๋ฅผ ๋ด๋นํ <div> ํ๋๋ง ์กด์ฌํ์๊ณ , ๋ชจ๋ ํ๋ก๊ทธ๋จ์ <script>๋ก ์๋ฐ์คํฌ๋ฆฝํธ ํ์ผ์ ๋ถ๋ฌ์ ์ฝ๋๋ฅผ ์คํํ๋ ์์ด์๋ค.
๊ทธ๋ฆฌ๊ณ ES6 class ๋ฌธ๋ฒ์ด ์ฃผ๋ ํ๋ก๊ทธ๋จ ๊ตฌ์ฑ ๋ฐฉ์์ด์์ผ๋ฉฐ, ๊ฐ๊ฐ์ class ๋ด๋ถ์๋ setState ๋ฉ์๋์ render ๋ฉ์๋๊ฐ ์ ์๋์ด์์๋ค. ๊ทธ๋ฆฌ๊ณ constructor ๋ด๋ถ์ render() ํจ์๋ฅผ ์คํํ๋ ์ฝ๋๊ฐ ์์ด์ constructor ์ํ ํ, render() ๋ฅผ ์ํํ๋ฉฐ DOM์ ๊ตฌ์ฑํ๋ ์์ด์๋ค.
๋ฌธ์ ๋ ๋ชจ๋ javascript๋ก ๋์ด์์์ง๋ง, setState ๋ฆฌ๋ ๋๋ง์ด๋ render() ์์ innerHTML์ ํตํด ์์DOM์ ๊ตฌ์ฑํ๋ ๊ฒ, class ๋ด๋ถ state๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ๋ฑ์ ์ฝ๋๊ตฌ์กฐ๊ฐ ๋ฆฌ์กํธ์ ๋ฌด์ฒ ๋น์ทํ๋ค๋ ๋๋์ ๋ฐ์๋ค.
// ์ด๋ฏธ์ง์์ธ๋ณด๊ธฐ์ฐฝ js
class ImageInfo {
$imageInfo = null
data = null
constructor({ $target, data }) {
const $imageInfo = document.createElement('div')
$imageInfo.className = 'ImageInfo'
this.$imageInfo = $imageInfo
$target.appendChild($imageInfo)
this.data = data
this.render()
}
setState(nextData) {
this.data = nextData
this.render()
}
render() {
if (this.data.visible) {
const { name, url, temperament, origin } = this.data.image
this.$imageInfo.innerHTML = `
<div class="content-wrapper">
<div class="title">
<span>${name}</span>
<div class="close">x</div>
</div>
<img src="${url}" alt="${name}"/>
<div class="description">
<div>์ฑ๊ฒฉ: ${temperament}</div>
<div>ํ์: ${origin}</div>
</div>
</div>`
this.$imageInfo.style.display = 'block'
} else {
this.$imageInfo.style.display = 'none'
}
}
}
์ด์ฒ๋ผ ์ฝ๋๋ class์ ์ธ์คํด์ค๋ฅผ ๋ง๋ฌ์ผ๋ก์จ, DOM์ ๋ ๋๋งํ์๊ณ , ํด๋น ์ฝ๋์๋ ์์ง๋ง setState() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ๋ฆฌ๋ ๋๋งํ๋ ๋ฑ classํ์์ ์๋ฐ์คํฌ๋ฆฝํธ ํ์ผ์ ๋ฆฌ์กํธ์
์ปดํฌ๋ํธ
์ ๋ง์ด ๋ฎ์์๋ค๊ณ ์๊ฐํ๋ค.
์ฒซ ๋ฌธ์ ๊ฐ ์ง์ฌ์ง ์ฝ๋๋ฅผ ์๋งจํฑํ๊ฒ ๋ฐ๊พธ๋ ๊ฒ์ด์๋๋ฐ ํ์์ div ์ธ์ ๋ค๋ฅธ ํ๊ทธ์ ์กด์ฌ๋ง ์๊ณ ์์๋ ๋๋ ํด๊ฒฐํ์ง ๋ชปํ๊ณ ๋ค์ ๋ฌธ์ ๋ก ๋์ด๊ฐ์ง๋ง, ์ฝ๋ ์ญ์ ํ๋์ ์ธ์ด์ด๊ณ ๊ฐ๊ฐ ์์๋ค์ ๋ช ์ํด์ฃผ๋ ๊ฒ์ด ์ค์ํ๊ฒ ๊ตฌ๋ ๋๋ผ๊ฒ ๋์๋ค.
ํฌ๋กค๋งํ ๊ณ ์์ด ๋ฐ์ดํฐ๋ฅผ ๋๋ฐ์ด์ค๋ณ๋ก ๋ ์ด์์์ ๋ฌ๋ฆฌํด์ฃผ๋ ๋ฌธ์ ๋ ์์๋ค. ์ฐ์ ์ด๋ฏธ์ง ๋ ์ด์์ ๋ฐฉ์์ grid
์๋๋ฐ, ํ์ ๋ ์ด์์์ก์ ๋, flex๋ฐ์ ์ํด์ ๋นํฉํ๊ธด ํ์ง๋ง @media ์ฟผ๋ฆฌ๋ฅผ ์ด์ฉํด ๋๋ฐ์ด์ค width ์ ๋ฐ๋ผ grid-template-columns
์์ฑ์ ๋ค๋ฅด๊ฒ ํด์ฃผ์๋ค.
OS ๋คํฌ๋ชจ๋ ์ค์ ์ ๋ฐ๋ฅธ CSS ๋ณ๊ฒฝ๊ณผ ์ง์ ํ
๋ง๋ฅผ ๋ฐ๊ฟ ์ ์๋ ํ ๊ธ ๋ฐ์ค๋ฅผ ๋ง๋๋ ๋ฌธ์ ๋ ์์๋ค. @media ์ฟผ๋ฆฌ๋ฅผ ํตํด OS ๋คํฌ๋ชจ๋ ์ค์ ์ ์ธ์ํ ์ ์๊ตฌ๋(@media (prefers-color-scheme: dark)
)์ ๋ํด์ ์ ์ ์์๊ณ ํ ๊ธ ๋ฐ์ค๋ ๋ง๋๋๋ฐ๋ ์ด๋ ต์ง ์์์ง๋ง CSS ์์ฑ ์ฐ์ ์์์ ๋ฐ๋ผ ํ
๋ง๊ฐ ๋ฐ๋์ด๋ ๋คํฌ๋ชจ๋์ CSS ์์ฑ์ ๋ฐ๋ผ๊ฐ๋ ๋ฌธ์ ๊ฐ ์์๋ค.ใ
ใ
๋ด ๋ธ๋ก๊ทธ์๋ ํ
๋ง ํ ๊ธ ๋ฐ์ค๊ฐ ์๋๋ฐ ์ฐธ๊ณ ํ๋ฉด์ ๊ณต๋ถํด๋ด์ผ๊ฒ ๋ค๊ณ ์๊ฐํ๋ค.
์ด๋ฏธ์ง๋ฅผ ํด๋ฆญํ์ ๋, ์์ธ๋ณด๊ธฐ ๋ชจ๋ฌ์ด ๋จ๋๋ฐ, ์ด ๋ํ ๋๋ฐ์ด์ค์ ๋ฐ๋ฅธ ํฌ๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์ด์ผ ํ๋ค.
๋ค์ ๋ถ์ ์ฌ๊ณผ
emoji๋ ํ
์คํธ์์ ์๊ตฌํ๋ ํ์๊ธฐ๋ฅ
์ด๋ค. ์ด๋ฌํ ํ์ ๊ธฐ๋ฅ์ด ์๋ค๋ ๊ฒ์ ํ๊ธฐ๊ธ์ ํฌ์คํ
ํ๋ ์ง๊ธ ์์ ์ ์๊ฒ๋์๋ค.. ใดใ
ใฑ!! ๊ณผ์ ๋ฉ๋ด์ผ์ ๋ณต์ฌํด์ ๋ค๋ฅธ ๊ณณ์ผ๋ก ์ฎ๊ธด ํ ๊ณผ์ ๋ฅผ ์งํํ๋๋ฐ ๋ณต์ฌํ๋ ๊ณผ์ ์์ ๋๋ฝ๋์ ๋ชป๋ดค๋๋ณด๋คโฆใ
ใ
์ฃผ์ด์ง ์ฝ๋์๋ ๋ชจ๋ฌ ๋ซ๊ธฐ ๊ธฐ๋ฅ์ด ์์ ์์๊ณ , ๋ชจ๋ฌ ์์ญ ๋ฐ์ ๋๋ฅด๊ฑฐ๋ / ํค๋ณด๋์ ESC ํค๋ฅผ ๋๋ฅด๊ฑฐ๋ / ๋ชจ๋ฌ ์ฐ์ธก์ ๋ซ๊ธฐ(x) ๋ฒํผ์ ๋๋ฅด๋ฉด ๋ซํ๋๋ก ์ฝ๋๋ฅผ ์ง๋ ๋ฌธ์ ์๋ค.
๋ชจ๋ฌ x ๋ฒํผ์ onclick ์ด๋ฒคํธํธ๋ค๋ฌ์, window ๊ฐ์ฒด์ onkeydown, onclick์ด๋ฒคํธํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํด์ฃผ๋ฉด ๋์๋ค.
ํฌ๋กค๋งํ ๋ฐ์ดํฐ๋ api ํํ๋ก ์ฃผ์ด์ก์ผ๋ฉฐ /cats/:id
์ GET ์์ฒญ์ ํตํด ๊ฐ์ ธ์ค๋ฉด ๋์๋ค.
AJAX ๋น๋๊ธฐ์์ฒญ์ด ์๋ฃ๋ ํ์ ํด๋น ๋ชจ๋ฌ์ฐฝ์ ๋ฐ์ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ๋ ๋๋ง ๋๊ฒ๋ ๊ตฌํํ๊ธฐ ์ํด์ Promise then() ๋ฉ์๋์ฒด์ด๋์ ํตํ์ฌ ๊ตฌํํ์๋ค.
ํ์ด์ง ์ง์ ์ ๊ฒ์์ฐฝ์ผ๋ก focus๋ฅผ ์ด๋์ํค๊ณ ๊ฒ์์ฐฝ์ ํค์๋๋ฅผ ์ ๋ ฅํ ์ํ์์ ๊ฒ์์ฐฝ ์ฌํด๋ฆญ์ ๊ธฐ์กด์ ํค์๋๋ฅผ ์ญ์ ํ๋ผ๋ ๋ฌธ์ ์๋ค. ๋น๊ต์ ์ฝ๊ฒ input์ autofocus ์์ฑ์ true๋ก ํ๊ณ , click ์ด๋ฒคํธ ์, input value๋ฅผ ๋น ๋ฌธ์์ด๋ก ๋ฐ๊พธ๊ฒ ํ์ฌ์ ํด๊ฒฐํ์๋ค.
๋ฐ์ดํฐ ๋ถ๋ฌ์ค๋ ์ค์์ ์ฌ์ฉ์์๊ฒ ์๋ฆฌ๋ UI๋ฅผ ๋ง๋๋ ๋ฌธ์ ์๋ค. ๋ฐ์ดํฐ๊ฐ ๋ก๋๋๋ฉด div.SearchResult ์ ํ์ ํ๊ทธ ์์ innerHTML ์์ฑ๊ฐ์ผ๋ก ์ ๋ณด๋ฅผ ๋ฃ๋ ๊ตฌ์กฐ์๋๋ฐ, ๋ฐ์ดํฐ ๋น๋๊ธฐ ์์ฒญ ์ ์ innerHTML๋ก ๋ก๋ฉ์ค์ด๋ผ๋ ํ ์คํธ๋ฅผ ๋ฃ์ด์ฃผ์ด์ ์ด์ฐจํผ ๋ฐ์ดํฐ ๋ก๋๊ฐ ๋๋ฉด innerHTML๋ก ์ ๋ณด๊ฐ ๋ฎ์ด์จ์ง๋ ๊ฒ์ ์ด์ฉํด์ ๊ตฌํํ์๋ค.
๋ ๋ ํจ์ ๋ด๋ถ ์กฐ๊ฑด๋ฌธ ์ฒ๋ฆฌ๋ก ์๋ต๋ฐ๋ ๋ฐ์ดํฐ๋ฐฐ์ด ๊ธธ์ด๊ฐ 0์ผ๊ฒฝ์ฐ, โ๋ฐ์ดํฐ ์์โ ์ด๋ผ๋ ํ ์คํธ๋ฅผ ๋ฃ์ด์ฃผ์๋ค.
ํค์๋๋ฅผ ๊ฒ์ํ์ ๋, ์ต๊ทผ ๊ฒ์์ด 5๊ฐ๋ฅผ ๋จ๊ธฐ๊ณ ํด๋น ๊ฒ์์ด๋ฅผ ๋๋ฅด๋ฉด ๊ฒ์๋ ๊ฐ๋ฅํ๊ฒ๋ ํ๋ ๋ฌธ์ ์๋ค. ํ ์คํธ ์ค์๋ ๊ฒ์์ด 5๊ฐ ๋ณด์ฌ์ง๋ ๊ฑฐ๋ง ๊ตฌํํ๋๋ฐ ํ ์คํธ ๋๋๊ณ ๊ฒ์์ด ํด๋ฆญ ์, ๊ฒ์์ด ๊ฐ๋ฅ๋๊ฒ๋ ๋ค์ ๊ตฌํํด๋ณด์๋ค.
const TEMPLATE = '<input type="text">'
class SearchInput {
$latestDOM = null
latest_arr = []
onSearch = null
constructor({ $target, onSearch }) {
const $searchInput = document.createElement('input')
const $latestDOM = document.createElement('div')
this.$latestDOM = $latestDOM
this.$searchInput = $searchInput
$searchInput.autofocus = true
this.$searchInput.placeholder = '๊ณ ์์ด๋ฅผ ๊ฒ์ํด๋ณด์ธ์.|'
this.onSearch = onSearch
$searchInput.className = 'SearchInput'
$target.appendChild($searchInput)
$target.appendChild($latestDOM)
$searchInput.addEventListener('keypress', e => {
if (e.keyCode === 13) {
this.latest_arr.unshift(e.target.value)
if (this.latest_arr.length > 5) {
this.latest_arr.pop()
} else {
}
console.log(this.latest_arr, e.target.value)
this.render()
onSearch(e.target.value)
}
})
$searchInput.addEventListener('click', () => {
$searchInput.value = ''
})
console.log('SearchInput created.', this)
}
render() {
this.$latestDOM.innerHTML = this.latest_arr
.map(item => {
return `<span class="latest" style="cursor:pointer; border:1px solid black; padding: 3px; margin:3px">${item}</span>`
})
.join('')
this.$latestDOM.querySelectorAll('.latest').forEach(($item, index) => {
$item.addEventListener('click', e => {
this.onSearch(this.latest_arr[index])
})
})
}
}
๋จผ์ ๊ฒ์์ด๊ฐ ์ ์ฅ๋ DOM ์ ์์ฑํ๊ณ , ๋ถ๋ชจ ๋ ธ๋์ DOM์ ์ถ๊ฐํด์ค ๋ค, ๊ฒ์(keypress) ์, innerHTML ์ ํตํด, ๋ด๋ถ ์์๋ค๋ ๋ง๋ค๊ณ , click ์ด๋ฒคํธํธ๋ค๋ฌ๋ ๊ฐ์ด ๋ฑ๋กํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํด์ฃผ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๊ธฐ ์ฝ๋์์๋ ๊ฒ์ ์ด๋ฒคํธ๊ฐ keypress
๊ฐ ์๋๋ผ keyup
์ด์๋ค. ๊ทธ๋ฐ๋ฐ keyup ์ด๋ฒคํธ ๋ฐ์์, ํค์๋๊ฐ ์์ด์ผ ์๋ ๋ฌธ์ ๊ฐ ์์ง๋ง ํ๊ธ ํค์๋์ผ ๊ฒฝ์ฐ, ๋๋ฒ ์ณ์ง๋ ํ์์ด ์ผ์ด๋์ keypress ๋ก ๋ฐ๊พธ์ด์ฃผ์๋ค.
์๋ก๊ณ ์นจ์, ๋ง์ง๋ง ๊ฒ์๊ฒฐ๊ณผ๋ฅผ ์ ์งํ๋ผ๋ ๋ฌธ์ ๋ฐ, ํ ์คํธ ์ค์๋ ์ฟ ํค๋ฅผ ์ด์ฉํด์ผ๋ ์ง ์ด๋ป๊ฒ ํด์ผ๋ ์ง ๊ฐ์ด ์์์ ํ ์คํธ ์ค์๋ ํด๊ฒฐํ์ง ๋ชปํ๊ณ ํ ์คํธ ๋๋ ํ์ ๋ค์ ๊ตฌํํด๋ณด์๋ค.
window ๊ฐ์ฒด ๋ด๋ถ์ localStorage
์์ฑ์ ์ด์ฉํ๋ฉด ๋ง์ง๋ง ๊ฒ์ ํค์๋๋ฅผ ๋ก์ปฌ์ ์ ์ฅํ ์ ์์๋ค.
myStorage = window.localStorage
localStorage ์์ฑ์ setItem() ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ๋ง์ง๋ง ๊ฒ์๊ฒฐ๊ณผ๋ฅผ ๋ก์ปฌ์ ์ ์ฅํด๋๋ค๊ฐ ๋์ค์ ์๋ก๊ณ ์นจํ์ฌ๋ DOM์ด ๋ก๋๋ ํ, getItem() ๋ฉ์๋๋ฅผ ์ด์ฉํด ์ ์ฅํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ด์ฉํด ํฌ๋กค๋ง๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๋ฉด ์ ์์ ์ผ๋ก ๋ง์ง๋ง ๊ฒ์๊ฒฐ๊ณผ๋ฅผ ์ ์งํ๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
์๋ก์ด api ์ฃผ์(GET /api/cats/random50
)๋ฅผ ์ด์ฉํด ๋ฒํผ ํด๋ฆญ์ ๋๋ค ๊ณ ์์ด๋ค์ด ํ๋ฉด์ ๊ทธ๋ ค์ง๋ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ๋ฌธ์ ์ธ๋ฐ, ํ์๋ฌธ์ ์ธ ๊ฒ๋ ๋ชจ๋ฅด๊ณ ์์์ ์๊ฐ์ ๋ค์จ๋ฒ๋ ค์ ๋ชปํ์์ง๋ง ํ
์คํธ ๋๋๊ณ ํ์ด๋ณด๋ ๊ฐ๋จํ๊ฒ ๋ง๋ค ์ ์์๋ค.
// App.js
this.searchInput = new SearchInput({
$target,
onSearch: keyword => {
api.fetchCats(keyword).then(({ data }) => this.setState(data))
},
onRandomClick: () => {
api3.fetchCats().then(({ data }) => this.setState(data))
},
})
App.js ์ SearchInput ๊ฐ์ฒด์ onRandomClick ๋ฉ์๋๋ฅผ ์ ๋ฌํ๋ค. ๊ฒ์์ ์๋ต๋ฐ๋ ๋ฐ์ดํฐ ํ์์ด ๊ฐ์์ api๋ช ๋ง ๋ฐ๊ฟ์ฃผ์๋ค.
๊ทธ๋ฆฌ๊ณ SearchInput.js ์์ ๋ฒํผ์ ๋ง๋ค๊ณ ๊ทธ ๋ฒํผ click ์ด๋ฒคํธํธ๋ค๋ฌ์ ์ ๋ฌ๋ฐ์ onRandomClick() ๋ง ๋ฃ์ด์ฃผ๋ฉด ๋๋ ๋ฌธ์ ์๋ค.
lazy load ๊ฐ๋ ์ด ์์ํ๋๋ฐ ๋์ค์ ๋ ์ก๊ณ ํ์ต์ ํด์ผ๊ฒ ๋ค.
์คํฌ๋กค์ด ๋ฐ๋ฅ์ ๋ฟ์์ ์, ๋ค์ํ์ด์ง๋ฅผ ๋ก๋ฉํ๋ผ๋ ๋ฌธ์ ์ด๋ค.
window ๊ฐ์ฒด์ scroll
์ด๋ฒคํธํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํ๊ณ scrollHeight์ scrollY,clientHeight๋ฅผ ์ด์ฉํด์ ์คํฌ๋กค์ด ๋ฐ๋ฅ์ ๋ฟ์ ๋ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ ๊ฒ๊น์ง๋ ํ๋๋ฐ ๋ค์ ํ์ด์ง๋ก ๊ฐ๋ ๋ฐฉ๋ฒ์ ๋ชจ๋ฅด๊ฒ ๋ค. api ์์ฒญ๋ถ๋ถ์๋ ํ์ด์ง๋ถ๋ถ์ด ์์ด์ ์ฐ์ ์ console์ฐฝ์๋ง ๋จ๊ฒ๋ํ์๋ค.
์ดํดํ์ง ๋ชปํ๋ค. ใ
ES7 ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฌธ๋ฒ์ธ async await ์ ์ฌ์ฉํด์ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ๋ผ๋ ๋ฌธ์ ์๋ค.
๋ณ๊ฒฝ์ .
const api3 = {
fetchCats: () => {
return fetch(`${API_ENDPOINT}/api/cats/random50`).then(res => res.json())
},
}
๋ณ๊ฒฝํ.
const api3 = {
fetchCats: async () => {
const response = await fetch(`${API_ENDPOINT}/api/cats/random50`)
return response.json()
},
}
๋ณ๊ฒฝํ.
const api3 = {
fetchCats: async () => {
const response = await fetch(`${API_ENDPOINT}/api/cats/random50`)
if (response.status === 200) {
return response.json()
} else {
console.error(`${response}์๋ฌ : ${response.statusText}`)
}
},
}
๊ทผ๋ฐ ์ด๊ฒ ๋ง๋ ๋ต์ธ์ง๋ ์ ๋ชจ๋ฅด๊ฒ ๋ค. ๐
SearchResult ์ ๊ฐ ์์ดํ
์ ํด๋ฆญํ๋ ์ด๋ฒคํธ๋ฅผ Event Delegation
๊ธฐ๋ฒ์ ์ด์ฉํด ์์ ํด์ฃผ์ธ์. ๋ผ๋ ๋ฌธ์ ์๋ค.
์ฆ, ๊ฒ์ํด์ ๋์จ ๊ณ ์์ด๋ฅผ ํด๋ฆญํ ๋, ์ด๋ฒคํธํธ๋ค๋ฌ๋ฅผ ๊ณ ์์ด ์ด๋ฏธ์ง๊ฐ ์๋๋ผ ๊ณ ์์ด ์ด๋ฏธ์ง ์ปจํ ์ด๋์ ๋ฑ๋กํ๋ ๋ฌธ์ ์๋ค.
๋จผ์ ๊ธฐ์กด์ ์ฝ๋.
this.$searchResult.querySelectorAll('.item').forEach(($item, index) => {
$item.addEventListener('click', () => {
this.onClick(this.data[index])
})
})
์์ ํ ๋ถ๋ถ.
this.$searchResult.innerHTML = this.data
.map(
(cat, i) => `
<div class="item">
<img src=${cat.url} alt=${cat.name} name=cat${i}/>
</div>
`
)
.join('')
๊ณ ์์ด ์ฌ์ง๋ค์ name์์ฑ์ cat + index๋ฅผ ๋ถ์ฌ์ฃผ์๋ค.
this.$searchResult.addEventListener('click', e => {
if (e.target.name) {
this.onClick(this.data[e.target.name.substr(3, e.target.name.length - 4)])
}
})
๊ทธ๋ฆฌ๊ณ ๊ณ ์์ด ์ฌ์ง ์ปจํ ์ด๋์ click ์ด๋ฒคํธํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํ๊ณ , ํด๋ฆญํ ๋ถ๋ถ์ด ์ด๋ฏธ์ง๊ฐ ์๋ ๊ฒฝ์ฐ ๊ทธ๋ฅ ๋์ด๊ฐ๋๋ก ์กฐ๊ฑด๋ฌธ ์ฒ๋ฆฌ๋ ํด์ฃผ์๋ค. ํ์ํ ๋ฐ์ดํฐ๋ index ๋ถ๋ถ์ด๋ฏ๋ก ์์ cat๋ถ๋ถ์ substr()๋ก ์ ๊ฑฐํด์ฃผ์๋ค.
๋ฌธ์
* Test suite์ ๊ฐ test ์ ๋ชฉ์ ์ ์ดํดํ๊ธฐ ์ฝ๊ฒ ๊ธฐ์ ํด์ฃผ์ธ์. ์๋ฅผ ๋ค์ด,
isNumber test (x)
isNumber ํจ์๋ number type ์ argument ๋ฅผ ๋ฐ์ผ๋ฉด True ๋ฅผ ๋ฆฌํดํฉ๋๋ค. (o)
* ๊ฐ ์ปดํฌ๋ํธ ๋ด๋ถ์ ์๋ ํจ์๋ค์ด๋, Util ํจ์๋ค์ ํ
์คํธ ํ ์ ์๊ฒ ๋ถ๋ฆฌํฉ๋๋ค.
* ์กฐ๊ฑด๋ฌธ์ด ์๋ ํจ์์ ๊ฒฝ์ฐ, edge case์ ๋ํ ํ
์คํธ๋ฅผ ์ค๋นํฉ๋๋ค.
* ํ
์คํธ ์ฝ๋ ๋ด์์ ๊ฐ ํ
์คํธ๋ง๋ค ๋ฐ๋ณต์ ์ผ๋ก ํ์ํ ๋ถ๋ถ์ life cycle ํจ์๋ฅผ ์ด์ฉํด ๊ด๋ฆฌํ๋๋ก ํฉ๋๋ค.
์ ํ ๋ฌด์จ๋ง์ธ์ง ๋ชจ๋ฅด๊ฒ ์ด์ PASS..ํ์๋ค
์ด๋ฒ ํ ์คํธ๋ ์ค๋ ฅ์๋ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ฅผ ์ฑ์ฉํ๋ ๊ฒ์ด ๋ชฉ์ ์ด์ง๋ง, ํ๋ก ํธ์๋ ๋ถ์ผ๋ฅผ ๊ณต๋ถํ๋ ์ ์ฅ์์๋ ์ ๋ง ์ข์ ๊ฒฝํ์ด ๋์๋ ๊ฒ ๊ฐ๋ค. ๊ฐ์ธํ๋ก์ ํธ๋ฅผ ํ๋ฉด์ ์ ํด๋ณด์ง ๋ชปํ๋ ์ฌ๋ฌ๊ฐ์ง ์ฌ์ฉ์์ธก๋ฉด ํน์ ๊ฐ๋ฐ์์ธก๋ฉด์ ์ด์๋ค์ด๋ lazy load๋ ์ด๋ฒคํธ์์๊ฐ์ ์์ํ๋ ๊ฐ๋ ๋ค๋ ์๊ฒ ๋์ด์ ๋๋ฌด ์ข์๋ค.
๊ทธ๋ฆฌ๊ณ ํ ์คํธ๋ฅผ ๋ง์น๊ณ ๋ด ์์ ์ด ์์ง ์ ๋ง ๋ง์ด ๋ถ์กฑํ๋ค๊ณ ๋๋ผ๊ฒ ๋์๋๋ฐ, ์์ผ๋ก๋ ๊ฐ์ธ๊ณต๋ถ๋ ์ค์ํ์ง๋ง ์๊ฐ์ด ๊ณ ์ด์ง ์๊ฒ ์ด๋ฌํ ์ธ๋ถ ํ ์คํธ๋, ์ฌ๋๋ค๊ณผ ์ฝ๋์ ๋ํด์ ์ด์ผ๊ธฐํ๋ ์๋ฆฌ๋ ๋ง์ด ๊ฐ์ ธ์ผ๊ฒ ๋ค๋ ์๊ฐ์ ํ๊ฒ ๋์๋ค.
ํด๊ฒฐํ์ง ๋ชปํ ๋ฌธ์ ๋ ๋ถ์กฑํ๋ค๊ณ ์๊ฐ๋๋ ๋ถ๋ถ์ ๋ฐ๋ก ์ ๋ฆฌํด๋๋ค๊ฐ ๋ค์ ๊ณต๋ถํด์ผ๊ฒ ๋ค.
๐ 2020-03-19