페이지 모달에서 에니메이션이 동작하지 않는 것이다.
원인은 DOM 추가와 active 클래스 추가가 동시에 일어나서 CSS transition 이 작동하지 않았음
그래서 requestAnimationFrame() 으로 해결했는데
혹시나 해서 과거 코드의 참 많이 쓰기도 했고 해악이기도 했었던 sleep(0) 이런거랑 같은거인지를 물어보았다.
아니라고 합니다.
뭐라고 하는지 들어보려면 아래를 확인
⏺ requestAnimationFrame은 단순한 sleep 함수가 아니라 브라우저의 렌더링 사이클과 동기화되는
특별한 함수입니다.
🎨 브라우저 렌더링 사이클
브라우저는 다음 순서로 화면을 그립니다:
1. JavaScript 실행
2. Style 계산 (CSS 적용)
3. Layout (요소 크기/위치 계산)
4. Paint (픽셀 그리기)
5. Composite (레이어 합성)
이 과정은 보통 60fps로 실행됩니다 (16.67ms마다 1번).
⏰ setTimeout vs requestAnimationFrame
setTimeout(fn, 10)
document.body.appendChild(element);
setTimeout(() => {
element.classList.add('active');
}, 10);
- 임의의 시간 후 실행
- 렌더링 사이클과 무관하게 실행
- 10ms 후 실행되지만, 렌더링 중간에 실행될 수도 있음
- 불필요한 렌더링을 유발할 수 있음
requestAnimationFrame(fn)
document.body.appendChild(element);
requestAnimationFrame(() => {
element.classList.add('active');
});
- 다음 렌더링 직전에 실행
- 브라우저가 "이제 그릴 준비됐어!"라고 할 때 실행
- 렌더링 사이클과 완벽하게 동기화
- 부드러운 애니메이션 보장
🔄 왜 두 번 호출하나요?
requestAnimationFrame(() => {
requestAnimationFrame(() => {
element.classList.add('active');
});
});
이유:
1. 첫 번째 RAF: DOM에 요소 추가 후 첫 렌더링 대기
- 요소가 DOM 트리에 추가됨
- 초기 스타일 계산 (opacity: 0, scale: 0.9)
- 화면에 그려짐 (보이지 않는 상태)
2. 두 번째 RAF: 초기 상태가 확정된 후 실행
- 이제 transition 시작점이 확정됨
- active 클래스 추가
- CSS transition 애니메이션 시작
📊 타이밍 비교
// ❌ 즉시 실행 - 애니메이션 없음
element.appendChild();
element.classList.add('active'); // 0ms - 초기 상태가 없어서 transition 안됨
// ⚠️ setTimeout - 불안정
element.appendChild();
setTimeout(() => {
element.classList.add('active'); // 10ms - 타이밍이 보장 안됨
}, 10);
// ✅ RAF - 안정적
element.appendChild();
requestAnimationFrame(() => {
requestAnimationFrame(() => {
element.classList.add('active'); // 다음 프레임 (약 16-33ms)
});
});
🎯 핵심 차이점
| 특징 | setTimeout | requestAnimationFrame |
|--------- ---|----------- --|-----------------------|
| 실행 시점 | 지정한 ms 후 | 다음 렌더링 직전 |
| 렌더링 동기화 | ❌ 없음 | ✅ 완벽 동기화 |
| 성능 | 불필요한 렌더링 가능 | 최적화됨 |
| 애니메이션 부드러움 | 떨림 가능 | 항상 부드러움 |
| 브라우저 최적화 | ❌ | ✅ (탭 비활성 시 중지) |
💡 실제 사용 예시
// 🎬 애니메이션이 필요한 경우
requestAnimationFrame(() => {
element.style.transform = 'translateX(100px)';
});
// ⏱️ 단순 지연이 필요한 경우
setTimeout(() => {
console.log('1초 후 실행');
}, 1000);
// 🎨 CSS transition 트리거
element.style.display = 'block';
requestAnimationFrame(() => {
requestAnimationFrame(() => {
element.style.opacity = '1'; // transition 작동
});
});
결론: requestAnimationFrame은 브라우저의 그리기 주기와 완벽하게 동기화되어 애니메이션을
부드럽고 효율적으로 만드는 전문 도구입니다!
🔎 CSS Transition 작동 과정
요소가 DOM에 추가됨
자바스크립트로 element.appendChild() 하거나, 서버에서 HTML을 내려주면 브라우저는 DOM(Document Object Model) 트리에 그 요소를 넣습니다.
브라우저가 초기 상태 렌더링
DOM에 들어온 요소는 CSSOM(CSS Object Model) 과 합쳐져서 렌더 트리를 구성합니다.
이때 적용되는 클래스/스타일 중에서 “시작 상태(start state)”가 반영됩니다. (예: opacity: 0)
reflow & repaint 발생
브라우저가 레이아웃을 다시 계산하고(reflow), 픽셀 단위로 화면에 그립니다(repaint).
즉 “초기 상태”를 사용자 화면에 그린 거예요.
클래스가 추가되어 최종 상태로 변화 요청
자바스크립트에서 element.classList.add("active") 같은 동작을 하거나, CSS 상태 변화(:hover 등)가 발생합니다.
CSS transition 이 정의되어 있다면, 브라우저는 이전 값 → 새로운 값 사이를 일정 시간에 걸쳐 보간(interpolate)해서 애니메이션합니다.
예: opacity: 0 → opacity: 1 으로 0.5초 동안 변화.
Transition 실행
이 과정에서 브라우저는 중간 단계의 스타일을 계산하면서 매 프레임마다 repaint 합니다.
개발자가 지정한 transition-duration, transition-timing-function 값에 따라 부드럽게 변화합니다.
👉 즉, CSS Transition 은 “상태 변화”가 감지된 순간, 브라우저가 현재 상태와 목표 상태 사이를 보간해서 화면에 반영하는 것
💡 핵심 정리
DOM에 추가될 때 이미 최종 클래스를 붙여버리면 브라우저는 “초기 상태를 렌더링할 기회”를 갖지 못합니다 → transition이 눈에 띄지 않고 바로 최종 상태로 그려져 버림.
그래서 보통은:
element.classList.add("hidden"); // 초기 상태
document.body.appendChild(element);
requestAnimationFrame(() => {
element.classList.add("active"); // transition 발동
});
이런 식으로 한 프레임 늦게 최종 상태 클래스를 붙여서 transition 을 보장합니다.