브라우저에 google.com을 입력하면 무슨일이 일어날까? 실습기반으로 이해하기

ichubtou·2025년 6월 26일
1

위 제목은 웹 개발자라면 누구나 한 번쯤 들어봤을 법한 대표적인 질문이다.

지금까지 나는 이 과정을 암기식으로 이해하고 있었지만, 실제로 시스템 레벨에서 어떤 일이 일어나는지는 제대로 파악하지 못하고 있었다.
나는 이 글을 통해 단순히 이론을 나열하는데 그치지 않고, 실제로 하나하나 실습해가면서 브라우저에 url을 입력하고 어떤 일들이 일어나는지에 대해 깊이 있게 정리해 보려고 한다.



DNS 질의 - 도메인 이름을 IP로 바꾸는 과정

브라우저 주소창에 google.com을 입력하면 브라우저는 먼저 캐시된 DNS 결과를 확인하고 없을 경우 OS에 도메인에 해당하는 IP 주소를 요청한다.

여기서 중요한건 브라우저가 직접 DNS 서버와 통신하지 않고 OS가 DNS 질의를 만들어 DNS 서버에 전송하게 되는 것이다.



DNS 서버 주소는 어떻게 정해질까?

대부분의 사용자는 공유기를 통해 인터넷에 연결되어 있고, 운영체제는 DHCP로부터 DNS 서버 주소를 할당 받는다.

이 DNS 서버 주소는 두가지 형태 중 하나일 수 있다.

  • 공유기 자체 IP 주소 (예: 192.168.0.1)
  • 외부 DNS 서버 주소 (예: 8.8.8.8, ISP 제공 DNS 등)

DHCP는 IP주소, 서브넷 마스크, 게이트웨이, DNS 서버 주소등을 자동으로 클라이언트에 할당해주는 프로토콜로 공유기에 DHCP 서버가 내장 되어있으며 공유기에 연결된 기기들에게 IP 주소를 자동으로 할당해준다.

DNS 서버 주소는 도메인 이름을 IP 주소로 변환해주는 서버의 IP주소로
클라이언트는 DNS 서버가 어딘지 모르기 때문에 공유기가 해당 정보를 제공해주고 운영체제는 해당 DNS 서버 주소를 받고 요청을 보내게 된다.

여기서 받을 수 있는 DNS 서버 주소는 하나가 아니라 여러개를 받고 첫 번째 서버가 응답이 없으면 두 번째로 fallback하게 된다.

만약 DNS 서버 주소가 공유기일 경우 OS는 공유기 IP에 DNS 질의를 보내게 되고 공유기는 내부 캐시에 결과가 있으면 응답하고 없으면 외부 공인 DNS 서버로 요청을 하게 된다.



DNS 질의 흐름 정리

  • 브라우저 → OS에게 IP 주소 요청
  • OS는 /etc/resolv.conf 설정을 참고해서 DNS 서버로 질의 전송

DNS 서버는 아래와 같은 흐름을 따른다.

브라우저 -> OS -> 공유기(DNS 역할) -> 외부 DNS 서버(: 8.8.8.8)
브라우저 -> OS -> 외부 DNS 서버(DHCP에서 공유기를 DNS 서버로 설정하지 않은 경우

위의 흐름은 DHCP에서 공유기를 DNS서버로 설정한 경우

  • 공유기에 DNS 캐시를 가지고 있으면 응답
  • 없으면 ISP DNS로 포워딩

아래 흐름은 DHCP에서 공유기를 DNS서버로 설정하지 않은 경우

  • 질의 대상에 공유기가 없음
  • OS가 직접 외부 DNS로 질의

결국 브라우저가 공유기에 직접 IP를 요청하지 않으며 운영체제가 DNS 시스템 설정에 따라 지정된 DNS 서버로 질의하게 된다.



운영체제가 질의할 DNS 서버 확인하기

이 운영체제가 질의할 대상 DNS 서버는 /etc/resolv.conf 파일에서 확인할 수 있다.

cat /etc/resolv.conf

여기에 있는 nameserver 주소가 바로 OS가 DNS 질의를 전송할 곳이고 이 주소가 공유기일 수도 있고 DNS 서버일 수도 있는 것이다.

이 주소는 일반적으로 DHCP를 통해 자동으로 설정된다.

위 주소는 와이파이를 바꿀 때마다 동적으로 바뀔 수 있는데
DHCP가 네트워크 설정을 자동으로 내려주기 때문에 DNS 서버 주소도 같이 재설정되기 때문이다.



dig 명령어로 google.com 서버 IP 주소 확인하기

dig google.com
; <<>> DiG 9.10.6 <<>> google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37431
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;google.com.			IN	A

;; ANSWER SECTION:
google.com.		52	IN	A	142.250.207.14

;; Query time: 8 msec
;; SERVER: 168.126.63.1#53(168.126.63.1)
;; WHEN: Wed Jun 25 21:03:41 KST 2025
;; MSG SIZE  rcvd: 55

위의 dig 명령어로 DNS 서버와 url의 ip 주소를 확인할 수 있다.

QUESTION SECTION: 질의한 도메인

ANSWER SECTION: 응답받은 IP주소

DNS 서버 주소: 168.126.63.1

google 서버 주소: 142.250.207.14



HTTP 요청 - 터미널에서 TCP 연결 후 요청 보내기

DNS를 통해 접속하려는 google.com의 IP 주소를 확인했다고 가정하고 이 IP로 실제 연결을 시도 해야한다.
이때 브라우저는 TCP 연결을 맺고, 그 위에서 HTTP 요청을 보낸다.

TCP는 3-way handshake를 한다라고 알고 있었으나 실제로 어떤 과정을 거치는지 직접 확인해 본 경험은 없었다.

이번에 직접 TCP 연결을 열고, HTTP 요청을 터미널에서 보내보려고 한다.

telnet 명령어를 통해 이 과정을 직접 확인할 수 있다.

telnet google.com 80

연결이 성공하면 아래와 같은 메세지가 출력된다.

Trying 142.250.206.206...
Connected to google.com.
Escape character is '^]'.

이 메세지는 TCP 세션이 정상적으로 맺어졌다는 의미로 이 상태에서 아래와 같이 HTTP 요청을 수작업으로 입력할 수 있다.

GET / HTTP/1.1
Host: google.com

텍스트를 작성하고 엔터를 두 번 쳐준다.

그러면 다음과 같은 응답을 받을 수 있다.

telnet google.com 80

Trying 142.250.206.206...
Connected to google.com.
Escape character is '^]'.
GET / HTTP/1.1
Host: google.com

HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/
Content-Type: text/html; charset=UTF-8
Content-Security-Policy-Report-Only: object-src 'none';base-uri 'self';script-src 'nonce-bMjy2CmRcgn5sZkfiauh4A' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
Date: Tue, 24 Jun 2025 14:36:56 GMT
Expires: Thu, 24 Jul 2025 14:36:56 GMT
Cache-Control: public, max-age=2592000
Server: gws
Content-Length: 219
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/">here</A>.
</BODY></HTML>

현재 301 상태 코드를 볼 수 있는데 이것은 리소스가 더 이상 여기 있지 않고, 새로운 주소로 옮겼으니 거기로 다시 요청을 보내라는 뜻이다. 즉 클라이언트에게 리디렉션을 알려주는 HTTP 상태 코드가 301이다.

내가 요청한 URL은 http://google.com

응답은

HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/

구글에서 http://google.com 이 아닌 http://www.google.com 으로 가라는 뜻이다

그럼 다시 www.google.com으로 요청을 보내보자

Trying 142.250.196.142...
Connected to www.google.com.
Escape character is '^]'.
GET / HTTP/1.1
Host: www.google.com

HTTP/1.1 200 OK
Date: Tue, 24 Jun 2025 14:41:35 GMT

그럼 다음과 같이 정상적인 200 응답을 볼 수 있다.

여기서 한가지 의문이 생겼다.
브라우저에서 http://www.google.com 으로 작성했을 때는 https://www.google.com 으로 리디렉션 되는데 telnet으로는 http 요청이 되고 리디렉션 없이 http 응답이 오는걸까?



브라우저는 왜 처음부터 HTTPS로 전환될까?

브라우저, telnet의 http 통신 방식의 차이는 다음과 같다.

브라우저는 HSTS와 HTTPS Upgrade를 적용한다.

브라우저 동작

즉 HTTP 요청 자체가 나가지 않고 리디렉션은 서버가 아니라 브라우저가 먼저 처리한 것이다.

여기서 HSTS는 브라우저에게 이 도메인은 무조건 HTTPS로만 접속해라 라고 명령하는 보안 정책으로 서버가 클라이언트에게 아래와 같은 헤더를 내려보내면

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

브라우저는 이 도메인을 지정한 시간 동안 HTTP로 절대 요청하지 않고 HTTPS로 자동 전환한다.

추가로 HSTS preload list는 아예 브라우저에 내장되어 있는, HSTS가 적용된 도메인 목록이다.

이 리스트에 등록된 도메인은 최초 접속 조차도 무조건 HTTPS로 강제된다.

https://hstspreload.org/
위 사이트는 어떤 도메인이 리스트에 포함되어있는지 조회할 수 있는 기능을 제공한다.

참고로 네이버를 입력해보니 네이버는 리스트에 포함되어있지 않았다.

그러나 http로 요청을 넣어보니 아까 설명했던 HSTS 헤더가 존재하는 것을 확인할 수 있었다.

이어서

telnet 동작

  • telnet www.google.com 80 이라고 입력하면
  • TCP 포트 80(HTTP)로 연결
  • 이후 타이핑한 텍스트 그대로 보냄
  • 서버는 그 요청을 받고 301 또는 200 응답을 보냄

telnet은 내가 보낸 프로토콜/포맥을 신경쓰지 않고 TCP 소켓을 열고 데이터를 주고 받을 뿐이다.

그럼 브라우저가 실제로 HTTP 요청을 보내는지 확인하는 법이 있는데
개발자 도구 Network 탭을 보면 알 수 있다.

http://www.google.com 을 입력하면 다음과 같은 요청 정보를 볼 수 있다.

Request URL
http://www.google.com/
Request Method
GET
Status Code
307 Internal Redirect
Referrer Policy
strict-origin-when-cross-origin

여기서 307 코드는 서버가 준 리디렉션이 아니라 브라우저 스스로가 만든 리디렉션이라는 것이다.

곧 이 응답은 서버가 아닌 브라우저 만든 가짜 응답이다.

이렇게 http로 요청을 보냈는데 https로 바뀌어서 요청이 가는 이유에 대해서 알아보았다.

다음으로 TCP 연결 과정인 3-way handshake에 대해서 알아보자



3-way Handshake 과정 실시간으로 확인해보기

먼저 3-way Handshake 과정을 직접 눈으로 확인하기 위해 패킷 캡쳐 도구인 Wireshark를 설치한다.

https://www.wireshark.org/download.html

wireshark를 설치하고

나는 현재 와이파이를 사용하고 있으므로 와이파이를 선택했다.

다음으로 터미널에서 nslookup google.com 명령어로 구글 서버 ip를 알아낸다.
나의 경우에는 142.250.198.14였다.

그 ip로 wireshark에 해당 주소로 오는 패킷을 캡쳐할 준비를 하면 되는데 위의 텍스트 박스에 ip.addr == 142.250.198.14를 입력하여 해당 ip와 통신하는 패킷을 필터링할 수 있다.

그 후 해당 ip로 telnet 연결을 시도한다.
google.com과 같이 주소로 연결하지 않고 ip로 연결한 이유는 구글은 google.com에 여러개의 ip 주소를 운영하며 DNS 서버가 사용자 위치, 네트워크, 부하 상황을 고려해 최적의 IP를 반환하기 때문에 고정으로 ip를 지정해서 연결을 시도해야 해당 ip에 연결이 된다.

telnet 142.250.198.14 

다음과 같이 터미널에 연결하면 wireshark에 패킷이 오가는 것을 확인할 수 있는데 여기서 3-way hand shake를 확인할 수 있다.

이론으로 배웠던 SYN - SYN, ACK - ACK 과정을 볼 수 있다.

현재까지 http 통신 연결을 확인했는데 https에 대해서 알아보자



HTTPS TLS Handshake 과정 실시간으로 확인해보기

telnet 142.250.198.14 443

위의 명령어를 사용하면 https로 tcp연결을 하게 된다.
여기서 아까와 같은

GET / HTTP/1.1
Host: google.com

http 요청을 보내면

이렇게 연결이 끊어지게 된다.

이 이유는 HTTPS는 TLS로 암호화된 연결을 요구하는데 여기서 443포트가 https 전용이다.
여기서 서버는 클라이언트가 TLS handshake를 먼저 시도하기를 기대하는데
그 과정을 생략하고 평문 HTTP 요청을 보냈기 때문에 프로토콜을 위반하였다고 생각하여 TCP 연결을 바로 종료하게 된다.

그러나 터미널에서도 https 과정을 볼 수 있는데 다음 명령어를 사용하면 가능하다.

openssl s_client -connect [google.com:443](http://google.com:443/) 

이 명령어는 OpenSSL CLI 유틸리티로 TLS 클라이언트 모드로 실제 서버에 연결을 시도할 수 있다.

다음 명령어로 HTTPS 통신을 해보고 wireshark로 패킷을 확인해보자

이렇게 3way handshake를 마치고 TLS handshake를 하는 과정을 볼 수 있다.

TLS는 다음과 같은 기능을 한다.

  • 암호화: 제 3자로부터 전송되는 데이터를 숨긴다.
  • 인증: 정보를 교환하는 당사자가 요청한 당사자임을 보장한다.
  • 무결성 데이터가 위조되거나 변조되지 않았는지 확인한다.

TLK handshake 과정은 다음과 같다.



TLS 1.3 Handshake 흐름

  1. 클라이언트 → ServerHello 요청

    클라이언트가 보내는 내용

    • 지원하는 암호화 알고리즘 목록
    • 지원하는 TLS 버전
    • 난수(랜덤 값 1개)
    • SNI: 요청할 도메인명 (google.com)
    • 자신의 EDCHE 공개키 (g^a mod p)

    여기서 a는 클라이언트만 알고 있는 비밀 숫자
    클라이언트는 g^a mod p 값을 계산해서 공개키로 보냄

    위의 내용은 모두 평문으로 전송된다.

  2. 서버 → ServerHello 응답

    서버가 보내는 내용

    • 선택한 암호화 알고리즘
    • 서버의 인증서
    • 서버가 생성한 난수
    • 서버의 ECDHE 공개키 (g^b mod p)

    서버도 b라는 비밀 숫자를 정하고 공개키 g^b mod p를 보냄

    위의 내용도 모두 평문으로 전송된다.

양쪽은 서로의 공개키를 이용해서 같은 대칭키를 계산할 수 있다.

  • 클라이언트: (g^b)^a mod p = g^(ab) mod p
  • 서버: (g^a)^b mod p = g^(ab) mod p

각 값 설명

  • a: 클라이언트가 보내는 내용의 난수
  • b: 서버가 보내는 내용의 난수
  • g: 생성기, 미리 정해진 상수 (선택한 암호화 알고리즘에 값이 정해져 있다)
  • p: 큰 소수, 미리 정해진 상수 (선택한 암호화 알고리즘에 값이 정해져 있다)

여기서 생긴 궁금증이 있다.

암호화 알고리즘에 따라 g, p 값이 결정된다고 했는데

서버가 아직 암호화 알고리즘을 고르지도 않았는데

클라이어느는 어떻게 g^a mod p를 미리 계산해서 보낼 수 있는 걸까?

이 의문은 실제 ClientHello 메세지 내부 구조를 보면 해소된다.

ClientHello:
  cipher_suites: [TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256]
  supported_groups: [x25519, secp256r1]
  key_share:
    - group: x25519
      key: g^a

클라이언트는 내가 사용할 수 있는 그룹을 알려주면서 그중 하나는 미리 계산해서 key_share로 보낸다.

ServerHello:
  cipher_suite: TLS_AES_128_GCM_SHA256 // 서버가 고른 알고리즘
  selected_group: x25519 // 서버가 고른 키 교환 그룹
  key_share:
    - group: x25519
      key: g^b

서버는 클라이언트가 보낸 리스트 중에서 자신이 지원하는 것을 골라서 응답한다.

클라이언트가 보낸 key_share가 서버가 선택한 알고리즘에 해당한다면 바로 사용하고

없으면 다시 보내달라고 새로운 key_share를 요청하게 된다.

  • 조건이 맞으면 → 1-RTT 핸드셰이크로 빠르게 완료
  • 안 맞으면 → 2-RTT로 안전하게 재협상

즉 위의 과정을 통해서 클라이언트와 서버 모두 똑같은 공유 대칭키를 만든 것이다.

  1. 인증 확인 & Finished 메세지
  • 서로 인증서 검증(클라이언트는 서버 인증서가 진짜인지 확인)
  • 계산한 대칭키로 Finishied 메세지로 암호화해서 주고받는다.

여기까지 완료되면 완전히 암호화 상태로 통신 가능하다.

위와 같이 TLS Handshake까지 성공적으로 마무리 되면 이제 클라이언트와 서버가 안전한 통신을 할 수 있는 상태가 된다.
이후부터는 모든 HTTP 요청과 응답이 TLS 위에서 암호화되어 주고받게 되고 HTTPS 웹 페이지가 이 과정을 거쳐 브라우저에 출력된다.



평소에 당연하기 생각했던 주소창에 URL을 입력하고 웹페이지가 뜨는 과정 뒤에는 이렇게 다양한 단계가 숨어 있다는 것을 알 수 있었다.

이번 글을 통해 단순히 외운 지식이 아니라 직접 실습하고 눈으로 확인하면서 그 과정을 조금 더 깊게 이해할 수 있는 경험이 되었다.

0개의 댓글