๐Ÿ” ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค FE ๋ฐ๋ธŒ์ฝ”์Šค 5๊ธฐ TIL 231030

Jun 2k (Jun2)ยท2023๋…„ 11์›” 1์ผ
2

๐Ÿ’ป Intro

๋ฌดํ•œ ์Šคํฌ๋กค์„ ํ™œ์šฉํ•œ ๊ณ ์–‘์ด ์‚ฌ์ง„์ฒฉ์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค์–ด๋ณด๋Š” ๊ฐ•์˜๋ฅผ ๋“ค์—ˆ์—ˆ๋‹ค.
๋ฌดํ•œ ์Šคํฌ๋กค์€ ์ธ์Šคํƒ€์™€ ๊ฐ™์€ SNS๋‚˜ ์‡ผํ•‘๋ชฐ์—์„œ๋„ ๊ฑฐ์˜ ํ•„์ˆ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.
๋ฌดํ•œ ์Šคํฌ๋กค์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ์‹์€ ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹์ด ์žˆ๋Š”๋ฐ ์ด๋ฅผ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.



๐Ÿง ์˜ค๋Š˜ ์ƒˆ๋กญ๊ฒŒ ๋ฐฐ์šด ๊ฒƒ

scroll ์ด๋ฒคํŠธ๋ฅผ ํ™œ์šฉํ•œ ๋ฌดํ•œ์Šคํฌ๋กค

์ถœ์ฒ˜: https://dmswl98-dev.tistory.com/17


์Šคํฌ๋กค ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด์˜ ๋†’์ด์™€ ์Šคํฌ๋กค์— ๋Œ€ํ•œ ๋†’์ด์˜ ์†์„ฑ๊ฐ’์€ ๋‹ค์Œ ๊ทธ๋ฆผ๊ณผ ๊ฐ™๋‹ค.

ํ•˜๋‚˜์”ฉ ์˜๋ฏธํ•˜๋Š” ๋ฐ”๋ฅผ ์•Œ์•„๋ดค๋‹ค.

  • window.innerHeight

    ์ถœ์ฒ˜: https://developer.mozilla.org/en-US/docs/Web/API/Window/innerHeight


    ๊ฐ€๋กœ ์Šคํฌ๋กค ๋ง‰๋Œ€๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์— ํ™”๋ฉด์—์„œ ํƒญ, url ์ฃผ์†Œ์ฐฝ, ๋ถ๋งˆํฌ ํƒญ ๋“ฑ์„ ์ œ์™ธํ•œ ์ปจํ…์ธ ๋งŒ์˜ ๋†’์ด๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

  • document.body.offsetHeight

    ์ถœ์ฒ˜: https://developer.mozilla.org/ko/docs/Web/API/HTMLElement/offsetHeight


    ํ•ด๋‹น DOM ์š”์†Œ์˜ ์ˆ˜์ง padding, border๋ฅผ ํฌํ•จํ•˜์—ฌ ์š”์†Œ์˜ ๋†’์ด๋ฅผ ์ •์ˆ˜๋กœ ๋ฐ˜ํ™˜ํ•œ ๊ฐ’์ด๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” body์˜ ์ „์ฒด height์ด๋‹ค.

  • window.scrollY
    ๋ฌธ์„œ๊ฐ€ ์ˆ˜์ง์œผ๋กœ ์–ผ๋งˆ๋‚˜ ์Šคํฌ๋กคํ–ˆ๋Š”์ง€๋Š” pixel ๋‹จ์œ„๋กœ ๋ฐ˜ํ™˜ํ•œ ๊ฐ’์ด๋‹ค. ํ˜„์žฌ ์Šคํฌ๋กค๋ฐ”์˜ ์œ„์น˜์™€ ๋™์ผํ•˜๋‹ค.

์ด ์„ธ ๊ฐ€์ง€๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ฌดํ•œ ์Šคํฌ๋กค์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌํ˜„ ๊ฐ€๋Šฅํ•˜๋‹ค.

// scroll ์ด๋ฒคํŠธ
window.addEventListener('scroll', () => {
  // isLoading: ๋กœ๋”ฉ ์ค‘ ์—ฌ๋ถ€, totalCount: ์„œ๋ฒ„ ๋‚ด ๋ฐ์ดํ„ฐ ๊ฐฏ์ˆ˜, photos: ๋ Œ๋”๋ง๋œ ๋ฐ์ดํ„ฐ ๊ฐฏ์ˆ˜
  const { isLoading, totalCount, photos } = this.state;
  // ๋งจ ์•„๋ž˜๊นŒ์ง€ ์Šคํฌ๋กคํ–ˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์ฒดํฌ
  const isScrollEnded =
    window.innerHeight + window.scrollY + 100 >= document.body.offsetHeight;
  // ๋งจ ์•„๋ž˜๊นŒ์ง€ ์Šคํฌ๋กคํ•˜๊ณ  ์„œ๋ฒ„์™€ ํ†ต์‹ (๋กœ๋”ฉ) ์ค‘์ด ์•„๋‹ˆ๋ฉฐ
  // ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ชจ๋‘ ๋ Œ๋”๋ง ๋œ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ๋ฉด ์ถ”๊ฐ€๋กœ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜จ๋‹ค.
  if (isScrollEnded && !isLoading && photos.length < totalCount)
    onScrollEnded(); // ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ ๋ฐ์ดํ„ฐ ํ˜ธ์ถœ 
});

๊ธฐ๋ณธ์ ์ธ ๋กœ์ง์€ ๋‚ด๊ฐ€ ๋ณด๊ณ  ์žˆ๋Š” ํ™”๋ฉด ๋†’์ด(innerHight)์™€ ์Šคํฌ๋กคํ•œ ๋†’์ด(scrollY)์˜ ํ•ฉ์ด ๋ฌธ์„œ ์ „์ฒด ๋†’์ด(offsetHeight)์„ ๋„˜์–ด์„œ๋Š” ์‹œ์ ์— ์ƒˆ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

window.innerHeight + window.scrollY >= document.body.offsetHeight;

ํ•˜์ง€๋งŒ ๋”ฑ ๋งž๊ฒŒ ์„ค์ •ํ•˜๋ฉด ๋ฌด์กฐ๊ฑด ์ตœํ•˜๋‹จ์— ๋‹ฟ์•„์•ผ๋งŒ ํ•˜๋Š” ๋ถˆํŽธํ•จ์ด ์žˆ๊ณ  margin๊ณผ ๊ฐ™์€ ์—ฌ๋ฐฑ์œผ๋กœ ์ธํ•ด ์ธ์‹์ด ์•ˆ๋˜๋Š” ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
๋”ฐ๋ผ์„œ 100px ์ •๋„์˜ ์—ฌ์œ ๋ฅผ ๋‘ฌ์„œ ์ตœํ•˜๋‹จ๊นŒ์ง€ ๋‚ด๋ ค๊ฐ€๊ธฐ ์ „์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ฒŒ ํ–ˆ๋‹ค.

๋˜ํ•œ ์ตœํ•˜๋‹จ์— ๋‹ฟ์•˜๋‹ค๊ณ  ๋ฌด์กฐ๊ฑด ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ฒŒ ํ•˜๋ฉด ์„œ๋ฒ„ ํ˜ธ์ถœ์„ ๋ฌด๋ถ„๋ณ„ํ•˜๊ฒŒ ํ•˜๊ฒŒ ๋  ์ˆ˜๋„ ์žˆ๋‹ค.
๊ทธ๋ž˜์„œ isLoading์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ผ ๋•Œ๋Š” ์ค‘๋ณต api ํ˜ธ์ถœ์„ ๋ง‰์•„์ฃผ๊ณ 
์„œ๋ฒ„ ๋‚ด ๋ฐ์ดํ„ฐ ๊ฐฏ์ˆ˜(photos.length < totalCount)๋ฅผ ์ฒดํฌํ•ด์„œ ์ „์ฒด ๋ฐ์ดํ„ฐ๊ฐ€ ๋ชจ๋‘ ๋ Œ๋”๋ง๋œ ํ›„์—๋„ ์ถ”๊ฐ€์ ์œผ๋กœ api ํ˜ธ์ถœ์ด ๋‚ญ๋น„๋˜์ง€ ์•Š๋„๋ก ํ•ธ๋“ค๋ง ์กฐ๊ฑด์„ ์ถ”๊ฐ€ํ•ด์คฌ๋‹ค.


IntersectionObserver๋ฅผ ํ™œ์šฉํ•œ ๋ฌดํ•œ์Šคํฌ๋กค

IntersectionObserver ๊ฐ์ฒด๋Š” ๊ฐ์‹œ(observe)ํ•  ๋Œ€์ƒ์„ ์ •ํ•ด์„œ ํ•ด๋‹น ์š”์†Œ๊ฐ€ ์„ค์ •ํ•œ ๋ทฐํฌํŠธ ๋‚ด ๊ฒฝ๊ณ„์™€ ๊ต์ฐจํ•˜๋Š” ์‹œ์ ์„ ํฌ์ฐฉํ•  ์ˆ˜ ์žˆ๋‹ค.

IntersectionObserver๋Š” 1๋ฒˆ์งธ๋กœ ์ฝœ๋ฐฑ ํ•จ์ˆ˜, 2๋ฒˆ์งธ๋กœ observer ์„ค์ •์— ๋Œ€ํ•œ ์ธ์ž๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

observer์— ๋Œ€ํ•œ ์„ค์ • ์†์„ฑ์€ ์„ธ ๊ฐ€์ง€์ด๋‹ค.

  1. root
    observer์˜ ๊ฐ์‹œ ๋Œ€์ƒ์ด ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด์— ๋“ค์–ด์˜ค๋Š” ์˜์—ญ(Box)์„ ์˜๋ฏธํ•œ๋‹ค.
    ์„ค์ •์„ ๋”ฐ๋กœ ํ•˜์ง€ ์•Š์œผ๋ฉด ์ตœ์ƒ์œ„ ๋ฌธ์„œ์˜ ๋ทฐํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. (์—ฌ๊ธฐ์„œ๋Š” body)

  2. rootMargin
    root ์˜์—ญ์˜ margin์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ธฐ๋ณธ margin ์„ค์ •์ฒ˜๋Ÿผ ์ƒ์šฐํ•˜์ขŒ ์ˆœ์œผ๋กœ ์„ค์ •๊ฐ€๋Šฅํ•˜๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ 0px 0px 0px 0px์ด๋‹ค.

  3. threshold
    ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ์†์„ฑ์ด์ง€ ์•Š์„๊นŒ ์‹ถ๋‹ค. ๊ฐ์‹œ ๋Œ€์ƒ์˜ ์–ผ๋งˆ๋‚˜ ๋…ธ์ถœ๋˜์—ˆ์„ ๋•Œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ• ์ง€๋ฅผ ์„ค์ •ํ•œ๋‹ค. 0 ~ 1๊นŒ์ง€์˜ ๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ 0์ด๋‹ค.
    ์˜ˆ๋ฅผ ๋“ค์–ด 0.5๋ฅผ ์„ค์ •ํ•˜๋ฉด ๋ฐ˜ ์ •๋„ ๋ณด์˜€์„ ๋•Œ ๊ฐ์‹œ ๋Œ€์ƒ์ด ์ ˆ๋ฐ˜ ์ •๋„ ๋…ธ์ถœ๋์„ ๋•Œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

IntersectionObserver์—์„œ์˜ ๋ฉ”์„œ๋“œ๋Š” ๋ณดํ†ต ๋‘ ๊ฐ€์ง€๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค.
observe์™€ unobserve์ธ๋ฐ ๊ฐ๊ฐ ๊ฐ์‹œ๋ฅผ ์„ค์ • ๋ฐ ํ•ด์ œ ๊ฐ€๋Šฅํ•˜๋‹ค.


 const obsever = new IntersectionObserver(
    (entries) => {
      // entries๋Š” li ํƒœ๊ทธ๋กœ ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜จ๋‹ค.
      entries.forEach((entry) => {
        // isIntersecting์€ ๊ฐ์‹œ์š”์†Œ๊ฐ€ root ๋ฐ•์Šค๋ฅผ ๊ต์ฐจํ–ˆ๋Š”์ง€ ์—ฌ๋ถ€์ด๋‹ค.
        // ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ isLoading ์ƒํƒœ์—์„œ๋Š” ์ค‘๋ณตํ•ด์„œ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š”๋‹ค.
        if (entry.isIntersecting && !this.state.isLoading) {
          console.log('ํ™”๋ฉด ๋', entry); // ํ™”๋ฉด ๋์ธ์ง€ ํ™•์ธ
          // ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์•˜๋‹ค๋ฉด
          if (this.state.totalCount > this.state.photos.length) {
            onScrollEnded(); // ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
          } // ๋‹ค ๊ฐ€์ ธ์™”๋‹ค๋ฉด
          else if (this.state.totalCount === this.state.photos.length) {
            // ์˜ต์ €๋ฒ„ ํ•ด์ œ
            obsever.unobserve(entry.target);
          }
        }
      });
    },
    {
      // ๋งˆ์ง€๋ง‰ ์š”์†Œ๊ฐ€ ์ ˆ๋ฐ˜ ์ •๋„ ๋ณด์ผ ๋•Œ ์ฝœ๋ฐฑ ์‹คํ–‰
      threshold: 0.5,
    }
);

let $lastLi = null;

// ...

// ๋‹ค์Œ ๊ฐ์‹œ ๋Œ€์ƒ์„ ๋งˆ์ง€๋ง‰ ๋ฆฌ์ŠคํŠธ๋กœ ์„ค์ •
const $nextLi = $photos.querySelector('li:last-child');

if ($nextLi !== null) {
  	// ๋งˆ์ง€๋ง‰ ๋ฆฌ์ŠคํŠธ๊ฐ€ ์—†๋‹ค๋ฉด ๊ฐ์‹œ ๋
    if ($lastLi !== null) {
      obsever.unobserve($lastLi);
    }
    // ๊ฐ์‹œ ๋Œ€์ƒ ๊ต์ฒด
    $lastLi = $nextLi; 
    obsever.observe($lastLi); 
}

์œ„์—์„œ ๊ตฌํ˜„ํ•œ IntersectionObserver ๋กœ์ง์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. obsever.observe($lastLi) : ๊ธฐ๋ณธ์ ์œผ๋กœ observer์˜ ๊ฐ์‹œ๋Œ€์ƒ($lastLi)์„ ์ด๋ฏธ ๋ Œ๋”๋ง ๋˜์–ด ์žˆ๋Š” ๋งˆ์ง€๋ง‰ ๋ฆฌ์ŠคํŠธ ์š”์†Œ($photos.querySelector('li:last-child'))๋กœ ์ •ํ•œ๋‹ค.
  1. observer ์•ˆ์—์„œ๋Š” ๊ฐ์‹œ์š”์†Œ์˜ ์ ˆ๋ฐ˜(threshold: 0.5,)์ด ๋ทฐํฌํŠธ์˜ ์ตœํ•˜๋‹จ(root๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ)๊ณผ ๊ฒน์น˜๋Š”์ง€ ๊ฐ์‹œํ•œ๋‹ค.

  2. onScrollEnded() : ๊ฒน์ณค์„ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์•„์žˆ๋‹ค๋ฉด ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์žฌ๋ Œ๋”๋งํ•œ๋‹ค.

  3. $lastLi = $nextLi, obsever.observe($lastLi) : ๋ Œ๋”๋ง๋œ ๋งˆ์ง€๋ง‰ ์š”์†Œ๋ฅผ ์ƒˆ๋กœ์šด ๊ฐ์‹œ๋Œ€์ƒ์œผ๋กœ ์ •ํ•œ๋‹ค.

  4. obsever.unobserve($lastLi): ์ด๋ฅผ ๋ฐ˜๋ณตํ•˜๋‹ค๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค ๊ฐ€์ ธ์™”๊ฑฐ๋‚˜ ๋งˆ์ง€๋ง‰ ๋ฆฌ์ŠคํŠธ๊ฐ€ ์—†์œผ๋ฉด ๊ฐ์‹œ๋ฅผ ๋๋‚ธ๋‹ค.



๐Ÿ™„ ๋” ๊ณต๋ถ€ํ•ด์•ผ ํ•  ๊ฒƒ

IntersectionObserver์˜ rootMargin

๊ฐ•์‚ฌ๋‹˜๋„ rootMargin์„ ๊ฑด๋“œ๋ ค๋ณด๋ ค๊ณ  ํ•˜์‹œ๋‹ค๊ฐ€ ์ƒ๊ฐ๋ณด๋‹ค ๊ธฐ์กด์˜ Margin๊ณผ๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ์กฐ์ž‘์„ ๋ฏธ๋ฃจ์…จ๋‹ค. ๋‚˜๋Š” ์ด์— ๋Œ€ํ•ด ์ข€ ๋” ์กฐ์‚ฌ๋ฅผ ํ•ด๋ณผ ์˜ˆ์ •์ด๋‹ค.



๐Ÿ‘€ ๋А๋‚€์ 

๐Ÿ‘ Keep

  • ๋ฌดํ•œ์Šคํฌ๋กค์€ ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋Š” ์ˆ˜๋‹จ์ด๊ธฐ์— ์ด๋ฒˆ ๊ธฐํšŒ์— ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์–ด์„œ ์ข‹์•˜๋‹ค.

๐Ÿ˜ฑ Problem

  • ์ƒ๊ฐ๋ณด๋‹ค IntersectionObserver์˜ ๋ฉ”์„œ๋“œ์™€ ์†์„ฑ์„ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ด ์ฒ˜์Œ์—๋Š” ์ดํ•ด๊ฐ€ ๋˜์ง€ ์•Š์•˜๋‹ค.
  • ์ดˆ๊ธฐ ์žฅ๋ฒฝ์€ scroll ์ด๋ฒคํŠธ๊ฐ€ ๋‚ฎ์•˜์œผ๋‚˜ ํ™œ์šฉ๋„ ๋ฉด์—์„œ๋Š” IntersectionObserver๊ฐ€ ์ข‹์€ ๊ฒƒ ๊ฐ™๋‹ค.

๐Ÿ˜œ Try

  • ํŒ€ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ๋„ ์—ฌ๋Ÿฌ ๊ฒŒ์ž„์„ ํ™ˆ ํ™”๋ฉด์—์„œ ๋ณด์—ฌ์ค˜์•ผ ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด ๋•Œ ๋ฌดํ•œ ์Šคํฌ๋กค์„ ์‚ฌ์šฉํ•ด๋ณด์•„์•ผ๊ฒ ๋‹ค.


๐Ÿ˜… ํ•ด๋‹น ๋‚ด์šฉ์€ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค. ํ‹€๋ฆฐ ๋ถ€๋ถ„์ด๋‚˜ ์˜คํ•ดํ•˜๊ณ  ์žˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ํ”ผ๋“œ๋ฐฑ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

profile
ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž ์ค€๋น„์ค‘...

2๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2023๋…„ 11์›” 1์ผ

๊ฒŒ์ž„์ฒœ๊ตญ ํ™ˆํ™”๋ฉด์— ๋ฌดํ•œ์Šคํฌ๋กค ํ• ์ •๋„๋ฉด ์šฐ๋ฆฌ ๊ฒŒ์ž„ ๋‘๊ฐœ์”ฉ ๋”๋งŒ๋“ค์–ด์˜ค๋ฉด๋˜๋‚˜์š”?๐Ÿซก๐Ÿซก

1๊ฐœ์˜ ๋‹ต๊ธ€