March 06, 2020
๋ฐฐ๊ฒฝํ๋ฉด์ ์ฐพ์ ๋, alpacoders๋ผ๋ ๋ํ Wallpaper ์ฌ์ดํธ๋ฅผ ์์ฃผ ์ด์ฉํ๋ ํธ์ธ๋ฐ
ํค์๋๋ฅผ ๊ฒ์ํ์ ๋, 30๊ฐ์ฉ ์ ๋ณด๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ๋ค์ํ์ด์ง๋ฅผ ํด๋ฆญํด์ ๋์ด๊ฐ์ผ์ง๋ง ๋ค์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋๋ฐ์ ๋ถํธํจ์ ๋๊ปด ํค์๋๋ฅผ ๊ฒ์ํ์ ๋ ํด๋ฆญํ๋ ๋ฒ๊ฑฐ๋ก์ ์์ด ํ๋์ ์ด๋ฏธ์ง๋ค์ ๋ณด๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ ์น์ฌ์ดํธ๋ฅผ ๊ธฐํํด๋ณด์๋ค.
์ ํต์ ์ธ web๊ฒ์ํ -> SPAํํ
์ด๋ฒ์๋ ๋ฆฌ์กํธ functial component + hooks ๊ฐ ์๋๋ผ class component๋ฅผ ์ด์ฉํ์๋ค.
page ๋ทฐ
: ์ฐ์ธก ์๋จ์ page ๋ทฐ๋ฅผ ๋ง๋ค์ด์ ํด๋น ํค์๋์ ๋ํ ํ์ฌ ์ด๋ฏธ์ง ์ / ์ ์ฒด ์ด๋ฏธ์ง ์ ๋ฅผ ๋ณด์ด๋๋ก ํ๋ค.๊ฒ์ ์ฐฝ
: ์ค์์ ๊ฒ์ ์ฐฝ์ ๋์๋ค.์ค๋ช
: ๊ฒ์ ์ฐฝ ์๋์๋ ์ดํ๋ฆฌ์ผ์ด์
์ ๋ํ ์ค๋ช
์ ์ ์๋ค.์ด๋ฏธ์ง ๋ณด๊ธฐ๋ฐฉ์ ๋ฒํผ
: ์ค๋ช
๋ฐ์ ์ด๋ฏธ์ง ๋ณด๊ธฐ ๋ฐฉ์์ ๋ํ ๋ฒํผ์ ๋์๋ค.keyword : captain
z-index
: ํด๋น ํ๋ฉด์ ๋ฉ์ธํ๋ฉด์ z-index๋ณด๋ค ๋ฎ๊ฒ ํ์ฌ ๊ฒ์๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด์ ๋ค์ ๊ฒ์์ ํ๊ฑฐ๋ ๋ฒํผ๋ฑ์ ์ด์ฉํ ์ ์๊ฒ ํ์๋ค.image Link
: ์ด๋ฏธ์ง๋ฅผ ํฌ๋กค๋ง ํ ๋, ํด๋น url์ ๊ฐ์ด ํฌ๋กค๋งํ์ฌ ์ด๋ฏธ์ง๋ฅผ ํด๋ฆญํ๋ฉด ํด๋น ์ด๋ฏธ์ง์ ๊ณ ํ์ง ์ด๋ฏธ์ง๋ฅผ ๋ณผ ์ ์๋ ๋งํฌ๋ก ์ด๋๋๊ฒ ํ์๋ค.๋ฒํผ์ ํด๋ฆญํ๋ฉด state์ gridmode๋ฅผ ๋ณ๊ฒฝํ๋๋ก ํ์๋ค.
<div
onClick={() => {
this.setState({
gridmode: 10,
})
}}
>
10๊ฐ์ฉ๋ณด๊ธฐ
</div>
๊ทธ๋ฆฌ๊ณ state์ gridmode ์ ๋ฐ๋ผ ์ด๋ฏธ์ง๋ฅผ ๋ ์ด์์์ ๋ณ๊ฒฝํ๋๋ก ํ์๋ค.
{
this.state.gridmode === 10 &&
this.state.result_arr.map((item, i) => (
<a
key={i}
href={'https://wall.alphacoders.com/' + item.link}
target="_blank"
>
<img style={{ width: '10%' }} src={item.img}></img>
</a>
))
}
class Layout extends Component {
state = {
searchingName: '',
page: 1,
imageNumber: 0,
imageMaxNumber: 0,
gridmode: 4,
result_arr: []
}
๊ธฐ์กด ํ๋ก์ ํธ์ ํ์ ์ดํ๋ฆฌ์ผ์ด์
์ ์ํ๋ค์ functional component + hooks๋ก ๋ง๋ค์์๋๋ฐ, ์ด๋ฒ ํฌ๋กค๋ง ์ดํ๋ฆฌ์ผ์ด์
์ class component
+ state
๋ก ๋ง๋ค์๋ค.
์ํ๋ณ์๋
searchingName
: ๊ฒ์์ด
page
: ํด๋น ๊ฒ์์ด์ ๋ํ ํฌ๋กค๋ง ํ์ด์ง ์ธ๋ฑ์ค
imageNumber
: ํด๋น ๊ฒ์์ด์ ๋ํ ์ด๋ฏธ์ง ์
imageMaxNumber
: ํด๋น ๊ฒ์์ด์ ๋ํ ์ ์ฒด ์ด๋ฏธ์ง ์
gridmode
: ์ด๋ฏธ์ง ๋ณด๊ธฐ๋ฐฉ์
result_arr
: ์ด๋ฏธ์ง ์ ๋ณด๊ฐ ๋ค์ด๊ฐ ๋ฐฐ์ด
ajax ๋ฐฉ์์ ๋ด์ฅ API fetch
๋ฅผ ์ฌ์ฉํ๋ค.
const url = 'url + searchingName info + page info'
fetch(url).then(res => {
return res.text()
})
url ์ ๋ํ html์ text() ๋ฉ์๋๋ก ๊ฐ์ ธ์ค๊ฒ๋ ํจ.
์ฃผ์๊ฐ ๋ค๋ฅธ ์๋ฒ์ ์์ฒญ ๋๋ฌธ์ CORS ์ด์๊ฐ ๋ฐ์ํ๋๋ฐ ๋ด๊ฐ ์๋ฒ๋ฅผ ์ ์ดํ ์ ์๋ ์ ์ฅ์ด์ด์
https://cors-anywhere.herokuapp.com
proxy API๋ฅผ ์ด์ฉํ๋ค.
๐ฅ ํน์๋ ํด์ nodeJS์์ ํฌ๋กค๋ง์ฝ๋๋ฅผ ๋๋ ค๋ดค๋๋ฐ CORS์ด์๊ฐ ์์๋ค. ๋ค์ ๊ณต๋ถํ ๊ฒ!
๋ถ๋ฌ์จ html ์ ๋ณด๋ฅผ ํ์ฑํ๊ธฐ ์ํด cheerio ๋ชจ๋์ ์ฌ์ฉํ๋ค.
npm i cheerio
const cheerio = require('cheerio')
fetch(url)
.then(res => {
return res.text()
})
.then(text => {
const $ = cheerio.load(text)
let json = [],
id,
link,
img
const maxNum = Number(
$('#page_container > h1')
.text()
.split(' ')[8]
)
// โญ ๋ฐ์ํ ์ฒ๋ฆฌ
if (window.innerWidth < 1070) {
$('#page_container > div:nth-child(6) > div.thumb-container').each(function(i, elem) {
id = i
link = $(this)
.find('div.thumb-container > a')
.attr('href')
img = $(this)
.find('div.thumb-container > a.wallpaper-thumb > img')
.attr('data-src')
json.push({ id: id, link: link, img: img })
})
} else {
$('#page_container > div:nth-child(6) > div.thumb-container-big').each(function(i, elem) {
id = i
link = $(this)
.find('div.thumb-container > div.boxgrid > a')
.attr('href')
img = $(this)
.find('div.thumb-container > div.boxgrid > a > img')
.attr('data-src')
json.push({ id: id, link: link, img: img })
})
}
// โญ ๋ง์ง๋ง ํ์ด์ง ์ฒ๋ฆฌ
if (this.state.imageMaxNumber >= this.state.imageNumber) {
this.setState({
result_arr: this.state.result_arr.concat(json),
imageMaxNumber: maxNum,
imageNumber: this.state.imageNumber + 30
})
}
})
.catch(error => console.log(error))
}
๋ํ ํฌ๋กค๋ง์ ํ๋ ค๋ฉด ํด๋น ์น์ฌ์ดํธ์ css ์ ํ์์ ์ ๊ทผ์ ํด์ผํ๋๋ฐ ํด๋น์ฌ์ดํธ๋ ๋๋ฐ์ด์ค์ ๋ฐ๋ผ ์ ํ์๊ฐ ๋ฌ๋๋ค.
์ฆ, ๋ชจ๋ฐ์ผ๊ณผ ๋ฐ์คํฌํ css ์ ํ์๊ฐ ๋ฌ๋๋ค.
๊ทธ๋์ innerWidth๊ฐ 1070๋ณด๋ค ํด ๊ฒฝ์ฐ ๋ฐ์คํฌํ์ผ๋ก ์ธ์ํ๊ฒ๋, 1070 ์ดํ์ผ ๊ฒฝ์ฐ๋ ๋ชจ๋ฐ์ผ๋ก ์ธ์ํ๊ฒ๋ ์กฐ๊ฑด๋ฌธ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์๋ค.
์คํฌ๋กค ์ด๋ฒคํธ๋ก ์ธํ HTTP ์์ฒญ,์๋ต ๋ถ๋ถ์ด ์์ฒญ,์๋ต,์์ฒญ,์๋ต ์์ด ์๋ ์์ฒญ,์์ฒญ,์๋ต,์๋ต ์์ ๋น๋๊ธฐ ๋ฐฉ์
์ผ๋ก ์ธํด ๋ง์ง๋ง ํ์ด์ง์ ๊ฐ์ ๋ ๋ง์ง๋งํ์ด์ง์ ๋ํ ์์ฒญ์ ์ฌ๋ฌ๋ฒ ์๋ตํด์ ์ค๋ณต๋๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ๋ค.
๋ง์ง๋ง ํ์ด์ง๊ฐ ์๋๋ผ ์ค๊ฐ ํ์ด์ง์ผ ๊ฒฝ์ฐ, ์ฌ๋ฌ ์์ฒญ์ ๋ฐ์๋ ์๊ด ์์ผ๋ ๋ง์ง๋ง ํ์ด์ง๋ ์ด๋ฏธ์ง๋ฅผ ์ค๋ณต์ผ๋ก ๊ฐ์ ธ์ค๋ ์ค๋ฅ๋ฅผ ๋ณ๋๋ค.
๊ทธ๋์ ์์ฒญ
ํ๋ ๋ถ๋ถ์ด ์๋ ์๋ต
๋ถ๋ถ์ธ ๋ฐ์ดํฐ๋ฅผ ๋ฃ๋ ๊ณผ์ ์์ ์กฐ๊ฑด๋ฌธ์ ๋ฃ์ด ์ด๋ฅผ ํด๊ฒฐํ์๋ค.
componentDidMount() {
window.addEventListener(
'scroll',
_.debounce(() => {
if (
window.scrollY + document.documentElement.clientHeight >
document.documentElement.scrollHeight - 260 &&
this.state.imageMaxNumber >= this.state.imageNumber
) {
this.setState(
{
page: this.state.page + 1
},
() => this.crawling()
)
}
}, 1000)
)
}
์คํฌ๋กค ์ด๋ฒคํธ๋ componentDidMount()
๋ฉ์๋๋ฅผ ํตํด ์ถ๊ฐํด์ฃผ์๋ค.
๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง ํ์ด์ง์๋ ์ด๋ฒคํธ์ ๋ํ ์ฝ๋ฐฑํจ์๊ฐ ์คํ๋์ง ์๋๋ก ์กฐ๊ฑด๋ฌธ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์๋ค.
๊ทธ๋ฆฌ๊ณ ์คํฌ๋กค ์ด๋ฒคํธ ๋ถ๋ถ์ lodash ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ _.debounce ๋ฉ์๋๋ฅผ ๋ฃ์ด์ฃผ์ด ์ต์ ํ ์์ ๋ ํด์ฃผ์๋ค.
npm i lodash
const _ = require('lodash')
submitHandler = e => {
e.preventDefault()
this.setState(
{
result_arr: [],
imageNumber: 0,
imageMaxNumber: 0,
page: 1,
},
() => {
this.crawling()
}
)
}
ํฌ๋กค๋ง ํจ์๋ฅผ state๊ฐ ๋ฐ๊พผ ํ์ ์คํ๋๋๋ก callback
์ ์ด์ฉํ๋ค.
๋ฌดํ์คํฌ๋กค ๋ถ๋ถ๋ ๋ง์ฐฌ๊ฐ์ง callback์ ์ฌ์ฉํ๋ค.
์ด๋ฒ ํ๋ก์ ํธ๋ฅผ ํ๋ฉด์ ๊ฐ์ฅ ๋งํ๊ณ ์ดํดํ๋ ค๊ณ ์ ์ผ๋ ๋ถ๋ถ์ด javascript์ ๋น๋๊ธฐ๋ฐฉ์์ด์๋ ๊ฒ ๊ฐ๋ค.
๋ฆฌ๋์ค hooks (useDispatch(), useSelector())๋ ํจ์ํ ์ปดํฌ๋ํธ์๋ง ์ธ ์ ์๋ค๋ ๊ฒ์ ๋ง์ง๋ง์ ๊นจ๋ฌ์๋ค..
๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๋ฆฌ๋์๋ฅผ ์ฅ์ฐฉํ ์ ์๊ฒ ์ง๋ง, ๋ค์๋ถํฐ๋ ์ต๋ํ ํจ์ํ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์๋ ์๊ฐ์ ํ๋ฉฐ, ๋ฆฌ๋์ค ๊ธฐ๋ฅ์ ํฌ๊ธฐํ๋ค.
๊ทธ๋ผ ๋ ์๋ก ์ ์ผ๋ก ํจ์ํ์ ํ์ง ๋ชปํ ์ด์ ๋?
์ฒ์์๋ ํจ์ํ ์ปดํฌ๋ํธ + hooks๋ฅผ ์ฌ์ฉํด์ ๋ง๋ค๋ ค๊ณ ํ์ผ๋, hooks ๋ก ๋ง๋ ์ํ๋ค์ด ๊ณ์ ๋ฆฌ์ ๋๊ณ ํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ๊ทธ๋ฆฌ๊ณ useState hooks ์ setState ๋ถ๋ถ์์ ์ฝ๋ฐฑํจ์๋ฅผ ๋ง๋ค ์ค ๋ชฐ๋ผ ๊ทธ๋ฅ class ๋ก ๋ค์ ์์ฑํ์๋ค.
hooks์ ์๋ฆฌ๋ฅผ ์ข๋ ๊น๊ฒ ํ์ตํ ํ์๋ฅผ ๋๊ผ๋ค.
์คํฌ๋กค์ ๋ด๋ฆฌ๋ฉด ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ธด ํ๋๋ฐ ๋ก๋ฉ ์ ๋๋ฉ์ด์ ์ ๋ฃ์ง ์์๋ค.
์ฌ์ฉ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋์ง ๋ง๋์ง ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์ ux์ ์ธ ๋ฉด์์ ๋ณ๋ฃจ๋ค..
๋ก๋ฉ ์ ๋๋ฉ์ด์ ๋ ๊ณต๋ถํ๊ธฐ!
์คํฌ๋กค์ด ๋ต๋ตํ๋ค๋ฉด
10๊ฐ์ฉ ๋ณด๊ธฐ
๋ฒํผ์ ๋ง๊ตฌ ํด๋ฆญํ์. ํฌ๋กค๋ง ์ฝ๋๋ฅผ ์ฌ๊ธฐ์๋ ๋ฃ์ด๋์ด์ ๋ฐ์ดํฐ๋ฅผ ๋ ๋นจ๋ฆฌ ๋ถ๋ฌ์ฌ ์ ์๋ค.