브라우저 렌더링 방식에 대해 설명해보시겠어요?
브라우저에 google.com을 검색하면 어떻게 되는지 설명해주세요.
프론트엔드 면접 질문 하면 빠지지 않는 단골 주제는 바로 브라우저의 렌더링 방식 이라고 할 수 있다.
아마 이 글을 보게된 사람들도 면접을 준비하며 온 경우가 많을 것 같다.
이걸 준비하다 보면 이런 생각이 들게 된다.
아니 외우라니까 외우긴 하는데... 대체 이게 프론트엔드랑 무슨 상관이지? 개발만 잘 하면 되는거 아닌가? 😒
사실 이건 내가 처음 했던 생각이었다. (ㅎㅎ;;;)
물론 그렇다고 프론트엔드 면접에 낚시는 어떻게 하나요? 정도의 아예 쌩뚱맞은 것이라고는 생각하지 않았고,
"알면 좋기야 할거같은데... 이게 필수로 알아야 할 정도로 중요한건가?" 라는 생각을 했었다.
이번에는 예전의 나와 같은 생각을 하는 사람들에게 최신의 렌더링 방식을 설명하고 왜 필요한지에 대한 필요성까지 와닿게 해주고자 정리해보려 한다.
아니 렌더링 방식 설명하는건 그렇다 치는데... 최신이라는 어그로는 좀.... 🤭
글을 시작하기에 앞서 최신
이라는 키워드에 잠깐 설명을 하고자 한다.
이 주제를 검색해 본 사람은 알겠지만 정말 많은 게시물들이 있다.
그리고 그 중 가장 유명한 아티클은 아마 네이버 D2의 번역 아티클 브라우저는 어떻게 동작하는가? 일 것이다.
이 아티클의 원본, 그리고 번역본은 정말 자세하게 잘 설명되어 있다.
그래서 그런지 다른 블로그의 아티클을 보게되면, 이 글을 참조한 글이 정말 많이 보인다.
근데 사실 이 번역 아티클은 2012년 5월 18일에 작성되었고,
원본 아티클은 무려 2011년 8월 5일에 작성되었다.
현재 작성하는 일자 기준으로 약 11년
정도가 흐른 정보라는 것이다.
나는 이 아티클들의 내용이 잘못됐다고 생각한다기 보다는,
오랜 시간이 흐른 만큼 기술적으로 변경된 부분이 있을거라 생각했다.
이 같은 이유로 최신을 붙여 작성한 글이니 최신 이라는 키워드에 오해하지 않았으면 좋겠다!
(무섭다!)
렌더링을 알아보기 전에, 일단 브라우저가 뭔지부터 짚고 넘어가 보자.
여기서 브라우저란
를 해주는 소프트웨어~ 라고 보면 된다.
다들 잘 아는 크롬
, 파이어폭스
, 사파리
, 엣지
, 가 있다.인터넷익스플로어(IE)
이번에 작성하는 게시글은 크롬
기준의 렌더링 방식이다.
한번 크롬
으로 벨로그 글을 본다 생각해보자.
(DNS 등 중간 과정 없다고 가정)
우선 우리는 브라우저의 주소창
에 주소를 적고 엔터를 칠거다.
그럼 브라우저는 입력된 주소값으로 GET
요청을 보내게 된다.
(GET
을 모른다면 이거 공부할때가 아니다. 그것부터 공부해보자.)
요청을 받은 서버는 알맞은 HTML
, CSS
파일을 브라우저 에게 다시 보내줄거다.
근데 만약 브라우저가 이후에 아무런 과정 없이 그냥 보여준다면 아래처럼 보일거다.
즉 렌더링이란 앞서 말한 브라우저의 과정 중
응답 받은 자원을 브라우저에 "알맞게 표시하는 행위"
라고 할 수 있다.
렌더링 과정은 Renderer Process
(렌더러 프로세스)가 담당하게 된다.
Process
내부에는 몇 가지 Thread
(쓰레드)가 구성되어 있는데, 이는 아래와 같다.
프로세스? 쓰레드? 무슨 말인지 하나도 모르겠네... 😢
간단하게 생각해서 Renderer Process
가 집 짓기 프로젝트
라면,
각각의 Thread
는 프로젝트에 참가하는 일꾼
들이라고 생각하면 된다.
각 Thread
의 역할은 아래에서 다시 설명한다.
렌더링의 전체 과정은 8개의 단계로 나눌 수 있다.
- Parsing
- Style
- Layout
- Layer
- Paint
- Tiling
- Raster
- Draw Quad
각 단계들은 하나의 Main Thread
가 아닌 경우에 따라 다른Thread
에서 실행이 되는 걸 볼 수 있는데, 이는 최적화를 위해서다.
만약 하나의 Main Thread
에서 모든 걸 처리한다면 화면에 지연이 발생 할 것이고, 뚝뚝 끊기는 화면을 보게 될 것이다.
때문에 각각의 역할에 따라 Thread
를 분리해서 사용한다.
파싱해봐
그거 파싱 결과가 어떻게 돼?
이런 이야기를 어디서 한번 쯤 들어본 사람이 있을 거다.
위키백과에서는 / 어떤 문장을
/ 그것을 이루고 있는 구성 성분으로 분해하고
/ 그들 사이의 위계 관계를 분석하여
/ 문장의 구조를 결정하는 것
을 말한다 라고 작성되어 있다.
우리가 궁금한 브라우저 렌더링에서의 Parsing
단계는 HTML
코드를 DOM Tree
로 만드는 것이다.
(Tree
를 모른다면 이걸 공부할 때가 아니다! Tree
부터 차근차근 공부해보자!)
여기서 '어떤 문장' 을 HTML
코드라고 생각해보자.
보통 HTML
코드는 아래처럼 생겼다.
<html>
<body>
<h1>제목</h1>
<div><p>내용</p></div>
</body>
</html>
코드를 보면, html
을 이루고 있는 구성 성분으로 Tag
가 존재한다.
<html>
, <body>
, <p>
같은 태그라고 볼 수 있다.
그리고 이들의 위계관계는 태그의 깊이 정도로 생각할 수 있을거다.
구성성분 : 태그
위계관계 : 태그의 깊이
그럼 이를 토대로 Tree
로 바꾸면 이렇게 표현된다.
이 처럼 Parsing
단계 에서는 HTML
코드를 Tree
구조로 만드는 것이다.
우리에게 있는 것 :
DOM Tree
현재 우리는 DOM Tree
를 가지고 있지만, 이걸로는 화면을 보여줄 수 없다.
각 요소들이 어느정도 크기인지, 글꼴은 무엇을 쓰는지, 색깔은 어떻게 되는지 알아야 한다.
Style
단계에서는 CSS
파일을 계산해 Computed Style
을 도출해낸다.
이 단계에서 %
, em
, rem
같은 상대적 수치들도 모두 px
로 계산된다.
관리자 콘솔에서 Computed
탭이 바로 그 내용이다.
잉? 저는 아직 css 파일 작성 안했는데, 그래도
Computed Style
이 보이는데요? 🤔
이건 브라우저가 기본적으로 가지고 있는 기본 세팅 CSS
파일이 존재하기 때문이다.
구글 Chromium의 Default CSS
파일은 여기서 확인할 수 있다.
우리에게 있는 것 :
DOM Tree
withComputed Style
어떤 요소가 있는지도 알고 색상, 크기도 아니까 이제 화면 그릴 수 있겠네요? 😏
라고 생각이 들었다면 한번 아래의 예시를 보자.
친구에게 뭐가 있고, 어떤 색상인지도 얘기해 주었지만 친구는 그릴 수가 없었다.
그 이유는 뭘까?
바로 어디에 어떻게 배치할지를 몰랐기 때문이다.
이 대로 그림을 그렸다가는 친구가 원하는 그림이 나오지 않을 것은 당연하다.
Layout
단계에서는 이 같은 x, y
좌표와 요소의 크기에 대한 정보를 계산한 Layout Tree
를 만들어낸다.
이는 DOM Tree
를 가지고 만드는데, display:none
과 같은 표시되지 않는 요소들을 제외하고 Layout Tree
를 만든다.
우리에게 있는 것 :
DOM Tree
,Layout Tree
자, 우리는 이제 요소도 알고 크기도 알고 위치도 안다.
이제 정말 그릴 수 있을까?
정답은 땡이다.
이유는 뭘까?
바로 뭘 먼저 그려야 할 지 모르기 때문이다.
엥?
html
그거 순서대로 적힌대로 그리면 되잖아요 ㅋㅋ 😋
문제는 무조건 html
코드 순서대로 그려지는게 아니기 때문이다.
그림의 z-index
같은 css
요소가 있으면 그거에 따라 그리는 순서를 달리 해주어야 한다.
그 외에도 position:absolute
나 transform
같은 css
가 있다면 맞춰서 처리해야한다.
때문에 Layout Tree
에서 필요한 부분만 골라 Layer
를 생성해 Layer Tree
를 만들어준다.
개발자 도구에 Layer
를 확인할 수 있는 탭이 있다.
(이건 정리하면서 처음 알았다.)
F12
로 개발자도구를 켠 후 우측 상단의 More tools
에서 Layers
를 클릭하면 된다.
여기까지만 보면 이게 뭐가 다른거지? 싶은데.. 이걸 기울여보면....
(!!!!!!!)페이지가 입체적으로 나타나며 어떤게 더 위에 있는지 알 수 있게 되었다.
앞서 말한 transform
같은 속성이 없으면 평면으로 나타내 보여지고, 아니면 입체적으로 표시된다.
그 예로 우측 하단의 광고사진의 CSS
를 확인해보면 transform:translateZ(0)
가 있는게 확인된다.
우리에게 있는 것 :
DOM Tree
,Layout Tree
,Layer Tree
우리는 이제 Layout Tree
와 Layer Tree
로 Paint Record
(=페인트기록)을 만들거다.
Paint Record
는 Action
, Position
, Style
로 구성되어 있다.
Paint Record
는 위에서 확인한 Layers
탭에서 Paint Profiler
로 확인 가능하다.
Action : drawRect
Position : left:0, top:0, right:1903, left:3277
Style : color '#FFF', strokeWidth:0, ...etc
지금까지 Main Thread
에서 일어나는 일들을 알아봤다.
나온 내용이 정말 많았으니 잠깐 쉬어가며 최적화에 대해 잠시 생각해보자.
지금까지 진행되는 프로세스를 한번 생각해보자.
우리는 DOM Tree
와 Style Computed
를 이용해 Layout Tree
를 만들고, Layer Tree
를 만들고, Paint Records
까지 만들었다.
즉, 앞선 결과를 이용해 다음 작업을 진행했다는 것이다.
DOM Tree
->Layout Tree
+Layer Tree
->Paint Records
여기서 만약 Layout Tree
가 조금만 변하게 된다면, 그 후의 과정들을 전부 다시 계산해야 한다.
대부분의 아티클에서 "렌더링 최적화를 위해서는 ~~~ 해야한다." 등의 이야기가 나오는 이유는 바로 이것이다.
이 같은 최적화 방법들은 동일한 계산을 최대한 덜 하도록 해서 속도를 높이는 것에 있다.
이런 렌더링 일련의 과정들을 잘 이해하고 있어야, 어떤 부분에서 최적화를 할 수 있을 지 생각할 수 있다.
이게 우리가 렌더링 과정
을 공부하는 이유라고 할 수 있다.
머리도 식혔으니 이제 남은 것들을 알아보자!
이제 3가지 단계만 남았다.
이는 Main Thread
가 아닌 각각 Compositor Thread
, Raster Thread
에서 진행된다.
Tiling
단계는 Paint Records
로 Tile
(그 타일 맞음)을 만드는 단계이다.
잘못된 단어 수정 Till -> Tile / ctdlog님 감사합니다 :)
한번에 전체를 렌더링하는 것이 아닌, 필요한 부분만 우선적으로 렌더링 하기 위해 조각조각 나누는 것이다.
velog
메인페이지를 예로 들면 아래와 같다.
velog
의 메인페이지는 Document
, Scroll Bar
두 개의 레이어가 확인된다.
Document
레이어를 Tile
로 쪼개면 아래와 같이 나타날 것이다. (예시)
여기서 브라우저는 Viewport
에서 보여져야 하는 Tile
을 우선적으로 렌더링한다.
이 예제에서는 ①, ②, ③, ④ Tile
이 이에 해당된다.
왜 굳이
Tile
을 나눠서 하나요? 어차피 다 할 건데.. 🙄
이 다음 단계는 Raster
로, 실제 우리가 보는 형태로 비트맵 이미지를 출력해주는 단계이다.
이 단계는 시간이 꽤 소모되므로 큰 하나의 전체 화면을 Raster
하게되면 업데이트 되는 시간이 오래 걸리게 된다.
(토렌트 동작방식 생각해보면 좀 이해가 쉽다)
따라서 속도를 위해 Tile
로 쪼개고 Viewport
에서 보여져야 하는 Tile
를 우선적으로 Raster
하는 것이다.
Raster
, 일명 래스터화
라고 불리는 과정은, 화면을 픽셀로 변환하는 작업을 일컫는다.
앞서 진행된 Tile
들을 가지고 실제 화면에 보여질 비트맵을 만들어주는 과정이다.
이는 Compositor Thread
가 아닌 Tile
마다 각각의 Raster Thread
에서 진행된다.
각각의 타일들의 Raster
가 완료되면 GPU Memory
에 비트맵을 저장하고,
이후 Compositor Thread
로 다시 넘겨준다.
드디어 끝!!!!.. 아닌가요 ?? 😥
우리에게 이제 실제 화면에 보여줄 이미지(Bitmaps
)가 생겼다.
하지만 이걸 바로 보여주면 어떻게 될까?
정확히 이렇진 않더라도 정상적이진 않을거다.
때문에 우리는 마지막으로 Raster
된 Bitmaps
들을 다시 합성해주는 과정이 필요하다.
마지막 단계는 Raster
가 완료된 Bitmaps
들을 이용해 Compositor Frame
을 만드는 과정이다.
여기서 Draw Quads
란 Compositor Frame
을 생성하기 위한 Tile
의 정보를 의미한다.
이제 정말 끝났다.
이전 단계에서 나온 Compositor Frame
을 GPU
로 보내 화면 렌더링을 진행하면 끝이난다.
우리의 생각보다 브라우저는 정말 많은 일을 하고 있었다.
지금 굉장히 축약해서 적어놓은 것이지, 사실 훨신 더 많은 내용들이 있다.
이 글만 읽는 것이 아닌, 첨부한 Reference를 참고해 더 자세히 공부해보는 것을 추천한다. (진짜 완전 추천)
(만약 이 글에 틀린게 있다면 Reference에 있는 게 맞다.)
+ 읽어주셔서 감사합니다.
+ 오타, 내용 지적, 피드백을 환영합니다. 많이 해주실 수록 제 성장의 밑거름이 됩니다.
(글을 정리하다가 안건데, 네이버 D2에서 이 아티클도 번역을 했었다. (갓이버))
(원문의 Part3 이외의 다른 Part도 읽어보면 좋다.)
정말 재미있어서 숨을 쉴 수 없습니다. 맙소사, 이와 같은 지식은 어디서 오는 것입니까? 혹여 가보로 내려옵니까? 나의 공중제비를 멈추게 하십시오! 당신과 같은 분들 덕분에 숨을 쉴 수 없습니다. 당신과 같은 똑똑한 분들 덕분에 인생이 굉장히 재미있습니다. 그러한 지식은 비밀히 보관하지 말고, 재빨리 내용물을 꺼내 주십시오. 세상에 이런 정리글이 다 있겠습니까? 완전한 공부 기계가 틀림 없습니다. 좌뇌, 우뇌, - 모두 망가지고 말았습니다. 나의 뇌를 보상해 내십시오! 이것은 살인 지식입니다! 호흡이 곤란합니다! 제발 목숨을 살려 주십시오!
좋은 글 잘 읽었습니다! 다만 6. Tilling 부분에서 타일(tile)을 깐다라는 의미로, tilling이라는 단어를 사용한 것 같아 till이 아닌 tile이 맞는 것 같습니다. Chrome Developers에 있는 원본 글도 tile 이라고 적혀있더라구요.
와우... 주현쓰 정성글이네!! 추천하고 갑니다 :)