이번 문서에서는 Google API와 여러 가지 다른 HTTP 버전 및 구현체의 호환 방식에 대해서 설명합니다. 이미 생성되었거나 직접 작성된 클라이언트 라이브러리(대부분 사용 사례에서 권장되는 방식)를 사용하고 있더라도 걱정할 필요 없습니다. 여기에서 제공되는 코드는 서버와 통신하면서 발생하는 하위 수준의 문제를 다루기 때문입니다.
하지만 숙련된 개발자가 커스텀 코드를 작성하여 원하는 제3의 HTTP 클라이언트 라이브러리를 사용해 API의 REST 인터페이스에 액세스해야 한다면 적어도 여기에서 설명하는(선택한 API와 관련된) 시맨틱스를 비롯해 HTTP 라이브러리에서 제공하는 기능에 대해 잘 알고 있어야 합니다.
통신 프로토콜 사용(HTTP/*)
이번 섹션에서는 Cloud API가 클라이언트와 서버 사이의 통신에 사용할 수 있도록 지원되는 통신 프로토콜(일반적으로 HTTP 버전)과 지원되는 통신 프로토콜 사용 시 권장하는 방식에 대해서 설명합니다. 이어지는 다음 섹션에서는 요청 및 응답의 구조에 대해서 알아보겠습니다.
HTTP 시맨틱스
API 클라이언트 코드를 개발할 때는 표준 HTTP 프로토콜 시맨틱스를 따릅니다. 서버 측 프록시 또는 API 스택은 표준 HTTP 기능의 하위 집합만 지원하거나, 혹은 하위 호환이 가능한 버전까지 지원할 수도 있습니다.
API의 서버 측 구현체에서 처리해야 하는 HTTP 프로토콜 시맨틱스는 서버 스택에서 제어됩니다. 이러한 기능이 캐싱 지원처럼 API 사양에 명시적으로 포함되어 있는 경우에는 HTTP 프로토콜 시맨틱스만 사용하세요.
HTTP 버전
클라이언트는 클라이언트 플랫폼 또는 클라이언트 측 네트워크에서 허용되거나, 혹은 서버 측 프록시와의 협상에 따라 모든 HTTP/* 프로토콜을 사용할 수 있습니다. 지원되는 프로토콜로는 HTTP/1.0, HTTP/1.1, SPDY/*, HTTP/2 및 QUIC가 있습니다.
최신 HTTP 버전에서는 서버 푸시나 우선순위 같은 일부 API 기능이 지원되지 않을 수도 있으며, 그 밖에 전이중 스트리밍 같은 기능은 HTTP/2에서만 완전하게 지정할 수 있습니다. API 사양에 포함되어 있는 이러한 기능이 필요하더라도 HTTP 버전에 따라 사용이 제한된다는 점을 알고 있어야 합니다.
일반적으로 우수한 성능과 네트워크 결함에 따른 복원력을 이유로 HTTP/2를 권장합니다.
채널
채널이란 L4 네트워크 연결(TCP 또는 UDP 소켓)을 말합니다. 클라이언트 애플리케이션은 HTTP 요청을 처리하기 위해 채널이 런타임에서 사용되는 방식에 대해 어떠한 가정도 해서는 안 됩니다. 채널은 거의 모든 경우에 서버 프로세스가 아닌 프록시를 통해 종료됩니다.
HTTP/1.1 클라이언트일 때는 항상 TCP 연결(연결: 연결 유지)을 재사용하여 HTTP 클라이언트 라이브러리가 성능 개선을 위해 연결 풀까지 관리할 가능성이 높습니다. 동일한 TCP 연결을 통해 요청을 파이프라인으로 전송하지 마십시오. 자세한 내용은 HTTP 및 TCP를 참조하세요.
오늘날 브라우저는 모두 단일 채널을 통해 요청을 다중화하는 SPDY/*, HTTP/2 또는 QUIC로 통신합니다. 기존 연결 제한(2-10)은 단일 클라이언트에서 다수의 HTTP 요청이 동시에 발생하여 서버 구현체에서 병목현상이 일어날 때(예: 단일 오리진에 대한 100개의 HTTP/2 스트림) 외에는 문제가 될 수 없습니다.
HTTPS
클라이언트는 API 사양에서 지원하는 HTTPS 또는 HTTP를 통해 API에 액세스할 수 있습니다. TLS 협상 및 TLS 버전은 클라이언트 애플리케이션에 투명합니다. Google API는 기본적으로 HTTPS 트래픽만 허용합니다.
요청/응답 형식
요청 URL
JSON-REST 매핑은 URL로 인코딩된 요청 데이터를 지원하며 HTTP 요청과 응답 본문은 application/json
을 Content-Type
으로 사용합니다.
HTTP 본문은 JSON 배열을 사용해 스트리밍되는 RPC 메서드를 지원하며, JSON 배열에는 다수의 JSON 메시지 또는 오류 상태 JSON 메시지가 포함될 수 있습니다.
긴 요청 URL
URL은 실제로 2k~8k의 길이 제한이 있습니다. 이러한 제한은 일부 브라우저와 프록시에서 적용됩니다. API가 길이 제한을 초과하는 URL과 함께 GET 요청을 사용하면 요청이 브라우저에서 거부될 수 있습니다.
이러한 제한을 우회하려면 클라이언트 코드는 HTTP 헤더 X-HTTP-Method-Override: GET
과 함께 Content-Type을 application/x-www-form-urlencoded
로 하여 POST 요청을 사용해야 합니다. 이 방법은 DELETE 요청에도 사용할 수 있습니다.
HTTP 메서드(동사)
요청 URL이 REST 모델을 따르는 경우에는 HTTP 메서드가 API 사양의 일부로 지정됩니다. 특히 모든 API 메서드는 API 메서드가 매핑되는 특정 HTTP 동사에 따라 HTTP 프로토콜의 요구 사항을 준수해야 합니다 자세한 내용은 HTTP(Hypertext Transfer Protocol) 사양과 PATCH 메서드 RFC를 참조하세요.
HTTP GET
및 HEAD
와 같은 안전한 메서드는 가져오기 외에 다른 작업을 표현해서는 안 됩니다. 특히 HTTP GET
은 안전한 메서드로 간주되어야 하며 클라이언트에서 나타나는 부작용이 있어서는 안 됩니다.
HTTP의 멱등성은 다수의 동일한 요청에서 발생하는 부작용이 단일 요청에서 발생하는 부작용과 동일하다는 것을 의미합니다. GET
, PUT
, DELETE
는 스타일 가이드와 관련된 멱등성을 갖는 HTTP 메서드입니다. 단, 멱등성은 오직 서버 부작용과 관련하여 표현되며, 응답에 대해서는 어떤 것도 지정하지 않습니다. 특히 존재하지 않는 리소스의 DELETE
는 404 (Not Found)
를 반환해야 합니다.
HTTP POST
및 PATCH
는 안전한 메서드도 아니고 멱등성을 갖지도 않습니다. PATCH
는 RFC 5789에서 도입되었습니다.
HTTP 동사 | 안전 | 멱등성 |
---|---|---|
GET |
예 | 예 |
PUT |
예 | |
DELETE |
예 | |
POST |
||
PATCH |
페이로드 형식
요청과 응답은 '
application/x-www-form-urlencoded
본문에서 요청이GET
또는POST
인 경우를 제외하고 동일한 Content-Type을 공유해야 합니다.JSON은
application/json
MIME 유형에서 지원됩니다. proto3과 JSON의 매핑은 JSON 매핑에서 공식적으로 지정됩니다.양식 매개변수(
POST
)는 URL 쿼리 매개변수(GET
) 대신에 사용될 수 있지만 요청 필드를 쿼리 매개변수에 매핑할 때는 동일한 REST 스타일 매핑 규칙을 따라야 합니다. 지원되는Content-Type
은application/x-www-form-urlencoded
입니다.
스트리밍
반이중과 전이중의 비교
HTTP는 TCP(HTTP/1.x) 또는 다중화 변형(SPDY, HTTP/2, QUIC) 같이 서로 다른 스트림 중심 전송을 통해 요청 또는 응답 본문을 전송할 수 있는 요청-응답 프로토콜입니다.
클라이언트 개발자의 애플리케이션은 요청 본문을 스트리밍 모드인 클라이언트 스트리밍으로 작성할 수 있습니다. 마찬가지로 애플리케이션 역시 응답 본문을 스트리밍 모드인 서버 스트리밍으로 사용할 수 있습니다.
하지만 HTTP 사양은 요청 본문이 여전히 대기 중일 때 응답 본문을 다시 스트리밍할 수 있는 서버의 허용 여부를 지정하지 않습니다(오류 응답인 경우 제외). 이러한 시맨틱스는 전이중 스트리밍이라고 알려져 있습니다. 다수의 HTTP 클라이언트/서버/프록시 소프트웨어가 HTTP/1.1일 때도 전이중 스트리밍을 허용하여 상호 운용성 문제를 피하고 있지만 HTTP 기반 Cloud API에서는 반이중 스트리밍으로 제한됩니다.
기본적으로 Cloud API의 양방향 스트리밍 방식은 전이중 시맨틱스라고 가정합니다. 즉, HTTP를 사용하여 이러한 메서드를 호출하는 것은 안전하지 않습니다. 스트리밍 방식이 서버에서 적용되는 반이중 방식인 경우 API 문서는 반이중 동작을 명확하게 지정해야 합니다.
브라우저 클라이언트의 경우 표준 HTTP 시맨틱스는 브라우저 네트워크 API에 의해 추가 제한을 받습니다. 현재 브라우저는 XHR 또는 Fetch를 통한 서버 스트리밍(일반적으로 전송 수준 프레이밍 준수)만 지원합니다. Fetch API는 whatwg 스트림을 사용합니다.
브라우저 제한이 있으므로 브라우저 지원이 필요한 Cloud API는 클라이언트 스트리밍과 전이중 스트리밍을 피하거나 브라우저 클라이언트 전용 별도의 API를 제공해야 합니다.
일반적으로 인터넷을 통한 클라이언트 스트리밍은 서버 스트리밍보다 유용하지 못합니다. 클라이언트 스트리밍을 사용하면 종종 상태 저장 서비스로 이어져 부하 분산에 악영향을 미칠 뿐만 아니라 장애 또는 공격에 대한 시스템 취약점이 더욱 커지기 때문입니다. 반면에 서버 스트리밍은 긴 RTT 지연으로 네트워크의 지연 시간을 크게 줄일 수 있으므로 유용합니다.
메시지 인코딩
JSON 메시지는 스트리밍할 때 JSON 메시지 배열로 인코딩됩니다. 이때 요청 또는 응답 본문은 유효한 JSON MIME 유형을 유지합니다.
클라이언트 스트리밍 인코딩 예는 다음과 같습니다.
1 <length> <message-bytes> 1 <length> <message-bytes> … EOF
서버 스트리밍 인코딩 예는 다음과 같습니다.
1 <length> <message-bytes> … 2 <length> <status-bytes> EOF
통신 수준 인코딩: StreamBody에 대한 정의는 '메시지' 필드의 태그-id 할당에서만 의미가 있으며 '상태'는
선택 사항인 패딩 필드는 base64 인코딩 스트림을 지원하는 데 필요합니다.
message StreamBody {
repeated bytes message = 1;
google.rpc.Status status = 2;
repeated bytes padding = 15; // max one-byte tag-id: xxx01111
}
오류 메시지는 일반 메시지와 동일한 형식에 따라 JSON 또는 protobuf 배열의 마지막 요소로 첨부되어야 합니다.
상태 관리
절반 종료 동작은 클라이언트 또는 서버가 본문이 완성되었다는 사실을 서로 반대쪽에 알리기 위해 사용하는 모든 HTTP 버전에 알기 쉽게 정의되어 있습니다.
특히 응답을 기다리고 있을 때도 클라이언트 코드를 자유롭게 사용하여 요청을 완성할 수 있습니다. 마찬가지로 클라이언트는 요청 본문이 서버에 작성되고 있는 도중에서 완성된 응답을 볼 수 있습니다. HTTP 표준에 따라 클라이언트는 예상치 못한 방식으로 응답이 완성되었을 때 일반적으로 오류 상태와 함께 요청을 취소하거나 완성할 수 있습니다. 다시 말해서 정상적인 조건에서 클라이언트가 요청을 보내고 있을 때 서버가 응답을 완성해서는 안 됩니다.
취소
클라이언트는 취소 기능이 지원되어 요청 또는 응답이 대기 중일 때 요청을 취소할 수 있습니다.
HTTP/1.* 클라이언트에서는 안정적으로 지원되는 취소 기능이 없습니다. 요청/응답 트랜잭션을 취소하지 않고 요청이 완성된 후에는 클라이언트가 자유롭게 TCP 연결을 종료할 수 있기 때문입니다. HTTP/1.1에 따른 TCP FIN은 연결이 연결 유지로 표시되어 있더라도(연결: 연결 유지) 취소로 해석되어서는 안 됩니다.
하지만 클라이언트가 TCP 연결을 종료한 이후에 서버가 데이터를 클라이언트에 작성하려고 하면 RST가 생성되어 취소 기능을 트리거할 수 있습니다.
스트리밍이 아닌 API에서도 취소는 문제가 될 수 있습니다. 특히 응답에 긴 폴링이 포함되어 연결이 장시간 유휴 상태로 유지되는 경우에 그렇습니다.
명시적인 취소 기능은 SPDY, HTTP/2 및 QUIC에서, 특히 종료 메시지와 함께 지원됩니다.
연결 유지
연결 유지 기능이 지원되어 클라이언트 또는 서버가 패킷 손실 또는 네트워크 장애가 발생하는 이벤트에서도 잘못된 피어를 찾아낼 수 있습니다.
HTTP/1.1에서는 연결 유지 기능이 지원되지 않습니다. TCP 연결 유지가 가능하지 않기 때문입니다.
QUIC 또는 HTTP/2는 브라우저를 포함해 애플리케이션에서 연결 유지 기능을 지원할 수 있도록 특별한 제어 메시지를 제공합니다.
하지만 안정적인 연결 유지와 장애 감지를 위해서는 서버 측 지원과 함께 클라이언트 라이브러리가 필요할 수 있습니다. 인터넷을 통해 스트리밍을 장시간 지속할 때 기본적인 HTTP에만 의존하여 통신 프로토콜을 이용할 경우 오류가 자주 발생하기 때문입니다.
흐름 제어
흐름 제어 기능을 지원하려면 클라이언트가 전송 수준의 흐름 제어 이벤트를 클라이언트 애플리케이션까지 전파해야 합니다. 실제 메커니즘은 클라이언트 애플리케이션이 사용하는 HTTP 클라이언트 API의 스타일에 따라 달라집니다. 예를 들어 애플리케이션이 클라이언트 또는 서버가 과부하에 걸리지 않도록 흐름 제어 이벤트를 처리하거나 따를 수 있도록 하려면 명시적인 흐름 제어 지원과 함께 블로킹 쓰기 및 읽기 또는 비블로킹 읽기 및 쓰기가 필요합니다.
HTTP/1.1은 TCP 흐름 제어를 이용합니다.
SPDY와 HTTP/2는 자체적으로 스트림 수준의 흐름 제어 기능을 지원하지만 요청이 단일 TCP 연결을 통해 다중화될 때는 연결 수준의 TCP 흐름 제어 기능이 추가로 적용됩니다.
QUIC는 UDP에서 실행되기 때문에 흐름 제어 기능을 완전히 자체적으로 관리합니다.