밑바닥부터 시작하는 웹 브라우저 - 추상에서 실체로, 브라우저의 밑바닥

최관수·2025년 10월 26일
1

서평

목록 보기
21/22

한빛미디어 서평단 <나는리뷰어다> 활동을 위해서 책을 협찬 받아 작성된 서평입니다.

프론트엔드 개발자인 내게 브라우저는 익숙해져야 하는 존재였다. 웹서비스는 결국 브라우저를 기반으로 제공되는 것과 동시에, 개발하는 시점에도 브라우저 위에서 어떻게 렌더링되고 있는지 지속적으로 확인하면서 작업하기 때문이다. 각각의 브라우저는 개발자 도구를 제공하고 있고, 처음엔 이 개발자 도구를 어떻게 쓰는지도, 각각의 탭이 어떤 역할을 하는지도 몰라서 이리저리 눌러봤던 기억이 난다. 이젠 개발자 도구를 통해 렌더링되는 요소들을 확인하거나 네트워크를 통해 어떻게 핸드쉐이킹을 하는지 확인하며 개발하는 건 일상이 되었다.

그만큼 브라우저는 가까이에 있어 왔고, 이런 이유로 V8 엔진이니, 주소창에 URL을 때려 넣고 난 이후의 순서니, 브라우저의 근간이 되는 기술에 대해 읽어가며 익히기도 했다. 달리 말해 그런 과정이 피상적으로 느껴지기도 했던 건 읽을 때는 이해가 되지만 시간이 지나면 금세 잊곤 했기 때문이다. 이 책은 그 점에서 명확히 다르다. ‘브라우저를 직접 만들어본다’는 경험 자체가 추상적인 개념을 직접 체화할 수 있도록 만든다. 개발자가 되고 싶다면 자기가 만들고 싶은 걸 만들어보라는 말처럼, 이건 단순히 글로 접하는 피상적인 지식이 아니라 그 근간을 파헤쳐 보고 전체 흐름을 몸으로 익혀보라는 의미처럼 느껴졌다.

처음엔 텔넷을 통해 웹서버와 간단한 통신을 시도해 보게 된다. 이를 바탕으로 직접 GET 요청을 보내보고, HTTP 응답 헤더와 바디가 어떤 구조로 전달되는지 확인한다. 브라우저가 하는 첫 단계가 결국 문서를 요청하고 문자열을 받는 일이라는 것을, 아주 원초적이고 투명한 방식으로 확인하게 된다.

(base)  ✘ ~  nc -v google.com 80
Connection to google.com port 80 [tcp/http] succeeded!

GET /index.html HTTP/1.0
Host: google.com

HTTP/1.0 301 Moved Permanently
Location: http://www.google.com/index.html
Content-Type: text/html; charset=UTF-8
Content-Security-Policy-Report-Only: object-src 'none';base-uri 'self';script-src 'nonce-ZZdfYRYG5H89BXUzpPCZnQ' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
Date: Wed, 22 Oct 2025 22:48:45 GMT
Expires: Fri, 21 Nov 2025 22:48:45 GMT
Cache-Control: public, max-age=2592000
Server: gws
Content-Length: 229
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/index.html">here</A>.
</BODY></HTML>

이를 통해 결국 HTTP 프로토콜은 문서를 주고받는 행위라는 걸 다시 한번 인지하게 된다. HTML을 왜 문서라 부르고 작성하는지 다시 한번 느끼게 되는 대목이었다. 이 과정에서 어떤 요청과 응답을 주고받는지 가볍게 인지하고, 몇 가지 파이썬 코드를 통해 어떤 방식으로 구성되는지 설명을 이어 간다.

def show(body):
		in tag = False
		for c in body:
				if c == "<":
						in_tag = True
				elif c == ">":
						in_tag = False
				elif not in_tag:
						print(c, end="")

이 간단한 파이썬 코드가 주는 의미가 남다르게 느껴졌다. 익숙하게 써왔던 태그의 <, >가 단순 문자열이 아니라 파싱의 기준이 되고, 왜 태그의 쌍 중 하나를 잊으면 에러를 뱉는지 와닿는 느낌이었다. 이후에는 DOM 트리가 쌓여가고, 노드를 순회하며 렌더링하는 과정이 더해지는데, 이 흐름은 실제 브라우저가 하는 일과 닮아 있었다.

화면을 그려내는 챕터에 들어서는 특히 Canvas API를 다뤄본 사람이라면 익숙하게 느낄 수 있는데, 축에 대한 정보를 주고 그려내는 방식이 꽤나 닮아 있었다. 파서를 만들어 문서 트리를 구축하고, 이를 통해 노드 트리를 사용하는 과정을 보면서, 웹에디터와 같은 기능을 구현하는 도메인의 회사라면 이 책이 특히 도움이 될 것 같다는 생각이 들었다.

block과 Inline 요소를 특정 조건문을 통해 나누고, 좌표를 통해 레이아웃을 그려내는데, 꽤나 고려해야 할 것이 많았다. 하나만 언급하자면 객체의 세로 위치는 이전 형제 노드가 있는지에 따라 달라질 수 있고, 높이는 당연하게도 모든 자식을 포함할 수 있을 만큼 충분히 커야 한다. 뿐만 아니라 텍스트 줄바꿈을 위한 LineLayout, Inline 요소 배치를 위한 word 단위 측정, 새 줄 생성, background 페인팅까지 이어진다. 여기서부터 브라우저가 단순히 HTML을 읽는 데에 그치는 것이 아니라 레이아웃 엔진을 통해 화면을 구성해 내는 존재라는 것이 온몸으로 느껴졌다.

사용자의 스타일을 적용하는 것도 파싱의 연속이다.

def pair(self):
		prop = self.word()
		self.whitespace()
		self.literal(":")
		self.whitespace()
		val = self.word()
		return prop.casefold(), val
def body(self):
		pairs = {}
		while self.i < len(self.s):
				prop, val = self.pair()
				pairs[prop.casefold()] = val
				self.whitespace()
				self.literal(";")
				self.whitespace()
		return pairs

대략적으로 보면 알 수 있듯이 인라인 스타일을 파싱하는 코드다. property를 파싱하는 함수, 그리고 각각의 property를 구분해 내는 함수로 이해할 수 있다. 이 과정에서 CSS 상속, 캐스케이딩 등의 규칙까지 직접 다뤄보게 된다. 보다 보니 !important가 궁금해졌다. 당연히 한 쌍의 property를 파싱해서 !important라는 문자가 존재하는 경우 우선순위를 높게 잡으면 되지 않을까 싶긴 했는데, 10000만큼 높은 우선순위를 부여하라고 하라는 팁으로 간단히 언급되어 있었다.

자바스크립트 코드를 실행하는 것도 크게 다르진 않다. 예컨대 querySelectorAll 같은 DOM API의 구현을 위해 함수를 정의하여 export 하고, 그 함수를 통해 셀렉터와 매칭되는 모든 노드를 찾아낸다. 여기에 더해 Task Queue, Event Loop, setTimeout, XHR, Same-Origin Policy, postMessage, iframe 통신 등 실제 브라우저 동작에 가까운 이벤트 흐름까지 구현해 보게 된다.

모던 브라우저의 시각적 효과 구현으로 넘어와서는 opacitymix-blend-mode, transform과 같은 시각 효과를 적용하기 위해 어떤 과정이 필요한지 Skia와 SDL을 통해 다루고 있다. 레이어를 나누고 합성하고, 어떤 요소는 GPU에서 처리하여 부드러운 애니메이션을 제공하는지 간단히 살펴보게 된다. WebGL, WebGPU, Canvas 같은 그래픽스 API가 왜 중요한지 자연스럽게 연결되는 지점이었다.

접근성에 대한 부분도 후반부에 언급하고 있는데, 줌 기능을 통해 컨텐츠의 글자를 키웠을 때 줄바꿈이 새롭게 바뀌는 건 평소 당연히 받아들이던 일이었지만, 브라우저 개발 관점에서 보면 결국 텍스트 영역의 크기를 다시 계산해 줘야 한다는, 즉 렌더링을 새롭게 해야 한다는 관점에서 접근해야 한다. 키보드 네비게이션이나 포커스 또한 접근과 처리 형식으로 보자면 크게 다르지 않다. 스크린 리더를 위한 Accessibility Tree 구성, 포커스 이동 규칙, aria 속성 처리 등 평소엔 깊이 생각하지 않던 접근성의 내부 구조를 직접 구현하게 되면서, 접근성은 별도의 기능이 아니라 브라우저의 근본적인 기능임을 다시 느끼게 되었다.

그리고 후반부에서 무효화와 점진적 성능의 원칙 등을 다루는 챕터는 특히 흥미로운 대목이었다. 사용자가 입력할 때마다 전체 레이아웃을 다시 계산하면 느릴 수밖에 없다. 이를 해결하기 위해 레이아웃 트리를 캐시하고, 변경된 부분만 다시 계산하는 보호된 필드 기반의 무효화 전략을 이야기하고 있다. 더티 플래그, 종속성 그래프, 멱등성과 결합된 부분 렌더링은 대규모 웹앱 성능 최적화의 근간이 되는 내용이었다. 책의 서두에 브라우저를 만든다는 건 꽤나 복잡한 일이라고 언급한 부분이 있었는데, 이 대목을 보면서 정말 세밀하게 접근해야 될 일이구나 싶은 생각이 들었다.

이 책은 특히 웹 프론트엔드 개발자에게 추천하고 싶은 책이다. 앞서 말했던 것처럼 글로서 접하는 지식이 아니라 구현을 통해 체득하는 경험을 할 수 있기 때문이다. 모던 웹 개발 환경에서는 React나 Vue 기반에서 작업을 하고, 이미 구현된 훌륭한 라이브러리 위에서 작업을 하는 경우가 대부분일 텐데, 이 책의 제목처럼 ‘그 밑바닥’을 훑어보는 경험을 직접 하기에 적절한 책이라는 생각이 든다. 단순히 기술 지식을 얻는 것을 넘어, 이제는 없어서는 안 될 웹이라는 거대한 플랫폼이 어떤 철학과 구조 위에 세워져 있는지를 느낄 수 있는 경험이었다.

profile
평소엔 책과 영화와 음악을 좋아합니다. 보편적이고 보통사람들을 위한 서비스 개발을 꿈꾸고 있습니다.

0개의 댓글