π λ μ΄μμμ μ λ°νλ CSS μ λλ©μ΄μ
Summary
CSS μ λλ©μ΄μ
μ width
, height
, top
, left
λ± λ μ΄μμμ μ λ°νλ μμ± λμ transform
κ³Ό opacity
λ₯Ό μ¬μ©νμ¬ μ±λ₯ μ ν μμ΄ λΆλλ¬μ΄ μ λλ©μ΄μ
μ ꡬνν΄μΌ ν©λλ€.
Why Wrong?
νλ‘ νΈμλμμ μ λλ©μ΄μ
μ±λ₯μ μ¬μ©μ κ²½νμ ν° μν₯μ λ―ΈμΉ©λλ€. CSS μ λλ©μ΄μ
μ ꡬνν λ width
, height
, top
, left
, margin
, padding
κ³Ό κ°μ΄ **λ μ΄μμ(Layout)**μ μ λ°νλ μμ±μ μ λλ©μ΄μ
νλ κ²μ λνμ μΈ μν°ν¨ν΄μ
λλ€.
λΈλΌμ°μ λ μΉ νμ΄μ§λ₯Ό λ λλ§ν λ λ€μκ³Ό κ°μ λ¨κ³λ₯Ό κ±°μΉ©λλ€:
- Style (μ€νμΌ κ³μ°): CSS μ€νμΌμ μμμ μ μ©ν©λλ€.
- Layout (λ μ΄μμ / Reflow): κ° μμμ ν¬κΈ°μ μμΉλ₯Ό κ³μ°ν©λλ€.
width
,height
,top
,left
λ± κΈ°ννμ μμ±μ΄ λ³κ²½λλ©΄ μ΄ λ¨κ³κ° λ€μ λ°μνλ©°, μ’ μλ λͺ¨λ μμλ€μ μμΉλ₯Ό μ¬κ³μ°ν΄μΌ ν©λλ€. μ΄λ CPUλ₯Ό λ§μ΄ μ¬μ©νλ κ³ λΉμ© μμ μ λλ€. - Paint (νμΈνΈ / Repaint): μμμ λ°°κ²½, μμ, κ·Έλ¦Όμ λ± μκ°μ λΆλΆμ ν½μ λ‘ μ±μλλ€.
- Composite (ν©μ±): κ°κ°μ λ μ΄μ΄λ₯Ό κ²°ν©νμ¬ μ΅μ’ νλ©΄μ λ§λλλ€.
width
, height
, left
, top
λ±μ μμ±μ μ λλ©μ΄μ
νλ©΄ λ§€ νλ μλ§λ€ Layout λ¨κ³κ° κ°μ λ‘ μ¬μ€νλ©λλ€. μ΄λ₯Ό 'λ μ΄μμ μ€λμ±(Layout Thrashing)'μ΄λΌκ³ λΆλ₯΄λ©°, νΉν 볡μ‘ν νμ΄μ§μμλ **μ±λ₯ μ ν(Jank, λ²λ²
κ±°λ¦Ό)**μ νλ μ λλ‘μ μ λ°νμ¬ λΆμμ°μ€λ¬μ΄ μ λλ©μ΄μ
μ²λΌ 보μ΄κ² λ©λλ€.
λ°λ©΄ transform
(μ: translateX
, translateY
, scale
, rotate
)μ΄λ opacity
μ κ°μ μμ±μ Layoutμ΄λ Paint λ¨κ³λ₯Ό 건λλ°κ³ λλΆλΆ Composite λ¨κ³μμ GPUμ λμμ λ°μ μ§μ μ²λ¦¬λ μ μμ΅λλ€. μ΄λ λΈλΌμ°μ μ λ λλ§ νμ΄νλΌμΈμμ κ°μ₯ ν¨μ¨μ μΈ κ²½λ‘μ΄λ©°, λΆλλ¬μ΄ 60fps μ λλ©μ΄μ
μ κ°λ₯νκ² ν©λλ€.
How to Fix?
width
, height
, top
, left
λμ transform
μμ±μ μ¬μ©νμ¬ μμμ ν¬κΈ°λ μμΉλ₯Ό λ³κ²½ν΄μΌ ν©λλ€. opacity
λ νμ΄λ μΈ/μμ ν¨κ³Όμ μ¬μ©ν©λλ€. μ΄λ¬ν μμ±λ€μ λ μ΄μμμ μ¬κ³μ°νμ§ μμΌλ―λ‘ ν¨μ¬ λμ μ±λ₯μ κΈ°λν μ μμ΅λλ€.
- μμΉ λ³κ²½:
left
/top
λμtransform: translateX()
,translateY()
μ¬μ©. - ν¬κΈ° λ³κ²½:
width
/height
λμtransform: scale()
μ¬μ©. - νμ΄λ ν¨κ³Ό:
opacity
μ¬μ©.
λν, μ λλ©μ΄μ
μ΄ μ μ©λ μμμ will-change
CSS μμ±μ μ¬μ©νμ¬ λΈλΌμ°μ μ ν΄λΉ μμμ νΉμ μμ±μ΄ λ³κ²½λ κ²μμ 미리 μλ €μ£Όλ κ²λ μ±λ₯ μ΅μ νμ λμμ΄ λ μ μμ΅λλ€. νμ§λ§ will-change
λ λ¨μ©λ κ²½μ° μ€νλ € μ±λ₯ μ νλ₯Ό μΌμΌν¬ μ μμΌλ―λ‘ νμν κ²½μ°μλ§ μ μ€νκ² μ¬μ©ν΄μΌ ν©λλ€.
Before Code (Bad)
.animated-box {
width: 100px;
height: 100px;
background-color: dodgerblue;
position: relative;
animation: slide-left-bad 2s infinite alternate;
}
@keyframes slide-left-bad {
from { left: 0; }
to { left: 200px; }
}
.resizable-box {
width: 50px;
height: 50px;
background-color: tomato;
animation: resize-bad 2s infinite alternate;
}
@keyframes resize-bad {
from { width: 50px; height: 50px; }
to { width: 200px; height: 200px; }
}
```
```html
<div class="animated-box"></div>
<div class="resizable-box"></div>
After Code (Good)
.animated-box {
width: 100px; /* κ³ μ κ° */
height: 100px; /* κ³ μ κ° */
background-color: dodgerblue;
/* position: relative; */ /* transform μ¬μ© μ λΆνμ */
animation: slide-right-good 2s infinite alternate;
/* will-change: transform; /* νμμ μΆκ° */
}
@keyframes slide-right-good {
from { transform: translateX(0); }
to { transform: translateX(200px); }
}
.resizable-box {
width: 50px; /* κ³ μ κ° */
height: 50px; /* κ³ μ κ° */
background-color: tomato;
animation: resize-good 2s infinite alternate;
/* will-change: transform; /* νμμ μΆκ° */
}
@keyframes resize-good {
from { transform: scale(1); }
to { transform: scale(4); } /* 50px * 4 = 200px */
}
.fade-element {
background-color: lightgreen;
width: 100px;
height: 100px;
animation: fade-good 2s infinite alternate;
/* will-change: opacity; /* νμμ μΆκ° */
}
@keyframes fade-good {
from { opacity: 0; }
to { opacity: 1; }
}
```
```html
<div class="animated-box"></div>
<div class="resizable-box"></div>
<div class="fade-element"></div>