주소창에 naver.com을 치면 일어나는 일을 쉽게 이해해보자

SangYoonLee (이상윤)·2022년 9월 13일
386

CS

목록 보기
1/1
post-thumbnail

🖌 시작하며..

스터디에서 발표를 준비하며, 기술 면접의 단골 질문인 웹 브라우저의 동작 원리에 대해 처음으로 공부하게 되었다. 그리고 이 주제가 왜 단골 질문으로 꼽히는지 이해할 수 있었다.

이 주제는 깊이 들어가기 시작하면 정말 끝없이 들어갈 수 있는 내용인 것 같아 보였다.
그렇기에 면접관이 면접자의 역량을 파악하는 데 이만한 질문이 없겠구나 싶은 생각이 들었다.
만일 CS 지식이 부족하다면, 이 주제를 처음 공부할 때 아마 내용이 많이 낯설고 어렵지 않을까 싶다. (사실 내 이야기다.)

그래서 이번 기회에 '주소창에 naver.com을 치면 일어나는 일련의 과정'을 CS 지식이 나처럼 부족한 분들도 이해할 수 있도록 최대한 쉽게 설명하여 정리해보고자 한다.


📖 사전 지식

웹 브라우저의 동작 원리를 설명하기에 앞서, 미리 알고 있어야 하는 일부 지식들을 우선 정리한다.

인터넷 브라우저와 구조

먼저 인터넷 브라우저가 무엇인지 살펴보고, 그 구조에 대해 알아보자.

인터넷 브라우저란 웹 서버와 통신하여 인터넷 사이트 및 다양한 컨텐츠를 볼 수 있도록 지원해주는 소프트웨어 프로그램이다.

인터넷 브라우저는 현재 가장 많이 사용되는 Chrome을 포함하여 Safari, Firefox, 오페라 등 여러 종류가 있다. 각 브라우저마다 전반적인 구조는 조금씩 다르지만 큰 틀은 아래의 그림으로 동일하다.


인터넷 브라우저의 구조 (👉사진 출처)


  • 사용자 인터페이스 (User Interface / UI)

    • 페이지를 보여주는 창을 제외한 나머지 모든 영역을 말한다.
    • 주소 표시줄, 이전, 이후 버튼, 홈버튼, 북마크 버튼 등
  • 브라우저 엔진 / 레이아웃 엔진

    • 사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어한다.
    • (UI를 그리는 UI 스레드, 네트워크 통신을 위한 네트워크 스레드, 파일에 접근하기 위한 스토리지 스레드 등이 존재한다.)
  • 렌더링 엔진

    • 브라우저 엔진과 밀접히 관련된 엔진으로, 웹 페이지가 표시되는 모든 영역을 제어한다.
    • 요청한 콘텐츠(HTML, CSS 등)를 파싱하고, 화면에 나타내는 일을 수행한다.
  • 자료 저장소 / 데이터 저장소

    • 말 그대로 자료 저장소다.
    • 쿠키나, localStorage, IndexedDB 같이 로컬에 저장되어 좀 더 오래 유지되어야 하는 데이터들을 보관할 수 있도록 지원하는 영역이다.
  • 통신 (Networking)

    • HTTP/HTTPS 네트워크 처리를 한다.
    • 플랫폼과 독립적인 인터페이스로, 각 플랫폼의 하부에서 실행된다.
  • 자바스크립트 해석기 (JS Interpreter) / 자바스크립트 엔진

    • 스크립트(JS 코드)를 파싱(해석)할 때 사용하는 JS 엔진이다.
    • HTML을 파싱(해석) 중 script 태그를 만나면, JS 엔진이 제어 권한을 넘겨받는다.
      이 작업은 동기적으로 진행된다.

      '동기적으로 진행된다'의 의미 :
      JS 엔진이 작업을 마칠 때까지 HTML에서 진행 중인 과정은 잠시 중지된다.

  • UI 백엔드 (UI Backend)

    • 기본 위젯을 그릴 때 이용한다.
    • OS의 방법을 사용한다.

모던 브라우저는 각각의 탭을 독립적으로 처리한다. 덕분에 높은 보안성과 더 좋은 사용성을 제공할 수 있다.


DOM 트리

그 다음으로 DOM 트리에 대해 알아보자.

DOM 트리란 '문서 객체 모델 (Document Object Model)' 의 줄임말로, 쉽게 말해 객체로 표현된 HTML 문서이다.

브라우저는 HTML 텍스트 문서를 바로 읽을 수 없기 때문에, 이를 객체의 형태로 바꾸어 브라우저가 읽을 수 있는 트리 구조로 변환해주어야 한다. 이것이 바로 DOM 트리이다.

DOM 트리 내 하나의 객체는 노드라 부르며, DOM 트리는 총 4가지 노드로 구성되어 있다.

  • 문서 노드 (Document Node) : 트리의 최상위 객체. DOM 트리에 접근하기 위한 시작점.
  • 요소 노드(Element Node) : HTML 요소 (태그) 를 객체로 표현한 것
  • 어트리뷰트 노드(Attribute Node) : HTML 요소의 'Attribute'를 객체로 표현한 것
  • 텍스트 노드(Text Node) : HTML 요소의 '텍스트'를 객체로 표현한 것

DOM 트리 구조 (👉사진 출처)

외부에서 특정 노드에 접근하기 위해 DOM을 확인해야 할 때
DOM 트리는 최상위 노드인 '문서 노드' 부터 아래 방향으로 순차적으로 탐색된다.
자바스크립트를 통해 HTML 문서에 접근할 때, 혹은 페이지를 조작하는 이벤트가 발생할 때 이 과정이 일어난다.

그리고 DOM 트리 생성과정에서 브라우저는 HTML 문서에 있는 에러들을 자동으로 처리해준다.



💻 주소 창에 naver.com를 치면 일어나는 일

이제 주소 창에 naver.com를 치고 키보드의 Enter을 눌렀을 때 무슨 일이 일어나는지 본격적으로 살펴보자.

이 과정은 아래 그림처럼 총 3단계로 구분할 수 있다.


📌 Step 1. 주소창에 입력한 텍스트 정보 확인

대부분의 인터넷 브라우저는 자사의 주소창을 검색창과 동일하게 사용하고 있다.
대표적으로 가장 많이 사용되고 있는 웹 브라우저인 Chrome은 주소창을 구글의 검색창으로도 쓰고 있다.

이럴 경우 브라우저는 사용자가 주소창에 어떤 텍스트를 입력했을 때,
이 텍스트가 검색어인지 URL인지 우선적으로 확인한다.
(이 작업의 주체는 브라우저 엔진의 UI 스레드이다.)

  • 만일 입력한 텍스트가 검색어이면,
    브라우저는 검색 엔진의 URL에 검색어를 포함한 주소로 페이지를 이동시킨다.

  • 만일 입력한 텍스트가 URL이면,
    브라우저 엔진에서 (네트워크 스레드를 통해) 네트워크 호출을 수행한다.



따라서 주소창에 naver.com을 입력한 경우 브라우저는 네트워크 호출을 수행하게 된다.


📌 Step 2. 네트워크 호출

먼저 브라우저가 네트워크 호출을 왜 수행해야 하는지부터 간단하게 짚어보자.

브라우저가 사용자에게 '네이버'라는 사이트를 화면에 보여주려면
네이버의 HTML 문서, CSS 문서, 스크립트, 이미지 등의 데이터를 미리 가지고 있어야 한다.
하지만 현재 브라우저엔 이러한 정보가 없다.
이러한 데이터들은 네이버 서버 컴퓨터에 존재한다.

그렇기 때문에 브라우저는 네이버 서버와의 네트워크 통신을 통해 이러한 데이터들을 가져와야 한다.

이 작업을 수행하기 위해선, 브라우저는 우선 네이버 서버가 있는 컴퓨터의 IP 주소부터 파악할 필요가 있다.
마치 친구의 집에 혼자 찾아가려면 그 집의 주소를 알고 있어야 하는 것처럼 말이다.

따라서 '네트워크 호출' 과정은 크게 아래의 두 가지 과정으로 나누어 볼 수 있다.

  1. 네이버 서버의 주소를 알기 위해 네임 서버(Name Server)와 통신하기
  2. 알아낸 주소를 바탕으로 네이버 서버와 통신하여 필요한 데이터 응답받기

1. 네이버 서버의 주소를 찾는 과정

네이버 서버의 주소를 알아내기 위해, 클라이언트(사용자의 컴퓨터)는 다음의 과정을 수행한다.

  1. 자신의 host 파일에서 도메인 네임(naver.com)에 대응하는 IP 주소(125.209.222.142)가 있는지 우선적으로 확인한다.
  2. 만일 없다면, 네임 서버(Name Server)에 '네이버의 IP 주소를 알려주세요' 라는 요청을 보낸다.

여기서 도메인 네임이란 URL www.naver.com에서 naver.com 에 해당하는 부분이다.

인터넷은 컴퓨터의 주소인 IP 주소를 기반으로 동작한다.
하지만 우리가 인터넷을 사용할 땐, IP 주소 대신 사용하기 쉽도록 문자로 이루어진 도메인 네임을 사용한다.

따라서 도메인 네임을 IP 주소로 변환해 주는 환경인 DNS (Domain Name Server)가 반드시 필요한데, 이 DNS를 운영하는 장치를 네임 서버 (Name Server) 혹은 DNS 서버라 한다.

즉, 네임 서버는 도메인 주소에 대응하는 IP 주소를 찾아주는 역할을 수행하는 것이다.

클라이언트는 일반적으로 네임 서버의 IP 주소를 이미 가지고 있다.
따라서 클라이언트는 네임 서버와 통신이 가능하고, naver.com에 해당하는 IP 주소를 요청 및 응답 받을 수 있다.


2. 네이버 서버와 통신하여 필요한 데이터를 받는 과정

이제 클라이언트는 네이버 서버의 IP 주소를 알게 되어 네이버 서버와 통신할 수 있게 되었다.
클라이언트의 브라우저는 (TCP 소켓을 열고 이를 통해) 네이버 서버에 데이터를 요청하는 HTTP Request를 보낸다.

HTTP Request를 받은 네이버 서버는 클라이언트가 요청한 문서를 찾아 읽고
이를 바이트 형태 (1과 0으로 이루어짐) 로 변환한 후, 클라이언트로 HTTP Reply (HTTP Response)를 보낸다.



📌 Step 3. 렌더링 작업

브라우저 엔진(의 네트워크 스레드)은 네이버 서버로부터 응답받은 데이터에 악성 바이러스가 있는지 우선 검사한다.

이 데이터는 바이트 형태의 텍스트 문서이므로, 브라우저 엔진이 읽을 수 없다.
따라서 브라우저 엔진(의 UI 스레드)은 렌더링 엔진에게 해당 데이터를 해석하고, 웹 페이지를 화면에 띄울 것을 요청한다.

요청을 받은 렌더링 엔진은 받은 데이터를 바탕으로 렌더링 프로세스를 수행하고, 이 과정이 끝나면 브라우저 엔진에게 작업 완료를 알린다.

그리고 화면에 네이버 페이지가 드디어 보여지게 된다.



📃 렌더링 프로세스

렌더링 엔진은 브라우저 엔진으로부터 요청받은 내용을 화면에 표시해주는 역할을 한다.
렌더링 프로세스에 대해 좀 더 자세히 알아보자.



렌더링 과정은 아래 4가지 과정으로 이루어진다.

  1. HTML을 파싱하여 DOM 트리 구축, CSS를 파싱하여 CSSOM 트리 구축 (+ JS 파싱)
  2. DOM 트리와 CSSOM 트리를 통해 랜더 트리 구축 (Attachment / 형상 구축)
  3. 랜더 트리 배치 (Layout / Reflow)
  4. 랜더 트리 그리기 (Paint)

렌더링 과정에서 쓰이는 명칭은 인터넷 브라우저의 종류마다 조금씩 다르다. 그러나 렌더링 엔진의 기본적인 동작 과정은 브라우저의 종류와 상관없이 동일하다.


  • 웹 브라우저 별 사용하는 렌더링 엔진

    • 파이어폭스는 모질라에서 직접 만든 게코(Gecko) 엔진을 사용하고, 사파리는 웹킷(Webkit) 엔진을 사용한다.

    • 크롬은 웹킷(Webkit) 엔진을 사용했다가, 웹킷을 Fork하여 자체적으로 구현한 블링크(Blink) 엔진을 현재 사용하고 있다.


    웹킷(+Blink)의 렌더링 프로세스 과정


    게코의 렌더링 프로세스 과정

    (👉사진 출처)


파싱 (Parsing) 이란?

렌더링 과정을 하나씩 설명하기에 앞서, 파싱의 개념부터 우선 이해하는 것이 중요하다.

앞서 말했다시피, 브라우저는 HTML, CSS 등 단순한 텍스트 문서를 이해하지 못한다.
따라서 이 문서를 브라우저가 이해할 수 있는 구조로 변환해주는 과정이 반드시 필요하다.
이를 파싱 (Parsing) 이라 한다.

파싱은 '어휘 분석'과 '구문 분석' 두 가지 과정으로 구분할 수 있다.

  1. 어휘 분석 (By 어휘 분석기) : 문자열을 의미 있는 작은 단위인 토큰(token)으로 분해하는 과정

  2. 구문 분석 (By 파서) : 문자열의 문법에 따라 토큰 간의 위계관계를 분석하여 parsing 트리를 생성하는 과정

    파싱 결과 생성되는 트리 형태를 parse 트리, parsing 트리, concrete syntax 트리 등 다양한 용어로 부른다.

    parse 트리는 토큰화 된 문자열의 단순한 트리에 불과하므로, 이를 바로 사용할 순 없다.
    따라서 브라우저는 이 parse 트리를 DOM 트리로 바꾸어 사용한다.


📌 Step 1-1. HTML 파싱 → DOM 트리 생성

렌더링 엔진이 HTML 문서를 수신받으면, HTML 파서는 이를 위에서부터 읽어 내려가며 파싱을 진행하고, 그 결과물로 DOM 트리를 생성한다.

  • HTML 파싱 과정

    1. 서버에서 바이트 형태의 HTML 문서를 응답받는다.

    2. 지정된 인코딩 방식(UTF-8)에 따라 이를 문자열로 변환한다.

      <meta charset="UTF-8">
    3. 변환된 문자열을 토큰으로 분해한다.

    4. 토큰을 내용에 따라 객체(노드)로 변환한다.

    5. 객체를 트리 구조로 구성하여 DOM을 생성한다.


사용자의 만족도를 높이기 위해 렌더링 엔진은 HTML 문서가 모두 파싱될 때까지 기다리지 않고 파싱 이후의 과정인 배치와 그리기를 미리 진행한다.


📌 Step 1-2. CSS 파싱 → CSSOM 트리 생성

HTML 파싱 중 CSS 문서를 가져오는 link 태그를 만나면, DOM 생성이 잠시 중단되고 해당 CSS의 파싱 과정이 시작된다.

CSS 파서는 서버에서 수신받은 CSS 문서를 파싱하여 CSSOM 트리를 생성한다.
참고로 CSS 파싱 과정은 기본적으로 HTML 파싱 과정과 동일하다.

  • CSS 파싱 과정

    1. 서버에서 바이트 형태의 CSS 문서를 응답받는다.

    2. 지정된 인코딩 방식(UTF-8)에 따라 이를 문자열로 변환한다.

      <meta charset="UTF-8">
    3. 변환된 문자열을 토큰으로 분해한다.

    4. 토큰을 내용에 따라 객체(노드)로 변환한다.

    5. 객체를 트리 구조로 구성하여 CSSOM을 생성한다.


CSSOM 트리의 노드는 DOM 트리 요소의 선택자에 맞춰 적용될 CSS 스타일 정보가 포함되어 있다.


📌 Step 1-3. JavaScript 파싱

HTML 파싱 과정에서 script 태그를 만나면, 렌더링 엔진은 DOM 생성을 잠시 중지하고 서버에서 해당 JavaScript 리소스를 브라우저 엔진으로부터 받아온다. 그리고 JavaScript 엔진에게 제어권을 넘겨준다.

JavaScript 엔진은 받아온 JS 리소스를 파싱하여 AST (추상 구문 트리) 를 생성하고
이를 바이트코드로 변환해 실행한다.

JavaScript 파싱이 종료되면 렌더링 엔진은 다시 제어권을 돌려받고 DOM 생성을 이어나간다.


만일 script 태그를 body 태그의 중간에 작성할 경우, HTML 파싱이 끝나지 않은 상태에서 JavaScript로 인해 DOM이 조작되어 에러가 발생할 위험이 생긴다.
따라서 script 태그는 반드시 body 태그 내부의 최하단에 위치해야 한다.

(혹은 script 태그에 defer 속성을 부여하는 방법도 있다.)


📌 Step 2. Render Tree 생성

HTML과 CSS의 파싱 과정이 모두 끝나면, 각각의 결과물인 DOM 트리와 CSSSOM 트리를 서로 결합하여 랜더 트리 (Render Tree)를 생성한다.

DOM 트리 + CSSSOM 트리 = 랜더 트리 (👉사진 출처)

  • 랜더 트리 생성 과정

    1. html 태그와 body 태그를 처리하며 랜더 트리 루트를 구성한다.

    2. DOM의 최상위 노드부터 순회하면서 화면에 보여지지 않는 노드를 랜더 트리의 구성에서 제외한다.

    3. 화면에 보여지는 나머지 노드에 CSSOM 규칙을 찾아 일치하는 스타일을 적용한다.
      (position이나 float를 사용했을 경우, 실제 그려지는 위치로 랜더 객체가 이동한다.)



📌 Step 3. 레이아웃 (Layout)

랜더 트리 생성이 끝나면, 전체적인 웹 페이지 화면 안에서 렌더 트리 내 각 노드의 위치, 크기 (너비와 높이) 를 계산하고, 이를 화면에 배치하는 레이아웃 (Layout) 과정이 진행된다.

즉, 레이아웃은 렌더 트리의 노드들을 화면에 배치하는 과정이다.

이때 노드의 위치는 (x, y) 좌표계를 사용하는데, 랜더 트리의 루트부터 아래로 내려가면서 계산을 진행한다.

참고로 최상위 노드의 위치는 (0, 0)이며, CSS에서 상대적인 모든 값들은 절대적인 값인 px 단위로 변환된다.


레이아웃은 전체의 배치과정이 필요한 경우인 글로벌 레이아웃, 일부의 배치과정만 변경하면 되는 경우인 로컬 레이아웃으로 구분할 수 있다.

글로벌 레이아웃은 맨 처음에 레이아웃이 발생할 때, 그리고 초기 배치 이후 font와 같은 전역 스타일이 변경되거나 창이 리사이즈 될 때 발생한다. 초기 배치 이후 레이아웃 (Layout) 작업이 다시 일어나는 것을 리플로우 (Reflow)라 한다.

반대로 로컬 레이아웃은 초기 배치 이후 일부 DOM 노드에 변경이 생기는 것처럼, 특정 부분만 재배치가 필요할 때 발생한다. 이는 일부 변경만으로 전체 배치과정이 다시 일어나 불필요한 낭비가 발생하는 상황을 막아준다. 즉 로컬 레이아웃이 일어나는 경우는 모두 리플로우가 발생할 때이다.

(로컬 레이아웃은 더티 비트 (Dirty Bit) 방식이라는 것을 통해 진행된다.)


📌 Step 4. 페인트 (Paint)

마지막으로, 레이아웃 과정에서 계산된 정보들을 바탕으로 각 노드를 화면에 그려주는 페인트 (Paint) 과정이 진행된다. 페인트는 렌더 트리의 각 노드를 화면의 실제 픽셀로 변환해주는 작업이다. 픽셀로 변환하는 이 과정을 래스터화 (Rasterizing)라 부른다.

페인트 과정 중 화면의 특정 위치에 여러 개의 노드가 함께 그려지는 경우, 여러 레이어를 만들고 이를 다시 합성하는 방식으로 작업이 이루어진다.

페인트 과정이 끝나면 브라우저 화면에 네이버 페이지가 보여진다.



브라우저에 특정 변경이 생긴다면 이를 화면에 다시 그려주어야 하는데, 이렇게 페인트 과정이 다시 일어나는 것을 리페인트 (Repaint)라 한다. 기본적으로 리플로우가 발생하면 리페인트도 함께 발생한다. 또한 화면의 구조가 변하지 않더라도 요소의 색깔이 변한다면 리페인트가 일어난다.

브라우저는 변경 사항이 발생했을 때 최소한의 작업만으로 동작하려 한다는 걸 이해하는 것이 중요하다.



🖌 마치며..

이 주제가 어디까지 깊이 들어갈 수 있는지 직접 확인해보며 CS 지식의 중요성을 절감할 수 있었다. 아직 CS를 제대로 공부해보지 않아 깊은 내용을 짧은 시간 내에 모두 학습하는데엔 어려움이 있었지만, 그렇기에 나중에 이 주제에 대해 다시 한 번 공부해보면서 이번에 미처 다 이해하지 못했던 내용들을 그땐 꼭 깨달을 수 있었으면 좋겠다.

이제 막 혼자서 공부하는 방법을 터득해가는 지금, 수많은 난해한 자료들을 읽고 비교해가며 모르는 내용을 학습하는 과정이 솔직히 쉽진 않았다. 하지만 힘들었던만큼 재밌었고 또 뿌듯했다.
앞으로도 이렇게 공부를 즐기면서 스스로 성장하는 개발자로 나아갔으면 좋겠다.


(혹시 작성한 내용 중 잘못된 부분이 있다면 꼭 댓글로 알려주시길 부탁드립니다. 🙇‍♂️)



참고 자료

profile
Slow, But Steady Wins The Race 😎

14개의 댓글

comment-user-thumbnail
2022년 9월 14일

좋은 글 잘 보았습니다. 감사합니다:)

1개의 답글
comment-user-thumbnail
2022년 9월 16일

정말 깔끔한 정리 고맙습니다!

1개의 답글
comment-user-thumbnail
2022년 9월 18일

포스팅 사전지식 섹션에 IndexedDM -> IndexedDB 오타가 있는 것 같아요!

글 잘 읽었습니다 :)

1개의 답글
comment-user-thumbnail
2022년 9월 20일

깔끔하게 정리해주셔서 이해가 잘 되었습니다. 감사합니다!

1개의 답글
comment-user-thumbnail
2022년 9월 23일

SY피디아... 복습 2일차 입니다 절 받으세여..

1개의 답글
comment-user-thumbnail
2022년 10월 24일

자세히, 친절히 설명해주신 글 잘 보았습니다. 감사합니다! :-)

1개의 답글
comment-user-thumbnail
2023년 4월 24일

풀어서 쉽게 설명해주셔서 이해하기 더 쉬웠어요. 좋은 글 감사합니다~!

1개의 답글