[ Browser ] 모던웹 브라우저 들여다보기 (part2)

이예빈·2022년 5월 25일
0

출처
https://developer.chrome.com/blog/inside-browser-part2/

위 출처의 내용을 번역본 입니다.
자세한 내용은 원문을 참고해주세요.

탐색도중 어떤 일이 일어날까?

이 게시물에서는 웹사이트를 표시하기 위해 각 프로세스와 스레드가 통신하는 방법을 더 자세히 살펴봅니다.

웹 브라우징의 간단한 사용 사례를 살펴보겠습니다.
브라우저에 URL을 입력하면 브라우저가 인터넷에서 데이터를 가져와 페이지를 표시합니다. 이 게시물에서는 사용자가 사이트를 요청하고 브라우저가 페이지 렌더링을 준비하는 부분(탐색이라고도 함)에 중점을 둘 것입니다.

browser process로 시작합니다.

탭 외부의 모든 것은 브라우저 프로세스에 의해 처리됩니다.
브라우저 프로세스엔 buttons과 input란을 그리는 'UI스레드', 인터넷에서 data를 가져와 네트워크 stack을 처리하는 'Network 스레드', 파일 등에 접근을 관리하는 'Storage 스레드'가 존재합니다.

주소창에 URL을 타이핑하면, 그 입력값은 브라우저프로세스의 UI스레드에서 처리되게 됩니다.

간단한 탐색

step 1: input 처리하기

사용자가 주소 표시줄에 타이핑하기 시작하면,
UI스레드는 "이것이 검색어일지,URL일지"를 가장 처음으로 묻게 됩니다.
Chrome에서는 주소창이 검색어 입력창이기도 해서, UI스레드는 input값을 분석해서
search 엔진으로 보내야할지, 요청한 site로 보내야할지를 결정합니다.

step 2: 탐색 시작하기

사용자가 enter를 누르면, UI스레드는 사이트의 콘텐츠를 얻기위해 네트워크 호출을 시작합니다.
로딩스피너가 탭의 코너에 보이게 되고, Network스레드는 요청에 대한 DNS lookup과 TLS 연결설정과 같은 적절한 프로토콜을 거치게됩니다.

이때 Network스레드는 HTTP 301과 같은 server redirect header를 수신할 수도 있습니다.
이 경우, Network스레드는 서버가 redirect을 요청하도록 UI스레드와 통신합니다.
그런 뒤에 다른 URL요청이 시작될 것입니다.

step 3: 응답 읽기


response body(payload)가 들어오기 시작하면
Network스레드는 필요에 따라 stream의 첫 몇 bytes를 확인합니다.
response의 Content-Type header는 data가 어떤 유형인지 말해야하지만 누락되거나 잘못되었을 수 있으므로,여기에서 MIME 유형 스니핑 이 수행됩니다.

이것은 링크에 있는 소스코드에서 "tricky business"라고 여겨지는 작업입니다.
다른 브라우저는 content-type/payload 쌍을 어떻게 처리하는지 보고싶다면 주석을 읽어보세요.

만약 response가 HTML파일인경우, 다음 스텝은 data를 renderer프로세스에 전달하는 것이겠지만,
만약 zip파일이나 다른종류의 file을 response로 받았다면 이는 다운로드 요청이므로 data를 download manager에 전달해야함을 의미합니다.

여기서 또한 SafeBrowing 검사가 발생하기도 합니다.
만약 도메인과 response data가 알려진 악성사이트와 일치하는 것으로 보이면 Network스레드는 경고페이지를 표시하도록 경고합니다.
추가적으로, 민감한 cross사이트 데이터가 Renderer프로세스에 전달되지 않도록 CORS검사가 발생합니다.

step 4: 렌더러 프로세스 찾기

모든 검사가 완료되고 Network스레드가 브라우저가 요청된 사이트로 이동해도된다고 확신하면 Network스레드는 UI스레드에게 data가 준비되었음을 알립니다.
그러면 UI스레드는 웹페이지 렌더링이 수행할 Renderer프로세스를 찾습니다.

Network요청이 응답을 받는데 수백 밀리초가 걸릴 수 있으므로
이 프로세스의 속도를 높이는 최적화가 적용됩니다.
UI스레드가 step2에서 Network스레드에 URL요청을 보낼 때부터 이미 어떤 사이트로 옮겨가야하는지 알고 있습니다.
UI스레드는 Network요청과 동시에 Renderer프로세스를 사전에 찾거나 시작하려고 합니다.
이렇게 하면 모든것이 예상대로 진행되었을 때
Network스레드가 data를 받으면 Renderer프로세스는 이미 대기상태에 있게 됩니다.
이런 standby 프로세스는 navigation이 서로다른 사이트간에 redirect를 하는 경우엔 사용되지 않을 수 있으며 이런 경우엔 다른 프로세스가 필요하게 됩니다.

steop 5: 탐색 커밋하기

이제 data와 Renderer프로세스가 준비되었으므로
navigation(탐색)을 커밋하기위해 browser프로세스에서 renderer프로세스로 IPC가 전송됩니다.
또한, renderer프로세스가 HTML data를 계속 받을 수 있도록 데이터스트림을 전달합니다.
browser프로세스는 renderer프로세스에서 커밋이 발생했다는 확인을 듣게되면 navigation(탐색)은 완료되고 document로드단계가 시작됩니다.

이때 주소 표시줄이 업데이트되고 보안표시기와 사이트 설정 UI는 새 페이지의 사이트 정보를 반영합니다.
탭에 대한 세션 기록이 업데이트되어 뒤로/앞으로 버튼이 방금 탐색한 사이트로 이동합니다.
탭이나 창을 닫을 때 탭/세션 복원을 용이하게 하기 위해 세션 기록이 디스크에 저장됩니다.

Extra step: 초기 로드 완료하기

navigation(탐색)이 커밋되면 renderer프로세스는 리소스 로드를 계속해나가고 페이지를 렌더링합니다.
renderer프로세스가 렌더링을 "완료"하면, renderer프로세스는 IPC를 browser프로세스로 돌려보냅니다.
( 이는 페이지의 모든 프레임에서 onload이벤트가 발생하고 실행이 완료된 후에 일어나는 일입니다. )
이 시점에, UI스레드는 탭에서 로딩스피너가 돌아가는 것을 멈춥니다.

"완료"라고 말했는데,
그 이유는 이 시점 이후에 클라이언트 측 JavaScript가 추가적인 리소스를 로드하고 새로운 views를 렌더링할 수 있기때문입니다.

다른 사이트로 이동하기

간단한 navigation(탐색)이 완료되었습니다!
그러나 사용자가 주소 표시줄에 다른 URL을 다시 입력하면 어떻게 될까요? 음, browser프로세스는 동일한 단계를 거쳐 다른 사이트로 이동하게 될것입니다.
그러나, 이것이 수행되기 전에, 현재 렌더링된 사이트에서 beforeunload이벤트를 고려하고 있는지 체크할 필요가 있습니다.

beforeunload이벤트는 탭을 닫거나 다른 곳으로 이동하려할 때
"이 사이트를 나가시겠습니까?"라는 alert를 만들 수 있습니다.
당신의 JavaScript코드를 포함한 탭 내부의 모든것들은 renderer프로세스에의해 처리됩니다.
그러므로 browser프로세스는 새로운 navigation(탐색)요청이 들어오면
현재의 renderer프로세스를 체크해야 합니다.

주의
무조건적으로 beforeunload핸들러를 사용하진 마세요!
탐색을 시작하기 전에 이 핸들러를 실행해야 하기 때문에 대기시간이 발생합니다.
따라서 사용자가 페이지에 입력한 데이터가 손실될 수 있음을 경고할때와 같이 필요한 경우에만 추가하세요.

탐색(naviation)이 renderer프로세스에서 시작했다면,
(예.사용자가 링크를 클릭했거나 클라이언트 측 JavaScript가 실행 window.location = "https://newsite.com"된 경우)
renderer프로세스는 먼저 beforeunload핸들러를 확인합니다.
그런 뒤, browser프로세스가 탐색을 시작한 것과 동일한 프로세스를 거치게 되는데, 유일한 차이점은 탐색 요청이 renderer프로세스에서 browser프로세스로 시작된다는 것입니다.

현재 렌더링된 사이트가 아닌 다른 사이트로의 새로운 탐색이 만들어지면,
현재의 renderer프로세스는 unload와 같은 이벤트를 처리하기위해 유지되는 한편,
새로운 탐색을 위한 또다른 render프로세스가 호출됩니다.
자세한 내용 은 페이지 수명 주기 상태에 대한 개요Page Lifecycle API를 사용하여 이벤트에 연결하는 방법을 참조하세요 .

Service Worker의 경우

탐색 절차에 있어 최근의 변경사항이 한가지 있는데 서비스워커가 도입되었다는 점입니다.
서비스워커는 어플의 코드에서 네트워크 proxy를 작성하는 방법을 말합니다.
웹 개발자가 로컬로 캐시할 항목과 네트워크에서 새 데이터를 가져올 시기를 더 잘 제어할 수 있게 해줍니다.
만약 서비스워커가 캐시로부터 페이지를 로드하도록 설정되어있다면 더이상 네트워크로부터 데이터를 요청할 필요가 없어집니다.

기억해야 할 중요 포인트는 서비스워커는 renderer프로세스에서 실행되는JavaScript코드라는 것입니다.
그런데, navigation요청이 들어왔을 때 browser프로세스는 어떻게 사이트가 서비스워커를 갖고있단 걸 알 수 있을까요?

서비스워커가 등록도면 서비스워커의 scope은 reference로 유지됩니다.
탐색이 발생하면, network스레드는 등록된 서비스워커scope에 대한 도메인을 확인하고,
만약 URL에 등록된 서비스워커가 있으면 UI스레드는 서비스워커 코드를 실행할 renderer프로세스를 찾게됩니다.
서비스워커는 캐시에서 데이터를 로드하여 네트워크에서 데이터를 요청할 필요가 없게하거나, 네트워크에 새 리소스를 요청할 수도 있습니다.

탐색 Preload

browser프로세스와 renderer프로세스간의 round trip은 서비스워커가 결국 네트워크로 data요청을 하기로 결정했다면 딜레이를 초래할 수 있음을 확인했습니다.
Navigation Preload는 서비스워커의 시작과 동시에 리소스를 로딩하여 이러한 프로세스의 속도를 높이는 메커니즘입니다.
header에 이러한 요청을 표시해서, 서버로 하여금 이런 요청에 대해선 다른 콘텐츠를 보낼 수 있도록 합니다.
예를들어, 전체 document대신에 업데이트된 data

마무리

이 글에서 우리는 navigation(탐색)동안에 어떤 일이 발생하는지 그리고 어떻게 당신의 웹어플리케이션 코드(response headers와 클라이언트 측 JavaScript)가 브라우저와 상호작용하는지 알아보았습니다.
브라우저가 네트워크로부터 data를 얻기위해 거치는 과정을 아는 것은
왜 navigation preload와 같은 API가 개발되었는지를 쉽게 이해할 수 있게 됩니다.

다음 게시물에서는 브라우저가 페이지를 렌더링하기 위해 HTML/CSS/JavaScript를 다루는 방법에 대해 자세히 알아볼 것입니다.

0개의 댓글