웹 개발자는 무슨 일을 할까요? 웹 개발자는 인터넷에서 웹사이트나 웹 애플리케이션을 설계하고 개발하며, 이를 개선하는 역할을 맡습니다. 사용자는 이러한 웹사이트나 애플리케이션에 브라우저를 통해 접근하게 됩니다. 브라우저는 사용자와 웹 콘텐츠를 연결하는 다리와 같은 역할을 합니다. 만약 브라우저가 없었다면 사용자는 복잡한 서버 요청을 직접 처리해야 했을지도 모릅니다. 따라서 브라우저는 온라인 콘텐츠를 쉽게 접근하고 상호작용할 수 있도록 돕는 핵심 도구이기에 웹 개발자는 브라우저의 동작 원리와 특성을 깊이 이해하고 이를 효과적으로 활용할 수 있어야 한다고 생각합니다. 이 글에서는 브라우저의 역사, 구성 요소, 동작 원리를 살펴보고 브라우저와 한층 더 가까워지는 시간을 가져보겠습니다.
브라우저는 1990년 Tim Berners-Lee가 만든 최초의 웹 브라우저 WorldWideWeb 에서 시작되었습니다. 이후 1993년의 Mosaic, 1994년의 Netscape, 1995년의 Internet Explorer, 그리고 2008년의 Google Chrome와 2015년 Microsoft Edge 에 이르기까지 발전을 거듭하며 사용자가 웹을 탐색하는 방식을 근본적으로 변화시켰습니다. 브라우저의 발전은 언제나 접근성과 사용자 경험 개선을 목적으로 이루어져 왔습니다. 예를 들어, 과거에는 단순히 텍스트와 링크만 표시하던 브라우저가 이제는 사용자가 직접 동적이고 복잡한 애플리케이션을 실행할 수 있을 정도로 진화했습니다.
사용자는 어떻게 브라우저를 통해 동적이고 복잡한 애플리케이션을 실행할 수 있을까요? 그 내부는 단순히 화면에 정보를 보여주는 것 이상의 다양한 구성 요소들이 협력하며 작동합니다. 브라우저는 사용자가 입력한 명령을 해석하고, 이를 처리하여 화면에 콘텐츠를 표시하며, 사용자가 다시 상호작용할 수 있도록 만드는 복합적인 시스템으로 구성되어 있습니다. 이 과정에서 각 구성 요소는 서로 유기적으로 연결되어 동작합니다. 브라우저를 이루는 주요 요소들을 살펴보겠습니다.
사용자 인터페이스: 사용자가 브라우저와 직접 상호작용하는 공간입니다. 예를 들어, 뒤로 가기, 앞으로 가기 버튼 등 시각적으로 보여지는 것들 중 브라우저를 제어할 수 있는 모든 것에 해당합니다.
브라우저 엔진: 사용자 인터페이스에서 처리한 입력 값을 받아 웹 페이지를 렌더링에 필요한 데이터 통신을 원활하게 하는 역할을 합니다. 이 공간에서는 링크를 클릭하거나 URL을 입력하는 등 사용자 작업이 올바르게 처리되고 브라우저 내에서 적절한 동작이 작동하도록 합니다.
렌더링 엔진: 브라우저 내의 웹의 콘텐츠를 표시하는 역할을 담당합니다. 이 엔진은 HTML 구조를 해석, CSS 스타일을 적용하여 페이지의 레이아웃과 모양을 결정하고 JavaScript 코드를 실행하여 페이지에 상호 작용 및 동적 요소를 추가하는 작업을 실행합니다.
네트워킹: 웹사이트 URL을 IP 주소로 확인하고 서버에 HTTP 요청을 전송합니다. 그 이후 네트워크 연결을 설정하고 렌더링에 필요한 응답 데이터를 처리하는 역할을 담당합니다. 이 요소는 HTML, CSS, 이미지 및 기타 파일과 같은 리소스를 서버에서 가져와 렌더링 엔진에 전달하여 표시하는 작업을 실행합니다.
자바스크립트 인터프리터: 페이지에 있는 JavaScript 코드를 실행하는 브라우저 내부 요소입니다. 이 인터프리터는 JavaScript 코드가 제대로 실행되도록 하여 페이지가 사용자 동작에 응답하고 콘텐츠를 동적으로 업데이트하며 API 및 기타 기술과 상호작용할 수 있게 도와줍니다.
UI 백엔드:
select
나input
태그 등 기본적인 위젯을 그리는 역할을 합니다. 이 UI는 브라우저 플랫폼에서 명시하지 않은 일반적인 인터페이스로 사용자의 운영체제 인터페이스 체계를 사용하여 해당 태그들의 동작을 보여줍니다.데이터 저장소: 쿠키나 로컬 스토리지 등 로컬에 데이터를 저장하는 역할을 합니다.
브라우저는 위에 나열된 구성 요소들이 각각의 역할을 수행하며 조화를 이루어 작동합니다. 이 모든 요소들이 협력하여 사용자의 입력을 처리하고, 필요한 데이터를 가져오고, 화면에 표시하며, 다시 사용자와의 상호작용을 가능하게 만듭니다. 브라우저의 이러한 구성 요소들의 역할을 이해하고 본격적으로 동작 원리에 대해 살펴보겠습니다.
브라우저의 기본적인 동작 원리는 한 문장으로 요약할 수 있습니다. “브라우저는 사용자가 입력한 URL을 기반으로 서버와 통신하여 데이터를 가져오고, 이를 파싱 및 렌더링하여 화면에 출력합니다.” 하지만 이 과정을 깊이 이해하려면 추가적인 설명이 필요합니다. 이를 돕기 위해 브라우저의 동작을 크게 탐색, 응답, 파싱, 렌더링의 단계로 나누어 살펴보겠습니다.
사용자가 브라우저에 URL을 입력하고 서버에 리소스를 요청하는 단계입니다.
사용자가 브라우저에 URL을 입력하면, 브라우저는 먼저 DNS 조회를 요청합니다. DNS는 입력된 URL에 해당하는 IP 주소를 제공하며, 요청된 결과를 캐싱합니다. 이렇게 캐싱된 IP 주소는 동일한 요청이 들어올 때 재사용되어 요청 속도를 높이는 데 기여합니다.
브라우저가 IP 주소를 받으면, 서버와 TCP 핸드셰이크를 통해 연결을 설정합니다. TCP 핸드셰이크는 신뢰할 수 있는 데이터 전송을 보장하기 위해 사용되며, 네트워크 연결을 설정하는 과정에서 양측이 데이터를 주고받을 준비가 되었음을 확인하고 데이터 통신을 시작할 준비를 완료합니다.
(만약 URL이 HTTPS 프로토콜을 사용하는 경우) TLS/SSL 핸드셰이크가 추가로 진행됩니다. TLS/SSL 핸드셰이크는 브라우저와 서버 간의 암호화된 연결을 설정합니다. 그리고 양측이 서로의 인증서를 검증하고 암호화 키를 교환한 뒤, 안전한 데이터 통신을 시작할 준비를 완료합니다.
이 과정을 통해 서버와 안정적으로 연결된 브라우저는 이제 사용자가 요청한 데이터를 받을 준비를 완료합니다. 다음 단계에서는 브라우저가 서버로부터 데이터를 어떻게 받고 처리하는지 살펴보겠습니다.
브라우저가 서버에 요청한 리소스에 대한 응답을 받는 단계입니다. 서버는 브라우저에 응답으로 보낼 때, 데이터를 나누어 전송하게 됩니다.
서버가 데이터를 분할하여 전송하는 이유는 아래와 같습니다.
- 전송하고자 하는 리소스의 크기가 클 경우, 한 번에 전송하기 어렵기 때문입니다.
- 전송 중 데이터 손실이 발생하면, 전체 데이터를 다시 보내는 대신 손실된 세그먼트만 재전송하여 효율성을 높일 수 있습니다.
- 네트워크 대역폭을 효율적으로 사용하기 위해, 작은 세그먼트 단위로 전송하여 혼잡을 줄일 수 있습니다.
서버로부터 데이터를 수신한 브라우저는 이를 처리하고 이해할 수 있는 구조로 변환해야 합니다. 브라우저의 이해를 돕기 위해, 다음 단계에서 받은 데이터를 어떻게 변환하는지 한번 살펴보겠습니다.
파싱은 브라우저가 읽을 수 있는 구조로 변환하는 작업을 의미합니다. 브라우저는 데이터를 수신하는 즉시 파싱을 시작하며, 이 과정에서 HTML과 CSS를 해석하여 웹 페이지의 구조와 스타일을 정의합니다. 아래 시각 자료와 함께 HTML, CSS를 어떻게 해석하고 정의하는지 살펴보겠습니다.
HTML 파싱
- 브라우저는 HTML 바이트 데이터를 지정된 인코딩 방식에 따라 문자열로 변환합니다.
- 변환된 문자열을 HTML 표준에 따라 토큰으로 변환합니다.
- 변환된 토큰을 HTML 노드로 만들어 노드의 속성에 따라 각 노드를 연결합니다.
- 연결된 노드를 기반으로 Tree 자료구조 모양의 DOM(Document Object Model) 트리를 생성합니다.
이처럼 HTML을 파싱하는 궁극적인 목적인 DOM 트리 생성입니다.
만약 HTML을 파싱하던 중 <script>
태그를 만나면 어떻게 될까요? 브라우저는 스크립트를 받아와 실행할 때까지 HTML 파싱을 멈춥니다. 이로 인해 DOM 트리가 완전히 생성되지 않은 상태에서 스크립트가 실행될 위험이 있습니다. 따라서 많은 <script>
태그가 <body>
아래에 배치되는 이유를 이해할 수 있습니다.
CSS 파싱
HTML 파싱 도중 CSS 링크를 만나면 브라우저는 CSS 파일을 요청하고 이를 기반으로 CSSOM(CSS Object Model) 을 생성합니다.
CSSOM은 DOM과 결합되어 렌더링을 위한 렌더 트리(Render Tree) 를 구성하는 데 사용됩니다. DOM만으로는 웹 페이지의 모양을 결정할 수 없으므로, 브라우저는 CSS를 가능한 빨리 파싱하여 웹 페이지를 신속하게 렌더링할 준비를 합니다. 이로 인해 우리는 <link>
및 <style>
태그가 주로 <head>
태그 안에 위치하는 이유를 확인할 수 있습니다.
브라우저가 HTML과 CSS를 모두 파싱하여 준비를 완료하면, 다음에는 화면에 실제로 표시하는 작업을 진행합니다.
브라우저가 데이터를 사용자 화면에 출력하는 단계입니다. 렌더링 과정은 DOM, CSSOM, 그리고 레이아웃 계산을 통해 웹 페이지를 아래와 같이 시각적으로 완성하는 일련의 작업으로 구성됩니다.
렌더 트리 생성
- HTML과 CSS를 기반으로 화면에 표시될 요소들만 포함하는 렌더 트리를 생성합니다.
- DOM 트리의 보이지 않는 요소(
display: none;
)는 렌더 트리에서 제외됩니다. - 렌더 트리의 각 노드에 CSSOM 규칙을 적용하여 스타일을 정의합니다.
렌더 트리 생성이 끝나면 웹페이지의 레이아웃 계산 작업을 진행합니다.
플로우
화면에 보이는 노드들로 렌더 트리가 만들어지면 각 노드의 위치와 크기, 너비, 높이를 계산합니다. 이 과정에서 상대적인 단위(rem, em, vw, vh 등)를 절대적인 단위(px)로 변환하여 요소들의 크기와 위치를 계산하고, 현재 viewport에 맞게 노드들을 배치합니다.
페인트
브라우저는 렌더 트리를 기반으로 화면에 요소를 그리는 작업을 수행합니다. 요소는 레이어별로 나뉘어 관리되며, 레이어를 나누는 이유는 UI 성능을 최적화하기 위해서입니다. 페이지의 일부 요소가 변경될 경우, 전체 페이지를 다시 그리는 대신 해당 레이어만 업데이트하여 효율성을 높입니다.
이 과정이 완료되면 사용자 화면에 완전한 웹 페이지가 표시됩니다. 렌더링 단계는 브라우저 동작의 마지막 단계이며, 브라우저는 이후에도 사용자 입력에 따라 동일한 과정을 반복적으로 수행합니다.
브라우저의 역사, 구성 요소, 그리고 동작 원리에 대해 살펴보았습니다. 이를 통해 브라우저의 내부 구조와 동작을 이해하면, 최적화할 수 있는 다양한 방법을 생각해볼 수 있습니다.
예를 들어, CDN(Content Delivery Network) 을 활용하면 네트워크 응답 속도를 개선할 수 있습니다. CDN은 사용자와 가까운 위치에 있는 서버에서 리소스를 제공하므로 요청 시간을 줄이고, 대역폭을 효율적으로 사용해 브라우저의 응답 과정을 최적화할 수 있는 도구입니다. 또한, 다시 레이아웃을 계산하거나 콘텐츠를 그리는 작업인 리플로우와 리페인트 최적화를 위해 CSS 애니메이션을 GPU 가속이 가능한 속성(transform
, opacity
)을 사용하거나, DOM 변경을 최소화하도록 작업할 수 있습니다.
이처럼 브라우저의 작동 원리를 이해하는 것은 성능 최적화뿐만 아니라, 사용자 경험을 개선하기 위해 우리가 더 나은 결정을 내릴 수 있도록 도울 수 있습니다. 클릭 한 번에 많은 동작 원리가 숨겨져 있는 만큼, 웹 개발자로서 사용자 경험을 향상시키기 위한 다양한 고민을 시도해보는 건 어떨까요?
학습 중인 내용을 정리하는 초보 개발자입니다. 잘못된 내용이 있다면 피드백 부탁드립니다.