이번에 블로그 디자인을 리뉴얼하면서 스크롤 이벤트에 따른 애니메이션이 들어가게되었고, 이를 위해서는 JS를 이용한 애니메이션기법 외에는 답이 없다는 판단을 했습니다. 따라서 JS를 이용한 고전적인 event를 받고 이를 top값을 바꿔 움직이는 애니메이션을 구현했습니다. transform을 이용하면 조금 더 간단하고 성능도 우월한 개발이 가능하지 않을까 생각하여, 코드를 수정해서 퍼포먼스를 비교해봤습니다. 처음 웹을 공부할때만 해도 IE6, 7 버전의 지원이 현실적인 문제였고, 당시 웹은 지금에 비해서 애니메이션에 대한 요구가 적었습니다. 애니메이션을 구현한다 하더라도 jQuery의 animate함수를 이용하거나 css property에 직접 접근하는 방법이 대부분의 개발자가 사용하는 방법이었고, CSS3 animation이 지원이야 했지만 아직 개발에 직접 적용하기에는 구형 브라우저 지원이라는 큰 장벽이 있었습니다. 하지만 근 1-2년에는 대부분의 대형 웹사이트마저도 구형 브라우저의 지원을 차단하는 방식으로 돌아섰습니다. 그래서 생각보다 정말 성능이 좋다면 이제는 transform을 써도 괜찮지 않을까 생각해봅니다.

크롬의 렌더링 순서

일반적인 브라우저에서 렌더링 순서를 다시 한번 살펴보았습니다. 우선 HTML DOM 파싱을 진행하고, Recalculate style(CSS 파싱)을 진행합니다. 이 과정을 크롬의 Devtools로 보면 Parse HTML이라는 과정으로 묶어서 나옵니다. 이 과정을 통해 얻은 DOM과 CSSDOM을 통해서 css 설정을 입힌 Render Tree를 작성합니다. Render Tree를 얻으면 비로소 Layout(레이아웃)을 계산할 수 있게 됩니다. 어떤 element(요소)를 어디에 배치할지 너비, 높이를 얼마를 갖을지 계산합니다. 이 과정은 각 요소를 배치하는 과정이고 레이아웃 과정이 끝나면 paint가 시작됩니다. 페인팅 과정을 통해서야 우리가 의도한 디자인이 픽셀들을 갖고(Rasterize) 화면에 그려지게 됩니다.

정리해보면, 아래와 같습니다.

  1. Parse HTML
    1. HTML DOM parsing
    2. CSS DOM parsing(Recalculate style)
  2. Render Tree
  3. Layout
  4. Paint (Rasterize)

JS를 통한 애니메이션

우선 CSS3에는 애니메이션이 있지만 JS를 이용해야만 하는 경우가 있습니다. (지금 블로그의 예가 그렇습니다.) js를 사용하는 경우 유저의 input혹은, timer와 같은 여러종류의 이벤트 핸들러의 js 코드를 통해서 css를 직접 수정하게되고 그러면 위의 크롬의 렌더링 순서 Recalculate style부터 다시 진행하게됩니다. 당연한 이야기지만 이 과정을 줄이면 줄일 수록 속도는 향상됩니다.

개발자 도구를 이용한 퍼포먼스 비교

position을 이용한 애니메이션 렌더링 시간 transform을 이용한 애니메이션 렌더링 시간

현재 블로그의 타이틀 부분, ‘subscribe rss’ 버튼, ‘생계코딩 이야기’을 모두 transform을 이용해서 움직이도록 구현하고 이를 비교했습니다. 당연히 transform이 신기술이고, 빠를것이라 예상했고, 실제 적용하고 눈에 보기에도 더 부드러웠습니다, 하지만 프레임 수가 낮게 나왔습니다. position을 이용한 경우 1 프레임의 렌더링 시간이 약 18ms, transform을 이용한 경우에는 약 31ms라는 결과가 나왔습니다. (transform 절대 쓰지 마라.)

조금 더 자세한 조사가 필요했고, 원인을 규명해볼 필요가 있었습니다. 우선 여기서 렌더링 시간이란 존재가 굉장히 애매합니다. fps는 화면에 구성요소를 전부 그리고 완성하면 1 frame으로 계산합니다, 크롬에 GPU Rasterize 기술이 들어온건 오래된 일이고, 문제는 중간중간에 GPU rasterize 도중에 잘라먹는 경우가 부지기수 입니다. 그래서 과연 이게 정상적으로 프레임이 렌더링 된건가에 대한 의문이 남았고, 이를 확인하기 위해 프레임을 스크린샷으로 남겨서 다시 확인해봤습니다.

위 이미지는 position을 이용한 애니메이션 구현 화면중에 일부 프레임을 캡쳐한 결과입니다. 여기서 문제를 찾을 수 있었습니다. 천천히 스크롤할 시에는 전혀 볼 수 없는 계산이 덜 끝난 화면도 일단은 넘어가고 다음 프레임을 렌더링합니다. 이에 비해 transform으로 구현한 경우에는 이런 비완성된 프레임을 거의 찾을 수 없었습니다. 이런 현상으로 인해 transform으로 구현할때 fps는 떨어지지만 전체적으로 애니메이션이 부드러운 느낌을 받을 수 있었던것이라 생각합니다. 개인적인 추론은 position같은 경우에는 기존에 cpu 기반에서 페인팅이 완료되어야하지만, 이를 chrome에서는 gpu레벨로에서 동작하도록 구현하고 있습니다. 하지만 transform은 원래부터 gpu를 통해서 레이어를 렌더링 한다고 알고 있습니다. 만약 position을 이용한 과정은 비동기적으로 동작하는 부분이 있고, transform을 이용한 과정은 동기적으로 동작하는 부분만 있다면 위와 같은 현상이 일어날 것 입니다.

이유야 어떻게 되었던 FPS를 비교하는 과정은 약간 의미가 없어졌다 판단했고, GPU 사용은 기준을 정하고 측정하는게 불가능했기 때문에 CPU 사용률을 확인해보고자 했습니다. CPU 사용률에서는 압도적으로 transform 과정이 빨랐고 특히 타이틀 렌더링(Recalculate style & Layout)에서는 2배 이상 빠른 성능을 확인할 수 있었습니다.

position을 이용한 애니메이션 cpu 사용시간 transform을 이용한 애니메이션 cpu 사용시간

결론적으로 transform을 사용하는것이 무조건 옳은가에 대한 고민을 했습니다. 일단 fps는 조금 떨어지덜도 완벽한 애니메이션 구현되어 전체적으로 애니메이션이 부드러운 transform 방식을 사용하는게 옳은 방법일 것입니다. 하지만 position을 이용해서 개발할때 이런 효과가 있음을 알고 상황에 따라서 적용할 필요가 있습니다.

그래도 transform을 쓰기 꺼려진다

저도 브라우저 지원 문제 떄문에 transform과 같은 CSS3의 기술을 쓰는걸 굉장히 꺼려왔습니다. 하지만 이 블로그는 개발자 블로그고 그 특성상 IE 유저가 거의 오지 않고 오더라도 10이상의 유저가 온다는 사실을 확인했습니다. 다른 자료로 KISA의 자료가 있습니다 2016년 11월 하반기 기준으로 70%의 사람들이 IE 10이상의 버전을 사용하고 있습니다.

IE 브라우저 점유율
IE 10 42.1
IE 11 28.0
IE 9 8.13
IE 8 7.51
IE 7 0.18
IE 6 0.03

물론 유저의 성격에 따라 다르겠지만, 이제는 개인적으로 생각해둔 지원 브라우저의 기준과 운영체제의 기준을 높여도 괜찮은 시대가 왔지 않았나 생각합니다.