리νλ‘μ°(Reflow)μ 리νμΈνΈ(Repaint)λ λΈλΌμ°μ μ λ λλ§ κ³Όμ μμ λ°μνλ λ κ°μ§ μ€μν λ¨κ³λ‘, μΉ νμ΄μ§μ μκ°μ μ
λ°μ΄νΈμ κ΄λ ¨μ΄ μμ΅λλ€. μ΄λ€μ DOMμ΄λ CSSμ λ³κ²½μΌλ‘ μΈν΄ μμμ λ μ΄μμμ΄λ μ€νμΌμ΄ μμ λ λ λΈλΌμ°μ κ° νλ©΄μ λ€μ 그리λ κ³Όμ μμ λ°μν©λλ€. λ¨Όμ λΈλΌμ°μ μ λ λλ§ κ³Όμ μ μ€λͺ
νκ² μ΅λλ€. λΈλΌμ°μ λ λλ§μ μΉ νμ΄μ§λ₯Ό νλ©΄μ νμνκΈ° μν΄ μ¬λ¬ λ¨κ³λ₯Ό κ±°μΉ©λλ€. μλλ μ£Όμ λ¨κ³λ₯Ό κ°λ¨ν μ€λͺ
ν λ΄μ©μ
λλ€.
λΈλΌμ°μ λ λλ§ κ³Όμ
π DOM λ° CSSOM μμ±
- HTMLμ νμ±νμ¬ DOM(Document Object Model) νΈλ¦¬λ₯Ό μμ±ν©λλ€.
- CSSλ₯Ό νμ±νμ¬ CSSOM(CSS Object Model) νΈλ¦¬λ₯Ό μμ±ν©λλ€.
π λ λ νΈλ¦¬(Render Tree) μμ±
- DOMκ³Ό CSSOMμ κ²°ν©νμ¬ μ€μ νλ©΄μ νμν μμλ€λ‘ ꡬμ±λ λ λ νΈλ¦¬λ₯Ό λ§λλλ€. νμλμ§ μλ μμ(μ: display: none)λ ν¬ν¨λμ§ μμ΅λλ€.
π λ μ΄μμ(Layout, Reflow)
- λ λ νΈλ¦¬μ κ° μμμ μμΉμ ν¬κΈ°λ₯Ό κ³μ°ν©λλ€. μ΄ κ³Όμ μμ λ·°ν¬νΈ ν¬κΈ°, μμμ μ€νμΌ λ±μ΄ λ°μλ©λλ€.
π νμΈνΈ(Paint, Repaint)
κ³μ°λ λ λ νΈλ¦¬λ₯Ό κΈ°λ°μΌλ‘ ν½μ
λ¨μλ‘ νλ©΄μ 그리λ κ³Όμ μ μνν©λλ€. μμ, μ΄λ―Έμ§, ν
μ€νΈ λ±μ΄ ν¬ν¨λ©λλ€.
π μ»΄ν¬μ§ν (Compositing)
νμΈνΈλ λ μ΄μ΄λ₯Ό ν©μ±νμ¬ μ΅μ’
νλ©΄μ λ λλ§ν©λλ€. GPUλ₯Ό νμ©νμ¬ λ μ΄μ΄ μ΄λ, λ³ν(μ: transform: translateX(100px)) λ±μ ν¨μ¨μ μΌλ‘ μ²λ¦¬ν©λλ€.
리νλ‘μ°(Reflow)λ?
리νλ‘μ°λ λΈλΌμ°μ κ° μμμ λ μ΄μμ(ν¬κΈ°, μμΉ λ±)μ λ€μ κ³μ°νλ κ³Όμ μ
λλ€. μ΄λ λ λ νΈλ¦¬μ ꡬ쑰λ μμμ κΈ°ννμ μμ±(μ: λλΉ, λμ΄, μμΉ)μ΄ λ³κ²½λ λ λ°μν©λλ€. 리νλ‘μ°λ λ μ΄μμ λ¨κ³μμ μνλλ©°, λ³κ²½λ μμλΏλ§ μλλΌ κ·Έμ μν₯μ λ°λ λ€λ₯Έ μμ(μ: λΆλͺ¨, μμ, νμ μμ)μ λ μ΄μμλ μ¬κ³μ°ν μ μμ΅λλ€.
리νλ‘μ°μ λΉμ©
리νλ‘μ°λ κ³μ°λμ΄ λ§μ μ±λ₯μ ν° μν₯μ λ―ΈμΉ©λλ€. νΉν, λ³κ²½λ μμκ° λ€λ₯Έ μμ(μ: λΆλͺ¨λ μμ)μ μν₯μ λ―ΈμΉλ©΄ μ 체 λ λ νΈλ¦¬ λλ ν° λΆλΆμ μ¬κ³μ°ν΄μΌ ν μ μμ΅λλ€.
νμ΄μ§κ° 볡μ‘νκ±°λ μμκ° λ§μμλ‘ λ¦¬νλ‘μ° λΉμ©μ΄ μ¦κ°ν©λλ€.
- κΈ°ννμ μμ± λ³κ²½: width, height, margin, padding, border, top, left, position λ±.
- λ°μ€ λͺ¨λΈ κ΄λ ¨ μμ± λ³κ²½: box-sizing, display (μ: block β none).
리νμΈνΈ(Repaint)λ?
리νμΈνΈλ μμμ μκ°μ μ€νμΌ(μ: μμ, λ°°κ²½, κ·Έλ¦Όμ λ±)μ΄ λ³κ²½λμμ λ, λΈλΌμ°μ κ° ν΄λΉ μμλ₯Ό νλ©΄μ λ€μ 그리λ κ³Όμ μ
λλ€. 리νμΈνΈλ νμΈν
λ¨κ³μμ μνλλ©°, λ μ΄μμ(μ: ν¬κΈ°λ μμΉ) λ³κ²½ μμ΄ μ€νμΌλ§ μ
λ°μ΄νΈν©λλ€.
- color, background-color, box-shadow, border-color, visibility λ±.
- opacity λ³κ²½ (λ¨, GPU κ°μμ΄ μ μ©λλ©΄ 리νμΈνΈλ₯Ό νΌν μ μμ).
리νμΈνΈμ λΉμ©
- 리νμΈνΈλ 리νλ‘μ°λ³΄λ€ κ³μ° λΉμ©μ΄ μ μ΅λλ€. λ μ΄μμμ μ¬κ³μ°νμ§ μκ³ ν½μ λ§ λ€μ 그리기 λλ¬Έμ λλ€.
κ·Έλ¬λ νμ΄μ§μ λ§μ μμκ° μκ±°λ 볡μ‘ν μ€νμΌ(μ: κ·Έλ¦Όμ, κ·ΈλΌλμΈνΈ)μ΄ μ μ©λ κ²½μ° λ¦¬νμΈνΈλ μ±λ₯μ μν₯μ μ€ μ μμ΅λλ€.
리νλ‘μ°μ 리νμΈνΈμ κ΄κ³
- 리νλ‘μ° β 리νμΈνΈ: 리νλ‘μ°λ λ μ΄μμμ λ³κ²½νλ―λ‘, λ μ΄μμμ΄ λ°λ μμλ λ€μ κ·Έλ €μ ΈμΌ νλ―λ‘ λ¦¬νμΈνΈκ° λ°μν©λλ€.
- 리νμΈνΈλ§ λ°μ: λ μ΄μμμ μν₯μ μ£Όμ§ μλ μ€νμΌ λ³κ²½(μ: color, background)μ 리νμΈνΈλ§ μ λ°ν©λλ€.
- μ»΄ν¬μ§ν : νλ λΈλΌμ°μ μμλ transformμ΄λ opacity κ°μ μμ± λ³κ²½μ λ μ΄μμμ΄λ νμΈν μμ΄ μ»΄ν¬μ§ν λ¨κ³μμ μ²λ¦¬λ©λλ€. μ΄λ 리νλ‘μ°μ 리νμΈνΈλ₯Ό λͺ¨λ νΌν μ μμ΄ μ±λ₯μ΄ λ°μ΄λ©λλ€.
π 리νλ‘μ°μ 리νμΈνΈμ μμ
λ€μμ React μ»΄ν¬λνΈμμ λ²νΌ ν΄λ¦ μ λ°μ€μ μ€νμΌμ λ³κ²½νμ¬ λ¦¬νλ‘μ°μ 리νμΈνΈλ₯Ό μ λ°νλ μμ μ
λλ€.
// src/App.jsx import { useState } from 'react'; function App() { const [boxStyle, setBoxStyle] = useState({ width: '200px', height: '100px', margin: '20px', backgroundColor: 'lightblue', }); const changeStyle = () => { setBoxStyle({ width: '300px', // 리νλ‘μ° μ λ° (λ μ΄μμ λ³κ²½) height: '150px', // 리νλ‘μ° μ λ° margin: '30px', // 리νλ‘μ° μ λ° backgroundColor: 'lightgreen', // 리νμΈνΈ μ λ° }); }; return ( <div> <h1>리νλ‘μ°μ 리νμΈνΈ μμ </h1> <div style={boxStyle}> ν μ€νΈ λ°μ€ </div> <button onClick={changeStyle}>μ€νμΌ λ³κ²½</button> </div> ); } export default App;
- 리νλ‘μ° μ λ° : width, height, margin λ³κ²½μ μμμ λ μ΄μμμ μμ νλ―λ‘ λ¦¬νλ‘μ°κ° λ°μν©λλ€. μ΄λ λ°μ€ λͺ¨λΈμ ν¬κΈ°μ μμΉλ₯Ό μ¬κ³μ°ν©λλ€.
- 리νμΈνΈ μ λ° : backgroundColor λ³κ²½μ λ μ΄μμμ μν₯μ μ£Όμ§ μμΌλ―λ‘ λ¦¬νμΈνΈλ§ λ°μν©λλ€. νμ§λ§ μμμ 리νλ‘μ°κ° λ°μνμΌλ―λ‘ λ¦¬νμΈνΈλ ν¬ν¨λ©λλ€.
- λ¬Έμ μ - μ¬λ¬ μ€νμΌ λ³κ²½μ΄ κ°λ³μ μΌλ‘ μ μ©λλ©΄ Reactμ μν μ λ°μ΄νΈλ‘ μΈν΄ λΆνμν 리λ λλ§μ΄ λ°μν μ μμ΅λλ€. - λ μ΄μμ λ³κ²½μ μ±λ₯ λΉμ©μ΄ ν¬λ―λ‘ λΉλ²ν λ³κ²½ μ μ¬μ©μ κ²½νμ΄ μ νλ μ μμ΅λλ€.
μ»΄ν¬μ§ν νμ© λ° React μ΅μ ν
μ΄ μμ λ 리νλ‘μ°μ 리νμΈνΈλ₯Ό μ΅μννκΈ° μν΄ transformκ³Ό κ°μ μ»΄ν¬μ§ν
μμ±μ μ¬μ© νκ³ , Reactμ λ λλ§ μ΅μ ν κΈ°λ²μ μ μ©ν©λλ€.
// src/App.jsx import { useState, useCallback } from 'react'; import './App.css'; function App() { const [isTransformed, setIsTransformed] = useState(false); const [isColorChanged, setIsColorChanged] = useState(false); const toggleTransform = useCallback(() => { setIsTransformed((prev) => !prev); }, []); const toggleColor = useCallback(() => { setIsColorChanged((prev) => !prev); }, []); return ( <div> <h1>μ΅μ νλ 리νλ‘μ°/리νμΈνΈ μμ </h1> <div className={"box ${isTransformed ? "transformed" : ""} ${ isColorChanged ? "color-changed" : "" }"} > ν μ€νΈ λ°μ€ </div> <button onClick={toggleTransform}>μμΉ μ΄λ (Transform)</button> <button onClick={toggleColor}>μμ λ³κ²½</button> </div> ); } export default App;
/* src/App.css */ .box { width: 200px; height: 100px; margin: 20px; background-color: lightblue; transition: transform 0.3s ease, background-color 0.3s ease; } .transformed { transform: translateX(100px); /* μ»΄ν¬μ§ν λ§ μ λ° */ } .color-changed { background-color: lightgreen; /* 리νμΈνΈλ§ μ λ° */ }
π μ»΄ν¬μ§ν νμ©
transform: translateX(100px)λ λ μ΄μμμ΄λ ν½μ
μ λ€μ κ³μ°νμ§ μκ³ GPUλ₯Ό νμ©ν΄ μ»΄ν¬μ§ν
λ¨κ³μμ μ²λ¦¬λ©λλ€. μ΄λ 리νλ‘μ°μ 리νμΈνΈλ₯Ό νΌν©λλ€.
π 리νμΈνΈλ§ μ λ°
background-color λ³κ²½μ 리νμΈνΈλ§ μ λ°νλ©°, λ μ΄μμμ μν₯μ μ£Όμ§ μμ΅λλ€.
π React μ΅μ ν
- ν΄λμ€ κΈ°λ° μ€νμΌ λ³κ²½: style κ°μ²΄λ₯Ό μ§μ λ³κ²½νλ λμ CSS ν΄λμ€λ₯Ό ν κΈνμ¬ λ¦¬λ λλ§ λΉμ©μ μ€μ λλ€.
- CSS μ λλ©μ΄μ : transition μμ±μ μ¬μ©ν΄ λΆλλ¬μ΄ μ λλ©μ΄μ μ ꡬννλ©°, λΈλΌμ°μ μ μ΅μ ν(GPU κ°μ)λ₯Ό νμ©ν©λλ€.
π λΈλΌμ°μ κ°λ°μ λꡬ νμ©
- Chrome DevToolsμ Performance νμμ 리νλ‘μ°μ 리νμΈνΈλ₯Ό κΈ°λ‘νμ¬ μ΄λ€ μμ±μ΄ μ±λ₯ λ³λͺ©μ μΌμΌν€λμ§ νμΈ.
- Rendering νμμ "Paint Flashing"μ νμ±ννλ©΄ 리νμΈνΈ μμμ μκ°μ μΌλ‘ νμΈ κ°λ₯.
π Core Web Vitals κ³ λ €
리νλ‘μ°λ CLS(Cumulative Layout Shift)λ₯Ό μ λ°ν μ μμΌλ―λ‘, λ μ΄μμ μ΄λμ μ΅μν.
리νμΈνΈλ LCP(Largest Contentful Paint)μ μν₯μ μ€ μ μμΌλ―λ‘, μ€μν μ½ν
μΈ μ λ λλ§μ μ΅μ ν νμ¬ μ¬μ© νλλ‘ ν©λλ€.
π λΌμ΄λΈλ¬λ¦¬ μ¬μ©
Reactμμ μ λλ©μ΄μ
μ μν΄ framer-motionμ΄λ react-spring κ°μ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νλ©΄ μ»΄ν¬μ§ν
μ μλμΌλ‘ νμ© κ°λ₯ν©λλ€.