브라우저 살펴보기2 - 내비게이션 과정에서 일어나는 일
기술글을 보고 정리하였음(https://d2.naver.com/helloworld/2922312)
저번에 이어 웹 사이트를 표시하기 위해 각 프로세스와 스레드가 어떻게 통신하는지 좀 더 깊게 알아보고 있음
기술글이 2019년도에 작성된 글이기 때문에 현재에는 사용되지 않는 이벤트등이 기재되어있어 이런 흐름으로 페이지 이동을 한다고 생각하면 될듯 (크롬에 많은 버전 업데이트가 있었기 때문)
내비게이션 과정에서 일어나는 일
브라우저의 주소 표시줄에 URL을 입력하면 브라우저가 인터넷에서 데이터를 가져와서 페이지를 표시함
이 동작에서 사용자가 사이트를 요청하고 브라우저가 페이지 렌더링을 준비하는 과정(= 내비게이션 = 사이트간의 이동)에 초점을 맞춰 살펴볼것임
▶ 브라우저 프로세스에서 시작
브라우저 프로세스가 탭 영역 밖에 있는 모든 부분을 제어
브라우저 프로세스에는 UI 스레드와 네트워크 스레드, 스토리지 스레드 등이 있음
UI 스레드는 브라우저의 버튼과 입력란을 그림
네트워크 스레드는 인터넷에서 데이터를 가져오기 위해 네트워크 스택을 다룸
스토리지 스레드는 파일에 대한 접근을 제어함
주소 표시줄에 URL을 입력하면 브라우저 프로세스의 UI 스레드가 입력을 처리
▶ 간단한 내비게이션
1단계: 입력 처리
사용자가 주소 표시줄에 타이핑을 시작하면 UI 스레드는 먼저 입력되는 내용이 검색어(search query)인지 URL인지 확인
Chrome에서 주소 표시줄은 검색창이기도 함
UI 스레드는 입력되는 내용을 파싱해서 검색 엔진으로 이동할지 요청한 사이트로 이동할지 결정해야
- 사용자가 입력한 문자열이 '검색어'라면 문자열을 사용자가 선택한 검색 엔진의 URL과 조합해 새로운 URL 형태로 변환함. 이런 변환을 위해 Chrome은 검색 엔진의 URL 주소를 관리
입력된 문자열이 검색어인지 URL인지 판별하는 것은 어려운 일이기 때문에 브라우저는 이를 도와줄 서버와 통신하거나 DNS Lookup(도메인 네임 시스템 조회)을 실행하기도 함 네이버에서 개발한 브라우저인 Whale의 '사이트 곧장 가기' 기능도 이 단계에서 실행되는 기능
2단계: 내비게이션 시작
사용자가 Enter 키를 누르면 사이트의 콘텐츠를 가져오기 위해 UI 스레드가 네트워크 호출을 시작
로딩 스피너가 탭의 모서리에 표시되고, 네트워크 스레드는 요청에 대한 DNS Lookup 및 TLS 연결 설정과 같은 적절한 프로토콜을 거쳐 요청을 처리
이때 네트워크 스레드가 HTTP 301과 같은 서버 리디렉션 헤더를 수신할 수도 있음
이런 경우에는 네트워크 스레드가 UI 스레드와 통신해 서버가 리디렉션을 요청했다는 것을 알림
그런 다음 새로운 URL 요청이 시작됨
3단계: 응답 읽기
응답 본문인 페이로드가 들어오기 시작하면 네트워크 스레드는 필요에 따라 스트림의 처음 몇 바이트를 확인
페이로드가 어떤 형식의 데이터인지는 응답 헤더의 Content-Type 헤더가 알려 주지만 정보가 없거나 잘못된 정보가 있을 수 있음 그래서 이때 MIME 스니핑(리소스를 보고 올바른 MIME 타입(파일 형식)을 추측)을 실행해 데이터의 실제 형식을 알아냄(매우 까다로운 작업이기에 브라우저가 다양한 방법으로 Content-Type 헤더와 페이로드를 처리)
응답이 HTML 파일이라면 데이터를 렌더러 프로세스에 전달하는 단계로 넘어감
하지만 응답이 ZIP 형식 파일이나 다른 형식의 파일이라면 다운로드 요청이므로 다운로드 매니저에 데이터를 전달하는 단계로 넘어가야 함(렌더러 프로세스가 다룰 수 있는 데이터 형식일때와 렌더러 프로세스가 다룰 수 없는 데이터 형식일때로 구분 - ex. PDF파일을 반환하는 주소로 접근했을 때 다운로드가 아닌 브라우저에서 문서를 바로 표시)
이 단계는 또한 Safe Browsing의 검사가 실행되는 단계
도메인과 응답 데이터가 악성 사이트로 알려진 사이트와 일치하는 것 같다면 네트워크 스레드는 경고 페이지를 표시하라고 알림
이에 더해서 CORB(Cross-Origin Read Blocking) 기능이 서로 다른 사이트(cross-site)의 민감한 데이터가 렌더러 프로세스에서 실행되지 않게 검사
3단계: 렌더러 프로세스 찾기
모든 검사가 끝나고 브라우저가 요청된 사이트로 이동해야 한다고 네트워크 스레드가 확신하게 되면 네트워크 스레드는 UI 스레드에 데이터가 준비되었음을 알림. 그러면 UI 스레드는 웹 페이지의 렌더링을 수행할 렌더러 프로세스를 찾음
네트워크 요청이 응답을 받기까지 수백 밀리초가 걸릴 수 있기 때문에 이 과정을 더 빨리 진행하기 위한 최적화가 적용되어 있음. 2단계에서 UI 스레드가 네트워크 스레드로 URL 요청을 보낼 때 UI 스레드는 어느 사이트로 이동할지 알고 있음
UI 스레드는 렌더러 프로세스를 먼저 찾거나 네트워크 요청과 동시에 렌더러 프로세스를 시작
이런 방식에서는 모든 것이 예상대로 잘 진행된다면 네트워크 스레드가 데이터를 받을 때 이미 렌더러 프로세스는 준비 상태에 있게 됨. 만약 다른 사이트로 리디렉션이 이루어져 다른 프로세스가 필요하게 되면 미리 준비한 프로세스가 사용되지 않을 수도 있음
4 단계: 내비게이션 실행
데이터와 렌더러 프로세스가 준비되었으므로 내비게이션을 실행하도록 브라우저 프로세스에서 렌더러 프로세스로 IPC 메시지를 전송
또한 렌더러 프로세스가 HTML 데이터를 계속 수신할 수 있도록 브라우저 프로세스는 데이터 스트림(데이터가 지속적으로 흐르는 방식으로 전달되는 것을 의미)을 전달
렌더러 프로세스에서 내비게이션이 실행되었다는 것을 브라우저 프로세스가 확인하고 나면 내비게이션이 완료되고 문서 로딩 단계가 시작
- 로딩은 프로그램 동작에 필요한 프로그램 그 자체와 리소스를 보조 기억장치(가령 하드디스크)에서 주기억장치(메모리)로 불러들이는 과정
이 시점에 주소 표시줄이 업데이트되고 보안 표시와 사이트 설정 UI도 새 페이지의 사이트 정보를 반영해 갱신
탭에 대한 세션 기록이 업데이트되어 뒤로 가기 버튼과 앞으로 가기 버튼도 방금 이동한 사이트를 반영해 작동
탭이나 창을 닫은 이후 탭과 세션을 복원할 수 있게 세션 기록이 디스크 드라이브에 저장
추가 단계: 초기 로드 완료
내비게이션이 실행되면 렌더러 프로세스는 계속 리소스를 로딩하고 페이지를 렌더링
렌더러 프로세스가 렌더링을 '끝내면' 브라우저 프로세스로 IPC 메시지를 보냄(이 시점은 페이지의 모든 프레임에서 onload 이벤트(문서의 모든 콘텐츠가 로드된 후 발생하는 이벤트)의 실행까지 끝낸 이후)
그러면 UI 스레드는 탭에서 로딩 스피너의 작동을 중지
'끝낸다(finish)'라고 표현한 이유는 클라이언트 사이드의 JavaScript가 여전히 추가적인 리소스를 로드하거나 이후에 새로운 뷰를 렌더링할 수도 있기 때문
▶ 다른 사이트로 내비게이션
사용자가 주소 표시줄에 다른 URL을 다시 입력하면 브라우저 프로세스는 동일한 단계를 거쳐 다른 사이트로 이동을 처리함. 하지만 그전에 현재 렌더링된 사이트에서 beforeunload 이벤트(리소스가 언로드 되려고 할때 시작)를 확인해야 함
beforeunload(꼭 필요한 경우 사용. 이벤트 핸들러를 실행해야하기 때문에 대기 시간이 늘어남) 이벤트는 탭을 닫거나 이동하려고 할 때 "이 사이트를 떠나시겠습니까?"라는 경고창을 만들 수 있음
JavaScript 코드를 포함해 탭 안의 모든 것은 렌더러 프로세스에 의해 처리되므로 브라우저 프로세스는 새로운 내비게이션 요청이 들어오면 현재 렌더러 프로세스를 확인해야 함
사용자가 링크를 클릭하거나 클라이언트 사이드 JavaScript에서 window.location = "https://newsite.com" 코드를 실행하는 것과 같이 렌더러 프로세스에서 내비게이션이 시작되면 렌더러 프로세스는 먼저 beforeunload 이벤트 핸들러를 확인
이후에는 브라우저 프로세스가 내비게이션을 시작했을 때와 동일한 과정을 거침
유일한 차이점은 내비게이션 요청이 렌더러 프로세스에서 시작되어 브라우저 프로세스로 넘어간다는 점이다.
현재 렌더링된 사이트와 다른 사이트로 이동하는 새로운 내비게이션이 발생하면 별도의 렌더러 프로세스가 새로운 내비게이션을 처리
현재 렌더링된 사이트를 처리한 렌더러 프로세스는 unload와 같은 이벤트를 처리하기 위해 유지
▶ 서비스 워커
최근에 서비스 워커가 도입되며 내비게이션 과정에도 변화가 생겼음
서비스 워커는 애플리케이션의 코드에 네트워크 프록시(클라이언트와 서버 간의 중계 역할을 하는 컴퓨터 시스템이나 응용 프로그램)를 작성할 수 있는 수단
서비스 워커를 통해 웹 개발자는 무엇을 로컬 캐시에 저장할지, 언제 네트워크에서 새 데이터를 가져올지 제어할 수 있음
서비스 워커가 캐시에서 페이지를 로드하도록 설정되었다면 네트워크에서 데이터를 가져오도록 요청할 필요가 없음
기억해야 할 중요한 점은 서비스 워커가 렌더러 프로세스에서 실행되는 JavaScript 코드라는 점
그렇다면 내비게이션 요청이 들어왔을 때 브라우저 프로세스는 사이트에 서비스 워커가 있다는 것을 어떻게 알 수 있을까?
서비스 워커가 등록되면 서비스 워커의 범위는 참조(reference)로 유지됨
내비게이션이 발생하면 네트워크 스레드는 도메인을 등록된 서비스 워커의 범위와 비교함
해당 URL에 등록된 서비스 워커가 있으면 UI 스레드는 서비스 워커 코드를 실행하기 위해 렌더러 프로세스를 찾음
서비스 워커를 이용하여 네트워크에 데이터를 요청하지 않고 캐시에서 데이터를 가져오거나 네트워크에 새 리소스를 요청할 수 있음
내비게이션 프리로드
브라우저 프로세스와 렌더러 프로세스 사이를 왕복해야 하는 상황에서 서비스 워커가 결국 네트워크에서 데이터를 요청하기로 하면 지연이 발생하게 됨을 알 수 있음(이리 저리 확인하다 시간을 낭비하는 것)
내비게이션 프리로드는 서비스 워커의 시작과 병렬로 리소스를 로딩해 내비게이션 과정의 속도를 높이는 메커니즘
이 요청은 헤더에 표시되어 서버가 이러한 요청에 대해 다른 콘텐츠를 보낼 수 있게 함
예를 들어 전체 문서를 보내지 않고 업데이트된 데이터만만 보낼 수 있음
헤더에 표시되는것은 처음부터 서비스 워커를 등록할때 내비게이션 프리로드 기능을 사용한다고 명시하면 내비게이션 프리로드가 활성화되어 브라우저가 알아서 내비게이션 요청 헤더를 추가해준다고 함(고마워요 gpt웨건)