본문 바로가기

공부

브라우저는 html을 다 받았는지 어떻게 알고 렌더링 할까

과거에 봤던 면접 질문이 갑자기 생각났다.

브라우저의 렌더링 과정에 대한 질문을 받고 답변하는 중에

브라우저에서 html을 다 받았는지 어떻게 알고 파싱을 시작하냐는 꼬리질문을 받았다.

 

그 당시 나는 굉장히 당황스러웠지만

http는 tcp 위에서 돌아가는 것이니 tcp의 close 핸드쉐이크 후에 알 수 있다고 답변했던 것 같다.

 

하지만 현재 대부분 브라우저에서 사용되는 http/2는 TCP 커넥션을 귀하게 여긴다.

왜냐하면 TCP 커넥션을 다시 맺는 것은 비용이 크기 때문이다.

핸드쉐이크도 맺어야 하고, 혼잡제어라는 TCP의 독특한 방식 때문에 커넥션이 최적화 되기까지 시간이 걸리기 때문이다. (처음에 크기를 1보내보고 괜찮으면 더 크게, 이래도 괜찮아? 하면 더 더 크게 보내는 방식..)

따라서 http2는 파일 요청 하나 응답했다고 커넥션을 닫지 않는다.

 

그래서 하나의 커넥션 안에서 다수의 스트림이 오가는 방식으로 통신한다.

그렇다면 현재 HTTP/2가 일반화된 시점에서 html 파일을 다 받았는지 브라우저는 어떻게 알까? 가 갑자기 궁금해서 공부하고 작성하는 포스트이다.

 

결론을 미리 말하자면 http2는 하나의 TCP 커넥션 위에 파일 하나당 하나의 스트림을 두고 각 스트림은 헤더프레임, 데이터 프레임으로 구성되고 이 헤더 프레임에 flags 라는 작은 공간에 스트림의 상태를 제어한다.

그 상태를 가지고 파일 전송이 끝났는지, 더 진행되는지 클라이언트와 소통하는 것이다.

 

아래 내용은 그 결론을 얻기 위해 공부한 것을 정리한 내용이다.

 

HTTP 1.1의 문제

과거 프론트엔드에 처음 입문했을 때 '네이버 카페' 웹사이트를 분석한 경험이 있다.

지금도 그런지 모르겠는데 그 당시에 작은 아이콘 하나가 마음에 들어서 내 개인 프로젝트에 사용하기 위해 추출을 했는데

아래 그림처럼 아이콘 묶음으로 처리되고 css의 position을 이용해 보여주는 것을 관찰했었다.

 

그 당시에 왜 이렇게 하는지 잘 이해가 안 됐는데 http2를 공부하면서 이해하게 됐다.

1.1에서는 파이프라이닝 기법으로 http2처럼 커넥션을 절약하지만 동시 전송이 되지 않기 때문에 블로킹 현상이 발생한다. (네트워크 탭에 폭포수 형태로 나타남)

그래서 하나의 이미지 덩어리로 한 번에 다 보내는 것이다.

프론트엔드의 선배 개발자분들이 이런 꼼수로 http 1.1의 느린 속도를 보완했다고 한다.

 

하지만 이렇게 되면 파일 하나 전체가 커지기 때문에 역시 비효율적이다.

http/2에서는 이렇게 하지 않아도 된다. 위에 설명했듯이 동시 전송이 되기 때문이다.

 

HTTP/2의 Stream 구조

하나의 커넥션 안에서 여러 개의 스트림을 보내서 통신한다.

스트림은 여러 종류가 있지만

보통 웹사이트에선 스트림 하나당 리소스(파일) 하나라고 보면 된다.

그렇다면 어떤 스트림이 어떤 리소스인지 어떻게 아냐고 생각할 수 있는데

각 스트림에 id가 있어서 각 리소스에 맞는 요청, 응답 스트림을 구분할 수 있다.

 

스트림은 헤더 프레임, 데이터 프레임으로 구성된다.

 

 

각 프레임은 위 그림처럼 구성되는데

Flags 헤더는 스트림의 상태를 변경하는 데 사용되는 약속된 값이다.

이 값을 기준으로 스트림의 상태를 변경하고 스트림의 상태를 기준으로 파일 전송을 완료했는지, 더 보낼 것이 있는지를 약속하고 소통할 수 있는 것이다.

 

스트림의 상태는 다섯 가지가 있다.

 

  1. Idle: 아직 생성되지 않음
  2. Open: 사용중
  3. Half-Close: 브라우저가 요청을 보냈고 응답을 기다리는 상태
  4. Closed: 응답이 끝난 상태
  5. Reserved: http2의 서버 푸시 기능을 이용하기 위한 상태

처음 요청 헤더 프레임을 보내면 스트림의 상태는 idle에서 open으로 변경되고

요청이 끝났을 때 브라우저에선 END_STREAM 플래그를 보낸다.

그럼 응답을 기다리는 Half-close 상태로 변경되고

다시 응답 스트림에서 END_STREAM을 보내면 closed 변경되어 전송이 마무리된다.

 

 

 

참고

https://cabulous.medium.com/http-2-and-how-it-works-9f645458e4b2

https://www.oreilly.com/library/view/learning-http2/9781491962435/app01.html

https://web.dev/performance-http2/