[그림으로 쉽게 이해하는 웹/HTTP/네트워크를 읽고 정리합니다.]
HTTP(Hypertext Transfer Protocol)
웹 브라우저와 웹 서버 간에 데이터를 주고 받기 위해 사용하는 프로토콜
HTML같은 하이퍼텍스트 형태의 데이터만 통신할 수 있었지만 오늘날에는 다양한 형식도 전달할 수 있다.
HTTP는 클라이언트/서버 모델을 따른다.
이는 서로 관계를 맺고 있는 컴퓨터가 클라이언트, 서버 2가지 역할로 구분되어 있다는 뜻으로,
클라이언트가 서버에게 요청을 보내고, 서버는 그에 대해 응답하는 구조를 말한다.
요청하고 응답하는 과정에서 클라이언트와 서버는 HTTP 메세지를 주고 받으며 통신한다.
이 HTTP 메세지는 클라이언트가 서버에 요청하는 요청 메세지, 서버가 클라이언트의 요청에 응답하는 응답 메세지로 구분된다.
즉 다른 프로토콜과 달리 현재 통신이 요청인지 응답인지에 따라 메세지의 형식이 다르다는 특징이 있다.
또 HTTP는 상태를 가지지 않는다. (stateless)
클라이언트와 서버가 첫 번째 통신에서 데이터를 주고받아도, 두 번째 통신에서는 그 데이터를 유지하지 않는다는 것이다.
따라서 웹 브라우저의 쿠키나 웹 서버의 세션처럼 상태를 저장하는 공간을 이용해 상태를 유지한다.
HTTP는 한 번 통신을 주고받으면 연결을 끊는 비연결성 프로토콜이다. (connectionless)
연결을 불필요하게 계속 유지할 때는 비용 낭비를 줄일 수 있지만, 요청마다 연결을 맺어야 하므로 연결 절차가 반복되는 문제가 있다.
다만 이는 초기 HTTP 버전의 특징(1.0)이며 오늘날 많이 사용하는 HTTP 버전(1.1과 2.0)에서는 이를 개선해 연결을 지속할 수 있다.
HTTP의 메세지 구조
1) 요청 메세지의 구성 요소
요청 메세지는 요청 라인, 헤더, 빈 줄, 본문으로 구성되어 있다.
1. 요청 라인
클라이언트가 무엇을, 어떤 방식으로, 어떻게 처리한다는 정보가 담겨 있다.
이를 요청 메서드(GET), 요청하는 자원의 경로(/), HTTP 버전 순서(HTTP/1.1)로 작성한다.
자원의 경로는 완전한 형식, /index.html처럼 상대적인 형식 등 상황에 따라 다양하게 작성할 수 있다.
프로토콜 버전은 0.9부터 1.0, 2.0, 3.0까지 있는데 버전별로 메세지의 구조가 다르기 때문에 작성해야 한다.
오늘날에는 1.1과 보안성을 강화한 2.0이 주를 이룬다.
2. 헤더
HTTP 메세지에 대한 추가 정보를 제공한다.
호스트의 정보와 접속한 사용자의 정보, 클라이언트에서 지원할 수 있는 데이터의 유형 등을 확인할 수 있다.
user-agent는 요청하는 웹 브라우저의 정보 및 운영 체제를,
accept-encoding은 클라이언트가 이해할 수 있는 압축 방식을 표현한다.
3. 빈 라인
헤더와 본문을 구별하기 위해 사용한다.
4. 본문
요청의 본문은 필수 요소가 아니며, POST처럼 새로운 자원을 추가해야 하는 경우에 작성한다.
단순히 GET, HEAD처럼 리소스를 가져오는 요청은 보통 본문이 필요하지 않다.
2) 응답 메세지의 구성 요소
응답 메세지는 응답 라인, 헤더, 빈 줄, 본문으로 구성되어 있다.
1. 응답 라인
응답의 HTTP 버전, 상태 코드, 상태 메세지 순서로 작성한다.
상태 코드는 클라이언트의 요청에 따른 서버의 응답 상태를 숫자로 나타낸 것이다.
따라서 이 상태 코드로 클라이언트는 요청이 잘 넘어갔는지, 메세지를 확인하지 않아도 이해할 수 있다.
상태 코드는 100번대부터 500번대까지 존재하며 상태 코드별로 의미가 다르다.
상태 메세지는 상태 코드를 이해하기 쉽게 텍스트로 풀어 쓴 메세지를 말한다.
2. 헤더
HTTP 메세지에 대한 추가 정보를 제공한다.
content-type은 전달한 리소스의 타입으로 텍스트 중에서도 html 타입이라는 뜻을 의미한다.
date는 해당 메세지가 만들어진 날짜와 시간을, content-encoding은 응답 메세지의 헤더와 동일한 뜻을 가진다.
3. 빈 라인
헤더와 본문을 구별하기 위해 사용한다.
4. 본문
가져온 리소스가 표시된다.
HTML 리소스라면 <html> 코드가 나타나고, 이미지나 동영상이면 그에 맞는 형식으로 표시된다.
응답받은 클라이언트가 이 본문안의 내용을 가져다 브라우저에 표현함으로써 웹 사이트가 만들어진다.
요청 메서드
- GET: 특정 리소스의 표시를 요청. 오직 데이터를 받기만 한다. 단순 조회 시 사용되기에 서버의 상태가 변경되지 않는다.
- HEAD: GET과 유사하지만 응답의 본문 없이 도착해 웹 서버가 정상적으로 동작하는지 서버 상태를 확인할 때 사용.
- POST: 특정 리소스를 서버에 전송할 때 사용. 데이터 추가, 수정, 삭제 시에도 사용하며 GET과 달리 서버의 상태가 변경된다.
- PUT: 데이터 전체를 완전히 수정할 때 사용.
- PATCH: 데이터 일부만 수정할 때 사용.
- DELETE: 특정 리소스를 삭제할 때 사용.
- OPTIONS: 목적 리소스의 통신을 설정할 때 사용. (클라이언트는 서버가 허용한 메서드가 무엇인지 요청, 서버는 Allow 헤더로 응답)
- CONNECT: 양방향 연결을 시작할 때 사용. (프록시 서버와의 터널링에 사용되는데 일반적인 웹 개발에서는 잘 사용하지 않음)
- TRACE: 특정 데이터의 경로를 조회할 때 사용. (거의 사용하지 않는다.)
안정성: 클라이언트가 요청을 해도 서버의 자원이 바뀌지 않는 특성
GET, HEAD, OPTIONS는 서버에 요청을 해도 단순 조회이기 때문에 서버의 자원이 바뀌지 않는다.
반면 POST, DELETE, PUT, PATCH는 서버에 요청을 하면 서버의 자원이 바뀌기 때문에 안정성이 없다.
멱등성(idempotent): 여러 번 서버에 요청을 보내도 결과가 달라지지 않는 특성
GET, HEAD, OPTIONS, DELETE, PUT은 서버에 1번 요청하든 100번 요청하든 달라지지 않는다.
반면 POST, PATCH는 멱등성을 가지지 않는다.
특히 PATCH의 경우 데이터의 일부만 수정하는데,
'내 닉네임을 wintermong으로 변경해줘' 같은 요청은 멱등성을 가지지만,
'내 정보의 나이에서 1을 더해줘' 같은 요청은 계속해서 +1 이 되기 때문에 멱등성을 가지지 않는다.
따라서 이러한 경우 때문에 PATCH는 PUT과 달리 멱등성을 가지지 않는다고 판단한다.
HTTP 헤더의 특징
HTTP 헤더는 TCP/IP 헤더와 차이점이 있다.
TCP/IP는 0101의 바이너리 유형으로 값을 입력하고, 헤더별로 정해진 위치가 있어 규격에 맞게 입력해야 한다.
반면 HTTP는 사람이 읽을 수 있는 텍스트 형태로 작성되고, 헤더가 아예 없어도 통신을 할 수 있어 필요한 만큼 헤더를 작성하면 된다.
또 HTTP 헤더는 헤더명: 헤더값의 형식으로 작성되며, 대소문자를 구분하지 않는다.
HTTP 헤더의 종류
헤더의 종류는 매우 많다. (MDN 참고 - 링크)
헤더는 컨텍스트에 따라 그룹핑 될 수 있는데, 대표적으로 4가지 헤더가 있다.
- General Header(공통 헤더): 요청과 응답 모두에 적용. Date, Cache-Control 등이 대표적인 공통 헤더다.
- Request Header(요청 헤더): 요청 메세지에 작성. 클라이언트에 대한 정보 또는 변경될 데이터에 대한 내용을 포함하는 헤더.
- Response Header(응답 헤더): 응답 메세지에 작성. 웹 서버의 종류 등 서버에 대한 정보를 포함하는 헤더.
- Entity Header(엔티티 헤더): 메세지 본문에 대한 정보를 포함하는 헤더. 본문의 길이, 자원의 미디어 타입 등이 해당된다.
헤더의 내용을 통해 요청과 응답 메세지의 정보를 빠르게 확인할 수 있다.
상태 코드(Status Code)
클라이언트의 요청에 따른 서버의 응답 상태를 세 자리 숫자로 나타낸 것
요청과 응답의 통신 성공 여부뿐만 아니라 문제가 발생했을 때, 어디가 원인인지도 확인할 수 있다.
상태 코드는 100번대부터 500번대까지, 첫 번째 자리의 숫자에 따라 5개의 클래스로 구분한다. (MDN 참고 - 링크)
- 100번대: 조건부 응답.
- 웹 서버가 현재 요청을 받아 작업을 진행하고 있음.
- 100(Continue) 등.
- 200번대: 클라이언트의 요청을 성공적으로 처리함.
- 200번(ok), 201번(Created), 204번(No Content) 등.
- 300번대: 리다이렉션 완료.
- 추가 요청한 페이지로 영구 이동하는 301번(Moved Permanently)
- 일시적 이동 302번(Found) 등.
- 400번대: 클라이언트의 오류.
- 클라이언트 요청에 문제가 있다는 400번(Bad Request)
- 인증되지 않은 사용자라는 401번(Unauthorized)
- 요청에는 문제가 없지만 요청한 데이터가 존재하지 않다는 404번(Not Found) 등.
- 500번대: 서버의 요청 수행 실패.
- 서버 내부의 오류로 응답에 실패했다는 500번(Internal Server Error)
- 서버가 다른 서버로부터 잘못된 응답을 받는 등 서버 간의 네트워크에 문제가 생겼을 때 502번(Bad Gateway)
SSL, TLS, HTTPS
HTTP는 정보를 누구나 볼 수 있게 암호화하지 않은 상태로 전송했다. (평문 Plain Text)
개발자들이 쉽게 메세지를 확인, 작성할 수 있는 장점이 있었지만 이를 역으로 이용한 사이버 범죄가 발생했다.
이러한 보안 문제로 대표적인 것이 중간자 공격(Man in the middle attack - MITM)이다.
네트워크 통신 중 중간자가 침입해 통신 내용을 도청/조작해 중요한 개인 정보가 노출되거나 정보가 위조되곤 했다.
또 HTTP는 통신 상대를 확인하지 않는다는 문제점이 있어 누군가가 통신에 몰래 끼어들거나 이상한 요청을 보내도 받아야 했다.
HTTP의 보안 문제를 해결하기 위해 탄생한 프로토콜이 HTTPS(HyperText Transfer Protocol Secure)다.
HTTPS는 메세지를 통신하는 당사자만 알아볼 수 있도록 암호화 한다.
이 암호화는 HTTPS가 아니라 SSL(Secure Sockets Layer)에서 담당하는데,
SSL은 클라이언트와 서버가 서로 데이터를 암호화해 통신할 수 있도록 돕는 보안 계층이다.
HTTPS를 사용하면 HTTP 메세지에 포함되는 콘텐츠 정보에 보안 요소가 추가되기 때문에,
OSI 계층 구조로 보면 HTTPS는 아래부터 위쪽으로 IP -> TCP -> SSL/TLS -> HTTP라고 볼 수 있다.
각 계층은 독립되어 있어 다른 프로토콜과 조합해서도 사용할 수 있다. (파일 전송을 위한 FTP, 이메일 전송 시 사용되는 SMTP 등)
SSL이 공개되었을 때 몇 가지 취약점이 발생해 구조를 재설계 하여 3.0 버전을 배포했는데,
기존 버전과 구분하기 위해 TLS(Transport Layer Security)로 이름을 변경했다.
그럼에도 SSL이 익숙하기 때문에 대부분의 프로토콜이 SSL -> TLS로 변경되었어도 SSL이라고 부르게 됐다.
암호화
암호를 만드는 행위, 암호화를 위해서는 비밀번호와 같은 개념인 키(key)가 필요하다.
이 키가 있어야 암호화를 하고, 반대로 암호를 푸는 복호화도 할 수 있다.
SSL은 공개키 기법과 대칭키 기법이라는 두 가지 암호화 기법을 함께 사용한다.
대칭키 기법은 하나의 키로 암호화/복호화를 할 수 있는 기법이다.
클라이언트와 서버가 각각 123이라는 키를 갖고 있다가
데이터가 오면 이 키로 복호화하고, 데이터를 보낼 때 같은 키로 암호화해 전송한다.
그렇기 때문에 속도가 빠르지만, 클라이언트와 서버가 같은 키를 주고받는 과정에서 탈취될 위험이 있다.
이 위험 요소를 해결하기 위해 등장한 것이 공개키 기법이다.
서로 다른 키 2개로 암호화와 복호화를 하는데, 키 2개를 각각 공개키/개인키라고 한다.
공개키는 누구나 가질 수 있지만, 개인키는 소유자 한 명만 가질 수 있다.
이 키는 늘 한 쌍으로 동작하기 때문에 암호화 하기 위해서는 반드시 두 키가 함께 필요하다.
공개키로 암호화한 데이터는 개인키로만 복호화 할 수 있고,
개인키로 암호화한 데이터는 공개키로만 복호화 할 수 있다.
그래서 서버가 개인키를 갖고, 클라이언트에게 공개키를 전달해 서로 데이터를 암호화 해서 주고 받는다.
중간에 누군가 클라이언트에게 전달될 공개키를 탈취해도, 개인키를 모르기 때문에 데이터를 복호화 할 수 없다.
이로써 대칭키 기법보다 훨씬 안전하지만, 과정이 복잡해 속도가 느리다는 단점이 존재한다.
SSL의 동작 과정
SSL은 핸드셰이크, 세션, 세션 종료 3가지 단계로 수행된다.
1. 클라이언트는 서버에게 인사를 건넨다. 이 때 랜덤한 데이터와 현재 지원할 수 있는 암호화 방식을 서버에 전달한다.
2. 서버는 클라이언트의 인사를 받고 똑같이 인사를 건넨다. 클라이언트처럼 랜덤한 데이터와 암호화 방식, 인증서를 전달한다.
인증서란 서버가 공식으로 인증된 기관인 CA(Certificate Authority)에서 발급받은 문서로
서버가 신뢰할 수 있는지 보장하는 역할인데, CA의 비밀키로 암호화 되어 발급된다.
3. 인증서를 받은 클라이언트는 CA가 발급한 인증서 목록 중에 전달 받은 인증서가 있는지 확인한다.
인증서가 목록에 있다면 CA에서 공유하는 공개키로 인증서를 복호화 한다.
복호화에 성공하면 이 인증서는 서버가 자신의 비밀키로 암호화 했다는 것이 검증되니 서버를 신뢰할 수 있게 된다.
만약 등록된 CA가 아니거나, 위조되었다면 여기서 발각되어 브라우저 경고를 보낸다.
4. 키를 주고받기 위해 클라이언트는 실제 데이터 통신에서 사용할 대칭키를 임시로 만든다.
앞에서 클라이언트와 서버가 주고받은 랜덤한 데이터를 조합해 임시 키를 만드는데,
이 임시 키는 대칭키이기 때문에 절대 노출되면 안되기에 공개키로 암호화해 서버에게 전달한다.
5. 키를 받은 서버는 자신의 비밀키로 암호를 해독해 임시 키를 전달 받아 클라이언트와 서버가 같은 키를 갖게 된다.
6. 클라이언트와 서버의 임시 키는 일련의 과정을 거쳐 세션 키로 바뀌고, 이 세션 키로 클라이언트와 서버가 통신할 수 있게 된다.
세션 단계에서는 세션 키를 이용해 대칭키 기법으로 데이터를 암호화해 통신하고,
데이터 전송이 끝나면 세션이 종료되며 통신이 끝난다.
이 때 세션 키도 삭제된다.
*참고
https://brunch.co.kr/@sangjinkang/38#comments
HTTP 변천
HTTP/0.9
1991년 월드 와이드 웹이 탄생하면서 등장한 초기 HTTP는 통신에 필요한 최소한의 기능(데이터 요청 - 응답한 데이터)만 갖춘 상태였다.
따라서 GET 요청 하나만 지원이 가능했고, 데이터 타입도 HTML 한 종류 뿐이었다.
이 초창기 HTTP를 HTTP/0.9라고 하는데, HTTP/1.0이 나온 후 이전 버전을 부르기 위해 뒤늦게 붙여진 이름이다.
HTTP/1.0
웹이 발전하면서 1996년 RFC(Request For Comments)라는 공식 인터넷 기술 문서로 공개되는데, 이게 HTTP/1.0이다.
이 때부터 헤더가 생겨 HTTP 버전 정보를 명시할 수 있게 됐고 GET, HEAD, POST가 추가되어 다양한 상호작용이 가능해졌다.
또 상태 코드를 표기했으며 HTML 외에도 이미지나 파일 등 다양한 타입의 데이터를 주고받을 수 있게 됐다.
하지만 이는 여러 기능을 하나로 정리했을 뿐 공식적인 표준은 아니었다.
HTTP/1.1
1997년에 HTTP/1.1이라는 이름으로 공식 표준이 정의되었다.
1.0은 맨 위에서 말했던 비연결성이라는 특징이 있었는데, 이를 해결하기 위해 1.1은 지속적 연결 상태(Persist Connection)을 지원했다.
이를 통해 한 번 TCP 연결을 맺으면 따로 연결을 끊기 전까지 기본적으로 연결을 유지해 메모리 자원을 절약할 수 있었다.
또 1.0은 이전 요청에 대한 응답이 도착해야만 다음 요청을 보낼 수 있었는데, 1.1에서는 파이프라이닝(Pipelining) 기법을 도입했다.
파이프라이닝은 첫 번째 요청에 대한 응답이 완전히 전송되기 전, 두 번째 전송 요청을 가능하게 하는 기법으로,
이전 응답과 상관없이 여러 개의 요청을 보낼 수 있게 됐고, 불필요한 지연을 막고 더 빠른 통신을 할 수 있게 됐다.
HTTP/2
2009년 구글에서 프로토콜 SPDY를 발표했다.
speedy라는 단어를 기반으로 한 이 프로토콜은 1.1의 성능 제한을 해결하고 웹 페이지의 로드 지연 시간을 줄이기 위해 탄생됐다.
SPDY를 기반으로 1.1 버전의 단점을 개선한 HTTP/2가 2015년에 세상에 나오게 됐다.
1.1은 하나의 요청에 하나의 응답이라는 1:1 대응을 이루고 있었고, 요청이 순서대로 진행되고 있었다.
따라서 하나의 요청에 대한 응답이 지연되면 나머지 요청들도 지연되는 문제점이 있었다.
HTTP/2는 스트림 다중화(Multiplexed Streams)를 도입해
응답 순서와 무관하게 한 연결에서 여러 개의 메세지를 주고 받을 수 있도록 개선했다.
또 서버 푸시(Server Push)를 통해 클라이언트가 굳이 요청하지 않아도
서버에서 미리 필요한 리소스를 푸시할 수 있게 되어 성능과 빠른 속도를 보장하게 됐다.
이러한 변화는 바이너리 프레이밍(binary framing)으로 가능했는데,
이는 텍스트 형식인 HTTP 메세지를 010101같은 바이너리 형태로 캡슐화 해서 용량을 줄이고, 효율적으로 전달하는 것을 말한다.
기존 HTTP 메세지가 헤더/바디의 한 묶음이었다면, 프레임으로 세분화해 전송하여 재조립할 수 있게 했다.
이렇게 바이너리 형태로 압축하고 압축을 풀기 위해서는 바이너리 프레이밍을 위한 새 계층이 필요했는데,
그렇기에 이전 버전인 1.1과 호환되지 않아 1.2가 아니라 HTTP/2라는 이름을 갖게 된 것이다.
HTTP/3
2020년에 등장한 3.0 버전은 TCP로 인한 HOLB(Head Of Line Blocking)을 해결했다.
HOLB는 이전에 TCP 부분에서 다뤘었는데, TCP가 순서대로 데이터를 처리하다 보니
데이터가 손실되면 이를 해결하는 동안 데이터가 블로킹되는 현상을 의미한다.
이 문제를 개선한 차세대 프로토콜이 구글에서 개발한 QUIC인데,
QUIC(Quick UDP Internet Connection)이라는 뜻으로, TCP가 아니라 UDP기반에서 작동한다.
UDP는 순서를 보장하지 않아 신뢰성이 낮은 편이었는데,
기존 TCP가 가진 데이터 검증 기능까지 구현해 이 신뢰성을 보장하게 되었다.
이것이 가능하게 된 것은 하나의 연결에 여러 전송 경로를 두는 멀티플렉싱(Multiplexing) 기법 덕분인데,
TCP와 달리 한 경로에 문제가 생기면 다른 경로를 지킬 수 있게 해 HOLB 문제를 해결하면서 안전하게 데이터를 운반할 수 있게 됐다.
QUIC가 TLS도 대체하면서 TCP의 3방향 핸드셰이크, TLS의 암호화를 위한 핸드셰이크를 생략하게 되어
이전보다 지연 시간이 드라마틱하게 줄어들었고, 2022년 6월 표준으로 지정되었다.
'🖥️ CS & Network & Web > 🕸️ Network' 카테고리의 다른 글
[Network] 8. 네트워크 접속 장치 (0) | 2023.12.22 |
---|---|
[Network] 7. HTTP 특징과 데이터 저장 방식 (0) | 2023.12.21 |
[Network] 5. TCP (0) | 2023.12.19 |
[Network] 4. IP (2) | 2023.12.19 |
[Network] 3. URL (0) | 2023.07.03 |