![[네트워크 이론] 응용 계층 - HTTP](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNO3Pl%2FbtsNwbenIqZ%2FYN9gWiZGpQwkeBnvIC8oNK%2Fimg.png)
이글은 혼자 공부하는 네트워크(저자 : 강민철)의 책과 강의 내용을 개인적으로 정리하는 글임을 알립니다.
HTTP의 특성
HTTP 프로토콜은 중요한 4가지 특성이 있다.
- 요청과 응답 기반
- 미디어 독립적
- 무상태성(Stateless)
- 지속 연결
요청-응답 기반 프로토콜
- HTTP는 ‘클라이언트–서버 구조 기반의 요청–응답 프로토콜’이다.
- 클라이언트와 서버가 있고, 클라이언트는 서버에게 요청 메시지를 전송하며, 서버는 클라이언트에게 요청에 대한 응답 메시지를 전송한다.
- HTTP는 이와 같이 클라이언트와 서버가 서로 HTTP 요청 메시지와 HTTP 응답 메시지를 주고받는 구조로 동작한다.
- 그렇기에 같은 HTTP 메시지일지라도 HTTP 요청 메시지와 HTTP 응답 메시지는 메시지 형태가 다르다.
미디어 독립적 프로토콜
HTTP를 정의한 공식 문서(RFC 9110)에서는 다음과 같이 이야기한다.
RFC 9110
The target of an HTTP request is called a “resource”. HTTP does not limit the nature of a resource; it merely defines an interface that might be used to interact with resources. Most resources are identified by a Uniform Resource Identifier (URI).
- 해당 내용을 직역하면 ‘HTTP가 요청하는 대상을 자원이라고 한다.
- HTTP는 자원의 특성을 제한하지 않으며, 단지 자원과 상호 작용하는 데 사용할 수 있는 인터페이스를 정의할 뿐이다.
- 대부분의 자원은 URI로 식별된다’라는 뜻이다.
다시 말해 HTTP는 주고받을 자원의 특성과 무관하게 그저 자원을 주고받는 수단(인터페이스)의 역할만을 수행한다.
실제로 HTTP를 통해서 HTML, JPEG, PNG, JSON, XML, PDF 파일 등 다양한 종류의 자원을 주고받을 수 있다.
HTTP에서 메시지로 주고받는 자원의 종류를 미디어 타입이라 부른다. MIME 타입이라고도 부른다.
즉, HTTP는 주고받을 미디어 타입에 특별히 제한을 두지 않고 독립적으로 동작이 가능한 미디어 독립적인 프로토콜이라 할 수 있다.
미디어 타입
미디어 타입은 기본적으로 슬래시를 기준으로 하는 ‘타입/서브타입’ 형식으로 구성된다. 타입은 데이터의 유형을 나타내고, 서브타입은 주어진 타입에 대한 세부 유형을 나타낸다.
- type/subtype
미디어 타입 통칭
참고로, 별표 문자(*)는 여러 미디어 타입을 통칭하기 위해 사용된다.
예를 들어 text/*는 text 타입의 모든 서브타입을 나타내고, image/*는 image 타입의 모든 서브타입을 나타낸다. 또 */*는 모든 미디어 타입을 나타낸다.
미디어 타입 메타데이터
또한 미디어 타입에는 부가적인 설명을 위해 선택적으로 매개변수가 포함될 수도 있다. 매개변수는 ‘타입/서브타입;매개변수=값’의 형식으로 표현된다. 다음 표현의 붉은색 글자를 참고.
- type/subtype;parameter=value
예를 들어, type/html;charset=UTF-8은 미디어 타입이 HTML 문서 타입이며, HTML 문서 내에서 사용된 문자는 UTF-8로 인코딩되었음을 의미한다.
무상태(Stateless) 프로토콜
- HTTP는 상태를 유지하지 않는 스테이트리스 프로토콜이다.
- 이는 서버가 HTTP 요청을 보낸 클라이언트와 관련된 상태를 기억하지 않는다는 의미이다.
- 그렇기 때문에 클라이언트의 모든 HTTP 요청은 기본적으로 독립적인 요청으로 간주된다.
예를 들어 클라이언트가 실수로 특정 HTTP 요청 메시지를 서버에게 여러 번 전송했다고 가정해 보자.
서버는 이 요청들을 각기 다른 요청으로 간주한다. 따라서 클라이언트는 같은 응답 메시지를 여러 번 받을 수 있다.
상태를 유지하지 않는 특성은 언뜻 효율적이지 않아 보일 수도 있지만, 실제로는 장점이 더 명확하다.
HTTP 서버는 일반적으로 많은 클라이언트와 동시에 상호 작용한다. 동시에 처리해야 할 요청 메시지의 수는 수천 개가 될 수도 있고, 많게는 수백만 개가 될 수도 있다. 이러한 상황에서 모든 클라이언트의 상태 정보를 유지하는 것은 서버에 큰 부담이다.
또한, 서버는 하나가 아니라 여러 대로 구성될 수도 있다. 이런 상황에서 모든 서버가 모든 클라이언트의 상태를 유지할 경우 클라이언트는 여러 서버를 동시에 이용하기가 어려워진다. 모든 서버가 모든 클라이언트의 상태 정보를 공유하는 작업은 매우 번거롭고 복잡하기 때문이다.
HTTP가 상태를 유지하는 프로토콜이었다면 클라이언트는 자신의 상태를 기억하는 특정 서버하고만 상호 작용할 수 있게 되어, 특정 클라이언트가 특정 서버에 종속될 수 있다.
이러한 상황에서 어느 한 서버에 문제가 발생하면 해당 서버에 종속된 클라이언트는 직전까지의 HTTP 통신 내역을 잃어버리는 상황이 발생할 수도 있다.
확장성과 견고성
HTTP가 처음 만들어졌을 때부터 오늘날까지 이어지는 중요한 설계 목표는 바로 확장성과 견고성이다.
서버는 하나가 아니라 여러 개가 있을 수도 있다. 상태를 유지하지 않고 모든 요청을 독립적인 요청으로 처리하는 것은 특정 클라이언트가 특정 서버에 종속되지 않도록 하며, 서버의 추가나 문제 발생 시 대처가 용이하도록 해준다.
즉, 상태를 유지하지 않는 스테이트리스한 특성은 필요하다면 언제든 쉽게 서버를 추가할 수 있기 때문에 확장성이 높고, 서버 중 하나에 문제가 생겨도 쉽게 다른 서버로 대체가 가능하기 때문에 견고성이 높다.
참고로, HTTP가 스테이트리스 프로토콜이라고 할지라도 서버가 클라이언트의 요청을 매번 처음 보는 것처럼 동작하는 것만은 아니다. 상태를 유지하지 않는 HTTP의 특성을 보완하기 위한 여러 방법이 있다.
지속 연결 프로토콜
비지속 연결
- 초기의 HTTP 버전(HTTP 1.0 이하)으로 쓰리 웨이 핸드셰이크를 통해 TCP 연결을 수립한 후, 요청에 대한 응답을 받은 연결을 종료하는 방식으로 동작했으며, 추가적인 요청–응답을 하기 위해서는 다시 TCP 연결을 수립해야 했다.
- 이러한 방식을 비지속 연결이라고 한다.
지속 연결
- 최근 대중적으로 사용되는 HTTP 버전(HTTP 1.1 이상)은 지속 연결이라는 기술을 제공한다.
- 다른 표현으로는 킵 얼라이브라고도 부른다.
- 이는 하나의 TCP 연결상에서 여러 개의 요청–응답을 주고받을 수 있는 기술이다.
위 그림의 내용처럼 지속 연결 기능을 지원하는 HTTP는 매번 새롭게 연결을 수립하고 종료해야 하는 비지속 연결에 비해 더 빠르게 여러 HTTP 요청과 응답을 처리할 수 있다.
HTTP 메시지 구조(헤더)
대중적으로 사용되는 HTTP 버전 중 하나인 HTTP 1.1 버전의 메시지를 위주로 다룬다. HTTP 메시지의 구성은 크게 다음과 같다.
HTTP 메시지는 시작 라인, 필드 라인, 메시지 본문으로 이루어져 있다.
필드 라인은 없거나 여러 개 있을 수 있고, 메시지 본문은 없을 수 있다. 또한 필드 라인과 메시지 본문 사이에는 빈 줄 바꿈이 있다.
시작 라인
HTTP 메시지는 HTTP 요청 메시지일 수도 있고, HTTP 응답 메시지일 수도 있다. 이때, HTTP 메시지가 HTTP 요청 메시지일 경우 시작 라인은 '요청 라인'이 되고, HTTP 메시지가 HTTP 응답 메시지일 경우 시작 라인은 '상태 라인'이 된다.
- HTTP 응답 메시지 : 시작 라인 -> 상태 라인
- HTTP 요청 메시지 : 시작 라인 -> 요청 라인
HTTP 요청 메시지의 시작 라인인 요청 라인의 형식은 다음과 같다. 메서드, 요청 대상, HTTP 버전은 모두 공백으로 구분된다는 점에 유의해야 한다.
메서드
- 메서드는 클라이언트가 서버의 자원(요청 대상)에 대해 수행할 작업의 종류를 나타낸다.
- 대표적으로 GET, POST, PUT, DELETE 등이 있다. 요청 대상이 같아도 메서드가 다르면 각기 다른 요청으로 처리된다.
- 요청 대상은 HTTP 요청을 보낼 서버의 자원을 의미한다.
- 보통 이곳에는 (쿼리가 포함된) URI의 경로가 명시된다. 예를 들어 클라이언트가 http://www.example.com/hello?q=world 로 요청을 보낼 경우, 요청 대상은 /hello?q=world 가 된다.
- 만약 하위 경로가 없더라도 요청 대상은 슬래시(/)로 표기해야 한다. 즉, 예를 들어 클라이언트가 http://www.example.com 으로 요청할 경우 요청 대상은 / 가 된다.
HTTP 버전
- HTTP 버전은 이름 그대로 사용된 HTTP 버전을 의미한다. HTTP/〈버전〉이라는 표기 방식을 따르며, HTTP 버전 1.1은 HTTP/1.1로 표기된다.
- HTTP 메시지가 HTTP 응답 메시지일 경우 시작 라인은 다음과 같은 상태 라인이 된다. 상태 라인의 형식은 다음과 같으며, HTTP 버전, 상태 코드, 이유 구문 모두 공백으로 구분된다.
상태 코드
- 상태 코드는 요청에 대한 결과를 나타내는 세 자리 정수이다. 클라이언트는 상태 코드를 통해 요청이 어떻게 처리되었는지 판단할 수 있다.
- ex) HTTP/1.1 200
- ex) HTTP/1.1 404
이유 구문
- 이유 구문은 상태 코드에 대한 문자열 형태의 설명을 의미한다.
- ex) HTTP/1.1 200 OK
- ex) HTTP/1.1 404 Not Fount
필드 라인
- 필드 라인에는 0개 이상의 HTTP 헤더가 명시된다. 그래서 이를 헤더 라인이라고도 부른다.
- 여기서 HTTP 헤더란 HTTP 통신에 필요한 부가 정보를 의미한다.
- 필드 라인에 0개 이상의 HTTP 헤더가 명시된다고 언급되지만, 실제로는 한 HTTP 메시지에 아주 다양한 HTTP 헤더들이 사용되는 것이 일반적이다.
필드 라인에 명시되는 각 HTTP 헤더는 콜론(:)을 기준으로 헤더 이름header-name과 하나 이상의 헤더 값header-value으로 구성된다.
다음 예시에서 붉은색으로 표기한 글자가 HTTP 헤더이다.
HTTP 요청 혹은 응답 메시지에서 본문이 필요할 경우 이는 메시지 본문(message-body)에 명시된다.
메시지 본문은 존재하지 않을 수도 있고, 다음과 같이 다양한 콘텐츠 타입이 사용될 수도 있다.
요청 시 활용되는 HTTP 헤더
HTTP 요청 시 주로 활용되는 대표적인 헤더는 ‘Host’, ‘User-Agent’, ‘Referer’, ‘Authorization’ 등이 있다.
Host
Host는 요청을 보낼 호스트를 나타내는 헤더이다. 주로 도메인 네임으로 명시되며, 포트 번호가 포함되어 있을 수 있다.
다음의 예시 메시지는 http://info.cern.ch/hypertext/WWW/TheProject.html에 접속할 때의 HTTP 요청 메시지 일부이다.
User-Agent
User-Agent는 Host 헤더와 더불어 HTTP 요청 메시지에서 가장 흔히 볼 수 있는 헤더 중 하나이다.
이 헤더는 웹 브라우저와 같이 HTTP 요청을 시작하는 클라이언트 측의 프로그램을 의미한다.
User-Agent 헤더에는 이러한 정보, 즉 요청 메시지 생성에 관여한 클라이언트 프로그램과 관련된 다양한 정보가 명시된다.
운영체제, 브라우저 종류 및 버전, 렌더링 엔진과 같은 다양한 정보가 User-Agent 헤더에 포함되어 있음을 알 수 있다.
서버 입장에서는 User-Agent 헤더를 통해 클라이언트의 접속 환경을 유추할 수 있다.
Referer
Referer는 개발 시 아주 유용한 헤더 중 하나이다.
이 헤더에는 클라이언트가 요청을 보낼 때 머무르고 있던 URL이 명시된다.
다음 예시 헤더는 클라이언트가 https://en.wikipedia.org에서 요청을 보냈음을 의미한다.
Referer를 통해 클라이언트의 유입 경로를 파악해 볼 수 있다.
참고로, 영문법적으로는 Referrer가 맞지만, 초기 개발 당시의 오타로 인해 Referer라는 표기가 오늘날까지 사용되고 있다.
Authorization
Authorization 헤더는 클라이언트의 인증 정보를 담는 헤더이다.
이 헤더에는 인증 타입 type과 인증을 위한 정보 credentials가 차례로 명시된다. 인증 타입에 따라 인증 정보에 명시될 값이 달라진다.
인증 타입의 종류는 다양하지만, 가장 기본적인 HTTP 인증 타입은 Basic이라는 타입이다.
Basic 타입 인증은 username:password와 같이 사용자 아이디 username과 비밀번호 password를 콜론을 이용해 합친 뒤, 이를Base64 인코딩한 값을 인증 정보 credential로 삼는 방식이다.
여기서 Base64 인코딩이란 문자를 코드로 변환하는 방법을 의미하는 인코딩 encoding 방식의 일종이다.
예를 들어 사용자 아이디가 ‘minchul’이고 비밀번호는 ‘1234’라고 가정해 보자. ‘minchul:1234’를 Base64 방식으로 인코딩하면 bWluY2h1bDoxMjM0이 된다.
따라서 사용자 아이디 ‘minchul’과 비밀번호 ‘1234’를 Basic 타입으로 인증하기 위해서는 위와 같은 헤더를 보내면 된다.
응답 시 활용되는 HTTP 헤더
HTTP 응답 시 주로 활용되는 대표적인 헤더인 ‘Server’, ‘Allow’, ‘Retry-After’, ‘Location’, ‘WWW-Authenticate’ 헤더 등이 있다.
Server
Server 헤더는 요청을 처리하는 서버 측의 소프트웨어와 관련된 정보를 명시한다. 예를 들어 다음 예시 헤더는 ‘Unix 운영체제에서 동작하는 아파치 HTTP 서버’를 의미한다.
Allow
Allow 헤더는 클라이언트에게 허용된 HTTP 메서드 목록을 알려 주기 위해 사용된다.
상태 코드 405(Method Not Allowed)는 ‘요청한 메서드를 지원하지 않음’을 의미하는 상태 코드이다.
상태 코드 405(Method Not Allowed)를 응답하는 메시지에서 Allow 헤더가 함께 사용된다.
Retry-After
상태 코드 503(Service Unavailable)는 ‘현재는 요청을 처리할 수 없으나 추후 가능할 수도 있음’을 의미한다.
이 응답과 함께 사용될 수 있는 헤더가 Retry-After 헤더이다.
이 헤더는 자원을 사용할 수 있는 날짜 혹은 시각을 나타낸다.
아래 예시는 각각 ‘2024년 8월 23일 금요일 09시 이후에 사용 가능하다’는 사실, ‘120초 이후에 사용 가능하다’는 사실을 나타내는 헤더이다.
Location
Location 헤더는 클라이언트에게 자원의 위치를 알려 주기 위해 사용되는 헤더이다.
주로 리다이렉션이 발생했을 때나 새로운 자원이 생성되었을 때 사용된다.
WWW-Authenticate
상태 코드 401(Unauthorized)는 요청한 자원에 대한 유효한 인증이 없을 때 응답하는 코드이다.
상태 코드 401(Unauthorized)과 함께 사용되는 헤더가 WWW-Authenticate이다.
WWW-Authenticate 헤더는 자원에 접근하기 위한 인증 방식을 설명하는 헤더이다. 이를테면 다음과 같이 Basic 인증을 요구할 수 있다.
다만 실제로는 이보다 조금 더 많은 정보를 알려 주는 경우가 많다. 예를 들어 다음과 같이 보안 영역(realm)을 함께 알려 주거나 인증에 사용될 문자 집합(charset)도 알려 줄 수 있다.
- 인증되지 않은 클라이언트가 서버에 GET 요청 메시지를 전송한다.
- 서버는 클라이언트에게 상태 코드 401(Unauthorized)과 함께 WWW-Authenticate 헤더를 통해 인증 방식을 알린다.
- 클라이언트는 사용자로부터 인증 정보(사용자 아이디와 비밀번호)를 전달받는다.
- “사용자 아이디:비밀번호”를 Base64 인코딩한 값을 인증 정보로 삼은 Authorization 헤더를 통해 다시 GET 요청 메시지를 전송한다.
- 서버는 인증 정보를 확인한다.
- 인증이 유효하면 상태 코드 200으로 응답하고, 인증되지 않았으면 상태 코드 401로 응답한다.
요청과 응답 모두에서 활용되는 HTTP 헤더
Date
Date는 메시지가 생성된 날짜와 시간에 관련된 정보를 담은 헤더이다. 클라이언트와 서버 모두에서 사용될 수 있는 헤더이다.
Connection
Connection 헤더는 클라이언트의 요청과 응답 간의 연결 방식을 설정하는 헤더이다.
‘Connection: keep-alive’ 헤더를 통해 상대방에게 지속 연결을 희망함을 알릴 수 있다.
또한 서버나 클라이언트가 연결을 종료하고 싶을 때는 ‘Connection: close’를 통해 알릴 수 있다.
Connection 필드는 다양한 값을 가질 수 있지만, 가장 대표적으로 사용되는 값은 keep-alive와 close이다.
content-Length
Content-Length 헤더는 본문의 바이트 단위 크기(길이)를 나타낸다.
Content-Type, Content-Language, Content-Encoding
이 헤더들은 전송하려는 메시지 본문의 표현 방식을 설명하는 헤더이다.
이런 점에서 이 헤더들은 표현 헤더(representation header)의 일종이라고도 부른다.
Content-Type 헤더는 메시지 본문에서 사용된 미디어 타입을 담고 있다.
- 이는 HTTP의 특성(미디어 독립적 프로토콜)을 설명할 때 자주 언급된다.
- 예를 들어, 다음 헤더는 메시지 본문이 HTML 문서 형식이며, 문자 인코딩으로 UTF-8을 사용한다는 정보를 알려준다.
Content-Language 헤더는 메시지 본문에 사용된 자연어를 명시한다.
- 어떤 언어로 작성되었는지 Content-Language를 통해 알 수 있다.
- Content-Language의 값은 언어 태그로 명시되며, 언어 태그는 하이픈(-)으로 구분된다.
예를 들어, Content-Language 헤더 값이 en이거나 ko일 경우 첫 번째 서브 태그만 사용된 것이고,
Content-Language 헤더 값이 en-US, ko-KR일 경우 두 번째 서브 태그까지 사용된 것이다.
- 첫 번째 서브 태그는 언어 코드로, 특정 언어를 의미하는 언어 코드가 명시된다.
- 두 번째 서브 태그는 국가 코드로, 특정 국가를 의미하는 국가 코드가 명시된다.
- 언어 코드와 국가 코드를 조합하면 ‘어떤 국가에서 사용하는 어떤 언어’인지를 알 수 있게 된다.
- 예컨대 ko는 ‘한국어’를 의미하고, ko-KR은 ‘한국에서 사용하는 한국어’라는 의미가 된다.
- 마찬가지로 en은 ‘영어’를 의미하고, en-US는 ‘미국에서 사용하는 영어’, en-GB는 ‘영국에서 사용하는 영어’를 의미한다.
Content-Encoding 헤더에는 메시지 본문을 압축하거나 변환한 방식이 명시된다.
- HTTP를 통해 송수신되는 데이터는 전송 속도를 개선하기 위해 종종 압축이나 변환이 되곤 하는데, 이때 사용된 방식이 Content-Encoding 필드에 명시된다.
- 수신 측은 이 헤더를 통해 압축 및 변환 방식을 인식하고, 압축을 해제하거나 원문으로 재변환하여 본문 내용을 확인할 수 있게 된다.
- Content-Encoding 헤더에 명시될 수 있는 대표적인 값은 gzip, compress, deflate, br 등이 있다.
HTTP 상태 코드
다음으로 HTTP 응답 메시지상의 상태 코드를 알아보겠다. 상태 코드는 요청에 대한 결과를 나타내는 세 자리 정수이다.
상태 코드의 종류는 다양한데, 백의 자리 수를 기준으로 유형을 구분할 수 있다.
즉, 100번대 상태 코드, 200번대 상태 코드, 300번대 상태 코드처럼 유사한 상태 코드는 같은 백의 자리 수를 공유한다.
200번대: 성공 상태 코드
200번대 상태 코드는 '요청이 성공했음'을 의미한다. 주로 사용되는 상태 코드는 200(OK), 201(Created), 202(Accepted), 204(No Content)이다.
서버가 이 요청을 성공적으로 받아들이고 처리한 경우, 서버는 요청한 자원과 함께 상태 코드 200(OK)을 포함한 응답을 할 수 있다.
POST 요청을 통해 서버에 새로운 자원을 생성한 경우, 상태 코드 201(Created)로 요청이 성공했으며 새로운 자원이 만들어졌음을 알릴 수 있다. 이 경우 Location 헤더를 통해 생성된 자원의 위치를 명시할 수 있다.
202(Accepted)는 요청을 잘 받았으나, 아직 요청한 작업을 끝내지 않았음을 의미한다. 작업 시간이 긴 대용량 파일 업로드 작업이나 배치 작업과 같이 요청 결과를 곧바로 응답하기 어려운 상황이 있다. 이 경우 서버는 202(Accepted)로 응답할 수 있다.
요청 메시지에 대해 성공적으로 작업을 완료했더라도 마땅히 메시지 본문으로 표기할 것이 없을 경우 서버는 상태 코드 204(No Content)로 응답할 수 있다.
300번대: 리다이렉션 상태 코드
300번대 상태 코드는 리다이렉션과 관련된 상태 코드이다.
클라이언트가 요청한 자원이 다른 URL에 있을 경우, 서버는 응답 메시지의 Location 헤더를 통해 요청한 자원이 위치한 URL을 안내해 줄 수 있다. 이를 수신한 클라이언트는 Location 헤더에 명시된 URL로 즉시 재요청을 보내어 새로운 URL에 대한 응답을 받게 된다.
위 그림은 http://example.com/old로 GET 요청을 보낸 호스트가 http://example.com/new로 리다이렉트되는 상황을 나타낸다. 그림 하단에서 상태 코드 200(OK)도 확인할 수 있다.
리다이렉션의 유형은 크게 영구적인 리다이렉션과 일시적인 리다이렉션으로 구분된다.
영구적인 리다이렉션
- 영구적인 리다이렉션은 자원이 완전히 새로운 곳으로 이동하여 경로가 영구적으로 재지정되는 것을 의미한다.
- 따라서 이 경우 기존의 URL에 요청 메시지를 보내면 항상 새로운 URL로 리다이렉트된다.
- 서버가 도메인을 이전하는 등 웹 사이트의 큰 개편이 있을 때 이런 영구적인 리다이렉션을 접할 수 있다. 영구적인 리다이렉션과 관련한 상태 코드로는 301(Moved Permanently)과 308(Permanent Redirect)이 있다.
상태 코드 301(Moved Permanently)과 308(Permanent Redirect)의 차이점은 '클라이언트의 재요청 메시지 변경 여부'에 있다.
예를 들어 클라이언트가 서버에 GET 요청 메시지를 보낸 뒤 301 또는 308 응답 메시지를 받았고, 응답 메시지의 Location 헤더에 명시된 경로로 재요청을 보내야 한다고 가정한다면, 클라이언트가 보내는 두 번째 요청 메서드는 첫 번째 요청 메서드와 동일하게 GET이다.
이번에는 클라이언트가 서버에 POST 메서드와 같이 GET 메서드가 아닌 요청 메시지를 보냈고, 301(Moved Permanently) 응답 메시지를 받았다고 가정하면, 클라이언트가 보내는 두 번째 요청 메서드는 GET 요청으로 바뀔 '수도' 있다.
이런 애매함으로 인해 등장한 상태 코드가 308(Permanent Redirect)이다.
클라이언트가 308(Permanent Redirect) 응답 메시지를 받을 경우, 두 번째 요청 메시드는 변하지 않는다.
즉, 다음 쪽의 그림과 같이 첫 번째 요청에서 POST 메서드를 사용했다면, 상태 코드 308(Permanent Redirect)을 받은 뒤 보내는 두 번째 요청에서도 POST 메서드를 유지하게 된다.
그렇기에 어떤 URL에 요청을 보낸 결과로 영구적인 리다이렉션 관련 상태 코드를 응답받았다면, 요청을 보낸 URL은 기억할 필요가 없어진다.(검색 엔진 등에 영향이 간다.)
일시적 리다이렉션
- 일시적인 리다이렉션temporary redirection은 자원의 위치가 임시로 변경되었거나 임시로 사용할 URL이 필요한 경우에 주로 사용된다.
- 따라서 어떤 URL에 대해 일시적인 리다이렉션 관련 상태 코드를 응답받았다면 여전히 요청을 보낸 URL은 기억해야 한다. 일시적인 리다이렉션과 관련한 상태 코드는 302(Found), 303(See Other), 307(Temporary Redirect)이 있다.
GET이 아닌 요청 메서드를 사용한 클라이언트가 상태 코드 302(Found)를 응답받을 경우, 두 번째 요청 메서드는 GET으로 바뀔 ‘수도’ 있다.
상태 코드 302(Found)의 애매모호함을 해결하기 위한 상태 코드는 307(Temporary Redirect)이다. 307(Temporary Redirect)은 두 번째 요청 메서드를 변경하지 않는 상태 코드이다.
상태 코드 303(See Other)은 두 번째 요청 메서드를 GET으로 바꿔 주기 위해 사용된다.
400번대: 클라이언트 에러 상태 코드
- 400번대 상태 코드는 ‘클라이언트에 의한 에러가 있음’을 알려 주는 상태 코드이다.
- 서버가 처리할 수 없는 형태로 요청을 보냈거나, 존재하지 않는 자원에 대해 요청을 보내는 경우가 이런 경우에 속한다.
특정 자원에 접근하기 위해 인증이 필요할 때가 있다. 요청에 대한 인증이 필요할 경우 서버는 401(Unauthorized) 상태 코드를 응답할 수 있다.
서버가 상태 코드 401(Unauthorized)로 응답할 때는 한 가지 특징이 있다. 반드시 WWW-Authenticate라는 헤더를 통해 인증 방법을 알려 주어야 한다는 점이다.
만약 클라이언트의 권한이 충분하지 않다면 상태 코드 403(Forbidden)을 응답한다. 즉, 상태 코드 403(Forbidden)은 ‘자원에 접근할 권한이 없음’을 의미한다.
상태 코드 401(Unauthorized) VS 403(Forbidden)
인증(Authentication) 여부와 권한 부여(Authorization) 여부는 다른 개념이다.
인증이란 ‘자신이 누구인지 증명하는 것’을 의미하고, 권한 부여는 ‘인증된 주체에게 작업을 허용하는 것’을 의미한다.
권한 부여는 ‘인가’라고도 부른다. 인증이 되었다더라도 권한이 충분하지 않을 수 있다.
500번대: 서버 에러 상태 코드
400번대 상태 코드의 원인이 클라이언트라면, 500번대 상태 코드 원인은 서버이다.
즉, 500번대 오류는 클라이언트가 올바르게 요청을 보냈을지라도 발생할 수 있는 서버 에러에 대한 상태 코드이다.
상태 코드 502(Bad Gateway) 또한 종종 마주칠 수 있다. 상태 코드 502(Bad Gateway)는 클라이언트와 서버 사이에 위치한 중간 서버의 통신 오류를 나타내는 상태 코드이다.
클라이언트와 서버는 일반적으로 일대일로 연결되어 통신하지 않는다. 클라이언트와 서버 사이에는 게이트웨이를 비롯한 여러 중간 서버가 존재할 수 있다. 클라이언트와 서버가 요청과 응답을 주고받는 과정에서 중간에 위치한 수많은 서버들도 요청과 응답을 주고받게 된다.
이때, 클라이언트와 서버 사이에 위치한 중간 서버가 유효하지 않거나 잘못된 응답을 받을 수도 있다.
이럴 때 상태 코드 502(Bad Gateway)를 응답한다.
캐시(Cashe)
캐시란 불필요한 대역폭 낭비와 응답 지연을 방지하기 위해 정보의 사본을 임시로 저장하는 기술이다.
이렇게 사본을 임시로 저장해 두면 동일한 요청에 대해 캐시된 데이터를 활용할 수 있기 때문에 불필요한 대역폭 낭비를 줄일 수 있고, 더 빠르게 데이터에 접근할 수 있다.
캐시는 웹 브라우저에 저장되기도 하고, 클라이언트와 서버 사이에 위치한 중간 서버에 저장되기도 한다.
- 웹 브라우저에 저장되는 캐시 : 개인 전용 캐시(private cache)
- 중간 서버에 저장되는 캐시 : 공용 캐시(public cache)
이 글에서는 개인 전용 캐시에 초점을 맞춰 설명한다.
캐시 신선도
캐시를 사용했다면 항상 캐시한 이후 원본 데이터가 변경되는 상황에 대비해야 한다.
캐시된 사본 데이터가 얼마나 최신 원본 데이터와 유사한지를 캐시 신선도(cache freshness)라 한다.
- 신선도를 유지하는 가장 기본적인 방법은 캐시된 데이터에 유효 기간을 설정하는 것이다.
- 캐시된 데이터를 한 달 뒤, 1년 뒤, 10년 뒤까지 참고하게 해두면 신선도는 자연히 떨어질 것이다.
- 따라서 유효 기간이 지난 후에는 원본 데이터를 다시 요청함으로써 신선도를 유지할 수 있다.
캐시할 데이터에 유효 기간을 부여하는 방법으로는 HTTP 응답 메시지의 Expires 헤더(날짜)나 Cache-Control 헤더의 Max-Age 값(초)을 사용할 수 있다.
예를 들어 응답 메시지에 다음과 같이 명시할 수 있다.
- Expires: Tue, 06 Feb 2024 12:00:00 GMT
- Cache-Control: max-age=1200
캐시 유효기간 만료시
클라이언트가 응답받은 자원을 캐시해서 이용하다가 캐시의 유효 기간이 만료되었다면 서버에게 자원을 다시 요청해야 한다.
그런데 만약 캐시의 유효 기간이 만료되었더라도 원본 데이터가 변하지 않았다면 서버는 굳이 같은 자원을 전송해 줄 필요가 없다.
어차피 같은 자원이 클라이언트에게 캐시되어 있기 때문이다.
그렇기 때문에 캐시된 자원을 (유효 기간을 연장하여) 이용하면 된다.
하지만 만일 서버의 원본 자원이 변경되었다면 클라이언트는 새로운 자원을 응답받아야 한다.
따라서 캐시의 유효 기간이 만료되었다면 클라이언트는 캐시된 자원이 여전히 신선한지, 여전히 최신 상태의 정보인지 재검사해야 한다.
캐시의 신선도를 재검사하는 방법은 크게 두 가지가 있다.
- 날짜를 기반으로 서버에게 물어보는 방법
- 엔터티 태그를 기반으로 서버에게 물어보는 방법
날짜를 기반으로 재검사
클라이언트는 If-Modified-Since 헤더를 통해 서버에게 특정 시점 이후로 원본 데이터에 변경이 있었는지 물어볼 수 있다.
이때 If-Modified-Since 헤더의 값으로 특정 시점(날짜와 시각)이 명시된다.
이 시점 이후로 원본에 변경이 있었다면 그때만 새 자원으로 응답하도록 서버에게 요청하는 헤더이다.
- 예를들어, 아래의 요청 메시지는 ‘2024년 8월 23일 금요일 09:00:00 이후에 http://www.example.com/index.html의 자원이 변경되었니? 변경이 되었을 경우에만 새 자원으로 응답해 줘’ 라는 요청 메시지와 같다.
이렇게 요청을 보내면 서버는 아래의 그림과 같이 응답을 한다.
엔티티 태그 기반 재검사
Etag는 ‘자원의 버전’을 식별하기 위한 정보이다. 여기서 버전이란 ‘의미 있는 변경 사항’을 의미한다.
즉, 자원이 변경될 때마다 자원의 버전을 식별하는 Etag 값이 변경된다. 반대로 자원이 변경되지 않았다면 Etag 값도 변경되지 않는다.
클라이언트가 Etag 값이 부여된 자원을 캐시할 때, 캐시 신선도를 검사하기 위해 서버에게 ‘이 Etag 값과 일치하는 자원이 있니?’와 같이 물어볼 수 있다.
이를 위해 사용하는 헤더가 바로 If-None-Match이다.
- 예를 들어 다음의 요청 메시지는 ‘혹시 Etag 값이 abc인 http://www.example.com/index.html이라는 자원이 있니?
이 자원이 변경되었다면(Etag 값이 바뀌었다면) 그때만 새 자원으로 응답해 줘’ 라는 요청 메시지와 같다.
이때도 서버의 자원은 크게 셋 중 하나의 상황을 따른다.
- 요청받은 자원이 변경되었음 (Etag 값이 변경됨) : 서버는 상태 코드 200(OK)과 함께 변경된 데이터와 Etag 값을 응답
- 요청받은 자원이 변경되지 않았음 (Etag 값이 동일함) : 서버는 메시지 본문 없는 상태 코드 304(Not Modified)를 응답
- 요청받은 자원이 삭제되었음 : 서버는 상태 코드 404(Not Found)를 응답
쿠키(Cookie)
- 쿠키(cookie)란 서버에서 생성되어 클라이언트 측에 저장되는 데이터로, 상태를 유지하지 않는 HTTP의 특성을 보완하기 위한 수단이다.
- 서버가 클라이언트의 상태를 알 수 있게끔 하는 특별한 데이터이다.
- 쿠키를 이루는 정보는 기본적으로 ⟨이름, 값⟩ 쌍의 형태를 띠고 있고, 추가로 적용 범위와 만료 기간 등 다양한 속성을 가질 수 있다.
서버는 쿠키를 생성하여 클라이언트에게 전송하고, 클라이언트는 전달받은 쿠키를 저장해 두었다가 추후 동일한 서버에 보내는 요청 메시지에 쿠키를 포함하여 전송한다.
서버는 쿠키 정보를 참고해 두 개의 요청이 같은 클라이언트에서 왔는지, 로그인 상태를 유지하고 있는지 등을 알 수 있다.
이들은 각각 응답 메시지의 Set-Cookie 헤더와 요청 메시지의 Cookie 헤더를 통해 전달된다.
헤더
- 응답 메시지의 Set-Cookie 헤더를 통해 쿠키의 이름, 값과 더불어 세미콜론(;)으로 구분되는 속성(들)을 전달할 수 있다.
- 한 응답 메시지에 전달할 쿠키가 여러 개라면 다음과 같이 여러 개의 Set-Cookie를 사용하기도 한다.
요청 메시지의 Cookie 헤더 값은 서버에 전달할 쿠키의 이름과 값을 나타내는 헤더이다.
여러 개의 쿠키 값을 서버에 전달해야 할 때는 다음과 같이 세미콜론(;) 을 사용하여 여러 쿠키의 이름-값을 나열할 수 있다.
속성
http://www.naver.com에게 받은 쿠키를 전혀 다른 웹 사이트인 http://www.google.com에게 전송하면 안 된다.
이처럼 쿠키는 사용 가능한 도메인이 정해져 있다.
이는 응답 메시지 속 Set-Cookie 헤더의 domain 속성으로 정해진다.
또한 같은 도메인이라도 경로(path) 별로 쿠키를 구분하여 사용하고 싶을 때가 있을 수 있다.
예를 들어 http://www.example.com/lectures를 포함한 하위 경로에서 사용하고자 하는 쿠키와 http://www.example.com/books를 포함한 하위 경로에서 사용하고자 하는 쿠키가 다를 수 있다.
이럴 때는 다음 예시에서 표시한 부분처럼 "path"로 쿠키가 적용될 경로를 명시하면 된다.
그러면 path로 지정된 경로와 그 앞부분이 일치하는 경로(하위 경로)에서 해당 쿠키 정보를 활용할 수 있게 된다.
Expires / Max-Age는 쿠키의 유효 기간을 나타낸다.
쿠키마다 보통 유효 기간이 정해져 있다.
- Expires: [요일, DD-MM-YY HH:MM:SS GMT] 형식으로 표기되는 쿠키 만료 시점을 의미
- Max-Age: 초 단위 유효 기간을 의미
Expires로 명시된 시점이 지나거나 Max-Age로 명시된 유효 기간이 지나면 해당 쿠키는 삭제되어 전달되지 않는다.
웹 스토리지
- 쿠키는 서버가 생성하고 클라이언트가 저장하는 정보이다. 이를 통해 클라이언트의 상태를 추측할 수 있다.
- 쿠키 이외에도 클라이언트가 저장하고 클라이언트의 상태를 추측할 수 있는 키–값 쌍 형태의 정보가 있다.
- 바로 웹 스토리지(web storage)이다.
웹 스토리지는 웹 브라우저 내의 저장 공간으로, 일반적으로 쿠키보다 더 큰 데이터를 저장할 수 있다.
또 쿠키는 서버로 자동 전송되지만, 웹 스토리지의 정보는 서버로 자동 전송되지 않는다.
필요할 때 직접 조회해야 한다.
웹 스토리지에는 크게 로컬 스토리지(local storage)와 세션 스토리지(session storage)가 있다.
- 세션 스토리지 : 세션이 유지되는 동안, 즉 브라우저가 열려 있는 동안 유지되는 정보
- 로컬 스토리지 : 별도로 삭제하지 않는 한 영구적으로 저장이 가능한 정보
콘텐츠 협상
- HTTP의 콘텐츠 협상은 클라이언트가 요청한 URI에 대해 가장 적절한 자원의 표현을 선택하는 메커니즘이다.
- 클라이언트가 영어로 요청하면 영어 HTML을, 한국어로 요청하면 한국어 HTML을 제공하는 방식이다.
- 이는 클라이언트가 수신 가능한 자원의 형태를 서버가 조정하도록 도와준다.
콘텐츠 협상을 위해 사용되는 대표적인 HTTP 헤더로는 Accept, Accept-Language, Accept-Charset, Accept-Encoding 등이 있다.
클라이언트는 이들 헤더를 통해 자신이 선호하는 미디어 타입, 언어, 문자 인코딩 방식 등을 명시할 수 있으며, 서버는 이를 기반으로 자원을 최적화하여 응답한다.
또한 콘텐츠 협상에서는 표현의 우선순위를 나타내기 위해 q값(Quality Value)을 사용한다.
q값은 0에서 1까지의 실수이며, 클라이언트가 특정 표현을 얼마나 선호하는지를 나타낸다. 예를 들어 한국어(ko-KR) q=1, 영어(en-US) q=0.8처럼 표현할 수 있고, 서버는 q값이 높은 표현을 우선적으로 제공한다.(q가 생략되었을 경우 1을 의미한다.)
'네트워크 > 네트워크 이론' 카테고리의 다른 글
[네트워크 이론] 암호화 방식 (0) | 2025.04.24 |
---|---|
[네트워크 이론] 가용성, 이중화, 로드 밸런싱 (0) | 2025.04.24 |
[네트워크 이론] 응용 계층 - DNS와 자원 (0) | 2025.04.22 |
[네트워크 이론] 전송 계층 - TCP와 UDP (0) | 2025.04.22 |
[네트워크 이론] 전송 계층 - IP의 한계와 포트 (0) | 2025.04.21 |