๐Ÿ’ก transform ์œผ๋กœ ๋ธŒ๋ผ์šฐ์ € ์ดค์ ํ™” ๋ง›๋ณด๊ธฐ (feat. GPU,Reflow)

์˜ˆ๋ฆฌ์—๋ฅดยท2024๋…„ 3์›” 14์ผ
1

Web

๋ชฉ๋ก ๋ณด๊ธฐ
4/4
post-thumbnail

์ตœ์ ํ™” ๊ณ ๋ฏผ์˜ ์‹œ์ž‘

์ตœ๊ทผ ํšŒ์‚ฌ์—์„œ ์ƒˆ๋กœ์šด ์„œ๋น„์Šค(๋ชจ๋ฐ”์ผ์ธ๋ฑ์Šค GAME) ๋ฅผ ์ถœ์‹œํ•˜๋ฉด์„œ ์ฒ˜์Œ์œผ๋กœ ์„œ๋น„์Šค์— 1์ธ๋ถ„์œผ๋กœ ๊ธฐ์—ฌํ•˜๊ฒŒ๋˜์—ˆ๋‹ค...(์•„๋งˆ?? ๋“œ๋””์–ด ๋งŒ 2๋…„ 2๊ฐœ์›”๋งŒ์—๐Ÿฅน)

์šฐ๋ฆฌ ํŒ€์˜ ํŠน์ง•์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ์„ ์ง€์–‘ํ•˜๊ณ  ์ง์ ‘ ๋งŒ๋“ค์–ด๋ณด์ž ์ธ๋ฐ, ๊ทธ์ค‘์—์„œ ์„œ๋น„์Šค์˜ ํŠน์ง•์ธ ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”์— ๋งŽ์€ ์ดˆ์ ์— ๋งž์ถฐ์ ธ์žˆ๋‹ค.

์„œ๋น„์Šค ์ถœ์‹œ ์ผ์ •์ด ์ด‰๋ฐ•ํ•ด ๊ธฐ์กด์— ๊ฐœ๋ฐœ๋˜์—ˆ๋˜ ์ฝ”๋“œ๋“ค์„ ์ฐธ๊ณ ํ•˜๋ฉด์„œ ์ฐจํŠธ๋ฅผ ๊ทธ๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์ธ ์ง€์‹๊ณผ ๋กœ์ง์„ ํŒŒ์•…ํ•˜๊ณ  ๋”ฐ๋ผ๊ฐ€๋ฉด ๋งŒ๋“ค์—ˆ๋‹ค. ๋‚˜๋ฆ„ ๋‚˜๋งŒ์˜ ๋ฐฉ์‹์„ ์„ž์–ด์„œ ๋‚˜๋งŒ์˜ ์ฐจํŠธ๋ฅผ ๋งŒ๋“ค๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ๋‹ค.

์ •์‹ ์—†๋˜ ์„œ๋น„์Šค ๊ฐœ๋ฐœ ๊ธฐ๊ฐ„ํ›„ ๋ฐ”๋กœ ๋‹ค๋ฅธ ํŒ€์˜ ์š”์ฒญ์„ ๋ฐ›์•„ ์‹œ์—ฐ์šฉ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ–ˆ๋‹ค. ๋‹ค๋ฅธ ํŒ€์›์ด ๋ฉ”์ธ์„ ์žก๊ณ  ๊ณ„์…จ๊ณ  ์ด๋ฒˆ ํ”Œ์ ์—์„œ ์ง€๋‚œ๋ฒˆ ๊ฐœ๋ฐœ๋•Œ ๋ชปํ–ˆ๋˜ ๋‹ค๋ฅธ ์ฐจํŠธ๋“ค์„ ๋งˆ์Œ๊ป ๋งŒ๋“ค์–ด๋ณด๋ผ๊ณ  ํ•ด์ฃผ์…จ๋‹ค.

๊ทธ๋ž˜์„œ ๊ธฐ์กด ํŒ€์—์„œ ๋งŒ๋“ค์—ˆ๋˜ ์ฐจํŠธ๋ฅผ ๋ฒ—์–ด๋‚˜ ์‚ฌ๋žŒ๋“ค์ด ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ์ฐจํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฒ€์ƒ‰ํ•˜๊ณ  ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋กœ ํ•˜๋‚˜ํ•˜๋‚˜ ๋ถ„์„ํ•˜๋ฉด์„œ ๋””์ž์ธ๋„ ํ•˜๊ณ  ๊ธฐ๋Šฅ๋„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์ง„์งœ ๋‚˜๋งŒ์˜ ์ฐจํŠธ๋ฅผ ๋งŒ๋“ค๊ธฐ์œ„ํ•ด ๋…ธ๋ ฅํ–ˆ๋‹ค.

๊ทธ๋Ÿฌ๋˜ ์ค‘!!! ํ•˜๋‚˜ ๊ธฐ์–ต๋‚จ๋Š” ์ ์€ ์ฐจํŠธ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ํˆดํŒ์˜ ์ด๋™๋ฐฉ์‹์ด์—ˆ๋‹ค.

๋„ˆ์˜ ์ฝ”๋“œ๋ฅผ ๋ณด์—ฌ์ค˜ (์ฝ”๋“œ๋น„๊ต)

โช ์ด์ „ ๋ฒ„์ „

const isPassHalfWidth = (refSize.width / 2) < toolTip.x;
const isPassHalfHeight = (refSize.height / 2) < toolTip.y;

const passHalfStyle = (isPassHalfHeight && isPassHalfWidth) ? `translate(-100%,-100%)` :
            isPassHalfWidth ? `translateX(-100%)` : isPassHalfHeight ? `translateY(-100%)` : '';

...
<div className={styels.tooltip}>
 style={{
        transform: passHalfStyle,
         left: toolTip.x - 1,
         top: toolTip.y,
  }}
  </div>

โฉ ๋ณ€๊ฒฝ ๋ฒ„์ „

const isPassHalfWidth = (refSize.width / 2) < toolTip.x;
const isPassHalfHeight = (refSize.height / 2) < toolTip.y;
const tooltipX = isPassHalfWidth ? `calc(${toolTip.x}px - 100% - 1rem)` : `calc(${toolTip.x}px + 1rem)`;
const tooltipY = isPassHalfHeight ? `calc(${toolTip.y}px - 100%)` : `${toolTip.y}px`;

...
<div className={styels.tooltip}>
  style={{
        left: 0,
        top: 0,
        transform: `translate(${tooltipX},${tooltipY})`
   }}
</div>

์ด์ „์— ์ž‘์„ฑํ–ˆ๋˜ ๋ฐฉ์‹์€ tooltip์˜ posistion์ด absolute์ธ๊ฑด ๊ฐ™์ง€๋งŒ ์ด๋™ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๋‹ฌ๋ž๋‹ค.
์ด์ „์—๋Š” left,top ํ˜•์‹์„ ํ™œ์šฉํ•˜์—ฌ ๋งค๋ฒˆ ์œ„์น˜๋ฅผ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๊ณ„์‚ฐํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ๋‹ค. ์ผ๋‹จ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ๋Š” ๊ตฌํ˜„ํ•˜๋Š”๊ฒŒ ๋ชฉํ‘œ์ด๊ธฐ ๋•Œ๋ฌธ์— ์˜๋„ํ•œ ๋Œ€๋กœ ์ž˜ ์›€์ง์ด๋Š”์ง€๊ฐ€ ์ค‘์š”ํ–ˆ๋˜๊ฑฐ ๊ฐ™๋‹ค.

ํ•˜์ง€๋งŒ ์ด๋ฒˆ์— ์ฐพ์€ ๋ฐฉ์‹์€ transform์„ ์‚ฌ์šฉํ•œ ๋ฐฉ๋ฒ•์ด์—ˆ๋‹ค. ์ฒจ์—๋Š” ๋ธŒ๋ผ์šฐ์ € ๋ Œ๋”๋ง ๋ฐฉ์‹์„ ์žŠ๊ณ  "์‹ ๋ฐ•ํ•œ๋ฐ??"๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์—ˆ๋‹ค. ์ตœ๊ทผ์— ๋‹ค์‹œ ๊ธฐ์ดˆ๋ถ€ํ„ฐ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ๋ธŒ๋ผ์šฐ์ € ๋ Œ๋”๋ง์— ๋Œ€ํ•ด ๋‹ค์‹œ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ์„ฑ๋Šฅ ์ตœ์ ํ™”์˜ ์ค‘์š”์„ฑ์„ ๋Š๋ผ๊ฒŒ๋˜์—ˆ๋‹ค.!

์‚ฌ์‹ค ์ฐจํŠธ๋ฅผ ๊ทธ๋ฆฌ๋ฉด์„œ ๋„ˆ๋ฌด ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๊ณ„์‚ฐํ•˜๋‹ค๋ณด๋‹ˆ ์„ฑ๋Šฅ์ด ์ข‹์ง€ ์•Š์„๊บผ ๊ฐ™๋‹ค ๋ผ๋Š” ๋Š๋‚Œ์€ ์žˆ์—ˆ์ง€๋งŒ ์‹ค์ œ๋กœ ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ์œ„ํ•ด ์–ด๋–ค๊ฑธ ํ•ด์•ผํ• ์ง€ ๊ฐ์ด ์˜ค์ง€ ์•Š์•˜์—ˆ๋‹ค.

์ด๋ฒˆ ๊ณ„๊ธฐ๋กœ ๋ Œ๋”๋ง ๋ถ€ํ„ฐ ์ฐจ๊ทผ์ฐจ๊ทผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋‚˜์ค‘์— javascript๋„ ์ˆ˜์ •ํ•˜๋ฉด ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๋„ ์ค„์ด๊ณ  Lighthouse์—์„œ๋„ ๋†’์€ ์ ์ˆ˜๋ฅผ ๋ฐ›๊ณ  ์‹ถ๋‹ค. ใ…Žใ…Žใ…Ž๐Ÿ˜š

์™œ transform์˜ ์„ฑ๋Šฅ์ด ๋” ์ข‹์„๊นŒ? (๋ธŒ๋ผ์šฐ์ € ์˜ CPU์™€ GPU ๊ด€์ ์—์„œ)

ํฌ๋กฌ ๋ธŒ๋ผ์šฐ์ €๋Š” ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์„œ ์•„ํ‚คํ…์ฒ˜๋กœ ๋™์ž‘์„ ํ•œ๋‹ค.

Google Developers

๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๋Š” ์›น ํŽ˜์ด์ง€๋ฅผ ํ‘œ์‹œํ•˜๋Š” ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋ฅผ ๋งก์•„์„œ ์ง„ํ–‰ํ•˜๋Š”๋ฐ, ๊ทธ ์ค‘ ๋ ˆ์ด์•„์›ƒ๊ณผ ํŽ˜์ธํŠธ๋Š” ์ค‘์š”ํ•œ ๊ณผ์ •์ด๋‹ค.

๋ ˆ์ด์•„์›ƒ์€ RenderTree๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ HTML ์š”์†Œ์˜ ํฌ๊ธฐ์™€ ์œ„์น˜๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค. CSS ์ •๋ณด๋„ ์ฐธ๊ณ ํ•˜์—ฌ ์–ด๋””์— ์œ„์น˜ํ• ์ง€ ๊ณ„์‚ฐํ•˜๊ณ  ํฌ๊ธฐ,์œ„์น˜,๊ฐ„๊ฒฉ ๋“ฑ์ด ๊ฒฐ์ •๋œ๋‹ค.

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

์—ฌ๊ธฐ์„œ left,top์€ ๋ ˆ์ด์•„์›ƒ์„ ๋‹ค์‹œ ๊ทธ๋ฆฌ๋Š” reflow์˜ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ๋กœ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋ธŒ๋ผ์šฐ์ €๋Š” ๋ ˆ์ด์•„์›ƒ์„ ๋ณ€๊ฒฝํ•œ๋‹ค. ์ด๋ง์€ ์ฆ‰, ์š”์†Œ์˜ ์œ„์น˜๋ฅผ ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜๊ณ  ํŽ˜์ธํŠธ ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค. ์ด๋Ÿฐ ๊ณผ์ •์€ ์ฃผ๋กœ CPU์—์„œ ์ง„ํ–‰์„ ํ•œ๋‹ค.

ํ•˜์ง€๋งŒ transform, opacity์™€ ๊ฐ™์€ ์†์„ฑ์€ composite ๋‹จ๊ณ„๋กœ ์ฃผ๋กœ GPU๊ฐ€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

CPU์™€ GPUํŠน์ง•์„ ๋ณด๊ธฐ ํŽธํ•˜๊ฒŒ ์ •๋ฆฌํ•œ ๋ธ”๋กœ๊ทธ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ๊ฐ ํŠน์ง•์„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.๐Ÿ‘

GPU๋Š” ๋ณ‘๋ ฌ ์ž‘์—…์— ์ตœ์ ํ™”๋˜์–ด ์žˆ์œผ๋ฉฐ ํ•˜๋“œ์›จ์–ด๋ฅผ ๊ฐ€์†ํ™” ํ•˜๊ณ  ๋ ˆ์ด์–ด์˜ ํ•ฉ์„ฑ์ด ๋” ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง€๊ฒŒ ํ•ด์ค€๋‹ค.

Reflow๋ฅผ ์กฐ์‹ฌํ•ด์•ผํ•˜๋Š” ์ด์œ 

CPU๋Š” ์›นํŽ˜์ด์ง€์˜ HTML,CSS,Javascipt ๋“ฑ์˜ ์ฝ”๋“œ๋ฅผ ํ•ด์„ํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•œ๋‹ค. ๋ณต์žกํ•œ javascript ์—ฐ์‚ฐ์ด ์žˆ๋‹ค๋ฉด ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋Š๊ธฐ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ณ  ์—ฐ์‡„์ ์œผ๋กœ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ๋‚˜๋น ์ง€๊ฒŒ ๋œ๋‹ค.

๋งŽ์„ ์–‘์„ GPU์— ๋งก๊ธฐ๊ฒŒ ๋˜๋ฉด ๊ทธ๊ฒƒ ๋˜ํ•œ GPU์˜ ๊ณผ๋ถ€ํ™”๋ฅผ ๋ถˆ๋Ÿฌ์ผ์œผํ‚ค๊ฒŒ ๋ฉ”๋ชจ๋ฆฌ ๋ฆฌ์†Œ์Šค๊ฐ€ ๊ณ ๊ฐˆ ๋  ์ˆ˜ ์žˆ๋‹ค.

์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ฒฐ๊ณผ (์„ฑ๋Šฅ ํƒญ,FPS)

์„ฑ๋Šฅ ํƒญ

๊ฐ™์€ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ฐ™์€ ms ๋™์•ˆ ์žฌ์—ฐํ•˜์—ฌ ์ธก์ •ํ•œ ๊ฒฐ๊ณผ์ด๋‹ค.

โช ์ตœ์ ํ™” ์ „

์ตœ์ ํ™” ์ „์—๋Š” ๋ ˆ์ด์•„์›ƒ ๋ณ€๊ฒฝ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ถ„ํ™์ƒ‰ ๋„ค๋ชจ๊ฐ€ ๋ณด์ด๊ณ , event log์— layout์„ ๊ฒ€์ƒ‰ํ•˜๋ฉด reflow๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

โฉ ์ตœ์ ํ™” ํ›„

์ตœ์ ํ™” ํ›„์—๋Š” ๋ถ„ํ™์ƒ‰ ๋„ค๋ชจ๋Š” ๋ณด์ด์ง€์•Š๊ณ  event log์— layout์„ ๊ฒ€์ƒ‰ํ•ด๋„ ๊ฒ€์ƒ‰๊ฒฐ๊ณผ์— ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค.

FPS(Frame Per Seconds meter)

ํฌ๋กฌ ๋ธŒ๋ผ์šฐ์ € ์„ฑ๋Šฅ ๋ถ„์„ ๋„๊ตฌ ์ค‘ FPS๋ฅผ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ๊ฐ€ ์žˆ๋‹ค. ๋งˆ์šฐ์Šค๋ฅผ ์›€์ง์ด๋… ์ดˆ๋‹น ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ”„๋ ˆ์ž„๊ณผ GPU ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ํ™•์ธ ํ•  ์ˆ˜์žˆ๋‹ค.

ํ•˜๋‹จ GPU๋ฅผ ๋ณด๊ฒŒ ๋ณด๋ฉด ์ตœ์ ํ™” ์ „์€ ๋Œ€๋žต 20.5MB์˜ GPU๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ ์ตœ์ ํ™” ํ›„ GPU๋Š” 17.2MB๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

โช ์ตœ์ ํ™” ์ „

โฉ ์ตœ์ ํ™” ํ›„

๋Š๋‚€ ์ 

ํšŒ์‚ฌ์— ๋‹ค๋‹ˆ๋‹ค๋ณด๋ฉด ์ฝ”๋“œ์˜ ํ€„๋ฆฌํ‹ฐ๊ฐ€ ์•„์‰ฝ๋”๋ผ๋„ ๊ธฐํ•œ์— ๋งž์ถฐ ์ž‘์—…์„ ํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. ๋น„์ง€๋‹ˆ์Šค๋ฅผ ์œ„ํ•ด์„œ๋Š” ๋‹น์—ฐํžˆ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์ด๊ณ  ๊ฐ์ˆ˜ํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

ํ•˜์ง€๋งŒ ์žฅ๊ธฐ์ ์œผ๋กœ ํ”„๋Ÿฌ๋•ํŠธ๋ฅผ ๋ณด๋ฉด ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์œ„ํ•ด์„œ๋„ ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•ด์„œ๋„ ๋‚˜ ์ž์‹ ์„ ์œ„ํ•ด์„œ๋„ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ๊ฐœ์„ ํ•ด๋‚˜๊ฐ€๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ์ง€๊ธˆ๊นŒ์ง€ ์‚ฌ์šฉ์ž๊ฐ€ ์‚ฌ๋‚ด ์œ„์ฃผ์˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์ฃผ๋กœ ํ–ˆ์—ˆ๋Š”๋ฐ ์‹ค์ œ๋กœ ์™ธ๋ถ€ ์ด์šฉ์ž๊ฐ€ ์žˆ๋Š” ์ž‘์—…์„ ํ•˜๋‹ˆ ๋Š๊ปด์ง„ ๋ถ€๋ถ„์ด ๋งŽ์•˜๋‹ค. ์•„์ง ๋ถ€์กฑํ•œ ๋ถ€๋ถ„์ด ๋งŽ์ง€๋งŒ ์ฐจ๊ทผ์ฐจ๊ทผ ๋” ๊ฐœ์„ ํ•ด์•ผ๊ฒ ๋‹ค๋Š” ์˜์ง€๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋˜์—ˆ๋‹ค!!!

๋‹ค๋ฅธ ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์„ ์ฐพ๊ฒŒ ๋˜๋ฉด ๋˜ ๊ธ€์„ ์“ฐ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค ใ…Žใ…Ž ๐Ÿ‘ฝ๐Ÿ‘ฝ

profile
๋น„์ „๊ณต ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž์˜ ๊ฐœ๋ฐœ์ผ๊ธฐ๐Ÿ˜ˆ โœ๏ธ

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