패키지 만들기

업로드 옵션

Android Over The Air API를 사용하면 패키지 데이터를 업로드하여 새 패키지 리소스를 만들 수 있습니다. 업데이트가 기기에 전송되도록 하나 이상의 구성과 연결할 수 있는 OTA 패키지입니다.

Google에서는 Linux 및 Windows용 바이너리를 제공하여 아래에 설명된 프로토콜을 구현하는 대신 재개 가능한 패키지 업로드를 용이하게 합니다. 더 심층적인 통합을 원한다면 아래에 설명된 프로토콜 중 하나를 사용하세요.

사용하려면 먼저 서비스 계정을 만들고 해당 계정의 JSON 키 파일을 가져와야 합니다. 여기에서 계정 만들기 가이드를 참고하세요.
바이너리와 키 파일이 있으면 명령줄 옵션으로 이를 실행하여 키 파일, 배포, 업로드할 패키지를 지정할 수 있습니다. 모든 옵션을 보려면 --help를 사용하세요.

프로토콜 업로드

업로드 요청은 다음과 같은 방법으로 실행할 수 있습니다. X-Goog-Upload-Protocol 요청 헤더와 함께 사용할 메서드를 지정합니다.

  • 멀티파트 업로드: X-Goog-Upload-Protocol: multipart. 크기가 작은 파일과 메타데이터를 빠르게 전송할 수 있으며, 요청 한 번으로 파일과 파일을 설명하는 메타데이터를 함께 전송합니다.
  • 재개 가능한 업로드: X-Goog-Upload-Protocol: resumable. 크기가 큰 파일에서 특히 중요한 안정적인 전송이 가능합니다. 이 메서드를 통해 메타데이터를 선택적으로 포함할 수 있는 세션 시작 요청을 사용할 수 있습니다. 업로드당 하나의 추가 HTTP 요청 비용으로 작은 파일에서도 작동하므로 대부분의 애플리케이션에서 사용할 수 있는 효과적인 전략입니다.

멀티파트 업로드

이 옵션은 전송할 데이터가 연결 실패 시 전체를 다시 업로드해도 될 만큼 작은 경우에 효과적입니다.

멀티파트 업로드를 사용하려면 /upload/package URI에 POST 요청을 실행하고 X-Goog-Upload-Protocolmultipart로 설정합니다.

멀티파트 업로드 요청을 실행할 때 사용하는 최상위 HTTP 헤더에는 다음 항목이 포함됩니다.

  • Content-Type: multipart/related로 설정하고 요청에서 관련 파트를 식별하는 데 사용하는 경계 문자열을 포함합니다.
  • Content-Length: 요청 본문의 총 바이트 수로 설정합니다.

요청 본문 형식은 multipart/related 콘텐츠 유형 [RFC2387] 이며 정확히 두 부분으로 구성됩니다. 파트는 경계 문자열로 식별되며 최종 경계 문자열 뒤에는 하이픈 2개가 나옵니다.

멀티파트 요청의 각 파트에는 추가 Content-Type 헤더가 필요합니다.

  1. 메타데이터 파트: 첫 번째 위치에 있어야 하며 Content-Typeapplication/json여야 합니다.
  2. 미디어 파트: 두 번째 위치에 있어야 하며 Content-Typeapplication/zip여야 합니다.

예: 멀티파트 업로드

아래 예는 Android Over The Air API의 멀티파트 업로드 요청을 보여줍니다.

POST /upload/package HTTP/1.1
Host: androidovertheair.googleapis.com
Authorization: Bearer your_auth_token
Content-Type: multipart/related; boundary=BOUNDARY
Content-Length: number_of_bytes_in_entire_request_body

--BOUNDARY
Content-Type: application/json; charset=UTF-8

{"deployment": "id", "package_title": "title" }
--BOUNDARY
Content-Type: application/zip; charset=UTF-8

Package ZIP
--BOUNDARY--

요청이 성공하면 서버에서 HTTP 200 OK 상태 코드를 반환합니다.

HTTP/1.1 200

이를 쉽게 수행하는 방법은 curloauth2l을 사용하는 것입니다. 다음은 서비스 키를 사용한다고 가정하는 샘플 요청입니다 (자세한 내용은 승인 방법 참조).

curl 요청 예시
    JSON={"deployment": "id", "package_title": "title" }
    SERVICE_KEY_FILE=path to your service key json file
    curl \
    -H "$(./oauth2l header --json $SERVICE_KEY_FILE android_partner_over_the_air)" \
    -H "Host: androidovertheair.googleapis.com" \
    -H "X-Goog-Upload-Protocol: multipart" \
    -H "Content-Type: multipart/form-data" \
    -F "json=$JSON;type=application/json" \
    -F "data=@update.zip;type=application/zip" \
    androidovertheair.googleapis.com/upload/package
  

재개 가능한 업로드

데이터 파일을 보다 안정적으로 업로드하려면 재개 가능한 업로드 프로토콜을 사용하면 됩니다. 이 프로토콜을 사용하면 통신 실패로 데이터 흐름이 중단된 후 업로드 작업을 재개할 수 있습니다. 대용량 파일을 전송하거나 모바일 클라이언트 앱에서 업로드할 때와 같이 네트워크 중단 또는 기타 전송 실패가 발생할 가능성이 높을 때 특히 유용합니다. 대용량 파일을 처음부터 다시 업로드할 필요가 없으므로 네트워크 실패 시 대역폭 사용량도 줄일 수 있습니다.

재개 가능한 업로드 프로토콜은 여러 명령어를 사용합니다.

  1. 재개 가능한 세션 시작. 메타데이터가 포함된 업로드 URI에 시작 요청을 실행하고 고유한 재개 가능한 업로드 위치를 설정합니다.
  2. 재개 가능한 세션 URI 저장: 시작 요청의 응답으로 반환된 세션 URI를 저장합니다. URI는 이 세션의 나머지 요청에 사용됩니다.
  3. 파일 업로드: ZIP 파일의 전체 또는 일부를 재개 가능한 세션 URI에 전송합니다.

또한 재개 가능한 업로드를 사용하는 앱에는 중단된 업로드를 다시 시작하는 코드가 있어야 합니다. 업로드가 중단되면 성공적으로 수신된 데이터 양을 확인한 후 중단된 지점부터 업로드를 재개합니다.

참고: 업로드 URI는 3일 후에 만료됩니다.

1단계: 재개 가능한 세션 시작

재개 가능한 업로드를 시작하려면 /upload/package URI에 POST 요청을 실행하고 X-Goog-Upload-Protocolresumable로 설정합니다.

이 시작 요청의 경우 본문에 메타데이터만 포함되어야 합니다. 이후 요청에서 업로드하려는 실제 파일 콘텐츠를 전송합니다.

시작 요청에는 다음 HTTP 헤더를 사용합니다.

  • X-Goog-Upload-Header-Content-Type: 업로드 중인 파일의 콘텐츠 유형이며 application/zip로 설정해야 합니다.
  • X-Goog-Upload-Command. start(으)로 설정합니다.
  • X-Goog-Upload-Header-Content-Length: 이후 요청에서 전송할 업로드 데이터의 바이트 수로 설정합니다. 이 요청 시점의 길이를 모르면 이 헤더를 생략할 수 있습니다.
  • Content-Type. 메타데이터의 콘텐츠 유형이며 application/json로 설정해야 합니다.
  • Content-Length: 이 시작 요청 본문에 제공된 바이트 수로 설정합니다.
예: 재개 가능한 세션 시작 요청

다음 예는 Android Over The Air API에서 재개 가능한 세션을 시작하는 방법을 보여줍니다.

POST /upload/package HTTP/1.1
Host: android/over-the-air.googleapis.com
Authorization: Bearer your_auth_token
Content-Length: 38
Content-Type: application/json; charset=UTF-8
X-Goog-Upload-Command: start
X-Goog-Upload-Header-Content-Type: application/zip
X-Goog-Upload-Header-Content-Length: 2000000

{"deployment": "id", "package_title": "title" }

다음 섹션에서는 응답 처리 방법을 설명합니다.

2단계: 재개 가능한 세션 URI 저장

세션 시작 요청이 성공하면 API 서버에서 HTTP 200 OK 상태 코드로 응답합니다. 또한 재개 가능한 세션 URI가 지정된 X-Goog-Upload-URL 헤더를 제공합니다. 아래 예와 같이 X-Goog-Upload-URL 헤더에는 이 세션에서 사용할 고유한 업로드 ID를 제공하는 upload_id 쿼리 매개변수 부분이 포함되어 있습니다. 응답에는 X-Goog-Upload-Status 헤더도 포함되며 업로드 요청이 유효하고 수락된 경우 active가 됩니다. 업로드가 거부되면 이 상태는 final일 수 있습니다.

예: 재개 가능한 세션 시작 응답

1단계의 요청에 대한 응답은 다음과 같습니다.

HTTP/1.1 200 OK
X-Goog-Upload-Status: active
X-Goog-Upload-URL: androidovertheair.googleapis.com/?upload_id=xa298sd_sdlkj2
Content-Length: 0

위 예의 응답에 있는 X-Goog-Upload-URL 헤더 값이 실제 파일 업로드 또는 업로드 상태를 쿼리할 때 HTTP 엔드포인트로 사용할 세션 URI입니다.

이후 요청에서 사용할 수 있도록 세션 URI를 복사 및 저장합니다.

3단계: 파일 업로드

파일을 업로드하려면 POST 요청을 이전 단계에서 가져온 업로드 URI에 전송합니다. 업로드 요청 형식은 다음과 같습니다.

POST session_uri

재개 가능한 파일 업로드 요청을 실행할 때 사용되는 HTTP 헤더에는 다음이 포함됩니다.

  1. Content-Length: 이 요청에서 업로드하는 바이트 수로 설정되며 일반적으로 업로드 파일 크기입니다.
  2. X-Goog-Upload-Command. uploadfinalize로 설정합니다.
  3. X-Goog-Upload-Offset: 바이트를 써야 하는 오프셋을 지정합니다. 클라이언트는 바이트를 순차적으로 업로드해야 합니다.
예: 재개 가능한 파일 업로드 요청

다음은 현재 예에서 전체 2,000,000바이트의 ZIP 파일을 업로드하기 위한 재개 가능한 요청입니다.

POST /?upload_id=xa298sd_sdlkj2 HTTP/1.1
Host: androidovertheair.googleapis.com
X-Goog-Upload-Protocol: resumable
X-Goog-Upload-Command: upload, finalize
X-Goog-Upload-Offset: 0
Content-Length: 2000000
Content-Type: application/zip

bytes 0-1999999

요청이 성공하면 서버가 HTTP 200 Ok로 응답합니다.

업로드 요청이 중단되거나 서버로부터 HTTP 503 Service Unavailable 또는 기타 5xx 응답을 받으면 중단된 업로드 재개에 설명된 절차를 따릅니다.


파일의 단위별 업로드

재개 가능한 업로드를 사용하면 한 파일을 여러 단위로 나누어 각 단위를 순서대로 업로드하는 일련의 요청을 전송할 수 있습니다. 추가 요청으로 성능에 영향을 미칠 수 있고 일반적으로 필요한 방법이 아니므로 선호되는 방식은 아닙니다. 클라이언트는 페이로드의 나머지 바이트를 모두 업로드하고 모든 upload 명령어에 finalize 명령어를 포함하는 것이 좋습니다.

하지만 단일 요청으로 전송되는 데이터 양을 줄이기 위해 파일을 단위별로 분할해야 할 수도 있습니다. 또한 업로드 진행률이 기본적으로 지원되지 않는 기존 브라우저에서 업로드 진행률을 표시하는 등의 작업도 가능합니다.


중단된 업로드 재개

응답을 받기 전에 업로드 요청이 종료되거나 서버로부터 HTTP 503 Service Unavailable 응답을 받은 경우에는 중단된 업로드를 재개해야 합니다. 방법은 다음과 같습니다.

  1. 상태 요청: X-Goog-Upload-Commandquery로 설정된 업로드 URI에 대한 요청을 실행하여 현재 업로드 상태를 쿼리합니다.

    참고: 업로드가 중단된 경우가 아니라도 각 단위 사이의 상태를 요청할 수 있습니다. 이러한 요청은 기존 브라우저에서 업로드 진행률을 표시하려는 경우에 유용합니다.

  2. 업로드된 바이트 수 가져오기: 상태 쿼리의 응답을 처리합니다. 서버에서 응답의 X-Goog-Upload-Size-Received 헤더를 사용하여 지금까지 수신한 바이트 수를 지정합니다.
  3. 남은 데이터 업로드: 마지막으로 요청을 재개할 위치를 결정했다면 나머지 데이터 또는 현재 단위를 전송합니다. 어떤 경우든 남은 데이터를 별도의 단위로 다루어야 하므로 업로드를 재개할 때 X-Goog-Upload-Offset 헤더를 적절한 오프셋으로 설정해야 합니다.
예: 중단된 업로드 재개

1) 업로드 상태 요청

POST /?upload_id=xa298sd_sdlkj2 HTTP/1.1
Host: androidovertheair.googleapis.com
X-Goog-Upload-Command: query

모든 명령어와 마찬가지로 클라이언트는 쿼리 명령어의 HTTP 응답에서 X-Goog-Upload-Status 헤더를 확인해야 합니다. 헤더가 있고 값이 active이 아니면 업로드가 이미 종료된 것입니다.

2) 응답에서 지금까지 업로드된 바이트 수 추출

서버의 응답은 X-Goog-Upload-Size-Received 헤더를 사용하여 지금까지 파일의 처음 43바이트를 수신했음을 나타냅니다.

HTTP/1.1 200 OK
X-Goog-Upload-Status: active
X-Goog-Upload-Size-Received: 42

3) 중단된 지점부터 업로드 재개

다음 요청에서는 43바이트부터 파일의 나머지 바이트를 전송해 업로드를 재개합니다.

POST /?upload_id=xa298sd_sdlkj2 HTTP/1.1
Host: androidovertheair.googleapis.com
X-Goog-Upload-Command: upload, finalize
Content-Length: 1999957
X-Goog-Upload-Offset: 43

bytes 43-1999999

권장사항

미디어를 업로드할 때는 오류 처리와 관련된 일부 권장사항을 알면 도움이 됩니다.

  • 연결 중단 또는 다음과 같은 기타 5xx 오류로 인해 실패한 업로드를 재개하거나 재시도합니다.
    • 500 Internal Server Error
    • 502 Bad Gateway
    • 503 Service Unavailable
    • 504 Gateway Timeout
  • 업로드 요청을 재개하거나 재시도할 때 5xx 서버 오류가 반환되면 지수 백오프 전략을 사용합니다. 서버가 과부하되면 이러한 오류가 발생할 수 있습니다. 요청량 또는 네트워크 트래픽이 많은 기간에 지수 백오프를 사용하면 이러한 문제가 완화될 수 있습니다.
  • 다른 종류의 요청을 지수 백오프로 처리하면 안 되지만 그 중 상당수를 여전히 재시도할 수 있습니다. 요청을 재시도할 때는 재시도 횟수를 제한합니다. 예를 들어 오류가 보고되기 전에 코드의 재시도 횟수를 10회 이하로 제한할 수 있습니다.
  • 재개 가능한 업로드를 실행할 때 처음부터 전체 업로드를 시작하여 404 Not Found 오류를 처리합니다.

지수 백오프

지수 백오프는 클라이언트에서 시간 간격을 늘려 실패한 요청을 주기적으로 다시 시도하는 네트워크 애플리케이션용 표준 오류 처리 전략입니다. 많은 양의 요청 또는 네트워크 트래픽으로 인해 서버에서 오류를 반환하는 경우 지수 백오프는 이러한 오류를 처리하는 데 효과적인 전략일 수 있습니다. 반대로 잘못된 승인 사용자 인증 정보 또는 파일을 찾을 수 없는 오류 등 네트워크 트래픽 양이나 응답 시간과 무관한 오류를 처리할 때는 적절한 전략이 아닙니다.

지수 백오프를 적절하게 사용하면 대역폭 사용량의 효율성을 높이고, 성공적인 응답을 가져오는 데 필요한 요청 수를 줄이며, 동시 환경에서의 요청 처리량을 극대화할 수 있습니다.

간단한 지수 백오프 구현 흐름은 다음과 같습니다.

  1. API에 요청을 전송합니다.
  2. 요청을 재시도해야 함을 알리는 HTTP 503 응답을 수신합니다.
  3. 1초 + random_number_milliseconds를 대기한 후 요청을 재시도합니다.
  4. 요청을 재시도해야 함을 알리는 HTTP 503 응답을 수신합니다.
  5. 2초 + random_number_milliseconds를 대기한 후 요청을 재시도합니다.
  6. 요청을 재시도해야 함을 알리는 HTTP 503 응답을 수신합니다.
  7. 4초 + random_number_milliseconds를 대기한 후 요청을 재시도합니다.
  8. 요청을 재시도해야 함을 알리는 HTTP 503 응답을 수신합니다.
  9. 8초 + random_number_milliseconds를 대기한 후 요청을 재시도합니다.
  10. 요청을 재시도해야 함을 알리는 HTTP 503 응답을 수신합니다.
  11. 16초 + random_number_milliseconds를 대기한 후 요청을 재시도합니다.
  12. 중지합니다. 오류를 보고하거나 로깅합니다.

위 흐름에서 random_number_milliseconds는 1,000밀리초 이하의 임의 숫자입니다. 이와 같은 임의의 작은 지연은 더욱 균일하게 부하를 분산하고 서버 과부하 가능성을 방지하므로 필요합니다. random_number_milliseconds 값은 대기 후 매번 재정의해야 합니다.

참고: 대기 시간은 항상 (2 ^ n) + random_number_milliseconds이며, 여기서 n은 단순 증가 정수로서 처음에는 0으로 정의됩니다. 정수 n은 반복(요청)할 때마다 1씩 증가합니다.

n이 5일 때 종료하도록 알고리즘이 설정되어 있습니다. 이러한 제한은 클라이언트의 무제한 재시도를 방지하고 요청이 '복구 불가능한 오류'로 간주되기 전에 약 32초의 총 지연 시간을 발생시킵니다. 특히 긴 업로드가 진행 중인 경우에는 최대 재시도 횟수를 늘리는 것이 좋습니다. 단, 재시도 지연 시간을 합리적인 시간(1분 미만)으로 제한해야 합니다.