본문 바로가기

Development/HTTP

HTTP 요청 흐름의 이해 (번역)

Understand the Flow of a HTTP Request 를 번역한 글 입니다.


브라우저에서 엔터 키를 눌렀을 때, 무슨 일이 일어나는 지 궁금한 적이 있는가? 이 아티클은 개발자의 관점에서, 개발자가 알아야 할 'HTTP 요청이 수행될 때 일어나는 일'에 대해 알려준다.

HTTP가 단순히 Document 전송 프로토콜일지라도, 본질적으로 인터넷의 근간이 된다.

HTTP 요청은 주소창에 URL을 입력하거나, 앱, 웹사이트 또는 다른 프로그램을 이용하는 것으로 요청이 시작되고, 응답을 수신하면 끝이 나는데, 이 사이에 마법이 일어난다.

이것이 우리가 전형적으로 HTTP 요청을 이해하는 방식이다.(과하게 표현하면)

 

 

우리 시스템에서 app.mydomain.com/me 를 열었다고 해보자. 브라우저는 URL앞에 http:// 가 있다고 가정 할 것이다.

자 이제 이 URL을 파헤쳐보자 http://app.mydomain.com/me

 

  • http:// : 통신에 사용된 프로토콜
  • mydomain.com : 서버의 도메인
  • app : 서버의 서브도메인
  • /me : 요청 Path

영어의 표준 문법에 따라 우리 모두가 의사소통이 가능한 것 처럼, HTTP 프로토콜도 전세계적으로 통용되는 미리 정의된 표준 규칙이 있어서, 유의미한 통신이 가능하다.

 

그래서 엔터를 치면, 우리 요청이 어떻게 서버로 가는데?


인터넷은 쉽게 말하면 글로벌 케이블 네트워크다. 이것은 네트워크에 연결된 각각의 디바이스 서로 통신할 수 있게 해준다.

이것은 지구 전역의 모든 데이터를 전달하는 심해 케이블을 통해 세계가 연결되는 방법이다.

생소하더라도 걱정하지마라, 나도 그랬다. 오랫동안 나는 인공위성을 통해 인터넷이 라우팅되는 줄 알았다.

우리가 런던에 있는데 미국에 있는 서버에 연결하려고 한다 치자. 이 케이블들 중에 하나는 우리 요청을 런던에서 미국으로 보낼 것이다.

근데 함정이 있다. 패킷/데이타 라우팅은 IP 주소를 이용하는 것이지 도메인네임을 이용하지 않는다.(우편이 거리이름과 집 주소를 보지, 우리 이름을 보고 배달되는 게 아닌 거랑 같다)

 

그래서 우린 app.domain.com의 IP 주소를 어떻게 찾는데?


이제 인터넷의 주소록인 Domain Name Server(DNS)가 활약할 지점이다. 우리는 DNS 서버에 붙고, app.domain.com의 IP주소를 물어본다. 그러면 DNS서버는 IP주소를 알려준다.

자 그러면, DNS 서버의 주소는 어떻게 알까?

통신을 위해서 한 기기는 다른 기기의 IP주소를 꼭 알아야 하기 때문에, 클라이언트는 DNS 서버의 IP 주소를 미리 알고 있어야 한다. 우리가 네트워크에 연결되면, 그 클라이언트는 IP 주소를 할당 받는데, 이때 DNS 서버의 주소도 같이 알려준다. 또한 우리가 수동으로 특정 DNS 서버를 등록하여 사용 할 수도 있다.

그러면, 실제로 DNS 서버가 어떻게 도메인을 IP 주소로 풀까?

우선 클라이언트는 DNS recursor server 로 요청을 보낸다.( DNS recursor server는 DNS 질의가 풀릴 때까지, 여러 네임서버로 질의를 날린다. )

우리가 도메인을 구입할 때 마다, 우리의 모든 DNS record를 관리하기에 믿을만한 네임서버에 설정을 한다.

자, 우리도메인(mydomain.com)을 Cloudfare 네임서버에 설정했다고 해보자. 그러면 우리 도메인에 대한 모든 쿼리는, Cloudfare가 응답할 권한을 가진다. 그래서 이 네임서버가 Authoritative Name Server 로 불린다.

그러면 DNS recursor는 어떻게 우리의 Authoritative Name Server로 도달할 수 있나?

  • 우선, 모든 TLD(Top Level Domain) Name Server의 주소를 얻기 위해 Root Name Server로 쿼리를 날린다. ( 이 경우 .com 에 해당 )

  • 이 TLD Name Server 는 해당 TLD(.com)의 모든 도메인에 대한 Authoritative Name Server의 주소를 저장하고 있다.

  • Authoritative Name Server는 우리가 설정한 레코드에 기반하여 응답을 반환한다. 예를 들어, 아래와 같이 DNS 레코드를 설정했다치자(mydomain.com)

우리는 처음에 app.mydomain.com 을 요청했기 때문에, 표를 보면 app에 해당하는 레코드를 찾을 수 있고, 그에 해당하는 IP 주소를 리턴한다.

이제 app.mydomain.com 에 해당하는 IP주소를 알기 때문에, 클라이언트가 서버에 붙을 수 있다

 

그러면 서버는 HTTP 요청에 대해 어떻게 응답하는데?


우리는 인입되는 HTTP 요청을 다룰 수 있는 웹 서버를 운영한다. 대개 자주 사용되는 웹서버는 Apache나 Nginx 가 있다. 이 웹서버는 계속해서 인입되는 요청을 기다리고, 요청에 대한 응답을 한다.

잠시!

서버에서 돌고 있는 프로세스가 많을 텐데, 어떻게 Apache로 잘 도착하지 ?

다른 장비처럼 여러 프로세스가 있을 것이다- 이때, 네트워크 포트가 유용한 지점이다. Apache와 같은 프로세스는 네트워크 포트를 물고 있고 이 포트를 통해서 listen한다. 단, 하나의 포트는 다수의 프로세스가 함께 사용할 수 없지만, 하나의 프로세스는 여러 포트를 사용할 수 있다.

그래서 우리는 통신을 위한 포트를 정해야한다. 그런데 우리는 포트를 정한 적이 없는데? 그렇지 않나?

앞서 말했듯이, HTTP 프로토콜은 전세계적으로 미리 정의된 규칙을 갖고 있다. 그중 하나가 기본 포트가 80(http), 443(https) 인 것이다. (같은 맥락으로 DNS는 53이다.) 그래서 우리의 Apache는 우리 서버에서 80,443 포트를 listen 하는 것이다.

단지 우리가 기본 포트를 변경하지 않았을 뿐이지, 우리는 3000번 포트로 아파치를 띄울 수 있다. 그러나 그렇게되면 HTTP의 표준포트를 이용하는 것이 아니기 때문에 우리는 명시적으로 포트를 써주어야 한다.

http://app.mydomain.com:3000/me

 

Apache/Nginx는 어떻게 HTTP Request를 서빙하는데?


웹 서버 애플리케이션의 대표적인 두가지 Use case 가 있다.

  • 요청 Path에 기반한 폴더에 스태틱 파일을 저장해 두고 서빙.
  • 노드 서버와 같은 다른 프로세스에게 요청을 넘기고, 그 프로세스가 응답을 만들게 한다.

스태틱 파일 서빙

<VirtualHost *:80>

    ServerName app.mydomain.com
    DocumentRoot "/var/www/my-static-website"
    ServerAdmin admin@mydomain.com

</VirtualHost>

우리가 app.mydomain.com/me 로 요청하면, /var/www/my-static-website 에서 폴더나 파일을 찾는다. 그리고 파일이 있으면 그 파일을 리턴한다. 또는 폴더가 존재한다면, 해당 폴더에서 index.html(변경가능) 찾아 리턴한다.

다른 프로세스로 요청을 전달

우리가 Node, Java, Python 또는 다른 프로세스를 4000번 포트로 띄우고, 그 프로세스가 요청을 처리하기를 원한다고 해보자.

이 경우, 아파치는 그 서버에 대해 프록시 역할을 할 수 있다.

<VirtualHost *:80>

    ServerName app.mydomain.com
    ProxyPreserveHost On

    ProxyPass / http://127.0.0.1:4000/
    ProxyPassReverse / http://127.0.0.1:4000/

</VirtualHost>

app.mydomain.com 으로 들어온 모든 요청은 http://127.0.0.1:4000 으로 포워딩 될 것이고, 응답을 받으면, 그 응답을 클라이언트로 다시 전달해 줄 것이다. 꼭 localhost로 포워딩 할 필요는 없고 다른 서버에 대한 프록시 역할도 가능하다.

 

Node/Java/Python 서버를 직접 80포트로 띄우는 건 어때? 아파치 부하도 줄일 겸!


보통 한 서버에 하나 이상의 웹사이트를 띄우는데 그렇게되면, 두개 이상의 프로세스가 하나의 포트를 점유할 수 없다, 다행히 Apache는 ServerName 기반으로 요청을 받기 때문에 여러 웹 사이트 운용이 가능!

그러나 만약 Node와 같이 우리가 하나의 서버만 호스팅한다면, Apache를 제거하고 얘네들을 직접 80으로 띄워서 운영할 수 있다. 직접 응답을 클라이언트로 보내게 된다.

 

그러면 HTTPS 는 ?


HTTPS는 HTTP의 업그레이드 버전이다. HTTP는 모든 것을 평문으로 전송한다. 우리는 이 데이타가 여러 회사가 갖고 있는 광케이블 네트워크를 통해 전달되고, 여러 정치관할구역을 지나는 것도 안다. 이때 누군가가 슬쩍 이 네트워크를 건드려서 클라이언트와 서버사이의 메시지를 볼 수도 있다

우리 Gmail 계정을 상상해봐라, 모두가 우리 이메일과 비밀번호를 읽을 수 있다. 끔찍하지 않은가 ?, 이것에 HTTPS 가 없는 인터넷이다. HTTPS는 클라이언트와 서버사이에 end-to-end 암호화를 제공하고, 아무도 이 메시지를 읽을 수 없다.

HTTPS의 보안이 없다면 광범위한 현대의 인터넷은 불가능 했을 것이다.