๐Ÿ”ฎ ์†Œ๋งˆ๋ฒ• ํ”„๋กœ์ ํŠธ -9 (loading)

์Šคํ”Œ๋ž˜์‹œ ํ™”๋ฉด๊ณผ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์ฐฝ์„ ๊ตฌํ˜„ํ•œ ํฌ๋กค๋ง์ด๋ฏธ์ง€ ์—ด๋žŒ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜

ํฌ๋กค๋ง์€ ์†Œ๋งˆ๋ฒ• ํ”„๋กœ์ ํŠธ - 4 (crawling)์— ์žˆ๋Š” ํฌ๋กค๋ง ํ•จ์ˆ˜๋ฅผ ์žฌ์‚ฌ์šฉํ•˜์˜€๋‹ค.


#. Project Map


์ œ์ž‘๋…ธํŠธ ํ•œ๋ˆˆ์—๋ณด๊ธฐ[์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ]

1. ๋ ˆ์ด์•„์›ƒ

1-1. ์Šคํ”Œ๋ž˜์‹œ ํ™”๋ฉด

loading1

LOADING ํ…์ŠคํŠธ์— ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ฃผ์—ˆ๋‹ค.

1-2. ๋ฐ์ดํ„ฐ ๋กœ๋”ฉํ™”๋ฉด

loading2

ํšŒ์ „ํ•˜๋Š” ์›ํ˜• ์—˜๋ฆฌ๋จผํŠธ ์• ๋‹ˆ๋ฉ”์ด์…˜..

1-3. ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ์™„๋ฃŒํ™”๋ฉด

loading3

โ€œHello cats!โ€

1-4. ์ปดํฌ๋„ŒํŠธ

<BackgroundContainer>
  <ContentsMenubar name="loading" />
  <MainLoader id="main_loader">
    <MainLoaderText transX="12px" delay="0.35s" color="firebrick">
      LOADING
    </MainLoaderText>
    <MainLoaderText transX="10px" delay="0.28s" color="darkgoldenrod">
      LOADING
    </MainLoaderText>
    <MainLoaderText transX="8px" delay="0.21s" color="darkolivegreen">
      LOADING
    </MainLoaderText>
    <MainLoaderText transX="6px" delay="0.14s" color="darksalmon">
      LOADING
    </MainLoaderText>
    <MainLoaderText transX="4px" delay="0.07s" color="cornflowerblue">
      LOADING
    </MainLoaderText>
    <MainLoaderText delay="0s" color="white">
      LOADING
    </MainLoaderText>
  </MainLoader>
  <Loader id="first_loader"></Loader>
  <Description>
    <p
      style={{
        fontSize: '20px',
        fontFamily: 'escore7',
        color: 'pink',
        textShadow: '2px 2px 4px black',
        textAlign: 'center',
      }}
    >
      ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ํฌ๋กค๋ง๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค์„ธ์š”
    </p>
    <p style={{ fontSize: '10px', fontFamily: 'escore5', marginTop: '10px' }}>
      Image data from{'  '}
      <a href="https://wall.alphacoders.com/" target="_blank">
        alphacoders.com
      </a>
    </p>
  </Description>
  <ButtonContainer>
    <button id="cat_button">๊ณ ์–‘์ด(default)</button>
    <button id="dog_button">๊ฐ•์•„์ง€</button>
  </ButtonContainer>
  <TextContainer id="myDiv" style={{ display: 'none' }}>
    {imgArr.map((item, i) => {
      return <img key={i} src={item.img}></img>
    })}
  </TextContainer>
</BackgroundContainer>

๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” styled-component๋กœ ๊ตฌ์„ฑํ•˜์˜€๋‹ค.

  1. MainLoader, MainLoaderText : ์Šคํ”Œ๋ž˜์‹œ ๋กœ๋”ฉํ™”๋ฉด

ํ•œ๋ˆˆ์— ๋ณด๊ธฐ ์ข‹์ง€ ์•Š์•„์„œ ์ฝ”๋“œ์Šคํ”Œ๋ฆฌํŒ…์ด ํ•„์š”ํ•ด๋ณด์ž„ ใ… ใ…  setTimeout() ๋ฉ”์†Œ๋“œ๋กœ 3์ดˆ๊ฐ„ ์ง€์†๋˜๊ฒŒ ํ•ด์ฃผ์—ˆ๋‹ค.

  1. Loader : ๋ฐ์ดํ„ฐ ๋กœ๋”ฉํ™”๋ฉด

๋กœ๋”ฉํ™”๋ฉด์€ div ํƒœ๊ทธ๋กœ ๊ตฌ์„ฑํ–ˆ๊ณ  fetch์˜ then ์ฒด์ด๋‹์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ถˆ๋Ÿฌ์™€์ง€๋ฉด ๋กœ๋”ฉํ™”๋ฉด์ด ์‚ฌ๋ผ์ง€๊ณ  ๋ฐ์ดํ„ฐ์ด๋ฏธ์ง€๊ฐ€ ๋ณด์—ฌ์ง€๋„๋ก ํ•ด์ฃผ์—ˆ๋‹ค.

  1. Description : ์„ค๋ช…์ฐฝ

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค๋ช… ์ฐฝ

  1. ButtonContainer : ํฌ๋กค๋ง ํ•จ์ˆ˜ ์‹คํ–‰๋ฒ„ํŠผ

๊ณ ์–‘์ด์™€ ๊ฐ•์•„์ง€ ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ํฌ๋กค๋งํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฒ„ํŠผ 2๊ฐœ Container

  1. TextContainer : ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ ๋ทฐ Container

ํฌ๋กค๋งํ•œ ๊ณ ์–‘์ด์™€ ๊ฐ•์•„์ง€ ์ด๋ฏธ์ง€๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๋ทฐ Container

์ดˆ๊ธฐ style ๊ฐ’์œผ๋กœ display:none์„ ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค.

2. ์ƒํƒœ๊ด€๋ฆฌ

const [imgLoadToggle, setImgLoadToggle] = useState(false)
const [imgArr, setImgArr] = useState([])

์ƒํƒœ๊ด€๋ฆฌ๋Š” ๋ฆฌ์•กํŠธ hooks๋ฅผ ์ด์šฉํ–ˆ๋‹ค.

  1. imgLoadToggle : ์ดˆ๊ธฐ๋กœ๋”ฉ ํŒ๋ณ„ ๋ณ€์ˆ˜

์ดˆ๊ธฐ DOM ๋กœ๋“œ์‹œ, ํ•œ๋ฒˆ๋งŒ ํฌ๋กค๋ง ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด boolean ๋ณ€์ˆ˜๋ฅผ ๋‘์—ˆ๋‹ค.

  1. imgArr : ์ด๋ฏธ์ง€๊ฐ€ ์ €์žฅ๋  ๋ฐฐ์—ด

ํฌ๋กค๋ง๋œ ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ๋“ค์ด ์ €์žฅ๋˜๋Š” ๋ฐฐ์—ด

3. ๋กœ๋”ฉ๋ฐ”

3-1. ์Šคํ”Œ๋ž˜์‹œ

์›นํŽ˜์ด์ง€ ์ดˆ๊ธฐ ์ง„์ž…์‹œ, ์‹คํ–‰๋˜๋Š” ์Šคํ”Œ๋ž˜์‹œ ํ™”๋ฉด.

const showFull = () => {
  document.getElementById('main_loader').style.display = 'none'
}
useEffect(() => {
  if (!imgLoadToggle) {
    crawling('cat')
    setImgLoadToggle(true)
    setTimeout(showFull, 3000)
  }
})

useEffect๋กœ DOM์ด ๋กœ๋“œ๋˜๋ฉด setTimeout()์œผ๋กœ 3์ดˆ๊ฐ„ ์Šคํ”Œ๋ž˜์‹œ ํ™”๋ฉด์ด ์‹คํ–‰๋˜๋„๋ก ํ•ด์ฃผ์—ˆ๋‹ค.

3์ดˆ๊ฐ€ ์ง€๋‚˜๋ฉด, main_loader ์—˜๋ฆฌ๋จผํŠธ๋ฅผ display:none ํ•˜์˜€๋‹ค.

๊ทธ๋ฆฌ๊ณ  setImgLoadToggle(true)๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ํ–ˆ๋‹ค.

3-2. ๋กœ๋”ฉ๋ฐ”

document.getElementById('cat_button').addEventListener('click', e => {
  e.stopImmediatePropagation()
  loadPage()
  crawling('cat')
})
document.getElementById('dog_button').addEventListener('click', e => {
  e.stopImmediatePropagation()
  loadPage()
  crawling('dog')
})

์šฐ์„  click ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋“ฑ๋กํ•˜๊ณ  ์„ธ๊ฐœ์˜ ํ•จ์ˆ˜ ๋ฐ ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰์ผ€ ํ•˜์˜€๋‹ค.


๐ŸŽ e.stopImmediatePropagation()

event.preventDefault()
// ํ˜„์žฌ ์ด๋ฒคํŠธ์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค.

event.stopPropagation()
// ํ˜„์žฌ ์ด๋ฒคํŠธ๊ฐ€ ์ƒ์œ„๋กœ ์ „ํŒŒ๋˜์ง€ ์•Š๋„๋ก ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค.

event.stopImmediatePropagation()
// ํ˜„์žฌ ์ด๋ฒคํŠธ๊ฐ€ ์ƒ์œ„๋ฟ ์•„๋‹ˆ๋ผ ํ˜„์žฌ ๋ ˆ๋ฒจ์— ๊ฑธ๋ฆฐ ๋‹ค๋ฅธ ์ด๋ฒคํŠธ๋„ ๋™์ž‘ํ•˜์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์ถœ์ฒ˜: https://programmingsummaries.tistory.com/313

์ค‘์š”

useEffect๊ฐ€ ์—ฌ๋Ÿฌ๋ฒˆ ์‹คํ–‰๋˜๋ฉฐ addEventListener๋„ ์—ฌ๋Ÿฌ๋ฒˆ ์‹คํ–‰๋˜์„œ ํฌ๋กค๋งํ•จ์ˆ˜๊ฐ€ ์—ฌ๋Ÿฌ๋ฒˆ ์‹คํ–‰๋˜์—ˆ๋‹ค.

๊ทธ๋ž˜์„œ stopImmediatePropagation() ๋ฅผ ํ†ตํ•ด ๋™์ผ ํ•จ์ˆ˜๋ฅผ ํ•œ๋ฒˆ๋งŒ ์‹คํ–‰ํ•˜๊ฒŒ๋” ํ•ด์ฃผ์—ˆ๋‹ค.


ํŽ˜์ด์ง€ ์ปจํŠธ๋กค loadPage(), showPage()

const loadPage = () => {
  document.getElementById('first_loader').style.display = 'block'
  document.getElementById('myDiv').style.display = 'none'
}
const showPage = () => {
  document.getElementById('first_loader').style.display = 'none'
  document.getElementById('myDiv').style.display = 'flex'
}

ํฌ๋กค๋ง ํ•จ์ˆ˜ ์‹œ์ž‘์‹œ loadPage()๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋ฉด showPage()๋ฅผ ์‹คํ–‰ํ•˜๊ฒŒ ํ•˜์˜€๋‹ค.

๊ฐ๊ฐ์˜ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋กœ๋”ฉ๋ฐ” ์—˜๋ฆฌ๋จผํŠธ์™€ ๋ฐ์ดํ„ฐ ๋ทฐ ์—˜๋ฆฌ๋จผํŠธ display๋ฅผ ์กฐ์ •ํ•ด์ฃผ์—ˆ๋‹ค.


crawling()

AJAX๋Š” fetch๋ฅผ ์‚ฌ์šฉํ–ˆ๊ณ , ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋Š” then ๋ฉ”์†Œ๋“œ ์ฒด์ด๋‹์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

ํฌ๋กค๋งํ•จ์ˆ˜๋„ ์ด์ „์— ๋ฐฐ๊ฒฝํ™”๋ฉด ํฌ๋กค๋ง ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ crawling()ํ•จ์ˆ˜๋ฅผ ์žฌ์‚ฌ์šฉํ•˜์˜€๋‹ค.

์†Œ๋งˆ๋ฒ• ํ”„๋กœ์ ํŠธ - 4 (crawling)

fetch(url)
  .then //...
  ()
  .then(json => {
    setImgArr(json)
    showPage()
  })

๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ๊ฐ€ ๋ชจ๋‘ ์ฝ์–ด์ง€๋ฉด, hooks๋ฅผ ํ†ตํ•ด imgArr์— ์ด๋ฏธ์ง€๋“ค์„ ์ €์žฅํ•˜๊ณ  showPage()๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ํ•˜์˜€๋‹ค.


4. css ์• ๋‹ˆ๋ฉ”์ด์…˜

css ์• ๋‹ˆ๋ฉ”์ด์…˜์€ styled-components์™€ keyframes๋ฅผ ์ด์šฉํ–ˆ๋‹ค.

์Šคํ”Œ๋ž˜์‹œ ํ™”๋ฉด, ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ๋ฐ”, ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ ๋ทฐ ์ด 3๊ฐœ์˜ ์—˜๋ฆฌ๋จผํŠธ์— ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ฃผ์—ˆ๋‹ค.

4-1. ์Šคํ”Œ๋ž˜์‹œ ์• ๋‹ˆ๋ฉ”์ด์…˜

ํ‚คํ”„๋ ˆ์ž„

const customAni = keyframes`
0%, 100% {
  transform: translatey(0); 
 }
 50% {
  transform: translatey(20px);
 }
`

์Šคํƒ€์ผ

const MainLoaderText = styled.div`
  position: absolute;
  font-size: 32px;
  text-shadow: 2px 2px 4px black;
  font-family: escore9;
  color: ${props => {
    return props.color
  }};
  animation: ${customAni} 1.5s ease-in-out infinite;
  animation-delay: ${props => {
    return props.delay
  }};
  margin-left: ${props => {
    return props.transX
  }};
`

์ปดํฌ๋„ŒํŠธ

<MainLoaderText transX="12px" delay="0.35s" color="firebrick">
  LOADING
</MainLoaderText>

styled-components ์˜ props๋กœ transX, delay, color๋ฅผ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  animation์€ transform: translatey()๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.


4-2. ๋กœ๋”ฉ๋ฐ” ์• ๋‹ˆ๋ฉ”์ด์…˜

ํ‚คํ”„๋ ˆ์ž„

const spin = keyframes`
  0% {
    transform: translate(-50%, -50%) rotate(0deg);
  }
  100% {
    transform: translate(-50%, -50%) rotate(360deg);
  }
`

์Šคํƒ€์ผ

const Loader = styled.div`
  position: fixed;
  top: 50%;
  left: 50%;
  border: 8px solid #f3f3f3;
  border-top: 8px solid #3498db;
  border-radius: 50%;
  width: 60px;
  height: 60px;
  transform: translate(-50%, -50%);
  animation: ${spin} 2s linear infinite;
`

์ปดํฌ๋„ŒํŠธ

<Loader id="first_loader"></Loader>

๋จผ์ € top:50%, left:50%์™€ transform: translate(-50%, -50%)๋ฅผ ํ†ตํ•˜์—ฌ ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ํ™”๋ฉด ์ค‘์•™์— ์˜ค๊ฒŒ ํ•˜์˜€๋‹ค.

๊ทธ๋ฆฌ๊ณ  animation์€ transform: rotate()๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.


4-3. ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ ๋ทฐ

ํ‚คํ”„๋ ˆ์ž„

const positionbottom = keyframes`
  from{ margin-top:150px; opacity:0 } 
  to{ margin-top:100px; opacity:1 }
`

์Šคํƒ€์ผ

const TextContainer = styled.div`
  margin-top: 100px;
  width: 100vw;
  justify-content: center;
  flex-wrap: wrap;
  animation: ${positionbottom} 1s linear;
`

์ปดํฌ๋„ŒํŠธ

<TextContainer id="myDiv" style={{ display: 'none' }}>
  {imgArr.map((item, i) => {
    return <img key={i} src={item.img}></img>
  })}
</TextContainer>

animation์€ margin๊ฐ’๊ณผ opacity๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์Šค๋ฅด๋ฅต ์˜ฌ๋ผ์˜ค๋Š” ํšจ๊ณผ๋ฅผ ์ฃผ์—ˆ๋‹ค.

5. ๊ฐœ์ธ์ ์ธ ํ”ผ๋“œ๋ฐฑ

5-1. lazy loading X

์†Œ๋งˆ๋ฒ• ํ”„๋กœ์ ํŠธ - 10 (lazyloading)


Written by@taenyKim
์›น ํ”„๋ก ํŠธ์—”๋“œ ๊ณต๋ถ€ ๋ธ”๋กœ๊ทธ / Learn in Public

GitHubFacebook