포스트

2023-11-30-TIL

2023-11-30-TIL

Today I Studied

The Architecture of Web Browsers

Chrome 개발자 블로그의 Inside look at modern web browser 시리즈를 중심으로 브라우저 구조를 읽었다. 1부에 따르면 Chrome은 여러 프로세스로 나뉜다.

  • 브라우저 프로세스: 주소창, 북마크, 네트워크 요청, 파일 접근 등 애플리케이션 전체를 조율한다.
  • 렌더러 프로세스: 탭 안에서 웹 페이지를 그리는 모든 일을 맡는다. 탭(최근에는 사이트)마다 따로 만든다.
  • GPU 프로세스: 다른 프로세스들의 그리기 요청을 받아 GPU로 처리한다.

프로세스를 나누는 이유는 안정성과 보안이다. 한 탭이 응답하지 않아도 그 탭만 닫으면 되고, 운영체제의 권한 제한 기능으로 렌더러 프로세스가 임의의 파일에 접근하지 못하게 샌드박스에 가둘 수 있다. 블로그는 이를 더 밀고 나가 사이트마다 렌더러 프로세스를 나누는 Site Isolation도 소개한다. 대신 프로세스마다 메모리를 따로 쓰므로 메모리 사용량이 늘어난다.

  • https://blog.quastor.org/p/architecture-web-browsers
  • https://seng130.wordpress.com/lectures-2/browser-architecture/
  • https://www.geeksforgeeks.org/browser-architecture/
  • https://blog.quastor.org/p/architecture-web-browsers
  • https://developer.chrome.com/blog/inside-browser-part1/
  • https://developer.chrome.com/blog/inside-browser-part2/
  • https://developer.chrome.com/blog/inside-browser-part3/
  • https://developer.chrome.com/blog/inside-browser-part4/

Web Browser Processing

렌더러 프로세스 안에서 페이지가 화면에 그려지는 과정은 같은 시리즈 3부에 나온다. 렌더러 프로세스에는 메인 스레드, 워커 스레드, 컴포지터 스레드, 래스터 스레드가 있고, 대부분의 작업은 메인 스레드가 한다.

flowchart LR
    A["HTML 파싱<br/>DOM 생성"] --> B["CSS 파싱<br/>스타일 계산"]
    B --> C["레이아웃<br/>(위치와 크기)"]
    C --> D["페인트<br/>(그리는 순서)"]
    D --> E["컴포지팅<br/>(레이어 합성)"]

HTML 파서가 <script> 태그를 만나면 파싱을 멈추고 자바스크립트를 내려받아 실행한다. 스크립트가 document.write() 등으로 문서 구조를 바꿀 수 있기 때문이다. 그래서 async나 defer 속성으로 이 멈춤을 피한다. 컴포지팅은 메인 스레드가 아닌 컴포지터 스레드에서 돌기 때문에, 메인 스레드가 자바스크립트로 바빠도 스크롤 같은 동작은 부드럽게 유지될 수 있다.

  • https://www.chromium.org/developers/design-documents/multi-process-architecture/
  • https://www.chromium.org/developers/design-documents/displaying-a-web-page-in-chrome/
  • https://ovindunambukara.medium.com/how-web-browsers-use-process-threads-4cec53e24789
  • https://web.dev/articles/howbrowserswork?hl=ko
  • https://cabulous.medium.com/how-does-browser-work-in-2019-part-ii-navigation-342b27e56d7b
  • https://sunandakarunajeewa.medium.com/how-web-browsers-use-processes-and-threads-5ddbea938b1c

JavaScript Engines

자바스크립트 엔진은 자바스크립트 코드를 해석하고 실행하는 프로그램이다. V8 문서는 V8을 C++로 작성한 Google의 오픈소스 고성능 JavaScript·WebAssembly 엔진으로 소개하고, Chrome과 Node.js 등에서 쓰인다고 설명한다. 엔진은 렌더러 프로세스의 메인 스레드에서 돌기 때문에, 오래 걸리는 자바스크립트는 위에서 본 파싱과 렌더링을 그만큼 막는다.

  • https://v8.dev/

ECMAScript

ECMA-262는 자바스크립트 언어의 표준 명세인 ECMAScript다. TC39 위원회가 관리하고, V8 같은 엔진은 이 명세를 구현한다. DOM이나 fetch 같은 브라우저 API는 ECMAScript가 아니라 WHATWG 등의 별도 웹 표준에 속한다. 언어 문법과 내장 객체는 ECMAScript, 브라우저가 제공하는 기능은 웹 API로 나눠 생각하면 된다.

  • https://tc39.es/ecma262/

WebAssembly

WebAssembly 명세는 WebAssembly를 효율적인 실행과 작은 크기를 위해 설계된 안전하고 이식 가능한 저수준 코드 형식으로 정의한다. 주된 목표는 웹에서 고성능 애플리케이션을 돌리는 것이지만 웹에만 묶이지는 않는다. C, C++, Rust 같은 언어를 Wasm으로 컴파일하면 브라우저의 자바스크립트 엔진 안에서 네이티브에 가까운 속도로 실행할 수 있다. W3C 커뮤니티 그룹이 만드는 개방형 표준이다.

  • https://webassembly.github.io/spec/core/

JPA to Native Query Parser

  • JPA를 네이티브 쿼리로
  • JPA는 bulk insert가 안됨

saveAll()로 엔티티를 많이 저장해도 기본 설정에서는 INSERT가 한 건씩 나간다. JDBC 배치를 쓰려면 hibernate.jdbc.batch_size를 설정해야 하고, ID 생성 전략도 중요하다. Hibernate 사용자 가이드는 IDENTITY 생성 전략을 쓰면 Hibernate가 INSERT 배치를 할 수 없다고 명시한다. MySQL의 AUTO_INCREMENT가 이 경우라서, 대량 삽입이 필요하면 JDBC 템플릿의 batchUpdate나 네이티브 쿼리의 다중 VALUES로 직접 처리하는 경우가 많다.

  • null == 0 ; false
  • null >= 0 ; true

자바스크립트의 비교 연산 규칙이다. MDN의 동등 연산자 문서에 따르면 ==에서 한쪽이 null이나 undefined이면 다른 쪽도 null이나 undefined일 때만 true다. 그래서 null == 0은 false다. 반면 >= 문서에 따르면 관계 비교는 피연산자를 숫자로 바꾸고, null은 숫자 0이 되므로 null >= 0은 true다. 같은 값이 연산자에 따라 다르게 변환되므로, 백엔드에서 null일 수 있는 숫자 필드를 프런트엔드가 비교할 때 주의해야 한다.

  • json -> 언어에 따라 long or string 전송방식이 달라짐

JSON 명세 자체는 숫자 크기를 제한하지 않지만, 자바스크립트의 Number는 배정밀도 부동소수점이라 정수를 정확히 표현할 수 있는 범위가 정해져 있다. MDN에 따르면 Number.MAX_SAFE_INTEGER는 9007199254740991이고, 여기에 1을 더한 값과 2를 더한 값이 같다고 판정된다. Java의 long ID가 이 값을 넘으면 브라우저가 JSON을 파싱하는 순간 값이 바뀐다. 그래서 큰 long 값은 JSON에서 문자열로 내려주는 경우가 많다.

이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다