앞 편에서 브라우저가 그릴 대상을 골라내고, 그것들이 화면의 어디에 얼마만 한 크기로 놓일지를 계산하는 과정을 살펴보았다. 이제 그 계산으로 윤곽이 정해진 화면 위에 실제로 색을 입히고, 여러 겹으로 나뉘어 그려진 그림들을 하나로 합쳐 최종 화면을 완성하는 단계로 넘어간다. 색칠과 합치기는 렌더링 과정의 마지막 두 걸음이자, 화면의 부드러움을 지키는 데 가장 큰 지렛대가 숨어 있는 지점이다.
이 두 단계를 이해하면 왜 어떤 움직임은 매끄럽고 어떤 움직임은 뚝뚝 끊기는지가 설명된다. 겉으로는 똑같이 무언가가 스르륵 움직이는 것처럼 보여도, 그 뒤에서 브라우저가 치르는 대가는 하늘과 땅 차이일 수 있다. 이번 편에서는 색칠과 합치기가 각각 무엇을 하는지, 왜 어떤 변화는 값싸고 어떤 변화는 비싼지를 짚는다.
윤곽 위에 색을 입히는 단계
자리 잡기 계산이 끝나면 각 요소가 화면의 어디에 얼마만 한 크기로 놓일지가 정해진다. 하지만 이것만으로는 아직 아무것도 눈에 보이지 않는다. 위치와 크기라는 윤곽만 있을 뿐, 그 안을 채울 색과 무늬가 아직 칠해지지 않았기 때문이다. 색을 입히는 단계는 바로 이 윤곽 안에 실제 색과 글자와 그림자와 테두리를 채워 넣는 작업이다.
브라우저는 이 단계에서 각 요소의 겉모습을 이루는 모든 세부를 하나하나 그려 낸다. 배경색을 칠하고, 글자의 획을 긋고, 테두리 선을 두르고, 그림자를 드리우는 일이 모두 여기서 일어난다. 스타일로 지정한 온갖 꾸밈이 실제 점의 색으로 바뀌는 순간이 바로 이 색칠 단계다.
흥미로운 점은 이 색칠이 한 장의 그림으로 한 번에 이뤄지지 않는다는 것이다. 브라우저는 화면을 여러 겹의 층으로 나누어 각 층을 따로따로 그린다. 마치 투명한 유리판 여러 장에 각기 다른 부분을 그린 뒤 겹쳐 놓는 것과 비슷하다. 이렇게 나누는 이유는 나중에 일부만 바꿔 다시 그릴 때 큰 이득을 주기 때문이다.
색칠은 자리 잡기 계산에 비하면 상대적으로 가벼운 편이지만 결코 공짜는 아니다. 특히 그림자나 둥근 모서리, 반투명한 겹침처럼 계산이 많이 필요한 꾸밈이 넓은 영역에 걸쳐 있으면 색칠 비용이 제법 커진다. 중요한 것은 색칠이 자리 잡기 계산과는 별개의 단계라는 사실이다. 어떤 변화는 자리를 다시 잡을 필요 없이 색만 다시 칠하면 되고, 어떤 변화는 자리부터 다시 계산해야 한다. 이 둘을 구분하는 감각이 성능을 다루는 첫걸음이다.
겹겹이 나눠 그리는 층
앞서 브라우저가 화면을 여러 겹의 층으로 나눠 그린다고 했다. 이 층 나누기는 성능을 이해하는 데 핵심이 되는 개념이라 조금 더 깊이 들여다볼 만하다. 브라우저는 어떤 요소들을 별도의 층으로 떼어내, 나머지와 독립적으로 그려 두고 관리한다.
층을 나누는 이유는 다시 그리기의 범위를 좁히기 위함이다. 만약 화면이 한 장의 그림이라면, 그 안의 작은 요소 하나가 움직여도 그림 전체를 다시 그려야 한다. 하지만 움직이는 요소만 별도의 층으로 떼어 두면, 그 층만 옮기고 나머지 층은 손대지 않아도 된다. 이 차이가 부드러움을 만든다.
어떤 요소가 별도의 층이 되는지는 브라우저가 여러 조건을 보고 판단한다. 다른 요소들 위로 떠서 움직이는 요소나 특별한 방식으로 변형되는 요소는 층으로 분리되곤 한다. 스타일로 어떤 힌트를 주면 브라우저가 미리 층을 준비하기도 한다. 다만 이 층 분리는 공짜가 아니라, 각 층이 저마다 메모리를 차지하는 대가를 치른다.
그래서 층은 무작정 많이 만든다고 좋은 것이 아니다. 층이 지나치게 많아지면 각 층을 담아 둘 메모리가 늘어나고, 나중에 그 많은 층을 하나로 합치는 작업도 무거워진다. 층은 최적화의 도구로 쓰되 필요한 곳에만 아껴서 써야 한다. 자주 움직이거나 자주 겉모습이 바뀌는 요소를 골라 별도의 층으로 삼으면, 그 요소가 변할 때 주변 전체를 다시 그리지 않아도 되어 화면이 눈에 띄게 매끄러워진다. 반대로 거의 바뀌지 않는 요소를 굳이 층으로 떼어내면 메모리만 낭비할 뿐 얻는 것이 없다.
층을 합쳐 완성하는 합치기
여러 층에 나눠 그린 그림들은 아직 화면에 나타난 것이 아니다. 이 층들을 정해진 순서와 위치에 맞춰 하나로 겹쳐야 비로소 우리가 보는 최종 화면이 된다. 이 마지막 단계가 합치기다. 여러 장의 투명한 유리판을 정확히 포개어 한 장의 완성된 그림으로 만드는 작업인 셈이다.
합치기의 가장 큰 특징은 이 작업을 화면을 담당하는 전용 장치가 아주 빠르게 처리한다는 점이다. 각 층은 이미 그려진 상태이므로, 합치기는 그것들을 어디에 어떻게 겹칠지만 결정하면 된다. 층을 옮기거나, 살짝 키우거나, 투명도를 조절하는 정도의 일은 이 전용 장치가 눈 깜짝할 사이에 해낸다.
여기서 아주 중요한 사실이 나온다. 이미 그려 둔 층을 옮기거나 투명도를 바꾸는 변화는, 층의 내용을 다시 그릴 필요가 없으므로 합치기만으로 끝난다는 것이다. 자리 잡기 계산도, 색칠도 건너뛰고 오직 합치기만 다시 하면 된다. 이것이 어떤 움직임이 유독 매끄러운 이유다.
그래서 무언가를 움직이게 만들 때, 그 움직임을 층을 옮기는 방식으로 표현하면 화면이 부드럽다. 요소의 실제 위치 값을 조금씩 바꿔 밀어내면 매번 자리 잡기 계산이 다시 일어나 무겁지만, 층 자체를 슬쩍 옮기는 방식으로 표현하면 합치기만으로 처리되어 훨씬 값싸다. 투명도를 서서히 바꾸는 변화도 마찬가지다. 요소를 서서히 나타나게 하거나 사라지게 할 때, 그 겉모습을 매번 다시 그리는 대신 층의 투명도만 조절하면 합치기 단계에서 값싸게 처리된다. 나타남과 움직임이라는 가장 흔한 두 가지 변화가 모두 합치기만으로 표현될 수 있다는 것은 큰 이점이다.
값싼 변화와 비싼 변화
지금까지의 이야기를 하나로 꿰면, 화면의 변화에는 세 가지 깊이가 있다는 것을 알 수 있다. 가장 깊은 변화는 자리부터 다시 잡아야 하는 것이고, 그다음은 색을 다시 칠해야 하는 것이며, 가장 얕은 변화는 합치기만 다시 하면 되는 것이다. 같은 움직임처럼 보여도 어느 깊이에 해당하느냐에 따라 비용이 크게 갈린다.
요소의 크기나 위치를 실제로 바꾸는 변화는 가장 비싸다. 자리 잡기 계산이 다시 일어나고, 그 결과 색칠도 다시 하고, 마지막에 합치기까지 이어지기 때문이다. 세 단계를 모두 치러야 하므로, 이런 변화를 자주 반복하면 화면이 무거워진다. 잦은 움직임에는 되도록 이런 변화를 피해야 한다.
색이나 배경만 바꾸는 변화는 그보다 값싸다. 자리는 그대로이므로 자리 잡기 계산을 건너뛰고 색칠부터 다시 하면 된다. 한 단계를 아낀 셈이다. 다만 색칠도 넓은 영역에 반복되면 부담이 되므로, 아주 잦은 변화라면 이마저도 조심하는 것이 좋다. 가장 값싼 변화는 앞서 말한 대로 층을 옮기거나 투명도를 조절하는 것이다. 자리 잡기도 색칠도 건너뛰고 오직 합치기만 하면 되므로, 아무리 자주 반복해도 부담이 적다.
이 세 깊이의 구분은 성능 문제를 진단할 때 아주 유용한 잣대가 된다. 화면이 버벅인다면, 잦은 변화가 어느 깊이에 해당하는지를 먼저 확인한다. 만약 잦은 움직임이 자리 잡기까지 건드리고 있다면, 그것을 층을 옮기는 방식으로 바꾸는 것만으로 문제가 풀리는 경우가 많다. 어떤 변화를 만들 때 이것이 어느 칸에 걸리는지를 먼저 가늠하고, 되도록 얕은 칸에서 해결하는 습관이 화면의 부드러움을 지킨다.
화면 밖은 그리지 않는다
색칠 비용을 아끼는 가장 근본적인 방법은 그릴 필요가 없는 것을 그리지 않는 것이다. 사용자의 눈에 지금 보이는 영역은 화면 크기만큼으로 한정되어 있는데, 긴 문서는 그 화면을 훌쩍 넘는 내용을 담고 있다. 화면 밖으로 벗어난 부분까지 매번 색을 칠하는 것은 아무도 보지 않는 그림을 그리는 셈이다.
그래서 브라우저는 지금 화면에 걸리는 부분을 우선해 그리고, 화면 밖의 부분은 그리기를 미루거나 건너뛴다. 사용자가 스크롤해 새 영역이 화면에 들어오는 순간에 맞춰 그 부분을 그리면, 눈에 보이지도 않는 영역을 미리 칠하느라 시간을 쓰지 않아도 된다. 이 원리는 특히 항목이 수천 개에 이르는 긴 목록에서 큰 차이를 만든다.
이 발상을 더 밀고 나가면, 화면 밖의 부분은 색칠뿐 아니라 배치 계산까지 아낄 수 있다. 브라우저에 어떤 부분이 지금 화면과 무관함을 알려 주면, 그 부분은 실제로 화면에 가까워질 때까지 그리기도 자리 잡기도 하지 않은 채 대기한다. 처음 문서를 세울 때 화면에 걸리는 몇 개 항목만 온전히 처리하면 되므로, 첫 화면이 훨씬 빨리 뜬다.
다만 화면 밖을 건너뛰는 데는 주의할 점이 있다. 아직 그리지 않은 부분의 크기를 브라우저가 정확히 알지 못하면, 나중에 그 부분이 실제로 그려질 때 예상과 크기가 달라 화면이 출렁일 수 있다. 그래서 미뤄 둔 부분이 대략 얼마만 한 자리를 차지할지를 미리 알려 주어, 실제로 그려질 때 자리가 어긋나지 않게 하는 배려가 함께 필요하다. 보이지 않는 것을 그리지 않되, 그 빈자리의 크기는 미리 짐작해 두는 것이 요령이다.
이 방식은 스크롤이 빠르게 이뤄질 때 특히 시험대에 오른다. 사용자가 빠르게 훑어 내리면 새 영역이 잇따라 화면에 들어오는데, 그때마다 그리기와 배치를 새로 하느라 오히려 끊김이 생길 수 있다. 그래서 화면에 곧 들어올 부분을 조금 앞서 준비해 두는 여유를 두면, 스크롤이 앞질러도 빈 화면이 노출되지 않는다. 보이지 않는 것을 아끼는 것과 곧 보일 것을 미리 챙기는 것 사이의 균형이 이 방식의 관건이다.
층을 잘 다루는 지혜
층을 활용해 화면을 부드럽게 만드는 첫 번째 지혜는, 자주 변하는 것을 미리 별도의 층으로 준비해 두는 것이다. 브라우저에게 어떤 요소가 곧 변할 것이라는 힌트를 주면, 브라우저는 그 요소를 미리 층으로 떼어내 변화에 대비한다. 그러면 실제 변화가 시작될 때 층을 새로 만드느라 끊기는 일이 줄어든다.
다만 이 힌트는 신중하게 써야 한다. 곧 변할 것이라는 신호를 아무 데나 남발하면, 브라우저가 필요 이상으로 많은 층을 미리 만들어 메모리를 낭비한다. 이 힌트는 변화가 실제로 임박한 요소에만, 그리고 변화가 끝나면 거둬들이는 식으로 아껴 써야 한다. 힌트는 상비약이 아니라 필요할 때만 꺼내 쓰는 처방이다.
두 번째 지혜는 층으로 떼어낼 요소를 화면에서 겹치는 관계까지 고려해 고르는 것이다. 어떤 요소를 층으로 만들면, 그 위나 아래에 겹쳐 있던 다른 요소들까지 덩달아 층으로 승격되어 층의 수가 예상보다 크게 불어날 수 있다. 층을 만들기 전에 그 요소가 무엇과 겹쳐 있는지를 살펴, 뜻하지 않은 층 폭발을 피해야 한다. 세 번째 지혜는 색칠 비용이 큰 꾸밈을 넓은 영역이나 자주 변하는 영역에 남용하지 않는 것이다.
흐릿한 그림자나 복잡한 겹침 효과는 보기에는 좋아도, 넓게 깔리거나 자주 다시 그려지면 색칠 단계를 무겁게 만든다. 이런 꾸밈은 꼭 필요한 곳에만 절제해서 써야 한다. 네 번째 지혜는 성능을 눈으로 확인하며 다루는 것이다. 어떤 변화가 어느 깊이에서 처리되는지는 짐작만으로는 확신하기 어렵다. 화면을 그리는 과정을 들여다볼 수 있는 도구로 실제로 어느 단계를 다시 밟는지를 확인하고 나서 판단해야 한다. 이 확인 습관은 이 서브영역의 마지막 편에서 따로 다룬다. 정리하면, 자리 잡기와 색칠은 무겁고 합치기는 값싸므로, 잦은 변화를 되도록 합치기만으로 처리되게 표현하는 것이 부드러운 화면의 비결이다. 다음 편에서는 이 화면 위에서 실제로 동작을 만들어 내는 스크립트가 브라우저 안에서 어떻게 실행되는지를 살펴본다.