Q. 주소창에 특정 웹 페이지의 주소를 입력했을 때 어떤 일이 일어나나요?
프론트엔드 개발자를 준비하고 있다면 면접 단골 질문에 해당 질문이 있는 걸 많이 봤을 것이라고 생각한다.
나 또한 해당 질문을 많이 봤지만 블로그를 작성하면서 한 번도 브라우저의 렌더링 관련이나, 데이터 전달을 작성하거나 공부하지 않았다는 것이 문득 생각났다.
프론트엔드를 개발하려면 어떤 과정을 통해 데이터가 전달되고 화면에 렌더링 되는지를 확실하게 알고 있어야 한다.
그래서 이번 기회에 해당 과정을 한번 제대로 정리해 보고자 해당 주제를 작성해보려 한다.
주소창에 주소를 입력한다 → 특정 주소로 이동한다.
사실 브라우저가 어떻게 작동하는지 알지 못하는 사람은 단순히 주소를 이동할 때 위 두 단계밖에 확인하지 못합니다.
그러나 저희는 이 단순한 두 과정 사이에 얼마나 많은 과정이 있는지 알고... 아니, 몰라도 알아야 합니다.
아주 자세하게 구석구석 뜯어보며 어떤 과정이 일어나는지 알아보도록 하겠습니다.
주소창에 google.com 입력하기
어떤 주소를 입력하든 동일하겠지만, 저희는 조금 더 익숙한 예시를 들어 살펴보기 위해 사용자가 google.com을 입력한다고 가정해 봅시다.
사용자는 단순히 도메인을 입력하는 것뿐이지만, 브라우저의 입장에서는 고민해야 하는 과정이 존재합니다.
주소일까, 검색어일까?
google.com이라고 치면 저희야 육안으로 확인하고, 당연하게 웹 사이트 주소라고 생각하지만 눈이 없는👀 브라우저의 입장에서는 두 가지 가능성을 고려해야 합니다.
- 사용자가 https://google.com을 생략하고 도메인만 입력한 것일 수도 있고
- 그냥 "google.com"이라는 검색어를 입력한 것일 수도 있습니다.
실제로 크롬, 사파리, 파이어폭스 같은 주요 브라우저들은 자동완성, 검색제안, 보안정책에 따라 이를 구분합니다.
대부분 https를 기본으로 붙여서 먼저 시도하고, 주소가 아니라고 판단되면 검색 엔진으로 넘깁니다.
입력 해석이 잘못되면 어떻게 될까?
사용자 입장에서는 그냥 "구글"을 입력하든 "naver"를 입력하든, 검색하면 알아서 잘 떠야 하는데
브라우저가 이게 웹 요청인지, 검색어인지를 파악하는 과정에서 잘못 파악하게 된다면
1. 사용자가 실제로는 웹 사이트를 방문하려 하는데, 검색 결과로 리디렉션 하거나
2. 내부망 도메인이나 특정 포트를 포함한 개발용 주소 (localhost:3000)처럼 일반적인 웹 사이트 주소가 아닌 경우 잘못된 검색으로 인식되거나 동작되지 않을 수 있습니다.
Q. 브라우저는 이런 것들을 어떻게 판단하나요?
사용자가 https://google.com처럼 명확한 URL을 입력하면, 브라우저는 그대로 해당 URL로 접속을 시도합니다.
하지만 대부분의 경우 사용자는 프로토콜을 생략하고 google.com 만 입력하죠.
이때 브라우저는 내부 정책에 따라 다음과 같은 과정을 수행합니다
- 파싱: 입력값을 URL 형식으로 해석 가능한지 확인
- 프로토콜 보완: 보통 https://를 기본으로 붙여 시도
- 오류 확인: 문법적으로 잘못된 URL인지 판단
- 검색어 여부 판단: 위 조건이 모두 실패하면 검색어로 처리
이것들은 매우 짧은 시간 안에 일어나지만, 웹 페이지 로딩의 아주 기초적인 부분이라고 볼 수 있습니다.
이 파싱 과정이 중요한 이유는, 웹 요청의 정확성과 보안 때문입니다.
http://와 https://는 단순히 암호화 여부만 다른 게 아니라, 서버 연결 방식, 포트 번호, 정책(CORS, 쿠키 등) 모두 달라지기 때문이죠.
또한 주소 파싱이 정확해야 DNS 조회, TCP 연결, TLS 핸드셰이크 같은 다음 단계로 자연스럽게 이어질 수 있습니다.
DNS 서버에서 검색하기
브라우저가 이제 https://google.com이라는 URL을 완성했습니다.
그러나 이 단순한 문자열만으로는 컴퓨터는 google.com이라는 도메인 이름을 이해하지 못합니다. 인터넷은 철저하게 숫자(IP 주소) 기반으로 움직이거든요.
따라서 우리가 입력한 문자열을 실제 서버의 IP 주소로 바꿔주는 작업이 필요합니다.
바로 이때 등장하는 것이 바로 DNS입니다.
DNS가 무엇인가요?
DNS는 간단하게 말하자면, 인터넷의 전화번호부와 같다고 생각하시면 됩니다.
google.com을 입력하면 DNS가 이 도메인 이름을 찾아 실제 존재하는 서버의 주소인 142.250.206.14와 같은 IP 주소로 변환해 줍니다.
- 브라우저는 DNS 서버에 검색하기 전에 캐싱된 DNS 기록을 확인합니다.
: 브라우저 캐시 → 운영체제 캐시 → 라우터/공유기 캐시
위와 같은 순서로 DNS 캐시가 있는지 없는지 확인합니다. 만약 이 단계를 거쳐도 찾지 못한다면 - ISP(통신사) DNS 서버 탐색 → 권한 있는 DNS 서버 탐색
: .com 루트 DNS → google.com 권한 DNS 서버까지 올라가 최종 IP 주소를 받아옵니다.
Q. 그래서.. 왜 이렇게 복잡한 과정을 거치는 건가요?
A. 제가 위에 잠깐 언급했었죠? 인터넷은 IP 주소 기반으로만 움직인다고요.
사람이 기억하기 좋은 도메인과, 컴퓨터가 이해할 수 있는 IP 주소를 연결해 주기 위해 이 과정이 필요합니다.
만약 이 과정이 없었다면 저희는 페이지를 이동하기 위해 google.com을 입력하는 게 아니라,
https://142.250.206.14처럼 숫자를 직접 입력해야 했을 겁니다.
IP주소를 이용해 html 문서 요청하기
이제 브라우저가 DNS를 통해 google.com의 IP주소를 알아내는 과정을 완료했습니다.
남은 건 이 주소로 직접 서버에 연결하는 일이겠네요.
다만, 인터넷의 연결은 단순하지 않습니다.
안정적인 데이터 전송을 위해 TCP/IP 프로토콜을 사용해서 데이터의 전송을 제어하고, 데이터를 어떻게 보낼지, 어떻게 맞출지 정하는 과정이 필요합니다.
IP는 비신뢰성과 비연결성이라는 특징을 가지고 있기 때문에 IP 프로토콜 만으로 통신하는 것은 위험이 동반됩니다.
그렇기에 신뢰성과 연결성을 가진 TCP의 3-Way Handshake를 통해 통신을 합니다.
TCP가 뭐예요? 🤔
간단하게 말하자면, 인터넷에서 가장 널리 쓰이는 전송 프로토콜 중에 하나로 데이터를 신뢰성 있고, 손실 없고, 순서대로 전달하기 위해 고안된 프로토콜입니다.
이와 반대되는 개념에는 UDP가 있는데, 오늘은 그에 대해서 자세히 알아보지는 않겠습니다.
그러나 다음 사진 하나로 바로 이해하실 거라고 생각합니다.
UDP는 데이터를 빠르게 던지는 방식이라면, TCP는 서로 신뢰할 수 있는 상태에서 데이터를 주고받기 위해 사전 준비 단계를 거칩니다.
이 과정이 바로 3-Way Handshake입니다.
3-Way Handshake
이 과정을 일상에서 좀 찾아보자면, 연락하기 전 문자를 보내는 상황과 비슷하다고 볼 수 있겠네요.
A: "나랑 지금 전화 가능해?" → B: "응, 나 지금 전화 가능. 넌? " → A: "좋아. 그럼 전화할게"
3-way handshake는 연결 및 데이터를 수신받기 위한 과정에서 사용됩니다.
총 세 번의 과정을 통해 연결을 성립합니다.
- 클라이언트는 서버에 접속을 요청하는 SYN 패킷을 전송
- 서버는 SYN 요청을 받고 클라이언트에게 요청을 수락한다는 SYN_ACK flag가 설정된 패킷 전송
- 클라이언트는 서버에게 ACK 전송 후, 연결 성립
이 세 과정이 끝나면 정상적으로 연결이 성립됩니다.
TCP는 패킷 손실이나, 순서 꼬임이 발생하더라도 복구할 수 있도록 설계되어 있어 이 초기 과정이 매우 중요합니다.
너... 믿을 수 있는 거 맞지?
TCP 연결이 완료되면, 이제 데이터를 주고받을 수 있는 연결이 성립되었습니다.
그러나 단순히 연결이 되었다고 안심할 수 있는 건 아닙니다.
"연결된 상대가 진짜 구글이 맞나?" "이 내용을 누군가 훔쳐보면 어떡해?"
이런 보안적인 의문이 생기기 마련입니다.
이런 보안 문제를 해결하는 것이 바로 HTTPS(HTTP Secure)이고, 그 핵심 과정이 바로 TLS 핸드셰이크입니다.
TLS 핸드셰이크?
TLS(Transport Layer Security)는 HTTP 통신을 암호화해 주는 프로토콜입니다.
TLS 핸드셰이크는 서버와 클라이언트가 보안 통신을 시작하기 전, 서로를 확인하고 암호화할 준비를 하는 절차입니다.
어떤 순서로 진행되는지 간단하게 알아보고 넘어가겠습니다.
- Client Hello
: 제가 지은 이름 아닙니다. 🙄
클라이언트가 서버에게 "저 TLS 버전, 암호화 알고리즘, 랜덤값을 지원해요" 하고 메시지를 보냅니다. - Server Hello
: 서버가 이에 응답하면서 "어, 저도 같은 알고리즘 지원해요. 그리고 이건 제 인증서예요." 하고 SSL 인증서를 보냅니다. - 서버 인증 확인 + 비밀키 생성
: 클라이언트는 인증서를 검증하고, 서버만이 복호화할 수 있는 방식으로 비밀키를 암호화해 전송합니다. - 세션 키 공유 완료
: 클라이언트와 서버만 아는 세션 키가 생성되고, 이를 통해 본격적인 대칭키 암호화 통신이 시작됩니다.
HTTP 요청, 서버 응답
드디어 서버와의 대화가 시작됩니다.
위의 저 많은 과정이 주소를 입력하고 페이지를 이동하는 그 짧은 순간동안 지원된다는 게 진짜.. 대단합니다.
각설하고, 이제 서버와의 응답에서 사용되는 프로토콜이 저희에게 익숙한 HTTP입니다.
HTTP 요청
브라우저는 https://google.com에 접속하기 위해 HTTP 요청을 보냅니다.
GET / HTTP/1.1
Host: google.com
User-Agent: Mozilla/5.0 ...
Accept: text/html,application/xhtml+xml
Accept-Language: ko-KR
Connection: keep-alive
- User-Agent: 요청을 보낸 브라우저와 OS 정보
- Accept: 어떤 타입의 응답을 원하는지
- Cookie: 로그인 정보, 세션 등
- Referer: 이전에 방문한 페이지 정보
브라우저는 단순히 페이지를 요청하는 게 아니라 "이런 환경에서, 이런 것들을 보여주세요" 하고 꽤 구체적인 요청을 보냅니다.
그리고 웹서버(Nginx, Apache 등)는 이 요청을 정적인 HTML이 아니라면, 웹 애플리케이션 서버(WAS)로 넘겨줍니다.
WAS와 DB에서 우선 처리
웹 서버 혼자 모든 로직을 수행하고 데이터를 관리할 수 있다면 간단하겠지만, 그렇게 될 경우 서버에 과부하가 일어날 수 있습니다.
그렇기 때문에 서버의 일을 돕는 조력자가 필요하고, WAS가 바로 그 역할을 수행합니다.
WAS는 사용자의 컴퓨터나 장치에 웹 애플리케이션을 수행해 주는 미들웨어로,
브라우저에게 요청을 받으면 웹 서버는 페이지의 로직이나 데이터베이스의 연동을 위해 WAS에게 처리를 요청합니다.
그럼 WAS는 요청을 받아 동적인 페이지 처리를 담당하고, DB에서 필요한 데이터를 받아 파일을 생성합니다.
그리고 이 처리가 완료되면 작업 처리 결과를 웹 서버로 전송합니다.
서버 응답
서버는 브라우저의 요청을 받고, 준비된 데이터를 HTTP Response로 되돌려 줍니다.
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 23123
Set-Cookie: SID=abc123; Path=/; Secure
그리고 실제 HTML 문서도 함께 전달합니다.
필요한 CSS, JS, 이미지 등 리소스 경로도 이 HTML 문서 안에 포함되어 있습니다.
그리고 이 과정에서 status code를 통해 서버 요청에 따른 결과나 상태를 전달하게 됩니다.
1xx : 정보가 담긴 메시지
2xx : response 성공
3xx : 클라이언트를 다른 URL로 리다이렉트
4xx : 클라이언트 측에서 에러 발생
5xx : 서버 측에서 에러 발생
브라우저 렌더링
자, 드디어 HTML을 전달받았습니다.
이제 브라우저는 이 HTML을 시각적 인터페이스로 변환해 보여주기 위한 과정(렌더링)을 수행합니다.
웹 브라우저에 출력되는 단계를 Critical Rendering Path라고 하여 6단계로 분류합니다.
- HTML 파싱 → DOM(Document Object Model) 생성
- 브라우저는 HTML 코드를 한 줄씩 읽으면서, 브라우저가 이해할 수 있는 트리 구조인 DOM을 만들어냅니다.
- CSS 파싱 → CSSOM(CSS Object Model) 생성
- <style> 태그나 CSS 파일을 파싱해, 시각적인 정보(폰트, 색상 등)를 구조화합니다.
- 렌더 트리(Render Tree) 생성
- DOM과 CSSOM을 결합해서, 실제로 화면에 보일 요소들만 포함된 렌더 트리를 구성합니다.
- 레이아웃 (Reflow)
- 각 요소들의 크기, 위치, 정렬 등을 계산합니다. 페이지 상에 존재하는 객체의 크기를 렌더링 트리의 루트부터 시작해 모든 객체의 정확한 위치와 크기를 계산합니다.
- 이를 Layout 또는 Reflow라고 부릅니다.
- 페인팅 (Repaint)
- 실제로 화면에 픽셀을 채워 넣는 작업이 이루어집니다. 폰트, 색상, 그림자, 이미지 등을 시각적으로 표현합니다.
- 합성 (Compositing)
- 레이어를 조합해 최종적으로 브라우저 창에 나타나게 합니다.
- Transform, opacity와 같은 요소들을 의미합니다.
React, Vue 같은 프레임워크는?
위와 같은 렌더링 과정을 직접 다루긴 복잡하기 때문에 React, Vue와 같은 프레임워크는 가상 DOM을 이용해 DOM 변경을 최소화합니다.
그러나 결국 브라우저가 화면에 그리는 단계는 기본 렌더링 흐름과 동일하기 때문에, 프레임워크를 사용한다고 해도 이런 원리를 이해하고 있는 것이 중요합니다.
www.google.com 화면이 브라우저에 출력
이 수많은 과정을 거치고 나면 드디어 주소창에 입력한 주소에 맞는 화면이 웹 브라우저에 출력됩니다.
사실, 이 블로그를 쓰기 전까지는 브라우저 렌더링 과정을 이렇게 복잡하게 알고 있지도 않았고, 이렇게 복잡할 거라고 생각하지도 못했습니다.
브라우저가 렌더링 되는 시간이 아주 짧다 보니까 무의식적으로 과정도 짧을 거라고 생각하고, 이에 대해 알아보는 걸 미뤄왔던 것 같다는 생각이 드네요.
이 과정에 대해 쭉 정리하면서 과정을 이해했지만 막상 "과정에 대해 설명해 보세요"라고 질문이 들어오면 내가 곧바로 대답할 수 있을까?라는 의문이 듭니다.
단순히 포스팅을 한 것에 안주하지 않고, 계속 공부하고 과정을 손으로 적어보고, 말로 정리해서 꺼낼 수 있는 연습이 필요하다는 생각이 절절하게 드는 포스팅이었습니다.
자세하게 알아보니 내용이 훨씬 길어졌는데, 간단하게 말하자면 아래의 단계를 거친다고 말할 수 있겠습니다.
요약
- 웹 브라우저는 캐싱된 DNS 기록을 통해 도메인 주소와 대응하는 IP주소를 찾는다
- 캐싱 기록에 없을 경우 HTTP를 사용해 DNS에 입력된 도메인 주소를 요청한다
- DNS가 브라우저에게 찾은 사이트의 IP주소를 반환한다
- 웹 브라우저가 서버에게 IP 주소를 이용해 HTML 문서를 요청한다
- 서버가 요청을 처리한 뒤 HTTP Response를 브라우저에게 보낸다
- 웹 브라우저가 화면에 페이지를 출력한다.
요약은 이 블로그에 작성된 내용을 참고했습니다.
출처
'Frontend' 카테고리의 다른 글
[디자인 패턴] MVC / MVP / MVVM 패턴 (0) | 2025.04.16 |
---|---|
Tanstack Query와 Next.js fetch. 뭘 사용해야 할까? (0) | 2025.04.02 |
SSE(Server-Sent Event) : 서버야 네가 알려줘 (1) | 2025.02.06 |
프론트엔드를 위한 Presigned URL 업로드 (1) | 2025.02.04 |