1. 애니메이션은 어떻게 만들어질까?
컴퓨터 화면 = 연속된 사진들
영화 = 초당 24장의 사진을 빠르게 보여줌 웹 애니메이션 = 초당 60장의 화면을 빠르게 보여줌 (60 FPS)
웹 애니메이션 방식 2가지
CPU 방식 (Main Thread) - 느림
브라우저: "높이를 100px에서 200px로 바꿔라" 1. HTML 다시 계산 (Reflow) 2. CSS 스타일 다시 계산 (Recalculate) 3. 화면 다시 그리기 (Repaint) 4. 다른 요소들도 영향받아서 다시 배치 → 60번/초 하면 버벅거림
GPU 방식 (Compositor) - 빠름
그래픽카드: "이미 그려진 이미지를 움직이기만 하면 됨" 1. 이미 완성된 이미지를 3D 공간에서 이동 2. 다른 요소들 건드리지 않음 → 60번/초도 부드럽게
2. CSS 속성이 문제되는 부분
CPU에서만 처리되는 속성들 (느림)
/* 레이아웃을 변경하는 속성들 */ height: 100px → 200px; /* 높이 변경 = 전체 레이아웃 재계산 */ width: 100px → 200px; /* 너비 변경 = 전체 레이아웃 재계산 */ margin: 10px → 20px; /* 여백 변경 = 주변 요소들 위치 변경 */ padding: 5px → 10px; /* 내부 여백 = 내용물 위치 변경 */ top: 0px → 100px; /* 위치 변경 = 레이아웃 재계산 */ left: 0px → 100px; /* 위치 변경 = 레이아웃 재계산 */ /* 그리기를 다시 해야 하는 속성들 */ background-color: red → blue; /* 배경색 = 다시 칠하기 */ color: black → white; /* 글자색 = 다시 칠하기 */ border: 1px → 5px; /* 테두리 = 다시 그리기 */
GPU에서 처리되는 속성들 (빠름)
/* 이미 그려진 것을 3D 공간에서 조작 */ transform: translateX(100px); /* 이동만 함 */ transform: scale(1.2); /* 크기만 변경 */ transform: rotate(45deg); /* 회전만 함 */ opacity: 0.5; /* 투명도만 변경 */
3. 실제 예시
나쁜 예시 (버벅거림)
.box { transition: height 0.3s ease; } .box:hover { height: 200px; /* CPU가 60번/초 레이아웃 재계산 = 버벅 */ }
브라우저 내부 상황
0.000초: height: 100px → 레이아웃 계산 → 화면 그리기 0.016초: height: 103px → 레이아웃 계산 → 화면 그리기 0.032초: height: 106px → 레이아웃 계산 → 화면 그리기 ... (매번 전체 페이지 재계산)
좋은 예시 (부드러움)
.box { transition: transform 0.3s ease; } .box:hover { transform: scaleY(2); /* GPU가 이미 그려진 것만 늘림 = 부드러움 */ }
브라우저 내부 상황
0.000초: transform: scaleY(1.0) → GPU에서 즉시 처리 0.016초: transform: scaleY(1.1) → GPU에서 즉시 처리 0.032초: transform: scaleY(1.2) → GPU에서 즉시 처리 ... (다른 요소들 건드리지 않음)
4. 커스텀 CSS 변수란?
일반적인 CSS
.button1 { background-color: #3498db; } .button2 { background-color: #3498db; } .button3 { background-color: #3498db; } /* 같은 색을 계속 반복 */
커스텀 CSS 변수 (CSS Custom Properties)
:root { --main-color: #3498db; /* 변수 정의 */ --spacing: 20px; --animation-duration: 0.3s; } .button1 { background-color: var(--main-color); } .button2 { background-color: var(--main-color); } .button3 { background-color: var(--main-color); } /* 한 곳만 바꾸면 모든 곳이 바뀜 */
JavaScript로 동적 변경
// 테마 색상을 즉시 변경 document.documentElement.style.setProperty('--main-color', '#e74c3c'); // → 모든 --main-color 사용하는 곳이 빨간색으로 변경!
5. 커스텀 변수 애니메이션의 문제
문제가 되는 코드
:root { --box-height: 100px; } .box { height: var(--box-height); transition: height 0.3s ease; } .box:hover { --box-height: 200px; /* 커스텀 변수 값 변*/ }
겉보기에는 문제가 없어 보이지만, 브라우저 내부에서는 비효율적인 과정이 발생
이는
height 속성 자체가 가진 특징 때문브라우저의 렌더링 과정과 문제점
브라우저는 웹페이지를 화면에 그리기 위해 다음과 같은 과정을 거침
- Layout (Reflow): 각 요소의 크기와 위치를 계산. "이
div는 너비가 얼마고 높이가 얼마이니 여기에 위치해야 하고, 그 옆에 있는 요소는 밀려나야겠군" 과 같은 계산을 하는 단계. 이 과정은 CPU를 많이 사용하며 매우 비용이 큰 작업.
- Paint (Repaint): Layout 계산이 끝난 요소들에 색상, 이미지, 텍스트 등을 입혀 픽셀로 변환하는 단계.
- Composite: Paint 된 여러 개의 레이어(층)를 순서대로 합쳐서 최종 화면을 만들어냄. 이 과정은 주로 GPU가 담당하여 매우 빠름
문제의 코드는
:hover 시 --box-height 값을 변경하여 결과적으로 height 속성을 바꾸고 있음. height는 요소의 실제 크기를 결정하므로, 이 값이 변하면 페이지 전체의 구조에 영향을 줄 수 있음.box의 높이가 커지면 그 아래에 있던 다른 요소들은 모두 아래로 밀려나야 함
- 브라우저는 이 변화를 감지하고 "요소 크기가 바뀌었네. 다른 요소들 위치를 전부 다시 계산해야겠다"라고 판단
- 결국 애니메이션의 매 프레임마다 비싼 Layout(Reflow) 과정과 Paint 과정을 반복
- 이는 CPU에 큰 부담을 주어 애니메이션이 뚝뚝 끊기는 현상을 유발하고 성능을 저하시킴
커스텀 변수 자체의 문제가 아니라, 그 변수가 Layout을 유발하는 height 속성에 적용되었기 때문에 성능 문제가 발생하는 것
올바른 해결책
.box { height: 100px; transform: scaleY(1); /* Y축(세로)으로 1배율 (원래 크기) */ transform-origin: top; /* 위쪽을 기준으로 커지도록 설정 */ transition: transform 0.3s ease; } .box:hover { transform: scaleY(2); /* Y축으로 2배율 (200px 처럼 보이게) */ }
올바른 해결책은 Layout 과정을 건너뛰고 Composite 단계에서 처리할 수 있는 속성을 사용하는 것. 대표적인 속성이 바로
transform과 opacity
transform이 더 빠른 이유
transform속성은 요소의 Layout에 영향을 주지 않음. 브라우저는transform이 적용된 요소를 별도의 레이어로 분리하여 처리
scaleY(2)는.box의 원래height인100px는 그대로 둔 채, 그래픽적으로만 세로로 2배 늘려 보여줌
6. Lighthouse가 체크하는 이유
Lighthouse = 웹사이트 성능 진단
의사: "웹사이트에서 애니메이션이 버벅거립니다" 진단: "CPU 애니메이션을 GPU 애니메이션으로 바꾸세요" 처방: "transform, opacity 사용하고 height, width 쓰지 마세요"
실제 Lighthouse 경고 메시지
"Avoid non-composited animations" "다음 속성들이 성능 문제를 일으킵니다:" - height (레이아웃 재계산 필요) - background-color (다시 그리기 필요) - --custom-variable (GPU에서 처리 불가)
7. 기존 문제 상황
개발자가 받던 혼란스러운 메시지
Before (기존): "Unsupported CSS Properties: height, --theme-color, width, --spacing" 개발자 반응: "height랑 --theme-color가 왜 같이 나오지?" "뭐가 다른 건지 모르겠어..."
이슈
GitHub Issue #14521: "커스텀 프로퍼티가 왜 일반 CSS랑 같이 나오는가, 이해가 안 된다. 분리해서 보여주라"
8. 해결 후
After (고친 후) "Unsupported CSS Properties: height, width" "Custom CSS properties cannot be animated on the compositor: --theme-color, --spacing" 개발자 "아! height, width는 transform으로 바꾸면 되겠네" "커스텀 변수는 원래 GPU에서 안 되는구나" "JavaScript로 변경하거나 다른 방법을 써야겠다"