검색창에 www.google.com을 검색하면 dns를 통해 해당 서버의 ip 주소를 가져오고 ip에 해당하는 서버와 tcp 연결이 이루어지면 이 위에서 http 통신이 이루어져 html 문서를 가져오게 된다. 이 때 가져온 html 문서의 렌더링 과정 대해 정리해 보았다.
dns 관련 포스팅 -> https://velog.io/@jeongye01/CSDNS%EB%9E%80
tcp 연결 관련 포스팅 -> https://velog.io/@jeongye01/CS-%EC%8A%A4%ED%84%B0%EB%94%94-TCP-%EC%97%B0%EA%B2%B0-%EC%A0%95%EB%A6%AC
HTML과 CSS는 표준 명세가 존재하고 이는 W3C(World Wide Web Consortium)에서 지정한다. 모던 브라우저들은 이를 기반으로 HTML파일을 해석한다. 과거에는 브라우저들이 일부만 이 명세에 따라 구현하고 독자적인 방법으로 확장함으로써 많은 크로스 브라우징 이슈를 유발했다.
브라우저 렌더링 과정에 많은 엔진들이 관여한다.
렌더링 엔진은 좀 더 나은 사용자 경험을 위해 가능하면 빠르게 내용을 표시하는데 모든 HTML을 파싱할 때까지 기다리지 않고 배치와 그리기 과정을 시작한다. 네트워크로부터 나머지 내용이 전송되기를 기다리는 동시에 받은 내용의 일부를 먼저 화면에 표시하는 것이다.
DOM은 html문서의 모든 요소를 노드 객체화 하여 트리 구조로 구성한 것이고,브라우저가 내부적으로 웹 페이지를 표현하는 방법일 뿐만 아니라 웹 개발자가 JavaScript를 통해 상호작용(DOM 조작)을 할 수 있는 데이터 구조이자 API이다.
위는 html 태그 구조이다. 이 태그는
우리가 사용하는 모던 웹서비스(ex.유튜브,당근 마켓 등등)들에서는 다양한 인터랙션들이 발생한다. 어떤 인터렉션("좋아요" 버튼 클릭, 댓글 달기, 장바구니 목록 담기 등)에 의해서 DOM에 변화가 발생하게 되면 Render Tree와 Layout을 재생성(reflow)하고 repaint 과정을 반복하게 된다. 따라서 dom 조작 횟수를 최소화시킬 필요가 있다.
Virtual DOM의 목적은 DOM 조작 횟수를 최소화하여 불필요한 리플로우(Reflow)와 리페인트(Repaint)를 방지하는 것이다.
Virtual DOM(DOM의 추상화 버전, dom 조작 api를 가지고 있지 않다.)은 원본 DOM을 복제한 가상의 DOM 구조를 생성하고, 변경 사항을 적용할 때마다 가상 DOM과 실제 DOM을 비교한다. 가상 DOM에서 변경 사항을 감지하면 해당 변경 사항만 실제 DOM에 반영한다. 이 과정에서 브라우저는 한 번만 렌더링을 하면 된다. 이렇게 하면 필요하지 않은 DOM 조작을 최소화하여 성능을 향상시킬 수 있다.
-> virtual dom에 변경 내역을 한번에 모으고(버퍼링) 실제 dom과 변경된 virtual dom의 차이를 판단한 후, 구성 요소의 변경부분만 찾아 dom fragment에 묶어 그에 따른 렌더링을 한번만 하게 됨.
만약 CSS 규칙이 변경되면, 브라우저는 해당 요소들의 위치와 크기를 다시 계산해야 하며, 이 과정에서 reflow와 repaint가 발생할 수 있다. 그러나 reflow는 항상 일어나는 것이 아니기에 css 속성이 reflow와 repaint에 어떤 영향을 미치는지 알아둘 필요가 있다.
html 파싱은 위에서 부터 아래로 순차적으로 이루어는데 중간에 js를 만나 js를 실행하는 경우 이 js가 아직 만들어지지 않은 dom을 조작하려고 할 수 있는데 이때 예기치 못한 오류가 발생할 수 있다. 또 js 파일 실행이 오래걸리는경우 html css 파싱이 지체되어 화면가 콘텐츠표시 되지 않으면 좋지 않은 사용자경험을 유발 할 수 있다. 따라서 "특별한 설정"이 없는 자바스크립트 태그는 제일 밑에 두도록 한다.
혹은 스크립트를 제일 위에 두고 async or defer 옵션을 사용할 수 있다.
async : 자바스크립트 로드만 비동기로 실행하고 다운로드가 완료되면 js실행.(html 파싱 중단). 먼저 로드된 js 순서로 실행이 되므로 실행 순서 보장이 안된다.( 예기치 못한 오류가 발생)
defer : js 로드를 비동기로 하고 html이 모두 파싱 된후 마지막에 배치된 순서대로 js가 실행된다. 실행 순서가 순차적이기 때문에 보통 defer로 선언한다.
필자는 지금까지 크롬브라우저와 사파리 기준으로 개발을 진행했었는데, 종종 같은 html css 코드인데, 사파리와 크롬에서 출력결과가 달랐다. 이 포스팅을 작성하면서 검색해보니 크롬은 Blink 렌더링 엔진을 사용하고 사파리는 Webkit 렌더링 엔진을 사용한다고 한다. 개인적으로 크롬이 좀 코드를 막(?) 적어도 의도한대로 잘 표현해 주었고 사파리는 조금 더 엄격하게 코드를 작성해야 의도한 대로 표현이 되었다.
결론적으로 각각의 렌더링 엔진의 차이로 인해 발생하는 크로스브라우징 이슈를 인지하고 여러 브라우저에서 테스트를 통해 더 나은 서비스를 만들 수 있도록 해야겠다. 그냥 브라우저들이 모두 좀 더 합심해서 통일성을 갖춰주면 좋겠다
참고
https://d2.naver.com/helloworld/5237120
https://legacy.reactjs.org/docs/faq-internals.html
https://velog.io/@suminllll/Reflow-RepaintNetwork